Antkeeper  0.0.1
camera-controls.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "game/controls.hpp"
24 #include <engine/debug/log.hpp>
25 
26 namespace {
27 
28  void handle_mouse_motion(::game& ctx, const input::mouse_moved_event& event)
29  {
30  if (ctx.active_camera_eid == entt::null)
31  {
32  return;
33  }
34 
35  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
36 
37  // Rotate camera
39  {
40  // Adjust target pitch and yaw angles according to mouse motion
41  auto target_angles = spring_arm.angles_spring.get_target_value();
42  target_angles.x() -= ctx.mouse_tilt_factor * static_cast<double>(event.difference.y());
43  target_angles.y() -= ctx.mouse_pan_factor * static_cast<double>(event.difference.x());
44 
45  // Apply angular constraints
46  target_angles = math::clamp(target_angles, spring_arm.min_angles, spring_arm.max_angles);
47 
48  // Update spring arm target angles
49  spring_arm.angles_spring.set_target_value(target_angles);
50  }
51 
52  // Zoom camera
54  {
55  // Adjust zoom factor
56  spring_arm.zoom -= static_cast<double>(event.difference.y()) / static_cast<double>(ctx.window->get_viewport_size().y());
57 
58  // Limit zoom factor
59  spring_arm.zoom = std::min<double>(std::max<double>(spring_arm.zoom, 0.0), 1.0);
60  }
61  }
62 
63  void load_camera_preset(::game& ctx,std::uint8_t index)
64  {
65 
66  }
67 
68  void save_camera_preset(::game& ctx,std::uint8_t index)
69  {
70 
71  }
72 
73  void step_camera_zoom(::game& ctx, double scale)
74  {
75  if (ctx.active_camera_eid == entt::null)
76  {
77  return;
78  }
79 
80  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
81  auto target_angles = spring_arm.angles_spring.get_target_value();
82 
83  // Modulate target pitch angle
84  target_angles.x() += (spring_arm.max_angles.x() - spring_arm.min_angles.x()) / ctx.zoom_steps * scale;
85 
86  // Apply angular constraints
87  target_angles = math::clamp(target_angles, spring_arm.min_angles, spring_arm.max_angles);
88 
89  // Update spring arm target angles
90  spring_arm.angles_spring.set_target_value(target_angles);
91  }
92 
93  void update_relative_mouse_mode(::game& ctx)
94  {
98  {
99  ctx.input_manager->set_relative_mouse_mode(true);
100  }
101  else
102  {
103  ctx.input_manager->set_relative_mouse_mode(false);
104  }
105  }
106 
107  void load_or_save_camera_preset(::game& ctx, std::uint8_t index)
108  {
110  {
111  save_camera_preset(ctx, index);
112  }
113  else
114  {
115  load_camera_preset(ctx, index);
116  }
117  }
118 }
119 
121 {
122  // Camera mouse motion
123  ctx.event_subscriptions.emplace_back
124  (
125  ctx.input_manager->get_event_dispatcher().subscribe<input::mouse_moved_event>
126  (
127  [&](const auto& event)
128  {
129  if (ctx.camera_action_map.is_enabled())
130  {
131  handle_mouse_motion(ctx, event);
132  }
133  }
134  )
135  );
136 
137  // Camera mouse pick
138  ctx.event_subscriptions.emplace_back
139  (
141  (
142  [&](const auto& event)
143  {
144  }
145  )
146  );
147 
148  // Camera mouse look
149  ctx.event_subscriptions.emplace_back
150  (
152  (
153  [&](const auto& event)
154  {
155  update_relative_mouse_mode(ctx);
156  }
157  )
158  );
159  ctx.event_subscriptions.emplace_back
160  (
162  (
163  [&](const auto& event)
164  {
165  update_relative_mouse_mode(ctx);
166  }
167  )
168  );
169 
170  // Camera mouse drag
171  ctx.event_subscriptions.emplace_back
172  (
174  (
175  [&](const auto& event)
176  {
177  update_relative_mouse_mode(ctx);
178 
179  /*
180  mouse_grip = ctx.toggle_mouse_grip ? !mouse_grip : true;
181 
182  if (mouse_grip)
183  {
184  const auto& mouse_position = (*ctx.input_manager->get_mice().begin())->get_position();
185 
186  // Cast ray to plane
187  const auto mouse_ray = get_mouse_ray(mouse_position);
188  const auto intersection = geom::intersection(mouse_ray, mouse_grip_plane);
189  if (intersection)
190  {
191  mouse_grip_point = mouse_ray.origin + mouse_ray.direction * (*intersection);
192  }
193  }
194 
195  ctx.input_manager->set_relative_mouse_mode(mouse_look || mouse_grip || mouse_zoom);
196  */
197  }
198  )
199  );
200  ctx.event_subscriptions.emplace_back
201  (
203  (
204  [&](const auto& event)
205  {
206  update_relative_mouse_mode(ctx);
207  }
208  )
209  );
210 
211  // Camera mouse zoom
212  ctx.event_subscriptions.emplace_back
213  (
215  (
216  [&](const auto& event)
217  {
218  update_relative_mouse_mode(ctx);
219  }
220  )
221  );
222  ctx.event_subscriptions.emplace_back
223  (
225  (
226  [&](const auto& event)
227  {
228  update_relative_mouse_mode(ctx);
229  }
230  )
231  );
232 
233  // Camera zoom in
234  ctx.event_subscriptions.emplace_back
235  (
237  (
238  [&](const auto& event)
239  {
240  step_camera_zoom(ctx, static_cast<double>(ctx.camera_zoom_in_action.get_input_value()));
241  }
242  )
243  );
244 
245  // Camera zoom out
246  ctx.event_subscriptions.emplace_back
247  (
249  (
250  [&](const auto& event)
251  {
252  step_camera_zoom(ctx, -static_cast<double>(ctx.camera_zoom_out_action.get_input_value()));
253  }
254  )
255  );
256 
257  // Camera orbit left
258  ctx.event_subscriptions.emplace_back
259  (
261  (
262  [&](const auto& event)
263  {
264  if (ctx.active_camera_eid == entt::null)
265  {
266  return;
267  }
268 
269  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
270  spring_arm.angular_velocities.y() = -ctx.gamepad_pan_factor * static_cast<double>(event.input_value);
271  }
272  )
273  );
274  ctx.event_subscriptions.emplace_back
275  (
277  (
278  [&](const auto& event)
279  {
280  if (ctx.active_camera_eid == entt::null)
281  {
282  return;
283  }
284 
285  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
286  spring_arm.angular_velocities.y() = 0.0;
287  }
288  )
289  );
290 
291  // Camera orbit right
292  ctx.event_subscriptions.emplace_back
293  (
295  (
296  [&](const auto& event)
297  {
298  if (ctx.active_camera_eid == entt::null)
299  {
300  return;
301  }
302 
303  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
304  spring_arm.angular_velocities.y() = ctx.gamepad_pan_factor * static_cast<double>(event.input_value);
305  }
306  )
307  );
308  ctx.event_subscriptions.emplace_back
309  (
311  (
312  [&](const auto& event)
313  {
314  if (ctx.active_camera_eid == entt::null)
315  {
316  return;
317  }
318 
319  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
320  spring_arm.angular_velocities.y() = 0.0;
321  }
322  )
323  );
324 
325  // Camera orbit up
326  ctx.event_subscriptions.emplace_back
327  (
329  (
330  [&](const auto& event)
331  {
332  if (ctx.active_camera_eid == entt::null)
333  {
334  return;
335  }
336 
337  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
338  spring_arm.angular_velocities.x() = ctx.gamepad_tilt_factor * static_cast<double>(event.input_value);
339  }
340  )
341  );
342  ctx.event_subscriptions.emplace_back
343  (
345  (
346  [&](const auto& event)
347  {
348  if (ctx.active_camera_eid == entt::null)
349  {
350  return;
351  }
352 
353  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
354  spring_arm.angular_velocities.x() = 0.0;
355  }
356  )
357  );
358 
359  // Camera orbit down
360  ctx.event_subscriptions.emplace_back
361  (
363  (
364  [&](const auto& event)
365  {
366  if (ctx.active_camera_eid == entt::null)
367  {
368  return;
369  }
370 
371  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
372  spring_arm.angular_velocities.x() = -ctx.gamepad_tilt_factor * static_cast<double>(event.input_value);
373  }
374  )
375  );
376  ctx.event_subscriptions.emplace_back
377  (
379  (
380  [&](const auto& event)
381  {
382  if (ctx.active_camera_eid == entt::null)
383  {
384  return;
385  }
386 
387  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
388  spring_arm.angular_velocities.x() = 0.0;
389  }
390  )
391  );
392 
393  // Camera look ahead
394  ctx.event_subscriptions.emplace_back
395  (
397  (
398  [&](const auto& event)
399  {
400  if (ctx.active_camera_eid == entt::null || ctx.controlled_ant_eid == entt::null)
401  {
402  return;
403  }
404 
405  auto& spring_arm = ctx.entity_registry->get<spring_arm_component>(ctx.active_camera_eid);
406  const auto& subject_rigid_body = *ctx.entity_registry->get<rigid_body_component>(ctx.controlled_ant_eid).body;
407 
408 
409  // Determine camera up direction
410  const auto camera_up = math::fvec3(spring_arm.up_rotation * math::dvec3{0, 1, 0});
411 
412  // Get spring arm target angles
413  auto target_angles = spring_arm.angles_spring.get_target_value();
414 
415 
416  // Determine camera forward direction
417  const auto camera_yaw_rotation = math::angle_axis(target_angles.y(), {0.0, 1.0, 0.0});
418  const auto camera_pitchless_orientation = math::normalize(spring_arm.up_rotation * camera_yaw_rotation);
419  const auto camera_forward = math::fvec3(camera_pitchless_orientation * math::dvec3{0, 0, -1});
420 
421  // Determine subject forward direction
422  const auto subject_forward = subject_rigid_body.get_transform().rotation * math::fvec3{0, 0, 1};
423 
424  // Find signed angle between the two forward directions about camera up axis
425  const auto angular_difference = math::signed_angle(camera_forward, subject_forward, camera_up);
426 
427  // Add angular difference to spring arm target yaw angle
428  target_angles.y() += angular_difference;
429 
430  // Update spring arm target angles
431  spring_arm.angles_spring.set_target_value(target_angles);
432  }
433  )
434  );
435 
436  // Camera presets
437  ctx.event_subscriptions.emplace_back
438  (
440  (
441  [&](const auto& event) {load_or_save_camera_preset(ctx, 0);}
442  )
443  );
444  ctx.event_subscriptions.emplace_back
445  (
447  (
448  [&](const auto& event) {load_or_save_camera_preset(ctx, 1);}
449  )
450  );
451  ctx.event_subscriptions.emplace_back
452  (
454  (
455  [&](const auto& event) {load_or_save_camera_preset(ctx, 2);}
456  )
457  );
458  ctx.event_subscriptions.emplace_back
459  (
461  (
462  [&](const auto& event) {load_or_save_camera_preset(ctx, 3);}
463  )
464  );
465  ctx.event_subscriptions.emplace_back
466  (
468  (
469  [&](const auto& event) {load_or_save_camera_preset(ctx, 4);}
470  )
471  );
472  ctx.event_subscriptions.emplace_back
473  (
475  (
476  [&](const auto& event) {load_or_save_camera_preset(ctx, 5);}
477  )
478  );
479  ctx.event_subscriptions.emplace_back
480  (
482  (
483  [&](const auto& event) {load_or_save_camera_preset(ctx, 6);}
484  )
485  );
486  ctx.event_subscriptions.emplace_back
487  (
489  (
490  [&](const auto& event) {load_or_save_camera_preset(ctx, 7);}
491  )
492  );
493  ctx.event_subscriptions.emplace_back
494  (
496  (
497  [&](const auto& event) {load_or_save_camera_preset(ctx, 8);}
498  )
499  );
500  ctx.event_subscriptions.emplace_back
501  (
503  (
504  [&](const auto& event) {load_or_save_camera_preset(ctx, 9);}
505  )
506  );
507 }
508 
510 {
512 }
513 
515 {
517  ctx.camera_action_map.reset();
518 }
void setup_camera_controls(::game &ctx)
void disable_camera_controls(::game &ctx)
void enable_camera_controls(::game &ctx)
Definition: game.hpp:121
input::action camera_preset_3_action
Definition: game.hpp:240
input::action camera_preset_6_action
Definition: game.hpp:243
input::action camera_preset_8_action
Definition: game.hpp:245
input::action camera_preset_4_action
Definition: game.hpp:241
input::action camera_look_ahead_action
Definition: game.hpp:237
entity::id active_camera_eid
Definition: game.hpp:395
input::action camera_preset_9_action
Definition: game.hpp:246
std::vector< std::shared_ptr<::event::subscription > > event_subscriptions
Definition: game.hpp:265
input::action camera_preset_7_action
Definition: game.hpp:244
input::action camera_save_preset_action
Definition: game.hpp:248
double gamepad_tilt_factor
Definition: game.hpp:291
input::action_map camera_action_map
Definition: game.hpp:226
input::action camera_orbit_right_action
Definition: game.hpp:234
input::action camera_mouse_drag_action
Definition: game.hpp:229
float zoom_steps
Definition: game.hpp:282
input::action camera_preset_1_action
Definition: game.hpp:238
input::action camera_orbit_left_action
Definition: game.hpp:233
input::action camera_preset_5_action
Definition: game.hpp:242
std::shared_ptr< app::window > window
Definition: game.hpp:166
input::action camera_mouse_look_action
Definition: game.hpp:228
double gamepad_pan_factor
Definition: game.hpp:290
double mouse_tilt_factor
Definition: game.hpp:278
input::action camera_mouse_pick_action
Definition: game.hpp:227
input::action camera_preset_10_action
Definition: game.hpp:247
input::action camera_orbit_up_action
Definition: game.hpp:235
input::action camera_preset_2_action
Definition: game.hpp:239
entity::id controlled_ant_eid
Definition: game.hpp:394
input::action camera_orbit_down_action
Definition: game.hpp:236
std::unique_ptr< entity::registry > entity_registry
Definition: game.hpp:392
input::action camera_zoom_in_action
Definition: game.hpp:231
std::unique_ptr< app::input_manager > input_manager
Definition: game.hpp:172
input::action camera_mouse_zoom_action
Definition: game.hpp:230
double mouse_pan_factor
Definition: game.hpp:277
input::action camera_zoom_out_action
Definition: game.hpp:232
void disable()
Disables the mapping of input events to actions.
Definition: action-map.cpp:41
void reset()
Resets the activation states of each action in the action map.
Definition: action-map.cpp:54
void enable()
Enables the mapping of input events to actions.
Definition: action-map.cpp:28
::event::channel< action_active_event > & get_active_channel() noexcept
Returns the channel through which action active events are published.
Definition: action.hpp:97
bool is_active() const noexcept
Returns true if the action is active, false otherwise.
Definition: action.hpp:79
::event::channel< action_deactivated_event > & get_deactivated_channel() noexcept
Returns the channel through which action deactivated events are published.
Definition: action.hpp:103
::event::channel< action_activated_event > & get_activated_channel() noexcept
Returns the channel through which action activated events are published.
Definition: action.hpp:91
Publish-subscribe messaging.
Definition: channel.hpp:32
T signed_angle(const vector< T, 3 > &x, const vector< T, 3 > &y, const vector< T, 3 > &n)
Calculates the signed angle between two direction vectors about axis.
Definition: vector.hpp:1508
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:679
quaternion< T > angle_axis(T angle, const vec3< T > &axis)
Creates a rotation from an angle and axis.
Definition: quaternion.hpp:685
constexpr mat4< T > scale(const vec3< T > &v)
Constructs a scale matrix.
constexpr vector< T, N > clamp(const vector< T, N > &x, const vector< T, N > &min, const vector< T, N > &max)
Clamps the values of a vector's elements.
Definition: vector.hpp:1069
Event generated when a mouse has been moved.
n-dimensional vector.
Definition: vector.hpp:44
constexpr element_type & x() noexcept
Returns a reference to the first element.
Definition: vector.hpp:164
constexpr element_type & y() noexcept
Returns a reference to the second element.
Definition: vector.hpp:180
std::unique_ptr< physics::rigid_body > body
Attaches a camera to an entity using springs.
physics::numeric_spring< math::dvec3, double > angles_spring
Pitch, yaw, and roll angles spring.
math::dvec3 angular_velocities
Pitch, yaw, and roll velocities, in radians per second.