PhysX Triangle Mesh Cooking code assistance
by Ronald J Nelson · in Torque Game Engine Advanced · 04/03/2009 (4:51 pm) · 7 replies
I am posting this in both TGE and TGEA sections as I know not everyone that may have used PhysX in their games will have both. Hopefully this will get me a solution to my problem.
I have developed a working Triangle Mesh Collision function. I say working but that is an exageration, it only works on very simple object like cubes. What I am hoping is that someone can help me find where I have gone wrong. Of course this code is free to use, especially if you can help me come up with a fully working version.
Here is my code:
At the bottom of PhysXTSStatic.cpp/cc add
In PhysXTSStatic.h right after:
Finally in PhysXWorld.cpp/cc, change the function
void PhysXWorld::SetupTSStatic(TSStatic &tsstatic)
to the following:
That will do it for very simple objects. If you have enabled polysoup on that object it will try creating a PhysX triangle mesh for it. That way PhysX will react to Polysoup objects. Keep in mind for those of you using TGE or older versions of TGEA that do not have PolySoup installed, you will need to add that or change the function in PhysXWorld.cpp/cc to make it work.
Thanks in advance.
I have developed a working Triangle Mesh Collision function. I say working but that is an exageration, it only works on very simple object like cubes. What I am hoping is that someone can help me find where I have gone wrong. Of course this code is free to use, especially if you can help me come up with a fully working version.
Here is my code:
At the bottom of PhysXTSStatic.cpp/cc add
void PhysXTSStatic::SetupTriangleCollision(bool server, TSStatic &tsstatic)
{
mServer = server;
PhysXWorld *PxWorld = PhysXWorld::getWorld(server);
// If we don't have a PhysXWorld, just return.
if ( !PxWorld )
{
Con::printf("*****SetupTriangleCollision - NO PHYSX WORLD*****");
return;
}
Box3F box = tsstatic.getObjBox();
VectorF scale = tsstatic.getScale();//*10.0;
box.minExtents.convolve(scale);
box.maxExtents.convolve(scale);
MatrixF wmat = tsstatic.getWorldTransform();
Point3F placePos = tsstatic.getPosition();
// make our matrices into Quats... for the physx engine
QuatF q(wmat);
Point3F wpos;
wmat.getColumn(3,&wpos);
NxQuat quat;
quat. setXYZW(q.x, q.y, q.z, q.w);
NxActorDesc actorDesc;
TSShapeInstance *shapeInst = tsstatic.mShapeInstance;
ConcretePolyList polyList;
if ( !shapeInst )
{
Con::printf("*****SetupTriangleCollision - NO SHAPE INSTANCE*****");
return;
}
if (!shapeInst->buildPolyList(&polyList, 0 ))
{
Con::printf("*****SetupTriangleCollision - NO POLYLIST*****");
return;
}
//need to build the index list because polylist reuses indices
Vector<U32> indices;
for (U32 i = 0; i < polyList.mPolyList.size(); i++)
{
ConcretePolyList::Poly poly = polyList.mPolyList[i];
U32 index = poly.vertexStart;
indices.push_back( polyList.mIndexList[index++] );
indices.push_back( polyList.mIndexList[index++] );
indices.push_back( polyList.mIndexList[index++] );
}
NxInitCooking();
NxTriangleMeshDesc triangleDesc;
triangleDesc.numVertices = polyList.mVertexList.size();
triangleDesc.numTriangles = polyList.mPolyList.size();
triangleDesc.pointStrideBytes = sizeof(NxVec3);
triangleDesc.triangleStrideBytes = 3*sizeof(NxU32);
triangleDesc.points = polyList.mVertexList.address();
triangleDesc.triangles = indices.address();
triangleDesc.flags = NX_MF_FLIPNORMALS;
// cook it
MemoryWriteBuffer buf;
NxCookingParams params;
params.targetPlatform = PLATFORM_PC;
params.skinWidth = 0.1f;
params.hintCollisionSpeed = false;
NxSetCookingParams(params);
bool status = NxCookTriangleMesh(triangleDesc, buf);
if (status)
{
MemoryReadBuffer readBuffer(buf.data);
mTriangleMesh = PxWorld->createTriangleMesh(readBuffer);
// Push this actor back into our actor list.
NxTriangleMeshShapeDesc staticMeshDesc;
staticMeshDesc.skinWidth = 0.1f;
staticMeshDesc.meshData = mTriangleMesh;
actorDesc.shapes.push_back(&staticMeshDesc);
NxCloseCooking();
}
else
{
Con::printf("*****SetupTriangleCollision - COOKING FAILED*****");
NxCloseCooking();
return;
}
actorDesc.body = NULL;
gTSStatic = PxWorld->AddActor(actorDesc);
if(gTSStatic->active)
{
gTSStatic->actor->setGlobalOrientationQuat(quat);
gTSStatic->actor->setGlobalPosition( NxVec3(placePos) );
gTSStatic->actor->userData = (void*) static_cast<SimObject*>( this );
}
else
Con::printf("*****SetupTriangleCollision - ACTOR NOT ACTIVE*****");
}In PhysXTSStatic.h right after:
void SetupCollision(bool server,TSStatic &);
Finally in PhysXWorld.cpp/cc, change the function
void PhysXWorld::SetupTSStatic(TSStatic &tsstatic)
to the following:
void PhysXWorld::SetupTSStatic(TSStatic &tsstatic)
{
if (mRemoteClient || isServerObject())
{
PhysXTSStatic *pxTSStatic = new PhysXTSStatic();
pxTSStatic->registerObject();
mTSStaticList.push_front(pxTSStatic);
if(tsstatic.usesPolysoup())
{
pxTSStatic->SetupTriangleCollision(isServerObject(),tsstatic);
}
else
{
pxTSStatic->SetupCollision(isServerObject(),tsstatic);
}
}
}That will do it for very simple objects. If you have enabled polysoup on that object it will try creating a PhysX triangle mesh for it. That way PhysX will react to Polysoup objects. Keep in mind for those of you using TGE or older versions of TGEA that do not have PolySoup installed, you will need to add that or change the function in PhysXWorld.cpp/cc to make it work.
Thanks in advance.
#2
Gerald - The problems I am getting and this seems to be based upon the amount of complexity (More Polys)of the mesh is it will go from good(very simple meshes), partial collisions( a few more polys), to not registering a collision( complex).
1. I kind of thought that would be true so I am testing the collisions with the stock actors provided in the PhysX resource.
2. I tried setting the skin depth and it did not help.
3. I you will notice above my loops are based upon the TSMesh buildPolyListOpcode, almost identical, and that is what the TSSapeINstance calls upon.
04/04/2009 (1:40 am)
Ok I updated the code above due to the fact that I noticed I was duplicating loop functions to do collect two different data items and they could just be combined.Gerald - The problems I am getting and this seems to be based upon the amount of complexity (More Polys)of the mesh is it will go from good(very simple meshes), partial collisions( a few more polys), to not registering a collision( complex).
1. I kind of thought that would be true so I am testing the collisions with the stock actors provided in the PhysX resource.
2. I tried setting the skin depth and it did not help.
3. I you will notice above my loops are based upon the TSMesh buildPolyListOpcode, almost identical, and that is what the TSSapeINstance calls upon.
#3
@#3, the point was that there's no reason to go the long route when there's already a method that makes it easy. Also that you shouldn't create a separate volume for each mesh in your shape, because depending on the shape you'll probably end up with some overlapping volumes which PhysX doesn't like. That's part of the point of the cooking process. So put all of them together in a single trimesh per object and life will be good.
Also I see that you're not transforming the vertices. Vertices in the TSMesh are mesh local, so you have to transform them by the mesh transform matrix. If you really want to go the complex route, that's where I'd start looking for the problem.
Here's an example of how I do it. I'm using an abstracted physics framework that I created, but the PhysicsTriMesh here is roughly analogous to the NxTriangleMeshDesc structure, so it should be pretty straightforward to convert it.
Quite a bit simpler, and you can adapt it directly to any other object that implements buildPolyList, such as the terrain, pretty easily.
04/04/2009 (6:14 am)
Ron,@#3, the point was that there's no reason to go the long route when there's already a method that makes it easy. Also that you shouldn't create a separate volume for each mesh in your shape, because depending on the shape you'll probably end up with some overlapping volumes which PhysX doesn't like. That's part of the point of the cooking process. So put all of them together in a single trimesh per object and life will be good.
Also I see that you're not transforming the vertices. Vertices in the TSMesh are mesh local, so you have to transform them by the mesh transform matrix. If you really want to go the complex route, that's where I'd start looking for the problem.
Here's an example of how I do it. I'm using an abstracted physics framework that I created, but the PhysicsTriMesh here is roughly analogous to the NxTriangleMeshDesc structure, so it should be pretty straightforward to convert it.
//init physics
ConcretePolyList polyList;
MatrixF mat;
mat.identity();
//set the polylist transform to identity
//so we can then transform our trimesh actor by the TSStatic transform
//we do have to set the object's scale since we can't scale a physics actor
polyList.setTransform( &mat, mObjScale );
//grab the polylist from detail level 0
mShapeInstance->buildPolyList( &polyList, 0 );
//build the trimesh from the polylist
PhysicsTriMesh trimesh;
trimesh.mNumVertices = polyList.mVertexList.size();
trimesh.mNumTriangles = polyList.mPolyList.size();
trimesh.mVertices = polyList.mVertexList.address(); //we can use the vertex list buffer directly
//need to build the index list because polylist reuses indices
Vector<U32> indices;
for (U32 i = 0; i < polyList.mPolyList.size(); i++)
{
ConcretePolyList::Poly poly = polyList.mPolyList[i];
U32 index = poly.vertexStart;
indices.push_back( polyList.mIndexList[index++] );
indices.push_back( polyList.mIndexList[index++] );
indices.push_back( polyList.mIndexList[index++] );
}
trimesh.mIndices = indices.address();Quite a bit simpler, and you can adapt it directly to any other object that implements buildPolyList, such as the terrain, pretty easily.
#4
04/04/2009 (10:46 am)
Gerald- If you look at the function above, I have changed it to match yours as much as possible. However, now I have no collisions at all. As you can see I did add checks for errors and none were called. I have even tried changing the flags from flip normals to other settings and removing them entirely just to be sure, but no help.
#5
Add this before the buildPolyList call:
This sets the polylist's transform to an identity, so it doesn't translate or rotate them, and sets the scale to the TSStatic's scale, so it will scale the vertices correctly.
04/04/2009 (10:55 am)
You forgot to set the transform on the ConcretePolyList. By default the matrix is junk, and it transforms the points added to it by that matrix, so you're most likely getting junk out of it.Add this before the buildPolyList call:
MatrixF mat; mat.identity(); polyList.setTransform( &mat, mObjScale );
This sets the polylist's transform to an identity, so it doesn't translate or rotate them, and sets the scale to the TSStatic's scale, so it will scale the vertices correctly.
#6
04/04/2009 (12:49 pm)
What can I say other than you rock and thanks a ton! It all works like a charm thanks to you.
Torque Owner Gerald Fishel
Development Ninja
Few things to keep in mind.
1) Trimesh objects wont collide against other trimesh objects
2) I've come across problems if you don't set NxTriangleMeshShapeDesc::skinWidth. Set it to something small like 0.1.
3) Creating a trimesh for each individual mesh component could be problematic. For static objects, concatenate them all into a single trimesh for each TSStatic. Probably the simplest way to do it is to call TSShapeInstance::buildPolyListOpcode to get all of the triangles.