From 1ad6bc34323f5b30d463ebe47a58d47ff9d4f328 Mon Sep 17 00:00:00 2001 From: hachem Date: Tue, 12 Aug 2025 19:17:50 +0200 Subject: [add]: Texture and Pixel buffers --- assets/Textured.glsl | 41 +++++++++++++ src/Core/Application.cpp | 96 +++++++++++++++++++++++++++---- src/Core/Application.h | 5 +- src/Platform/OpenGL/OpenGLRendererAPI.cpp | 17 ++++++ src/Platform/OpenGL/OpenGLRendererAPI.h | 1 + src/Platform/OpenGL/OpenGLTexture.cpp | 66 +++++++++++++++++++++ src/Platform/OpenGL/OpenGLTexture.h | 33 +++++++++++ src/Rendering/Renderer.h | 6 ++ src/Rendering/Texture.cpp | 32 +++++++++++ src/Rendering/Texture.h | 30 ++++++++++ 10 files changed, 314 insertions(+), 13 deletions(-) create mode 100644 assets/Textured.glsl create mode 100644 src/Platform/OpenGL/OpenGLTexture.cpp create mode 100644 src/Platform/OpenGL/OpenGLTexture.h create mode 100644 src/Rendering/Texture.cpp create mode 100644 src/Rendering/Texture.h diff --git a/assets/Textured.glsl b/assets/Textured.glsl new file mode 100644 index 0000000..ad135fe --- /dev/null +++ b/assets/Textured.glsl @@ -0,0 +1,41 @@ +#type vertex +#version 330 core + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec4 a_Color; +layout(location = 2) in vec2 a_TexCoord; + +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; + +out vec3 v_Position; +out vec4 v_Color; +out vec2 v_TexCoord; + +void main() +{ + v_Position = a_Position; + v_Color = a_Color; + v_TexCoord = a_TexCoord; + gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0); +} + +#type fragment +#version 330 core + +layout(location = 0) out vec4 color; + +in vec3 v_Position; +in vec4 v_Color; +in vec2 v_TexCoord; + +uniform sampler2D u_Texture; + +void main() +{ + vec4 texColor = texture(u_Texture, v_TexCoord); + if (texColor.rgb == vec3(0.0)) + color = v_Color; + else + color = v_Color * texColor; +} diff --git a/src/Core/Application.cpp b/src/Core/Application.cpp index 1af049a..0fd0023 100644 --- a/src/Core/Application.cpp +++ b/src/Core/Application.cpp @@ -5,6 +5,7 @@ #include "Rendering/VertexBuffer.h" #include "Rendering/IndexBuffer.h" #include "Rendering/Shader.h" +#include "Rendering/Texture.h" #include #include @@ -71,6 +72,8 @@ namespace Donut else m_Minimized = false; + Renderer::OnWindowResize(e.GetWidth(), e.GetHeight()); + if (m_Camera) m_Camera->SetProjection(45.0f, (float)e.GetWidth() / (float)e.GetHeight(), 0.1f, 100.0f); @@ -150,6 +153,8 @@ namespace Donut void Application::OnInit() { Renderer::Init(); + Renderer::OnWindowResize(1280, 720); + RenderCommand::SetFaceCulling(false); m_Camera = std::make_unique(45.0f, 1280.0f / 720.0f, 0.1f, 100.0f); m_Camera->SetPosition({ 0.0f, 0.0f, 3.0f }); @@ -158,14 +163,15 @@ 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 + // Position (3) | Color (4) | TexCoords (2) + -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 +181,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_VertexArray = std::shared_ptr(VertexArray::Create()); @@ -184,6 +190,7 @@ namespace Donut VertexBufferLayout layout; layout.Push(3); layout.Push(4); + layout.Push(2); m_VertexBuffer->SetLayout(layout); m_VertexArray->AddVertexBuffer(m_VertexBuffer); @@ -191,7 +198,32 @@ namespace Donut m_IndexBuffer = std::shared_ptr(IndexBuffer::Create(indices, 36)); m_VertexArray->SetIndexBuffer(m_IndexBuffer); - 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* pixelData = 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; + + pixelData[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + + m_Texture->SetData(pixelData, 256 * 256 * 4); + delete[] pixelData; } void Application::OnShutdown() @@ -215,11 +247,13 @@ namespace Donut m_Camera->MoveLeft(m_DeltaTime); if (m_Keys[GLFW_KEY_D]) m_Camera->MoveRight(m_DeltaTime); - if (m_Keys[GLFW_KEY_SPACE]) + if (m_Keys[GLFW_KEY_SPACE]) m_Camera->MoveUp(m_DeltaTime); if (m_Keys[GLFW_KEY_LEFT_SHIFT]) - m_Camera->MoveDown(m_DeltaTime); + m_Camera->MoveDown(m_DeltaTime); } + + UpdateTexture(); } void Application::OnRender() @@ -227,6 +261,12 @@ namespace Donut Renderer::SetClearColor({ 0.0f, 0.0f, 0.0f, 1.0f }); Renderer::Clear(); + if (m_Texture) + { + m_Texture->Bind(0); + m_Shader->SetInt("u_Texture", 0); + } + glm::mat4 viewProjection = m_Camera ? m_Camera->GetViewProjectionMatrix() : glm::mat4(1.0f); glm::vec3 centerPos(0.0f, 0.0f, 0.0f); @@ -252,4 +292,36 @@ namespace Donut transform = glm::translate(glm::mat4(1.0f), backPos); Renderer::Submit(m_Shader, m_VertexArray, transform, viewProjection); } + + void Application::UpdateTexture() + { + if (!m_Texture) + return; + + m_TextureTime += m_DeltaTime; + + uint32_t* pixelData = new uint32_t[256 * 256]; + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + float time = m_TextureTime * 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; + + pixelData[y * 256 + x] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + + m_Texture->SetData(pixelData, 256 * 256 * 4); + delete[] pixelData; + } } diff --git a/src/Core/Application.h b/src/Core/Application.h index 9e5d31d..7406136 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 OnUpdate(); void OnRender(); void OnEvent(Event& event); + void UpdateTexture(); private: std::unique_ptr m_Window; bool m_Running; @@ -43,10 +45,10 @@ namespace Donut std::shared_ptr m_VertexBuffer; std::shared_ptr m_IndexBuffer; std::shared_ptr m_Shader; + std::shared_ptr m_Texture; std::unique_ptr m_Camera; - // Input state bool m_Keys[1024] = { false }; bool m_FirstMouse = true; float m_LastX = 0.0f; @@ -54,6 +56,7 @@ namespace Donut float m_DeltaTime = 0.0f; float m_LastFrame = 0.0f; bool m_MouseDragging = false; + float m_TextureTime = 0.0f; static Application* s_Instance; }; diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/Platform/OpenGL/OpenGLRendererAPI.cpp index 3660555..5ee7d8d 100644 --- a/src/Platform/OpenGL/OpenGLRendererAPI.cpp +++ b/src/Platform/OpenGL/OpenGLRendererAPI.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::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) @@ -30,6 +35,18 @@ namespace Donut glEnable(GL_DEPTH_TEST); } + void OpenGLRendererAPI::SetFaceCulling(bool enabled) + { + if (enabled) + { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + } + else + glDisable(GL_CULL_FACE); + } + void OpenGLRendererAPI::DrawIndexed(const std::shared_ptr& vertexArray, uint32_t indexCount) { uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount(); diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h index f37cc79..35bd9c3 100644 --- a/src/Platform/OpenGL/OpenGLRendererAPI.h +++ b/src/Platform/OpenGL/OpenGLRendererAPI.h @@ -14,6 +14,7 @@ namespace Donut virtual void SetClearColor(const glm::vec4& color) override; virtual void Clear() override; virtual void EnableDepthTest() override; + virtual void SetFaceCulling(bool enabled) override; virtual void DrawIndexed(const std::shared_ptr& vertexArray, uint32_t indexCount = 0) override; }; diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/Platform/OpenGL/OpenGLTexture.cpp new file mode 100644 index 0000000..02252d1 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLTexture.cpp @@ -0,0 +1,66 @@ +#include "OpenGLTexture.h" +#include + +namespace Donut +{ + OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height) + : m_Width(width), m_Height(height) + { + m_InternalFormat = GL_RGBA8; + m_DataFormat = GL_RGBA; + + glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + // TODO: Texture Loading + OpenGLTexture2D::OpenGLTexture2D(const std::string& path) + : m_Path(path) + { + m_Width = 1; + m_Height = 1; + m_InternalFormat = GL_RGBA8; + m_DataFormat = GL_RGBA; + + glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID); + glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height); + + glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT); + + uint32_t whitePixel = 0xFFFFFFFF; + glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, &whitePixel); + + std::cout << "Created default texture (stb_image not available for loading: " << path << ")" << std::endl; + } + + OpenGLTexture2D::~OpenGLTexture2D() + { + glDeleteTextures(1, &m_RendererID); + } + + void OpenGLTexture2D::SetData(void* data, uint32_t size) + { + uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3; + if (size != m_Width * m_Height * bpp) + { + std::cout << "Data must be entire texture!" << std::endl; + return; + } + glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data); + } + + void OpenGLTexture2D::Bind(uint32_t slot) const + { + glBindTextureUnit(slot, m_RendererID); + } +}; \ No newline at end of file diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h new file mode 100644 index 0000000..4a95ab4 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLTexture.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 GetWidth() const override { return m_Width; } + virtual uint32_t GetHeight() const override { return m_Height; } + virtual uint32_t GetRendererID() const override { return m_RendererID; } + + virtual void SetData(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_RendererID == other.GetRendererID(); + } + + private: + std::string m_Path; + uint32_t m_Width, m_Height; + uint32_t m_RendererID; + GLenum m_InternalFormat, m_DataFormat; + }; +} diff --git a/src/Rendering/Renderer.h b/src/Rendering/Renderer.h index d08d015..82c156e 100644 --- a/src/Rendering/Renderer.h +++ b/src/Rendering/Renderer.h @@ -34,6 +34,7 @@ namespace Donut virtual void SetClearColor(const glm::vec4& color) = 0; virtual void Clear() = 0; virtual void EnableDepthTest() = 0; + virtual void SetFaceCulling(bool enabled) = 0; virtual void DrawIndexed(const std::shared_ptr& vertexArray, uint32_t indexCount = 0) = 0; @@ -72,6 +73,11 @@ namespace Donut s_RendererAPI->EnableDepthTest(); } + inline static void SetFaceCulling(bool enabled) + { + s_RendererAPI->SetFaceCulling(enabled); + } + inline static void DrawIndexed(const std::shared_ptr& vertexArray, uint32_t indexCount = 0) { s_RendererAPI->DrawIndexed(vertexArray, indexCount); diff --git a/src/Rendering/Texture.cpp b/src/Rendering/Texture.cpp new file mode 100644 index 0000000..a52a469 --- /dev/null +++ b/src/Rendering/Texture.cpp @@ -0,0 +1,32 @@ +#include "Texture.h" +#include "Renderer.h" +#include "../Platform/OpenGL/OpenGLTexture.h" + +namespace Donut +{ + std::shared_ptr Texture2D::Create(uint32_t width, uint32_t height) + { + switch (Renderer::GetAPI()) + { + 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::GetAPI()) + { + case RendererAPI::API::OpenGL: + return std::make_shared(path); + case RendererAPI::API::None: + return nullptr; + default: + return nullptr; + } + } +}; \ No newline at end of file diff --git a/src/Rendering/Texture.h b/src/Rendering/Texture.h new file mode 100644 index 0000000..eebce85 --- /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 GetWidth() const = 0; + virtual uint32_t GetHeight() const = 0; + virtual uint32_t GetRendererID() const = 0; + + virtual void SetData(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); + }; +}; \ No newline at end of file -- cgit v1.3