diff options
| author | hachem <im@hachem.wtf> | 2026-08-23 17:21:37 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-23 17:21:37 +0200 |
| commit | 3fd33ecff7472e4d6fc9e6b3905f56982e497ef2 (patch) | |
| tree | 66e5812f1a18b926e88a0dd549fe14e7c7327165 /src/engine | |
| parent | e3aa33fc9a99d8b817bda42a567a4c347e18ab32 (diff) | |
[feat]: complete RHI api
Diffstat (limited to 'src/engine')
| -rw-r--r-- | src/engine/engine.cpp | 625 | ||||
| -rw-r--r-- | src/engine/engine.h | 191 | ||||
| -rw-r--r-- | src/engine/object.h | 76 | ||||
| -rw-r--r-- | src/engine/scene.h | 70 |
4 files changed, 0 insertions, 962 deletions
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp deleted file mode 100644 index bf5a2b6..0000000 --- a/src/engine/engine.cpp +++ /dev/null @@ -1,625 +0,0 @@ -#include <iostream> -#include <fstream> -#include <sstream> - -#include <GLFW/glfw3.h> -#include <glad/glad.h> - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include "stb_image_write.h" - -#include "engine.h" -#include "core/log.h" -#include "core/hdri_manager.h" -#include "rendering/vertex_buffer.h" -#include "rendering/index_buffer.h" - -namespace Donut -{ - Engine::Engine() - : m_sag_a(glm::vec3(0.0f, 0.0f, 0.0f), 8.54e36f) - { - m_width = 1280; - m_height = 720; - - m_camera.set_camera_mode(CameraMode::Orbital); - m_camera.set_orbital_radius(1e11); - m_camera.set_orbital_limits(1e9, 1e13); - m_camera.set_orbital_speed(0.01f); - m_camera.set_zoom_speed(1e10f); - - m_objects = - { - { glm::vec4(0.00f, 0.00f, 0.00f, m_sag_a.m_rs), glm::vec4(0, 0, 0, 1), static_cast<float>(m_sag_a.m_mass) } - }; - - // The geodesic ray tracer used to be a compute shader; it is now a - // fullscreen vertex+fragment pass (see dispatch_compute) so it runs on - // macOS OpenGL 4.1, which has no compute shaders. - m_compute_program = Ref<Shader>(Shader::create("assets/shaders/Geodesic.glsl")); - m_shader_program = Ref<Shader>(Shader::create("assets/shaders/TexturedQuad.glsl")); - m_blur_shader = Ref<Shader>(Shader::create("assets/shaders/Blur.glsl")); - - auto& hdri_manager = HDRIManager::get(); - m_hdri_environment = hdri_manager.get_current_hdri(); - if (!m_hdri_environment) - { - hdri_manager.set_current_hdri("assets/hdri/HDR_blue_nebulae-1.hdr"); - m_hdri_environment = hdri_manager.get_current_hdri(); - if (!m_hdri_environment) - DONUT_WARN("Failed to load default HDRI, using fallback"); - } - - m_camera_ubo = UniformBuffer::create(128, 1); - m_disk_ubo = UniformBuffer::create(sizeof(float) * 6, 2); // r1,r2,turbulence,thickness,brightness,temperature - - uint32_t obj_ubo_size = sizeof(int) + 3 * sizeof(float) - + 16 * (sizeof(glm::vec4) + sizeof(glm::vec4)) - + 16 * sizeof(float); - m_objects_ubo = UniformBuffer::create(obj_ubo_size, 3); - - m_simulation_ubo = UniformBuffer::create(sizeof(int) * 2 + sizeof(float) * 2, 4); - - auto result = QuadVAO(); - m_quad_vao = result.first; - m_texture = result.second; - - // GLSL 4.10 forbids explicit binding qualifiers on uniform blocks, so - // associate the geodesic shader's blocks with their UBO binding points - // from the host side instead. - if (m_compute_program) - { - uint32_t prog = m_compute_program->get_renderer_id(); - struct { const char* name; uint32_t point; } blocks[] = - { - { "Camera", 1 }, { "Disk", 2 }, { "Objects", 3 }, { "Simulation", 4 } - }; - for (const auto& b : blocks) - { - uint32_t idx = glGetUniformBlockIndex(prog, b.name); - if (idx != GL_INVALID_INDEX) - glUniformBlockBinding(prog, idx, b.point); - } - } - - glGenFramebuffers(1, &m_geodesic_fbo); - } - - auto Engine::update_window_dimensions() -> void - { - update_compute_dimensions(); - } - - auto Engine::set_window_dimensions(int width, int height) -> void - { - int old_width = m_width; - int old_height = m_height; - int old_compute_height = m_compute_height; - - m_width = width; - m_height = height; - - if (old_width != m_width || - old_height != m_height || - old_compute_height != m_compute_height) - update_compute_dimensions(); - } - - auto Engine::update_performance(float delta_time) -> void - { - if (delta_time > 0.0f) - m_current_fps = 1.0f / delta_time; - } - - auto Engine::update_compute_dimensions() -> void - { - m_texture = Texture2D::create(get_compute_width(), m_compute_height); - } - - auto Engine::draw_full_screen_quad() -> void - { - RenderCommand::set_viewport(0, 0, m_width, m_height); - - m_shader_program->bind(); - m_quad_vao->bind(); - - m_texture->bind(0); - m_shader_program->set_int("u_ScreenTexture", 0); - - RenderCommand::disable_depth_test(); - RenderCommand::draw_arrays(6); - RenderCommand::enable_depth_test(); - } - - auto Engine::draw_blur_pass() -> void - { - RenderCommand::set_viewport(0, 0, m_width, m_height); - - m_blur_shader->bind(); - m_quad_vao->bind(); - - m_texture->bind(0); - m_blur_shader->set_int("u_ScreenTexture", 0); - m_blur_shader->set_float2("u_Resolution", glm::vec2(m_width, m_height)); - m_blur_shader->set_float("u_BlurStrength", m_blur_strength); - m_blur_shader->set_float("u_GlowIntensity", m_glow_intensity); - - RenderCommand::disable_depth_test(); - RenderCommand::draw_arrays(6); - RenderCommand::enable_depth_test(); - } - - auto Engine::draw_geodesic_pass(int cw, int ch) -> void - { - m_quad_vao->bind(); - RenderCommand::disable_depth_test(); - -#ifdef __APPLE__ - // macOS aborts any GPU submission that runs longer than a couple of - // seconds ("GPU Hang"). The geodesic ray-marcher can far exceed that in - // a single fullscreen draw, so render it in scissored tiles and flush - // after each, keeping every submission short enough to survive the - // watchdog. Compute-capable platforms draw it in one pass. - // Largest tile that keeps a tile's worst-case work (tile^2 * step_cap) - // inside the safe watchdog zone measured on this GPU (~25M pixel-steps - // per submission); bigger tiles mean fewer glFinish stalls. - const int tile = 64; - glEnable(GL_SCISSOR_TEST); - for (int y = 0; y < ch; y += tile) - { - int th = std::min(tile, ch - y); - for (int x = 0; x < cw; x += tile) - { - int tw = std::min(tile, cw - x); - glScissor(x, y, tw, th); - RenderCommand::draw_arrays(6); - glFinish(); - } - } - glDisable(GL_SCISSOR_TEST); -#else - RenderCommand::draw_arrays(6); -#endif - - RenderCommand::enable_depth_test(); - } - - auto Engine::dispatch_compute(const Camera& cam) -> void - { - auto& hdri_manager = HDRIManager::get(); - m_hdri_environment = hdri_manager.get_current_hdri(); - - int cw = get_compute_width(); - int ch = m_compute_height; - - // Render the geodesic pass into m_texture through an FBO. This replaces - // the old compute dispatch + image_store path, which relied on OpenGL - // 4.3 compute and 4.2 image load/store that macOS does not provide. - glBindFramebuffer(GL_FRAMEBUFFER, m_geodesic_fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture->get_renderer_id(), 0); - glViewport(0, 0, cw, ch); - - m_compute_program->bind(); - upload_camera_ubo(cam); - upload_disk_ubo(); - upload_objects_ubo(m_objects); - upload_simulation_ubo(); - m_compute_program->set_float2("u_Resolution", glm::vec2(static_cast<float>(cw), static_cast<float>(ch))); - - if (m_hdri_environment) - { - m_hdri_environment->bind(5); - m_compute_program->set_int("u_HDRIEnvironment", 5); - } - - draw_geodesic_pass(cw, ch); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - auto Engine::upload_camera_ubo(const Camera& cam) -> void - { - struct UBOData - { - glm::vec3 pos; float _pad0; - glm::vec3 right; float _pad1; - glm::vec3 up; float _pad2; - glm::vec3 forward; float _pad3; - float tan_half_fov; - float aspect; - bool moving; - int _pad4; - } data; - - glm::vec3 pos, fwd; - if (cam.get_camera_mode() == CameraMode::FPS) - { - pos = cam.get_position(); - fwd = cam.get_forward_direction(); - } - else - { - pos = cam.get_orbital_position(); - fwd = glm::normalize(cam.get_orbital_target() - pos); - } - glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0))); - glm::vec3 up = glm::cross(right, fwd); - - data.pos = pos; - data.right = right; - data.up = up; - data.forward = fwd; - data.tan_half_fov = static_cast<float>(tan(glm::radians(m_bh.fov_degrees * 0.5f))); - data.aspect = static_cast<float>(get_compute_width()) / static_cast<float>(m_compute_height); - data.moving = cam.is_dragging() || cam.is_panning(); - - m_camera_ubo->set_data(&data, sizeof(UBOData)); - m_camera_ubo->bind(1); - } - - auto Engine::upload_objects_ubo(const std::vector<ObjectData>& objs) -> void - { - struct UBOData - { - int num_objects; - float _pad0, _pad1, _pad2; - glm::vec4 pos_radius[16]; - glm::vec4 color[16]; - float mass[16]; - } data; - - size_t count = std::min(objs.size(), size_t(16)); - data.num_objects = static_cast<int>(count); - - for (size_t i = 0; i < count; ++i) - { - data.pos_radius[i] = objs[i].m_pos_radius; - data.color[i] = objs[i].m_color; - data.mass[i] = objs[i].m_mass; - } - - m_objects_ubo->set_data(&data, sizeof(data)); - m_objects_ubo->bind(3); - } - - auto Engine::upload_disk_ubo() -> void - { - // Layout matches the shared Geodesic Disk struct (same as Vulkan). Radii - // are in Schwarzschild radii x the shader's SagA_rs constant (1.269e10). - const float rs = 1.269e10f; - float disk_data[6] = { - std::max(m_bh.disk_inner_rs, 3.0f) * rs, - std::max(m_bh.disk_outer_rs, m_bh.disk_inner_rs + 0.5f) * rs, - std::max(m_bh.turbulence, 0.0f), - rs * 0.1f, // slab half-thickness (fixed) - std::max(m_bh.brightness, 0.0f), - std::max(m_bh.temperature, 1000.0f), - }; - m_disk_ubo->set_data(disk_data, sizeof(disk_data)); - m_disk_ubo->bind(2); - } - - auto Engine::upload_simulation_ubo() -> void - { - struct UBOData - { - int max_steps_moving; - int max_steps_static; - float early_exit_distance; - float time; - } data; - - data.max_steps_moving = m_bh.quality_steps; - data.max_steps_static = m_bh.quality_steps; // resolution/tiling is the lever, not step count - data.early_exit_distance = m_early_exit_distance; - data.time = static_cast<float>(glfwGetTime()) * m_rotation_speed; - -#ifdef __APPLE__ - // macOS has no compute shaders, so the geodesic pass runs as a tiled - // fragment shader under the OS GPU watchdog. Cap the step count so a - // single 64px tile stays under the safe per-submission budget (~25M - // pixel-steps); Windows/Linux keep the full count. - data.max_steps_moving = std::min(data.max_steps_moving, 6000); - data.max_steps_static = std::min(data.max_steps_static, 6000); -#endif - - m_simulation_ubo->set_data(&data, sizeof(data)); - m_simulation_ubo->bind(4); - } - - auto Engine::update_physics(float delta_time) -> void - { - for (auto& obj : m_objects) - { - for (auto& obj2 : m_objects) - { - if (&obj == &obj2) continue; - float dx = obj2.m_pos_radius.x - obj.m_pos_radius.x; - float dy = obj2.m_pos_radius.y - obj.m_pos_radius.y; - float dz = obj2.m_pos_radius.z - obj.m_pos_radius.z; - float distance = sqrt(dx * dx + dy * dy + dz * dz); - if (distance > 0) - { - std::vector<double> direction = {dx / distance, dy / distance, dz / distance}; - double Gforce = (G * obj.m_mass * obj2.m_mass) / (distance * distance); - double acc1 = Gforce / obj.m_mass; - std::vector<double> acc = {direction[0] * acc1, direction[1] * acc1, direction[2] * acc1}; - - if (m_gravity) - { - obj.m_velocity.x += static_cast<float>(acc[0]); - obj.m_velocity.y += static_cast<float>(acc[1]); - obj.m_velocity.z += static_cast<float>(acc[2]); - - obj.m_pos_radius.x += static_cast<float>(obj.m_velocity.x); - obj.m_pos_radius.y += static_cast<float>(obj.m_velocity.y); - obj.m_pos_radius.z += static_cast<float>(obj.m_velocity.z); - } - } - } - } - } - - auto Engine::render_scene() -> void - { - RenderCommand::clear(); - m_shader_program->bind(); - m_quad_vao->bind(); - m_texture->bind(0); - RenderCommand::draw_arrays(6); - } - - auto Engine::create_compute_program(const char* path) -> Ref<Shader> - { - std::ifstream in(path); - if(!in.is_open()) - { - std::cerr << "Failed to open compute shader: " << path << "\n"; - return nullptr; - } - std::stringstream ss; - ss << in.rdbuf(); - std::string src_str = ss.str(); - return Ref<Shader>(Shader::create_compute("ComputeShader", src_str)); - } - - auto Engine::QuadVAO() -> std::pair<Ref<VertexArray>, Ref<Texture2D>> - { - float quad_vertices[] = - { - // Positions // TexCoords - -1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 0.0f, - 1.0f, -1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 1.0f, 1.0f - }; - - auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(quad_vertices, static_cast<uint32_t>(sizeof(quad_vertices)))); - VertexBufferLayout layout; - layout.push<float>(2); // Position (x, y) - layout.push<float>(2); // TexCoord (u, v) - vertex_buffer->set_layout(layout); - - auto vertex_array = Ref<VertexArray>(VertexArray::create()); - vertex_array->add_vertex_buffer(vertex_buffer); - auto texture = Texture2D::create(get_compute_width(), m_compute_height); - - return { vertex_array, texture }; - } - - auto Engine::load_objects_from_scene(const std::vector<Donut::Object>& objects) -> void - { - m_objects.clear(); - m_objects.push_back( - { - glm::vec4(0.00f, 0.00f, 0.00f, m_sag_a.m_rs), - glm::vec4(0, 0, 0, 1), - static_cast<float>(m_sag_a.m_mass) - }); - - for (const auto& obj : objects) - { - ObjectData engine_obj; - - float scale_factor = 1e10f; - engine_obj.m_pos_radius = glm::vec4 - ( - obj.m_centre.x * scale_factor, - obj.m_centre.y * scale_factor, - obj.m_centre.z * scale_factor, - obj.m_radius * scale_factor - ); - - engine_obj.m_color = glm::vec4(obj.m_material.m_color, 1.0f); - - float volume = (4.0f / 3.0f) * 3.14159f * engine_obj.m_pos_radius.w * engine_obj.m_pos_radius.w * engine_obj.m_pos_radius.w; - float density = 1e12f; - engine_obj.m_mass = volume * density; - engine_obj.m_velocity = glm::vec3(0.0f, 0.0f, 0.0f); - - m_objects.push_back(engine_obj); - } - - DONUT_INFO("Loaded {} objects from WorldBuilder scene (scaled up by {})", objects.size(), 1e10f); - print_object_info(); - } - - auto Engine::print_object_info() const -> void - { - DONUT_INFO("=== Object Information ==="); - DONUT_INFO("Total objects: {}", m_objects.size()); - - for (size_t i = 0; i < m_objects.size(); ++i) - { - const auto& obj = m_objects[i]; - DONUT_INFO("Object {}: Pos=({}, {}, {}), Radius={}, Mass={}, Color=({}, {}, {})", - i, - obj.m_pos_radius.x, obj.m_pos_radius.y, obj.m_pos_radius.z, - obj.m_pos_radius.w, - obj.m_mass, - obj.m_color.x, obj.m_color.y, obj.m_color.z - ); - } - DONUT_INFO("Camera position: ({}, {}, {})", - m_camera.get_orbital_position().x, - m_camera.get_orbital_position().y, - m_camera.get_orbital_position().z - ); - DONUT_INFO("Camera radius: {}", m_camera.get_orbital_radius()); - DONUT_INFO("========================"); - } - - auto Engine::export_high_res_frame(const std::string& filename, int width, int height) -> void - { - DONUT_INFO("Exporting high-resolution frame: {}x{} to {}", width, height, filename); - - if (width <= 0 || height <= 0) - { - DONUT_ERROR("Invalid dimensions for export: {}x{}", width, height); - return; - } - - if (filename.empty()) - { - DONUT_ERROR("Invalid filename for export"); - return; - } - - int original_width = m_width; - int original_height = m_height; - int original_compute_height = m_compute_height; - - m_width = width; - m_height = height; - m_compute_height = height; - - int compute_height = height; - int compute_width = (width * compute_height) / height; - - if (compute_width <= 0 || compute_height <= 0) - { - DONUT_ERROR("Invalid compute dimensions: {}x{}", compute_width, compute_height); - return; - } - - FramebufferSpecification fb_spec; - fb_spec.Width = width; - fb_spec.Height = height; - fb_spec.attachments = { FramebufferTextureFormat::RGBA8 }; - - auto high_res_framebuffer = Framebuffer::create(fb_spec); - if (!high_res_framebuffer) - { - DONUT_ERROR("Failed to create high-resolution framebuffer"); - return; - } - - auto high_res_texture = Texture2D::create(compute_width, compute_height); - if (!high_res_texture) - { - DONUT_ERROR("Failed to create high-resolution texture"); - return; - } - - // Render the geodesic pass into high_res_texture through the geodesic FBO. - glBindFramebuffer(GL_FRAMEBUFFER, m_geodesic_fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, high_res_texture->get_renderer_id(), 0); - glViewport(0, 0, compute_width, compute_height); - - m_compute_program->bind(); - - struct UBOData - { - glm::vec3 pos; float _pad0; - glm::vec3 right; float _pad1; - glm::vec3 up; float _pad2; - glm::vec3 forward; float _pad3; - float tan_half_fov; - float aspect; - bool moving; - int _pad4; - } data; - - glm::vec3 fwd = glm::normalize(m_camera.get_orbital_target() - m_camera.get_orbital_position()); - glm::vec3 up = glm::vec3(0, 1, 0); - glm::vec3 right = glm::normalize(glm::cross(fwd, up)); - up = glm::cross(right, fwd); - - data.pos = m_camera.get_orbital_position(); - data.right = right; - data.up = up; - data.forward = fwd; - data.tan_half_fov = static_cast<float>(tan(glm::radians(60.0f * 0.5f))); - data.aspect = static_cast<float>(compute_width) / static_cast<float>(compute_height); - data.moving = m_camera.is_dragging() || m_camera.is_panning(); - - m_camera_ubo->set_data(&data, sizeof(UBOData)); - m_camera_ubo->bind(1); - - upload_disk_ubo(); - upload_objects_ubo(m_objects); - upload_simulation_ubo(); - m_compute_program->set_float2("u_Resolution", glm::vec2(static_cast<float>(compute_width), static_cast<float>(compute_height))); - - if (m_hdri_environment) - { - m_hdri_environment->bind(5); - m_compute_program->set_int("u_HDRIEnvironment", 5); - } - - draw_geodesic_pass(compute_width, compute_height); - - // Display the rendered frame into the high-res framebuffer for read-back. - high_res_framebuffer->bind(); - RenderCommand::set_viewport(0, 0, width, height); - RenderCommand::clear(); - - m_shader_program->bind(); - m_quad_vao->bind(); - - high_res_texture->bind(0); - m_shader_program->set_int("u_ScreenTexture", 0); - - RenderCommand::disable_depth_test(); - RenderCommand::draw_arrays(6); - RenderCommand::enable_depth_test(); - - std::vector<unsigned char> pixels(width * height * 4); - DONUT_INFO("Reading {} pixels from framebuffer...", width * height); - RenderCommand::read_pixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); - - DONUT_INFO("Flipping image vertically..."); - std::vector<unsigned char> flipped_pixels(width * height * 4); - for (int y = 0; y < height; ++y) - { - for (int x = 0; x < width; ++x) - { - int src_index = (y * width + x) * 4; - int dst_index = ((height - 1 - y) * width + x) * 4; - flipped_pixels[dst_index + 0] = pixels[src_index + 0]; // R - flipped_pixels[dst_index + 1] = pixels[src_index + 1]; // G - flipped_pixels[dst_index + 2] = pixels[src_index + 2]; // B - flipped_pixels[dst_index + 3] = pixels[src_index + 3]; // A - } - } - - DONUT_INFO("Saving PNG file: {}...", filename); - int result = stbi_write_png(filename.c_str(), width, height, 4, flipped_pixels.data(), width * 4); - - if (result) - DONUT_INFO("Successfully exported high-resolution frame to: {}", filename); - else - DONUT_ERROR("Failed to export high-resolution frame to: {}", filename); - - high_res_framebuffer->unbind(); - - m_width = original_width; - m_height = original_height; - m_compute_height = original_compute_height; - - RenderCommand::set_viewport(0, 0, original_width, original_height); - } - -} diff --git a/src/engine/engine.h b/src/engine/engine.h deleted file mode 100644 index fef8b57..0000000 --- a/src/engine/engine.h +++ /dev/null @@ -1,191 +0,0 @@ -#pragma once - -#include <vector> -#include <numbers> -#include <iostream> -#include <fstream> -#include <sstream> -#include <chrono> -#include <cmath> - -#include "core/camera.h" -#include "object.h" - -#include "rendering/renderer.h" -#include "rendering/renderer_backend.h" -#include "rendering/shader.h" -#include "rendering/vertex_array.h" -#include "rendering/texture.h" -#include "rendering/uniform_buffer.h" -#include "rendering/texture_manager.h" - -#include <GLFW/glfw3.h> -#include <glm/glm.hpp> -#include <glm/gtc/matrix_transform.hpp> -#include <glm/gtc/type_ptr.hpp> - -namespace Donut -{ - const double c = 299792458.0; - const double G = 6.67430e-11; - - struct BlackHole - { - glm::vec3 m_position; - double m_mass; - double m_radius; - double m_rs; - - BlackHole(glm::vec3 pos, float mass) - : m_position(pos), m_mass(mass) - { - m_rs = 2.0 * G * m_mass / (c * c); - } - - bool intercept(float px, float py, float pz) const - { - double dx = double(px) - double(m_position.x); - double dy = double(py) - double(m_position.y); - double dz = double(pz) - double(m_position.z); - double dist2 = dx * dx + dy * dy + dz * dz; - return dist2 < m_rs * m_rs; - } - }; - - struct ObjectData - { - glm::vec4 m_pos_radius; - glm::vec4 m_color; - float m_mass; - glm::vec3 m_velocity = glm::vec3(0.0f, 0.0f, 0.0f); - }; - - class Engine - { - public: - Engine(); - ~Engine() = default; - - auto draw_full_screen_quad() -> void; - auto draw_blur_pass() -> void; - auto dispatch_compute(const Camera& cam) -> void; - auto upload_camera_ubo(const Camera& cam) -> void; - auto upload_objects_ubo(const std::vector<ObjectData>& objs) -> void; - auto upload_disk_ubo() -> void; - auto upload_simulation_ubo() -> void; - auto render_scene() -> void; - auto update_physics(float delta_time) -> void; - auto update_window_dimensions() -> void; - auto set_window_dimensions(int width, int height) -> void; - - auto get_width() const -> int { return m_width; } - auto get_height() const -> int { return m_height; } - - auto get_objects() -> std::vector<ObjectData>& { return m_objects; } - auto get_sag_a() -> BlackHole& { return m_sag_a; } - auto get_camera() -> Camera& { return m_camera; } - auto get_gravity() -> bool& { return m_gravity; } - - auto update_performance(float delta_time) -> void; - auto set_target_fps(int fps) -> void { m_target_fps = fps; } - auto get_target_fps() const -> int { return m_target_fps; } - auto get_current_fps() const -> float { return m_current_fps; } - void set_compute_height(int height) - { -#ifdef __APPLE__ - // Without compute shaders the geodesic pass runs as a tiled - // fragment shader (see draw_geodesic_pass), so very high working - // resolutions make each frame take many seconds. Cap it on macOS. - if (height > 256) height = 256; -#endif - m_compute_height = height; - } - auto get_compute_height() const -> int { return m_compute_height; } - auto get_compute_width() const -> int { return (m_width * m_compute_height) / m_height; } - auto update_compute_dimensions() -> void; - - auto get_max_steps_moving() const -> int { return m_max_steps_moving; } - auto get_max_steps_static() const -> int { return m_max_steps_static; } - auto get_early_exit_distance() const -> float { return m_early_exit_distance; } - auto set_max_steps_moving(int steps) -> void { m_max_steps_moving = steps; } - auto set_max_steps_static(int steps) -> void { m_max_steps_static = steps; } - auto set_early_exit_distance(float distance) -> void { m_early_exit_distance = distance; } - - auto get_disk_thickness() const -> float { return m_disk_thickness; } - auto set_disk_thickness(float thickness) -> void { m_disk_thickness = thickness; } - - auto get_disk_density() const -> float { return m_disk_density; } - auto set_disk_density(float density) -> void { m_disk_density = density; } - - auto get_rotation_speed() const -> float { return m_rotation_speed; } - auto set_rotation_speed(float speed) -> void { m_rotation_speed = speed; } - - auto get_blur_strength() const -> float { return m_blur_strength; } - auto set_blur_strength(float strength) -> void { m_blur_strength = strength; } - - auto get_glow_intensity() const -> float { return m_glow_intensity; } - auto set_glow_intensity(float intensity) -> void { m_glow_intensity = intensity; } - - auto load_objects_from_scene(const std::vector<Donut::Object>& objects) -> void; - auto export_high_res_frame(const std::string& filename, int width = 4096, int height = 3072) -> void; - auto print_object_info() const -> void; - - auto set_hdri_environment(Ref<CubemapTexture> hdri) -> void { m_hdri_environment = hdri; } - auto get_hdri_environment() const -> Ref<CubemapTexture> { return m_hdri_environment; } - - // Shared live black-hole/disk parameters (drives the geodesic UBOs so the - // GL path renders identically to Vulkan). The OpenGL backend mutates this. - auto black_hole_params() -> BlackHoleParams& { return m_bh; } - private: - auto create_compute_program(const char* path) -> Ref<Shader>; - std::pair<Ref<VertexArray>, Ref<Texture2D>> QuadVAO(); - - // Draws the bound geodesic shader over a cw x ch target. On macOS this - // is split into scissored tiles (with a flush each) so no single GPU - // submission trips the OS watchdog; elsewhere it is one fast draw. - auto draw_geodesic_pass(int cw, int ch) -> void; - private: - Ref<VertexArray> m_quad_vao; - Ref<Texture2D> m_texture; - Ref<CubemapTexture> m_hdri_environment; - Ref<Shader> m_shader_program; - Ref<Shader> m_compute_program; - Ref<Shader> m_blur_shader; - Ref<UniformBuffer> m_camera_ubo; - Ref<UniformBuffer> m_disk_ubo; - Ref<UniformBuffer> m_objects_ubo; - Ref<UniformBuffer> m_simulation_ubo; - - // FBO used to render the geodesic pass into m_texture. The geodesic - // shader is a fragment shader (macOS has no compute), so it draws a - // fullscreen quad into this framebuffer instead of dispatching compute. - uint32_t m_geodesic_fbo = 0; - - int m_width; - int m_height; - float m_width_f = 100.0f*1e10f; - float m_height_f = 75.0f*1e10f; - - int m_target_fps = 60; - float m_current_fps = 60.0f; - float m_last_frame_time = 0.0f; - int m_compute_height = 150; - - std::vector<ObjectData> m_objects; - BlackHole m_sag_a; - Camera m_camera; - bool m_gravity = false; - - int m_max_steps_moving = 60000; - int m_max_steps_static = 30000; - float m_early_exit_distance = 5.0e11f; - - BlackHoleParams m_bh; // shared UI-tunable disk/camera params (GL geodesic driver) - - float m_disk_thickness = 0.1f; - float m_disk_density = 0.1f; - float m_rotation_speed = 1.0f; - float m_blur_strength = 2.0f; - float m_glow_intensity = 0.1f; - }; -}; diff --git a/src/engine/object.h b/src/engine/object.h deleted file mode 100644 index 8101540..0000000 --- a/src/engine/object.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include <glm/glm.hpp> - -namespace Donut -{ - class Ray - { - public: - Ray(glm::vec3 o, glm::vec3 d) - : m_origin(o), - m_direction(glm::normalize(d)) { } - - public: - glm::vec3 m_direction; - glm::vec3 m_origin; - }; - - class Material - { - public: - Material() : m_color(1.0f, 1.0f, 1.0f), m_specular(0.5f), m_emission(0.0f) { } - Material(glm::vec3 c, float s, float e) - : m_color(c), - m_specular(s), - m_emission(e) { } - - public: - glm::vec3 m_color; - float m_specular; - float m_emission; - }; - - class Object - { - public: - Object() : m_centre(0.0f, 0.0f, 0.0f), m_radius(1.0f), m_material() { } - Object(glm::vec3 c, float r, Material m) - : m_centre(c), - m_radius(r), - m_material(m) { } - - auto intersect(Ray& ray, float& t) -> bool - { - glm::vec3 oc = ray.m_origin - m_centre; - float a = glm::dot(ray.m_direction, ray.m_direction); - float b = 2.0f * glm::dot(oc, ray.m_direction); - float c = glm::dot(oc, oc) - m_radius * m_radius; - float discriminant = static_cast<float>(b*b - 4*a*c); - - if (discriminant < 0) - return false; - - float intercept = (-b - sqrt(discriminant)) / (2.0f*a); - if (intercept < 0) - { - intercept = (-b + sqrt(discriminant)) / (2.0f*a); - if (intercept < 0) - return false; - } - - t = intercept; - return true; - } - - auto get_normal(glm::vec3& point) const -> glm::vec3 - { - return glm::normalize(point - m_centre); - } - - public: - glm::vec3 m_centre; - float m_radius; - Material m_material; - }; -}; diff --git a/src/engine/scene.h b/src/engine/scene.h deleted file mode 100644 index 4b41203..0000000 --- a/src/engine/scene.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include <vector> -#include <limits> - -#include "object.h" - -namespace Donut -{ - class Scene - { - public: - Scene() - : m_light_pos(5.0f, 5.0f, 5.0f) { } - - auto trace(Ray& ray) -> glm::vec3 - { - float closest = std::numeric_limits<float>::infinity(); - const Object* hit_obj = nullptr; - - for (auto& obj : objs) - { - float t; - - if (obj.intersect(ray, t)) - if (t < closest) - { - closest = t; - hit_obj = &obj; - } - } - - if (hit_obj) - { - glm::vec3 hit_point = ray.m_origin + ray.m_direction * closest; - glm::vec3 normal = hit_obj->get_normal(hit_point); - glm::vec3 light_dir = glm::normalize(m_light_pos - hit_point); - - float diff = std::max(glm::dot(normal, light_dir), 0.0f); - - Ray shadow_ray(hit_point + normal * 0.001f, light_dir); - bool in_shadow = false; - - for (auto& obj : objs) - { - float t; - - if (obj.intersect(shadow_ray, t)) - { - in_shadow = true; - break; - } - } - - glm::vec3 color = hit_obj->m_material.m_color; - float ambient = 0.1f; - - if (in_shadow) - return color * ambient; - return color * (ambient + diff * 0.9f); - } - - return glm::vec3(0.0f, 0.0f, 0.1f); - } - - public: - std::vector<Object> objs; - glm::vec3 m_light_pos; - }; -}; |
