/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 |