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.inl |
43 | * @brief Blender `DNA` (file format specification embedded in |
44 | * blend file itself) loader. |
45 | */ |
46 | #ifndef INCLUDED_AI_BLEND_DNA_INL |
47 | #define INCLUDED_AI_BLEND_DNA_INL |
48 | |
49 | #include <memory> |
50 | #include "TinyFormatter.h" |
51 | |
52 | namespace Assimp { |
53 | namespace Blender { |
54 | |
55 | //-------------------------------------------------------------------------------- |
56 | const Field& Structure :: operator [] (const std::string& ss) const |
57 | { |
58 | std::map<std::string, size_t>::const_iterator it = indices.find(ss); |
59 | if (it == indices.end()) { |
60 | throw Error((Formatter::format(), |
61 | "BlendDNA: Did not find a field named `" ,ss,"` in structure `" ,name,"`" |
62 | )); |
63 | } |
64 | |
65 | return fields[(*it).second]; |
66 | } |
67 | |
68 | //-------------------------------------------------------------------------------- |
69 | const Field* Structure :: Get (const std::string& ss) const |
70 | { |
71 | std::map<std::string, size_t>::const_iterator it = indices.find(ss); |
72 | return it == indices.end() ? NULL : &fields[(*it).second]; |
73 | } |
74 | |
75 | //-------------------------------------------------------------------------------- |
76 | const Field& Structure :: operator [] (const size_t i) const |
77 | { |
78 | if (i >= fields.size()) { |
79 | throw Error((Formatter::format(), |
80 | "BlendDNA: There is no field with index `" ,i,"` in structure `" ,name,"`" |
81 | )); |
82 | } |
83 | |
84 | return fields[i]; |
85 | } |
86 | |
87 | //-------------------------------------------------------------------------------- |
88 | template <typename T> std::shared_ptr<ElemBase> Structure :: Allocate() const |
89 | { |
90 | return std::shared_ptr<T>(new T()); |
91 | } |
92 | |
93 | //-------------------------------------------------------------------------------- |
94 | template <typename T> void Structure :: Convert( |
95 | std::shared_ptr<ElemBase> in, |
96 | const FileDatabase& db) const |
97 | { |
98 | Convert<T> (*static_cast<T*> ( in.get() ),db); |
99 | } |
100 | |
101 | //-------------------------------------------------------------------------------- |
102 | template <int error_policy, typename T, size_t M> |
103 | void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const |
104 | { |
105 | const StreamReaderAny::pos old = db.reader->GetCurrentPos(); |
106 | try { |
107 | const Field& f = (*this)[name]; |
108 | const Structure& s = db.dna[f.type]; |
109 | |
110 | // is the input actually an array? |
111 | if (!(f.flags & FieldFlag_Array)) { |
112 | throw Error((Formatter::format(),"Field `" ,name,"` of structure `" , |
113 | this->name,"` ought to be an array of size " ,M |
114 | )); |
115 | } |
116 | |
117 | db.reader->IncPtr(f.offset); |
118 | |
119 | // size conversions are always allowed, regardless of error_policy |
120 | unsigned int i = 0; |
121 | for(; i < std::min(f.array_sizes[0],M); ++i) { |
122 | s.Convert(out[i],db); |
123 | } |
124 | for(; i < M; ++i) { |
125 | _defaultInitializer<ErrorPolicy_Igno>()(out[i]); |
126 | } |
127 | } |
128 | catch (const Error& e) { |
129 | _defaultInitializer<error_policy>()(out,e.what()); |
130 | } |
131 | |
132 | // and recover the previous stream position |
133 | db.reader->SetCurrentPos(old); |
134 | |
135 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
136 | ++db.stats().fields_read; |
137 | #endif |
138 | } |
139 | |
140 | //-------------------------------------------------------------------------------- |
141 | template <int error_policy, typename T, size_t M, size_t N> |
142 | void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const |
143 | { |
144 | const StreamReaderAny::pos old = db.reader->GetCurrentPos(); |
145 | try { |
146 | const Field& f = (*this)[name]; |
147 | const Structure& s = db.dna[f.type]; |
148 | |
149 | // is the input actually an array? |
150 | if (!(f.flags & FieldFlag_Array)) { |
151 | throw Error((Formatter::format(),"Field `" ,name,"` of structure `" , |
152 | this->name,"` ought to be an array of size " ,M,"*" ,N |
153 | )); |
154 | } |
155 | |
156 | db.reader->IncPtr(f.offset); |
157 | |
158 | // size conversions are always allowed, regardless of error_policy |
159 | unsigned int i = 0; |
160 | for(; i < std::min(f.array_sizes[0],M); ++i) { |
161 | unsigned int j = 0; |
162 | for(; j < std::min(f.array_sizes[1],N); ++j) { |
163 | s.Convert(out[i][j],db); |
164 | } |
165 | for(; j < N; ++j) { |
166 | _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]); |
167 | } |
168 | } |
169 | for(; i < M; ++i) { |
170 | _defaultInitializer<ErrorPolicy_Igno>()(out[i]); |
171 | } |
172 | } |
173 | catch (const Error& e) { |
174 | _defaultInitializer<error_policy>()(out,e.what()); |
175 | } |
176 | |
177 | // and recover the previous stream position |
178 | db.reader->SetCurrentPos(old); |
179 | |
180 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
181 | ++db.stats().fields_read; |
182 | #endif |
183 | } |
184 | |
185 | //-------------------------------------------------------------------------------- |
186 | template <int error_policy, template <typename> class TOUT, typename T> |
187 | bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db, |
188 | bool non_recursive /*= false*/) const |
189 | { |
190 | const StreamReaderAny::pos old = db.reader->GetCurrentPos(); |
191 | Pointer ptrval; |
192 | const Field* f; |
193 | try { |
194 | f = &(*this)[name]; |
195 | |
196 | // sanity check, should never happen if the genblenddna script is right |
197 | if (!(f->flags & FieldFlag_Pointer)) { |
198 | throw Error((Formatter::format(),"Field `" ,name,"` of structure `" , |
199 | this->name,"` ought to be a pointer" )); |
200 | } |
201 | |
202 | db.reader->IncPtr(f->offset); |
203 | Convert(ptrval,db); |
204 | // actually it is meaningless on which Structure the Convert is called |
205 | // because the `Pointer` argument triggers a special implementation. |
206 | } |
207 | catch (const Error& e) { |
208 | _defaultInitializer<error_policy>()(out,e.what()); |
209 | |
210 | out.reset(); |
211 | return false; |
212 | } |
213 | |
214 | // resolve the pointer and load the corresponding structure |
215 | const bool res = ResolvePointer(out,ptrval,db,*f, non_recursive); |
216 | |
217 | if(!non_recursive) { |
218 | // and recover the previous stream position |
219 | db.reader->SetCurrentPos(old); |
220 | } |
221 | |
222 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
223 | ++db.stats().fields_read; |
224 | #endif |
225 | |
226 | return res; |
227 | } |
228 | |
229 | //-------------------------------------------------------------------------------- |
230 | template <int error_policy, template <typename> class TOUT, typename T, size_t N> |
231 | bool Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name, |
232 | const FileDatabase& db) const |
233 | { |
234 | // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr |
235 | const StreamReaderAny::pos old = db.reader->GetCurrentPos(); |
236 | Pointer ptrval[N]; |
237 | const Field* f; |
238 | try { |
239 | f = &(*this)[name]; |
240 | |
241 | // sanity check, should never happen if the genblenddna script is right |
242 | if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) { |
243 | throw Error((Formatter::format(),"Field `" ,name,"` of structure `" , |
244 | this->name,"` ought to be a pointer AND an array" )); |
245 | } |
246 | |
247 | db.reader->IncPtr(f->offset); |
248 | |
249 | size_t i = 0; |
250 | for(; i < std::min(f->array_sizes[0],N); ++i) { |
251 | Convert(ptrval[i],db); |
252 | } |
253 | for(; i < N; ++i) { |
254 | _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]); |
255 | } |
256 | |
257 | // actually it is meaningless on which Structure the Convert is called |
258 | // because the `Pointer` argument triggers a special implementation. |
259 | } |
260 | catch (const Error& e) { |
261 | _defaultInitializer<error_policy>()(out,e.what()); |
262 | for(size_t i = 0; i < N; ++i) { |
263 | out[i].reset(); |
264 | } |
265 | return false; |
266 | } |
267 | |
268 | bool res = true; |
269 | for(size_t i = 0; i < N; ++i) { |
270 | // resolve the pointer and load the corresponding structure |
271 | res = ResolvePointer(out[i],ptrval[i],db,*f) && res; |
272 | } |
273 | |
274 | // and recover the previous stream position |
275 | db.reader->SetCurrentPos(old); |
276 | |
277 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
278 | ++db.stats().fields_read; |
279 | #endif |
280 | return res; |
281 | } |
282 | |
283 | //-------------------------------------------------------------------------------- |
284 | template <int error_policy, typename T> |
285 | void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const |
286 | { |
287 | const StreamReaderAny::pos old = db.reader->GetCurrentPos(); |
288 | try { |
289 | const Field& f = (*this)[name]; |
290 | // find the structure definition pertaining to this field |
291 | const Structure& s = db.dna[f.type]; |
292 | |
293 | db.reader->IncPtr(f.offset); |
294 | s.Convert(out,db); |
295 | } |
296 | catch (const Error& e) { |
297 | _defaultInitializer<error_policy>()(out,e.what()); |
298 | } |
299 | |
300 | // and recover the previous stream position |
301 | db.reader->SetCurrentPos(old); |
302 | |
303 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
304 | ++db.stats().fields_read; |
305 | #endif |
306 | } |
307 | |
308 | |
309 | //-------------------------------------------------------------------------------- |
310 | template <template <typename> class TOUT, typename T> |
311 | bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, |
312 | const Field& f, |
313 | bool non_recursive /*= false*/) const |
314 | { |
315 | out.reset(); // ensure null pointers work |
316 | if (!ptrval.val) { |
317 | return false; |
318 | } |
319 | const Structure& s = db.dna[f.type]; |
320 | // find the file block the pointer is pointing to |
321 | const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); |
322 | |
323 | // also determine the target type from the block header |
324 | // and check if it matches the type which we expect. |
325 | const Structure& ss = db.dna[block->dna_index]; |
326 | if (ss != s) { |
327 | throw Error((Formatter::format(),"Expected target to be of type `" ,s.name, |
328 | "` but seemingly it is a `" ,ss.name,"` instead" |
329 | )); |
330 | } |
331 | |
332 | // try to retrieve the object from the cache |
333 | db.cache(out).get(s,out,ptrval); |
334 | if (out) { |
335 | return true; |
336 | } |
337 | |
338 | // seek to this location, but save the previous stream pointer. |
339 | const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); |
340 | db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); |
341 | // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems. |
342 | // I really ought to improve StreamReader to work with 64 bit indices exclusively. |
343 | |
344 | // continue conversion after allocating the required storage |
345 | size_t num = block->size / ss.size; |
346 | T* o = _allocate(out,num); |
347 | |
348 | // cache the object before we convert it to avoid cyclic recursion. |
349 | db.cache(out).set(s,out,ptrval); |
350 | |
351 | // if the non_recursive flag is set, we don't do anything but leave |
352 | // the cursor at the correct position to resolve the object. |
353 | if (!non_recursive) { |
354 | for (size_t i = 0; i < num; ++i,++o) { |
355 | s.Convert(*o,db); |
356 | } |
357 | |
358 | db.reader->SetCurrentPos(pold); |
359 | } |
360 | |
361 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
362 | if(out) { |
363 | ++db.stats().pointers_resolved; |
364 | } |
365 | #endif |
366 | return false; |
367 | } |
368 | |
369 | |
370 | //-------------------------------------------------------------------------------- |
371 | inline bool Structure :: ResolvePointer( std::shared_ptr< FileOffset >& out, const Pointer & ptrval, |
372 | const FileDatabase& db, |
373 | const Field&, |
374 | bool) const |
375 | { |
376 | // Currently used exclusively by PackedFile::data to represent |
377 | // a simple offset into the mapped BLEND file. |
378 | out.reset(); |
379 | if (!ptrval.val) { |
380 | return false; |
381 | } |
382 | |
383 | // find the file block the pointer is pointing to |
384 | const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); |
385 | |
386 | out = std::shared_ptr< FileOffset > (new FileOffset()); |
387 | out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) ); |
388 | return false; |
389 | } |
390 | |
391 | //-------------------------------------------------------------------------------- |
392 | template <template <typename> class TOUT, typename T> |
393 | bool Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, |
394 | const FileDatabase& db, |
395 | const Field& f, |
396 | bool) const |
397 | { |
398 | // This is a function overload, not a template specialization. According to |
399 | // the partial ordering rules, it should be selected by the compiler |
400 | // for array-of-pointer inputs, i.e. Object::mats. |
401 | |
402 | out.reset(); |
403 | if (!ptrval.val) { |
404 | return false; |
405 | } |
406 | |
407 | // find the file block the pointer is pointing to |
408 | const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); |
409 | const size_t num = block->size / (db.i64bit?8:4); |
410 | |
411 | // keep the old stream position |
412 | const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); |
413 | db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); |
414 | |
415 | bool res = false; |
416 | // allocate raw storage for the array |
417 | out.resize(num); |
418 | for (size_t i = 0; i< num; ++i) { |
419 | Pointer val; |
420 | Convert(val,db); |
421 | |
422 | // and resolve the pointees |
423 | res = ResolvePointer(out[i],val,db,f) && res; |
424 | } |
425 | |
426 | db.reader->SetCurrentPos(pold); |
427 | return res; |
428 | } |
429 | |
430 | //-------------------------------------------------------------------------------- |
431 | template <> bool Structure :: ResolvePointer<std::shared_ptr,ElemBase>(std::shared_ptr<ElemBase>& out, |
432 | const Pointer & ptrval, |
433 | const FileDatabase& db, |
434 | const Field&, |
435 | bool |
436 | ) const |
437 | { |
438 | // Special case when the data type needs to be determined at runtime. |
439 | // Less secure than in the `strongly-typed` case. |
440 | |
441 | out.reset(); |
442 | if (!ptrval.val) { |
443 | return false; |
444 | } |
445 | |
446 | // find the file block the pointer is pointing to |
447 | const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db); |
448 | |
449 | // determine the target type from the block header |
450 | const Structure& s = db.dna[block->dna_index]; |
451 | |
452 | // try to retrieve the object from the cache |
453 | db.cache(out).get(s,out,ptrval); |
454 | if (out) { |
455 | return true; |
456 | } |
457 | |
458 | // seek to this location, but save the previous stream pointer. |
459 | const StreamReaderAny::pos pold = db.reader->GetCurrentPos(); |
460 | db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) )); |
461 | // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems. |
462 | // I really ought to improve StreamReader to work with 64 bit indices exclusively. |
463 | |
464 | // continue conversion after allocating the required storage |
465 | DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db); |
466 | if (!builders.first) { |
467 | // this might happen if DNA::RegisterConverters hasn't been called so far |
468 | // or if the target type is not contained in `our` DNA. |
469 | out.reset(); |
470 | DefaultLogger::get()->warn((Formatter::format(), |
471 | "Failed to find a converter for the `" ,s.name,"` structure" |
472 | )); |
473 | return false; |
474 | } |
475 | |
476 | // allocate the object hull |
477 | out = (s.*builders.first)(); |
478 | |
479 | // cache the object immediately to prevent infinite recursion in a |
480 | // circular list with a single element (i.e. a self-referencing element). |
481 | db.cache(out).set(s,out,ptrval); |
482 | |
483 | // and do the actual conversion |
484 | (s.*builders.second)(out,db); |
485 | db.reader->SetCurrentPos(pold); |
486 | |
487 | // store a pointer to the name string of the actual type |
488 | // in the object itself. This allows the conversion code |
489 | // to perform additional type checking. |
490 | out->dna_type = s.name.c_str(); |
491 | |
492 | |
493 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
494 | ++db.stats().pointers_resolved; |
495 | #endif |
496 | return false; |
497 | } |
498 | |
499 | //-------------------------------------------------------------------------------- |
500 | const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const |
501 | { |
502 | // the file blocks appear in list sorted by |
503 | // with ascending base addresses so we can run a |
504 | // binary search to locate the pointee quickly. |
505 | |
506 | // NOTE: Blender seems to distinguish between side-by-side |
507 | // data (stored in the same data block) and far pointers, |
508 | // which are only used for structures starting with an ID. |
509 | // We don't need to make this distinction, our algorithm |
510 | // works regardless where the data is stored. |
511 | vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval); |
512 | if (it == db.entries.end()) { |
513 | // this is crucial, pointers may not be invalid. |
514 | // this is either a corrupted file or an attempted attack. |
515 | throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x" , |
516 | std::hex,ptrval.val,", no file block falls into this address range" |
517 | )); |
518 | } |
519 | if (ptrval.val >= (*it).address.val + (*it).size) { |
520 | throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x" , |
521 | std::hex,ptrval.val,", nearest file block starting at 0x" , |
522 | (*it).address.val," ends at 0x" , |
523 | (*it).address.val + (*it).size |
524 | )); |
525 | } |
526 | return &*it; |
527 | } |
528 | |
529 | // ------------------------------------------------------------------------------------------------ |
530 | // NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has |
531 | // caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask. |
532 | |
533 | template <typename T> struct signless; |
534 | template <> struct signless<char> {typedef unsigned char type;}; |
535 | template <> struct signless<short> {typedef unsigned short type;}; |
536 | template <> struct signless<int> {typedef unsigned int type;}; |
537 | template <> struct signless<unsigned char> { typedef unsigned char type; }; |
538 | template <typename T> |
539 | struct static_cast_silent { |
540 | template <typename V> |
541 | T operator()(V in) { |
542 | return static_cast<T>(in & static_cast<typename signless<T>::type>(-1)); |
543 | } |
544 | }; |
545 | |
546 | template <> struct static_cast_silent<float> { |
547 | template <typename V> float operator()(V in) { |
548 | return static_cast<float> (in); |
549 | } |
550 | }; |
551 | |
552 | template <> struct static_cast_silent<double> { |
553 | template <typename V> double operator()(V in) { |
554 | return static_cast<double>(in); |
555 | } |
556 | }; |
557 | |
558 | // ------------------------------------------------------------------------------------------------ |
559 | template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db) |
560 | { |
561 | if (in.name == "int" ) { |
562 | out = static_cast_silent<T>()(db.reader->GetU4()); |
563 | } |
564 | else if (in.name == "short" ) { |
565 | out = static_cast_silent<T>()(db.reader->GetU2()); |
566 | } |
567 | else if (in.name == "char" ) { |
568 | out = static_cast_silent<T>()(db.reader->GetU1()); |
569 | } |
570 | else if (in.name == "float" ) { |
571 | out = static_cast<T>(db.reader->GetF4()); |
572 | } |
573 | else if (in.name == "double" ) { |
574 | out = static_cast<T>(db.reader->GetF8()); |
575 | } |
576 | else { |
577 | throw DeadlyImportError("Unknown source for conversion to primitive data type: " +in.name); |
578 | } |
579 | } |
580 | |
581 | // ------------------------------------------------------------------------------------------------ |
582 | template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const |
583 | { |
584 | ConvertDispatcher(dest,*this,db); |
585 | } |
586 | |
587 | // ------------------------------------------------------------------------------------------------ |
588 | template<> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const |
589 | { |
590 | // automatic rescaling from short to float and vice versa (seems to be used by normals) |
591 | if (name == "float" ) { |
592 | float f = db.reader->GetF4(); |
593 | if ( f > 1.0f ) |
594 | f = 1.0f; |
595 | dest = static_cast<short>( f * 32767.f); |
596 | //db.reader->IncPtr(-4); |
597 | return; |
598 | } |
599 | else if (name == "double" ) { |
600 | dest = static_cast<short>(db.reader->GetF8() * 32767.); |
601 | //db.reader->IncPtr(-8); |
602 | return; |
603 | } |
604 | ConvertDispatcher(dest,*this,db); |
605 | } |
606 | |
607 | // ------------------------------------------------------------------------------------------------ |
608 | template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const |
609 | { |
610 | // automatic rescaling from char to float and vice versa (seems useful for RGB colors) |
611 | if (name == "float" ) { |
612 | dest = static_cast<char>(db.reader->GetF4() * 255.f); |
613 | return; |
614 | } |
615 | else if (name == "double" ) { |
616 | dest = static_cast<char>(db.reader->GetF8() * 255.f); |
617 | return; |
618 | } |
619 | ConvertDispatcher(dest,*this,db); |
620 | } |
621 | |
622 | // ------------------------------------------------------------------------------------------------ |
623 | template <> inline void Structure::Convert<unsigned char>(unsigned char& dest, const FileDatabase& db) const |
624 | { |
625 | // automatic rescaling from char to float and vice versa (seems useful for RGB colors) |
626 | if (name == "float" ) { |
627 | dest = static_cast<unsigned char>(db.reader->GetF4() * 255.f); |
628 | return; |
629 | } |
630 | else if (name == "double" ) { |
631 | dest = static_cast<unsigned char>(db.reader->GetF8() * 255.f); |
632 | return; |
633 | } |
634 | ConvertDispatcher(dest, *this, db); |
635 | } |
636 | |
637 | |
638 | // ------------------------------------------------------------------------------------------------ |
639 | template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const |
640 | { |
641 | // automatic rescaling from char to float and vice versa (seems useful for RGB colors) |
642 | if (name == "char" ) { |
643 | dest = db.reader->GetI1() / 255.f; |
644 | return; |
645 | } |
646 | // automatic rescaling from short to float and vice versa (used by normals) |
647 | else if (name == "short" ) { |
648 | dest = db.reader->GetI2() / 32767.f; |
649 | return; |
650 | } |
651 | ConvertDispatcher(dest,*this,db); |
652 | } |
653 | |
654 | // ------------------------------------------------------------------------------------------------ |
655 | template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const |
656 | { |
657 | if (name == "char" ) { |
658 | dest = db.reader->GetI1() / 255.; |
659 | return; |
660 | } |
661 | else if (name == "short" ) { |
662 | dest = db.reader->GetI2() / 32767.; |
663 | return; |
664 | } |
665 | ConvertDispatcher(dest,*this,db); |
666 | } |
667 | |
668 | // ------------------------------------------------------------------------------------------------ |
669 | template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const |
670 | { |
671 | if (db.i64bit) { |
672 | dest.val = db.reader->GetU8(); |
673 | //db.reader->IncPtr(-8); |
674 | return; |
675 | } |
676 | dest.val = db.reader->GetU4(); |
677 | //db.reader->IncPtr(-4); |
678 | } |
679 | |
680 | //-------------------------------------------------------------------------------- |
681 | const Structure& DNA :: operator [] (const std::string& ss) const |
682 | { |
683 | std::map<std::string, size_t>::const_iterator it = indices.find(ss); |
684 | if (it == indices.end()) { |
685 | throw Error((Formatter::format(), |
686 | "BlendDNA: Did not find a structure named `" ,ss,"`" |
687 | )); |
688 | } |
689 | |
690 | return structures[(*it).second]; |
691 | } |
692 | |
693 | //-------------------------------------------------------------------------------- |
694 | const Structure* DNA :: Get (const std::string& ss) const |
695 | { |
696 | std::map<std::string, size_t>::const_iterator it = indices.find(ss); |
697 | return it == indices.end() ? NULL : &structures[(*it).second]; |
698 | } |
699 | |
700 | //-------------------------------------------------------------------------------- |
701 | const Structure& DNA :: operator [] (const size_t i) const |
702 | { |
703 | if (i >= structures.size()) { |
704 | throw Error((Formatter::format(), |
705 | "BlendDNA: There is no structure with index `" ,i,"`" |
706 | )); |
707 | } |
708 | |
709 | return structures[i]; |
710 | } |
711 | |
712 | //-------------------------------------------------------------------------------- |
713 | template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get ( |
714 | const Structure& s, |
715 | TOUT<T>& out, |
716 | const Pointer& ptr |
717 | ) const { |
718 | |
719 | if(s.cache_idx == static_cast<size_t>(-1)) { |
720 | s.cache_idx = db.next_cache_idx++; |
721 | caches.resize(db.next_cache_idx); |
722 | return; |
723 | } |
724 | |
725 | typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr); |
726 | if (it != caches[s.cache_idx].end()) { |
727 | out = std::static_pointer_cast<T>( (*it).second ); |
728 | |
729 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
730 | ++db.stats().cache_hits; |
731 | #endif |
732 | } |
733 | // otherwise, out remains untouched |
734 | } |
735 | |
736 | |
737 | //-------------------------------------------------------------------------------- |
738 | template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set ( |
739 | const Structure& s, |
740 | const TOUT<T>& out, |
741 | const Pointer& ptr |
742 | ) { |
743 | if(s.cache_idx == static_cast<size_t>(-1)) { |
744 | s.cache_idx = db.next_cache_idx++; |
745 | caches.resize(db.next_cache_idx); |
746 | } |
747 | caches[s.cache_idx][ptr] = std::static_pointer_cast<ElemBase>( out ); |
748 | |
749 | #ifndef ASSIMP_BUILD_BLENDER_NO_STATS |
750 | ++db.stats().cached_objects; |
751 | #endif |
752 | } |
753 | |
754 | }} |
755 | #endif |
756 | |