Collision Shape From FBX SDK Vertices

Post Reply
User avatar
KKlouzal
Posts: 65
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

Collision Shape From FBX SDK Vertices

Post by KKlouzal »

I am using extracting vertex information from the FBX SDK to construct a btBvhTriangleMeshShape. Firstly I'm not sure if this is the most appropriate type of collision shape to use and secondly the collision shape appears to be deformed. Objects sometimes only collide with one side of some other objects. Objects also sometimes do not collide with some other objects if they are moving too fast. I do not believe this is a Continuous Collision Detection issue, however I will not rule that out as the issue. Let me show you what I'm doing to better understand.

We load in a .fbx file which is split up into multiple child mesh objects. First we must calculate a transform based on the child mesh translation/rotation/scale which will later be applied to offset our vertices from local to global positions:

Code: Select all

FbxAMatrix matrixGeo;
matrixGeo.SetIdentity();
if (Node->GetNodeAttribute())
{
	const fbxsdk::FbxVector4 lT = Node->GetGeometricTranslation(FbxNode::eSourcePivot);
	const fbxsdk::FbxVector4 lR = Node->GetGeometricRotation(FbxNode::eSourcePivot);
	const fbxsdk::FbxVector4 lS = Node->GetGeometricScaling(FbxNode::eSourcePivot);
	matrixGeo.SetT(lT);
	matrixGeo.SetR(lR);
	matrixGeo.SetS(lS);
}
FbxAMatrix globalMatrix = Node->EvaluateGlobalTransform();
FbxAMatrix matrix = globalMatrix * matrixGeo;
Then we use 'matrix' to offset the vertices and save them into a buffer for later use:

Code: Select all

const FbxMesh* Mesh = FbxCast<const FbxMesh>(Node->GetNodeAttribute());
fbxsdk::FbxVector4* Vertices = Mesh->GetControlPoints();
uint32_t IndexCount = 0;
for (unsigned int j = 0; j < Mesh->GetPolygonCount(); j++) {
	const unsigned int NumVerts = Mesh->GetPolygonSize(j);
	if (NumVerts != 3) { continue; }
	for (unsigned int k = 0; k < NumVerts; k++) {
		Vertex* NewVertex = new Vertex;
		const unsigned int Index = Mesh->GetPolygonVertex(j, k);
		//NewVertex->pos.x = (float)(Vertices[Index].mData[0]);
		//NewVertex->pos.y = (float)(Vertices[Index].mData[1]);
		//NewVertex->pos.z = (float)(Vertices[Index].mData[2]);
		fbxsdk::FbxVector4 result = matrix.MultT(Vertices[Index]);
		NewVertex->pos.x = (float)result.mData[0];
		NewVertex->pos.y = (float)result.mData[1];
		NewVertex->pos.z = (float)result.mData[2];

		NewFBX->Meshes.back()->Indices.push_back(IndexCount++);
		NewFBX->Meshes.back()->Vertices.push_back(*NewVertex);
	}
}
Before we continue, I would like to mention that once a vertex is stored into its array it is not altered in any way shape or form. The same array of vertices are passed over to bullet to create the collision object and also passed to the renderer for visual display and representation. Visually the meshes are being rendered correctly with no noticeable defects from that which is shown in the 3d modeling software.

After constructing array of vertices we pass this array over to create a collision shape with bullet:

Code: Select all

TriangleMesh* Mesh = new TriangleMesh(_Driver, Pipe, FBXMesh, DiffuseTex);
btCollisionShape* ColShape;
btTriangleMesh* trimesh = new btTriangleMesh();
for (unsigned int i = 0; i < FBXMesh->Indices.size() / 3; i++) {
	auto V1 = FBXMesh->Vertices[i * 3].pos;
	auto V2 = FBXMesh->Vertices[i * 3 + 1].pos;
	auto V3 = FBXMesh->Vertices[i * 3 + 2].pos;
		
	trimesh->addTriangle(btVector3(V1.x, V1.y, V1.z), btVector3(V2.x, V2.y, V2.z), btVector3(V3.x, V3.y, V3.z));
}
ColShape = new btBvhTriangleMeshShape(trimesh, true);
Create our 'btTransform' which, please correct me if I am wrong, does not need an origin or rotation, the commented out lines here were back from before I was using FBX SDK to transform vertex positions into global space, and as such required me to manually translate/rotate/scale the objects into their intended positions (otherwise all the child mesh objects would be bunched up at 0,0,0 with incorrect rotations and scale)

Code: Select all

btTransform Transform;
Transform.setIdentity();
//Transform.setOrigin(btVector3(FBXMesh->translation[0], FBXMesh->translation[1], FBXMesh->translation[2]));
//Transform.setRotation(btQuaternion(glm::radians(FBXMesh->rotation[1]), glm::radians(FBXMesh->rotation[0]), glm::radians(FBXMesh->rotation[2])));
Finally construct our rigid body and store important objects into various locations:

Code: Select all

