23#ifndef VCL_IO_MESH_GLTF_DETAIL_LOAD_MESH_H
24#define VCL_IO_MESH_GLTF_DETAIL_LOAD_MESH_H
26#include <vclib/io/mesh/settings.h>
28#include <vclib/algorithms/mesh.h>
29#include <vclib/mesh.h>
30#include <vclib/space/complex.h>
31#include <vclib/space/core.h>
37namespace vcl::detail {
39enum class GltfAttrType {
47inline const std::array<std::string, 5>
48 GLTF_ATTR_STR {
"POSITION",
"NORMAL",
"COLOR_0",
"TEXCOORD_0",
"TANGENT"};
50template<MeshConcept MeshType>
51int loadGltfPrimitiveMaterial(
53 const tinygltf::Model& model,
54 const tinygltf::Primitive& p)
58 if (p.material >= 0) {
61 double metallic, roughness, alphaCutoff, normalScale, occlusionStrength;
63 int baseColorTextureId, metallicRoughnessTextureId, normalTextureId,
64 occlusionTextureId, emissiveTextureId;
65 const tinygltf::Material& mat = model.materials[p.material];
67 std::string matName = mat.name;
70 const std::vector<double>& vc =
71 mat.pbrMetallicRoughness.baseColorFactor;
72 for (uint i = 0; i < 4; i++)
73 baseColor[i] = vc[i] * 255.0;
77 mat.pbrMetallicRoughness.baseColorTexture
81 metallic = mat.pbrMetallicRoughness.metallicFactor;
85 mat.pbrMetallicRoughness.roughnessFactor;
87 metallicRoughnessTextureId =
88 mat.pbrMetallicRoughness.metallicRoughnessTexture.index;
91 const std::vector<double>& emissiveFactor =
93 for (uint i = 0; i < 3; i++)
94 emissiveColor[i] = emissiveFactor[i] * 255.0;
97 emissiveTextureId = mat.emissiveTexture.index;
100 normalTextureId = mat.normalTexture.index;
103 occlusionTextureId = mat.occlusionTexture.index;
106 doubleSided = mat.doubleSided;
109 if (mat.alphaMode ==
"MASK")
111 else if (mat.alphaMode ==
"BLEND")
117 alphaCutoff = mat.alphaCutoff;
120 normalScale = mat.normalTexture.scale;
123 occlusionStrength = mat.occlusionTexture.strength;
126 auto loadTextureInMaterial = [&](Material& mat,
129 if (textureId != -1) {
130 const tinygltf::Image& img =
131 model.images[model.textures[textureId].source];
133 std::string uri = img.uri;
134 uri = std::regex_replace(uri, std::regex(
"\\%20"),
" ");
136 uri =
"texture_" + std::to_string(textureId);
141 texture.
path() = uri;
144 int samplerId = model.textures[textureId].sampler;
145 if (samplerId >= 0) {
146 const tinygltf::Sampler& sampler =
147 model.samplers[samplerId];
160 assert(samplerId == -1);
174 if (img.image.size() > 0 &&
175 (img.bits == 8 || img.component == 4)) {
176 Image timg(img.image.data(), img.width, img.height);
179 m.pushTextureImage(uri, std::move(timg));
186 if constexpr (HasMaterials<MeshType>) {
188 mat.name() = matName;
189 mat.baseColor() = baseColor;
190 mat.metallic() = metallic;
191 mat.roughness() = roughness;
192 mat.emissiveColor() = emissiveColor;
193 mat.alphaMode() = alphaMode;
194 mat.alphaCutoff() = alphaCutoff;
195 mat.doubleSided() = doubleSided;
196 mat.normalScale() = normalScale;
197 mat.occlusionStrength() = occlusionStrength;
198 loadTextureInMaterial(
200 loadTextureInMaterial(
202 metallicRoughnessTextureId,
204 loadTextureInMaterial(
206 loadTextureInMaterial(
208 loadTextureInMaterial(
211 idx = m.materialsNumber() - 1;
212 if constexpr (HasColor<MeshType>) {
214 m.color() = Color::White;
217 else if constexpr (HasColor<MeshType>) {
220 m.color() = baseColor;
227template<MeshConcept MeshType,
typename Scalar>
228bool populateGltfVertices(
230 const Scalar* posArray,
234 using PositionType =
typename MeshType::VertexType::PositionType;
236 uint base = m.addVertices(vertNumber);
238 for (uint i = 0; i < vertNumber; ++i) {
239 const Scalar* posBase =
reinterpret_cast<const Scalar*
>(
240 reinterpret_cast<const char*
>(posArray) + (i) *stride);
241 m.vertex(base + i).position() =
242 PositionType(posBase[0], posBase[1], posBase[2]);
247template<MeshConcept MeshType,
typename Scalar>
248bool populateGltfVNormals(
251 bool enableOptionalComponents,
252 const Scalar* normArray,
254 unsigned int vertNumber)
256 if constexpr (HasPerVertexNormal<MeshType>) {
257 using NormalType =
typename MeshType::VertexType::NormalType;
259 if (enableOptionalComponents)
260 enableIfPerVertexNormalOptional(m);
262 if (isPerVertexNormalAvailable(m)) {
263 for (
unsigned int i = 0; i < vertNumber; i++) {
264 const Scalar* normBase =
reinterpret_cast<const Scalar*
>(
265 reinterpret_cast<const char*
>(normArray) + i * stride);
266 m.vertex(firstVertex + i).normal() =
267 NormalType(normBase[0], normBase[1], normBase[2]);
280template<MeshConcept MeshType,
typename Scalar>
281bool populateGltfVTangents(
284 bool enableOptionalComponents,
285 const Scalar* tangArray,
287 unsigned int vertNumber)
289 if constexpr (HasPerVertexTangent<MeshType>) {
290 using TangentType =
typename MeshType::VertexType::TangentType;
292 if (enableOptionalComponents)
293 enableIfPerVertexTangentOptional(m);
295 if (isPerVertexTangentAvailable(m)) {
296 for (
unsigned int i = 0; i < vertNumber; i++) {
297 const Scalar* tangBase =
reinterpret_cast<const Scalar*
>(
298 reinterpret_cast<const char*
>(tangArray) + i * stride);
299 m.vertex(firstVertex + i).tangent() =
300 TangentType(tangBase[0], tangBase[1], tangBase[2]);
301 bool rh = tangBase[3] >= 0;
302 m.vertex(firstVertex + i).tangentRightHanded() = rh;
315template<MeshConcept MeshType,
typename Scalar>
316bool populateGltfVColors(
319 bool enableOptionalComponents,
320 const Scalar* colorArray,
322 unsigned int vertNumber,
325 unsigned int nElemns = colorWithAlpha ? 4 : 3;
326 if constexpr (HasPerVertexColor<MeshType>) {
327 if (enableOptionalComponents)
328 enableIfPerVertexColorOptional(m);
330 if (isPerVertexColorAvailable(m)) {
331 for (
unsigned int i = 0; i < vertNumber * nElemns; i += nElemns) {
332 const Scalar* colorBase =
reinterpret_cast<const Scalar*
>(
333 reinterpret_cast<const char*
>(colorArray) +
334 (i / nElemns) * stride);
335 const auto vi = firstVertex + i / nElemns;
337 if constexpr (!std::is_floating_point<Scalar>::value) {
338 uint alpha = nElemns == 4 ? colorBase[3] : 255;
340 colorBase[0], colorBase[1], colorBase[2], alpha);
343 uint alpha = nElemns == 4 ? colorBase[3] * 255 : 255;
350 m.vertex(vi).color() = c;
363template<MeshConcept MeshType,
typename Scalar>
364bool populateGltfVTextCoords(
367 bool enableOptionalComponents,
368 const Scalar* textCoordArray,
370 unsigned int vertNumber)
372 if constexpr (HasPerVertexTexCoord<MeshType>) {
373 using TexCoordType =
typename MeshType::VertexType::TexCoordType;
375 if (enableOptionalComponents)
376 enableIfPerVertexTexCoordOptional(m);
378 if (isPerVertexTexCoordAvailable(m)) {
379 for (
unsigned int i = 0; i < vertNumber; i++) {
380 const Scalar* textCoordBase =
reinterpret_cast<const Scalar*
>(
381 reinterpret_cast<const char*
>(textCoordArray) + i * stride);
383 m.vertex(firstVertex + i).texCoord() =
384 TexCoordType(textCoordBase[0], 1 - textCoordBase[1]);
397template<MeshConcept MeshType,
typename Scalar>
398bool populateGltfTriangles(
401 const Scalar* triArray,
404 if constexpr (HasFaces<MeshType>) {
405 if (triArray !=
nullptr) {
406 uint fi = m.addFaces(triNumber);
407 for (
unsigned int i = 0; i < triNumber * 3; i += 3, ++fi) {
408 auto& f = m.face(fi);
409 if constexpr (HasPolygons<MeshType>) {
412 for (
int j = 0; j < 3; ++j) {
413 f.setVertex(j, firstVertex + triArray[i + j]);
418 triNumber = m.vertexNumber() / 3 - firstVertex;
419 uint fi = m.addFaces(triNumber);
420 for (uint i = 0; i < triNumber * 3; i += 3, ++fi) {
421 auto& f = m.face(fi);
422 for (uint j = 0; j < 3; ++j) {
423 f.setVertex(j, firstVertex + i + j);
447template<
typename Scalar, MeshConcept MeshType>
448bool populateGltfAttr(
452 bool enableOptionalComponents,
456 bool colorWithAlpha =
true)
458 using enum GltfAttrType;
461 case POSITION:
return populateGltfVertices(m, array, stride, number);
463 return populateGltfVNormals(
464 m, firstVertex, enableOptionalComponents, array, stride, number);
466 return populateGltfVColors(
469 enableOptionalComponents,
475 return populateGltfVTextCoords(
476 m, firstVertex, enableOptionalComponents, array, stride, number);
478 return populateGltfVTangents(
479 m, firstVertex, enableOptionalComponents, array, stride, number);
481 return populateGltfTriangles(m, firstVertex, array, number / 3);
482 default:
return false;
502template<MeshConcept MeshType>
503bool loadGltfAttribute(
506 bool enableOptionalComponents,
507 const tinygltf::Model& model,
508 const tinygltf::Primitive& p,
511 using enum GltfAttrType;
513 const tinygltf::Accessor* accessor =
nullptr;
516 if (attr != INDICES) {
517 auto it = p.attributes.find(GLTF_ATTR_STR[toUnderlying(attr)]);
519 if (it != p.attributes.end()) {
520 accessor = &model.accessors[it->second];
522 else if (attr == POSITION) {
524 throw std::runtime_error(
"File has not 'Position' attribute");
529 if (p.mode == TINYGLTF_MODE_TRIANGLES && p.indices >= 0 &&
530 (uint) p.indices < model.accessors.size()) {
531 accessor = &model.accessors[p.indices];
538 const tinygltf::BufferView& posbw =
539 model.bufferViews[accessor->bufferView];
543 const std::vector<unsigned char>& posdata =
544 model.buffers[posbw.buffer].data;
547 uint posOffset = posbw.byteOffset + accessor->byteOffset;
551 bool colorWithAlpha =
true;
552 if (attr == COLOR_0) {
553 if (accessor->type == TINYGLTF_TYPE_VEC3)
554 colorWithAlpha =
false;
557 const uint elementSize =
558 tinygltf::GetNumComponentsInType(accessor->type) *
559 tinygltf::GetComponentSizeInBytes(accessor->componentType);
561 (posbw.byteStride > elementSize) ? posbw.byteStride : elementSize;
564 if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
566 const float* posArray = (
const float*) (posdata.data() + posOffset);
567 return populateGltfAttr(
571 enableOptionalComponents,
578 else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
580 const double* posArray =
581 (
const double*) (posdata.data() + posOffset);
582 return populateGltfAttr(
586 enableOptionalComponents,
594 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
596 const unsigned char* triArray =
597 (
const unsigned char*) (posdata.data() + posOffset);
598 return populateGltfAttr(
602 enableOptionalComponents,
610 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
612 const unsigned short* triArray =
613 (
const unsigned short*) (posdata.data() + posOffset);
614 return populateGltfAttr(
618 enableOptionalComponents,
626 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
628 const uint* triArray = (
const uint*) (posdata.data() + posOffset);
629 return populateGltfAttr(
633 enableOptionalComponents,
643 else if (attr == INDICES) {
645 if (p.mode != TINYGLTF_MODE_POINTS) {
647 return populateGltfAttr<unsigned char>(
651 enableOptionalComponents,
667 MeshConcept MeshType,
668 Matrix44Concept MatrixType,
669 LoggerConcept LogType>
670void loadGltfMeshPrimitive(
673 const tinygltf::Model& model,
674 const tinygltf::Primitive& p,
675 const MatrixType& transf,
676 const LoadSettings& settings,
679 int materialId = loadGltfPrimitiveMaterial(m, model, p);
681 uint firstVertex = m.vertexNumber();
687 settings.enableOptionalComponents,
690 GltfAttrType::POSITION);
693 bool lvn = loadGltfAttribute(
696 settings.enableOptionalComponents,
699 GltfAttrType::NORMAL);
700 info.setPerVertexNormal(lvn);
702 bool lvc = loadGltfAttribute(
705 settings.enableOptionalComponents,
708 GltfAttrType::COLOR_0);
710 info.setPerVertexColor();
713 bool lvt = loadGltfAttribute(
716 settings.enableOptionalComponents,
719 GltfAttrType::TEXCOORD_0);
721 info.setPerVertexTexCoord();
727 settings.enableOptionalComponents,
730 GltfAttrType::TANGENT);
732 if constexpr (HasPerVertexMaterialIndex<MeshType>) {
733 if (settings.enableOptionalComponents) {
734 enableIfPerVertexMaterialIndexOptional(m);
736 if (isPerVertexMaterialIndexAvailable(m)) {
737 uint vnum = m.vertexNumber();
738 for (uint v = firstVertex; v < vnum; ++v) {
739 m.vertex(v).materialIndex() = materialId;
741 info.setPerVertexMaterialIndex();
745 if constexpr (HasFaces<MeshType>) {
746 uint firstFace = m.faceNumber();
747 bool lti = loadGltfAttribute(
750 settings.enableOptionalComponents,
753 GltfAttrType::INDICES);
755 info.setTriangleMesh();
757 info.setPerFaceVertexReferences();
761 if (HasTransformMatrix<MeshType>) {
762 m.transformMatrix() = transf;
767 vcl::applyTransformMatrix(m, transf);
780 MeshConcept MeshType,
781 Matrix44Concept MatrixType,
782 LoggerConcept LogType>
786 const tinygltf::Mesh& tm,
787 const tinygltf::Model& model,
788 const MatrixType& transf,
789 const LoadSettings& settings,
792 if constexpr (HasName<MeshType>) {
793 if (!tm.name.empty()) {
803 for (uint i = 0;
const tinygltf::Primitive& p : tm.primitives) {
804 loadGltfMeshPrimitive(m, info, model, p, transf, settings, log);
811 "Loaded mesh '" + tm.name +
"' with " +
812 std::to_string(tm.primitives.size()) +
" primitives.",
813 LogType::LogLevel::MESSAGE_LOG);
The Color class represents a 32 bit color.
Definition color.h:48
TextureType
Defines the types of textures used in the PBR material model.
Definition material.h:62
@ NORMAL
The tangent-space normal map. Stored in linear color space.
@ BASE_COLOR
The base color (albedo) texture. Stored in sRGB color space.
@ EMISSIVE
The emissive color texture. Stored in sRGB color space.
static Image::ColorSpace textureTypeToColorSpace(TextureType type)
Determines the appropriate color space for a given texture type.
Definition material.h:375
AlphaMode
Defines the alpha rendering mode of the material.
Definition material.h:50
Describes the properties of a texture, such as its source path and rendering parameters.
Definition texture_descriptor.h:42
MagnificationFilter magFilter() const
Gets the magnification filter of the texture.
Definition texture_descriptor.h:151
MinificationFilter minFilter() const
Gets the minification filter of the texture.
Definition texture_descriptor.h:138
MinificationFilter
Defines the texture minification filter modes, following the glTF 2.0 specification....
Definition texture_descriptor.h:50
@ NONE
No filter specified.
MagnificationFilter
Defines the texture magnification filter modes, following the glTF 2.0 specification....
Definition texture_descriptor.h:70
@ NONE
No filter specified.
WrapMode wrapU() const
Gets the wrap mode for the U (S) texture coordinate.
Definition texture_descriptor.h:164
const std::string & path() const
Gets the file path of the texture.
Definition texture_descriptor.h:126
WrapMode
Defines the texture wrapping modes for S (U) and T (V) coordinates, following the glTF 2....
Definition texture_descriptor.h:81
@ REPEAT
The texture repeats.
WrapMode wrapV() const
Gets the wrap mode for the V (T) texture coordinate.
Definition texture_descriptor.h:177