diff --git a/include/blt/gfx/model.h b/include/blt/gfx/model.h index 222a9f7..93c7dcc 100644 --- a/include/blt/gfx/model.h +++ b/include/blt/gfx/model.h @@ -41,14 +41,20 @@ namespace blt::gfx { GLuint bufferID_ = 0; GLsizeiptr size_ = 0; - GLint buffer_type; - GLint memory_type; + GLint buffer_type = 0; + GLint memory_type = 0; - void create(GLint type); + void create(GLint type = GL_ARRAY_BUFFER); void bind() const; - void allocate(GLsizeiptr size, GLint memory_type = GL_STATIC_DRAW, void* data = nullptr); + void allocate(GLsizeiptr size, GLint mem_type = GL_STATIC_DRAW, const void* data = nullptr); + + template + void allocate(GLsizeiptr size, const T* data, GLint mem_type = GL_STATIC_DRAW) + { + allocate(size, mem_type, static_cast(data)); + } void sub_update(GLsizeiptr offset, GLsizeiptr size, void* data); @@ -105,7 +111,7 @@ namespace blt::gfx private: GLuint vaoID; static_dynamic_array VBOs; - HASHSET used_attributes; + HASHSET used_attributes; vbo_t element; public: vertex_array(); @@ -125,10 +131,24 @@ namespace blt::gfx * @param coordinate_size size of the data (number of * @param type type of data * @param stride how many bytes this data takes (for the entire per-vertex data structure) 0 will assume packed data + * This is in effect how many bytes until the next block of data * @param offset offset into the data structure to where the data is stored */ void bindVBO(const vbo_t& vbo, int attribute_number, int coordinate_size, GLenum type, int stride, long offset); + inline void bindElement(const vbo_t& vbo) + { + bind(); + element = vbo; + vbo.bind(); + unbind(); + } + + inline vbo_t& getElement() + { + return element; + } + /** * Returns a non-owning reference to a vbo allowing for updating the VBO * The VBO is considered invalid if its ID is 0 @@ -143,6 +163,11 @@ namespace blt::gfx glBindVertexArray(vaoID); } + static inline void unbind() + { + glBindVertexArray(0); + } + ~vertex_array(); }; diff --git a/include/blt/gfx/shader.h b/include/blt/gfx/shader.h index 11fac50..9e44c03 100644 --- a/include/blt/gfx/shader.h +++ b/include/blt/gfx/shader.h @@ -64,7 +64,7 @@ namespace blt::gfx } }; - class shader_base + class shader_base_t { friend uniform_buffer; protected: @@ -139,12 +139,12 @@ namespace blt::gfx /** * a basic computer shader class, contains the functions and resources required to use compute shaders! */ - class compute_shader : public shader_base + class compute_shader_t : public shader_base_t { private: GLuint shaderID = 0; public: - explicit compute_shader(const std::string& shader_source, bool loadAsString = true); + explicit compute_shader_t(const std::string& shader_source, bool loadAsString = true); inline void execute(int x, int y, int z) const { @@ -152,10 +152,10 @@ namespace blt::gfx glDispatchCompute(x, y, z); } - ~compute_shader(); + ~compute_shader_t(); }; - class shader : public shader_base + class shader_t : public shader_base_t { private: GLuint vertexShaderID = 0; @@ -171,9 +171,9 @@ namespace blt::gfx * @param geometry geometry shader source or file (optional) * @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false) */ - shader(const std::string& vertex, const std::string& fragment, bool load_as_string = true); + shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string = true); - shader(shader&& move) noexcept; + shader_t(shader_t&& move) noexcept; // used to set the location of VAOs to the in variables in opengl shaders. void bindAttribute(int attribute, const std::string& name) const; @@ -181,7 +181,7 @@ namespace blt::gfx // used to set location of shared UBOs like the perspective and view matrix void setUniformBlockLocation(const std::string& name, int location) const; - ~shader(); + ~shader_t(); }; } diff --git a/include/blt/gfx/texture.h b/include/blt/gfx/texture.h index e85d5d3..02feefe 100644 --- a/include/blt/gfx/texture.h +++ b/include/blt/gfx/texture.h @@ -42,7 +42,7 @@ namespace blt::gfx texture_data(int width, int height, int channels = 4): m_width(width), m_height(height), m_channels(channels) { - m_data = static_cast(STBI_MALLOC(width * height * channels)); + m_data = static_cast(std::malloc(width * height * channels)); } texture_data() = default; @@ -74,7 +74,7 @@ namespace blt::gfx ~texture_data() { - STBI_FREE(m_data); + std::free(m_data); } }; @@ -127,12 +127,8 @@ namespace blt::gfx GLint textureColorMode; int m_width, m_height; - texture_gl( - int width, int height, GLint bind_type = GL_TEXTURE_2D, - GLint color_mode = GL_RGBA - ): - textureBindType(bind_type), textureColorMode(color_mode), m_width(width), - m_height(height) + texture_gl(int width, int height, GLint bind_type = GL_TEXTURE_2D, GLint color_mode = GL_RGBA): + textureBindType(bind_type), textureColorMode(color_mode), m_width(width), m_height(height) { glGenTextures(1, &textureID); } @@ -144,6 +140,7 @@ namespace blt::gfx { glGenerateMipmap(textureBindType); } + public: inline void bind() const { @@ -169,51 +166,19 @@ namespace blt::gfx struct texture_gl2D : public texture_gl { public: - texture_gl2D(int width, int height, GLint colorMode = GL_RGBA): - texture_gl(width, height, GL_TEXTURE_2D, colorMode) - { - bind(); - setDefaults(); - // TODO: - const int MIPMAP_LEVELS = 4; - glTexStorage2D( - textureBindType, MIPMAP_LEVELS, colorMode, - width, height - ); - } + texture_gl2D(const texture_data& data); + + texture_gl2D(int width, int height, GLint colorMode = GL_RGBA8); void upload(void* data, GLint dataColorMode = GL_RGBA, int level = 0, int x_offset = 0, int y_offset = 0, int sub_width = -1, - int sub_height = -1) const - { - if (sub_width < 0) - sub_width = m_width; - if (sub_height < 0) - sub_height = m_height; - bind(); - glTexSubImage2D( - textureBindType, level, x_offset, y_offset, sub_width, sub_height, - dataColorMode, GL_UNSIGNED_BYTE, data - ); - generateMipmaps(); - unbind(); - } + int sub_height = -1) const; - void upload(const texture_file& tex_file) const - { - upload(tex_file.texture().data(), tex_file.channels() == 4 ? GL_RGBA : GL_RGB, 0, 0, 0, tex_file.width(), tex_file.height()); - } + void upload(const texture_file& tex_file) const; /** * Resizes the internal memory for the texture but does NOT resize the texture image stored */ - inline void resize(int width, int height) - { - m_width = width; - m_height = height; - bind(); - glTexStorage2D(textureBindType, 0, textureColorMode, m_width, m_height); - unbind(); - } + inline void resize(int width, int height); }; struct gl_texture2D_array : public texture_gl @@ -221,31 +186,10 @@ namespace blt::gfx protected: int m_layers; public: - gl_texture2D_array(int width, int height, int layers, GLint colorMode = GL_RGBA8): - texture_gl(width, height, GL_TEXTURE_2D_ARRAY, colorMode), m_layers(layers) - { - bind(); - setDefaults(); - // 6+ mipmaps is about where I stop noticing any difference (size is 4x4 pixels, so that makes sense) - glTexStorage3D(textureBindType, 6, colorMode, width, height, layers); - BLT_DEBUG("Creating 2D Texture Array with ID: %d", textureID); - } + gl_texture2D_array(int width, int height, int layers, GLint colorMode = GL_RGBA8); void upload(void* data, int index, GLint dataColorMode = GL_RGBA, int level = 0, int x_offset = 0, int y_offset = 0, int sub_width = -1, - int sub_height = -1) const - { - if (sub_width < 0) - sub_width = m_width; - if (sub_height < 0) - sub_height = m_height; - bind(); - glTexSubImage3D( - textureBindType, level, x_offset, y_offset, index, sub_width, sub_height, 1, - dataColorMode, GL_UNSIGNED_BYTE, data - ); - generateMipmaps(); - unbind(); - } + int sub_height = -1) const; }; class gl_buffer_texture : public texture_gl2D diff --git a/resources/textures/cumdollar.jpg b/resources/textures/cumdollar.jpg new file mode 100644 index 0000000..6069363 Binary files /dev/null and b/resources/textures/cumdollar.jpg differ diff --git a/src/blt/gfx/model.cpp b/src/blt/gfx/model.cpp index 94febf6..20576f0 100644 --- a/src/blt/gfx/model.cpp +++ b/src/blt/gfx/model.cpp @@ -29,21 +29,19 @@ namespace blt::gfx if (std::holds_alternative(data_)) { auto* ptr = std::get(data_); - std::memcpy(new_data, ptr, size_); + std::memcpy(new_data, ptr, sizeof(vbo_t) * size_); delete[] ptr; - } else { + } else + { auto data = std::get(data_); - std::memcpy(new_data, data.data(), DATA_SIZE); + std::memcpy(new_data, data.data(), sizeof(vbo_t) * DATA_SIZE); } data_ = new_data; size_ = next_size; } static_dynamic_array::static_dynamic_array() - { - auto ptr = std::get(data_); - std::memset(ptr.data(), 0, DATA_SIZE); - } + {} vbo_t& static_dynamic_array::operator[](size_t index) { @@ -72,8 +70,9 @@ namespace blt::gfx glDeleteBuffers(1, &bufferID_); } - void vbo_t::allocate(GLsizeiptr size, GLint mem_type, void* data) + void vbo_t::allocate(GLsizeiptr size, GLint mem_type, const void* data) { + bind(); size_ = size; glBufferData(buffer_type, size, data, mem_type); memory_type = mem_type; @@ -90,6 +89,7 @@ namespace blt::gfx void vbo_t::sub_update(GLsizeiptr offset, GLsizeiptr size, void* data) { + bind(); glBufferSubData(buffer_type, offset, size, data); } @@ -131,5 +131,7 @@ namespace blt::gfx glEnableVertexAttribArray(attribute_number); VBOs[attribute_number] = vbo; + + unbind(); } } \ No newline at end of file diff --git a/src/blt/gfx/shader.cpp b/src/blt/gfx/shader.cpp index 78463f1..50b3edb 100644 --- a/src/blt/gfx/shader.cpp +++ b/src/blt/gfx/shader.cpp @@ -34,7 +34,7 @@ namespace blt::gfx return new_source_string; } - unsigned int shader::createShader(const std::string& source, int type) { + unsigned int shader_t::createShader(const std::string& source, int type) { const char* shader_code = source.c_str(); // creates a Shader unsigned int shaderID = glCreateShader(type); @@ -68,7 +68,7 @@ namespace blt::gfx return shaderID; } - shader::shader(const std::string& vertex, const std::string& fragment, bool load_as_string) { + shader_t::shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string) { // load shader sources std::string vertex_source = vertex; std::string fragment_source = fragment; @@ -118,12 +118,12 @@ namespace blt::gfx glUseProgram(0); } - void shader::bindAttribute(int attribute, const std::string &name) const { + void shader_t::bindAttribute(int attribute, const std::string &name) const { bind(); glBindAttribLocation(programID, attribute, name.c_str()); } - void shader::setUniformBlockLocation(const std::string &name, int location) const { + void shader_t::setUniformBlockLocation(const std::string &name, int location) const { bind(); auto blockID = glGetUniformBlockIndex(programID, name.c_str()); if (blockID != GL_INVALID_INDEX) @@ -132,7 +132,7 @@ namespace blt::gfx BLT_WARN("Unable to find uniform buffer '%s'. Did you forget to declare it?", name.c_str()); } - shader::~shader() { + shader_t::~shader_t() { glUseProgram(0); // shader was moved if (programID <= 0) @@ -149,7 +149,7 @@ namespace blt::gfx glDeleteProgram(programID); } - shader::shader(shader&& move) noexcept { + shader_t::shader_t(shader_t&& move) noexcept { // the move constructor doesn't need to construct a new shader but it does need to ensure all old variables are moved over programID = move.programID; vertexShaderID = move.vertexShaderID; diff --git a/src/blt/gfx/texture.cpp b/src/blt/gfx/texture.cpp index a93d487..c2ec470 100644 --- a/src/blt/gfx/texture.cpp +++ b/src/blt/gfx/texture.cpp @@ -21,20 +21,25 @@ #define STB_PERLIN_IMPLEMENTATION #include + #if defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Warray-bounds" + #include + #pragma GCC diagnostic pop #else #include #endif + #include #include #include blt::gfx::texture_file::texture_file(const std::string& path, const std::string& name): m_name(name.empty() ? path : name), m_path(path) { + stbi_set_flip_vertically_on_load(true); m_texture.m_data = stbi_load( m_path.c_str(), reinterpret_cast(&m_texture.m_width), reinterpret_cast(&m_texture.m_height), @@ -89,3 +94,74 @@ void blt::gfx::texture_gl::setDefaults() const glTexParameterf(textureBindType, GL_TEXTURE_MAX_ANISOTROPY_EXT, a); #endif } + +void blt::gfx::texture_gl2D::upload(void* data, GLint dataColorMode, int level, int x_offset, int y_offset, int sub_width, int sub_height) const +{ + if (sub_width < 0) + sub_width = m_width; + if (sub_height < 0) + sub_height = m_height; + bind(); + glTexSubImage2D(textureBindType, level, x_offset, y_offset, sub_width, sub_height, dataColorMode, GL_UNSIGNED_BYTE, data); + unbind(); +} + +void blt::gfx::texture_gl2D::upload(const blt::gfx::texture_file& tex_file) const +{ + upload(tex_file.texture().data(), tex_file.channels() == 4 ? GL_RGBA : GL_RGB, 0, 0, 0, tex_file.width(), tex_file.height()); +} + +void blt::gfx::texture_gl2D::resize(int width, int height) +{ + m_width = width; + m_height = height; + bind(); + glTexStorage2D(textureBindType, 0, textureColorMode, m_width, m_height); + unbind(); +} + +blt::gfx::texture_gl2D::texture_gl2D(int width, int height, GLint colorMode): texture_gl(width, height, GL_TEXTURE_2D, colorMode) +{ + bind(); + setDefaults(); + // TODO: + const int MIPMAP_LEVELS = 4; + glTexStorage2D(textureBindType, MIPMAP_LEVELS, colorMode, width, height); +} + +blt::gfx::texture_gl2D::texture_gl2D(const blt::gfx::texture_data& data): texture_gl(data.width(), data.height(), GL_TEXTURE_2D, GL_RGBA8) +{ + bind(); + setDefaults(); + glTexStorage2D(textureBindType, 4, GL_RGBA8, data.width(), data.height()); + upload((void*) data.data(), data.channels() == 4 ? GL_RGBA : GL_RGB, 0, 0, 0, data.width(), data.height()); + bind(); + //glTexImage2D(textureBindType, 0, GL_RGBA, data.width(), data.height(), 0, data.channels() == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, (void*)data.data()); + generateMipmaps(); + unbind(); +} + +void blt::gfx::gl_texture2D_array::upload(void* data, int index, GLint dataColorMode, int level, int x_offset, int y_offset, int sub_width, + int sub_height) const +{ + if (sub_width < 0) + sub_width = m_width; + if (sub_height < 0) + sub_height = m_height; + bind(); + glTexSubImage3D( + textureBindType, level, x_offset, y_offset, index, sub_width, sub_height, 1, + dataColorMode, GL_UNSIGNED_BYTE, data + ); + generateMipmaps(); + unbind(); +} + +blt::gfx::gl_texture2D_array::gl_texture2D_array(int width, int height, int layers, GLint colorMode): texture_gl(width, height, layers, colorMode) +{ + bind(); + setDefaults(); + // 6+ mipmaps is about where I stop noticing any difference (size is 4x4 pixels, so that makes sense) + glTexStorage3D(textureBindType, 6, colorMode, width, height, layers); + BLT_DEBUG("Creating 2D Texture Array with ID: %d", textureID); +} diff --git a/tests/include/shaders/test.frag b/tests/include/shaders/test.frag new file mode 100644 index 0000000..069fa53 --- /dev/null +++ b/tests/include/shaders/test.frag @@ -0,0 +1,23 @@ +#ifdef __cplusplus +#include +std::string shader_test_frag = R"(" +#version 300 es +precision mediump float; + +out vec4 FragColor; +in vec2 uv; +in vec2 pos; + +uniform sampler2D tex; + +vec4 linear_iter(vec4 i, vec4 p, float factor){ + return (i + p) * factor; +} + +void main() { + //FragColor = vec4(pos, 0.0, 1.0f); + FragColor = linear_iter(texture(tex, uv), vec4(pos, 0.0, 1.0), (uv.x + uv.y + 1.0) / 3.0); +} + +")"; +#endif \ No newline at end of file diff --git a/tests/include/shaders/test.vert b/tests/include/shaders/test.vert new file mode 100644 index 0000000..4fffb2d --- /dev/null +++ b/tests/include/shaders/test.vert @@ -0,0 +1,29 @@ +#ifdef __cplusplus +#include +std::string shader_test_vert = R"(" +#version 300 es +precision mediump float; + +layout (location = 0) in vec3 vertex; +layout (location = 1) in vec2 uv_in; + +out vec2 pos; +out vec2 uv; + +layout (std140) uniform GlobalMatrices +{ + mat4 projection; + mat4 view; +// projection view matrix + mat4 pvm; +}; + +void main() { + //gl_Position = projection * view * vec4(vertex.x, vertex.y, vertex.z, 1.0); + gl_Position = vec4(vertex, 1.0); + pos = vertex.xy; + uv = uv_in; +} + +")"; +#endif \ No newline at end of file diff --git a/tests/src/main.cpp b/tests/src/main.cpp index f5ea90e..993c3f0 100644 --- a/tests/src/main.cpp +++ b/tests/src/main.cpp @@ -1,12 +1,65 @@ -#include - #include +#include +#include +#include +#include #include #include "blt/gfx/imgui/IconsFontAwesome5.h" +#include +#include + +const float raw_vertices[18] = { + // first triangle + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, 0.5f, 0.0f, // top left + // second triangle + 0.5f, -0.5f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f // top left +}; + +float vertices[20] = { + // positions // texture coords + 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left +}; +const unsigned int indices[6] = { // note that we start from 0! + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle +}; + +blt::gfx::vertex_array* vao; +blt::gfx::shader_t* shader; +blt::gfx::texture_gl2D* texture; + void init() { - + using namespace blt::gfx; + + vbo_t vertices_vbo; + vbo_t indices_vbo; + + vertices_vbo.create(); + indices_vbo.create(GL_ELEMENT_ARRAY_BUFFER); + + vertices_vbo.allocate(sizeof(vertices), vertices); + indices_vbo.allocate(sizeof(indices), indices); + + vao = new vertex_array(); + vao->bindVBO(vertices_vbo, 0, 3, GL_FLOAT, 5 * sizeof(float), 0); + vao->bindVBO(vertices_vbo, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float)); + vao->bindElement(indices_vbo); + + shader = new shader_t(shader_test_vert, shader_test_frag); + shader->bindAttribute(0, "vertex"); + shader->bindAttribute(1, "uv_in"); + + texture_file file("../resources/textures/cumdollar.jpg"); + texture = new texture_gl2D(file.texture()); } void update(std::int32_t width, std::int32_t height) @@ -15,11 +68,25 @@ void update(std::int32_t width, std::int32_t height) ImGui::Text("%s among %d items", ICON_FA_FILE, 0); ImGui::Button(ICON_FA_SEARCH " Search"); ImGui::End(); + + + shader->bind(); + + glActiveTexture(GL_TEXTURE0); + texture->bind(); + + vao->bind(); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } int main() { blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update}.setSyncInterval(1)); + delete vao; + delete shader; + delete texture; blt::gfx::cleanup(); return 0; }