ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
GuiSettingsView.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 <FileSystem.h>
11 #include <Logging.h>
12 
13 #include <cmath>
14 
24 
25 namespace cloudViewer {
26 namespace visualization {
27 
28 static const char *CUSTOM_LIGHTING = "Custom";
29 
30 std::shared_ptr<gui::Slider> MakeSlider(const gui::Slider::Type type,
31  const double min,
32  const double max,
33  const double value) {
34  auto slider = std::make_shared<gui::Slider>(type);
35  slider->SetLimits(min, max);
36  slider->SetValue(value);
37  return slider;
38 }
39 
41  const gui::Theme &theme,
42  const std::string &resource_path,
43  std::function<void(const char *)> on_load_ibl)
44  : model_(model), on_load_ibl_(on_load_ibl) {
45  const auto em = theme.font_size;
46  const int lm = int(std::ceil(0.5 * em));
47  const int grid_spacing = int(std::ceil(0.25 * em));
48 
49  const int separation_height = int(std::ceil(0.75 * em));
50  // (we don't want as much left margin because the twisty arrow is the
51  // only thing there, and visually it looks larger than the right.)
52  const gui::Margins base_margins(int(std::ceil(0.5 * lm)), lm, lm, lm);
53  SetMargins(base_margins);
54 
55  gui::Margins indent(em, 0, 0, 0);
56  auto view_ctrls =
57  std::make_shared<gui::CollapsableVert>("Scene controls", 0, indent);
58 
59  // Background
60  show_skybox_ = std::make_shared<gui::Checkbox>("Show skymap");
61  show_skybox_->SetOnChecked(
62  [this](bool checked) { model_.SetShowSkybox(checked); });
63 
64  bg_color_ = std::make_shared<gui::ColorEdit>();
65  bg_color_->SetOnValueChanged([this](const gui::Color &newColor) {
66  model_.SetBackgroundColor(
67  {newColor.GetRed(), newColor.GetGreen(), newColor.GetBlue()});
68  });
69 
70  auto bg_layout = std::make_shared<gui::VGrid>(2, grid_spacing);
71  bg_layout->AddChild(std::make_shared<gui::Label>("BG Color"));
72  bg_layout->AddChild(bg_color_);
73 
74  view_ctrls->AddChild(show_skybox_);
75  view_ctrls->AddFixed(int(std::ceil(0.25 * em)));
76  view_ctrls->AddChild(bg_layout);
77 
78  // Show axes
79  show_axes_ = std::make_shared<gui::Checkbox>("Show axes");
80  show_axes_->SetOnChecked(
81  [this](bool is_checked) { model_.SetShowAxes(is_checked); });
82  view_ctrls->AddFixed(separation_height);
83  view_ctrls->AddChild(show_axes_);
84 
85  // Show ground plane
86  show_ground_ = std::make_shared<gui::Checkbox>("Show ground");
87  show_ground_->SetOnChecked(
88  [this](bool is_checked) { model_.SetShowGround(is_checked); });
89  view_ctrls->AddFixed(separation_height);
90  view_ctrls->AddChild(show_ground_);
91 
92  // Lighting profiles
93  lighting_profile_ = std::make_shared<gui::Combobox>();
94  for (auto &lp : GuiSettingsModel::lighting_profiles_) {
95  lighting_profile_->AddItem(lp.name.c_str());
96  }
97  lighting_profile_->AddItem(CUSTOM_LIGHTING);
98  lighting_profile_->SetOnValueChanged([this](const char *, int index) {
99  if (index < int(GuiSettingsModel::lighting_profiles_.size())) {
100  sun_dir_->SetEnabled(true);
101  model_.SetSunFollowsCamera(false);
102  model_.SetLightingProfile(
104  if (GuiSettingsModel::lighting_profiles_[index].use_default_ibl) {
105  ibls_->SetSelectedValue(GuiSettingsModel::DEFAULT_IBL);
106  }
107  }
108  });
109 
110  auto profile_layout = std::make_shared<gui::Vert>();
111  profile_layout->AddChild(std::make_shared<gui::Label>("Lighting profiles"));
112  profile_layout->AddChild(lighting_profile_);
113  view_ctrls->AddFixed(separation_height);
114  view_ctrls->AddChild(profile_layout);
115 
116  AddChild(view_ctrls);
117  AddFixed(separation_height);
118 
119  // Advanced lighting
120  advanced_ = std::make_shared<gui::CollapsableVert>("Advanced lighting", 0,
121  indent);
122  advanced_->SetIsOpen(false);
123  AddChild(advanced_);
124 
125  // ... lighting on/off
126  advanced_->AddChild(std::make_shared<gui::Label>("Light sources"));
127  auto checkboxes = std::make_shared<gui::Horiz>();
128  ibl_enabled_ = std::make_shared<gui::Checkbox>("HDR map");
129  ibl_enabled_->SetOnChecked([this](bool checked) {
130  auto lighting = model_.GetLighting(); // copy
131  lighting.ibl_enabled = checked;
132  model_.SetCustomLighting(lighting);
133  });
134  checkboxes->AddChild(ibl_enabled_);
135 
136  sun_enabled_ = std::make_shared<gui::Checkbox>("Sun");
137  sun_enabled_->SetOnChecked([this](bool checked) {
138  auto lighting = model_.GetLighting(); // copy
139  lighting.sun_enabled = checked;
140  model_.SetCustomLighting(lighting);
141  });
142  checkboxes->AddChild(sun_enabled_);
143  advanced_->AddChild(checkboxes);
144 
145  advanced_->AddFixed(separation_height);
146 
147  // ... IBL
148  ibls_ = std::make_shared<gui::Combobox>();
149  std::vector<std::string> resource_files;
150  utility::filesystem::ListFilesInDirectory(resource_path, resource_files);
151  std::sort(resource_files.begin(), resource_files.end());
152  int n = 0;
153  for (auto &f : resource_files) {
154  if (f.find("_ibl.ktx") == f.size() - 8) {
156  name = name.substr(0, name.size() - 8);
157  ibls_->AddItem(name.c_str());
159  ibls_->SetSelectedIndex(n);
160  }
161  n++;
162  }
163  }
164  ibls_->AddItem(GuiSettingsModel::CUSTOM_IBL);
165  ibls_->SetOnValueChanged(
166  [this](const char *name, int) { on_load_ibl_(name); });
167 
168  ibl_intensity_ = MakeSlider(gui::Slider::INT, 0.0, 150000.0,
169  /*lighting_profile.ibl_intensity*/ 45000);
170  ibl_intensity_->SetOnValueChanged([this](double new_value) {
171  auto lighting = model_.GetLighting(); // copy
172  lighting.ibl_intensity = new_value;
173  model_.SetCustomLighting(lighting);
174  });
175 
176  auto ambient_layout = std::make_shared<gui::VGrid>(2, grid_spacing);
177  ambient_layout->AddChild(std::make_shared<gui::Label>("HDR map"));
178  ambient_layout->AddChild(ibls_);
179  ambient_layout->AddChild(std::make_shared<gui::Label>("Intensity"));
180  ambient_layout->AddChild(ibl_intensity_);
181 
182  advanced_->AddChild(std::make_shared<gui::Label>("Environment"));
183  advanced_->AddChild(ambient_layout);
184  advanced_->AddFixed(separation_height);
185 
186  // ... directional light (sun)
187  sun_intensity_ = MakeSlider(gui::Slider::INT, 0.0, 500000.0,
188  /*lighting_profile.sun_intensity*/ 45000);
189  sun_intensity_->SetOnValueChanged([this](double new_value) {
190  auto lighting = model_.GetLighting(); // copy
191  lighting.sun_intensity = new_value;
192  model_.SetCustomLighting(lighting);
193  });
194  sun_dir_ = std::make_shared<gui::VectorEdit>();
195  sun_dir_->SetOnValueChanged([this](const Eigen::Vector3f &dir) {
196  auto lighting = model_.GetLighting(); // copy
197  lighting.sun_dir = dir.normalized();
198  model_.SetCustomLighting(lighting);
199  });
200 
201  sun_follows_camera_ = std::make_shared<gui::Checkbox>(" ");
202  sun_follows_camera_->SetChecked(true);
203  sun_follows_camera_->SetOnChecked([this](bool checked) {
204  sun_dir_->SetEnabled(!checked);
205  model_.SetSunFollowsCamera(checked);
206  });
207 
208  sun_color_ = std::make_shared<gui::ColorEdit>();
209  sun_color_->SetOnValueChanged([this](const gui::Color &new_color) {
210  auto lighting = model_.GetLighting(); // copy
211  lighting.sun_color = {new_color.GetRed(), new_color.GetGreen(),
212  new_color.GetBlue()};
213  model_.SetCustomLighting(lighting);
214  });
215 
216  auto sun_layout = std::make_shared<gui::VGrid>(2, grid_spacing);
217  sun_layout->AddChild(std::make_shared<gui::Label>("Intensity"));
218  sun_layout->AddChild(sun_intensity_);
219  sun_layout->AddChild(std::make_shared<gui::Label>("Direction"));
220  sun_layout->AddChild(sun_dir_);
221  sun_layout->AddChild(sun_follows_camera_);
222  sun_layout->AddChild(std::make_shared<gui::Label>("Sun Follows Camera"));
223  sun_layout->AddChild(std::make_shared<gui::Label>("Color"));
224  sun_layout->AddChild(sun_color_);
225 
226  advanced_->AddChild(
227  std::make_shared<gui::Label>("Sun (Directional light)"));
228  advanced_->AddChild(sun_layout);
229 
230  // Materials
231  auto materials = std::make_shared<gui::CollapsableVert>("Material settings",
232  0, indent);
233 
234  auto mat_grid = std::make_shared<gui::VGrid>(2, grid_spacing);
235  mat_grid->AddChild(std::make_shared<gui::Label>("Type"));
236  // If edit order of items, change Update()
237  material_type_.reset(
238  new gui::Combobox({"Lit", "Unlit", "Normal map", "Depth"}));
239  material_type_->SetOnValueChanged([this](const char *, int selected_idx) {
240  switch (selected_idx) {
241  default: // fall through
242  case 0:
243  model_.SetMaterialType(GuiSettingsModel::MaterialType::LIT);
244  break;
245  case 1:
246  model_.SetMaterialType(GuiSettingsModel::MaterialType::UNLIT);
247  break;
248  case 2:
249  model_.SetMaterialType(
250  GuiSettingsModel::MaterialType::NORMAL_MAP);
251  break;
252  case 3:
253  model_.SetMaterialType(GuiSettingsModel::MaterialType::DEPTH);
254  break;
255  }
256  });
257  mat_grid->AddChild(material_type_);
258 
259  prefab_material_ = std::make_shared<gui::Combobox>();
260  for (auto &prefab : GuiSettingsModel::prefab_materials_) {
261  prefab_material_->AddItem(prefab.first.c_str());
262  }
263  prefab_material_->SetOnValueChanged([this](const char *name, int) {
264  auto mat = GuiSettingsModel::prefab_materials_.find(name);
265  if (mat != GuiSettingsModel::prefab_materials_.end()) {
266  model_.SetLitMaterial(mat->second, name);
267  } else {
268  // If it's not a preset it must be the material from the file.
269  // Set the name so that GuiVisualizer::Impl can pick it up.
270  model_.SetLitMaterial(model_.GetCurrentMaterials().lit, name);
271  }
272  });
273  mat_grid->AddChild(std::make_shared<gui::Label>("Material"));
274  mat_grid->AddChild(prefab_material_);
275 
276  material_color_ = std::make_shared<gui::ColorEdit>();
277  material_color_->SetOnValueChanged([this](const gui::Color &color) {
279  {color.GetRed(), color.GetGreen(), color.GetBlue()});
280  });
281  reset_material_color_ = std::make_shared<SmallButton>("Reset");
282  reset_material_color_->SetOnClicked([this]() { model_.ResetColors(); });
283 
284  mat_grid->AddChild(std::make_shared<gui::Label>("Color"));
285  auto color_layout = std::make_shared<gui::Horiz>();
286  color_layout->AddChild(material_color_);
287  color_layout->AddFixed(int(std::ceil(0.25 * em)));
288  color_layout->AddChild(reset_material_color_);
289  mat_grid->AddChild(color_layout);
290 
291  mat_grid->AddChild(std::make_shared<gui::Label>("Point size"));
292  point_size_ = MakeSlider(gui::Slider::INT, 1.0, 10.0, 3);
293  point_size_->SetOnValueChanged([this](double value) {
294  model_.SetPointSize(int(std::round(value)));
295  });
296  mat_grid->AddChild(point_size_);
297 
298  mat_grid->AddChild(std::make_shared<gui::Label>(""));
299  generate_normals_ = std::make_shared<SmallButton>("Estimate PCD Normals");
300  generate_normals_->SetOnClicked(
301  [this]() { model_.EstimateNormalsClicked(); });
302  generate_normals_->SetEnabled(false);
303  mat_grid->AddChild(generate_normals_);
304  mat_grid->AddChild(std::make_shared<gui::Label>("Raw Mode"));
305  basic_mode_ = std::make_shared<gui::Checkbox>("");
306  basic_mode_->SetOnChecked([this](bool checked) {
307  UpdateUIForBasicMode(checked);
308  model_.SetBasicMode(checked);
309  });
310  mat_grid->AddChild(basic_mode_);
311  mat_grid->AddChild(std::make_shared<gui::Label>("Wireframe"));
312  wireframe_mode_ = std::make_shared<gui::Checkbox>("");
313  wireframe_mode_->SetOnChecked(
314  [this](bool checked) { model_.SetWireframeMode(checked); });
315  mat_grid->AddChild(wireframe_mode_);
316 
317  materials->AddChild(mat_grid);
318 
319  AddFixed(separation_height);
320  AddChild(materials);
321 
322  Update();
323 }
324 
326  if (show) {
327  prefab_material_->AddItem(GuiSettingsModel::MATERIAL_FROM_FILE_NAME);
328  prefab_material_->ChangeItem(
330  " [default]")
331  .c_str(),
333  } else {
334  prefab_material_->RemoveItem(GuiSettingsModel::MATERIAL_FROM_FILE_NAME);
335  prefab_material_->ChangeItem(
338  " [default]")
339  .c_str());
340  }
341 }
342 
344  generate_normals_->SetEnabled(enable);
345 }
346 
348  show_skybox_->SetChecked(model_.GetShowSkybox());
349  show_axes_->SetChecked(model_.GetShowAxes());
350  bg_color_->SetValue({model_.GetBackgroundColor().x(),
351  model_.GetBackgroundColor().y(),
352  model_.GetBackgroundColor().z()});
353  auto &lighting = model_.GetLighting();
354  if (model_.GetUserHasCustomizedLighting()) {
355  lighting_profile_->SetSelectedValue(CUSTOM_LIGHTING);
356  } else {
357  if (!lighting_profile_->SetSelectedValue(lighting.name.c_str())) {
359  "Internal Error: lighting profile '{}' is not in combobox",
360  lighting.name.c_str());
361  lighting_profile_->SetSelectedValue(CUSTOM_LIGHTING);
362  }
363  }
364  ibl_enabled_->SetChecked(lighting.ibl_enabled);
365  sun_enabled_->SetChecked(lighting.sun_enabled);
366  ibl_intensity_->SetValue(lighting.ibl_intensity);
367  sun_intensity_->SetValue(lighting.sun_intensity);
368  sun_dir_->SetValue(lighting.sun_dir);
369  sun_color_->SetValue({lighting.sun_color.x(), lighting.sun_color.y(),
370  lighting.sun_color.z()});
371  auto &materials = model_.GetCurrentMaterials();
372  if (!prefab_material_->SetSelectedValue(materials.lit_name.c_str())) {
373  if (materials.lit_name.find(GuiSettingsModel::DEFAULT_MATERIAL_NAME) ==
374  0) {
375  // if we didn't find the default material, it must be appended
376  // " [default]".
377  for (int i = 0; i < prefab_material_->GetNumberOfItems(); ++i) {
378  if (materials.lit_name.find(prefab_material_->GetItem(i)) ==
379  0) {
380  prefab_material_->SetSelectedIndex(i);
381  break;
382  }
383  }
384  } else {
385  utility::LogWarning("Unknown prefab material '{}'",
386  materials.lit_name);
387  prefab_material_->SetSelectedValue(
389  }
390  }
391  switch (model_.GetMaterialType()) {
392  case GuiSettingsModel::MaterialType::LIT:
393  material_type_->SetSelectedIndex(0);
394  prefab_material_->SetEnabled(true);
395  material_color_->SetEnabled(true);
396  material_color_->SetValue({materials.lit.base_color.x(),
397  materials.lit.base_color.y(),
398  materials.lit.base_color.z()});
399  point_size_->SetValue(materials.point_size);
400  break;
401  case GuiSettingsModel::MaterialType::UNLIT:
402  material_type_->SetSelectedIndex(1);
403  prefab_material_->SetEnabled(false);
404  material_color_->SetEnabled(true);
405  material_color_->SetValue({materials.unlit.base_color.x(),
406  materials.unlit.base_color.y(),
407  materials.unlit.base_color.z()});
408  point_size_->SetValue(materials.point_size);
409  break;
410  case GuiSettingsModel::MaterialType::NORMAL_MAP:
411  material_type_->SetSelectedIndex(2);
412  prefab_material_->SetEnabled(false);
413  material_color_->SetEnabled(false);
414  material_color_->SetValue({1.0f, 1.0f, 1.0f});
415  break;
416  case GuiSettingsModel::MaterialType::DEPTH:
417  material_type_->SetSelectedIndex(3);
418  prefab_material_->SetEnabled(false);
419  material_color_->SetEnabled(false);
420  material_color_->SetValue({1.0f, 1.0f, 1.0f});
421  break;
422  }
423  reset_material_color_->SetEnabled(
424  model_.GetUserHasChangedColor() &&
425  (model_.GetMaterialType() == GuiSettingsModel::MaterialType::LIT ||
426  model_.GetMaterialType() ==
427  GuiSettingsModel::MaterialType::UNLIT));
428  point_size_->SetEnabled(model_.GetDisplayingPointClouds());
429 }
430 
431 void GuiSettingsView::UpdateUIForBasicMode(bool enable) {
432  // Enable/disable UI elements
433  show_skybox_->SetEnabled(!enable);
434  lighting_profile_->SetEnabled(!enable);
435  ibls_->SetEnabled(!enable);
436  ibl_enabled_->SetEnabled(!enable);
437  ibl_intensity_->SetEnabled(!enable);
438  sun_enabled_->SetEnabled(!enable);
439  sun_dir_->SetEnabled(!enable);
440  sun_color_->SetEnabled(!enable);
441  sun_follows_camera_->SetEnabled(!enable);
442  material_color_->SetEnabled(!enable);
443  prefab_material_->SetEnabled(!enable);
444  wireframe_mode_->SetEnabled(!enable);
445 
446  // Set lighting environment for basic/non-basic mode
447  auto lighting = model_.GetLighting(); // copy
448  if (enable) {
449  sun_follows_cam_was_on_ = sun_follows_camera_->IsChecked();
450  lighting.ibl_enabled = !enable;
451  lighting.sun_enabled = enable;
452  lighting.sun_intensity = 160000.f;
453  sun_enabled_->SetChecked(true);
454  ibl_enabled_->SetChecked(false);
455  sun_intensity_->SetValue(160000.0);
456  model_.SetCustomLighting(lighting);
457  model_.SetSunFollowsCamera(true);
458  sun_follows_camera_->SetChecked(true);
459  wireframe_mode_->SetChecked(false);
460  model_.SetWireframeMode(false);
461  } else {
463  if (!sun_follows_cam_was_on_) {
464  sun_follows_camera_->SetChecked(false);
465  model_.SetSunFollowsCamera(false);
466  }
467  }
468 }
469 
470 } // namespace visualization
471 } // namespace cloudViewer
int size
std::string name
char type
math::float4 color
const Eigen::Vector3f & GetBackgroundColor() const
static constexpr const char * DEFAULT_IBL
static constexpr const char * MATERIAL_FROM_FILE_NAME
void SetCustomLighting(const LightingProfile &profile)
void SetLitMaterial(const LitMaterial &material, const std::string &name)
void SetBackgroundColor(const Eigen::Vector3f &color)
void SetCurrentMaterialColor(const Eigen::Vector3f &color)
static constexpr const char * CUSTOM_IBL
static constexpr const char * DEFAULT_MATERIAL_NAME
void SetLightingProfile(const LightingProfile &profile)
static const std::map< std::string, const LitMaterial > prefab_materials_
static const std::vector< LightingProfile > lighting_profiles_
const LightingProfile & GetLighting() const
GuiSettingsView(GuiSettingsModel &model, const gui::Theme &theme, const std::string &resource_path, std::function< void(const char *)> on_load_ibl)
void AddFixed(int size)
Adds a fixed number of pixels after the previously added widget.
Definition: Layout.cpp:212
void SetMargins(const Margins &margins)
Definition: Layout.cpp:210
virtual void AddChild(std::shared_ptr< Widget > child)
Definition: Widget.cpp:43
#define LogWarning(...)
Definition: Logging.h:72
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
const Theme * theme
Definition: Window.cpp:74
bool ListFilesInDirectory(const std::string &directory, std::vector< std::string > &filenames)
Definition: FileSystem.cpp:561
std::string GetFileNameWithoutDirectory(const std::string &filename)
Definition: FileSystem.cpp:301
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
std::shared_ptr< gui::Slider > MakeSlider(const gui::Slider::Type type, const double min, const double max, const double value)
static const char * CUSTOM_LIGHTING
Generic file read and write utility for python interface.