Browse Source

Various FBX import fixes:
- Properly transform complex import hierarchies
- When import node is influenced by animation, evaluate the 0th time of the animation and use it as the base pose
- Properly import triangle winding and account for winding changes due to negative scaling factors
- Properly import mesh orientation
- Account for 3DS Max geometry translation, rotation and scaling properties
- Fix mesh corruption caused when vertex colors were imported

BearishSun 8 years ago
parent
commit
36e1caf94b

+ 1 - 2
Source/BansheeCore/Source/BsRendererMeshData.cpp

@@ -154,8 +154,7 @@ namespace bs
 		UINT32 numElements = mMeshData->getNumVertices();
 		assert(numElements * sizeof(UINT32) == size);
 
-		UINT8* colorDst = mMeshData->getElementData(VES_COLOR);
-		memcpy(colorDst, buffer, size);
+		mMeshData->setVertexData(VES_COLOR, (UINT8*)buffer, size);
 	}
 
 	void RendererMeshData::getUV0(Vector2* buffer, UINT32 size)

+ 2 - 1
Source/BansheeFBXImporter/Include/BsFBXImportData.h

@@ -38,10 +38,11 @@ namespace bs
 	{
 		~FBXImportNode();
 
-		Matrix4 localTransform;
+		Matrix4 geomTransform;
 		Matrix4 worldTransform;
 		String name;
 		FbxNode* fbxNode;
+		bool flipWinding;
 
 		Vector<FBXImportNode*> children;
 	};

+ 54 - 13
Source/BansheeFBXImporter/Source/BsFBXImporter.cpp

