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