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