SceneNodeMotionState* MotionState = new SceneNodeMotionState(MeshNode, Transform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(Mass, MotionState, MeshNode->_CollisionShape, localInertia);
MeshNode->_RigidBody = new btRigidBody(rbInfo);
MeshNode->_RigidBody->setUserPointer(MeshNode);
dynamicsWorld->addRigidBody(MeshNode->_RigidBody);
So that's the important bits of the process. Visually everything looks okay, physically there are problems.

I would like to draw your attention to this section of code:

Code: Select all

		//NewVertex->pos.x = (float)(Vertices[Index].mData[0]);
		//NewVertex->pos.y = (float)(Vertices[Index].mData[1]);
		//NewVertex->pos.z = (float)(Vertices[Index].mData[2]);
		fbxsdk::FbxVector4 result = matrix.MultT(Vertices[Index]);
		NewVertex->pos.x = (float)result.mData[0];
		NewVertex->pos.y = (float)result.mData[1];
		NewVertex->pos.z = (float)result.mData[2];
Flipping which lines here are commented out results in absolutely no issues with the collision system. Essentially swapping which lines are commented here will no longer use the global matrix to translate vertex positions and instead directly store positions as they are read from the .fbx file (albeit with incorrect position/rotation/scale). So this leads me to believe the vertex transformation isn't quite right, but then why does everything look okay visually..? I have also inspected the vertex buffer with a break point and nothing stands out as odd..

Any ideas?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Collision Shape From FBX SDK Vertices

Post by drleviathan »

btBvhTriangleMeshShape only supports static objects, not objects with non-zero velocity. For dynamic objects use btCompoundShape with convex decompositions of your concave shapes, or maybe the GImpactShapes.
User avatar
KKlouzal
Posts: 65
Joined: Thu Mar 06, 2014 5:56 am
Location: USA - Arizona

Re: Collision Shape From FBX SDK Vertices

Post by KKlouzal »

Thank you for the reply, yes the objects using btBvhTriangleMeshShape are static objects and anything else is currently using btCompoundShape via convex decomposition.

excerpt of static btBvhTriangleMeshShape creation:

Code: Select all

			btScalar Mass(0.0f);
			bool isDynamic = (Mass != 0.f);

			btVector3 localInertia(0, 0, 0);
			if (isDynamic) {
				MeshNode->_CollisionShape->calculateLocalInertia(Mass, localInertia);
			}

			SceneNodeMotionState* MotionState = new SceneNodeMotionState(MeshNode, Transform);
			btRigidBody::btRigidBodyConstructionInfo rbInfo(Mass, MotionState, MeshNode->_CollisionShape, localInertia);
I think the issue *may* be related to FBX SDK vertex transformations, my vertex positions may not be quite right since btCompoundShape objects created with convex decomposition appear to have a physics shape that is roughly 1.3X larger than the visual representation. Again nothing seems wrong/out-of-place with the physical or visual representation of objects using my "old method", it is only now that I have applied vertex transformations with the FBX SDK that things are starting to get buggy.
I am here making sure I haven't done anything wrong on the bullet side of things..
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Collision Shape From FBX SDK Vertices

Post by drleviathan »

Sorry, I misunderstood the collision problems you were describing: it sounded like the mesh objects were moving around. I re-read your post more carefully and offer this numerated summary of the problems you are experiencing, with some re-wording for clarity. If I got any of these wrong please offer a suggestion as to how to make them correct:

(1) the collision shape appears to be deformed
(2) dynamic objects sometimes only collide with one side of some static mesh objects
(3) fast dynamic objects also sometimes do not collide with some static mesh objects

I assume items (2) and (3) are evidence of item (1). That is, something appears to be wrong with the collision shape because (2) and (3) are happening. If this is an incorrect assumption then please clarify (1).

First of all, tunneling (3) can definitely a problem with mesh objects because their triangles are so thin. However, if (2) is happening then I could see how (3) would be exacerbated. So let us consider item (2)...

One idea that occurs to me is if the axis-aligned bounding box (AABB) of the mesh is incorrect in the broadphase then you can experience intermittent collisions like that, where dynamic objects collide correctly in one corner (where the AABB overlaps the mesh) but not in another (outside the AABB). Often this error results in "snagging" where the dynamic object appears to get tangled inside the mesh, at the edge where the AABB cuts through the mesh. Advice: sanity-check the AABB to make sure it is correct and contains all the mesh points. Note, there is a version of the ctor that accepts explicit AABB min/max corners, so you could override the AABB with a very big generous box just to quickly determine if the problem goes away.

btBvhTriangleMeshShape uses a Boundary Volume Hierarchy (hence the Bvh prefix) data structure on the inside. This to optimize overlap queries to limit the number of triangles submitted to the narrowphase. I mention this because: I wonder if the Bvh is getting messed up somehow? I can't imagine how that would be... unless you are submitting zero-area triangles which I think might mess it up. Advice: sanity check each triangle you are adding to the mesh to make sure it isn't degenerate.

Note: Depending on the shape and nature of your whole mesh it might be advantageous to combine all the submeshes into one big mesh and let the Bvh sort them for you. That is, I perhaps your problem goes away when you do this? However if the big mesh is volatile in any way (i.e. you need to add/remove/modify submesh parts at runtime), or if the big mesh is very sparse, then splitting it into multiple meshes will probably be better. The time to recompute the Bvh structure of a large mesh can interfere with real-time performance when the triangle count gets high, and compact+complex submeshes are often better when split out into their own btCollisionObject instance.
Post Reply