Răsfoiți Sursa

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

Alexander Gessler 13 ani în urmă
părinte
comite
94e1e78c55
1 a modificat fișierele cu 183 adăugiri și 52 ștergeri
  1. 183 52
      code/FBXConverter.cpp

+ 183 - 52
code/FBXConverter.cpp

@@ -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();
-
-				if (!hasScale && !hasRotation && !hasTranslation) {
-					FBXImporter::LogWarn("ignoring node animation, did not find transformation key frames");
-					continue;
-				}
+				bool has_any = false;
+				bool has_complex = false;
 
-				aiNodeAnim* const na = new aiNodeAnim();
-				node_anims.push_back(na);
+				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;
 
-				na->mNodeName.Set(kv.first);
+						if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling &&
+							comp != TransformationComp_Translation) {
 
-				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);
+							has_complex = true;
+						}
+					}
 				}
-				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 (!has_any) {
+					FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
+					continue;
 				}
 
-				if(hasRotation) {
-					ConvertRotationKeys(na, (*itRotation).second, layer_map, 
-						max_time, min_time);
-				}
-				else {
-					na->mRotationKeys = new aiQuatKey[1];
-					na->mNumRotationKeys = 1;
+				ai_assert(curve_node->TargetAsModel());
 
-					na->mRotationKeys[0].mTime = 0.;
-					na->mRotationKeys[0].mValue = EulerToQuaternion(
-						PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f))
+				// 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)) {
+
+					aiNodeAnim* const nd = GenerateSimpleNodeAnim(kv.first, target, chain, 
+						node_property_map.end(), 
+						layer_map,
+						max_time,
+						min_time
 					);
-				}
 
-				if(hasTranslation) {
-					ConvertTranslationKeys(na, (*itTranslation).second, layer_map, 
-						max_time, min_time);
+					ai_assert(nd);
+					node_anims.push_back(nd);
+					continue;
 				}
-				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));
-				}
+				// otherwise, things get gruesome.
+				aiNodeAnim* const na = new aiNodeAnim();
+
+				// 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;