- fbx: work on converting animations for complex transformation chains (i.e. chains that contain pivots and offsets). (WIP)
parent
7f082e0aae
commit
94e1e78c55
|
@ -189,7 +189,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** the different parts that make up the final local transformation of a fbx node */
|
||||||
enum TransformationComp
|
enum TransformationComp
|
||||||
{
|
{
|
||||||
TransformationComp_Translation = 0,
|
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)
|
const char* NameTransformationComp(TransformationComp comp)
|
||||||
{
|
{
|
||||||
switch(comp)
|
switch(comp)
|
||||||
|
@ -238,6 +240,42 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
ai_assert(false);
|
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 */
|
/** note: memory for output_nodes will be managed by the caller */
|
||||||
void GenerateTransformationNodeChain(const Model& model,
|
void GenerateTransformationNodeChain(const Model& model,
|
||||||
|
@ -354,6 +417,12 @@ private:
|
||||||
GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
|
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());
|
const std::string& name = FixNodeName(model.Name());
|
||||||
|
|
||||||
// now, if we have more than just Translation, Scaling and Rotation,
|
// now, if we have more than just Translation, Scaling and Rotation,
|
||||||
|
@ -1266,6 +1335,9 @@ private:
|
||||||
|
|
||||||
typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
|
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)
|
void ConvertAnimationStack(const AnimationStack& st)
|
||||||
|
@ -1288,8 +1360,6 @@ private:
|
||||||
|
|
||||||
// need to find all nodes for which we need to generate node animations -
|
// need to find all nodes for which we need to generate node animations -
|
||||||
// it may happen that we need to merge multiple layers, though.
|
// 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;
|
NodeMap node_map;
|
||||||
|
|
||||||
// reverse mapping from curves to layers, much faster than querying
|
// reverse mapping from curves to layers, much faster than querying
|
||||||
|
@ -1350,68 +1420,57 @@ private:
|
||||||
|
|
||||||
ai_assert(curve_node);
|
ai_assert(curve_node);
|
||||||
|
|
||||||
const NodeMap::const_iterator itScale = node_property_map.find("Lcl Scaling");
|
// check for all possible transformation components
|
||||||
const NodeMap::const_iterator itRotation = node_property_map.find("Lcl Rotation");
|
NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
|
||||||
const NodeMap::const_iterator itTranslation = node_property_map.find("Lcl Translation");
|
|
||||||
|
|
||||||
const bool hasScale = itScale != node_property_map.end();
|
bool has_any = false;
|
||||||
const bool hasRotation = itRotation != node_property_map.end();
|
bool has_complex = false;
|
||||||
const bool hasTranslation = itTranslation != node_property_map.end();
|
|
||||||
|
|
||||||
if (!hasScale && !hasRotation && !hasTranslation) {
|
for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
|
||||||
FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames");
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
aiNodeAnim* const na = new aiNodeAnim();
|
|
||||||
node_anims.push_back(na);
|
|
||||||
|
|
||||||
na->mNodeName.Set(kv.first);
|
|
||||||
|
|
||||||
ai_assert(curve_node->TargetAsModel());
|
ai_assert(curve_node->TargetAsModel());
|
||||||
const PropertyTable& props = curve_node->TargetAsModel()->Props();
|
|
||||||
|
|
||||||
// if a particular transformation is not given, grab it from
|
// this needs to play nicely with GenerateTransformationNodeChain() which will
|
||||||
// the corresponding node to meet the semantics of aiNodeAnim,
|
// be invoked _later_ (animations come first). If this node has only rotation,
|
||||||
// which requires all of rotation, scaling and translation
|
// scaling and translation _and_ there are no animated other components either,
|
||||||
// to be set.
|
// we can use a single node and also a single node animation channel.
|
||||||
if(hasScale) {
|
const Model& target = *curve_node->TargetAsModel();
|
||||||
ConvertScaleKeys(na, (*itScale).second, layer_map,
|
if (!has_complex && !NeedsComplexTransformationChain(target)) {
|
||||||
max_time, min_time);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
na->mScalingKeys = new aiVectorKey[1];
|
|
||||||
na->mNumScalingKeys = 1;
|
|
||||||
|
|
||||||
na->mScalingKeys[0].mTime = 0.;
|
aiNodeAnim* const nd = GenerateSimpleNodeAnim(kv.first, target, chain,
|
||||||
na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",aiVector3D(1.f,1.f,1.f));
|
node_property_map.end(),
|
||||||
}
|
layer_map,
|
||||||
|
max_time,
|
||||||
if(hasRotation) {
|
min_time
|
||||||
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))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ai_assert(nd);
|
||||||
|
node_anims.push_back(nd);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasTranslation) {
|
// otherwise, things get gruesome.
|
||||||
ConvertTranslationKeys(na, (*itTranslation).second, layer_map,
|
aiNodeAnim* const na = new aiNodeAnim();
|
||||||
max_time, min_time);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
na->mPositionKeys = new aiVectorKey[1];
|
|
||||||
na->mNumPositionKeys = 1;
|
|
||||||
|
|
||||||
na->mPositionKeys[0].mTime = 0.;
|
// XXX todo
|
||||||
na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",aiVector3D(0.f,0.f,0.f));
|
ai_assert(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception&) {
|
catch(std::exception&) {
|
||||||
|
@ -1439,6 +1498,78 @@ private:
|
||||||
anim->mTicksPerSecond = 1000.0;
|
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)
|
// key (time), value, mapto (component index)
|
||||||
typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
|
typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
|
||||||
typedef std::vector<KeyFrameList> KeyFrameListList;
|
typedef std::vector<KeyFrameList> KeyFrameListList;
|
||||||
|
|
Loading…
Reference in New Issue