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