aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--premake5.lua4
-rw-r--r--src/core/application.cpp49
-rw-r--r--src/platform/opengl/opengl_framebuffer.cpp246
-rw-r--r--src/platform/opengl/opengl_framebuffer.h35
-rw-r--r--src/platform/opengl/opengl_index_buffer.cpp28
-rw-r--r--src/platform/opengl/opengl_index_buffer.h22
-rw-r--r--src/platform/opengl/opengl_renderer_api.cpp118
-rw-r--r--src/platform/opengl/opengl_renderer_api.h44
-rw-r--r--src/platform/opengl/opengl_shader.cpp302
-rw-r--r--src/platform/opengl/opengl_shader.h58
-rw-r--r--src/platform/opengl/opengl_texture.cpp272
-rw-r--r--src/platform/opengl/opengl_texture.h68
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.cpp29
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.h21
-rw-r--r--src/platform/opengl/opengl_vertex_array.cpp58
-rw-r--r--src/platform/opengl/opengl_vertex_array.h41
-rw-r--r--src/platform/opengl/opengl_vertex_buffer.cpp35
-rw-r--r--src/platform/opengl/opengl_vertex_buffer.h25
-rw-r--r--src/platform/vulkan/vulkan_index_buffer.cpp25
-rw-r--r--src/platform/vulkan/vulkan_index_buffer.h22
-rw-r--r--src/platform/vulkan/vulkan_renderer.cpp191
-rw-r--r--src/platform/vulkan/vulkan_renderer.h5
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.cpp86
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.h38
-rw-r--r--src/platform/vulkan/vulkan_shader.cpp146
-rw-r--r--src/platform/vulkan/vulkan_shader.h52
-rw-r--r--src/platform/vulkan/vulkan_texture.cpp73
-rw-r--r--src/platform/vulkan/vulkan_texture.h60
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.cpp25
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.h19
-rw-r--r--src/platform/vulkan/vulkan_vertex_array.cpp36
-rw-r--r--src/platform/vulkan/vulkan_vertex_array.h28
-rw-r--r--src/platform/vulkan/vulkan_vertex_buffer.cpp34
-rw-r--r--src/platform/vulkan/vulkan_vertex_buffer.h26
-rw-r--r--src/rendering/framebuffer.cpp246
-rw-r--r--src/rendering/framebuffer.h37
-rw-r--r--src/rendering/index_buffer.cpp39
-rw-r--r--src/rendering/index_buffer.h13
-rw-r--r--src/rendering/renderer.cpp128
-rw-r--r--src/rendering/renderer.h43
-rw-r--r--src/rendering/shader.cpp324
-rw-r--r--src/rendering/shader.h58
-rw-r--r--src/rendering/texture.cpp318
-rw-r--r--src/rendering/texture.h57
-rw-r--r--src/rendering/texture_manager.cpp6
-rw-r--r--src/rendering/uniform_buffer.cpp41
-rw-r--r--src/rendering/uniform_buffer.h12
-rw-r--r--src/rendering/vertex_array.cpp67
-rw-r--r--src/rendering/vertex_array.h23
-rw-r--r--src/rendering/vertex_buffer.cpp51
-rw-r--r--src/rendering/vertex_buffer.h29
51 files changed, 1477 insertions, 2336 deletions
diff --git a/premake5.lua b/premake5.lua
index 2559098..f2d840e 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -318,10 +318,6 @@ project "Donut"
"src/**.h",
"src/**.cpp",
- "src/rendering/framebuffer.h",
- "src/rendering/framebuffer.cpp",
- "src/platform/opengl/opengl_framebuffer.h",
- "src/platform/opengl/opengl_framebuffer.cpp",
"ext/imgui/backends/imgui_impl_glfw.cpp",
"ext/imgui/backends/imgui_impl_opengl3.cpp",
diff --git a/src/core/application.cpp b/src/core/application.cpp
index 4d21586..8708417 100644
--- a/src/core/application.cpp
+++ b/src/core/application.cpp
@@ -88,30 +88,45 @@ namespace Donut
if (m_vulkan_renderer)
{
- bool free_fly = m_vulkan_renderer->is_free_fly();
- ImGui::Text("Camera");
- if (ImGui::RadioButton("Orbital", !free_fly)) m_vulkan_renderer->set_free_fly(false);
+ bool scene = m_vulkan_renderer->is_scene_mode();
+ ImGui::Text("View");
+ if (ImGui::RadioButton("Black hole", !scene)) m_vulkan_renderer->set_scene_mode(false);
ImGui::SameLine();
- if (ImGui::RadioButton("Free-fly", free_fly)) m_vulkan_renderer->set_free_fly(true);
- ImGui::TextDisabled(free_fly ? "WASD move | Q/E down-up | Shift boost | drag to look"
- : "Drag to orbit | scroll to zoom");
+ if (ImGui::RadioButton("Scene", scene)) m_vulkan_renderer->set_scene_mode(true);
ImGui::Separator();
- auto& hdri = HDRIManager::get();
- std::string current = m_vulkan_renderer->current_hdri();
- std::string preview = hdri.get_hdri_name(current);
- if (ImGui::BeginCombo("HDRI", preview.c_str()))
+ if (scene)
{
- for (const auto& path : hdri.get_available_hdri())
+ ImGui::TextDisabled("World-builder scene (grid). Drag to orbit, scroll to zoom.");
+ ImGui::TextDisabled("Lit sphere + skybox coming next.");
+ }
+ else
+ {
+ bool free_fly = m_vulkan_renderer->is_free_fly();
+ ImGui::Text("Camera");
+ if (ImGui::RadioButton("Orbital", !free_fly)) m_vulkan_renderer->set_free_fly(false);
+ ImGui::SameLine();
+ if (ImGui::RadioButton("Free-fly", free_fly)) m_vulkan_renderer->set_free_fly(true);
+ ImGui::TextDisabled(free_fly ? "WASD move | Q/E down-up | Shift boost | drag to look"
+ : "Drag to orbit | scroll to zoom");
+ ImGui::Separator();
+
+ auto& hdri = HDRIManager::get();
+ std::string current = m_vulkan_renderer->current_hdri();
+ std::string preview = hdri.get_hdri_name(current);
+ if (ImGui::BeginCombo("HDRI", preview.c_str()))
{
- bool selected = (path == current);
- if (ImGui::Selectable(hdri.get_hdri_name(path).c_str(), selected))
- m_vulkan_renderer->set_hdri(path);
- if (selected) ImGui::SetItemDefaultFocus();
+ for (const auto& path : hdri.get_available_hdri())
+ {
+ bool selected = (path == current);
+ if (ImGui::Selectable(hdri.get_hdri_name(path).c_str(), selected))
+ m_vulkan_renderer->set_hdri(path);
+ if (selected) ImGui::SetItemDefaultFocus();
+ }
+ ImGui::EndCombo();
}
- ImGui::EndCombo();
+ ImGui::TextDisabled("Switching rebuilds the cubemap (brief pause).");
}
- ImGui::TextDisabled("Switching rebuilds the cubemap (brief pause).");
}
ImGui::End();
diff --git a/src/platform/opengl/opengl_framebuffer.cpp b/src/platform/opengl/opengl_framebuffer.cpp
deleted file mode 100644
index 1624439..0000000
--- a/src/platform/opengl/opengl_framebuffer.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-#include "opengl_framebuffer.h"
-#include "core/log.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- namespace Utils
- {
- static GLenum TextureTarget(bool multisampled)
- {
- return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
- }
-
- static void bind_texture(bool multisampled, uint32_t id)
- {
- glBindTexture(TextureTarget(multisampled), id);
- }
-
- static void CreateTextures(bool multisampled, uint32_t* out_id, uint32_t count)
- {
- // glCreateTextures is 4.5 DSA; macOS caps at 4.1. Callers bind each
- // texture (with the correct target) before use.
- glGenTextures(count, out_id);
- }
-
- static void AttachColorTexture(uint32_t id, int samples, GLenum internal_format, GLenum format, uint32_t width, uint32_t height, int index)
- {
- bool multisampled = samples > 1;
- if (multisampled)
- {
- glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internal_format, width, height, GL_FALSE);
- }
- else
- {
- // Integer color formats require an integer pixel type even when
- // data is null, or macOS's strict core profile rejects the call.
- GLenum type = (format == GL_RED_INTEGER) ? GL_INT : GL_UNSIGNED_BYTE;
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0);
- }
-
- static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachment_type, uint32_t width, uint32_t height)
- {
- bool multisampled = samples > 1;
- if (multisampled)
- {
- glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE);
- }
- else
- {
- // glTexStorage2D is 4.2; use mutable storage for macOS (4.1).
- GLenum depth_format = (format == GL_DEPTH24_STENCIL8) ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
- GLenum depth_type = (format == GL_DEPTH24_STENCIL8) ? GL_UNSIGNED_INT_24_8 : GL_FLOAT;
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, depth_format, depth_type, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- }
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, attachment_type, TextureTarget(multisampled), id, 0);
- }
-
- static bool IsDepthFormat(FramebufferTextureFormat format)
- {
- switch (format)
- {
- case FramebufferTextureFormat::DEPTH24STENCIL8: return true;
- }
- return false;
- }
-
- static GLenum DonutFBTextureFormatToGL(FramebufferTextureFormat format)
- {
- switch (format)
- {
- case FramebufferTextureFormat::RGBA8: return GL_RGBA8;
- case FramebufferTextureFormat::RED_INTEGER: return GL_RED_INTEGER;
- }
-
- return 0;
- }
- }
-
- OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec)
- : m_specification(spec)
- {
- for (auto spec : m_specification.attachments.attachments)
- {
- if (!Utils::IsDepthFormat(spec.texture_format))
- m_color_attachment_specifications.emplace_back(spec);
- else
- m_depth_attachment_specification = spec;
- }
-
- invalidate();
- }
-
- OpenGLFramebuffer::~OpenGLFramebuffer()
- {
- glDeleteFramebuffers(1, &m_renderer_id);
- glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
- glDeleteTextures(1, &m_depth_attachment);
- }
-
- auto OpenGLFramebuffer::invalidate() -> void
- {
- if (m_renderer_id)
- {
- glDeleteFramebuffers(1, &m_renderer_id);
- glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
- glDeleteTextures(1, &m_depth_attachment);
-
- m_color_attachments.clear();
- m_depth_attachment = 0;
- }
-
- glGenFramebuffers(1, &m_renderer_id); // glCreateFramebuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
-
- bool multisample = m_specification.Samples > 1;
-
- if (m_color_attachment_specifications.size())
- {
- m_color_attachments.resize(m_color_attachment_specifications.size());
- Utils::CreateTextures(multisample, m_color_attachments.data(), static_cast<uint32_t>(m_color_attachments.size()));
-
- for (size_t i = 0; i < m_color_attachments.size(); i++)
- {
- Utils::bind_texture(multisample, m_color_attachments[i]);
- switch (m_color_attachment_specifications[i].texture_format)
- {
- case FramebufferTextureFormat::RGBA8:
- Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_RGBA8, GL_RGBA, m_specification.Width, m_specification.Height, static_cast<int>(i));
- break;
- case FramebufferTextureFormat::RED_INTEGER:
- Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_R32I, GL_RED_INTEGER, m_specification.Width, m_specification.Height, static_cast<int>(i));
- break;
- }
- }
- }
-
- if (m_depth_attachment_specification.texture_format != FramebufferTextureFormat::None)
- {
- Utils::CreateTextures(multisample, &m_depth_attachment, 1);
- Utils::bind_texture(multisample, m_depth_attachment);
- switch (m_depth_attachment_specification.texture_format)
- {
- case FramebufferTextureFormat::DEPTH24STENCIL8:
- Utils::AttachDepthTexture(m_depth_attachment, m_specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_specification.Width, m_specification.Height);
- break;
- }
- }
-
- if (m_color_attachments.size() > 1)
- {
- GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
- glDrawBuffers(static_cast<GLsizei>(m_color_attachments.size()), buffers);
- }
- else if (m_color_attachments.empty())
- glDrawBuffer(GL_NONE);
-
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
- {
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- switch (status)
- {
- case GL_FRAMEBUFFER_UNDEFINED:
- DONUT_ERROR("Framebuffer is undefined");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- DONUT_ERROR("Framebuffer has incomplete attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- DONUT_ERROR("Framebuffer is missing attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
- DONUT_ERROR("Framebuffer has incomplete draw buffer");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
- DONUT_ERROR("Framebuffer has incomplete read buffer");
- break;
- case GL_FRAMEBUFFER_UNSUPPORTED:
- DONUT_ERROR("Framebuffer format is unsupported");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
- DONUT_ERROR("Framebuffer has incomplete multisample");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
- DONUT_ERROR("Framebuffer has incomplete layer targets");
- break;
- default:
- DONUT_ERROR("Framebuffer is incomplete (unknown error: {})", status);
- break;
- }
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- }
-
- auto OpenGLFramebuffer::bind() -> void
- {
- glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
- glViewport(0, 0, m_specification.Width, m_specification.Height);
- }
-
- auto OpenGLFramebuffer::unbind() -> void
- {
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- }
-
- auto OpenGLFramebuffer::resize(uint32_t width, uint32_t height) -> void
- {
- m_specification.Width = width;
- m_specification.Height = height;
-
- invalidate();
- }
-
- auto OpenGLFramebuffer::read_pixel(uint32_t attachment_index, int x, int y) -> int
- {
- glReadBuffer(GL_COLOR_ATTACHMENT0 + attachment_index);
- int pixel_data;
- glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixel_data);
- return pixel_data;
- }
-
- auto OpenGLFramebuffer::clear_attachment(uint32_t attachment_index, int value) -> void
- {
- // glClearTexImage is 4.4 and unavailable on macOS. clear the integer
- // attachment by binding this framebuffer and clearing its draw buffer.
- glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
- glClearBufferiv(GL_COLOR, static_cast<GLint>(attachment_index), &value);
- }
-};
diff --git a/src/platform/opengl/opengl_framebuffer.h b/src/platform/opengl/opengl_framebuffer.h
deleted file mode 100644
index 456d924..0000000
--- a/src/platform/opengl/opengl_framebuffer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "rendering/framebuffer.h"
-
-namespace Donut
-{
- class OpenGLFramebuffer : public Framebuffer
- {
- public:
- OpenGLFramebuffer(const FramebufferSpecification& spec);
- virtual ~OpenGLFramebuffer();
-
- auto invalidate() -> void;
-
- virtual auto bind() -> void override;
- virtual auto unbind() -> void override;
-
- virtual auto resize(uint32_t width, uint32_t height) -> void override;
- virtual auto read_pixel(uint32_t attachment_index, int x, int y) -> int override;
-
- virtual auto clear_attachment(uint32_t attachment_index, int value) -> void override;
- virtual auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t override{ return m_color_attachments[index]; }
-
- virtual auto get_specification() const -> const FramebufferSpecification& override{ return m_specification; }
- private:
- uint32_t m_renderer_id = 0;
- FramebufferSpecification m_specification;
-
- std::vector<FramebufferTextureSpecification> m_color_attachment_specifications;
- FramebufferTextureSpecification m_depth_attachment_specification = FramebufferTextureFormat::None;
-
- std::vector<uint32_t> m_color_attachments;
- uint32_t m_depth_attachment = 0;
- };
-};
diff --git a/src/platform/opengl/opengl_index_buffer.cpp b/src/platform/opengl/opengl_index_buffer.cpp
deleted file mode 100644
index faa6b81..0000000
--- a/src/platform/opengl/opengl_index_buffer.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "opengl_index_buffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count)
- : m_count(count)
- {
- glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
- }
-
- OpenGLIndexBuffer::~OpenGLIndexBuffer()
- {
- glDeleteBuffers(1, &m_renderer_id);
- }
-
- auto OpenGLIndexBuffer::bind() const -> void
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
- }
-
- auto OpenGLIndexBuffer::unbind() const -> void
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- }
-};
diff --git a/src/platform/opengl/opengl_index_buffer.h b/src/platform/opengl/opengl_index_buffer.h
deleted file mode 100644
index 397da43..0000000
--- a/src/platform/opengl/opengl_index_buffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "rendering/index_buffer.h"
-
-namespace Donut
-{
- class OpenGLIndexBuffer
- : public IndexBuffer
- {
- public:
- OpenGLIndexBuffer(const uint32_t* indices, uint32_t count);
- virtual ~OpenGLIndexBuffer();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
- virtual auto get_count() const -> uint32_t override{ return m_count; }
-
- private:
- uint32_t m_renderer_id;
- uint32_t m_count;
- };
-};
diff --git a/src/platform/opengl/opengl_renderer_api.cpp b/src/platform/opengl/opengl_renderer_api.cpp
deleted file mode 100644
index 39ed786..0000000
--- a/src/platform/opengl/opengl_renderer_api.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-#include "opengl_renderer_api.h"
-
-#include <glad/glad.h>
-#include <GLFW/glfw3.h>
-
-namespace Donut
-{
- auto OpenGLRendererAPI::init() -> void
- {
- if (!glfwGetCurrentContext())
- {
- DONUT_ERROR("No OpenGL context is current! Cannot initialize GLAD.");
- return;
- }
-
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
- {
- DONUT_ERROR("Failed to initialize GLAD!");
- return;
- }
-
- 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);
- }
-
- auto OpenGLRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
- {
- glViewport(x, y, width, height);
- }
-
- auto OpenGLRendererAPI::set_clear_color(const glm::vec4& color) -> void
- {
- glClearColor(color.r, color.g, color.b, color.a);
- }
-
- auto OpenGLRendererAPI::clear() -> void
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- }
-
- auto OpenGLRendererAPI::enable_depth_test() -> void
- {
- glEnable(GL_DEPTH_TEST);
- }
-
- auto OpenGLRendererAPI::disable_depth_test() -> void
- {
- glDisable(GL_DEPTH_TEST);
- }
-
- auto OpenGLRendererAPI::set_face_culling(bool enabled) -> void
- {
- if (enabled)
- {
- glEnable(GL_CULL_FACE);
- glCullFace(GL_BACK);
- glFrontFace(GL_CCW);
- }
- else
- glDisable(GL_CULL_FACE);
- }
-
- auto OpenGLRendererAPI::enable_blending() -> void
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- }
-
- auto OpenGLRendererAPI::disable_blending() -> void
- {
- glDisable(GL_BLEND);
- }
-
- auto OpenGLRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
- {
- uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
- glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
- glBindTexture(GL_TEXTURE_2D, 0);
- }
-
- auto OpenGLRendererAPI::draw_arrays(uint32_t vertex_count, uint32_t first) -> void
- {
- glDrawArrays(GL_TRIANGLES, first, vertex_count);
- }
-
- auto OpenGLRendererAPI::draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
- {
- uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
- glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, nullptr);
- }
-
- auto OpenGLRendererAPI::bind_texture(uint32_t texture_id, uint32_t slot) -> void
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, texture_id);
- }
-
- auto OpenGLRendererAPI::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
- {
- // Image load/store is OpenGL 4.2; the pointer is null on macOS (4.1).
- if (glBindImageTexture == nullptr)
- return;
- glBindImageTexture(slot, texture_id, 0, GL_FALSE, 0,
- read_only ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
- }
-
- auto OpenGLRendererAPI::read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) -> void
- {
- glReadPixels(x, y, width, height, format, type, pixels);
- }
-};
diff --git a/src/platform/opengl/opengl_renderer_api.h b/src/platform/opengl/opengl_renderer_api.h
deleted file mode 100644
index 02f18ab..0000000
--- a/src/platform/opengl/opengl_renderer_api.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include "core/memory.h"
-#include "core/log.h"
-
-#include "rendering/renderer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLRendererAPI
- : public RendererAPI
- {
- public:
- virtual auto init() -> void override;
- virtual void set_viewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual auto set_clear_color(const glm::vec4& color) -> void override;
- virtual auto clear() -> void override;
- virtual auto enable_depth_test() -> void override;
- virtual auto disable_depth_test() -> void override;
- virtual auto set_face_culling(bool enabled) -> void override;
- virtual auto enable_blending() -> void override;
- virtual auto disable_blending() -> void override;
-
- virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) override;
-
- virtual void draw_arrays(uint32_t vertex_count,
- uint32_t first = 0) override;
- virtual void draw_lines(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) override;
- virtual void bind_texture(uint32_t texture_id,
- uint32_t slot = 0) override;
- virtual void bind_image_texture(uint32_t texture_id,
- uint32_t slot = 0,
- bool read_only = false) override;
- virtual void read_pixels(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height,
- uint32_t format, uint32_t type,
- void* pixels) override;
- };
-};
diff --git a/src/platform/opengl/opengl_shader.cpp b/src/platform/opengl/opengl_shader.cpp
deleted file mode 100644
index d15d7e0..0000000
--- a/src/platform/opengl/opengl_shader.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-#include "opengl_shader.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;
- if (type == "compute")
- return GL_COMPUTE_SHADER;
- return 0;
- }
-
- // Shaders are authored in Slang and compiled to assets/shaders/generated/
- // <name>.glsl by Tools/compile-shaders.sh. Given a legacy ".../<name>.glsl"
- // path, prefer that generated file when present; otherwise fall back to the
- // hand-written GLSL (e.g. shaders not yet ported to Slang).
- static std::string ResolveShaderPath(const std::string& filepath)
- {
- size_t slash = filepath.find_last_of("/\\");
- std::string dir = (slash == std::string::npos) ? std::string() : filepath.substr(0, slash + 1);
- std::string file = (slash == std::string::npos) ? filepath : filepath.substr(slash + 1);
- size_t dot = file.rfind('.');
- std::string base = (dot == std::string::npos) ? file : file.substr(0, dot);
-
- std::string generated = dir + "generated/" + base + ".glsl";
- std::ifstream test(generated);
- if (test.good())
- return generated;
- return filepath;
- }
-
- OpenGLShader::OpenGLShader(const std::string& filepath)
- {
- std::string resolved = ResolveShaderPath(filepath);
- m_is_slang = (resolved != filepath);
- std::string source = read_file(resolved);
- auto shader_sources = pre_process(source);
- compile(shader_sources);
-
- auto last_slash = filepath.find_last_of("/\\");
- last_slash = last_slash == std::string::npos ? 0 : last_slash + 1;
- auto last_dot = filepath.rfind('.');
- auto count = last_dot == std::string::npos ? filepath.size() - last_slash : last_dot - last_slash;
- m_name = filepath.substr(last_slash, count);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
- : m_name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_VERTEX_SHADER] = vertex_src;
- sources[GL_FRAGMENT_SHADER] = fragment_src;
- compile(sources);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& compute_src)
- : m_name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_COMPUTE_SHADER] = compute_src;
- compile(sources);
- }
-
- OpenGLShader::~OpenGLShader()
- {
- glDeleteProgram(m_renderer_id);
- }
-
- auto OpenGLShader::read_file(const std::string& filepath) -> std::string
- {
- 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;
- }
-
- auto OpenGLShader::pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>
- {
- std::unordered_map<uint32_t, std::string> shader_sources;
-
- const char* type_token = "#type";
- size_t type_token_length = strlen(type_token);
- size_t pos = source.find(type_token, 0);
-
- while (pos != std::string::npos)
- {
- size_t eol = source.find_first_of("\r\n", pos);
- size_t begin = pos + type_token_length + 1;
- std::string type = source.substr(begin, eol - begin);
-
- size_t next_line_pos = source.find_first_not_of("\r\n", eol);
- pos = source.find(type_token, next_line_pos);
- shader_sources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(next_line_pos) :
- source.substr(next_line_pos, pos - next_line_pos);
- }
-
- return shader_sources;
- }
-
- auto OpenGLShader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void
- {
- uint32_t program = glCreateProgram();
- std::vector<uint32_t> glShaderIDs(shader_sources.size());
- for (auto& kv : shader_sources)
- {
- uint32_t type = kv.first;
- const std::string& source = kv.second;
-
- uint32_t shader = glCreateShader(type);
- const char* source_c_str = source.c_str();
- glShaderSource(shader, 1, &source_c_str, 0);
- glCompileShader(shader);
-
- int is_compiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled);
- if (is_compiled == GL_FALSE)
- {
- int max_length = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length);
- std::vector<char> info_log(max_length);
- glGetShaderInfoLog(shader, max_length, &max_length, &info_log[0]);
- glDeleteShader(shader);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- glDeleteProgram(program);
- m_renderer_id = 0;
- // info_log.data() is null when the driver returns an empty log
- // (e.g. macOS rejecting a compute shader); streaming a null
- // char* into std::cout calls strlen(NULL) and crashes.
- const char* log = info_log.empty() ? "" : info_log.data();
- std::cout << "Shader compilation failure!" << std::endl << log << std::endl;
- return;
- }
- glAttachShader(program, shader);
- glShaderIDs.push_back(shader);
- }
-
- m_renderer_id = program;
- glLinkProgram(m_renderer_id);
-
- int is_linked = 0;
- glGetProgramiv(m_renderer_id, GL_LINK_STATUS, (int*)&is_linked);
- if (is_linked == GL_FALSE)
- {
- int max_length = 0;
- glGetProgramiv(m_renderer_id, GL_INFO_LOG_LENGTH, &max_length);
- std::vector<char> info_log(max_length);
- glGetProgramInfoLog(m_renderer_id, max_length, &max_length, &info_log[0]);
- glDeleteProgram(m_renderer_id);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- m_renderer_id = 0;
- const char* log = info_log.empty() ? "" : info_log.data();
- std::cout << "Shader link failure!" << std::endl << log << std::endl;
- return;
- }
-
- for (auto id : glShaderIDs)
- {
- glDetachShader(m_renderer_id, id);
- glDeleteShader(id);
- }
- }
-
- auto OpenGLShader::bind() const -> void
- {
- glUseProgram(m_renderer_id);
- }
-
- auto OpenGLShader::unbind() const -> void
- {
- glUseProgram(0);
- }
-
- auto OpenGLShader::set_int(const std::string& name, int value) -> void
- {
- upload_uniform_int(name, value);
- }
-
- auto OpenGLShader::set_int_array(const std::string& name, int* values, uint32_t count) -> void
- {
- upload_uniform_int_array(name, values, count);
- }
-
- auto OpenGLShader::set_float(const std::string& name, float value) -> void
- {
- upload_uniform_float(name, value);
- }
-
- auto OpenGLShader::set_float2(const std::string& name, const glm::vec2& value) -> void
- {
- upload_uniform_float2(name, value);
- }
-
- auto OpenGLShader::set_float3(const std::string& name, const glm::vec3& value) -> void
- {
- upload_uniform_float3(name, value);
- }
-
- auto OpenGLShader::set_float4(const std::string& name, const glm::vec4& value) -> void
- {
- upload_uniform_float4(name, value);
- }
-
- auto OpenGLShader::set_mat4(const std::string& name, const glm::mat4& value) -> void
- {
- upload_uniform_mat4(name, value);
- }
-
- auto OpenGLShader::upload_uniform_int(const std::string& name, int value) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform1i(location, value);
- }
-
- auto OpenGLShader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform1iv(location, count, values);
- }
-
- auto OpenGLShader::upload_uniform_float(const std::string& name, float value) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform1f(location, value);
- }
-
- auto OpenGLShader::upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform2f(location, value.x, value.y);
- }
-
- auto OpenGLShader::upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform3f(location, value.x, value.y, value.z);
- }
-
- auto OpenGLShader::upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniform4f(location, value.x, value.y, value.z, value.w);
- }
-
- auto OpenGLShader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniformMatrix3fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- auto OpenGLShader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void
- {
- int location = glGetUniformLocation(m_renderer_id, name.c_str());
- glUniformMatrix4fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- auto OpenGLShader::dispatch(uint32_t x, uint32_t y, uint32_t z) -> void
- {
- // Compute shaders require OpenGL 4.3+. On drivers that cap out earlier
- // (e.g. macOS, which is frozen at 4.1) glDispatchCompute is never
- // loaded and the pointer is null. Guard so we no-op instead of crash.
- if (m_renderer_id == 0 || glDispatchCompute == nullptr)
- return;
- glDispatchCompute(x, y, z);
- }
-
- auto OpenGLShader::dispatch_indirect(uint32_t offset) -> void
- {
- if (m_renderer_id == 0 || glDispatchComputeIndirect == nullptr)
- return;
- glDispatchComputeIndirect(offset);
- }
-
- auto OpenGLShader::memory_barrier(uint32_t barriers) -> void
- {
- if (glMemoryBarrier == nullptr)
- return;
- glMemoryBarrier(barriers);
- }
-};
diff --git a/src/platform/opengl/opengl_shader.h b/src/platform/opengl/opengl_shader.h
deleted file mode 100644
index 289bfac..0000000
--- a/src/platform/opengl/opengl_shader.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#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& vertex_src, const std::string& fragment_src);
- OpenGLShader(const std::string& name, const std::string& compute_src);
- virtual ~OpenGLShader();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto set_int( const std::string& name, int value) -> void override;
- virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void override;
- virtual auto set_float( const std::string& name, float value) -> void override;
- virtual auto set_float2( const std::string& name, const glm::vec2& value) -> void override;
- virtual auto set_float3( const std::string& name, const glm::vec3& value) -> void override;
- virtual auto set_float4( const std::string& name, const glm::vec4& value) -> void override;
- virtual auto set_mat4( const std::string& name, const glm::mat4& value) -> void override;
-
- virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void override;
- virtual auto dispatch_indirect(uint32_t offset = 0) -> void override;
- virtual auto memory_barrier(uint32_t barriers) -> void override;
-
- virtual auto get_name() const -> const std::string& override{ return m_name; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- auto upload_uniform_int( const std::string& name, int value) -> void;
- auto upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void;
- auto upload_uniform_float( const std::string& name, float value) -> void;
- auto upload_uniform_float2( const std::string& name, const glm::vec2& value) -> void;
- auto upload_uniform_float3( const std::string& name, const glm::vec3& value) -> void;
- auto upload_uniform_float4( const std::string& name, const glm::vec4& value) -> void;
- auto upload_uniform_mat3( const std::string& name, const glm::mat3& matrix) -> void;
- auto upload_uniform_mat4( const std::string& name, const glm::mat4& matrix) -> void;
-
- private:
- auto read_file(const std::string& filepath) -> std::string;
- auto pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>;
- auto compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void;
- private:
- uint32_t m_renderer_id = 0;
- std::string m_name;
- // True when loaded from a Slang-compiled GLSL. Slang expects row-major
- // matrix data, so matrix uniforms are transposed on upload (glm is
- // column-major) to keep all matrix math correct.
- bool m_is_slang = false;
- };
-};
diff --git a/src/platform/opengl/opengl_texture.cpp b/src/platform/opengl/opengl_texture.cpp
deleted file mode 100644
index 6bcb9cd..0000000
--- a/src/platform/opengl/opengl_texture.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-#include "opengl_texture.h"
-
-#include "rendering/shader.h"
-
-#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h"
-#include <glm/glm.hpp>
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-// NOTE: This file targets OpenGL 4.1 (the maximum macOS exposes). It uses the
-// classic bind-based texture API rather than 4.5 Direct State Access
-// (glCreateTextures / glTextureStorage2D / glTextureParameteri / glBindTextureUnit),
-// none of which exist on macOS.
-
-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;
-
- glGenTextures(1, &m_renderer_id);
- glBindTexture(GL_TEXTURE_2D, m_renderer_id);
- glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, 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;
-
- glGenTextures(1, &m_renderer_id);
- glBindTexture(GL_TEXTURE_2D, m_renderer_id);
- glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
- uint32_t white_pixel = 0xFFFFFFFF;
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, &white_pixel);
-
- DONUT_INFO("Created default texture (stb_image not available for loading: ", path, ")");
- }
-
- OpenGLTexture2D::~OpenGLTexture2D()
- {
- glDeleteTextures(1, &m_renderer_id);
- }
-
- auto OpenGLTexture2D::set_data(void* data, uint32_t size) -> void
- {
- uint32_t bpp = m_data_format == GL_RGBA ? 4 : 3;
- if (size != m_width * m_height * bpp)
- {
- DONUT_ERROR("Data must be entire texture!");
- return;
- }
-
- glBindTexture(GL_TEXTURE_2D, m_renderer_id);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, data);
- }
-
- auto OpenGLTexture2D::bind(uint32_t slot) const -> void
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, m_renderer_id);
- }
-
- auto OpenGLTexture2D::bind_as_image(uint32_t slot, bool read_only) const -> void
- {
- // Image load/store is OpenGL 4.2 and unavailable on macOS. Guard the
- // function pointer so this degrades to a no-op instead of crashing.
- if (glBindImageTexture == nullptr)
- return;
- GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_renderer_id, 0, GL_FALSE, 0, access, m_internal_format);
- }
-
- OpenGLCubemapTexture::OpenGLCubemapTexture(uint32_t width, uint32_t height)
- : m_width(width), m_height(height)
- {
- m_internal_format = GL_RGBA16F;
- m_data_format = GL_RGBA;
-
- glGenTextures(1, &m_renderer_id);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
- for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
-
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
- }
-
- OpenGLCubemapTexture::OpenGLCubemapTexture(const std::string& path)
- : m_path(path)
- {
- m_width = 1024;
- m_height = 1024;
- m_internal_format = GL_RGBA16F;
- m_data_format = GL_RGBA;
-
- glGenTextures(1, &m_renderer_id);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
- for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
-
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
-
- LoadHDRI(path);
- }
-
- OpenGLCubemapTexture::~OpenGLCubemapTexture()
- {
- glDeleteTextures(1, &m_renderer_id);
- }
-
- auto OpenGLCubemapTexture::LoadHDRI(const std::string& path) -> void
- {
- stbi_set_flip_vertically_on_load(true);
- int width, height, channels;
- float* hdr_data = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
-
- if (!hdr_data)
- {
- DONUT_ERROR("Failed to load HDRI: {}", path);
- float default_sky[6 * 4] =
- {
- 0.5f, 0.7f, 1.0f, 1.0f, // Right
- 0.5f, 0.7f, 1.0f, 1.0f, // Left
- 0.5f, 0.7f, 1.0f, 1.0f, // Top
- 0.5f, 0.7f, 1.0f, 1.0f, // Bottom
- 0.5f, 0.7f, 1.0f, 1.0f, // Front
- 0.5f, 0.7f, 1.0f, 1.0f // Back
- };
-
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
- for (int i = 0; i < 6; ++i)
- glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, &default_sky[i * 4]);
- return;
- }
-
- convert_equirectangular_to_cubemap(hdr_data, width, height);
- stbi_image_free(hdr_data);
-
- DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height);
- }
-
- auto OpenGLCubemapTexture::convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void
- {
- uint32_t capture_fbo, capture_rbo;
- glGenFramebuffers(1, &capture_fbo);
- glGenRenderbuffers(1, &capture_rbo);
-
- glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
- glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_width, m_height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, capture_rbo);
-
- uint32_t hdr_texture;
- glGenTextures(1, &hdr_texture);
- glBindTexture(GL_TEXTURE_2D, hdr_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdr_data);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
- auto equirect_shader = Shader::create("assets/shaders/EquirectToCubemap.glsl");
- if (!equirect_shader)
- {
- DONUT_ERROR("Failed to create equirectangular to cubemap shader");
- return;
- }
-
- uint32_t shader_program = equirect_shader->get_renderer_id();
-
- float vertices[] =
- {
- -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
- -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
- 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
- -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
- -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
- -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f
- };
-
- uint32_t cube_vao, cube_vbo;
- glGenVertexArrays(1, &cube_vao);
- glGenBuffers(1, &cube_vbo);
- glBindVertexArray(cube_vao);
- glBindBuffer(GL_ARRAY_BUFFER, cube_vbo);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
-
- glm::mat4 capture_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- glm::mat4 capture_views[] =
- {
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
- glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
- };
-
- glUseProgram(shader_program);
- glUniform1i(glGetUniformLocation(shader_program, "u_EquirectangularMap"), 0);
- // EquirectToCubemap is authored in Slang (row-major); transpose glm's
- // column-major matrices on upload (GL_TRUE) to match.
- glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_Projection"), 1, GL_TRUE, &capture_projection[0][0]);
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, hdr_texture);
-
- glViewport(0, 0, m_width, m_height);
- glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
- for (unsigned int i = 0; i < 6; ++i)
- {
- glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_View"), 1, GL_TRUE, &capture_views[i][0][0]);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_renderer_id, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glBindVertexArray(cube_vao);
- glDrawArrays(GL_TRIANGLES, 0, 36);
- }
- glBindVertexArray(0);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- glDeleteVertexArrays(1, &cube_vao);
- glDeleteBuffers(1, &cube_vbo);
- glDeleteTextures(1, &hdr_texture);
- glDeleteFramebuffers(1, &capture_fbo);
- glDeleteRenderbuffers(1, &capture_rbo);
- }
-
- auto OpenGLCubemapTexture::set_data(void* data, uint32_t size) -> void
- {
- DONUT_WARN("set_data not implemented for cubemaps");
- }
-
- auto OpenGLCubemapTexture::bind(uint32_t slot) const -> void
- {
- glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
- }
-
- auto OpenGLCubemapTexture::bind_as_image(uint32_t slot, bool read_only) const -> void
- {
- if (glBindImageTexture == nullptr)
- return;
- GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_renderer_id, 0, GL_TRUE, 0, access, m_internal_format);
- }
-};
diff --git a/src/platform/opengl/opengl_texture.h b/src/platform/opengl/opengl_texture.h
deleted file mode 100644
index 7aa87ea..0000000
--- a/src/platform/opengl/opengl_texture.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include "rendering/texture.h"
-#include "core/log.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLTexture2D
- : public Texture2D
- {
- public:
- OpenGLTexture2D(uint32_t width, uint32_t height);
- OpenGLTexture2D(const std::string& path);
- virtual ~OpenGLTexture2D();
-
- virtual auto get_width() const -> uint32_t override{ return m_width; }
- virtual auto get_height() const -> uint32_t override{ return m_height; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- virtual auto set_data(void* data, uint32_t size) -> void override;
- virtual auto bind(uint32_t slot = 0) const -> void override;
- virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void 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;
- };
-
- class OpenGLCubemapTexture
- : public CubemapTexture
- {
- public:
- OpenGLCubemapTexture(uint32_t width, uint32_t height);
- OpenGLCubemapTexture(const std::string& path);
- virtual ~OpenGLCubemapTexture();
-
- virtual auto get_width() const -> uint32_t override{ return m_width; }
- virtual auto get_height() const -> uint32_t override{ return m_height; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- virtual auto set_data(void* data, uint32_t size) -> void override;
- virtual auto bind(uint32_t slot = 0) const -> void override;
- virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_renderer_id == other.get_renderer_id();
- }
-
- private:
- void LoadHDRI(const std::string& path);
- auto convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void;
-
- 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/platform/opengl/opengl_uniform_buffer.cpp b/src/platform/opengl/opengl_uniform_buffer.cpp
deleted file mode 100644
index 9b3b6d2..0000000
--- a/src/platform/opengl/opengl_uniform_buffer.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "opengl_uniform_buffer.h"
-
-namespace Donut
-{
- OpenGLUniformBuffer::OpenGLUniformBuffer(uint32_t size, uint32_t binding)
- : m_size(size), m_binding(binding)
- {
- glGenBuffers(1, &m_renderer_id);
- glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
- glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
- }
-
- OpenGLUniformBuffer::~OpenGLUniformBuffer()
- {
- glDeleteBuffers(1, &m_renderer_id);
- }
-
- auto OpenGLUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset) -> void
- {
- glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
- glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
- }
-
- auto OpenGLUniformBuffer::bind(uint32_t binding) -> void
- {
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
- }
-};
diff --git a/src/platform/opengl/opengl_uniform_buffer.h b/src/platform/opengl/opengl_uniform_buffer.h
deleted file mode 100644
index db137a0..0000000
--- a/src/platform/opengl/opengl_uniform_buffer.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include "rendering/uniform_buffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLUniformBuffer : public UniformBuffer
- {
- public:
- OpenGLUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~OpenGLUniformBuffer();
-
- virtual auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void override;
- virtual auto bind(uint32_t binding) -> void override;
- private:
- uint32_t m_renderer_id = 0;
- uint32_t m_size = 0;
- uint32_t m_binding = 0;
- };
-};
diff --git a/src/platform/opengl/opengl_vertex_array.cpp b/src/platform/opengl/opengl_vertex_array.cpp
deleted file mode 100644
index 01e6f56..0000000
--- a/src/platform/opengl/opengl_vertex_array.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <glad/glad.h>
-
-#include "opengl_vertex_array.h"
-#include "rendering/vertex_buffer.h"
-#include "rendering/index_buffer.h"
-
-namespace Donut
-{
- OpenGLVertexArray::OpenGLVertexArray()
- {
- // glCreateVertexArrays is 4.5 DSA; macOS caps at 4.1. glGenVertexArrays
- // reserves the name and the VAO is created on first bind (done below).
- glGenVertexArrays(1, &m_renderer_id);
- }
-
- OpenGLVertexArray::~OpenGLVertexArray()
- {
- glDeleteVertexArrays(1, &m_renderer_id);
- }
-
- auto OpenGLVertexArray::bind() const -> void
- {
- glBindVertexArray(m_renderer_id);
- }
-
- auto OpenGLVertexArray::unbind() const -> void
- {
- glBindVertexArray(0);
- }
-
- auto OpenGLVertexArray::add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void
- {
- glBindVertexArray(m_renderer_id);
- vertex_buffer->bind();
-
- const auto& layout = vertex_buffer->get_layout();
- for (const auto& element : layout.get_elements())
- {
- glEnableVertexAttribArray(m_vertex_buffer_index);
- glVertexAttribPointer(m_vertex_buffer_index,
- element.count,
- element.type,
- element.normalized ? GL_TRUE : GL_FALSE,
- layout.get_stride(),
- reinterpret_cast<const void*>(static_cast<uintptr_t>(element.offset)));
- m_vertex_buffer_index++;
- }
-
- m_vertex_buffers.push_back(vertex_buffer);
- }
-
- auto OpenGLVertexArray::set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void
- {
- glBindVertexArray(m_renderer_id);
- index_buffer->bind();
- m_index_buffer = index_buffer;
- }
-};
diff --git a/src/platform/opengl/opengl_vertex_array.h b/src/platform/opengl/opengl_vertex_array.h
deleted file mode 100644
index 5115830..0000000
--- a/src/platform/opengl/opengl_vertex_array.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "core/memory.h"
-
-#include "rendering/vertex_array.h"
-#include "rendering/vertex_buffer.h"
-#include "rendering/index_buffer.h"
-
-#include <vector>
-
-namespace Donut
-{
- class OpenGLVertexArray
- : public VertexArray
- {
- public:
- OpenGLVertexArray();
- virtual ~OpenGLVertexArray();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void override;
- virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void override;
-
- virtual const std::vector<Ref<VertexBuffer>>& get_vertex_buffers() const override
- {
- return m_vertex_buffers;
- }
-
- virtual const Ref<IndexBuffer>& get_index_buffer() const override
- {
- return m_index_buffer;
- }
- private:
- uint32_t m_renderer_id;
- uint32_t m_vertex_buffer_index = 0;
- std::vector<Ref<VertexBuffer>> m_vertex_buffers;
- Ref<IndexBuffer> m_index_buffer;
- };
-};
diff --git a/src/platform/opengl/opengl_vertex_buffer.cpp b/src/platform/opengl/opengl_vertex_buffer.cpp
deleted file mode 100644
index 5cd4b82..0000000
--- a/src/platform/opengl/opengl_vertex_buffer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "opengl_vertex_buffer.h"
-#include "rendering/vertex_buffer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size)
- {
- glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
- glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
- }
-
- OpenGLVertexBuffer::~OpenGLVertexBuffer()
- {
- glDeleteBuffers(1, &m_renderer_id);
- }
-
- auto OpenGLVertexBuffer::bind() const -> void
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
- }
-
- auto OpenGLVertexBuffer::unbind() const -> void
- {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- auto OpenGLVertexBuffer::set_data(const void* data, uint32_t size) -> void
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
- glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
- }
-};
diff --git a/src/platform/opengl/opengl_vertex_buffer.h b/src/platform/opengl/opengl_vertex_buffer.h
deleted file mode 100644
index df2e0e6..0000000
--- a/src/platform/opengl/opengl_vertex_buffer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "rendering/vertex_buffer.h"
-
-namespace Donut
-{
- class OpenGLVertexBuffer
- : public VertexBuffer
- {
- public:
- OpenGLVertexBuffer(const void* data, uint32_t size);
- virtual ~OpenGLVertexBuffer();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
- virtual auto set_data(const void* data, uint32_t size) -> void override;
-
- virtual auto get_layout() const -> const VertexBufferLayout& override{ return m_layout; }
- virtual auto set_layout(const VertexBufferLayout& layout) -> void override{ m_layout = layout; }
-
- private:
- uint32_t m_renderer_id;
- VertexBufferLayout m_layout;
- };
-};
diff --git a/src/platform/vulkan/vulkan_index_buffer.cpp b/src/platform/vulkan/vulkan_index_buffer.cpp
deleted file mode 100644
index a6e5912..0000000
--- a/src/platform/vulkan/vulkan_index_buffer.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "vulkan_index_buffer.h"
-
-namespace Donut
-{
- VulkanIndexBuffer::VulkanIndexBuffer(uint32_t* indices, uint32_t count)
- : m_count(count)
- {
- // TODO(Hachem): Implement Vulkan index buffer creation
- }
-
- VulkanIndexBuffer::~VulkanIndexBuffer()
- {
- // TODO(Hachem): Implement Vulkan index buffer cleanup
- }
-
- auto VulkanIndexBuffer::bind() const -> void
- {
- // TODO(Hachem): Implement Vulkan index buffer binding
- }
-
- auto VulkanIndexBuffer::unbind() const -> void
- {
- // TODO(Hachem): Implement Vulkan index buffer unbinding
- }
-};
diff --git a/src/platform/vulkan/vulkan_index_buffer.h b/src/platform/vulkan/vulkan_index_buffer.h
deleted file mode 100644
index a0734a8..0000000
--- a/src/platform/vulkan/vulkan_index_buffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "rendering/index_buffer.h"
-
-namespace Donut
-{
- class VulkanIndexBuffer
- : public IndexBuffer
- {
- public:
- VulkanIndexBuffer(uint32_t* indices, uint32_t count);
- virtual ~VulkanIndexBuffer();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto get_count() const -> uint32_t override{ return m_count; }
- private:
- uint32_t m_renderer_id;
- uint32_t m_count;
- };
-};
diff --git a/src/platform/vulkan/vulkan_renderer.cpp b/src/platform/vulkan/vulkan_renderer.cpp
index 69d6501..cad0a44 100644
--- a/src/platform/vulkan/vulkan_renderer.cpp
+++ b/src/platform/vulkan/vulkan_renderer.cpp
@@ -121,6 +121,20 @@ namespace Donut
VkPipelineLayout present_pipeline_layout = VK_NULL_HANDLE;
VkPipeline present_pipeline = VK_NULL_HANDLE;
+ // World-builder scene view (grid now; sphere/skybox next). Normal-scale
+ // orbital camera; geometry drawn straight into the swapchain render pass.
+ bool scene_mode = false;
+ Camera scene_camera{ 45.0f, (float)GEO_W / (float)GEO_H, 0.1f, 1000.0f };
+ VkBuffer grid_vb = VK_NULL_HANDLE; VkDeviceMemory grid_vb_mem = VK_NULL_HANDLE;
+ uint32_t grid_vertex_count = 0;
+ VkBuffer grid_ubo = VK_NULL_HANDLE; VkDeviceMemory grid_ubo_mem = VK_NULL_HANDLE;
+ void* grid_ubo_mapped = nullptr;
+ VkDescriptorSetLayout grid_set_layout = VK_NULL_HANDLE;
+ VkDescriptorPool grid_pool = VK_NULL_HANDLE;
+ VkDescriptorSet grid_set = VK_NULL_HANDLE;
+ VkPipelineLayout grid_pipeline_layout = VK_NULL_HANDLE;
+ VkPipeline grid_pipeline = VK_NULL_HANDLE;
+
auto find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags flags) const -> uint32_t;
auto create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buf, VkDeviceMemory& mem) const -> bool;
static auto load_spirv(const std::string& path) -> std::vector<uint32_t>;
@@ -138,8 +152,10 @@ namespace Donut
auto create_hdri_cubemap(const char* path) -> bool;
auto rebuild_hdri_cubemap(const char* path) -> void;
auto create_present_resources() -> bool;
+ auto create_scene_resources() -> bool;
auto process_input() -> void;
auto update_geodesic_uniforms() -> void;
+ auto update_scene_uniforms() -> void;
auto destroy_geodesic_resources() -> void;
auto recreate_swapchain() -> bool;
auto cleanup_swapchain() -> void;
@@ -931,6 +947,120 @@ namespace Donut
return true;
}
+ auto VulkanRenderer::Impl::create_scene_resources() -> bool
+ {
+ const VkMemoryPropertyFlags host_vis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+
+ // Scene camera: normal-scale orbital viewer (matches the OpenGL world-builder).
+ scene_camera.set_camera_mode(CameraMode::Orbital);
+ scene_camera.set_orbital_target(glm::vec3(0.0f));
+ scene_camera.set_orbital_radius(15.0);
+ scene_camera.set_orbital_limits(2.0, 200.0);
+ scene_camera.set_orbital_speed(0.01f);
+ scene_camera.set_zoom_speed(2.0);
+ scene_camera.set_azimuth(0.0f);
+ scene_camera.set_elevation((float)std::numbers::pi / 3.0f);
+ scene_camera.update_orbital();
+
+ // Line grid on the XZ plane (+/-50 units, 1-unit cells). Grid.slang scales
+ // the position by u_GridSize/50, so u_GridSize = 50 keeps it 1:1.
+ std::vector<glm::vec3> lines;
+ const int N = 50;
+ for (int i = -N; i <= N; ++i)
+ {
+ lines.push_back({ (float)i, 0.0f, (float)-N });
+ lines.push_back({ (float)i, 0.0f, (float) N });
+ lines.push_back({ (float)-N, 0.0f, (float)i });
+ lines.push_back({ (float) N, 0.0f, (float)i });
+ }
+ grid_vertex_count = (uint32_t)lines.size();
+ VkDeviceSize vbsize = lines.size() * sizeof(glm::vec3);
+ if (!create_buffer(vbsize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, grid_vb, grid_vb_mem)) return false;
+ void* mp = nullptr; vkMapMemory(device, grid_vb_mem, 0, vbsize, 0, &mp); memcpy(mp, lines.data(), vbsize); vkUnmapMemory(device, grid_vb_mem);
+
+ // $Globals UBO (set 0, binding 0), std140, 176 bytes; refilled each frame.
+ if (!create_buffer(176, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, grid_ubo, grid_ubo_mem)) return false;
+ vkMapMemory(device, grid_ubo_mem, 0, 176, 0, &grid_ubo_mapped);
+
+ VkDescriptorSetLayoutBinding b{}; b.binding = 0; b.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; b.descriptorCount = 1; b.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+ VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dslci.bindingCount = 1; dslci.pBindings = &b;
+ VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &grid_set_layout));
+ VkDescriptorPoolSize psize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 };
+ VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 1; dpci.poolSizeCount = 1; dpci.pPoolSizes = &psize;
+ VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &grid_pool));
+ VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = grid_pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &grid_set_layout;
+ VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &grid_set));
+ VkDescriptorBufferInfo bi{ grid_ubo, 0, VK_WHOLE_SIZE };
+ VkWriteDescriptorSet w{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; w.dstSet = grid_set; w.dstBinding = 0; w.descriptorCount = 1; w.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; w.pBufferInfo = &bi;
+ vkUpdateDescriptorSets(device, 1, &w, 0, nullptr);
+
+ VkShaderModule vmod, fmod;
+ if (!create_shader_module("assets/shaders/generated/Grid.vertexMain.spv", vmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/Grid.fragmentMain.spv", fmod)) return false;
+ VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &grid_set_layout;
+ VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &grid_pipeline_layout));
+ VkPipelineShaderStageCreateInfo stages[2]{};
+ stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
+ stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
+ VkVertexInputBindingDescription vib{ 0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX };
+ VkVertexInputAttributeDescription via{ 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 };
+ VkPipelineVertexInputStateCreateInfo vin{ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
+ vin.vertexBindingDescriptionCount = 1; vin.pVertexBindingDescriptions = &vib;
+ vin.vertexAttributeDescriptionCount = 1; vin.pVertexAttributeDescriptions = &via;
+ VkPipelineInputAssemblyStateCreateInfo ia{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; ia.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+ VkPipelineViewportStateCreateInfo vps{ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; vps.viewportCount = 1; vps.scissorCount = 1;
+ VkDynamicState dyn[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dsci{ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; dsci.dynamicStateCount = 2; dsci.pDynamicStates = dyn;
+ VkPipelineRasterizationStateCreateInfo rs{ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; rs.polygonMode = VK_POLYGON_MODE_FILL; rs.cullMode = VK_CULL_MODE_NONE; rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rs.lineWidth = 1.0f;
+ VkPipelineMultisampleStateCreateInfo ms{ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+ VkPipelineColorBlendAttachmentState cba{};
+ cba.blendEnable = VK_TRUE;
+ cba.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; cba.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; cba.colorBlendOp = VK_BLEND_OP_ADD;
+ cba.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; cba.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; cba.alphaBlendOp = VK_BLEND_OP_ADD;
+ cba.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ VkPipelineColorBlendStateCreateInfo cb{ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; cb.attachmentCount = 1; cb.pAttachments = &cba;
+ VkGraphicsPipelineCreateInfo gpci{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
+ gpci.stageCount = 2; gpci.pStages = stages;
+ gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
+ gpci.pDynamicState = &dsci;
+ gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
+ gpci.layout = grid_pipeline_layout; gpci.renderPass = render_pass; gpci.subpass = 0;
+ VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &grid_pipeline);
+ vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
+ if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: grid pipeline creation failed ({})", (int)pr); return false; }
+ DONUT_INFO("Vulkan: scene resources ready ({} grid verts)", grid_vertex_count);
+ return true;
+ }
+
+ auto VulkanRenderer::Impl::update_scene_uniforms() -> void
+ {
+ struct GridUBO {
+ glm::mat4 view_projection; // 0 (SPIR-V RowMajor -> upload transposed)
+ glm::mat4 transform; // 64
+ float grid_size; float p0[3]; // 128
+ glm::vec3 grid_color; // 144
+ float grid_alpha; // 156
+ glm::vec3 camera_pos; // 160
+ float p1; // 172
+ } g{};
+ static_assert(sizeof(GridUBO) == 176, "GridUBO std140 layout mismatch");
+
+ uint32_t h = swapchain_extent.height ? swapchain_extent.height : 1;
+ float aspect = (float)swapchain_extent.width / (float)h;
+ scene_camera.set_projection(45.0f, aspect, 0.1f, 1000.0f);
+ glm::mat4 vp = scene_camera.get_projection_matrix() * scene_camera.get_view_matrix();
+
+ // Slang's mul(M,v) + the SPIR-V RowMajor decoration means glm's column-major
+ // matrices upload directly here (no transpose) to read as the intended M.
+ g.view_projection = vp;
+ g.transform = glm::mat4(1.0f);
+ g.grid_size = 50.0f;
+ g.grid_color = glm::vec3(0.55f, 0.55f, 0.6f);
+ g.grid_alpha = 0.75f;
+ g.camera_pos = scene_camera.get_orbital_position();
+ if (grid_ubo_mapped) memcpy(grid_ubo_mapped, &g, sizeof(g));
+ }
+
auto VulkanRenderer::Impl::update_geodesic_uniforms() -> void
{
struct CamUBO {
@@ -984,7 +1114,7 @@ namespace Donut
glfwGetCursorPos(window, &mx, &my);
bool left_down = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
- if (camera.get_camera_mode() == CameraMode::FPS)
+ if (!scene_mode && camera.get_camera_mode() == CameraMode::FPS)
{
// Left-drag looks around (screen-up looks up); WASD/QE move.
if (left_down && !left_was_down) { fps_last_x = mx; fps_last_y = my; }
@@ -1010,24 +1140,35 @@ namespace Donut
}
else
{
+ Camera& cam = scene_mode ? scene_camera : camera;
if (left_down && !left_was_down && !over_ui)
- camera.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);
+ cam.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);
else if (!left_down && left_was_down)
- camera.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
+ cam.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
left_was_down = left_down;
- camera.process_orbital_mouse_move(mx, my); // orbits only while dragging
+ cam.process_orbital_mouse_move(mx, my); // orbits only while dragging
double scroll = g_ScrollAccum; g_ScrollAccum = 0.0;
if (scroll != 0.0 && !over_ui)
- camera.process_orbital_scroll(0.0, scroll);
+ cam.process_orbital_scroll(0.0, scroll);
- user_moving = camera.is_dragging() || camera.is_panning();
+ user_moving = cam.is_dragging() || cam.is_panning();
}
}
auto VulkanRenderer::Impl::destroy_geodesic_resources() -> void
{
+ if (grid_pipeline) vkDestroyPipeline(device, grid_pipeline, nullptr);
+ if (grid_pipeline_layout) vkDestroyPipelineLayout(device, grid_pipeline_layout, nullptr);
+ if (grid_pool) vkDestroyDescriptorPool(device, grid_pool, nullptr);
+ if (grid_set_layout) vkDestroyDescriptorSetLayout(device, grid_set_layout, nullptr);
+ if (grid_ubo_mapped) { vkUnmapMemory(device, grid_ubo_mem); grid_ubo_mapped = nullptr; }
+ if (grid_ubo) vkDestroyBuffer(device, grid_ubo, nullptr);
+ if (grid_ubo_mem) vkFreeMemory(device, grid_ubo_mem, nullptr);
+ if (grid_vb) vkDestroyBuffer(device, grid_vb, nullptr);
+ if (grid_vb_mem) vkFreeMemory(device, grid_vb_mem, nullptr);
+
if (present_pipeline) vkDestroyPipeline(device, present_pipeline, nullptr);
if (present_pipeline_layout) vkDestroyPipelineLayout(device, present_pipeline_layout, nullptr);
if (present_pool) vkDestroyDescriptorPool(device, present_pool, nullptr);
@@ -1061,6 +1202,31 @@ namespace Donut
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
+ if (scene_mode)
+ {
+ // Scene view: grid lines drawn straight into the swapchain, then ImGui.
+ VkClearValue cv{}; cv.color = { { clear.r, clear.g, clear.b, clear.a } };
+ VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
+ rpbi.renderPass = render_pass; rpbi.framebuffer = framebuffers[image_index];
+ rpbi.renderArea = { { 0, 0 }, swapchain_extent };
+ rpbi.clearValueCount = 1; rpbi.pClearValues = &cv;
+ vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, grid_pipeline);
+ // Negative-height viewport flips Y so the scene reads like the GL path.
+ VkViewport gvp{ 0, (float)swapchain_extent.height, (float)swapchain_extent.width, -(float)swapchain_extent.height, 0, 1 };
+ VkRect2D gsc{ { 0, 0 }, swapchain_extent };
+ vkCmdSetViewport(cmd, 0, 1, &gvp);
+ vkCmdSetScissor(cmd, 0, 1, &gsc);
+ vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, grid_pipeline_layout, 0, 1, &grid_set, 0, nullptr);
+ VkDeviceSize goff = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &grid_vb, &goff);
+ vkCmdDraw(cmd, grid_vertex_count, 1, 0, 0);
+ if (draw_data)
+ ImGui_ImplVulkan_RenderDrawData(draw_data, cmd);
+ vkCmdEndRenderPass(cmd);
+ VK_CHECK(vkEndCommandBuffer(cmd));
+ return true;
+ }
+
// Geodesic offscreen pass
VkClearValue geo_clear{}; geo_clear.color = { { 0, 0, 0, 1 } };
VkRenderPassBeginInfo grp{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
@@ -1153,6 +1319,7 @@ namespace Donut
if (!v.create_sync_objects()) return false;
if (!v.create_geodesic_resources()) return false;
if (!v.create_present_resources()) return false;
+ if (!v.create_scene_resources()) return false;
DONUT_INFO("Vulkan renderer ready: {} swapchain images, {}x{}",
(int)v.images.size(), v.swapchain_extent.width, v.swapchain_extent.height);
@@ -1253,6 +1420,16 @@ namespace Donut
return m_impl->camera.get_camera_mode() == CameraMode::FPS;
}
+ auto VulkanRenderer::set_scene_mode(bool enabled) -> void
+ {
+ if (m_impl) m_impl->scene_mode = enabled;
+ }
+
+ auto VulkanRenderer::is_scene_mode() const -> bool
+ {
+ return m_impl && m_impl->scene_mode;
+ }
+
auto VulkanRenderer::draw_frame(const glm::vec4& clear_color, const std::function<void()>& build_ui) -> void
{
Impl& v = *m_impl;
@@ -1286,7 +1463,7 @@ namespace Donut
if (v.geo_in_use != VK_NULL_HANDLE)
vkWaitForFences(v.device, 1, &v.geo_in_use, VK_TRUE, UINT64_MAX);
v.process_input();
- v.update_geodesic_uniforms();
+ if (v.scene_mode) v.update_scene_uniforms(); else v.update_geodesic_uniforms();
vkResetCommandBuffer(v.command_buffers[v.current_frame], 0);
if (!v.record_command_buffer(v.command_buffers[v.current_frame], image_index, clear_color, draw_data)) return;
diff --git a/src/platform/vulkan/vulkan_renderer.h b/src/platform/vulkan/vulkan_renderer.h
index 0e643c1..e6920c5 100644
--- a/src/platform/vulkan/vulkan_renderer.h
+++ b/src/platform/vulkan/vulkan_renderer.h
@@ -47,6 +47,11 @@ namespace Donut
auto set_free_fly(bool enabled) -> void;
auto is_free_fly() const -> bool;
+ // Switch between the black-hole geodesic view and the world-builder scene
+ // view (grid/sphere/skybox drawn with real Vulkan geometry pipelines).
+ auto set_scene_mode(bool enabled) -> void;
+ auto is_scene_mode() const -> bool;
+
private:
struct Impl;
Impl* m_impl = nullptr;
diff --git a/src/platform/vulkan/vulkan_renderer_api.cpp b/src/platform/vulkan/vulkan_renderer_api.cpp
deleted file mode 100644
index 56499f4..0000000
--- a/src/platform/vulkan/vulkan_renderer_api.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include "vulkan_renderer_api.h"
-
-namespace Donut
-{
- auto VulkanRendererAPI::init() -> void
- {
- // TODO(Hachem): Implement Vulkan renderer API initialization
- }
-
- auto VulkanRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
- {
- // TODO(Hachem): Implement Vulkan viewport setting
- }
-
- auto VulkanRendererAPI::set_clear_color(const glm::vec4& color) -> void
- {
- // TODO(Hachem): Implement Vulkan clear color setting
- }
-
- auto VulkanRendererAPI::clear() -> void
- {
- // TODO(Hachem): Implement Vulkan clear
- }
-
- auto VulkanRendererAPI::enable_depth_test() -> void
- {
- // TODO(Hachem): Implement Vulkan depth test enabling
- }
-
- auto VulkanRendererAPI::disable_depth_test() -> void
- {
- // TODO(Hachem): Implement Vulkan depth test disabling
- }
-
- auto VulkanRendererAPI::set_face_culling(bool enabled) -> void
- {
- // TODO(Hachem): Implement Vulkan face culling setting
- }
-
- auto VulkanRendererAPI::enable_blending() -> void
- {
- // TODO(Hachem): Implement Vulkan blending enabling
- }
-
- auto VulkanRendererAPI::disable_blending() -> void
- {
- // TODO(Hachem): Implement Vulkan blending disabling
- }
-
- auto VulkanRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
- {
- // TODO(Hachem): Implement Vulkan indexed drawing
- }
-
- auto VulkanRendererAPI::draw_arrays(uint32_t vertex_count, uint32_t first) -> void
- {
- // TODO(Hachem): Implement Vulkan array drawing
- }
-
- auto VulkanRendererAPI::draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
- {
- // TODO(Hachem): Implement Vulkan line drawing
- }
-
- auto VulkanRendererAPI::bind_texture(uint32_t texture_id, uint32_t slot) -> void
- {
- // TODO(Hachem): Implement Vulkan texture binding
- }
-
- auto VulkanRendererAPI::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
- {
- // TODO(Hachem): Implement Vulkan image texture binding
- }
-
- auto VulkanRendererAPI::read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) -> void
- {
- // TODO(Hachem): Implement Vulkan pixel reading
- // For now, this is a placeholder implementation
- // In a real Vulkan implementation, this would involve:
- // 1. Creating a staging buffer
- // 2. Copying the framebuffer to the staging buffer
- // 3. Mapping the staging buffer and copying to the pixels array
- // 4. Unmapping and destroying the staging buffer
- }
-};
diff --git a/src/platform/vulkan/vulkan_renderer_api.h b/src/platform/vulkan/vulkan_renderer_api.h
deleted file mode 100644
index 15bf76d..0000000
--- a/src/platform/vulkan/vulkan_renderer_api.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "core/memory.h"
-#include "rendering/renderer.h"
-
-namespace Donut
-{
- class VulkanRendererAPI
- : public RendererAPI
- {
- public:
- virtual auto init() -> void override;
- virtual void set_viewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual auto set_clear_color(const glm::vec4& color) -> void override;
- virtual auto clear() -> void override;
- virtual auto enable_depth_test() -> void override;
- virtual auto disable_depth_test() -> void override;
- virtual auto set_face_culling(bool enabled) -> void override;
- virtual auto enable_blending() -> void override;
- virtual auto disable_blending() -> void override;
-
- virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) override;
-
- virtual void draw_arrays(uint32_t vertex_count,
- uint32_t first = 0) override;
- virtual void draw_lines(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) override;
- virtual void bind_texture(uint32_t texture_id,
- uint32_t slot = 0) override;
- virtual void bind_image_texture(uint32_t texture_id,
- uint32_t slot = 0,
- bool read_only = false) override;
- virtual void read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) override;
- };
-};
diff --git a/src/platform/vulkan/vulkan_shader.cpp b/src/platform/vulkan/vulkan_shader.cpp
deleted file mode 100644
index 7549761..0000000
--- a/src/platform/vulkan/vulkan_shader.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-#include "vulkan_shader.h"
-
-#include <fstream>
-#include <glm/gtc/type_ptr.hpp>
-
-namespace Donut
-{
- VulkanShader::VulkanShader(const std::string& filepath)
- {
- // TODO(Hachem): Implement Vulkan shader creation from filepath
- }
-
- VulkanShader::VulkanShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
- : m_name(name)
- {
- // TODO(Hachem): Implement Vulkan shader creation from source
- }
-
- VulkanShader::VulkanShader(const std::string& name, const std::string& compute_src)
- : m_name(name)
- {
- // TODO(Hachem): Implement Vulkan compute shader creation
- }
-
- VulkanShader::~VulkanShader()
- {
- // TODO(Hachem): Implement Vulkan shader cleanup
- }
-
- auto VulkanShader::bind() const -> void
- {
- // TODO(Hachem): Implement Vulkan shader binding
- }
-
- auto VulkanShader::unbind() const -> void
- {
- // TODO(Hachem): Implement Vulkan shader unbinding
- }
-
- auto VulkanShader::set_int(const std::string& name, int value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader int uniform setting
- }
-
- auto VulkanShader::set_int_array(const std::string& name, int* values, uint32_t count) -> void
- {
- // TODO(Hachem): Implement Vulkan shader int array uniform setting
- }
-
- auto VulkanShader::set_float(const std::string& name, float value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader float uniform setting
- }
-
- auto VulkanShader::set_float2(const std::string& name, const glm::vec2& value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader float2 uniform setting
- }
-
- auto VulkanShader::set_float3(const std::string& name, const glm::vec3& value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader float3 uniform setting
- }
-
- auto VulkanShader::set_float4(const std::string& name, const glm::vec4& value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader float4 uniform setting
- }
-
- auto VulkanShader::set_mat4(const std::string& name, const glm::mat4& value) -> void
- {
- // TODO(Hachem): Implement Vulkan shader mat4 uniform setting
- }
-
- auto VulkanShader::dispatch(uint32_t x, uint32_t y, uint32_t z) -> void
- {
- // TODO(Hachem): Implement Vulkan compute shader dispatch
- }
-
- auto VulkanShader::dispatch_indirect(uint32_t offset) -> void
- {
- // TODO(Hachem): Implement Vulkan indirect compute shader dispatch
- }
-
- auto VulkanShader::memory_barrier(uint32_t barriers) -> void
- {
- // TODO(Hachem): Implement Vulkan memory barrier
- }
-
- auto VulkanShader::upload_uniform_int(const std::string& name, int value) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform int upload
- }
-
- auto VulkanShader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform int array upload
- }
-
- auto VulkanShader::upload_uniform_float(const std::string& name, float value) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform float upload
- }
-
- auto VulkanShader::upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform float2 upload
- }
-
- auto VulkanShader::upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform float3 upload
- }
-
- auto VulkanShader::upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform float4 upload
- }
-
- auto VulkanShader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform mat3 upload
- }
-
- auto VulkanShader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void
- {
- // TODO(Hachem): Implement Vulkan uniform mat4 upload
- }
-
- auto VulkanShader::read_file(const std::string& filepath) -> std::string
- {
- // TODO(Hachem): Implement file reading for Vulkan shader
- return "";
- }
-
- auto VulkanShader::pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>
- {
- // TODO(Hachem): Implement shader preprocessing for Vulkan
- return {};
- }
-
- auto VulkanShader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void
- {
- // TODO(Hachem): Implement Vulkan shader compilation
- }
-};
diff --git a/src/platform/vulkan/vulkan_shader.h b/src/platform/vulkan/vulkan_shader.h
deleted file mode 100644
index 9453130..0000000
--- a/src/platform/vulkan/vulkan_shader.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "rendering/shader.h"
-
-#include <string>
-#include <unordered_map>
-
-namespace Donut
-{
- class VulkanShader
- : public Shader
- {
- public:
- VulkanShader(const std::string& filepath);
- VulkanShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
- VulkanShader(const std::string& name, const std::string& compute_src);
- virtual ~VulkanShader();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto set_int(const std::string& name, int value) -> void override;
- virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void override;
- virtual auto set_float(const std::string& name, float value) -> void override;
- virtual auto set_float2(const std::string& name, const glm::vec2& value) -> void override;
- virtual auto set_float3(const std::string& name, const glm::vec3& value) -> void override;
- virtual auto set_float4(const std::string& name, const glm::vec4& value) -> void override;
- virtual auto set_mat4(const std::string& name, const glm::mat4& value) -> void override;
-
- virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void override;
- virtual auto dispatch_indirect(uint32_t offset = 0) -> void override;
- virtual auto memory_barrier(uint32_t barriers) -> void override;
-
- virtual auto get_name() const -> const std::string& override{ return m_name; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- auto upload_uniform_int(const std::string& name, int value) -> void;
- auto upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void;
- auto upload_uniform_float(const std::string& name, float value) -> void;
- auto upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void;
- auto upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void;
- auto upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void;
- auto upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void;
- auto upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void;
- private:
- auto read_file(const std::string& filepath) -> std::string;
- auto pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>;
- auto compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void;
- uint32_t m_renderer_id;
- std::string m_name;
- };
-};
diff --git a/src/platform/vulkan/vulkan_texture.cpp b/src/platform/vulkan/vulkan_texture.cpp
deleted file mode 100644
index a970a53..0000000
--- a/src/platform/vulkan/vulkan_texture.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "vulkan_texture.h"
-
-namespace Donut
-{
- VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height)
- : m_width(width), m_height(height)
- {
- m_internal_format = 0;
- m_data_format = 0;
- m_renderer_id = 0;
- }
-
- VulkanTexture2D::VulkanTexture2D(const std::string& path)
- : m_path(path)
- {
- m_width = 1;
- m_height = 1;
- m_internal_format = 0;
- m_data_format = 0;
- m_renderer_id = 0;
- }
-
- VulkanTexture2D::~VulkanTexture2D()
- {
- }
-
- auto VulkanTexture2D::set_data(void* data, uint32_t size) -> void
- {
- }
-
- auto VulkanTexture2D::bind(uint32_t slot) const -> void
- {
- }
-
- auto VulkanTexture2D::bind_as_image(uint32_t slot, bool read_only) const -> void
- {
- }
-
- // Vulkan Cubemap Implementation (Placeholder)
- VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height)
- : m_width(width), m_height(height)
- {
- m_internal_format = 0;
- m_data_format = 0;
- m_renderer_id = 0;
- }
-
- VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path)
- : m_path(path)
- {
- m_width = 1024;
- m_height = 1024;
- m_internal_format = 0;
- m_data_format = 0;
- m_renderer_id = 0;
- }
-
- VulkanCubemapTexture::~VulkanCubemapTexture()
- {
- }
-
- auto VulkanCubemapTexture::set_data(void* data, uint32_t size) -> void
- {
- }
-
- auto VulkanCubemapTexture::bind(uint32_t slot) const -> void
- {
- }
-
- auto VulkanCubemapTexture::bind_as_image(uint32_t slot, bool read_only) const -> void
- {
- }
-};
diff --git a/src/platform/vulkan/vulkan_texture.h b/src/platform/vulkan/vulkan_texture.h
deleted file mode 100644
index ea8471b..0000000
--- a/src/platform/vulkan/vulkan_texture.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include "rendering/texture.h"
-
-namespace Donut
-{
- class VulkanTexture2D
- : public Texture2D
- {
- public:
- VulkanTexture2D(uint32_t width, uint32_t height);
- VulkanTexture2D(const std::string& path);
- virtual ~VulkanTexture2D();
-
- virtual auto get_width() const -> uint32_t override{ return m_width; }
- virtual auto get_height() const -> uint32_t override{ return m_height; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- virtual auto set_data(void* data, uint32_t size) -> void override;
- virtual auto bind(uint32_t slot = 0) const -> void override;
- virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_renderer_id == ((VulkanTexture2D&)other).m_renderer_id;
- }
- private:
- std::string m_path;
- uint32_t m_width, m_height;
- uint32_t m_renderer_id;
- uint32_t m_internal_format, m_data_format;
- };
-
- class VulkanCubemapTexture
- : public CubemapTexture
- {
- public:
- VulkanCubemapTexture(uint32_t width, uint32_t height);
- VulkanCubemapTexture(const std::string& path);
- virtual ~VulkanCubemapTexture();
-
- virtual auto get_width() const -> uint32_t override{ return m_width; }
- virtual auto get_height() const -> uint32_t override{ return m_height; }
- virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
-
- virtual auto set_data(void* data, uint32_t size) -> void override;
- virtual auto bind(uint32_t slot = 0) const -> void override;
- virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_renderer_id == ((VulkanCubemapTexture&)other).m_renderer_id;
- }
- private:
- std::string m_path;
- uint32_t m_width, m_height;
- uint32_t m_renderer_id;
- uint32_t m_internal_format, m_data_format;
- };
-};
diff --git a/src/platform/vulkan/vulkan_uniform_buffer.cpp b/src/platform/vulkan/vulkan_uniform_buffer.cpp
deleted file mode 100644
index 1a64099..0000000
--- a/src/platform/vulkan/vulkan_uniform_buffer.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "vulkan_uniform_buffer.h"
-
-namespace Donut
-{
- VulkanUniformBuffer::VulkanUniformBuffer(uint32_t size, uint32_t binding)
- : m_size(size), m_binding(binding)
- {
- // TODO: Implement Vulkan uniform buffer
- }
-
- VulkanUniformBuffer::~VulkanUniformBuffer()
- {
- // TODO: Implement Vulkan uniform buffer cleanup
- }
-
- auto VulkanUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset) -> void
- {
- // TODO: Implement Vulkan uniform buffer data setting
- }
-
- auto VulkanUniformBuffer::bind(uint32_t binding) -> void
- {
- // TODO: Implement Vulkan uniform buffer binding
- }
-};
diff --git a/src/platform/vulkan/vulkan_uniform_buffer.h b/src/platform/vulkan/vulkan_uniform_buffer.h
deleted file mode 100644
index 7d9ad0a..0000000
--- a/src/platform/vulkan/vulkan_uniform_buffer.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "rendering/uniform_buffer.h"
-
-namespace Donut
-{
- class VulkanUniformBuffer : public UniformBuffer
- {
- public:
- VulkanUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~VulkanUniformBuffer();
-
- virtual auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void override;
- virtual auto bind(uint32_t binding) -> void override;
- private:
- uint32_t m_size;
- uint32_t m_binding;
- };
-};
diff --git a/src/platform/vulkan/vulkan_vertex_array.cpp b/src/platform/vulkan/vulkan_vertex_array.cpp
deleted file mode 100644
index 73219b1..0000000
--- a/src/platform/vulkan/vulkan_vertex_array.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "vulkan_vertex_array.h"
-
-namespace Donut
-{
- VulkanVertexArray::VulkanVertexArray()
- {
- // TODO(Hachem): Implement Vulkan vertex array creation
- }
-
- VulkanVertexArray::~VulkanVertexArray()
- {
- // TODO(Hachem): Implement Vulkan vertex array cleanup
- }
-
- auto VulkanVertexArray::bind() const -> void
- {
- // TODO(Hachem): Implement Vulkan vertex array binding
- }
-
- auto VulkanVertexArray::unbind() const -> void
- {
- // TODO(Hachem): Implement Vulkan vertex array unbinding
- }
-
- auto VulkanVertexArray::add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void
- {
- // TODO(Hachem): Implement Vulkan vertex buffer addition
- m_vertex_buffers.push_back(vertex_buffer);
- }
-
- auto VulkanVertexArray::set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void
- {
- // TODO(Hachem): Implement Vulkan index buffer setting
- m_index_buffer = index_buffer;
- }
-};
diff --git a/src/platform/vulkan/vulkan_vertex_array.h b/src/platform/vulkan/vulkan_vertex_array.h
deleted file mode 100644
index 0d282a2..0000000
--- a/src/platform/vulkan/vulkan_vertex_array.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "rendering/vertex_array.h"
-#include "core/memory.h"
-
-namespace Donut
-{
- class VulkanVertexArray
- : public VertexArray
- {
- public:
- VulkanVertexArray();
- virtual ~VulkanVertexArray();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void override;
- virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void override;
-
- virtual auto get_vertex_buffers() const -> const std::vector<Ref<VertexBuffer>>& { return m_vertex_buffers; }
- virtual auto get_index_buffer() const -> const Ref<IndexBuffer>& { return m_index_buffer; }
- private:
- uint32_t m_renderer_id;
- std::vector<Ref<VertexBuffer>> m_vertex_buffers;
- Ref<IndexBuffer> m_index_buffer;
- };
-};
diff --git a/src/platform/vulkan/vulkan_vertex_buffer.cpp b/src/platform/vulkan/vulkan_vertex_buffer.cpp
deleted file mode 100644
index 6a222c1..0000000
--- a/src/platform/vulkan/vulkan_vertex_buffer.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "vulkan_vertex_buffer.h"
-
-namespace Donut
-{
- VulkanVertexBuffer::VulkanVertexBuffer(uint32_t size)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer creation with size
- }
-
- VulkanVertexBuffer::VulkanVertexBuffer(float* vertices, uint32_t size)
- {
- // TODO(Hachem): Implement Vulkan vertex buffer creation with data
- }
-
- VulkanVertexBuffer::~VulkanVertexBuffer()
- {
- // TODO(Hachem): Implement Vulkan vertex buffer cleanup
- }
-
- auto VulkanVertexBuffer::bind() const -> void
- {
- // TODO(Hachem): Implement Vulkan vertex buffer binding
- }
-
- auto VulkanVertexBuffer::unbind() const -> void
- {
- // TODO(Hachem): Implement Vulkan vertex buffer unbinding
- }
-
- auto VulkanVertexBuffer::set_data(const void* data, uint32_t size) -> void
- {
- // TODO(Hachem): Implement Vulkan vertex buffer data setting
- }
-}; \ No newline at end of file
diff --git a/src/platform/vulkan/vulkan_vertex_buffer.h b/src/platform/vulkan/vulkan_vertex_buffer.h
deleted file mode 100644
index 3218021..0000000
--- a/src/platform/vulkan/vulkan_vertex_buffer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include "rendering/vertex_buffer.h"
-
-namespace Donut
-{
- class VulkanVertexBuffer
- : public VertexBuffer
- {
- public:
- VulkanVertexBuffer(uint32_t size);
- VulkanVertexBuffer(float* vertices, uint32_t size);
- virtual ~VulkanVertexBuffer();
-
- virtual auto bind() const -> void override;
- virtual auto unbind() const -> void override;
-
- virtual auto set_data(const void* data, uint32_t size) -> void override;
-
- virtual auto get_layout() const -> const VertexBufferLayout& override{ return m_layout; }
- virtual auto set_layout(const VertexBufferLayout& layout) -> void override{ m_layout = layout; }
- private:
- uint32_t m_renderer_id;
- VertexBufferLayout m_layout;
- };
-};
diff --git a/src/rendering/framebuffer.cpp b/src/rendering/framebuffer.cpp
index 089c31e..3da4b48 100644
--- a/src/rendering/framebuffer.cpp
+++ b/src/rendering/framebuffer.cpp
@@ -1,17 +1,251 @@
#include "framebuffer.h"
-#include "renderer.h"
+#include "core/log.h"
-#include "platform/opengl/opengl_framebuffer.h"
+#include <glad/glad.h>
namespace Donut
{
- auto Framebuffer::create(const FramebufferSpecification& spec) -> Ref<Framebuffer>
+ namespace Utils
+ {
+ static GLenum TextureTarget(bool multisampled)
+ {
+ return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
+ }
+
+ static void bind_texture(bool multisampled, uint32_t id)
+ {
+ glBindTexture(TextureTarget(multisampled), id);
+ }
+
+ static void CreateTextures(bool multisampled, uint32_t* out_id, uint32_t count)
+ {
+ // glCreateTextures is 4.5 DSA; macOS caps at 4.1. Callers bind each
+ // texture (with the correct target) before use.
+ glGenTextures(count, out_id);
+ }
+
+ static void AttachColorTexture(uint32_t id, int samples, GLenum internal_format, GLenum format, uint32_t width, uint32_t height, int index)
+ {
+ bool multisampled = samples > 1;
+ if (multisampled)
+ {
+ glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internal_format, width, height, GL_FALSE);
+ }
+ else
+ {
+ // Integer color formats require an integer pixel type even when
+ // data is null, or macOS's strict core profile rejects the call.
+ GLenum type = (format == GL_RED_INTEGER) ? GL_INT : GL_UNSIGNED_BYTE;
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0);
+ }
+
+ static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachment_type, uint32_t width, uint32_t height)
+ {
+ bool multisampled = samples > 1;
+ if (multisampled)
+ {
+ glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, format, width, height, GL_FALSE);
+ }
+ else
+ {
+ // glTexStorage2D is 4.2; use mutable storage for macOS (4.1).
+ GLenum depth_format = (format == GL_DEPTH24_STENCIL8) ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
+ GLenum depth_type = (format == GL_DEPTH24_STENCIL8) ? GL_UNSIGNED_INT_24_8 : GL_FLOAT;
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, depth_format, depth_type, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, attachment_type, TextureTarget(multisampled), id, 0);
+ }
+
+ static bool IsDepthFormat(FramebufferTextureFormat format)
+ {
+ switch (format)
+ {
+ case FramebufferTextureFormat::DEPTH24STENCIL8: return true;
+ }
+ return false;
+ }
+
+ static GLenum DonutFBTextureFormatToGL(FramebufferTextureFormat format)
+ {
+ switch (format)
+ {
+ case FramebufferTextureFormat::RGBA8: return GL_RGBA8;
+ case FramebufferTextureFormat::RED_INTEGER: return GL_RED_INTEGER;
+ }
+
+ return 0;
+ }
+ }
+
+ Framebuffer::Framebuffer(const FramebufferSpecification& spec)
+ : m_specification(spec)
+ {
+ for (auto spec : m_specification.attachments.attachments)
+ {
+ if (!Utils::IsDepthFormat(spec.texture_format))
+ m_color_attachment_specifications.emplace_back(spec);
+ else
+ m_depth_attachment_specification = spec;
+ }
+
+ invalidate();
+ }
+
+ Framebuffer::~Framebuffer()
+ {
+ glDeleteFramebuffers(1, &m_renderer_id);
+ glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
+ glDeleteTextures(1, &m_depth_attachment);
+ }
+
+ auto Framebuffer::invalidate() -> void
{
- switch (Renderer::get_api())
+ if (m_renderer_id)
+ {
+ glDeleteFramebuffers(1, &m_renderer_id);
+ glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
+ glDeleteTextures(1, &m_depth_attachment);
+
+ m_color_attachments.clear();
+ m_depth_attachment = 0;
+ }
+
+ glGenFramebuffers(1, &m_renderer_id); // glCreateFramebuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
+
+ bool multisample = m_specification.Samples > 1;
+
+ if (m_color_attachment_specifications.size())
{
- case RendererAPI::API::OpenGL: return create_ref<OpenGLFramebuffer>(spec);
+ m_color_attachments.resize(m_color_attachment_specifications.size());
+ Utils::CreateTextures(multisample, m_color_attachments.data(), static_cast<uint32_t>(m_color_attachments.size()));
+
+ for (size_t i = 0; i < m_color_attachments.size(); i++)
+ {
+ Utils::bind_texture(multisample, m_color_attachments[i]);
+ switch (m_color_attachment_specifications[i].texture_format)
+ {
+ case FramebufferTextureFormat::RGBA8:
+ Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_RGBA8, GL_RGBA, m_specification.Width, m_specification.Height, static_cast<int>(i));
+ break;
+ case FramebufferTextureFormat::RED_INTEGER:
+ Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_R32I, GL_RED_INTEGER, m_specification.Width, m_specification.Height, static_cast<int>(i));
+ break;
+ }
+ }
}
- return nullptr;
+ if (m_depth_attachment_specification.texture_format != FramebufferTextureFormat::None)
+ {
+ Utils::CreateTextures(multisample, &m_depth_attachment, 1);
+ Utils::bind_texture(multisample, m_depth_attachment);
+ switch (m_depth_attachment_specification.texture_format)
+ {
+ case FramebufferTextureFormat::DEPTH24STENCIL8:
+ Utils::AttachDepthTexture(m_depth_attachment, m_specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_specification.Width, m_specification.Height);
+ break;
+ }
+ }
+
+ if (m_color_attachments.size() > 1)
+ {
+ GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
+ glDrawBuffers(static_cast<GLsizei>(m_color_attachments.size()), buffers);
+ }
+ else if (m_color_attachments.empty())
+ glDrawBuffer(GL_NONE);
+
+ if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+ {
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ switch (status)
+ {
+ case GL_FRAMEBUFFER_UNDEFINED:
+ DONUT_ERROR("Framebuffer is undefined");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ DONUT_ERROR("Framebuffer has incomplete attachment");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ DONUT_ERROR("Framebuffer is missing attachment");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+ DONUT_ERROR("Framebuffer has incomplete draw buffer");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+ DONUT_ERROR("Framebuffer has incomplete read buffer");
+ break;
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ DONUT_ERROR("Framebuffer format is unsupported");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+ DONUT_ERROR("Framebuffer has incomplete multisample");
+ break;
+ case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
+ DONUT_ERROR("Framebuffer has incomplete layer targets");
+ break;
+ default:
+ DONUT_ERROR("Framebuffer is incomplete (unknown error: {})", status);
+ break;
+ }
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+
+ auto Framebuffer::bind() -> void
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
+ glViewport(0, 0, m_specification.Width, m_specification.Height);
+ }
+
+ auto Framebuffer::unbind() -> void
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ }
+
+ auto Framebuffer::resize(uint32_t width, uint32_t height) -> void
+ {
+ m_specification.Width = width;
+ m_specification.Height = height;
+
+ invalidate();
+ }
+
+ auto Framebuffer::read_pixel(uint32_t attachment_index, int x, int y) -> int
+ {
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + attachment_index);
+ int pixel_data;
+ glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixel_data);
+ return pixel_data;
+ }
+
+ auto Framebuffer::clear_attachment(uint32_t attachment_index, int value) -> void
+ {
+ // glClearTexImage is 4.4 and unavailable on macOS. clear the integer
+ // attachment by binding this framebuffer and clearing its draw buffer.
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
+ glClearBufferiv(GL_COLOR, static_cast<GLint>(attachment_index), &value);
+ }
+
+ auto Framebuffer::create(const FramebufferSpecification& spec) -> Ref<Framebuffer>
+ {
+ return create_ref<Framebuffer>(spec);
}
};
diff --git a/src/rendering/framebuffer.h b/src/rendering/framebuffer.h
index a01a81b..cf1fbef 100644
--- a/src/rendering/framebuffer.h
+++ b/src/rendering/framebuffer.h
@@ -3,17 +3,19 @@
#include "core/memory.h"
#include "texture.h"
+#include <vector>
+
namespace Donut
{
enum class FramebufferTextureFormat
{
None = 0,
-
+
RGBA8,
RED_INTEGER,
-
+
DEPTH24STENCIL8,
-
+
Depth = DEPTH24STENCIL8
};
@@ -47,19 +49,32 @@ namespace Donut
class Framebuffer
{
public:
- virtual ~Framebuffer() = default;
+ Framebuffer(const FramebufferSpecification& spec);
+ ~Framebuffer();
- virtual auto bind() -> void = 0;
- virtual auto unbind() -> void = 0;
+ auto bind() -> void;
+ auto unbind() -> void;
- virtual auto resize(uint32_t width, uint32_t height) -> void = 0;
- virtual auto read_pixel(uint32_t attachment_index, int x, int y) -> int = 0;
+ auto resize(uint32_t width, uint32_t height) -> void;
+ auto read_pixel(uint32_t attachment_index, int x, int y) -> int;
- virtual auto clear_attachment(uint32_t attachment_index, int value) -> void = 0;
- virtual auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t = 0;
+ auto clear_attachment(uint32_t attachment_index, int value) -> void;
+ auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t { return m_color_attachments[index]; }
- virtual auto get_specification() const -> const FramebufferSpecification& = 0;
+ auto get_specification() const -> const FramebufferSpecification& { return m_specification; }
static auto create(const FramebufferSpecification& spec) -> Ref<Framebuffer>;
+
+ private:
+ auto invalidate() -> void;
+
+ uint32_t m_renderer_id = 0;
+ FramebufferSpecification m_specification;
+
+ std::vector<FramebufferTextureSpecification> m_color_attachment_specifications;
+ FramebufferTextureSpecification m_depth_attachment_specification = FramebufferTextureFormat::None;
+
+ std::vector<uint32_t> m_color_attachments;
+ uint32_t m_depth_attachment = 0;
};
};
diff --git a/src/rendering/index_buffer.cpp b/src/rendering/index_buffer.cpp
index 9603c72..2c933e0 100644
--- a/src/rendering/index_buffer.cpp
+++ b/src/rendering/index_buffer.cpp
@@ -1,21 +1,34 @@
#include "index_buffer.h"
-#include "renderer.h"
-#include "platform/opengl/opengl_index_buffer.h"
-#include "platform/vulkan/vulkan_index_buffer.h"
+#include <glad/glad.h>
-namespace Donut
+namespace Donut
{
+ IndexBuffer::IndexBuffer(const uint32_t* indices, uint32_t count)
+ : m_count(count)
+ {
+ glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
+ }
+
+ IndexBuffer::~IndexBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ auto IndexBuffer::bind() const -> void
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
+ }
+
+ auto IndexBuffer::unbind() const -> void
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
auto IndexBuffer::create(const uint32_t* indices, uint32_t count) -> IndexBuffer*
{
- switch (Renderer::get_api())
- {
- case RendererAPI::API::OpenGL:
- return new OpenGLIndexBuffer(indices, count);
- case RendererAPI::API::Vulkan:
- return new VulkanIndexBuffer((uint32_t*)indices, count);
- default:
- return nullptr;
- }
+ return new IndexBuffer(indices, count);
}
};
diff --git a/src/rendering/index_buffer.h b/src/rendering/index_buffer.h
index 8389f2d..0b1b2b6 100644
--- a/src/rendering/index_buffer.h
+++ b/src/rendering/index_buffer.h
@@ -7,12 +7,17 @@ namespace Donut
class IndexBuffer
{
public:
- virtual ~IndexBuffer() = default;
+ IndexBuffer(const uint32_t* indices, uint32_t count);
+ ~IndexBuffer();
- virtual auto bind() const -> void = 0;
- virtual auto unbind() const -> void = 0;
- virtual auto get_count() const -> uint32_t = 0;
+ auto bind() const -> void;
+ auto unbind() const -> void;
+ auto get_count() const -> uint32_t { return m_count; }
static auto create(const uint32_t* indices, uint32_t count) -> IndexBuffer*;
+
+ private:
+ uint32_t m_renderer_id = 0;
+ uint32_t m_count = 0;
};
};
diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp
index 28b7962..1c82447 100644
--- a/src/rendering/renderer.cpp
+++ b/src/rendering/renderer.cpp
@@ -1,26 +1,130 @@
#include "renderer.h"
-#include "platform/opengl/opengl_renderer_api.h"
-#include "platform/vulkan/vulkan_renderer_api.h"
+#include "core/log.h"
+#include <glad/glad.h>
+#include <GLFW/glfw3.h>
#include <glm/gtc/matrix_transform.hpp>
-namespace Donut
+namespace Donut
{
+ RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
+
auto RendererAPI::create() -> Scope<RendererAPI>
- {
- switch (s_api)
+ {
+ return create_scope<RendererAPI>();
+ }
+
+ auto RendererAPI::init() -> void
+ {
+ if (!glfwGetCurrentContext())
+ {
+ DONUT_ERROR("No OpenGL context is current! Cannot initialize GLAD.");
+ return;
+ }
+
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
- case API::OpenGL:
- return create_scope<OpenGLRendererAPI>();
- case API::Vulkan:
- return create_scope<VulkanRendererAPI>();
- default:
- return nullptr;
+ DONUT_ERROR("Failed to initialize GLAD!");
+ return;
}
+
+ 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);
}
- RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
+ auto RendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
+ {
+ glViewport(x, y, width, height);
+ }
+
+ auto RendererAPI::set_clear_color(const glm::vec4& color) -> void
+ {
+ glClearColor(color.r, color.g, color.b, color.a);
+ }
+
+ auto RendererAPI::clear() -> void
+ {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ }
+
+ auto RendererAPI::enable_depth_test() -> void
+ {
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ auto RendererAPI::disable_depth_test() -> void
+ {
+ glDisable(GL_DEPTH_TEST);
+ }
+
+ auto RendererAPI::set_face_culling(bool enabled) -> void
+ {
+ if (enabled)
+ {
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ glFrontFace(GL_CCW);
+ }
+ else
+ glDisable(GL_CULL_FACE);
+ }
+
+ auto RendererAPI::enable_blending() -> void
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ auto RendererAPI::disable_blending() -> void
+ {
+ glDisable(GL_BLEND);
+ }
+
+ auto RendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
+ {
+ uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ auto RendererAPI::draw_arrays(uint32_t vertex_count, uint32_t first) -> void
+ {
+ glDrawArrays(GL_TRIANGLES, first, vertex_count);
+ }
+
+ auto RendererAPI::draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
+ {
+ uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
+ glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, nullptr);
+ }
+
+ auto RendererAPI::bind_texture(uint32_t texture_id, uint32_t slot) -> void
+ {
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ }
+
+ auto RendererAPI::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
+ {
+ // Image load/store is OpenGL 4.2; the pointer is null on macOS (4.1).
+ if (glBindImageTexture == nullptr)
+ return;
+ glBindImageTexture(slot, texture_id, 0, GL_FALSE, 0,
+ read_only ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
+ }
+
+ auto RendererAPI::read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void
+ {
+ glReadPixels(x, y, width, height, format, type, pixels);
+ }
auto Renderer::init() -> void
{
diff --git a/src/rendering/renderer.h b/src/rendering/renderer.h
index 189723d..d7855a4 100644
--- a/src/rendering/renderer.h
+++ b/src/rendering/renderer.h
@@ -9,6 +9,8 @@
namespace Donut
{
+ // Single concrete backend: the OpenGL renderer. (Vulkan runs through its own
+ // dedicated VulkanRenderer, not this immediate-mode command layer.)
class RendererAPI
{
public:
@@ -20,31 +22,26 @@ namespace Donut
};
public:
- virtual ~RendererAPI() = default;
+ RendererAPI() = default;
+ ~RendererAPI() = default;
- virtual auto init() -> void = 0;
- virtual auto set_viewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) -> void = 0;
- virtual auto set_clear_color(const glm::vec4& color) -> void = 0;
- virtual auto clear() -> void = 0;
- virtual auto enable_depth_test() -> void = 0;
- virtual auto disable_depth_test() -> void = 0;
- virtual auto set_face_culling(bool enabled) -> void = 0;
- virtual auto enable_blending() -> void = 0;
- virtual auto disable_blending() -> void = 0;
+ auto init() -> void;
+ auto set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void;
+ auto set_clear_color(const glm::vec4& color) -> void;
+ auto clear() -> void;
+ auto enable_depth_test() -> void;
+ auto disable_depth_test() -> void;
+ auto set_face_culling(bool enabled) -> void;
+ auto enable_blending() -> void;
+ auto disable_blending() -> void;
- virtual auto draw_indexed(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) -> void = 0;
-
- virtual auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void = 0;
- virtual auto draw_lines(const Ref<VertexArray>& vertex_array,
- uint32_t index_count = 0) -> void = 0;
- virtual auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void = 0;
- virtual auto bind_image_texture(uint32_t texture_id,
- uint32_t slot = 0,
- bool read_only = false) -> void = 0;
- virtual auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) -> void = 0;
+ auto draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void;
+ auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void;
+ auto draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void;
+ auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void;
+ auto bind_image_texture(uint32_t texture_id, uint32_t slot = 0, bool read_only = false) -> void;
+ auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void;
inline static auto get_api() -> API { return s_api; }
inline static auto set_api(API api) -> void { s_api = api; }
diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp
index 167b855..80015e6 100644
--- a/src/rendering/shader.cpp
+++ b/src/rendering/shader.cpp
@@ -1,48 +1,318 @@
#include "shader.h"
-#include "renderer.h"
-#include "platform/opengl/opengl_shader.h"
-#include "platform/vulkan/vulkan_shader.h"
+#include <glad/glad.h>
+#include <glm/gtc/type_ptr.hpp>
+
+#include <fstream>
+#include <iostream>
namespace Donut
{
- auto Shader::create(const std::string& filepath) -> Shader*
+ static uint32_t ShaderTypeFromString(const std::string& type)
+ {
+ if (type == "vertex")
+ return GL_VERTEX_SHADER;
+ if (type == "fragment" || type == "pixel")
+ return GL_FRAGMENT_SHADER;
+ if (type == "compute")
+ return GL_COMPUTE_SHADER;
+ return 0;
+ }
+
+ // Shaders are authored in Slang and compiled to assets/shaders/generated/
+ // <name>.glsl by tools/compile-shaders.sh. Given a legacy ".../<name>.glsl"
+ // path, prefer that generated file when present; otherwise fall back to the
+ // hand-written GLSL (e.g. shaders not yet ported to Slang).
+ static std::string ResolveShaderPath(const std::string& filepath)
+ {
+ size_t slash = filepath.find_last_of("/\\");
+ std::string dir = (slash == std::string::npos) ? std::string() : filepath.substr(0, slash + 1);
+ std::string file = (slash == std::string::npos) ? filepath : filepath.substr(slash + 1);
+ size_t dot = file.rfind('.');
+ std::string base = (dot == std::string::npos) ? file : file.substr(0, dot);
+
+ std::string generated = dir + "generated/" + base + ".glsl";
+ std::ifstream test(generated);
+ if (test.good())
+ return generated;
+ return filepath;
+ }
+
+ Shader::Shader(const std::string& filepath)
+ {
+ std::string resolved = ResolveShaderPath(filepath);
+ m_is_slang = (resolved != filepath);
+ std::string source = read_file(resolved);
+ auto shader_sources = pre_process(source);
+ compile(shader_sources);
+
+ auto last_slash = filepath.find_last_of("/\\");
+ last_slash = last_slash == std::string::npos ? 0 : last_slash + 1;
+ auto last_dot = filepath.rfind('.');
+ auto count = last_dot == std::string::npos ? filepath.size() - last_slash : last_dot - last_slash;
+ m_name = filepath.substr(last_slash, count);
+ }
+
+ Shader::Shader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
+ : m_name(name)
+ {
+ std::unordered_map<uint32_t, std::string> sources;
+ sources[GL_VERTEX_SHADER] = vertex_src;
+ sources[GL_FRAGMENT_SHADER] = fragment_src;
+ compile(sources);
+ }
+
+ Shader::Shader(const std::string& name, const std::string& compute_src)
+ : m_name(name)
+ {
+ std::unordered_map<uint32_t, std::string> sources;
+ sources[GL_COMPUTE_SHADER] = compute_src;
+ compile(sources);
+ }
+
+ Shader::~Shader()
+ {
+ glDeleteProgram(m_renderer_id);
+ }
+
+ auto Shader::read_file(const std::string& filepath) -> std::string
{
- switch (Renderer::get_api())
+ std::string result;
+ std::ifstream in(filepath, std::ios::in |
+ std::ios::binary);
+
+ if (in)
{
- case RendererAPI::API::OpenGL:
- return new OpenGLShader(filepath);
- case RendererAPI::API::Vulkan:
- return new VulkanShader(filepath);
- default:
- return nullptr;
+ 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;
}
- auto Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*
+ auto Shader::pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>
{
- switch (Renderer::get_api())
+ std::unordered_map<uint32_t, std::string> shader_sources;
+
+ const char* type_token = "#type";
+ size_t type_token_length = strlen(type_token);
+ size_t pos = source.find(type_token, 0);
+
+ while (pos != std::string::npos)
{
- case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, vertex_src, fragment_src);
- case RendererAPI::API::Vulkan:
- return new VulkanShader(name, vertex_src, fragment_src);
- default:
- return nullptr;
+ size_t eol = source.find_first_of("\r\n", pos);
+ size_t begin = pos + type_token_length + 1;
+ std::string type = source.substr(begin, eol - begin);
+
+ size_t next_line_pos = source.find_first_not_of("\r\n", eol);
+ pos = source.find(type_token, next_line_pos);
+ shader_sources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(next_line_pos) :
+ source.substr(next_line_pos, pos - next_line_pos);
}
+
+ return shader_sources;
}
- auto Shader::create_compute(const std::string& name, const std::string& compute_src) -> Shader*
+ auto Shader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void
{
- switch (Renderer::get_api())
+ uint32_t program = glCreateProgram();
+ std::vector<uint32_t> glShaderIDs(shader_sources.size());
+ for (auto& kv : shader_sources)
+ {
+ uint32_t type = kv.first;
+ const std::string& source = kv.second;
+
+ uint32_t shader = glCreateShader(type);
+ const char* source_c_str = source.c_str();
+ glShaderSource(shader, 1, &source_c_str, 0);
+ glCompileShader(shader);
+
+ int is_compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled);
+ if (is_compiled == GL_FALSE)
+ {
+ int max_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length);
+ std::vector<char> info_log(max_length);
+ glGetShaderInfoLog(shader, max_length, &max_length, &info_log[0]);
+ glDeleteShader(shader);
+ for (auto id : glShaderIDs)
+ glDeleteShader(id);
+ glDeleteProgram(program);
+ m_renderer_id = 0;
+ // info_log.data() is null when the driver returns an empty log
+ // (e.g. macOS rejecting a compute shader); streaming a null
+ // char* into std::cout calls strlen(NULL) and crashes.
+ const char* log = info_log.empty() ? "" : info_log.data();
+ std::cout << "Shader compilation failure!" << std::endl << log << std::endl;
+ return;
+ }
+ glAttachShader(program, shader);
+ glShaderIDs.push_back(shader);
+ }
+
+ m_renderer_id = program;
+ glLinkProgram(m_renderer_id);
+
+ int is_linked = 0;
+ glGetProgramiv(m_renderer_id, GL_LINK_STATUS, (int*)&is_linked);
+ if (is_linked == GL_FALSE)
{
- case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, compute_src);
- case RendererAPI::API::Vulkan:
- return new VulkanShader(name, compute_src);
- default:
- return nullptr;
+ int max_length = 0;
+ glGetProgramiv(m_renderer_id, GL_INFO_LOG_LENGTH, &max_length);
+ std::vector<char> info_log(max_length);
+ glGetProgramInfoLog(m_renderer_id, max_length, &max_length, &info_log[0]);
+ glDeleteProgram(m_renderer_id);
+ for (auto id : glShaderIDs)
+ glDeleteShader(id);
+ m_renderer_id = 0;
+ const char* log = info_log.empty() ? "" : info_log.data();
+ std::cout << "Shader link failure!" << std::endl << log << std::endl;
+ return;
}
+
+ for (auto id : glShaderIDs)
+ {
+ glDetachShader(m_renderer_id, id);
+ glDeleteShader(id);
+ }
+ }
+
+ auto Shader::bind() const -> void
+ {
+ glUseProgram(m_renderer_id);
+ }
+
+ auto Shader::unbind() const -> void
+ {
+ glUseProgram(0);
+ }
+
+ auto Shader::set_int(const std::string& name, int value) -> void
+ {
+ upload_uniform_int(name, value);
+ }
+
+ auto Shader::set_int_array(const std::string& name, int* values, uint32_t count) -> void
+ {
+ upload_uniform_int_array(name, values, count);
+ }
+
+ auto Shader::set_float(const std::string& name, float value) -> void
+ {
+ upload_uniform_float(name, value);
+ }
+
+ auto Shader::set_float2(const std::string& name, const glm::vec2& value) -> void
+ {
+ upload_uniform_float2(name, value);
+ }
+
+ auto Shader::set_float3(const std::string& name, const glm::vec3& value) -> void
+ {
+ upload_uniform_float3(name, value);
+ }
+
+ auto Shader::set_float4(const std::string& name, const glm::vec4& value) -> void
+ {
+ upload_uniform_float4(name, value);
+ }
+
+ auto Shader::set_mat4(const std::string& name, const glm::mat4& value) -> void
+ {
+ upload_uniform_mat4(name, value);
+ }
+
+ auto Shader::upload_uniform_int(const std::string& name, int value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1i(location, value);
+ }
+
+ auto Shader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1iv(location, count, values);
+ }
+
+ auto Shader::upload_uniform_float(const std::string& name, float value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1f(location, value);
+ }
+
+ auto Shader::upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform2f(location, value.x, value.y);
+ }
+
+ auto Shader::upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform3f(location, value.x, value.y, value.z);
+ }
+
+ auto Shader::upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform4f(location, value.x, value.y, value.z, value.w);
+ }
+
+ auto Shader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniformMatrix3fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
+ }
+
+ auto Shader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniformMatrix4fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
+ }
+
+ auto Shader::dispatch(uint32_t x, uint32_t y, uint32_t z) -> void
+ {
+ // Compute shaders require OpenGL 4.3+. On drivers that cap out earlier
+ // (e.g. macOS, which is frozen at 4.1) glDispatchCompute is never
+ // loaded and the pointer is null. Guard so we no-op instead of crash.
+ if (m_renderer_id == 0 || glDispatchCompute == nullptr)
+ return;
+ glDispatchCompute(x, y, z);
+ }
+
+ auto Shader::dispatch_indirect(uint32_t offset) -> void
+ {
+ if (m_renderer_id == 0 || glDispatchComputeIndirect == nullptr)
+ return;
+ glDispatchComputeIndirect(offset);
+ }
+
+ auto Shader::memory_barrier(uint32_t barriers) -> void
+ {
+ if (glMemoryBarrier == nullptr)
+ return;
+ glMemoryBarrier(barriers);
+ }
+
+ auto Shader::create(const std::string& filepath) -> Shader*
+ {
+ return new Shader(filepath);
+ }
+
+ auto Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*
+ {
+ return new Shader(name, vertex_src, fragment_src);
+ }
+
+ auto Shader::create_compute(const std::string& name, const std::string& compute_src) -> Shader*
+ {
+ return new Shader(name, compute_src);
}
auto ShaderLibrary::add(const Ref<Shader>& shader) -> void
diff --git a/src/rendering/shader.h b/src/rendering/shader.h
index bc3fd9a..409aca3 100644
--- a/src/rendering/shader.h
+++ b/src/rendering/shader.h
@@ -11,34 +11,58 @@
#define TEXTURE_FETCH_BARRIER_BIT 0x00000008
#define IMAGE_ACCESS_BARRIER_BIT 0x00000020
-namespace Donut
+namespace Donut
{
class Shader
{
public:
- virtual ~Shader() = default;
+ Shader(const std::string& filepath);
+ Shader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
+ Shader(const std::string& name, const std::string& compute_src);
+ ~Shader();
- virtual auto bind() const -> void = 0;
- virtual auto unbind() const -> void = 0;
+ auto bind() const -> void;
+ auto unbind() const -> void;
- virtual auto set_int( const std::string& name, int value) -> void = 0;
- virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void = 0;
- virtual auto set_float( const std::string& name, float value) -> void = 0;
- virtual auto set_float2( const std::string& name, const glm::vec2& value) -> void = 0;
- virtual auto set_float3( const std::string& name, const glm::vec3& value) -> void = 0;
- virtual auto set_float4( const std::string& name, const glm::vec4& value) -> void = 0;
- virtual auto set_mat4( const std::string& name, const glm::mat4& value) -> void = 0;
+ auto set_int( const std::string& name, int value) -> void;
+ auto set_int_array(const std::string& name, int* values, uint32_t count) -> void;
+ auto set_float( const std::string& name, float value) -> void;
+ auto set_float2( const std::string& name, const glm::vec2& value) -> void;
+ auto set_float3( const std::string& name, const glm::vec3& value) -> void;
+ auto set_float4( const std::string& name, const glm::vec4& value) -> void;
+ auto set_mat4( const std::string& name, const glm::mat4& value) -> void;
- virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void = 0;
- virtual auto dispatch_indirect(uint32_t offset = 0) -> void = 0;
- virtual auto memory_barrier(uint32_t barriers) -> void = 0;
+ auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void;
+ auto dispatch_indirect(uint32_t offset = 0) -> void;
+ auto memory_barrier(uint32_t barriers) -> void;
- virtual auto get_name() const -> const std::string& = 0;
- virtual auto get_renderer_id() const -> uint32_t = 0;
+ auto get_name() const -> const std::string& { return m_name; }
+ auto get_renderer_id() const -> uint32_t { return m_renderer_id; }
+
+ auto upload_uniform_int( const std::string& name, int value) -> void;
+ auto upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void;
+ auto upload_uniform_float( const std::string& name, float value) -> void;
+ auto upload_uniform_float2( const std::string& name, const glm::vec2& value) -> void;
+ auto upload_uniform_float3( const std::string& name, const glm::vec3& value) -> void;
+ auto upload_uniform_float4( const std::string& name, const glm::vec4& value) -> void;
+ auto upload_uniform_mat3( const std::string& name, const glm::mat3& matrix) -> void;
+ auto upload_uniform_mat4( const std::string& name, const glm::mat4& matrix) -> void;
static auto create(const std::string& filepath) -> Shader*;
static auto create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*;
static auto create_compute(const std::string& name, const std::string& compute_src) -> Shader*;
+
+ private:
+ auto read_file(const std::string& filepath) -> std::string;
+ auto pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>;
+ auto compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void;
+
+ uint32_t m_renderer_id = 0;
+ std::string m_name;
+ // True when loaded from a Slang-compiled GLSL. Slang expects row-major
+ // matrix data, so matrix uniforms are transposed on upload (glm is
+ // column-major) to keep all matrix math correct.
+ bool m_is_slang = false;
};
class ShaderLibrary
@@ -49,7 +73,7 @@ namespace Donut
auto load(const std::string& filepath) -> Ref<Shader>;
auto load(const std::string& name, const std::string& filepath) -> Ref<Shader>;
- Ref<Shader> Get(const std::string& name);
+ auto Get(const std::string& name) -> Ref<Shader>;
auto exists(const std::string& name) const -> bool;
private:
diff --git a/src/rendering/texture.cpp b/src/rendering/texture.cpp
index 2470e46..12e07f1 100644
--- a/src/rendering/texture.cpp
+++ b/src/rendering/texture.cpp
@@ -1,68 +1,296 @@
#include "texture.h"
-#include "renderer.h"
-#include "platform/opengl/opengl_texture.h"
-#include "platform/vulkan/vulkan_texture.h"
+#include "shader.h"
+#include "core/log.h"
+
+#include <glad/glad.h>
+
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtc/type_ptr.hpp>
+
+// NOTE: This file targets OpenGL 4.1 (the maximum macOS exposes). It uses the
+// classic bind-based texture API rather than 4.5 Direct State Access
+// (glCreateTextures / glTextureStorage2D / glTextureParameteri / glBindTextureUnit),
+// none of which exist on macOS. It also hosts the single STB_IMAGE_IMPLEMENTATION
+// for the whole build (the Vulkan backend links stbi_loadf from here too).
namespace Donut
{
- auto Texture2D::create(uint32_t width, uint32_t height) -> Ref<Texture2D>
+ Texture2D::Texture2D(uint32_t width, uint32_t height)
+ : m_width(width), m_height(height)
{
- switch (Renderer::get_api())
+ m_internal_format = GL_RGBA8;
+ m_data_format = GL_RGBA;
+
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+
+ Texture2D::Texture2D(const std::string& path)
+ : m_path(path)
+ {
+ m_width = 1;
+ m_height = 1;
+ m_internal_format = GL_RGBA8;
+ m_data_format = GL_RGBA;
+
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+ uint32_t white_pixel = 0xFFFFFFFF;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, &white_pixel);
+
+ DONUT_INFO("Created default texture (stb_image not available for loading: ", path, ")");
+ }
+
+ Texture2D::~Texture2D()
+ {
+ glDeleteTextures(1, &m_renderer_id);
+ }
+
+ auto Texture2D::set_data(void* data, uint32_t size) -> void
+ {
+ uint32_t bpp = m_data_format == GL_RGBA ? 4 : 3;
+ if (size != m_width * m_height * bpp)
{
- case RendererAPI::API::OpenGL:
- return create_ref<OpenGLTexture2D>(width, height);
- case RendererAPI::API::Vulkan:
- return create_ref<VulkanTexture2D>(width, height);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
+ DONUT_ERROR("Data must be entire texture!");
+ return;
}
+
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, data);
}
- auto Texture2D::create(const std::string& path) -> Ref<Texture2D>
+ auto Texture2D::bind(uint32_t slot) const -> void
{
- switch (Renderer::get_api())
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ }
+
+ auto Texture2D::bind_as_image(uint32_t slot, bool read_only) const -> void
+ {
+ // Image load/store is OpenGL 4.2 and unavailable on macOS. Guard the
+ // function pointer so this degrades to a no-op instead of crashing.
+ if (glBindImageTexture == nullptr)
+ return;
+ GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
+ glBindImageTexture(slot, m_renderer_id, 0, GL_FALSE, 0, access, m_internal_format);
+ }
+
+ CubemapTexture::CubemapTexture(uint32_t width, uint32_t height)
+ : m_width(width), m_height(height)
+ {
+ m_internal_format = GL_RGBA16F;
+ m_data_format = GL_RGBA;
+
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
+ for (uint32_t i = 0; i < 6; ++i)
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ }
+
+ CubemapTexture::CubemapTexture(const std::string& path)
+ : m_path(path)
+ {
+ m_width = 1024;
+ m_height = 1024;
+ m_internal_format = GL_RGBA16F;
+ m_data_format = GL_RGBA;
+
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
+ for (uint32_t i = 0; i < 6; ++i)
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
+
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+ LoadHDRI(path);
+ }
+
+ CubemapTexture::~CubemapTexture()
+ {
+ glDeleteTextures(1, &m_renderer_id);
+ }
+
+ auto CubemapTexture::LoadHDRI(const std::string& path) -> void
+ {
+ stbi_set_flip_vertically_on_load(true);
+ int width, height, channels;
+ float* hdr_data = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
+
+ if (!hdr_data)
{
- case RendererAPI::API::OpenGL:
- return create_ref<OpenGLTexture2D>(path);
- case RendererAPI::API::Vulkan:
- return create_ref<VulkanTexture2D>(path);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
+ DONUT_ERROR("Failed to load HDRI: {}", path);
+ float default_sky[6 * 4] =
+ {
+ 0.5f, 0.7f, 1.0f, 1.0f, // Right
+ 0.5f, 0.7f, 1.0f, 1.0f, // Left
+ 0.5f, 0.7f, 1.0f, 1.0f, // Top
+ 0.5f, 0.7f, 1.0f, 1.0f, // Bottom
+ 0.5f, 0.7f, 1.0f, 1.0f, // Front
+ 0.5f, 0.7f, 1.0f, 1.0f // Back
+ };
+
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
+ for (int i = 0; i < 6; ++i)
+ glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, &default_sky[i * 4]);
+ return;
}
+
+ convert_equirectangular_to_cubemap(hdr_data, width, height);
+ stbi_image_free(hdr_data);
+
+ DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height);
}
- auto CubemapTexture::create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>
+ auto CubemapTexture::convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void
{
- switch (Renderer::get_api())
+ uint32_t capture_fbo, capture_rbo;
+ glGenFramebuffers(1, &capture_fbo);
+ glGenRenderbuffers(1, &capture_rbo);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_width, m_height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, capture_rbo);
+
+ uint32_t hdr_texture;
+ glGenTextures(1, &hdr_texture);
+ glBindTexture(GL_TEXTURE_2D, hdr_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdr_data);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ auto equirect_shader = Shader::create("assets/shaders/EquirectToCubemap.glsl");
+ if (!equirect_shader)
{
- case RendererAPI::API::OpenGL:
- return create_ref<OpenGLCubemapTexture>(width, height);
- case RendererAPI::API::Vulkan:
- return create_ref<VulkanCubemapTexture>(width, height);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
+ DONUT_ERROR("Failed to create equirectangular to cubemap shader");
+ return;
}
+
+ uint32_t shader_program = equirect_shader->get_renderer_id();
+
+ float vertices[] =
+ {
+ -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+ 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
+ -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
+ -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
+ -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f
+ };
+
+ uint32_t cube_vao, cube_vbo;
+ glGenVertexArrays(1, &cube_vao);
+ glGenBuffers(1, &cube_vbo);
+ glBindVertexArray(cube_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
+
+ glm::mat4 capture_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
+ glm::mat4 capture_views[] =
+ {
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
+ glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
+ };
+
+ glUseProgram(shader_program);
+ glUniform1i(glGetUniformLocation(shader_program, "u_EquirectangularMap"), 0);
+ // EquirectToCubemap is authored in Slang (row-major); transpose glm's
+ // column-major matrices on upload (GL_TRUE) to match.
+ glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_Projection"), 1, GL_TRUE, &capture_projection[0][0]);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, hdr_texture);
+
+ glViewport(0, 0, m_width, m_height);
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
+ for (unsigned int i = 0; i < 6; ++i)
+ {
+ glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_View"), 1, GL_TRUE, &capture_views[i][0][0]);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_renderer_id, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glBindVertexArray(cube_vao);
+ glDrawArrays(GL_TRIANGLES, 0, 36);
+ }
+ glBindVertexArray(0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glDeleteVertexArrays(1, &cube_vao);
+ glDeleteBuffers(1, &cube_vbo);
+ glDeleteTextures(1, &hdr_texture);
+ glDeleteFramebuffers(1, &capture_fbo);
+ glDeleteRenderbuffers(1, &capture_rbo);
+ }
+
+ auto CubemapTexture::set_data(void* data, uint32_t size) -> void
+ {
+ DONUT_WARN("set_data not implemented for cubemaps");
+ }
+
+ auto CubemapTexture::bind(uint32_t slot) const -> void
+ {
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
+ }
+
+ auto CubemapTexture::bind_as_image(uint32_t slot, bool read_only) const -> void
+ {
+ if (glBindImageTexture == nullptr)
+ return;
+ GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
+ glBindImageTexture(slot, m_renderer_id, 0, GL_TRUE, 0, access, m_internal_format);
+ }
+
+ auto Texture2D::create(uint32_t width, uint32_t height) -> Ref<Texture2D>
+ {
+ return create_ref<Texture2D>(width, height);
+ }
+
+ auto Texture2D::create(const std::string& path) -> Ref<Texture2D>
+ {
+ return create_ref<Texture2D>(path);
+ }
+
+ auto CubemapTexture::create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>
+ {
+ return create_ref<CubemapTexture>(width, height);
}
auto CubemapTexture::create_from_hdri(const std::string& path) -> Ref<CubemapTexture>
{
- switch (Renderer::get_api())
- {
- case RendererAPI::API::OpenGL:
- return create_ref<OpenGLCubemapTexture>(path);
- case RendererAPI::API::Vulkan:
- return create_ref<VulkanCubemapTexture>(path);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
- }
+ return create_ref<CubemapTexture>(path);
}
-}; \ No newline at end of file
+};
diff --git a/src/rendering/texture.h b/src/rendering/texture.h
index e9b3e0a..5f68c78 100644
--- a/src/rendering/texture.h
+++ b/src/rendering/texture.h
@@ -3,6 +3,7 @@
#include "core/memory.h"
#include <string>
+#include <cstdint>
#include <glm/glm.hpp>
namespace Donut
@@ -18,25 +19,65 @@ namespace Donut
virtual auto set_data(void* data, uint32_t size) -> void = 0;
virtual auto bind(uint32_t slot = 0) const -> void = 0;
- virtual void bind_as_image(uint32_t slot = 0,
- bool read_only = false) const = 0;
+ virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void = 0;
- virtual bool operator==(const Texture& other) const = 0;
+ virtual auto operator==(const Texture& other) const -> bool = 0;
};
- class Texture2D
- : public Texture
+ class Texture2D : public Texture
{
public:
+ Texture2D(uint32_t width, uint32_t height);
+ Texture2D(const std::string& path);
+ ~Texture2D();
+
+ auto get_width() const -> uint32_t override { return m_width; }
+ auto get_height() const -> uint32_t override { return m_height; }
+ auto get_renderer_id() const -> uint32_t override { return m_renderer_id; }
+
+ auto set_data(void* data, uint32_t size) -> void override;
+ auto bind(uint32_t slot = 0) const -> void override;
+ auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ auto operator==(const Texture& other) const -> bool override { return m_renderer_id == other.get_renderer_id(); }
+
static auto create(uint32_t width, uint32_t height) -> Ref<Texture2D>;
static auto create(const std::string& path) -> Ref<Texture2D>;
+
+ private:
+ std::string m_path;
+ uint32_t m_width = 0, m_height = 0;
+ uint32_t m_renderer_id = 0;
+ uint32_t m_internal_format = 0, m_data_format = 0; // GLenum values
};
- class CubemapTexture
- : public Texture
+ class CubemapTexture : public Texture
{
public:
+ CubemapTexture(uint32_t width, uint32_t height);
+ CubemapTexture(const std::string& path);
+ ~CubemapTexture();
+
+ auto get_width() const -> uint32_t override { return m_width; }
+ auto get_height() const -> uint32_t override { return m_height; }
+ auto get_renderer_id() const -> uint32_t override { return m_renderer_id; }
+
+ auto set_data(void* data, uint32_t size) -> void override;
+ auto bind(uint32_t slot = 0) const -> void override;
+ auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ auto operator==(const Texture& other) const -> bool override { return m_renderer_id == other.get_renderer_id(); }
+
static auto create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>;
static auto create_from_hdri(const std::string& path) -> Ref<CubemapTexture>;
+
+ private:
+ auto LoadHDRI(const std::string& path) -> void;
+ auto convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void;
+
+ std::string m_path;
+ uint32_t m_width = 0, m_height = 0;
+ uint32_t m_renderer_id = 0;
+ uint32_t m_internal_format = 0, m_data_format = 0; // GLenum values
};
-}; \ No newline at end of file
+};
diff --git a/src/rendering/texture_manager.cpp b/src/rendering/texture_manager.cpp
index ad2c68b..3afba0d 100644
--- a/src/rendering/texture_manager.cpp
+++ b/src/rendering/texture_manager.cpp
@@ -1,13 +1,13 @@
#include "texture_manager.h"
-#include "platform/opengl/opengl_texture.h"
-#include "platform/opengl/opengl_renderer_api.h"
+#include "renderer.h"
+
#include <glad/glad.h>
namespace Donut
{
auto TextureManager::create_texture(uint32_t width, uint32_t height) -> Ref<Texture2D>
{
- return create_ref<OpenGLTexture2D>(width, height);
+ return create_ref<Texture2D>(width, height);
}
auto TextureManager::bind_texture(uint32_t texture_id, uint32_t slot) -> void
diff --git a/src/rendering/uniform_buffer.cpp b/src/rendering/uniform_buffer.cpp
index c344aec..c4118b8 100644
--- a/src/rendering/uniform_buffer.cpp
+++ b/src/rendering/uniform_buffer.cpp
@@ -1,23 +1,36 @@
#include "uniform_buffer.h"
-#include "renderer.h"
-#include "platform/opengl/opengl_uniform_buffer.h"
-#include "platform/vulkan/vulkan_uniform_buffer.h"
+#include <glad/glad.h>
namespace Donut
{
+ UniformBuffer::UniformBuffer(uint32_t size, uint32_t binding)
+ : m_size(size), m_binding(binding)
+ {
+ glGenBuffers(1, &m_renderer_id);
+ glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
+ glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
+ glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
+ }
+
+ UniformBuffer::~UniformBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ auto UniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset) -> void
+ {
+ glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
+ glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
+ }
+
+ auto UniformBuffer::bind(uint32_t binding) -> void
+ {
+ glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
+ }
+
auto UniformBuffer::create(uint32_t size, uint32_t binding) -> Ref<UniformBuffer>
{
- switch (Renderer::get_api())
- {
- case RendererAPI::API::OpenGL:
- return create_ref<OpenGLUniformBuffer>(size, binding);
- case RendererAPI::API::Vulkan:
- return create_ref<VulkanUniformBuffer>(size, binding);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
- }
+ return create_ref<UniformBuffer>(size, binding);
}
};
diff --git a/src/rendering/uniform_buffer.h b/src/rendering/uniform_buffer.h
index eb28e53..b5617b9 100644
--- a/src/rendering/uniform_buffer.h
+++ b/src/rendering/uniform_buffer.h
@@ -8,11 +8,17 @@ namespace Donut
class UniformBuffer
{
public:
- virtual ~UniformBuffer() = default;
+ UniformBuffer(uint32_t size, uint32_t binding);
+ ~UniformBuffer();
- virtual auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void = 0;
- virtual auto bind(uint32_t binding) -> void = 0;
+ auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void;
+ auto bind(uint32_t binding) -> void;
static auto create(uint32_t size, uint32_t binding) -> Ref<UniformBuffer>;
+
+ private:
+ uint32_t m_renderer_id = 0;
+ uint32_t m_size = 0;
+ uint32_t m_binding = 0;
};
};
diff --git a/src/rendering/vertex_array.cpp b/src/rendering/vertex_array.cpp
index 8045707..cddcf35 100644
--- a/src/rendering/vertex_array.cpp
+++ b/src/rendering/vertex_array.cpp
@@ -1,32 +1,61 @@
-#include "vertex_array.h"
-#include "renderer.h"
+#include <glad/glad.h>
-#include "platform/opengl/opengl_vertex_array.h"
-#include "platform/vulkan/vulkan_vertex_array.h"
+#include "vertex_array.h"
namespace Donut
{
- auto VertexBufferElement::get_size_of_type(uint32_t type) -> uint32_t
+ VertexArray::VertexArray()
+ {
+ // glCreateVertexArrays is 4.5 DSA; macOS caps at 4.1. glGenVertexArrays
+ // reserves the name and the VAO is created on first bind (done below).
+ glGenVertexArrays(1, &m_renderer_id);
+ }
+
+ VertexArray::~VertexArray()
+ {
+ glDeleteVertexArrays(1, &m_renderer_id);
+ }
+
+ auto VertexArray::bind() const -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ }
+
+ auto VertexArray::unbind() const -> void
{
- switch (type)
+ glBindVertexArray(0);
+ }
+
+ auto VertexArray::add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ vertex_buffer->bind();
+
+ const auto& layout = vertex_buffer->get_layout();
+ for (const auto& element : layout.get_elements())
{
- case 0x1406: return 4; // GL_FLOAT
- case 0x1405: return 4; // GL_UNSIGNED_INT
- case 0x1401: return 1; // GL_UNSIGNED_BYTE
- default: return 0;
+ glEnableVertexAttribArray(m_vertex_buffer_index);
+ glVertexAttribPointer(m_vertex_buffer_index,
+ element.count,
+ element.type,
+ element.normalized ? GL_TRUE : GL_FALSE,
+ layout.get_stride(),
+ reinterpret_cast<const void*>(static_cast<uintptr_t>(element.offset)));
+ m_vertex_buffer_index++;
}
+
+ m_vertex_buffers.push_back(vertex_buffer);
+ }
+
+ auto VertexArray::set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ index_buffer->bind();
+ m_index_buffer = index_buffer;
}
auto VertexArray::create() -> VertexArray*
{
- switch (Renderer::get_api())
- {
- case RendererAPI::API::OpenGL:
- return new OpenGLVertexArray();
- case RendererAPI::API::Vulkan:
- return new VulkanVertexArray();
- default:
- return nullptr;
- }
+ return new VertexArray();
}
};
diff --git a/src/rendering/vertex_array.h b/src/rendering/vertex_array.h
index 921df56..1f06758 100644
--- a/src/rendering/vertex_array.h
+++ b/src/rendering/vertex_array.h
@@ -9,20 +9,27 @@
namespace Donut
{
- class VertexArray
+ class VertexArray
{
public:
- virtual ~VertexArray() = default;
+ VertexArray();
+ ~VertexArray();
- virtual auto bind() const -> void = 0;
- virtual auto unbind() const -> void = 0;
+ auto bind() const -> void;
+ auto unbind() const -> void;
- virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void = 0;
- virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void = 0;
+ auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void;
+ auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void;
- virtual auto get_vertex_buffers() const -> const std::vector<Ref<VertexBuffer>>& = 0;
- virtual auto get_index_buffer() const -> const Ref<IndexBuffer>& = 0;
+ auto get_vertex_buffers() const -> const std::vector<Ref<VertexBuffer>>& { return m_vertex_buffers; }
+ auto get_index_buffer() const -> const Ref<IndexBuffer>& { return m_index_buffer; }
static auto create() -> VertexArray*;
+
+ private:
+ uint32_t m_renderer_id = 0;
+ uint32_t m_vertex_buffer_index = 0;
+ std::vector<Ref<VertexBuffer>> m_vertex_buffers;
+ Ref<IndexBuffer> m_index_buffer;
};
};
diff --git a/src/rendering/vertex_buffer.cpp b/src/rendering/vertex_buffer.cpp
index 354d0cd..1bc471c 100644
--- a/src/rendering/vertex_buffer.cpp
+++ b/src/rendering/vertex_buffer.cpp
@@ -1,21 +1,50 @@
#include "vertex_buffer.h"
-#include "renderer.h"
-#include "platform/opengl/opengl_vertex_buffer.h"
-#include "platform/vulkan/vulkan_vertex_buffer.h"
+#include <glad/glad.h>
namespace Donut
{
- auto VertexBuffer::create(const void* data, uint32_t size) -> VertexBuffer*
+ auto VertexBufferElement::get_size_of_type(uint32_t type) -> uint32_t
{
- switch (Renderer::get_api())
+ switch (type)
{
- case RendererAPI::API::OpenGL:
- return new OpenGLVertexBuffer(data, size);
- case RendererAPI::API::Vulkan:
- return new VulkanVertexBuffer((float*)data, size);
- default:
- return nullptr;
+ case 0x1406: return 4; // GL_FLOAT
+ case 0x1405: return 4; // GL_UNSIGNED_INT
+ case 0x1401: return 1; // GL_UNSIGNED_BYTE
+ default: return 0;
}
}
+
+ VertexBuffer::VertexBuffer(const void* data, uint32_t size)
+ {
+ glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
+ }
+
+ VertexBuffer::~VertexBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ auto VertexBuffer::bind() const -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ }
+
+ auto VertexBuffer::unbind() const -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ auto VertexBuffer::set_data(const void* data, uint32_t size) -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
+ }
+
+ auto VertexBuffer::create(const void* data, uint32_t size) -> VertexBuffer*
+ {
+ return new VertexBuffer(data, size);
+ }
};
diff --git a/src/rendering/vertex_buffer.h b/src/rendering/vertex_buffer.h
index 6be7e34..4e61700 100644
--- a/src/rendering/vertex_buffer.h
+++ b/src/rendering/vertex_buffer.h
@@ -3,9 +3,9 @@
#include <cstdint>
#include <vector>
-namespace Donut
+namespace Donut
{
- struct VertexBufferElement
+ struct VertexBufferElement
{
uint32_t type;
uint32_t count;
@@ -22,27 +22,27 @@ namespace Donut
~VertexBufferLayout() = default;
template<typename T>
- auto push(uint32_t count) -> void
+ auto push(uint32_t count) -> void
{
static_assert(false);
}
template<>
- void push<float>(uint32_t count)
+ void push<float>(uint32_t count)
{
m_elements.push_back({ 0x1406, count, 0, m_stride }); // GL_FLOAT
m_stride += count * VertexBufferElement::get_size_of_type(0x1406);
}
template<>
- void push<uint32_t>(uint32_t count)
+ void push<uint32_t>(uint32_t count)
{
m_elements.push_back({ 0x1405, count, 0, m_stride }); // GL_UNSIGNED_INT
m_stride += count * VertexBufferElement::get_size_of_type(0x1405);
}
template<>
- void push<uint8_t>(uint32_t count)
+ void push<uint8_t>(uint32_t count)
{
m_elements.push_back({ 0x1401, count, 1, m_stride }); // GL_UNSIGNED_BYTE
m_stride += count * VertexBufferElement::get_size_of_type(0x1401);
@@ -59,15 +59,20 @@ namespace Donut
class VertexBuffer
{
public:
- virtual ~VertexBuffer() = default;
+ VertexBuffer(const void* data, uint32_t size);
+ ~VertexBuffer();
- virtual auto bind() const -> void = 0;
- virtual auto unbind() const -> void = 0;
- virtual auto set_data(const void* data, uint32_t size) -> void = 0;
+ auto bind() const -> void;
+ auto unbind() const -> void;
+ auto set_data(const void* data, uint32_t size) -> void;
- virtual auto get_layout() const -> const VertexBufferLayout& = 0;
- virtual auto set_layout(const VertexBufferLayout& layout) -> void = 0;
+ auto get_layout() const -> const VertexBufferLayout& { return m_layout; }
+ auto set_layout(const VertexBufferLayout& layout) -> void { m_layout = layout; }
static auto create(const void* data, uint32_t size) -> VertexBuffer*;
+
+ private:
+ uint32_t m_renderer_id = 0;
+ VertexBufferLayout m_layout;
};
};