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