2
0
Эх сурвалжийг харах

- fbx: revamp transformation code to support rotation/scaling pivots and offsets. The information is encoded in additional nodes in the scene hierarchy, which are tagged with a special naming scheme to make them easy for users to identify and map to their systems.

Alexander Gessler 13 жил өмнө
parent
commit
7f082e0aae

+ 209 - 31
code/FBXConverter.cpp

@@ -59,6 +59,9 @@ namespace FBX {
 
 	using namespace Util;
 
+
+#define MAGIC_NODE_TAG "_$AssimpFbx$"
+
 	// XXX vc9's debugger won't step into anonymous namespaces
 //namespace {
 
@@ -129,6 +132,8 @@ private:
 		std::vector<aiNode*> nodes;
 		nodes.reserve(conns.size());
 
+		std::vector<aiNode*> nodes_chain;
+
 		try {
 			BOOST_FOREACH(const Connection* con, conns) {
 
@@ -146,16 +151,27 @@ private:
 				const Model* const model = dynamic_cast<const Model*>(object);
 
 				if(model) {
-					aiNode* nd = new aiNode();
-					nodes.push_back(nd);
+					nodes_chain.clear();
+					GenerateTransformationNodeChain(*model,nodes_chain);
 
-					nd->mName.Set(FixNodeName(model->Name()));
-					nd->mParent = &parent;
+					ai_assert(nodes_chain.size());
 
-					ConvertTransformation(*model,*nd);
+					// link all nodes in a row
+					aiNode* last_parent = &parent;
+					BOOST_FOREACH(aiNode* prenode, nodes_chain) {
+						ai_assert(prenode);
+
+						prenode->mParent = last_parent;
+						last_parent = prenode;
+					}
 
-					ConvertModel(*model, *nd);
-					ConvertNodes(model->ID(), *nd);
+					// attach geometry
+					ConvertModel(*model, *nodes_chain.back());
+
+					// attach sub-nodes
+					ConvertNodes(model->ID(), *last_parent);
+
+					nodes.push_back(nodes_chain.back());					
 				}
 			}
 
@@ -167,50 +183,212 @@ private:
 			}
 		} 
 		catch(std::exception&)	{
-			std::for_each(nodes.begin(),nodes.end(),Util::delete_fun<aiNode>());
+			Util::delete_fun<aiNode> deleter;
+			std::for_each(nodes.begin(),nodes.end(),deleter);
+			std::for_each(nodes_chain.begin(),nodes_chain.end(),deleter);
+		}
+	}
+
+
+	enum TransformationComp
+	{
+		TransformationComp_Translation = 0,
+		TransformationComp_RotationOffset,
+		TransformationComp_RotationPivot,
+		TransformationComp_PreRotation,
+		TransformationComp_Rotation,
+		TransformationComp_PostRotation,
+		TransformationComp_RotationPivotInverse,
+		TransformationComp_ScalingOffset,
+		TransformationComp_ScalingPivot,
+		TransformationComp_Scaling,
+		TransformationComp_ScalingPivotInverse,
+
+		TransformationComp_MAXIMUM
+	};
+
+
+	// ------------------------------------------------------------------------------------------------
+	const char* NameTransformationComp(TransformationComp comp)
+	{
+		switch(comp)
+		{
+		case TransformationComp_Translation:
+			return "Translation";
+		case TransformationComp_RotationOffset:
+			return "RotationOffset";
+		case TransformationComp_RotationPivot:
+			return "RotationPivot";
+		case TransformationComp_PreRotation:
+			return "PreRotation";
+		case TransformationComp_Rotation:
+			return "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 "Scaling";
+		case TransformationComp_ScalingPivotInverse:
+			return "ScalingPivotInverse";
+		}
+
+		ai_assert(false);
+	}
+
+
+	enum RotationMode
+	{
+		RotationMode_Euler_XYZ
+	};
+
+
+	// ------------------------------------------------------------------------------------------------
+	void GetRotationMatrix(RotationMode mode, const aiVector3D& rotation, aiMatrix4x4& out)
+	{
+		const float angle_epsilon = 1e-6f;
+		aiMatrix4x4 temp;
+
+		out = aiMatrix4x4();
+
+		switch(mode)
+		{
+		case RotationMode_Euler_XYZ:
+
+			if(fabs(rotation.x) > angle_epsilon) {
+				out *= aiMatrix4x4::RotationX(rotation.x,temp);
+			}
+			if(fabs(rotation.y) > angle_epsilon) {
+				out *= aiMatrix4x4::RotationY(rotation.y,temp);
+			}
+			if(fabs(rotation.z) > angle_epsilon) {
+				out *= aiMatrix4x4::RotationZ(rotation.z,temp);
+			}
+
+			return;
 		}
+
+		ai_assert(false);
 	}
 
 
 	// ------------------------------------------------------------------------------------------------
