ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
FilamentRenderer.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 <utils/Entity.h>
11 
12 // 4068: Filament has some clang-specific vectorizing pragma's that MSVC flags
13 // 4146: Filament's utils/algorithm.h utils::details::ctz() tries to negate
14 // an unsigned int.
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 <backend/PixelBufferDescriptor.h>
26 #include <filament/Engine.h>
27 #include <filament/LightManager.h>
28 #include <filament/RenderableManager.h>
29 #include <filament/Renderer.h>
30 #include <filament/Scene.h>
31 #include <filament/SwapChain.h>
32 
33 #ifdef _MSC_VER
34 #pragma warning(pop)
35 #endif // _MSC_VER
36 
37 #include <Logging.h>
38 
39 #include "core/Tensor.h"
46 
47 namespace cloudViewer {
48 namespace visualization {
49 namespace rendering {
50 
51 FilamentRenderer::FilamentRenderer(filament::Engine& engine,
52  void* native_drawable,
53  FilamentResourceManager& resource_mgr)
54  : engine_(engine), resource_mgr_(resource_mgr) {
55  swap_chain_ = engine_.createSwapChain(native_drawable,
56  filament::SwapChain::CONFIG_READABLE);
57  renderer_ = engine_.createRenderer();
58 
59  materials_modifier_ = std::make_unique<FilamentMaterialModifier>();
60 }
61 
62 FilamentRenderer::FilamentRenderer(filament::Engine& engine,
63  int width,
64  int height,
65  FilamentResourceManager& resource_mgr)
66  : engine_(engine), resource_mgr_(resource_mgr) {
67  swap_chain_ = engine_.createSwapChain(width, height,
68  filament::SwapChain::CONFIG_READABLE);
69  renderer_ = engine_.createRenderer();
70 
71  materials_modifier_ = std::make_unique<FilamentMaterialModifier>();
72 }
73 
75  scenes_.clear();
76 
77  engine_.destroy(renderer_);
78  engine_.destroy(swap_chain_);
79 }
80 
82  auto handle = SceneHandle::Next();
83  scenes_[handle] =
84  std::make_unique<FilamentScene>(engine_, resource_mgr_, *this);
85 
86  return handle;
87 }
88 
90  auto found = scenes_.find(id);
91  if (found != scenes_.end()) {
92  return found->second.get();
93  }
94 
95  return nullptr;
96 }
97 
99  scenes_.erase(id);
100 }
101 
102 void FilamentRenderer::SetClearColor(const Eigen::Vector4f& color) {
103  filament::Renderer::ClearOptions co;
104  co.clearColor.r = color.x();
105  co.clearColor.g = color.y();
106  co.clearColor.b = color.z();
107  co.clearColor.a = color.w();
108  co.clear = false;
109  co.discard = true;
110  renderer_->setClearOptions(co);
111 }
112 
113 void FilamentRenderer::SetOnAfterDraw(std::function<void()> callback) {
114  on_after_draw_ = callback;
115 }
116 
118  void* native_win = swap_chain_->getNativeWindow();
119  engine_.destroy(swap_chain_);
120  swap_chain_ = engine_.createSwapChain(native_win);
121 }
122 
124  engine_.destroy(swap_chain_);
125  swap_chain_ = engine_.createSwapChain(width, height,
126  filament::SwapChain::CONFIG_READABLE);
127 }
128 
130  // We will complete render to buffer requests first
131  if (!buffer_renderers_.empty()) {
132  for (auto& br : buffer_renderers_) {
133  if (br->pending_) {
134  br->Render();
135  }
136  }
137 
138  // Force the engine to render, otherwise it sometimes doesn't
139  // render for a while, especially on Linux. This means the read
140  // pixels callback does not get called until sometime later,
141  // possibly several draws later.
142  engine_.flushAndWait();
143 
144  buffer_renderers_.clear(); // Cleanup
145  }
146 
147  frame_started_ = renderer_->beginFrame(swap_chain_);
148 }
149 
151  if (frame_started_) {
152  // Draw 3D scenes into textures
153  for (const auto& pair : scenes_) {
154  pair.second->Draw(*renderer_);
155  }
156 
157  // Draw the UI. This should come after the 3D scene(s), as SceneWidget
158  // will draw the textures as an image, and this way we will have the
159  // current frame's content from above.
160  if (gui_scene_) {
161  gui_scene_->Draw(*renderer_);
162  }
163 
164  if (on_after_draw_) {
165  on_after_draw_();
166  }
167  }
168 }
169 
171  if (frame_started_) {
172  renderer_->endFrame();
173  if (needs_wait_after_draw_) {
174  engine_.flushAndWait();
175  needs_wait_after_draw_ = false;
176  }
177  }
178 }
179 
180 namespace {
181 
182 struct UserData {
183  std::function<void(std::shared_ptr<core::Tensor>)> callback;
184  std::shared_ptr<core::Tensor> image;
185 
186  UserData(std::function<void(std::shared_ptr<core::Tensor>)> cb,
187  std::shared_ptr<core::Tensor> img)
188  : callback(cb), image(img) {}
189 };
190 
191 void ReadPixelsCallback(void*, size_t, void* user) {
192  auto* user_data = static_cast<UserData*>(user);
193  user_data->callback(user_data->image);
194  delete user_data;
195 }
196 
197 } // namespace
198 
200  int width,
201  int height,
202  std::function<void(std::shared_ptr<core::Tensor>)> callback) {
203  core::SizeVector shape{height, width, 3};
204  core::Dtype dtype = core::UInt8;
205  int64_t nbytes = shape.NumElements() * dtype.ByteSize();
206 
207  auto image = std::make_shared<core::Tensor>(shape, dtype);
208  auto* user_data = new UserData(callback, image);
209 
210  using namespace filament;
211  using namespace backend;
212 
213  PixelBufferDescriptor pd(image->GetDataPtr(), nbytes, PixelDataFormat::RGB,
214  PixelDataType::UBYTE, ReadPixelsCallback,
215  user_data);
216  renderer_->readPixels(0, 0, width, height, std::move(pd));
217  needs_wait_after_draw_ = true;
218 }
219 
221  const ResourceLoadRequest& request) {
222  return resource_mgr_.CreateMaterial(request);
223 }
224 
226  const MaterialHandle& material) {
227  return resource_mgr_.CreateMaterialInstance(material);
228 }
229 
231  materials_modifier_->Reset();
232 
233  auto instance_id = resource_mgr_.CreateMaterialInstance(id);
234 
235  if (instance_id) {
236  auto w_material_instance =
237  resource_mgr_.GetMaterialInstance(instance_id);
238  materials_modifier_->Init(w_material_instance.lock(), instance_id);
239  } else {
241  "Failed to create material instance for material handle {}.",
242  id);
243  }
244 
245  return *materials_modifier_;
246 }
247 
249  const MaterialInstanceHandle& id) {
250  materials_modifier_->Reset();
251 
252  auto w_material_instance = resource_mgr_.GetMaterialInstance(id);
253  if (!w_material_instance.expired()) {
254  materials_modifier_->Init(w_material_instance.lock(), id);
255  } else {
257  "Failed to modify material instance: unknown instance handle "
258  "{}.",
259  id);
260  }
261 
262  return *materials_modifier_;
263 }
264 
266  const MaterialInstanceHandle& id) {
267  resource_mgr_.Destroy(id);
268 }
269 
271  bool srgb) {
272  if (request.path_.empty()) {
273  request.error_callback_(request, -1,
274  "Texture can be loaded only from file");
275  return {};
276  }
277 
278  return resource_mgr_.CreateTexture(request.path_.data(), srgb);
279 }
280 
282  TextureHandle texture,
283  const std::shared_ptr<geometry::Image> image,
284  bool srgb) {
285  return resource_mgr_.UpdateTexture(texture, image, srgb);
286 }
287 
289  const t::geometry::Image& image,
290  bool srgb) {
291  return resource_mgr_.UpdateTexture(texture, image, srgb);
292 }
293 
295  resource_mgr_.Destroy(id);
296 }
297 
299  const ResourceLoadRequest& request) {
300  if (request.path_.empty()) {
301  request.error_callback_(
302  request, -1, "Indirect lights can be loaded only from files");
303  return {};
304  }
305 
306  return resource_mgr_.CreateIndirectLight(request);
307 }
308 
310  resource_mgr_.Destroy(id);
311 }
312 
314  if (request.path_.empty()) {
315  request.error_callback_(request, -1,
316  "Skyboxes can be loaded only from files");
317  return {};
318  }
319 
320  return resource_mgr_.CreateSkybox(request);
321 }
322 
324  resource_mgr_.Destroy(id);
325 }
326 
327 std::shared_ptr<RenderToBuffer> FilamentRenderer::CreateBufferRenderer() {
328  auto renderer = std::make_shared<FilamentRenderToBuffer>(engine_);
329  buffer_renderers_.insert(renderer);
330  return renderer;
331 }
332 
334  auto found = scenes_.find(id);
335  // TODO: assert(found != scenes_.end())
336  if (found != scenes_.end()) {
337  if (gui_scene_ != nullptr) {
339  "FilamentRenderer::ConvertToGuiScene: guiScene_ is already "
340  "set");
341  }
342  gui_scene_ = std::move(found->second);
343  scenes_.erase(found);
344  }
345 }
346 
348  const std::shared_ptr<geometry::Image> image, bool srgb) {
349  return resource_mgr_.CreateTexture(image, srgb);
350 }
351 
353  bool srgb) {
354  return resource_mgr_.CreateTexture(image, srgb);
355 }
356 
357 // void FilamentRenderer::OnBufferRenderDestroyed(FilamentRenderToBuffer*
358 // render) {
359 // buffer_renderers_.erase(render);
360 //}
361 
362 } // namespace rendering
363 } // namespace visualization
364 } // namespace cloudViewer
std::shared_ptr< core::Tensor > image
std::function< void(std::shared_ptr< core::Tensor >)> callback
int width
int height
math::float4 color
int64_t ByteSize() const
Definition: Dtype.h:59
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
std::shared_ptr< visualization::rendering::RenderToBuffer > CreateBufferRenderer() override
MaterialInstanceHandle AddMaterialInstance(const MaterialHandle &material) override
IndirectLightHandle AddIndirectLight(const ResourceLoadRequest &request) override
virtual void SetClearColor(const Eigen::Vector4f &color) override
FilamentRenderer(filament::Engine &engine, void *native_drawable, FilamentResourceManager &resource_mgr)
void RemoveMaterialInstance(const MaterialInstanceHandle &id) override
void RemoveIndirectLight(const IndirectLightHandle &id) override
void RemoveTexture(const TextureHandle &id) override
bool UpdateTexture(TextureHandle texture, const std::shared_ptr< geometry::Image > image, bool srgb) override
void RemoveSkybox(const SkyboxHandle &id) override
Scene * GetScene(const SceneHandle &id) const override
TextureHandle AddTexture(const ResourceLoadRequest &request, bool srgb=false) override
SkyboxHandle AddSkybox(const ResourceLoadRequest &request) override
void SetOnAfterDraw(std::function< void()> callback) override
MaterialModifier & ModifyMaterial(const MaterialHandle &id) override
MaterialHandle AddMaterial(const ResourceLoadRequest &request) override
void RequestReadPixels(int width, int height, std::function< void(std::shared_ptr< core::Tensor >)> callback) override
void UpdateBitmapSwapChain(int width, int height) override
#define LogWarning(...)
Definition: Logging.h:72
constexpr const char * UserData
Definition: LasDetails.h:69
const Dtype UInt8
Definition: Dtype.cpp:48
void ReadPixelsCallback(void *buffer, size_t buffer_size, void *user)
Generic file read and write utility for python interface.