hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2025-08-14 18:21:00 +0200
committerhachem <im@hachem.wtf>2025-08-14 18:21:00 +0200
commit3d8d3259e1cf5a7d88e5d1e530cc8c9bf9140734 (patch)
tree450b674caaf60327104aa76375c045f1f8f77076
parent79045760ce6100b4c87846d793f9dceeba4c9c36 (diff)
feat: I am going to throw my computer out a window
-rw-r--r--assets/shaders/basic.glsl31
-rw-r--r--assets/shaders/compute.glsl24
-rw-r--r--assets/shaders/geodesic.glsl215
-rw-r--r--assets/shaders/grid.glsl19
-rw-r--r--assets/shaders/image_processing.glsl31
-rw-r--r--assets/shaders/particle_system.glsl58
-rw-r--r--assets/shaders/quad.glsl25
-rw-r--r--assets/shaders/texture_processor.glsl43
-rw-r--r--assets/shaders/textured.glsl41
-rw-r--r--src/core/camera.cpp180
-rw-r--r--src/core/camera.h67
-rw-r--r--src/engine/engine.cpp347
-rw-r--r--src/engine/engine.h118
-rw-r--r--src/engine/object.cpp46
-rw-r--r--src/engine/object.h38
-rw-r--r--src/engine/scene.cpp57
-rw-r--r--src/engine/scene.h20
-rw-r--r--src/platform/opengl/opengl_renderer_api.cpp16
-rw-r--r--src/platform/opengl/opengl_renderer_api.h3
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.cpp29
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.h22
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.cpp12
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.h3
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.cpp21
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.h20
-rw-r--r--src/rendering/index_buffer.cpp6
-rw-r--r--src/rendering/index_buffer.h3
-rw-r--r--src/rendering/renderer.cpp8
-rw-r--r--src/rendering/renderer.h22
-rw-r--r--src/rendering/shader.cpp22
-rw-r--r--src/rendering/shader.h6
-rw-r--r--src/rendering/uniform_buffer.cpp23
-rw-r--r--src/rendering/uniform_buffer.h18
-rw-r--r--src/rendering/vertex_array.cpp6
-rw-r--r--src/rendering/vertex_array.h2
-rw-r--r--src/rendering/vertex_buffer.cpp6
-rw-r--r--src/rendering/vertex_buffer.h6
-rw-r--r--src/states/simulation_state.cpp93
-rw-r--r--src/states/simulation_state.h6
39 files changed, 1411 insertions, 302 deletions
diff --git a/assets/shaders/basic.glsl b/assets/shaders/basic.glsl
deleted file mode 100644
index f6016f7..0000000
--- a/assets/shaders/basic.glsl
+++ /dev/null
@@ -1,31 +0,0 @@
-#type vertex
-#version 330 core
-
-layout(location = 0) in vec3 a_Position;
-layout(location = 1) in vec4 a_Color;
-
-uniform mat4 u_ViewProjection;
-uniform mat4 u_Transform;
-
-out vec3 v_Position;
-out vec4 v_Color;
-
-void main()
-{
- v_Position = a_Position;
- v_Color = a_Color;
- gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
-}
-
-#type fragment
-#version 330 core
-
-layout(location = 0) out vec4 color;
-
-in vec3 v_Position;
-in vec4 v_Color;
-
-void main()
-{
- color = v_Color;
-}
diff --git a/assets/shaders/compute.glsl b/assets/shaders/compute.glsl
deleted file mode 100644
index 9a36a84..0000000
--- a/assets/shaders/compute.glsl
+++ /dev/null
@@ -1,24 +0,0 @@
-#type compute
-#version 430 core
-
-layout(local_size_x = 256) in;
-
-layout(std430, binding = 0) buffer InputBuffer
-{
- float data[];
-} inputBuffer;
-
-layout(std430, binding = 1) buffer OutputBuffer
-{
- float data[];
-} outputBuffer;
-
-uniform float u_Scale;
-uniform float u_Offset;
-
-void main()
-{
- uint index = gl_GlobalInvocationID.x;
- float inputValue = inputBuffer.data[index];
- outputBuffer.data[index] = inputValue * u_Scale + u_Offset;
-}
diff --git a/assets/shaders/geodesic.glsl b/assets/shaders/geodesic.glsl
new file mode 100644
index 0000000..6b077be
--- /dev/null
+++ b/assets/shaders/geodesic.glsl
@@ -0,0 +1,215 @@
+#version 430
+layout(local_size_x = 16, local_size_y = 16) in;
+
+layout(binding = 0, rgba8) writeonly uniform image2D outImage;
+
+layout(std140, binding = 1) uniform Camera
+{
+ vec3 camPos; float _pad0;
+ vec3 camRight; float _pad1;
+ vec3 camUp; float _pad2;
+ vec3 camForward; float _pad3;
+ float tanHalfFov;
+ float aspect;
+ bool moving;
+ int _pad4;
+} cam;
+
+layout(std140, binding = 2) uniform Disk
+{
+ float disk_r1;
+ float disk_r2;
+ float disk_num;
+ float thickness;
+};
+
+layout(std140, binding = 3) uniform Objects
+{
+ int numObjects;
+ vec4 objPosRadius[16];
+ vec4 objColor[16];
+ float mass[16];
+};
+
+const float SagA_rs = 1.269e10;
+const float D_LAMBDA = 1e7;
+const double ESCAPE_R = 1e30;
+
+vec4 objectColor = vec4(0.0);
+vec3 hitCenter = vec3(0.0);
+float hitRadius = 0.0;
+
+struct Ray
+{
+ float x, y, z, radius, theta, phi;
+ float dRadius, dTheta, dPhi;
+ float energy, angularMomentum;
+};
+
+Ray InitRay(vec3 position, vec3 direction)
+{
+ Ray ray;
+ ray.x = position.x;
+ ray.y = position.y;
+ ray.z = position.z;
+ ray.radius = length(position);
+ ray.theta = acos(position.z / ray.radius);
+ ray.phi = atan(position.y, position.x);
+
+ float dx = direction.x, dy = direction.y, dz = direction.z;
+ ray.dRadius = sin(ray.theta) * cos(ray.phi) * dx + sin(ray.theta) * sin(ray.phi) * dy + cos(ray.theta) * dz;
+ ray.dTheta = (cos(ray.theta) * cos(ray.phi) * dx + cos(ray.theta) * sin(ray.phi) * dy - sin(ray.theta) * dz) / ray.radius;
+ ray.dPhi = (-sin(ray.phi) * dx + cos(ray.phi) * dy) / (ray.radius * sin(ray.theta));
+
+ ray.angularMomentum = ray.radius * ray.radius * sin(ray.theta) * ray.dPhi;
+ float f = 1.0 - SagA_rs / ray.radius;
+ float dt_dL = sqrt((ray.dRadius * ray.dRadius) / f + ray.radius * ray.radius * (ray.dTheta * ray.dTheta + sin(ray.theta) * sin(ray.theta) * ray.dPhi * ray.dPhi));
+ ray.energy = f * dt_dL;
+
+ return ray;
+}
+
+bool Intercept(Ray ray, float schwarzschildRadius)
+{
+ return ray.radius <= schwarzschildRadius;
+}
+
+bool InterceptObject(Ray ray)
+{
+ vec3 position = vec3(ray.x, ray.y, ray.z);
+
+ for (int i = 0; i < numObjects; ++i)
+ {
+ vec3 center = objPosRadius[i].xyz;
+ float radius = objPosRadius[i].w;
+
+ if (distance(position, center) <= radius)
+ {
+ objectColor = objColor[i];
+ hitCenter = center;
+ hitRadius = radius;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void GeodesicRHS(Ray ray, out vec3 derivatives1, out vec3 derivatives2)
+{
+ float radius = ray.radius, theta = ray.theta;
+ float dRadius = ray.dRadius, dTheta = ray.dTheta, dPhi = ray.dPhi;
+ float f = 1.0 - SagA_rs / radius;
+ float dt_dL = ray.energy / f;
+
+ derivatives1 = vec3(dRadius, dTheta, dPhi);
+ derivatives2.x = -(SagA_rs / (2.0 * radius * radius)) * f * dt_dL * dt_dL
+ + (SagA_rs / (2.0 * radius * radius * f)) * dRadius * dRadius
+ + radius * (dTheta * dTheta + sin(theta) * sin(theta) * dPhi * dPhi);
+ derivatives2.y = -2.0 * dRadius * dTheta / radius + sin(theta) * cos(theta) * dPhi * dPhi;
+ derivatives2.z = -2.0 * dRadius * dPhi / radius - 2.0 * cos(theta) / sin(theta) * dTheta * dPhi;
+}
+
+void RK4Step(inout Ray ray, float deltaLambda)
+{
+ vec3 k1a, k1b;
+ GeodesicRHS(ray, k1a, k1b);
+
+ ray.radius += deltaLambda * k1a.x;
+ ray.theta += deltaLambda * k1a.y;
+ ray.phi += deltaLambda * k1a.z;
+ ray.dRadius += deltaLambda * k1b.x;
+ ray.dTheta += deltaLambda * k1b.y;
+ ray.dPhi += deltaLambda * k1b.z;
+
+ ray.x = ray.radius * sin(ray.theta) * cos(ray.phi);
+ ray.y = ray.radius * sin(ray.theta) * sin(ray.phi);
+ ray.z = ray.radius * cos(ray.theta);
+}
+
+bool CrossesEquatorialPlane(vec3 oldPosition, vec3 newPosition)
+{
+ bool crossed = (oldPosition.y * newPosition.y < 0.0);
+ float radius = length(vec2(newPosition.x, newPosition.z));
+ return crossed && (radius >= disk_r1 && radius <= disk_r2);
+}
+
+void main()
+{
+ ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
+ int width = imageSize(outImage).x;
+ int height = imageSize(outImage).y;
+
+ if (pixel.x >= width || pixel.y >= height)
+ return;
+
+ float u = (2.0 * (pixel.x + 0.5) / width - 1.0) * cam.aspect * cam.tanHalfFov;
+ float v = (1.0 - 2.0 * (pixel.y + 0.5) / height) * cam.tanHalfFov;
+ vec3 direction = normalize(u * cam.camRight - v * cam.camUp + cam.camForward);
+ Ray ray = InitRay(cam.camPos, direction);
+
+ vec4 color = vec4(0.0);
+ vec3 prevPosition = vec3(ray.x, ray.y, ray.z);
+ float lambda = 0.0;
+
+ bool hitBlackHole = false;
+ bool hitDisk = false;
+ bool hitObject = false;
+
+ int steps = cam.moving ? 60000 : 60000;
+
+ for (int i = 0; i < steps; ++i)
+ {
+ if (Intercept(ray, SagA_rs))
+ {
+ hitBlackHole = true;
+ break;
+ }
+
+ RK4Step(ray, D_LAMBDA);
+ lambda += D_LAMBDA;
+
+ vec3 newPosition = vec3(ray.x, ray.y, ray.z);
+
+ if (CrossesEquatorialPlane(prevPosition, newPosition))
+ {
+ hitDisk = true;
+ break;
+ }
+
+ if (InterceptObject(ray))
+ {
+ hitObject = true;
+ break;
+ }
+
+ prevPosition = newPosition;
+
+ if (ray.radius > ESCAPE_R)
+ break;
+ }
+
+ if (hitDisk)
+ {
+ double radius = length(vec3(ray.x, ray.y, ray.z)) / disk_r2;
+ vec3 diskColor = vec3(1.0, radius, 0.2);
+ color = vec4(diskColor, radius);
+ }
+ else if (hitBlackHole)
+ color = vec4(0.0, 0.0, 0.0, 1.0);
+ else if (hitObject)
+ {
+ vec3 position = vec3(ray.x, ray.y, ray.z);
+ vec3 normal = normalize(position - hitCenter);
+ vec3 viewDirection = normalize(cam.camPos - position);
+ float ambient = 0.1;
+ float diffuse = max(dot(normal, viewDirection), 0.0);
+ float intensity = ambient + (1.0 - ambient) * diffuse;
+ vec3 shaded = objectColor.rgb * intensity;
+ color = vec4(shaded, objectColor.a);
+ }
+ else
+ color = vec4(0.0);
+
+ imageStore(outImage, pixel, color);
+}
diff --git a/assets/shaders/grid.glsl b/assets/shaders/grid.glsl
new file mode 100644
index 0000000..66096c4
--- /dev/null
+++ b/assets/shaders/grid.glsl
@@ -0,0 +1,19 @@
+#type vertex
+#version 330 core
+layout (location = 0) in vec3 aPos;
+
+uniform mat4 viewProj;
+
+void main()
+{
+ gl_Position = viewProj * vec4(aPos, 1.0);
+}
+
+#type fragment
+#version 330 core
+out vec4 FragColor;
+
+void main()
+{
+ FragColor = vec4(0.5, 0.5, 0.5, 0.3);
+}
diff --git a/assets/shaders/image_processing.glsl b/assets/shaders/image_processing.glsl
deleted file mode 100644
index 3a05425..0000000
--- a/assets/shaders/image_processing.glsl
+++ /dev/null
@@ -1,31 +0,0 @@
-#type compute
-#version 430 core
-
-layout(local_size_x = 16, local_size_y = 16) in;
-
-layout(binding = 0) uniform sampler2D u_InputTexture;
-layout(binding = 1, rgba8) uniform image2D u_OutputTexture;
-
-uniform vec2 u_TextureSize;
-uniform float u_Brightness;
-uniform float u_Contrast;
-uniform float u_Saturation;
-
-void main()
-{
- ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
-
- if (pixelCoord.x >= int(u_TextureSize.x) || pixelCoord.y >= int(u_TextureSize.y))
- return;
-
- vec2 texCoord = (vec2(pixelCoord) + 0.5) / u_TextureSize;
- vec4 color = texture(u_InputTexture, texCoord);
-
- color.rgb = (color.rgb - 0.5) * u_Contrast + 0.5 + u_Brightness;
-
- float luminance = dot(color.rgb, vec3(0.299, 0.587, 0.114));
- color.rgb = mix(vec3(luminance), color.rgb, u_Saturation);
- color.rgb = clamp(color.rgb, 0.0, 1.0);
-
- imageStore(u_OutputTexture, pixelCoord, color);
-}
diff --git a/assets/shaders/particle_system.glsl b/assets/shaders/particle_system.glsl
deleted file mode 100644
index 1e9d435..0000000
--- a/assets/shaders/particle_system.glsl
+++ /dev/null
@@ -1,58 +0,0 @@
-#type compute
-#version 430 core
-
-layout(local_size_x = 256) in;
-
-struct Particle
-{
- vec4 position;
- vec4 velocity;
- vec4 color;
-};
-
-layout(std430, binding = 0) buffer ParticleBuffer
-{
- Particle particles[];
-} particleBuffer;
-
-uniform float u_DeltaTime;
-uniform vec3 u_Gravity;
-uniform vec3 u_EmitterPosition;
-uniform float u_EmissionRate;
-uniform float u_ParticleLifetime;
-uniform float u_Time;
-
-void main()
-{
- uint index = gl_GlobalInvocationID.x;
-
- if (index >= particles.length())
- return;
-
- Particle particle = particleBuffer.particles[index];
-
- particle.position.w -= u_DeltaTime;
-
- if (particle.position.w <= 0.0)
- {
- particle.position.xyz = u_EmitterPosition;
- particle.position.w = u_ParticleLifetime;
-
- float angle = fract(sin(dot(vec2(index, u_Time), vec2(12.9898, 78.233))) * 43758.5453) * 2.0 * 3.14159;
- float speed = 2.0 + fract(sin(dot(vec2(index + 1, u_Time), vec2(12.9898, 78.233))) * 43758.5453) * 3.0;
- particle.velocity.xyz = vec3(cos(angle) * speed, 5.0, sin(angle) * speed);
- particle.velocity.w = 1.0;
-
- particle.color = vec4(1.0, 0.5 + 0.5 * fract(sin(index * 123.456) * 43758.5453), 0.2, 1.0);
- }
- else
- {
- particle.velocity.xyz += u_Gravity * u_DeltaTime;
- particle.position.xyz += particle.velocity.xyz * u_DeltaTime;
-
- float lifeRatio = particle.position.w / u_ParticleLifetime;
- particle.color.a = lifeRatio;
- }
-
- particleBuffer.particles[index] = particle;
-}
diff --git a/assets/shaders/quad.glsl b/assets/shaders/quad.glsl
new file mode 100644
index 0000000..57693a6
--- /dev/null
+++ b/assets/shaders/quad.glsl
@@ -0,0 +1,25 @@
+#type vertex
+#version 330 core
+layout(location = 0) in vec2 a_Pos;
+layout(location = 1) in vec2 a_TexCoord;
+
+out vec2 v_TexCoord;
+
+void main()
+{
+ gl_Position = vec4(a_Pos.xy, 0.0, 1.0);
+ v_TexCoord = a_TexCoord;
+}
+
+#type fragment
+#version 330 core
+out vec4 FragColor;
+
+in vec2 v_TexCoord;
+
+uniform sampler2D screenTexture;
+
+void main()
+{
+ FragColor = texture(screenTexture, v_TexCoord);
+}
diff --git a/assets/shaders/texture_processor.glsl b/assets/shaders/texture_processor.glsl
deleted file mode 100644
index a234ac8..0000000
--- a/assets/shaders/texture_processor.glsl
+++ /dev/null
@@ -1,43 +0,0 @@
-#type compute
-#version 430 core
-
-layout(local_size_x = 16, local_size_y = 16) in;
-
-layout(binding = 0) uniform sampler2D u_InputTexture;
-layout(binding = 1, rgba8) uniform image2D u_OutputTexture;
-
-uniform vec2 u_TextureSize;
-uniform float u_Time;
-uniform float u_Brightness;
-uniform float u_Contrast;
-uniform float u_Saturation;
-
-void main()
-{
- ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
-
- if (pixelCoord.x >= int(u_TextureSize.x) || pixelCoord.y >= int(u_TextureSize.y))
- return;
-
- vec2 texCoord = (vec2(pixelCoord) + 0.5) / u_TextureSize;
- vec4 color = texture(u_InputTexture, texCoord);
-
- float time = u_Time * 2.0f;
- float u = texCoord.x;
- float v = texCoord.y;
-
- float wave = sin(u * 10.0f + time) * cos(v * 10.0f + time * 0.5f);
- wave = (wave + 1.0f) * 0.5f;
-
- vec4 animatedColor = vec4(wave, 1.0f - wave, (u + v) * 0.5f, 1.0f);
- color = mix(color, animatedColor, 0.3f);
-
- color.rgb = (color.rgb - 0.5) * u_Contrast + 0.5 + u_Brightness;
-
- float luminance = dot(color.rgb, vec3(0.299, 0.587, 0.114));
- color.rgb = mix(vec3(luminance), color.rgb, u_Saturation);
-
- color.rgb = clamp(color.rgb, 0.0, 1.0);
-
- imageStore(u_OutputTexture, pixelCoord, color);
-}
diff --git a/assets/shaders/textured.glsl b/assets/shaders/textured.glsl
deleted file mode 100644
index ad135fe..0000000
--- a/assets/shaders/textured.glsl
+++ /dev/null
@@ -1,41 +0,0 @@
-#type vertex
-#version 330 core
-
-layout(location = 0) in vec3 a_Position;
-layout(location = 1) in vec4 a_Color;
-layout(location = 2) in vec2 a_TexCoord;
-
-uniform mat4 u_ViewProjection;
-uniform mat4 u_Transform;
-
-out vec3 v_Position;
-out vec4 v_Color;
-out vec2 v_TexCoord;
-
-void main()
-{
- v_Position = a_Position;
- v_Color = a_Color;
- v_TexCoord = a_TexCoord;
- gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
-}
-
-#type fragment
-#version 330 core
-
-layout(location = 0) out vec4 color;
-
-in vec3 v_Position;
-in vec4 v_Color;
-in vec2 v_TexCoord;
-
-uniform sampler2D u_Texture;
-
-void main()
-{
- vec4 texColor = texture(u_Texture, v_TexCoord);
- if (texColor.rgb == vec3(0.0))
- color = v_Color;
- else
- color = v_Color * texColor;
-}
diff --git a/src/core/camera.cpp b/src/core/camera.cpp
index f580191..43cb1ee 100644
--- a/src/core/camera.cpp
+++ b/src/core/camera.cpp
@@ -28,22 +28,32 @@ namespace Donut
void Camera::recalculate_view_matrix()
{
- float pitch = glm::radians(m_rotation.x);
- float yaw = glm::radians(m_rotation.y);
- float roll = glm::radians(m_rotation.z);
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ float pitch = glm::radians(m_rotation.x);
+ float yaw = glm::radians(m_rotation.y);
+ float roll = glm::radians(m_rotation.z);
- glm::vec3 direction;
- direction.x = cos(yaw) * cos(pitch);
- direction.y = sin(pitch);
- direction.z = sin(yaw) * cos(pitch);
+ glm::vec3 direction;
+ direction.x = cos(yaw) * cos(pitch);
+ direction.y = sin(pitch);
+ direction.z = sin(yaw) * cos(pitch);
- glm::vec3 world_up(0.0f, 1.0f, 0.0f);
- glm::vec3 front = glm::normalize(direction);
- glm::vec3 right = glm::normalize(glm::cross(front, world_up));
- glm::vec3 up = glm::normalize(glm::cross(right, front));
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ glm::vec3 front = glm::normalize(direction);
+ glm::vec3 right = glm::normalize(glm::cross(front, world_up));
+ glm::vec3 up = glm::normalize(glm::cross(right, front));
- m_view_matrix = glm::lookAt(m_position, m_position + front, up);
- m_view_projection_matrix = m_projection_matrix * m_view_matrix;
+ m_view_matrix = glm::lookAt(m_position, m_position + front, up);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
+ }
+ else if (m_camera_mode == CameraMode::Orbital)
+ {
+ glm::vec3 position = get_orbital_position();
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ m_view_matrix = glm::lookAt(position, m_target, world_up);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
+ }
}
glm::vec3 Camera::get_forward_direction() const
@@ -72,58 +82,152 @@ namespace Donut
void Camera::on_mouse_move(float x_offset, float y_offset, bool constrain_pitch)
{
- x_offset *= m_mouse_sensitivity;
- y_offset *= m_mouse_sensitivity;
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ x_offset *= m_mouse_sensitivity;
+ y_offset *= m_mouse_sensitivity;
- m_rotation.y += x_offset;
- m_rotation.x += y_offset;
+ m_rotation.y += x_offset;
+ m_rotation.x += y_offset;
- if (constrain_pitch)
- {
- if (m_rotation.x > 89.0f)
- m_rotation.x = 89.0f;
- if (m_rotation.x < -89.0f)
- m_rotation.x = -89.0f;
- }
+ if (constrain_pitch)
+ {
+ if (m_rotation.x > 89.0f)
+ m_rotation.x = 89.0f;
+ if (m_rotation.x < -89.0f)
+ m_rotation.x = -89.0f;
+ }
- recalculate_view_matrix();
+ recalculate_view_matrix();
+ }
}
void Camera::move_forward(float delta_time)
{
- m_position += get_forward_direction() * m_movement_speed * delta_time;
- recalculate_view_matrix();
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position += get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
}
void Camera::move_backward(float delta_time)
{
- m_position -= get_forward_direction() * m_movement_speed * delta_time;
- recalculate_view_matrix();
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position -= get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
}
void Camera::move_right(float delta_time)
{
- m_position += get_right_direction() * m_movement_speed * delta_time;
- recalculate_view_matrix();
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position += get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
}
void Camera::move_left(float delta_time)
{
- m_position -= get_right_direction() * m_movement_speed * delta_time;
- recalculate_view_matrix();
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position -= get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
}
void Camera::move_up(float delta_time)
{
- glm::vec3 world_up(0.0f, 1.0f, 0.0f);
- m_position += world_up * m_movement_speed * delta_time;
- recalculate_view_matrix();
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ m_position += world_up * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
}
void Camera::move_down(float delta_time)
{
- glm::vec3 world_up(0.0f, 1.0f, 0.0f);
- m_position -= world_up * m_movement_speed * delta_time;
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ m_position -= world_up * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
+ }
+
+ void Camera::set_orbital_mode(bool enabled)
+ {
+ m_camera_mode = enabled ? CameraMode::Orbital : CameraMode::FPS;
+ recalculate_view_matrix();
+ }
+
+ glm::vec3 Camera::get_orbital_position() const
+ {
+ float clamped_elevation = glm::clamp(m_elevation, 0.01f, float(M_PI) - 0.01f);
+ return glm::vec3(
+ m_radius * sin(clamped_elevation) * cos(m_azimuth),
+ m_radius * cos(clamped_elevation),
+ m_radius * sin(clamped_elevation) * sin(m_azimuth)
+ );
+ }
+
+ void Camera::process_orbital_mouse_move(double x, double y)
+ {
+ if (m_dragging && !m_panning)
+ {
+ float dx = float(x - m_orbital_last_x);
+ float dy = float(y - m_orbital_last_y);
+
+ m_azimuth += dx * m_orbit_speed;
+ m_elevation -= dy * m_orbit_speed;
+ m_elevation = glm::clamp(m_elevation, 0.01f, float(M_PI) - 0.01f);
+ }
+
+ m_orbital_last_x = x;
+ m_orbital_last_y = y;
+ update_orbital_state();
recalculate_view_matrix();
}
+
+ void Camera::process_orbital_mouse_button(int button, int action, int mods)
+ {
+ if (button == GLFW_MOUSE_BUTTON_LEFT)
+ {
+ if (action == GLFW_PRESS)
+ {
+ m_dragging = true;
+ m_panning = false;
+ }
+ else if (action == GLFW_RELEASE)
+ {
+ m_dragging = false;
+ m_panning = false;
+ }
+ }
+ update_orbital_state();
+ }
+
+ void Camera::process_orbital_scroll(double x_offset, double y_offset)
+ {
+ m_radius -= y_offset * m_zoom_speed;
+ m_radius = glm::clamp(m_radius, m_min_radius, m_max_radius);
+ update_orbital_state();
+ recalculate_view_matrix();
+ }
+
+ void Camera::update_orbital_state()
+ {
+ m_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ if (m_dragging || m_panning)
+ {
+ m_moving = true;
+ }
+ else
+ {
+ m_moving = false;
+ }
+ }
}
diff --git a/src/core/camera.h b/src/core/camera.h
index 25f7e2a..7ea5472 100644
--- a/src/core/camera.h
+++ b/src/core/camera.h
@@ -2,9 +2,21 @@
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
+#include <GLFW/glfw3.h>
+
+#define _USE_MATH_DEFINES
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
namespace Donut
{
+ enum class CameraMode
+ {
+ FPS,
+ Orbital
+ };
+
class Camera
{
public:
@@ -42,10 +54,51 @@ namespace Donut
void set_movement_speed(float speed) { m_movement_speed = speed; }
float get_movement_speed() const { return m_movement_speed; }
+ void set_orbital_mode(bool enabled);
+ bool is_orbital_mode() const { return m_camera_mode == CameraMode::Orbital; }
+
+ void set_target(const glm::vec3& target) { m_target = target; }
+ const glm::vec3& get_target() const { return m_target; }
+
+ void set_radius(double radius) { m_radius = radius; }
+ double get_radius() const { return m_radius; }
+
+ void set_min_radius(double min_radius) { m_min_radius = min_radius; }
+ double get_min_radius() const { return m_min_radius; }
+
+ void set_max_radius(double max_radius) { m_max_radius = max_radius; }
+ double get_max_radius() const { return m_max_radius; }
+
+ void set_azimuth(float azimuth) { m_azimuth = azimuth; }
+ float get_azimuth() const { return m_azimuth; }
+
+ void set_elevation(float elevation) { m_elevation = elevation; }
+ float get_elevation() const { return m_elevation; }
+
+ void set_orbit_speed(float orbit_speed) { m_orbit_speed = orbit_speed; }
+ float get_orbit_speed() const { return m_orbit_speed; }
+
+ void set_zoom_speed(double zoom_speed) { m_zoom_speed = zoom_speed; }
+ double get_zoom_speed() const { return m_zoom_speed; }
+
+ glm::vec3 get_orbital_position() const;
+
+ void process_orbital_mouse_move(double x, double y);
+ void process_orbital_mouse_button(int button, int action, int mods);
+ void process_orbital_scroll(double x_offset, double y_offset);
+
+ bool is_dragging() const { return m_dragging; }
+ bool is_panning() const { return m_panning; }
+ bool is_moving() const { return m_moving; }
+
private:
void recalculate_view_matrix();
void recalculate_projection_matrix();
+ void update_orbital_state();
+
private:
+ CameraMode m_camera_mode = CameraMode::FPS;
+
glm::mat4 m_projection_matrix;
glm::mat4 m_view_matrix;
glm::mat4 m_view_projection_matrix;
@@ -63,5 +116,19 @@ namespace Donut
bool m_first_mouse = true;
float m_last_x = 0.0f;
float m_last_y = 0.0f;
+
+ glm::vec3 m_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ double m_radius = 6.34194e10;
+ double m_min_radius = 1e10;
+ double m_max_radius = 1e12;
+ float m_azimuth = 0.0f;
+ float m_elevation = static_cast<float>(M_PI) / 2.0f;
+ float m_orbit_speed = 0.01f;
+ double m_zoom_speed = 25e9f;
+ bool m_dragging = false;
+ bool m_panning = false;
+ bool m_moving = false;
+ double m_orbital_last_x = 0.0;
+ double m_orbital_last_y = 0.0;
};
}
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);
+ };
+};
diff --git a/src/platform/opengl/opengl_renderer_api.cpp b/src/platform/opengl/opengl_renderer_api.cpp
index 4542231..628c0af 100644
--- a/src/platform/opengl/opengl_renderer_api.cpp
+++ b/src/platform/opengl/opengl_renderer_api.cpp
@@ -49,6 +49,11 @@ namespace Donut
glEnable(GL_DEPTH_TEST);
}
+ void OpenGLRendererAPI::disable_depth_test()
+ {
+ glDisable(GL_DEPTH_TEST);
+ }
+
void OpenGLRendererAPI::set_face_culling(bool enabled)
{
if (enabled)
@@ -61,6 +66,17 @@ namespace Donut
glDisable(GL_CULL_FACE);
}
+ void OpenGLRendererAPI::enable_blending()
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ void OpenGLRendererAPI::disable_blending()
+ {
+ glDisable(GL_BLEND);
+ }
+
void OpenGLRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count)
{
uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
diff --git a/src/platform/opengl/opengl_renderer_api.h b/src/platform/opengl/opengl_renderer_api.h
index 25accbb..fd446c6 100644
--- a/src/platform/opengl/opengl_renderer_api.h
+++ b/src/platform/opengl/opengl_renderer_api.h
@@ -19,7 +19,10 @@ namespace Donut
virtual void set_clear_color(const glm::vec4& color) override;
virtual void clear() override;
virtual void enable_depth_test() override;
+ virtual void disable_depth_test() override;
virtual void set_face_culling(bool enabled) override;
+ virtual void enable_blending() override;
+ virtual void disable_blending() override;
virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
uint32_t index_count = 0) override;
diff --git a/src/platform/opengl/opengl_uniform_buffer.cpp b/src/platform/opengl/opengl_uniform_buffer.cpp
new file mode 100644
index 0000000..ca7d3d2
--- /dev/null
+++ b/src/platform/opengl/opengl_uniform_buffer.cpp
@@ -0,0 +1,29 @@
+#include "opengl_uniform_buffer.h"
+
+namespace Donut
+{
+ OpenGLUniformBuffer::OpenGLUniformBuffer(uint32_t size, uint32_t binding)
+ : m_size(size), m_binding(binding)
+ {
+ glGenBuffers(1, &m_renderer_id);
+ glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
+ glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
+ glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
+ }
+
+ OpenGLUniformBuffer::~OpenGLUniformBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ void OpenGLUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset)
+ {
+ glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
+ glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
+ }
+
+ void OpenGLUniformBuffer::bind(uint32_t binding)
+ {
+ glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_renderer_id);
+ }
+};
diff --git a/src/platform/opengl/opengl_uniform_buffer.h b/src/platform/opengl/opengl_uniform_buffer.h
new file mode 100644
index 0000000..2a9131f
--- /dev/null
+++ b/src/platform/opengl/opengl_uniform_buffer.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "rendering/uniform_buffer.h"
+#include <glad/glad.h>
+
+namespace Donut
+{
+ class OpenGLUniformBuffer : public UniformBuffer
+ {
+ public:
+ OpenGLUniformBuffer(uint32_t size, uint32_t binding);
+ virtual ~OpenGLUniformBuffer();
+
+ virtual void set_data(const void* data, uint32_t size, uint32_t offset = 0) override;
+ virtual void bind(uint32_t binding) override;
+
+ private:
+ uint32_t m_renderer_id = 0;
+ uint32_t m_size = 0;
+ uint32_t m_binding = 0;
+ };
+};
diff --git a/src/platform/vulkan/vulkan_renderer_api.cpp b/src/platform/vulkan/vulkan_renderer_api.cpp
index f91a0ea..6273d31 100644
--- a/src/platform/vulkan/vulkan_renderer_api.cpp
+++ b/src/platform/vulkan/vulkan_renderer_api.cpp
@@ -22,10 +22,22 @@ namespace Donut
{
}
+ void VulkanRendererAPI::disable_depth_test()
+ {
+ }
+
void VulkanRendererAPI::set_face_culling(bool enabled)
{
}
+ void VulkanRendererAPI::enable_blending()
+ {
+ }
+
+ void VulkanRendererAPI::disable_blending()
+ {
+ }
+
void VulkanRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count)
{
}
diff --git a/src/platform/vulkan/vulkan_renderer_api.h b/src/platform/vulkan/vulkan_renderer_api.h
index d1394b3..c0075a2 100644
--- a/src/platform/vulkan/vulkan_renderer_api.h
+++ b/src/platform/vulkan/vulkan_renderer_api.h
@@ -15,7 +15,10 @@ namespace Donut
virtual void set_clear_color(const glm::vec4& color) override;
virtual void clear() override;
virtual void enable_depth_test() override;
+ virtual void disable_depth_test() override;
virtual void set_face_culling(bool enabled) override;
+ virtual void enable_blending() override;
+ virtual void disable_blending() override;
virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
uint32_t index_count = 0) override;
diff --git a/src/platform/vulkan/vulkan_uniform_buffer.cpp b/src/platform/vulkan/vulkan_uniform_buffer.cpp
new file mode 100644
index 0000000..6fafeee
--- /dev/null
+++ b/src/platform/vulkan/vulkan_uniform_buffer.cpp
@@ -0,0 +1,21 @@
+#include "vulkan_uniform_buffer.h"
+
+namespace Donut
+{
+ VulkanUniformBuffer::VulkanUniformBuffer(uint32_t size, uint32_t binding)
+ : m_size(size), m_binding(binding)
+ {
+ }
+
+ VulkanUniformBuffer::~VulkanUniformBuffer()
+ {
+ }
+
+ void VulkanUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset)
+ {
+ }
+
+ void VulkanUniformBuffer::bind(uint32_t binding)
+ {
+ }
+};
diff --git a/src/platform/vulkan/vulkan_uniform_buffer.h b/src/platform/vulkan/vulkan_uniform_buffer.h
new file mode 100644
index 0000000..e3c9196
--- /dev/null
+++ b/src/platform/vulkan/vulkan_uniform_buffer.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "rendering/uniform_buffer.h"
+
+namespace Donut
+{
+ class VulkanUniformBuffer : public UniformBuffer
+ {
+ public:
+ VulkanUniformBuffer(uint32_t size, uint32_t binding);
+ virtual ~VulkanUniformBuffer();
+
+ virtual void set_data(const void* data, uint32_t size, uint32_t offset = 0) override;
+ virtual void bind(uint32_t binding) override;
+
+ private:
+ uint32_t m_size = 0;
+ uint32_t m_binding = 0;
+ };
+};
diff --git a/src/rendering/index_buffer.cpp b/src/rendering/index_buffer.cpp
index 1095e3b..3271a5a 100644
--- a/src/rendering/index_buffer.cpp
+++ b/src/rendering/index_buffer.cpp
@@ -6,14 +6,14 @@
namespace Donut
{
- IndexBuffer* IndexBuffer::create(const uint32_t* indices, uint32_t count)
+ Ref<IndexBuffer> IndexBuffer::create(const uint32_t* indices, uint32_t count)
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLIndexBuffer(indices, count);
+ return create_ref<OpenGLIndexBuffer>(indices, count);
case RendererAPI::API::Vulkan:
- return new VulkanIndexBuffer((uint32_t*)indices, count);
+ return create_ref<VulkanIndexBuffer>((uint32_t*)indices, count);
default:
return nullptr;
}
diff --git a/src/rendering/index_buffer.h b/src/rendering/index_buffer.h
index 856559a..9859c20 100644
--- a/src/rendering/index_buffer.h
+++ b/src/rendering/index_buffer.h
@@ -1,5 +1,6 @@
#pragma once
+#include "core/memory.h"
#include <cstdint>
namespace Donut
@@ -13,6 +14,6 @@ namespace Donut
virtual void unbind() const = 0;
virtual uint32_t get_count() const = 0;
- static IndexBuffer* create(const uint32_t* indices, uint32_t count);
+ static Ref<IndexBuffer> create(const uint32_t* indices, uint32_t count);
};
};
diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp
index 5e4682c..f34e2b0 100644
--- a/src/rendering/renderer.cpp
+++ b/src/rendering/renderer.cpp
@@ -7,14 +7,14 @@
namespace Donut
{
- Scope<RendererAPI> RendererAPI::create()
+ Ref<RendererAPI> RendererAPI::create()
{
switch (s_api)
{
case API::OpenGL:
- return create_scope<OpenGLRendererAPI>();
+ return create_ref<OpenGLRendererAPI>();
case API::Vulkan:
- return create_scope<VulkanRendererAPI>();
+ return create_ref<VulkanRendererAPI>();
default:
return nullptr;
}
@@ -50,7 +50,7 @@ namespace Donut
RenderCommand::draw_indexed(vertex_array);
}
- Scope<RendererAPI> RenderCommand::s_renderer_api = RendererAPI::create();
+ Ref<RendererAPI> RenderCommand::s_renderer_api = RendererAPI::create();
void Renderer::set_clear_color(const glm::vec4& color)
{
diff --git a/src/rendering/renderer.h b/src/rendering/renderer.h
index 6212b21..4a028e5 100644
--- a/src/rendering/renderer.h
+++ b/src/rendering/renderer.h
@@ -26,14 +26,17 @@ namespace Donut
virtual void set_clear_color(const glm::vec4& color) = 0;
virtual void clear() = 0;
virtual void enable_depth_test() = 0;
+ virtual void disable_depth_test() = 0;
virtual void set_face_culling(bool enabled) = 0;
+ virtual void enable_blending() = 0;
+ virtual void disable_blending() = 0;
virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
uint32_t index_count = 0) = 0;
inline static API get_api() { return s_api; }
inline static void set_api(API api) { s_api = api; }
- static Scope<RendererAPI> create();
+ static Ref<RendererAPI> create();
private:
static API s_api;
};
@@ -66,18 +69,33 @@ namespace Donut
s_renderer_api->enable_depth_test();
}
+ inline static void disable_depth_test()
+ {
+ s_renderer_api->disable_depth_test();
+ }
+
inline static void set_face_culling(bool enabled)
{
s_renderer_api->set_face_culling(enabled);
}
+ inline static void enable_blending()
+ {
+ s_renderer_api->enable_blending();
+ }
+
+ inline static void disable_blending()
+ {
+ s_renderer_api->disable_blending();
+ }
+
inline static void draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0)
{
s_renderer_api->draw_indexed(vertex_array, index_count);
}
private:
- static Scope<RendererAPI> s_renderer_api;
+ static Ref<RendererAPI> s_renderer_api;
};
class Renderer
diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp
index 1bbd0fc..833b719 100644
--- a/src/rendering/shader.cpp
+++ b/src/rendering/shader.cpp
@@ -6,40 +6,40 @@
namespace Donut
{
- Shader* Shader::create(const std::string& filepath)
+ Ref<Shader> Shader::create(const std::string& filepath)
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLShader(filepath);
+ return create_ref<OpenGLShader>(filepath);
case RendererAPI::API::Vulkan:
- return new VulkanShader(filepath);
+ return create_ref<VulkanShader>(filepath);
default:
return nullptr;
}
}
- Shader* Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
+ Ref<Shader> Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, vertex_src, fragment_src);
+ return create_ref<OpenGLShader>(name, vertex_src, fragment_src);
case RendererAPI::API::Vulkan:
- return new VulkanShader(name, vertex_src, fragment_src);
+ return create_ref<VulkanShader>(name, vertex_src, fragment_src);
default:
return nullptr;
}
}
- Shader* Shader::create_compute(const std::string& name, const std::string& compute_src)
+ Ref<Shader> Shader::create_compute(const std::string& name, const std::string& compute_src)
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, compute_src);
+ return create_ref<OpenGLShader>(name, compute_src);
case RendererAPI::API::Vulkan:
- return new VulkanShader(name, compute_src);
+ return create_ref<VulkanShader>(name, compute_src);
default:
return nullptr;
}
@@ -58,14 +58,14 @@ namespace Donut
Ref<Shader> ShaderLibrary::load(const std::string& filepath)
{
- auto shader = Ref<Shader>(Shader::create(filepath));
+ auto shader = Shader::create(filepath);
add(shader);
return shader;
}
Ref<Shader> ShaderLibrary::load(const std::string& name, const std::string& filepath)
{
- auto shader = Ref<Shader>(Shader::create(filepath));
+ auto shader = Shader::create(filepath);
add(name, shader);
return shader;
}
diff --git a/src/rendering/shader.h b/src/rendering/shader.h
index 814f0cf..7f024bd 100644
--- a/src/rendering/shader.h
+++ b/src/rendering/shader.h
@@ -35,9 +35,9 @@ namespace Donut
virtual const std::string& get_name() const = 0;
- static Shader* create(const std::string& filepath);
- static Shader* create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
- static Shader* create_compute(const std::string& name, const std::string& compute_src);
+ static Ref<Shader> create(const std::string& filepath);
+ static Ref<Shader> create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
+ static Ref<Shader> create_compute(const std::string& name, const std::string& compute_src);
};
class ShaderLibrary
diff --git a/src/rendering/uniform_buffer.cpp b/src/rendering/uniform_buffer.cpp
new file mode 100644
index 0000000..4e693a2
--- /dev/null
+++ b/src/rendering/uniform_buffer.cpp
@@ -0,0 +1,23 @@
+#include "uniform_buffer.h"
+#include "renderer.h"
+
+#include "platform/opengl/opengl_uniform_buffer.h"
+#include "platform/vulkan/vulkan_uniform_buffer.h"
+
+namespace Donut
+{
+ Ref<UniformBuffer> UniformBuffer::create(uint32_t size, uint32_t binding)
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return create_ref<OpenGLUniformBuffer>(size, binding);
+ case RendererAPI::API::Vulkan:
+ return create_ref<VulkanUniformBuffer>(size, binding);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+};
diff --git a/src/rendering/uniform_buffer.h b/src/rendering/uniform_buffer.h
new file mode 100644
index 0000000..4df39d9
--- /dev/null
+++ b/src/rendering/uniform_buffer.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "core/memory.h"
+#include <cstdint>
+
+namespace Donut
+{
+ class UniformBuffer
+ {
+ public:
+ virtual ~UniformBuffer() = default;
+
+ virtual void set_data(const void* data, uint32_t size, uint32_t offset = 0) = 0;
+ virtual void bind(uint32_t binding) = 0;
+
+ static Ref<UniformBuffer> create(uint32_t size, uint32_t binding);
+ };
+};
diff --git a/src/rendering/vertex_array.cpp b/src/rendering/vertex_array.cpp
index bb92ff3..d0cd4b0 100644
--- a/src/rendering/vertex_array.cpp
+++ b/src/rendering/vertex_array.cpp
@@ -17,14 +17,14 @@ namespace Donut
}
}
- VertexArray* VertexArray::create()
+ Ref<VertexArray> VertexArray::create()
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLVertexArray();
+ return create_ref<OpenGLVertexArray>();
case RendererAPI::API::Vulkan:
- return new VulkanVertexArray();
+ return create_ref<VulkanVertexArray>();
default:
return nullptr;
}
diff --git a/src/rendering/vertex_array.h b/src/rendering/vertex_array.h
index a39849f..3729167 100644
--- a/src/rendering/vertex_array.h
+++ b/src/rendering/vertex_array.h
@@ -23,6 +23,6 @@ namespace Donut
virtual const std::vector<Ref<VertexBuffer>>& get_vertex_buffers() const = 0;
virtual const Ref<IndexBuffer>& get_index_buffer() const = 0;
- static VertexArray* create();
+ static Ref<VertexArray> create();
};
};
diff --git a/src/rendering/vertex_buffer.cpp b/src/rendering/vertex_buffer.cpp
index 4929711..ef2b288 100644
--- a/src/rendering/vertex_buffer.cpp
+++ b/src/rendering/vertex_buffer.cpp
@@ -6,14 +6,14 @@
namespace Donut
{
- VertexBuffer* VertexBuffer::create(const void* data, uint32_t size)
+ Ref<VertexBuffer> VertexBuffer::create(const void* data, uint32_t size)
{
switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLVertexBuffer(data, size);
+ return create_ref<OpenGLVertexBuffer>(data, size);
case RendererAPI::API::Vulkan:
- return new VulkanVertexBuffer((float*)data, size);
+ return create_ref<VulkanVertexBuffer>((float*)data, size);
default:
return nullptr;
}
diff --git a/src/rendering/vertex_buffer.h b/src/rendering/vertex_buffer.h
index 77a76c9..39f43ba 100644
--- a/src/rendering/vertex_buffer.h
+++ b/src/rendering/vertex_buffer.h
@@ -1,5 +1,7 @@
#pragma once
+#include "core/memory.h"
+
#include <cstdint>
#include <vector>
@@ -22,7 +24,7 @@ namespace Donut
~VertexBufferLayout() = default;
template<typename T>
- void push(uint32_t count)
+ void push(uint32_t count)
{
static_assert(false);
}
@@ -68,6 +70,6 @@ namespace Donut
virtual const VertexBufferLayout& get_layout() const = 0;
virtual void set_layout(const VertexBufferLayout& layout) = 0;
- static VertexBuffer* create(const void* data, uint32_t size);
+ static Ref<VertexBuffer> create(const void* data, uint32_t size);
};
};
diff --git a/src/states/simulation_state.cpp b/src/states/simulation_state.cpp
index 7e9cb76..fc235ff 100644
--- a/src/states/simulation_state.cpp
+++ b/src/states/simulation_state.cpp
@@ -1,14 +1,18 @@
#include "simulation_state.h"
#include "rendering/renderer.h"
#include "core/application.h"
+#include "core/window.h"
+#include "core/event.h"
#include <imgui.h>
+#include <GLFW/glfw3.h>
namespace Donut
{
void SimulationState::on_enter()
{
DONUT_INFO("Entering Simulation State");
+ m_initialized = true;
}
void SimulationState::on_exit()
@@ -18,23 +22,79 @@ namespace Donut
void SimulationState::on_update(float delta_time)
{
+
+ m_engine.update_window_dimensions();
+ m_engine.update_physics(delta_time);
+
+ if (m_engine.get_camera().is_dragging())
+ {
+ GLFWwindow* window = static_cast<GLFWwindow*>(Application::get().get_window().get_native_window());
+ double xpos, ypos;
+ glfwGetCursorPos(window, &xpos, &ypos);
+ m_engine.get_camera().process_orbital_mouse_move(xpos, ypos);
+ }
}
void SimulationState::on_render()
{
- Renderer::set_clear_color({ 0.0f, 0.0f, 0.0f, 1.0f });
- Renderer::clear();
+ if (!m_initialized) return;
+
+ RenderCommand::set_clear_color(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
+ RenderCommand::clear();
+
+ m_engine.generate_grid(m_engine.get_objects());
+
+ glm::mat4 view = glm::lookAt(m_engine.get_camera().get_orbital_position(), m_engine.get_camera().get_target(), glm::vec3(0,1,0));
+ glm::mat4 proj = glm::perspective(glm::radians(60.0f), float(m_engine.get_width())/m_engine.get_height(), 1e9f, 1e14f);
+ glm::mat4 view_proj = proj * view;
+
+ m_engine.draw_grid(view_proj);
+
+ RenderCommand::set_viewport(0, 0, m_engine.get_width(), m_engine.get_height());
+ m_engine.dispatch_compute(m_engine.get_camera());
+ m_engine.draw_full_screen_quad();
}
void SimulationState::on_event(Event& event)
{
+ if (!m_initialized) return;
+
+ if (event.get_event_type() == EventType::MouseButtonPressed)
+ {
+ MouseButtonPressedEvent& e = (MouseButtonPressedEvent&)event;
+ int button = e.get_mouse_button();
+
+ m_engine.get_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();
+ m_engine.get_camera().process_orbital_mouse_button(button, GLFW_RELEASE, 0);
+ }
+
+ if (event.get_event_type() == EventType::MouseScrolled)
+ {
+ MouseScrolledEvent& e = (MouseScrolledEvent&)event;
+ m_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)
+ {
+ m_engine.set_gravity(!m_engine.get_gravity());
+ DONUT_INFO("Gravity turned {}", m_engine.get_gravity() ? "ON" : "OFF");
+ }
+ }
}
void SimulationState::on_im_ui_render()
{
ImGui::Begin("Simulation Controls");
- ImGui::Text("Simulation State");
+ ImGui::Text("Black Hole Simulation");
if (ImGui::Button("Back to Config"))
Application::get().get_state_manager().switch_to_state("Config");
@@ -43,6 +103,33 @@ namespace Donut
ImGui::Text("Simulation Info:");
ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
ImGui::Text("Frame Time: %.3f ms", 1000.0f / ImGui::GetIO().Framerate);
+ ImGui::Text("Resolution: %dx%d", m_engine.get_width(), m_engine.get_height());
+ ImGui::Text("Compute Resolution: %dx%d", m_engine.get_compute_width(), m_engine.get_compute_height());
+ ImGui::Text("Objects: %zu", m_engine.get_objects().size());
+
+ ImGui::Separator();
+
+ ImGui::Text("Controls:");
+ 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::Separator();
+
+ ImGui::Text("Physics:");
+ bool gravity = m_engine.get_gravity();
+ if (ImGui::Checkbox("Gravity Enabled", &gravity))
+ m_engine.set_gravity(gravity);
+
+ ImGui::Text("Camera:");
+ ImGui::Text("Position: (%.2e, %.2e, %.2e)",
+ m_engine.get_camera().get_orbital_position().x,
+ m_engine.get_camera().get_orbital_position().y,
+ m_engine.get_camera().get_orbital_position().z);
+ ImGui::Text("Radius: %.2e", m_engine.get_camera().get_radius());
+ ImGui::Text("Azimuth: %.2f", m_engine.get_camera().get_azimuth());
+ ImGui::Text("Elevation: %.2f", m_engine.get_camera().get_elevation());
ImGui::End();
}
diff --git a/src/states/simulation_state.h b/src/states/simulation_state.h
index 4bb1c1b..68788e8 100644
--- a/src/states/simulation_state.h
+++ b/src/states/simulation_state.h
@@ -3,6 +3,8 @@
#include "core/state.h"
#include "core/event.h"
#include "core/log.h"
+#include "engine/engine.h"
+#include <vector>
namespace Donut
{
@@ -18,5 +20,9 @@ namespace Donut
void on_render() override;
void on_im_ui_render() override;
void on_event(Event& event) override;
+
+ private:
+ Engine m_engine;
+ bool m_initialized = false;
};
};