Coverage Report

Created: 2025-06-22 07:30

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