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 { POSITION, NORMAL, COLOR_0, TEXCOORD_0, INDICES };
40inline const std::array<std::string, 4> GLTF_ATTR_STR {
46inline void checkGltfPrimitiveMaterial(
47 const tinygltf::Model& model,
48 const tinygltf::Primitive& p,
53 if (p.material >= 0) {
54 const tinygltf::Material& mat = model.materials[p.material];
55 auto it = mat.values.find(
"baseColorTexture");
56 if (it != mat.values.end()) {
57 auto it2 = it->second.json_double_value.find(
"index");
58 if (it2 != it->second.json_double_value.end()) {
59 textureImg = it2->second;
62 it = mat.values.find(
"baseColorFactor");
66 const std::vector<double>& vc = it->second.number_array;
67 for (uint i = 0; i < 4; i++)
68 color[i] = vc[i] * 255.0;
73template<MeshConcept MeshType,
typename Scalar>
74bool populateGltfVertices(
76 const Scalar* posArray,
80 using PositionType =
typename MeshType::VertexType::PositionType;
82 uint base = m.addVertices(vertNumber);
84 for (uint i = 0; i < vertNumber; ++i) {
85 const Scalar* posBase =
reinterpret_cast<const Scalar*
>(
86 reinterpret_cast<const char*
>(posArray) + (i) *stride);
87 m.vertex(base + i).position() =
88 PositionType(posBase[0], posBase[1], posBase[2]);
93template<MeshConcept MeshType,
typename Scalar>
94bool populateGltfVNormals(
97 bool enableOptionalComponents,
98 const Scalar* normArray,
100 unsigned int vertNumber)
102 if constexpr (HasPerVertexNormal<MeshType>) {
103 using NormalType =
typename MeshType::VertexType::NormalType;
105 if (enableOptionalComponents)
106 enableIfPerVertexNormalOptional(m);
108 if (isPerVertexNormalAvailable(m)) {
109 for (
unsigned int i = 0; i < vertNumber; i++) {
110 const Scalar* normBase =
reinterpret_cast<const Scalar*
>(
111 reinterpret_cast<const char*
>(normArray) + i * stride);
112 m.vertex(firstVertex + i).normal() =
113 NormalType(normBase[0], normBase[1], normBase[2]);
126template<MeshConcept MeshType,
typename Scalar>
127bool populateGltfVColors(
130 bool enableOptionalComponents,
131 const Scalar* colorArray,
133 unsigned int vertNumber,
136 if constexpr (HasPerVertexColor<MeshType>) {
137 if (enableOptionalComponents)
138 enableIfPerVertexColorOptional(m);
140 if (isPerVertexColorAvailable(m)) {
141 for (
unsigned int i = 0; i < vertNumber * nElemns; i += nElemns) {
142 const Scalar* colorBase =
reinterpret_cast<const Scalar*
>(
143 reinterpret_cast<const char*
>(colorArray) +
144 (i / nElemns) * stride);
145 const auto vi = firstVertex + i / nElemns;
147 if constexpr (!std::is_floating_point<Scalar>::value) {
148 uint alpha = nElemns == 4 ? colorBase[3] : 255;
150 colorBase[0], colorBase[1], colorBase[2], alpha);
153 uint alpha = nElemns == 4 ? colorBase[3] * 255 : 255;
160 m.vertex(vi).color() = c;
173template<MeshConcept MeshType,
typename Scalar>
174bool populateGltfVTextCoords(
177 bool enableOptionalComponents,
178 const Scalar* textCoordArray,
180 unsigned int vertNumber,
183 if constexpr (HasPerVertexTexCoord<MeshType>) {
184 using TexCoordType =
typename MeshType::VertexType::TexCoordType;
186 if (enableOptionalComponents)
187 enableIfPerVertexTexCoordOptional(m);
189 if (isPerVertexTexCoordAvailable(m)) {
190 for (
unsigned int i = 0; i < vertNumber; i++) {
191 const Scalar* textCoordBase =
reinterpret_cast<const Scalar*
>(
192 reinterpret_cast<const char*
>(textCoordArray) + i * stride);
194 m.vertex(firstVertex + i).texCoord() = TexCoordType(
195 textCoordBase[0], 1 - textCoordBase[1], textID);
208template<MeshConcept MeshType,
typename Scalar>
209bool populateGltfTriangles(
212 const Scalar* triArray,
215 if constexpr (HasFaces<MeshType>) {
216 if (triArray !=
nullptr) {
217 uint fi = m.addFaces(triNumber);
218 for (
unsigned int i = 0; i < triNumber * 3; i += 3, ++fi) {
219 auto& f = m.face(fi);
220 if constexpr (HasPolygons<MeshType>) {
223 for (
int j = 0; j < 3; ++j) {
224 f.setVertex(j, firstVertex + triArray[i + j]);
229 triNumber = m.vertexNumber() / 3 - firstVertex;
230 uint fi = m.addFaces(triNumber);
231 for (uint i = 0; i < triNumber * 3; i += 3, ++fi) {
232 auto& f = m.face(fi);
233 for (uint j = 0; j < 3; ++j) {
234 f.setVertex(j, firstVertex + i + j);
260template<
typename Scalar, MeshConcept MeshType>
261bool populateGltfAttr(
265 bool enableOptionalComponents,
271 using enum GltfAttrType;
274 case POSITION:
return populateGltfVertices(m, array, stride, number);
276 return populateGltfVNormals(
277 m, firstVertex, enableOptionalComponents, array, stride, number);
279 return populateGltfVColors(
282 enableOptionalComponents,
288 return populateGltfVTextCoords(
291 enableOptionalComponents,
297 return populateGltfTriangles(m, firstVertex, array, number / 3);
298 default:
return false;
318template<MeshConcept MeshType>
319bool loadGltfAttribute(
322 bool enableOptionalComponents,
323 const tinygltf::Model& model,
324 const tinygltf::Primitive& p,
328 using enum GltfAttrType;
330 const tinygltf::Accessor* accessor =
nullptr;
333 if (attr != INDICES) {
334 auto it = p.attributes.find(GLTF_ATTR_STR[toUnderlying(attr)]);
336 if (it != p.attributes.end()) {
337 accessor = &model.accessors[it->second];
339 else if (attr == POSITION) {
341 throw MalformedFileException(
"File has not 'Position' attribute");
346 if (p.mode == TINYGLTF_MODE_TRIANGLES && p.indices >= 0 &&
347 (uint) p.indices < model.accessors.size()) {
348 accessor = &model.accessors[p.indices];
355 const tinygltf::BufferView& posbw =
356 model.bufferViews[accessor->bufferView];
360 const std::vector<unsigned char>& posdata =
361 model.buffers[posbw.buffer].data;
364 uint posOffset = posbw.byteOffset + accessor->byteOffset;
368 if (attr == COLOR_0) {
369 if (accessor->type == TINYGLTF_TYPE_VEC3)
371 else if (accessor->type == TINYGLTF_TYPE_VEC4)
375 const uint elementSize =
376 tinygltf::GetNumComponentsInType(accessor->type) *
377 tinygltf::GetComponentSizeInBytes(accessor->componentType);
379 (posbw.byteStride > elementSize) ? posbw.byteStride : elementSize;
382 if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
384 const float* posArray = (
const float*) (posdata.data() + posOffset);
385 return populateGltfAttr(
389 enableOptionalComponents,
396 else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
398 const double* posArray =
399 (
const double*) (posdata.data() + posOffset);
400 return populateGltfAttr(
404 enableOptionalComponents,
412 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
414 const unsigned char* triArray =
415 (
const unsigned char*) (posdata.data() + posOffset);
416 return populateGltfAttr(
420 enableOptionalComponents,
428 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
430 const unsigned short* triArray =
431 (
const unsigned short*) (posdata.data() + posOffset);
432 return populateGltfAttr(
436 enableOptionalComponents,
444 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
446 const uint* triArray = (
const uint*) (posdata.data() + posOffset);
447 return populateGltfAttr(
451 enableOptionalComponents,
461 else if (attr == INDICES) {
463 if (p.mode != TINYGLTF_MODE_POINTS) {
465 return populateGltfAttr<unsigned char>(
469 enableOptionalComponents,
485 MeshConcept MeshType,
486 Matrix44Concept MatrixType,
487 LoggerConcept LogType>
488void loadGltfMeshPrimitive(
491 const tinygltf::Model& model,
492 const tinygltf::Primitive& p,
493 const MatrixType& transf,
494 const LoadSettings& settings,
502 checkGltfPrimitiveMaterial(model, p, textureImg, vCol, col);
504 if constexpr (HasTexturePaths<MeshType>) {
505 if (textureImg != -1) {
507 const tinygltf::Image& img =
508 model.images[model.textures[textureImg].source];
510 std::string uri = img.uri;
511 uri = std::regex_replace(uri, std::regex(
"\\%20"),
" ");
513 bool textureAdded =
false;
514 if constexpr (HasTextureImages<MeshType>) {
515 if (img.image.size() > 0) {
516 if (img.bits == 8 || img.component == 4) {
518 uri =
"texture_" + std::to_string(textureImg);
521 Image(img.image.data(), img.width, img.height),
530 m.pushTexturePath(uri);
532 textureImg = m.textureNumber() - 1;
536 uint firstVertex = m.vertexNumber();
542 settings.enableOptionalComponents,
545 GltfAttrType::POSITION,
549 bool lvn = loadGltfAttribute(
552 settings.enableOptionalComponents,
555 GltfAttrType::NORMAL,
557 info.setPerVertexNormal(lvn);
560 if constexpr (HasPerVertexColor<MeshType>) {
561 if (settings.enableOptionalComponents) {
562 enableIfPerVertexColorOptional(m);
564 if (isPerVertexColorAvailable(m)) {
567 info.setPerVertexColor();
572 bool lvc = loadGltfAttribute(
575 settings.enableOptionalComponents,
578 GltfAttrType::COLOR_0,
581 info.setPerVertexColor();
584 bool lvt = loadGltfAttribute(
587 settings.enableOptionalComponents,
590 GltfAttrType::TEXCOORD_0,
593 info.setPerVertexTexCoord();
596 bool lti = loadGltfAttribute(
599 settings.enableOptionalComponents,
602 GltfAttrType::INDICES,
605 info.setTriangleMesh();
607 info.setPerFaceVertexReferences();
610 if (HasTransformMatrix<MeshType>) {
611 m.transformMatrix() = transf;
616 vcl::applyTransformMatrix(m, transf);
629 MeshConcept MeshType,
630 Matrix44Concept MatrixType,
631 LoggerConcept LogType>
635 const tinygltf::Mesh& tm,
636 const tinygltf::Model& model,
637 const MatrixType& transf,
638 const LoadSettings& settings,
641 if constexpr (HasName<MeshType>) {
642 if (!tm.name.empty()) {
652 for (uint i = 0;
const tinygltf::Primitive& p : tm.primitives) {
653 loadGltfMeshPrimitive(m, info, model, p, transf, settings, log);
660 "Loaded mesh '" + tm.name +
"' with " +
661 std::to_string(tm.primitives.size()) +
" primitives.",
662 LogType::LogLevel::MESSAGE_LOG);
The Color class represents a 32 bit color.
Definition color.h:48
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:92