aboutsummaryrefslogtreecommitdiff
path: root/src/rendering
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-23 21:55:36 +0200
committerhachem <im@hachem.wtf>2026-08-23 21:55:36 +0200
commit5ea6c14c0e14bacee766ce1280ab2c71564e05ec (patch)
tree34620051e8eaf65db4ff9cd40ffd982a03e1ccc8 /src/rendering
parent8742482311b86bb705d93336805ab26881e96070 (diff)
[feat]: implement ui layer arch
Diffstat (limited to 'src/rendering')
-rw-r--r--src/rendering/black_hole_renderer.h2
-rw-r--r--src/rendering/render_path.cpp94
-rw-r--r--src/rendering/render_path.h40
-rw-r--r--src/rendering/renderer_backend.h32
-rw-r--r--src/rendering/scene_renderer.h2
-rw-r--r--src/rendering/shader.cpp354
-rw-r--r--src/rendering/shader.h82
-rw-r--r--src/rendering/texture.cpp296
-rw-r--r--src/rendering/texture.h83
-rw-r--r--src/rendering/view.h13
10 files changed, 149 insertions, 849 deletions
diff --git a/src/rendering/black_hole_renderer.h b/src/rendering/black_hole_renderer.h
index b9c158b..63c5aa2 100644
--- a/src/rendering/black_hole_renderer.h
+++ b/src/rendering/black_hole_renderer.h
@@ -1,7 +1,7 @@
#pragma once
#include "rhi.h"
-#include "renderer_backend.h" // BlackHoleParams
+#include "scene/scene_types.h" // BlackHoleParams
#include <glm/glm.hpp>
namespace Donut
diff --git a/src/rendering/render_path.cpp b/src/rendering/render_path.cpp
new file mode 100644
index 0000000..172e13c
--- /dev/null
+++ b/src/rendering/render_path.cpp
@@ -0,0 +1,94 @@
+#include "render_path.h"
+
+#include "scene/scene.h"
+
+#include <glm/gtc/matrix_transform.hpp>
+#include <algorithm>
+#include <cmath>
+
+namespace Donut
+{
+ auto RenderPath::init(RHI::Device& device, const std::string& hdri_path) -> bool
+ {
+ m_device = &device;
+ m_hdri_path = hdri_path;
+ m_cubemap = device.create_cubemap_from_hdri(hdri_path);
+
+ m_scene_renderer = create_scope<SceneRenderer>();
+ m_scene_renderer->init(device);
+ m_black_hole_renderer = create_scope<BlackHoleRenderer>();
+ m_black_hole_renderer->init(device);
+ return true;
+ }
+
+ auto RenderPath::sync_hdri(const std::string& hdri_path) -> void
+ {
+ if (hdri_path == m_hdri_path || !m_device) return;
+ m_device->wait_idle();
+ m_cubemap = m_device->create_cubemap_from_hdri(hdri_path); // old cube freed after idle
+ m_hdri_path = hdri_path;
+ }
+
+ auto RenderPath::render(RHI::CommandList& cmd, Scene& scene, View view,
+ int fb_width, int fb_height, bool moving, float time) -> void
+ {
+ const glm::vec4 clear(0.05f, 0.06f, 0.10f, 1.0f);
+
+ // The black hole is the only view with an off-screen pass; it renders (and
+ // runs the expensive geodesic) ONLY on the Simulation tab. Scene and None
+ // are a single swapchain pass — None draws nothing (empty viewport).
+ if (view != View::BlackHole)
+ {
+ cmd.begin_render_pass(nullptr, clear);
+ if (view == View::Scene)
+ {
+ float aspect = (float)fb_width / (float)std::max(fb_height, 1);
+ scene.scene_camera.set_projection(45.0f, aspect, 0.1f, 1000.0f);
+ CameraView cv;
+ cv.view = scene.scene_camera.get_view_matrix();
+ cv.projection = scene.scene_camera.get_projection_matrix();
+ cv.position = scene.scene_camera.get_orbital_position();
+ cv.fb_width = fb_width; cv.fb_height = fb_height;
+ m_scene_renderer->render(cmd, cv, scene.objects, scene.selected_object, m_cubemap.get());
+ }
+ m_device->imgui_render(cmd);
+ cmd.end_render_pass();
+ }
+ else
+ {
+ GeodesicView gv;
+ glm::vec3 pos, fwd;
+ if (scene.sim_camera.get_camera_mode() == CameraMode::FPS)
+ {
+ pos = scene.sim_camera.get_position();
+ fwd = scene.sim_camera.get_forward_direction();
+ }
+ else
+ {
+ pos = scene.sim_camera.get_orbital_position();
+ fwd = glm::normalize(scene.sim_camera.get_orbital_target() - pos);
+ }
+ glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
+ gv.position = pos; gv.right = right; gv.up = glm::cross(right, fwd); gv.forward = fwd;
+ gv.tan_half_fov = (float)tan(glm::radians(scene.black_hole.fov_degrees * 0.5f));
+ gv.aspect = (float)BlackHoleRenderer::GEO_HI_W / (float)BlackHoleRenderer::GEO_HI_H;
+ gv.moving = moving;
+ gv.time = time;
+
+ m_black_hole_renderer->render_geodesic(cmd, gv, scene.black_hole, m_cubemap.get());
+ cmd.begin_render_pass(nullptr, clear);
+ m_black_hole_renderer->blit(cmd, fb_width, fb_height);
+ m_device->imgui_render(cmd);
+ cmd.end_render_pass();
+ }
+ }
+
+ auto RenderPath::shutdown() -> void
+ {
+ m_scene_renderer.reset();
+ m_black_hole_renderer.reset();
+ m_cubemap.reset();
+ // Backend-specific HDRI/GPU resource cleanup is the device's job (see e.g.
+ // the OpenGL device clearing the HDRIManager texture cache on shutdown).
+ }
+}
diff --git a/src/rendering/render_path.h b/src/rendering/render_path.h
new file mode 100644
index 0000000..0d97395
--- /dev/null
+++ b/src/rendering/render_path.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "rhi.h"
+#include "view.h"
+#include "scene_renderer.h"
+#include "black_hole_renderer.h"
+#include "core/memory.h"
+
+#include <string>
+
+namespace Donut
+{
+ class Scene;
+
+ // Owns the device-side rendering: the two portable renderers, the shared HDRI
+ // cubemap, and the per-frame composition (off-screen geodesic pass -> present,
+ // or the scene pass), with ImGui drawn on top inside the swapchain pass. The
+ // Application hands it a Scene + View each frame; it knows nothing about the UI.
+ class RenderPath
+ {
+ public:
+ auto init(RHI::Device& device, const std::string& hdri_path) -> bool;
+ auto shutdown() -> void;
+
+ // Rebuilds the environment cubemap if the path changed (device-idle).
+ auto sync_hdri(const std::string& hdri_path) -> void;
+
+ // Composes one frame's viewport into the swapchain command list. `moving`
+ // drives the black hole's progressive resolution; `time` animates the disk.
+ auto render(RHI::CommandList& cmd, Scene& scene, View view,
+ int fb_width, int fb_height, bool moving, float time) -> void;
+
+ private:
+ RHI::Device* m_device = nullptr;
+ Scope<SceneRenderer> m_scene_renderer;
+ Scope<BlackHoleRenderer> m_black_hole_renderer;
+ Ref<RHI::Texture> m_cubemap;
+ std::string m_hdri_path;
+ };
+}
diff --git a/src/rendering/renderer_backend.h b/src/rendering/renderer_backend.h
deleted file mode 100644
index d34983f..0000000
--- a/src/rendering/renderer_backend.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <glm/glm.hpp>
-
-namespace Donut
-{
- // Backend-agnostic scene/black-hole data shared by the Application and the
- // portable renderers (SceneRenderer, BlackHoleRenderer). The rendering itself
- // is written once on the RHI (rendering/rhi.h); these are just the inputs.
-
- // A placeable/editable sphere in the world-builder scene view. The app owns
- // the list; SceneRenderer draws it the same way on every backend.
- struct SceneObject
- {
- glm::vec3 position = { 0.0f, 2.0f, 0.0f };
- float radius = 2.0f;
- glm::vec3 color = { 0.85f, 0.35f, 0.2f };
- };
-
- // Live-tunable black-hole / accretion-disk parameters. Radii are in
- // Schwarzschild radii; the inner edge is clamped to the ISCO (3 r_s).
- struct BlackHoleParams
- {
- float disk_inner_rs = 3.0f; // inner edge (x r_s)
- float disk_outer_rs = 12.0f; // outer edge (x r_s)
- float temperature = 4800.0f; // Kelvin at the flux peak (disk colour)
- float brightness = 0.9f; // disk exposure
- float turbulence = 0.4f; // 0 = smooth physical disk
- int quality_steps = 15000; // geodesic integration steps
- float fov_degrees = 60.0f; // camera field of view
- };
-}
diff --git a/src/rendering/scene_renderer.h b/src/rendering/scene_renderer.h
index eafd56e..c3ee02f 100644
--- a/src/rendering/scene_renderer.h
+++ b/src/rendering/scene_renderer.h
@@ -1,7 +1,7 @@
#pragma once
#include "rhi.h"
-#include "renderer_backend.h" // SceneObject
+#include "scene/scene_types.h" // SceneObject
#include <glm/glm.hpp>
#include <vector>
diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp
deleted file mode 100644
index 80015e6..0000000
--- a/src/rendering/shader.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-#include "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;
- }
-
- 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
- {
- 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 Shader::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 Shader::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 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
- {
- auto& name = shader->get_name();
- add(name, shader);
- }
-
- auto ShaderLibrary::add(const std::string& name, const Ref<Shader>& shader) -> void
- {
- m_shaders[name] = shader;
- }
-
- auto ShaderLibrary::load(const std::string& filepath) -> Ref<Shader>
- {
- auto shader = Ref<Shader>(Shader::create(filepath));
- add(shader);
- return shader;
- }
-
- auto ShaderLibrary::load(const std::string& name, const std::string& filepath) -> Ref<Shader>
- {
- auto shader = Ref<Shader>(Shader::create(filepath));
- add(name, shader);
- return shader;
- }
-
- auto ShaderLibrary::Get(const std::string& name) -> Ref<Shader>
- {
- if (exists(name))
- return m_shaders[name];
- return nullptr;
- }
-
- auto ShaderLibrary::exists(const std::string& name) const -> bool
- {
- return m_shaders.find(name) != m_shaders.end();
- }
-};
diff --git a/src/rendering/shader.h b/src/rendering/shader.h
deleted file mode 100644
index 409aca3..0000000
--- a/src/rendering/shader.h
+++ /dev/null
@@ -1,82 +0,0 @@
-#pragma once
-
-#include "core/memory.h"
-
-#include <string>
-#include <unordered_map>
-#include <glm/glm.hpp>
-
-#define SHADER_STORAGE_BARRIER_BIT 0x00002000
-#define UNIFORM_BARRIER_BIT 0x00000004
-#define TEXTURE_FETCH_BARRIER_BIT 0x00000008
-#define IMAGE_ACCESS_BARRIER_BIT 0x00000020
-
-namespace Donut
-{
- class Shader
- {
- public:
- 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();
-
- auto bind() const -> void;
- auto unbind() const -> void;
-
- 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;
-
- 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;
-
- 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
- {
- public:
- auto add(const Ref<Shader>& shader) -> void;
- auto add(const std::string& name, const Ref<Shader>& shader) -> void;
- auto load(const std::string& filepath) -> Ref<Shader>;
- auto load(const std::string& name, const std::string& filepath) -> Ref<Shader>;
-
- auto Get(const std::string& name) -> Ref<Shader>;
-
- auto exists(const std::string& name) const -> bool;
- private:
- std::unordered_map<std::string, Ref<Shader>> m_shaders;
- };
-};
diff --git a/src/rendering/texture.cpp b/src/rendering/texture.cpp
deleted file mode 100644
index 12e07f1..0000000
--- a/src/rendering/texture.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-#include "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
-{
- Texture2D::Texture2D(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);
- }
-
- 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)
- {
- 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::bind(uint32_t slot) const -> void
- {
- 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)
- {
- 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::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 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>
- {
- return create_ref<CubemapTexture>(path);
- }
-};
diff --git a/src/rendering/texture.h b/src/rendering/texture.h
deleted file mode 100644
index 5f68c78..0000000
--- a/src/rendering/texture.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include "core/memory.h"
-
-#include <string>
-#include <cstdint>
-#include <glm/glm.hpp>
-
-namespace Donut
-{
- class Texture
- {
- public:
- virtual ~Texture() = default;
-
- virtual auto get_width() const -> uint32_t = 0;
- virtual auto get_height() const -> uint32_t = 0;
- virtual auto get_renderer_id() const -> uint32_t = 0;
-
- virtual auto set_data(void* data, uint32_t size) -> void = 0;
- virtual auto bind(uint32_t slot = 0) const -> void = 0;
- virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void = 0;
-
- virtual auto operator==(const Texture& other) const -> bool = 0;
- };
-
- 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
- {
- 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
- };
-};
diff --git a/src/rendering/view.h b/src/rendering/view.h
new file mode 100644
index 0000000..0ac6ae0
--- /dev/null
+++ b/src/rendering/view.h
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace Donut
+{
+ // What the RenderPath draws in the viewport this frame. Each UI workspace
+ // (tab) declares which one it wants, so switching tabs switches the view.
+ enum class View
+ {
+ None, // empty viewport (tabs that aren't a live view, e.g. Setup / Export)
+ Scene, // world-builder: grid + placed objects + skybox
+ BlackHole, // the ray-traced accretion-disk simulation (only the Simulation tab)
+ };
+}