Visual Computing Library  devel
Loading...
Searching...
No Matches
load_mesh.h
1/*****************************************************************************
2 * VCLib *
3 * Visual Computing Library *
4 * *
5 * Copyright(C) 2021-2025 *
6 * Visual Computing Lab *
7 * ISTI - Italian National Research Council *
8 * *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the Mozilla Public License Version 2.0 as published *
13 * by the Mozilla Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * Mozilla Public License Version 2.0 *
20 * (https://www.mozilla.org/en-US/MPL/2.0/) for more details. *
21 ****************************************************************************/
22
23#ifndef VCL_IO_MESH_GLTF_DETAIL_LOAD_MESH_H
24#define VCL_IO_MESH_GLTF_DETAIL_LOAD_MESH_H
25
26#include <vclib/io/mesh/settings.h>
27
28#include <vclib/algorithms/mesh.h>
29#include <vclib/mesh.h>
30#include <vclib/space/complex.h>
31#include <vclib/space/core.h>
32
33#include <tiny_gltf.h>
34
35#include <regex>
36
37namespace vcl::detail {
38
39enum class GltfAttrType {
40 POSITION,
41 NORMAL,
42 COLOR_0,
43 TEXCOORD_0,
44 TANGENT,
45 INDICES
46};
47inline const std::array<std::string, 5>
48 GLTF_ATTR_STR {"POSITION", "NORMAL", "COLOR_0", "TEXCOORD_0", "TANGENT"};
49
50template<MeshConcept MeshType>
51int loadGltfPrimitiveMaterial(
52 MeshType& m,
53 const tinygltf::Model& model,
54 const tinygltf::Primitive& p)
55{
56 int idx = -1;
57
58 if (p.material >= 0) {
59 vcl::Color baseColor, emissiveColor;
60 Material::AlphaMode alphaMode;
61 double metallic, roughness, alphaCutoff, normalScale, occlusionStrength;
62 bool doubleSided;
63 int baseColorTextureId, metallicRoughnessTextureId, normalTextureId,
64 occlusionTextureId, emissiveTextureId;
65 const tinygltf::Material& mat = model.materials[p.material];
66
67 std::string matName = mat.name;
68
69 // baseColorFactor
70 const std::vector<double>& vc =
71 mat.pbrMetallicRoughness.baseColorFactor; // has default value
72 for (uint i = 0; i < 4; i++)
73 baseColor[i] = vc[i] * 255.0;
74
75 // baseColorTexture
76 baseColorTextureId =
77 mat.pbrMetallicRoughness.baseColorTexture
78 .index; // get the id of the texture, -1 if not present
79
80 // metallicFactor
81 metallic = mat.pbrMetallicRoughness.metallicFactor; // has default value
82
83 // roughnessFactor
84 roughness =
85 mat.pbrMetallicRoughness.roughnessFactor; // has default value
86
87 metallicRoughnessTextureId =
88 mat.pbrMetallicRoughness.metallicRoughnessTexture.index;
89
90 // emissiveFactor
91 const std::vector<double>& emissiveFactor =
92 mat.emissiveFactor; // has default value
93 for (uint i = 0; i < 3; i++)
94 emissiveColor[i] = emissiveFactor[i] * 255.0;
95
96 // emissiveTexture
97 emissiveTextureId = mat.emissiveTexture.index;
98
99 // normalTexture
100 normalTextureId = mat.normalTexture.index;
101
102 // occlusionTexture
103 occlusionTextureId = mat.occlusionTexture.index;
104
105 // doubleSided
106 doubleSided = mat.doubleSided; // has default value
107
108 // alphaMode
109 if (mat.alphaMode == "MASK")
111 else if (mat.alphaMode == "BLEND")
113 else
114 alphaMode = Material::AlphaMode::ALPHA_OPAQUE; // has default value
115
116 // alphaCutoff
117 alphaCutoff = mat.alphaCutoff; // has default value
118
119 // normalScale
120 normalScale = mat.normalTexture.scale;
121
122 // occlusionStrength
123 occlusionStrength = mat.occlusionTexture.strength;
124
125 // function to load a texture in a material
126 auto loadTextureInMaterial = [&](Material& mat,
127 int textureId,
129 if (textureId != -1) {
130 const tinygltf::Image& img =
131 model.images[model.textures[textureId].source];
132 // add the path of the texture to the mesh
133 std::string uri = img.uri;
134 uri = std::regex_replace(uri, std::regex("\\%20"), " ");
135 if (uri.empty()) {
136 uri = "texture_" + std::to_string(textureId);
137 }
138
139 vcl::TextureDescriptor& texture = mat.textureDescriptor(type);
140
141 texture.path() = uri;
142
143 // set sampler parameters
144 int samplerId = model.textures[textureId].sampler;
145 if (samplerId >= 0) {
146 const tinygltf::Sampler& sampler =
147 model.samplers[samplerId];
148 texture.minFilter() =
150 sampler.minFilter);
151 texture.magFilter() =
153 sampler.magFilter);
154 texture.wrapU() =
155 static_cast<TextureDescriptor::WrapMode>(sampler.wrapS);
156 texture.wrapV() =
157 static_cast<TextureDescriptor::WrapMode>(sampler.wrapT);
158 }
159 else {
160 assert(samplerId == -1);
161 assert(
162 texture.minFilter() ==
164 assert(
165 texture.magFilter() ==
167 assert(
169 assert(
171 }
172
173 // if the image is valid, load it to the texture
174 if (img.image.size() > 0 &&
175 (img.bits == 8 || img.component == 4)) {
176 Image timg(img.image.data(), img.width, img.height);
177 timg.colorSpace() = Material::textureTypeToColorSpace(type);
178
179 m.pushTextureImage(uri, std::move(timg));
180 }
181 }
182 };
183
184 /* Put the data in the mesh */
185
186 if constexpr (HasMaterials<MeshType>) {
187 Material mat;
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(
199 mat, baseColorTextureId, Material::TextureType::BASE_COLOR);
200 loadTextureInMaterial(
201 mat,
202 metallicRoughnessTextureId,
204 loadTextureInMaterial(
205 mat, normalTextureId, Material::TextureType::NORMAL);
206 loadTextureInMaterial(
207 mat, occlusionTextureId, Material::TextureType::OCCLUSION);
208 loadTextureInMaterial(
209 mat, emissiveTextureId, Material::TextureType::EMISSIVE);
210 m.pushMaterial(mat);
211 idx = m.materialsNumber() - 1; // index of the added material
212 if constexpr (HasColor<MeshType>) {
213 // set mesh color to white when materials are used
214 m.color() = Color::White;
215 }
216 }
217 else if constexpr (HasColor<MeshType>) {
218 // base color is set to the mesh color only if the mesh has no
219 // materials
220 m.color() = baseColor;
221 }
222 }
223
224 return idx;
225}
226
227template<MeshConcept MeshType, typename Scalar>
228bool populateGltfVertices(
229 MeshType& m,
230 const Scalar* posArray,
231 uint stride,
232 uint vertNumber)
233{
234 using PositionType = typename MeshType::VertexType::PositionType;
235
236 uint base = m.addVertices(vertNumber);
237
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]);
243 }
244 return true;
245}
246
247template<MeshConcept MeshType, typename Scalar>
248bool populateGltfVNormals(
249 MeshType& m,
250 uint firstVertex,
251 bool enableOptionalComponents,
252 const Scalar* normArray,
253 unsigned int stride,
254 unsigned int vertNumber)
255{
256 if constexpr (HasPerVertexNormal<MeshType>) {
257 using NormalType = typename MeshType::VertexType::NormalType;
258
259 if (enableOptionalComponents)
260 enableIfPerVertexNormalOptional(m);
261
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]);
268 }
269 return true;
270 }
271 else {
272 return false;
273 }
274 }
275 else {
276 return false;
277 }
278}
279
280template<MeshConcept MeshType, typename Scalar>
281bool populateGltfVTangents(
282 MeshType& m,
283 uint firstVertex,
284 bool enableOptionalComponents,
285 const Scalar* tangArray,
286 unsigned int stride,
287 unsigned int vertNumber)
288{
289 if constexpr (HasPerVertexTangent<MeshType>) {
290 using TangentType = typename MeshType::VertexType::TangentType;
291
292 if (enableOptionalComponents)
293 enableIfPerVertexTangentOptional(m);
294
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;
303 }
304 return true;
305 }
306 else {
307 return false;
308 }
309 }
310 else {
311 return false;
312 }
313}
314
315template<MeshConcept MeshType, typename Scalar>
316bool populateGltfVColors(
317 MeshType& m,
318 uint firstVertex,
319 bool enableOptionalComponents,
320 const Scalar* colorArray,
321 unsigned int stride,
322 unsigned int vertNumber,
323 bool colorWithAlpha)
324{
325 unsigned int nElemns = colorWithAlpha ? 4 : 3;
326 if constexpr (HasPerVertexColor<MeshType>) {
327 if (enableOptionalComponents)
328 enableIfPerVertexColorOptional(m);
329
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;
336 vcl::Color c;
337 if constexpr (!std::is_floating_point<Scalar>::value) {
338 uint alpha = nElemns == 4 ? colorBase[3] : 255;
339 c = vcl::Color(
340 colorBase[0], colorBase[1], colorBase[2], alpha);
341 }
342 else {
343 uint alpha = nElemns == 4 ? colorBase[3] * 255 : 255;
344 c = vcl::Color(
345 colorBase[0] * 255,
346 colorBase[1] * 255,
347 colorBase[2] * 255,
348 alpha);
349 }
350 m.vertex(vi).color() = c;
351 }
352 return true;
353 }
354 else {
355 return false;
356 }
357 }
358 else {
359 return false;
360 }
361}
362
363template<MeshConcept MeshType, typename Scalar>
364bool populateGltfVTextCoords(
365 MeshType& m,
366 uint firstVertex,
367 bool enableOptionalComponents,
368 const Scalar* textCoordArray,
369 unsigned int stride,
370 unsigned int vertNumber)
371{
372 if constexpr (HasPerVertexTexCoord<MeshType>) {
373 using TexCoordType = typename MeshType::VertexType::TexCoordType;
374
375 if (enableOptionalComponents)
376 enableIfPerVertexTexCoordOptional(m);
377
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);
382
383 m.vertex(firstVertex + i).texCoord() =
384 TexCoordType(textCoordBase[0], 1 - textCoordBase[1]);
385 }
386 return true;
387 }
388 else {
389 return false;
390 }
391 }
392 else {
393 return false;
394 }
395}
396
397template<MeshConcept MeshType, typename Scalar>
398bool populateGltfTriangles(
399 MeshType& m,
400 uint firstVertex,
401 const Scalar* triArray,
402 uint triNumber)
403{
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>) {
410 f.resizeVertices(3);
411 }
412 for (int j = 0; j < 3; ++j) {
413 f.setVertex(j, firstVertex + triArray[i + j]);
414 }
415 }
416 }
417 else {
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);
424 }
425 }
426 }
427 return true;
428 }
429 else {
430 return false;
431 }
432}
433
447template<typename Scalar, MeshConcept MeshType>
448bool populateGltfAttr(
449 GltfAttrType attr,
450 MeshType& m,
451 uint firstVertex,
452 bool enableOptionalComponents,
453 const Scalar* array,
454 unsigned int stride,
455 unsigned int number,
456 bool colorWithAlpha = true)
457{
458 using enum GltfAttrType;
459
460 switch (attr) {
461 case POSITION: return populateGltfVertices(m, array, stride, number);
462 case NORMAL:
463 return populateGltfVNormals(
464 m, firstVertex, enableOptionalComponents, array, stride, number);
465 case COLOR_0:
466 return populateGltfVColors(
467 m,
468 firstVertex,
469 enableOptionalComponents,
470 array,
471 stride,
472 number,
473 colorWithAlpha);
474 case TEXCOORD_0:
475 return populateGltfVTextCoords(
476 m, firstVertex, enableOptionalComponents, array, stride, number);
477 case TANGENT:
478 return populateGltfVTangents(
479 m, firstVertex, enableOptionalComponents, array, stride, number);
480 case INDICES:
481 return populateGltfTriangles(m, firstVertex, array, number / 3);
482 default: return false;
483 }
484}
485
502template<MeshConcept MeshType>
503bool loadGltfAttribute(
504 MeshType& m,
505 uint startingVertex,
506 bool enableOptionalComponents,
507 const tinygltf::Model& model,
508 const tinygltf::Primitive& p,
509 GltfAttrType attr)
510{
511 using enum GltfAttrType;
512
513 const tinygltf::Accessor* accessor = nullptr;
514
515 // get the accessor associated to the attribute
516 if (attr != INDICES) {
517 auto it = p.attributes.find(GLTF_ATTR_STR[toUnderlying(attr)]);
518
519 if (it != p.attributes.end()) { // accessor found
520 accessor = &model.accessors[it->second];
521 }
522 else if (attr == POSITION) { // if we were looking for POSITION and
523 // didn't find any
524 throw std::runtime_error("File has not 'Position' attribute");
525 }
526 }
527 else { // if the attribute is triangle indices
528 // if the mode is GL_TRIANGLES and we have triangle indices
529 if (p.mode == TINYGLTF_MODE_TRIANGLES && p.indices >= 0 &&
530 (uint) p.indices < model.accessors.size()) {
531 accessor = &model.accessors[p.indices];
532 }
533 }
534
535 // if we found an accessor of the attribute
536 if (accessor) {
537 // bufferview: contains infos on how to access buffer with the accessor
538 const tinygltf::BufferView& posbw =
539 model.bufferViews[accessor->bufferView];
540
541 // data of the whole buffer (vector of bytes);
542 // may contain also other data not associated to our attribute
543 const std::vector<unsigned char>& posdata =
544 model.buffers[posbw.buffer].data;
545
546 // offset where the data of the attribute starts
547 uint posOffset = posbw.byteOffset + accessor->byteOffset;
548 // hack:
549 // if the attribute is a color, textid is used to tell the size of the
550 // color (3 or 4 components)
551 bool colorWithAlpha = true;
552 if (attr == COLOR_0) {
553 if (accessor->type == TINYGLTF_TYPE_VEC3)
554 colorWithAlpha = false;
555 }
556
557 const uint elementSize =
558 tinygltf::GetNumComponentsInType(accessor->type) *
559 tinygltf::GetComponentSizeInBytes(accessor->componentType);
560 const uint stride =
561 (posbw.byteStride > elementSize) ? posbw.byteStride : elementSize;
562
563 // if data is float
564 if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
565 // get the starting point of the data as float pointer
566 const float* posArray = (const float*) (posdata.data() + posOffset);
567 return populateGltfAttr(
568 attr,
569 m,
570 startingVertex,
571 enableOptionalComponents,
572 posArray,
573 stride,
574 accessor->count,
575 colorWithAlpha);
576 }
577 // if data is double
578 else if (accessor->componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
579 // get the starting point of the data as double pointer
580 const double* posArray =
581 (const double*) (posdata.data() + posOffset);
582 return populateGltfAttr(
583 attr,
584 m,
585 startingVertex,
586 enableOptionalComponents,
587 posArray,
588 stride,
589 accessor->count,
590 colorWithAlpha);
591 }
592 // if data is ubyte
593 else if (
594 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
595 // get the starting point of the data as uchar pointer
596 const unsigned char* triArray =
597 (const unsigned char*) (posdata.data() + posOffset);
598 return populateGltfAttr(
599 attr,
600 m,
601 startingVertex,
602 enableOptionalComponents,
603 triArray,
604 stride,
605 accessor->count,
606 colorWithAlpha);
607 }
608 // if data is ushort
609 else if (
610 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
611 // get the starting point of the data as ushort pointer
612 const unsigned short* triArray =
613 (const unsigned short*) (posdata.data() + posOffset);
614 return populateGltfAttr(
615 attr,
616 m,
617 startingVertex,
618 enableOptionalComponents,
619 triArray,
620 stride,
621 accessor->count,
622 colorWithAlpha);
623 }
624 // if data is uint
625 else if (
626 accessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
627 // get the starting point of the data as uint pointer
628 const uint* triArray = (const uint*) (posdata.data() + posOffset);
629 return populateGltfAttr(
630 attr,
631 m,
632 startingVertex,
633 enableOptionalComponents,
634 triArray,
635 stride,
636 accessor->count,
637 colorWithAlpha);
638 }
639 }
640 // if accessor not found and attribute is indices, it means that
641 // the mesh is not indexed, and triplets of contiguous vertices
642 // generate triangles
643 else if (attr == INDICES) {
644 // avoid explicitly the point clouds
645 if (p.mode != TINYGLTF_MODE_POINTS) {
646 // this case is managed when passing nullptr as data
647 return populateGltfAttr<unsigned char>(
648 attr,
649 m,
650 startingVertex,
651 enableOptionalComponents,
652 nullptr,
653 0,
654 0);
655 }
656 }
657 return false;
658}
659
666template<
667 MeshConcept MeshType,
668 Matrix44Concept MatrixType,
669 LoggerConcept LogType>
670void loadGltfMeshPrimitive(
671 MeshType& m,
672 MeshInfo& info,
673 const tinygltf::Model& model,
674 const tinygltf::Primitive& p,
675 const MatrixType& transf,
676 const LoadSettings& settings,
677 LogType& log)
678{
679 int materialId = loadGltfPrimitiveMaterial(m, model, p);
680
681 uint firstVertex = m.vertexNumber();
682
683 // load vertex position attribute
684 loadGltfAttribute(
685 m,
686 firstVertex,
687 settings.enableOptionalComponents,
688 model,
689 p,
690 GltfAttrType::POSITION);
691 info.setVertices();
692
693 bool lvn = loadGltfAttribute(
694 m,
695 firstVertex,
696 settings.enableOptionalComponents,
697 model,
698 p,
699 GltfAttrType::NORMAL);
700 info.setPerVertexNormal(lvn);
701
702 bool lvc = loadGltfAttribute(
703 m,
704 firstVertex,
705 settings.enableOptionalComponents,
706 model,
707 p,
708 GltfAttrType::COLOR_0);
709 if (lvc) {
710 info.setPerVertexColor();
711 }
712
713 bool lvt = loadGltfAttribute(
714 m,
715 firstVertex,
716 settings.enableOptionalComponents,
717 model,
718 p,
719 GltfAttrType::TEXCOORD_0);
720 if (lvt) {
721 info.setPerVertexTexCoord();
722 }
723
724 loadGltfAttribute(
725 m,
726 firstVertex,
727 settings.enableOptionalComponents,
728 model,
729 p,
730 GltfAttrType::TANGENT);
731
732 if constexpr (HasPerVertexMaterialIndex<MeshType>) {
733 if (settings.enableOptionalComponents) {
734 enableIfPerVertexMaterialIndexOptional(m);
735 }
736 if (isPerVertexMaterialIndexAvailable(m)) {
737 uint vnum = m.vertexNumber();
738 for (uint v = firstVertex; v < vnum; ++v) {
739 m.vertex(v).materialIndex() = materialId;
740 }
741 info.setPerVertexMaterialIndex();
742 }
743 }
744
745 if constexpr (HasFaces<MeshType>) {
746 uint firstFace = m.faceNumber();
747 bool lti = loadGltfAttribute(
748 m,
749 firstVertex,
750 settings.enableOptionalComponents,
751 model,
752 p,
753 GltfAttrType::INDICES);
754 if (lti) {
755 info.setTriangleMesh();
756 info.setFaces();
757 info.setPerFaceVertexReferences();
758 }
759 }
760
761 if (HasTransformMatrix<MeshType>) {
762 m.transformMatrix() = transf;
763 }
764 else {
765 // if the mesh does not have a transform matrix, apply the
766 // transformation matrix to the vertices
767 vcl::applyTransformMatrix(m, transf);
768 }
769}
770
779template<
780 MeshConcept MeshType,
781 Matrix44Concept MatrixType,
782 LoggerConcept LogType>
783void gltfLoadMesh(
784 MeshType& m,
785 MeshInfo& info,
786 const tinygltf::Mesh& tm,
787 const tinygltf::Model& model,
788 const MatrixType& transf,
789 const LoadSettings& settings,
790 LogType& log)
791{
792 if constexpr (HasName<MeshType>) {
793 if (!tm.name.empty()) {
794 m.name() = tm.name;
795 }
796 }
797
798 // TODO: fix logger - save the progress state each time a new task is
799 // started
800 // log.startProgress("Reading primitives", tm.primitives.size());
801
802 // for each primitive, load it into the mesh
803 for (uint i = 0; const tinygltf::Primitive& p : tm.primitives) {
804 loadGltfMeshPrimitive(m, info, model, p, transf, settings, log);
805 // log.progress(++i);
806 }
807
808 // log.endProgress();
809
810 log.log(
811 "Loaded mesh '" + tm.name + "' with " +
812 std::to_string(tm.primitives.size()) + " primitives.",
813 LogType::LogLevel::MESSAGE_LOG);
814}
815
816} // namespace vcl::detail
817
818#endif // VCL_IO_MESH_GLTF_DETAIL_LOAD_MESH_H
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
MagnificationFilter
Defines the texture magnification filter modes, following the glTF 2.0 specification....
Definition texture_descriptor.h:70
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