1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2021, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42#include "AssetLib/glTF/glTFCommon.h"
43
44#include <assimp/MemoryIOWrapper.h>
45#include <assimp/StringUtils.h>
46#include <assimp/DefaultLogger.hpp>
47
48// clang-format off
49#ifdef ASSIMP_ENABLE_DRACO
50
51// Google draco library headers spew many warnings. Bad Google, no cookie
52#   if _MSC_VER
53#       pragma warning(push)
54#       pragma warning(disable : 4018) // Signed/unsigned mismatch
55#       pragma warning(disable : 4804) // Unsafe use of type 'bool'
56#   elif defined(__clang__)
57#       pragma clang diagnostic push
58#       pragma clang diagnostic ignored "-Wsign-compare"
59#   elif defined(__GNUC__)
60#       pragma GCC diagnostic push
61#       if (__GNUC__ > 4)
62#           pragma GCC diagnostic ignored "-Wbool-compare"
63#       endif
64#   pragma GCC diagnostic ignored "-Wsign-compare"
65#endif
66
67#include "draco/compression/decode.h"
68#include "draco/core/decoder_buffer.h"
69
70#if _MSC_VER
71#   pragma warning(pop)
72#elif defined(__clang__)
73#   pragma clang diagnostic pop
74#elif defined(__GNUC__)
75#   pragma GCC diagnostic pop
76#endif
77#ifndef DRACO_MESH_COMPRESSION_SUPPORTED
78#   error glTF: KHR_draco_mesh_compression: draco library must have DRACO_MESH_COMPRESSION_SUPPORTED
79#endif
80#endif
81// clang-format on
82
83using namespace Assimp;
84using namespace glTFCommon;
85
86namespace glTF2 {
87
88namespace {
89
90//
91// JSON Value reading helpers
92//
93inline CustomExtension ReadExtensions(const char *name, Value &obj) {
94    CustomExtension ret;
95    ret.name = name;
96    if (obj.IsObject()) {
97        ret.mValues.isPresent = true;
98        for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
99            auto &val = it->value;
100            ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val));
101        }
102    } else if (obj.IsArray()) {
103        ret.mValues.value.reserve(obj.Size());
104        ret.mValues.isPresent = true;
105        for (unsigned int i = 0; i < obj.Size(); ++i) {
106            ret.mValues.value.push_back(ReadExtensions(name, obj[i]));
107        }
108    } else if (obj.IsNumber()) {
109        if (obj.IsUint64()) {
110            ret.mUint64Value.value = obj.GetUint64();
111            ret.mUint64Value.isPresent = true;
112        } else if (obj.IsInt64()) {
113            ret.mInt64Value.value = obj.GetInt64();
114            ret.mInt64Value.isPresent = true;
115        } else if (obj.IsDouble()) {
116            ret.mDoubleValue.value = obj.GetDouble();
117            ret.mDoubleValue.isPresent = true;
118        }
119    } else if (obj.IsString()) {
120        ReadValue(obj, ret.mStringValue);
121        ret.mStringValue.isPresent = true;
122    } else if (obj.IsBool()) {
123        ret.mBoolValue.value = obj.GetBool();
124        ret.mBoolValue.isPresent = true;
125    }
126    return ret;
127}
128
129inline void CopyData(size_t count, const uint8_t *src, size_t src_stride,
130        uint8_t *dst, size_t dst_stride) {
131    if (src_stride == dst_stride) {
132        memcpy(dst, src, count * src_stride);
133        return;
134    }
135
136    size_t sz = std::min(src_stride, dst_stride);
137    for (size_t i = 0; i < count; ++i) {
138        memcpy(dst, src, sz);
139        if (sz < dst_stride) {
140            memset(dst + sz, 0, dst_stride - sz);
141        }
142        src += src_stride;
143        dst += dst_stride;
144    }
145}
146
147void SetVector(vec4 &v, const float (&in)[4]) {
148    v[0] = in[0];
149    v[1] = in[1];
150    v[2] = in[2];
151    v[3] = in[3];
152}
153
154void SetVector(vec3 &v, const float (&in)[3]) {
155    v[0] = in[0];
156    v[1] = in[1];
157    v[2] = in[2];
158}
159
160template <int N>
161inline int Compare(const char *attr, const char (&str)[N]) {
162    return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
163}
164
165#if _MSC_VER
166#pragma warning(push)
167#pragma warning(disable : 4706)
168#endif // _MSC_VER
169
170inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) {
171    if ((pos = Compare(attr, "POSITION"))) {
172        v = &(p.attributes.position);
173    } else if ((pos = Compare(attr, "NORMAL"))) {
174        v = &(p.attributes.normal);
175    } else if ((pos = Compare(attr, "TANGENT"))) {
176        v = &(p.attributes.tangent);
177    } else if ((pos = Compare(attr, "TEXCOORD"))) {
178        v = &(p.attributes.texcoord);
179    } else if ((pos = Compare(attr, "COLOR"))) {
180        v = &(p.attributes.color);
181    } else if ((pos = Compare(attr, "JOINT"))) {
182        v = &(p.attributes.joint);
183    } else if ((pos = Compare(attr, "JOINTMATRIX"))) {
184        v = &(p.attributes.jointmatrix);
185    } else if ((pos = Compare(attr, "WEIGHT"))) {
186        v = &(p.attributes.weight);
187    } else
188        return false;
189    return true;
190}
191
192inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, const char *attr, Mesh::AccessorList *&v, int &pos) {
193    if ((pos = Compare(attr, "POSITION"))) {
194        v = &(p.targets[targetIndex].position);
195    } else if ((pos = Compare(attr, "NORMAL"))) {
196        v = &(p.targets[targetIndex].normal);
197    } else if ((pos = Compare(attr, "TANGENT"))) {
198        v = &(p.targets[targetIndex].tangent);
199    } else
200        return false;
201    return true;
202}
203
204} // namespace
205
206inline Value *Object::FindString(Value &val, const char *memberId) {
207    return FindStringInContext(val, memberId, id.c_str(), name.c_str());
208}
209
210inline Value *Object::FindNumber(Value &val, const char *memberId) {
211    return FindNumberInContext(val, memberId, id.c_str(), name.c_str());
212}
213
214inline Value *Object::FindUInt(Value &val, const char *memberId) {
215    return FindUIntInContext(val, memberId, id.c_str(), name.c_str());
216}
217
218inline Value *Object::FindArray(Value &val, const char *memberId) {
219    return FindArrayInContext(val, memberId, id.c_str(), name.c_str());
220}
221
222inline Value *Object::FindObject(Value &val, const char *memberId) {
223    return FindObjectInContext(val, memberId, id.c_str(), name.c_str());
224}
225
226inline Value *Object::FindExtension(Value &val, const char *extensionId) {
227    return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str());
228}
229
230inline void Object::ReadExtensions(Value &val) {
231    if (Value *curExtensions = FindObject(val, "extensions")) {
232        this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions);
233    }
234}
235
236inline void Object::ReadExtras(Value &val) {
237    if (Value *curExtras = FindObject(val, "extras")) {
238        this->extras = glTF2::ReadExtensions("extras", *curExtras);
239    }
240}
241
242#ifdef ASSIMP_ENABLE_DRACO
243
244template <typename T>
245inline void CopyFaceIndex_Draco(Buffer &decodedIndexBuffer, const draco::Mesh &draco_mesh) {
246    const size_t faceStride = sizeof(T) * 3;
247    for (draco::FaceIndex f(0); f < draco_mesh.num_faces(); ++f) {
248        const draco::Mesh::Face &face = draco_mesh.face(f);
249        T indices[3] = { static_cast<T>(face[0].value()), static_cast<T>(face[1].value()), static_cast<T>(face[2].value()) };
250        memcpy(decodedIndexBuffer.GetPointer() + (f.value() * faceStride), &indices[0], faceStride);
251    }
252}
253
254inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Primitive &prim) {
255    if (!prim.indices || dracoMesh.num_faces() == 0)
256        return;
257
258    // Create a decoded Index buffer (if there is one)
259    size_t componentBytes = prim.indices->GetBytesPerComponent();
260
261    std::unique_ptr<Buffer> decodedIndexBuffer(new Buffer());
262    decodedIndexBuffer->Grow(dracoMesh.num_faces() * 3 * componentBytes);
263
264    // If accessor uses the same size as draco implementation, copy the draco buffer directly
265
266    // Usually uint32_t but shouldn't assume
267    if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) {
268        memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength);
269        return;
270    }
271
272    // Not same size, convert
273    switch (componentBytes) {
274    case sizeof(uint32_t):
275        CopyFaceIndex_Draco<uint32_t>(*decodedIndexBuffer, dracoMesh);
276        break;
277    case sizeof(uint16_t):
278        CopyFaceIndex_Draco<uint16_t>(*decodedIndexBuffer, dracoMesh);
279        break;
280    case sizeof(uint8_t):
281        CopyFaceIndex_Draco<uint8_t>(*decodedIndexBuffer, dracoMesh);
282        break;
283    default:
284        ai_assert(false);
285        break;
286    }
287
288    // Assign this alternate data buffer to the accessor
289    prim.indices->decodedBuffer.swap(decodedIndexBuffer);
290}
291
292template <typename T>
293static bool GetAttributeForAllPoints_Draco(const draco::Mesh &dracoMesh,
294        const draco::PointAttribute &dracoAttribute,
295        Buffer &outBuffer) {
296    size_t byteOffset = 0;
297    T values[4] = { 0, 0, 0, 0 };
298    for (draco::PointIndex i(0); i < dracoMesh.num_points(); ++i) {
299        const draco::AttributeValueIndex val_index = dracoAttribute.mapped_index(i);
300        if (!dracoAttribute.ConvertValue<T>(val_index, dracoAttribute.num_components(), values)) {
301            return false;
302        }
303
304        memcpy(outBuffer.GetPointer() + byteOffset, &values[0], sizeof(T) * dracoAttribute.num_components());
305        byteOffset += sizeof(T) * dracoAttribute.num_components();
306    }
307
308    return true;
309}
310
311inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32_t dracoAttribId, Accessor &accessor) {
312    // Create decoded buffer
313    const draco::PointAttribute *pDracoAttribute = dracoMesh.GetAttributeByUniqueId(dracoAttribId);
314    if (pDracoAttribute == nullptr) {
315        throw DeadlyImportError("GLTF: Invalid draco attribute id: ", dracoAttribId);
316    }
317
318    size_t componentBytes = accessor.GetBytesPerComponent();
319
320    std::unique_ptr<Buffer> decodedAttribBuffer(new Buffer());
321    decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes);
322
323    switch (accessor.componentType) {
324    case ComponentType_BYTE:
325        GetAttributeForAllPoints_Draco<int8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
326        break;
327    case ComponentType_UNSIGNED_BYTE:
328        GetAttributeForAllPoints_Draco<uint8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
329        break;
330    case ComponentType_SHORT:
331        GetAttributeForAllPoints_Draco<int16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
332        break;
333    case ComponentType_UNSIGNED_SHORT:
334        GetAttributeForAllPoints_Draco<uint16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
335        break;
336    case ComponentType_UNSIGNED_INT:
337        GetAttributeForAllPoints_Draco<uint32_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
338        break;
339    case ComponentType_FLOAT:
340        GetAttributeForAllPoints_Draco<float>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer);
341        break;
342    default:
343        ai_assert(false);
344        break;
345    }
346
347    // Assign this alternate data buffer to the accessor
348    accessor.decodedBuffer.swap(decodedAttribBuffer);
349}
350
351#endif // ASSIMP_ENABLE_DRACO
352
353//
354// LazyDict methods
355//
356
357template <class T>
358inline LazyDict<T>::LazyDict(Asset &asset, const char *dictId, const char *extId) :
359        mDictId(dictId),
360        mExtId(extId),
361        mDict(0),
362        mAsset(asset) {
363    asset.mDicts.push_back(this); // register to the list of dictionaries
364}
365
366template <class T>
367inline LazyDict<T>::~LazyDict() {
368    for (size_t i = 0; i < mObjs.size(); ++i) {
369        delete mObjs[i];
370    }
371}
372
373template <class T>
374inline void LazyDict<T>::AttachToDocument(Document &doc) {
375    Value *container = nullptr;
376    const char *context = nullptr;
377
378    if (mExtId) {
379        if (Value *exts = FindObject(doc, "extensions")) {
380            container = FindObjectInContext(*exts, mExtId, "extensions");
381            context = mExtId;
382        }
383    } else {
384        container = &doc;
385        context = "the document";
386    }
387
388    if (container) {
389        mDict = FindArrayInContext(*container, mDictId, context);
390    }
391}
392
393template <class T>
394inline void LazyDict<T>::DetachFromDocument() {
395    mDict = nullptr;
396}
397
398template <class T>
399unsigned int LazyDict<T>::Remove(const char *id) {
400    id = T::TranslateId(mAsset, id);
401
402    typename IdDict::iterator objIt = mObjsById.find(id);
403
404    if (objIt == mObjsById.end()) {
405        throw DeadlyExportError("GLTF: Object with id \"" + std::string(id) + "\" is not found");
406    }
407
408    const unsigned int index = objIt->second;
409
410    mAsset.mUsedIds[id] = false;
411    mObjsById.erase(id);
412    mObjsByOIndex.erase(index);
413    delete mObjs[index];
414    mObjs.erase(mObjs.begin() + index);
415
416    //update index of object in mObjs;
417    for (unsigned int i = index; i < mObjs.size(); ++i) {
418        T *obj = mObjs[i];
419
420        obj->index = i;
421    }
422
423    for (IdDict::iterator it = mObjsById.begin(); it != mObjsById.end(); ++it) {
424        if (it->second <= index) {
425            continue;
426        }
427
428        mObjsById[it->first] = it->second - 1;
429    }
430
431    for (Dict::iterator it = mObjsByOIndex.begin(); it != mObjsByOIndex.end(); ++it) {
432        if (it->second <= index) {
433            continue;
434        }
435
436        mObjsByOIndex[it->first] = it->second - 1;
437    }
438
439    return index;
440}
441
442template <class T>
443Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
444
445    typename Dict::iterator it = mObjsByOIndex.find(i);
446    if (it != mObjsByOIndex.end()) { // already created?
447        return Ref<T>(mObjs, it->second);
448    }
449
450    // read it from the JSON object
451    if (!mDict) {
452        throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\"");
453    }
454
455    if (!mDict->IsArray()) {
456        throw DeadlyImportError("GLTF: Field \"", mDictId, "\"  is not an array");
457    }
458
459    if (i >= mDict->Size()) {
460        throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\"");
461    }
462
463    Value &obj = (*mDict)[i];
464
465    if (!obj.IsObject()) {
466        throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object");
467    }
468
469    if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
470        throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself");
471    }
472    mRecursiveReferenceCheck.insert(i);
473
474    // Unique ptr prevents memory leak in case of Read throws an exception
475    auto inst = std::unique_ptr<T>(new T());
476    // Try to make this human readable so it can be used in error messages.
477    inst->id = std::string(mDictId) + "[" + ai_to_string(i) + "]";
478    inst->oIndex = i;
479    ReadMember(obj, "name", inst->name);
480    inst->Read(obj, mAsset);
481    inst->ReadExtensions(obj);
482    inst->ReadExtras(obj);
483
484    Ref<T> result = Add(inst.release());
485    mRecursiveReferenceCheck.erase(i);
486    return result;
487}
488
489template <class T>
490Ref<T> LazyDict<T>::Get(unsigned int i) {
491    return Ref<T>(mObjs, i);
492}
493
494template <class T>
495Ref<T> LazyDict<T>::Get(const char *id) {
496    id = T::TranslateId(mAsset, id);
497
498    typename IdDict::iterator it = mObjsById.find(id);
499    if (it != mObjsById.end()) { // already created?
500        return Ref<T>(mObjs, it->second);
501    }
502
503    return Ref<T>();
504}
505
506template <class T>
507Ref<T> LazyDict<T>::Add(T *obj) {
508    unsigned int idx = unsigned(mObjs.size());
509    mObjs.push_back(obj);
510    mObjsByOIndex[obj->oIndex] = idx;
511    mObjsById[obj->id] = idx;
512    mAsset.mUsedIds[obj->id] = true;
513    return Ref<T>(mObjs, idx);
514}
515
516template <class T>
517Ref<T> LazyDict<T>::Create(const char *id) {
518    Asset::IdMap::iterator it = mAsset.mUsedIds.find(id);
519    if (it != mAsset.mUsedIds.end()) {
520        throw DeadlyImportError("GLTF: two objects with the same ID exist");
521    }
522    T *inst = new T();
523    unsigned int idx = unsigned(mObjs.size());
524    inst->id = id;
525    inst->index = idx;
526    inst->oIndex = idx;
527    return Add(inst);
528}
529
530//
531// glTF dictionary objects methods
532//
533inline Buffer::Buffer() :
534        byteLength(0),
535        type(Type_arraybuffer),
536        EncodedRegion_Current(nullptr),
537        mIsSpecial(false) {}
538
539inline Buffer::~Buffer() {
540    for (SEncodedRegion *reg : EncodedRegion_List)
541        delete reg;
542}
543
544inline const char *Buffer::TranslateId(Asset & /*r*/, const char *id) {
545    return id;
546}
547
548inline void Buffer::Read(Value &obj, Asset &r) {
549    size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0);
550    byteLength = statedLength;
551
552    Value *it = FindString(obj, "uri");
553    if (!it) {
554        if (statedLength > 0) {
555            throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute");
556        }
557        return;
558    }
559
560    const char *uri = it->GetString();
561
562    glTFCommon::Util::DataURI dataURI;
563    if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
564        if (dataURI.base64) {
565            uint8_t *data = nullptr;
566            this->byteLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
567            this->mData.reset(data, std::default_delete<uint8_t[]>());
568
569            if (statedLength > 0 && this->byteLength != statedLength) {
570                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength),
571                        " bytes, but found ", ai_to_string(dataURI.dataLength));
572            }
573        } else { // assume raw data
574            if (statedLength != dataURI.dataLength) {
575                throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength),
576                        " bytes, but found ", ai_to_string(dataURI.dataLength));
577            }
578
579            this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>());
580            memcpy(this->mData.get(), dataURI.data, dataURI.dataLength);
581        }
582    } else { // Local file
583        if (byteLength > 0) {
584            std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir.back() == '/' ? r.mCurrentAssetDir : r.mCurrentAssetDir + '/') : "";
585
586            IOStream *file = r.OpenFile(dir + uri, "rb");
587            if (file) {
588                bool ok = LoadFromStream(*file, byteLength);
589                delete file;
590
591                if (!ok)
592                    throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\"");
593            } else {
594                throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\"");
595            }
596        }
597    }
598}
599
600inline bool Buffer::LoadFromStream(IOStream &stream, size_t length, size_t baseOffset) {
601    byteLength = length ? length : stream.FileSize();
602
603    if (byteLength > stream.FileSize()) {
604        throw DeadlyImportError("GLTF: Invalid byteLength exceeds size of actual data.");
605    }
606
607    if (baseOffset) {
608        stream.Seek(baseOffset, aiOrigin_SET);
609    }
610
611    mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>());
612
613    if (stream.Read(mData.get(), byteLength, 1) != 1) {
614        return false;
615    }
616    return true;
617}
618
619inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) {
620    // Check pointer to data
621    if (pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided.");
622
623    // Check offset
624    if (pOffset > byteLength) {
625        const uint8_t val_size = 32;
626
627        char val[val_size];
628
629        ai_snprintf(val, val_size, AI_SIZEFMT, pOffset);
630        throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region.");
631    }
632
633    // Check length
634    if ((pOffset + pEncodedData_Length) > byteLength) {
635        const uint8_t val_size = 64;
636
637        char val[val_size];
638
639        ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length);
640        throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range.");
641    }
642
643    // Add new region
644    EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID));
645    // And set new value for "byteLength"
646    byteLength += (pDecodedData_Length - pEncodedData_Length);
647}
648
649inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
650    if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) {
651        return;
652    }
653
654    for (SEncodedRegion *reg : EncodedRegion_List) {
655        if (reg->ID == pID) {
656            EncodedRegion_Current = reg;
657            return;
658        }
659    }
660
661    throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found.");
662}
663
664inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
665
666    if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) {
667        return false;
668    }
669
670    const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
671    uint8_t *new_data = new uint8_t[new_data_size];
672    // Copy data which place before replacing part.
673    ::memcpy(new_data, mData.get(), pBufferData_Offset);
674    // Copy new data.
675    ::memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count);
676    // Copy data which place after replacing part.
677    ::memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset);
678    // Apply new data
679    mData.reset(new_data, std::default_delete<uint8_t[]>());
680    byteLength = new_data_size;
681
682    return true;
683}
684
685inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
686    if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) {
687        return false;
688    }
689
690    const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
691    uint8_t *new_data = new uint8_t[new_data_size];
692    // Copy data which place before replacing part.
693    memcpy(new_data, mData.get(), pBufferData_Offset);
694    // Copy new data.
695    memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count);
696    // Copy data which place after replacing part.
697    memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], new_data_size - (pBufferData_Offset + pReplace_Count));
698    // Apply new data
699    mData.reset(new_data, std::default_delete<uint8_t[]>());
700    byteLength = new_data_size;
701
702    return true;
703}
704
705inline size_t Buffer::AppendData(uint8_t *data, size_t length) {
706    const size_t offset = this->byteLength;
707
708    // Force alignment to 4 bits
709    const size_t paddedLength = (length + 3) & ~3;
710    Grow(paddedLength);
711    memcpy(mData.get() + offset, data, length);
712    memset(mData.get() + offset + length, 0, paddedLength - length);
713    return offset;
714}
715
716inline void Buffer::Grow(size_t amount) {
717    if (amount <= 0) {
718        return;
719    }
720
721    // Capacity is big enough
722    if (capacity >= byteLength + amount) {
723        byteLength += amount;
724        return;
725    }
726
727    // Just allocate data which we need
728    capacity = byteLength + amount;
729
730    uint8_t *b = new uint8_t[capacity];
731    if (nullptr != mData) {
732        memcpy(b, mData.get(), byteLength);
733    }
734    mData.reset(b, std::default_delete<uint8_t[]>());
735    byteLength += amount;
736}
737
738//
739// struct BufferView
740//
741inline void BufferView::Read(Value &obj, Asset &r) {
742    if (Value *bufferVal = FindUInt(obj, "buffer")) {
743        buffer = r.buffers.Retrieve(bufferVal->GetUint());
744    }
745
746    if (!buffer) {
747        throw DeadlyImportError("GLTF: Buffer view without valid buffer.");
748    }
749
750    byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0));
751    byteLength = MemberOrDefault(obj, "byteLength", size_t(0));
752    byteStride = MemberOrDefault(obj, "byteStride", 0u);
753
754    // Check length
755    if ((byteOffset + byteLength) > buffer->byteLength) {
756        throw DeadlyImportError("GLTF: Buffer view with offset/length (", byteOffset, "/", byteLength, ") is out of range.");
757    }
758}
759
760inline uint8_t *BufferView::GetPointer(size_t accOffset) {
761    if (!buffer) {
762        return nullptr;
763    }
764    uint8_t *basePtr = buffer->GetPointer();
765    if (!basePtr) {
766        return nullptr;
767    }
768
769    size_t offset = accOffset + byteOffset;
770    if (buffer->EncodedRegion_Current != nullptr) {
771        const size_t begin = buffer->EncodedRegion_Current->Offset;
772        const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length;
773        if ((offset >= begin) && (offset < end)) {
774            return &buffer->EncodedRegion_Current->DecodedData[offset - begin];
775        }
776    }
777
778    return basePtr + offset;
779}
780
781//
782// struct Accessor
783//
784inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) {
785    if (bytes) {
786        data.assign(bytes, bytes + numBytes);
787    } else {
788        data.resize(numBytes, 0x00);
789    }
790}
791
792inline void Accessor::Sparse::PatchData(unsigned int elementSize) {
793    uint8_t *pIndices = indices->GetPointer(indicesByteOffset);
794    const unsigned int indexSize = int(ComponentTypeSize(indicesType));
795    uint8_t *indicesEnd = pIndices + count * indexSize;
796
797    uint8_t *pValues = values->GetPointer(valuesByteOffset);
798    while (pIndices != indicesEnd) {
799        size_t offset;
800        switch (indicesType) {
801        case ComponentType_UNSIGNED_BYTE:
802            offset = *pIndices;
803            break;
804        case ComponentType_UNSIGNED_SHORT:
805            offset = *reinterpret_cast<uint16_t *>(pIndices);
806            break;
807        case ComponentType_UNSIGNED_INT:
808            offset = *reinterpret_cast<uint32_t *>(pIndices);
809            break;
810        default:
811            // have fun with float and negative values from signed types as indices.
812            throw DeadlyImportError("Unsupported component type in index.");
813        }
814
815        offset *= elementSize;
816
817        if (offset + elementSize > data.size()) {
818            throw DeadlyImportError("Invalid sparse accessor. Byte offset for patching points outside allocated memory.");
819        }
820
821        std::memcpy(data.data() + offset, pValues, elementSize);
822
823        pValues += elementSize;
824        pIndices += indexSize;
825    }
826}
827
828inline void Accessor::Read(Value &obj, Asset &r) {
829    if (Value *bufferViewVal = FindUInt(obj, "bufferView")) {
830        bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
831    }
832
833    byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0));
834    componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE);
835    {
836        const Value *countValue = FindUInt(obj, "count");
837        if (!countValue) {
838            throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")");
839        }
840        count = countValue->GetUint();
841    }
842
843    const char *typestr;
844    type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR;
845
846    if (bufferView) {
847        // Check length
848        unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count;
849
850        // handle integer overflow
851        if (byteLength < count) {
852            throw DeadlyImportError("GLTF: Accessor with offset/count (", byteOffset, "/", count, ") is out of range.");
853        }
854
855        if ((byteOffset + byteLength) > bufferView->byteLength || (bufferView->byteOffset + byteOffset + byteLength) > bufferView->buffer->byteLength) {
856            throw DeadlyImportError("GLTF: Accessor with offset/length (", byteOffset, "/", byteLength, ") is out of range.");
857        }
858    }
859
860    if (Value *sparseValue = FindObject(obj, "sparse")) {
861        sparse.reset(new Sparse);
862        // count
863        ReadMember(*sparseValue, "count", sparse->count);
864
865        // indices
866        if (Value *indicesValue = FindObject(*sparseValue, "indices")) {
867            //indices bufferView
868            Value *indiceViewID = FindUInt(*indicesValue, "bufferView");
869            sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint());
870            //indices byteOffset
871            sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0));
872            //indices componentType
873            sparse->indicesType = MemberOrDefault(*indicesValue, "componentType", ComponentType_BYTE);
874            //sparse->indices->Read(*indicesValue, r);
875        } else {
876            // indicesType
877            sparse->indicesType = MemberOrDefault(*sparseValue, "componentType", ComponentType_UNSIGNED_SHORT);
878        }
879
880        // value
881        if (Value *valuesValue = FindObject(*sparseValue, "values")) {
882            //value bufferView
883            Value *valueViewID = FindUInt(*valuesValue, "bufferView");
884            sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint());
885            //value byteOffset
886            sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0));
887            //sparse->values->Read(*valuesValue, r);
888        }
889
890
891        const unsigned int elementSize = GetElementSize();
892        const size_t dataSize = count * elementSize;
893        sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0);
894        sparse->PatchData(elementSize);
895    }
896}
897
898inline unsigned int Accessor::GetNumComponents() {
899    return AttribType::GetNumComponents(type);
900}
901
902inline unsigned int Accessor::GetBytesPerComponent() {
903    return int(ComponentTypeSize(componentType));
904}
905
906inline unsigned int Accessor::GetElementSize() {
907    return GetNumComponents() * GetBytesPerComponent();
908}
909
910inline uint8_t *Accessor::GetPointer() {
911    if (decodedBuffer)
912        return decodedBuffer->GetPointer();
913
914    if (sparse)
915        return sparse->data.data();
916
917    if (!bufferView || !bufferView->buffer) return nullptr;
918    uint8_t *basePtr = bufferView->buffer->GetPointer();
919    if (!basePtr) return nullptr;
920
921    size_t offset = byteOffset + bufferView->byteOffset;
922
923    // Check if region is encoded.
924    if (bufferView->buffer->EncodedRegion_Current != nullptr) {
925        const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset;
926        const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length;
927
928        if ((offset >= begin) && (offset < end))
929            return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin];
930    }
931
932    return basePtr + offset;
933}
934
935inline size_t Accessor::GetStride() {
936    // Decoded buffer is always packed
937    if (decodedBuffer)
938        return GetElementSize();
939
940    // Sparse and normal bufferView
941    return (bufferView && bufferView->byteStride ? bufferView->byteStride : GetElementSize());
942}
943
944inline size_t Accessor::GetMaxByteSize() {
945    if (decodedBuffer)
946        return decodedBuffer->byteLength;
947
948    return (bufferView ? bufferView->byteLength : sparse->data.size());
949}
950
951template <class T>
952void Accessor::ExtractData(T *&outData) {
953    uint8_t *data = GetPointer();
954    if (!data) {
955        throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
956    }
957
958    const size_t elemSize = GetElementSize();
959    const size_t totalSize = elemSize * count;
960
961    const size_t stride = GetStride();
962
963    const size_t targetElemSize = sizeof(T);
964
965    if (elemSize > targetElemSize) {
966        throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
967    }
968
969    const size_t maxSize = GetMaxByteSize();
970    if (count * stride > maxSize) {
971        throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
972    }
973
974    outData = new T[count];
975    if (stride == elemSize && targetElemSize == elemSize) {
976        memcpy(outData, data, totalSize);
977    } else {
978        for (size_t i = 0; i < count; ++i) {
979            memcpy(outData + i, data + i * stride, elemSize);
980        }
981    }
982}
983
984inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {
985    uint8_t *buffer_ptr = bufferView->buffer->GetPointer();
986    size_t offset = byteOffset + bufferView->byteOffset;
987
988    size_t dst_stride = GetNumComponents() * GetBytesPerComponent();
989
990    const uint8_t *src = reinterpret_cast<const uint8_t *>(src_buffer);
991    uint8_t *dst = reinterpret_cast<uint8_t *>(buffer_ptr + offset);
992
993    ai_assert(dst + _count * dst_stride <= buffer_ptr + bufferView->buffer->byteLength);
994    CopyData(_count, src, src_stride, dst, dst_stride);
995}
996
997inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) {
998    if (!sparse)
999        return;
1000
1001    // values
1002    uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer();
1003    size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset;
1004    size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent();
1005    const uint8_t *value_src = reinterpret_cast<const uint8_t *>(src_data);
1006    uint8_t *value_dst = reinterpret_cast<uint8_t *>(value_buffer_ptr + value_offset);
1007    ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength);
1008    CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride);
1009}
1010
1011inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) {
1012    if (!sparse)
1013        return;
1014
1015    // indices
1016    uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer();
1017    size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset;
1018    size_t indices_dst_stride = 1 * sizeof(unsigned short);
1019    const uint8_t *indices_src = reinterpret_cast<const uint8_t *>(src_idx);
1020    uint8_t *indices_dst = reinterpret_cast<uint8_t *>(indices_buffer_ptr + indices_offset);
1021    ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength);
1022    CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride);
1023}
1024
1025inline Accessor::Indexer::Indexer(Accessor &acc) :
1026    accessor(acc),
1027    data(acc.GetPointer()),
1028    elemSize(acc.GetElementSize()),
1029    stride(acc.GetStride()) {
1030}
1031
1032//! Accesses the i-th value as defined by the accessor
1033template <class T>
1034T Accessor::Indexer::GetValue(int i) {
1035    ai_assert(data);
1036    if (i * stride >= accessor.GetMaxByteSize()) {
1037        throw DeadlyImportError("GLTF: Invalid index ", i, ", count out of range for buffer with stride ", stride, " and size ", accessor.GetMaxByteSize(), ".");
1038    }
1039    // Ensure that the memcpy doesn't overwrite the local.
1040    const size_t sizeToCopy = std::min(elemSize, sizeof(T));
1041    T value = T();
1042    // Assume platform endianness matches GLTF binary data (which is little-endian).
1043    memcpy(&value, data + i * stride, sizeToCopy);
1044    return value;
1045}
1046
1047inline Image::Image() :
1048        width(0),
1049        height(0),
1050        mDataLength(0) {
1051}
1052
1053inline void Image::Read(Value &obj, Asset &r) {
1054    //basisu: no need to handle .ktx2, .basis, load as is
1055    if (!mDataLength) {
1056        Value *curUri = FindString(obj, "uri");
1057        if (nullptr != curUri) {
1058            const char *uristr = curUri->GetString();
1059
1060            glTFCommon::Util::DataURI dataURI;
1061            if (ParseDataURI(uristr, curUri->GetStringLength(), dataURI)) {
1062                mimeType = dataURI.mediaType;
1063                if (dataURI.base64) {
1064                    uint8_t *ptr = nullptr;
1065                    mDataLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr);
1066                    mData.reset(ptr);
1067                }
1068            } else {
1069                this->uri = uristr;
1070            }
1071        } else if (Value *bufferViewVal = FindUInt(obj, "bufferView")) {
1072            this->bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint());
1073            if (Value *mtype = FindString(obj, "mimeType")) {
1074                this->mimeType = mtype->GetString();
1075            }
1076            if (!this->bufferView || this->mimeType.empty()) {
1077                throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " does not have a URI, so it must have a valid bufferView and mimetype");
1078            }
1079
1080            Ref<Buffer> buffer = this->bufferView->buffer;
1081
1082            this->mDataLength = this->bufferView->byteLength;
1083            // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction.
1084
1085            this->mData.reset(new uint8_t[this->mDataLength]);
1086            memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength);
1087        } else {
1088            throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " should have either a URI of a bufferView and mimetype");
1089        }
1090    }
1091}
1092
1093inline uint8_t *Image::StealData() {
1094    mDataLength = 0;
1095    return mData.release();
1096}
1097
1098// Never take over the ownership of data whenever binary or not
1099inline void Image::SetData(uint8_t *data, size_t length, Asset &r) {
1100    Ref<Buffer> b = r.GetBodyBuffer();
1101    if (b) { // binary file: append to body
1102        std::string bvId = r.FindUniqueID(this->id, "imgdata");
1103        bufferView = r.bufferViews.Create(bvId);
1104
1105        bufferView->buffer = b;
1106        bufferView->byteLength = length;
1107        bufferView->byteOffset = b->AppendData(data, length);
1108    } else { // text file: will be stored as a data uri
1109        uint8_t *temp = new uint8_t[length];
1110        memcpy(temp, data, length);
1111        this->mData.reset(temp);
1112        this->mDataLength = length;
1113    }
1114}
1115
1116inline void Sampler::Read(Value &obj, Asset & /*r*/) {
1117    SetDefaults();
1118
1119    ReadMember(obj, "name", name);
1120    ReadMember(obj, "magFilter", magFilter);
1121    ReadMember(obj, "minFilter", minFilter);
1122    ReadMember(obj, "wrapS", wrapS);
1123    ReadMember(obj, "wrapT", wrapT);
1124}
1125
1126inline void Sampler::SetDefaults() {
1127    //only wrapping modes have defaults
1128    wrapS = SamplerWrap::Repeat;
1129    wrapT = SamplerWrap::Repeat;
1130    magFilter = SamplerMagFilter::UNSET;
1131    minFilter = SamplerMinFilter::UNSET;
1132}
1133
1134inline void Texture::Read(Value &obj, Asset &r) {
1135    if (Value *sourceVal = FindUInt(obj, "source")) {
1136        source = r.images.Retrieve(sourceVal->GetUint());
1137    }
1138
1139    if (Value *samplerVal = FindUInt(obj, "sampler")) {
1140        sampler = r.samplers.Retrieve(samplerVal->GetUint());
1141    }
1142}
1143
1144void Material::SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
1145    if (r.extensionsUsed.KHR_texture_transform) {
1146        if (Value *pKHR_texture_transform = FindExtension(*prop, "KHR_texture_transform")) {
1147            out.textureTransformSupported = true;
1148            if (Value *array = FindArray(*pKHR_texture_transform, "offset")) {
1149                out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat();
1150                out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat();
1151            } else {
1152                out.TextureTransformExt_t.offset[0] = 0;
1153                out.TextureTransformExt_t.offset[1] = 0;
1154            }
1155
1156            if (!ReadMember(*pKHR_texture_transform, "rotation", out.TextureTransformExt_t.rotation)) {
1157                out.TextureTransformExt_t.rotation = 0;
1158            }
1159
1160            if (Value *array = FindArray(*pKHR_texture_transform, "scale")) {
1161                out.TextureTransformExt_t.scale[0] = (*array)[0].GetFloat();
1162                out.TextureTransformExt_t.scale[1] = (*array)[1].GetFloat();
1163            } else {
1164                out.TextureTransformExt_t.scale[0] = 1;
1165                out.TextureTransformExt_t.scale[1] = 1;
1166            }
1167        }
1168    }
1169
1170    if (Value *indexProp = FindUInt(*prop, "index")) {
1171        out.texture = r.textures.Retrieve(indexProp->GetUint());
1172    }
1173
1174    if (Value *texcoord = FindUInt(*prop, "texCoord")) {
1175        out.texCoord = texcoord->GetUint();
1176    }
1177}
1178
1179inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out) {
1180    if (Value *prop = FindMember(vals, propName)) {
1181        SetTextureProperties(r, prop, out);
1182    }
1183}
1184
1185inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out) {
1186    if (Value *prop = FindMember(vals, propName)) {
1187        SetTextureProperties(r, prop, out);
1188
1189        if (Value *scale = FindNumber(*prop, "scale")) {
1190            out.scale = static_cast<float>(scale->GetDouble());
1191        }
1192    }
1193}
1194
1195inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out) {
1196    if (Value *prop = FindMember(vals, propName)) {
1197        SetTextureProperties(r, prop, out);
1198
1199        if (Value *strength = FindNumber(*prop, "strength")) {
1200            out.strength = static_cast<float>(strength->GetDouble());
1201        }
1202    }
1203}
1204
1205inline void Material::Read(Value &material, Asset &r) {
1206    SetDefaults();
1207
1208    if (Value *curPbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) {
1209        ReadMember(*curPbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor);
1210        ReadTextureProperty(r, *curPbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture);
1211        ReadTextureProperty(r, *curPbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture);
1212        ReadMember(*curPbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor);
1213        ReadMember(*curPbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor);
1214    }
1215
1216    ReadTextureProperty(r, material, "normalTexture", this->normalTexture);
1217    ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture);
1218    ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture);
1219    ReadMember(material, "emissiveFactor", this->emissiveFactor);
1220
1221    ReadMember(material, "doubleSided", this->doubleSided);
1222    ReadMember(material, "alphaMode", this->alphaMode);
1223    ReadMember(material, "alphaCutoff", this->alphaCutoff);
1224
1225    if (Value *extensions = FindObject(material, "extensions")) {
1226        if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) {
1227            if (Value *curPbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) {
1228                PbrSpecularGlossiness pbrSG;
1229
1230                ReadMember(*curPbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor);
1231                ReadTextureProperty(r, *curPbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture);
1232                ReadTextureProperty(r, *curPbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture);
1233                ReadMember(*curPbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor);
1234                ReadMember(*curPbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor);
1235
1236                this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
1237            }
1238        }
1239
1240        // Extension KHR_texture_transform is handled in ReadTextureProperty
1241
1242        if (r.extensionsUsed.KHR_materials_sheen) {
1243            if (Value *curMaterialSheen = FindObject(*extensions, "KHR_materials_sheen")) {
1244                MaterialSheen sheen;
1245
1246                ReadMember(*curMaterialSheen, "sheenColorFactor", sheen.sheenColorFactor);
1247                ReadTextureProperty(r, *curMaterialSheen, "sheenColorTexture", sheen.sheenColorTexture);
1248                ReadMember(*curMaterialSheen, "sheenRoughnessFactor", sheen.sheenRoughnessFactor);
1249                ReadTextureProperty(r, *curMaterialSheen, "sheenRoughnessTexture", sheen.sheenRoughnessTexture);
1250
1251                this->materialSheen = Nullable<MaterialSheen>(sheen);
1252            }
1253        }
1254
1255        if (r.extensionsUsed.KHR_materials_clearcoat) {
1256            if (Value *curMaterialClearcoat = FindObject(*extensions, "KHR_materials_clearcoat")) {
1257                MaterialClearcoat clearcoat;
1258
1259                ReadMember(*curMaterialClearcoat, "clearcoatFactor", clearcoat.clearcoatFactor);
1260                ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatTexture", clearcoat.clearcoatTexture);
1261                ReadMember(*curMaterialClearcoat, "clearcoatRoughnessFactor", clearcoat.clearcoatRoughnessFactor);
1262                ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatRoughnessTexture", clearcoat.clearcoatRoughnessTexture);
1263                ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatNormalTexture", clearcoat.clearcoatNormalTexture);
1264
1265                this->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat);
1266            }
1267        }
1268
1269        if (r.extensionsUsed.KHR_materials_transmission) {
1270            if (Value *curMaterialTransmission = FindObject(*extensions, "KHR_materials_transmission")) {
1271                MaterialTransmission transmission;
1272
1273                ReadMember(*curMaterialTransmission, "transmissionFactor", transmission.transmissionFactor);
1274                ReadTextureProperty(r, *curMaterialTransmission, "transmissionTexture", transmission.transmissionTexture);
1275
1276                this->materialTransmission = Nullable<MaterialTransmission>(transmission);
1277            }
1278        }
1279
1280        if (r.extensionsUsed.KHR_materials_volume) {
1281            if (Value *curMaterialVolume = FindObject(*extensions, "KHR_materials_volume")) {
1282                MaterialVolume volume;
1283
1284                ReadMember(*curMaterialVolume, "thicknessFactor", volume.thicknessFactor);
1285                ReadTextureProperty(r, *curMaterialVolume, "thicknessTexture", volume.thicknessTexture);
1286                ReadMember(*curMaterialVolume, "attenuationDistance", volume.attenuationDistance);
1287                ReadMember(*curMaterialVolume, "attenuationColor", volume.attenuationColor);
1288
1289                this->materialVolume = Nullable<MaterialVolume>(volume);
1290            }
1291        }
1292
1293        if (r.extensionsUsed.KHR_materials_ior) {
1294            if (Value *curMaterialIOR = FindObject(*extensions, "KHR_materials_ior")) {
1295                MaterialIOR ior;
1296
1297                ReadMember(*curMaterialIOR, "ior", ior.ior);
1298
1299                this->materialIOR = Nullable<MaterialIOR>(ior);
1300            }
1301        }
1302
1303        unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit");
1304    }
1305}
1306
1307inline void Material::SetDefaults() {
1308    //pbr materials
1309    SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor);
1310    pbrMetallicRoughness.metallicFactor = 1.0f;
1311    pbrMetallicRoughness.roughnessFactor = 1.0f;
1312
1313    SetVector(emissiveFactor, defaultEmissiveFactor);
1314    alphaMode = "OPAQUE";
1315    alphaCutoff = 0.5f;
1316    doubleSided = false;
1317    unlit = false;
1318}
1319
1320inline void PbrSpecularGlossiness::SetDefaults() {
1321    //pbrSpecularGlossiness properties
1322    SetVector(diffuseFactor, defaultDiffuseFactor);
1323    SetVector(specularFactor, defaultSpecularFactor);
1324    glossinessFactor = 1.0f;
1325}
1326
1327inline void MaterialSheen::SetDefaults() {
1328    //KHR_materials_sheen properties
1329    SetVector(sheenColorFactor, defaultSheenFactor);
1330    sheenRoughnessFactor = 0.f;
1331}
1332
1333inline void MaterialVolume::SetDefaults() {
1334    //KHR_materials_volume properties
1335    thicknessFactor = 0.f;
1336    attenuationDistance = INFINITY;
1337    SetVector(attenuationColor, defaultAttenuationColor);
1338}
1339
1340inline void MaterialIOR::SetDefaults() {
1341    //KHR_materials_ior properties
1342    ior = 1.5f;
1343}
1344
1345inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
1346    Value *curName = FindMember(pJSON_Object, "name");
1347    if (nullptr != curName && curName->IsString()) {
1348        name = curName->GetString();
1349    }
1350
1351    /****************** Mesh primitives ******************/
1352    Value *curPrimitives = FindArray(pJSON_Object, "primitives");
1353    if (nullptr != curPrimitives) {
1354        this->primitives.resize(curPrimitives->Size());
1355        for (unsigned int i = 0; i < curPrimitives->Size(); ++i) {
1356            Value &primitive = (*curPrimitives)[i];
1357
1358            Primitive &prim = this->primitives[i];
1359            prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
1360
1361            if (Value *indices = FindUInt(primitive, "indices")) {
1362                prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
1363            }
1364
1365            if (Value *material = FindUInt(primitive, "material")) {
1366                prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
1367            }
1368
1369            if (Value *attrs = FindObject(primitive, "attributes")) {
1370                for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
1371                    if (!it->value.IsUint()) continue;
1372                    const char *attr = it->name.GetString();
1373                    // Valid attribute semantics include POSITION, NORMAL, TANGENT, TEXCOORD, COLOR, JOINT, JOINTMATRIX,
1374                    // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
1375
1376                    int undPos = 0;
1377                    Mesh::AccessorList *vec = nullptr;
1378                    if (GetAttribVector(prim, attr, vec, undPos)) {
1379                        size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
1380                        if ((*vec).size() != idx) {
1381                            throw DeadlyImportError("GLTF: Invalid attribute in mesh: ", name, " primitive: ", i, "attrib: ", attr,
1382                                    ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
1383                        }
1384                        (*vec).resize(idx + 1);
1385                        (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
1386                    }
1387                }
1388            }
1389
1390#ifdef ASSIMP_ENABLE_DRACO
1391            // KHR_draco_mesh_compression spec: Draco can only be used for glTF Triangles or Triangle Strips
1392            if (pAsset_Root.extensionsUsed.KHR_draco_mesh_compression && (prim.mode == PrimitiveMode_TRIANGLES || prim.mode == PrimitiveMode_TRIANGLE_STRIP)) {
1393                // Look for draco mesh compression extension and bufferView
1394                // Skip if any missing
1395                if (Value *dracoExt = FindExtension(primitive, "KHR_draco_mesh_compression")) {
1396                    if (Value *bufView = FindUInt(*dracoExt, "bufferView")) {
1397                        // Attempt to load indices and attributes using draco compression
1398                        auto bufferView = pAsset_Root.bufferViews.Retrieve(bufView->GetUint());
1399                        // Attempt to perform the draco decode on the buffer data
1400                        const char *bufferViewData = reinterpret_cast<const char *>(bufferView->buffer->GetPointer() + bufferView->byteOffset);
1401                        draco::DecoderBuffer decoderBuffer;
1402                        decoderBuffer.Init(bufferViewData, bufferView->byteLength);
1403                        draco::Decoder decoder;
1404                        auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
1405                        if (!decodeResult.ok()) {
1406                            // A corrupt Draco isn't actually fatal if the primitive data is also provided in a standard buffer, but does anyone do that?
1407                            throw DeadlyImportError("GLTF: Invalid Draco mesh compression in mesh: ", name, " primitive: ", i, ": ", decodeResult.status().error_msg_string());
1408                        }
1409
1410                        // Now we have a draco mesh
1411                        const std::unique_ptr<draco::Mesh> &pDracoMesh = decodeResult.value();
1412
1413                        // Redirect the accessors to the decoded data
1414
1415                        // Indices
1416                        SetDecodedIndexBuffer_Draco(*pDracoMesh, prim);
1417
1418                        // Vertex attributes
1419                        if (Value *attrs = FindObject(*dracoExt, "attributes")) {
1420                            for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
1421                                if (!it->value.IsUint()) continue;
1422                                const char *attr = it->name.GetString();
1423
1424                                int undPos = 0;
1425                                Mesh::AccessorList *vec = nullptr;
1426                                if (GetAttribVector(prim, attr, vec, undPos)) {
1427                                    size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
1428                                    if (idx >= (*vec).size()) {
1429                                        throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr,
1430                                                ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
1431                                    }
1432
1433                                    if (!(*vec)[idx]) {
1434                                        throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr,
1435                                                ". All draco-encoded attributes must also define an accessor.");
1436                                    }
1437
1438                                    Accessor &attribAccessor = *(*vec)[idx];
1439                                    if (attribAccessor.count == 0)
1440                                        throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr);
1441
1442                                    // Redirect this accessor to the appropriate Draco vertex attribute data
1443                                    const uint32_t dracoAttribId = it->value.GetUint();
1444                                    SetDecodedAttributeBuffer_Draco(*pDracoMesh, dracoAttribId, attribAccessor);
1445                                }
1446                            }
1447                        }
1448                    }
1449                }
1450            }
1451#endif
1452
1453            Value *targetsArray = FindArray(primitive, "targets");
1454            if (nullptr != targetsArray) {
1455                prim.targets.resize(targetsArray->Size());
1456                for (unsigned int j = 0; j < targetsArray->Size(); ++j) {
1457                    Value &target = (*targetsArray)[j];
1458                    if (!target.IsObject()) {
1459                        continue;
1460                    }
1461                    for (Value::MemberIterator it = target.MemberBegin(); it != target.MemberEnd(); ++it) {
1462                        if (!it->value.IsUint()) {
1463                            continue;
1464                        }
1465                        const char *attr = it->name.GetString();
1466                        // Valid attribute semantics include POSITION, NORMAL, TANGENT
1467                        int undPos = 0;
1468                        Mesh::AccessorList *vec = nullptr;
1469                        if (GetAttribTargetVector(prim, j, attr, vec, undPos)) {
1470                            size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
1471                            if ((*vec).size() <= idx) {
1472                                (*vec).resize(idx + 1);
1473                            }
1474                            (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
1475                        }
1476                    }
1477                }
1478            }
1479        }
1480    }
1481
1482    Value *curWeights = FindArray(pJSON_Object, "weights");
1483    if (nullptr != curWeights) {
1484        this->weights.resize(curWeights->Size());
1485        for (unsigned int i = 0; i < curWeights->Size(); ++i) {
1486            Value &weightValue = (*curWeights)[i];
1487            if (weightValue.IsNumber()) {
1488                this->weights[i] = weightValue.GetFloat();
1489            }
1490        }
1491    }
1492
1493    Value *curExtras = FindObject(pJSON_Object, "extras");
1494    if (nullptr != curExtras) {
1495        if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) {
1496            this->targetNames.resize(curTargetNames->Size());
1497            for (unsigned int i = 0; i < curTargetNames->Size(); ++i) {
1498                Value &targetNameValue = (*curTargetNames)[i];
1499                if (targetNameValue.IsString()) {
1500                    this->targetNames[i] = targetNameValue.GetString();
1501                }
1502            }
1503        }
1504    }
1505}
1506
1507inline void Camera::Read(Value &obj, Asset & /*r*/) {
1508    std::string type_string = std::string(MemberOrDefault(obj, "type", "perspective"));
1509    if (type_string == "orthographic") {
1510        type = Camera::Orthographic;
1511    } else {
1512        type = Camera::Perspective;
1513    }
1514
1515    const char *subobjId = (type == Camera::Orthographic) ? "orthographic" : "perspective";
1516
1517    Value *it = FindObject(obj, subobjId);
1518    if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters");
1519
1520    if (type == Camera::Perspective) {
1521        cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f);
1522        cameraProperties.perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f / 2.f);
1523        cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f);
1524        cameraProperties.perspective.znear = MemberOrDefault(*it, "znear", 0.01f);
1525    } else {
1526        cameraProperties.ortographic.xmag = MemberOrDefault(*it, "xmag", 1.f);
1527        cameraProperties.ortographic.ymag = MemberOrDefault(*it, "ymag", 1.f);
1528        cameraProperties.ortographic.zfar = MemberOrDefault(*it, "zfar", 100.f);
1529        cameraProperties.ortographic.znear = MemberOrDefault(*it, "znear", 0.01f);
1530    }
1531}
1532
1533inline void Light::Read(Value &obj, Asset & /*r*/) {
1534#ifndef M_PI
1535    const float M_PI = 3.14159265358979323846f;
1536#endif
1537
1538    std::string type_string;
1539    ReadMember(obj, "type", type_string);
1540    if (type_string == "directional")
1541        type = Light::Directional;
1542    else if (type_string == "point")
1543        type = Light::Point;
1544    else
1545        type = Light::Spot;
1546
1547    name = MemberOrDefault(obj, "name", "");
1548
1549    SetVector(color, vec3{ 1.0f, 1.0f, 1.0f });
1550    ReadMember(obj, "color", color);
1551
1552    intensity = MemberOrDefault(obj, "intensity", 1.0f);
1553
1554    ReadMember(obj, "range", range);
1555
1556    if (type == Light::Spot) {
1557        Value *spot = FindObject(obj, "spot");
1558        if (!spot) throw DeadlyImportError("GLTF: Light missing its spot parameters");
1559        innerConeAngle = MemberOrDefault(*spot, "innerConeAngle", 0.0f);
1560        outerConeAngle = MemberOrDefault(*spot, "outerConeAngle", static_cast<float>(M_PI / 4.0f));
1561    }
1562}
1563
1564inline void Node::Read(Value &obj, Asset &r) {
1565    if (name.empty()) {
1566        name = id;
1567    }
1568
1569    Value *curChildren = FindArray(obj, "children");
1570    if (nullptr != curChildren) {
1571        this->children.reserve(curChildren->Size());
1572        for (unsigned int i = 0; i < curChildren->Size(); ++i) {
1573            Value &child = (*curChildren)[i];
1574            if (child.IsUint()) {
1575                // get/create the child node
1576                Ref<Node> chn = r.nodes.Retrieve(child.GetUint());
1577                if (chn) {
1578                    this->children.push_back(chn);
1579                }
1580            }
1581        }
1582    }
1583
1584    Value *curMatrix = FindArray(obj, "matrix");
1585    if (nullptr != curMatrix) {
1586        ReadValue(*curMatrix, this->matrix);
1587    } else {
1588        ReadMember(obj, "translation", translation);
1589        ReadMember(obj, "scale", scale);
1590        ReadMember(obj, "rotation", rotation);
1591    }
1592
1593    Value *curMesh = FindUInt(obj, "mesh");
1594    if (nullptr != curMesh) {
1595        unsigned int numMeshes = 1;
1596        this->meshes.reserve(numMeshes);
1597        Ref<Mesh> meshRef = r.meshes.Retrieve((*curMesh).GetUint());
1598        if (meshRef) {
1599            this->meshes.push_back(meshRef);
1600        }
1601    }
1602
1603    // Do not retrieve a skin here, just take a reference, to avoid infinite recursion
1604    // Skins will be properly loaded later
1605    Value *curSkin = FindUInt(obj, "skin");
1606    if (nullptr != curSkin) {
1607        this->skin = r.skins.Get(curSkin->GetUint());
1608    }
1609
1610    Value *curCamera = FindUInt(obj, "camera");
1611    if (nullptr != curCamera) {
1612        this->camera = r.cameras.Retrieve(curCamera->GetUint());
1613        if (this->camera) {
1614            this->camera->id = this->id;
1615        }
1616    }
1617
1618    Value *curExtensions = FindObject(obj, "extensions");
1619    if (nullptr != curExtensions) {
1620        if (r.extensionsUsed.KHR_lights_punctual) {
1621            if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) {
1622                Value *curLight = FindUInt(*ext, "light");
1623                if (nullptr != curLight) {
1624                    this->light = r.lights.Retrieve(curLight->GetUint());
1625                    if (this->light) {
1626                        this->light->id = this->id;
1627                    }
1628                }
1629            }
1630        }
1631    }
1632}
1633
1634inline void Scene::Read(Value &obj, Asset &r) {
1635    if (Value *scene_name = FindString(obj, "name")) {
1636        if (scene_name->IsString()) {
1637            this->name = scene_name->GetString();
1638        }
1639    }
1640    if (Value *array = FindArray(obj, "nodes")) {
1641        for (unsigned int i = 0; i < array->Size(); ++i) {
1642            if (!(*array)[i].IsUint()) continue;
1643            Ref<Node> node = r.nodes.Retrieve((*array)[i].GetUint());
1644            if (node)
1645                this->nodes.push_back(node);
1646        }
1647    }
1648}
1649
1650inline void Skin::Read(Value &obj, Asset &r) {
1651    if (Value *matrices = FindUInt(obj, "inverseBindMatrices")) {
1652        inverseBindMatrices = r.accessors.Retrieve(matrices->GetUint());
1653    }
1654
1655    if (Value *joints = FindArray(obj, "joints")) {
1656        for (unsigned i = 0; i < joints->Size(); ++i) {
1657            if (!(*joints)[i].IsUint()) continue;
1658            Ref<Node> node = r.nodes.Retrieve((*joints)[i].GetUint());
1659            if (node) {
1660                this->jointNames.push_back(node);
1661            }
1662        }
1663    }
1664}
1665
1666inline void Animation::Read(Value &obj, Asset &r) {
1667    Value *curSamplers = FindArray(obj, "samplers");
1668    if (nullptr != curSamplers) {
1669        for (unsigned i = 0; i < curSamplers->Size(); ++i) {
1670            Value &sampler = (*curSamplers)[i];
1671
1672            Sampler s;
1673            if (Value *input = FindUInt(sampler, "input")) {
1674                s.input = r.accessors.Retrieve(input->GetUint());
1675            }
1676            if (Value *output = FindUInt(sampler, "output")) {
1677                s.output = r.accessors.Retrieve(output->GetUint());
1678            }
1679            s.interpolation = Interpolation_LINEAR;
1680            if (Value *interpolation = FindString(sampler, "interpolation")) {
1681                const std::string interp = interpolation->GetString();
1682                if (interp == "LINEAR") {
1683                    s.interpolation = Interpolation_LINEAR;
1684                } else if (interp == "STEP") {
1685                    s.interpolation = Interpolation_STEP;
1686                } else if (interp == "CUBICSPLINE") {
1687                    s.interpolation = Interpolation_CUBICSPLINE;
1688                }
1689            }
1690            this->samplers.push_back(s);
1691        }
1692    }
1693
1694    Value *curChannels = FindArray(obj, "channels");
1695    if (nullptr != curChannels) {
1696        for (unsigned i = 0; i < curChannels->Size(); ++i) {
1697            Value &channel = (*curChannels)[i];
1698
1699            Channel c;
1700            Value *curSampler = FindUInt(channel, "sampler");
1701            if (nullptr != curSampler) {
1702                c.sampler = curSampler->GetUint();
1703            }
1704
1705            if (Value *target = FindObject(channel, "target")) {
1706                if (Value *node = FindUInt(*target, "node")) {
1707                    c.target.node = r.nodes.Retrieve(node->GetUint());
1708                }
1709                if (Value *path = FindString(*target, "path")) {
1710                    const std::string p = path->GetString();
1711                    if (p == "translation") {
1712                        c.target.path = AnimationPath_TRANSLATION;
1713                    } else if (p == "rotation") {
1714                        c.target.path = AnimationPath_ROTATION;
1715                    } else if (p == "scale") {
1716                        c.target.path = AnimationPath_SCALE;
1717                    } else if (p == "weights") {
1718                        c.target.path = AnimationPath_WEIGHTS;
1719                    }
1720                }
1721            }
1722            this->channels.push_back(c);
1723        }
1724    }
1725}
1726
1727inline void AssetMetadata::Read(Document &doc) {
1728    if (Value *obj = FindObject(doc, "asset")) {
1729        ReadMember(*obj, "copyright", copyright);
1730        ReadMember(*obj, "generator", generator);
1731
1732        if (Value *versionString = FindStringInContext(*obj, "version", "\"asset\"")) {
1733            version = versionString->GetString();
1734        }
1735        Value *curProfile = FindObjectInContext(*obj, "profile", "\"asset\"");
1736        if (nullptr != curProfile) {
1737            ReadMember(*curProfile, "api", this->profile.api);
1738            ReadMember(*curProfile, "version", this->profile.version);
1739        }
1740    }
1741
1742    if (version.empty() || version[0] != '2') {
1743        throw DeadlyImportError("GLTF: Unsupported glTF version: ", version);
1744    }
1745}
1746
1747//
1748// Asset methods implementation
1749//
1750
1751inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData) {
1752    ASSIMP_LOG_DEBUG("Reading GLTF2 binary");
1753    GLB_Header header;
1754    if (stream.Read(&header, sizeof(header), 1) != 1) {
1755        throw DeadlyImportError("GLTF: Unable to read the file header");
1756    }
1757
1758    if (strncmp((char *)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
1759        throw DeadlyImportError("GLTF: Invalid binary glTF file");
1760    }
1761
1762    AI_SWAP4(header.version);
1763    asset.version = ai_to_string(header.version);
1764    if (header.version != 2) {
1765        throw DeadlyImportError("GLTF: Unsupported binary glTF version");
1766    }
1767
1768    GLB_Chunk chunk;
1769    if (stream.Read(&chunk, sizeof(chunk), 1) != 1) {
1770        throw DeadlyImportError("GLTF: Unable to read JSON chunk");
1771    }
1772
1773    AI_SWAP4(chunk.chunkLength);
1774    AI_SWAP4(chunk.chunkType);
1775
1776    if (chunk.chunkType != ChunkType_JSON) {
1777        throw DeadlyImportError("GLTF: JSON chunk missing");
1778    }
1779
1780    // read the scene data, ensure null termination
1781    static_assert(std::numeric_limits<uint32_t>::max() <= std::numeric_limits<size_t>::max(), "size_t must be at least 32bits");
1782    mSceneLength = chunk.chunkLength; // Can't be larger than 4GB (max. uint32_t)
1783    sceneData.resize(mSceneLength + 1);
1784    sceneData[mSceneLength] = '\0';
1785
1786    if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
1787        throw DeadlyImportError("GLTF: Could not read the file contents");
1788    }
1789
1790    uint32_t padding = ((chunk.chunkLength + 3) & ~3) - chunk.chunkLength;
1791    if (padding > 0) {
1792        stream.Seek(padding, aiOrigin_CUR);
1793    }
1794
1795    AI_SWAP4(header.length);
1796    mBodyOffset = 12 + 8 + chunk.chunkLength + padding + 8;
1797    if (header.length >= mBodyOffset) {
1798        if (stream.Read(&chunk, sizeof(chunk), 1) != 1) {
1799            throw DeadlyImportError("GLTF: Unable to read BIN chunk");
1800        }
1801
1802        AI_SWAP4(chunk.chunkLength);
1803        AI_SWAP4(chunk.chunkType);
1804
1805        if (chunk.chunkType != ChunkType_BIN) {
1806            throw DeadlyImportError("GLTF: BIN chunk missing");
1807        }
1808
1809        mBodyLength = chunk.chunkLength;
1810    } else {
1811        mBodyOffset = mBodyLength = 0;
1812    }
1813}
1814
1815inline rapidjson::Document Asset::ReadDocument(IOStream &stream, bool isBinary, std::vector<char> &sceneData) {
1816    ASSIMP_LOG_DEBUG("Loading GLTF2 asset");
1817
1818    // is binary? then read the header
1819    if (isBinary) {
1820        SetAsBinary(); // also creates the body buffer
1821        ReadBinaryHeader(stream, sceneData);
1822    } else {
1823        mSceneLength = stream.FileSize();
1824        mBodyLength = 0;
1825
1826        // Binary format only supports up to 4GB of JSON, use that as a maximum
1827        if (mSceneLength >= std::numeric_limits<uint32_t>::max()) {
1828            throw DeadlyImportError("GLTF: JSON size greater than 4GB");
1829        }
1830
1831        // read the scene data, ensure null termination
1832        sceneData.resize(mSceneLength + 1);
1833        sceneData[mSceneLength] = '\0';
1834
1835        if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
1836            throw DeadlyImportError("GLTF: Could not read the file contents");
1837        }
1838    }
1839
1840    // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later
1841    if (mSceneLength < 2) {
1842        throw DeadlyImportError("GLTF: No JSON file contents");
1843    }
1844
1845    // parse the JSON document
1846    ASSIMP_LOG_DEBUG("Parsing GLTF2 JSON");
1847    Document doc;
1848    doc.ParseInsitu(&sceneData[0]);
1849
1850    if (doc.HasParseError()) {
1851        char buffer[32];
1852        ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
1853        throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError()));
1854    }
1855
1856    if (!doc.IsObject()) {
1857        throw DeadlyImportError("GLTF: JSON document root must be a JSON object");
1858    }
1859
1860    return doc;
1861}
1862
1863inline void Asset::Load(const std::string &pFile, bool isBinary)
1864{
1865    mCurrentAssetDir.clear();
1866    if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) {
1867        mCurrentAssetDir = glTFCommon::getCurrentAssetDir(pFile);
1868    }
1869
1870    shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
1871    if (!stream) {
1872        throw DeadlyImportError("GLTF: Could not open file for reading");
1873    }
1874
1875    std::vector<char> sceneData;
1876    rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData);
1877
1878    // If a schemaDocumentProvider is available, see if the glTF schema is present.
1879    // If so, use it to validate the document.
1880    if (mSchemaDocumentProvider) {
1881        if (const rapidjson::SchemaDocument *gltfSchema = mSchemaDocumentProvider->GetRemoteDocument("glTF.schema.json", 16)) {
1882            // The schemas are found here: https://github.com/KhronosGroup/glTF/tree/main/specification/2.0/schema
1883            rapidjson::SchemaValidator validator(*gltfSchema);
1884            if (!doc.Accept(validator)) {
1885                rapidjson::StringBuffer pathBuffer;
1886                validator.GetInvalidSchemaPointer().StringifyUriFragment(pathBuffer);
1887                rapidjson::StringBuffer argumentBuffer;
1888                validator.GetInvalidDocumentPointer().StringifyUriFragment(argumentBuffer);
1889                throw DeadlyImportError("GLTF: The JSON document did not satisfy the glTF2 schema. Schema keyword: ", validator.GetInvalidSchemaKeyword(), ", document path: ", pathBuffer.GetString(), ", argument: ", argumentBuffer.GetString());
1890            }
1891        }
1892    }
1893
1894    // Fill the buffer instance for the current file embedded contents
1895    if (mBodyLength > 0) {
1896        if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) {
1897            throw DeadlyImportError("GLTF: Unable to read gltf file");
1898        }
1899    }
1900
1901    // Load the metadata
1902    asset.Read(doc);
1903    ReadExtensionsUsed(doc);
1904    ReadExtensionsRequired(doc);
1905
1906#ifndef ASSIMP_ENABLE_DRACO
1907    // Is Draco required?
1908    if (extensionsRequired.KHR_draco_mesh_compression) {
1909        throw DeadlyImportError("GLTF: Draco mesh compression not supported.");
1910    }
1911#endif
1912
1913    // Prepare the dictionaries
1914    for (size_t i = 0; i < mDicts.size(); ++i) {
1915        mDicts[i]->AttachToDocument(doc);
1916    }
1917
1918    // Read the "scene" property, which specifies which scene to load
1919    // and recursively load everything referenced by it
1920    unsigned int sceneIndex = 0;
1921    Value *curScene = FindUInt(doc, "scene");
1922    if (nullptr != curScene) {
1923        sceneIndex = curScene->GetUint();
1924    }
1925
1926    if (Value *scenesArray = FindArray(doc, "scenes")) {
1927        if (sceneIndex < scenesArray->Size()) {
1928            this->scene = scenes.Retrieve(sceneIndex);
1929        }
1930    }
1931
1932    if (Value *skinsArray = FindArray(doc, "skins")) {
1933        for (unsigned int i = 0; i < skinsArray->Size(); ++i) {
1934            skins.Retrieve(i);
1935        }
1936    }
1937
1938    if (Value *animsArray = FindArray(doc, "animations")) {
1939        for (unsigned int i = 0; i < animsArray->Size(); ++i) {
1940            animations.Retrieve(i);
1941        }
1942    }
1943
1944    // Clean up
1945    for (size_t i = 0; i < mDicts.size(); ++i) {
1946        mDicts[i]->DetachFromDocument();
1947    }
1948}
1949
1950inline bool Asset::CanRead(const std::string &pFile, bool isBinary) {
1951    try {
1952        shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
1953        if (!stream) {
1954            return false;
1955        }
1956        std::vector<char> sceneData;
1957        rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData);
1958        asset.Read(doc);
1959    } catch (...) {
1960        return false;
1961    }
1962    return true;
1963}
1964
1965inline void Asset::SetAsBinary() {
1966    if (!mBodyBuffer) {
1967        mBodyBuffer = buffers.Create("binary_glTF");
1968        mBodyBuffer->MarkAsSpecial();
1969    }
1970}
1971
1972// As required extensions are only a concept in glTF 2.0, this is here
1973// instead of glTFCommon.h
1974#define CHECK_REQUIRED_EXT(EXT) \
1975    if (exts.find(#EXT) != exts.end()) extensionsRequired.EXT = true;
1976
1977inline void Asset::ReadExtensionsRequired(Document &doc) {
1978    Value *extsRequired = FindArray(doc, "extensionsRequired");
1979    if (nullptr == extsRequired) {
1980        return;
1981    }
1982
1983    std::gltf_unordered_map<std::string, bool> exts;
1984    for (unsigned int i = 0; i < extsRequired->Size(); ++i) {
1985        if ((*extsRequired)[i].IsString()) {
1986            exts[(*extsRequired)[i].GetString()] = true;
1987        }
1988    }
1989
1990    CHECK_REQUIRED_EXT(KHR_draco_mesh_compression);
1991
1992#undef CHECK_REQUIRED_EXT
1993}
1994
1995inline void Asset::ReadExtensionsUsed(Document &doc) {
1996    Value *extsUsed = FindArray(doc, "extensionsUsed");
1997    if (!extsUsed) return;
1998
1999    std::gltf_unordered_map<std::string, bool> exts;
2000
2001    for (unsigned int i = 0; i < extsUsed->Size(); ++i) {
2002        if ((*extsUsed)[i].IsString()) {
2003            exts[(*extsUsed)[i].GetString()] = true;
2004        }
2005    }
2006
2007    CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
2008    CHECK_EXT(KHR_materials_unlit);
2009    CHECK_EXT(KHR_lights_punctual);
2010    CHECK_EXT(KHR_texture_transform);
2011    CHECK_EXT(KHR_materials_sheen);
2012    CHECK_EXT(KHR_materials_clearcoat);
2013    CHECK_EXT(KHR_materials_transmission);
2014    CHECK_EXT(KHR_materials_volume);
2015    CHECK_EXT(KHR_materials_ior);
2016    CHECK_EXT(KHR_draco_mesh_compression);
2017    CHECK_EXT(KHR_texture_basisu);
2018
2019#undef CHECK_EXT
2020}
2021
2022inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool /*absolute*/) {
2023#ifdef ASSIMP_API
2024    return mIOSystem->Open(path, mode);
2025#else
2026    if (path.size() < 2) return nullptr;
2027    if (!absolute && path[1] != ':' && path[0] != '/') { // relative?
2028        path = mCurrentAssetDir + path;
2029    }
2030    FILE *f = fopen(path.c_str(), mode);
2031    return f ? new IOStream(f) : nullptr;
2032#endif
2033}
2034
2035inline std::string Asset::FindUniqueID(const std::string &str, const char *suffix) {
2036    std::string id = str;
2037
2038    if (!id.empty()) {
2039        if (mUsedIds.find(id) == mUsedIds.end())
2040            return id;
2041
2042        id += "_";
2043    }
2044
2045    id += suffix;
2046
2047    Asset::IdMap::iterator it = mUsedIds.find(id);
2048    if (it == mUsedIds.end()) {
2049        return id;
2050    }
2051
2052    std::vector<char> buffer;
2053    buffer.resize(id.size() + 16);
2054    int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str());
2055    for (int i = 0; it != mUsedIds.end(); ++i) {
2056        ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i);
2057        id = buffer.data();
2058        it = mUsedIds.find(id);
2059    }
2060
2061    return id;
2062}
2063
2064#if _MSC_VER
2065#   pragma warning(pop)
2066#endif // _MSC_VER
2067
2068} // namespace glTF2
2069