aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules28
-rwxr-xr-xTools/compile-shaders.sh10
-rwxr-xr-xTools/fetch-slang.sh6
-rw-r--r--assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttfbin0 -> 904532 bytes
-rw-r--r--assets/fonts/inter/Inter-VariableFont_opsz,wght.ttfbin0 -> 874708 bytes
-rw-r--r--assets/fonts/inter/OFL.txt93
-rw-r--r--assets/fonts/inter/README.txt118
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Black.ttfbin0 -> 344820 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-BlackItalic.ttfbin0 -> 348712 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Bold.ttfbin0 -> 344152 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-BoldItalic.ttfbin0 -> 348180 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-ExtraBold.ttfbin0 -> 345008 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-ExtraBoldItalic.ttfbin0 -> 349064 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-ExtraLight.ttfbin0 -> 343532 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-ExtraLightItalic.ttfbin0 -> 347452 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Italic.ttfbin0 -> 346480 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Light.ttfbin0 -> 343704 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-LightItalic.ttfbin0 -> 347316 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Medium.ttfbin0 -> 343200 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-MediumItalic.ttfbin0 -> 346884 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Regular.ttfbin0 -> 342680 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-SemiBold.ttfbin0 -> 343828 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-SemiBoldItalic.ttfbin0 -> 347760 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-Thin.ttfbin0 -> 343088 bytes
-rw-r--r--assets/fonts/inter/static/Inter_18pt-ThinItalic.ttfbin0 -> 346916 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Black.ttfbin0 -> 344764 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-BlackItalic.ttfbin0 -> 348612 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Bold.ttfbin0 -> 344028 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-BoldItalic.ttfbin0 -> 347948 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-ExtraBold.ttfbin0 -> 344800 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-ExtraBoldItalic.ttfbin0 -> 348896 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-ExtraLight.ttfbin0 -> 343516 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-ExtraLightItalic.ttfbin0 -> 347352 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Italic.ttfbin0 -> 346580 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Light.ttfbin0 -> 343440 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-LightItalic.ttfbin0 -> 347300 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Medium.ttfbin0 -> 342936 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-MediumItalic.ttfbin0 -> 347000 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Regular.ttfbin0 -> 342732 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-SemiBold.ttfbin0 -> 343640 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-SemiBoldItalic.ttfbin0 -> 347616 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-Thin.ttfbin0 -> 342944 bytes
-rw-r--r--assets/fonts/inter/static/Inter_24pt-ThinItalic.ttfbin0 -> 346976 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Black.ttfbin0 -> 344676 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-BlackItalic.ttfbin0 -> 348608 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Bold.ttfbin0 -> 343972 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-BoldItalic.ttfbin0 -> 348084 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-ExtraBold.ttfbin0 -> 344692 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-ExtraBoldItalic.ttfbin0 -> 348944 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-ExtraLight.ttfbin0 -> 343184 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-ExtraLightItalic.ttfbin0 -> 347268 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Italic.ttfbin0 -> 346528 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Light.ttfbin0 -> 343092 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-LightItalic.ttfbin0 -> 347092 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Medium.ttfbin0 -> 342808 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-MediumItalic.ttfbin0 -> 346900 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Regular.ttfbin0 -> 342484 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-SemiBold.ttfbin0 -> 343244 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-SemiBoldItalic.ttfbin0 -> 347380 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-Thin.ttfbin0 -> 342904 bytes
-rw-r--r--assets/fonts/inter/static/Inter_28pt-ThinItalic.ttfbin0 -> 346980 bytes
-rw-r--r--assets/hdri/HDR_blue_nebulae-1.hdrbin0 -> 62208386 bytes
-rw-r--r--assets/hdri/HDR_subdued_blue_nebulae.hdrbin0 -> 62041084 bytes
-rw-r--r--assets/hdri/HDR_subdued_multi_nebulae.hdrbin0 -> 64541302 bytes
-rw-r--r--assets/hdri/night_sky.hdrbin0 -> 51708063 bytes
-rw-r--r--assets/shaders/Blur.slang60
-rw-r--r--assets/shaders/EquirectToCubemap.slang33
-rw-r--r--assets/shaders/Geodesic.slang490
-rw-r--r--assets/shaders/Grid.slang34
-rw-r--r--assets/shaders/Skybox.slang23
-rw-r--r--assets/shaders/Sphere.slang67
-rw-r--r--assets/shaders/TexturedQuad.slang28
-rw-r--r--assets/shaders/VkPipelineTest.slang25
-rw-r--r--branding/Logo-Black.jpgbin0 -> 31049 bytes
-rw-r--r--branding/Logo-Monochrome-Black.jpgbin0 -> 25555 bytes
-rw-r--r--branding/Logo-Monochrome-White.jpgbin0 -> 26074 bytes
-rw-r--r--branding/Logo-White.jpgbin0 -> 31870 bytes
-rw-r--r--branding/Screenshot1.pngbin0 -> 566012 bytes
-rw-r--r--branding/Screenshot2.pngbin0 -> 916806 bytes
m---------ext/ImGuizmo (renamed from Vendor/ImGuizmo)0
-rw-r--r--ext/glad/include/KHR/khrplatform.h (renamed from Vendor/glad/include/KHR/khrplatform.h)0
-rw-r--r--ext/glad/include/glad/glad.h (renamed from Vendor/glad/include/glad/glad.h)0
-rw-r--r--ext/glad/src/glad.c (renamed from Vendor/glad/src/glad.c)0
m---------ext/glfw (renamed from Vendor/glfw)0
m---------ext/glm (renamed from Vendor/glm)0
m---------ext/imgui (renamed from Vendor/imgui)0
m---------ext/json (renamed from Vendor/json)0
m---------ext/stb (renamed from Vendor/stb)0
m---------ext/toml11 (renamed from Vendor/toml11)0
-rw-r--r--premake5.lua200
-rw-r--r--src/Core/Application.cpp216
-rw-r--r--src/Core/Application.h66
-rw-r--r--src/Core/Camera.cpp214
-rw-r--r--src/Core/Camera.h193
-rw-r--r--src/Core/Event.h206
-rw-r--r--src/Core/HDRIManager.h47
-rw-r--r--src/Core/Log.cpp154
-rw-r--r--src/Core/Log.h147
-rw-r--r--src/Core/Memory.h16
-rw-r--r--src/Core/SettingsManager.cpp200
-rw-r--r--src/Core/SettingsManager.h80
-rw-r--r--src/Core/State.h18
-rw-r--r--src/Core/StateManager.cpp88
-rw-r--r--src/Core/StateManager.h40
-rw-r--r--src/Core/ThemeManager.h27
-rw-r--r--src/Core/Window.cpp220
-rw-r--r--src/Core/Window.h94
-rw-r--r--src/Engine/Engine.cpp530
-rw-r--r--src/Engine/Engine.h210
-rw-r--r--src/Engine/Object.h73
-rw-r--r--src/Engine/Scene.h71
-rw-r--r--src/Platform/OpenGL/OpenGLFramebuffer.h35
-rw-r--r--src/Platform/OpenGL/OpenGLIndexBuffer.cpp28
-rw-r--r--src/Platform/OpenGL/OpenGLIndexBuffer.h22
-rw-r--r--src/Platform/OpenGL/OpenGLRendererAPI.h44
-rw-r--r--src/Platform/OpenGL/OpenGLShader.cpp302
-rw-r--r--src/Platform/OpenGL/OpenGLShader.h58
-rw-r--r--src/Platform/OpenGL/OpenGLTexture.h68
-rw-r--r--src/Platform/OpenGL/OpenGLUniformBuffer.cpp29
-rw-r--r--src/Platform/OpenGL/OpenGLUniformBuffer.h21
-rw-r--r--src/Platform/OpenGL/OpenGLVertexArray.cpp58
-rw-r--r--src/Platform/OpenGL/OpenGLVertexArray.h41
-rw-r--r--src/Platform/OpenGL/OpenGLVertexBuffer.cpp35
-rw-r--r--src/Platform/OpenGL/OpenGLVertexBuffer.h25
-rw-r--r--src/Platform/Vulkan/VulkanIndexBuffer.h22
-rw-r--r--src/Platform/Vulkan/VulkanRendererAPI.h38
-rw-r--r--src/Platform/Vulkan/VulkanShader.h52
-rw-r--r--src/Platform/Vulkan/VulkanTexture.cpp73
-rw-r--r--src/Platform/Vulkan/VulkanTexture.h60
-rw-r--r--src/Platform/Vulkan/VulkanUniformBuffer.h19
-rw-r--r--src/Platform/Vulkan/VulkanVertexArray.h28
-rw-r--r--src/Platform/Vulkan/VulkanVertexBuffer.h26
-rw-r--r--src/Rendering/Framebuffer.cpp12
-rw-r--r--src/Rendering/Framebuffer.h30
-rw-r--r--src/Rendering/IndexBuffer.h18
-rw-r--r--src/Rendering/Renderer.cpp54
-rw-r--r--src/Rendering/Renderer.h154
-rw-r--r--src/Rendering/Shader.cpp60
-rw-r--r--src/Rendering/Shader.h48
-rw-r--r--src/Rendering/Texture.cpp40
-rw-r--r--src/Rendering/Texture.h24
-rw-r--r--src/Rendering/TextureManager.cpp34
-rw-r--r--src/Rendering/TextureManager.h19
-rw-r--r--src/Rendering/UniformBuffer.cpp23
-rw-r--r--src/Rendering/UniformBuffer.h18
-rw-r--r--src/Rendering/VertexArray.h28
-rw-r--r--src/Rendering/VertexBuffer.h73
-rw-r--r--src/States/ConfigState.cpp307
-rw-r--r--src/States/ConfigState.h47
-rw-r--r--src/States/SimulationState.cpp386
-rw-r--r--src/States/SimulationState.h27
-rw-r--r--src/States/WorldBuilderState.cpp1113
-rw-r--r--src/States/WorldBuilderState.h96
-rw-r--r--src/core/application.cpp238
-rw-r--r--src/core/application.h53
-rw-r--r--src/core/camera.cpp225
-rw-r--r--src/core/camera.h131
-rw-r--r--src/core/event.h362
-rw-r--r--src/core/hdri_manager.cpp (renamed from src/Core/HDRIManager.cpp)36
-rw-r--r--src/core/hdri_manager.h48
-rw-r--r--src/core/log.cpp156
-rw-r--r--src/core/log.h156
-rw-r--r--src/core/memory.h24
-rw-r--r--src/core/settings_manager.cpp200
-rw-r--r--src/core/settings_manager.h82
-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/core/theme_manager.cpp (renamed from src/Core/ThemeManager.cpp)20
-rw-r--r--src/core/theme_manager.h27
-rw-r--r--src/core/window.cpp341
-rw-r--r--src/core/window.h78
-rw-r--r--src/engine/engine.cpp614
-rw-r--r--src/engine/engine.h184
-rw-r--r--src/engine/object.h76
-rw-r--r--src/engine/scene.h70
-rw-r--r--src/main.cpp4
-rw-r--r--src/platform/metal/metal_context.h (renamed from src/Platform/Metal/MetalContext.h)4
-rw-r--r--src/platform/metal/metal_context.mm (renamed from src/Platform/Metal/MetalContext.mm)8
-rw-r--r--src/platform/opengl/opengl_framebuffer.cpp (renamed from src/Platform/OpenGL/OpenGLFramebuffer.cpp)130
-rw-r--r--src/platform/opengl/opengl_framebuffer.h35
-rw-r--r--src/platform/opengl/opengl_index_buffer.cpp28
-rw-r--r--src/platform/opengl/opengl_index_buffer.h22
-rw-r--r--src/platform/opengl/opengl_renderer_api.cpp (renamed from src/Platform/OpenGL/OpenGLRendererAPI.cpp)46
-rw-r--r--src/platform/opengl/opengl_renderer_api.h44
-rw-r--r--src/platform/opengl/opengl_shader.cpp302
-rw-r--r--src/platform/opengl/opengl_shader.h58
-rw-r--r--src/platform/opengl/opengl_texture.cpp (renamed from src/Platform/OpenGL/OpenGLTexture.cpp)190
-rw-r--r--src/platform/opengl/opengl_texture.h68
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.cpp29
-rw-r--r--src/platform/opengl/opengl_uniform_buffer.h21
-rw-r--r--src/platform/opengl/opengl_vertex_array.cpp58
-rw-r--r--src/platform/opengl/opengl_vertex_array.h41
-rw-r--r--src/platform/opengl/opengl_vertex_buffer.cpp35
-rw-r--r--src/platform/opengl/opengl_vertex_buffer.h25
-rw-r--r--src/platform/vulkan/vulkan_context.cpp (renamed from src/Platform/Vulkan/VulkanContext.cpp)414
-rw-r--r--src/platform/vulkan/vulkan_context.h (renamed from src/Platform/Vulkan/VulkanContext.h)16
-rw-r--r--src/platform/vulkan/vulkan_index_buffer.cpp (renamed from src/Platform/Vulkan/VulkanIndexBuffer.cpp)8
-rw-r--r--src/platform/vulkan/vulkan_index_buffer.h22
-rw-r--r--src/platform/vulkan/vulkan_renderer.cpp (renamed from src/Platform/Vulkan/VulkanRenderer.cpp)912
-rw-r--r--src/platform/vulkan/vulkan_renderer.h (renamed from src/Platform/Vulkan/VulkanRenderer.h)18
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.cpp (renamed from src/Platform/Vulkan/VulkanRendererAPI.cpp)34
-rw-r--r--src/platform/vulkan/vulkan_renderer_api.h38
-rw-r--r--src/platform/vulkan/vulkan_shader.cpp (renamed from src/Platform/Vulkan/VulkanShader.cpp)56
-rw-r--r--src/platform/vulkan/vulkan_shader.h52
-rw-r--r--src/platform/vulkan/vulkan_texture.cpp73
-rw-r--r--src/platform/vulkan/vulkan_texture.h60
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.cpp (renamed from src/Platform/Vulkan/VulkanUniformBuffer.cpp)8
-rw-r--r--src/platform/vulkan/vulkan_uniform_buffer.h19
-rw-r--r--src/platform/vulkan/vulkan_vertex_array.cpp (renamed from src/Platform/Vulkan/VulkanVertexArray.cpp)14
-rw-r--r--src/platform/vulkan/vulkan_vertex_array.h28
-rw-r--r--src/platform/vulkan/vulkan_vertex_buffer.cpp (renamed from src/Platform/Vulkan/VulkanVertexBuffer.cpp)8
-rw-r--r--src/platform/vulkan/vulkan_vertex_buffer.h26
-rw-r--r--src/rendering/framebuffer.cpp17
-rw-r--r--src/rendering/framebuffer.h65
-rw-r--r--src/rendering/index_buffer.cpp (renamed from src/Rendering/IndexBuffer.cpp)12
-rw-r--r--src/rendering/index_buffer.h18
-rw-r--r--src/rendering/renderer.cpp64
-rw-r--r--src/rendering/renderer.h158
-rw-r--r--src/rendering/shader.cpp84
-rw-r--r--src/rendering/shader.h58
-rw-r--r--src/rendering/texture.cpp68
-rw-r--r--src/rendering/texture.h42
-rw-r--r--src/rendering/texture_manager.cpp34
-rw-r--r--src/rendering/texture_manager.h19
-rw-r--r--src/rendering/uniform_buffer.cpp23
-rw-r--r--src/rendering/uniform_buffer.h18
-rw-r--r--src/rendering/vertex_array.cpp (renamed from src/Rendering/VertexArray.cpp)14
-rw-r--r--src/rendering/vertex_array.h28
-rw-r--r--src/rendering/vertex_buffer.cpp (renamed from src/Rendering/VertexBuffer.cpp)12
-rw-r--r--src/rendering/vertex_buffer.h73
-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.cpp1113
-rw-r--r--src/states/world_builder_state.h96
-rwxr-xr-xtools/compile-shaders.sh110
-rwxr-xr-xtools/fetch-slang.sh29
240 files changed, 10689 insertions, 6280 deletions
diff --git a/.gitignore b/.gitignore
index c27381f..dc948ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,5 +24,5 @@ Makefile
*.vcxproj
*.vcxproj.*
-Tools/slang/
-Assets/Shaders/generated/
+tools/slang/
+assets/shaders/generated/
diff --git a/.gitmodules b/.gitmodules
index a19e60c..658ebf8 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,22 +1,22 @@
-[submodule "Vendor/glfw"]
- path = Vendor/glfw
+[submodule "ext/glfw"]
+ path = ext/glfw
url = https://github.com/glfw/glfw.git
-[submodule "Vendor/glm"]
- path = Vendor/glm
+[submodule "ext/glm"]
+ path = ext/glm
url = https://github.com/g-truc/glm.git
-[submodule "Vendor/imgui"]
- path = Vendor/imgui
+[submodule "ext/imgui"]
+ path = ext/imgui
url = https://github.com/ocornut/imgui.git
branch = docking
-[submodule "Vendor/toml11"]
- path = Vendor/toml11
+[submodule "ext/toml11"]
+ path = ext/toml11
url = https://github.com/ToruNiina/toml11
-[submodule "Vendor/ImGuizmo"]
- path = Vendor/ImGuizmo
+[submodule "ext/ImGuizmo"]
+ path = ext/ImGuizmo
url = https://github.com/CedricGuillemet/ImGuizmo.git
-[submodule "Vendor/json"]
- path = Vendor/json
+[submodule "ext/json"]
+ path = ext/json
url = https://github.com/nlohmann/json.git
-[submodule "Vendor/stb"]
- path = Vendor/stb
+[submodule "ext/stb"]
+ path = ext/stb
url = https://github.com/nothings/stb.git
diff --git a/Tools/compile-shaders.sh b/Tools/compile-shaders.sh
index ea1aa15..1f58c2a 100755
--- a/Tools/compile-shaders.sh
+++ b/Tools/compile-shaders.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
#
-# Compiles every Assets/Shaders/*.slang to all backend targets, into
-# Assets/Shaders/generated/ (gitignored):
+# Compiles every assets/shaders/*.slang to all backend targets, into
+# assets/shaders/generated/ (gitignored):
#
# OpenGL : <name>.glsl - combined "#type" GLSL 4.10 (Slang -> SPIR-V ->
# SPIRV-Cross), macOS-4.1-core valid, loaded by
@@ -14,12 +14,12 @@
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
-SLANGC="${SLANGC:-$ROOT/Tools/slang/bin/slangc}"
+SLANGC="${SLANGC:-$ROOT/tools/slang/bin/slangc}"
SPIRV_CROSS="${SPIRV_CROSS:-$(command -v spirv-cross || echo spirv-cross)}"
-SRC="$ROOT/Assets/Shaders"
+SRC="$ROOT/assets/Shaders"
OUT="$SRC/generated"
-[ -x "$SLANGC" ] || { echo "compile-shaders: slangc not found at $SLANGC (run Tools/fetch-slang.sh)"; exit 1; }
+[ -x "$SLANGC" ] || { echo "compile-shaders: slangc not found at $SLANGC (run tools/fetch-slang.sh)"; exit 1; }
command -v "$SPIRV_CROSS" >/dev/null 2>&1 || { echo "compile-shaders: spirv-cross not found (brew install spirv-cross)"; exit 1; }
mkdir -p "$OUT"
diff --git a/Tools/fetch-slang.sh b/Tools/fetch-slang.sh
index 5ee6de5..5229bf7 100755
--- a/Tools/fetch-slang.sh
+++ b/Tools/fetch-slang.sh
@@ -1,15 +1,15 @@
#!/usr/bin/env bash
#
-# Fetches the Slang shader-compiler toolchain into Tools/slang (gitignored).
+# Fetches the Slang shader-compiler toolchain into tools/slang (gitignored).
# Slang is the single shader-authoring language for Donut; slangc cross-compiles
-# each Assets/Shaders/*.slang to GLSL (OpenGL), SPIR-V (Vulkan), and MSL (Metal).
+# each assets/shaders/*.slang to GLSL (OpenGL), SPIR-V (Vulkan), and MSL (Metal).
#
# Re-run to (re)install. Pin the version here so builds are reproducible.
set -euo pipefail
VERSION="2026.14.1"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
-DEST="$ROOT/Tools/slang"
+DEST="$ROOT/tools/slang"
case "$(uname -s)-$(uname -m)" in
Darwin-arm64|Darwin-aarch64) ASSET="macos-aarch64" ;;
diff --git a/assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttf b/assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttf
new file mode 100644
index 0000000..43ed4f5
--- /dev/null
+++ b/assets/fonts/inter/Inter-Italic-VariableFont_opsz,wght.ttf
Binary files differ
diff --git a/assets/fonts/inter/Inter-VariableFont_opsz,wght.ttf b/assets/fonts/inter/Inter-VariableFont_opsz,wght.ttf
new file mode 100644
index 0000000..e31b51e
--- /dev/null
+++ b/assets/fonts/inter/Inter-VariableFont_opsz,wght.ttf
Binary files differ
diff --git a/assets/fonts/inter/OFL.txt b/assets/fonts/inter/OFL.txt
new file mode 100644
index 0000000..63db4fa
--- /dev/null
+++ b/assets/fonts/inter/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/assets/fonts/inter/README.txt b/assets/fonts/inter/README.txt
new file mode 100644
index 0000000..f7a47e8
--- /dev/null
+++ b/assets/fonts/inter/README.txt
@@ -0,0 +1,118 @@
+Inter Variable Font
+===================
+
+This download contains Inter as both variable fonts and static fonts.
+
+Inter is a variable font with these axes:
+ opsz
+ wght
+
+This means all the styles are contained in these files:
+ Inter-VariableFont_opsz,wght.ttf
+ Inter-Italic-VariableFont_opsz,wght.ttf
+
+If your app fully supports variable fonts, you can now pick intermediate styles
+that aren’t available as static fonts. Not all apps support variable fonts, and
+in those cases you can use the static font files for Inter:
+ static/Inter_18pt-Thin.ttf
+ static/Inter_18pt-ExtraLight.ttf
+ static/Inter_18pt-Light.ttf
+ static/Inter_18pt-Regular.ttf
+ static/Inter_18pt-Medium.ttf
+ static/Inter_18pt-SemiBold.ttf
+ static/Inter_18pt-Bold.ttf
+ static/Inter_18pt-ExtraBold.ttf
+ static/Inter_18pt-Black.ttf
+ static/Inter_24pt-Thin.ttf
+ static/Inter_24pt-ExtraLight.ttf
+ static/Inter_24pt-Light.ttf
+ static/Inter_24pt-Regular.ttf
+ static/Inter_24pt-Medium.ttf
+ static/Inter_24pt-SemiBold.ttf
+ static/Inter_24pt-Bold.ttf
+ static/Inter_24pt-ExtraBold.ttf
+ static/Inter_24pt-Black.ttf
+ static/Inter_28pt-Thin.ttf
+ static/Inter_28pt-ExtraLight.ttf
+ static/Inter_28pt-Light.ttf
+ static/Inter_28pt-Regular.ttf
+ static/Inter_28pt-Medium.ttf
+ static/Inter_28pt-SemiBold.ttf
+ static/Inter_28pt-Bold.ttf
+ static/Inter_28pt-ExtraBold.ttf
+ static/Inter_28pt-Black.ttf
+ static/Inter_18pt-ThinItalic.ttf
+ static/Inter_18pt-ExtraLightItalic.ttf
+ static/Inter_18pt-LightItalic.ttf
+ static/Inter_18pt-Italic.ttf
+ static/Inter_18pt-MediumItalic.ttf
+ static/Inter_18pt-SemiBoldItalic.ttf
+ static/Inter_18pt-BoldItalic.ttf
+ static/Inter_18pt-ExtraBoldItalic.ttf
+ static/Inter_18pt-BlackItalic.ttf
+ static/Inter_24pt-ThinItalic.ttf
+ static/Inter_24pt-ExtraLightItalic.ttf
+ static/Inter_24pt-LightItalic.ttf
+ static/Inter_24pt-Italic.ttf
+ static/Inter_24pt-MediumItalic.ttf
+ static/Inter_24pt-SemiBoldItalic.ttf
+ static/Inter_24pt-BoldItalic.ttf
+ static/Inter_24pt-ExtraBoldItalic.ttf
+ static/Inter_24pt-BlackItalic.ttf
+ static/Inter_28pt-ThinItalic.ttf
+ static/Inter_28pt-ExtraLightItalic.ttf
+ static/Inter_28pt-LightItalic.ttf
+ static/Inter_28pt-Italic.ttf
+ static/Inter_28pt-MediumItalic.ttf
+ static/Inter_28pt-SemiBoldItalic.ttf
+ static/Inter_28pt-BoldItalic.ttf
+ static/Inter_28pt-ExtraBoldItalic.ttf
+ static/Inter_28pt-BlackItalic.ttf
+
+Get started
+-----------
+
+1. Install the font files you want to use
+
+2. Use your app's font picker to view the font family and all the
+available styles
+
+Learn more about variable fonts
+-------------------------------
+
+ https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
+ https://variablefonts.typenetwork.com
+ https://medium.com/variable-fonts
+
+In desktop apps
+
+ https://theblog.adobe.com/can-variable-fonts-illustrator-cc
+ https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
+
+Online
+
+ https://developers.google.com/fonts/docs/getting_started
+ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
+ https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
+
+Installing fonts
+
+ MacOS: https://support.apple.com/en-us/HT201749
+ Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
+ Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
+
+Android Apps
+
+ https://developers.google.com/fonts/docs/android
+ https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
+
+License
+-------
+Please read the full license text (OFL.txt) to understand the permissions,
+restrictions and requirements for usage, redistribution, and modification.
+
+You can use them in your products & projects – print or digital,
+commercial or otherwise.
+
+This isn't legal advice, please consider consulting a lawyer and see the full
+license for all details.
diff --git a/assets/fonts/inter/static/Inter_18pt-Black.ttf b/assets/fonts/inter/static/Inter_18pt-Black.ttf
new file mode 100644
index 0000000..89673de
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Black.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-BlackItalic.ttf b/assets/fonts/inter/static/Inter_18pt-BlackItalic.ttf
new file mode 100644
index 0000000..b33602f
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-BlackItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Bold.ttf b/assets/fonts/inter/static/Inter_18pt-Bold.ttf
new file mode 100644
index 0000000..cd13f60
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Bold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-BoldItalic.ttf b/assets/fonts/inter/static/Inter_18pt-BoldItalic.ttf
new file mode 100644
index 0000000..0d19c1a
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-BoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-ExtraBold.ttf b/assets/fonts/inter/static/Inter_18pt-ExtraBold.ttf
new file mode 100644
index 0000000..e71c601
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-ExtraBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-ExtraBoldItalic.ttf b/assets/fonts/inter/static/Inter_18pt-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..df45062
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-ExtraBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-ExtraLight.ttf b/assets/fonts/inter/static/Inter_18pt-ExtraLight.ttf
new file mode 100644
index 0000000..f9c6cfc
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-ExtraLight.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-ExtraLightItalic.ttf b/assets/fonts/inter/static/Inter_18pt-ExtraLightItalic.ttf
new file mode 100644
index 0000000..275f305
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-ExtraLightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Italic.ttf b/assets/fonts/inter/static/Inter_18pt-Italic.ttf
new file mode 100644
index 0000000..14d3595
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Italic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Light.ttf b/assets/fonts/inter/static/Inter_18pt-Light.ttf
new file mode 100644
index 0000000..acae361
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Light.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-LightItalic.ttf b/assets/fonts/inter/static/Inter_18pt-LightItalic.ttf
new file mode 100644
index 0000000..f69e18b
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-LightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Medium.ttf b/assets/fonts/inter/static/Inter_18pt-Medium.ttf
new file mode 100644
index 0000000..71d9017
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Medium.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-MediumItalic.ttf b/assets/fonts/inter/static/Inter_18pt-MediumItalic.ttf
new file mode 100644
index 0000000..5c8c8b1
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-MediumItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Regular.ttf b/assets/fonts/inter/static/Inter_18pt-Regular.ttf
new file mode 100644
index 0000000..ce097c8
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Regular.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-SemiBold.ttf b/assets/fonts/inter/static/Inter_18pt-SemiBold.ttf
new file mode 100644
index 0000000..053185e
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-SemiBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-SemiBoldItalic.ttf b/assets/fonts/inter/static/Inter_18pt-SemiBoldItalic.ttf
new file mode 100644
index 0000000..d9c9896
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-SemiBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-Thin.ttf b/assets/fonts/inter/static/Inter_18pt-Thin.ttf
new file mode 100644
index 0000000..e68ec47
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-Thin.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_18pt-ThinItalic.ttf b/assets/fonts/inter/static/Inter_18pt-ThinItalic.ttf
new file mode 100644
index 0000000..134e837
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_18pt-ThinItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Black.ttf b/assets/fonts/inter/static/Inter_24pt-Black.ttf
new file mode 100644
index 0000000..dbb1b3b
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Black.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-BlackItalic.ttf b/assets/fonts/inter/static/Inter_24pt-BlackItalic.ttf
new file mode 100644
index 0000000..b89d61c
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-BlackItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Bold.ttf b/assets/fonts/inter/static/Inter_24pt-Bold.ttf
new file mode 100644
index 0000000..46b3583
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Bold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-BoldItalic.ttf b/assets/fonts/inter/static/Inter_24pt-BoldItalic.ttf
new file mode 100644
index 0000000..d1c0f53
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-BoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-ExtraBold.ttf b/assets/fonts/inter/static/Inter_24pt-ExtraBold.ttf
new file mode 100644
index 0000000..b775c08
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-ExtraBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-ExtraBoldItalic.ttf b/assets/fonts/inter/static/Inter_24pt-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..3461a92
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-ExtraBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-ExtraLight.ttf b/assets/fonts/inter/static/Inter_24pt-ExtraLight.ttf
new file mode 100644
index 0000000..2ec6ca3
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-ExtraLight.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-ExtraLightItalic.ttf b/assets/fonts/inter/static/Inter_24pt-ExtraLightItalic.ttf
new file mode 100644
index 0000000..c634a5d
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-ExtraLightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Italic.ttf b/assets/fonts/inter/static/Inter_24pt-Italic.ttf
new file mode 100644
index 0000000..1048b07
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Italic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Light.ttf b/assets/fonts/inter/static/Inter_24pt-Light.ttf
new file mode 100644
index 0000000..1a2a6f2
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Light.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-LightItalic.ttf b/assets/fonts/inter/static/Inter_24pt-LightItalic.ttf
new file mode 100644
index 0000000..ded5a75
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-LightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Medium.ttf b/assets/fonts/inter/static/Inter_24pt-Medium.ttf
new file mode 100644
index 0000000..5c88739
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Medium.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-MediumItalic.ttf b/assets/fonts/inter/static/Inter_24pt-MediumItalic.ttf
new file mode 100644
index 0000000..be091b1
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-MediumItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Regular.ttf b/assets/fonts/inter/static/Inter_24pt-Regular.ttf
new file mode 100644
index 0000000..6b088a7
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Regular.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-SemiBold.ttf b/assets/fonts/inter/static/Inter_24pt-SemiBold.ttf
new file mode 100644
index 0000000..ceb8576
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-SemiBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-SemiBoldItalic.ttf b/assets/fonts/inter/static/Inter_24pt-SemiBoldItalic.ttf
new file mode 100644
index 0000000..6921df2
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-SemiBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-Thin.ttf b/assets/fonts/inter/static/Inter_24pt-Thin.ttf
new file mode 100644
index 0000000..3505b35
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-Thin.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_24pt-ThinItalic.ttf b/assets/fonts/inter/static/Inter_24pt-ThinItalic.ttf
new file mode 100644
index 0000000..a3e6feb
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_24pt-ThinItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Black.ttf b/assets/fonts/inter/static/Inter_28pt-Black.ttf
new file mode 100644
index 0000000..66a252f
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Black.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-BlackItalic.ttf b/assets/fonts/inter/static/Inter_28pt-BlackItalic.ttf
new file mode 100644
index 0000000..3c8fdf9
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-BlackItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Bold.ttf b/assets/fonts/inter/static/Inter_28pt-Bold.ttf
new file mode 100644
index 0000000..d17828b
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Bold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-BoldItalic.ttf b/assets/fonts/inter/static/Inter_28pt-BoldItalic.ttf
new file mode 100644
index 0000000..6fce50a
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-BoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-ExtraBold.ttf b/assets/fonts/inter/static/Inter_28pt-ExtraBold.ttf
new file mode 100644
index 0000000..6d87cae
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-ExtraBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-ExtraBoldItalic.ttf b/assets/fonts/inter/static/Inter_28pt-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..1a56735
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-ExtraBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-ExtraLight.ttf b/assets/fonts/inter/static/Inter_28pt-ExtraLight.ttf
new file mode 100644
index 0000000..d42b3f5
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-ExtraLight.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-ExtraLightItalic.ttf b/assets/fonts/inter/static/Inter_28pt-ExtraLightItalic.ttf
new file mode 100644
index 0000000..90e2f20
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-ExtraLightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Italic.ttf b/assets/fonts/inter/static/Inter_28pt-Italic.ttf
new file mode 100644
index 0000000..c2a143a
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Italic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Light.ttf b/assets/fonts/inter/static/Inter_28pt-Light.ttf
new file mode 100644
index 0000000..5eeff3a
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Light.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-LightItalic.ttf b/assets/fonts/inter/static/Inter_28pt-LightItalic.ttf
new file mode 100644
index 0000000..6b90b76
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-LightItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Medium.ttf b/assets/fonts/inter/static/Inter_28pt-Medium.ttf
new file mode 100644
index 0000000..00120fe
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Medium.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-MediumItalic.ttf b/assets/fonts/inter/static/Inter_28pt-MediumItalic.ttf
new file mode 100644
index 0000000..7481e7b
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-MediumItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Regular.ttf b/assets/fonts/inter/static/Inter_28pt-Regular.ttf
new file mode 100644
index 0000000..855b6f4
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Regular.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-SemiBold.ttf b/assets/fonts/inter/static/Inter_28pt-SemiBold.ttf
new file mode 100644
index 0000000..8b84efc
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-SemiBold.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-SemiBoldItalic.ttf b/assets/fonts/inter/static/Inter_28pt-SemiBoldItalic.ttf
new file mode 100644
index 0000000..2e22c5a
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-SemiBoldItalic.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-Thin.ttf b/assets/fonts/inter/static/Inter_28pt-Thin.ttf
new file mode 100644
index 0000000..94e6108
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-Thin.ttf
Binary files differ
diff --git a/assets/fonts/inter/static/Inter_28pt-ThinItalic.ttf b/assets/fonts/inter/static/Inter_28pt-ThinItalic.ttf
new file mode 100644
index 0000000..d3d44cd
--- /dev/null
+++ b/assets/fonts/inter/static/Inter_28pt-ThinItalic.ttf
Binary files differ
diff --git a/assets/hdri/HDR_blue_nebulae-1.hdr b/assets/hdri/HDR_blue_nebulae-1.hdr
new file mode 100644
index 0000000..f34b25d
--- /dev/null
+++ b/assets/hdri/HDR_blue_nebulae-1.hdr
Binary files differ
diff --git a/assets/hdri/HDR_subdued_blue_nebulae.hdr b/assets/hdri/HDR_subdued_blue_nebulae.hdr
new file mode 100644
index 0000000..ea34298
--- /dev/null
+++ b/assets/hdri/HDR_subdued_blue_nebulae.hdr
Binary files differ
diff --git a/assets/hdri/HDR_subdued_multi_nebulae.hdr b/assets/hdri/HDR_subdued_multi_nebulae.hdr
new file mode 100644
index 0000000..da88396
--- /dev/null
+++ b/assets/hdri/HDR_subdued_multi_nebulae.hdr
Binary files differ
diff --git a/assets/hdri/night_sky.hdr b/assets/hdri/night_sky.hdr
new file mode 100644
index 0000000..b473659
--- /dev/null
+++ b/assets/hdri/night_sky.hdr
Binary files differ
diff --git a/assets/shaders/Blur.slang b/assets/shaders/Blur.slang
new file mode 100644
index 0000000..17e4eae
--- /dev/null
+++ b/assets/shaders/Blur.slang
@@ -0,0 +1,60 @@
+struct VSInput
+{
+ float2 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+};
+
+struct VSOutput
+{
+ float4 position : SV_Position;
+ float2 texCoord : TEXCOORD0;
+};
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.position = float4(input.position, 0.0, 1.0);
+ output.texCoord = input.texCoord;
+ return output;
+}
+
+Sampler2D u_ScreenTexture;
+uniform float2 u_Resolution;
+uniform float u_BlurStrength;
+uniform float u_GlowIntensity;
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ float2 texelSize = 1.0 / u_Resolution;
+ float4 centerColor = u_ScreenTexture.Sample(input.texCoord);
+
+ float brightness = (centerColor.r + centerColor.g + centerColor.b) / 3.0;
+ float glowMask = smoothstep(0.05, 0.3, brightness);
+
+ float4 blurredColor = float4(0.0, 0.0, 0.0, 0.0);
+ float totalWeight = 0.0;
+
+ for (int x = -8; x <= 8; x++)
+ {
+ for (int y = -8; y <= 8; y++)
+ {
+ float2 offset = float2(x, y) * texelSize * u_BlurStrength;
+ float4 sampleColor = u_ScreenTexture.Sample(input.texCoord + offset);
+
+ float weight = exp(-float(x * x + y * y) / (2.0 * 8.0 * 8.0));
+ blurredColor += sampleColor * weight;
+ totalWeight += weight;
+ }
+ }
+
+ blurredColor /= totalWeight;
+
+ float3 emissionColor = float3(1.0, 0.8, 0.6);
+ float3 glowColor = emissionColor * glowMask * u_GlowIntensity * 2.0;
+ float3 auraColor = blurredColor.rgb * glowMask * u_GlowIntensity * 0.8;
+ float3 finalColor = centerColor.rgb + glowColor + auraColor;
+
+ return float4(finalColor, centerColor.a);
+}
diff --git a/assets/shaders/EquirectToCubemap.slang b/assets/shaders/EquirectToCubemap.slang
new file mode 100644
index 0000000..9b2ea4b
--- /dev/null
+++ b/assets/shaders/EquirectToCubemap.slang
@@ -0,0 +1,33 @@
+uniform float4x4 u_Projection;
+uniform float4x4 u_View;
+
+struct VSInput { float3 position : POSITION; };
+struct VSOutput { float4 position : SV_Position; float3 worldPos : TEXCOORD0; };
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.worldPos = input.position;
+ output.position = mul(u_Projection, mul(u_View, float4(input.position, 1.0)));
+ return output;
+}
+
+Sampler2D u_EquirectangularMap;
+static const float2 invAtan = float2(0.1591, 0.3183);
+
+float2 SampleSphericalMap(float3 v)
+{
+ float2 uv = float2(atan2(v.z, v.x), asin(v.y));
+ uv *= invAtan;
+ uv += 0.5;
+ return uv;
+}
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ float2 uv = SampleSphericalMap(normalize(input.worldPos));
+ float3 color = u_EquirectangularMap.Sample(uv).rgb;
+ return float4(color, 1.0);
+}
diff --git a/assets/shaders/Geodesic.slang b/assets/shaders/Geodesic.slang
new file mode 100644
index 0000000..9490f21
--- /dev/null
+++ b/assets/shaders/Geodesic.slang
@@ -0,0 +1,490 @@
+struct VSInput { float2 position : POSITION; float2 texCoord : TEXCOORD0; };
+struct VSOutput { float4 position : SV_Position; float2 texCoord : TEXCOORD0; };
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.position = float4(input.position, 0.0, 1.0);
+ output.texCoord = input.texCoord;
+ return output;
+}
+
+struct Camera
+{
+ float3 camPos; float _pad0;
+ float3 camRight; float _pad1;
+ float3 camUp; float _pad2;
+ float3 camForward; float _pad3;
+ float tanHalfFov;
+ float aspect;
+ bool moving;
+ int _pad4;
+};
+ConstantBuffer<Camera> cam;
+
+struct Disk
+{
+ float disk_r1;
+ float disk_r2;
+ float disk_num;
+ float thickness;
+ float disk_density;
+};
+ConstantBuffer<Disk> disk;
+
+struct Objects
+{
+ int numObjects;
+ float4 objPosRadius[16];
+ float4 objColor[16];
+ float mass[16];
+};
+ConstantBuffer<Objects> obj;
+
+struct Simulation
+{
+ int maxStepsMoving;
+ int maxStepsStatic;
+ float earlyExitDistance;
+ float time;
+};
+ConstantBuffer<Simulation> sim;
+
+SamplerCube u_HDRIEnvironment;
+
+static const float SagA_rs = 1.269e10;
+static const float D_LAMBDA = 1e7;
+static const float ESCAPE_R = 1e30;
+
+static const int DEFAULT_MAX_STEPS_MOVING = 12000;
+static const int DEFAULT_MAX_STEPS_STATIC = 8000;
+static const float DEFAULT_EARLY_EXIT_DISTANCE = 2e12;
+
+static const float MIN_STEP_SIZE = 1e6;
+static const float MAX_STEP_SIZE = 5e7;
+
+struct Hit
+{
+ float4 objectColor;
+ float3 hitCenter;
+ float hitRadius;
+};
+
+float3 SampleHDRI(float3 direction)
+{
+ return u_HDRIEnvironment.Sample(direction).rgb;
+}
+
+float hash(float p)
+{
+ p = frac(p * 0.1031);
+ p *= p + 33.33;
+ p *= p + p;
+ return frac(p);
+}
+
+float hash(float2 p)
+{
+ float3 p3 = frac(float3(p.xyx) * float3(0.1031, 0.1030, 0.0973));
+ p3 += dot(p3, p3.yzx + 33.33);
+ return frac((p3.x + p3.y) * p3.z);
+}
+
+float hash(float3 p)
+{
+ p = frac(p * float3(0.1031, 0.1030, 0.0973));
+ p += dot(p, p.yxz + 33.33);
+ return frac((p.x + p.y) * p.z);
+}
+
+float noise(float3 x)
+{
+ float3 i = floor(x);
+ float3 fr = frac(x);
+
+ float3 u = fr * fr * (3.0 - 2.0 * fr);
+
+ float a = hash(i);
+ float b = hash(i + float3(1.0, 0.0, 0.0));
+ float c = hash(i + float3(0.0, 1.0, 0.0));
+ float d = hash(i + float3(1.0, 1.0, 0.0));
+ float e = hash(i + float3(0.0, 0.0, 1.0));
+ float f = hash(i + float3(1.0, 0.0, 1.0));
+ float g = hash(i + float3(0.0, 1.0, 1.0));
+ float h = hash(i + float3(1.0, 1.0, 1.0));
+
+ return lerp(lerp(lerp(a, b, u.x), lerp(c, d, u.x), u.y),
+ lerp(lerp(e, f, u.x), lerp(g, h, u.x), u.y), u.z);
+}
+
+float fbm(float3 x, int octaves)
+{
+ float v = 0.0;
+ float a = 0.5;
+ float f = 1.0;
+ float3 shift = float3(100, 200, 300);
+
+ for (int i = 0; i < octaves; ++i)
+ {
+ v += a * noise(x * f);
+ x = x * 2.0 + shift;
+ a *= 0.5;
+ f *= 2.0;
+ }
+ return v;
+}
+
+float GetCloudDensity(float3 pos)
+{
+ float r_cyl = length(float2(pos.x, pos.z));
+ float r_norm = (r_cyl - disk.disk_r1) / (disk.disk_r2 - disk.disk_r1);
+
+ if (r_norm < 0.0 || r_norm > 1.0)
+ return 0.0;
+
+ float h_norm = abs(pos.y) / disk.thickness;
+ float vertical_falloff = exp(-h_norm * h_norm * 3.0);
+ float radial_density = 1.0 - r_norm * 0.5;
+
+ float keplerian_speed = 1.0 / sqrt(r_norm + 0.1);
+
+ float rotation_angle = sim.time * keplerian_speed * 0.5;
+ float3 rotated_pos = float3(
+ pos.x * cos(rotation_angle) - pos.z * sin(rotation_angle),
+ pos.y,
+ pos.x * sin(rotation_angle) + pos.z * cos(rotation_angle)
+ ) * 1e-10;
+
+ float large_turbulence = fbm(rotated_pos * 1.2, 5);
+ float medium_wisps = fbm(rotated_pos * 2.5, 4);
+ float small_detail = fbm(rotated_pos * 6.0, 3);
+ float fine_detail = fbm(rotated_pos * 10.0, 2);
+
+ float noise_mask = large_turbulence * 0.4 +
+ medium_wisps * 0.3 +
+ small_detail * 0.2 +
+ fine_detail * 0.1;
+
+ noise_mask = smoothstep(0.25, 0.75, noise_mask);
+
+ float angle = atan2(pos.z, pos.x);
+ float rotated_angle = angle + sim.time * 0.5;
+
+ float spiral_arms = sin(rotated_angle * 3.0 + r_norm * 15.0) * 0.15 + 0.85;
+
+ float orbital_angle = angle + sim.time * keplerian_speed * 0.8;
+ float orbital_pattern = sin(orbital_angle * 2.0 + r_norm * 8.0) * 0.2 + 0.8;
+
+ float density = vertical_falloff * radial_density * noise_mask * spiral_arms * orbital_pattern;
+ return density * disk.disk_density;
+}
+
+struct Ray
+{
+ float x, y, z;
+ float r, theta, phi;
+ float dr, dtheta, dphi;
+ float E, L;
+};
+
+Ray InitRay(float3 pos, float3 dir)
+{
+ Ray ray;
+ ray.x = pos.x;
+ ray.y = pos.y;
+ ray.z = pos.z;
+ ray.r = length(pos);
+ ray.theta = acos(pos.z / ray.r);
+ ray.phi = atan2(pos.y, pos.x);
+
+ float dx = dir.x;
+ float dy = dir.y;
+ float dz = dir.z;
+
+ ray.dr = 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.r;
+
+ ray.dphi = (-sin(ray.phi)*dx + cos(ray.phi)*dy) /
+ (ray.r * sin(ray.theta));
+
+ ray.L = ray.r * ray.r * sin(ray.theta) * ray.dphi;
+ float f = 1.0 - SagA_rs / ray.r;
+ float dt_dL = sqrt((ray.dr*ray.dr)/f +
+ ray.r*ray.r*(ray.dtheta*ray.dtheta +
+ sin(ray.theta)*sin(ray.theta)*
+ ray.dphi*ray.dphi));
+ ray.E = f * dt_dL;
+
+ return ray;
+}
+
+bool Intercept(Ray ray, float rs)
+{
+ return ray.r <= rs;
+}
+
+bool InterceptObject(Ray ray, inout Hit hit)
+{
+ float3 P = float3(ray.x, ray.y, ray.z);
+
+ for (int i = 0; i < obj.numObjects; ++i)
+ {
+ float3 center = obj.objPosRadius[i].xyz;
+ float radius = obj.objPosRadius[i].w;
+
+ float distSq = dot(P - center, P - center);
+ if (distSq > radius * radius * 4.0)
+ continue;
+
+ if (distSq <= radius * radius)
+ {
+ hit.objectColor = obj.objColor[i];
+ hit.hitCenter = center;
+ hit.hitRadius = radius;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void GeodesicRHS(Ray ray, out float3 d1, out float3 d2)
+{
+ float r = ray.r;
+ float theta = ray.theta;
+ float dr = ray.dr;
+ float dtheta = ray.dtheta;
+ float dphi = ray.dphi;
+ float f = 1.0 - SagA_rs / r;
+ float dt_dL = ray.E / f;
+
+ d1 = float3(dr, dtheta, dphi);
+ d2.x = - (SagA_rs / (2.0 * r*r)) * f * dt_dL * dt_dL
+ + (SagA_rs / (2.0 * r*r * f)) * dr * dr
+ + r * (dtheta*dtheta + sin(theta)*sin(theta)*dphi*dphi);
+ d2.y = -2.0*dr*dtheta/r + sin(theta)*cos(theta)*dphi*dphi;
+ d2.z = -2.0*dr*dphi/r - 2.0*cos(theta)/(sin(theta)) * dtheta * dphi;
+}
+
+void RK4Step(inout Ray ray, float dL)
+{
+ float3 k1a, k1b;
+ GeodesicRHS(ray, k1a, k1b);
+
+ ray.r += dL * k1a.x;
+ ray.theta += dL * k1a.y;
+ ray.phi += dL * k1a.z;
+ ray.dr += dL * k1b.x;
+ ray.dtheta += dL * k1b.y;
+ ray.dphi += dL * k1b.z;
+
+ ray.x = ray.r * sin(ray.theta) * cos(ray.phi);
+ ray.y = ray.r * sin(ray.theta) * sin(ray.phi);
+ ray.z = ray.r * cos(ray.theta);
+}
+
+bool IsInDiskVolume(float3 pos)
+{
+ float r_cyl = length(float2(pos.x, pos.z));
+ return (r_cyl >= disk.disk_r1 && r_cyl <= disk.disk_r2 && abs(pos.y) <= disk.thickness);
+}
+
+float4 SampleDiskColor(float3 pos)
+{
+ float r_cyl = length(float2(pos.x, pos.z));
+ float r_norm = (r_cyl - disk.disk_r1) / (disk.disk_r2 - disk.disk_r1);
+
+ float3 innerColor = float3(1.0, 0.9, 0.5);
+ float3 midColor = float3(1.0, 0.6, 0.2);
+ float3 outerColor = float3(0.9, 0.3, 0.1);
+
+ float3 baseColor;
+ if (r_norm < 0.5)
+ baseColor = lerp(innerColor, midColor, r_norm * 2.0);
+ else
+ baseColor = lerp(midColor, outerColor, (r_norm - 0.5) * 2.0);
+
+ float r_norm_rot = (r_cyl - disk.disk_r1) / (disk.disk_r2 - disk.disk_r1);
+ float keplerian_speed = 1.0 / sqrt(r_norm_rot + 0.1);
+
+ float color_rotation_angle = sim.time * keplerian_speed * 0.3;
+ float3 rotated_color_pos = float3(
+ pos.x * cos(color_rotation_angle) - pos.z * sin(color_rotation_angle),
+ pos.y,
+ pos.x * sin(color_rotation_angle) + pos.z * cos(color_rotation_angle)
+ ) * 1e-10;
+
+ float large_color = fbm(rotated_color_pos * 1.8, 4);
+ float medium_color = fbm(rotated_color_pos * 4.0, 3);
+ float small_color = fbm(rotated_color_pos * 8.0, 2);
+ float colorVariation = (large_color * 0.5 + medium_color * 0.3 + small_color * 0.2) * 0.6;
+ baseColor = baseColor * (1.0 + colorVariation);
+
+ float density = GetCloudDensity(pos);
+
+ float brightness_rotation_angle = sim.time * keplerian_speed * 0.7;
+ float3 rotated_brightness_pos = float3(
+ pos.x * cos(brightness_rotation_angle) - pos.z * sin(brightness_rotation_angle),
+ pos.y,
+ pos.x * sin(brightness_rotation_angle) + pos.z * cos(brightness_rotation_angle)
+ ) * 1e-10;
+
+ float brightness_large = fbm(rotated_brightness_pos * 3.0, 3);
+ float brightness_medium = fbm(rotated_brightness_pos * 5.0, 2);
+ float brightness_small = fbm(rotated_brightness_pos * 7.0, 2);
+ float brightness_noise = (brightness_large * 0.6 + brightness_medium * 0.3 + brightness_small * 0.1);
+
+ float baseBrightness = 1.0 + density * 1.5;
+ float glowBrightness = brightness_noise * 0.8;
+ float brightness = baseBrightness + glowBrightness;
+
+ return float4(baseColor * brightness, density);
+}
+
+float CalculateAdaptiveStepSize(Ray ray, float baseStepSize)
+{
+ float r_factor = clamp(ray.r / (SagA_rs * 10.0), 0.1, 1.0);
+ float curvature = length(float3(ray.dr, ray.dtheta * ray.r, ray.dphi * ray.r * sin(ray.theta)));
+ float curvature_factor = clamp(1e12 / (curvature + 1e6), 0.1, 2.0);
+
+ return clamp(baseStepSize * r_factor * curvature_factor, MIN_STEP_SIZE, MAX_STEP_SIZE);
+}
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ float u = (2.0 * input.texCoord.x - 1.0) * cam.aspect * cam.tanHalfFov;
+ float v = (1.0 - 2.0 * input.texCoord.y) * cam.tanHalfFov;
+ float3 dir = normalize(u * cam.camRight - v * cam.camUp + cam.camForward);
+ Ray ray = InitRay(cam.camPos, dir);
+
+ float4 color = float4(0.0, 0.0, 0.0, 0.0);
+
+ bool hitBlackHole = false;
+ bool hitObject = false;
+
+ Hit hit;
+ hit.objectColor = float4(0.0, 0.0, 0.0, 0.0);
+ hit.hitCenter = float3(0.0, 0.0, 0.0);
+ hit.hitRadius = 0.0;
+
+ float4 accumulatedColor = float4(0.0, 0.0, 0.0, 0.0);
+ float transmittance = 1.0;
+
+ int maxSteps = cam.moving ? sim.maxStepsMoving : sim.maxStepsStatic;
+
+ if (maxSteps <= 0)
+ maxSteps = cam.moving ? DEFAULT_MAX_STEPS_MOVING : DEFAULT_MAX_STEPS_STATIC;
+
+ float cameraDistance = length(cam.camPos);
+ if (cameraDistance > 2e12)
+ maxSteps = maxSteps / 2;
+ else if (cameraDistance > 1e12)
+ maxSteps = int(maxSteps * 0.75);
+
+ float initialEscapeVelocity = sqrt(2.0 * SagA_rs / ray.r);
+ if (ray.dr > initialEscapeVelocity * 0.95 &&
+ ray.r > SagA_rs * 200.0)
+ maxSteps = maxSteps / 2;
+
+ float lambda = 0.0;
+ int objectCheckInterval = 5;
+
+ for (int i = 0; i < maxSteps; ++i)
+ {
+ float exitDistance = sim.earlyExitDistance > 0.0 ? sim.earlyExitDistance : DEFAULT_EARLY_EXIT_DISTANCE;
+ if (ray.r > exitDistance)
+ break;
+ if (ray.r > ESCAPE_R)
+ break;
+
+ if (Intercept(ray, SagA_rs))
+ {
+ hitBlackHole = true;
+ break;
+ }
+
+ float currentStepSize = CalculateAdaptiveStepSize(ray, D_LAMBDA);
+
+ RK4Step(ray, currentStepSize);
+ lambda += currentStepSize;
+
+ float3 newPos = float3(ray.x, ray.y, ray.z);
+
+ if (IsInDiskVolume(newPos))
+ {
+ float4 diskSample = SampleDiskColor(newPos);
+ float density = diskSample.a;
+ float3 diskColor = diskSample.rgb;
+
+ float stepLength = currentStepSize * 1e-8;
+
+ float absorption = density * stepLength * 0.8;
+ float scattering = density * stepLength * 1.5;
+ float extinction = absorption + scattering;
+
+ float stepTransmittance = exp(-extinction);
+
+ float3 emission = diskColor * density * stepLength * 4.0 * sqrt(disk.disk_density);
+
+ float3 glowColor = lerp(diskColor, float3(1.0, 0.8, 0.6), 0.3);
+ float glowIntensity = density * stepLength * 2.0;
+ float3 atmosphericGlow = glowColor * glowIntensity * 0.8;
+
+ float3 totalEmission = emission + atmosphericGlow;
+ accumulatedColor.rgb += totalEmission * transmittance;
+
+ transmittance *= stepTransmittance;
+
+ if (transmittance < 0.01)
+ {
+ accumulatedColor.a = 1.0 - transmittance;
+ break;
+ }
+ }
+
+ if (i % objectCheckInterval == 0 && InterceptObject(ray, hit))
+ {
+ hitObject = true;
+ break;
+ }
+
+ if (ray.dr > 0.0 && ray.r > SagA_rs * 100.0 && lambda > 2e8)
+ break;
+ }
+
+ accumulatedColor.a = 1.0 - transmittance;
+
+ if (hitBlackHole)
+ {
+ color = float4(0.0, 0.0, 0.0, 1.0);
+ }
+ else if (hitObject)
+ {
+ float3 P = float3(ray.x, ray.y, ray.z);
+ float3 N = normalize(P - hit.hitCenter);
+ float3 V = normalize(cam.camPos - P);
+
+ float ambient = 0.1;
+ float diff = max(dot(N, V), 0.0);
+ float intensity = ambient + (1.0 - ambient) * diff;
+ float3 shaded = hit.objectColor.rgb * intensity;
+
+ color = float4(shaded, hit.objectColor.a);
+ color = lerp(accumulatedColor, color, color.a);
+ }
+ else
+ {
+ float3 rayDirection = normalize(float3(ray.x, ray.y, ray.z) - cam.camPos);
+ float3 hdriColor = SampleHDRI(rayDirection);
+ color = float4(lerp(accumulatedColor.rgb, hdriColor, 1.0 - accumulatedColor.a), 1.0);
+ }
+
+ return color;
+}
diff --git a/assets/shaders/Grid.slang b/assets/shaders/Grid.slang
new file mode 100644
index 0000000..ea7d263
--- /dev/null
+++ b/assets/shaders/Grid.slang
@@ -0,0 +1,34 @@
+uniform float4x4 u_ViewProjection;
+uniform float4x4 u_Transform;
+uniform float u_GridSize;
+
+struct VSInput { float3 position : POSITION; };
+struct VSOutput { float4 position : SV_Position; };
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ float3 scaledPosition = input.position * (u_GridSize / 50.0);
+ output.position = mul(u_ViewProjection, mul(u_Transform, float4(scaledPosition, 1.0)));
+ return output;
+}
+
+uniform float3 u_GridColor;
+uniform float u_GridAlpha;
+uniform float3 u_CameraPos;
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ float dist = length(u_CameraPos);
+ float fadeFactor = 1.0 - clamp(dist / 100.0, 0.0, 0.8);
+
+ float3 color = u_GridColor;
+ float alpha = u_GridAlpha * fadeFactor;
+
+ float gridFade = 1.0 - clamp(dist / 50.0, 0.0, 0.9);
+ alpha *= gridFade;
+
+ return float4(color, alpha);
+}
diff --git a/assets/shaders/Skybox.slang b/assets/shaders/Skybox.slang
new file mode 100644
index 0000000..6ba81bc
--- /dev/null
+++ b/assets/shaders/Skybox.slang
@@ -0,0 +1,23 @@
+uniform float4x4 u_Projection;
+uniform float4x4 u_View;
+
+struct VSInput { float3 position : POSITION; };
+struct VSOutput { float4 position : SV_Position; float3 texCoords : TEXCOORD0; };
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.texCoords = input.position;
+ float4 pos = mul(u_Projection, mul(u_View, float4(input.position, 1.0)));
+ output.position = pos.xyww; // force depth = 1 so the skybox stays behind everything
+ return output;
+}
+
+SamplerCube u_Skybox;
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ return u_Skybox.Sample(input.texCoords);
+}
diff --git a/assets/shaders/Sphere.slang b/assets/shaders/Sphere.slang
new file mode 100644
index 0000000..0871438
--- /dev/null
+++ b/assets/shaders/Sphere.slang
@@ -0,0 +1,67 @@
+uniform float4x4 u_ViewProjection;
+uniform float4x4 u_Transform;
+
+struct VSInput
+{
+ float3 position : POSITION;
+ float3 normal : NORMAL;
+};
+
+struct VSOutput
+{
+ float4 position : SV_Position;
+ float3 normal : TEXCOORD0;
+ float3 worldPos : TEXCOORD1;
+};
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.worldPos = mul(u_Transform, float4(input.position, 1.0)).xyz;
+ float3x3 normalMatrix = (float3x3)u_Transform;
+ output.normal = mul(normalMatrix, input.normal);
+ output.position = mul(u_ViewProjection, float4(output.worldPos, 1.0));
+ return output;
+}
+
+uniform float3 u_Color;
+uniform float u_Specular;
+uniform float u_Emission;
+uniform float3 u_LightPos;
+uniform float3 u_CameraPos;
+uniform int u_IsSelected;
+uniform float3 u_OutlineColor;
+uniform float u_OutlineWidth;
+SamplerCube u_HDRIEnvironment;
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ float3 normal = normalize(input.normal);
+ float3 lightDir = normalize(u_LightPos - input.worldPos);
+ float3 viewDir = normalize(u_CameraPos - input.worldPos);
+ float3 reflectDir = reflect(-lightDir, normal);
+
+ float3 hdriLight = u_HDRIEnvironment.Sample(normal).rgb;
+ float hdriIntensity = 0.3;
+
+ float diff = max(dot(normal, lightDir), 0.0);
+ float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
+
+ float3 diffuse = u_Color * (diff + hdriIntensity * hdriLight);
+ float3 specular = float3(u_Specular, u_Specular, u_Specular) * spec;
+ float3 emission = u_Color * u_Emission;
+
+ float3 result = diffuse + specular + emission;
+
+ if (u_IsSelected > 0)
+ {
+ float ndotv = max(dot(normal, viewDir), 0.0);
+ float rim = 1.0 - ndotv;
+ float outlineMask = step(1.0 - u_OutlineWidth, rim);
+ result = lerp(result, u_OutlineColor, outlineMask);
+ }
+
+ return float4(result, 1.0);
+}
diff --git a/assets/shaders/TexturedQuad.slang b/assets/shaders/TexturedQuad.slang
new file mode 100644
index 0000000..52d46ed
--- /dev/null
+++ b/assets/shaders/TexturedQuad.slang
@@ -0,0 +1,28 @@
+struct VSInput
+{
+ float2 position : POSITION;
+ float2 texCoord : TEXCOORD0;
+};
+
+struct VSOutput
+{
+ float4 position : SV_Position;
+ float2 texCoord : TEXCOORD0;
+};
+
+[shader("vertex")]
+VSOutput vertexMain(VSInput input)
+{
+ VSOutput output;
+ output.position = float4(input.position, 0.0, 1.0);
+ output.texCoord = input.texCoord;
+ return output;
+}
+
+Sampler2D u_ScreenTexture;
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ return u_ScreenTexture.Sample(input.texCoord);
+}
diff --git a/assets/shaders/VkPipelineTest.slang b/assets/shaders/VkPipelineTest.slang
new file mode 100644
index 0000000..f4d1897
--- /dev/null
+++ b/assets/shaders/VkPipelineTest.slang
@@ -0,0 +1,25 @@
+// Minimal shader used only to verify the Vulkan graphics-pipeline path (SPIR-V
+// module -> pipeline -> draw -> read-back). Draws a full-screen gradient
+// triangle from the vertex id, so it needs no vertex buffer, UBO, or sampler.
+
+struct VSOutput
+{
+ float4 position : SV_Position;
+ float3 color : COLOR0;
+};
+
+[shader("vertex")]
+VSOutput vertexMain(uint vid : SV_VertexID)
+{
+ float2 p = float2(float((vid << 1) & 2), float(vid & 2));
+ VSOutput output;
+ output.position = float4(p * 2.0 - 1.0, 0.0, 1.0);
+ output.color = float3(p, 0.5);
+ return output;
+}
+
+[shader("fragment")]
+float4 fragmentMain(VSOutput input) : SV_Target
+{
+ return float4(input.color, 1.0);
+}
diff --git a/branding/Logo-Black.jpg b/branding/Logo-Black.jpg
new file mode 100644
index 0000000..5960c8e
--- /dev/null
+++ b/branding/Logo-Black.jpg
Binary files differ
diff --git a/branding/Logo-Monochrome-Black.jpg b/branding/Logo-Monochrome-Black.jpg
new file mode 100644
index 0000000..65181e4
--- /dev/null
+++ b/branding/Logo-Monochrome-Black.jpg
Binary files differ
diff --git a/branding/Logo-Monochrome-White.jpg b/branding/Logo-Monochrome-White.jpg
new file mode 100644
index 0000000..4397006
--- /dev/null
+++ b/branding/Logo-Monochrome-White.jpg
Binary files differ
diff --git a/branding/Logo-White.jpg b/branding/Logo-White.jpg
new file mode 100644
index 0000000..7981c4b
--- /dev/null
+++ b/branding/Logo-White.jpg
Binary files differ
diff --git a/branding/Screenshot1.png b/branding/Screenshot1.png
new file mode 100644
index 0000000..7175ab6
--- /dev/null
+++ b/branding/Screenshot1.png
Binary files differ
diff --git a/branding/Screenshot2.png b/branding/Screenshot2.png
new file mode 100644
index 0000000..2aece5d
--- /dev/null
+++ b/branding/Screenshot2.png
Binary files differ
diff --git a/Vendor/ImGuizmo b/ext/ImGuizmo
-Subproject 3e725628fa64986c71ebc7c764621a8e7423fef
+Subproject 3e725628fa64986c71ebc7c764621a8e7423fef
diff --git a/Vendor/glad/include/KHR/khrplatform.h b/ext/glad/include/KHR/khrplatform.h
index 0164644..0164644 100644
--- a/Vendor/glad/include/KHR/khrplatform.h
+++ b/ext/glad/include/KHR/khrplatform.h
diff --git a/Vendor/glad/include/glad/glad.h b/ext/glad/include/glad/glad.h
index fc3d009..fc3d009 100644
--- a/Vendor/glad/include/glad/glad.h
+++ b/ext/glad/include/glad/glad.h
diff --git a/Vendor/glad/src/glad.c b/ext/glad/src/glad.c
index c4b142e..c4b142e 100644
--- a/Vendor/glad/src/glad.c
+++ b/ext/glad/src/glad.c
diff --git a/Vendor/glfw b/ext/glfw
-Subproject ac10768495837eb98da27d01fe706073d6d251c
+Subproject ac10768495837eb98da27d01fe706073d6d251c
diff --git a/Vendor/glm b/ext/glm
-Subproject 2d4c4b4dd31fde06cfffad7915c2b3006402322
+Subproject 2d4c4b4dd31fde06cfffad7915c2b3006402322
diff --git a/Vendor/imgui b/ext/imgui
-Subproject 1f7f1f54af38b0350d8c0008b096a9af6de299c
+Subproject 1f7f1f54af38b0350d8c0008b096a9af6de299c
diff --git a/Vendor/json b/ext/json
-Subproject 52d3f6e034504b4097741b2da5b644a38ee273f
+Subproject 52d3f6e034504b4097741b2da5b644a38ee273f
diff --git a/Vendor/stb b/ext/stb
-Subproject f58f558c120e9b32c217290b80bad1a0729fbb2
+Subproject f58f558c120e9b32c217290b80bad1a0729fbb2
diff --git a/Vendor/toml11 b/ext/toml11
-Subproject 2a18a89008d3daac6d8f9db03ddd582173032c7
+Subproject 2a18a89008d3daac6d8f9db03ddd582173032c7
diff --git a/premake5.lua b/premake5.lua
index 4b5b270..2559098 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -28,15 +28,15 @@ workspace "Donut"
outputdir = "%{cfg.buildcfg}-%{cfg.system}/%{prj.name}"
IncludeDir = {}
-IncludeDir["glm"] = "Vendor/glm"
-IncludeDir["glfw"] = "Vendor/glfw/include"
-IncludeDir["glad"] = "Vendor/glad/include"
-IncludeDir["imgui"] = "Vendor/imgui"
-IncludeDir["imgui_backends"] = "Vendor/imgui/backends"
-IncludeDir["imguizmo"] = "Vendor/ImGuizmo"
-IncludeDir["toml11"] = "Vendor/toml11/include"
-IncludeDir["nlohmann"] = "Vendor/json/single_include"
-IncludeDir["stb"] = "Vendor/stb"
+IncludeDir["glm"] = "ext/glm"
+IncludeDir["glfw"] = "ext/glfw/include"
+IncludeDir["glad"] = "ext/glad/include"
+IncludeDir["imgui"] = "ext/imgui"
+IncludeDir["imgui_backends"] = "ext/imgui/backends"
+IncludeDir["imguizmo"] = "ext/ImGuizmo"
+IncludeDir["toml11"] = "ext/toml11/include"
+IncludeDir["nlohmann"] = "ext/json/single_include"
+IncludeDir["stb"] = "ext/stb"
group "Dependencies"
@@ -49,15 +49,15 @@ project "GLAD"
files
{
- "Vendor/glad/include/glad/glad.h",
- "Vendor/glad/include/KHR/khrplatform.h",
+ "ext/glad/include/glad/glad.h",
+ "ext/glad/include/KHR/khrplatform.h",
- "Vendor/glad/src/glad.c"
+ "ext/glad/src/glad.c"
}
includedirs
{
- "Vendor/glad/include"
+ "ext/glad/include"
}
filter "system:windows"
@@ -82,24 +82,24 @@ project "GLFW"
files
{
- "Vendor/glfw/src/context.c",
- "Vendor/glfw/src/init.c",
- "Vendor/glfw/src/input.c",
- "Vendor/glfw/src/monitor.c",
- "Vendor/glfw/src/platform.c",
- "Vendor/glfw/src/vulkan.c",
- "Vendor/glfw/src/window.c",
+ "ext/glfw/src/context.c",
+ "ext/glfw/src/init.c",
+ "ext/glfw/src/input.c",
+ "ext/glfw/src/monitor.c",
+ "ext/glfw/src/platform.c",
+ "ext/glfw/src/vulkan.c",
+ "ext/glfw/src/window.c",
- "Vendor/glfw/src/internal.h",
- "Vendor/glfw/src/platform.h",
- "Vendor/glfw/src/mappings.h",
+ "ext/glfw/src/internal.h",
+ "ext/glfw/src/platform.h",
+ "ext/glfw/src/mappings.h",
- "Vendor/glfw/src/null_init.c",
- "Vendor/glfw/src/null_joystick.c",
- "Vendor/glfw/src/null_joystick.h",
- "Vendor/glfw/src/null_monitor.c",
- "Vendor/glfw/src/null_platform.h",
- "Vendor/glfw/src/null_window.c",
+ "ext/glfw/src/null_init.c",
+ "ext/glfw/src/null_joystick.c",
+ "ext/glfw/src/null_joystick.h",
+ "ext/glfw/src/null_monitor.c",
+ "ext/glfw/src/null_platform.h",
+ "ext/glfw/src/null_window.c",
}
filter "system:linux"
@@ -108,30 +108,30 @@ project "GLFW"
files
{
- "Vendor/glfw/src/x11_init.c",
- "Vendor/glfw/src/x11_monitor.c",
- "Vendor/glfw/src/x11_platform.h",
- "Vendor/glfw/src/x11_window.c",
- "Vendor/glfw/src/xkb_unicode.c",
- "Vendor/glfw/src/xkb_unicode.h",
+ "ext/glfw/src/x11_init.c",
+ "ext/glfw/src/x11_monitor.c",
+ "ext/glfw/src/x11_platform.h",
+ "ext/glfw/src/x11_window.c",
+ "ext/glfw/src/xkb_unicode.c",
+ "ext/glfw/src/xkb_unicode.h",
- "Vendor/glfw/src/wl_init.c",
- "Vendor/glfw/src/wl_monitor.c",
- "Vendor/glfw/src/wl_platform.h",
- "Vendor/glfw/src/wl_window.c",
+ "ext/glfw/src/wl_init.c",
+ "ext/glfw/src/wl_monitor.c",
+ "ext/glfw/src/wl_platform.h",
+ "ext/glfw/src/wl_window.c",
- "Vendor/glfw/src/posix_module.c",
- "Vendor/glfw/src/posix_time.c",
- "Vendor/glfw/src/posix_time.h",
- "Vendor/glfw/src/posix_thread.c",
- "Vendor/glfw/src/posix_thread.h",
+ "ext/glfw/src/posix_module.c",
+ "ext/glfw/src/posix_time.c",
+ "ext/glfw/src/posix_time.h",
+ "ext/glfw/src/posix_thread.c",
+ "ext/glfw/src/posix_thread.h",
- "Vendor/glfw/src/glx_context.c",
- "Vendor/glfw/src/egl_context.c",
- "Vendor/glfw/src/osmesa_context.c",
+ "ext/glfw/src/glx_context.c",
+ "ext/glfw/src/egl_context.c",
+ "ext/glfw/src/osmesa_context.c",
- "Vendor/glfw/src/linux_joystick.c",
- "Vendor/glfw/src/linux_joystick.h"
+ "ext/glfw/src/linux_joystick.c",
+ "ext/glfw/src/linux_joystick.h"
}
defines
@@ -144,22 +144,22 @@ project "GLFW"
files
{
- "Vendor/glfw/src/cocoa_init.m",
- "Vendor/glfw/src/cocoa_joystick.h",
- "Vendor/glfw/src/cocoa_joystick.m",
- "Vendor/glfw/src/cocoa_monitor.m",
- "Vendor/glfw/src/cocoa_platform.h",
- "Vendor/glfw/src/cocoa_time.c",
- "Vendor/glfw/src/cocoa_time.h",
- "Vendor/glfw/src/cocoa_window.m",
+ "ext/glfw/src/cocoa_init.m",
+ "ext/glfw/src/cocoa_joystick.h",
+ "ext/glfw/src/cocoa_joystick.m",
+ "ext/glfw/src/cocoa_monitor.m",
+ "ext/glfw/src/cocoa_platform.h",
+ "ext/glfw/src/cocoa_time.c",
+ "ext/glfw/src/cocoa_time.h",
+ "ext/glfw/src/cocoa_window.m",
- "Vendor/glfw/src/nsgl_context.m",
- "Vendor/glfw/src/egl_context.c",
- "Vendor/glfw/src/osmesa_context.c",
+ "ext/glfw/src/nsgl_context.m",
+ "ext/glfw/src/egl_context.c",
+ "ext/glfw/src/osmesa_context.c",
- "Vendor/glfw/src/posix_module.c",
- "Vendor/glfw/src/posix_thread.c",
- "Vendor/glfw/src/posix_thread.h"
+ "ext/glfw/src/posix_module.c",
+ "ext/glfw/src/posix_thread.c",
+ "ext/glfw/src/posix_thread.h"
}
defines
@@ -172,20 +172,20 @@ project "GLFW"
files
{
- "Vendor/glfw/src/win32_init.c",
- "Vendor/glfw/src/win32_joystick.c",
- "Vendor/glfw/src/win32_joystick.h",
- "Vendor/glfw/src/win32_module.c",
- "Vendor/glfw/src/win32_monitor.c",
- "Vendor/glfw/src/win32_platform.h",
- "Vendor/glfw/src/win32_thread.c",
- "Vendor/glfw/src/win32_thread.h",
- "Vendor/glfw/src/win32_time.c",
- "Vendor/glfw/src/win32_time.h",
- "Vendor/glfw/src/win32_window.c",
- "Vendor/glfw/src/wgl_context.c",
- "Vendor/glfw/src/egl_context.c",
- "Vendor/glfw/src/osmesa_context.c"
+ "ext/glfw/src/win32_init.c",
+ "ext/glfw/src/win32_joystick.c",
+ "ext/glfw/src/win32_joystick.h",
+ "ext/glfw/src/win32_module.c",
+ "ext/glfw/src/win32_monitor.c",
+ "ext/glfw/src/win32_platform.h",
+ "ext/glfw/src/win32_thread.c",
+ "ext/glfw/src/win32_thread.h",
+ "ext/glfw/src/win32_time.c",
+ "ext/glfw/src/win32_time.h",
+ "ext/glfw/src/win32_window.c",
+ "ext/glfw/src/wgl_context.c",
+ "ext/glfw/src/egl_context.c",
+ "ext/glfw/src/osmesa_context.c"
}
defines
@@ -223,11 +223,11 @@ project "ImGui"
files
{
- "Vendor/imgui/imgui.cpp",
- "Vendor/imgui/imgui_draw.cpp",
- "Vendor/imgui/imgui_tables.cpp",
- "Vendor/imgui/imgui_widgets.cpp",
- "Vendor/imgui/imgui_demo.cpp"
+ "ext/imgui/imgui.cpp",
+ "ext/imgui/imgui_draw.cpp",
+ "ext/imgui/imgui_tables.cpp",
+ "ext/imgui/imgui_widgets.cpp",
+ "ext/imgui/imgui_demo.cpp"
}
filter "system:windows"
@@ -262,8 +262,8 @@ project "ImGuizmo"
files
{
- "Vendor/ImGuizmo/ImGuizmo.cpp",
- "Vendor/ImGuizmo/ImGuizmo.h"
+ "ext/ImGuizmo/ImGuizmo.cpp",
+ "ext/ImGuizmo/ImGuizmo.h"
}
includedirs
@@ -313,22 +313,22 @@ project "Donut"
files
{
- "Vendor/glm/glm/**.hpp",
- "Vendor/glm/glm/**.inl",
+ "ext/glm/glm/**.hpp",
+ "ext/glm/glm/**.inl",
"src/**.h",
"src/**.cpp",
- "src/Rendering/Framebuffer.h",
- "src/Rendering/Framebuffer.cpp",
- "src/Platform/OpenGL/OpenGLFramebuffer.h",
- "src/Platform/OpenGL/OpenGLFramebuffer.cpp",
+ "src/rendering/framebuffer.h",
+ "src/rendering/framebuffer.cpp",
+ "src/platform/opengl/opengl_framebuffer.h",
+ "src/platform/opengl/opengl_framebuffer.cpp",
- "Vendor/imgui/backends/imgui_impl_glfw.cpp",
- "Vendor/imgui/backends/imgui_impl_opengl3.cpp",
+ "ext/imgui/backends/imgui_impl_glfw.cpp",
+ "ext/imgui/backends/imgui_impl_opengl3.cpp",
- "Assets/Fonts/Inter/static/Inter_18pt-Regular.ttf",
- "Assets/Fonts/Inter/static/Inter_18pt-Bold.ttf",
- "Assets/Fonts/Inter/static/Inter_18pt-Light.ttf"
+ "assets/fonts/inter/static/Inter_18pt-Regular.ttf",
+ "assets/fonts/inter/static/Inter_18pt-Bold.ttf",
+ "assets/fonts/inter/static/Inter_18pt-Light.ttf"
}
includedirs
@@ -368,17 +368,17 @@ project "Donut"
files
{
"src/**.mm",
- "Vendor/imgui/backends/imgui_impl_vulkan.cpp"
+ "ext/imgui/backends/imgui_impl_vulkan.cpp"
}
- -- Compile the Slang shaders to Assets/Shaders/generated/ before building.
- -- Requires Tools/slang (Tools/fetch-slang.sh) and spirv-cross on PATH.
+ -- Compile the Slang shaders to assets/shaders/generated/ before building.
+ -- Requires tools/slang (tools/fetch-slang.sh) and spirv-cross on PATH.
prebuildcommands
{
- 'bash "%{wks.location}/Tools/compile-shaders.sh"'
+ 'bash "%{wks.location}/tools/compile-shaders.sh"'
}
- -- Vulkan (via MoltenVK) from Homebrew; see Tools/vulkan-env.sh for runtime.
+ -- Vulkan (via MoltenVK) from Homebrew; see tools/vulkan-env.sh for runtime.
includedirs { "/opt/homebrew/include" }
libdirs { "/opt/homebrew/lib" }
diff --git a/src/Core/Application.cpp b/src/Core/Application.cpp
index c73dc6d..667fc73 100644
--- a/src/Core/Application.cpp
+++ b/src/Core/Application.cpp
@@ -1,87 +1,87 @@
-#include "Application.h"
+#include "application.h"
-#include "Rendering/Renderer.h"
-#include "SettingsManager.h"
+#include "rendering/renderer.h"
+#include "settings_manager.h"
-#include "States/SimulationState.h"
-#include "States/ConfigState.h"
-#include "States/WorldBuilderState.h"
+#include "states/simulation_state.h"
+#include "states/config_state.h"
+#include "states/world_builder_state.h"
#include <GLFW/glfw3.h>
#include <imgui.h>
#include <ImGuizmo.h>
-#include "Platform/Vulkan/VulkanRenderer.h"
+#include "platform/vulkan/vulkan_renderer.h"
#ifdef __APPLE__
-#include "Platform/Metal/MetalContext.h"
-#include "Platform/Vulkan/VulkanContext.h"
+#include "platform/metal/metal_context.h"
+#include "platform/vulkan/vulkan_context.h"
#endif
namespace Donut
{
- Application* Application::s_Instance = nullptr;
+ Application* Application::s_instance = nullptr;
Application::Application(const std::string& name, int width, int height)
- : m_Running(true), m_Minimized(false)
+ : m_running(true), m_minimized(false)
{
- s_Instance = this;
+ s_instance = this;
// Select the render API before the window is created: the window is
// built differently for Vulkan (GLFW_NO_API) than for OpenGL.
- Logger::Init();
- SettingsManager::Initialize();
+ Logger::init();
+ SettingsManager::initialize();
{
- const auto& settings = SettingsManager::GetSettingsConst();
- RendererAPI::SetAPI(settings.graphics.renderAPI == "Vulkan"
+ const auto& settings = SettingsManager::get_settings_const();
+ RendererAPI::set_api(settings.graphics.render_api == "Vulkan"
? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL);
}
- if (RendererAPI::GetAPI() == RendererAPI::API::Vulkan)
- VulkanPrepareGLFW(); // must precede glfwInit() inside the Window ctor
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
+ vulkan_prepare_glfw(); // must precede glfwInit() inside the Window ctor
- m_Window = CreateScope<Window>(name, width, height);
- m_Window->SetEventCallback([this](Event& event)
+ m_window = create_scope<Window>(name, width, height);
+ m_window->set_event_callback([this](Event& event)
{
- OnEvent(event);
+ on_event(event);
});
- OnInit();
+ on_init();
}
Application::~Application()
{
- OnShutdown();
- s_Instance = nullptr;
+ on_shutdown();
+ s_instance = nullptr;
}
- void Application::Run()
+ auto Application::run() -> void
{
- while (m_Running)
+ while (m_running)
{
- if (m_VulkanRenderer)
+ if (m_vulkan_renderer)
{
glfwPollEvents(); // before the ImGui frame so input is current
- if (!m_Minimized)
- m_VulkanRenderer->DrawFrame(glm::vec4(0.05f, 0.06f, 0.10f, 1.0f),
- [this] { BuildVulkanUI(); });
+ if (!m_minimized)
+ m_vulkan_renderer->draw_frame(glm::vec4(0.05f, 0.06f, 0.10f, 1.0f),
+ [this] { build_vulkan_ui(); });
}
else
{
- if (!m_Minimized)
+ if (!m_minimized)
{
- OnUpdate();
- OnRender();
+ on_update();
+ on_render();
}
- m_Window->OnUpdate(); // polls events + swaps buffers (OpenGL)
+ m_window->on_update(); // polls events + swaps buffers (OpenGL)
}
}
}
- void Application::BuildVulkanUI()
+ auto Application::build_vulkan_ui() -> void
{
// Placeholder UI proving the ImGui Vulkan backend renders. The real
- // application UI (states' OnImUIRender) moves here once the scene is
+ // application UI (states' on_im_ui_render) moves here once the scene is
// ported to Vulkan (B-3).
ImGui::Begin("Donut - Vulkan backend");
ImGui::Text("Geodesic black hole through Vulkan (MoltenVK).");
@@ -92,145 +92,145 @@ namespace Donut
ImGui::End();
}
- void Application::Close()
+ auto Application::close() -> void
{
- m_Running = false;
+ m_running = false;
}
- void Application::OnEvent(Event& event)
+ auto Application::on_event(Event& event) -> void
{
EventDispatcher dispatcher(event);
- dispatcher.Dispatch<WindowCloseEvent>([this, &event](WindowCloseEvent& e)
+ dispatcher.dispatch<WindowCloseEvent>([this, &event](WindowCloseEvent& e)
{
- m_Running = false;
- event.Handled = true;
+ m_running = false;
+ event.handled = true;
return true;
});
-
- dispatcher.Dispatch<WindowResizeEvent>([this, &event](WindowResizeEvent& e)
+
+ dispatcher.dispatch<WindowResizeEvent>([this, &event](WindowResizeEvent& e)
{
- if (e.GetWidth() == 0 || e.GetHeight() == 0)
- m_Minimized = true;
+ if (e.get_width() == 0 || e.get_height() == 0)
+ m_minimized = true;
else
- m_Minimized = false;
+ m_minimized = false;
- if (m_VulkanRenderer)
+ if (m_vulkan_renderer)
{
- m_VulkanRenderer->OnResize(e.GetWidth(), e.GetHeight());
+ m_vulkan_renderer->on_resize(e.get_width(), e.get_height());
}
else
{
- Renderer::OnWindowResize(e.GetWidth(), e.GetHeight());
- if (m_Engine) m_Engine->SetWindowDimensions(e.GetWidth(), e.GetHeight());
+ Renderer::on_window_resize(e.get_width(), e.get_height());
+ if (m_engine) m_engine->set_window_dimensions(e.get_width(), e.get_height());
}
- event.Handled = true;
+ event.handled = true;
return true;
});
-
- dispatcher.Dispatch<KeyPressedEvent>([this, &event](KeyPressedEvent& e)
+
+ dispatcher.dispatch<KeyPressedEvent>([this, &event](KeyPressedEvent& e)
{
- if (!m_StateManager)
+ if (!m_state_manager)
return true;
- if (e.GetKeyCode() == GLFW_KEY_1)
+ if (e.get_key_code() == GLFW_KEY_1)
{
- m_StateManager->SwitchToState("Config");
- event.Handled = true;
+ m_state_manager->switch_to_state("Config");
+ event.handled = true;
return true;
}
- else if (e.GetKeyCode() == GLFW_KEY_2)
+ else if (e.get_key_code() == GLFW_KEY_2)
{
- m_StateManager->SwitchToState("Simulation");
- event.Handled = true;
+ m_state_manager->switch_to_state("Simulation");
+ event.handled = true;
return true;
}
- else if (e.GetKeyCode() == GLFW_KEY_3)
+ else if (e.get_key_code() == GLFW_KEY_3)
{
- m_StateManager->SwitchToState("WorldBuilder");
- event.Handled = true;
+ m_state_manager->switch_to_state("WorldBuilder");
+ event.handled = true;
return true;
}
-
+
return true;
});
- if (m_StateManager)
- m_StateManager->OnEvent(event);
+ if (m_state_manager)
+ m_state_manager->on_event(event);
}
- void Application::OnInit()
+ auto Application::on_init() -> void
{
// (Logger, settings and API selection happen in the constructor.)
- if (RendererAPI::GetAPI() == RendererAPI::API::Vulkan)
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
{
int w = 0, h = 0;
- glfwGetFramebufferSize((GLFWwindow*)m_Window->GetNativeWindow(), &w, &h);
- m_VulkanRenderer = CreateScope<VulkanRenderer>();
- if (!m_VulkanRenderer->Init(m_Window->GetNativeWindow(), w, h))
+ glfwGetFramebufferSize((GLFWwindow*)m_window->get_native_window(), &w, &h);
+ m_vulkan_renderer = create_scope<VulkanRenderer>();
+ if (!m_vulkan_renderer->init(m_window->get_native_window(), w, h))
DONUT_ERROR("Vulkan renderer initialization failed");
else
- m_VulkanRenderer->InitImGui();
+ m_vulkan_renderer->init_im_gui();
// Scene rendering hooks in here in the B-3 phase; the Vulkan path now
// clears, presents, and draws the ImGui UI.
return;
}
- Renderer::Init();
- m_Window->InitImGui();
+ Renderer::init();
+ m_window->init_im_gui();
- Renderer::OnWindowResize(1280, 720);
- RenderCommand::SetFaceCulling(false);
+ Renderer::on_window_resize(1280, 720);
+ RenderCommand::set_face_culling(false);
- m_Engine = CreateScope<Engine>();
- m_Engine->SetWindowDimensions(1280, 720);
+ m_engine = create_scope<Engine>();
+ m_engine->set_window_dimensions(1280, 720);
- m_StateManager = CreateScope<StateManager>();
- m_StateManager->RegisterState("Config", CreateScope<ConfigState>());
- m_StateManager->RegisterState("Simulation", CreateScope<SimulationState>());
- m_StateManager->RegisterState("WorldBuilder", CreateScope<WorldBuilderState>());
- m_StateManager->SwitchToState("Config");
+ m_state_manager = create_scope<StateManager>();
+ m_state_manager->register_state("Config", create_scope<ConfigState>());
+ m_state_manager->register_state("Simulation", create_scope<SimulationState>());
+ m_state_manager->register_state("WorldBuilder", create_scope<WorldBuilderState>());
+ m_state_manager->switch_to_state("Config");
}
- void Application::OnShutdown()
+ auto Application::on_shutdown() -> void
{
- if (m_VulkanRenderer)
+ if (m_vulkan_renderer)
{
- m_VulkanRenderer->Shutdown();
- m_VulkanRenderer.reset();
+ m_vulkan_renderer->shutdown();
+ m_vulkan_renderer.reset();
}
else
{
- if (m_StateManager)
- m_StateManager->Shutdown();
- Renderer::Shutdown();
+ if (m_state_manager)
+ m_state_manager->shutdown();
+ Renderer::shutdown();
}
- SettingsManager::Shutdown();
- Logger::Shutdown();
+ SettingsManager::shutdown();
+ Logger::shutdown();
}
- void Application::OnUpdate()
+ auto Application::on_update() -> void
{
- float currentFrame = (float)glfwGetTime();
- m_DeltaTime = currentFrame - m_LastFrame;
- m_LastFrame = currentFrame;
+ float current_frame = (float)glfwGetTime();
+ m_delta_time = current_frame - m_last_frame;
+ m_last_frame = current_frame;
- m_StateManager->Update(m_DeltaTime);
+ m_state_manager->update(m_delta_time);
}
- void Application::OnRender()
+ auto Application::on_render() -> void
{
- m_StateManager->Render();
- m_Window->BeginImGuiFrame();
-
+ m_state_manager->render();
+ m_window->begin_im_gui_frame();
+
ImGuizmo::BeginFrame();
ImGuizmo::SetOrthographic(false);
-
- SetupDockingLayout();
- m_StateManager->OnImUIRender();
- m_Window->EndImGuiFrame();
+
+ setup_docking_layout();
+ m_state_manager->on_im_ui_render();
+ m_window->end_im_gui_frame();
}
-
- void Application::SetupDockingLayout()
+
+ auto Application::setup_docking_layout() -> void
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags_PassthruCentralNode);
diff --git a/src/Core/Application.h b/src/Core/Application.h
index b715170..06c4e94 100644
--- a/src/Core/Application.h
+++ b/src/Core/Application.h
@@ -1,51 +1,53 @@
#pragma once
-#include "StateManager.h"
-#include "Memory.h"
-#include "Window.h"
-#include "Event.h"
-#include "Log.h"
+#include "state_manager.h"
+#include "memory.h"
+#include "window.h"
+#include "event.h"
+#include "log.h"
-#include "Engine/Engine.h"
+#include "engine/engine.h"
namespace Donut
{
- class VulkanRenderer; // live-window Vulkan backend (Platform/Vulkan)
+ class VulkanRenderer; // live-window Vulkan backend (platform/vulkan)
class Application
{
public:
- Application(const std::string& name = "Donut",
+ Application(const std::string& name = "Donut",
int width = 1280, int height = 720);
~Application();
- void Run();
- void Close();
+ auto run() -> void;
+ auto close() -> void;
+
+ auto get_window() -> Window& { return *m_window; }
+ auto get_state_manager() -> StateManager& { return *m_state_manager; }
+ auto get_engine() -> Engine& { return *m_engine; }
+ static auto get() -> Application& { return *s_instance; }
- Window& GetWindow() { return *m_Window; }
- StateManager& GetStateManager() { return *m_StateManager; }
- Engine& GetEngine() { return *m_Engine; }
- static Application& Get() { return *s_Instance; }
private:
- void OnInit();
- void OnShutdown();
- void OnUpdate();
- void OnRender();
- void OnEvent(Event& event);
- void SetupDockingLayout();
- void BuildVulkanUI(); // ImGui UI built each frame on the Vulkan path
+ auto on_init() -> void;
+ auto on_shutdown() -> void;
+ auto on_update() -> void;
+ auto on_render() -> void;
+ auto on_event(Event& event) -> void;
+ auto setup_docking_layout() -> void;
+ auto build_vulkan_ui() -> void; // ImGui UI built each frame on the Vulkan path
+
private:
- Scope<StateManager> m_StateManager;
- Scope<Window> m_Window;
- Scope<Engine> m_Engine;
- Scope<VulkanRenderer> m_VulkanRenderer; // non-null only when the Vulkan API is selected
+ Scope<StateManager> m_state_manager;
+ Scope<Window> m_window;
+ Scope<Engine> m_engine;
+ Scope<VulkanRenderer> m_vulkan_renderer; // non-null only when the Vulkan API is selected
+
+ bool m_running;
+ bool m_minimized;
- bool m_Running;
- bool m_Minimized;
-
- float m_DeltaTime = 0.0f;
- float m_LastFrame = 0.0f;
+ float m_delta_time = 0.0f;
+ float m_last_frame = 0.0f;
- static Application* s_Instance;
+ static Application* s_instance;
};
-} \ No newline at end of file
+}
diff --git a/src/Core/Camera.cpp b/src/Core/Camera.cpp
index 028924e..35b7a2f 100644
--- a/src/Core/Camera.cpp
+++ b/src/Core/Camera.cpp
@@ -1,68 +1,68 @@
-#include "Camera.h"
+#include "camera.h"
#include <glm/gtc/quaternion.hpp>
#include <GLFW/glfw3.h>
namespace Donut
{
- Camera::Camera(float fov, float aspectRatio, float nearPlane, float farPlane)
- : m_FOV(fov), m_AspectRatio(aspectRatio), m_NearPlane(nearPlane), m_FarPlane(farPlane)
+ Camera::Camera(float fov, float aspect_ratio, float near_plane, float far_plane)
+ : m_fov(fov), m_aspect_ratio(aspect_ratio), m_near_plane(near_plane), m_far_plane(far_plane)
{
- RecalculateProjectionMatrix();
- RecalculateViewMatrix();
+ recalculate_projection_matrix();
+ recalculate_view_matrix();
}
- void Camera::SetProjection(float fov, float aspectRatio, float nearPlane, float farPlane)
+ auto Camera::set_projection(float fov, float aspect_ratio, float near_plane, float far_plane) -> void
{
- m_FOV = fov;
- m_AspectRatio = aspectRatio;
- m_NearPlane = nearPlane;
- m_FarPlane = farPlane;
- RecalculateProjectionMatrix();
+ m_fov = fov;
+ m_aspect_ratio = aspect_ratio;
+ m_near_plane = near_plane;
+ m_far_plane = far_plane;
+ recalculate_projection_matrix();
}
- void Camera::RecalculateProjectionMatrix()
+ auto Camera::recalculate_projection_matrix() -> void
{
- m_ProjectionMatrix = glm::perspective(glm::radians(m_FOV), m_AspectRatio, m_NearPlane, m_FarPlane);
- m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
+ m_projection_matrix = glm::perspective(glm::radians(m_fov), m_aspect_ratio, m_near_plane, m_far_plane);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
}
- void Camera::RecalculateViewMatrix()
+ auto Camera::recalculate_view_matrix() -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ 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);
+ 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 worldUp(0.0f, 1.0f, 0.0f);
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
glm::vec3 front = glm::normalize(direction);
- glm::vec3 right = glm::normalize(glm::cross(front, worldUp));
+ glm::vec3 right = glm::normalize(glm::cross(front, world_up));
glm::vec3 up = glm::normalize(glm::cross(right, front));
- m_ViewMatrix = glm::lookAt(m_Position, m_Position + front, up);
- m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
+ m_view_matrix = glm::lookAt(m_position, m_position + front, up);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
}
- else if (m_CameraMode == CameraMode::Orbital)
+ else if (m_camera_mode == CameraMode::Orbital)
{
- glm::vec3 position = GetOrbitalPosition();
- glm::vec3 target = m_OrbitalTarget;
+ glm::vec3 position = get_orbital_position();
+ glm::vec3 target = m_orbital_target;
glm::vec3 up(0.0f, 1.0f, 0.0f);
-
- m_ViewMatrix = glm::lookAt(position, target, up);
- m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
+
+ m_view_matrix = glm::lookAt(position, target, up);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
}
}
- glm::vec3 Camera::GetForwardDirection() const
+ auto Camera::get_forward_direction() const -> glm::vec3
{
- float pitch = glm::radians(m_Rotation.x);
- float yaw = glm::radians(m_Rotation.y);
+ float pitch = glm::radians(m_rotation.x);
+ float yaw = glm::radians(m_rotation.y);
glm::vec3 direction;
direction.x = cos(yaw) * cos(pitch);
@@ -72,154 +72,154 @@ namespace Donut
return glm::normalize(direction);
}
- glm::vec3 Camera::GetRightDirection() const
+ auto Camera::get_right_direction() const -> glm::vec3
{
- glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
- return glm::normalize(glm::cross(GetForwardDirection(), worldUp));
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ return glm::normalize(glm::cross(get_forward_direction(), world_up));
}
- glm::vec3 Camera::GetUpDirection() const
+ auto Camera::get_up_direction() const -> glm::vec3
{
- return glm::normalize(glm::cross(GetRightDirection(), GetForwardDirection()));
+ return glm::normalize(glm::cross(get_right_direction(), get_forward_direction()));
}
- void Camera::OnMouseMove(float xOffset, float yOffset, bool constrainPitch)
+ auto Camera::on_mouse_move(float x_offset, float y_offset, bool constrain_pitch) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- xOffset *= m_MouseSensitivity;
- yOffset *= m_MouseSensitivity;
+ x_offset *= m_mouse_sensitivity;
+ y_offset *= m_mouse_sensitivity;
- m_Rotation.y += xOffset;
- m_Rotation.x += yOffset;
+ m_rotation.y += x_offset;
+ m_rotation.x += y_offset;
- if (constrainPitch)
+ 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 (m_rotation.x > 89.0f)
+ m_rotation.x = 89.0f;
+ if (m_rotation.x < -89.0f)
+ m_rotation.x = -89.0f;
}
- RecalculateViewMatrix();
+ recalculate_view_matrix();
}
}
- void Camera::MoveForward(float deltaTime)
+ auto Camera::move_forward(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- m_Position += GetForwardDirection() * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ m_position += get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- void Camera::MoveBackward(float deltaTime)
+ auto Camera::move_backward(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- m_Position -= GetForwardDirection() * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ m_position -= get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- void Camera::MoveRight(float deltaTime)
+ auto Camera::move_right(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- m_Position += GetRightDirection() * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ m_position += get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- void Camera::MoveLeft(float deltaTime)
+ auto Camera::move_left(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- m_Position -= GetRightDirection() * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ m_position -= get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- void Camera::MoveUp(float deltaTime)
+ auto Camera::move_up(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
- m_Position += worldUp * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ m_position += world_up * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- void Camera::MoveDown(float deltaTime)
+ auto Camera::move_down(float delta_time) -> void
{
- if (m_CameraMode == CameraMode::FPS)
+ if (m_camera_mode == CameraMode::FPS)
{
- glm::vec3 worldUp(0.0f, 1.0f, 0.0f);
- m_Position -= worldUp * m_MovementSpeed * deltaTime;
- RecalculateViewMatrix();
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ m_position -= world_up * m_movement_speed * delta_time;
+ recalculate_view_matrix();
}
}
- glm::vec3 Camera::GetOrbitalPosition() const
+ auto Camera::get_orbital_position() const -> glm::vec3
{
- float clampedElevation = glm::clamp(m_Elevation, 0.01f, float(std::numbers::pi) - 0.01f);
+ float clamped_elevation = glm::clamp(m_elevation, 0.01f, float(std::numbers::pi) - 0.01f);
return glm::vec3
(
- m_OrbitalRadius * sin(clampedElevation) * cos(m_Azimuth),
- m_OrbitalRadius * cos(clampedElevation),
- m_OrbitalRadius * sin(clampedElevation) * sin(m_Azimuth)
+ m_orbital_radius * sin(clamped_elevation) * cos(m_azimuth),
+ m_orbital_radius * cos(clamped_elevation),
+ m_orbital_radius * sin(clamped_elevation) * sin(m_azimuth)
);
}
- void Camera::UpdateOrbital()
+ auto Camera::update_orbital() -> void
{
- m_OrbitalTarget = glm::vec3(0.0f, 0.0f, 0.0f);
- if (m_Dragging || m_Panning)
- m_Moving = true;
+ m_orbital_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ if (m_dragging || m_panning)
+ m_moving = true;
else
- m_Moving = false;
- RecalculateViewMatrix();
+ m_moving = false;
+ recalculate_view_matrix();
}
- void Camera::ProcessOrbitalMouseMove(double x, double y)
+ auto Camera::process_orbital_mouse_move(double x, double y) -> void
{
- if (m_Dragging && !m_Panning)
+ if (m_dragging && !m_panning)
{
- float dx = float(x - m_LastX_Orbital);
- float dy = float(y - m_LastY_Orbital);
-
- m_Azimuth += dx * m_OrbitalSpeed;
- m_Elevation -= dy * m_OrbitalSpeed;
- m_Elevation = glm::clamp(m_Elevation, 0.01f, float(std::numbers::pi) - 0.01f);
+ float dx = float(x - m_last_x_orbital);
+ float dy = float(y - m_last_y_orbital);
+
+ m_azimuth += dx * m_orbital_speed;
+ m_elevation -= dy * m_orbital_speed;
+ m_elevation = glm::clamp(m_elevation, 0.01f, float(std::numbers::pi) - 0.01f);
}
-
- m_LastX_Orbital = x;
- m_LastY_Orbital = y;
- UpdateOrbital();
+
+ m_last_x_orbital = x;
+ m_last_y_orbital = y;
+ update_orbital();
}
- void Camera::ProcessOrbitalMouseButton(int button, int action, int mods)
+ auto Camera::process_orbital_mouse_button(int button, int action, int mods) -> void
{
if (button == GLFW_MOUSE_BUTTON_LEFT)
{
if (action == GLFW_PRESS)
{
- m_Dragging = true;
- m_Panning = false;
+ m_dragging = true;
+ m_panning = false;
}
else if (action == GLFW_RELEASE)
{
- m_Dragging = false;
- m_Panning = false;
+ m_dragging = false;
+ m_panning = false;
}
}
}
- void Camera::ProcessOrbitalScroll(double xoffset, double yoffset)
+ auto Camera::process_orbital_scroll(double x_offset, double y_offset) -> void
{
- m_OrbitalRadius -= yoffset * m_ZoomSpeed;
- m_OrbitalRadius = glm::clamp(m_OrbitalRadius, m_OrbitalMinRadius, m_OrbitalMaxRadius);
- UpdateOrbital();
+ m_orbital_radius -= y_offset * m_zoom_speed;
+ m_orbital_radius = glm::clamp(m_orbital_radius, m_orbital_min_radius, m_orbital_max_radius);
+ update_orbital();
}
}
diff --git a/src/Core/Camera.h b/src/Core/Camera.h
index a9aec2a..c997c35 100644
--- a/src/Core/Camera.h
+++ b/src/Core/Camera.h
@@ -16,115 +16,116 @@ namespace Donut
class Camera
{
public:
- Camera(float fov = 45.0f, float aspectRatio = 16.0f / 9.0f,
- float nearPlane = 0.1f, float farPlane = 100.0f);
+ Camera(float fov = 45.0f, float aspect_ratio = 16.0f / 9.0f,
+ float near_plane = 0.1f, float far_plane = 100.0f);
~Camera() = default;
- void SetPosition(const glm::vec3& position) { m_Position = position; RecalculateViewMatrix(); }
- void SetRotation(const glm::vec3& rotation) { m_Rotation = rotation; RecalculateViewMatrix(); }
-
- const glm::vec3& GetPosition() const { return m_Position; }
- const glm::vec3& GetRotation() const { return m_Rotation; }
-
- glm::vec3 GetForwardDirection() const;
- glm::vec3 GetRightDirection() const;
- glm::vec3 GetUpDirection() const;
+ auto set_position(const glm::vec3& position) -> void { m_position = position; recalculate_view_matrix(); }
+ auto set_rotation(const glm::vec3& rotation) -> void { m_rotation = rotation; recalculate_view_matrix(); }
- const glm::mat4& GetProjectionMatrix() const { return m_ProjectionMatrix; }
- const glm::mat4& GetViewMatrix() const { return m_ViewMatrix; }
- const glm::mat4& GetViewProjectionMatrix() const { return m_ViewProjectionMatrix; }
+ auto get_position() const -> const glm::vec3& { return m_position; }
+ auto get_rotation() const -> const glm::vec3& { return m_rotation; }
- void SetProjection(float fov, float aspectRatio, float nearPlane, float farPlane);
- void OnMouseMove(float xOffset, float yOffset, bool constrainPitch = true);
-
- void MoveForward(float deltaTime);
- void MoveBackward(float deltaTime);
- void MoveRight(float deltaTime);
- void MoveLeft(float deltaTime);
- void MoveUp(float deltaTime);
- void MoveDown(float deltaTime);
+ auto get_forward_direction() const -> glm::vec3;
+ auto get_right_direction() const -> glm::vec3;
+ auto get_up_direction() const -> glm::vec3;
- void SetMouseSensitivity(float sensitivity) { m_MouseSensitivity = sensitivity; }
- float GetMouseSensitivity() const { return m_MouseSensitivity; }
+ auto get_projection_matrix() const -> const glm::mat4& { return m_projection_matrix; }
+ auto get_view_matrix() const -> const glm::mat4& { return m_view_matrix; }
+ auto get_view_projection_matrix() const -> const glm::mat4& { return m_view_projection_matrix; }
- void SetMovementSpeed(float speed) { m_MovementSpeed = speed; }
- float GetMovementSpeed() const { return m_MovementSpeed; }
+ auto set_projection(float fov, float aspect_ratio, float near_plane, float far_plane) -> void;
+ auto on_mouse_move(float x_offset, float y_offset, bool constrain_pitch = true) -> void;
- void SetOrbitalTarget(const glm::vec3& target) { m_OrbitalTarget = target; }
- const glm::vec3& GetOrbitalTarget() const { return m_OrbitalTarget; }
-
- void SetOrbitalRadius(double radius) { m_OrbitalRadius = radius; }
- double GetOrbitalRadius() const { return m_OrbitalRadius; }
-
- void SetOrbitalLimits(double minRadius, double maxRadius)
- {
- m_OrbitalMinRadius = minRadius;
- m_OrbitalMaxRadius = maxRadius;
+ auto move_forward(float delta_time) -> void;
+ auto move_backward(float delta_time) -> void;
+ auto move_right(float delta_time) -> void;
+ auto move_left(float delta_time) -> void;
+ auto move_up(float delta_time) -> void;
+ auto move_down(float delta_time) -> void;
+
+ auto set_mouse_sensitivity(float sensitivity) -> void { m_mouse_sensitivity = sensitivity; }
+ auto get_mouse_sensitivity() const -> float { return m_mouse_sensitivity; }
+
+ auto set_movement_speed(float speed) -> void { m_movement_speed = speed; }
+ auto get_movement_speed() const -> float { return m_movement_speed; }
+
+ auto set_orbital_target(const glm::vec3& target) -> void { m_orbital_target = target; }
+ auto get_orbital_target() const -> const glm::vec3& { return m_orbital_target; }
+
+ auto set_orbital_radius(double radius) -> void { m_orbital_radius = radius; }
+ auto get_orbital_radius() const -> double { return m_orbital_radius; }
+
+ auto set_orbital_limits(double min_radius, double max_radius) -> void
+ {
+ m_orbital_min_radius = min_radius;
+ m_orbital_max_radius = max_radius;
}
-
- void SetOrbitalSpeed(float speed) { m_OrbitalSpeed = speed; }
- float GetOrbitalSpeed() const { return m_OrbitalSpeed; }
-
- void SetZoomSpeed(double speed) { m_ZoomSpeed = speed; }
- double GetZoomSpeed() const { return m_ZoomSpeed; }
-
- void SetAzimuth(float azimuth) { m_Azimuth = azimuth; }
- float GetAzimuth() const { return m_Azimuth; }
-
- void SetElevation(float elevation) { m_Elevation = elevation; }
- float GetElevation() const { return m_Elevation; }
-
- glm::vec3 GetOrbitalPosition() const;
- void UpdateOrbital();
- void ProcessOrbitalMouseMove(double x, double y);
- void ProcessOrbitalMouseButton(int button, int action, int mods);
- void ProcessOrbitalScroll(double xoffset, double yoffset);
-
- void SetCameraMode(CameraMode mode) { m_CameraMode = mode; }
- CameraMode GetCameraMode() const { return m_CameraMode; }
-
- bool IsDragging() const { return m_Dragging; }
- bool IsPanning() const { return m_Panning; }
- bool IsMoving() const { return m_Moving; }
- double GetLastX() const { return m_LastX; }
- double GetLastY() const { return m_LastY; }
+
+ auto set_orbital_speed(float speed) -> void { m_orbital_speed = speed; }
+ auto get_orbital_speed() const -> float { return m_orbital_speed; }
+
+ auto set_zoom_speed(double speed) -> void { m_zoom_speed = speed; }
+ auto get_zoom_speed() const -> double { return m_zoom_speed; }
+
+ auto set_azimuth(float azimuth) -> void { m_azimuth = azimuth; }
+ auto get_azimuth() const -> float { return m_azimuth; }
+
+ auto set_elevation(float elevation) -> void { m_elevation = elevation; }
+ auto get_elevation() const -> float { return m_elevation; }
+
+ auto get_orbital_position() const -> glm::vec3;
+ auto update_orbital() -> void;
+ auto process_orbital_mouse_move(double x, double y) -> void;
+ auto process_orbital_mouse_button(int button, int action, int mods) -> void;
+ auto process_orbital_scroll(double x_offset, double y_offset) -> void;
+
+ auto set_camera_mode(CameraMode mode) -> void { m_camera_mode = mode; }
+ auto get_camera_mode() const -> CameraMode { return m_camera_mode; }
+
+ auto is_dragging() const -> bool { return m_dragging; }
+ auto is_panning() const -> bool { return m_panning; }
+ auto is_moving() const -> bool { return m_moving; }
+ auto get_last_x() const -> double { return m_last_x; }
+ auto get_last_y() const -> double { return m_last_y; }
private:
- void RecalculateViewMatrix();
- void RecalculateProjectionMatrix();
+ auto recalculate_view_matrix() -> void;
+ auto recalculate_projection_matrix() -> void;
+
private:
- CameraMode m_CameraMode = CameraMode::FPS;
-
- glm::mat4 m_ProjectionMatrix;
- glm::mat4 m_ViewMatrix;
- glm::mat4 m_ViewProjectionMatrix;
+ CameraMode m_camera_mode = CameraMode::FPS;
+
+ glm::mat4 m_projection_matrix;
+ glm::mat4 m_view_matrix;
+ glm::mat4 m_view_projection_matrix;
+
+ glm::vec3 m_position = { 0.0f, 0.0f, 3.0f };
+ glm::vec3 m_rotation = { 0.0f, 0.0f, 0.0f };
- glm::vec3 m_Position = { 0.0f, 0.0f, 3.0f };
- glm::vec3 m_Rotation = { 0.0f, 0.0f, 0.0f };
+ float m_fov = 45.0f;
+ float m_aspect_ratio = 16.0f / 9.0f;
+ float m_near_plane = 0.1f;
+ float m_far_plane = 100.0f;
- float m_FOV = 45.0f;
- float m_AspectRatio = 16.0f / 9.0f;
- float m_NearPlane = 0.1f;
- float m_FarPlane = 100.0f;
+ float m_mouse_sensitivity = 0.1f;
+ float m_movement_speed = 5.0f;
+ bool m_first_mouse = true;
+ float m_last_x = 0.0f;
+ float m_last_y = 0.0f;
- float m_MouseSensitivity = 0.1f;
- float m_MovementSpeed = 5.0f;
- bool m_FirstMouse = true;
- float m_LastX = 0.0f;
- float m_LastY = 0.0f;
-
- glm::vec3 m_OrbitalTarget = glm::vec3(0.0f, 0.0f, 0.0f);
- double m_OrbitalRadius = 6.34194e10;
- double m_OrbitalMinRadius = 1e10;
- double m_OrbitalMaxRadius = 1e12;
- float m_Azimuth = 0.0f;
- float m_Elevation = static_cast<float>(std::numbers::pi) / 2.0f;
- float m_OrbitalSpeed = 0.01f;
- double m_ZoomSpeed = 25e9f;
- bool m_Dragging = false;
- bool m_Panning = false;
- bool m_Moving = false;
- double m_LastX_Orbital = 0.0;
- double m_LastY_Orbital = 0.0;
+ glm::vec3 m_orbital_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ double m_orbital_radius = 6.34194e10;
+ double m_orbital_min_radius = 1e10;
+ double m_orbital_max_radius = 1e12;
+ float m_azimuth = 0.0f;
+ float m_elevation = static_cast<float>(std::numbers::pi) / 2.0f;
+ float m_orbital_speed = 0.01f;
+ double m_zoom_speed = 25e9f;
+ bool m_dragging = false;
+ bool m_panning = false;
+ bool m_moving = false;
+ double m_last_x_orbital = 0.0;
+ double m_last_y_orbital = 0.0;
};
}
diff --git a/src/Core/Event.h b/src/Core/Event.h
index 23d92fd..1891206 100644
--- a/src/Core/Event.h
+++ b/src/Core/Event.h
@@ -46,63 +46,63 @@ namespace Donut
public:
virtual ~Event() = default;
- bool Handled = false;
+ virtual auto get_event_type() const -> EventType = 0;
+ virtual auto get_name() const -> const char* = 0;
+ virtual auto get_category_flags() const -> int = 0;
+ virtual auto to_string() const -> std::string { return get_name(); }
- virtual EventType GetEventType() const = 0;
- virtual const char* GetName() const = 0;
- virtual int GetCategoryFlags() const = 0;
- virtual std::string ToString() const { return GetName(); }
+ auto is_in_category(EventCategory category) -> bool { return get_category_flags() & category; }
- bool IsInCategory(EventCategory category) { return GetCategoryFlags() & category; }
+ bool handled = false;
};
- #define EVENT_CLASS_TYPE(type) \
- static EventType GetStaticType() { return EventType::type; } \
- virtual EventType GetEventType() const override { return GetStaticType(); } \
- virtual const char* GetName() const override { return #type; }
-
+ #define EVENT_CLASS_TYPE(type) \
+ static auto get_static_type() -> EventType { return EventType::type; } \
+ virtual auto get_event_type() const -> EventType override { return get_static_type(); } \
+ virtual auto get_name() const -> const char* override { return #type; }
+
#define APPLICATION_EVENT_CLASS_TYPE(type) \
EVENT_CLASS_TYPE(type) \
- virtual int GetCategoryFlags() const override { return EventCategoryApplication; }
-
+ virtual auto get_category_flags() const -> int override { return EventCategoryApplication; }
+
#define MOUSE_EVENT_CLASS_TYPE(type) \
EVENT_CLASS_TYPE(type) \
- virtual int GetCategoryFlags() const override { return EventCategoryMouse | \
- EventCategoryInput; }
-
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouse | \
+ EventCategoryInput; }
+
#define MOUSE_BUTTON_EVENT_CLASS_TYPE(type) \
EVENT_CLASS_TYPE(type) \
- virtual int GetCategoryFlags() const override { return EventCategoryMouseButton | \
- EventCategoryMouse | \
- EventCategoryInput; }
-
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouseButton | \
+ EventCategoryMouse | \
+ EventCategoryInput; }
+
#define KEYBOARD_EVENT_CLASS_TYPE(type) \
EVENT_CLASS_TYPE(type) \
- virtual int GetCategoryFlags() const override { return EventCategoryKeyboard | \
- EventCategoryInput; }
+ virtual auto get_category_flags() const -> int override { return EventCategoryKeyboard | \
+ EventCategoryInput; }
class WindowResizeEvent
: public Event
{
public:
WindowResizeEvent(unsigned int width, unsigned int height)
- : m_Width(width), m_Height(height) { }
+ : m_width(width), m_height(height) { }
- unsigned int GetWidth() const { return m_Width; }
- unsigned int GetHeight() const { return m_Height; }
+ auto get_width() const -> unsigned int { return m_width; }
+ auto get_height() const -> unsigned int { return m_height; }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "WindowResizeEvent: " + std::to_string(m_Width) + ", " + std::to_string(m_Height);
+ return "WindowResizeEvent: " + std::to_string(m_width) + ", " + std::to_string(m_height);
}
APPLICATION_EVENT_CLASS_TYPE(WindowResize)
private:
- unsigned int m_Width, m_Height;
+ unsigned int m_width, m_height;
};
- class WindowCloseEvent
+ class WindowCloseEvent
: public Event
{
public:
@@ -110,7 +110,7 @@ namespace Donut
APPLICATION_EVENT_CLASS_TYPE(WindowClose)
};
- class WindowFocusEvent
+ class WindowFocusEvent
: public Event
{
public:
@@ -118,7 +118,7 @@ namespace Donut
APPLICATION_EVENT_CLASS_TYPE(WindowFocus)
};
- class WindowLostFocusEvent
+ class WindowLostFocusEvent
: public Event
{
public:
@@ -126,175 +126,175 @@ namespace Donut
APPLICATION_EVENT_CLASS_TYPE(WindowLostFocus)
};
- class WindowMovedEvent
+ class WindowMovedEvent
: public Event
{
public:
WindowMovedEvent(int x, int y)
- : m_X(x), m_Y(y) { }
+ : m_x(x), m_y(y) { }
- int GetX() const { return m_X; }
- int GetY() const { return m_Y; }
+ auto get_x() const -> int { return m_x; }
+ auto get_y() const -> int { return m_y; }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "WindowMovedEvent: " + std::to_string(m_X) + ", " + std::to_string(m_Y);
+ return "WindowMovedEvent: " + std::to_string(m_x) + ", " + std::to_string(m_y);
}
APPLICATION_EVENT_CLASS_TYPE(WindowMoved)
private:
- int m_X, m_Y;
+ int m_x, m_y;
};
- class KeyEvent
+ class KeyEvent
: public Event
{
public:
- int GetKeyCode() const { return m_KeyCode; }
- virtual int GetCategoryFlags() const override { return EventCategoryKeyboard |
- EventCategoryInput; }
+ auto get_key_code() const -> int { return m_key_code; }
+ virtual auto get_category_flags() const -> int override { return EventCategoryKeyboard |
+ EventCategoryInput; }
protected:
KeyEvent(int keycode)
- : m_KeyCode(keycode) { }
- int m_KeyCode;
+ : m_key_code(keycode) { }
+ int m_key_code;
};
- class KeyPressedEvent
+ class KeyPressedEvent
: public KeyEvent
{
public:
- KeyPressedEvent(int keycode, bool isRepeat = false)
- : KeyEvent(keycode), m_IsRepeat(isRepeat) { }
+ KeyPressedEvent(int keycode, bool is_repeat = false)
+ : KeyEvent(keycode), m_is_repeat(is_repeat) { }
- bool IsRepeat() const { return m_IsRepeat; }
+ auto is_repeat() const -> bool { return m_is_repeat; }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "KeyPressedEvent: " + std::to_string(m_KeyCode) +
- " (repeat = " + std::to_string(m_IsRepeat) + ")";
+ return "KeyPressedEvent: " + std::to_string(m_key_code) +
+ " (repeat = " + std::to_string(m_is_repeat) + ")";
}
KEYBOARD_EVENT_CLASS_TYPE(KeyPressed)
private:
- bool m_IsRepeat;
+ bool m_is_repeat;
};
- class KeyReleasedEvent
+ class KeyReleasedEvent
: public KeyEvent
{
public:
KeyReleasedEvent(int keycode)
: KeyEvent(keycode) { }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "KeyReleasedEvent: " + std::to_string(m_KeyCode);
+ return "KeyReleasedEvent: " + std::to_string(m_key_code);
}
KEYBOARD_EVENT_CLASS_TYPE(KeyReleased)
};
- class KeyTypedEvent
+ class KeyTypedEvent
: public KeyEvent
{
public:
KeyTypedEvent(int keycode)
: KeyEvent(keycode) { }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "KeyTypedEvent: " + std::to_string(m_KeyCode);
+ return "KeyTypedEvent: " + std::to_string(m_key_code);
}
KEYBOARD_EVENT_CLASS_TYPE(KeyTyped)
};
- class MouseMovedEvent
+ class MouseMovedEvent
: public Event
{
public:
MouseMovedEvent(float x, float y)
- : m_MouseX(x), m_MouseY(y) { }
+ : m_mouse_x(x), m_mouse_y(y) { }
- float GetX() const { return m_MouseX; }
- float GetY() const { return m_MouseY; }
+ auto get_x() const -> float { return m_mouse_x; }
+ auto get_y() const -> float { return m_mouse_y; }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "MouseMovedEvent: " + std::to_string(m_MouseX) + ", " + std::to_string(m_MouseY);
+ return "MouseMovedEvent: " + std::to_string(m_mouse_x) + ", " + std::to_string(m_mouse_y);
}
MOUSE_EVENT_CLASS_TYPE(MouseMoved)
private:
- float m_MouseX, m_MouseY;
+ float m_mouse_x, m_mouse_y;
};
- class MouseScrolledEvent
+ class MouseScrolledEvent
: public Event
{
public:
- MouseScrolledEvent(float xOffset, float yOffset)
- : m_XOffset(xOffset), m_YOffset(yOffset) { }
+ MouseScrolledEvent(float x_offset, float y_offset)
+ : m_x_offset(x_offset), m_y_offset(y_offset) { }
- float GetXOffset() const { return m_XOffset; }
- float GetYOffset() const { return m_YOffset; }
+ auto get_x_offset() const -> float { return m_x_offset; }
+ auto get_y_offset() const -> float { return m_y_offset; }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "MouseScrolledEvent: " + std::to_string(m_XOffset) +
- ", " + std::to_string(m_YOffset);
+ return "MouseScrolledEvent: " + std::to_string(m_x_offset) +
+ ", " + std::to_string(m_y_offset);
}
MOUSE_EVENT_CLASS_TYPE(MouseScrolled)
private:
- float m_XOffset, m_YOffset;
+ float m_x_offset, m_y_offset;
};
- class MouseButtonEvent
+ class MouseButtonEvent
: public Event
{
public:
- int GetMouseButton() const { return m_Button; }
- virtual int GetCategoryFlags() const override { return EventCategoryMouseButton |
- EventCategoryMouse |
- EventCategoryInput; }
+ auto get_mouse_button() const -> int { return m_button; }
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouseButton |
+ EventCategoryMouse |
+ EventCategoryInput; }
protected:
MouseButtonEvent(int button)
- : m_Button(button) { }
- int m_Button;
+ : m_button(button) { }
+ int m_button;
};
- class MouseButtonPressedEvent
+ class MouseButtonPressedEvent
: public MouseButtonEvent
{
public:
MouseButtonPressedEvent(int button)
: MouseButtonEvent(button) { }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "MouseButtonPressedEvent: " + std::to_string(m_Button);
+ return "MouseButtonPressedEvent: " + std::to_string(m_button);
}
MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonPressed)
};
- class MouseButtonReleasedEvent
+ class MouseButtonReleasedEvent
: public MouseButtonEvent
{
public:
MouseButtonReleasedEvent(int button)
: MouseButtonEvent(button) { }
- std::string ToString() const override
+ auto to_string() const -> std::string override
{
- return "MouseButtonReleasedEvent: " + std::to_string(m_Button);
+ return "MouseButtonReleasedEvent: " + std::to_string(m_button);
}
MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonReleased)
@@ -303,22 +303,22 @@ namespace Donut
class EventDispatcher
{
public:
- EventDispatcher(Event& event)
- : m_Event(event) { }
+ EventDispatcher(Event& event)
+ : m_event(event) { }
template<typename T, typename F>
- bool Dispatch(const F& func)
+ auto dispatch(const F& func) -> bool
{
- if (m_Event.GetEventType() == T::GetStaticType())
+ if (m_event.get_event_type() == T::get_static_type())
{
- m_Event.Handled = func(static_cast<T&>(m_Event));
+ m_event.handled = func(static_cast<T&>(m_event));
return true;
}
return false;
}
private:
- Event& m_Event;
+ Event& m_event;
};
class EventHandler
@@ -329,29 +329,29 @@ namespace Donut
EventHandler() = default;
~EventHandler() = default;
- void SetEventCallback(const EventCallbackFn& callback)
+ auto set_event_callback(const EventCallbackFn& callback) -> void
{
- m_EventCallback = callback;
+ m_event_callback = callback;
}
- void OnEvent(Event& event)
+ auto on_event(Event& event) -> void
{
- if (m_EventCallback)
- m_EventCallback(event);
+ if (m_event_callback)
+ m_event_callback(event);
}
template<typename T>
- void BindEvent(const std::function<void(T&)>& callback)
+ auto bind_event(const std::function<void(T&)>& callback) -> void
{
- m_EventCallback = [callback](Event& event)
+ m_event_callback = [callback](Event& event)
{
- if (event.GetEventType() == T::GetStaticType())
+ if (event.get_event_type() == T::get_static_type())
callback(static_cast<T&>(event));
};
}
private:
- EventCallbackFn m_EventCallback;
+ EventCallbackFn m_event_callback;
};
#undef EVENT_CLASS_TYPE
@@ -359,4 +359,4 @@ namespace Donut
#undef MOUSE_EVENT_CLASS_TYPE
#undef MOUSE_BUTTON_EVENT_CLASS_TYPE
#undef KEYBOARD_EVENT_CLASS_TYPE
-} \ No newline at end of file
+}
diff --git a/src/Core/HDRIManager.h b/src/Core/HDRIManager.h
deleted file mode 100644
index 3c1ff57..0000000
--- a/src/Core/HDRIManager.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "Core/Log.h"
-#include "Rendering/Texture.h"
-
-#include <string>
-#include <unordered_map>
-#include <memory>
-
-namespace Donut
-{
- class HDRIManager
- {
- public:
- static HDRIManager& Get()
- {
- static HDRIManager instance;
- return instance;
- }
-
- Ref<CubemapTexture> LoadHDRI(const std::string& path);
- Ref<CubemapTexture> GetCurrentHDRI() const { return m_CurrentHDRI; }
-
- void SetCurrentHDRI(const std::string& path);
- const std::vector<std::string>& GetAvailableHDRI() const { return m_AvailableHDRI; }
- std::string GetHDRIName(const std::string& path) const;
- void ClearCache();
- private:
- HDRIManager() = default;
- ~HDRIManager() = default;
-
- HDRIManager(const HDRIManager&) = delete;
- HDRIManager& operator=(const HDRIManager&) = delete;
-
- std::unordered_map<std::string, Ref<CubemapTexture>> m_HDRICache;
-
- Ref<CubemapTexture> m_CurrentHDRI;
-
- std::vector<std::string> m_AvailableHDRI =
- {
- "Assets/HDRI/HDR_blue_nebulae-1.hdr",
- "Assets/HDRI/HDR_subdued_blue_nebulae.hdr",
- "Assets/HDRI/HDR_subdued_multi_nebulae.hdr",
- "Assets/HDRI/night_sky.hdr"
- };
- };
-};
diff --git a/src/Core/Log.cpp b/src/Core/Log.cpp
index f2f3e44..5a0434f 100644
--- a/src/Core/Log.cpp
+++ b/src/Core/Log.cpp
@@ -1,4 +1,4 @@
-#include "Log.h"
+#include "log.h"
#include <filesystem>
@@ -6,148 +6,148 @@
#include <windows.h>
#endif
-namespace Donut
+namespace Donut
{
- Ref<Logger> Logger::s_Logger;
+ Ref<Logger> Logger::s_logger;
- void Logger::Init()
+ auto Logger::init() -> void
{
- s_Logger = CreateRef<Logger>();
- s_Logger->SetLogLevel(LogLevel::INFO);
- s_Logger->EnableConsoleOutput(true);
- s_Logger->EnableFileOutput(true);
- s_Logger->SetLogFile("logs/donut.log");
-
- s_Logger->LogMessage(LogLevel::INFO, "Logging system initialized");
+ s_logger = create_ref<Logger>();
+ s_logger->set_log_level(LogLevel::Info);
+ s_logger->enable_console_output(true);
+ s_logger->enable_file_output(true);
+ s_logger->set_log_file("logs/donut.log");
+
+ s_logger->log_message(LogLevel::Info, "Logging system initialized");
}
- void Logger::Shutdown()
+ auto Logger::shutdown() -> void
{
- if (s_Logger)
- s_Logger->LogMessage(LogLevel::INFO, "Shutting down logging system");
- s_Logger.reset();
+ if (s_logger)
+ s_logger->log_message(LogLevel::Info, "Shutting down logging system");
+ s_logger.reset();
}
- Ref<Logger> Logger::GetLogger()
+ auto Logger::get_logger() -> Ref<Logger>
{
- return s_Logger;
+ return s_logger;
}
Logger::Logger()
- : m_LogLevel(LogLevel::INFO),
- m_ConsoleOutput(true),
- m_FileOutput(false) { }
+ : m_log_level(LogLevel::Info),
+ m_console_output(true),
+ m_file_output(false) { }
- Logger::~Logger()
+ Logger::~Logger()
{
- if (m_LogFile.is_open())
- m_LogFile.close();
+ if (m_log_file.is_open())
+ m_log_file.close();
}
- void Logger::SetLogFile(const std::string& filename)
+ auto Logger::set_log_file(const std::string& filename) -> void
{
- std::filesystem::path logPath(filename);
- std::filesystem::create_directories(logPath.parent_path());
-
- m_LogFile.open(filename, std::ios::app);
- if (!m_LogFile.is_open())
+ std::filesystem::path log_path(filename);
+ std::filesystem::create_directories(log_path.parent_path());
+
+ m_log_file.open(filename, std::ios::app);
+ if (!m_log_file.is_open())
std::cerr << "Failed to open log file: " << filename << std::endl;
}
- std::string Logger::GetTimeStamp()
+ auto Logger::get_time_stamp() -> std::string
{
auto now = std::chrono::system_clock::now();
- auto time_t = std::chrono::system_clock::to_time_t(now);
-
+ auto time = std::chrono::system_clock::to_time_t(now);
+
std::stringstream ss;
- ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
-
+ ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S");
+
return ss.str();
}
- std::string Logger::GetLogLevelString(LogLevel level)
+ auto Logger::get_log_level_string(LogLevel level) -> std::string
{
- switch (level)
+ switch (level)
{
- case LogLevel::TRACE: return "TRACE";
- case LogLevel::INFO: return "INFO";
- case LogLevel::WARN: return "WARN";
- case LogLevel::ERR: return "ERROR";
- case LogLevel::FATAL: return "FATAL";
+ case LogLevel::trace: return "TRACE";
+ case LogLevel::Info: return "INFO";
+ case LogLevel::Warn: return "WARN";
+ case LogLevel::Err: return "ERROR";
+ case LogLevel::Fatal: return "FATAL";
default: return "UNKNOWN";
}
}
- void Logger::SetConsoleColor(LogLevel level)
+ auto Logger::set_console_color(LogLevel level) -> void
{
#if defined(DONUT_WINDOWS)
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE);
WORD color;
-
+
switch (level) {
- case LogLevel::TRACE:
+ case LogLevel::trace:
color = FOREGROUND_RED |
- FOREGROUND_GREEN |
+ FOREGROUND_GREEN |
FOREGROUND_BLUE;
break;
- case LogLevel::INFO:
- color = FOREGROUND_GREEN |
+ case LogLevel::Info:
+ color = FOREGROUND_GREEN |
FOREGROUND_INTENSITY;
break;
- case LogLevel::WARN:
- color = FOREGROUND_RED |
- FOREGROUND_GREEN |
+ case LogLevel::Warn:
+ color = FOREGROUND_RED |
+ FOREGROUND_GREEN |
FOREGROUND_INTENSITY;
break;
- case LogLevel::ERR:
- color = FOREGROUND_RED |
+ case LogLevel::Err:
+ color = FOREGROUND_RED |
FOREGROUND_INTENSITY;
break;
- case LogLevel::FATAL:
- color = FOREGROUND_RED |
- FOREGROUND_BLUE |
+ case LogLevel::Fatal:
+ color = FOREGROUND_RED |
+ FOREGROUND_BLUE |
FOREGROUND_INTENSITY;
break;
default:
- color = FOREGROUND_RED |
- FOREGROUND_GREEN |
+ color = FOREGROUND_RED |
+ FOREGROUND_GREEN |
FOREGROUND_BLUE;
break;
}
-
- SetConsoleTextAttribute(hConsole, color);
+
+ SetConsoleTextAttribute(h_console, color);
#else
- const char* colorCode;
+ const char* color_code;
switch (level) {
- case LogLevel::TRACE:
- colorCode = "\033[37m";
+ case LogLevel::trace:
+ color_code = "\033[37m";
break;
- case LogLevel::INFO:
- colorCode = "\033[32;1m";
+ case LogLevel::Info:
+ color_code = "\033[32;1m";
break;
- case LogLevel::WARN:
- colorCode = "\033[33;1m";
+ case LogLevel::Warn:
+ color_code = "\033[33;1m";
break;
- case LogLevel::ERR:
- colorCode = "\033[31;1m";
+ case LogLevel::Err:
+ color_code = "\033[31;1m";
break;
- case LogLevel::FATAL:
- colorCode = "\033[35;1m";
+ case LogLevel::Fatal:
+ color_code = "\033[35;1m";
break;
default:
- colorCode = "\033[37m";
+ color_code = "\033[37m";
break;
}
- std::cout << colorCode;
+ std::cout << color_code;
#endif
}
- void Logger::ResetConsoleColor()
+ auto Logger::reset_console_color() -> void
{
#if defined(DONUT_WINDOWS)
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- SetConsoleTextAttribute(hConsole, FOREGROUND_RED |
- FOREGROUND_GREEN |
+ HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleTextAttribute(h_console, FOREGROUND_RED |
+ FOREGROUND_GREEN |
FOREGROUND_BLUE);
#else
std::cout << "\033[0m";
diff --git a/src/Core/Log.h b/src/Core/Log.h
index c7b2e47..db6b4b4 100644
--- a/src/Core/Log.h
+++ b/src/Core/Log.h
@@ -1,6 +1,6 @@
#pragma once
-#include "Core/Memory.h"
+#include "core/memory.h"
#include <string>
#include <vector>
@@ -16,140 +16,141 @@ namespace Donut
{
enum class LogLevel
{
- TRACE = 0,
- INFO = 2,
- WARN = 3,
- ERR = 4,
- FATAL = 5
+ trace = 0,
+ Info = 2,
+ Warn = 3,
+ Err = 4,
+ Fatal = 5
};
- class Logger
+ class Logger
{
public:
- static void Init();
- static void Shutdown();
+ Logger();
+ ~Logger();
+
+ static auto init() -> void;
+ static auto shutdown() -> void;
- static Ref<Logger> GetLogger();
+ static auto get_logger() -> Ref<Logger>;
template<typename... Args>
- static void Trace(const std::string_view& format, const Args&... args)
+ static auto trace(const std::string_view& format, const Args&... args) -> void
{
- auto logger = GetLogger();
- if (logger) logger->LogMessage(LogLevel::TRACE, format, args...);
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::trace, format, args...);
}
template<typename... Args>
- static void Info(const std::string_view& format, const Args&... args)
+ static auto info(const std::string_view& format, const Args&... args) -> void
{
- auto logger = GetLogger();
- if (logger) logger->LogMessage(LogLevel::INFO, format, args...);
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Info, format, args...);
}
template<typename... Args>
- static void Warn(const std::string_view& format, const Args&... args)
+ static auto warn(const std::string_view& format, const Args&... args) -> void
{
- auto logger = GetLogger();
- if (logger) logger->LogMessage(LogLevel::WARN, format, args...);
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Warn, format, args...);
}
template<typename... Args>
- static void Error(const std::string_view& format, const Args&... args)
+ static auto error(const std::string_view& format, const Args&... args) -> void
{
- auto logger = GetLogger();
- if (logger) logger->LogMessage(LogLevel::ERR, format, args...);
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Err, format, args...);
}
template<typename... Args>
- static void Fatal(const std::string_view& format, const Args&... args)
+ static auto fatal(const std::string_view& format, const Args&... args) -> void
{
- auto logger = GetLogger();
- if (logger) logger->LogMessage(LogLevel::FATAL, format, args...);
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Fatal, format, args...);
}
template<typename... Args>
- void LogMessage(LogLevel level, const std::string_view& format, const Args&... args)
+ auto log_message(LogLevel level, const std::string_view& format, const Args&... args) -> void
{
- if (level < m_LogLevel)
+ if (level < m_log_level)
return;
- std::lock_guard<std::mutex> lock(m_Mutex);
- std::string message = FormatString(format, args...);
- std::string fullMessage = GetTimeStamp() + " [" + GetLogLevelString(level) + "] " + message;
-
- if (m_ConsoleOutput)
+ std::lock_guard<std::mutex> lock(m_mutex);
+ std::string message = format_string(format, args...);
+ std::string full_message = get_time_stamp() + " [" + get_log_level_string(level) + "] " + message;
+
+ if (m_console_output)
{
- SetConsoleColor(level);
- std::cout << fullMessage << std::endl;
- ResetConsoleColor();
+ set_console_color(level);
+ std::cout << full_message << std::endl;
+ reset_console_color();
}
-
- if (m_FileOutput && m_LogFile.is_open())
+
+ if (m_file_output && m_log_file.is_open())
{
- m_LogFile << fullMessage << std::endl;
- m_LogFile.flush();
+ m_log_file << full_message << std::endl;
+ m_log_file.flush();
}
}
template<typename... Args>
- std::string FormatString(const std::string_view& format, const Args&... args)
+ auto format_string(const std::string_view& format, const Args&... args) -> std::string
{
std::string result = format.data();
- std::vector<std::string> argStrings = { ToString(args)... };
-
- size_t argIndex = 0;
+ std::vector<std::string> arg_strings = { to_string(args)... };
+
+ size_t arg_index = 0;
size_t pos = 0;
-
- while ((pos = result.find("{}", pos)) != std::string::npos && argIndex < argStrings.size())
+
+ while ((pos = result.find("{}", pos)) != std::string::npos && arg_index < arg_strings.size())
{
- result.replace(pos, 2, argStrings[argIndex]);
- pos += argStrings[argIndex].length();
- argIndex++;
+ result.replace(pos, 2, arg_strings[arg_index]);
+ pos += arg_strings[arg_index].length();
+ arg_index++;
}
-
+
return result;
}
template<typename T>
- std::string ToString(const T& value)
+ auto to_string(const T& value) -> std::string
{
std::stringstream ss;
ss << value;
return ss.str();
}
- void SetLogLevel(LogLevel level) { m_LogLevel = level; }
- void EnableConsoleOutput(bool enable) { m_ConsoleOutput = enable; }
- void EnableFileOutput(bool enable) { m_FileOutput = enable; }
- void SetLogFile(const std::string& filename);
- public:
- Logger();
- ~Logger();
+ auto set_log_level(LogLevel level) -> void { m_log_level = level; }
+ auto enable_console_output(bool enable) -> void { m_console_output = enable; }
+ auto enable_file_output(bool enable) -> void { m_file_output = enable; }
+ auto set_log_file(const std::string& filename) -> void;
+
+ auto get_time_stamp() -> std::string;
+ auto get_log_level_string(LogLevel level) -> std::string;
+ auto set_console_color(LogLevel level) -> void;
+ auto reset_console_color() -> void;
- std::string GetTimeStamp();
- std::string GetLogLevelString(LogLevel level);
- void SetConsoleColor(LogLevel level);
- void ResetConsoleColor();
private:
- LogLevel m_LogLevel;
- bool m_ConsoleOutput;
- bool m_FileOutput;
- std::ofstream m_LogFile;
- std::mutex m_Mutex;
+ LogLevel m_log_level;
+ bool m_console_output;
+ bool m_file_output;
+ std::ofstream m_log_file;
+ std::mutex m_mutex;
- static Ref<Logger> s_Logger;
+ static Ref<Logger> s_logger;
};
}
#if defined(DONUT_DEBUG)
- #define DONUT_TRACE(format, ...) ::Donut::Logger::Trace(format, ##__VA_ARGS__)
- #define DONUT_INFO(format, ...) ::Donut::Logger::Info(format, ##__VA_ARGS__)
- #define DONUT_WARN(format, ...) ::Donut::Logger::Warn(format, ##__VA_ARGS__)
- #define DONUT_ERROR(format, ...) ::Donut::Logger::Error(format, ##__VA_ARGS__)
- #define DONUT_FATAL(format, ...) ::Donut::Logger::Fatal(format, ##__VA_ARGS__)
+ #define DONUT_TRACE(format, ...) ::Donut::Logger::trace(format, ##__VA_ARGS__)
+ #define DONUT_INFO(format, ...) ::Donut::Logger::info(format, ##__VA_ARGS__)
+ #define DONUT_WARN(format, ...) ::Donut::Logger::warn(format, ##__VA_ARGS__)
+ #define DONUT_ERROR(format, ...) ::Donut::Logger::error(format, ##__VA_ARGS__)
+ #define DONUT_FATAL(format, ...) ::Donut::Logger::fatal(format, ##__VA_ARGS__)
#else
#define DONUT_TRACE(format, ...) {}
#define DONUT_INFO(format, ...) {}
#define DONUT_WARN(format, ...) {}
#define DONUT_ERROR(format, ...) {}
#define DONUT_FATAL(format, ...) {}
-#endif \ No newline at end of file
+#endif
diff --git a/src/Core/Memory.h b/src/Core/Memory.h
index 0abc6b1..3240c71 100644
--- a/src/Core/Memory.h
+++ b/src/Core/Memory.h
@@ -2,23 +2,23 @@
#include <memory>
-namespace Donut
+namespace Donut
{
template<typename T>
using Ref = std::shared_ptr<T>;
- template<typename T>
+ template<typename T>
using Scope = std::unique_ptr<T>;
-
- template<typename T, typename ... Args>
- constexpr Scope<T> CreateScope(Args&& ... args)
+
+ template<typename T, typename ... Args>
+ constexpr auto create_scope(Args&& ... args) -> Scope<T>
{
return std::make_unique<T>(std::forward<Args>(args)...);
}
-
+
template<typename T, typename ... Args>
- constexpr Ref<T> CreateRef(Args&& ... args)
+ constexpr auto create_ref(Args&& ... args) -> Ref<T>
{
return std::make_shared<T>(std::forward<Args>(args)...);
}
-}; \ No newline at end of file
+};
diff --git a/src/Core/SettingsManager.cpp b/src/Core/SettingsManager.cpp
deleted file mode 100644
index 9543787..0000000
--- a/src/Core/SettingsManager.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-#include "SettingsManager.h"
-#include "Log.h"
-#include "ThemeManager.h"
-#include "Rendering/Renderer.h"
-
-#include <fstream>
-#include <filesystem>
-#include <algorithm>
-
-namespace Donut
-{
- Settings SettingsManager::s_Settings;
- bool SettingsManager::s_Initialized = false;
-
- void SettingsManager::Initialize()
- {
- if (s_Initialized)
- return;
-
- LoadSettings();
- s_Initialized = true;
- DONUT_INFO("Settings Manager initialized");
- }
-
- void SettingsManager::Shutdown()
- {
- if (!s_Initialized)
- return;
-
- SaveSettings();
- s_Initialized = false;
- DONUT_INFO("Settings Manager shutdown");
- }
-
- void SettingsManager::LoadSettings()
- {
- std::string filePath = GetSettingsFilePath();
-
- try
- {
- if (std::filesystem::exists(filePath))
- {
- auto config = toml::parse(filePath);
-
- if (config.contains("simulation"))
- {
- auto sim = config["simulation"];
- s_Settings.simulation.targetFPS = toml::find_or(sim, "target_fps", 60);
- s_Settings.simulation.computeHeight = toml::find_or(sim, "compute_height", 512);
- s_Settings.simulation.maxStepsMoving = toml::find_or(sim, "max_steps_moving", 30000);
- s_Settings.simulation.maxStepsStatic = toml::find_or(sim, "max_steps_static", 15000);
- s_Settings.simulation.earlyExitDistance = toml::find_or(sim, "early_exit_distance", 5e12f);
- s_Settings.simulation.gravityEnabled = toml::find_or(sim, "gravity_enabled", true);
- s_Settings.simulation.diskThickness = toml::find_or(sim, "disk_thickness", 0.1f);
- s_Settings.simulation.diskDensity = toml::find_or(sim, "disk_density", 0.1f);
- s_Settings.simulation.rotationSpeed = toml::find_or(sim, "rotation_speed", 1.0f);
- s_Settings.simulation.blurStrength = toml::find_or(sim, "blur_strength", 2.0f);
- s_Settings.simulation.glowIntensity = toml::find_or(sim, "glow_intensity", 0.1f);
-
- s_Settings.simulation.targetFPS = std::max(30, std::min(120, s_Settings.simulation.targetFPS));
- s_Settings.simulation.computeHeight = std::max(64, std::min(2048, s_Settings.simulation.computeHeight));
- s_Settings.simulation.maxStepsMoving = std::max(1000, std::min(60000, s_Settings.simulation.maxStepsMoving));
- s_Settings.simulation.maxStepsStatic = std::max(1000, std::min(30000, s_Settings.simulation.maxStepsStatic));
- s_Settings.simulation.earlyExitDistance = std::max(1e11f, std::min(1e13f, s_Settings.simulation.earlyExitDistance));
- s_Settings.simulation.diskThickness = std::max(0.01f, std::min(5.0f, s_Settings.simulation.diskThickness));
- s_Settings.simulation.diskDensity = std::max(0.01f, std::min(5.0f, s_Settings.simulation.diskDensity));
- s_Settings.simulation.rotationSpeed = std::max(0.0f, std::min(5.0f, s_Settings.simulation.rotationSpeed));
- s_Settings.simulation.blurStrength = std::max(0.1f, std::min(10.0f, s_Settings.simulation.blurStrength));
- s_Settings.simulation.glowIntensity = std::max(0.01f, std::min(5.0f, s_Settings.simulation.glowIntensity));
- }
-
- if (config.contains("graphics"))
- {
- auto gfx = config["graphics"];
- s_Settings.graphics.renderAPI = toml::find_or(gfx, "render_api", std::string("OpenGL"));
- s_Settings.graphics.vSyncEnabled = toml::find_or(gfx, "vsync_enabled", true);
- s_Settings.graphics.showFPS = toml::find_or(gfx, "show_fps", true);
- s_Settings.graphics.showPerformanceMetrics = toml::find_or(gfx, "show_performance_metrics", true);
- s_Settings.graphics.showDebugInfo = toml::find_or(gfx, "show_debug_info", false);
- s_Settings.graphics.enableAntiAliasing = toml::find_or(gfx, "enable_anti_aliasing", true);
- s_Settings.graphics.selectedTheme = toml::find_or(gfx, "selected_theme", std::string("Dark"));
-
- if (s_Settings.graphics.renderAPI != "OpenGL" &&
- s_Settings.graphics.renderAPI != "Vulkan")
- s_Settings.graphics.renderAPI = "OpenGL";
- if (s_Settings.graphics.selectedTheme != "Dark" &&
- s_Settings.graphics.selectedTheme != "Light" &&
- s_Settings.graphics.selectedTheme != "Blue")
- s_Settings.graphics.selectedTheme = "Dark";
- }
-
- DONUT_INFO("Settings loaded from {}", filePath);
- }
- else
- {
- LoadDefaultSettings();
- SaveSettings();
- DONUT_INFO("No settings file found, created default settings");
- }
- }
- catch (const std::exception& e)
- {
- DONUT_ERROR("Failed to load settings: {}", e.what());
- LoadDefaultSettings();
- }
- }
-
- void SettingsManager::SaveSettings()
- {
- std::string filePath = GetSettingsFilePath();
-
- try
- {
- std::filesystem::path path(filePath);
- std::filesystem::create_directories(path.parent_path());
-
- toml::value simulation = toml::table
- {
- {"target_fps", s_Settings.simulation.targetFPS },
- {"compute_height", s_Settings.simulation.computeHeight },
- {"max_steps_moving", s_Settings.simulation.maxStepsMoving },
- {"max_steps_static", s_Settings.simulation.maxStepsStatic },
- {"early_exit_distance", s_Settings.simulation.earlyExitDistance},
- {"gravity_enabled", s_Settings.simulation.gravityEnabled },
- {"disk_thickness", s_Settings.simulation.diskThickness },
- {"disk_density", s_Settings.simulation.diskDensity },
- {"rotation_speed", s_Settings.simulation.rotationSpeed },
- {"blur_strength", s_Settings.simulation.blurStrength },
- {"glow_intensity", s_Settings.simulation.glowIntensity }
- };
-
- toml::value graphics = toml::table
- {
- {"render_api", s_Settings.graphics.renderAPI },
- {"vsync_enabled", s_Settings.graphics.vSyncEnabled },
- {"show_fps", s_Settings.graphics.showFPS },
- {"show_performance_metrics", s_Settings.graphics.showPerformanceMetrics},
- {"show_debug_info", s_Settings.graphics.showDebugInfo },
- {"enable_anti_aliasing", s_Settings.graphics.enableAntiAliasing },
- {"selected_theme", s_Settings.graphics.selectedTheme }
- };
-
- toml::value config = toml::table
- {
- {"simulation", simulation},
- {"graphics", graphics}
- };
-
- std::ofstream file(filePath);
- file << config;
- file.close();
-
- DONUT_INFO("Settings saved to {}", filePath);
- }
- catch (const std::exception& e)
- {
- DONUT_ERROR("Failed to save settings: {}", e.what());
- }
- }
-
- void SettingsManager::SetSimulationSettings(const SimulationSettings& settings)
- {
- s_Settings.simulation = settings;
- SaveSettings();
- }
-
- void SettingsManager::SetGraphicsSettings(const GraphicsSettings& settings)
- {
- s_Settings.graphics = settings;
- SaveSettings();
- }
-
- std::string SettingsManager::GetSettingsFilePath()
- {
- return "config/settings.toml";
- }
-
- void SettingsManager::LoadDefaultSettings()
- {
- s_Settings.simulation.targetFPS = 60;
- s_Settings.simulation.computeHeight = 512;
- s_Settings.simulation.maxStepsMoving = 30000;
- s_Settings.simulation.maxStepsStatic = 15000;
- s_Settings.simulation.earlyExitDistance = 5e12f;
- s_Settings.simulation.gravityEnabled = true;
- s_Settings.simulation.diskThickness = 0.1f;
- s_Settings.simulation.diskDensity = 0.1f;
- s_Settings.simulation.rotationSpeed = 1.0f;
- s_Settings.simulation.blurStrength = 2.0f;
- s_Settings.simulation.glowIntensity = 0.1f;
-
- s_Settings.graphics.renderAPI = "OpenGL";
- s_Settings.graphics.vSyncEnabled = true;
- s_Settings.graphics.showFPS = true;
- s_Settings.graphics.showPerformanceMetrics = true;
- s_Settings.graphics.showDebugInfo = false;
- s_Settings.graphics.enableAntiAliasing = true;
- s_Settings.graphics.selectedTheme = "Dark";
- }
-}
diff --git a/src/Core/SettingsManager.h b/src/Core/SettingsManager.h
deleted file mode 100644
index de0774b..0000000
--- a/src/Core/SettingsManager.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#pragma once
-
-#include <string>
-#include <toml.hpp>
-
-namespace Donut
-{
- struct SimulationSettings
- {
- int targetFPS = 60;
- int computeHeight = 512;
- int maxStepsMoving = 30000;
- int maxStepsStatic = 15000;
- float earlyExitDistance = 5e12f;
- bool gravityEnabled = true;
- float diskThickness = 0.1f;
- float diskDensity = 0.1f;
- float rotationSpeed = 1.0f;
- float blurStrength = 2.0f;
- float glowIntensity = 0.1f;
- };
-
- struct GraphicsSettings
- {
- std::string renderAPI = "OpenGL";
- bool vSyncEnabled = true;
- bool showFPS = true;
- bool showPerformanceMetrics = true;
- bool showDebugInfo = false;
- bool enableAntiAliasing = true;
- std::string selectedTheme = "Dark";
- };
-
- struct Settings
- {
- SimulationSettings simulation;
- GraphicsSettings graphics;
- };
-
- class SettingsManager
- {
- public:
- static void Initialize();
- static void Shutdown();
-
- static void LoadSettings();
- static void SaveSettings();
-
- static Settings& GetSettings() { return s_Settings; }
- static const Settings& GetSettingsConst() { return s_Settings; }
-
- static void SetSimulationSettings(const SimulationSettings& settings);
- static void SetGraphicsSettings(const GraphicsSettings& settings);
-
- static int GetTargetFPS() { return s_Settings.simulation.targetFPS; }
- static int GetComputeHeight() { return s_Settings.simulation.computeHeight; }
- static int GetMaxStepsMoving() { return s_Settings.simulation.maxStepsMoving; }
- static int GetMaxStepsStatic() { return s_Settings.simulation.maxStepsStatic; }
- static float GetEarlyExitDistance() { return s_Settings.simulation.earlyExitDistance; }
- static bool GetGravityEnabled() { return s_Settings.simulation.gravityEnabled; }
- static float GetDiskThickness() { return s_Settings.simulation.diskThickness; }
- static float GetDiskDensity() { return s_Settings.simulation.diskDensity; }
- static float GetRotationSpeed() { return s_Settings.simulation.rotationSpeed; }
- static float GetBlurStrength() { return s_Settings.simulation.blurStrength; }
- static float GetGlowIntensity() { return s_Settings.simulation.glowIntensity; }
- static std::string GetRenderAPI() { return s_Settings.graphics.renderAPI; }
- static bool GetVSyncEnabled() { return s_Settings.graphics.vSyncEnabled; }
- static bool GetShowFPS() { return s_Settings.graphics.showFPS; }
- static bool GetShowPerformanceMetrics() { return s_Settings.graphics.showPerformanceMetrics; }
- static bool GetShowDebugInfo() { return s_Settings.graphics.showDebugInfo; }
- static bool GetEnableAntiAliasing() { return s_Settings.graphics.enableAntiAliasing; }
- static std::string GetSelectedTheme() { return s_Settings.graphics.selectedTheme; }
- private:
- static std::string GetSettingsFilePath();
- static void LoadDefaultSettings();
- private:
- static Settings s_Settings;
- static bool s_Initialized;
- };
-}
diff --git a/src/Core/State.h b/src/Core/State.h
index cd1b7ee..623f1c2 100644
--- a/src/Core/State.h
+++ b/src/Core/State.h
@@ -1,6 +1,6 @@
#pragma once
-#include "Event.h"
+#include "event.h"
namespace Donut
{
@@ -8,12 +8,12 @@ namespace Donut
{
public:
virtual ~State() = default;
-
- virtual void OnEnter() = 0;
- virtual void OnExit() = 0;
- virtual void OnUpdate(float deltaTime) = 0;
- virtual void OnRender() = 0;
- virtual void OnImUIRender() = 0;
- virtual void OnEvent(Event& event) = 0;
+
+ 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;
};
-}; \ No newline at end of file
+};
diff --git a/src/Core/StateManager.cpp b/src/Core/StateManager.cpp
deleted file mode 100644
index e2aba02..0000000
--- a/src/Core/StateManager.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-#include "StateManager.h"
-#include "Log.h"
-
-namespace Donut
-{
- void StateManager::Shutdown()
- {
- if (m_CurrentState)
- m_CurrentState->OnExit();
-
- DestroyStates();
- DONUT_INFO("StateManager shutdown");
- }
-
- void StateManager::DestroyStates()
- {
- m_States.clear();
- m_CurrentState = nullptr;
- m_CurrentStateName = "";
- }
-
- void StateManager::RegisterState(const std::string& stateName, Scope<State> state)
- {
- if (m_States.find(stateName) != m_States.end())
- DONUT_WARN("State '{}' already exists, overwriting", stateName);
- m_States[stateName] = std::move(state);
- DONUT_INFO("Registered state: {}", stateName);
- }
-
- void StateManager::SwitchToState(const std::string& stateName)
- {
- auto it = m_States.find(stateName);
- if (it == m_States.end())
- {
- DONUT_ERROR("Attempted to switch to unknown state: {}", stateName);
- return;
- }
-
- State* newState = it->second.get();
- if (newState == m_CurrentState)
- return;
-
- if (m_CurrentState)
- m_CurrentState->OnExit();
-
- m_CurrentState = newState;
- m_CurrentStateName = stateName;
- m_CurrentState->OnEnter();
- DONUT_INFO("Switched to state: {}", stateName);
- }
-
- State* StateManager::GetState(const std::string& stateName)
- {
- auto it = m_States.find(stateName);
- if (it != m_States.end())
- return it->second.get();
- return nullptr;
- }
-
- bool StateManager::HasState(const std::string& stateName) const
- {
- return m_States.find(stateName) != m_States.end();
- }
-
- void StateManager::Update(float deltaTime)
- {
- if (m_CurrentState)
- m_CurrentState->OnUpdate(deltaTime);
- }
-
- void StateManager::Render()
- {
- if (m_CurrentState)
- m_CurrentState->OnRender();
- }
-
- void StateManager::OnImUIRender()
- {
- if (m_CurrentState)
- m_CurrentState->OnImUIRender();
- }
-
- void StateManager::OnEvent(Event& event)
- {
- if (m_CurrentState)
- m_CurrentState->OnEvent(event);
- }
-}
diff --git a/src/Core/StateManager.h b/src/Core/StateManager.h
deleted file mode 100644
index 4ffc86d..0000000
--- a/src/Core/StateManager.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#pragma once
-
-#include "Memory.h"
-#include "State.h"
-
-#include <string>
-#include <unordered_map>
-
-namespace Donut
-{
- class StateManager
- {
- public:
- ~StateManager() = default;
- void Shutdown();
-
- void Update(float deltaTime);
- void Render();
- void OnImUIRender();
- void OnEvent(Event& event);
-
- void RegisterState(const std::string& stateName, Scope<State> state);
- void SwitchToState(const std::string& stateName);
-
- std::string GetCurrentStateName() const { return m_CurrentStateName; }
- State* GetCurrentState() const { return m_CurrentState; }
-
- State* GetState(const std::string& stateName);
- bool HasState(const std::string& stateName) const;
- private:
- void CreateStates();
- void DestroyStates();
-
- private:
- State* m_CurrentState = nullptr;
- std::string m_CurrentStateName = "";
-
- std::unordered_map<std::string, Scope<State>> m_States;
- };
-}
diff --git a/src/Core/ThemeManager.h b/src/Core/ThemeManager.h
deleted file mode 100644
index 7ff08ea..0000000
--- a/src/Core/ThemeManager.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <imgui.h>
-
-namespace Donut
-{
- enum class Theme
- {
- Dark = 0,
- Light = 1,
- Blue = 2
- };
-
- class ThemeManager
- {
- public:
- static void SetTheme(Theme theme);
- static Theme GetCurrentTheme() { return s_CurrentTheme; }
-
- static void ApplyDarkTheme();
- static void ApplyLightTheme();
- static void ApplyBlueTheme();
-
- private:
- static Theme s_CurrentTheme;
- };
-}
diff --git a/src/Core/Window.cpp b/src/Core/Window.cpp
index ade0c26..047ef0d 100644
--- a/src/Core/Window.cpp
+++ b/src/Core/Window.cpp
@@ -1,7 +1,7 @@
#include <glad/glad.h>
-#include "Window.h"
-#include "ThemeManager.h"
-#include "Rendering/Renderer.h" // RendererAPI::GetAPI()
+#include "window.h"
+#include "theme_manager.h"
+#include "rendering/renderer.h" // RendererAPI::get_api()
#include <cstdint>
@@ -11,26 +11,26 @@
namespace Donut
{
- static bool s_GLFWInitialized = false;
- static uint32_t s_GLFWWindowCount = 0;
+ static bool s_glfw_initialized = false;
+ static uint32_t s_glfw_window_count = 0;
Window::Window(const std::string& title, int width, int height)
- : m_Title(title), m_Width(width),
- m_Height(height), m_IsClosed(false)
+ : m_title(title), m_width(width),
+ m_height(height), m_is_closed(false)
{
- Init();
+ init();
}
Window::~Window()
{
- Shutdown();
+ shutdown();
}
- void Window::Init()
+ auto Window::init() -> void
{
- DONUT_INFO("Initializing window: ", m_Title, " (", m_Width, "x", m_Height, ")");
-
- if (!s_GLFWInitialized)
+ DONUT_INFO("Initializing window: ", m_title, " (", m_width, "x", m_height, ")");
+
+ if (!s_glfw_initialized)
{
int success = glfwInit();
if (!success)
@@ -39,11 +39,11 @@ namespace Donut
return;
}
- s_GLFWInitialized = true;
+ s_glfw_initialized = true;
DONUT_INFO("GLFW initialized successfully");
}
- if (RendererAPI::GetAPI() == RendererAPI::API::Vulkan)
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
{
// Vulkan manages presentation itself; GLFW must not create a GL context.
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
@@ -62,78 +62,78 @@ namespace Donut
#endif
}
- m_Window = glfwCreateWindow(m_Width, m_Height, m_Title.c_str(), nullptr, nullptr);
- if (!m_Window)
+ m_window = glfwCreateWindow(m_width, m_height, m_title.c_str(), nullptr, nullptr);
+ if (!m_window)
{
DONUT_ERROR("Could not create GLFW window!");
glfwTerminate();
return;
}
-
+
DONUT_INFO("GLFW window created successfully");
- if (RendererAPI::GetAPI() != RendererAPI::API::Vulkan)
- glfwMakeContextCurrent(m_Window);
- glfwSetWindowUserPointer(m_Window, this);
+ if (RendererAPI::get_api() != RendererAPI::API::Vulkan)
+ glfwMakeContextCurrent(m_window);
+ glfwSetWindowUserPointer(m_window, this);
- glfwSetErrorCallback(GLFWErrorCallback);
- glfwSetWindowCloseCallback(m_Window, GLFWWindowCloseCallback);
- glfwSetWindowSizeCallback(m_Window, GLFWWindowSizeCallback);
- glfwSetWindowFocusCallback(m_Window, GLFWWindowFocusCallback);
- glfwSetWindowPosCallback(m_Window, GLFWWindowPosCallback);
- glfwSetKeyCallback(m_Window, GLFWKeyCallback);
- glfwSetCharCallback(m_Window, GLFWCharCallback);
- glfwSetMouseButtonCallback(m_Window, GLFWMouseButtonCallback);
- glfwSetScrollCallback(m_Window, GLFWMouseScrollCallback);
- glfwSetCursorPosCallback(m_Window, GLFWCursorPosCallback);
+ glfwSetErrorCallback(glfw_error_callback);
+ glfwSetWindowCloseCallback(m_window, glfw_window_close_callback);
+ glfwSetWindowSizeCallback(m_window, glfw_window_size_callback);
+ glfwSetWindowFocusCallback(m_window, glfw_window_focus_callback);
+ glfwSetWindowPosCallback(m_window, glfw_window_pos_callback);
+ glfwSetKeyCallback(m_window, glfw_key_callback);
+ glfwSetCharCallback(m_window, glfw_char_callback);
+ glfwSetMouseButtonCallback(m_window, glfw_mouse_button_callback);
+ glfwSetScrollCallback(m_window, glfw_mouse_scroll_callback);
+ glfwSetCursorPosCallback(m_window, glfw_cursor_pos_callback);
- s_GLFWWindowCount++;
+ s_glfw_window_count++;
}
- void Window::Shutdown()
+ auto Window::shutdown() -> void
{
- DONUT_INFO("Shutting down window: ", m_Title);
-
- ShutdownImGui();
-
- glfwDestroyWindow(m_Window);
- s_GLFWWindowCount--;
+ DONUT_INFO("Shutting down window: ", m_title);
+
+ shutdown_im_gui();
+
+ glfwDestroyWindow(m_window);
+ s_glfw_window_count--;
- if (s_GLFWWindowCount == 0)
+ if (s_glfw_window_count == 0)
{
glfwTerminate();
- s_GLFWInitialized = false;
+ s_glfw_initialized = false;
DONUT_INFO("GLFW terminated (no more windows)");
}
}
- void Window::OnUpdate() const
+ auto Window::on_update() const -> void
{
glfwPollEvents();
- if (RendererAPI::GetAPI() != RendererAPI::API::Vulkan)
- glfwSwapBuffers(m_Window); // Vulkan presents via the swapchain instead
+ if (RendererAPI::get_api() != RendererAPI::API::Vulkan)
+ glfwSwapBuffers(m_window); // Vulkan presents via the swapchain instead
}
- bool Window::ShouldClose() const
+ auto Window::should_close() const -> bool
{
- return glfwWindowShouldClose(m_Window) || m_IsClosed;
+ return glfwWindowShouldClose(m_window) || m_is_closed;
}
- void Window::SetCursorLocked(bool locked)
+ auto Window::set_cursor_locked(bool locked) -> void
{
- m_CursorLocked = locked;
- glfwSetInputMode(m_Window, GLFW_CURSOR, locked ?
+ m_cursor_locked = locked;
+ glfwSetInputMode(m_window, GLFW_CURSOR, locked ?
GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
}
- void Window::SetCursorVisible(bool visible)
+ auto Window::set_cursor_visible(bool visible) -> void
{
- m_CursorVisible = visible;
- glfwSetInputMode(m_Window, GLFW_CURSOR, visible ?
+ m_cursor_visible = visible;
+ glfwSetInputMode(m_window, GLFW_CURSOR, visible ?
GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
}
- void Window::InitImGui()
+ auto Window::init_im_gui() -> void
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@@ -148,8 +148,8 @@ namespace Donut
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
#endif
- SetupImGuiFonts();
- ThemeManager::SetTheme(Theme::Dark);
+ setup_im_gui_fonts();
+ ThemeManager::set_theme(Theme::Dark);
ImGuiStyle& style = ImGui::GetStyle();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -158,7 +158,7 @@ namespace Donut
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
- ImGui_ImplGlfw_InitForOpenGL(m_Window, true);
+ ImGui_ImplGlfw_InitForOpenGL(m_window, true);
#ifdef __APPLE__
// macOS uses a core-profile context, which rejects the legacy
// "#version 130" GLSL the ImGui backend defaults to. 150 is the
@@ -167,53 +167,53 @@ namespace Donut
#else
ImGui_ImplOpenGL3_Init("#version 130");
#endif
-
+
DONUT_INFO("ImGUI initialized successfully");
}
- void Window::SetupImGuiFonts()
+ auto Window::setup_im_gui_fonts() -> void
{
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
-
- m_MainFont = io.Fonts->AddFontFromFileTTF("Assets/Fonts/Inter/static/Inter_18pt-Regular.ttf", 16.0f);
- if (!m_MainFont)
+
+ m_main_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Regular.ttf", 16.0f);
+ if (!m_main_font)
{
DONUT_WARN("Failed to load Inter font, falling back to default");
- m_MainFont = io.Fonts->AddFontDefault();
+ m_main_font = io.Fonts->AddFontDefault();
}
else
DONUT_INFO("Successfully loaded Inter font");
-
- m_LargeFont = io.Fonts->AddFontFromFileTTF("Assets/Fonts/Inter/static/Inter_18pt-Bold.ttf", 20.0f);
- if (!m_LargeFont)
- m_LargeFont = m_MainFont;
-
- m_SmallFont = io.Fonts->AddFontFromFileTTF("Assets/Fonts/Inter/static/Inter_18pt-Light.ttf", 12.0f);
- if (!m_SmallFont)
- m_SmallFont = m_MainFont;
-
- io.FontDefault = m_MainFont;
+
+ m_large_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Bold.ttf", 20.0f);
+ if (!m_large_font)
+ m_large_font = m_main_font;
+
+ m_small_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Light.ttf", 12.0f);
+ if (!m_small_font)
+ m_small_font = m_main_font;
+
+ io.FontDefault = m_main_font;
DONUT_INFO("ImGUI fonts loaded successfully");
}
- void Window::ShutdownImGui()
+ auto Window::shutdown_im_gui() -> void
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
-
+
DONUT_INFO("ImGUI shutdown");
}
- void Window::BeginImGuiFrame()
+ auto Window::begin_im_gui_frame() -> void
{
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
- void Window::EndImGuiFrame()
+ auto Window::end_im_gui_frame() -> void
{
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
@@ -228,114 +228,114 @@ namespace Donut
}
}
- void Window::GLFWErrorCallback(int error, const char* description)
+ auto Window::glfw_error_callback(int error, const char* description) -> void
{
DONUT_ERROR("GLFW Error ({}): {}", error, description ? description : "");
}
- void Window::GLFWWindowCloseCallback(GLFWwindow* window)
+ auto Window::glfw_window_close_callback(GLFWwindow* window) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
WindowCloseEvent event;
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
- void Window::GLFWWindowSizeCallback(GLFWwindow* window, int width, int height)
+ auto Window::glfw_window_size_callback(GLFWwindow* window, int width, int height) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
- win->m_Width = width;
- win->m_Height = height;
-
+ win->m_width = width;
+ win->m_height = height;
+
WindowResizeEvent event(width, height);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
- void Window::GLFWWindowFocusCallback(GLFWwindow* window, int focused)
+ auto Window::glfw_window_focus_callback(GLFWwindow* window, int focused) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
-
+
if (focused)
{
WindowFocusEvent event;
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
else
{
WindowLostFocusEvent event;
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
}
- void Window::GLFWWindowPosCallback(GLFWwindow* window, int xpos, int ypos)
+ auto Window::glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
WindowMovedEvent event(xpos, ypos);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
- void Window::GLFWKeyCallback(GLFWwindow* window, int key, int scancode,
- int action, int mods)
+ auto Window::glfw_key_callback(GLFWwindow* window, int key, int scancode,
+ int action, int mods) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
-
+
switch (action)
{
case GLFW_PRESS:
{
KeyPressedEvent event(key, false);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
} break;
case GLFW_RELEASE:
{
KeyReleasedEvent event(key);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
} break;
case GLFW_REPEAT:
{
KeyPressedEvent event(key, true);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
} break;
}
}
- void Window::GLFWCharCallback(GLFWwindow* window, unsigned int keycode)
+ auto Window::glfw_char_callback(GLFWwindow* window, unsigned int keycode) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
KeyTypedEvent event(keycode);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
}
- void Window::GLFWMouseButtonCallback(GLFWwindow* window, int button,
- int action, int mods)
+ auto Window::glfw_mouse_button_callback(GLFWwindow* window, int button,
+ int action, int mods) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
-
+
switch (action)
{
case GLFW_PRESS:
{
MouseButtonPressedEvent event(button);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
} break;
case GLFW_RELEASE:
{
MouseButtonReleasedEvent event(button);
- win->m_EventHandler.OnEvent(event);
+ win->m_event_handler.on_event(event);
} break;
}
}
- void Window::GLFWMouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
+ auto Window::glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
- MouseScrolledEvent event((float)xOffset, (float)yOffset);
- win->m_EventHandler.OnEvent(event);
+ MouseScrolledEvent event((float)x_offset, (float)y_offset);
+ win->m_event_handler.on_event(event);
}
- void Window::GLFWCursorPosCallback(GLFWwindow* window, double xPos, double yPos)
+ auto Window::glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos) -> void
{
Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
- MouseMovedEvent event((float)xPos, (float)yPos);
- win->m_EventHandler.OnEvent(event);
+ MouseMovedEvent event((float)x_pos, (float)y_pos);
+ win->m_event_handler.on_event(event);
}
-} \ No newline at end of file
+}
diff --git a/src/Core/Window.h b/src/Core/Window.h
index f450bad..9b7d7b9 100644
--- a/src/Core/Window.h
+++ b/src/Core/Window.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Event.h"
-#include "Log.h"
+#include "event.h"
+#include "log.h"
#include <GLFW/glfw3.h>
#include <imgui.h>
@@ -18,59 +18,61 @@ namespace Donut
Window(const std::string& title, int width, int height);
~Window();
- bool ShouldClose() const;
- void OnUpdate() const;
+ auto should_close() const -> bool;
+ auto on_update() const -> void;
- void SetEventCallback(const EventCallbackFn& callback)
+ auto set_event_callback(const EventCallbackFn& callback) -> void
{
- m_EventHandler.SetEventCallback(callback);
+ m_event_handler.set_event_callback(callback);
}
- EventHandler& GetEventHandler() { return m_EventHandler; }
+ auto get_event_handler() -> EventHandler& { return m_event_handler; }
- unsigned int GetWidth() const { return m_Width; }
- unsigned int GetHeight() const { return m_Height; }
+ auto get_width() const -> unsigned int { return m_width; }
+ auto get_height() const -> unsigned int { return m_height; }
- void* GetNativeWindow() const { return m_Window; }
+ auto get_native_window() const -> void* { return m_window; }
+
+ auto set_cursor_locked(bool locked) -> void;
+ auto set_cursor_visible(bool visible) -> void;
+ auto is_cursor_locked() const -> bool { return m_cursor_locked; }
+ auto is_cursor_visible() const -> bool { return m_cursor_visible; }
+
+ auto init_im_gui() -> void;
+ auto begin_im_gui_frame() -> void;
+ auto end_im_gui_frame() -> void;
- void SetCursorLocked(bool locked);
- void SetCursorVisible(bool visible);
- bool IsCursorLocked() const { return m_CursorLocked; }
- bool IsCursorVisible() const { return m_CursorVisible; }
-
- void InitImGui();
- void BeginImGuiFrame();
- void EndImGuiFrame();
private:
- void Init();
- void Shutdown();
+ auto init() -> void;
+ auto shutdown() -> void;
+
+ auto setup_im_gui_fonts() -> void;
+ auto shutdown_im_gui() -> void;
+
+ static auto glfw_error_callback(int error, const char* description) -> void;
+ static auto glfw_window_close_callback(GLFWwindow* window) -> void;
+ static auto glfw_window_size_callback(GLFWwindow* window, int width, int height) -> void;
+ static auto glfw_window_focus_callback(GLFWwindow* window, int focused) -> void;
+ static auto glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos) -> void;
+ static auto glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) -> void;
+ static auto glfw_char_callback(GLFWwindow* window, unsigned int keycode) -> void;
+ static auto glfw_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) -> void;
+ static auto glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset) -> void;
+ static auto glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos) -> void;
- void SetupImGuiFonts();
- void ShutdownImGui();
-
- static void GLFWErrorCallback(int error, const char* description);
- static void GLFWWindowCloseCallback(GLFWwindow* window);
- static void GLFWWindowSizeCallback(GLFWwindow* window, int width, int height);
- static void GLFWWindowFocusCallback(GLFWwindow* window, int focused);
- static void GLFWWindowPosCallback(GLFWwindow* window, int xpos, int ypos);
- static void GLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
- static void GLFWCharCallback(GLFWwindow* window, unsigned int keycode);
- static void GLFWMouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
- static void GLFWMouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset);
- static void GLFWCursorPosCallback(GLFWwindow* window, double xPos, double yPos);
private:
- GLFWwindow* m_Window;
- EventHandler m_EventHandler;
+ GLFWwindow* m_window;
+ EventHandler m_event_handler;
+
+ std::string m_title;
+ unsigned int m_width;
+ unsigned int m_height;
+ bool m_is_closed;
+ bool m_cursor_locked = false;
+ bool m_cursor_visible = true;
- std::string m_Title;
- unsigned int m_Width;
- unsigned int m_Height;
- bool m_IsClosed;
- bool m_CursorLocked = false;
- bool m_CursorVisible = true;
-
- ImFont* m_MainFont = nullptr;
- ImFont* m_SmallFont = nullptr;
- ImFont* m_LargeFont = nullptr;
+ ImFont* m_main_font = nullptr;
+ ImFont* m_small_font = nullptr;
+ ImFont* m_large_font = nullptr;
};
-} \ No newline at end of file
+}
diff --git a/src/Engine/Engine.cpp b/src/Engine/Engine.cpp
index e436f4c..059fdda 100644
--- a/src/Engine/Engine.cpp
+++ b/src/Engine/Engine.cpp
@@ -8,68 +8,68 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
-#include "Engine.h"
-#include "Core/Log.h"
-#include "Core/HDRIManager.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.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_SagA(glm::vec3(0.0f, 0.0f, 0.0f), 8.54e36f)
+ : m_sag_a(glm::vec3(0.0f, 0.0f, 0.0f), 8.54e36f)
{
- m_Width = 1280;
- m_Height = 720;
+ m_width = 1280;
+ m_height = 720;
- m_Camera.SetCameraMode(CameraMode::Orbital);
- m_Camera.SetOrbitalRadius(1e11);
- m_Camera.SetOrbitalLimits(1e9, 1e13);
- m_Camera.SetOrbitalSpeed(0.01f);
- m_Camera.SetZoomSpeed(1e10f);
+ 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 =
+ m_objects =
{
- { glm::vec4(0.00f, 0.00f, 0.00f, m_SagA.m_Rs), glm::vec4(0, 0, 0, 1), static_cast<float>(m_SagA.m_Mass) }
+ { 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) }
};
// The geodesic ray tracer used to be a compute shader; it is now a
- // fullscreen vertex+fragment pass (see DispatchCompute) so it runs on
+ // fullscreen vertex+fragment pass (see dispatch_compute) so it runs on
// macOS OpenGL 4.1, which has no compute shaders.
- m_ComputeProgram = Ref<Shader>(Shader::Create("Assets/Shaders/Geodesic.glsl"));
- m_ShaderProgram = Ref<Shader>(Shader::Create("Assets/Shaders/TexturedQuad.glsl"));
- m_BlurShader = Ref<Shader>(Shader::Create("Assets/Shaders/Blur.glsl"));
+ m_compute_program = Ref<Shader>(Shader::create("assets/shaders/Geodesic.glsl"));
+ m_shader_program = Ref<Shader>(Shader::create("assets/shaders/TexturedQuad.glsl"));
+ m_blur_shader = Ref<Shader>(Shader::create("assets/shaders/Blur.glsl"));
- auto& hdriManager = HDRIManager::Get();
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
- if (!m_HDRIEnvironment)
+ auto& hdri_manager = HDRIManager::get();
+ m_hdri_environment = hdri_manager.get_current_hdri();
+ if (!m_hdri_environment)
{
- hdriManager.SetCurrentHDRI("Assets/HDRI/HDR_blue_nebulae-1.hdr");
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
- if (!m_HDRIEnvironment)
+ 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_CameraUBO = UniformBuffer::Create(128, 1);
- m_DiskUBO = UniformBuffer::Create(sizeof(float) * 5, 2);
+ m_camera_ubo = UniformBuffer::create(128, 1);
+ m_disk_ubo = UniformBuffer::create(sizeof(float) * 5, 2);
- uint32_t objUBOSize = sizeof(int) + 3 * sizeof(float)
+ uint32_t obj_ubo_size = sizeof(int) + 3 * sizeof(float)
+ 16 * (sizeof(glm::vec4) + sizeof(glm::vec4))
+ 16 * sizeof(float);
- m_ObjectsUBO = UniformBuffer::Create(objUBOSize, 3);
+ m_objects_ubo = UniformBuffer::create(obj_ubo_size, 3);
- m_SimulationUBO = UniformBuffer::Create(sizeof(int) * 2 + sizeof(float) * 2, 4);
+ m_simulation_ubo = UniformBuffer::create(sizeof(int) * 2 + sizeof(float) * 2, 4);
auto result = QuadVAO();
- m_QuadVAO = result.first;
- m_Texture = result.second;
+ m_quad_vao = result.first;
+ m_texture = result.second;
// GLSL 4.10 forbids explicit binding qualifiers on uniform blocks, so
// associate the geodesic shader's blocks with their UBO binding points
// from the host side instead.
- if (m_ComputeProgram)
+ if (m_compute_program)
{
- uint32_t prog = m_ComputeProgram->GetRendererID();
+ 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 }
@@ -82,77 +82,77 @@ namespace Donut
}
}
- glGenFramebuffers(1, &m_GeodesicFBO);
+ glGenFramebuffers(1, &m_geodesic_fbo);
}
- void Engine::UpdateWindowDimensions()
+ auto Engine::update_window_dimensions() -> void
{
- UpdateComputeDimensions();
+ update_compute_dimensions();
}
- void Engine::SetWindowDimensions(int width, int height)
+ auto Engine::set_window_dimensions(int width, int height) -> void
{
- int oldWidth = m_Width;
- int oldHeight = m_Height;
- int oldComputeHeight = m_ComputeHeight;
+ int old_width = m_width;
+ int old_height = m_height;
+ int old_compute_height = m_compute_height;
- m_Width = width;
- m_Height = height;
+ m_width = width;
+ m_height = height;
- if (oldWidth != m_Width ||
- oldHeight != m_Height ||
- oldComputeHeight != m_ComputeHeight)
- UpdateComputeDimensions();
+ if (old_width != m_width ||
+ old_height != m_height ||
+ old_compute_height != m_compute_height)
+ update_compute_dimensions();
}
- void Engine::UpdatePerformance(float deltaTime)
+ auto Engine::update_performance(float delta_time) -> void
{
- if (deltaTime > 0.0f)
- m_CurrentFPS = 1.0f / deltaTime;
+ if (delta_time > 0.0f)
+ m_current_fps = 1.0f / delta_time;
}
- void Engine::UpdateComputeDimensions()
+ auto Engine::update_compute_dimensions() -> void
{
- m_Texture = Texture2D::Create(GetComputeWidth(), m_ComputeHeight);
+ m_texture = Texture2D::create(get_compute_width(), m_compute_height);
}
- void Engine::DrawFullScreenQuad()
+ auto Engine::draw_full_screen_quad() -> void
{
- RenderCommand::SetViewport(0, 0, m_Width, m_Height);
+ RenderCommand::set_viewport(0, 0, m_width, m_height);
- m_ShaderProgram->Bind();
- m_QuadVAO->Bind();
+ m_shader_program->bind();
+ m_quad_vao->bind();
- m_Texture->Bind(0);
- m_ShaderProgram->SetInt("u_ScreenTexture", 0);
+ m_texture->bind(0);
+ m_shader_program->set_int("u_ScreenTexture", 0);
- RenderCommand::DisableDepthTest();
- RenderCommand::DrawArrays(6);
- RenderCommand::EnableDepthTest();
+ RenderCommand::disable_depth_test();
+ RenderCommand::draw_arrays(6);
+ RenderCommand::enable_depth_test();
}
- void Engine::DrawBlurPass()
+ auto Engine::draw_blur_pass() -> void
{
- RenderCommand::SetViewport(0, 0, m_Width, m_Height);
+ RenderCommand::set_viewport(0, 0, m_width, m_height);
- m_BlurShader->Bind();
- m_QuadVAO->Bind();
+ m_blur_shader->bind();
+ m_quad_vao->bind();
- m_Texture->Bind(0);
- m_BlurShader->SetInt("u_ScreenTexture", 0);
- m_BlurShader->SetFloat2("u_Resolution", glm::vec2(m_Width, m_Height));
- m_BlurShader->SetFloat("u_BlurStrength", m_BlurStrength);
- m_BlurShader->SetFloat("u_GlowIntensity", m_GlowIntensity);
+ 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::DisableDepthTest();
- RenderCommand::DrawArrays(6);
- RenderCommand::EnableDepthTest();
+ RenderCommand::disable_depth_test();
+ RenderCommand::draw_arrays(6);
+ RenderCommand::enable_depth_test();
}
- void Engine::DrawGeodesicPass(int cw, int ch)
+ auto Engine::draw_geodesic_pass(int cw, int ch) -> void
{
- m_QuadVAO->Bind();
- RenderCommand::DisableDepthTest();
+ m_quad_vao->bind();
+ RenderCommand::disable_depth_test();
#ifdef __APPLE__
// macOS aborts any GPU submission that runs longer than a couple of
@@ -160,7 +160,7 @@ namespace Donut
// a single fullscreen draw, so render it in scissored tiles and flush
// after each, keeping every submission short enough to survive the
// watchdog. Compute-capable platforms draw it in one pass.
- // Largest tile that keeps a tile's worst-case work (tile^2 * stepCap)
+ // Largest tile that keeps a tile's worst-case work (tile^2 * step_cap)
// inside the safe watchdog zone measured on this GPU (~25M pixel-steps
// per submission); bigger tiles mean fewer glFinish stalls.
const int tile = 64;
@@ -172,52 +172,52 @@ namespace Donut
{
int tw = std::min(tile, cw - x);
glScissor(x, y, tw, th);
- RenderCommand::DrawArrays(6);
+ RenderCommand::draw_arrays(6);
glFinish();
}
}
glDisable(GL_SCISSOR_TEST);
#else
- RenderCommand::DrawArrays(6);
+ RenderCommand::draw_arrays(6);
#endif
- RenderCommand::EnableDepthTest();
+ RenderCommand::enable_depth_test();
}
- void Engine::DispatchCompute(const Camera& cam)
+ auto Engine::dispatch_compute(const Camera& cam) -> void
{
- auto& hdriManager = HDRIManager::Get();
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
+ auto& hdri_manager = HDRIManager::get();
+ m_hdri_environment = hdri_manager.get_current_hdri();
- int cw = GetComputeWidth();
- int ch = m_ComputeHeight;
+ int cw = get_compute_width();
+ int ch = m_compute_height;
- // Render the geodesic pass into m_Texture through an FBO. This replaces
- // the old compute dispatch + imageStore path, which relied on OpenGL
+ // Render the geodesic pass into m_texture through an FBO. This replaces
+ // the old compute dispatch + image_store path, which relied on OpenGL
// 4.3 compute and 4.2 image load/store that macOS does not provide.
- glBindFramebuffer(GL_FRAMEBUFFER, m_GeodesicFBO);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_Texture->GetRendererID(), 0);
+ 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_ComputeProgram->Bind();
- UploadCameraUBO(cam);
- UploadDiskUBO();
- UploadObjectsUBO(m_Objects);
- UploadSimulationUBO();
- m_ComputeProgram->SetFloat2("u_Resolution", glm::vec2(static_cast<float>(cw), static_cast<float>(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_HDRIEnvironment)
+ if (m_hdri_environment)
{
- m_HDRIEnvironment->Bind(5);
- m_ComputeProgram->SetInt("u_HDRIEnvironment", 5);
+ m_hdri_environment->bind(5);
+ m_compute_program->set_int("u_HDRIEnvironment", 5);
}
- DrawGeodesicPass(cw, ch);
+ draw_geodesic_pass(cw, ch);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
- void Engine::UploadCameraUBO(const Camera& cam)
+ auto Engine::upload_camera_ubo(const Camera& cam) -> void
{
struct UBOData
{
@@ -225,80 +225,80 @@ namespace Donut
glm::vec3 right; float _pad1;
glm::vec3 up; float _pad2;
glm::vec3 forward; float _pad3;
- float tanHalfFov;
+ float tan_half_fov;
float aspect;
bool moving;
int _pad4;
} data;
- glm::vec3 fwd = glm::normalize(cam.GetOrbitalTarget() - cam.GetOrbitalPosition());
+ glm::vec3 fwd = glm::normalize(cam.get_orbital_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.GetOrbitalPosition();
+ data.pos = cam.get_orbital_position();
data.right = right;
data.up = up;
data.forward = fwd;
- data.tanHalfFov = static_cast<float>(tan(glm::radians(60.0f * 0.5f)));
- data.aspect = static_cast<float>(GetComputeWidth()) / static_cast<float>(m_ComputeHeight);
- data.moving = cam.IsDragging() || cam.IsPanning();
+ data.tan_half_fov = static_cast<float>(tan(glm::radians(60.0f * 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_CameraUBO->SetData(&data, sizeof(UBOData));
- m_CameraUBO->Bind(1);
+ m_camera_ubo->set_data(&data, sizeof(UBOData));
+ m_camera_ubo->bind(1);
}
- void Engine::UploadObjectsUBO(const std::vector<ObjectData>& objs)
+ auto Engine::upload_objects_ubo(const std::vector<ObjectData>& objs) -> void
{
struct UBOData
{
- int numObjects;
+ int num_objects;
float _pad0, _pad1, _pad2;
- glm::vec4 posRadius[16];
+ glm::vec4 pos_radius[16];
glm::vec4 color[16];
float mass[16];
} data;
size_t count = std::min(objs.size(), size_t(16));
- data.numObjects = static_cast<int>(count);
+ data.num_objects = static_cast<int>(count);
for (size_t i = 0; i < count; ++i)
{
- data.posRadius[i] = objs[i].m_PosRadius;
- data.color[i] = objs[i].m_Color;
- data.mass[i] = objs[i].m_Mass;
+ data.pos_radius[i] = objs[i].m_pos_radius;
+ data.color[i] = objs[i].m_color;
+ data.mass[i] = objs[i].m_mass;
}
- m_ObjectsUBO->SetData(&data, sizeof(data));
- m_ObjectsUBO->Bind(3);
+ m_objects_ubo->set_data(&data, sizeof(data));
+ m_objects_ubo->bind(3);
}
- void Engine::UploadDiskUBO()
+ auto Engine::upload_disk_ubo() -> void
{
- float r1 = static_cast<float>(m_SagA.m_Rs * 2.2);
- float r2 = static_cast<float>(m_SagA.m_Rs * 5.2);
+ 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 = static_cast<float>(m_SagA.m_Rs * m_DiskThickness);
- float diskData[5] = { r1, r2, num, thickness, m_DiskDensity };
+ float thickness = static_cast<float>(m_sag_a.m_rs * m_disk_thickness);
+ float disk_data[5] = { r1, r2, num, thickness, m_disk_density };
- m_DiskUBO->SetData(diskData, sizeof(diskData));
- m_DiskUBO->Bind(2);
+ m_disk_ubo->set_data(disk_data, sizeof(disk_data));
+ m_disk_ubo->bind(2);
}
- void Engine::UploadSimulationUBO()
+ auto Engine::upload_simulation_ubo() -> void
{
struct UBOData
{
- int maxStepsMoving;
- int maxStepsStatic;
- float earlyExitDistance;
+ int max_steps_moving;
+ int max_steps_static;
+ float early_exit_distance;
float time;
} data;
- data.maxStepsMoving = m_MaxStepsMoving;
- data.maxStepsStatic = m_MaxStepsStatic;
- data.earlyExitDistance = m_EarlyExitDistance;
- data.time = static_cast<float>(glfwGetTime()) * m_RotationSpeed;
+ data.max_steps_moving = m_max_steps_moving;
+ data.max_steps_static = m_max_steps_static;
+ data.early_exit_distance = m_early_exit_distance;
+ data.time = static_cast<float>(glfwGetTime()) * m_rotation_speed;
#ifdef __APPLE__
// macOS has no compute shaders, so the geodesic pass runs as a tiled
@@ -307,58 +307,58 @@ namespace Donut
// GPU, so cap them here. Windows/Linux keep the full step count.
// While the camera moves, render cheaply so interaction stays smooth;
// when it settles, spend more steps for a cleaner image. Both stay well
- // under the per-tile GPU-watchdog budget (see DrawGeodesicPass).
- data.maxStepsMoving = std::min(data.maxStepsMoving, 4000);
- data.maxStepsStatic = std::min(data.maxStepsStatic, 6000);
+ // under the per-tile GPU-watchdog budget (see draw_geodesic_pass).
+ data.max_steps_moving = std::min(data.max_steps_moving, 4000);
+ data.max_steps_static = std::min(data.max_steps_static, 6000);
#endif
- m_SimulationUBO->SetData(&data, sizeof(data));
- m_SimulationUBO->Bind(4);
+ m_simulation_ubo->set_data(&data, sizeof(data));
+ m_simulation_ubo->bind(4);
}
- void Engine::UpdatePhysics(float deltaTime)
+ auto Engine::update_physics(float delta_time) -> void
{
- for (auto& obj : m_Objects)
+ for (auto& obj : m_objects)
{
- for (auto& obj2 : m_Objects)
+ for (auto& obj2 : m_objects)
{
if (&obj == &obj2) continue;
- float dx = obj2.m_PosRadius.x - obj.m_PosRadius.x;
- float dy = obj2.m_PosRadius.y - obj.m_PosRadius.y;
- float dz = obj2.m_PosRadius.z - obj.m_PosRadius.z;
+ 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;
+ 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)
+ 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_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_PosRadius.x += static_cast<float>(obj.m_Velocity.x);
- obj.m_PosRadius.y += static_cast<float>(obj.m_Velocity.y);
- obj.m_PosRadius.z += static_cast<float>(obj.m_Velocity.z);
+ 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::RenderScene()
+ auto Engine::render_scene() -> void
{
- RenderCommand::Clear();
- m_ShaderProgram->Bind();
- m_QuadVAO->Bind();
- m_Texture->Bind(0);
- RenderCommand::DrawArrays(6);
+ RenderCommand::clear();
+ m_shader_program->bind();
+ m_quad_vao->bind();
+ m_texture->bind(0);
+ RenderCommand::draw_arrays(6);
}
- Ref<Shader> Engine::CreateComputeProgram(const char* path)
+ auto Engine::create_compute_program(const char* path) -> Ref<Shader>
{
std::ifstream in(path);
if(!in.is_open())
@@ -368,13 +368,13 @@ namespace Donut
}
std::stringstream ss;
ss << in.rdbuf();
- std::string srcStr = ss.str();
- return Ref<Shader>(Shader::CreateCompute("ComputeShader", srcStr));
+ std::string src_str = ss.str();
+ return Ref<Shader>(Shader::create_compute("ComputeShader", src_str));
}
- std::pair<Ref<VertexArray>, Ref<Texture2D>> Engine::QuadVAO()
+ auto Engine::QuadVAO() -> std::pair<Ref<VertexArray>, Ref<Texture2D>>
{
- float quadVertices[] =
+ float quad_vertices[] =
{
// Positions // TexCoords
-1.0f, 1.0f, 0.0f, 1.0f,
@@ -385,82 +385,82 @@ namespace Donut
1.0f, 1.0f, 1.0f, 1.0f
};
- auto vertexBuffer = Ref<VertexBuffer>(VertexBuffer::Create(quadVertices, static_cast<uint32_t>(sizeof(quadVertices))));
+ auto vertex_buffer = Ref<VertexBuffer>(VertexBuffer::create(quad_vertices, static_cast<uint32_t>(sizeof(quad_vertices))));
VertexBufferLayout layout;
- layout.Push<float>(2); // Position (x, y)
- layout.Push<float>(2); // TexCoord (u, v)
- vertexBuffer->SetLayout(layout);
+ layout.push<float>(2); // Position (x, y)
+ layout.push<float>(2); // TexCoord (u, v)
+ vertex_buffer->set_layout(layout);
- auto vertexArray = Ref<VertexArray>(VertexArray::Create());
- vertexArray->AddVertexBuffer(vertexBuffer);
- auto texture = Texture2D::Create(GetComputeWidth(), m_ComputeHeight);
+ 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 { vertexArray, texture };
+ return { vertex_array, texture };
}
- void Engine::LoadObjectsFromScene(const std::vector<Donut::Object>& objects)
+ auto Engine::load_objects_from_scene(const std::vector<Donut::Object>& objects) -> void
{
- m_Objects.clear();
- m_Objects.push_back(
+ m_objects.clear();
+ m_objects.push_back(
{
- glm::vec4(0.00f, 0.00f, 0.00f, m_SagA.m_Rs),
+ glm::vec4(0.00f, 0.00f, 0.00f, m_sag_a.m_rs),
glm::vec4(0, 0, 0, 1),
- static_cast<float>(m_SagA.m_Mass)
+ static_cast<float>(m_sag_a.m_mass)
});
for (const auto& obj : objects)
{
- ObjectData engineObj;
+ ObjectData engine_obj;
- float scaleFactor = 1e10f;
- engineObj.m_PosRadius = glm::vec4
+ float scale_factor = 1e10f;
+ engine_obj.m_pos_radius = glm::vec4
(
- obj.m_Centre.x * scaleFactor,
- obj.m_Centre.y * scaleFactor,
- obj.m_Centre.z * scaleFactor,
- obj.m_Radius * scaleFactor
+ obj.m_centre.x * scale_factor,
+ obj.m_centre.y * scale_factor,
+ obj.m_centre.z * scale_factor,
+ obj.m_radius * scale_factor
);
- engineObj.m_Color = glm::vec4(obj.m_Material.m_Color, 1.0f);
+ engine_obj.m_color = glm::vec4(obj.m_material.m_color, 1.0f);
- float volume = (4.0f / 3.0f) * 3.14159f * engineObj.m_PosRadius.w * engineObj.m_PosRadius.w * engineObj.m_PosRadius.w;
+ 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;
- engineObj.m_Mass = volume * density;
- engineObj.m_Velocity = glm::vec3(0.0f, 0.0f, 0.0f);
+ engine_obj.m_mass = volume * density;
+ engine_obj.m_velocity = glm::vec3(0.0f, 0.0f, 0.0f);
- m_Objects.push_back(engineObj);
+ m_objects.push_back(engine_obj);
}
DONUT_INFO("Loaded {} objects from WorldBuilder scene (scaled up by {})", objects.size(), 1e10f);
- PrintObjectInfo();
+ print_object_info();
}
- void Engine::PrintObjectInfo() const
+ auto Engine::print_object_info() const -> void
{
DONUT_INFO("=== Object Information ===");
- DONUT_INFO("Total objects: {}", m_Objects.size());
+ DONUT_INFO("Total objects: {}", m_objects.size());
- for (size_t i = 0; i < m_Objects.size(); ++i)
+ for (size_t i = 0; i < m_objects.size(); ++i)
{
- const auto& obj = m_Objects[i];
+ const auto& obj = m_objects[i];
DONUT_INFO("Object {}: Pos=({}, {}, {}), Radius={}, Mass={}, Color=({}, {}, {})",
i,
- obj.m_PosRadius.x, obj.m_PosRadius.y, obj.m_PosRadius.z,
- obj.m_PosRadius.w,
- obj.m_Mass,
- obj.m_Color.x, obj.m_Color.y, obj.m_Color.z
+ 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.GetOrbitalPosition().x,
- m_Camera.GetOrbitalPosition().y,
- m_Camera.GetOrbitalPosition().z
+ m_camera.get_orbital_position().x,
+ m_camera.get_orbital_position().y,
+ m_camera.get_orbital_position().z
);
- DONUT_INFO("Camera radius: {}", m_Camera.GetOrbitalRadius());
+ DONUT_INFO("Camera radius: {}", m_camera.get_orbital_radius());
DONUT_INFO("========================");
}
- void Engine::ExportHighResFrame(const std::string& filename, int width, int height)
+ 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);
@@ -476,48 +476,48 @@ namespace Donut
return;
}
- int originalWidth = m_Width;
- int originalHeight = m_Height;
- int originalComputeHeight = m_ComputeHeight;
+ int original_width = m_width;
+ int original_height = m_height;
+ int original_compute_height = m_compute_height;
- m_Width = width;
- m_Height = height;
- m_ComputeHeight = height;
+ m_width = width;
+ m_height = height;
+ m_compute_height = height;
- int computeHeight = height;
- int computeWidth = (width * computeHeight) / height;
+ int compute_height = height;
+ int compute_width = (width * compute_height) / height;
- if (computeWidth <= 0 || computeHeight <= 0)
+ if (compute_width <= 0 || compute_height <= 0)
{
- DONUT_ERROR("Invalid compute dimensions: {}x{}", computeWidth, computeHeight);
+ DONUT_ERROR("Invalid compute dimensions: {}x{}", compute_width, compute_height);
return;
}
- FramebufferSpecification fbSpec;
- fbSpec.Width = width;
- fbSpec.Height = height;
- fbSpec.Attachments = { FramebufferTextureFormat::RGBA8 };
+ FramebufferSpecification fb_spec;
+ fb_spec.Width = width;
+ fb_spec.Height = height;
+ fb_spec.attachments = { FramebufferTextureFormat::RGBA8 };
- auto highResFramebuffer = Framebuffer::Create(fbSpec);
- if (!highResFramebuffer)
+ auto high_res_framebuffer = Framebuffer::create(fb_spec);
+ if (!high_res_framebuffer)
{
DONUT_ERROR("Failed to create high-resolution framebuffer");
return;
}
- auto highResTexture = Texture2D::Create(computeWidth, computeHeight);
- if (!highResTexture)
+ auto high_res_texture = Texture2D::create(compute_width, compute_height);
+ if (!high_res_texture)
{
DONUT_ERROR("Failed to create high-resolution texture");
return;
}
- // Render the geodesic pass into highResTexture through the geodesic FBO.
- glBindFramebuffer(GL_FRAMEBUFFER, m_GeodesicFBO);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, highResTexture->GetRendererID(), 0);
- glViewport(0, 0, computeWidth, computeHeight);
+ // Render the geodesic pass into high_res_texture through the geodesic FBO.
+ 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_ComputeProgram->Bind();
+ m_compute_program->bind();
struct UBOData
{
@@ -525,90 +525,90 @@ namespace Donut
glm::vec3 right; float _pad1;
glm::vec3 up; float _pad2;
glm::vec3 forward; float _pad3;
- float tanHalfFov;
+ float tan_half_fov;
float aspect;
bool moving;
int _pad4;
} data;
- glm::vec3 fwd = glm::normalize(m_Camera.GetOrbitalTarget() - m_Camera.GetOrbitalPosition());
+ 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.GetOrbitalPosition();
+ data.pos = m_camera.get_orbital_position();
data.right = right;
data.up = up;
data.forward = fwd;
- data.tanHalfFov = static_cast<float>(tan(glm::radians(60.0f * 0.5f)));
- data.aspect = static_cast<float>(computeWidth) / static_cast<float>(computeHeight);
- data.moving = m_Camera.IsDragging() || m_Camera.IsPanning();
+ 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_CameraUBO->SetData(&data, sizeof(UBOData));
- m_CameraUBO->Bind(1);
+ m_camera_ubo->set_data(&data, sizeof(UBOData));
+ m_camera_ubo->bind(1);
- UploadDiskUBO();
- UploadObjectsUBO(m_Objects);
- UploadSimulationUBO();
- m_ComputeProgram->SetFloat2("u_Resolution", glm::vec2(static_cast<float>(computeWidth), static_cast<float>(computeHeight)));
+ 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_HDRIEnvironment)
+ if (m_hdri_environment)
{
- m_HDRIEnvironment->Bind(5);
- m_ComputeProgram->SetInt("u_HDRIEnvironment", 5);
+ m_hdri_environment->bind(5);
+ m_compute_program->set_int("u_HDRIEnvironment", 5);
}
- DrawGeodesicPass(computeWidth, computeHeight);
+ draw_geodesic_pass(compute_width, compute_height);
// Display the rendered frame into the high-res framebuffer for read-back.
- highResFramebuffer->Bind();
- RenderCommand::SetViewport(0, 0, width, height);
- RenderCommand::Clear();
+ high_res_framebuffer->bind();
+ RenderCommand::set_viewport(0, 0, width, height);
+ RenderCommand::clear();
- m_ShaderProgram->Bind();
- m_QuadVAO->Bind();
+ m_shader_program->bind();
+ m_quad_vao->bind();
- highResTexture->Bind(0);
- m_ShaderProgram->SetInt("u_ScreenTexture", 0);
+ high_res_texture->bind(0);
+ m_shader_program->set_int("u_ScreenTexture", 0);
- RenderCommand::DisableDepthTest();
- RenderCommand::DrawArrays(6);
- RenderCommand::EnableDepthTest();
+ 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::ReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+ RenderCommand::read_pixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
DONUT_INFO("Flipping image vertically...");
- std::vector<unsigned char> flippedPixels(width * height * 4);
+ std::vector<unsigned char> flipped_pixels(width * height * 4);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
- int srcIndex = (y * width + x) * 4;
- int dstIndex = ((height - 1 - y) * width + x) * 4;
- flippedPixels[dstIndex + 0] = pixels[srcIndex + 0]; // R
- flippedPixels[dstIndex + 1] = pixels[srcIndex + 1]; // G
- flippedPixels[dstIndex + 2] = pixels[srcIndex + 2]; // B
- flippedPixels[dstIndex + 3] = pixels[srcIndex + 3]; // A
+ 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]; // R
+ flipped_pixels[dst_index + 1] = pixels[src_index + 1]; // G
+ flipped_pixels[dst_index + 2] = pixels[src_index + 2]; // B
+ flipped_pixels[dst_index + 3] = pixels[src_index + 3]; // A
}
}
DONUT_INFO("Saving PNG file: {}...", filename);
- int result = stbi_write_png(filename.c_str(), width, height, 4, flippedPixels.data(), width * 4);
+ 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);
- highResFramebuffer->Unbind();
+ high_res_framebuffer->unbind();
- m_Width = originalWidth;
- m_Height = originalHeight;
- m_ComputeHeight = originalComputeHeight;
+ m_width = original_width;
+ m_height = original_height;
+ m_compute_height = original_compute_height;
- RenderCommand::SetViewport(0, 0, originalWidth, originalHeight);
+ RenderCommand::set_viewport(0, 0, original_width, original_height);
}
}
diff --git a/src/Engine/Engine.h b/src/Engine/Engine.h
index a4cbdc7..c7d02b5 100644
--- a/src/Engine/Engine.h
+++ b/src/Engine/Engine.h
@@ -8,15 +8,15 @@
#include <chrono>
#include <cmath>
-#include "Core/Camera.h"
-#include "Object.h"
+#include "core/camera.h"
+#include "object.h"
-#include "Rendering/Renderer.h"
-#include "Rendering/Shader.h"
-#include "Rendering/VertexArray.h"
-#include "Rendering/Texture.h"
-#include "Rendering/UniformBuffer.h"
-#include "Rendering/TextureManager.h"
+#include "rendering/renderer.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>
@@ -30,33 +30,33 @@ namespace Donut
struct BlackHole
{
- glm::vec3 m_Position;
- double m_Mass;
- double m_Radius;
- double m_Rs;
+ 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_position(pos), m_mass(mass)
{
- m_Rs = 2.0 * G * m_Mass / (c * c);
+ m_rs = 2.0 * G * m_mass / (c * c);
}
- bool Intercept(float px, float py, float pz) const
+ 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 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;
+ return dist2 < m_rs * m_rs;
}
};
struct ObjectData
{
- glm::vec4 m_PosRadius;
- glm::vec4 m_Color;
- float m_Mass;
- glm::vec3 m_Velocity = glm::vec3(0.0f, 0.0f, 0.0f);
+ 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
@@ -65,120 +65,120 @@ namespace Donut
Engine();
~Engine() = default;
- void DrawFullScreenQuad();
- void DrawBlurPass();
- void DispatchCompute(const Camera& cam);
- void UploadCameraUBO(const Camera& cam);
- void UploadObjectsUBO(const std::vector<ObjectData>& objs);
- void UploadDiskUBO();
- void UploadSimulationUBO();
- void RenderScene();
- void UpdatePhysics(float deltaTime);
- void UpdateWindowDimensions();
- void SetWindowDimensions(int width, int height);
+ 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;
- int GetWidth() const { return m_Width; }
- int GetHeight() const { return m_Height; }
+ auto get_width() const -> int { return m_width; }
+ auto get_height() const -> int { return m_height; }
- std::vector<ObjectData>& GetObjects() { return m_Objects; }
- BlackHole& GetSagA() { return m_SagA; }
- Camera& GetCamera() { return m_Camera; }
- bool& GetGravity() { return m_Gravity; }
+ 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; }
- void UpdatePerformance(float deltaTime);
- void SetTargetFPS(int fps) { m_TargetFPS = fps; }
- int GetTargetFPS() const { return m_TargetFPS; }
- float GetCurrentFPS() const { return m_CurrentFPS; }
- void SetComputeHeight(int height)
+ 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__
// Without compute shaders the geodesic pass runs as a tiled
- // fragment shader (see DrawGeodesicPass), so very high working
+ // fragment shader (see draw_geodesic_pass), so very high working
// resolutions make each frame take many seconds. Cap it on macOS.
if (height > 256) height = 256;
#endif
- m_ComputeHeight = height;
+ m_compute_height = height;
}
- int GetComputeHeight() const { return m_ComputeHeight; }
- int GetComputeWidth() const { return (m_Width * m_ComputeHeight) / m_Height; }
- void UpdateComputeDimensions();
+ 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;
- int GetMaxStepsMoving() const { return m_MaxStepsMoving; }
- int GetMaxStepsStatic() const { return m_MaxStepsStatic; }
- float GetEarlyExitDistance() const { return m_EarlyExitDistance; }
- void SetMaxStepsMoving(int steps) { m_MaxStepsMoving = steps; }
- void SetMaxStepsStatic(int steps) { m_MaxStepsStatic = steps; }
- void SetEarlyExitDistance(float distance) { m_EarlyExitDistance = distance; }
+ 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; }
- float GetDiskThickness() const { return m_DiskThickness; }
- void SetDiskThickness(float thickness) { m_DiskThickness = thickness; }
+ auto get_disk_thickness() const -> float { return m_disk_thickness; }
+ auto set_disk_thickness(float thickness) -> void { m_disk_thickness = thickness; }
- float GetDiskDensity() const { return m_DiskDensity; }
- void SetDiskDensity(float density) { m_DiskDensity = density; }
+ auto get_disk_density() const -> float { return m_disk_density; }
+ auto set_disk_density(float density) -> void { m_disk_density = density; }
- float GetRotationSpeed() const { return m_RotationSpeed; }
- void SetRotationSpeed(float speed) { m_RotationSpeed = speed; }
+ auto get_rotation_speed() const -> float { return m_rotation_speed; }
+ auto set_rotation_speed(float speed) -> void { m_rotation_speed = speed; }
- float GetBlurStrength() const { return m_BlurStrength; }
- void SetBlurStrength(float strength) { m_BlurStrength = strength; }
+ auto get_blur_strength() const -> float { return m_blur_strength; }
+ auto set_blur_strength(float strength) -> void { m_blur_strength = strength; }
- float GetGlowIntensity() const { return m_GlowIntensity; }
- void SetGlowIntensity(float intensity) { m_GlowIntensity = intensity; }
+ auto get_glow_intensity() const -> float { return m_glow_intensity; }
+ auto set_glow_intensity(float intensity) -> void { m_glow_intensity = intensity; }
- void LoadObjectsFromScene(const std::vector<Donut::Object>& objects);
- void ExportHighResFrame(const std::string& filename, int width = 4096, int height = 3072);
- void PrintObjectInfo() const;
+ 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;
- void SetHDRIEnvironment(Ref<CubemapTexture> hdri) { m_HDRIEnvironment = hdri; }
- Ref<CubemapTexture> GetHDRIEnvironment() const { return m_HDRIEnvironment; }
+ auto set_hdri_environment(Ref<CubemapTexture> hdri) -> void { m_hdri_environment = hdri; }
+ auto get_hdri_environment() const -> Ref<CubemapTexture> { return m_hdri_environment; }
private:
- Ref<Shader> CreateComputeProgram(const char* path);
+ auto create_compute_program(const char* path) -> Ref<Shader>;
std::pair<Ref<VertexArray>, Ref<Texture2D>> QuadVAO();
// Draws the bound geodesic shader over a cw x ch target. On macOS this
// is split into scissored tiles (with a flush each) so no single GPU
// submission trips the OS watchdog; elsewhere it is one fast draw.
- void DrawGeodesicPass(int cw, int ch);
+ auto draw_geodesic_pass(int cw, int ch) -> void;
private:
- Ref<VertexArray> m_QuadVAO;
- Ref<Texture2D> m_Texture;
- Ref<CubemapTexture> m_HDRIEnvironment;
- Ref<Shader> m_ShaderProgram;
- Ref<Shader> m_ComputeProgram;
- Ref<Shader> m_BlurShader;
- Ref<UniformBuffer> m_CameraUBO;
- Ref<UniformBuffer> m_DiskUBO;
- Ref<UniformBuffer> m_ObjectsUBO;
- Ref<UniformBuffer> m_SimulationUBO;
+ 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;
- // FBO used to render the geodesic pass into m_Texture. The geodesic
+ // FBO used to render the geodesic pass into m_texture. The geodesic
// shader is a fragment shader (macOS has no compute), so it draws a
// fullscreen quad into this framebuffer instead of dispatching compute.
- uint32_t m_GeodesicFBO = 0;
+ 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_width;
+ int m_height;
+ float m_width_f = 100.0f*1e10f;
+ float m_height_f = 75.0f*1e10f;
- int m_TargetFPS = 60;
- float m_CurrentFPS = 60.0f;
- float m_LastFrameTime = 0.0f;
- int m_ComputeHeight = 150;
+ 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_SagA;
- Camera m_Camera;
- bool m_Gravity = false;
+ std::vector<ObjectData> m_objects;
+ BlackHole m_sag_a;
+ Camera m_camera;
+ bool m_gravity = false;
- int m_MaxStepsMoving = 60000;
- int m_MaxStepsStatic = 30000;
- float m_EarlyExitDistance = 5.0e11f;
+ int m_max_steps_moving = 60000;
+ int m_max_steps_static = 30000;
+ float m_early_exit_distance = 5.0e11f;
- float m_DiskThickness = 0.1f;
- float m_DiskDensity = 0.1f;
- float m_RotationSpeed = 1.0f;
- float m_BlurStrength = 2.0f;
- float m_GlowIntensity = 0.1f;
+ 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
index 842708e..8101540 100644
--- a/src/Engine/Object.h
+++ b/src/Engine/Object.h
@@ -7,67 +7,70 @@ namespace Donut
class Ray
{
public:
- glm::vec3 m_Direction;
- glm::vec3 m_Origin;
+ Ray(glm::vec3 o, glm::vec3 d)
+ : m_origin(o),
+ m_direction(glm::normalize(d)) { }
- 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:
- glm::vec3 m_Color;
- float m_Specular;
- float m_Emission;
-
- Material() : m_Color(1.0f, 1.0f, 1.0f), m_Specular(0.5f), m_Emission(0.0f) { }
+ 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) { }
+ : m_color(c),
+ m_specular(s),
+ m_emission(e) { }
+
+ public:
+ glm::vec3 m_color;
+ float m_specular;
+ float m_emission;
};
class Object
{
public:
- glm::vec3 m_Centre;
- float m_Radius;
- Material m_Material;
-
- 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) { }
-
- bool Intersect(Ray &ray, float &t)
+ 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;
+ 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)
+ if (discriminant < 0)
return false;
-
+
float intercept = (-b - sqrt(discriminant)) / (2.0f*a);
- if(intercept < 0)
+ if (intercept < 0)
{
intercept = (-b + sqrt(discriminant)) / (2.0f*a);
- if(intercept<0)
+ if (intercept < 0)
return false;
}
t = intercept;
return true;
}
-
- glm::vec3 GetNormal(glm::vec3 &point) const
+
+ auto get_normal(glm::vec3& point) const -> glm::vec3
{
- return glm::normalize(point - m_Centre);
+ 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
index f85fc13..4b41203 100644
--- a/src/Engine/Scene.h
+++ b/src/Engine/Scene.h
@@ -3,67 +3,68 @@
#include <vector>
#include <limits>
-#include "Object.h"
+#include "object.h"
namespace Donut
{
- class Scene
+ class Scene
{
public:
- std::vector<Object> objs;
- glm::vec3 m_LightPos;
+ Scene()
+ : m_light_pos(5.0f, 5.0f, 5.0f) { }
- Scene()
- : m_LightPos(5.0f, 5.0f, 5.0f) { }
-
- glm::vec3 Trace(Ray &ray)
+ auto trace(Ray& ray) -> glm::vec3
{
float closest = std::numeric_limits<float>::infinity();
- const Object* hitObj = nullptr;
-
- for(auto& obj : objs)
+ const Object* hit_obj = nullptr;
+
+ for (auto& obj : objs)
{
float t;
- if(obj.Intersect(ray, t))
- if(t < closest)
+ if (obj.intersect(ray, t))
+ if (t < closest)
{
closest = t;
- hitObj = &obj;
+ hit_obj = &obj;
}
}
-
- if(hitObj)
+
+ if (hit_obj)
{
- glm::vec3 hitPoint = ray.m_Origin + ray.m_Direction * closest;
- glm::vec3 normal = hitObj->GetNormal(hitPoint);
- glm::vec3 lightDir = glm::normalize(m_LightPos - hitPoint);
-
- float diff = std::max(glm::dot(normal, lightDir), 0.0f);
-
- Ray shadowRay(hitPoint + normal * 0.001f, lightDir);
- bool inShadow = false;
-
- for(auto& obj : objs)
+ 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(shadowRay, t))
+ if (obj.intersect(shadow_ray, t))
{
- inShadow = true;
+ in_shadow = true;
break;
}
}
-
- glm::vec3 color = hitObj->m_Material.m_Color;
+
+ glm::vec3 color = hit_obj->m_material.m_color;
float ambient = 0.1f;
-
- if (inShadow)
+
+ if (in_shadow)
return color * ambient;
return color * (ambient + diff * 0.9f);
}
-
- return glm::vec3(0.0f, 0.0f, 0.1f);
+
+ return glm::vec3(0.0f, 0.0f, 0.1f);
}
+
+ public:
+ std::vector<Object> objs;
+ glm::vec3 m_light_pos;
};
-}; \ No newline at end of file
+};
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.h b/src/Platform/OpenGL/OpenGLFramebuffer.h
deleted file mode 100644
index 8cd376c..0000000
--- a/src/Platform/OpenGL/OpenGLFramebuffer.h
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include "Rendering/Framebuffer.h"
-
-namespace Donut
-{
- class OpenGLFramebuffer : public Framebuffer
- {
- public:
- OpenGLFramebuffer(const FramebufferSpecification& spec);
- virtual ~OpenGLFramebuffer();
-
- void Invalidate();
-
- virtual void Bind() override;
- virtual void Unbind() override;
-
- virtual void Resize(uint32_t width, uint32_t height) override;
- virtual int ReadPixel(uint32_t attachmentIndex, int x, int y) override;
-
- virtual void ClearAttachment(uint32_t attachmentIndex, int value) override;
- virtual uint32_t GetColorAttachmentRendererID(uint32_t index = 0) const override { return m_ColorAttachments[index]; }
-
- virtual const FramebufferSpecification& GetSpecification() const override { return m_Specification; }
- private:
- uint32_t m_RendererID = 0;
- FramebufferSpecification m_Specification;
-
- std::vector<FramebufferTextureSpecification> m_ColorAttachmentSpecifications;
- FramebufferTextureSpecification m_DepthAttachmentSpecification = FramebufferTextureFormat::None;
-
- std::vector<uint32_t> m_ColorAttachments;
- uint32_t m_DepthAttachment = 0;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.cpp b/src/Platform/OpenGL/OpenGLIndexBuffer.cpp
deleted file mode 100644
index 668a898..0000000
--- a/src/Platform/OpenGL/OpenGLIndexBuffer.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "OpenGLIndexBuffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count)
- : m_Count(count)
- {
- glGenBuffers(1, &m_RendererID); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
- }
-
- OpenGLIndexBuffer::~OpenGLIndexBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLIndexBuffer::Bind() const
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
- }
-
- void OpenGLIndexBuffer::Unbind() const
- {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLIndexBuffer.h b/src/Platform/OpenGL/OpenGLIndexBuffer.h
deleted file mode 100644
index b3048a3..0000000
--- a/src/Platform/OpenGL/OpenGLIndexBuffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- class OpenGLIndexBuffer
- : public IndexBuffer
- {
- public:
- OpenGLIndexBuffer(const uint32_t* indices, uint32_t count);
- virtual ~OpenGLIndexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
- virtual uint32_t GetCount() const override { return m_Count; }
-
- private:
- uint32_t m_RendererID;
- uint32_t m_Count;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.h b/src/Platform/OpenGL/OpenGLRendererAPI.h
deleted file mode 100644
index 5a68622..0000000
--- a/src/Platform/OpenGL/OpenGLRendererAPI.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include "Core/Log.h"
-
-#include "Rendering/Renderer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLRendererAPI
- : public RendererAPI
- {
- public:
- virtual void Init() override;
- virtual void SetViewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual void SetClearColor(const glm::vec4& color) override;
- virtual void Clear() override;
- virtual void EnableDepthTest() override;
- virtual void DisableDepthTest() override;
- virtual void SetFaceCulling(bool enabled) override;
- virtual void EnableBlending() override;
- virtual void DisableBlending() override;
-
- virtual void DrawIndexed(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
-
- virtual void DrawArrays(uint32_t vertexCount,
- uint32_t first = 0) override;
- virtual void DrawLines(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
- virtual void BindTexture(uint32_t textureID,
- uint32_t slot = 0) override;
- virtual void BindImageTexture(uint32_t textureID,
- uint32_t slot = 0,
- bool readOnly = false) override;
- virtual void ReadPixels(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height,
- uint32_t format, uint32_t type,
- void* pixels) override;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLShader.cpp b/src/Platform/OpenGL/OpenGLShader.cpp
deleted file mode 100644
index 5d6dd66..0000000
--- a/src/Platform/OpenGL/OpenGLShader.cpp
+++ /dev/null
@@ -1,302 +0,0 @@
-#include "OpenGLShader.h"
-
-#include <glad/glad.h>
-#include <glm/gtc/type_ptr.hpp>
-
-#include <fstream>
-#include <iostream>
-
-namespace Donut
-{
- static uint32_t ShaderTypeFromString(const std::string& type)
- {
- if (type == "vertex")
- return GL_VERTEX_SHADER;
- if (type == "fragment" || type == "pixel")
- return GL_FRAGMENT_SHADER;
- if (type == "compute")
- return GL_COMPUTE_SHADER;
- return 0;
- }
-
- // Shaders are authored in Slang and compiled to Assets/Shaders/generated/
- // <name>.glsl by Tools/compile-shaders.sh. Given a legacy ".../<name>.glsl"
- // path, prefer that generated file when present; otherwise fall back to the
- // hand-written GLSL (e.g. shaders not yet ported to Slang).
- static std::string ResolveShaderPath(const std::string& filepath)
- {
- size_t slash = filepath.find_last_of("/\\");
- std::string dir = (slash == std::string::npos) ? std::string() : filepath.substr(0, slash + 1);
- std::string file = (slash == std::string::npos) ? filepath : filepath.substr(slash + 1);
- size_t dot = file.rfind('.');
- std::string base = (dot == std::string::npos) ? file : file.substr(0, dot);
-
- std::string generated = dir + "generated/" + base + ".glsl";
- std::ifstream test(generated);
- if (test.good())
- return generated;
- return filepath;
- }
-
- OpenGLShader::OpenGLShader(const std::string& filepath)
- {
- std::string resolved = ResolveShaderPath(filepath);
- m_IsSlang = (resolved != filepath);
- std::string source = ReadFile(resolved);
- auto shaderSources = PreProcess(source);
- Compile(shaderSources);
-
- auto lastSlash = filepath.find_last_of("/\\");
- lastSlash = lastSlash == std::string::npos ? 0 : lastSlash + 1;
- auto lastDot = filepath.rfind('.');
- auto count = lastDot == std::string::npos ? filepath.size() - lastSlash : lastDot - lastSlash;
- m_Name = filepath.substr(lastSlash, count);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc)
- : m_Name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_VERTEX_SHADER] = vertexSrc;
- sources[GL_FRAGMENT_SHADER] = fragmentSrc;
- Compile(sources);
- }
-
- OpenGLShader::OpenGLShader(const std::string& name, const std::string& computeSrc)
- : m_Name(name)
- {
- std::unordered_map<uint32_t, std::string> sources;
- sources[GL_COMPUTE_SHADER] = computeSrc;
- Compile(sources);
- }
-
- OpenGLShader::~OpenGLShader()
- {
- glDeleteProgram(m_RendererID);
- }
-
- std::string OpenGLShader::ReadFile(const std::string& filepath)
- {
- std::string result;
- std::ifstream in(filepath, std::ios::in |
- std::ios::binary);
-
- if (in)
- {
- in.seekg(0, std::ios::end);
- size_t size = in.tellg();
- if (size != -1)
- {
- result.resize(size);
- in.seekg(0, std::ios::beg);
- in.read(&result[0], size);
- }
- }
- return result;
- }
-
- std::unordered_map<uint32_t, std::string> OpenGLShader::PreProcess(const std::string& source)
- {
- std::unordered_map<uint32_t, std::string> shaderSources;
-
- const char* typeToken = "#type";
- size_t typeTokenLength = strlen(typeToken);
- size_t pos = source.find(typeToken, 0);
-
- while (pos != std::string::npos)
- {
- size_t eol = source.find_first_of("\r\n", pos);
- size_t begin = pos + typeTokenLength + 1;
- std::string type = source.substr(begin, eol - begin);
-
- size_t nextLinePos = source.find_first_not_of("\r\n", eol);
- pos = source.find(typeToken, nextLinePos);
- shaderSources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(nextLinePos) :
- source.substr(nextLinePos, pos - nextLinePos);
- }
-
- return shaderSources;
- }
-
- void OpenGLShader::Compile(const std::unordered_map<uint32_t, std::string>& shaderSources)
- {
- uint32_t program = glCreateProgram();
- std::vector<uint32_t> glShaderIDs(shaderSources.size());
- for (auto& kv : shaderSources)
- {
- uint32_t type = kv.first;
- const std::string& source = kv.second;
-
- uint32_t shader = glCreateShader(type);
- const char* sourceCStr = source.c_str();
- glShaderSource(shader, 1, &sourceCStr, 0);
- glCompileShader(shader);
-
- int isCompiled = 0;
- glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
- if (isCompiled == GL_FALSE)
- {
- int maxLength = 0;
- glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
- std::vector<char> infoLog(maxLength);
- glGetShaderInfoLog(shader, maxLength, &maxLength, &infoLog[0]);
- glDeleteShader(shader);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- glDeleteProgram(program);
- m_RendererID = 0;
- // infoLog.data() is null when the driver returns an empty log
- // (e.g. macOS rejecting a compute shader); streaming a null
- // char* into std::cout calls strlen(NULL) and crashes.
- const char* log = infoLog.empty() ? "" : infoLog.data();
- std::cout << "Shader compilation failure!" << std::endl << log << std::endl;
- return;
- }
- glAttachShader(program, shader);
- glShaderIDs.push_back(shader);
- }
-
- m_RendererID = program;
- glLinkProgram(m_RendererID);
-
- int isLinked = 0;
- glGetProgramiv(m_RendererID, GL_LINK_STATUS, (int*)&isLinked);
- if (isLinked == GL_FALSE)
- {
- int maxLength = 0;
- glGetProgramiv(m_RendererID, GL_INFO_LOG_LENGTH, &maxLength);
- std::vector<char> infoLog(maxLength);
- glGetProgramInfoLog(m_RendererID, maxLength, &maxLength, &infoLog[0]);
- glDeleteProgram(m_RendererID);
- for (auto id : glShaderIDs)
- glDeleteShader(id);
- m_RendererID = 0;
- const char* log = infoLog.empty() ? "" : infoLog.data();
- std::cout << "Shader link failure!" << std::endl << log << std::endl;
- return;
- }
-
- for (auto id : glShaderIDs)
- {
- glDetachShader(m_RendererID, id);
- glDeleteShader(id);
- }
- }
-
- void OpenGLShader::Bind() const
- {
- glUseProgram(m_RendererID);
- }
-
- void OpenGLShader::Unbind() const
- {
- glUseProgram(0);
- }
-
- void OpenGLShader::SetInt(const std::string& name, int value)
- {
- UploadUniformInt(name, value);
- }
-
- void OpenGLShader::SetIntArray(const std::string& name, int* values, uint32_t count)
- {
- UploadUniformIntArray(name, values, count);
- }
-
- void OpenGLShader::SetFloat(const std::string& name, float value)
- {
- UploadUniformFloat(name, value);
- }
-
- void OpenGLShader::SetFloat2(const std::string& name, const glm::vec2& value)
- {
- UploadUniformFloat2(name, value);
- }
-
- void OpenGLShader::SetFloat3(const std::string& name, const glm::vec3& value)
- {
- UploadUniformFloat3(name, value);
- }
-
- void OpenGLShader::SetFloat4(const std::string& name, const glm::vec4& value)
- {
- UploadUniformFloat4(name, value);
- }
-
- void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value)
- {
- UploadUniformMat4(name, value);
- }
-
- void OpenGLShader::UploadUniformInt(const std::string& name, int value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1i(location, value);
- }
-
- void OpenGLShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1iv(location, count, values);
- }
-
- void OpenGLShader::UploadUniformFloat(const std::string& name, float value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform1f(location, value);
- }
-
- void OpenGLShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform2f(location, value.x, value.y);
- }
-
- void OpenGLShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform3f(location, value.x, value.y, value.z);
- }
-
- void OpenGLShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniform4f(location, value.x, value.y, value.z, value.w);
- }
-
- void OpenGLShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniformMatrix3fv(location, 1, m_IsSlang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- void OpenGLShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
- {
- int location = glGetUniformLocation(m_RendererID, name.c_str());
- glUniformMatrix4fv(location, 1, m_IsSlang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
- }
-
- void OpenGLShader::Dispatch(uint32_t x, uint32_t y, uint32_t z)
- {
- // Compute shaders require OpenGL 4.3+. On drivers that cap out earlier
- // (e.g. macOS, which is frozen at 4.1) glDispatchCompute is never
- // loaded and the pointer is null. Guard so we no-op instead of crash.
- if (m_RendererID == 0 || glDispatchCompute == nullptr)
- return;
- glDispatchCompute(x, y, z);
- }
-
- void OpenGLShader::DispatchIndirect(uint32_t offset)
- {
- if (m_RendererID == 0 || glDispatchComputeIndirect == nullptr)
- return;
- glDispatchComputeIndirect(offset);
- }
-
- void OpenGLShader::MemoryBarrier(uint32_t barriers)
- {
- if (glMemoryBarrier == nullptr)
- return;
- glMemoryBarrier(barriers);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLShader.h b/src/Platform/OpenGL/OpenGLShader.h
deleted file mode 100644
index 4b63597..0000000
--- a/src/Platform/OpenGL/OpenGLShader.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#pragma once
-
-#include "Rendering/Shader.h"
-
-#include <unordered_map>
-#include <glm/glm.hpp>
-
-namespace Donut
-{
- class OpenGLShader
- : public Shader
- {
- public:
- OpenGLShader(const std::string& filepath);
- OpenGLShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
- OpenGLShader(const std::string& name, const std::string& computeSrc);
- virtual ~OpenGLShader();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetInt( const std::string& name, int value) override;
- virtual void SetIntArray(const std::string& name, int* values, uint32_t count) override;
- virtual void SetFloat( const std::string& name, float value) override;
- virtual void SetFloat2( const std::string& name, const glm::vec2& value) override;
- virtual void SetFloat3( const std::string& name, const glm::vec3& value) override;
- virtual void SetFloat4( const std::string& name, const glm::vec4& value) override;
- virtual void SetMat4( const std::string& name, const glm::mat4& value) override;
-
- virtual void Dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) override;
- virtual void DispatchIndirect(uint32_t offset = 0) override;
- virtual void MemoryBarrier(uint32_t barriers) override;
-
- virtual const std::string& GetName() const override { return m_Name; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- void UploadUniformInt( const std::string& name, int value);
- void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
- void UploadUniformFloat( const std::string& name, float value);
- void UploadUniformFloat2( const std::string& name, const glm::vec2& value);
- void UploadUniformFloat3( const std::string& name, const glm::vec3& value);
- void UploadUniformFloat4( const std::string& name, const glm::vec4& value);
- void UploadUniformMat3( const std::string& name, const glm::mat3& matrix);
- void UploadUniformMat4( const std::string& name, const glm::mat4& matrix);
-
- private:
- std::string ReadFile(const std::string& filepath);
- std::unordered_map<uint32_t, std::string> PreProcess(const std::string& source);
- void Compile(const std::unordered_map<uint32_t, std::string>& shaderSources);
- private:
- uint32_t m_RendererID = 0;
- std::string m_Name;
- // True when loaded from a Slang-compiled GLSL. Slang expects row-major
- // matrix data, so matrix uniforms are transposed on upload (glm is
- // column-major) to keep all matrix math correct.
- bool m_IsSlang = false;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLTexture.h b/src/Platform/OpenGL/OpenGLTexture.h
deleted file mode 100644
index 220df09..0000000
--- a/src/Platform/OpenGL/OpenGLTexture.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#pragma once
-
-#include "Rendering/Texture.h"
-#include "Core/Log.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLTexture2D
- : public Texture2D
- {
- public:
- OpenGLTexture2D(uint32_t width, uint32_t height);
- OpenGLTexture2D(const std::string& path);
- virtual ~OpenGLTexture2D();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == other.GetRendererID();
- }
-
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- GLenum m_InternalFormat, m_DataFormat;
- };
-
- class OpenGLCubemapTexture
- : public CubemapTexture
- {
- public:
- OpenGLCubemapTexture(uint32_t width, uint32_t height);
- OpenGLCubemapTexture(const std::string& path);
- virtual ~OpenGLCubemapTexture();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == other.GetRendererID();
- }
-
- private:
- void LoadHDRI(const std::string& path);
- void ConvertEquirectangularToCubemap(float* hdrData, int width, int height);
-
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- GLenum m_InternalFormat, m_DataFormat;
- };
-}
diff --git a/src/Platform/OpenGL/OpenGLUniformBuffer.cpp b/src/Platform/OpenGL/OpenGLUniformBuffer.cpp
deleted file mode 100644
index 631b863..0000000
--- a/src/Platform/OpenGL/OpenGLUniformBuffer.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "OpenGLUniformBuffer.h"
-
-namespace Donut
-{
- OpenGLUniformBuffer::OpenGLUniformBuffer(uint32_t size, uint32_t binding)
- : m_Size(size), m_Binding(binding)
- {
- glGenBuffers(1, &m_RendererID);
- glBindBuffer(GL_UNIFORM_BUFFER, m_RendererID);
- glBufferData(GL_UNIFORM_BUFFER, size, nullptr, GL_DYNAMIC_DRAW);
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_RendererID);
- }
-
- OpenGLUniformBuffer::~OpenGLUniformBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLUniformBuffer::SetData(const void* data, uint32_t size, uint32_t offset)
- {
- glBindBuffer(GL_UNIFORM_BUFFER, m_RendererID);
- glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
- }
-
- void OpenGLUniformBuffer::Bind(uint32_t binding)
- {
- glBindBufferBase(GL_UNIFORM_BUFFER, binding, m_RendererID);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLUniformBuffer.h b/src/Platform/OpenGL/OpenGLUniformBuffer.h
deleted file mode 100644
index fd96f37..0000000
--- a/src/Platform/OpenGL/OpenGLUniformBuffer.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include "Rendering/UniformBuffer.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- class OpenGLUniformBuffer : public UniformBuffer
- {
- public:
- OpenGLUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~OpenGLUniformBuffer();
-
- virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override;
- virtual void Bind(uint32_t binding) override;
- private:
- uint32_t m_RendererID = 0;
- uint32_t m_Size = 0;
- uint32_t m_Binding = 0;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexArray.cpp b/src/Platform/OpenGL/OpenGLVertexArray.cpp
deleted file mode 100644
index afe5fe2..0000000
--- a/src/Platform/OpenGL/OpenGLVertexArray.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <glad/glad.h>
-
-#include "OpenGLVertexArray.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- OpenGLVertexArray::OpenGLVertexArray()
- {
- // glCreateVertexArrays is 4.5 DSA; macOS caps at 4.1. glGenVertexArrays
- // reserves the name and the VAO is created on first bind (done below).
- glGenVertexArrays(1, &m_RendererID);
- }
-
- OpenGLVertexArray::~OpenGLVertexArray()
- {
- glDeleteVertexArrays(1, &m_RendererID);
- }
-
- void OpenGLVertexArray::Bind() const
- {
- glBindVertexArray(m_RendererID);
- }
-
- void OpenGLVertexArray::Unbind() const
- {
- glBindVertexArray(0);
- }
-
- void OpenGLVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
- {
- glBindVertexArray(m_RendererID);
- vertexBuffer->Bind();
-
- const auto& layout = vertexBuffer->GetLayout();
- for (const auto& element : layout.GetElements())
- {
- glEnableVertexAttribArray(m_VertexBufferIndex);
- glVertexAttribPointer(m_VertexBufferIndex,
- element.count,
- element.type,
- element.normalized ? GL_TRUE : GL_FALSE,
- layout.GetStride(),
- reinterpret_cast<const void*>(static_cast<uintptr_t>(element.offset)));
- m_VertexBufferIndex++;
- }
-
- m_VertexBuffers.push_back(vertexBuffer);
- }
-
- void OpenGLVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer)
- {
- glBindVertexArray(m_RendererID);
- indexBuffer->Bind();
- m_IndexBuffer = indexBuffer;
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexArray.h b/src/Platform/OpenGL/OpenGLVertexArray.h
deleted file mode 100644
index b6ae82f..0000000
--- a/src/Platform/OpenGL/OpenGLVertexArray.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-
-#include "Rendering/VertexArray.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.h"
-
-#include <vector>
-
-namespace Donut
-{
- class OpenGLVertexArray
- : public VertexArray
- {
- public:
- OpenGLVertexArray();
- virtual ~OpenGLVertexArray();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) override;
- virtual void SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer) override;
-
- virtual const std::vector<Ref<VertexBuffer>>& GetVertexBuffers() const override
- {
- return m_VertexBuffers;
- }
-
- virtual const Ref<IndexBuffer>& GetIndexBuffer() const override
- {
- return m_IndexBuffer;
- }
- private:
- uint32_t m_RendererID;
- uint32_t m_VertexBufferIndex = 0;
- std::vector<Ref<VertexBuffer>> m_VertexBuffers;
- Ref<IndexBuffer> m_IndexBuffer;
- };
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.cpp b/src/Platform/OpenGL/OpenGLVertexBuffer.cpp
deleted file mode 100644
index 4f5e48f..0000000
--- a/src/Platform/OpenGL/OpenGLVertexBuffer.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "OpenGLVertexBuffer.h"
-#include "Rendering/VertexBuffer.h"
-
-#include <glad/glad.h>
-
-namespace Donut
-{
- OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size)
- {
- glGenBuffers(1, &m_RendererID); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
- }
-
- OpenGLVertexBuffer::~OpenGLVertexBuffer()
- {
- glDeleteBuffers(1, &m_RendererID);
- }
-
- void OpenGLVertexBuffer::Bind() const
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- }
-
- void OpenGLVertexBuffer::Unbind() const
- {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- void OpenGLVertexBuffer::SetData(const void* data, uint32_t size)
- {
- glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
- glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
- }
-};
diff --git a/src/Platform/OpenGL/OpenGLVertexBuffer.h b/src/Platform/OpenGL/OpenGLVertexBuffer.h
deleted file mode 100644
index 6244f7d..0000000
--- a/src/Platform/OpenGL/OpenGLVertexBuffer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexBuffer.h"
-
-namespace Donut
-{
- class OpenGLVertexBuffer
- : public VertexBuffer
- {
- public:
- OpenGLVertexBuffer(const void* data, uint32_t size);
- virtual ~OpenGLVertexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
- virtual void SetData(const void* data, uint32_t size) override;
-
- virtual const VertexBufferLayout& GetLayout() const override { return m_Layout; }
- virtual void SetLayout(const VertexBufferLayout& layout) override { m_Layout = layout; }
-
- private:
- uint32_t m_RendererID;
- VertexBufferLayout m_Layout;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanIndexBuffer.h b/src/Platform/Vulkan/VulkanIndexBuffer.h
deleted file mode 100644
index 74e69cf..0000000
--- a/src/Platform/Vulkan/VulkanIndexBuffer.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "Rendering/IndexBuffer.h"
-
-namespace Donut
-{
- class VulkanIndexBuffer
- : public IndexBuffer
- {
- public:
- VulkanIndexBuffer(uint32_t* indices, uint32_t count);
- virtual ~VulkanIndexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual uint32_t GetCount() const override { return m_Count; }
- private:
- uint32_t m_RendererID;
- uint32_t m_Count;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanRendererAPI.h b/src/Platform/Vulkan/VulkanRendererAPI.h
deleted file mode 100644
index 63a623f..0000000
--- a/src/Platform/Vulkan/VulkanRendererAPI.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include "Rendering/Renderer.h"
-
-namespace Donut
-{
- class VulkanRendererAPI
- : public RendererAPI
- {
- public:
- virtual void Init() override;
- virtual void SetViewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) override;
- virtual void SetClearColor(const glm::vec4& color) override;
- virtual void Clear() override;
- virtual void EnableDepthTest() override;
- virtual void DisableDepthTest() override;
- virtual void SetFaceCulling(bool enabled) override;
- virtual void EnableBlending() override;
- virtual void DisableBlending() override;
-
- virtual void DrawIndexed(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
-
- virtual void DrawArrays(uint32_t vertexCount,
- uint32_t first = 0) override;
- virtual void DrawLines(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) override;
- virtual void BindTexture(uint32_t textureID,
- uint32_t slot = 0) override;
- virtual void BindImageTexture(uint32_t textureID,
- uint32_t slot = 0,
- bool readOnly = false) override;
- virtual void ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) override;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanShader.h b/src/Platform/Vulkan/VulkanShader.h
deleted file mode 100644
index 138a742..0000000
--- a/src/Platform/Vulkan/VulkanShader.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#pragma once
-
-#include "Rendering/Shader.h"
-
-#include <string>
-#include <unordered_map>
-
-namespace Donut
-{
- class VulkanShader
- : public Shader
- {
- public:
- VulkanShader(const std::string& filepath);
- VulkanShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
- VulkanShader(const std::string& name, const std::string& computeSrc);
- virtual ~VulkanShader();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetInt(const std::string& name, int value) override;
- virtual void SetIntArray(const std::string& name, int* values, uint32_t count) override;
- virtual void SetFloat(const std::string& name, float value) override;
- virtual void SetFloat2(const std::string& name, const glm::vec2& value) override;
- virtual void SetFloat3(const std::string& name, const glm::vec3& value) override;
- virtual void SetFloat4(const std::string& name, const glm::vec4& value) override;
- virtual void SetMat4(const std::string& name, const glm::mat4& value) override;
-
- virtual void Dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) override;
- virtual void DispatchIndirect(uint32_t offset = 0) override;
- virtual void MemoryBarrier(uint32_t barriers) override;
-
- virtual const std::string& GetName() const override { return m_Name; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- void UploadUniformInt(const std::string& name, int value);
- void UploadUniformIntArray(const std::string& name, int* values, uint32_t count);
- void UploadUniformFloat(const std::string& name, float value);
- void UploadUniformFloat2(const std::string& name, const glm::vec2& value);
- void UploadUniformFloat3(const std::string& name, const glm::vec3& value);
- void UploadUniformFloat4(const std::string& name, const glm::vec4& value);
- void UploadUniformMat3(const std::string& name, const glm::mat3& matrix);
- void UploadUniformMat4(const std::string& name, const glm::mat4& matrix);
- private:
- std::string ReadFile(const std::string& filepath);
- std::unordered_map<uint32_t, std::string> PreProcess(const std::string& source);
- void Compile(const std::unordered_map<uint32_t, std::string>& shaderSources);
- uint32_t m_RendererID;
- std::string m_Name;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanTexture.cpp b/src/Platform/Vulkan/VulkanTexture.cpp
deleted file mode 100644
index 4145130..0000000
--- a/src/Platform/Vulkan/VulkanTexture.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "VulkanTexture.h"
-
-namespace Donut
-{
- VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanTexture2D::VulkanTexture2D(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1;
- m_Height = 1;
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanTexture2D::~VulkanTexture2D()
- {
- }
-
- void VulkanTexture2D::SetData(void* data, uint32_t size)
- {
- }
-
- void VulkanTexture2D::Bind(uint32_t slot) const
- {
- }
-
- void VulkanTexture2D::BindAsImage(uint32_t slot, bool readOnly) const
- {
- }
-
- // Vulkan Cubemap Implementation (Placeholder)
- VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
- {
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path)
- : m_Path(path)
- {
- m_Width = 1024;
- m_Height = 1024;
- m_InternalFormat = 0;
- m_DataFormat = 0;
- m_RendererID = 0;
- }
-
- VulkanCubemapTexture::~VulkanCubemapTexture()
- {
- }
-
- void VulkanCubemapTexture::SetData(void* data, uint32_t size)
- {
- }
-
- void VulkanCubemapTexture::Bind(uint32_t slot) const
- {
- }
-
- void VulkanCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
- {
- }
-};
diff --git a/src/Platform/Vulkan/VulkanTexture.h b/src/Platform/Vulkan/VulkanTexture.h
deleted file mode 100644
index dd9e1df..0000000
--- a/src/Platform/Vulkan/VulkanTexture.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#pragma once
-
-#include "Rendering/Texture.h"
-
-namespace Donut
-{
- class VulkanTexture2D
- : public Texture2D
- {
- public:
- VulkanTexture2D(uint32_t width, uint32_t height);
- VulkanTexture2D(const std::string& path);
- virtual ~VulkanTexture2D();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == ((VulkanTexture2D&)other).m_RendererID;
- }
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- uint32_t m_InternalFormat, m_DataFormat;
- };
-
- class VulkanCubemapTexture
- : public CubemapTexture
- {
- public:
- VulkanCubemapTexture(uint32_t width, uint32_t height);
- VulkanCubemapTexture(const std::string& path);
- virtual ~VulkanCubemapTexture();
-
- virtual uint32_t GetWidth() const override { return m_Width; }
- virtual uint32_t GetHeight() const override { return m_Height; }
- virtual uint32_t GetRendererID() const override { return m_RendererID; }
-
- virtual void SetData(void* data, uint32_t size) override;
- virtual void Bind(uint32_t slot = 0) const override;
- virtual void BindAsImage(uint32_t slot = 0, bool readOnly = false) const override;
-
- virtual bool operator==(const Texture& other) const override
- {
- return m_RendererID == ((VulkanCubemapTexture&)other).m_RendererID;
- }
- private:
- std::string m_Path;
- uint32_t m_Width, m_Height;
- uint32_t m_RendererID;
- uint32_t m_InternalFormat, m_DataFormat;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanUniformBuffer.h b/src/Platform/Vulkan/VulkanUniformBuffer.h
deleted file mode 100644
index bbf152d..0000000
--- a/src/Platform/Vulkan/VulkanUniformBuffer.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "Rendering/UniformBuffer.h"
-
-namespace Donut
-{
- class VulkanUniformBuffer : public UniformBuffer
- {
- public:
- VulkanUniformBuffer(uint32_t size, uint32_t binding);
- virtual ~VulkanUniformBuffer();
-
- virtual void SetData(const void* data, uint32_t size, uint32_t offset = 0) override;
- virtual void Bind(uint32_t binding) override;
- private:
- uint32_t m_Size;
- uint32_t m_Binding;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanVertexArray.h b/src/Platform/Vulkan/VulkanVertexArray.h
deleted file mode 100644
index e4484e5..0000000
--- a/src/Platform/Vulkan/VulkanVertexArray.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexArray.h"
-#include "Core/Memory.h"
-
-namespace Donut
-{
- class VulkanVertexArray
- : public VertexArray
- {
- public:
- VulkanVertexArray();
- virtual ~VulkanVertexArray();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) override;
- virtual void SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer) override;
-
- virtual const std::vector<Ref<VertexBuffer>>& GetVertexBuffers() const { return m_VertexBuffers; }
- virtual const Ref<IndexBuffer>& GetIndexBuffer() const { return m_IndexBuffer; }
- private:
- uint32_t m_RendererID;
- std::vector<Ref<VertexBuffer>> m_VertexBuffers;
- Ref<IndexBuffer> m_IndexBuffer;
- };
-};
diff --git a/src/Platform/Vulkan/VulkanVertexBuffer.h b/src/Platform/Vulkan/VulkanVertexBuffer.h
deleted file mode 100644
index e809e44..0000000
--- a/src/Platform/Vulkan/VulkanVertexBuffer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-#pragma once
-
-#include "Rendering/VertexBuffer.h"
-
-namespace Donut
-{
- class VulkanVertexBuffer
- : public VertexBuffer
- {
- public:
- VulkanVertexBuffer(uint32_t size);
- VulkanVertexBuffer(float* vertices, uint32_t size);
- virtual ~VulkanVertexBuffer();
-
- virtual void Bind() const override;
- virtual void Unbind() const override;
-
- virtual void SetData(const void* data, uint32_t size) override;
-
- virtual const VertexBufferLayout& GetLayout() const override { return m_Layout; }
- virtual void SetLayout(const VertexBufferLayout& layout) override { m_Layout = layout; }
- private:
- uint32_t m_RendererID;
- VertexBufferLayout m_Layout;
- };
-};
diff --git a/src/Rendering/Framebuffer.cpp b/src/Rendering/Framebuffer.cpp
index 1e95c6d..089c31e 100644
--- a/src/Rendering/Framebuffer.cpp
+++ b/src/Rendering/Framebuffer.cpp
@@ -1,15 +1,15 @@
-#include "Framebuffer.h"
-#include "Renderer.h"
+#include "framebuffer.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLFramebuffer.h"
+#include "platform/opengl/opengl_framebuffer.h"
namespace Donut
{
- Ref<Framebuffer> Framebuffer::Create(const FramebufferSpecification& spec)
+ auto Framebuffer::create(const FramebufferSpecification& spec) -> Ref<Framebuffer>
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
- case RendererAPI::API::OpenGL: return CreateRef<OpenGLFramebuffer>(spec);
+ case RendererAPI::API::OpenGL: return create_ref<OpenGLFramebuffer>(spec);
}
return nullptr;
diff --git a/src/Rendering/Framebuffer.h b/src/Rendering/Framebuffer.h
index e4c1481..a01a81b 100644
--- a/src/Rendering/Framebuffer.h
+++ b/src/Rendering/Framebuffer.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Core/Memory.h"
-#include "Texture.h"
+#include "core/memory.h"
+#include "texture.h"
namespace Donut
{
@@ -21,24 +21,24 @@ namespace Donut
{
FramebufferTextureSpecification() = default;
FramebufferTextureSpecification(FramebufferTextureFormat format)
- : TextureFormat(format) {}
+ : texture_format(format) {}
- FramebufferTextureFormat TextureFormat = FramebufferTextureFormat::None;
+ FramebufferTextureFormat texture_format = FramebufferTextureFormat::None;
};
struct FramebufferAttachmentSpecification
{
FramebufferAttachmentSpecification() = default;
FramebufferAttachmentSpecification(std::initializer_list<FramebufferTextureSpecification> attachments)
- : Attachments(attachments) {}
+ : attachments(attachments) {}
- std::vector<FramebufferTextureSpecification> Attachments;
+ std::vector<FramebufferTextureSpecification> attachments;
};
struct FramebufferSpecification
{
uint32_t Width = 0, Height = 0;
- FramebufferAttachmentSpecification Attachments;
+ FramebufferAttachmentSpecification attachments;
uint32_t Samples = 1;
bool SwapChainTarget = false;
@@ -49,17 +49,17 @@ namespace Donut
public:
virtual ~Framebuffer() = default;
- virtual void Bind() = 0;
- virtual void Unbind() = 0;
+ virtual auto bind() -> void = 0;
+ virtual auto unbind() -> void = 0;
- virtual void Resize(uint32_t width, uint32_t height) = 0;
- virtual int ReadPixel(uint32_t attachmentIndex, int x, int y) = 0;
+ virtual auto resize(uint32_t width, uint32_t height) -> void = 0;
+ virtual auto read_pixel(uint32_t attachment_index, int x, int y) -> int = 0;
- virtual void ClearAttachment(uint32_t attachmentIndex, int value) = 0;
- virtual uint32_t GetColorAttachmentRendererID(uint32_t index = 0) const = 0;
+ virtual auto clear_attachment(uint32_t attachment_index, int value) -> void = 0;
+ virtual auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t = 0;
- virtual const FramebufferSpecification& GetSpecification() const = 0;
+ virtual auto get_specification() const -> const FramebufferSpecification& = 0;
- static Ref<Framebuffer> Create(const FramebufferSpecification& spec);
+ static auto create(const FramebufferSpecification& spec) -> Ref<Framebuffer>;
};
};
diff --git a/src/Rendering/IndexBuffer.h b/src/Rendering/IndexBuffer.h
deleted file mode 100644
index a58cc6c..0000000
--- a/src/Rendering/IndexBuffer.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <cstdint>
-
-namespace Donut
-{
- class IndexBuffer
- {
- public:
- virtual ~IndexBuffer() = default;
-
- virtual void Bind() const = 0;
- virtual void Unbind() const = 0;
- virtual uint32_t GetCount() const = 0;
-
- static IndexBuffer* Create(const uint32_t* indices, uint32_t count);
- };
-};
diff --git a/src/Rendering/Renderer.cpp b/src/Rendering/Renderer.cpp
index c0684cb..28b7962 100644
--- a/src/Rendering/Renderer.cpp
+++ b/src/Rendering/Renderer.cpp
@@ -1,64 +1,64 @@
-#include "Renderer.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLRendererAPI.h"
-#include "Platform/Vulkan/VulkanRendererAPI.h"
+#include "platform/opengl/opengl_renderer_api.h"
+#include "platform/vulkan/vulkan_renderer_api.h"
#include <glm/gtc/matrix_transform.hpp>
namespace Donut
{
- Scope<RendererAPI> RendererAPI::Create()
+ auto RendererAPI::create() -> Scope<RendererAPI>
{
- switch (s_API)
+ switch (s_api)
{
case API::OpenGL:
- return CreateScope<OpenGLRendererAPI>();
+ return create_scope<OpenGLRendererAPI>();
case API::Vulkan:
- return CreateScope<VulkanRendererAPI>();
+ return create_scope<VulkanRendererAPI>();
default:
return nullptr;
}
}
- RendererAPI::API RendererAPI::s_API = RendererAPI::API::OpenGL;
+ RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
- void Renderer::Init()
+ auto Renderer::init() -> void
{
- RenderCommand::Init();
- RenderCommand::EnableDepthTest();
+ RenderCommand::init();
+ RenderCommand::enable_depth_test();
}
- void Renderer::Shutdown()
+ auto Renderer::shutdown() -> void
{
}
- void Renderer::OnWindowResize(uint32_t width, uint32_t height)
+ auto Renderer::on_window_resize(uint32_t width, uint32_t height) -> void
{
- RenderCommand::SetViewport(0, 0, width, height);
+ RenderCommand::set_viewport(0, 0, width, height);
}
- void Renderer::Submit(const Ref<Shader>& shader,
- const Ref<VertexArray>& vertexArray,
+ auto Renderer::submit(const Ref<Shader>& shader,
+ const Ref<VertexArray>& vertex_array,
const glm::mat4& transform,
- const glm::mat4& viewProjection)
+ const glm::mat4& view_projection) -> void
{
- shader->Bind();
- shader->SetMat4("u_ViewProjection", viewProjection);
- shader->SetMat4("u_Transform", transform);
+ shader->bind();
+ shader->set_mat4("u_ViewProjection", view_projection);
+ shader->set_mat4("u_Transform", transform);
- vertexArray->Bind();
- RenderCommand::DrawIndexed(vertexArray);
+ vertex_array->bind();
+ RenderCommand::draw_indexed(vertex_array);
}
- Scope<RendererAPI> RenderCommand::s_RendererAPI = RendererAPI::Create();
+ Scope<RendererAPI> RenderCommand::s_renderer_api = RendererAPI::create();
- void Renderer::SetClearColor(const glm::vec4& color)
+ auto Renderer::set_clear_color(const glm::vec4& color) -> void
{
- RenderCommand::SetClearColor(color);
+ RenderCommand::set_clear_color(color);
}
- void Renderer::Clear()
+ auto Renderer::clear() -> void
{
- RenderCommand::Clear();
+ RenderCommand::clear();
}
};
diff --git a/src/Rendering/Renderer.h b/src/Rendering/Renderer.h
index 6e87411..189723d 100644
--- a/src/Rendering/Renderer.h
+++ b/src/Rendering/Renderer.h
@@ -1,156 +1,158 @@
#pragma once
-#include "Core/Memory.h"
-#include "VertexArray.h"
-#include "Shader.h"
-#include "Framebuffer.h"
+#include "core/memory.h"
+#include "vertex_array.h"
+#include "shader.h"
+#include "framebuffer.h"
#include <glm/glm.hpp>
namespace Donut
{
- class RendererAPI
+ class RendererAPI
{
public:
- enum class API
+ enum class API
{
- None = 0,
+ None = 0,
OpenGL = 1,
Vulkan = 2,
};
+
public:
virtual ~RendererAPI() = default;
- virtual void Init() = 0;
- virtual void SetViewport(uint32_t x, uint32_t y,
- uint32_t width, uint32_t height) = 0;
- virtual void SetClearColor(const glm::vec4& color) = 0;
- virtual void Clear() = 0;
- virtual void EnableDepthTest() = 0;
- virtual void DisableDepthTest() = 0;
- virtual void SetFaceCulling(bool enabled) = 0;
- virtual void EnableBlending() = 0;
- virtual void DisableBlending() = 0;
+ virtual auto init() -> void = 0;
+ virtual auto set_viewport(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height) -> void = 0;
+ virtual auto set_clear_color(const glm::vec4& color) -> void = 0;
+ virtual auto clear() -> void = 0;
+ virtual auto enable_depth_test() -> void = 0;
+ virtual auto disable_depth_test() -> void = 0;
+ virtual auto set_face_culling(bool enabled) -> void = 0;
+ virtual auto enable_blending() -> void = 0;
+ virtual auto disable_blending() -> void = 0;
+
+ virtual auto draw_indexed(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) -> void = 0;
+
+ virtual auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void = 0;
+ virtual auto draw_lines(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) -> void = 0;
+ virtual auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void = 0;
+ virtual auto bind_image_texture(uint32_t texture_id,
+ uint32_t slot = 0,
+ bool read_only = false) -> void = 0;
+ virtual auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void = 0;
- virtual void DrawIndexed(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) = 0;
-
- virtual void DrawArrays(uint32_t vertexCount, uint32_t first = 0) = 0;
- virtual void DrawLines(const Ref<VertexArray>& vertexArray,
- uint32_t indexCount = 0) = 0;
- virtual void BindTexture(uint32_t textureID, uint32_t slot = 0) = 0;
- virtual void BindImageTexture(uint32_t textureID,
- uint32_t slot = 0,
- bool readOnly = false) = 0;
- virtual void ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels) = 0;
+ inline static auto get_api() -> API { return s_api; }
+ inline static auto set_api(API api) -> void { s_api = api; }
+ static auto create() -> Scope<RendererAPI>;
- inline static API GetAPI() { return s_API; }
- inline static void SetAPI(API api) { s_API = api; }
- static Scope<RendererAPI> Create();
private:
- static API s_API;
+ static API s_api;
};
- class RenderCommand
+ class RenderCommand
{
public:
- inline static void Init()
+ inline static auto init() -> void
{
- s_RendererAPI->Init();
+ s_renderer_api->init();
}
- inline static void SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+ inline static auto set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
{
- s_RendererAPI->SetViewport(x, y, width, height);
+ s_renderer_api->set_viewport(x, y, width, height);
}
- inline static void SetClearColor(const glm::vec4& color)
+ inline static auto set_clear_color(const glm::vec4& color) -> void
{
- s_RendererAPI->SetClearColor(color);
+ s_renderer_api->set_clear_color(color);
}
- inline static void Clear()
+ inline static auto clear() -> void
{
- s_RendererAPI->Clear();
+ s_renderer_api->clear();
}
- inline static void EnableDepthTest()
+ inline static auto enable_depth_test() -> void
{
- s_RendererAPI->EnableDepthTest();
+ s_renderer_api->enable_depth_test();
}
- inline static void DisableDepthTest()
+ inline static auto disable_depth_test() -> void
{
- s_RendererAPI->DisableDepthTest();
+ s_renderer_api->disable_depth_test();
}
- inline static void SetFaceCulling(bool enabled)
+ inline static auto set_face_culling(bool enabled) -> void
{
- s_RendererAPI->SetFaceCulling(enabled);
+ s_renderer_api->set_face_culling(enabled);
}
- inline static void EnableBlending()
+ inline static auto enable_blending() -> void
{
- s_RendererAPI->EnableBlending();
+ s_renderer_api->enable_blending();
}
- inline static void DisableBlending()
+ inline static auto disable_blending() -> void
{
- s_RendererAPI->DisableBlending();
+ s_renderer_api->disable_blending();
}
- inline static void DrawIndexed(const Ref<VertexArray>& vertexArray, uint32_t indexCount = 0)
+ inline static auto draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void
{
- s_RendererAPI->DrawIndexed(vertexArray, indexCount);
+ s_renderer_api->draw_indexed(vertex_array, index_count);
}
- inline static void DrawArrays(uint32_t vertexCount, uint32_t first = 0)
+ inline static auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void
{
- s_RendererAPI->DrawArrays(vertexCount, first);
+ s_renderer_api->draw_arrays(vertex_count, first);
}
- inline static void DrawLines(const Ref<VertexArray>& vertexArray, uint32_t indexCount = 0)
+ inline static auto draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void
{
- s_RendererAPI->DrawLines(vertexArray, indexCount);
+ s_renderer_api->draw_lines(vertex_array, index_count);
}
- inline static void BindTexture(uint32_t textureID, uint32_t slot = 0)
+ inline static auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void
{
- s_RendererAPI->BindTexture(textureID, slot);
+ s_renderer_api->bind_texture(texture_id, slot);
}
- inline static void BindImageTexture(uint32_t textureID, uint32_t slot = 0, bool readOnly = false)
+ inline static auto bind_image_texture(uint32_t texture_id, uint32_t slot = 0, bool read_only = false) -> void
{
- s_RendererAPI->BindImageTexture(textureID, slot, readOnly);
+ s_renderer_api->bind_image_texture(texture_id, slot, read_only);
}
- inline static void ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels)
+ inline static auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void
{
- s_RendererAPI->ReadPixels(x, y, width, height, format, type, pixels);
+ s_renderer_api->read_pixels(x, y, width, height, format, type, pixels);
}
private:
- static Scope<RendererAPI> s_RendererAPI;
+ static Scope<RendererAPI> s_renderer_api;
};
- class Renderer
+ class Renderer
{
public:
- static void Init();
- static void Shutdown();
+ static auto init() -> void;
+ static auto shutdown() -> void;
- static void OnWindowResize(uint32_t width, uint32_t height);
+ static auto on_window_resize(uint32_t width, uint32_t height) -> void;
- static void Submit(const Ref<Shader>& shader,
- const Ref<VertexArray>& vertexArray,
+ static auto submit(const Ref<Shader>& shader,
+ const Ref<VertexArray>& vertex_array,
const glm::mat4& transform,
- const glm::mat4& viewProjection);
+ const glm::mat4& view_projection) -> void;
- static void SetClearColor(const glm::vec4& color);
- static void Clear();
+ static auto set_clear_color(const glm::vec4& color) -> void;
+ static auto clear() -> void;
- inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); }
+ inline static auto get_api() -> RendererAPI::API { return RendererAPI::get_api(); }
};
};
diff --git a/src/Rendering/Shader.cpp b/src/Rendering/Shader.cpp
index 8266e00..167b855 100644
--- a/src/Rendering/Shader.cpp
+++ b/src/Rendering/Shader.cpp
@@ -1,14 +1,14 @@
-#include "Shader.h"
-#include "Renderer.h"
+#include "shader.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLShader.h"
-#include "Platform/Vulkan/VulkanShader.h"
+#include "platform/opengl/opengl_shader.h"
+#include "platform/vulkan/vulkan_shader.h"
namespace Donut
{
- Shader* Shader::Create(const std::string& filepath)
+ auto Shader::create(const std::string& filepath) -> Shader*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
return new OpenGLShader(filepath);
@@ -19,66 +19,66 @@ namespace Donut
}
}
- Shader* Shader::Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc)
+ auto Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, vertexSrc, fragmentSrc);
+ return new OpenGLShader(name, vertex_src, fragment_src);
case RendererAPI::API::Vulkan:
- return new VulkanShader(name, vertexSrc, fragmentSrc);
+ return new VulkanShader(name, vertex_src, fragment_src);
default:
return nullptr;
}
}
- Shader* Shader::CreateCompute(const std::string& name, const std::string& computeSrc)
+ auto Shader::create_compute(const std::string& name, const std::string& compute_src) -> Shader*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return new OpenGLShader(name, computeSrc);
+ return new OpenGLShader(name, compute_src);
case RendererAPI::API::Vulkan:
- return new VulkanShader(name, computeSrc);
+ return new VulkanShader(name, compute_src);
default:
return nullptr;
}
}
- void ShaderLibrary::Add(const Ref<Shader>& shader)
+ auto ShaderLibrary::add(const Ref<Shader>& shader) -> void
{
- auto& name = shader->GetName();
- Add(name, shader);
+ auto& name = shader->get_name();
+ add(name, shader);
}
- void ShaderLibrary::Add(const std::string& name, const Ref<Shader>& shader)
+ auto ShaderLibrary::add(const std::string& name, const Ref<Shader>& shader) -> void
{
- m_Shaders[name] = shader;
+ m_shaders[name] = shader;
}
- Ref<Shader> ShaderLibrary::Load(const std::string& filepath)
+ auto ShaderLibrary::load(const std::string& filepath) -> Ref<Shader>
{
- auto shader = Ref<Shader>(Shader::Create(filepath));
- Add(shader);
+ auto shader = Ref<Shader>(Shader::create(filepath));
+ add(shader);
return shader;
}
- Ref<Shader> ShaderLibrary::Load(const std::string& name, const std::string& filepath)
+ auto ShaderLibrary::load(const std::string& name, const std::string& filepath) -> Ref<Shader>
{
- auto shader = Ref<Shader>(Shader::Create(filepath));
- Add(name, shader);
+ auto shader = Ref<Shader>(Shader::create(filepath));
+ add(name, shader);
return shader;
}
- Ref<Shader> ShaderLibrary::Get(const std::string& name)
+ auto ShaderLibrary::Get(const std::string& name) -> Ref<Shader>
{
- if (Exists(name))
- return m_Shaders[name];
+ if (exists(name))
+ return m_shaders[name];
return nullptr;
}
- bool ShaderLibrary::Exists(const std::string& name) const
+ auto ShaderLibrary::exists(const std::string& name) const -> bool
{
- return m_Shaders.find(name) != m_Shaders.end();
+ return m_shaders.find(name) != m_shaders.end();
}
};
diff --git a/src/Rendering/Shader.h b/src/Rendering/Shader.h
index 483102d..bc3fd9a 100644
--- a/src/Rendering/Shader.h
+++ b/src/Rendering/Shader.h
@@ -1,6 +1,6 @@
#pragma once
-#include "Core/Memory.h"
+#include "core/memory.h"
#include <string>
#include <unordered_map>
@@ -18,41 +18,41 @@ namespace Donut
public:
virtual ~Shader() = default;
- virtual void Bind() const = 0;
- virtual void Unbind() const = 0;
+ virtual auto bind() const -> void = 0;
+ virtual auto unbind() const -> void = 0;
- virtual void SetInt( const std::string& name, int value) = 0;
- virtual void SetIntArray(const std::string& name, int* values, uint32_t count) = 0;
- virtual void SetFloat( const std::string& name, float value) = 0;
- virtual void SetFloat2( const std::string& name, const glm::vec2& value) = 0;
- virtual void SetFloat3( const std::string& name, const glm::vec3& value) = 0;
- virtual void SetFloat4( const std::string& name, const glm::vec4& value) = 0;
- virtual void SetMat4( const std::string& name, const glm::mat4& value) = 0;
+ virtual auto set_int( const std::string& name, int value) -> void = 0;
+ virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void = 0;
+ virtual auto set_float( const std::string& name, float value) -> void = 0;
+ virtual auto set_float2( const std::string& name, const glm::vec2& value) -> void = 0;
+ virtual auto set_float3( const std::string& name, const glm::vec3& value) -> void = 0;
+ virtual auto set_float4( const std::string& name, const glm::vec4& value) -> void = 0;
+ virtual auto set_mat4( const std::string& name, const glm::mat4& value) -> void = 0;
- virtual void Dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) = 0;
- virtual void DispatchIndirect(uint32_t offset = 0) = 0;
- virtual void MemoryBarrier(uint32_t barriers) = 0;
+ virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void = 0;
+ virtual auto dispatch_indirect(uint32_t offset = 0) -> void = 0;
+ virtual auto memory_barrier(uint32_t barriers) -> void = 0;
- virtual const std::string& GetName() const = 0;
- virtual uint32_t GetRendererID() const = 0;
+ virtual auto get_name() const -> const std::string& = 0;
+ virtual auto get_renderer_id() const -> uint32_t = 0;
- static Shader* Create(const std::string& filepath);
- static Shader* Create(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc);
- static Shader* CreateCompute(const std::string& name, const std::string& computeSrc);
+ static auto create(const std::string& filepath) -> Shader*;
+ static auto create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*;
+ static auto create_compute(const std::string& name, const std::string& compute_src) -> Shader*;
};
class ShaderLibrary
{
public:
- void Add(const Ref<Shader>& shader);
- void Add(const std::string& name, const Ref<Shader>& shader);
- Ref<Shader> Load(const std::string& filepath);
- Ref<Shader> Load(const std::string& name, const std::string& filepath);
+ auto add(const Ref<Shader>& shader) -> void;
+ auto add(const std::string& name, const Ref<Shader>& shader) -> void;
+ auto load(const std::string& filepath) -> Ref<Shader>;
+ auto load(const std::string& name, const std::string& filepath) -> Ref<Shader>;
Ref<Shader> Get(const std::string& name);
- bool Exists(const std::string& name) const;
+ auto exists(const std::string& name) const -> bool;
private:
- std::unordered_map<std::string, Ref<Shader>> m_Shaders;
+ std::unordered_map<std::string, Ref<Shader>> m_shaders;
};
};
diff --git a/src/Rendering/Texture.cpp b/src/Rendering/Texture.cpp
index 93b8a75..2470e46 100644
--- a/src/Rendering/Texture.cpp
+++ b/src/Rendering/Texture.cpp
@@ -1,19 +1,19 @@
-#include "Texture.h"
-#include "Renderer.h"
+#include "texture.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLTexture.h"
-#include "Platform/Vulkan/VulkanTexture.h"
+#include "platform/opengl/opengl_texture.h"
+#include "platform/vulkan/vulkan_texture.h"
namespace Donut
{
- Ref<Texture2D> Texture2D::Create(uint32_t width, uint32_t height)
+ auto Texture2D::create(uint32_t width, uint32_t height) -> Ref<Texture2D>
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return CreateRef<OpenGLTexture2D>(width, height);
+ return create_ref<OpenGLTexture2D>(width, height);
case RendererAPI::API::Vulkan:
- return CreateRef<VulkanTexture2D>(width, height);
+ return create_ref<VulkanTexture2D>(width, height);
case RendererAPI::API::None:
return nullptr;
default:
@@ -21,14 +21,14 @@ namespace Donut
}
}
- Ref<Texture2D> Texture2D::Create(const std::string& path)
+ auto Texture2D::create(const std::string& path) -> Ref<Texture2D>
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return CreateRef<OpenGLTexture2D>(path);
+ return create_ref<OpenGLTexture2D>(path);
case RendererAPI::API::Vulkan:
- return CreateRef<VulkanTexture2D>(path);
+ return create_ref<VulkanTexture2D>(path);
case RendererAPI::API::None:
return nullptr;
default:
@@ -36,14 +36,14 @@ namespace Donut
}
}
- Ref<CubemapTexture> CubemapTexture::Create(uint32_t width, uint32_t height)
+ auto CubemapTexture::create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return CreateRef<OpenGLCubemapTexture>(width, height);
+ return create_ref<OpenGLCubemapTexture>(width, height);
case RendererAPI::API::Vulkan:
- return CreateRef<VulkanCubemapTexture>(width, height);
+ return create_ref<VulkanCubemapTexture>(width, height);
case RendererAPI::API::None:
return nullptr;
default:
@@ -51,14 +51,14 @@ namespace Donut
}
}
- Ref<CubemapTexture> CubemapTexture::CreateFromHDRI(const std::string& path)
+ auto CubemapTexture::create_from_hdri(const std::string& path) -> Ref<CubemapTexture>
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
- return CreateRef<OpenGLCubemapTexture>(path);
+ return create_ref<OpenGLCubemapTexture>(path);
case RendererAPI::API::Vulkan:
- return CreateRef<VulkanCubemapTexture>(path);
+ return create_ref<VulkanCubemapTexture>(path);
case RendererAPI::API::None:
return nullptr;
default:
diff --git a/src/Rendering/Texture.h b/src/Rendering/Texture.h
index 3e53b2d..e9b3e0a 100644
--- a/src/Rendering/Texture.h
+++ b/src/Rendering/Texture.h
@@ -1,6 +1,6 @@
#pragma once
-#include "Core/Memory.h"
+#include "core/memory.h"
#include <string>
#include <glm/glm.hpp>
@@ -12,14 +12,14 @@ namespace Donut
public:
virtual ~Texture() = default;
- virtual uint32_t GetWidth() const = 0;
- virtual uint32_t GetHeight() const = 0;
- virtual uint32_t GetRendererID() const = 0;
+ virtual auto get_width() const -> uint32_t = 0;
+ virtual auto get_height() const -> uint32_t = 0;
+ virtual auto get_renderer_id() const -> uint32_t = 0;
- virtual void SetData(void* data, uint32_t size) = 0;
- virtual void Bind(uint32_t slot = 0) const = 0;
- virtual void BindAsImage(uint32_t slot = 0,
- bool readOnly = false) const = 0;
+ virtual auto set_data(void* data, uint32_t size) -> void = 0;
+ virtual auto bind(uint32_t slot = 0) const -> void = 0;
+ virtual void bind_as_image(uint32_t slot = 0,
+ bool read_only = false) const = 0;
virtual bool operator==(const Texture& other) const = 0;
};
@@ -28,15 +28,15 @@ namespace Donut
: public Texture
{
public:
- static Ref<Texture2D> Create(uint32_t width, uint32_t height);
- static Ref<Texture2D> Create(const std::string& path);
+ static auto create(uint32_t width, uint32_t height) -> Ref<Texture2D>;
+ static auto create(const std::string& path) -> Ref<Texture2D>;
};
class CubemapTexture
: public Texture
{
public:
- static Ref<CubemapTexture> Create(uint32_t width, uint32_t height);
- static Ref<CubemapTexture> CreateFromHDRI(const std::string& path);
+ static auto create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>;
+ static auto create_from_hdri(const std::string& path) -> Ref<CubemapTexture>;
};
}; \ No newline at end of file
diff --git a/src/Rendering/TextureManager.cpp b/src/Rendering/TextureManager.cpp
deleted file mode 100644
index 9110558..0000000
--- a/src/Rendering/TextureManager.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "TextureManager.h"
-#include "Platform/OpenGL/OpenGLTexture.h"
-#include "Platform/OpenGL/OpenGLRendererAPI.h"
-#include <glad/glad.h>
-
-namespace Donut
-{
- Ref<Texture2D> TextureManager::CreateTexture(uint32_t width, uint32_t height)
- {
- return CreateRef<OpenGLTexture2D>(width, height);
- }
-
- void TextureManager::BindTexture(uint32_t textureID, uint32_t slot)
- {
- RenderCommand::BindTexture(textureID, slot);
- }
-
- void TextureManager::BindImageTexture(uint32_t textureID, uint32_t slot, bool readOnly)
- {
- RenderCommand::BindImageTexture(textureID, slot, readOnly);
- }
-
- void TextureManager::SetTextureData(uint32_t textureID, void* data, uint32_t width, uint32_t height)
- {
- glBindTexture(GL_TEXTURE_2D, textureID);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
- }
-
- void TextureManager::ResizeTexture(uint32_t textureID, uint32_t width, uint32_t height)
- {
- glBindTexture(GL_TEXTURE_2D, textureID);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- }
-};
diff --git a/src/Rendering/TextureManager.h b/src/Rendering/TextureManager.h
deleted file mode 100644
index 0ab6c5f..0000000
--- a/src/Rendering/TextureManager.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include "Rendering/Texture.h"
-#include <cstdint>
-
-namespace Donut
-{
- class TextureManager
- {
- public:
- static Ref<Texture2D> CreateTexture(uint32_t width, uint32_t height);
-
- static void BindTexture(uint32_t textureID, uint32_t slot = 0);
- static void BindImageTexture(uint32_t textureID, uint32_t slot = 0, bool readOnly = false);
- static void SetTextureData(uint32_t textureID, void* data, uint32_t width, uint32_t height);
- static void ResizeTexture(uint32_t textureID, uint32_t width, uint32_t height);
- };
-};
diff --git a/src/Rendering/UniformBuffer.cpp b/src/Rendering/UniformBuffer.cpp
deleted file mode 100644
index 16cf04c..0000000
--- a/src/Rendering/UniformBuffer.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "UniformBuffer.h"
-#include "Renderer.h"
-
-#include "Platform/OpenGL/OpenGLUniformBuffer.h"
-#include "Platform/Vulkan/VulkanUniformBuffer.h"
-
-namespace Donut
-{
- Ref<UniformBuffer> UniformBuffer::Create(uint32_t size, uint32_t binding)
- {
- switch (Renderer::GetAPI())
- {
- case RendererAPI::API::OpenGL:
- return CreateRef<OpenGLUniformBuffer>(size, binding);
- case RendererAPI::API::Vulkan:
- return CreateRef<VulkanUniformBuffer>(size, binding);
- case RendererAPI::API::None:
- return nullptr;
- default:
- return nullptr;
- }
- }
-};
diff --git a/src/Rendering/UniformBuffer.h b/src/Rendering/UniformBuffer.h
deleted file mode 100644
index ce68908..0000000
--- a/src/Rendering/UniformBuffer.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-#include <cstdint>
-
-namespace Donut
-{
- class UniformBuffer
- {
- public:
- virtual ~UniformBuffer() = default;
-
- virtual void SetData(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/VertexArray.h b/src/Rendering/VertexArray.h
deleted file mode 100644
index 10fff89..0000000
--- a/src/Rendering/VertexArray.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include "Core/Memory.h"
-
-#include "VertexBuffer.h"
-#include "IndexBuffer.h"
-
-#include <vector>
-
-namespace Donut
-{
- class VertexArray
- {
- public:
- virtual ~VertexArray() = default;
-
- virtual void Bind() const = 0;
- virtual void Unbind() const = 0;
-
- virtual void AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer) = 0;
- virtual void SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer) = 0;
-
- virtual const std::vector<Ref<VertexBuffer>>& GetVertexBuffers() const = 0;
- virtual const Ref<IndexBuffer>& GetIndexBuffer() const = 0;
-
- static VertexArray* Create();
- };
-};
diff --git a/src/Rendering/VertexBuffer.h b/src/Rendering/VertexBuffer.h
deleted file mode 100644
index 35b9a84..0000000
--- a/src/Rendering/VertexBuffer.h
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-namespace Donut
-{
- struct VertexBufferElement
- {
- uint32_t type;
- uint32_t count;
- uint8_t normalized;
- uint32_t offset;
-
- static uint32_t GetSizeOfType(uint32_t type);
- };
-
- class VertexBufferLayout
- {
- public:
- VertexBufferLayout() = default;
- ~VertexBufferLayout() = default;
-
- template<typename T>
- void Push(uint32_t count)
- {
- static_assert(false);
- }
-
- template<>
- void Push<float>(uint32_t count)
- {
- m_Elements.push_back({ 0x1406, count, 0, m_Stride }); // GL_FLOAT
- m_Stride += count * VertexBufferElement::GetSizeOfType(0x1406);
- }
-
- template<>
- void Push<uint32_t>(uint32_t count)
- {
- m_Elements.push_back({ 0x1405, count, 0, m_Stride }); // GL_UNSIGNED_INT
- m_Stride += count * VertexBufferElement::GetSizeOfType(0x1405);
- }
-
- template<>
- void Push<uint8_t>(uint32_t count)
- {
- m_Elements.push_back({ 0x1401, count, 1, m_Stride }); // GL_UNSIGNED_BYTE
- m_Stride += count * VertexBufferElement::GetSizeOfType(0x1401);
- }
-
- inline const std::vector<VertexBufferElement>& GetElements() const { return m_Elements; }
- inline uint32_t GetStride() const { return m_Stride; }
-
- private:
- std::vector<VertexBufferElement> m_Elements;
- uint32_t m_Stride = 0;
- };
-
- class VertexBuffer
- {
- public:
- virtual ~VertexBuffer() = default;
-
- virtual void Bind() const = 0;
- virtual void Unbind() const = 0;
- virtual void SetData(const void* data, uint32_t size) = 0;
-
- virtual const VertexBufferLayout& GetLayout() const = 0;
- virtual void SetLayout(const VertexBufferLayout& layout) = 0;
-
- static VertexBuffer* Create(const void* data, uint32_t size);
- };
-};
diff --git a/src/States/ConfigState.cpp b/src/States/ConfigState.cpp
deleted file mode 100644
index c57bced..0000000
--- a/src/States/ConfigState.cpp
+++ /dev/null
@@ -1,307 +0,0 @@
-#include "ConfigState.h"
-#include "Rendering/Renderer.h"
-#include "Core/Application.h"
-#include "Core/ThemeManager.h"
-#include "Core/SettingsManager.h"
-
-#include <imgui.h>
-#include <imgui_internal.h>
-#include <glad/glad.h>
-
-namespace Donut
-{
- void ConfigState::OnEnter()
- {
- DONUT_INFO("Entering Config State");
-
- const auto& settings = SettingsManager::GetSettingsConst();
-
- m_SelectedAPI = (settings.graphics.renderAPI == "Vulkan") ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL;
- m_SelectedTheme = (settings.graphics.selectedTheme == "Light") ? 1 :
- (settings.graphics.selectedTheme == "Blue") ? 2 : 0;
- m_TargetFPS = settings.simulation.targetFPS;
- m_ComputeHeight = settings.simulation.computeHeight;
- m_MaxStepsMoving = settings.simulation.maxStepsMoving;
- m_MaxStepsStatic = settings.simulation.maxStepsStatic;
- m_EarlyExitDistance = settings.simulation.earlyExitDistance;
- m_GravityEnabled = settings.simulation.gravityEnabled;
- m_VSyncEnabled = settings.graphics.vSyncEnabled;
- m_ShowFPS = settings.graphics.showFPS;
- m_ShowPerformanceMetrics = settings.graphics.showPerformanceMetrics;
- m_ShowDebugInfo = settings.graphics.showDebugInfo;
- m_EnableAntiAliasing = settings.graphics.enableAntiAliasing;
- }
-
- void ConfigState::OnExit()
- {
- DONUT_INFO("Exiting Config State");
- }
-
- void ConfigState::OnUpdate(float deltaTime)
- {
- if (m_ShowRestartMessage)
- {
- m_RestartMessageTimer += deltaTime;
- if (m_RestartMessageTimer > 3.0f)
- {
- m_ShowRestartMessage = false;
- m_RestartMessageTimer = 0.0f;
- }
- }
- }
-
- void ConfigState::OnRender()
- {
- Renderer::SetClearColor({ 0.1f, 0.1f, 0.1f, 1.0f });
- Renderer::Clear();
- }
-
- void ConfigState::OnEvent(Event& event)
- {
- }
-
- void ConfigState::OnImUIRender()
- {
- 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 availableHeight = ImGui::GetWindowHeight() - 140;
-
- ImGui::BeginChild("GraphicsSettings", ImVec2(0, availableHeight), 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* apiNames[] = { "OpenGL", "Vulkan" };
- static int currentAPI = (int)m_SelectedAPI - 1;
-
- if (ImGui::BeginCombo("##RenderAPI", apiNames[currentAPI]))
- {
- for (int i = 0; i < IM_ARRAYSIZE(apiNames); i++)
- {
- const bool isSelected = (currentAPI == i);
- if (ImGui::Selectable(apiNames[i], isSelected))
- {
- currentAPI = i;
- m_SelectedAPI = (RendererAPI::API)(i + 1);
- m_ShowRestartMessage = true;
- m_RestartMessageTimer = 0.0f;
- }
-
- if (isSelected)
- ImGui::SetItemDefaultFocus();
- }
-
- ImGui::EndCombo();
- }
-
- ImGui::Text("Current API: ");
- ImGui::SameLine();
- const char* currentAPIName = (Renderer::GetAPI() == RendererAPI::API::OpenGL) ? "OpenGL" : "Vulkan";
- ImGui::TextColored(ImVec4(0.3f, 0.8f, 0.3f, 1.0f), currentAPIName);
-
- ImGui::Spacing();
-
- ImGui::Checkbox("Enable VSync", &m_VSyncEnabled);
- 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_TargetFPS, 30, 120, "%d FPS");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Target frame rate for the simulation");
-
- ImGui::Checkbox("Show FPS Counter", &m_ShowFPS);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Display current FPS in the simulation");
-
- ImGui::Checkbox("Show Performance Metrics", &m_ShowPerformanceMetrics);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Show detailed performance information");
-
- ImGui::Checkbox("Show Debug Info", &m_ShowDebugInfo);
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Display debug information and statistics");
-
- ImGui::Checkbox("Enable Anti-Aliasing", &m_EnableAntiAliasing);
- 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* themeNames[] = { "Dark", "Light", "Blue" };
- if (ImGui::Combo("UI Theme", &m_SelectedTheme, themeNames, IM_ARRAYSIZE(themeNames)))
- ThemeManager::SetTheme(static_cast<Theme>(m_SelectedTheme));
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Choose the application theme");
-
- ImGui::EndChild();
-
- ImGui::NextColumn();
-
- ImGui::BeginChild("SimulationSettings", ImVec2(0, availableHeight), 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_ComputeHeight, 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_MaxStepsMoving, 1000, 60000, "%d");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Maximum ray marching steps when camera is moving");
-
- ImGui::SliderInt("Max Steps (Static)", &m_MaxStepsStatic, 1000, 30000, "%d");
- if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Maximum ray marching steps when camera is stationary");
-
- ImGui::SliderFloat("Early Exit Distance", &m_EarlyExitDistance, 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_GravityEnabled);
-
- ImGui::EndChild();
-
- ImGui::Columns(1);
-
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::Spacing();
-
- float buttonWidth = (ImGui::GetWindowWidth() - 120) / 5.0f;
-
- if (ImGui::Button("World Builder", ImVec2(buttonWidth, 35)))
- {
- ApplySettings();
- Application::Get().GetStateManager().SwitchToState("WorldBuilder");
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Reset to Defaults", ImVec2(buttonWidth, 35)))
- ResetToDefaults();
-
- ImGui::SameLine();
- if (ImGui::Button("Apply Settings", ImVec2(buttonWidth, 35)))
- ApplySettings();
-
- ImGui::SameLine();
- if (ImGui::Button("Save Settings", ImVec2(buttonWidth, 35)))
- {
- ApplySettings();
- DONUT_INFO("Settings saved manually");
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Exit", ImVec2(buttonWidth, 35)))
- Application::Get().Close();
-
- if (m_ShowRestartMessage)
- {
- 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();
- }
-
- void ConfigState::ApplySettings()
- {
- SimulationSettings simSettings;
- simSettings.targetFPS = m_TargetFPS;
- simSettings.computeHeight = m_ComputeHeight;
- simSettings.maxStepsMoving = m_MaxStepsMoving;
- simSettings.maxStepsStatic = m_MaxStepsStatic;
- simSettings.earlyExitDistance = m_EarlyExitDistance;
- simSettings.gravityEnabled = m_GravityEnabled;
-
- GraphicsSettings gfxSettings;
- gfxSettings.renderAPI = (m_SelectedAPI == RendererAPI::API::Vulkan) ? "Vulkan" : "OpenGL";
- gfxSettings.vSyncEnabled = m_VSyncEnabled;
- gfxSettings.showFPS = m_ShowFPS;
- gfxSettings.showPerformanceMetrics = m_ShowPerformanceMetrics;
- gfxSettings.showDebugInfo = m_ShowDebugInfo;
- gfxSettings.enableAntiAliasing = m_EnableAntiAliasing;
- gfxSettings.selectedTheme = (m_SelectedTheme == 1) ? "Light" :
- (m_SelectedTheme == 2) ? "Blue" : "Dark";
-
- SettingsManager::SetSimulationSettings(simSettings);
- SettingsManager::SetGraphicsSettings(gfxSettings);
-
- RendererAPI::SetAPI(m_SelectedAPI);
- DONUT_INFO("Settings applied and saved");
- }
-
- void ConfigState::ResetToDefaults()
- {
- m_SelectedAPI = RendererAPI::API::OpenGL;
- m_TargetFPS = 60;
- m_ComputeHeight = 512;
- m_MaxStepsMoving = 30000;
- m_MaxStepsStatic = 15000;
- m_EarlyExitDistance = 5e12f;
- m_GravityEnabled = true;
- m_VSyncEnabled = true;
- m_ShowFPS = true;
- m_ShowPerformanceMetrics = true;
- m_ShowDebugInfo = false;
- m_EnableAntiAliasing = true;
- m_SelectedTheme = 0;
-
- ApplySettings();
- DONUT_INFO("Settings reset to defaults and saved");
- }
-};
diff --git a/src/States/ConfigState.h b/src/States/ConfigState.h
deleted file mode 100644
index ad7e238..0000000
--- a/src/States/ConfigState.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;
-
- void OnEnter() override;
- void OnExit() override;
- void OnUpdate(float deltaTime) override;
- void OnRender() override;
- void OnImUIRender() override;
- void OnEvent(Event& event) override;
-
- private:
- void ApplySettings();
- void ResetToDefaults();
-
- private:
- RendererAPI::API m_SelectedAPI = RendererAPI::API::OpenGL;
- bool m_ShowRestartMessage = false;
- float m_RestartMessageTimer = 0.0f;
-
- int m_TargetFPS = 60;
- int m_ComputeHeight = 512;
- int m_MaxStepsMoving = 30000;
- int m_MaxStepsStatic = 15000;
- float m_EarlyExitDistance = 5e12f;
- bool m_GravityEnabled = true;
-
- bool m_VSyncEnabled = true;
- bool m_ShowFPS = true;
- bool m_ShowPerformanceMetrics = true;
- bool m_ShowDebugInfo = false;
- bool m_EnableAntiAliasing = true;
-
- int m_SelectedTheme = 0; // 0=Dark, 1=Light, 2=Blue
- };
-}; \ No newline at end of file
diff --git a/src/States/SimulationState.cpp b/src/States/SimulationState.cpp
deleted file mode 100644
index 12c9397..0000000
--- a/src/States/SimulationState.cpp
+++ /dev/null
@@ -1,386 +0,0 @@
-#include "SimulationState.h"
-#include "Rendering/Renderer.h"
-#include "Core/Application.h"
-#include "Core/HDRIManager.h"
-#include "Core/Window.h"
-#include "Core/Event.h"
-#include "Core/SettingsManager.h"
-
-#include <imgui.h>
-#include <GLFW/glfw3.h>
-#include <chrono>
-#include <iomanip>
-#include <sstream>
-#include <vector>
-
-namespace Donut
-{
- void SimulationState::OnEnter()
- {
- DONUT_INFO("Entering Simulation State");
-
- const auto& settings = SettingsManager::GetSettingsConst();
- auto& engine = Application::Get().GetEngine();
-
- engine.SetTargetFPS(settings.simulation.targetFPS);
- engine.SetComputeHeight(settings.simulation.computeHeight);
- engine.SetMaxStepsMoving(settings.simulation.maxStepsMoving);
- engine.SetMaxStepsStatic(settings.simulation.maxStepsStatic);
- engine.SetEarlyExitDistance(settings.simulation.earlyExitDistance);
- engine.GetGravity() = settings.simulation.gravityEnabled;
-
- engine.SetDiskThickness(settings.simulation.diskThickness);
- engine.SetDiskDensity(settings.simulation.diskDensity);
- engine.SetRotationSpeed(settings.simulation.rotationSpeed);
- engine.SetBlurStrength(settings.simulation.blurStrength);
- engine.SetGlowIntensity(settings.simulation.glowIntensity);
-
- engine.UpdateComputeDimensions();
- m_Initialized = true;
- }
-
- void SimulationState::OnExit()
- {
- DONUT_INFO("Exiting Simulation State");
- }
-
- void SimulationState::OnUpdate(float deltaTime)
- {
- auto& engine = Application::Get().GetEngine();
- engine.UpdatePerformance(deltaTime);
- engine.UpdateWindowDimensions();
- engine.UpdatePhysics(deltaTime);
-
- if (engine.GetCamera().IsDragging())
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
- engine.GetCamera().ProcessOrbitalMouseMove(xpos, ypos);
- }
- }
-
- void SimulationState::OnRender()
- {
- auto& engine = Application::Get().GetEngine();
- RenderCommand::SetClearColor(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
- RenderCommand::Clear();
- RenderCommand::SetViewport(0, 0, static_cast<uint32_t>(engine.GetWidth()), static_cast<uint32_t>(engine.GetHeight()));
-
- engine.DispatchCompute(engine.GetCamera());
- engine.DrawBlurPass();
- }
-
- void SimulationState::OnEvent(Event& event)
- {
- auto& engine = Application::Get().GetEngine();
-
- if (event.GetEventType() == EventType::MouseButtonPressed)
- {
- MouseButtonPressedEvent& e = (MouseButtonPressedEvent&)event;
- int button = e.GetMouseButton();
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- Camera& camera = engine.GetCamera();
- double lastX = camera.GetLastX();
- double lastY = camera.GetLastY();
- glfwGetCursorPos(window, &lastX, &lastY);
-
- camera.ProcessOrbitalMouseButton(button, GLFW_PRESS, 0);
- }
- else if (event.GetEventType() == EventType::MouseButtonReleased)
- {
- MouseButtonReleasedEvent& e = (MouseButtonReleasedEvent&)event;
- int button = e.GetMouseButton();
- engine.GetCamera().ProcessOrbitalMouseButton(button, GLFW_RELEASE, 0);
- }
-
- if (event.GetEventType() == EventType::MouseScrolled)
- {
- MouseScrolledEvent& e = (MouseScrolledEvent&)event;
- engine.GetCamera().ProcessOrbitalScroll(e.GetXOffset(), e.GetYOffset());
- }
-
- if (event.GetEventType() == EventType::KeyPressed)
- {
- KeyPressedEvent& e = (KeyPressedEvent&)event;
- if (e.GetKeyCode() == GLFW_KEY_G)
- {
- engine.GetGravity() = !engine.GetGravity();
- DONUT_INFO("Gravity turned {}", engine.GetGravity() ? "ON" : "OFF");
- }
- }
- }
-
- void SimulationState::OnImUIRender()
- {
- auto& engine = Application::Get().GetEngine();
-
- 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().GetStateManager().SwitchToState("Config");
- ImGui::SameLine();
- if (ImGui::Button("Save Settings"))
- {
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.targetFPS = engine.GetTargetFPS();
- settings.computeHeight = engine.GetComputeHeight();
- settings.maxStepsMoving = engine.GetMaxStepsMoving();
- settings.maxStepsStatic = engine.GetMaxStepsStatic();
- settings.earlyExitDistance = engine.GetEarlyExitDistance();
- settings.gravityEnabled = engine.GetGravity();
- SettingsManager::SetSimulationSettings(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.GetCurrentFPS());
-
- int targetFPS = engine.GetTargetFPS();
- if (ImGui::SliderInt("Target FPS", &targetFPS, 30, 120))
- {
- engine.SetTargetFPS(targetFPS);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.targetFPS = targetFPS;
- SettingsManager::SetSimulationSettings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Simulation Info");
- ImGui::Separator();
-
- ImGui::Text("Resolution: %dx%d", engine.GetWidth(), engine.GetHeight());
- ImGui::Text("Compute Resolution: %dx%d", engine.GetComputeWidth(), engine.GetComputeHeight());
- ImGui::Text("Objects: %zu", engine.GetObjects().size());
-
- if (ImGui::Button("Print Object Info"))
- engine.PrintObjectInfo();
-
- int computeHeight = engine.GetComputeHeight();
- if (ImGui::SliderInt("Compute Height", &computeHeight, 64, 2048))
- {
- engine.SetComputeHeight(computeHeight);
- engine.UpdateComputeDimensions();
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.computeHeight = computeHeight;
- SettingsManager::SetSimulationSettings(settings);
- }
-
- ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Compute Width: %d (auto-calculated)", engine.GetComputeWidth());
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Quality Settings");
- ImGui::Separator();
-
- int maxStepsMoving = engine.GetMaxStepsMoving();
- if (ImGui::SliderInt("Max Steps (Moving)", &maxStepsMoving, 1000, 60000))
- {
- engine.SetMaxStepsMoving(maxStepsMoving);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.maxStepsMoving = maxStepsMoving;
- SettingsManager::SetSimulationSettings(settings);
- }
- int maxStepsStatic = engine.GetMaxStepsStatic();
- if (ImGui::SliderInt("Max Steps (Static)", &maxStepsStatic, 1000, 30000))
- {
- engine.SetMaxStepsStatic(maxStepsStatic);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.maxStepsStatic = maxStepsStatic;
- SettingsManager::SetSimulationSettings(settings);
- }
- float earlyExitDistance = engine.GetEarlyExitDistance();
- if (ImGui::SliderFloat("Early Exit Distance", &earlyExitDistance, 1e11f, 1e13f, "%.2e"))
- {
- engine.SetEarlyExitDistance(earlyExitDistance);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.earlyExitDistance = earlyExitDistance;
- SettingsManager::SetSimulationSettings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Physics");
- ImGui::Separator();
-
- bool& gravity = engine.GetGravity();
- if (ImGui::Checkbox("Gravity Enabled", &gravity))
- {
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.gravityEnabled = gravity;
- SettingsManager::SetSimulationSettings(settings);
- }
-
- ImGui::Spacing();
-
- ImGui::TextColored(ImVec4(0.9f, 0.9f, 1.0f, 1.0f), "Accretion Disk");
- ImGui::Separator();
-
- float diskThickness = engine.GetDiskThickness();
- if (ImGui::SliderFloat("Cloud Thickness", &diskThickness, 0.1f, 2.0f, "%.2f"))
- {
- engine.SetDiskThickness(diskThickness);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.diskThickness = diskThickness;
- SettingsManager::SetSimulationSettings(settings);
- }
- ImGui::TextDisabled("Thickness relative to Schwarzschild radius");
-
- float diskDensity = engine.GetDiskDensity();
- if (ImGui::SliderFloat("Cloud Density", &diskDensity, 0.1f, 3.0f, "%.2f"))
- {
- engine.SetDiskDensity(diskDensity);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.diskDensity = diskDensity;
- SettingsManager::SetSimulationSettings(settings);
- }
- ImGui::TextDisabled("Overall density multiplier");
-
- float rotationSpeed = engine.GetRotationSpeed();
- if (ImGui::SliderFloat("Rotation Speed", &rotationSpeed, 0.0f, 3.0f, "%.2f"))
- {
- engine.SetRotationSpeed(rotationSpeed);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.rotationSpeed = rotationSpeed;
- SettingsManager::SetSimulationSettings(settings);
- }
- ImGui::TextDisabled("Rotation speed multiplier (0 = no rotation)");
-
- float blurStrength = engine.GetBlurStrength();
- if (ImGui::SliderFloat("Blur Strength", &blurStrength, 0.5f, 5.0f, "%.2f"))
- {
- engine.SetBlurStrength(blurStrength);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.blurStrength = blurStrength;
- SettingsManager::SetSimulationSettings(settings);
- }
- ImGui::TextDisabled("Blur radius for glow effect");
-
- float glowIntensity = engine.GetGlowIntensity();
- if (ImGui::SliderFloat("Glow Intensity", &glowIntensity, 0.1f, 3.0f, "%.2f"))
- {
- engine.SetGlowIntensity(glowIntensity);
- SimulationSettings settings = SettingsManager::GetSettingsConst().simulation;
- settings.glowIntensity = glowIntensity;
- SettingsManager::SetSimulationSettings(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 selectedHDRI = 0;
- ImGui::PushID("SimulationHDRI");
- auto& hdriManager = HDRIManager::Get();
- const auto& availableHDRI = hdriManager.GetAvailableHDRI();
-
- static std::vector<std::string> hdriOptionNames;
- static std::vector<const char*> hdriOptions;
-
- if (hdriOptionNames.size() != availableHDRI.size())
- {
- hdriOptionNames.clear();
- hdriOptions.clear();
-
- for (const auto& path : availableHDRI)
- {
- hdriOptionNames.push_back(hdriManager.GetHDRIName(path));
- hdriOptions.push_back(hdriOptionNames.back().c_str());
- }
- }
-
- if (ImGui::Combo("HDRI Environment", &selectedHDRI, hdriOptions.data(), static_cast<int>(hdriOptions.size())))
- {
- auto& hdriManager = HDRIManager::Get();
- hdriManager.SetCurrentHDRI(availableHDRI[selectedHDRI]);
- engine.SetHDRIEnvironment(hdriManager.GetCurrentHDRI());
- }
- 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.GetCamera().GetOrbitalPosition().x,
- engine.GetCamera().GetOrbitalPosition().y,
- engine.GetCamera().GetOrbitalPosition().z);
- ImGui::Text("Radius: %.2e", engine.GetCamera().GetOrbitalRadius());
- ImGui::Text("Azimuth: %.2f", engine.GetCamera().GetAzimuth());
- ImGui::Text("Elevation: %.2f", engine.GetCamera().GetElevation());
-
- 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().GetEngine();
- 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.ExportHighResFrame(ss.str(), 1920, 1080);
- }
-
- if (ImGui::Button("Export High-Res Frame (4K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::Get().GetEngine();
- 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.ExportHighResFrame(ss.str(), 4096, 3072);
- }
-
- if (ImGui::Button("Export High-Res Frame (8K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::Get().GetEngine();
- 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.ExportHighResFrame(ss.str(), 8192, 6144);
- }
-
- if (ImGui::Button("Export Ultra High-Res Frame (16K)", ImVec2(-1, 30)))
- {
- auto& engine = Application::Get().GetEngine();
- 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.ExportHighResFrame(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/SimulationState.h b/src/States/SimulationState.h
deleted file mode 100644
index 2589804..0000000
--- a/src/States/SimulationState.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;
-
- void OnEnter() override;
- void OnExit() override;
- void OnUpdate(float deltaTime) override;
- void OnRender() override;
- void OnImUIRender() override;
- void OnEvent(Event& event) override;
-
- private:
- bool m_Initialized = false;
- };
-};
diff --git a/src/States/WorldBuilderState.cpp b/src/States/WorldBuilderState.cpp
deleted file mode 100644
index 3c49f62..0000000
--- a/src/States/WorldBuilderState.cpp
+++ /dev/null
@@ -1,1113 +0,0 @@
-#include "WorldBuilderState.h"
-
-#include "Core/Application.h"
-#include "Core/Window.h"
-#include "Core/HDRIManager.h"
-
-#include "Rendering/Renderer.h"
-#include "Rendering/Shader.h"
-#include "Rendering/VertexArray.h"
-#include "Rendering/VertexBuffer.h"
-#include "Rendering/IndexBuffer.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
-{
- void WorldBuilderState::OnEnter()
- {
- DONUT_INFO("Entering World Builder State");
-
- ImGuizmo::Enable(true);
-
- m_Camera.SetCameraMode(CameraMode::Orbital);
- m_Camera.SetOrbitalTarget(glm::vec3(0.0f, 0.0f, 0.0f));
- m_Camera.SetOrbitalRadius(15.0f);
- m_Camera.SetOrbitalLimits(2.0f, 200.0f);
- m_Camera.SetOrbitalSpeed(0.01f);
- m_Camera.SetZoomSpeed(2.0f);
- m_Camera.SetAzimuth(0.0f);
- m_Camera.SetElevation(static_cast<float>(std::numbers::pi) / 3.0f);
- m_Camera.UpdateOrbital();
-
- m_SphereShader = Ref<Shader>(Shader::Create("Assets/Shaders/Sphere.glsl"));
- m_SkyboxShader = Ref<Shader>(Shader::Create("Assets/Shaders/Skybox.glsl"));
- m_GridShader = Ref<Shader>(Shader::Create("Assets/Shaders/Grid.glsl"));
-
- if (!m_SphereShader)
- DONUT_ERROR("Failed to create sphere shader");
- if (!m_SkyboxShader)
- DONUT_ERROR("Failed to create skybox shader");
- if (!m_GridShader)
- DONUT_ERROR("Failed to create grid shader");
-
- InitializeSphereGeometry();
- InitializeSkyboxGeometry();
- InitializeGridGeometry();
-
- auto& hdriManager = HDRIManager::Get();
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
- if (!m_HDRIEnvironment)
- {
- hdriManager.SetCurrentHDRI("Assets/HDRI/HDR_blue_nebulae-1.hdr");
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
- if (!m_HDRIEnvironment)
- DONUT_WARN("Failed to load default HDRI for WorldBuilder, using fallback");
- }
-
- Material blackHoleMaterial(glm::vec3(0.0f, 0.0f, 0.0f), 0.0f, 0.0f);
- m_BlackHole = Object(glm::vec3(0.0f, 0.0f, 0.0f), 2.0f, blackHoleMaterial);
- m_BlackHoleInitialized = true;
-
- // Set default grid size
- m_GridSize = 10.0f;
-
- m_Initialized = true;
- }
-
- void WorldBuilderState::OnExit()
- {
- DONUT_INFO("Exiting World Builder State");
- }
-
- void WorldBuilderState::OnUpdate(float deltaTime)
- {
- if (m_CameraDragging &&
- !ImGuizmo::IsUsing())
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
-
- glm::vec2 currentMousePos(xpos, ypos);
- glm::vec2 delta = currentMousePos - m_LastMousePos;
-
- float sensitivity = 0.005f;
- float azimuthDelta = delta.x * sensitivity;
- float elevationDelta = -delta.y * sensitivity;
-
- float newAzimuth = m_Camera.GetAzimuth() + azimuthDelta;
- float newElevation = m_Camera.GetElevation() + elevationDelta;
-
- newElevation = glm::clamp(newElevation, 0.01f, static_cast<float>(std::numbers::pi) - 0.01f);
-
- m_Camera.SetAzimuth(newAzimuth);
- m_Camera.SetElevation(newElevation);
- m_Camera.UpdateOrbital();
-
- m_LastMousePos = currentMousePos;
- }
- }
-
- void WorldBuilderState::OnRender()
- {
- auto& hdriManager = HDRIManager::Get();
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
-
- RenderCommand::SetClearColor(glm::vec4(0.1f, 0.1f, 0.1f, 1.0f));
- RenderCommand::Clear();
-
- if (m_HDRIEnvironment)
- RenderSkybox();
-
- if (m_ShowGrid)
- RenderGrid();
-
- RenderScene();
- }
-
- void WorldBuilderState::OnEvent(Event& event)
- {
- EventDispatcher dispatcher(event);
-
- dispatcher.Dispatch<MouseButtonPressedEvent>([this](MouseButtonPressedEvent& e)
- {
- if (e.GetMouseButton() == GLFW_MOUSE_BUTTON_LEFT)
- {
- if (ImGuizmo::IsUsing() ||
- ImGuizmo::IsOver())
- return false;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- double xpos, ypos;
- glfwGetCursorPos(window, &xpos, &ypos);
-
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- float ndcX = (2.0f * static_cast<float>(xpos)) / static_cast<float>(width) - 1.0f;
- float ndcY = 1.0f - (2.0f * static_cast<float>(ypos)) / static_cast<float>(height);
-
- glm::vec4 rayStart_NDC(ndcX, ndcY, -1.0f, 1.0f);
- glm::vec4 rayEnd_NDC(ndcX, ndcY, 0.0f, 1.0f);
-
- glm::mat4 invVP = glm::inverse(m_Camera.GetProjectionMatrix() * m_Camera.GetViewMatrix());
- glm::vec4 rayStart_World = invVP * rayStart_NDC;
- glm::vec4 rayEnd_World = invVP * rayEnd_NDC;
-
- rayStart_World /= rayStart_World.w;
- rayEnd_World /= rayEnd_World.w;
-
- glm::vec3 rayDir = glm::normalize(glm::vec3(rayEnd_World - rayStart_World));
- glm::vec3 rayOrigin = glm::vec3(rayStart_World);
-
- float closestDistance = std::numeric_limits<float>::max();
- int closestObjectIndex = -1;
-
- if (m_BlackHoleInitialized)
- {
- glm::vec3 oc = rayOrigin - m_BlackHole.m_Centre;
- float a = glm::dot(rayDir, rayDir);
- float b = 2.0f * glm::dot(oc, rayDir);
- float c = glm::dot(oc, oc) - m_BlackHole.m_Radius * m_BlackHole.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 < closestDistance)
- {
- closestDistance = t1;
- closestObjectIndex = -2;
- }
- else if (t2 > 0 && t2 < closestDistance)
- {
- closestDistance = t2;
- closestObjectIndex = -2;
- }
- }
- }
-
- for (size_t i = 0; i < m_Scene.objs.size(); ++i)
- {
- const Object& obj = m_Scene.objs[i];
-
- glm::vec3 oc = rayOrigin - obj.m_Centre;
- float a = glm::dot(rayDir, rayDir);
- float b = 2.0f * glm::dot(oc, rayDir);
- 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 < closestDistance)
- {
- closestDistance = t1;
- closestObjectIndex = static_cast<int>(i);
- }
- else if (t2 > 0 && t2 < closestDistance)
- {
- closestDistance = t2;
- closestObjectIndex = static_cast<int>(i);
- }
- }
- }
-
- if (closestObjectIndex >= 0)
- {
- m_SelectedObjectIndex = closestObjectIndex;
- DONUT_INFO("Selected object {}", closestObjectIndex);
- return true;
- }
- else if (closestObjectIndex == -2)
- {
- m_SelectedObjectIndex = -1;
- DONUT_INFO("Black hole clicked (not selectable)");
- return true;
- }
- else
- {
- m_SelectedObjectIndex = -1;
- m_CameraDragging = true;
- m_LastMousePos = glm::vec2(xpos, ypos);
- }
-
- return true;
- }
- return false;
- });
-
- dispatcher.Dispatch<MouseButtonReleasedEvent>([this](MouseButtonReleasedEvent& e)
- {
- if (e.GetMouseButton() == GLFW_MOUSE_BUTTON_LEFT)
- {
- if (!ImGuizmo::IsUsing())
- m_CameraDragging = false;
- return true;
- }
-
- return false;
- });
-
- dispatcher.Dispatch<WindowResizeEvent>([this](WindowResizeEvent& e)
- {
- int newWidth = e.GetWidth();
- int newHeight = e.GetHeight();
- if (newWidth > 0 && newHeight > 0)
- {
- float aspect = static_cast<float>(newWidth) / static_cast<float>(newHeight);
- m_Camera.SetProjection(45.0f, aspect, 0.1f, 1000.0f);
- }
- return false;
- });
-
- dispatcher.Dispatch<MouseScrolledEvent>([this](MouseScrolledEvent& e)
- {
- float zoomSpeed = 0.1f;
- float zoomDelta = e.GetYOffset() * zoomSpeed;
-
- double currentRadius = m_Camera.GetOrbitalRadius();
- double newRadius = currentRadius - zoomDelta * currentRadius * 0.1f;
-
- double minRadius = 2.0f;
- double maxRadius = 200.0f;
- newRadius = glm::clamp(newRadius, minRadius, maxRadius);
-
- m_Camera.SetOrbitalRadius(newRadius);
- m_Camera.UpdateOrbital();
-
- return true;
- });
-
- dispatcher.Dispatch<KeyPressedEvent>([this](KeyPressedEvent& e)
- {
- if (m_SelectedObjectIndex >= 0 &&
- m_SelectedObjectIndex < static_cast<int>(m_Scene.objs.size()))
- {
- switch (e.GetKeyCode())
- {
- case GLFW_KEY_M:
- m_GizmoOperation = ImGuizmo::TRANSLATE;
- return true;
- case GLFW_KEY_R:
- m_GizmoOperation = ImGuizmo::ROTATE;
- return true;
- case GLFW_KEY_S:
- m_GizmoOperation = ImGuizmo::SCALE;
- return true;
- }
- }
- return false;
- });
- }
-
- void WorldBuilderState::OnImUIRender()
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- 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 cameraPos = m_Camera.GetOrbitalPosition();
- glm::vec3 target = m_Camera.GetOrbitalTarget();
-
- ImGui::Text("Position: (%.2f, %.2f, %.2f)", cameraPos.x, cameraPos.y, cameraPos.z);
- ImGui::Text("Target: (%.2f, %.2f, %.2f)", target.x, target.y, target.z);
- ImGui::Text("Distance: %.2f", m_Camera.GetOrbitalRadius());
- ImGui::Text("Azimuth: %.1f°", glm::degrees(m_Camera.GetAzimuth()));
- ImGui::Text("Elevation: %.1f°", glm::degrees(m_Camera.GetElevation()));
-
- ImGui::Separator();
-
- if (ImGui::Button("Reset Camera"))
- {
- m_Camera.SetOrbitalRadius(15.0f);
- m_Camera.SetAzimuth(0.0f);
- m_Camera.SetElevation(static_cast<float>(std::numbers::pi) / 3.0f);
- m_Camera.SetOrbitalTarget(glm::vec3(0.0f, 0.0f, 0.0f));
- m_Camera.UpdateOrbital();
- }
-
- ImGui::SameLine();
- if (ImGui::Button("Clear Scene"))
- ClearScene();
-
- 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.SetOrbitalTarget(center);
- m_Camera.UpdateOrbital();
- }
- }
- }
-
- 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_NewObjectPosition.x, 0.1f);
-
- ImGui::Text("Radius:");
- ImGui::DragFloat("##Radius", &m_NewObjectRadius, 0.1f, 0.1f, 10.0f);
-
- ImGui::Text("Color:");
- ImGui::ColorEdit3("##Color", &m_NewObjectColor.x);
-
- ImGui::Text("Specular:");
- ImGui::SliderFloat("##Specular", &m_NewObjectSpecular, 0.0f, 1.0f);
-
- ImGui::Text("Emission:");
- ImGui::SliderFloat("##Emission", &m_NewObjectEmission, 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)))
- AddSphere();
- }
- }
-
- ImGui::Spacing();
-
- if (ImGui::CollapsingHeader("Scene Objects"))
- {
- if (m_BlackHoleInitialized)
- {
- 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_BlackHole.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 isSelected = (m_SelectedObjectIndex == static_cast<int>(i));
- if (ImGui::Selectable(("Sphere " + std::to_string(i)).c_str(), isSelected))
- m_SelectedObjectIndex = static_cast<int>(i);
-
- if (isSelected)
- {
- ImGui::SameLine();
- if (ImGui::Button("Remove"))
- {
- RemoveSelectedObject();
- }
-
- 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_GizmoOperation == ImGuizmo::TRANSLATE))
- m_GizmoOperation = ImGuizmo::TRANSLATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Rotate", m_GizmoOperation == ImGuizmo::ROTATE))
- m_GizmoOperation = ImGuizmo::ROTATE;
- ImGui::SameLine();
- if (ImGui::RadioButton("Scale", m_GizmoOperation == ImGuizmo::SCALE))
- m_GizmoOperation = 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_OutlineColor.x);
-
- ImGui::Text("Outline Width:");
- ImGui::SliderFloat("##OutlineWidth", &m_OutlineWidth, 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_ShowGrid);
-
- ImGui::Text("Grid Color:");
- ImGui::ColorEdit3("##GridColor", &m_GridColor.x);
-
- ImGui::Text("Grid Alpha:");
- ImGui::SliderFloat("##GridAlpha", &m_GridAlpha, 0.0f, 1.0f, "%.2f");
-
- ImGui::Text("Grid Size:");
- ImGui::SliderFloat("##GridSize", &m_GridSize, 1.0f, 500.0f, "%.1f");
-
- ImGui::Spacing();
- if (ImGui::Button("Regenerate Grid", ImVec2(ImGui::GetWindowWidth() - 20, 25)))
- InitializeGridGeometry();
-
- 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 selectedHDRI = 0;
- ImGui::PushID("WorldBuilderHDRI");
- auto& hdriManager = HDRIManager::Get();
- const auto& availableHDRI = hdriManager.GetAvailableHDRI();
-
- static std::vector<std::string> hdriOptionNames;
- static std::vector<const char*> hdriOptions;
-
- if (hdriOptionNames.size() != availableHDRI.size())
- {
- hdriOptionNames.clear();
- hdriOptions.clear();
-
- for (const auto& path : availableHDRI)
- {
- hdriOptionNames.push_back(hdriManager.GetHDRIName(path));
- hdriOptions.push_back(hdriOptionNames.back().c_str());
- }
- }
-
- if (ImGui::Combo("HDRI Environment", &selectedHDRI, hdriOptions.data(), static_cast<int>(hdriOptions.size())))
- {
- auto& hdriManager = HDRIManager::Get();
- hdriManager.SetCurrentHDRI(availableHDRI[selectedHDRI]);
- m_HDRIEnvironment = hdriManager.GetCurrentHDRI();
- }
-
- 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 buttonWidth = (ImGui::GetWindowWidth() - 30) / 2.0f;
-
- if (ImGui::Button("Save Scene", ImVec2(buttonWidth, 30)))
- SaveScene();
-
- ImGui::SameLine();
- if (ImGui::Button("Load Scene", ImVec2(buttonWidth, 30)))
- LoadScene();
-
- ImGui::Spacing();
-
- if (ImGui::Button("Start Simulation", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
- {
- auto& engine = Application::Get().GetEngine();
- engine.LoadObjectsFromScene(m_Scene.objs);
- Application::Get().GetStateManager().SwitchToState("Simulation");
- }
-
- ImGui::Spacing();
-
- if (ImGui::Button("Back to Config", ImVec2(ImGui::GetWindowWidth() - 20, 30)))
- Application::Get().GetStateManager().SwitchToState("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_SelectedObjectIndex >= 0 &&
- m_SelectedObjectIndex < static_cast<int>(m_Scene.objs.size()) &&
- m_SelectedObjectIndex != -2)
- {
- glm::mat4 view = m_Camera.GetViewMatrix();
- glm::mat4 projection = m_Camera.GetProjectionMatrix();
-
- Object& selectedObj = m_Scene.objs[m_SelectedObjectIndex];
-
- glm::mat4 objectTransform = glm::mat4(1.0f);
- objectTransform = glm::translate(objectTransform, selectedObj.m_Centre);
- objectTransform = glm::scale(objectTransform, glm::vec3(selectedObj.m_Radius));
-
- DONUT_INFO("Object position: ({}, {}, {})", selectedObj.m_Centre.x, selectedObj.m_Centre.y, selectedObj.m_Centre.z);
- glm::vec3 cameraPos = m_Camera.GetOrbitalPosition();
- DONUT_INFO("Camera position: ({}, {}, {})", cameraPos.x, cameraPos.y, cameraPos.z);
-
- glm::vec3 viewTranslation = glm::vec3(view[3][0], view[3][1], view[3][2]);
- DONUT_INFO("View translation: ({}, {}, {})", viewTranslation.x, viewTranslation.y, viewTranslation.z);
-
- ImGuizmo::SetDrawlist(ImGui::GetForegroundDrawList());
- if (ImGuizmo::Manipulate(glm::value_ptr(view), glm::value_ptr(projection),
- m_GizmoOperation, m_GizmoMode,
- glm::value_ptr(objectTransform),
- nullptr, nullptr))
- {
- glm::vec3 newPosition = glm::vec3(objectTransform[3][0], objectTransform[3][1], objectTransform[3][2]);
- selectedObj.m_Centre = newPosition;
-
- glm::vec3 scale = glm::vec3
- (
- glm::length(glm::vec3(objectTransform[0])),
- glm::length(glm::vec3(objectTransform[1])),
- glm::length(glm::vec3(objectTransform[2]))
- );
- selectedObj.m_Radius = (scale.x + scale.y + scale.z) / 3.0f;
-
- DONUT_INFO("Matrix [3]: ({}, {}, {}, {})", objectTransform[3][0], objectTransform[3][1], objectTransform[3][2], objectTransform[3][3]);
- DONUT_INFO("New position: ({}, {}, {})", newPosition.x, newPosition.y, newPosition.z);
- }
- }
- }
-
- void WorldBuilderState::AddSphere()
- {
- if (m_Scene.objs.size() >= 16)
- {
- DONUT_WARN("Cannot add more objects. Maximum of 16 objects reached.");
- return;
- }
-
- Material material(m_NewObjectColor, m_NewObjectSpecular, m_NewObjectEmission);
- Object sphere(m_NewObjectPosition, m_NewObjectRadius, material);
- m_Scene.objs.push_back(sphere);
-
- m_SelectedObjectIndex = static_cast<int>(m_Scene.objs.size() - 1);
-
- m_NewObjectPosition = glm::vec3(0.0f, 0.0f, 0.0f);
- m_NewObjectRadius = 1.0f;
- m_NewObjectColor = glm::vec3(1.0f, 1.0f, 1.0f);
- m_NewObjectSpecular = 0.5f;
- m_NewObjectEmission = 0.0f;
-
- DONUT_INFO("Added sphere to scene and selected it");
- }
-
- void WorldBuilderState::RemoveSelectedObject()
- {
- if (m_SelectedObjectIndex >= 0 && m_SelectedObjectIndex < static_cast<int>(m_Scene.objs.size()))
- {
- m_Scene.objs.erase(m_Scene.objs.begin() + m_SelectedObjectIndex);
- m_SelectedObjectIndex = -1;
- DONUT_INFO("Removed object from scene");
- }
- }
-
- void WorldBuilderState::ClearScene()
- {
- m_Scene.objs.clear();
- m_SelectedObjectIndex = -1;
- DONUT_INFO("Cleared scene (black hole remains at center)");
- }
-
- void WorldBuilderState::SaveScene()
- {
- nlohmann::json sceneData;
- sceneData["objects"] = nlohmann::json::array();
-
- for (const auto& obj : m_Scene.objs)
- {
- nlohmann::json sphereData;
- sphereData["position"] =
- {
- obj.m_Centre.x,
- obj.m_Centre.y,
- obj.m_Centre.z
- };
-
- sphereData["color"] =
- {
- obj.m_Material.m_Color.x,
- obj.m_Material.m_Color.y,
- obj.m_Material.m_Color.z
- };
-
- sphereData["radius"] = obj.m_Radius;
- sphereData["specular"] = obj.m_Material.m_Specular;
- sphereData["emission"] = obj.m_Material.m_Emission;
- sceneData["objects"].push_back(sphereData);
- }
-
- std::ofstream file("Scene.json");
- if (file.is_open())
- {
- file << sceneData.dump(4);
- file.close();
- DONUT_INFO("Scene saved to Scene.json (black hole always present at center)");
- }
- else
- DONUT_ERROR("Failed to save scene");
- }
-
- void WorldBuilderState::LoadScene()
- {
- std::ifstream file("Scene.json");
- if (file.is_open())
- {
- m_Scene.objs.clear();
- m_SelectedObjectIndex = -1;
-
- nlohmann::json sceneData;
- file >> sceneData;
-
- if (sceneData.contains("objects"))
- {
- for (const auto& sphereData : sceneData["objects"])
- {
- glm::vec3 position;
- glm::vec3 color;
- float radius;
- float specular;
- float emission;
-
- auto posArray = sphereData["position"];
- position.x = posArray[0].get<float>();
- position.y = posArray[1].get<float>();
- position.z = posArray[2].get<float>();
-
- auto colorArray = sphereData["color"];
- color.x = colorArray[0].get<float>();
- color.y = colorArray[1].get<float>();
- color.z = colorArray[2].get<float>();
-
- radius = sphereData["radius"].get<float>();
- specular = sphereData["specular"].get<float>();
- emission = sphereData["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");
- }
-
- void WorldBuilderState::InitializeSphereGeometry()
- {
- 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 sinPhi = sin(phi);
- float cosPhi = cos(phi);
-
- for (int segment = 0; segment <= segments; ++segment)
- {
- float theta = 2.0f * static_cast<float>(std::numbers::pi) * segment / segments;
- float sinTheta = sin(theta);
- float cosTheta = cos(theta);
-
- float x = cosTheta * sinPhi;
- float y = cosPhi;
- float z = sinTheta * sinPhi;
-
- 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 vertexBuffer = Ref<VertexBuffer>(VertexBuffer::Create(vertices.data(), static_cast<uint32_t>(vertices.size() * sizeof(float))));
- VertexBufferLayout layout;
- layout.Push<float>(3);
- layout.Push<float>(3);
- vertexBuffer->SetLayout(layout);
-
- auto indexBuffer = Ref<IndexBuffer>(IndexBuffer::Create(indices.data(), static_cast<uint32_t>(indices.size())));
-
- m_SphereVAO = Ref<VertexArray>(VertexArray::Create());
- m_SphereVAO->AddVertexBuffer(vertexBuffer);
- m_SphereVAO->SetIndexBuffer(indexBuffer);
- }
-
- void WorldBuilderState::RenderScene()
- {
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::SetViewport(0, 0, width, height);
- RenderCommand::EnableDepthTest();
-
- glm::mat4 view = m_Camera.GetViewMatrix();
- glm::mat4 projection = m_Camera.GetProjectionMatrix();
- glm::mat4 viewProjection = projection * view;
-
- glm::vec3 lightPos = m_Scene.m_LightPos;
- glm::vec3 cameraPos = m_Camera.GetOrbitalPosition();
-
- m_SphereShader->Bind();
- m_SphereShader->SetMat4("u_ViewProjection", viewProjection);
- m_SphereShader->SetFloat3("u_LightPos", lightPos);
- m_SphereShader->SetFloat3("u_CameraPos", cameraPos);
-
- m_SphereShader->SetFloat3("u_OutlineColor", m_OutlineColor);
- m_SphereShader->SetFloat("u_OutlineWidth", m_OutlineWidth);
-
- if (m_HDRIEnvironment)
- {
- m_HDRIEnvironment->Bind(1);
- m_SphereShader->SetInt("u_HDRIEnvironment", 1);
- }
-
- if (m_BlackHoleInitialized)
- {
- glm::mat4 blackHoleTransform = glm::translate(glm::mat4(1.0f), m_BlackHole.m_Centre);
- blackHoleTransform = glm::scale(blackHoleTransform, glm::vec3(m_BlackHole.m_Radius));
-
- m_SphereShader->SetMat4("u_Transform", blackHoleTransform);
- m_SphereShader->SetInt("u_IsSelected", 1);
- m_SphereShader->SetFloat3("u_Color", m_BlackHole.m_Material.m_Color);
- m_SphereShader->SetFloat("u_Emission", m_BlackHole.m_Material.m_Emission);
- m_SphereShader->SetFloat("u_Specular", m_BlackHole.m_Material.m_Specular);
- m_SphereShader->SetFloat("u_OutlineWidth", 0.3f);
-
- m_SphereVAO->Bind();
- RenderCommand::DrawIndexed(m_SphereVAO);
-
- m_SphereShader->SetFloat("u_OutlineWidth", m_OutlineWidth);
- }
-
- 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_SphereShader->SetMat4("u_Transform", transform);
-
- bool isSelected = (m_SelectedObjectIndex == static_cast<int>(i));
- m_SphereShader->SetInt("u_IsSelected", isSelected ? 1 : 0);
- if (isSelected)
- {
- glm::vec3 highlightColor = obj.m_Material.m_Color * 1.5f;
- highlightColor = glm::clamp(highlightColor, 0.0f, 1.0f);
- m_SphereShader->SetFloat3("u_Color", highlightColor);
- m_SphereShader->SetFloat("u_Emission", 0.2f);
- }
- else
- {
- m_SphereShader->SetFloat3("u_Color", obj.m_Material.m_Color);
- m_SphereShader->SetFloat("u_Emission", obj.m_Material.m_Emission);
- }
-
- m_SphereShader->SetFloat("u_Specular", obj.m_Material.m_Specular);
-
- m_SphereVAO->Bind();
- RenderCommand::DrawIndexed(m_SphereVAO);
- }
-
- RenderCommand::DisableDepthTest();
- }
-
-
-
- void WorldBuilderState::InitializeSkyboxGeometry()
- {
- float skyboxVertices[] =
- {
- -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 vertexBuffer = Ref<VertexBuffer>(VertexBuffer::Create(skyboxVertices, sizeof(skyboxVertices)));
- VertexBufferLayout layout;
- layout.Push<float>(3);
- vertexBuffer->SetLayout(layout);
-
- m_SkyboxVAO = Ref<VertexArray>(VertexArray::Create());
- m_SkyboxVAO->AddVertexBuffer(vertexBuffer);
- }
-
- void WorldBuilderState::RenderSkybox()
- {
- if (!m_SkyboxShader || !m_SkyboxVAO || !m_HDRIEnvironment)
- return;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::SetViewport(0, 0, width, height);
- RenderCommand::DisableDepthTest();
-
- glm::mat4 view = m_Camera.GetViewMatrix();
- glm::mat4 projection = m_Camera.GetProjectionMatrix();
-
- view = glm::mat4(glm::mat3(view));
-
- m_SkyboxShader->Bind();
- m_SkyboxShader->SetMat4("u_View", view);
- m_SkyboxShader->SetMat4("u_Projection", projection);
-
- m_HDRIEnvironment->Bind(0);
- m_SkyboxShader->SetInt("u_Skybox", 0);
-
- m_SkyboxVAO->Bind();
- RenderCommand::DrawArrays(36);
-
- RenderCommand::EnableDepthTest();
- }
-
- void WorldBuilderState::InitializeGridGeometry()
- {
- if (m_GridVAO)
- {
- m_GridVAO->Unbind();
- m_GridVAO.reset();
- }
-
- const float gridSize = 50.0f;
- const int gridLines = 101;
- const float halfSize = gridSize * 0.5f;
- const float step = gridSize / (gridLines - 1);
-
- std::vector<float> vertices;
- std::vector<uint32_t> indices;
-
- for (int i = 0; i < gridLines; ++i)
- {
- float z = -halfSize + i * step;
-
- vertices.push_back(-halfSize);
- vertices.push_back(0.0f);
- vertices.push_back(z);
-
- vertices.push_back(halfSize);
- vertices.push_back(0.0f);
- vertices.push_back(z);
-
- uint32_t baseIndex = static_cast<uint32_t>(vertices.size() / 3) - 2;
- indices.push_back(baseIndex);
- indices.push_back(baseIndex + 1);
- }
-
- for (int i = 0; i < gridLines; ++i)
- {
- float x = -halfSize + i * step;
-
- vertices.push_back(x);
- vertices.push_back(0.0f);
- vertices.push_back(-halfSize);
-
- vertices.push_back(x);
- vertices.push_back(0.0f);
- vertices.push_back(halfSize);
-
- uint32_t baseIndex = static_cast<uint32_t>(vertices.size() / 3) - 2;
- indices.push_back(baseIndex);
- indices.push_back(baseIndex + 1);
- }
-
- auto vertexBuffer = Ref<VertexBuffer>(VertexBuffer::Create(vertices.data(), static_cast<uint32_t>(vertices.size() * sizeof(float))));
- VertexBufferLayout layout;
- layout.Push<float>(3);
- vertexBuffer->SetLayout(layout);
-
- auto indexBuffer = Ref<IndexBuffer>(IndexBuffer::Create(indices.data(), static_cast<uint32_t>(indices.size())));
-
- m_GridVAO = Ref<VertexArray>(VertexArray::Create());
- m_GridVAO->AddVertexBuffer(vertexBuffer);
- m_GridVAO->SetIndexBuffer(indexBuffer);
-
- m_GridVAO->Unbind();
- }
-
- void WorldBuilderState::RenderGrid()
- {
- if (!m_GridShader || !m_GridVAO)
- return;
-
- GLFWwindow* window = static_cast<GLFWwindow*>(Application::Get().GetWindow().GetNativeWindow());
- int width, height;
- glfwGetFramebufferSize(window, &width, &height);
-
- RenderCommand::SetViewport(0, 0, width, height);
- RenderCommand::EnableDepthTest();
- RenderCommand::EnableBlending();
-
- glm::mat4 view = m_Camera.GetViewMatrix();
- glm::mat4 projection = m_Camera.GetProjectionMatrix();
- glm::mat4 viewProjection = projection * view;
- glm::mat4 transform = glm::mat4(1.0f);
-
- glm::vec3 cameraPos = m_Camera.GetOrbitalPosition();
-
- m_GridShader->Bind();
- m_GridShader->SetMat4("u_ViewProjection", viewProjection);
- m_GridShader->SetMat4("u_Transform", transform);
- m_GridShader->SetFloat3("u_GridColor", m_GridColor);
- m_GridShader->SetFloat("u_GridAlpha", m_GridAlpha);
- m_GridShader->SetFloat("u_GridSize", m_GridSize);
- m_GridShader->SetFloat3("u_CameraPos", cameraPos);
-
- m_GridVAO->Bind();
- RenderCommand::DrawLines(m_GridVAO);
-
- // Clean up state to prevent conflicts with scene rendering
- m_GridVAO->Unbind();
- m_GridShader->Unbind();
- RenderCommand::DisableBlending();
-
- // Ensure depth test is still enabled for scene rendering
- RenderCommand::EnableDepthTest();
- }
-};
diff --git a/src/States/WorldBuilderState.h b/src/States/WorldBuilderState.h
deleted file mode 100644
index 2d65060..0000000
--- a/src/States/WorldBuilderState.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/VertexArray.h"
-
-#include <imgui.h>
-#include <ImGuizmo.h>
-
-#include <vector>
-#include <memory>
-
-namespace Donut
-{
- class WorldBuilderState
- : public State
- {
- public:
- ~WorldBuilderState() = default;
-
- void OnEnter() override;
- void OnExit() override;
- void OnUpdate(float deltaTime) override;
- void OnRender() override;
- void OnImUIRender() override;
- void OnEvent(Event& event) override;
- private:
- void AddSphere();
- void RemoveSelectedObject();
- void ClearScene();
- void SaveScene();
- void LoadScene();
- void RenderScene();
- void InitializeSphereGeometry();
- void InitializeGridGeometry();
- void RenderGrid();
- private:
- Scene m_Scene;
- Camera m_Camera;
- bool m_Initialized = false;
-
- Ref<Shader> m_SphereShader;
- Ref<VertexArray> m_SphereVAO;
- Ref<CubemapTexture> m_HDRIEnvironment;
-
- Ref<Shader> m_SkyboxShader;
- Ref<VertexArray> m_SkyboxVAO;
-
- Ref<Shader> m_GridShader;
- Ref<VertexArray> m_GridVAO;
-
- glm::vec3 m_NewObjectPosition = glm::vec3(0.0f, 0.0f, 0.0f);
- float m_NewObjectRadius = 1.0f;
- glm::vec3 m_NewObjectColor = glm::vec3(1.0f, 1.0f, 1.0f);
- float m_NewObjectSpecular = 0.5f;
- float m_NewObjectEmission = 0.0f;
-
- int m_SelectedObjectIndex = -1;
- bool m_CameraDragging = false;
- glm::vec2 m_LastMousePos = glm::vec2(0.0f, 0.0f);
-
- ImGuizmo::OPERATION m_GizmoOperation = ImGuizmo::TRANSLATE;
- ImGuizmo::MODE m_GizmoMode = ImGuizmo::LOCAL;
-
- bool m_ShowObjectList = true;
- bool m_ShowObjectCreator = true;
- bool m_ShowSceneInfo = true;
- bool m_ShowGizmoControls = true;
- bool m_ShowOutlineControls = true;
- bool m_ShowGrid = true;
-
- glm::vec3 m_OutlineColor = glm::vec3(1.0f, 1.0f, 1.0f);
- float m_OutlineWidth = 0.25f;
-
- glm::vec3 m_GridColor = glm::vec3(0.5f, 0.5f, 0.5f);
- float m_GridAlpha = 0.5f;
- float m_GridSize = 50.0f;
-
- Object m_BlackHole;
- bool m_BlackHoleInitialized = false;
-
- void SetHDRIEnvironment(Ref<CubemapTexture> hdri) { m_HDRIEnvironment = hdri; }
- Ref<CubemapTexture> GetHDRIEnvironment() const { return m_HDRIEnvironment; }
-
- void InitializeSkyboxGeometry();
- void RenderSkybox();
- };
-};
diff --git a/src/core/application.cpp b/src/core/application.cpp
new file mode 100644
index 0000000..667fc73
--- /dev/null
+++ b/src/core/application.cpp
@@ -0,0 +1,238 @@
+#include "application.h"
+
+#include "rendering/renderer.h"
+#include "settings_manager.h"
+
+#include "states/simulation_state.h"
+#include "states/config_state.h"
+#include "states/world_builder_state.h"
+
+#include <GLFW/glfw3.h>
+#include <imgui.h>
+#include <ImGuizmo.h>
+
+#include "platform/vulkan/vulkan_renderer.h"
+
+#ifdef __APPLE__
+#include "platform/metal/metal_context.h"
+#include "platform/vulkan/vulkan_context.h"
+#endif
+
+namespace Donut
+{
+ Application* Application::s_instance = nullptr;
+
+ Application::Application(const std::string& name, int width, int height)
+ : m_running(true), m_minimized(false)
+ {
+ s_instance = this;
+
+ // Select the render API before the window is created: the window is
+ // built differently for Vulkan (GLFW_NO_API) than for OpenGL.
+ Logger::init();
+ SettingsManager::initialize();
+ {
+ const auto& settings = SettingsManager::get_settings_const();
+ RendererAPI::set_api(settings.graphics.render_api == "Vulkan"
+ ? RendererAPI::API::Vulkan : RendererAPI::API::OpenGL);
+ }
+
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
+ vulkan_prepare_glfw(); // must precede glfwInit() inside the Window ctor
+
+ m_window = create_scope<Window>(name, width, height);
+ m_window->set_event_callback([this](Event& event)
+ {
+ on_event(event);
+ });
+
+ on_init();
+ }
+
+ Application::~Application()
+ {
+ on_shutdown();
+ s_instance = nullptr;
+ }
+
+ auto Application::run() -> void
+ {
+ while (m_running)
+ {
+ if (m_vulkan_renderer)
+ {
+ glfwPollEvents(); // before the ImGui frame so input is current
+ if (!m_minimized)
+ m_vulkan_renderer->draw_frame(glm::vec4(0.05f, 0.06f, 0.10f, 1.0f),
+ [this] { build_vulkan_ui(); });
+ }
+ else
+ {
+ if (!m_minimized)
+ {
+ on_update();
+ on_render();
+ }
+ m_window->on_update(); // polls events + swaps buffers (OpenGL)
+ }
+ }
+ }
+
+ auto Application::build_vulkan_ui() -> void
+ {
+ // Placeholder UI proving the ImGui Vulkan backend renders. The real
+ // application UI (states' on_im_ui_render) moves here once the scene is
+ // ported to Vulkan (B-3).
+ ImGui::Begin("Donut - Vulkan backend");
+ ImGui::Text("Geodesic black hole through Vulkan (MoltenVK).");
+ ImGui::Text("%.1f FPS", ImGui::GetIO().Framerate);
+ ImGui::Separator();
+ ImGui::TextWrapped("Drag to orbit, scroll to zoom. HDRI starfield is live; "
+ "next is porting the simulation UI to Vulkan.");
+ ImGui::End();
+ }
+
+ auto Application::close() -> void
+ {
+ m_running = false;
+ }
+
+ auto Application::on_event(Event& event) -> void
+ {
+ EventDispatcher dispatcher(event);
+ dispatcher.dispatch<WindowCloseEvent>([this, &event](WindowCloseEvent& e)
+ {
+ m_running = false;
+ event.handled = true;
+ return true;
+ });
+
+ dispatcher.dispatch<WindowResizeEvent>([this, &event](WindowResizeEvent& e)
+ {
+ if (e.get_width() == 0 || e.get_height() == 0)
+ m_minimized = true;
+ else
+ m_minimized = false;
+
+ if (m_vulkan_renderer)
+ {
+ m_vulkan_renderer->on_resize(e.get_width(), e.get_height());
+ }
+ else
+ {
+ Renderer::on_window_resize(e.get_width(), e.get_height());
+ if (m_engine) m_engine->set_window_dimensions(e.get_width(), e.get_height());
+ }
+ event.handled = true;
+ return true;
+ });
+
+ dispatcher.dispatch<KeyPressedEvent>([this, &event](KeyPressedEvent& e)
+ {
+ if (!m_state_manager)
+ return true;
+ if (e.get_key_code() == GLFW_KEY_1)
+ {
+ m_state_manager->switch_to_state("Config");
+ event.handled = true;
+ return true;
+ }
+ else if (e.get_key_code() == GLFW_KEY_2)
+ {
+ m_state_manager->switch_to_state("Simulation");
+ event.handled = true;
+ return true;
+ }
+ else if (e.get_key_code() == GLFW_KEY_3)
+ {
+ m_state_manager->switch_to_state("WorldBuilder");
+ event.handled = true;
+ return true;
+ }
+
+ return true;
+ });
+
+ if (m_state_manager)
+ m_state_manager->on_event(event);
+ }
+
+ auto Application::on_init() -> void
+ {
+ // (Logger, settings and API selection happen in the constructor.)
+
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
+ {
+ int w = 0, h = 0;
+ glfwGetFramebufferSize((GLFWwindow*)m_window->get_native_window(), &w, &h);
+ m_vulkan_renderer = create_scope<VulkanRenderer>();
+ if (!m_vulkan_renderer->init(m_window->get_native_window(), w, h))
+ DONUT_ERROR("Vulkan renderer initialization failed");
+ else
+ m_vulkan_renderer->init_im_gui();
+ // Scene rendering hooks in here in the B-3 phase; the Vulkan path now
+ // clears, presents, and draws the ImGui UI.
+ return;
+ }
+
+ Renderer::init();
+ m_window->init_im_gui();
+
+ Renderer::on_window_resize(1280, 720);
+ RenderCommand::set_face_culling(false);
+
+ m_engine = create_scope<Engine>();
+ m_engine->set_window_dimensions(1280, 720);
+
+ m_state_manager = create_scope<StateManager>();
+ m_state_manager->register_state("Config", create_scope<ConfigState>());
+ m_state_manager->register_state("Simulation", create_scope<SimulationState>());
+ m_state_manager->register_state("WorldBuilder", create_scope<WorldBuilderState>());
+ m_state_manager->switch_to_state("Config");
+ }
+
+ auto Application::on_shutdown() -> void
+ {
+ if (m_vulkan_renderer)
+ {
+ m_vulkan_renderer->shutdown();
+ m_vulkan_renderer.reset();
+ }
+ else
+ {
+ if (m_state_manager)
+ m_state_manager->shutdown();
+ Renderer::shutdown();
+ }
+ SettingsManager::shutdown();
+ Logger::shutdown();
+ }
+
+ auto Application::on_update() -> void
+ {
+ float current_frame = (float)glfwGetTime();
+ m_delta_time = current_frame - m_last_frame;
+ m_last_frame = current_frame;
+
+ m_state_manager->update(m_delta_time);
+ }
+
+ auto Application::on_render() -> void
+ {
+ m_state_manager->render();
+ m_window->begin_im_gui_frame();
+
+ ImGuizmo::BeginFrame();
+ ImGuizmo::SetOrthographic(false);
+
+ setup_docking_layout();
+ m_state_manager->on_im_ui_render();
+ m_window->end_im_gui_frame();
+ }
+
+ auto Application::setup_docking_layout() -> void
+ {
+ ImGuiViewport* viewport = ImGui::GetMainViewport();
+ ImGui::DockSpaceOverViewport(0, viewport, ImGuiDockNodeFlags_PassthruCentralNode);
+ }
+};
diff --git a/src/core/application.h b/src/core/application.h
new file mode 100644
index 0000000..06c4e94
--- /dev/null
+++ b/src/core/application.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "state_manager.h"
+#include "memory.h"
+#include "window.h"
+#include "event.h"
+#include "log.h"
+
+#include "engine/engine.h"
+
+namespace Donut
+{
+ class VulkanRenderer; // live-window Vulkan backend (platform/vulkan)
+
+ class Application
+ {
+ public:
+ Application(const std::string& name = "Donut",
+ int width = 1280, int height = 720);
+ ~Application();
+
+ auto run() -> void;
+ auto close() -> void;
+
+ auto get_window() -> Window& { return *m_window; }
+ auto get_state_manager() -> StateManager& { return *m_state_manager; }
+ auto get_engine() -> Engine& { return *m_engine; }
+ static auto get() -> Application& { return *s_instance; }
+
+ private:
+ auto on_init() -> void;
+ auto on_shutdown() -> void;
+ auto on_update() -> void;
+ auto on_render() -> void;
+ auto on_event(Event& event) -> void;
+ auto setup_docking_layout() -> void;
+ auto build_vulkan_ui() -> void; // ImGui UI built each frame on the Vulkan path
+
+ private:
+ Scope<StateManager> m_state_manager;
+ Scope<Window> m_window;
+ Scope<Engine> m_engine;
+ Scope<VulkanRenderer> m_vulkan_renderer; // non-null only when the Vulkan API is selected
+
+ bool m_running;
+ bool m_minimized;
+
+ float m_delta_time = 0.0f;
+ float m_last_frame = 0.0f;
+
+ static Application* s_instance;
+ };
+}
diff --git a/src/core/camera.cpp b/src/core/camera.cpp
new file mode 100644
index 0000000..35b7a2f
--- /dev/null
+++ b/src/core/camera.cpp
@@ -0,0 +1,225 @@
+#include "camera.h"
+
+#include <glm/gtc/quaternion.hpp>
+#include <GLFW/glfw3.h>
+
+namespace Donut
+{
+ Camera::Camera(float fov, float aspect_ratio, float near_plane, float far_plane)
+ : m_fov(fov), m_aspect_ratio(aspect_ratio), m_near_plane(near_plane), m_far_plane(far_plane)
+ {
+ recalculate_projection_matrix();
+ recalculate_view_matrix();
+ }
+
+ auto Camera::set_projection(float fov, float aspect_ratio, float near_plane, float far_plane) -> void
+ {
+ m_fov = fov;
+ m_aspect_ratio = aspect_ratio;
+ m_near_plane = near_plane;
+ m_far_plane = far_plane;
+ recalculate_projection_matrix();
+ }
+
+ auto Camera::recalculate_projection_matrix() -> void
+ {
+ m_projection_matrix = glm::perspective(glm::radians(m_fov), m_aspect_ratio, m_near_plane, m_far_plane);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
+ }
+
+ auto Camera::recalculate_view_matrix() -> void
+ {
+ 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 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;
+ }
+ else if (m_camera_mode == CameraMode::Orbital)
+ {
+ glm::vec3 position = get_orbital_position();
+ glm::vec3 target = m_orbital_target;
+ glm::vec3 up(0.0f, 1.0f, 0.0f);
+
+ m_view_matrix = glm::lookAt(position, target, up);
+ m_view_projection_matrix = m_projection_matrix * m_view_matrix;
+ }
+ }
+
+ auto Camera::get_forward_direction() const -> glm::vec3
+ {
+ float pitch = glm::radians(m_rotation.x);
+ float yaw = glm::radians(m_rotation.y);
+
+ glm::vec3 direction;
+ direction.x = cos(yaw) * cos(pitch);
+ direction.y = sin(pitch);
+ direction.z = sin(yaw) * cos(pitch);
+
+ return glm::normalize(direction);
+ }
+
+ auto Camera::get_right_direction() const -> glm::vec3
+ {
+ glm::vec3 world_up(0.0f, 1.0f, 0.0f);
+ return glm::normalize(glm::cross(get_forward_direction(), world_up));
+ }
+
+ auto Camera::get_up_direction() const -> glm::vec3
+ {
+ return glm::normalize(glm::cross(get_right_direction(), get_forward_direction()));
+ }
+
+ auto Camera::on_mouse_move(float x_offset, float y_offset, bool constrain_pitch) -> void
+ {
+ 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;
+
+ 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();
+ }
+ }
+
+ auto Camera::move_forward(float delta_time) -> void
+ {
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position += get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
+ }
+
+ auto Camera::move_backward(float delta_time) -> void
+ {
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position -= get_forward_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
+ }
+
+ auto Camera::move_right(float delta_time) -> void
+ {
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position += get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
+ }
+
+ auto Camera::move_left(float delta_time) -> void
+ {
+ if (m_camera_mode == CameraMode::FPS)
+ {
+ m_position -= get_right_direction() * m_movement_speed * delta_time;
+ recalculate_view_matrix();
+ }
+ }
+
+ auto Camera::move_up(float delta_time) -> void
+ {
+ 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();
+ }
+ }
+
+ auto Camera::move_down(float delta_time) -> void
+ {
+ 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();
+ }
+ }
+
+ auto Camera::get_orbital_position() const -> glm::vec3
+ {
+ float clamped_elevation = glm::clamp(m_elevation, 0.01f, float(std::numbers::pi) - 0.01f);
+ return glm::vec3
+ (
+ m_orbital_radius * sin(clamped_elevation) * cos(m_azimuth),
+ m_orbital_radius * cos(clamped_elevation),
+ m_orbital_radius * sin(clamped_elevation) * sin(m_azimuth)
+ );
+ }
+
+ auto Camera::update_orbital() -> void
+ {
+ m_orbital_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ if (m_dragging || m_panning)
+ m_moving = true;
+ else
+ m_moving = false;
+ recalculate_view_matrix();
+ }
+
+ auto Camera::process_orbital_mouse_move(double x, double y) -> void
+ {
+ if (m_dragging && !m_panning)
+ {
+ float dx = float(x - m_last_x_orbital);
+ float dy = float(y - m_last_y_orbital);
+
+ m_azimuth += dx * m_orbital_speed;
+ m_elevation -= dy * m_orbital_speed;
+ m_elevation = glm::clamp(m_elevation, 0.01f, float(std::numbers::pi) - 0.01f);
+ }
+
+ m_last_x_orbital = x;
+ m_last_y_orbital = y;
+ update_orbital();
+ }
+
+ auto Camera::process_orbital_mouse_button(int button, int action, int mods) -> void
+ {
+ 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;
+ }
+ }
+ }
+
+ auto Camera::process_orbital_scroll(double x_offset, double y_offset) -> void
+ {
+ m_orbital_radius -= y_offset * m_zoom_speed;
+ m_orbital_radius = glm::clamp(m_orbital_radius, m_orbital_min_radius, m_orbital_max_radius);
+ update_orbital();
+ }
+}
diff --git a/src/core/camera.h b/src/core/camera.h
new file mode 100644
index 0000000..c997c35
--- /dev/null
+++ b/src/core/camera.h
@@ -0,0 +1,131 @@
+#pragma once
+
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+#include <numbers>
+
+namespace Donut
+{
+ enum class CameraMode
+ {
+ FPS,
+ Orbital
+ };
+
+ class Camera
+ {
+ public:
+ Camera(float fov = 45.0f, float aspect_ratio = 16.0f / 9.0f,
+ float near_plane = 0.1f, float far_plane = 100.0f);
+ ~Camera() = default;
+
+ auto set_position(const glm::vec3& position) -> void { m_position = position; recalculate_view_matrix(); }
+ auto set_rotation(const glm::vec3& rotation) -> void { m_rotation = rotation; recalculate_view_matrix(); }
+
+ auto get_position() const -> const glm::vec3& { return m_position; }
+ auto get_rotation() const -> const glm::vec3& { return m_rotation; }
+
+ auto get_forward_direction() const -> glm::vec3;
+ auto get_right_direction() const -> glm::vec3;
+ auto get_up_direction() const -> glm::vec3;
+
+ auto get_projection_matrix() const -> const glm::mat4& { return m_projection_matrix; }
+ auto get_view_matrix() const -> const glm::mat4& { return m_view_matrix; }
+ auto get_view_projection_matrix() const -> const glm::mat4& { return m_view_projection_matrix; }
+
+ auto set_projection(float fov, float aspect_ratio, float near_plane, float far_plane) -> void;
+ auto on_mouse_move(float x_offset, float y_offset, bool constrain_pitch = true) -> void;
+
+ auto move_forward(float delta_time) -> void;
+ auto move_backward(float delta_time) -> void;
+ auto move_right(float delta_time) -> void;
+ auto move_left(float delta_time) -> void;
+ auto move_up(float delta_time) -> void;
+ auto move_down(float delta_time) -> void;
+
+ auto set_mouse_sensitivity(float sensitivity) -> void { m_mouse_sensitivity = sensitivity; }
+ auto get_mouse_sensitivity() const -> float { return m_mouse_sensitivity; }
+
+ auto set_movement_speed(float speed) -> void { m_movement_speed = speed; }
+ auto get_movement_speed() const -> float { return m_movement_speed; }
+
+ auto set_orbital_target(const glm::vec3& target) -> void { m_orbital_target = target; }
+ auto get_orbital_target() const -> const glm::vec3& { return m_orbital_target; }
+
+ auto set_orbital_radius(double radius) -> void { m_orbital_radius = radius; }
+ auto get_orbital_radius() const -> double { return m_orbital_radius; }
+
+ auto set_orbital_limits(double min_radius, double max_radius) -> void
+ {
+ m_orbital_min_radius = min_radius;
+ m_orbital_max_radius = max_radius;
+ }
+
+ auto set_orbital_speed(float speed) -> void { m_orbital_speed = speed; }
+ auto get_orbital_speed() const -> float { return m_orbital_speed; }
+
+ auto set_zoom_speed(double speed) -> void { m_zoom_speed = speed; }
+ auto get_zoom_speed() const -> double { return m_zoom_speed; }
+
+ auto set_azimuth(float azimuth) -> void { m_azimuth = azimuth; }
+ auto get_azimuth() const -> float { return m_azimuth; }
+
+ auto set_elevation(float elevation) -> void { m_elevation = elevation; }
+ auto get_elevation() const -> float { return m_elevation; }
+
+ auto get_orbital_position() const -> glm::vec3;
+ auto update_orbital() -> void;
+ auto process_orbital_mouse_move(double x, double y) -> void;
+ auto process_orbital_mouse_button(int button, int action, int mods) -> void;
+ auto process_orbital_scroll(double x_offset, double y_offset) -> void;
+
+ auto set_camera_mode(CameraMode mode) -> void { m_camera_mode = mode; }
+ auto get_camera_mode() const -> CameraMode { return m_camera_mode; }
+
+ auto is_dragging() const -> bool { return m_dragging; }
+ auto is_panning() const -> bool { return m_panning; }
+ auto is_moving() const -> bool { return m_moving; }
+ auto get_last_x() const -> double { return m_last_x; }
+ auto get_last_y() const -> double { return m_last_y; }
+
+ private:
+ auto recalculate_view_matrix() -> void;
+ auto recalculate_projection_matrix() -> void;
+
+ private:
+ CameraMode m_camera_mode = CameraMode::FPS;
+
+ glm::mat4 m_projection_matrix;
+ glm::mat4 m_view_matrix;
+ glm::mat4 m_view_projection_matrix;
+
+ glm::vec3 m_position = { 0.0f, 0.0f, 3.0f };
+ glm::vec3 m_rotation = { 0.0f, 0.0f, 0.0f };
+
+ float m_fov = 45.0f;
+ float m_aspect_ratio = 16.0f / 9.0f;
+ float m_near_plane = 0.1f;
+ float m_far_plane = 100.0f;
+
+ float m_mouse_sensitivity = 0.1f;
+ float m_movement_speed = 5.0f;
+ bool m_first_mouse = true;
+ float m_last_x = 0.0f;
+ float m_last_y = 0.0f;
+
+ glm::vec3 m_orbital_target = glm::vec3(0.0f, 0.0f, 0.0f);
+ double m_orbital_radius = 6.34194e10;
+ double m_orbital_min_radius = 1e10;
+ double m_orbital_max_radius = 1e12;
+ float m_azimuth = 0.0f;
+ float m_elevation = static_cast<float>(std::numbers::pi) / 2.0f;
+ float m_orbital_speed = 0.01f;
+ double m_zoom_speed = 25e9f;
+ bool m_dragging = false;
+ bool m_panning = false;
+ bool m_moving = false;
+ double m_last_x_orbital = 0.0;
+ double m_last_y_orbital = 0.0;
+ };
+}
diff --git a/src/core/event.h b/src/core/event.h
new file mode 100644
index 0000000..1891206
--- /dev/null
+++ b/src/core/event.h
@@ -0,0 +1,362 @@
+#pragma once
+
+#include <functional>
+#include <unordered_map>
+#include <vector>
+#include <string>
+#include <sstream>
+
+namespace Donut
+{
+ class Event;
+ class EventDispatcher;
+
+ enum class EventType
+ {
+ None = 0,
+
+ WindowClose,
+ WindowResize,
+ WindowFocus,
+ WindowLostFocus,
+ WindowMoved,
+
+ KeyPressed,
+ KeyReleased,
+ KeyTyped,
+
+ MouseButtonPressed,
+ MouseButtonReleased,
+ MouseMoved,
+ MouseScrolled
+ };
+
+ enum EventCategory
+ {
+ None = 0,
+ EventCategoryApplication = 1 << 0,
+ EventCategoryInput = 1 << 1,
+ EventCategoryKeyboard = 1 << 2,
+ EventCategoryMouse = 1 << 3,
+ EventCategoryMouseButton = 1 << 4
+ };
+
+ class Event
+ {
+ public:
+ virtual ~Event() = default;
+
+ virtual auto get_event_type() const -> EventType = 0;
+ virtual auto get_name() const -> const char* = 0;
+ virtual auto get_category_flags() const -> int = 0;
+ virtual auto to_string() const -> std::string { return get_name(); }
+
+ auto is_in_category(EventCategory category) -> bool { return get_category_flags() & category; }
+
+ bool handled = false;
+ };
+
+ #define EVENT_CLASS_TYPE(type) \
+ static auto get_static_type() -> EventType { return EventType::type; } \
+ virtual auto get_event_type() const -> EventType override { return get_static_type(); } \
+ virtual auto get_name() const -> const char* override { return #type; }
+
+ #define APPLICATION_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual auto get_category_flags() const -> int override { return EventCategoryApplication; }
+
+ #define MOUSE_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouse | \
+ EventCategoryInput; }
+
+ #define MOUSE_BUTTON_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouseButton | \
+ EventCategoryMouse | \
+ EventCategoryInput; }
+
+ #define KEYBOARD_EVENT_CLASS_TYPE(type) \
+ EVENT_CLASS_TYPE(type) \
+ virtual auto get_category_flags() const -> int override { return EventCategoryKeyboard | \
+ EventCategoryInput; }
+
+ class WindowResizeEvent
+ : public Event
+ {
+ public:
+ WindowResizeEvent(unsigned int width, unsigned int height)
+ : m_width(width), m_height(height) { }
+
+ auto get_width() const -> unsigned int { return m_width; }
+ auto get_height() const -> unsigned int { return m_height; }
+
+ auto to_string() const -> std::string override
+ {
+ return "WindowResizeEvent: " + std::to_string(m_width) + ", " + std::to_string(m_height);
+ }
+
+ APPLICATION_EVENT_CLASS_TYPE(WindowResize)
+
+ private:
+ unsigned int m_width, m_height;
+ };
+
+ class WindowCloseEvent
+ : public Event
+ {
+ public:
+ WindowCloseEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowClose)
+ };
+
+ class WindowFocusEvent
+ : public Event
+ {
+ public:
+ WindowFocusEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowFocus)
+ };
+
+ class WindowLostFocusEvent
+ : public Event
+ {
+ public:
+ WindowLostFocusEvent() = default;
+ APPLICATION_EVENT_CLASS_TYPE(WindowLostFocus)
+ };
+
+ class WindowMovedEvent
+ : public Event
+ {
+ public:
+ WindowMovedEvent(int x, int y)
+ : m_x(x), m_y(y) { }
+
+ auto get_x() const -> int { return m_x; }
+ auto get_y() const -> int { return m_y; }
+
+ auto to_string() const -> std::string override
+ {
+ return "WindowMovedEvent: " + std::to_string(m_x) + ", " + std::to_string(m_y);
+ }
+
+ APPLICATION_EVENT_CLASS_TYPE(WindowMoved)
+
+ private:
+ int m_x, m_y;
+ };
+
+ class KeyEvent
+ : public Event
+ {
+ public:
+ auto get_key_code() const -> int { return m_key_code; }
+ virtual auto get_category_flags() const -> int override { return EventCategoryKeyboard |
+ EventCategoryInput; }
+
+ protected:
+ KeyEvent(int keycode)
+ : m_key_code(keycode) { }
+ int m_key_code;
+ };
+
+ class KeyPressedEvent
+ : public KeyEvent
+ {
+ public:
+ KeyPressedEvent(int keycode, bool is_repeat = false)
+ : KeyEvent(keycode), m_is_repeat(is_repeat) { }
+
+ auto is_repeat() const -> bool { return m_is_repeat; }
+
+ auto to_string() const -> std::string override
+ {
+ return "KeyPressedEvent: " + std::to_string(m_key_code) +
+ " (repeat = " + std::to_string(m_is_repeat) + ")";
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyPressed)
+
+ private:
+ bool m_is_repeat;
+ };
+
+ class KeyReleasedEvent
+ : public KeyEvent
+ {
+ public:
+ KeyReleasedEvent(int keycode)
+ : KeyEvent(keycode) { }
+
+ auto to_string() const -> std::string override
+ {
+ return "KeyReleasedEvent: " + std::to_string(m_key_code);
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyReleased)
+ };
+
+ class KeyTypedEvent
+ : public KeyEvent
+ {
+ public:
+ KeyTypedEvent(int keycode)
+ : KeyEvent(keycode) { }
+
+ auto to_string() const -> std::string override
+ {
+ return "KeyTypedEvent: " + std::to_string(m_key_code);
+ }
+
+ KEYBOARD_EVENT_CLASS_TYPE(KeyTyped)
+ };
+
+ class MouseMovedEvent
+ : public Event
+ {
+ public:
+ MouseMovedEvent(float x, float y)
+ : m_mouse_x(x), m_mouse_y(y) { }
+
+ auto get_x() const -> float { return m_mouse_x; }
+ auto get_y() const -> float { return m_mouse_y; }
+
+ auto to_string() const -> std::string override
+ {
+ return "MouseMovedEvent: " + std::to_string(m_mouse_x) + ", " + std::to_string(m_mouse_y);
+ }
+
+ MOUSE_EVENT_CLASS_TYPE(MouseMoved)
+
+ private:
+ float m_mouse_x, m_mouse_y;
+ };
+
+ class MouseScrolledEvent
+ : public Event
+ {
+ public:
+ MouseScrolledEvent(float x_offset, float y_offset)
+ : m_x_offset(x_offset), m_y_offset(y_offset) { }
+
+ auto get_x_offset() const -> float { return m_x_offset; }
+ auto get_y_offset() const -> float { return m_y_offset; }
+
+ auto to_string() const -> std::string override
+ {
+ return "MouseScrolledEvent: " + std::to_string(m_x_offset) +
+ ", " + std::to_string(m_y_offset);
+ }
+
+ MOUSE_EVENT_CLASS_TYPE(MouseScrolled)
+
+ private:
+ float m_x_offset, m_y_offset;
+ };
+
+ class MouseButtonEvent
+ : public Event
+ {
+ public:
+ auto get_mouse_button() const -> int { return m_button; }
+ virtual auto get_category_flags() const -> int override { return EventCategoryMouseButton |
+ EventCategoryMouse |
+ EventCategoryInput; }
+
+ protected:
+ MouseButtonEvent(int button)
+ : m_button(button) { }
+ int m_button;
+ };
+
+ class MouseButtonPressedEvent
+ : public MouseButtonEvent
+ {
+ public:
+ MouseButtonPressedEvent(int button)
+ : MouseButtonEvent(button) { }
+
+ auto to_string() const -> std::string override
+ {
+ return "MouseButtonPressedEvent: " + std::to_string(m_button);
+ }
+
+ MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonPressed)
+ };
+
+ class MouseButtonReleasedEvent
+ : public MouseButtonEvent
+ {
+ public:
+ MouseButtonReleasedEvent(int button)
+ : MouseButtonEvent(button) { }
+
+ auto to_string() const -> std::string override
+ {
+ return "MouseButtonReleasedEvent: " + std::to_string(m_button);
+ }
+
+ MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonReleased)
+ };
+
+ class EventDispatcher
+ {
+ public:
+ EventDispatcher(Event& event)
+ : m_event(event) { }
+
+ template<typename T, typename F>
+ auto dispatch(const F& func) -> bool
+ {
+ if (m_event.get_event_type() == T::get_static_type())
+ {
+ m_event.handled = func(static_cast<T&>(m_event));
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ Event& m_event;
+ };
+
+ class EventHandler
+ {
+ public:
+ using EventCallbackFn = std::function<void(Event&)>;
+
+ EventHandler() = default;
+ ~EventHandler() = default;
+
+ auto set_event_callback(const EventCallbackFn& callback) -> void
+ {
+ m_event_callback = callback;
+ }
+
+ auto on_event(Event& event) -> void
+ {
+ if (m_event_callback)
+ m_event_callback(event);
+ }
+
+ template<typename T>
+ auto bind_event(const std::function<void(T&)>& callback) -> void
+ {
+ m_event_callback = [callback](Event& event)
+ {
+ if (event.get_event_type() == T::get_static_type())
+ callback(static_cast<T&>(event));
+ };
+ }
+
+ private:
+ EventCallbackFn m_event_callback;
+ };
+
+ #undef EVENT_CLASS_TYPE
+ #undef APPLICATION_EVENT_CLASS_TYPE
+ #undef MOUSE_EVENT_CLASS_TYPE
+ #undef MOUSE_BUTTON_EVENT_CLASS_TYPE
+ #undef KEYBOARD_EVENT_CLASS_TYPE
+}
diff --git a/src/Core/HDRIManager.cpp b/src/core/hdri_manager.cpp
index 6e420a6..605dcf6 100644
--- a/src/Core/HDRIManager.cpp
+++ b/src/core/hdri_manager.cpp
@@ -1,23 +1,23 @@
-#include "HDRIManager.h"
+#include "hdri_manager.h"
#include <filesystem>
namespace Donut
{
- Ref<CubemapTexture> HDRIManager::LoadHDRI(const std::string& path)
+ auto HDRIManager::load_hdri(const std::string& path) -> Ref<CubemapTexture>
{
- auto it = m_HDRICache.find(path);
- if (it != m_HDRICache.end())
+ auto it = m_hdri_cache.find(path);
+ if (it != m_hdri_cache.end())
{
DONUT_INFO("HDRI already cached: {}", path);
return it->second;
}
DONUT_INFO("Loading HDRI: {}", path);
- auto hdri = CubemapTexture::CreateFromHDRI(path);
-
+ auto hdri = CubemapTexture::create_from_hdri(path);
+
if (hdri)
{
- m_HDRICache[path] = hdri;
+ m_hdri_cache[path] = hdri;
DONUT_INFO("Successfully loaded and cached HDRI: {}", path);
}
else
@@ -26,24 +26,24 @@ namespace Donut
return hdri;
}
- void HDRIManager::SetCurrentHDRI(const std::string& path)
+ auto HDRIManager::set_current_hdri(const std::string& path) -> void
{
- auto hdri = LoadHDRI(path);
+ auto hdri = load_hdri(path);
if (hdri)
{
- m_CurrentHDRI = hdri;
+ m_current_hdri = hdri;
DONUT_INFO("Set current HDRI to: {}", path);
}
else
DONUT_ERROR("Failed to set current HDRI: {}", path);
}
- std::string HDRIManager::GetHDRIName(const std::string& path) const
+ auto HDRIManager::get_hdri_name(const std::string& path) const -> std::string
{
- std::filesystem::path filePath(path);
- std::string filename = filePath.stem().string();
+ std::filesystem::path file_path(path);
+ std::string filename = file_path.stem().string();
std::string name = filename;
-
+
for (size_t i = 0; i < name.length(); ++i)
{
if (name[i] == '_')
@@ -51,14 +51,14 @@ namespace Donut
else if (i == 0 || name[i-1] == ' ')
name[i] = std::toupper(name[i]);
}
-
+
return name;
}
- void HDRIManager::ClearCache()
+ auto HDRIManager::clear_cache() -> void
{
- m_HDRICache.clear();
- m_CurrentHDRI.reset();
+ m_hdri_cache.clear();
+ m_current_hdri.reset();
DONUT_INFO("HDRI cache cleared");
}
};
diff --git a/src/core/hdri_manager.h b/src/core/hdri_manager.h
new file mode 100644
index 0000000..fa05330
--- /dev/null
+++ b/src/core/hdri_manager.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "core/log.h"
+#include "rendering/texture.h"
+
+#include <string>
+#include <unordered_map>
+#include <memory>
+
+namespace Donut
+{
+ class HDRIManager
+ {
+ public:
+ static auto get() -> HDRIManager&
+ {
+ static HDRIManager instance;
+ return instance;
+ }
+
+ auto load_hdri(const std::string& path) -> Ref<CubemapTexture>;
+ auto get_current_hdri() const -> Ref<CubemapTexture> { return m_current_hdri; }
+
+ auto set_current_hdri(const std::string& path) -> void;
+ auto get_available_hdri() const -> const std::vector<std::string>& { return m_available_hdri; }
+ auto get_hdri_name(const std::string& path) const -> std::string;
+ auto clear_cache() -> void;
+
+ private:
+ HDRIManager() = default;
+ ~HDRIManager() = default;
+
+ HDRIManager(const HDRIManager&) = delete;
+ HDRIManager& operator=(const HDRIManager&) = delete;
+
+ std::unordered_map<std::string, Ref<CubemapTexture>> m_hdri_cache;
+
+ Ref<CubemapTexture> m_current_hdri;
+
+ std::vector<std::string> m_available_hdri =
+ {
+ "assets/hdri/HDR_blue_nebulae-1.hdr",
+ "assets/hdri/HDR_subdued_blue_nebulae.hdr",
+ "assets/hdri/HDR_subdued_multi_nebulae.hdr",
+ "assets/hdri/night_sky.hdr"
+ };
+ };
+};
diff --git a/src/core/log.cpp b/src/core/log.cpp
new file mode 100644
index 0000000..5a0434f
--- /dev/null
+++ b/src/core/log.cpp
@@ -0,0 +1,156 @@
+#include "log.h"
+
+#include <filesystem>
+
+#if defined(DONUT_WINDOWS)
+ #include <windows.h>
+#endif
+
+namespace Donut
+{
+ Ref<Logger> Logger::s_logger;
+
+ auto Logger::init() -> void
+ {
+ s_logger = create_ref<Logger>();
+ s_logger->set_log_level(LogLevel::Info);
+ s_logger->enable_console_output(true);
+ s_logger->enable_file_output(true);
+ s_logger->set_log_file("logs/donut.log");
+
+ s_logger->log_message(LogLevel::Info, "Logging system initialized");
+ }
+
+ auto Logger::shutdown() -> void
+ {
+ if (s_logger)
+ s_logger->log_message(LogLevel::Info, "Shutting down logging system");
+ s_logger.reset();
+ }
+
+ auto Logger::get_logger() -> Ref<Logger>
+ {
+ return s_logger;
+ }
+
+ Logger::Logger()
+ : m_log_level(LogLevel::Info),
+ m_console_output(true),
+ m_file_output(false) { }
+
+ Logger::~Logger()
+ {
+ if (m_log_file.is_open())
+ m_log_file.close();
+ }
+
+ auto Logger::set_log_file(const std::string& filename) -> void
+ {
+ std::filesystem::path log_path(filename);
+ std::filesystem::create_directories(log_path.parent_path());
+
+ m_log_file.open(filename, std::ios::app);
+ if (!m_log_file.is_open())
+ std::cerr << "Failed to open log file: " << filename << std::endl;
+ }
+
+ auto Logger::get_time_stamp() -> std::string
+ {
+ auto now = std::chrono::system_clock::now();
+ auto time = std::chrono::system_clock::to_time_t(now);
+
+ std::stringstream ss;
+ ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S");
+
+ return ss.str();
+ }
+
+ auto Logger::get_log_level_string(LogLevel level) -> std::string
+ {
+ switch (level)
+ {
+ case LogLevel::trace: return "TRACE";
+ case LogLevel::Info: return "INFO";
+ case LogLevel::Warn: return "WARN";
+ case LogLevel::Err: return "ERROR";
+ case LogLevel::Fatal: return "FATAL";
+ default: return "UNKNOWN";
+ }
+ }
+
+ auto Logger::set_console_color(LogLevel level) -> void
+ {
+#if defined(DONUT_WINDOWS)
+ HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE);
+ WORD color;
+
+ switch (level) {
+ case LogLevel::trace:
+ color = FOREGROUND_RED |
+ FOREGROUND_GREEN |
+ FOREGROUND_BLUE;
+ break;
+ case LogLevel::Info:
+ color = FOREGROUND_GREEN |
+ FOREGROUND_INTENSITY;
+ break;
+ case LogLevel::Warn:
+ color = FOREGROUND_RED |
+ FOREGROUND_GREEN |
+ FOREGROUND_INTENSITY;
+ break;
+ case LogLevel::Err:
+ color = FOREGROUND_RED |
+ FOREGROUND_INTENSITY;
+ break;
+ case LogLevel::Fatal:
+ color = FOREGROUND_RED |
+ FOREGROUND_BLUE |
+ FOREGROUND_INTENSITY;
+ break;
+ default:
+ color = FOREGROUND_RED |
+ FOREGROUND_GREEN |
+ FOREGROUND_BLUE;
+ break;
+ }
+
+ SetConsoleTextAttribute(h_console, color);
+#else
+ const char* color_code;
+ switch (level) {
+ case LogLevel::trace:
+ color_code = "\033[37m";
+ break;
+ case LogLevel::Info:
+ color_code = "\033[32;1m";
+ break;
+ case LogLevel::Warn:
+ color_code = "\033[33;1m";
+ break;
+ case LogLevel::Err:
+ color_code = "\033[31;1m";
+ break;
+ case LogLevel::Fatal:
+ color_code = "\033[35;1m";
+ break;
+ default:
+ color_code = "\033[37m";
+ break;
+ }
+ std::cout << color_code;
+#endif
+ }
+
+ auto Logger::reset_console_color() -> void
+ {
+#if defined(DONUT_WINDOWS)
+ HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleTextAttribute(h_console, FOREGROUND_RED |
+ FOREGROUND_GREEN |
+ FOREGROUND_BLUE);
+#else
+ std::cout << "\033[0m";
+#endif
+ }
+}
diff --git a/src/core/log.h b/src/core/log.h
new file mode 100644
index 0000000..db6b4b4
--- /dev/null
+++ b/src/core/log.h
@@ -0,0 +1,156 @@
+#pragma once
+
+#include "core/memory.h"
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iostream>
+#include <fstream>
+#include <chrono>
+#include <iomanip>
+#include <mutex>
+#include <string_view>
+
+namespace Donut
+{
+ enum class LogLevel
+ {
+ trace = 0,
+ Info = 2,
+ Warn = 3,
+ Err = 4,
+ Fatal = 5
+ };
+
+ class Logger
+ {
+ public:
+ Logger();
+ ~Logger();
+
+ static auto init() -> void;
+ static auto shutdown() -> void;
+
+ static auto get_logger() -> Ref<Logger>;
+
+ template<typename... Args>
+ static auto trace(const std::string_view& format, const Args&... args) -> void
+ {
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::trace, format, args...);
+ }
+
+ template<typename... Args>
+ static auto info(const std::string_view& format, const Args&... args) -> void
+ {
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Info, format, args...);
+ }
+
+ template<typename... Args>
+ static auto warn(const std::string_view& format, const Args&... args) -> void
+ {
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Warn, format, args...);
+ }
+
+ template<typename... Args>
+ static auto error(const std::string_view& format, const Args&... args) -> void
+ {
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Err, format, args...);
+ }
+
+ template<typename... Args>
+ static auto fatal(const std::string_view& format, const Args&... args) -> void
+ {
+ auto logger = get_logger();
+ if (logger) logger->log_message(LogLevel::Fatal, format, args...);
+ }
+
+ template<typename... Args>
+ auto log_message(LogLevel level, const std::string_view& format, const Args&... args) -> void
+ {
+ if (level < m_log_level)
+ return;
+
+ std::lock_guard<std::mutex> lock(m_mutex);
+ std::string message = format_string(format, args...);
+ std::string full_message = get_time_stamp() + " [" + get_log_level_string(level) + "] " + message;
+
+ if (m_console_output)
+ {
+ set_console_color(level);
+ std::cout << full_message << std::endl;
+ reset_console_color();
+ }
+
+ if (m_file_output && m_log_file.is_open())
+ {
+ m_log_file << full_message << std::endl;
+ m_log_file.flush();
+ }
+ }
+
+ template<typename... Args>
+ auto format_string(const std::string_view& format, const Args&... args) -> std::string
+ {
+ std::string result = format.data();
+ std::vector<std::string> arg_strings = { to_string(args)... };
+
+ size_t arg_index = 0;
+ size_t pos = 0;
+
+ while ((pos = result.find("{}", pos)) != std::string::npos && arg_index < arg_strings.size())
+ {
+ result.replace(pos, 2, arg_strings[arg_index]);
+ pos += arg_strings[arg_index].length();
+ arg_index++;
+ }
+
+ return result;
+ }
+
+ template<typename T>
+ auto to_string(const T& value) -> std::string
+ {
+ std::stringstream ss;
+ ss << value;
+ return ss.str();
+ }
+
+ auto set_log_level(LogLevel level) -> void { m_log_level = level; }
+ auto enable_console_output(bool enable) -> void { m_console_output = enable; }
+ auto enable_file_output(bool enable) -> void { m_file_output = enable; }
+ auto set_log_file(const std::string& filename) -> void;
+
+ auto get_time_stamp() -> std::string;
+ auto get_log_level_string(LogLevel level) -> std::string;
+ auto set_console_color(LogLevel level) -> void;
+ auto reset_console_color() -> void;
+
+ private:
+ LogLevel m_log_level;
+ bool m_console_output;
+ bool m_file_output;
+ std::ofstream m_log_file;
+ std::mutex m_mutex;
+
+ static Ref<Logger> s_logger;
+ };
+}
+
+#if defined(DONUT_DEBUG)
+ #define DONUT_TRACE(format, ...) ::Donut::Logger::trace(format, ##__VA_ARGS__)
+ #define DONUT_INFO(format, ...) ::Donut::Logger::info(format, ##__VA_ARGS__)
+ #define DONUT_WARN(format, ...) ::Donut::Logger::warn(format, ##__VA_ARGS__)
+ #define DONUT_ERROR(format, ...) ::Donut::Logger::error(format, ##__VA_ARGS__)
+ #define DONUT_FATAL(format, ...) ::Donut::Logger::fatal(format, ##__VA_ARGS__)
+#else
+ #define DONUT_TRACE(format, ...) {}
+ #define DONUT_INFO(format, ...) {}
+ #define DONUT_WARN(format, ...) {}
+ #define DONUT_ERROR(format, ...) {}
+ #define DONUT_FATAL(format, ...) {}
+#endif
diff --git a/src/core/memory.h b/src/core/memory.h
new file mode 100644
index 0000000..3240c71
--- /dev/null
+++ b/src/core/memory.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <memory>
+
+namespace Donut
+{
+ template<typename T>
+ using Ref = std::shared_ptr<T>;
+
+ template<typename T>
+ using Scope = std::unique_ptr<T>;
+
+ template<typename T, typename ... Args>
+ constexpr auto create_scope(Args&& ... args) -> Scope<T>
+ {
+ return std::make_unique<T>(std::forward<Args>(args)...);
+ }
+
+ template<typename T, typename ... Args>
+ constexpr auto create_ref(Args&& ... args) -> Ref<T>
+ {
+ return std::make_shared<T>(std::forward<Args>(args)...);
+ }
+};
diff --git a/src/core/settings_manager.cpp b/src/core/settings_manager.cpp
new file mode 100644
index 0000000..ca4cac5
--- /dev/null
+++ b/src/core/settings_manager.cpp
@@ -0,0 +1,200 @@
+#include "settings_manager.h"
+#include "log.h"
+#include "theme_manager.h"
+#include "rendering/renderer.h"
+
+#include <fstream>
+#include <filesystem>
+#include <algorithm>
+
+namespace Donut
+{
+ Settings SettingsManager::s_settings;
+ bool SettingsManager::s_initialized = false;
+
+ auto SettingsManager::initialize() -> void
+ {
+ if (s_initialized)
+ return;
+
+ load_settings();
+ s_initialized = true;
+ DONUT_INFO("Settings Manager initialized");
+ }
+
+ auto SettingsManager::shutdown() -> void
+ {
+ if (!s_initialized)
+ return;
+
+ save_settings();
+ s_initialized = false;
+ DONUT_INFO("Settings Manager shutdown");
+ }
+
+ auto SettingsManager::load_settings() -> void
+ {
+ std::string file_path = get_settings_file_path();
+
+ try
+ {
+ if (std::filesystem::exists(file_path))
+ {
+ auto config = toml::parse(file_path);
+
+ if (config.contains("simulation"))
+ {
+ auto sim = config["simulation"];
+ s_settings.simulation.target_fps = toml::find_or(sim, "target_fps", 60);
+ s_settings.simulation.compute_height = toml::find_or(sim, "compute_height", 512);
+ s_settings.simulation.max_steps_moving = toml::find_or(sim, "max_steps_moving", 30000);
+ s_settings.simulation.max_steps_static = toml::find_or(sim, "max_steps_static", 15000);
+ s_settings.simulation.early_exit_distance = toml::find_or(sim, "early_exit_distance", 5e12f);
+ s_settings.simulation.gravity_enabled = toml::find_or(sim, "gravity_enabled", true);
+ s_settings.simulation.disk_thickness = toml::find_or(sim, "disk_thickness", 0.1f);
+ s_settings.simulation.disk_density = toml::find_or(sim, "disk_density", 0.1f);
+ s_settings.simulation.rotation_speed = toml::find_or(sim, "rotation_speed", 1.0f);
+ s_settings.simulation.blur_strength = toml::find_or(sim, "blur_strength", 2.0f);
+ s_settings.simulation.glow_intensity = toml::find_or(sim, "glow_intensity", 0.1f);
+
+ s_settings.simulation.target_fps = std::max(30, std::min(120, s_settings.simulation.target_fps));
+ s_settings.simulation.compute_height = std::max(64, std::min(2048, s_settings.simulation.compute_height));
+ s_settings.simulation.max_steps_moving = std::max(1000, std::min(60000, s_settings.simulation.max_steps_moving));
+ s_settings.simulation.max_steps_static = std::max(1000, std::min(30000, s_settings.simulation.max_steps_static));
+ s_settings.simulation.early_exit_distance = std::max(1e11f, std::min(1e13f, s_settings.simulation.early_exit_distance));
+ s_settings.simulation.disk_thickness = std::max(0.01f, std::min(5.0f, s_settings.simulation.disk_thickness));
+ s_settings.simulation.disk_density = std::max(0.01f, std::min(5.0f, s_settings.simulation.disk_density));
+ s_settings.simulation.rotation_speed = std::max(0.0f, std::min(5.0f, s_settings.simulation.rotation_speed));
+ s_settings.simulation.blur_strength = std::max(0.1f, std::min(10.0f, s_settings.simulation.blur_strength));
+ s_settings.simulation.glow_intensity = std::max(0.01f, std::min(5.0f, s_settings.simulation.glow_intensity));
+ }
+
+ if (config.contains("graphics"))
+ {
+ auto gfx = config["graphics"];
+ s_settings.graphics.render_api = toml::find_or(gfx, "render_api", std::string("OpenGL"));
+ s_settings.graphics.v_sync_enabled = toml::find_or(gfx, "vsync_enabled", true);
+ s_settings.graphics.show_fps = toml::find_or(gfx, "show_fps", true);
+ s_settings.graphics.show_performance_metrics = toml::find_or(gfx, "show_performance_metrics", true);
+ s_settings.graphics.show_debug_info = toml::find_or(gfx, "show_debug_info", false);
+ s_settings.graphics.enable_anti_aliasing = toml::find_or(gfx, "enable_anti_aliasing", true);
+ s_settings.graphics.selected_theme = toml::find_or(gfx, "selected_theme", std::string("Dark"));
+
+ if (s_settings.graphics.render_api != "OpenGL" &&
+ s_settings.graphics.render_api != "Vulkan")
+ s_settings.graphics.render_api = "OpenGL";
+ if (s_settings.graphics.selected_theme != "Dark" &&
+ s_settings.graphics.selected_theme != "Light" &&
+ s_settings.graphics.selected_theme != "Blue")
+ s_settings.graphics.selected_theme = "Dark";
+ }
+
+ DONUT_INFO("Settings loaded from {}", file_path);
+ }
+ else
+ {
+ load_default_settings();
+ save_settings();
+ DONUT_INFO("No settings file found, created default settings");
+ }
+ }
+ catch (const std::exception& e)
+ {
+ DONUT_ERROR("Failed to load settings: {}", e.what());
+ load_default_settings();
+ }
+ }
+
+ auto SettingsManager::save_settings() -> void
+ {
+ std::string file_path = get_settings_file_path();
+
+ try
+ {
+ std::filesystem::path path(file_path);
+ std::filesystem::create_directories(path.parent_path());
+
+ toml::value simulation = toml::table
+ {
+ {"target_fps", s_settings.simulation.target_fps },
+ {"compute_height", s_settings.simulation.compute_height },
+ {"max_steps_moving", s_settings.simulation.max_steps_moving },
+ {"max_steps_static", s_settings.simulation.max_steps_static },
+ {"early_exit_distance", s_settings.simulation.early_exit_distance},
+ {"gravity_enabled", s_settings.simulation.gravity_enabled },
+ {"disk_thickness", s_settings.simulation.disk_thickness },
+ {"disk_density", s_settings.simulation.disk_density },
+ {"rotation_speed", s_settings.simulation.rotation_speed },
+ {"blur_strength", s_settings.simulation.blur_strength },
+ {"glow_intensity", s_settings.simulation.glow_intensity }
+ };
+
+ toml::value graphics = toml::table
+ {
+ {"render_api", s_settings.graphics.render_api },
+ {"vsync_enabled", s_settings.graphics.v_sync_enabled },
+ {"show_fps", s_settings.graphics.show_fps },
+ {"show_performance_metrics", s_settings.graphics.show_performance_metrics},
+ {"show_debug_info", s_settings.graphics.show_debug_info },
+ {"enable_anti_aliasing", s_settings.graphics.enable_anti_aliasing },
+ {"selected_theme", s_settings.graphics.selected_theme }
+ };
+
+ toml::value config = toml::table
+ {
+ {"simulation", simulation},
+ {"graphics", graphics}
+ };
+
+ std::ofstream file(file_path);
+ file << config;
+ file.close();
+
+ DONUT_INFO("Settings saved to {}", file_path);
+ }
+ catch (const std::exception& e)
+ {
+ DONUT_ERROR("Failed to save settings: {}", e.what());
+ }
+ }
+
+ auto SettingsManager::set_simulation_settings(const SimulationSettings& settings) -> void
+ {
+ s_settings.simulation = settings;
+ save_settings();
+ }
+
+ auto SettingsManager::set_graphics_settings(const GraphicsSettings& settings) -> void
+ {
+ s_settings.graphics = settings;
+ save_settings();
+ }
+
+ auto SettingsManager::get_settings_file_path() -> std::string
+ {
+ return "config/settings.toml";
+ }
+
+ auto SettingsManager::load_default_settings() -> void
+ {
+ s_settings.simulation.target_fps = 60;
+ s_settings.simulation.compute_height = 512;
+ s_settings.simulation.max_steps_moving = 30000;
+ s_settings.simulation.max_steps_static = 15000;
+ s_settings.simulation.early_exit_distance = 5e12f;
+ s_settings.simulation.gravity_enabled = true;
+ s_settings.simulation.disk_thickness = 0.1f;
+ s_settings.simulation.disk_density = 0.1f;
+ s_settings.simulation.rotation_speed = 1.0f;
+ s_settings.simulation.blur_strength = 2.0f;
+ s_settings.simulation.glow_intensity = 0.1f;
+
+ s_settings.graphics.render_api = "OpenGL";
+ s_settings.graphics.v_sync_enabled = true;
+ s_settings.graphics.show_fps = true;
+ s_settings.graphics.show_performance_metrics = true;
+ s_settings.graphics.show_debug_info = false;
+ s_settings.graphics.enable_anti_aliasing = true;
+ s_settings.graphics.selected_theme = "Dark";
+ }
+}
diff --git a/src/core/settings_manager.h b/src/core/settings_manager.h
new file mode 100644
index 0000000..95826c7
--- /dev/null
+++ b/src/core/settings_manager.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <string>
+#include <toml.hpp>
+
+namespace Donut
+{
+ struct SimulationSettings
+ {
+ int target_fps = 60;
+ int compute_height = 512;
+ int max_steps_moving = 30000;
+ int max_steps_static = 15000;
+ float early_exit_distance = 5e12f;
+ bool gravity_enabled = true;
+ float disk_thickness = 0.1f;
+ float disk_density = 0.1f;
+ float rotation_speed = 1.0f;
+ float blur_strength = 2.0f;
+ float glow_intensity = 0.1f;
+ };
+
+ struct GraphicsSettings
+ {
+ std::string render_api = "OpenGL";
+ bool v_sync_enabled = true;
+ bool show_fps = true;
+ bool show_performance_metrics = true;
+ bool show_debug_info = false;
+ bool enable_anti_aliasing = true;
+ std::string selected_theme = "Dark";
+ };
+
+ struct Settings
+ {
+ SimulationSettings simulation;
+ GraphicsSettings graphics;
+ };
+
+ class SettingsManager
+ {
+ public:
+ static auto initialize() -> void;
+ static auto shutdown() -> void;
+
+ static auto load_settings() -> void;
+ static auto save_settings() -> void;
+
+ static auto get_settings() -> Settings& { return s_settings; }
+ static auto get_settings_const() -> const Settings& { return s_settings; }
+
+ static auto set_simulation_settings(const SimulationSettings& settings) -> void;
+ static auto set_graphics_settings(const GraphicsSettings& settings) -> void;
+
+ static auto get_target_fps() -> int { return s_settings.simulation.target_fps; }
+ static auto get_compute_height() -> int { return s_settings.simulation.compute_height; }
+ static auto get_max_steps_moving() -> int { return s_settings.simulation.max_steps_moving; }
+ static auto get_max_steps_static() -> int { return s_settings.simulation.max_steps_static; }
+ static auto get_early_exit_distance() -> float { return s_settings.simulation.early_exit_distance; }
+ static auto get_gravity_enabled() -> bool { return s_settings.simulation.gravity_enabled; }
+ static auto get_disk_thickness() -> float { return s_settings.simulation.disk_thickness; }
+ static auto get_disk_density() -> float { return s_settings.simulation.disk_density; }
+ static auto get_rotation_speed() -> float { return s_settings.simulation.rotation_speed; }
+ static auto get_blur_strength() -> float { return s_settings.simulation.blur_strength; }
+ static auto get_glow_intensity() -> float { return s_settings.simulation.glow_intensity; }
+ static auto get_render_api() -> std::string { return s_settings.graphics.render_api; }
+ static auto get_v_sync_enabled() -> bool { return s_settings.graphics.v_sync_enabled; }
+ static auto get_show_fps() -> bool { return s_settings.graphics.show_fps; }
+ static auto get_show_performance_metrics() -> bool { return s_settings.graphics.show_performance_metrics; }
+ static auto get_show_debug_info() -> bool { return s_settings.graphics.show_debug_info; }
+ static auto get_enable_anti_aliasing() -> bool { return s_settings.graphics.enable_anti_aliasing; }
+ static auto get_selected_theme() -> std::string { return s_settings.graphics.selected_theme; }
+
+ private:
+ static auto get_settings_file_path() -> std::string;
+ static auto load_default_settings() -> void;
+
+ private:
+ static Settings s_settings;
+ static bool s_initialized;
+ };
+}
diff --git a/src/core/state.h b/src/core/state.h
new file mode 100644
index 0000000..623f1c2
--- /dev/null
+++ b/src/core/state.h
@@ -0,0 +1,19 @@
+#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
new file mode 100644
index 0000000..8018401
--- /dev/null
+++ b/src/core/state_manager.cpp
@@ -0,0 +1,88 @@
+#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
new file mode 100644
index 0000000..446841e
--- /dev/null
+++ b/src/core/state_manager.h
@@ -0,0 +1,41 @@
+#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/Core/ThemeManager.cpp b/src/core/theme_manager.cpp
index e7e2f9e..4ae025f 100644
--- a/src/Core/ThemeManager.cpp
+++ b/src/core/theme_manager.cpp
@@ -1,28 +1,28 @@
-#include "ThemeManager.h"
+#include "theme_manager.h"
namespace Donut
{
- Theme ThemeManager::s_CurrentTheme = Theme::Dark;
+ Theme ThemeManager::s_current_theme = Theme::Dark;
- void ThemeManager::SetTheme(Theme theme)
+ auto ThemeManager::set_theme(Theme theme) -> void
{
- s_CurrentTheme = theme;
+ s_current_theme = theme;
switch (theme)
{
case Theme::Dark:
- ApplyDarkTheme();
+ apply_dark_theme();
break;
case Theme::Light:
- ApplyLightTheme();
+ apply_light_theme();
break;
case Theme::Blue:
- ApplyBlueTheme();
+ apply_blue_theme();
break;
}
}
- void ThemeManager::ApplyDarkTheme()
+ auto ThemeManager::apply_dark_theme() -> void
{
ImGuiStyle& style = ImGui::GetStyle();
ImVec4* colors = style.Colors;
@@ -111,7 +111,7 @@ namespace Donut
style.DisabledAlpha = 0.6f;
}
- void ThemeManager::ApplyLightTheme()
+ auto ThemeManager::apply_light_theme() -> void
{
ImGuiStyle& style = ImGui::GetStyle();
ImVec4* colors = style.Colors;
@@ -200,7 +200,7 @@ namespace Donut
style.DisabledAlpha = 0.6f;
}
- void ThemeManager::ApplyBlueTheme()
+ auto ThemeManager::apply_blue_theme() -> void
{
ImGuiStyle& style = ImGui::GetStyle();
ImVec4* colors = style.Colors;
diff --git a/src/core/theme_manager.h b/src/core/theme_manager.h
new file mode 100644
index 0000000..d067906
--- /dev/null
+++ b/src/core/theme_manager.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <imgui.h>
+
+namespace Donut
+{
+ enum class Theme
+ {
+ Dark = 0,
+ Light = 1,
+ Blue = 2
+ };
+
+ class ThemeManager
+ {
+ public:
+ static auto set_theme(Theme theme) -> void;
+ static auto get_current_theme() -> Theme { return s_current_theme; }
+
+ static auto apply_dark_theme() -> void;
+ static auto apply_light_theme() -> void;
+ static auto apply_blue_theme() -> void;
+
+ private:
+ static Theme s_current_theme;
+ };
+}
diff --git a/src/core/window.cpp b/src/core/window.cpp
new file mode 100644
index 0000000..047ef0d
--- /dev/null
+++ b/src/core/window.cpp
@@ -0,0 +1,341 @@
+#include <glad/glad.h>
+#include "window.h"
+#include "theme_manager.h"
+#include "rendering/renderer.h" // RendererAPI::get_api()
+
+#include <cstdint>
+
+#include <imgui.h>
+#include <imgui_impl_glfw.h>
+#include <imgui_impl_opengl3.h>
+
+namespace Donut
+{
+ static bool s_glfw_initialized = false;
+ static uint32_t s_glfw_window_count = 0;
+
+ Window::Window(const std::string& title, int width, int height)
+ : m_title(title), m_width(width),
+ m_height(height), m_is_closed(false)
+ {
+ init();
+ }
+
+ Window::~Window()
+ {
+ shutdown();
+ }
+
+ auto Window::init() -> void
+ {
+ DONUT_INFO("Initializing window: ", m_title, " (", m_width, "x", m_height, ")");
+
+ if (!s_glfw_initialized)
+ {
+ int success = glfwInit();
+ if (!success)
+ {
+ DONUT_ERROR("Could not initialize GLFW!");
+ return;
+ }
+
+ s_glfw_initialized = true;
+ DONUT_INFO("GLFW initialized successfully");
+ }
+
+ if (RendererAPI::get_api() == RendererAPI::API::Vulkan)
+ {
+ // Vulkan manages presentation itself; GLFW must not create a GL context.
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ }
+ else
+ {
+#ifdef __APPLE__
+ // macOS only exposes OpenGL up to 4.1 Core Profile, and requires a
+ // forward-compatible core-profile context for any modern (>= 3.3)
+ // shader to compile. Without these hints GLFW hands back a legacy
+ // 2.1 context and every GLSL shader in the project fails to build.
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
+#endif
+ }
+
+ m_window = glfwCreateWindow(m_width, m_height, m_title.c_str(), nullptr, nullptr);
+ if (!m_window)
+ {
+ DONUT_ERROR("Could not create GLFW window!");
+ glfwTerminate();
+ return;
+ }
+
+ DONUT_INFO("GLFW window created successfully");
+
+ if (RendererAPI::get_api() != RendererAPI::API::Vulkan)
+ glfwMakeContextCurrent(m_window);
+ glfwSetWindowUserPointer(m_window, this);
+
+ glfwSetErrorCallback(glfw_error_callback);
+ glfwSetWindowCloseCallback(m_window, glfw_window_close_callback);
+ glfwSetWindowSizeCallback(m_window, glfw_window_size_callback);
+ glfwSetWindowFocusCallback(m_window, glfw_window_focus_callback);
+ glfwSetWindowPosCallback(m_window, glfw_window_pos_callback);
+ glfwSetKeyCallback(m_window, glfw_key_callback);
+ glfwSetCharCallback(m_window, glfw_char_callback);
+ glfwSetMouseButtonCallback(m_window, glfw_mouse_button_callback);
+ glfwSetScrollCallback(m_window, glfw_mouse_scroll_callback);
+ glfwSetCursorPosCallback(m_window, glfw_cursor_pos_callback);
+
+ s_glfw_window_count++;
+ }
+
+ auto Window::shutdown() -> void
+ {
+ DONUT_INFO("Shutting down window: ", m_title);
+
+ shutdown_im_gui();
+
+ glfwDestroyWindow(m_window);
+ s_glfw_window_count--;
+
+ if (s_glfw_window_count == 0)
+ {
+ glfwTerminate();
+ s_glfw_initialized = false;
+ DONUT_INFO("GLFW terminated (no more windows)");
+ }
+ }
+
+ auto Window::on_update() const -> void
+ {
+ glfwPollEvents();
+ if (RendererAPI::get_api() != RendererAPI::API::Vulkan)
+ glfwSwapBuffers(m_window); // Vulkan presents via the swapchain instead
+ }
+
+ auto Window::should_close() const -> bool
+ {
+ return glfwWindowShouldClose(m_window) || m_is_closed;
+ }
+
+ auto Window::set_cursor_locked(bool locked) -> void
+ {
+ m_cursor_locked = locked;
+ glfwSetInputMode(m_window, GLFW_CURSOR, locked ?
+ GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
+ }
+
+ auto Window::set_cursor_visible(bool visible) -> void
+ {
+ m_cursor_visible = visible;
+ glfwSetInputMode(m_window, GLFW_CURSOR, visible ?
+ GLFW_CURSOR_NORMAL : GLFW_CURSOR_HIDDEN);
+ }
+
+ auto Window::init_im_gui() -> void
+ {
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+ ImGuiIO& io = ImGui::GetIO();
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+ io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
+#ifndef __APPLE__
+ // Multi-viewport (dragging ImGui panels out as separate OS windows)
+ // relies on a populated platform-monitor list and is unreliable on
+ // macOS, where it intermittently asserts (Monitors.Size > 0) and
+ // aborts. Docking stays enabled; panels just remain inside the window.
+ io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
+#endif
+
+ setup_im_gui_fonts();
+ ThemeManager::set_theme(Theme::Dark);
+
+ ImGuiStyle& style = ImGui::GetStyle();
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ {
+ style.WindowRounding = 0.0f;
+ style.Colors[ImGuiCol_WindowBg].w = 1.0f;
+ }
+
+ ImGui_ImplGlfw_InitForOpenGL(m_window, true);
+#ifdef __APPLE__
+ // macOS uses a core-profile context, which rejects the legacy
+ // "#version 130" GLSL the ImGui backend defaults to. 150 is the
+ // minimum core-profile GLSL that macOS's OpenGL 4.1 accepts.
+ ImGui_ImplOpenGL3_Init("#version 150");
+#else
+ ImGui_ImplOpenGL3_Init("#version 130");
+#endif
+
+ DONUT_INFO("ImGUI initialized successfully");
+ }
+
+ auto Window::setup_im_gui_fonts() -> void
+ {
+ ImGuiIO& io = ImGui::GetIO();
+ io.Fonts->Clear();
+
+ m_main_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Regular.ttf", 16.0f);
+ if (!m_main_font)
+ {
+ DONUT_WARN("Failed to load Inter font, falling back to default");
+ m_main_font = io.Fonts->AddFontDefault();
+ }
+ else
+ DONUT_INFO("Successfully loaded Inter font");
+
+ m_large_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Bold.ttf", 20.0f);
+ if (!m_large_font)
+ m_large_font = m_main_font;
+
+ m_small_font = io.Fonts->AddFontFromFileTTF("assets/fonts/inter/static/Inter_18pt-Light.ttf", 12.0f);
+ if (!m_small_font)
+ m_small_font = m_main_font;
+
+ io.FontDefault = m_main_font;
+ DONUT_INFO("ImGUI fonts loaded successfully");
+ }
+
+ auto Window::shutdown_im_gui() -> void
+ {
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+
+ DONUT_INFO("ImGUI shutdown");
+ }
+
+ auto Window::begin_im_gui_frame() -> void
+ {
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+ }
+
+ auto Window::end_im_gui_frame() -> void
+ {
+ ImGui::Render();
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ ImGuiIO& io = ImGui::GetIO();
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
+ {
+ GLFWwindow* backup_current_context = glfwGetCurrentContext();
+ ImGui::UpdatePlatformWindows();
+ ImGui::RenderPlatformWindowsDefault();
+ glfwMakeContextCurrent(backup_current_context);
+ }
+ }
+
+ auto Window::glfw_error_callback(int error, const char* description) -> void
+ {
+ DONUT_ERROR("GLFW Error ({}): {}", error, description ? description : "");
+ }
+
+ auto Window::glfw_window_close_callback(GLFWwindow* window) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ WindowCloseEvent event;
+ win->m_event_handler.on_event(event);
+ }
+
+ auto Window::glfw_window_size_callback(GLFWwindow* window, int width, int height) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ win->m_width = width;
+ win->m_height = height;
+
+ WindowResizeEvent event(width, height);
+ win->m_event_handler.on_event(event);
+ }
+
+ auto Window::glfw_window_focus_callback(GLFWwindow* window, int focused) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ if (focused)
+ {
+ WindowFocusEvent event;
+ win->m_event_handler.on_event(event);
+ }
+ else
+ {
+ WindowLostFocusEvent event;
+ win->m_event_handler.on_event(event);
+ }
+ }
+
+ auto Window::glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ WindowMovedEvent event(xpos, ypos);
+ win->m_event_handler.on_event(event);
+ }
+
+ auto Window::glfw_key_callback(GLFWwindow* window, int key, int scancode,
+ int action, int mods) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ switch (action)
+ {
+ case GLFW_PRESS:
+ {
+ KeyPressedEvent event(key, false);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_RELEASE:
+ {
+ KeyReleasedEvent event(key);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_REPEAT:
+ {
+ KeyPressedEvent event(key, true);
+ win->m_event_handler.on_event(event);
+ } break;
+ }
+ }
+
+ auto Window::glfw_char_callback(GLFWwindow* window, unsigned int keycode) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ KeyTypedEvent event(keycode);
+ win->m_event_handler.on_event(event);
+ }
+
+ auto Window::glfw_mouse_button_callback(GLFWwindow* window, int button,
+ int action, int mods) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+
+ switch (action)
+ {
+ case GLFW_PRESS:
+ {
+ MouseButtonPressedEvent event(button);
+ win->m_event_handler.on_event(event);
+ } break;
+ case GLFW_RELEASE:
+ {
+ MouseButtonReleasedEvent event(button);
+ win->m_event_handler.on_event(event);
+ } break;
+ }
+ }
+
+ auto Window::glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ MouseScrolledEvent event((float)x_offset, (float)y_offset);
+ win->m_event_handler.on_event(event);
+ }
+
+ auto Window::glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos) -> void
+ {
+ Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window));
+ MouseMovedEvent event((float)x_pos, (float)y_pos);
+ win->m_event_handler.on_event(event);
+ }
+}
diff --git a/src/core/window.h b/src/core/window.h
new file mode 100644
index 0000000..9b7d7b9
--- /dev/null
+++ b/src/core/window.h
@@ -0,0 +1,78 @@
+#pragma once
+
+#include "event.h"
+#include "log.h"
+
+#include <GLFW/glfw3.h>
+#include <imgui.h>
+
+#include <string>
+
+namespace Donut
+{
+ class Window
+ {
+ public:
+ using EventCallbackFn = std::function<void(Event&)>;
+
+ Window(const std::string& title, int width, int height);
+ ~Window();
+
+ auto should_close() const -> bool;
+ auto on_update() const -> void;
+
+ auto set_event_callback(const EventCallbackFn& callback) -> void
+ {
+ m_event_handler.set_event_callback(callback);
+ }
+
+ auto get_event_handler() -> EventHandler& { return m_event_handler; }
+
+ auto get_width() const -> unsigned int { return m_width; }
+ auto get_height() const -> unsigned int { return m_height; }
+
+ auto get_native_window() const -> void* { return m_window; }
+
+ auto set_cursor_locked(bool locked) -> void;
+ auto set_cursor_visible(bool visible) -> void;
+ auto is_cursor_locked() const -> bool { return m_cursor_locked; }
+ auto is_cursor_visible() const -> bool { return m_cursor_visible; }
+
+ auto init_im_gui() -> void;
+ auto begin_im_gui_frame() -> void;
+ auto end_im_gui_frame() -> void;
+
+ private:
+ auto init() -> void;
+ auto shutdown() -> void;
+
+ auto setup_im_gui_fonts() -> void;
+ auto shutdown_im_gui() -> void;
+
+ static auto glfw_error_callback(int error, const char* description) -> void;
+ static auto glfw_window_close_callback(GLFWwindow* window) -> void;
+ static auto glfw_window_size_callback(GLFWwindow* window, int width, int height) -> void;
+ static auto glfw_window_focus_callback(GLFWwindow* window, int focused) -> void;
+ static auto glfw_window_pos_callback(GLFWwindow* window, int xpos, int ypos) -> void;
+ static auto glfw_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) -> void;
+ static auto glfw_char_callback(GLFWwindow* window, unsigned int keycode) -> void;
+ static auto glfw_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) -> void;
+ static auto glfw_mouse_scroll_callback(GLFWwindow* window, double x_offset, double y_offset) -> void;
+ static auto glfw_cursor_pos_callback(GLFWwindow* window, double x_pos, double y_pos) -> void;
+
+ private:
+ GLFWwindow* m_window;
+ EventHandler m_event_handler;
+
+ std::string m_title;
+ unsigned int m_width;
+ unsigned int m_height;
+ bool m_is_closed;
+ bool m_cursor_locked = false;
+ bool m_cursor_visible = true;
+
+ ImFont* m_main_font = nullptr;
+ ImFont* m_small_font = nullptr;
+ ImFont* m_large_font = nullptr;
+ };
+}
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
new file mode 100644
index 0000000..059fdda
--- /dev/null
+++ b/src/engine/engine.cpp
@@ -0,0 +1,614 @@
+#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) }
+ };
+
+ // The geodesic ray tracer used to be a compute shader; it is now a
+ // fullscreen vertex+fragment pass (see dispatch_compute) so it runs on
+ // macOS OpenGL 4.1, which has no compute shaders.
+ m_compute_program = Ref<Shader>(Shader::create("assets/shaders/Geodesic.glsl"));
+ m_shader_program = Ref<Shader>(Shader::create("assets/shaders/TexturedQuad.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) * 5, 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 = QuadVAO();
+ m_quad_vao = result.first;
+ m_texture = result.second;
+
+ // GLSL 4.10 forbids explicit binding qualifiers on uniform blocks, so
+ // associate the geodesic shader's blocks with their UBO binding points
+ // from the host side instead.
+ 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__
+ // macOS aborts any GPU submission that runs longer than a couple of
+ // seconds ("GPU Hang"). The geodesic ray-marcher can far exceed that in
+ // a single fullscreen draw, so render it in scissored tiles and flush
+ // after each, keeping every submission short enough to survive the
+ // watchdog. Compute-capable platforms draw it in one pass.
+ // Largest tile that keeps a tile's worst-case work (tile^2 * step_cap)
+ // inside the safe watchdog zone measured on this GPU (~25M pixel-steps
+ // per submission); bigger tiles mean fewer glFinish stalls.
+ 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;
+
+ // Render the geodesic pass into m_texture through an FBO. This replaces
+ // the old compute dispatch + image_store path, which relied on OpenGL
+ // 4.3 compute and 4.2 image load/store that macOS does not provide.
+ 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 fwd = glm::normalize(cam.get_orbital_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>(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
+ {
+ 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 = static_cast<float>(m_sag_a.m_rs * m_disk_thickness);
+ float disk_data[5] = { r1, r2, num, thickness, m_disk_density };
+
+ 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_max_steps_moving;
+ data.max_steps_static = m_max_steps_static;
+ data.early_exit_distance = m_early_exit_distance;
+ data.time = static_cast<float>(glfwGetTime()) * m_rotation_speed;
+
+#ifdef __APPLE__
+ // macOS has no compute shaders, so the geodesic pass runs as a tiled
+ // fragment shader under the OS GPU watchdog. The stock step counts
+ // (up to 30000) make a single tile exceed the watchdog and hang the
+ // GPU, so cap them here. Windows/Linux keep the full step count.
+ // While the camera moves, render cheaply so interaction stays smooth;
+ // when it settles, spend more steps for a cleaner image. Both stay well
+ // under the per-tile GPU-watchdog budget (see draw_geodesic_pass).
+ data.max_steps_moving = std::min(data.max_steps_moving, 4000);
+ 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::QuadVAO() -> std::pair<Ref<VertexArray>, Ref<Texture2D>>
+ {
+ float quad_vertices[] =
+ {
+ // Positions // TexCoords
+ -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); // Position (x, y)
+ layout.push<float>(2); // TexCoord (u, v)
+ 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;
+ }
+
+ // Render the geodesic pass into high_res_texture through the geodesic FBO.
+ 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);
+
+ // Display the rendered frame into the high-res framebuffer for read-back.
+ 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]; // R
+ flipped_pixels[dst_index + 1] = pixels[src_index + 1]; // G
+ flipped_pixels[dst_index + 2] = pixels[src_index + 2]; // B
+ flipped_pixels[dst_index + 3] = pixels[src_index + 3]; // A
+ }
+ }
+
+ 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
new file mode 100644
index 0000000..c7d02b5
--- /dev/null
+++ b/src/engine/engine.h
@@ -0,0 +1,184 @@
+#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/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__
+ // Without compute shaders the geodesic pass runs as a tiled
+ // fragment shader (see draw_geodesic_pass), so very high working
+ // resolutions make each frame take many seconds. Cap it on macOS.
+ 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; }
+ private:
+ auto create_compute_program(const char* path) -> Ref<Shader>;
+ std::pair<Ref<VertexArray>, Ref<Texture2D>> QuadVAO();
+
+ // Draws the bound geodesic shader over a cw x ch target. On macOS this
+ // is split into scissored tiles (with a flush each) so no single GPU
+ // submission trips the OS watchdog; elsewhere it is one fast draw.
+ 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;
+
+ // FBO used to render the geodesic pass into m_texture. The geodesic
+ // shader is a fragment shader (macOS has no compute), so it draws a
+ // fullscreen quad into this framebuffer instead of dispatching compute.
+ 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;
+
+ 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
new file mode 100644
index 0000000..8101540
--- /dev/null
+++ b/src/engine/object.h
@@ -0,0 +1,76 @@
+#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
new file mode 100644
index 0000000..4b41203
--- /dev/null
+++ b/src/engine/scene.h
@@ -0,0 +1,70 @@
+#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/main.cpp b/src/main.cpp
index 85554e4..a59cf8b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,8 +1,8 @@
-#include "Core/Application.h"
+#include "core/application.h"
int main()
{
Donut::Application* app = new Donut::Application("Donut Engine - Black Hole Simulation", 1280, 720);
- app->Run();
+ app->run();
delete app;
} \ No newline at end of file
diff --git a/src/Platform/Metal/MetalContext.h b/src/platform/metal/metal_context.h
index 5d9eb08..f1e376b 100644
--- a/src/Platform/Metal/MetalContext.h
+++ b/src/platform/metal/metal_context.h
@@ -8,9 +8,9 @@ namespace Donut
// logs its capabilities, proving the Metal toolchain and build integration
// work natively on Apple Silicon. This grows into the Metal compute island
// that runs the geodesic ray tracer in real time.
- bool MetalProbe();
+ auto metal_probe() -> bool;
// Compiles + dispatches a trivial compute kernel and validates the read-back,
// proving the Metal compute path the geodesic ray tracer will run on.
- bool MetalComputeSelfTest();
+ auto metal_compute_self_test() -> bool;
}
diff --git a/src/Platform/Metal/MetalContext.mm b/src/platform/metal/metal_context.mm
index 1bb9e70..71e8551 100644
--- a/src/Platform/Metal/MetalContext.mm
+++ b/src/platform/metal/metal_context.mm
@@ -1,14 +1,14 @@
#import <Metal/Metal.h>
#import <Foundation/Foundation.h>
-#include "MetalContext.h"
-#include "Core/Log.h"
+#include "metal_context.h"
+#include "core/log.h"
#include <vector>
namespace Donut
{
- bool MetalProbe()
+ bool metal_probe()
{
@autoreleasepool
{
@@ -41,7 +41,7 @@ namespace Donut
// reads the result back to confirm the full Metal compute path works: source
// compilation, pipeline state, command encoding, dispatch, and shared-memory
// read-back. This is the mechanism the geodesic ray tracer will run on.
- bool MetalComputeSelfTest()
+ bool metal_compute_self_test()
{
@autoreleasepool
{
diff --git a/src/Platform/OpenGL/OpenGLFramebuffer.cpp b/src/platform/opengl/opengl_framebuffer.cpp
index c46e0e6..1624439 100644
--- a/src/Platform/OpenGL/OpenGLFramebuffer.cpp
+++ b/src/platform/opengl/opengl_framebuffer.cpp
@@ -1,5 +1,5 @@
-#include "OpenGLFramebuffer.h"
-#include "Core/Log.h"
+#include "opengl_framebuffer.h"
+#include "core/log.h"
#include <glad/glad.h>
@@ -12,31 +12,31 @@ namespace Donut
return multisampled ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
}
- static void BindTexture(bool multisampled, uint32_t id)
+ static void bind_texture(bool multisampled, uint32_t id)
{
glBindTexture(TextureTarget(multisampled), id);
}
- static void CreateTextures(bool multisampled, uint32_t* outID, uint32_t count)
+ static void CreateTextures(bool multisampled, uint32_t* out_id, uint32_t count)
{
// glCreateTextures is 4.5 DSA; macOS caps at 4.1. Callers bind each
// texture (with the correct target) before use.
- glGenTextures(count, outID);
+ glGenTextures(count, out_id);
}
- static void AttachColorTexture(uint32_t id, int samples, GLenum internalFormat, GLenum format, uint32_t width, uint32_t height, int index)
+ static void AttachColorTexture(uint32_t id, int samples, GLenum internal_format, GLenum format, uint32_t width, uint32_t height, int index)
{
bool multisampled = samples > 1;
if (multisampled)
{
- glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_FALSE);
+ glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internal_format, width, height, GL_FALSE);
}
else
{
// Integer color formats require an integer pixel type even when
// data is null, or macOS's strict core profile rejects the call.
GLenum type = (format == GL_RED_INTEGER) ? GL_INT : GL_UNSIGNED_BYTE;
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -48,7 +48,7 @@ namespace Donut
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + index, TextureTarget(multisampled), id, 0);
}
- static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachmentType, uint32_t width, uint32_t height)
+ static void AttachDepthTexture(uint32_t id, int samples, GLenum format, GLenum attachment_type, uint32_t width, uint32_t height)
{
bool multisampled = samples > 1;
if (multisampled)
@@ -58,9 +58,9 @@ namespace Donut
else
{
// glTexStorage2D is 4.2; use mutable storage for macOS (4.1).
- GLenum depthFormat = (format == GL_DEPTH24_STENCIL8) ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
- GLenum depthType = (format == GL_DEPTH24_STENCIL8) ? GL_UNSIGNED_INT_24_8 : GL_FLOAT;
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, depthFormat, depthType, nullptr);
+ GLenum depth_format = (format == GL_DEPTH24_STENCIL8) ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT;
+ GLenum depth_type = (format == GL_DEPTH24_STENCIL8) ? GL_UNSIGNED_INT_24_8 : GL_FLOAT;
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, depth_format, depth_type, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -69,7 +69,7 @@ namespace Donut
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
- glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, TextureTarget(multisampled), id, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, attachment_type, TextureTarget(multisampled), id, 0);
}
static bool IsDepthFormat(FramebufferTextureFormat format)
@@ -94,81 +94,81 @@ namespace Donut
}
OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec)
- : m_Specification(spec)
+ : m_specification(spec)
{
- for (auto spec : m_Specification.Attachments.Attachments)
+ for (auto spec : m_specification.attachments.attachments)
{
- if (!Utils::IsDepthFormat(spec.TextureFormat))
- m_ColorAttachmentSpecifications.emplace_back(spec);
+ if (!Utils::IsDepthFormat(spec.texture_format))
+ m_color_attachment_specifications.emplace_back(spec);
else
- m_DepthAttachmentSpecification = spec;
+ m_depth_attachment_specification = spec;
}
- Invalidate();
+ invalidate();
}
OpenGLFramebuffer::~OpenGLFramebuffer()
{
- glDeleteFramebuffers(1, &m_RendererID);
- glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data());
- glDeleteTextures(1, &m_DepthAttachment);
+ glDeleteFramebuffers(1, &m_renderer_id);
+ glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
+ glDeleteTextures(1, &m_depth_attachment);
}
- void OpenGLFramebuffer::Invalidate()
+ auto OpenGLFramebuffer::invalidate() -> void
{
- if (m_RendererID)
+ if (m_renderer_id)
{
- glDeleteFramebuffers(1, &m_RendererID);
- glDeleteTextures(static_cast<GLsizei>(m_ColorAttachments.size()), m_ColorAttachments.data());
- glDeleteTextures(1, &m_DepthAttachment);
+ glDeleteFramebuffers(1, &m_renderer_id);
+ glDeleteTextures(static_cast<GLsizei>(m_color_attachments.size()), m_color_attachments.data());
+ glDeleteTextures(1, &m_depth_attachment);
- m_ColorAttachments.clear();
- m_DepthAttachment = 0;
+ m_color_attachments.clear();
+ m_depth_attachment = 0;
}
- glGenFramebuffers(1, &m_RendererID); // glCreateFramebuffers is 4.5 DSA; unavailable on macOS 4.1
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
+ glGenFramebuffers(1, &m_renderer_id); // glCreateFramebuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
- bool multisample = m_Specification.Samples > 1;
+ bool multisample = m_specification.Samples > 1;
- if (m_ColorAttachmentSpecifications.size())
+ if (m_color_attachment_specifications.size())
{
- m_ColorAttachments.resize(m_ColorAttachmentSpecifications.size());
- Utils::CreateTextures(multisample, m_ColorAttachments.data(), static_cast<uint32_t>(m_ColorAttachments.size()));
+ m_color_attachments.resize(m_color_attachment_specifications.size());
+ Utils::CreateTextures(multisample, m_color_attachments.data(), static_cast<uint32_t>(m_color_attachments.size()));
- for (size_t i = 0; i < m_ColorAttachments.size(); i++)
+ for (size_t i = 0; i < m_color_attachments.size(); i++)
{
- Utils::BindTexture(multisample, m_ColorAttachments[i]);
- switch (m_ColorAttachmentSpecifications[i].TextureFormat)
+ Utils::bind_texture(multisample, m_color_attachments[i]);
+ switch (m_color_attachment_specifications[i].texture_format)
{
case FramebufferTextureFormat::RGBA8:
- Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_RGBA8, GL_RGBA, m_Specification.Width, m_Specification.Height, static_cast<int>(i));
+ Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_RGBA8, GL_RGBA, m_specification.Width, m_specification.Height, static_cast<int>(i));
break;
case FramebufferTextureFormat::RED_INTEGER:
- Utils::AttachColorTexture(m_ColorAttachments[i], m_Specification.Samples, GL_R32I, GL_RED_INTEGER, m_Specification.Width, m_Specification.Height, static_cast<int>(i));
+ Utils::AttachColorTexture(m_color_attachments[i], m_specification.Samples, GL_R32I, GL_RED_INTEGER, m_specification.Width, m_specification.Height, static_cast<int>(i));
break;
}
}
}
- if (m_DepthAttachmentSpecification.TextureFormat != FramebufferTextureFormat::None)
+ if (m_depth_attachment_specification.texture_format != FramebufferTextureFormat::None)
{
- Utils::CreateTextures(multisample, &m_DepthAttachment, 1);
- Utils::BindTexture(multisample, m_DepthAttachment);
- switch (m_DepthAttachmentSpecification.TextureFormat)
+ Utils::CreateTextures(multisample, &m_depth_attachment, 1);
+ Utils::bind_texture(multisample, m_depth_attachment);
+ switch (m_depth_attachment_specification.texture_format)
{
case FramebufferTextureFormat::DEPTH24STENCIL8:
- Utils::AttachDepthTexture(m_DepthAttachment, m_Specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_Specification.Width, m_Specification.Height);
+ Utils::AttachDepthTexture(m_depth_attachment, m_specification.Samples, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL_ATTACHMENT, m_specification.Width, m_specification.Height);
break;
}
}
- if (m_ColorAttachments.size() > 1)
+ if (m_color_attachments.size() > 1)
{
GLenum buffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
- glDrawBuffers(static_cast<GLsizei>(m_ColorAttachments.size()), buffers);
+ glDrawBuffers(static_cast<GLsizei>(m_color_attachments.size()), buffers);
}
- else if (m_ColorAttachments.empty())
+ else if (m_color_attachments.empty())
glDrawBuffer(GL_NONE);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
@@ -209,38 +209,38 @@ namespace Donut
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
- void OpenGLFramebuffer::Bind()
+ auto OpenGLFramebuffer::bind() -> void
{
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
- glViewport(0, 0, m_Specification.Width, m_Specification.Height);
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
+ glViewport(0, 0, m_specification.Width, m_specification.Height);
}
- void OpenGLFramebuffer::Unbind()
+ auto OpenGLFramebuffer::unbind() -> void
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
- void OpenGLFramebuffer::Resize(uint32_t width, uint32_t height)
+ auto OpenGLFramebuffer::resize(uint32_t width, uint32_t height) -> void
{
- m_Specification.Width = width;
- m_Specification.Height = height;
+ m_specification.Width = width;
+ m_specification.Height = height;
- Invalidate();
+ invalidate();
}
- int OpenGLFramebuffer::ReadPixel(uint32_t attachmentIndex, int x, int y)
+ auto OpenGLFramebuffer::read_pixel(uint32_t attachment_index, int x, int y) -> int
{
- glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex);
- int pixelData;
- glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixelData);
- return pixelData;
+ glReadBuffer(GL_COLOR_ATTACHMENT0 + attachment_index);
+ int pixel_data;
+ glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixel_data);
+ return pixel_data;
}
- void OpenGLFramebuffer::ClearAttachment(uint32_t attachmentIndex, int value)
+ auto OpenGLFramebuffer::clear_attachment(uint32_t attachment_index, int value) -> void
{
- // glClearTexImage is 4.4 and unavailable on macOS. Clear the integer
+ // glClearTexImage is 4.4 and unavailable on macOS. clear the integer
// attachment by binding this framebuffer and clearing its draw buffer.
- glBindFramebuffer(GL_FRAMEBUFFER, m_RendererID);
- glClearBufferiv(GL_COLOR, static_cast<GLint>(attachmentIndex), &value);
+ glBindFramebuffer(GL_FRAMEBUFFER, m_renderer_id);
+ glClearBufferiv(GL_COLOR, static_cast<GLint>(attachment_index), &value);
}
};
diff --git a/src/platform/opengl/opengl_framebuffer.h b/src/platform/opengl/opengl_framebuffer.h
new file mode 100644
index 0000000..456d924
--- /dev/null
+++ b/src/platform/opengl/opengl_framebuffer.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "rendering/framebuffer.h"
+
+namespace Donut
+{
+ class OpenGLFramebuffer : public Framebuffer
+ {
+ public:
+ OpenGLFramebuffer(const FramebufferSpecification& spec);
+ virtual ~OpenGLFramebuffer();
+
+ auto invalidate() -> void;
+
+ virtual auto bind() -> void override;
+ virtual auto unbind() -> void override;
+
+ virtual auto resize(uint32_t width, uint32_t height) -> void override;
+ virtual auto read_pixel(uint32_t attachment_index, int x, int y) -> int override;
+
+ virtual auto clear_attachment(uint32_t attachment_index, int value) -> void override;
+ virtual auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t override{ return m_color_attachments[index]; }
+
+ virtual auto get_specification() const -> const FramebufferSpecification& override{ return m_specification; }
+ private:
+ uint32_t m_renderer_id = 0;
+ FramebufferSpecification m_specification;
+
+ std::vector<FramebufferTextureSpecification> m_color_attachment_specifications;
+ FramebufferTextureSpecification m_depth_attachment_specification = FramebufferTextureFormat::None;
+
+ std::vector<uint32_t> m_color_attachments;
+ uint32_t m_depth_attachment = 0;
+ };
+};
diff --git a/src/platform/opengl/opengl_index_buffer.cpp b/src/platform/opengl/opengl_index_buffer.cpp
new file mode 100644
index 0000000..faa6b81
--- /dev/null
+++ b/src/platform/opengl/opengl_index_buffer.cpp
@@ -0,0 +1,28 @@
+#include "opengl_index_buffer.h"
+#include <glad/glad.h>
+
+namespace Donut
+{
+ OpenGLIndexBuffer::OpenGLIndexBuffer(const uint32_t* indices, uint32_t count)
+ : m_count(count)
+ {
+ glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
+ }
+
+ OpenGLIndexBuffer::~OpenGLIndexBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ auto OpenGLIndexBuffer::bind() const -> void
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_renderer_id);
+ }
+
+ auto OpenGLIndexBuffer::unbind() const -> void
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+};
diff --git a/src/platform/opengl/opengl_index_buffer.h b/src/platform/opengl/opengl_index_buffer.h
new file mode 100644
index 0000000..397da43
--- /dev/null
+++ b/src/platform/opengl/opengl_index_buffer.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "rendering/index_buffer.h"
+
+namespace Donut
+{
+ class OpenGLIndexBuffer
+ : public IndexBuffer
+ {
+ public:
+ OpenGLIndexBuffer(const uint32_t* indices, uint32_t count);
+ virtual ~OpenGLIndexBuffer();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+ virtual auto get_count() const -> uint32_t override{ return m_count; }
+
+ private:
+ uint32_t m_renderer_id;
+ uint32_t m_count;
+ };
+};
diff --git a/src/Platform/OpenGL/OpenGLRendererAPI.cpp b/src/platform/opengl/opengl_renderer_api.cpp
index fd3c491..39ed786 100644
--- a/src/Platform/OpenGL/OpenGLRendererAPI.cpp
+++ b/src/platform/opengl/opengl_renderer_api.cpp
@@ -1,11 +1,11 @@
-#include "OpenGLRendererAPI.h"
+#include "opengl_renderer_api.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>
namespace Donut
{
- void OpenGLRendererAPI::Init()
+ auto OpenGLRendererAPI::init() -> void
{
if (!glfwGetCurrentContext())
{
@@ -29,32 +29,32 @@ namespace Donut
glFrontFace(GL_CCW);
}
- void OpenGLRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+ auto OpenGLRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
{
glViewport(x, y, width, height);
}
- void OpenGLRendererAPI::SetClearColor(const glm::vec4& color)
+ auto OpenGLRendererAPI::set_clear_color(const glm::vec4& color) -> void
{
glClearColor(color.r, color.g, color.b, color.a);
}
- void OpenGLRendererAPI::Clear()
+ auto OpenGLRendererAPI::clear() -> void
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
- void OpenGLRendererAPI::EnableDepthTest()
+ auto OpenGLRendererAPI::enable_depth_test() -> void
{
glEnable(GL_DEPTH_TEST);
}
- void OpenGLRendererAPI::DisableDepthTest()
+ auto OpenGLRendererAPI::disable_depth_test() -> void
{
glDisable(GL_DEPTH_TEST);
}
- void OpenGLRendererAPI::SetFaceCulling(bool enabled)
+ auto OpenGLRendererAPI::set_face_culling(bool enabled) -> void
{
if (enabled)
{
@@ -66,52 +66,52 @@ namespace Donut
glDisable(GL_CULL_FACE);
}
- void OpenGLRendererAPI::EnableBlending()
+ auto OpenGLRendererAPI::enable_blending() -> void
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
- void OpenGLRendererAPI::DisableBlending()
+ auto OpenGLRendererAPI::disable_blending() -> void
{
glDisable(GL_BLEND);
}
- void OpenGLRendererAPI::DrawIndexed(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
+ auto OpenGLRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
{
- uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount();
+ uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
}
- void OpenGLRendererAPI::DrawArrays(uint32_t vertexCount, uint32_t first)
+ auto OpenGLRendererAPI::draw_arrays(uint32_t vertex_count, uint32_t first) -> void
{
- glDrawArrays(GL_TRIANGLES, first, vertexCount);
+ glDrawArrays(GL_TRIANGLES, first, vertex_count);
}
- void OpenGLRendererAPI::DrawLines(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
+ auto OpenGLRendererAPI::draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
{
- uint32_t count = indexCount ? indexCount : vertexArray->GetIndexBuffer()->GetCount();
+ uint32_t count = index_count ? index_count : vertex_array->get_index_buffer()->get_count();
glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, nullptr);
}
- void OpenGLRendererAPI::BindTexture(uint32_t textureID, uint32_t slot)
+ auto OpenGLRendererAPI::bind_texture(uint32_t texture_id, uint32_t slot) -> void
{
glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, textureID);
+ glBindTexture(GL_TEXTURE_2D, texture_id);
}
- void OpenGLRendererAPI::BindImageTexture(uint32_t textureID, uint32_t slot, bool readOnly)
+ auto OpenGLRendererAPI::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
{
// Image load/store is OpenGL 4.2; the pointer is null on macOS (4.1).
if (glBindImageTexture == nullptr)
return;
- glBindImageTexture(slot, textureID, 0, GL_FALSE, 0,
- readOnly ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
+ glBindImageTexture(slot, texture_id, 0, GL_FALSE, 0,
+ read_only ? GL_READ_ONLY : GL_WRITE_ONLY, GL_RGBA8);
}
- void OpenGLRendererAPI::ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels)
+ auto OpenGLRendererAPI::read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void
{
glReadPixels(x, y, width, height, format, type, pixels);
}
diff --git a/src/platform/opengl/opengl_renderer_api.h b/src/platform/opengl/opengl_renderer_api.h
new file mode 100644
index 0000000..02f18ab
--- /dev/null
+++ b/src/platform/opengl/opengl_renderer_api.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "core/memory.h"
+#include "core/log.h"
+
+#include "rendering/renderer.h"
+
+#include <glad/glad.h>
+
+namespace Donut
+{
+ class OpenGLRendererAPI
+ : public RendererAPI
+ {
+ public:
+ virtual auto init() -> void override;
+ virtual void set_viewport(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height) override;
+ virtual auto set_clear_color(const glm::vec4& color) -> void override;
+ virtual auto clear() -> void override;
+ virtual auto enable_depth_test() -> void override;
+ virtual auto disable_depth_test() -> void override;
+ virtual auto set_face_culling(bool enabled) -> void override;
+ virtual auto enable_blending() -> void override;
+ virtual auto disable_blending() -> void override;
+
+ virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) override;
+
+ virtual void draw_arrays(uint32_t vertex_count,
+ uint32_t first = 0) override;
+ virtual void draw_lines(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) override;
+ virtual void bind_texture(uint32_t texture_id,
+ uint32_t slot = 0) override;
+ virtual void bind_image_texture(uint32_t texture_id,
+ uint32_t slot = 0,
+ bool read_only = false) override;
+ virtual void read_pixels(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type,
+ void* pixels) override;
+ };
+};
diff --git a/src/platform/opengl/opengl_shader.cpp b/src/platform/opengl/opengl_shader.cpp
new file mode 100644
index 0000000..d15d7e0
--- /dev/null
+++ b/src/platform/opengl/opengl_shader.cpp
@@ -0,0 +1,302 @@
+#include "opengl_shader.h"
+
+#include <glad/glad.h>
+#include <glm/gtc/type_ptr.hpp>
+
+#include <fstream>
+#include <iostream>
+
+namespace Donut
+{
+ static uint32_t ShaderTypeFromString(const std::string& type)
+ {
+ if (type == "vertex")
+ return GL_VERTEX_SHADER;
+ if (type == "fragment" || type == "pixel")
+ return GL_FRAGMENT_SHADER;
+ if (type == "compute")
+ return GL_COMPUTE_SHADER;
+ return 0;
+ }
+
+ // Shaders are authored in Slang and compiled to assets/shaders/generated/
+ // <name>.glsl by Tools/compile-shaders.sh. Given a legacy ".../<name>.glsl"
+ // path, prefer that generated file when present; otherwise fall back to the
+ // hand-written GLSL (e.g. shaders not yet ported to Slang).
+ static std::string ResolveShaderPath(const std::string& filepath)
+ {
+ size_t slash = filepath.find_last_of("/\\");
+ std::string dir = (slash == std::string::npos) ? std::string() : filepath.substr(0, slash + 1);
+ std::string file = (slash == std::string::npos) ? filepath : filepath.substr(slash + 1);
+ size_t dot = file.rfind('.');
+ std::string base = (dot == std::string::npos) ? file : file.substr(0, dot);
+
+ std::string generated = dir + "generated/" + base + ".glsl";
+ std::ifstream test(generated);
+ if (test.good())
+ return generated;
+ return filepath;
+ }
+
+ OpenGLShader::OpenGLShader(const std::string& filepath)
+ {
+ std::string resolved = ResolveShaderPath(filepath);
+ m_is_slang = (resolved != filepath);
+ std::string source = read_file(resolved);
+ auto shader_sources = pre_process(source);
+ compile(shader_sources);
+
+ auto last_slash = filepath.find_last_of("/\\");
+ last_slash = last_slash == std::string::npos ? 0 : last_slash + 1;
+ auto last_dot = filepath.rfind('.');
+ auto count = last_dot == std::string::npos ? filepath.size() - last_slash : last_dot - last_slash;
+ m_name = filepath.substr(last_slash, count);
+ }
+
+ OpenGLShader::OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
+ : m_name(name)
+ {
+ std::unordered_map<uint32_t, std::string> sources;
+ sources[GL_VERTEX_SHADER] = vertex_src;
+ sources[GL_FRAGMENT_SHADER] = fragment_src;
+ compile(sources);
+ }
+
+ OpenGLShader::OpenGLShader(const std::string& name, const std::string& compute_src)
+ : m_name(name)
+ {
+ std::unordered_map<uint32_t, std::string> sources;
+ sources[GL_COMPUTE_SHADER] = compute_src;
+ compile(sources);
+ }
+
+ OpenGLShader::~OpenGLShader()
+ {
+ glDeleteProgram(m_renderer_id);
+ }
+
+ auto OpenGLShader::read_file(const std::string& filepath) -> std::string
+ {
+ std::string result;
+ std::ifstream in(filepath, std::ios::in |
+ std::ios::binary);
+
+ if (in)
+ {
+ in.seekg(0, std::ios::end);
+ size_t size = in.tellg();
+ if (size != -1)
+ {
+ result.resize(size);
+ in.seekg(0, std::ios::beg);
+ in.read(&result[0], size);
+ }
+ }
+ return result;
+ }
+
+ auto OpenGLShader::pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>
+ {
+ std::unordered_map<uint32_t, std::string> shader_sources;
+
+ const char* type_token = "#type";
+ size_t type_token_length = strlen(type_token);
+ size_t pos = source.find(type_token, 0);
+
+ while (pos != std::string::npos)
+ {
+ size_t eol = source.find_first_of("\r\n", pos);
+ size_t begin = pos + type_token_length + 1;
+ std::string type = source.substr(begin, eol - begin);
+
+ size_t next_line_pos = source.find_first_not_of("\r\n", eol);
+ pos = source.find(type_token, next_line_pos);
+ shader_sources[ShaderTypeFromString(type)] = (pos == std::string::npos) ? source.substr(next_line_pos) :
+ source.substr(next_line_pos, pos - next_line_pos);
+ }
+
+ return shader_sources;
+ }
+
+ auto OpenGLShader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void
+ {
+ uint32_t program = glCreateProgram();
+ std::vector<uint32_t> glShaderIDs(shader_sources.size());
+ for (auto& kv : shader_sources)
+ {
+ uint32_t type = kv.first;
+ const std::string& source = kv.second;
+
+ uint32_t shader = glCreateShader(type);
+ const char* source_c_str = source.c_str();
+ glShaderSource(shader, 1, &source_c_str, 0);
+ glCompileShader(shader);
+
+ int is_compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &is_compiled);
+ if (is_compiled == GL_FALSE)
+ {
+ int max_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &max_length);
+ std::vector<char> info_log(max_length);
+ glGetShaderInfoLog(shader, max_length, &max_length, &info_log[0]);
+ glDeleteShader(shader);
+ for (auto id : glShaderIDs)
+ glDeleteShader(id);
+ glDeleteProgram(program);
+ m_renderer_id = 0;
+ // info_log.data() is null when the driver returns an empty log
+ // (e.g. macOS rejecting a compute shader); streaming a null
+ // char* into std::cout calls strlen(NULL) and crashes.
+ const char* log = info_log.empty() ? "" : info_log.data();
+ std::cout << "Shader compilation failure!" << std::endl << log << std::endl;
+ return;
+ }
+ glAttachShader(program, shader);
+ glShaderIDs.push_back(shader);
+ }
+
+ m_renderer_id = program;
+ glLinkProgram(m_renderer_id);
+
+ int is_linked = 0;
+ glGetProgramiv(m_renderer_id, GL_LINK_STATUS, (int*)&is_linked);
+ if (is_linked == GL_FALSE)
+ {
+ int max_length = 0;
+ glGetProgramiv(m_renderer_id, GL_INFO_LOG_LENGTH, &max_length);
+ std::vector<char> info_log(max_length);
+ glGetProgramInfoLog(m_renderer_id, max_length, &max_length, &info_log[0]);
+ glDeleteProgram(m_renderer_id);
+ for (auto id : glShaderIDs)
+ glDeleteShader(id);
+ m_renderer_id = 0;
+ const char* log = info_log.empty() ? "" : info_log.data();
+ std::cout << "Shader link failure!" << std::endl << log << std::endl;
+ return;
+ }
+
+ for (auto id : glShaderIDs)
+ {
+ glDetachShader(m_renderer_id, id);
+ glDeleteShader(id);
+ }
+ }
+
+ auto OpenGLShader::bind() const -> void
+ {
+ glUseProgram(m_renderer_id);
+ }
+
+ auto OpenGLShader::unbind() const -> void
+ {
+ glUseProgram(0);
+ }
+
+ auto OpenGLShader::set_int(const std::string& name, int value) -> void
+ {
+ upload_uniform_int(name, value);
+ }
+
+ auto OpenGLShader::set_int_array(const std::string& name, int* values, uint32_t count) -> void
+ {
+ upload_uniform_int_array(name, values, count);
+ }
+
+ auto OpenGLShader::set_float(const std::string& name, float value) -> void
+ {
+ upload_uniform_float(name, value);
+ }
+
+ auto OpenGLShader::set_float2(const std::string& name, const glm::vec2& value) -> void
+ {
+ upload_uniform_float2(name, value);
+ }
+
+ auto OpenGLShader::set_float3(const std::string& name, const glm::vec3& value) -> void
+ {
+ upload_uniform_float3(name, value);
+ }
+
+ auto OpenGLShader::set_float4(const std::string& name, const glm::vec4& value) -> void
+ {
+ upload_uniform_float4(name, value);
+ }
+
+ auto OpenGLShader::set_mat4(const std::string& name, const glm::mat4& value) -> void
+ {
+ upload_uniform_mat4(name, value);
+ }
+
+ auto OpenGLShader::upload_uniform_int(const std::string& name, int value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1i(location, value);
+ }
+
+ auto OpenGLShader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1iv(location, count, values);
+ }
+
+ auto OpenGLShader::upload_uniform_float(const std::string& name, float value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform1f(location, value);
+ }
+
+ auto OpenGLShader::upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform2f(location, value.x, value.y);
+ }
+
+ auto OpenGLShader::upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform3f(location, value.x, value.y, value.z);
+ }
+
+ auto OpenGLShader::upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniform4f(location, value.x, value.y, value.z, value.w);
+ }
+
+ auto OpenGLShader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniformMatrix3fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
+ }
+
+ auto OpenGLShader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void
+ {
+ int location = glGetUniformLocation(m_renderer_id, name.c_str());
+ glUniformMatrix4fv(location, 1, m_is_slang ? GL_TRUE : GL_FALSE, glm::value_ptr(matrix));
+ }
+
+ auto OpenGLShader::dispatch(uint32_t x, uint32_t y, uint32_t z) -> void
+ {
+ // Compute shaders require OpenGL 4.3+. On drivers that cap out earlier
+ // (e.g. macOS, which is frozen at 4.1) glDispatchCompute is never
+ // loaded and the pointer is null. Guard so we no-op instead of crash.
+ if (m_renderer_id == 0 || glDispatchCompute == nullptr)
+ return;
+ glDispatchCompute(x, y, z);
+ }
+
+ auto OpenGLShader::dispatch_indirect(uint32_t offset) -> void
+ {
+ if (m_renderer_id == 0 || glDispatchComputeIndirect == nullptr)
+ return;
+ glDispatchComputeIndirect(offset);
+ }
+
+ auto OpenGLShader::memory_barrier(uint32_t barriers) -> void
+ {
+ if (glMemoryBarrier == nullptr)
+ return;
+ glMemoryBarrier(barriers);
+ }
+};
diff --git a/src/platform/opengl/opengl_shader.h b/src/platform/opengl/opengl_shader.h
new file mode 100644
index 0000000..289bfac
--- /dev/null
+++ b/src/platform/opengl/opengl_shader.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "rendering/shader.h"
+
+#include <unordered_map>
+#include <glm/glm.hpp>
+
+namespace Donut
+{
+ class OpenGLShader
+ : public Shader
+ {
+ public:
+ OpenGLShader(const std::string& filepath);
+ OpenGLShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
+ OpenGLShader(const std::string& name, const std::string& compute_src);
+ virtual ~OpenGLShader();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto set_int( const std::string& name, int value) -> void override;
+ virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void override;
+ virtual auto set_float( const std::string& name, float value) -> void override;
+ virtual auto set_float2( const std::string& name, const glm::vec2& value) -> void override;
+ virtual auto set_float3( const std::string& name, const glm::vec3& value) -> void override;
+ virtual auto set_float4( const std::string& name, const glm::vec4& value) -> void override;
+ virtual auto set_mat4( const std::string& name, const glm::mat4& value) -> void override;
+
+ virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void override;
+ virtual auto dispatch_indirect(uint32_t offset = 0) -> void override;
+ virtual auto memory_barrier(uint32_t barriers) -> void override;
+
+ virtual auto get_name() const -> const std::string& override{ return m_name; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ auto upload_uniform_int( const std::string& name, int value) -> void;
+ auto upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void;
+ auto upload_uniform_float( const std::string& name, float value) -> void;
+ auto upload_uniform_float2( const std::string& name, const glm::vec2& value) -> void;
+ auto upload_uniform_float3( const std::string& name, const glm::vec3& value) -> void;
+ auto upload_uniform_float4( const std::string& name, const glm::vec4& value) -> void;
+ auto upload_uniform_mat3( const std::string& name, const glm::mat3& matrix) -> void;
+ auto upload_uniform_mat4( const std::string& name, const glm::mat4& matrix) -> void;
+
+ private:
+ auto read_file(const std::string& filepath) -> std::string;
+ auto pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>;
+ auto compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void;
+ private:
+ uint32_t m_renderer_id = 0;
+ std::string m_name;
+ // True when loaded from a Slang-compiled GLSL. Slang expects row-major
+ // matrix data, so matrix uniforms are transposed on upload (glm is
+ // column-major) to keep all matrix math correct.
+ bool m_is_slang = false;
+ };
+};
diff --git a/src/Platform/OpenGL/OpenGLTexture.cpp b/src/platform/opengl/opengl_texture.cpp
index 16892c6..6bcb9cd 100644
--- a/src/Platform/OpenGL/OpenGLTexture.cpp
+++ b/src/platform/opengl/opengl_texture.cpp
@@ -1,6 +1,6 @@
-#include "OpenGLTexture.h"
+#include "opengl_texture.h"
-#include "Rendering/Shader.h"
+#include "rendering/shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
@@ -16,14 +16,14 @@
namespace Donut
{
OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
+ : m_width(width), m_height(height)
{
- m_InternalFormat = GL_RGBA8;
- m_DataFormat = GL_RGBA;
+ m_internal_format = GL_RGBA8;
+ m_data_format = GL_RGBA;
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -32,72 +32,72 @@ namespace Donut
}
OpenGLTexture2D::OpenGLTexture2D(const std::string& path)
- : m_Path(path)
+ : m_path(path)
{
- m_Width = 1;
- m_Height = 1;
- m_InternalFormat = GL_RGBA8;
- m_DataFormat = GL_RGBA;
+ m_width = 1;
+ m_height = 1;
+ m_internal_format = GL_RGBA8;
+ m_data_format = GL_RGBA;
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- uint32_t whitePixel = 0xFFFFFFFF;
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, &whitePixel);
+ uint32_t white_pixel = 0xFFFFFFFF;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, &white_pixel);
DONUT_INFO("Created default texture (stb_image not available for loading: ", path, ")");
}
OpenGLTexture2D::~OpenGLTexture2D()
{
- glDeleteTextures(1, &m_RendererID);
+ glDeleteTextures(1, &m_renderer_id);
}
- void OpenGLTexture2D::SetData(void* data, uint32_t size)
+ auto OpenGLTexture2D::set_data(void* data, uint32_t size) -> void
{
- uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3;
- if (size != m_Width * m_Height * bpp)
+ uint32_t bpp = m_data_format == GL_RGBA ? 4 : 3;
+ if (size != m_width * m_height * bpp)
{
DONUT_ERROR("Data must be entire texture!");
return;
}
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_Width, m_Height, m_DataFormat, GL_UNSIGNED_BYTE, data);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_data_format, GL_UNSIGNED_BYTE, data);
}
- void OpenGLTexture2D::Bind(uint32_t slot) const
+ auto OpenGLTexture2D::bind(uint32_t slot) const -> void
{
glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_2D, m_RendererID);
+ glBindTexture(GL_TEXTURE_2D, m_renderer_id);
}
- void OpenGLTexture2D::BindAsImage(uint32_t slot, bool readOnly) const
+ auto OpenGLTexture2D::bind_as_image(uint32_t slot, bool read_only) const -> void
{
// Image load/store is OpenGL 4.2 and unavailable on macOS. Guard the
// function pointer so this degrades to a no-op instead of crashing.
if (glBindImageTexture == nullptr)
return;
- GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_RendererID, 0, GL_FALSE, 0, access, m_InternalFormat);
+ GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
+ glBindImageTexture(slot, m_renderer_id, 0, GL_FALSE, 0, access, m_internal_format);
}
OpenGLCubemapTexture::OpenGLCubemapTexture(uint32_t width, uint32_t height)
- : m_Width(width), m_Height(height)
+ : m_width(width), m_height(height)
{
- m_InternalFormat = GL_RGBA16F;
- m_DataFormat = GL_RGBA;
+ m_internal_format = GL_RGBA16F;
+ m_data_format = GL_RGBA;
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_FLOAT, nullptr);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -107,17 +107,17 @@ namespace Donut
}
OpenGLCubemapTexture::OpenGLCubemapTexture(const std::string& path)
- : m_Path(path)
+ : m_path(path)
{
- m_Width = 1024;
- m_Height = 1024;
- m_InternalFormat = GL_RGBA16F;
- m_DataFormat = GL_RGBA;
+ m_width = 1024;
+ m_height = 1024;
+ m_internal_format = GL_RGBA16F;
+ m_data_format = GL_RGBA;
- glGenTextures(1, &m_RendererID);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
+ glGenTextures(1, &m_renderer_id);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
for (uint32_t i = 0; i < 6; ++i)
- glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_InternalFormat, m_Width, m_Height, 0, m_DataFormat, GL_FLOAT, nullptr);
+ glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, m_internal_format, m_width, m_height, 0, m_data_format, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -130,19 +130,19 @@ namespace Donut
OpenGLCubemapTexture::~OpenGLCubemapTexture()
{
- glDeleteTextures(1, &m_RendererID);
+ glDeleteTextures(1, &m_renderer_id);
}
- void OpenGLCubemapTexture::LoadHDRI(const std::string& path)
+ auto OpenGLCubemapTexture::LoadHDRI(const std::string& path) -> void
{
stbi_set_flip_vertically_on_load(true);
int width, height, channels;
- float* hdrData = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
+ float* hdr_data = stbi_loadf(path.c_str(), &width, &height, &channels, 3);
- if (!hdrData)
+ if (!hdr_data)
{
DONUT_ERROR("Failed to load HDRI: {}", path);
- float defaultSky[6 * 4] =
+ float default_sky[6 * 4] =
{
0.5f, 0.7f, 1.0f, 1.0f, // Right
0.5f, 0.7f, 1.0f, 1.0f, // Left
@@ -152,46 +152,46 @@ namespace Donut
0.5f, 0.7f, 1.0f, 1.0f // Back
};
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
for (int i = 0; i < 6; ++i)
- glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, &defaultSky[i * 4]);
+ glTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, 1, 1, GL_RGBA, GL_FLOAT, &default_sky[i * 4]);
return;
}
- ConvertEquirectangularToCubemap(hdrData, width, height);
- stbi_image_free(hdrData);
+ convert_equirectangular_to_cubemap(hdr_data, width, height);
+ stbi_image_free(hdr_data);
DONUT_INFO("Successfully loaded HDRI: {} ({}x{})", path, width, height);
}
- void OpenGLCubemapTexture::ConvertEquirectangularToCubemap(float* hdrData, int width, int height)
+ auto OpenGLCubemapTexture::convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void
{
- uint32_t captureFBO, captureRBO;
- glGenFramebuffers(1, &captureFBO);
- glGenRenderbuffers(1, &captureRBO);
+ uint32_t capture_fbo, capture_rbo;
+ glGenFramebuffers(1, &capture_fbo);
+ glGenRenderbuffers(1, &capture_rbo);
- glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
- glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_Width, m_Height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
+ glBindRenderbuffer(GL_RENDERBUFFER, capture_rbo);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, m_width, m_height);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, capture_rbo);
- uint32_t hdrTexture;
- glGenTextures(1, &hdrTexture);
- glBindTexture(GL_TEXTURE_2D, hdrTexture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdrData);
+ uint32_t hdr_texture;
+ glGenTextures(1, &hdr_texture);
+ glBindTexture(GL_TEXTURE_2D, hdr_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, hdr_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- auto equirectShader = Shader::Create("Assets/Shaders/EquirectToCubemap.glsl");
- if (!equirectShader)
+ auto equirect_shader = Shader::create("assets/shaders/EquirectToCubemap.glsl");
+ if (!equirect_shader)
{
DONUT_ERROR("Failed to create equirectangular to cubemap shader");
return;
}
- uint32_t shaderProgram = equirectShader->GetRendererID();
+ uint32_t shader_program = equirect_shader->get_renderer_id();
float vertices[] =
{
@@ -203,17 +203,17 @@ namespace Donut
-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
};
- uint32_t cubeVAO, cubeVBO;
- glGenVertexArrays(1, &cubeVAO);
- glGenBuffers(1, &cubeVBO);
- glBindVertexArray(cubeVAO);
- glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
+ uint32_t cube_vao, cube_vbo;
+ glGenVertexArrays(1, &cube_vao);
+ glGenBuffers(1, &cube_vbo);
+ glBindVertexArray(cube_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, cube_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
- glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
- glm::mat4 captureViews[] =
+ glm::mat4 capture_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
+ glm::mat4 capture_views[] =
{
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)),
@@ -223,50 +223,50 @@ namespace Donut
glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f))
};
- glUseProgram(shaderProgram);
- glUniform1i(glGetUniformLocation(shaderProgram, "u_EquirectangularMap"), 0);
+ glUseProgram(shader_program);
+ glUniform1i(glGetUniformLocation(shader_program, "u_EquirectangularMap"), 0);
// EquirectToCubemap is authored in Slang (row-major); transpose glm's
// column-major matrices on upload (GL_TRUE) to match.
- glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "u_Projection"), 1, GL_TRUE, &captureProjection[0][0]);
+ glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_Projection"), 1, GL_TRUE, &capture_projection[0][0]);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, hdrTexture);
+ glBindTexture(GL_TEXTURE_2D, hdr_texture);
- glViewport(0, 0, m_Width, m_Height);
- glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
+ glViewport(0, 0, m_width, m_height);
+ glBindFramebuffer(GL_FRAMEBUFFER, capture_fbo);
for (unsigned int i = 0; i < 6; ++i)
{
- glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "u_View"), 1, GL_TRUE, &captureViews[i][0][0]);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_RendererID, 0);
+ glUniformMatrix4fv(glGetUniformLocation(shader_program, "u_View"), 1, GL_TRUE, &capture_views[i][0][0]);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, m_renderer_id, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glBindVertexArray(cubeVAO);
+ glBindVertexArray(cube_vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glDeleteVertexArrays(1, &cubeVAO);
- glDeleteBuffers(1, &cubeVBO);
- glDeleteTextures(1, &hdrTexture);
- glDeleteFramebuffers(1, &captureFBO);
- glDeleteRenderbuffers(1, &captureRBO);
+ glDeleteVertexArrays(1, &cube_vao);
+ glDeleteBuffers(1, &cube_vbo);
+ glDeleteTextures(1, &hdr_texture);
+ glDeleteFramebuffers(1, &capture_fbo);
+ glDeleteRenderbuffers(1, &capture_rbo);
}
- void OpenGLCubemapTexture::SetData(void* data, uint32_t size)
+ auto OpenGLCubemapTexture::set_data(void* data, uint32_t size) -> void
{
- DONUT_WARN("SetData not implemented for cubemaps");
+ DONUT_WARN("set_data not implemented for cubemaps");
}
- void OpenGLCubemapTexture::Bind(uint32_t slot) const
+ auto OpenGLCubemapTexture::bind(uint32_t slot) const -> void
{
glActiveTexture(GL_TEXTURE0 + slot);
- glBindTexture(GL_TEXTURE_CUBE_MAP, m_RendererID);
+ glBindTexture(GL_TEXTURE_CUBE_MAP, m_renderer_id);
}
- void OpenGLCubemapTexture::BindAsImage(uint32_t slot, bool readOnly) const
+ auto OpenGLCubemapTexture::bind_as_image(uint32_t slot, bool read_only) const -> void
{
if (glBindImageTexture == nullptr)
return;
- GLenum access = readOnly ? GL_READ_ONLY : GL_WRITE_ONLY;
- glBindImageTexture(slot, m_RendererID, 0, GL_TRUE, 0, access, m_InternalFormat);
+ GLenum access = read_only ? GL_READ_ONLY : GL_WRITE_ONLY;
+ glBindImageTexture(slot, m_renderer_id, 0, GL_TRUE, 0, access, m_internal_format);
}
};
diff --git a/src/platform/opengl/opengl_texture.h b/src/platform/opengl/opengl_texture.h
new file mode 100644
index 0000000..7aa87ea
--- /dev/null
+++ b/src/platform/opengl/opengl_texture.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "rendering/texture.h"
+#include "core/log.h"
+
+#include <glad/glad.h>
+
+namespace Donut
+{
+ class OpenGLTexture2D
+ : public Texture2D
+ {
+ public:
+ OpenGLTexture2D(uint32_t width, uint32_t height);
+ OpenGLTexture2D(const std::string& path);
+ virtual ~OpenGLTexture2D();
+
+ virtual auto get_width() const -> uint32_t override{ return m_width; }
+ virtual auto get_height() const -> uint32_t override{ return m_height; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ virtual auto set_data(void* data, uint32_t size) -> void override;
+ virtual auto bind(uint32_t slot = 0) const -> void override;
+ virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_renderer_id == other.get_renderer_id();
+ }
+
+ private:
+ std::string m_path;
+ uint32_t m_width, m_height;
+ uint32_t m_renderer_id;
+ GLenum m_internal_format, m_data_format;
+ };
+
+ class OpenGLCubemapTexture
+ : public CubemapTexture
+ {
+ public:
+ OpenGLCubemapTexture(uint32_t width, uint32_t height);
+ OpenGLCubemapTexture(const std::string& path);
+ virtual ~OpenGLCubemapTexture();
+
+ virtual auto get_width() const -> uint32_t override{ return m_width; }
+ virtual auto get_height() const -> uint32_t override{ return m_height; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ virtual auto set_data(void* data, uint32_t size) -> void override;
+ virtual auto bind(uint32_t slot = 0) const -> void override;
+ virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_renderer_id == other.get_renderer_id();
+ }
+
+ private:
+ void LoadHDRI(const std::string& path);
+ auto convert_equirectangular_to_cubemap(float* hdr_data, int width, int height) -> void;
+
+ std::string m_path;
+ uint32_t m_width, m_height;
+ uint32_t m_renderer_id;
+ GLenum m_internal_format, m_data_format;
+ };
+}
diff --git a/src/platform/opengl/opengl_uniform_buffer.cpp b/src/platform/opengl/opengl_uniform_buffer.cpp
new file mode 100644
index 0000000..9b3b6d2
--- /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);
+ }
+
+ auto OpenGLUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset) -> void
+ {
+ glBindBuffer(GL_UNIFORM_BUFFER, m_renderer_id);
+ glBufferSubData(GL_UNIFORM_BUFFER, offset, size, data);
+ }
+
+ auto OpenGLUniformBuffer::bind(uint32_t binding) -> void
+ {
+ 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..db137a0
--- /dev/null
+++ b/src/platform/opengl/opengl_uniform_buffer.h
@@ -0,0 +1,21 @@
+#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 auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void override;
+ virtual auto bind(uint32_t binding) -> void override;
+ private:
+ uint32_t m_renderer_id = 0;
+ uint32_t m_size = 0;
+ uint32_t m_binding = 0;
+ };
+};
diff --git a/src/platform/opengl/opengl_vertex_array.cpp b/src/platform/opengl/opengl_vertex_array.cpp
new file mode 100644
index 0000000..01e6f56
--- /dev/null
+++ b/src/platform/opengl/opengl_vertex_array.cpp
@@ -0,0 +1,58 @@
+#include <glad/glad.h>
+
+#include "opengl_vertex_array.h"
+#include "rendering/vertex_buffer.h"
+#include "rendering/index_buffer.h"
+
+namespace Donut
+{
+ OpenGLVertexArray::OpenGLVertexArray()
+ {
+ // glCreateVertexArrays is 4.5 DSA; macOS caps at 4.1. glGenVertexArrays
+ // reserves the name and the VAO is created on first bind (done below).
+ glGenVertexArrays(1, &m_renderer_id);
+ }
+
+ OpenGLVertexArray::~OpenGLVertexArray()
+ {
+ glDeleteVertexArrays(1, &m_renderer_id);
+ }
+
+ auto OpenGLVertexArray::bind() const -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ }
+
+ auto OpenGLVertexArray::unbind() const -> void
+ {
+ glBindVertexArray(0);
+ }
+
+ auto OpenGLVertexArray::add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ vertex_buffer->bind();
+
+ const auto& layout = vertex_buffer->get_layout();
+ for (const auto& element : layout.get_elements())
+ {
+ glEnableVertexAttribArray(m_vertex_buffer_index);
+ glVertexAttribPointer(m_vertex_buffer_index,
+ element.count,
+ element.type,
+ element.normalized ? GL_TRUE : GL_FALSE,
+ layout.get_stride(),
+ reinterpret_cast<const void*>(static_cast<uintptr_t>(element.offset)));
+ m_vertex_buffer_index++;
+ }
+
+ m_vertex_buffers.push_back(vertex_buffer);
+ }
+
+ auto OpenGLVertexArray::set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void
+ {
+ glBindVertexArray(m_renderer_id);
+ index_buffer->bind();
+ m_index_buffer = index_buffer;
+ }
+};
diff --git a/src/platform/opengl/opengl_vertex_array.h b/src/platform/opengl/opengl_vertex_array.h
new file mode 100644
index 0000000..5115830
--- /dev/null
+++ b/src/platform/opengl/opengl_vertex_array.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "core/memory.h"
+
+#include "rendering/vertex_array.h"
+#include "rendering/vertex_buffer.h"
+#include "rendering/index_buffer.h"
+
+#include <vector>
+
+namespace Donut
+{
+ class OpenGLVertexArray
+ : public VertexArray
+ {
+ public:
+ OpenGLVertexArray();
+ virtual ~OpenGLVertexArray();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void override;
+ virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void override;
+
+ virtual const std::vector<Ref<VertexBuffer>>& get_vertex_buffers() const override
+ {
+ return m_vertex_buffers;
+ }
+
+ virtual const Ref<IndexBuffer>& get_index_buffer() const override
+ {
+ return m_index_buffer;
+ }
+ private:
+ uint32_t m_renderer_id;
+ uint32_t m_vertex_buffer_index = 0;
+ std::vector<Ref<VertexBuffer>> m_vertex_buffers;
+ Ref<IndexBuffer> m_index_buffer;
+ };
+};
diff --git a/src/platform/opengl/opengl_vertex_buffer.cpp b/src/platform/opengl/opengl_vertex_buffer.cpp
new file mode 100644
index 0000000..5cd4b82
--- /dev/null
+++ b/src/platform/opengl/opengl_vertex_buffer.cpp
@@ -0,0 +1,35 @@
+#include "opengl_vertex_buffer.h"
+#include "rendering/vertex_buffer.h"
+
+#include <glad/glad.h>
+
+namespace Donut
+{
+ OpenGLVertexBuffer::OpenGLVertexBuffer(const void* data, uint32_t size)
+ {
+ glGenBuffers(1, &m_renderer_id); // glCreateBuffers is 4.5 DSA; unavailable on macOS 4.1
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
+ }
+
+ OpenGLVertexBuffer::~OpenGLVertexBuffer()
+ {
+ glDeleteBuffers(1, &m_renderer_id);
+ }
+
+ auto OpenGLVertexBuffer::bind() const -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ }
+
+ auto OpenGLVertexBuffer::unbind() const -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ auto OpenGLVertexBuffer::set_data(const void* data, uint32_t size) -> void
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, size, data);
+ }
+};
diff --git a/src/platform/opengl/opengl_vertex_buffer.h b/src/platform/opengl/opengl_vertex_buffer.h
new file mode 100644
index 0000000..df2e0e6
--- /dev/null
+++ b/src/platform/opengl/opengl_vertex_buffer.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "rendering/vertex_buffer.h"
+
+namespace Donut
+{
+ class OpenGLVertexBuffer
+ : public VertexBuffer
+ {
+ public:
+ OpenGLVertexBuffer(const void* data, uint32_t size);
+ virtual ~OpenGLVertexBuffer();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+ virtual auto set_data(const void* data, uint32_t size) -> void override;
+
+ virtual auto get_layout() const -> const VertexBufferLayout& override{ return m_layout; }
+ virtual auto set_layout(const VertexBufferLayout& layout) -> void override{ m_layout = layout; }
+
+ private:
+ uint32_t m_renderer_id;
+ VertexBufferLayout m_layout;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanContext.cpp b/src/platform/vulkan/vulkan_context.cpp
index 9a67fe8..ece7700 100644
--- a/src/Platform/Vulkan/VulkanContext.cpp
+++ b/src/platform/vulkan/vulkan_context.cpp
@@ -1,5 +1,5 @@
-#include "VulkanContext.h"
-#include "Core/Log.h"
+#include "vulkan_context.h"
+#include "core/log.h"
#include <vulkan/vulkan.h>
@@ -29,20 +29,20 @@ namespace Donut
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physical = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
- VkQueue graphicsQueue = VK_NULL_HANDLE;
- uint32_t graphicsFamily = 0;
- VkPhysicalDeviceMemoryProperties memProps{};
+ VkQueue graphics_queue = VK_NULL_HANDLE;
+ uint32_t graphics_family = 0;
+ VkPhysicalDeviceMemoryProperties mem_props{};
- uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const
+ auto find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags flags) const -> uint32_t
{
- for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
- if ((typeFilter & (1u << i)) &&
- (memProps.memoryTypes[i].propertyFlags & flags) == flags)
+ for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i)
+ if ((type_filter & (1u << i)) &&
+ (mem_props.memoryTypes[i].propertyFlags & flags) == flags)
return i;
return UINT32_MAX;
}
- bool CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
+ bool create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
VkBuffer& buf, VkDeviceMemory& mem) const
{
VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
@@ -51,19 +51,19 @@ namespace Donut
VkMemoryRequirements req{}; vkGetBufferMemoryRequirements(device, buf, &req);
VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
ai.allocationSize = req.size;
- ai.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, props);
+ ai.memoryTypeIndex = find_memory_type(req.memoryTypeBits, props);
if (vkAllocateMemory(device, &ai, nullptr, &mem) != VK_SUCCESS) return false;
vkBindBufferMemory(device, buf, mem, 0);
return true;
}
};
- VulkanContext::VulkanContext() { m_Impl = new Impl(); }
- VulkanContext::~VulkanContext() { Shutdown(); delete m_Impl; m_Impl = nullptr; }
+ VulkanContext::VulkanContext() { m_impl = new Impl(); }
+ VulkanContext::~VulkanContext() { shutdown(); delete m_impl; m_impl = nullptr; }
- bool VulkanContext::Init()
+ auto VulkanContext::init() -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
#ifdef __APPLE__
// The Homebrew Vulkan loader does not auto-discover MoltenVK or the
@@ -88,10 +88,10 @@ namespace Donut
// Enable validation layers when they are installed (optional).
std::vector<const char*> layers;
- uint32_t layerCount = 0;
- vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
- std::vector<VkLayerProperties> avail(layerCount);
- vkEnumerateInstanceLayerProperties(&layerCount, avail.data());
+ uint32_t layer_count = 0;
+ vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
+ std::vector<VkLayerProperties> avail(layer_count);
+ vkEnumerateInstanceLayerProperties(&layer_count, avail.data());
for (const auto& l : avail)
if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0)
layers.push_back("VK_LAYER_KHRONOS_validation");
@@ -106,51 +106,51 @@ namespace Donut
VK_CHECK(vkCreateInstance(&ici, nullptr, &v.instance));
// Physical device
- uint32_t deviceCount = 0;
- vkEnumeratePhysicalDevices(v.instance, &deviceCount, nullptr);
- if (deviceCount == 0) { DONUT_ERROR("Vulkan: no physical devices"); return false; }
- std::vector<VkPhysicalDevice> devices(deviceCount);
- vkEnumeratePhysicalDevices(v.instance, &deviceCount, devices.data());
+ uint32_t device_count = 0;
+ vkEnumeratePhysicalDevices(v.instance, &device_count, nullptr);
+ if (device_count == 0) { DONUT_ERROR("Vulkan: no physical devices"); return false; }
+ std::vector<VkPhysicalDevice> devices(device_count);
+ vkEnumeratePhysicalDevices(v.instance, &device_count, devices.data());
v.physical = devices[0];
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(v.physical, &props);
- vkGetPhysicalDeviceMemoryProperties(v.physical, &v.memProps);
+ vkGetPhysicalDeviceMemoryProperties(v.physical, &v.mem_props);
// Graphics queue family
- uint32_t qCount = 0;
- vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &qCount, nullptr);
- std::vector<VkQueueFamilyProperties> qfams(qCount);
- vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &qCount, qfams.data());
+ uint32_t q_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &q_count, nullptr);
+ std::vector<VkQueueFamilyProperties> qfams(q_count);
+ vkGetPhysicalDeviceQueueFamilyProperties(v.physical, &q_count, qfams.data());
bool found = false;
- for (uint32_t i = 0; i < qCount; ++i)
- if (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { v.graphicsFamily = i; found = true; break; }
+ for (uint32_t i = 0; i < q_count; ++i)
+ if (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { v.graphics_family = i; found = true; break; }
if (!found) { DONUT_ERROR("Vulkan: no graphics queue family"); return false; }
// Logical device
// MoltenVK requires VK_KHR_portability_subset to be enabled if present.
- std::vector<const char*> devExts;
- uint32_t devExtCount = 0;
- vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &devExtCount, nullptr);
- std::vector<VkExtensionProperties> devExtProps(devExtCount);
- vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &devExtCount, devExtProps.data());
- for (const auto& e : devExtProps)
+ std::vector<const char*> dev_exts;
+ uint32_t dev_ext_count = 0;
+ vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &dev_ext_count, nullptr);
+ std::vector<VkExtensionProperties> dev_ext_props(dev_ext_count);
+ vkEnumerateDeviceExtensionProperties(v.physical, nullptr, &dev_ext_count, dev_ext_props.data());
+ for (const auto& e : dev_ext_props)
if (std::strcmp(e.extensionName, "VK_KHR_portability_subset") == 0)
- devExts.push_back("VK_KHR_portability_subset");
+ dev_exts.push_back("VK_KHR_portability_subset");
float priority = 1.0f;
VkDeviceQueueCreateInfo qci{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
- qci.queueFamilyIndex = v.graphicsFamily;
+ qci.queueFamilyIndex = v.graphics_family;
qci.queueCount = 1;
qci.pQueuePriorities = &priority;
VkDeviceCreateInfo dci{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
dci.queueCreateInfoCount = 1;
dci.pQueueCreateInfos = &qci;
- dci.enabledExtensionCount = (uint32_t)devExts.size();
- dci.ppEnabledExtensionNames = devExts.data();
+ dci.enabledExtensionCount = (uint32_t)dev_exts.size();
+ dci.ppEnabledExtensionNames = dev_exts.data();
VK_CHECK(vkCreateDevice(v.physical, &dci, nullptr, &v.device));
- vkGetDeviceQueue(v.device, v.graphicsFamily, 0, &v.graphicsQueue);
+ vkGetDeviceQueue(v.device, v.graphics_family, 0, &v.graphics_queue);
DONUT_INFO("Vulkan device: {} (API {}.{}.{}, validation {})",
props.deviceName,
@@ -161,16 +161,16 @@ namespace Donut
return true;
}
- bool VulkanContext::SelfTestClear()
+ auto VulkanContext::self_test_clear() -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) return false;
const uint32_t W = 64, H = 64;
const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
// Offscreen colour image
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
+ VkImage image = VK_NULL_HANDLE; VkDeviceMemory image_mem = VK_NULL_HANDLE;
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.imageType = VK_IMAGE_TYPE_2D;
ici.format = fmt;
@@ -183,13 +183,13 @@ namespace Donut
ici.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
- VkMemoryRequirements imReq{};
- vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
+ VkMemoryRequirements im_req{};
+ vkGetImageMemoryRequirements(v.device, image, &im_req);
+ VkMemoryAllocateInfo im_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ im_alloc.allocationSize = im_req.size;
+ im_alloc.memoryTypeIndex = v.find_memory_type(im_req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(v.device, &im_alloc, nullptr, &image_mem));
+ VK_CHECK(vkBindImageMemory(v.device, image, image_mem, 0));
VkImageView view = VK_NULL_HANDLE;
VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
@@ -210,11 +210,11 @@ namespace Donut
color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference color_ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
- subpass.pColorAttachments = &colorRef;
+ subpass.pColorAttachments = &color_ref;
// Ensure colour writes finish before the read-back copy.
VkSubpassDependency dep{};
@@ -225,40 +225,40 @@ namespace Donut
dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ VkRenderPass render_pass = VK_NULL_HANDLE;
VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rpci.attachmentCount = 1; rpci.pAttachments = &color;
rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
+ VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &render_pass));
VkFramebuffer fb = VK_NULL_HANDLE;
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass;
+ fbci.renderPass = render_pass;
fbci.attachmentCount = 1; fbci.pAttachments = &view;
fbci.width = W; fbci.height = H; fbci.layers = 1;
VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
// Host-visible staging buffer for read-back
- VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory stagingMem = VK_NULL_HANDLE;
+ VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory staging_mem = VK_NULL_HANDLE;
VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bci.size = (VkDeviceSize)W * H * 4;
bci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_CHECK(vkCreateBuffer(v.device, &bci, nullptr, &staging));
- VkMemoryRequirements bReq{};
- vkGetBufferMemoryRequirements(v.device, staging, &bReq);
- VkMemoryAllocateInfo bAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- bAlloc.allocationSize = bReq.size;
- bAlloc.memoryTypeIndex = v.FindMemoryType(bReq.memoryTypeBits,
+ VkMemoryRequirements b_req{};
+ vkGetBufferMemoryRequirements(v.device, staging, &b_req);
+ VkMemoryAllocateInfo b_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ b_alloc.allocationSize = b_req.size;
+ b_alloc.memoryTypeIndex = v.find_memory_type(b_req.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &bAlloc, nullptr, &stagingMem));
- VK_CHECK(vkBindBufferMemory(v.device, staging, stagingMem, 0));
+ VK_CHECK(vkAllocateMemory(v.device, &b_alloc, nullptr, &staging_mem));
+ VK_CHECK(vkBindBufferMemory(v.device, staging, staging_mem, 0));
// Command buffer: clear via render pass, then copy image -> buffer
VkCommandPool pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
- pci.queueFamilyIndex = v.graphicsFamily;
+ pci.queueFamilyIndex = v.graphics_family;
VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &pool));
VkCommandBuffer cmd = VK_NULL_HANDLE;
@@ -273,7 +273,7 @@ namespace Donut
VkClearValue clear{};
clear.color = { { 0.2f, 0.4f, 0.8f, 1.0f } }; // -> RGBA8 (51, 102, 204, 255)
VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb;
+ rpbi.renderPass = render_pass; rpbi.framebuffer = fb;
rpbi.renderArea = { { 0, 0 }, { W, H } };
rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
@@ -290,34 +290,34 @@ namespace Donut
VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
+ VK_CHECK(vkQueueSubmit(v.graphics_queue, 1, &submit, fence));
VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
// Read back + verify
void* mapped = nullptr;
- VK_CHECK(vkMapMemory(v.device, stagingMem, 0, bci.size, 0, &mapped));
+ VK_CHECK(vkMapMemory(v.device, staging_mem, 0, bci.size, 0, &mapped));
const uint8_t* px = (const uint8_t*)mapped;
DONUT_INFO("Vulkan clear self-test: pixel RGBA = ({}, {}, {}, {})",
(int)px[0], (int)px[1], (int)px[2], (int)px[3]);
bool ok = px[0] > 45 && px[0] < 60 && px[1] > 95 && px[1] < 110 &&
px[2] > 195 && px[2] < 210 && px[3] == 255;
- vkUnmapMemory(v.device, stagingMem);
+ vkUnmapMemory(v.device, staging_mem);
DONUT_INFO("Vulkan clear self-test: {}", ok ? "PASS" : "FAIL");
// Cleanup
vkDestroyFence(v.device, fence, nullptr);
vkDestroyCommandPool(v.device, pool, nullptr);
vkDestroyBuffer(v.device, staging, nullptr);
- vkFreeMemory(v.device, stagingMem, nullptr);
+ vkFreeMemory(v.device, staging_mem, nullptr);
vkDestroyFramebuffer(v.device, fb, nullptr);
- vkDestroyRenderPass(v.device, renderPass, nullptr);
+ vkDestroyRenderPass(v.device, render_pass, nullptr);
vkDestroyImageView(v.device, view, nullptr);
vkDestroyImage(v.device, image, nullptr);
- vkFreeMemory(v.device, imageMem, nullptr);
+ vkFreeMemory(v.device, image_mem, nullptr);
return ok;
}
- static std::vector<uint32_t> LoadSpirv(const std::string& path)
+ static std::vector<uint32_t> load_spirv(const std::string& path)
{
std::ifstream f(path, std::ios::binary | std::ios::ate);
if (!f) return {};
@@ -328,28 +328,28 @@ namespace Donut
return data;
}
- bool VulkanContext::SelfTestTriangle()
+ auto VulkanContext::self_test_triangle() -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) return false;
const uint32_t W = 64, H = 64;
const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
// Offscreen image + view (as in the clear test)
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
+ VkImage image = VK_NULL_HANDLE; VkDeviceMemory image_mem = VK_NULL_HANDLE;
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { W, H, 1 };
ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
+ VkMemoryRequirements im_req{}; vkGetImageMemoryRequirements(v.device, image, &im_req);
+ VkMemoryAllocateInfo im_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ im_alloc.allocationSize = im_req.size;
+ im_alloc.memoryTypeIndex = v.find_memory_type(im_req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(v.device, &im_alloc, nullptr, &image_mem));
+ VK_CHECK(vkBindImageMemory(v.device, image, image_mem, 0));
VkImageView view = VK_NULL_HANDLE;
VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
vci.image = image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
@@ -362,29 +362,29 @@ namespace Donut
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference color_ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorRef;
+ subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref;
VkSubpassDependency dep{};
dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL;
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ VkRenderPass render_pass = VK_NULL_HANDLE;
VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rpci.attachmentCount = 1; rpci.pAttachments = &color;
rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
+ VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &render_pass));
VkFramebuffer fb = VK_NULL_HANDLE;
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
+ fbci.renderPass = render_pass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
fbci.width = W; fbci.height = H; fbci.layers = 1;
VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
// Shader modules from Slang SPIR-V
- auto vspv = LoadSpirv("Assets/Shaders/generated/VkPipelineTest.vertexMain.spv");
- auto fspv = LoadSpirv("Assets/Shaders/generated/VkPipelineTest.fragmentMain.spv");
+ auto vspv = load_spirv("assets/shaders/generated/VkPipelineTest.vertexMain.spv");
+ auto fspv = load_spirv("assets/shaders/generated/VkPipelineTest.fragmentMain.spv");
if (vspv.empty() || fspv.empty()) { DONUT_ERROR("Vulkan: VkPipelineTest SPIR-V not found"); return false; }
VkShaderModule vmod = VK_NULL_HANDLE, fmod = VK_NULL_HANDLE;
VkShaderModuleCreateInfo smci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
@@ -426,25 +426,25 @@ namespace Donut
gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia;
gpci.pViewportState = &vps; gpci.pRasterizationState = &rs;
gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = layout; gpci.renderPass = renderPass; gpci.subpass = 0;
+ gpci.layout = layout; gpci.renderPass = render_pass; gpci.subpass = 0;
VK_CHECK(vkCreateGraphicsPipelines(v.device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline));
// Readback staging buffer
- VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory stagingMem = VK_NULL_HANDLE;
+ VkBuffer staging = VK_NULL_HANDLE; VkDeviceMemory staging_mem = VK_NULL_HANDLE;
VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bci.size = (VkDeviceSize)W * H * 4; bci.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VK_CHECK(vkCreateBuffer(v.device, &bci, nullptr, &staging));
- VkMemoryRequirements bReq{}; vkGetBufferMemoryRequirements(v.device, staging, &bReq);
- VkMemoryAllocateInfo bAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- bAlloc.allocationSize = bReq.size;
- bAlloc.memoryTypeIndex = v.FindMemoryType(bReq.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &bAlloc, nullptr, &stagingMem));
- VK_CHECK(vkBindBufferMemory(v.device, staging, stagingMem, 0));
+ VkMemoryRequirements b_req{}; vkGetBufferMemoryRequirements(v.device, staging, &b_req);
+ VkMemoryAllocateInfo b_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ b_alloc.allocationSize = b_req.size;
+ b_alloc.memoryTypeIndex = v.find_memory_type(b_req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+ VK_CHECK(vkAllocateMemory(v.device, &b_alloc, nullptr, &staging_mem));
+ VK_CHECK(vkBindBufferMemory(v.device, staging, staging_mem, 0));
// Record + submit
VkCommandPool pool = VK_NULL_HANDLE;
VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
- pci.queueFamilyIndex = v.graphicsFamily;
+ pci.queueFamilyIndex = v.graphics_family;
VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &pool));
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
@@ -455,7 +455,7 @@ namespace Donut
VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
VkClearValue clear{}; clear.color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb;
+ rpbi.renderPass = render_pass; rpbi.framebuffer = fb;
rpbi.renderArea = { { 0, 0 }, { W, H } };
rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
@@ -473,40 +473,40 @@ namespace Donut
VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
+ VK_CHECK(vkQueueSubmit(v.graphics_queue, 1, &submit, fence));
VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
// Verify: centre pixel should be the mid-gradient (not black)
void* mapped = nullptr;
- VK_CHECK(vkMapMemory(v.device, stagingMem, 0, bci.size, 0, &mapped));
+ VK_CHECK(vkMapMemory(v.device, staging_mem, 0, bci.size, 0, &mapped));
const uint8_t* px = (const uint8_t*)mapped;
size_t c = ((size_t)(H / 2) * W + (W / 2)) * 4;
DONUT_INFO("Vulkan triangle self-test: centre pixel RGBA = ({}, {}, {}, {})",
(int)px[c + 0], (int)px[c + 1], (int)px[c + 2], (int)px[c + 3]);
bool ok = (px[c + 0] > 40 || px[c + 1] > 40) && px[c + 3] == 255;
- vkUnmapMemory(v.device, stagingMem);
+ vkUnmapMemory(v.device, staging_mem);
DONUT_INFO("Vulkan triangle self-test: {}", ok ? "PASS" : "FAIL");
// Cleanup
vkDestroyFence(v.device, fence, nullptr);
vkDestroyCommandPool(v.device, pool, nullptr);
vkDestroyBuffer(v.device, staging, nullptr);
- vkFreeMemory(v.device, stagingMem, nullptr);
+ vkFreeMemory(v.device, staging_mem, nullptr);
vkDestroyPipeline(v.device, pipeline, nullptr);
vkDestroyPipelineLayout(v.device, layout, nullptr);
vkDestroyShaderModule(v.device, vmod, nullptr);
vkDestroyShaderModule(v.device, fmod, nullptr);
vkDestroyFramebuffer(v.device, fb, nullptr);
- vkDestroyRenderPass(v.device, renderPass, nullptr);
+ vkDestroyRenderPass(v.device, render_pass, nullptr);
vkDestroyImageView(v.device, view, nullptr);
vkDestroyImage(v.device, image, nullptr);
- vkFreeMemory(v.device, imageMem, nullptr);
+ vkFreeMemory(v.device, image_mem, nullptr);
return ok;
}
- bool VulkanContext::RenderGeodesic(const char* pngPath)
+ auto VulkanContext::render_geodesic(const char* png_path) -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) return false;
const uint32_t W = 384, H = 216;
@@ -514,19 +514,19 @@ namespace Donut
const float SagA_rs = 1.269e10f;
// Offscreen colour target
- VkImage image = VK_NULL_HANDLE; VkDeviceMemory imageMem = VK_NULL_HANDLE;
+ VkImage image = VK_NULL_HANDLE; VkDeviceMemory image_mem = VK_NULL_HANDLE;
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
ici.imageType = VK_IMAGE_TYPE_2D; ici.format = fmt; ici.extent = { W, H, 1 };
ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VK_CHECK(vkCreateImage(v.device, &ici, nullptr, &image));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(v.device, image, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = v.FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &imAlloc, nullptr, &imageMem));
- VK_CHECK(vkBindImageMemory(v.device, image, imageMem, 0));
+ VkMemoryRequirements im_req{}; vkGetImageMemoryRequirements(v.device, image, &im_req);
+ VkMemoryAllocateInfo im_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ im_alloc.allocationSize = im_req.size;
+ im_alloc.memoryTypeIndex = v.find_memory_type(im_req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(v.device, &im_alloc, nullptr, &image_mem));
+ VK_CHECK(vkBindImageMemory(v.device, image, image_mem, 0));
VkImageView view = VK_NULL_HANDLE;
VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
vci.image = image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
@@ -538,62 +538,62 @@ namespace Donut
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
- VkAttachmentReference colorRef{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+ VkAttachmentReference color_ref{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpass{}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
- subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorRef;
+ subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref;
VkSubpassDependency dep{};
dep.srcSubpass = 0; dep.dstSubpass = VK_SUBPASS_EXTERNAL;
dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dep.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; dep.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ VkRenderPass render_pass = VK_NULL_HANDLE;
VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rpci.attachmentCount = 1; rpci.pAttachments = &color;
rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &renderPass));
+ VK_CHECK(vkCreateRenderPass(v.device, &rpci, nullptr, &render_pass));
VkFramebuffer fb = VK_NULL_HANDLE;
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
+ fbci.renderPass = render_pass; fbci.attachmentCount = 1; fbci.pAttachments = &view;
fbci.width = W; fbci.height = H; fbci.layers = 1;
VK_CHECK(vkCreateFramebuffer(v.device, &fbci, nullptr, &fb));
// Uniform buffers (host-visible), filled to match the shader's std140 layout
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- VkBuffer camBuf, diskBuf, objBuf, simBuf;
- VkDeviceMemory camMem, diskMem, objMem, simMem;
- v.CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, camBuf, camMem);
- v.CreateBuffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, diskBuf, diskMem);
- v.CreateBuffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, objBuf, objMem);
- v.CreateBuffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, simBuf, simMem);
+ const VkMemoryPropertyFlags host_vis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ VkBuffer cam_buf, disk_buf, obj_buf, sim_buf;
+ VkDeviceMemory cam_mem, disk_mem, obj_mem, sim_mem;
+ v.create_buffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, cam_buf, cam_mem);
+ v.create_buffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, disk_buf, disk_mem);
+ v.create_buffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, obj_buf, obj_mem);
+ v.create_buffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, sim_buf, sim_mem);
struct CamUBO {
glm::vec3 pos; float p0; glm::vec3 right; float p1;
glm::vec3 up; float p2; glm::vec3 fwd; float p3;
- float tanHalfFov; float aspect; uint32_t moving; int p4;
+ float tan_half_fov; float aspect; uint32_t moving; int p4;
} cam{};
- glm::vec3 camPos(1e11f, 0.32e11f, 0.0f);
- glm::vec3 fwd = glm::normalize(glm::vec3(0.0f) - camPos);
+ glm::vec3 cam_pos(1e11f, 0.32e11f, 0.0f);
+ glm::vec3 fwd = glm::normalize(glm::vec3(0.0f) - cam_pos);
glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
glm::vec3 up = glm::cross(right, fwd);
- cam.pos = camPos; cam.right = right; cam.up = up; cam.fwd = fwd;
- cam.tanHalfFov = 0.57735f; cam.aspect = (float)W / (float)H; cam.moving = 0;
+ cam.pos = cam_pos; cam.right = right; cam.up = up; cam.fwd = fwd;
+ cam.tan_half_fov = 0.57735f; cam.aspect = (float)W / (float)H; cam.moving = 0;
void* p = nullptr;
- vkMapMemory(v.device, camMem, 0, 128, 0, &p); memcpy(p, &cam, sizeof(cam)); vkUnmapMemory(v.device, camMem);
+ vkMapMemory(v.device, cam_mem, 0, 128, 0, &p); memcpy(p, &cam, sizeof(cam)); vkUnmapMemory(v.device, cam_mem);
float disk[8] = { SagA_rs * 2.2f, SagA_rs * 5.2f, 2.0f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 };
- vkMapMemory(v.device, diskMem, 0, 32, 0, &p); memcpy(p, disk, sizeof(disk)); vkUnmapMemory(v.device, diskMem);
+ vkMapMemory(v.device, disk_mem, 0, 32, 0, &p); memcpy(p, disk, sizeof(disk)); vkUnmapMemory(v.device, disk_mem);
- std::vector<uint8_t> objData(800, 0);
- int numObjects = 1; memcpy(objData.data(), &numObjects, 4);
- float posRadius[4] = { 0, 0, 0, SagA_rs }; memcpy(objData.data() + 16, posRadius, 16);
- float objColor[4] = { 0, 0, 0, 1 }; memcpy(objData.data() + 272, objColor, 16);
- vkMapMemory(v.device, objMem, 0, 800, 0, &p); memcpy(p, objData.data(), 800); vkUnmapMemory(v.device, objMem);
+ std::vector<uint8_t> obj_data(800, 0);
+ int num_objects = 1; memcpy(obj_data.data(), &num_objects, 4);
+ float pos_radius[4] = { 0, 0, 0, SagA_rs }; memcpy(obj_data.data() + 16, pos_radius, 16);
+ float obj_color[4] = { 0, 0, 0, 1 }; memcpy(obj_data.data() + 272, obj_color, 16);
+ vkMapMemory(v.device, obj_mem, 0, 800, 0, &p); memcpy(p, obj_data.data(), 800); vkUnmapMemory(v.device, obj_mem);
- struct SimUBO { int stepsMoving; int stepsStatic; float earlyExit; float time; } sim{ 6000, 6000, 5e12f, 0.0f };
- vkMapMemory(v.device, simMem, 0, 16, 0, &p); memcpy(p, &sim, sizeof(sim)); vkUnmapMemory(v.device, simMem);
+ struct SimUBO { int steps_moving; int steps_static; float early_exit; float time; } sim{ 6000, 6000, 5e12f, 0.0f };
+ vkMapMemory(v.device, sim_mem, 0, 16, 0, &p); memcpy(p, &sim, sizeof(sim)); vkUnmapMemory(v.device, sim_mem);
// Dark cubemap (stands in for the HDRI for now)
- VkImage cube = VK_NULL_HANDLE; VkDeviceMemory cubeMem = VK_NULL_HANDLE;
+ VkImage cube = VK_NULL_HANDLE; VkDeviceMemory cube_mem = VK_NULL_HANDLE;
VkImageCreateInfo cci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
cci.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
cci.imageType = VK_IMAGE_TYPE_2D; cci.format = fmt; cci.extent = { 1, 1, 1 };
@@ -601,37 +601,37 @@ namespace Donut
cci.tiling = VK_IMAGE_TILING_OPTIMAL;
cci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
VK_CHECK(vkCreateImage(v.device, &cci, nullptr, &cube));
- VkMemoryRequirements cubeReq{}; vkGetImageMemoryRequirements(v.device, cube, &cubeReq);
- VkMemoryAllocateInfo cubeAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- cubeAlloc.allocationSize = cubeReq.size;
- cubeAlloc.memoryTypeIndex = v.FindMemoryType(cubeReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(v.device, &cubeAlloc, nullptr, &cubeMem));
- VK_CHECK(vkBindImageMemory(v.device, cube, cubeMem, 0));
- VkImageView cubeView = VK_NULL_HANDLE;
+ VkMemoryRequirements cube_req{}; vkGetImageMemoryRequirements(v.device, cube, &cube_req);
+ VkMemoryAllocateInfo cube_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ cube_alloc.allocationSize = cube_req.size;
+ cube_alloc.memoryTypeIndex = v.find_memory_type(cube_req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(v.device, &cube_alloc, nullptr, &cube_mem));
+ VK_CHECK(vkBindImageMemory(v.device, cube, cube_mem, 0));
+ VkImageView cube_view = VK_NULL_HANDLE;
VkImageViewCreateInfo cvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
cvci.image = cube; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = fmt;
cvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- VK_CHECK(vkCreateImageView(v.device, &cvci, nullptr, &cubeView));
+ VK_CHECK(vkCreateImageView(v.device, &cvci, nullptr, &cube_view));
VkSampler sampler = VK_NULL_HANDLE;
VkSamplerCreateInfo smci{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
smci.magFilter = VK_FILTER_LINEAR; smci.minFilter = VK_FILTER_LINEAR;
smci.addressModeU = smci.addressModeV = smci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
VK_CHECK(vkCreateSampler(v.device, &smci, nullptr, &sampler));
- VkBuffer cubeStaging; VkDeviceMemory cubeStagingMem;
- v.CreateBuffer(6 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, hostVis, cubeStaging, cubeStagingMem);
- uint8_t cubePixels[6 * 4];
- for (int i = 0; i < 6; ++i) { cubePixels[i * 4 + 0] = 6; cubePixels[i * 4 + 1] = 6; cubePixels[i * 4 + 2] = 14; cubePixels[i * 4 + 3] = 255; }
- vkMapMemory(v.device, cubeStagingMem, 0, 24, 0, &p); memcpy(p, cubePixels, 24); vkUnmapMemory(v.device, cubeStagingMem);
+ VkBuffer cube_staging; VkDeviceMemory cube_staging_mem;
+ v.create_buffer(6 * 4, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_vis, cube_staging, cube_staging_mem);
+ uint8_t cube_pixels[6 * 4];
+ for (int i = 0; i < 6; ++i) { cube_pixels[i * 4 + 0] = 6; cube_pixels[i * 4 + 1] = 6; cube_pixels[i * 4 + 2] = 14; cube_pixels[i * 4 + 3] = 255; }
+ vkMapMemory(v.device, cube_staging_mem, 0, 24, 0, &p); memcpy(p, cube_pixels, 24); vkUnmapMemory(v.device, cube_staging_mem);
// Descriptor set: 4 UBOs (bindings 0-3) + cubemap sampler (binding 4)
VkDescriptorSetLayoutBinding binds[5]{};
for (int i = 0; i < 4; ++i) { binds[i].binding = i; binds[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[i].descriptorCount = 1; binds[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; }
binds[4].binding = 4; binds[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[4].descriptorCount = 1; binds[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
+ VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
dslci.bindingCount = 5; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(v.device, &dslci, nullptr, &setLayout));
+ VK_CHECK(vkCreateDescriptorSetLayout(v.device, &dslci, nullptr, &set_layout));
VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } };
VkDescriptorPool pool = VK_NULL_HANDLE;
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
@@ -639,12 +639,12 @@ namespace Donut
VK_CHECK(vkCreateDescriptorPool(v.device, &dpci, nullptr, &pool));
VkDescriptorSet set = VK_NULL_HANDLE;
VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &setLayout;
+ dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &set_layout;
VK_CHECK(vkAllocateDescriptorSets(v.device, &dsai, &set));
- // Load geodesic SPIR-V + build the pipeline
- auto vspv = LoadSpirv("Assets/Shaders/generated/Geodesic.vertexMain.spv");
- auto fspv = LoadSpirv("Assets/Shaders/generated/Geodesic.fragmentMain.spv");
+ // load geodesic SPIR-V + build the pipeline
+ auto vspv = load_spirv("assets/shaders/generated/Geodesic.vertexMain.spv");
+ auto fspv = load_spirv("assets/shaders/generated/Geodesic.fragmentMain.spv");
if (vspv.empty() || fspv.empty()) { DONUT_ERROR("Vulkan: geodesic SPIR-V not found"); return false; }
VkShaderModule vmod, fmod;
VkShaderModuleCreateInfo smci2{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
@@ -653,7 +653,7 @@ namespace Donut
VkPipelineLayout layout = VK_NULL_HANDLE;
VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &setLayout;
+ plci.setLayoutCount = 1; plci.pSetLayouts = &set_layout;
VK_CHECK(vkCreatePipelineLayout(v.device, &plci, nullptr, &layout));
VkPipelineShaderStageCreateInfo stages[2]{};
@@ -676,7 +676,7 @@ namespace Donut
gpci.stageCount = 2; gpci.pStages = stages;
gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = layout; gpci.renderPass = renderPass; gpci.subpass = 0;
+ gpci.layout = layout; gpci.renderPass = render_pass; gpci.subpass = 0;
VK_CHECK(vkCreateGraphicsPipelines(v.device, VK_NULL_HANDLE, 1, &gpci, nullptr, &pipeline));
// Fullscreen quad (position.xy, texcoord.uv)
@@ -684,25 +684,25 @@ namespace Donut
-1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f, 1.f, -1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
};
- VkBuffer vbuf; VkDeviceMemory vbufMem;
- v.CreateBuffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, vbuf, vbufMem);
- vkMapMemory(v.device, vbufMem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(v.device, vbufMem);
+ VkBuffer vbuf; VkDeviceMemory vbuf_mem;
+ v.create_buffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, vbuf, vbuf_mem);
+ vkMapMemory(v.device, vbuf_mem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(v.device, vbuf_mem);
// Write the descriptor set
VkDescriptorBufferInfo bi[4] = {
- { camBuf, 0, VK_WHOLE_SIZE }, { diskBuf, 0, VK_WHOLE_SIZE }, { objBuf, 0, VK_WHOLE_SIZE }, { simBuf, 0, VK_WHOLE_SIZE } };
- VkDescriptorImageInfo ii{ sampler, cubeView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
+ { cam_buf, 0, VK_WHOLE_SIZE }, { disk_buf, 0, VK_WHOLE_SIZE }, { obj_buf, 0, VK_WHOLE_SIZE }, { sim_buf, 0, VK_WHOLE_SIZE } };
+ VkDescriptorImageInfo ii{ sampler, cube_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
VkWriteDescriptorSet writes[5]{};
for (int i = 0; i < 4; ++i) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].dstSet = set; writes[i].dstBinding = i; writes[i].descriptorCount = 1; writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writes[i].pBufferInfo = &bi[i]; }
writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[4].dstSet = set; writes[4].dstBinding = 4; writes[4].descriptorCount = 1; writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[4].pImageInfo = &ii;
vkUpdateDescriptorSets(v.device, 5, writes, 0, nullptr);
- VkBuffer readback; VkDeviceMemory readbackMem;
- v.CreateBuffer((VkDeviceSize)W * H * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT, hostVis, readback, readbackMem);
+ VkBuffer readback; VkDeviceMemory readback_mem;
+ v.create_buffer((VkDeviceSize)W * H * 4, VK_BUFFER_USAGE_TRANSFER_DST_BIT, host_vis, readback, readback_mem);
// Record + submit
VkCommandPool cpool = VK_NULL_HANDLE;
- VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; pci.queueFamilyIndex = v.graphicsFamily;
+ VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; pci.queueFamilyIndex = v.graphics_family;
VK_CHECK(vkCreateCommandPool(v.device, &pci, nullptr, &cpool));
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; cbai.commandPool = cpool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
@@ -710,21 +710,21 @@ namespace Donut
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
- VkImageMemoryBarrier toDst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
- toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- toDst.image = cube; toDst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- toDst.srcAccessMask = 0; toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toDst);
- VkBufferImageCopy cubeCopy{}; cubeCopy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6 }; cubeCopy.imageExtent = { 1, 1, 1 };
- vkCmdCopyBufferToImage(cmd, cubeStaging, cube, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cubeCopy);
- VkImageMemoryBarrier toRead = toDst;
- toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
+ VkImageMemoryBarrier to_dst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ to_dst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; to_dst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ to_dst.image = cube; to_dst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
+ to_dst.srcAccessMask = 0; to_dst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_dst);
+ VkBufferImageCopy cube_copy{}; cube_copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 6 }; cube_copy.imageExtent = { 1, 1, 1 };
+ vkCmdCopyBufferToImage(cmd, cube_staging, cube, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cube_copy);
+ VkImageMemoryBarrier to_read = to_dst;
+ to_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; to_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ to_read.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; to_read.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_read);
VkClearValue clear{}; clear.color = { { 0, 0, 0, 1 } };
VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass; rpbi.framebuffer = fb; rpbi.renderArea = { { 0, 0 }, { W, H } };
+ rpbi.renderPass = render_pass; rpbi.framebuffer = fb; rpbi.renderArea = { { 0, 0 }, { W, H } };
rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
@@ -739,51 +739,51 @@ namespace Donut
VkFence fence = VK_NULL_HANDLE; VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
VK_CHECK(vkCreateFence(v.device, &fci, nullptr, &fence));
VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; submit.commandBufferCount = 1; submit.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(v.graphicsQueue, 1, &submit, fence));
+ VK_CHECK(vkQueueSubmit(v.graphics_queue, 1, &submit, fence));
VK_CHECK(vkWaitForFences(v.device, 1, &fence, VK_TRUE, UINT64_MAX));
- vkMapMemory(v.device, readbackMem, 0, (VkDeviceSize)W * H * 4, 0, &p);
- stbi_write_png(pngPath, W, H, 4, p, W * 4);
- vkUnmapMemory(v.device, readbackMem);
- DONUT_INFO("Vulkan geodesic render written to {}", pngPath);
+ vkMapMemory(v.device, readback_mem, 0, (VkDeviceSize)W * H * 4, 0, &p);
+ stbi_write_png(png_path, W, H, 4, p, W * 4);
+ vkUnmapMemory(v.device, readback_mem);
+ DONUT_INFO("Vulkan geodesic render written to {}", png_path);
vkDestroyFence(v.device, fence, nullptr);
vkDestroyCommandPool(v.device, cpool, nullptr);
- vkDestroyBuffer(v.device, readback, nullptr); vkFreeMemory(v.device, readbackMem, nullptr);
- vkDestroyBuffer(v.device, vbuf, nullptr); vkFreeMemory(v.device, vbufMem, nullptr);
+ vkDestroyBuffer(v.device, readback, nullptr); vkFreeMemory(v.device, readback_mem, nullptr);
+ vkDestroyBuffer(v.device, vbuf, nullptr); vkFreeMemory(v.device, vbuf_mem, nullptr);
vkDestroyPipeline(v.device, pipeline, nullptr); vkDestroyPipelineLayout(v.device, layout, nullptr);
vkDestroyShaderModule(v.device, vmod, nullptr); vkDestroyShaderModule(v.device, fmod, nullptr);
- vkDestroyDescriptorPool(v.device, pool, nullptr); vkDestroyDescriptorSetLayout(v.device, setLayout, nullptr);
- vkDestroySampler(v.device, sampler, nullptr); vkDestroyImageView(v.device, cubeView, nullptr);
- vkDestroyImage(v.device, cube, nullptr); vkFreeMemory(v.device, cubeMem, nullptr);
- vkDestroyBuffer(v.device, cubeStaging, nullptr); vkFreeMemory(v.device, cubeStagingMem, nullptr);
- vkDestroyBuffer(v.device, camBuf, nullptr); vkFreeMemory(v.device, camMem, nullptr);
- vkDestroyBuffer(v.device, diskBuf, nullptr); vkFreeMemory(v.device, diskMem, nullptr);
- vkDestroyBuffer(v.device, objBuf, nullptr); vkFreeMemory(v.device, objMem, nullptr);
- vkDestroyBuffer(v.device, simBuf, nullptr); vkFreeMemory(v.device, simMem, nullptr);
- vkDestroyFramebuffer(v.device, fb, nullptr); vkDestroyRenderPass(v.device, renderPass, nullptr);
- vkDestroyImageView(v.device, view, nullptr); vkDestroyImage(v.device, image, nullptr); vkFreeMemory(v.device, imageMem, nullptr);
+ vkDestroyDescriptorPool(v.device, pool, nullptr); vkDestroyDescriptorSetLayout(v.device, set_layout, nullptr);
+ vkDestroySampler(v.device, sampler, nullptr); vkDestroyImageView(v.device, cube_view, nullptr);
+ vkDestroyImage(v.device, cube, nullptr); vkFreeMemory(v.device, cube_mem, nullptr);
+ vkDestroyBuffer(v.device, cube_staging, nullptr); vkFreeMemory(v.device, cube_staging_mem, nullptr);
+ vkDestroyBuffer(v.device, cam_buf, nullptr); vkFreeMemory(v.device, cam_mem, nullptr);
+ vkDestroyBuffer(v.device, disk_buf, nullptr); vkFreeMemory(v.device, disk_mem, nullptr);
+ vkDestroyBuffer(v.device, obj_buf, nullptr); vkFreeMemory(v.device, obj_mem, nullptr);
+ vkDestroyBuffer(v.device, sim_buf, nullptr); vkFreeMemory(v.device, sim_mem, nullptr);
+ vkDestroyFramebuffer(v.device, fb, nullptr); vkDestroyRenderPass(v.device, render_pass, nullptr);
+ vkDestroyImageView(v.device, view, nullptr); vkDestroyImage(v.device, image, nullptr); vkFreeMemory(v.device, image_mem, nullptr);
return true;
}
- void VulkanContext::Shutdown()
+ auto VulkanContext::shutdown() -> void
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device) { vkDestroyDevice(v.device, nullptr); v.device = VK_NULL_HANDLE; }
if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; }
}
- bool VulkanSelfTest()
+ auto vulkan_self_test() -> bool
{
VulkanContext ctx;
- if (!ctx.Init())
+ if (!ctx.init())
{
DONUT_ERROR("Vulkan: initialization failed");
return false;
}
- bool ok = ctx.SelfTestClear();
- ok = ctx.SelfTestTriangle() && ok;
- ctx.Shutdown();
+ bool ok = ctx.self_test_clear();
+ ok = ctx.self_test_triangle() && ok;
+ ctx.shutdown();
return ok;
}
}
diff --git a/src/Platform/Vulkan/VulkanContext.h b/src/platform/vulkan/vulkan_context.h
index 71bceda..de57c90 100644
--- a/src/Platform/Vulkan/VulkanContext.h
+++ b/src/platform/vulkan/vulkan_context.h
@@ -13,29 +13,29 @@ namespace Donut
// Creates the instance, picks a physical device, and creates the logical
// device + graphics queue. Returns false (and logs) on failure.
- bool Init();
- void Shutdown();
+ auto init() -> bool;
+ auto shutdown() -> void;
// Phase 1 verification: renders a known clear colour into an offscreen
// image and reads it back, confirming instance -> device -> render pass
// -> command buffer -> submit -> read-back all work end to end.
- bool SelfTestClear();
+ auto self_test_clear() -> bool;
// Phase 2/3 verification: builds a graphics pipeline from Slang-compiled
// SPIR-V and draws a full-screen gradient triangle into the offscreen
// image, confirming the SPIR-V -> pipeline -> draw path works.
- bool SelfTestTriangle();
+ auto self_test_triangle() -> bool;
// B-3: renders the geodesic (black hole) fragment shader through Vulkan
// into an offscreen image and writes it to pngPath. Exercises UBOs,
// descriptor sets, a cubemap sampler and the geodesic pipeline.
- bool RenderGeodesic(const char* pngPath);
+ auto render_geodesic(const char* pngPath) -> bool;
private:
struct Impl;
- Impl* m_Impl = nullptr;
+ Impl* m_impl = nullptr;
};
- // Convenience one-shot: Init() + SelfTestClear() + Shutdown(). Logs results.
- bool VulkanSelfTest();
+ // Convenience one-shot: init() + self_test_clear() + shutdown(). Logs results.
+ auto vulkan_self_test() -> bool;
}
diff --git a/src/Platform/Vulkan/VulkanIndexBuffer.cpp b/src/platform/vulkan/vulkan_index_buffer.cpp
index 930401e..a6e5912 100644
--- a/src/Platform/Vulkan/VulkanIndexBuffer.cpp
+++ b/src/platform/vulkan/vulkan_index_buffer.cpp
@@ -1,9 +1,9 @@
-#include "VulkanIndexBuffer.h"
+#include "vulkan_index_buffer.h"
namespace Donut
{
VulkanIndexBuffer::VulkanIndexBuffer(uint32_t* indices, uint32_t count)
- : m_Count(count)
+ : m_count(count)
{
// TODO(Hachem): Implement Vulkan index buffer creation
}
@@ -13,12 +13,12 @@ namespace Donut
// TODO(Hachem): Implement Vulkan index buffer cleanup
}
- void VulkanIndexBuffer::Bind() const
+ auto VulkanIndexBuffer::bind() const -> void
{
// TODO(Hachem): Implement Vulkan index buffer binding
}
- void VulkanIndexBuffer::Unbind() const
+ auto VulkanIndexBuffer::unbind() const -> void
{
// TODO(Hachem): Implement Vulkan index buffer unbinding
}
diff --git a/src/platform/vulkan/vulkan_index_buffer.h b/src/platform/vulkan/vulkan_index_buffer.h
new file mode 100644
index 0000000..a0734a8
--- /dev/null
+++ b/src/platform/vulkan/vulkan_index_buffer.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "rendering/index_buffer.h"
+
+namespace Donut
+{
+ class VulkanIndexBuffer
+ : public IndexBuffer
+ {
+ public:
+ VulkanIndexBuffer(uint32_t* indices, uint32_t count);
+ virtual ~VulkanIndexBuffer();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto get_count() const -> uint32_t override{ return m_count; }
+ private:
+ uint32_t m_renderer_id;
+ uint32_t m_count;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanRenderer.cpp b/src/platform/vulkan/vulkan_renderer.cpp
index eb92695..95a546b 100644
--- a/src/Platform/Vulkan/VulkanRenderer.cpp
+++ b/src/platform/vulkan/vulkan_renderer.cpp
@@ -1,6 +1,6 @@
-#include "VulkanRenderer.h"
-#include "Core/Log.h"
-#include "Core/Camera.h"
+#include "vulkan_renderer.h"
+#include "core/log.h"
+#include "core/camera.h"
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
@@ -32,7 +32,7 @@ namespace Donut
static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
- void VulkanPrepareGLFW()
+ auto vulkan_prepare_glfw() -> void
{
#ifdef __APPLE__
if (!getenv("VK_ICD_FILENAMES"))
@@ -43,7 +43,7 @@ namespace Donut
setenv("DYLD_LIBRARY_PATH", "/opt/homebrew/lib", 0);
#endif
// GLFW dlopen's the Vulkan loader by bare name, which fails on
- // macOS/Homebrew; hand it the loader entry point we already link against.
+ // mac_os/Homebrew; hand it the loader entry point we already link against.
glfwInitVulkanLoader(vkGetInstanceProcAddr);
}
@@ -51,97 +51,97 @@ namespace Donut
{
GLFWwindow* window = nullptr;
int width = 0, height = 0;
- bool framebufferResized = false;
+ bool framebuffer_resized = false;
VkInstance instance = VK_NULL_HANDLE;
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkPhysicalDevice physical = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
- uint32_t graphicsFamily = 0, presentFamily = 0;
- VkQueue graphicsQueue = VK_NULL_HANDLE, presentQueue = VK_NULL_HANDLE;
+ uint32_t graphics_family = 0, present_family = 0;
+ VkQueue graphics_queue = VK_NULL_HANDLE, present_queue = VK_NULL_HANDLE;
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
- VkFormat swapchainFormat = VK_FORMAT_B8G8R8A8_UNORM;
- VkExtent2D swapchainExtent{};
+ VkFormat swapchain_format = VK_FORMAT_B8G8R8A8_UNORM;
+ VkExtent2D swapchain_extent{};
std::vector<VkImage> images;
- std::vector<VkImageView> imageViews;
- VkRenderPass renderPass = VK_NULL_HANDLE;
+ std::vector<VkImageView> image_views;
+ VkRenderPass render_pass = VK_NULL_HANDLE;
std::vector<VkFramebuffer> framebuffers;
- VkCommandPool commandPool = VK_NULL_HANDLE;
- std::vector<VkCommandBuffer> commandBuffers; // MAX_FRAMES_IN_FLIGHT
+ VkCommandPool command_pool = VK_NULL_HANDLE;
+ std::vector<VkCommandBuffer> command_buffers; // MAX_FRAMES_IN_FLIGHT
- std::vector<VkSemaphore> imageAvailable; // per frame in flight
- std::vector<VkSemaphore> renderFinished; // per swapchain image
- std::vector<VkFence> inFlight; // per frame in flight
- std::vector<VkFence> imagesInFlight; // per swapchain image
- uint32_t currentFrame = 0;
+ std::vector<VkSemaphore> image_available; // per frame in flight
+ std::vector<VkSemaphore> render_finished; // per swapchain image
+ std::vector<VkFence> in_flight; // per frame in flight
+ std::vector<VkFence> images_in_flight; // per swapchain image
+ uint32_t current_frame = 0;
- VkDescriptorPool imguiPool = VK_NULL_HANDLE;
- bool imguiInit = false;
+ VkDescriptorPool imgui_pool = VK_NULL_HANDLE;
+ bool imgui_init = false;
- VkPhysicalDeviceMemoryProperties memProps{};
+ VkPhysicalDeviceMemoryProperties mem_props{};
// Geodesic scene, rendered every frame into a fixed low-resolution
// offscreen image (keeps each draw well under the Metal GPU watchdog),
// then upscaled onto the swapchain by the present pass below.
static constexpr uint32_t GEO_W = 480, GEO_H = 270;
- VkImage geoImage = VK_NULL_HANDLE;
- VkDeviceMemory geoImageMem = VK_NULL_HANDLE;
- VkImageView geoImageView = VK_NULL_HANDLE;
- VkRenderPass geoRenderPass = VK_NULL_HANDLE;
- VkFramebuffer geoFramebuffer = VK_NULL_HANDLE;
- VkBuffer camBuf = VK_NULL_HANDLE, diskBuf = VK_NULL_HANDLE, objBuf = VK_NULL_HANDLE, simBuf = VK_NULL_HANDLE;
- VkDeviceMemory camMem = VK_NULL_HANDLE, diskMem = VK_NULL_HANDLE, objMem = VK_NULL_HANDLE, simMem = VK_NULL_HANDLE;
- void* camMapped = nullptr;
- void* simMapped = nullptr;
+ VkImage geo_image = VK_NULL_HANDLE;
+ VkDeviceMemory geo_image_mem = VK_NULL_HANDLE;
+ VkImageView geo_image_view = VK_NULL_HANDLE;
+ VkRenderPass geo_render_pass = VK_NULL_HANDLE;
+ VkFramebuffer geo_framebuffer = VK_NULL_HANDLE;
+ VkBuffer cam_buf = VK_NULL_HANDLE, disk_buf = VK_NULL_HANDLE, obj_buf = VK_NULL_HANDLE, sim_buf = VK_NULL_HANDLE;
+ VkDeviceMemory cam_mem = VK_NULL_HANDLE, disk_mem = VK_NULL_HANDLE, obj_mem = VK_NULL_HANDLE, sim_mem = VK_NULL_HANDLE;
+ void* cam_mapped = nullptr;
+ void* sim_mapped = nullptr;
Camera camera{ 60.0f, (float)GEO_W / (float)GEO_H, 0.1f, 100.0f };
- bool leftWasDown = false;
- VkImage cubeImage = VK_NULL_HANDLE; VkDeviceMemory cubeMem = VK_NULL_HANDLE;
- VkImageView cubeView = VK_NULL_HANDLE; VkSampler cubeSampler = VK_NULL_HANDLE;
- VkDescriptorSetLayout geoSetLayout = VK_NULL_HANDLE;
- VkDescriptorPool geoPool = VK_NULL_HANDLE;
- VkDescriptorSet geoSet = VK_NULL_HANDLE;
- VkPipelineLayout geoPipelineLayout = VK_NULL_HANDLE;
- VkPipeline geoPipeline = VK_NULL_HANDLE;
- VkBuffer quadVB = VK_NULL_HANDLE; VkDeviceMemory quadVBMem = VK_NULL_HANDLE;
- double startTime = 0.0;
- VkFence geoInUse = VK_NULL_HANDLE; // previous frame's fence; guards the shared geodesic image
+ bool left_was_down = false;
+ VkImage cube_image = VK_NULL_HANDLE; VkDeviceMemory cube_mem = VK_NULL_HANDLE;
+ VkImageView cube_view = VK_NULL_HANDLE; VkSampler cube_sampler = VK_NULL_HANDLE;
+ VkDescriptorSetLayout geo_set_layout = VK_NULL_HANDLE;
+ VkDescriptorPool geo_pool = VK_NULL_HANDLE;
+ VkDescriptorSet geo_set = VK_NULL_HANDLE;
+ VkPipelineLayout geo_pipeline_layout = VK_NULL_HANDLE;
+ VkPipeline geo_pipeline = VK_NULL_HANDLE;
+ VkBuffer quad_vb = VK_NULL_HANDLE; VkDeviceMemory quad_vb_mem = VK_NULL_HANDLE;
+ double start_time = 0.0;
+ VkFence geo_in_use = VK_NULL_HANDLE; // previous frame's fence; guards the shared geodesic image
// Present pass: samples the geodesic image with a full-screen textured
// quad, drawn into the swapchain render pass just before the ImGui UI.
- VkSampler presentSampler = VK_NULL_HANDLE;
- VkDescriptorSetLayout presentSetLayout = VK_NULL_HANDLE;
- VkDescriptorPool presentPool = VK_NULL_HANDLE;
- VkDescriptorSet presentSet = VK_NULL_HANDLE;
- VkPipelineLayout presentPipelineLayout = VK_NULL_HANDLE;
- VkPipeline presentPipeline = VK_NULL_HANDLE;
+ VkSampler present_sampler = VK_NULL_HANDLE;
+ VkDescriptorSetLayout present_set_layout = VK_NULL_HANDLE;
+ VkDescriptorPool present_pool = VK_NULL_HANDLE;
+ VkDescriptorSet present_set = VK_NULL_HANDLE;
+ VkPipelineLayout present_pipeline_layout = VK_NULL_HANDLE;
+ VkPipeline present_pipeline = VK_NULL_HANDLE;
- uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const;
- bool CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buf, VkDeviceMemory& mem) const;
- static std::vector<uint32_t> LoadSpirv(const std::string& path);
- bool CreateShaderModule(const std::string& path, VkShaderModule& out) const;
+ auto find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags flags) const -> uint32_t;
+ auto create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buf, VkDeviceMemory& mem) const -> bool;
+ static auto load_spirv(const std::string& path) -> std::vector<uint32_t>;
+ auto create_shader_module(const std::string& path, VkShaderModule& out) const -> bool;
- bool CreateInstance();
- bool PickPhysicalAndDevice();
- bool CreateSwapchain();
- bool CreateImageViews();
- bool CreateRenderPass();
- bool CreateFramebuffers();
- bool CreateCommandBuffers();
- bool CreateSyncObjects();
- bool CreateGeodesicResources();
- bool CreateHDRICubemap(const char* path);
- bool CreatePresentResources();
- void ProcessInput();
- void UpdateGeodesicUniforms();
- void DestroyGeodesicResources();
- bool RecreateSwapchain();
- void CleanupSwapchain();
- bool RecordCommandBuffer(VkCommandBuffer cmd, uint32_t imageIndex, const glm::vec4& clear, ImDrawData* drawData);
+ auto create_instance() -> bool;
+ auto pick_physical_and_device() -> bool;
+ auto create_swapchain() -> bool;
+ auto create_image_views() -> bool;
+ auto create_render_pass() -> bool;
+ auto create_framebuffers() -> bool;
+ auto create_command_buffers() -> bool;
+ auto create_sync_objects() -> bool;
+ auto create_geodesic_resources() -> bool;
+ auto create_hdri_cubemap(const char* path) -> bool;
+ auto create_present_resources() -> bool;
+ auto process_input() -> void;
+ auto update_geodesic_uniforms() -> void;
+ auto destroy_geodesic_resources() -> void;
+ auto recreate_swapchain() -> bool;
+ auto cleanup_swapchain() -> void;
+ auto record_command_buffer(VkCommandBuffer cmd, uint32_t image_index, const glm::vec4& clear, ImDrawData* draw_data) -> bool;
};
- bool VulkanRenderer::Impl::CreateInstance()
+ auto VulkanRenderer::Impl::create_instance() -> bool
{
#ifdef __APPLE__
if (!getenv("VK_ICD_FILENAMES"))
@@ -166,10 +166,10 @@ namespace Donut
exts.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
std::vector<const char*> layers;
- uint32_t layerCount = 0;
- vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
- std::vector<VkLayerProperties> avail(layerCount);
- vkEnumerateInstanceLayerProperties(&layerCount, avail.data());
+ uint32_t layer_count = 0;
+ vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
+ std::vector<VkLayerProperties> avail(layer_count);
+ vkEnumerateInstanceLayerProperties(&layer_count, avail.data());
for (const auto& l : avail)
if (std::strcmp(l.layerName, "VK_LAYER_KHRONOS_validation") == 0)
layers.push_back("VK_LAYER_KHRONOS_validation");
@@ -200,7 +200,7 @@ namespace Donut
return true;
}
- bool VulkanRenderer::Impl::PickPhysicalAndDevice()
+ auto VulkanRenderer::Impl::pick_physical_and_device() -> bool
{
uint32_t count = 0;
vkEnumeratePhysicalDevices(instance, &count, nullptr);
@@ -209,33 +209,33 @@ namespace Donut
vkEnumeratePhysicalDevices(instance, &count, devices.data());
physical = devices[0];
- uint32_t qCount = 0;
- vkGetPhysicalDeviceQueueFamilyProperties(physical, &qCount, nullptr);
- std::vector<VkQueueFamilyProperties> qfams(qCount);
- vkGetPhysicalDeviceQueueFamilyProperties(physical, &qCount, qfams.data());
- bool foundG = false, foundP = false;
- for (uint32_t i = 0; i < qCount; ++i)
+ uint32_t q_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(physical, &q_count, nullptr);
+ std::vector<VkQueueFamilyProperties> qfams(q_count);
+ vkGetPhysicalDeviceQueueFamilyProperties(physical, &q_count, qfams.data());
+ bool found_g = false, found_p = false;
+ for (uint32_t i = 0; i < q_count; ++i)
{
- if (!foundG && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { graphicsFamily = i; foundG = true; }
+ if (!found_g && (qfams[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { graphics_family = i; found_g = true; }
VkBool32 present = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(physical, i, surface, &present);
- if (!foundP && present) { presentFamily = i; foundP = true; }
+ if (!found_p && present) { present_family = i; found_p = true; }
}
- if (!foundG || !foundP) { DONUT_ERROR("Vulkan: no graphics/present queue"); return false; }
+ if (!found_g || !found_p) { DONUT_ERROR("Vulkan: no graphics/present queue"); return false; }
- std::vector<const char*> devExts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
- uint32_t devExtCount = 0;
- vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, nullptr);
- std::vector<VkExtensionProperties> devExtProps(devExtCount);
- vkEnumerateDeviceExtensionProperties(physical, nullptr, &devExtCount, devExtProps.data());
- for (const auto& e : devExtProps)
+ std::vector<const char*> dev_exts = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
+ uint32_t dev_ext_count = 0;
+ vkEnumerateDeviceExtensionProperties(physical, nullptr, &dev_ext_count, nullptr);
+ std::vector<VkExtensionProperties> dev_ext_props(dev_ext_count);
+ vkEnumerateDeviceExtensionProperties(physical, nullptr, &dev_ext_count, dev_ext_props.data());
+ for (const auto& e : dev_ext_props)
if (std::strcmp(e.extensionName, "VK_KHR_portability_subset") == 0)
- devExts.push_back("VK_KHR_portability_subset");
+ dev_exts.push_back("VK_KHR_portability_subset");
float priority = 1.0f;
std::vector<VkDeviceQueueCreateInfo> qcis;
- uint32_t families[2] = { graphicsFamily, presentFamily };
- for (uint32_t i = 0; i < (graphicsFamily == presentFamily ? 1u : 2u); ++i)
+ uint32_t families[2] = { graphics_family, present_family };
+ for (uint32_t i = 0; i < (graphics_family == present_family ? 1u : 2u); ++i)
{
VkDeviceQueueCreateInfo qci{ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
qci.queueFamilyIndex = families[i];
@@ -246,52 +246,52 @@ namespace Donut
VkDeviceCreateInfo dci{ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
dci.queueCreateInfoCount = (uint32_t)qcis.size();
dci.pQueueCreateInfos = qcis.data();
- dci.enabledExtensionCount = (uint32_t)devExts.size();
- dci.ppEnabledExtensionNames = devExts.data();
+ dci.enabledExtensionCount = (uint32_t)dev_exts.size();
+ dci.ppEnabledExtensionNames = dev_exts.data();
VK_CHECK(vkCreateDevice(physical, &dci, nullptr, &device));
- vkGetDeviceQueue(device, graphicsFamily, 0, &graphicsQueue);
- vkGetDeviceQueue(device, presentFamily, 0, &presentQueue);
+ vkGetDeviceQueue(device, graphics_family, 0, &graphics_queue);
+ vkGetDeviceQueue(device, present_family, 0, &present_queue);
VkPhysicalDeviceProperties props{};
vkGetPhysicalDeviceProperties(physical, &props);
- vkGetPhysicalDeviceMemoryProperties(physical, &memProps);
+ vkGetPhysicalDeviceMemoryProperties(physical, &mem_props);
DONUT_INFO("Vulkan device: {}", props.deviceName);
return true;
}
- bool VulkanRenderer::Impl::CreateSwapchain()
+ auto VulkanRenderer::Impl::create_swapchain() -> bool
{
VkSurfaceCapabilitiesKHR caps{};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical, surface, &caps);
- uint32_t fmtCount = 0;
- vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, nullptr);
- std::vector<VkSurfaceFormatKHR> formats(fmtCount);
- vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmtCount, formats.data());
+ uint32_t fmt_count = 0;
+ vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmt_count, nullptr);
+ std::vector<VkSurfaceFormatKHR> formats(fmt_count);
+ vkGetPhysicalDeviceSurfaceFormatsKHR(physical, surface, &fmt_count, formats.data());
VkSurfaceFormatKHR chosen = formats[0];
for (const auto& f : formats)
if (f.format == VK_FORMAT_B8G8R8A8_UNORM && f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
chosen = f;
- swapchainFormat = chosen.format;
+ swapchain_format = chosen.format;
if (caps.currentExtent.width != UINT32_MAX)
- swapchainExtent = caps.currentExtent;
+ swapchain_extent = caps.currentExtent;
else
{
- swapchainExtent.width = std::clamp((uint32_t)width, caps.minImageExtent.width, caps.maxImageExtent.width);
- swapchainExtent.height = std::clamp((uint32_t)height, caps.minImageExtent.height, caps.maxImageExtent.height);
+ swapchain_extent.width = std::clamp((uint32_t)width, caps.minImageExtent.width, caps.maxImageExtent.width);
+ swapchain_extent.height = std::clamp((uint32_t)height, caps.minImageExtent.height, caps.maxImageExtent.height);
}
- uint32_t imageCount = caps.minImageCount + 1;
- if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount)
- imageCount = caps.maxImageCount;
+ uint32_t image_count = caps.minImageCount + 1;
+ if (caps.maxImageCount > 0 && image_count > caps.maxImageCount)
+ image_count = caps.maxImageCount;
VkSwapchainCreateInfoKHR sci{ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
sci.surface = surface;
- sci.minImageCount = imageCount;
+ sci.minImageCount = image_count;
sci.imageFormat = chosen.format;
sci.imageColorSpace = chosen.colorSpace;
- sci.imageExtent = swapchainExtent;
+ sci.imageExtent = swapchain_extent;
sci.imageArrayLayers = 1;
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
sci.preTransform = caps.currentTransform;
@@ -299,12 +299,12 @@ namespace Donut
sci.presentMode = VK_PRESENT_MODE_FIFO_KHR; // always supported, vsync
sci.clipped = VK_TRUE;
- uint32_t famIdx[2] = { graphicsFamily, presentFamily };
- if (graphicsFamily != presentFamily)
+ uint32_t fam_idx[2] = { graphics_family, present_family };
+ if (graphics_family != present_family)
{
sci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
sci.queueFamilyIndexCount = 2;
- sci.pQueueFamilyIndices = famIdx;
+ sci.pQueueFamilyIndices = fam_idx;
}
else
sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -317,25 +317,25 @@ namespace Donut
return true;
}
- bool VulkanRenderer::Impl::CreateImageViews()
+ auto VulkanRenderer::Impl::create_image_views() -> bool
{
- imageViews.resize(images.size());
+ image_views.resize(images.size());
for (size_t i = 0; i < images.size(); ++i)
{
VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
vci.image = images[i];
vci.viewType = VK_IMAGE_VIEW_TYPE_2D;
- vci.format = swapchainFormat;
+ vci.format = swapchain_format;
vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &vci, nullptr, &imageViews[i]));
+ VK_CHECK(vkCreateImageView(device, &vci, nullptr, &image_views[i]));
}
return true;
}
- bool VulkanRenderer::Impl::CreateRenderPass()
+ auto VulkanRenderer::Impl::create_render_pass() -> bool
{
VkAttachmentDescription color{};
- color.format = swapchainFormat;
+ color.format = swapchain_format;
color.samples = VK_SAMPLE_COUNT_1_BIT;
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@@ -361,70 +361,70 @@ namespace Donut
rpci.attachmentCount = 1; rpci.pAttachments = &color;
rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
rpci.dependencyCount = 1; rpci.pDependencies = &dep;
- VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &renderPass));
+ VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &render_pass));
return true;
}
- bool VulkanRenderer::Impl::CreateFramebuffers()
+ auto VulkanRenderer::Impl::create_framebuffers() -> bool
{
- framebuffers.resize(imageViews.size());
- for (size_t i = 0; i < imageViews.size(); ++i)
+ framebuffers.resize(image_views.size());
+ for (size_t i = 0; i < image_views.size(); ++i)
{
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = renderPass;
- fbci.attachmentCount = 1; fbci.pAttachments = &imageViews[i];
- fbci.width = swapchainExtent.width; fbci.height = swapchainExtent.height; fbci.layers = 1;
+ fbci.renderPass = render_pass;
+ fbci.attachmentCount = 1; fbci.pAttachments = &image_views[i];
+ fbci.width = swapchain_extent.width; fbci.height = swapchain_extent.height; fbci.layers = 1;
VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &framebuffers[i]));
}
return true;
}
- bool VulkanRenderer::Impl::CreateCommandBuffers()
+ auto VulkanRenderer::Impl::create_command_buffers() -> bool
{
VkCommandPoolCreateInfo pci{ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
pci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
- pci.queueFamilyIndex = graphicsFamily;
- VK_CHECK(vkCreateCommandPool(device, &pci, nullptr, &commandPool));
+ pci.queueFamilyIndex = graphics_family;
+ VK_CHECK(vkCreateCommandPool(device, &pci, nullptr, &command_pool));
- commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
+ command_buffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool;
+ cbai.commandPool = command_pool;
cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cbai.commandBufferCount = MAX_FRAMES_IN_FLIGHT;
- VK_CHECK(vkAllocateCommandBuffers(device, &cbai, commandBuffers.data()));
+ VK_CHECK(vkAllocateCommandBuffers(device, &cbai, command_buffers.data()));
return true;
}
- bool VulkanRenderer::Impl::CreateSyncObjects()
+ auto VulkanRenderer::Impl::create_sync_objects() -> bool
{
- imageAvailable.resize(MAX_FRAMES_IN_FLIGHT);
- inFlight.resize(MAX_FRAMES_IN_FLIGHT);
- renderFinished.resize(images.size());
- imagesInFlight.assign(images.size(), VK_NULL_HANDLE);
+ image_available.resize(MAX_FRAMES_IN_FLIGHT);
+ in_flight.resize(MAX_FRAMES_IN_FLIGHT);
+ render_finished.resize(images.size());
+ images_in_flight.assign(images.size(), VK_NULL_HANDLE);
VkSemaphoreCreateInfo sci{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
VkFenceCreateInfo fci{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{
- VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &imageAvailable[i]));
- VK_CHECK(vkCreateFence(device, &fci, nullptr, &inFlight[i]));
+ VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &image_available[i]));
+ VK_CHECK(vkCreateFence(device, &fci, nullptr, &in_flight[i]));
}
for (size_t i = 0; i < images.size(); ++i)
- VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &renderFinished[i]));
+ VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &render_finished[i]));
return true;
}
- uint32_t VulkanRenderer::Impl::FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags) const
+ auto VulkanRenderer::Impl::find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags flags) const -> uint32_t
{
- for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
- if ((typeFilter & (1u << i)) && (memProps.memoryTypes[i].propertyFlags & flags) == flags)
+ for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i)
+ if ((type_filter & (1u << i)) && (mem_props.memoryTypes[i].propertyFlags & flags) == flags)
return i;
return UINT32_MAX;
}
- bool VulkanRenderer::Impl::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
- VkBuffer& buf, VkDeviceMemory& mem) const
+ auto VulkanRenderer::Impl::create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props,
+ VkBuffer& buf, VkDeviceMemory& mem) const -> bool
{
VkBufferCreateInfo bci{ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bci.size = size; bci.usage = usage; bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -432,13 +432,13 @@ namespace Donut
VkMemoryRequirements req{}; vkGetBufferMemoryRequirements(device, buf, &req);
VkMemoryAllocateInfo ai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
ai.allocationSize = req.size;
- ai.memoryTypeIndex = FindMemoryType(req.memoryTypeBits, props);
+ ai.memoryTypeIndex = find_memory_type(req.memoryTypeBits, props);
if (vkAllocateMemory(device, &ai, nullptr, &mem) != VK_SUCCESS) return false;
vkBindBufferMemory(device, buf, mem, 0);
return true;
}
- std::vector<uint32_t> VulkanRenderer::Impl::LoadSpirv(const std::string& path)
+ auto VulkanRenderer::Impl::load_spirv(const std::string& path) -> std::vector<uint32_t>
{
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) return {};
@@ -449,19 +449,19 @@ namespace Donut
return data;
}
- bool VulkanRenderer::Impl::CreateShaderModule(const std::string& path, VkShaderModule& out) const
+ auto VulkanRenderer::Impl::create_shader_module(const std::string& path, VkShaderModule& out) const -> bool
{
- auto spv = LoadSpirv(path);
+ auto spv = load_spirv(path);
if (spv.empty()) { DONUT_ERROR("Vulkan: failed to load SPIR-V {}", path); return false; }
VkShaderModuleCreateInfo ci{ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
ci.codeSize = spv.size() * 4; ci.pCode = spv.data();
return vkCreateShaderModule(device, &ci, nullptr, &out) == VK_SUCCESS;
}
- bool VulkanRenderer::Impl::CreateGeodesicResources()
+ auto VulkanRenderer::Impl::create_geodesic_resources() -> bool
{
const VkFormat fmt = VK_FORMAT_R8G8B8A8_UNORM;
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ const VkMemoryPropertyFlags host_vis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
const float SagA_rs = 1.269e10f;
VkImageCreateInfo ici{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
@@ -469,17 +469,17 @@ namespace Donut
ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
- VK_CHECK(vkCreateImage(device, &ici, nullptr, &geoImage));
- VkMemoryRequirements imReq{}; vkGetImageMemoryRequirements(device, geoImage, &imReq);
- VkMemoryAllocateInfo imAlloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- imAlloc.allocationSize = imReq.size;
- imAlloc.memoryTypeIndex = FindMemoryType(imReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &imAlloc, nullptr, &geoImageMem));
- VK_CHECK(vkBindImageMemory(device, geoImage, geoImageMem, 0));
+ VK_CHECK(vkCreateImage(device, &ici, nullptr, &geo_image));
+ VkMemoryRequirements im_req{}; vkGetImageMemoryRequirements(device, geo_image, &im_req);
+ VkMemoryAllocateInfo im_alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
+ im_alloc.allocationSize = im_req.size;
+ im_alloc.memoryTypeIndex = find_memory_type(im_req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(device, &im_alloc, nullptr, &geo_image_mem));
+ VK_CHECK(vkBindImageMemory(device, geo_image, geo_image_mem, 0));
VkImageViewCreateInfo vci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- vci.image = geoImage; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
+ vci.image = geo_image; vci.viewType = VK_IMAGE_VIEW_TYPE_2D; vci.format = fmt;
vci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &vci, nullptr, &geoImageView));
+ VK_CHECK(vkCreateImageView(device, &vci, nullptr, &geo_image_view));
VkAttachmentDescription color{};
color.format = fmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
@@ -500,75 +500,75 @@ namespace Donut
rpci.attachmentCount = 1; rpci.pAttachments = &color;
rpci.subpassCount = 1; rpci.pSubpasses = &subpass;
rpci.dependencyCount = 2; rpci.pDependencies = deps;
- VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &geoRenderPass));
+ VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &geo_render_pass));
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = geoRenderPass; fbci.attachmentCount = 1; fbci.pAttachments = &geoImageView;
+ fbci.renderPass = geo_render_pass; fbci.attachmentCount = 1; fbci.pAttachments = &geo_image_view;
fbci.width = GEO_W; fbci.height = GEO_H; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &geoFramebuffer));
+ VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &geo_framebuffer));
- CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, camBuf, camMem);
- CreateBuffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, diskBuf, diskMem);
- CreateBuffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, objBuf, objMem);
- CreateBuffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, simBuf, simMem);
+ create_buffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, cam_buf, cam_mem);
+ create_buffer(32, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, disk_buf, disk_mem);
+ create_buffer(800, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, obj_buf, obj_mem);
+ create_buffer(16, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, sim_buf, sim_mem);
- camera.SetCameraMode(CameraMode::Orbital);
- camera.SetOrbitalTarget(glm::vec3(0.0f));
- camera.SetOrbitalRadius(1e11);
- camera.SetOrbitalLimits(4e10, 3e11);
- camera.SetOrbitalSpeed(0.01f);
- camera.SetZoomSpeed(1e10);
- camera.SetAzimuth(0.0f);
- camera.SetElevation(1.25f);
+ camera.set_camera_mode(CameraMode::Orbital);
+ camera.set_orbital_target(glm::vec3(0.0f));
+ camera.set_orbital_radius(1e11);
+ camera.set_orbital_limits(4e10, 3e11);
+ camera.set_orbital_speed(0.01f);
+ camera.set_zoom_speed(1e10);
+ camera.set_azimuth(0.0f);
+ camera.set_elevation(1.25f);
void* p = nullptr;
- vkMapMemory(device, camMem, 0, 128, 0, &camMapped); // camera UBO is refilled every frame
+ vkMapMemory(device, cam_mem, 0, 128, 0, &cam_mapped); // camera UBO is refilled every frame
- float diskData[8] = { SagA_rs * 2.2f, SagA_rs * 5.2f, 2.0f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 };
- vkMapMemory(device, diskMem, 0, 32, 0, &p); memcpy(p, diskData, sizeof(diskData)); vkUnmapMemory(device, diskMem);
+ float disk_data[8] = { SagA_rs * 2.2f, SagA_rs * 5.2f, 2.0f, SagA_rs * 0.1f, 0.1f, 0, 0, 0 };
+ vkMapMemory(device, disk_mem, 0, 32, 0, &p); memcpy(p, disk_data, sizeof(disk_data)); vkUnmapMemory(device, disk_mem);
- std::vector<uint8_t> objData(800, 0);
- int numObjects = 1; memcpy(objData.data(), &numObjects, 4);
- float posRadius[4] = { 0, 0, 0, SagA_rs }; memcpy(objData.data() + 16, posRadius, 16);
- float objColor[4] = { 0, 0, 0, 1 }; memcpy(objData.data() + 272, objColor, 16);
- vkMapMemory(device, objMem, 0, 800, 0, &p); memcpy(p, objData.data(), 800); vkUnmapMemory(device, objMem);
+ std::vector<uint8_t> obj_data(800, 0);
+ int num_objects = 1; memcpy(obj_data.data(), &num_objects, 4);
+ float pos_radius[4] = { 0, 0, 0, SagA_rs }; memcpy(obj_data.data() + 16, pos_radius, 16);
+ float obj_color[4] = { 0, 0, 0, 1 }; memcpy(obj_data.data() + 272, obj_color, 16);
+ vkMapMemory(device, obj_mem, 0, 800, 0, &p); memcpy(p, obj_data.data(), 800); vkUnmapMemory(device, obj_mem);
- vkMapMemory(device, simMem, 0, 16, 0, &simMapped);
+ vkMapMemory(device, sim_mem, 0, 16, 0, &sim_mapped);
- if (!CreateHDRICubemap("Assets/HDRI/HDR_blue_nebulae-1.hdr")) return false;
+ if (!create_hdri_cubemap("assets/hdri/HDR_blue_nebulae-1.hdr")) return false;
VkDescriptorSetLayoutBinding binds[5]{};
for (int i = 0; i < 4; ++i) { binds[i].binding = i; binds[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[i].descriptorCount = 1; binds[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; }
binds[4].binding = 4; binds[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[4].descriptorCount = 1; binds[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
dslci.bindingCount = 5; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &geoSetLayout));
+ VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &geo_set_layout));
VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 } };
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
dpci.maxSets = 1; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
- VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &geoPool));
+ VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &geo_pool));
VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = geoPool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &geoSetLayout;
- VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &geoSet));
- VkDescriptorBufferInfo bi[4] = { { camBuf, 0, VK_WHOLE_SIZE }, { diskBuf, 0, VK_WHOLE_SIZE }, { objBuf, 0, VK_WHOLE_SIZE }, { simBuf, 0, VK_WHOLE_SIZE } };
- VkDescriptorImageInfo cubeInfo{ cubeSampler, cubeView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
+ dsai.descriptorPool = geo_pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &geo_set_layout;
+ VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &geo_set));
+ VkDescriptorBufferInfo bi[4] = { { cam_buf, 0, VK_WHOLE_SIZE }, { disk_buf, 0, VK_WHOLE_SIZE }, { obj_buf, 0, VK_WHOLE_SIZE }, { sim_buf, 0, VK_WHOLE_SIZE } };
+ VkDescriptorImageInfo cube_info{ cube_sampler, cube_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
VkWriteDescriptorSet writes[5]{};
- for (int i = 0; i < 4; ++i) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].dstSet = geoSet; writes[i].dstBinding = i; writes[i].descriptorCount = 1; writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writes[i].pBufferInfo = &bi[i]; }
- writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[4].dstSet = geoSet; writes[4].dstBinding = 4; writes[4].descriptorCount = 1; writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[4].pImageInfo = &cubeInfo;
+ for (int i = 0; i < 4; ++i) { writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[i].dstSet = geo_set; writes[i].dstBinding = i; writes[i].descriptorCount = 1; writes[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writes[i].pBufferInfo = &bi[i]; }
+ writes[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[4].dstSet = geo_set; writes[4].dstBinding = 4; writes[4].descriptorCount = 1; writes[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[4].pImageInfo = &cube_info;
vkUpdateDescriptorSets(device, 5, writes, 0, nullptr);
float quad[] = {
-1.f, 1.f, 0.f, 1.f, -1.f, -1.f, 0.f, 0.f, 1.f, -1.f, 1.f, 0.f,
-1.f, 1.f, 0.f, 1.f, 1.f, -1.f, 1.f, 0.f, 1.f, 1.f, 1.f, 1.f,
};
- CreateBuffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, quadVB, quadVBMem);
- vkMapMemory(device, quadVBMem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(device, quadVBMem);
+ create_buffer(sizeof(quad), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, quad_vb, quad_vb_mem);
+ vkMapMemory(device, quad_vb_mem, 0, sizeof(quad), 0, &p); memcpy(p, quad, sizeof(quad)); vkUnmapMemory(device, quad_vb_mem);
VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/Geodesic.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/Geodesic.fragmentMain.spv", fmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/Geodesic.vertexMain.spv", vmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/Geodesic.fragmentMain.spv", fmod)) return false;
VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &geoSetLayout;
- VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &geoPipelineLayout));
+ plci.setLayoutCount = 1; plci.pSetLayouts = &geo_set_layout;
+ VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &geo_pipeline_layout));
VkPipelineShaderStageCreateInfo stages[2]{};
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
@@ -588,43 +588,43 @@ namespace Donut
gpci.stageCount = 2; gpci.pStages = stages;
gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = geoPipelineLayout; gpci.renderPass = geoRenderPass; gpci.subpass = 0;
- VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &geoPipeline);
+ gpci.layout = geo_pipeline_layout; gpci.renderPass = geo_render_pass; gpci.subpass = 0;
+ VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &geo_pipeline);
vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: geodesic pipeline creation failed ({})", (int)pr); return false; }
- startTime = glfwGetTime();
- UpdateGeodesicUniforms();
+ start_time = glfwGetTime();
+ update_geodesic_uniforms();
DONUT_INFO("Vulkan: geodesic resources ready ({}x{} offscreen)", (int)GEO_W, (int)GEO_H);
return true;
}
- bool VulkanRenderer::Impl::CreateHDRICubemap(const char* path)
+ auto VulkanRenderer::Impl::create_hdri_cubemap(const char* path) -> bool
{
- const VkMemoryPropertyFlags hostVis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ const VkMemoryPropertyFlags host_vis = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
const uint32_t FACE = 1024;
- const VkFormat cubeFmt = VK_FORMAT_R16G16B16A16_SFLOAT;
+ const VkFormat cube_fmt = VK_FORMAT_R16G16B16A16_SFLOAT;
VkImageCreateInfo cci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
cci.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- cci.imageType = VK_IMAGE_TYPE_2D; cci.format = cubeFmt; cci.extent = { FACE, FACE, 1 };
+ cci.imageType = VK_IMAGE_TYPE_2D; cci.format = cube_fmt; cci.extent = { FACE, FACE, 1 };
cci.mipLevels = 1; cci.arrayLayers = 6; cci.samples = VK_SAMPLE_COUNT_1_BIT;
cci.tiling = VK_IMAGE_TILING_OPTIMAL;
cci.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- VK_CHECK(vkCreateImage(device, &cci, nullptr, &cubeImage));
- VkMemoryRequirements creq{}; vkGetImageMemoryRequirements(device, cubeImage, &creq);
+ VK_CHECK(vkCreateImage(device, &cci, nullptr, &cube_image));
+ VkMemoryRequirements creq{}; vkGetImageMemoryRequirements(device, cube_image, &creq);
VkMemoryAllocateInfo cai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- cai.allocationSize = creq.size; cai.memoryTypeIndex = FindMemoryType(creq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &cai, nullptr, &cubeMem));
- VK_CHECK(vkBindImageMemory(device, cubeImage, cubeMem, 0));
+ cai.allocationSize = creq.size; cai.memoryTypeIndex = find_memory_type(creq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(device, &cai, nullptr, &cube_mem));
+ VK_CHECK(vkBindImageMemory(device, cube_image, cube_mem, 0));
VkImageViewCreateInfo cvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- cvci.image = cubeImage; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = cubeFmt;
+ cvci.image = cube_image; cvci.viewType = VK_IMAGE_VIEW_TYPE_CUBE; cvci.format = cube_fmt;
cvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- VK_CHECK(vkCreateImageView(device, &cvci, nullptr, &cubeView));
+ VK_CHECK(vkCreateImageView(device, &cvci, nullptr, &cube_view));
VkSamplerCreateInfo csm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
csm.magFilter = VK_FILTER_LINEAR; csm.minFilter = VK_FILTER_LINEAR;
csm.addressModeU = csm.addressModeV = csm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &csm, nullptr, &cubeSampler));
+ VK_CHECK(vkCreateSampler(device, &csm, nullptr, &cube_sampler));
int w = 0, h = 0, ch = 0;
float* pixels = stbi_loadf(path, &w, &h, &ch, 4);
@@ -632,76 +632,76 @@ namespace Donut
{
DONUT_WARN("Vulkan: HDRI '{}' could not be loaded; using a dark background", path);
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
+ cbai.commandPool = command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
VkCommandBuffer cmd; VK_CHECK(vkAllocateCommandBuffers(device, &cbai, &cmd));
VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(cmd, &bi);
VkImageMemoryBarrier tb{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
tb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; tb.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- tb.image = cubeImage; tb.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
+ tb.image = cube_image; tb.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
tb.srcAccessMask = 0; tb.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &tb);
VkClearColorValue dark{}; dark.float32[0] = 0.02f; dark.float32[1] = 0.02f; dark.float32[2] = 0.05f; dark.float32[3] = 1.0f;
VkImageSubresourceRange rng{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 6 };
- vkCmdClearColorImage(cmd, cubeImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dark, 1, &rng);
+ vkCmdClearColorImage(cmd, cube_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dark, 1, &rng);
VkImageMemoryBarrier rb = tb; rb.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; rb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
rb.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; rb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &rb);
vkEndCommandBuffer(cmd);
VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd;
- vkQueueSubmit(graphicsQueue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(graphicsQueue);
- vkFreeCommandBuffers(device, commandPool, 1, &cmd);
+ vkQueueSubmit(graphics_queue, 1, &si, VK_NULL_HANDLE); vkQueueWaitIdle(graphics_queue);
+ vkFreeCommandBuffers(device, command_pool, 1, &cmd);
return true;
}
// Apple GPUs can't linearly filter RGBA32F, so store the equirect as
// RGBA16F (convert the loaded floats to half on the way into staging).
- const VkFormat eqFmt = VK_FORMAT_R16G16B16A16_SFLOAT;
- size_t texelCount = (size_t)w * h * 4;
- VkDeviceSize eqSize = (VkDeviceSize)texelCount * sizeof(uint16_t);
- VkBuffer eqStaging; VkDeviceMemory eqStagingMem;
- if (!CreateBuffer(eqSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, hostVis, eqStaging, eqStagingMem)) { stbi_image_free(pixels); return false; }
- void* mp = nullptr; vkMapMemory(device, eqStagingMem, 0, eqSize, 0, &mp);
+ const VkFormat eq_fmt = VK_FORMAT_R16G16B16A16_SFLOAT;
+ size_t texel_count = (size_t)w * h * 4;
+ VkDeviceSize eq_size = (VkDeviceSize)texel_count * sizeof(uint16_t);
+ VkBuffer eq_staging; VkDeviceMemory eq_staging_mem;
+ if (!create_buffer(eq_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_vis, eq_staging, eq_staging_mem)) { stbi_image_free(pixels); return false; }
+ void* mp = nullptr; vkMapMemory(device, eq_staging_mem, 0, eq_size, 0, &mp);
uint16_t* dst = (uint16_t*)mp;
- for (size_t i = 0; i < texelCount; ++i) { __fp16 hf = (__fp16)pixels[i]; memcpy(&dst[i], &hf, sizeof(uint16_t)); }
- vkUnmapMemory(device, eqStagingMem);
+ for (size_t i = 0; i < texel_count; ++i) { __fp16 hf = (__fp16)pixels[i]; memcpy(&dst[i], &hf, sizeof(uint16_t)); }
+ vkUnmapMemory(device, eq_staging_mem);
stbi_image_free(pixels);
- VkImage eqImage; VkDeviceMemory eqMem;
+ VkImage eq_image; VkDeviceMemory eq_mem;
VkImageCreateInfo eci{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
- eci.imageType = VK_IMAGE_TYPE_2D; eci.format = eqFmt; eci.extent = { (uint32_t)w, (uint32_t)h, 1 };
+ eci.imageType = VK_IMAGE_TYPE_2D; eci.format = eq_fmt; eci.extent = { (uint32_t)w, (uint32_t)h, 1 };
eci.mipLevels = 1; eci.arrayLayers = 1; eci.samples = VK_SAMPLE_COUNT_1_BIT;
eci.tiling = VK_IMAGE_TILING_OPTIMAL; eci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
- VK_CHECK(vkCreateImage(device, &eci, nullptr, &eqImage));
- VkMemoryRequirements ereq{}; vkGetImageMemoryRequirements(device, eqImage, &ereq);
+ VK_CHECK(vkCreateImage(device, &eci, nullptr, &eq_image));
+ VkMemoryRequirements ereq{}; vkGetImageMemoryRequirements(device, eq_image, &ereq);
VkMemoryAllocateInfo eai{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
- eai.allocationSize = ereq.size; eai.memoryTypeIndex = FindMemoryType(ereq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- VK_CHECK(vkAllocateMemory(device, &eai, nullptr, &eqMem));
- VK_CHECK(vkBindImageMemory(device, eqImage, eqMem, 0));
- VkImageView eqView;
+ eai.allocationSize = ereq.size; eai.memoryTypeIndex = find_memory_type(ereq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK(vkAllocateMemory(device, &eai, nullptr, &eq_mem));
+ VK_CHECK(vkBindImageMemory(device, eq_image, eq_mem, 0));
+ VkImageView eq_view;
VkImageViewCreateInfo evci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- evci.image = eqImage; evci.viewType = VK_IMAGE_VIEW_TYPE_2D; evci.format = eqFmt;
+ evci.image = eq_image; evci.viewType = VK_IMAGE_VIEW_TYPE_2D; evci.format = eq_fmt;
evci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- VK_CHECK(vkCreateImageView(device, &evci, nullptr, &eqView));
- VkSampler eqSampler;
+ VK_CHECK(vkCreateImageView(device, &evci, nullptr, &eq_view));
+ VkSampler eq_sampler;
VkSamplerCreateInfo esm{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
esm.magFilter = VK_FILTER_LINEAR; esm.minFilter = VK_FILTER_LINEAR;
esm.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // longitude wraps
esm.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; // latitude clamps
esm.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &esm, nullptr, &eqSampler));
+ VK_CHECK(vkCreateSampler(device, &esm, nullptr, &eq_sampler));
- VkImageView faceViews[6];
+ VkImageView face_views[6];
for (uint32_t i = 0; i < 6; ++i)
{
VkImageViewCreateInfo fvci{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
- fvci.image = cubeImage; fvci.viewType = VK_IMAGE_VIEW_TYPE_2D; fvci.format = cubeFmt;
+ fvci.image = cube_image; fvci.viewType = VK_IMAGE_VIEW_TYPE_2D; fvci.format = cube_fmt;
fvci.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, i, 1 };
- VK_CHECK(vkCreateImageView(device, &fvci, nullptr, &faceViews[i]));
+ VK_CHECK(vkCreateImageView(device, &fvci, nullptr, &face_views[i]));
}
VkAttachmentDescription color{};
- color.format = cubeFmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
+ color.format = cube_fmt; color.samples = VK_SAMPLE_COUNT_1_BIT;
color.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; color.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@@ -714,30 +714,30 @@ namespace Donut
VkRenderPassCreateInfo rpci{ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
rpci.attachmentCount = 1; rpci.pAttachments = &color; rpci.subpassCount = 1; rpci.pSubpasses = &subpass; rpci.dependencyCount = 1; rpci.pDependencies = &dep;
VK_CHECK(vkCreateRenderPass(device, &rpci, nullptr, &rp));
- VkFramebuffer faceFB[6];
+ VkFramebuffer face_fb[6];
for (uint32_t i = 0; i < 6; ++i)
{
VkFramebufferCreateInfo fbci{ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
- fbci.renderPass = rp; fbci.attachmentCount = 1; fbci.pAttachments = &faceViews[i]; fbci.width = FACE; fbci.height = FACE; fbci.layers = 1;
- VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &faceFB[i]));
+ fbci.renderPass = rp; fbci.attachmentCount = 1; fbci.pAttachments = &face_views[i]; fbci.width = FACE; fbci.height = FACE; fbci.layers = 1;
+ VK_CHECK(vkCreateFramebuffer(device, &fbci, nullptr, &face_fb[i]));
}
VkDescriptorSetLayoutBinding binds[2]{};
binds[0].binding = 0; binds[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; binds[0].descriptorCount = 1; binds[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
binds[1].binding = 1; binds[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; binds[1].descriptorCount = 1; binds[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
- VkDescriptorSetLayout setLayout;
+ VkDescriptorSetLayout set_layout;
VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; dslci.bindingCount = 2; dslci.pBindings = binds;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &setLayout));
+ VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &set_layout));
VkDescriptorPoolSize psizes[2] = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 6 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6 } };
VkDescriptorPool pool;
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; dpci.maxSets = 6; dpci.poolSizeCount = 2; dpci.pPoolSizes = psizes;
VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &pool));
VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/EquirectToCubemap.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/EquirectToCubemap.fragmentMain.spv", fmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/EquirectToCubemap.vertexMain.spv", vmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/EquirectToCubemap.fragmentMain.spv", fmod)) return false;
VkPipelineLayout playout;
- VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &setLayout;
+ VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; plci.setLayoutCount = 1; plci.pSetLayouts = &set_layout;
VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &playout));
VkPipelineShaderStageCreateInfo stages[2]{};
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
@@ -761,7 +761,7 @@ namespace Donut
vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: equirect pipeline failed ({})", (int)pr); return false; }
- float cubeVerts[] = {
+ float cube_verts[] = {
-1,1,-1, -1,-1,-1, 1,-1,-1, 1,-1,-1, 1,1,-1, -1,1,-1,
-1,-1,1, -1,-1,-1, -1,1,-1, -1,1,-1, -1,1,1, -1,-1,1,
1,-1,-1, 1,-1,1, 1,1,1, 1,1,1, 1,1,-1, 1,-1,-1,
@@ -769,9 +769,9 @@ namespace Donut
-1,1,-1, 1,1,-1, 1,1,1, 1,1,1, -1,1,1, -1,1,-1,
-1,-1,-1, -1,-1,1, 1,-1,-1, 1,-1,-1, -1,-1,1, 1,-1,1,
};
- VkBuffer cubeVB; VkDeviceMemory cubeVBMem;
- CreateBuffer(sizeof(cubeVerts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, hostVis, cubeVB, cubeVBMem);
- vkMapMemory(device, cubeVBMem, 0, sizeof(cubeVerts), 0, &mp); memcpy(mp, cubeVerts, sizeof(cubeVerts)); vkUnmapMemory(device, cubeVBMem);
+ VkBuffer cube_vb; VkDeviceMemory cube_vb_mem;
+ create_buffer(sizeof(cube_verts), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, host_vis, cube_vb, cube_vb_mem);
+ vkMapMemory(device, cube_vb_mem, 0, sizeof(cube_verts), 0, &mp); memcpy(mp, cube_verts, sizeof(cube_verts)); vkUnmapMemory(device, cube_vb_mem);
glm::mat4 proj = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);
proj[1][1] *= -1.0f; // Vulkan clip space is Y-down vs OpenGL
@@ -783,97 +783,97 @@ namespace Donut
glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, 1), glm::vec3(0, -1, 0)),
glm::lookAt(glm::vec3(0), glm::vec3( 0, 0, -1), glm::vec3(0, -1, 0)),
};
- VkBuffer ubo[6]; VkDeviceMemory uboMem[6]; VkDescriptorSet sets[6];
+ VkBuffer ubo[6]; VkDeviceMemory ubo_mem[6]; VkDescriptorSet sets[6];
for (uint32_t i = 0; i < 6; ++i)
{
- CreateBuffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, hostVis, ubo[i], uboMem[i]);
+ create_buffer(128, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, host_vis, ubo[i], ubo_mem[i]);
glm::mat4 mats[2] = { glm::transpose(proj), glm::transpose(views[i]) }; // SPIR-V expects row-major
- vkMapMemory(device, uboMem[i], 0, 128, 0, &mp); memcpy(mp, mats, 128); vkUnmapMemory(device, uboMem[i]);
- VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &setLayout;
+ vkMapMemory(device, ubo_mem[i], 0, 128, 0, &mp); memcpy(mp, mats, 128); vkUnmapMemory(device, ubo_mem[i]);
+ VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; dsai.descriptorPool = pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &set_layout;
VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &sets[i]));
- VkDescriptorBufferInfo bufInfo{ ubo[i], 0, VK_WHOLE_SIZE };
- VkDescriptorImageInfo imgInfo{ eqSampler, eqView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
+ VkDescriptorBufferInfo buf_info{ ubo[i], 0, VK_WHOLE_SIZE };
+ VkDescriptorImageInfo img_info{ eq_sampler, eq_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
VkWriteDescriptorSet ws[2]{};
- ws[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[0].dstSet = sets[i]; ws[0].dstBinding = 0; ws[0].descriptorCount = 1; ws[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ws[0].pBufferInfo = &bufInfo;
- ws[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[1].dstSet = sets[i]; ws[1].dstBinding = 1; ws[1].descriptorCount = 1; ws[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; ws[1].pImageInfo = &imgInfo;
+ ws[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[0].dstSet = sets[i]; ws[0].dstBinding = 0; ws[0].descriptorCount = 1; ws[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; ws[0].pBufferInfo = &buf_info;
+ ws[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; ws[1].dstSet = sets[i]; ws[1].dstBinding = 1; ws[1].descriptorCount = 1; ws[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; ws[1].pImageInfo = &img_info;
vkUpdateDescriptorSets(device, 2, ws, 0, nullptr);
}
VkCommandBufferAllocateInfo cbai{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
- cbai.commandPool = commandPool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
+ cbai.commandPool = command_pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1;
VkCommandBuffer cmd; VK_CHECK(vkAllocateCommandBuffers(device, &cbai, &cmd));
VkCommandBufferBeginInfo bi{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_CHECK(vkBeginCommandBuffer(cmd, &bi));
- VkImageMemoryBarrier toDst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
- toDst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; toDst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
- toDst.image = eqImage; toDst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
- toDst.srcAccessMask = 0; toDst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toDst);
+ VkImageMemoryBarrier to_dst{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
+ to_dst.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; to_dst.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ to_dst.image = eq_image; to_dst.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ to_dst.srcAccessMask = 0; to_dst.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_dst);
VkBufferImageCopy copy{}; copy.imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }; copy.imageExtent = { (uint32_t)w, (uint32_t)h, 1 };
- vkCmdCopyBufferToImage(cmd, eqStaging, eqImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
- VkImageMemoryBarrier toRead = toDst; toRead.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; toRead.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- toRead.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; toRead.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
- vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &toRead);
+ vkCmdCopyBufferToImage(cmd, eq_staging, eq_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy);
+ VkImageMemoryBarrier to_read = to_dst; to_read.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; to_read.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ to_read.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; to_read.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+ vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &to_read);
VkClearValue clear{}; clear.color = { { 0, 0, 0, 1 } };
for (uint32_t i = 0; i < 6; ++i)
{
VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = rp; rpbi.framebuffer = faceFB[i]; rpbi.renderArea = { { 0, 0 }, { FACE, FACE } }; rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
+ rpbi.renderPass = rp; rpbi.framebuffer = face_fb[i]; rpbi.renderArea = { { 0, 0 }, { FACE, FACE } }; rpbi.clearValueCount = 1; rpbi.pClearValues = &clear;
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, playout, 0, 1, &sets[i], 0, nullptr);
- VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &cubeVB, &off);
+ VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &cube_vb, &off);
vkCmdDraw(cmd, 36, 1, 0, 0);
vkCmdEndRenderPass(cmd);
}
VK_CHECK(vkEndCommandBuffer(cmd));
VkSubmitInfo si{ VK_STRUCTURE_TYPE_SUBMIT_INFO }; si.commandBufferCount = 1; si.pCommandBuffers = &cmd;
- VK_CHECK(vkQueueSubmit(graphicsQueue, 1, &si, VK_NULL_HANDLE));
- VK_CHECK(vkQueueWaitIdle(graphicsQueue));
+ VK_CHECK(vkQueueSubmit(graphics_queue, 1, &si, VK_NULL_HANDLE));
+ VK_CHECK(vkQueueWaitIdle(graphics_queue));
- vkFreeCommandBuffers(device, commandPool, 1, &cmd);
- for (uint32_t i = 0; i < 6; ++i) { vkDestroyBuffer(device, ubo[i], nullptr); vkFreeMemory(device, uboMem[i], nullptr); vkDestroyFramebuffer(device, faceFB[i], nullptr); vkDestroyImageView(device, faceViews[i], nullptr); }
- vkDestroyBuffer(device, cubeVB, nullptr); vkFreeMemory(device, cubeVBMem, nullptr);
+ vkFreeCommandBuffers(device, command_pool, 1, &cmd);
+ for (uint32_t i = 0; i < 6; ++i) { vkDestroyBuffer(device, ubo[i], nullptr); vkFreeMemory(device, ubo_mem[i], nullptr); vkDestroyFramebuffer(device, face_fb[i], nullptr); vkDestroyImageView(device, face_views[i], nullptr); }
+ vkDestroyBuffer(device, cube_vb, nullptr); vkFreeMemory(device, cube_vb_mem, nullptr);
vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, playout, nullptr);
- vkDestroyDescriptorPool(device, pool, nullptr); vkDestroyDescriptorSetLayout(device, setLayout, nullptr);
+ vkDestroyDescriptorPool(device, pool, nullptr); vkDestroyDescriptorSetLayout(device, set_layout, nullptr);
vkDestroyRenderPass(device, rp, nullptr);
- vkDestroySampler(device, eqSampler, nullptr); vkDestroyImageView(device, eqView, nullptr);
- vkDestroyImage(device, eqImage, nullptr); vkFreeMemory(device, eqMem, nullptr);
- vkDestroyBuffer(device, eqStaging, nullptr); vkFreeMemory(device, eqStagingMem, nullptr);
+ vkDestroySampler(device, eq_sampler, nullptr); vkDestroyImageView(device, eq_view, nullptr);
+ vkDestroyImage(device, eq_image, nullptr); vkFreeMemory(device, eq_mem, nullptr);
+ vkDestroyBuffer(device, eq_staging, nullptr); vkFreeMemory(device, eq_staging_mem, nullptr);
DONUT_INFO("Vulkan: HDRI cubemap built from {} ({}x{} equirect -> {}^2 cube)", path, w, h, (int)FACE);
return true;
}
- bool VulkanRenderer::Impl::CreatePresentResources()
+ auto VulkanRenderer::Impl::create_present_resources() -> bool
{
VkSamplerCreateInfo smci{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
smci.magFilter = VK_FILTER_LINEAR; smci.minFilter = VK_FILTER_LINEAR;
smci.addressModeU = smci.addressModeV = smci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
- VK_CHECK(vkCreateSampler(device, &smci, nullptr, &presentSampler));
+ VK_CHECK(vkCreateSampler(device, &smci, nullptr, &present_sampler));
VkDescriptorSetLayoutBinding bind{}; bind.binding = 0; bind.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; bind.descriptorCount = 1; bind.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo dslci{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
dslci.bindingCount = 1; dslci.pBindings = &bind;
- VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &presentSetLayout));
+ VK_CHECK(vkCreateDescriptorSetLayout(device, &dslci, nullptr, &present_set_layout));
VkDescriptorPoolSize psize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 };
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
dpci.maxSets = 1; dpci.poolSizeCount = 1; dpci.pPoolSizes = &psize;
- VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &presentPool));
+ VK_CHECK(vkCreateDescriptorPool(device, &dpci, nullptr, &present_pool));
VkDescriptorSetAllocateInfo dsai{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
- dsai.descriptorPool = presentPool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &presentSetLayout;
- VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &presentSet));
- VkDescriptorImageInfo ii{ presentSampler, geoImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
+ dsai.descriptorPool = present_pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &present_set_layout;
+ VK_CHECK(vkAllocateDescriptorSets(device, &dsai, &present_set));
+ VkDescriptorImageInfo ii{ present_sampler, geo_image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
VkWriteDescriptorSet write{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
- write.dstSet = presentSet; write.dstBinding = 0; write.descriptorCount = 1; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = &ii;
+ write.dstSet = present_set; write.dstBinding = 0; write.descriptorCount = 1; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = &ii;
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
VkShaderModule vmod, fmod;
- if (!CreateShaderModule("Assets/Shaders/generated/TexturedQuad.vertexMain.spv", vmod)) return false;
- if (!CreateShaderModule("Assets/Shaders/generated/TexturedQuad.fragmentMain.spv", fmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/TexturedQuad.vertexMain.spv", vmod)) return false;
+ if (!create_shader_module("assets/shaders/generated/TexturedQuad.fragmentMain.spv", fmod)) return false;
VkPipelineLayoutCreateInfo plci{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
- plci.setLayoutCount = 1; plci.pSetLayouts = &presentSetLayout;
- VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &presentPipelineLayout));
+ plci.setLayoutCount = 1; plci.pSetLayouts = &present_set_layout;
+ VK_CHECK(vkCreatePipelineLayout(device, &plci, nullptr, &present_pipeline_layout));
VkPipelineShaderStageCreateInfo stages[2]{};
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vmod; stages[0].pName = "main";
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fmod; stages[1].pName = "main";
@@ -895,152 +895,152 @@ namespace Donut
gpci.pVertexInputState = &vin; gpci.pInputAssemblyState = &ia; gpci.pViewportState = &vps;
gpci.pDynamicState = &dsci;
gpci.pRasterizationState = &rs; gpci.pMultisampleState = &ms; gpci.pColorBlendState = &cb;
- gpci.layout = presentPipelineLayout; gpci.renderPass = renderPass; gpci.subpass = 0;
- VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &presentPipeline);
+ gpci.layout = present_pipeline_layout; gpci.renderPass = render_pass; gpci.subpass = 0;
+ VkResult pr = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &present_pipeline);
vkDestroyShaderModule(device, vmod, nullptr); vkDestroyShaderModule(device, fmod, nullptr);
if (pr != VK_SUCCESS) { DONUT_ERROR("Vulkan: present pipeline creation failed ({})", (int)pr); return false; }
DONUT_INFO("Vulkan: present pipeline ready");
return true;
}
- void VulkanRenderer::Impl::UpdateGeodesicUniforms()
+ auto VulkanRenderer::Impl::update_geodesic_uniforms() -> void
{
struct CamUBO {
glm::vec3 pos; float p0; glm::vec3 right; float p1;
glm::vec3 up; float p2; glm::vec3 fwd; float p3;
- float tanHalfFov; float aspect; uint32_t moving; int p4;
- } camData{};
- glm::vec3 pos = camera.GetOrbitalPosition();
- glm::vec3 fwd = glm::normalize(camera.GetOrbitalTarget() - pos);
+ float tan_half_fov; float aspect; uint32_t moving; int p4;
+ } cam_data{};
+ glm::vec3 pos = camera.get_orbital_position();
+ glm::vec3 fwd = glm::normalize(camera.get_orbital_target() - pos);
glm::vec3 right = glm::normalize(glm::cross(fwd, glm::vec3(0, 1, 0)));
- camData.pos = pos; camData.right = right; camData.up = glm::cross(right, fwd); camData.fwd = fwd;
- camData.tanHalfFov = (float)tan(glm::radians(60.0f * 0.5f));
- camData.aspect = (float)GEO_W / (float)GEO_H;
- camData.moving = camera.IsDragging() || camera.IsPanning() ? 1u : 0u;
- if (camMapped) memcpy(camMapped, &camData, sizeof(camData));
+ cam_data.pos = pos; cam_data.right = right; cam_data.up = glm::cross(right, fwd); cam_data.fwd = fwd;
+ cam_data.tan_half_fov = (float)tan(glm::radians(60.0f * 0.5f));
+ cam_data.aspect = (float)GEO_W / (float)GEO_H;
+ cam_data.moving = camera.is_dragging() || camera.is_panning() ? 1u : 0u;
+ if (cam_mapped) memcpy(cam_mapped, &cam_data, sizeof(cam_data));
// Fewer integration steps while the camera moves keeps dragging responsive;
// more steps once it settles renders the disk in full.
- struct SimUBO { int stepsMoving; int stepsStatic; float earlyExit; float time; } sim;
- sim.stepsMoving = 3500; sim.stepsStatic = 5000; sim.earlyExit = 5e12f;
- sim.time = (float)(glfwGetTime() - startTime);
- if (simMapped) memcpy(simMapped, &sim, sizeof(sim));
+ struct SimUBO { int steps_moving; int steps_static; float early_exit; float time; } sim;
+ sim.steps_moving = 3500; sim.steps_static = 5000; sim.early_exit = 5e12f;
+ sim.time = (float)(glfwGetTime() - start_time);
+ if (sim_mapped) memcpy(sim_mapped, &sim, sizeof(sim));
}
static double g_ScrollAccum = 0.0;
static GLFWscrollfun g_PrevScroll = nullptr;
- static void DonutVkScrollCallback(GLFWwindow* w, double x, double y)
+ static void donut_vk_scroll_callback(GLFWwindow* w, double x, double y)
{
if (g_PrevScroll) g_PrevScroll(w, x, y); // keep ImGui's scroll handling intact
g_ScrollAccum += y;
}
- void VulkanRenderer::Impl::ProcessInput()
+ auto VulkanRenderer::Impl::process_input() -> void
{
- bool overUI = imguiInit && ImGui::GetIO().WantCaptureMouse;
+ bool over_ui = imgui_init && ImGui::GetIO().WantCaptureMouse;
- bool leftDown = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
- if (leftDown && !leftWasDown && !overUI)
- camera.ProcessOrbitalMouseButton(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);
- else if (!leftDown && leftWasDown)
- camera.ProcessOrbitalMouseButton(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
- leftWasDown = leftDown;
+ bool left_down = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS;
+ if (left_down && !left_was_down && !over_ui)
+ camera.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);
+ else if (!left_down && left_was_down)
+ camera.process_orbital_mouse_button(GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
+ left_was_down = left_down;
double mx = 0, my = 0;
glfwGetCursorPos(window, &mx, &my);
- camera.ProcessOrbitalMouseMove(mx, my); // tracks last position internally; orbits only while dragging
+ camera.process_orbital_mouse_move(mx, my); // tracks last position internally; orbits only while dragging
double scroll = g_ScrollAccum; g_ScrollAccum = 0.0;
- if (scroll != 0.0 && !overUI)
- camera.ProcessOrbitalScroll(0.0, scroll);
+ if (scroll != 0.0 && !over_ui)
+ camera.process_orbital_scroll(0.0, scroll);
}
- void VulkanRenderer::Impl::DestroyGeodesicResources()
+ auto VulkanRenderer::Impl::destroy_geodesic_resources() -> void
{
- if (presentPipeline) vkDestroyPipeline(device, presentPipeline, nullptr);
- if (presentPipelineLayout) vkDestroyPipelineLayout(device, presentPipelineLayout, nullptr);
- if (presentPool) vkDestroyDescriptorPool(device, presentPool, nullptr);
- if (presentSetLayout) vkDestroyDescriptorSetLayout(device, presentSetLayout, nullptr);
- if (presentSampler) vkDestroySampler(device, presentSampler, nullptr);
+ if (present_pipeline) vkDestroyPipeline(device, present_pipeline, nullptr);
+ if (present_pipeline_layout) vkDestroyPipelineLayout(device, present_pipeline_layout, nullptr);
+ if (present_pool) vkDestroyDescriptorPool(device, present_pool, nullptr);
+ if (present_set_layout) vkDestroyDescriptorSetLayout(device, present_set_layout, nullptr);
+ if (present_sampler) vkDestroySampler(device, present_sampler, nullptr);
- if (geoPipeline) vkDestroyPipeline(device, geoPipeline, nullptr);
- if (geoPipelineLayout) vkDestroyPipelineLayout(device, geoPipelineLayout, nullptr);
- if (geoPool) vkDestroyDescriptorPool(device, geoPool, nullptr);
- if (geoSetLayout) vkDestroyDescriptorSetLayout(device, geoSetLayout, nullptr);
- if (quadVB) vkDestroyBuffer(device, quadVB, nullptr);
- if (quadVBMem) vkFreeMemory(device, quadVBMem, nullptr);
- if (cubeSampler) vkDestroySampler(device, cubeSampler, nullptr);
- if (cubeView) vkDestroyImageView(device, cubeView, nullptr);
- if (cubeImage) vkDestroyImage(device, cubeImage, nullptr);
- if (cubeMem) vkFreeMemory(device, cubeMem, nullptr);
- if (camMapped) { vkUnmapMemory(device, camMem); camMapped = nullptr; }
- if (simMapped) { vkUnmapMemory(device, simMem); simMapped = nullptr; }
- VkBuffer ubos[4] = { camBuf, diskBuf, objBuf, simBuf };
- VkDeviceMemory umem[4] = { camMem, diskMem, objMem, simMem };
+ if (geo_pipeline) vkDestroyPipeline(device, geo_pipeline, nullptr);
+ if (geo_pipeline_layout) vkDestroyPipelineLayout(device, geo_pipeline_layout, nullptr);
+ if (geo_pool) vkDestroyDescriptorPool(device, geo_pool, nullptr);
+ if (geo_set_layout) vkDestroyDescriptorSetLayout(device, geo_set_layout, nullptr);
+ if (quad_vb) vkDestroyBuffer(device, quad_vb, nullptr);
+ if (quad_vb_mem) vkFreeMemory(device, quad_vb_mem, nullptr);
+ if (cube_sampler) vkDestroySampler(device, cube_sampler, nullptr);
+ if (cube_view) vkDestroyImageView(device, cube_view, nullptr);
+ if (cube_image) vkDestroyImage(device, cube_image, nullptr);
+ if (cube_mem) vkFreeMemory(device, cube_mem, nullptr);
+ if (cam_mapped) { vkUnmapMemory(device, cam_mem); cam_mapped = nullptr; }
+ if (sim_mapped) { vkUnmapMemory(device, sim_mem); sim_mapped = nullptr; }
+ VkBuffer ubos[4] = { cam_buf, disk_buf, obj_buf, sim_buf };
+ VkDeviceMemory umem[4] = { cam_mem, disk_mem, obj_mem, sim_mem };
for (int i = 0; i < 4; ++i) { if (ubos[i]) vkDestroyBuffer(device, ubos[i], nullptr); if (umem[i]) vkFreeMemory(device, umem[i], nullptr); }
- if (geoFramebuffer) vkDestroyFramebuffer(device, geoFramebuffer, nullptr);
- if (geoRenderPass) vkDestroyRenderPass(device, geoRenderPass, nullptr);
- if (geoImageView) vkDestroyImageView(device, geoImageView, nullptr);
- if (geoImage) vkDestroyImage(device, geoImage, nullptr);
- if (geoImageMem) vkFreeMemory(device, geoImageMem, nullptr);
+ if (geo_framebuffer) vkDestroyFramebuffer(device, geo_framebuffer, nullptr);
+ if (geo_render_pass) vkDestroyRenderPass(device, geo_render_pass, nullptr);
+ if (geo_image_view) vkDestroyImageView(device, geo_image_view, nullptr);
+ if (geo_image) vkDestroyImage(device, geo_image, nullptr);
+ if (geo_image_mem) vkFreeMemory(device, geo_image_mem, nullptr);
}
- bool VulkanRenderer::Impl::RecordCommandBuffer(VkCommandBuffer cmd, uint32_t imageIndex, const glm::vec4& clear, ImDrawData* drawData)
+ auto VulkanRenderer::Impl::record_command_buffer(VkCommandBuffer cmd, uint32_t image_index, const glm::vec4& clear, ImDrawData* draw_data) -> bool
{
VkCommandBufferBeginInfo begin{ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
VK_CHECK(vkBeginCommandBuffer(cmd, &begin));
// Geodesic offscreen pass
- VkClearValue geoClear{}; geoClear.color = { { 0, 0, 0, 1 } };
+ VkClearValue geo_clear{}; geo_clear.color = { { 0, 0, 0, 1 } };
VkRenderPassBeginInfo grp{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- grp.renderPass = geoRenderPass; grp.framebuffer = geoFramebuffer;
+ grp.renderPass = geo_render_pass; grp.framebuffer = geo_framebuffer;
grp.renderArea = { { 0, 0 }, { GEO_W, GEO_H } };
- grp.clearValueCount = 1; grp.pClearValues = &geoClear;
+ grp.clearValueCount = 1; grp.pClearValues = &geo_clear;
vkCmdBeginRenderPass(cmd, &grp, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geoPipeline);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geoPipelineLayout, 0, 1, &geoSet, 0, nullptr);
- VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &quadVB, &off);
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geo_pipeline);
+ vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, geo_pipeline_layout, 0, 1, &geo_set, 0, nullptr);
+ VkDeviceSize off = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &quad_vb, &off);
vkCmdDraw(cmd, 6, 1, 0, 0);
vkCmdEndRenderPass(cmd);
// Swapchain pass: upscale the geodesic image, then the ImGui UI on top
VkClearValue cv{}; cv.color = { { clear.r, clear.g, clear.b, clear.a } };
VkRenderPassBeginInfo rpbi{ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
- rpbi.renderPass = renderPass;
- rpbi.framebuffer = framebuffers[imageIndex];
- rpbi.renderArea = { { 0, 0 }, swapchainExtent };
+ rpbi.renderPass = render_pass;
+ rpbi.framebuffer = framebuffers[image_index];
+ rpbi.renderArea = { { 0, 0 }, swapchain_extent };
rpbi.clearValueCount = 1; rpbi.pClearValues = &cv;
vkCmdBeginRenderPass(cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
- vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, presentPipeline);
+ vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, present_pipeline);
// Negative-height viewport flips the geodesic image vertically so the scene
// reads the same as the OpenGL path (Vulkan's clip space is Y-down). Only
// this draw is affected; ImGui sets its own viewport.
- VkViewport vp{ 0, (float)swapchainExtent.height, (float)swapchainExtent.width, -(float)swapchainExtent.height, 0, 1 };
- VkRect2D scissor{ { 0, 0 }, swapchainExtent };
+ VkViewport vp{ 0, (float)swapchain_extent.height, (float)swapchain_extent.width, -(float)swapchain_extent.height, 0, 1 };
+ VkRect2D scissor{ { 0, 0 }, swapchain_extent };
vkCmdSetViewport(cmd, 0, 1, &vp);
vkCmdSetScissor(cmd, 0, 1, &scissor);
- vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, presentPipelineLayout, 0, 1, &presentSet, 0, nullptr);
- vkCmdBindVertexBuffers(cmd, 0, 1, &quadVB, &off);
+ vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, present_pipeline_layout, 0, 1, &present_set, 0, nullptr);
+ vkCmdBindVertexBuffers(cmd, 0, 1, &quad_vb, &off);
vkCmdDraw(cmd, 6, 1, 0, 0);
- if (drawData)
- ImGui_ImplVulkan_RenderDrawData(drawData, cmd);
+ if (draw_data)
+ ImGui_ImplVulkan_RenderDrawData(draw_data, cmd);
vkCmdEndRenderPass(cmd);
VK_CHECK(vkEndCommandBuffer(cmd));
return true;
}
- void VulkanRenderer::Impl::CleanupSwapchain()
+ auto VulkanRenderer::Impl::cleanup_swapchain() -> void
{
for (auto fb : framebuffers) vkDestroyFramebuffer(device, fb, nullptr);
framebuffers.clear();
- for (auto iv : imageViews) vkDestroyImageView(device, iv, nullptr);
- imageViews.clear();
- if (renderPass) { vkDestroyRenderPass(device, renderPass, nullptr); renderPass = VK_NULL_HANDLE; }
+ for (auto iv : image_views) vkDestroyImageView(device, iv, nullptr);
+ image_views.clear();
+ if (render_pass) { vkDestroyRenderPass(device, render_pass, nullptr); render_pass = VK_NULL_HANDLE; }
if (swapchain) { vkDestroySwapchainKHR(device, swapchain, nullptr); swapchain = VK_NULL_HANDLE; }
}
- bool VulkanRenderer::Impl::RecreateSwapchain()
+ auto VulkanRenderer::Impl::recreate_swapchain() -> bool
{
// Wait until the window has a non-zero size (e.g. after un-minimizing).
int w = 0, h = 0;
@@ -1053,52 +1053,52 @@ namespace Donut
width = w; height = h;
vkDeviceWaitIdle(device);
- CleanupSwapchain();
- // renderFinished are tied to image count; recreate below via sync if it changed.
- if (!CreateSwapchain()) return false;
- if (!CreateImageViews()) return false;
- if (!CreateRenderPass()) return false;
- if (!CreateFramebuffers())return false;
- imagesInFlight.assign(images.size(), VK_NULL_HANDLE);
+ cleanup_swapchain();
+ // render_finished are tied to image count; recreate below via sync if it changed.
+ if (!create_swapchain()) return false;
+ if (!create_image_views()) return false;
+ if (!create_render_pass()) return false;
+ if (!create_framebuffers())return false;
+ images_in_flight.assign(images.size(), VK_NULL_HANDLE);
return true;
}
- VulkanRenderer::VulkanRenderer() { m_Impl = new Impl(); }
- VulkanRenderer::~VulkanRenderer() { Shutdown(); delete m_Impl; m_Impl = nullptr; }
+ VulkanRenderer::VulkanRenderer() { m_impl = new Impl(); }
+ VulkanRenderer::~VulkanRenderer() { shutdown(); delete m_impl; m_impl = nullptr; }
- bool VulkanRenderer::Init(void* glfwWindow, int width, int height)
+ auto VulkanRenderer::init(void* glfwWindow, int width, int height) -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
v.window = (GLFWwindow*)glfwWindow;
v.width = width; v.height = height;
- if (!v.CreateInstance()) return false;
- if (!v.PickPhysicalAndDevice()) return false;
- if (!v.CreateSwapchain()) return false;
- if (!v.CreateImageViews()) return false;
- if (!v.CreateRenderPass()) return false;
- if (!v.CreateFramebuffers()) return false;
- if (!v.CreateCommandBuffers()) return false;
- if (!v.CreateSyncObjects()) return false;
- if (!v.CreateGeodesicResources()) return false;
- if (!v.CreatePresentResources()) return false;
+ if (!v.create_instance()) return false;
+ if (!v.pick_physical_and_device()) return false;
+ if (!v.create_swapchain()) return false;
+ if (!v.create_image_views()) return false;
+ if (!v.create_render_pass()) return false;
+ if (!v.create_framebuffers()) return false;
+ if (!v.create_command_buffers()) return false;
+ if (!v.create_sync_objects()) return false;
+ if (!v.create_geodesic_resources()) return false;
+ if (!v.create_present_resources()) return false;
DONUT_INFO("Vulkan renderer ready: {} swapchain images, {}x{}",
- (int)v.images.size(), v.swapchainExtent.width, v.swapchainExtent.height);
+ (int)v.images.size(), v.swapchain_extent.width, v.swapchain_extent.height);
return true;
}
- bool VulkanRenderer::InitImGui()
+ auto VulkanRenderer::init_im_gui() -> bool
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) return false;
- VkDescriptorPoolSize poolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 };
+ VkDescriptorPoolSize pool_size{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 };
VkDescriptorPoolCreateInfo dpci{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
dpci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
dpci.maxSets = 1000;
- dpci.poolSizeCount = 1; dpci.pPoolSizes = &poolSize;
- VK_CHECK(vkCreateDescriptorPool(v.device, &dpci, nullptr, &v.imguiPool));
+ dpci.poolSizeCount = 1; dpci.pPoolSizes = &pool_size;
+ VK_CHECK(vkCreateDescriptorPool(v.device, &dpci, nullptr, &v.imgui_pool));
IMGUI_CHECKVERSION();
ImGui::CreateContext();
@@ -1108,16 +1108,16 @@ namespace Donut
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForVulkan(v.window, true);
- g_PrevScroll = glfwSetScrollCallback(v.window, DonutVkScrollCallback); // chain ImGui + camera zoom
+ g_PrevScroll = glfwSetScrollCallback(v.window, donut_vk_scroll_callback); // chain ImGui + camera zoom
ImGui_ImplVulkan_InitInfo info{};
info.ApiVersion = VK_API_VERSION_1_2;
info.Instance = v.instance;
info.PhysicalDevice = v.physical;
info.Device = v.device;
- info.QueueFamily = v.graphicsFamily;
- info.Queue = v.graphicsQueue;
- info.DescriptorPool = v.imguiPool;
- info.RenderPass = v.renderPass;
+ info.QueueFamily = v.graphics_family;
+ info.Queue = v.graphics_queue;
+ info.DescriptorPool = v.imgui_pool;
+ info.RenderPass = v.render_pass;
info.MinImageCount = 2;
info.ImageCount = (uint32_t)v.images.size();
info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
@@ -1127,107 +1127,107 @@ namespace Donut
return false;
}
- v.imguiInit = true;
+ v.imgui_init = true;
DONUT_INFO("Vulkan: ImGui backend initialized");
return true;
}
- void VulkanRenderer::OnResize(int width, int height)
+ auto VulkanRenderer::on_resize(int width, int height) -> void
{
- m_Impl->framebufferResized = true;
- m_Impl->width = width; m_Impl->height = height;
+ m_impl->framebuffer_resized = true;
+ m_impl->width = width; m_impl->height = height;
}
- void VulkanRenderer::DrawFrame(const glm::vec4& clearColor, const std::function<void()>& buildUI)
+ auto VulkanRenderer::draw_frame(const glm::vec4& clear_color, const std::function<void()>& build_ui) -> void
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) return;
- ImDrawData* drawData = nullptr;
- if (v.imguiInit)
+ ImDrawData* draw_data = nullptr;
+ if (v.imgui_init)
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
- if (buildUI) buildUI();
+ if (build_ui) build_ui();
ImGui::Render();
- drawData = ImGui::GetDrawData();
+ draw_data = ImGui::GetDrawData();
}
- vkWaitForFences(v.device, 1, &v.inFlight[v.currentFrame], VK_TRUE, UINT64_MAX);
+ vkWaitForFences(v.device, 1, &v.in_flight[v.current_frame], VK_TRUE, UINT64_MAX);
- uint32_t imageIndex = 0;
+ uint32_t image_index = 0;
VkResult r = vkAcquireNextImageKHR(v.device, v.swapchain, UINT64_MAX,
- v.imageAvailable[v.currentFrame], VK_NULL_HANDLE, &imageIndex);
- if (r == VK_ERROR_OUT_OF_DATE_KHR) { v.RecreateSwapchain(); return; }
+ v.image_available[v.current_frame], VK_NULL_HANDLE, &image_index);
+ if (r == VK_ERROR_OUT_OF_DATE_KHR) { v.recreate_swapchain(); return; }
if (r != VK_SUCCESS && r != VK_SUBOPTIMAL_KHR) { DONUT_ERROR("Vulkan: acquire failed ({})", (int)r); return; }
- if (v.imagesInFlight[imageIndex] != VK_NULL_HANDLE)
- vkWaitForFences(v.device, 1, &v.imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
- v.imagesInFlight[imageIndex] = v.inFlight[v.currentFrame];
+ if (v.images_in_flight[image_index] != VK_NULL_HANDLE)
+ vkWaitForFences(v.device, 1, &v.images_in_flight[image_index], VK_TRUE, UINT64_MAX);
+ v.images_in_flight[image_index] = v.in_flight[v.current_frame];
// The geodesic offscreen image is shared across frames in flight; wait for
// the previous frame to finish reading it before overwriting it this frame.
- if (v.geoInUse != VK_NULL_HANDLE)
- vkWaitForFences(v.device, 1, &v.geoInUse, VK_TRUE, UINT64_MAX);
- v.ProcessInput();
- v.UpdateGeodesicUniforms();
+ if (v.geo_in_use != VK_NULL_HANDLE)
+ vkWaitForFences(v.device, 1, &v.geo_in_use, VK_TRUE, UINT64_MAX);
+ v.process_input();
+ v.update_geodesic_uniforms();
- vkResetCommandBuffer(v.commandBuffers[v.currentFrame], 0);
- if (!v.RecordCommandBuffer(v.commandBuffers[v.currentFrame], imageIndex, clearColor, drawData)) return;
+ vkResetCommandBuffer(v.command_buffers[v.current_frame], 0);
+ if (!v.record_command_buffer(v.command_buffers[v.current_frame], image_index, clear_color, draw_data)) return;
- VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkSubmitInfo submit{ VK_STRUCTURE_TYPE_SUBMIT_INFO };
submit.waitSemaphoreCount = 1;
- submit.pWaitSemaphores = &v.imageAvailable[v.currentFrame];
- submit.pWaitDstStageMask = &waitStage;
+ submit.pWaitSemaphores = &v.image_available[v.current_frame];
+ submit.pWaitDstStageMask = &wait_stage;
submit.commandBufferCount = 1;
- submit.pCommandBuffers = &v.commandBuffers[v.currentFrame];
+ submit.pCommandBuffers = &v.command_buffers[v.current_frame];
submit.signalSemaphoreCount = 1;
- submit.pSignalSemaphores = &v.renderFinished[imageIndex];
+ submit.pSignalSemaphores = &v.render_finished[image_index];
- vkResetFences(v.device, 1, &v.inFlight[v.currentFrame]);
- if (vkQueueSubmit(v.graphicsQueue, 1, &submit, v.inFlight[v.currentFrame]) != VK_SUCCESS)
+ vkResetFences(v.device, 1, &v.in_flight[v.current_frame]);
+ if (vkQueueSubmit(v.graphics_queue, 1, &submit, v.in_flight[v.current_frame]) != VK_SUCCESS)
{ DONUT_ERROR("Vulkan: queue submit failed"); return; }
- v.geoInUse = v.inFlight[v.currentFrame];
+ v.geo_in_use = v.in_flight[v.current_frame];
VkPresentInfoKHR present{ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
present.waitSemaphoreCount = 1;
- present.pWaitSemaphores = &v.renderFinished[imageIndex];
+ present.pWaitSemaphores = &v.render_finished[image_index];
present.swapchainCount = 1;
present.pSwapchains = &v.swapchain;
- present.pImageIndices = &imageIndex;
- r = vkQueuePresentKHR(v.presentQueue, &present);
- if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR || v.framebufferResized)
+ present.pImageIndices = &image_index;
+ r = vkQueuePresentKHR(v.present_queue, &present);
+ if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR || v.framebuffer_resized)
{
- v.framebufferResized = false;
- v.RecreateSwapchain();
+ v.framebuffer_resized = false;
+ v.recreate_swapchain();
}
- v.currentFrame = (v.currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
+ v.current_frame = (v.current_frame + 1) % MAX_FRAMES_IN_FLIGHT;
}
- void VulkanRenderer::Shutdown()
+ auto VulkanRenderer::shutdown() -> void
{
- Impl& v = *m_Impl;
+ Impl& v = *m_impl;
if (v.device == VK_NULL_HANDLE) { if (v.instance && v.surface) { vkDestroySurfaceKHR(v.instance, v.surface, nullptr); v.surface = VK_NULL_HANDLE; } if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; } return; }
vkDeviceWaitIdle(v.device);
- v.DestroyGeodesicResources();
- if (v.imguiInit)
+ v.destroy_geodesic_resources();
+ if (v.imgui_init)
{
ImGui_ImplVulkan_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
- v.imguiInit = false;
+ v.imgui_init = false;
}
- if (v.imguiPool) { vkDestroyDescriptorPool(v.device, v.imguiPool, nullptr); v.imguiPool = VK_NULL_HANDLE; }
- for (auto s : v.renderFinished) vkDestroySemaphore(v.device, s, nullptr);
- for (auto s : v.imageAvailable) vkDestroySemaphore(v.device, s, nullptr);
- for (auto f : v.inFlight) vkDestroyFence(v.device, f, nullptr);
- v.renderFinished.clear(); v.imageAvailable.clear(); v.inFlight.clear();
- if (v.commandPool) { vkDestroyCommandPool(v.device, v.commandPool, nullptr); v.commandPool = VK_NULL_HANDLE; }
- v.CleanupSwapchain();
+ if (v.imgui_pool) { vkDestroyDescriptorPool(v.device, v.imgui_pool, nullptr); v.imgui_pool = VK_NULL_HANDLE; }
+ for (auto s : v.render_finished) vkDestroySemaphore(v.device, s, nullptr);
+ for (auto s : v.image_available) vkDestroySemaphore(v.device, s, nullptr);
+ for (auto f : v.in_flight) vkDestroyFence(v.device, f, nullptr);
+ v.render_finished.clear(); v.image_available.clear(); v.in_flight.clear();
+ if (v.command_pool) { vkDestroyCommandPool(v.device, v.command_pool, nullptr); v.command_pool = VK_NULL_HANDLE; }
+ v.cleanup_swapchain();
vkDestroyDevice(v.device, nullptr); v.device = VK_NULL_HANDLE;
if (v.surface) { vkDestroySurfaceKHR(v.instance, v.surface, nullptr); v.surface = VK_NULL_HANDLE; }
if (v.instance) { vkDestroyInstance(v.instance, nullptr); v.instance = VK_NULL_HANDLE; }
diff --git a/src/Platform/Vulkan/VulkanRenderer.h b/src/platform/vulkan/vulkan_renderer.h
index 9b5a23f..97a4e38 100644
--- a/src/Platform/Vulkan/VulkanRenderer.h
+++ b/src/platform/vulkan/vulkan_renderer.h
@@ -12,7 +12,7 @@ namespace Donut
// Must be called BEFORE glfwInit() when the Vulkan API is selected: points
// GLFW at the loader the app links against (GLFW's own dlopen fails on
// macOS/Homebrew) and configures the MoltenVK ICD / layer paths.
- void VulkanPrepareGLFW();
+ auto vulkan_prepare_glfw() -> void;
class VulkanRenderer
{
@@ -21,20 +21,20 @@ namespace Donut
~VulkanRenderer();
// glfwWindow must be a GLFW window created with GLFW_NO_API.
- bool Init(void* glfwWindow, int width, int height);
- void Shutdown();
+ auto init(void* glfwWindow, int width, int height) -> bool;
+ auto shutdown() -> void;
- // Creates the ImGui context + Vulkan/GLFW backends. Call after Init().
- bool InitImGui();
+ // Creates the ImGui context + Vulkan/GLFW backends. Call after init().
+ auto init_im_gui() -> bool;
- // Renders + presents one frame: clears to clearColor, then (if InitImGui
+ // Renders + presents one frame: clears to clearColor, then (if init_im_gui
// ran) opens an ImGui frame, invokes buildUI to populate it, and draws it.
- void DrawFrame(const glm::vec4& clearColor, const std::function<void()>& buildUI = {});
+ auto draw_frame(const glm::vec4& clearColor, const std::function<void()>& buildUI = {}) -> void;
- void OnResize(int width, int height);
+ auto on_resize(int width, int height) -> void;
private:
struct Impl;
- Impl* m_Impl = nullptr;
+ Impl* m_impl = nullptr;
};
}
diff --git a/src/Platform/Vulkan/VulkanRendererAPI.cpp b/src/platform/vulkan/vulkan_renderer_api.cpp
index 79f7633..56499f4 100644
--- a/src/Platform/Vulkan/VulkanRendererAPI.cpp
+++ b/src/platform/vulkan/vulkan_renderer_api.cpp
@@ -1,79 +1,79 @@
-#include "VulkanRendererAPI.h"
+#include "vulkan_renderer_api.h"
namespace Donut
{
- void VulkanRendererAPI::Init()
+ auto VulkanRendererAPI::init() -> void
{
// TODO(Hachem): Implement Vulkan renderer API initialization
}
- void VulkanRendererAPI::SetViewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+ auto VulkanRendererAPI::set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
{
// TODO(Hachem): Implement Vulkan viewport setting
}
- void VulkanRendererAPI::SetClearColor(const glm::vec4& color)
+ auto VulkanRendererAPI::set_clear_color(const glm::vec4& color) -> void
{
// TODO(Hachem): Implement Vulkan clear color setting
}
- void VulkanRendererAPI::Clear()
+ auto VulkanRendererAPI::clear() -> void
{
// TODO(Hachem): Implement Vulkan clear
}
- void VulkanRendererAPI::EnableDepthTest()
+ auto VulkanRendererAPI::enable_depth_test() -> void
{
// TODO(Hachem): Implement Vulkan depth test enabling
}
- void VulkanRendererAPI::DisableDepthTest()
+ auto VulkanRendererAPI::disable_depth_test() -> void
{
// TODO(Hachem): Implement Vulkan depth test disabling
}
- void VulkanRendererAPI::SetFaceCulling(bool enabled)
+ auto VulkanRendererAPI::set_face_culling(bool enabled) -> void
{
// TODO(Hachem): Implement Vulkan face culling setting
}
- void VulkanRendererAPI::EnableBlending()
+ auto VulkanRendererAPI::enable_blending() -> void
{
// TODO(Hachem): Implement Vulkan blending enabling
}
- void VulkanRendererAPI::DisableBlending()
+ auto VulkanRendererAPI::disable_blending() -> void
{
// TODO(Hachem): Implement Vulkan blending disabling
}
- void VulkanRendererAPI::DrawIndexed(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
+ auto VulkanRendererAPI::draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
{
// TODO(Hachem): Implement Vulkan indexed drawing
}
- void VulkanRendererAPI::DrawArrays(uint32_t vertexCount, uint32_t first)
+ auto VulkanRendererAPI::draw_arrays(uint32_t vertex_count, uint32_t first) -> void
{
// TODO(Hachem): Implement Vulkan array drawing
}
- void VulkanRendererAPI::DrawLines(const Ref<VertexArray>& vertexArray, uint32_t indexCount)
+ auto VulkanRendererAPI::draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count) -> void
{
// TODO(Hachem): Implement Vulkan line drawing
}
- void VulkanRendererAPI::BindTexture(uint32_t textureID, uint32_t slot)
+ auto VulkanRendererAPI::bind_texture(uint32_t texture_id, uint32_t slot) -> void
{
// TODO(Hachem): Implement Vulkan texture binding
}
- void VulkanRendererAPI::BindImageTexture(uint32_t textureID, uint32_t slot, bool readOnly)
+ auto VulkanRendererAPI::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
{
// TODO(Hachem): Implement Vulkan image texture binding
}
- void VulkanRendererAPI::ReadPixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
- uint32_t format, uint32_t type, void* pixels)
+ auto VulkanRendererAPI::read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void
{
// TODO(Hachem): Implement Vulkan pixel reading
// For now, this is a placeholder implementation
diff --git a/src/platform/vulkan/vulkan_renderer_api.h b/src/platform/vulkan/vulkan_renderer_api.h
new file mode 100644
index 0000000..15bf76d
--- /dev/null
+++ b/src/platform/vulkan/vulkan_renderer_api.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "core/memory.h"
+#include "rendering/renderer.h"
+
+namespace Donut
+{
+ class VulkanRendererAPI
+ : public RendererAPI
+ {
+ public:
+ virtual auto init() -> void override;
+ virtual void set_viewport(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height) override;
+ virtual auto set_clear_color(const glm::vec4& color) -> void override;
+ virtual auto clear() -> void override;
+ virtual auto enable_depth_test() -> void override;
+ virtual auto disable_depth_test() -> void override;
+ virtual auto set_face_culling(bool enabled) -> void override;
+ virtual auto enable_blending() -> void override;
+ virtual auto disable_blending() -> void override;
+
+ virtual void draw_indexed(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) override;
+
+ virtual void draw_arrays(uint32_t vertex_count,
+ uint32_t first = 0) override;
+ virtual void draw_lines(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) override;
+ virtual void bind_texture(uint32_t texture_id,
+ uint32_t slot = 0) override;
+ virtual void bind_image_texture(uint32_t texture_id,
+ uint32_t slot = 0,
+ bool read_only = false) override;
+ virtual void read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) override;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanShader.cpp b/src/platform/vulkan/vulkan_shader.cpp
index 2117384..7549761 100644
--- a/src/Platform/Vulkan/VulkanShader.cpp
+++ b/src/platform/vulkan/vulkan_shader.cpp
@@ -1,4 +1,4 @@
-#include "VulkanShader.h"
+#include "vulkan_shader.h"
#include <fstream>
#include <glm/gtc/type_ptr.hpp>
@@ -10,14 +10,14 @@ namespace Donut
// TODO(Hachem): Implement Vulkan shader creation from filepath
}
- VulkanShader::VulkanShader(const std::string& name, const std::string& vertexSrc, const std::string& fragmentSrc)
- : m_Name(name)
+ VulkanShader::VulkanShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src)
+ : m_name(name)
{
// TODO(Hachem): Implement Vulkan shader creation from source
}
- VulkanShader::VulkanShader(const std::string& name, const std::string& computeSrc)
- : m_Name(name)
+ VulkanShader::VulkanShader(const std::string& name, const std::string& compute_src)
+ : m_name(name)
{
// TODO(Hachem): Implement Vulkan compute shader creation
}
@@ -27,119 +27,119 @@ namespace Donut
// TODO(Hachem): Implement Vulkan shader cleanup
}
- void VulkanShader::Bind() const
+ auto VulkanShader::bind() const -> void
{
// TODO(Hachem): Implement Vulkan shader binding
}
- void VulkanShader::Unbind() const
+ auto VulkanShader::unbind() const -> void
{
// TODO(Hachem): Implement Vulkan shader unbinding
}
- void VulkanShader::SetInt(const std::string& name, int value)
+ auto VulkanShader::set_int(const std::string& name, int value) -> void
{
// TODO(Hachem): Implement Vulkan shader int uniform setting
}
- void VulkanShader::SetIntArray(const std::string& name, int* values, uint32_t count)
+ auto VulkanShader::set_int_array(const std::string& name, int* values, uint32_t count) -> void
{
// TODO(Hachem): Implement Vulkan shader int array uniform setting
}
- void VulkanShader::SetFloat(const std::string& name, float value)
+ auto VulkanShader::set_float(const std::string& name, float value) -> void
{
// TODO(Hachem): Implement Vulkan shader float uniform setting
}
- void VulkanShader::SetFloat2(const std::string& name, const glm::vec2& value)
+ auto VulkanShader::set_float2(const std::string& name, const glm::vec2& value) -> void
{
// TODO(Hachem): Implement Vulkan shader float2 uniform setting
}
- void VulkanShader::SetFloat3(const std::string& name, const glm::vec3& value)
+ auto VulkanShader::set_float3(const std::string& name, const glm::vec3& value) -> void
{
// TODO(Hachem): Implement Vulkan shader float3 uniform setting
}
- void VulkanShader::SetFloat4(const std::string& name, const glm::vec4& value)
+ auto VulkanShader::set_float4(const std::string& name, const glm::vec4& value) -> void
{
// TODO(Hachem): Implement Vulkan shader float4 uniform setting
}
- void VulkanShader::SetMat4(const std::string& name, const glm::mat4& value)
+ auto VulkanShader::set_mat4(const std::string& name, const glm::mat4& value) -> void
{
// TODO(Hachem): Implement Vulkan shader mat4 uniform setting
}
- void VulkanShader::Dispatch(uint32_t x, uint32_t y, uint32_t z)
+ auto VulkanShader::dispatch(uint32_t x, uint32_t y, uint32_t z) -> void
{
// TODO(Hachem): Implement Vulkan compute shader dispatch
}
- void VulkanShader::DispatchIndirect(uint32_t offset)
+ auto VulkanShader::dispatch_indirect(uint32_t offset) -> void
{
// TODO(Hachem): Implement Vulkan indirect compute shader dispatch
}
- void VulkanShader::MemoryBarrier(uint32_t barriers)
+ auto VulkanShader::memory_barrier(uint32_t barriers) -> void
{
// TODO(Hachem): Implement Vulkan memory barrier
}
- void VulkanShader::UploadUniformInt(const std::string& name, int value)
+ auto VulkanShader::upload_uniform_int(const std::string& name, int value) -> void
{
// TODO(Hachem): Implement Vulkan uniform int upload
}
- void VulkanShader::UploadUniformIntArray(const std::string& name, int* values, uint32_t count)
+ auto VulkanShader::upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void
{
// TODO(Hachem): Implement Vulkan uniform int array upload
}
- void VulkanShader::UploadUniformFloat(const std::string& name, float value)
+ auto VulkanShader::upload_uniform_float(const std::string& name, float value) -> void
{
// TODO(Hachem): Implement Vulkan uniform float upload
}
- void VulkanShader::UploadUniformFloat2(const std::string& name, const glm::vec2& value)
+ auto VulkanShader::upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void
{
// TODO(Hachem): Implement Vulkan uniform float2 upload
}
- void VulkanShader::UploadUniformFloat3(const std::string& name, const glm::vec3& value)
+ auto VulkanShader::upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void
{
// TODO(Hachem): Implement Vulkan uniform float3 upload
}
- void VulkanShader::UploadUniformFloat4(const std::string& name, const glm::vec4& value)
+ auto VulkanShader::upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void
{
// TODO(Hachem): Implement Vulkan uniform float4 upload
}
- void VulkanShader::UploadUniformMat3(const std::string& name, const glm::mat3& matrix)
+ auto VulkanShader::upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void
{
// TODO(Hachem): Implement Vulkan uniform mat3 upload
}
- void VulkanShader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
+ auto VulkanShader::upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void
{
// TODO(Hachem): Implement Vulkan uniform mat4 upload
}
- std::string VulkanShader::ReadFile(const std::string& filepath)
+ auto VulkanShader::read_file(const std::string& filepath) -> std::string
{
// TODO(Hachem): Implement file reading for Vulkan shader
return "";
}
- std::unordered_map<uint32_t, std::string> VulkanShader::PreProcess(const std::string& source)
+ auto VulkanShader::pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>
{
// TODO(Hachem): Implement shader preprocessing for Vulkan
return {};
}
- void VulkanShader::Compile(const std::unordered_map<uint32_t, std::string>& shaderSources)
+ auto VulkanShader::compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void
{
// TODO(Hachem): Implement Vulkan shader compilation
}
diff --git a/src/platform/vulkan/vulkan_shader.h b/src/platform/vulkan/vulkan_shader.h
new file mode 100644
index 0000000..9453130
--- /dev/null
+++ b/src/platform/vulkan/vulkan_shader.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "rendering/shader.h"
+
+#include <string>
+#include <unordered_map>
+
+namespace Donut
+{
+ class VulkanShader
+ : public Shader
+ {
+ public:
+ VulkanShader(const std::string& filepath);
+ VulkanShader(const std::string& name, const std::string& vertex_src, const std::string& fragment_src);
+ VulkanShader(const std::string& name, const std::string& compute_src);
+ virtual ~VulkanShader();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto set_int(const std::string& name, int value) -> void override;
+ virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void override;
+ virtual auto set_float(const std::string& name, float value) -> void override;
+ virtual auto set_float2(const std::string& name, const glm::vec2& value) -> void override;
+ virtual auto set_float3(const std::string& name, const glm::vec3& value) -> void override;
+ virtual auto set_float4(const std::string& name, const glm::vec4& value) -> void override;
+ virtual auto set_mat4(const std::string& name, const glm::mat4& value) -> void override;
+
+ virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void override;
+ virtual auto dispatch_indirect(uint32_t offset = 0) -> void override;
+ virtual auto memory_barrier(uint32_t barriers) -> void override;
+
+ virtual auto get_name() const -> const std::string& override{ return m_name; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ auto upload_uniform_int(const std::string& name, int value) -> void;
+ auto upload_uniform_int_array(const std::string& name, int* values, uint32_t count) -> void;
+ auto upload_uniform_float(const std::string& name, float value) -> void;
+ auto upload_uniform_float2(const std::string& name, const glm::vec2& value) -> void;
+ auto upload_uniform_float3(const std::string& name, const glm::vec3& value) -> void;
+ auto upload_uniform_float4(const std::string& name, const glm::vec4& value) -> void;
+ auto upload_uniform_mat3(const std::string& name, const glm::mat3& matrix) -> void;
+ auto upload_uniform_mat4(const std::string& name, const glm::mat4& matrix) -> void;
+ private:
+ auto read_file(const std::string& filepath) -> std::string;
+ auto pre_process(const std::string& source) -> std::unordered_map<uint32_t, std::string>;
+ auto compile(const std::unordered_map<uint32_t, std::string>& shader_sources) -> void;
+ uint32_t m_renderer_id;
+ std::string m_name;
+ };
+};
diff --git a/src/platform/vulkan/vulkan_texture.cpp b/src/platform/vulkan/vulkan_texture.cpp
new file mode 100644
index 0000000..a970a53
--- /dev/null
+++ b/src/platform/vulkan/vulkan_texture.cpp
@@ -0,0 +1,73 @@
+#include "vulkan_texture.h"
+
+namespace Donut
+{
+ VulkanTexture2D::VulkanTexture2D(uint32_t width, uint32_t height)
+ : m_width(width), m_height(height)
+ {
+ m_internal_format = 0;
+ m_data_format = 0;
+ m_renderer_id = 0;
+ }
+
+ VulkanTexture2D::VulkanTexture2D(const std::string& path)
+ : m_path(path)
+ {
+ m_width = 1;
+ m_height = 1;
+ m_internal_format = 0;
+ m_data_format = 0;
+ m_renderer_id = 0;
+ }
+
+ VulkanTexture2D::~VulkanTexture2D()
+ {
+ }
+
+ auto VulkanTexture2D::set_data(void* data, uint32_t size) -> void
+ {
+ }
+
+ auto VulkanTexture2D::bind(uint32_t slot) const -> void
+ {
+ }
+
+ auto VulkanTexture2D::bind_as_image(uint32_t slot, bool read_only) const -> void
+ {
+ }
+
+ // Vulkan Cubemap Implementation (Placeholder)
+ VulkanCubemapTexture::VulkanCubemapTexture(uint32_t width, uint32_t height)
+ : m_width(width), m_height(height)
+ {
+ m_internal_format = 0;
+ m_data_format = 0;
+ m_renderer_id = 0;
+ }
+
+ VulkanCubemapTexture::VulkanCubemapTexture(const std::string& path)
+ : m_path(path)
+ {
+ m_width = 1024;
+ m_height = 1024;
+ m_internal_format = 0;
+ m_data_format = 0;
+ m_renderer_id = 0;
+ }
+
+ VulkanCubemapTexture::~VulkanCubemapTexture()
+ {
+ }
+
+ auto VulkanCubemapTexture::set_data(void* data, uint32_t size) -> void
+ {
+ }
+
+ auto VulkanCubemapTexture::bind(uint32_t slot) const -> void
+ {
+ }
+
+ auto VulkanCubemapTexture::bind_as_image(uint32_t slot, bool read_only) const -> void
+ {
+ }
+};
diff --git a/src/platform/vulkan/vulkan_texture.h b/src/platform/vulkan/vulkan_texture.h
new file mode 100644
index 0000000..ea8471b
--- /dev/null
+++ b/src/platform/vulkan/vulkan_texture.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "rendering/texture.h"
+
+namespace Donut
+{
+ class VulkanTexture2D
+ : public Texture2D
+ {
+ public:
+ VulkanTexture2D(uint32_t width, uint32_t height);
+ VulkanTexture2D(const std::string& path);
+ virtual ~VulkanTexture2D();
+
+ virtual auto get_width() const -> uint32_t override{ return m_width; }
+ virtual auto get_height() const -> uint32_t override{ return m_height; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ virtual auto set_data(void* data, uint32_t size) -> void override;
+ virtual auto bind(uint32_t slot = 0) const -> void override;
+ virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_renderer_id == ((VulkanTexture2D&)other).m_renderer_id;
+ }
+ private:
+ std::string m_path;
+ uint32_t m_width, m_height;
+ uint32_t m_renderer_id;
+ uint32_t m_internal_format, m_data_format;
+ };
+
+ class VulkanCubemapTexture
+ : public CubemapTexture
+ {
+ public:
+ VulkanCubemapTexture(uint32_t width, uint32_t height);
+ VulkanCubemapTexture(const std::string& path);
+ virtual ~VulkanCubemapTexture();
+
+ virtual auto get_width() const -> uint32_t override{ return m_width; }
+ virtual auto get_height() const -> uint32_t override{ return m_height; }
+ virtual auto get_renderer_id() const -> uint32_t override{ return m_renderer_id; }
+
+ virtual auto set_data(void* data, uint32_t size) -> void override;
+ virtual auto bind(uint32_t slot = 0) const -> void override;
+ virtual auto bind_as_image(uint32_t slot = 0, bool read_only = false) const -> void override;
+
+ virtual bool operator==(const Texture& other) const override
+ {
+ return m_renderer_id == ((VulkanCubemapTexture&)other).m_renderer_id;
+ }
+ private:
+ std::string m_path;
+ uint32_t m_width, m_height;
+ uint32_t m_renderer_id;
+ uint32_t m_internal_format, m_data_format;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanUniformBuffer.cpp b/src/platform/vulkan/vulkan_uniform_buffer.cpp
index 8152478..1a64099 100644
--- a/src/Platform/Vulkan/VulkanUniformBuffer.cpp
+++ b/src/platform/vulkan/vulkan_uniform_buffer.cpp
@@ -1,9 +1,9 @@
-#include "VulkanUniformBuffer.h"
+#include "vulkan_uniform_buffer.h"
namespace Donut
{
VulkanUniformBuffer::VulkanUniformBuffer(uint32_t size, uint32_t binding)
- : m_Size(size), m_Binding(binding)
+ : m_size(size), m_binding(binding)
{
// TODO: Implement Vulkan uniform buffer
}
@@ -13,12 +13,12 @@ namespace Donut
// TODO: Implement Vulkan uniform buffer cleanup
}
- void VulkanUniformBuffer::SetData(const void* data, uint32_t size, uint32_t offset)
+ auto VulkanUniformBuffer::set_data(const void* data, uint32_t size, uint32_t offset) -> void
{
// TODO: Implement Vulkan uniform buffer data setting
}
- void VulkanUniformBuffer::Bind(uint32_t binding)
+ auto VulkanUniformBuffer::bind(uint32_t binding) -> void
{
// TODO: Implement Vulkan uniform buffer 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..7d9ad0a
--- /dev/null
+++ b/src/platform/vulkan/vulkan_uniform_buffer.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "rendering/uniform_buffer.h"
+
+namespace Donut
+{
+ class VulkanUniformBuffer : public UniformBuffer
+ {
+ public:
+ VulkanUniformBuffer(uint32_t size, uint32_t binding);
+ virtual ~VulkanUniformBuffer();
+
+ virtual auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void override;
+ virtual auto bind(uint32_t binding) -> void override;
+ private:
+ uint32_t m_size;
+ uint32_t m_binding;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanVertexArray.cpp b/src/platform/vulkan/vulkan_vertex_array.cpp
index de22f05..73219b1 100644
--- a/src/Platform/Vulkan/VulkanVertexArray.cpp
+++ b/src/platform/vulkan/vulkan_vertex_array.cpp
@@ -1,4 +1,4 @@
-#include "VulkanVertexArray.h"
+#include "vulkan_vertex_array.h"
namespace Donut
{
@@ -12,25 +12,25 @@ namespace Donut
// TODO(Hachem): Implement Vulkan vertex array cleanup
}
- void VulkanVertexArray::Bind() const
+ auto VulkanVertexArray::bind() const -> void
{
// TODO(Hachem): Implement Vulkan vertex array binding
}
- void VulkanVertexArray::Unbind() const
+ auto VulkanVertexArray::unbind() const -> void
{
// TODO(Hachem): Implement Vulkan vertex array unbinding
}
- void VulkanVertexArray::AddVertexBuffer(const Ref<VertexBuffer>& vertexBuffer)
+ auto VulkanVertexArray::add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void
{
// TODO(Hachem): Implement Vulkan vertex buffer addition
- m_VertexBuffers.push_back(vertexBuffer);
+ m_vertex_buffers.push_back(vertex_buffer);
}
- void VulkanVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& indexBuffer)
+ auto VulkanVertexArray::set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void
{
// TODO(Hachem): Implement Vulkan index buffer setting
- m_IndexBuffer = indexBuffer;
+ m_index_buffer = index_buffer;
}
};
diff --git a/src/platform/vulkan/vulkan_vertex_array.h b/src/platform/vulkan/vulkan_vertex_array.h
new file mode 100644
index 0000000..0d282a2
--- /dev/null
+++ b/src/platform/vulkan/vulkan_vertex_array.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "rendering/vertex_array.h"
+#include "core/memory.h"
+
+namespace Donut
+{
+ class VulkanVertexArray
+ : public VertexArray
+ {
+ public:
+ VulkanVertexArray();
+ virtual ~VulkanVertexArray();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void override;
+ virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void override;
+
+ virtual auto get_vertex_buffers() const -> const std::vector<Ref<VertexBuffer>>& { return m_vertex_buffers; }
+ virtual auto get_index_buffer() const -> const Ref<IndexBuffer>& { return m_index_buffer; }
+ private:
+ uint32_t m_renderer_id;
+ std::vector<Ref<VertexBuffer>> m_vertex_buffers;
+ Ref<IndexBuffer> m_index_buffer;
+ };
+};
diff --git a/src/Platform/Vulkan/VulkanVertexBuffer.cpp b/src/platform/vulkan/vulkan_vertex_buffer.cpp
index f77cbe4..6a222c1 100644
--- a/src/Platform/Vulkan/VulkanVertexBuffer.cpp
+++ b/src/platform/vulkan/vulkan_vertex_buffer.cpp
@@ -1,4 +1,4 @@
-#include "VulkanVertexBuffer.h"
+#include "vulkan_vertex_buffer.h"
namespace Donut
{
@@ -17,17 +17,17 @@ namespace Donut
// TODO(Hachem): Implement Vulkan vertex buffer cleanup
}
- void VulkanVertexBuffer::Bind() const
+ auto VulkanVertexBuffer::bind() const -> void
{
// TODO(Hachem): Implement Vulkan vertex buffer binding
}
- void VulkanVertexBuffer::Unbind() const
+ auto VulkanVertexBuffer::unbind() const -> void
{
// TODO(Hachem): Implement Vulkan vertex buffer unbinding
}
- void VulkanVertexBuffer::SetData(const void* data, uint32_t size)
+ auto VulkanVertexBuffer::set_data(const void* data, uint32_t size) -> void
{
// TODO(Hachem): Implement Vulkan vertex buffer data setting
}
diff --git a/src/platform/vulkan/vulkan_vertex_buffer.h b/src/platform/vulkan/vulkan_vertex_buffer.h
new file mode 100644
index 0000000..3218021
--- /dev/null
+++ b/src/platform/vulkan/vulkan_vertex_buffer.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "rendering/vertex_buffer.h"
+
+namespace Donut
+{
+ class VulkanVertexBuffer
+ : public VertexBuffer
+ {
+ public:
+ VulkanVertexBuffer(uint32_t size);
+ VulkanVertexBuffer(float* vertices, uint32_t size);
+ virtual ~VulkanVertexBuffer();
+
+ virtual auto bind() const -> void override;
+ virtual auto unbind() const -> void override;
+
+ virtual auto set_data(const void* data, uint32_t size) -> void override;
+
+ virtual auto get_layout() const -> const VertexBufferLayout& override{ return m_layout; }
+ virtual auto set_layout(const VertexBufferLayout& layout) -> void override{ m_layout = layout; }
+ private:
+ uint32_t m_renderer_id;
+ VertexBufferLayout m_layout;
+ };
+};
diff --git a/src/rendering/framebuffer.cpp b/src/rendering/framebuffer.cpp
new file mode 100644
index 0000000..089c31e
--- /dev/null
+++ b/src/rendering/framebuffer.cpp
@@ -0,0 +1,17 @@
+#include "framebuffer.h"
+#include "renderer.h"
+
+#include "platform/opengl/opengl_framebuffer.h"
+
+namespace Donut
+{
+ auto Framebuffer::create(const FramebufferSpecification& spec) -> Ref<Framebuffer>
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL: return create_ref<OpenGLFramebuffer>(spec);
+ }
+
+ return nullptr;
+ }
+};
diff --git a/src/rendering/framebuffer.h b/src/rendering/framebuffer.h
new file mode 100644
index 0000000..a01a81b
--- /dev/null
+++ b/src/rendering/framebuffer.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "core/memory.h"
+#include "texture.h"
+
+namespace Donut
+{
+ enum class FramebufferTextureFormat
+ {
+ None = 0,
+
+ RGBA8,
+ RED_INTEGER,
+
+ DEPTH24STENCIL8,
+
+ Depth = DEPTH24STENCIL8
+ };
+
+ struct FramebufferTextureSpecification
+ {
+ FramebufferTextureSpecification() = default;
+ FramebufferTextureSpecification(FramebufferTextureFormat format)
+ : texture_format(format) {}
+
+ FramebufferTextureFormat texture_format = FramebufferTextureFormat::None;
+ };
+
+ struct FramebufferAttachmentSpecification
+ {
+ FramebufferAttachmentSpecification() = default;
+ FramebufferAttachmentSpecification(std::initializer_list<FramebufferTextureSpecification> attachments)
+ : attachments(attachments) {}
+
+ std::vector<FramebufferTextureSpecification> attachments;
+ };
+
+ struct FramebufferSpecification
+ {
+ uint32_t Width = 0, Height = 0;
+ FramebufferAttachmentSpecification attachments;
+ uint32_t Samples = 1;
+
+ bool SwapChainTarget = false;
+ };
+
+ class Framebuffer
+ {
+ public:
+ virtual ~Framebuffer() = default;
+
+ virtual auto bind() -> void = 0;
+ virtual auto unbind() -> void = 0;
+
+ virtual auto resize(uint32_t width, uint32_t height) -> void = 0;
+ virtual auto read_pixel(uint32_t attachment_index, int x, int y) -> int = 0;
+
+ virtual auto clear_attachment(uint32_t attachment_index, int value) -> void = 0;
+ virtual auto get_color_attachment_renderer_id(uint32_t index = 0) const -> uint32_t = 0;
+
+ virtual auto get_specification() const -> const FramebufferSpecification& = 0;
+
+ static auto create(const FramebufferSpecification& spec) -> Ref<Framebuffer>;
+ };
+};
diff --git a/src/Rendering/IndexBuffer.cpp b/src/rendering/index_buffer.cpp
index 185684b..9603c72 100644
--- a/src/Rendering/IndexBuffer.cpp
+++ b/src/rendering/index_buffer.cpp
@@ -1,14 +1,14 @@
-#include "IndexBuffer.h"
-#include "Renderer.h"
+#include "index_buffer.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLIndexBuffer.h"
-#include "Platform/Vulkan/VulkanIndexBuffer.h"
+#include "platform/opengl/opengl_index_buffer.h"
+#include "platform/vulkan/vulkan_index_buffer.h"
namespace Donut
{
- IndexBuffer* IndexBuffer::Create(const uint32_t* indices, uint32_t count)
+ auto IndexBuffer::create(const uint32_t* indices, uint32_t count) -> IndexBuffer*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
return new OpenGLIndexBuffer(indices, count);
diff --git a/src/rendering/index_buffer.h b/src/rendering/index_buffer.h
new file mode 100644
index 0000000..8389f2d
--- /dev/null
+++ b/src/rendering/index_buffer.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <cstdint>
+
+namespace Donut
+{
+ class IndexBuffer
+ {
+ public:
+ virtual ~IndexBuffer() = default;
+
+ virtual auto bind() const -> void = 0;
+ virtual auto unbind() const -> void = 0;
+ virtual auto get_count() const -> uint32_t = 0;
+
+ static auto create(const uint32_t* indices, uint32_t count) -> IndexBuffer*;
+ };
+};
diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp
new file mode 100644
index 0000000..28b7962
--- /dev/null
+++ b/src/rendering/renderer.cpp
@@ -0,0 +1,64 @@
+#include "renderer.h"
+
+#include "platform/opengl/opengl_renderer_api.h"
+#include "platform/vulkan/vulkan_renderer_api.h"
+
+#include <glm/gtc/matrix_transform.hpp>
+
+namespace Donut
+{
+ auto RendererAPI::create() -> Scope<RendererAPI>
+ {
+ switch (s_api)
+ {
+ case API::OpenGL:
+ return create_scope<OpenGLRendererAPI>();
+ case API::Vulkan:
+ return create_scope<VulkanRendererAPI>();
+ default:
+ return nullptr;
+ }
+ }
+
+ RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
+
+ auto Renderer::init() -> void
+ {
+ RenderCommand::init();
+ RenderCommand::enable_depth_test();
+ }
+
+ auto Renderer::shutdown() -> void
+ {
+ }
+
+ auto Renderer::on_window_resize(uint32_t width, uint32_t height) -> void
+ {
+ RenderCommand::set_viewport(0, 0, width, height);
+ }
+
+ auto Renderer::submit(const Ref<Shader>& shader,
+ const Ref<VertexArray>& vertex_array,
+ const glm::mat4& transform,
+ const glm::mat4& view_projection) -> void
+ {
+ shader->bind();
+ shader->set_mat4("u_ViewProjection", view_projection);
+ shader->set_mat4("u_Transform", transform);
+
+ vertex_array->bind();
+ RenderCommand::draw_indexed(vertex_array);
+ }
+
+ Scope<RendererAPI> RenderCommand::s_renderer_api = RendererAPI::create();
+
+ auto Renderer::set_clear_color(const glm::vec4& color) -> void
+ {
+ RenderCommand::set_clear_color(color);
+ }
+
+ auto Renderer::clear() -> void
+ {
+ RenderCommand::clear();
+ }
+};
diff --git a/src/rendering/renderer.h b/src/rendering/renderer.h
new file mode 100644
index 0000000..189723d
--- /dev/null
+++ b/src/rendering/renderer.h
@@ -0,0 +1,158 @@
+#pragma once
+
+#include "core/memory.h"
+#include "vertex_array.h"
+#include "shader.h"
+#include "framebuffer.h"
+
+#include <glm/glm.hpp>
+
+namespace Donut
+{
+ class RendererAPI
+ {
+ public:
+ enum class API
+ {
+ None = 0,
+ OpenGL = 1,
+ Vulkan = 2,
+ };
+
+ public:
+ virtual ~RendererAPI() = default;
+
+ virtual auto init() -> void = 0;
+ virtual auto set_viewport(uint32_t x, uint32_t y,
+ uint32_t width, uint32_t height) -> void = 0;
+ virtual auto set_clear_color(const glm::vec4& color) -> void = 0;
+ virtual auto clear() -> void = 0;
+ virtual auto enable_depth_test() -> void = 0;
+ virtual auto disable_depth_test() -> void = 0;
+ virtual auto set_face_culling(bool enabled) -> void = 0;
+ virtual auto enable_blending() -> void = 0;
+ virtual auto disable_blending() -> void = 0;
+
+ virtual auto draw_indexed(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) -> void = 0;
+
+ virtual auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void = 0;
+ virtual auto draw_lines(const Ref<VertexArray>& vertex_array,
+ uint32_t index_count = 0) -> void = 0;
+ virtual auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void = 0;
+ virtual auto bind_image_texture(uint32_t texture_id,
+ uint32_t slot = 0,
+ bool read_only = false) -> void = 0;
+ virtual auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void = 0;
+
+ inline static auto get_api() -> API { return s_api; }
+ inline static auto set_api(API api) -> void { s_api = api; }
+ static auto create() -> Scope<RendererAPI>;
+
+ private:
+ static API s_api;
+ };
+
+ class RenderCommand
+ {
+ public:
+ inline static auto init() -> void
+ {
+ s_renderer_api->init();
+ }
+
+ inline static auto set_viewport(uint32_t x, uint32_t y, uint32_t width, uint32_t height) -> void
+ {
+ s_renderer_api->set_viewport(x, y, width, height);
+ }
+
+ inline static auto set_clear_color(const glm::vec4& color) -> void
+ {
+ s_renderer_api->set_clear_color(color);
+ }
+
+ inline static auto clear() -> void
+ {
+ s_renderer_api->clear();
+ }
+
+ inline static auto enable_depth_test() -> void
+ {
+ s_renderer_api->enable_depth_test();
+ }
+
+ inline static auto disable_depth_test() -> void
+ {
+ s_renderer_api->disable_depth_test();
+ }
+
+ inline static auto set_face_culling(bool enabled) -> void
+ {
+ s_renderer_api->set_face_culling(enabled);
+ }
+
+ inline static auto enable_blending() -> void
+ {
+ s_renderer_api->enable_blending();
+ }
+
+ inline static auto disable_blending() -> void
+ {
+ s_renderer_api->disable_blending();
+ }
+
+ inline static auto draw_indexed(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void
+ {
+ s_renderer_api->draw_indexed(vertex_array, index_count);
+ }
+
+ inline static auto draw_arrays(uint32_t vertex_count, uint32_t first = 0) -> void
+ {
+ s_renderer_api->draw_arrays(vertex_count, first);
+ }
+
+ inline static auto draw_lines(const Ref<VertexArray>& vertex_array, uint32_t index_count = 0) -> void
+ {
+ s_renderer_api->draw_lines(vertex_array, index_count);
+ }
+
+ inline static auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void
+ {
+ s_renderer_api->bind_texture(texture_id, slot);
+ }
+
+ inline static auto bind_image_texture(uint32_t texture_id, uint32_t slot = 0, bool read_only = false) -> void
+ {
+ s_renderer_api->bind_image_texture(texture_id, slot, read_only);
+ }
+
+ inline static auto read_pixels(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
+ uint32_t format, uint32_t type, void* pixels) -> void
+ {
+ s_renderer_api->read_pixels(x, y, width, height, format, type, pixels);
+ }
+
+ private:
+ static Scope<RendererAPI> s_renderer_api;
+ };
+
+ class Renderer
+ {
+ public:
+ static auto init() -> void;
+ static auto shutdown() -> void;
+
+ static auto on_window_resize(uint32_t width, uint32_t height) -> void;
+
+ static auto submit(const Ref<Shader>& shader,
+ const Ref<VertexArray>& vertex_array,
+ const glm::mat4& transform,
+ const glm::mat4& view_projection) -> void;
+
+ static auto set_clear_color(const glm::vec4& color) -> void;
+ static auto clear() -> void;
+
+ inline static auto get_api() -> RendererAPI::API { return RendererAPI::get_api(); }
+ };
+};
diff --git a/src/rendering/shader.cpp b/src/rendering/shader.cpp
new file mode 100644
index 0000000..167b855
--- /dev/null
+++ b/src/rendering/shader.cpp
@@ -0,0 +1,84 @@
+#include "shader.h"
+#include "renderer.h"
+
+#include "platform/opengl/opengl_shader.h"
+#include "platform/vulkan/vulkan_shader.h"
+
+namespace Donut
+{
+ auto Shader::create(const std::string& filepath) -> Shader*
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return new OpenGLShader(filepath);
+ case RendererAPI::API::Vulkan:
+ return new VulkanShader(filepath);
+ default:
+ return nullptr;
+ }
+ }
+
+ auto Shader::create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return new OpenGLShader(name, vertex_src, fragment_src);
+ case RendererAPI::API::Vulkan:
+ return new VulkanShader(name, vertex_src, fragment_src);
+ default:
+ return nullptr;
+ }
+ }
+
+ auto Shader::create_compute(const std::string& name, const std::string& compute_src) -> Shader*
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return new OpenGLShader(name, compute_src);
+ case RendererAPI::API::Vulkan:
+ return new VulkanShader(name, compute_src);
+ default:
+ return nullptr;
+ }
+ }
+
+ auto ShaderLibrary::add(const Ref<Shader>& shader) -> void
+ {
+ auto& name = shader->get_name();
+ add(name, shader);
+ }
+
+ auto ShaderLibrary::add(const std::string& name, const Ref<Shader>& shader) -> void
+ {
+ m_shaders[name] = shader;
+ }
+
+ auto ShaderLibrary::load(const std::string& filepath) -> Ref<Shader>
+ {
+ auto shader = Ref<Shader>(Shader::create(filepath));
+ add(shader);
+ return shader;
+ }
+
+ auto ShaderLibrary::load(const std::string& name, const std::string& filepath) -> Ref<Shader>
+ {
+ auto shader = Ref<Shader>(Shader::create(filepath));
+ add(name, shader);
+ return shader;
+ }
+
+ auto ShaderLibrary::Get(const std::string& name) -> Ref<Shader>
+ {
+ if (exists(name))
+ return m_shaders[name];
+ return nullptr;
+ }
+
+ auto ShaderLibrary::exists(const std::string& name) const -> bool
+ {
+ return m_shaders.find(name) != m_shaders.end();
+ }
+};
diff --git a/src/rendering/shader.h b/src/rendering/shader.h
new file mode 100644
index 0000000..bc3fd9a
--- /dev/null
+++ b/src/rendering/shader.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include "core/memory.h"
+
+#include <string>
+#include <unordered_map>
+#include <glm/glm.hpp>
+
+#define SHADER_STORAGE_BARRIER_BIT 0x00002000
+#define UNIFORM_BARRIER_BIT 0x00000004
+#define TEXTURE_FETCH_BARRIER_BIT 0x00000008
+#define IMAGE_ACCESS_BARRIER_BIT 0x00000020
+
+namespace Donut
+{
+ class Shader
+ {
+ public:
+ virtual ~Shader() = default;
+
+ virtual auto bind() const -> void = 0;
+ virtual auto unbind() const -> void = 0;
+
+ virtual auto set_int( const std::string& name, int value) -> void = 0;
+ virtual auto set_int_array(const std::string& name, int* values, uint32_t count) -> void = 0;
+ virtual auto set_float( const std::string& name, float value) -> void = 0;
+ virtual auto set_float2( const std::string& name, const glm::vec2& value) -> void = 0;
+ virtual auto set_float3( const std::string& name, const glm::vec3& value) -> void = 0;
+ virtual auto set_float4( const std::string& name, const glm::vec4& value) -> void = 0;
+ virtual auto set_mat4( const std::string& name, const glm::mat4& value) -> void = 0;
+
+ virtual auto dispatch(uint32_t x, uint32_t y = 1, uint32_t z = 1) -> void = 0;
+ virtual auto dispatch_indirect(uint32_t offset = 0) -> void = 0;
+ virtual auto memory_barrier(uint32_t barriers) -> void = 0;
+
+ virtual auto get_name() const -> const std::string& = 0;
+ virtual auto get_renderer_id() const -> uint32_t = 0;
+
+ static auto create(const std::string& filepath) -> Shader*;
+ static auto create(const std::string& name, const std::string& vertex_src, const std::string& fragment_src) -> Shader*;
+ static auto create_compute(const std::string& name, const std::string& compute_src) -> Shader*;
+ };
+
+ class ShaderLibrary
+ {
+ public:
+ auto add(const Ref<Shader>& shader) -> void;
+ auto add(const std::string& name, const Ref<Shader>& shader) -> void;
+ auto load(const std::string& filepath) -> Ref<Shader>;
+ auto load(const std::string& name, const std::string& filepath) -> Ref<Shader>;
+
+ Ref<Shader> Get(const std::string& name);
+
+ auto exists(const std::string& name) const -> bool;
+ private:
+ std::unordered_map<std::string, Ref<Shader>> m_shaders;
+ };
+};
diff --git a/src/rendering/texture.cpp b/src/rendering/texture.cpp
new file mode 100644
index 0000000..2470e46
--- /dev/null
+++ b/src/rendering/texture.cpp
@@ -0,0 +1,68 @@
+#include "texture.h"
+#include "renderer.h"
+
+#include "platform/opengl/opengl_texture.h"
+#include "platform/vulkan/vulkan_texture.h"
+
+namespace Donut
+{
+ auto Texture2D::create(uint32_t width, uint32_t height) -> Ref<Texture2D>
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return create_ref<OpenGLTexture2D>(width, height);
+ case RendererAPI::API::Vulkan:
+ return create_ref<VulkanTexture2D>(width, height);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+
+ auto Texture2D::create(const std::string& path) -> Ref<Texture2D>
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return create_ref<OpenGLTexture2D>(path);
+ case RendererAPI::API::Vulkan:
+ return create_ref<VulkanTexture2D>(path);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+
+ auto CubemapTexture::create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return create_ref<OpenGLCubemapTexture>(width, height);
+ case RendererAPI::API::Vulkan:
+ return create_ref<VulkanCubemapTexture>(width, height);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+
+ auto CubemapTexture::create_from_hdri(const std::string& path) -> Ref<CubemapTexture>
+ {
+ switch (Renderer::get_api())
+ {
+ case RendererAPI::API::OpenGL:
+ return create_ref<OpenGLCubemapTexture>(path);
+ case RendererAPI::API::Vulkan:
+ return create_ref<VulkanCubemapTexture>(path);
+ case RendererAPI::API::None:
+ return nullptr;
+ default:
+ return nullptr;
+ }
+ }
+}; \ No newline at end of file
diff --git a/src/rendering/texture.h b/src/rendering/texture.h
new file mode 100644
index 0000000..e9b3e0a
--- /dev/null
+++ b/src/rendering/texture.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "core/memory.h"
+
+#include <string>
+#include <glm/glm.hpp>
+
+namespace Donut
+{
+ class Texture
+ {
+ public:
+ virtual ~Texture() = default;
+
+ virtual auto get_width() const -> uint32_t = 0;
+ virtual auto get_height() const -> uint32_t = 0;
+ virtual auto get_renderer_id() const -> uint32_t = 0;
+
+ virtual auto set_data(void* data, uint32_t size) -> void = 0;
+ virtual auto bind(uint32_t slot = 0) const -> void = 0;
+ virtual void bind_as_image(uint32_t slot = 0,
+ bool read_only = false) const = 0;
+
+ virtual bool operator==(const Texture& other) const = 0;
+ };
+
+ class Texture2D
+ : public Texture
+ {
+ public:
+ static auto create(uint32_t width, uint32_t height) -> Ref<Texture2D>;
+ static auto create(const std::string& path) -> Ref<Texture2D>;
+ };
+
+ class CubemapTexture
+ : public Texture
+ {
+ public:
+ static auto create(uint32_t width, uint32_t height) -> Ref<CubemapTexture>;
+ static auto create_from_hdri(const std::string& path) -> Ref<CubemapTexture>;
+ };
+}; \ No newline at end of file
diff --git a/src/rendering/texture_manager.cpp b/src/rendering/texture_manager.cpp
new file mode 100644
index 0000000..ad2c68b
--- /dev/null
+++ b/src/rendering/texture_manager.cpp
@@ -0,0 +1,34 @@
+#include "texture_manager.h"
+#include "platform/opengl/opengl_texture.h"
+#include "platform/opengl/opengl_renderer_api.h"
+#include <glad/glad.h>
+
+namespace Donut
+{
+ auto TextureManager::create_texture(uint32_t width, uint32_t height) -> Ref<Texture2D>
+ {
+ return create_ref<OpenGLTexture2D>(width, height);
+ }
+
+ auto TextureManager::bind_texture(uint32_t texture_id, uint32_t slot) -> void
+ {
+ RenderCommand::bind_texture(texture_id, slot);
+ }
+
+ auto TextureManager::bind_image_texture(uint32_t texture_id, uint32_t slot, bool read_only) -> void
+ {
+ RenderCommand::bind_image_texture(texture_id, slot, read_only);
+ }
+
+ auto TextureManager::set_texture_data(uint32_t texture_id, void* data, uint32_t width, uint32_t height) -> void
+ {
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ }
+
+ auto TextureManager::resize_texture(uint32_t texture_id, uint32_t width, uint32_t height) -> void
+ {
+ glBindTexture(GL_TEXTURE_2D, texture_id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ }
+};
diff --git a/src/rendering/texture_manager.h b/src/rendering/texture_manager.h
new file mode 100644
index 0000000..02c32b1
--- /dev/null
+++ b/src/rendering/texture_manager.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "core/memory.h"
+#include "rendering/texture.h"
+#include <cstdint>
+
+namespace Donut
+{
+ class TextureManager
+ {
+ public:
+ static auto create_texture(uint32_t width, uint32_t height) -> Ref<Texture2D>;
+
+ static auto bind_texture(uint32_t texture_id, uint32_t slot = 0) -> void;
+ static auto bind_image_texture(uint32_t texture_id, uint32_t slot = 0, bool read_only = false) -> void;
+ static auto set_texture_data(uint32_t texture_id, void* data, uint32_t width, uint32_t height) -> void;
+ static auto resize_texture(uint32_t texture_id, uint32_t width, uint32_t height) -> void;
+ };
+};
diff --git a/src/rendering/uniform_buffer.cpp b/src/rendering/uniform_buffer.cpp
new file mode 100644
index 0000000..c344aec
--- /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
+{
+ auto UniformBuffer::create(uint32_t size, uint32_t binding) -> Ref<UniformBuffer>
+ {
+ 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..eb28e53
--- /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 auto set_data(const void* data, uint32_t size, uint32_t offset = 0) -> void = 0;
+ virtual auto bind(uint32_t binding) -> void = 0;
+
+ static auto create(uint32_t size, uint32_t binding) -> Ref<UniformBuffer>;
+ };
+};
diff --git a/src/Rendering/VertexArray.cpp b/src/rendering/vertex_array.cpp
index 366675f..8045707 100644
--- a/src/Rendering/VertexArray.cpp
+++ b/src/rendering/vertex_array.cpp
@@ -1,12 +1,12 @@
-#include "VertexArray.h"
-#include "Renderer.h"
+#include "vertex_array.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLVertexArray.h"
-#include "Platform/Vulkan/VulkanVertexArray.h"
+#include "platform/opengl/opengl_vertex_array.h"
+#include "platform/vulkan/vulkan_vertex_array.h"
namespace Donut
{
- uint32_t VertexBufferElement::GetSizeOfType(uint32_t type)
+ auto VertexBufferElement::get_size_of_type(uint32_t type) -> uint32_t
{
switch (type)
{
@@ -17,9 +17,9 @@ namespace Donut
}
}
- VertexArray* VertexArray::Create()
+ auto VertexArray::create() -> VertexArray*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
return new OpenGLVertexArray();
diff --git a/src/rendering/vertex_array.h b/src/rendering/vertex_array.h
new file mode 100644
index 0000000..921df56
--- /dev/null
+++ b/src/rendering/vertex_array.h
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "core/memory.h"
+
+#include "vertex_buffer.h"
+#include "index_buffer.h"
+
+#include <vector>
+
+namespace Donut
+{
+ class VertexArray
+ {
+ public:
+ virtual ~VertexArray() = default;
+
+ virtual auto bind() const -> void = 0;
+ virtual auto unbind() const -> void = 0;
+
+ virtual auto add_vertex_buffer(const Ref<VertexBuffer>& vertex_buffer) -> void = 0;
+ virtual auto set_index_buffer(const Ref<IndexBuffer>& index_buffer) -> void = 0;
+
+ virtual auto get_vertex_buffers() const -> const std::vector<Ref<VertexBuffer>>& = 0;
+ virtual auto get_index_buffer() const -> const Ref<IndexBuffer>& = 0;
+
+ static auto create() -> VertexArray*;
+ };
+};
diff --git a/src/Rendering/VertexBuffer.cpp b/src/rendering/vertex_buffer.cpp
index a7e2504..354d0cd 100644
--- a/src/Rendering/VertexBuffer.cpp
+++ b/src/rendering/vertex_buffer.cpp
@@ -1,14 +1,14 @@
-#include "VertexBuffer.h"
-#include "Renderer.h"
+#include "vertex_buffer.h"
+#include "renderer.h"
-#include "Platform/OpenGL/OpenGLVertexBuffer.h"
-#include "Platform/Vulkan/VulkanVertexBuffer.h"
+#include "platform/opengl/opengl_vertex_buffer.h"
+#include "platform/vulkan/vulkan_vertex_buffer.h"
namespace Donut
{
- VertexBuffer* VertexBuffer::Create(const void* data, uint32_t size)
+ auto VertexBuffer::create(const void* data, uint32_t size) -> VertexBuffer*
{
- switch (Renderer::GetAPI())
+ switch (Renderer::get_api())
{
case RendererAPI::API::OpenGL:
return new OpenGLVertexBuffer(data, size);
diff --git a/src/rendering/vertex_buffer.h b/src/rendering/vertex_buffer.h
new file mode 100644
index 0000000..6be7e34
--- /dev/null
+++ b/src/rendering/vertex_buffer.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <cstdint>
+#include <vector>
+
+namespace Donut
+{
+ struct VertexBufferElement
+ {
+ uint32_t type;
+ uint32_t count;
+ uint8_t normalized;
+ uint32_t offset;
+
+ static auto get_size_of_type(uint32_t type) -> uint32_t;
+ };
+
+ class VertexBufferLayout
+ {
+ public:
+ VertexBufferLayout() = default;
+ ~VertexBufferLayout() = default;
+
+ template<typename T>
+ auto push(uint32_t count) -> void
+ {
+ static_assert(false);
+ }
+
+ template<>
+ void push<float>(uint32_t count)
+ {
+ m_elements.push_back({ 0x1406, count, 0, m_stride }); // GL_FLOAT
+ m_stride += count * VertexBufferElement::get_size_of_type(0x1406);
+ }
+
+ template<>
+ void push<uint32_t>(uint32_t count)
+ {
+ m_elements.push_back({ 0x1405, count, 0, m_stride }); // GL_UNSIGNED_INT
+ m_stride += count * VertexBufferElement::get_size_of_type(0x1405);
+ }
+
+ template<>
+ void push<uint8_t>(uint32_t count)
+ {
+ m_elements.push_back({ 0x1401, count, 1, m_stride }); // GL_UNSIGNED_BYTE
+ m_stride += count * VertexBufferElement::get_size_of_type(0x1401);
+ }
+
+ inline auto get_elements() const -> const std::vector<VertexBufferElement>& { return m_elements; }
+ inline auto get_stride() const -> uint32_t { return m_stride; }
+
+ private:
+ std::vector<VertexBufferElement> m_elements;
+ uint32_t m_stride = 0;
+ };
+
+ class VertexBuffer
+ {
+ public:
+ virtual ~VertexBuffer() = default;
+
+ virtual auto bind() const -> void = 0;
+ virtual auto unbind() const -> void = 0;
+ virtual auto set_data(const void* data, uint32_t size) -> void = 0;
+
+ virtual auto get_layout() const -> const VertexBufferLayout& = 0;
+ virtual auto set_layout(const VertexBufferLayout& layout) -> void = 0;
+
+ static auto create(const void* data, uint32_t size) -> VertexBuffer*;
+ };
+};
diff --git a/src/states/config_state.cpp b/src/states/config_state.cpp
new file mode 100644
index 0000000..d5fca87
--- /dev/null
+++ b/src/states/config_state.cpp
@@ -0,0 +1,307 @@
+#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
new file mode 100644
index 0000000..e64889b
--- /dev/null
+++ b/src/states/config_state.h
@@ -0,0 +1,47 @@
+#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; // 0=Dark, 1=Light, 2=Blue
+ };
+}; \ No newline at end of file
diff --git a/src/states/simulation_state.cpp b/src/states/simulation_state.cpp
new file mode 100644
index 0000000..06955ff
--- /dev/null
+++ b/src/states/simulation_state.cpp
@@ -0,0 +1,386 @@
+#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
new file mode 100644
index 0000000..625d19e
--- /dev/null
+++ b/src/states/simulation_state.h
@@ -0,0 +1,27 @@
+#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
new file mode 100644
index 0000000..09283c3
--- /dev/null
+++ b/src/states/world_builder_state.cpp
@@ -0,0 +1,1113 @@
+#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;
+
+ // Set default grid size
+ 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 rayStart_NDC(ndc_x, ndc_y, -1.0f, 1.0f);
+ glm::vec4 rayEnd_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 rayStart_World = inv_vp * rayStart_NDC;
+ glm::vec4 rayEnd_World = inv_vp * rayEnd_NDC;
+
+ rayStart_World /= rayStart_World.w;
+ rayEnd_World /= rayEnd_World.w;
+
+ glm::vec3 ray_dir = glm::normalize(glm::vec3(rayEnd_World - rayStart_World));
+ glm::vec3 ray_origin = glm::vec3(rayStart_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);
+
+ // Clean up state to prevent conflicts with scene rendering
+ m_grid_vao->unbind();
+ m_grid_shader->unbind();
+ RenderCommand::disable_blending();
+
+ // Ensure depth test is still enabled for scene rendering
+ RenderCommand::enable_depth_test();
+ }
+};
diff --git a/src/states/world_builder_state.h b/src/states/world_builder_state.h
new file mode 100644
index 0000000..8b4678a
--- /dev/null
+++ b/src/states/world_builder_state.h
@@ -0,0 +1,96 @@
+#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;
+ };
+};
diff --git a/tools/compile-shaders.sh b/tools/compile-shaders.sh
new file mode 100755
index 0000000..1f58c2a
--- /dev/null
+++ b/tools/compile-shaders.sh
@@ -0,0 +1,110 @@
+#!/usr/bin/env bash
+#
+# Compiles every assets/shaders/*.slang to all backend targets, into
+# assets/shaders/generated/ (gitignored):
+#
+# OpenGL : <name>.glsl - combined "#type" GLSL 4.10 (Slang -> SPIR-V ->
+# SPIRV-Cross), macOS-4.1-core valid, loaded by
+# OpenGLShader.
+# Vulkan : <name>.<entry>.spv - SPIR-V per entry point.
+# Metal : <name>.metal - MSL, whole module.
+#
+# Entry points are auto-discovered from Slang [shader("stage")] attributes.
+# Override tool paths with SLANGC / SPIRV_CROSS.
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+SLANGC="${SLANGC:-$ROOT/tools/slang/bin/slangc}"
+SPIRV_CROSS="${SPIRV_CROSS:-$(command -v spirv-cross || echo spirv-cross)}"
+SRC="$ROOT/assets/Shaders"
+OUT="$SRC/generated"
+
+[ -x "$SLANGC" ] || { echo "compile-shaders: slangc not found at $SLANGC (run tools/fetch-slang.sh)"; exit 1; }
+command -v "$SPIRV_CROSS" >/dev/null 2>&1 || { echo "compile-shaders: spirv-cross not found (brew install spirv-cross)"; exit 1; }
+
+mkdir -p "$OUT"
+shopt -s nullglob
+shaders=("$SRC"/*.slang)
+[ ${#shaders[@]} -gt 0 ] || { echo "compile-shaders: no .slang files in $SRC yet"; exit 0; }
+
+# Prints "stage entryName" for each [shader("stage")] entry point in a file.
+entry_points() {
+ awk '
+ /\[shader\(/ { if (match($0, /"[a-zA-Z]+"/)) { stage = substr($0, RSTART+1, RLENGTH-2); want = 1 } next }
+ want && $0 ~ /^[ \t]*\[/ { next } # skip further attributes
+ want && /\(/ {
+ sig = $0; sub(/\(.*/, "", sig)
+ n = split(sig, t, /[ \t*&]+/); name = t[n]
+ if (name != "") print stage, name
+ want = 0
+ }
+ ' "$1"
+}
+
+fail=0
+for f in "${shaders[@]}"; do
+ name="$(basename "$f" .slang)"
+ echo "== $name.slang =="
+
+ # Metal: whole module.
+ if "$SLANGC" "$f" -target metal -o "$OUT/$name.metal" 2>"$OUT/.err"; then
+ echo " metal -> $name.metal"
+ else echo " metal FAILED:"; sed 's/^/ /' "$OUT/.err"; fail=1; fi
+
+ # OpenGL + Vulkan: per entry point. Shaders that declare a ConstantBuffer keep
+ # real UBO blocks (the Engine binds them by name); the rest have Slang's
+ # $Globals flattened to loose uniforms so per-draw glUniform* calls still work.
+ if grep -q 'ConstantBuffer<' "$f"; then ubo=1; else ubo=0; fi
+ combined="$OUT/$name.glsl"; : > "$combined"
+ while read -r stage entry; do
+ [ -z "${stage:-}" ] && continue
+ spv="$OUT/$name.$entry.spv"
+
+ if ! "$SLANGC" "$f" -target spirv -entry "$entry" -stage "$stage" -o "$spv" 2>"$OUT/.err"; then
+ echo " spirv FAILED ($entry):"; sed 's/^/ /' "$OUT/.err"; fail=1; continue
+ fi
+ echo " spirv -> $name.$entry.spv ($stage)"
+
+ # SPIR-V -> classic GLSL 4.10 (combined samplers, no binding layouts).
+ xcopts="--version 410 --no-es --no-420pack-extension"
+ [ "$ubo" = 0 ] && xcopts="$xcopts --glsl-emit-ubo-as-plain-uniforms"
+
+ if "$SPIRV_CROSS" $xcopts "$spv" --output "$OUT/.stage.glsl" 2>"$OUT/.err"; then
+ if [ "$ubo" = 1 ]; then
+ # Real UBOs: strip SPIRV-Cross's "_std140" block-name suffix so the block
+ # names match glGetUniformBlockIndex("Camera") etc. in the Engine.
+ perl -i -pe 's/_std140//g' "$OUT/.stage.glsl"
+ else
+ # Flatten "struct S {..}; uniform S inst;" into loose "uniform TYPE NAME;"
+ # and strip the "inst." prefix, so uniforms keep their original names and
+ # the existing C++ SetMat4("u_X") / SetFloat("u_Y") calls work unchanged.
+ perl -0777 -i -pe '
+ while (/struct\s+(\w+)\s*\{(.*?)\}\s*;\s*uniform\s+\1\s+(\w+)\s*;/s) {
+ my ($s,$body,$inst)=($1,$2,$3); my @u;
+ while ($body =~ /([A-Za-z_]\w*)\s+([A-Za-z_]\w*(?:\[\d+\])?)\s*;/g) { push @u,"uniform $1 $2;"; }
+ my $loose=join("\n",@u);
+ s/struct\s+\Q$s\E\s*\{.*?\}\s*;\s*uniform\s+\Q$s\E\s+\Q$inst\E\s*;/$loose/s;
+ s/\b\Q$inst\E\.//g;
+ }
+ ' "$OUT/.stage.glsl"
+ fi
+ # SPIRV-Cross names inter-stage varyings per stage and relies on explicit
+ # locations, but macOS's GL linker matches varyings by NAME. Rename each
+ # varying to a location-canonical name (_v<loc>) so stages agree: rename
+ # `out` in vertex stages and `in` in fragment stages (never attributes).
+ case "$stage" in vertex) vdir=out ;; fragment) vdir=in ;; *) vdir="" ;; esac
+ if [ -n "$vdir" ]; then
+ DIR="$vdir" perl -0777 -i -pe '
+ my $d=$ENV{DIR}; my %m;
+ while (/layout\(location = (\d+)\)\s+\Q$d\E\s+\w+\s+(\w+)\s*;/g) { $m{$2}="_v$1"; }
+ for my $n (keys %m) { s/\b\Q$n\E\b/$m{$n}/g; }
+ ' "$OUT/.stage.glsl"
+ fi
+ { echo "#type $stage"; cat "$OUT/.stage.glsl"; echo; } >> "$combined"
+ echo " glsl -> $name.glsl (#type $stage)"
+ else echo " glsl FAILED ($entry):"; sed 's/^/ /' "$OUT/.err"; fail=1; fi
+ done < <(entry_points "$f")
+done
+
+rm -f "$OUT/.err" "$OUT/.stage.glsl"
+exit $fail
diff --git a/tools/fetch-slang.sh b/tools/fetch-slang.sh
new file mode 100755
index 0000000..5229bf7
--- /dev/null
+++ b/tools/fetch-slang.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+#
+# Fetches the Slang shader-compiler toolchain into tools/slang (gitignored).
+# Slang is the single shader-authoring language for Donut; slangc cross-compiles
+# each assets/shaders/*.slang to GLSL (OpenGL), SPIR-V (Vulkan), and MSL (Metal).
+#
+# Re-run to (re)install. Pin the version here so builds are reproducible.
+set -euo pipefail
+
+VERSION="2026.14.1"
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+DEST="$ROOT/tools/slang"
+
+case "$(uname -s)-$(uname -m)" in
+ Darwin-arm64|Darwin-aarch64) ASSET="macos-aarch64" ;;
+ Darwin-x86_64) ASSET="macos-x86_64" ;;
+ Linux-x86_64) ASSET="linux-x86_64" ;;
+ Linux-aarch64|Linux-arm64) ASSET="linux-aarch64" ;;
+ *) echo "fetch-slang: unsupported platform $(uname -s)-$(uname -m)"; exit 1 ;;
+esac
+
+URL="https://github.com/shader-slang/slang/releases/download/v${VERSION}/slang-${VERSION}-${ASSET}.tar.gz"
+
+echo "fetch-slang: downloading Slang ${VERSION} (${ASSET})..."
+rm -rf "$DEST"
+mkdir -p "$DEST"
+curl -fsSL "$URL" | tar -xz -C "$DEST"
+
+"$DEST/bin/slangc" -v >/dev/null 2>&1 && echo "fetch-slang: installed to $DEST (slangc $("$DEST/bin/slangc" -v))"