vrshoot

diff 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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/FBXDocument.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,713 @@
     1.4 +/*
     1.5 +Open Asset Import Library (assimp)
     1.6 +----------------------------------------------------------------------
     1.7 +
     1.8 +Copyright (c) 2006-2012, assimp team
     1.9 +All rights reserved.
    1.10 +
    1.11 +Redistribution and use of this software in source and binary forms, 
    1.12 +with or without modification, are permitted provided that the 
    1.13 +following conditions are met:
    1.14 +
    1.15 +* Redistributions of source code must retain the above
    1.16 +  copyright notice, this list of conditions and the
    1.17 +  following disclaimer.
    1.18 +
    1.19 +* Redistributions in binary form must reproduce the above
    1.20 +  copyright notice, this list of conditions and the
    1.21 +  following disclaimer in the documentation and/or other
    1.22 +  materials provided with the distribution.
    1.23 +
    1.24 +* Neither the name of the assimp team, nor the names of its
    1.25 +  contributors may be used to endorse or promote products
    1.26 +  derived from this software without specific prior
    1.27 +  written permission of the assimp team.
    1.28 +
    1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.40 +
    1.41 +----------------------------------------------------------------------
    1.42 +*/
    1.43 +
    1.44 +/** @file  FBXDocument.cpp
    1.45 + *  @brief Implementation of the FBX DOM classes
    1.46 + */
    1.47 +#include "AssimpPCH.h"
    1.48 +
    1.49 +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
    1.50 +
    1.51 +#include <functional>
    1.52 +
    1.53 +#include "FBXParser.h"
    1.54 +#include "FBXDocument.h"
    1.55 +#include "FBXUtil.h"
    1.56 +#include "FBXImporter.h"
    1.57 +#include "FBXImportSettings.h"
    1.58 +#include "FBXDocumentUtil.h"
    1.59 +#include "FBXProperties.h"
    1.60 +
    1.61 +namespace Assimp {
    1.62 +namespace FBX {
    1.63 +
    1.64 +using namespace Util;
    1.65 +
    1.66 +// ------------------------------------------------------------------------------------------------
    1.67 +LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
    1.68 +: doc(doc)
    1.69 +, element(element)
    1.70 +, id(id)
    1.71 +, flags()
    1.72 +{
    1.73 +
    1.74 +}
    1.75 +
    1.76 +// ------------------------------------------------------------------------------------------------
    1.77 +LazyObject::~LazyObject()
    1.78 +{
    1.79 +
    1.80 +}
    1.81 +
    1.82 +// ------------------------------------------------------------------------------------------------
    1.83 +const Object* LazyObject::Get(bool dieOnError)
    1.84 +{
    1.85 +	if(IsBeingConstructed() || FailedToConstruct()) {
    1.86 +		return NULL;
    1.87 +	}
    1.88 +
    1.89 +	if (object.get()) {
    1.90 +		return object.get();
    1.91 +	}
    1.92 +
    1.93 +	// if this is the root object, we return a dummy since there
    1.94 +	// is no root object int he fbx file - it is just referenced
    1.95 +	// with id 0.
    1.96 +	if(id == 0L) {
    1.97 +		object.reset(new Object(id, element, "Model::RootNode"));
    1.98 +		return object.get();
    1.99 +	}
   1.100 +
   1.101 +	const Token& key = element.KeyToken();
   1.102 +	const TokenList& tokens = element.Tokens();
   1.103 +
   1.104 +	if(tokens.size() < 3) {
   1.105 +		DOMError("expected at least 3 tokens: id, name and class tag",&element);
   1.106 +	}
   1.107 +
   1.108 +	const char* err;
   1.109 +	std::string name = ParseTokenAsString(*tokens[1],err);
   1.110 +	if (err) {
   1.111 +		DOMError(err,&element);
   1.112 +	} 
   1.113 +
   1.114 +	// small fix for binary reading: binary fbx files don't use
   1.115 +	// prefixes such as Model:: in front of their names. The
   1.116 +	// loading code expects this at many places, though!
   1.117 +	// so convert the binary representation (a 0x0001) to the
   1.118 +	// double colon notation.
   1.119 +	if(tokens[1]->IsBinary()) {
   1.120 +		for (size_t i = 0; i < name.length(); ++i) {
   1.121 +			if (name[i] == 0x0 && name[i+1] == 0x1) {
   1.122 +				name = name.substr(i+2) + "::" + name.substr(0,i);
   1.123 +			}
   1.124 +		}
   1.125 +	}
   1.126 +
   1.127 +	const std::string classtag = ParseTokenAsString(*tokens[2],err);
   1.128 +	if (err) {
   1.129 +		DOMError(err,&element);
   1.130 +	} 
   1.131 +
   1.132 +	// prevent recursive calls
   1.133 +	flags |= BEING_CONSTRUCTED;
   1.134 +
   1.135 +	try {
   1.136 +		// this needs to be relatively fast since it happens a lot,
   1.137 +		// so avoid constructing strings all the time.
   1.138 +		const char* obtype = key.begin();
   1.139 +		const size_t length = static_cast<size_t>(key.end()-key.begin());
   1.140 +		if (!strncmp(obtype,"Geometry",length)) {
   1.141 +			if (!strcmp(classtag.c_str(),"Mesh")) {
   1.142 +				object.reset(new MeshGeometry(id,element,name,doc));
   1.143 +			}
   1.144 +		}
   1.145 +		else if (!strncmp(obtype,"NodeAttribute",length)) {
   1.146 +			if (!strcmp(classtag.c_str(),"Camera")) {
   1.147 +				object.reset(new Camera(id,element,doc,name));
   1.148 +			}
   1.149 +			else if (!strcmp(classtag.c_str(),"CameraSwitcher")) {
   1.150 +				object.reset(new CameraSwitcher(id,element,doc,name));
   1.151 +			}
   1.152 +			else if (!strcmp(classtag.c_str(),"Light")) {
   1.153 +				object.reset(new Light(id,element,doc,name));
   1.154 +			}
   1.155 +			else if (!strcmp(classtag.c_str(),"Null")) {
   1.156 +				object.reset(new Null(id,element,doc,name));
   1.157 +			}
   1.158 +			else if (!strcmp(classtag.c_str(),"LimbNode")) {
   1.159 +				object.reset(new LimbNode(id,element,doc,name));
   1.160 +			}
   1.161 +		}
   1.162 +		else if (!strncmp(obtype,"Deformer",length)) {
   1.163 +			if (!strcmp(classtag.c_str(),"Cluster")) {
   1.164 +				object.reset(new Cluster(id,element,doc,name));
   1.165 +			}
   1.166 +			else if (!strcmp(classtag.c_str(),"Skin")) {
   1.167 +				object.reset(new Skin(id,element,doc,name));
   1.168 +			}
   1.169 +		}
   1.170 +		else if (!strncmp(obtype,"Model",length)) {
   1.171 +			// FK and IK effectors are not supported
   1.172 +			if (strcmp(classtag.c_str(),"IKEffector") && strcmp(classtag.c_str(),"FKEffector")) {
   1.173 +				object.reset(new Model(id,element,doc,name));
   1.174 +			}
   1.175 +		}
   1.176 +		else if (!strncmp(obtype,"Material",length)) {
   1.177 +			object.reset(new Material(id,element,doc,name));
   1.178 +		}
   1.179 +		else if (!strncmp(obtype,"Texture",length)) {
   1.180 +			object.reset(new Texture(id,element,doc,name));
   1.181 +		}
   1.182 +		else if (!strncmp(obtype,"AnimationStack",length)) {
   1.183 +			object.reset(new AnimationStack(id,element,name,doc));
   1.184 +		}
   1.185 +		else if (!strncmp(obtype,"AnimationLayer",length)) {
   1.186 +			object.reset(new AnimationLayer(id,element,name,doc));
   1.187 +		}
   1.188 +		// note: order matters for these two
   1.189 +		else if (!strncmp(obtype,"AnimationCurve",length)) {
   1.190 +			object.reset(new AnimationCurve(id,element,name,doc));
   1.191 +		}
   1.192 +		else if (!strncmp(obtype,"AnimationCurveNode",length)) {
   1.193 +			object.reset(new AnimationCurveNode(id,element,name,doc));
   1.194 +		}	
   1.195 +	}
   1.196 +	catch(std::exception& ex) {
   1.197 +		flags &= ~BEING_CONSTRUCTED;
   1.198 +		flags |= FAILED_TO_CONSTRUCT;
   1.199 +
   1.200 +		if(dieOnError || doc.Settings().strictMode) {
   1.201 +			throw;
   1.202 +		}
   1.203 +
   1.204 +		// note: the error message is already formatted, so raw logging is ok
   1.205 +		if(!DefaultLogger::isNullLogger()) {
   1.206 +			DefaultLogger::get()->error(ex.what());
   1.207 +		}
   1.208 +		return NULL;
   1.209 +	}
   1.210 +
   1.211 +	if (!object.get()) {
   1.212 +		//DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
   1.213 +	}
   1.214 +
   1.215 +	flags &= ~BEING_CONSTRUCTED;
   1.216 +	return object.get();
   1.217 +}
   1.218 +
   1.219 +// ------------------------------------------------------------------------------------------------
   1.220 +Object::Object(uint64_t id, const Element& element, const std::string& name)
   1.221 +: element(element)
   1.222 +, name(name)
   1.223 +, id(id)
   1.224 +{
   1.225 +
   1.226 +}
   1.227 +
   1.228 +// ------------------------------------------------------------------------------------------------
   1.229 +Object::~Object()
   1.230 +{
   1.231 +
   1.232 +}
   1.233 +
   1.234 +
   1.235 +// ------------------------------------------------------------------------------------------------
   1.236 +FileGlobalSettings::FileGlobalSettings(const Document& doc, boost::shared_ptr<const PropertyTable> props)
   1.237 +: props(props)
   1.238 +, doc(doc) 
   1.239 +{
   1.240 +
   1.241 +}
   1.242 +
   1.243 +
   1.244 +// ------------------------------------------------------------------------------------------------
   1.245 +FileGlobalSettings::~FileGlobalSettings()
   1.246 +{
   1.247 +
   1.248 +}
   1.249 +
   1.250 +
   1.251 +// ------------------------------------------------------------------------------------------------
   1.252 +Document::Document(const Parser& parser, const ImportSettings& settings)
   1.253 +: settings(settings)
   1.254 +, parser(parser)
   1.255 +{
   1.256 +	// cannot use array default initialization syntax because vc8 fails on it
   1.257 +	for (unsigned int i = 0; i < 7; ++i) {
   1.258 +		creationTimeStamp[i] = 0;
   1.259 +	}
   1.260 +
   1.261 +	ReadHeader();
   1.262 +	ReadPropertyTemplates();
   1.263 +
   1.264 +	ReadGlobalSettings();
   1.265 +
   1.266 +	// this order is important, connections need parsed objects to check
   1.267 +	// whether connections are ok or not. Objects may not be evaluated yet,
   1.268 +	// though, since this may require valid connections.
   1.269 +	ReadObjects();
   1.270 +	ReadConnections();
   1.271 +}
   1.272 +
   1.273 +
   1.274 +// ------------------------------------------------------------------------------------------------
   1.275 +Document::~Document()
   1.276 +{
   1.277 +	BOOST_FOREACH(ObjectMap::value_type& v, objects) {
   1.278 +		delete v.second;
   1.279 +	}
   1.280 +}
   1.281 +
   1.282 +
   1.283 +// ------------------------------------------------------------------------------------------------
   1.284 +void Document::ReadHeader()
   1.285 +{
   1.286 +	// read ID objects from "Objects" section
   1.287 +	const Scope& sc = parser.GetRootScope();
   1.288 +	const Element* const ehead = sc["FBXHeaderExtension"];
   1.289 +	if(!ehead || !ehead->Compound()) {
   1.290 +		DOMError("no FBXHeaderExtension dictionary found");
   1.291 +	}
   1.292 +
   1.293 +	const Scope& shead = *ehead->Compound();
   1.294 +	fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0));
   1.295 +
   1.296 +	
   1.297 +	if(fbxVersion < 7200 || fbxVersion > 7300) {
   1.298 +		if(Settings().strictMode) {
   1.299 +			DOMError("unsupported format version, supported are only FBX 2012 and FBX 2013"\
   1.300 +				" in ASCII format (turn off strict mode to try anyhow) ");
   1.301 +		}
   1.302 +		else {
   1.303 +			DOMWarning("unsupported format version, supported are only FBX 2012 and FBX 2013, trying to read it nevertheless");
   1.304 +		}
   1.305 +	}
   1.306 +	
   1.307 +
   1.308 +	const Element* const ecreator = shead["Creator"];
   1.309 +	if(ecreator) {
   1.310 +		creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
   1.311 +	}
   1.312 +
   1.313 +	const Element* const etimestamp = shead["CreationTimeStamp"];
   1.314 +	if(etimestamp && etimestamp->Compound()) {
   1.315 +		const Scope& stimestamp = *etimestamp->Compound();
   1.316 +		creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0));
   1.317 +		creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0));
   1.318 +		creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0));
   1.319 +		creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0));
   1.320 +		creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0));
   1.321 +		creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0));
   1.322 +		creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0));
   1.323 +	}
   1.324 +}
   1.325 +
   1.326 +// ------------------------------------------------------------------------------------------------
   1.327 +void Document::ReadGlobalSettings()
   1.328 +{
   1.329 +	const Scope& sc = parser.GetRootScope();
   1.330 +	const Element* const ehead = sc["GlobalSettings"];
   1.331 +	if(!ehead || !ehead->Compound()) {
   1.332 +		DOMWarning("no GlobalSettings dictionary found");
   1.333 +
   1.334 +		globals.reset(new FileGlobalSettings(*this, boost::make_shared<const PropertyTable>()));
   1.335 +		return;
   1.336 +	}
   1.337 +
   1.338 +	boost::shared_ptr<const PropertyTable> props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true);
   1.339 +
   1.340 +	if(!props) {
   1.341 +		DOMError("GlobalSettings dictionary contains no property table");
   1.342 +	}
   1.343 +
   1.344 +	globals.reset(new FileGlobalSettings(*this, props));
   1.345 +}
   1.346 +
   1.347 +
   1.348 +// ------------------------------------------------------------------------------------------------
   1.349 +void Document::ReadObjects()
   1.350 +{
   1.351 +	// read ID objects from "Objects" section
   1.352 +	const Scope& sc = parser.GetRootScope();
   1.353 +	const Element* const eobjects = sc["Objects"];
   1.354 +	if(!eobjects || !eobjects->Compound()) {
   1.355 +		DOMError("no Objects dictionary found");
   1.356 +	}
   1.357 +
   1.358 +	// add a dummy entry to represent the Model::RootNode object (id 0),
   1.359 +	// which is only indirectly defined in the input file
   1.360 +	objects[0] = new LazyObject(0L, *eobjects, *this);
   1.361 +
   1.362 +	const Scope& sobjects = *eobjects->Compound();
   1.363 +	BOOST_FOREACH(const ElementMap::value_type& el, sobjects.Elements()) {
   1.364 +		
   1.365 +		// extract ID 
   1.366 +		const TokenList& tok = el.second->Tokens();
   1.367 +		
   1.368 +		if (tok.empty()) {
   1.369 +			DOMError("expected ID after object key",el.second);
   1.370 +		}
   1.371 +
   1.372 +		const char* err;
   1.373 +
   1.374 +		const uint64_t id = ParseTokenAsID(*tok[0], err);
   1.375 +		if(err) {
   1.376 +			DOMError(err,el.second);
   1.377 +		}
   1.378 +
   1.379 +		// id=0 is normally implicit
   1.380 +		if(id == 0L) {
   1.381 +			DOMError("encountered object with implicitly defined id 0",el.second);
   1.382 +		}
   1.383 +
   1.384 +		if(objects.find(id) != objects.end()) {
   1.385 +			DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
   1.386 +		}
   1.387 +
   1.388 +		objects[id] = new LazyObject(id, *el.second, *this);
   1.389 +
   1.390 +		// grab all animation stacks upfront since there is no listing of them
   1.391 +		if(!strcmp(el.first.c_str(),"AnimationStack")) {
   1.392 +			animationStacks.push_back(id);
   1.393 +		}
   1.394 +	}
   1.395 +}
   1.396 +
   1.397 +
   1.398 +// ------------------------------------------------------------------------------------------------
   1.399 +void Document::ReadPropertyTemplates()
   1.400 +{
   1.401 +	const Scope& sc = parser.GetRootScope();
   1.402 +	// read property templates from "Definitions" section
   1.403 +	const Element* const edefs = sc["Definitions"];
   1.404 +	if(!edefs || !edefs->Compound()) {
   1.405 +		DOMWarning("no Definitions dictionary found");
   1.406 +		return;
   1.407 +	}
   1.408 +
   1.409 +	const Scope& sdefs = *edefs->Compound();
   1.410 +	const ElementCollection otypes = sdefs.GetCollection("ObjectType");
   1.411 +	for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
   1.412 +		const Element& el = *(*it).second;
   1.413 +		const Scope* sc = el.Compound();
   1.414 +		if(!sc) {
   1.415 +			DOMWarning("expected nested scope in ObjectType, ignoring",&el);
   1.416 +			continue;
   1.417 +		}
   1.418 +
   1.419 +		const TokenList& tok = el.Tokens();
   1.420 +		if(tok.empty()) {
   1.421 +			DOMWarning("expected name for ObjectType element, ignoring",&el);
   1.422 +			continue;
   1.423 +		}
   1.424 +
   1.425 +		const std::string& oname = ParseTokenAsString(*tok[0]);
   1.426 +
   1.427 +		const ElementCollection templs = sc->GetCollection("PropertyTemplate");
   1.428 +		for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
   1.429 +			const Element& el = *(*it).second;
   1.430 +			const Scope* sc = el.Compound();
   1.431 +			if(!sc) {
   1.432 +				DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
   1.433 +				continue;
   1.434 +			}
   1.435 +
   1.436 +			const TokenList& tok = el.Tokens();
   1.437 +			if(tok.empty()) {
   1.438 +				DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
   1.439 +				continue;
   1.440 +			}
   1.441 +
   1.442 +			const std::string& pname = ParseTokenAsString(*tok[0]);
   1.443 +
   1.444 +			const Element* Properties70 = (*sc)["Properties70"];
   1.445 +			if(Properties70) {
   1.446 +				boost::shared_ptr<const PropertyTable> props = boost::make_shared<const PropertyTable>(
   1.447 +					*Properties70,boost::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
   1.448 +				);
   1.449 +
   1.450 +				templates[oname+"."+pname] = props;
   1.451 +			}
   1.452 +		}
   1.453 +	}
   1.454 +}
   1.455 +
   1.456 +
   1.457 +
   1.458 +// ------------------------------------------------------------------------------------------------
   1.459 +void Document::ReadConnections()
   1.460 +{
   1.461 +	const Scope& sc = parser.GetRootScope();
   1.462 +	// read property templates from "Definitions" section
   1.463 +	const Element* const econns = sc["Connections"];
   1.464 +	if(!econns || !econns->Compound()) {
   1.465 +		DOMError("no Connections dictionary found");
   1.466 +	}
   1.467 +
   1.468 +	uint64_t insertionOrder = 0l;
   1.469 +
   1.470 +	const Scope& sconns = *econns->Compound();
   1.471 +	const ElementCollection conns = sconns.GetCollection("C");
   1.472 +	for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
   1.473 +		const Element& el = *(*it).second;
   1.474 +		const std::string& type = ParseTokenAsString(GetRequiredToken(el,0));
   1.475 +		const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
   1.476 +		const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
   1.477 +
   1.478 +		// OO = object-object connection
   1.479 +		// OP = object-property connection, in which case the destination property follows the object ID
   1.480 +		const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : "");
   1.481 +
   1.482 +		if(objects.find(src) == objects.end()) {
   1.483 +			DOMWarning("source object for connection does not exist",&el);
   1.484 +			continue;
   1.485 +		}
   1.486 +
   1.487 +		// dest may be 0 (root node) but we added a dummy object before
   1.488 +		if(objects.find(dest) == objects.end()) {
   1.489 +			DOMWarning("destination object for connection does not exist",&el);
   1.490 +			continue;
   1.491 +		}
   1.492 +
   1.493 +		// add new connection
   1.494 +		const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
   1.495 +		src_connections.insert(ConnectionMap::value_type(src,c));  
   1.496 +		dest_connections.insert(ConnectionMap::value_type(dest,c));  
   1.497 +	}
   1.498 +}
   1.499 +
   1.500 +
   1.501 +// ------------------------------------------------------------------------------------------------
   1.502 +const std::vector<const AnimationStack*>& Document::AnimationStacks() const
   1.503 +{
   1.504 +	if (!animationStacksResolved.empty() || !animationStacks.size()) {
   1.505 +		return animationStacksResolved;
   1.506 +	}
   1.507 +
   1.508 +	animationStacksResolved.reserve(animationStacks.size());
   1.509 +	BOOST_FOREACH(uint64_t id, animationStacks) {
   1.510 +		LazyObject* const lazy = GetObject(id);
   1.511 +		const AnimationStack* stack;
   1.512 +		if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
   1.513 +			DOMWarning("failed to read AnimationStack object");
   1.514 +			continue;
   1.515 +		}
   1.516 +		animationStacksResolved.push_back(stack);
   1.517 +	}
   1.518 +
   1.519 +	return animationStacksResolved;
   1.520 +}
   1.521 +
   1.522 +
   1.523 +// ------------------------------------------------------------------------------------------------
   1.524 +LazyObject* Document::GetObject(uint64_t id) const
   1.525 +{
   1.526 +	ObjectMap::const_iterator it = objects.find(id);
   1.527 +	return it == objects.end() ? NULL : (*it).second;
   1.528 +}
   1.529 +
   1.530 +#define MAX_CLASSNAMES 6
   1.531 +
   1.532 +// ------------------------------------------------------------------------------------------------
   1.533 +std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, 
   1.534 +	const ConnectionMap& conns) const
   1.535 +{
   1.536 +	std::vector<const Connection*> temp;
   1.537 +
   1.538 +	const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = 
   1.539 +		conns.equal_range(id);
   1.540 +
   1.541 +	temp.reserve(std::distance(range.first,range.second));
   1.542 +	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
   1.543 +		temp.push_back((*it).second);
   1.544 +	}
   1.545 +
   1.546 +	std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
   1.547 +
   1.548 +	return temp; // NRVO should handle this
   1.549 +}
   1.550 +
   1.551 +
   1.552 +// ------------------------------------------------------------------------------------------------
   1.553 +std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src, 
   1.554 +	const ConnectionMap& conns, 
   1.555 +	const char* const* classnames, 
   1.556 +	size_t count) const
   1.557 +
   1.558 +{
   1.559 +	ai_assert(classnames);
   1.560 +	ai_assert(count != 0 && count <= MAX_CLASSNAMES);
   1.561 +
   1.562 +	size_t lenghts[MAX_CLASSNAMES];
   1.563 +
   1.564 +	const size_t c = count;
   1.565 +	for (size_t i = 0; i < c; ++i) {
   1.566 +		lenghts[i] = strlen(classnames[i]);
   1.567 +	}
   1.568 +
   1.569 +	std::vector<const Connection*> temp;
   1.570 +
   1.571 +	const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = 
   1.572 +		conns.equal_range(id);
   1.573 +
   1.574 +	temp.reserve(std::distance(range.first,range.second));
   1.575 +	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
   1.576 +		const Token& key = (is_src 
   1.577 +			? (*it).second->LazyDestinationObject()
   1.578 +			: (*it).second->LazySourceObject()
   1.579 +		).GetElement().KeyToken();
   1.580 +
   1.581 +		const char* obtype = key.begin();
   1.582 +
   1.583 +		for (size_t i = 0; i < c; ++i) {
   1.584 +			ai_assert(classnames[i]);
   1.585 +			if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
   1.586 +				obtype = NULL;
   1.587 +				break;
   1.588 +			}
   1.589 +		}
   1.590 +
   1.591 +		if(obtype) {
   1.592 +			continue;
   1.593 +		}
   1.594 +
   1.595 +		temp.push_back((*it).second);
   1.596 +	}
   1.597 +
   1.598 +	std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
   1.599 +	return temp; // NRVO should handle this
   1.600 +}
   1.601 +
   1.602 +
   1.603 +// ------------------------------------------------------------------------------------------------
   1.604 +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
   1.605 +{
   1.606 +	return GetConnectionsSequenced(source, ConnectionsBySource());
   1.607 +}
   1.608 +
   1.609 +
   1.610 +
   1.611 +// ------------------------------------------------------------------------------------------------
   1.612 +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest, 
   1.613 +	const char* classname) const
   1.614 +{
   1.615 +	const char* arr[] = {classname};
   1.616 +	return GetConnectionsBySourceSequenced(dest, arr,1);
   1.617 +}
   1.618 +
   1.619 +
   1.620 +
   1.621 +// ------------------------------------------------------------------------------------------------
   1.622 +std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source, 
   1.623 +	const char* const* classnames, size_t count) const
   1.624 +{
   1.625 +	return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
   1.626 +}
   1.627 +
   1.628 +
   1.629 +// ------------------------------------------------------------------------------------------------
   1.630 +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, 
   1.631 +	const char* classname) const
   1.632 +{
   1.633 +	const char* arr[] = {classname};
   1.634 +	return GetConnectionsByDestinationSequenced(dest, arr,1);
   1.635 +}
   1.636 +
   1.637 +
   1.638 +// ------------------------------------------------------------------------------------------------
   1.639 +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
   1.640 +{
   1.641 +	return GetConnectionsSequenced(dest, ConnectionsByDestination());
   1.642 +}
   1.643 +
   1.644 +
   1.645 +// ------------------------------------------------------------------------------------------------
   1.646 +std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, 
   1.647 +	const char* const* classnames, size_t count) const
   1.648 +
   1.649 +{
   1.650 +	return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
   1.651 +}
   1.652 +
   1.653 +
   1.654 +// ------------------------------------------------------------------------------------------------
   1.655 +Connection::Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop, 
   1.656 +	const Document& doc)
   1.657 +
   1.658 +: insertionOrder(insertionOrder)
   1.659 +, prop(prop)
   1.660 +, src(src)
   1.661 +, dest(dest)
   1.662 +, doc(doc)
   1.663 +{
   1.664 +	ai_assert(doc.Objects().find(src) != doc.Objects().end());
   1.665 +	// dest may be 0 (root node)
   1.666 +	ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
   1.667 +}
   1.668 +
   1.669 +
   1.670 +// ------------------------------------------------------------------------------------------------
   1.671 +Connection::~Connection()
   1.672 +{
   1.673 +
   1.674 +}
   1.675 +
   1.676 +
   1.677 +// ------------------------------------------------------------------------------------------------
   1.678 +LazyObject& Connection::LazySourceObject() const
   1.679 +{
   1.680 +	LazyObject* const lazy = doc.GetObject(src);
   1.681 +	ai_assert(lazy);
   1.682 +	return *lazy;
   1.683 +}
   1.684 +
   1.685 +
   1.686 +// ------------------------------------------------------------------------------------------------
   1.687 +LazyObject& Connection::LazyDestinationObject() const
   1.688 +{
   1.689 +	LazyObject* const lazy = doc.GetObject(dest);
   1.690 +	ai_assert(lazy);
   1.691 +	return *lazy;
   1.692 +}
   1.693 +
   1.694 +
   1.695 +// ------------------------------------------------------------------------------------------------
   1.696 +const Object* Connection::SourceObject() const
   1.697 +{
   1.698 +	LazyObject* const lazy = doc.GetObject(src);
   1.699 +	ai_assert(lazy);
   1.700 +	return lazy->Get();
   1.701 +}
   1.702 +
   1.703 +
   1.704 +// ------------------------------------------------------------------------------------------------
   1.705 +const Object* Connection::DestinationObject() const
   1.706 +{
   1.707 +	LazyObject* const lazy = doc.GetObject(dest);
   1.708 +	ai_assert(lazy);
   1.709 +	return lazy->Get();
   1.710 +}
   1.711 +
   1.712 +} // !FBX
   1.713 +} // !Assimp
   1.714 +
   1.715 +#endif
   1.716 +