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