- 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.
parent
23c62f07f7
commit
7f082e0aae
|
@ -59,6 +59,9 @@ namespace FBX {
|
||||||
|
|
||||||
using namespace Util;
|
using namespace Util;
|
||||||
|
|
||||||
|
|
||||||
|
#define MAGIC_NODE_TAG "_$AssimpFbx$"
|
||||||
|
|
||||||
// XXX vc9's debugger won't step into anonymous namespaces
|
// XXX vc9's debugger won't step into anonymous namespaces
|
||||||
//namespace {
|
//namespace {
|
||||||
|
|
||||||
|
@ -129,6 +132,8 @@ private:
|
||||||
std::vector<aiNode*> nodes;
|
std::vector<aiNode*> nodes;
|
||||||
nodes.reserve(conns.size());
|
nodes.reserve(conns.size());
|
||||||
|
|
||||||
|
std::vector<aiNode*> nodes_chain;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
BOOST_FOREACH(const Connection* con, conns) {
|
BOOST_FOREACH(const Connection* con, conns) {
|
||||||
|
|
||||||
|
@ -146,16 +151,27 @@ private:
|
||||||
const Model* const model = dynamic_cast<const Model*>(object);
|
const Model* const model = dynamic_cast<const Model*>(object);
|
||||||
|
|
||||||
if(model) {
|
if(model) {
|
||||||
aiNode* nd = new aiNode();
|
nodes_chain.clear();
|
||||||
nodes.push_back(nd);
|
GenerateTransformationNodeChain(*model,nodes_chain);
|
||||||
|
|
||||||
nd->mName.Set(FixNodeName(model->Name()));
|
ai_assert(nodes_chain.size());
|
||||||
nd->mParent = &parent;
|
|
||||||
|
|
||||||
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);
|
prenode->mParent = last_parent;
|
||||||
ConvertNodes(model->ID(), *nd);
|
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&) {
|
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();
|
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;
|
bool ok;
|
||||||
|
|
||||||
aiVector3D Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
|
aiMatrix4x4 chain[TransformationComp_MAXIMUM];
|
||||||
if(!ok) {
|
std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
|
||||||
Translation = aiVector3D(0.0f,0.0f,0.0f);
|
|
||||||
|
// 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);
|
const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
|
||||||
if(!ok) {
|
if(ok && PostRotation.SquareLength() > zero_epsilon) {
|
||||||
Scaling = aiVector3D(1.0f,1.0f,1.0f);
|
is_complex = true;
|
||||||
|
|
||||||
|
GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX euler angles, radians, xyz order?
|
const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
|
||||||
aiVector3D Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
|
if(ok && RotationPivot.SquareLength() > zero_epsilon) {
|
||||||
if(!ok) {
|
is_complex = true;
|
||||||
Rotation = aiVector3D(0.0f,0.0f,0.0f);
|
|
||||||
|
aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
|
||||||
|
aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
|
||||||
}
|
}
|
||||||
|
|
||||||
aiMatrix4x4 temp;
|
const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
|
||||||
nd.mTransformation = aiMatrix4x4::Scaling(Scaling,temp);
|
if(ok && RotationOffset.SquareLength() > zero_epsilon) {
|
||||||
if(fabs(Rotation.x) > 1e-6f) {
|
is_complex = true;
|
||||||
nd.mTransformation *= aiMatrix4x4::RotationX(Rotation.x,temp);
|
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ struct ImportSettings
|
||||||
, readAnimations(true)
|
, readAnimations(true)
|
||||||
, strictMode(true)
|
, strictMode(true)
|
||||||
, readWeights(true)
|
, readWeights(true)
|
||||||
|
, preservePivots(true)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,6 +104,28 @@ struct ImportSettings
|
||||||
/** read bones (vertex weights and deform info).
|
/** read bones (vertex weights and deform info).
|
||||||
* Default value is true. */
|
* Default value is true. */
|
||||||
bool readWeights;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue