Blue Brain BioExplorer
Scene.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015-2024, EPFL/Blue Brain Project
3  *
4  * The Blue Brain BioExplorer is a tool for scientists to extract and analyse
5  * scientific data from visualization
6  *
7  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
8  *
9  * This library is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU Lesser General Public License version 3.0 as published
11  * by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this library; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include "Scene.h"
24 
31 
33 
35 
36 #include <fstream>
37 #include <mutex>
38 
39 namespace
40 {
41 template <typename T, typename U = T> // U seems to be needed when getID is a
42  // member function of a base of T.
43 std::shared_ptr<T> _find(const std::vector<std::shared_ptr<T>>& list, const size_t id,
44  size_t (U::*getID)() const = &T::getID)
45 {
46  auto i = std::find_if(list.begin(), list.end(), [id, getID](auto x) { return id == ((*x).*getID)(); });
47  return i == list.end() ? std::shared_ptr<T>{} : *i;
48 }
49 
50 template <typename T, typename U = T>
51 std::shared_ptr<T> _remove(std::vector<std::shared_ptr<T>>& list, const size_t id,
52  size_t (U::*getID)() const = &T::getID)
53 {
54  auto i = std::find_if(list.begin(), list.end(), [id, getID](auto x) { return id == ((*x).*getID)(); });
55  if (i == list.end())
56  return std::shared_ptr<T>{};
57  auto result = *i;
58  list.erase(i);
59  return result;
60 }
61 } // namespace
62 
63 namespace core
64 {
65 Scene::Scene(AnimationParameters& animationParameters, GeometryParameters& geometryParameters,
66  VolumeParameters& volumeParameters, FieldParameters& fieldParameters)
67  : _animationParameters(animationParameters)
68  , _geometryParameters(geometryParameters)
69  , _volumeParameters(volumeParameters)
70  , _fieldParameters(fieldParameters)
71 {
72 }
73 
74 void Scene::copyFrom(const Scene& rhs)
75 {
76  if (this == &rhs)
77  return;
78 
79  {
80  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
81  std::shared_lock<std::shared_timed_mutex> rhsLock(rhs._modelMutex);
82 
83  _modelDescriptors.clear();
84  _modelDescriptors.reserve(rhs._modelDescriptors.size());
85  for (const auto& modelDesc : rhs._modelDescriptors)
86  _modelDescriptors.push_back(modelDesc->clone(createModel()));
87  }
88  computeBounds();
89 
91  _backgroundMaterial->markModified();
92 
95 
96  markModified();
97 }
98 
99 void Scene::commit() {}
100 
101 size_t Scene::getSizeInBytes() const
102 {
103  auto lock = acquireReadAccess();
104  size_t sizeInBytes = 0;
105  for (auto modelDescriptor : _modelDescriptors)
106  sizeInBytes += modelDescriptor->getModel().getSizeInBytes();
107  return sizeInBytes;
108 }
109 
110 size_t Scene::getNumModels() const
111 {
112  auto lock = acquireReadAccess();
113  return _modelDescriptors.size();
114 }
115 
116 size_t Scene::addModel(ModelDescriptorPtr modelDescriptor)
117 {
118  auto& model = modelDescriptor->getModel();
119  if (model.empty())
120  throw std::runtime_error("Empty models not supported.");
121 
122  const auto defaultBVHFlags = _geometryParameters.getDefaultBVHFlags();
123 
124  model.setBVHFlags(defaultBVHFlags);
125  model.buildBoundingBox();
126 
127  // Since models can be added concurrently we check if that is supported
129  model.commitGeometry();
130 
131  {
132  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
133  modelDescriptor->setModelID(_modelID++);
134  _modelDescriptors.push_back(modelDescriptor);
135 
136  // add default instance of this model to render something
137  if (modelDescriptor->getInstances().empty())
138  modelDescriptor->addInstance({true, true, modelDescriptor->getTransformation()});
139  }
140 
141  computeBounds();
142  markModified();
143 
144  return modelDescriptor->getModelID();
145 }
146 
147 bool Scene::removeModel(const size_t id)
148 {
149  ModelDescriptorPtr model = nullptr;
150 
152  {
153  {
154  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
155  model = _remove(_modelDescriptors, id, &ModelDescriptor::getModelID);
156  }
157  if (model)
158  model->callOnRemoved();
159  }
160  else
161  {
162  {
163  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
164  model = _find(_modelDescriptors, id, &ModelDescriptor::getModelID);
165  }
166  if (model)
167  model->markForRemoval();
168  }
169 
170  if (model)
171  {
172  markModified();
173  return true;
174  }
175 
176  return false;
177 }
178 
179 ModelDescriptorPtr Scene::getModel(const size_t id) const
180 {
181  auto lock = acquireReadAccess();
183 }
184 
185 bool Scene::empty() const
186 {
187  auto lock = acquireReadAccess();
188  for (auto modelDescriptor : _modelDescriptors)
189  if (!modelDescriptor->getModel().empty())
190  return false;
191  return true;
192 }
193 
194 size_t Scene::addClipPlane(const Plane& plane)
195 {
196  auto clipPlane = std::make_shared<ClipPlane>(plane);
197  clipPlane->onModified([&](const BaseObject&) { markModified(false); });
198  _clipPlanes.emplace_back(std::move(clipPlane));
199  markModified();
200  return _clipPlanes.back()->getID();
201 }
202 
203 ClipPlanePtr Scene::getClipPlane(const size_t id) const
204 {
205  return _find(_clipPlanes, id);
206 }
207 
208 void Scene::removeClipPlane(const size_t id)
209 {
210  if (_remove(_clipPlanes, id))
211  markModified();
212 }
213 
215 {
216  const auto& loader = _loaderRegistry.getSuitableLoader("", blob.type, params.getLoaderName());
217 
218  // HACK: Add loader name in properties for archive loader
219  auto propCopy = params.getLoaderProperties();
220  propCopy.setProperty({"loaderName", params.getLoaderName()});
221  auto modelDescriptor = loader.importFromBlob(std::move(blob), cb, propCopy);
222  if (!modelDescriptor)
223  throw std::runtime_error("No model returned by loader");
224  *modelDescriptor = params;
225  addModel(modelDescriptor);
226  return modelDescriptor;
227 }
228 
229 ModelDescriptorPtr Scene::loadModel(const std::string& path, const ModelParams& params, LoaderProgress cb)
230 {
231  const auto& loader = _loaderRegistry.getSuitableLoader(path, "", params.getLoaderName());
232  // HACK: Add loader name in properties for archive loader
233  auto propCopy = params.getLoaderProperties();
234  propCopy.setProperty({"loaderName", params.getLoaderName()});
235  auto modelDescriptor = loader.importFromStorage(path, cb, propCopy);
236  if (!modelDescriptor)
237  throw std::runtime_error("No model returned by loader");
238  *modelDescriptor = params;
239  addModel(modelDescriptor);
240  return modelDescriptor;
241 }
242 
243 void Scene::visitModels(const std::function<void(Model&)>& functor)
244 {
245  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
246  for (const auto& modelDescriptor : _modelDescriptors)
247  functor(modelDescriptor->getModel());
248 }
249 
251 {
252  CORE_INFO("Building default Cornell Box scene");
253 
254  auto model = createModel();
255 #if 0
256  const Vector3f WHITE = {1.f, 1.f, 1.f};
257 
258  const Vector3f positions[8] = {
259  {0.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, // 6--------7
260  {0.f, 1.f, 0.f}, // /| /|
261  {1.f, 1.f, 0.f}, // 2--------3 |
262  {0.f, 0.f, 1.f}, // | | | |
263  {1.f, 0.f, 1.f}, // | 4------|-5
264  {0.f, 1.f, 1.f}, // |/ |/
265  {1.f, 1.f, 1.f} // 0--------1
266  };
267 
268  const uint16_t indices[6][6] = {
269  {5, 4, 6, 6, 7, 5}, // Front
270  {7, 5, 1, 1, 3, 7}, // Right
271  {3, 1, 0, 0, 2, 3}, // Back
272  {2, 0, 4, 4, 6, 2}, // Left
273  {0, 1, 5, 5, 4, 0}, // Bottom
274  {7, 3, 2, 2, 6, 7} // Top
275  };
276 
277  const Vector3f colors[6] = {{0.8f, 0.8f, 0.8f}, {1.f, 0.f, 0.f}, {0.8f, 0.8f, 0.8f},
278  {0.f, 1.f, 0.f}, {0.8f, 0.8f, 0.8f}, {0.8f, 0.8f, 0.8f}};
279 
280  size_t materialId = 0;
281  for (size_t i = 1; i < 6; ++i)
282  {
283  // Cornell box
284  auto material = model->createMaterial(materialId, "wall_" + std::to_string(materialId));
285  material->setDiffuseColor(colors[i]);
286  material->setSpecularColor(WHITE);
287  material->setSpecularExponent(10.f);
288  material->setReflectionIndex(i == 4 ? 0.2f : 0.f);
289  material->setGlossiness(i == 4 ? 0.9f : 1.f);
290  material->setOpacity(1.f);
291 
292  auto& triangleMesh = model->getTriangleMeshes()[materialId];
293  for (size_t j = 0; j < 6; ++j)
294  {
295  const auto position = positions[indices[i][j]];
296  triangleMesh.vertices.push_back(position);
297  }
298  triangleMesh.indices.push_back(Vector3ui(0, 1, 2));
299  triangleMesh.indices.push_back(Vector3ui(3, 4, 5));
300  ++materialId;
301  }
302 
303  {
304  // Sphere
305  auto material = model->createMaterial(materialId, "sphere");
306  material->setOpacity(0.5f);
307  material->setRefractionIndex(1.05f);
308  material->setReflectionIndex(0.1f);
309  material->setDiffuseColor(WHITE);
310  material->setSpecularColor(WHITE);
311  material->setSpecularExponent(100.f);
312  material->setShadingMode(MaterialShadingMode::diffuse);
313  model->addSphere(materialId, {{0.25f, 0.26f, 0.30f}, 0.25f});
314  ++materialId;
315  }
316 
317  {
318  // Cylinder
319  auto material = model->createMaterial(materialId, "cylinder");
320  material->setDiffuseColor({0.1f, 0.1f, 0.8f});
321  material->setSpecularColor(WHITE);
322  material->setSpecularExponent(10.f);
323  material->setShadingMode(MaterialShadingMode::diffuse);
324  model->addCylinder(materialId, {{0.25f, 0.126f, 0.75f}, {0.75f, 0.126f, 0.75f}, 0.125f});
325  ++materialId;
326  }
327 
328  {
329  // Cone
330  auto material = model->createMaterial(materialId, "cone");
331  material->setReflectionIndex(0.8f);
332  material->setSpecularColor(WHITE);
333  material->setSpecularExponent(10.f);
334  material->setShadingMode(MaterialShadingMode::diffuse);
335  model->addCone(materialId, {{0.75f, 0.01f, 0.25f}, {0.75f, 0.5f, 0.25f}, 0.15f, 0.f});
336  ++materialId;
337  }
338 
339 /*
340  {
341  // Lamp
342  auto material = model->createMaterial(materialId, "lamp");
343  material->setDiffuseColor(WHITE);
344  material->setEmission(5.f);
345  const Vector3f lampInfo = {0.15f, 0.99f, 0.15f};
346  const Vector3f lampPositions[4] = {{0.5f - lampInfo.x, lampInfo.y, 0.5f - lampInfo.z},
347  {0.5f + lampInfo.x, lampInfo.y, 0.5f - lampInfo.z},
348  {0.5f + lampInfo.x, lampInfo.y, 0.5f + lampInfo.z},
349  {0.5f - lampInfo.x, lampInfo.y, 0.5f + lampInfo.z}};
350  auto& triangleMesh = model->getTriangleMeshes()[materialId];
351  for (size_t i = 0; i < 4; ++i)
352  triangleMesh.vertices.push_back(lampPositions[i]);
353  triangleMesh.indices.push_back(Vector3i(2, 1, 0));
354  triangleMesh.indices.push_back(Vector3i(0, 3, 2));
355  }
356 */
357 #else
358  for (size_t materialId = 0; materialId < 10; ++materialId)
359  {
360  auto material = model->createMaterial(materialId, "Material");
361  material->setOpacity(0.75f);
362  material->setRefractionIndex(1.5f);
363  material->setReflectionIndex(0.5f);
364  material->setDiffuseColor({materialId * 0.1f, 0.f, 1.f - materialId * 0.1f});
365  material->setSpecularColor({1.f, 1.f, 1.f});
366  material->setSpecularExponent(100.f);
367  material->setShadingMode(MaterialShadingMode::basic);
368  material->setEmission(materialId % 2 == 0 ? 0.5f : 0.f);
369  material->setCastUserData(true);
370 
371  model->addSphere(materialId, {{-50.f + materialId * 10.f, 0.f, 0.f}, 5.f});
372  model->addCylinder(materialId,
373  {{-50.f + materialId * 10.f, 0.f, 0.f}, {-50.f + materialId * 10.f, 20.f, 0.f}, 2.5f});
374  model->addCone(materialId,
375  {{-50.f + materialId * 10.f, 0.f, 0.f}, {-50.f + materialId * 10.f, -20.f, 0.f}, 2.5f, 0.f});
376  }
377 #endif
378 
379  addModel(std::make_shared<ModelDescriptor>(std::move(model), "Demo scene"));
380 }
381 
383 {
384  {
385  auto lock = acquireReadAccess();
386  for (auto modelDescriptors : _modelDescriptors)
387  modelDescriptors->getModel().setMaterialsColorMap(colorMap);
388  }
389  markModified();
390 }
391 
392 bool Scene::setEnvironmentMap(const std::string& envMap)
393 {
394  bool success = true;
395  if (envMap.empty())
396  {
398  _backgroundMaterial->clearTextures();
399  }
400  else
401  {
402  try
403  {
404  _backgroundMaterial->setTexture(envMap, TextureType::diffuse);
405  }
406  catch (const std::runtime_error& e)
407  {
408  CORE_DEBUG("Cannot load environment map: " << e.what());
409  _backgroundMaterial->clearTextures();
410  success = false;
411  }
412 
413  _loadIBLMaps(envMap);
414  }
415 
416  _updateValue(_environmentMap, success ? envMap : "");
417  if (_backgroundMaterial && _backgroundMaterial->isModified())
418  markModified();
419  return success;
420 }
421 
423 {
424  return !_environmentMap.empty();
425 }
426 
428 {
429  std::unique_lock<std::shared_timed_mutex> lock(_modelMutex);
430  _bounds.reset();
431  for (const auto& modelDescriptor : _modelDescriptors)
432  {
433  const auto& modelBounds = modelDescriptor->getModel().getBounds();
434  Transformation modelTransformation;
435  modelTransformation.setTranslation(modelBounds.getCenter());
436 
437  const auto modelHalfSize = modelBounds.getSize() / 2.0;
438 
439  Transformation finalTransformation = modelTransformation * modelDescriptor->getTransformation();
440  _bounds.merge(finalTransformation.getTranslation() - modelHalfSize);
441  _bounds.merge(finalTransformation.getTranslation() + modelHalfSize);
442  for (const auto& instance : modelDescriptor->getInstances())
443  {
444  finalTransformation = modelTransformation * instance.getTransformation();
445  _bounds.merge(finalTransformation.getTranslation() - modelHalfSize);
446  _bounds.merge(finalTransformation.getTranslation() + modelHalfSize);
447  }
448  }
449 
450  if (_bounds.isEmpty())
451  // If no model is enabled. return empty bounding box
452  _bounds.merge({0, 0, 0});
453 }
454 
455 void Scene::_loadIBLMaps(const std::string& envMap)
456 {
457  try
458  {
459  auto tex = _backgroundMaterial->getTexture(TextureType::diffuse);
460 
461  const auto path = fs::path(envMap).parent_path();
462  const auto basename = (path / fs::path(envMap).stem()).string();
463 
464  const std::string irradianceMap = basename + IRRADIANCE_MAP + ".hdr";
465  const std::string radianceMap = basename + RADIANCE_MAP + ".hdr";
466  const std::string brdfLUT = basename + BRDF_LUT + ".hdr";
467 
468  if (fs::exists(irradianceMap) && fs::exists(radianceMap) && fs::exists(brdfLUT))
469  {
470  _backgroundMaterial->setTexture(irradianceMap, TextureType::irradiance);
471  _backgroundMaterial->setTexture(radianceMap, TextureType::radiance);
472  _backgroundMaterial->setTexture(brdfLUT, TextureType::brdf_lut);
473  }
474  }
475  catch (...)
476  {
477  }
478 }
479 } // namespace core
void _updateValue(T &member, const T &newValue, const bool triggerCallback=true)
Definition: BaseObject.h:87
void markModified(const bool triggerCallback=true)
Definition: BaseObject.h:65
void reset()
Definition: MathTypes.h:82
void merge(const Box< T > &aabb)
Definition: MathTypes.h:64
bool isEmpty() const
Definition: MathTypes.h:88
const std::set< BVHFlag > & getDefaultBVHFlags() const
const Loader & getSuitableLoader(const std::string &filename, const std::string &filetype, const std::string &loaderName) const
PLATFORM_API size_t getModelID() const
Get the value of _modelID.
Definition: Model.h:149
The ModelParams class represents the parameters needed for initializing a model instance.
Definition: Model.h:178
PLATFORM_API const std::string & getLoaderName() const
getLoaderName gets the loader name of the model
Definition: Model.h:250
PLATFORM_API const PropertyMap & getLoaderProperties() const
getLoaderProperties gets the loader properties of the model
Definition: Model.h:256
The abstract Model class holds the geometry attached to an asset of the scene (mesh,...
Definition: Model.h:469
void setProperty(const Property &newProperty)
Definition: PropertyMap.h:307
Scene object This object contains collections of geometries, materials and light sources that are use...
Definition: Scene.h:43
ModelDescriptors _modelDescriptors
Definition: Scene.h:247
ClipPlanes _clipPlanes
Definition: Scene.h:251
PLATFORM_API size_t addModel(ModelDescriptorPtr model)
Adds a model to the scene.
Definition: Scene.cpp:116
virtual PLATFORM_API ModelPtr createModel() const =0
Factory method to create an engine-specific model.
PLATFORM_API bool setEnvironmentMap(const std::string &envMap)
Set a new environment map as the background image.
Definition: Scene.cpp:392
PLATFORM_API void computeBounds()
Compute the bounds of the geometry handled by the scene.
Definition: Scene.cpp:427
std::shared_timed_mutex _modelMutex
Definition: Scene.h:248
PLATFORM_API size_t getNumModels() const
Get the current number of models in the scene.
Definition: Scene.cpp:110
size_t _modelID
Definition: Scene.h:246
PLATFORM_API ModelDescriptorPtr loadModel(Blob &&blob, const ModelParams &params, LoaderProgress cb)
Load a model from the given blob.
Definition: Scene.cpp:214
LoaderRegistry _loaderRegistry
Definition: Scene.h:253
PLATFORM_API size_t getSizeInBytes() const
Get the current size in bytes of the loaded geometry.
Definition: Scene.cpp:101
PLATFORM_API size_t addClipPlane(const Plane &plane)
Add a clip plane to the scene.
Definition: Scene.cpp:194
GeometryParameters & _geometryParameters
Definition: Scene.h:240
std::string _environmentMap
Definition: Scene.h:244
Boxd _bounds
Definition: Scene.h:254
virtual PLATFORM_API void commit()
Called after scene-related changes have been made before rendering the scene.
Definition: Scene.cpp:99
LightManager _lightManager
Definition: Scene.h:250
PLATFORM_API void removeClipPlane(const size_t id)
Remove a clip plane by its ID, or no-op if not found.
Definition: Scene.cpp:208
PLATFORM_API void buildDefault()
Builds a default scene made of a Cornell box, a reflective cube, and a transparent sphere.
Definition: Scene.cpp:250
PLATFORM_API void visitModels(const std::function< void(Model &)> &functor)
Apply the given functor to every model in the scene.
Definition: Scene.cpp:243
PLATFORM_API void setMaterialsColorMap(MaterialsColorMap colorMap)
Initializes materials for all models in the scene.
Definition: Scene.cpp:382
PLATFORM_API ModelDescriptorPtr getModel(const size_t id) const
Get a model descriptor given its ID.
Definition: Scene.cpp:179
PLATFORM_API void copyFrom(const Scene &rhs)
Copy the scene from another scene.
Definition: Scene.cpp:74
PLATFORM_API auto acquireReadAccess() const
Definition: Scene.h:217
virtual bool supportsConcurrentSceneUpdates() const
Check whether this scene supports scene updates from any thread.
Definition: Scene.h:235
MaterialPtr _backgroundMaterial
Definition: Scene.h:243
void _loadIBLMaps(const std::string &envMap)
Definition: Scene.cpp:455
PLATFORM_API bool hasEnvironmentMap() const
Check if an environment map is currently set in the scene.
Definition: Scene.cpp:422
PLATFORM_API ClipPlanePtr getClipPlane(const size_t id) const
Get a clip plane by its ID.
Definition: Scene.cpp:203
PLATFORM_API bool removeModel(const size_t id)
Removes a model from the scene.
Definition: Scene.cpp:147
PLATFORM_API Scene(AnimationParameters &animationParameters, GeometryParameters &geometryParameters, VolumeParameters &volumeParameters, FieldParameters &fieldParameters)
Creates a scene object responsible for handling models, simulations and light sources.
Definition: Scene.cpp:65
PLATFORM_API bool empty() const
Checks whether the scene is empty.
Definition: Scene.cpp:185
Defines the translation, rotation and scale parameters to be applied to a scene asset.
const Vector3d & getTranslation() const
void setTranslation(const Vector3d &value)
glm::vec3 Vector3f
Definition: MathTypes.h:137
glm::vec< 3, uint32_t > Vector3ui
Definition: MathTypes.h:134
std::shared_ptr< ClipPlane > ClipPlanePtr
Definition: Types.h:121
MaterialsColorMap
Definition: Types.h:287
std::shared_ptr< ModelDescriptor > ModelDescriptorPtr
Definition: Types.h:112
std::array< double, 4 > Plane
Definition: Types.h:308
@ diffuse
Definition: CommonTypes.h:47
@ basic
Definition: CommonTypes.h:46
@ plane
Definition: CommonTypes.h:39
@ vector
Definition: CommonTypes.h:68
#define CORE_DEBUG(__msg)
Definition: Logs.h:38
#define CORE_INFO(__msg)
Definition: Logs.h:33
_SOURCES ArchiveLoader cpp list(APPEND ${NAME}_HEADERS ArchiveLoader.h) list(APPEND $
Definition: CMakeLists.txt:40