SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
FreeLookCameraControllerComponent.cpp
Go to the documentation of this file.
4#include <Core/Window.hpp>
5#include <SDL3/SDL.h>
6#include <Events/Event.hpp>
7#include <Math/Math.hpp>
8#include <Math/Quaternion.hpp>
9
10namespace Sleak {
11
13 : CameraController(object) {
14
15 speed = 5.0f;
16 sensitivity = 0.01f;
17 acceleration = 13.5f;
18 damping = 8.0f;
19 maxSpeed = 10.0f;
20
21 YawRange = Math::Vector2D(-360, 360);
22 PitchRange = Math::Vector2D(-89, 89);
23 RollRange = Math::Vector2D(-89, 89);
24
26 velocity = Math::Vector3D::Zero();
27
30 }
31
36
39 return false;
40
41 if (camera) {
42 Math::Vector3D forward = camera->GetDirection();
43 yaw = atan2(forward.GetX(), forward.GetZ());
44 pitch = -asin(forward.GetY());
45 pitch = Math::Clamp(pitch,
46 static_cast<float>(PitchRange.GetX() * D2R),
47 static_cast<float>(PitchRange.GetY() * D2R));
48 }
49
50 return true;
51 }
52
53 void FreeLookCameraController::Update(float deltaTime) {
54 if (!bIsInitialized || !isEnabled) return;
55
56 UpdateInput(deltaTime);
57 UpdateCamera(deltaTime);
58 }
59
61 bool cursor_set = enabled ? SDL_ShowCursor() : SDL_HideCursor();
62
63 if(!cursor_set) {
64 SLEAK_ERROR("Failed to set cursor visibility {}", SDL_GetError());
65 }
66 }
67
68 void FreeLookCameraController::UpdateInput(float deltaTime) {
69 // TODO: Make a class to handle keyboard LATER
70 // TODO: Make a class to handle mouse inputs LATER
71
72 float x, y;
73 SDL_GetRelativeMouseState(&x, &y);
74
75 if (m_firstFrame) {
76 m_firstFrame = false;
77 return;
78 }
79
80 // Smooth mouse movement using lerp
81 MousePosition = Math::Lerp(MousePosition, Math::Vector2D(x, y), 0.2f);
82
83 // Apply mouse sensitivity
85 pitch += MousePosition.GetY() * sensitivity * (isInvertY ? -1.0f : 1.0f);
86
87 // Clamp pitch to prevent gimbal lock
88 pitch = Math::Clamp(pitch, static_cast<float>(PitchRange.GetX() * D2R), static_cast<float>(PitchRange.GetY() * D2R));
89 }
90
91 void FreeLookCameraController::UpdateCamera(float deltaTime) {
92 if(!camera)
93 return;
94
95 // Calculate rotation using clamped pitch and yaw
96 Math::Quaternion yawRotation = Math::Quaternion(Math::Vector3D::Up(), yaw);
97 Math::Quaternion pitchRotation = Math::Quaternion(Math::Vector3D::Right(), pitch);
98 Math::Quaternion combinedRotation = yawRotation * pitchRotation;
99
100 // Calculate camera axes
101 Math::Vector3D forward = combinedRotation * Math::Vector3D::Forward();
102 Math::Vector3D up = Math::Vector3D::Up();
103 Math::Vector3D right = forward.Cross(up).Normalized();
104
105 // Calculate acceleration based on input
106 Math::Vector3D targetVelocity(
107 translationInput.GetX() * speed, // Right/Left
108 translationInput.GetY() * speed, // Up/Down
109 translationInput.GetZ() * speed // Forward/Backward
110 );
111
112 // Smooth velocity interpolation
113 velocity = Math::Lerp(velocity, targetVelocity, deltaTime * acceleration);
114
115 // Project velocity onto camera's local axes
116 Math::Vector3D worldVelocity =
117 (right * velocity.GetX()) +
118 (up * velocity.GetY()) +
119 (forward * velocity.GetZ());
120
121 // Clamp total velocity magnitude
122 if (worldVelocity.Magnitude() > maxSpeed) {
123 worldVelocity = worldVelocity.Normalized() * maxSpeed;
124 }
125
126 // Apply damping more consistently
127 velocity = velocity * (1.0f - damping * deltaTime);
128
129 // Cancel velocity component along collision normal for smooth sliding
130 auto* rb = owner->GetComponent<RigidbodyComponent>();
131 if (rb && rb->HadCollision()) {
132 Math::Vector3D normal = rb->GetLastCollisionNormal();
133 float dot = worldVelocity.Dot(normal);
134 if (dot < 0.0f) {
135 worldVelocity = worldVelocity - normal * dot;
136 }
137 }
138
139 // Update camera position (collision handled by ColliderComponent + RigidbodyComponent)
140 camera->AddPosition(worldVelocity * deltaTime);
141
142 // Update look target
143 Math::Vector3D lookTarget = camera->GetPosition() + forward;
144 camera->SetLookTarget(lookTarget);
145 }
146
148 switch (e.GetKeyCode()) {
149 case Input::KEY_CODE::KEY__W: translationInput.SetZ(1.0f); break;
150 case Input::KEY_CODE::KEY__S: translationInput.SetZ(-1.0f); break;
151 case Input::KEY_CODE::KEY__A: translationInput.SetX(+1.0f); break;
152 case Input::KEY_CODE::KEY__D: translationInput.SetX(-1.0f); break;
153 case Input::KEY_CODE::KEY__SPACE: translationInput.SetY(1.0f); break;
154 case Input::KEY_CODE::KEY__LSHIFT: translationInput.SetY(-1.0f); break;
155 case Input::KEY_CODE::KEY__LCTRL: if(!e.IsRepeat()) speed *= 2; break;
156 }
157 }
158
170
171 void FreeLookCameraController::ApplyDamping(float deltaTime)
172 {
173 if (translationInput.Magnitude() == 0) {
174 velocity = velocity * (1.0f - damping * deltaTime);
175 if (velocity.Magnitude() < 0.01f) {
176 velocity = Math::Vector3D::Zero();
177 }
178 }
179 }
180
181
182 void FreeLookCameraController::ClampVelocity() {
183 if(velocity.Magnitude() > maxSpeed) {
184 velocity = velocity.Normalize() * maxSpeed;
185 }
186 }
187
190
191 if(!camera)
192 return;
193
194 auto* app = Application::GetInstance();
195 if (app) app->GetWindow().SetRelativeMouseMode(enabled);
196
197 ToggleCursor(!enabled);
198
199 if (enabled) {
200 // Reset velocity and input when enabling
201 velocity = Math::Vector3D::Zero();
203 m_firstFrame = true;
204
205 // Reset yaw and pitch to match the current camera direction
206 Math::Vector3D forward = camera->GetDirection();
207 yaw = atan2(forward.GetX(), forward.GetZ());
208 pitch = -asin(forward.GetY());
209
210 // Clamp pitch to avoid gimbal lock
211 pitch = Math::Clamp(pitch, static_cast<float>(PitchRange.GetX() * D2R), static_cast<float>(PitchRange.GetY() * D2R));
212 }
213 }
214
215}
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
#define D2R
Definition Math.hpp:8
static Application * GetInstance()
The one Application for this process, or null before construction.
virtual void SetEnabled(bool enabled)
CameraController(GameObject *object)
virtual bool Initialize()
One-time setup, called after the component is attached and the owner is initialized.
GameObject * owner
Definition Component.hpp:81
static void UnregisterEvent(EventType type, std::string id)
Removes the single handler with matching id from type's handler list.
Definition Event.hpp:163
static std::string RegisterEventHandler(T *instance, void(T::*memberFunction)(const EventT &))
Registers a member-function handler bound to instance, returning an ID for later unregistration.
Definition Event.hpp:149
bool Initialize() override
Derives initial yaw/pitch from the camera's current facing direction.
void ToggleCursor(bool enable) override
Shows or hides the OS cursor and, typically, toggles relative mouse mode.
void SetEnabled(bool enabled) override
Toggles relative mouse mode and, when re-enabling, resets velocity and re-syncs yaw/pitch.
virtual void OnKeyPressed(const Sleak::Events::Input::KeyPressedEvent &e)
Updates translation input state from a key-down event; doubles speed while LCTRL is held.
virtual void OnKeyReleased(const Sleak::Events::Input::KeyReleasedEvent &e)
Clears translation input state from a key-up event.
float GetX() const
Definition Vector.hpp:216
float GetY() const
Definition Vector.hpp:217
static Vector3D Right()
Definition Vector.hpp:499
float GetY() const
Definition Vector.hpp:361
float GetX() const
Definition Vector.hpp:360
float GetZ() const
Definition Vector.hpp:362
static Vector3D Forward()
Definition Vector.hpp:501
static Vector3D Zero()
Definition Vector.hpp:495
float Magnitude() const
Definition Vector.hpp:430
static Vector3D Up()
Definition Vector.hpp:497
constexpr T Lerp(const T &start, const T &end, float t)
Linear interpolation from start to end; t is clamped to [0,1].
Definition Math.hpp:37
constexpr const T & Clamp(const T &value, T min, T max)
Clamps value into [min, max].
Definition Math.hpp:15
Root namespace for everything the engine exposes.
Definition Camera.hpp:10