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