1/* 2Open Asset Import Library (assimp) 3---------------------------------------------------------------------- 4 5Copyright (c) 2006-2021, 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 <assimp/MemoryIOWrapper.h> 43#include <assimp/StringUtils.h> 44#include <iomanip> 45 46// Header files, Assimp 47#include <assimp/DefaultLogger.hpp> 48 49#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 50// Header files, Open3DGC. 51#include <Open3DGC/o3dgcSC3DMCDecoder.h> 52#endif 53 54using namespace Assimp; 55using namespace glTFCommon; 56 57namespace glTF { 58 59#if _MSC_VER 60#pragma warning(push) 61#pragma warning(disable : 4706) 62#endif // _MSC_VER 63 64// 65// LazyDict methods 66// 67 68template <class T> 69inline LazyDict<T>::LazyDict(Asset &asset, const char *dictId, const char *extId) : 70 mDictId(dictId), mExtId(extId), mDict(0), mAsset(asset) { 71 asset.mDicts.push_back(this); // register to the list of dictionaries 72} 73 74template <class T> 75inline LazyDict<T>::~LazyDict() { 76 for (size_t i = 0; i < mObjs.size(); ++i) { 77 delete mObjs[i]; 78 } 79} 80 81template <class T> 82inline void LazyDict<T>::AttachToDocument(Document &doc) { 83 Value *container = 0; 84 85 if (mExtId) { 86 if (Value *exts = FindObject(doc, "extensions")) { 87 container = FindObject(*exts, mExtId); 88 } 89 } else { 90 container = &doc; 91 } 92 93 if (container) { 94 mDict = FindObject(*container, mDictId); 95 } 96} 97 98template <class T> 99inline void LazyDict<T>::DetachFromDocument() { 100 mDict = 0; 101} 102 103template <class T> 104Ref<T> LazyDict<T>::Get(unsigned int i) { 105 return Ref<T>(mObjs, i); 106} 107 108template <class T> 109Ref<T> LazyDict<T>::Get(const char *id) { 110 id = T::TranslateId(mAsset, id); 111 112 typename Dict::iterator it = mObjsById.find(id); 113 if (it != mObjsById.end()) { // already created? 114 return Ref<T>(mObjs, it->second); 115 } 116 117 // read it from the JSON object 118 if (!mDict) { 119 throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\""); 120 } 121 122 Value::MemberIterator obj = mDict->FindMember(id); 123 if (obj == mDict->MemberEnd()) { 124 throw DeadlyImportError("GLTF: Missing object with id \"", id, "\" in \"", mDictId, "\""); 125 } 126 if (!obj->value.IsObject()) { 127 throw DeadlyImportError("GLTF: Object with id \"", id, "\" is not a JSON object"); 128 } 129 130 // create an instance of the given type 131 T *inst = new T(); 132 inst->id = id; 133 ReadMember(obj->value, "name", inst->name); 134 inst->Read(obj->value, mAsset); 135 return Add(inst); 136} 137 138template <class T> 139Ref<T> LazyDict<T>::Add(T *obj) { 140 unsigned int idx = unsigned(mObjs.size()); 141 mObjs.push_back(obj); 142 mObjsById[obj->id] = idx; 143 mAsset.mUsedIds[obj->id] = true; 144 return Ref<T>(mObjs, idx); 145} 146 147template <class T> 148Ref<T> LazyDict<T>::Create(const char *id) { 149 Asset::IdMap::iterator it = mAsset.mUsedIds.find(id); 150 if (it != mAsset.mUsedIds.end()) { 151 throw DeadlyImportError("GLTF: two objects with the same ID exist"); 152 } 153 T *inst = new T(); 154 inst->id = id; 155 return Add(inst); 156} 157 158// 159// glTF dictionary objects methods 160// 161 162inline Buffer::Buffer() : 163 byteLength(0), type(Type_arraybuffer), EncodedRegion_Current(nullptr), mIsSpecial(false) {} 164 165inline Buffer::~Buffer() { 166 for (SEncodedRegion *reg : EncodedRegion_List) 167 delete reg; 168} 169 170inline const char *Buffer::TranslateId(Asset &r, const char *id) { 171 // Compatibility with old spec 172 if (r.extensionsUsed.KHR_binary_glTF && strcmp(id, "KHR_binary_glTF") == 0) { 173 return "binary_glTF"; 174 } 175 176 return id; 177} 178 179inline void Buffer::Read(Value &obj, Asset &r) { 180 size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0); 181 byteLength = statedLength; 182 183 Value *it = FindString(obj, "uri"); 184 if (!it) { 185 if (statedLength > 0) { 186 throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute"); 187 } 188 return; 189 } 190 191 const char *uri = it->GetString(); 192 193 glTFCommon::Util::DataURI dataURI; 194 if (ParseDataURI(uri, it->GetStringLength(), dataURI)) { 195 if (dataURI.base64) { 196 uint8_t *data = 0; 197 this->byteLength = Util::DecodeBase64(dataURI.data, dataURI.dataLength, data); 198 this->mData.reset(data, std::default_delete<uint8_t[]>()); 199 200 if (statedLength > 0 && this->byteLength != statedLength) { 201 throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), 202 " bytes, but found ", ai_to_string(dataURI.dataLength)); 203 } 204 } else { // assume raw data 205 if (statedLength != dataURI.dataLength) { 206 throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), 207 " bytes, but found ", ai_to_string(dataURI.dataLength)); 208 } 209 210 this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>()); 211 memcpy(this->mData.get(), dataURI.data, dataURI.dataLength); 212 } 213 } else { // Local file 214 if (byteLength > 0) { 215 std::string dir = !r.mCurrentAssetDir.empty() ? ( 216 r.mCurrentAssetDir.back() == '/' ? 217 r.mCurrentAssetDir : 218 r.mCurrentAssetDir + '/') : 219 ""; 220 221 IOStream *file = r.OpenFile(dir + uri, "rb"); 222 if (file) { 223 bool ok = LoadFromStream(*file, byteLength); 224 delete file; 225 226 if (!ok) 227 throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\""); 228 } else { 229 throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\""); 230 } 231 } 232 } 233} 234 235inline bool Buffer::LoadFromStream(IOStream &stream, size_t length, size_t baseOffset) { 236 byteLength = length ? length : stream.FileSize(); 237 238 if (baseOffset) { 239 stream.Seek(baseOffset, aiOrigin_SET); 240 } 241 242 mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>()); 243 244 if (stream.Read(mData.get(), byteLength, 1) != 1) { 245 return false; 246 } 247 return true; 248} 249 250inline 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) { 251 // Check pointer to data 252 if (pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided."); 253 254 // Check offset 255 if (pOffset > byteLength) { 256 const uint8_t val_size = 32; 257 258 char val[val_size]; 259 260 ai_snprintf(val, val_size, AI_SIZEFMT, pOffset); 261 throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region."); 262 } 263 264 // Check length 265 if ((pOffset + pEncodedData_Length) > byteLength) { 266 const uint8_t val_size = 64; 267 268 char val[val_size]; 269 270 ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length); 271 throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range."); 272 } 273 274 // Add new region 275 EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID)); 276 // And set new value for "byteLength" 277 byteLength += (pDecodedData_Length - pEncodedData_Length); 278} 279 280inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { 281 if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return; 282 283 for (SEncodedRegion *reg : EncodedRegion_List) { 284 if (reg->ID == pID) { 285 EncodedRegion_Current = reg; 286 287 return; 288 } 289 } 290 291 throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found."); 292} 293 294inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) { 295 const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count; 296 297 uint8_t *new_data; 298 299 if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) return false; 300 301 new_data = new uint8_t[new_data_size]; 302 // Copy data which place before replacing part. 303 memcpy(new_data, mData.get(), pBufferData_Offset); 304 // Copy new data. 305 memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count); 306 // Copy data which place after replacing part. 307 memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset); 308 // Apply new data 309 mData.reset(new_data, std::default_delete<uint8_t[]>()); 310 byteLength = new_data_size; 311 312 return true; 313} 314 315inline size_t Buffer::AppendData(uint8_t *data, size_t length) { 316 size_t offset = this->byteLength; 317 Grow(length); 318 memcpy(mData.get() + offset, data, length); 319 return offset; 320} 321 322inline void Buffer::Grow(size_t amount) { 323 if (amount <= 0) return; 324 if (capacity >= byteLength + amount) { 325 byteLength += amount; 326 return; 327 } 328 329 // Shift operation is standard way to divide integer by 2, it doesn't cast it to float back and forth, also works for odd numbers, 330 // originally it would look like: static_cast<size_t>(capacity * 1.5f) 331 capacity = std::max(capacity + (capacity >> 1), byteLength + amount); 332 333 uint8_t *b = new uint8_t[capacity]; 334 if (mData) memcpy(b, mData.get(), byteLength); 335 mData.reset(b, std::default_delete<uint8_t[]>()); 336 byteLength += amount; 337} 338 339// 340// struct BufferView 341// 342 343inline void BufferView::Read(Value &obj, Asset &r) { 344 const char *bufferId = MemberOrDefault<const char *>(obj, "buffer", 0); 345 if (bufferId) { 346 buffer = r.buffers.Get(bufferId); 347 } 348 349 byteOffset = MemberOrDefault(obj, "byteOffset", 0u); 350 byteLength = MemberOrDefault(obj, "byteLength", 0u); 351} 352 353// 354// struct Accessor 355// 356 357inline void Accessor::Read(Value &obj, Asset &r) { 358 const char *bufferViewId = MemberOrDefault<const char *>(obj, "bufferView", 0); 359 if (bufferViewId) { 360 bufferView = r.bufferViews.Get(bufferViewId); 361 } 362 363 byteOffset = MemberOrDefault(obj, "byteOffset", 0u); 364 byteStride = MemberOrDefault(obj, "byteStride", 0u); 365 componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); 366 count = MemberOrDefault(obj, "count", 0u); 367 368 const char *typestr; 369 type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; 370} 371 372inline unsigned int Accessor::GetNumComponents() { 373 return AttribType::GetNumComponents(type); 374} 375 376inline unsigned int Accessor::GetBytesPerComponent() { 377 return int(ComponentTypeSize(componentType)); 378} 379 380inline unsigned int Accessor::GetElementSize() { 381 return GetNumComponents() * GetBytesPerComponent(); 382} 383 384inline uint8_t *Accessor::GetPointer() { 385 if (!bufferView || !bufferView->buffer) return 0; 386 uint8_t *basePtr = bufferView->buffer->GetPointer(); 387 if (!basePtr) return 0; 388 389 size_t offset = byteOffset + bufferView->byteOffset; 390 391 // Check if region is encoded. 392 if (bufferView->buffer->EncodedRegion_Current != nullptr) { 393 const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset; 394 const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length; 395 396 if ((offset >= begin) && (offset < end)) 397 return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin]; 398 } 399 400 return basePtr + offset; 401} 402 403namespace { 404inline void CopyData(size_t count, 405 const uint8_t *src, size_t src_stride, 406 uint8_t *dst, size_t dst_stride) { 407 if (src_stride == dst_stride) { 408 memcpy(dst, src, count * src_stride); 409 } else { 410 size_t sz = std::min(src_stride, dst_stride); 411 for (size_t i = 0; i < count; ++i) { 412 memcpy(dst, src, sz); 413 if (sz < dst_stride) { 414 memset(dst + sz, 0, dst_stride - sz); 415 } 416 src += src_stride; 417 dst += dst_stride; 418 } 419 } 420} 421} // namespace 422 423template <class T> 424bool Accessor::ExtractData(T *&outData) { 425 uint8_t *data = GetPointer(); 426 if (!data) return false; 427 428 const size_t elemSize = GetElementSize(); 429 const size_t totalSize = elemSize * count; 430 431 const size_t stride = byteStride ? byteStride : elemSize; 432 433 const size_t targetElemSize = sizeof(T); 434 ai_assert(elemSize <= targetElemSize); 435 436 ai_assert(count * stride <= bufferView->byteLength); 437 438 outData = new T[count]; 439 if (stride == elemSize && targetElemSize == elemSize) { 440 memcpy(outData, data, totalSize); 441 } else { 442 for (size_t i = 0; i < count; ++i) { 443 memcpy(outData + i, data + i * stride, elemSize); 444 } 445 } 446 447 return true; 448} 449 450inline void Accessor::WriteData(size_t cnt, const void *src_buffer, size_t src_stride) { 451 uint8_t *buffer_ptr = bufferView->buffer->GetPointer(); 452 size_t offset = byteOffset + bufferView->byteOffset; 453 454 size_t dst_stride = GetNumComponents() * GetBytesPerComponent(); 455 456 const uint8_t *src = reinterpret_cast<const uint8_t *>(src_buffer); 457 uint8_t *dst = reinterpret_cast<uint8_t *>(buffer_ptr + offset); 458 459 ai_assert(dst + count * dst_stride <= buffer_ptr + bufferView->buffer->byteLength); 460 CopyData(cnt, src, src_stride, dst, dst_stride); 461} 462 463inline Accessor::Indexer::Indexer(Accessor &acc) : 464 accessor(acc), data(acc.GetPointer()), elemSize(acc.GetElementSize()), stride(acc.byteStride ? acc.byteStride : elemSize) { 465} 466 467//! Accesses the i-th value as defined by the accessor 468template <class T> 469T Accessor::Indexer::GetValue(int i) { 470 ai_assert(data); 471 ai_assert(i * stride < accessor.bufferView->byteLength); 472 T value = T(); 473 memcpy(&value, data + i * stride, elemSize); 474 //value >>= 8 * (sizeof(T) - elemSize); 475 return value; 476} 477 478inline Image::Image() : 479 width(0), height(0), mDataLength(0) { 480} 481 482inline void Image::Read(Value &obj, Asset &r) { 483 // Check for extensions first (to detect binary embedded data) 484 if (Value *extensions = FindObject(obj, "extensions")) { 485 if (r.extensionsUsed.KHR_binary_glTF) { 486 if (Value *ext = FindObject(*extensions, "KHR_binary_glTF")) { 487 488 width = MemberOrDefault(*ext, "width", 0); 489 height = MemberOrDefault(*ext, "height", 0); 490 491 ReadMember(*ext, "mimeType", mimeType); 492 493 const char *bufferViewId; 494 if (ReadMember(*ext, "bufferView", bufferViewId)) { 495 Ref<BufferView> bv = r.bufferViews.Get(bufferViewId); 496 if (bv) { 497 mDataLength = bv->byteLength; 498 mData.reset(new uint8_t[mDataLength]); 499 memcpy(mData.get(), bv->buffer->GetPointer() + bv->byteOffset, mDataLength); 500 } 501 } 502 } 503 } 504 } 505 506 if (!mDataLength) { 507 Value *curUri = FindString(obj, "uri"); 508 if (nullptr != curUri) { 509 const char *uristr = curUri->GetString(); 510 511 glTFCommon::Util::DataURI dataURI; 512 if (ParseDataURI(uristr, curUri->GetStringLength(), dataURI)) { 513 mimeType = dataURI.mediaType; 514 if (dataURI.base64) { 515 uint8_t *ptr = nullptr; 516 mDataLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr); 517 mData.reset(ptr); 518 } 519 } else { 520 this->uri = uristr; 521 } 522 } 523 } 524} 525 526inline uint8_t *Image::StealData() { 527 mDataLength = 0; 528 return mData.release(); 529} 530 531inline void Image::SetData(uint8_t *data, size_t length, Asset &r) { 532 Ref<Buffer> b = r.GetBodyBuffer(); 533 if (b) { // binary file: append to body 534 std::string bvId = r.FindUniqueID(this->id, "imgdata"); 535 bufferView = r.bufferViews.Create(bvId); 536 537 bufferView->buffer = b; 538 bufferView->byteLength = length; 539 bufferView->byteOffset = b->AppendData(data, length); 540 } else { // text file: will be stored as a data uri 541 uint8_t *temp = new uint8_t[length]; 542 memcpy(temp, data, length); 543 this->mData.reset(temp); 544 this->mDataLength = length; 545 } 546} 547 548inline void Sampler::Read(Value &obj, Asset & /*r*/) { 549 SetDefaults(); 550 551 ReadMember(obj, "magFilter", magFilter); 552 ReadMember(obj, "minFilter", minFilter); 553 ReadMember(obj, "wrapS", wrapS); 554 ReadMember(obj, "wrapT", wrapT); 555} 556 557inline void Sampler::SetDefaults() { 558 magFilter = SamplerMagFilter_Linear; 559 minFilter = SamplerMinFilter_Linear; 560 wrapS = SamplerWrap_Repeat; 561 wrapT = SamplerWrap_Repeat; 562} 563 564inline void Texture::Read(Value &obj, Asset &r) { 565 const char *sourcestr; 566 if (ReadMember(obj, "source", sourcestr)) { 567 source = r.images.Get(sourcestr); 568 } 569 570 const char *samplerstr; 571 if (ReadMember(obj, "sampler", samplerstr)) { 572 sampler = r.samplers.Get(samplerstr); 573 } 574} 575 576namespace { 577inline void ReadMaterialProperty(Asset &r, Value &vals, const char *propName, TexProperty &out) { 578 if (Value *prop = FindMember(vals, propName)) { 579 if (prop->IsString()) { 580 out.texture = r.textures.Get(prop->GetString()); 581 } else { 582 ReadValue(*prop, out.color); 583 } 584 } 585} 586} // namespace 587 588inline void Material::Read(Value &material, Asset &r) { 589 SetDefaults(); 590 591 if (Value *values = FindObject(material, "values")) { 592 ReadMaterialProperty(r, *values, "ambient", this->ambient); 593 ReadMaterialProperty(r, *values, "diffuse", this->diffuse); 594 ReadMaterialProperty(r, *values, "specular", this->specular); 595 596 ReadMember(*values, "transparency", transparency); 597 ReadMember(*values, "shininess", shininess); 598 } 599 600 if (Value *extensions = FindObject(material, "extensions")) { 601 if (r.extensionsUsed.KHR_materials_common) { 602 if (Value *ext = FindObject(*extensions, "KHR_materials_common")) { 603 if (Value *tnq = FindString(*ext, "technique")) { 604 const char *t = tnq->GetString(); 605 if (strcmp(t, "BLINN") == 0) 606 technique = Technique_BLINN; 607 else if (strcmp(t, "PHONG") == 0) 608 technique = Technique_PHONG; 609 else if (strcmp(t, "LAMBERT") == 0) 610 technique = Technique_LAMBERT; 611 else if (strcmp(t, "CONSTANT") == 0) 612 technique = Technique_CONSTANT; 613 } 614 615 if (Value *values = FindObject(*ext, "values")) { 616 ReadMaterialProperty(r, *values, "ambient", this->ambient); 617 ReadMaterialProperty(r, *values, "diffuse", this->diffuse); 618 ReadMaterialProperty(r, *values, "specular", this->specular); 619 620 ReadMember(*values, "doubleSided", doubleSided); 621 ReadMember(*values, "transparent", transparent); 622 ReadMember(*values, "transparency", transparency); 623 ReadMember(*values, "shininess", shininess); 624 } 625 } 626 } 627 } 628} 629 630namespace { 631void SetVector(vec4 &v, float x, float y, float z, float w) { 632 v[0] = x; 633 v[1] = y; 634 v[2] = z; 635 v[3] = w; 636} 637} // namespace 638 639inline void Material::SetDefaults() { 640 SetVector(ambient.color, 0, 0, 0, 1); 641 SetVector(diffuse.color, 0, 0, 0, 1); 642 SetVector(specular.color, 0, 0, 0, 1); 643 SetVector(emission.color, 0, 0, 0, 1); 644 645 doubleSided = false; 646 transparent = false; 647 transparency = 1.0; 648 shininess = 0.0; 649 650 technique = Technique_undefined; 651} 652 653namespace { 654 655template <int N> 656inline int Compare(const char *attr, const char (&str)[N]) { 657 return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0; 658} 659 660inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) { 661 if ((pos = Compare(attr, "POSITION"))) { 662 v = &(p.attributes.position); 663 } else if ((pos = Compare(attr, "NORMAL"))) { 664 v = &(p.attributes.normal); 665 } else if ((pos = Compare(attr, "TEXCOORD"))) { 666 v = &(p.attributes.texcoord); 667 } else if ((pos = Compare(attr, "COLOR"))) { 668 v = &(p.attributes.color); 669 } else if ((pos = Compare(attr, "JOINT"))) { 670 v = &(p.attributes.joint); 671 } else if ((pos = Compare(attr, "JOINTMATRIX"))) { 672 v = &(p.attributes.jointmatrix); 673 } else if ((pos = Compare(attr, "WEIGHT"))) { 674 v = &(p.attributes.weight); 675 } else 676 return false; 677 return true; 678} 679} // namespace 680 681inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { 682 /****************** Mesh primitives ******************/ 683 Value *curPrimitives = FindArray(pJSON_Object, "primitives"); 684 if (nullptr != curPrimitives) { 685 this->primitives.resize(curPrimitives->Size()); 686 for (unsigned int i = 0; i < curPrimitives->Size(); ++i) { 687 Value &primitive = (*curPrimitives)[i]; 688 689 Primitive &prim = this->primitives[i]; 690 prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES); 691 692 if (Value *attrs = FindObject(primitive, "attributes")) { 693 for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { 694 if (!it->value.IsString()) continue; 695 const char *attr = it->name.GetString(); 696 // Valid attribute semantics include POSITION, NORMAL, TEXCOORD, COLOR, JOINT, JOINTMATRIX, 697 // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. 698 699 int undPos = 0; 700 Mesh::AccessorList *vec = 0; 701 if (GetAttribVector(prim, attr, vec, undPos)) { 702 size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; 703 if ((*vec).size() <= idx) (*vec).resize(idx + 1); 704 (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetString()); 705 } 706 } 707 } 708 709 if (Value *indices = FindString(primitive, "indices")) { 710 prim.indices = pAsset_Root.accessors.Get(indices->GetString()); 711 } 712 713 if (Value *material = FindString(primitive, "material")) { 714 prim.material = pAsset_Root.materials.Get(material->GetString()); 715 } 716 } 717 } 718 719 /****************** Mesh extensions ******************/ 720 Value *json_extensions = FindObject(pJSON_Object, "extensions"); 721 722 if (json_extensions == nullptr) goto mr_skip_extensions; 723 724#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 725 for (Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++) { 726 if (it_memb->name.GetString() == std::string("Open3DGC-compression")) { 727 // Search for compressed data. 728 // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and 729 // new data will replace old encoded part by request. In fact \"compressedData\" is kind of "accessor" structure. 730 Value *comp_data = FindObject(it_memb->value, "compressedData"); 731 732 if (comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\"."); 733 734 ASSIMP_LOG_INFO("GLTF: Decompressing Open3DGC data."); 735 736/************** Read data from JSON-document **************/ 737#define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \ 738 if (!ReadMember(*comp_data, pFieldName, pOut)) { \ 739 throw DeadlyImportError("GLTF: \"compressedData\" must has \"", pFieldName, "\"."); \ 740 } 741 742 const char *mode_str; 743 const char *type_str; 744 ComponentType component_type; 745 SCompression_Open3DGC *ext_o3dgc = new SCompression_Open3DGC; 746 747 MESH_READ_COMPRESSEDDATA_MEMBER("buffer", ext_o3dgc->Buffer); 748 MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", ext_o3dgc->Offset); 749 MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type); 750 MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str); 751 MESH_READ_COMPRESSEDDATA_MEMBER("count", ext_o3dgc->Count); 752 MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str); 753 MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", ext_o3dgc->IndicesCount); 754 MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", ext_o3dgc->VerticesCount); 755 756#undef MESH_READ_COMPRESSEDDATA_MEMBER 757 758 // Check some values 759 if (strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data."); 760 if (component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data."); 761 762 // Set read/write data mode. 763 if (strcmp(mode_str, "binary") == 0) 764 ext_o3dgc->Binary = true; 765 else if (strcmp(mode_str, "ascii") == 0) 766 ext_o3dgc->Binary = false; 767 else 768 throw DeadlyImportError("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"", mode_str, "\"."); 769 770 /************************ Decoding ************************/ 771 Decode_O3DGC(*ext_o3dgc, pAsset_Root); 772 Extension.push_back(ext_o3dgc); // store info in mesh extensions list. 773 } // if(it_memb->name.GetString() == "Open3DGC-compression") 774 else { 775 throw DeadlyImportError("GLTF: Unknown mesh extension: \"", it_memb->name.GetString(), "\"."); 776 } 777 } // for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++) 778#endif 779 780mr_skip_extensions: 781 782 return; // After label some operators must be present. 783} 784 785#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC 786inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC &pCompression_Open3DGC, Asset &pAsset_Root) { 787 typedef unsigned short IndicesType; ///< \sa glTFExporter::ExportMeshes. 788 789 o3dgc::SC3DMCDecoder<IndicesType> decoder; 790 o3dgc::IndexedFaceSet<IndicesType> ifs; 791 o3dgc::BinaryStream bstream; 792 uint8_t *decoded_data; 793 size_t decoded_data_size = 0; 794 Ref<Buffer> buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); 795 796 // Read data from buffer and place it in BinaryStream for decoder. 797 // Just "Count" because always is used type equivalent to uint8_t. 798 bstream.LoadFromBuffer(&buf->GetPointer()[pCompression_Open3DGC.Offset], static_cast<unsigned long>(pCompression_Open3DGC.Count)); 799 800 // After decoding header we can get size of primitives. 801 if (decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header."); 802 803 /****************** Get sizes of arrays and check sizes ******************/ 804 // Note. See "Limitations for meshes when using Open3DGC-compression". 805 806 // Indices 807 size_t size_coordindex = ifs.GetNCoordIndex() * 3; // See float attributes note. 808 809 if (primitives[0].indices->count != size_coordindex) 810 throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (", ai_to_string(size_coordindex), 811 ") not equal to uncompressed (", ai_to_string(primitives[0].indices->count), ")."); 812 813 size_coordindex *= sizeof(IndicesType); 814 // Coordinates 815 size_t size_coord = ifs.GetNCoord(); // See float attributes note. 816 817 if (primitives[0].attributes.position[0]->count != size_coord) 818 throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (", ai_to_string(size_coord), 819 ") not equal to uncompressed (", ai_to_string(primitives[0].attributes.position[0]->count), ")."); 820 821 size_coord *= 3 * sizeof(float); 822 // Normals 823 size_t size_normal = ifs.GetNNormal(); // See float attributes note. 824 825 if (primitives[0].attributes.normal[0]->count != size_normal) 826 throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (", ai_to_string(size_normal), 827 ") not equal to uncompressed (", ai_to_string(primitives[0].attributes.normal[0]->count), ")."); 828 829 size_normal *= 3 * sizeof(float); 830 // Additional attributes. 831 std::vector<size_t> size_floatattr; 832 std::vector<size_t> size_intattr; 833 834 size_floatattr.resize(ifs.GetNumFloatAttributes()); 835 size_intattr.resize(ifs.GetNumIntAttributes()); 836 837 decoded_data_size = size_coordindex + size_coord + size_normal; 838 for (size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++) { 839 // size = number_of_elements * components_per_element * size_of_component. 840 // Note. But as you can see above, at first we are use this variable in meaning "count". After checking count of objects... 841 size_t tval = ifs.GetNFloatAttribute(static_cast<unsigned long>(idx)); 842 843 switch (ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))) { 844 case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: 845 // Check situation when encoded data contain texture coordinates but primitive not. 846 if (idx_texcoord < primitives[0].attributes.texcoord.size()) { 847 if (primitives[0].attributes.texcoord[idx]->count != tval) 848 throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (", ai_to_string(tval), 849 ") not equal to uncompressed (", ai_to_string(primitives[0].attributes.texcoord[idx]->count), ")."); 850 851 idx_texcoord++; 852 } else { 853 ifs.SetNFloatAttribute(static_cast<unsigned long>(idx), 0ul); // Disable decoding this attribute. 854 } 855 856 break; 857 default: 858 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))); 859 } 860 861 tval *= ifs.GetFloatAttributeDim(static_cast<unsigned long>(idx)) * sizeof(o3dgc::Real); // After checking count of objects we can get size of array. 862 size_floatattr[idx] = tval; 863 decoded_data_size += tval; 864 } 865 866 for (size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) { 867 // size = number_of_elements * components_per_element * size_of_component. See float attributes note. 868 size_t tval = ifs.GetNIntAttribute(static_cast<unsigned long>(idx)); 869 switch (ifs.GetIntAttributeType(static_cast<unsigned long>(idx))) { 870 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: 871 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: 872 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: 873 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: 874 break; 875 876 default: 877 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx)))); 878 } 879 880 tval *= ifs.GetIntAttributeDim(static_cast<unsigned long>(idx)) * sizeof(long); // See float attributes note. 881 size_intattr[idx] = tval; 882 decoded_data_size += tval; 883 } 884 885 // Create array for decoded data. 886 decoded_data = new uint8_t[decoded_data_size]; 887 888 /****************** Set right array regions for decoder ******************/ 889 890 auto get_buf_offset = [](Ref<Accessor> &pAccessor) -> size_t { return pAccessor->byteOffset + pAccessor->bufferView->byteOffset; }; 891 892 // Indices 893 ifs.SetCoordIndex((IndicesType *const)(decoded_data + get_buf_offset(primitives[0].indices))); 894 // Coordinates 895 ifs.SetCoord((o3dgc::Real *const)(decoded_data + get_buf_offset(primitives[0].attributes.position[0]))); 896 // Normals 897 if (size_normal) { 898 ifs.SetNormal((o3dgc::Real *const)(decoded_data + get_buf_offset(primitives[0].attributes.normal[0]))); 899 } 900 901 for (size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++) { 902 switch (ifs.GetFloatAttributeType(static_cast<unsigned long>(idx))) { 903 case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD: 904 if (idx_texcoord < primitives[0].attributes.texcoord.size()) { 905 // See above about absent attributes. 906 ifs.SetFloatAttribute(static_cast<unsigned long>(idx), (o3dgc::Real *const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx]))); 907 idx_texcoord++; 908 } 909 910 break; 911 default: 912 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: ", ai_to_string(ifs.GetFloatAttributeType(static_cast<unsigned long>(idx)))); 913 } 914 } 915 916 for (size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++) { 917 switch (ifs.GetIntAttributeType(static_cast<unsigned int>(idx))) { 918 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_UNKOWN: 919 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX: 920 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_JOINT_ID: 921 case o3dgc::O3DGC_IFS_INT_ATTRIBUTE_TYPE_INDEX_BUFFER_ID: 922 break; 923 924 // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint))); 925 default: 926 throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: ", ai_to_string(ifs.GetIntAttributeType(static_cast<unsigned long>(idx)))); 927 } 928 } 929 930 // 931 // Decode data 932 // 933 if (decoder.DecodePayload(ifs, bstream) != o3dgc::O3DGC_OK) { 934 throw DeadlyImportError("GLTF: can not decode Open3DGC data."); 935 } 936 937 // Set encoded region for "buffer". 938 buf->EncodedRegion_Mark(pCompression_Open3DGC.Offset, pCompression_Open3DGC.Count, decoded_data, decoded_data_size, id); 939 // No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data". 940 // "delete [] output_data;" 941} 942#endif 943 944inline void Camera::Read(Value &obj, Asset & /*r*/) { 945 type = MemberOrDefault(obj, "type", Camera::Perspective); 946 947 const char *subobjId = (type == Camera::Orthographic) ? "orthographic" : "perspective"; 948 949 Value *it = FindObject(obj, subobjId); 950 if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters"); 951 952 if (type == Camera::Perspective) { 953 perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f); 954 perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f / 2.f); 955 perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); 956 perspective.znear = MemberOrDefault(*it, "znear", 0.01f); 957 } else { 958 ortographic.xmag = MemberOrDefault(*it, "xmag", 1.f); 959 ortographic.ymag = MemberOrDefault(*it, "ymag", 1.f); 960 ortographic.zfar = MemberOrDefault(*it, "zfar", 100.f); 961 ortographic.znear = MemberOrDefault(*it, "znear", 0.01f); 962 } 963} 964 965inline void Light::Read(Value &obj, Asset & /*r*/) { 966 SetDefaults(); 967 968 Value *curType = FindString(obj, "type"); 969 if (nullptr != curType) { 970 const char *t = curType->GetString(); 971 if (strcmp(t, "ambient") == 0) 972 this->type = Type_ambient; 973 else if (strcmp(t, "directional") == 0) 974 this->type = Type_directional; 975 else if (strcmp(t, "point") == 0) 976 this->type = Type_point; 977 else if (strcmp(t, "spot") == 0) 978 this->type = Type_spot; 979 980 if (this->type != Type_undefined) { 981 if (Value *vals = FindString(obj, t)) { 982 ReadMember(*vals, "color", color); 983 984 ReadMember(*vals, "constantAttenuation", constantAttenuation); 985 ReadMember(*vals, "linearAttenuation", linearAttenuation); 986 ReadMember(*vals, "quadraticAttenuation", quadraticAttenuation); 987 ReadMember(*vals, "distance", distance); 988 989 ReadMember(*vals, "falloffAngle", falloffAngle); 990 ReadMember(*vals, "falloffExponent", falloffExponent); 991 } 992 } 993 } 994} 995 996inline void Light::SetDefaults() { 997#ifndef M_PI 998 const float M_PI = 3.14159265358979323846f; 999#endif 1000 1001 type = Type_undefined; 1002 1003 SetVector(color, 0.f, 0.f, 0.f, 1.f); 1004 1005 constantAttenuation = 0.f; 1006 linearAttenuation = 1.f; 1007 quadraticAttenuation = 1.f; 1008 distance = 0.f; 1009 1010 falloffAngle = static_cast<float>(M_PI / 2.f); 1011 falloffExponent = 0.f; 1012} 1013 1014inline void Node::Read(Value &obj, Asset &r) { 1015 if (name.empty()) { 1016 name = id; 1017 } 1018 1019 Value *curChildren = FindArray(obj, "children"); 1020 if (nullptr != curChildren) { 1021 this->children.reserve(curChildren->Size()); 1022 for (unsigned int i = 0; i < curChildren->Size(); ++i) { 1023 Value &child = (*curChildren)[i]; 1024 if (child.IsString()) { 1025 // get/create the child node 1026 Ref<Node> chn = r.nodes.Get(child.GetString()); 1027 if (chn) this->children.push_back(chn); 1028 } 1029 } 1030 } 1031 1032 Value *curMatrix = FindArray(obj, "matrix"); 1033 if (nullptr != curMatrix) { 1034 ReadValue(*curMatrix, this->matrix); 1035 } else { 1036 ReadMember(obj, "translation", translation); 1037 ReadMember(obj, "scale", scale); 1038 ReadMember(obj, "rotation", rotation); 1039 } 1040 1041 Value *curMeshes = FindArray(obj, "meshes"); 1042 if (nullptr != curMeshes) { 1043 unsigned int numMeshes = (unsigned int)curMeshes->Size(); 1044 1045 std::vector<unsigned int> meshList; 1046 1047 this->meshes.reserve(numMeshes); 1048 for (unsigned i = 0; i < numMeshes; ++i) { 1049 if ((*curMeshes)[i].IsString()) { 1050 Ref<Mesh> mesh = r.meshes.Get((*curMeshes)[i].GetString()); 1051 if (mesh) { 1052 this->meshes.push_back(mesh); 1053 } 1054 } 1055 } 1056 } 1057 1058 Value *curCamera = FindString(obj, "camera"); 1059 if (nullptr != curCamera) { 1060 this->camera = r.cameras.Get(curCamera->GetString()); 1061 if (this->camera) { 1062 this->camera->id = this->id; 1063 } 1064 } 1065 1066 // TODO load "skeletons", "skin", "jointName" 1067 1068 if (Value *extensions = FindObject(obj, "extensions")) { 1069 if (r.extensionsUsed.KHR_materials_common) { 1070 1071 if (Value *ext = FindObject(*extensions, "KHR_materials_common")) { 1072 Value *curLight = FindString(*ext, "light"); 1073 if (nullptr != curLight) { 1074 this->light = r.lights.Get(curLight->GetString()); 1075 } 1076 } 1077 } 1078 } 1079} 1080 1081inline void Scene::Read(Value &obj, Asset &r) { 1082 if (Value *array = FindArray(obj, "nodes")) { 1083 for (unsigned int i = 0; i < array->Size(); ++i) { 1084 if (!(*array)[i].IsString()) continue; 1085 Ref<Node> node = r.nodes.Get((*array)[i].GetString()); 1086 if (node) 1087 this->nodes.push_back(node); 1088 } 1089 } 1090} 1091 1092inline void AssetMetadata::Read(Document &doc) { 1093 // read the version, etc. 1094 if (Value *obj = FindObject(doc, "asset")) { 1095 ReadMember(*obj, "copyright", copyright); 1096 ReadMember(*obj, "generator", generator); 1097 1098 premultipliedAlpha = MemberOrDefault(*obj, "premultipliedAlpha", false); 1099 1100 if (Value *versionString = FindString(*obj, "version")) { 1101 version = versionString->GetString(); 1102 } else if (Value *versionNumber = FindNumber(*obj, "version")) { 1103 char buf[4]; 1104 1105 ai_snprintf(buf, 4, "%.1f", versionNumber->GetDouble()); 1106 1107 version = buf; 1108 } 1109 1110 Value *curProfile = FindObject(*obj, "profile"); 1111 if (nullptr != curProfile) { 1112 ReadMember(*curProfile, "api", this->profile.api); 1113 ReadMember(*curProfile, "version", this->profile.version); 1114 } 1115 } 1116 1117 if (version.empty() || version[0] != '1') { 1118 throw DeadlyImportError("GLTF: Unsupported glTF version: ", version); 1119 } 1120} 1121 1122// 1123// Asset methods implementation 1124// 1125 1126inline void Asset::ReadBinaryHeader(IOStream &stream) { 1127 GLB_Header header; 1128 if (stream.Read(&header, sizeof(header), 1) != 1) { 1129 throw DeadlyImportError("GLTF: Unable to read the file header"); 1130 } 1131 1132 if (strncmp((char *)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) { 1133 throw DeadlyImportError("GLTF: Invalid binary glTF file"); 1134 } 1135 1136 AI_SWAP4(header.version); 1137 asset.version = ai_to_string(header.version); 1138 if (header.version != 1) { 1139 throw DeadlyImportError("GLTF: Unsupported binary glTF version"); 1140 } 1141 1142 AI_SWAP4(header.sceneFormat); 1143 if (header.sceneFormat != SceneFormat_JSON) { 1144 throw DeadlyImportError("GLTF: Unsupported binary glTF scene format"); 1145 } 1146 1147 AI_SWAP4(header.length); 1148 AI_SWAP4(header.sceneLength); 1149 1150 static_assert(std::numeric_limits<uint32_t>::max() <= std::numeric_limits<size_t>::max(), "size_t must be at least 32bits"); 1151 mSceneLength = static_cast<size_t>(header.sceneLength); // Can't be larger than 4GB (max. uint32_t) 1152 1153 mBodyOffset = sizeof(header) + mSceneLength; 1154 mBodyOffset = (mBodyOffset + 3) & ~3; // Round up to next multiple of 4 1155 1156 mBodyLength = header.length - mBodyOffset; 1157} 1158 1159inline void Asset::Load(const std::string &pFile, bool isBinary) { 1160 mCurrentAssetDir.clear(); 1161 1162 /*int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); 1163 if (pos != int(std::string::npos)) mCurrentAssetDir = pFile.substr(0, pos + 1);*/ 1164 if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) { 1165 mCurrentAssetDir = getCurrentAssetDir(pFile); 1166 } 1167 1168 shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true)); 1169 if (!stream) { 1170 throw DeadlyImportError("GLTF: Could not open file for reading"); 1171 } 1172 1173 // is binary? then read the header 1174 if (isBinary) { 1175 SetAsBinary(); // also creates the body buffer 1176 ReadBinaryHeader(*stream); 1177 } else { 1178 mSceneLength = stream->FileSize(); 1179 mBodyLength = 0; 1180 } 1181 1182 // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later 1183 if (mSceneLength < 2) { 1184 throw DeadlyImportError("GLTF: No JSON file contents"); 1185 } 1186 1187 // Binary format only supports up to 4GB of JSON so limit it there to avoid extreme memory allocation 1188 if (mSceneLength >= std::numeric_limits<uint32_t>::max()) { 1189 throw DeadlyImportError("GLTF: JSON size greater than 4GB"); 1190 } 1191 1192 // read the scene data, ensure null termination 1193 std::vector<char> sceneData(mSceneLength + 1); 1194 sceneData[mSceneLength] = '\0'; 1195 1196 if (stream->Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { 1197 throw DeadlyImportError("GLTF: Could not read the file contents"); 1198 } 1199 1200 // parse the JSON document 1201 1202 Document doc; 1203 doc.ParseInsitu(&sceneData[0]); 1204 1205 if (doc.HasParseError()) { 1206 char buffer[32]; 1207 ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset())); 1208 throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError())); 1209 } 1210 1211 if (!doc.IsObject()) { 1212 throw DeadlyImportError("GLTF: JSON document root must be a JSON object"); 1213 } 1214 1215 // Fill the buffer instance for the current file embedded contents 1216 if (mBodyLength > 0) { 1217 if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) { 1218 throw DeadlyImportError("GLTF: Unable to read gltf file"); 1219 } 1220 } 1221 1222 // Load the metadata 1223 asset.Read(doc); 1224 ReadExtensionsUsed(doc); 1225 1226 // Prepare the dictionaries 1227 for (size_t i = 0; i < mDicts.size(); ++i) { 1228 mDicts[i]->AttachToDocument(doc); 1229 } 1230 1231 // Read the "scene" property, which specifies which scene to load 1232 // and recursively load everything referenced by it 1233 Value *curScene = FindString(doc, "scene"); 1234 if (nullptr != curScene) { 1235 this->scene = scenes.Get(curScene->GetString()); 1236 } 1237 1238 // Clean up 1239 for (size_t i = 0; i < mDicts.size(); ++i) { 1240 mDicts[i]->DetachFromDocument(); 1241 } 1242} 1243 1244inline void Asset::SetAsBinary() { 1245 if (!extensionsUsed.KHR_binary_glTF) { 1246 extensionsUsed.KHR_binary_glTF = true; 1247 mBodyBuffer = buffers.Create("binary_glTF"); 1248 mBodyBuffer->MarkAsSpecial(); 1249 } 1250} 1251 1252inline void Asset::ReadExtensionsUsed(Document &doc) { 1253 Value *extsUsed = FindArray(doc, "extensionsUsed"); 1254 if (!extsUsed) return; 1255 1256 std::gltf_unordered_map<std::string, bool> exts; 1257 1258 for (unsigned int i = 0; i < extsUsed->Size(); ++i) { 1259 if ((*extsUsed)[i].IsString()) { 1260 exts[(*extsUsed)[i].GetString()] = true; 1261 } 1262 } 1263 1264 CHECK_EXT(KHR_binary_glTF); 1265 CHECK_EXT(KHR_materials_common); 1266 1267#undef CHECK_EXT 1268} 1269 1270inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool absolute) { 1271#ifdef ASSIMP_API 1272 (void)absolute; 1273 return mIOSystem->Open(path, mode); 1274#else 1275 if (path.size() < 2) return 0; 1276 if (!absolute && path[1] != ':' && path[0] != '/') { // relative? 1277 path = mCurrentAssetDir + path; 1278 } 1279 FILE *f = fopen(path.c_str(), mode); 1280 return f ? new IOStream(f) : 0; 1281#endif 1282} 1283 1284inline std::string Asset::FindUniqueID(const std::string &str, const char *suffix) { 1285 std::string id = str; 1286 1287 if (!id.empty()) { 1288 if (mUsedIds.find(id) == mUsedIds.end()) 1289 return id; 1290 1291 id += "_"; 1292 } 1293 1294 id += suffix; 1295 1296 Asset::IdMap::iterator it = mUsedIds.find(id); 1297 if (it == mUsedIds.end()) 1298 return id; 1299 1300 char buffer[1024]; 1301 int offset = ai_snprintf(buffer, sizeof(buffer), "%s_", id.c_str()); 1302 for (int i = 0; it != mUsedIds.end(); ++i) { 1303 ai_snprintf(buffer + offset, sizeof(buffer) - offset, "%d", i); 1304 id = buffer; 1305 it = mUsedIds.find(id); 1306 } 1307 1308 return id; 1309} 1310 1311#if _MSC_VER 1312#pragma warning(pop) 1313#endif // _MSC_VER 1314 1315} // namespace glTF 1316