ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilamentResourceManager.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 "core/Dtype.h"
11 
12 // 4068: Filament has some clang-specific vectorizing pragma's that MSVC flags
13 // 4146: PixelBufferDescriptor assert unsigned is positive before subtracting
14 // but MSVC can't figure that out.
15 // 4293: Filament's utils/algorithm.h utils::details::clz() does strange
16 // things with MSVC. Somehow sizeof(unsigned int) > 4, but its size is
17 // 32 so that x >> 32 gives a warning. (Or maybe the compiler can't
18 // determine the if statement does not run.)
19 // 4305: LightManager.h needs to specify some constants as floats
20 #ifdef _MSC_VER
21 #pragma warning(push)
22 #pragma warning(disable : 4068 4146 4293 4305)
23 #endif // _MSC_VER
24 
25 #include <filament/Engine.h>
26 #include <filament/IndexBuffer.h>
27 #include <filament/IndirectLight.h>
28 #include <filament/LightManager.h>
29 #include <filament/Material.h>
30 #include <filament/RenderTarget.h>
31 #include <filament/RenderableManager.h>
32 #include <filament/Scene.h>
33 #include <filament/Skybox.h>
34 #include <filament/Texture.h>
35 #include <filament/TextureSampler.h>
36 #include <image/KtxBundle.h>
37 #include <image/KtxUtility.h>
38 
39 #ifdef _MSC_VER
40 #pragma warning(pop)
41 #endif // _MSC_VER
42 
43 #include <FileSystem.h>
44 #include <ImageIO.h>
45 #include <Logging.h>
46 
47 #include "t/geometry/Image.h"
51 
52 namespace cloudViewer {
53 namespace visualization {
54 namespace rendering {
55 
56 namespace {
57 template <class ResourceType>
58 using ResourcesContainer =
59  FilamentResourceManager::ResourcesContainer<ResourceType>;
60 
61 // We need custom shared pointer make function to
62 // use engine deleter for allocated filament entities
63 template <class ResourceType>
64 std::shared_ptr<ResourceType> MakeShared(ResourceType* pointer,
65  filament::Engine& engine) {
66  return std::move(std::shared_ptr<ResourceType>(
67  pointer, [&engine](ResourceType* p) { engine.destroy(p); }));
68 }
69 
70 template <class ResourceType>
71 FilamentResourceManager::BoxedResource<ResourceType> BoxResource(
72  ResourceType* pointer, filament::Engine& engine) {
73  return FilamentResourceManager::BoxedResource<ResourceType>(
74  MakeShared(pointer, engine));
75 }
76 
77 template <class Handle, class ResourceType>
78 Handle RegisterResource(filament::Engine& engine,
79  ResourceType* resource,
80  ResourcesContainer<ResourceType>& container) {
81  if (!resource) {
82  utility::LogError("Trying to register empty resource!");
83  return Handle::kBad;
84  }
85 
86  auto new_handle = Handle::Next();
87  container[new_handle] = std::move(BoxResource(resource, engine));
88  return new_handle;
89 }
90 
91 template <class ResourceType>
92 std::weak_ptr<ResourceType> FindResource(
93  const REHandle_abstract& id,
94  ResourcesContainer<ResourceType>& container) {
95  auto found = container.find(id);
96  if (found != container.end()) {
97  return found->second.ptr;
98  }
99 
100  utility::LogWarning("Resource {} not found.", id);
101  return std::weak_ptr<ResourceType>();
102 }
103 
104 template <class ResourceType>
105 void DestroyResource(const REHandle_abstract& id,
106  ResourcesContainer<ResourceType>& container) {
107  auto found = container.find(id);
108  if (found == container.end()) {
109  utility::LogError("Trying to destroy nonexistent resource ({})!", id);
110  return;
111  }
112 
113  found->second.use_count -= 1;
114  if (found->second.use_count == 0) {
115  container.erase(found);
116  } else if (found->second.use_count < 0) {
117  utility::LogError("Negative use count for resource ({})!", id);
118  return;
119  }
120 }
121 
122 // Image data that is retained by renderer thread,
123 // will be freed on PixelBufferDescriptor callback
124 std::unordered_map<std::intptr_t, std::shared_ptr<geometry::Image>>
125  pending_images;
126 
127 std::intptr_t RetainImageForLoading(
128  const std::shared_ptr<geometry::Image>& img) {
129  static std::intptr_t img_id = 1;
130 
131  const auto id = img_id;
132  pending_images[img_id] = img;
133  ++img_id;
134 
135  return id;
136 }
137 
138 static void DeallocateBuffer(void* buffer, size_t size, void* user_ptr) {
139  free(buffer);
140 }
141 
142 void FreeRetainedImage(void* buffer, size_t size, void* user_ptr) {
143  const auto id = reinterpret_cast<std::intptr_t>(user_ptr);
144  auto found = pending_images.find(id);
145  if (found != pending_images.end()) {
146  pending_images.erase(found);
147  } else {
149  "Trying to release non existent image shared pointer, id: {}",
150  id);
151  }
152 }
153 
154 filament::Material* LoadMaterialFromFile(const std::string& path,
155  filament::Engine& engine) {
156  std::vector<char> material_data;
157  std::string error_str;
158 
159  std::string platform_path = path;
160 #ifdef _WIN32
161  std::replace(platform_path.begin(), platform_path.end(), '/', '\\');
162 #endif // _WIN32
163  utility::LogDebug("LoadMaterialFromFile(): {}", platform_path);
164  if (utility::filesystem::FReadToBuffer(platform_path, material_data,
165  &error_str)) {
166  using namespace filament;
167  return Material::Builder()
168  .package(material_data.data(), material_data.size())
169  .build(engine);
170  }
171 
172  utility::LogDebug("Failed to load default material from {}. Error: {}",
173  platform_path, error_str);
174 
175  return nullptr;
176 }
177 
178 struct TextureSettings {
179  filament::Texture::Format image_format = filament::Texture::Format::RGB;
180  filament::Texture::Type image_type = filament::Texture::Type::UBYTE;
181  filament::Texture::InternalFormat format =
182  filament::Texture::InternalFormat::RGB8;
183  std::uint32_t texel_width = 0;
184  std::uint32_t texel_height = 0;
185 };
186 
187 void FormatSettingsFromImage(TextureSettings& settings,
188  int num_channels,
189  int bytes_per_channel,
190  bool srgb) {
191  // Map of (bytes_per_channel << 4 | num_channles) -> internal format
192  static std::unordered_map<unsigned int, filament::Texture::InternalFormat>
193  format_map = {
194  {(1 << 4 | 1), filament::Texture::InternalFormat::R8},
195  {(1 << 4 | 2), filament::Texture::InternalFormat::RG8},
196  {(1 << 4 | 3), filament::Texture::InternalFormat::RGB8},
197  {(1 << 4 | 4), filament::Texture::InternalFormat::RGBA8}};
198 
199  // Set image format
200  switch (num_channels) {
201  case 1:
202  settings.image_format = filament::Texture::Format::R;
203  break;
204  case 2:
205  settings.image_format = filament::Texture::Format::RG;
206  break;
207  case 3:
208  settings.image_format = filament::Texture::Format::RGB;
209  break;
210  case 4:
211  settings.image_format = filament::Texture::Format::RGBA;
212  break;
213  default:
214  utility::LogError("Unsupported image number of channels: {}",
215  num_channels);
216  break;
217  }
218 
219  // Figure out internal format
220  unsigned int key = (bytes_per_channel << 4 | num_channels);
221  if (format_map.count(key) > 0) {
222  settings.format = format_map[key];
223  } else {
225  "Unsupported combination of number of channels ({}) and bytes "
226  "per channel ({}).",
227  num_channels, bytes_per_channel);
228  }
229 
230  // Override the two special cases of RGB/RGBA with srgb=true
231  if (srgb && bytes_per_channel == 1 &&
232  (num_channels == 3 || num_channels == 4)) {
233  if (num_channels == 3) {
234  settings.format = filament::Texture::InternalFormat::SRGB8;
235  } else {
236  settings.format = filament::Texture::InternalFormat::SRGB8_A8;
237  }
238  }
239 }
240 
241 void DataTypeFromImage(TextureSettings& settings, int bytes_per_channel) {
242  switch (bytes_per_channel) {
243  case 1:
244  settings.image_type = filament::Texture::Type::UBYTE;
245  break;
246  case 2:
247  settings.image_type = filament::Texture::Type::USHORT;
248  break;
249 
250  case 4:
251  settings.image_type = filament::Texture::Type::FLOAT;
252  break;
253 
254  default:
255  utility::LogError("Unsupported image bytes per channel: {}",
256  bytes_per_channel);
257  break;
258  }
259 }
260 
261 TextureSettings GetSettingsFromImage(const geometry::Image& image, bool srgb) {
262  TextureSettings settings;
263 
264  settings.texel_width = image.width_;
265  settings.texel_height = image.height_;
266 
267  FormatSettingsFromImage(settings, image.num_of_channels_,
268  image.bytes_per_channel_, srgb);
269  DataTypeFromImage(settings, image.bytes_per_channel_);
270  return settings;
271 }
272 
273 TextureSettings GetSettingsFromImage(const t::geometry::Image& image,
274  bool srgb) {
275  TextureSettings settings;
276 
277  settings.texel_width = image.GetCols();
278  settings.texel_height = image.GetRows();
279 
280  FormatSettingsFromImage(settings, image.GetChannels(),
281  image.GetDtype().ByteSize(), srgb);
282  DataTypeFromImage(settings, image.GetDtype().ByteSize());
283  return settings;
284 }
285 
286 } // namespace
287 
330 
331 static const std::unordered_set<REHandle_abstract> kDefaultResources = {
348 
350  : engine_(engine) {
351  LoadDefaults();
352 }
353 
355 
357  const void* material_data, const size_t data_size) {
358  using namespace filament;
359 
360  Material* material = Material::Builder()
361  .package(material_data, data_size)
362  .build(engine_);
363 
364  MaterialHandle handle;
365  if (material) {
366  handle =
367  RegisterResource<MaterialHandle>(engine_, material, materials_);
368  }
369 
370  return handle;
371 }
372 
374  const ResourceLoadRequest& request) {
375  MaterialHandle handle;
376 
377  if (!request.path_.empty()) {
378  std::vector<char> material_data;
379  std::string error_str;
380 
381  if (utility::filesystem::FReadToBuffer(request.path_, material_data,
382  &error_str)) {
383  handle = CreateMaterial(material_data.data(), material_data.size());
384  } else {
385  request.error_callback_(request, errno, error_str);
386  }
387  } else if (request.data_size_ > 0) {
388  // TODO: Filament throws an exception if it can't parse the
389  // material. Handle this exception across library boundary
390  // to avoid aborting.
391  handle = CreateMaterial(request.data_, request.data_size_);
392  } else {
393  request.error_callback_(request, -1, "");
394  }
395 
396  return handle;
397 }
398 
400  const MaterialHandle& id) {
401  auto found = materials_.find(id);
402  if (found != materials_.end()) {
403  auto material_instance = found->second->createInstance();
404  return RegisterResource<MaterialInstanceHandle>(
405  engine_, material_instance, material_instances_);
406  }
407 
408  utility::LogWarning("Material ({}) for creating instance not found", id);
409  return {};
410 }
411 
413  bool srgb) {
414  std::shared_ptr<geometry::Image> img;
415 
416  if (path) {
418  } else {
419  utility::LogWarning("Empty path for texture loading provided");
420  }
421 
422  return CreateTexture(img, srgb);
423 }
424 
426  const std::shared_ptr<geometry::Image>& img, bool srgb) {
427  TextureHandle handle;
428  if (img->HasData()) {
429  auto texture = LoadTextureFromImage(img, srgb);
430 
431  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
432  }
433 
434  return handle;
435 }
436 
438  const geometry::Image& image, bool srgb) {
439  TextureHandle handle;
440  if (image.HasData()) {
441  auto copy = std::make_shared<geometry::Image>(image);
442 
443  auto texture = LoadTextureFromImage(copy, srgb);
444 
445  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
446  }
447 
448  return handle;
449 }
450 
452  const t::geometry::Image& image, bool srgb) {
453  TextureHandle handle;
454  auto texture = LoadTextureFromImage(image, srgb);
455  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
456  return handle;
457 }
458 
460  const Eigen::Vector3f& color, size_t dimension) {
461  TextureHandle handle;
462  auto texture = LoadFilledTexture(color, dimension);
463  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
464 
465  return handle;
466 }
467 
469  TextureHandle texture,
470  const std::shared_ptr<geometry::Image> image,
471  bool srgb) {
472  auto ftexture_weak = GetTexture(texture);
473  if (auto ftexture = ftexture_weak.lock()) {
474  if (ftexture->getWidth() == size_t(image->width_) &&
475  ftexture->getHeight() == size_t(image->height_)) {
476  auto retained_img_id = RetainImageForLoading(image);
477  auto texture_settings = GetSettingsFromImage(*image, srgb);
478  filament::Texture::PixelBufferDescriptor desc(
479  image->data_.data(), image->data_.size(),
480  texture_settings.image_format, texture_settings.image_type,
481  FreeRetainedImage, (void*)retained_img_id);
482  ftexture->setImage(engine_, 0, std::move(desc));
483  return true;
484  }
485  }
486  return false;
487 }
488 
490  const t::geometry::Image& image,
491  bool srgb) {
492  auto ftexture_weak = GetTexture(texture);
493  if (auto ftexture = ftexture_weak.lock()) {
494  if (ftexture->getWidth() == size_t(image.GetCols()) &&
495  ftexture->getHeight() == size_t(image.GetRows())) {
496  auto texture_settings = GetSettingsFromImage(image, srgb);
497  filament::Texture::PixelBufferDescriptor desc(
498  image.GetDataPtr(),
499  image.GetRows() * image.GetCols() * image.GetChannels() *
500  image.GetDtype().ByteSize(),
501  texture_settings.image_format, texture_settings.image_type);
502  ftexture->setImage(engine_, 0, std::move(desc));
503  return true;
504  }
505  }
506  return false;
507 }
508 
510  int width, int height) {
511  using namespace filament;
512  auto texture = Texture::Builder()
513  .width(width)
514  .height(height)
515  .levels(1)
516  .format(Texture::InternalFormat::RGBA16F)
517  .usage(Texture::Usage::COLOR_ATTACHMENT |
518  Texture::Usage::SAMPLEABLE)
519  .build(engine_);
520  TextureHandle handle;
521  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
522  return handle;
523 }
524 
526  int width, int height) {
527  using namespace filament;
528  auto texture = Texture::Builder()
529  .width(width)
530  .height(height)
531  .levels(1)
532  .format(Texture::InternalFormat::DEPTH32F)
533  .usage(Texture::Usage::DEPTH_ATTACHMENT)
534  .build(engine_);
535  TextureHandle handle;
536  handle = RegisterResource<TextureHandle>(engine_, texture, textures_);
537  return handle;
538 }
539 
542  using namespace filament;
543 
544  RenderTargetHandle handle;
545  auto color_tex_weak = GetTexture(color);
546  auto depth_tex_weak = GetTexture(depth);
547  auto color_tex = color_tex_weak.lock();
548  auto depth_tex = depth_tex_weak.lock();
549  if (!color_tex || !depth_tex) {
550  utility::LogWarning("Supplied texture attachments are invalid.");
551  return handle;
552  }
553 
554  auto rt = RenderTarget::Builder()
555  .texture(RenderTarget::COLOR, color_tex.get())
556  .texture(RenderTarget::DEPTH, depth_tex.get())
557  .build(engine_);
558  handle = RegisterResource<RenderTargetHandle>(engine_, rt, render_targets_);
559  return handle;
560 }
561 
563  const ResourceLoadRequest& request) {
564  IndirectLightHandle handle;
565 
566  if (!request.path_.empty()) {
567  std::vector<char> ibl_data;
568  std::string error_str;
569 
570  if (utility::filesystem::FReadToBuffer(request.path_, ibl_data,
571  &error_str)) {
572  using namespace filament;
573  // will be destroyed later by image::ktx::createTexture
574  auto* ibl_ktx = new image::KtxBundle(
575  reinterpret_cast<std::uint8_t*>(ibl_data.data()),
576  std::uint32_t(ibl_data.size()));
577  auto* ibl_texture =
578  image::ktx::createTexture(&engine_, ibl_ktx, false);
579 
580  filament::math::float3 bands[9] = {};
581  if (!ibl_ktx->getSphericalHarmonics(bands)) {
582  engine_.destroy(ibl_texture);
583  request.error_callback_(
584  request, 2,
585  "Failed to read spherical harmonics from ktx");
586  return handle;
587  }
588 
589  auto indirect_light = IndirectLight::Builder()
590  .reflections(ibl_texture)
591  .irradiance(3, bands)
592  .intensity(30000.f)
593  .build(engine_);
594 
595  if (indirect_light) {
596  handle = RegisterResource<IndirectLightHandle>(
597  engine_, indirect_light, ibls_);
598 
599  auto htexture = RegisterResource<TextureHandle>(
600  engine_, ibl_texture, textures_);
601  dependencies_[handle].insert(htexture);
602  } else {
603  request.error_callback_(
604  request, 3, "Failed to create indirect light from ktx");
605  engine_.destroy(ibl_texture);
606  }
607  } else {
608  request.error_callback_(request, errno, error_str);
609  }
610  } else {
611  request.error_callback_(request, -1, "");
612  }
613 
614  return handle;
615 }
616 
618  const Eigen::Vector3f& color) {
619  filament::math::float4 fcolor;
620  fcolor.r = color.x();
621  fcolor.g = color.y();
622  fcolor.b = color.z();
623  fcolor.a = 1.0f;
624  auto skybox =
625  filament::Skybox::Builder().showSun(false).color(fcolor).build(
626  engine_);
627  auto handle = RegisterResource<SkyboxHandle>(engine_, skybox, skyboxes_);
628  return handle;
629 }
630 
632  const ResourceLoadRequest& request) {
633  SkyboxHandle handle;
634 
635  if (!request.path_.empty()) {
636  std::vector<char> sky_data;
637  std::string error_str;
638 
639  if (utility::filesystem::FReadToBuffer(request.path_, sky_data,
640  &error_str)) {
641  using namespace filament;
642  // will be destroyed later by image::ktx::createTexture
643  auto* sky_ktx = new image::KtxBundle(
644  reinterpret_cast<std::uint8_t*>(sky_data.data()),
645  std::uint32_t(sky_data.size()));
646  auto* sky_texture =
647  image::ktx::createTexture(&engine_, sky_ktx, false);
648 
649  auto skybox = Skybox::Builder()
650  .environment(sky_texture)
651  .showSun(true)
652  .build(engine_);
653 
654  if (skybox) {
655  handle = RegisterResource<SkyboxHandle>(engine_, skybox,
656  skyboxes_);
657 
658  auto htex = RegisterResource<TextureHandle>(
659  engine_, sky_texture, textures_);
660  dependencies_[handle].insert(htex);
661  } else {
662  request.error_callback_(
663  request, 3, "Failed to create indirect light from ktx");
664  engine_.destroy(sky_texture);
665  }
666  } else {
667  request.error_callback_(request, errno, error_str);
668  }
669  } else {
670  request.error_callback_(request, -1, "");
671  }
672 
673  return handle;
674 }
675 
677  filament::VertexBuffer* vertex_buffer) {
678  return RegisterResource<VertexBufferHandle>(engine_, vertex_buffer,
679  vertex_buffers_);
680 }
681 
683  auto found = vertex_buffers_.find(vb);
684  if (found != vertex_buffers_.end()) {
685  found->second.use_count += 1;
686  } else {
687  utility::LogError("Reusing non-existant vertex buffer");
688  }
689 }
690 
692  size_t indices_count, size_t index_stride) {
693  using namespace filament;
694 
695  IndexBuffer* ibuf =
696  IndexBuffer::Builder()
697  .bufferType(index_stride == 2
698  ? IndexBuffer::IndexType::USHORT
699  : IndexBuffer::IndexType::UINT)
700  .indexCount(std::uint32_t(indices_count))
701  .build(engine_);
702 
703  IndexBufferHandle handle;
704  if (ibuf) {
705  handle = RegisterResource<IndexBufferHandle>(engine_, ibuf,
706  index_buffers_);
707  }
708 
709  return handle;
710 }
711 
712 std::weak_ptr<filament::Material> FilamentResourceManager::GetMaterial(
713  const MaterialHandle& id) {
714  return FindResource(id, materials_);
715 }
716 
717 std::weak_ptr<filament::MaterialInstance>
719  return FindResource(id, material_instances_);
720 }
721 
722 std::weak_ptr<filament::Texture> FilamentResourceManager::GetTexture(
723  const TextureHandle& id) {
724  return FindResource(id, textures_);
725 }
726 
727 std::weak_ptr<filament::RenderTarget> FilamentResourceManager::GetRenderTarget(
728  const RenderTargetHandle& id) {
729  return FindResource(id, render_targets_);
730 }
731 
732 std::weak_ptr<filament::IndirectLight>
734  return FindResource(id, ibls_);
735 }
736 
737 std::weak_ptr<filament::Skybox> FilamentResourceManager::GetSkybox(
738  const SkyboxHandle& id) {
739  return FindResource(id, skyboxes_);
740 }
741 
742 std::weak_ptr<filament::VertexBuffer> FilamentResourceManager::GetVertexBuffer(
743  const VertexBufferHandle& id) {
744  return FindResource(id, vertex_buffers_);
745 }
746 
747 std::weak_ptr<filament::IndexBuffer> FilamentResourceManager::GetIndexBuffer(
748  const IndexBufferHandle& id) {
749  return FindResource(id, index_buffers_);
750 }
751 
753  material_instances_.clear();
754  materials_.clear();
755  textures_.clear();
756  render_targets_.clear();
757  vertex_buffers_.clear();
758  index_buffers_.clear();
759  ibls_.clear();
760  skyboxes_.clear();
761 }
762 
764  if (kDefaultResources.count(id) > 0) {
766  "Trying to destroy default resource {}. Nothing will happen.",
767  id);
768  return;
769  }
770 
771  switch (id.type) {
773  DestroyResource(id, materials_);
774  break;
776  DestroyResource(id, material_instances_);
777  break;
778  case EntityType::Texture:
779  DestroyResource(id, textures_);
780  break;
782  DestroyResource(id, vertex_buffers_);
783  break;
785  DestroyResource(id, index_buffers_);
786  break;
787  case EntityType::Skybox:
788  DestroyResource(id, skyboxes_);
789  break;
791  DestroyResource(id, ibls_);
792  break;
794  DestroyResource(id, render_targets_);
795  break;
796 
797  default:
799  "Resource {} is not suited for destruction by "
800  "ResourceManager",
802  return;
803  }
804 
805  auto found = dependencies_.find(id);
806  if (found != dependencies_.end()) {
807  for (const auto& dependent : found->second) {
808  Destroy(dependent);
809  }
810 
811  dependencies_.erase(found);
812  }
813 }
814 
815 inline uint8_t maxLevelCount(uint32_t width, uint32_t height) {
816  auto maxdim = std::max(width, height);
817  uint8_t levels = static_cast<uint8_t>(std::ilogbf(float(maxdim)));
818  return std::max(1, levels + 1);
819 }
820 
821 filament::Texture* FilamentResourceManager::LoadTextureFromImage(
822  const std::shared_ptr<geometry::Image>& image, bool srgb) {
823  using namespace filament;
824 
825  auto retained_img_id = RetainImageForLoading(image);
826  auto texture_settings = GetSettingsFromImage(*image, srgb);
827  auto levels = maxLevelCount(texture_settings.texel_width,
828  texture_settings.texel_height);
829 
830  Texture::PixelBufferDescriptor pb(
831  image->data_.data(), image->data_.size(),
832  texture_settings.image_format, texture_settings.image_type,
833  FreeRetainedImage, (void*)retained_img_id);
834  auto texture = Texture::Builder()
835  .width(texture_settings.texel_width)
836  .height(texture_settings.texel_height)
837  .levels(levels)
838  .format(texture_settings.format)
839  .sampler(Texture::Sampler::SAMPLER_2D)
840  .build(engine_);
841 
842  texture->setImage(engine_, 0, std::move(pb));
843  texture->generateMipmaps(engine_);
844  return texture;
845 }
846 
847 filament::Texture* FilamentResourceManager::LoadTextureFromImage(
848  const t::geometry::Image& image, bool srgb) {
849  using namespace filament;
850 
851  auto texture_settings = GetSettingsFromImage(image, srgb);
852  auto levels = maxLevelCount(texture_settings.texel_width,
853  texture_settings.texel_height);
854 
855  const size_t image_bytes = image.GetRows() * image.GetCols() *
856  image.GetChannels() *
857  image.GetDtype().ByteSize();
858  if (image.GetDevice().GetType() == core::Device::DeviceType::CUDA) {
859  t::geometry::Image cpu_image = image.CPU();
860  auto* image_data = malloc(image_bytes);
861  memcpy(image_data, cpu_image.GetDataPtr(), image_bytes);
862  Texture::PixelBufferDescriptor pb(
863  image_data, image_bytes, texture_settings.image_format,
864  texture_settings.image_type, DeallocateBuffer);
865  auto texture = Texture::Builder()
866  .width(texture_settings.texel_width)
867  .height(texture_settings.texel_height)
868  .levels(levels)
869  .format(texture_settings.format)
870  .sampler(Texture::Sampler::SAMPLER_2D)
871  .build(engine_);
872  texture->setImage(engine_, 0, std::move(pb));
873  texture->generateMipmaps(engine_);
874  return texture;
875  } else {
876  Texture::PixelBufferDescriptor pb(image.GetDataPtr(), image_bytes,
877  texture_settings.image_format,
878  texture_settings.image_type);
879  auto texture = Texture::Builder()
880  .width(texture_settings.texel_width)
881  .height(texture_settings.texel_height)
882  .levels(levels)
883  .format(texture_settings.format)
884  .sampler(Texture::Sampler::SAMPLER_2D)
885  .build(engine_);
886  texture->setImage(engine_, 0, std::move(pb));
887  texture->generateMipmaps(engine_);
888  return texture;
889  }
890 }
891 
892 filament::Texture* FilamentResourceManager::LoadFilledTexture(
893  const Eigen::Vector3f& color, size_t dimension) {
894  auto image = std::make_shared<geometry::Image>();
895  image->Prepare(int(dimension), int(dimension), 3, 1);
896 
897  struct RGB {
898  std::uint8_t r, g, b;
899  };
900 
901  RGB c = {static_cast<uint8_t>(color(0) * 255.f),
902  static_cast<uint8_t>(color(1) * 255.f),
903  static_cast<uint8_t>(color(2) * 255.f)};
904 
905  auto data = reinterpret_cast<RGB*>(image->data_.data());
906  for (size_t i = 0; i < dimension * dimension; ++i) {
907  data[i] = c;
908  }
909 
910  auto texture = LoadTextureFromImage(image, false);
911  return texture;
912 }
913 
914 void FilamentResourceManager::LoadDefaults() {
915  // FIXME: Move to precompiled resource blobs
916  const std::string& resource_root = EngineInstance::GetResourcePath();
917 
918  const auto texture_path = resource_root + "/defaultTexture.png";
919  auto texture_img = io::CreateImageFromFile(texture_path);
920  auto texture = LoadTextureFromImage(texture_img, false);
921  textures_[kDefaultTexture] = BoxResource(texture, engine_);
922 
923  const auto colormap_path = resource_root + "/defaultGradient.png";
924  auto colormap_img = io::CreateImageFromFile(colormap_path);
925  auto color_map = LoadTextureFromImage(colormap_img, false);
926  textures_[kDefaultColorMap] = BoxResource(color_map, engine_);
927 
928  auto normal_map = LoadFilledTexture(Eigen::Vector3f(0.5, 0.5, 1.f), 1);
929  textures_[kDefaultNormalMap] = BoxResource(normal_map, engine_);
930 
931  const auto default_sampler =
934  const auto default_color = filament::math::float3{1.0f, 1.0f, 1.0f};
935  const auto default_color_alpha =
936  filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f};
937 
938  const auto lit_path = resource_root + "/defaultLit.filamat";
939  auto lit_mat = LoadMaterialFromFile(lit_path, engine_);
940  lit_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
941  default_color);
942  lit_mat->setDefaultParameter("baseRoughness", 0.7f);
943  lit_mat->setDefaultParameter("reflectance", 0.5f);
944  lit_mat->setDefaultParameter("baseMetallic", 0.f);
945  lit_mat->setDefaultParameter("clearCoat", 0.f);
946  lit_mat->setDefaultParameter("clearCoatRoughness", 0.f);
947  lit_mat->setDefaultParameter("anisotropy", 0.f);
948  lit_mat->setDefaultParameter("pointSize", 3.f);
949  lit_mat->setDefaultParameter("albedo", texture, default_sampler);
950  lit_mat->setDefaultParameter("ao_rough_metalMap", texture, default_sampler);
951  lit_mat->setDefaultParameter("normalMap", normal_map, default_sampler);
952  lit_mat->setDefaultParameter("reflectanceMap", texture, default_sampler);
953  // NOTE: Disabled to avoid Filament warning until shader is reworked to
954  // reduce sampler usage.
955  // lit_mat->setDefaultParameter("clearCoatMap", texture, default_sampler);
956  // lit_mat->setDefaultParameter("clearCoatRoughnessMap", texture,
957  // default_sampler);
958  lit_mat->setDefaultParameter("anisotropyMap", texture, default_sampler);
959  materials_[kDefaultLit] = BoxResource(lit_mat, engine_);
960 
961  const auto gaussian_path = resource_root + "/gaussianSplat.filamat";
962  auto gaussian_mat = LoadMaterialFromFile(gaussian_path, engine_);
963  gaussian_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
964  default_color);
965  gaussian_mat->setDefaultParameter("baseRoughness", 0.7f);
966  gaussian_mat->setDefaultParameter("reflectance", 0.5f);
967  gaussian_mat->setDefaultParameter("baseMetallic", 0.f);
968  gaussian_mat->setDefaultParameter("clearCoat", 0.f);
969  gaussian_mat->setDefaultParameter("clearCoatRoughness", 0.f);
970  gaussian_mat->setDefaultParameter("anisotropy", 0.f);
971  gaussian_mat->setDefaultParameter("pointSize", 3.f);
972  gaussian_mat->setDefaultParameter("albedo", texture, default_sampler);
973  gaussian_mat->setDefaultParameter("ao_rough_metalMap", texture,
974  default_sampler);
975  gaussian_mat->setDefaultParameter("normalMap", normal_map, default_sampler);
976  gaussian_mat->setDefaultParameter("reflectanceMap", texture,
977  default_sampler);
978 
979  gaussian_mat->setDefaultParameter("anisotropyMap", texture,
980  default_sampler);
981  materials_[kGaussianSplatShader] = BoxResource(gaussian_mat, engine_);
982 
983  const auto lit_trans_path =
984  resource_root + "/defaultLitTransparency.filamat";
985  auto lit_trans_mat = LoadMaterialFromFile(lit_trans_path, engine_);
986  lit_trans_mat->setDefaultParameter("baseColor",
987  filament::RgbaType::PREMULTIPLIED_sRGB,
988  default_color_alpha);
989  lit_trans_mat->setDefaultParameter("baseRoughness", 0.7f);
990  lit_trans_mat->setDefaultParameter("reflectance", 0.5f);
991  lit_trans_mat->setDefaultParameter("baseMetallic", 0.f);
992  lit_trans_mat->setDefaultParameter("clearCoat", 0.f);
993  lit_trans_mat->setDefaultParameter("clearCoatRoughness", 0.f);
994  lit_trans_mat->setDefaultParameter("anisotropy", 0.f);
995  lit_trans_mat->setDefaultParameter("pointSize", 3.f);
996  lit_trans_mat->setDefaultParameter("albedo", texture, default_sampler);
997  lit_trans_mat->setDefaultParameter("ao_rough_metalMap", texture,
998  default_sampler);
999  lit_trans_mat->setDefaultParameter("normalMap", normal_map,
1000  default_sampler);
1001  lit_trans_mat->setDefaultParameter("reflectanceMap", texture,
1002  default_sampler);
1003  // NOTE: Disabled to avoid Filament warning until shader is reworked to
1004  // reduce sampler usage.
1005  // lit_trans_mat->setDefaultParameter("clearCoatMap", texture,
1006  // default_sampler);
1007  // lit_trans_mat->setDefaultParameter("clearCoatRoughnessMap", texture,
1008  // default_sampler);
1009  lit_trans_mat->setDefaultParameter("anisotropyMap", texture,
1010  default_sampler);
1011  materials_[kDefaultLitWithTransparency] =
1012  BoxResource(lit_trans_mat, engine_);
1013 
1014  const auto lit_ssr_path = resource_root + "/defaultLitSSR.filamat";
1015  auto lit_ssr_mat = LoadMaterialFromFile(lit_ssr_path, engine_);
1016  lit_ssr_mat->setDefaultParameter("baseColor",
1017  filament::RgbaType::PREMULTIPLIED_sRGB,
1018  default_color_alpha);
1019  lit_ssr_mat->setDefaultParameter("baseRoughness", 0.7f);
1020  lit_ssr_mat->setDefaultParameter("reflectance", 0.5f);
1021  lit_ssr_mat->setDefaultParameter("baseMetallic", 0.f);
1022  lit_ssr_mat->setDefaultParameter("clearCoat", 0.f);
1023  lit_ssr_mat->setDefaultParameter("clearCoatRoughness", 0.f);
1024  lit_ssr_mat->setDefaultParameter("anisotropy", 0.f);
1025  lit_ssr_mat->setDefaultParameter("thickness", 0.5f);
1026  lit_ssr_mat->setDefaultParameter("transmission", 1.f);
1027  lit_ssr_mat->setDefaultParameter("absorption",
1028  filament::math::float3(0.f, 0.f, 0.f));
1029  lit_ssr_mat->setDefaultParameter("pointSize", 3.f);
1030  lit_ssr_mat->setDefaultParameter("albedo", texture, default_sampler);
1031  lit_ssr_mat->setDefaultParameter("ao_rough_metalMap", texture,
1032  default_sampler);
1033  lit_ssr_mat->setDefaultParameter("normalMap", normal_map, default_sampler);
1034  lit_ssr_mat->setDefaultParameter("reflectanceMap", texture,
1035  default_sampler);
1036  materials_[kDefaultLitSSR] = BoxResource(lit_ssr_mat, engine_);
1037 
1038  const auto unlit_path = resource_root + "/defaultUnlit.filamat";
1039  auto unlit_mat = LoadMaterialFromFile(unlit_path, engine_);
1040  unlit_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
1041  default_color);
1042  unlit_mat->setDefaultParameter("pointSize", 3.f);
1043  unlit_mat->setDefaultParameter("albedo", texture, default_sampler);
1044  unlit_mat->setDefaultParameter("srgbColor", 0.f);
1045  materials_[kDefaultUnlit] = BoxResource(unlit_mat, engine_);
1046 
1047  const auto unlit_trans_path =
1048  resource_root + "/defaultUnlitTransparency.filamat";
1049  auto unlit_trans_mat = LoadMaterialFromFile(unlit_trans_path, engine_);
1050  unlit_trans_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
1051  default_color);
1052  unlit_trans_mat->setDefaultParameter("pointSize", 3.f);
1053  unlit_trans_mat->setDefaultParameter("albedo", texture, default_sampler);
1054  materials_[kDefaultUnlitWithTransparency] =
1055  BoxResource(unlit_trans_mat, engine_);
1056 
1057  const auto depth_path = resource_root + "/depth.filamat";
1058  auto depth_mat = LoadMaterialFromFile(depth_path, engine_);
1059  depth_mat->setDefaultParameter("pointSize", 3.f);
1060  materials_[kDefaultDepthShader] = BoxResource(depth_mat, engine_);
1061 
1062  const auto gradient_path = resource_root + "/unlitGradient.filamat";
1063  auto gradient_mat = LoadMaterialFromFile(gradient_path, engine_);
1064  gradient_mat->setDefaultParameter("pointSize", 3.f);
1065  materials_[kDefaultUnlitGradientShader] =
1066  BoxResource(gradient_mat, engine_);
1067 
1068  // NOTE: Legacy. Can be removed soon.
1069  const auto hdepth = CreateMaterial(ResourceLoadRequest(depth_path.data()));
1070  auto depth_mat_inst = materials_[hdepth];
1071  depth_mat_inst->setDefaultParameter("pointSize", 3.f);
1072  material_instances_[kDepthMaterial] =
1073  BoxResource(depth_mat_inst->createInstance(), engine_);
1074 
1075  const auto normals_path = resource_root + "/normals.filamat";
1076  auto normals_mat = LoadMaterialFromFile(normals_path, engine_);
1077  normals_mat->setDefaultParameter("pointSize", 3.f);
1078  materials_[kDefaultNormalShader] = BoxResource(normals_mat, engine_);
1079 
1080  // NOTE: Legacy. Can be removed soon.
1081  const auto hnormals =
1082  CreateMaterial(ResourceLoadRequest(normals_path.data()));
1083  auto normals_mat_inst = materials_[hnormals];
1084  normals_mat_inst->setDefaultParameter("pointSize", 3.f);
1085  material_instances_[kNormalsMaterial] =
1086  BoxResource(normals_mat_inst->createInstance(), engine_);
1087 
1088  const auto colormap_map_path = resource_root + "/colorMap.filamat";
1089  const auto hcolormap_mat =
1090  CreateMaterial(ResourceLoadRequest(colormap_map_path.data()));
1091  auto colormap_mat = materials_[hcolormap_mat];
1092  auto colormap_mat_inst = colormap_mat->createInstance();
1093  colormap_mat_inst->setParameter("colorMap", color_map, default_sampler);
1094  material_instances_[kColorMapMaterial] =
1095  BoxResource(colormap_mat_inst, engine_);
1096 
1097  const auto solid_path = resource_root + "/unlitSolidColor.filamat";
1098  auto solid_mat = LoadMaterialFromFile(solid_path, engine_);
1099  solid_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
1100  {0.5f, 0.5f, 0.5f});
1101  materials_[kDefaultUnlitSolidColorShader] = BoxResource(solid_mat, engine_);
1102 
1103  const auto bg_path = resource_root + "/unlitBackground.filamat";
1104  auto bg_mat = LoadMaterialFromFile(bg_path, engine_);
1105  bg_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
1106  {1.0f, 1.0f, 1.0f});
1107  bg_mat->setDefaultParameter("albedo", texture, default_sampler);
1108  bg_mat->setDefaultParameter("aspectRatio", 0.0f);
1109  bg_mat->setDefaultParameter("yOrigin", 0.0f);
1110  materials_[kDefaultUnlitBackgroundShader] = BoxResource(bg_mat, engine_);
1111 
1112  const auto inf_path = resource_root + "/infiniteGroundPlane.filamat";
1113  auto inf_mat = LoadMaterialFromFile(inf_path, engine_);
1114  inf_mat->setDefaultParameter("baseColor", filament::RgbType::sRGB,
1115  {0.4f, 0.4f, 0.4f});
1116  inf_mat->setDefaultParameter("axis", 0.0f);
1117  materials_[kInfinitePlaneShader] = BoxResource(inf_mat, engine_);
1118 
1119  const auto line_path = resource_root + "/unlitLine.filamat";
1120  auto line_mat = LoadMaterialFromFile(line_path, engine_);
1121  line_mat->setDefaultParameter("baseColor", filament::RgbaType::LINEAR,
1122  default_color_alpha);
1123  line_mat->setDefaultParameter("emissiveColor",
1124  filament::math::float4(0.0, 0.0, 0.0, 1.0f));
1125  line_mat->setDefaultParameter("lineWidth", 1.f);
1126  materials_[kDefaultLineShader] = BoxResource(line_mat, engine_);
1127 
1128  const auto poffset_path = resource_root + "/unlitPolygonOffset.filamat";
1129  auto poffset_mat = LoadMaterialFromFile(poffset_path, engine_);
1130  materials_[kDefaultUnlitPolygonOffsetShader] =
1131  BoxResource(poffset_mat, engine_);
1132 }
1133 
1134 } // namespace rendering
1135 } // namespace visualization
1136 } // namespace cloudViewer
std::shared_ptr< core::Tensor > image
filament::Texture::InternalFormat format
filament::Texture::Format image_format
std::uint32_t texel_width
filament::Texture::Type image_type
std::uint32_t texel_height
int width
int size
int height
char type
math::float4 color
std::vector< UVAtlasVertex > vb
bool copy
Definition: VtkUtils.cpp:74
The Image class stores image with customizable width, height, num of channels and bytes per channel.
Definition: Image.h:33
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
void * GetDataPtr()
Get raw buffer of the Image data.
Definition: Image.h:118
static filament::TextureSampler SamplerFromSamplerParameters(const TextureSamplerParameters &sampler_config)
IndexBufferHandle CreateIndexBuffer(size_t indices_count, size_t index_stride)
TextureHandle CreateTextureFilled(const Eigen::Vector3f &color, size_t dimension)
std::weak_ptr< filament::Material > GetMaterial(const MaterialHandle &id)
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::RenderTarget > GetRenderTarget(const RenderTargetHandle &id)
std::weak_ptr< filament::Texture > GetTexture(const TextureHandle &id)
std::weak_ptr< filament::Skybox > GetSkybox(const SkyboxHandle &id)
RenderTargetHandle CreateRenderTarget(TextureHandle color, TextureHandle depth)
std::weak_ptr< filament::VertexBuffer > GetVertexBuffer(const VertexBufferHandle &id)
std::weak_ptr< filament::MaterialInstance > GetMaterialInstance(const MaterialInstanceHandle &id)
SkyboxHandle CreateSkybox(const ResourceLoadRequest &request)
MaterialInstanceHandle CreateMaterialInstance(const MaterialHandle &id)
IndirectLightHandle CreateIndirectLight(const ResourceLoadRequest &request)
MaterialHandle CreateMaterial(const void *material_data, size_t data_size)
VertexBufferHandle AddVertexBuffer(filament::VertexBuffer *vertex_buffer)
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
#define LogDebug(...)
Definition: Logging.h:90
int max(int a, int b)
Definition: cutil_math.h:48
std::shared_ptr< geometry::Image > CreateImageFromFile(const std::string &filename)
static const std::string path
Definition: PointCloud.cpp:59
bool FReadToBuffer(const std::string &path, std::vector< char > &bytes, std::string *errorStr)
Definition: FileSystem.cpp:678
REHandle< EntityType::Texture > TextureHandle
REHandle< EntityType::Material > MaterialHandle
static const std::unordered_set< REHandle_abstract > kDefaultResources
REHandle< EntityType::MaterialInstance > MaterialInstanceHandle
uint8_t maxLevelCount(uint32_t width, uint32_t height)
Generic file read and write utility for python interface.
static const char * TypeToString(EntityType type)