diff --git a/code/FBXAnimation.cpp b/code/FBXAnimation.cpp index bf6cddfbd..6497279e4 100644 --- a/code/FBXAnimation.cpp +++ b/code/FBXAnimation.cpp @@ -81,7 +81,7 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::s const Element* KeyAttrFlags = sc["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) , target() { + const Scope& sc = GetRequiredScope(element); + props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc); + { // resolve attached animation curves const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve"); diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 45492289f..f6cc78c2f 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -950,6 +950,11 @@ private: continue; } + if (node->Curves().empty()) { + FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode"); + continue; + } + 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 itTranslation = node_property_map.find("Lcl Translation"); - const bool hasScale = !!(*itScale).second.size(); - const bool hasRotation = !!(*itRotation).second.size(); - const bool hasTranslation = !!(*itTranslation).second.size(); + const bool hasScale = itScale != node_property_map.end(); + const bool hasRotation = itRotation != node_property_map.end(); + const bool hasTranslation = itTranslation != node_property_map.end(); if (!hasScale && !hasRotation && !hasTranslation) { FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames"); @@ -996,10 +1001,10 @@ private: } // key (time), value, mapto (component index) - typedef boost::tuple< std::vector*, std::vector*, unsigned int > KeyFrameList; + typedef boost::tuple< KeyTimeList*, KeyValueList*, unsigned int > KeyFrameList; typedef std::vector KeyFrameListList; - typedef std::vector KeyTimeList; + // ------------------------------------------------------------------------------------------------ @@ -1040,14 +1045,14 @@ private: // ------------------------------------------------------------------------------------------------ - std::vector GetKeyTimeList(const KeyFrameListList& inputs) + KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs) { ai_assert(inputs.size()); // reserve some space upfront - it is likely that the keyframe lists // have matching time values, so max(of all keyframe lists) should // be a good estimate. - std::vector keys; + KeyTimeList keys; size_t estimate = 0; BOOST_FOREACH(const KeyFrameList& kfl, inputs) { @@ -1062,7 +1067,7 @@ private: const size_t count = inputs.size(); while(true) { - float min_tick = 1e10f; + uint64_t min_tick = std::numeric_limits::max(); for (size_t i = 0; i < count; ++i) { const KeyFrameList& kfl = inputs[i]; @@ -1071,7 +1076,7 @@ private: } } - if (min_tick > 1e9f) { + if (min_tick == std::numeric_limits::max()) { break; } keys.push_back(min_tick); @@ -1079,8 +1084,8 @@ private: for (size_t i = 0; i < count; ++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]; } } @@ -1101,7 +1106,7 @@ private: 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}; if(geom) { result[0] = result[1] = result[2] = 1.0f; @@ -1110,20 +1115,25 @@ private: for (size_t i = 0; i < count; ++i) { const KeyFrameList& kfl = inputs[i]; - const float time_epsilon = 1e-4f; - if (kfl.get<0>()->size() > next_pos[i] && fabs(kfl.get<0>()->at(next_pos[i]) - time) < time_epsilon) { + const size_t ksize = kfl.get<0>()->size(); + if (ksize > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == time) { ++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 - const float valueA = kfl.get<1>()->at(next_pos[i]>0 ? next_pos[i]-1 : 0); - const float valueB = kfl.get<1>()->at(next_pos[i]); + const KeyValueList::value_type valueA = kfl.get<1>()->at(id0); + 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 float timeB = kfl.get<0>()->at(next_pos[i]); + const KeyTimeList::value_type timeA = kfl.get<0>()->at(id0); + const KeyTimeList::value_type timeB = kfl.get<0>()->at(id1); - const float factor = (time - timeA) / (timeB - timeA); - const float interpValue = valueA + (valueB - valueA) * factor; + // do the actual interpolation in double-precision arithmetics + // because it is a bit sensitive to rounding errors. + const double factor = timeB == timeA ? 0. : static_cast((time - timeA) / (timeB - timeA)); + const float interpValue = static_cast(valueA + (valueB - valueA) * factor); if(geom) { result[kfl.get<2>()] *= interpValue; @@ -1133,7 +1143,7 @@ private: } } - valOut->mTime = time; + valOut->mTime = static_cast(time); valOut->mValue.x = result[0]; valOut->mValue.y = result[1]; valOut->mValue.z = result[2]; @@ -1213,7 +1223,7 @@ private: // XXX see notes in ConvertScaleKeys() const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes); - const std::vector& keys = GetKeyTimeList(inputs); + const KeyTimeList& keys = GetKeyTimeList(inputs); na->mNumRotationKeys = static_cast(keys.size()); na->mRotationKeys = new aiQuatKey[keys.size()]; diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 321366a15..d6375a9c4 100644 --- a/code/FBXDocument.cpp +++ b/code/FBXDocument.cpp @@ -199,6 +199,8 @@ const Element& GetRequiredElement(const Scope& sc, const std::string& index, con 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 @@ -352,6 +354,28 @@ void ReadVectorDataArray(std::vector& out, const Element& el) } +// ------------------------------------------------------------------------------------------------ +// read an array of uint64_ts +void ReadVectorDataArray(std::vector& 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 boost::shared_ptr GetPropertyTable(const Document& doc, @@ -459,12 +483,12 @@ const Object* LazyObject::Get(bool dieOnError) object.reset(new AnimationLayer(id,element,name,doc)); } // 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)) { 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) { flags &= ~BEING_CONSTRUCTED; @@ -801,7 +825,7 @@ std::vector Document::GetConnectionsSequenced(uint64_t id, bo for (size_t i = 0; i < c; ++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; break; } diff --git a/code/FBXDocument.h b/code/FBXDocument.h index bb074a919..d69544e2e 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -426,7 +426,8 @@ private: std::vector mappings; }; - +typedef std::vector KeyTimeList; +typedef std::vector KeyValueList; /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */ class AnimationCurve : public Object @@ -440,14 +441,14 @@ public: /** get list of keyframe positions (time). * Invariant: |GetKeys()| > 0 */ - const std::vector& GetKeys() const { + const KeyTimeList& GetKeys() const { return keys; } /** get list of keyframe values. * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/ - const std::vector& GetValues() const { + const KeyValueList& GetValues() const { return values; } @@ -456,16 +457,16 @@ public: return attributes; } - unsigned int GetFlags() const { + const std::vector& GetFlags() const { return flags; } private: - std::vector keys; - std::vector values; + KeyTimeList keys; + KeyValueList values; std::vector attributes; - unsigned int flags; + std::vector flags; }; // property-name -> animation curve diff --git a/code/FBXDocumentUtil.h b/code/FBXDocumentUtil.h index 4e0975e0a..0e25e5629 100644 --- a/code/FBXDocumentUtil.h +++ b/code/FBXDocumentUtil.h @@ -94,6 +94,9 @@ void ReadVectorDataArray(std::vector& out, const Element& el); // read an array of uints void ReadVectorDataArray(std::vector& out, const Element& el); +// read an array of uint64_t's +void ReadVectorDataArray(std::vector& out, const Element& el); + // fetch a property table and the corresponding property template