Added a debug controller

Made a default template for cameras.
Added delta to mouse state so we can check delta for debug Camera.
This commit is contained in:
2026-08-09 13:18:04 -04:00
parent ae790f5a9e
commit 1a56c9e99c
16 changed files with 275 additions and 26 deletions
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include <Core/Common/CoreTypes.h>
constexpr index_t kPlayCamera = 0;
constexpr index_t kDebugCamera = 1;
+177
View File
@@ -0,0 +1,177 @@
#include <Controller/DebugCameraController.h>
#include <Controller/ControllerUtils.h>
#include <Core/Common/CoreUtils.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/HAL/Mouse/Mouse.h>
#include <Graphics/Camera.h>
#include <imgui.h>
#include <math.h>
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
+12
View File
@@ -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
+19
View File
@@ -5,6 +5,7 @@
#include <game.h> #include <game.h>
#include <Controller/DebugCameraController.h>
#include <Core/Common/EnumUtils.h> #include <Core/Common/EnumUtils.h>
#include <Core/HAL/Filesystem/Filesystem.h> #include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/Keyboard/Keyboard.h> #include <Core/HAL/Keyboard/Keyboard.h>
@@ -95,6 +96,7 @@ extern "C" JULIET_API void __cdecl GameUpdate(Juliet::GameData* params, [[maybe_
printf("Rock has %d health points\n", rock->Health); printf("Rock has %d health points\n", rock->Health);
} }
GameMode previousFrameMode = gGameState->Mode;
if (IsKeyPressed(ScanCode::F1)) if (IsKeyPressed(ScanCode::F1))
{ {
gGameState->Mode = static_cast<GameMode>((ToUnderlying(gGameState->Mode) + 1) % 2); gGameState->Mode = static_cast<GameMode>((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 (gGameState->Mode == GameMode::Debug)
{ {
if (previousFrameMode != gGameState->Mode)
{
ActivateDebugController();
}
UpdateDebugController(deltaTime);
#if JULIET_DEBUG
ImGui::Begin("Debug"); ImGui::Begin("Debug");
RenderImGuiDebugController(deltaTime);
ImGui::End(); ImGui::End();
#endif
}
else
{
if (previousFrameMode != gGameState->Mode)
{
DeactivateDebugController();
}
} }
} }
@@ -4,6 +4,7 @@
namespace Juliet namespace Juliet
{ {
struct Camera;
struct RenderPass; struct RenderPass;
struct CommandList; struct CommandList;
struct Texture; struct Texture;
@@ -25,9 +26,9 @@ namespace Juliet
virtual struct GraphicsDevice* GetGraphicsDevice() = 0; virtual struct GraphicsDevice* GetGraphicsDevice() = 0;
// Render Lifecycle (Engine-Managed Render Loop) // Render Lifecycle (Engine-Managed Render Loop)
virtual void OnPreRender(CommandList* cmd) = 0; virtual void OnPreRender(CommandList* cmd) = 0;
virtual void OnRender(RenderPass* pass, CommandList* cmd) = 0; virtual void OnRender(RenderPass* pass, CommandList* cmd, const Camera& camera) = 0;
virtual ColorTargetInfo GetColorTargetInfo(Texture* swapchainTexture) = 0; virtual ColorTargetInfo GetColorTargetInfo(Texture* swapchainTexture) = 0;
virtual DepthStencilTargetInfo* GetDepthTargetInfo() = 0; virtual DepthStencilTargetInfo* GetDepthTargetInfo() = 0;
}; };
} // namespace Juliet } // namespace Juliet
+4 -3
View File
@@ -21,7 +21,8 @@ namespace Juliet
float Y; float Y;
}; };
extern bool IsMouseButtonDown(MouseButton button); JULIET_API extern bool IsMouseButtonDown(MouseButton button);
extern MousePosition GetMousePosition(); JULIET_API extern MousePosition GetMousePosition();
extern MouseButton GetMouseButtonState(); JULIET_API extern MousePosition GetMouseDelta();
JULIET_API extern MouseButton GetMouseButtonState();
} // namespace Juliet } // namespace Juliet
+3 -5
View File
@@ -32,9 +32,7 @@ namespace Juliet
return Camera_GetProjectionMatrix(cam) * Camera_GetViewMatrix(cam); return Camera_GetProjectionMatrix(cam) * Camera_GetViewMatrix(cam);
} }
JULIET_API extern void ReserveCamera(size_t amount); JULIET_API extern void ReserveCamera(size_t amount);
extern Camera* GetCurrentCamera(); JULIET_API extern Camera* GetCurrentCamera();
extern void SetCurrentCamera(index_t index); JULIET_API extern void SetCurrentCamera(index_t index);
extern index_t AddCamera();
extern void RemoveCamera(index_t index);
} // namespace Juliet } // namespace Juliet
+1 -1
View File
@@ -99,7 +99,7 @@ namespace Juliet
evt.Type = type; evt.Type = type;
evt.Data.Keyboard.AssociatedKeyboardID = kGlobalKeyboardID; evt.Data.Keyboard.AssociatedKeyboardID = kGlobalKeyboardID;
evt.Data.Keyboard.Key = key; evt.Data.Keyboard.Key = key;
evt.Data.Keyboard.KeyState = { keyPosition }; evt.Data.Keyboard.KeyState = { keyPosition, 0.0f };
evt.Data.Keyboard.KeyModeState = keyboardState.KeyModState; evt.Data.Keyboard.KeyModeState = keyboardState.KeyModState;
bool evtPosted = AddEvent(evt); bool evtPosted = AddEvent(evt);
+16
View File
@@ -55,6 +55,9 @@ namespace Juliet
mouseState.X_Previous = x; mouseState.X_Previous = x;
mouseState.Y_Previous = y; mouseState.Y_Previous = y;
mouseState.DeltaX += xDisplacement;
mouseState.DeltaY += yDisplacement;
SystemEvent evt; SystemEvent evt;
evt.Type = EventType::Mouse_Move; evt.Type = EventType::Mouse_Move;
evt.Timestamp = timestamp; evt.Timestamp = timestamp;
@@ -133,10 +136,23 @@ namespace Juliet
return { .X = mouseState.X, .Y = mouseState.Y }; return { .X = mouseState.X, .Y = mouseState.Y };
} }
MousePosition GetMouseDelta()
{
auto& mouseState = GetMouseState();
return { .X = mouseState.DeltaX, .Y = mouseState.DeltaY };
}
MouseButton GetMouseButtonState() MouseButton GetMouseButtonState()
{ {
const auto& mouseState = GetMouseState(); const auto& mouseState = GetMouseState();
return mouseState.ButtonState; return mouseState.ButtonState;
} }
void UpdateMouseState()
{
auto& mouseState = GetMouseState();
mouseState.DeltaX = 0.0f;
mouseState.DeltaY = 0.0f;
}
} // namespace Juliet } // namespace Juliet
@@ -14,6 +14,9 @@ namespace Juliet
float X_Previous; float X_Previous;
float Y_Previous; float Y_Previous;
float DeltaX;
float DeltaY;
MouseButton ButtonState; MouseButton ButtonState;
bool HasPosition : 1; bool HasPosition : 1;
@@ -21,6 +24,7 @@ namespace Juliet
Mouse& GetMouseState(); Mouse& GetMouseState();
extern void UpdateMouseState();
extern void SendMouseMotion(uint64 timestamp, NonNullPtr<Window> window, MouseID ID, float x, float y); extern void SendMouseMotion(uint64 timestamp, NonNullPtr<Window> window, MouseID ID, float x, float y);
extern void SendMouseButton(uint64 timestamp, NonNullPtr<Window> window, MouseID mouseID, MouseButton button, bool pressed); extern void SendMouseButton(uint64 timestamp, NonNullPtr<Window> window, MouseID mouseID, MouseButton button, bool pressed);
@@ -2,6 +2,7 @@
#include <Core/HAL/Event/SystemEvent.h> #include <Core/HAL/Event/SystemEvent.h>
#include <Core/HAL/Event/Keyboard_Private.h> #include <Core/HAL/Event/Keyboard_Private.h>
#include <Core/HAL/Event/Mouse_Private.h>
#pragma push_macro("global") #pragma push_macro("global")
#undef global #undef global
@@ -93,6 +94,7 @@ namespace Juliet
void Events_NewFrame(float deltaTime) void Events_NewFrame(float deltaTime)
{ {
UpdateKeyboardstate(deltaTime); UpdateKeyboardstate(deltaTime);
UpdateMouseState();
} }
} // namespace Juliet } // namespace Juliet
+4 -3
View File
@@ -132,12 +132,13 @@ namespace Juliet
RenderPass* pass = BeginRenderPass(cmdList, colorInfo, depthInfo); RenderPass* pass = BeginRenderPass(cmdList, colorInfo, depthInfo);
Camera camera = *GetCurrentCamera();
// Application rendering // Application rendering
EngineInstance.Application->OnRender(pass, cmdList); EngineInstance.Application->OnRender(pass, cmdList, camera);
// Debug display flush (inside render pass) // Debug display flush (inside render pass)
Camera debugCamera = *GetCurrentCamera(); DebugDisplay_Flush(cmdList, pass, camera);
DebugDisplay_Flush(cmdList, pass, debugCamera);
// Note: The MeshRenderer and SkyboxRenderer draw calls are still inside Application->OnRender // 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. // They shouldn't be moved here directly without an interface since they require PushData.
+13
View File
@@ -11,6 +11,18 @@ namespace Juliet
{ {
namespace 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; size_t CameraAmount = 0;
Camera* CameraArray = nullptr; Camera* CameraArray = nullptr;
Camera* CurrentCamera = nullptr; Camera* CurrentCamera = nullptr;
@@ -28,6 +40,7 @@ namespace Juliet
for (index_t index = 0; index < CameraAmount; ++index) for (index_t index = 0; index < CameraAmount; ++index)
{ {
Camera* cam = CameraArray + index; Camera* cam = CameraArray + index;
*cam = kDefaultCameraTemplate;
cam->Index = index; cam->Index = index;
} }
+3 -5
View File
@@ -228,7 +228,6 @@ void JulietApplication::Shutdown()
void JulietApplication::Update(float deltaTime) void JulietApplication::Update(float deltaTime)
{ {
CameraTime += deltaTime; CameraTime += deltaTime;
static float fpsTimer = 0.0f; 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_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); 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)) if (ShouldReloadCode(GameCode))
{ {
@@ -540,11 +539,10 @@ void JulietApplication::Update(float deltaTime)
void JulietApplication::OnPreRender(CommandList* /*cmd*/) {} 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 pushData = {};
pushData.ViewProjection = Camera_GetViewProjectionMatrix(cam); pushData.ViewProjection = Camera_GetViewProjectionMatrix(camera);
#if 0 #if 0
if (enableGlobalLight) if (enableGlobalLight)
+2 -2
View File
@@ -27,8 +27,8 @@ class JulietApplication : public Juliet::IApplication
Juliet::GraphicsDevice* GetGraphicsDevice() override { return GraphicsDevice; } Juliet::GraphicsDevice* GetGraphicsDevice() override { return GraphicsDevice; }
// Render Lifecycle // Render Lifecycle
void OnPreRender(Juliet::CommandList* cmd) override; void OnPreRender(Juliet::CommandList* cmd) override;
void OnRender(Juliet::RenderPass* pass, 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::ColorTargetInfo GetColorTargetInfo(Juliet::Texture* swapchainTexture) override;
Juliet::DepthStencilTargetInfo* GetDepthTargetInfo() override; Juliet::DepthStencilTargetInfo* GetDepthTargetInfo() override;
+4 -3
View File
@@ -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); 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); EnsureDirectoryExists(objDir);
@@ -581,7 +581,8 @@ static int ExecuteIncrementalCompile(const char* srcDir, const char* objDir, con
if (!FileExists(objPath)) { if (!FileExists(objPath)) {
dirty = true; dirty = true;
} else { } else {
if (GetFileModTimeInt64(srcPath) > GetFileModTimeInt64(objPath)) { int64_t objTime = GetFileModTimeInt64(objPath);
if (GetFileModTimeInt64(srcPath) > objTime || maxDepTime > objTime) {
dirty = true; dirty = true;
} }
} }
@@ -870,7 +871,7 @@ static int CommandRunPlan(int argc, char* argv[])
{ {
LARGE_INTEGER tStart, tEnd; LARGE_INTEGER tStart, tEnd;
QueryPerformanceCounter(&tStart); QueryPerformanceCounter(&tStart);
int res = ExecuteIncrementalCompile(step->IncSrc, step->IncObj, step->Command); int res = ExecuteIncrementalCompile(step->IncSrc, step->IncObj, step->Command, maxTime);
QueryPerformanceCounter(&tEnd); QueryPerformanceCounter(&tEnd);
AppendTiming(step->StepName, (double)(tEnd.QuadPart - tStart.QuadPart) / (double)perfFreq.QuadPart); AppendTiming(step->StepName, (double)(tEnd.QuadPart - tStart.QuadPart) / (double)perfFreq.QuadPart);
if (res != 0) { if (res != 0) {