ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilamentScene.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 
8 // 4068: Filament has some clang-specific vectorizing pragma's that MSVC flags
9 // 4146: PixelBufferDescriptor assert unsigned is positive before subtracting
10 // but MSVC can't figure that out.
11 // 4293: Filament's utils/algorithm.h utils::details::clz() does strange
12 // things with MSVC. Somehow sizeof(unsigned int) > 4, but its size is
13 // 32 so that x >> 32 gives a warning. (Or maybe the compiler can't
14 // determine the if statement does not run.)
15 // 4305: LightManager.h needs to specify some constants as floats
16 #include <unordered_set>
17 
18 #ifdef _MSC_VER
19 #pragma warning(push)
20 #pragma warning(disable : 4068 4146 4293 4305)
21 #endif // _MSC_VER
22 
23 #include <backend/PixelBufferDescriptor.h> // bogus 4146 warning on MSVC
24 #include <filament/Engine.h>
25 #include <filament/IndirectLight.h>
26 #include <filament/LightManager.h>
27 #include <filament/MaterialInstance.h>
28 #include <filament/Renderer.h>
29 #include <filament/Scene.h>
30 #include <filament/Skybox.h>
31 #include <filament/SwapChain.h>
32 #include <filament/Texture.h>
33 #include <filament/TextureSampler.h>
34 #include <filament/TransformManager.h>
35 #include <filament/VertexBuffer.h>
36 #include <filament/View.h>
37 #include <geometry/SurfaceOrientation.h>
38 #include <utils/EntityManager.h>
39 
40 #ifdef _MSC_VER
41 #pragma warning(pop)
42 #endif // _MSC_VER
43 
44 // We do NOT include this first because it includes Image.h, which includes
45 // the fmt library, which includes windows.h (on Windows), which #defines
46 // OPAQUE (!!??) which causes syntax errors with filament/View.h which tries
47 // to make OPAQUE an member of a class enum. So include this after all the
48 // Filament headers to avoid this problem.
49 #if 1 // (enclose in #if so that apply-style doesn't move this)
51 #endif // 1
52 
53 #include <LineSet.h>
54 #include <Logging.h>
55 #include <ecvBBox.h>
56 #include <ecvMesh.h>
57 #include <ecvPointCloud.h>
58 
59 #include "t/geometry/PointCloud.h"
70 
71 namespace { // avoid polluting global namespace, since only used here
73 
74 static void DeallocateBuffer(void* buffer, size_t size, void* user_ptr) {
75  free(buffer);
76 }
77 
78 const std::string kBackgroundName = "__background";
79 const std::string kGroundPlaneName = "__ground_plane";
80 const Eigen::Vector4f kDefaultGroundPlaneColor(0.5f, 0.5f, 0.5f, 1.f);
81 
82 namespace defaults_mapping {
83 
86 using ResourceManager =
88 
89 std::unordered_map<std::string, MaterialHandle> shader_mappings = {
90  {"defaultLit", ResourceManager::kDefaultLit},
91  {"defaultLitTransparency",
92  ResourceManager::kDefaultLitWithTransparency},
93  {"defaultLitSSR", ResourceManager::kDefaultLitSSR},
94  {"defaultUnlitTransparency",
95  ResourceManager::kDefaultUnlitWithTransparency},
96  {"defaultUnlit", ResourceManager::kDefaultUnlit},
97  {"normals", ResourceManager::kDefaultNormalShader},
98  {"depth", ResourceManager::kDefaultDepthShader},
99  {"depthValue", ResourceManager::kDefaultDepthValueShader},
100  {"unlitGradient", ResourceManager::kDefaultUnlitGradientShader},
101  {"unlitSolidColor", ResourceManager::kDefaultUnlitSolidColorShader},
102  {"unlitPolygonOffset",
103  ResourceManager::kDefaultUnlitPolygonOffsetShader},
104  {"unlitBackground", ResourceManager::kDefaultUnlitBackgroundShader},
105  {"infiniteGroundPlane", ResourceManager::kInfinitePlaneShader},
106  {"unlitLine", ResourceManager::kDefaultLineShader},
107  {"gaussianSplat", ResourceManager::kGaussianSplatShader}};
108 
109 MaterialHandle kColorOnlyMesh = ResourceManager::kDefaultUnlit;
110 MaterialHandle kPlainMesh = ResourceManager::kDefaultLit;
111 MaterialHandle kMesh = ResourceManager::kDefaultLit;
112 
113 MaterialHandle kColoredPointcloud = ResourceManager::kDefaultUnlit;
114 MaterialHandle kPointcloud = ResourceManager::kDefaultLit;
115 
116 MaterialHandle kLineset = ResourceManager::kDefaultUnlit;
117 
118 } // namespace defaults_mapping
119 
120 namespace converters {
121 using EigenMatrix = cloudViewer::visualization::rendering::FilamentScene::
122  Transform::MatrixType;
123 using FilamentMatrix = filament::math::mat4f;
124 EigenMatrix EigenMatrixFromFilamentMatrix(const filament::math::mat4f& fm) {
125  EigenMatrix em;
126 
127  em << fm(0, 0), fm(0, 1), fm(0, 2), fm(0, 3), fm(1, 0), fm(1, 1), fm(1, 2),
128  fm(1, 3), fm(2, 0), fm(2, 1), fm(2, 2), fm(2, 3), fm(3, 0),
129  fm(3, 1), fm(3, 2), fm(3, 3);
130 
131  return em;
132 }
133 
134 FilamentMatrix FilamentMatrixFromEigenMatrix(const EigenMatrix& em) {
135  // Filament matrices is column major and Eigen's - row major
136  return FilamentMatrix(FilamentMatrix::row_major_init{
137  em(0, 0), em(0, 1), em(0, 2), em(0, 3), em(1, 0), em(1, 1),
138  em(1, 2), em(1, 3), em(2, 0), em(2, 1), em(2, 2), em(2, 3),
139  em(3, 0), em(3, 1), em(3, 2), em(3, 3)});
140 }
141 } // namespace converters
143 } // namespace
144 
145 namespace cloudViewer {
146 namespace visualization {
147 namespace rendering {
148 
149 using namespace cloudViewer;
150 
151 FilamentScene::FilamentScene(filament::Engine& engine,
152  FilamentResourceManager& resource_mgr,
153  Renderer& renderer)
154  : Scene(renderer), engine_(engine), resource_mgr_(resource_mgr) {
155  scene_ = engine_.createScene();
156  CreateSunDirectionalLight();
157  // Note: can't set background color, because ImguiFilamentBridge
158  // creates a Scene, and it needs to not have anything drawing, or it
159  // covers up any SceneWidgets in the window.
160 }
161 
163  for (auto& le : lights_) {
164  engine_.destroy(le.second.filament_entity);
165  le.second.filament_entity.clear();
166  }
167  engine_.destroy(sun_.filament_entity);
168  sun_.filament_entity.clear();
169  if (ibl_handle_) {
170  resource_mgr_.Destroy(ibl_handle_);
171  }
172  if (skybox_handle_) {
173  resource_mgr_.Destroy(skybox_handle_);
174  }
175  engine_.destroy(scene_);
176 }
177 
179  auto copy = new FilamentScene(engine_, resource_mgr_, renderer_);
180  copy->geometries_ = this->geometries_;
181  copy->lights_ = this->lights_;
182  copy->model_geometries_ = this->model_geometries_;
183  copy->background_color_ = this->background_color_;
184  copy->background_image_ = this->background_image_;
185  copy->ibl_name_ = this->ibl_name_;
186  copy->ibl_enabled_ = this->ibl_enabled_;
187  copy->skybox_enabled_ = this->skybox_enabled_;
188  copy->indirect_light_ = this->indirect_light_;
189  copy->skybox_ = this->skybox_;
190  copy->sun_ = this->sun_;
191 
192  for (auto& name_geom : copy->geometries_) {
193  auto& name = name_geom.first;
194  auto& geom = name_geom.second;
195 
196  auto& renderable_mgr = copy->engine_.getRenderableManager();
197  auto inst = renderable_mgr.getInstance(geom.filament_entity);
198  auto box = renderable_mgr.getAxisAlignedBoundingBox(inst);
199  auto vbuf = copy->resource_mgr_.GetVertexBuffer(geom.vb).lock();
200  auto ibuf = copy->resource_mgr_.GetIndexBuffer(geom.ib).lock();
201  auto new_entity = utils::EntityManager::get().create();
202  filament::RenderableManager::Builder builder(1);
203  builder.boundingBox(box)
204  .layerMask(FilamentView::kAllLayersMask,
206  .castShadows(geom.cast_shadows)
207  .receiveShadows(geom.receive_shadows)
208  .culling(geom.culling_enabled)
209  .geometry(0, geom.primitive_type, vbuf.get(), ibuf.get());
210  if (geom.priority >= 0) {
211  builder.priority(uint8_t(geom.priority));
212  }
213  copy->resource_mgr_.ReuseVertexBuffer(geom.vb);
214 
215  auto material_instance = copy->AssignMaterialToFilamentGeometry(
216  builder, geom.mat.properties);
217 
218  auto result = builder.build(copy->engine_, new_entity);
219  if (result == filament::RenderableManager::Builder::Success) {
220  if (geom.visible) {
221  copy->scene_->addEntity(new_entity);
222  }
223  geom.filament_entity = new_entity;
224  geom.mat.mat_instance = material_instance;
225 
226  auto transform = this->GetGeometryTransform(name);
227  copy->SetGeometryTransform(name, transform);
228  copy->UpdateMaterialProperties(
229  copy->geometries_[name]); // for non-const
230 
231  } else {
233  "Failed to copy Filament resources for geometry {}", name);
234  }
235  }
236  return copy;
237 }
238 
240  std::int32_t y,
241  std::uint32_t w,
242  std::uint32_t h) {
243  auto handle = ViewHandle::Next();
244  auto view = std::make_unique<FilamentView>(engine_, *this, resource_mgr_);
245 
246  view->SetViewport(x, y, w, h);
247  if (!views_.empty()) {
248  view->SetDiscardBuffers(View::TargetBuffers::DepthAndStencil);
249  }
250 
251  ViewContainer c;
252  c.view = std::move(view);
253  views_.emplace(handle, std::move(c));
254 
255  return handle;
256 }
257 
258 View* FilamentScene::GetView(const ViewHandle& view_id) const {
259  auto found = views_.find(view_id);
260  if (found != views_.end()) {
261  return found->second.view.get();
262  }
263 
264  return nullptr;
265 }
266 
267 void FilamentScene::SetViewActive(const ViewHandle& view_id, bool is_active) {
268  auto found = views_.find(view_id);
269  if (found != views_.end()) {
270  found->second.is_active = is_active;
271  found->second.render_count = -1;
272  }
273 }
274 
276  auto found = views_.find(view_id);
277  if (found != views_.end()) {
278  found->second.is_active = true;
279  found->second.render_count = 1;
280  }
281 }
282 
283 void FilamentScene::RemoveView(const ViewHandle& view_id) {
284  views_.erase(view_id);
285 }
286 
287 void FilamentScene::AddCamera(const std::string& camera_name,
288  std::shared_ptr<Camera> cam) {}
289 
290 void FilamentScene::RemoveCamera(const std::string& camera_name) {}
291 
292 void FilamentScene::SetActiveCamera(const std::string& camera_name) {}
293 
294 MaterialInstanceHandle FilamentScene::AssignMaterialToFilamentGeometry(
295  filament::RenderableManager::Builder& builder,
296  const MaterialRecord& material) {
297  // TODO: put this in a method
298  auto shader = defaults_mapping::shader_mappings[material.shader];
299  if (!shader) shader = defaults_mapping::kColorOnlyMesh;
300 
301  auto material_instance = resource_mgr_.CreateMaterialInstance(shader);
302  auto wmat_instance = resource_mgr_.GetMaterialInstance(material_instance);
303  if (!wmat_instance.expired()) {
304  builder.material(0, wmat_instance.lock().get());
305  }
306  return material_instance;
307 }
308 
309 bool FilamentScene::AddGeometry(const std::string& object_name,
310  const ccHObject& geometry,
311  const MaterialRecord& material,
312  const std::string& downsampled_name /*= ""*/,
313  size_t downsample_threshold /*= SIZE_MAX*/) {
314  if (geometries_.count(object_name) > 0) {
316  "Geometry {} has already been added to scene graph.",
317  object_name);
318  return false;
319  }
320 
321  // Basic sanity checks
322  if (geometry.IsEmpty()) {
324  "Geometry for object {} is empty. Not adding geometry to scene",
325  object_name);
326  return false;
327  }
328 
329  auto tris = dynamic_cast<const ccMesh*>(&geometry);
330  if (tris && !tris->hasNormals() && !tris->hasTriNormals() &&
331  (material.shader == "defaultUnlit" ||
332  material.shader == "defaultLitTransparency")) {
334  "Using a shader with lighting but geometry has no normals.");
335  }
336 
337  // Build Filament buffers
338  auto buffer_builder = GeometryBuffersBuilder::GetBuilder(geometry);
339  if (!buffer_builder) {
340  utility::LogWarning("Geometry type {} is not supported yet!",
341  static_cast<size_t>(geometry.getClassID()));
342  return false;
343  }
344  if (!downsampled_name.empty()) {
345  buffer_builder->SetDownsampleThreshold(downsample_threshold);
346  }
347  buffer_builder->SetAdjustColorsForSRGBToneMapping(material.sRGB_color);
348  if (material.shader == "unlitLine") {
349  buffer_builder->SetWideLines();
350  }
351 
352  auto buffers = buffer_builder->ConstructBuffers();
353  auto vb = std::get<0>(buffers);
354  auto ib = std::get<1>(buffers);
355  auto ib_downsampled = std::get<2>(buffers);
356  filament::Box aabb = buffer_builder->ComputeAABB(); // expensive
357  bool success = CreateAndAddFilamentEntity(object_name, *buffer_builder,
358  aabb, vb, ib, material);
359  if (success && ib_downsampled) {
360  if (!CreateAndAddFilamentEntity(downsampled_name, *buffer_builder, aabb,
361  vb, ib_downsampled, material,
362  BufferReuse::kYes)) {
364  "Internal error: could not create downsampled point cloud");
365  }
366  }
367  return success;
368 }
369 
370 bool FilamentScene::AddGeometry(const std::string& object_name,
371  const t::geometry::Geometry& geometry,
372  const MaterialRecord& material,
373  const std::string& downsampled_name /*= ""*/,
374  size_t downsample_threshold /*= SIZE_MAX*/) {
375  // Basic sanity checks
376  if (geometry.IsEmpty()) {
377  utility::LogWarning("Geometry for object {} is empty", object_name);
378  return false;
379  }
380  auto buffer_builder = GeometryBuffersBuilder::GetBuilder(geometry);
381  if (!buffer_builder) {
383  "Unable to create GPU resources for object {}. Please check "
384  "console for further details.",
385  object_name);
386  return false;
387  }
388 
389  // Setup and build filament resources
390  if (!downsampled_name.empty()) {
391  buffer_builder->SetDownsampleThreshold(downsample_threshold);
392  }
393  buffer_builder->SetAdjustColorsForSRGBToneMapping(material.sRGB_color);
394  if (material.shader == "unlitLine") {
395  buffer_builder->SetWideLines();
396  }
397 
398  auto buffers = buffer_builder->ConstructBuffers();
399  auto vb = std::get<0>(buffers);
400  auto ib = std::get<1>(buffers);
401  auto ib_downsampled = std::get<2>(buffers);
402  filament::Box aabb = buffer_builder->ComputeAABB();
403  // NOTE: pointer not checked because if we get to this point then we know
404  // that the dynamic cast must succeed
405  auto* drawable_geom =
406  dynamic_cast<const t::geometry::DrawableGeometry*>(&geometry);
407  MaterialRecord internal_material = material;
408  if (drawable_geom->HasMaterial()) {
409  drawable_geom->GetMaterial().ToMaterialRecord(internal_material);
410  }
411  bool success = CreateAndAddFilamentEntity(object_name, *buffer_builder,
412  aabb, vb, ib, internal_material);
413  if (success && ib_downsampled) {
414  if (!CreateAndAddFilamentEntity(downsampled_name, *buffer_builder, aabb,
415  vb, ib_downsampled, internal_material,
416  BufferReuse::kYes)) {
417  // If we failed to create a downsampled cloud, which would be
418  // unlikely, create another entity with the original buffers
419  // (since that succeeded).
421  "Internal error: could not create downsampled point cloud");
422  CreateAndAddFilamentEntity(downsampled_name, *buffer_builder, aabb,
423  vb, ib, material, BufferReuse::kYes);
424  }
425  }
426  return success;
427 }
428 
429 #ifndef NDEBUG
432  utility::LogInfo("Material {}", mat.name);
433  utility::LogInfo("\tAlpha: {}", mat.has_alpha);
434  utility::LogInfo("\tBase Color: {},{},{},{}", mat.base_color.x(),
435  mat.base_color.y(), mat.base_color.z(),
436  mat.base_color.w());
437  utility::LogInfo("\tBase Metallic: {}", mat.base_metallic);
438  utility::LogInfo("\tBase Roughness: {}", mat.base_roughness);
439  utility::LogInfo("\tBase Reflectance: {}", mat.base_reflectance);
440  utility::LogInfo("\tBase Clear Cout: {}", mat.base_clearcoat);
441 }
442 #endif
443 
444 bool FilamentScene::AddGeometry(const std::string& object_name,
445  const TriangleMeshModel& model) {
446  if (geometries_.count(object_name) > 0 ||
447  model_geometries_.count(object_name) > 0) {
448  utility::LogWarning("Model {} has already been added to scene graph.",
449  object_name);
450  return false;
451  }
452 
453  std::vector<std::string> mesh_object_names;
454  std::unordered_multiset<std::string> check_duplicates;
455  for (const auto& mesh : model.meshes_) {
456  auto& mat = model.materials_[mesh.material_idx];
457  std::string derived_name(object_name + ":" + mesh.mesh_name);
458  check_duplicates.insert(derived_name);
459  if (check_duplicates.count(derived_name) > 1) {
460  derived_name +=
461  std::string("_") +
462  std::to_string(check_duplicates.count(derived_name));
463  }
464  AddGeometry(derived_name, *(mesh.mesh), mat);
465  mesh_object_names.push_back(derived_name);
466  }
467  model_geometries_[object_name] = mesh_object_names;
468 
469  return true;
470 }
471 
472 bool FilamentScene::CreateAndAddFilamentEntity(
473  const std::string& object_name,
474  GeometryBuffersBuilder& buffer_builder,
475  filament::Box& aabb,
478  const MaterialRecord& material,
479  BufferReuse reusing_vertex_buffer /*= kNo*/) {
480  auto vbuf = resource_mgr_.GetVertexBuffer(vb).lock();
481  auto ibuf = resource_mgr_.GetIndexBuffer(ib).lock();
482 
483  auto filament_entity = utils::EntityManager::get().create();
484  filament::RenderableManager::Builder builder(1);
485  builder.boundingBox(aabb)
487  .castShadows(true)
488  .receiveShadows(true)
489  .geometry(0, buffer_builder.GetPrimitiveType(), vbuf.get(),
490  ibuf.get());
491 
492  auto material_instance =
493  AssignMaterialToFilamentGeometry(builder, material);
494 
495  auto result = builder.build(engine_, filament_entity);
496  if (result == filament::RenderableManager::Builder::Success) {
497  scene_->addEntity(filament_entity);
498 
499  auto giter = geometries_.emplace(std::make_pair(
500  object_name,
501  RenderableGeometry{object_name,
502  true,
503  false,
504  true,
505  true,
506  true,
507  -1,
508  {{}, material, material_instance},
509  filament_entity,
510  buffer_builder.GetPrimitiveType(),
511  vb,
512  ib}));
513 
514  SetGeometryTransform(object_name, Transform::Identity());
515  UpdateMaterialProperties(giter.first->second);
516  } else {
517  // NOTE: Is there a better way to handle builder failing? That's a
518  // sign of a major problem.
520  "Failed to build Filament resources for geometry {}",
521  object_name);
522  return false;
523  }
524 
525  if (reusing_vertex_buffer == BufferReuse::kYes) {
526  resource_mgr_.ReuseVertexBuffer(vb);
527  }
528 
529  return true;
530 }
531 
532 bool FilamentScene::HasGeometry(const std::string& object_name) const {
533  if (GeometryIsModel(object_name)) {
534  return true;
535  }
536  auto geom_entry = geometries_.find(object_name);
537  return (geom_entry != geometries_.end());
538 }
539 
540 void FilamentScene::UpdateGeometry(const std::string& object_name,
541  const t::geometry::PointCloud& point_cloud,
542  uint32_t update_flags) {
543  auto geoms = GetGeometry(object_name, false);
544  if (!geoms.empty()) {
545  // Note: There should only be a single entry in geoms
546  auto* g = geoms[0];
547  auto vbuf_ptr = resource_mgr_.GetVertexBuffer(g->vb).lock();
548  auto vbuf = vbuf_ptr.get();
549 
550  const auto& points = point_cloud.GetPoints();
551  const size_t n_vertices = points.GetLength();
552 
553  // NOTE: number of points in the updated point cloud must be the
554  // same as the number of points when the vertex buffer was first
555  // created. If the number of points has changed then it cannot be
556  // updated. In that case, you must remove the geometry then add it
557  // again.
558  if (n_vertices > vbuf->getVertexCount()) {
560  "Geometry for point cloud {} cannot be updated because the "
561  "number of points exceeds the existing point count (Old: "
562  "{}, New: {})",
563  object_name, vbuf->getVertexCount(), n_vertices);
564  return;
565  }
566 
567  bool geometry_update_needed = n_vertices != vbuf->getVertexCount();
568  bool pcloud_is_gpu =
569  points.GetDevice().GetType() == core::Device::DeviceType::CUDA;
570  t::geometry::PointCloud cpu_pcloud;
571  if (pcloud_is_gpu) {
572  cpu_pcloud = point_cloud.CPU();
573  }
574 
575  // Update the each of the attribute requested
576  if (update_flags & kUpdatePointsFlag) {
577  const size_t vertex_array_size = n_vertices * 3 * sizeof(float);
578  if (pcloud_is_gpu) {
579  auto vertex_data =
580  static_cast<float*>(malloc(vertex_array_size));
581  memcpy(vertex_data, cpu_pcloud.GetPoints().GetDataPtr(),
582  vertex_array_size);
583  filament::VertexBuffer::BufferDescriptor pts_descriptor(
584  vertex_data, vertex_array_size, DeallocateBuffer);
585  vbuf->setBufferAt(engine_, 0, std::move(pts_descriptor));
586  } else {
587  filament::VertexBuffer::BufferDescriptor pts_descriptor(
588  points.GetDataPtr(), vertex_array_size);
589  vbuf->setBufferAt(engine_, 0, std::move(pts_descriptor));
590  }
591  }
592 
593  if (update_flags & kUpdateColorsFlag && point_cloud.HasPointColors()) {
594  const size_t color_array_size = n_vertices * 3 * sizeof(float);
595  if (pcloud_is_gpu) {
596  auto color_data = static_cast<float*>(malloc(color_array_size));
597  memcpy(color_data, cpu_pcloud.GetPoints().GetDataPtr(),
598  color_array_size);
599  filament::VertexBuffer::BufferDescriptor color_descriptor(
600  color_data, color_array_size, DeallocateBuffer);
601  vbuf->setBufferAt(engine_, 1, std::move(color_descriptor));
602  } else {
603  filament::VertexBuffer::BufferDescriptor color_descriptor(
604  point_cloud.GetPointColors().GetDataPtr(),
605  color_array_size);
606  vbuf->setBufferAt(engine_, 1, std::move(color_descriptor));
607  }
608  }
609 
610  if (update_flags & kUpdateNormalsFlag &&
611  point_cloud.HasPointNormals()) {
612  const size_t normal_array_size = n_vertices * 4 * sizeof(float);
613  const void* normal_data = nullptr;
614  if (pcloud_is_gpu) {
615  const auto& normals = point_cloud.GetPointNormals();
616  normal_data = normals.GetDataPtr();
617  } else {
618  normal_data = cpu_pcloud.GetPointNormals().GetDataPtr();
619  }
620 
621  // Converting normals to Filament type - quaternions
622  auto float4v_tangents = static_cast<filament::math::quatf*>(
623  malloc(normal_array_size));
624  auto orientation = filament::geometry::SurfaceOrientation::Builder()
625  .vertexCount(n_vertices)
626  .normals(reinterpret_cast<
627  const filament::math::float3*>(
628  normal_data))
629  .build();
630  orientation->getQuats(float4v_tangents, n_vertices);
631  filament::VertexBuffer::BufferDescriptor normals_descriptor(
632  float4v_tangents, normal_array_size, DeallocateBuffer);
633  vbuf->setBufferAt(engine_, 2, std::move(normals_descriptor));
634  delete orientation;
635  }
636 
637  if (update_flags & kUpdateUv0Flag) {
638  const size_t uv_array_size = n_vertices * 2 * sizeof(float);
639  if (point_cloud.HasPointAttr("uv")) {
640  filament::VertexBuffer::BufferDescriptor uv_descriptor(
641  point_cloud.GetPointAttr("uv").GetDataPtr(),
642  uv_array_size);
643  vbuf->setBufferAt(engine_, 3, std::move(uv_descriptor));
644  } else if (point_cloud.HasPointAttr("__visualization_scalar")) {
645  // Update in PointCloudBuffers.cpp, too:
646  // TPointCloudBuffersBuilder::ConstructBuffers
647  float* uv_array = static_cast<float*>(malloc(uv_array_size));
648  memset(uv_array, 0, uv_array_size);
649  const float* src = static_cast<const float*>(
650  point_cloud.GetPointAttr("__visualization_scalar")
651  .GetDataPtr());
652  const size_t n = 2 * n_vertices;
653  for (size_t i = 0; i < n; i += 2) {
654  uv_array[i] = *src++;
655  }
656  filament::VertexBuffer::BufferDescriptor uv_descriptor(
657  uv_array, uv_array_size, DeallocateBuffer);
658  vbuf->setBufferAt(engine_, 3, std::move(uv_descriptor));
659  }
660  }
661 
662  // Update the geometry to reflect new geometry count
663  if (geometry_update_needed) {
664  auto& renderable_mgr = engine_.getRenderableManager();
665  auto inst = renderable_mgr.getInstance(g->filament_entity);
666  renderable_mgr.setGeometryAt(
667  inst, 0, filament::RenderableManager::PrimitiveType::POINTS,
668  0, n_vertices);
669  }
670  }
671 }
672 
673 void FilamentScene::RemoveGeometry(const std::string& object_name) {
674  auto geoms = GetGeometry(object_name, false);
675  if (!geoms.empty()) {
676  for (auto* g : geoms) {
677  scene_->remove(g->filament_entity);
678  g->ReleaseResources(engine_, resource_mgr_);
679  geometries_.erase(g->name);
680  }
681  }
682 
683  if (GeometryIsModel(object_name)) {
684  model_geometries_.erase(object_name);
685  }
686 }
687 
688 void FilamentScene::ShowGeometry(const std::string& object_name, bool show) {
689  auto geoms = GetGeometry(object_name);
690  for (auto* g : geoms) {
691  if (g->visible != show) {
692  g->visible = show;
693  if (show) {
694  scene_->addEntity(g->filament_entity);
695  } else {
696  scene_->remove(g->filament_entity);
697  }
698  }
699  }
700 }
701 
702 bool FilamentScene::GeometryIsVisible(const std::string& object_name) {
703  auto geoms = GetGeometry(object_name);
704  if (!geoms.empty()) {
705  // NOTE: all meshes of model share same visibility so we only need to
706  // check first entry of this array
707  return geoms[0]->visible;
708  } else {
709  return false;
710  }
711 }
712 
713 utils::EntityInstance<filament::TransformManager>
714 FilamentScene::GetGeometryTransformInstance(RenderableGeometry* geom) {
715  filament::TransformManager::Instance itransform;
716  auto& transform_mgr = engine_.getTransformManager();
717  itransform = transform_mgr.getInstance(geom->filament_entity);
718  if (!itransform.isValid()) {
719  using namespace filament::math;
720  transform_mgr.create(geom->filament_entity);
721  itransform = transform_mgr.getInstance(geom->filament_entity);
722  transform_mgr.create(
723  geom->filament_entity, itransform,
724  mat4f::translation(filament::math::float3{0.0f, 0.0f, 0.0f}));
725  }
726  return itransform;
727 }
728 
729 void FilamentScene::SetGeometryTransform(const std::string& object_name,
730  const Transform& transform) {
731  auto geoms = GetGeometry(object_name);
732  for (auto* g : geoms) {
733  auto itransform = GetGeometryTransformInstance(g);
734  if (itransform.isValid()) {
735  const auto& ematrix = transform.matrix();
736  auto& transform_mgr = engine_.getTransformManager();
737  transform_mgr.setTransform(
738  itransform,
739  converters::FilamentMatrixFromEigenMatrix(ematrix));
740  }
741  }
742 }
743 
745  const std::string& object_name) {
746  Transform etransform;
747  auto geoms = GetGeometry(object_name);
748  if (!geoms.empty()) {
749  auto itransform = GetGeometryTransformInstance(geoms[0]);
750  if (itransform.isValid()) {
751  auto& transform_mgr = engine_.getTransformManager();
752  auto ftransform = transform_mgr.getTransform(itransform);
753  etransform = converters::EigenMatrixFromFilamentMatrix(ftransform);
754  }
755  }
756  return etransform;
757 }
758 
759 ccBBox FilamentScene::GetGeometryBoundingBox(const std::string& object_name) {
760  ccBBox result;
761  auto geoms = GetGeometry(object_name);
762  for (auto* g : geoms) {
763  auto& renderable_mgr = engine_.getRenderableManager();
764  auto inst = renderable_mgr.getInstance(g->filament_entity);
765  auto box = renderable_mgr.getAxisAlignedBoundingBox(inst);
766 
767  auto& transform_mgr = engine_.getTransformManager();
768  auto itransform = transform_mgr.getInstance(g->filament_entity);
769  auto transform = transform_mgr.getWorldTransform(itransform);
770 
771  box = rigidTransform(box, transform);
772 
773  auto min = box.center - box.halfExtent;
774  auto max = box.center + box.halfExtent;
775  result += ccBBox(CCVector3(min.x, min.y, min.z),
776  CCVector3(max.x, max.y, max.z));
777  }
778  return result;
779 }
780 
781 void FilamentScene::GeometryShadows(const std::string& object_name,
782  bool cast_shadows,
783  bool receive_shadows) {
784  auto geoms = GetGeometry(object_name);
785  for (auto* g : geoms) {
786  auto& renderable_mgr = engine_.getRenderableManager();
787  filament::RenderableManager::Instance inst =
788  renderable_mgr.getInstance(g->filament_entity);
789  renderable_mgr.setCastShadows(inst, cast_shadows);
790  renderable_mgr.setReceiveShadows(inst, receive_shadows);
791  }
792 }
793 
794 void FilamentScene::SetGeometryCulling(const std::string& object_name,
795  bool enable) {
796  auto geoms = GetGeometry(object_name);
797  for (auto* g : geoms) {
798  auto& renderable_mgr = engine_.getRenderableManager();
799  filament::RenderableManager::Instance inst =
800  renderable_mgr.getInstance(g->filament_entity);
801  renderable_mgr.setCulling(inst, enable);
802  g->culling_enabled = enable;
803  }
804 }
805 
806 void FilamentScene::SetGeometryPriority(const std::string& object_name,
807  uint8_t priority) {
808  auto geoms = GetGeometry(object_name);
809  for (auto* g : geoms) {
810  auto& renderable_mgr = engine_.getRenderableManager();
811  filament::RenderableManager::Instance inst =
812  renderable_mgr.getInstance(g->filament_entity);
813  renderable_mgr.setPriority(inst, priority);
814  g->priority = (int)priority;
815  }
816 }
817 
818 void FilamentScene::UpdateDefaultLit(GeometryMaterialInstance& geom_mi) {
819  auto& material = geom_mi.properties;
820  auto& maps = geom_mi.maps;
821 
822  renderer_.ModifyMaterial(geom_mi.mat_instance)
823  .SetColor("baseColor", material.base_color, false)
824  .SetParameter("pointSize", material.point_size)
825  .SetParameter("baseRoughness", material.base_roughness)
826  .SetParameter("baseMetallic", material.base_metallic)
827  .SetParameter("reflectance", material.base_reflectance)
828  .SetParameter("clearCoat", material.base_clearcoat)
829  .SetParameter("clearCoatRoughness",
830  material.base_clearcoat_roughness)
831  .SetParameter("anisotropy", material.base_anisotropy)
832  .SetTexture("albedo", maps.albedo_map,
834  .SetTexture("normalMap", maps.normal_map,
836  .SetTexture("ao_rough_metalMap", maps.ao_rough_metal_map,
838  .SetTexture("reflectanceMap", maps.reflectance_map,
840  // NOTE: Disabled temporarily to avoid Filament warning until
841  // defaultLit is reworked to use fewer samplers
842  // .SetTexture("clearCoatMap", maps.clear_coat_map,
843  // rendering::TextureSamplerParameters::Pretty())
844  // .SetTexture("clearCoatRoughnessMap",
845  // maps.clear_coat_roughness_map,
846  // rendering::TextureSamplerParameters::Pretty())
847  // .SetTexture("anisotropyMap", maps.anisotropy_map,
848  // rendering::TextureSamplerParameters::Pretty())
849  .Finish();
850 }
851 
852 void FilamentScene::UpdateGaussianSplat(GeometryMaterialInstance& geom_mi) {
853  auto& material = geom_mi.properties;
854  auto& maps = geom_mi.maps;
855 
856  renderer_.ModifyMaterial(geom_mi.mat_instance)
857  .SetColor("baseColor", material.base_color, false)
858  .SetParameter("pointSize", material.point_size)
859  .SetParameter("baseRoughness", material.base_roughness)
860  .SetParameter("baseMetallic", material.base_metallic)
861  .SetParameter("reflectance", material.base_reflectance)
862  .SetParameter("clearCoat", material.base_clearcoat)
863  .SetParameter("clearCoatRoughness",
864  material.base_clearcoat_roughness)
865  .SetParameter("anisotropy", material.base_anisotropy)
866  .SetParameter("shDegree", material.sh_degree)
867  .SetTexture("albedo", maps.albedo_map,
869  .SetTexture("normalMap", maps.normal_map,
871  .SetTexture("ao_rough_metalMap", maps.ao_rough_metal_map,
873  .SetTexture("reflectanceMap", maps.reflectance_map,
875  .Finish();
876 }
877 
878 void FilamentScene::UpdateDefaultLitSSR(GeometryMaterialInstance& geom_mi) {
879  auto& material = geom_mi.properties;
880  auto& maps = geom_mi.maps;
881 
882  auto absorption_float3 = filament::Color::absorptionAtDistance(
883  filament::math::float3(material.absorption_color.x(),
884  material.absorption_color.y(),
885  material.absorption_color.z()),
886  material.absorption_distance);
887  Eigen::Vector3f absorption(absorption_float3.x, absorption_float3.y,
888  absorption_float3.z);
889 
890  renderer_.ModifyMaterial(geom_mi.mat_instance)
891  .SetColor("baseColor", material.base_color, false)
892  .SetParameter("pointSize", material.point_size)
893  .SetParameter("baseRoughness", material.base_roughness)
894  .SetParameter("baseMetallic", material.base_metallic)
895  .SetParameter("reflectance", material.base_reflectance)
896  .SetParameter("clearCoat", material.base_clearcoat)
897  .SetParameter("clearCoatRoughness",
898  material.base_clearcoat_roughness)
899  .SetParameter("anisotropy", material.base_anisotropy)
900  .SetParameter("thickness", material.thickness)
901  .SetParameter("transmission", material.transmission)
902  .SetParameter("absorption", absorption)
903  .SetTexture("albedo", maps.albedo_map,
905  .SetTexture("normalMap", maps.normal_map,
907  .SetTexture("ao_rough_metalMap", maps.ao_rough_metal_map,
909  .SetTexture("reflectanceMap", maps.reflectance_map,
911  .Finish();
912 }
913 
914 void FilamentScene::UpdateDefaultUnlit(GeometryMaterialInstance& geom_mi) {
915  float srgb = (geom_mi.properties.sRGB_vertex_color ? 1.f : 0.f);
916 
917  renderer_.ModifyMaterial(geom_mi.mat_instance)
918  .SetColor("baseColor", geom_mi.properties.base_color, true)
919  .SetParameter("pointSize", geom_mi.properties.point_size)
920  .SetParameter("srgbColor", srgb)
921  .SetTexture("albedo", geom_mi.maps.albedo_map,
923  .Finish();
924 }
925 
926 void FilamentScene::UpdateNormalShader(GeometryMaterialInstance& geom_mi) {
927  renderer_.ModifyMaterial(geom_mi.mat_instance)
928  .SetParameter("pointSize", geom_mi.properties.point_size)
929  .Finish();
930 }
931 
932 void FilamentScene::UpdateDepthShader(GeometryMaterialInstance& geom_mi) {
933  auto* camera = views_.begin()->second.view->GetCamera();
934  const float f = float(camera->GetFar());
935  const float n = float(camera->GetNear());
936  renderer_.ModifyMaterial(geom_mi.mat_instance)
937  .SetParameter("pointSize", geom_mi.properties.point_size)
938  .SetParameter("cameraNear", n)
939  .SetParameter("cameraFar", f)
940  .Finish();
941 }
942 
943 void FilamentScene::UpdateDepthValueShader(GeometryMaterialInstance& geom_mi) {
944  renderer_.ModifyMaterial(geom_mi.mat_instance)
945  .SetParameter("pointSize", geom_mi.properties.point_size)
946  .Finish();
947 }
948 
949 void FilamentScene::UpdateGradientShader(GeometryMaterialInstance& geom_mi) {
950  bool isLUT =
951  (geom_mi.properties.gradient->GetMode() == Gradient::Mode::kLUT);
952  renderer_.ModifyMaterial(geom_mi.mat_instance)
953  .SetParameter("minValue", geom_mi.properties.scalar_min)
954  .SetParameter("maxValue", geom_mi.properties.scalar_max)
955  .SetParameter("isLUT", (isLUT ? 1.0f : 0.0f))
956  .SetParameter("pointSize", geom_mi.properties.point_size)
957  .SetTexture(
958  "gradient", geom_mi.maps.gradient_texture,
961  .Finish();
962 }
963 
964 void FilamentScene::UpdateSolidColorShader(GeometryMaterialInstance& geom_mi) {
965  renderer_.ModifyMaterial(geom_mi.mat_instance)
966  .SetColor("baseColor", geom_mi.properties.base_color, true)
967  .SetParameter("pointSize", geom_mi.properties.point_size)
968  .Finish();
969 }
970 
971 void FilamentScene::UpdateBackgroundShader(GeometryMaterialInstance& geom_mi) {
972  renderer_.ModifyMaterial(geom_mi.mat_instance)
973  .SetColor("baseColor", geom_mi.properties.base_color, true)
974  .SetParameter("aspectRatio", geom_mi.properties.aspect_ratio)
975  .SetParameter("yOrigin", 1.0f)
976  .SetTexture("albedo", geom_mi.maps.albedo_map,
978  .Finish();
979 }
980 
981 void FilamentScene::UpdateGroundPlaneShader(GeometryMaterialInstance& geom_mi) {
982  renderer_.ModifyMaterial(geom_mi.mat_instance)
983  .SetColor("baseColor", geom_mi.properties.base_color, true)
984  .SetParameter("axis", geom_mi.properties.ground_plane_axis)
985  .Finish();
986 }
987 
988 void FilamentScene::UpdateLineShader(GeometryMaterialInstance& geom_mi) {
989  renderer_.ModifyMaterial(geom_mi.mat_instance)
990  .SetColor("baseColor", geom_mi.properties.base_color, true)
991  .SetColor("emissiveColor", geom_mi.properties.emissive_color, false)
992  .SetParameter("lineWidth", geom_mi.properties.line_width)
993  .Finish();
994 }
995 
996 void FilamentScene::UpdateUnlitPolygonOffsetShader(
997  GeometryMaterialInstance& geom_mi) {
998  renderer_.ModifyMaterial(geom_mi.mat_instance)
999  .SetParameter("pointSize", geom_mi.properties.point_size)
1000  .Finish();
1001 }
1002 
1003 std::shared_ptr<geometry::Image> CombineTextures(
1004  std::shared_ptr<geometry::Image> ao,
1005  std::shared_ptr<geometry::Image> rough,
1006  std::shared_ptr<geometry::Image> metal) {
1007  int width = 0, height = 0;
1008  if (ao && ao->HasData()) {
1009  width = ao->width_;
1010  height = ao->height_;
1011  }
1012  if (rough && rough->HasData()) {
1013  if (width == 0) {
1014  width = rough->width_;
1015  height = rough->height_;
1016  } else if (width != rough->width_ || height != rough->height_) {
1018  "Attribute texture maps must have same dimensions");
1019  return {};
1020  }
1021  }
1022  if (metal && metal->HasData()) {
1023  if (width == 0) {
1024  width = metal->width_;
1025  height = metal->height_;
1026  } else if (width != metal->width_ || height != metal->height_) {
1028  "Attribute texture maps must have same dimensions");
1029  return {};
1030  }
1031  }
1032 
1033  // no maps are valid so return empty texture and let caller use defaults
1034  if (width == 0 || height == 0) {
1035  return {};
1036  }
1037 
1038  auto image = std::make_shared<geometry::Image>();
1039  image->Prepare(width, height, 3, 1);
1040  auto data = reinterpret_cast<uint8_t*>(image->data_.data());
1041 
1042  auto set_pixel = [&data](std::shared_ptr<geometry::Image> map, int i,
1043  int j) {
1044  if (map && map->HasData()) {
1045  *data++ = *(map->PointerAt<uint8_t>(j, i, 0));
1046  } else {
1047  *data++ = 255;
1048  }
1049  };
1050 
1051  for (int i = 0; i < width; ++i) {
1052  for (int j = 0; j < height; ++j) {
1053  set_pixel(ao, i, j);
1054  set_pixel(rough, i, j);
1055  set_pixel(metal, i, j);
1056  }
1057  }
1058 
1059  return image;
1060 }
1061 
1062 void CombineTextures(std::shared_ptr<geometry::Image> ao,
1063  std::shared_ptr<geometry::Image> rough_metal) {
1064  int width = rough_metal->width_;
1065  int height = rough_metal->height_;
1066 
1067  if (ao && ao->HasData()) {
1068  if (width != ao->width_ || height != ao->height_) {
1070  "Attribute texture maps must have same dimensions");
1071  return;
1072  }
1073  }
1074 
1075  auto data = reinterpret_cast<uint8_t*>(rough_metal->data_.data());
1076 
1077  auto stride = rough_metal->num_of_channels_;
1078  for (int i = 0; i < width; ++i) {
1079  for (int j = 0; j < height; ++j) {
1080  if (ao && ao->HasData()) {
1081  *data = *(ao->PointerAt<uint8_t>(j, i, 0));
1082  } else {
1083  *data = 255;
1084  }
1085  data += stride;
1086  }
1087  }
1088 }
1089 
1090 void FilamentScene::UpdateMaterialProperties(RenderableGeometry& geom) {
1091  auto& props = geom.mat.properties;
1092  auto& maps = geom.mat.maps;
1093 
1094  // Load textures
1095  auto is_map_valid = [](std::shared_ptr<geometry::Image> map) -> bool {
1096  return map && map->HasData();
1097  };
1098  if (is_map_valid(props.albedo_img)) {
1099  maps.albedo_map = renderer_.AddTexture(props.albedo_img, true);
1100  }
1101  if (is_map_valid(props.normal_img)) {
1102  maps.normal_map = renderer_.AddTexture(props.normal_img);
1103  }
1104  if (is_map_valid(props.reflectance_img)) {
1105  maps.reflectance_map = renderer_.AddTexture(props.reflectance_img);
1106  }
1107  if (is_map_valid(props.clearcoat_img)) {
1108  maps.clear_coat_map = renderer_.AddTexture(props.clearcoat_img);
1109  }
1110  if (is_map_valid(props.clearcoat_roughness_img)) {
1111  maps.clear_coat_roughness_map =
1112  renderer_.AddTexture(props.clearcoat_roughness_img);
1113  }
1114  if (is_map_valid(props.anisotropy_img)) {
1115  maps.anisotropy_map = renderer_.AddTexture(props.anisotropy_img);
1116  }
1117  if (props.shader == "unlitGradient") {
1118  maps.gradient_texture = props.gradient->GetTextureHandle(renderer_);
1119  }
1120 
1121  // Create combined ao/rough/metal texture
1122  if (is_map_valid(props.ao_rough_metal_img)) {
1123  CombineTextures(props.ao_img, props.ao_rough_metal_img);
1124  maps.ao_rough_metal_map =
1125  renderer_.AddTexture(props.ao_rough_metal_img);
1126  } else if (is_map_valid(props.ao_img) ||
1127  is_map_valid(props.roughness_img) ||
1128  is_map_valid(props.metallic_img)) {
1129  props.ao_rough_metal_img = CombineTextures(
1130  props.ao_img, props.roughness_img, props.metallic_img);
1131  maps.ao_rough_metal_map =
1132  renderer_.AddTexture(props.ao_rough_metal_img);
1133  }
1134 
1135  // Update shader properties
1136  // TODO: Use a functional interface to get appropriate update methods
1137  if (props.shader == "defaultLit" ||
1138  props.shader == "defaultLitTransparency") {
1139  UpdateDefaultLit(geom.mat);
1140  } else if (props.shader == "defaultLitSSR") {
1141  UpdateDefaultLitSSR(geom.mat);
1142  } else if (props.shader == "defaultUnlit" ||
1143  props.shader == "defaultUnlitTransparency") {
1144  UpdateDefaultUnlit(geom.mat);
1145  } else if (props.shader == "normals") {
1146  UpdateNormalShader(geom.mat);
1147  } else if (props.shader == "depth") {
1148  UpdateDepthShader(geom.mat);
1149  } else if (props.shader == "depthValue") {
1150  UpdateDepthValueShader(geom.mat);
1151  } else if (props.shader == "unlitGradient") {
1152  UpdateGradientShader(geom.mat);
1153  } else if (props.shader == "unlitSolidColor") {
1154  UpdateSolidColorShader(geom.mat);
1155  } else if (props.shader == "unlitBackground") {
1156  UpdateBackgroundShader(geom.mat);
1157  } else if (props.shader == "infiniteGroundPlane") {
1158  UpdateGroundPlaneShader(geom.mat);
1159  } else if (props.shader == "unlitLine") {
1160  UpdateLineShader(geom.mat);
1161  } else if (props.shader == "unlitPolygonOffset") {
1162  UpdateUnlitPolygonOffsetShader(geom.mat);
1163  } else if (props.shader == "gaussianSplat") {
1164  UpdateGaussianSplat(geom.mat);
1165  } else {
1166  utility::LogWarning("'{}' is not a valid shader", props.shader);
1167  }
1168 }
1169 
1170 void FilamentScene::OverrideMaterialInternal(RenderableGeometry* geom,
1171  const MaterialRecord& material,
1172  bool shader_only) {
1173  // Has the shader changed?
1174  if (geom->mat.properties.shader != material.shader) {
1175  // TODO: put this in a method
1176  auto shader = defaults_mapping::shader_mappings[material.shader];
1177  if (!shader) shader = defaults_mapping::kColorOnlyMesh;
1178  auto old_mi = geom->mat.mat_instance;
1179  auto new_mi = resource_mgr_.CreateMaterialInstance(shader);
1180  auto wmat_instance = resource_mgr_.GetMaterialInstance(new_mi);
1181  if (!wmat_instance.expired()) {
1182  auto& renderable_mgr = engine_.getRenderableManager();
1183  filament::RenderableManager::Instance inst =
1184  renderable_mgr.getInstance(geom->filament_entity);
1185  renderable_mgr.setMaterialInstanceAt(inst, 0,
1186  wmat_instance.lock().get());
1187  }
1188  geom->mat.mat_instance = new_mi;
1189  resource_mgr_.Destroy(old_mi);
1190  }
1191  geom->mat.properties = material;
1192  if (shader_only) {
1193  if (material.shader == "defaultLit" ||
1194  material.shader == "defaultLitTransparency") {
1195  UpdateDefaultLit(geom->mat);
1196  } else if (material.shader == "defaultLitSSR") {
1197  UpdateDefaultLitSSR(geom->mat);
1198  } else if (material.shader == "defaultUnlit" ||
1199  material.shader == "defaultUnlitTransparency") {
1200  UpdateDefaultUnlit(geom->mat);
1201  } else if (material.shader == "normals") {
1202  UpdateNormalShader(geom->mat);
1203  } else if (material.shader == "unlitGradient") {
1204  UpdateGradientShader(geom->mat);
1205  } else if (material.shader == "unlitColorMap") {
1206  UpdateGradientShader(geom->mat);
1207  } else if (material.shader == "unlitSolidColor") {
1208  UpdateSolidColorShader(geom->mat);
1209  } else if (material.shader == "unlitBackground") {
1210  UpdateBackgroundShader(geom->mat);
1211  } else if (material.shader == "infiniteGroundPlane") {
1212  UpdateGroundPlaneShader(geom->mat);
1213  } else if (material.shader == "unlitLine") {
1214  UpdateLineShader(geom->mat);
1215  } else if (material.shader == "unlitPolygonOffset") {
1216  UpdateUnlitPolygonOffsetShader(geom->mat);
1217  } else if (material.shader == "depthValue") {
1218  UpdateDepthValueShader(geom->mat);
1219  } else {
1220  UpdateDepthShader(geom->mat);
1221  }
1222  } else {
1223  UpdateMaterialProperties(*geom);
1224  }
1225 }
1226 
1227 void FilamentScene::OverrideMaterial(const std::string& object_name,
1228  const MaterialRecord& material) {
1229  auto geoms = GetGeometry(object_name);
1230  for (auto* g : geoms) {
1231  OverrideMaterialInternal(g, material);
1232  }
1233 }
1234 
1235 void FilamentScene::QueryGeometry(std::vector<std::string>& geometry) {
1236  for (const auto& ge : geometries_) {
1237  geometry.push_back(ge.first);
1238  }
1239 }
1240 
1242  bool shader_only) {
1243  for (auto& ge : geometries_) {
1244  if (ge.first == kBackgroundName) {
1245  continue;
1246  }
1247  OverrideMaterialInternal(&ge.second, material, shader_only);
1248  }
1249 }
1250 
1251 bool FilamentScene::AddPointLight(const std::string& light_name,
1252  const Eigen::Vector3f& color,
1253  const Eigen::Vector3f& position,
1254  float intensity,
1255  float falloff,
1256  bool cast_shadows) {
1257  if (lights_.count(light_name) > 0) {
1259  "Cannot add point light because {} has already been added",
1260  light_name);
1261  return false;
1262  }
1263 
1264  filament::LightManager::Type light_type =
1265  filament::LightManager::Type::POINT;
1266  auto light = utils::EntityManager::get().create();
1267  auto result = filament::LightManager::Builder(light_type)
1268  .position({position.x(), position.y(), position.z()})
1269  .intensity(intensity)
1270  .falloff(falloff)
1271  .castShadows(cast_shadows)
1272  .color({color.x(), color.y(), color.z()})
1273  .build(engine_, light);
1274 
1275  if (result == filament::LightManager::Builder::Success) {
1276  lights_.emplace(std::make_pair(light_name, LightEntity{true, light}));
1277  scene_->addEntity(light);
1278  } else {
1279  utility::LogWarning("Failed to build Filament light resources for {}",
1280  light_name);
1281  return false;
1282  }
1283 
1284  return true;
1285 }
1286 
1287 bool FilamentScene::AddSpotLight(const std::string& light_name,
1288  const Eigen::Vector3f& color,
1289  const Eigen::Vector3f& position,
1290  const Eigen::Vector3f& direction,
1291  float intensity,
1292  float falloff,
1293  float inner_cone_angle,
1294  float outer_cone_angle,
1295  bool cast_shadows) {
1296  if (lights_.count(light_name) > 0) {
1298  "Cannot add point light because {} has already been added",
1299  light_name);
1300  return false;
1301  }
1302 
1303  filament::LightManager::Type light_type =
1304  filament::LightManager::Type::SPOT;
1305  auto light = utils::EntityManager::get().create();
1306  auto result =
1307  filament::LightManager::Builder(light_type)
1308  .direction({direction.x(), direction.y(), direction.z()})
1309  .position({position.x(), position.y(), position.z()})
1310  .intensity(intensity)
1311  .falloff(falloff)
1312  .castShadows(cast_shadows)
1313  .color({color.x(), color.y(), color.z()})
1314  .spotLightCone(inner_cone_angle, outer_cone_angle)
1315  .build(engine_, light);
1316 
1317  if (result == filament::LightManager::Builder::Success) {
1318  lights_.emplace(std::make_pair(light_name, LightEntity{true, light}));
1319  scene_->addEntity(light);
1320  } else {
1321  utility::LogWarning("Failed to build Filament light resources for {}",
1322  light_name);
1323  return false;
1324  }
1325 
1326  return true;
1327 }
1328 
1329 bool FilamentScene::AddDirectionalLight(const std::string& light_name,
1330  const Eigen::Vector3f& color,
1331  const Eigen::Vector3f& direction,
1332  float intensity,
1333  bool cast_shadows) {
1334  if (lights_.count(light_name) > 0) {
1336  "Cannot add point light because {} has already been added",
1337  light_name);
1338  return false;
1339  }
1340 
1341  filament::LightManager::Type light_type =
1342  filament::LightManager::Type::DIRECTIONAL;
1343  auto light = utils::EntityManager::get().create();
1344  auto result =
1345  filament::LightManager::Builder(light_type)
1346  .castShadows(cast_shadows)
1347  .direction({direction.x(), direction.y(), direction.z()})
1348  .color({color.x(), color.y(), color.z()})
1349  .intensity(intensity)
1350  .build(engine_, light);
1351 
1352  if (result == filament::LightManager::Builder::Success) {
1353  lights_.emplace(std::make_pair(light_name, LightEntity{true, light}));
1354  scene_->addEntity(light);
1355  } else {
1356  utility::LogWarning("Failed to build Filament light resources for {}",
1357  light_name);
1358  return false;
1359  }
1360 
1361  return true;
1362 }
1363 
1364 Light& FilamentScene::GetLight(const std::string& light_name) {
1365  // TODO: Not yet implemented. I still don't see any advantage to doing this
1366  static Light blah;
1367  return blah;
1368 }
1369 
1370 void FilamentScene::UpdateLight(const std::string& light_name,
1371  const Light& light) {
1372  // TODO: Not yet implemented. I still don't see any advantage to doing this
1373 }
1374 
1375 void FilamentScene::RemoveLight(const std::string& light_name) {
1376  auto light = GetLightInternal(light_name);
1377  if (light) {
1378  scene_->remove(light->filament_entity);
1379  engine_.destroy(light->filament_entity);
1380  lights_.erase(light_name);
1381  }
1382 }
1383 
1384 void FilamentScene::UpdateLightColor(const std::string& light_name,
1385  const Eigen::Vector3f& color) {
1386  auto light = GetLightInternal(light_name);
1387  if (light) {
1388  auto& light_mgr = engine_.getLightManager();
1389  filament::LightManager::Instance inst =
1390  light_mgr.getInstance(light->filament_entity);
1391  light_mgr.setColor(inst, {color(0), color(1), color(2)});
1392  }
1393 }
1394 
1395 void FilamentScene::UpdateLightPosition(const std::string& light_name,
1396  const Eigen::Vector3f& position) {
1397  auto light = GetLightInternal(light_name);
1398  if (light) {
1399  auto& light_mgr = engine_.getLightManager();
1400  filament::LightManager::Instance inst =
1401  light_mgr.getInstance(light->filament_entity);
1402  if (!light_mgr.isDirectional(inst)) {
1403  light_mgr.setPosition(inst,
1404  {position.x(), position.y(), position.z()});
1405  }
1406  }
1407 }
1408 
1409 void FilamentScene::UpdateLightDirection(const std::string& light_name,
1410  const Eigen::Vector3f& direction) {
1411  auto light = GetLightInternal(light_name);
1412  if (light) {
1413  auto& light_mgr = engine_.getLightManager();
1414  filament::LightManager::Instance inst =
1415  light_mgr.getInstance(light->filament_entity);
1416  light_mgr.setDirection(inst,
1417  {direction.x(), direction.y(), direction.z()});
1418  }
1419 }
1420 
1421 void FilamentScene::UpdateLightIntensity(const std::string& light_name,
1422  float intensity) {
1423  auto light = GetLightInternal(light_name);
1424  if (light) {
1425  auto& light_mgr = engine_.getLightManager();
1426  filament::LightManager::Instance inst =
1427  light_mgr.getInstance(light->filament_entity);
1428  light_mgr.setIntensity(inst, intensity);
1429  }
1430 }
1431 
1432 void FilamentScene::UpdateLightFalloff(const std::string& light_name,
1433  float falloff) {
1434  auto light = GetLightInternal(light_name);
1435  if (light) {
1436  auto& light_mgr = engine_.getLightManager();
1437  filament::LightManager::Instance inst =
1438  light_mgr.getInstance(light->filament_entity);
1439  light_mgr.setFalloff(inst, falloff);
1440  }
1441 }
1442 
1443 void FilamentScene::UpdateLightConeAngles(const std::string& light_name,
1444  float inner_cone_angle,
1445  float outer_cone_angle) {
1446  // TODO: Research. Not previously implemented
1447 }
1448 
1449 void FilamentScene::EnableLightShadow(const std::string& light_name,
1450  bool cast_shadows) {
1451  auto light = GetLightInternal(light_name);
1452  if (light) {
1453  auto& light_mgr = engine_.getLightManager();
1454  filament::LightManager::Instance inst =
1455  light_mgr.getInstance(light->filament_entity);
1456  light_mgr.setShadowCaster(inst, cast_shadows);
1457  }
1458 }
1459 
1460 void FilamentScene::CreateSunDirectionalLight() {
1461  filament::LightManager::Type light_type = filament::LightManager::Type::SUN;
1462  auto light = utils::EntityManager::get().create();
1463  auto result = filament::LightManager::Builder(light_type)
1464  .direction({0.f, 0.f, 1.f})
1465  .intensity(10000.0f)
1466  .falloff(10.0)
1467  .castShadows(true)
1468  .color({1.f, 1.f, 1.f})
1469  .build(engine_, light);
1470 
1471  if (result == filament::LightManager::Builder::Success) {
1472  sun_.filament_entity = light;
1473  scene_->addEntity(sun_.filament_entity);
1474  } else {
1476  "Failed to build Filament light resources for sun light");
1477  }
1478 }
1479 
1480 void FilamentScene::SetSunLight(const Eigen::Vector3f& direction,
1481  const Eigen::Vector3f& color,
1482  float intensity) {
1483  auto& light_mgr = engine_.getLightManager();
1484  filament::LightManager::Instance inst =
1485  light_mgr.getInstance(sun_.filament_entity);
1486  light_mgr.setDirection(inst, {direction.x(), direction.y(), direction.z()});
1487  light_mgr.setColor(inst, {color.x(), color.y(), color.z()});
1488  light_mgr.setIntensity(inst, intensity);
1489 }
1490 
1492  if (sun_.enabled != enable) {
1493  sun_.enabled = enable;
1494  if (enable) {
1495  scene_->addEntity(sun_.filament_entity);
1496  } else {
1497  scene_->remove(sun_.filament_entity);
1498  }
1499  }
1500 }
1501 
1503  auto& light_mgr = engine_.getLightManager();
1504  filament::LightManager::Instance inst =
1505  light_mgr.getInstance(sun_.filament_entity);
1506  return light_mgr.setShadowCaster(inst, enable);
1507 }
1508 
1510  auto& light_mgr = engine_.getLightManager();
1511  filament::LightManager::Instance inst =
1512  light_mgr.getInstance(sun_.filament_entity);
1513  light_mgr.setIntensity(inst, intensity);
1514 }
1515 
1517  auto& light_mgr = engine_.getLightManager();
1518  filament::LightManager::Instance inst =
1519  light_mgr.getInstance(sun_.filament_entity);
1520  return light_mgr.getIntensity(inst);
1521 }
1522 
1523 void FilamentScene::SetSunLightColor(const Eigen::Vector3f& color) {
1524  auto& light_mgr = engine_.getLightManager();
1525  filament::LightManager::Instance inst =
1526  light_mgr.getInstance(sun_.filament_entity);
1527  light_mgr.setColor(inst, {color.x(), color.y(), color.z()});
1528 }
1529 
1531  auto& light_mgr = engine_.getLightManager();
1532  filament::LightManager::Instance inst =
1533  light_mgr.getInstance(sun_.filament_entity);
1534  auto dir = light_mgr.getColor(inst);
1535  return {dir[0], dir[1], dir[2]};
1536 }
1537 
1538 void FilamentScene::SetSunLightDirection(const Eigen::Vector3f& direction) {
1539  auto& light_mgr = engine_.getLightManager();
1540  filament::LightManager::Instance inst =
1541  light_mgr.getInstance(sun_.filament_entity);
1542  light_mgr.setDirection(inst, {direction.x(), direction.y(), direction.z()});
1543 }
1544 
1546  auto& light_mgr = engine_.getLightManager();
1547  filament::LightManager::Instance inst =
1548  light_mgr.getInstance(sun_.filament_entity);
1549  light_mgr.setSunAngularRadius(inst, radius);
1550 }
1551 
1553  auto& light_mgr = engine_.getLightManager();
1554  filament::LightManager::Instance inst =
1555  light_mgr.getInstance(sun_.filament_entity);
1556  light_mgr.setSunHaloSize(inst, size);
1557 }
1558 
1560  auto& light_mgr = engine_.getLightManager();
1561  filament::LightManager::Instance inst =
1562  light_mgr.getInstance(sun_.filament_entity);
1563  light_mgr.setSunHaloFalloff(inst, falloff);
1564 }
1565 
1567  auto& light_mgr = engine_.getLightManager();
1568  filament::LightManager::Instance inst =
1569  light_mgr.getInstance(sun_.filament_entity);
1570  auto dir = light_mgr.getDirection(inst);
1571  return {dir[0], dir[1], dir[2]};
1572 }
1573 
1574 bool FilamentScene::SetIndirectLight(const std::string& ibl_name) {
1575  auto old_ibl = ibl_handle_;
1576  auto old_sky = skybox_handle_;
1577 
1578  // Load IBL
1579  std::string ibl_path = ibl_name + std::string("_ibl.ktx");
1581  renderer_.AddIndirectLight(ResourceLoadRequest(ibl_path.c_str()));
1582  if (!new_ibl) {
1583  return false;
1584  }
1585 
1586  auto wlight = resource_mgr_.GetIndirectLight(new_ibl);
1587  if (auto light = wlight.lock()) {
1588  indirect_light_ = wlight;
1589  if (ibl_enabled_) scene_->setIndirectLight(light.get());
1590  ibl_name_ = ibl_name;
1591  ibl_handle_ = new_ibl;
1592  if (old_ibl) {
1593  resource_mgr_.Destroy(old_ibl);
1594  }
1595  }
1596 
1597  // Load matching skybox
1598  std::string skybox_path = ibl_name + std::string("_skybox.ktx");
1599  SkyboxHandle sky =
1600  renderer_.AddSkybox(ResourceLoadRequest(skybox_path.c_str()));
1601  auto wskybox = resource_mgr_.GetSkybox(sky);
1602  if (auto skybox = wskybox.lock()) {
1603  skybox_ = wskybox;
1604  if (skybox_enabled_) {
1605  scene_->setSkybox(skybox.get());
1606  ShowSkybox(true);
1607  }
1608  skybox_handle_ = sky;
1609  if (old_sky) {
1610  resource_mgr_.Destroy(old_sky);
1611  }
1612  }
1613 
1614  return true;
1615 }
1616 
1617 const std::string& FilamentScene::GetIndirectLight() { return ibl_name_; }
1618 
1620  if (enable != ibl_enabled_) {
1621  if (enable) {
1622  if (auto light = indirect_light_.lock()) {
1623  scene_->setIndirectLight(light.get());
1624  }
1625  } else {
1626  scene_->setIndirectLight(nullptr);
1627  }
1628  ibl_enabled_ = enable;
1629  }
1630 }
1631 
1633  if (auto light = indirect_light_.lock()) {
1634  light->setIntensity(intensity);
1635  }
1636 }
1637 
1639  if (auto light = indirect_light_.lock()) {
1640  return light->getIntensity();
1641  }
1642  return 0.f;
1643 }
1644 
1646  if (auto light = indirect_light_.lock()) {
1647  auto ft = converters::FilamentMatrixFromEigenMatrix(rotation.matrix());
1648  light->setRotation(ft.upperLeft());
1649  }
1650 }
1651 
1653  if (auto light = indirect_light_.lock()) {
1654  converters::FilamentMatrix ft(light->getRotation());
1655  auto et = converters::EigenMatrixFromFilamentMatrix(ft);
1656 
1657  return Transform(et);
1658  }
1659  return {};
1660 }
1661 
1662 void FilamentScene::ShowSkybox(bool show) {
1663  if (show != skybox_enabled_) {
1664  if (show) {
1665  if (auto skybox = skybox_.lock()) {
1666  scene_->setSkybox(skybox.get());
1667  }
1668  } else {
1669  scene_->setSkybox(nullptr);
1670  }
1671  ShowGeometry(kBackgroundName, !show);
1672  skybox_enabled_ = show;
1673  }
1674 }
1675 
1677  return (scene_->getSkybox() != nullptr);
1678 }
1679 
1680 void FilamentScene::CreateBackgroundGeometry() {
1681  if (!HasGeometry(kBackgroundName)) {
1682  ccMesh quad;
1683  quad.CreateInternalCloud();
1684  // The coordinates are in raw GL coordinates, what Filament calls
1685  // "device coordinates". Since we want to draw on the entire screen,
1686  // and GL's native coordinates range from (-1, 1) to (1, 1), that's
1687  // what we use. The z value does not matter, as we are writing directly
1688  // to GL coordinates and we won't be writing depth values.
1689 
1690  quad.reserve(2);
1691  quad.addEigenVertices({{-1.0, -1.0, 0.0},
1692  {1.0, -1.0, 0.0},
1693  {1.0, 1.0, 0.0},
1694  {-1.0, 1.0, 0.0}});
1695  quad.addTriangles({{0, 1, 2}, {0, 2, 3}});
1696 
1697  MaterialRecord m;
1698  m.shader = "unlitBackground";
1699  m.base_color = {1.f, 1.f, 1.f, 1.f};
1700  m.aspect_ratio = 0.0;
1701  AddGeometry(kBackgroundName, quad, m);
1702  // Since this is the background,
1703  // we don't want any shadows,
1704  // we want to prevent the object from getting culled out of the
1705  // scene (since the whole point is for it to always draw), and
1706  // we want to set the priority so that it draws first (the shader
1707  // will overwrite all the pixels
1708  GeometryShadows(kBackgroundName, false, false);
1709  SetGeometryPriority(kBackgroundName, 0);
1710  SetGeometryCulling(kBackgroundName, false);
1711  }
1712 }
1713 
1715  const Eigen::Vector4f& color,
1716  const std::shared_ptr<geometry::Image> image) {
1717  // Make sure background geometry exists
1718  CreateBackgroundGeometry();
1719 
1720  std::shared_ptr<geometry::Image> new_image;
1721  if (image && image->width_ != 0 && image->height_ != 0) {
1722  new_image = image;
1723  }
1724 
1725  MaterialRecord m;
1726  m.shader = "unlitBackground";
1727  m.base_color = color;
1728  if (new_image) {
1729  m.albedo_img = new_image;
1730  m.aspect_ratio = static_cast<float>(new_image->width_) /
1731  static_cast<float>(new_image->height_);
1732  // See if we can replace the image data instead of re-creating the
1733  // whole texture. We need to make sure that both old and new images
1734  // actually exist, first. (The texture may exist, so we can't query it)
1735  if (new_image && background_image_) {
1736  auto geom_it = geometries_.find(kBackgroundName);
1737  if (geom_it != geometries_.end()) {
1738  // Try updating the texture. If the sizes are incorrect, this
1739  // will fail.
1740  if (resource_mgr_.UpdateTexture(
1741  geom_it->second.mat.maps.albedo_map, new_image,
1742  false /*not sRGB*/)) {
1743  return;
1744  }
1745  }
1746  }
1747  } else {
1748  m.albedo_img = nullptr;
1749  m.aspect_ratio = 0.0;
1750  }
1751  auto geom_it = geometries_.find(kBackgroundName);
1752  if (geom_it != geometries_.end()) {
1753  if (geom_it->second.mat.maps.albedo_map) {
1754  resource_mgr_.Destroy(geom_it->second.mat.maps.albedo_map);
1755  geom_it->second.mat.maps.albedo_map =
1757  }
1758  }
1759  OverrideMaterial(kBackgroundName, m);
1760  background_image_ = new_image;
1761 }
1762 
1764  CreateBackgroundGeometry();
1765  auto geoms = GetGeometry(kBackgroundName);
1766  auto geom_mi = geoms[0]->mat;
1767 
1768  auto tex_weak = resource_mgr_.GetTexture(image);
1769  auto tex = tex_weak.lock();
1770  float aspect = 1.f;
1771  if (tex) {
1772  aspect = static_cast<float>(tex->getWidth()) /
1773  static_cast<float>(tex->getHeight());
1774  }
1775 
1776  renderer_.ModifyMaterial(geom_mi.mat_instance)
1777  .SetParameter("aspectRatio", aspect)
1778  .SetParameter("yOrigin", 0.0f)
1779  .SetTexture("albedo", image,
1781  .Finish();
1782 }
1783 
1784 void FilamentScene::CreateGroundPlaneGeometry() {
1785  ccMesh quad;
1786  quad.CreateInternalCloud();
1787  // Please see note above about drawing a full screen quad with
1788  // Filament. However, the ground plane shader expects the full screen quad
1789  // to be rendered at the far plan hence the z must be set to 1.0
1790  quad.reserve(2);
1791  quad.addEigenVertices({{-1.0, -1.0, 1.0},
1792  {1.0, -1.0, 1.0},
1793  {1.0, 1.0, 1.0},
1794  {-1.0, 1.0, 1.0}});
1795  quad.addTriangles({{0, 1, 2}, {0, 2, 3}});
1797  m.shader = "infiniteGroundPlane";
1798  m.base_color = kDefaultGroundPlaneColor;
1799  AddGeometry(kGroundPlaneName, quad, m);
1800  GeometryShadows(kGroundPlaneName, false, false);
1801  SetGeometryPriority(kGroundPlaneName, 1);
1802  SetGeometryCulling(kGroundPlaneName, false);
1803 }
1804 
1806  if (!HasGeometry(kGroundPlaneName)) {
1807  CreateGroundPlaneGeometry();
1808  }
1809  if (enable) {
1811  m.shader = "infiniteGroundPlane";
1812  m.base_color = kDefaultGroundPlaneColor;
1813  if (plane == GroundPlane::XY) {
1814  m.ground_plane_axis = 1.f;
1815  } else if (plane == GroundPlane::YZ) {
1816  m.ground_plane_axis = -1.f;
1817  }
1818  OverrideMaterial(kGroundPlaneName, m);
1819  }
1820 
1821  ShowGeometry(kGroundPlaneName, enable);
1822 }
1823 
1824 void FilamentScene::SetGroundPlaneColor(const Eigen::Vector4f& color) {
1825  if (!HasGeometry(kGroundPlaneName)) {
1826  CreateGroundPlaneGeometry();
1827  }
1828 
1829  // NOTE: Not implemented yet. Not sure if we want/need this.
1830 }
1831 
1833  bool frame_done = false;
1834  std::shared_ptr<geometry::Image> image;
1835 };
1836 
1837 void ReadPixelsCallback(void* buffer, size_t buffer_size, void* user) {
1838  auto rr = static_cast<RenderRequest*>(user);
1839  rr->frame_done = true;
1840 
1841  if (buffer_size > 0) {
1842  rr->image->data_ = std::vector<uint8_t>((uint8_t*)buffer,
1843  (uint8_t*)buffer + buffer_size);
1844  } else {
1846  "0 buffer size encountered while rendering to image");
1847  }
1848 }
1849 
1851  std::function<void(std::shared_ptr<geometry::Image>)> callback) {
1852  auto view = views_.begin()->second.view.get();
1853  renderer_.RenderToImage(view, this, callback);
1854 }
1855 
1857  std::function<void(std::shared_ptr<geometry::Image>)> callback) {
1858  auto view = views_.begin()->second.view.get();
1859  renderer_.RenderToDepthImage(view, this, callback);
1860 }
1861 
1862 std::vector<FilamentScene::RenderableGeometry*> FilamentScene::GetGeometry(
1863  const std::string& object_name, bool warn_if_not_found) {
1864  std::vector<RenderableGeometry*> geoms;
1865  if (GeometryIsModel(object_name)) {
1866  for (const auto& name : model_geometries_[object_name]) {
1867  auto geom_entry = geometries_.find(name);
1868  if (geom_entry == geometries_.end()) {
1869  if (warn_if_not_found) {
1870  utility::LogWarning("Geometry {} is not in the scene graph",
1871  name);
1872  }
1873  } else {
1874  geoms.push_back(&geom_entry->second);
1875  }
1876  }
1877  } else {
1878  auto geom_entry = geometries_.find(object_name);
1879  if (geom_entry == geometries_.end()) {
1880  if (warn_if_not_found) {
1881  utility::LogWarning("Geometry {} is not in the scene graph",
1882  object_name);
1883  }
1884  } else {
1885  geoms.push_back(&geom_entry->second);
1886  }
1887  }
1888 
1889  return geoms;
1890 }
1891 
1892 bool FilamentScene::GeometryIsModel(const std::string& object_name) const {
1893  return model_geometries_.count(object_name) > 0;
1894 }
1895 
1896 FilamentScene::LightEntity* FilamentScene::GetLightInternal(
1897  const std::string& light_name, bool warn_if_not_found) {
1898  auto light_entry = lights_.find(light_name);
1899  if (light_entry == lights_.end()) {
1900  if (warn_if_not_found) {
1901  utility::LogWarning("Light {} is not in the scene graph",
1902  light_name);
1903  }
1904  return nullptr;
1905  }
1906  return &(light_entry->second);
1907 }
1908 
1909 void FilamentScene::RenderableGeometry::ReleaseResources(
1910  filament::Engine& engine, FilamentResourceManager& manager) {
1911  if (vb) manager.Destroy(vb);
1912  if (ib) manager.Destroy(ib);
1913  engine.destroy(filament_entity);
1914 
1915  // Delete texture maps...
1916  auto destroy_map = [&manager](rendering::TextureHandle map) {
1919  manager.Destroy(map);
1920  };
1921  destroy_map(mat.maps.albedo_map);
1922  destroy_map(mat.maps.normal_map);
1923  destroy_map(mat.maps.ao_rough_metal_map);
1924  destroy_map(mat.maps.reflectance_map);
1925  destroy_map(mat.maps.clear_coat_map);
1926  destroy_map(mat.maps.clear_coat_roughness_map);
1927  destroy_map(mat.maps.anisotropy_map);
1928 
1929  manager.Destroy(mat.mat_instance);
1930 
1931  filament_entity.clear();
1932 }
1933 
1934 void FilamentScene::Draw(filament::Renderer& renderer) {
1935  for (auto& pair : views_) {
1936  auto& container = pair.second;
1937  // Skip inactive views
1938  if (!container.is_active) continue;
1939  if (container.render_count-- == 0) {
1940  container.is_active = false;
1941  continue;
1942  }
1943 
1944  container.view->PreRender();
1945  renderer.render(container.view->GetNativeView());
1946  container.view->PostRender();
1947  }
1948 }
1949 
1951  for (auto geom : geometries_) {
1952  if (geom.second.mat.properties.shader == "defaultLitSSR") {
1953  if (hide) {
1954  if (!geom.second.visible) {
1955  geom.second.was_hidden_before_picking = true;
1956  } else {
1957  ShowGeometry(geom.first, false);
1958  }
1959  } else {
1960  if (!geom.second.was_hidden_before_picking) {
1961  ShowGeometry(geom.first, true);
1962  }
1963  }
1964  }
1965  }
1966 }
1967 
1968 } // namespace rendering
1969 } // namespace visualization
1970 } // namespace cloudViewer
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
Definition: CVGeom.h:798
std::shared_ptr< core::Tensor > image
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int size
std::string name
int height
int points
math::float4 color
math::float3 position
size_t stride
std::vector< UVAtlasVertex > vb
std::vector< uint8_t > ib
core::Tensor result
Definition: VtkUtils.cpp:76
bool copy
Definition: VtkUtils.cpp:74
Bounding box structure.
Definition: ecvBBox.h:25
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
virtual bool IsEmpty() const
Definition: ecvHObject.h:142
CV_CLASS_ENUM getClassID() const override
Returns class ID.
Definition: ecvHObject.h:232
Triangular mesh.
Definition: ecvMesh.h:35
void addEigenVertices(const std::vector< Eigen::Vector3d > &vertices)
void addTriangles(const std::vector< Eigen::Vector3i > &triangles)
Definition: ecvMesh.h:265
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
Mix-in class for geometry types that can be visualized.
The base geometry class.
Definition: Geometry.h:23
virtual bool IsEmpty() const =0
Returns true iff the geometry is empty.
A point cloud contains a list of 3D points.
Definition: PointCloud.h:82
core::Tensor & GetPointNormals()
Get the value of the "normals" attribute. Convenience function.
Definition: PointCloud.h:130
bool HasPointAttr(const std::string &key) const
Definition: PointCloud.h:207
PointCloud CPU() const
Backward-compatible convenience method to obtain a CPU-resident copy.
Definition: PointCloud.h:245
const TensorMap & GetPointAttr() const
Getter for point_attr_ TensorMap. Used in Pybind.
Definition: PointCloud.h:111
core::Tensor & GetPointColors()
Get the value of the "colors" attribute. Convenience function.
Definition: PointCloud.h:127
std::weak_ptr< filament::IndexBuffer > GetIndexBuffer(const IndexBufferHandle &id)
std::weak_ptr< filament::IndirectLight > GetIndirectLight(const IndirectLightHandle &id)
bool UpdateTexture(TextureHandle texture, const std::shared_ptr< geometry::Image > image, bool srgb)
std::weak_ptr< filament::Texture > GetTexture(const TextureHandle &id)
std::weak_ptr< filament::Skybox > GetSkybox(const SkyboxHandle &id)
std::weak_ptr< filament::VertexBuffer > GetVertexBuffer(const VertexBufferHandle &id)
std::weak_ptr< filament::MaterialInstance > GetMaterialInstance(const MaterialInstanceHandle &id)
MaterialInstanceHandle CreateMaterialInstance(const MaterialHandle &id)
bool AddDirectionalLight(const std::string &light_name, const Eigen::Vector3f &color, const Eigen::Vector3f &direction, float intensity, bool cast_shadows) override
bool AddSpotLight(const std::string &light_name, const Eigen::Vector3f &color, const Eigen::Vector3f &position, const Eigen::Vector3f &direction, float intensity, float falloff, float inner_cone_angle, float outer_cone_angle, bool cast_shadows) override
void SetIndirectLightIntensity(float intensity) override
Transform GetGeometryTransform(const std::string &object_name) override
View * GetView(const ViewHandle &view_id) const override
void EnableGroundPlane(bool enable, GroundPlane plane) override
ViewHandle AddView(std::int32_t x, std::int32_t y, std::uint32_t w, std::uint32_t h) override
void OverrideMaterial(const std::string &object_name, const MaterialRecord &material) override
void SetRenderOnce(const ViewHandle &view_id) override
void RemoveGeometry(const std::string &object_name) override
void UpdateGeometry(const std::string &object_name, const t::geometry::PointCloud &point_cloud, uint32_t update_flags) override
void SetSunLight(const Eigen::Vector3f &direction, const Eigen::Vector3f &color, float intensity) override
void UpdateLightPosition(const std::string &light_name, const Eigen::Vector3f &position) override
bool GeometryIsVisible(const std::string &object_name) override
void RemoveCamera(const std::string &camera_name) override
void EnableLightShadow(const std::string &light_name, bool cast_shadows) override
bool HasGeometry(const std::string &object_name) const override
void UpdateLight(const std::string &light_name, const Light &light) override
void SetActiveCamera(const std::string &camera_name) override
void SetSunLightColor(const Eigen::Vector3f &color) override
void SetBackground(const Eigen::Vector4f &color, const std::shared_ptr< geometry::Image > image=nullptr) override
void AddCamera(const std::string &camera_name, std::shared_ptr< Camera > cam) override
void SetGeometryPriority(const std::string &object_name, uint8_t priority) override
void RenderToDepthImage(std::function< void(std::shared_ptr< geometry::Image >)> callback) override
Size of image is the size of the window.
Light & GetLight(const std::string &light_name) override
ccBBox GetGeometryBoundingBox(const std::string &object_name) override
void UpdateLightConeAngles(const std::string &light_name, float inner_cone_angle, float outer_cone_angle) override
void SetGroundPlaneColor(const Eigen::Vector4f &color) override
void SetGeometryTransform(const std::string &object_name, const Transform &transform) override
void GeometryShadows(const std::string &object_name, bool cast_shadows, bool receive_shadows) override
void UpdateLightColor(const std::string &light_name, const Eigen::Vector3f &color) override
void UpdateLightIntensity(const std::string &light_name, float intensity) override
bool SetIndirectLight(const std::string &ibl_name) override
void QueryGeometry(std::vector< std::string > &geometry) override
void OverrideMaterialAll(const MaterialRecord &material, bool shader_only=true) override
FilamentScene(filament::Engine &engine, FilamentResourceManager &resource_mgr, Renderer &renderer)
void ShowGeometry(const std::string &object_name, bool show) override
void RemoveLight(const std::string &light_name) override
void SetGeometryCulling(const std::string &object_name, bool enable) override
void RemoveView(const ViewHandle &view_id) override
void SetViewActive(const ViewHandle &view_id, bool is_active) override
bool AddGeometry(const std::string &object_name, const ccHObject &geometry, const MaterialRecord &material, const std::string &downsampled_name="", size_t downsample_threshold=SIZE_MAX) override
void UpdateLightDirection(const std::string &light_name, const Eigen::Vector3f &direction) override
void RenderToImage(std::function< void(std::shared_ptr< geometry::Image >)> callback) override
Size of image is the size of the window.
void SetIndirectLightRotation(const Transform &rotation) override
void UpdateLightFalloff(const std::string &light_name, float falloff) override
void SetSunLightDirection(const Eigen::Vector3f &direction) override
bool AddPointLight(const std::string &light_name, const Eigen::Vector3f &color, const Eigen::Vector3f &position, float intensity, float falloff, bool cast_shadows) override
static constexpr std::uint8_t kAllLayersMask
Definition: FilamentView.h:35
virtual filament::RenderableManager::PrimitiveType GetPrimitiveType() const =0
static std::unique_ptr< GeometryBuffersBuilder > GetBuilder(const ccHObject &geometry)
virtual MaterialModifier & SetParameter(const char *parameter, int value)=0
virtual MaterialModifier & SetColor(const char *parameter, const Eigen::Vector3f &value, bool srgb)=0
virtual MaterialModifier & SetTexture(const char *parameter, const TextureHandle &texture, const TextureSamplerParameters &sampler)=0
virtual MaterialModifier & ModifyMaterial(const MaterialHandle &id)=0
virtual IndirectLightHandle AddIndirectLight(const ResourceLoadRequest &request)=0
void RenderToImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb)
Definition: Renderer.cpp:68
virtual TextureHandle AddTexture(const ResourceLoadRequest &request, bool srgb=false)=0
virtual SkyboxHandle AddSkybox(const ResourceLoadRequest &request)=0
void RenderToDepthImage(View *view, Scene *scene, std::function< void(std::shared_ptr< geometry::Image >)> cb, bool z_in_view_space=false)
Definition: Renderer.cpp:95
Eigen::Transform< float, 3, Eigen::Affine > Transform
Definition: Scene.h:55
static const uint32_t kUpdateNormalsFlag
Definition: Scene.h:47
static const uint32_t kUpdatePointsFlag
Definition: Scene.h:46
static const uint32_t kUpdateColorsFlag
Definition: Scene.h:48
static const uint32_t kUpdateUv0Flag
Definition: Scene.h:49
double normals[3]
#define LogWarning(...)
Definition: Logging.h:72
#define LogInfo(...)
Definition: Logging.h:81
#define LogDebug(...)
Definition: Logging.h:90
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
GeometryType
Definition: CVTypes.h:101
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
void OutputMaterialProperties(const visualization::rendering::MaterialRecord &mat)
void ReadPixelsCallback(void *buffer, size_t buffer_size, void *user)
REHandle< EntityType::Material > MaterialHandle
std::shared_ptr< geometry::Image > CombineTextures(std::shared_ptr< geometry::Image > ao, std::shared_ptr< geometry::Image > rough, std::shared_ptr< geometry::Image > metal)
Generic file read and write utility for python interface.
constexpr Rgbaf light(0.66f, 0.66f, 0.66f, 1.00f)
std::string to_string(const T &n)
Definition: Common.h:20
std::shared_ptr< geometry::Image > albedo_img
std::vector< visualization::rendering::MaterialRecord > materials_
Definition: Model.h:26