Game Development Community

PhysX and Polysoup Work

by Ronald J Nelson · in Torque Game Engine Advanced · 03/19/2009 (12:23 am) · 2 replies

I have been trying to get PhysX and Polysoup to work together. So far the best I have come up with is to create Triangle Mesh Collision for the PhysX objects to use in collisions, but that seems inefficient as youa re creating two different collisions for the same object. Also if you try to use my method with Shape Replicators like the fxShapeReplicator or the BG Cliff Kit replicator it crashes the engine.

I am sharing my code for what I tried, but I would really like to see if anyone has come up with a better method yet.

Add this to the bottom of PhysXTSStatic.cpp
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.min.convolve(scale);
	box.max.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;

		// Loop through the TSDrawPrimitives.	
		for ( U32 j = 0; j < mesh->primitives.size(); j++ )
		{
			// Pull out the TSDrawPrimitive.
			TSDrawPrimitive &draw = mesh->primitives[j];
			U32 start = draw.start;
         
			if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles)
			{
				nbFaces += draw.numElements / 3;
			}
			else
			{
				U32 idx0 = base + mesh->indices[start + 0];
				U32 idx1;
				U32 idx2 = base + mesh->indices[start + 1];
				U32 * nextIdx = &idx1;
				for ( S32 k = 2; k < draw.numElements; k++ )
				{
					*nextIdx = idx2;
					nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1);
					idx2 = base + mesh->indices[start + k];
					if (idx0 == idx1 || idx0 == idx2 || idx1 == idx2)
						continue; 
					nbFaces++;  
				}
			}
		}


		for ( U32 j = 0; j < mesh->primitives.size(); j++ )
		{
			TSDrawPrimitive &draw = mesh->primitives[j];
			
			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.0f;
		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.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 add
void SetupTriangleCollision(bool server,TSStatic &);
right below
void SetupCollision(bool server,TSStatic &);

Finally in PhysXWorld.cpp change PhysXWorld::SetupTSStatic 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);
		}
	}
}

I hope someone has something better or can use this to get replicators to work.

#1
03/19/2009 (1:16 am)
Not sure exactly what you mean by getting PhysX and Polysoup to work together. What is the result that you want to accomplish with it?

If you want PhysX to act on polysoup meshes, then your only real option is using triangle collision meshes like you're doing.

You should keep in mind one thing though; in PhysX, triangle mesh objects won't collide against other triangle mesh objects. So use triangle meshes for all of your static meshes, and other types of volumes for any movable objects, and maybe something like a capsule character controller for "player" characters.

To maybe simplify what you're doing, you could just try querying everything for their collision meshes using I believe the buildPolyList method. This would give you the polysoup meshes for polysoup objects, and convex meshes from objects that use convex volumes. Then you could cook convex meshes for PhysX with the convex ones that work a little faster and I believe will collide against tri meshes. I may have the name of that method wrong, its 4am, but whatever method is used to get the collision polys :P

Also, if you cook the terrain, you'll probably want to cook it to a file, and only recook it when the terrain changes, maybe hook that into the mission re-lighting or something. Lots of triangles there and it will take a while ;)

I haven't really looked at any of the replicator stuff so I'm not sure what the issues would be around that. What is it crashing on?
#2
03/19/2009 (1:51 am)
Gerald - I know the technique works for all non-replicated statics, but when you try it with a replicated static I am getting heap crashes.