1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42#include "StringUtils.h"
43#include <iomanip>
44
45// Header files, Assimp
46#include <assimp/DefaultLogger.hpp>
47
48#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
49 // Header files, Open3DGC.
50# include <Open3DGC/o3dgcSC3DMCDecoder.h>
51#endif
52
53using namespace Assimp;
54
55namespace glTF {
56
57namespace {
58
59 //
60 // JSON Value reading helpers
61 //
62
63 template<class T>
64 struct ReadHelper { static bool Read(Value& val, T& out) {
65 return val.IsInt() ? out = static_cast<T>(val.GetInt()), true : false;
66 }};
67
68 template<> struct ReadHelper<bool> { static bool Read(Value& val, bool& out) {
69 return val.IsBool() ? out = val.GetBool(), true : false;
70 }};
71
72 template<> struct ReadHelper<float> { static bool Read(Value& val, float& out) {
73 return val.IsNumber() ? out = static_cast<float>(val.GetDouble()), true : false;
74 }};
75
76 template<unsigned int N> struct ReadHelper<float[N]> { static bool Read(Value& val, float (&out)[N]) {
77 if (!val.IsArray() || val.Size() != N) return false;
78 for (unsigned int i = 0; i < N; ++i) {
79 if (val[i].IsNumber())
80 out[i] = static_cast<float>(val[i].GetDouble());
81 }
82 return true;
83 }};
84
85 template<> struct ReadHelper<const char*> { static bool Read(Value& val, const char*& out) {
86 return val.IsString() ? (out = val.GetString(), true) : false;
87 }};
88
89 template<> struct ReadHelper<std::string> { static bool Read(Value& val, std::string& out) {
90 return val.IsString() ? (out = std::string(val.GetString(), val.GetStringLength()), true) : false;
91 }};
92
93 template<class T> struct ReadHelper< Nullable<T> > { static bool Read(Value& val, Nullable<T>& out) {
94 return out.isPresent = ReadHelper<T>::Read(val, out.value);
95 }};
96
97 template<class T>
98 inline static bool ReadValue(Value& val, T& out)
99 {
100 return ReadHelper<T>::Read(val, out);
101 }
102
103 template<class T>
104 inline static bool ReadMember(Value& obj, const char* id, T& out)
105 {
106 Value::MemberIterator it = obj.FindMember(id);
107 if (it != obj.MemberEnd()) {
108 return ReadHelper<T>::Read(it->value, out);
109 }
110 return false;
111 }
112
113 template<class T>
114 inline static T MemberOrDefault(Value& obj, const char* id, T defaultValue)
115 {
116 T out;
117 return ReadMember(obj, id, out) ? out : defaultValue;
118 }
119
120 inline Value* FindMember(Value& val, const char* id)
121 {
122 Value::MemberIterator it = val.FindMember(id);
123 return (it != val.MemberEnd()) ? &it->value : 0;
124 }
125
126 inline Value* FindString(Value& val, const char* id)
127 {
128 Value::MemberIterator it = val.FindMember(id);
129 return (it != val.MemberEnd() && it->value.IsString()) ? &it->value : 0;
130 }
131
132 inline Value* FindNumber(Value& val, const char* id)
133 {
134 Value::MemberIterator it = val.FindMember(id);
135 return (it != val.MemberEnd() && it->value.IsNumber()) ? &it->value : 0;
136 }
137
138 inline Value* FindArray(Value& val, const char* id)
139 {
140 Value::MemberIterator it = val.FindMember(id);
141 return (it != val.MemberEnd() && it->value.IsArray()) ? &it->value : 0;
142 }
143
144 inline Value* FindObject(Value& val, const char* id)
145 {
146 Value::MemberIterator it = val.FindMember(id);
147 return (it != val.MemberEnd() && it->value.IsObject()) ? &it->value : 0;
148 }
149}
150
151//
152// LazyDict methods
153//
154
155template<class T>
156inline LazyDict<T>::LazyDict(Asset& asset, const char* dictId, const char* extId)
157 : mDictId(dictId), mExtId(extId), mDict(0), mAsset(asset)
158{
159 asset.mDicts.push_back(this); // register to the list of dictionaries
160}
161
162template<class T>
163inline LazyDict<T>::~LazyDict()
164{
165 for (size_t i = 0; i < mObjs.size(); ++i) {
166 delete mObjs[i];
167 }
168}
169
170
171template<class T>
172inline void LazyDict<T>::AttachToDocument(Document& doc)
173{
174 Value* container = 0;
175
176 if (mExtId) {
177 if (Value* exts = FindObject(doc, "extensions")) {
178 container = FindObject(*exts, mExtId);
179 }
180 }
181 else {
182 container = &doc;
183 }
184
185 if (container) {
186 mDict = FindObject(*container, mDictId);
187 }
188}
189
190template<class T>
191inline void LazyDict<T>::DetachFromDocument()
192{
193 mDict = 0;
194}
195
196template<class T>
197Ref<T> LazyDict<T>::Get(unsigned int i)
198{
199 return Ref<T>(mObjs, i);
200}
201
202template<class T>
203Ref<T> LazyDict<T>::Get(const char* id)
204{
205 id = T::TranslateId(mAsset, id);
206
207 typename Dict::iterator it = mObjsById.find(id);
208 if (it != mObjsById.end()) { // already created?
209 return Ref<T>(mObjs, it->second);
210 }
211
212 // read it from the JSON object
213 if (!mDict) {
214 throw DeadlyImportError("GLTF: Missing section \"" + std::string(mDictId) + "\"");
215 }
216
217 Value::MemberIterator obj = mDict->FindMember(id);
218 if (obj == mDict->MemberEnd()) {
219 throw DeadlyImportError("GLTF: Missing object with id \"" + std::string(id) + "\" in \"" + mDictId + "\"");
220 }
221 if (!obj->value.IsObject()) {
222 throw DeadlyImportError("GLTF: Object with id \"" + std::string(id) + "\" is not a JSON object");
223 }
224
225 // create an instance of the given type
226 T* inst = new T();
227 inst->id = id;
228 ReadMember(obj->value, "name", inst->name);
229 inst->Read(obj->value, mAsset);
230 return Add(inst);
231}
232
233template<class T>
234Ref<T> LazyDict<T>::Add(T* obj)
235{
236 unsigned int idx = unsigned(mObjs.size());
237 mObjs.push_back(obj);
238 mObjsById[obj->id] = idx;
239 mAsset.mUsedIds[obj->id] = true;
240 return Ref<T>(mObjs, idx);
241}
242
243template<class T>
244Ref<T> LazyDict<T>::Create(const char* id)
245{
246 Asset::IdMap::iterator it = mAsset.mUsedIds.find(id);
247 if (it != mAsset.mUsedIds.end()) {
248 throw DeadlyImportError("GLTF: two objects with the same ID exist");
249 }
250 T* inst = new T();
251 inst->id = id;
252 return Add(inst);
253}
254
255
256//
257// glTF dictionary objects methods
258//
259
260
261inline Buffer::Buffer()
262 : byteLength(0), type(Type_arraybuffer), EncodedRegion_Current(nullptr), mIsSpecial(false)
263{ }
264
265inline Buffer::~Buffer()
266{
267 for(SEncodedRegion* reg : EncodedRegion_List) delete reg;
268}
269
270inline const char* Buffer::TranslateId(Asset& r, const char* id)
271{
272 // Compatibility with old spec
273 if (r.extensionsUsed.KHR_binary_glTF && strcmp(id, "KHR_binary_glTF") == 0) {
274 return "binary_glTF";
275 }
276
277 return id;
278}
279
280inline void Buffer::Read(Value& obj, Asset& r)
281{
282 size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0);
283 byteLength = statedLength;
284
285 Value* it = FindString(obj, "uri");
286 if (!it) {
287 if (statedLength > 0) {
288 throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute");
289 }
290 return;
291 }
292
293 const char* uri = it->GetString();
294
295 Util::DataURI dataURI;
296 if (ParseDataURI(uri, it->GetStringLength(), dataURI)) {
297 if (dataURI.base64) {
298 uint8_t* data = 0;
299 this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data);
300 this->mData.reset(data);
301
302 if (statedLength > 0 && this->byteLength != statedLength) {
303 throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
304 " bytes, but found " + to_string(dataURI.dataLength));
305 }
306 }
307 else { // assume raw data
308 if (statedLength != dataURI.dataLength) {
309 throw DeadlyImportError("GLTF: buffer \"" + id + "\", expected " + to_string(statedLength) +
310 " bytes, but found " + to_string(dataURI.dataLength));
311 }
312
313 this->mData.reset(new uint8_t[dataURI.dataLength]);
314 memcpy( this->mData.get(), dataURI.data, dataURI.dataLength );
315 }
316 }
317 else { // Local file
318 if (byteLength > 0) {
319 std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : "";
320
321 IOStream* file = r.OpenFile(dir + uri, "rb");
322 if (file) {
323 bool ok = LoadFromStream(*file, byteLength);
324 delete file;
325
326 if (!ok)
327 throw DeadlyImportError("GLTF: error while reading referenced file \"" + std::string(uri) + "\"" );
328 }
329 else {
330 throw DeadlyImportError("GLTF: could not open referenced file \"" + std::string(uri) + "\"");
331 }
332 }
333 }
334}
335
336inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseOffset)
337{
338 byteLength = length ? length : stream.FileSize();
339
340 if (baseOffset) {
341 stream.Seek(baseOffset, aiOrigin_SET);
342 }
343
344 mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>());
345
346 if (stream.Read(mData.get(), byteLength, 1) != 1) {
347 return false;
348 }
349 return true;
350}
351
352inline 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)
353{
354 // Check pointer to data
355 if(pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided.");
356
357 // Check offset
358 if(pOffset > byteLength)
359 {
360 const uint8_t val_size = 32;
361
362 char val[val_size];
363
364 ai_snprintf(val, val_size, "%llu", (long long)pOffset);
365 throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
366 }
367
368 // Check length
369 if((pOffset + pEncodedData_Length) > byteLength)
370 {
371 const uint8_t val_size = 64;
372
373 char val[val_size];
374
375 ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
376 throw DeadlyImportError(std::string("GLTF: encoded region with offset/length (") + val + ") is out of range.");
377 }
378
379 // Add new region
380 EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID));
381 // And set new value for "byteLength"
382 byteLength += (pDecodedData_Length - pEncodedData_Length);
383}
384
385inline void Buffer::EncodedRegion_SetCurrent(const std::string& pID)
386{
387 if((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return;
388
389 for(SEncodedRegion* reg : EncodedRegion_List)
390 {
391 if(reg->ID == pID)
392 {
393 EncodedRegion_Current = reg;
394
395 return;
396 }
397
398 }
399
400 throw DeadlyImportError("GLTF: EncodedRegion with ID: \"" + pID + "\" not found.");
401}
402
403inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
404{
405const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
406
407uint8_t* new_data;
408
409 if((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) return false;
410
411 new_data = new uint8_t[new_data_size];
412 // Copy data which place before replacing part.
413 memcpy(new_data, mData.get(), pBufferData_Offset);
414 // Copy new data.
415 memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count);
416 // Copy data which place after replacing part.
417 memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset);
418 // Apply new data
419 mData.reset(new_data);
420 byteLength = new_data_size;
421
422 return true;
423}
424
425inline size_t Buffer::AppendData(uint8_t* data, size_t length)
426{
427 size_t offset = this->byteLength;
428 Grow(length);
429 memcpy(mData.get() + offset, data, length);
430 return offset;
431}
432
433inline void Buffer::Grow(size_t amount)
434{
435 if (amount <= 0) return;
436 uint8_t* b = new uint8_t[byteLength + amount];
437 if (mData) memcpy(b, mData.get(), byteLength);
438 mData.reset(b);
439 byteLength += amount;
440}
441
442//
443// struct BufferView
444//
445
446inline void BufferView::Read(Value& obj, Asset& r)
447{
448 const char* bufferId = MemberOrDefault<const char*>(obj, "buffer", 0);
449 if (bufferId) {
450 buffer = r.buffers.Get(bufferId);
451 }
452
453 byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
454 byteLength = MemberOrDefault(obj, "byteLength", 0u);
455}
456
457//
458// struct Accessor
459//
460
461inline void Accessor::Read(Value& obj, Asset& r)
462{
463 const char* bufferViewId = MemberOrDefault<const char*>(obj, "bufferView", 0);
464 if (bufferViewId) {
465 bufferView = r.bufferViews.Get(bufferViewId);
466 }
467
468 byteOffset = MemberOrDefault(obj, "byteOffset", 0u);
469 byteStride = MemberOrDefault(obj, "byteStride", 0u);
470 componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE);
471 count = MemberOrDefault(obj, "count", 0u);
472
473 const char* typestr;
474 type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR;
475}
476
477inline unsigned int Accessor::GetNumComponents()
478{
479 return AttribType::GetNumComponents(type);
480}
481
482inline unsigned int Accessor::GetBytesPerComponent()
483{
484 return int(ComponentTypeSize(componentType));
485}
486
487inline unsigned int Accessor::GetElementSize()
488{
489 return GetNumComponents() * GetBytesPerComponent();
490}
491
492inline uint8_t* Accessor::GetPointer()
493{
494 if (!bufferView || !bufferView->buffer) return 0;
495 uint8_t* basePtr = bufferView->buffer->GetPointer();
496 if (!basePtr) return 0;
497
498 size_t offset = byteOffset + bufferView->byteOffset;
499
500 // Check if region is encoded.
501 if(bufferView->buffer->EncodedRegion_Current != nullptr)
502 {
503 const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset;
504 const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length;
505
506 if((offset >= begin) && (offset < end))
507 return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin];
508 }
509
510 return basePtr + offset;
511}
512
513namespace {
514 inline void CopyData(size_t count,
515 const uint8_t* src, size_t src_stride,
516 uint8_t* dst, size_t dst_stride)
517 {
518 if (src_stride == dst_stride) {
519 memcpy(dst, src, count * src_stride);
520 }
521 else {
522 size_t sz = std::min(src_stride, dst_stride);
523 for (size_t i = 0; i < count; ++i) {
524 memcpy(dst, src, sz);
525 if (sz < dst_stride) {
526 memset(dst + sz, 0, dst_stride - sz);
527 }
528 src += src_stride;
529 dst += dst_stride;
530 }
531 }
532 }
533}
534
535template<class T>
536bool Accessor::ExtractData(T*& outData)
537{
538 uint8_t* data = GetPointer();
539 if (!data) return false;
540
541 const size_t elemSize = GetElementSize();
542 const size_t totalSize = elemSize * count;
543
544 const size_t stride = byteStride ? byteStride : elemSize;
545
546 const size_t targetElemSize = sizeof(T);
547 ai_assert(elemSize <= targetElemSize);
548
549 ai_assert(count*stride <= bufferView->byteLength);
550
551 outData = new T[count];
552 if (stride == elemSize && targetElemSize == elemSize) {
553 memcpy(outData, data, totalSize);
554 }
555 else {
556 for (size_t i = 0; i < count; ++i) {
557 memcpy(outData + i, data + i*stride, elemSize);
558 }
559 }
560
561 return true;
562}
563
564inline void Accessor::WriteData(size_t count, const void* src_buffer, size_t src_stride)
565{
566 uint8_t* buffer_ptr = bufferView->buffer->GetPointer();
567 size_t offset = byteOffset + bufferView->byteOffset;
568
569 size_t dst_stride = GetNumComponents() * GetBytesPerComponent();
570
571 const uint8_t* src = reinterpret_cast<const uint8_t*>(src_buffer);
572 uint8_t* dst = reinterpret_cast< uint8_t*>(buffer_ptr + offset);
573
574 ai_assert(dst + count*dst_stride <= buffer_ptr + bufferView->buffer->byteLength);
575 CopyData(count, src, src_stride, dst, dst_stride);
576}
577
578
579
580inline Accessor::Indexer::Indexer(Accessor& acc)
581 : accessor(acc)
582 , data(acc.GetPointer())
583 , elemSize(acc.GetElementSize())
584 , stride(acc.byteStride ? acc.byteStride : elemSize)
585{
586
587}
588
589//! Accesses the i-th value as defined by the accessor
590template<class T>
591T Accessor::Indexer::GetValue(int i)
592{
593 ai_assert(data);
594 ai_assert(i*stride < accessor.bufferView->byteLength);
595 T value = T();
596 memcpy(&value, data + i*stride, elemSize);
597 //value >>= 8 * (sizeof(T) - elemSize);
598 return value;
599}
600
601inline Image::Image()
602 : width(0)
603 , height(0)
604 , mData(0)
605 , mDataLength(0)
606{
607
608}
609
610inline void Image::Read(Value& obj, Asset& r)
611{
612 // Check for extensions first (to detect binary embedded data)
613 if (Value* extensions = FindObject(obj, "extensions")) {
614 if (r.extensionsUsed.KHR_binary_glTF) {
615 if (Value* ext = FindObject(*extensions, "KHR_binary_glTF")) {
616
617 width = MemberOrDefault(*ext, "width", 0);
618 height = MemberOrDefault(*ext, "height", 0);
619
620 ReadMember(*ext, "mimeType", mimeType);
621
622 const char* bufferViewId;
623 if (ReadMember(*ext, "bufferView", bufferViewId)) {
624 Ref<BufferView> bv = r.bufferViews.Get(bufferViewId);
625 if (bv) {
626 mDataLength = bv->byteLength;
627 mData = new uint8_t[mDataLength];
628 memcpy(mData, bv->buffer->GetPointer() + bv->byteOffset, mDataLength);
629 }
630 }
631 }
632 }
633 }
634
635 if (!mDataLength) {
636 if (Value* uri = FindString(obj, "uri")) {
637 const char* uristr = uri->GetString();
638
639 Util::DataURI dataURI;
640 if (ParseDataURI(uristr, uri->GetStringLength(), dataURI)) {
641 mimeType = dataURI.mediaType;
642 if (dataURI.base64) {
643 mDataLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, mData);
644 }
645 }
646 else {
647 this->uri = uristr;
648 }
649 }
650 }
651}
652
653inline uint8_t* Image::StealData()
654{
655 uint8_t* data = mData;
656 mDataLength = 0;
657 mData = 0;
658 return data;
659}
660
661inline void Image::SetData(uint8_t* data, size_t length, Asset& r)
662{
663 Ref<Buffer> b = r.GetBodyBuffer();
664 if (b) { // binary file: append to body
665 std::string bvId = r.FindUniqueID(this->id, "imgdata");
666 bufferView = r.bufferViews.Create(bvId);
667
668 bufferView->buffer = b;
669 bufferView->byteLength = length;
670 bufferView->byteOffset = b->AppendData(data, length);
671 }
672 else { // text file: will be stored as a data uri
673 this->mData = data;
674 this->mDataLength = length;
675 }
676}
677
678inline void Sampler::Read(Value& obj, Asset& /*r*/)
679{
680 SetDefaults();
681
682 ReadMember(obj, "magFilter", magFilter);
683 ReadMember(obj, "minFilter", minFilter);
684 ReadMember(obj, "wrapS", wrapS);
685 ReadMember(obj, "wrapT", wrapT);
686}
687
688inline void Sampler::SetDefaults()
689{
690 magFilter = SamplerMagFilter_Linear;
691 minFilter = SamplerMinFilter_Linear;
692 wrapS = SamplerWrap_Repeat;
693 wrapT = SamplerWrap_Repeat;
694}
695
696inline void Texture::Read(Value& obj, Asset& r)
697{
698 const char* sourcestr;
699 if (ReadMember(obj, "source", sourcestr)) {
700 source = r.images.Get(sourcestr);
701 }
702
703 const char* samplerstr;
704 if (ReadMember(obj, "sampler", samplerstr)) {
705 sampler = r.samplers.Get(samplerstr);
706 }
707}
708
709namespace {
710 inline void ReadMaterialProperty(Asset& r, Value& vals, const char* propName, TexProperty& out)
711 {
712 if (Value* prop = FindMember(vals, propName)) {
713 if (prop->IsString()) {
714 out.texture = r.textures.Get(prop->GetString());
715 }
716 else {
717 ReadValue(*prop, out.color);
718 }
719 }
720 }
721}
722
723inline void Material::Read(Value& material, Asset& r)
724{
725 SetDefaults();
726
727 if (Value* values = FindObject(material, "values")) {
728 ReadMaterialProperty(r, *values, "ambient", this->ambient);
729 ReadMaterialProperty(r, *values, "diffuse", this->diffuse);
730 ReadMaterialProperty(r, *values, "specular", this->specular);
731
732 ReadMember(*values, "transparency", transparency);
733 ReadMember(*values, "shininess", shininess);
734 }
735
736 if (Value* extensions = FindObject(material, "extensions")) {
737 if (r.extensionsUsed.KHR_materials_common) {
738 if (Value* ext = FindObject(*extensions, "KHR_materials_common")) {
739 if (Value* tnq = FindString(*ext, "technique")) {
740 const char* t = tnq->GetString();
741 if (strcmp(t, "BLINN") == 0) technique = Technique_BLINN;
742 else if (strcmp(t, "PHONG") == 0) technique = Technique_PHONG;
743 else if (strcmp(t, "LAMBERT") == 0) technique = Technique_LAMBERT;
744 else if (strcmp(t, "CONSTANT") == 0) technique = Technique_CONSTANT;
745 }
746
747 if (Value* values = FindObject(*ext, "values")) {
748 ReadMaterialProperty(r, *values, "ambient", this->ambient);
749 ReadMaterialProperty(r, *values, "diffuse", this->diffuse);
750 ReadMaterialProperty(r, *values, "specular", this->specular);
751
752 ReadMember(*values, "doubleSided", doubleSided);
753 ReadMember(*values, "transparent", transparent);
754 ReadMember(*values, "transparency", transparency);
755 ReadMember(*values, "shininess", shininess);
756 }
757 }
758 }
759 }
760}
761
762namespace {
763 void SetVector(vec4& v, float x, float y, float z, float w)
764 { v[0] = x; v[1] = y; v[2] = z; v[3] = w; }
765}
766
767inline void Material::SetDefaults()
768{
769 SetVector(ambient.color, 0, 0, 0, 1);
770 SetVector(diffuse.color, 0, 0, 0, 1);
771 SetVector(specular.color, 0, 0, 0, 1);
772 SetVector(emission.color, 0, 0, 0, 1);
773
774 doubleSided = false;
775 transparent = false;
776 transparency = 1.0;
777 shininess = 0.0;
778
779 technique = Technique_undefined;
780}
781
782namespace {
783
784 template<int N>
785 inline int Compare(const char* attr, const char (&str)[N]) {
786 return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0;
787 }
788
789 inline bool GetAttribVector(Mesh::Primitive& p, const char* attr, Mesh::AccessorList*& v, int& pos)
790 {
791 if ((pos = Compare(attr, "POSITION"))) {
792 v = &(p.attributes.position);
793 }
794 else if ((pos = Compare(attr, "NORMAL"))) {
795 v = &(p.attributes.normal);
796 }
797 else if ((pos = Compare(attr, "TEXCOORD"))) {
798 v = &(p.attributes.texcoord);
799 }
800 else if ((pos = Compare(attr, "COLOR"))) {
801 v = &(p.attributes.color);
802 }
803 else if ((pos = Compare(attr, "JOINT"))) {
804 v = &(p.attributes.joint);
805 }
806 else if ((pos = Compare(attr, "JOINTMATRIX"))) {
807 v = &(p.attributes.jointmatrix);
808 }
809 else if ((pos = Compare(attr, "WEIGHT"))) {
810 v = &(p.attributes.weight);
811 }
812 else return false;
813 return true;
814 }
815}
816
817inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
818{
819 /****************** Mesh primitives ******************/
820 if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
821 this->primitives.resize(primitives->Size());
822 for (unsigned int i = 0; i < primitives->Size(); ++i) {
823 Value& primitive = (*primitives)[i];
824
825 Primitive& prim = this->primitives[i];
826 prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
827
828 if (Value* attrs = FindObject(primitive, "attributes")) {
829 for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
830 if (!it->value.IsString()) continue;
831 const char* attr = it->name.GetString();
832 // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX,
833 // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
834
835 int undPos = 0;
836 Mesh::AccessorList* vec = 0;
837 if (GetAttribVector(prim, attr, vec, undPos)) {
838 size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
839 if ((*vec).size() <= idx) (*vec).resize(idx + 1);
840 (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetString());
841 }
842 }
843 }
844
845 if (Value* indices = FindString(primitive, "indices")) {
846 prim.indices = pAsset_Root.accessors.Get(indices->GetString());
847 }
848
849 if (Value* material = FindString(primitive, "material")) {
850 prim.material = pAsset_Root.materials.Get(material->GetString());
851 }
852 }
853 }
854
855 /****************** Mesh extensions ******************/
856 Value* json_extensions = FindObject(pJSON_Object, "extensions");
857
858 if(json_extensions == nullptr) goto mr_skip_extensions;
859
860 for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++)
861 {
862#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
863 if(it_memb->name.GetString() == std::string("Open3DGC-compression"))
864 {
865 // Search for compressed data.
866 // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and
867 // new data will replace old encoded part by request. In fact \"compressedData\" is kind of "accessor" structure.
868 Value* comp_data = FindObject(it_memb->value, "compressedData");
869
870 if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\".");
871
872 DefaultLogger::get()->info("GLTF: Decompressing Open3DGC data.");
873
874 /************** Read data from JSON-document **************/
875 #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \
876 if(!ReadMember(*comp_data, pFieldName, pOut)) \
877 { \
878 throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \
879 }
880
881 const char* mode_str;
882 const char* type_str;
883 ComponentType component_type;
884 SCompression_Open3DGC* ext_o3dgc = new SCompression_Open3DGC;
885
886 MESH_READ_COMPRESSEDDATA_MEMBER("buffer", ext_o3dgc->Buffer);
887 MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", ext_o3dgc->Offset);
888 MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type);
889 MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str);
890 MESH_READ_COMPRESSEDDATA_MEMBER("count", ext_o3dgc->Count);
891 MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str);
892 MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", ext_o3dgc->IndicesCount);
893 MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", ext_o3dgc->VerticesCount);
894
895 #undef MESH_READ_COMPRESSEDDATA_MEMBER
896
897 // Check some values
898 if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data.");
899 if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data.");
900
901 // Set read/write data mode.
902 if(strcmp(mode_str, "binary") == 0)
903 ext_o3dgc->Binary = true;
904 else if(strcmp(mode_str, "ascii") == 0)
905 ext_o3dgc->Binary = false;
906 else
907 throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
908
909 /************************ Decoding ************************/
910 Decode_O3DGC(*ext_o3dgc, pAsset_Root);
911 Extension.push_back(ext_o3dgc);// store info in mesh extensions list.
912 }// if(it_memb->name.GetString() == "Open3DGC-compression")
913 else
914#endif
915 {
916 throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\".");
917 }
918 }// for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
919
920mr_skip_extensions:
921
922 return;// After label some operators must be present.
923}
924
925#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
926inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root)
927{
928typedef unsigned short IndicesType;///< \sa glTFExporter::ExportMeshes.
929
930o3dgc::SC3DMCDecoder<IndicesType> decoder;
931o3dgc::IndexedFaceSet<IndicesType> ifs;
932o3dgc::BinaryStream bstream;
933uint8_t* decoded_data;
934size_t decoded_data_size = 0;
935Ref<Buffer> buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer);
936
937 // Read data from buffer and place it in BinaryStream for decoder.
938 // Just "Count" because always is used type equivalent to uint8_t.
939 bstream.LoadFromBuffer(&buf->GetPointer()[pCompression_Open3DGC.Offset], static_cast<unsigned long>(pCompression_Open3DGC.Count));
940
941 // After decoding header we can get size of primitives.
942 if(decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header.");
943
944 /****************** Get sizes of arrays and check sizes ******************/
945 // Note. See "Limitations for meshes when using Open3DGC-compression".
946
947 // Indices
948 size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note.
949
950 if(primitives[0].indices->count != size_coordindex)
951 throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) +
952 ") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ").");
953
954 size_coordindex *= sizeof(IndicesType);
955 // Coordinates
956 size_t size_coord = ifs.GetNCoord();// See float attributes note.
957
958 if(primitives[0].attributes.position[0]->count != size_coord)
959 throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) +
960 ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ").");
961
962 size_coord *= 3 * sizeof(float);
963 // Normals
964 size_t size_normal = ifs.GetNNormal();// See float attributes note.
965
966 if(primitives[0].attributes.normal[0]->count != size_normal)
967 throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) +
968 ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ").");
969
970 size_normal *= 3 * sizeof(float);
971 // Additional attributes.
972 std::vector<size_t> size_floatattr;
973 std::vector<size_t> size_intattr;
974
975 size_floatattr.resize(ifs.GetNumFloatAttributes());
976 size_intattr.resize(ifs.GetNumIntAttributes());
977
978 decoded_data_size = size_coordindex + size_coord + size_normal;
979 for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
980 {
981 // size = number_of_elements * components_per_element * size_of_component.
982 // Note. But as you can see above, at first we are use this variable in meaning "count". After checking count of objects...
983 size_t tval = ifs.GetNFloatAttribute(static_cast<unsigned long>(idx));
984
985 switch(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))
986 {
987 case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
988 // Check situation when encoded data contain texture coordinates but primitive not.
989 if(idx_texcoord < primitives[0].attributes.texcoord.size())
990 {
991 if(primitives[0].attributes.texcoord[idx]->count != tval)
992 throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) +
993 ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ").");
994
995 idx_texcoord++;
996 }
997 else
998 {
999 ifs.SetNFloatAttribute(static_cast<unsigned long>(idx), 0ul);// Disable decoding this attribute.
1000 }
1001
1002 break;
1003 default:
1004 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
1005 }
1006
1007 tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real);// After checking count of objects we can get size of array.
1008 size_floatattr[idx] = tval;
1009 decoded_data_size += tval;
1010 }
1011
1012 for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++)
1013 {
1014 // size = number_of_elements * components_per_element * size_of_component. See float attributes note.
1015 size_t tval = ifs.GetNIntAttribute(static_cast<unsigned long>(idx));
1016 switch( ifs.GetIntAttributeType(static_cast<unsigned long>(idx) ) )
1017 {
1018 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN:
1019 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX:
1020 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID:
1021 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID:
1022 break;
1023
1024 default:
1025 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
1026 }
1027
1028 tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long);// See float attributes note.
1029 size_intattr[idx] = tval;
1030 decoded_data_size += tval;
1031 }
1032
1033 // Create array for decoded data.
1034 decoded_data = new uint8_t[decoded_data_size];
1035
1036 /****************** Set right array regions for decoder ******************/
1037
1038 auto get_buf_offset = [](Ref<Accessor>& pAccessor) -> size_t { return pAccessor->byteOffset + pAccessor->bufferView->byteOffset; };
1039
1040 // Indices
1041 ifs.SetCoordIndex((IndicesType* const)(decoded_data + get_buf_offset(primitives[0].indices)));
1042 // Coordinates
1043 ifs.SetCoord((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.position[0])));
1044 // Normals
1045 if(size_normal)
1046 {
1047 ifs.SetNormal((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.normal[0])));
1048 }
1049
1050 for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
1051 {
1052 switch(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))
1053 {
1054 case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
1055 if(idx_texcoord < primitives[0].attributes.texcoord.size())
1056 {
1057 // See above about absent attributes.
1058 ifs.SetFloatAttribute(static_cast<unsigned long>(idx), (o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx])));
1059 idx_texcoord++;
1060 }
1061
1062 break;
1063 default:
1064 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))));
1065 }
1066 }
1067
1068 for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) {
1069 switch(ifs.GetIntAttributeType(static_cast<unsigned int>(idx))) {
1070 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN:
1071 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX:
1072 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID:
1073 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID:
1074 break;
1075
1076 // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
1077 default:
1078 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx))));
1079 }
1080 }
1081
1082 //
1083 // Decode data
1084 //
1085 if ( decoder.DecodePayload( ifs, bstream ) != o3dgc::O3DGC_OK ) {
1086 throw DeadlyImportError( "GLTF: can not decode Open3DGC data." );
1087 }
1088
1089 // Set encoded region for "buffer".
1090 buf->EncodedRegion_Mark(pCompression_Open3DGC.Offset, pCompression_Open3DGC.Count, decoded_data, decoded_data_size, id);
1091 // No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data".
1092 // "delete [] output_data;"
1093}
1094#endif
1095
1096inline void Camera::Read(Value& obj, Asset& /*r*/)
1097{
1098 type = MemberOrDefault(obj, "type", Camera::Perspective);
1099
1100 const char* subobjId = (type == Camera::Orthographic) ? "ortographic" : "perspective";
1101
1102 Value* it = FindObject(obj, subobjId);
1103 if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters");
1104
1105 if (type == Camera::Perspective) {
1106 perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f);
1107 perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f/2.f);
1108 perspective.zfar = MemberOrDefault(*it, "zfar", 100.f);
1109 perspective.znear = MemberOrDefault(*it, "znear", 0.01f);
1110 }
1111 else {
1112 ortographic.xmag = MemberOrDefault(obj, "xmag", 1.f);
1113 ortographic.ymag = MemberOrDefault(obj, "ymag", 1.f);
1114 ortographic.zfar = MemberOrDefault(obj, "zfar", 100.f);
1115 ortographic.znear = MemberOrDefault(obj, "znear", 0.01f);
1116 }
1117}
1118
1119inline void Light::Read(Value& obj, Asset& /*r*/)
1120{
1121 SetDefaults();
1122
1123 if (Value* type = FindString(obj, "type")) {
1124 const char* t = type->GetString();
1125 if (strcmp(t, "ambient") == 0) this->type = Type_ambient;
1126 else if (strcmp(t, "directional") == 0) this->type = Type_directional;
1127 else if (strcmp(t, "point") == 0) this->type = Type_point;
1128 else if (strcmp(t, "spot") == 0) this->type = Type_spot;
1129
1130 if (this->type != Type_undefined) {
1131 if (Value* vals = FindString(obj, t)) {
1132 ReadMember(*vals, "color", color);
1133
1134 ReadMember(*vals, "constantAttenuation", constantAttenuation);
1135 ReadMember(*vals, "linearAttenuation", linearAttenuation);
1136 ReadMember(*vals, "quadraticAttenuation", quadraticAttenuation);
1137 ReadMember(*vals, "distance", distance);
1138
1139 ReadMember(*vals, "falloffAngle", falloffAngle);
1140 ReadMember(*vals, "falloffExponent", falloffExponent);
1141 }
1142 }
1143 }
1144}
1145
1146inline void Light::SetDefaults()
1147{
1148 #ifndef M_PI
1149 const float M_PI = 3.14159265358979323846f;
1150 #endif
1151
1152 type = Type_undefined;
1153
1154 SetVector(color, 0.f, 0.f, 0.f, 1.f);
1155
1156 constantAttenuation = 0.f;
1157 linearAttenuation = 1.f;
1158 quadraticAttenuation = 1.f;
1159 distance = 0.f;
1160
1161 falloffAngle = static_cast<float>(M_PI / 2.f);
1162 falloffExponent = 0.f;
1163}
1164
1165inline void Node::Read(Value& obj, Asset& r)
1166{
1167 if (Value* children = FindArray(obj, "children")) {
1168 this->children.reserve(children->Size());
1169 for (unsigned int i = 0; i < children->Size(); ++i) {
1170 Value& child = (*children)[i];
1171 if (child.IsString()) {
1172 // get/create the child node
1173 Ref<Node> chn = r.nodes.Get(child.GetString());
1174 if (chn) this->children.push_back(chn);
1175 }
1176 }
1177 }
1178
1179
1180 if (Value* matrix = FindArray(obj, "matrix")) {
1181 ReadValue(*matrix, this->matrix);
1182 }
1183 else {
1184 ReadMember(obj, "translation", translation);
1185 ReadMember(obj, "scale", scale);
1186 ReadMember(obj, "rotation", rotation);
1187 }
1188
1189 if (Value* meshes = FindArray(obj, "meshes")) {
1190 unsigned numMeshes = (unsigned)meshes->Size();
1191
1192 std::vector<unsigned int> meshList;
1193
1194 this->meshes.reserve(numMeshes);
1195 for (unsigned i = 0; i < numMeshes; ++i) {
1196 if ((*meshes)[i].IsString()) {
1197 Ref<Mesh> mesh = r.meshes.Get((*meshes)[i].GetString());
1198 if (mesh) this->meshes.push_back(mesh);
1199 }
1200 }
1201 }
1202
1203 if (Value* camera = FindString(obj, "camera")) {
1204 this->camera = r.cameras.Get(camera->GetString());
1205 if (this->camera)
1206 this->camera->id = this->id;
1207 }
1208
1209 // TODO load "skeletons", "skin", "jointName"
1210
1211 if (Value* extensions = FindObject(obj, "extensions")) {
1212 if (r.extensionsUsed.KHR_materials_common) {
1213
1214 if (Value* ext = FindObject(*extensions, "KHR_materials_common")) {
1215 if (Value* light = FindString(*ext, "light")) {
1216 this->light = r.lights.Get(light->GetString());
1217 }
1218 }
1219
1220 }
1221 }
1222}
1223
1224inline void Scene::Read(Value& obj, Asset& r)
1225{
1226 if (Value* array = FindArray(obj, "nodes")) {
1227 for (unsigned int i = 0; i < array->Size(); ++i) {
1228 if (!(*array)[i].IsString()) continue;
1229 Ref<Node> node = r.nodes.Get((*array)[i].GetString());
1230 if (node)
1231 this->nodes.push_back(node);
1232 }
1233 }
1234}
1235
1236
1237inline void AssetMetadata::Read(Document& doc)
1238{
1239 // read the version, etc.
1240 if (Value* obj = FindObject(doc, "asset")) {
1241 ReadMember(*obj, "copyright", copyright);
1242 ReadMember(*obj, "generator", generator);
1243
1244 premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false);
1245
1246 if (Value* versionString = FindString(*obj, "version")) {
1247 version = versionString->GetString();
1248 } else if (Value* versionNumber = FindNumber (*obj, "version")) {
1249 char buf[4];
1250
1251 ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble());
1252
1253 version = buf;
1254 }
1255
1256 if (Value* profile = FindObject(*obj, "profile")) {
1257 ReadMember(*profile, "api", this->profile.api);
1258 ReadMember(*profile, "version", this->profile.version);
1259 }
1260 }
1261
1262 if (version.empty() || version[0] != '1') {
1263 throw DeadlyImportError("GLTF: Unsupported glTF version: " + version);
1264 }
1265}
1266
1267
1268
1269//
1270// Asset methods implementation
1271//
1272
1273inline void Asset::ReadBinaryHeader(IOStream& stream)
1274{
1275 GLB_Header header;
1276 if (stream.Read(&header, sizeof(header), 1) != 1) {
1277 throw DeadlyImportError("GLTF: Unable to read the file header");
1278 }
1279
1280 if (strncmp((char*)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) {
1281 throw DeadlyImportError("GLTF: Invalid binary glTF file");
1282 }
1283
1284 AI_SWAP4(header.version);
1285 asset.version = to_string(header.version);
1286 if (header.version != 1) {
1287 throw DeadlyImportError("GLTF: Unsupported binary glTF version");
1288 }
1289
1290 AI_SWAP4(header.sceneFormat);
1291 if (header.sceneFormat != SceneFormat_JSON) {
1292 throw DeadlyImportError("GLTF: Unsupported binary glTF scene format");
1293 }
1294
1295 AI_SWAP4(header.length);
1296 AI_SWAP4(header.sceneLength);
1297
1298 mSceneLength = static_cast<size_t>(header.sceneLength);
1299
1300 mBodyOffset = sizeof(header)+mSceneLength;
1301 mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4
1302
1303 mBodyLength = header.length - mBodyOffset;
1304}
1305
1306inline void Asset::Load(const std::string& pFile, bool isBinary)
1307{
1308 mCurrentAssetDir.clear();
1309 int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\')));
1310 if (pos != int(std::string::npos)) mCurrentAssetDir = pFile.substr(0, pos + 1);
1311
1312 shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true));
1313 if (!stream) {
1314 throw DeadlyImportError("GLTF: Could not open file for reading");
1315 }
1316
1317 // is binary? then read the header
1318 if (isBinary) {
1319 SetAsBinary(); // also creates the body buffer
1320 ReadBinaryHeader(*stream);
1321 }
1322 else {
1323 mSceneLength = stream->FileSize();
1324 mBodyLength = 0;
1325 }
1326
1327
1328 // read the scene data
1329
1330 std::vector<char> sceneData(mSceneLength + 1);
1331 sceneData[mSceneLength] = '\0';
1332
1333 if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) {
1334 throw DeadlyImportError("GLTF: Could not read the file contents");
1335 }
1336
1337
1338 // parse the JSON document
1339
1340 Document doc;
1341 doc.ParseInsitu(&sceneData[0]);
1342
1343 if (doc.HasParseError()) {
1344 char buffer[32];
1345 ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset()));
1346 throw DeadlyImportError(std::string("GLTF: JSON parse error, offset ") + buffer + ": "
1347 + GetParseError_En(doc.GetParseError()));
1348 }
1349
1350 if (!doc.IsObject()) {
1351 throw DeadlyImportError("GLTF: JSON document root must be a JSON object");
1352 }
1353
1354 // Fill the buffer instance for the current file embedded contents
1355 if (mBodyLength > 0) {
1356 if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) {
1357 throw DeadlyImportError("GLTF: Unable to read gltf file");
1358 }
1359 }
1360
1361
1362 // Load the metadata
1363 asset.Read(doc);
1364 ReadExtensionsUsed(doc);
1365
1366 // Prepare the dictionaries
1367 for (size_t i = 0; i < mDicts.size(); ++i) {
1368 mDicts[i]->AttachToDocument(doc);
1369 }
1370
1371
1372
1373 // Read the "scene" property, which specifies which scene to load
1374 // and recursively load everything referenced by it
1375 if (Value* scene = FindString(doc, "scene")) {
1376 this->scene = scenes.Get(scene->GetString());
1377 }
1378
1379 // Clean up
1380 for (size_t i = 0; i < mDicts.size(); ++i) {
1381 mDicts[i]->DetachFromDocument();
1382 }
1383}
1384
1385inline void Asset::SetAsBinary()
1386{
1387 if (!extensionsUsed.KHR_binary_glTF) {
1388 extensionsUsed.KHR_binary_glTF = true;
1389 mBodyBuffer = buffers.Create("binary_glTF");
1390 mBodyBuffer->MarkAsSpecial();
1391 }
1392}
1393
1394
1395inline void Asset::ReadExtensionsUsed(Document& doc)
1396{
1397 Value* extsUsed = FindArray(doc, "extensionsUsed");
1398 if (!extsUsed) return;
1399
1400 std::gltf_unordered_map<std::string, bool> exts;
1401
1402 for (unsigned int i = 0; i < extsUsed->Size(); ++i) {
1403 if ((*extsUsed)[i].IsString()) {
1404 exts[(*extsUsed)[i].GetString()] = true;
1405 }
1406 }
1407
1408 #define CHECK_EXT(EXT) \
1409 if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
1410
1411 CHECK_EXT(KHR_binary_glTF);
1412 CHECK_EXT(KHR_materials_common);
1413
1414 #undef CHECK_EXT
1415}
1416
1417inline IOStream* Asset::OpenFile(std::string path, const char* mode, bool /*absolute*/)
1418{
1419 #ifdef ASSIMP_API
1420 return mIOSystem->Open(path, mode);
1421 #else
1422 if (path.size() < 2) return 0;
1423 if (!absolute && path[1] != ':' && path[0] != '/') { // relative?
1424 path = mCurrentAssetDir + path;
1425 }
1426 FILE* f = fopen(path.c_str(), mode);
1427 return f ? new IOStream(f) : 0;
1428 #endif
1429}
1430
1431inline std::string Asset::FindUniqueID(const std::string& str, const char* suffix)
1432{
1433 std::string id = str;
1434
1435 if (!id.empty()) {
1436 if (mUsedIds.find(id) == mUsedIds.end())
1437 return id;
1438
1439 id += "_";
1440 }
1441
1442 id += suffix;
1443
1444 Asset::IdMap::iterator it = mUsedIds.find(id);
1445 if (it == mUsedIds.end())
1446 return id;
1447
1448 char buffer[256];
1449 int offset = ai_snprintf(buffer, sizeof(buffer), "%s_", id.c_str());
1450 for (int i = 0; it != mUsedIds.end(); ++i) {
1451 ai_snprintf(buffer + offset, sizeof(buffer) - offset, "%d", i);
1452 id = buffer;
1453 it = mUsedIds.find(id);
1454 }
1455
1456 return id;
1457}
1458
1459namespace Util {
1460
1461 inline
1462 bool ParseDataURI(const char* const_uri, size_t uriLen, DataURI& out) {
1463 if ( NULL == const_uri ) {
1464 return false;
1465 }
1466
1467 if (const_uri[0] != 0x10) { // we already parsed this uri?
1468 if (strncmp(const_uri, "data:", 5) != 0) // not a data uri?
1469 return false;
1470 }
1471
1472 // set defaults
1473 out.mediaType = "text/plain";
1474 out.charset = "US-ASCII";
1475 out.base64 = false;
1476
1477 char* uri = const_cast<char*>(const_uri);
1478 if (uri[0] != 0x10) {
1479 uri[0] = 0x10;
1480 uri[1] = uri[2] = uri[3] = uri[4] = 0;
1481
1482 size_t i = 5, j;
1483 if (uri[i] != ';' && uri[i] != ',') { // has media type?
1484 uri[1] = char(i);
1485 for (; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
1486 // nothing to do!
1487 }
1488 }
1489 while (uri[i] == ';' && i < uriLen) {
1490 uri[i++] = '\0';
1491 for (j = i; uri[i] != ';' && uri[i] != ',' && i < uriLen; ++i) {
1492 // nothing to do!
1493 }
1494
1495 if ( strncmp( uri + j, "charset=", 8 ) == 0 ) {
1496 uri[2] = char(j + 8);
1497 } else if ( strncmp( uri + j, "base64", 6 ) == 0 ) {
1498 uri[3] = char(j);
1499 }
1500 }
1501 if (i < uriLen) {
1502 uri[i++] = '\0';
1503 uri[4] = char(i);
1504 } else {
1505 uri[1] = uri[2] = uri[3] = 0;
1506 uri[4] = 5;
1507 }
1508 }
1509
1510 if ( uri[ 1 ] != 0 ) {
1511 out.mediaType = uri + uri[ 1 ];
1512 }
1513 if ( uri[ 2 ] != 0 ) {
1514 out.charset = uri + uri[ 2 ];
1515 }
1516 if ( uri[ 3 ] != 0 ) {
1517 out.base64 = true;
1518 }
1519 out.data = uri + uri[4];
1520 out.dataLength = (uri + uriLen) - out.data;
1521
1522 return true;
1523 }
1524
1525 template<bool B>
1526 struct DATA
1527 {
1528 static const uint8_t tableDecodeBase64[128];
1529 };
1530
1531 template<bool B>
1532 const uint8_t DATA<B>::tableDecodeBase64[128] = {
1533 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1534 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1535 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
1536 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0,
1537 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
1538 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
1539 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1540 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
1541 };
1542
1543 inline char EncodeCharBase64(uint8_t b)
1544 {
1545 return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
1546 }
1547
1548 inline uint8_t DecodeCharBase64(char c)
1549 {
1550 return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
1551 /*if (c >= 'A' && c <= 'Z') return c - 'A';
1552 if (c >= 'a' && c <= 'z') return c - 'a' + 26;
1553 if (c >= '0' && c <= '9') return c - '0' + 52;
1554 if (c == '+') return 62;
1555 if (c == '/') return 63;
1556 return 64; // '-' */
1557 }
1558
1559 inline size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out)
1560 {
1561 ai_assert(inLength % 4 == 0);
1562
1563 if (inLength < 4) {
1564 out = 0;
1565 return 0;
1566 }
1567
1568 int nEquals = int(in[inLength - 1] == '=') +
1569 int(in[inLength - 2] == '=');
1570
1571 size_t outLength = (inLength * 3) / 4 - nEquals;
1572 out = new uint8_t[outLength];
1573 memset(out, 0, outLength);
1574
1575 size_t i, j = 0;
1576
1577 for (i = 0; i + 4 < inLength; i += 4) {
1578 uint8_t b0 = DecodeCharBase64(in[i]);
1579 uint8_t b1 = DecodeCharBase64(in[i + 1]);
1580 uint8_t b2 = DecodeCharBase64(in[i + 2]);
1581 uint8_t b3 = DecodeCharBase64(in[i + 3]);
1582
1583 out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
1584 out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
1585 out[j++] = (uint8_t)((b2 << 6) | b3);
1586 }
1587
1588 {
1589 uint8_t b0 = DecodeCharBase64(in[i]);
1590 uint8_t b1 = DecodeCharBase64(in[i + 1]);
1591 uint8_t b2 = DecodeCharBase64(in[i + 2]);
1592 uint8_t b3 = DecodeCharBase64(in[i + 3]);
1593
1594 out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
1595 if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
1596 if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
1597 }
1598
1599 return outLength;
1600 }
1601
1602
1603
1604 inline void EncodeBase64(
1605 const uint8_t* in, size_t inLength,
1606 std::string& out)
1607 {
1608 size_t outLength = ((inLength + 2) / 3) * 4;
1609
1610 size_t j = out.size();
1611 out.resize(j + outLength);
1612
1613 for (size_t i = 0; i < inLength; i += 3) {
1614 uint8_t b = (in[i] & 0xFC) >> 2;
1615 out[j++] = EncodeCharBase64(b);
1616
1617 b = (in[i] & 0x03) << 4;
1618 if (i + 1 < inLength) {
1619 b |= (in[i + 1] & 0xF0) >> 4;
1620 out[j++] = EncodeCharBase64(b);
1621
1622 b = (in[i + 1] & 0x0F) << 2;
1623 if (i + 2 < inLength) {
1624 b |= (in[i + 2] & 0xC0) >> 6;
1625 out[j++] = EncodeCharBase64(b);
1626
1627 b = in[i + 2] & 0x3F;
1628 out[j++] = EncodeCharBase64(b);
1629 }
1630 else {
1631 out[j++] = EncodeCharBase64(b);
1632 out[j++] = '=';
1633 }
1634 }
1635 else {
1636 out[j++] = EncodeCharBase64(b);
1637 out[j++] = '=';
1638 out[j++] = '=';
1639 }
1640 }
1641 }
1642
1643}
1644
1645} // ns glTF
1646