- fbx: bugfix animation interpolation code, fix various quirks.
parent
09aaaba7b8
commit
e4305149be
|
@ -81,7 +81,7 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::s
|
||||||
|
|
||||||
const Element* KeyAttrFlags = sc["KeyAttrFlags"];
|
const Element* KeyAttrFlags = sc["KeyAttrFlags"];
|
||||||
if(KeyAttrFlags) {
|
if(KeyAttrFlags) {
|
||||||
flags = ParseTokenAsInt(GetRequiredToken(*KeyAttrFlags,0));
|
ReadVectorDataArray(flags, *KeyAttrFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,9 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, cons
|
||||||
: Object(id, element, name)
|
: Object(id, element, name)
|
||||||
, target()
|
, target()
|
||||||
{
|
{
|
||||||
|
const Scope& sc = GetRequiredScope(element);
|
||||||
|
props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc);
|
||||||
|
|
||||||
{
|
{
|
||||||
// resolve attached animation curves
|
// resolve attached animation curves
|
||||||
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
|
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
|
||||||
|
|
|
@ -950,6 +950,11 @@ private:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node->Curves().empty()) {
|
||||||
|
FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
node_property_map[node->TargetProperty()].push_back(node);
|
node_property_map[node->TargetProperty()].push_back(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -957,9 +962,9 @@ private:
|
||||||
const NodeMap::const_iterator itRotation = node_property_map.find("Lcl Rotation");
|
const NodeMap::const_iterator itRotation = node_property_map.find("Lcl Rotation");
|
||||||
const NodeMap::const_iterator itTranslation = node_property_map.find("Lcl Translation");
|
const NodeMap::const_iterator itTranslation = node_property_map.find("Lcl Translation");
|
||||||
|
|
||||||
const bool hasScale = !!(*itScale).second.size();
|
const bool hasScale = itScale != node_property_map.end();
|
||||||
const bool hasRotation = !!(*itRotation).second.size();
|
const bool hasRotation = itRotation != node_property_map.end();
|
||||||
const bool hasTranslation = !!(*itTranslation).second.size();
|
const bool hasTranslation = itTranslation != node_property_map.end();
|
||||||
|
|
||||||
if (!hasScale && !hasRotation && !hasTranslation) {
|
if (!hasScale && !hasRotation && !hasTranslation) {
|
||||||
FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames");
|
FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames");
|
||||||
|
@ -996,10 +1001,10 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
// key (time), value, mapto (component index)
|
// key (time), value, mapto (component index)
|
||||||
typedef boost::tuple< std::vector<float>*, std::vector<float>*, unsigned int > KeyFrameList;
|
typedef boost::tuple< KeyTimeList*, KeyValueList*, unsigned int > KeyFrameList;
|
||||||
typedef std::vector<KeyFrameList> KeyFrameListList;
|
typedef std::vector<KeyFrameList> KeyFrameListList;
|
||||||
|
|
||||||
typedef std::vector<float> KeyTimeList;
|
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
@ -1040,14 +1045,14 @@ private:
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
std::vector<float> GetKeyTimeList(const KeyFrameListList& inputs)
|
KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs)
|
||||||
{
|
{
|
||||||
ai_assert(inputs.size());
|
ai_assert(inputs.size());
|
||||||
|
|
||||||
// reserve some space upfront - it is likely that the keyframe lists
|
// reserve some space upfront - it is likely that the keyframe lists
|
||||||
// have matching time values, so max(of all keyframe lists) should
|
// have matching time values, so max(of all keyframe lists) should
|
||||||
// be a good estimate.
|
// be a good estimate.
|
||||||
std::vector<float> keys;
|
KeyTimeList keys;
|
||||||
|
|
||||||
size_t estimate = 0;
|
size_t estimate = 0;
|
||||||
BOOST_FOREACH(const KeyFrameList& kfl, inputs) {
|
BOOST_FOREACH(const KeyFrameList& kfl, inputs) {
|
||||||
|
@ -1062,7 +1067,7 @@ private:
|
||||||
const size_t count = inputs.size();
|
const size_t count = inputs.size();
|
||||||
while(true) {
|
while(true) {
|
||||||
|
|
||||||
float min_tick = 1e10f;
|
uint64_t min_tick = std::numeric_limits<uint64_t>::max();
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
const KeyFrameList& kfl = inputs[i];
|
const KeyFrameList& kfl = inputs[i];
|
||||||
|
|
||||||
|
@ -1071,7 +1076,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (min_tick > 1e9f) {
|
if (min_tick == std::numeric_limits<uint64_t>::max()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
keys.push_back(min_tick);
|
keys.push_back(min_tick);
|
||||||
|
@ -1079,8 +1084,8 @@ private:
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
const KeyFrameList& kfl = inputs[i];
|
const KeyFrameList& kfl = inputs[i];
|
||||||
|
|
||||||
const float time_epsilon = 1e-4f;
|
|
||||||
while(kfl.get<0>()->size() > next_pos[i] && fabs(kfl.get<0>()->at(next_pos[i]) - min_tick) < time_epsilon) {
|
while(kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == min_tick) {
|
||||||
++next_pos[i];
|
++next_pos[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1101,7 +1106,7 @@ private:
|
||||||
|
|
||||||
next_pos.resize(inputs.size(),0);
|
next_pos.resize(inputs.size(),0);
|
||||||
|
|
||||||
BOOST_FOREACH(float time, keys) {
|
BOOST_FOREACH(KeyTimeList::value_type time, keys) {
|
||||||
float result[3] = {0.0f, 0.0f, 0.0f};
|
float result[3] = {0.0f, 0.0f, 0.0f};
|
||||||
if(geom) {
|
if(geom) {
|
||||||
result[0] = result[1] = result[2] = 1.0f;
|
result[0] = result[1] = result[2] = 1.0f;
|
||||||
|
@ -1110,20 +1115,25 @@ private:
|
||||||
for (size_t i = 0; i < count; ++i) {
|
for (size_t i = 0; i < count; ++i) {
|
||||||
const KeyFrameList& kfl = inputs[i];
|
const KeyFrameList& kfl = inputs[i];
|
||||||
|
|
||||||
const float time_epsilon = 1e-4f;
|
const size_t ksize = kfl.get<0>()->size();
|
||||||
if (kfl.get<0>()->size() > next_pos[i] && fabs(kfl.get<0>()->at(next_pos[i]) - time) < time_epsilon) {
|
if (ksize > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == time) {
|
||||||
++next_pos[i];
|
++next_pos[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const size_t id0 = next_pos[i]>0 ? next_pos[i]-1 : 0;
|
||||||
|
const size_t id1 = next_pos[i]==ksize ? ksize-1 : next_pos[i];
|
||||||
|
|
||||||
// use lerp for interpolation
|
// use lerp for interpolation
|
||||||
const float valueA = kfl.get<1>()->at(next_pos[i]>0 ? next_pos[i]-1 : 0);
|
const KeyValueList::value_type valueA = kfl.get<1>()->at(id0);
|
||||||
const float valueB = kfl.get<1>()->at(next_pos[i]);
|
const KeyValueList::value_type valueB = kfl.get<1>()->at(id1);
|
||||||
|
|
||||||
const float timeA = kfl.get<0>()->at(next_pos[i]>0 ? next_pos[i]-1 : 0);
|
const KeyTimeList::value_type timeA = kfl.get<0>()->at(id0);
|
||||||
const float timeB = kfl.get<0>()->at(next_pos[i]);
|
const KeyTimeList::value_type timeB = kfl.get<0>()->at(id1);
|
||||||
|
|
||||||
const float factor = (time - timeA) / (timeB - timeA);
|
// do the actual interpolation in double-precision arithmetics
|
||||||
const float interpValue = valueA + (valueB - valueA) * factor;
|
// because it is a bit sensitive to rounding errors.
|
||||||
|
const double factor = timeB == timeA ? 0. : static_cast<double>((time - timeA) / (timeB - timeA));
|
||||||
|
const float interpValue = static_cast<float>(valueA + (valueB - valueA) * factor);
|
||||||
|
|
||||||
if(geom) {
|
if(geom) {
|
||||||
result[kfl.get<2>()] *= interpValue;
|
result[kfl.get<2>()] *= interpValue;
|
||||||
|
@ -1133,7 +1143,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valOut->mTime = time;
|
valOut->mTime = static_cast<double>(time);
|
||||||
valOut->mValue.x = result[0];
|
valOut->mValue.x = result[0];
|
||||||
valOut->mValue.y = result[1];
|
valOut->mValue.y = result[1];
|
||||||
valOut->mValue.z = result[2];
|
valOut->mValue.z = result[2];
|
||||||
|
@ -1213,7 +1223,7 @@ private:
|
||||||
|
|
||||||
// XXX see notes in ConvertScaleKeys()
|
// XXX see notes in ConvertScaleKeys()
|
||||||
const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes);
|
const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes);
|
||||||
const std::vector<float>& keys = GetKeyTimeList(inputs);
|
const KeyTimeList& keys = GetKeyTimeList(inputs);
|
||||||
|
|
||||||
na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
|
na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
|
||||||
na->mRotationKeys = new aiQuatKey[keys.size()];
|
na->mRotationKeys = new aiQuatKey[keys.size()];
|
||||||
|
|
|
@ -199,6 +199,8 @@ const Element& GetRequiredElement(const Scope& sc, const std::string& index, con
|
||||||
return *el;
|
return *el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: tacke code duplication in the various ReadVectorDataArray() overloads below.
|
||||||
|
// could use a type traits based solution.
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// read an array of float3 tuples
|
// read an array of float3 tuples
|
||||||
|
@ -352,6 +354,28 @@ void ReadVectorDataArray(std::vector<unsigned int>& out, const Element& el)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// read an array of uint64_ts
|
||||||
|
void ReadVectorDataArray(std::vector<uint64_t>& out, const Element& el)
|
||||||
|
{
|
||||||
|
out.clear();
|
||||||
|
const TokenList& tok = el.Tokens();
|
||||||
|
const size_t dim = ParseTokenAsDim(*tok[0]);
|
||||||
|
|
||||||
|
// see notes in ReadVectorDataArray()
|
||||||
|
out.reserve(dim);
|
||||||
|
|
||||||
|
const Scope& scope = GetRequiredScope(el);
|
||||||
|
const Element& a = GetRequiredElement(scope,"a",&el);
|
||||||
|
|
||||||
|
for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
||||||
|
const uint64_t ival = ParseTokenAsID(**it++);
|
||||||
|
|
||||||
|
out.push_back(ival);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// fetch a property table and the corresponding property template
|
// fetch a property table and the corresponding property template
|
||||||
boost::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
|
boost::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
|
||||||
|
@ -459,12 +483,12 @@ const Object* LazyObject::Get(bool dieOnError)
|
||||||
object.reset(new AnimationLayer(id,element,name,doc));
|
object.reset(new AnimationLayer(id,element,name,doc));
|
||||||
}
|
}
|
||||||
// note: order matters for these two
|
// note: order matters for these two
|
||||||
else if (!strncmp(obtype,"AnimationCurveNode",length)) {
|
|
||||||
object.reset(new AnimationCurveNode(id,element,name,doc));
|
|
||||||
}
|
|
||||||
else if (!strncmp(obtype,"AnimationCurve",length)) {
|
else if (!strncmp(obtype,"AnimationCurve",length)) {
|
||||||
object.reset(new AnimationCurve(id,element,name,doc));
|
object.reset(new AnimationCurve(id,element,name,doc));
|
||||||
}
|
}
|
||||||
|
else if (!strncmp(obtype,"AnimationCurveNode",length)) {
|
||||||
|
object.reset(new AnimationCurveNode(id,element,name,doc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(std::exception& ex) {
|
catch(std::exception& ex) {
|
||||||
flags &= ~BEING_CONSTRUCTED;
|
flags &= ~BEING_CONSTRUCTED;
|
||||||
|
@ -801,7 +825,7 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
|
||||||
|
|
||||||
for (size_t i = 0; i < c; ++i) {
|
for (size_t i = 0; i < c; ++i) {
|
||||||
ai_assert(classnames[i]);
|
ai_assert(classnames[i]);
|
||||||
if(!strncmp(classnames[i],obtype,lenghts[i])) {
|
if(std::distance(key.begin(),key.end()) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
|
||||||
obtype = NULL;
|
obtype = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -426,7 +426,8 @@ private:
|
||||||
std::vector<unsigned int> mappings;
|
std::vector<unsigned int> mappings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::vector<uint64_t> KeyTimeList;
|
||||||
|
typedef std::vector<float> KeyValueList;
|
||||||
|
|
||||||
/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
|
/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
|
||||||
class AnimationCurve : public Object
|
class AnimationCurve : public Object
|
||||||
|
@ -440,14 +441,14 @@ public:
|
||||||
|
|
||||||
/** get list of keyframe positions (time).
|
/** get list of keyframe positions (time).
|
||||||
* Invariant: |GetKeys()| > 0 */
|
* Invariant: |GetKeys()| > 0 */
|
||||||
const std::vector<float>& GetKeys() const {
|
const KeyTimeList& GetKeys() const {
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** get list of keyframe values.
|
/** get list of keyframe values.
|
||||||
* Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
|
* Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
|
||||||
const std::vector<float>& GetValues() const {
|
const KeyValueList& GetValues() const {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,16 +457,16 @@ public:
|
||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GetFlags() const {
|
const std::vector<unsigned int>& GetFlags() const {
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::vector<float> keys;
|
KeyTimeList keys;
|
||||||
std::vector<float> values;
|
KeyValueList values;
|
||||||
std::vector<float> attributes;
|
std::vector<float> attributes;
|
||||||
unsigned int flags;
|
std::vector<unsigned int> flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
// property-name -> animation curve
|
// property-name -> animation curve
|
||||||
|
|
|
@ -94,6 +94,9 @@ void ReadVectorDataArray(std::vector<float>& out, const Element& el);
|
||||||
// read an array of uints
|
// read an array of uints
|
||||||
void ReadVectorDataArray(std::vector<unsigned int>& out, const Element& el);
|
void ReadVectorDataArray(std::vector<unsigned int>& out, const Element& el);
|
||||||
|
|
||||||
|
// read an array of uint64_t's
|
||||||
|
void ReadVectorDataArray(std::vector<uint64_t>& out, const Element& el);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// fetch a property table and the corresponding property template
|
// fetch a property table and the corresponding property template
|
||||||
|
|
Loading…
Reference in New Issue