-	void ConvertTransformation(const Model& model, aiNode& nd)
+	/** note: memory for output_nodes will be managed by the caller */
+	void GenerateTransformationNodeChain(const Model& model, 
+		std::vector<aiNode*>& output_nodes)
 	{
 		const PropertyTable& props = model.Props();
 
-		// XXX this is not complete, need to handle rotation and scaling pivots etc
+		// XXX handle different rotation modes
+		const RotationMode rot = RotationMode_Euler_XYZ;
 
 		bool ok;
+
+		aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+		std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
 		
-		aiVector3D Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
-		if(!ok) {
-			Translation = aiVector3D(0.0f,0.0f,0.0f);
+		// generate transformation matrices for all the different transformation components
+		const float zero_epsilon = 1e-6f;
+		bool is_complex = false;
+
+		const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props,"PreRotation",ok);
+		if(ok && PreRotation.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			GetRotationMatrix(rot, PreRotation, chain[TransformationComp_PreRotation]);
 		}
 
-		aiVector3D Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
-		if(!ok) {
-			Scaling = aiVector3D(1.0f,1.0f,1.0f);
+		const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
+		if(ok && PostRotation.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
 		}
 
-		// XXX euler angles, radians, xyz order?
-		aiVector3D Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
-		if(!ok) {
-			Rotation = aiVector3D(0.0f,0.0f,0.0f);
+		const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
+		if(ok && RotationPivot.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
+			aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
 		}
 
-		aiMatrix4x4 temp;
-		nd.mTransformation = aiMatrix4x4::Scaling(Scaling,temp);
-		if(fabs(Rotation.x) > 1e-6f) {
-			nd.mTransformation *= aiMatrix4x4::RotationX(Rotation.x,temp);
+		const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
+		if(ok && RotationOffset.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			aiMatrix4x4::Translation(RotationOffset,chain[TransformationComp_RotationOffset]);
 		}
-		if(fabs(Rotation.y) > 1e-6f) {
-			nd.mTransformation *= aiMatrix4x4::RotationY(Rotation.y,temp);
+
+		const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props,"ScalingOffset",ok);
+		if(ok && ScalingOffset.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			aiMatrix4x4::Translation(ScalingOffset,chain[TransformationComp_ScalingOffset]);
+		}
+
+		const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props,"ScalingPivot",ok);
+		if(ok && ScalingPivot.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			aiMatrix4x4::Translation(ScalingPivot,chain[TransformationComp_ScalingPivot]);
+			aiMatrix4x4::Translation(-ScalingPivot,chain[TransformationComp_ScalingPivotInverse]);
+		}
+
+		const aiVector3D& Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
+		if(ok && Translation.SquareLength() > zero_epsilon) {
+			aiMatrix4x4::Translation(Translation,chain[TransformationComp_Translation]);
+		}
+
+		const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
+		if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
+			aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
 		}
-		if(fabs(Rotation.z) > 1e-6f) {
-			nd.mTransformation *= aiMatrix4x4::RotationZ(Rotation.z,temp);
+
+		const aiVector3D& Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
+		if(ok && Translation.SquareLength() > zero_epsilon) {
+			GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
+		}
+
+		const std::string& name = FixNodeName(model.Name());
+
+		// now, if we have more than just Translation, Scaling and Rotation,
+		// we need to generate a full node chain to accommodate for assimp's
+		// lack to express pivots and offsets.
+		if(is_complex && doc.Settings().preservePivots) {
+			FBXImporter::LogInfo("generating full transformation chain for node: " + name);
+
+			const std::string mid = std::string(MAGIC_NODE_TAG) + "_";
+			for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+				// XXX this may cause trouble with animations
+				if (chain[i].IsIdentity()) {
+					continue;
+				}
+
+				aiNode* nd = new aiNode();
+				output_nodes.push_back(nd);
+
+				nd->mName.Set(name + mid  + NameTransformationComp(static_cast<TransformationComp>(i)));
+				nd->mTransformation = chain[i];
+			}
+
+			ai_assert(output_nodes.size());
+			return;
+		}
+
+		// else, we can just multiply the matrices together
+		aiNode* nd = new aiNode();
+		output_nodes.push_back(nd);
+
+		nd->mName.Set(name);
+
+		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+			nd->mTransformation *= chain[i];
 		}
-		nd.mTransformation.a4 = Translation.x;
-		nd.mTransformation.b4 = Translation.y;
-		nd.mTransformation.c4 = Translation.z;
 	}
 
 

+ 23 - 0
code/FBXImportSettings.h

@@ -59,6 +59,7 @@ struct ImportSettings
 		, readAnimations(true)
 		, strictMode(true)
 		, readWeights(true)
+		, preservePivots(true)
 	{}
  
 
@@ -103,6 +104,28 @@ struct ImportSettings
 	/** read bones (vertex weights and deform info).
 	 *  Default value is true. */
 	bool readWeights;
+
+	/** preserve transformation pivots and offsets. Since these can
+	 *  not directly be represented in assimp, additional dummy
+	 *  nodes will be generated. Note that settings this to false
+	 *  can make animation import a lot slower. The default value
+	 *  is true.
+	 *
+	 *  The naming scheme for the generated nodes is:
+	 *    <OriginalName>_$AssimpFbx$_<TransformName>
+	 *  
+	 *  where <TransformName> is one of
+	 *    RotationPivot
+	 *    RotationOffset
+	 *    PreRotation
+	 *    PostRotation
+	 *    ScalingPivot
+	 *    ScalingOffset
+	 *    Translation
+	 *    Scaling
+	 *    Rotation
+	 **/
+	bool preservePivots;
 };