vrshoot

view libs/assimp/BlenderDNA.inl @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file BlenderDNA.inl
42 * @brief Blender `DNA` (file format specification embedded in
43 * blend file itself) loader.
44 */
45 #ifndef INCLUDED_AI_BLEND_DNA_INL
46 #define INCLUDED_AI_BLEND_DNA_INL
48 namespace Assimp {
49 namespace Blender {
51 //--------------------------------------------------------------------------------
52 const Field& Structure :: operator [] (const std::string& ss) const
53 {
54 std::map<std::string, size_t>::const_iterator it = indices.find(ss);
55 if (it == indices.end()) {
56 throw Error((Formatter::format(),
57 "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
58 ));
59 }
61 return fields[(*it).second];
62 }
64 //--------------------------------------------------------------------------------
65 const Field* Structure :: Get (const std::string& ss) const
66 {
67 std::map<std::string, size_t>::const_iterator it = indices.find(ss);
68 return it == indices.end() ? NULL : &fields[(*it).second];
69 }
71 //--------------------------------------------------------------------------------
72 const Field& Structure :: operator [] (const size_t i) const
73 {
74 if (i >= fields.size()) {
75 throw Error((Formatter::format(),
76 "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
77 ));
78 }
80 return fields[i];
81 }
83 //--------------------------------------------------------------------------------
84 template <typename T> boost::shared_ptr<ElemBase> Structure :: Allocate() const
85 {
86 return boost::shared_ptr<T>(new T());
87 }
89 //--------------------------------------------------------------------------------
90 template <typename T> void Structure :: Convert(
91 boost::shared_ptr<ElemBase> in,
92 const FileDatabase& db) const
93 {
94 Convert<T> (*static_cast<T*> ( in.get() ),db);
95 }
97 //--------------------------------------------------------------------------------
98 template <int error_policy, typename T, size_t M>
99 void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const
100 {
101 const StreamReaderAny::pos old = db.reader->GetCurrentPos();
102 try {
103 const Field& f = (*this)[name];
104 const Structure& s = db.dna[f.type];
106 // is the input actually an array?
107 if (!(f.flags & FieldFlag_Array)) {
108 throw Error((Formatter::format(),"Field `",name,"` of structure `",
109 this->name,"` ought to be an array of size ",M
110 ));
111 }
113 db.reader->IncPtr(f.offset);
115 // size conversions are always allowed, regardless of error_policy
116 unsigned int i = 0;
117 for(; i < std::min(f.array_sizes[0],M); ++i) {
118 s.Convert(out[i],db);
119 }
120 for(; i < M; ++i) {
121 _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
122 }
123 }
124 catch (const Error& e) {
125 _defaultInitializer<error_policy>()(out,e.what());
126 }
128 // and recover the previous stream position
129 db.reader->SetCurrentPos(old);
131 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
132 ++db.stats().fields_read;
133 #endif
134 }
136 //--------------------------------------------------------------------------------
137 template <int error_policy, typename T, size_t M, size_t N>
138 void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const
139 {
140 const StreamReaderAny::pos old = db.reader->GetCurrentPos();
141 try {
142 const Field& f = (*this)[name];
143 const Structure& s = db.dna[f.type];
145 // is the input actually an array?
146 if (!(f.flags & FieldFlag_Array)) {
147 throw Error((Formatter::format(),"Field `",name,"` of structure `",
148 this->name,"` ought to be an array of size ",M,"*",N
149 ));
150 }
152 db.reader->IncPtr(f.offset);
154 // size conversions are always allowed, regardless of error_policy
155 unsigned int i = 0;
156 for(; i < std::min(f.array_sizes[0],M); ++i) {
157 unsigned int j = 0;
158 for(; j < std::min(f.array_sizes[1],N); ++j) {
159 s.Convert(out[i][j],db);
160 }
161 for(; j < N; ++j) {
162 _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]);
163 }
164 }
165 for(; i < M; ++i) {
166 _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
167 }
168 }
169 catch (const Error& e) {
170 _defaultInitializer<error_policy>()(out,e.what());
171 }
173 // and recover the previous stream position
174 db.reader->SetCurrentPos(old);
176 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
177 ++db.stats().fields_read;
178 #endif
179 }
181 //--------------------------------------------------------------------------------
182 template <int error_policy, template <typename> class TOUT, typename T>
183 void Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db) const
184 {
185 const StreamReaderAny::pos old = db.reader->GetCurrentPos();
186 Pointer ptrval;
187 const Field* f;
188 try {
189 f = &(*this)[name];
191 // sanity check, should never happen if the genblenddna script is right
192 if (!(f->flags & FieldFlag_Pointer)) {
193 throw Error((Formatter::format(),"Field `",name,"` of structure `",
194 this->name,"` ought to be a pointer"));
195 }
197 db.reader->IncPtr(f->offset);
198 Convert(ptrval,db);
199 // actually it is meaningless on which Structure the Convert is called
200 // because the `Pointer` argument triggers a special implementation.
201 }
202 catch (const Error& e) {
203 _defaultInitializer<error_policy>()(out,e.what());
205 out.reset();
206 return;
207 }
209 // resolve the pointer and load the corresponding structure
210 ResolvePointer(out,ptrval,db,*f);
212 // and recover the previous stream position
213 db.reader->SetCurrentPos(old);
215 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
216 ++db.stats().fields_read;
217 #endif
218 }
220 //--------------------------------------------------------------------------------
221 template <int error_policy, template <typename> class TOUT, typename T, size_t N>
222 void Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
223 const FileDatabase& db) const
224 {
225 // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr
226 const StreamReaderAny::pos old = db.reader->GetCurrentPos();
227 Pointer ptrval[N];
228 const Field* f;
229 try {
230 f = &(*this)[name];
232 // sanity check, should never happen if the genblenddna script is right
233 if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
234 throw Error((Formatter::format(),"Field `",name,"` of structure `",
235 this->name,"` ought to be a pointer AND an array"));
236 }
238 db.reader->IncPtr(f->offset);
240 size_t i = 0;
241 for(; i < std::min(f->array_sizes[0],N); ++i) {
242 Convert(ptrval[i],db);
243 }
244 for(; i < N; ++i) {
245 _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]);
246 }
248 // actually it is meaningless on which Structure the Convert is called
249 // because the `Pointer` argument triggers a special implementation.
250 }
251 catch (const Error& e) {
252 _defaultInitializer<error_policy>()(out,e.what());
253 for(size_t i = 0; i < N; ++i) {
254 out[i].reset();
255 }
256 return;
257 }
258 for(size_t i = 0; i < N; ++i) {
259 // resolve the pointer and load the corresponding structure
260 ResolvePointer(out[i],ptrval[i],db,*f);
261 }
263 // and recover the previous stream position
264 db.reader->SetCurrentPos(old);
266 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
267 ++db.stats().fields_read;
268 #endif
269 }
271 //--------------------------------------------------------------------------------
272 template <int error_policy, typename T>
273 void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const
274 {
275 const StreamReaderAny::pos old = db.reader->GetCurrentPos();
276 try {
277 const Field& f = (*this)[name];
278 // find the structure definition pertaining to this field
279 const Structure& s = db.dna[f.type];
281 db.reader->IncPtr(f.offset);
282 s.Convert(out,db);
283 }
284 catch (const Error& e) {
285 _defaultInitializer<error_policy>()(out,e.what());
286 }
288 // and recover the previous stream position
289 db.reader->SetCurrentPos(old);
291 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
292 ++db.stats().fields_read;
293 #endif
294 }
297 //--------------------------------------------------------------------------------
298 template <template <typename> class TOUT, typename T>
299 void Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
300 {
301 out.reset();
302 if (!ptrval.val) {
303 return;
304 }
305 const Structure& s = db.dna[f.type];
306 // find the file block the pointer is pointing to
307 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
309 // also determine the target type from the block header
310 // and check if it matches the type which we expect.
311 const Structure& ss = db.dna[block->dna_index];
312 if (ss != s) {
313 throw Error((Formatter::format(),"Expected target to be of type `",s.name,
314 "` but seemingly it is a `",ss.name,"` instead"
315 ));
316 }
318 // try to retrieve the object from the cache
319 db.cache(out).get(s,out,ptrval);
320 if (out) {
321 return;
322 }
324 // seek to this location, but save the previous stream pointer.
325 const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
326 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
327 // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
328 // I really ought to improve StreamReader to work with 64 bit indices exclusively.
330 // continue conversion after allocating the required storage
331 size_t num = block->size / ss.size;
332 T* o = _allocate(out,num);
334 // cache the object before we convert it to avoid cyclic recursion.
335 db.cache(out).set(s,out,ptrval);
337 for (size_t i = 0; i < num; ++i,++o) {
338 s.Convert(*o,db);
339 }
341 db.reader->SetCurrentPos(pold);
343 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
344 if(out) {
345 ++db.stats().pointers_resolved;
346 }
347 #endif
348 }
350 //--------------------------------------------------------------------------------
351 inline void Structure :: ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, const FileDatabase& db, const Field& /*f*/) const
352 {
353 // Currently used exclusively by PackedFile::data to represent
354 // a simple offset into the mapped BLEND file.
355 out.reset();
356 if (!ptrval.val) {
357 return;
358 }
360 // find the file block the pointer is pointing to
361 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
363 out = boost::shared_ptr< FileOffset > (new FileOffset());
364 out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) );
365 }
367 //--------------------------------------------------------------------------------
368 template <template <typename> class TOUT, typename T>
369 void Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
370 {
371 // This is a function overload, not a template specialization. According to
372 // the partial ordering rules, it should be selected by the compiler
373 // for array-of-pointer inputs, i.e. Object::mats.
375 out.reset();
376 if (!ptrval.val) {
377 return;
378 }
380 // find the file block the pointer is pointing to
381 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
382 const size_t num = block->size / (db.i64bit?8:4);
384 // keep the old stream position
385 const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
386 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
388 // allocate raw storage for the array
389 out.resize(num);
390 for (size_t i = 0; i< num; ++i) {
391 Pointer val;
392 Convert(val,db);
394 // and resolve the pointees
395 ResolvePointer(out[i],val,db,f);
396 }
398 db.reader->SetCurrentPos(pold);
399 }
401 //--------------------------------------------------------------------------------
402 template <> void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out,
403 const Pointer & ptrval,
404 const FileDatabase& db,
405 const Field& /*f*/
406 ) const
407 {
408 // Special case when the data type needs to be determined at runtime.
409 // Less secure than in the `strongly-typed` case.
411 out.reset();
412 if (!ptrval.val) {
413 return;
414 }
416 // find the file block the pointer is pointing to
417 const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
419 // determine the target type from the block header
420 const Structure& s = db.dna[block->dna_index];
422 // try to retrieve the object from the cache
423 db.cache(out).get(s,out,ptrval);
424 if (out) {
425 return;
426 }
428 // seek to this location, but save the previous stream pointer.
429 const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
430 db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
431 // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
432 // I really ought to improve StreamReader to work with 64 bit indices exclusively.
434 // continue conversion after allocating the required storage
435 DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db);
436 if (!builders.first) {
437 // this might happen if DNA::RegisterConverters hasn't been called so far
438 // or if the target type is not contained in `our` DNA.
439 out.reset();
440 DefaultLogger::get()->warn((Formatter::format(),
441 "Failed to find a converter for the `",s.name,"` structure"
442 ));
443 return;
444 }
446 // allocate the object hull
447 out = (s.*builders.first)();
449 // cache the object immediately to prevent infinite recursion in a
450 // circular list with a single element (i.e. a self-referencing element).
451 db.cache(out).set(s,out,ptrval);
453 // and do the actual conversion
454 (s.*builders.second)(out,db);
455 db.reader->SetCurrentPos(pold);
457 // store a pointer to the name string of the actual type
458 // in the object itself. This allows the conversion code
459 // to perform additional type checking.
460 out->dna_type = s.name.c_str();
464 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
465 ++db.stats().pointers_resolved;
466 #endif
467 }
469 //--------------------------------------------------------------------------------
470 const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const
471 {
472 // the file blocks appear in list sorted by
473 // with ascending base addresses so we can run a
474 // binary search to locate the pointee quickly.
476 // NOTE: Blender seems to distinguish between side-by-side
477 // data (stored in the same data block) and far pointers,
478 // which are only used for structures starting with an ID.
479 // We don't need to make this distinction, our algorithm
480 // works regardless where the data is stored.
481 vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval);
482 if (it == db.entries.end()) {
483 // this is crucial, pointers may not be invalid.
484 // this is either a corrupted file or an attempted attack.
485 throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
486 std::hex,ptrval.val,", no file block falls into this address range"
487 ));
488 }
489 if (ptrval.val >= (*it).address.val + (*it).size) {
490 throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
491 std::hex,ptrval.val,", nearest file block starting at 0x",
492 (*it).address.val," ends at 0x",
493 (*it).address.val + (*it).size
494 ));
495 }
496 return &*it;
497 }
499 // ------------------------------------------------------------------------------------------------
500 // NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has
501 // caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask.
503 template <typename T> struct signless;
504 template <> struct signless<char> {typedef unsigned char type;};
505 template <> struct signless<short> {typedef unsigned short type;};
506 template <> struct signless<int> {typedef unsigned int type;};
508 template <typename T>
509 struct static_cast_silent {
510 template <typename V>
511 T operator()(V in) {
512 return static_cast<T>(in & static_cast<typename signless<T>::type>(-1));
513 }
514 };
516 template <> struct static_cast_silent<float> {
517 template <typename V> float operator()(V in) {
518 return static_cast<float> (in);
519 }
520 };
522 template <> struct static_cast_silent<double> {
523 template <typename V> double operator()(V in) {
524 return static_cast<double>(in);
525 }
526 };
528 // ------------------------------------------------------------------------------------------------
529 template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db)
530 {
531 if (in.name == "int") {
532 out = static_cast_silent<T>()(db.reader->GetU4());
533 }
534 else if (in.name == "short") {
535 out = static_cast_silent<T>()(db.reader->GetU2());
536 }
537 else if (in.name == "char") {
538 out = static_cast_silent<T>()(db.reader->GetU1());
539 }
540 else if (in.name == "float") {
541 out = static_cast<T>(db.reader->GetF4());
542 }
543 else if (in.name == "double") {
544 out = static_cast<T>(db.reader->GetF8());
545 }
546 else {
547 throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
548 }
549 }
551 // ------------------------------------------------------------------------------------------------
552 template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const
553 {
554 ConvertDispatcher(dest,*this,db);
555 }
557 // ------------------------------------------------------------------------------------------------
558 template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const
559 {
560 // automatic rescaling from short to float and vice versa (seems to be used by normals)
561 if (name == "float") {
562 dest = static_cast<short>(db.reader->GetF4() * 32767.f);
563 //db.reader->IncPtr(-4);
564 return;
565 }
566 else if (name == "double") {
567 dest = static_cast<short>(db.reader->GetF8() * 32767.);
568 //db.reader->IncPtr(-8);
569 return;
570 }
571 ConvertDispatcher(dest,*this,db);
572 }
574 // ------------------------------------------------------------------------------------------------
575 template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const
576 {
577 // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
578 if (name == "float") {
579 dest = static_cast<char>(db.reader->GetF4() * 255.f);
580 return;
581 }
582 else if (name == "double") {
583 dest = static_cast<char>(db.reader->GetF8() * 255.f);
584 return;
585 }
586 ConvertDispatcher(dest,*this,db);
587 }
589 // ------------------------------------------------------------------------------------------------
590 template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const
591 {
592 // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
593 if (name == "char") {
594 dest = db.reader->GetI1() / 255.f;
595 return;
596 }
597 // automatic rescaling from short to float and vice versa (used by normals)
598 else if (name == "short") {
599 dest = db.reader->GetI2() / 32767.f;
600 return;
601 }
602 ConvertDispatcher(dest,*this,db);
603 }
605 // ------------------------------------------------------------------------------------------------
606 template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const
607 {
608 if (name == "char") {
609 dest = db.reader->GetI1() / 255.;
610 return;
611 }
612 else if (name == "short") {
613 dest = db.reader->GetI2() / 32767.;
614 return;
615 }
616 ConvertDispatcher(dest,*this,db);
617 }
619 // ------------------------------------------------------------------------------------------------
620 template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const
621 {
622 if (db.i64bit) {
623 dest.val = db.reader->GetU8();
624 //db.reader->IncPtr(-8);
625 return;
626 }
627 dest.val = db.reader->GetU4();
628 //db.reader->IncPtr(-4);
629 }
631 //--------------------------------------------------------------------------------
632 const Structure& DNA :: operator [] (const std::string& ss) const
633 {
634 std::map<std::string, size_t>::const_iterator it = indices.find(ss);
635 if (it == indices.end()) {
636 throw Error((Formatter::format(),
637 "BlendDNA: Did not find a structure named `",ss,"`"
638 ));
639 }
641 return structures[(*it).second];
642 }
644 //--------------------------------------------------------------------------------
645 const Structure* DNA :: Get (const std::string& ss) const
646 {
647 std::map<std::string, size_t>::const_iterator it = indices.find(ss);
648 return it == indices.end() ? NULL : &structures[(*it).second];
649 }
651 //--------------------------------------------------------------------------------
652 const Structure& DNA :: operator [] (const size_t i) const
653 {
654 if (i >= structures.size()) {
655 throw Error((Formatter::format(),
656 "BlendDNA: There is no structure with index `",i,"`"
657 ));
658 }
660 return structures[i];
661 }
663 //--------------------------------------------------------------------------------
664 template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get (
665 const Structure& s,
666 TOUT<T>& out,
667 const Pointer& ptr
668 ) const {
670 if(s.cache_idx == static_cast<size_t>(-1)) {
671 s.cache_idx = db.next_cache_idx++;
672 caches.resize(db.next_cache_idx);
673 return;
674 }
676 typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr);
677 if (it != caches[s.cache_idx].end()) {
678 out = boost::static_pointer_cast<T>( (*it).second );
680 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
681 ++db.stats().cache_hits;
682 #endif
683 }
684 // otherwise, out remains untouched
685 }
688 //--------------------------------------------------------------------------------
689 template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set (
690 const Structure& s,
691 const TOUT<T>& out,
692 const Pointer& ptr
693 ) {
694 if(s.cache_idx == static_cast<size_t>(-1)) {
695 s.cache_idx = db.next_cache_idx++;
696 caches.resize(db.next_cache_idx);
697 }
698 caches[s.cache_idx][ptr] = boost::static_pointer_cast<ElemBase>( out );
700 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
701 ++db.stats().cached_objects;
702 #endif
703 }
705 }}
706 #endif