vrshoot

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