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