Game Development Community

PhysX Cooked Mesh Raycast Code help please

by Ronald J Nelson · in Torque Game Engine · 11/04/2008 (4:16 pm) · 6 replies

I am posting this in both TGE and TGEA forums because I know not everyone owns both and I am hoping to up my chances for a successful resolution to my code problems.

I have successfully gotten Triangle Mesh Cooking working in PhysX(with help). What I am trying to get working is the TSStatic::castRay function so collisions can be detected. I have almost succeeded.

The following is everything I added to get this working. Everyone is welcome to use this I just want a fix to the castray issue.

Thanks in advance.

#1
11/11/2008 (1:51 pm)
First in TSStatic.h

Change this
class TSStatic : public SceneObject
{
   typedef SceneObject Parent;
   friend class TSStaticConvex;

to

class TSStatic : public SceneObject
{
   typedef SceneObject Parent;
   friend class TSStaticConvex;
   friend class PhysXTSStatic;
   friend class PhysXWorld;

Right above
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);

add

void prepCollision();

Right below
TSShapeInstance*  mShapeInstance;
   Shadow * mShadow;

add

//Triangle Mesh Cooking
   bool mUseTriangleCook;
   bool mAllowPlayerStep;
   //Triangle Mesh Cooking

Right below
void inspectPostApply();

add

//Triangle Mesh Cooking
   bool usesTriangleCook() {return mUseTriangleCook;}
   bool allowPlayerStep() {return mAllowPlayerStep;}
   //Triangle Mesh Cooking
#2
11/11/2008 (1:52 pm)
Next in TSStatic.cc/cpp

Above
#include "game/tsStatic.h"

add

//Triangle Mesh Cooking
#include "physX/PhysX.h"
#include "physX/PhysXWorld.h"
//Triangle Mesh Cooking

In TSStatic::TSStatic() after
mShadow           = NULL;

add

mUseTriangleCook = false;
   mAllowPlayerStep = true;

In TSStatic::initPersistFields()after
addField("customAmbientLighting", TypeColorF, Offset(customAmbientLighting, SceneObject));
   addField("lightGroupName", TypeString, Offset(lightGroupName, SceneObject));
   endGroup("Lighting");

add

addGroup("Collision");
   addField("useTriangleCook", TypeBool, Offset(mUseTriangleCook, TSStatic));
   addField("allowPlayerStep", TypeBool, Offset(mAllowPlayerStep, TSStatic));
   endGroup("Collision");

Replace the entire bool TSStatic::onAdd() function with
bool TSStatic::onAdd()
{
   if(!Parent::onAdd())
      return false;

   if (!mShapeName || mShapeName[0] == '[[60c230ec27ce8]]') {
      Con::errorf("TSStatic::onAdd: no shape name!");
      return false;
   }
   mShapeHash = _StringTable::hashString(mShapeName);

   mShape = ResourceManager->load(mShapeName);

   if (bool(mShape) == false)
   {
      Con::errorf("TSStatic::onAdd: unable to load shape: %s", mShapeName);
      return false;
   }

   if(isClientObject() && !mShape->preloadMaterialList() && NetConnection::filesWereDownloaded())
      return false;

   mObjBox = mShape->bounds;
   resetWorldBox();
   setRenderTransform(mObjToWorld);

   mShapeInstance = new TSShapeInstance(mShape, isClientObject());

   // Triangle Mesh Cooking
   PhysXWorld *PxWorld = PhysXWorld::getWorld(isServerObject());
   if(mUseTriangleCook)
   {
      // Cleanup any old collision data.
      mCollisionDetails.clear();
      mLOSDetails.clear();
   }
   else
   {
      prepCollision();
   }
   
   addToScene();
   
   if (PxWorld)
   {
      if(mUseTriangleCook)
      {
         PxWorld->SetupCookedTSStatic(*this);
      }
      else
      {
         PxWorld->SetupTSStatic(*this);
      }
   }

   return true;
}

void TSStatic::prepCollision()
{
   // Scan out the collision hulls...
   U32 i;
   for (i = 0; i < mShape->details.size(); i++)
   {
      char* name = (char*)mShape->names[mShape->details[i].nameIndex];

      if (dStrstr((const char*)dStrlwr(name), "collision-"))
      {
         mCollisionDetails.push_back(i);

         // The way LOS works is that it will check to see if there is a LOS detail that matches
         // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in
         // the future). If it can't find a matching LOS it will simply use the collision instead.
         // We check for any "unmatched" LOS's further down
         mLOSDetails.increment();

      char buff[128];
         dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + MaxCollisionShapes);
         U32 los = mShape->findDetail(buff);
         if (los == -1)
            mLOSDetails.last() = i;
         else
            mLOSDetails.last() = los;
      }
   }

