diff options
| author | hachem <im@hachem.wtf> | 2026-08-23 21:55:36 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-23 21:55:36 +0200 |
| commit | 5ea6c14c0e14bacee766ce1280ab2c71564e05ec (patch) | |
| tree | 34620051e8eaf65db4ff9cd40ffd982a03e1ccc8 /src/rendering | |
| parent | 8742482311b86bb705d93336805ab26881e96070 (diff) | |
[feat]: implement ui layer arch
Diffstat (limited to 'src/rendering')
| -rw-r--r-- | src/rendering/black_hole_renderer.h | 2 | ||||
| -rw-r--r-- | src/rendering/render_path.cpp | 94 | ||||
| -rw-r--r-- | src/rendering/render_path.h | 40 | ||||
| -rw-r--r-- | src/rendering/renderer_backend.h | 32 | ||||
| -rw-r--r-- | src/rendering/scene_renderer.h | 2 | ||||
| -rw-r--r-- | src/rendering/shader.cpp | 354 | ||||
| -rw-r--r-- | src/rendering/shader.h | 82 | ||||
| -rw-r--r-- | src/rendering/texture.cpp | 296 | ||||
| -rw-r--r-- | src/rendering/texture.h | 83 | ||||
| -rw-r--r-- | src/rendering/view.h | 13 |
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) + }; +} |
