From 1a56c9e99cba00ceb20ac96149e7a4ab95d76fd2 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 9 Aug 2026 13:18:04 -0400 Subject: [PATCH] Added a debug controller Made a default template for cameras. Added delta to mouse state so we can check delta for debug Camera. --- Game/Controller/ControllerUtils.h | 6 + Game/Controller/DebugCameraController.cpp | 177 ++++++++++++++++++ Game/Controller/DebugCameraController.h | 12 ++ Game/game.cpp | 19 ++ .../include/Core/Application/IApplication.h | 9 +- Juliet/include/Core/HAL/Mouse/Mouse.h | 7 +- Juliet/include/Graphics/Camera.h | 8 +- Juliet/src/Core/HAL/Event/Keyboard.cpp | 2 +- Juliet/src/Core/HAL/Event/Mouse.cpp | 16 ++ Juliet/src/Core/HAL/Event/Mouse_Private.h | 4 + Juliet/src/Core/HAL/Event/SystemEvent.cpp | 2 + Juliet/src/Engine/Engine.cpp | 7 +- Juliet/src/Graphics/Camera.cpp | 13 ++ JulietApp/main.cpp | 8 +- JulietApp/main.h | 4 +- tools/build_system.c | 7 +- 16 files changed, 275 insertions(+), 26 deletions(-) create mode 100644 Game/Controller/ControllerUtils.h create mode 100644 Game/Controller/DebugCameraController.cpp create mode 100644 Game/Controller/DebugCameraController.h diff --git a/Game/Controller/ControllerUtils.h b/Game/Controller/ControllerUtils.h new file mode 100644 index 0000000..d0318d0 --- /dev/null +++ b/Game/Controller/ControllerUtils.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +constexpr index_t kPlayCamera = 0; +constexpr index_t kDebugCamera = 1; diff --git a/Game/Controller/DebugCameraController.cpp b/Game/Controller/DebugCameraController.cpp new file mode 100644 index 0000000..82f0c08 --- /dev/null +++ b/Game/Controller/DebugCameraController.cpp @@ -0,0 +1,177 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + + bool gIsDebugCameraActive = false; + index_t gPreviousCameraIndex = 0; + + float gPitch = 0.0f; + float gYaw = 0.0f; + bool gFirstUpdate = true; + bool gIsFpsModeActive = false; + bool gWasRightMouseButtonDown = false; +} // namespace + +void ActivateDebugController() +{ + Assert(gIsDebugCameraActive == false); + + Juliet::Camera* currentCam = Juliet::GetCurrentCamera(); + gPreviousCameraIndex = currentCam->Index; + + Juliet::SetCurrentCamera(kDebugCamera); + + gIsDebugCameraActive = true; + gFirstUpdate = true; + gIsFpsModeActive = false; + gWasRightMouseButtonDown = false; +} + +void DeactivateDebugController() +{ + Assert(gIsDebugCameraActive); + + gIsDebugCameraActive = false; + + Juliet::SetCurrentCamera(gPreviousCameraIndex); +} + +bool IsDebugControllerActive() +{ + return gIsDebugCameraActive; +} + +void UpdateDebugController(float dt) +{ + Juliet::Camera* currentCam = Juliet::GetCurrentCamera(); + + if (gFirstUpdate) + { + Juliet::Vector3 dir = Juliet::Vector3{ 0.0f, 0.0f, 0.0f } - currentCam->Position; + dir = Juliet::Normalize(dir); + gPitch = asinf(dir.z); + gYaw = atan2f(dir.y, dir.x); + gFirstUpdate = false; + + Juliet::Vector3 forward; + forward.x = cosf(gPitch) * cosf(gYaw); + forward.y = cosf(gPitch) * sinf(gYaw); + forward.z = sinf(gPitch); + Juliet::Vector3 right = Juliet::Normalize(Juliet::Cross(Juliet::Vector3{ 0.0f, 0.0f, 1.0f }, forward)); + currentCam->Target = currentCam->Position + forward; + currentCam->Up = Juliet::Cross(forward, right); + } + + bool isRightMouseButtonDown = Juliet::IsMouseButtonDown(Juliet::MouseButton::Right); + if (isRightMouseButtonDown && !gWasRightMouseButtonDown) + { + gIsFpsModeActive = !gIsFpsModeActive; + } + gWasRightMouseButtonDown = isRightMouseButtonDown; + + if (!gIsFpsModeActive) + { + return; + } + + Juliet::MousePosition mouseDelta = Juliet::GetMouseDelta(); + + float sensitivity = 0.005f; + gYaw += mouseDelta.X * sensitivity; + gPitch -= mouseDelta.Y * sensitivity; + + if (gPitch > 1.5f) + { + gPitch = 1.5f; + } + if (gPitch < -1.5f) + { + gPitch = -1.5f; + } + + if (Juliet::IsKeyDown(Juliet::ScanCode::Q)) + { + gYaw -= 2.0f * dt; + } + if (Juliet::IsKeyDown(Juliet::ScanCode::E)) + { + gYaw += 2.0f * dt; + } + + Juliet::Vector3 forward; + forward.x = cosf(gPitch) * cosf(gYaw); + forward.y = cosf(gPitch) * sinf(gYaw); + forward.z = sinf(gPitch); + + Juliet::Vector3 right = Juliet::Normalize(Juliet::Cross(Juliet::Vector3{ 0.0f, 0.0f, 1.0f }, forward)); + Juliet::Vector3 defaultUp = Juliet::Cross(forward, right); + + static const float kMovementPerFrame = 10.f; // 10m/s + + float speedPerFrame = kMovementPerFrame; + if (Juliet::IsKeyDown(Juliet::ScanCode::LeftShift)) + { + speedPerFrame *= 10.f; // 100m/s + } + + if (Juliet::IsKeyDown(Juliet::ScanCode::W)) + { + currentCam->Position = currentCam->Position + forward * (speedPerFrame * dt); + } + if (Juliet::IsKeyDown(Juliet::ScanCode::S)) + { + currentCam->Position = currentCam->Position - forward * (speedPerFrame * dt); + } + if (Juliet::IsKeyDown(Juliet::ScanCode::D)) + { + currentCam->Position = currentCam->Position + right * (speedPerFrame * dt); + } + if (Juliet::IsKeyDown(Juliet::ScanCode::A)) + { + currentCam->Position = currentCam->Position - right * (speedPerFrame * dt); + } + if (Juliet::IsKeyDown(Juliet::ScanCode::Space)) + { + currentCam->Position = currentCam->Position + defaultUp * (speedPerFrame * dt); + } + if (Juliet::IsKeyDown(Juliet::ScanCode::LeftControl)) + { + currentCam->Position = currentCam->Position - defaultUp * (speedPerFrame * dt); + } + + currentCam->Target = currentCam->Position + forward; + currentCam->Up = defaultUp; +} + +#if JULIET_DEBUG +void RenderImGuiDebugController(float dt) +{ + Juliet::Camera* currentCam = Juliet::GetCurrentCamera(); + + ImGui::Text("Delta time: %f", dt); + + ImGui::Text("Camera:"); + ImGui::Indent(); + ImGui::BulletText("Index: %zu", (size_t)currentCam->Index); + ImGui::BulletText("Position: (%.3f, %.3f, %.3f)", currentCam->Position.x, currentCam->Position.y, + currentCam->Position.z); + ImGui::BulletText("Target: (%.3f, %.3f, %.3f)", currentCam->Target.x, currentCam->Target.y, currentCam->Target.z); + ImGui::BulletText("Up: (%.3f, %.3f, %.3f)", currentCam->Up.x, currentCam->Up.y, currentCam->Up.z); + ImGui::BulletText("FOV: %.3f rad", currentCam->FOV); + ImGui::BulletText("AspectRatio: %.3f", currentCam->AspectRatio); + ImGui::BulletText("NearPlane: %.3f", currentCam->NearPlane); + ImGui::BulletText("FarPlane: %.3f", currentCam->FarPlane); + ImGui::BulletText("Pitch: %.3f, Yaw: %.3f", gPitch, gYaw); + ImGui::BulletText("FPS Mode: %s", gIsFpsModeActive ? "Active" : "Inactive"); + ImGui::Unindent(); +} +#endif diff --git a/Game/Controller/DebugCameraController.h b/Game/Controller/DebugCameraController.h new file mode 100644 index 0000000..de7d6b2 --- /dev/null +++ b/Game/Controller/DebugCameraController.h @@ -0,0 +1,12 @@ +#pragma once + +// TODO : Should support Debug/Profile/Release properly. +// I think debug profile should have this controller but not release +void ActivateDebugController(); +void DeactivateDebugController(); +bool IsDebugControllerActive(); +void UpdateDebugController(float dt); + +#if JULIET_DEBUG +void RenderImGuiDebugController(float dt); +#endif diff --git a/Game/game.cpp b/Game/game.cpp index 7fbd809..a10456e 100644 --- a/Game/game.cpp +++ b/Game/game.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -95,6 +96,7 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_ printf("Rock has %d health points\n", rock->Health); } + GameMode previousFrameMode = gGameState->Mode; if (IsKeyPressed(ScanCode::F1)) { gGameState->Mode = static_cast((ToUnderlying(gGameState->Mode) + 1) % 2); @@ -107,7 +109,24 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_ if (gGameState->Mode == GameMode::Debug) { + if (previousFrameMode != gGameState->Mode) + { + ActivateDebugController(); + } + + UpdateDebugController(deltaTime); + +#if JULIET_DEBUG ImGui::Begin("Debug"); + RenderImGuiDebugController(deltaTime); ImGui::End(); +#endif + } + else + { + if (previousFrameMode != gGameState->Mode) + { + DeactivateDebugController(); + } } } diff --git a/Juliet/include/Core/Application/IApplication.h b/Juliet/include/Core/Application/IApplication.h index 9b85e6a..83acc0b 100644 --- a/Juliet/include/Core/Application/IApplication.h +++ b/Juliet/include/Core/Application/IApplication.h @@ -4,6 +4,7 @@ namespace Juliet { + struct Camera; struct RenderPass; struct CommandList; struct Texture; @@ -25,9 +26,9 @@ namespace Juliet virtual struct GraphicsDevice* GetGraphicsDevice() = 0; // Render Lifecycle (Engine-Managed Render Loop) - virtual void OnPreRender(CommandList* cmd) = 0; - virtual void OnRender(RenderPass* pass, CommandList* cmd) = 0; - virtual ColorTargetInfo GetColorTargetInfo(Texture* swapchainTexture) = 0; - virtual DepthStencilTargetInfo* GetDepthTargetInfo() = 0; + virtual void OnPreRender(CommandList* cmd) = 0; + virtual void OnRender(RenderPass* pass, CommandList* cmd, const Camera& camera) = 0; + virtual ColorTargetInfo GetColorTargetInfo(Texture* swapchainTexture) = 0; + virtual DepthStencilTargetInfo* GetDepthTargetInfo() = 0; }; } // namespace Juliet diff --git a/Juliet/include/Core/HAL/Mouse/Mouse.h b/Juliet/include/Core/HAL/Mouse/Mouse.h index e0ec93a..9bd0f78 100644 --- a/Juliet/include/Core/HAL/Mouse/Mouse.h +++ b/Juliet/include/Core/HAL/Mouse/Mouse.h @@ -21,7 +21,8 @@ namespace Juliet float Y; }; - extern bool IsMouseButtonDown(MouseButton button); - extern MousePosition GetMousePosition(); - extern MouseButton GetMouseButtonState(); + JULIET_API extern bool IsMouseButtonDown(MouseButton button); + JULIET_API extern MousePosition GetMousePosition(); + JULIET_API extern MousePosition GetMouseDelta(); + JULIET_API extern MouseButton GetMouseButtonState(); } // namespace Juliet diff --git a/Juliet/include/Graphics/Camera.h b/Juliet/include/Graphics/Camera.h index eacf0ed..0040636 100644 --- a/Juliet/include/Graphics/Camera.h +++ b/Juliet/include/Graphics/Camera.h @@ -32,9 +32,7 @@ namespace Juliet return Camera_GetProjectionMatrix(cam) * Camera_GetViewMatrix(cam); } - JULIET_API extern void ReserveCamera(size_t amount); - extern Camera* GetCurrentCamera(); - extern void SetCurrentCamera(index_t index); - extern index_t AddCamera(); - extern void RemoveCamera(index_t index); + JULIET_API extern void ReserveCamera(size_t amount); + JULIET_API extern Camera* GetCurrentCamera(); + JULIET_API extern void SetCurrentCamera(index_t index); } // namespace Juliet diff --git a/Juliet/src/Core/HAL/Event/Keyboard.cpp b/Juliet/src/Core/HAL/Event/Keyboard.cpp index 5447677..1745b77 100644 --- a/Juliet/src/Core/HAL/Event/Keyboard.cpp +++ b/Juliet/src/Core/HAL/Event/Keyboard.cpp @@ -99,7 +99,7 @@ namespace Juliet evt.Type = type; evt.Data.Keyboard.AssociatedKeyboardID = kGlobalKeyboardID; evt.Data.Keyboard.Key = key; - evt.Data.Keyboard.KeyState = { keyPosition }; + evt.Data.Keyboard.KeyState = { keyPosition, 0.0f }; evt.Data.Keyboard.KeyModeState = keyboardState.KeyModState; bool evtPosted = AddEvent(evt); diff --git a/Juliet/src/Core/HAL/Event/Mouse.cpp b/Juliet/src/Core/HAL/Event/Mouse.cpp index 822f6cb..f34d905 100644 --- a/Juliet/src/Core/HAL/Event/Mouse.cpp +++ b/Juliet/src/Core/HAL/Event/Mouse.cpp @@ -55,6 +55,9 @@ namespace Juliet mouseState.X_Previous = x; mouseState.Y_Previous = y; + mouseState.DeltaX += xDisplacement; + mouseState.DeltaY += yDisplacement; + SystemEvent evt; evt.Type = EventType::Mouse_Move; evt.Timestamp = timestamp; @@ -133,10 +136,23 @@ namespace Juliet return { .X = mouseState.X, .Y = mouseState.Y }; } + MousePosition GetMouseDelta() + { + auto& mouseState = GetMouseState(); + return { .X = mouseState.DeltaX, .Y = mouseState.DeltaY }; + } + MouseButton GetMouseButtonState() { const auto& mouseState = GetMouseState(); return mouseState.ButtonState; } + void UpdateMouseState() + { + auto& mouseState = GetMouseState(); + mouseState.DeltaX = 0.0f; + mouseState.DeltaY = 0.0f; + } + } // namespace Juliet diff --git a/Juliet/src/Core/HAL/Event/Mouse_Private.h b/Juliet/src/Core/HAL/Event/Mouse_Private.h index a7e70e9..c5ab485 100644 --- a/Juliet/src/Core/HAL/Event/Mouse_Private.h +++ b/Juliet/src/Core/HAL/Event/Mouse_Private.h @@ -14,6 +14,9 @@ namespace Juliet float X_Previous; float Y_Previous; + float DeltaX; + float DeltaY; + MouseButton ButtonState; bool HasPosition : 1; @@ -21,6 +24,7 @@ namespace Juliet Mouse& GetMouseState(); + extern void UpdateMouseState(); extern void SendMouseMotion(uint64 timestamp, NonNullPtr window, MouseID ID, float x, float y); extern void SendMouseButton(uint64 timestamp, NonNullPtr window, MouseID mouseID, MouseButton button, bool pressed); diff --git a/Juliet/src/Core/HAL/Event/SystemEvent.cpp b/Juliet/src/Core/HAL/Event/SystemEvent.cpp index 5d65146..1096979 100644 --- a/Juliet/src/Core/HAL/Event/SystemEvent.cpp +++ b/Juliet/src/Core/HAL/Event/SystemEvent.cpp @@ -2,6 +2,7 @@ #include #include +#include #pragma push_macro("global") #undef global @@ -93,6 +94,7 @@ namespace Juliet void Events_NewFrame(float deltaTime) { UpdateKeyboardstate(deltaTime); + UpdateMouseState(); } } // namespace Juliet diff --git a/Juliet/src/Engine/Engine.cpp b/Juliet/src/Engine/Engine.cpp index b7d5ef6..6a42122 100644 --- a/Juliet/src/Engine/Engine.cpp +++ b/Juliet/src/Engine/Engine.cpp @@ -132,12 +132,13 @@ namespace Juliet RenderPass* pass = BeginRenderPass(cmdList, colorInfo, depthInfo); + Camera camera = *GetCurrentCamera(); + // Application rendering - EngineInstance.Application->OnRender(pass, cmdList); + EngineInstance.Application->OnRender(pass, cmdList, camera); // Debug display flush (inside render pass) - Camera debugCamera = *GetCurrentCamera(); - DebugDisplay_Flush(cmdList, pass, debugCamera); + DebugDisplay_Flush(cmdList, pass, camera); // Note: The MeshRenderer and SkyboxRenderer draw calls are still inside Application->OnRender // They shouldn't be moved here directly without an interface since they require PushData. diff --git a/Juliet/src/Graphics/Camera.cpp b/Juliet/src/Graphics/Camera.cpp index f75cff3..5dc3c35 100644 --- a/Juliet/src/Graphics/Camera.cpp +++ b/Juliet/src/Graphics/Camera.cpp @@ -11,6 +11,18 @@ namespace Juliet { namespace { + + Camera kDefaultCameraTemplate = { + .Index = 0, + .Position = { 25.0f, 0.0f, 12.5f }, + .Target = { cosf(0.f) * cosf(0.f), sinf(0.f) * cosf(0.f), sinf(0.f) }, + .Up = { 0.0f, 0.0f, 1.0f }, + .FOV = 1.047f, + .AspectRatio = 1200.0f / 800.0f, + .NearPlane = 0.1f, + .FarPlane = 1000.0f, + }; // namespace + size_t CameraAmount = 0; Camera* CameraArray = nullptr; Camera* CurrentCamera = nullptr; @@ -28,6 +40,7 @@ namespace Juliet for (index_t index = 0; index < CameraAmount; ++index) { Camera* cam = CameraArray + index; + *cam = kDefaultCameraTemplate; cam->Index = index; } diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 54672a7..80aa849 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -228,7 +228,6 @@ void JulietApplication::Shutdown() void JulietApplication::Update(float deltaTime) { - CameraTime += deltaTime; static float fpsTimer = 0.0f; @@ -488,7 +487,7 @@ void JulietApplication::Update(float deltaTime) DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true); DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 0.5f, { 1.0f, 1.0f, 0.0f, 1.0f }, true); - Game.Update(&Data, 0.0f); + Game.Update(&Data, deltaTime); if (ShouldReloadCode(GameCode)) { @@ -540,11 +539,10 @@ void JulietApplication::Update(float deltaTime) void JulietApplication::OnPreRender(CommandList* /*cmd*/) {} -void JulietApplication::OnRender(RenderPass* pass, CommandList* cmd) +void JulietApplication::OnRender(RenderPass* pass, CommandList* cmd, const Camera& camera) { - Camera cam = {}; PushData pushData = {}; - pushData.ViewProjection = Camera_GetViewProjectionMatrix(cam); + pushData.ViewProjection = Camera_GetViewProjectionMatrix(camera); #if 0 if (enableGlobalLight) diff --git a/JulietApp/main.h b/JulietApp/main.h index 930b211..bc68eea 100644 --- a/JulietApp/main.h +++ b/JulietApp/main.h @@ -27,8 +27,8 @@ class JulietApplication : public Juliet::IApplication Juliet::GraphicsDevice* GetGraphicsDevice() override { return GraphicsDevice; } // Render Lifecycle - void OnPreRender(Juliet::CommandList* cmd) override; - void OnRender(Juliet::RenderPass* pass, Juliet::CommandList* cmd) override; + void OnPreRender(Juliet::CommandList* cmd) override; + void OnRender(Juliet::RenderPass* pass, Juliet::CommandList* cmd, const Juliet::Camera& camera) override; Juliet::ColorTargetInfo GetColorTargetInfo(Juliet::Texture* swapchainTexture) override; Juliet::DepthStencilTargetInfo* GetDepthTargetInfo() override; diff --git a/tools/build_system.c b/tools/build_system.c index c853a90..a3e2494 100644 --- a/tools/build_system.c +++ b/tools/build_system.c @@ -512,7 +512,7 @@ static void AppendTiming(const char* stepName, double secs) g_TimingsLen += sprintf_s(g_TimingsBuf + g_TimingsLen, sizeof(g_TimingsBuf) - g_TimingsLen, "%s|%.3fs\n", stepName, secs); } -static int ExecuteIncrementalCompile(const char* srcDir, const char* objDir, const char* baseCmd) +static int ExecuteIncrementalCompile(const char* srcDir, const char* objDir, const char* baseCmd, int64_t maxDepTime) { EnsureDirectoryExists(objDir); @@ -581,7 +581,8 @@ static int ExecuteIncrementalCompile(const char* srcDir, const char* objDir, con if (!FileExists(objPath)) { dirty = true; } else { - if (GetFileModTimeInt64(srcPath) > GetFileModTimeInt64(objPath)) { + int64_t objTime = GetFileModTimeInt64(objPath); + if (GetFileModTimeInt64(srcPath) > objTime || maxDepTime > objTime) { dirty = true; } } @@ -870,7 +871,7 @@ static int CommandRunPlan(int argc, char* argv[]) { LARGE_INTEGER tStart, tEnd; QueryPerformanceCounter(&tStart); - int res = ExecuteIncrementalCompile(step->IncSrc, step->IncObj, step->Command); + int res = ExecuteIncrementalCompile(step->IncSrc, step->IncObj, step->Command, maxTime); QueryPerformanceCounter(&tEnd); AppendTiming(step->StepName, (double)(tEnd.QuadPart - tStart.QuadPart) / (double)perfFreq.QuadPart); if (res != 0) {