diff options
| author | hachem <im@hachem.wtf> | 2025-08-14 18:21:00 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2025-08-14 18:21:00 +0200 |
| commit | 3d8d3259e1cf5a7d88e5d1e530cc8c9bf9140734 (patch) | |
| tree | 450b674caaf60327104aa76375c045f1f8f77076 /src/engine | |
| parent | 79045760ce6100b4c87846d793f9dceeba4c9c36 (diff) | |
feat: I am going to throw my computer out a window
Diffstat (limited to 'src/engine')
| -rw-r--r-- | src/engine/engine.cpp | 347 | ||||
| -rw-r--r-- | src/engine/engine.h | 118 | ||||
| -rw-r--r-- | src/engine/object.cpp | 46 | ||||
| -rw-r--r-- | src/engine/object.h | 38 | ||||
| -rw-r--r-- | src/engine/scene.cpp | 57 | ||||
| -rw-r--r-- | src/engine/scene.h | 20 |
6 files changed, 626 insertions, 0 deletions
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp new file mode 100644 index 0000000..29fa65a --- /dev/null +++ b/src/engine/engine.cpp @@ -0,0 +1,347 @@ +#include "engine.h" +#include <GLFW/glfw3.h> +#include <glad/glad.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <limits> +#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<float>(8.54e36)) + { + GLFWwindow* window = static_cast<GLFWwindow*>(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<float>(1.98892e30) }, + { glm::vec4(0.0f, 0.0f, 4e11f, 4e10f), glm::vec4(1,0,0,1), static_cast<float>(1.98892e30) }, + { glm::vec4(0.0f, 0.0f, 0.0f, m_sag_a.m_rs), glm::vec4(0,0,0,1), static_cast<float>(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<uint32_t>(sizeof(quad_vertices))); + VertexBufferLayout layout; + layout.push<float>(2); + layout.push<float>(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<ObjectData>& objects) + { + const int grid_size = 25; + const float spacing = 1e10f; + + std::vector<glm::vec3> vertices; + std::vector<uint32_t> 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<float>(delta_y) - 3e10f; + } + else + { + y += 2.0f * static_cast<float>(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>(VertexArray::create()); + + auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(vertices.data(), static_cast<uint32_t>(vertices.size() * sizeof(glm::vec3)))); + VertexBufferLayout layout; + layout.push<float>(3); + vertex_buffer->set_layout(layout); + m_grid_vao->add_vertex_buffer(vertex_buffer); + + auto index_buffer = Ref<IndexBuffer>(IndexBuffer::create(indices.data(), static_cast<uint32_t>(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<uint32_t>(vertices.size() * sizeof(glm::vec3))); + } + + m_grid_index_count = static_cast<int>(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<GLFWwindow*>(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<float>(tan(glm::radians(60.0f * 0.5f))); + data.aspect = static_cast<float>(m_width) / static_cast<float>(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<ObjectData>& 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<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)); + } + + void Engine::upload_disk_ubo() + { + float r1 = static_cast<float>(m_sag_a.m_rs * 2.2); + float r2 = static_cast<float>(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<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); + } + } + } + } + } + + 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; + } +} diff --git a/src/engine/engine.h b/src/engine/engine.h new file mode 100644 index 0000000..6f90efb --- /dev/null +++ b/src/engine/engine.h @@ -0,0 +1,118 @@ +#pragma once + +#include <vector> +#include <iostream> +#include <GLFW/glfw3.h> +#include <glm/glm.hpp> +#include <glm/gtc/matrix_transform.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <fstream> +#include <sstream> +#include <chrono> +#include <cmath> + +#include "core/application.h" +#include "core/camera.h" +#include "rendering/renderer.h" +#include "rendering/shader.h" +#include "rendering/texture.h" +#include "rendering/vertex_array.h" +#include "rendering/uniform_buffer.h" + +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; + + void generate_grid(const std::vector<ObjectData>& objects); + void draw_grid(const glm::mat4& view_proj); + void draw_full_screen_quad(); + void dispatch_compute(const Camera& cam); + void upload_camera_ubo(const Camera& cam); + void upload_objects_ubo(const std::vector<ObjectData>& objs); + void upload_disk_ubo(); + void render_scene(); + void update_physics(float delta_time); + void update_window_dimensions(); + + Camera& get_camera() { return m_camera; } + const Camera& get_camera() const { return m_camera; } + + const std::vector<ObjectData>& get_objects() const { return m_objects; } + std::vector<ObjectData>& get_objects() { return m_objects; } + + bool get_gravity() const { return m_gravity; } + void set_gravity(bool gravity) { m_gravity = gravity; } + + int get_width() const { return m_width; } + int get_height() const { return m_height; } + int get_compute_width() const { return m_compute_width; } + int get_compute_height() const { return m_compute_height; } + + private: + std::string create_basic_vertex_shader(); + std::string create_basic_fragment_shader(); + std::string load_compute_shader(const std::string& path); + private: + Ref<Shader> m_grid_shader; + Ref<Shader> m_quad_shader; + Ref<Shader> m_compute_shader; + Ref<Texture2D> m_texture; + Ref<VertexArray> m_quad_vao; + Ref<VertexArray> m_grid_vao; + Ref<UniformBuffer> m_camera_ubo; + Ref<UniformBuffer> m_disk_ubo; + Ref<UniformBuffer> m_objects_ubo; + + int m_grid_index_count = 0; + int m_width; + int m_height; + int m_compute_width; + int m_compute_height; + float m_width_scale = 100000000000.0f; + float m_height_scale = 75000000000.0f; + + std::vector<ObjectData> m_objects; + BlackHole m_sag_a; + Camera m_camera; + bool m_gravity = false; + }; +} diff --git a/src/engine/object.cpp b/src/engine/object.cpp new file mode 100644 index 0000000..2e76b16 --- /dev/null +++ b/src/engine/object.cpp @@ -0,0 +1,46 @@ +#include "object.h" + +namespace Donut +{ + Ray::Ray(glm::vec3 o, glm::vec3 d) + : m_origin(o), + m_direction(glm::normalize(d)) { } + + Material::Material(glm::vec3 c, float s, float e) + : m_color(c), + m_specular(s), + m_emission(e) { } + + Object::Object(glm::vec3 c, float r, Material m) + : m_centre(c), + m_radius(r), + m_material(m) { } + + bool Object::intersect(Ray &ray, float &t) + { + 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; + double discriminant = 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; + } + + glm::vec3 Object::get_normal(glm::vec3 &point) const + { + return glm::normalize(point - m_centre); + } +} diff --git a/src/engine/object.h b/src/engine/object.h new file mode 100644 index 0000000..5f5d86f --- /dev/null +++ b/src/engine/object.h @@ -0,0 +1,38 @@ +#pragma once + +#include <glm/glm.hpp> + +namespace Donut +{ + class Ray + { + public: + glm::vec3 m_direction; + glm::vec3 m_origin; + + Ray(glm::vec3 o, glm::vec3 d); + }; + + class Material + { + public: + glm::vec3 m_color; + float m_specular; + float m_emission; + + Material(glm::vec3 c, float s, float e); + }; + + class Object + { + public: + glm::vec3 m_centre; + float m_radius; + Material m_material; + + Object(glm::vec3 c, float r, Material m); + + bool intersect(Ray &ray, float &t); + glm::vec3 get_normal(glm::vec3 &point) const; + }; +}; diff --git a/src/engine/scene.cpp b/src/engine/scene.cpp new file mode 100644 index 0000000..bfac1aa --- /dev/null +++ b/src/engine/scene.cpp @@ -0,0 +1,57 @@ +#include "scene.h" + +namespace Donut +{ + Scene::Scene() + : m_light_pos(5.0f, 5.0f, 5.0f) { } + + glm::vec3 Scene::trace(Ray &ray) + { + 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); + } +} diff --git a/src/engine/scene.h b/src/engine/scene.h new file mode 100644 index 0000000..abe582e --- /dev/null +++ b/src/engine/scene.h @@ -0,0 +1,20 @@ +#pragma once + +#include <vector> +#include <limits> +#include <glm/glm.hpp> + +#include "object.h" + +namespace Donut +{ + class Scene + { + public: + std::vector<Object> objs; + glm::vec3 m_light_pos; + + Scene(); + glm::vec3 trace(Ray &ray); + }; +}; |
