#include "render_path.h" #include "scene/scene.h" #include #include #include 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(); m_scene_renderer->init(device); m_black_hole_renderer = create_scope(); 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). } }