vrshoot
view libs/assimp/FBXDocument.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file FBXDocument.cpp
42 * @brief Implementation of the FBX DOM classes
43 */
44 #include "AssimpPCH.h"
46 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
48 #include <functional>
50 #include "FBXParser.h"
51 #include "FBXDocument.h"
52 #include "FBXUtil.h"
53 #include "FBXImporter.h"
54 #include "FBXImportSettings.h"
55 #include "FBXDocumentUtil.h"
56 #include "FBXProperties.h"
58 namespace Assimp {
59 namespace FBX {
61 using namespace Util;
63 // ------------------------------------------------------------------------------------------------
64 LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
65 : doc(doc)
66 , element(element)
67 , id(id)
68 , flags()
69 {
71 }
73 // ------------------------------------------------------------------------------------------------
74 LazyObject::~LazyObject()
75 {
77 }
79 // ------------------------------------------------------------------------------------------------
80 const Object* LazyObject::Get(bool dieOnError)
81 {
82 if(IsBeingConstructed() || FailedToConstruct()) {
83 return NULL;
84 }
86 if (object.get()) {
87 return object.get();
88 }
90 // if this is the root object, we return a dummy since there
91 // is no root object int he fbx file - it is just referenced
92 // with id 0.
93 if(id == 0L) {
94 object.reset(new Object(id, element, "Model::RootNode"));
95 return object.get();
96 }
98 const Token& key = element.KeyToken();
99 const TokenList& tokens = element.Tokens();
101 if(tokens.size() < 3) {
102 DOMError("expected at least 3 tokens: id, name and class tag",&element);
103 }
105 const char* err;
106 std::string name = ParseTokenAsString(*tokens[1],err);
107 if (err) {
108 DOMError(err,&element);
109 }
111 // small fix for binary reading: binary fbx files don't use
112 // prefixes such as Model:: in front of their names. The
113 // loading code expects this at many places, though!
114 // so convert the binary representation (a 0x0001) to the
115 // double colon notation.
116 if(tokens[1]->IsBinary()) {
117 for (size_t i = 0; i < name.length(); ++i) {
118 if (name[i] == 0x0 && name[i+1] == 0x1) {
119 name = name.substr(i+2) + "::" + name.substr(0,i);
120 }
121 }
122 }
124 const std::string classtag = ParseTokenAsString(*tokens[2],err);
125 if (err) {
126 DOMError(err,&element);
127 }
129 // prevent recursive calls
130 flags |= BEING_CONSTRUCTED;
132 try {
133 // this needs to be relatively fast since it happens a lot,
134 // so avoid constructing strings all the time.
135 const char* obtype = key.begin();
136 const size_t length = static_cast<size_t>(key.end()-key.begin());
137 if (!strncmp(obtype,"Geometry",length)) {
138 if (!strcmp(classtag.c_str(),"Mesh")) {
139 object.reset(new MeshGeometry(id,element,name,doc));
140 }
141 }
142 else if (!strncmp(obtype,"NodeAttribute",length)) {
143 if (!strcmp(classtag.c_str(),"Camera")) {
144 object.reset(new Camera(id,element,doc,name));
145 }
146 else if (!strcmp(classtag.c_str(),"CameraSwitcher")) {
147 object.reset(new CameraSwitcher(id,element,doc,name));
148 }
149 else if (!strcmp(classtag.c_str(),"Light")) {
150 object.reset(new Light(id,element,doc,name));
151 }
152 else if (!strcmp(classtag.c_str(),"Null")) {
153 object.reset(new Null(id,element,doc,name));
154 }
155 else if (!strcmp(classtag.c_str(),"LimbNode")) {
156 object.reset(new LimbNode(id,element,doc,name));
157 }
158 }
159 else if (!strncmp(obtype,"Deformer",length)) {
160 if (!strcmp(classtag.c_str(),"Cluster")) {
161 object.reset(new Cluster(id,element,doc,name));
162 }
163 else if (!strcmp(classtag.c_str(),"Skin")) {
164 object.reset(new Skin(id,element,doc,name));
165 }
166 }
167 else if (!strncmp(obtype,"Model",length)) {
168 // FK and IK effectors are not supported
169 if (strcmp(classtag.c_str(),"IKEffector") && strcmp(classtag.c_str(),"FKEffector")) {
170 object.reset(new Model(id,element,doc,name));
171 }
172 }
173 else if (!strncmp(obtype,"Material",length)) {
174 object.reset(new Material(id,element,doc,name));
175 }
176 else if (!strncmp(obtype,"Texture",length)) {
177 object.reset(new Texture(id,element,doc,name));
178 }
179 else if (!strncmp(obtype,"AnimationStack",length)) {
180 object.reset(new AnimationStack(id,element,name,doc));
181 }
182 else if (!strncmp(obtype,"AnimationLayer",length)) {
183 object.reset(new AnimationLayer(id,element,name,doc));
184 }
185 // note: order matters for these two
186 else if (!strncmp(obtype,"AnimationCurve",length)) {
187 object.reset(new AnimationCurve(id,element,name,doc));
188 }
189 else if (!strncmp(obtype,"AnimationCurveNode",length)) {
190 object.reset(new AnimationCurveNode(id,element,name,doc));
191 }
192 }
193 catch(std::exception& ex) {
194 flags &= ~BEING_CONSTRUCTED;
195 flags |= FAILED_TO_CONSTRUCT;
197 if(dieOnError || doc.Settings().strictMode) {
198 throw;
199 }
201 // note: the error message is already formatted, so raw logging is ok
202 if(!DefaultLogger::isNullLogger()) {
203 DefaultLogger::get()->error(ex.what());
204 }
205 return NULL;
206 }
208 if (!object.get()) {
209 //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
210 }
212 flags &= ~BEING_CONSTRUCTED;
213 return object.get();
214 }
216 // ------------------------------------------------------------------------------------------------
217 Object::Object(uint64_t id, const Element& element, const std::string& name)
218 : element(element)
219 , name(name)
220 , id(id)
221 {
223 }
225 // ------------------------------------------------------------------------------------------------
226 Object::~Object()
227 {
229 }
232 // ------------------------------------------------------------------------------------------------
233 FileGlobalSettings::FileGlobalSettings(const Document& doc, boost::shared_ptr<const PropertyTable> props)
234 : props(props)
235 , doc(doc)
236 {
238 }
241 // ------------------------------------------------------------------------------------------------
242 FileGlobalSettings::~FileGlobalSettings()
243 {
245 }
248 // ------------------------------------------------------------------------------------------------
249 Document::Document(const Parser& parser, const ImportSettings& settings)
250 : settings(settings)
251 , parser(parser)
252 {
253 // cannot use array default initialization syntax because vc8 fails on it
254 for (unsigned int i = 0; i < 7; ++i) {
255 creationTimeStamp[i] = 0;
256 }
258 ReadHeader();
259 ReadPropertyTemplates();
261 ReadGlobalSettings();
263 // this order is important, connections need parsed objects to check
264 // whether connections are ok or not. Objects may not be evaluated yet,
265 // though, since this may require valid connections.
266 ReadObjects();
267 ReadConnections();
268 }
271 // ------------------------------------------------------------------------------------------------
272 Document::~Document()
273 {
274 BOOST_FOREACH(ObjectMap::value_type& v, objects) {
275 delete v.second;
276 }
277 }
280 // ------------------------------------------------------------------------------------------------
281 void Document::ReadHeader()
282 {
283 // read ID objects from "Objects" section
284 const Scope& sc = parser.GetRootScope();
285 const Element* const ehead = sc["FBXHeaderExtension"];
286 if(!ehead || !ehead->Compound()) {
287 DOMError("no FBXHeaderExtension dictionary found");
288 }
290 const Scope& shead = *ehead->Compound();
291 fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0));
294 if(fbxVersion < 7200 || fbxVersion > 7300) {
295 if(Settings().strictMode) {
296 DOMError("unsupported format version, supported are only FBX 2012 and FBX 2013"\
297 " in ASCII format (turn off strict mode to try anyhow) ");
298 }
299 else {
300 DOMWarning("unsupported format version, supported are only FBX 2012 and FBX 2013, trying to read it nevertheless");
301 }
302 }
305 const Element* const ecreator = shead["Creator"];
306 if(ecreator) {
307 creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
308 }
310 const Element* const etimestamp = shead["CreationTimeStamp"];
311 if(etimestamp && etimestamp->Compound()) {
312 const Scope& stimestamp = *etimestamp->Compound();
313 creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0));
314 creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0));
315 creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0));
316 creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0));
317 creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0));
318 creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0));
319 creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0));
320 }
321 }
323 // ------------------------------------------------------------------------------------------------
324 void Document::ReadGlobalSettings()
325 {
326 const Scope& sc = parser.GetRootScope();
327 const Element* const ehead = sc["GlobalSettings"];
328 if(!ehead || !ehead->Compound()) {
329 DOMWarning("no GlobalSettings dictionary found");
331 globals.reset(new FileGlobalSettings(*this, boost::make_shared<const PropertyTable>()));
332 return;
333 }
335 boost::shared_ptr<const PropertyTable> props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true);
337 if(!props) {
338 DOMError("GlobalSettings dictionary contains no property table");
339 }
341 globals.reset(new FileGlobalSettings(*this, props));
342 }
345 // ------------------------------------------------------------------------------------------------
346 void Document::ReadObjects()
347 {
348 // read ID objects from "Objects" section
349 const Scope& sc = parser.GetRootScope();
350 const Element* const eobjects = sc["Objects"];
351 if(!eobjects || !eobjects->Compound()) {
352 DOMError("no Objects dictionary found");
353 }
355 // add a dummy entry to represent the Model::RootNode object (id 0),
356 // which is only indirectly defined in the input file
357 objects[0] = new LazyObject(0L, *eobjects, *this);
359 const Scope& sobjects = *eobjects->Compound();
360 BOOST_FOREACH(const ElementMap::value_type& el, sobjects.Elements()) {
362 // extract ID
363 const TokenList& tok = el.second->Tokens();
365 if (tok.empty()) {
366 DOMError("expected ID after object key",el.second);
367 }
369 const char* err;
371 const uint64_t id = ParseTokenAsID(*tok[0], err);
372 if(err) {
373 DOMError(err,el.second);
374 }
376 // id=0 is normally implicit
377 if(id == 0L) {
378 DOMError("encountered object with implicitly defined id 0",el.second);
379 }
381 if(objects.find(id) != objects.end()) {
382 DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
383 }
385 objects[id] = new LazyObject(id, *el.second, *this);
387 // grab all animation stacks upfront since there is no listing of them
388 if(!strcmp(el.first.c_str(),"AnimationStack")) {
389 animationStacks.push_back(id);
390 }
391 }
392 }
395 // ------------------------------------------------------------------------------------------------
396 void Document::ReadPropertyTemplates()
397 {
398 const Scope& sc = parser.GetRootScope();
399 // read property templates from "Definitions" section
400 const Element* const edefs = sc["Definitions"];
401 if(!edefs || !edefs->Compound()) {
402 DOMWarning("no Definitions dictionary found");
403 return;
404 }
406 const Scope& sdefs = *edefs->Compound();
407 const ElementCollection otypes = sdefs.GetCollection("ObjectType");
408 for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
409 const Element& el = *(*it).second;
410 const Scope* sc = el.Compound();
411 if(!sc) {
412 DOMWarning("expected nested scope in ObjectType, ignoring",&el);
413 continue;
414 }
416 const TokenList& tok = el.Tokens();
417 if(tok.empty()) {
418 DOMWarning("expected name for ObjectType element, ignoring",&el);
419 continue;
420 }
422 const std::string& oname = ParseTokenAsString(*tok[0]);
424 const ElementCollection templs = sc->GetCollection("PropertyTemplate");
425 for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
426 const Element& el = *(*it).second;
427 const Scope* sc = el.Compound();
428 if(!sc) {
429 DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
430 continue;
431 }
433 const TokenList& tok = el.Tokens();
434 if(tok.empty()) {
435 DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
436 continue;
437 }
439 const std::string& pname = ParseTokenAsString(*tok[0]);
441 const Element* Properties70 = (*sc)["Properties70"];
442 if(Properties70) {
443 boost::shared_ptr<const PropertyTable> props = boost::make_shared<const PropertyTable>(
444 *Properties70,boost::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
445 );
447 templates[oname+"."+pname] = props;
448 }
449 }
450 }
451 }
455 // ------------------------------------------------------------------------------------------------
456 void Document::ReadConnections()
457 {
458 const Scope& sc = parser.GetRootScope();
459 // read property templates from "Definitions" section
460 const Element* const econns = sc["Connections"];
461 if(!econns || !econns->Compound()) {
462 DOMError("no Connections dictionary found");
463 }
465 uint64_t insertionOrder = 0l;
467 const Scope& sconns = *econns->Compound();
468 const ElementCollection conns = sconns.GetCollection("C");
469 for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
470 const Element& el = *(*it).second;
471 const std::string& type = ParseTokenAsString(GetRequiredToken(el,0));
472 const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
473 const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
475 // OO = object-object connection
476 // OP = object-property connection, in which case the destination property follows the object ID
477 const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : "");
479 if(objects.find(src) == objects.end()) {
480 DOMWarning("source object for connection does not exist",&el);
481 continue;
482 }
484 // dest may be 0 (root node) but we added a dummy object before
485 if(objects.find(dest) == objects.end()) {
486 DOMWarning("destination object for connection does not exist",&el);
487 continue;
488 }
490 // add new connection
491 const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
492 src_connections.insert(ConnectionMap::value_type(src,c));
493 dest_connections.insert(ConnectionMap::value_type(dest,c));
494 }
495 }
498 // ------------------------------------------------------------------------------------------------
499 const std::vector<const AnimationStack*>& Document::AnimationStacks() const
500 {
501 if (!animationStacksResolved.empty() || !animationStacks.size()) {
502 return animationStacksResolved;
503 }
505 animationStacksResolved.reserve(animationStacks.size());
506 BOOST_FOREACH(uint64_t id, animationStacks) {
507 LazyObject* const lazy = GetObject(id);
508 const AnimationStack* stack;
509 if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
510 DOMWarning("failed to read AnimationStack object");
511 continue;
512 }
513 animationStacksResolved.push_back(stack);
514 }
516 return animationStacksResolved;
517 }
520 // ------------------------------------------------------------------------------------------------
521 LazyObject* Document::GetObject(uint64_t id) const
522 {
523 ObjectMap::const_iterator it = objects.find(id);
524 return it == objects.end() ? NULL : (*it).second;
525 }
527 #define MAX_CLASSNAMES 6
529 // ------------------------------------------------------------------------------------------------
530 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id,
531 const ConnectionMap& conns) const
532 {
533 std::vector<const Connection*> temp;
535 const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
536 conns.equal_range(id);
538 temp.reserve(std::distance(range.first,range.second));
539 for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
540 temp.push_back((*it).second);
541 }
543 std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
545 return temp; // NRVO should handle this
546 }
549 // ------------------------------------------------------------------------------------------------
550 std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
551 const ConnectionMap& conns,
552 const char* const* classnames,
553 size_t count) const
555 {
556 ai_assert(classnames);
557 ai_assert(count != 0 && count <= MAX_CLASSNAMES);
559 size_t lenghts[MAX_CLASSNAMES];
561 const size_t c = count;
562 for (size_t i = 0; i < c; ++i) {
563 lenghts[i] = strlen(classnames[i]);
564 }
566 std::vector<const Connection*> temp;
568 const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
569 conns.equal_range(id);
571 temp.reserve(std::distance(range.first,range.second));
572 for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
573 const Token& key = (is_src
574 ? (*it).second->LazyDestinationObject()
575 : (*it).second->LazySourceObject()
576 ).GetElement().KeyToken();
578 const char* obtype = key.begin();
580 for (size_t i = 0; i < c; ++i) {
581 ai_assert(classnames[i]);
582 if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
583 obtype = NULL;
584 break;
585 }
586 }
588 if(obtype) {
589 continue;
590 }
592 temp.push_back((*it).second);
593 }
595 std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
596 return temp; // NRVO should handle this
597 }
600 // ------------------------------------------------------------------------------------------------
601 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
602 {
603 return GetConnectionsSequenced(source, ConnectionsBySource());
604 }
608 // ------------------------------------------------------------------------------------------------
609 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest,
610 const char* classname) const
611 {
612 const char* arr[] = {classname};
613 return GetConnectionsBySourceSequenced(dest, arr,1);
614 }
618 // ------------------------------------------------------------------------------------------------
619 std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
620 const char* const* classnames, size_t count) const
621 {
622 return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
623 }
626 // ------------------------------------------------------------------------------------------------
627 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
628 const char* classname) const
629 {
630 const char* arr[] = {classname};
631 return GetConnectionsByDestinationSequenced(dest, arr,1);
632 }
635 // ------------------------------------------------------------------------------------------------
636 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
637 {
638 return GetConnectionsSequenced(dest, ConnectionsByDestination());
639 }
642 // ------------------------------------------------------------------------------------------------
643 std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
644 const char* const* classnames, size_t count) const
646 {
647 return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
648 }
651 // ------------------------------------------------------------------------------------------------
652 Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop,
653 const Document& doc)
655 : insertionOrder(insertionOrder)
656 , prop(prop)
657 , src(src)
658 , dest(dest)
659 , doc(doc)
660 {
661 ai_assert(doc.Objects().find(src) != doc.Objects().end());
662 // dest may be 0 (root node)
663 ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
664 }
667 // ------------------------------------------------------------------------------------------------
668 Connection::~Connection()
669 {
671 }
674 // ------------------------------------------------------------------------------------------------
675 LazyObject& Connection::LazySourceObject() const
676 {
677 LazyObject* const lazy = doc.GetObject(src);
678 ai_assert(lazy);
679 return *lazy;
680 }
683 // ------------------------------------------------------------------------------------------------
684 LazyObject& Connection::LazyDestinationObject() const
685 {
686 LazyObject* const lazy = doc.GetObject(dest);
687 ai_assert(lazy);
688 return *lazy;
689 }
692 // ------------------------------------------------------------------------------------------------
693 const Object* Connection::SourceObject() const
694 {
695 LazyObject* const lazy = doc.GetObject(src);
696 ai_assert(lazy);
697 return lazy->Get();
698 }
701 // ------------------------------------------------------------------------------------------------
702 const Object* Connection::DestinationObject() const
703 {
704 LazyObject* const lazy = doc.GetObject(dest);
705 ai_assert(lazy);
706 return lazy->Get();
707 }
709 } // !FBX
710 } // !Assimp
712 #endif