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