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 14 copyright notice, this list of conditions and the 15 following disclaimer. 16 17* Redistributions in binary form must reproduce the above 18 copyright notice, this list of conditions and the 19 following disclaimer in the documentation and/or other 20 materials provided with the distribution. 21 22* Neither the name of the assimp team, nor the names of its 23 contributors may be used to endorse or promote products 24 derived from this software without specific prior 25 written 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/** @file BlenderDNA.inl 43 * @brief Blender `DNA` (file format specification embedded in 44 * blend file itself) loader. 45 */ 46#ifndef INCLUDED_AI_BLEND_DNA_INL 47#define INCLUDED_AI_BLEND_DNA_INL 48 49#include <memory> 50#include "TinyFormatter.h" 51 52namespace Assimp { 53namespace Blender { 54 55//-------------------------------------------------------------------------------- 56const Field& Structure :: operator [] (const std::string& ss) const 57{ 58 std::map<std::string, size_t>::const_iterator it = indices.find(ss); 59 if (it == indices.end()) { 60 throw Error((Formatter::format(), 61 "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`" 62 )); 63 } 64 65 return fields[(*it).second]; 66} 67 68//-------------------------------------------------------------------------------- 69const Field* Structure :: Get (const std::string& ss) const 70{ 71 std::map<std::string, size_t>::const_iterator it = indices.find(ss); 72 return it == indices.end() ? NULL : &fields[(*it).second]; 73} 74 75//-------------------------------------------------------------------------------- 76const Field& Structure :: operator [] (const size_t i) const 77{ 78 if (i >= fields.size()) { 79 throw Error((Formatter::format(), 80 "BlendDNA: There is no field with index `",i,"` in structure `",name,"`" 81 )); 82 } 83 84 return fields[i]; 85} 86 87//-------------------------------------------------------------------------------- 88template <typename T> std::shared_ptr<ElemBase> Structure :: Allocate() const 89{ 90 return std::shared_ptr<T>(new T()); 91} 92 93//-------------------------------------------------------------------------------- 94template <typename T> void Structure :: Convert( 95 std::shared_ptr<ElemBase> in, 96 const FileDatabase& db) const 97{ 98 Convert<T> (*static_cast<T*> ( in.get() ),db); 99} 100 101//-------------------------------------------------------------------------------- 102template <int error_policy, typename T, size_t M> 103void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const 104{ 105 const StreamReaderAny::pos old = db.reader->GetCurrentPos(); 106 try { 107 const Field& f = (*this)[name]; 108 const Structure& s = db.dna[f.type]; 109 110 // is the input actually an array? 111 if (!(f.flags & FieldFlag_Array)) { 112 throw Error((Formatter::format(),"Field `",name,"` of structure `", 113 this->name,"` ought to be an array of size ",M 114 )); 115 } 116 117 db.reader->IncPtr(f.offset); 118 119 // size conversions are always allowed, regardless of error_policy 120 unsigned int i = 0; 121 for(; i < std::min(f.array_sizes[0],M); ++i) { 122 s.Convert(out[i],db); 123 } 124 for(; i < M; ++i) { 125 _defaultInitializer<ErrorPolicy_Igno>()(out[i]); 126 } 127 } 128 catch (const Error& e) { 129 _defaultInitializer<error_policy>()(out,e.what()); 130 } 131 132 // and recover the previous stream position 133 db.reader->SetCurrentPos(old); 134 135#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 136 ++db.stats().fields_read; 137#endif 138} 139 140//-------------------------------------------------------------------------------- 141template <int error_policy, typename T, size_t M, size_t N> 142void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const 143{ 144 const StreamReaderAny::pos old = db.reader->GetCurrentPos(); 145 try { 146 const Field& f = (*this)[name]; 147 const Structure& s = db.dna[f.type]; 148 149 // is the input actually an array? 150 if (!(f.flags & FieldFlag_Array)) { 151 throw Error((Formatter::format(),"Field `",name,"` of structure `", 152 this->name,"` ought to be an array of size ",M,"*",N 153 )); 154 } 155 156 db.reader->IncPtr(f.offset); 157 158 // size conversions are always allowed, regardless of error_policy 159 unsigned int i = 0; 160 for(; i < std::min(f.array_sizes[0],M); ++i) { 161 unsigned int j = 0; 162 for(; j < std::min(f.array_sizes[1],N); ++j) { 163 s.Convert(out[i][j],db); 164 } 165 for(; j < N; ++j) { 166 _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]); 167 } 168 } 169 for(; i < M; ++i) { 170 _defaultInitializer<ErrorPolicy_Igno>()(out[i]); 171 } 172 } 173 catch (const Error& e) { 174 _defaultInitializer<error_policy>()(out,e.what()); 175 } 176 177 // and recover the previous stream position 178 db.reader->SetCurrentPos(old); 179 180#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 181 ++db.stats().fields_read; 182#endif 183} 184 185//-------------------------------------------------------------------------------- 186template <int error_policy, template <typename> class TOUT, typename T> 187bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db, 188 bool non_recursive /*= false*/) const 189{ 190 const StreamReaderAny::pos old = db.reader->GetCurrentPos(); 191 Pointer ptrval; 192 const Field* f; 193 try { 194 f = &(*this)[name]; 195 196 // sanity check, should never happen if the genblenddna script is right 197 if (!(f->flags & FieldFlag_Pointer)) { 198 throw Error((Formatter::format(),"Field `",name,"` of structure `", 199 this->name,"` ought to be a pointer")); 200 } 201 202 db.reader->IncPtr(f->offset); 203 Convert(ptrval,db); 204 // actually it is meaningless on which Structure the Convert is called 205 // because the `Pointer` argument triggers a special implementation. 206 } 207 catch (const Error& e) { 208 _defaultInitializer<error_policy>()(out,e.what()); 209 210 out.reset(); 211 return false; 212 } 213 214 // resolve the pointer and load the corresponding structure 215 const bool res = ResolvePointer(out,ptrval,db,*f, non_recursive); 216 217 if(!non_recursive) { 218 // and recover the previous stream position 219 db.reader->SetCurrentPos(old); 220 } 221 222#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 223 ++db.stats().fields_read; 224#endif 225 226 return res; 227} 228 229//-------------------------------------------------------------------------------- 230template <int error_policy, template <typename> class TOUT, typename T, size_t N> 231bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name, 232 const FileDatabase& db) const 233{ 234 // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr 235 const StreamReaderAny::pos old = db.reader->GetCurrentPos(); 236 Pointer ptrval[N]; 237 const Field* f; 238 try { 239 f = &(*this)[name]; 240 241 // sanity check, should never happen if the genblenddna script is right 242 if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) { 243 throw Error((Formatter::format(),"Field `",name,"` of structure `", 244 this->name,"` ought to be a pointer AND an array")); 245 } 246 247 db.reader->IncPtr(f->offset); 248 249 size_t i = 0; 250 for(; i < std::min(f->array_sizes[0],N); ++i) { 251 Convert(ptrval[i],db); 252 } 253 for(; i < N; ++i) { 254 _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]); 255 } 256 257 // actually it is meaningless on which Structure the Convert is called 258 // because the `Pointer` argument triggers a special implementation. 259 } 260 catch (const Error& e) { 261 _defaultInitializer<error_policy>()(out,e.what()); 262 for(size_t i = 0; i < N; ++i) { 263 out[i].reset(); 264 } 265 return false; 266 } 267 268 bool res = true; 269 for(size_t i = 0; i < N; ++i) { 270 // resolve the pointer and load the corresponding structure 271 res = ResolvePointer(out[i],ptrval[i],db,*f) && res; 272 } 273 274 // and recover the previous stream position 275 db.reader->SetCurrentPos(old); 276 277#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 278 ++db.stats().fields_read; 279#endif 280 return res; 281} 282 283//-------------------------------------------------------------------------------- 284template <int error_policy, typename T> 285void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const 286{ 287 const StreamReaderAny::pos old = db.reader->GetCurrentPos(); 288 try { 289 const Field& f = (*this)[name]; 290 // find the structure definition pertaining to this field 291 const Structure& s = db.dna[f.type]; 292 293 db.reader->IncPtr(f.offset); 294 s.Convert(out,db); 295 } 296 catch (const Error& e) { 297 _defaultInitializer<error_policy>()(out,e.what()); 298 } 299 300 // and recover the previous stream position 301 db.reader->SetCurrentPos(old); 302 303#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 304 ++db.stats().fields_read; 305#endif 306} 307 308 309//-------------------------------------------------------------------------------- 310template <template <typename> class TOUT, typename T> 311bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, 312 const Field& f, 313 bool non_recursive /*= false*/) const 314{ 315 out.reset(); // ensure null pointers work 316 if (!ptrval.val) { 317 return false; 318 } 319 const Structure& s = db.dna[f.type]; 320 // find the file block the pointer is pointing to 321 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); 322 323 // also determine the target type from the block header 324 // and check if it matches the type which we expect. 325 const Structure& ss = db.dna[block->dna_index]; 326 if (ss != s) { 327 throw Error((Formatter::format(),"Expected target to be of type `",s.name, 328 "` but seemingly it is a `",ss.name,"` instead" 329 )); 330 } 331 332 // try to retrieve the object from the cache 333 db.cache(out).get(s,out,ptrval); 334 if (out) { 335 return true; 336 } 337 338 // seek to this location, but save the previous stream pointer. 339 const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); 340 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); 341 // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems. 342 // I really ought to improve StreamReader to work with 64 bit indices exclusively. 343 344 // continue conversion after allocating the required storage 345 size_t num = block->size / ss.size; 346 T* o = _allocate(out,num); 347 348 // cache the object before we convert it to avoid cyclic recursion. 349 db.cache(out).set(s,out,ptrval); 350 351 // if the non_recursive flag is set, we don't do anything but leave 352 // the cursor at the correct position to resolve the object. 353 if (!non_recursive) { 354 for (size_t i = 0; i < num; ++i,++o) { 355 s.Convert(*o,db); 356 } 357 358 db.reader->SetCurrentPos(pold); 359 } 360 361#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 362 if(out) { 363 ++db.stats().pointers_resolved; 364 } 365#endif 366 return false; 367} 368 369 370//-------------------------------------------------------------------------------- 371inline bool Structure :: ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval, 372 const FileDatabase& db, 373 const Field&, 374 bool) const 375{ 376 // Currently used exclusively by PackedFile::data to represent 377 // a simple offset into the mapped BLEND file. 378 out.reset(); 379 if (!ptrval.val) { 380 return false; 381 } 382 383 // find the file block the pointer is pointing to 384 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); 385 386 out = std::shared_ptr< FileOffset > (new FileOffset()); 387 out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) ); 388 return false; 389} 390 391//-------------------------------------------------------------------------------- 392template <template <typename> class TOUT, typename T> 393bool Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, 394 const FileDatabase& db, 395 const Field& f, 396 bool) const 397{ 398 // This is a function overload, not a template specialization. According to 399 // the partial ordering rules, it should be selected by the compiler 400 // for array-of-pointer inputs, i.e. Object::mats. 401 402 out.reset(); 403 if (!ptrval.val) { 404 return false; 405 } 406 407 // find the file block the pointer is pointing to 408 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); 409 const size_t num = block->size / (db.i64bit?8:4); 410 411 // keep the old stream position 412 const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); 413 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); 414 415 bool res = false; 416 // allocate raw storage for the array 417 out.resize(num); 418 for (size_t i = 0; i< num; ++i) { 419 Pointer val; 420 Convert(val,db); 421 422 // and resolve the pointees 423 res = ResolvePointer(out[i],val,db,f) && res; 424 } 425 426 db.reader->SetCurrentPos(pold); 427 return res; 428} 429 430//-------------------------------------------------------------------------------- 431template <> bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out, 432 const Pointer & ptrval, 433 const FileDatabase& db, 434 const Field&, 435 bool 436) const 437{ 438 // Special case when the data type needs to be determined at runtime. 439 // Less secure than in the `strongly-typed` case. 440 441 out.reset(); 442 if (!ptrval.val) { 443 return false; 444 } 445 446 // find the file block the pointer is pointing to 447 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); 448 449 // determine the target type from the block header 450 const Structure& s = db.dna[block->dna_index]; 451 452 // try to retrieve the object from the cache 453 db.cache(out).get(s,out,ptrval); 454 if (out) { 455 return true; 456 } 457 458 // seek to this location, but save the previous stream pointer. 459 const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); 460 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); 461 // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems. 462 // I really ought to improve StreamReader to work with 64 bit indices exclusively. 463 464 // continue conversion after allocating the required storage 465 DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db); 466 if (!builders.first) { 467 // this might happen if DNA::RegisterConverters hasn't been called so far 468 // or if the target type is not contained in `our` DNA. 469 out.reset(); 470 DefaultLogger::get()->warn((Formatter::format(), 471 "Failed to find a converter for the `",s.name,"` structure" 472 )); 473 return false; 474 } 475 476 // allocate the object hull 477 out = (s.*builders.first)(); 478 479 // cache the object immediately to prevent infinite recursion in a 480 // circular list with a single element (i.e. a self-referencing element). 481 db.cache(out).set(s,out,ptrval); 482 483 // and do the actual conversion 484 (s.*builders.second)(out,db); 485 db.reader->SetCurrentPos(pold); 486 487 // store a pointer to the name string of the actual type 488 // in the object itself. This allows the conversion code 489 // to perform additional type checking. 490 out->dna_type = s.name.c_str(); 491 492 493#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 494 ++db.stats().pointers_resolved; 495#endif 496 return false; 497} 498 499//-------------------------------------------------------------------------------- 500const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const 501{ 502 // the file blocks appear in list sorted by 503 // with ascending base addresses so we can run a 504 // binary search to locate the pointee quickly. 505 506 // NOTE: Blender seems to distinguish between side-by-side 507 // data (stored in the same data block) and far pointers, 508 // which are only used for structures starting with an ID. 509 // We don't need to make this distinction, our algorithm 510 // works regardless where the data is stored. 511 vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval); 512 if (it == db.entries.end()) { 513 // this is crucial, pointers may not be invalid. 514 // this is either a corrupted file or an attempted attack. 515 throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x", 516 std::hex,ptrval.val,", no file block falls into this address range" 517 )); 518 } 519 if (ptrval.val >= (*it).address.val + (*it).size) { 520 throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x", 521 std::hex,ptrval.val,", nearest file block starting at 0x", 522 (*it).address.val," ends at 0x", 523 (*it).address.val + (*it).size 524 )); 525 } 526 return &*it; 527} 528 529// ------------------------------------------------------------------------------------------------ 530// NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has 531// caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask. 532 533template <typename T> struct signless; 534template <> struct signless<char> {typedef unsigned char type;}; 535template <> struct signless<short> {typedef unsigned short type;}; 536template <> struct signless<int> {typedef unsigned int type;}; 537template <> struct signless<unsigned char> { typedef unsigned char type; }; 538template <typename T> 539struct static_cast_silent { 540 template <typename V> 541 T operator()(V in) { 542 return static_cast<T>(in & static_cast<typename signless<T>::type>(-1)); 543 } 544}; 545 546template <> struct static_cast_silent<float> { 547 template <typename V> float operator()(V in) { 548 return static_cast<float> (in); 549 } 550}; 551 552template <> struct static_cast_silent<double> { 553 template <typename V> double operator()(V in) { 554 return static_cast<double>(in); 555 } 556}; 557 558// ------------------------------------------------------------------------------------------------ 559template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db) 560{ 561 if (in.name == "int") { 562 out = static_cast_silent<T>()(db.reader->GetU4()); 563 } 564 else if (in.name == "short") { 565 out = static_cast_silent<T>()(db.reader->GetU2()); 566 } 567 else if (in.name == "char") { 568 out = static_cast_silent<T>()(db.reader->GetU1()); 569 } 570 else if (in.name == "float") { 571 out = static_cast<T>(db.reader->GetF4()); 572 } 573 else if (in.name == "double") { 574 out = static_cast<T>(db.reader->GetF8()); 575 } 576 else { 577 throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name); 578 } 579} 580 581// ------------------------------------------------------------------------------------------------ 582template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const 583{ 584 ConvertDispatcher(dest,*this,db); 585} 586 587// ------------------------------------------------------------------------------------------------ 588template<> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const 589{ 590 // automatic rescaling from short to float and vice versa (seems to be used by normals) 591 if (name == "float") { 592 float f = db.reader->GetF4(); 593 if ( f > 1.0f ) 594 f = 1.0f; 595 dest = static_cast<short>( f * 32767.f); 596 //db.reader->IncPtr(-4); 597 return; 598 } 599 else if (name == "double") { 600 dest = static_cast<short>(db.reader->GetF8() * 32767.); 601 //db.reader->IncPtr(-8); 602 return; 603 } 604 ConvertDispatcher(dest,*this,db); 605} 606 607// ------------------------------------------------------------------------------------------------ 608template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const 609{ 610 // automatic rescaling from char to float and vice versa (seems useful for RGB colors) 611 if (name == "float") { 612 dest = static_cast<char>(db.reader->GetF4() * 255.f); 613 return; 614 } 615 else if (name == "double") { 616 dest = static_cast<char>(db.reader->GetF8() * 255.f); 617 return; 618 } 619 ConvertDispatcher(dest,*this,db); 620} 621 622// ------------------------------------------------------------------------------------------------ 623template <> inline void Structure::Convert<unsigned char>(unsigned char& dest, const FileDatabase& db) const 624{ 625 // automatic rescaling from char to float and vice versa (seems useful for RGB colors) 626 if (name == "float") { 627 dest = static_cast<unsigned char>(db.reader->GetF4() * 255.f); 628 return; 629 } 630 else if (name == "double") { 631 dest = static_cast<unsigned char>(db.reader->GetF8() * 255.f); 632 return; 633 } 634 ConvertDispatcher(dest, *this, db); 635} 636 637 638// ------------------------------------------------------------------------------------------------ 639template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const 640{ 641 // automatic rescaling from char to float and vice versa (seems useful for RGB colors) 642 if (name == "char") { 643 dest = db.reader->GetI1() / 255.f; 644 return; 645 } 646 // automatic rescaling from short to float and vice versa (used by normals) 647 else if (name == "short") { 648 dest = db.reader->GetI2() / 32767.f; 649 return; 650 } 651 ConvertDispatcher(dest,*this,db); 652} 653 654// ------------------------------------------------------------------------------------------------ 655template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const 656{ 657 if (name == "char") { 658 dest = db.reader->GetI1() / 255.; 659 return; 660 } 661 else if (name == "short") { 662 dest = db.reader->GetI2() / 32767.; 663 return; 664 } 665 ConvertDispatcher(dest,*this,db); 666} 667 668// ------------------------------------------------------------------------------------------------ 669template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const 670{ 671 if (db.i64bit) { 672 dest.val = db.reader->GetU8(); 673 //db.reader->IncPtr(-8); 674 return; 675 } 676 dest.val = db.reader->GetU4(); 677 //db.reader->IncPtr(-4); 678} 679 680//-------------------------------------------------------------------------------- 681const Structure& DNA :: operator [] (const std::string& ss) const 682{ 683 std::map<std::string, size_t>::const_iterator it = indices.find(ss); 684 if (it == indices.end()) { 685 throw Error((Formatter::format(), 686 "BlendDNA: Did not find a structure named `",ss,"`" 687 )); 688 } 689 690 return structures[(*it).second]; 691} 692 693//-------------------------------------------------------------------------------- 694const Structure* DNA :: Get (const std::string& ss) const 695{ 696 std::map<std::string, size_t>::const_iterator it = indices.find(ss); 697 return it == indices.end() ? NULL : &structures[(*it).second]; 698} 699 700//-------------------------------------------------------------------------------- 701const Structure& DNA :: operator [] (const size_t i) const 702{ 703 if (i >= structures.size()) { 704 throw Error((Formatter::format(), 705 "BlendDNA: There is no structure with index `",i,"`" 706 )); 707 } 708 709 return structures[i]; 710} 711 712//-------------------------------------------------------------------------------- 713template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get ( 714 const Structure& s, 715 TOUT<T>& out, 716 const Pointer& ptr 717) const { 718 719 if(s.cache_idx == static_cast<size_t>(-1)) { 720 s.cache_idx = db.next_cache_idx++; 721 caches.resize(db.next_cache_idx); 722 return; 723 } 724 725 typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr); 726 if (it != caches[s.cache_idx].end()) { 727 out = std::static_pointer_cast<T>( (*it).second ); 728 729#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 730 ++db.stats().cache_hits; 731#endif 732 } 733 // otherwise, out remains untouched 734} 735 736 737//-------------------------------------------------------------------------------- 738template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set ( 739 const Structure& s, 740 const TOUT<T>& out, 741 const Pointer& ptr 742) { 743 if(s.cache_idx == static_cast<size_t>(-1)) { 744 s.cache_idx = db.next_cache_idx++; 745 caches.resize(db.next_cache_idx); 746 } 747 caches[s.cache_idx][ptr] = std::static_pointer_cast<ElemBase>( out ); 748 749#ifndef ASSIMP_BUILD_BLENDER_NO_STATS 750 ++db.stats().cached_objects; 751#endif 752} 753 754}} 755#endif 756