From 521483f4e054750af764575c7b228223b978da2d Mon Sep 17 00:00:00 2001 From: NaifBanana <30419422+NaifBanana@users.noreply.github.com> Date: Fri, 26 Jun 2026 02:42:16 -0500 Subject: [PATCH] Added vertex buffers/data handling and shader/program handling --- engine/NBGraphics/Buffers.hpp | 93 +-- engine/NBGraphics/CMakeLists.txt | 3 +- engine/NBGraphics/OGLObjects.hpp | 6 +- engine/NBGraphics/ProgramPipeline.hpp | 123 ++++ engine/NBGraphics/VertexArray.hpp | 96 +-- engine/NBGraphics/Window.hpp | 5 - engine/NBGraphics/shader.hpp | 193 ------ engine/NBGraphics/src/Buffers.cpp | 7 +- engine/NBGraphics/src/ProgramPipeline.cpp | 75 +++ engine/NBGraphics/src/VertexArray.cpp | 98 +-- engine/NBGraphics/src/Window.cpp | 2 - engine/NBGraphics/src/shader.cpp | 742 ---------------------- engine/NBGraphics/tests/CMakeLists.txt | 2 - engine/NBGraphics/tests/TestWindow.cpp | 50 +- 14 files changed, 386 insertions(+), 1109 deletions(-) create mode 100644 engine/NBGraphics/ProgramPipeline.hpp delete mode 100644 engine/NBGraphics/shader.hpp create mode 100644 engine/NBGraphics/src/ProgramPipeline.cpp delete mode 100644 engine/NBGraphics/src/shader.cpp diff --git a/engine/NBGraphics/Buffers.hpp b/engine/NBGraphics/Buffers.hpp index 7132a6c..0e08b99 100644 --- a/engine/NBGraphics/Buffers.hpp +++ b/engine/NBGraphics/Buffers.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -16,69 +15,20 @@ namespace nb { -static uint8_t GLSLTypeSize(GLenum type) { - switch(type) { - case GL_SHORT: - case GL_UNSIGNED_SHORT: - case GL_HALF_FLOAT: - return 2; - break; - case GL_INT: - case GL_UNSIGNED_INT: - case GL_FLOAT: - return 4; - break; - case GL_DOUBLE: - return 8; - break; - default: - return 1; - break; - } -} - -template -ByteVector vectorToRaw(const std::vector& vec) { - unsigned int num_bytes = vec.size() * sizeof(T); - ByteVector ret(num_bytes); - memcpy(ret.data(), vec.data(), num_bytes); - return ret; -} - -template -ByteVector concatVectorsToRaw(const std::vector& vec1, const std::vector& vec2) { - ByteVector vec1_raw = vectorToRaw(vec1); - ByteVector vec2_raw = vectorToRaw(vec2); - unsigned int vec1_raw_size = vec1_raw.size(); - unsigned int vec2_raw_size = vec2_raw.size(); - ByteVector ret(vec1_raw_size + vec2_raw_size); - memcpy(ret.data(), vec1_raw.data(), vec1_raw_size); - memcpy(ret.data() + vec1_raw_size, vec2_raw.data(), vec2_raw_size); - return ret; -} - -template -std::vector rawToVector(const ByteVector& vec) { - if (vec.size() % sizeof(T) != 0) { - THROW(std::runtime_error("Data size does not align to std::vector<" + std::string(typeid(T).name()) + ">.")); - } - unsigned int num_elmts = vec.size() / sizeof(T); - std::vector ret(num_elmts); - memcpy(ret.data(), vec.data(), vec.size()); - - return ret; -} +extern const std::unordered_map BufferTypes; class BufferError : public Error { + protected: using Base = Error; - - public: using Base::Base; - - enum Codes : unsigned int { - UNDEFINED, DATA_OVERFLOW, INVALID_BUFFER, HANDLE_OVERWRITE - }; + public: + enum Codes : unsigned int { + UNDEFINED, + DATA_OVERFLOW, + INVALID_BUFFER, + HANDLE_OVERWRITE, + }; static const std::string type; static const ErrorCodeMap ErrorMessages; }; @@ -89,13 +39,25 @@ class Buffer : public OpenGLObject { public: using OpenGLObject::OpenGLObject; - Buffer() = default; Buffer(Buffer&& other) { *this = std::move(other); } Buffer& operator=(Buffer&& rhs) { - if (_id) { THROW(BufferError(Codes::HANDLE_OVERWRITE)); } + auto targ_name = BufferTypes.at(Target); + if (Target != rhs.Target) { + THROW(BufferError( + Codes::INVALID_BUFFER, + "w/ ("+targ_name+") := ("+BufferTypes.at(rhs.Target)+")" + )); + } + //nb::logger.log(std::to_string(_id) + ":=" + std::to_string(rhs._id)); + if (_id) { + THROW(BufferError( + Codes::HANDLE_OVERWRITE, + "w/ BufferType = " + targ_name + )); + } _id = rhs._id; _usage = rhs._usage; _size = rhs._size; @@ -109,8 +71,12 @@ class Buffer : public OpenGLObject { virtual void bind() const override { if (_id) { glBindBuffer(Target, _id); + } else { + THROW(BufferError( + Codes::INVALID_BUFFER, + "w/ BufferType " + BufferTypes.at(Target) + )); } - THROW(BufferError(Codes::INVALID_BUFFER)); } virtual void unbind() const override { glBindBuffer(Target, 0); } virtual GLenum usage() const { return _usage; } @@ -240,13 +206,14 @@ class ArrayBuffer using BufferType = ImmutableBuffer>; using BufferType::BufferType; + public: using Base::Target; }; template -class ElementBuffer : public virtual Buffer> { +class ElementBuffer : public Buffer> { using Base = Buffer>; using BufferType = Base; diff --git a/engine/NBGraphics/CMakeLists.txt b/engine/NBGraphics/CMakeLists.txt index f799880..5a94b3f 100644 --- a/engine/NBGraphics/CMakeLists.txt +++ b/engine/NBGraphics/CMakeLists.txt @@ -10,6 +10,7 @@ set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) toAbsolutePath(NB_GRAPHICS_SOURCE ./src/Buffers.cpp ./src/OGLObjects.cpp + ./src/ProgramPipeline.cpp ./src/Textures.cpp ./src/VertexArray.cpp ./src/Window.cpp @@ -21,7 +22,7 @@ toAbsolutePath(NB_GRAPHICS_INCLUDE ./Draw.hpp ./GLLoad.hpp ./OGLObjects.hpp - ./shader.hpp + ./ProgramPipeline.hpp ./Textures.hpp ./VertexArray.hpp ./Window.hpp diff --git a/engine/NBGraphics/OGLObjects.hpp b/engine/NBGraphics/OGLObjects.hpp index 64cd302..bb0dc8a 100644 --- a/engine/NBGraphics/OGLObjects.hpp +++ b/engine/NBGraphics/OGLObjects.hpp @@ -26,7 +26,7 @@ class OpenGLObject { public: OpenGLObject(const OpenGLObject&) = delete; OpenGLObject& operator=(const OpenGLObject&) = delete; - ~OpenGLObject() { remove(); } + virtual ~OpenGLObject() {}; virtual void bind() const = 0; virtual void unbind() const = 0; @@ -36,9 +36,9 @@ class OpenGLObject { OpenGLObject() = default; virtual GLuint declare() = 0; - virtual void remove() { this->remove(); }; + virtual void remove() = 0; - GLuint _id; + GLuint _id = 0; }; } // namespace nb diff --git a/engine/NBGraphics/ProgramPipeline.hpp b/engine/NBGraphics/ProgramPipeline.hpp new file mode 100644 index 0000000..fce51a7 --- /dev/null +++ b/engine/NBGraphics/ProgramPipeline.hpp @@ -0,0 +1,123 @@ +#pragma once +#ifndef _NB_SHADER +#define _NB_SHADER + +#include "GLLoad.hpp" + +#include + +#include +#include + +#include "OGLObjects.hpp" + +namespace nb { + +class ShaderError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class ProgramError : public Error { + using Base = Error; + + public: + using Base::Base; + + enum Codes : unsigned int { + UNDEFINED, LINKING_ERROR + }; + + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +enum OpenGLProfiles { + Core, + Compatibility, + ES +}; + +class Shader : public OpenGLObject { + using Base = OpenGLObject; + + public: + using Base::Base; + using Base::id; + Shader(GLenum, const std::string&); + Shader(Shader&&); + Shader& operator=(Shader&&) = delete; + ~Shader() { remove(); } + operator bool(); + virtual void bind() const override { /* TODO: Some warning of some kind perhaps*/ } + virtual void unbind() const override { /* TODO: Some warning of some kind perhaps*/ } + GLint status(GLenum) const; + std::string log() const; + const GLenum target; + friend Base; + + protected: + using Base::_id; + GLint _success; + virtual GLuint declare() override { + if (!_id) { + _id = _id = glCreateShader(target); + } + return _id; + } + + virtual void remove() override { + if (_id) { + glDeleteShader(_id); + } + } + +}; + +class Program : public OpenGLObject { + using Base = OpenGLObject; + + public: + using Base::Base; + using Base::id; + Program(SharedVector); + Program(Program&&); + Program& operator=(Program&&) = delete; + operator bool(); + ~Program() { remove(); } + virtual void bind() const override { + glUseProgram(_id); + } + virtual void unbind() const override { /* TODO: Some warning of some kind perhaps*/ } + GLint status(GLenum) const; + std::string log() const; + friend Base; + + protected: + using Base::_id; + GLint _success; + virtual GLuint declare() override { + if (!_id) { + _id = glCreateProgram(); + } + return _id; + } + virtual void remove() override { + if (_id) { + glDeleteProgram(_id); + } + } + +}; + +} +#endif \ No newline at end of file diff --git a/engine/NBGraphics/VertexArray.hpp b/engine/NBGraphics/VertexArray.hpp index be13f52..2f97c28 100644 --- a/engine/NBGraphics/VertexArray.hpp +++ b/engine/NBGraphics/VertexArray.hpp @@ -12,6 +12,27 @@ namespace nb { +static uint8_t GLSLTypeSize(GLenum type) { + switch(type) { + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_HALF_FLOAT: + return 2; + break; + case GL_INT: + case GL_UNSIGNED_INT: + case GL_FLOAT: + return 4; + break; + case GL_DOUBLE: + return 8; + break; + default: + return 1; + break; + } +} + struct VertexAttributeLayout { int32_t offset = 0; GLsizei stride = 0; @@ -53,42 +74,6 @@ class VAOError : public Error { static const ErrorCodeMap ErrorMessages; }; -template -class VertexBuffer : public ArrayBuffer{ - using Base = ArrayBuffer; - - public: - template - VertexBuffer(const VertexAttributeList& vas={}, Args... args) - : attributes(vas) { - if (sizeof...(Args)) { - data(args...); - } - } - VertexBuffer(VertexBuffer&& rhs) - : attributes(rhs.attributes), Base(std::move(rhs)){} - - - using Base::Target; - const VertexAttributeList attributes; - - protected: - using Base::_usage; - -}; - -template <> -class VertexBuffer -: public virtual VertexBuffer, public virtual ImmutableBuffer> { - using Base = ArrayBuffer; - using BufferType = ImmutableBuffer>; - using BufferType::BufferType; - - public: - using Base::Target; - -}; - class VAO : public OpenGLObject { using Base = OpenGLObject; using Codes = VAOError::Codes; @@ -139,25 +124,40 @@ class VAO : public OpenGLObject { }; +using VBO = ArrayBuffer; +using VertBufVec = SharedVector; +using EBO = ElementBuffer; + +struct VertexData { + std::shared_ptr vbo; + VertexAttributeList attrs; +}; + +using VertexDataVec = std::vector; + class VertexGroup { public: VertexGroup(VertexGroup&&); VertexGroup& operator=(VertexGroup&&); + + VertexGroup(const VertexData&); + VertexGroup(const VertexDataVec&); + VertexGroup(const ByteVector&, const VertexAttributeList&); void bind() const { - _elmt_buf.bind(); - _vao.bind(); + _ebo->bind(); + _vao->bind(); } void unbind() const { - _vao.bind(); - _elmt_buf.unbind(); + _vao->unbind(); + _ebo->unbind(); } - VertexBuffer& buffer(size_t); - size_t buffer(VertexBuffer&&); - std::vector>> buffers(); - size_t buffers(std::vector>&&); - VertexBuffer&& dropBuffer(size_t); + VertexDataVec getBuffers(); + VertexData getBuffers(size_t); + size_t setBuffers(const VertexDataVec&); + size_t addBuffer(const VertexData&); + VertexData dropBuffer(size_t); VertexAttributePointer attribute(size_t) const; void enableAttr(GLuint); @@ -168,9 +168,9 @@ class VertexGroup { void disable(); protected: - std::vector>> _vertex_data; - ElementBuffer _elmt_buf; - VAO _vao; + VertexDataVec _vertex_data; + std::shared_ptr _ebo; + std::shared_ptr _vao; }; } // namespace nb diff --git a/engine/NBGraphics/Window.hpp b/engine/NBGraphics/Window.hpp index 36e1d4b..b72cfa6 100644 --- a/engine/NBGraphics/Window.hpp +++ b/engine/NBGraphics/Window.hpp @@ -14,11 +14,6 @@ namespace nb { -class GLError : public std::runtime_error { -public: - GLError(const std::string&); -}; - class OpenGLError : public Error { using Base = Error; diff --git a/engine/NBGraphics/shader.hpp b/engine/NBGraphics/shader.hpp deleted file mode 100644 index ca92fe3..0000000 --- a/engine/NBGraphics/shader.hpp +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once -#ifndef _NB_SHADER -#define _NB_SHADER - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// #define _PREPROC_FUNC_PARAMS_ (const ShaderPreprocessor&, ShaderUnit&, unsigned int, const std::string&, std::vector&) - -namespace NB { - -class ShaderPreprocessorError : public std::runtime_error { -public: - enum Codes : unsigned char { - NONE, - FILE_NOT_FOUND, - BUILTIN_NOT_FOUND, - CUSTOM, - UNDEFINED - }; - - const Codes code; - - ShaderPreprocessorError(const std::string&, const std::string& file="", int line=-1); - ShaderPreprocessorError(Codes, const std::string& arg="", const std::string& file="", int line=-1); - -protected: - static std::string errorCodeParser(Codes, const std::string& arg=""); -}; - -class ShaderError : public std::runtime_error { -public: - ShaderError(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); - -protected: - std::string formatString(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); -}; - -struct File { - // typedef std::tuple FilePath; - struct FilePath { - std::string dir; - std::string basename; - std::string ext; - }; - FilePath path; - std::stringstream src; - std::map> include_map; - - File() {} - File(const File& rhs) { *this = rhs; } - - File& operator=(const File& rhs) { - path = rhs.path; - src.str(""); - src << rhs.src.rdbuf(); - include_map = rhs.include_map; - return *this; - } -}; - -// File::FilePath get_file_path(std::string); - -class ShaderPreprocessor; - -enum OpenGLProfiles { - Core, - Compatibility, - ES -}; - -struct ShaderUnit { - GLenum type = 0x0; - File file; - std::string preprocSource; - std::map defines; - short vMajor=0, vMinor=0; - OpenGLProfiles profile = Core; -}; - -class ShaderPreprocessor; - -class ShaderProgram { - friend ShaderPreprocessor; -public: - ShaderProgram() {} - ShaderProgram(const ShaderProgram& rhs) = delete; - ShaderProgram(ShaderProgram&& rhs); - - ~ShaderProgram(); - - ShaderProgram& operator=(const ShaderProgram& rhs) = delete; - ShaderProgram& operator=(ShaderProgram&& rhs); - - std::vector getShaders() const; - ShaderUnit getShaders(unsigned int) const; - void use() const; - unsigned int id() const; - - void setBool(const std::string& name, bool value) const; - void setInt(const std::string& name, int value) const; - void setUnsigned(const std::string& name, int value) const; - void setFloat(const std::string& name, float value) const; - void setMat4(const std::string& name, glm::mat4& value) const; - - static ShaderProgram CreateShaderProgram(std::vector&); - -private: - ShaderProgram(std::vector& shaders) : _shader_units(shaders){} - - std::vector _shader_units; - unsigned int _id; -}; - -class ShaderPreprocessor { -public: - typedef ShaderPreprocessorError::Codes Codes; - - enum TokenType { - TK, - DR, - WS, - NL, - LC, - BC - }; - static std::string TokenName(const TokenType&); - - std::map AcceptedExtensions = { - {".frag", GL_FRAGMENT_SHADER}, - {".fs", GL_FRAGMENT_SHADER}, - {".vert", GL_VERTEX_SHADER}, - {".vs", GL_VERTEX_SHADER}, - {".tess", 0x0}, - {".geom", GL_GEOMETRY_SHADER}, - {".comp", 0x0}, - {".shad", 0x0}, - {".glsl", 0x0} - }; - std::map AcceptedProfiles { - {"core", Core}, - {"compatibility", Compatibility}, - {"es", ES} - }; - std::vector Directories; - std::map BuiltIns; - - ShaderPreprocessor(); - - - File load(const std::string, bool builtin_first=false) const; - ShaderUnit& preprocess(const std::string&, ShaderUnit&) const; - ShaderUnit preprocess(File, GLenum shader_type=0x0) const; - ShaderUnit preprocess(const std::string&, GLenum shader_type=0x0) const; - ShaderProgram ReloadFromFile(const ShaderProgram& rhs) const; - ShaderProgram CreateShaderProgram(std::vector) const; - -private: - typedef std::pair Token; - typedef std::vector StringVec; - - inline bool directive_dispatch(ShaderUnit&, const std::string&) const; - inline std::vector tokenize(const std::string&) const; - - inline bool preprocessor_include(ShaderUnit&, const StringVec&, const std::string&) const; - inline bool preprocessor_version(ShaderUnit&, const StringVec&, const std::string&) const; - inline bool preprocessor_define(ShaderUnit&, const StringVec&, const std::string&) const; - // inline bool preprocessor_uniform(ShaderUnit&, const std::string&, const std::string&) const; - - File loadFromBase(const std::string, const std::string base="") const; - File loadFromDirectories(const std::string) const; - File loadFromBuiltIn(const std::string) const; - File load_BuiltInFirst(const std::string) const; - File load_FilesFirst(const std::string) const; -}; - -} -#endif \ No newline at end of file diff --git a/engine/NBGraphics/src/Buffers.cpp b/engine/NBGraphics/src/Buffers.cpp index 3f80a45..99379da 100644 --- a/engine/NBGraphics/src/Buffers.cpp +++ b/engine/NBGraphics/src/Buffers.cpp @@ -2,13 +2,18 @@ namespace nb { +const std::unordered_map BufferTypes({ + {GL_ARRAY_BUFFER, "GL_ARRAY_BUFFER"}, + {GL_ELEMENT_ARRAY_BUFFER, "GL_ELEMENT_BUFFER"} +}); + using BufferErrorCodes = BufferError::Codes; const std::string BufferError::type = "nb::BufferError"; const ErrorCodeMap BufferError::ErrorMessages = { { BufferErrorCodes::UNDEFINED, "Error" }, { BufferErrorCodes::DATA_OVERFLOW, "Attempting buffer overflow" }, { BufferErrorCodes::INVALID_BUFFER, "Attempting operation on invalid buffer" }, - {BufferErrorCodes::HANDLE_OVERWRITE, "Attempting to overwrite active buffer"} + { BufferErrorCodes::HANDLE_OVERWRITE, "Attempting to overwrite active buffer"} }; diff --git a/engine/NBGraphics/src/ProgramPipeline.cpp b/engine/NBGraphics/src/ProgramPipeline.cpp new file mode 100644 index 0000000..d4cea76 --- /dev/null +++ b/engine/NBGraphics/src/ProgramPipeline.cpp @@ -0,0 +1,75 @@ +#include "ProgramPipeline.hpp" + +namespace nb{ + +using ShaderErrCodes = ShaderError::Codes; +const std::string ShaderError::type = "nb::ShaderError"; +const ErrorCodeMap ShaderError::ErrorMessages = { + {ShaderErrCodes::UNDEFINED, "Error"} +}; + +using ProgramErrCodes = ProgramError::Codes; +const std::string ProgramError::type = "nb::ProgramError"; +const ErrorCodeMap ProgramError::ErrorMessages = { + {ProgramErrCodes::UNDEFINED, "Error"}, + {ProgramErrCodes::LINKING_ERROR, "Linker error"} +}; + +Shader::Shader(GLenum target_, const std::string& source_) : target(target_) { + declare(); + _success = status(GL_COMPILE_STATUS); +} + +Shader::Shader(Shader&& cpy) : target(cpy.target) { + _id = cpy._id; +} + +Shader::operator bool() { return _success; } + +GLint Shader::status(GLenum parameter) const { + GLint param = 0; + glGetShaderiv(_id, parameter, ¶m); + return param; +} + +std::string Shader::log() const { + GLint logsize = status(GL_INFO_LOG_LENGTH); + char* log_ = new char[logsize]; + glGetShaderInfoLog(_id, logsize, NULL, log_); + std::string ret(log_, logsize); + delete[] log_; + return ret; +} + +Program::Program(SharedVector shaders_) { + declare(); + for (auto shad_ptr : shaders_) { + glAttachShader(_id, shad_ptr->id()); + } + glLinkProgram(_id); + _success = status(GL_LINK_STATUS); + if (!_success) { + WARN(log(), 0x01); + } +} + +Program::operator bool() { return _success; } + +GLint Program::status(GLenum parameter) const { + GLint param = 0; + glGetProgramiv(_id, parameter, ¶m); + return param; +} + +std::string Program::log() const { + GLint logsize = status(GL_INFO_LOG_LENGTH); + char* log_ = new char[logsize]; + glGetProgramInfoLog(_id, logsize, NULL, log_); + std::string ret(log_, logsize); + delete[] log_; + return ret; +} + + + +} \ No newline at end of file diff --git a/engine/NBGraphics/src/VertexArray.cpp b/engine/NBGraphics/src/VertexArray.cpp index b84eebf..f7ae2d3 100644 --- a/engine/NBGraphics/src/VertexArray.cpp +++ b/engine/NBGraphics/src/VertexArray.cpp @@ -60,7 +60,7 @@ VertexAttributePointer VAO::attr(GLuint idx) const { return attr_ptr; } } - THROW(BufferError(Codes::INVALID_ATTRIBUTE)); + THROW(VAOError(Codes::INVALID_ATTRIBUTE)); } void VAO::enable() const { @@ -105,121 +105,127 @@ VertexGroup::VertexGroup(VertexGroup&& other) { VertexGroup& VertexGroup::operator=(VertexGroup&& rhs) { _vertex_data = rhs._vertex_data; - _elmt_buf = std::move(rhs._elmt_buf); - _vao = std::move(rhs._vao); + _ebo = rhs._ebo; + _vao = rhs._vao; rhs._vertex_data = {}; - rhs._elmt_buf = ElementBuffer(); - rhs._vao = VAO(); + rhs._ebo = nullptr; + rhs._vao = nullptr; return *this; } -size_t VertexGroup::buffer(VertexBuffer&& buf_) { - _vertex_data.emplace_back( - std::make_shared>(std::move(buf_)) - ); - VertexBuffer* buf_ptr = _vertex_data.back().get(); - VertexAttributePointerList attrs = _vao.attributes(); +VertexGroup::VertexGroup(const VertexDataVec& vertex_data_) +: _vao(std::make_shared()), _ebo(std::make_shared()) { + setBuffers(vertex_data_); +} + +VertexGroup::VertexGroup(const VertexData& vertex_data_) +: VertexGroup(VertexDataVec({vertex_data_})) {} + +VertexGroup::VertexGroup( + const ByteVector& data_, + const VertexAttributeList& attrs_ +) : VertexGroup({std::make_shared(data_), attrs_}) {} + +size_t VertexGroup::addBuffer(const VertexData& vertex_data_) { + VertexData data = vertex_data_; + _vertex_data.emplace_back(data); + VertexAttributePointerList attrs = _vao->attributes(); GLuint idx = attrs.size(); - for (auto attr : buf_ptr->attributes) { + for (auto attr : data.attrs) { attrs.emplace_back( - VertexAttributePointer{attr, buf_ptr->id(),idx} + VertexAttributePointer{attr, data.vbo->id(),idx} ); idx++; } - _vao.attributes(attrs); + _vao->attributes(attrs); return _vertex_data.size(); } -VertexBuffer& VertexGroup::buffer(size_t idx) { - return *_vertex_data[idx]; +VertexData VertexGroup::getBuffers(size_t idx) { + return _vertex_data[idx]; } -std::vector>> VertexGroup::buffers() { + VertexDataVec VertexGroup::getBuffers() { return _vertex_data; } -size_t VertexGroup::buffers(std::vector>&& bufs_) { - _vertex_data.clear(); - for (auto& buf : bufs_) { - _vertex_data.emplace_back( - std::make_shared>(std::move(buf)) - ); - } +size_t VertexGroup::setBuffers(const VertexDataVec& vertex_data_) { + _vertex_data = vertex_data_; VertexAttributePointerList attr_ptrs; GLuint idx = 0; - for (auto buf : _vertex_data) { - for (auto attr : buf->attributes) { + for (auto data : _vertex_data) { + for (auto attr : data.attrs) { attr_ptrs.emplace_back( - VertexAttributePointer{attr, buf->id(), idx} + VertexAttributePointer{attr, data.vbo->id(), idx} ); idx++; } } - _vao.attributes(attr_ptrs); + _vao->attributes(attr_ptrs); return _vertex_data.size(); } -VertexBuffer&& VertexGroup::dropBuffer(size_t idx) { +VertexData VertexGroup::dropBuffer(size_t idx) { if (idx >= _vertex_data.size()) { THROW(BufferError(BufferError::Codes::INVALID_BUFFER)); } - std::vector> tmp; - std::shared_ptr> ret_ptr; + VertexDataVec tmp; + VertexData ret; size_t i = 0; for (auto buf : _vertex_data) { if (i == idx) { - ret_ptr = std::make_shared>(std::move(tmp[idx])); + ret = tmp[idx]; } else { - tmp.emplace_back(std::move(*buf)); + tmp.emplace_back(buf); } i++; } - buffers(std::move(tmp)); - return std::move(*ret_ptr); + setBuffers(tmp); + return ret; } VertexAttributePointer VertexGroup::attribute(size_t idx_) const { - return _vao.attr(idx_); + return _vao->attr(idx_); } void VertexGroup::enableAttr(GLuint idx) { bind(); - _vao.enable(idx); + _vao->enable(idx); unbind(); } void VertexGroup::disableAttr(GLuint idx) { bind(); - _vao.disable(idx); + _vao->disable(idx); unbind(); } void VertexGroup::disableBuffer(size_t idx) { - auto buf_idx = _vertex_data[idx]->id(); + auto buf_idx = _vertex_data[idx].vbo->id(); bind(); - for (auto attr : _vao.attributes()) { + for (auto attr : _vao->attributes()) { if (attr.buffer == buf_idx) { - _vao.disable(attr.index); + _vao->disable(attr.index); } } unbind(); } void VertexGroup::enableBuffer(size_t idx) { - auto buf_idx = _vertex_data[idx]->id(); + auto buf_idx = _vertex_data[idx].vbo->id(); bind(); - for (auto attr : _vao.attributes()) { + for (auto attr : _vao->attributes()) { if (attr.buffer == buf_idx) { - _vao.enable(attr.index); + _vao->enable(attr.index); } } unbind(); } -void VertexGroup::enable() { _vao.enable(); } +void VertexGroup::enable() { _vao->enable(); } -void VertexGroup::disable() { _vao.disable(); } +void VertexGroup::disable() { _vao->disable(); } } // namespace nb diff --git a/engine/NBGraphics/src/Window.cpp b/engine/NBGraphics/src/Window.cpp index 5229af1..7cffeca 100644 --- a/engine/NBGraphics/src/Window.cpp +++ b/engine/NBGraphics/src/Window.cpp @@ -34,8 +34,6 @@ const ErrorCodeMap WindowError::ErrorMessages = { {WindowErrorCodes::INIT_FAILED, "Could not intialized window"} }; -GLError::GLError(const std::string& msg) : std::runtime_error(msg) {} - int Window::getGLFWHint(int hint_key) { if (GLFWHints.find(hint_key) == GLFWHints.end()) { return 0; diff --git a/engine/NBGraphics/src/shader.cpp b/engine/NBGraphics/src/shader.cpp deleted file mode 100644 index f24d86e..0000000 --- a/engine/NBGraphics/src/shader.cpp +++ /dev/null @@ -1,742 +0,0 @@ -#include "Shader.h" - -// #define _PREPROC_FUNC_PARAM_NAMES_ (const ShaderPreprocessor& proc, ShaderUnit& shad, unsigned int linenum, const std::string& line, std::vector& params) - -namespace NB{ - -File::FilePath get_file_path(std::string name) { - const std::vector allowed_special_chars = { - '.', '!', '#', '$', '%', '&', '\'', '(', ')', '+', ',', '-', ';', '=', '@', '[', ']', '^', '_', '`', '~' - }; - std::vector path = {""}; - for (const char c : name) { - if (c == '/' || c == '\\') { - path.push_back(""); - } else if (std::isalnum(c)) { - path.back() += c; - } else { - bool spec_char_found = false; - for (const char sc : allowed_special_chars) { - if (c == sc) { - path.back() += c; - spec_char_found = true; - break; - } - } - if (!spec_char_found) { - throw std::runtime_error("'" + name + "' is not valid filepath."); - } - } - } - File::FilePath ret; - for (const auto& tk : path) { - if (tk == path.back()) { - size_t period = tk.find("."); - if (period == std::string::npos) { - ret.basename = tk; - ret.ext = ""; - } else { - ret.basename = tk.substr(0, period); - ret.ext = tk.substr(period); - } - } else { - ret.dir += tk + "/"; - } - } - return ret; -} - -// ShaderPreprocessorError class -ShaderPreprocessorError::ShaderPreprocessorError( - const std::string& msg, - const std::string& file, - int line -) : code(Codes::UNDEFINED), std::runtime_error(formatDebugString(msg, file, line)) {} - -ShaderPreprocessorError::ShaderPreprocessorError( - Codes err_code, - const std::string& arg, - const std::string& file, - int line -) : code(err_code), std::runtime_error(formatDebugString(errorCodeParser(err_code, arg), file, line)) {} - -std::string ShaderPreprocessorError::errorCodeParser(Codes err_code, const std::string& arg) { - switch(err_code) { - case Codes::FILE_NOT_FOUND: - return "File '" + arg + "' not found."; - case Codes::BUILTIN_NOT_FOUND: - return "Built-in '" + arg + "' not found."; - case Codes::CUSTOM: - case Codes::UNDEFINED: - return arg; - case Codes::NONE: - default: - return ""; - } -} - -// ShaderError class -ShaderError::ShaderError( - const std::string& msg, - const char* shad, - const std::string& file, - int line -) : std::runtime_error(formatString(msg, shad, file, line)) {} - -std::string ShaderError::formatString( - const std::string& msg, - const char* shad, - const std::string& file, - int line -) { - std::stringstream ret; - if (file != "") { - ret << "In file " << file; - if (line >= 0) { - ret << " at line " << line; - } - ret << ":\n\t"; - } - ret << msg; - if (shad != nullptr) { - ret << " with shader error: " << shad; - } - return ret.str(); -} - -// ShaderPreprocessor class -std::string ShaderPreprocessor::TokenName(const ShaderPreprocessor::TokenType& x) { - switch(x) { - case TK: - return "Token"; - break; - case DR: - return "Directive"; - break; - case WS: - return "Whitespace"; - break; - case NL: - return "NewLine"; - break; - case LC: - return "LineComment"; - break; - case BC: - return "BlockComment"; - break; - default: - return "Unrecognized"; - break; - } -} - -ShaderPreprocessor::ShaderPreprocessor() {} - -File ShaderPreprocessor::loadFromBase(const std::string path, const std::string base) const { - File ret; - std::ifstream filestream; - filestream.open(base + path); - if (filestream.is_open()) { - ret.path = get_file_path(base + path); - ret.src << filestream.rdbuf(); - return ret; - } - filestream.close(); - std::string ext; - for (const auto& kv : AcceptedExtensions) { - ext = kv.first; - filestream.open(base + path + ext); - if (filestream.is_open()) { - ret.path = get_file_path(base + path + ext); - ret.src << filestream.rdbuf(); - return ret; - } - filestream.close(); - } - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, base + path); -} - -File ShaderPreprocessor::loadFromDirectories(const std::string name) const { - File ret; - std::ifstream fstream; - for (const std::string& path : Directories) { - fstream.open(path + name); - if (fstream.is_open()) { - ret.path = get_file_path(path + name); - ret.src << fstream.rdbuf(); - return ret; - } - fstream.close(); - } - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, name); -} - -File ShaderPreprocessor::load(const std::string path, bool builtin_first) const { - if(builtin_first) { - return load_BuiltInFirst(path); - } - return load_FilesFirst(path); -} - -File ShaderPreprocessor::loadFromBuiltIn(const std::string name) const { - File ret; - //std::stringstream ret; - decltype(BuiltIns)::const_iterator builtin_it = BuiltIns.find(name); - if (builtin_it != BuiltIns.end()) { - ret.path = File::FilePath{"builtin:", name, ""}; - ret.src << builtin_it->second; - return ret; - } - throw ShaderPreprocessorError(Codes::BUILTIN_NOT_FOUND, name); -} - -File ShaderPreprocessor::load_FilesFirst(const std::string path) const { - try { - return loadFromBase(path); - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - try { - return loadFromDirectories(path); - } catch (ShaderPreprocessorError f) { - if (f.code == Codes::FILE_NOT_FOUND) { - try { - return loadFromBuiltIn(path); - } catch(ShaderPreprocessorError g) { - if (g.code == Codes::BUILTIN_NOT_FOUND) { - throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, path); - } else { throw g; } - } - } else { throw f; } - } - } else { throw e; } - } -} - -File ShaderPreprocessor::load_BuiltInFirst(const std::string path) const { - try { - return loadFromBuiltIn(path); - } catch (ShaderPreprocessorError f) { - if (f.code == Codes::BUILTIN_NOT_FOUND) { - try { - return loadFromDirectories(path); - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - return loadFromBase(path); - } else { - throw e; - } - } - } else { - throw f; - } - } - -} - -std::vector ShaderPreprocessor::tokenize(const std::string& code) const { - enum State { - FSlash, - WhiteSpace, - LineComment, - BlockComment, - BlockCommentEndStar, - Directive, - Token - }; - - std::vector tks; - - std::string token = ""; - State state = WhiteSpace; - for(char c : code) { - if (c==13) { - continue; - } - switch(state) { - case WhiteSpace: - if (c=='/') { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = FSlash; - } else if (c=='#') { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = Directive; - } else if (c=='\n') { - if (token != "") { tks.push_back({WS, token}); } - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - token += c; - } else { - if (token != "") { tks.push_back({WS, token}); } - token = c; - state = Token; - } - break; - case FSlash: - if (c=='/') { - token += c; - state = LineComment; - } else if (c=='*') { - token += c; - state = BlockComment; - } else if (c=='\n') { - tks.push_back({TK, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - tks.push_back({TK, token}); - token = c; - }else { - token += c; - state = Token; - } - break; - case LineComment: - if (c=='\n') { - tks.push_back({LC, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else { - token += c; - } - break; - case BlockComment: - token += c; - if (c=='*') { - state = BlockCommentEndStar; - } - break; - case BlockCommentEndStar: - token += c; - if (c=='/') { - tks.push_back({BC, token}); - token = ""; - state = WhiteSpace; - } else { - state = BlockComment; - } - break; - case Directive: - if (c=='\n') { - tks.push_back({DR, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (c=='/') { - tks.push_back({DR, token}); - token = c; - state = FSlash; - } else { - token += c; - } - break; - case Token: - if (c=='\n') { - tks.push_back({TK, token}); - tks.push_back({NL, "\n"}); - token = ""; - state = WhiteSpace; - } else if (std::isblank(c)) { - tks.push_back({TK, token}); - token = c; - state = WhiteSpace; - } else if (c=='/') { - tks.push_back({TK, token}); - token = c; - state = FSlash; - }else { - token += c; - } - break; - default: - break; - } - - } - - switch(state) { - case WhiteSpace: - if (token != "") { tks.push_back({WS, token}); } - case FSlash: - tks.push_back({TK, token}); - break; - case LineComment: - tks.push_back({LC, token}); - break; - case BlockComment: - case BlockCommentEndStar: - tks.push_back({BC, token}); - break; - case Directive: - tks.push_back({DR, token}); - break; - case Token: - tks.push_back({TK, token}); - break; - default: - break; - } - - return tks; -} - -ShaderUnit& ShaderPreprocessor::preprocess(const std::string& code, ShaderUnit& shad) const { - typedef ShaderPreprocessor::Token Token; - std::vector tks = tokenize(code); - - for (int i{0}; i < tks.size(); ++i) { - switch(tks[i].first) { - case DR: - directive_dispatch(shad, tks[i].second); - break; - case TK: - case NL: - case LC: - case BC: - case WS: - default: - shad.preprocSource += tks[i].second; - break; - } - } - - if (shad.vMajor == 0 && shad.vMinor == 0) { - shad.vMajor = 1; - shad.vMinor = 10; - } - return shad; -} - -ShaderUnit ShaderPreprocessor::preprocess(File file, GLenum shader_type) const { - ShaderUnit ret; - ret.file = file; - if (shader_type) { - ret.type = shader_type; - } else { - decltype(AcceptedExtensions.begin()) find_type = AcceptedExtensions.find(file.path.ext); - if (find_type != AcceptedExtensions.end()) { - ret.type = find_type->second; - } - } - preprocess(file.src.str(), ret); - return ret; -} - -ShaderUnit ShaderPreprocessor::preprocess(const std::string& code, GLenum shader_type) const { - File local; - local.path = File::FilePath{"live:", "live", ".shad"}; - return preprocess(local, shader_type); -} - -ShaderProgram ShaderPreprocessor::CreateShaderProgram(std::vector shads) const { - std::vector _shader_units; - for (const auto& name : shads) { - _shader_units.push_back(preprocess(load(name))); - } - return ShaderProgram::CreateShaderProgram(_shader_units); -} - -ShaderProgram ShaderPreprocessor::ReloadFromFile(const ShaderProgram& rhs) const { - std::vector shader_names; - shader_names.reserve(rhs._shader_units.size()); - std::string filename; - for (const ShaderUnit& shad : rhs._shader_units) { - filename = shad.file.path.dir + shad.file.path.basename + shad.file.path.ext; - shader_names.emplace_back(filename); - } - return CreateShaderProgram(shader_names); -} - -bool ShaderPreprocessor::directive_dispatch(ShaderUnit& shad, const std::string& line) const { - StringVec dir_tks = {""}; - for (char c : line) { - if (std::isblank(c)) { - if (dir_tks.back() != "") { - dir_tks.push_back(""); - } - } else { - dir_tks.back() += c; - } - } - if (!dir_tks.size()) { return false; } - - if (dir_tks[0][0] != '#') { return false; } - - if (dir_tks[0] == "#define") { - return preprocessor_define(shad, dir_tks, line); - } else if (dir_tks[0] == "#version") { - return preprocessor_version(shad, dir_tks, line); - } else if (dir_tks[0] == "#include") { - return preprocessor_include(shad, dir_tks, line); - } - return false; -} - -typedef std::vector StringVec; - -bool ShaderPreprocessor::preprocessor_include( - ShaderUnit& shad, - const StringVec& tokens, - const std::string& line -) const -{ - try { - if (tokens[0] != "#include") { return false; } - } catch (std::out_of_range e) { - return false; - } - std::string path = ""; - for (const auto& tk : tokens) { - if (tk != tokens.front()) { - path += tk; - } - } - // Add file-inclusion base + - // Do path cleanup + - // Restructure preprocessing data flow - std::string filename = path.substr(1, path.size()-2); - try { - if (path[0] == '"' && path.back() == '"') { - preprocess(load(filename).src.str(), shad); - return true; - } else if (path[0] == '<' && path.back() == '>') { - preprocess(load(filename, true).src.str(), shad); - return true; - } - } catch (ShaderPreprocessorError e) { - if (e.code == Codes::FILE_NOT_FOUND) { - std::cout << "COULD NOT FIND " << filename << ".\n"; - } else { - throw e; - } - } - return false; -} - -bool ShaderPreprocessor::preprocessor_define(ShaderUnit& shad, const StringVec& tokens, const std::string& line) const { - shad.preprocSource += line; - try { - if (tokens[0] != "#define") { return false; } - try { - shad.defines[tokens[1]] = tokens[2]; - } catch (std::length_error& f) { - shad.defines[tokens[1]] = ""; - } - return true; - } catch (std::out_of_range& e) { - return false; - } -} - -bool ShaderPreprocessor::preprocessor_version(ShaderUnit& shad, const StringVec& tokens, const std::string& line) const { - shad.preprocSource += line; - std::cout << "Shader version: "; - try { - if (tokens[0] != "#version") { return false; } - if (tokens[1].size() == 3) { - short vMajor = tokens[1][0]-'0'; - short vMinorA = tokens[1][1]-'0'; - short vMinorB = tokens[1][2]-'0'; - if (vMajor > 10 || vMinorA > 10 || vMinorB > 10) { - return false; - } - shad.vMajor = vMajor; - shad.vMinor = vMinorA*10 + vMinorB; - shad.profile = AcceptedProfiles.at(tokens[2]); - std::cout << shad.vMajor << "." << shad.vMinor << "\n"; - return true; - } else { - return false; - } - } catch (std::out_of_range& e) { - return false; - } -} - - -/* bool ShaderPreprocessor::preprocessor_uniform( - ShaderUnit& shad, - const std::string& type, - const std::string& name -) const { - std::string type_str = ""; - unsigned int type_str_len = 0; - for (const char& c : type) { - if (type_str_len) { - if (std::isdigit(c)) { - type_str += c; - continue; - } else if (c=='[') { - type_str += '_'; - }else if (c == ']' || std::isblank(c)){ - continue; - } else { - return false; - } - } else { - if (std::isalnum(c)) { - type_str += c; - continue; - } else if (c == '[') { - type_str_len = type_str.length(); - type_str += '_'; - continue; - } else { - return false; - } - } - } - std::string name_str = ""; - unsigned int name_str_len = 0; - for (const char& c : name) { - if (c == ';') { - break; - } - if (name_str_len) { - if (std::isdigit(c)) { - type_str.insert(type_str_len, 1, c); - type_str_len++; - continue; - } else if (c=='[') { - type_str.insert(type_str_len, "_"); - type_str_len++; - continue; - }else if (c == ']' || std::isblank(c)){ - continue; - } else { - return false; - } - } else { - if (std::isalnum(c)) { - name_str += c; - continue; - } else if ( c == '[') { - name_str_len = name_str.length(); - type_str.insert(type_str_len, "_"); - type_str_len++; - continue; - } else { - return false; - } - } - } - shad.uniforms.push_back(UniformHandle{ - name_str, - type_str, - 0x0, - 0x0 - }); - return true; -} */ - - -// ShaderProgram - -ShaderProgram::ShaderProgram(ShaderProgram&& rhs) { - _shader_units = rhs._shader_units; - _id = rhs._id; - rhs._id = 0; -} - -ShaderProgram::~ShaderProgram() { - glDeleteProgram(_id); -} - -ShaderProgram& ShaderProgram::operator=(ShaderProgram&& rhs) { - _shader_units = rhs._shader_units; - _id = rhs._id; - rhs._id = 0; - return *this; -} - - -ShaderProgram ShaderProgram::CreateShaderProgram(std::vector& shaders) { - int success; - ShaderProgram ret(shaders); - char infoLog[512]; - unsigned int shad_id; - std::vector shad_ids; - shad_ids.reserve(shaders.size()); - for (auto& shad : ret._shader_units) { - const char* source = shad.preprocSource.data(); - shad_id = glCreateShader(shad.type); - shad_ids.emplace_back(shad_id); - glShaderSource(shad_id, 1, &source, NULL); - glCompileShader(shad_id); - glGetShaderiv(shad_id, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(shad_id, 512, NULL, infoLog); - File::FilePath& fp = shad.file.path; - std::string filename = fp.dir + fp.basename + fp.ext; - // std::cout << "Could not compile '" + filename + "': " << infoLog << "\n"; - // return *ret; - throw ShaderError("Could not compile '" + filename + "'.", infoLog); - } - } - - ret._id = glCreateProgram(); - for(auto& id : shad_ids) { - glAttachShader(ret._id, id); - } - glLinkProgram(ret._id); - glGetProgramiv(ret._id, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(ret._id, 512, NULL, infoLog); - throw ShaderError("Could not link shader program.", infoLog); - } - for (auto& id : shad_ids) { - glDeleteShader(id); - } - return ret; -} - -unsigned int ShaderProgram::id() const { return _id; } - -void ShaderProgram::use() const { - glUseProgram(_id); -} - -std::vector ShaderProgram::getShaders() const { - return _shader_units; -} - -ShaderUnit ShaderProgram::getShaders(unsigned int idx) const { - return _shader_units[idx]; -} - -void ShaderProgram::setBool(const std::string& name, bool value) const { - glUniform1i(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setInt(const std::string& name, int value) const { - glUniform1i(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setFloat(const std::string& name, float value) const { - glUniform1f(glGetUniformLocation(_id, name.c_str()), (int)value); -} - -void ShaderProgram::setMat4(const std::string& name, glm::mat4& value) const { - glUniformMatrix4fv(glGetUniformLocation(_id, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); -} - -/* // Shader class -Shader::Shader() { _id = 0x0; } - -Shader::Shader(const Shader& cpy_shader) { _id = cpy_shader._id; } - -Shader& Shader::operator=(const Shader& cpy_shader) { _id = cpy_shader._id; return *this; } - -void Shader::use() const{ - glUseProgram(_id); -} - -*/ - -} \ No newline at end of file diff --git a/engine/NBGraphics/tests/CMakeLists.txt b/engine/NBGraphics/tests/CMakeLists.txt index 62ecefc..8e6d9d6 100644 --- a/engine/NBGraphics/tests/CMakeLists.txt +++ b/engine/NBGraphics/tests/CMakeLists.txt @@ -9,8 +9,6 @@ if (NB_BUILD_TESTS) target_link_libraries(TestWindow NBCore NBGraphics -# GTest::gtest_main ) -# gtest_discover_tests(TestWindow) endif() \ No newline at end of file diff --git a/engine/NBGraphics/tests/TestWindow.cpp b/engine/NBGraphics/tests/TestWindow.cpp index 03a7c0e..765c9e6 100644 --- a/engine/NBGraphics/tests/TestWindow.cpp +++ b/engine/NBGraphics/tests/TestWindow.cpp @@ -1,9 +1,8 @@ #include "GLLoad.hpp" -// #define _NB_AUTOLOG +#include "ProgramPipeline.hpp" #include "VertexArray.hpp" #include "Window.hpp" - int main() { nb::logger.log("Howdy!"); nb::Window window(200, 200, "Hello!"); @@ -11,8 +10,53 @@ int main() { window.setWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); window.init(); + nb::logger.log("Goob"); + + auto vert = std::make_shared( + GL_VERTEX_SHADER, + "#version 330 core\n" + "layout (location = 0) in vec2 aPos;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);\n" + "}\0" + ); + + LOG("Vertex Shader: " + vert->log()); + + auto frag = std::make_shared( + GL_FRAGMENT_SHADER, + "#version 330 core\n" + "out vec4 FragColor;\n" + "void main()\n" + "{\n" + " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" + "}\n\0" + ); + + LOG("Fragment shader: " + frag->log()); + + nb::ByteVector data = nb::vectorToBytes({ + -0.5, -0.5, 0.5, -0.5, 0.0, 0.5 + }); + + nb::Program prog({vert, frag}); + prog.bind(); + + nb::VertexGroup tri(data, { + nb::VertexAttribute{ + 2, + GL_FLOAT, + false, + {0, 8} + } + }); + tri.bind(); + GLFWwindow* window_ptr = window.getWindow(); - while(!glfwWindowShouldClose(window_ptr)) {} + while(!glfwWindowShouldClose(window_ptr)) { + glDrawArrays(GL_TRIANGLES, 0, 3); + } return 0; } \ No newline at end of file