- fbx: revamp transformation code to support rotation/scaling pivots and offsets. The information is encoded in additional nodes in the scene hierarchy, which are tagged with a special naming scheme to make them easy for users to identify and map to their systems.

pull/14/head
Alexander Gessler 2012-07-27 03:00:16 +02:00
parent 23c62f07f7
commit 7f082e0aae
2 changed files with 232 additions and 31 deletions

View File

@ -59,6 +59,9 @@ namespace FBX {
using namespace Util;
#define MAGIC_NODE_TAG "_$AssimpFbx$"
// XXX vc9's debugger won't step into anonymous namespaces
//namespace {
@ -129,6 +132,8 @@ private:
std::vector<aiNode*> nodes;
nodes.reserve(conns.size());
std::vector<aiNode*> nodes_chain;
try {
BOOST_FOREACH(const Connection* con, conns) {
@ -146,16 +151,27 @@ private:
const Model* const model = dynamic_cast<const Model*>(object);
if(model) {
aiNode* nd = new aiNode();
nodes.push_back(nd);
nodes_chain.clear();
GenerateTransformationNodeChain(*model,nodes_chain);
nd->mName.Set(FixNodeName(model->Name()));
nd->mParent = &parent;
ai_assert(nodes_chain.size());
ConvertTransformation(*model,*nd);
// link all nodes in a row
aiNode* last_parent = &parent;
BOOST_FOREACH(aiNode* prenode, nodes_chain) {
ai_assert(prenode);
ConvertModel(*model, *nd);
ConvertNodes(model->ID(), *nd);
prenode->mParent = last_parent;
last_parent = prenode;
}
// attach geometry
ConvertModel(*model, *nodes_chain.back());
// attach sub-nodes
ConvertNodes(model->ID(), *last_parent);
nodes.push_back(nodes_chain.back());
}
}
@ -167,50 +183,212 @@ private:
}
}
catch(std::exception&) {
std::for_each(nodes.begin(),nodes.end(),Util::delete_fun<aiNode>());
Util::delete_fun<aiNode> deleter;
std::for_each(nodes.begin(),nodes.end(),deleter);
std::for_each(nodes_chain.begin(),nodes_chain.end(),deleter);
}
}
enum TransformationComp
{
TransformationComp_Translation = 0,
TransformationComp_RotationOffset,
TransformationComp_RotationPivot,
TransformationComp_PreRotation,
TransformationComp_Rotation,
TransformationComp_PostRotation,
TransformationComp_RotationPivotInverse,
TransformationComp_ScalingOffset,
TransformationComp_ScalingPivot,
TransformationComp_Scaling,
TransformationComp_ScalingPivotInverse,
TransformationComp_MAXIMUM
};
// ------------------------------------------------------------------------------------------------
const char* NameTransformationComp(TransformationComp comp)
{
switch(comp)
{
case TransformationComp_Translation:
return "Translation";
case TransformationComp_RotationOffset:
return "RotationOffset";
case TransformationComp_RotationPivot:
return "RotationPivot";
case TransformationComp_PreRotation:
return "PreRotation";
case TransformationComp_Rotation:
return "Rotation";
case TransformationComp_PostRotation:
return "PostRotation";
case TransformationComp_RotationPivotInverse:
return "RotationPivotInverse";
case TransformationComp_ScalingOffset:
return "ScalingOffset";
case TransformationComp_ScalingPivot:
return "ScalingPivot";
case TransformationComp_Scaling:
return "Scaling";
case TransformationComp_ScalingPivotInverse:
return "ScalingPivotInverse";
}
ai_assert(false);
}
enum RotationMode
{
RotationMode_Euler_XYZ
};
// ------------------------------------------------------------------------------------------------
void GetRotationMatrix(RotationMode mode, const aiVector3D& rotation, aiMatrix4x4& out)
{
const float angle_epsilon = 1e-6f;
aiMatrix4x4 temp;
out = aiMatrix4x4();
switch(mode)
{
case RotationMode_Euler_XYZ:
if(fabs(rotation.x) > angle_epsilon) {
out *= aiMatrix4x4::RotationX(rotation.x,temp);
}
if(fabs(rotation.y) > angle_epsilon) {
out *= aiMatrix4x4::RotationY(rotation.y,temp);
}
if(fabs(rotation.z) > angle_epsilon) {
out *= aiMatrix4x4::RotationZ(rotation.z,temp);
}
return;
}
ai_assert(false);
}
// ------------------------------------------------------------------------------------------------
void ConvertTransformation(const Model& model, aiNode& nd)
/** note: memory for output_nodes will be managed by the caller */
void GenerateTransformationNodeChain(const Model& model,
std::vector<aiNode*>& output_nodes)
{
const PropertyTable& props = model.Props();
// XXX this is not complete, need to handle rotation and scaling pivots etc
// XXX handle different rotation modes
const RotationMode rot = RotationMode_Euler_XYZ;
bool ok;
aiVector3D Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
if(!ok) {
Translation = aiVector3D(0.0f,0.0f,0.0f);
aiMatrix4x4 chain[TransformationComp_MAXIMUM];
std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
// generate transformation matrices for all the different transformation components
const float zero_epsilon = 1e-6f;
bool is_complex = false;
const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props,"PreRotation",ok);
if(ok && PreRotation.SquareLength() > zero_epsilon) {
is_complex = true;
GetRotationMatrix(rot, PreRotation, chain[TransformationComp_PreRotation]);
}
aiVector3D Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
if(!ok) {
Scaling = aiVector3D(1.0f,1.0f,1.0f);
const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
if(ok && PostRotation.SquareLength() > zero_epsilon) {
is_complex = true;
GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
}
// XXX euler angles, radians, xyz order?
aiVector3D Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
if(!ok) {
Rotation = aiVector3D(0.0f,0.0f,0.0f);
const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
if(ok && RotationPivot.SquareLength() > zero_epsilon) {
is_complex = true;
aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
}
aiMatrix4x4 temp;
nd.mTransformation = aiMatrix4x4::Scaling(Scaling,temp);
if(fabs(Rotation.x) > 1e-6f) {
nd.mTransformation *= aiMatrix4x4::RotationX(Rotation.x,temp);
const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
if(ok && RotationOffset.SquareLength() > zero_epsilon) {
is_complex = true;
aiMatrix4x4::Translation(RotationOffset,chain[TransformationComp_RotationOffset]);
}
if(fabs(Rotation.y) > 1e-6f) {
nd.mTransformation *= aiMatrix4x4::RotationY(Rotation.y,temp);
const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props,"ScalingOffset",ok);
if(ok && ScalingOffset.SquareLength() > zero_epsilon) {
is_complex = true;
aiMatrix4x4::Translation(ScalingOffset,chain[TransformationComp_ScalingOffset]);
}
if(fabs(Rotation.z) > 1e-6f) {
nd.mTransformation *= aiMatrix4x4::RotationZ(Rotation.z,temp);
const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props,"ScalingPivot",ok);
if(ok && ScalingPivot.SquareLength() > zero_epsilon) {
is_complex = true;
aiMatrix4x4::Translation(ScalingPivot,chain[TransformationComp_ScalingPivot]);
aiMatrix4x4::Translation(-ScalingPivot,chain[TransformationComp_ScalingPivotInverse]);
}
const aiVector3D& Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
if(ok && Translation.SquareLength() > zero_epsilon) {
aiMatrix4x4::Translation(Translation,chain[TransformationComp_Translation]);
}
const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
}
const aiVector3D& Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
if(ok && Translation.SquareLength() > zero_epsilon) {
GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
}
const std::string& name = FixNodeName(model.Name());
// now, if we have more than just Translation, Scaling and Rotation,
// we need to generate a full node chain to accommodate for assimp's
// lack to express pivots and offsets.
if(is_complex && doc.Settings().preservePivots) {
FBXImporter::LogInfo("generating full transformation chain for node: " + name);
const std::string mid = std::string(MAGIC_NODE_TAG) + "_";
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
// XXX this may cause trouble with animations
if (chain[i].IsIdentity()) {
continue;
}
aiNode* nd = new aiNode();
output_nodes.push_back(nd);
nd->mName.Set(name + mid + NameTransformationComp(static_cast<TransformationComp>(i)));
nd->mTransformation = chain[i];
}
ai_assert(output_nodes.size());
return;
}
// else, we can just multiply the matrices together
aiNode* nd = new aiNode();
output_nodes.push_back(nd);
nd->mName.Set(name);
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
nd->mTransformation *= chain[i];
}
nd.mTransformation.a4 = Translation.x;
nd.mTransformation.b4 = Translation.y;
nd.mTransformation.c4 = Translation.z;
}

View File

@ -59,6 +59,7 @@ struct ImportSettings
, readAnimations(true)
, strictMode(true)
, readWeights(true)
, preservePivots(true)
{}
@ -103,6 +104,28 @@ struct ImportSettings
/** read bones (vertex weights and deform info).
* Default value is true. */
bool readWeights;
/** preserve transformation pivots and offsets. Since these can
* not directly be represented in assimp, additional dummy
* nodes will be generated. Note that settings this to false
* can make animation import a lot slower. The default value
* is true.
*
* The naming scheme for the generated nodes is:
* <OriginalName>_$AssimpFbx$_<TransformName>
*
* where <TransformName> is one of
* RotationPivot
* RotationOffset
* PreRotation
* PostRotation
* ScalingPivot
* ScalingOffset
* Translation
* Scaling
* Rotation
**/
bool preservePivots;
};