10 #include <CloudViewerConfig.h>
55 #define LOAD_IN_NEW_WINDOW 0
58 namespace visualization {
62 std::shared_ptr<gui::Dialog> CreateAboutDialog(
gui::Window *window) {
63 auto &
theme = window->GetTheme();
64 auto dlg = std::make_shared<gui::Dialog>(
"About");
66 auto title = std::make_shared<gui::Label>(
67 (std::string(
"CloudViewer ") + CLOUDVIEWER_VERSION).c_str());
68 auto text = std::make_shared<gui::Label>(
69 "The MIT License (MIT)\n"
70 "Copyright (c) 2018-2025 asher-1.github.io\n\n"
72 "Permission is hereby granted, free of charge, to any person "
73 "obtaining a copy of this software and associated documentation "
74 "files (the \"Software\"), to deal in the Software without "
75 "restriction, including without limitation the rights to use, "
76 "copy, modify, merge, publish, distribute, sublicense, and/or "
77 "sell copies of the Software, and to permit persons to whom "
78 "the Software is furnished to do so, subject to the following "
81 "The above copyright notice and this permission notice shall be "
82 "included in all copies or substantial portions of the "
85 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, "
86 "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES "
87 "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND "
88 "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT "
89 "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, "
90 "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING "
91 "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR "
92 "OTHER DEALINGS IN THE SOFTWARE.");
93 auto ok = std::make_shared<gui::Button>(
"OK");
94 ok->SetOnClicked([window]() { window->CloseDialog(); });
96 gui::Margins margins(
theme.font_size);
97 auto layout = std::make_shared<gui::Vert>(0, margins);
99 layout->AddFixed(
theme.font_size);
100 auto v = std::make_shared<gui::ScrollableVert>(0);
103 layout->AddFixed(
theme.font_size);
105 dlg->AddChild(layout);
110 std::shared_ptr<gui::VGrid> CreateHelpDisplay(
gui::Window *window) {
111 auto &
theme = window->GetTheme();
113 gui::Margins margins(
theme.font_size);
114 auto layout = std::make_shared<gui::VGrid>(2, 0, margins);
115 layout->SetBackgroundColor(gui::Color(0, 0, 0, 0.5));
117 auto AddLabel = [layout](
const char *text) {
118 auto label = std::make_shared<gui::Label>(text);
119 label->SetTextColor(gui::Color(1, 1, 1));
120 layout->AddChild(label);
122 auto AddRow = [layout, &AddLabel](
const char *left,
const char *right) {
127 AddRow(
"Arcball mode",
" ");
128 AddRow(
"Left-drag",
"Rotate camera");
129 AddRow(
"Shift + left-drag",
"Forward/backward");
131 #if defined(__APPLE__)
132 AddLabel(
"Cmd + left-drag");
134 AddLabel(
"Ctrl + left-drag");
136 AddLabel(
"Pan camera");
138 #if defined(__APPLE__)
139 AddLabel(
"Opt + left-drag (up/down) ");
141 AddLabel(
"Win + left-drag (up/down) ");
143 AddLabel(
"Rotate around forward axis");
147 AddLabel(
"Ctrl + Shift + left-drag");
148 AddLabel(
"Rotate around forward axis");
150 #if defined(__APPLE__)
151 AddLabel(
"Ctrl + left-drag");
153 AddLabel(
"Alt + left-drag");
155 AddLabel(
"Rotate directional light");
157 AddRow(
"Right-drag",
"Pan camera");
158 AddRow(
"Middle-drag",
"Rotate directional light");
159 AddRow(
"Wheel",
"Forward/backward");
160 AddRow(
"Shift + Wheel",
"Change field of view");
163 AddRow(
"Fly mode",
" ");
164 AddRow(
"Left-drag",
"Rotate camera");
165 #if defined(__APPLE__)
166 AddLabel(
"Opt + left-drag");
168 AddLabel(
"Win + left-drag");
170 AddLabel(
"Rotate around forward axis");
171 AddRow(
"W",
"Forward");
172 AddRow(
"S",
"Backward");
173 AddRow(
"A",
"Step left");
174 AddRow(
"D",
"Step right");
175 AddRow(
"Q",
"Step up");
176 AddRow(
"Z",
"Step down");
177 AddRow(
"E",
"Roll left");
178 AddRow(
"R",
"Roll right");
179 AddRow(
"Up",
"Look up");
180 AddRow(
"Down",
"Look down");
181 AddRow(
"Left",
"Look left");
182 AddRow(
"Right",
"Look right");
187 std::shared_ptr<gui::VGrid> CreateCameraDisplay(
gui::Window *window) {
188 auto &
theme = window->GetTheme();
190 gui::Margins margins(
theme.font_size);
191 auto layout = std::make_shared<gui::VGrid>(2, 0, margins);
192 layout->SetBackgroundColor(gui::Color(0, 0, 0, 0.5));
194 auto AddLabel = [layout](
const char *text) {
195 auto label = std::make_shared<gui::Label>(text);
196 label->SetTextColor(gui::Color(1, 1, 1));
197 layout->AddChild(label);
199 auto AddRow = [layout, &AddLabel](
const char *left,
const char *right) {
204 AddRow(
"Position:",
"[0 0 0]");
205 AddRow(
"Forward:",
"[0 0 0]");
206 AddRow(
"Left:",
"[0 0 0]");
207 AddRow(
"Up:",
"[0 0 0]");
212 std::shared_ptr<gui::Dialog> CreateContactDialog(
gui::Window *window) {
213 auto &
theme = window->GetTheme();
214 auto em =
theme.font_size;
215 auto dlg = std::make_shared<gui::Dialog>(
"Contact Us");
217 auto title = std::make_shared<gui::Label>(
"Contact Us");
218 auto left_col = std::make_shared<gui::Label>(
223 auto right_col = std::make_shared<gui::Label>(
224 "https://Asher-1.github.io\n"
225 "https://github.com/Asher-1/ACloudViewer\n"
226 "https://Asher-1.github.io/docs/\n"
227 "https://discord.gg/D35BGvn");
228 auto ok = std::make_shared<gui::Button>(
"OK");
229 ok->SetOnClicked([window]() { window->CloseDialog(); });
231 gui::Margins margins(em);
232 auto layout = std::make_shared<gui::Vert>(0, margins);
234 layout->AddFixed(em);
236 auto columns = std::make_shared<gui::Horiz>(em, gui::Margins());
237 columns->AddChild(left_col);
238 columns->AddChild(right_col);
239 layout->AddChild(columns);
241 layout->AddFixed(em);
243 dlg->AddChild(layout);
248 bool ColorArrayIsUniform(
const std::vector<Eigen::Vector3d> &
colors) {
249 static const double e = 1.0 / 255.0;
250 static const double SQ_EPSILON = Eigen::Vector3d(e, e, e).squaredNorm();
253 for (
const auto &c :
colors) {
254 if ((
color - c).squaredNorm() > SQ_EPSILON) {
263 if (!pcd.hasColors()) {
266 return ColorArrayIsUniform(pcd.getEigenColors());
270 class DrawTimeLabel :
public gui::Label {
274 DrawTimeLabel(
gui::Window *w) : Label(
"0.0 ms") { window_ = w; }
276 gui::Size CalcPreferredSize(
const gui::LayoutContext &
context,
277 const Constraints &constraints)
const override {
278 auto h = Super::CalcPreferredSize(
context, constraints).height;
279 return gui::Size(
context.theme.font_size * 5, h);
282 DrawResult
Draw(
const gui::DrawContext &
context)
override {
286 snprintf(text,
sizeof(text) - 1,
"%.1f ms", ms);
335 std::shared_ptr<GuiSettingsView>
view_;
350 const std::string &resource_path) {
356 UpdateMaterials(renderer, defaults);
369 auto *render_scene =
scene_wgt_->GetScene()->GetScene();
370 std::string ibl_name(
path);
371 if (ibl_name.empty()) {
377 if (ibl_name.find(
"_ibl.ktx") != std::string::npos) {
378 ibl_name = ibl_name.substr(0, ibl_name.size() - 8);
380 render_scene->SetIndirectLight(ibl_name);
381 float intensity = render_scene->GetIndirectLightIntensity();
382 render_scene->SetIndirectLightIntensity(intensity);
432 auto new_mesh = mi.mesh->cloneMesh();
433 if (!new_mesh->HasTriangleNormals()) {
434 new_mesh->ComputeTriangleNormals();
437 {std::shared_ptr<ccMesh>(new_mesh),
438 mi.mesh_name, mi.material_idx});
453 auto view = o3dscene->GetView();
454 auto low_scene = o3dscene->GetScene();
459 o3dscene->SetBackground({1.f, 1.f, 1.f, 1.f});
460 low_scene->ShowSkybox(
false);
462 view->SetPostProcessing(
false);
464 view->SetPostProcessing(
true);
476 o3dscene->ShowSkybox(
true);
478 o3dscene->ShowSkybox(
false);
487 RunNormalEstimation();
505 wireframe_mat.
shader =
"unlitLine";
507 wireframe_mat.
base_color = {0.f, 0.3f, 1.f, 1.f};
513 o3dscene->GetView()->SetWireframe(
true);
514 o3dscene->SetBackground({0.1f, 0.1f, 0.1f, 1.f});
518 o3dscene->GetView()->SetWireframe(
false);
520 o3dscene->SetBackground(
521 {bcolor.x(), bcolor.y(), bcolor.z(), 1.f});
536 if (!material_changed)
return;
540 GuiSettingsModel::MaterialType::LIT &&
541 current_materials.lit_name ==
546 UpdateMaterials(renderer, current_materials);
547 UpdateSceneMaterial();
552 case GuiSettingsModel::MaterialType::LIT: {
556 case GuiSettingsModel::MaterialType::UNLIT: {
560 case GuiSettingsModel::MaterialType::NORMAL_MAP:
563 case GuiSettingsModel::MaterialType::DEPTH:
576 auto *render_scene = scene->GetScene();
579 this->
SetIBL(renderer,
"");
586 auto render_scene =
scene_wgt_->GetScene()->GetScene();
589 render_scene->SetSunLightDirection(
590 scene->GetCamera()->GetForwardVector());
595 std::function<
void(rendering::Camera *)>());
601 render_scene->EnableIndirectLight(lighting.
ibl_enabled);
602 render_scene->SetIndirectLightIntensity(
float(lighting.
ibl_intensity));
603 render_scene->SetIndirectLightRotation(lighting.
ibl_rotation);
604 render_scene->SetSunLightColor(lighting.
sun_color);
605 render_scene->SetSunLightIntensity(
float(lighting.
sun_intensity));
607 render_scene->SetSunLightDirection(lighting.
sun_dir);
609 render_scene->SetSunLightDirection(
610 scene->GetCamera()->GetForwardVector());
612 render_scene->EnableSunLight(lighting.
sun_enabled);
615 void RunNormalEstimation() {
621 std::make_shared<gui::Dialog>(
"Loading");
622 auto vert = std::make_shared<gui::Vert>(
623 0, gui::Margins(
theme.font_size));
624 auto loading_text = std::string(
625 "Estimating normals. Be patient. This may take "
627 vert->AddChild(std::make_shared<gui::Label>(
628 loading_text.c_str()));
629 loading_dlg->AddChild(vert);
640 scene3d->ClearGeometry();
641 rendering::MaterialRecord mat;
644 UpdateSceneMaterial();
652 void UpdateSceneMaterial() {
654 case GuiSettingsModel::MaterialType::LIT:
656 rendering::MaterialRecord basic_mat(
659 scene_wgt_->GetScene()->UpdateMaterial(basic_mat);
665 case GuiSettingsModel::MaterialType::UNLIT:
667 rendering::MaterialRecord basic_mat(
670 scene_wgt_->GetScene()->UpdateMaterial(basic_mat);
676 case GuiSettingsModel::MaterialType::NORMAL_MAP: {
681 case GuiSettingsModel::MaterialType::DEPTH: {
692 void UpdateMaterials(rendering::Renderer &renderer,
693 const GuiSettingsModel::Materials &materials) {
699 lit.
base_color.x() = materials.lit.base_color.x();
700 lit.base_color.y() = materials.lit.base_color.y();
701 lit.base_color.z() = materials.lit.base_color.z();
702 lit.point_size = materials.point_size;
703 lit.base_metallic = materials.lit.metallic;
704 lit.base_roughness = materials.lit.roughness;
705 lit.base_reflectance = materials.lit.reflectance;
706 lit.base_clearcoat = materials.lit.clear_coat;
707 lit.base_clearcoat_roughness = materials.lit.clear_coat_roughness;
708 lit.base_anisotropy = materials.lit.anisotropy;
711 unlit.base_color.x() = materials.unlit.base_color.x();
712 unlit.base_color.y() = materials.unlit.base_color.y();
713 unlit.base_color.z() = materials.unlit.base_color.z();
714 unlit.point_size = materials.point_size;
717 normal_depth.point_size = materials.point_size;
720 void OnNewIBL(
Window &window,
const char *
name) {
722 path += std::string(
"/") +
name +
"_ibl.ktx";
725 auto dlg = std::make_shared<gui::FileDialog>(
728 dlg->AddFilter(
".ktx",
"Khronos Texture (.ktx)");
729 dlg->SetOnCancel([&window]() { window.CloseDialog(); });
730 dlg->SetOnDone([
this, &window](
const char *
path) {
731 window.CloseDialog();
738 window.ShowDialog(dlg);
749 const std::vector<std::shared_ptr<const geometry::Geometry>>
751 const std::string &title,
762 auto on_geometry = [
this](std::shared_ptr<geometry::Geometry> geom,
763 const std::string &
path,
int time,
764 const std::string &layer) {
768 impl_->scene_wgt_->GetScene()->AddGeometry(
path, geom.get(),
772 impl_->message_processor_ =
773 std::make_shared<MessageProcessor>(
this, on_geometry);
776 void GuiVisualizer::Init() {
782 auto menu = std::make_shared<gui::Menu>();
783 #if defined(__APPLE__)
786 auto app_menu = std::make_shared<gui::Menu>();
788 app_menu->AddSeparator();
789 impl_->app_menu_custom_items_index_ = app_menu->GetNumberOfItems();
791 menu->AddMenu(
"CloudViewer", app_menu);
792 impl_->app_menu_ = app_menu;
794 auto file_menu = std::make_shared<gui::Menu>();
797 file_menu->AddSeparator();
800 #elif !defined(__APPLE__)
803 menu->AddMenu(
"File", file_menu);
805 auto settings_menu = std::make_shared<gui::Menu>();
806 settings_menu->AddItem(
"Lighting & Materials",
809 menu->AddMenu(
"Settings", settings_menu);
811 auto help_menu = std::make_shared<gui::Menu>();
812 help_menu->AddItem(
"Show Controls",
HELP_KEYS);
813 help_menu->AddItem(
"Show Camera Info",
HELP_CAMERA);
814 help_menu->AddSeparator();
817 #if defined(__APPLE__)
820 menu->AddMenu(
"Help ", help_menu);
822 menu->AddMenu(
"Help", help_menu);
829 impl_->visualizer_ =
this;
832 impl_->scene_wgt_ = std::make_shared<gui::SceneWidget>();
833 impl_->scene_wgt_->SetScene(
834 std::make_shared<rendering::CloudViewerScene>(
GetRenderer()));
835 impl_->scene_wgt_->SetOnSunDirectionChanged(
836 [
this](
const Eigen::Vector3f &new_dir) {
837 auto lighting = impl_->settings_.model_.GetLighting();
838 lighting.
sun_dir = new_dir.normalized();
839 impl_->settings_.model_.SetCustomLighting(lighting);
841 impl_->scene_wgt_->EnableSceneCaching(
true);
844 auto &settings = impl_->settings_;
845 std::string resource_path = app.GetResourcePath();
846 auto ibl_path = resource_path +
"/default";
847 auto *render_scene = impl_->scene_wgt_->GetScene()->GetScene();
848 render_scene->SetIndirectLight(ibl_path);
851 impl_->InitializeMaterials(
GetRenderer(), resource_path);
854 const auto em =
theme.font_size;
856 const int grid_spacing = int(
std::ceil(0.25 * em));
861 const int separation_height = int(
std::ceil(0.75 * em));
864 const gui::Margins base_margins(
int(std::round(0.5 * lm)), lm, lm, lm);
865 settings.wgt_base = std::make_shared<gui::Vert>(0, base_margins);
867 gui::Margins indent(em, 0, 0, 0);
869 std::make_shared<gui::CollapsableVert>(
"Mouse controls", 0, indent);
872 settings.wgt_mouse_arcball = std::make_shared<SmallToggleButton>(
"Arcball");
873 impl_->settings_.wgt_mouse_arcball->SetOn(
true);
874 settings.wgt_mouse_arcball->SetOnClicked([
this]() {
875 impl_->SetMouseControls(*
this,
876 gui::SceneWidget::Controls::ROTATE_CAMERA);
878 settings.wgt_mouse_fly = std::make_shared<SmallToggleButton>(
"Fly");
879 settings.wgt_mouse_fly->SetOnClicked([
this]() {
880 impl_->SetMouseControls(*
this, gui::SceneWidget::Controls::FLY);
882 settings.wgt_mouse_model = std::make_shared<SmallToggleButton>(
"Model");
883 settings.wgt_mouse_model->SetOnClicked([
this]() {
884 impl_->SetMouseControls(*
this,
885 gui::SceneWidget::Controls::ROTATE_MODEL);
887 settings.wgt_mouse_sun = std::make_shared<SmallToggleButton>(
"Sun");
888 settings.wgt_mouse_sun->SetOnClicked([
this]() {
889 impl_->SetMouseControls(*
this, gui::SceneWidget::Controls::ROTATE_SUN);
891 settings.wgt_mouse_ibl = std::make_shared<SmallToggleButton>(
"Environment");
892 settings.wgt_mouse_ibl->SetOnClicked([
this]() {
893 impl_->SetMouseControls(*
this, gui::SceneWidget::Controls::ROTATE_IBL);
896 auto reset_camera = std::make_shared<SmallButton>(
"Reset camera");
897 reset_camera->SetOnClicked([
this]() {
898 impl_->scene_wgt_->GoToCameraPreset(
902 auto camera_controls1 = std::make_shared<gui::Horiz>(grid_spacing);
903 camera_controls1->AddStretch();
904 camera_controls1->AddChild(settings.wgt_mouse_arcball);
905 camera_controls1->AddChild(settings.wgt_mouse_fly);
906 camera_controls1->AddChild(settings.wgt_mouse_model);
907 camera_controls1->AddStretch();
908 auto camera_controls2 = std::make_shared<gui::Horiz>(grid_spacing);
909 camera_controls2->AddStretch();
910 camera_controls2->AddChild(settings.wgt_mouse_sun);
911 camera_controls2->AddChild(settings.wgt_mouse_ibl);
912 camera_controls2->AddStretch();
913 view_ctrls->AddChild(camera_controls1);
914 view_ctrls->AddFixed(
int(
std::ceil(0.25 * em)));
915 view_ctrls->AddChild(camera_controls2);
916 view_ctrls->AddFixed(separation_height);
918 settings.wgt_base->AddChild(view_ctrls);
921 settings.view_ = std::make_shared<GuiSettingsView>(
922 settings.model_,
theme, resource_path, [
this](
const char *
name) {
923 if (std::string(name) ==
924 std::string(GuiSettingsModel::CUSTOM_IBL)) {
925 auto dlg = std::make_shared<gui::FileDialog>(
926 gui::FileDialog::Mode::OPEN,
"Open HDR Map",
928 dlg->AddFilter(
".ktx",
"Khronos Texture (.ktx)");
929 dlg->SetOnCancel([this]() { CloseDialog(); });
930 dlg->SetOnDone([this](const char *path) {
932 impl_->SetIBL(GetRenderer(), path);
936 std::string resource_path =
937 gui::Application::GetInstance().GetResourcePath();
938 impl_->SetIBL(GetRenderer(),
939 resource_path +
"/" + name +
"_ibl.ktx");
942 settings.model_.SetOnChanged([
this](
bool material_type_changed) {
943 impl_->settings_.view_->Update();
944 impl_->UpdateFromModel(GetRenderer(), material_type_changed);
946 settings.wgt_base->AddChild(settings.view_);
948 AddChild(settings.wgt_base);
951 impl_->UpdateFromModel(GetRenderer(),
false);
954 impl_->help_keys_ = CreateHelpDisplay(
this);
955 impl_->help_keys_->SetVisible(
false);
956 AddChild(impl_->help_keys_);
957 impl_->help_camera_ = CreateCameraDisplay(
this);
958 impl_->help_camera_->SetVisible(
false);
959 AddChild(impl_->help_camera_);
969 const std::vector<std::pair<std::string, gui::Menu::ItemId>> &items) {
970 #if !defined(__APPLE__)
974 if (impl_->app_menu_ && impl_->app_menu_custom_items_index_ >= 0) {
975 for (
auto &it : items) {
976 impl_->app_menu_->InsertItem(impl_->app_menu_custom_items_index_++,
977 it.first.c_str(), it.second);
979 impl_->app_menu_->InsertSeparator(
980 impl_->app_menu_custom_items_index_++);
985 std::shared_ptr<const geometry::Geometry> geometry,
bool loaded_model) {
986 auto scene3d = impl_->scene_wgt_->GetScene();
987 scene3d->ClearGeometry();
989 impl_->SetMaterialsToDefault();
993 scene3d->AddModel(
MODEL_NAME, impl_->loaded_model_);
994 impl_->settings_.model_.SetDisplayingPointClouds(
false);
995 loaded_material.
shader =
"defaultLit";
998 std::shared_ptr<const geometry::Geometry> g = geometry;
1006 auto pcd = std::static_pointer_cast<const geometry::PointCloud>(g);
1008 if (pcd->hasNormals()) {
1009 loaded_material.
shader =
"defaultLit";
1010 }
else if (pcd->hasColors() && !PointCloudHasUniformColor(*pcd)) {
1011 loaded_material.
shader =
"defaultUnlit";
1013 loaded_material.
shader =
"defaultLit";
1016 scene3d->AddGeometry(
MODEL_NAME, pcd.get(), loaded_material);
1018 impl_->settings_.model_.SetDisplayingPointClouds(
true);
1019 impl_->settings_.view_->EnableEstimateNormals(
true);
1020 if (!impl_->settings_.model_.GetUserHasChangedLightingProfile()) {
1023 impl_->settings_.model_.SetLightingProfile(
profile);
1028 auto type = impl_->settings_.model_.GetMaterialType();
1029 if (
type == GuiSettingsModel::MaterialType::LIT ||
1030 type == GuiSettingsModel::MaterialType::UNLIT) {
1031 if (loaded_material.
shader ==
"defaultUnlit") {
1032 impl_->settings_.model_.SetMaterialType(
1033 GuiSettingsModel::MaterialType::UNLIT);
1035 impl_->settings_.model_.SetMaterialType(
1036 GuiSettingsModel::MaterialType::LIT);
1041 impl_->settings_.model_.UnsetCustomDefaultColor();
1043 impl_->settings_.view_->ShowFileMaterialEntry(
true);
1044 impl_->settings_.model_.SetCurrentMaterials(
1047 impl_->settings_.view_->ShowFileMaterialEntry(
false);
1049 impl_->settings_.view_->Update();
1051 auto &bounds = scene3d->GetBoundingBox();
1052 impl_->scene_wgt_->SetupCamera(60.0, bounds,
1053 bounds.GetCenter().cast<
float>());
1056 if (impl_->basic_mode_enabled_) {
1057 impl_->SetBasicModeGeometry(
true);
1058 scene3d->GetScene()->SetSunLightDirection(
1059 scene3d->GetCamera()->GetForwardVector());
1063 impl_->scene_wgt_->ForceRedraw();
1068 const auto em =
context.theme.font_size;
1069 impl_->scene_wgt_->SetFrame(r);
1072 const auto pref = impl_->help_keys_->CalcPreferredSize(
1074 impl_->help_keys_->SetFrame(
gui::Rect(0, r.y, pref.width, pref.height));
1075 impl_->help_keys_->Layout(
context);
1078 const auto prefcam = impl_->help_camera_->CalcPreferredSize(
1080 impl_->help_camera_->SetFrame(
gui::Rect(0, r.height + r.y - prefcam.height,
1081 prefcam.width, prefcam.height));
1082 impl_->help_camera_->Layout(
context);
1085 const auto LIGHT_SETTINGS_WIDTH = 18 * em;
1086 auto light_settings_size = impl_->settings_.wgt_base->CalcPreferredSize(
1088 gui::Rect lightSettingsRect(r.width - LIGHT_SETTINGS_WIDTH, r.y,
1089 LIGHT_SETTINGS_WIDTH,
1090 std::min(r.height, light_settings_size.height));
1091 impl_->settings_.wgt_base->SetFrame(lightSettingsRect);
1097 impl_->receiver_ = std::make_shared<io::rpc::ZMQReceiver>(address, timeout);
1098 impl_->receiver_->SetMessageProcessor(impl_->message_processor_);
1101 impl_->receiver_->Start();
1102 }
catch (std::exception &e) {
1116 auto progressbar = std::make_shared<gui::ProgressBar>();
1120 auto loading_dlg = std::make_shared<gui::Dialog>(
"Loading");
1123 auto loading_text = std::string(
"Loading ") +
path;
1124 vert->AddChild(std::make_shared<gui::Label>(loading_text.c_str()));
1125 vert->AddFixed(
theme.font_size);
1126 vert->AddChild(progressbar);
1127 loading_dlg->AddChild(vert);
1135 [progressbar, value]() { progressbar->SetValue(value); });
1139 impl_->loaded_model_.meshes_.clear();
1140 impl_->loaded_model_.materials_.clear();
1141 impl_->basic_model_.meshes_.clear();
1142 impl_->basic_model_.materials_.clear();
1143 impl_->wireframe_model_.reset();
1144 impl_->loaded_pcd_.reset();
1148 bool model_success =
false;
1150 const float ioProgressAmount = 1.0f;
1161 model_success =
false;
1165 auto geometry = std::shared_ptr<geometry::Geometry>();
1166 if (!model_success) {
1167 auto cloud = std::make_shared<geometry::PointCloud>();
1168 bool success =
false;
1169 const float ioProgressAmount = 0.5f;
1184 if (!cloud->hasNormals() && !cloud->hasColors()) {
1185 cloud->EstimateNormals();
1188 cloud->NormalizeNormals();
1191 impl_->loaded_pcd_ = cloud;
1198 if (model_success || geometry) {
1200 this, [
this, model_success, geometry]() {
1208 auto msg = std::string(
"Could not load '") +
path +
"'.";
1216 impl_->scene_wgt_->EnableSceneCaching(
false);
1217 impl_->scene_wgt_->GetScene()->GetScene()->RenderToImage(
1218 [
this,
path](std::shared_ptr<geometry::Image>
image)
mutable {
1221 "Error", (std::string(
"Could not write image to ") +
1225 impl_->scene_wgt_->EnableSceneCaching(
true);
1230 auto menu_id =
MenuId(item_id);
1233 auto dlg = std::make_shared<gui::FileDialog>(
1235 dlg->AddFilter(
".ply .stl .fbx .obj .off .gltf .glb",
1236 "Triangle mesh files (.ply, .stl, .fbx, .obj, .off, "
1238 dlg->AddFilter(
".xyz .xyzn .xyzrgb .ply .pcd .pts",
1239 "Point cloud files (.xyz, .xyzn, .xyzrgb, .ply, "
1241 dlg->AddFilter(
".ply",
"Polygon files (.ply)");
1242 dlg->AddFilter(
".stl",
"Stereolithography files (.stl)");
1243 dlg->AddFilter(
".fbx",
"Autodesk Filmbox files (.fbx)");
1244 dlg->AddFilter(
".obj",
"Wavefront OBJ files (.obj)");
1245 dlg->AddFilter(
".off",
"Object file format (.off)");
1246 dlg->AddFilter(
".gltf",
"OpenGL transfer files (.gltf)");
1247 dlg->AddFilter(
".glb",
"OpenGL binary transfer files (.glb)");
1248 dlg->AddFilter(
".xyz",
"ASCII point cloud files (.xyz)");
1249 dlg->AddFilter(
".xyzn",
"ASCII point cloud with normals (.xyzn)");
1250 dlg->AddFilter(
".xyzrgb",
1251 "ASCII point cloud files with colors (.xyzrgb)");
1252 dlg->AddFilter(
".pcd",
"Point Cloud Data files (.pcd)");
1253 dlg->AddFilter(
".pts",
"3D Points files (.pts)");
1254 dlg->AddFilter(
"",
"All files");
1255 dlg->SetOnCancel([
this]() { this->
CloseDialog(); });
1256 dlg->SetOnDone([
this](
const char *
path) {
1264 auto dlg = std::make_shared<gui::FileDialog>(
1266 dlg->AddFilter(
".png",
"PNG images (.png)");
1267 dlg->AddFilter(
"",
"All files");
1268 dlg->SetOnCancel([
this]() { this->
CloseDialog(); });
1269 dlg->SetOnDone([
this](
const char *
path) {
1280 auto visibility = !impl_->settings_.wgt_base->IsVisible();
1281 impl_->settings_.wgt_base->SetVisible(visibility);
1292 bool is_visible = !impl_->help_keys_->IsVisible();
1293 impl_->help_keys_->SetVisible(is_visible);
1295 menubar->SetChecked(
HELP_KEYS, is_visible);
1299 bool is_visible = !impl_->help_camera_->IsVisible();
1300 impl_->help_camera_->SetVisible(is_visible);
1306 auto children = this->impl_->help_camera_->GetChildren();
1307 auto set_text = [](
const Eigen::Vector3f &v,
1308 std::shared_ptr<gui::Widget> label) {
1309 auto l = std::dynamic_pointer_cast<gui::Label>(label);
1312 v.x(), v.y(), v.z())
1322 impl_->scene_wgt_->SetOnCameraChanged(
1328 auto dlg = CreateAboutDialog(
this);
1333 auto dlg = CreateContactDialog(
this);
1344 auto title = std::string(
"CloudViewer - ") +
path;
1345 #if LOAD_IN_NEW_WINDOW
1346 auto frame = this->GetFrame();
1347 std::vector<std::shared_ptr<const geometry::Geometry>> nothing;
1348 auto vis = std::make_shared<GuiVisualizer>(nothing, title.c_str(),
1356 vis->LoadGeometry(
path);
std::shared_ptr< core::Tensor > image
filament::Texture::InternalFormat format
CloudViewerScene::LightingProfile profile
static std::shared_ptr< LineSet > CreateFromTriangleMesh(const ccMesh &mesh)
bool GetUserHasCustomizedLighting() const
const Eigen::Vector3f & GetBackgroundColor() const
static constexpr const char * DEFAULT_IBL
static constexpr const char * MATERIAL_FROM_FILE_NAME
void SetCustomLighting(const LightingProfile &profile)
bool GetShowSkybox() const
bool GetUserWantsEstimateNormals()
const Materials & GetCurrentMaterials() const
void SetMaterialsToDefault()
static const LightingProfile & GetDefaultPointCloudLightingProfile()
bool GetSunFollowsCamera() const
bool GetWireframeMode() const
bool GetBasicMode() const
bool GetShowGround() const
const LightingProfile & GetLighting() const
MaterialType GetMaterialType() const
void ExportCurrentImage(const std::string &path)
void StartRPCInterface(const std::string &address, int timeout)
Starts the RPC interface. See io/rpc/ReceiverBase for the parameters.
void OnMenuItemSelected(gui::Menu::ItemId item_id) override
void Layout(const gui::LayoutContext &context) override
void LoadGeometry(const std::string &path)
Loads asynchronously, will return immediately.
void SetTitle(const std::string &title)
void AddItemsToAppMenu(const std::vector< std::pair< std::string, gui::Menu::ItemId >> &items)
void OnDragDropped(const char *path) override
void SetGeometry(std::shared_ptr< const ccHObject > geometry, bool loaded_model)
bool SetIBL(const char *path)
GuiVisualizer(const std::string &title, int width, int height)
void SetMenubar(std::shared_ptr< Menu > menubar)
static Application & GetInstance()
std::shared_ptr< Menu > GetMenubar() const
const char * GetResourcePath() const
void RunInThread(std::function< void()> f)
void AddWindow(std::shared_ptr< Window > window)
Must be called on the same thread that calls Run()
void Quit()
Closes all the windows, which exits as a result.
void PostToMainThread(Window *window, std::function< void()> f)
static std::shared_ptr< Horiz > MakeCentered(std::shared_ptr< Widget > w)
void SetFocusWidget(Widget *w)
Sets.
void SetNeedsLayout()
Instructs the window to relayout before the next draw.
const Theme & GetTheme() const
void AddChild(std::shared_ptr< Widget > w)
Rect GetContentRect() const
visualization::rendering::Renderer & GetRenderer() const
void SetTitle(const char *title)
virtual void Layout(const LayoutContext &context)
void ShowDialog(std::shared_ptr< Dialog > dlg)
void CloseDialog()
Closes the dialog.
void ShowMessageBox(const char *title, const char *message)
virtual Eigen::Vector3f GetUpVector() const =0
virtual Eigen::Vector3f GetLeftVector() const =0
virtual Eigen::Vector3f GetForwardVector() const =0
virtual Eigen::Vector3f GetPosition() const =0
static void UpdateProgress(unsigned increment)
::ccPointCloud PointCloud
bool WriteImage(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
FileGeometry ReadFileGeometryType(const std::string &path)
bool ReadPointCloud(const std::string &filename, ccPointCloud &pointcloud, const ReadPointCloudOption ¶ms)
bool ReadTriangleModel(const std::string &filename, visualization::rendering::TriangleMeshModel &model, ReadTriangleModelOptions params)
static const std::string path
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
const std::string WIREFRAME_NAME
void Draw(const std::vector< std::shared_ptr< ccHObject >> &geometries, const std::string &window_name, int width, int height, const std::vector< DrawAction > &actions)
@ SETTINGS_LIGHT_AND_MATERIALS
const std::string INSPECT_MODEL_NAME
const std::string MODEL_NAME
Generic file read and write utility for python interface.
Optional parameters to ReadPointCloud.
std::function< bool(double)> update_progress
std::function< bool(double)> update_progress
rendering::Scene::Transform ibl_rotation
Eigen::Vector3f sun_color
std::shared_ptr< gui::Button > wgt_mouse_sun
std::shared_ptr< GuiSettingsView > view_
rendering::MaterialRecord unlit_material_
std::shared_ptr< gui::Button > wgt_mouse_arcball
std::shared_ptr< gui::Button > wgt_mouse_ibl
std::shared_ptr< gui::Button > wgt_mouse_model
rendering::MaterialRecord lit_material_
std::shared_ptr< gui::Vert > wgt_base
std::shared_ptr< gui::Button > wgt_mouse_fly
rendering::MaterialRecord normal_depth_material_
std::shared_ptr< io::rpc::ZMQReceiver > receiver_
void SetMouseControls(gui::Window &window, gui::SceneWidget::Controls mode)
GuiVisualizer * visualizer_
int app_menu_custom_items_index_
bool SetIBL(rendering::Renderer &renderer, const std::string &path)
void ModifyMaterialForBasicMode(rendering::MaterialRecord &basic_mat)
void UpdateFromModel(rendering::Renderer &renderer, bool material_changed)
rendering::TriangleMeshModel loaded_model_
std::shared_ptr< gui::VGrid > help_keys_
std::shared_ptr< MessageProcessor > message_processor_
std::shared_ptr< geometry::PointCloud > loaded_pcd_
rendering::TriangleMeshModel basic_model_
std::shared_ptr< gui::Menu > app_menu_
void SetBasicModeGeometry(bool enable)
void InitializeMaterials(rendering::Renderer &renderer, const std::string &resource_path)
void SetBasicMode(bool enable)
std::shared_ptr< geometry::LineSet > wireframe_model_
std::shared_ptr< gui::VGrid > help_camera_
void SetMaterialsToDefault()
std::shared_ptr< gui::SceneWidget > scene_wgt_
struct cloudViewer::visualization::GuiVisualizer::Impl::Settings settings_
std::shared_ptr< geometry::Image > normal_img
std::shared_ptr< geometry::Image > albedo_img
std::shared_ptr< geometry::Image > ao_img
std::shared_ptr< geometry::Image > metallic_img
Eigen::Vector4f emissive_color
std::shared_ptr< geometry::Image > roughness_img
std::shared_ptr< geometry::Image > reflectance_img
Eigen::Vector4f base_color
std::vector< MeshInfo > meshes_
std::vector< visualization::rendering::MaterialRecord > materials_