From 3b02022fd79ad0e130b8c49e7707d221bfc97bb5 Mon Sep 17 00:00:00 2001 From: hachem Date: Tue, 12 Aug 2025 19:17:50 +0200 Subject: feat: texture and pixel buffers --- src/core/application.cpp | 95 +++++++++++++++++++++++++---- src/core/application.h | 4 ++ src/platform/opengl/opengl_renderer_api.cpp | 17 ++++++ src/platform/opengl/opengl_renderer_api.h | 1 + src/platform/opengl/opengl_texture.cpp | 65 ++++++++++++++++++++ src/platform/opengl/opengl_texture.h | 33 ++++++++++ src/rendering/renderer.h | 6 ++ src/rendering/texture.cpp | 32 ++++++++++ src/rendering/texture.h | 30 +++++++++ 9 files changed, 271 insertions(+), 12 deletions(-) create mode 100644 src/platform/opengl/opengl_texture.cpp create mode 100644 src/platform/opengl/opengl_texture.h create mode 100644 src/rendering/texture.cpp create mode 100644 src/rendering/texture.h (limited to 'src') diff --git a/src/core/application.cpp b/src/core/application.cpp index b80ad84..0acd232 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -5,6 +5,7 @@ #include "rendering/vertex_buffer.h" #include "rendering/index_buffer.h" #include "rendering/shader.h" +#include "rendering/texture.h" #include #include @@ -71,6 +72,8 @@ namespace Donut else m_minimized = false; + Renderer::on_window_resize(e.get_width(), e.get_height()); + if (m_camera) m_camera->set_projection(45.0f, (float)e.get_width() / (float)e.get_height(), 0.1f, 100.0f); @@ -150,6 +153,8 @@ namespace Donut void Application::on_init() { Renderer::init(); + Renderer::on_window_resize(1280, 720); + RenderCommand::set_face_culling(false); m_camera = std::make_unique(45.0f, 1280.0f / 720.0f, 0.1f, 100.0f); m_camera->set_position({ 0.0f, 0.0f, 3.0f }); @@ -158,14 +163,14 @@ namespace Donut float vertices[] = { - -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 1.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f + -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f }; uint32_t indices[] = @@ -175,7 +180,7 @@ namespace Donut 5, 4, 7, 7, 6, 5, 4, 0, 3, 3, 7, 4, 3, 2, 6, 6, 7, 3, - 4, 5, 1, 1, 0, 4 + 4, 5, 1, 1, 0, 4 }; m_vertex_array = std::shared_ptr(VertexArray::create()); @@ -184,6 +189,7 @@ namespace Donut VertexBufferLayout layout; layout.push(3); layout.push(4); + layout.push(2); m_vertex_buffer->set_layout(layout); m_vertex_array->add_vertex_buffer(m_vertex_buffer); @@ -191,7 +197,32 @@ namespace Donut m_index_buffer = std::shared_ptr(IndexBuffer::create(indices, 36)); m_vertex_array->set_index_buffer(m_index_buffer); - m_shader = std::shared_ptr(Shader::create("assets/basic.glsl")); + m_shader = std::shared_ptr(Shader::create("assets/textured.glsl")); + + m_texture = Texture2D::create(256, 256); + + uint32_t* pixel_data = new uint32_t[256 * 256]; + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + float u = (float)x / 256.0f; + float v = (float)y / 256.0f; + + float wave = sin(u * 10.0f) * cos(v * 10.0f); + wave = (wave + 1.0f) * 0.5f; + + uint8_t r = (uint8_t)(wave * 255); + uint8_t g = (uint8_t)((1.0f - wave) * 255); + uint8_t b = (uint8_t)((u + v) * 0.5f * 255); + uint8_t a = 255; + + pixel_data[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + + m_texture->set_data(pixel_data, 256 * 256 * 4); + delete[] pixel_data; } void Application::on_shutdown() @@ -215,11 +246,13 @@ namespace Donut m_camera->move_left(m_delta_time); if (m_keys[GLFW_KEY_D]) m_camera->move_right(m_delta_time); - if (m_keys[GLFW_KEY_SPACE]) + if (m_keys[GLFW_KEY_SPACE]) m_camera->move_up(m_delta_time); if (m_keys[GLFW_KEY_LEFT_SHIFT]) - m_camera->move_down(m_delta_time); + m_camera->move_down(m_delta_time); } + + update_texture(); } void Application::on_render() @@ -227,6 +260,12 @@ namespace Donut Renderer::set_clear_color({ 0.0f, 0.0f, 0.0f, 1.0f }); Renderer::clear(); + if (m_texture) + { + m_texture->bind(0); + m_shader->set_int("u_Texture", 0); + } + glm::mat4 view_projection = m_camera ? m_camera->get_view_projection_matrix() : glm::mat4(1.0f); glm::vec3 center_pos(0.0f, 0.0f, 0.0f); @@ -252,4 +291,36 @@ namespace Donut transform = glm::translate(glm::mat4(1.0f), back_pos); Renderer::submit(m_shader, m_vertex_array, transform, view_projection); } + + void Application::update_texture() + { + if (!m_texture) + return; + + m_texture_time += m_delta_time; + + uint32_t* pixel_data = new uint32_t[256 * 256]; + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + float time = m_texture_time * 2.0f; + float u = (float)x / 256.0f; + float v = (float)y / 256.0f; + + float wave = sin(u * 10.0f + time) * cos(v * 10.0f + time * 0.5f); + wave = (wave + 1.0f) * 0.5f; + + uint8_t r = (uint8_t)(wave * 255); + uint8_t g = (uint8_t)((1.0f - wave) * 255); + uint8_t b = (uint8_t)((u + v) * 0.5f * 255); + uint8_t a = 255; + + pixel_data[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + + m_texture->set_data(pixel_data, 256 * 256 * 4); + delete[] pixel_data; + } } diff --git a/src/core/application.h b/src/core/application.h index f78f107..2d8c55a 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -12,6 +12,7 @@ namespace Donut class VertexBuffer; class IndexBuffer; class Shader; + class Texture2D; }; namespace Donut @@ -34,6 +35,7 @@ namespace Donut void on_update(); void on_render(); void on_event(Event& event); + void update_texture(); private: std::unique_ptr m_window; bool m_running; @@ -43,6 +45,7 @@ namespace Donut std::shared_ptr m_vertex_buffer; std::shared_ptr m_index_buffer; std::shared_ptr m_shader; + std::shared_ptr m_texture; std::unique_ptr m_camera; @@ -53,6 +56,7 @@ namespace Donut float m_delta_time = 0.0f; float m_last_frame = 0.0f; bool m_mouse_dragging = false; + float m_texture_time = 0.0f; static Application* s_instance; }; diff --git a/src/platform/opengl/opengl_renderer_api.cpp b/src/platform/opengl/opengl_renderer_api.cpp index 49b4e53..3a147b9 100644 --- a/src/platform/opengl/opengl_renderer_api.cpp +++ b/src/platform/opengl/opengl_renderer_api.cpp @@ -8,6 +8,11 @@ namespace Donut glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); } void OpenGLRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) @@ -30,6 +35,18 @@ namespace Donut glEnable(GL_DEPTH_TEST); } + void OpenGLRendererAPI::set_face_culling(bool enabled) + { + if (enabled) + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + } + else + glDisable(GL_CULL_FACE); + } + void OpenGLRendererAPI::draw_indexed(const std::shared_ptr& vertex_array, uint32_t index_count) { uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count(); diff --git a/src/platform/opengl/opengl_renderer_api.h b/src/platform/opengl/opengl_renderer_api.h index 30be426..f28567c 100644 --- a/src/platform/opengl/opengl_renderer_api.h +++ b/src/platform/opengl/opengl_renderer_api.h @@ -14,6 +14,7 @@ namespace Donut virtual void set_clear_color(const glm::vec4& color) override; virtual void clear() override; virtual void enable_depth_test() override; + virtual void set_face_culling(bool enabled) override; virtual void draw_indexed(const std::shared_ptr& vertex_array, uint32_t index_count = 0) override; }; diff --git a/src/platform/opengl/opengl_texture.cpp b/src/platform/opengl/opengl_texture.cpp new file mode 100644 index 0000000..87e9345 --- /dev/null +++ b/src/platform/opengl/opengl_texture.cpp @@ -0,0 +1,65 @@ +#include "opengl_texture.h" +#include + +namespace Donut +{ + OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) + : m_width(width), m_height(height) + { + m_internal_format = GL_RGBA8; + m_data_format = GL_RGBA; + + glCreateTextures(GL_TEXTURE_2D, 1, &m_renderer_id); + glTextureStorage2D(m_renderer_id, 1, m_internal_format, m_width, m_height); + + glTextureParameteri(m_renderer_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_renderer_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(m_renderer_id, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_renderer_id, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + OpenGLTexture2D::OpenGLTexture2D(const std::string& path) + : m_path(path) + { + m_width = 1; + m_height = 1; + m_internal_format = GL_RGBA8; + m_data_format = GL_RGBA; + + glCreateTextures(GL_TEXTURE_2D, 1, &m_renderer_id); + glTextureStorage2D(m_renderer_id, 1, m_internal_format, m_width, m_height); + + glTextureParameteri(m_renderer_id, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_renderer_id, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(m_renderer_id, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_renderer_id, GL_TEXTURE_WRAP_T, GL_REPEAT); + + uint32_t white_pixel = 0xFFFFFFFF; + glTextureSubImage2D(m_renderer_id, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, &white_pixel); + + std::cout << "Created default texture (stb_image not available for loading: " << path << ")" << std::endl; + } + + OpenGLTexture2D::~OpenGLTexture2D() + { + glDeleteTextures(1, &m_renderer_id); + } + + void OpenGLTexture2D::set_data(void* data, uint32_t size) + { + uint32_t bpp = m_data_format == GL_RGBA ? 4 : 3; + if (size != m_width * m_height * bpp) + { + std::cout << "Data must be entire texture!" << std::endl; + return; + } + glTextureSubImage2D(m_renderer_id, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, data); + } + + void OpenGLTexture2D::bind(uint32_t slot) const + { + glBindTextureUnit(slot, m_renderer_id); + } +}; diff --git a/src/platform/opengl/opengl_texture.h b/src/platform/opengl/opengl_texture.h new file mode 100644 index 0000000..0782e8b --- /dev/null +++ b/src/platform/opengl/opengl_texture.h @@ -0,0 +1,33 @@ +#pragma once + +#include "rendering/texture.h" +#include + +namespace Donut +{ + class OpenGLTexture2D : public Texture2D + { + public: + OpenGLTexture2D(uint32_t width, uint32_t height); + OpenGLTexture2D(const std::string& path); + virtual ~OpenGLTexture2D(); + + virtual uint32_t get_width() const override { return m_width; } + virtual uint32_t get_height() const override { return m_height; } + virtual uint32_t get_renderer_id() const override { return m_renderer_id; } + + virtual void set_data(void* data, uint32_t size) override; + virtual void bind(uint32_t slot = 0) const override; + + virtual bool operator==(const Texture& other) const override + { + return m_renderer_id == other.get_renderer_id(); + } + + private: + std::string m_path; + uint32_t m_width, m_height; + uint32_t m_renderer_id; + GLenum m_internal_format, m_data_format; + }; +} diff --git a/src/rendering/renderer.h b/src/rendering/renderer.h index 09e9521..7bbdd4b 100644 --- a/src/rendering/renderer.h +++ b/src/rendering/renderer.h @@ -34,6 +34,7 @@ namespace Donut virtual void set_clear_color(const glm::vec4& color) = 0; virtual void clear() = 0; virtual void enable_depth_test() = 0; + virtual void set_face_culling(bool enabled) = 0; virtual void draw_indexed(const std::shared_ptr& vertex_array, uint32_t index_count = 0) = 0; @@ -72,6 +73,11 @@ namespace Donut s_renderer_api->enable_depth_test(); } + inline static void set_face_culling(bool enabled) + { + s_renderer_api->set_face_culling(enabled); + } + inline static void draw_indexed(const std::shared_ptr& vertex_array, uint32_t index_count = 0) { s_renderer_api->draw_indexed(vertex_array, index_count); diff --git a/src/rendering/texture.cpp b/src/rendering/texture.cpp new file mode 100644 index 0000000..9603034 --- /dev/null +++ b/src/rendering/texture.cpp @@ -0,0 +1,32 @@ +#include "texture.h" +#include "renderer.h" +#include "../platform/opengl/opengl_texture.h" + +namespace Donut +{ + std::shared_ptr Texture2D::create(uint32_t width, uint32_t height) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return std::make_shared(width, height); + case RendererAPI::API::None: + return nullptr; + default: + return nullptr; + } + } + + std::shared_ptr Texture2D::create(const std::string& path) + { + switch (Renderer::get_api()) + { + case RendererAPI::API::OpenGL: + return std::make_shared(path); + case RendererAPI::API::None: + return nullptr; + default: + return nullptr; + } + } +}; diff --git a/src/rendering/texture.h b/src/rendering/texture.h new file mode 100644 index 0000000..e3c53f8 --- /dev/null +++ b/src/rendering/texture.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace Donut +{ + class Texture + { + public: + virtual ~Texture() = default; + + virtual uint32_t get_width() const = 0; + virtual uint32_t get_height() const = 0; + virtual uint32_t get_renderer_id() const = 0; + + virtual void set_data(void* data, uint32_t size) = 0; + virtual void bind(uint32_t slot = 0) const = 0; + + virtual bool operator==(const Texture& other) const = 0; + }; + + class Texture2D : public Texture + { + public: + static std::shared_ptr create(uint32_t width, uint32_t height); + static std::shared_ptr create(const std::string& path); + }; +}; -- cgit v1.3