ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
CloudViewerScene.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 <LineSet.h>
11 #include <ecvMesh.h>
12 #include <ecvPointCloud.h>
13 
14 #include <algorithm>
15 
20 
21 namespace cloudViewer {
22 namespace visualization {
23 namespace rendering {
24 
25 const std::string kAxisObjectName("__axis__");
26 const std::string kFastModelObjectSuffix("__fast__");
27 const std::string kLowQualityModelObjectSuffix("__low__");
28 
29 namespace {
30 std::shared_ptr<ccMesh> CreateAxisGeometry(double axis_length) {
31  const double sphere_radius = 0.005 * axis_length;
32  const double cyl_radius = 0.0025 * axis_length;
33  const double cone_radius = 0.0075 * axis_length;
34  const double cyl_height = 0.975 * axis_length;
35  const double cone_height = 0.025 * axis_length;
36 
37  auto mesh_frame = ccMesh::CreateSphere(sphere_radius);
38  mesh_frame->ComputeVertexNormals();
39  mesh_frame->PaintUniformColor(Eigen::Vector3d(0.5, 0.5, 0.5));
40 
41  std::shared_ptr<ccMesh> mesh_arrow;
42  Eigen::Matrix4d transformation;
43 
44  mesh_arrow = ccMesh::CreateArrow(cyl_radius, cone_radius, cyl_height,
45  cone_height);
46  mesh_arrow->ComputeVertexNormals();
47  mesh_arrow->PaintUniformColor(Eigen::Vector3d(1.0, 0.0, 0.0));
48  transformation << 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1;
49  mesh_arrow->Transform(transformation);
50  *mesh_frame += *mesh_arrow;
51 
52  mesh_arrow = ccMesh::CreateArrow(cyl_radius, cone_radius, cyl_height,
53  cone_height);
54  mesh_arrow->ComputeVertexNormals();
55  mesh_arrow->PaintUniformColor(Eigen::Vector3d(0.0, 1.0, 0.0));
56  transformation << 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1;
57  mesh_arrow->Transform(transformation);
58  *mesh_frame += *mesh_arrow;
59 
60  mesh_arrow = ccMesh::CreateArrow(cyl_radius, cone_radius, cyl_height,
61  cone_height);
62  mesh_arrow->ComputeVertexNormals();
63  mesh_arrow->PaintUniformColor(Eigen::Vector3d(0.0, 0.0, 1.0));
64  transformation << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1;
65  mesh_arrow->Transform(transformation);
66  *mesh_frame += *mesh_arrow;
67 
68  // Add UVs because material shader for axes expects them
69  mesh_frame->triangle_uvs_.resize(mesh_frame->size() * 3, {0.0, 0.0});
70 
71  return mesh_frame;
72 }
73 
74 void RecreateAxis(Scene* scene, const ccBBox& bounds, bool enabled) {
75  scene->RemoveGeometry(kAxisObjectName);
76 
77  // Axes length should be the longer of the bounds extent or 25% of the
78  // distance from the origin. The latter is necessary so that the axis is
79  // big enough to be visible even if the object is far from the origin.
80  // See caterpillar.ply from Tanks & Temples.
81  auto axis_length = bounds.GetMaxExtent();
82  if (axis_length < 0.001) { // avoid div by zero errors in CreateAxes()
83  axis_length = 1.0;
84  }
85  axis_length = std::max(axis_length, 0.25f * bounds.getCenter().norm());
86  auto mesh = CreateAxisGeometry(axis_length);
87  MaterialRecord mat;
88  mat.shader = "defaultUnlit";
89  scene->AddGeometry(kAxisObjectName, *mesh, mat);
90  // It looks awkward to have the axis cast a a shadow, and even stranger
91  // to receive a shadow.
92  scene->GeometryShadows(kAxisObjectName, false, false);
93  scene->ShowGeometry(kAxisObjectName, enabled);
94 }
95 
96 } // namespace
97 
98 CloudViewerScene::CloudViewerScene(Renderer& renderer) : renderer_(renderer) {
99  scene_ = renderer_.CreateScene();
100  auto scene = renderer_.GetScene(scene_);
101  view_ = scene->AddView(0, 0, 1, 1);
102  SetBackground({1.0f, 1.0f, 1.0f, 1.0f});
103 
104  SetLighting(LightingProfile::MED_SHADOWS, {0.577f, -0.577f, -0.577f});
105 
106  RecreateAxis(scene, bounds_, false);
107 }
108 
110  ClearGeometry();
111  auto scene = renderer_.GetScene(scene_);
113  scene->RemoveView(view_);
114 }
115 
117  auto scene = renderer_.GetScene(scene_);
118  return scene->GetView(view_);
119 }
120 
121 void CloudViewerScene::SetViewport(std::int32_t x,
122  std::int32_t y,
123  std::uint32_t width,
124  std::uint32_t height) {
125  // Setup the view in which we render to a texture. Since this is just a
126  // texture, we want our viewport to be the entire texture.
127  auto view = GetView();
128  // Since we are rendering into a texture (EnableViewCaching(true) below),
129  // we need to use the entire texture; the viewport passed in is the viewport
130  // with respect to the window, and we are setting the viewport with respect
131  // to the render target here.
132  view->SetViewport(0, 0, width, height);
133  view->EnableViewCaching(true);
134 }
135 
136 void CloudViewerScene::ShowSkybox(bool enable) {
137  auto scene = renderer_.GetScene(scene_);
138  scene->ShowSkybox(enable);
139 }
140 
141 void CloudViewerScene::ShowAxes(bool enable) {
142  auto scene = renderer_.GetScene(scene_);
143  if (enable && axis_dirty_) {
144  RecreateAxis(scene, bounds_, false);
145  axis_dirty_ = false;
146  }
147  scene->ShowGeometry(kAxisObjectName, enable);
148 }
149 
151  const Eigen::Vector4f& color,
152  std::shared_ptr<geometry::Image> image /*=0*/) {
153  auto scene = renderer_.GetScene(scene_);
154  scene->SetBackground(color, image);
155  background_color = color;
156 }
157 
158 const Eigen::Vector4f CloudViewerScene::GetBackgroundColor() const {
159  return background_color;
160 }
161 
163  auto scene = renderer_.GetScene(scene_);
164  scene->EnableGroundPlane(enable, plane);
165 }
166 
168  const Eigen::Vector3f& sun_dir) {
169  auto scene = renderer_.GetScene(scene_);
170 
172  if (scene->GetIndirectLight().empty()) {
174  scene->SetIndirectLight(std::string(path) + "/default");
175  }
176  }
177 
178  Eigen::Vector3f sun_color(1.0f, 1.0f, 1.0f);
179 
180  // These intensities have been chosen so that a white object on a white
181  // background is clearly visible even when the highlight is next to the
182  // background. Increasing the intensities much more make the highlight's
183  // white too similar to the background's white.
184  switch (profile) {
186  scene->EnableIndirectLight(false);
187  scene->EnableSunLight(true);
188  scene->SetSunLight(sun_dir, sun_color, 100000);
189  break;
191  scene->EnableIndirectLight(true);
192  scene->EnableSunLight(true);
193  scene->SetIndirectLightIntensity(10000);
194  scene->SetSunLight(sun_dir, sun_color, 85000);
195  break;
196  default:
198  scene->EnableIndirectLight(true);
199  scene->EnableSunLight(true);
200  scene->SetIndirectLightIntensity(20000);
201  scene->SetSunLight(sun_dir, sun_color, 80000);
202  break;
204  scene->EnableIndirectLight(true);
205  scene->EnableSunLight(true);
206  scene->SetIndirectLightIntensity(37500);
207  scene->SetSunLight(sun_dir, sun_color, 75000);
208  break;
210  scene->EnableIndirectLight(true);
211  scene->SetIndirectLightIntensity(37500);
212  scene->EnableSunLight(false);
213  break;
214  }
215 }
216 
218  auto scene = renderer_.GetScene(scene_);
219  for (auto& g : geometries_) {
220  scene->RemoveGeometry(g.second.name);
221  if (!g.second.fast_name.empty()) {
222  scene->RemoveGeometry(g.second.fast_name);
223  }
224  if (!g.second.low_name.empty()) {
225  scene->RemoveGeometry(g.second.low_name);
226  }
227  }
228  geometries_.clear();
229  bounds_ = ccBBox();
230  axis_dirty_ = true;
231 }
232 
234  const std::string& name,
235  const ccHObject* geom,
236  const MaterialRecord& mat,
237  bool add_downsampled_copy_for_fast_rendering /*= true*/) {
238  size_t downsample_threshold = SIZE_MAX;
239  std::string fast_name;
240  if (add_downsampled_copy_for_fast_rendering) {
241  fast_name = name + "." + kFastModelObjectSuffix;
242  downsample_threshold = downsample_threshold_;
243  }
244 
245  auto scene = renderer_.GetScene(scene_);
246  if (scene->AddGeometry(name, *geom, mat, fast_name, downsample_threshold)) {
247  bounds_ += scene->GetGeometryBoundingBox(name);
248  GeometryData info(name, "");
249  // If the downsampled object got created, add it. It may not have been
250  // created if downsampling wasn't enabled or if the object does not meet
251  // the threshold.
252  if (add_downsampled_copy_for_fast_rendering &&
253  scene->HasGeometry(fast_name)) {
254  info.fast_name = fast_name;
255  geometries_[fast_name] = info;
256  }
257  geometries_[name] = info;
258  SetGeometryToLOD(info, lod_);
259  }
260 
261  axis_dirty_ = true;
262 }
263 
265  const std::string& name,
266  const t::geometry::Geometry* geom,
267  const MaterialRecord& mat,
268  bool add_downsampled_copy_for_fast_rendering /*= true*/) {
269  size_t downsample_threshold = SIZE_MAX;
270  std::string fast_name;
271  if (add_downsampled_copy_for_fast_rendering) {
272  fast_name = name + "." + kFastModelObjectSuffix;
273  downsample_threshold = downsample_threshold_;
274  }
275 
276  auto scene = renderer_.GetScene(scene_);
277  if (scene->AddGeometry(name, *geom, mat, fast_name, downsample_threshold)) {
278  auto bbox = scene->GetGeometryBoundingBox(name);
279  bounds_ += bbox;
280  GeometryData info(name, "");
281  // If the downsampled object got created, add it. It may not have been
282  // created if downsampling wasn't enabled or if the object does not meet
283  // the threshold.
284  if (add_downsampled_copy_for_fast_rendering &&
285  scene->HasGeometry(fast_name)) {
286  info.fast_name = fast_name;
287 
288  auto lowq_name = name + kLowQualityModelObjectSuffix;
289  auto bbox_geom =
291  MaterialRecord bbox_mat;
292  bbox_mat.base_color = {1.0f, 0.5f, 0.0f, 1.0f}; // orange
293  bbox_mat.shader = "unlitSolidColor";
294  scene->AddGeometry(lowq_name, *bbox_geom, bbox_mat);
295  info.low_name = lowq_name;
296  }
297  geometries_[name] = info;
298  SetGeometryToLOD(info, lod_);
299  }
300 
301  // Axes may need to be recreated
302  axis_dirty_ = true;
303 }
304 
305 bool CloudViewerScene::HasGeometry(const std::string& name) const {
306  auto scene = renderer_.GetScene(scene_);
307  return scene->HasGeometry(name);
308 }
309 
310 void CloudViewerScene::RemoveGeometry(const std::string& name) {
311  auto scene = renderer_.GetScene(scene_);
312  auto g = geometries_.find(name);
313  if (g != geometries_.end()) {
314  scene->RemoveGeometry(name);
315  if (!g->second.fast_name.empty()) {
316  scene->RemoveGeometry(g->second.fast_name);
317  }
318  if (!g->second.low_name.empty()) {
319  scene->RemoveGeometry(g->second.low_name);
320  }
321  geometries_.erase(name);
322  }
323 }
324 
325 bool CloudViewerScene::GeometryIsVisible(const std::string& name) {
326  auto scene = renderer_.GetScene(scene_);
327  return scene->GeometryIsVisible(name);
328 }
329 
331  const Eigen::Matrix4d& Transform) {
332  auto scene = renderer_.GetScene(scene_);
333  auto g = geometries_.find(name);
334  if (g != geometries_.end()) {
335  const Eigen::Transform<float, 3, Eigen::Affine> t(
336  Transform.cast<float>());
337  scene->SetGeometryTransform(name, t);
338  if (!g->second.fast_name.empty()) {
339  scene->SetGeometryTransform(g->second.fast_name, t);
340  }
341  if (!g->second.low_name.empty()) {
342  scene->SetGeometryTransform(g->second.low_name, t);
343  }
344  }
345 }
346 
348  const std::string& name) {
349  auto scene = renderer_.GetScene(scene_);
350  return scene->GetGeometryTransform(name).matrix().cast<double>();
351 }
352 
354  const MaterialRecord& mat) {
355  auto scene = renderer_.GetScene(scene_);
356  scene->OverrideMaterial(name, mat);
357  auto it = geometries_.find(name);
358  if (it != geometries_.end()) {
359  if (!it->second.fast_name.empty()) {
360  scene->OverrideMaterial(it->second.fast_name, mat);
361  }
362  // Don't want to override low_name, as that is a bounding box.
363  }
364 }
365 
366 void CloudViewerScene::ShowGeometry(const std::string& name, bool show) {
367  auto it = geometries_.find(name);
368  if (it != geometries_.end()) {
369  it->second.visible = show;
370 
371  int n_lowq_visible = 0;
372  for (auto& g : geometries_) {
373  if (g.second.visible && !g.second.low_name.empty()) {
374  n_lowq_visible += 1;
375  }
376  }
377  use_low_quality_if_available_ = (n_lowq_visible > 1);
378 
379  SetGeometryToLOD(it->second, lod_);
380  }
381 }
382 
383 void CloudViewerScene::AddModel(const std::string& name,
384  const TriangleMeshModel& model) {
385  auto scene = renderer_.GetScene(scene_);
386  if (scene->AddGeometry(name, model)) {
387  GeometryData info(name, "");
388  bounds_ += scene->GetGeometryBoundingBox(name);
389  geometries_[name] = info;
390  scene->ShowGeometry(name, true);
391  }
392 
393  axis_dirty_ = true;
394 }
395 
397  auto scene = renderer_.GetScene(scene_);
398  for (auto& g : geometries_) {
399  scene->OverrideMaterial(g.second.name, mat);
400  if (!g.second.fast_name.empty()) {
401  scene->OverrideMaterial(g.second.fast_name, mat);
402  }
403  // Low-quality model is a bounding box right now, and we want it to
404  // be a solid color, so we do not want to override.
405  // if (!g.second.low_name.empty()) {
406  // scene->OverrideMaterial(g.second.low_name, mat);
407  // }
408  }
409 }
410 
412  const TriangleMeshModel& model) {
413  auto scene = renderer_.GetScene(scene_);
414  scene->RemoveGeometry(name);
415  scene->AddGeometry(name, model);
416 }
417 
418 std::vector<std::string> CloudViewerScene::GetGeometries() {
419  std::vector<std::string> names;
420  names.reserve(geometries_.size());
421  for (auto& it : geometries_) {
422  names.push_back(it.first);
423  }
424  return names;
425 }
426 
428  if (lod != lod_) {
429  lod_ = lod;
430 
431  for (auto& g : geometries_) {
432  SetGeometryToLOD(g.second, lod);
433  }
434  }
435 }
436 
437 void CloudViewerScene::SetGeometryToLOD(const GeometryData& data, LOD lod) {
438  auto scene = renderer_.GetScene(scene_);
439  scene->ShowGeometry(data.name, false);
440  if (!data.fast_name.empty()) {
441  scene->ShowGeometry(data.fast_name, false);
442  }
443  if (!data.low_name.empty()) {
444  scene->ShowGeometry(data.low_name, false);
445  }
446 
447  if (data.visible) {
448  if (lod == LOD::HIGH_DETAIL) {
449  scene->ShowGeometry(data.name, true);
450  } else {
451  std::string id;
452  if (use_low_quality_if_available_) {
453  id = data.low_name;
454  }
455  if (id.empty()) {
456  id = data.fast_name;
457  }
458  if (id.empty()) {
459  id = data.name;
460  }
461  scene->ShowGeometry(id, true);
462  }
463  }
464 }
465 
467 
468 Scene* CloudViewerScene::GetScene() const { return renderer_.GetScene(scene_); }
469 
471  auto scene = renderer_.GetScene(scene_);
472  auto view = scene->GetView(view_);
473  return view->GetCamera();
474 }
475 
476 Renderer& CloudViewerScene::GetRenderer() const { return renderer_; }
477 
478 } // namespace rendering
479 } // namespace visualization
480 } // namespace cloudViewer
std::shared_ptr< core::Tensor > image
int width
std::string name
int height
math::float4 color
CloudViewerScene::LightingProfile profile
Type norm() const
Returns vector norm.
Definition: CVGeom.h:424
Bounding box structure.
Definition: ecvBBox.h:25
PointCoordinateType GetMaxExtent() const
Definition: ecvBBox.h:156
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
static std::shared_ptr< ccMesh > CreateArrow(double cylinder_radius=1.0, double cone_radius=1.5, double cylinder_height=5.0, double cone_height=4.0, int resolution=20, int cylinder_split=4, int cone_split=1)
static std::shared_ptr< ccMesh > CreateSphere(double radius=1.0, int resolution=20, bool create_uv_map=false)
Vector3Tpl< T > getCenter() const
Returns center.
Definition: BoundingBox.h:164
static std::shared_ptr< LineSet > CreateFromAxisAlignedBoundingBox(const ccBBox &box)
Factory function to create a LineSet from an ccBBox.
The base geometry class.
Definition: Geometry.h:23
void SetViewport(std::int32_t x, std::int32_t y, std::uint32_t width, std::uint32_t height)
Eigen::Matrix4d GetGeometryTransform(const std::string &name)
void ShowGeometry(const std::string &name, bool show)
Shows or hides the geometry with the specified name.
void ModifyGeometryMaterial(const std::string &name, const MaterialRecord &mat)
void UpdateModelMaterial(const std::string &name, const TriangleMeshModel &model)
Updates the named model to use this material.
void SetBackground(const Eigen::Vector4f &color, std::shared_ptr< geometry::Image > image=nullptr)
void SetGeometryTransform(const std::string &name, const Eigen::Matrix4d &transform)
void ShowGroundPlane(bool enable, Scene::GroundPlane plane)
void UpdateMaterial(const MaterialRecord &mat)
Updates all geometries to use this material.
void SetLighting(LightingProfile profile, const Eigen::Vector3f &sun_dir)
void AddGeometry(const std::string &name, const ccHObject *geom, const MaterialRecord &mat, bool add_downsampled_copy_for_fast_rendering=true)
Adds a geometry with the specified name. Default visible is true.
void AddModel(const std::string &name, const TriangleMeshModel &model)
virtual Scene * GetScene(const SceneHandle &id) const =0
virtual ccBBox GetGeometryBoundingBox(const std::string &object_name)=0
virtual bool GeometryIsVisible(const std::string &object_name)=0
virtual void RemoveGeometry(const std::string &object_name)=0
virtual Transform GetGeometryTransform(const std::string &object_name)=0
virtual void SetBackground(const Eigen::Vector4f &color, const std::shared_ptr< geometry::Image > image=nullptr)=0
virtual bool HasGeometry(const std::string &object_name) const =0
virtual View * GetView(const ViewHandle &view_id) const =0
virtual void EnableGroundPlane(bool enable, GroundPlane plane)=0
virtual void OverrideMaterial(const std::string &object_name, const MaterialRecord &material)=0
virtual void ShowGeometry(const std::string &object_name, bool show)=0
virtual Camera * GetCamera() const =0
int max(int a, int b)
Definition: cutil_math.h:48
#define SIZE_MAX
void Transform(benchmark::State &state, const core::Device &device)
Definition: PointCloud.cpp:127
static const std::string path
Definition: PointCloud.cpp:59
const std::string kLowQualityModelObjectSuffix("__low__")
const std::string kAxisObjectName("__axis__")
const std::string kFastModelObjectSuffix("__fast__")
Generic file read and write utility for python interface.