Game Development Community

PhysX Triangle Mesh Cooking code assistance

by Ronald J Nelson · in Torque Game Engine · 04/03/2009 (4:53 pm) · 1 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
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 ) 
		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;
	
	Vector<U32> indicesList;

	TSShapeInstance *shapeInst = tsstatic.mShapeInstance; 

	if ( !shapeInst )
		return;

	NxInitCooking();

	// Loop through each *MeshObjectInstance* in the TSShapeInstance.
	for( U32 i = 0; i < shapeInst->mMeshObjects.size(); i++ )
   	{
		// Grab out the MeshObjectInstance.
		TSShapeInstance::MeshObjectInstance *meshInst = &shapeInst->mMeshObjects[i];

		// Grab out the TSMesh for that MeshObjectInstance.
		TSMesh *mesh = meshInst->getMesh( 0 );

		if ( !mesh )
		{
			continue; // Nothing left to do if no mesh.
		}
		
		// Figure out how many triangles we have...
		nbFaces = 0;
		const U32 base = 0;

		for ( U32 j = 0; j < mesh->primitives.size(); j++ )
		{
			TSDrawPrimitive &draw = mesh->primitives[j];
			
			nbFaces += draw.numElements/3;

			U32 start = draw.start;
			
			if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
			{
				for ( S32 j = 0; j < draw.numElements; )
				{
					U32 idx0 = base + mesh->indices[start + j + 0];
					U32 idx1 = base + mesh->indices[start + j + 1];
					U32 idx2 = base + mesh->indices[start + j + 2];
					indicesList.push_back( idx0 );
					indicesList.push_back( idx1 );
					indicesList.push_back( idx2 );
					
					j += 3;
				}
			}
			else
			{
				U32 idx0 = base + mesh->indices[start + 0];
				U32 idx1;
				U32 idx2 = base + mesh->indices[start + 1];
				U32 * nextIdx = &idx1;
				for (S32 j=2; j<draw.numElements; j++)
				{
					*nextIdx = idx2;
					
					nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
					idx2 = base + mesh->indices[start + j];
					if (idx0 == idx1 || idx0 == idx2 || idx1 == idx2)
						continue;

					indicesList.push_back( idx0 );
					indicesList.push_back( idx1 );
					indicesList.push_back( idx2 );
				}
			}
		}
	
		// Here you can loop through your
		// indicesList and index mesh->verts
		// to collect them all.
		nbVerts = mesh->verts.size();
		verts = new NxVec3[nbVerts];
	
		// Load vertices
		for(U32 i=0; i < nbVerts; i++)
		{
			verts[i].x = mesh->verts[i].x;
			verts[i].y = mesh->verts[i].y;
			verts[i].z = mesh->verts[i].z;
		}
		faces = indicesList.address();
		NxTriangleMeshDesc triangleDesc;
		triangleDesc.numVertices            = nbVerts;
		triangleDesc.numTriangles           = nbFaces;
		triangleDesc.pointStrideBytes       = sizeof(NxVec3);
		triangleDesc.triangleStrideBytes	= 3*sizeof(NxU32);
		triangleDesc.points			        = verts;
		triangleDesc.triangles              = faces;
		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);
		}
		else
		{
			return;
		}
	}
	NxCloseCooking();

	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 );
	}
}

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 are not using Polysoup, you will need to add that or change the function in PhysXWorld.cpp/cc to make it work.

Thanks in advance.

#1
04/04/2009 (1:38 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.