hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/state.h19
-rw-r--r--src/core/state_manager.cpp88
-rw-r--r--src/core/state_manager.h41
-rw-r--r--src/engine/engine.cpp599
-rw-r--r--src/engine/engine.h180
-rw-r--r--src/engine/object.h76
-rw-r--r--src/engine/scene.h70
-rw-r--r--src/platform/metal/metal_context.h8
-rw-r--r--src/platform/metal/metal_context.mm119
-rw-r--r--src/states/config_state.cpp307
-rw-r--r--src/states/config_state.h47
-rw-r--r--src/states/simulation_state.cpp386
-rw-r--r--src/states/simulation_state.h27
-rw-r--r--src/states/world_builder_state.cpp1110
-rw-r--r--src/states/world_builder_state.h96
15 files changed, 0 insertions, 3173 deletions
diff --git a/src/core/state.h b/src/core/state.h
deleted file mode 100644
index 623f1c2..0000000
--- a/src/core/state.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "event.h"
-
-namespace Donut
-{
- class State
- {
- public:
- virtual ~State() = default;
-
- virtual auto on_enter() -> void = 0;
- virtual auto on_exit() -> void = 0;
- virtual auto on_update(float delta_time) -> void = 0;
- virtual auto on_render() -> void = 0;
- virtual auto on_im_ui_render() -> void = 0;
- virtual auto on_event(Event& event) -> void = 0;
- };
-};
diff --git a/src/core/state_manager.cpp b/src/core/state_manager.cpp
deleted file mode 100644
index 8018401..0000000
--- a/src/core/state_manager.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "state_manager.h"
-#include "log.h"
-
-namespace Donut
-{
- auto StateManager::shutdown() -> void
- {
- if (m_current_state)
- m_current_state->on_exit();
-
- destroy_states();
- DONUT_INFO("StateManager shutdown");
- }
-
- auto StateManager::destroy_states() -> void
- {
- m_states.clear();
- m_current_state = nullptr;
- m_current_state_name = "";
- }
-
- auto StateManager::register_state(const std::string& state_name, Scope<State> state) -> void
- {
- if (m_states.find(state_name) != m_states.end())
- DONUT_WARN("State '{}' already exists, overwriting", state_name);
- m_states[state_name] = std::move(state);
- DONUT_INFO("Registered state: {}", state_name);
- }
-
- auto StateManager::switch_to_state(const std::string& state_name) -> void
- {
- auto it = m_states.find(state_name);
- if (it == m_states.end())
- {
- DONUT_ERROR("Attempted to switch to unknown state: {}", state_name);
- return;
- }
-
- State* new_state = it->second.get();
- if (new_state == m_current_state)
- return;
-
- if (m_current_state)
- m_current_state->on_exit();
-
- m_current_state = new_state;
- m_current_state_name = state_name;
- m_current_state->on_enter();
- DONUT_INFO("Switched to state: {}", state_name);
- }
-
- auto StateManager::get_state(const std::string& state_name) -> State*
- {
- auto it = m_states.find(state_name);
- if (it != m_states.end())
- return it->second.get();
- return nullptr;
- }
-
- auto StateManager::has_state(const std::string& state_name) const -> bool
- {
- return m_states.find(state_name) != m_states.end();
- }
-
- auto StateManager::update(float delta_time) -> void
- {
- if (m_current_state)
- m_current_state->on_update(delta_time);
- }
-
- auto StateManager::render() -> void
- {
- if (m_current_state)
- m_current_state->on_render();
- }
-
- auto StateManager::on_im_ui_render() -> void
- {
- if (m_current_state)
- m_current_state->on_im_ui_render();
- }
-
- auto StateManager::on_event(Event& event) -> void
- {
- if (m_current_state)
- m_current_state->on_event(event);
- }
-}
diff --git a/src/core/state_manager.h b/src/core/state_manager.h
deleted file mode 100644
index 446841e..0000000
--- a/src/core/state_manager.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "memory.h"
-#include "state.h"
-
-#include <string>
-#include <unordered_map>
-
-namespace Donut
-{
- class StateManager
- {
- public:
- ~StateManager() = default;
- auto shutdown() -> void;
-
- auto update(float delta_time) -> void;
- auto render() -> void;
- auto on_im_ui_render() -> void;
- auto on_event(Event& event) -> void;
-
- auto register_state(const std::string& state_name, Scope<State> state) -> void;
- auto switch_to_state(const std::string& state_name) -> void;
-
- auto get_current_state_name() const -> std::string { return m_current_state_name; }
- auto get_current_state() const -> State* { return m_current_state; }
-
- auto get_state(const std::string& state_name) -> State*;
- auto has_state(const std::string& state_name) const -> bool;
-
- private:
- auto create_states() -> void;
- auto destroy_states() -> void;
-
- private:
- State* m_current_state = nullptr;
- std::string m_current_state_name = "";
-
- std::unordered_map<std::string, Scope<State>> m_states;
- };
-}
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
deleted file mode 100644
index 496dd56..0000000
--- a/src/engine/engine.cpp
+++ /dev/null
@@ -1,599 +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) }
- };
-
- m_compute_program = Ref<Shader>(Shader::create("assets/shaders/geodesic.glsl"));
- m_shader_program = Ref<Shader>(Shader::create("assets/shaders/textured_quad.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);
-
- 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 = quad_vao();
- m_quad_vao = result.first;
- m_texture = result.second;
-
- 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__
- 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;
-
- 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
- {
- 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,
- 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;
- data.early_exit_distance = m_early_exit_distance;
- data.time = static_cast<float>(glfwGetTime()) * m_rotation_speed;
-
-#ifdef __APPLE__
- 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::quad_vao() -> std::pair<Ref<VertexArray>, Ref<Texture2D>>
- {
- float quad_vertices[] =
- {
- -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);
- layout.push<float>(2);
- 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;
- }
-
- 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);
-
- 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];
- flipped_pixels[dst_index + 1] = pixels[src_index + 1];
- flipped_pixels[dst_index + 2] = pixels[src_index + 2];
- flipped_pixels[dst_index + 3] = pixels[src_index + 3];
- }
- }
-
- 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 7bab179..0000000
--- a/src/engine/engine.h
+++ /dev/null
@@ -1,180 +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__
- 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; }
-
- auto black_hole_params() -> BlackHoleParams& { return m_bh; }
- private:
- auto create_compute_program(const char* path) -> Ref<Shader>;
- std::pair<Ref<VertexArray>, Ref<Texture2D>> quad_vao();
-
- 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;
-
- 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;
-
- 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;
- };
-};
diff --git a/src/platform/metal/metal_context.h b/src/platform/metal/metal_context.h
deleted file mode 100644
index 7a206ea..0000000
--- a/src/platform/metal/metal_context.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-namespace Donut
-{
- auto metal_probe() -> bool;
-
- auto metal_compute_self_test() -> bool;
-}
diff --git a/src/platform/metal/metal_context.mm b/src/platform/metal/metal_context.mm
deleted file mode 100644
index a616865..0000000
--- a/src/platform/metal/metal_context.mm
+++ /dev/null
@@ -1,119 +0,0 @@
-#import <Metal/Metal.h>
-#import <Foundation/Foundation.h>
-
-#include "metal_context.h"
-#include "core/log.h"
-
-#include <vector>
-
-namespace Donut
-{
- bool metal_probe()
- {
- @autoreleasepool
- {
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
- if (device == nil)
- {
- DONUT_ERROR("Metal: no default device available");
- return false;
- }
-
- id<MTLCommandQueue> queue = [device newCommandQueue];
- const char* name = [[device name] UTF8String];
-
- DONUT_INFO("Metal device: {}", name ? name : "(unknown)");
- DONUT_INFO("Metal: unified memory = {}, max threads/threadgroup = {}",
- device.hasUnifiedMemory ? "yes" : "no",
- (unsigned long)device.maxThreadsPerThreadgroup.width);
-
- if (queue == nil)
- {
- DONUT_WARN("Metal: failed to create command queue");
- return false;
- }
-
- return true;
- }
- }
-
- bool metal_compute_self_test()
- {
- @autoreleasepool
- {
- id<MTLDevice> device = MTLCreateSystemDefaultDevice();
- id<MTLCommandQueue> queue = [device newCommandQueue];
- if (device == nil || queue == nil)
- return false;
-
- NSString* src =
- @"#include <metal_stdlib>\n"
- "using namespace metal;\n"
- "kernel void selfTest(texture2d<float, access::write> outTex [[texture(0)]],\n"
- " uint2 gid [[thread_position_in_grid]])\n"
- "{\n"
- " uint w = outTex.get_width();\n"
- " uint h = outTex.get_height();\n"
- " if (gid.x >= w || gid.y >= h) return;\n"
- " outTex.write(float4(float(gid.x) / float(w - 1),\n"
- " float(gid.y) / float(h - 1), 0.5, 1.0), gid);\n"
- "}\n";
-
- NSError* err = nil;
- id<MTLLibrary> lib = [device newLibraryWithSource:src options:nil error:&err];
- if (lib == nil)
- {
- DONUT_ERROR("Metal self-test: kernel compile failed: {}",
- err ? [[err localizedDescription] UTF8String] : "unknown");
- return false;
- }
-
- id<MTLFunction> fn = [lib newFunctionWithName:@"selfTest"];
- id<MTLComputePipelineState> pipeline =
- [device newComputePipelineStateWithFunction:fn error:&err];
- if (pipeline == nil)
- {
- DONUT_ERROR("Metal self-test: pipeline creation failed");
- return false;
- }
-
- const uint32_t W = 64, H = 64;
- MTLTextureDescriptor* desc =
- [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
- width:W
- height:H
- mipmapped:NO];
- desc.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead;
- desc.storageMode = MTLStorageModeShared;
- id<MTLTexture> tex = [device newTextureWithDescriptor:desc];
-
- id<MTLCommandBuffer> cb = [queue commandBuffer];
- id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder];
- [enc setComputePipelineState:pipeline];
- [enc setTexture:tex atIndex:0];
-
- MTLSize tg = MTLSizeMake(16, 16, 1);
- MTLSize grid = MTLSizeMake(W, H, 1);
- [enc dispatchThreads:grid threadsPerThreadgroup:tg];
- [enc endEncoding];
- [cb commit];
- [cb waitUntilCompleted];
-
- std::vector<uint8_t> px(W * H * 4);
- [tex getBytes:px.data()
- bytesPerRow:W * 4
- fromRegion:MTLRegionMake2D(0, 0, W, H)
- mipmapLevel:0];
-
- size_t corner = ((size_t)(H - 1) * W + (W - 1)) * 4;
- DONUT_INFO("Metal compute self-test: corner pixel RGBA = ({}, {}, {}, {})",
- (int)px[corner + 0], (int)px[corner + 1],
- (int)px[corner + 2], (int)px[corner + 3]);
-
- bool ok = px[corner + 0] > 250 && px[corner + 1] > 250 &&
- px[corner + 3] == 255;
- DONUT_INFO("Metal compute self-test: {}", ok ? "PASS" : "FAIL");
- return ok;
- }
- }
-}
diff --git a/src/states/config_state.cpp b/src/states/config_state.cpp
deleted file mode 100644
index d5fca87..0000000
--- a/src/states/config_state.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-#include "config_state.h"
-#include "rendering/renderer.h"
-#include "core/application.h"
-#include "core/theme_manager.h"
-#include "core/settings_manager.h"
-
-#include <imgui.h>
-#include <imgui_internal.h>
-#include <glad/glad.h>
-
-namespace Donut
-{
- auto ConfigState::on_enter() -> void
- {
- DONUT_INFO("Entering Config State");
-
- const auto& settings = SettingsManager::get_settings_const();
-
- m_selected_api = (settings.graphics.render_api == "Vulkan") ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL;
- m_selected_theme = (settings.graphics.selected_theme == "Light") ? 1 :
- (settings.graphics.selected_theme == "Blue") ? 2 : 0;
- m_target_fps = settings.simulation.target_fps;
- m_compute_height = settings.simulation.compute_height;
- m_max_steps_moving = settings.simulation.max_steps_moving;
- m_max_steps_static = settings.simulation.max_steps_static;
- m_early_exit_distance = settings.simulation.early_exit_distance;
- m_gravity_enabled = settings.simulation.gravity_enabled;
- m_v_sync_enabled = settings.graphics.v_sync_enabled;
- m_show_fps = settings.graphics.show_fps;
- m_show_performance_metrics = settings.graphics.show_performance_metrics;
- m_show_debug_info = settings.graphics.show_debug_info;
- m_enable_anti_aliasing = settings.graphics.enable_anti_aliasing;
- }
-
- auto ConfigState::on_exit() -> void
- {
- DONUT_INFO("Exiting Config State");
- }
-
- auto ConfigState::on_update(float delta_time) -> void
- {
- if (m_show_restart_message)
- {
- m_restart_message_timer += delta_time;
- if (m_restart_message_timer > 3.0f)
- {
- m_show_restart_message = false;
- m_restart_message_timer = 0.0f;
- }
- }
- }
-
- auto ConfigState::on_render() -> void
- {
- Renderer::set_clear_color({ 0.1f, 0.1f, 0.1f, 1.0f });
- Renderer::clear();
- }
-
- auto ConfigState::on_event(Event& event) -> void
- {
- }
-
- auto ConfigState::on_im_ui_render() -> void
- {
- ImGui::SetNextWindowSize(ImVec2(900, 700), ImGuiCond_FirstUseEver);
- ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f),
- ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
-
- ImGui::Begin("Donut Configuration", nullptr, ImGuiWindowFlags_NoCollapse);
-
- ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]);
- ImGui::TextColored(ImVec4(0.8f, 0.8f, 1.0f, 1.0f), "Donut Configuration");
- ImGui::PopFont();
- ImGui::Separator();
-
- ImGui::Columns(2, "ConfigColumns", true);
-
- float available_height = ImGui::GetWindowHeight() - 140;
-
- ImGui::BeginChild("GraphicsSettings", ImVec2(0, available_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Graphics Settings");
- ImGui::Separator();
-
- ImGui::Text("Render API:");
- ImGui::SameLine();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "(requires restart)");
-
- const char* api_names[] = { "OpenGL", "Vulkan" };
- static int current_api = (int)m_selected_api - 1;
-
- if (ImGui::BeginCombo("##RenderAPI", api_names[current_api]))
- {
- for (int i = 0; i < IM_ARRAYSIZE(api_names); i++)
- {
- const bool is_selected = (current_api == i);
- if (ImGui::Selectable(api_names[i], is_selected))
- {
- current_api = i;
- m_selected_api = (RendererAPI::API)(i + 1);
- m_show_restart_message = true;
- m_restart_message_timer = 0.0f;
- }
-
- if (is_selected)
- ImGui::SetItemDefaultFocus();
- }
-
- ImGui::EndCombo();
- }
-
- ImGui::Text("Current API: ");
- ImGui::SameLine();
- const char* current_api_name = (Renderer::get_api() == RendererAPI::API::OpenGL) ? "OpenGL" : "Vulkan";
- ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), current_api_name);
-
- ImGui::Spacing();
-
- ImGui::Checkbox("Enable VSync", &m_v_sync_enabled);
- ImGui::SameLine();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "(recommended)");
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Display");
- ImGui::Separator();
-
- ImGui::Text("Window Size: 1280x720");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Fullscreen: Not implemented yet");
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "System Info");
- ImGui::Separator();
-
- ImGui::Text("OpenGL Version: %s", glGetString(GL_VERSION));
- ImGui::Text("GPU: %s", glGetString(GL_RENDERER));
- ImGui::Text("Vendor: %s", glGetString(GL_VENDOR));
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Performance");
- ImGui::Separator();
-
- ImGui::SliderInt("Target FPS", &m_target_fps, 30, 120, "%d FPS");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Target frame rate for the simulation");
-
- ImGui::Checkbox("Show FPS Counter", &m_show_fps);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Display current FPS in the simulation");
-
- ImGui::Checkbox("Show Performance Metrics", &m_show_performance_metrics);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Show detailed performance information");
-
- ImGui::Checkbox("Show Debug Info", &m_show_debug_info);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Display debug information and statistics");
-
- ImGui::Checkbox("Enable Anti-Aliasing", &m_enable_anti_aliasing);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Enable anti-aliasing for smoother rendering");
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Theme");
- ImGui::Separator();
-
- const char* theme_names[] = { "Dark", "Light", "Blue" };
- if (ImGui::Combo("UI Theme", &m_selected_theme, theme_names, IM_ARRAYSIZE(theme_names)))
- ThemeManager::set_theme(static_cast<Theme>(m_selected_theme));
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Choose the application theme");
-
- ImGui::EndChild();
-
- ImGui::NextColumn();
-
- ImGui::BeginChild("SimulationSettings", ImVec2(0, available_height), true, ImGuiWindowFlags_AlwaysVerticalScrollbar);
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Simulation Settings");
- ImGui::Separator();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Quality");
- ImGui::Separator();
-
- ImGui::SliderInt("Compute Height", &m_compute_height, 64, 2048, "%d px");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Resolution of the compute shader. Higher values give better quality but lower performance.");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Higher = better quality, lower performance");
-
- ImGui::SliderInt("Max Steps (Moving)", &m_max_steps_moving, 1000, 60000, "%d");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Maximum ray marching steps when camera is moving");
-
- ImGui::SliderInt("Max Steps (Static)", &m_max_steps_static, 1000, 30000, "%d");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Maximum ray marching steps when camera is stationary");
-
- ImGui::SliderFloat("Early Exit Distance", &m_early_exit_distance, 1e11f, 1e13f, "%.2e");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Distance at which ray marching stops to improve performance");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Distance at which ray marching stops");
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Physics");
- ImGui::Separator();
-
- ImGui::Checkbox("Enable Gravity", &m_gravity_enabled);
-
- ImGui::EndChild();
-
- ImGui::Columns(1);
-
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::Spacing();
-
- float button_width = (ImGui::GetWindowWidth() - 120) / 5.0f;
-
- if (ImGui::Button("World Builder", ImVec2(button_width, 35)))
- {
- apply_settings();
- Application::get().get_state_manager().switch_to_state("WorldBuilder");
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Reset to Defaults", ImVec2(button_width, 35)))
- reset_to_defaults();
-
- ImGui::SameLine();
- if (ImGui::Button("Apply Settings", ImVec2(button_width, 35)))
- apply_settings();
-
- ImGui::SameLine();
- if (ImGui::Button("Save Settings", ImVec2(button_width, 35)))
- {
- apply_settings();
- DONUT_INFO("Settings saved manually");
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Exit", ImVec2(button_width, 35)))
- Application::get().close();
-
- if (m_show_restart_message)
- {
- ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
- ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
- ImGui::TextWrapped("Warning: Render API changed! Please restart the application for changes to take effect.");
- ImGui::PopStyleColor();
- }
-
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Donut Engine v1.0.0 | Black Hole Simulation");
-
- ImGui::End();
- }
-
- auto ConfigState::apply_settings() -> void
- {
- SimulationSettings sim_settings;
- sim_settings.target_fps = m_target_fps;
- sim_settings.compute_height = m_compute_height;
- sim_settings.max_steps_moving = m_max_steps_moving;
- sim_settings.max_steps_static = m_max_steps_static;
- sim_settings.early_exit_distance = m_early_exit_distance;
- sim_settings.gravity_enabled = m_gravity_enabled;
-
- GraphicsSettings gfx_settings;
- gfx_settings.render_api = (m_selected_api == RendererAPI::API::Vulkan) ? "Vulkan" : "OpenGL";
- gfx_settings.v_sync_enabled = m_v_sync_enabled;
- gfx_settings.show_fps = m_show_fps;
- gfx_settings.show_performance_metrics = m_show_performance_metrics;
- gfx_settings.show_debug_info = m_show_debug_info;
- gfx_settings.enable_anti_aliasing = m_enable_anti_aliasing;
- gfx_settings.selected_theme = (m_selected_theme == 1) ? "Light" :
- (m_selected_theme == 2) ? "Blue" : "Dark";
-
- SettingsManager::set_simulation_settings(sim_settings);
- SettingsManager::set_graphics_settings(gfx_settings);
-
- RendererAPI::set_api(m_selected_api);
- DONUT_INFO("Settings applied and saved");
- }
-
- auto ConfigState::reset_to_defaults() -> void
- {
- m_selected_api = RendererAPI::API::OpenGL;
- m_target_fps = 60;
- m_compute_height = 512;
- m_max_steps_moving = 30000;
- m_max_steps_static = 15000;
- m_early_exit_distance = 5e12f;
- m_gravity_enabled = true;
- m_v_sync_enabled = true;
- m_show_fps = true;
- m_show_performance_metrics = true;
- m_show_debug_info = false;
- m_enable_anti_aliasing = true;
- m_selected_theme = 0;
-
- apply_settings();
- DONUT_INFO("Settings reset to defaults and saved");
- }
-};
diff --git a/src/states/config_state.h b/src/states/config_state.h
deleted file mode 100644
index c723daa..0000000
--- a/src/states/config_state.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "core/state.h"
-#include "core/event.h"
-#include "core/log.h"
-#include "rendering/renderer.h"
-
-namespace Donut
-{
- class ConfigState
- : public State
- {
- public:
- ~ConfigState() = default;
-
- auto on_enter() -> void override;
- auto on_exit() -> void override;
- auto on_update(float delta_time) -> void override;
- auto on_render() -> void override;
- auto on_im_ui_render() -> void override;
- auto on_event(Event& event) -> void override;
-
- private:
- auto apply_settings() -> void;
- auto reset_to_defaults() -> void;
-
- private:
- RendererAPI::API m_selected_api = RendererAPI::API::OpenGL;
- bool m_show_restart_message = false;
- float m_restart_message_timer = 0.0f;
-
- int m_target_fps = 60;
- int m_compute_height = 512;
- int m_max_steps_moving = 30000;
- int m_max_steps_static = 15000;
- float m_early_exit_distance = 5e12f;
- bool m_gravity_enabled = true;
-
- bool m_v_sync_enabled = true;
- bool m_show_fps = true;
- bool m_show_performance_metrics = true;
- bool m_show_debug_info = false;
- bool m_enable_anti_aliasing = true;
-
- int m_selected_theme = 0;
- };
-};
diff --git a/src/states/simulation_state.cpp b/src/states/simulation_state.cpp
deleted file mode 100644
index 06955ff..0000000
--- a/src/states/simulation_state.cpp
+++ /dev/null
@@ -1,386 +0,0 @@
-#include "simulation_state.h"
-#include "rendering/renderer.h"
-#include "core/application.h"
-#include "core/hdri_manager.h"
-#include "core/window.h"
-#include "core/event.h"
-#include "core/settings_manager.h"
-
-#include <imgui.h>
-#include <GLFW/glfw3.h>
-#include <chrono>
-#include <iomanip>
-#include <sstream>
-#include <vector>
-
-namespace Donut
-{
- auto SimulationState::on_enter() -> void
- {
- DONUT_INFO("Entering Simulation State");
-
- const auto& settings = SettingsManager::get_settings_const();
- auto& engine = Application::get().get_engine();
-
- engine.set_target_fps(settings.simulation.target_fps);
- engine.set_compute_height(settings.simulation.compute_height);
- engine.set_max_steps_moving(settings.simulation.max_steps_moving);
- engine.set_max_steps_static(settings.simulation.max_steps_static);
- engine.set_early_exit_distance(settings.simulation.early_exit_distance);
- engine.get_gravity() = settings.simulation.gravity_enabled;
-
- engine.set_disk_thickness(settings.simulation.disk_thickness);
- engine.set_disk_density(settings.simulation.disk_density);
- engine.set_rotation_speed(settings.simulation.rotation_speed);
- engine.set_blur_strength(settings.simulation.blur_strength);
- engine.set_glow_intensity(settings.simulation.glow_intensity);
-
- engine.update_compute_dimensions();
- m_initialized = true;
- }
-
- auto SimulationState::on_exit() -> void
- {
- DONUT_INFO("Exiting Simulation State");
- }
-
- auto SimulationState::on_update(float delta_time) -> void
- {
- auto& engine = Application::get().get_engine();
- engine.update_performance(delta_time);
- engine.update_window_dimensions();
- engine.update_physics(delta_time);
-
- if (engine.get_camera().is_dragging())
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
- engine.get_camera().process_orbital_mouse_move(xpos, ypos);
- }
- }
-
- auto SimulationState::on_render() -> void
- {
- auto& engine = Application::get().get_engine();
- RenderCommand::set_clear_color(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
- RenderCommand::clear();
- RenderCommand::set_viewport(0, 0, static_cast<uint32_t>(engine.get_width()), static_cast<uint32_t>(engine.get_height()));
-
- engine.dispatch_compute(engine.get_camera());
- engine.draw_blur_pass();
- }
-
- auto SimulationState::on_event(Event& event) -> void
- {
- auto& engine = Application::get().get_engine();
-
- if (event.get_event_type() == EventType::MouseButtonPressed)
- {
- MouseButtonPressedEvent& e = (MouseButtonPressedEvent&)event;
- int button = e.get_mouse_button();
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- Camera& camera = engine.get_camera();
- double last_x = camera.get_last_x();
- double last_y = camera.get_last_y();
- glfwGetCursorPos(window, &last_x, &last_y);
-
- camera.process_orbital_mouse_button(button, GLFW_PRESS, 0);
- }
- else if (event.get_event_type() == EventType::MouseButtonReleased)
- {
- MouseButtonReleasedEvent& e = (MouseButtonReleasedEvent&)event;
- int button = e.get_mouse_button();
- engine.get_camera().process_orbital_mouse_button(button, GLFW_RELEASE, 0);
- }
-
- if (event.get_event_type() == EventType::MouseScrolled)
- {
- MouseScrolledEvent& e = (MouseScrolledEvent&)event;
- engine.get_camera().process_orbital_scroll(e.get_x_offset(), e.get_y_offset());
- }
-
- if (event.get_event_type() == EventType::KeyPressed)
- {
- KeyPressedEvent& e = (KeyPressedEvent&)event;
- if (e.get_key_code() == GLFW_KEY_G)
- {
- engine.get_gravity() = !engine.get_gravity();
- DONUT_INFO("Gravity turned {}", engine.get_gravity() ? "ON" : "OFF");
- }
- }
- }
-
- auto SimulationState::on_im_ui_render() -> void
- {
- auto& engine = Application::get().get_engine();
-
- ImGui::SetNextWindowSize(ImVec2(400, 600), ImGuiCond_FirstUseEver);
- ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - 420, 20), ImGuiCond_FirstUseEver);
-
- ImGui::Begin("Simulation Controls", nullptr, ImGuiWindowFlags_NoCollapse);
-
- ImGui::TextColored(ImVec4(0.8f, 0.8f, 1.0f, 1.0f), "Black Hole Simulation");
- ImGui::SameLine();
- if (ImGui::Button("Back to Config"))
- Application::get().get_state_manager().switch_to_state("Config");
- ImGui::SameLine();
- if (ImGui::Button("Save Settings"))
- {
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.target_fps = engine.get_target_fps();
- settings.compute_height = engine.get_compute_height();
- settings.max_steps_moving = engine.get_max_steps_moving();
- settings.max_steps_static = engine.get_max_steps_static();
- settings.early_exit_distance = engine.get_early_exit_distance();
- settings.gravity_enabled = engine.get_gravity();
- SettingsManager::set_simulation_settings(settings);
- DONUT_INFO("Simulation settings saved manually");
- }
-
- ImGui::Separator();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Performance");
- ImGui::Separator();
-
- ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
- ImGui::Text("Frame Time: %.3f ms", 1000.0f / ImGui::GetIO().Framerate);
- ImGui::Text("Engine FPS: %.1f", engine.get_current_fps());
-
- int target_fps = engine.get_target_fps();
- if (ImGui::SliderInt("Target FPS", &target_fps, 30, 120))
- {
- engine.set_target_fps(target_fps);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.target_fps = target_fps;
- SettingsManager::set_simulation_settings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Simulation Info");
- ImGui::Separator();
-
- ImGui::Text("Resolution: %dx%d", engine.get_width(), engine.get_height());
- ImGui::Text("Compute Resolution: %dx%d", engine.get_compute_width(), engine.get_compute_height());
- ImGui::Text("Objects: %zu", engine.get_objects().size());
-
- if (ImGui::Button("Print Object Info"))
- engine.print_object_info();
-
- int compute_height = engine.get_compute_height();
- if (ImGui::SliderInt("Compute Height", &compute_height, 64, 2048))
- {
- engine.set_compute_height(compute_height);
- engine.update_compute_dimensions();
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.compute_height = compute_height;
- SettingsManager::set_simulation_settings(settings);
- }
-
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Compute Width: %d (auto-calculated)", engine.get_compute_width());
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Quality Settings");
- ImGui::Separator();
-
- int max_steps_moving = engine.get_max_steps_moving();
- if (ImGui::SliderInt("Max Steps (Moving)", &max_steps_moving, 1000, 60000))
- {
- engine.set_max_steps_moving(max_steps_moving);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.max_steps_moving = max_steps_moving;
- SettingsManager::set_simulation_settings(settings);
- }
- int max_steps_static = engine.get_max_steps_static();
- if (ImGui::SliderInt("Max Steps (Static)", &max_steps_static, 1000, 30000))
- {
- engine.set_max_steps_static(max_steps_static);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.max_steps_static = max_steps_static;
- SettingsManager::set_simulation_settings(settings);
- }
- float early_exit_distance = engine.get_early_exit_distance();
- if (ImGui::SliderFloat("Early Exit Distance", &early_exit_distance, 1e11f, 1e13f, "%.2e"))
- {
- engine.set_early_exit_distance(early_exit_distance);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.early_exit_distance = early_exit_distance;
- SettingsManager::set_simulation_settings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Physics");
- ImGui::Separator();
-
- bool& gravity = engine.get_gravity();
- if (ImGui::Checkbox("Gravity Enabled", &gravity))
- {
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.gravity_enabled = gravity;
- SettingsManager::set_simulation_settings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Accretion Disk");
- ImGui::Separator();
-
- float disk_thickness = engine.get_disk_thickness();
- if (ImGui::SliderFloat("Cloud Thickness", &disk_thickness, 0.1f, 2.0f, "%.2f"))
- {
- engine.set_disk_thickness(disk_thickness);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.disk_thickness = disk_thickness;
- SettingsManager::set_simulation_settings(settings);
- }
- ImGui::TextDisabled("Thickness relative to Schwarzschild radius");
-
- float disk_density = engine.get_disk_density();
- if (ImGui::SliderFloat("Cloud Density", &disk_density, 0.1f, 3.0f, "%.2f"))
- {
- engine.set_disk_density(disk_density);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.disk_density = disk_density;
- SettingsManager::set_simulation_settings(settings);
- }
- ImGui::TextDisabled("Overall density multiplier");
-
- float rotation_speed = engine.get_rotation_speed();
- if (ImGui::SliderFloat("Rotation Speed", &rotation_speed, 0.0f, 3.0f, "%.2f"))
- {
- engine.set_rotation_speed(rotation_speed);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.rotation_speed = rotation_speed;
- SettingsManager::set_simulation_settings(settings);
- }
- ImGui::TextDisabled("Rotation speed multiplier (0 = no rotation)");
-
- float blur_strength = engine.get_blur_strength();
- if (ImGui::SliderFloat("Blur Strength", &blur_strength, 0.5f, 5.0f, "%.2f"))
- {
- engine.set_blur_strength(blur_strength);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.blur_strength = blur_strength;
- SettingsManager::set_simulation_settings(settings);
- }
- ImGui::TextDisabled("Blur radius for glow effect");
-
- float glow_intensity = engine.get_glow_intensity();
- if (ImGui::SliderFloat("Glow Intensity", &glow_intensity, 0.1f, 3.0f, "%.2f"))
- {
- engine.set_glow_intensity(glow_intensity);
- SimulationSettings settings = SettingsManager::get_settings_const().simulation;
- settings.glow_intensity = glow_intensity;
- SettingsManager::set_simulation_settings(settings);
- }
- ImGui::TextDisabled("Intensity of the glow effect");
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "HDRI Environment");
- ImGui::Separator();
-
- static int selected_hdri = 0;
- ImGui::PushID("SimulationHDRI");
- auto& hdri_manager = HDRIManager::get();
- const auto& available_hdri = hdri_manager.get_available_hdri();
-
- static std::vector<std::string> hdri_option_names;
- static std::vector<const char*> hdri_options;
-
- if (hdri_option_names.size() != available_hdri.size())
- {
- hdri_option_names.clear();
- hdri_options.clear();
-
- for (const auto& path : available_hdri)
- {
- hdri_option_names.push_back(hdri_manager.get_hdri_name(path));
- hdri_options.push_back(hdri_option_names.back().c_str());
- }
- }
-
- if (ImGui::Combo("HDRI Environment", &selected_hdri, hdri_options.data(), static_cast<int>(hdri_options.size())))
- {
- auto& hdri_manager = HDRIManager::get();
- hdri_manager.set_current_hdri(available_hdri[selected_hdri]);
- engine.set_hdri_environment(hdri_manager.get_current_hdri());
- }
- ImGui::TextDisabled("HDRI provides background and lighting for the simulation");
- ImGui::PopID();
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Camera");
- ImGui::Separator();
-
- ImGui::Text("Position: (%.2e, %.2e, %.2e)",
- engine.get_camera().get_orbital_position().x,
- engine.get_camera().get_orbital_position().y,
- engine.get_camera().get_orbital_position().z);
- ImGui::Text("Radius: %.2e", engine.get_camera().get_orbital_radius());
- ImGui::Text("Azimuth: %.2f", engine.get_camera().get_azimuth());
- ImGui::Text("Elevation: %.2f", engine.get_camera().get_elevation());
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Export");
- ImGui::Separator();
-
- if (ImGui::Button("Export Frame (1080p)", ImVec2(-1, 30)))
- {
- auto& engine = Application::get().get_engine();
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- std::stringstream ss;
- ss << "frame_1080p_" << std::put_time(std::localtime(&time_t), "%Y-%m-%d_%H-%M-%S") << ".png";
- engine.export_high_res_frame(ss.str(), 1920, 1080);
- }
-
- if (ImGui::Button("Export High-Res Frame (4K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::get().get_engine();
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- std::stringstream ss;
- ss << "high_res_frame_4k_" << std::put_time(std::localtime(&time_t), "%Y-%m-%d_%H-%M-%S") << ".png";
- engine.export_high_res_frame(ss.str(), 4096, 3072);
- }
-
- if (ImGui::Button("Export High-Res Frame (8K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::get().get_engine();
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- std::stringstream ss;
- ss << "high_res_frame_8k_" << std::put_time(std::localtime(&time_t), "%Y-%m-%d_%H-%M-%S") << ".png";
- engine.export_high_res_frame(ss.str(), 8192, 6144);
- }
-
- if (ImGui::Button("Export Ultra High-Res Frame (16K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::get().get_engine();
- auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
- std::stringstream ss;
- ss << "high_res_frame_16k_" << std::put_time(std::localtime(&time_t), "%Y%m%d_%H%M%S") << ".png";
- engine.export_high_res_frame(ss.str(), 16384, 12288);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Controls");
- ImGui::Separator();
-
- ImGui::Text("Left Mouse: Orbit camera");
- ImGui::Text("Scroll: Zoom in/out");
- ImGui::Text("G: Toggle gravity");
- ImGui::Text("Right Mouse: Enable gravity (hold)");
-
- ImGui::End();
- }
-};
diff --git a/src/states/simulation_state.h b/src/states/simulation_state.h
deleted file mode 100644
index 625d19e..0000000
--- a/src/states/simulation_state.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include "core/state.h"
-#include "core/event.h"
-#include "core/log.h"
-#include "engine/engine.h"
-#include <vector>
-
-namespace Donut
-{
- class SimulationState
- : public State
- {
- public:
- ~SimulationState() = default;
-
- auto on_enter() -> void override;
- auto on_exit() -> void override;
- auto on_update(float delta_time) -> void override;
- auto on_render() -> void override;
- auto on_im_ui_render() -> void override;
- auto on_event(Event& event) -> void override;
-
- private:
- bool m_initialized = false;
- };
-};
diff --git a/src/states/world_builder_state.cpp b/src/states/world_builder_state.cpp
deleted file mode 100644
index be74eb6..0000000
--- a/src/states/world_builder_state.cpp
+++ /dev/null
@@ -1,1110 +0,0 @@
-#include "world_builder_state.h"
-
-#include "core/application.h"
-#include "core/window.h"
-#include "core/hdri_manager.h"
-
-#include "rendering/renderer.h"
-#include "rendering/shader.h"
-#include "rendering/vertex_array.h"
-#include "rendering/vertex_buffer.h"
-#include "rendering/index_buffer.h"
-#include "rendering/texture.h"
-
-#include <imgui.h>
-#include <ImGuizmo.h>
-#include <GLFW/glfw3.h>
-
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtc/type_ptr.hpp>
-
-#include <nlohmann/json.hpp>
-
-#include <numbers>
-#include <fstream>
-#include <sstream>
-#include <limits>
-#include <vector>
-
-namespace Donut
-{
- auto WorldBuilderState::on_enter() -> void
- {
- DONUT_INFO("Entering World Builder State");
-
- ImGuizmo::Enable(true);
-
- m_camera.set_camera_mode(CameraMode::Orbital);
- m_camera.set_orbital_target(glm::vec3(0.0f, 0.0f, 0.0f));
- m_camera.set_orbital_radius(15.0f);
- m_camera.set_orbital_limits(2.0f, 200.0f);
- m_camera.set_orbital_speed(0.01f);
- m_camera.set_zoom_speed(2.0f);
- m_camera.set_azimuth(0.0f);
- m_camera.set_elevation(static_cast<float>(std::numbers::pi) / 3.0f);
- m_camera.update_orbital();
-
- m_sphere_shader = Ref<Shader>(Shader::create("assets/shaders/sphere.glsl"));
- m_skybox_shader = Ref<Shader>(Shader::create("assets/shaders/skybox.glsl"));
- m_grid_shader = Ref<Shader>(Shader::create("assets/shaders/grid.glsl"));
-
- if (!m_sphere_shader)
- DONUT_ERROR("Failed to create sphere shader");
- if (!m_skybox_shader)
- DONUT_ERROR("Failed to create skybox shader");
- if (!m_grid_shader)
- DONUT_ERROR("Failed to create grid shader");
-
- initialize_sphere_geometry();
- initialize_skybox_geometry();
- initialize_grid_geometry();
-
- 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 for WorldBuilder, using fallback");
- }
-
- Material black_hole_material(glm::vec3(0.0f, 0.0f, 0.0f), 0.0f, 0.0f);
- m_black_hole = Object(glm::vec3(0.0f, 0.0f, 0.0f), 2.0f, black_hole_material);
- m_black_hole_initialized = true;
-
- m_grid_size = 10.0f;
-
- m_initialized = true;
- }
-
- auto WorldBuilderState::on_exit() -> void
- {
- DONUT_INFO("Exiting World Builder State");
- }
-
- auto WorldBuilderState::on_update(float delta_time) -> void
- {
- if (m_camera_dragging &&
- !ImGuizmo::IsUsing())
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
-
- glm::vec2 current_mouse_pos(xpos, ypos);
- glm::vec2 delta = current_mouse_pos - m_last_mouse_pos;
-
- float sensitivity = 0.005f;
- float azimuth_delta = delta.x * sensitivity;
- float elevation_delta = -delta.y * sensitivity;
-
- float new_azimuth = m_camera.get_azimuth() + azimuth_delta;
- float new_elevation = m_camera.get_elevation() + elevation_delta;
-
- new_elevation = glm::clamp(new_elevation, 0.01f, static_cast<float>(std::numbers::pi) - 0.01f);
-
- m_camera.set_azimuth(new_azimuth);
- m_camera.set_elevation(new_elevation);
- m_camera.update_orbital();
-
- m_last_mouse_pos = current_mouse_pos;
- }
- }
-
- auto WorldBuilderState::on_render() -> void
- {
- auto& hdri_manager = HDRIManager::get();
- m_hdri_environment = hdri_manager.get_current_hdri();
-
- RenderCommand::set_clear_color(glm::vec4(0.1f, 0.1f, 0.1f, 1.0f));
- RenderCommand::clear();
-
- if (m_hdri_environment)
- render_skybox();
-
- if (m_show_grid)
- render_grid();
-
- render_scene();
- }
-
- auto WorldBuilderState::on_event(Event& event) -> void
- {
- EventDispatcher dispatcher(event);
-
- dispatcher.dispatch<MouseButtonPressedEvent>([this](MouseButtonPressedEvent& e)
- {
- if (e.get_mouse_button() == GLFW_MOUSE_BUTTON_LEFT)
- {
- if (ImGuizmo::IsUsing() ||
- ImGuizmo::IsOver())
- return false;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
-
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- float ndc_x = (2.0f * static_cast<float>(xpos)) / static_cast<float>(width) - 1.0f;
- float ndc_y = 1.0f - (2.0f * static_cast<float>(ypos)) / static_cast<float>(height);
-
- glm::vec4 ray_start_ndc(ndc_x, ndc_y, -1.0f, 1.0f);
- glm::vec4 ray_end_ndc(ndc_x, ndc_y, 0.0f, 1.0f);
-
- glm::mat4 inv_vp = glm::inverse(m_camera.get_projection_matrix() * m_camera.get_view_matrix());
- glm::vec4 ray_start_world = inv_vp * ray_start_ndc;
- glm::vec4 ray_end_world = inv_vp * ray_end_ndc;
-
- ray_start_world /= ray_start_world.w;
- ray_end_world /= ray_end_world.w;
-
- glm::vec3 ray_dir = glm::normalize(glm::vec3(ray_end_world - ray_start_world));
- glm::vec3 ray_origin = glm::vec3(ray_start_world);
-
- float closest_distance = std::numeric_limits<float>::max();
- int closest_object_index = -1;
-
- if (m_black_hole_initialized)
- {
- glm::vec3 oc = ray_origin - m_black_hole.m_centre;
- float a = glm::dot(ray_dir, ray_dir);
- float b = 2.0f * glm::dot(oc, ray_dir);
- float c = glm::dot(oc, oc) - m_black_hole.m_radius * m_black_hole.m_radius;
- float discriminant = b * b - 4 * a * c;
-
- if (discriminant > 0)
- {
- float t1 = (-b - sqrt(discriminant)) / (2.0f * a);
- float t2 = (-b + sqrt(discriminant)) / (2.0f * a);
-
- if (t1 > 0 && t1 < closest_distance)
- {
- closest_distance = t1;
- closest_object_index = -2;
- }
- else if (t2 > 0 && t2 < closest_distance)
- {
- closest_distance = t2;
- closest_object_index = -2;
- }
- }
- }
-
- for (size_t i = 0; i < m_scene.objs.size(); ++i)
- {
- const Object& obj = m_scene.objs[i];
-
- glm::vec3 oc = ray_origin - obj.m_centre;
- float a = glm::dot(ray_dir, ray_dir);
- float b = 2.0f * glm::dot(oc, ray_dir);
- float c = glm::dot(oc, oc) - obj.m_radius * obj.m_radius;
- float discriminant = b * b - 4 * a * c;
-
- if (discriminant > 0)
- {
- float t1 = (-b - sqrt(discriminant)) / (2.0f * a);
- float t2 = (-b + sqrt(discriminant)) / (2.0f * a);
-
- if (t1 > 0 && t1 < closest_distance)
- {
- closest_distance = t1;
- closest_object_index = static_cast<int>(i);
- }
- else if (t2 > 0 && t2 < closest_distance)
- {
- closest_distance = t2;
- closest_object_index = static_cast<int>(i);
- }
- }
- }
-
- if (closest_object_index >= 0)
- {
- m_selected_object_index = closest_object_index;
- DONUT_INFO("Selected object {}", closest_object_index);
- return true;
- }
- else if (closest_object_index == -2)
- {
- m_selected_object_index = -1;
- DONUT_INFO("Black hole clicked (not selectable)");
- return true;
- }
- else
- {
- m_selected_object_index = -1;
- m_camera_dragging = true;
- m_last_mouse_pos = glm::vec2(xpos, ypos);
- }
-
- return true;
- }
- return false;
- });
-
- dispatcher.dispatch<MouseButtonReleasedEvent>([this](MouseButtonReleasedEvent& e)
- {
- if (e.get_mouse_button() == GLFW_MOUSE_BUTTON_LEFT)
- {
- if (!ImGuizmo::IsUsing())
- m_camera_dragging = false;
- return true;
- }
-
- return false;
- });
-
- dispatcher.dispatch<WindowResizeEvent>([this](WindowResizeEvent& e)
- {
- int new_width = e.get_width();
- int new_height = e.get_height();
- if (new_width > 0 && new_height > 0)
- {
- float aspect = static_cast<float>(new_width) / static_cast<float>(new_height);
- m_camera.set_projection(45.0f, aspect, 0.1f, 1000.0f);
- }
- return false;
- });
-
- dispatcher.dispatch<MouseScrolledEvent>([this](MouseScrolledEvent& e)
- {
- float zoom_speed = 0.1f;
- float zoom_delta = e.get_y_offset() * zoom_speed;
-
- double current_radius = m_camera.get_orbital_radius();
- double new_radius = current_radius - zoom_delta * current_radius * 0.1f;
-
- double min_radius = 2.0f;
- double max_radius = 200.0f;
- new_radius = glm::clamp(new_radius, min_radius, max_radius);
-
- m_camera.set_orbital_radius(new_radius);
- m_camera.update_orbital();
-
- return true;
- });
-
- dispatcher.dispatch<KeyPressedEvent>([this](KeyPressedEvent& e)
- {
- if (m_selected_object_index >= 0 &&
- m_selected_object_index < static_cast<int>(m_scene.objs.size()))
- {
- switch (e.get_key_code())
- {
- case GLFW_KEY_M:
- m_gizmo_operation = ImGuizmo::TRANSLATE;
- return true;
- case GLFW_KEY_R:
- m_gizmo_operation = ImGuizmo::ROTATE;
- return true;
- case GLFW_KEY_S:
- m_gizmo_operation = ImGuizmo::SCALE;
- return true;
- }
- }
- return false;
- });
- }
-
- auto WorldBuilderState::on_im_ui_render() -> void
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- ImGuiViewport* vp = ImGui::GetMainViewport();
- ImGuizmo::SetRect(vp->Pos.x, vp->Pos.y, vp->Size.x, vp->Size.y);
-
- ImGui::SetNextWindowSize(ImVec2(400, 600), ImGuiCond_FirstUseEver);
- ImGui::SetNextWindowPos(ImVec2(10, 10), ImGuiCond_FirstUseEver);
-
- ImGui::Begin("World Builder", nullptr, ImGuiWindowFlags_NoCollapse);
-
- ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]);
- ImGui::TextColored(ImVec4(0.8f, 0.8f, 1.0f, 1.0f), "World Builder");
- ImGui::PopFont();
- ImGui::Separator();
-
- if (ImGui::CollapsingHeader("Scene Info"))
- {
- ImGui::Text("Objects in scene: %zu/16 (+ 1 black hole)", m_scene.objs.size());
-
- ImGui::Separator();
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Camera Info:");
-
- glm::vec3 camera_pos = m_camera.get_orbital_position();
- glm::vec3 target = m_camera.get_orbital_target();
-
- ImGui::Text("Position: (%.2f, %.2f, %.2f)", camera_pos.x, camera_pos.y, camera_pos.z);
- ImGui::Text("Target: (%.2f, %.2f, %.2f)", target.x, target.y, target.z);
- ImGui::Text("Distance: %.2f", m_camera.get_orbital_radius());
- ImGui::Text("Azimuth: %.1f°", glm::degrees(m_camera.get_azimuth()));
- ImGui::Text("Elevation: %.1f°", glm::degrees(m_camera.get_elevation()));
-
- ImGui::Separator();
-
- if (ImGui::Button("Reset Camera"))
- {
- m_camera.set_orbital_radius(15.0f);
- m_camera.set_azimuth(0.0f);
- m_camera.set_elevation(static_cast<float>(std::numbers::pi) / 3.0f);
- m_camera.set_orbital_target(glm::vec3(0.0f, 0.0f, 0.0f));
- m_camera.update_orbital();
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Clear Scene"))
- clear_scene();
-
- ImGui::SameLine();
- if (ImGui::Button("Focus on Objects"))
- {
- if (!m_scene.objs.empty())
- {
- glm::vec3 center(0.0f);
- for (const auto& obj : m_scene.objs)
- center += obj.m_centre;
- center /= static_cast<float>(m_scene.objs.size());
- m_camera.set_orbital_target(center);
- m_camera.update_orbital();
- }
- }
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("create Object"))
- {
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "New Sphere");
- ImGui::Separator();
-
- ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "Objects: %zu/16", m_scene.objs.size());
-
- ImGui::Text("Position:");
- ImGui::DragFloat3("##Position", &m_new_object_position.x, 0.1f);
-
- ImGui::Text("Radius:");
- ImGui::DragFloat("##Radius", &m_new_object_radius, 0.1f, 0.1f, 10.0f);
-
- ImGui::Text("Color:");
- ImGui::ColorEdit3("##Color", &m_new_object_color.x);
-
- ImGui::Text("Specular:");
- ImGui::SliderFloat("##Specular", &m_new_object_specular, 0.0f, 1.0f);
-
- ImGui::Text("Emission:");
- ImGui::SliderFloat("##Emission", &m_new_object_emission, 0.0f, 1.0f);
-
- ImGui::Spacing();
-
- if (m_scene.objs.size() >= 16)
- {
- ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
- ImGui::Button("add Sphere (Limit Reached)", ImVec2(ImGui::GetWindowWidth() - 20, 30));
- ImGui::PopStyleVar();
- }
- else
- {
- if (ImGui::Button("add Sphere", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
- add_sphere();
- }
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("Scene Objects"))
- {
- if (m_black_hole_initialized)
- {
- ImGui::PushID(-1);
-
- ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "Black Hole (Center)");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Position: (0.00, 0.00, 0.00)");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Radius: %.2f", m_black_hole.m_radius);
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Color: Black (with white outline)");
- ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Cannot be removed or modified");
-
- ImGui::PopID();
- ImGui::Separator();
- }
-
- if (m_scene.objs.empty())
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "No additional objects in scene");
- else
- {
- for (size_t i = 0; i < m_scene.objs.size(); ++i)
- {
- Object& obj = m_scene.objs[i];
-
- ImGui::PushID(static_cast<int>(i));
-
- bool is_selected = (m_selected_object_index == static_cast<int>(i));
- if (ImGui::Selectable(("Sphere " + std::to_string(i)).c_str(), is_selected))
- m_selected_object_index = static_cast<int>(i);
-
- if (is_selected)
- {
- ImGui::SameLine();
- if (ImGui::Button("Remove"))
- {
- remove_selected_object();
- }
-
- ImGui::Text("Position: (%.2f, %.2f, %.2f)",
- obj.m_centre.x, obj.m_centre.y, obj.m_centre.z);
- ImGui::Text("Radius: %.2f", obj.m_radius);
- ImGui::Text("Color: (%.2f, %.2f, %.2f)",
- obj.m_material.m_color.x, obj.m_material.m_color.y, obj.m_material.m_color.z);
- }
-
- ImGui::PopID();
- }
- }
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("Gizmo Controls"))
- {
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Gizmo Operation:");
-
- if (ImGui::RadioButton("Translate", m_gizmo_operation == ImGuizmo::TRANSLATE))
- m_gizmo_operation = ImGuizmo::TRANSLATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Rotate", m_gizmo_operation == ImGuizmo::ROTATE))
- m_gizmo_operation = ImGuizmo::ROTATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Scale", m_gizmo_operation == ImGuizmo::SCALE))
- m_gizmo_operation = ImGuizmo::SCALE;
-
- ImGui::Spacing();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Hotkeys:");
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "M = Move, R = Rotate, S = Scale");
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("Selection Outline"))
- {
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Outline Settings:");
-
- ImGui::Text("Outline Color:");
- ImGui::ColorEdit3("##OutlineColor", &m_outline_color.x);
-
- ImGui::Text("Outline Width:");
- ImGui::SliderFloat("##OutlineWidth", &m_outline_width, 0.01f, 0.9f, "%.2f");
-
- ImGui::Spacing();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "White rim around sphere silhouette");
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("Grid Settings"))
- {
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Grid Settings:");
-
- ImGui::Text("Show Grid:");
- ImGui::SameLine();
- ImGui::Checkbox("##ShowGrid", &m_show_grid);
-
- ImGui::Text("Grid Color:");
- ImGui::ColorEdit3("##GridColor", &m_grid_color.x);
-
- ImGui::Text("Grid Alpha:");
- ImGui::SliderFloat("##GridAlpha", &m_grid_alpha, 0.0f, 1.0f, "%.2f");
-
- ImGui::Text("Grid Size:");
- ImGui::SliderFloat("##GridSize", &m_grid_size, 1.0f, 500.0f, "%.1f");
-
- ImGui::Spacing();
- if (ImGui::Button("Regenerate Grid", ImVec2(ImGui::GetWindowWidth() - 20, 25)))
- initialize_grid_geometry();
-
- ImGui::Spacing();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Reference grid for spatial orientation");
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("HDRI Environment", nullptr))
- {
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "HDRI Settings:");
-
- static int selected_hdri = 0;
- ImGui::PushID("WorldBuilderHDRI");
- auto& hdri_manager = HDRIManager::get();
- const auto& available_hdri = hdri_manager.get_available_hdri();
-
- static std::vector<std::string> hdri_option_names;
- static std::vector<const char*> hdri_options;
-
- if (hdri_option_names.size() != available_hdri.size())
- {
- hdri_option_names.clear();
- hdri_options.clear();
-
- for (const auto& path : available_hdri)
- {
- hdri_option_names.push_back(hdri_manager.get_hdri_name(path));
- hdri_options.push_back(hdri_option_names.back().c_str());
- }
- }
-
- if (ImGui::Combo("HDRI Environment", &selected_hdri, hdri_options.data(), static_cast<int>(hdri_options.size())))
- {
- auto& hdri_manager = HDRIManager::get();
- hdri_manager.set_current_hdri(available_hdri[selected_hdri]);
- m_hdri_environment = hdri_manager.get_current_hdri();
- }
-
- ImGui::Spacing();
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "HDRI provides background skybox and lighting for the scene");
- ImGui::PopID();
- }
-
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::Spacing();
-
- float button_width = (ImGui::GetWindowWidth() - 30) / 2.0f;
-
- if (ImGui::Button("Save Scene", ImVec2(button_width, 30)))
- save_scene();
-
- ImGui::SameLine();
- if (ImGui::Button("load Scene", ImVec2(button_width, 30)))
- load_scene();
-
- ImGui::Spacing();
-
- if (ImGui::Button("Start Simulation", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
- {
- auto& engine = Application::get().get_engine();
- engine.load_objects_from_scene(m_scene.objs);
- Application::get().get_state_manager().switch_to_state("Simulation");
- }
-
- ImGui::Spacing();
-
- if (ImGui::Button("Back to Config", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
- Application::get().get_state_manager().switch_to_state("Config");
-
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Camera: Left mouse = rotate, Scroll = zoom");
- ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Gizmo: M=Move, R=Rotate, S=Scale");
-
- ImGui::End();
-
- if (m_selected_object_index >= 0 &&
- m_selected_object_index < static_cast<int>(m_scene.objs.size()) &&
- m_selected_object_index != -2)
- {
- glm::mat4 view = m_camera.get_view_matrix();
- glm::mat4 projection = m_camera.get_projection_matrix();
-
- Object& selected_obj = m_scene.objs[m_selected_object_index];
-
- glm::mat4 object_transform = glm::mat4(1.0f);
- object_transform = glm::translate(object_transform, selected_obj.m_centre);
- object_transform = glm::scale(object_transform, glm::vec3(selected_obj.m_radius));
-
- DONUT_INFO("Object position: ({}, {}, {})", selected_obj.m_centre.x, selected_obj.m_centre.y, selected_obj.m_centre.z);
- glm::vec3 camera_pos = m_camera.get_orbital_position();
- DONUT_INFO("Camera position: ({}, {}, {})", camera_pos.x, camera_pos.y, camera_pos.z);
-
- glm::vec3 view_translation = glm::vec3(view[3][0], view[3][1], view[3][2]);
- DONUT_INFO("View translation: ({}, {}, {})", view_translation.x, view_translation.y, view_translation.z);
-
- ImGuizmo::SetDrawlist(ImGui::GetForegroundDrawList());
- if (ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection),
- m_gizmo_operation, m_gizmo_mode,
- glm::value_ptr(object_transform),
- nullptr, nullptr))
- {
- glm::vec3 new_position = glm::vec3(object_transform[3][0], object_transform[3][1], object_transform[3][2]);
- selected_obj.m_centre = new_position;
-
- glm::vec3 scale = glm::vec3
- (
- glm::length(glm::vec3(object_transform[0])),
- glm::length(glm::vec3(object_transform[1])),
- glm::length(glm::vec3(object_transform[2]))
- );
- selected_obj.m_radius = (scale.x + scale.y + scale.z) / 3.0f;
-
- DONUT_INFO("Matrix [3]: ({}, {}, {}, {})", object_transform[3][0], object_transform[3][1], object_transform[3][2], object_transform[3][3]);
- DONUT_INFO("New position: ({}, {}, {})", new_position.x, new_position.y, new_position.z);
- }
- }
- }
-
- auto WorldBuilderState::add_sphere() -> void
- {
- if (m_scene.objs.size() >= 16)
- {
- DONUT_WARN("Cannot add more objects. Maximum of 16 objects reached.");
- return;
- }
-
- Material material(m_new_object_color, m_new_object_specular, m_new_object_emission);
- Object sphere(m_new_object_position, m_new_object_radius, material);
- m_scene.objs.push_back(sphere);
-
- m_selected_object_index = static_cast<int>(m_scene.objs.size() - 1);
-
- m_new_object_position = glm::vec3(0.0f, 0.0f, 0.0f);
- m_new_object_radius = 1.0f;
- m_new_object_color = glm::vec3(1.0f, 1.0f, 1.0f);
- m_new_object_specular = 0.5f;
- m_new_object_emission = 0.0f;
-
- DONUT_INFO("Added sphere to scene and selected it");
- }
-
- auto WorldBuilderState::remove_selected_object() -> void
- {
- if (m_selected_object_index >= 0 && m_selected_object_index < static_cast<int>(m_scene.objs.size()))
- {
- m_scene.objs.erase(m_scene.objs.begin() + m_selected_object_index);
- m_selected_object_index = -1;
- DONUT_INFO("Removed object from scene");
- }
- }
-
- auto WorldBuilderState::clear_scene() -> void
- {
- m_scene.objs.clear();
- m_selected_object_index = -1;
- DONUT_INFO("Cleared scene (black hole remains at center)");
- }
-
- auto WorldBuilderState::save_scene() -> void
- {
- nlohmann::json scene_data;
- scene_data["objects"] = nlohmann::json::array();
-
- for (const auto& obj : m_scene.objs)
- {
- nlohmann::json sphere_data;
- sphere_data["position"] =
- {
- obj.m_centre.x,
- obj.m_centre.y,
- obj.m_centre.z
- };
-
- sphere_data["color"] =
- {
- obj.m_material.m_color.x,
- obj.m_material.m_color.y,
- obj.m_material.m_color.z
- };
-
- sphere_data["radius"] = obj.m_radius;
- sphere_data["specular"] = obj.m_material.m_specular;
- sphere_data["emission"] = obj.m_material.m_emission;
- scene_data["objects"].push_back(sphere_data);
- }
-
- std::ofstream file("Scene.json");
- if (file.is_open())
- {
- file << scene_data.dump(4);
- file.close();
- DONUT_INFO("Scene saved to Scene.json (black hole always present at center)");
- }
- else
- DONUT_ERROR("Failed to save scene");
- }
-
- auto WorldBuilderState::load_scene() -> void
- {
- std::ifstream file("Scene.json");
- if (file.is_open())
- {
- m_scene.objs.clear();
- m_selected_object_index = -1;
-
- nlohmann::json scene_data;
- file >> scene_data;
-
- if (scene_data.contains("objects"))
- {
- for (const auto& sphere_data : scene_data["objects"])
- {
- glm::vec3 position;
- glm::vec3 color;
- float radius;
- float specular;
- float emission;
-
- auto pos_array = sphere_data["position"];
- position.x = pos_array[0].get<float>();
- position.y = pos_array[1].get<float>();
- position.z = pos_array[2].get<float>();
-
- auto color_array = sphere_data["color"];
- color.x = color_array[0].get<float>();
- color.y = color_array[1].get<float>();
- color.z = color_array[2].get<float>();
-
- radius = sphere_data["radius"].get<float>();
- specular = sphere_data["specular"].get<float>();
- emission = sphere_data["emission"].get<float>();
-
- Material material(color, specular, emission);
- Object sphere(position, radius, material);
- m_scene.objs.push_back(sphere);
- }
- }
-
- file.close();
- DONUT_INFO("Scene loaded from Scene.json (black hole remains at center)");
- }
- else
- DONUT_ERROR("Failed to load scene");
- }
-
- auto WorldBuilderState::initialize_sphere_geometry() -> void
- {
- std::vector<float> vertices;
- std::vector<uint32_t> indices;
-
- const int segments = 32;
- const int rings = 16;
-
- for (int ring = 0; ring <= rings; ++ring)
- {
- float phi = static_cast<float>(std::numbers::pi) * ring / rings;
- float sin_phi = sin(phi);
- float cos_phi = cos(phi);
-
- for (int segment = 0; segment <= segments; ++segment)
- {
- float theta = 2.0f * static_cast<float>(std::numbers::pi) * segment / segments;
- float sin_theta = sin(theta);
- float cos_theta = cos(theta);
-
- float x = cos_theta * sin_phi;
- float y = cos_phi;
- float z = sin_theta * sin_phi;
-
- float nx = x;
- float ny = y;
- float nz = z;
-
- vertices.push_back(x);
- vertices.push_back(y);
- vertices.push_back(z);
- vertices.push_back(nx);
- vertices.push_back(ny);
- vertices.push_back(nz);
- }
- }
-
- for (int ring = 0; ring < rings; ++ring)
- {
- for (int segment = 0; segment < segments; ++segment)
- {
- uint32_t first = ring * (segments + 1) + segment;
- uint32_t second = first + segments + 1;
-
- indices.push_back(first);
- indices.push_back(second);
- indices.push_back(first + 1);
-
- indices.push_back(second);
- indices.push_back(second + 1);
- indices.push_back(first + 1);
- }
- }
-
- auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(vertices.data(), static_cast<uint32_t>(vertices.size() * sizeof(float))));
- VertexBufferLayout layout;
- layout.push<float>(3);
- layout.push<float>(3);
- vertex_buffer->set_layout(layout);
-
- auto index_buffer = Ref<IndexBuffer>(IndexBuffer::create(indices.data(), static_cast<uint32_t>(indices.size())));
-
- m_sphere_vao = Ref<VertexArray>(VertexArray::create());
- m_sphere_vao->add_vertex_buffer(vertex_buffer);
- m_sphere_vao->set_index_buffer(index_buffer);
- }
-
- auto WorldBuilderState::render_scene() -> void
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::set_viewport(0, 0, width, height);
- RenderCommand::enable_depth_test();
-
- glm::mat4 view = m_camera.get_view_matrix();
- glm::mat4 projection = m_camera.get_projection_matrix();
- glm::mat4 view_projection = projection * view;
-
- glm::vec3 light_pos = m_scene.m_light_pos;
- glm::vec3 camera_pos = m_camera.get_orbital_position();
-
- m_sphere_shader->bind();
- m_sphere_shader->set_mat4("u_ViewProjection", view_projection);
- m_sphere_shader->set_float3("u_LightPos", light_pos);
- m_sphere_shader->set_float3("u_CameraPos", camera_pos);
-
- m_sphere_shader->set_float3("u_OutlineColor", m_outline_color);
- m_sphere_shader->set_float("u_OutlineWidth", m_outline_width);
-
- if (m_hdri_environment)
- {
- m_hdri_environment->bind(1);
- m_sphere_shader->set_int("u_HDRIEnvironment", 1);
- }
-
- if (m_black_hole_initialized)
- {
- glm::mat4 black_hole_transform = glm::translate(glm::mat4(1.0f), m_black_hole.m_centre);
- black_hole_transform = glm::scale(black_hole_transform, glm::vec3(m_black_hole.m_radius));
-
- m_sphere_shader->set_mat4("u_Transform", black_hole_transform);
- m_sphere_shader->set_int("u_IsSelected", 1);
- m_sphere_shader->set_float3("u_Color", m_black_hole.m_material.m_color);
- m_sphere_shader->set_float("u_Emission", m_black_hole.m_material.m_emission);
- m_sphere_shader->set_float("u_Specular", m_black_hole.m_material.m_specular);
- m_sphere_shader->set_float("u_OutlineWidth", 0.3f);
-
- m_sphere_vao->bind();
- RenderCommand::draw_indexed(m_sphere_vao);
-
- m_sphere_shader->set_float("u_OutlineWidth", m_outline_width);
- }
-
- for (size_t i = 0; i < m_scene.objs.size(); ++i)
- {
- const auto& obj = m_scene.objs[i];
-
- glm::mat4 transform = glm::translate(glm::mat4(1.0f), obj.m_centre);
- transform = glm::scale(transform, glm::vec3(obj.m_radius));
-
- m_sphere_shader->set_mat4("u_Transform", transform);
-
- bool is_selected = (m_selected_object_index == static_cast<int>(i));
- m_sphere_shader->set_int("u_IsSelected", is_selected ? 1 : 0);
- if (is_selected)
- {
- glm::vec3 highlight_color = obj.m_material.m_color * 1.5f;
- highlight_color = glm::clamp(highlight_color, 0.0f, 1.0f);
- m_sphere_shader->set_float3("u_Color", highlight_color);
- m_sphere_shader->set_float("u_Emission", 0.2f);
- }
- else
- {
- m_sphere_shader->set_float3("u_Color", obj.m_material.m_color);
- m_sphere_shader->set_float("u_Emission", obj.m_material.m_emission);
- }
-
- m_sphere_shader->set_float("u_Specular", obj.m_material.m_specular);
-
- m_sphere_vao->bind();
- RenderCommand::draw_indexed(m_sphere_vao);
- }
-
- RenderCommand::disable_depth_test();
- }
-
-
-
- auto WorldBuilderState::initialize_skybox_geometry() -> void
- {
- float skybox_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
- };
-
- auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(skybox_vertices, sizeof(skybox_vertices)));
- VertexBufferLayout layout;
- layout.push<float>(3);
- vertex_buffer->set_layout(layout);
-
- m_skybox_vao = Ref<VertexArray>(VertexArray::create());
- m_skybox_vao->add_vertex_buffer(vertex_buffer);
- }
-
- auto WorldBuilderState::render_skybox() -> void
- {
- if (!m_skybox_shader || !m_skybox_vao || !m_hdri_environment)
- return;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::set_viewport(0, 0, width, height);
- RenderCommand::disable_depth_test();
-
- glm::mat4 view = m_camera.get_view_matrix();
- glm::mat4 projection = m_camera.get_projection_matrix();
-
- view = glm::mat4(glm::mat3(view));
-
- m_skybox_shader->bind();
- m_skybox_shader->set_mat4("u_View", view);
- m_skybox_shader->set_mat4("u_Projection", projection);
-
- m_hdri_environment->bind(0);
- m_skybox_shader->set_int("u_Skybox", 0);
-
- m_skybox_vao->bind();
- RenderCommand::draw_arrays(36);
-
- RenderCommand::enable_depth_test();
- }
-
- auto WorldBuilderState::initialize_grid_geometry() -> void
- {
- if (m_grid_vao)
- {
- m_grid_vao->unbind();
- m_grid_vao.reset();
- }
-
- const float grid_size = 50.0f;
- const int grid_lines = 101;
- const float half_size = grid_size * 0.5f;
- const float step = grid_size / (grid_lines - 1);
-
- std::vector<float> vertices;
- std::vector<uint32_t> indices;
-
- for (int i = 0; i < grid_lines; ++i)
- {
- float z = -half_size + i * step;
-
- vertices.push_back(-half_size);
- vertices.push_back(0.0f);
- vertices.push_back(z);
-
- vertices.push_back(half_size);
- vertices.push_back(0.0f);
- vertices.push_back(z);
-
- uint32_t base_index = static_cast<uint32_t>(vertices.size() / 3) - 2;
- indices.push_back(base_index);
- indices.push_back(base_index + 1);
- }
-
- for (int i = 0; i < grid_lines; ++i)
- {
- float x = -half_size + i * step;
-
- vertices.push_back(x);
- vertices.push_back(0.0f);
- vertices.push_back(-half_size);
-
- vertices.push_back(x);
- vertices.push_back(0.0f);
- vertices.push_back(half_size);
-
- uint32_t base_index = static_cast<uint32_t>(vertices.size() / 3) - 2;
- indices.push_back(base_index);
- indices.push_back(base_index + 1);
- }
-
- auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(vertices.data(), static_cast<uint32_t>(vertices.size() * sizeof(float))));
- VertexBufferLayout layout;
- layout.push<float>(3);
- vertex_buffer->set_layout(layout);
-
- auto index_buffer = Ref<IndexBuffer>(IndexBuffer::create(indices.data(), static_cast<uint32_t>(indices.size())));
-
- m_grid_vao = Ref<VertexArray>(VertexArray::create());
- m_grid_vao->add_vertex_buffer(vertex_buffer);
- m_grid_vao->set_index_buffer(index_buffer);
-
- m_grid_vao->unbind();
- }
-
- auto WorldBuilderState::render_grid() -> void
- {
- if (!m_grid_shader || !m_grid_vao)
- return;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::set_viewport(0, 0, width, height);
- RenderCommand::enable_depth_test();
- RenderCommand::enable_blending();
-
- glm::mat4 view = m_camera.get_view_matrix();
- glm::mat4 projection = m_camera.get_projection_matrix();
- glm::mat4 view_projection = projection * view;
- glm::mat4 transform = glm::mat4(1.0f);
-
- glm::vec3 camera_pos = m_camera.get_orbital_position();
-
- m_grid_shader->bind();
- m_grid_shader->set_mat4("u_ViewProjection", view_projection);
- m_grid_shader->set_mat4("u_Transform", transform);
- m_grid_shader->set_float3("u_GridColor", m_grid_color);
- m_grid_shader->set_float("u_GridAlpha", m_grid_alpha);
- m_grid_shader->set_float("u_GridSize", m_grid_size);
- m_grid_shader->set_float3("u_CameraPos", camera_pos);
-
- m_grid_vao->bind();
- RenderCommand::draw_lines(m_grid_vao);
-
- m_grid_vao->unbind();
- m_grid_shader->unbind();
- RenderCommand::disable_blending();
-
- RenderCommand::enable_depth_test();
- }
-};
diff --git a/src/states/world_builder_state.h b/src/states/world_builder_state.h
deleted file mode 100644
index 8b4678a..0000000
--- a/src/states/world_builder_state.h
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-
-#include "core/camera.h"
-#include "core/state.h"
-#include "core/event.h"
-#include "core/log.h"
-
-#include "engine/scene.h"
-#include "engine/object.h"
-
-#include "rendering/renderer.h"
-#include "rendering/shader.h"
-#include "rendering/vertex_array.h"
-
-#include <imgui.h>
-#include <ImGuizmo.h>
-
-#include <vector>
-#include <memory>
-
-namespace Donut
-{
- class WorldBuilderState
- : public State
- {
- public:
- ~WorldBuilderState() = default;
-
- auto on_enter() -> void override;
- auto on_exit() -> void override;
- auto on_update(float delta_time) -> void override;
- auto on_render() -> void override;
- auto on_im_ui_render() -> void override;
- auto on_event(Event& event) -> void override;
- private:
- auto add_sphere() -> void;
- auto remove_selected_object() -> void;
- auto clear_scene() -> void;
- auto save_scene() -> void;
- auto load_scene() -> void;
- auto render_scene() -> void;
- auto initialize_sphere_geometry() -> void;
- auto initialize_grid_geometry() -> void;
- auto render_grid() -> void;
- private:
- Scene m_scene;
- Camera m_camera;
- bool m_initialized = false;
-
- Ref<Shader> m_sphere_shader;
- Ref<VertexArray> m_sphere_vao;
- Ref<CubemapTexture> m_hdri_environment;
-
- Ref<Shader> m_skybox_shader;
- Ref<VertexArray> m_skybox_vao;
-
- Ref<Shader> m_grid_shader;
- Ref<VertexArray> m_grid_vao;
-
- glm::vec3 m_new_object_position = glm::vec3(0.0f, 0.0f, 0.0f);
- float m_new_object_radius = 1.0f;
- glm::vec3 m_new_object_color = glm::vec3(1.0f, 1.0f, 1.0f);
- float m_new_object_specular = 0.5f;
- float m_new_object_emission = 0.0f;
-
- int m_selected_object_index = -1;
- bool m_camera_dragging = false;
- glm::vec2 m_last_mouse_pos = glm::vec2(0.0f, 0.0f);
-
- ImGuizmo::OPERATION m_gizmo_operation = ImGuizmo::TRANSLATE;
- ImGuizmo::MODE m_gizmo_mode = ImGuizmo::LOCAL;
-
- bool m_show_object_list = true;
- bool m_show_object_creator = true;
- bool m_show_scene_info = true;
- bool m_show_gizmo_controls = true;
- bool m_show_outline_controls = true;
- bool m_show_grid = true;
-
- glm::vec3 m_outline_color = glm::vec3(1.0f, 1.0f, 1.0f);
- float m_outline_width = 0.25f;
-
- glm::vec3 m_grid_color = glm::vec3(0.5f, 0.5f, 0.5f);
- float m_grid_alpha = 0.5f;
- float m_grid_size = 50.0f;
-
- Object m_black_hole;
- bool m_black_hole_initialized = false;
-
- auto set_hdri_environment(Ref<CubemapTexture> hdri) -> void { m_hdri_environment = hdri; }
- auto get_hdri_environment() const -> Ref<CubemapTexture> { return m_hdri_environment; }
-
- auto initialize_skybox_geometry() -> void;
- auto render_skybox() -> void;
- };
-};