Browse Source

- fbx: add option to detect dummy animation tracks and to remove them. Actually, this is mostly a debugging aid (and it cuts down memory usage).

Alexander Gessler 13 years ago
parent
commit
54be52ea28
2 changed files with 89 additions and 8 deletions
  1. 83 8
      code/FBXConverter.cpp
  2. 6 0
      code/FBXImportSettings.h

+ 83 - 8
code/FBXConverter.cpp

@@ -282,6 +282,15 @@ private:
 	}
 	}
 
 
 
 
+	// ------------------------------------------------------------------------------------------------
+	aiVector3D TransformationCompDefaultValue(TransformationComp comp)
+	{
+		// XXX a neat way to solve the never-ending special cases for scaling 
+		// would be to do everything in log space!
+		return comp == TransformationComp_Scaling ? aiVector3D(1.f,1.f,1.f) : aiVector3D();
+	}
+
+
 	enum RotationMode
 	enum RotationMode
 	{
 	{
 		RotationMode_Euler_XYZ
 		RotationMode_Euler_XYZ
@@ -1316,10 +1325,6 @@ private:
 	}
 	}
 
 
 
 
-	// name -> prefix_stripped?
-	typedef std::map<std::string, bool> NodeNameMap;
-	NodeNameMap node_names;
-
 	// ------------------------------------------------------------------------------------------------
 	// ------------------------------------------------------------------------------------------------
 	std::string FixNodeName(const std::string& name)
 	std::string FixNodeName(const std::string& name)
 	{
 	{
@@ -1458,6 +1463,17 @@ private:
 		NodeMap node_property_map;
 		NodeMap node_property_map;
 		ai_assert(curves.size());
 		ai_assert(curves.size());
 
 
+		// sanity check whether the input is ok
+#ifdef _DEBUG
+		{ const Object* target = NULL;
+		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
+			if(!target) {
+				target = node->Target();
+			}
+			ai_assert(node->Target() == target);
+		}}
+#endif
+
 		const AnimationCurveNode* curve_node;
 		const AnimationCurveNode* curve_node;
 		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
 		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
 			ai_assert(node);
 			ai_assert(node);
@@ -1477,6 +1493,9 @@ private:
 		}
 		}
 
 
 		ai_assert(curve_node);
 		ai_assert(curve_node);
+		ai_assert(curve_node->TargetAsModel());
+
+		const Model& target = *curve_node->TargetAsModel();
 
 
 		// check for all possible transformation components
 		// check for all possible transformation components
 		NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
 		NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
@@ -1495,6 +1514,14 @@ private:
 
 
 			chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
 			chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
 			if (chain[i] != node_property_map.end()) {
 			if (chain[i] != node_property_map.end()) {
+
+				// check if this curves contains redundant information by looking
+				// up the corresponding node's transformation chain.
+				if (doc.Settings().optimizeEmptyAnimationCurves && IsRedundantAnimationData(target, comp, (*chain[i]).second)) {
+					FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name());
+					continue;
+				}
+
 				has_any = true;
 				has_any = true;
 
 
 				if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
 				if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
@@ -1510,13 +1537,10 @@ private:
 			return;
 			return;
 		}
 		}
 
 
-		ai_assert(curve_node->TargetAsModel());
-
 		// this needs to play nicely with GenerateTransformationNodeChain() which will
 		// this needs to play nicely with GenerateTransformationNodeChain() which will
 		// be invoked _later_ (animations come first). If this node has only rotation,
 		// be invoked _later_ (animations come first). If this node has only rotation,
 		// scaling and translation _and_ there are no animated other components either,
 		// scaling and translation _and_ there are no animated other components either,
 		// we can use a single node and also a single node animation channel.
 		// we can use a single node and also a single node animation channel.
-		const Model& target = *curve_node->TargetAsModel();
 		if (!has_complex && !NeedsComplexTransformationChain(target)) {
 		if (!has_complex && !NeedsComplexTransformationChain(target)) {
 
 
 			aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, 
 			aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, 
@@ -1635,6 +1659,53 @@ private:
 	}
 	}
 
 
 
 
+	// ------------------------------------------------------------------------------------------------
+	bool IsRedundantAnimationData(const Model& target, 
+		TransformationComp comp, 
+		const std::vector<const AnimationCurveNode*>& curves)
+	{
+		ai_assert(curves.size());
+
+		// look for animation nodes with
+		//  * sub channels for all relevant components set
+		//  * one key/value pair per component
+		//  * combined values match up the corresponding value in the bind pose node transformation
+		// only such nodes are 'redundant' for this function.
+
+		if (curves.size() > 1) {
+			return false;
+		}
+
+		const AnimationCurveNode& nd = *curves.front();
+		const AnimationCurveMap& sub_curves = nd.Curves();
+
+		const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
+		const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
+		const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
+
+		if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
+			return false;
+		}
+
+		const KeyValueList& vx = (*dx).second->GetValues();
+		const KeyValueList& vy = (*dy).second->GetValues();
+		const KeyValueList& vz = (*dz).second->GetValues();
+
+		if(vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
+			return false;
+		}
+
+		const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
+		const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(), 
+			NameTransformationCompProperty(comp), 
+			TransformationCompDefaultValue(comp)
+		);
+
+		const float epsilon = 1e-6f;
+		return (dyn_val - static_val).SquareLength() < epsilon;
+	}
+
+
 	// ------------------------------------------------------------------------------------------------
 	// ------------------------------------------------------------------------------------------------
 	aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, 
 	aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, 
 		const Model& target, 
 		const Model& target, 
@@ -2101,13 +2172,17 @@ private:
 	typedef std::map<const Material*, unsigned int> MaterialMap;
 	typedef std::map<const Material*, unsigned int> MaterialMap;
 	MaterialMap materials_converted;
 	MaterialMap materials_converted;
 
 
-
 	typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
 	typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
 	MeshMap meshes_converted;
 	MeshMap meshes_converted;
 
 
+	// fixed node name -> which trafo chain components have animations?
 	typedef std::map<std::string, unsigned int> NodeAnimBitMap;
 	typedef std::map<std::string, unsigned int> NodeAnimBitMap;
 	NodeAnimBitMap node_anim_chain_bits;
 	NodeAnimBitMap node_anim_chain_bits;
 
 
+	// name -> has had its prefix_stripped?
+	typedef std::map<std::string, bool> NodeNameMap;
+	NodeNameMap node_names;
+
 
 
 	aiScene* const out;
 	aiScene* const out;
 	const FBX::Document& doc;
 	const FBX::Document& doc;

+ 6 - 0
code/FBXImportSettings.h

@@ -60,6 +60,7 @@ struct ImportSettings
 		, strictMode(true)
 		, strictMode(true)
 		, readWeights(true)
 		, readWeights(true)
 		, preservePivots(true)
 		, preservePivots(true)
+		, optimizeEmptyAnimationCurves(true)
 	{}
 	{}
  
  
 
 
@@ -126,6 +127,11 @@ struct ImportSettings
 	 *    Rotation
 	 *    Rotation
 	 **/
 	 **/
 	bool preservePivots;
 	bool preservePivots;
+
+	/** do not import animation curves that specify a constant
+	 *  values matching the corresponding node transformation.
+	 *  The default value is true. */
+	bool optimizeEmptyAnimationCurves;
 };
 };