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