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 "AssetLib/glTF/glTFCommon.h" 43 44#include <assimp/MemoryIOWrapper.h> 45#include <assimp/StringUtils.h> 46#include <assimp/DefaultLogger.hpp> 47 48// clang-format off 49#ifdef ASSIMP_ENABLE_DRACO 50 51// Google draco library headers spew many warnings. Bad Google, no cookie 52# if _MSC_VER 53# pragma warning(push) 54# pragma warning(disable : 4018) // Signed/unsigned mismatch 55# pragma warning(disable : 4804) // Unsafe use of type 'bool' 56# elif defined(__clang__) 57# pragma clang diagnostic push 58# pragma clang diagnostic ignored "-Wsign-compare" 59# elif defined(__GNUC__) 60# pragma GCC diagnostic push 61# if (__GNUC__ > 4) 62# pragma GCC diagnostic ignored "-Wbool-compare" 63# endif 64# pragma GCC diagnostic ignored "-Wsign-compare" 65#endif 66 67#include "draco/compression/decode.h" 68#include "draco/core/decoder_buffer.h" 69 70#if _MSC_VER 71# pragma warning(pop) 72#elif defined(__clang__) 73# pragma clang diagnostic pop 74#elif defined(__GNUC__) 75# pragma GCC diagnostic pop 76#endif 77#ifndef DRACO_MESH_COMPRESSION_SUPPORTED 78# error glTF: KHR_draco_mesh_compression: draco library must have DRACO_MESH_COMPRESSION_SUPPORTED 79#endif 80#endif 81// clang-format on 82 83using namespace Assimp; 84using namespace glTFCommon; 85 86namespace glTF2 { 87 88namespace { 89 90// 91// JSON Value reading helpers 92// 93inline CustomExtension ReadExtensions(const char *name, Value &obj) { 94 CustomExtension ret; 95 ret.name = name; 96 if (obj.IsObject()) { 97 ret.mValues.isPresent = true; 98 for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { 99 auto &val = it->value; 100 ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); 101 } 102 } else if (obj.IsArray()) { 103 ret.mValues.value.reserve(obj.Size()); 104 ret.mValues.isPresent = true; 105 for (unsigned int i = 0; i < obj.Size(); ++i) { 106 ret.mValues.value.push_back(ReadExtensions(name, obj[i])); 107 } 108 } else if (obj.IsNumber()) { 109 if (obj.IsUint64()) { 110 ret.mUint64Value.value = obj.GetUint64(); 111 ret.mUint64Value.isPresent = true; 112 } else if (obj.IsInt64()) { 113 ret.mInt64Value.value = obj.GetInt64(); 114 ret.mInt64Value.isPresent = true; 115 } else if (obj.IsDouble()) { 116 ret.mDoubleValue.value = obj.GetDouble(); 117 ret.mDoubleValue.isPresent = true; 118 } 119 } else if (obj.IsString()) { 120 ReadValue(obj, ret.mStringValue); 121 ret.mStringValue.isPresent = true; 122 } else if (obj.IsBool()) { 123 ret.mBoolValue.value = obj.GetBool(); 124 ret.mBoolValue.isPresent = true; 125 } 126 return ret; 127} 128 129inline void CopyData(size_t count, const uint8_t *src, size_t src_stride, 130 uint8_t *dst, size_t dst_stride) { 131 if (src_stride == dst_stride) { 132 memcpy(dst, src, count * src_stride); 133 return; 134 } 135 136 size_t sz = std::min(src_stride, dst_stride); 137 for (size_t i = 0; i < count; ++i) { 138 memcpy(dst, src, sz); 139 if (sz < dst_stride) { 140 memset(dst + sz, 0, dst_stride - sz); 141 } 142 src += src_stride; 143 dst += dst_stride; 144 } 145} 146 147void SetVector(vec4 &v, const float (&in)[4]) { 148 v[0] = in[0]; 149 v[1] = in[1]; 150 v[2] = in[2]; 151 v[3] = in[3]; 152} 153 154void SetVector(vec3 &v, const float (&in)[3]) { 155 v[0] = in[0]; 156 v[1] = in[1]; 157 v[2] = in[2]; 158} 159 160template <int N> 161inline int Compare(const char *attr, const char (&str)[N]) { 162 return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0; 163} 164 165#if _MSC_VER 166#pragma warning(push) 167#pragma warning(disable : 4706) 168#endif // _MSC_VER 169 170inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) { 171 if ((pos = Compare(attr, "POSITION"))) { 172 v = &(p.attributes.position); 173 } else if ((pos = Compare(attr, "NORMAL"))) { 174 v = &(p.attributes.normal); 175 } else if ((pos = Compare(attr, "TANGENT"))) { 176 v = &(p.attributes.tangent); 177 } else if ((pos = Compare(attr, "TEXCOORD"))) { 178 v = &(p.attributes.texcoord); 179 } else if ((pos = Compare(attr, "COLOR"))) { 180 v = &(p.attributes.color); 181 } else if ((pos = Compare(attr, "JOINT"))) { 182 v = &(p.attributes.joint); 183 } else if ((pos = Compare(attr, "JOINTMATRIX"))) { 184 v = &(p.attributes.jointmatrix); 185 } else if ((pos = Compare(attr, "WEIGHT"))) { 186 v = &(p.attributes.weight); 187 } else 188 return false; 189 return true; 190} 191 192inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, const char *attr, Mesh::AccessorList *&v, int &pos) { 193 if ((pos = Compare(attr, "POSITION"))) { 194 v = &(p.targets[targetIndex].position); 195 } else if ((pos = Compare(attr, "NORMAL"))) { 196 v = &(p.targets[targetIndex].normal); 197 } else if ((pos = Compare(attr, "TANGENT"))) { 198 v = &(p.targets[targetIndex].tangent); 199 } else 200 return false; 201 return true; 202} 203 204} // namespace 205 206inline Value *Object::FindString(Value &val, const char *memberId) { 207 return FindStringInContext(val, memberId, id.c_str(), name.c_str()); 208} 209 210inline Value *Object::FindNumber(Value &val, const char *memberId) { 211 return FindNumberInContext(val, memberId, id.c_str(), name.c_str()); 212} 213 214inline Value *Object::FindUInt(Value &val, const char *memberId) { 215 return FindUIntInContext(val, memberId, id.c_str(), name.c_str()); 216} 217 218inline Value *Object::FindArray(Value &val, const char *memberId) { 219 return FindArrayInContext(val, memberId, id.c_str(), name.c_str()); 220} 221 222inline Value *Object::FindObject(Value &val, const char *memberId) { 223 return FindObjectInContext(val, memberId, id.c_str(), name.c_str()); 224} 225 226inline Value *Object::FindExtension(Value &val, const char *extensionId) { 227 return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); 228} 229 230inline void Object::ReadExtensions(Value &val) { 231 if (Value *curExtensions = FindObject(val, "extensions")) { 232 this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); 233 } 234} 235 236inline void Object::ReadExtras(Value &val) { 237 if (Value *curExtras = FindObject(val, "extras")) { 238 this->extras = glTF2::ReadExtensions("extras", *curExtras); 239 } 240} 241 242#ifdef ASSIMP_ENABLE_DRACO 243 244template <typename T> 245inline void CopyFaceIndex_Draco(Buffer &decodedIndexBuffer, const draco::Mesh &draco_mesh) { 246 const size_t faceStride = sizeof(T) * 3; 247 for (draco::FaceIndex f(0); f < draco_mesh.num_faces(); ++f) { 248 const draco::Mesh::Face &face = draco_mesh.face(f); 249 T indices[3] = { static_cast<T>(face[0].value()), static_cast<T>(face[1].value()), static_cast<T>(face[2].value()) }; 250 memcpy(decodedIndexBuffer.GetPointer() + (f.value() * faceStride), &indices[0], faceStride); 251 } 252} 253 254inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Primitive &prim) { 255 if (!prim.indices || dracoMesh.num_faces() == 0) 256 return; 257 258 // Create a decoded Index buffer (if there is one) 259 size_t componentBytes = prim.indices->GetBytesPerComponent(); 260 261 std::unique_ptr<Buffer> decodedIndexBuffer(new Buffer()); 262 decodedIndexBuffer->Grow(dracoMesh.num_faces() * 3 * componentBytes); 263 264 // If accessor uses the same size as draco implementation, copy the draco buffer directly 265 266 // Usually uint32_t but shouldn't assume 267 if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) { 268 memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength); 269 return; 270 } 271 272 // Not same size, convert 273 switch (componentBytes) { 274 case sizeof(uint32_t): 275 CopyFaceIndex_Draco<uint32_t>(*decodedIndexBuffer, dracoMesh); 276 break; 277 case sizeof(uint16_t): 278 CopyFaceIndex_Draco<uint16_t>(*decodedIndexBuffer, dracoMesh); 279 break; 280 case sizeof(uint8_t): 281 CopyFaceIndex_Draco<uint8_t>(*decodedIndexBuffer, dracoMesh); 282 break; 283 default: 284 ai_assert(false); 285 break; 286 } 287 288 // Assign this alternate data buffer to the accessor 289 prim.indices->decodedBuffer.swap(decodedIndexBuffer); 290} 291 292template <typename T> 293static bool GetAttributeForAllPoints_Draco(const draco::Mesh &dracoMesh, 294 const draco::PointAttribute &dracoAttribute, 295 Buffer &outBuffer) { 296 size_t byteOffset = 0; 297 T values[4] = { 0, 0, 0, 0 }; 298 for (draco::PointIndex i(0); i < dracoMesh.num_points(); ++i) { 299 const draco::AttributeValueIndex val_index = dracoAttribute.mapped_index(i); 300 if (!dracoAttribute.ConvertValue<T>(val_index, dracoAttribute.num_components(), values)) { 301 return false; 302 } 303 304 memcpy(outBuffer.GetPointer() + byteOffset, &values[0], sizeof(T) * dracoAttribute.num_components()); 305 byteOffset += sizeof(T) * dracoAttribute.num_components(); 306 } 307 308 return true; 309} 310 311inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32_t dracoAttribId, Accessor &accessor) { 312 // Create decoded buffer 313 const draco::PointAttribute *pDracoAttribute = dracoMesh.GetAttributeByUniqueId(dracoAttribId); 314 if (pDracoAttribute == nullptr) { 315 throw DeadlyImportError("GLTF: Invalid draco attribute id: ", dracoAttribId); 316 } 317 318 size_t componentBytes = accessor.GetBytesPerComponent(); 319 320 std::unique_ptr<Buffer> decodedAttribBuffer(new Buffer()); 321 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); 322 323 switch (accessor.componentType) { 324 case ComponentType_BYTE: 325 GetAttributeForAllPoints_Draco<int8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 326 break; 327 case ComponentType_UNSIGNED_BYTE: 328 GetAttributeForAllPoints_Draco<uint8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 329 break; 330 case ComponentType_SHORT: 331 GetAttributeForAllPoints_Draco<int16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 332 break; 333 case ComponentType_UNSIGNED_SHORT: 334 GetAttributeForAllPoints_Draco<uint16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 335 break; 336 case ComponentType_UNSIGNED_INT: 337 GetAttributeForAllPoints_Draco<uint32_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 338 break; 339 case ComponentType_FLOAT: 340 GetAttributeForAllPoints_Draco<float>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); 341 break; 342 default: 343 ai_assert(false); 344 break; 345 } 346 347 // Assign this alternate data buffer to the accessor 348 accessor.decodedBuffer.swap(decodedAttribBuffer); 349} 350 351#endif // ASSIMP_ENABLE_DRACO 352 353// 354// LazyDict methods 355// 356 357template <class T> 358inline LazyDict<T>::LazyDict(Asset &asset, const char *dictId, const char *extId) : 359 mDictId(dictId), 360 mExtId(extId), 361 mDict(0), 362 mAsset(asset) { 363 asset.mDicts.push_back(this); // register to the list of dictionaries 364} 365 366template <class T> 367inline LazyDict<T>::~LazyDict() { 368 for (size_t i = 0; i < mObjs.size(); ++i) { 369 delete mObjs[i]; 370 } 371} 372 373template <class T> 374inline void LazyDict<T>::AttachToDocument(Document &doc) { 375 Value *container = nullptr; 376 const char *context = nullptr; 377 378 if (mExtId) { 379 if (Value *exts = FindObject(doc, "extensions")) { 380 container = FindObjectInContext(*exts, mExtId, "extensions"); 381 context = mExtId; 382 } 383 } else { 384 container = &doc; 385 context = "the document"; 386 } 387 388 if (container) { 389 mDict = FindArrayInContext(*container, mDictId, context); 390 } 391} 392 393template <class T> 394inline void LazyDict<T>::DetachFromDocument() { 395 mDict = nullptr; 396} 397 398template <class T> 399unsigned int LazyDict<T>::Remove(const char *id) { 400 id = T::TranslateId(mAsset, id); 401 402 typename IdDict::iterator objIt = mObjsById.find(id); 403 404 if (objIt == mObjsById.end()) { 405 throw DeadlyExportError("GLTF: Object with id \"" + std::string(id) + "\" is not found"); 406 } 407 408 const unsigned int index = objIt->second; 409 410 mAsset.mUsedIds[id] = false; 411 mObjsById.erase(id); 412 mObjsByOIndex.erase(index); 413 delete mObjs[index]; 414 mObjs.erase(mObjs.begin() + index); 415 416 //update index of object in mObjs; 417 for (unsigned int i = index; i < mObjs.size(); ++i) { 418 T *obj = mObjs[i]; 419 420 obj->index = i; 421 } 422 423 for (IdDict::iterator it = mObjsById.begin(); it != mObjsById.end(); ++it) { 424 if (it->second <= index) { 425 continue; 426 } 427 428 mObjsById[it->first] = it->second - 1; 429 } 430 431 for (Dict::iterator it = mObjsByOIndex.begin(); it != mObjsByOIndex.end(); ++it) { 432 if (it->second <= index) { 433 continue; 434 } 435 436 mObjsByOIndex[it->first] = it->second - 1; 437 } 438 439 return index; 440} 441 442template <class T> 443Ref<T> LazyDict<T>::Retrieve(unsigned int i) { 444 445 typename Dict::iterator it = mObjsByOIndex.find(i); 446 if (it != mObjsByOIndex.end()) { // already created? 447 return Ref<T>(mObjs, it->second); 448 } 449 450 // read it from the JSON object 451 if (!mDict) { 452 throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\""); 453 } 454 455 if (!mDict->IsArray()) { 456 throw DeadlyImportError("GLTF: Field \"", mDictId, "\" is not an array"); 457 } 458 459 if (i >= mDict->Size()) { 460 throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\""); 461 } 462 463 Value &obj = (*mDict)[i]; 464 465 if (!obj.IsObject()) { 466 throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object"); 467 } 468 469 if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) { 470 throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself"); 471 } 472 mRecursiveReferenceCheck.insert(i); 473 474 // Unique ptr prevents memory leak in case of Read throws an exception 475 auto inst = std::unique_ptr<T>(new T()); 476 // Try to make this human readable so it can be used in error messages. 477 inst->id = std::string(mDictId) + "[" + ai_to_string(i) + "]"; 478 inst->oIndex = i; 479 ReadMember(obj, "name", inst->name); 480 inst->Read(obj, mAsset); 481 inst->ReadExtensions(obj); 482 inst->ReadExtras(obj); 483 484 Ref<T> result = Add(inst.release()); 485 mRecursiveReferenceCheck.erase(i); 486 return result; 487} 488 489template <class T> 490Ref<T> LazyDict<T>::Get(unsigned int i) { 491 return Ref<T>(mObjs, i); 492} 493 494template <class T> 495Ref<T> LazyDict<T>::Get(const char *id) { 496 id = T::TranslateId(mAsset, id); 497 498 typename IdDict::iterator it = mObjsById.find(id); 499 if (it != mObjsById.end()) { // already created? 500 return Ref<T>(mObjs, it->second); 501 } 502 503 return Ref<T>(); 504} 505 506template <class T> 507Ref<T> LazyDict<T>::Add(T *obj) { 508 unsigned int idx = unsigned(mObjs.size()); 509 mObjs.push_back(obj); 510 mObjsByOIndex[obj->oIndex] = idx; 511 mObjsById[obj->id] = idx; 512 mAsset.mUsedIds[obj->id] = true; 513 return Ref<T>(mObjs, idx); 514} 515 516template <class T> 517Ref<T> LazyDict<T>::Create(const char *id) { 518 Asset::IdMap::iterator it = mAsset.mUsedIds.find(id); 519 if (it != mAsset.mUsedIds.end()) { 520 throw DeadlyImportError("GLTF: two objects with the same ID exist"); 521 } 522 T *inst = new T(); 523 unsigned int idx = unsigned(mObjs.size()); 524 inst->id = id; 525 inst->index = idx; 526 inst->oIndex = idx; 527 return Add(inst); 528} 529 530// 531// glTF dictionary objects methods 532// 533inline Buffer::Buffer() : 534 byteLength(0), 535 type(Type_arraybuffer), 536 EncodedRegion_Current(nullptr), 537 mIsSpecial(false) {} 538 539inline Buffer::~Buffer() { 540 for (SEncodedRegion *reg : EncodedRegion_List) 541 delete reg; 542} 543 544inline const char *Buffer::TranslateId(Asset & /*r*/, const char *id) { 545 return id; 546} 547 548inline void Buffer::Read(Value &obj, Asset &r) { 549 size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0); 550 byteLength = statedLength; 551 552 Value *it = FindString(obj, "uri"); 553 if (!it) { 554 if (statedLength > 0) { 555 throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute"); 556 } 557 return; 558 } 559 560 const char *uri = it->GetString(); 561 562 glTFCommon::Util::DataURI dataURI; 563 if (ParseDataURI(uri, it->GetStringLength(), dataURI)) { 564 if (dataURI.base64) { 565 uint8_t *data = nullptr; 566 this->byteLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, data); 567 this->mData.reset(data, std::default_delete<uint8_t[]>()); 568 569 if (statedLength > 0 && this->byteLength != statedLength) { 570 throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), 571 " bytes, but found ", ai_to_string(dataURI.dataLength)); 572 } 573 } else { // assume raw data 574 if (statedLength != dataURI.dataLength) { 575 throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), 576 " bytes, but found ", ai_to_string(dataURI.dataLength)); 577 } 578 579 this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>()); 580 memcpy(this->mData.get(), dataURI.data, dataURI.dataLength); 581 } 582 } else { // Local file 583 if (byteLength > 0) { 584 std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir.back() == '/' ? r.mCurrentAssetDir : r.mCurrentAssetDir + '/') : ""; 585 586 IOStream *file = r.OpenFile(dir + uri, "rb"); 587 if (file) { 588 bool ok = LoadFromStream(*file, byteLength); 589 delete file; 590 591 if (!ok) 592 throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\""); 593 } else { 594 throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\""); 595 } 596 } 597 } 598} 599 600inline bool Buffer::LoadFromStream(IOStream &stream, size_t length, size_t baseOffset) { 601 byteLength = length ? length : stream.FileSize(); 602 603 if (byteLength > stream.FileSize()) { 604 throw DeadlyImportError("GLTF: Invalid byteLength exceeds size of actual data."); 605 } 606 607 if (baseOffset) { 608 stream.Seek(baseOffset, aiOrigin_SET); 609 } 610 611 mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>()); 612 613 if (stream.Read(mData.get(), byteLength, 1) != 1) { 614 return false; 615 } 616 return true; 617} 618 619inline 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) { 620 // Check pointer to data 621 if (pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided."); 622 623 // Check offset 624 if (pOffset > byteLength) { 625 const uint8_t val_size = 32; 626 627 char val[val_size]; 628 629 ai_snprintf(val, val_size, AI_SIZEFMT, pOffset); 630 throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region."); 631 } 632 633 // Check length 634 if ((pOffset + pEncodedData_Length) > byteLength) { 635 const uint8_t val_size = 64; 636 637 char val[val_size]; 638 639 ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length); 640 throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range."); 641 } 642 643 // Add new region 644 EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID)); 645 // And set new value for "byteLength" 646 byteLength += (pDecodedData_Length - pEncodedData_Length); 647} 648 649inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { 650 if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { 651 return; 652 } 653 654 for (SEncodedRegion *reg : EncodedRegion_List) { 655 if (reg->ID == pID) { 656 EncodedRegion_Current = reg; 657 return; 658 } 659 } 660 661 throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found."); 662} 663 664inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) { 665 666 if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) { 667 return false; 668 } 669 670 const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count; 671 uint8_t *new_data = new uint8_t[new_data_size]; 672 // Copy data which place before replacing part. 673 ::memcpy(new_data, mData.get(), pBufferData_Offset); 674 // Copy new data. 675 ::memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count); 676 // Copy data which place after replacing part. 677 ::memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset); 678 // Apply new data 679 mData.reset(new_data, std::default_delete<uint8_t[]>()); 680 byteLength = new_data_size; 681 682 return true; 683} 684 685inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) { 686 if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) { 687 return false; 688 } 689 690 const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count; 691 uint8_t *new_data = new uint8_t[new_data_size]; 692 // Copy data which place before replacing part. 693 memcpy(new_data, mData.get(), pBufferData_Offset); 694 // Copy new data. 695 memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count); 696 // Copy data which place after replacing part. 697 memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], new_data_size - (pBufferData_Offset + pReplace_Count)); 698 // Apply new data 699 mData.reset(new_data, std::default_delete<uint8_t[]>()); 700 byteLength = new_data_size; 701 702 return true; 703} 704 705inline size_t Buffer::AppendData(uint8_t *data, size_t length) { 706 const size_t offset = this->byteLength; 707 708 // Force alignment to 4 bits 709 const size_t paddedLength = (length + 3) & ~3; 710 Grow(paddedLength); 711 memcpy(mData.get() + offset, data, length); 712 memset(mData.get() + offset + length, 0, paddedLength - length); 713 return offset; 714} 715 716inline void Buffer::Grow(size_t amount) { 717 if (amount <= 0) { 718 return; 719 } 720 721 // Capacity is big enough 722 if (capacity >= byteLength + amount) { 723 byteLength += amount; 724 return; 725 } 726 727 // Just allocate data which we need 728 capacity = byteLength + amount; 729 730 uint8_t *b = new uint8_t[capacity]; 731 if (nullptr != mData) { 732 memcpy(b, mData.get(), byteLength); 733 } 734 mData.reset(b, std::default_delete<uint8_t[]>()); 735 byteLength += amount; 736} 737 738// 739// struct BufferView 740// 741inline void BufferView::Read(Value &obj, Asset &r) { 742 if (Value *bufferVal = FindUInt(obj, "buffer")) { 743 buffer = r.buffers.Retrieve(bufferVal->GetUint()); 744 } 745 746 if (!buffer) { 747 throw DeadlyImportError("GLTF: Buffer view without valid buffer."); 748 } 749 750 byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); 751 byteLength = MemberOrDefault(obj, "byteLength", size_t(0)); 752 byteStride = MemberOrDefault(obj, "byteStride", 0u); 753 754 // Check length 755 if ((byteOffset + byteLength) > buffer->byteLength) { 756 throw DeadlyImportError("GLTF: Buffer view with offset/length (", byteOffset, "/", byteLength, ") is out of range."); 757 } 758} 759 760inline uint8_t *BufferView::GetPointer(size_t accOffset) { 761 if (!buffer) { 762 return nullptr; 763 } 764 uint8_t *basePtr = buffer->GetPointer(); 765 if (!basePtr) { 766 return nullptr; 767 } 768 769 size_t offset = accOffset + byteOffset; 770 if (buffer->EncodedRegion_Current != nullptr) { 771 const size_t begin = buffer->EncodedRegion_Current->Offset; 772 const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; 773 if ((offset >= begin) && (offset < end)) { 774 return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; 775 } 776 } 777 778 return basePtr + offset; 779} 780 781// 782// struct Accessor 783// 784inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { 785 if (bytes) { 786 data.assign(bytes, bytes + numBytes); 787 } else { 788 data.resize(numBytes, 0x00); 789 } 790} 791 792inline void Accessor::Sparse::PatchData(unsigned int elementSize) { 793 uint8_t *pIndices = indices->GetPointer(indicesByteOffset); 794 const unsigned int indexSize = int(ComponentTypeSize(indicesType)); 795 uint8_t *indicesEnd = pIndices + count * indexSize; 796 797 uint8_t *pValues = values->GetPointer(valuesByteOffset); 798 while (pIndices != indicesEnd) { 799 size_t offset; 800 switch (indicesType) { 801 case ComponentType_UNSIGNED_BYTE: 802 offset = *pIndices; 803 break; 804 case ComponentType_UNSIGNED_SHORT: 805 offset = *reinterpret_cast<uint16_t *>(pIndices); 806 break; 807 case ComponentType_UNSIGNED_INT: 808 offset = *reinterpret_cast<uint32_t *>(pIndices); 809 break; 810 default: 811 // have fun with float and negative values from signed types as indices. 812 throw DeadlyImportError("Unsupported component type in index."); 813 } 814 815 offset *= elementSize; 816 817 if (offset + elementSize > data.size()) { 818 throw DeadlyImportError("Invalid sparse accessor. Byte offset for patching points outside allocated memory."); 819 } 820 821 std::memcpy(data.data() + offset, pValues, elementSize); 822 823 pValues += elementSize; 824 pIndices += indexSize; 825 } 826} 827 828inline void Accessor::Read(Value &obj, Asset &r) { 829 if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { 830 bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); 831 } 832 833 byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); 834 componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); 835 { 836 const Value *countValue = FindUInt(obj, "count"); 837 if (!countValue) { 838 throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); 839 } 840 count = countValue->GetUint(); 841 } 842 843 const char *typestr; 844 type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; 845 846 if (bufferView) { 847 // Check length 848 unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count; 849 850 // handle integer overflow 851 if (byteLength < count) { 852 throw DeadlyImportError("GLTF: Accessor with offset/count (", byteOffset, "/", count, ") is out of range."); 853 } 854 855 if ((byteOffset + byteLength) > bufferView->byteLength || (bufferView->byteOffset + byteOffset + byteLength) > bufferView->buffer->byteLength) { 856 throw DeadlyImportError("GLTF: Accessor with offset/length (", byteOffset, "/", byteLength, ") is out of range."); 857 } 858 } 859 860 if (Value *sparseValue = FindObject(obj, "sparse")) { 861 sparse.reset(new Sparse); 862 // count 863 ReadMember(*sparseValue, "count", sparse->count); 864 865 // indices 866 if (Value *indicesValue = FindObject(*sparseValue, "indices")) { 867 //indices bufferView 868 Value *indiceViewID = FindUInt(*indicesValue, "bufferView"); 869 sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint()); 870 //indices byteOffset 871 sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0)); 872 //indices componentType 873 sparse->indicesType = MemberOrDefault(*indicesValue, "componentType", ComponentType_BYTE); 874 //sparse->indices->Read(*indicesValue, r); 875 } else { 876 // indicesType 877 sparse->indicesType = MemberOrDefault(*sparseValue, "componentType", ComponentType_UNSIGNED_SHORT); 878 } 879 880 // value 881 if (Value *valuesValue = FindObject(*sparseValue, "values")) { 882 //value bufferView 883 Value *valueViewID = FindUInt(*valuesValue, "bufferView"); 884 sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint()); 885 //value byteOffset 886 sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0)); 887 //sparse->values->Read(*valuesValue, r); 888 } 889 890 891 const unsigned int elementSize = GetElementSize(); 892 const size_t dataSize = count * elementSize; 893 sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0); 894 sparse->PatchData(elementSize); 895 } 896} 897 898inline unsigned int Accessor::GetNumComponents() { 899 return AttribType::GetNumComponents(type); 900} 901 902inline unsigned int Accessor::GetBytesPerComponent() { 903 return int(ComponentTypeSize(componentType)); 904} 905 906inline unsigned int Accessor::GetElementSize() { 907 return GetNumComponents() * GetBytesPerComponent(); 908} 909 910inline uint8_t *Accessor::GetPointer() { 911 if (decodedBuffer) 912 return decodedBuffer->GetPointer(); 913 914 if (sparse) 915 return sparse->data.data(); 916 917 if (!bufferView || !bufferView->buffer) return nullptr; 918 uint8_t *basePtr = bufferView->buffer->GetPointer(); 919 if (!basePtr) return nullptr; 920 921 size_t offset = byteOffset + bufferView->byteOffset; 922 923 // Check if region is encoded. 924 if (bufferView->buffer->EncodedRegion_Current != nullptr) { 925 const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset; 926 const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length; 927 928 if ((offset >= begin) && (offset < end)) 929 return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin]; 930 } 931 932 return basePtr + offset; 933} 934 935inline size_t Accessor::GetStride() { 936 // Decoded buffer is always packed 937 if (decodedBuffer) 938 return GetElementSize(); 939 940 // Sparse and normal bufferView 941 return (bufferView && bufferView->byteStride ? bufferView->byteStride : GetElementSize()); 942} 943 944inline size_t Accessor::GetMaxByteSize() { 945 if (decodedBuffer) 946 return decodedBuffer->byteLength; 947 948 return (bufferView ? bufferView->byteLength : sparse->data.size()); 949} 950 951template <class T> 952void Accessor::ExtractData(T *&outData) { 953 uint8_t *data = GetPointer(); 954 if (!data) { 955 throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name)); 956 } 957 958 const size_t elemSize = GetElementSize(); 959 const size_t totalSize = elemSize * count; 960 961 const size_t stride = GetStride(); 962 963 const size_t targetElemSize = sizeof(T); 964 965 if (elemSize > targetElemSize) { 966 throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name)); 967 } 968 969 const size_t maxSize = GetMaxByteSize(); 970 if (count * stride > maxSize) { 971 throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); 972 } 973 974 outData = new T[count]; 975 if (stride == elemSize && targetElemSize == elemSize) { 976 memcpy(outData, data, totalSize); 977 } else { 978 for (size_t i = 0; i < count; ++i) { 979 memcpy(outData + i, data + i * stride, elemSize); 980 } 981 } 982} 983 984inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) { 985 uint8_t *buffer_ptr = bufferView->buffer->GetPointer(); 986 size_t offset = byteOffset + bufferView->byteOffset; 987 988 size_t dst_stride = GetNumComponents() * GetBytesPerComponent(); 989 990 const uint8_t *src = reinterpret_cast<const uint8_t *>(src_buffer); 991 uint8_t *dst = reinterpret_cast<uint8_t *>(buffer_ptr + offset); 992 993 ai_assert(dst + _count * dst_stride <= buffer_ptr + bufferView->buffer->byteLength); 994 CopyData(_count, src, src_stride, dst, dst_stride); 995} 996 997inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) { 998 if (!sparse) 999 return; 1000 1001 // values 1002 uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer(); 1003 size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset; 1004 size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent(); 1005 const uint8_t *value_src = reinterpret_cast<const uint8_t *>(src_data); 1006 uint8_t *value_dst = reinterpret_cast<uint8_t *>(value_buffer_ptr + value_offset); 1007 ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength); 1008 CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride); 1009} 1010 1011inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) { 1012 if (!sparse) 1013 return; 1014 1015 // indices 1016 uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer(); 1017 size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset; 1018 size_t indices_dst_stride = 1 * sizeof(unsigned short); 1019 const uint8_t *indices_src = reinterpret_cast<const uint8_t *>(src_idx); 1020 uint8_t *indices_dst = reinterpret_cast<uint8_t *>(indices_buffer_ptr + indices_offset); 1021 ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength); 1022 CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride); 1023} 1024 1025inline Accessor::Indexer::Indexer(Accessor &acc) : 1026 accessor(acc), 1027 data(acc.GetPointer()), 1028 elemSize(acc.GetElementSize()), 1029 stride(acc.GetStride()) { 1030} 1031 1032//! Accesses the i-th value as defined by the accessor 1033template <class T> 1034T Accessor::Indexer::GetValue(int i) { 1035 ai_assert(data); 1036 if (i * stride >= accessor.GetMaxByteSize()) { 1037 throw DeadlyImportError("GLTF: Invalid index ", i, ", count out of range for buffer with stride ", stride, " and size ", accessor.GetMaxByteSize(), "."); 1038 } 1039 // Ensure that the memcpy doesn't overwrite the local. 1040 const size_t sizeToCopy = std::min(elemSize, sizeof(T)); 1041 T value = T(); 1042 // Assume platform endianness matches GLTF binary data (which is little-endian). 1043 memcpy(&value, data + i * stride, sizeToCopy); 1044 return value; 1045} 1046 1047inline Image::Image() : 1048 width(0), 1049 height(0), 1050 mDataLength(0) { 1051} 1052 1053inline void Image::Read(Value &obj, Asset &r) { 1054 //basisu: no need to handle .ktx2, .basis, load as is 1055 if (!mDataLength) { 1056 Value *curUri = FindString(obj, "uri"); 1057 if (nullptr != curUri) { 1058 const char *uristr = curUri->GetString(); 1059 1060 glTFCommon::Util::DataURI dataURI; 1061 if (ParseDataURI(uristr, curUri->GetStringLength(), dataURI)) { 1062 mimeType = dataURI.mediaType; 1063 if (dataURI.base64) { 1064 uint8_t *ptr = nullptr; 1065 mDataLength = glTFCommon::Util::DecodeBase64(dataURI.data, dataURI.dataLength, ptr); 1066 mData.reset(ptr); 1067 } 1068 } else { 1069 this->uri = uristr; 1070 } 1071 } else if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { 1072 this->bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); 1073 if (Value *mtype = FindString(obj, "mimeType")) { 1074 this->mimeType = mtype->GetString(); 1075 } 1076 if (!this->bufferView || this->mimeType.empty()) { 1077 throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " does not have a URI, so it must have a valid bufferView and mimetype"); 1078 } 1079 1080 Ref<Buffer> buffer = this->bufferView->buffer; 1081 1082 this->mDataLength = this->bufferView->byteLength; 1083 // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction. 1084 1085 this->mData.reset(new uint8_t[this->mDataLength]); 1086 memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); 1087 } else { 1088 throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " should have either a URI of a bufferView and mimetype"); 1089 } 1090 } 1091} 1092 1093inline uint8_t *Image::StealData() { 1094 mDataLength = 0; 1095 return mData.release(); 1096} 1097 1098// Never take over the ownership of data whenever binary or not 1099inline void Image::SetData(uint8_t *data, size_t length, Asset &r) { 1100 Ref<Buffer> b = r.GetBodyBuffer(); 1101 if (b) { // binary file: append to body 1102 std::string bvId = r.FindUniqueID(this->id, "imgdata"); 1103 bufferView = r.bufferViews.Create(bvId); 1104 1105 bufferView->buffer = b; 1106 bufferView->byteLength = length; 1107 bufferView->byteOffset = b->AppendData(data, length); 1108 } else { // text file: will be stored as a data uri 1109 uint8_t *temp = new uint8_t[length]; 1110 memcpy(temp, data, length); 1111 this->mData.reset(temp); 1112 this->mDataLength = length; 1113 } 1114} 1115 1116inline void Sampler::Read(Value &obj, Asset & /*r*/) { 1117 SetDefaults(); 1118 1119 ReadMember(obj, "name", name); 1120 ReadMember(obj, "magFilter", magFilter); 1121 ReadMember(obj, "minFilter", minFilter); 1122 ReadMember(obj, "wrapS", wrapS); 1123 ReadMember(obj, "wrapT", wrapT); 1124} 1125 1126inline void Sampler::SetDefaults() { 1127 //only wrapping modes have defaults 1128 wrapS = SamplerWrap::Repeat; 1129 wrapT = SamplerWrap::Repeat; 1130 magFilter = SamplerMagFilter::UNSET; 1131 minFilter = SamplerMinFilter::UNSET; 1132} 1133 1134inline void Texture::Read(Value &obj, Asset &r) { 1135 if (Value *sourceVal = FindUInt(obj, "source")) { 1136 source = r.images.Retrieve(sourceVal->GetUint()); 1137 } 1138 1139 if (Value *samplerVal = FindUInt(obj, "sampler")) { 1140 sampler = r.samplers.Retrieve(samplerVal->GetUint()); 1141 } 1142} 1143 1144void Material::SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) { 1145 if (r.extensionsUsed.KHR_texture_transform) { 1146 if (Value *pKHR_texture_transform = FindExtension(*prop, "KHR_texture_transform")) { 1147 out.textureTransformSupported = true; 1148 if (Value *array = FindArray(*pKHR_texture_transform, "offset")) { 1149 out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat(); 1150 out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat(); 1151 } else { 1152 out.TextureTransformExt_t.offset[0] = 0; 1153 out.TextureTransformExt_t.offset[1] = 0; 1154 } 1155 1156 if (!ReadMember(*pKHR_texture_transform, "rotation", out.TextureTransformExt_t.rotation)) { 1157 out.TextureTransformExt_t.rotation = 0; 1158 } 1159 1160 if (Value *array = FindArray(*pKHR_texture_transform, "scale")) { 1161 out.TextureTransformExt_t.scale[0] = (*array)[0].GetFloat(); 1162 out.TextureTransformExt_t.scale[1] = (*array)[1].GetFloat(); 1163 } else { 1164 out.TextureTransformExt_t.scale[0] = 1; 1165 out.TextureTransformExt_t.scale[1] = 1; 1166 } 1167 } 1168 } 1169 1170 if (Value *indexProp = FindUInt(*prop, "index")) { 1171 out.texture = r.textures.Retrieve(indexProp->GetUint()); 1172 } 1173 1174 if (Value *texcoord = FindUInt(*prop, "texCoord")) { 1175 out.texCoord = texcoord->GetUint(); 1176 } 1177} 1178 1179inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out) { 1180 if (Value *prop = FindMember(vals, propName)) { 1181 SetTextureProperties(r, prop, out); 1182 } 1183} 1184 1185inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out) { 1186 if (Value *prop = FindMember(vals, propName)) { 1187 SetTextureProperties(r, prop, out); 1188 1189 if (Value *scale = FindNumber(*prop, "scale")) { 1190 out.scale = static_cast<float>(scale->GetDouble()); 1191 } 1192 } 1193} 1194 1195inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out) { 1196 if (Value *prop = FindMember(vals, propName)) { 1197 SetTextureProperties(r, prop, out); 1198 1199 if (Value *strength = FindNumber(*prop, "strength")) { 1200 out.strength = static_cast<float>(strength->GetDouble()); 1201 } 1202 } 1203} 1204 1205inline void Material::Read(Value &material, Asset &r) { 1206 SetDefaults(); 1207 1208 if (Value *curPbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) { 1209 ReadMember(*curPbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor); 1210 ReadTextureProperty(r, *curPbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture); 1211 ReadTextureProperty(r, *curPbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture); 1212 ReadMember(*curPbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor); 1213 ReadMember(*curPbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor); 1214 } 1215 1216 ReadTextureProperty(r, material, "normalTexture", this->normalTexture); 1217 ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture); 1218 ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture); 1219 ReadMember(material, "emissiveFactor", this->emissiveFactor); 1220 1221 ReadMember(material, "doubleSided", this->doubleSided); 1222 ReadMember(material, "alphaMode", this->alphaMode); 1223 ReadMember(material, "alphaCutoff", this->alphaCutoff); 1224 1225 if (Value *extensions = FindObject(material, "extensions")) { 1226 if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { 1227 if (Value *curPbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) { 1228 PbrSpecularGlossiness pbrSG; 1229 1230 ReadMember(*curPbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor); 1231 ReadTextureProperty(r, *curPbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture); 1232 ReadTextureProperty(r, *curPbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture); 1233 ReadMember(*curPbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor); 1234 ReadMember(*curPbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor); 1235 1236 this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG); 1237 } 1238 } 1239 1240 // Extension KHR_texture_transform is handled in ReadTextureProperty 1241 1242 if (r.extensionsUsed.KHR_materials_sheen) { 1243 if (Value *curMaterialSheen = FindObject(*extensions, "KHR_materials_sheen")) { 1244 MaterialSheen sheen; 1245 1246 ReadMember(*curMaterialSheen, "sheenColorFactor", sheen.sheenColorFactor); 1247 ReadTextureProperty(r, *curMaterialSheen, "sheenColorTexture", sheen.sheenColorTexture); 1248 ReadMember(*curMaterialSheen, "sheenRoughnessFactor", sheen.sheenRoughnessFactor); 1249 ReadTextureProperty(r, *curMaterialSheen, "sheenRoughnessTexture", sheen.sheenRoughnessTexture); 1250 1251 this->materialSheen = Nullable<MaterialSheen>(sheen); 1252 } 1253 } 1254 1255 if (r.extensionsUsed.KHR_materials_clearcoat) { 1256 if (Value *curMaterialClearcoat = FindObject(*extensions, "KHR_materials_clearcoat")) { 1257 MaterialClearcoat clearcoat; 1258 1259 ReadMember(*curMaterialClearcoat, "clearcoatFactor", clearcoat.clearcoatFactor); 1260 ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatTexture", clearcoat.clearcoatTexture); 1261 ReadMember(*curMaterialClearcoat, "clearcoatRoughnessFactor", clearcoat.clearcoatRoughnessFactor); 1262 ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatRoughnessTexture", clearcoat.clearcoatRoughnessTexture); 1263 ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatNormalTexture", clearcoat.clearcoatNormalTexture); 1264 1265 this->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat); 1266 } 1267 } 1268 1269 if (r.extensionsUsed.KHR_materials_transmission) { 1270 if (Value *curMaterialTransmission = FindObject(*extensions, "KHR_materials_transmission")) { 1271 MaterialTransmission transmission; 1272 1273 ReadMember(*curMaterialTransmission, "transmissionFactor", transmission.transmissionFactor); 1274 ReadTextureProperty(r, *curMaterialTransmission, "transmissionTexture", transmission.transmissionTexture); 1275 1276 this->materialTransmission = Nullable<MaterialTransmission>(transmission); 1277 } 1278 } 1279 1280 if (r.extensionsUsed.KHR_materials_volume) { 1281 if (Value *curMaterialVolume = FindObject(*extensions, "KHR_materials_volume")) { 1282 MaterialVolume volume; 1283 1284 ReadMember(*curMaterialVolume, "thicknessFactor", volume.thicknessFactor); 1285 ReadTextureProperty(r, *curMaterialVolume, "thicknessTexture", volume.thicknessTexture); 1286 ReadMember(*curMaterialVolume, "attenuationDistance", volume.attenuationDistance); 1287 ReadMember(*curMaterialVolume, "attenuationColor", volume.attenuationColor); 1288 1289 this->materialVolume = Nullable<MaterialVolume>(volume); 1290 } 1291 } 1292 1293 if (r.extensionsUsed.KHR_materials_ior) { 1294 if (Value *curMaterialIOR = FindObject(*extensions, "KHR_materials_ior")) { 1295 MaterialIOR ior; 1296 1297 ReadMember(*curMaterialIOR, "ior", ior.ior); 1298 1299 this->materialIOR = Nullable<MaterialIOR>(ior); 1300 } 1301 } 1302 1303 unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit"); 1304 } 1305} 1306 1307inline void Material::SetDefaults() { 1308 //pbr materials 1309 SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor); 1310 pbrMetallicRoughness.metallicFactor = 1.0f; 1311 pbrMetallicRoughness.roughnessFactor = 1.0f; 1312 1313 SetVector(emissiveFactor, defaultEmissiveFactor); 1314 alphaMode = "OPAQUE"; 1315 alphaCutoff = 0.5f; 1316 doubleSided = false; 1317 unlit = false; 1318} 1319 1320inline void PbrSpecularGlossiness::SetDefaults() { 1321 //pbrSpecularGlossiness properties 1322 SetVector(diffuseFactor, defaultDiffuseFactor); 1323 SetVector(specularFactor, defaultSpecularFactor); 1324 glossinessFactor = 1.0f; 1325} 1326 1327inline void MaterialSheen::SetDefaults() { 1328 //KHR_materials_sheen properties 1329 SetVector(sheenColorFactor, defaultSheenFactor); 1330 sheenRoughnessFactor = 0.f; 1331} 1332 1333inline void MaterialVolume::SetDefaults() { 1334 //KHR_materials_volume properties 1335 thicknessFactor = 0.f; 1336 attenuationDistance = INFINITY; 1337 SetVector(attenuationColor, defaultAttenuationColor); 1338} 1339 1340inline void MaterialIOR::SetDefaults() { 1341 //KHR_materials_ior properties 1342 ior = 1.5f; 1343} 1344 1345inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { 1346 Value *curName = FindMember(pJSON_Object, "name"); 1347 if (nullptr != curName && curName->IsString()) { 1348 name = curName->GetString(); 1349 } 1350 1351 /****************** Mesh primitives ******************/ 1352 Value *curPrimitives = FindArray(pJSON_Object, "primitives"); 1353 if (nullptr != curPrimitives) { 1354 this->primitives.resize(curPrimitives->Size()); 1355 for (unsigned int i = 0; i < curPrimitives->Size(); ++i) { 1356 Value &primitive = (*curPrimitives)[i]; 1357 1358 Primitive &prim = this->primitives[i]; 1359 prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES); 1360 1361 if (Value *indices = FindUInt(primitive, "indices")) { 1362 prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint()); 1363 } 1364 1365 if (Value *material = FindUInt(primitive, "material")) { 1366 prim.material = pAsset_Root.materials.Retrieve(material->GetUint()); 1367 } 1368 1369 if (Value *attrs = FindObject(primitive, "attributes")) { 1370 for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { 1371 if (!it->value.IsUint()) continue; 1372 const char *attr = it->name.GetString(); 1373 // Valid attribute semantics include POSITION, NORMAL, TANGENT, TEXCOORD, COLOR, JOINT, JOINTMATRIX, 1374 // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. 1375 1376 int undPos = 0; 1377 Mesh::AccessorList *vec = nullptr; 1378 if (GetAttribVector(prim, attr, vec, undPos)) { 1379 size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; 1380 if ((*vec).size() != idx) { 1381 throw DeadlyImportError("GLTF: Invalid attribute in mesh: ", name, " primitive: ", i, "attrib: ", attr, 1382 ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc."); 1383 } 1384 (*vec).resize(idx + 1); 1385 (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); 1386 } 1387 } 1388 } 1389 1390#ifdef ASSIMP_ENABLE_DRACO 1391 // KHR_draco_mesh_compression spec: Draco can only be used for glTF Triangles or Triangle Strips 1392 if (pAsset_Root.extensionsUsed.KHR_draco_mesh_compression && (prim.mode == PrimitiveMode_TRIANGLES || prim.mode == PrimitiveMode_TRIANGLE_STRIP)) { 1393 // Look for draco mesh compression extension and bufferView 1394 // Skip if any missing 1395 if (Value *dracoExt = FindExtension(primitive, "KHR_draco_mesh_compression")) { 1396 if (Value *bufView = FindUInt(*dracoExt, "bufferView")) { 1397 // Attempt to load indices and attributes using draco compression 1398 auto bufferView = pAsset_Root.bufferViews.Retrieve(bufView->GetUint()); 1399 // Attempt to perform the draco decode on the buffer data 1400 const char *bufferViewData = reinterpret_cast<const char *>(bufferView->buffer->GetPointer() + bufferView->byteOffset); 1401 draco::DecoderBuffer decoderBuffer; 1402 decoderBuffer.Init(bufferViewData, bufferView->byteLength); 1403 draco::Decoder decoder; 1404 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); 1405 if (!decodeResult.ok()) { 1406 // A corrupt Draco isn't actually fatal if the primitive data is also provided in a standard buffer, but does anyone do that? 1407 throw DeadlyImportError("GLTF: Invalid Draco mesh compression in mesh: ", name, " primitive: ", i, ": ", decodeResult.status().error_msg_string()); 1408 } 1409 1410 // Now we have a draco mesh 1411 const std::unique_ptr<draco::Mesh> &pDracoMesh = decodeResult.value(); 1412 1413 // Redirect the accessors to the decoded data 1414 1415 // Indices 1416 SetDecodedIndexBuffer_Draco(*pDracoMesh, prim); 1417 1418 // Vertex attributes 1419 if (Value *attrs = FindObject(*dracoExt, "attributes")) { 1420 for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { 1421 if (!it->value.IsUint()) continue; 1422 const char *attr = it->name.GetString(); 1423 1424 int undPos = 0; 1425 Mesh::AccessorList *vec = nullptr; 1426 if (GetAttribVector(prim, attr, vec, undPos)) { 1427 size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; 1428 if (idx >= (*vec).size()) { 1429 throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr, 1430 ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc."); 1431 } 1432 1433 if (!(*vec)[idx]) { 1434 throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr, 1435 ". All draco-encoded attributes must also define an accessor."); 1436 } 1437 1438 Accessor &attribAccessor = *(*vec)[idx]; 1439 if (attribAccessor.count == 0) 1440 throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr); 1441 1442 // Redirect this accessor to the appropriate Draco vertex attribute data 1443 const uint32_t dracoAttribId = it->value.GetUint(); 1444 SetDecodedAttributeBuffer_Draco(*pDracoMesh, dracoAttribId, attribAccessor); 1445 } 1446 } 1447 } 1448 } 1449 } 1450 } 1451#endif 1452 1453 Value *targetsArray = FindArray(primitive, "targets"); 1454 if (nullptr != targetsArray) { 1455 prim.targets.resize(targetsArray->Size()); 1456 for (unsigned int j = 0; j < targetsArray->Size(); ++j) { 1457 Value &target = (*targetsArray)[j]; 1458 if (!target.IsObject()) { 1459 continue; 1460 } 1461 for (Value::MemberIterator it = target.MemberBegin(); it != target.MemberEnd(); ++it) { 1462 if (!it->value.IsUint()) { 1463 continue; 1464 } 1465 const char *attr = it->name.GetString(); 1466 // Valid attribute semantics include POSITION, NORMAL, TANGENT 1467 int undPos = 0; 1468 Mesh::AccessorList *vec = nullptr; 1469 if (GetAttribTargetVector(prim, j, attr, vec, undPos)) { 1470 size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; 1471 if ((*vec).size() <= idx) { 1472 (*vec).resize(idx + 1); 1473 } 1474 (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); 1475 } 1476 } 1477 } 1478 } 1479 } 1480 } 1481 1482 Value *curWeights = FindArray(pJSON_Object, "weights"); 1483 if (nullptr != curWeights) { 1484 this->weights.resize(curWeights->Size()); 1485 for (unsigned int i = 0; i < curWeights->Size(); ++i) { 1486 Value &weightValue = (*curWeights)[i]; 1487 if (weightValue.IsNumber()) { 1488 this->weights[i] = weightValue.GetFloat(); 1489 } 1490 } 1491 } 1492 1493 Value *curExtras = FindObject(pJSON_Object, "extras"); 1494 if (nullptr != curExtras) { 1495 if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { 1496 this->targetNames.resize(curTargetNames->Size()); 1497 for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { 1498 Value &targetNameValue = (*curTargetNames)[i]; 1499 if (targetNameValue.IsString()) { 1500 this->targetNames[i] = targetNameValue.GetString(); 1501 } 1502 } 1503 } 1504 } 1505} 1506 1507inline void Camera::Read(Value &obj, Asset & /*r*/) { 1508 std::string type_string = std::string(MemberOrDefault(obj, "type", "perspective")); 1509 if (type_string == "orthographic") { 1510 type = Camera::Orthographic; 1511 } else { 1512 type = Camera::Perspective; 1513 } 1514 1515 const char *subobjId = (type == Camera::Orthographic) ? "orthographic" : "perspective"; 1516 1517 Value *it = FindObject(obj, subobjId); 1518 if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters"); 1519 1520 if (type == Camera::Perspective) { 1521 cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f); 1522 cameraProperties.perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f / 2.f); 1523 cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); 1524 cameraProperties.perspective.znear = MemberOrDefault(*it, "znear", 0.01f); 1525 } else { 1526 cameraProperties.ortographic.xmag = MemberOrDefault(*it, "xmag", 1.f); 1527 cameraProperties.ortographic.ymag = MemberOrDefault(*it, "ymag", 1.f); 1528 cameraProperties.ortographic.zfar = MemberOrDefault(*it, "zfar", 100.f); 1529 cameraProperties.ortographic.znear = MemberOrDefault(*it, "znear", 0.01f); 1530 } 1531} 1532 1533inline void Light::Read(Value &obj, Asset & /*r*/) { 1534#ifndef M_PI 1535 const float M_PI = 3.14159265358979323846f; 1536#endif 1537 1538 std::string type_string; 1539 ReadMember(obj, "type", type_string); 1540 if (type_string == "directional") 1541 type = Light::Directional; 1542 else if (type_string == "point") 1543 type = Light::Point; 1544 else 1545 type = Light::Spot; 1546 1547 name = MemberOrDefault(obj, "name", ""); 1548 1549 SetVector(color, vec3{ 1.0f, 1.0f, 1.0f }); 1550 ReadMember(obj, "color", color); 1551 1552 intensity = MemberOrDefault(obj, "intensity", 1.0f); 1553 1554 ReadMember(obj, "range", range); 1555 1556 if (type == Light::Spot) { 1557 Value *spot = FindObject(obj, "spot"); 1558 if (!spot) throw DeadlyImportError("GLTF: Light missing its spot parameters"); 1559 innerConeAngle = MemberOrDefault(*spot, "innerConeAngle", 0.0f); 1560 outerConeAngle = MemberOrDefault(*spot, "outerConeAngle", static_cast<float>(M_PI / 4.0f)); 1561 } 1562} 1563 1564inline void Node::Read(Value &obj, Asset &r) { 1565 if (name.empty()) { 1566 name = id; 1567 } 1568 1569 Value *curChildren = FindArray(obj, "children"); 1570 if (nullptr != curChildren) { 1571 this->children.reserve(curChildren->Size()); 1572 for (unsigned int i = 0; i < curChildren->Size(); ++i) { 1573 Value &child = (*curChildren)[i]; 1574 if (child.IsUint()) { 1575 // get/create the child node 1576 Ref<Node> chn = r.nodes.Retrieve(child.GetUint()); 1577 if (chn) { 1578 this->children.push_back(chn); 1579 } 1580 } 1581 } 1582 } 1583 1584 Value *curMatrix = FindArray(obj, "matrix"); 1585 if (nullptr != curMatrix) { 1586 ReadValue(*curMatrix, this->matrix); 1587 } else { 1588 ReadMember(obj, "translation", translation); 1589 ReadMember(obj, "scale", scale); 1590 ReadMember(obj, "rotation", rotation); 1591 } 1592 1593 Value *curMesh = FindUInt(obj, "mesh"); 1594 if (nullptr != curMesh) { 1595 unsigned int numMeshes = 1; 1596 this->meshes.reserve(numMeshes); 1597 Ref<Mesh> meshRef = r.meshes.Retrieve((*curMesh).GetUint()); 1598 if (meshRef) { 1599 this->meshes.push_back(meshRef); 1600 } 1601 } 1602 1603 // Do not retrieve a skin here, just take a reference, to avoid infinite recursion 1604 // Skins will be properly loaded later 1605 Value *curSkin = FindUInt(obj, "skin"); 1606 if (nullptr != curSkin) { 1607 this->skin = r.skins.Get(curSkin->GetUint()); 1608 } 1609 1610 Value *curCamera = FindUInt(obj, "camera"); 1611 if (nullptr != curCamera) { 1612 this->camera = r.cameras.Retrieve(curCamera->GetUint()); 1613 if (this->camera) { 1614 this->camera->id = this->id; 1615 } 1616 } 1617 1618 Value *curExtensions = FindObject(obj, "extensions"); 1619 if (nullptr != curExtensions) { 1620 if (r.extensionsUsed.KHR_lights_punctual) { 1621 if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { 1622 Value *curLight = FindUInt(*ext, "light"); 1623 if (nullptr != curLight) { 1624 this->light = r.lights.Retrieve(curLight->GetUint()); 1625 if (this->light) { 1626 this->light->id = this->id; 1627 } 1628 } 1629 } 1630 } 1631 } 1632} 1633 1634inline void Scene::Read(Value &obj, Asset &r) { 1635 if (Value *scene_name = FindString(obj, "name")) { 1636 if (scene_name->IsString()) { 1637 this->name = scene_name->GetString(); 1638 } 1639 } 1640 if (Value *array = FindArray(obj, "nodes")) { 1641 for (unsigned int i = 0; i < array->Size(); ++i) { 1642 if (!(*array)[i].IsUint()) continue; 1643 Ref<Node> node = r.nodes.Retrieve((*array)[i].GetUint()); 1644 if (node) 1645 this->nodes.push_back(node); 1646 } 1647 } 1648} 1649 1650inline void Skin::Read(Value &obj, Asset &r) { 1651 if (Value *matrices = FindUInt(obj, "inverseBindMatrices")) { 1652 inverseBindMatrices = r.accessors.Retrieve(matrices->GetUint()); 1653 } 1654 1655 if (Value *joints = FindArray(obj, "joints")) { 1656 for (unsigned i = 0; i < joints->Size(); ++i) { 1657 if (!(*joints)[i].IsUint()) continue; 1658 Ref<Node> node = r.nodes.Retrieve((*joints)[i].GetUint()); 1659 if (node) { 1660 this->jointNames.push_back(node); 1661 } 1662 } 1663 } 1664} 1665 1666inline void Animation::Read(Value &obj, Asset &r) { 1667 Value *curSamplers = FindArray(obj, "samplers"); 1668 if (nullptr != curSamplers) { 1669 for (unsigned i = 0; i < curSamplers->Size(); ++i) { 1670 Value &sampler = (*curSamplers)[i]; 1671 1672 Sampler s; 1673 if (Value *input = FindUInt(sampler, "input")) { 1674 s.input = r.accessors.Retrieve(input->GetUint()); 1675 } 1676 if (Value *output = FindUInt(sampler, "output")) { 1677 s.output = r.accessors.Retrieve(output->GetUint()); 1678 } 1679 s.interpolation = Interpolation_LINEAR; 1680 if (Value *interpolation = FindString(sampler, "interpolation")) { 1681 const std::string interp = interpolation->GetString(); 1682 if (interp == "LINEAR") { 1683 s.interpolation = Interpolation_LINEAR; 1684 } else if (interp == "STEP") { 1685 s.interpolation = Interpolation_STEP; 1686 } else if (interp == "CUBICSPLINE") { 1687 s.interpolation = Interpolation_CUBICSPLINE; 1688 } 1689 } 1690 this->samplers.push_back(s); 1691 } 1692 } 1693 1694 Value *curChannels = FindArray(obj, "channels"); 1695 if (nullptr != curChannels) { 1696 for (unsigned i = 0; i < curChannels->Size(); ++i) { 1697 Value &channel = (*curChannels)[i]; 1698 1699 Channel c; 1700 Value *curSampler = FindUInt(channel, "sampler"); 1701 if (nullptr != curSampler) { 1702 c.sampler = curSampler->GetUint(); 1703 } 1704 1705 if (Value *target = FindObject(channel, "target")) { 1706 if (Value *node = FindUInt(*target, "node")) { 1707 c.target.node = r.nodes.Retrieve(node->GetUint()); 1708 } 1709 if (Value *path = FindString(*target, "path")) { 1710 const std::string p = path->GetString(); 1711 if (p == "translation") { 1712 c.target.path = AnimationPath_TRANSLATION; 1713 } else if (p == "rotation") { 1714 c.target.path = AnimationPath_ROTATION; 1715 } else if (p == "scale") { 1716 c.target.path = AnimationPath_SCALE; 1717 } else if (p == "weights") { 1718 c.target.path = AnimationPath_WEIGHTS; 1719 } 1720 } 1721 } 1722 this->channels.push_back(c); 1723 } 1724 } 1725} 1726 1727inline void AssetMetadata::Read(Document &doc) { 1728 if (Value *obj = FindObject(doc, "asset")) { 1729 ReadMember(*obj, "copyright", copyright); 1730 ReadMember(*obj, "generator", generator); 1731 1732 if (Value *versionString = FindStringInContext(*obj, "version", "\"asset\"")) { 1733 version = versionString->GetString(); 1734 } 1735 Value *curProfile = FindObjectInContext(*obj, "profile", "\"asset\""); 1736 if (nullptr != curProfile) { 1737 ReadMember(*curProfile, "api", this->profile.api); 1738 ReadMember(*curProfile, "version", this->profile.version); 1739 } 1740 } 1741 1742 if (version.empty() || version[0] != '2') { 1743 throw DeadlyImportError("GLTF: Unsupported glTF version: ", version); 1744 } 1745} 1746 1747// 1748// Asset methods implementation 1749// 1750 1751inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData) { 1752 ASSIMP_LOG_DEBUG("Reading GLTF2 binary"); 1753 GLB_Header header; 1754 if (stream.Read(&header, sizeof(header), 1) != 1) { 1755 throw DeadlyImportError("GLTF: Unable to read the file header"); 1756 } 1757 1758 if (strncmp((char *)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) { 1759 throw DeadlyImportError("GLTF: Invalid binary glTF file"); 1760 } 1761 1762 AI_SWAP4(header.version); 1763 asset.version = ai_to_string(header.version); 1764 if (header.version != 2) { 1765 throw DeadlyImportError("GLTF: Unsupported binary glTF version"); 1766 } 1767 1768 GLB_Chunk chunk; 1769 if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { 1770 throw DeadlyImportError("GLTF: Unable to read JSON chunk"); 1771 } 1772 1773 AI_SWAP4(chunk.chunkLength); 1774 AI_SWAP4(chunk.chunkType); 1775 1776 if (chunk.chunkType != ChunkType_JSON) { 1777 throw DeadlyImportError("GLTF: JSON chunk missing"); 1778 } 1779 1780 // read the scene data, ensure null termination 1781 static_assert(std::numeric_limits<uint32_t>::max() <= std::numeric_limits<size_t>::max(), "size_t must be at least 32bits"); 1782 mSceneLength = chunk.chunkLength; // Can't be larger than 4GB (max. uint32_t) 1783 sceneData.resize(mSceneLength + 1); 1784 sceneData[mSceneLength] = '\0'; 1785 1786 if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { 1787 throw DeadlyImportError("GLTF: Could not read the file contents"); 1788 } 1789 1790 uint32_t padding = ((chunk.chunkLength + 3) & ~3) - chunk.chunkLength; 1791 if (padding > 0) { 1792 stream.Seek(padding, aiOrigin_CUR); 1793 } 1794 1795 AI_SWAP4(header.length); 1796 mBodyOffset = 12 + 8 + chunk.chunkLength + padding + 8; 1797 if (header.length >= mBodyOffset) { 1798 if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { 1799 throw DeadlyImportError("GLTF: Unable to read BIN chunk"); 1800 } 1801 1802 AI_SWAP4(chunk.chunkLength); 1803 AI_SWAP4(chunk.chunkType); 1804 1805 if (chunk.chunkType != ChunkType_BIN) { 1806 throw DeadlyImportError("GLTF: BIN chunk missing"); 1807 } 1808 1809 mBodyLength = chunk.chunkLength; 1810 } else { 1811 mBodyOffset = mBodyLength = 0; 1812 } 1813} 1814 1815inline rapidjson::Document Asset::ReadDocument(IOStream &stream, bool isBinary, std::vector<char> &sceneData) { 1816 ASSIMP_LOG_DEBUG("Loading GLTF2 asset"); 1817 1818 // is binary? then read the header 1819 if (isBinary) { 1820 SetAsBinary(); // also creates the body buffer 1821 ReadBinaryHeader(stream, sceneData); 1822 } else { 1823 mSceneLength = stream.FileSize(); 1824 mBodyLength = 0; 1825 1826 // Binary format only supports up to 4GB of JSON, use that as a maximum 1827 if (mSceneLength >= std::numeric_limits<uint32_t>::max()) { 1828 throw DeadlyImportError("GLTF: JSON size greater than 4GB"); 1829 } 1830 1831 // read the scene data, ensure null termination 1832 sceneData.resize(mSceneLength + 1); 1833 sceneData[mSceneLength] = '\0'; 1834 1835 if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { 1836 throw DeadlyImportError("GLTF: Could not read the file contents"); 1837 } 1838 } 1839 1840 // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later 1841 if (mSceneLength < 2) { 1842 throw DeadlyImportError("GLTF: No JSON file contents"); 1843 } 1844 1845 // parse the JSON document 1846 ASSIMP_LOG_DEBUG("Parsing GLTF2 JSON"); 1847 Document doc; 1848 doc.ParseInsitu(&sceneData[0]); 1849 1850 if (doc.HasParseError()) { 1851 char buffer[32]; 1852 ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset())); 1853 throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError())); 1854 } 1855 1856 if (!doc.IsObject()) { 1857 throw DeadlyImportError("GLTF: JSON document root must be a JSON object"); 1858 } 1859 1860 return doc; 1861} 1862 1863inline void Asset::Load(const std::string &pFile, bool isBinary) 1864{ 1865 mCurrentAssetDir.clear(); 1866 if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) { 1867 mCurrentAssetDir = glTFCommon::getCurrentAssetDir(pFile); 1868 } 1869 1870 shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true)); 1871 if (!stream) { 1872 throw DeadlyImportError("GLTF: Could not open file for reading"); 1873 } 1874 1875 std::vector<char> sceneData; 1876 rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData); 1877 1878 // If a schemaDocumentProvider is available, see if the glTF schema is present. 1879 // If so, use it to validate the document. 1880 if (mSchemaDocumentProvider) { 1881 if (const rapidjson::SchemaDocument *gltfSchema = mSchemaDocumentProvider->GetRemoteDocument("glTF.schema.json", 16)) { 1882 // The schemas are found here: https://github.com/KhronosGroup/glTF/tree/main/specification/2.0/schema 1883 rapidjson::SchemaValidator validator(*gltfSchema); 1884 if (!doc.Accept(validator)) { 1885 rapidjson::StringBuffer pathBuffer; 1886 validator.GetInvalidSchemaPointer().StringifyUriFragment(pathBuffer); 1887 rapidjson::StringBuffer argumentBuffer; 1888 validator.GetInvalidDocumentPointer().StringifyUriFragment(argumentBuffer); 1889 throw DeadlyImportError("GLTF: The JSON document did not satisfy the glTF2 schema. Schema keyword: ", validator.GetInvalidSchemaKeyword(), ", document path: ", pathBuffer.GetString(), ", argument: ", argumentBuffer.GetString()); 1890 } 1891 } 1892 } 1893 1894 // Fill the buffer instance for the current file embedded contents 1895 if (mBodyLength > 0) { 1896 if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) { 1897 throw DeadlyImportError("GLTF: Unable to read gltf file"); 1898 } 1899 } 1900 1901 // Load the metadata 1902 asset.Read(doc); 1903 ReadExtensionsUsed(doc); 1904 ReadExtensionsRequired(doc); 1905 1906#ifndef ASSIMP_ENABLE_DRACO 1907 // Is Draco required? 1908 if (extensionsRequired.KHR_draco_mesh_compression) { 1909 throw DeadlyImportError("GLTF: Draco mesh compression not supported."); 1910 } 1911#endif 1912 1913 // Prepare the dictionaries 1914 for (size_t i = 0; i < mDicts.size(); ++i) { 1915 mDicts[i]->AttachToDocument(doc); 1916 } 1917 1918 // Read the "scene" property, which specifies which scene to load 1919 // and recursively load everything referenced by it 1920 unsigned int sceneIndex = 0; 1921 Value *curScene = FindUInt(doc, "scene"); 1922 if (nullptr != curScene) { 1923 sceneIndex = curScene->GetUint(); 1924 } 1925 1926 if (Value *scenesArray = FindArray(doc, "scenes")) { 1927 if (sceneIndex < scenesArray->Size()) { 1928 this->scene = scenes.Retrieve(sceneIndex); 1929 } 1930 } 1931 1932 if (Value *skinsArray = FindArray(doc, "skins")) { 1933 for (unsigned int i = 0; i < skinsArray->Size(); ++i) { 1934 skins.Retrieve(i); 1935 } 1936 } 1937 1938 if (Value *animsArray = FindArray(doc, "animations")) { 1939 for (unsigned int i = 0; i < animsArray->Size(); ++i) { 1940 animations.Retrieve(i); 1941 } 1942 } 1943 1944 // Clean up 1945 for (size_t i = 0; i < mDicts.size(); ++i) { 1946 mDicts[i]->DetachFromDocument(); 1947 } 1948} 1949 1950inline bool Asset::CanRead(const std::string &pFile, bool isBinary) { 1951 try { 1952 shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true)); 1953 if (!stream) { 1954 return false; 1955 } 1956 std::vector<char> sceneData; 1957 rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData); 1958 asset.Read(doc); 1959 } catch (...) { 1960 return false; 1961 } 1962 return true; 1963} 1964 1965inline void Asset::SetAsBinary() { 1966 if (!mBodyBuffer) { 1967 mBodyBuffer = buffers.Create("binary_glTF"); 1968 mBodyBuffer->MarkAsSpecial(); 1969 } 1970} 1971 1972// As required extensions are only a concept in glTF 2.0, this is here 1973// instead of glTFCommon.h 1974#define CHECK_REQUIRED_EXT(EXT) \ 1975 if (exts.find(#EXT) != exts.end()) extensionsRequired.EXT = true; 1976 1977inline void Asset::ReadExtensionsRequired(Document &doc) { 1978 Value *extsRequired = FindArray(doc, "extensionsRequired"); 1979 if (nullptr == extsRequired) { 1980 return; 1981 } 1982 1983 std::gltf_unordered_map<std::string, bool> exts; 1984 for (unsigned int i = 0; i < extsRequired->Size(); ++i) { 1985 if ((*extsRequired)[i].IsString()) { 1986 exts[(*extsRequired)[i].GetString()] = true; 1987 } 1988 } 1989 1990 CHECK_REQUIRED_EXT(KHR_draco_mesh_compression); 1991 1992#undef CHECK_REQUIRED_EXT 1993} 1994 1995inline void Asset::ReadExtensionsUsed(Document &doc) { 1996 Value *extsUsed = FindArray(doc, "extensionsUsed"); 1997 if (!extsUsed) return; 1998 1999 std::gltf_unordered_map<std::string, bool> exts; 2000 2001 for (unsigned int i = 0; i < extsUsed->Size(); ++i) { 2002 if ((*extsUsed)[i].IsString()) { 2003 exts[(*extsUsed)[i].GetString()] = true; 2004 } 2005 } 2006 2007 CHECK_EXT(KHR_materials_pbrSpecularGlossiness); 2008 CHECK_EXT(KHR_materials_unlit); 2009 CHECK_EXT(KHR_lights_punctual); 2010 CHECK_EXT(KHR_texture_transform); 2011 CHECK_EXT(KHR_materials_sheen); 2012 CHECK_EXT(KHR_materials_clearcoat); 2013 CHECK_EXT(KHR_materials_transmission); 2014 CHECK_EXT(KHR_materials_volume); 2015 CHECK_EXT(KHR_materials_ior); 2016 CHECK_EXT(KHR_draco_mesh_compression); 2017 CHECK_EXT(KHR_texture_basisu); 2018 2019#undef CHECK_EXT 2020} 2021 2022inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool /*absolute*/) { 2023#ifdef ASSIMP_API 2024 return mIOSystem->Open(path, mode); 2025#else 2026 if (path.size() < 2) return nullptr; 2027 if (!absolute && path[1] != ':' && path[0] != '/') { // relative? 2028 path = mCurrentAssetDir + path; 2029 } 2030 FILE *f = fopen(path.c_str(), mode); 2031 return f ? new IOStream(f) : nullptr; 2032#endif 2033} 2034 2035inline std::string Asset::FindUniqueID(const std::string &str, const char *suffix) { 2036 std::string id = str; 2037 2038 if (!id.empty()) { 2039 if (mUsedIds.find(id) == mUsedIds.end()) 2040 return id; 2041 2042 id += "_"; 2043 } 2044 2045 id += suffix; 2046 2047 Asset::IdMap::iterator it = mUsedIds.find(id); 2048 if (it == mUsedIds.end()) { 2049 return id; 2050 } 2051 2052 std::vector<char> buffer; 2053 buffer.resize(id.size() + 16); 2054 int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str()); 2055 for (int i = 0; it != mUsedIds.end(); ++i) { 2056 ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i); 2057 id = buffer.data(); 2058 it = mUsedIds.find(id); 2059 } 2060 2061 return id; 2062} 2063 2064#if _MSC_VER 2065# pragma warning(pop) 2066#endif // _MSC_VER 2067 2068} // namespace glTF2 2069