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