Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
BoardCameraController.cpp
Go to the documentation of this file.
1#include "Display.hpp"
3
4#include <algorithm>
5#include <array>
6#include <cmath>
7
9{
11 {
13 return state.desired2DView==display::Screen2D::Main &&
14 state.viewportInUse==display::Viewport3D::Main &&
15 state.desiredBoardCamera==overview && state.currentBoardCamera==overview &&
16 !state.manualMouseCamLock && !state.manualCameraRequested &&
17 !state.mouseLeftPressed && !state.mouseRightPressed &&
18 state.bssmCameraState==0 && !state.diceCameraControlActive &&
19 !state.desiredCameraInvalidatedLock && !state.floatingCameraActive &&
20 !state.demoModeDesired && state.worldCamera==preset(overview);
21 }
22
24 {
26 return state.desired2DView==display::Screen2D::Main &&
27 state.viewportInUse==display::Viewport3D::Main &&
28 state.desiredBoardCamera==overview && state.currentBoardCamera==overview &&
29 !state.manualMouseCamLock && !state.manualCameraRequested &&
30 !state.mouseLeftPressed && !state.mouseRightPressed &&
31 state.bssmCameraState==0 && !state.diceCameraControlActive &&
32 !state.desiredCameraInvalidatedLock && !state.floatingCameraActive &&
33 !state.demoModeDesired && state.worldCamera==preset(overview);
34 }
35
36 namespace
37 {
38 constexpr float Fov45 = 0.7853981633974483F;
39 constexpr std::array<engine::World3DCamera, 39> Presets{{
40 engine::World3DCamera{{{242.9F, 1200.0F, 243.0F}}, {0.0045F, -0.99999F, 0.0F}, {1.0F, 0.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
41 engine::World3DCamera{{{-420.0F, 605.0F, 243.0F}}, {0.70710678F, -0.70710678F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
42 engine::World3DCamera{{{-370.0F, 645.0F, -370.0F}}, {0.54167522F, -0.64278761F, 0.54167522F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
43 engine::World3DCamera{{{-139.0F, 78.0F, 124.5F}}, {0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
44 engine::World3DCamera{{{-139.0F, 78.0F, 243.0F}}, {0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
45 engine::World3DCamera{{{-139.0F, 78.0F, 361.5F}}, {0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
46 engine::World3DCamera{{{124.5F, 78.0F, 625.0F}}, {0.0F, -0.35836795F, -0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
47 engine::World3DCamera{{{243.0F, 78.0F, 625.0F}}, {0.0F, -0.35836795F, -0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
48 engine::World3DCamera{{{361.5F, 78.0F, 625.0F}}, {0.0F, -0.35836795F, -0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
49 engine::World3DCamera{{{625.0F, 78.0F, 361.5F}}, {-0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
50 engine::World3DCamera{{{625.0F, 78.0F, 243.0F}}, {-0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
51 engine::World3DCamera{{{625.0F, 78.0F, 124.5F}}, {-0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
52 engine::World3DCamera{{{361.5F, 78.0F, -139.0F}}, {0.0F, -0.35836795F, 0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
53 engine::World3DCamera{{{243.0F, 78.0F, -139.0F}}, {0.0F, -0.35836795F, 0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
54 engine::World3DCamera{{{124.5F, 78.0F, -139.0F}}, {0.0F, -0.35836795F, 0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
55 engine::World3DCamera{{{-105.0F, 143.0F, -105.0F}}, {0.577096F, -0.577858F, 0.577096F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
56 engine::World3DCamera{{{-105.0F, 143.0F, 591.0F}}, {0.577096F, -0.577858F, -0.577096F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
57 engine::World3DCamera{{{591.0F, 143.0F, 591.0F}}, {-0.577096F, -0.577858F, -0.577096F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
58 engine::World3DCamera{{{591.0F, 143.0F, -105.0F}}, {-0.577096F, -0.577858F, 0.577096F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
59 engine::World3DCamera{{{-226.0F, 130.0F, 164.0F}}, {0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
60 engine::World3DCamera{{{-226.0F, 130.0F, 322.0F}}, {0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
61 engine::World3DCamera{{{164.0F, 130.0F, 712.0F}}, {0.0F, -0.35836795F, -0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
62 engine::World3DCamera{{{322.0F, 130.0F, 712.0F}}, {0.0F, -0.35836795F, -0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
63 engine::World3DCamera{{{712.0F, 130.0F, 322.0F}}, {-0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
64 engine::World3DCamera{{{712.0F, 130.0F, 164.0F}}, {-0.93358043F, -0.35836795F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
65 engine::World3DCamera{{{322.0F, 130.0F, -226.0F}}, {0.0F, -0.35836795F, 0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
66 engine::World3DCamera{{{164.0F, 130.0F, -226.0F}}, {0.0F, -0.35836795F, 0.93358043F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
67 engine::World3DCamera{{{-291.0F, 255.0F, -126.0F}}, {0.612372F, -0.5F, 0.612372F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
68 engine::World3DCamera{{{-280.0F, 285.0F, 337.0F}}, {0.81915204F, -0.57357644F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
69 engine::World3DCamera{{{-209.0F, 227.0F, 650.0F}}, {0.717968F, -0.5F, -0.484275F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
70 engine::World3DCamera{{{-126.0F, 255.0F, 777.0F}}, {0.612372F, -0.5F, -0.612372F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
71 engine::World3DCamera{{{337.0F, 285.0F, 766.0F}}, {0.0F, -0.57357644F, -0.81915204F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
72 engine::World3DCamera{{{650.0F, 227.0F, 695.0F}}, {-0.484275F, -0.5F, -0.717968F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
73 engine::World3DCamera{{{777.0F, 255.0F, 612.0F}}, {-0.612372F, -0.5F, -0.612372F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
74 engine::World3DCamera{{{766.0F, 285.0F, 149.0F}}, {-0.81915204F, -0.57357644F, 0.0F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
75 engine::World3DCamera{{{695.0F, 227.0F, -164.0F}}, {-0.717968F, -0.5F, 0.484275F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
76 engine::World3DCamera{{{612.0F, 255.0F, -291.0F}}, {-0.612372F, -0.5F, 0.612372F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
77 engine::World3DCamera{{{149.0F, 285.0F, -280.0F}}, {0.0F, -0.57357644F, 0.81915204F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
78 engine::World3DCamera{{{-164.0F, 227.0F, -209.0F}}, {0.484275F, -0.5F, 0.717968F}, {0.0F, 1.0F, 0.0F}, Fov45, engine::MonopolyBoardNearPlane, engine::MonopolyBoardFarPlane},
79 }};
80
81 [[nodiscard]] float adjustedRatio(float ratio) noexcept
82 {
83 ratio = std::clamp(ratio, 0.0F, 1.0F);
84 constexpr float Pi = 3.14159265358979323846F;
85 if (ratio < 0.5F)
86 return 0.5F - 0.5F * std::cos(ratio * Pi);
87 return 0.5F + 0.5F * std::sin((ratio - 0.5F) * Pi);
88 }
89
90 [[nodiscard]] std::array<float, 3> lerp3(
91 const std::array<float, 3>& a, const std::array<float, 3>& b, float t) noexcept
92 {
93 return {a[0] + (b[0] - a[0]) * t,
94 a[1] + (b[1] - a[1]) * t,
95 a[2] + (b[2] - a[2]) * t};
96 }
97
98 [[nodiscard]] std::array<float, 3> cubicBezier3(
99 const std::array<float, 3>& p0,
100 const std::array<float, 3>& p1,
101 const std::array<float, 3>& p2,
102 const std::array<float, 3>& p3, float t) noexcept
103 {
104 const float u = 1.0F - t;
105 const float a = u * u * u;
106 const float b = 3.0F * u * u * t;
107 const float c = 3.0F * u * t * t;
108 const float d = t * t * t;
109 return {
110 a * p0[0] + b * p1[0] + c * p2[0] + d * p3[0],
111 a * p0[1] + b * p1[1] + c * p2[1] + d * p3[1],
112 a * p0[2] + b * p1[2] + c * p2[2] + d * p3[2]};
113 }
114
115 [[nodiscard]] std::array<float, 3> normalized(
116 std::array<float, 3> v) noexcept
117 {
118 const float length = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
119 if (length <= 0.00001F) return {0.0F, 0.0F, 1.0F};
120 v[0] /= length; v[1] /= length; v[2] /= length;
121 return v;
122 }
123 }
124
126 {
127 const auto index = static_cast<std::size_t>(view);
128 return Presets[index < Presets.size() ? index : 0U];
129 }
130
131 void Controller::reset(std::uint64_t tick) noexcept
132 {
134 move_ = Move{};
135 move_.start = current_;
136 move_.end = current_;
137 move_.startTick = tick;
138 move_.endTick = tick;
139 waiting_.reset();
140 manualMouseActive_ = false;
141 manualPresetRequested_ = false;
142 }
143
144 void Controller::startMove(const engine::World3DCamera& target,
145 std::uint64_t tick, bool instant, bool linear, std::uint64_t duration) noexcept
146 {
147 move_.start = current_;
148 move_.end = target;
149 move_.startTick = tick;
150 move_.endTick = instant ? tick : tick + duration;
151 move_.active = !instant;
152 move_.linear = linear;
153 move_.interruptible = false;
154 move_.bezierPosition = false;
155 move_.control1 = {};
156 move_.control2 = {};
157 if (instant) current_ = target;
158 }
159
160 engine::World3DCamera Controller::sample(const Move& move,
161 std::uint64_t tick) noexcept
162 {
163 if (!move.active || move.endTick == move.startTick) return move.end;
164 const auto clamped = std::clamp(tick, move.startTick, move.endTick);
165 const float linear = static_cast<float>(clamped - move.startTick) /
166 static_cast<float>(move.endTick - move.startTick);
167 const float ratio = move.linear ? linear : adjustedRatio(linear);
168 auto result = move.start;
169 result.location = move.bezierPosition ?
170 cubicBezier3(move.start.location, move.control1, move.control2,
171 move.end.location, ratio) :
172 lerp3(move.start.location, move.end.location, ratio);
173 result.forward = lerp3(move.start.forward, move.end.forward, ratio);
174 result.up = lerp3(move.start.up, move.end.up, ratio);
175 result.fieldOfView = move.start.fieldOfView +
176 (move.end.fieldOfView - move.start.fieldOfView) * ratio;
177 result.nearPlane = move.start.nearPlane +
178 (move.end.nearPlane - move.start.nearPlane) * ratio;
179 result.farPlane = move.start.farPlane +
180 (move.end.farPlane - move.start.farPlane) * ratio;
181 return result;
182 }
183
184 Update Controller::tick(std::uint64_t tick) noexcept
185 {
186 Update update{};
187 if (move_.active)
188 {
189 current_ = sample(move_, tick);
190 if (tick >= move_.endTick)
191 {
192 current_ = move_.end;
193 move_.active = false;
194 update.completedMove = true;
195 }
196 }
197 if (waiting_ && (!manualMouseActive_ || manualPresetRequested_) &&
198 (!move_.active || move_.interruptible))
199 {
200 const auto target = *waiting_;
201 waiting_.reset();
202 if (manualPresetRequested_)
203 {
204 manualMouseActive_ = false;
205 manualPresetRequested_ = false;
206 }
207 startMove(target, tick, false);
208 update.startedWaitingMove = true;
209 }
210 update.camera = current_;
211 return update;
212 }
213
215 std::uint64_t tick, bool forceInterrupt, bool manualRequest) noexcept
216 {
217 const auto& target = preset(view);
218 if (forceInterrupt)
219 {
220 waiting_.reset();
221 manualPresetRequested_ = false;
222 startMove(target, tick, true);
223 return;
224 }
225 waiting_ = target;
226 manualPresetRequested_ = manualRequest;
227 }
228
229 void Controller::requestDiceMove(std::uint64_t tick,
230 std::uint8_t randomFourteen) noexcept
231 {
232 (void)tick;
233 const auto base = endCamera();
234 const float ratioA = 0.30F +
235 static_cast<float>(randomFourteen % 14U) / 100.0F;
236 const float ratioB = 1.0F - ratioA;
237 auto target = base;
238 target.location[0] = base.location[0] * ratioB + 240.0F * ratioA;
239 target.location[1] = base.location[1] * ratioB + 300.0F * ratioA;
240 target.location[2] = base.location[2] * ratioB + 240.0F * ratioA;
241 target.forward = normalized({243.0F - target.location[0],
242 -10.0F - target.location[1], 243.0F - target.location[2]});
243 target.up = {0.0F, 1.0F, 0.0F};
244 target.fieldOfView = Fov45;
247 waiting_ = target;
248 manualPresetRequested_ = false;
249 }
250
252 std::uint64_t tick, std::uint64_t duration) noexcept
253 {
254 waiting_.reset();
255 manualPresetRequested_ = false;
256 startMove(preset(view), tick, duration == 0, false, duration);
257 }
258
259 void Controller::requestFloatingIdle(std::uint64_t tick,
260 const std::array<float, 3>& previousVariation,
261 const std::array<float, 3>& nextVariation) noexcept
262 {
263 const auto anchor = current_;
264 manualPresetRequested_ = false;
265 startMove(anchor, tick, false, false, 100U);
266 move_.interruptible = true;
267 move_.bezierPosition = true;
268 for (std::size_t axis = 0; axis < 3; ++axis)
269 {
270 move_.control1[axis] = anchor.location[axis] - previousVariation[axis];
271 move_.control2[axis] = anchor.location[axis] + nextVariation[axis];
272 }
273 }
274
275 bool Controller::requestManualMouseMove(std::int32_t deltaX,
276 std::int32_t deltaY, bool verticalOrbit, std::uint64_t tick) noexcept
277 {
278 if (!manualMouseActive_ && move_.active && !move_.interruptible) return false;
279
280 constexpr float Pi = 3.14159265358979323846F;
281 constexpr std::array<float, 3> Center{243.0F, 10.0F, 243.0F};
282 auto target = manualMouseActive_ ? move_.end : current_;
283
284 auto relative = std::array<float, 3>{
285 target.location[0] - Center[0],
286 target.location[1] - Center[1],
287 target.location[2] - Center[2]};
288 const float horizontal = std::sqrt(relative[0] * relative[0] +
289 relative[2] * relative[2]);
290 if (horizontal > 0.0000001F)
291 {
292 if (deltaX > 40) deltaX = 50;
293 else if (deltaX < -40) deltaX = -50;
294 float angle = std::atan2(relative[0], relative[2]);
295 angle += static_cast<float>(deltaX) / 300.0F * Pi;
296 target.location[0] = horizontal * std::sin(angle) + Center[0];
297 target.location[2] = horizontal * std::cos(angle) + Center[2];
298 }
299
300 relative = {target.location[0] - Center[0],
301 target.location[1] - Center[1], target.location[2] - Center[2]};
302 float magnitude = std::sqrt(relative[0] * relative[0] +
303 relative[1] * relative[1] + relative[2] * relative[2]);
304 if (verticalOrbit)
305 {
306 deltaY = std::clamp(deltaY, -50, 50);
307 if (magnitude > 1.0F)
308 {
309 relative[1] += static_cast<float>(deltaY) * magnitude / 300.0F;
310 relative[1] = std::clamp(relative[1], -50.0F, 0.98F * magnitude);
311 const auto direction = normalized(relative);
312 for (std::size_t axis = 0; axis < 3; ++axis)
313 target.location[axis] = Center[axis] + direction[axis] * magnitude;
314 }
315 }
316 else if (magnitude > 0.00001F)
317 {
318 deltaY = std::clamp(deltaY, -42, 42);
319 magnitude = std::clamp(magnitude + static_cast<float>(deltaY) / 0.47F,
320 100.0F, 1200.0F);
321 const auto direction = normalized(relative);
322 for (std::size_t axis = 0; axis < 3; ++axis)
323 target.location[axis] = Center[axis] + direction[axis] * magnitude;
324 }
325
326 target.forward = normalized({Center[0] - target.location[0],
327 Center[1] - target.location[1], Center[2] - target.location[2]});
328 if (!manualMouseActive_)
329 {
330 manualMouseActive_ = true;
331 waiting_.reset();
332 manualPresetRequested_ = false;
333 startMove(target, tick, false, true);
334 }
335 else if (move_.active)
336 move_.end = target;
337 else
338 {
339 current_ = target;
340 move_ = Move{};
341 move_.start = target;
342 move_.end = target;
343 move_.startTick = tick;
344 move_.endTick = tick;
345 move_.linear = true;
346 }
347 return true;
348 }
349
351 {
352 manualMouseActive_ = false;
353 waiting_.reset();
354 manualPresetRequested_ = false;
355 }
356}
fs::path relative
unsigned state
unsigned index
void reset(std::uint64_t tick=0) noexcept
Update tick(std::uint64_t tick) noexcept
void requestFloatingIdle(std::uint64_t tick, const std::array< float, 3 > &previousVariation, const std::array< float, 3 > &nextVariation) noexcept
void requestPreset(pieces::BoardCameraView view, std::uint64_t tick, bool forceInterrupt=false, bool manualRequest=false) noexcept
bool requestManualMouseMove(std::int32_t deltaX, std::int32_t deltaY, bool verticalOrbit, std::uint64_t tick) noexcept
void requestDemoPreset(pieces::BoardCameraView view, std::uint64_t tick, std::uint64_t duration) noexcept
void requestDiceMove(std::uint64_t tick, std::uint8_t randomFourteen) noexcept
bool isPresentationSquareDefault(const display::State &state) noexcept
const engine::World3DCamera & preset(pieces::BoardCameraView view) noexcept
bool isPresentationDefault(const display::State &state) noexcept
constexpr float MonopolyBoardFarPlane
constexpr float MonopolyBoardNearPlane