ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
O3DVisualizer.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 <CloudViewerConfig.h>
11 #include <FileSystem.h>
12 #include <ImageIO.h>
13 #include <Logging.h>
14 
15 #include <set>
16 #include <unordered_map>
17 #include <unordered_set>
18 
51 
52 #define GROUPS_USE_TREE 1
53 
54 using namespace cloudViewer::visualization::gui;
56 
57 namespace cloudViewer {
58 namespace visualization {
59 namespace visualizer {
60 
61 namespace {
62 static const std::string kShaderLit = "defaultLit";
63 static const std::string kShaderUnlit = "defaultUnlit";
64 static const std::string kShaderUnlitLines = "unlitLine";
65 static const std::string kShaderGaussianSplat = "gaussianSplat";
66 
67 static const std::string kDefaultIBL = "default";
68 
69 enum MenuId {
70  MENU_ABOUT = 0,
71  MENU_EXPORT_RGB,
72  MENU_CLOSE,
73  MENU_SETTINGS,
74  MENU_ACTIONS_BASE = 1000 /* this should be last */
75 };
76 
77 template <typename T>
78 std::shared_ptr<T> GiveOwnership(T *ptr) {
79  return std::shared_ptr<T>(ptr);
80 }
81 
82 class ButtonList : public Widget {
83 public:
84  explicit ButtonList(int spacing) : spacing_(spacing) {}
85 
86  void SetWidth(int width) { width_ = width; }
87 
88  Size CalcPreferredSize(const LayoutContext &context,
89  const Constraints &constraints) const override {
90  auto frames = CalcFrames(context, constraints);
91  if (!frames.empty()) {
92  // Add spacing on the bottom to look like the start of a new row
93  return Size(width_,
94  frames.back().GetBottom() - frames[0].y + spacing_);
95  } else {
96  return Size(width_, 0);
97  }
98  }
99 
100  void Layout(const LayoutContext &context) override {
101  auto frames = CalcFrames(context, Constraints());
102  auto &children = GetChildren();
103  for (size_t i = 0; i < children.size(); ++i) {
104  children[i]->SetFrame(frames[i]);
105  }
106  }
107 
108  size_t size() const { return GetChildren().size(); }
109 
110 private:
111  int spacing_;
112  int width_ = 10000;
113 
114  std::vector<Rect> CalcFrames(const LayoutContext &context,
115  const Widget::Constraints &constraints) const {
116  auto &f = GetFrame();
117  std::vector<Rect> frames;
118  int x = f.x;
119  int y = f.y;
120  int lineHeight = 0;
121  for (auto child : GetChildren()) {
122  auto pref = child->CalcPreferredSize(context, constraints);
123  if (x > f.x && x + pref.width > f.x + width_) {
124  y = y + lineHeight + spacing_;
125  x = f.x;
126  lineHeight = 0;
127  }
128  frames.emplace_back(x, y, pref.width, pref.height);
129  x += pref.width + spacing_;
130  lineHeight = std::max(lineHeight, pref.height);
131  }
132  return frames;
133  }
134 };
135 
136 class EmptyIfHiddenVert : public CollapsableVert {
137  using Super = CollapsableVert;
138 
139 public:
140  EmptyIfHiddenVert(const char *text) : CollapsableVert(text) {}
141  EmptyIfHiddenVert(const char *text,
142  int spacing,
143  const Margins &margins = Margins())
144  : CollapsableVert(text, spacing, margins) {}
145 
146  void SetVisible(bool vis) override {
147  Super::SetVisible(vis);
148  Super::SetIsOpen(vis);
149  needsLayout_ = true;
150  }
151 
152  Size CalcPreferredSize(const LayoutContext &context,
153  const Constraints &constraints) const override {
154  if (IsVisible()) {
155  return Super::CalcPreferredSize(context, constraints);
156  } else {
157  return Size(0, 0);
158  }
159  }
160 
161  Widget::DrawResult Draw(const DrawContext &context) override {
162  auto result = Super::Draw(context);
163  if (needsLayout_) {
164  needsLayout_ = false;
166  } else {
167  return result;
168  }
169  }
170 
171 private:
172  bool needsLayout_ = false;
173 };
174 
175 class DrawObjectTreeCell : public Widget {
176  using Super = Widget;
177 
178 public:
179  enum { FLAG_NONE = 0, FLAG_GROUP = (1 << 0), FLAG_TIME = (1 << 1) };
180 
181  DrawObjectTreeCell(const char *name,
182  const char *group,
183  double time,
184  bool is_checked,
185  int flags,
186  std::function<void(bool)> on_toggled) {
187  flags_ = flags;
188 
189  std::string time_str;
190  if (flags & FLAG_TIME) {
191  char buf[32];
192  if (time == double(int(time))) {
193  snprintf(buf, sizeof(buf), "t=%d", int(time));
194  } else {
195  snprintf(buf, sizeof(buf), "t=%g", time);
196  }
197  time_str = std::string(buf);
198  }
199 
200  // We don't want any text in the checkbox, but passing "" seems to make
201  // it not toggle, so we need to pass in something. This way it will
202  // just be extra spacing.
203  checkbox_ = std::make_shared<Checkbox>(" ");
204  checkbox_->SetChecked(is_checked);
205  checkbox_->SetOnChecked(on_toggled);
206  name_ = std::make_shared<Label>(name);
207  group_ = std::make_shared<Label>((flags & FLAG_GROUP) ? group : "");
208  time_ = std::make_shared<Label>(time_str.c_str());
209  AddChild(checkbox_);
210  AddChild(name_);
211  AddChild(group_);
212  AddChild(time_);
213  }
214 
215  ~DrawObjectTreeCell() {}
216 
217  std::shared_ptr<Checkbox> GetCheckbox() { return checkbox_; }
218  std::shared_ptr<Label> GetName() { return name_; }
219 
220  Size CalcPreferredSize(const LayoutContext &context,
221  const Constraints &constraints) const override {
222  auto check_pref = checkbox_->CalcPreferredSize(context, constraints);
223  auto name_pref = name_->CalcPreferredSize(context, constraints);
224  int w = check_pref.width + name_pref.width + GroupWidth(context.theme) +
225  TimeWidth(context.theme);
226  return Size(w, std::max(check_pref.height, name_pref.height));
227  }
228 
229  void Layout(const LayoutContext &context) override {
230  auto &frame = GetFrame();
231  auto check_width =
232  checkbox_->CalcPreferredSize(context, Constraints()).width;
233  checkbox_->SetFrame(Rect(frame.x, frame.y, check_width, frame.height));
234  auto group_width = GroupWidth(context.theme);
235  auto time_width = TimeWidth(context.theme);
236  auto x = checkbox_->GetFrame().GetRight();
237  auto name_width = frame.GetRight() - group_width - time_width - x;
238  name_->SetFrame(Rect(x, frame.y, name_width, frame.height));
239  x += name_width;
240  group_->SetFrame(Rect(x, frame.y, group_width, frame.height));
241  x += group_width;
242  time_->SetFrame(Rect(x, frame.y, time_width, frame.height));
243  }
244 
245 private:
246  int flags_;
247  std::shared_ptr<Checkbox> checkbox_;
248  std::shared_ptr<Label> name_;
249  std::shared_ptr<Label> group_;
250  std::shared_ptr<Label> time_;
251 
252  int GroupWidth(const Theme &theme) const {
253  if (flags_ & FLAG_GROUP) {
254  return 5 * theme.font_size;
255  } else {
256  return 0;
257  }
258  }
259 
260  int TimeWidth(const Theme &theme) const {
261  if (flags_ & FLAG_TIME) {
262  return 3 * theme.font_size;
263  } else {
264  return 0;
265  }
266  }
267 };
268 
269 struct LightingProfile {
270  std::string name;
272 };
273 
274 static const char *kCustomName = "Custom";
275 static const std::vector<LightingProfile> gLightingProfiles = {
276  {"Dark shadows", CloudViewerScene::LightingProfile::DARK_SHADOWS},
277  {"Medium shadows", CloudViewerScene::LightingProfile::MED_SHADOWS},
278  {"Soft shadows", CloudViewerScene::LightingProfile::SOFT_SHADOWS},
279  {"No shadows", CloudViewerScene::LightingProfile::NO_SHADOWS}};
280 
281 } // namespace
282 
284  std::set<std::string> added_names_;
285  std::set<std::string> added_groups_;
286  std::vector<DrawObject> objects_;
287  std::vector<DrawObject> inspection_objects_;
288  std::vector<DrawObject> wireframe_objects_;
289  std::shared_ptr<O3DVisualizerSelections> selections_;
290  bool polygon_selection_unselects_ = false;
291  bool selections_need_update_ = true;
292  std::function<void(double)> on_animation_;
293  std::function<bool()> on_animation_tick_;
294  std::shared_ptr<io::rpc::ZMQReceiver> receiver_;
295  std::shared_ptr<MessageProcessor> message_processor_;
296 
298  bool can_auto_show_settings_ = true;
299  bool was_using_sun_follows_cam_ = false;
300 
301  double min_time_ = 0.0;
302  double max_time_ = 0.0;
303  double start_animation_clock_time_ = 0.0;
304  double next_animation_tick_clock_time_ = 0.0;
305  double last_animation_tick_clock_time_ = 0.0;
306 
307  Window *window_ = nullptr;
308  SceneWidget *scene_ = nullptr;
309 
310  struct {
311  // We only keep pointers here because that way we don't have to release
312  // all the shared_ptrs at destruction just to ensure that the gui gets
313  // destroyed before the Window, because the Window will do that for us.
315  std::unordered_map<int, std::function<void(O3DVisualizer &)>>
317 
323  std::map<SceneWidget::Controls, Button *> mouse_buttons;
329 
341 
351 
354 #if GROUPS_USE_TREE
355  std::map<std::string, TreeView::ItemId> group2itemid;
356 #endif // GROUPS_USE_TREE
357  std::map<std::string, TreeView::ItemId> object2itemid;
358 
359 #if !GROUPS_USE_TREE
360  EmptyIfHiddenVert *groups_panel;
361  TreeView *groups;
362 #endif // !GROUPS_USE_TREE
363 
364  EmptyIfHiddenVert *time_panel;
368 
369  EmptyIfHiddenVert *actions_panel;
370  ButtonList *actions;
371  } settings;
372 
374  if (window_) {
375  return;
376  }
377 
378  window_ = w;
379  scene_ = new SceneWidget();
380  selections_ = std::make_shared<O3DVisualizerSelections>(*scene_);
381  scene_->SetScene(std::make_shared<CloudViewerScene>(w->GetRenderer()));
382  scene_->EnableSceneCaching(true); // smoother UI with large geometry
383  scene_->SetOnPointsPicked(
384  [this](const std::map<
385  std::string,
386  std::vector<std::pair<size_t, Eigen::Vector3d>>>
387  &indices,
388  int keymods) {
389  bool unselect_mode_requested =
390  keymods & int(KeyModifier::SHIFT);
391  if (unselect_mode_requested ||
392  polygon_selection_unselects_) {
393  selections_->UnselectIndices(indices);
394  } else {
395  selections_->SelectIndices(indices);
396  }
397  polygon_selection_unselects_ = false;
398  });
399  w->AddChild(GiveOwnership(scene_));
400 
401  auto o3dscene = scene_->GetScene();
402  o3dscene->SetBackground(ui_state_.bg_color);
403 
404  MakeSettingsUI();
405  SetMouseMode(SceneWidget::Controls::ROTATE_CAMERA);
406  SetLightingProfile(gLightingProfiles[2]); // soft shadows
407  SetPointSize(ui_state_.point_size); // sync selections_' point size
408  EnableSunFollowsCamera(true);
409  }
410 
411  void MakeSettingsUI() {
412  auto em = window_->GetTheme().font_size;
413  auto half_em = int(std::round(0.5f * float(em)));
414  auto v_spacing = int(std::round(0.25 * float(em)));
415 
416  settings.panel = new Vert(half_em);
417  window_->AddChild(GiveOwnership(settings.panel));
418 
419  Margins margins(em, 0, half_em, 0);
420  Margins tabbed_margins(0, half_em, 0, 0);
421 
422  settings.mouse_panel =
423  new CollapsableVert("Mouse Controls", v_spacing, margins);
424  settings.panel->AddChild(GiveOwnership(settings.mouse_panel));
425 
426  settings.mouse_tab = new TabControl();
427  settings.mouse_panel->AddChild(GiveOwnership(settings.mouse_tab));
428 
429  settings.view_panel = new Vert(v_spacing, tabbed_margins);
430  settings.pick_panel = new Vert(v_spacing, tabbed_margins);
431  settings.mouse_tab->AddTab("Scene", GiveOwnership(settings.view_panel));
432  settings.mouse_tab->AddTab("Selection",
433  GiveOwnership(settings.pick_panel));
434  settings.mouse_tab->SetOnSelectedTabChanged([this](int tab_idx) {
435  if (tab_idx == 0) {
436  SetMouseMode(settings.view_mouse_mode);
437  } else {
438  SetPicking();
439  }
440  });
441 
442  // Mouse countrols
443  auto MakeMouseButton = [this](const char *name,
445  auto button = new SmallToggleButton(name);
446  button->SetOnClicked([this, type]() { this->SetMouseMode(type); });
447  this->settings.mouse_buttons[type] = button;
448  return button;
449  };
450  auto *h = new Horiz(v_spacing);
451  h->AddStretch();
452  h->AddChild(GiveOwnership(MakeMouseButton(
453  "Arcball", SceneWidget::Controls::ROTATE_CAMERA)));
454  h->AddChild(GiveOwnership(
455  MakeMouseButton("Fly", SceneWidget::Controls::FLY)));
456  h->AddChild(GiveOwnership(
457  MakeMouseButton("Model", SceneWidget::Controls::ROTATE_MODEL)));
458  h->AddStretch();
459  settings.view_panel->AddChild(GiveOwnership(h));
460 
461  h = new Horiz(v_spacing);
462  h->AddStretch();
463  h->AddChild(GiveOwnership(MakeMouseButton(
464  "Sun Direction", SceneWidget::Controls::ROTATE_SUN)));
465  h->AddChild(GiveOwnership(MakeMouseButton(
466  "Environment", SceneWidget::Controls::ROTATE_IBL)));
467  h->AddStretch();
468  settings.view_panel->AddChild(GiveOwnership(h));
469  settings.view_panel->AddFixed(half_em);
470 
471  auto *reset = new SmallButton("Reset Camera");
472  reset->SetOnClicked([this]() { this->ResetCameraToDefault(); });
473 
474  h = new Horiz(v_spacing);
475  h->AddStretch();
476  h->AddChild(GiveOwnership(reset));
477  h->AddStretch();
478  settings.view_panel->AddChild(GiveOwnership(h));
479 
480  // Selection sets controls
481  settings.new_selection_set = new SmallButton(" + ");
482  settings.new_selection_set->SetOnClicked(
483  [this]() { NewSelectionSet(); });
484  settings.delete_selection_set = new SmallButton(" - ");
485  settings.delete_selection_set->SetOnClicked([this]() {
486  int idx = settings.selection_sets->GetSelectedIndex();
487  RemoveSelectionSet(idx);
488  });
489  settings.selection_sets = new ListView();
490  settings.selection_sets->SetOnValueChanged([this](const char *, bool) {
491  SelectSelectionSet(settings.selection_sets->GetSelectedIndex());
492  });
493 
494 #if __APPLE__
495  const char *selection_help = R"(Cmd-click to select a point
496 Cmd-shift-click to deselect a point
497 Cmd-alt-click to polygon select)";
498 #else
499  const char *selection_help = R"(Ctrl-click to select a point
500 Ctrl-shift-click to deselect a point
501 Ctrl-alt-click to polygon select)";
502 #endif // __APPLE__
503  h = new Horiz();
504  h->AddStretch();
505  h->AddChild(std::make_shared<Label>(selection_help));
506  h->AddStretch();
507  settings.pick_panel->AddChild(GiveOwnership(h));
508 
509  h = new Horiz(int(std::round(0.25f * float(em))));
510  settings.polygon_selection_panel = h;
511  h->AddStretch();
512  auto b = std::make_shared<SmallButton>("Select");
513  b->SetOnClicked([this]() {
515  settings.polygon_selection_panel->SetVisible(false);
516  });
517  h->AddChild(b);
518  b = std::make_shared<SmallButton>("Unselect");
519  b->SetOnClicked([this]() {
520  polygon_selection_unselects_ = true;
522  settings.polygon_selection_panel->SetVisible(false);
523  });
524  h->AddChild(b);
525  b = std::make_shared<SmallButton>("Cancel");
526  b->SetOnClicked([this]() {
528  settings.polygon_selection_panel->SetVisible(false);
529  });
530  h->AddChild(b);
531  h->AddStretch();
532  h->SetVisible(false);
533  settings.pick_panel->AddChild(GiveOwnership(h));
534 
535  h = new Horiz(v_spacing);
536  h->AddChild(std::make_shared<Label>("Selection Sets"));
537  h->AddStretch();
538  h->AddChild(GiveOwnership(settings.new_selection_set));
539  h->AddChild(GiveOwnership(settings.delete_selection_set));
540  settings.pick_panel->AddChild(GiveOwnership(h));
541  settings.pick_panel->AddChild(GiveOwnership(settings.selection_sets));
542 
543  // Scene controls
544  settings.scene_panel = new CollapsableVert("Scene", v_spacing, margins);
545  settings.panel->AddChild(GiveOwnership(settings.scene_panel));
546 
547  settings.show_skybox = new Checkbox("Show Skybox");
548  settings.show_skybox->SetOnChecked(
549  [this](bool is_checked) { this->ShowSkybox(is_checked); });
550 
551  settings.show_axes = new Checkbox("Show Axis");
552  settings.show_axes->SetOnChecked(
553  [this](bool is_checked) { this->ShowAxes(is_checked); });
554 
555  settings.show_ground = new Checkbox("Show Ground");
556  settings.show_ground->SetOnChecked(
557  [this](bool is_checked) { this->ShowGround(is_checked); });
558 
559  settings.ground_plane = new Combobox();
560  settings.ground_plane->AddItem("XZ");
561  settings.ground_plane->AddItem("XY");
562  settings.ground_plane->AddItem("YZ");
563  settings.ground_plane->SetOnValueChanged([this](const char *item,
564  int idx) {
565  if (idx == 1) {
567  } else if (idx == 2) {
569  } else {
571  }
572  this->ShowGround(ui_state_.show_ground);
573  });
574 
575  h = new Horiz(v_spacing);
576  h->AddChild(GiveOwnership(settings.show_axes));
577  h->AddFixed(em);
578  h->AddChild(GiveOwnership(settings.show_skybox));
579  settings.scene_panel->AddChild(GiveOwnership(h));
580  settings.scene_panel->AddChild(GiveOwnership(settings.show_ground));
581  settings.scene_panel->AddChild(GiveOwnership(settings.ground_plane));
582 
583  settings.bg_color = new ColorEdit();
584  settings.bg_color->SetValue(ui_state_.bg_color.x(),
585  ui_state_.bg_color.y(),
586  ui_state_.bg_color.z());
587  settings.bg_color->SetOnValueChanged([this](const Color &c) {
588  this->SetBackground({c.GetRed(), c.GetGreen(), c.GetBlue(), 1.0f},
589  nullptr);
590  });
591 
592  settings.point_size = new Slider(Slider::INT);
593  settings.point_size->SetLimits(1, 10);
594  settings.point_size->SetValue(ui_state_.point_size);
595  settings.point_size->SetOnValueChanged([this](const double newValue) {
596  this->SetPointSize(int(newValue));
597  });
598 
599  settings.shader = new Combobox();
600  settings.shader->AddItem("Standard");
601  settings.shader->AddItem("Unlit");
602  settings.shader->AddItem("Normal Map");
603  settings.shader->AddItem("Depth");
604  settings.shader->SetOnValueChanged([this](const char *item, int idx) {
605  if (idx == 1) {
606  this->SetShader(O3DVisualizer::Shader::UNLIT);
607  } else if (idx == 2) {
608  this->SetShader(O3DVisualizer::Shader::NORMALS);
609  } else if (idx == 3) {
610  this->SetShader(O3DVisualizer::Shader::DEPTH);
611  } else {
612  this->SetShader(O3DVisualizer::Shader::STANDARD);
613  }
614  });
615 
616  settings.lighting = new Combobox();
617  for (auto &profile : gLightingProfiles) {
618  settings.lighting->AddItem(profile.name.c_str());
619  }
620  settings.lighting->AddItem(kCustomName);
621  settings.lighting->SetOnValueChanged([this](const char *, int index) {
622  if (index < int(gLightingProfiles.size())) {
623  this->SetLightingProfile(gLightingProfiles[index]);
624  }
625  });
626 
627  settings.basic_mode = new Checkbox("");
628  settings.basic_mode->SetOnChecked(
629  [this](bool enable) { this->EnableBasicMode(enable); });
630  settings.wireframe_mode = new Checkbox("");
631  settings.wireframe_mode->SetOnChecked(
632  [this](bool enable) { this->EnableWireframeMode(enable); });
633 
634  auto *grid = new VGrid(2, v_spacing);
635  settings.scene_panel->AddChild(GiveOwnership(grid));
636 
637  grid->AddChild(std::make_shared<Label>("BG Color"));
638  grid->AddChild(GiveOwnership(settings.bg_color));
639  grid->AddChild(std::make_shared<Label>("PointSize"));
640  grid->AddChild(GiveOwnership(settings.point_size));
641  grid->AddChild(std::make_shared<Label>("Shader"));
642  grid->AddChild(GiveOwnership(settings.shader));
643  grid->AddChild(std::make_shared<Label>("Lighting"));
644  grid->AddChild(GiveOwnership(settings.lighting));
645  grid->AddChild(std::make_shared<Label>("Raw Mode"));
646  grid->AddChild(GiveOwnership(settings.basic_mode));
647  grid->AddChild(std::make_shared<Label>("Wireframe"));
648  grid->AddChild(GiveOwnership(settings.wireframe_mode));
649 
650  // Light list
651  settings.light_panel = new CollapsableVert("Lighting", 0, margins);
652  settings.light_panel->SetIsOpen(false);
653  settings.panel->AddChild(GiveOwnership(settings.light_panel));
654 
655  h = new Horiz(v_spacing);
656  settings.use_ibl = new Checkbox("HDR map");
657  settings.use_ibl->SetChecked(ui_state_.use_ibl);
658  settings.use_ibl->SetOnChecked([this](bool checked) {
659  this->ui_state_.use_ibl = checked;
660  this->SetUIState(ui_state_);
661  this->settings.lighting->SetSelectedValue(kCustomName);
662  });
663 
664  settings.use_sun = new Checkbox("Sun");
665  settings.use_sun->SetChecked(settings.use_sun);
666  settings.use_sun->SetOnChecked([this](bool checked) {
667  this->ui_state_.use_sun = checked;
668  this->SetUIState(ui_state_);
669  this->settings.lighting->SetSelectedValue(kCustomName);
670  });
671 
672  h->AddChild(GiveOwnership(settings.use_ibl));
673  h->AddFixed(int(std::round(
674  1.4 * em))); // align with Show Skybox checkbox above
675  h->AddChild(GiveOwnership(settings.use_sun));
676 
677  settings.light_panel->AddChild(
678  std::make_shared<Label>("Light sources"));
679  settings.light_panel->AddChild(GiveOwnership(h));
680  settings.light_panel->AddFixed(half_em);
681 
682  grid = new VGrid(2, v_spacing);
683 
684  settings.ibl_names = new Combobox();
685  for (auto &name : GetListOfIBLs()) {
686  settings.ibl_names->AddItem(name.c_str());
687  }
688  settings.ibl_names->SetSelectedValue(kDefaultIBL.c_str());
689  settings.ibl_names->SetOnValueChanged([this](const char *val, int idx) {
690  std::string resource_path =
692  this->SetIBL(resource_path + std::string("/") + std::string(val));
693  this->settings.lighting->SetSelectedValue(kCustomName);
694  });
695  grid->AddChild(std::make_shared<Label>("HDR map"));
696  grid->AddChild(GiveOwnership(settings.ibl_names));
697 
698  settings.ibl_intensity = new Slider(Slider::INT);
699  settings.ibl_intensity->SetLimits(0.0, 150000.0);
700  settings.ibl_intensity->SetValue(ui_state_.ibl_intensity);
701  settings.ibl_intensity->SetOnValueChanged([this](double new_value) {
702  this->ui_state_.ibl_intensity = int(new_value);
703  this->SetUIState(ui_state_);
704  this->settings.lighting->SetSelectedValue(kCustomName);
705  });
706  grid->AddChild(std::make_shared<Label>("Intensity"));
707  grid->AddChild(GiveOwnership(settings.ibl_intensity));
708 
709  settings.light_panel->AddChild(std::make_shared<Label>("Environment"));
710  settings.light_panel->AddChild(GiveOwnership(grid));
711  settings.light_panel->AddFixed(half_em);
712 
713  grid = new VGrid(2, v_spacing);
714 
715  settings.sun_intensity = new Slider(Slider::INT);
716  settings.sun_intensity->SetLimits(0.0, 250000.0);
717  settings.sun_intensity->SetValue(ui_state_.sun_intensity);
718  settings.sun_intensity->SetOnValueChanged([this](double new_value) {
719  this->ui_state_.sun_intensity = int(new_value);
720  this->SetUIState(ui_state_);
721  this->settings.lighting->SetSelectedValue(kCustomName);
722  });
723  grid->AddChild(std::make_shared<Label>("Intensity"));
724  grid->AddChild(GiveOwnership(settings.sun_intensity));
725 
726  settings.sun_dir = new VectorEdit();
727  settings.sun_dir->SetValue(ui_state_.sun_dir);
728  settings.sun_dir->SetOnValueChanged([this](const Eigen::Vector3f &dir) {
729  this->ui_state_.sun_dir = dir;
730  this->SetUIState(ui_state_);
731  this->settings.lighting->SetSelectedValue(kCustomName);
732  });
733  scene_->SetOnSunDirectionChanged(
734  [this](const Eigen::Vector3f &new_dir) {
735  this->ui_state_.sun_dir = new_dir;
736  this->settings.sun_dir->SetValue(new_dir);
737  // Don't need to call SetUIState(), the SceneWidget already
738  // modified the scene.
739  this->settings.lighting->SetSelectedValue(kCustomName);
740  });
741  grid->AddChild(std::make_shared<Label>("Direction"));
742  grid->AddChild(GiveOwnership(settings.sun_dir));
743 
744  settings.sun_follows_camera = new gui::Checkbox(" ");
745  settings.sun_follows_camera->SetChecked(true);
746  settings.sun_follows_camera->SetOnChecked([this](bool checked) {
747  ui_state_.sun_follows_camera = checked;
748  this->SetUIState(ui_state_);
749  EnableSunFollowsCamera(checked);
750  });
751  grid->AddChild(GiveOwnership(settings.sun_follows_camera));
752  grid->AddChild(std::make_shared<gui::Label>("Sun Follows Camera"));
753 
754  settings.sun_color = new ColorEdit();
755  settings.sun_color->SetValue(ui_state_.sun_color);
756  settings.sun_color->SetOnValueChanged([this](const Color &new_color) {
757  this->ui_state_.sun_color = {new_color.GetRed(),
758  new_color.GetGreen(),
759  new_color.GetBlue()};
760  this->SetUIState(ui_state_);
761  this->settings.lighting->SetSelectedValue(kCustomName);
762  });
763  grid->AddChild(std::make_shared<Label>("Color"));
764  grid->AddChild(GiveOwnership(settings.sun_color));
765 
766  settings.light_panel->AddChild(
767  std::make_shared<Label>("Sun (Directional light)"));
768  settings.light_panel->AddChild(GiveOwnership(grid));
769 
770  // Geometry list
771  settings.geometries_panel =
772  new CollapsableVert("Geometries", v_spacing, margins);
773  settings.panel->AddChild(GiveOwnership(settings.geometries_panel));
774 
775  settings.geometries = new TreeView();
776  settings.geometries_panel->AddChild(GiveOwnership(settings.geometries));
777 
778 #if !GROUPS_USE_TREE
779  // Groups
780  settings.groups_panel =
781  new EmptyIfHiddenVert("Groups", v_spacing, margins);
782  settings.panel->AddChild(GiveOwnership(settings.groups_panel));
783 
784  settings.groups = new TreeView();
785  settings.groups_panel->AddChild(GiveOwnership(settings.groups));
786 
787  settings.groups_panel->SetVisible(false);
788 #endif // !GROUPS_USE_TREE
789 
790  // Time controls
791  settings.time_panel = new EmptyIfHiddenVert("Time", v_spacing, margins);
792  settings.panel->AddChild(GiveOwnership(settings.time_panel));
793 
794  settings.time_slider = new Slider(Slider::DOUBLE);
795  settings.time_slider->SetOnValueChanged([this](double new_value) {
796  this->ui_state_.current_time = new_value;
797  this->UpdateTimeUI();
798  this->SetCurrentTime(new_value);
799  });
800 
801  settings.time_edit = new NumberEdit(NumberEdit::DOUBLE);
802  settings.time_edit->SetOnValueChanged([this](double new_value) {
803  this->ui_state_.current_time = new_value;
804  this->UpdateTimeUI();
805  this->SetCurrentTime(new_value);
806  });
807 
808  settings.play = new SmallToggleButton("Play");
809  settings.play->SetOnClicked(
810  [this]() { this->SetAnimating(settings.play->GetIsOn()); });
811 
812  h = new Horiz(v_spacing);
813  h->AddChild(GiveOwnership(settings.time_slider));
814  h->AddChild(GiveOwnership(settings.time_edit));
815  h->AddChild(GiveOwnership(settings.play));
816  settings.time_panel->AddChild(GiveOwnership(h));
817 
818  settings.time_panel->SetVisible(false); // hide until we add a
819  // geometry with time
820 
821  // Custom actions
822  settings.actions_panel =
823  new EmptyIfHiddenVert("Custom Actions", v_spacing, margins);
824  settings.panel->AddChild(GiveOwnership(settings.actions_panel));
825  settings.actions_panel->SetVisible(false);
826 
827  settings.actions = new ButtonList(v_spacing);
828  settings.actions_panel->AddChild(GiveOwnership(settings.actions));
829 
830  // Picking callbacks
831  scene_->SetOnStartedPolygonPicking([this]() {
832  settings.polygon_selection_panel->SetVisible(true);
833  });
834  }
835 
836  void AddGeometry(const std::string &name,
837  std::shared_ptr<geometry::Geometry> geom,
838  std::shared_ptr<t::geometry::Geometry> tgeom,
839  std::shared_ptr<rendering::TriangleMeshModel> model,
840  const rendering::MaterialRecord *material,
841  const std::string &group,
842  double time,
843  bool is_visible) {
844  std::string group_name = group;
845  if (group_name == "") {
846  group_name = "default";
847  }
848  bool is_default_color = false;
849  bool no_shadows = false;
850  MaterialRecord mat;
851 
852  if (material) {
853  mat = *material;
854  is_default_color = false;
855  auto t_cloud =
856  std::dynamic_pointer_cast<t::geometry::PointCloud>(tgeom);
857  ui_state_.point_size = static_cast<int>(mat.point_size);
858  } else if (model) {
859  // Adding a triangle mesh model. Shader needs to be set to
860  // defaultLit for O3D shader handling logic to work.
861  mat.shader = kShaderLit;
862  } else { // branch only applies to geometries
863  bool has_colors = false;
864  bool has_normals = false;
865  bool is_gaussian_splat = false;
866 
867  auto cloud = std::dynamic_pointer_cast<geometry::PointCloud>(geom);
868  auto lines = std::dynamic_pointer_cast<geometry::LineSet>(geom);
869  auto obb = std::dynamic_pointer_cast<geometry::OrientedBoundingBox>(
870  geom);
871  auto aabb =
872  std::dynamic_pointer_cast<geometry::AxisAlignedBoundingBox>(
873  geom);
874  auto mesh = std::dynamic_pointer_cast<ccMesh>(geom);
875  auto voxel_grid =
876  std::dynamic_pointer_cast<geometry::VoxelGrid>(geom);
877  auto octree = std::dynamic_pointer_cast<geometry::Octree>(geom);
878 
879  auto t_cloud =
880  std::dynamic_pointer_cast<t::geometry::PointCloud>(tgeom);
881  auto t_mesh =
882  std::dynamic_pointer_cast<t::geometry::TriangleMesh>(tgeom);
883  auto t_lines =
884  std::dynamic_pointer_cast<t::geometry::LineSet>(tgeom);
885 
886  if (cloud) {
887  has_colors = cloud->hasColors();
888  has_normals = cloud->hasNormals();
889  } else if (t_cloud) {
890  if (t_cloud->IsGaussianSplat()) is_gaussian_splat = true;
891  has_colors = t_cloud->HasPointColors();
892  has_normals = t_cloud->HasPointNormals();
893  } else if (lines) {
894  has_colors = !lines->colors_.empty();
895  no_shadows = true;
896  } else if (t_lines) {
897  has_colors = t_lines->HasLineColors();
898  no_shadows = true;
899  } else if (obb) {
900  has_colors = (obb->color_ != Eigen::Vector3d{0.0, 0.0, 0.0});
901  no_shadows = true;
902  } else if (aabb) {
903  has_colors =
904  (aabb->GetColor() != Eigen::Vector3d{0.0, 0.0, 0.0});
905  no_shadows = true;
906  } else if (mesh) {
907  has_normals = mesh->hasNormals() || mesh->HasTriangleNormals();
908  has_colors = true; // always want base_color as white
909  } else if (t_mesh) {
910  has_normals = t_mesh->HasVertexNormals() ||
911  t_mesh->HasTriangleNormals();
912  has_colors = true; // always want base_color as white
913  } else if (voxel_grid) {
914  has_normals = false;
915  has_colors = voxel_grid->HasColors();
916  } else if (octree) {
917  has_normals = false;
918  has_colors = true;
919  }
920 
921  mat.base_color = CalcDefaultUnlitColor();
922  mat.shader = kShaderUnlit;
923  if (lines || obb || aabb || t_lines) {
924  mat.shader = kShaderUnlitLines;
925  mat.line_width = ui_state_.line_width * window_->GetScaling();
926  }
927  is_default_color = true;
928  if (has_colors) {
929  mat.base_color = {1.0f, 1.0f, 1.0f, 1.0f};
930  is_default_color = false;
931  }
932  if (has_normals) {
933  mat.base_color = {1.0f, 1.0f, 1.0f, 1.0f};
934  mat.shader = kShaderLit;
935  is_default_color = false;
936  }
937  if (is_gaussian_splat) {
938  mat.shader = kShaderGaussianSplat;
939  mat.sh_degree = t_cloud->GaussianSplatGetSHOrder();
940  }
941  mat.point_size = ConvertToScaledPixels(ui_state_.point_size);
942 
943  // If T Geometry has a valid material convert it to MaterialRecord
944  if (t_mesh && t_mesh->HasMaterial()) {
945  t_mesh->GetMaterial().ToMaterialRecord(mat);
946  } else if (t_cloud && t_cloud->HasMaterial()) {
947  t_cloud->GetMaterial().ToMaterialRecord(mat);
948  } else if (t_lines && t_lines->HasMaterial()) {
949  t_lines->GetMaterial().ToMaterialRecord(mat);
950  }
951 
952  // Finally assign material properties if geometry is a triangle mesh
953  if (mesh && mesh->materials_.size() > 0) {
954  std::size_t material_index;
955  if (mesh->hasTriangleMaterialIds()) {
956  auto minmax_it = std::minmax_element(
957  mesh->triangle_material_ids_.begin(),
958  mesh->triangle_material_ids_.end());
959  if (*minmax_it.first != *minmax_it.second) {
961  "Only a single material is "
962  "supported for TriangleMesh visualization, "
963  "only the first referenced material will be "
964  "used. Use TriangleMeshModel if more than one "
965  "material is required.");
966  }
967  material_index = *minmax_it.first;
968  } else {
969  material_index = 0;
970  }
971  auto &mesh_material = mesh->materials_[material_index].second;
972  mat.base_color = {mesh_material.baseColor.r(),
973  mesh_material.baseColor.g(),
974  mesh_material.baseColor.b(),
975  mesh_material.baseColor.a()};
976  mat.base_metallic = mesh_material.baseMetallic;
977  mat.base_roughness = mesh_material.baseRoughness;
978  mat.base_reflectance = mesh_material.baseReflectance;
979  mat.base_clearcoat = mesh_material.baseClearCoat;
981  mesh_material.baseClearCoatRoughness;
982  mat.base_anisotropy = mesh_material.baseAnisotropy;
983  mat.albedo_img = mesh_material.albedo;
984  mat.normal_img = mesh_material.normalMap;
985  mat.ao_img = mesh_material.ambientOcclusion;
986  mat.metallic_img = mesh_material.metallic;
987  mat.roughness_img = mesh_material.roughness;
988  mat.reflectance_img = mesh_material.reflectance;
989  mat.clearcoat_img = mesh_material.clearCoat;
990  mat.clearcoat_roughness_img = mesh_material.clearCoatRoughness;
991  mat.anisotropy_img = mesh_material.anisotropy;
992  }
993  }
994 
995  // We assume that the caller isn't setting a group or time (and in any
996  // case we don't know beforehand what they will do). So if they do,
997  // we need to update the geometry tree accordingly. This needs to happen
998  // before we add the object to the list, otherwise when we regenerate
999  // the object will already be added in the list and then get added again
1000  // below.
1001  AddGroup(group_name); // regenerates if necessary
1002  bool update_for_time = (min_time_ == max_time_ && time != max_time_);
1003  min_time_ = std::min(min_time_, time);
1004  max_time_ = std::max(max_time_, time);
1005  if (time != 0.0) {
1006  UpdateTimeUIRange();
1007  settings.time_panel->SetVisible(true);
1008  }
1009  if (update_for_time) {
1010  UpdateObjectTree();
1011  }
1012  // Auto-open the settings panel if we set anything fancy that would
1013  // imply using the UI.
1014  if (can_auto_show_settings_ &&
1015  (added_groups_.size() == 2 || update_for_time)) {
1016  ShowSettings(true);
1017  }
1018 
1019  objects_.push_back({name, geom, tgeom, model, mat, group_name, time,
1020  is_visible, is_default_color});
1021  AddObjectToTree(objects_.back());
1022 
1023  auto scene = scene_->GetScene();
1024  // Do we have a geometry, tgeometry or model?
1025  if (geom) {
1026  scene->AddGeometry(name, geom.get(), mat);
1027  } else if (tgeom) {
1028  scene->AddGeometry(name, tgeom.get(), mat);
1029  } else if (model) {
1030  scene->AddModel(name, *model);
1031  } else {
1033  "No valid geometry specified to O3DVisualizer. Only "
1034  "supported geometries are Geometry3D and TGeometry "
1035  "PointClouds and TriangleMeshes.");
1036  }
1037 
1038  if (no_shadows) {
1039  scene->GetScene()->GeometryShadows(name, false, false);
1040  }
1041  UpdateGeometryVisibility(objects_.back());
1042 
1043  // Bounds have changed, so update the selection point size, since they
1044  // depend on the bounds.
1045  SetPointSize(ui_state_.point_size);
1046 
1047  scene_->ForceRedraw();
1048  }
1049 
1050  void UpdateGeometry(const std::string &name,
1051  std::shared_ptr<t::geometry::Geometry> tgeom,
1052  uint32_t update_flags) {
1053  auto t_cloud =
1054  std::dynamic_pointer_cast<t::geometry::PointCloud>(tgeom);
1055  if (!t_cloud) {
1057  "Only TGeometry PointClouds can currently be updated using "
1058  "UpdateGeometry. Try removing the geometry that needs to "
1059  "be updated then adding the update geometry.");
1060  return;
1061  }
1062  scene_->GetScene()->GetScene()->UpdateGeometry(name, *t_cloud,
1063  update_flags);
1064  scene_->ForceRedraw();
1065  }
1066 
1067  void RemoveGeometry(const std::string &name) {
1068  std::string group;
1069  for (size_t i = 0; i < objects_.size(); ++i) {
1070  if (objects_[i].name == name) {
1071  group = objects_[i].group;
1072  settings.object2itemid.erase(objects_[i].name);
1073  objects_.erase(objects_.begin() + i);
1074  break;
1075  }
1076  }
1077 
1078  // Need to check group membership in case this was the last item in its
1079  // group. As long as we're doing that, recompute the min/max time, too.
1080  std::set<std::string> groups;
1081  min_time_ = max_time_ = 0.0;
1082  for (size_t i = 0; i < objects_.size(); ++i) {
1083  auto &o = objects_[i];
1084  min_time_ = std::min(min_time_, o.time);
1085  max_time_ = std::max(max_time_, o.time);
1086  groups.insert(o.group);
1087  }
1088  if (min_time_ == max_time_) {
1089  SetAnimating(false);
1090  }
1091  UpdateTimeUIRange();
1092 
1093  added_groups_ = groups;
1094  std::set<std::string> enabled;
1095  for (auto &g : ui_state_.enabled_groups) { // remove deleted groups
1096  if (groups.find(g) != groups.end()) {
1097  enabled.insert(g);
1098  }
1099  }
1100  ui_state_.enabled_groups = enabled;
1101  UpdateObjectTree();
1102  scene_->GetScene()->RemoveGeometry(name);
1103 
1104  // Bounds have changed, so update the selection point size, since they
1105  // depend on the bounds.
1106  SetPointSize(ui_state_.point_size);
1107 
1108  scene_->ForceRedraw();
1109  }
1110 
1111  void ShowGeometry(const std::string &name, bool show) {
1112  for (auto &o : objects_) {
1113  if (o.name == name) {
1114  if (show != o.is_visible) {
1115  o.is_visible = show;
1116 
1117  auto id = settings.object2itemid[o.name];
1118  auto cell = settings.geometries->GetItem(id);
1119  auto obj_cell =
1120  std::dynamic_pointer_cast<DrawObjectTreeCell>(cell);
1121  if (obj_cell) {
1122  obj_cell->GetCheckbox()->SetChecked(show);
1123  }
1124 
1125  UpdateGeometryVisibility(o); // calls ForceRedraw()
1126  window_->PostRedraw();
1127 
1128  if (selections_->IsActive()) {
1129  UpdateSelectableGeometry();
1130  } else {
1131  selections_need_update_ = true;
1132  }
1133  }
1134  break;
1135  }
1136  }
1137  }
1138 
1139  O3DVisualizer::DrawObject GetGeometry(const std::string &name) const {
1140  for (auto &o : objects_) {
1141  if (o.name == name) {
1142  return o;
1143  }
1144  }
1145  return DrawObject();
1146  }
1147 
1149  for (auto &o : objects_) {
1150  if (o.name == name) {
1151  return o.material;
1152  }
1153  }
1154  return MaterialRecord();
1155  }
1156 
1157  void ModifyGeometryMaterial(const std::string &name,
1158  const MaterialRecord *material) {
1159  for (auto &o : objects_) {
1160  if (o.name == name) {
1161  o.material = *material;
1162  scene_->GetScene()->ModifyGeometryMaterial(name, *material);
1163  break;
1164  }
1165  }
1166  }
1167 
1169  bool pcd = false) {
1170  if (inspect_mat.shader == "defaultLit" ||
1171  inspect_mat.shader == "defaultLitTransparency" ||
1172  inspect_mat.shader == "defaultListSSR") {
1173  inspect_mat.shader = "defaultLit";
1174  // Set parameters for 'simple' rendering
1175  inspect_mat.base_color = {1.f, 1.f, 1.f, 1.f};
1176  inspect_mat.base_metallic = 0.f;
1177  if (pcd) {
1178  inspect_mat.base_roughness = 0.1f;
1179  inspect_mat.base_reflectance = 0.6f;
1180  } else {
1181  inspect_mat.base_roughness = 0.5f;
1182  inspect_mat.base_reflectance = 0.8f;
1183  }
1184  inspect_mat.base_clearcoat = 0.f;
1185  inspect_mat.base_anisotropy = 0.f;
1186  inspect_mat.albedo_img.reset();
1187  inspect_mat.normal_img.reset();
1188  inspect_mat.ao_img.reset();
1189  inspect_mat.metallic_img.reset();
1190  inspect_mat.roughness_img.reset();
1191  inspect_mat.reflectance_img.reset();
1192  // inspect_mat.sRGB_color = false;
1193  // inspect_mat.sRGB_vertex_color = true;
1194  }
1195  }
1196 
1197  std::shared_ptr<ccMesh> DuplicateGeometryForInspection(
1198  std::shared_ptr<ccMesh> mesh) {
1199  auto new_mesh = mesh->cloneMesh();
1200  if (!new_mesh->HasTriangleNormals()) {
1201  new_mesh->ComputeTriangleNormals();
1202  }
1203  return std::shared_ptr<ccMesh>(new_mesh);
1204  }
1205 
1207  if (enable) {
1208  // Copy the objects...
1209  for (auto &o : objects_) {
1210  scene_->GetScene()->ShowGeometry(o.name, false);
1211  inspection_objects_.push_back(o);
1212  inspection_objects_.back().name = o.name + "_inspect";
1213  }
1214  // Add inspection objects to scene
1215  for (auto &o : inspection_objects_) {
1216  if (o.geometry) {
1217  CreateInspectionModeMaterial(
1218  o.material,
1219  o.geometry->isKindOf(CV_TYPES::POINT_CLOUD));
1220  // Need to compute triangle normals for triangle meshes
1221  if (auto mesh =
1222  std::dynamic_pointer_cast<ccMesh>(o.geometry)) {
1223  o.geometry = DuplicateGeometryForInspection(mesh);
1224  }
1225  scene_->GetScene()->AddGeometry(o.name, o.geometry.get(),
1226  o.material);
1227  } else if (o.tgeometry) {
1228  CreateInspectionModeMaterial(
1229  o.material,
1230  o.tgeometry->GetGeometryType() ==
1232  PointCloud);
1233  scene_->GetScene()->AddGeometry(o.name, o.tgeometry.get(),
1234  o.material);
1235  } else if (o.model) {
1236  for (auto &mat : o.model->materials_) {
1237  CreateInspectionModeMaterial(mat, false);
1238  }
1239  for (auto &mi : o.model->meshes_) {
1240  auto new_mesh = DuplicateGeometryForInspection(mi.mesh);
1241  mi.mesh = new_mesh;
1242  }
1243  scene_->GetScene()->AddModel(o.name, *o.model);
1244  }
1245  }
1246  } else {
1247  // Remove inspection geometries
1248  for (auto &o : inspection_objects_) {
1249  scene_->GetScene()->RemoveGeometry(o.name);
1250  }
1251  inspection_objects_.clear();
1252  // Show original geometries
1253  for (auto &o : objects_) {
1254  scene_->GetScene()->ShowGeometry(o.name, true);
1255  }
1256  }
1257  }
1258 
1260  if (enable) {
1261  // Material for wireframe
1262  MaterialRecord mat;
1263  mat.shader = "unlitLine";
1264  mat.line_width = 2.f;
1265  mat.base_color = {0.f, 0.3f, 1.f, 1.f};
1266  mat.emissive_color = {10000.f, 10000.f, 10000.f, 1.f};
1267  // Create line sets for eligible geometry
1268  for (auto &o : objects_) {
1269  if (o.geometry && o.geometry->isKindOf(CV_TYPES::MESH)) {
1270  // Hide original geometry
1271  scene_->GetScene()->ShowGeometry(o.name, false);
1272  auto mesh = std::dynamic_pointer_cast<ccMesh>(o.geometry);
1273  auto lines =
1275  DrawObject draw_obj;
1276  draw_obj.name = o.name + "_wireframe";
1277  draw_obj.geometry = lines;
1278  draw_obj.material = mat;
1279  wireframe_objects_.push_back(draw_obj);
1280  scene_->GetScene()->AddGeometry(draw_obj.name,
1281  draw_obj.geometry.get(),
1282  draw_obj.material);
1283  } else if (o.tgeometry &&
1284  o.tgeometry->GetGeometryType() ==
1286  TriangleMesh) {
1287  // Hide original geometry
1288  scene_->GetScene()->ShowGeometry(o.name, false);
1289  auto tmesh = std::dynamic_pointer_cast<
1290  t::geometry::TriangleMesh>(o.tgeometry);
1291  auto tmesh_legacy = tmesh->ToLegacy();
1293  tmesh_legacy);
1294  DrawObject draw_obj;
1295  draw_obj.name = o.name + "_wireframe";
1296  draw_obj.geometry = lines;
1297  draw_obj.material = mat;
1298  wireframe_objects_.push_back(draw_obj);
1299  scene_->GetScene()->AddGeometry(draw_obj.name,
1300  draw_obj.geometry.get(),
1301  draw_obj.material);
1302  } else if (o.model) {
1303  // Hide original geometry
1304  scene_->GetScene()->ShowGeometry(o.name, false);
1305  for (auto &mi : o.model->meshes_) {
1307  *mi.mesh);
1308  DrawObject draw_obj;
1309  draw_obj.name = mi.mesh_name + "_wireframe";
1310  draw_obj.geometry = lines;
1311  draw_obj.material = mat;
1312  wireframe_objects_.push_back(draw_obj);
1313  scene_->GetScene()->AddGeometry(draw_obj.name,
1314  draw_obj.geometry.get(),
1315  draw_obj.material);
1316  }
1317  }
1318  }
1319  } else {
1320  // Remove inspection geometries
1321  for (auto &o : wireframe_objects_) {
1322  scene_->GetScene()->RemoveGeometry(o.name);
1323  }
1324  wireframe_objects_.clear();
1325  // Show original geometries
1326  for (auto &o : objects_) {
1327  scene_->GetScene()->ShowGeometry(o.name, true);
1328  }
1329  }
1330  }
1331 
1332  void Add3DLabel(const Eigen::Vector3f &pos, const char *text) {
1333  scene_->AddLabel(pos, text);
1334  }
1335 
1336  void Clear3DLabels() { scene_->ClearLabels(); }
1337 
1338  void SetupCamera(float fov,
1339  const Eigen::Vector3f &center,
1340  const Eigen::Vector3f &eye,
1341  const Eigen::Vector3f &up) {
1342  scene_->LookAt(center, eye, up);
1343  scene_->ForceRedraw();
1344  }
1345 
1347  const Eigen::Matrix4d &extrinsic) {
1348  scene_->SetupCamera(intrinsic, extrinsic,
1349  scene_->GetScene()->GetBoundingBox());
1350  scene_->ForceRedraw();
1351  }
1352 
1353  void SetupCamera(const Eigen::Matrix3d &intrinsic,
1354  const Eigen::Matrix4d &extrinsic,
1355  int intrinsic_width_px,
1356  int intrinsic_height_px) {
1357  scene_->SetupCamera(intrinsic, extrinsic, intrinsic_width_px,
1358  intrinsic_height_px,
1359  scene_->GetScene()->GetBoundingBox());
1360  scene_->ForceRedraw();
1361  }
1362 
1364  auto scene = scene_->GetScene();
1365  scene_->SetupCamera(60.0f, scene->GetBoundingBox(), {0.0f, 0.0f, 0.0f});
1366  scene_->ForceRedraw();
1367  }
1368 
1369  void SetBackground(const Eigen::Vector4f &bg_color,
1370  std::shared_ptr<geometry::Image> bg_image) {
1371  auto old_default_color = CalcDefaultUnlitColor();
1372  ui_state_.bg_color = bg_color;
1373  settings.bg_color->SetValue(bg_color.x(), bg_color.y(), bg_color.z());
1374  auto scene = scene_->GetScene();
1375  scene->SetBackground(ui_state_.bg_color, bg_image);
1376 
1377  auto new_default_color = CalcDefaultUnlitColor();
1378  if (new_default_color != old_default_color) {
1379  for (auto &o : objects_) {
1380  if (o.is_color_default) {
1381  o.material.base_color = new_default_color;
1382  OverrideMaterial(o.name, o.material,
1383  ui_state_.scene_shader);
1384  }
1385  }
1386  }
1387 
1388  scene_->ForceRedraw();
1389  }
1390 
1391  void ShowSettings(bool show, bool cancel_auto_show = true) {
1392  if (cancel_auto_show) {
1393  can_auto_show_settings_ = false;
1394  }
1395  ui_state_.show_settings = show;
1396  settings.panel->SetVisible(show);
1397  auto menubar = Application::GetInstance().GetMenubar();
1398  if (menubar) { // might not have been created yet
1399  menubar->SetChecked(MENU_SETTINGS, show);
1400  }
1401  window_->SetNeedsLayout();
1402  }
1403 
1404  void ShowSkybox(bool show) {
1405  ui_state_.show_skybox = show;
1406  settings.show_skybox->SetChecked(show); // in case called manually
1407  scene_->GetScene()->ShowSkybox(show);
1408  scene_->ForceRedraw();
1409  }
1410 
1411  void ShowAxes(bool show) {
1412  ui_state_.show_axes = show;
1413  settings.show_axes->SetChecked(show); // in case called manually
1414  scene_->GetScene()->ShowAxes(show);
1415  scene_->ForceRedraw();
1416  }
1417 
1418  void ShowGround(bool show) {
1419  ui_state_.show_ground = show;
1420  settings.show_ground->SetChecked(show); // in case called manually
1421  scene_->GetScene()->ShowGroundPlane(show, ui_state_.ground_plane);
1422  scene_->ForceRedraw();
1423  }
1424 
1426  ui_state_.ground_plane = plane;
1427  if (plane == rendering::Scene::GroundPlane::XZ) {
1428  settings.ground_plane->SetSelectedIndex(0);
1429  } else if (plane == rendering::Scene::GroundPlane::XY) {
1430  settings.ground_plane->SetSelectedIndex(1);
1431  } else {
1432  settings.ground_plane->SetSelectedIndex(2);
1433  }
1434  // Update ground plane if it is currently showing
1435  if (ui_state_.show_ground) {
1436  scene_->GetScene()->ShowGroundPlane(ui_state_.show_ground, plane);
1437  scene_->ForceRedraw();
1438  }
1439  }
1440 
1441  void EnableSunFollowsCamera(bool enable) {
1442  auto low_scene = scene_->GetScene()->GetScene();
1443  if (enable) {
1444  scene_->SetOnCameraChanged([this](rendering::Camera *cam) {
1445  auto low_scene = scene_->GetScene()->GetScene();
1446  low_scene->SetSunLightDirection(cam->GetForwardVector());
1447  });
1448  low_scene->SetSunLightDirection(
1449  scene_->GetScene()->GetCamera()->GetForwardVector());
1450  scene_->SetSunInteractorEnabled(false);
1451  } else {
1452  scene_->SetOnCameraChanged(
1453  std::function<void(rendering::Camera *)>());
1454  low_scene->SetSunLightDirection(ui_state_.sun_dir);
1455  scene_->SetSunInteractorEnabled(true);
1456  }
1457  }
1458 
1459  void EnableInspectionRelatedUI(bool enable) {
1460  settings.show_skybox->SetEnabled(enable);
1461  settings.lighting->SetEnabled(enable);
1462  settings.use_ibl->SetEnabled(enable);
1463  settings.ibl_intensity->SetEnabled(enable);
1464  settings.ibl_names->SetEnabled(enable);
1465  settings.use_sun->SetEnabled(enable);
1466  settings.sun_dir->SetEnabled(enable);
1467  settings.sun_color->SetEnabled(enable);
1468  settings.mouse_buttons[SceneWidget::Controls::ROTATE_SUN]->SetEnabled(
1469  enable);
1470  settings.sun_follows_camera->SetEnabled(enable);
1471  settings.wireframe_mode->SetEnabled(enable);
1472  }
1473 
1474  void EnableBasicMode(bool enable) {
1475  auto o3dscene = scene_->GetScene();
1476  auto view = o3dscene->GetView();
1477  auto low_scene = o3dscene->GetScene();
1478  if (enable) {
1479  // Set lighting environment for inspection
1480  o3dscene->SetBackground({1.f, 1.f, 1.f, 1.f});
1481  low_scene->ShowSkybox(false);
1482  low_scene->EnableIndirectLight(false);
1483  low_scene->EnableSunLight(true);
1484  low_scene->SetSunLightIntensity(160000.f);
1485  view->SetShadowing(false, View::ShadowType::kPCF);
1486  view->SetPostProcessing(false);
1487  if (!ui_state_.sun_follows_camera) {
1488  EnableSunFollowsCamera(true);
1489  settings.sun_follows_camera->SetChecked(true);
1490  was_using_sun_follows_cam_ = false;
1491  } else {
1492  was_using_sun_follows_cam_ = true;
1493  }
1494  // Update geometry for inspection
1495  settings.wireframe_mode->SetChecked(false);
1496  EnableWireframeMode(false);
1497  UpdateGeometryForInspectionMode(true);
1498  EnableInspectionRelatedUI(false);
1499  // Force screen redraw
1500  scene_->ForceRedraw();
1501  } else {
1502  view->SetPostProcessing(true);
1503  view->SetShadowing(true, View::ShadowType::kPCF);
1504  UpdateGeometryForInspectionMode(false);
1505  EnableInspectionRelatedUI(true);
1506  if (!was_using_sun_follows_cam_) {
1507  settings.sun_follows_camera->SetChecked(false);
1508  EnableSunFollowsCamera(false);
1509  }
1510  SetUIState(ui_state_);
1511  }
1512  }
1513 
1514  void EnableWireframeMode(bool enable) {
1515  auto o3dscene = scene_->GetScene();
1516  auto view = o3dscene->GetView();
1517  auto low_scene = o3dscene->GetScene();
1518  UpdateGeometryForWireframeMode(enable);
1519  if (enable) {
1520  o3dscene->SetBackground({0.1f, 0.1f, 0.1f, 1.0f});
1521  low_scene->ShowSkybox(false);
1522  view->SetWireframe(true);
1523  scene_->ForceRedraw();
1524  } else {
1525  view->SetWireframe(false);
1526  SetUIState(ui_state_);
1527  }
1528  }
1529 
1530  void SetPointSize(int px) {
1531  ui_state_.point_size = px;
1532  settings.point_size->SetValue(double(px));
1533 
1534  px = int(ConvertToScaledPixels(px));
1535  for (auto &o : objects_) {
1536  // Ignore Models since they can never be point clouds
1537  if (o.model) continue;
1538 
1539  o.material.point_size = float(px);
1540  OverrideMaterial(o.name, o.material, ui_state_.scene_shader);
1541  }
1542  for (auto &o : inspection_objects_) {
1543  o.material.point_size = float(px);
1544  OverrideMaterial(o.name, o.material, ui_state_.scene_shader);
1545  }
1546 
1547  auto bbox_extend = scene_->GetScene()->GetBoundingBox().GetExtent();
1548  auto psize = double(std::max(5, px)) * 0.000666 *
1549  std::max(bbox_extend.x(),
1550  std::max(bbox_extend.y(), bbox_extend.z()));
1551  selections_->SetPointSize(psize);
1552 
1553  scene_->SetPickablePointSize(px);
1554  scene_->ForceRedraw();
1555  }
1556 
1557  void SetLineWidth(int px) {
1558  ui_state_.line_width = px;
1559 
1560  px = int(ConvertToScaledPixels(px));
1561  for (auto &o : objects_) {
1562  // Ignore Models since they can never be point clouds
1563  if (o.model) continue;
1564 
1565  o.material.line_width = float(px);
1566  OverrideMaterial(o.name, o.material, ui_state_.scene_shader);
1567  }
1568  for (auto &o : inspection_objects_) {
1569  o.material.line_width = float(px);
1570  OverrideMaterial(o.name, o.material, ui_state_.scene_shader);
1571  }
1572  scene_->ForceRedraw();
1573  }
1574 
1576  // Don't force material override if no change
1577  if (ui_state_.scene_shader == shader) return;
1578 
1579  ui_state_.scene_shader = shader;
1580  for (auto &o : objects_) {
1581  OverrideMaterial(o.name, o.material, shader);
1582  }
1583  for (auto &o : inspection_objects_) {
1584  OverrideMaterial(o.name, o.material, shader);
1585  }
1586  scene_->ForceRedraw();
1587  }
1588 
1589  void OverrideMaterial(const std::string &name,
1590  const MaterialRecord &original_material,
1591  O3DVisualizer::Shader shader) {
1592  bool is_lines = (original_material.shader == "unlitLine");
1593  bool is_gradient = (original_material.shader == "unlitGradient");
1594  auto scene = scene_->GetScene();
1595  // Lines are already unlit, so keep using the original shader when in
1596  // unlit mode so that we can keep the wide lines.
1597  if (shader == Shader::STANDARD || is_gradient ||
1598  (shader == Shader::UNLIT && is_lines)) {
1599  scene->ModifyGeometryMaterial(name, original_material);
1600  } else {
1601  MaterialRecord m = original_material;
1602  m.shader = GetShaderString(shader);
1603  scene->ModifyGeometryMaterial(name, m);
1604  }
1605  }
1606 
1607  float ConvertToScaledPixels(int px) {
1608  return std::round(px * window_->GetScaling());
1609  }
1610 
1612  switch (shader) {
1613  case Shader::STANDARD:
1614  return nullptr;
1615  case Shader::UNLIT:
1616  return "defaultUnlit";
1617  case Shader::NORMALS:
1618  return "normals";
1619  case Shader::DEPTH:
1620  return "depth";
1621  default:
1623  "O3DVisualizer::GetShaderString(): unhandled Shader "
1624  "value");
1625  return nullptr;
1626  }
1627  }
1628 
1629  void SetIBL(std::string path) {
1630  std::string ibl_path;
1631  if (path == "") {
1632  // Set IBL back to default
1633  ibl_path =
1634  std::string(Application::GetInstance().GetResourcePath()) +
1635  std::string("/") + std::string(kDefaultIBL);
1636  } else if (utility::filesystem::FileExists(path + "_ibl.ktx")) {
1637  // Set IBL to named IBL - probably came from selecting in UI
1638  ibl_path = path;
1639  } else if (utility::filesystem::FileExists(path)) {
1640  // User specified full path to IBL file
1641  if (path.find("_ibl.ktx") == path.size() - 8) {
1642  ibl_path = path.substr(0, path.size() - 8);
1643  } else {
1645  "Could not load IBL path. Filename must be of the form "
1646  "'name_ibl.ktx' and be paired with 'name_skybox.ktx'");
1647  return;
1648  }
1649  } else {
1650  // User specified the IBL 'stem' without the full path
1651  ibl_path =
1652  std::string(Application::GetInstance().GetResourcePath()) +
1653  "/" + path;
1654  if (!utility::filesystem::FileExists(ibl_path + "_ibl.ktx")) {
1655  return;
1656  }
1657  }
1658  ui_state_.ibl_path = ibl_path;
1659  scene_->GetScene()->GetScene()->SetIndirectLight(ibl_path);
1660  scene_->ForceRedraw();
1661  }
1662 
1663  void SetIBLIntensity(float intensity) {
1664  ui_state_.ibl_intensity = intensity;
1665  SetUIState(ui_state_);
1666  }
1667 
1668  void SetLightingProfile(const LightingProfile &profile) {
1669  Eigen::Vector3f sun_dir = {0.577f, -0.577f, -0.577f};
1670  auto scene = scene_->GetScene();
1671  scene->SetLighting(profile.profile, sun_dir);
1672  ui_state_.use_ibl = (profile.profile !=
1673  CloudViewerScene::LightingProfile::HARD_SHADOWS);
1674  ui_state_.use_sun = (profile.profile !=
1675  CloudViewerScene::LightingProfile::NO_SHADOWS);
1676  ui_state_.ibl_intensity =
1677  int(scene->GetScene()->GetIndirectLightIntensity());
1678  ui_state_.sun_intensity =
1679  int(scene->GetScene()->GetSunLightIntensity());
1680  ui_state_.sun_dir = sun_dir;
1681  ui_state_.sun_color = {1.0f, 1.0f, 1.0f};
1682  SetUIState(ui_state_);
1683  // SetUIState will set the combobox to "Custom", so undo that.
1684  this->settings.lighting->SetSelectedValue(profile.name.c_str());
1685  }
1686 
1688  if (selections_->IsActive()) {
1689  selections_->MakeInactive();
1690  }
1691 
1692  scene_->SetViewControls(mode);
1693  ui_state_.mouse_mode = mode;
1694  settings.view_mouse_mode = mode;
1695  for (const auto &t_b : settings.mouse_buttons) {
1696  t_b.second->SetOn(false);
1697  }
1698  auto it = settings.mouse_buttons.find(mode);
1699  if (it != settings.mouse_buttons.end()) {
1700  it->second->SetOn(true);
1701  }
1702  }
1703 
1704  void SetPanelOpen(const std::string &name, bool open) {
1705  if (name == settings.mouse_panel->GetText()) {
1706  settings.mouse_panel->SetIsOpen(open);
1707  } else if (name == settings.scene_panel->GetText()) {
1708  settings.scene_panel->SetIsOpen(open);
1709  } else if (name == settings.light_panel->GetText()) {
1710  settings.light_panel->SetIsOpen(open);
1711  } else if (name == settings.geometries_panel->GetText()) {
1712  settings.geometries_panel->SetIsOpen(open);
1713  }
1714  }
1715 
1716  void SetPicking() {
1717  if (selections_->GetNumberOfSets() == 0) {
1718  NewSelectionSet();
1719  }
1720  if (selections_need_update_) {
1721  UpdateSelectableGeometry();
1722  }
1723  selections_->MakeActive();
1724  }
1725 
1726  std::vector<O3DVisualizerSelections::SelectionSet> GetSelectionSets()
1727  const {
1728  return selections_->GetSets();
1729  }
1730 
1731  void SetCurrentTime(double t) {
1732  ui_state_.current_time = t;
1733  if (ui_state_.current_time > max_time_) {
1734  ui_state_.current_time = min_time_;
1735  }
1736  for (auto &o : objects_) {
1737  UpdateGeometryVisibility(o);
1738  }
1739  UpdateTimeUI();
1740 
1741  if (on_animation_) {
1742  on_animation_(ui_state_.current_time);
1743  }
1744  }
1745 
1746  void SetAnimating(bool is_animating) {
1747  if (is_animating == ui_state_.is_animating) {
1748  return;
1749  }
1750 
1751  ui_state_.is_animating = is_animating;
1752  if (is_animating) {
1753  ui_state_.current_time = max_time_;
1754  auto now = Application::GetInstance().Now();
1755  start_animation_clock_time_ = now;
1756  last_animation_tick_clock_time_ = now;
1757  if (on_animation_tick_) {
1758  window_->SetOnTickEvent(on_animation_tick_);
1759  } else {
1760  window_->SetOnTickEvent(
1761  [this]() -> bool { return this->OnAnimationTick(); });
1762  }
1763  } else {
1764  window_->SetOnTickEvent(nullptr);
1765  SetCurrentTime(0.0);
1766  next_animation_tick_clock_time_ = 0.0;
1767  }
1768  settings.time_slider->SetEnabled(!is_animating);
1769  settings.time_edit->SetEnabled(!is_animating);
1770  }
1771 
1773  O3DVisualizer &o3dvis,
1774  std::function<TickResult(O3DVisualizer &, double, double)> cb) {
1775  if (cb) {
1776  on_animation_tick_ = [this, &o3dvis, cb]() -> bool {
1777  auto now = Application::GetInstance().Now();
1778  auto dt = now - this->last_animation_tick_clock_time_;
1779  auto total_time = now - this->start_animation_clock_time_;
1780  this->last_animation_tick_clock_time_ = now;
1781 
1782  auto result = cb(o3dvis, dt, total_time);
1783 
1784  if (result == TickResult::REDRAW) {
1785  this->scene_->ForceRedraw();
1786  return true;
1787  } else {
1788  return false;
1789  }
1790  };
1791  } else {
1792  on_animation_tick_ = nullptr;
1793  }
1794  }
1795 
1796  void SetUIState(const UIState &new_state) {
1797  int point_size_changed = (new_state.point_size != ui_state_.point_size);
1798  int line_width_changed = (new_state.line_width != ui_state_.line_width);
1799  bool ibl_path_changed = (new_state.ibl_path != ui_state_.ibl_path);
1800  auto old_enabled_groups = ui_state_.enabled_groups;
1801  bool old_is_animating = ui_state_.is_animating;
1802  bool new_is_animating = new_state.is_animating;
1803  bool is_new_lighting =
1804  (ibl_path_changed || new_state.use_ibl != ui_state_.use_ibl ||
1805  new_state.use_sun != ui_state_.use_sun ||
1806  new_state.ibl_intensity != ui_state_.ibl_intensity ||
1807  new_state.sun_intensity != ui_state_.sun_intensity ||
1808  new_state.sun_dir != ui_state_.sun_dir ||
1809  new_state.sun_color != ui_state_.sun_color);
1810  bool in_basic_mode = settings.basic_mode->IsChecked();
1811 
1812  if (&new_state != &ui_state_) {
1813  ui_state_ = new_state;
1814  }
1815 
1816  if (ibl_path_changed) {
1817  SetIBL(ui_state_.ibl_path);
1818  }
1819 
1820  ShowSettings(ui_state_.show_settings, false);
1821  SetShader(ui_state_.scene_shader);
1822  SetBackground(ui_state_.bg_color, nullptr);
1823  if (!in_basic_mode) ShowSkybox(ui_state_.show_skybox);
1824  ShowAxes(ui_state_.show_axes);
1825  ShowGround(ui_state_.show_ground);
1826 
1827  if (point_size_changed) {
1828  SetPointSize(ui_state_.point_size);
1829  }
1830  if (line_width_changed) {
1831  SetLineWidth(ui_state_.line_width);
1832  }
1833 
1834  settings.use_ibl->SetChecked(ui_state_.use_ibl);
1835  settings.use_sun->SetChecked(ui_state_.use_sun);
1836  settings.ibl_intensity->SetValue(ui_state_.ibl_intensity);
1837  settings.sun_intensity->SetValue(ui_state_.sun_intensity);
1838  settings.sun_dir->SetValue(ui_state_.sun_dir);
1839  settings.sun_color->SetValue(ui_state_.sun_color);
1840  // Re-assign intensity in case it was out of range.
1841  ui_state_.ibl_intensity = settings.ibl_intensity->GetIntValue();
1842  ui_state_.sun_intensity = settings.sun_intensity->GetIntValue();
1843 
1844  if (ui_state_.sun_follows_camera) {
1845  settings.sun_dir->SetEnabled(false);
1846  settings.mouse_buttons[SceneWidget::Controls::ROTATE_SUN]
1847  ->SetEnabled(false);
1848  } else {
1849  settings.sun_dir->SetEnabled(true);
1850  settings.mouse_buttons[SceneWidget::Controls::ROTATE_SUN]
1851  ->SetEnabled(true);
1852  }
1853 
1854  if (is_new_lighting) {
1855  settings.lighting->SetSelectedValue(kCustomName);
1856  }
1857 
1858  auto *raw_scene = scene_->GetScene()->GetScene();
1859  if (!in_basic_mode) {
1860  raw_scene->EnableIndirectLight(ui_state_.use_ibl);
1861  raw_scene->SetIndirectLightIntensity(
1862  float(ui_state_.ibl_intensity));
1863  raw_scene->SetSunLightColor(ui_state_.sun_color);
1864  if (!ui_state_.sun_follows_camera) {
1865  raw_scene->SetSunLightDirection(ui_state_.sun_dir);
1866  }
1867  }
1868  raw_scene->EnableSunLight(ui_state_.use_sun);
1869  raw_scene->SetSunLightIntensity(ui_state_.sun_intensity);
1870 
1871  if (old_enabled_groups != ui_state_.enabled_groups) {
1872  for (auto &group : added_groups_) {
1873  bool enabled = (ui_state_.enabled_groups.find(group) !=
1874  ui_state_.enabled_groups.end());
1875  EnableGroup(group, enabled);
1876  }
1877  }
1878 
1879  if (old_is_animating != new_is_animating) {
1880  ui_state_.is_animating = old_is_animating;
1881  SetAnimating(new_is_animating);
1882  }
1883 
1884  scene_->ForceRedraw();
1885  }
1886 
1887  void AddGroup(const std::string &group) {
1888 #if GROUPS_USE_TREE
1889  if (added_groups_.find(group) == added_groups_.end()) {
1890  added_groups_.insert(group);
1891  ui_state_.enabled_groups.insert(group);
1892  }
1893  if (added_groups_.size() == 2) {
1894  UpdateObjectTree();
1895  }
1896 #else
1897  if (added_groups_.find(group) == added_groups_.end()) {
1898  added_groups_.insert(group);
1899  ui_state_.enabled_groups.insert(group);
1900 
1901  auto cell = std::make_shared<CheckableTextTreeCell>(
1902  group.c_str(), true, [this, group](bool is_on) {
1903  this->EnableGroup(group, is_on);
1904  });
1905  auto root = settings.groups->GetRootItem();
1906  settings.groups->AddItem(root, cell);
1907  }
1908  if (added_groups_.size() >= 2) {
1909  settings.groups_panel->SetVisible(true);
1910  }
1911 #endif // GROUPS_USE_TREE
1912  }
1913 
1914  void EnableGroup(const std::string &group, bool enable) {
1915 #if GROUPS_USE_TREE
1916  auto group_it = settings.group2itemid.find(group);
1917  if (group_it != settings.group2itemid.end()) {
1918  auto cell = settings.geometries->GetItem(group_it->second);
1919  auto group_cell =
1920  std::dynamic_pointer_cast<CheckableTextTreeCell>(cell);
1921  if (group_cell) {
1922  group_cell->GetCheckbox()->SetChecked(enable);
1923  }
1924  }
1925 #endif // GROUPS_USE_TREE
1926  if (enable) {
1927  ui_state_.enabled_groups.insert(group);
1928  } else {
1929  ui_state_.enabled_groups.erase(group);
1930  }
1931  for (auto &o : objects_) {
1932  UpdateGeometryVisibility(o);
1933  }
1934  }
1935 
1936  void AddObjectToTree(const DrawObject &o) {
1937  TreeView::ItemId parent = settings.geometries->GetRootItem();
1938 #if GROUPS_USE_TREE
1939  if (added_groups_.size() >= 2) {
1940  auto it = settings.group2itemid.find(o.group);
1941  if (it != settings.group2itemid.end()) {
1942  parent = it->second;
1943  } else {
1944  auto cell = std::make_shared<CheckableTextTreeCell>(
1945  o.group.c_str(), true,
1946  [this, group = o.group](bool is_on) {
1947  this->EnableGroup(group, is_on);
1948  });
1949  parent = settings.geometries->AddItem(parent, cell);
1950  settings.group2itemid[o.group] = parent;
1951  }
1952  }
1953 #endif // GROUPS_USE_TREE
1954 
1955  int flag = DrawObjectTreeCell::FLAG_NONE;
1956 #if !GROUPS_USE_TREE
1957  flag |= (added_groups_.size() >= 2 ? DrawObjectTreeCell::FLAG_GROUP
1958  : 0);
1959 #endif // !GROUPS_USE_TREE
1960  flag |= (min_time_ != max_time_ ? DrawObjectTreeCell::FLAG_TIME : 0);
1961  auto cell = std::make_shared<DrawObjectTreeCell>(
1962  o.name.c_str(), o.group.c_str(), o.time, o.is_visible, flag,
1963  [this, name = o.name](bool is_on) {
1964  ShowGeometry(name, is_on);
1965  });
1966  auto id = settings.geometries->AddItem(parent, cell);
1967  settings.object2itemid[o.name] = id;
1968  }
1969 
1971 #if GROUPS_USE_TREE
1972  settings.group2itemid.clear();
1973 #endif // GROUPS_USE_TREE
1974  settings.object2itemid.clear();
1975  settings.geometries->Clear();
1976 
1977  for (auto &o : objects_) {
1978  AddObjectToTree(o);
1979  }
1980  }
1981 
1983  bool enabled = (min_time_ < max_time_);
1984  settings.time_slider->SetEnabled(enabled);
1985  settings.time_edit->SetEnabled(enabled);
1986  settings.play->SetEnabled(enabled);
1987 
1988  settings.time_slider->SetLimits(min_time_, max_time_);
1989  ui_state_.current_time = std::min(
1990  max_time_, std::max(min_time_, ui_state_.current_time));
1991  UpdateTimeUI();
1992  }
1993 
1994  void UpdateTimeUI() {
1995  settings.time_slider->SetValue(ui_state_.current_time);
1996  settings.time_edit->SetValue(ui_state_.current_time);
1997  }
1998 
2000  scene_->GetScene()->ShowGeometry(o.name, IsGeometryVisible(o));
2001  scene_->ForceRedraw();
2002  }
2003 
2005  bool is_current =
2006  (o.time >= ui_state_.current_time &&
2007  o.time < ui_state_.current_time + ui_state_.time_step);
2008  bool is_group_enabled = (ui_state_.enabled_groups.find(o.group) !=
2009  ui_state_.enabled_groups.end());
2010  bool is_visible = o.is_visible;
2011  return (is_visible & is_current & is_group_enabled);
2012  }
2013 
2015  selections_->NewSet();
2016  UpdateSelectionSetList();
2017  SelectSelectionSet(int(selections_->GetNumberOfSets()) - 1);
2018  }
2019 
2020  void RemoveSelectionSet(int index) {
2021  selections_->RemoveSet(index);
2022  if (selections_->GetNumberOfSets() == 0) {
2023  // You can remove the last set, but there must always be one
2024  // set, so we re-create one. (So removing the last set has the
2025  // effect of clearing it.)
2026  selections_->NewSet();
2027  }
2028  UpdateSelectionSetList();
2029  }
2030 
2031  void SelectSelectionSet(int index) {
2032  settings.selection_sets->SetSelectedIndex(index);
2033  selections_->SelectSet(index);
2034  scene_->ForceRedraw(); // redraw with new selection highlighted
2035  }
2036 
2038  size_t n = selections_->GetNumberOfSets();
2039  int idx = settings.selection_sets->GetSelectedIndex();
2040  idx = std::max(0, idx);
2041  idx = std::min(idx, int(n) - 1);
2042 
2043  std::vector<std::string> items;
2044  items.reserve(n);
2045  for (size_t i = 0; i < n; ++i) {
2046  std::stringstream s;
2047  s << "Set " << (i + 1);
2048  items.push_back(s.str());
2049  }
2050  settings.selection_sets->SetItems(items);
2051  SelectSelectionSet(idx);
2052  window_->PostRedraw();
2053  }
2054 
2056  std::vector<SceneWidget::PickableGeometry> pickable;
2057  // Count number of meshes stored in TriangleMeshModels
2058  size_t model_mesh_count = 0;
2059  size_t model_count = 0;
2060  for (auto &o : objects_) {
2061  if (!IsGeometryVisible(o)) {
2062  continue;
2063  }
2064  if (o.model.get()) {
2065  model_count++;
2066  model_mesh_count += o.model.get()->meshes_.size();
2067  }
2068  }
2069  pickable.reserve(objects_.size() + model_mesh_count - model_count);
2070  for (auto &o : objects_) {
2071  if (!IsGeometryVisible(o)) {
2072  continue;
2073  }
2074  if (o.model.get()) {
2075  for (auto &g : o.model->meshes_) {
2076  pickable.emplace_back(g.mesh_name, g.mesh.get(),
2077  o.tgeometry.get());
2078  }
2079  } else {
2080  pickable.emplace_back(o.name, o.geometry.get(),
2081  o.tgeometry.get());
2082  }
2083  }
2084  selections_->SetSelectableGeometry(pickable);
2085  }
2086 
2088  auto now = Application::GetInstance().Now();
2089  if (now >= next_animation_tick_clock_time_) {
2090  SetCurrentTime(ui_state_.current_time + ui_state_.time_step);
2091  UpdateAnimationTickClockTime(now);
2092 
2093  return true;
2094  }
2095  return false;
2096  }
2097  void UpdateAnimationTickClockTime(double now) {
2098  next_animation_tick_clock_time_ = now + ui_state_.frame_delay;
2099  }
2100 
2101  void ExportCurrentImage(const std::string &path) {
2102  scene_->EnableSceneCaching(false);
2103  scene_->GetScene()->GetScene()->RenderToImage(
2104  [this, path](std::shared_ptr<geometry::Image> image) mutable {
2105  if (!io::WriteImage(path, *image)) {
2106  this->window_->ShowMessageBox(
2107  "Error",
2108  (std::string("Could not write image to ") +
2109  path + ".")
2110  .c_str());
2111  }
2112  scene_->EnableSceneCaching(true);
2113  });
2114  }
2115 
2116  void OnAbout() {
2117  auto &theme = window_->GetTheme();
2118  auto dlg = std::make_shared<gui::Dialog>("About");
2119 
2120  auto title = std::make_shared<gui::Label>(
2121  (std::string("CloudViewer ") + CLOUDVIEWER_VERSION).c_str());
2122  auto text = std::make_shared<gui::Label>(
2123  "The MIT License (MIT)\n"
2124  "Copyright (c) 2018-2023 www.cloudViewer.org\n\n"
2125 
2126  "Permission is hereby granted, free of charge, to any person "
2127  "obtaining a copy of this software and associated "
2128  "documentation files (the \"Software\"), to deal in the "
2129  "Software without restriction, including without limitation "
2130  "the rights to use, copy, modify, merge, publish, distribute, "
2131  "sublicense, and/or sell copies of the Software, and to "
2132  "permit persons to whom the Software is furnished to do so, "
2133  "subject to the following conditions:\n\n"
2134 
2135  "The above copyright notice and this permission notice shall "
2136  "be included in all copies or substantial portions of the "
2137  "Software.\n\n"
2138 
2139  "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY "
2140  "KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE "
2141  "WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR "
2142  "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR "
2143  "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER "
2144  "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR "
2145  "OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE "
2146  "SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.");
2147  auto ok = std::make_shared<gui::Button>("OK");
2148  ok->SetOnClicked([this]() { this->window_->CloseDialog(); });
2149 
2150  gui::Margins margins(theme.font_size);
2151  auto layout = std::make_shared<gui::Vert>(0, margins);
2152  layout->AddChild(gui::Horiz::MakeCentered(title));
2153  layout->AddFixed(theme.font_size);
2154  auto v = std::make_shared<gui::ScrollableVert>(0);
2155  v->AddChild(text);
2156  layout->AddChild(v);
2157  layout->AddFixed(theme.font_size);
2158  layout->AddChild(gui::Horiz::MakeCentered(ok));
2159  dlg->AddChild(layout);
2160 
2161  window_->ShowDialog(dlg);
2162  }
2163 
2164  void OnExportRGB() {
2165  auto dlg = std::make_shared<gui::FileDialog>(
2166  gui::FileDialog::Mode::SAVE, "Save File", window_->GetTheme());
2167  dlg->AddFilter(".png", "PNG images (.png)");
2168  dlg->AddFilter("", "All files");
2169  dlg->SetOnCancel([this]() { this->window_->CloseDialog(); });
2170  dlg->SetOnDone([this](const char *path) {
2171  this->window_->CloseDialog();
2172  this->ExportCurrentImage(path);
2173  });
2174  window_->ShowDialog(dlg);
2175  }
2176 
2177  void OnClose() { window_->Close(); }
2178 
2179  void OnToggleSettings() { ShowSettings(!ui_state_.show_settings); }
2180 
2181  std::string UniquifyName(const std::string &name) {
2182  if (added_names_.find(name) == added_names_.end()) {
2183  return name;
2184  }
2185 
2186  int n = 0;
2187  std::string unique;
2188  do {
2189  n += 1;
2190  std::stringstream s;
2191  s << name << "_" << n;
2192  unique = s.str();
2193  } while (added_names_.find(unique) != added_names_.end());
2194 
2195  return unique;
2196  }
2197 
2198  Eigen::Vector4f CalcDefaultUnlitColor() {
2199  float luminosity = 0.21f * ui_state_.bg_color.x() +
2200  0.72f * ui_state_.bg_color.y() +
2201  0.07f * ui_state_.bg_color.z();
2202  if (luminosity >= 0.5f) {
2203  return {0.0f, 0.0f, 0.0f, 1.0f};
2204  } else {
2205  return {1.0f, 1.0f, 1.0f, 1.0f};
2206  }
2207  }
2208 
2209  std::vector<std::string> GetListOfIBLs() {
2210  std::vector<std::string> ibls;
2211  std::vector<std::string> resource_files;
2212  std::string resource_path =
2215  resource_files);
2216  std::sort(resource_files.begin(), resource_files.end());
2217  for (auto &f : resource_files) {
2218  if (f.find("_ibl.ktx") == f.size() - 8) {
2220  name = name.substr(0, name.size() - 8);
2221  ibls.push_back(name);
2222  }
2223  }
2224  return ibls;
2225  }
2226 };
2227 
2228 // ----------------------------------------------------------------------------
2229 O3DVisualizer::O3DVisualizer(const std::string &title, int width, int height)
2230  : Window(title, width, height), impl_(new O3DVisualizer::Impl()) {
2231  impl_->Construct(this);
2232 
2233  // Create a message processor for incoming messages.
2234  auto on_geometry = [this](std::shared_ptr<geometry::Geometry> geom,
2235  const std::string &path, int time,
2236  const std::string &layer) {
2237  impl_->AddGeometry(path, geom, nullptr, nullptr, nullptr, layer, time,
2238  true);
2239  if (impl_->objects_.size() == 1) {
2240  impl_->ResetCameraToDefault();
2241  }
2242  };
2243  impl_->message_processor_ =
2244  std::make_shared<MessageProcessor>(this, on_geometry);
2245 
2246  // Create the app menu. We will take over the existing menubar (if any)
2247  // since a) we need to cache a pointer, and b) we should be the only
2248  // window, since the whole point of this class is to have an easy way to
2249  // visualize something with a blocking call to draw().
2250  auto menu = std::make_shared<Menu>();
2251 #if defined(__APPLE__)
2252  // The first menu item to be added on macOS becomes the application
2253  // menu (no matter its name)
2254  auto app_menu = std::make_shared<Menu>();
2255  app_menu->AddItem("About", MENU_ABOUT);
2256  menu->AddMenu("CloudViewer", app_menu);
2257 #endif // __APPLE__
2258 
2259  if (Application::GetInstance().UsingNativeWindows()) {
2260  auto file_menu = std::make_shared<Menu>();
2261  file_menu->AddItem("Export Current Image...", MENU_EXPORT_RGB);
2262  file_menu->AddSeparator();
2263  file_menu->AddItem("Close Window", MENU_CLOSE, KeyName::KEY_W);
2264  menu->AddMenu("File", file_menu);
2265  }
2266 
2267  auto actions_menu = std::make_shared<Menu>();
2268  actions_menu->AddItem("Show Settings", MENU_SETTINGS);
2269  actions_menu->SetChecked(MENU_SETTINGS, false);
2270  menu->AddMenu("Actions", actions_menu);
2271  impl_->settings.actions_menu = actions_menu.get();
2272 
2273 #if !defined(__APPLE__)
2274  auto help_menu = std::make_shared<Menu>();
2275  help_menu->AddItem("About", MENU_ABOUT);
2276  menu->AddMenu("Help", help_menu);
2277 #endif // !__APPLE__
2278 
2279  Application::GetInstance().SetMenubar(menu);
2280 
2281  SetOnMenuItemActivated(MENU_ABOUT, [this]() { this->impl_->OnAbout(); });
2282  SetOnMenuItemActivated(MENU_EXPORT_RGB,
2283  [this]() { this->impl_->OnExportRGB(); });
2284  SetOnMenuItemActivated(MENU_CLOSE, [this]() { this->impl_->OnClose(); });
2285  SetOnMenuItemActivated(MENU_SETTINGS,
2286  [this]() { this->impl_->OnToggleSettings(); });
2287 
2288  impl_->ShowSettings(false, false);
2289 }
2290 
2292 
2294  return impl_->scene_->GetScene().get();
2295 }
2296 
2297 void O3DVisualizer::StartRPCInterface(const std::string &address, int timeout) {
2298  impl_->receiver_ = std::make_shared<io::rpc::ZMQReceiver>(address, timeout);
2299  impl_->receiver_->SetMessageProcessor(impl_->message_processor_);
2300 
2301  try {
2302  utility::LogInfo("Starting to listen on {}", address);
2303  impl_->receiver_->Start();
2304  } catch (std::exception &e) {
2305  utility::LogWarning("Failed to start RPC interface: {}", e.what());
2306  }
2307 }
2308 
2310  if (impl_->receiver_) {
2311  utility::LogInfo("Stopping RPC interface");
2312  }
2313  impl_->receiver_.reset();
2314 }
2315 
2316 void O3DVisualizer::AddAction(const std::string &name,
2317  std::function<void(O3DVisualizer &)> callback) {
2318  // Add button to the "Custom Actions" segment in the UI
2319  SmallButton *button = new SmallButton(name.c_str());
2320  button->SetOnClicked([this, callback]() { callback(*this); });
2321  impl_->settings.actions->AddChild(GiveOwnership(button));
2322 
2323  SetNeedsLayout();
2324  impl_->settings.actions_panel->SetVisible(true);
2325  impl_->settings.actions_panel->SetIsOpen(true);
2326 
2327  if (impl_->can_auto_show_settings_ &&
2328  impl_->settings.actions->size() == 1) {
2329  impl_->ShowSettings(true);
2330  }
2331 
2332  // Add menu item
2333  if (impl_->settings.menuid2action.empty()) {
2334  impl_->settings.actions_menu->AddSeparator();
2335  }
2336  int id = MENU_ACTIONS_BASE + int(impl_->settings.menuid2action.size());
2337  impl_->settings.actions_menu->AddItem(name.c_str(), id);
2338  impl_->settings.menuid2action[id] = callback;
2339  SetOnMenuItemActivated(id, [this, callback]() { callback(*this); });
2340 }
2341 
2343  const Eigen::Vector4f &bg_color,
2344  std::shared_ptr<geometry::Image> bg_image /*= nullptr*/) {
2345  impl_->SetBackground(bg_color, bg_image);
2346 }
2347 
2348 void O3DVisualizer::SetShader(Shader shader) { impl_->SetShader(shader); }
2349 
2351  const std::string &name,
2352  std::shared_ptr<ccHObject> geom,
2353  const rendering::MaterialRecord *material /*=nullptr*/,
2354  const std::string &group /*= ""*/,
2355  double time /*= 0.0*/,
2356  bool is_visible /*= true*/) {
2357  impl_->AddGeometry(name, geom, nullptr, nullptr, material, group, time,
2358  is_visible);
2359 }
2360 
2362  const std::string &name,
2363  std::shared_ptr<t::geometry::Geometry> tgeom,
2364  const rendering::MaterialRecord *material /*=nullptr*/,
2365  const std::string &group /*= ""*/,
2366  double time /*= 0.0*/,
2367  bool is_visible /*= true*/) {
2368  impl_->AddGeometry(name, nullptr, tgeom, nullptr, material, group, time,
2369  is_visible);
2370 }
2371 
2373  const std::string &name,
2374  std::shared_ptr<rendering::TriangleMeshModel> model,
2375  const rendering::MaterialRecord *material /*=nullptr*/,
2376  const std::string &group /*= ""*/,
2377  double time /*= 0.0*/,
2378  bool is_visible /*= true*/) {
2379  impl_->AddGeometry(name, nullptr, nullptr, model, material, group, time,
2380  is_visible);
2381 }
2382 
2383 void O3DVisualizer::Add3DLabel(const Eigen::Vector3f &pos, const char *text) {
2384  impl_->Add3DLabel(pos, text);
2385 }
2386 
2387 void O3DVisualizer::Clear3DLabels() { impl_->Clear3DLabels(); }
2388 
2389 void O3DVisualizer::UpdateGeometry(const std::string &name,
2390  std::shared_ptr<t::geometry::Geometry> tgeom,
2391  uint32_t update_flags) {
2392  impl_->UpdateGeometry(name, tgeom, update_flags);
2393 }
2394 
2395 void O3DVisualizer::RemoveGeometry(const std::string &name) {
2396  return impl_->RemoveGeometry(name);
2397 }
2398 
2399 void O3DVisualizer::ShowGeometry(const std::string &name, bool show) {
2400  return impl_->ShowGeometry(name, show);
2401 }
2402 
2404  const std::string &name) const {
2405  return impl_->GetGeometry(name);
2406 }
2407 
2409  const std::string &name) const {
2410  return impl_->GetGeometryMaterial(name);
2411 }
2412 
2414  const std::string &name, const rendering::MaterialRecord *material) {
2415  impl_->ModifyGeometryMaterial(name, material);
2416 }
2417 
2418 void O3DVisualizer::ShowSettings(bool show) { impl_->ShowSettings(show); }
2419 
2420 void O3DVisualizer::ShowSkybox(bool show) { impl_->ShowSkybox(show); }
2421 
2422 void O3DVisualizer::SetIBL(const std::string &path) { impl_->SetIBL(path); }
2423 
2424 void O3DVisualizer::SetIBLIntensity(float intensity) {
2425  impl_->SetIBLIntensity(intensity);
2426 }
2427 
2428 void O3DVisualizer::ShowAxes(bool show) { impl_->ShowAxes(show); }
2429 
2430 void O3DVisualizer::ShowGround(bool show) { impl_->ShowGround(show); }
2431 
2433  impl_->SetGroundPlane(plane);
2434 }
2435 
2437  impl_->EnableBasicMode(enable);
2438 }
2439 
2441  impl_->EnableWireframeMode(enable);
2442 }
2443 
2444 void O3DVisualizer::SetPointSize(int point_size) {
2445  impl_->SetPointSize(point_size);
2446 }
2447 
2448 void O3DVisualizer::SetLineWidth(int line_width) {
2449  impl_->SetLineWidth(line_width);
2450 }
2451 
2453  impl_->SetMouseMode(mode);
2454 }
2455 
2456 void O3DVisualizer::SetPanelOpen(const std::string &name, bool open) {
2457  impl_->SetPanelOpen(name, open);
2458 }
2459 
2460 void O3DVisualizer::EnableGroup(const std::string &group, bool enable) {
2461  impl_->EnableGroup(group, enable);
2462 }
2463 
2464 std::vector<O3DVisualizerSelections::SelectionSet>
2466  return impl_->GetSelectionSets();
2467 }
2468 
2470  return impl_->ui_state_.frame_delay;
2471 }
2472 
2474  impl_->ui_state_.frame_delay = secs;
2475 }
2476 
2478  return impl_->ui_state_.time_step;
2479 }
2480 
2481 void O3DVisualizer::SetAnimationTimeStep(double time_step) {
2482  impl_->ui_state_.time_step = time_step;
2483  SetAnimationFrameDelay(time_step);
2484 }
2485 
2487  return impl_->max_time_ - impl_->min_time_ + GetAnimationTimeStep();
2488 }
2489 
2491  impl_->max_time_ = impl_->min_time_ + sec - GetAnimationTimeStep();
2492  impl_->UpdateTimeUIRange();
2493  impl_->settings.time_panel->SetVisible(impl_->min_time_ < impl_->max_time_);
2494 }
2495 
2497  return impl_->ui_state_.current_time;
2498 }
2499 
2500 void O3DVisualizer::SetCurrentTime(double t) { impl_->SetCurrentTime(t); }
2501 
2503  return impl_->ui_state_.is_animating;
2504 }
2505 
2506 void O3DVisualizer::SetAnimating(bool is_animating) {
2507  impl_->SetAnimating(is_animating);
2508 }
2509 
2511  const Eigen::Vector3f &center,
2512  const Eigen::Vector3f &eye,
2513  const Eigen::Vector3f &up) {
2514  impl_->SetupCamera(fov, center, eye, up);
2515 }
2516 
2518  const Eigen::Matrix4d &extrinsic) {
2519  impl_->SetupCamera(intrinsic, extrinsic);
2520 }
2521 
2522 void O3DVisualizer::SetupCamera(const Eigen::Matrix3d &intrinsic,
2523  const Eigen::Matrix4d &extrinsic,
2524  int intrinsic_width_px,
2525  int intrinsic_height_px) {
2526  impl_->SetupCamera(intrinsic, extrinsic, intrinsic_width_px,
2527  intrinsic_height_px);
2528 }
2529 
2531  return impl_->ResetCameraToDefault();
2532 }
2533 
2535  return impl_->ui_state_;
2536 }
2537 
2539  std::function<void(O3DVisualizer &, double)> cb) {
2540  if (cb) {
2541  impl_->on_animation_ = [this, cb](double t) { cb(*this, t); };
2542  } else {
2543  impl_->on_animation_ = nullptr;
2544  }
2545 }
2546 
2548  std::function<TickResult(O3DVisualizer &, double, double)> cb) {
2549  impl_->SetOnAnimationTick(*this, cb);
2550 }
2551 
2552 void O3DVisualizer::ExportCurrentImage(const std::string &path) {
2553  impl_->ExportCurrentImage(path);
2554 }
2555 
2557  auto em = context.theme.font_size;
2558  int settings_width = 16 * context.theme.font_size;
2559 #if !GROUPS_USE_TREE
2560  if (impl_->added_groups_.size() >= 2) {
2561  settings_width += 5 * context.theme.font_size;
2562  }
2563 #endif // !GROUPS_USE_TREE
2564  if (impl_->min_time_ != impl_->max_time_) {
2565  settings_width += 3 * context.theme.font_size;
2566  }
2567 
2568  auto f = GetContentRect();
2569  impl_->settings.actions->SetWidth(settings_width -
2570  int(std::round(1.5 * em)));
2571  if (impl_->settings.panel->IsVisible()) {
2572  impl_->scene_->SetFrame(
2573  Rect(f.x, f.y, f.width - settings_width, f.height));
2574  impl_->settings.panel->SetFrame(Rect(f.GetRight() - settings_width, f.y,
2575  settings_width, f.height));
2576  } else {
2577  impl_->scene_->SetFrame(f);
2578  }
2579 
2581 }
2582 
2583 } // namespace visualizer
2584 } // namespace visualization
2585 } // namespace cloudViewer
Rect frame
std::shared_ptr< core::Tensor > image
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int size
bool has_colors
bool has_normals
int height
char type
std::string name_
Definition: FilePLY.cpp:33
CloudViewerScene::LightingProfile profile
std::string name
core::Tensor result
Definition: VtkUtils.cpp:76
Contains the pinhole camera intrinsic parameters.
static std::shared_ptr< LineSet > CreateFromTriangleMesh(const ccMesh &mesh)
GeometryType
Specifies possible geometry types.
Definition: Geometry.h:28
A triangle mesh contains vertices and triangles.
Definition: TriangleMesh.h:98
ccMesh ToLegacy() const
Convert to a legacy CloudViewer TriangleMesh.
std::shared_ptr< Menu > GetMenubar() const
void SetOnClicked(std::function< void()> on_clicked)
Definition: Button.cpp:77
Lays out widgets horizontally.
Definition: Layout.h:188
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
Definition: Layout.cpp:531
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
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 SetScene(std::shared_ptr< rendering::CloudViewerScene > scene)
void DoPolygonPick(PolygonPickAction action)
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)
Lays out widgets vertically.
Definition: Layout.h:112
void SetNeedsLayout()
Instructs the window to relayout before the next draw.
Definition: Window.cpp:571
const Theme & GetTheme() const
Definition: Window.cpp:445
void AddChild(std::shared_ptr< Widget > w)
Definition: Window.cpp:597
void SetOnMenuItemActivated(Menu::ItemId item_id, std::function< void()> callback)
Definition: Window.cpp:602
visualization::rendering::Renderer & GetRenderer() const
Definition: Window.cpp:447
virtual void Layout(const LayoutContext &context)
Definition: Window.cpp:681
void ShowDialog(std::shared_ptr< Dialog > dlg)
Definition: Window.cpp:619
void SetOnTickEvent(std::function< bool()> callback)
Definition: Window.cpp:607
void CloseDialog()
Closes the dialog.
Definition: Window.cpp:648
void ShowMessageBox(const char *title, const char *message)
Definition: Window.cpp:661
float GetScaling() const
Returns the scaling factor from OS pixels to device pixels.
Definition: Window.cpp:538
virtual Eigen::Vector3f GetForwardVector() const =0
void StartRPCInterface(const std::string &address, int timeout)
Starts the RPC interface. See io/rpc/ZMQReceiver for the parameters.
void SetMouseMode(gui::SceneWidget::Controls mode)
void SetOnAnimationTick(std::function< TickResult(O3DVisualizer &, double, double)> cb)
void SetGroundPlane(rendering::Scene::GroundPlane plane)
void SetBackground(const Eigen::Vector4f &bg_color, std::shared_ptr< geometry::Image > bg_image=nullptr)
DrawObject GetGeometry(const std::string &name) const
Returns Visualizer's internal DrawObject for the named geometry.
void SetOnAnimationFrame(std::function< void(O3DVisualizer &, double)> cb)
rendering::MaterialRecord GetGeometryMaterial(const std::string &name) const
void EnableGroup(const std::string &group, bool enable)
void SetPanelOpen(const std::string &name, bool open)
void ShowGeometry(const std::string &name, bool show)
Show/hide the named geometry.
void SetupCamera(float fov, const Eigen::Vector3f &center, const Eigen::Vector3f &eye, const Eigen::Vector3f &up)
rendering::CloudViewerScene * GetScene() const
void RemoveGeometry(const std::string &name)
Removes the named geometry from the Visualizer.
void UpdateGeometry(const std::string &name, std::shared_ptr< t::geometry::Geometry > tgeom, uint32_t update_flags)
std::vector< O3DVisualizerSelections::SelectionSet > GetSelectionSets() const
void Add3DLabel(const Eigen::Vector3f &pos, const char *text)
void Layout(const gui::LayoutContext &context)
void AddAction(const std::string &name, std::function< void(O3DVisualizer &)> callback)
void ModifyGeometryMaterial(const std::string &name, const rendering::MaterialRecord *material)
O3DVisualizer(const std::string &title, int width, int height)
void AddGeometry(const std::string &name, std::shared_ptr< ccHObject > geom, const rendering::MaterialRecord *material=nullptr, const std::string &group="", double time=0.0, bool is_visible=true)
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
ImGuiContext * context
Definition: Window.cpp:76
const Theme * theme
Definition: Window.cpp:74
@ MESH
Definition: CVTypes.h:105
@ POINT_CLOUD
Definition: CVTypes.h:104
ccGuiPythonInstance * GetInstance() noexcept
Definition: Runtime.cpp:72
bool WriteImage(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
static const std::string path
Definition: PointCloud.cpp:59
bool ListFilesInDirectory(const std::string &directory, std::vector< std::string > &filenames)
Definition: FileSystem.cpp:561
bool FileExists(const std::string &filename)
Definition: FileSystem.cpp:524
std::string GetFileNameWithoutDirectory(const std::string &filename)
Definition: FileSystem.cpp:301
void Draw(const std::vector< std::shared_ptr< ccHObject >> &geometries, const std::string &window_name, int width, int height, const std::vector< DrawAction > &actions)
Definition: Draw.cpp:45
Generic file read and write utility for python interface.
cloudViewer::DgmOctree * octree
struct TreeView TreeView
Definition: sqlite3.c:14668
std::shared_ptr< geometry::Image > normal_img
std::shared_ptr< geometry::Image > clearcoat_roughness_img
std::shared_ptr< geometry::Image > clearcoat_img
std::shared_ptr< geometry::Image > albedo_img
std::shared_ptr< geometry::Image > metallic_img
std::shared_ptr< geometry::Image > roughness_img
std::shared_ptr< geometry::Image > anisotropy_img
std::shared_ptr< geometry::Image > reflectance_img
void SetOnAnimationTick(O3DVisualizer &o3dvis, std::function< TickResult(O3DVisualizer &, double, double)> cb)
void AddGeometry(const std::string &name, std::shared_ptr< geometry::Geometry > geom, std::shared_ptr< t::geometry::Geometry > tgeom, std::shared_ptr< rendering::TriangleMeshModel > model, const rendering::MaterialRecord *material, const std::string &group, double time, bool is_visible)
std::shared_ptr< O3DVisualizerSelections > selections_
std::vector< O3DVisualizerSelections::SelectionSet > GetSelectionSets() const
void ShowSettings(bool show, bool cancel_auto_show=true)
std::map< std::string, TreeView::ItemId > group2itemid
void SetupCamera(const Eigen::Matrix3d &intrinsic, const Eigen::Matrix4d &extrinsic, int intrinsic_width_px, int intrinsic_height_px)
void SetupCamera(const camera::PinholeCameraIntrinsic &intrinsic, const Eigen::Matrix4d &extrinsic)
void Add3DLabel(const Eigen::Vector3f &pos, const char *text)
void OverrideMaterial(const std::string &name, const MaterialRecord &original_material, O3DVisualizer::Shader shader)
std::shared_ptr< ccMesh > DuplicateGeometryForInspection(std::shared_ptr< ccMesh > mesh)
void SetBackground(const Eigen::Vector4f &bg_color, std::shared_ptr< geometry::Image > bg_image)
void SetPanelOpen(const std::string &name, bool open)
void UpdateGeometry(const std::string &name, std::shared_ptr< t::geometry::Geometry > tgeom, uint32_t update_flags)
std::map< std::string, TreeView::ItemId > object2itemid
void ModifyGeometryMaterial(const std::string &name, const MaterialRecord *material)
std::shared_ptr< io::rpc::ZMQReceiver > receiver_
void ShowGeometry(const std::string &name, bool show)
O3DVisualizer::DrawObject GetGeometry(const std::string &name) const
void SetupCamera(float fov, const Eigen::Vector3f &center, const Eigen::Vector3f &eye, const Eigen::Vector3f &up)
std::unordered_map< int, std::function< void(O3DVisualizer &)> > menuid2action
std::map< SceneWidget::Controls, Button * > mouse_buttons
const char * GetShaderString(O3DVisualizer::Shader shader)
void EnableGroup(const std::string &group, bool enable)
MaterialRecord GetGeometryMaterial(const std::string &name)
void SetGroundPlane(rendering::Scene::GroundPlane plane)
void CreateInspectionModeMaterial(MaterialRecord &inspect_mat, bool pcd=false)