Coverage Report

Created: 2025-12-05 06:25

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