diff options
| author | hachem <im@hachem.wtf> | 2025-08-12 18:12:37 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-12 18:12:37 +0200 |
| commit | a2ec7dabd48a70ea69623406ddd0f8f97af403c8 (patch) | |
| tree | 52d71b51753b88473607f5e97f8bc15bdb5ad7d3 | |
| parent | 16dff443789c7f1461b46ce52966972ae14c042b (diff) | |
[add]: Start of a basic rendering backend
27 files changed, 1098 insertions, 3 deletions
diff --git a/.gitmodules b/.gitmodules index 7b6c772..aaa1cbc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Vendor/glfw"] path = Vendor/glfw url = https://github.com/glfw/glfw.git +[submodule "Vendor/glm"] + path = Vendor/glm + url = https://github.com/g-truc/glm.git diff --git a/Vendor/glm b/Vendor/glm new file mode 160000 +Subproject 2d4c4b4dd31fde06cfffad7915c2b3006402322 diff --git a/assets/Basic.glsl b/assets/Basic.glsl new file mode 100644 index 0000000..f6016f7 --- /dev/null +++ b/assets/Basic.glsl @@ -0,0 +1,31 @@ +#type vertex +#version 330 core + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec4 a_Color; + +uniform mat4 u_ViewProjection; +uniform mat4 u_Transform; + +out vec3 v_Position; +out vec4 v_Color; + +void main() +{ + v_Position = a_Position; + v_Color = a_Color; + 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; + +void main() +{ + color = v_Color; +} diff --git a/premake5.lua b/premake5.lua index 7b615c0..c8f1405 100644 --- a/premake5.lua +++ b/premake5.lua @@ -19,6 +19,7 @@ outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}" IncludeDir = {} IncludeDir["glfw"] = "Vendor/glfw/include" IncludeDir["glad"] = "Vendor/glad/include" +IncludeDir["glm"] = "Vendor/glm" group "Dependencies" @@ -212,6 +213,9 @@ project "Donut" files { + "Vendor/glm/glm/**.hpp", + "Vendor/glm/glm/**.inl", + "src/**.h", "src/**.cpp" } @@ -219,8 +223,10 @@ project "Donut" includedirs { "src", + "%{IncludeDir.glfw}", - "%{IncludeDir.glad}" + "%{IncludeDir.glad}", + "%{IncludeDir.glm}" } links diff --git a/src/Core/Application.cpp b/src/Core/Application.cpp index 9ca060c..98d1de7 100644 --- a/src/Core/Application.cpp +++ b/src/Core/Application.cpp @@ -1,5 +1,14 @@ #include "Application.h" + +#include "Rendering/Renderer.h" +#include "Rendering/VertexArray.h" +#include "Rendering/VertexBuffer.h" +#include "Rendering/IndexBuffer.h" +#include "Rendering/Shader.h" + #include <iostream> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> namespace Donut { @@ -73,8 +82,39 @@ namespace Donut }); } - void Application::OnInit() { } - void Application::OnShutdown() { } + void Application::OnInit() + { + Renderer::Init(); + + float vertices[3 * 7] = + { + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f + }; + + uint32_t indices[3] = { 0, 1, 2 }; + + m_VertexArray = std::shared_ptr<VertexArray>(VertexArray::Create()); + m_VertexBuffer = std::shared_ptr<VertexBuffer>(VertexBuffer::Create(vertices, sizeof(vertices))); + + VertexBufferLayout layout; + layout.Push<float>(3); + layout.Push<float>(4); + m_VertexBuffer->SetLayout(layout); + + m_VertexArray->AddVertexBuffer(m_VertexBuffer); + + m_IndexBuffer = std::shared_ptr<IndexBuffer>(IndexBuffer::Create(indices, 3)); + m_VertexArray->SetIndexBuffer(m_IndexBuffer); + + m_Shader = std::shared_ptr<Shader>(Shader::Create("assets/Basic.glsl")); + } + + void Application::OnShutdown() + { + Renderer::Shutdown(); + } void Application::OnUpdate() { @@ -82,5 +122,15 @@ namespace Donut void Application::OnRender() { + Renderer::SetClearColor({ 0.1f, 0.1f, 0.1f, 1.0f }); + Renderer::Clear(); + + Renderer::BeginScene(); + + glm::mat4 viewProjection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + + Renderer::Submit(m_Shader, m_VertexArray, glm::mat4(1.0f)); + + Renderer::EndScene(); } } diff --git a/src/Core/Application.h b/src/Core/Application.h index fca4c42..b8c9d49 100644 --- a/src/Core/Application.h +++ b/src/Core/Application.h @@ -7,6 +7,14 @@ namespace Donut { + class VertexArray; + class VertexBuffer; + class IndexBuffer; + class Shader; +}; + +namespace Donut +{ class Application { public: @@ -30,6 +38,11 @@ namespace Donut bool m_Running; bool m_Minimized; + std::shared_ptr<VertexArray> m_VertexArray; + std::shared_ptr<VertexBuffer> m_VertexBuffer; + std::shared_ptr<IndexBuffer> m_IndexBuffer; + std::shared_ptr<Shader> m_Shader; + static Application* s_Instance; }; }
\ No newline at end of file diff --git a/src/Core/Window.cpp b/src/Core/Window.cpp index 13025cf..440aeb1 100644 --- a/src/Core/Window.cpp +++ b/src/Core/Window.cpp @@ -1,6 +1,7 @@ #include "Window.h" #include <cstdint> #include <iostream> +#include <glad/glad.h> namespace Donut { @@ -42,6 +43,15 @@ namespace Donut } glfwMakeContextCurrent(m_Window); + + // Initialize GLAD + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + { + std::cout << "Failed to initialize GLAD!" << std::endl; + glfwTerminate(); + return; + } + glfwSetWindowUserPointer(m_Window, this); glfwSetErrorCallback(GLFWErrorCallback); diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.cpp b/src/Platform/OpenGL/OpenGLIndexBuffer.cpp new file mode 100644 index 0000000..7faee62 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLIndexBuffer.cpp @@ -0,0 +1,28 @@ +#include "OpenGLIndexBuffer.h" +#include <glad/glad.h> + +namespace Donut +{ + OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count) + : m_Count(count) + { + glCreateBuffers(1, &m_RendererID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW); + } + + OpenGLIndexBuffer::~OpenGLIndexBuffer() + { + glDeleteBuffers(1, &m_RendererID); + } + + void OpenGLIndexBuffer::Bind() const + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID); + } + + void OpenGLIndexBuffer::Unbind() const + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +}; diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.h b/src/Platform/OpenGL/OpenGLIndexBuffer.h new file mode 100644 index 0000000..0de8fb2 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLIndexBuffer.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Rendering/IndexBuffer.h" + +namespace Donut +{ + class OpenGLIndexBuffer + : public IndexBuffer + { + public: + OpenGLIndexBuffer(const uint32_t* indices, uint32_t count); + virtual ~OpenGLIndexBuffer(); + + virtual void Bind() const override; + virtual void Unbind() const override; + virtual uint32_t GetCount() const override { return m_Count; } + + private: + uint32_t m_RendererID; + uint32_t m_Count; + }; +}; diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/Platform/OpenGL/OpenGLRendererAPI.cpp new file mode 100644 index 0000000..c1c2e9b --- /dev/null +++ b/src/Platform/OpenGL/OpenGLRendererAPI.cpp @@ -0,0 +1,34 @@ +#include "OpenGLRendererAPI.h" +#include <glad/glad.h> + +namespace Donut +{ + void OpenGLRendererAPI::Init() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_DEPTH_TEST); + } + + void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) + { + glViewport(x, y, width, height); + } + + void OpenGLRendererAPI::SetClearColor(const glm::vec4& color) + { + glClearColor(color.r, color.g, color.b, color.a); + } + + void OpenGLRendererAPI::Clear() + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + void OpenGLRendererAPI::DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount) + { + uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount(); + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr); + glBindTexture(GL_TEXTURE_2D, 0); + } +}; diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h new file mode 100644 index 0000000..33547c0 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLRendererAPI.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Rendering/Renderer.h" +#include <glad/glad.h> + +namespace Donut +{ + class OpenGLRendererAPI + : public RendererAPI + { + public: + virtual void Init() override; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) override; + virtual void SetClearColor(const glm::vec4& color) override; + virtual void Clear() override; + + virtual void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount = 0) override; + }; +}; diff --git a/src/Platform/OpenGL/OpenGLShader.cpp b/src/Platform/OpenGL/OpenGLShader.cpp new file mode 100644 index 0000000..bb75f3d --- /dev/null +++ b/src/Platform/OpenGL/OpenGLShader.cpp @@ -0,0 +1,224 @@ +#include "OpenGLShader.h" +#include <glad/glad.h> +#include <glm/gtc/type_ptr.hpp> +#include <fstream> +#include <iostream> + +namespace Donut +{ + static uint32_t ShaderTypeFromString(const std::string& type) + { + if (type == "vertex") + return GL_VERTEX_SHADER; + if (type == "fragment" || type == "pixel") + return GL_FRAGMENT_SHADER; + return 0; + } + + OpenGLShader::OpenGLShader(const std::string& filepath) + { + std::string source = ReadFile(filepath); + auto shaderSources = PreProcess(source); + Compile(shaderSources); + + auto lastSlash = filepath.find_last_of("/\\"); + lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1; + auto lastDot = filepath.rfind('.'); + auto count = lastDot == std::string::npos ? filepath.size() - lastSlash : lastDot - lastSlash; + m_Name = filepath.substr(lastSlash, count); + } + + OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc) + : m_Name(name) + { + std::unordered_map<uint32_t, std::string> sources; + sources[GL_VERTEX_SHADER] = vertexSrc; + sources[GL_FRAGMENT_SHADER] = fragmentSrc; + Compile(sources); + } + + OpenGLShader::~OpenGLShader() + { + glDeleteProgram(m_RendererID); + } + + std::string OpenGLShader::ReadFile(const std::string& filepath) + { + std::string result; + std::ifstream in(filepath, std::ios::in | std::ios::binary); + if (in) { + in.seekg(0, std::ios::end); + size_t size = in.tellg(); + if (size != -1) { + result.resize(size); + in.seekg(0, std::ios::beg); + in.read(&result[0], size); + } + } + return result; + } + + std::unordered_map<uint32_t, std::string> OpenGLShader::PreProcess(const std::string& source) + { + std::unordered_map<uint32_t, std::string> shaderSources; + + const char* typeToken = "#type"; + size_t typeTokenLength = strlen(typeToken); + size_t pos = source.find(typeToken, 0); + while (pos != std::string::npos) { + size_t eol = source.find_first_of("\r\n", pos); + size_t begin = pos + typeTokenLength + 1; + std::string type = source.substr(begin, eol - begin); + + size_t nextLinePos = source.find_first_not_of("\r\n", eol); + pos = source.find(typeToken, nextLinePos); + shaderSources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(nextLinePos) : + source.substr(nextLinePos, pos - nextLinePos); + } + + return shaderSources; + } + + void OpenGLShader::Compile(const std::unordered_map<uint32_t, std::string>& shaderSources) + { + uint32_t program = glCreateProgram(); + std::vector<uint32_t> glShaderIDs(shaderSources.size()); + for (auto& kv : shaderSources) + { + uint32_t type = kv.first; + const std::string& source = kv.second; + + uint32_t shader = glCreateShader(type); + const char* sourceCStr = source.c_str(); + glShaderSource(shader, 1, &sourceCStr, 0); + glCompileShader(shader); + + int isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (isCompiled == GL_FALSE) + { + int maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<char> infoLog(maxLength); + glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]); + glDeleteShader(shader); + std::cout << "Shader compilation failure!" << std::endl << infoLog.data() << std::endl; + return; + } + glAttachShader(program, shader); + glShaderIDs.push_back(shader); + } + + m_RendererID = program; + glLinkProgram(m_RendererID); + + int isLinked = 0; + glGetProgramiv(m_RendererID, GL_LINK_STATUS, (int*)&isLinked); + if (isLinked == GL_FALSE) + { + int maxLength = 0; + glGetProgramiv(m_RendererID, GL_INFO_LOG_LENGTH, &maxLength); + std::vector<char> infoLog(maxLength); + glGetProgramInfoLog(m_RendererID, maxLength, &maxLength, &infoLog[0]); + glDeleteProgram(m_RendererID); + for (auto id : glShaderIDs) + glDeleteShader(id); + std::cout << "Shader link failure!" << std::endl << infoLog.data() << std::endl; + return; + } + + for (auto id : glShaderIDs) + { + glDetachShader(m_RendererID, id); + glDeleteShader(id); + } + } + + void OpenGLShader::Bind() const + { + glUseProgram(m_RendererID); + } + + void OpenGLShader::Unbind() const + { + glUseProgram(0); + } + + void OpenGLShader::SetInt(const std::string& name, int value) + { + UploadUniformInt(name, value); + } + + void OpenGLShader::SetIntArray(const std::string& name, int* values, uint32_t count) + { + UploadUniformIntArray(name, values, count); + } + + void OpenGLShader::SetFloat(const std::string& name, float value) + { + UploadUniformFloat(name, value); + } + + void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value) + { + UploadUniformFloat3(name, value); + } + + void OpenGLShader::SetFloat4(const std::string& name, const glm::vec4& value) + { + UploadUniformFloat4(name, value); + } + + void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value) + { + UploadUniformMat4(name, value); + } + + void OpenGLShader::UploadUniformInt(const std::string& name, int value) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform1i(location, value); + } + + void OpenGLShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform1iv(location, count, values); + } + + void OpenGLShader::UploadUniformFloat(const std::string& name, float value) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform1f(location, value); + } + + void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform2f(location, value.x, value.y); + } + + void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform3f(location, value.x, value.y, value.z); + } + + void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniform4f(location, value.x, value.y, value.z, value.w); + } + + void OpenGLShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniformMatrix3fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); + } + + void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix) + { + int location = glGetUniformLocation(m_RendererID, name.c_str()); + glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); + } +}; diff --git a/src/Platform/OpenGL/OpenGLShader.h b/src/Platform/OpenGL/OpenGLShader.h new file mode 100644 index 0000000..2ebde12 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLShader.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Rendering/Shader.h" +#include <unordered_map> +#include <glm/glm.hpp> + +namespace Donut +{ + class OpenGLShader + : public Shader + { + public: + OpenGLShader(const std::string& filepath); + OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc); + virtual ~OpenGLShader(); + + virtual void Bind() const override; + virtual void Unbind() const override; + + virtual void SetInt(const std::string& name, int value) override; + virtual void SetIntArray(const std::string& name, int* values, uint32_t count) override; + virtual void SetFloat(const std::string& name, float value) override; + virtual void SetFloat3(const std::string& name, const glm::vec3& value) override; + virtual void SetFloat4(const std::string& name, const glm::vec4& value) override; + virtual void SetMat4(const std::string& name, const glm::mat4& value) override; + + virtual const std::string& GetName() const override { return m_Name; } + + void UploadUniformInt(const std::string& name, int value); + void UploadUniformIntArray(const std::string& name, int* values, uint32_t count); + void UploadUniformFloat(const std::string& name, float value); + void UploadUniformFloat2(const std::string& name, const glm::vec2& value); + void UploadUniformFloat3(const std::string& name, const glm::vec3& value); + void UploadUniformFloat4(const std::string& name, const glm::vec4& value); + void UploadUniformMat3(const std::string& name, const glm::mat3& matrix); + void UploadUniformMat4(const std::string& name, const glm::mat4& matrix); + + private: + std::string ReadFile(const std::string& filepath); + std::unordered_map<uint32_t, std::string> PreProcess(const std::string& source); + void Compile(const std::unordered_map<uint32_t, std::string>& shaderSources); + + private: + uint32_t m_RendererID; + std::string m_Name; + }; +}; diff --git a/src/Platform/OpenGL/OpenGLVertexArray.cpp b/src/Platform/OpenGL/OpenGLVertexArray.cpp new file mode 100644 index 0000000..f172b9f --- /dev/null +++ b/src/Platform/OpenGL/OpenGLVertexArray.cpp @@ -0,0 +1,55 @@ +#include <glad/glad.h> +#include "OpenGLVertexArray.h" +#include "Rendering/VertexBuffer.h" +#include "Rendering/IndexBuffer.h" + +namespace Donut +{ + OpenGLVertexArray::OpenGLVertexArray() + { + glCreateVertexArrays(1, &m_RendererID); + } + + OpenGLVertexArray::~OpenGLVertexArray() + { + glDeleteVertexArrays(1, &m_RendererID); + } + + void OpenGLVertexArray::Bind() const + { + glBindVertexArray(m_RendererID); + } + + void OpenGLVertexArray::Unbind() const + { + glBindVertexArray(0); + } + + void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) + { + glBindVertexArray(m_RendererID); + vertexBuffer->Bind(); + + const auto& layout = vertexBuffer->GetLayout(); + for (const auto& element : layout.GetElements()) + { + glEnableVertexAttribArray(m_VertexBufferIndex); + glVertexAttribPointer(m_VertexBufferIndex, + element.count, + element.type, + element.normalized ? GL_TRUE : GL_FALSE, + layout.GetStride(), + (const void*)element.offset); + m_VertexBufferIndex++; + } + + m_VertexBuffers.push_back(vertexBuffer); + } + + void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) + { + glBindVertexArray(m_RendererID); + indexBuffer->Bind(); + m_IndexBuffer = indexBuffer; + } +}; diff --git a/src/Platform/OpenGL/OpenGLVertexArray.h b/src/Platform/OpenGL/OpenGLVertexArray.h new file mode 100644 index 0000000..6783140 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLVertexArray.h @@ -0,0 +1,40 @@ +#pragma once + +#include "Rendering/VertexArray.h" +#include <vector> +#include <memory> + +namespace Donut +{ + class VertexBuffer; + class IndexBuffer; + + class OpenGLVertexArray + : public VertexArray + { + public: + OpenGLVertexArray(); + virtual ~OpenGLVertexArray(); + + virtual void Bind() const override; + virtual void Unbind() const override; + + virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) override; + virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) override; + + virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const override + { + return m_VertexBuffers; + } + + virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const override + { + return m_IndexBuffer; + } + private: + uint32_t m_RendererID; + uint32_t m_VertexBufferIndex = 0; + std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers; + std::shared_ptr<IndexBuffer> m_IndexBuffer; + }; +}; diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.cpp b/src/Platform/OpenGL/OpenGLVertexBuffer.cpp new file mode 100644 index 0000000..56080c7 --- /dev/null +++ b/src/Platform/OpenGL/OpenGLVertexBuffer.cpp @@ -0,0 +1,34 @@ +#include "OpenGLVertexBuffer.h" +#include "Rendering/VertexBuffer.h" +#include <glad/glad.h> + +namespace Donut +{ + OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size) + { + glCreateBuffers(1, &m_RendererID); + glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); + glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); + } + + OpenGLVertexBuffer::~OpenGLVertexBuffer() + { + glDeleteBuffers(1, &m_RendererID); + } + + void OpenGLVertexBuffer::Bind() const + { + glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); + } + + void OpenGLVertexBuffer::Unbind() const + { + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void OpenGLVertexBuffer::SetData(const void* data, uint32_t size) + { + glBindBuffer(GL_ARRAY_BUFFER, m_RendererID); + glBufferSubData(GL_ARRAY_BUFFER, 0, size, data); + } +}; diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.h b/src/Platform/OpenGL/OpenGLVertexBuffer.h new file mode 100644 index 0000000..491af6f --- /dev/null +++ b/src/Platform/OpenGL/OpenGLVertexBuffer.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Rendering/VertexBuffer.h" + +namespace Donut +{ + class OpenGLVertexBuffer + : public VertexBuffer + { + public: + OpenGLVertexBuffer(const void* data, uint32_t size); + virtual ~OpenGLVertexBuffer(); + + virtual void Bind() const override; + virtual void Unbind() const override; + virtual void SetData(const void* data, uint32_t size) override; + + virtual const VertexBufferLayout& GetLayout() const override { return m_Layout; } + virtual void SetLayout(const VertexBufferLayout& layout) override { m_Layout = layout; } + + private: + uint32_t m_RendererID; + VertexBufferLayout m_Layout; + }; +}; diff --git a/src/Rendering/IndexBuffer.cpp b/src/Rendering/IndexBuffer.cpp new file mode 100644 index 0000000..d8854d3 --- /dev/null +++ b/src/Rendering/IndexBuffer.cpp @@ -0,0 +1,17 @@ +#include "IndexBuffer.h" +#include "Renderer.h" +#include "Platform/OpenGL/OpenGLIndexBuffer.h" + +namespace Donut +{ + IndexBuffer* IndexBuffer::Create(const uint32_t* indices, uint32_t count) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::OpenGL: + return new OpenGLIndexBuffer(indices, count); + default: + return nullptr; + } + } +}; diff --git a/src/Rendering/IndexBuffer.h b/src/Rendering/IndexBuffer.h new file mode 100644 index 0000000..0729d95 --- /dev/null +++ b/src/Rendering/IndexBuffer.h @@ -0,0 +1,18 @@ +#pragma once + +#include <cstdint> + +namespace Donut +{ + class IndexBuffer + { + public: + virtual ~IndexBuffer() = default; + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + virtual uint32_t GetCount() const = 0; + + static IndexBuffer* Create(const uint32_t* indices, uint32_t count); + }; +}; diff --git a/src/Rendering/Renderer.cpp b/src/Rendering/Renderer.cpp new file mode 100644 index 0000000..7b75a18 --- /dev/null +++ b/src/Rendering/Renderer.cpp @@ -0,0 +1,63 @@ +#include "Renderer.h" +#include "Platform/OpenGL/OpenGLRendererAPI.h" +#include <memory> +#include <glm/gtc/matrix_transform.hpp> + +namespace Donut { + + Scope<RendererAPI> RendererAPI::Create() { + switch (s_API) { + case API::OpenGL: + return std::make_unique<OpenGLRendererAPI>(); + default: + return nullptr; + } + } + + RendererAPI::API RendererAPI::s_API = RendererAPI::API::OpenGL; + + Renderer::SceneData* Renderer::s_SceneData = new Renderer::SceneData; + + void Renderer::Init() { + RenderCommand::Init(); + } + + void Renderer::Shutdown() { + delete s_SceneData; + } + + void Renderer::OnWindowResize(uint32_t width, uint32_t height) { + RenderCommand::SetViewport(0, 0, width, height); + } + + void Renderer::BeginScene() { + // Set up scene data + s_SceneData->ViewProjectionMatrix = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + } + + void Renderer::EndScene() { + // Clean up scene data + } + + void Renderer::Submit(const std::shared_ptr<Shader>& shader, + const std::shared_ptr<VertexArray>& vertexArray, + const glm::mat4& transform) { + shader->Bind(); + shader->SetMat4("u_ViewProjection", s_SceneData->ViewProjectionMatrix); + shader->SetMat4("u_Transform", transform); + + vertexArray->Bind(); + RenderCommand::DrawIndexed(vertexArray); + } + + Scope<RendererAPI> RenderCommand::s_RendererAPI = RendererAPI::Create(); + + void Renderer::SetClearColor(const glm::vec4& color) { + RenderCommand::SetClearColor(color); + } + + void Renderer::Clear() { + RenderCommand::Clear(); + } + +} diff --git a/src/Rendering/Renderer.h b/src/Rendering/Renderer.h new file mode 100644 index 0000000..29f66a5 --- /dev/null +++ b/src/Rendering/Renderer.h @@ -0,0 +1,100 @@ +#pragma once + +#include "VertexArray.h" +#include "Shader.h" +#include <glm/glm.hpp> +#include <memory> + +namespace Donut +{ + template<typename T> + using Scope = std::unique_ptr<T>; + + template<typename T, typename ... Args> + constexpr Scope<T> CreateScope(Args&& ... args) + { + return std::make_unique<T>(std::forward<Args>(args)...); + } + + class RendererAPI + { + public: + enum class API + { + None = 0, + OpenGL = 1, + Vulkan = 2, + }; + + public: + virtual ~RendererAPI() = default; + + virtual void Init() = 0; + virtual void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) = 0; + virtual void SetClearColor(const glm::vec4& color) = 0; + virtual void Clear() = 0; + + virtual void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount = 0) = 0; + + inline static API GetAPI() { return s_API; } + static Scope<RendererAPI> Create(); + + private: + static API s_API; + }; + + class RenderCommand + { + public: + inline static void Init() { + s_RendererAPI->Init(); + } + + inline static void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + s_RendererAPI->SetViewport(x, y, width, height); + } + + inline static void SetClearColor(const glm::vec4& color) { + s_RendererAPI->SetClearColor(color); + } + + inline static void Clear() { + s_RendererAPI->Clear(); + } + + inline static void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray, uint32_t indexCount = 0) { + s_RendererAPI->DrawIndexed(vertexArray, indexCount); + } + + private: + static Scope<RendererAPI> s_RendererAPI; + }; + + class Renderer + { + public: + static void Init(); + static void Shutdown(); + + static void OnWindowResize(uint32_t width, uint32_t height); + + static void BeginScene(); + static void EndScene(); + + static void Submit(const std::shared_ptr<Shader>& shader, + const std::shared_ptr<VertexArray>& vertexArray, + const glm::mat4& transform = glm::mat4(1.0f)); + + static void SetClearColor(const glm::vec4& color); + static void Clear(); + + inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); } + private: + struct SceneData + { + glm::mat4 ViewProjectionMatrix; + }; + + static SceneData* s_SceneData; + }; +}; diff --git a/src/Rendering/Shader.cpp b/src/Rendering/Shader.cpp new file mode 100644 index 0000000..b055fe4 --- /dev/null +++ b/src/Rendering/Shader.cpp @@ -0,0 +1,66 @@ +#include "Shader.h" +#include "Renderer.h" +#include "Platform/OpenGL/OpenGLShader.h" +#include <memory> + +namespace Donut +{ + Shader* Shader::Create(const std::string& filepath) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::OpenGL: + return new OpenGLShader(filepath); + default: + return nullptr; + } + } + + Shader* Shader::Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::OpenGL: + return new OpenGLShader(name, vertexSrc, fragmentSrc); + default: + return nullptr; + } + } + + void ShaderLibrary::Add(const std::shared_ptr<Shader>& shader) + { + auto& name = shader->GetName(); + Add(name, shader); + } + + void ShaderLibrary::Add(const std::string& name, const std::shared_ptr<Shader>& shader) + { + m_Shaders[name] = shader; + } + + std::shared_ptr<Shader> ShaderLibrary::Load(const std::string& filepath) + { + auto shader = std::shared_ptr<Shader>(Shader::Create(filepath)); + Add(shader); + return shader; + } + + std::shared_ptr<Shader> ShaderLibrary::Load(const std::string& name, const std::string& filepath) + { + auto shader = std::shared_ptr<Shader>(Shader::Create(filepath)); + Add(name, shader); + return shader; + } + + std::shared_ptr<Shader> ShaderLibrary::Get(const std::string& name) + { + if (Exists(name)) + return m_Shaders[name]; + return nullptr; + } + + bool ShaderLibrary::Exists(const std::string& name) const + { + return m_Shaders.find(name) != m_Shaders.end(); + } +}; diff --git a/src/Rendering/Shader.h b/src/Rendering/Shader.h new file mode 100644 index 0000000..a6b287a --- /dev/null +++ b/src/Rendering/Shader.h @@ -0,0 +1,46 @@ +#pragma once + +#include <string> +#include <unordered_map> +#include <memory> +#include <glm/glm.hpp> + +namespace Donut +{ + class Shader + { + public: + virtual ~Shader() = default; + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + + virtual void SetInt(const std::string& name, int value) = 0; + virtual void SetIntArray(const std::string& name, int* values, uint32_t count) = 0; + virtual void SetFloat(const std::string& name, float value) = 0; + virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0; + virtual void SetFloat4(const std::string& name, const glm::vec4& value) = 0; + virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0; + + virtual const std::string& GetName() const = 0; + + static Shader* Create(const std::string& filepath); + static Shader* Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc); + }; + + class ShaderLibrary + { + public: + void Add(const std::shared_ptr<Shader>& shader); + void Add(const std::string& name, const std::shared_ptr<Shader>& shader); + std::shared_ptr<Shader> Load(const std::string& filepath); + std::shared_ptr<Shader> Load(const std::string& name, const std::string& filepath); + + std::shared_ptr<Shader> Get(const std::string& name); + + bool Exists(const std::string& name) const; + + private: + std::unordered_map<std::string, std::shared_ptr<Shader>> m_Shaders; + }; +}; diff --git a/src/Rendering/VertexArray.cpp b/src/Rendering/VertexArray.cpp new file mode 100644 index 0000000..87db8fb --- /dev/null +++ b/src/Rendering/VertexArray.cpp @@ -0,0 +1,28 @@ +#include "VertexArray.h" +#include "Renderer.h" +#include "Platform/OpenGL/OpenGLVertexArray.h" + +namespace Donut +{ + uint32_t VertexBufferElement::GetSizeOfType(uint32_t type) + { + switch (type) + { + case 0x1406: return 4; // GL_FLOAT + case 0x1405: return 4; // GL_UNSIGNED_INT + case 0x1401: return 1; // GL_UNSIGNED_BYTE + default: return 0; + } + } + + VertexArray* VertexArray::Create() + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::OpenGL: + return new OpenGLVertexArray(); + default: + return nullptr; + } + } +}; diff --git a/src/Rendering/VertexArray.h b/src/Rendering/VertexArray.h new file mode 100644 index 0000000..3c8254e --- /dev/null +++ b/src/Rendering/VertexArray.h @@ -0,0 +1,26 @@ +#pragma once + +#include "VertexBuffer.h" +#include "IndexBuffer.h" +#include <memory> +#include <vector> + +namespace Donut +{ + class VertexArray + { + public: + virtual ~VertexArray() = default; + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + + virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) = 0; + virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) = 0; + + virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const = 0; + virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const = 0; + + static VertexArray* Create(); + }; +}; diff --git a/src/Rendering/VertexBuffer.cpp b/src/Rendering/VertexBuffer.cpp new file mode 100644 index 0000000..bd9c0f9 --- /dev/null +++ b/src/Rendering/VertexBuffer.cpp @@ -0,0 +1,17 @@ +#include "VertexBuffer.h" +#include "Renderer.h" +#include "Platform/OpenGL/OpenGLVertexBuffer.h" + +namespace Donut +{ + VertexBuffer* VertexBuffer::Create(const void* data, uint32_t size) + { + switch (Renderer::GetAPI()) + { + case RendererAPI::API::OpenGL: + return new OpenGLVertexBuffer(data, size); + default: + return nullptr; + } + } +}; diff --git a/src/Rendering/VertexBuffer.h b/src/Rendering/VertexBuffer.h new file mode 100644 index 0000000..807e1be --- /dev/null +++ b/src/Rendering/VertexBuffer.h @@ -0,0 +1,73 @@ +#pragma once + +#include <cstdint> +#include <vector> + +namespace Donut +{ + struct VertexBufferElement + { + uint32_t type; + uint32_t count; + uint8_t normalized; + uint32_t offset; + + static uint32_t GetSizeOfType(uint32_t type); + }; + + class VertexBufferLayout + { + public: + VertexBufferLayout() = default; + ~VertexBufferLayout() = default; + + template<typename T> + void Push(uint32_t count) + { + static_assert(false); + } + + template<> + void Push<float>(uint32_t count) + { + m_Elements.push_back({ 0x1406, count, 0, m_Stride }); // GL_FLOAT + m_Stride += count * VertexBufferElement::GetSizeOfType(0x1406); + } + + template<> + void Push<uint32_t>(uint32_t count) + { + m_Elements.push_back({ 0x1405, count, 0, m_Stride }); // GL_UNSIGNED_INT + m_Stride += count * VertexBufferElement::GetSizeOfType(0x1405); + } + + template<> + void Push<uint8_t>(uint32_t count) + { + m_Elements.push_back({ 0x1401, count, 1, m_Stride }); // GL_UNSIGNED_BYTE + m_Stride += count * VertexBufferElement::GetSizeOfType(0x1401); + } + + inline const std::vector<VertexBufferElement>& GetElements() const { return m_Elements; } + inline uint32_t GetStride() const { return m_Stride; } + + private: + std::vector<VertexBufferElement> m_Elements; + uint32_t m_Stride = 0; + }; + + class VertexBuffer + { + public: + virtual ~VertexBuffer() = default; + + virtual void Bind() const = 0; + virtual void Unbind() const = 0; + virtual void SetData(const void* data, uint32_t size) = 0; + + virtual const VertexBufferLayout& GetLayout() const = 0; + virtual void SetLayout(const VertexBufferLayout& layout) = 0; + + static VertexBuffer* Create(const void* data, uint32_t size); + }; +}; |
