Lugdunum  0.1.0
GltfLoader.cpp
Go to the documentation of this file.
2 
3 #if defined(LUG_SYSTEM_ANDROID)
4  #include <android/asset_manager.h>
5 
7  #include <lug/Window/Window.hpp>
8 #endif
9 
10 #include <gltf2/Exceptions.hpp>
11 
18 
19 namespace lug {
20 namespace Graphics {
21 
22 GltfLoader::GltfLoader(Renderer& renderer): Loader(renderer) {}
23 
24 static void* getBufferViewData(const gltf2::Asset& asset, const gltf2::Accessor& accessor) {
25  const gltf2::BufferView& bufferView = asset.bufferViews[accessor.bufferView];
26  const gltf2::Buffer& buffer = asset.buffers[bufferView.buffer];
27 
28  if (!buffer.data) {
29  LUG_LOG.error("GltfLoader::createMesh Buffer data can't be null");
30  return nullptr;
31  }
32 
33  // TODO(nokitoo): handle uri
34  return buffer.data + bufferView.byteOffset + accessor.byteOffset;
35 }
36 
37 static uint32_t getAttributeSize(const gltf2::Accessor& accessor) {
38  uint32_t componentSize = 0;
39  switch (accessor.componentType) {
40  case gltf2::Accessor::ComponentType::Byte:
41  componentSize = sizeof(char);
42  break;
43  case gltf2::Accessor::ComponentType::UnsignedByte:
44  componentSize = sizeof(unsigned char);
45  break;
46  case gltf2::Accessor::ComponentType::Short:
47  componentSize = sizeof(short);
48  break;
49  case gltf2::Accessor::ComponentType::UnsignedShort:
50  componentSize = sizeof(unsigned short);
51  break;
52  case gltf2::Accessor::ComponentType::UnsignedInt:
53  componentSize = sizeof(unsigned int);
54  break;
55  case gltf2::Accessor::ComponentType::Float:
56  componentSize = sizeof(float);
57  break;
58  }
59 
60  // Mutliply the componentSize according to the type
61  switch (accessor.type) {
62  case gltf2::Accessor::Type::Scalar:
63  return componentSize;
64  case gltf2::Accessor::Type::Vec2:
65  return componentSize * 2;
66  case gltf2::Accessor::Type::Vec3:
67  return componentSize * 3;
68  case gltf2::Accessor::Type::Vec4:
69  return componentSize * 4;
70  case gltf2::Accessor::Type::Mat2:
71  return componentSize * 4;
72  case gltf2::Accessor::Type::Mat3:
73  return componentSize * 9;
74  case gltf2::Accessor::Type::Mat4:
75  return componentSize * 16;
76  }
77 
78  return componentSize;
79 }
80 
81 Resource::SharedPtr<Render::Texture> GltfLoader::createTexture(Renderer& renderer, const gltf2::Asset& asset, GltfLoader::LoadedAssets& loadedAssets, int32_t index) {
82  const gltf2::Texture& gltfTexture = asset.textures[index];
83 
84  if (loadedAssets.textures[index]) {
85  return loadedAssets.textures[index];
86  }
87 
88  Builder::Texture textureBuilder(renderer);
89 
90  if (gltfTexture.source != -1) {
91  // TODO: Handle correctly the load with bufferView / uri data
92  if (!textureBuilder.addLayer(asset.images[gltfTexture.source].uri)) {
93  LUG_LOG.error("GltfLoader::createTexture: Can't load the texture \"{}\"", asset.images[gltfTexture.source].uri);
94  return nullptr;
95  }
96  }
97 
98  if (gltfTexture.sampler != -1) {
99  const gltf2::Sampler& sampler = asset.samplers[gltfTexture.sampler];
100 
101  switch(sampler.magFilter) {
102  case gltf2::Sampler::MagFilter::None:
103  break;
104  case gltf2::Sampler::MagFilter::Nearest:
106  break;
107  case gltf2::Sampler::MagFilter::Linear:
109  break;
110  }
111 
112  switch(sampler.minFilter) {
113  case gltf2::Sampler::MinFilter::None:
114  break;
115  case gltf2::Sampler::MinFilter::Nearest:
117  break;
118  case gltf2::Sampler::MinFilter::Linear:
120  break;
121  case gltf2::Sampler::MinFilter::NearestMipMapNearest:
124  break;
125  case gltf2::Sampler::MinFilter::LinearMipMapNearest:
128  break;
129  case gltf2::Sampler::MinFilter::NearestMipMapLinear:
132  break;
133  case gltf2::Sampler::MinFilter::LinearMipMapLinear:
136  break;
137  }
138 
139  switch(sampler.wrapS) {
140  case gltf2::Sampler::WrappingMode::ClampToEdge:
142  break;
143  case gltf2::Sampler::WrappingMode::MirroredRepeat:
145  break;
146  case gltf2::Sampler::WrappingMode::Repeat:
148  break;
149  }
150 
151  switch(sampler.wrapT) {
152  case gltf2::Sampler::WrappingMode::ClampToEdge:
154  break;
155  case gltf2::Sampler::WrappingMode::MirroredRepeat:
157  break;
158  case gltf2::Sampler::WrappingMode::Repeat:
160  break;
161  }
162  }
163 
164  loadedAssets.textures[index] = textureBuilder.build();
165  return loadedAssets.textures[index];
166 }
167 
168 Resource::SharedPtr<Render::Material> GltfLoader::createMaterial(Renderer& renderer, const gltf2::Asset& asset, GltfLoader::LoadedAssets& loadedAssets, int32_t index) {
169  if (index == -1) {
170  return createDefaultMaterial(renderer, loadedAssets);
171  }
172 
173  if (loadedAssets.materials[index]) {
174  return loadedAssets.materials[index];
175  }
176 
177  const gltf2::Material& gltfMaterial = asset.materials[index];
178 
179  Builder::Material materialBuilder(renderer);
180 
181  materialBuilder.setName(gltfMaterial.name);
182 
183  materialBuilder.setBaseColorFactor({
184  gltfMaterial.pbr.baseColorFactor[0],
185  gltfMaterial.pbr.baseColorFactor[1],
186  gltfMaterial.pbr.baseColorFactor[2],
187  gltfMaterial.pbr.baseColorFactor[3]
188  });
189 
190  if (gltfMaterial.pbr.baseColorTexture.index != -1) {
191  Resource::SharedPtr<Render::Texture> texture = createTexture(renderer, asset, loadedAssets, gltfMaterial.pbr.baseColorTexture.index);
192  if (!texture) {
193  LUG_LOG.error("GltfLoader::createMaterial Can't create the texture resource");
194  return nullptr;
195  }
196 
197  materialBuilder.setBaseColorTexture(texture, gltfMaterial.pbr.baseColorTexture.texCoord);
198  }
199 
200  materialBuilder.setMetallicFactor(gltfMaterial.pbr.metallicFactor);
201  materialBuilder.setRoughnessFactor(gltfMaterial.pbr.roughnessFactor);
202 
203  if (gltfMaterial.pbr.metallicRoughnessTexture.index != -1) {
204  Resource::SharedPtr<Render::Texture> texture = createTexture(renderer, asset, loadedAssets, gltfMaterial.pbr.metallicRoughnessTexture.index);
205  if (!texture) {
206  LUG_LOG.error("GltfLoader::createMaterial Can't create the texture resource");
207  return nullptr;
208  }
209 
210  materialBuilder.setMetallicRoughnessTexture(texture, gltfMaterial.pbr.metallicRoughnessTexture.texCoord);
211  }
212 
213  if (gltfMaterial.normalTexture.index != -1) {
214  Resource::SharedPtr<Render::Texture> texture = createTexture(renderer, asset, loadedAssets, gltfMaterial.normalTexture.index);
215  if (!texture) {
216  LUG_LOG.error("GltfLoader::createMaterial Can't create the texture resource");
217  return nullptr;
218  }
219 
220  materialBuilder.setNormalTexture(texture, gltfMaterial.normalTexture.texCoord);
221  }
222 
223  if (gltfMaterial.occlusionTexture.index != -1) {
224  Resource::SharedPtr<Render::Texture> texture = createTexture(renderer, asset, loadedAssets, gltfMaterial.occlusionTexture.index);
225  if (!texture) {
226  LUG_LOG.error("GltfLoader::createMaterial Can't create the texture resource");
227  return nullptr;
228  }
229 
230  materialBuilder.setOcclusionTexture(texture, gltfMaterial.occlusionTexture.texCoord);
231  }
232 
233  if (gltfMaterial.emissiveTexture.index != -1) {
234  Resource::SharedPtr<Render::Texture> texture = createTexture(renderer, asset, loadedAssets, gltfMaterial.emissiveTexture.index);
235  if (!texture) {
236  LUG_LOG.error("GltfLoader::createMaterial Can't create the texture resource");
237  return nullptr;
238  }
239 
240  materialBuilder.setEmissiveTexture(texture, gltfMaterial.emissiveTexture.texCoord);
241  }
242 
243  materialBuilder.setEmissiveFactor({
244  gltfMaterial.emissiveFactor[0],
245  gltfMaterial.emissiveFactor[1],
246  gltfMaterial.emissiveFactor[2]
247  });
248 
249  loadedAssets.materials[index] = materialBuilder.build();
250  return loadedAssets.materials[index];
251 }
252 
254  if (loadedAssets.defaultMaterial) {
255  return loadedAssets.defaultMaterial;
256  }
257 
258  Builder::Material materialBuilder(renderer);
259 
260  loadedAssets.defaultMaterial = materialBuilder.build();
261  return loadedAssets.defaultMaterial;
262 }
263 
264 static void* generateNormals(float* positions, uint32_t accessorCount) {
265  Math::Vec3f* data = new Math::Vec3f[accessorCount];
266 
267  uint32_t trianglesCount = accessorCount / 3;
268  uint32_t positionsIdx = 0;
269  for (uint32_t i = 0; i < trianglesCount; ++i) {
270  Math::Vec3f a{positions[positionsIdx], positions[positionsIdx + 1], positions[positionsIdx + 2]};
271  Math::Vec3f b{positions[positionsIdx + 3], positions[positionsIdx + 4], positions[positionsIdx + 5]};
272  Math::Vec3f c{positions[positionsIdx + 6], positions[positionsIdx + 7], positions[positionsIdx + 8]};
273  Math::Vec3f edge1 = b - a;
274  Math::Vec3f edge2 = c - a;
275 
276  data[i++] = cross(edge1, edge2);
277  }
278 
279  return data;
280 }
281 
282 Resource::SharedPtr<Render::Mesh> GltfLoader::createMesh(Renderer& renderer, const gltf2::Asset& asset, GltfLoader::LoadedAssets& loadedAssets, int32_t index) {
283  const gltf2::Mesh& gltfMesh = asset.meshes[index];
284 
285  if (loadedAssets.meshes[index]) {
286  return loadedAssets.meshes[index];
287  }
288 
289  Builder::Mesh meshBuilder(renderer);
290  meshBuilder.setName(gltfMesh.name);
291 
292  for (const gltf2::Primitive& gltfPrimitive : gltfMesh.primitives) {
293  Builder::Mesh::PrimitiveSet* primitiveSet = meshBuilder.addPrimitiveSet();
294 
295  // Mode
296  switch (gltfPrimitive.mode) {
297  case gltf2::Primitive::Mode::Points:
299  break;
300  case gltf2::Primitive::Mode::Lines:
302  break;
303  case gltf2::Primitive::Mode::LineLoop:
304  LUG_LOG.error("GltfLoader::createMesh Unsupported mode LineLoop");
305  return nullptr;
306  case gltf2::Primitive::Mode::LineStrip:
308  break;
309  case gltf2::Primitive::Mode::Triangles:
311  break;
312  case gltf2::Primitive::Mode::TriangleStrip:
314  break;
315  case gltf2::Primitive::Mode::TriangleFan:
317  break;
318  }
319 
320  // Indices
321  if (gltfPrimitive.indices != -1) {
322  const gltf2::Accessor& accessor = asset.accessors[gltfPrimitive.indices]; // Get the accessor from its index (directly from indices)
323 
324  uint32_t componentSize = getAttributeSize(accessor);
325  void* data = getBufferViewData(asset, accessor);
326  if (!data) {
327  return nullptr;
328  }
329  primitiveSet->addAttributeBuffer(
330  data,
331  componentSize,
332  accessor.count,
334  );
335  }
336 
337  // Attributes
338  struct {
339  void* data{nullptr};
340  uint32_t accessorCount{0};
341  } positions; // Store positions for normals generation
342  bool hasNormals = false;
343 
344  for (auto& attribute : gltfPrimitive.attributes) {
346  if (attribute.first == "POSITION") {
348  } else if (attribute.first == "NORMAL") {
350  hasNormals = true;
351  } else if (attribute.first == "TANGENT") {
353  } else if (attribute.first.find("TEXCOORD_") != std::string::npos) {
355  } else if (attribute.first.find("COLOR_") != std::string::npos) {
357  } else {
358  LUG_LOG.warn("GltfLoader::createMesh Unsupported attribute {}", attribute.first);
359  continue;
360  }
361 
362  // TODO(nokitoo): See if we can use COLOR_0 or if we just discard it
363  const gltf2::Accessor& accessor = asset.accessors[attribute.second]; // Get the accessor from its index (second in the pair)
364 
365  uint32_t componentSize = getAttributeSize(accessor);
366  void* data = getBufferViewData(asset, accessor);
367  if (!data) {
368  return nullptr;
369  }
370  if (type == Render::Mesh::PrimitiveSet::Attribute::Type::Position) { // Store positions in case we need to generate the normals later
371  positions.data = data;
372  positions.accessorCount = accessor.count;
373  }
374  primitiveSet->addAttributeBuffer(data, componentSize, accessor.count, type);
375  }
376 
377  // Generate flat normals if there is not any
378  if (!hasNormals) {
379  void* data = generateNormals((float*)positions.data, positions.accessorCount);
380  if (!data) {
381  return nullptr;
382  }
383  primitiveSet->addAttributeBuffer(
384  data, sizeof(Math::Vec3f), positions.accessorCount,
386  );
387  }
388 
389  // Material
390  Resource::SharedPtr<Render::Material> material = createMaterial(renderer, asset, loadedAssets, gltfPrimitive.material);
391  if (!material) {
392  LUG_LOG.error("GltfLoader::createMesh Can't create the material resource");
393  return nullptr;
394  }
395 
396  primitiveSet->setMaterial(material);
397 
398  // TODO(nokitoo): set node transformations
399  }
400 
401  loadedAssets.meshes[index] = meshBuilder.build();
402  return loadedAssets.meshes[index];
403 }
404 
405 bool GltfLoader::createNode(Renderer& renderer, const gltf2::Asset& asset, GltfLoader::LoadedAssets& loadedAssets, int32_t index, Scene::Node& parent) {
406  const gltf2::Node& gltfNode = asset.nodes[index];
407 
408  Scene::Node* node = parent.createSceneNode(gltfNode.name);
409  parent.attachChild(*node);
410 
411  if (gltfNode.mesh != -1) {
412  Resource::SharedPtr<Render::Mesh> mesh = createMesh(renderer, asset, loadedAssets, gltfNode.mesh);
413  if (!mesh) {
414  LUG_LOG.error("GltfLoader::createNode Can't create the mesh resource");
415  return false;
416  }
417  node->attachMeshInstance(mesh);
418  }
419 
420  node->setPosition({
421  gltfNode.translation[0],
422  gltfNode.translation[1],
423  gltfNode.translation[2]
425 
426  node->setRotation(Math::Quatf{
427  gltfNode.rotation[3],
428  gltfNode.rotation[0],
429  gltfNode.rotation[1],
430  gltfNode.rotation[2]
432 
433  node->scale({
434  gltfNode.scale[0],
435  gltfNode.scale[1],
436  gltfNode.scale[2]
437  });
438 
439  for (uint32_t nodeIdx : gltfNode.children) {
440  if (!createNode(renderer, asset, loadedAssets, nodeIdx, *node)) {
441  return false;
442  }
443  }
444 
445  return true;
446 }
447 
449  gltf2::Asset asset;
450  try {
451 #if defined(LUG_SYSTEM_ANDROID)
452  asset = gltf2::load(filename, (lug::Window::priv::WindowImpl::activity)->assetManager);
453 #else
454  asset = gltf2::load(filename);
455 #endif
456  // TODO(nokitoo): Format the asset if not already done
457  // Should we store the version of format in asset.extensions or asset.copyright/asset.version ?
458  } catch (gltf2::MisformattedException& e) {
459  LUG_LOG.error("GltfLoader::loadFile Can't load the file \"{}\": {}", filename, e.what());
460  return nullptr;
461  }
462 
463  // Create the container for the already loaded assets
464  GltfLoader::LoadedAssets loadedAssets;
465 
466  loadedAssets.textures.resize(asset.textures.size());
467  loadedAssets.materials.resize(asset.materials.size());
468  loadedAssets.meshes.resize(asset.meshes.size());
469 
470  // Load the scene
471  if (asset.scene == -1) { // No scene to load
472  return nullptr;
473  }
474  const gltf2::Scene& gltfScene = asset.scenes[asset.scene];
475 
476  Builder::Scene sceneBuilder(_renderer);
477  sceneBuilder.setName(gltfScene.name);
478 
480  if (!scene) {
481  LUG_LOG.error("GltfLoader::loadFile Can't create the scene resource");
482  return nullptr;
483  }
484 
485  for (uint32_t nodeIdx : gltfScene.nodes) {
486  if (!createNode(_renderer, asset, loadedAssets, nodeIdx, scene->getRoot())) {
487  return nullptr;
488  }
489  }
490 
492 }
493 
494 } // Graphics
495 } // lug
Resource::SharedPtr< Render::Mesh > build()
Definition: Mesh.cpp:24
void setName(const std::string &name)
Sets the name.
Class for loading a type of file.
Definition: Loader.hpp:16
Resource::SharedPtr< Render::Material > createMaterial(Renderer &renderer, const gltf2::Asset &asset, GltfLoader::LoadedAssets &loadedAssets, int32_t index)
Definition: GltfLoader.cpp:168
void setOcclusionTexture(Resource::SharedPtr< Render::Texture > texture, uint32_t texCoord=0, float strength=1.0f)
Definition: Material.inl:37
void attachChild(Node &child)
Definition: Node.cpp:41
PrimitiveSet * addPrimitiveSet()
Adds a primitive set to the builder and returns it.
Definition: Mesh.inl:21
void setMetallicRoughnessTexture(Resource::SharedPtr< Render::Texture > texture, uint32_t texCoord=0)
Definition: Material.inl:26
Tangent (VEC4<FLOAT> w component is a sign value (-1 or +1) indicating handedness of the tangent basi...
void scale(const Math::Vec3f &scale)
Definition: Node.cpp:78
void setMinFilter(Render::Texture::Filter minFilter)
Definition: Texture.inl:17
void setRoughnessFactor(float factor)
Definition: Material.inl:17
void setMaterial(Resource::SharedPtr< Render::Material > material)
Definition: Mesh.inl:1
void setName(const std::string &name)
Sets the name.
Definition: Mesh.inl:26
std::vector< Resource::SharedPtr< Render::Texture > > textures
Definition: GltfLoader.hpp:25
Resource::SharedPtr< Render::Mesh > createMesh(Renderer &renderer, const gltf2::Asset &asset, GltfLoader::LoadedAssets &loadedAssets, int32_t index)
Definition: GltfLoader.cpp:282
void setRotation(float angle, const Math::Vec3f &axis, TransformSpace space=TransformSpace::Local)
Definition: Node.cpp:116
The first two vertices define the first segment, with subsequent pairs of vertices each defining one ...
void setWrapS(Render::Texture::WrappingMode wrapS)
Definition: Texture.inl:25
void addAttributeBuffer(const void *data, uint32_t elementSize, uint32_t elementsCount, Render::Mesh::PrimitiveSet::Attribute::Type type)
Definition: Mesh.cpp:11
void setMetallicFactor(float factor)
Definition: Material.inl:13
constexpr Vector< 3, T > cross(const Vector< 3, T > &lhs, const Vector< 3, T > &rhs)
Definition: Vector.inl:72
Dummy class for a shared pointer.
Definition: Resource.hpp:66
std::vector< Resource::SharedPtr< Render::Material > > materials
Definition: GltfLoader.hpp:27
Resource::SharedPtr< Render::Material > build()
Definition: Material.cpp:11
Resource::SharedPtr< Render::Texture > createTexture(Renderer &renderer, const gltf2::Asset &asset, GltfLoader::LoadedAssets &loadedAssets, int32_t index)
Definition: GltfLoader.cpp:81
void attachMeshInstance(Resource::SharedPtr< Render::Mesh > mesh, Resource::SharedPtr< Render::Material > material=nullptr)
Definition: Node.cpp:19
void setMagFilter(Render::Texture::Filter magFilter)
Definition: Texture.inl:13
std::vector< Resource::SharedPtr< Render::Mesh > > meshes
Definition: GltfLoader.hpp:28
static void * getBufferViewData(const gltf2::Asset &asset, const gltf2::Accessor &accessor)
Definition: GltfLoader.cpp:24
void setBaseColorTexture(Resource::SharedPtr< Render::Texture > texture, uint32_t texCoord=0)
Definition: Material.inl:21
void setEmissiveTexture(Resource::SharedPtr< Render::Texture > texture, uint32_t texCoord=0)
Definition: Material.inl:43
T e()
Definition: Constant.inl:82
bool addLayer(const std::string &filename, bool hdr=false)
Definition: Texture.cpp:98
void setWrapT(Render::Texture::WrappingMode wrapT)
Definition: Texture.inl:29
static ANativeActivity * activity
Resource::SharedPtr< Render::Material > defaultMaterial
Definition: GltfLoader.hpp:26
void setEmissiveFactor(const Math::Vec3f &factor)
Definition: Material.inl:9
Node * createSceneNode(const std::string &name)
Definition: Node.cpp:11
static SharedPtr< T > cast(const SharedPtr< RhsT > &rhs)
Dynamic casting of a SharedPtr to another one (RhsT to T)
Resource::SharedPtr< Resource > loadFile(const std::string &filename) override final
Loads a glTF ressource from a file.
Definition: GltfLoader.cpp:448
static void * generateNormals(float *positions, uint32_t accessorCount)
Definition: GltfLoader.cpp:264
bool createNode(Renderer &renderer, const gltf2::Asset &asset, GltfLoader::LoadedAssets &loadedAssets, int32_t index, Scene::Node &parent)
Definition: GltfLoader.cpp:405
void setMipMapFilter(Render::Texture::Filter mipMapFilter)
Definition: Texture.inl:21
Resource::SharedPtr< lug::Graphics::Scene::Scene > build()
Definition: Scene.cpp:11
Resource::SharedPtr< Render::Material > createDefaultMaterial(Renderer &renderer, GltfLoader::LoadedAssets &loadedAssets)
Definition: GltfLoader.cpp:253
void setName(const std::string &name)
Sets the name.
Definition: Scene.inl:1
void setPosition(const Math::Vec3f &position, TransformSpace space=TransformSpace::Local)
Definition: Node.cpp:83
#define LUG_LOG
Definition: Logger.hpp:73
The first vertex specifies the first segment’s start point while the second vertex specifies the fir...
void setMode(Render::Mesh::PrimitiveSet::Mode mode)
Definition: Mesh.inl:5
GltfLoader(Renderer &renderer)
Definition: GltfLoader.cpp:22
void setNormalTexture(Resource::SharedPtr< Render::Texture > texture, uint32_t texCoord=0, float scale=1.0f)
Definition: Material.inl:31
Each vertex defines a separate point.
Resource::SharedPtr< Render::Texture > build()
Definition: Texture.cpp:37
void setBaseColorFactor(const Math::Vec4f &factor)
Definition: Material.inl:5
Quaternion< float > Quatf
Definition: Quaternion.hpp:75
Renderer & _renderer
Definition: Loader.hpp:36
static uint32_t getAttributeSize(const gltf2::Accessor &accessor)
Definition: GltfLoader.cpp:37