diff options
| author | hachem <im@hachem.wtf> | 2026-08-20 02:27:02 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-08-20 02:40:07 +0200 |
| commit | 5b361d81dbd2af0d0e99b9eb1adebea76ff05db0 (patch) | |
| tree | 34db1df17e8c7248aeeb92156d635d31c2484e3f /src | |
| parent | e3abaaf59777258ba06705135a0cafcb5918ad12 (diff) | |
[chore]: MASSIVE refactor + more vulkan bs
Diffstat (limited to 'src')
147 files changed, 9455 insertions, 6156 deletions
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 = ⅈ 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, ©); - 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, ©); + 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 = ⅈ + write.dstSet = present_set; write.dstBinding = 0; write.descriptorCount = 1; write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write.pImageInfo = ⅈ 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; + }; +}; |
