Euphoria
camera3.editor.cc
Go to the documentation of this file.
1 #include "core/camera3.editor.h"
2 
3 #include "log/log.h"
4 
5 #include "core/camera3.h"
6 #include "core/viewport.h"
7 #include "core/intersection.h"
8 #include "base/plane.h"
9 #include "base/numeric.h"
10 #include "core/sphere.position.h"
11 
12 
13 namespace eu::core
14 {
15  /*
16  todo:
17  tweak zoom factor:
18  > reminder that you should use exponential interpolation, not lerp, for multiplicative quantities~
19  > if you want to find the frequency, zoom level or scale, halfway between 2 and 8, then the right answer is 4, not 5
20  > lerp( 2, 8, 0.5 ) = 5
21  > eerp( 2, 8, 0.5 ) = 4
22  https://mobile.twitter.com/FreyaHolmer/status/1486714301540831234?t=pA5lC-hN6l_AssTX-rxKbw&s=09
23  2d camera
24  */
25  namespace detail
26  {
27  std::string to_string(const CameraFrame& f)
28  { return fmt::format("[{} {} {}]", f.rotation_angle, f.look_angle, f.position); }
29 
30 
32  {
33  if(self->next_state == nullptr) { return; }
34  self->state = std::move(self->next_state);
35  }
36 
37 
40  {
41  switch(current_style)
42  {
43  default:
46  }
47  }
48 
49 
51 
53  {
54  return
55  {
56  editor->fps.rotation_angle,
57  editor->fps.look_angle,
58  editor->fps.position
59  };
60  }
61 
62  void set_frame_for_editor(const CameraFrame& frame, EditorCamera3* editor)
63  {
64  editor->fps.rotation_angle = frame.rotation_angle;
65  editor->fps.look_angle = frame.look_angle;
66  editor->fps.position = frame.position;
67  }
68 
70 
71 
72  std::unique_ptr<detail::EditorCameraState3> make_orbit_camera
73  (
74  const CameraFrame& start,
75  const CompiledCamera3& latest_camera,
76  const Viewport& latest_viewport,
77  const vec2i& latest_mouse,
78  bool latest_shift
79  );
80  std::unique_ptr<detail::EditorCameraState3> make_default_camera();
81 
82 
84 
85  struct PanData
86  {
87  std::optional<vec3f> collision;
88  };
89 
90  struct OrbitData
91  {
92  bool valid;
94  float distance;
97 
98  OrbitData(std::optional<vec3f> collision, const CameraFrame& f)
99  : valid(collision.has_value())
100  , center(collision.has_value() ? *collision : zero3f)
101  , distance( vec3f::from_to(center, f.position).get_length() )
104  {
105  }
106  };
107 
108  std::optional<float>
109  calc_zoom_move(int dx, int dy, float length, EditorCamera3* owner)
110  {
111  const auto change = dx+dy;
112  if (change == 0) { return std::nullopt; }
113 
114  const auto suggested_move = c_int_to_float(change) * length * owner->zoom_percent;
115  const auto sign = get_sign(change > 0);
116  const auto val = eu::min(square(suggested_move), length);
117  return sign * eu::min(val, owner->max_zoom_change);
118  }
119 
120 
122  {
129  bool shift;
130 
131  std::optional<PanData> pan;
132  std::optional<OrbitData> orbit;
133 
135  (
136  // need mouse center
137  const CameraFrame& start,
138  const CompiledCamera3& latest_c,
139  const Viewport& latest_viewport,
140  const vec2i& latest_mouse,
141  bool latest_shift
142  )
143  : start_frame(start)
144  , camera(latest_c)
145  , latest_camera(latest_c)
146  , viewport(latest_viewport)
147  , start_mouse(latest_mouse)
148  , mouse(latest_mouse)
149  , shift(latest_shift)
150  {
151  }
152 
154  {
155  if(is_panning())
156  {
157  orbit.reset();
158  if(pan.has_value() == false)
159  {
161  pan = PanData
162  {
163  owner->raycast(ray)
164  };
165  }
166  }
167  else
168  {
169  pan.reset();
170  if(orbit.has_value() == false)
171  {
172  const auto ray = camera.from_clip_to_world_ray(vec2f{0.0f, 0.0f}).get_normalized();
173  orbit = OrbitData
174  {
175  owner->raycast(ray),
177  };
178  }
179  }
180  }
181 
183  {
184  if(pan.has_value() == false) { return; }
185  if(pan->collision.has_value() == false) { return; }
186 
187  // based on https://prideout.net/blog/perfect_panning/
188 
189  const auto far_start = get_far_point(start_mouse, camera);
190  const auto u = vec3f::from_to(start_frame.position, *pan->collision).get_length();
191  const auto v = vec3f::from_to(*pan->collision, far_start).get_length();
192 
193  const auto far_point = get_far_point(mouse, latest_camera);
194  const auto offset = vec3f::from_to(far_start, far_point) * (-u / v);
195 
196  auto new_frame = start_frame;
197  new_frame.position += offset;
198  detail::set_frame_for_editor(new_frame, owner);
199  }
200 
202  {
203  if(orbit.has_value() == false) { return; }
204  if(orbit->valid == false) { return; }
205 
206  const auto out = FpsController::calc_rotation
207  (
208  orbit->rotation_angle, orbit->look_angle
209  ).create_from_right_up_in(vec3f{0.0f, 0.0f, -1.0f});
210 
211  const auto new_frame = CameraFrame
212  {
213  orbit->rotation_angle,
214  orbit->look_angle,
215  orbit->center + orbit->distance * out
216  };
217  detail::set_frame_for_editor(new_frame, owner);
218  }
219 
221  {
222  if(is_panning())
223  {
224  update_panning(owner);
225  }
226  else
227  {
228  update_orbit(owner);
229  }
230  }
231 
232  [[nodiscard]] vec3f get_far_point(const vec2i& p, const CompiledCamera3& cc) const
233  {
234  return from_mouse_to_ray(cc, viewport, p).get_point(1.0f);
235  }
236 
237  [[nodiscard]]
238  bool is_panning() const
239  {
240  return shift;
241  }
242 
243  void on_mouse_move(EditorCamera3* owner, int dx, int dy) override
244  {
245  ASSERT(owner != nullptr);
246 
247  if(orbit.has_value() && orbit->valid)
248  {
249  const auto x = c_int_to_float(dx);
250  const auto y = c_int_to_float(dy);
253 
254  orbit->rotation_angle.wrap();
255  }
256  }
257 
258  void on_scroll(EditorCamera3* owner, int dx, int dy) override
259  {
260  // only orbit can zoom
261  if(owner->scroll_in_orbit && orbit.has_value() && orbit->valid )
262  {
263  const auto move = calc_zoom_move(dx, dy, orbit->distance, owner);
264  if(move)
265  {
266  orbit->distance += *move;
267  }
268  }
269  }
270 
271  void on_key(EditorCamera3*, Key, bool) override
272  {
273  }
274 
275  void update
276  (
277  EditorCamera3* owner,
278  bool shift_state,
279  const vec2i& m,
280  const CompiledCamera3& cc,
281  const Viewport&,
282  float
283  ) override
284  {
285  latest_camera = cc;
286  bool changed = false;
287 
288  if(shift_state != shift)
289  {
290  shift = shift_state;
291  changed = true;
292  }
293 
294  if(mouse != m)
295  {
296  mouse = m;
297  changed = true;
298  }
299 
300  if(changed)
301  {
302  update_data(owner);
303  mouse = m;
304  update_camera(owner);
305  }
306  }
307 
309  {
310  }
311 
312  void on_camera_stop(EditorCamera3* owner) override
313  {
314  owner->next_state = make_default_camera();
315  }
316 
317  MouseBehaviour get_mouse(const EditorCamera3*) const override
318  {
320  }
321  };
322 
323 
325 
326 
328  {
329  bool latest_shift = false;
333  bool looking = false;
334 
336  : latest_camera
337  (
338  m4_identity,
340  )
342  (
343  Recti::from_width_height(0, 0)
344  )
345  {
346  }
347 
348 
349  void
350  on_mouse_move(EditorCamera3* owner, int dx, int dy) override
351  {
352  if(owner->style == EditorCameraStyle3::fps)
353  {
354  owner->fps.look(c_int_to_float(dx), c_int_to_float(dy));
355  }
356  }
357 
358 
359  void
360  on_scroll(EditorCamera3* owner, int dx, int dy) override
361  {
362  // fps camera can't scroll
363  if(owner->style == EditorCameraStyle3::fps) { return; }
364  if(latest_viewport.bounds.get_width() == 0) { return; }
365  if(latest_viewport.bounds.get_height() == 0) { return; }
366 
368  const auto collision = owner->raycast(ray);
369 
370  // todo(Gustav): implement basic zoom if no collision
371  if(collision.has_value() == false) { return; }
372 
373  const auto dir = vec3f::from_to(owner->fps.position, *collision);
374  const auto length = dir.get_length();
375  const auto unit = dir.get_normalized();
376 
377  const auto move = calc_zoom_move(dx, dy, length, owner);
378 
379  if(move)
380  {
381  const auto change = *move * unit;
382  owner->fps.position += change;
383  }
384  }
385 
386 
387  void
388  on_key(EditorCamera3* owner, Key key, bool down) override
389  {
390  if(owner->style == EditorCameraStyle3::fps)
391  {
392  owner->fps.on_key(key, down);
393  }
394  }
395 
396 
397  void
399  (
400  EditorCamera3* owner,
401  bool shift,
402  const vec2i& mouse,
403  const CompiledCamera3& camera,
404  const Viewport& viewport,
405  float dt
406  ) override
407  {
408  latest_shift = shift;
409  latest_mouse = mouse;
410  latest_camera = camera;
411  latest_viewport = viewport;
412  if(owner->style == EditorCameraStyle3::fps)
413  {
414  owner->fps.update(dt);
415  }
416  }
417 
418 
419  void
421  {
422  if(owner->style == EditorCameraStyle3::orbital)
423  {
425  (
429  latest_mouse,
431  );
432  looking = true;
433  }
434  }
435 
436 
437  void
439  {
440  looking = false;
441  }
442 
444  get_mouse(const EditorCamera3*) const override
445  {
446  if(looking)
447  {
449  }
450  else
451  {
452  return MouseBehaviour::normal;
453  }
454  }
455  };
456 
457 
459  {
462  float total_time;
463  float timer = 0.0f;
464 
465  LerpCamera(EditorCamera3* owner, const CameraFrame& ato, float atime)
466  : from{get_frame_from_editor(owner)}
467  , to{ato}
468  , total_time{atime}
469  {
470  }
471 
472  void on_mouse_move(EditorCamera3*, int, int) override
473  {
474  }
475 
476  void on_key(EditorCamera3* owner, Key, bool) override
477  {
478  owner->next_state = make_default_camera();
479  }
480 
481  void update
482  (
483  EditorCamera3* owner,
484  bool,
485  const vec2i&,
486  const CompiledCamera3&,
487  const Viewport&,
488  float dt
489  ) override
490  {
491  timer += dt;
492  if(timer >= total_time)
493  {
494  set_frame_for_editor(to, owner);
495  owner->next_state = make_default_camera();
496  return;
497  }
498 
499  const auto factor = easing::apply(owner->lerp_function, timer / total_time);
500  const auto frame = CameraFrame
501  {
505  };
506 
507  set_frame_for_editor(frame, owner);
508  }
509 
510  void on_scroll(EditorCamera3*, int, int) override {}
511  void on_camera_start(EditorCamera3*) override {}
512  void on_camera_stop(EditorCamera3*) override {}
513 
515  };
516 
517 
518  void
520  {
521  cam->state = make_default_camera();
522  }
523 
524 
525 
527 
528 
529  std::unique_ptr<detail::EditorCameraState3> make_orbit_camera
530  (
531  const detail::CameraFrame& start,
532  const CompiledCamera3& latest_camera,
533  const Viewport& latest_viewport,
534  const vec2i& latest_mouse,
535  bool latest_shift
536  )
537  {
538  return std::make_unique<detail::OrbitCamera>(start, latest_camera, latest_viewport, latest_mouse, latest_shift);
539  }
540 
541 
542  std::unique_ptr<detail::EditorCameraState3> make_default_camera()
543  {
544  return std::make_unique<detail::DefaultCamera>();
545  }
546 
547  std::unique_ptr<detail::EditorCameraState3> make_lerp_camera(EditorCamera3* owner, const CameraFrame& to, float time)
548  {
549  return std::make_unique<detail::LerpCamera>(owner, to, time);
550  }
551  }
552 
553 
555 
556 
558  {
559  // todo(Gustav): default to inverted? or don't subtract delta values
563 
564  const auto default_frame = detail::get_frame_from_editor(this);
565  for(int index=0; index<EditorCamera3::max_stored_index; index+=1)
566  {
567  stored_cameras.emplace_back(default_frame);
568  }
569  }
570 
571 
572  void
574  (
575  bool shift_state,
576  const vec2i& mouse,
577  const CompiledCamera3& camera,
578  const Viewport& viewport,
579  float dt
580  )
581  {
582  if(state == nullptr) { return; }
583  state->update(this, shift_state, mouse, camera, viewport, dt);
584  detail::update_state(this);
585  }
586 
587 
588  void
590  {
591  if(state == nullptr) { return; }
592  state->on_mouse_move(this, dx, dy);
593  detail::update_state(this);
594  }
595 
596 
597  void
598  EditorCamera3::on_scroll(int dx, int dy)
599  {
600  if(state == nullptr) { return; }
601  state->on_scroll(this, dx, dy);
602  detail::update_state(this);
603  }
604 
605 
606  void
607  EditorCamera3::on_key(Key key, bool down)
608  {
609  if(state == nullptr) { return; }
610  state->on_key(this, key, down);
611  detail::update_state(this);
612  }
613 
614 
615  void
617  {
618  if(state == nullptr) { return; }
619  state->on_camera_start(this);
620  detail::update_state(this);
621  }
622 
623 
624  void
626  {
627  fps.is_left_down = false;
628  fps.is_right_down = false;
629  fps.is_forward_down = false;
630  fps.is_backward_down = false;
631  fps.is_up_down = false;
632  fps.is_down_down = false;
633 
634  if(state == nullptr) { return; }
635  state->on_camera_stop(this);
636  detail::update_state(this);
637  }
638 
639 
640  bool
642  {
644  }
645 
646 
647  void
649  {
651  }
652 
653  void
655  {
657  const auto index = c_int_to_sizet(id);
658  const auto frame = detail::get_frame_from_editor(this);
659  stored_cameras[index] = frame;
660  }
661 
662  void
664  {
666  const auto index = c_int_to_sizet(id);
667  const auto& frame = stored_cameras[index];
668  apply_frame(frame);
669  }
670 
671 
672  void
674  {
675  const auto from = detail::get_frame_from_editor(this);
676 
677  // algorithm: https://stackoverflow.com/a/32836605
679  const auto distance = (s.sphere.radius * 2.0f) / tan(cam.fov / 2.0f);
680  fps.position = s.center + fps.get_rotation().get_out() * distance;
681 
682 
683  const auto to = detail::get_frame_from_editor(this);
684  detail::set_frame_for_editor(from, this);
685  apply_frame(to);
686  }
687 
688 
689  void
691  {
692  if(animate_camera)
693  {
695  }
696  else
697  {
698  detail::set_frame_for_editor(frame, this);
699  }
700  }
701 
702 
705  {
706  if(state == nullptr) { return MouseBehaviour::normal; }
707  return state->get_mouse(this);
708  }
709 }
#define ASSERTX(x,...)
Definition: assert.h:48
#define ASSERT(x)
Definition: assert.h:29
std::optional< float > calc_zoom_move(int dx, int dy, float length, EditorCamera3 *owner)
std::string to_string(const CameraFrame &f)
std::unique_ptr< detail::EditorCameraState3 > make_lerp_camera(EditorCamera3 *owner, const CameraFrame &to, float time)
void set_frame_for_editor(const CameraFrame &frame, EditorCamera3 *editor)
CameraFrame get_frame_from_editor(EditorCamera3 *editor)
EditorCameraStyle3 get_next_style(EditorCameraStyle3 current_style)
std::unique_ptr< detail::EditorCameraState3 > make_default_camera()
std::unique_ptr< detail::EditorCameraState3 > make_orbit_camera(const CameraFrame &start, const CompiledCamera3 &latest_camera, const Viewport &latest_viewport, const vec2i &latest_mouse, bool latest_shift)
void update_state(EditorCamera3 *self)
void set_default_state(EditorCamera3 *cam)
float apply(Function f, float t)
Definition: easing.cc:61
UnitRay3f from_mouse_to_unit_ray(const core::CompiledCamera3 &camera, const core::Viewport &viewport, const vec2i &position)
Definition: viewport.cc:48
Key
Definition: key.h:26
Ray3f from_mouse_to_ray(const core::CompiledCamera3 &camera, const core::Viewport &viewport, const vec2i &position)
Definition: viewport.cc:31
constexpr mat4f m4_identity
Definition: mat4.h:118
size_t c_int_to_sizet(int i)
Definition: cint.cc:35
vec3f lerp_vec3f(const vec3f &f, float v, const vec3f &t)
Functions.
Definition: vec3.cc:264
float tan(const Angle &ang)
Definition: angle.cc:75
float square(float r)
Definition: numeric.cc:94
Angle lerp_angle(const Angle &from, float v, const Angle &to)
Definition: angle.cc:194
constexpr float c_int_to_float(int i)
Definition: cint.h:50
int get_sign(float r)
Calculates the sign as a positive or a negative int.
Definition: numeric.cc:63
size2f min(const size2f lhs, const size2f rhs)
Definition: size2.cc:140
constexpr vec3f zero3f
Definition: vec3.h:95
constexpr static Angle from_degrees(float degrees)
Definition: angle.h:16
vec3f get_point(float at) const
Definition: ray.cc:98
int get_height() const
Definition: rect.cc:891
int get_width() const
Definition: rect.cc:897
Ray3f from_clip_to_world_ray(const vec2f &p) const
Definition: camera3.cc:34
void update(bool shift_state, const vec2i &mouse, const CompiledCamera3 &camera, const Viewport &viewport, float dt)
void on_scroll(int dx, int dy)
easing::Function lerp_function
void on_key(Key key, bool down)
void on_mouse_move(int dx, int dy)
EditorCameraStyle3 style
std::vector< detail::CameraFrame > stored_cameras
MouseBehaviour get_mouse() const
void apply_frame(const detail::CameraFrame &frame)
virtual std::optional< vec3f > raycast(const UnitRay3f &ray)=0
static constexpr int max_stored_index
std::unique_ptr< detail::EditorCameraState3 > next_state
std::unique_ptr< detail::EditorCameraState3 > state
void focus(const SphereAndPosition &s, const Camera3 &cam)
void look(float delta_rot, float delta_look)
quatf get_rotation() const
Sensitivity look_sensitivity
Definition: fpscontroller.h:23
static quatf calc_rotation(const Angle &rotation_angle, const Angle &look_angle)
void on_key(Key key, bool down)
void update(float delta)
void look_in_direction(const unit3f &v)
float get_multiplier_with_sign() const
Definition: sensitivity.cc:22
void update(EditorCamera3 *owner, bool shift, const vec2i &mouse, const CompiledCamera3 &camera, const Viewport &viewport, float dt) override
MouseBehaviour get_mouse(const EditorCamera3 *) const override
void on_camera_stop(EditorCamera3 *) override
void on_scroll(EditorCamera3 *owner, int dx, int dy) override
void on_key(EditorCamera3 *owner, Key key, bool down) override
void on_camera_start(EditorCamera3 *owner) override
void on_mouse_move(EditorCamera3 *owner, int dx, int dy) override
LerpCamera(EditorCamera3 *owner, const CameraFrame &ato, float atime)
void on_key(EditorCamera3 *owner, Key, bool) override
void update(EditorCamera3 *owner, bool, const vec2i &, const CompiledCamera3 &, const Viewport &, float dt) override
void on_camera_start(EditorCamera3 *) override
void on_camera_stop(EditorCamera3 *) override
void on_mouse_move(EditorCamera3 *, int, int) override
void on_scroll(EditorCamera3 *, int, int) override
MouseBehaviour get_mouse(const EditorCamera3 *) const override
void on_camera_stop(EditorCamera3 *owner) override
std::optional< OrbitData > orbit
void update_data(EditorCamera3 *owner)
void on_camera_start(EditorCamera3 *) override
void update(EditorCamera3 *owner, bool shift_state, const vec2i &m, const CompiledCamera3 &cc, const Viewport &, float) override
void update_orbit(EditorCamera3 *owner)
std::optional< PanData > pan
void update_panning(EditorCamera3 *owner)
void on_mouse_move(EditorCamera3 *owner, int dx, int dy) override
MouseBehaviour get_mouse(const EditorCamera3 *) const override
void on_scroll(EditorCamera3 *owner, int dx, int dy) override
void update_camera(EditorCamera3 *owner)
OrbitCamera(const CameraFrame &start, const CompiledCamera3 &latest_c, const Viewport &latest_viewport, const vec2i &latest_mouse, bool latest_shift)
void on_key(EditorCamera3 *, Key, bool) override
vec3f get_far_point(const vec2i &p, const CompiledCamera3 &cc) const
OrbitData(std::optional< vec3f > collision, const CameraFrame &f)
std::optional< vec3f > collision
unit3f get_out() const
Definition: quat.cc:187
vec3f create_from_right_up_in(const vec3f &v) const
returns In*Z + Right*X + Up*Y
Definition: quat.cc:196
Definition: vec2.h:33
Definition: vec2.h:72
Definition: vec3.h:48
unit3f get_normalized() const
Definition: vec3.cc:174
float get_length() const
Definition: vec3.cc:152
static vec3f from_to(const vec3f &from, const vec3f &to)
Definition: vec3.cc:127