- fbx: work on converting animations for complex transformation chains (i.e. chains that contain pivots and offsets). (WIP)

pull/14/head
Alexander Gessler 2012-07-27 04:55:56 +02:00
parent 7f082e0aae
commit 94e1e78c55
1 changed files with 184 additions and 53 deletions

View File

@ -189,7 +189,7 @@ private:
}
}
/** the different parts that make up the final local transformation of a fbx node */
enum TransformationComp
{
TransformationComp_Translation = 0,
@ -209,6 +209,8 @@ private:
// ------------------------------------------------------------------------------------------------
// this returns unified names usable within assimp identifiers (i.e. no space characters -
// while these would be allowed, they are a potential trouble spot so better not use them).
const char* NameTransformationComp(TransformationComp comp)
{
switch(comp)
@ -238,6 +240,42 @@ private:
}
ai_assert(false);
return NULL;
}
// ------------------------------------------------------------------------------------------------
// note: this returns the REAL fbx property names
const char* NameTransformationCompProperty(TransformationComp comp)
{
switch(comp)
{
case TransformationComp_Translation:
return "Lcl Translation";
case TransformationComp_RotationOffset:
return "RotationOffset";
case TransformationComp_RotationPivot:
return "RotationPivot";
case TransformationComp_PreRotation:
return "PreRotation";
case TransformationComp_Rotation:
return "Lcl 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 "Lcl Scaling";
case TransformationComp_ScalingPivotInverse:
return "ScalingPivotInverse";
}
ai_assert(false);
return NULL;
}
@ -276,6 +314,31 @@ private:
}
// ------------------------------------------------------------------------------------------------
/** checks if a node has more than just scaling, rotation and translation components */
bool NeedsComplexTransformationChain(const Model& model)
{
const PropertyTable& props = model.Props();
bool ok;
const float zero_epsilon = 1e-6f;
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
const TransformationComp comp = static_cast<TransformationComp>(i);
if(comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation) {
continue;
}
const aiVector3D& v = PropertyGet<aiVector3D>(props,NameTransformationCompProperty(comp),ok);
if(ok && v.SquareLength() > zero_epsilon) {
return true;
}
}
return false;
}
// ------------------------------------------------------------------------------------------------
/** note: memory for output_nodes will be managed by the caller */
void GenerateTransformationNodeChain(const Model& model,
@ -354,6 +417,12 @@ private:
GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
}
// is_complex needs to be consistent with NeedsComplexTransformationChain()
// or the interplay between this code and the animation converter would
// not be guaranteed.
ai_assert(NeedsComplexTransformationChain(model) == is_complex);
const std::string& name = FixNodeName(model.Name());
// now, if we have more than just Translation, Scaling and Rotation,
@ -1266,6 +1335,9 @@ private:
typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
// XXX: better use multi_map ..
typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
// ------------------------------------------------------------------------------------------------
void ConvertAnimationStack(const AnimationStack& st)
@ -1288,8 +1360,6 @@ private:
// need to find all nodes for which we need to generate node animations -
// it may happen that we need to merge multiple layers, though.
// XXX: better use multi_map ..
typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
NodeMap node_map;
// reverse mapping from curves to layers, much faster than querying
@ -1350,68 +1420,57 @@ private:
ai_assert(curve_node);
const NodeMap::const_iterator itScale = node_property_map.find("Lcl Scaling");
const NodeMap::const_iterator itRotation = node_property_map.find("Lcl Rotation");
const NodeMap::const_iterator itTranslation = node_property_map.find("Lcl Translation");
// check for all possible transformation components
NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
const bool hasScale = itScale != node_property_map.end();
const bool hasRotation = itRotation != node_property_map.end();
const bool hasTranslation = itTranslation != node_property_map.end();
bool has_any = false;
bool has_complex = false;
if (!hasScale && !hasRotation && !hasTranslation) {
FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames");
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
const TransformationComp comp = static_cast<TransformationComp>(i);
chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
if (chain[i] != node_property_map.end()) {
has_any = true;
if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
comp != TransformationComp_Translation) {
has_complex = true;
}
}
}
if (!has_any) {
FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
continue;
}
aiNodeAnim* const na = new aiNodeAnim();
node_anims.push_back(na);
na->mNodeName.Set(kv.first);
ai_assert(curve_node->TargetAsModel());
const PropertyTable& props = curve_node->TargetAsModel()->Props();
// if a particular transformation is not given, grab it from
// the corresponding node to meet the semantics of aiNodeAnim,
// which requires all of rotation, scaling and translation
// to be set.
if(hasScale) {
ConvertScaleKeys(na, (*itScale).second, layer_map,
max_time, min_time);
}
else {
na->mScalingKeys = new aiVectorKey[1];
na->mNumScalingKeys = 1;
// this needs to play nicely with GenerateTransformationNodeChain() which will
// be invoked _later_ (animations come first). If this node has only rotation,
// scaling and translation _and_ there are no animated other components either,
// we can use a single node and also a single node animation channel.
const Model& target = *curve_node->TargetAsModel();
if (!has_complex && !NeedsComplexTransformationChain(target)) {
na->mScalingKeys[0].mTime = 0.;
na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",aiVector3D(1.f,1.f,1.f));
}
if(hasRotation) {
ConvertRotationKeys(na, (*itRotation).second, layer_map,
max_time, min_time);
}
else {
na->mRotationKeys = new aiQuatKey[1];
na->mNumRotationKeys = 1;
na->mRotationKeys[0].mTime = 0.;
na->mRotationKeys[0].mValue = EulerToQuaternion(
PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f))
aiNodeAnim* const nd = GenerateSimpleNodeAnim(kv.first, target, chain,
node_property_map.end(),
layer_map,
max_time,
min_time
);
ai_assert(nd);
node_anims.push_back(nd);
continue;
}
if(hasTranslation) {
ConvertTranslationKeys(na, (*itTranslation).second, layer_map,
max_time, min_time);
}
else {
na->mPositionKeys = new aiVectorKey[1];
na->mNumPositionKeys = 1;
// otherwise, things get gruesome.
aiNodeAnim* const na = new aiNodeAnim();
na->mPositionKeys[0].mTime = 0.;
na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",aiVector3D(0.f,0.f,0.f));
}
// XXX todo
ai_assert(false);
}
}
catch(std::exception&) {
@ -1439,6 +1498,78 @@ private:
anim->mTicksPerSecond = 1000.0;
}
// ------------------------------------------------------------------------------------------------
// generate node anim, extracting only Rotation, Scaling and Translation from the given chain
aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name,
const Model& target,
NodeMap::const_iterator chain[TransformationComp_MAXIMUM],
NodeMap::const_iterator iter_end,
const LayerMap& layer_map,
double& max_time,
double& min_time)
{
ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
na->mNodeName.Set(name);
const PropertyTable& props = target.Props();
// if a particular transformation is not given, grab it from
// the corresponding node to meet the semantics of aiNodeAnim,
// which requires all of rotation, scaling and translation
// to be set.
if(chain[TransformationComp_Scaling] != iter_end) {
ConvertScaleKeys(na, (*chain[TransformationComp_Scaling]).second,
layer_map,
max_time,
min_time);
}
else {
na->mScalingKeys = new aiVectorKey[1];
na->mNumScalingKeys = 1;
na->mScalingKeys[0].mTime = 0.;
na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",
aiVector3D(1.f,1.f,1.f));
}
if(chain[TransformationComp_Rotation] != iter_end) {
ConvertRotationKeys(na, (*chain[TransformationComp_Rotation]).second,
layer_map,
max_time,
min_time);
}
else {
na->mRotationKeys = new aiQuatKey[1];
na->mNumRotationKeys = 1;
na->mRotationKeys[0].mTime = 0.;
na->mRotationKeys[0].mValue = EulerToQuaternion(
PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f))
);
}
if(chain[TransformationComp_Translation] != iter_end) {
ConvertTranslationKeys(na, (*chain[TransformationComp_Translation]).second,
layer_map,
max_time,
min_time);
}
else {
na->mPositionKeys = new aiVectorKey[1];
na->mNumPositionKeys = 1;
na->mPositionKeys[0].mTime = 0.;
na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",
aiVector3D(0.f,0.f,0.f));
}
return na.dismiss();
}
// key (time), value, mapto (component index)
typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
typedef std::vector<KeyFrameList> KeyFrameListList;