1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "gltf/gltf2_util.h"
17 
18 #include <algorithm>
19 #include <cinttypes>
20 #include <cstring>
21 
22 #include <base/containers/fixed_string.h>
23 #include <base/util/base64_decode.h>
24 #include <core/io/intf_file_manager.h>
25 #include <core/log.h>
26 #include <core/namespace.h>
27 
28 CORE3D_BEGIN_NAMESPACE()
29 using namespace BASE_NS;
30 using namespace CORE_NS;
31 
32 namespace GLTF2 {
33 namespace {
34 using ByteBuffer = vector<uint8_t>;
35 
Read(const uint8_t * src,uint32_t componentByteSize,uint32_t componentCount,uint32_t elementSize,size_t byteStride,uint32_t count)36 vector<uint8_t> Read(const uint8_t* src, uint32_t componentByteSize, uint32_t componentCount, uint32_t elementSize,
37     size_t byteStride, uint32_t count)
38 {
39     vector<uint8_t> result;
40 
41     if (elementSize > 0u) {
42         const size_t sourceSize = componentCount * componentByteSize * count;
43         result.reserve(sourceSize);
44 
45         if (elementSize == byteStride || byteStride == 0u) {
46             result.insert(result.end(), src, src + sourceSize);
47         } else {
48             const uint8_t* source = src;
49 
50             for (size_t i = 0u; i < count; ++i) {
51                 result.insert(result.end(), source, source + elementSize);
52                 source += byteStride;
53             }
54         }
55     }
56 
57     return result;
58 }
59 
Read(Accessor const & accessor)60 vector<uint8_t> Read(Accessor const& accessor)
61 {
62     if (!accessor.bufferView->data) {
63         return {};
64     }
65 
66     const size_t startOffset = accessor.bufferView->byteOffset + accessor.byteOffset;
67     const size_t bufferLength = accessor.bufferView->buffer->byteLength;
68     const size_t bufferRemaining = bufferLength - startOffset;
69 
70     const uint8_t* src = accessor.bufferView->data + accessor.byteOffset;
71 
72     const uint32_t componentByteSize = GetComponentByteSize(accessor.componentType);
73     const uint32_t componentCount = GetComponentsCount(accessor.type);
74     const uint32_t elementSize = componentCount * componentByteSize;
75     const size_t byteStride = accessor.bufferView->byteStride;
76     const uint32_t count = accessor.count;
77 
78     size_t readBytes = 0u;
79 
80     if (elementSize == byteStride || byteStride == 0u) {
81         readBytes = accessor.count * elementSize;
82     } else {
83         readBytes = (accessor.count - 1) * byteStride + elementSize;
84     }
85 
86     if (bufferRemaining < readBytes) {
87         // Avoid buffer overflow.
88         return vector<uint8_t>();
89     }
90 
91     return Read(src, componentByteSize, componentCount, elementSize, byteStride, count);
92 }
93 
94 template<class T>
CopySparseElements(ByteBuffer & destination,ByteBuffer & source,ByteBuffer & indices,uint32_t elementSize,size_t count)95 void CopySparseElements(
96     ByteBuffer& destination, ByteBuffer& source, ByteBuffer& indices, uint32_t elementSize, size_t count)
97 {
98     T* indicesPtr = reinterpret_cast<T*>(indices.data());
99     auto const end = ptrdiff_t(destination.data() + destination.size());
100     for (size_t i = 0u; i < count; ++i) {
101         const uint8_t* sourcePtr = source.data() + (i * elementSize);
102         uint8_t* destinationPtr = destination.data() + (indicesPtr[i] * elementSize);
103         auto const left = end - ptrdiff_t(destinationPtr);
104         if (left > 0) {
105             if (!CloneData(destinationPtr, size_t(left), sourcePtr, elementSize)) {
106                 CORE_LOG_E("Copying of sparseElements failed.");
107             }
108         }
109     }
110 }
111 
LoadBuffer(Data const & data,Buffer & buffer,IFileManager & fileManager)112 BufferLoadResult LoadBuffer(Data const& data, Buffer& buffer, IFileManager& fileManager)
113 {
114     if (IsDataURI(buffer.uri)) {
115         string_view type;
116         if (!DecodeDataURI(buffer.data, buffer.uri, 0u, false, type)) {
117             return BufferLoadResult { false, "Failed to decode data uri: " + buffer.uri + '\n' };
118         }
119     } else {
120         uint32_t offset;
121         string_view uri;
122         if (buffer.uri.size()) {
123             uri = buffer.uri;
124             offset = 0u;
125         } else if (data.defaultResourcesOffset >= 0) {
126             uri = data.defaultResources;
127             offset = static_cast<uint32_t>(data.defaultResourcesOffset);
128         } else {
129             return BufferLoadResult { false, "Failed to open buffer: " + buffer.uri + '\n' };
130         }
131 
132         IFile::Ptr file;
133         IFile* filePtr = nullptr;
134         if (data.memoryFile_) {
135             filePtr = data.memoryFile_.get();
136         } else {
137             string fileName;
138             fileName.reserve(data.filepath.size() + 1u + uri.size());
139             fileName.append(data.filepath);
140             fileName.append("/");
141             fileName.append(uri);
142 
143             file = fileManager.OpenFile(fileName);
144             filePtr = file.get();
145         }
146         if (!filePtr) {
147             return BufferLoadResult { false, "Failed open uri: " + buffer.uri + '\n' };
148         }
149 
150         filePtr->Seek(offset);
151 
152         // Check that buffer does not overflow over the file boundaries.
153         const auto length = filePtr->GetLength();
154         const auto remaining = length - filePtr->GetPosition();
155         if (remaining < buffer.byteLength) {
156             // Clamp buffer to file boundary.
157             CORE_LOG_W("Buffer size %zu larger than file size (%" PRIu64 " remaining).", buffer.byteLength, remaining);
158             buffer.byteLength = static_cast<size_t>(remaining);
159         }
160 
161         buffer.data.resize(buffer.byteLength);
162 
163         if (filePtr->Read(buffer.data.data(), buffer.byteLength) != buffer.byteLength) {
164             return BufferLoadResult { false, "Failed to read buffer: " + buffer.uri + '\n' };
165         }
166     }
167 
168     return BufferLoadResult {};
169 }
170 
LoadSparseAccessor(Accessor const & accessor,GLTFLoadDataResult & result)171 void LoadSparseAccessor(Accessor const& accessor, GLTFLoadDataResult& result)
172 {
173     auto const& sparseIndicesBufferView = accessor.sparse.indices.bufferView;
174     vector<uint8_t> sparseIndicesData;
175     if (sparseIndicesBufferView->buffer) {
176         const uint8_t* src = sparseIndicesBufferView->data + accessor.sparse.indices.byteOffset;
177 
178         auto const componentCount = 1u;
179         auto const componentByteSize = GetComponentByteSize(accessor.sparse.indices.componentType);
180         auto const elementSize = componentCount * componentByteSize;
181         auto const byteStride = accessor.bufferView->byteStride;
182         auto const count = accessor.sparse.count;
183 
184         sparseIndicesData = Read(src, componentByteSize, componentCount, elementSize, byteStride, count);
185     }
186 
187     auto const& sparseValuesBufferView = accessor.sparse.values.bufferView;
188     if (sparseValuesBufferView->buffer) {
189         vector<uint8_t> sourceData;
190 
191         const uint8_t* src = sparseValuesBufferView->data + accessor.sparse.indices.byteOffset;
192         auto const componentCount = GetComponentsCount(accessor.type);
193         auto const componentByteSize = GetComponentByteSize(accessor.componentType);
194         auto const elementSize = componentCount * componentByteSize;
195         auto const byteStride = accessor.bufferView->byteStride;
196         auto const count = accessor.sparse.count;
197 
198         sourceData = Read(src, componentByteSize, componentCount, elementSize, byteStride, count);
199 
200         switch (accessor.sparse.indices.componentType) {
201             case ComponentType::UNSIGNED_BYTE: {
202                 CopySparseElements<uint8_t>(
203                     result.data, sourceData, sparseIndicesData, elementSize, accessor.sparse.count);
204                 break;
205             }
206 
207             case ComponentType::UNSIGNED_SHORT: {
208                 CopySparseElements<uint16_t>(
209                     result.data, sourceData, sparseIndicesData, elementSize, accessor.sparse.count);
210                 break;
211             }
212 
213             case ComponentType::UNSIGNED_INT: {
214                 CopySparseElements<uint32_t>(
215                     result.data, sourceData, sparseIndicesData, elementSize, accessor.sparse.count);
216                 break;
217             }
218 
219             case ComponentType::BYTE:
220             case ComponentType::SHORT:
221             case ComponentType::INT:
222             case ComponentType::FLOAT:
223             default:
224                 result.error += "invalid accessor.sparse.indices.componentType\n";
225                 result.success = false;
226                 break;
227         }
228     }
229 }
230 
ReadUriToVector(const string_view filePath,IFileManager & fileManager,string_view const & uri,vector<uint8_t> & out)231 bool ReadUriToVector(
232     const string_view filePath, IFileManager& fileManager, string_view const& uri, vector<uint8_t>& out)
233 {
234     string filepath;
235     filepath.reserve(filePath.size() + 1u + uri.size());
236     filepath += filePath;
237     filepath += '/';
238     filepath += uri;
239     auto file = fileManager.OpenFile(filepath);
240     if (file) {
241         const size_t count = static_cast<size_t>(file->GetLength());
242         out.resize(count);
243         return file->Read(out.data(), count) == count;
244     }
245     return false;
246 }
247 } // namespace
248 
249 // Helper functions to access GLTF2 data
GetAttributeType(const string_view dataType,AttributeBase & out)250 bool GetAttributeType(const string_view dataType, AttributeBase& out)
251 {
252     const char* data = dataType.data();
253     const unsigned int size = static_cast<unsigned int>(dataType.size());
254 
255     AttributeBase attribute;
256     attribute.type = AttributeType::INVALID;
257     attribute.index = 0u;
258     if (data == nullptr) {
259         return false;
260     }
261 
262     /*
263     POSITION, NORMAL, TANGENT, TEXCOORD_0, TEXCOORD_1, COLOR_0, JOINTS_0, and WEIGHTS_0
264     */
265     if (dataType == "POSITION") {
266         attribute.type = AttributeType::POSITION;
267     } else if (dataType == "NORMAL") {
268         attribute.type = AttributeType::NORMAL;
269     } else if (dataType == "TANGENT") {
270         attribute.type = AttributeType::TANGENT;
271     } else if (dataType.find("TEXCOORD_") == 0u) {
272         attribute.type = AttributeType::TEXCOORD;
273         attribute.index = 0u;
274 
275         if (size > 9u) {
276             attribute.index = (unsigned int)(data[9u] - '0'); // NOTE: check if size is over 10 => index more than 9
277         }
278     } else if (dataType.find("COLOR_") == 0u) {
279         attribute.type = AttributeType::COLOR;
280         attribute.index = 0u;
281 
282         if (size > 6u) {
283             attribute.index = (unsigned int)(data[6u] - '0'); // NOTE: check if size is over 7 => index more than 9
284         }
285     } else if (dataType.find("JOINTS_") == 0u) {
286         attribute.type = AttributeType::JOINTS;
287         attribute.index = 0u;
288 
289         if (size > 7u) {
290             attribute.index = (unsigned int)(data[7u] - '0'); // NOTE: check if size is over 8 => index more than 9
291         }
292     } else if (dataType.find("WEIGHTS_") == 0u) {
293         attribute.type = AttributeType::WEIGHTS;
294         attribute.index = 0u;
295 
296         if (size > 8u) {
297             attribute.index = (unsigned int)(data[8u] - '0'); // NOTE: check if size is over 9 => index more than 9
298         }
299     } else {
300         return false;
301     }
302 
303     if (attribute.index > 9u) {
304         return false;
305     }
306 
307     out = attribute;
308     return true;
309 }
310 
GetMimeType(const string_view type,MimeType & out)311 bool GetMimeType(const string_view type, MimeType& out)
312 {
313     out = MimeType::INVALID;
314 
315     if (type == "image/jpeg") {
316         out = MimeType::JPEG;
317     } else if (type == "image/png") {
318         out = MimeType::PNG;
319     } else if (type == "image/ktx") {
320         out = MimeType::KTX;
321     } else if (type == "image/ktx2") {
322         out = MimeType::KTX2;
323     } else if (type == "image/vnd-ms.dds") {
324         out = MimeType::DDS;
325     } else if (type == "application/octet-stream") {
326         out = MimeType::KTX; // Probably it's a KTX file, bundled by using an external application.
327     }
328     return out != MimeType::INVALID;
329 }
330 
GetDataType(const string_view dataType,DataType & out)331 bool GetDataType(const string_view dataType, DataType& out)
332 {
333     out = DataType::INVALID;
334 
335     if (dataType == "SCALAR") {
336         out = DataType::SCALAR;
337     } else if (dataType == "VEC2") {
338         out = DataType::VEC2;
339     } else if (dataType == "VEC3") {
340         out = DataType::VEC3;
341     } else if (dataType == "VEC4") {
342         out = DataType::VEC4;
343     } else if (dataType == "MAT2") {
344         out = DataType::MAT2;
345     } else if (dataType == "MAT3") {
346         out = DataType::MAT3;
347     } else if (dataType == "MAT4") {
348         out = DataType::MAT4;
349     }
350     return out != DataType::INVALID;
351 }
352 
GetCameraType(const string_view type,CameraType & out)353 bool GetCameraType(const string_view type, CameraType& out)
354 {
355     out = CameraType::INVALID;
356 
357     if (type == "perspective") {
358         out = CameraType::PERSPECTIVE;
359     } else if (type == "orthographic") {
360         out = CameraType::ORTHOGRAPHIC;
361     }
362     return out != CameraType::INVALID;
363 }
364 
GetAlphaMode(const string_view dataType,AlphaMode & out)365 bool GetAlphaMode(const string_view dataType, AlphaMode& out)
366 {
367     out = AlphaMode::OPAQUE;
368 
369     bool result = true;
370 
371     if (dataType == "BLEND") {
372         out = AlphaMode::BLEND;
373     } else if (dataType == "MASK") {
374         out = AlphaMode::MASK;
375     } else if (dataType == "OPAQUE") {
376         out = AlphaMode::OPAQUE;
377     } else {
378         result = false;
379     }
380     return result;
381 }
382 
GetBlendMode(const string_view dataType,BlendMode & out)383 bool GetBlendMode(const string_view dataType, BlendMode& out)
384 {
385     out = BlendMode::REPLACE;
386 
387     bool result = true;
388 
389     if (dataType == "transparentColor") {
390         out = BlendMode::TRANSPARENT_COLOR;
391     } else if (dataType == "transparentAlpha") {
392         out = BlendMode::TRANSPARENT_ALPHA;
393     } else if (dataType == "add") {
394         out = BlendMode::ADD;
395     } else if (dataType == "modulate") {
396         out = BlendMode::MODULATE;
397     } else if (dataType == "replace") {
398         out = BlendMode::REPLACE;
399     } else if (dataType == "none") {
400         out = BlendMode::NONE;
401     } else {
402         result = false;
403     }
404     return result;
405 }
406 
GetAnimationInterpolation(string_view interpolation,AnimationInterpolation & out)407 bool GetAnimationInterpolation(string_view interpolation, AnimationInterpolation& out)
408 {
409     // Default type is linear, this is not required attribute.
410     out = AnimationInterpolation::LINEAR;
411 
412     if (interpolation == "LINEAR") {
413         out = AnimationInterpolation::LINEAR;
414     } else if (interpolation == "STEP") {
415         out = AnimationInterpolation::STEP;
416     } else if (interpolation == "CUBICSPLINE") {
417         out = AnimationInterpolation::SPLINE;
418     }
419     return true;
420 }
421 
GetAnimationPath(string_view path,AnimationPath & out)422 bool GetAnimationPath(string_view path, AnimationPath& out)
423 {
424     bool result = true;
425 
426     if (path == "translation") {
427         out = AnimationPath::TRANSLATION;
428     } else if (path == "rotation") {
429         out = AnimationPath::ROTATION;
430     } else if (path == "scale") {
431         out = AnimationPath::SCALE;
432     } else if (path == "weights") {
433         out = AnimationPath::WEIGHTS;
434     } else if (path == "visible") {
435         out = AnimationPath::VISIBLE;
436     } else if (path == "material.opacity") {
437         out = AnimationPath::OPACITY;
438     } else {
439         result = false;
440     }
441     return result;
442 }
443 
444 namespace {
445 constexpr const char* ATTRIBUTE_TYPES[] = { "NORMAL", "POSITION", "TANGENT" };
446 constexpr const char* TEXCOORD_ATTRIBUTES[] = { "TEXCOORD_0", "TEXCOORD_1", "TEXCOORD_2", "TEXCOORD_3", "TEXCOORD_4" };
447 constexpr const char* COLOR_ATTRIBUTES[] = { "COLOR_0", "COLOR_1", "COLOR_2", "COLOR_3", "COLOR_4" };
448 constexpr const char* JOINTS_ATTRIBUTES[] = { "JOINTS_0", "JOINTS_1", "JOINTS_2", "JOINTS_3", "JOINTS_4" };
449 constexpr const char* WEIGHTS_ATTRIBUTES[] = { "WEIGHTS_0", "WEIGHTS_1", "WEIGHTS_2", "WEIGHTS_3", "WEIGHTS_4" };
450 constexpr const char* ATTRIBUTE_INVALID = "INVALID";
451 } // namespace
452 
GetAttributeType(AttributeBase dataType)453 string_view GetAttributeType(AttributeBase dataType)
454 {
455     if ((dataType.type < AttributeType::NORMAL)) {
456     } else if (dataType.type <= AttributeType::TANGENT) {
457         return ATTRIBUTE_TYPES[static_cast<int>(dataType.type)];
458     } else if (dataType.type == AttributeType::TEXCOORD) {
459         if (dataType.index < countof(TEXCOORD_ATTRIBUTES)) {
460             return TEXCOORD_ATTRIBUTES[dataType.index];
461         }
462     } else if (dataType.type == AttributeType::COLOR) {
463         if (dataType.index < countof(COLOR_ATTRIBUTES)) {
464             return COLOR_ATTRIBUTES[dataType.index];
465         }
466     } else if (dataType.type == AttributeType::JOINTS) {
467         if (dataType.index < countof(JOINTS_ATTRIBUTES)) {
468             return JOINTS_ATTRIBUTES[dataType.index];
469         }
470     } else if (dataType.type == AttributeType::WEIGHTS) {
471         if (dataType.index < countof(WEIGHTS_ATTRIBUTES)) {
472             return WEIGHTS_ATTRIBUTES[dataType.index];
473         }
474     }
475     return ATTRIBUTE_INVALID;
476 }
477 
GetMimeType(MimeType type)478 string_view GetMimeType(MimeType type)
479 {
480     switch (type) {
481         case MimeType::JPEG:
482             return "image/jpeg";
483         case MimeType::PNG:
484             return "image/png";
485         case MimeType::KTX:
486             return "image/ktx";
487         case MimeType::DDS:
488             return "image/vnd-ms.dds";
489         case MimeType::KTX2:
490             return "image/ktx2";
491 
492         case MimeType::INVALID:
493         default:
494             break;
495     }
496     return "INVALID";
497 }
498 
GetDataType(DataType dataType)499 string_view GetDataType(DataType dataType)
500 {
501     switch (dataType) {
502         case DataType::SCALAR:
503             return "SCALAR";
504         case DataType::VEC2:
505             return "VEC2";
506         case DataType::VEC3:
507             return "VEC3";
508         case DataType::VEC4:
509             return "VEC4";
510         case DataType::MAT2:
511             return "MAT2";
512         case DataType::MAT3:
513             return "MAT3";
514         case DataType::MAT4:
515             return "MAT4";
516         case DataType::INVALID:
517         default:
518             break;
519     }
520     return "INVALID";
521 }
522 
GetCameraType(CameraType type)523 string_view GetCameraType(CameraType type)
524 {
525     switch (type) {
526         case CameraType::PERSPECTIVE:
527             return "perspective";
528         case CameraType::ORTHOGRAPHIC:
529             return "orthographic";
530         case CameraType::INVALID:
531         default:
532             break;
533     }
534 
535     return "INVALID";
536 }
537 
538 #if defined(GLTF2_EXTENSION_KHR_LIGHTS) || defined(GLTF2_EXTENSION_KHR_LIGHTS_PBR)
GetLightType(const string_view type,LightType & out)539 bool GetLightType(const string_view type, LightType& out)
540 {
541     out = LightType::INVALID;
542 
543     if (type == "directional") {
544         out = LightType::DIRECTIONAL;
545     } else if (type == "point") {
546         out = LightType::POINT;
547     } else if (type == "spot") {
548         out = LightType::SPOT;
549     } else if (type == "ambient") {
550         out = LightType::AMBIENT;
551     }
552     return out != LightType::INVALID;
553 }
554 
GetLightType(LightType type)555 string_view GetLightType(LightType type)
556 {
557     switch (type) {
558         case LightType::DIRECTIONAL:
559             return "directional";
560         case LightType::POINT:
561             return "point";
562         case LightType::SPOT:
563             return "spot";
564         case LightType::AMBIENT:
565             return "ambient";
566         case LightType::INVALID:
567         default:
568             break;
569     }
570 
571     return "INVALID";
572 }
573 #endif
574 
GetAlphaMode(AlphaMode aMode)575 string_view GetAlphaMode(AlphaMode aMode)
576 {
577     switch (aMode) {
578         case AlphaMode::BLEND:
579             return "BLEND";
580         case AlphaMode::MASK:
581             return "MASK";
582         case AlphaMode::OPAQUE:
583         default:
584             return "OPAQUE";
585     }
586 }
587 
GetBlendMode(BlendMode data)588 string_view GetBlendMode(BlendMode data)
589 {
590     switch (data) {
591         case BlendMode::TRANSPARENT_ALPHA:
592             return "transparentAlpha";
593         case BlendMode::TRANSPARENT_COLOR:
594             return "transparentColor";
595         case BlendMode::ADD:
596             return "add";
597         case BlendMode::MODULATE:
598             return "modulate";
599 
600         case BlendMode::NONE:
601         case BlendMode::REPLACE:
602         default:
603             return "replace";
604     }
605 }
606 
GetAnimationInterpolation(AnimationInterpolation interpolation)607 string_view GetAnimationInterpolation(AnimationInterpolation interpolation)
608 {
609     switch (interpolation) {
610         case AnimationInterpolation::STEP:
611             return "STEP";
612         default:
613             [[fallthrough]];
614         case AnimationInterpolation::INVALID:
615             [[fallthrough]];
616         case AnimationInterpolation::LINEAR:
617             return "LINEAR";
618         case AnimationInterpolation::SPLINE:
619             return "CUBICSPLINE";
620     }
621 }
622 
GetAnimationPath(AnimationPath path)623 string_view GetAnimationPath(AnimationPath path)
624 {
625     switch (path) {
626         default:
627             [[fallthrough]];
628         case AnimationPath::INVALID:
629             CORE_LOG_W("invalid animation path %d", path);
630             return "translation";
631         case AnimationPath::TRANSLATION:
632             return "translation";
633         case AnimationPath::ROTATION:
634             return "rotation";
635         case AnimationPath::SCALE:
636             return "scale";
637         case AnimationPath::WEIGHTS:
638             return "weights";
639     }
640 }
641 
GetComponentByteSize(ComponentType component)642 uint32_t GetComponentByteSize(ComponentType component)
643 {
644     switch (component) {
645         case ComponentType::BYTE:
646         case ComponentType::UNSIGNED_BYTE:
647             return 1u;
648 
649         case ComponentType::SHORT:
650         case ComponentType::UNSIGNED_SHORT:
651             return 2u;
652 
653         case ComponentType::UNSIGNED_INT:
654         case ComponentType::INT:
655         case ComponentType::FLOAT:
656             return 4u;
657 
658         default:
659             return 0u;
660     }
661 }
662 
GetComponentsCount(DataType type)663 uint32_t GetComponentsCount(DataType type)
664 {
665     switch (type) {
666         case DataType::SCALAR:
667             return 1u;
668         case DataType::VEC2:
669             return 2u;
670         case DataType::VEC3:
671             return 3u;
672         case DataType::VEC4:
673             return 4u;
674         case DataType::MAT2:
675             return 4u;
676         case DataType::MAT3:
677             return 9u;
678         case DataType::MAT4:
679             return 16u;
680 
681         case DataType::INVALID:
682         default:
683             return 0u;
684     }
685 }
686 
GetAlternativeType(ComponentType component,size_t aNewByteCount)687 ComponentType GetAlternativeType(ComponentType component, size_t aNewByteCount)
688 {
689     switch (component) {
690         case ComponentType::UNSIGNED_SHORT:
691         case ComponentType::UNSIGNED_INT:
692         case ComponentType::UNSIGNED_BYTE: {
693             switch (aNewByteCount) {
694                 case 1u:
695                     return ComponentType::UNSIGNED_BYTE;
696                 case 2u:
697                     return ComponentType::UNSIGNED_SHORT;
698                 case 4u:
699                     return ComponentType::UNSIGNED_INT;
700                 default:
701                     break;
702             }
703             break;
704         }
705 
706         case ComponentType::BYTE:
707         case ComponentType::SHORT:
708         case ComponentType::INT: {
709             switch (aNewByteCount) {
710                 case 1u:
711                     return ComponentType::BYTE;
712                 case 2u:
713                     return ComponentType::SHORT;
714                 case 4u:
715                     return ComponentType::INT;
716                 default:
717                     break;
718             }
719             break;
720         }
721 
722         case ComponentType::FLOAT:
723         default:
724             // do nothing
725             break;
726     }
727 
728     return component;
729 }
730 
731 namespace {
732 struct AttributeValidation {
733     array_view<const DataType> dataTypes;
734     array_view<const ComponentType> componentTypes;
735 };
736 struct AttributeValidationErrors {
737     string_view dataTypeError;
738     string_view componentTypeError;
739 };
740 
741 constexpr const DataType NORMAL_DATA_TYPES[] = { DataType::VEC3 };
742 constexpr const ComponentType NORMAL_COMPONENT_TYPES[] = { ComponentType::FLOAT };
743 constexpr const DataType POSITION_DATA_TYPES[] = { DataType::VEC3 };
744 constexpr const ComponentType POSITION_COMPONENT_TYPES[] = { ComponentType::FLOAT };
745 constexpr const DataType TANGENT_DATA_TYPES[] = { DataType::VEC4 };
746 constexpr const ComponentType TANGENT_COMPONENT_TYPES[] = { ComponentType::FLOAT };
747 constexpr const DataType TEXCOORD_DATA_TYPES[] = { DataType::VEC2 };
748 constexpr const ComponentType TEXCOORD_COMPONENT_TYPES[] = { ComponentType::FLOAT, ComponentType::UNSIGNED_BYTE,
749     ComponentType::UNSIGNED_SHORT };
750 constexpr const DataType COLOR_DATA_TYPES[] = { DataType::VEC3, DataType::VEC4 };
751 constexpr const ComponentType COLOR_COMPONENT_TYPES[] = { ComponentType::FLOAT, ComponentType::UNSIGNED_BYTE,
752     ComponentType::UNSIGNED_SHORT };
753 constexpr const DataType JOINTS_DATA_TYPES[] = { DataType::VEC4 };
754 constexpr const ComponentType JOINTS_COMPONENT_TYPES[] = { ComponentType::UNSIGNED_BYTE,
755     ComponentType::UNSIGNED_SHORT };
756 constexpr const DataType WEIGHTS_DATA_TYPES[] = { DataType::VEC4 };
757 constexpr const ComponentType WEIGHTS_COMPONENT_TYPES[] = { ComponentType::FLOAT, ComponentType::UNSIGNED_BYTE,
758     ComponentType::UNSIGNED_SHORT };
759 
760 constexpr const AttributeValidation ATTRIBUTE_VALIDATION[] = {
761     { NORMAL_DATA_TYPES, NORMAL_COMPONENT_TYPES },
762     { POSITION_DATA_TYPES, POSITION_COMPONENT_TYPES },
763     { TANGENT_DATA_TYPES, TANGENT_COMPONENT_TYPES },
764     { TEXCOORD_DATA_TYPES, TEXCOORD_COMPONENT_TYPES },
765     { COLOR_DATA_TYPES, COLOR_COMPONENT_TYPES },
766     { JOINTS_DATA_TYPES, JOINTS_COMPONENT_TYPES },
767     { WEIGHTS_DATA_TYPES, WEIGHTS_COMPONENT_TYPES },
768 };
769 
770 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
771 constexpr const ComponentType NORMAL_COMPONENT_TYPES_Q[] = { ComponentType::BYTE, ComponentType::SHORT,
772     ComponentType::FLOAT };
773 constexpr const ComponentType POSITION_COMPONENT_TYPES_Q[] = { ComponentType::BYTE, ComponentType::UNSIGNED_BYTE,
774     ComponentType::SHORT, ComponentType::UNSIGNED_SHORT, ComponentType::FLOAT };
775 constexpr const ComponentType TANGENT_COMPONENT_TYPES_Q[] = { ComponentType::BYTE, ComponentType::SHORT,
776     ComponentType::FLOAT };
777 constexpr const ComponentType TEXCOORD_COMPONENT_TYPES_Q[] = { ComponentType::BYTE, ComponentType::UNSIGNED_BYTE,
778     ComponentType::SHORT, ComponentType::UNSIGNED_SHORT, ComponentType::FLOAT };
779 
780 constexpr const AttributeValidation ATTRIBUTE_VALIDATION_Q[] = {
781     { NORMAL_DATA_TYPES, NORMAL_COMPONENT_TYPES_Q },
782     { POSITION_DATA_TYPES, POSITION_COMPONENT_TYPES_Q },
783     { TANGENT_DATA_TYPES, TANGENT_COMPONENT_TYPES_Q },
784     { TEXCOORD_DATA_TYPES, TEXCOORD_COMPONENT_TYPES_Q },
785     { COLOR_DATA_TYPES, COLOR_COMPONENT_TYPES },
786     { JOINTS_DATA_TYPES, JOINTS_COMPONENT_TYPES },
787     { WEIGHTS_DATA_TYPES, WEIGHTS_COMPONENT_TYPES },
788 };
789 #endif
790 
791 constexpr const AttributeValidationErrors ATTRIBUTE_VALIDATION_ERRORS[] = {
792     { "NORMAL accessor must use VEC3.", "NORMAL accessor must use FLOAT." },
793     { "POSITION accessor must use VEC3.", "POSITION accessor must use FLOAT." },
794     { "TANGENT accessor must use VEC4.", "TANGENT accessor must use FLOAT." },
795     { "TEXCOORD accessor must use VEC2.", "TEXCOORD accessor must use FLOAT, UNSIGNED_BYTE, or UNSIGNED_SHORT." },
796     { "COLOR accessor must use VEC3 or VEC4.", "COLOR accessor must use FLOAT, UNSIGNED_BYTE, or UNSIGNED_SHORT." },
797     { "JOINTS accessor must use VEC4.", "JOINTS accessor must use UNSIGNED_BYTE or UNSIGNED_SHORT." },
798     { "WEIGHTS accessor must use VEC4.", "WEIGHTS accessor must use FLOAT, UNSIGNED_BYTE, or UNSIGNED_SHORT." },
799 };
800 } // namespace
801 
ValidatePrimitiveAttribute(AttributeType attribute,DataType accessorType,ComponentType accessorComponentType)802 string_view ValidatePrimitiveAttribute(
803     AttributeType attribute, DataType accessorType, ComponentType accessorComponentType)
804 {
805     const auto attributeIndex = static_cast<size_t>(attribute);
806     if (attributeIndex <= countof(ATTRIBUTE_VALIDATION)) {
807         auto& validation = ATTRIBUTE_VALIDATION[attributeIndex];
808         if (std::none_of(validation.dataTypes.begin(), validation.dataTypes.end(),
809                 [accessorType](const DataType& validType) { return validType == accessorType; })) {
810             return ATTRIBUTE_VALIDATION_ERRORS[attributeIndex].dataTypeError;
811         } else if (std::none_of(validation.componentTypes.begin(), validation.componentTypes.end(),
812                         [accessorComponentType](
813                             const ComponentType& validType) { return validType == accessorComponentType; })) {
814             return ATTRIBUTE_VALIDATION_ERRORS[attributeIndex].componentTypeError;
815         }
816     } else {
817         return "invalid attribute";
818     }
819     return string_view();
820 }
821 
ValidateMorphTargetAttribute(AttributeType attribute,DataType accessorType,ComponentType accessorComponentType)822 string_view ValidateMorphTargetAttribute(
823     AttributeType attribute, DataType accessorType, ComponentType accessorComponentType)
824 {
825     switch (attribute) {
826         case AttributeType::NORMAL:
827             if (accessorType != DataType::VEC3) {
828                 return "ValidateMorphTargetAttribute: NORMAL accessor must use VEC3.";
829             }
830             if (accessorComponentType != ComponentType::FLOAT) {
831                 return "ValidateMorphTargetAttribute: NORMAL accessor must use FLOAT.";
832             }
833             break;
834 
835         case AttributeType::POSITION:
836             if (accessorType != DataType::VEC3) {
837                 return "ValidateMorphTargetAttribute: POSITION accessor must use VEC3.";
838             }
839             if (accessorComponentType != ComponentType::FLOAT && accessorComponentType != ComponentType::SHORT) {
840                 return "ValidateMorphTargetAttribute: POSITION accessor must use FLOAT or SHORT.";
841             }
842             break;
843 
844         case AttributeType::TANGENT:
845             if (accessorType != DataType::VEC3) {
846                 return "ValidateMorphTargetAttribute: TANGENT accessor must use VEC3.";
847             }
848             if (accessorComponentType != ComponentType::FLOAT) {
849                 return "ValidateMorphTargetAttribute: TANGENT accessor must use FLOAT.";
850             }
851             break;
852 
853         case AttributeType::INVALID:
854         case AttributeType::TEXCOORD:
855         case AttributeType::COLOR:
856         case AttributeType::JOINTS:
857         case AttributeType::WEIGHTS:
858         default:
859             return "ValidateMorphTargetAttribute: invalid attribute";
860     }
861 
862     return string_view();
863 }
864 
865 #if defined(GLTF2_EXTENSION_KHR_MESH_QUANTIZATION)
ValidatePrimitiveAttributeQuatization(AttributeType attribute,DataType accessorType,ComponentType accessorComponentType)866 string_view ValidatePrimitiveAttributeQuatization(
867     AttributeType attribute, DataType accessorType, ComponentType accessorComponentType)
868 {
869     const auto attributeIndex = static_cast<size_t>(attribute);
870     if (attributeIndex <= countof(ATTRIBUTE_VALIDATION_Q)) {
871         auto& validation = ATTRIBUTE_VALIDATION_Q[attributeIndex];
872         if (std::none_of(validation.dataTypes.begin(), validation.dataTypes.end(),
873                 [accessorType](const DataType& validType) { return validType == accessorType; })) {
874             return ATTRIBUTE_VALIDATION_ERRORS[attributeIndex].dataTypeError;
875         } else if (std::none_of(validation.componentTypes.begin(), validation.componentTypes.end(),
876                         [accessorComponentType](
877                             const ComponentType& validType) { return validType == accessorComponentType; })) {
878             return ATTRIBUTE_VALIDATION_ERRORS[attributeIndex].componentTypeError;
879         }
880     } else {
881         return "invalid attribute";
882     }
883     return string_view();
884 }
885 
ValidateMorphTargetAttributeQuantization(AttributeType attribute,DataType accessorType,ComponentType accessorComponentType)886 string_view ValidateMorphTargetAttributeQuantization(
887     AttributeType attribute, DataType accessorType, ComponentType accessorComponentType)
888 {
889     switch (attribute) {
890         case AttributeType::NORMAL:
891             if (accessorType != DataType::VEC3) {
892                 return "ValidateMorphTargetAttribute: NORMAL accessor must use VEC3.";
893             }
894             if (accessorComponentType != ComponentType::FLOAT && accessorComponentType != ComponentType::BYTE &&
895                 accessorComponentType != ComponentType::SHORT) {
896                 return "ValidateMorphTargetAttribute: NORMAL accessor must use FLOAT, BYTE or SHORT.";
897             }
898             break;
899 
900         case AttributeType::POSITION:
901             if (accessorType != DataType::VEC3) {
902                 return "ValidateMorphTargetAttribute: POSITION accessor must use VEC3.";
903             }
904             if (accessorComponentType != ComponentType::FLOAT && accessorComponentType != ComponentType::BYTE &&
905                 accessorComponentType != ComponentType::SHORT) {
906                 return "ValidateMorphTargetAttribute: POSITION accessor must use FLOAT, BYTE or SHORT.";
907             }
908             break;
909 
910         case AttributeType::TANGENT:
911             if (accessorType != DataType::VEC3) {
912                 return "ValidateMorphTargetAttribute: TANGENT accessor must use VEC3.";
913             }
914             if (accessorComponentType != ComponentType::FLOAT && accessorComponentType != ComponentType::BYTE &&
915                 accessorComponentType != ComponentType::SHORT) {
916                 return "ValidateMorphTargetAttribute: TANGENT accessor must use FLOAT, BYTE or SHORT.";
917             }
918             break;
919 
920         case AttributeType::INVALID:
921         case AttributeType::TEXCOORD:
922         case AttributeType::COLOR:
923         case AttributeType::JOINTS:
924         case AttributeType::WEIGHTS:
925         default:
926             return "ValidateMorphTargetAttribute: invalid attribute";
927     }
928 
929     return string_view();
930 }
931 #endif
932 
SplitFilename(const string_view source,string_view & base,string_view & path)933 void SplitFilename(const string_view source, string_view& base, string_view& path)
934 {
935     size_t slash = source.rfind('\\');
936     if (slash == string_view::npos) {
937         slash = source.rfind('/');
938     }
939     if (slash == string_view::npos) {
940         base = source;
941         path = {};
942         return;
943     }
944     path = source.substr(0u, slash);
945     base = source.substr(slash + 1u);
946 }
947 
SplitBaseFilename(const string_view source,string_view & name,string_view & extension)948 void SplitBaseFilename(const string_view source, string_view& name, string_view& extension)
949 {
950     const size_t slash = source.rfind('.');
951     if (slash == string_view::npos) {
952         extension = {};
953         name = source;
954         return;
955     }
956     name = source.substr(0u, slash);
957     extension = source.substr(slash + 1u);
958 }
959 
ParseDataUri(const string_view in,size_t & offsetToData)960 string_view ParseDataUri(const string_view in, size_t& offsetToData)
961 {
962     offsetToData = 0u;
963     // see: https://en.wikipedia.org/wiki/Data_URI_scheme
964     auto const scheme = in.substr(0u, 5u);
965     if (scheme != "data:") {
966         return {}; // scheme is not data:
967     }
968     auto pos = in.find(",");
969     if (pos == string_view::npos) {
970         return {}; // no start of data.
971     }
972     auto const mediaType = in.substr(5u, pos - 5u);
973     offsetToData = pos + 1u;
974     pos = mediaType.find_last_of(
975         ';'); // technically media-type could contain parameters. but the last parameter should be base64 here
976     if (pos == string_view::npos) {
977         return {}; // no encoding defined.
978     }
979     auto const encoding = mediaType.substr(pos + 1u);
980     if (encoding != "base64") {
981         return {};
982     }
983 
984     // NOTE: 0 to pos would return media-type without the base64 encoding option. (which leaves all the optional
985     // parameters to be handled by user)
986     pos = mediaType.find_first_of(';');
987     return mediaType.substr(0u, pos); // NOTE: return media-type without any of the parameters.
988 }
989 
DecodeDataURI(vector<uint8_t> & out,string_view in,size_t reqBytes,bool checkSize,string_view & outMimeType)990 bool DecodeDataURI(vector<uint8_t>& out, string_view in, size_t reqBytes, bool checkSize, string_view& outMimeType)
991 {
992     size_t offsetToData = 0u;
993     out.clear();
994     if (auto const mimeType = ParseDataUri(in, offsetToData); mimeType.empty()) {
995         return false;
996     } else {
997         outMimeType = mimeType;
998     }
999     in.remove_prefix(offsetToData);
1000     out = BASE_NS::Base64Decode(in);
1001 
1002     if (out.empty()) {
1003         return false;
1004     }
1005 
1006     if (checkSize) {
1007         if (out.size() != reqBytes) {
1008             return false;
1009         }
1010     }
1011     return true;
1012 }
1013 
IsDataURI(const string_view in)1014 bool IsDataURI(const string_view in)
1015 {
1016     size_t offsetToData;
1017     if (auto const mimeType = ParseDataUri(in, offsetToData); !mimeType.empty()) {
1018         if (mimeType == "application/octet-stream") {
1019             return true;
1020         }
1021         if (mimeType == "image/jpeg") {
1022             return true;
1023         }
1024         if (mimeType == "image/png") {
1025             return true;
1026         }
1027         if (mimeType == "image/bmp") {
1028             return true;
1029         }
1030         if (mimeType == "image/gif") {
1031             return true;
1032         }
1033         if (mimeType == "text/plain") {
1034             return true;
1035         }
1036         if (mimeType == "application/gltf-buffer") {
1037             return true;
1038         }
1039     }
1040     return false;
1041 }
1042 
1043 // Buffer / data helpers
GLTFLoadDataResult(GLTFLoadDataResult && other)1044 GLTFLoadDataResult::GLTFLoadDataResult(GLTFLoadDataResult&& other) noexcept
1045     : success(other.success), normalized(other.normalized), error(move(other.error)),
1046       componentType(other.componentType), componentByteSize(other.componentByteSize),
1047       componentCount(other.componentCount), elementSize(other.elementSize), elementCount(other.elementCount),
1048       min(move(other.min)), max(move(other.max)), data(move(other.data))
1049 {}
1050 
operator =(GLTFLoadDataResult && other)1051 GLTFLoadDataResult& GLTFLoadDataResult::operator=(GLTFLoadDataResult&& other) noexcept
1052 {
1053     success = other.success;
1054     normalized = other.normalized;
1055     error = move(other.error);
1056     componentType = other.componentType;
1057     componentByteSize = other.componentByteSize;
1058     componentCount = other.componentCount;
1059     elementSize = other.elementSize;
1060     elementCount = other.elementCount;
1061     min = move(other.min);
1062     max = move(other.max);
1063     data = move(other.data);
1064 
1065     return *this;
1066 }
1067 
1068 // Populate GLTF buffers with data.
LoadBuffers(const Data & data,IFileManager & fileManager)1069 BufferLoadResult LoadBuffers(const Data& data, IFileManager& fileManager)
1070 {
1071     BufferLoadResult result;
1072     // Load data to all buffers.
1073     for (size_t i = 0u; i < data.buffers.size(); ++i) {
1074         Buffer* buffer = data.buffers[i].get();
1075         if (buffer->data.empty()) {
1076             result = LoadBuffer(data, *buffer, fileManager);
1077             if (!result.success) {
1078                 return result;
1079             }
1080         }
1081     }
1082 
1083     // Set up bufferview data pointers.
1084     for (size_t i = 0u; i < data.bufferViews.size(); ++i) {
1085         BufferView* view = data.bufferViews[i].get();
1086         view->data = &(view->buffer->data[view->byteOffset]);
1087     }
1088 
1089     return result;
1090 }
1091 
LoadUri(const string_view uri,const string_view expectedMimeType,const string_view filePath,IFileManager & fileManager,string_view & outExtension,vector<uint8_t> & outData)1092 UriLoadResult LoadUri(const string_view uri, const string_view expectedMimeType, const string_view filePath,
1093     IFileManager& fileManager, string_view& outExtension, vector<uint8_t>& outData)
1094 {
1095     size_t offsetToData;
1096     if (auto const mimeType = ParseDataUri(uri, offsetToData); !mimeType.empty()) {
1097         bool isValidMimeType = true;
1098         const auto pos = mimeType.find_first_of('/');
1099         if (pos != string_view::npos) {
1100             auto const type = mimeType.substr(0u, pos);
1101 
1102             if (!expectedMimeType.empty()) {
1103                 if (type != expectedMimeType) {
1104                     isValidMimeType = false;
1105                 }
1106             }
1107             outExtension = mimeType.substr(pos + 1u);
1108         }
1109         if (isValidMimeType) {
1110             string_view outMimeType;
1111             DecodeDataURI(outData, uri, 0u, false, outMimeType);
1112             if (outData.empty()) {
1113                 return URI_LOAD_FAILED_TO_DECODE_BASE64;
1114             }
1115         } else {
1116             return URI_LOAD_FAILED_INVALID_MIME_TYPE;
1117         }
1118     } else {
1119         string_view baseName, extension;
1120         SplitBaseFilename(uri, baseName, extension);
1121         outExtension = extension;
1122         if (!ReadUriToVector(filePath, fileManager, uri, outData)) {
1123             return URI_LOAD_FAILED_TO_READ_FILE;
1124         }
1125     }
1126 
1127     return URI_LOAD_SUCCESS;
1128 }
1129 
1130 // Load accessor data.
LoadData(Accessor const & accessor)1131 GLTFLoadDataResult LoadData(Accessor const& accessor)
1132 {
1133     GLTFLoadDataResult result;
1134     result.success = true;
1135 
1136     const auto* bufferView = accessor.bufferView;
1137     const auto dataType = accessor.type;
1138     const auto component = accessor.componentType;
1139 
1140     result.normalized = accessor.normalized;
1141     result.componentType = accessor.componentType;
1142     result.componentByteSize = GetComponentByteSize(component);
1143     result.componentCount = GetComponentsCount(dataType);
1144     result.elementSize = result.componentCount * result.componentByteSize;
1145     result.elementCount = accessor.count;
1146     result.min = accessor.min;
1147     result.max = accessor.max;
1148 
1149     if (bufferView) {
1150         if (bufferView->buffer) {
1151             vector<uint8_t> fileData = Read(accessor);
1152             if (fileData.empty()) {
1153                 result.error = "Failed to load attribute data.\n";
1154                 result.success = false;
1155             }
1156 
1157             result.data.swap(fileData);
1158         }
1159         if (accessor.sparse.count) {
1160             LoadSparseAccessor(accessor, result);
1161         }
1162     }
1163 
1164     return result;
1165 }
1166 
1167 // class Data from GLTFData.h
Data(IFileManager & fileManager)1168 Data::Data(IFileManager& fileManager) : fileManager_(fileManager) {}
1169 
LoadBuffers()1170 bool Data::LoadBuffers()
1171 {
1172     BufferLoadResult result = GLTF2::LoadBuffers(*this, fileManager_);
1173     return result.success;
1174 }
1175 
ReleaseBuffers()1176 void Data::ReleaseBuffers()
1177 {
1178     // Reset bufferview pointers.
1179     for (size_t i = 0u; i < bufferViews.size(); ++i) {
1180         BufferView* view = bufferViews[i].get();
1181         view->data = nullptr;
1182     }
1183 
1184     // Release buffer data.
1185     for (size_t i = 0u; i < buffers.size(); ++i) {
1186         Buffer* buffer = buffers[i].get();
1187         buffer->data = vector<uint8_t>();
1188     }
1189 }
1190 
GetExternalFileUris()1191 vector<string> Data::GetExternalFileUris()
1192 {
1193     vector<string> result;
1194 
1195     // All images in this glTF.
1196     for (auto const& image : images) {
1197         if (!IsDataURI(image->uri)) {
1198             result.push_back(image->uri);
1199         }
1200     }
1201 
1202     // All buffers in this glTF (in case there are several .bin files).
1203     for (auto const& buffer : buffers) {
1204         if (!IsDataURI(buffer->uri)) {
1205             result.push_back(buffer->uri);
1206         }
1207     }
1208 
1209 #if defined(GLTF2_EXTENSION_HW_XR_EXT)
1210     // All thumbnails glTF (in case there are non-embedded thumbnail uris).
1211     for (auto const& thumbnail : thumbnails) {
1212         if (!IsDataURI(thumbnail.uri)) {
1213             result.push_back(thumbnail.uri);
1214         }
1215     }
1216 #endif
1217 
1218     return result;
1219 }
1220 
GetDefaultSceneIndex() const1221 size_t Data::GetDefaultSceneIndex() const
1222 {
1223     if (defaultScene != nullptr) {
1224         for (size_t i = 0u; i < scenes.size(); ++i) {
1225             if (scenes[i].get() == defaultScene) {
1226                 return i;
1227             }
1228         }
1229     }
1230 
1231     return CORE_GLTF_INVALID_INDEX;
1232 }
1233 
GetSceneCount() const1234 size_t Data::GetSceneCount() const
1235 {
1236     return scenes.size();
1237 }
1238 
GetThumbnailImageCount() const1239 size_t Data::GetThumbnailImageCount() const
1240 {
1241 #if defined(GLTF2_EXTENSION_HW_XR_EXT)
1242     return thumbnails.size();
1243 #else
1244     return 0;
1245 #endif
1246 }
1247 
GetThumbnailImage(size_t thumbnailIndex)1248 IGLTFData::ThumbnailImage Data::GetThumbnailImage(size_t thumbnailIndex)
1249 {
1250     IGLTFData::ThumbnailImage result;
1251 
1252 #if defined(GLTF2_EXTENSION_HW_XR_EXT)
1253     if (thumbnailIndex >= thumbnails.size()) {
1254         return result;
1255     }
1256 
1257     auto& thumbnail = thumbnails[thumbnailIndex];
1258     if (thumbnail.data.empty()) {
1259         // Load thumbnail data.
1260         string_view extension;
1261         if (LoadUri(thumbnail.uri, "image", filepath, fileManager_, extension, thumbnail.data) != URI_LOAD_SUCCESS) {
1262             thumbnails[thumbnailIndex].data.clear();
1263             thumbnails[thumbnailIndex].extension = "";
1264         } else {
1265             thumbnail.extension = extension;
1266         }
1267     }
1268 
1269     result.data = thumbnail.data;
1270     result.extension = thumbnail.extension;
1271 #endif
1272 
1273     return result;
1274 }
1275 
Destroy()1276 void Data::Destroy()
1277 {
1278     delete this;
1279 }
1280 
1281 // class SceneData from GLTFData.h
SceneData(unique_ptr<GLTF2::Data> data)1282 SceneData::SceneData(unique_ptr<GLTF2::Data> data) : data_(BASE_NS::move(data)) {}
1283 
GetData() const1284 const GLTF2::Data* SceneData::GetData() const
1285 {
1286     return data_.get();
1287 }
1288 
GetDefaultSceneIndex() const1289 size_t SceneData::GetDefaultSceneIndex() const
1290 {
1291     return data_->GetDefaultSceneIndex();
1292 }
1293 
GetSceneCount() const1294 size_t SceneData::GetSceneCount() const
1295 {
1296     return data_->GetSceneCount();
1297 }
1298 
GetInterface(const BASE_NS::Uid & uid) const1299 const IInterface* SceneData::GetInterface(const BASE_NS::Uid& uid) const
1300 {
1301     if (uid == ISceneData::UID) {
1302         return static_cast<const ISceneData*>(this);
1303     }
1304     if (uid == SceneData::UID) {
1305         return static_cast<const SceneData*>(this);
1306     }
1307     if (uid == IInterface::UID) {
1308         return static_cast<const IInterface*>(this);
1309     }
1310     return nullptr;
1311 }
1312 
GetInterface(const BASE_NS::Uid & uid)1313 IInterface* SceneData::GetInterface(const BASE_NS::Uid& uid)
1314 {
1315     if (uid == ISceneData::UID) {
1316         return static_cast<ISceneData*>(this);
1317     }
1318     if (uid == SceneData::UID) {
1319         return static_cast<SceneData*>(this);
1320     }
1321     if (uid == IInterface::UID) {
1322         return static_cast<IInterface*>(this);
1323     }
1324     return nullptr;
1325 }
1326 
Ref()1327 void SceneData::Ref()
1328 {
1329     ++refcnt_;
1330 }
1331 
Unref()1332 void SceneData::Unref()
1333 {
1334     if (--refcnt_ == 0) {
1335         delete this;
1336     }
1337 }
1338 } // namespace GLTF2
1339 CORE3D_END_NAMESPACE()
1340