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