1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2016, assimp team
6 All rights reserved.
7 
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:
11 
12 * Redistributions of source code must retain the above
13   copyright notice, this list of conditions and the
14   following disclaimer.
15 
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.
20 
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.
25 
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.
37 
38 ----------------------------------------------------------------------
39 */
40 
41 /** @file  BlenderDNA.h
42  *  @brief Blender `DNA` (file format specification embedded in
43  *    blend file itself) loader.
44  */
45 #ifndef INCLUDED_AI_BLEND_DNA_H
46 #define INCLUDED_AI_BLEND_DNA_H
47 
48 #include "BaseImporter.h"
49 #include "TinyFormatter.h"
50 #include "StreamReader.h"
51 #include <assimp/DefaultLogger.hpp>
52 #include <stdint.h>
53 #include <memory>
54 
55 
56 // enable verbose log output. really verbose, so be careful.
57 #ifdef ASSIMP_BUILD_DEBUG
58 #   define ASSIMP_BUILD_BLENDER_DEBUG
59 #endif
60 
61 // #define ASSIMP_BUILD_BLENDER_NO_STATS
62 
63 namespace Assimp    {
64     template <bool,bool> class StreamReader;
65     typedef StreamReader<true,true> StreamReaderAny;
66 
67     namespace Blender {
68         class  FileDatabase;
69         struct FileBlockHead;
70 
71         template <template <typename> class TOUT>
72         class ObjectCache;
73 
74 // -------------------------------------------------------------------------------
75 /** Exception class used by the blender loader to selectively catch exceptions
76  *  thrown in its own code (DeadlyImportErrors thrown in general utility
77  *  functions are untouched then). If such an exception is not caught by
78  *  the loader itself, it will still be caught by Assimp due to its
79  *  ancestry. */
80 // -------------------------------------------------------------------------------
81 struct Error : DeadlyImportError
82 {
ErrorError83     Error (const std::string& s)
84         : DeadlyImportError(s)
85     {}
86 };
87 
88 // -------------------------------------------------------------------------------
89 /** The only purpose of this structure is to feed a virtual dtor into its
90  *  descendents. It serves as base class for all data structure fields. */
91 // -------------------------------------------------------------------------------
92 struct ElemBase
93 {
~ElemBaseElemBase94     virtual ~ElemBase() {}
95 
96     /** Type name of the element. The type
97      * string points is the `c_str` of the `name` attribute of the
98      * corresponding `Structure`, that is, it is only valid as long
99      * as the DNA is not modified. The dna_type is only set if the
100      * data type is not static, i.e. a std::shared_ptr<ElemBase>
101      * in the scene description would have its type resolved
102      * at runtime, so this member is always set. */
103     const char* dna_type;
104 };
105 
106 
107 // -------------------------------------------------------------------------------
108 /** Represents a generic pointer to a memory location, which can be either 32
109  *  or 64 bits. These pointers are loaded from the BLEND file and finally
110  *  fixed to point to the real, converted representation of the objects
111  *  they used to point to.*/
112 // -------------------------------------------------------------------------------
113 struct Pointer
114 {
PointerPointer115     Pointer() : val() {}
116     uint64_t val;
117 };
118 
119 // -------------------------------------------------------------------------------
120 /** Represents a generic offset within a BLEND file */
121 // -------------------------------------------------------------------------------
122 struct FileOffset
123 {
FileOffsetFileOffset124     FileOffset() : val() {}
125     uint64_t val;
126 };
127 
128 // -------------------------------------------------------------------------------
129 /** Dummy derivate of std::vector to be able to use it in templates simultaenously
130  *  with std::shared_ptr, which takes only one template argument
131  *  while std::vector takes three. Also we need to provide some special member
132  *  functions of shared_ptr */
133 // -------------------------------------------------------------------------------
134 template <typename T>
135 class vector : public std::vector<T>
136 {
137 public:
138     using std::vector<T>::resize;
139     using std::vector<T>::empty;
140 
reset()141     void reset() {
142         resize(0);
143     }
144 
145     operator bool () const {
146         return !empty();
147     }
148 };
149 
150 // -------------------------------------------------------------------------------
151 /** Mixed flags for use in #Field */
152 // -------------------------------------------------------------------------------
153 enum FieldFlags
154 {
155     FieldFlag_Pointer = 0x1,
156     FieldFlag_Array   = 0x2
157 };
158 
159 // -------------------------------------------------------------------------------
160 /** Represents a single member of a data structure in a BLEND file */
161 // -------------------------------------------------------------------------------
162 struct Field
163 {
164     std::string name;
165     std::string type;
166 
167     size_t size;
168     size_t offset;
169 
170     /** Size of each array dimension. For flat arrays,
171      *  the second dimension is set to 1. */
172     size_t array_sizes[2];
173 
174     /** Any of the #FieldFlags enumerated values */
175     unsigned int flags;
176 };
177 
178 // -------------------------------------------------------------------------------
179 /** Range of possible behaviours for fields absend in the input file. Some are
180  *  mission critical so we need them, while others can silently be default
181  *  initialized and no animations are harmed. */
182 // -------------------------------------------------------------------------------
183 enum ErrorPolicy
184 {
185     /** Substitute default value and ignore */
186     ErrorPolicy_Igno,
187     /** Substitute default value and write to log */
188     ErrorPolicy_Warn,
189     /** Substitute a massive error message and crash the whole matrix. Its time for another zion */
190     ErrorPolicy_Fail
191 };
192 
193 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
194 #   define ErrorPolicy_Igno ErrorPolicy_Warn
195 #endif
196 
197 // -------------------------------------------------------------------------------
198 /** Represents a data structure in a BLEND file. A Structure defines n fields
199  *  and their locatios and encodings the input stream. Usually, every
200  *  Structure instance pertains to one equally-named data structure in the
201  *  BlenderScene.h header. This class defines various utilities to map a
202  *  binary `blob` read from the file to such a structure instance with
203  *  meaningful contents. */
204 // -------------------------------------------------------------------------------
205 class Structure
206 {
207     template <template <typename> class> friend class ObjectCache;
208 
209 public:
210 
Structure()211     Structure()
212         :   cache_idx(static_cast<size_t>(-1) )
213     {}
214 
215 public:
216 
217     // publicly accessible members
218     std::string name;
219     vector< Field > fields;
220     std::map<std::string, size_t> indices;
221 
222     size_t size;
223 
224 public:
225 
226     // --------------------------------------------------------
227     /** Access a field of the structure by its canonical name. The pointer version
228      *  returns NULL on failure while the reference version raises an import error. */
229     inline const Field& operator [] (const std::string& ss) const;
230     inline const Field* Get (const std::string& ss) const;
231 
232     // --------------------------------------------------------
233     /** Access a field of the structure by its index */
234     inline const Field& operator [] (const size_t i) const;
235 
236     // --------------------------------------------------------
237     inline bool operator== (const Structure& other) const {
238         return name == other.name; // name is meant to be an unique identifier
239     }
240 
241     // --------------------------------------------------------
242     inline bool operator!= (const Structure& other) const {
243         return name != other.name;
244     }
245 
246 public:
247 
248     // --------------------------------------------------------
249     /** Try to read an instance of the structure from the stream
250      *  and attempt to convert to `T`. This is done by
251      *  an appropriate specialization. If none is available,
252      *  a compiler complain is the result.
253      *  @param dest Destination value to be written
254      *  @param db File database, including input stream. */
255     template <typename T> inline void Convert (T& dest,
256         const FileDatabase& db) const;
257 
258 
259 
260     // --------------------------------------------------------
261     // generic converter
262     template <typename T>
263     void Convert(std::shared_ptr<ElemBase> in,const FileDatabase& db) const;
264 
265     // --------------------------------------------------------
266     // generic allocator
267     template <typename T> std::shared_ptr<ElemBase> Allocate() const;
268 
269 
270 
271     // --------------------------------------------------------
272     // field parsing for 1d arrays
273     template <int error_policy, typename T, size_t M>
274     void ReadFieldArray(T (& out)[M], const char* name,
275         const FileDatabase& db) const;
276 
277     // --------------------------------------------------------
278     // field parsing for 2d arrays
279     template <int error_policy, typename T, size_t M, size_t N>
280     void ReadFieldArray2(T (& out)[M][N], const char* name,
281         const FileDatabase& db) const;
282 
283     // --------------------------------------------------------
284     // field parsing for pointer or dynamic array types
285     // (std::shared_ptr)
286     // The return value indicates whether the data was already cached.
287     template <int error_policy, template <typename> class TOUT, typename T>
288     bool ReadFieldPtr(TOUT<T>& out, const char* name,
289         const FileDatabase& db,
290         bool non_recursive = false) const;
291 
292     // --------------------------------------------------------
293     // field parsing for static arrays of pointer or dynamic
294     // array types (std::shared_ptr[])
295     // The return value indicates whether the data was already cached.
296     template <int error_policy, template <typename> class TOUT, typename T, size_t N>
297     bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
298         const FileDatabase& db) const;
299 
300     // --------------------------------------------------------
301     // field parsing for `normal` values
302     // The return value indicates whether the data was already cached.
303     template <int error_policy, typename T>
304     void ReadField(T& out, const char* name,
305         const FileDatabase& db) const;
306 
307 private:
308 
309     // --------------------------------------------------------
310     template <template <typename> class TOUT, typename T>
311     bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
312         const FileDatabase& db, const Field& f,
313         bool non_recursive = false) const;
314 
315     // --------------------------------------------------------
316     template <template <typename> class TOUT, typename T>
317     bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
318         const FileDatabase& db, const Field& f, bool) const;
319 
320     // --------------------------------------------------------
321     bool ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval,
322         const FileDatabase& db, const Field& f, bool) const;
323 
324     // --------------------------------------------------------
325     inline const FileBlockHead* LocateFileBlockForAddress(
326         const Pointer & ptrval,
327         const FileDatabase& db) const;
328 
329 private:
330 
331     // ------------------------------------------------------------------------------
_allocate(std::shared_ptr<T> & out,size_t & s)332     template <typename T> T* _allocate(std::shared_ptr<T>& out, size_t& s) const {
333         out = std::shared_ptr<T>(new T());
334         s = 1;
335         return out.get();
336     }
337 
_allocate(vector<T> & out,size_t & s)338     template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
339         out.resize(s);
340         return s ? &out.front() : NULL;
341     }
342 
343     // --------------------------------------------------------
344     template <int error_policy>
345     struct _defaultInitializer {
346 
347         template <typename T, unsigned int N>
operator_defaultInitializer348         void operator ()(T (& out)[N], const char* = NULL) {
349             for (unsigned int i = 0; i < N; ++i) {
350                 out[i] = T();
351             }
352         }
353 
354         template <typename T, unsigned int N, unsigned int M>
operator_defaultInitializer355         void operator ()(T (& out)[N][M], const char* = NULL) {
356             for (unsigned int i = 0; i < N; ++i) {
357                 for (unsigned int j = 0; j < M; ++j) {
358                     out[i][j] = T();
359                 }
360             }
361         }
362 
363         template <typename T>
operator_defaultInitializer364         void operator ()(T& out, const char* = NULL) {
365             out = T();
366         }
367     };
368 
369 private:
370 
371     mutable size_t cache_idx;
372 };
373 
374 // --------------------------------------------------------
375 template <>  struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
376 
377     template <typename T>
378     void operator ()(T& out, const char* reason = "<add reason>") {
379         DefaultLogger::get()->warn(reason);
380 
381         // ... and let the show go on
382         _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out);
383     }
384 };
385 
386 template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
387 
388     template <typename T>
389     void operator ()(T& /*out*/,const char* = "") {
390         // obviously, it is crucial that _DefaultInitializer is used
391         // only from within a catch clause.
392         throw;
393     }
394 };
395 
396 // -------------------------------------------------------------------------------------------------------
397 template <> inline bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out,
398     const Pointer & ptrval,
399     const FileDatabase& db,
400     const Field& f,
401     bool
402     ) const;
403 
404 
405 // -------------------------------------------------------------------------------
406 /** Represents the full data structure information for a single BLEND file.
407  *  This data is extracted from the DNA1 chunk in the file.
408  *  #DNAParser does the reading and represents currently the only place where
409  *  DNA is altered.*/
410 // -------------------------------------------------------------------------------
411 class DNA
412 {
413 public:
414 
415     typedef void (Structure::*ConvertProcPtr) (
416         std::shared_ptr<ElemBase> in,
417         const FileDatabase&
418     ) const;
419 
420     typedef std::shared_ptr<ElemBase> (
421         Structure::*AllocProcPtr) () const;
422 
423     typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
424 
425 public:
426 
427     std::map<std::string, FactoryPair > converters;
428     vector<Structure > structures;
429     std::map<std::string, size_t> indices;
430 
431 public:
432 
433     // --------------------------------------------------------
434     /** Access a structure by its canonical name, the pointer version returns NULL on failure
435       * while the reference version raises an error. */
436     inline const Structure& operator [] (const std::string& ss) const;
437     inline const Structure* Get (const std::string& ss) const;
438 
439     // --------------------------------------------------------
440     /** Access a structure by its index */
441     inline const Structure& operator [] (const size_t i) const;
442 
443 public:
444 
445     // --------------------------------------------------------
446     /** Add structure definitions for all the primitive types,
447      *  i.e. integer, short, char, float */
448     void AddPrimitiveStructures();
449 
450     // --------------------------------------------------------
451     /** Fill the @c converters member with converters for all
452      *  known data types. The implementation of this method is
453      *  in BlenderScene.cpp and is machine-generated.
454      *  Converters are used to quickly handle objects whose
455      *  exact data type is a runtime-property and not yet
456      *  known at compile time (consier Object::data).*/
457     void RegisterConverters();
458 
459 
460     // --------------------------------------------------------
461     /** Take an input blob from the stream, interpret it according to
462      *  a its structure name and convert it to the intermediate
463      *  representation.
464      *  @param structure Destination structure definition
465      *  @param db File database.
466      *  @return A null pointer if no appropriate converter is available.*/
467     std::shared_ptr< ElemBase > ConvertBlobToStructure(
468         const Structure& structure,
469         const FileDatabase& db
470         ) const;
471 
472     // --------------------------------------------------------
473     /** Find a suitable conversion function for a given Structure.
474      *  Such a converter function takes a blob from the input
475      *  stream, reads as much as it needs, and builds up a
476      *  complete object in intermediate representation.
477      *  @param structure Destination structure definition
478      *  @param db File database.
479      *  @return A null pointer in .first if no appropriate converter is available.*/
480     FactoryPair GetBlobToStructureConverter(
481         const Structure& structure,
482         const FileDatabase& db
483         ) const;
484 
485 
486 #ifdef ASSIMP_BUILD_BLENDER_DEBUG
487     // --------------------------------------------------------
488     /** Dump the DNA to a text file. This is for debugging purposes.
489      *  The output file is `dna.txt` in the current working folder*/
490     void DumpToFile();
491 #endif
492 
493     // --------------------------------------------------------
494     /** Extract array dimensions from a C array declaration, such
495      *  as `...[4][6]`. Returned string would be `...[][]`.
496      *  @param out
497      *  @param array_sizes Receive maximally two array dimensions,
498      *    the second element is set to 1 if the array is flat.
499      *    Both are set to 1 if the input is not an array.
500      *  @throw DeadlyImportError if more than 2 dimensions are
501      *    encountered. */
502     static void ExtractArraySize(
503         const std::string& out,
504         size_t array_sizes[2]
505     );
506 };
507 
508 // special converters for primitive types
509 template <> inline void Structure :: Convert<int>       (int& dest,const FileDatabase& db) const;
510 template <> inline void Structure :: Convert<short>     (short& dest,const FileDatabase& db) const;
511 template <> inline void Structure :: Convert<char>      (char& dest,const FileDatabase& db) const;
512 template <> inline void Structure :: Convert<float>     (float& dest,const FileDatabase& db) const;
513 template <> inline void Structure :: Convert<double>    (double& dest,const FileDatabase& db) const;
514 template <> inline void Structure :: Convert<Pointer>   (Pointer& dest,const FileDatabase& db) const;
515 
516 // -------------------------------------------------------------------------------
517 /** Describes a master file block header. Each master file sections holds n
518  *  elements of a certain SDNA structure (or otherwise unspecified data). */
519 // -------------------------------------------------------------------------------
520 struct FileBlockHead
521 {
522     // points right after the header of the file block
523     StreamReaderAny::pos start;
524 
525     std::string id;
526     size_t size;
527 
528     // original memory address of the data
529     Pointer address;
530 
531     // index into DNA
532     unsigned int dna_index;
533 
534     // number of structure instances to follow
535     size_t num;
536 
537 
538 
539     // file blocks are sorted by address to quickly locate specific memory addresses
540     bool operator < (const FileBlockHead& o) const {
541         return address.val < o.address.val;
542     }
543 
544     // for std::upper_bound
545     operator const Pointer& () const {
546         return address;
547     }
548 };
549 
550 // for std::upper_bound
551 inline bool operator< (const Pointer& a, const Pointer& b) {
552     return a.val < b.val;
553 }
554 
555 // -------------------------------------------------------------------------------
556 /** Utility to read all master file blocks in turn. */
557 // -------------------------------------------------------------------------------
558 class SectionParser
559 {
560 public:
561 
562     // --------------------------------------------------------
563     /** @param stream Inout stream, must point to the
564      *  first section in the file. Call Next() once
565      *  to have it read.
566      *  @param ptr64 Pointer size in file is 64 bits? */
567     SectionParser(StreamReaderAny& stream,bool ptr64)
568         : stream(stream)
569         , ptr64(ptr64)
570     {
571         current.size = current.start = 0;
572     }
573 
574 public:
575 
576     // --------------------------------------------------------
577     const FileBlockHead& GetCurrent() const {
578         return current;
579     }
580 
581 
582 public:
583 
584     // --------------------------------------------------------
585     /** Advance to the next section.
586      *  @throw DeadlyImportError if the last chunk was passed. */
587     void Next();
588 
589 public:
590 
591     FileBlockHead current;
592     StreamReaderAny& stream;
593     bool ptr64;
594 };
595 
596 
597 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
598 // -------------------------------------------------------------------------------
599 /** Import statistics, i.e. number of file blocks read*/
600 // -------------------------------------------------------------------------------
601 class Statistics {
602 
603 public:
604 
605     Statistics ()
606         : fields_read       ()
607         , pointers_resolved ()
608         , cache_hits        ()
609 //      , blocks_read       ()
610         , cached_objects    ()
611     {}
612 
613 public:
614 
615     /** total number of fields we read */
616     unsigned int fields_read;
617 
618     /** total number of resolved pointers */
619     unsigned int pointers_resolved;
620 
621     /** number of pointers resolved from the cache */
622     unsigned int cache_hits;
623 
624     /** number of blocks (from  FileDatabase::entries)
625       we did actually read from. */
626     // unsigned int blocks_read;
627 
628     /** objects in FileData::cache */
629     unsigned int cached_objects;
630 };
631 #endif
632 
633 // -------------------------------------------------------------------------------
634 /** The object cache - all objects addressed by pointers are added here. This
635  *  avoids circular references and avoids object duplication. */
636 // -------------------------------------------------------------------------------
637 template <template <typename> class TOUT>
638 class ObjectCache
639 {
640 public:
641 
642     typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
643 
644 public:
645 
646     ObjectCache(const FileDatabase& db)
647         : db(db)
648     {
649         // currently there are only ~400 structure records per blend file.
650         // we read only a small part of them and don't cache objects
651         // which we don't need, so this should suffice.
652         caches.reserve(64);
653     }
654 
655 public:
656 
657     // --------------------------------------------------------
658     /** Check whether a specific item is in the cache.
659      *  @param s Data type of the item
660      *  @param out Output pointer. Unchanged if the
661      *   cache doens't know the item yet.
662      *  @param ptr Item address to look for. */
663     template <typename T> void get (
664         const Structure& s,
665         TOUT<T>& out,
666         const Pointer& ptr) const;
667 
668     // --------------------------------------------------------
669     /** Add an item to the cache after the item has
670      * been fully read. Do not insert anything that
671      * may be faulty or might cause the loading
672      * to abort.
673      *  @param s Data type of the item
674      *  @param out Item to insert into the cache
675      *  @param ptr address (cache key) of the item. */
676     template <typename T> void set
677         (const Structure& s,
678         const TOUT<T>& out,
679         const Pointer& ptr);
680 
681 private:
682 
683     mutable vector<StructureCache> caches;
684     const FileDatabase& db;
685 };
686 
687 // -------------------------------------------------------------------------------
688 // -------------------------------------------------------------------------------
689 template <> class ObjectCache<Blender::vector>
690 {
691 public:
692 
693     ObjectCache(const FileDatabase&) {}
694 
695     template <typename T> void get(const Structure&, vector<T>&, const Pointer&) {}
696     template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
697 };
698 
699 #ifdef _MSC_VER
700 #   pragma warning(disable:4355)
701 #endif
702 
703 // -------------------------------------------------------------------------------
704 /** Memory representation of a full BLEND file and all its dependencies. The
705  *  output aiScene is constructed from an instance of this data structure. */
706 // -------------------------------------------------------------------------------
707 class FileDatabase
708 {
709     template <template <typename> class TOUT> friend class ObjectCache;
710 
711 public:
712 
713 
714     FileDatabase()
715         : _cacheArrays(*this)
716         , _cache(*this)
717         , next_cache_idx()
718     {}
719 
720 public:
721 
722     // publicly accessible fields
723     bool i64bit;
724     bool little;
725 
726     DNA dna;
727     std::shared_ptr< StreamReaderAny > reader;
728     vector< FileBlockHead > entries;
729 
730 public:
731 
732     Statistics& stats() const {
733         return _stats;
734     }
735 
736     // For all our templates to work on both shared_ptr's and vector's
737     // using the same code, a dummy cache for arrays is provided. Actually,
738     // arrays of objects are never cached because we can't easily
739     // ensure their proper destruction.
740     template <typename T>
741     ObjectCache<std::shared_ptr>& cache(std::shared_ptr<T>& /*in*/) const {
742         return _cache;
743     }
744 
745     template <typename T>
746     ObjectCache<vector>& cache(vector<T>& /*in*/) const {
747         return _cacheArrays;
748     }
749 
750 private:
751 
752 
753 #ifndef ASSIMP_BUILD_BLENDER_NO_STATS
754     mutable Statistics _stats;
755 #endif
756 
757     mutable ObjectCache<vector> _cacheArrays;
758     mutable ObjectCache<std::shared_ptr> _cache;
759 
760     mutable size_t next_cache_idx;
761 };
762 
763 #ifdef _MSC_VER
764 #   pragma warning(default:4355)
765 #endif
766 
767 // -------------------------------------------------------------------------------
768 /** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
769 // -------------------------------------------------------------------------------
770 class DNAParser
771 {
772 
773 public:
774 
775     /** Bind the parser to a empty DNA and an input stream */
776     DNAParser(FileDatabase& db)
777         : db(db)
778     {}
779 
780 public:
781 
782     // --------------------------------------------------------
783     /** Locate the DNA in the file and parse it. The input
784      *  stream is expected to point to the beginning of the DN1
785      *  chunk at the time this method is called and is
786      *  undefined afterwards.
787      *  @throw DeadlyImportError if the DNA cannot be read.
788      *  @note The position of the stream pointer is undefined
789      *    afterwards.*/
790     void Parse ();
791 
792 public:
793 
794     /** Obtain a reference to the extracted DNA information */
795     const Blender::DNA& GetDNA() const {
796         return db.dna;
797     }
798 
799 private:
800 
801     FileDatabase& db;
802 };
803 
804     } // end Blend
805 } // end Assimp
806 
807 #include "BlenderDNA.inl"
808 
809 #endif
810