vrshoot

diff libs/assimp/FBXConverter.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/FBXConverter.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,2750 @@
     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  FBXConverter.cpp
    1.45 + *  @brief Implementation of the FBX DOM -> aiScene converter
    1.46 + */
    1.47 +#include "AssimpPCH.h"
    1.48 +
    1.49 +#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
    1.50 +
    1.51 +#include <iterator>
    1.52 +#include <boost/tuple/tuple.hpp>
    1.53 +
    1.54 +#include "FBXParser.h"
    1.55 +#include "FBXConverter.h"
    1.56 +#include "FBXDocument.h"
    1.57 +#include "FBXUtil.h"
    1.58 +#include "FBXProperties.h"
    1.59 +#include "FBXImporter.h"
    1.60 +
    1.61 +namespace Assimp {
    1.62 +namespace FBX {
    1.63 +
    1.64 +	using namespace Util;
    1.65 +
    1.66 +
    1.67 +#define MAGIC_NODE_TAG "_$AssimpFbx$"
    1.68 +#define MAGIC_NULL_TAG "_$AssimpFbxNull$"
    1.69 +
    1.70 +#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
    1.71 +
    1.72 +	// XXX vc9's debugger won't step into anonymous namespaces
    1.73 +//namespace {
    1.74 +
    1.75 +/** Dummy class to encapsulate the conversion process */
    1.76 +class Converter
    1.77 +{
    1.78 +public:
    1.79 +
    1.80 +	/** the different parts that make up the final local transformation of a fbx node */
    1.81 +	enum TransformationComp
    1.82 +	{
    1.83 +		TransformationComp_Translation = 0,
    1.84 +		TransformationComp_RotationOffset,
    1.85 +		TransformationComp_RotationPivot,
    1.86 +		TransformationComp_PreRotation,
    1.87 +		TransformationComp_Rotation,
    1.88 +		TransformationComp_PostRotation,
    1.89 +		TransformationComp_RotationPivotInverse,
    1.90 +		TransformationComp_ScalingOffset,
    1.91 +		TransformationComp_ScalingPivot,
    1.92 +		TransformationComp_Scaling,
    1.93 +		TransformationComp_ScalingPivotInverse,
    1.94 +
    1.95 +		TransformationComp_MAXIMUM
    1.96 +	};
    1.97 +
    1.98 +
    1.99 +
   1.100 +public:
   1.101 +
   1.102 +	Converter(aiScene* out, const Document& doc)
   1.103 +		: defaultMaterialIndex()
   1.104 +		, out(out) 
   1.105 +		, doc(doc)
   1.106 +	{
   1.107 +		// animations need to be converted first since this will
   1.108 +		// populate the node_anim_chain_bits map, which is needed
   1.109 +		// to determine which nodes need to be generated.
   1.110 +		ConvertAnimations();
   1.111 +		ConvertRootNode();
   1.112 +
   1.113 +		if(doc.Settings().readAllMaterials) {
   1.114 +			// unfortunately this means we have to evaluate all objects
   1.115 +			BOOST_FOREACH(const ObjectMap::value_type& v,doc.Objects()) {
   1.116 +
   1.117 +				const Object* ob = v.second->Get();
   1.118 +				if(!ob) {
   1.119 +					continue;
   1.120 +				}
   1.121 +
   1.122 +				const Material* mat = dynamic_cast<const Material*>(ob);
   1.123 +				if(mat) {
   1.124 +
   1.125 +					if (materials_converted.find(mat) == materials_converted.end()) {
   1.126 +						ConvertMaterial(*mat);
   1.127 +					}
   1.128 +				}
   1.129 +			}
   1.130 +		}
   1.131 +
   1.132 +		TransferDataToScene();
   1.133 +
   1.134 +		// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
   1.135 +		// to make sure the scene passes assimp's validation. FBX files
   1.136 +		// need not contain geometry (i.e. camera animations, raw armatures).
   1.137 +		if (out->mNumMeshes == 0) {
   1.138 +			out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
   1.139 +		}
   1.140 +	}
   1.141 +
   1.142 +
   1.143 +	~Converter()
   1.144 +	{
   1.145 +		std::for_each(meshes.begin(),meshes.end(),Util::delete_fun<aiMesh>());
   1.146 +		std::for_each(materials.begin(),materials.end(),Util::delete_fun<aiMaterial>());
   1.147 +		std::for_each(animations.begin(),animations.end(),Util::delete_fun<aiAnimation>());
   1.148 +		std::for_each(lights.begin(),lights.end(),Util::delete_fun<aiLight>());
   1.149 +		std::for_each(cameras.begin(),cameras.end(),Util::delete_fun<aiCamera>());
   1.150 +	}
   1.151 +
   1.152 +
   1.153 +private:
   1.154 +
   1.155 +	// ------------------------------------------------------------------------------------------------
   1.156 +	// find scene root and trigger recursive scene conversion
   1.157 +	void ConvertRootNode() 
   1.158 +	{
   1.159 +		out->mRootNode = new aiNode();
   1.160 +		out->mRootNode->mName.Set("RootNode");
   1.161 +
   1.162 +		// root has ID 0
   1.163 +		ConvertNodes(0L, *out->mRootNode);
   1.164 +	}
   1.165 +
   1.166 +
   1.167 +	// ------------------------------------------------------------------------------------------------
   1.168 +	// collect and assign child nodes
   1.169 +	void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4())
   1.170 +	{
   1.171 +		const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
   1.172 +
   1.173 +		std::vector<aiNode*> nodes;
   1.174 +		nodes.reserve(conns.size());
   1.175 +
   1.176 +		std::vector<aiNode*> nodes_chain;
   1.177 +
   1.178 +		try {
   1.179 +			BOOST_FOREACH(const Connection* con, conns) {
   1.180 +
   1.181 +				// ignore object-property links
   1.182 +				if(con->PropertyName().length()) {
   1.183 +					continue;
   1.184 +				}
   1.185 +
   1.186 +				const Object* const object = con->SourceObject();
   1.187 +				if(!object) {
   1.188 +					FBXImporter::LogWarn("failed to convert source object for Model link");
   1.189 +					continue;
   1.190 +				}
   1.191 +
   1.192 +				const Model* const model = dynamic_cast<const Model*>(object);
   1.193 +
   1.194 +				if(model) {
   1.195 +					nodes_chain.clear();
   1.196 +
   1.197 +					aiMatrix4x4 new_abs_transform = parent_transform;
   1.198 +
   1.199 +					// even though there is only a single input node, the design of
   1.200 +					// assimp (or rather: the complicated transformation chain that
   1.201 +					// is employed by fbx) means that we may need multiple aiNode's
   1.202 +					// to represent a fbx node's transformation.
   1.203 +					GenerateTransformationNodeChain(*model,nodes_chain);
   1.204 +
   1.205 +					ai_assert(nodes_chain.size());
   1.206 +
   1.207 +					const std::string& original_name = FixNodeName(model->Name());
   1.208 +
   1.209 +					// check if any of the nodes in the chain has the name the fbx node
   1.210 +					// is supposed to have. If there is none, add another node to 
   1.211 +					// preserve the name - people might have scripts etc. that rely
   1.212 +					// on specific node names.
   1.213 +					aiNode* name_carrier = NULL;
   1.214 +					BOOST_FOREACH(aiNode* prenode, nodes_chain) {
   1.215 +						if ( !strcmp(prenode->mName.C_Str(), original_name.c_str()) ) {
   1.216 +							name_carrier = prenode;
   1.217 +							break;
   1.218 +						}
   1.219 +					}
   1.220 +
   1.221 +					if(!name_carrier) {
   1.222 +						nodes_chain.push_back(new aiNode(original_name));
   1.223 +						name_carrier = nodes_chain.back();
   1.224 +					}
   1.225 +
   1.226 +					// link all nodes in a row
   1.227 +					aiNode* last_parent = &parent;
   1.228 +					BOOST_FOREACH(aiNode* prenode, nodes_chain) {
   1.229 +						ai_assert(prenode);
   1.230 +
   1.231 +						if(last_parent != &parent) {
   1.232 +							last_parent->mNumChildren = 1;
   1.233 +							last_parent->mChildren = new aiNode*[1];
   1.234 +							last_parent->mChildren[0] = prenode;
   1.235 +						}
   1.236 +
   1.237 +						prenode->mParent = last_parent;
   1.238 +						last_parent = prenode;
   1.239 +
   1.240 +						new_abs_transform *= prenode->mTransformation;
   1.241 +					}
   1.242 +
   1.243 +					// attach geometry
   1.244 +					ConvertModel(*model, *nodes_chain.back(), new_abs_transform);
   1.245 +
   1.246 +					// attach sub-nodes
   1.247 +					ConvertNodes(model->ID(), *nodes_chain.back(), new_abs_transform);
   1.248 +
   1.249 +					if(doc.Settings().readLights) {
   1.250 +						ConvertLights(*model);
   1.251 +					}
   1.252 +
   1.253 +					if(doc.Settings().readCameras) {
   1.254 +						ConvertCameras(*model);
   1.255 +					}
   1.256 +
   1.257 +					// preserve the info that a node was marked as Null node
   1.258 +					// in the original file.
   1.259 +					if(model->IsNull()) {
   1.260 +						const std::string& new_name = original_name + MAGIC_NULL_TAG;
   1.261 +						RenameNode(original_name, new_name);
   1.262 +						name_carrier->mName.Set( new_name.c_str() );
   1.263 +					}
   1.264 +
   1.265 +					nodes.push_back(nodes_chain.front());	
   1.266 +					nodes_chain.clear();
   1.267 +				}
   1.268 +			}
   1.269 +
   1.270 +			if(nodes.size()) {
   1.271 +				parent.mChildren = new aiNode*[nodes.size()]();
   1.272 +				parent.mNumChildren = static_cast<unsigned int>(nodes.size());
   1.273 +
   1.274 +				std::swap_ranges(nodes.begin(),nodes.end(),parent.mChildren);
   1.275 +			}
   1.276 +		} 
   1.277 +		catch(std::exception&)	{
   1.278 +			Util::delete_fun<aiNode> deleter;
   1.279 +			std::for_each(nodes.begin(),nodes.end(),deleter);
   1.280 +			std::for_each(nodes_chain.begin(),nodes_chain.end(),deleter);
   1.281 +		}
   1.282 +	}
   1.283 +
   1.284 +
   1.285 +	// ------------------------------------------------------------------------------------------------
   1.286 +	void ConvertLights(const Model& model)
   1.287 +	{
   1.288 +		const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
   1.289 +		BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
   1.290 +			const Light* const light = dynamic_cast<const Light*>(attr);
   1.291 +			if(light) {
   1.292 +				ConvertLight(model, *light);
   1.293 +			}
   1.294 +		}
   1.295 +	}
   1.296 +
   1.297 +
   1.298 +	// ------------------------------------------------------------------------------------------------
   1.299 +	void ConvertCameras(const Model& model)
   1.300 +	{
   1.301 +		const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
   1.302 +		BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
   1.303 +			const Camera* const cam = dynamic_cast<const Camera*>(attr);
   1.304 +			if(cam) {
   1.305 +				ConvertCamera(model, *cam);
   1.306 +			}
   1.307 +		}
   1.308 +	}
   1.309 +
   1.310 +
   1.311 +	// ------------------------------------------------------------------------------------------------
   1.312 +	void ConvertLight(const Model& model, const Light& light)
   1.313 +	{
   1.314 +		lights.push_back(new aiLight());
   1.315 +		aiLight* const out_light = lights.back();
   1.316 +
   1.317 +		out_light->mName.Set(FixNodeName(model.Name()));
   1.318 +
   1.319 +		const float intensity = light.Intensity();
   1.320 +		const aiVector3D& col = light.Color();
   1.321 +
   1.322 +		out_light->mColorDiffuse = aiColor3D(col.x,col.y,col.z);
   1.323 +		out_light->mColorDiffuse.r *= intensity;
   1.324 +		out_light->mColorDiffuse.g *= intensity;
   1.325 +		out_light->mColorDiffuse.b *= intensity;
   1.326 +
   1.327 +		out_light->mColorSpecular = out_light->mColorDiffuse;
   1.328 +
   1.329 +		switch(light.LightType())
   1.330 +		{
   1.331 +		case Light::Type_Point:
   1.332 +			out_light->mType = aiLightSource_POINT;
   1.333 +			break;
   1.334 +
   1.335 +		case Light::Type_Directional:
   1.336 +			out_light->mType = aiLightSource_DIRECTIONAL;
   1.337 +			break;
   1.338 +
   1.339 +		case Light::Type_Spot:
   1.340 +			out_light->mType = aiLightSource_SPOT;
   1.341 +			out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle());
   1.342 +			out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle());
   1.343 +			break;
   1.344 +
   1.345 +		case Light::Type_Area:
   1.346 +			FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED");
   1.347 +			out_light->mType = aiLightSource_UNDEFINED;
   1.348 +			break;
   1.349 +
   1.350 +		case Light::Type_Volume:
   1.351 +			FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED");
   1.352 +			out_light->mType = aiLightSource_UNDEFINED;
   1.353 +			break;
   1.354 +		default:
   1.355 +			ai_assert(false);
   1.356 +		}
   1.357 +
   1.358 +		// XXX: how to best convert the near and far decay ranges?
   1.359 +		switch(light.DecayType())
   1.360 +		{
   1.361 +		case Light::Decay_None:
   1.362 +			out_light->mAttenuationConstant = 1.0f;
   1.363 +			break;
   1.364 +		case Light::Decay_Linear:
   1.365 +			out_light->mAttenuationLinear = 1.0f;
   1.366 +			break;
   1.367 +		case Light::Decay_Quadratic:
   1.368 +			out_light->mAttenuationQuadratic = 1.0f;
   1.369 +			break;
   1.370 +		case Light::Decay_Cubic:
   1.371 +			FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic");
   1.372 +			out_light->mAttenuationQuadratic = 1.0f;
   1.373 +			break;
   1.374 +		default:
   1.375 +			ai_assert(false);
   1.376 +		}
   1.377 +	}
   1.378 +
   1.379 +
   1.380 +	// ------------------------------------------------------------------------------------------------
   1.381 +	void ConvertCamera(const Model& model, const Camera& cam)
   1.382 +	{
   1.383 +		cameras.push_back(new aiCamera());
   1.384 +		aiCamera* const out_camera = cameras.back();
   1.385 +
   1.386 +		out_camera->mName.Set(FixNodeName(model.Name()));
   1.387 +
   1.388 +		out_camera->mAspect = cam.AspectWidth();
   1.389 +		out_camera->mPosition = cam.Position();
   1.390 +		out_camera->mLookAt = cam.InterestPosition() - out_camera->mPosition;
   1.391 +
   1.392 +		out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
   1.393 +	}
   1.394 +
   1.395 +
   1.396 +	// ------------------------------------------------------------------------------------------------
   1.397 +	// this returns unified names usable within assimp identifiers (i.e. no space characters -
   1.398 +	// while these would be allowed, they are a potential trouble spot so better not use them).
   1.399 +	const char* NameTransformationComp(TransformationComp comp)
   1.400 +	{
   1.401 +		switch(comp)
   1.402 +		{
   1.403 +		case TransformationComp_Translation:
   1.404 +			return "Translation";
   1.405 +		case TransformationComp_RotationOffset:
   1.406 +			return "RotationOffset";
   1.407 +		case TransformationComp_RotationPivot:
   1.408 +			return "RotationPivot";
   1.409 +		case TransformationComp_PreRotation:
   1.410 +			return "PreRotation";
   1.411 +		case TransformationComp_Rotation:
   1.412 +			return "Rotation";
   1.413 +		case TransformationComp_PostRotation:
   1.414 +			return "PostRotation";
   1.415 +		case TransformationComp_RotationPivotInverse:
   1.416 +			return "RotationPivotInverse";
   1.417 +		case TransformationComp_ScalingOffset:
   1.418 +			return "ScalingOffset";
   1.419 +		case TransformationComp_ScalingPivot:
   1.420 +			return "ScalingPivot";
   1.421 +		case TransformationComp_Scaling:
   1.422 +			return "Scaling";
   1.423 +		case TransformationComp_ScalingPivotInverse:
   1.424 +			return "ScalingPivotInverse";
   1.425 +		case TransformationComp_MAXIMUM: // this is to silence compiler warnings
   1.426 +			break;
   1.427 +		}
   1.428 +
   1.429 +		ai_assert(false);
   1.430 +		return NULL;
   1.431 +	}
   1.432 +
   1.433 +
   1.434 +	// ------------------------------------------------------------------------------------------------
   1.435 +	// note: this returns the REAL fbx property names
   1.436 +	const char* NameTransformationCompProperty(TransformationComp comp)
   1.437 +	{
   1.438 +		switch(comp)
   1.439 +		{
   1.440 +		case TransformationComp_Translation:
   1.441 +			return "Lcl Translation";
   1.442 +		case TransformationComp_RotationOffset:
   1.443 +			return "RotationOffset";
   1.444 +		case TransformationComp_RotationPivot:
   1.445 +			return "RotationPivot";
   1.446 +		case TransformationComp_PreRotation:
   1.447 +			return "PreRotation";
   1.448 +		case TransformationComp_Rotation:
   1.449 +			return "Lcl Rotation";
   1.450 +		case TransformationComp_PostRotation:
   1.451 +			return "PostRotation";
   1.452 +		case TransformationComp_RotationPivotInverse:
   1.453 +			return "RotationPivotInverse";
   1.454 +		case TransformationComp_ScalingOffset:
   1.455 +			return "ScalingOffset";
   1.456 +		case TransformationComp_ScalingPivot:
   1.457 +			return "ScalingPivot";
   1.458 +		case TransformationComp_Scaling:
   1.459 +			return "Lcl Scaling";
   1.460 +		case TransformationComp_ScalingPivotInverse:
   1.461 +			return "ScalingPivotInverse";
   1.462 +		case TransformationComp_MAXIMUM: // this is to silence compiler warnings
   1.463 +			break;
   1.464 +		}
   1.465 +
   1.466 +		ai_assert(false);
   1.467 +		return NULL;
   1.468 +	}
   1.469 +
   1.470 +
   1.471 +	// ------------------------------------------------------------------------------------------------
   1.472 +	aiVector3D TransformationCompDefaultValue(TransformationComp comp)
   1.473 +	{
   1.474 +		// XXX a neat way to solve the never-ending special cases for scaling 
   1.475 +		// would be to do everything in log space!
   1.476 +		return comp == TransformationComp_Scaling ? aiVector3D(1.f,1.f,1.f) : aiVector3D();
   1.477 +	}
   1.478 +
   1.479 +
   1.480 +	// ------------------------------------------------------------------------------------------------
   1.481 +	void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out)
   1.482 +	{
   1.483 +		if(mode == Model::RotOrder_SphericXYZ) {
   1.484 +			FBXImporter::LogError("Unsupported RotationMode: SphericXYZ");
   1.485 +			out = aiMatrix4x4();
   1.486 +			return;
   1.487 +		}
   1.488 +
   1.489 +		const float angle_epsilon = 1e-6f;
   1.490 +
   1.491 +		out = aiMatrix4x4();
   1.492 +
   1.493 +		bool is_id[3] = { true, true, true };
   1.494 +
   1.495 +		aiMatrix4x4 temp[3];
   1.496 +		if(fabs(rotation.z) > angle_epsilon) {
   1.497 +			aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z),temp[2]);
   1.498 +			is_id[2] = false;
   1.499 +		}
   1.500 +		if(fabs(rotation.y) > angle_epsilon) {
   1.501 +			aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y),temp[1]);
   1.502 +			is_id[1] = false;
   1.503 +		}
   1.504 +		if(fabs(rotation.x) > angle_epsilon) {
   1.505 +			aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x),temp[0]);
   1.506 +			is_id[0] = false;
   1.507 +		}
   1.508 +
   1.509 +		int order[3] = {-1, -1, -1};
   1.510 +
   1.511 +		// note: rotation order is inverted since we're left multiplying as is usual in assimp
   1.512 +		switch(mode)
   1.513 +		{
   1.514 +		case Model::RotOrder_EulerXYZ:
   1.515 +			order[0] = 2;
   1.516 +			order[1] = 1;
   1.517 +			order[2] = 0;
   1.518 +			break;
   1.519 +
   1.520 +		case Model::RotOrder_EulerXZY: 
   1.521 +			order[0] = 1;
   1.522 +			order[1] = 2;
   1.523 +			order[2] = 0;
   1.524 +			break;
   1.525 +
   1.526 +		case Model::RotOrder_EulerYZX:
   1.527 +			order[0] = 0;
   1.528 +			order[1] = 2;
   1.529 +			order[2] = 1;
   1.530 +			break;
   1.531 +
   1.532 +		case Model::RotOrder_EulerYXZ: 
   1.533 +			order[0] = 2;
   1.534 +			order[1] = 0;
   1.535 +			order[2] = 1;
   1.536 +			break;
   1.537 +
   1.538 +		case Model::RotOrder_EulerZXY: 
   1.539 +			order[0] = 1;
   1.540 +			order[1] = 0;
   1.541 +			order[2] = 2;
   1.542 +			break;
   1.543 +
   1.544 +		case Model::RotOrder_EulerZYX:
   1.545 +			order[0] = 0;
   1.546 +			order[1] = 1;
   1.547 +			order[2] = 2;
   1.548 +			break;
   1.549 +
   1.550 +			default:
   1.551 +				ai_assert(false);
   1.552 +		}
   1.553 +
   1.554 +		if(!is_id[order[0]]) {
   1.555 +			out = temp[order[0]];
   1.556 +		}
   1.557 +
   1.558 +		if(!is_id[order[1]]) {
   1.559 +			out = out * temp[order[1]];
   1.560 +		}
   1.561 +
   1.562 +		if(!is_id[order[2]]) {
   1.563 +			out = out * temp[order[2]];
   1.564 +		}
   1.565 +	}
   1.566 +
   1.567 +
   1.568 +	// ------------------------------------------------------------------------------------------------
   1.569 +	/** checks if a node has more than just scaling, rotation and translation components */
   1.570 +	bool NeedsComplexTransformationChain(const Model& model)
   1.571 +	{
   1.572 +		const PropertyTable& props = model.Props();
   1.573 +		bool ok;
   1.574 +
   1.575 +		const float zero_epsilon = 1e-6f;
   1.576 +		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
   1.577 +			const TransformationComp comp = static_cast<TransformationComp>(i);
   1.578 +
   1.579 +			if(comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || 
   1.580 +				comp == TransformationComp_Translation) {
   1.581 +
   1.582 +				continue;
   1.583 +			}
   1.584 +
   1.585 +			const aiVector3D& v = PropertyGet<aiVector3D>(props,NameTransformationCompProperty(comp),ok);
   1.586 +			if(ok && v.SquareLength() > zero_epsilon) {
   1.587 +				return true;
   1.588 +			}
   1.589 +		}
   1.590 +
   1.591 +		return false;
   1.592 +	}
   1.593 +
   1.594 +
   1.595 +	// ------------------------------------------------------------------------------------------------
   1.596 +	// note: name must be a FixNodeName() result
   1.597 +	std::string NameTransformationChainNode(const std::string& name, TransformationComp comp)
   1.598 +	{
   1.599 +		return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
   1.600 +	}
   1.601 +
   1.602 +
   1.603 +	// ------------------------------------------------------------------------------------------------
   1.604 +	/** note: memory for output_nodes will be managed by the caller */
   1.605 +	void GenerateTransformationNodeChain(const Model& model, 
   1.606 +		std::vector<aiNode*>& output_nodes)
   1.607 +	{
   1.608 +		const PropertyTable& props = model.Props();
   1.609 +		const Model::RotOrder rot = model.RotationOrder();
   1.610 +
   1.611 +		bool ok;
   1.612 +
   1.613 +		aiMatrix4x4 chain[TransformationComp_MAXIMUM];
   1.614 +		std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
   1.615 +		
   1.616 +		// generate transformation matrices for all the different transformation components
   1.617 +		const float zero_epsilon = 1e-6f;
   1.618 +		bool is_complex = false;
   1.619 +
   1.620 +		const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props,"PreRotation",ok);
   1.621 +		if(ok && PreRotation.SquareLength() > zero_epsilon) {
   1.622 +			is_complex = true;
   1.623 +
   1.624 +			GetRotationMatrix(rot, PreRotation, chain[TransformationComp_PreRotation]);
   1.625 +		}
   1.626 +
   1.627 +		const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
   1.628 +		if(ok && PostRotation.SquareLength() > zero_epsilon) {
   1.629 +			is_complex = true;
   1.630 +			
   1.631 +			GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
   1.632 +		}
   1.633 +
   1.634 +		const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
   1.635 +		if(ok && RotationPivot.SquareLength() > zero_epsilon) {
   1.636 +			is_complex = true;
   1.637 +			
   1.638 +			aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
   1.639 +			aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
   1.640 +		}
   1.641 +
   1.642 +		const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
   1.643 +		if(ok && RotationOffset.SquareLength() > zero_epsilon) {
   1.644 +			is_complex = true;
   1.645 +
   1.646 +			aiMatrix4x4::Translation(RotationOffset,chain[TransformationComp_RotationOffset]);
   1.647 +		}
   1.648 +
   1.649 +		const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props,"ScalingOffset",ok);
   1.650 +		if(ok && ScalingOffset.SquareLength() > zero_epsilon) {
   1.651 +			is_complex = true;
   1.652 +			
   1.653 +			aiMatrix4x4::Translation(ScalingOffset,chain[TransformationComp_ScalingOffset]);
   1.654 +		}
   1.655 +
   1.656 +		const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props,"ScalingPivot",ok);
   1.657 +		if(ok && ScalingPivot.SquareLength() > zero_epsilon) {
   1.658 +			is_complex = true;
   1.659 +
   1.660 +			aiMatrix4x4::Translation(ScalingPivot,chain[TransformationComp_ScalingPivot]);
   1.661 +			aiMatrix4x4::Translation(-ScalingPivot,chain[TransformationComp_ScalingPivotInverse]);
   1.662 +		}
   1.663 +
   1.664 +		const aiVector3D& Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
   1.665 +		if(ok && Translation.SquareLength() > zero_epsilon) {
   1.666 +			aiMatrix4x4::Translation(Translation,chain[TransformationComp_Translation]);
   1.667 +		}
   1.668 +
   1.669 +		const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
   1.670 +		if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
   1.671 +			aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
   1.672 +		}
   1.673 +
   1.674 +		const aiVector3D& Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
   1.675 +		if(ok && Rotation.SquareLength() > zero_epsilon) {
   1.676 +			GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
   1.677 +		}
   1.678 +
   1.679 +		// is_complex needs to be consistent with NeedsComplexTransformationChain()
   1.680 +		// or the interplay between this code and the animation converter would
   1.681 +		// not be guaranteed.
   1.682 +		ai_assert(NeedsComplexTransformationChain(model) == is_complex);
   1.683 +
   1.684 +		const std::string& name = FixNodeName(model.Name());
   1.685 +
   1.686 +		// now, if we have more than just Translation, Scaling and Rotation,
   1.687 +		// we need to generate a full node chain to accommodate for assimp's
   1.688 +		// lack to express pivots and offsets.
   1.689 +		if(is_complex && doc.Settings().preservePivots) {
   1.690 +			FBXImporter::LogInfo("generating full transformation chain for node: " + name);
   1.691 +
   1.692 +			// query the anim_chain_bits dictionary to find out which chain elements
   1.693 +			// have associated node animation channels. These can not be dropped 
   1.694 +			// even if they have identity transform in bind pose.
   1.695 +			NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name);
   1.696 +			const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second);
   1.697 +
   1.698 +			unsigned int bit = 0x1;
   1.699 +			for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
   1.700 +				const TransformationComp comp = static_cast<TransformationComp>(i);
   1.701 +				
   1.702 +				if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
   1.703 +					continue;
   1.704 +				}
   1.705 +
   1.706 +				aiNode* nd = new aiNode();
   1.707 +				output_nodes.push_back(nd);
   1.708 +				
   1.709 +				nd->mName.Set(NameTransformationChainNode(name, comp));
   1.710 +				nd->mTransformation = chain[i];
   1.711 +			}
   1.712 +
   1.713 +			ai_assert(output_nodes.size());
   1.714 +			return;
   1.715 +		}
   1.716 +
   1.717 +		// else, we can just multiply the matrices together
   1.718 +		aiNode* nd = new aiNode();
   1.719 +		output_nodes.push_back(nd);
   1.720 +
   1.721 +		nd->mName.Set(name);
   1.722 +
   1.723 +		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
   1.724 +			nd->mTransformation = nd->mTransformation * chain[i];
   1.725 +		}
   1.726 +	}
   1.727 +
   1.728 +
   1.729 +	// ------------------------------------------------------------------------------------------------
   1.730 +	void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform)
   1.731 +	{
   1.732 +		const std::vector<const Geometry*>& geos = model.GetGeometry();
   1.733 +
   1.734 +		std::vector<unsigned int> meshes;
   1.735 +		meshes.reserve(geos.size());
   1.736 +
   1.737 +		BOOST_FOREACH(const Geometry* geo, geos) {
   1.738 +
   1.739 +			const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
   1.740 +			if(mesh) {
   1.741 +				const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform);
   1.742 +				std::copy(indices.begin(),indices.end(),std::back_inserter(meshes) );
   1.743 +			}
   1.744 +			else {
   1.745 +				FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
   1.746 +			}
   1.747 +		}
   1.748 +
   1.749 +		if(meshes.size()) {
   1.750 +			nd.mMeshes = new unsigned int[meshes.size()]();
   1.751 +			nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
   1.752 +
   1.753 +			std::swap_ranges(meshes.begin(),meshes.end(),nd.mMeshes);
   1.754 +		}
   1.755 +	}
   1.756 +
   1.757 +
   1.758 +	// ------------------------------------------------------------------------------------------------
   1.759 +	// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
   1.760 +	std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh,const Model& model, 
   1.761 +		const aiMatrix4x4& node_global_transform)
   1.762 +	{
   1.763 +		std::vector<unsigned int> temp; 
   1.764 +
   1.765 +		MeshMap::const_iterator it = meshes_converted.find(&mesh);
   1.766 +		if (it != meshes_converted.end()) {
   1.767 +			std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(temp));
   1.768 +			return temp;
   1.769 +		}
   1.770 +
   1.771 +		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
   1.772 +		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
   1.773 +		if(vertices.empty() || faces.empty()) {
   1.774 +			FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
   1.775 +			return temp;
   1.776 +		}
   1.777 +
   1.778 +		// one material per mesh maps easily to aiMesh. Multiple material 
   1.779 +		// meshes need to be split.
   1.780 +		const MatIndexArray& mindices = mesh.GetMaterialIndices();
   1.781 +		if (doc.Settings().readMaterials && !mindices.empty()) {
   1.782 +			const MatIndexArray::value_type base = mindices[0];
   1.783 +			BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
   1.784 +				if(index != base) {
   1.785 +					return ConvertMeshMultiMaterial(mesh, model, node_global_transform);
   1.786 +				}
   1.787 +			}
   1.788 +		}
   1.789 +
   1.790 +		// faster codepath, just copy the data
   1.791 +		temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform));
   1.792 +		return temp;
   1.793 +	}
   1.794 +
   1.795 +
   1.796 +	// ------------------------------------------------------------------------------------------------
   1.797 +	aiMesh* SetupEmptyMesh(const MeshGeometry& mesh)
   1.798 +	{
   1.799 +		aiMesh* const out_mesh = new aiMesh();
   1.800 +		meshes.push_back(out_mesh);
   1.801 +		meshes_converted[&mesh].push_back(static_cast<unsigned int>(meshes.size()-1));
   1.802 +
   1.803 +		// set name
   1.804 +		std::string name = mesh.Name();
   1.805 +		if (name.substr(0,10) == "Geometry::") {
   1.806 +			name = name.substr(10);
   1.807 +		}
   1.808 +
   1.809 +		if(name.length()) {
   1.810 +			out_mesh->mName.Set(name);
   1.811 +		}
   1.812 +
   1.813 +		return out_mesh;
   1.814 +	}
   1.815 +
   1.816 +
   1.817 +	// ------------------------------------------------------------------------------------------------
   1.818 +	unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, 
   1.819 +		const aiMatrix4x4& node_global_transform)	
   1.820 +	{
   1.821 +		const MatIndexArray& mindices = mesh.GetMaterialIndices();
   1.822 +		aiMesh* const out_mesh = SetupEmptyMesh(mesh); 
   1.823 +
   1.824 +		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
   1.825 +		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
   1.826 +
   1.827 +		// copy vertices
   1.828 +		out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
   1.829 +		out_mesh->mVertices = new aiVector3D[vertices.size()];
   1.830 +		std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices);
   1.831 +
   1.832 +		// generate dummy faces
   1.833 +		out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
   1.834 +		aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
   1.835 +
   1.836 +		unsigned int cursor = 0;
   1.837 +		BOOST_FOREACH(unsigned int pcount, faces) {
   1.838 +			aiFace& f = *fac++;
   1.839 +			f.mNumIndices = pcount;
   1.840 +			f.mIndices = new unsigned int[pcount];
   1.841 +			switch(pcount) 
   1.842 +			{
   1.843 +			case 1:
   1.844 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
   1.845 +				break;
   1.846 +			case 2:
   1.847 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
   1.848 +				break;
   1.849 +			case 3:
   1.850 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
   1.851 +				break;
   1.852 +			default:
   1.853 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
   1.854 +				break;
   1.855 +			}
   1.856 +			for (unsigned int i = 0; i < pcount; ++i) {
   1.857 +				f.mIndices[i] = cursor++;
   1.858 +			}
   1.859 +		}
   1.860 +
   1.861 +		// copy normals
   1.862 +		const std::vector<aiVector3D>& normals = mesh.GetNormals();
   1.863 +		if(normals.size()) {
   1.864 +			ai_assert(normals.size() == vertices.size());
   1.865 +
   1.866 +			out_mesh->mNormals = new aiVector3D[vertices.size()];
   1.867 +			std::copy(normals.begin(),normals.end(),out_mesh->mNormals);
   1.868 +		}
   1.869 +
   1.870 +		// copy tangents - assimp requires both tangents and bitangents (binormals)
   1.871 +		// to be present, or neither of them. Compute binormals from normals
   1.872 +		// and tangents if needed.
   1.873 +		const std::vector<aiVector3D>& tangents = mesh.GetTangents();
   1.874 +		const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
   1.875 +
   1.876 +		if(tangents.size()) {
   1.877 +			std::vector<aiVector3D> tempBinormals;
   1.878 +			if (!binormals->size()) {
   1.879 +				if (normals.size()) {
   1.880 +					tempBinormals.resize(normals.size());
   1.881 +					for (unsigned int i = 0; i < tangents.size(); ++i) {
   1.882 +						tempBinormals[i] = normals[i] ^ tangents[i];
   1.883 +					}
   1.884 +
   1.885 +					binormals = &tempBinormals;
   1.886 +				}
   1.887 +				else {
   1.888 +					binormals = NULL;	
   1.889 +				}
   1.890 +			}
   1.891 +
   1.892 +			if(binormals) {
   1.893 +				ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
   1.894 +
   1.895 +				out_mesh->mTangents = new aiVector3D[vertices.size()];
   1.896 +				std::copy(tangents.begin(),tangents.end(),out_mesh->mTangents);
   1.897 +
   1.898 +				out_mesh->mBitangents = new aiVector3D[vertices.size()];
   1.899 +				std::copy(binormals->begin(),binormals->end(),out_mesh->mBitangents);
   1.900 +			}
   1.901 +		}
   1.902 +
   1.903 +		// copy texture coords
   1.904 +		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
   1.905 +			const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
   1.906 +			if(uvs.empty()) {
   1.907 +				break;
   1.908 +			}
   1.909 +
   1.910 +			aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
   1.911 +			BOOST_FOREACH(const aiVector2D& v, uvs) {
   1.912 +				*out_uv++ = aiVector3D(v.x,v.y,0.0f);
   1.913 +			}
   1.914 +
   1.915 +			out_mesh->mNumUVComponents[i] = 2;
   1.916 +		}
   1.917 +
   1.918 +		// copy vertex colors
   1.919 +		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
   1.920 +			const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
   1.921 +			if(colors.empty()) {
   1.922 +				break;
   1.923 +			}
   1.924 +
   1.925 +			out_mesh->mColors[i] = new aiColor4D[vertices.size()];
   1.926 +			std::copy(colors.begin(),colors.end(),out_mesh->mColors[i]);
   1.927 +		}
   1.928 +
   1.929 +		if(!doc.Settings().readMaterials || mindices.empty()) {
   1.930 +			FBXImporter::LogError("no material assigned to mesh, setting default material");
   1.931 +			out_mesh->mMaterialIndex = GetDefaultMaterial();
   1.932 +		}
   1.933 +		else {
   1.934 +			ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
   1.935 +		}
   1.936 +
   1.937 +		if(doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
   1.938 +			ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
   1.939 +		}
   1.940 +
   1.941 +		return static_cast<unsigned int>(meshes.size() - 1);
   1.942 +	}
   1.943 +
   1.944 +
   1.945 +	// ------------------------------------------------------------------------------------------------
   1.946 +	std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, 
   1.947 +		const aiMatrix4x4& node_global_transform)	
   1.948 +	{
   1.949 +		const MatIndexArray& mindices = mesh.GetMaterialIndices();
   1.950 +		ai_assert(mindices.size());
   1.951 +	
   1.952 +		std::set<MatIndexArray::value_type> had;
   1.953 +		std::vector<unsigned int> indices;
   1.954 +
   1.955 +		BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
   1.956 +			if(had.find(index) == had.end()) {
   1.957 +
   1.958 +				indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform));
   1.959 +				had.insert(index);
   1.960 +			}
   1.961 +		}
   1.962 +
   1.963 +		return indices;
   1.964 +	}
   1.965 +
   1.966 +
   1.967 +	// ------------------------------------------------------------------------------------------------
   1.968 +	unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, 
   1.969 +		MatIndexArray::value_type index, 
   1.970 +		const aiMatrix4x4& node_global_transform)	
   1.971 +	{
   1.972 +		aiMesh* const out_mesh = SetupEmptyMesh(mesh);
   1.973 +
   1.974 +		const MatIndexArray& mindices = mesh.GetMaterialIndices();
   1.975 +		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
   1.976 +		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
   1.977 +
   1.978 +		const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL;
   1.979 +
   1.980 +		unsigned int count_faces = 0;
   1.981 +		unsigned int count_vertices = 0;
   1.982 +
   1.983 +		// count faces
   1.984 +		std::vector<unsigned int>::const_iterator itf = faces.begin();
   1.985 +		for(MatIndexArray::const_iterator it = mindices.begin(), 
   1.986 +			end = mindices.end(); it != end; ++it, ++itf) 
   1.987 +		{	
   1.988 +			if ((*it) != index) {
   1.989 +				continue;
   1.990 +			}
   1.991 +			++count_faces;
   1.992 +			count_vertices += *itf;
   1.993 +		}
   1.994 +
   1.995 +		ai_assert(count_faces);
   1.996 +		ai_assert(count_vertices);
   1.997 +
   1.998 +		// mapping from output indices to DOM indexing, needed to resolve weights
   1.999 +		std::vector<unsigned int> reverseMapping;
  1.1000 +
  1.1001 +		if (process_weights) {
  1.1002 +			reverseMapping.resize(count_vertices);
  1.1003 +		}
  1.1004 +
  1.1005 +		// allocate output data arrays, but don't fill them yet
  1.1006 +		out_mesh->mNumVertices = count_vertices;
  1.1007 +		out_mesh->mVertices = new aiVector3D[count_vertices];
  1.1008 +
  1.1009 +		out_mesh->mNumFaces = count_faces;
  1.1010 +		aiFace* fac = out_mesh->mFaces = new aiFace[count_faces]();
  1.1011 +
  1.1012 +
  1.1013 +		// allocate normals
  1.1014 +		const std::vector<aiVector3D>& normals = mesh.GetNormals();
  1.1015 +		if(normals.size()) {
  1.1016 +			ai_assert(normals.size() == vertices.size());
  1.1017 +			out_mesh->mNormals = new aiVector3D[vertices.size()];
  1.1018 +		}
  1.1019 +
  1.1020 +		// allocate tangents, binormals. 
  1.1021 +		const std::vector<aiVector3D>& tangents = mesh.GetTangents();
  1.1022 +		const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
  1.1023 +
  1.1024 +		if(tangents.size()) {
  1.1025 +			std::vector<aiVector3D> tempBinormals;
  1.1026 +			if (!binormals->size()) {
  1.1027 +				if (normals.size()) {
  1.1028 +					// XXX this computes the binormals for the entire mesh, not only 
  1.1029 +					// the part for which we need them.
  1.1030 +					tempBinormals.resize(normals.size());
  1.1031 +					for (unsigned int i = 0; i < tangents.size(); ++i) {
  1.1032 +						tempBinormals[i] = normals[i] ^ tangents[i];
  1.1033 +					}
  1.1034 +
  1.1035 +					binormals = &tempBinormals;
  1.1036 +				}
  1.1037 +				else {
  1.1038 +					binormals = NULL;	
  1.1039 +				}
  1.1040 +			}
  1.1041 +
  1.1042 +			if(binormals) {
  1.1043 +				ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
  1.1044 +
  1.1045 +				out_mesh->mTangents = new aiVector3D[vertices.size()];
  1.1046 +				out_mesh->mBitangents = new aiVector3D[vertices.size()];
  1.1047 +			}
  1.1048 +		}
  1.1049 +
  1.1050 +		// allocate texture coords
  1.1051 +		unsigned int num_uvs = 0;
  1.1052 +		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
  1.1053 +			const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
  1.1054 +			if(uvs.empty()) {
  1.1055 +				break;
  1.1056 +			}
  1.1057 +
  1.1058 +			out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
  1.1059 +			out_mesh->mNumUVComponents[i] = 2;
  1.1060 +		}
  1.1061 +
  1.1062 +		// allocate vertex colors
  1.1063 +		unsigned int num_vcs = 0;
  1.1064 +		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
  1.1065 +			const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
  1.1066 +			if(colors.empty()) {
  1.1067 +				break;
  1.1068 +			}
  1.1069 +
  1.1070 +			out_mesh->mColors[i] = new aiColor4D[vertices.size()];
  1.1071 +		}
  1.1072 +
  1.1073 +		unsigned int cursor = 0, in_cursor = 0;
  1.1074 +
  1.1075 +		itf = faces.begin();
  1.1076 +		for(MatIndexArray::const_iterator it = mindices.begin(), 
  1.1077 +			end = mindices.end(); it != end; ++it, ++itf) 
  1.1078 +		{	
  1.1079 +			const unsigned int pcount = *itf;
  1.1080 +			if ((*it) != index) {
  1.1081 +				in_cursor += pcount;
  1.1082 +				continue;
  1.1083 +			}
  1.1084 +
  1.1085 +			aiFace& f = *fac++;
  1.1086 +
  1.1087 +			f.mNumIndices = pcount;
  1.1088 +			f.mIndices = new unsigned int[pcount];
  1.1089 +			switch(pcount) 
  1.1090 +			{
  1.1091 +			case 1:
  1.1092 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
  1.1093 +				break;
  1.1094 +			case 2:
  1.1095 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
  1.1096 +				break;
  1.1097 +			case 3:
  1.1098 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
  1.1099 +				break;
  1.1100 +			default:
  1.1101 +				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
  1.1102 +				break;
  1.1103 +			}
  1.1104 +			for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
  1.1105 +				f.mIndices[i] = cursor;
  1.1106 +
  1.1107 +				if(reverseMapping.size()) {
  1.1108 +					reverseMapping[cursor] = in_cursor;
  1.1109 +				}
  1.1110 +
  1.1111 +				out_mesh->mVertices[cursor] = vertices[in_cursor];
  1.1112 +
  1.1113 +				if(out_mesh->mNormals) {
  1.1114 +					out_mesh->mNormals[cursor] = normals[in_cursor];
  1.1115 +				}
  1.1116 +
  1.1117 +				if(out_mesh->mTangents) {
  1.1118 +					out_mesh->mTangents[cursor] = tangents[in_cursor];
  1.1119 +					out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
  1.1120 +				}
  1.1121 +
  1.1122 +				for (unsigned int i = 0; i < num_uvs; ++i) {
  1.1123 +					const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
  1.1124 +					out_mesh->mTextureCoords[i][cursor] = aiVector3D(uvs[in_cursor].x,uvs[in_cursor].y, 0.0f);
  1.1125 +				}
  1.1126 +
  1.1127 +				for (unsigned int i = 0; i < num_vcs; ++i) {
  1.1128 +					const std::vector<aiColor4D>& cols = mesh.GetVertexColors(i);
  1.1129 +					out_mesh->mColors[i][cursor] = cols[in_cursor];
  1.1130 +				}
  1.1131 +			}
  1.1132 +		}
  1.1133 +	
  1.1134 +		ConvertMaterialForMesh(out_mesh,model,mesh,index);
  1.1135 +
  1.1136 +		if(process_weights) {
  1.1137 +			ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
  1.1138 +		}
  1.1139 +
  1.1140 +		return static_cast<unsigned int>(meshes.size() - 1);
  1.1141 +	}
  1.1142 +
  1.1143 +	static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ 
  1.1144 +		static_cast<unsigned int>(-1);
  1.1145 +
  1.1146 +
  1.1147 +	// ------------------------------------------------------------------------------------------------
  1.1148 +	/** - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
  1.1149 +	 *  account when determining which weights to include. 
  1.1150 +	 *  - outputVertStartIndices is only used when a material index is specified, it gives for
  1.1151 +	 *    each output vertex the DOM index it maps to. */
  1.1152 +	void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, 
  1.1153 +		const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
  1.1154 +		unsigned int materialIndex = NO_MATERIAL_SEPARATION, 
  1.1155 +		std::vector<unsigned int>* outputVertStartIndices = NULL)
  1.1156 +	{
  1.1157 +		ai_assert(geo.DeformerSkin());
  1.1158 +
  1.1159 +		std::vector<size_t> out_indices;
  1.1160 +		std::vector<size_t> index_out_indices;
  1.1161 +		std::vector<size_t> count_out_indices;
  1.1162 +
  1.1163 +		const Skin& sk = *geo.DeformerSkin();
  1.1164 +
  1.1165 +		std::vector<aiBone*> bones;
  1.1166 +		bones.reserve(sk.Clusters().size());
  1.1167 +
  1.1168 +		const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
  1.1169 +		ai_assert(no_mat_check || outputVertStartIndices);
  1.1170 +
  1.1171 +		try {
  1.1172 +
  1.1173 +			BOOST_FOREACH(const Cluster* cluster, sk.Clusters()) {
  1.1174 +				ai_assert(cluster);
  1.1175 +
  1.1176 +				const WeightIndexArray& indices = cluster->GetIndices();
  1.1177 +
  1.1178 +				if(indices.empty()) {
  1.1179 +					continue;
  1.1180 +				}
  1.1181 +
  1.1182 +				const MatIndexArray& mats = geo.GetMaterialIndices();
  1.1183 +
  1.1184 +				bool ok = false;		
  1.1185 +
  1.1186 +				const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
  1.1187 +
  1.1188 +				count_out_indices.clear();
  1.1189 +				index_out_indices.clear();
  1.1190 +				out_indices.clear();
  1.1191 +
  1.1192 +				// now check if *any* of these weights is contained in the output mesh,
  1.1193 +				// taking notes so we don't need to do it twice.
  1.1194 +				BOOST_FOREACH(WeightIndexArray::value_type index, indices) {
  1.1195 +
  1.1196 +					unsigned int count;
  1.1197 +					const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
  1.1198 +
  1.1199 +					index_out_indices.push_back(no_index_sentinel);
  1.1200 +					count_out_indices.push_back(0);
  1.1201 +
  1.1202 +					for(unsigned int i = 0; i < count; ++i) {					
  1.1203 +						if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) {
  1.1204 +							
  1.1205 +							if (index_out_indices.back() == no_index_sentinel) {
  1.1206 +								index_out_indices.back() = out_indices.size();
  1.1207 +								
  1.1208 +							}
  1.1209 +
  1.1210 +							if (no_mat_check) {
  1.1211 +								out_indices.push_back(out_idx[i]);
  1.1212 +							}
  1.1213 +							else {
  1.1214 +								// this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
  1.1215 +								const std::vector<unsigned int>::iterator it = std::lower_bound(
  1.1216 +									outputVertStartIndices->begin(),
  1.1217 +									outputVertStartIndices->end(),
  1.1218 +									out_idx[i]
  1.1219 +								);
  1.1220 +
  1.1221 +								out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
  1.1222 +							}
  1.1223 +
  1.1224 +							++count_out_indices.back();
  1.1225 +							ok = true;
  1.1226 +						}
  1.1227 +					}		
  1.1228 +				}
  1.1229 +
  1.1230 +				// if we found at least one, generate the output bones
  1.1231 +				// XXX this could be heavily simplified by collecting the bone
  1.1232 +				// data in a single step.
  1.1233 +				if (ok) {
  1.1234 +					ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, 
  1.1235 +						count_out_indices, node_global_transform);
  1.1236 +				}
  1.1237 +			}
  1.1238 +		}
  1.1239 +		catch (std::exception&) {
  1.1240 +			std::for_each(bones.begin(),bones.end(),Util::delete_fun<aiBone>());
  1.1241 +			throw;
  1.1242 +		}
  1.1243 +
  1.1244 +		if(bones.empty()) {
  1.1245 +			return;
  1.1246 +		}
  1.1247 +
  1.1248 +		out->mBones = new aiBone*[bones.size()]();
  1.1249 +		out->mNumBones = static_cast<unsigned int>(bones.size());
  1.1250 +
  1.1251 +		std::swap_ranges(bones.begin(),bones.end(),out->mBones);
  1.1252 +	}
  1.1253 +
  1.1254 +
  1.1255 +
  1.1256 +	// ------------------------------------------------------------------------------------------------
  1.1257 +	void ConvertCluster(std::vector<aiBone*>& bones, const Model& model, const Cluster& cl, 		
  1.1258 +		std::vector<size_t>& out_indices,
  1.1259 +		std::vector<size_t>& index_out_indices,
  1.1260 +		std::vector<size_t>& count_out_indices,
  1.1261 +		const aiMatrix4x4& node_global_transform)
  1.1262 +	{
  1.1263 +
  1.1264 +		aiBone* const bone = new aiBone();
  1.1265 +		bones.push_back(bone);
  1.1266 +
  1.1267 +		bone->mName = FixNodeName(cl.TargetNode()->Name());
  1.1268 +
  1.1269 +		bone->mOffsetMatrix = cl.TransformLink();
  1.1270 +		bone->mOffsetMatrix.Inverse();
  1.1271 +
  1.1272 +		bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
  1.1273 +
  1.1274 +		bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
  1.1275 +		aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
  1.1276 +
  1.1277 +		const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
  1.1278 +		const WeightArray& weights = cl.GetWeights();
  1.1279 +
  1.1280 +		const size_t c = index_out_indices.size();
  1.1281 +		for (size_t i = 0; i < c; ++i) {
  1.1282 +			const size_t index_index =  index_out_indices[i];
  1.1283 +
  1.1284 +			if (index_index == no_index_sentinel) {
  1.1285 +				continue;
  1.1286 +			}
  1.1287 +
  1.1288 +			const size_t cc = count_out_indices[i];
  1.1289 +			for (size_t j = 0; j < cc; ++j) {
  1.1290 +				aiVertexWeight& out_weight = *cursor++;
  1.1291 +
  1.1292 +				out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
  1.1293 +				out_weight.mWeight = weights[i];
  1.1294 +			}			
  1.1295 +		}
  1.1296 +	}
  1.1297 +
  1.1298 +
  1.1299 +	// ------------------------------------------------------------------------------------------------
  1.1300 +	void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, 
  1.1301 +		MatIndexArray::value_type materialIndex)
  1.1302 +	{
  1.1303 +		// locate source materials for this mesh
  1.1304 +		const std::vector<const Material*>& mats = model.GetMaterials();
  1.1305 +		if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) {
  1.1306 +			FBXImporter::LogError("material index out of bounds, setting default material");
  1.1307 +			out->mMaterialIndex = GetDefaultMaterial();
  1.1308 +			return;
  1.1309 +		}
  1.1310 +
  1.1311 +		const Material* const mat = mats[materialIndex];
  1.1312 +		MaterialMap::const_iterator it = materials_converted.find(mat);
  1.1313 +		if (it != materials_converted.end()) {
  1.1314 +			out->mMaterialIndex = (*it).second;
  1.1315 +			return;
  1.1316 +		}
  1.1317 +
  1.1318 +		out->mMaterialIndex = ConvertMaterial(*mat);	
  1.1319 +		materials_converted[mat] = out->mMaterialIndex;
  1.1320 +	}
  1.1321 +
  1.1322 +
  1.1323 +	// ------------------------------------------------------------------------------------------------
  1.1324 +	unsigned int GetDefaultMaterial()
  1.1325 +	{
  1.1326 +		if (defaultMaterialIndex) {
  1.1327 +			return defaultMaterialIndex - 1; 
  1.1328 +		}
  1.1329 +
  1.1330 +		aiMaterial* out_mat = new aiMaterial();
  1.1331 +		materials.push_back(out_mat);
  1.1332 +
  1.1333 +		const aiColor3D diffuse = aiColor3D(0.8f,0.8f,0.8f);
  1.1334 +		out_mat->AddProperty(&diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
  1.1335 +
  1.1336 +		aiString s;
  1.1337 +		s.Set(AI_DEFAULT_MATERIAL_NAME);
  1.1338 +
  1.1339 +		out_mat->AddProperty(&s,AI_MATKEY_NAME);
  1.1340 +
  1.1341 +		defaultMaterialIndex = static_cast<unsigned int>(materials.size());
  1.1342 +		return defaultMaterialIndex - 1;
  1.1343 +	}
  1.1344 +
  1.1345 +
  1.1346 +	// ------------------------------------------------------------------------------------------------
  1.1347 +	// Material -> aiMaterial
  1.1348 +	unsigned int ConvertMaterial(const Material& material)
  1.1349 +	{
  1.1350 +		const PropertyTable& props = material.Props();
  1.1351 +
  1.1352 +		// generate empty output material
  1.1353 +		aiMaterial* out_mat = new aiMaterial();
  1.1354 +		materials_converted[&material] = static_cast<unsigned int>(materials.size());
  1.1355 +
  1.1356 +		materials.push_back(out_mat);
  1.1357 +
  1.1358 +		aiString str;
  1.1359 +
  1.1360 +		// stip Material:: prefix
  1.1361 +		std::string name = material.Name();
  1.1362 +		if(name.substr(0,10) == "Material::") {
  1.1363 +			name = name.substr(10);
  1.1364 +		}
  1.1365 +
  1.1366 +		// set material name if not empty - this could happen
  1.1367 +		// and there should be no key for it in this case.
  1.1368 +		if(name.length()) {
  1.1369 +			str.Set(name);
  1.1370 +			out_mat->AddProperty(&str,AI_MATKEY_NAME);
  1.1371 +		}
  1.1372 +
  1.1373 +		// shading stuff and colors
  1.1374 +		SetShadingPropertiesCommon(out_mat,props);
  1.1375 +	
  1.1376 +		// texture assignments
  1.1377 +		SetTextureProperties(out_mat,material.Textures());
  1.1378 +
  1.1379 +		return static_cast<unsigned int>(materials.size() - 1);
  1.1380 +	}
  1.1381 +
  1.1382 +
  1.1383 +	// ------------------------------------------------------------------------------------------------
  1.1384 +	void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, 
  1.1385 +		const std::string& propName, 
  1.1386 +		aiTextureType target)
  1.1387 +	{
  1.1388 +		TextureMap::const_iterator it = textures.find(propName);
  1.1389 +		if(it == textures.end()) {
  1.1390 +			return;
  1.1391 +		}
  1.1392 +
  1.1393 +		const Texture* const tex = (*it).second;
  1.1394 +		
  1.1395 +		aiString path;
  1.1396 +		path.Set(tex->RelativeFilename());
  1.1397 +
  1.1398 +		out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
  1.1399 +
  1.1400 +		aiUVTransform uvTrafo;
  1.1401 +		// XXX handle all kinds of UV transformations
  1.1402 +		uvTrafo.mScaling = tex->UVScaling();
  1.1403 +		uvTrafo.mTranslation = tex->UVTranslation();
  1.1404 +		out_mat->AddProperty(&uvTrafo,1,_AI_MATKEY_UVTRANSFORM_BASE,target,0);
  1.1405 +
  1.1406 +		const PropertyTable& props = tex->Props();
  1.1407 +
  1.1408 +		int uvIndex = 0;
  1.1409 +
  1.1410 +		bool ok;
  1.1411 +		const std::string& uvSet = PropertyGet<std::string>(props,"UVSet",ok);
  1.1412 +		if(ok) {
  1.1413 +			// "default" is the name which usually appears in the FbxFileTexture template
  1.1414 +			if(uvSet != "default" && uvSet.length()) {
  1.1415 +				// this is a bit awkward - we need to find a mesh that uses this
  1.1416 +				// material and scan its UV channels for the given UV name because
  1.1417 +				// assimp references UV channels by index, not by name.
  1.1418 +
  1.1419 +				// XXX: the case that UV channels may appear in different orders
  1.1420 +				// in meshes is unhandled. A possible solution would be to sort
  1.1421 +				// the UV channels alphabetically, but this would have the side
  1.1422 +				// effect that the primary (first) UV channel would sometimes
  1.1423 +				// be moved, causing trouble when users read only the first
  1.1424 +				// UV channel and ignore UV channel assignments altogether.
  1.1425 +
  1.1426 +				const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), 
  1.1427 +					std::find(materials.begin(),materials.end(),out_mat)
  1.1428 +				));
  1.1429 +
  1.1430 +				uvIndex = -1;
  1.1431 +				BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) {
  1.1432 +					const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
  1.1433 +					if(!mesh) {
  1.1434 +						continue;
  1.1435 +					}
  1.1436 +
  1.1437 +					const MatIndexArray& mats = mesh->GetMaterialIndices();
  1.1438 +					if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
  1.1439 +						continue;
  1.1440 +					}
  1.1441 +
  1.1442 +					int index = -1;
  1.1443 +					for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
  1.1444 +						if(mesh->GetTextureCoords(i).empty()) {
  1.1445 +							break;
  1.1446 +						}
  1.1447 +						const std::string& name = mesh->GetTextureCoordChannelName(i);
  1.1448 +						if(name == uvSet) {
  1.1449 +							index = static_cast<int>(i);
  1.1450 +							break;
  1.1451 +						}
  1.1452 +					}
  1.1453 +					if(index == -1) {
  1.1454 +						FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
  1.1455 +						continue;
  1.1456 +					}
  1.1457 +
  1.1458 +					if(uvIndex == -1) {
  1.1459 +						uvIndex = index;
  1.1460 +					}
  1.1461 +					else {
  1.1462 +						FBXImporter::LogWarn("the UV channel named " + uvSet + 
  1.1463 +							" appears at different positions in meshes, results will be wrong");
  1.1464 +					}
  1.1465 +				}
  1.1466 +
  1.1467 +				if(uvIndex == -1) {
  1.1468 +					FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
  1.1469 +					uvIndex = 0;
  1.1470 +				}
  1.1471 +			}
  1.1472 +		}
  1.1473 +
  1.1474 +		out_mat->AddProperty(&uvIndex,1,_AI_MATKEY_UVWSRC_BASE,target,0);
  1.1475 +	}
  1.1476 +
  1.1477 +
  1.1478 +	// ------------------------------------------------------------------------------------------------
  1.1479 +	void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures)
  1.1480 +	{
  1.1481 +		TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE);
  1.1482 +		TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT);
  1.1483 +		TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE);
  1.1484 +		TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR);
  1.1485 +		TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY);
  1.1486 +		TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION);
  1.1487 +		TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT);
  1.1488 +		TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS);
  1.1489 +		TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT);
  1.1490 +	}
  1.1491 +
  1.1492 +
  1.1493 +
  1.1494 +	// ------------------------------------------------------------------------------------------------
  1.1495 +	aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, 
  1.1496 +		bool& result)
  1.1497 +	{
  1.1498 +		result = true;
  1.1499 +
  1.1500 +		bool ok;
  1.1501 +		const aiVector3D& Diffuse = PropertyGet<aiVector3D>(props,baseName,ok);
  1.1502 +		if(ok) {
  1.1503 +			return aiColor3D(Diffuse.x,Diffuse.y,Diffuse.z);
  1.1504 +		}
  1.1505 +		else {
  1.1506 +			aiVector3D DiffuseColor = PropertyGet<aiVector3D>(props,baseName + "Color",ok);
  1.1507 +			if(ok) {
  1.1508 +				float DiffuseFactor = PropertyGet<float>(props,baseName + "Factor",ok);
  1.1509 +				if(ok) {
  1.1510 +					DiffuseColor *= DiffuseFactor;
  1.1511 +				}
  1.1512 +
  1.1513 +				return aiColor3D(DiffuseColor.x,DiffuseColor.y,DiffuseColor.z);
  1.1514 +			}
  1.1515 +		}
  1.1516 +		result = false;
  1.1517 +		return aiColor3D(0.0f,0.0f,0.0f);
  1.1518 +	}
  1.1519 +
  1.1520 +
  1.1521 +	// ------------------------------------------------------------------------------------------------
  1.1522 +	void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props)
  1.1523 +	{
  1.1524 +		// set shading properties. There are various, redundant ways in which FBX materials
  1.1525 +		// specify their shading settings (depending on shading models, prop
  1.1526 +		// template etc.). No idea which one is right in a particular context. 
  1.1527 +		// Just try to make sense of it - there's no spec to verify this against, 
  1.1528 +		// so why should we.
  1.1529 +		bool ok;
  1.1530 +		const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props,"Diffuse",ok);
  1.1531 +		if(ok) {
  1.1532 +			out_mat->AddProperty(&Diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
  1.1533 +		}
  1.1534 +
  1.1535 +		const aiColor3D& Emissive = GetColorPropertyFromMaterial(props,"Emissive",ok);
  1.1536 +		if(ok) {
  1.1537 +			out_mat->AddProperty(&Emissive,1,AI_MATKEY_COLOR_EMISSIVE);
  1.1538 +		}
  1.1539 +
  1.1540 +		const aiColor3D& Ambient = GetColorPropertyFromMaterial(props,"Ambient",ok);
  1.1541 +		if(ok) {
  1.1542 +			out_mat->AddProperty(&Ambient,1,AI_MATKEY_COLOR_AMBIENT);
  1.1543 +		}
  1.1544 +
  1.1545 +		const aiColor3D& Specular = GetColorPropertyFromMaterial(props,"Specular",ok);
  1.1546 +		if(ok) {
  1.1547 +			out_mat->AddProperty(&Specular,1,AI_MATKEY_COLOR_SPECULAR);
  1.1548 +		}
  1.1549 +
  1.1550 +		const float Opacity = PropertyGet<float>(props,"Opacity",ok);
  1.1551 +		if(ok) {
  1.1552 +			out_mat->AddProperty(&Opacity,1,AI_MATKEY_OPACITY);
  1.1553 +		}
  1.1554 +
  1.1555 +		const float Reflectivity = PropertyGet<float>(props,"Reflectivity",ok);
  1.1556 +		if(ok) {
  1.1557 +			out_mat->AddProperty(&Reflectivity,1,AI_MATKEY_REFLECTIVITY);
  1.1558 +		}
  1.1559 +
  1.1560 +		const float Shininess = PropertyGet<float>(props,"Shininess",ok);
  1.1561 +		if(ok) {
  1.1562 +			out_mat->AddProperty(&Shininess,1,AI_MATKEY_SHININESS_STRENGTH);
  1.1563 +		}
  1.1564 +
  1.1565 +		const float ShininessExponent = PropertyGet<float>(props,"ShininessExponent",ok);
  1.1566 +		if(ok) {
  1.1567 +			out_mat->AddProperty(&ShininessExponent,1,AI_MATKEY_SHININESS);
  1.1568 +		}
  1.1569 +	}
  1.1570 +
  1.1571 +
  1.1572 +	// ------------------------------------------------------------------------------------------------
  1.1573 +	// get the number of fps for a FrameRate enumerated value
  1.1574 +	static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0)
  1.1575 +	{
  1.1576 +		switch(fp) {
  1.1577 +			case FileGlobalSettings::FrameRate_DEFAULT:
  1.1578 +				return 1.0;
  1.1579 +
  1.1580 +			case FileGlobalSettings::FrameRate_120:
  1.1581 +				return 120.0;
  1.1582 +
  1.1583 +			case FileGlobalSettings::FrameRate_100:
  1.1584 +				return 100.0;
  1.1585 +
  1.1586 +			case FileGlobalSettings::FrameRate_60:
  1.1587 +				return 60.0;
  1.1588 +
  1.1589 +			case FileGlobalSettings::FrameRate_50:
  1.1590 +				return 50.0;
  1.1591 +
  1.1592 +			case FileGlobalSettings::FrameRate_48:
  1.1593 +				return 48.0;
  1.1594 +
  1.1595 +			case FileGlobalSettings::FrameRate_30:
  1.1596 +			case FileGlobalSettings::FrameRate_30_DROP:
  1.1597 +				return 30.0;
  1.1598 +
  1.1599 +			case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
  1.1600 +			case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
  1.1601 +				return 29.9700262;
  1.1602 +
  1.1603 +			case FileGlobalSettings::FrameRate_PAL:
  1.1604 +				return 25.0;
  1.1605 +
  1.1606 +			case FileGlobalSettings::FrameRate_CINEMA:
  1.1607 +				return 24.0;
  1.1608 +
  1.1609 +			case FileGlobalSettings::FrameRate_1000:
  1.1610 +				return 1000.0;
  1.1611 +
  1.1612 +			case FileGlobalSettings::FrameRate_CINEMA_ND:
  1.1613 +				return 23.976;
  1.1614 +
  1.1615 +			case FileGlobalSettings::FrameRate_CUSTOM:
  1.1616 +				return customFPSVal;
  1.1617 +
  1.1618 +			case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
  1.1619 +				break;
  1.1620 +		}
  1.1621 +
  1.1622 +		ai_assert(false);
  1.1623 +		return -1.0f;
  1.1624 +	}
  1.1625 +
  1.1626 +
  1.1627 +	// ------------------------------------------------------------------------------------------------
  1.1628 +	// convert animation data to aiAnimation et al
  1.1629 +	void ConvertAnimations() 
  1.1630 +	{
  1.1631 +		// first of all determine framerate
  1.1632 +		const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
  1.1633 +		const float custom = doc.GlobalSettings().CustomFrameRate();
  1.1634 +		anim_fps = FrameRateToDouble(fps, custom);
  1.1635 +
  1.1636 +		const std::vector<const AnimationStack*>& animations = doc.AnimationStacks();
  1.1637 +		BOOST_FOREACH(const AnimationStack* stack, animations) {
  1.1638 +			ConvertAnimationStack(*stack);
  1.1639 +		}
  1.1640 +	}
  1.1641 +
  1.1642 +
  1.1643 +	// ------------------------------------------------------------------------------------------------
  1.1644 +	// rename a node already partially converted. fixed_name is a string previously returned by 
  1.1645 +	// FixNodeName, new_name specifies the string FixNodeName should return on all further invocations 
  1.1646 +	// which would previously have returned the old value.
  1.1647 +	//
  1.1648 +	// this also updates names in node animations, cameras and light sources and is thus slow.
  1.1649 +	//
  1.1650 +	// NOTE: the caller is responsible for ensuring that the new name is unique and does
  1.1651 +	// not collide with any other identifiers. The best way to ensure this is to only
  1.1652 +	// append to the old name, which is guaranteed to match these requirements.
  1.1653 +	void RenameNode(const std::string& fixed_name, const std::string& new_name)
  1.1654 +	{
  1.1655 +		ai_assert(node_names.find(fixed_name) != node_names.end());
  1.1656 +		ai_assert(node_names.find(new_name) == node_names.end());
  1.1657 +
  1.1658 +		renamed_nodes[fixed_name] = new_name;
  1.1659 +
  1.1660 +		const aiString fn(fixed_name);
  1.1661 +
  1.1662 +		BOOST_FOREACH(aiCamera* cam, cameras) {
  1.1663 +			if (cam->mName == fn) {
  1.1664 +				cam->mName.Set(new_name);
  1.1665 +				break;
  1.1666 +			}
  1.1667 +		}
  1.1668 +
  1.1669 +		BOOST_FOREACH(aiLight* light, lights) {
  1.1670 +			if (light->mName == fn) {
  1.1671 +				light->mName.Set(new_name);
  1.1672 +				break;
  1.1673 +			}
  1.1674 +		}
  1.1675 +
  1.1676 +		BOOST_FOREACH(aiAnimation* anim, animations) {
  1.1677 +			for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
  1.1678 +				aiNodeAnim* const na = anim->mChannels[i];
  1.1679 +				if (na->mNodeName == fn) {
  1.1680 +					na->mNodeName.Set(new_name);
  1.1681 +					break;
  1.1682 +				}
  1.1683 +			}
  1.1684 +		}
  1.1685 +	}
  1.1686 +
  1.1687 +
  1.1688 +	// ------------------------------------------------------------------------------------------------
  1.1689 +	// takes a fbx node name and returns the identifier to be used in the assimp output scene.
  1.1690 +	// the function is guaranteed to provide consistent results over multiple invocations
  1.1691 +	// UNLESS RenameNode() is called for a particular node name.
  1.1692 +	std::string FixNodeName(const std::string& name)
  1.1693 +	{
  1.1694 +		// strip Model:: prefix, avoiding ambiguities (i.e. don't strip if 
  1.1695 +		// this causes ambiguities, well possible between empty identifiers,
  1.1696 +		// such as "Model::" and ""). Make sure the behaviour is consistent
  1.1697 +		// across multiple calls to FixNodeName().
  1.1698 +		if(name.substr(0,7) == "Model::") {
  1.1699 +			std::string temp = name.substr(7);
  1.1700 +
  1.1701 +			const NodeNameMap::const_iterator it = node_names.find(temp);
  1.1702 +			if (it != node_names.end()) {
  1.1703 +				if (!(*it).second) {
  1.1704 +					return FixNodeName(name + "_");
  1.1705 +				}
  1.1706 +			}
  1.1707 +			node_names[temp] = true;
  1.1708 +
  1.1709 +			const NameNameMap::const_iterator rit = renamed_nodes.find(temp);
  1.1710 +			return rit == renamed_nodes.end() ? temp : (*rit).second;
  1.1711 +		}
  1.1712 +
  1.1713 +		const NodeNameMap::const_iterator it = node_names.find(name);
  1.1714 +		if (it != node_names.end()) {
  1.1715 +			if ((*it).second) {
  1.1716 +				return FixNodeName(name + "_");
  1.1717 +			}
  1.1718 +		}
  1.1719 +		node_names[name] = false;
  1.1720 +
  1.1721 +		const NameNameMap::const_iterator rit = renamed_nodes.find(name);
  1.1722 +		return rit == renamed_nodes.end() ? name : (*rit).second;
  1.1723 +	}
  1.1724 +
  1.1725 +
  1.1726 +	typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
  1.1727 +
  1.1728 +	// XXX: better use multi_map ..
  1.1729 +	typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
  1.1730 +
  1.1731 +
  1.1732 +	// ------------------------------------------------------------------------------------------------
  1.1733 +	void ConvertAnimationStack(const AnimationStack& st)
  1.1734 +	{				
  1.1735 +		const AnimationLayerList& layers = st.Layers();
  1.1736 +		if(layers.empty()) {
  1.1737 +			return;
  1.1738 +		}
  1.1739 +
  1.1740 +		aiAnimation* const anim = new aiAnimation();
  1.1741 +		animations.push_back(anim);
  1.1742 +
  1.1743 +		// strip AnimationStack:: prefix
  1.1744 +		std::string name = st.Name();
  1.1745 +		if(name.substr(0,16) == "AnimationStack::") {
  1.1746 +			name = name.substr(16);
  1.1747 +		}
  1.1748 +
  1.1749 +		anim->mName.Set(name);
  1.1750 +		
  1.1751 +		// need to find all nodes for which we need to generate node animations -
  1.1752 +		// it may happen that we need to merge multiple layers, though.
  1.1753 +		NodeMap node_map;
  1.1754 +
  1.1755 +		// reverse mapping from curves to layers, much faster than querying 
  1.1756 +		// the FBX DOM for it.
  1.1757 +		LayerMap layer_map;
  1.1758 +
  1.1759 +		const char* prop_whitelist[] = {
  1.1760 +			"Lcl Scaling",
  1.1761 +			"Lcl Rotation",
  1.1762 +			"Lcl Translation"
  1.1763 +		};
  1.1764 +		
  1.1765 +		BOOST_FOREACH(const AnimationLayer* layer, layers) {
  1.1766 +			ai_assert(layer);
  1.1767 +
  1.1768 +			const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 3);
  1.1769 +			BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
  1.1770 +				ai_assert(node);
  1.1771 +
  1.1772 +				const Model* const model = dynamic_cast<const Model*>(node->Target());
  1.1773 +				// this can happen - it could also be a NodeAttribute (i.e. for camera animations)
  1.1774 +				if(!model) {
  1.1775 +					continue;
  1.1776 +				}
  1.1777 +
  1.1778 +				const std::string& name = FixNodeName(model->Name());
  1.1779 +				node_map[name].push_back(node);
  1.1780 +
  1.1781 +				layer_map[node] = layer;
  1.1782 +			}
  1.1783 +		}
  1.1784 +
  1.1785 +		// generate node animations
  1.1786 +		std::vector<aiNodeAnim*> node_anims;
  1.1787 +
  1.1788 +		double min_time = 1e10;
  1.1789 +		double max_time = -1e10;
  1.1790 +
  1.1791 +		try {
  1.1792 +			BOOST_FOREACH(const NodeMap::value_type& kv, node_map) {
  1.1793 +				GenerateNodeAnimations(node_anims, 
  1.1794 +					kv.first, 
  1.1795 +					kv.second, 
  1.1796 +					layer_map, 
  1.1797 +					max_time, 
  1.1798 +					min_time);
  1.1799 +			}
  1.1800 +		}
  1.1801 +		catch(std::exception&) {
  1.1802 +			std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
  1.1803 +			throw;
  1.1804 +		}
  1.1805 +
  1.1806 +		if(node_anims.size()) {
  1.1807 +			anim->mChannels = new aiNodeAnim*[node_anims.size()]();
  1.1808 +			anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
  1.1809 +
  1.1810 +			std::swap_ranges(node_anims.begin(),node_anims.end(),anim->mChannels);
  1.1811 +		}
  1.1812 +		else {
  1.1813 +			// empty animations would fail validation, so drop them
  1.1814 +			delete anim;
  1.1815 +			animations.pop_back();
  1.1816 +			FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name);
  1.1817 +			return;
  1.1818 +		}
  1.1819 +
  1.1820 +		// for some mysterious reason, mDuration is simply the maximum key -- the
  1.1821 +		// validator always assumes animations to start at zero.
  1.1822 +		anim->mDuration = max_time /*- min_time */;
  1.1823 +		anim->mTicksPerSecond = anim_fps;
  1.1824 +	}
  1.1825 +
  1.1826 +
  1.1827 +	// ------------------------------------------------------------------------------------------------
  1.1828 +	void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims, 
  1.1829 +		const std::string& fixed_name, 
  1.1830 +		const std::vector<const AnimationCurveNode*>& curves, 
  1.1831 +		const LayerMap& layer_map, 
  1.1832 +		double& max_time,
  1.1833 +		double& min_time)
  1.1834 +	{
  1.1835 +
  1.1836 +		NodeMap node_property_map;
  1.1837 +		ai_assert(curves.size());
  1.1838 +
  1.1839 +		// sanity check whether the input is ok
  1.1840 +#ifdef _DEBUG
  1.1841 +		{ const Object* target = NULL;
  1.1842 +		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
  1.1843 +			if(!target) {
  1.1844 +				target = node->Target();
  1.1845 +			}
  1.1846 +			ai_assert(node->Target() == target);
  1.1847 +		}}
  1.1848 +#endif
  1.1849 +
  1.1850 +		const AnimationCurveNode* curve_node;
  1.1851 +		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
  1.1852 +			ai_assert(node);
  1.1853 +
  1.1854 +			if (node->TargetProperty().empty()) {
  1.1855 +				FBXImporter::LogWarn("target property for animation curve not set: " + node->Name());
  1.1856 +				continue;
  1.1857 +			}
  1.1858 +
  1.1859 +			curve_node = node;
  1.1860 +			if (node->Curves().empty()) {
  1.1861 +				FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name());
  1.1862 +				continue;
  1.1863 +			}
  1.1864 +
  1.1865 +			node_property_map[node->TargetProperty()].push_back(node);
  1.1866 +		}
  1.1867 +
  1.1868 +		ai_assert(curve_node);
  1.1869 +		ai_assert(curve_node->TargetAsModel());
  1.1870 +
  1.1871 +		const Model& target = *curve_node->TargetAsModel();
  1.1872 +
  1.1873 +		// check for all possible transformation components
  1.1874 +		NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
  1.1875 +
  1.1876 +		bool has_any = false;
  1.1877 +		bool has_complex = false;
  1.1878 +
  1.1879 +		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
  1.1880 +			const TransformationComp comp = static_cast<TransformationComp>(i);
  1.1881 +
  1.1882 +			// inverse pivots don't exist in the input, we just generate them
  1.1883 +			if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
  1.1884 +				chain[i] = node_property_map.end();
  1.1885 +				continue;
  1.1886 +			}
  1.1887 +
  1.1888 +			chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
  1.1889 +			if (chain[i] != node_property_map.end()) {
  1.1890 +
  1.1891 +				// check if this curves contains redundant information by looking
  1.1892 +				// up the corresponding node's transformation chain.
  1.1893 +				if (doc.Settings().optimizeEmptyAnimationCurves && 
  1.1894 +					IsRedundantAnimationData(target, comp, (*chain[i]).second)) {
  1.1895 +
  1.1896 +					FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name());
  1.1897 +					continue;
  1.1898 +				}
  1.1899 +
  1.1900 +				has_any = true;
  1.1901 +
  1.1902 +				if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
  1.1903 +					comp != TransformationComp_Translation) {
  1.1904 +
  1.1905 +					has_complex = true;
  1.1906 +				}
  1.1907 +			}
  1.1908 +		}
  1.1909 +
  1.1910 +		if (!has_any) {
  1.1911 +			FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
  1.1912 +			return;
  1.1913 +		}
  1.1914 +
  1.1915 +		// this needs to play nicely with GenerateTransformationNodeChain() which will
  1.1916 +		// be invoked _later_ (animations come first). If this node has only rotation,
  1.1917 +		// scaling and translation _and_ there are no animated other components either,
  1.1918 +		// we can use a single node and also a single node animation channel.
  1.1919 +		if (!has_complex && !NeedsComplexTransformationChain(target)) {
  1.1920 +
  1.1921 +			aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, 
  1.1922 +				node_property_map.end(), 
  1.1923 +				layer_map,
  1.1924 +				max_time,
  1.1925 +				min_time,
  1.1926 +				true // input is TRS order, assimp is SRT
  1.1927 +				);
  1.1928 +
  1.1929 +			ai_assert(nd);
  1.1930 +			node_anims.push_back(nd);
  1.1931 +			return;
  1.1932 +		}
  1.1933 +
  1.1934 +		// otherwise, things get gruesome and we need separate animation channels
  1.1935 +		// for each part of the transformation chain. Remember which channels
  1.1936 +		// we generated and pass this information to the node conversion
  1.1937 +		// code to avoid nodes that have identity transform, but non-identity
  1.1938 +		// animations, being dropped.
  1.1939 +		unsigned int flags = 0, bit = 0x1;
  1.1940 +		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
  1.1941 +			const TransformationComp comp = static_cast<TransformationComp>(i);
  1.1942 +
  1.1943 +			if (chain[i] != node_property_map.end()) {
  1.1944 +				flags |= bit;
  1.1945 +
  1.1946 +				ai_assert(comp != TransformationComp_RotationPivotInverse);
  1.1947 +				ai_assert(comp != TransformationComp_ScalingPivotInverse);
  1.1948 +
  1.1949 +				const std::string& chain_name = NameTransformationChainNode(fixed_name, comp);
  1.1950 +
  1.1951 +				aiNodeAnim* na;
  1.1952 +				switch(comp) 
  1.1953 +				{
  1.1954 +				case TransformationComp_Rotation:
  1.1955 +				case TransformationComp_PreRotation:
  1.1956 +				case TransformationComp_PostRotation:
  1.1957 +					na = GenerateRotationNodeAnim(chain_name, 
  1.1958 +						target, 
  1.1959 +						(*chain[i]).second,
  1.1960 +						layer_map,
  1.1961 +						max_time,
  1.1962 +						min_time);
  1.1963 +
  1.1964 +					break;
  1.1965 +
  1.1966 +				case TransformationComp_RotationOffset:
  1.1967 +				case TransformationComp_RotationPivot:
  1.1968 +				case TransformationComp_ScalingOffset:
  1.1969 +				case TransformationComp_ScalingPivot:
  1.1970 +				case TransformationComp_Translation:
  1.1971 +					na = GenerateTranslationNodeAnim(chain_name, 
  1.1972 +						target, 
  1.1973 +						(*chain[i]).second,
  1.1974 +						layer_map,
  1.1975 +						max_time,
  1.1976 +						min_time);
  1.1977 +
  1.1978 +					// pivoting requires us to generate an implicit inverse channel to undo the pivot translation
  1.1979 +					if (comp == TransformationComp_RotationPivot) {
  1.1980 +						const std::string& invName = NameTransformationChainNode(fixed_name, 
  1.1981 +							TransformationComp_RotationPivotInverse);
  1.1982 +
  1.1983 +						aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, 
  1.1984 +							target, 
  1.1985 +							(*chain[i]).second,
  1.1986 +							layer_map,
  1.1987 +							max_time,
  1.1988 +							min_time,
  1.1989 +							true);
  1.1990 +
  1.1991 +						ai_assert(inv);
  1.1992 +						node_anims.push_back(inv);
  1.1993 +
  1.1994 +						ai_assert(TransformationComp_RotationPivotInverse > i);
  1.1995 +						flags |= bit << (TransformationComp_RotationPivotInverse - i);
  1.1996 +					}
  1.1997 +					else if (comp == TransformationComp_ScalingPivot) {
  1.1998 +						const std::string& invName = NameTransformationChainNode(fixed_name, 
  1.1999 +							TransformationComp_ScalingPivotInverse);
  1.2000 +
  1.2001 +						aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, 
  1.2002 +							target, 
  1.2003 +							(*chain[i]).second,
  1.2004 +							layer_map,
  1.2005 +							max_time,
  1.2006 +							min_time,
  1.2007 +							true);
  1.2008 +
  1.2009 +						ai_assert(inv);
  1.2010 +						node_anims.push_back(inv);
  1.2011 +					
  1.2012 +						ai_assert(TransformationComp_RotationPivotInverse > i);
  1.2013 +						flags |= bit << (TransformationComp_RotationPivotInverse - i);
  1.2014 +					}
  1.2015 +
  1.2016 +					break;
  1.2017 +
  1.2018 +				case TransformationComp_Scaling:
  1.2019 +					na = GenerateScalingNodeAnim(chain_name, 
  1.2020 +						target, 
  1.2021 +						(*chain[i]).second,
  1.2022 +						layer_map,
  1.2023 +						max_time,
  1.2024 +						min_time);
  1.2025 +
  1.2026 +					break;
  1.2027 +
  1.2028 +				default:
  1.2029 +					ai_assert(false);
  1.2030 +				}
  1.2031 +
  1.2032 +				ai_assert(na);
  1.2033 +				node_anims.push_back(na);
  1.2034 +				continue;
  1.2035 +			}
  1.2036 +		}
  1.2037 +
  1.2038 +		node_anim_chain_bits[fixed_name] = flags;
  1.2039 +	}
  1.2040 +
  1.2041 +
  1.2042 +	// ------------------------------------------------------------------------------------------------
  1.2043 +	bool IsRedundantAnimationData(const Model& target, 
  1.2044 +		TransformationComp comp, 
  1.2045 +		const std::vector<const AnimationCurveNode*>& curves)
  1.2046 +	{
  1.2047 +		ai_assert(curves.size());
  1.2048 +
  1.2049 +		// look for animation nodes with
  1.2050 +		//  * sub channels for all relevant components set
  1.2051 +		//  * one key/value pair per component
  1.2052 +		//  * combined values match up the corresponding value in the bind pose node transformation
  1.2053 +		// only such nodes are 'redundant' for this function.
  1.2054 +
  1.2055 +		if (curves.size() > 1) {
  1.2056 +			return false;
  1.2057 +		}
  1.2058 +
  1.2059 +		const AnimationCurveNode& nd = *curves.front();
  1.2060 +		const AnimationCurveMap& sub_curves = nd.Curves();
  1.2061 +
  1.2062 +		const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
  1.2063 +		const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
  1.2064 +		const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
  1.2065 +
  1.2066 +		if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
  1.2067 +			return false;
  1.2068 +		}
  1.2069 +
  1.2070 +		const KeyValueList& vx = (*dx).second->GetValues();
  1.2071 +		const KeyValueList& vy = (*dy).second->GetValues();
  1.2072 +		const KeyValueList& vz = (*dz).second->GetValues();
  1.2073 +
  1.2074 +		if(vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
  1.2075 +			return false;
  1.2076 +		}
  1.2077 +
  1.2078 +		const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
  1.2079 +		const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(), 
  1.2080 +			NameTransformationCompProperty(comp), 
  1.2081 +			TransformationCompDefaultValue(comp)
  1.2082 +		);
  1.2083 +
  1.2084 +		const float epsilon = 1e-6f;
  1.2085 +		return (dyn_val - static_val).SquareLength() < epsilon;
  1.2086 +	}
  1.2087 +
  1.2088 +
  1.2089 +	// ------------------------------------------------------------------------------------------------
  1.2090 +	aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, 
  1.2091 +		const Model& target, 
  1.2092 +		const std::vector<const AnimationCurveNode*>& curves,
  1.2093 +		const LayerMap& layer_map,
  1.2094 +		double& max_time,
  1.2095 +		double& min_time)
  1.2096 +	{
  1.2097 +		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
  1.2098 +		na->mNodeName.Set(name);
  1.2099 +
  1.2100 +		ConvertRotationKeys(na, curves, layer_map, max_time,min_time, target.RotationOrder());
  1.2101 +
  1.2102 +		// dummy scaling key
  1.2103 +		na->mScalingKeys = new aiVectorKey[1];
  1.2104 +		na->mNumScalingKeys = 1;
  1.2105 +
  1.2106 +		na->mScalingKeys[0].mTime = 0.;
  1.2107 +		na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
  1.2108 +
  1.2109 +		// dummy position key
  1.2110 +		na->mPositionKeys = new aiVectorKey[1];
  1.2111 +		na->mNumPositionKeys = 1;
  1.2112 +
  1.2113 +		na->mPositionKeys[0].mTime = 0.;
  1.2114 +		na->mPositionKeys[0].mValue = aiVector3D();
  1.2115 +
  1.2116 +		return na.dismiss();
  1.2117 +	}
  1.2118 +
  1.2119 +
  1.2120 +	// ------------------------------------------------------------------------------------------------
  1.2121 +	aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, 
  1.2122 +		const Model& target, 
  1.2123 +		const std::vector<const AnimationCurveNode*>& curves,
  1.2124 +		const LayerMap& layer_map,
  1.2125 +		double& max_time,
  1.2126 +		double& min_time)
  1.2127 +	{
  1.2128 +		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
  1.2129 +		na->mNodeName.Set(name);
  1.2130 +
  1.2131 +		ConvertScaleKeys(na, curves, layer_map, max_time,min_time);
  1.2132 +
  1.2133 +		// dummy rotation key
  1.2134 +		na->mRotationKeys = new aiQuatKey[1];
  1.2135 +		na->mNumRotationKeys = 1;
  1.2136 +
  1.2137 +		na->mRotationKeys[0].mTime = 0.;
  1.2138 +		na->mRotationKeys[0].mValue = aiQuaternion();
  1.2139 +
  1.2140 +		// dummy position key
  1.2141 +		na->mPositionKeys = new aiVectorKey[1];
  1.2142 +		na->mNumPositionKeys = 1;
  1.2143 +
  1.2144 +		na->mPositionKeys[0].mTime = 0.;
  1.2145 +		na->mPositionKeys[0].mValue = aiVector3D();
  1.2146 +
  1.2147 +		return na.dismiss();
  1.2148 +	}
  1.2149 +
  1.2150 +
  1.2151 +	// ------------------------------------------------------------------------------------------------
  1.2152 +	aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, 
  1.2153 +		const Model& target, 
  1.2154 +		const std::vector<const AnimationCurveNode*>& curves,
  1.2155 +		const LayerMap& layer_map,
  1.2156 +		double& max_time,
  1.2157 +		double& min_time,
  1.2158 +		bool inverse = false)
  1.2159 +	{
  1.2160 +		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
  1.2161 +		na->mNodeName.Set(name);
  1.2162 +
  1.2163 +		ConvertTranslationKeys(na, curves, layer_map, max_time,min_time);
  1.2164 +
  1.2165 +		if (inverse) {
  1.2166 +			for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) {
  1.2167 +				na->mPositionKeys[i].mValue *= -1.0f;
  1.2168 +			}
  1.2169 +		}
  1.2170 +
  1.2171 +		// dummy scaling key
  1.2172 +		na->mScalingKeys = new aiVectorKey[1];
  1.2173 +		na->mNumScalingKeys = 1;
  1.2174 +
  1.2175 +		na->mScalingKeys[0].mTime = 0.;
  1.2176 +		na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
  1.2177 +
  1.2178 +		// dummy rotation key
  1.2179 +		na->mRotationKeys = new aiQuatKey[1];
  1.2180 +		na->mNumRotationKeys = 1;
  1.2181 +
  1.2182 +		na->mRotationKeys[0].mTime = 0.;
  1.2183 +		na->mRotationKeys[0].mValue = aiQuaternion();
  1.2184 +
  1.2185 +		return na.dismiss();
  1.2186 +	}
  1.2187 +
  1.2188 +
  1.2189 +	// ------------------------------------------------------------------------------------------------
  1.2190 +	// generate node anim, extracting only Rotation, Scaling and Translation from the given chain
  1.2191 +	aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name, 
  1.2192 +		const Model& target, 
  1.2193 +		NodeMap::const_iterator chain[TransformationComp_MAXIMUM], 
  1.2194 +		NodeMap::const_iterator iter_end,
  1.2195 +		const LayerMap& layer_map,
  1.2196 +		double& max_time,
  1.2197 +		double& min_time,
  1.2198 +		bool reverse_order = false)
  1.2199 +
  1.2200 +	{
  1.2201 +		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
  1.2202 +		na->mNodeName.Set(name);
  1.2203 +
  1.2204 +		const PropertyTable& props = target.Props();
  1.2205 +
  1.2206 +		// need to convert from TRS order to SRT?
  1.2207 +		if(reverse_order) {
  1.2208 +		
  1.2209 +			aiVector3D def_scale, def_translate;
  1.2210 +			aiQuaternion def_rot;
  1.2211 +
  1.2212 +			KeyFrameListList scaling;
  1.2213 +			KeyFrameListList translation;
  1.2214 +			KeyFrameListList rotation;
  1.2215 +			
  1.2216 +			if(chain[TransformationComp_Scaling] != iter_end) {
  1.2217 +				scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second);
  1.2218 +			}
  1.2219 +			else {
  1.2220 +				def_scale = PropertyGet(props,"Lcl Scaling",aiVector3D(1.f,1.f,1.f));
  1.2221 +			}
  1.2222 +
  1.2223 +			if(chain[TransformationComp_Translation] != iter_end) {
  1.2224 +				translation = GetKeyframeList((*chain[TransformationComp_Translation]).second);
  1.2225 +			}
  1.2226 +			else {
  1.2227 +				def_translate = PropertyGet(props,"Lcl Translation",aiVector3D(0.f,0.f,0.f));
  1.2228 +			}
  1.2229 +			
  1.2230 +			if(chain[TransformationComp_Rotation] != iter_end) {
  1.2231 +				rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second);
  1.2232 +			}
  1.2233 +			else {
  1.2234 +				def_rot = EulerToQuaternion(PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
  1.2235 +					target.RotationOrder());
  1.2236 +			}
  1.2237 +
  1.2238 +			KeyFrameListList joined;
  1.2239 +			joined.insert(joined.end(), scaling.begin(), scaling.end());
  1.2240 +			joined.insert(joined.end(), translation.begin(), translation.end());
  1.2241 +			joined.insert(joined.end(), rotation.begin(), rotation.end());
  1.2242 +
  1.2243 +			const KeyTimeList& times = GetKeyTimeList(joined);
  1.2244 +
  1.2245 +			aiQuatKey* out_quat = new aiQuatKey[times.size()];
  1.2246 +			aiVectorKey* out_scale = new aiVectorKey[times.size()];
  1.2247 +			aiVectorKey* out_translation = new aiVectorKey[times.size()];
  1.2248 +
  1.2249 +			ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation, 
  1.2250 +				scaling, 
  1.2251 +				translation, 
  1.2252 +				rotation, 
  1.2253 +				times,
  1.2254 +				max_time,
  1.2255 +				min_time,
  1.2256 +				target.RotationOrder(),
  1.2257 +				def_scale,
  1.2258 +				def_translate,
  1.2259 +				def_rot);
  1.2260 +
  1.2261 +			// XXX remove duplicates / redundant keys which this operation did
  1.2262 +			// likely produce if not all three channels were equally dense.
  1.2263 +
  1.2264 +			na->mNumScalingKeys = static_cast<unsigned int>(times.size());
  1.2265 +			na->mNumRotationKeys = na->mNumScalingKeys;
  1.2266 +			na->mNumPositionKeys = na->mNumScalingKeys;
  1.2267 +
  1.2268 +			na->mScalingKeys = out_scale;
  1.2269 +			na->mRotationKeys = out_quat;
  1.2270 +			na->mPositionKeys = out_translation;
  1.2271 +		}
  1.2272 +		else {
  1.2273 +
  1.2274 +			// if a particular transformation is not given, grab it from
  1.2275 +			// the corresponding node to meet the semantics of aiNodeAnim,
  1.2276 +			// which requires all of rotation, scaling and translation
  1.2277 +			// to be set.
  1.2278 +			if(chain[TransformationComp_Scaling] != iter_end) {
  1.2279 +				ConvertScaleKeys(na, (*chain[TransformationComp_Scaling]).second, 
  1.2280 +					layer_map, 
  1.2281 +					max_time, 
  1.2282 +					min_time);
  1.2283 +			}
  1.2284 +			else {
  1.2285 +				na->mScalingKeys = new aiVectorKey[1];
  1.2286 +				na->mNumScalingKeys = 1;
  1.2287 +
  1.2288 +				na->mScalingKeys[0].mTime = 0.;
  1.2289 +				na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",
  1.2290 +					aiVector3D(1.f,1.f,1.f));
  1.2291 +			}
  1.2292 +
  1.2293 +			if(chain[TransformationComp_Rotation] != iter_end) {
  1.2294 +				ConvertRotationKeys(na, (*chain[TransformationComp_Rotation]).second, 
  1.2295 +					layer_map, 
  1.2296 +					max_time,
  1.2297 +					min_time,
  1.2298 +					target.RotationOrder());
  1.2299 +			}
  1.2300 +			else {
  1.2301 +				na->mRotationKeys = new aiQuatKey[1];
  1.2302 +				na->mNumRotationKeys = 1;
  1.2303 +
  1.2304 +				na->mRotationKeys[0].mTime = 0.;
  1.2305 +				na->mRotationKeys[0].mValue = EulerToQuaternion(
  1.2306 +					PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
  1.2307 +					target.RotationOrder());
  1.2308 +			}
  1.2309 +
  1.2310 +			if(chain[TransformationComp_Translation] != iter_end) {
  1.2311 +				ConvertTranslationKeys(na, (*chain[TransformationComp_Translation]).second, 
  1.2312 +					layer_map, 
  1.2313 +					max_time, 
  1.2314 +					min_time);
  1.2315 +			}
  1.2316 +			else {
  1.2317 +				na->mPositionKeys = new aiVectorKey[1];
  1.2318 +				na->mNumPositionKeys = 1;
  1.2319 +
  1.2320 +				na->mPositionKeys[0].mTime = 0.;
  1.2321 +				na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",
  1.2322 +					aiVector3D(0.f,0.f,0.f));
  1.2323 +			}
  1.2324 +
  1.2325 +		}
  1.2326 +		return na.dismiss();
  1.2327 +	}
  1.2328 +
  1.2329 +
  1.2330 +
  1.2331 +	// key (time), value, mapto (component index)
  1.2332 +	typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
  1.2333 +	typedef std::vector<KeyFrameList> KeyFrameListList;
  1.2334 +
  1.2335 +	
  1.2336 +
  1.2337 +	// ------------------------------------------------------------------------------------------------
  1.2338 +	KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes)
  1.2339 +	{
  1.2340 +		KeyFrameListList inputs;
  1.2341 +		inputs.reserve(nodes.size()*3);
  1.2342 +
  1.2343 +		BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
  1.2344 +			ai_assert(node);
  1.2345 +
  1.2346 +			const AnimationCurveMap& curves = node->Curves();
  1.2347 +			BOOST_FOREACH(const AnimationCurveMap::value_type& kv, curves) {
  1.2348 +
  1.2349 +				unsigned int mapto;
  1.2350 +				if (kv.first == "d|X") {
  1.2351 +					mapto = 0;
  1.2352 +				}
  1.2353 +				else if (kv.first == "d|Y") {
  1.2354 +					mapto = 1;
  1.2355 +				}
  1.2356 +				else if (kv.first == "d|Z") {
  1.2357 +					mapto = 2;
  1.2358 +				}
  1.2359 +				else {
  1.2360 +					FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component");
  1.2361 +					continue;
  1.2362 +				}
  1.2363 +
  1.2364 +				const AnimationCurve* const curve = kv.second;
  1.2365 +				ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size());
  1.2366 +
  1.2367 +				inputs.push_back(boost::make_tuple(&curve->GetKeys(), &curve->GetValues(), mapto));
  1.2368 +			}
  1.2369 +		}
  1.2370 +		return inputs; // pray for NRVO :-)
  1.2371 +	}
  1.2372 +
  1.2373 +
  1.2374 +	// ------------------------------------------------------------------------------------------------
  1.2375 +	KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs)
  1.2376 +	{
  1.2377 +		ai_assert(inputs.size());
  1.2378 +
  1.2379 +		// reserve some space upfront - it is likely that the keyframe lists
  1.2380 +		// have matching time values, so max(of all keyframe lists) should 
  1.2381 +		// be a good estimate.
  1.2382 +		KeyTimeList keys;
  1.2383 +		
  1.2384 +		size_t estimate = 0;
  1.2385 +		BOOST_FOREACH(const KeyFrameList& kfl, inputs) {
  1.2386 +			estimate = std::max(estimate, kfl.get<0>()->size());
  1.2387 +		}
  1.2388 +
  1.2389 +		keys.reserve(estimate);
  1.2390 +
  1.2391 +		std::vector<unsigned int> next_pos;
  1.2392 +		next_pos.resize(inputs.size(),0);
  1.2393 +
  1.2394 +		const size_t count = inputs.size();
  1.2395 +		while(true) {
  1.2396 +
  1.2397 +			uint64_t min_tick = std::numeric_limits<uint64_t>::max();
  1.2398 +			for (size_t i = 0; i < count; ++i) {
  1.2399 +				const KeyFrameList& kfl = inputs[i];
  1.2400 +
  1.2401 +				if (kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) < min_tick) {
  1.2402 +					min_tick = kfl.get<0>()->at(next_pos[i]);
  1.2403 +				}
  1.2404 +			}
  1.2405 +
  1.2406 +			if (min_tick == std::numeric_limits<uint64_t>::max()) {
  1.2407 +				break;
  1.2408 +			}
  1.2409 +			keys.push_back(min_tick);
  1.2410 +
  1.2411 +			for (size_t i = 0; i < count; ++i) {
  1.2412 +				const KeyFrameList& kfl = inputs[i];
  1.2413 +
  1.2414 +
  1.2415 +				while(kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == min_tick) {
  1.2416 +					++next_pos[i];
  1.2417 +				}
  1.2418 +			}
  1.2419 +		}	
  1.2420 +
  1.2421 +		return keys;
  1.2422 +	}
  1.2423 +
  1.2424 +
  1.2425 +	// ------------------------------------------------------------------------------------------------
  1.2426 +	void InterpolateKeys(aiVectorKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs, 
  1.2427 +		const bool geom, 
  1.2428 +		double& max_time,
  1.2429 +		double& min_time)
  1.2430 +
  1.2431 +	{
  1.2432 +		ai_assert(keys.size());
  1.2433 +		ai_assert(valOut);
  1.2434 +
  1.2435 +		std::vector<unsigned int> next_pos;
  1.2436 +		const size_t count = inputs.size();
  1.2437 +
  1.2438 +		next_pos.resize(inputs.size(),0);
  1.2439 +
  1.2440 +		BOOST_FOREACH(KeyTimeList::value_type time, keys) {
  1.2441 +			float result[3] = {0.0f, 0.0f, 0.0f};
  1.2442 +			if(geom) {
  1.2443 +				result[0] = result[1] = result[2] = 1.0f;
  1.2444 +			}
  1.2445 +
  1.2446 +			for (size_t i = 0; i < count; ++i) {
  1.2447 +				const KeyFrameList& kfl = inputs[i];
  1.2448 +
  1.2449 +				const size_t ksize = kfl.get<0>()->size();
  1.2450 +				if (ksize > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == time) {
  1.2451 +					++next_pos[i]; 
  1.2452 +				}
  1.2453 +
  1.2454 +				const size_t id0 = next_pos[i]>0 ? next_pos[i]-1 : 0;
  1.2455 +				const size_t id1 = next_pos[i]==ksize ? ksize-1 : next_pos[i];
  1.2456 +
  1.2457 +				// use lerp for interpolation
  1.2458 +				const KeyValueList::value_type valueA = kfl.get<1>()->at(id0);
  1.2459 +				const KeyValueList::value_type valueB = kfl.get<1>()->at(id1);
  1.2460 +
  1.2461 +				const KeyTimeList::value_type timeA = kfl.get<0>()->at(id0);
  1.2462 +				const KeyTimeList::value_type timeB = kfl.get<0>()->at(id1);
  1.2463 +
  1.2464 +				// do the actual interpolation in double-precision arithmetics
  1.2465 +				// because it is a bit sensitive to rounding errors.
  1.2466 +				const double factor = timeB == timeA ? 0. : static_cast<double>((time - timeA) / (timeB - timeA));
  1.2467 +				const float interpValue = static_cast<float>(valueA + (valueB - valueA) * factor);
  1.2468 +
  1.2469 +				if(geom) {
  1.2470 +					result[kfl.get<2>()] *= interpValue;
  1.2471 +				}
  1.2472 +				else {
  1.2473 +					result[kfl.get<2>()] += interpValue;
  1.2474 +				}
  1.2475 +			}
  1.2476 +
  1.2477 +			// magic value to convert fbx times to seconds
  1.2478 +			valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
  1.2479 +
  1.2480 +			min_time = std::min(min_time, valOut->mTime);
  1.2481 +			max_time = std::max(max_time, valOut->mTime);
  1.2482 +
  1.2483 +			valOut->mValue.x = result[0];
  1.2484 +			valOut->mValue.y = result[1];
  1.2485 +			valOut->mValue.z = result[2];
  1.2486 +			
  1.2487 +			++valOut;
  1.2488 +		}
  1.2489 +	}
  1.2490 +
  1.2491 +
  1.2492 +	// ------------------------------------------------------------------------------------------------
  1.2493 +	void InterpolateKeys(aiQuatKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs, 
  1.2494 +		const bool geom,
  1.2495 +		double& maxTime,
  1.2496 +		double& minTime,
  1.2497 +		Model::RotOrder order)
  1.2498 +	{
  1.2499 +		ai_assert(keys.size());
  1.2500 +		ai_assert(valOut);
  1.2501 +
  1.2502 +		boost::scoped_array<aiVectorKey> temp(new aiVectorKey[keys.size()]);
  1.2503 +		InterpolateKeys(temp.get(),keys,inputs,geom,maxTime, minTime);
  1.2504 +
  1.2505 +		aiMatrix4x4 m;
  1.2506 +
  1.2507 +		aiQuaternion lastq;
  1.2508 +
  1.2509 +		for (size_t i = 0, c = keys.size(); i < c; ++i) {
  1.2510 +
  1.2511 +			valOut[i].mTime = temp[i].mTime;
  1.2512 +
  1.2513 +			
  1.2514 +			GetRotationMatrix(order, temp[i].mValue, m);
  1.2515 +			aiQuaternion quat = aiQuaternion(aiMatrix3x3(m));
  1.2516 +
  1.2517 +			// take shortest path by checking the inner product
  1.2518 +			// http://www.3dkingdoms.com/weekly/weekly.php?a=36
  1.2519 +			if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0)
  1.2520 +			{
  1.2521 +				quat.x = -quat.x;
  1.2522 +				quat.y = -quat.y;
  1.2523 +				quat.z = -quat.z;
  1.2524 +				quat.w = -quat.w;
  1.2525 +			} 
  1.2526 +			lastq = quat;
  1.2527 +
  1.2528 +			valOut[i].mValue = quat; 
  1.2529 +		}
  1.2530 +	}
  1.2531 +
  1.2532 +
  1.2533 +	// ------------------------------------------------------------------------------------------------
  1.2534 +	void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
  1.2535 +		aiVectorKey* out_translation, 
  1.2536 +		const KeyFrameListList& scaling, 
  1.2537 +		const KeyFrameListList& translation, 
  1.2538 +		const KeyFrameListList& rotation, 
  1.2539 +		const KeyTimeList& times,
  1.2540 +		double& maxTime,
  1.2541 +		double& minTime,
  1.2542 +		Model::RotOrder order,
  1.2543 +		const aiVector3D& def_scale,
  1.2544 +		const aiVector3D& def_translate,
  1.2545 +		const aiQuaternion& def_rotation)
  1.2546 +	{
  1.2547 +		if (rotation.size()) {
  1.2548 +			InterpolateKeys(out_quat, times, rotation, false, maxTime, minTime, order);
  1.2549 +		}
  1.2550 +		else {
  1.2551 +			for (size_t i = 0; i < times.size(); ++i) {
  1.2552 +				out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
  1.2553 +				out_quat[i].mValue = def_rotation;
  1.2554 +			}
  1.2555 +		}
  1.2556 +
  1.2557 +		if (scaling.size()) {
  1.2558 +			InterpolateKeys(out_scale, times, scaling, true, maxTime, minTime);
  1.2559 +		}
  1.2560 +		else {
  1.2561 +			for (size_t i = 0; i < times.size(); ++i) {
  1.2562 +				out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
  1.2563 +				out_scale[i].mValue = def_scale;
  1.2564 +			}
  1.2565 +		}
  1.2566 +
  1.2567 +		if (translation.size()) {
  1.2568 +			InterpolateKeys(out_translation, times, translation, false, maxTime, minTime);
  1.2569 +		}
  1.2570 +		else {
  1.2571 +			for (size_t i = 0; i < times.size(); ++i) {
  1.2572 +				out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
  1.2573 +				out_translation[i].mValue = def_translate;
  1.2574 +			}
  1.2575 +		}
  1.2576 +
  1.2577 +		const size_t count = times.size();
  1.2578 +		for (size_t i = 0; i < count; ++i) {
  1.2579 +			aiQuaternion& r = out_quat[i].mValue;
  1.2580 +			aiVector3D& s = out_scale[i].mValue;
  1.2581 +			aiVector3D& t = out_translation[i].mValue;
  1.2582 +
  1.2583 +			aiMatrix4x4 mat, temp;
  1.2584 +			aiMatrix4x4::Translation(t, mat);
  1.2585 +			mat *= aiMatrix4x4( r.GetMatrix() );
  1.2586 +			mat *= aiMatrix4x4::Scaling(s, temp);
  1.2587 +
  1.2588 +			mat.Decompose(s, r, t);
  1.2589 +		}
  1.2590 +	}
  1.2591 +
  1.2592 +
  1.2593 +	// ------------------------------------------------------------------------------------------------
  1.2594 +	// euler xyz -> quat
  1.2595 +	aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order) 
  1.2596 +	{
  1.2597 +		aiMatrix4x4 m;
  1.2598 +		GetRotationMatrix(order, rot, m);
  1.2599 +
  1.2600 +		return aiQuaternion(aiMatrix3x3(m));
  1.2601 +	}
  1.2602 +
  1.2603 +
  1.2604 +	// ------------------------------------------------------------------------------------------------
  1.2605 +	void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& layers,
  1.2606 +		double& maxTime,
  1.2607 +		double& minTime)
  1.2608 +	{
  1.2609 +		ai_assert(nodes.size());
  1.2610 +
  1.2611 +		// XXX for now, assume scale should be blended geometrically (i.e. two
  1.2612 +		// layers should be multiplied with each other). There is a FBX 
  1.2613 +		// property in the layer to specify the behaviour, though.
  1.2614 +
  1.2615 +		const KeyFrameListList& inputs = GetKeyframeList(nodes);
  1.2616 +		const KeyTimeList& keys = GetKeyTimeList(inputs);
  1.2617 +
  1.2618 +		na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
  1.2619 +		na->mScalingKeys = new aiVectorKey[keys.size()];
  1.2620 +		InterpolateKeys(na->mScalingKeys, keys, inputs, true, maxTime, minTime);
  1.2621 +	}
  1.2622 +
  1.2623 +
  1.2624 +	// ------------------------------------------------------------------------------------------------
  1.2625 +	void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
  1.2626 +		const LayerMap& layers,
  1.2627 +		double& maxTime,
  1.2628 +		double& minTime)
  1.2629 +	{
  1.2630 +		ai_assert(nodes.size());
  1.2631 +
  1.2632 +		// XXX see notes in ConvertScaleKeys()
  1.2633 +		const KeyFrameListList& inputs = GetKeyframeList(nodes);
  1.2634 +		const KeyTimeList& keys = GetKeyTimeList(inputs);
  1.2635 +
  1.2636 +		na->mNumPositionKeys = static_cast<unsigned int>(keys.size());
  1.2637 +		na->mPositionKeys = new aiVectorKey[keys.size()];
  1.2638 +		InterpolateKeys(na->mPositionKeys, keys, inputs, false, maxTime, minTime);
  1.2639 +	}
  1.2640 +
  1.2641 +
  1.2642 +	// ------------------------------------------------------------------------------------------------
  1.2643 +	void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
  1.2644 +		const LayerMap& layers, 
  1.2645 +		double& maxTime,
  1.2646 +		double& minTime,
  1.2647 +		Model::RotOrder order)
  1.2648 +	{
  1.2649 +		ai_assert(nodes.size());
  1.2650 +
  1.2651 +		// XXX see notes in ConvertScaleKeys()
  1.2652 +		const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes);
  1.2653 +		const KeyTimeList& keys = GetKeyTimeList(inputs);
  1.2654 +
  1.2655 +		na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
  1.2656 +		na->mRotationKeys = new aiQuatKey[keys.size()];
  1.2657 +		InterpolateKeys(na->mRotationKeys, keys, inputs, false, maxTime, minTime, order);
  1.2658 +	}
  1.2659 +
  1.2660 +
  1.2661 +	// ------------------------------------------------------------------------------------------------
  1.2662 +	// copy generated meshes, animations, lights, cameras and textures to the output scene
  1.2663 +	void TransferDataToScene()
  1.2664 +	{
  1.2665 +		ai_assert(!out->mMeshes && !out->mNumMeshes);
  1.2666 +
  1.2667 +		// note: the trailing () ensures initialization with NULL - not
  1.2668 +		// many C++ users seem to know this, so pointing it out to avoid
  1.2669 +		// confusion why this code works.
  1.2670 +
  1.2671 +		if(meshes.size()) {
  1.2672 +			out->mMeshes = new aiMesh*[meshes.size()]();
  1.2673 +			out->mNumMeshes = static_cast<unsigned int>(meshes.size());
  1.2674 +
  1.2675 +			std::swap_ranges(meshes.begin(),meshes.end(),out->mMeshes);
  1.2676 +		}
  1.2677 +
  1.2678 +		if(materials.size()) {
  1.2679 +			out->mMaterials = new aiMaterial*[materials.size()]();
  1.2680 +			out->mNumMaterials = static_cast<unsigned int>(materials.size());
  1.2681 +
  1.2682 +			std::swap_ranges(materials.begin(),materials.end(),out->mMaterials);
  1.2683 +		}
  1.2684 +
  1.2685 +		if(animations.size()) {
  1.2686 +			out->mAnimations = new aiAnimation*[animations.size()]();
  1.2687 +			out->mNumAnimations = static_cast<unsigned int>(animations.size());
  1.2688 +
  1.2689 +			std::swap_ranges(animations.begin(),animations.end(),out->mAnimations);
  1.2690 +		}
  1.2691 +
  1.2692 +		if(lights.size()) {
  1.2693 +			out->mLights = new aiLight*[lights.size()]();
  1.2694 +			out->mNumLights = static_cast<unsigned int>(lights.size());
  1.2695 +
  1.2696 +			std::swap_ranges(lights.begin(),lights.end(),out->mLights);
  1.2697 +		}
  1.2698 +
  1.2699 +		if(cameras.size()) {
  1.2700 +			out->mCameras = new aiCamera*[cameras.size()]();
  1.2701 +			out->mNumCameras = static_cast<unsigned int>(cameras.size());
  1.2702 +
  1.2703 +			std::swap_ranges(cameras.begin(),cameras.end(),out->mCameras);
  1.2704 +		}
  1.2705 +	}
  1.2706 +
  1.2707 +
  1.2708 +private:
  1.2709 +
  1.2710 +	// 0: not assigned yet, others: index is value - 1
  1.2711 +	unsigned int defaultMaterialIndex;
  1.2712 +
  1.2713 +	std::vector<aiMesh*> meshes;
  1.2714 +	std::vector<aiMaterial*> materials;
  1.2715 +	std::vector<aiAnimation*> animations;
  1.2716 +	std::vector<aiLight*> lights;
  1.2717 +	std::vector<aiCamera*> cameras;
  1.2718 +
  1.2719 +	typedef std::map<const Material*, unsigned int> MaterialMap;
  1.2720 +	MaterialMap materials_converted;
  1.2721 +
  1.2722 +	typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
  1.2723 +	MeshMap meshes_converted;
  1.2724 +
  1.2725 +	// fixed node name -> which trafo chain components have animations?
  1.2726 +	typedef std::map<std::string, unsigned int> NodeAnimBitMap;
  1.2727 +	NodeAnimBitMap node_anim_chain_bits;
  1.2728 +
  1.2729 +	// name -> has had its prefix_stripped?
  1.2730 +	typedef std::map<std::string, bool> NodeNameMap;
  1.2731 +	NodeNameMap node_names;
  1.2732 +
  1.2733 +	typedef std::map<std::string, std::string> NameNameMap;
  1.2734 +	NameNameMap renamed_nodes;
  1.2735 +
  1.2736 +	double anim_fps;
  1.2737 +
  1.2738 +	aiScene* const out;
  1.2739 +	const FBX::Document& doc;
  1.2740 +};
  1.2741 +
  1.2742 +//} // !anon
  1.2743 +
  1.2744 +// ------------------------------------------------------------------------------------------------
  1.2745 +void ConvertToAssimpScene(aiScene* out, const Document& doc)
  1.2746 +{
  1.2747 +	Converter converter(out,doc);
  1.2748 +}
  1.2749 +
  1.2750 +} // !FBX
  1.2751 +} // !Assimp
  1.2752 +
  1.2753 +#endif