Zen 0.3.0
Loading...
Searching...
No Matches
ZEN_CameraController.cpp
1#include "zen/log/ZEN_Log.h"
2#include <zen/camera/ZEN_CameraController.h>
3
4namespace Zen {
5 CameraController::CameraController(Camera &camera) : m_camera(camera) {
6 m_aspectRatio = m_camera.aspectRatio();
7 }
8
9 void CameraController::onUpdate(DeltaTime deltaTime) {
10 float step = m_camera.speed() * (float)deltaTime;
11 if (Input::isKeyHeld(Key::W)) {
12 m_cameraPosition.y += step;
13 }
14 if (Input::isKeyHeld(Key::S)) {
15 m_cameraPosition.y -= step;
16 }
17 if (Input::isKeyHeld(Key::D)) {
18 m_cameraPosition.x += step;
19 }
20 if (Input::isKeyHeld(Key::A)) {
21 m_cameraPosition.x -= step;
22 }
23 if (m_worldBoundsEnabled) {
24 clampCameraToWorld();
25 }
26
27 m_camera.setPosition(m_cameraPosition);
28 }
29
30 bool CameraController::onEvent(const ZenEvent &event) {
31 if (event.header.type == EventType::MouseScrolled) {
32 return this->onMouseScrolled(event);
33 }
34 if (event.header.type == EventType::WindowResize) {
35 return this->onWindowResize(event);
36 }
37 return false;
38 }
39
40 bool CameraController::onMouseScrolled(const ZenEvent &event) {
41 if (!m_worldBoundsEnabled) {
42 m_aspectRatio = m_camera.aspectRatio();
43 m_zoomLevel -= (float)event.mouseWheel.scrl_height * 0.25f;
44 m_zoomLevel = std::max(m_zoomLevel, 0.25f);
45 m_camera.setOrthographic(-m_aspectRatio * m_zoomLevel,
46 m_aspectRatio * m_zoomLevel,
47 -m_zoomLevel,
48 m_zoomLevel);
49 } else {
50 m_zoomLevel -= (float)event.mouseWheel.scrl_height * 0.25f;
51 m_zoomLevel = std::max(m_zoomLevel, 0.25f);
52 updateCameraProjection();
53 clampCameraToWorld();
54 m_camera.setPosition(m_cameraPosition);
55 }
56
57 return false;
58 }
59
60 bool CameraController::onWindowResize(const ZenEvent &event) {
61 m_aspectRatio = (float)event.windowResize.width / (float)event.windowResize.height;
62
63 if (!m_worldBoundsEnabled) {
64 m_camera.setOrthographic(-m_aspectRatio * m_zoomLevel,
65 m_aspectRatio * m_zoomLevel,
66 -m_zoomLevel,
67 m_zoomLevel);
68 } else {
69 updateCameraProjection();
70 clampCameraToWorld();
71 m_camera.setPosition(m_cameraPosition);
72 }
73
74 ZEN_LOG_TRACE("aspect ratio: {}", m_aspectRatio);
75 return false;
76 }
77
78 void CameraController::setWorldBounds(float left, float right, float bottom, float top) {
79 m_worldLeft = left;
80 m_worldRight = right;
81 m_worldBottom = bottom;
82 m_worldTop = top;
83
84 m_worldWidth = m_worldRight - m_worldLeft;
85 m_worldHeight = m_worldTop - m_worldBottom;
86 m_worldAspectRatio = m_worldWidth / m_worldHeight;
87
88 if (m_worldBoundsEnabled) {
89 updateCameraProjection();
90 clampCameraToWorld();
91 m_camera.setPosition(m_cameraPosition);
92 }
93 }
94
95 void CameraController::enableWorldBounds(bool enabled) {
96 m_worldBoundsEnabled = enabled;
97 if (enabled) {
98 updateCameraProjection();
99 clampCameraToWorld();
100 m_camera.setPosition(m_cameraPosition);
101 }
102 }
103
104 void CameraController::getViewDimensions(float &outWidth, float &outHeight) const {
105 // the world has a fixed size: m_worldWidth x m_worldHeight
106 // use this to adjust the view to match the world while respecting zoom and aspect ratio
107
108 float baseWidth = m_worldWidth / m_zoomLevel;
109 float baseHeight = m_worldHeight / m_zoomLevel;
110
111 float zoomedWorldAspect = baseWidth / baseHeight;
112
113 if (m_aspectRatio > zoomedWorldAspect) {
114 // window is WIDER than the zoomed world
115 // lock width to world, crop height
116 outWidth = baseWidth;
117 outHeight = baseWidth / m_aspectRatio;
118 } else {
119 // window is TALLER/EQUAL to the zoomed world
120 // lock height to world, crop width
121 outHeight = baseHeight;
122 outWidth = baseHeight * m_aspectRatio;
123 }
124 }
125
126 void CameraController::updateCameraProjection() {
127 float viewWidth;
128 float viewHeight;
129 getViewDimensions(viewWidth, viewHeight);
130
131 float halfWidth = viewWidth / 2.0f;
132 float halfHeight = viewHeight / 2.0f;
133
134 m_camera.setOrthographic(-halfWidth, halfWidth, -halfHeight, halfHeight);
135
136 ZEN_LOG_TRACE("View size: {}x{}, Zoom: {}, Aspect: {}",
137 viewWidth,
138 viewHeight,
139 m_zoomLevel,
140 m_aspectRatio);
141 }
142
143 void CameraController::clampCameraToWorld() {
144 const float minZoomOut = 1.0f;
145 float oldZoom = m_zoomLevel;
146 m_zoomLevel = std::max(m_zoomLevel, minZoomOut);
147 if (m_zoomLevel != oldZoom) {
148 updateCameraProjection();
149 }
150 float viewWidth;
151 float viewHeight;
152 getViewDimensions(viewWidth, viewHeight);
153
154 float halfWidth = viewWidth / 2.0f;
155 float halfHeight = viewHeight / 2.0f;
156
157 // calculate world center
158 float worldCenterX = (m_worldLeft + m_worldRight) / 2.0f;
159 float worldCenterY = (m_worldBottom + m_worldTop) / 2.0f;
160
161 // clamp camera position so view edges don't exceed world bounds
162 // x axis clamping
163 if (viewWidth >= m_worldWidth) {
164 // view is wider than world, center camera on world x
165 m_cameraPosition.x = worldCenterX;
166 } else {
167 // view is smaller, clamp position so edges stay within world
168 float minX = m_worldLeft + halfWidth;
169 float maxX = m_worldRight - halfWidth;
170 m_cameraPosition.x = std::clamp(m_cameraPosition.x, minX, maxX);
171 }
172
173 // x axis clamping
174 if (viewHeight >= m_worldHeight) {
175 // view is taller than world, center camera on world y
176 m_cameraPosition.y = worldCenterY;
177 } else {
178 // view is smaller, clamp position so edges stay within world
179 float minY = m_worldBottom + halfHeight;
180 float maxY = m_worldTop - halfHeight;
181 m_cameraPosition.y = std::clamp(m_cameraPosition.y, minY, maxY);
182 }
183
184 ZEN_LOG_TRACE("Camera pos: ({}, {}), View: {}x{}",
185 m_cameraPosition.x,
186 m_cameraPosition.y,
187 viewWidth,
188 viewHeight);
189 }
190
191} // namespace Zen