#include "engine.h" #include #include #include #include #include #include #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), static_cast(8.54e36)) { GLFWwindow* window = static_cast(Application::get().get_window().get_native_window()); glfwGetFramebufferSize(window, &m_width, &m_height); m_compute_height = 420; m_compute_width = (m_width * 420) / m_height; m_camera.set_orbital_mode(true); m_camera.set_radius(6.34194e10); m_camera.set_min_radius(1e10); m_camera.set_max_radius(1e12); m_camera.set_orbit_speed(0.01f); m_camera.set_zoom_speed(25e9f); m_objects = { { glm::vec4(4e11f, 0.0f, 0.0f, 4e10f), glm::vec4(1,1,0,1), static_cast(1.98892e30) }, { glm::vec4(0.0f, 0.0f, 4e11f, 4e10f), glm::vec4(1,0,0,1), static_cast(1.98892e30) }, { glm::vec4(0.0f, 0.0f, 0.0f, m_sag_a.m_rs), glm::vec4(0,0,0,1), static_cast(m_sag_a.m_mass) } }; m_quad_shader = Shader::create("assets/shaders/quad.glsl"); m_grid_shader = Shader::create("assets/shaders/grid.glsl"); m_compute_shader = Shader::create_compute("Geodesic", load_compute_shader("assets/shaders/geodesic.glsl")); m_camera_ubo = UniformBuffer::create(128, 1); m_disk_ubo = UniformBuffer::create(sizeof(float) * 4, 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); 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 }; m_quad_vao = VertexArray::create(); auto vertex_buffer = VertexBuffer::create(quad_vertices, static_cast(sizeof(quad_vertices))); VertexBufferLayout layout; layout.push(2); layout.push(2); vertex_buffer->set_layout(layout); m_quad_vao->add_vertex_buffer(vertex_buffer); m_texture = Texture2D::create(m_width, m_height); } void Engine::generate_grid(const std::vector& objects) { const int grid_size = 25; const float spacing = 1e10f; std::vector vertices; std::vector indices; for (int z = 0; z <= grid_size; ++z) { for (int x = 0; x <= grid_size; ++x) { float world_x = (x - grid_size / 2) * spacing; float world_z = (z - grid_size / 2) * spacing; float y = 0.0f; for (const auto& obj : objects) { glm::vec3 obj_pos = glm::vec3(obj.m_pos_radius); double mass = obj.m_mass; double radius = obj.m_pos_radius.w; double r_s = 2.0 * G * mass / (c * c); double dx = world_x - obj_pos.x; double dz = world_z - obj_pos.z; double dist = sqrt(dx * dx + dz * dz); if (dist > r_s) { double delta_y = 2.0 * sqrt(r_s * (dist - r_s)); y += static_cast(delta_y) - 3e10f; } else { y += 2.0f * static_cast(sqrt(r_s * r_s)) - 3e10f; } } vertices.emplace_back(world_x, y, world_z); } } for (int z = 0; z < grid_size; ++z) { for (int x = 0; x < grid_size; ++x) { int i = z * (grid_size + 1) + x; indices.push_back(i); indices.push_back(i + 1); indices.push_back(i); indices.push_back(i + grid_size + 1); } } if (!m_grid_vao) { m_grid_vao = Ref(VertexArray::create()); auto vertex_buffer = Ref(VertexBuffer::create(vertices.data(), static_cast(vertices.size() * sizeof(glm::vec3)))); VertexBufferLayout layout; layout.push(3); vertex_buffer->set_layout(layout); m_grid_vao->add_vertex_buffer(vertex_buffer); auto index_buffer = Ref(IndexBuffer::create(indices.data(), static_cast(indices.size()))); m_grid_vao->set_index_buffer(index_buffer); } else { auto vertex_buffer = m_grid_vao->get_vertex_buffers()[0]; vertex_buffer->set_data(vertices.data(), static_cast(vertices.size() * sizeof(glm::vec3))); } m_grid_index_count = static_cast(indices.size()); } void Engine::draw_grid(const glm::mat4& view_proj) { if (!m_grid_shader || !m_grid_vao) return; m_grid_shader->bind(); m_grid_shader->set_mat4("viewProj", view_proj); RenderCommand::disable_depth_test(); RenderCommand::enable_blending(); m_grid_vao->bind(); glDrawElements(GL_LINES, m_grid_index_count, GL_UNSIGNED_INT, nullptr); RenderCommand::enable_depth_test(); } void Engine::update_window_dimensions() { GLFWwindow* window = static_cast(Application::get().get_window().get_native_window()); int old_width = m_width; int old_height = m_height; glfwGetFramebufferSize(window, &m_width, &m_height); m_compute_height = 420; m_compute_width = (m_width * 420) / m_height; if (old_width != m_width || old_height != m_height) m_texture = Texture2D::create(m_width, m_height); } void Engine::draw_full_screen_quad() { RenderCommand::set_viewport(0, 0, m_width, m_height); m_quad_shader->bind(); std::cout << "Texture ID: " << m_texture->get_renderer_id() << ", Size: " << m_texture->get_width() << "x" << m_texture->get_height() << std::endl; m_texture->bind(0); m_quad_shader->set_int("screenTexture", 0); std::cout << "Set screenTexture uniform to slot 0\n"; RenderCommand::disable_depth_test(); RenderCommand::draw_indexed(m_quad_vao, 6); RenderCommand::enable_depth_test(); } void Engine::dispatch_compute(const Camera& cam) { int cw = m_compute_width; int ch = m_compute_height; if (m_texture->get_width() != cw || m_texture->get_height() != ch) m_texture = Texture2D::create(cw, ch); m_compute_shader->bind(); upload_camera_ubo(cam); upload_disk_ubo(); upload_objects_ubo(m_objects); m_texture->bind_as_image(0, false); uint32_t groups_x = (uint32_t)std::ceil(cw / 16.0f); uint32_t groups_y = (uint32_t)std::ceil(ch / 16.0f); m_compute_shader->dispatch(groups_x, groups_y, 1); m_compute_shader->memory_barrier(IMAGE_ACCESS_BARRIER_BIT); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); } void Engine::upload_camera_ubo(const Camera& cam) { 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(cam.get_target() - cam.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 = cam.get_orbital_position(); data.right = right; data.up = up; data.forward = fwd; data.tan_half_fov = static_cast(tan(glm::radians(60.0f * 0.5f))); data.aspect = static_cast(m_width) / static_cast(m_height); data.moving = cam.is_dragging() || cam.is_panning(); m_camera_ubo->set_data(&data, sizeof(UBOData)); } void Engine::upload_objects_ubo(const std::vector& objs) { 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(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)); } void Engine::upload_disk_ubo() { float r1 = static_cast(m_sag_a.m_rs * 2.2); float r2 = static_cast(m_sag_a.m_rs * 5.2); float num = 2.0f; float thickness = 1e9f; float disk_data[4] = { r1, r2, num, thickness }; m_disk_ubo->set_data(disk_data, sizeof(disk_data)); } void Engine::update_physics(float delta_time) { 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 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 acc = {direction[0] * acc1, direction[1] * acc1, direction[2] * acc1}; if (m_gravity) { obj.m_velocity.x += static_cast(acc[0]); obj.m_velocity.y += static_cast(acc[1]); obj.m_velocity.z += static_cast(acc[2]); obj.m_pos_radius.x += static_cast(obj.m_velocity.x); obj.m_pos_radius.y += static_cast(obj.m_velocity.y); obj.m_pos_radius.z += static_cast(obj.m_velocity.z); } } } } } void Engine::render_scene() { glm::mat4 view = glm::lookAt(m_camera.get_orbital_position(), m_camera.get_target(), glm::vec3(0,1,0)); glm::mat4 proj = glm::perspective(glm::radians(60.0f), float(m_width)/m_height, 1e9f, 1e14f); glm::mat4 view_proj = proj * view; generate_grid(m_objects); draw_grid(view_proj); RenderCommand::set_viewport(0, 0, m_width, m_height); dispatch_compute(m_camera); draw_full_screen_quad(); } std::string Engine::load_compute_shader(const std::string& path) { std::ifstream in(path); if(!in.is_open()) { std::cerr << "Failed to open compute shader: " << path << "\n"; return ""; } std::stringstream ss; ss << in.rdbuf(); std::string result = ss.str(); if (result.empty()) std::cerr << "Warning: Compute shader file is empty: " << path << "\n"; else std::cout << "Successfully loaded compute shader: " << path << " (size: " << result.size() << " bytes)\n"; return result; } }