ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SceneWidget.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <Image.h>
11 #include <ecvBBox.h>
12 #include <imgui.h>
13 
14 #include <Eigen/Geometry>
15 #include <set>
16 #include <unordered_set>
17 
25 #include "visualization/gui/Util.h"
35 
36 namespace cloudViewer {
37 namespace visualization {
38 namespace gui {
39 
40 static const double MIN_FAR_PLANE = 1.0;
41 
42 static const double DELAY_FOR_BEST_RENDERING_SECS = 0.2; // seconds
43 // ----------------------------------------------------------------------------
45 public:
47  rendering::Camera* camera)
48  : light_dir_(std::make_unique<rendering::LightDirectionInteractorLogic>(
49  scene->GetScene(), camera)) {}
50 
52  return *light_dir_.get();
53  }
54 
56  std::function<void(const Eigen::Vector3f&)> on_changed) {
57  on_light_dir_changed_ = on_changed;
58  }
59 
60  void Mouse(const MouseEvent& e) override {
61  switch (e.type) {
63  mouse_down_x_ = e.x;
64  mouse_down_y_ = e.y;
65  light_dir_->StartMouseDrag();
66  break;
67  case MouseEvent::DRAG: {
68  int dx = e.x - mouse_down_x_;
69  int dy = e.y - mouse_down_y_;
70  light_dir_->Rotate(dx, dy);
71  if (on_light_dir_changed_) {
72  on_light_dir_changed_(light_dir_->GetCurrentDirection());
73  }
74  break;
75  }
76  case MouseEvent::WHEEL: {
77  break;
78  }
80  light_dir_->EndMouseDrag();
81  break;
82  default:
83  break;
84  }
85  }
86 
87  void Key(const KeyEvent& e) override {}
88 
89 private:
90  std::unique_ptr<rendering::LightDirectionInteractorLogic> light_dir_;
91  int mouse_down_x_ = 0;
92  int mouse_down_y_ = 0;
93  std::function<void(const Eigen::Vector3f&)> on_light_dir_changed_;
94 };
95 
97 public:
99  : ibl_(std::make_unique<rendering::IBLRotationInteractorLogic>(
100  scene, camera)) {}
101 
103  return *ibl_.get();
104  }
105 
106  void SetOnChanged(std::function<void(const rendering::Camera::Transform&)>
107  on_changed) {
108  on_rotation_changed_ = on_changed;
109  }
110 
111  void Mouse(const MouseEvent& e) override {
112  switch (e.type) {
114  mouse_down_x_ = e.x;
115  mouse_down_y_ = e.y;
116  ibl_->StartMouseDrag();
117  break;
118  case MouseEvent::DRAG: {
119  int dx = e.x - mouse_down_x_;
120  int dy = e.y - mouse_down_y_;
121  if (e.modifiers & int(KeyModifier::META)) {
122  ibl_->RotateZ(dx, dy);
123  } else {
124  ibl_->Rotate(dx, dy);
125  }
126  if (on_rotation_changed_) {
127  on_rotation_changed_(ibl_->GetCurrentRotation());
128  }
129  break;
130  }
131  case MouseEvent::WHEEL: {
132  break;
133  }
135  ibl_->EndMouseDrag();
136  break;
137  default:
138  break;
139  }
140  }
141 
142  void Key(const KeyEvent& e) override {}
143 
144 private:
145  std::unique_ptr<rendering::IBLRotationInteractorLogic> ibl_;
146  int mouse_down_x_ = 0;
147  int mouse_down_y_ = 0;
148  std::function<void(const rendering::Camera::Transform&)>
149  on_rotation_changed_;
150 };
151 
153 public:
155  : camera_controls_(std::make_unique<rendering::CameraInteractorLogic>(
156  camera, MIN_FAR_PLANE)) {}
157 
159  return *camera_controls_.get();
160  }
161 
162  void Mouse(const MouseEvent& e) override {
163  switch (e.type) {
165  last_mouse_x_ = e.x;
166  last_mouse_y_ = e.y;
167  camera_controls_->StartMouseDrag();
168  break;
169  case MouseEvent::DRAG: {
170  // Use relative movement because user may be moving
171  // with keys at the same time.
172  int dx = e.x - last_mouse_x_;
173  int dy = e.y - last_mouse_y_;
174  if (e.modifiers & int(KeyModifier::META)) {
175  // RotateZ() was not intended to be used for relative
176  // movement, so reset the mouse-down matrix first.
177  camera_controls_->ResetMouseDrag();
178  camera_controls_->RotateZ(dx, dy);
179  } else {
180  camera_controls_->RotateFly(-dx, -dy);
181  }
182  last_mouse_x_ = e.x;
183  last_mouse_y_ = e.y;
184  break;
185  }
186  case MouseEvent::WHEEL: {
187  break;
188  }
190  camera_controls_->EndMouseDrag();
191  break;
192  default:
193  break;
194  }
195  }
196 
197  void Key(const KeyEvent& e) override {
198  switch (e.type) {
199  case KeyEvent::Type::DOWN:
200  keys_down_.insert(e.key);
201  break;
202  case KeyEvent::Type::UP:
203  keys_down_.erase(e.key);
204  break;
205  }
206  }
207 
208  bool Tick(const TickEvent& e) override {
209  bool redraw = false;
210  if (!keys_down_.empty()) {
211  auto& bounds = camera_controls_->GetBoundingBox();
212  const float dist = float(0.0025 * bounds.GetExtent().norm());
213  const float angle_rad = 0.0075f;
214 
215  auto HasKey = [this](uint32_t key) -> bool {
216  return (keys_down_.find(key) != keys_down_.end());
217  };
218 
219  auto move = [this, &redraw](const Eigen::Vector3f& v) {
220  camera_controls_->MoveLocal(v);
221  redraw = true;
222  };
223  auto rotate = [this, &redraw](float angle_rad,
224  const Eigen::Vector3f& axis) {
225  camera_controls_->RotateLocal(angle_rad, axis);
226  redraw = true;
227  };
228  auto rotateZ = [this, &redraw](int dy) {
229  camera_controls_->StartMouseDrag();
230  camera_controls_->RotateZ(0, dy);
231  redraw = true;
232  };
233 
234  if (HasKey('a')) {
235  move({-dist, 0, 0});
236  }
237  if (HasKey('d')) {
238  move({dist, 0, 0});
239  }
240  if (HasKey('w')) {
241  move({0, 0, -dist});
242  }
243  if (HasKey('s')) {
244  move({0, 0, dist});
245  }
246  if (HasKey('q')) {
247  move({0, dist, 0});
248  }
249  if (HasKey('z')) {
250  move({0, -dist, 0});
251  }
252  if (HasKey('e')) {
253  rotateZ(-2);
254  }
255  if (HasKey('r')) {
256  rotateZ(2);
257  }
258  if (HasKey(KEY_UP)) {
259  rotate(angle_rad, {1, 0, 0});
260  }
261  if (HasKey(KEY_DOWN)) {
262  rotate(-angle_rad, {1, 0, 0});
263  }
264  if (HasKey(KEY_LEFT)) {
265  rotate(angle_rad, {0, 1, 0});
266  }
267  if (HasKey(KEY_RIGHT)) {
268  rotate(-angle_rad, {0, 1, 0});
269  }
270  }
271  return redraw;
272  }
273 
274 private:
275  std::unique_ptr<rendering::CameraInteractorLogic> camera_controls_;
276  int last_mouse_x_ = 0;
277  int last_mouse_y_ = 0;
278  std::set<uint32_t> keys_down_;
279 };
280 
282 protected:
284  interactor_ = r;
285  }
286 
287 public:
289  return *interactor_;
290  }
291 
292  Eigen::Vector3f GetCenterOfRotation() const {
294  }
295 
296  void SetCenterOfRotation(const Eigen::Vector3f& center) {
298  }
299 
300  void Mouse(const MouseEvent& e) override {
301  switch (e.type) {
303  mouse_down_x_ = e.x;
304  mouse_down_y_ = e.y;
305  if (e.button.button == MouseButton::LEFT) {
306  if (e.modifiers & int(KeyModifier::SHIFT)) {
307 #ifdef __APPLE__
308  if (e.modifiers & int(KeyModifier::ALT)) {
309 #else
310  if (e.modifiers & int(KeyModifier::CTRL)) {
311 #endif // __APPLE__
313  } else {
315  }
316  } else if (e.modifiers & int(KeyModifier::CTRL)) {
317  state_ = State::PAN;
318  } else if (e.modifiers & int(KeyModifier::META)) {
320  } else {
322  }
323  } else if (e.button.button == MouseButton::RIGHT) {
324  state_ = State::PAN;
325  }
327  break;
328  case MouseEvent::DRAG: {
329  int dx = e.x - mouse_down_x_;
330  int dy = e.y - mouse_down_y_;
331  switch (state_) {
332  case State::NONE:
333  break;
334  case State::PAN:
335  interactor_->Pan(dx, dy);
336  break;
337  case State::DOLLY:
338  interactor_->Dolly(dy,
340  DragType::MOUSE);
341  break;
342  case State::ROTATE_XY:
343  interactor_->Rotate(dx, dy);
344  break;
345  case State::ROTATE_Z:
346  interactor_->RotateZ(dx, dy);
347  break;
348  }
350  break;
351  }
352  case MouseEvent::WHEEL: {
353  interactor_->Dolly(2.0f * e.wheel.dy,
354  e.wheel.isTrackpad
356  DragType::TWO_FINGER
358  DragType::WHEEL);
359  break;
360  }
364  break;
365  default:
366  break;
367  }
368  }
369 
370  void Key(const KeyEvent& e) override {}
371 
372 protected:
374  int mouse_down_x_ = 0;
375  int mouse_down_y_ = 0;
376 
377  enum class State { NONE, PAN, DOLLY, ROTATE_XY, ROTATE_Z };
379 };
380 
382  using Super = RotationInteractor;
383 
384 public:
386  rendering::Camera* camera)
387  : RotationInteractor(),
388  rotation_(new rendering::ModelInteractorLogic(
389  scene, camera, MIN_FAR_PLANE)) {
390  SetInteractor(rotation_.get());
391  }
392 
393  void Mouse(const MouseEvent& e) override { Super::Mouse(e); }
394 
395 private:
396  std::unique_ptr<rendering::ModelInteractorLogic> rotation_;
397 };
398 
400  using Super = RotationInteractor;
401 
402 public:
404  rendering::Camera* camera)
405  : camera_controls_(std::make_unique<rendering::CameraInteractorLogic>(
406  camera, MIN_FAR_PLANE)),
407  scene_(scene) {
408  SetInteractor(camera_controls_.get());
409  }
410 
411  void Mouse(const MouseEvent& e) override {
412  switch (e.type) {
414  if (e.button.count == 2 &&
415  e.button.button == MouseButton::LEFT) {
416  int x = e.x;
417  int y = e.y;
418  scene_->GetRenderer().RenderToDepthImage(
419  scene_->GetView(), scene_->GetScene(),
420  [x, y, this](std::shared_ptr<geometry::Image> img) {
421  ChangeCenterOfRotation(img, x, y);
422  });
423  } else {
424  Super::Mouse(e);
425  }
426  break;
427  }
428  case MouseEvent::DRAG:
430  default:
431  Super::Mouse(e);
432  break;
433  case MouseEvent::WHEEL: {
434  if (e.modifiers == int(KeyModifier::SHIFT)) {
435  camera_controls_->Zoom(
436  e.wheel.dy,
437  e.wheel.isTrackpad
439  DragType::TWO_FINGER
441  DragType::WHEEL);
442  } else {
443  Super::Mouse(e);
444  }
445  break;
446  }
447  }
448  }
449 
450 private:
451  std::unique_ptr<rendering::CameraInteractorLogic> camera_controls_;
453 
454  void ChangeCenterOfRotation(std::shared_ptr<geometry::Image> depth_img,
455  int x,
456  int y) {
457  const int radius_px = 2; // should be even; total size is 2*r+1
458  float far_z = 0.999999f; // 1.0 - epsilon
459  float win_z = *depth_img->PointerAt<float>(x, y);
460  if (win_z >= far_z) {
461  for (int v = y - radius_px; v < y + radius_px; ++v) {
462  for (int u = x - radius_px; u < x + radius_px; ++u) {
463  float z = *depth_img->PointerAt<float>(x, y);
464  win_z = std::min(win_z, z);
465  }
466  }
467  }
468 
469  if (win_z < far_z) {
470  auto vp = scene_->GetView()->GetViewport();
471  auto point = scene_->GetCamera()->Unproject(
472  float(x), float(vp[3] - y), win_z, float(vp[2]),
473  float(vp[3]));
475  interactor_->Rotate(0, 0); // update now
476  }
477  }
478 };
479 
481  using Super = RotationInteractor;
482 
483 public:
485  rendering::Camera* camera)
486  : RotateCameraInteractor(scene, camera),
487  camera_controls_(
488  std::make_unique<rendering::CameraSphereInteractorLogic>(
489  camera, MIN_FAR_PLANE)) {
490  SetInteractor(camera_controls_.get());
491  }
492 
493 private:
494  std::unique_ptr<rendering::CameraInteractorLogic> camera_controls_;
495 };
496 
499 
500 public:
502  rendering::Camera* camera)
503  : Super(scene, camera),
504  pick_(new PickPointsInteractor(scene, camera)) {}
505 
506  void SetViewSize(const Size& size) {
507  GetMatrixInteractor().SetViewSize(size.width, size.height);
508  pick_->GetMatrixInteractor().SetViewSize(size.width, size.height);
509  }
510 
512  const std::vector<SceneWidget::PickableGeometry>& geometry) {
513  pick_->SetPickableGeometry(geometry);
514  }
515 
516  void SetPickablePointSize(int px) { pick_->SetPointSize(px); }
517 
519  std::function<void(
520  const std::map<
521  std::string,
522  std::vector<std::pair<size_t, Eigen::Vector3d>>>&,
523  int)> on_picked) {
524  pick_->SetOnPointsPicked(on_picked);
525  }
526 
528  std::function<void(const std::vector<Eigen::Vector2i>&)> on_ui) {
529  pick_->SetOnUIChanged(on_ui);
530  }
531 
532  void SetOnStartedPolygonPicking(std::function<void()> on_poly_pick) {
533  pick_->SetOnStartedPolygonPicking(on_poly_pick);
534  }
535 
536  void DoPolygonPick() { pick_->DoPick(); }
537 
538  void ClearPolygonPick() { pick_->ClearPick(); }
539 
540  void SetNeedsRedraw() { pick_->SetNeedsRedraw(); }
541 
542  void Mouse(const MouseEvent& e) override {
543  if (e.modifiers & int(KeyModifier::CTRL)) {
544  pick_->Mouse(e);
545  } else {
546  Super::Mouse(e);
547  pick_->SetNeedsRedraw();
548  }
549  }
550 
551  void Key(const KeyEvent& e) override { pick_->Key(e); }
552 
553 private:
554  std::unique_ptr<PickPointsInteractor> pick_;
555 };
556 
557 // ----------------------------------------------------------------------------
558 class Interactors {
559 public:
561  : rotate_(std::make_unique<RotateCameraInteractor>(scene, camera)),
562  rotate_sphere_(std::make_unique<RotateCameraSphereInteractor>(
563  scene, camera)),
564  fly_(std::make_unique<FlyInteractor>(camera)),
565  sun_(std::make_unique<RotateSunInteractor>(scene, camera)),
566  ibl_(std::make_unique<RotateIBLInteractor>(scene->GetScene(),
567  camera)),
568  model_(std::make_unique<RotateModelInteractor>(scene, camera)),
569  pick_(std::make_unique<PickInteractor>(scene, camera)) {
570  current_ = rotate_.get();
571  }
572 
573  void SetViewSize(const Size& size) {
574  rotate_->GetMatrixInteractor().SetViewSize(size.width, size.height);
575  rotate_sphere_->GetMatrixInteractor().SetViewSize(size.width,
576  size.height);
577  fly_->GetMatrixInteractor().SetViewSize(size.width, size.height);
578  sun_->GetMatrixInteractor().SetViewSize(size.width, size.height);
579  ibl_->GetMatrixInteractor().SetViewSize(size.width, size.height);
580  model_->GetMatrixInteractor().SetViewSize(size.width, size.height);
581  pick_->SetViewSize(size);
582  }
583 
584  void SetBoundingBox(const ccBBox& bounds) {
585  rotate_->GetMatrixInteractor().SetBoundingBox(bounds);
586  rotate_sphere_->GetMatrixInteractor().SetBoundingBox(bounds);
587  fly_->GetMatrixInteractor().SetBoundingBox(bounds);
588  sun_->GetMatrixInteractor().SetBoundingBox(bounds);
589  ibl_->GetMatrixInteractor().SetBoundingBox(bounds);
590  model_->GetMatrixInteractor().SetBoundingBox(bounds);
591  pick_->GetMatrixInteractor().SetBoundingBox(bounds);
592  }
593 
594  Eigen::Vector3f GetCenterOfRotation() const {
595  if (GetControls() == SceneWidget::Controls::ROTATE_CAMERA_SPHERE) {
596  return rotate_sphere_->GetCenterOfRotation();
597  } else {
598  return rotate_->GetCenterOfRotation();
599  }
600  }
601 
602  void SetCenterOfRotation(const Eigen::Vector3f& center) {
603  rotate_->SetCenterOfRotation(center);
604  rotate_sphere_->SetCenterOfRotation(center);
605  }
606 
608  std::function<void(const Eigen::Vector3f&)> onChanged) {
609  sun_->SetOnSunLightChanged(onChanged);
610  }
611 
612  void SetSunInteractorEnabled(bool enable) {
613  sun_interactor_enabled_ = enable;
614  }
615 
617  const std::vector<SceneWidget::PickableGeometry>& geometry) {
618  pick_->SetPickableGeometry(geometry);
619  }
620 
621  void SetPickablePointSize(int px) { pick_->SetPickablePointSize(px); }
622 
624  std::function<void(
625  const std::map<
626  std::string,
627  std::vector<std::pair<size_t, Eigen::Vector3d>>>&,
628  int)> on_picked) {
629  pick_->SetOnPointsPicked(on_picked);
630  }
631 
632  void SetOnStartedPolygonPicking(std::function<void()> on_poly_pick) {
633  pick_->SetOnStartedPolygonPicking(on_poly_pick);
634  }
635 
636  void DoPolygonPick() { pick_->DoPolygonPick(); }
637 
638  void ClearPolygonPick() { pick_->ClearPolygonPick(); }
639 
640  void SetPickNeedsRedraw() { pick_->SetNeedsRedraw(); }
641 
643  std::function<void(const std::vector<Eigen::Vector2i>&)> on_ui) {
644  pick_->SetOnInteractorUIUpdated(on_ui);
645  }
646 
648  if (current_ == rotate_sphere_.get()) {
649  return SceneWidget::Controls::ROTATE_CAMERA_SPHERE;
650  } else if (current_ == fly_.get()) {
651  return SceneWidget::Controls::FLY;
652  } else if (current_ == sun_.get()) {
653  return SceneWidget::Controls::ROTATE_SUN;
654  } else if (current_ == ibl_.get()) {
655  return SceneWidget::Controls::ROTATE_IBL;
656  } else if (current_ == model_.get()) {
657  return SceneWidget::Controls::ROTATE_MODEL;
658  } else if (current_ == pick_.get()) {
659  return SceneWidget::Controls::PICK_POINTS;
660  } else {
661  return SceneWidget::Controls::ROTATE_CAMERA;
662  }
663  }
664 
666  switch (mode) {
667  case SceneWidget::Controls::ROTATE_CAMERA:
668  current_ = rotate_.get();
669  break;
670  case SceneWidget::Controls::ROTATE_CAMERA_SPHERE:
671  current_ = rotate_sphere_.get();
672  break;
673  case SceneWidget::Controls::FLY:
674  current_ = fly_.get();
675  break;
676  case SceneWidget::Controls::ROTATE_SUN:
677  current_ = sun_.get();
678  break;
679  case SceneWidget::Controls::ROTATE_IBL:
680  current_ = ibl_.get();
681  break;
682  case SceneWidget::Controls::ROTATE_MODEL:
683  current_ = model_.get();
684  break;
685  case SceneWidget::Controls::PICK_POINTS:
686  current_ = pick_.get();
687  break;
688  }
689  }
690 
691  void Mouse(const MouseEvent& e) {
692  if (current_ == rotate_.get() && sun_interactor_enabled_) {
693  if (e.type == MouseEvent::Type::BUTTON_DOWN &&
694  (e.button.button == MouseButton::MIDDLE ||
695  e.modifiers == int(KeyModifier::ALT))) {
696  override_ = sun_.get();
697  }
698  }
699 
700  if (override_) {
701  override_->Mouse(e);
702  } else if (current_) {
703  current_->Mouse(e);
704  }
705 
706  if (override_ && e.type == MouseEvent::Type::BUTTON_UP) {
707  override_ = nullptr;
708  }
709  }
710 
711  void Key(const KeyEvent& e) {
712  if (current_) {
713  current_->Key(e);
714  }
715  }
716 
718  if (current_) {
719  if (current_->Tick(e)) {
721  }
722  }
724  }
725 
726 private:
727  bool sun_interactor_enabled_ = true;
728 
729  std::unique_ptr<RotateCameraInteractor> rotate_;
730  std::unique_ptr<RotateCameraSphereInteractor> rotate_sphere_;
731  std::unique_ptr<FlyInteractor> fly_;
732  std::unique_ptr<RotateSunInteractor> sun_;
733  std::unique_ptr<RotateIBLInteractor> ibl_;
734  std::unique_ptr<RotateModelInteractor> model_;
735  std::unique_ptr<PickInteractor> pick_;
736 
737  SceneWidget::MouseInteractor* current_ = nullptr;
738  SceneWidget::MouseInteractor* override_ = nullptr;
739 };
740 
741 // ----------------------------------------------------------------------------
742 namespace {
743 static int g_next_button_id = 1;
744 } // namespace
745 
747  std::string id_;
748  std::shared_ptr<rendering::CloudViewerScene> scene_;
750  std::shared_ptr<Interactors> controls_;
751  std::function<void(const Eigen::Vector3f&)> on_light_dir_changed_;
752  std::function<void(rendering::Camera*)> on_camera_changed_;
753  int buttons_down_ = 0;
754  double last_fast_time_ = 0.0;
755  bool frame_rect_changed_ = false;
757  bool scene_caching_enabled_ = false;
758  std::vector<Eigen::Vector2i> ui_lines_;
759  std::unordered_set<std::shared_ptr<Label3D>> labels_3d_;
760  struct {
761  Eigen::Matrix3d matrix;
762  float width = 1.0f;
763  float height = 1.0f;
764  bool is_using = false;
765  } intrinsics_;
766 
768  float orig_aspect = intrinsics_.width / intrinsics_.height;
769  float aspect = float(frame.width) / float(frame.height);
770  Eigen::Matrix3d scale;
771  if (aspect < 1.0f) {
772  scale << 1.0, 0.0, 0.0, 0.0, (aspect / orig_aspect), 0.0, 0.0, 0.0,
773  1.0;
774  } else {
775  scale << (orig_aspect / aspect), 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
776  1.0;
777  }
778  Eigen::Matrix3d m = intrinsics_.matrix * scale;
779  auto* camera = scene_->GetCamera();
780  camera->SetProjection(m, rendering::Camera::CalcNearPlane(),
781  rendering::Camera::CalcFarPlane(*camera, bounds_),
782  intrinsics_.width, intrinsics_.height);
783  }
784 
785  void UpdateFarPlane(const Rect& frame, float verticalFoV) {
786  float aspect = 1.0f;
787  if (frame.height > 0) {
788  aspect = float(frame.width) / float(frame.height);
789  }
790  auto* camera = scene_->GetCamera();
791  auto far_v = rendering::Camera::CalcFarPlane(*camera, bounds_);
792  camera->SetProjection(verticalFoV, aspect,
795  }
796 };
797 
798 SceneWidget::SceneWidget() : impl_(new Impl()) {
799  impl_->id_ = std::string("SceneWidget##widget3d_") +
800  std::to_string(g_next_button_id++);
801 }
802 
804  SetScene(nullptr); // will do any necessary cleanup
805 }
806 
807 void SceneWidget::SetFrame(const Rect& f) {
808  // Early exit if frame hasn't changed because changing frame size causes GPU
809  // memory re-allocations that are best avoided if unecessary
810  auto old_frame = GetFrame();
811  if (f.width == old_frame.width && f.height == old_frame.height) return;
812 
813  Super::SetFrame(f);
814 
815  impl_->controls_->SetViewSize(Size(f.width, f.height));
816 
817  // We need to update the viewport and camera, but we can't do it here
818  // because we need to know the window height to convert the frame
819  // to OpenGL coordinates. We will actually do the updating in Draw().
820  impl_->frame_rect_changed_ = true;
821 }
822 
823 void SceneWidget::SetupCamera(float verticalFoV,
824  const ccBBox& scene_bounds,
825  const Eigen::Vector3f& center_of_rotation) {
826  impl_->intrinsics_.is_using = false;
827  impl_->bounds_ = scene_bounds;
828  impl_->controls_->SetBoundingBox(scene_bounds);
829  impl_->controls_->SetCenterOfRotation(center_of_rotation);
830 
831  GoToCameraPreset(CameraPreset::PLUS_Z); // default OpenGL view
832 
833  impl_->UpdateFarPlane(GetFrame(), verticalFoV);
834 }
835 
837  const Eigen::Matrix4d& extrinsic,
838  const ccBBox& scene_bounds) {
839  SetupCamera(intrinsic.intrinsic_matrix_, extrinsic, intrinsic.width_,
840  intrinsic.height_, scene_bounds);
841 }
842 
843 void SceneWidget::SetupCamera(const Eigen::Matrix3d& intrinsic,
844  const Eigen::Matrix4d& extrinsic,
845  int intrinsic_width_px,
846  int intrinsic_height_px,
847  const ccBBox& scene_bounds) {
848  impl_->intrinsics_.is_using = true;
849  impl_->intrinsics_.matrix = intrinsic;
850  impl_->intrinsics_.width = intrinsic_width_px;
851  impl_->intrinsics_.height = intrinsic_height_px;
852  impl_->bounds_ = scene_bounds;
853  impl_->controls_->SetBoundingBox(scene_bounds);
854 
855  auto* camera = GetCamera();
857  *camera, intrinsic, extrinsic, intrinsic_width_px,
858  intrinsic_height_px, scene_bounds);
859 
860  // We need to calculate the center of rotation (rather than specifying it
861  // because the intrinsic/extrinsic matrices define a position for the camera
862  // and the center of rotation needs to be visually consistent.
863  Eigen::Vector3f forward = camera->GetForwardVector();
864  Eigen::Vector3f pos = camera->GetPosition();
865  Eigen::Vector3f toCenter = scene_bounds.GetCenter().cast<float>() - pos;
866  float dist = toCenter.dot(forward);
867  Eigen::Vector3f cor = pos + dist * forward;
868  impl_->controls_->SetCenterOfRotation(cor);
869 }
870 
871 void SceneWidget::LookAt(const Eigen::Vector3f& center,
872  const Eigen::Vector3f& eye,
873  const Eigen::Vector3f& up) {
874  GetCamera()->LookAt(center, eye, up);
875  impl_->controls_->SetCenterOfRotation(center);
876  impl_->UpdateFarPlane(GetFrame(), GetCamera()->GetFieldOfView());
877 }
878 
879 Eigen::Vector3f SceneWidget::GetCenterOfRotation() const {
880  return impl_->controls_->GetCenterOfRotation();
881 }
882 
883 void SceneWidget::SetCenterOfRotation(const Eigen::Vector3f& center) {
884  impl_->controls_->SetCenterOfRotation(center);
885 }
886 
888  std::function<void(rendering::Camera*)> on_cam_changed) {
889  impl_->on_camera_changed_ = on_cam_changed;
890 }
891 
893  std::function<void(const Eigen::Vector3f&)> on_dir_changed) {
894  impl_->on_light_dir_changed_ = on_dir_changed;
895  impl_->controls_->SetOnSunLightChanged([this](const Eigen::Vector3f& dir) {
896  impl_->scene_->GetScene()->SetSunLightDirection(dir);
897  if (impl_->on_light_dir_changed_) {
898  impl_->on_light_dir_changed_(dir);
899  }
900  });
901 }
902 
904  impl_->controls_->SetSunInteractorEnabled(enable);
905 }
906 
908  const std::vector<PickableGeometry>& geometry) {
909  impl_->controls_->SetPickableGeometry(geometry);
910 }
911 
913  impl_->controls_->SetPickablePointSize(px);
914 }
915 
917  std::function<
918  void(const std::map<
919  std::string,
920  std::vector<std::pair<size_t, Eigen::Vector3d>>>&,
921  int)> on_picked) {
922  impl_->controls_->SetOnPointsPicked(on_picked);
923 }
924 
925 void SceneWidget::SetScene(std::shared_ptr<rendering::CloudViewerScene> scene) {
926  impl_->scene_ = scene;
927  if (impl_->scene_) {
928  auto view = impl_->scene_->GetView();
929  impl_->controls_ = std::make_shared<Interactors>(impl_->scene_.get(),
930  view->GetCamera());
931  impl_->controls_->SetOnInteractorUIUpdated(
932  [this](const std::vector<Eigen::Vector2i>& lines) {
933  impl_->ui_lines_ = lines;
934  ForceRedraw();
935  });
936  }
937 }
938 
940  std::function<void()> on_poly_pick) {
941  impl_->controls_->SetOnStartedPolygonPicking(on_poly_pick);
942 }
943 
945  switch (action) {
947  impl_->controls_->ClearPolygonPick();
948  break;
950  impl_->controls_->DoPolygonPick();
951  break;
952  };
953 }
954 
955 std::shared_ptr<rendering::CloudViewerScene> SceneWidget::GetScene() const {
956  return impl_->scene_;
957 }
958 
960  if (impl_->scene_) {
961  return impl_->scene_->GetView();
962  } else {
963  return nullptr;
964  }
965 }
966 
968  if (mode == Controls::ROTATE_CAMERA &&
969  impl_->controls_->GetControls() == Controls::FLY) {
970  impl_->controls_->SetControls(mode);
971  // If we're going from fly to standard rotate obj, we need to
972  // adjust the center of rotation or it will jump to a different
973  // matrix rather abruptly. The center of rotation is used for the
974  // panning distance so that the cursor stays in roughly the same
975  // position as the user moves the mouse. Use the distance to the
976  // center of the model, which should be reasonable.
977  auto camera = GetCamera();
978  Eigen::Vector3f to_center = impl_->bounds_.GetCenter().cast<float>() -
979  camera->GetPosition();
980  Eigen::Vector3f forward = camera->GetForwardVector();
981  Eigen::Vector3f center =
982  camera->GetPosition() + to_center.norm() * forward;
983  impl_->controls_->SetCenterOfRotation(center);
984  } else {
985  impl_->controls_->SetControls(mode);
986  }
987 }
988 
990  impl_->scene_caching_enabled_ = enable;
991  if (!enable) {
992  impl_->scene_->GetScene()->SetViewActive(impl_->scene_->GetViewId(),
993  true);
994  }
995 }
996 
998  // ForceRedraw only applies when scene caching is enabled
999  if (!impl_->scene_caching_enabled_) return;
1000 
1001  impl_->scene_->GetScene()->SetRenderOnce(impl_->scene_->GetViewId());
1002  impl_->controls_->SetPickNeedsRedraw();
1003 }
1004 
1006  auto currentQuality = GetRenderQuality();
1007  if (currentQuality != quality) {
1008  impl_->current_render_quality_ = quality;
1009  if (quality == Quality::FAST) {
1010  impl_->scene_->SetLOD(rendering::CloudViewerScene::LOD::FAST);
1011  if (impl_->scene_caching_enabled_) {
1012  impl_->scene_->GetScene()->SetViewActive(
1013  impl_->scene_->GetViewId(), true);
1014  }
1015  } else {
1016  impl_->scene_->SetLOD(
1018  if (impl_->scene_caching_enabled_) {
1019  impl_->scene_->GetScene()->SetRenderOnce(
1020  impl_->scene_->GetViewId());
1021  }
1022  }
1023  }
1024 }
1025 
1027  return impl_->current_render_quality_;
1028 }
1029 
1031  // To get the eye position we move maxDim away from the center in the
1032  // appropriate direction. We cannot simply use maxDim as that value
1033  // for that dimension, because the model may not be centered around
1034  // (0, 0, 0), and this will result in the far plane being not being
1035  // far enough and clipping the model. To test, use
1036  // https://docs.google.com/uc?export=download&id=0B-ePgl6HF260ODdvT09Xc1JxOFE
1037  float max_dim = float(1.25 * impl_->bounds_.GetMaxExtent());
1038  Eigen::Vector3f center = impl_->bounds_.GetCenter().cast<float>();
1039  Eigen::Vector3f eye, up;
1040  switch (preset) {
1041  case CameraPreset::PLUS_X: {
1042  eye = Eigen::Vector3f(center.x() + max_dim, center.y(), center.z());
1043  up = Eigen::Vector3f(0, 1, 0);
1044  break;
1045  }
1046  case CameraPreset::PLUS_Y: {
1047  eye = Eigen::Vector3f(center.x(), center.y() + max_dim, center.z());
1048  up = Eigen::Vector3f(1, 0, 0);
1049  break;
1050  }
1051  case CameraPreset::PLUS_Z: {
1052  eye = Eigen::Vector3f(center.x(), center.y(), center.z() + max_dim);
1053  up = Eigen::Vector3f(0, 1, 0);
1054  break;
1055  }
1056  }
1057  GetCamera()->LookAt(center, eye, up);
1058  impl_->controls_->SetCenterOfRotation(center);
1059  ForceRedraw();
1060 }
1061 
1062 rendering::Camera* SceneWidget::GetCamera() const {
1063  return impl_->scene_->GetCamera();
1064 }
1065 
1066 std::shared_ptr<Label3D> SceneWidget::AddLabel(const Eigen::Vector3f& pos,
1067  const char* text) {
1068  auto l = std::make_shared<Label3D>(pos, text);
1069  impl_->labels_3d_.insert(l);
1070  return l;
1071 }
1072 
1073 void SceneWidget::RemoveLabel(std::shared_ptr<Label3D> label) {
1074  auto liter = impl_->labels_3d_.find(label);
1075  if (liter != impl_->labels_3d_.end()) {
1076  impl_->labels_3d_.erase(liter);
1077  }
1078 }
1079 
1080 void SceneWidget::ClearLabels() { impl_->labels_3d_.clear(); }
1081 
1083  const auto f = GetFrame();
1084 
1085  // If the widget has changed size we need to update the viewport and the
1086  // camera. We can't do it in SetFrame() because we need to know the height
1087  // of the window to convert to OpenGL coordinates for the viewport.
1088  if (impl_->frame_rect_changed_) {
1089  impl_->frame_rect_changed_ = false;
1090 
1091  impl_->controls_->SetViewSize(Size(f.width, f.height));
1092  // GUI has origin of Y axis at top, but renderer has it at bottom
1093  // so we need to convert coordinates.
1094  int y = context.screenHeight - (f.height + f.y);
1095 
1096  impl_->scene_->SetViewport(f.x, y, f.width, f.height);
1097 
1098  if (impl_->intrinsics_.is_using) {
1099  if (f.height > 0) {
1100  impl_->UpdateFromIntrinsicMatrix(f);
1101  }
1102  } else {
1103  float aspect = 1.0f;
1104  if (f.height > 0) {
1105  aspect = float(f.width) / float(f.height);
1106  }
1107  auto* camera = GetCamera();
1108  camera->SetProjection(camera->GetFieldOfView(), aspect,
1109  camera->GetNear(), camera->GetFar(),
1110  camera->GetFieldOfViewType());
1111  }
1112 
1113  impl_->controls_->SetPickNeedsRedraw();
1114  ForceRedraw();
1115  }
1116 
1117  // The scene will be rendered to texture, so all we need to do is
1118  // draw the image. This is just a pass-through, and the ImGuiFilamentBridge
1119  // will blit the texture.
1120  ImGui::SetNextWindowPos(ImVec2(float(f.x), float(f.y)));
1121  ImGui::SetNextWindowSize(ImVec2(float(f.width), float(f.height)));
1122  ImGui::Begin(impl_->id_.c_str(), nullptr,
1123  ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs |
1124  ImGuiWindowFlags_NoNav |
1125  ImGuiWindowFlags_NoBackground);
1126 
1127  auto render_tex = impl_->scene_->GetView()->GetColorBuffer();
1128  ImTextureID image_id = reinterpret_cast<ImTextureID>(render_tex.GetId());
1129  ImGui::Image(image_id, ImVec2(f.width, f.height), ImVec2(0.0f, 1.0f),
1130  ImVec2(1.0f, 0.0f));
1131 
1132  if (!impl_->labels_3d_.empty()) {
1133  // Draw each text label
1134  for (const auto& l : impl_->labels_3d_) {
1135  auto ndc = GetCamera()->GetNDC(l->GetPosition());
1136  ndc += Eigen::Vector2f::Ones();
1137  ndc *= 0.5f;
1138  ndc.x() *= f.width;
1139  ndc.y() *= f.height;
1140  ImGui::SetCursorScreenPos(
1141  ImVec2(ndc.x() - f.x, f.height - ndc.y() - f.y));
1142  auto color = l->GetTextColor();
1143  ImGui::TextColored({color.GetRed(), color.GetGreen(),
1144  color.GetBlue(), color.GetAlpha()},
1145  "%s", l->GetText());
1146  }
1147  }
1148 
1149  // Draw any interactor UI
1150  if (!impl_->ui_lines_.empty()) {
1151  ImDrawList* draw_list = ImGui::GetWindowDrawList();
1152  auto ui_color = colorToImguiRGBA(Color(1.0f, 0.0f, 1.0f, 1.0f));
1153  for (size_t i = 0; i < impl_->ui_lines_.size() - 1; i += 2) {
1154  auto& p0 = impl_->ui_lines_[i];
1155  auto& p1 = impl_->ui_lines_[i + 1];
1156  draw_list->AddLine({float(p0.x()), float(p0.y())},
1157  {float(p1.x()), float(p1.y())}, ui_color, 2);
1158  }
1159  }
1160 
1161  ImGui::End();
1162 
1163  return Widget::DrawResult::NONE;
1164 }
1165 
1167  // Lower render quality while rotating, since we will be redrawing
1168  // frequently. This will give a snappier feel to mouse movements,
1169  // especially for point clouds, which are a little slow.
1170  if (e.type != MouseEvent::MOVE) {
1172  }
1173  // Render quality will revert back to BEST after a short delay,
1174  // unless the user starts rotating again, or is scroll-wheeling.
1175  if (e.type == MouseEvent::DRAG || e.type == MouseEvent::WHEEL) {
1176  impl_->last_fast_time_ = Application::GetInstance().Now();
1177  }
1178 
1179  if (e.type == MouseEvent::BUTTON_DOWN) {
1180  impl_->buttons_down_ |= int(e.button.button);
1181  } else if (e.type == MouseEvent::BUTTON_UP) {
1182  impl_->buttons_down_ &= ~int(e.button.button);
1183  }
1184 
1185  auto& frame = GetFrame();
1186  MouseEvent local = e;
1187  local.x -= frame.x;
1188  local.y -= frame.y;
1189  impl_->controls_->Mouse(local);
1190 
1191  if (impl_->on_camera_changed_) {
1192  impl_->on_camera_changed_(GetCamera());
1193  }
1194 
1196 }
1197 
1199  impl_->controls_->Key(e);
1200 
1201  if (impl_->on_camera_changed_) {
1202  impl_->on_camera_changed_(GetCamera());
1203  }
1205 }
1206 
1208  auto result = impl_->controls_->Tick(e);
1209  // If Tick() redraws, then a key is down. Make sure we are rendering
1210  // FAST and mark the time so that we don't timeout and revert back
1211  // to slow rendering before the key up happens.
1214  impl_->last_fast_time_ = Application::GetInstance().Now();
1215  }
1216  if (impl_->buttons_down_ == 0 && GetRenderQuality() == Quality::FAST) {
1217  double now = Application::GetInstance().Now();
1218  if (now - impl_->last_fast_time_ > DELAY_FOR_BEST_RENDERING_SECS) {
1221  }
1222  }
1223  return result;
1224 }
1225 
1226 } // namespace gui
1227 } // namespace visualization
1228 } // namespace cloudViewer
Rect frame
int width
int size
int height
math::float4 color
core::Tensor result
Definition: VtkUtils.cpp:76
Bounding box structure.
Definition: ecvBBox.h:25
virtual Eigen::Vector3d GetCenter() const override
Returns the center of the geometry coordinates.
Definition: ecvBBox.h:87
Contains the pinhole camera intrinsic parameters.
bool Tick(const TickEvent &e) override
void Mouse(const MouseEvent &e) override
rendering::MatrixInteractorLogic & GetMatrixInteractor() override
void Key(const KeyEvent &e) override
void SetBoundingBox(const ccBBox &bounds)
void SetOnPointsPicked(std::function< void(const std::map< std::string, std::vector< std::pair< size_t, Eigen::Vector3d >>> &, int)> on_picked)
void SetCenterOfRotation(const Eigen::Vector3f &center)
void SetOnSunLightChanged(std::function< void(const Eigen::Vector3f &)> onChanged)
Interactors(rendering::CloudViewerScene *scene, rendering::Camera *camera)
SceneWidget::Controls GetControls() const
Widget::DrawResult Tick(const TickEvent &e)
void SetOnInteractorUIUpdated(std::function< void(const std::vector< Eigen::Vector2i > &)> on_ui)
void SetPickableGeometry(const std::vector< SceneWidget::PickableGeometry > &geometry)
void SetControls(SceneWidget::Controls mode)
void SetOnStartedPolygonPicking(std::function< void()> on_poly_pick)
void SetPickableGeometry(const std::vector< SceneWidget::PickableGeometry > &geometry)
void SetOnPointsPicked(std::function< void(const std::map< std::string, std::vector< std::pair< size_t, Eigen::Vector3d >>> &, int)> on_picked)
PickInteractor(rendering::CloudViewerScene *scene, rendering::Camera *camera)
void SetOnInteractorUIUpdated(std::function< void(const std::vector< Eigen::Vector2i > &)> on_ui)
void Mouse(const MouseEvent &e) override
void Key(const KeyEvent &e) override
void SetOnStartedPolygonPicking(std::function< void()> on_poly_pick)
RotateCameraInteractor(rendering::CloudViewerScene *scene, rendering::Camera *camera)
RotateCameraSphereInteractor(rendering::CloudViewerScene *scene, rendering::Camera *camera)
void SetOnChanged(std::function< void(const rendering::Camera::Transform &)> on_changed)
rendering::MatrixInteractorLogic & GetMatrixInteractor() override
RotateIBLInteractor(rendering::Scene *scene, rendering::Camera *camera)
Definition: SceneWidget.cpp:98
void Mouse(const MouseEvent &e) override
RotateModelInteractor(rendering::CloudViewerScene *scene, rendering::Camera *camera)
RotateSunInteractor(rendering::CloudViewerScene *scene, rendering::Camera *camera)
Definition: SceneWidget.cpp:46
rendering::MatrixInteractorLogic & GetMatrixInteractor() override
Definition: SceneWidget.cpp:51
void SetOnSunLightChanged(std::function< void(const Eigen::Vector3f &)> on_changed)
Definition: SceneWidget.cpp:55
void Mouse(const MouseEvent &e) override
Definition: SceneWidget.cpp:60
rendering::MatrixInteractorLogic & GetMatrixInteractor() override
void SetInteractor(rendering::RotationInteractorLogic *r)
rendering::RotationInteractorLogic * interactor_
void SetCenterOfRotation(const Eigen::Vector3f &center)
void Mouse(const MouseEvent &e) override
void SetOnSunDirectionChanged(std::function< void(const Eigen::Vector3f &)> on_dir_changed)
std::shared_ptr< Label3D > AddLabel(const Eigen::Vector3f &pos, const char *text)
std::shared_ptr< rendering::CloudViewerScene > GetScene() const
Widget::EventResult Mouse(const MouseEvent &e) override
void SetOnPointsPicked(std::function< void(const std::map< std::string, std::vector< std::pair< size_t, Eigen::Vector3d >>> &, int)> on_picked)
void SetOnCameraChanged(std::function< void(visualization::rendering::Camera *)> on_cam_changed)
void SetCenterOfRotation(const Eigen::Vector3f &center)
Widget::DrawResult Draw(const DrawContext &context) override
void SetScene(std::shared_ptr< rendering::CloudViewerScene > scene)
void DoPolygonPick(PolygonPickAction action)
void SetFrame(const Rect &f) override
void SetPickableGeometry(const std::vector< PickableGeometry > &geometry)
void RemoveLabel(std::shared_ptr< Label3D > label)
Widget::DrawResult Tick(const TickEvent &e) override
void SetOnStartedPolygonPicking(std::function< void()> on_poly_pick)
void SetupCamera(float verticalFoV, const ccBBox &scene_bounds, const Eigen::Vector3f &center_of_rotation)
void LookAt(const Eigen::Vector3f &center, const Eigen::Vector3f &eye, const Eigen::Vector3f &up)
Widget::EventResult Key(const KeyEvent &e) override
virtual void SetFrame(const Rect &f)
Definition: Widget.cpp:53
virtual const Rect & GetFrame() const
Returns the frame size in pixels.
Definition: Widget.cpp:51
virtual Eigen::Vector2f GetNDC(const Eigen::Vector3f &pt) const =0
virtual Eigen::Vector3f Unproject(float x, float y, float z, float view_width, float view_height) const =0
Eigen::Transform< float, 3, Eigen::Affine > Transform
Definition: Camera.h:29
static void SetupCameraAsPinholeCamera(rendering::Camera &camera, const Eigen::Matrix3d &intrinsic, const Eigen::Matrix4d &extrinsic, int intrinsic_width_px, int intrinsic_height_px, const ccBBox &scene_bounds)
Definition: Camera.cpp:35
static float CalcNearPlane()
Returns a good value for the near plane.
Definition: Camera.cpp:47
static float CalcFarPlane(const rendering::Camera &camera, const ccBBox &scene_bounds)
Definition: Camera.cpp:49
virtual void LookAt(const Eigen::Vector3f &center, const Eigen::Vector3f &eye, const Eigen::Vector3f &up)=0
virtual void RotateZ(int dx, int dy)
Rotates about the forward axis of the matrix.
virtual std::array< int, 4 > GetViewport() const =0
int min(int a, int b)
Definition: cutil_math.h:53
ImGuiContext * context
Definition: Window.cpp:76
static double dist(double x1, double y1, double x2, double y2)
Definition: lsd.c:207
uint32_t colorToImguiRGBA(const Color &color)
Definition: Util.cpp:25
static const double DELAY_FOR_BEST_RENDERING_SECS
Definition: SceneWidget.cpp:42
static const double MIN_FAR_PLANE
Definition: SceneWidget.cpp:40
Generic file read and write utility for python interface.
std::string to_string(const T &n)
Definition: Common.h:20
Definition: Eigen.h:85
struct cloudViewer::visualization::gui::MouseEvent::@17::@21 wheel
std::function< void(rendering::Camera *)> on_camera_changed_
std::function< void(const Eigen::Vector3f &)> on_light_dir_changed_
std::shared_ptr< rendering::CloudViewerScene > scene_
void UpdateFarPlane(const Rect &frame, float verticalFoV)
std::unordered_set< std::shared_ptr< Label3D > > labels_3d_
Definition: lsd.c:149
#define local
Definition: unzip.c:93