@@ -359,7 +359,7 @@ namespace bs
 
 			for (auto& node : mesh->referencedBy)
 			{
-				Matrix4 worldTransform = scene.globalScale * node->worldTransform;
+				Matrix4 worldTransform = scene.globalScale * node->worldTransform * node->geomTransform;
 				Matrix4 worldTransformIT = worldTransform.inverse();
 				worldTransformIT = worldTransformIT.transpose();
 
@@ -511,6 +511,7 @@ namespace bs
 
 	void FBXImporter::parseScene(FbxScene* scene, const FBXImportOptions& options, FBXImportScene& outputScene)
 	{
+		// Scale from file units to engine units, and apply optional user scale
 		float importScale = 1.0f;
 		if (options.importScale > 0.0001f)
 			importScale = options.importScale;
@@ -589,27 +590,49 @@ namespace bs
 	{
 		FBXImportNode* node = bs_new<FBXImportNode>();
 
-		Vector3 translation = FBXToNativeType(fbxNode->LclTranslation.Get());
-		Vector3 rotationEuler = FBXToNativeType(fbxNode->LclRotation.Get());
-		Vector3 scale = FBXToNativeType(fbxNode->LclScaling.Get());
+		Vector3 translation = FBXToNativeType(fbxNode->EvaluateLocalTranslation(FbxTime(0)));
+		Vector3 rotationEuler = FBXToNativeType(fbxNode->EvaluateLocalRotation(FbxTime(0)));
+		Vector3 scale = FBXToNativeType(fbxNode->EvaluateLocalScaling(FbxTime(0)));
 
 		Quaternion rotation((Degree)rotationEuler.x, (Degree)rotationEuler.y, (Degree)rotationEuler.z);
 
-		node->localTransform.setTRS(translation, rotation, scale);
+		Matrix4 localTransform = Matrix4::TRS(translation, rotation, scale);
 		node->name = fbxNode->GetNameWithoutNameSpacePrefix().Buffer();
 		node->fbxNode = fbxNode;
 
 		if (parent != nullptr)
 		{
-			node->worldTransform = node->localTransform * parent->worldTransform;
+			node->worldTransform = parent->worldTransform * localTransform;
 
 			parent->children.push_back(node);
 		}
 		else
-			node->worldTransform = node->localTransform;
+			node->worldTransform = localTransform;
+
+		// Geometry transform is applied to geometry (mesh data) only, it is not inherited by children, so we store it
+		// separately
+		Vector3 geomTrans = FBXToNativeType(fbxNode->GeometricTranslation.Get());
+		Vector3 geomRotEuler = FBXToNativeType(fbxNode->GeometricRotation.Get());
+		Vector3 geomScale = FBXToNativeType(fbxNode->GeometricScaling.Get());
+
+		Quaternion geomRotation((Degree)geomRotEuler.x, (Degree)geomRotEuler.y, (Degree)geomRotEuler.z);
+		node->geomTransform = Matrix4::TRS(geomTrans, geomRotation, geomScale);
 
 		scene.nodeMap.insert(std::make_pair(fbxNode, node));
 
+		// Determine if geometry winding needs to be flipped to match the engine convention. This is true by default, but
+		// each negative scaling factor changes the winding.
+		if (parent != nullptr)
+			node->flipWinding = parent->flipWinding;
+		else
+			node->flipWinding = true;
+
+		for (UINT32 i = 0; i < 3; i++)
+		{
+			if (scale[i] < 0.0f) node->flipWinding = !node->flipWinding;
+			if (geomScale[i] < 0.0f) node->flipWinding = !node->flipWinding;
+		}
+
 		return node;
 	}
 
@@ -625,7 +648,6 @@ namespace bs
 			splitMesh->bones = mesh->bones;
 
 			FBXUtility::splitVertices(*mesh, *splitMesh);
-			FBXUtility::flipWindingOrder(*splitMesh);
 			splitMeshes.push_back(splitMesh);
 
 			bs_delete(mesh);
@@ -891,14 +913,29 @@ namespace bs
 			UINT32 numIndices = (UINT32)mesh->indices.size();
 			for (auto& node : mesh->referencedBy)
 			{
-				Matrix4 worldTransform = scene.globalScale * node->worldTransform;
+				Matrix4 worldTransform = scene.globalScale * node->worldTransform * node->geomTransform;
 				Matrix4 worldTransformIT = worldTransform.inverse();
 				worldTransformIT = worldTransformIT.transpose();
 
 				SPtr<RendererMeshData> meshData = RendererMeshData::create((UINT32)numVertices, numIndices, (VertexLayout)vertexLayout);
 
 				// Copy indices
-				meshData->setIndices((UINT32*)mesh->indices.data(), numIndices * sizeof(UINT32));
+				if(!node->flipWinding)
+					meshData->setIndices(orderedIndices, numIndices * sizeof(UINT32));
+				else
+				{
+					UINT32* flippedIndices = bs_stack_alloc<UINT32>(numIndices);
+
+					for (UINT32 i = 0; i < numIndices; i += 3)
+					{
+						flippedIndices[i + 0] = orderedIndices[i + 0];
+						flippedIndices[i + 1] = orderedIndices[i + 2];
+						flippedIndices[i + 2] = orderedIndices[i + 1];
+					}
+
+					meshData->setIndices(flippedIndices, numIndices * sizeof(UINT32));
+					bs_stack_free(flippedIndices);
+				}
 
 				// Copy & transform positions
 				UINT32 positionsSize = sizeof(Vector3) * (UINT32)numVertices;
@@ -1014,6 +1051,8 @@ namespace bs
 				allSubMeshes.push_back(subMeshes);
 			}
 
+			bs_free(orderedIndices);
+
 			UINT32 numBones = (UINT32)mesh->bones.size();
 			boneIndexOffset += numBones;
 		}
@@ -1882,9 +1921,11 @@ namespace bs
 				node->SetScalingOffset(FbxNode::eDestinationPivot, zero);
 				node->SetRotationPivot(FbxNode::eDestinationPivot, zero);
 				node->SetScalingPivot(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricTranslation(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricRotation(FbxNode::eDestinationPivot, zero);
-				node->SetGeometricScaling(FbxNode::eDestinationPivot, one);
+
+				// We account for geometric properties separately during node traversal
+				node->SetGeometricTranslation(FbxNode::eDestinationPivot, node->GetGeometricTranslation(FbxNode::eSourcePivot));
+				node->SetGeometricRotation(FbxNode::eDestinationPivot, node->GetGeometricRotation(FbxNode::eSourcePivot));
+				node->SetGeometricScaling(FbxNode::eDestinationPivot, node->GetGeometricScaling(FbxNode::eSourcePivot));
 
 				// Banshee assumes euler angles are in YXZ order
 				node->SetRotationOrder(FbxNode::eDestinationPivot, FbxEuler::eOrderYXZ);

+ 1 - 1
Source/BansheeFBXImporter/Source/BsFBXUtility.cpp

@@ -229,7 +229,7 @@ namespace bs
 				if (splits.empty())
 				{
 					dstVertIdx = srcVertIdx;
-					copyVertexAttributes(source, i, dest, srcVertIdx);
+					copyVertexAttributes(source, i, dest, dstVertIdx);
 				}
 				else // Split occurred, add a brand new vertex
 				{