1 | /* |
2 | Open Asset Import Library (assimp) |
3 | ---------------------------------------------------------------------- |
4 | |
5 | Copyright (c) 2006-2017, assimp team |
6 | |
7 | All rights reserved. |
8 | |
9 | Redistribution and use of this software in source and binary forms, |
10 | with or without modification, are permitted provided that the |
11 | following 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 | |
27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | OF 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 | |
63 | namespace Assimp { |
64 | |
65 | template <bool,bool> class StreamReader; |
66 | typedef StreamReader<true,true> StreamReaderAny; |
67 | |
68 | namespace Blender { |
69 | |
70 | class FileDatabase; |
71 | struct FileBlockHead; |
72 | |
73 | template <template <typename> class TOUT> |
74 | class 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 | // ------------------------------------------------------------------------------- |
83 | struct 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 | // ------------------------------------------------------------------------------- |
94 | struct 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 | // ------------------------------------------------------------------------------- |
121 | struct 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 | // ------------------------------------------------------------------------------- |
132 | struct 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 | // ------------------------------------------------------------------------------- |
146 | template <typename T> |
147 | class vector : public std::vector<T> { |
148 | public: |
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 | // ------------------------------------------------------------------------------- |
164 | enum 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 | // ------------------------------------------------------------------------------- |
172 | struct 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 | // ------------------------------------------------------------------------------- |
192 | enum 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 | // ------------------------------------------------------------------------------- |
213 | class Structure { |
214 | template <template <typename> class> friend class ObjectCache; |
215 | |
216 | public: |
217 | Structure() |
218 | : cache_idx(static_cast<size_t>(-1) ){ |
219 | // empty |
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> 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 | |
311 | private: |
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 | |
333 | private: |
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 | |
373 | private: |
374 | |
375 | mutable size_t cache_idx; |
376 | }; |
377 | |
378 | // -------------------------------------------------------- |
379 | template <> 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 | |
390 | template <> 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 | // ------------------------------------------------------------------------------------------------------- |
401 | template <> 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 | // ------------------------------------------------------------------------------- |
415 | class DNA |
416 | { |
417 | public: |
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 | |
429 | public: |
430 | |
431 | std::map<std::string, FactoryPair > converters; |
432 | vector<Structure > structures; |
433 | std::map<std::string, size_t> indices; |
434 | |
435 | public: |
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 | |
447 | public: |
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 ( |
507 | const std::string& out, |
508 | size_t array_sizes[2] |
509 | ); |
510 | }; |
511 | |
512 | // special converters for primitive types |
513 | template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const; |
514 | template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const; |
515 | template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const; |
516 | template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const; |
517 | template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const; |
518 | template <> 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 | // ------------------------------------------------------------------------------- |
524 | struct 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 |
555 | inline 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 | // ------------------------------------------------------------------------------- |
562 | class SectionParser |
563 | { |
564 | public: |
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 | |
578 | public: |
579 | |
580 | // -------------------------------------------------------- |
581 | const FileBlockHead& GetCurrent() const { |
582 | return current; |
583 | } |
584 | |
585 | |
586 | public: |
587 | |
588 | // -------------------------------------------------------- |
589 | /** Advance to the next section. |
590 | * @throw DeadlyImportError if the last chunk was passed. */ |
591 | void Next(); |
592 | |
593 | public: |
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 | // ------------------------------------------------------------------------------- |
605 | class Statistics { |
606 | |
607 | public: |
608 | |
609 | Statistics () |
610 | : fields_read () |
611 | , pointers_resolved () |
612 | , cache_hits () |
613 | // , blocks_read () |
614 | , cached_objects () |
615 | {} |
616 | |
617 | public: |
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 | // ------------------------------------------------------------------------------- |
641 | template <template <typename> class TOUT> |
642 | class ObjectCache |
643 | { |
644 | public: |
645 | |
646 | typedef std::map< Pointer, TOUT<ElemBase> > StructureCache; |
647 | |
648 | public: |
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 | |
659 | public: |
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 | |
685 | private: |
686 | |
687 | mutable vector<StructureCache> caches; |
688 | const FileDatabase& db; |
689 | }; |
690 | |
691 | // ------------------------------------------------------------------------------- |
692 | // ------------------------------------------------------------------------------- |
693 | template <> class ObjectCache<Blender::vector> |
694 | { |
695 | public: |
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 | // ------------------------------------------------------------------------------- |
711 | class FileDatabase |
712 | { |
713 | template <template <typename> class TOUT> friend class ObjectCache; |
714 | |
715 | public: |
716 | FileDatabase() |
717 | : _cacheArrays(*this) |
718 | , _cache(*this) |
719 | , next_cache_idx() |
720 | {} |
721 | |
722 | public: |
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 | |
731 | public: |
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 | |
751 | private: |
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 | // ------------------------------------------------------------------------------- |
771 | class DNAParser |
772 | { |
773 | |
774 | public: |
775 | |
776 | /** Bind the parser to a empty DNA and an input stream */ |
777 | DNAParser(FileDatabase& db) |
778 | : db(db) |
779 | {} |
780 | |
781 | public: |
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 | |
793 | public: |
794 | |
795 | /** Obtain a reference to the extracted DNA information */ |
796 | const Blender::DNA& GetDNA() const { |
797 | return db.dna; |
798 | } |
799 | |
800 | private: |
801 | |
802 | FileDatabase& db; |
803 | }; |
804 | |
805 | } // end Blend |
806 | } // end Assimp |
807 | |
808 | #include "BlenderDNA.inl" |
809 | |
810 | #endif |
811 | |