   // Snag any "unmatched" LOS details
   for (i = 0; i < mShape->details.size(); i++)
   {
      char* name = (char*)mShape->names[mShape->details[i].nameIndex];

      if (dStrstr((const char*)dStrlwr(name), "los-"))
      {
         // See if we already have this LOS
         bool found = false;
         for (U32 j = 0; j < mLOSDetails.size(); j++)
         {
            if (mLOSDetails[j] == i)
            {
               found = true;
               break;
            }
         }

         if (!found)
            mLOSDetails.push_back(i);
      }
   }

   // Compute the hull accelerators (actually, just force the shape to compute them)
   for (i = 0; i < mCollisionDetails.size(); i++)
         mShapeInstance->getShape()->getAccelerator(mCollisionDetails[i]);

   return;
}
// Triangle Mesh Cooking

In U32 TSStatic::packUpdate after
stream->writeString(mShapeName);

add

//Triangle Mesh Cooking
   stream->writeFlag(mUseTriangleCook);
   stream->writeFlag(mAllowPlayerStep);
   //Triangle Mesh Cooking

In void TSStatic::unpackUpdate after
mShapeName = stream->readSTString();

add

//Triangle Mesh Cooking
   mUseTriangleCook = stream->readFlag();
   mAllowPlayerStep = stream->readFlag();
   //Triangle Mesh Cooking

Replace the entire TSStatic::castRay function with
bool TSStatic::castRay(const Point3F &start, const Point3F &end, RayInfo* info)
{
   if (!mShapeInstance)
      return false;

   //Triangle Mesh Cooking
   if (mUseTriangleCook)
   {
	   PhysXWorld *PxWorld = PhysXWorld::getWorld(true);
	   if (!PxWorld)
		   return false;

	   bool res = true;
	   RayInfo localInfo;
	   
	   //Convert the variables
	   NxVec3 startTMC(start.x,start.y,start.z);
	   NxVec3 endTMC(end.x,end.y,end.z);

	   NxRaycastHit rayInfoTMC;
	   dMemset( &rayInfoTMC, 0, sizeof( NxRaycastHit ) );
	   
	   NxRay ray;
	   ray.dir = NxVec3(endTMC - startTMC); 
	   ray.orig = startTMC;
	   ray.dir.normalize();

	   PxWorld->gScene->raycastClosestShape(ray, NX_STATIC_SHAPES, rayInfoTMC);
	   
	   if(!rayInfoTMC.shape)
		   res = false;
	   
	   //Convert the variables
	   F32 dist = F32(rayInfoTMC.distance);
	   U32 face = U32(rayInfoTMC.faceID);
	   //F32 t = F32(rayInfoTMC.distance/maxDist);
	   VectorF nm;
	   nm.set(rayInfoTMC.worldNormal.x, rayInfoTMC.worldNormal.y, rayInfoTMC.worldNormal.z);
	   Point3F pnt;
	   pnt.set(rayInfoTMC.worldImpact.x, rayInfoTMC.worldImpact.y, rayInfoTMC.worldImpact.z);
	   
	   Con::printf("Distance: %f",dist);
	   Con::printf("Face: %i",face);
	   //Con::printf("t: %f",t);
	   Con::printf("Normal X: %f",nm.x);
	   Con::printf("Normal Y: %f",nm.y);
	   Con::printf("Normal Z: %f",nm.z);
	   Con::printf("Point X: %f",pnt.x);
	   Con::printf("Point Y: %f",pnt.y);
	   Con::printf("Point Z: %f",pnt.z);
	   
	   localInfo.distance = dist;
	   localInfo.face = face;
	   localInfo.HitBoxNum = 0;
	   localInfo.material = 0;
	   localInfo.normal = nm;
	   localInfo.point = pnt;
	   //localInfo.t = t;
	   //localInfo.texCoord = texCrd;
	   
	   *info = localInfo;
	   
	   info->object = this;
	   
	   if(!res)
		   Con::printf("TSStatic::castRay Failed");
	   else
		   Con::printf("TSStatic::castRay Succesful");
	   
	   return res;
	   
   }
   //Triangle Mesh Cooking

	RayInfo shortest;
	shortest.t = 1e8;

	info->object = NULL;
	for (U32 i = 0; i < mLOSDetails.size(); i++)
	{
		mShapeInstance->animate(mLOSDetails[i]);

		if (mShapeInstance->castRay(start, end, info, mLOSDetails[i]))
		{
			info->object = this;
			if (info->t < shortest.t)
				shortest = *info;
		}
	}

	if (info->object == this) 
	{
		// Copy out the shortest time...
		*info = shortest;
		return true;
	}

	return false;
}
#3
11/11/2008 (1:53 pm)
In PhysXWorld.h

