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