After
typedef NetObject Parent;

add

friend class TSStatic;

then move
NxPhysicsSDK	*gPhysicsSDK;
down below
public:
a few lines down.

Finally, below
void SetupTSStatic(TSStatic &);

add

void SetupCookedTSStatic(TSStatic &);
#4
11/11/2008 (1:53 pm)
In PhysXWorld.cc/cpp

Right after the void PhysXWorld::SetupTSStatic(TSStatic &tsstatic) function add the following funtion

void PhysXWorld::SetupCookedTSStatic(TSStatic &tsstatic)
{
	if (mRemoteClient || isServerObject())
	{
		PhysXTSStatic *pxTSStatic = new PhysXTSStatic();
		pxTSStatic->registerObject();
		mTSStaticList.push_front(pxTSStatic);

		pxTSStatic->SetupTriangleCollision(isServerObject(),tsstatic);
	}
}
#5
11/11/2008 (1:54 pm)
In PhysXTSStatic.h

Change
private:
	typedef SimObject Parent;
	bool	mServer;

	S32						mNumTSStatic;
	NxConvexMesh *tsstaticMesh;
	Vector<NxConvexShapeDesc*> convexShapeDesc;

to

private:
	typedef SimObject Parent;
	bool	mServer;

	S32						mNumTSStatic;
	NxConvexMesh *tsstaticMesh;
	NxTriangleMesh *mTriangleMesh;
	Vector<NxConvexShapeDesc*> convexShapeDesc;
	Vector<NxTriangleMeshDesc*> triangleMeshDesc;

Finally after
void SetupCollision(bool server,TSStatic &);

add

void SetupTriangleCollision(bool server,TSStatic &);
#6
11/11/2008 (1:54 pm)
In PhysXTSStatic.cc/cpp

Change
PhysXTSStatic::PhysXTSStatic()
{
	mServer = false;
	gTSStatic = NULL;
	tsstaticMesh = NULL;
	VECTOR_SET_ASSOCIATION(convexShapeDesc);
	mNumTSStatic = 1;
	nbVerts = 0;
	nbFaces = 0;
	verts = NULL;
	faces = NULL;
}

to

PhysXTSStatic::PhysXTSStatic()
{
	mServer = false;
	gTSStatic = NULL;
	tsstaticMesh = NULL;
	mTriangleMesh = NULL;
	VECTOR_SET_ASSOCIATION(convexShapeDesc);
	VECTOR_SET_ASSOCIATION(triangleMeshDesc);
	mNumTSStatic = 1;
	nbVerts = 0;
	nbFaces = 0;
	verts = NULL;
	faces = NULL;
}

Finally at the end of the file 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.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( idx2 );
					indicesList.push_back( idx1 );
					indicesList.push_back( idx0 );
					
					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( idx2 );
					indicesList.push_back( idx1 );
					indicesList.push_back( idx0 );
				}
			}
		}
	
		// Here you can loop through your
		// indicesList and index mesh->verts
		// to collect them all.
		nbVerts = mesh->verts.size();
		NxVec3 * 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;
		
		// 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 );
	}
}


Thats all of it.