Monday, October 17, 2011

Cloth in PhysX 3.1

Before I start of this tutorial, here is my disclaimer
I wrote this tutorial based on some hints from the PhysX3.1 guide since it hardly tells the reader how to do the basic cloth. Therefore, this might not be the perfect way of doing cloth with PhysX3.1. Since the documentation is severely lacking such down to core tutorials, I wrote this tutorial to bridge the gap for beginners. If you find anything wrong, please let me know.
This is the first tutorial on doing cloth in PhysX3.1. The major changes in PhysX 3.1 has been the cloth API and hopefully, I will try to detail how to do a simple cloth using the new cloth API. Ok so now lets get started. This tutorial is building up on the picking tutorial I did earlier so head to it if you have not understood it completely. As usual, here are the headers and libs that we would need for this tutorial.
#include < iostream >
#include < GL/freeglut.h >
#include < PxPhysicsAPI.h > 
#include < PxExtensionsAPI.h >   
#include "Stream.h"

using namespace std;
using namespace physx;

#pragma comment(lib, "PhysX3_x86.lib")
#pragma comment(lib, "PhysX3Cooking_x86.lib")
#pragma comment(lib, "PxTask.lib")
#pragma comment(lib, "Foundation.lib")
#pragma comment(lib, "PhysX3Extensions.lib")
#pragma comment(lib, "GeomUtils.lib") 
Next, we generate the cloth variables and vectors to store the cloth positions and normals. In the previous PhysX version, we could hook the receivebuffers but now this has changed and we need to manually copy the data from the cloth and also calculate the normals. In this demo, we will do a simple collision of the cloth with a simple box. In PhysX3.1, there is no direct support for rigid body collision with a cloth. Instead, the new API provides methods for intersection between the cloth and the capsule/spheres.
vector< PxVec3 > pos;
vector< PxVec3 > normal;
vector< PxU32 > indices;
PxCloth* cloth; 
PxClothCollisionSphere box_collider;
The initialization of PhysX and other stuff is similar to the Picking tutorial. After initializing PhysX and the scene, the scene visualization parameters are set.
gScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, 1.0);
gScene->setVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES, 1.0f);
Creating the Cloth object:
The steps required to create a cloth are as follows, 1) Fill in the PxClothMeshDesc and specify the cloth geometry and topology. 2) Cook the cloth fabric by using the mesh desc generated in step 1. 3) Use the cooked buffer to generate the PxClothFabric. 4) Initialize and fill the PxClothParticle buffer with the cloth point positions and masses. 5) Call createCloth on physicsSDK pointer. 6) Adjust the cloth properties using the PxClothPhaseSolverConfig (to specify the individual stiffness and stretch limits of the PxClothFabric fibres) and assign the damping using PxCloth::setDampingCoefficient function. 7) Add the cloth actor to the scene. Now we will show the relevant code of each step. Step1:
//Create cloth
PxClothMeshDesc meshDesc;
meshDesc.setToDefault();
 
//Fill the geometry
int w = 8, h=7;
float hw = w / 2.0f;
float hh = h / 2.0f;
d = 0.2f;
int numX = (int)(w / d) + 1;    
int numY = (int)(h / d) + 1;    
meshDesc.points.count= (numX+1) * (numY+1);        
meshDesc.triangles.count= numX*numY*2;    
meshDesc.points.stride= sizeof(PxVec3);  
meshDesc.triangles.stride= 3*sizeof(PxU32);  
meshDesc.points.data= (PxVec3*)malloc(sizeof(PxVec3)*meshDesc.points.count);    
meshDesc.triangles.data= (PxU32*)malloc(sizeof(PxU32)*meshDesc.triangles.count*3);    
meshDesc.edgeFlags = 0;

//Fill the geometry
int i,j;    
PxVec3 *p = (PxVec3*)meshDesc.points.data;   
 
pos.resize(meshDesc.points.count);
normal.resize(meshDesc.points.count);
indices.resize(meshDesc.triangles.count*3);

for (i = 0; i <= numY; i++) {        
   for (j = 0; j <= numX; j++) {            
   p -> x = d*j-hw;
   p -> y = float(h);
   p -> z = d*i;             
   p++;   
   }    
}   

memcpy(&pos[0].x, (meshDesc.points.data), sizeof(PxVec3)*meshDesc.points.count);

//Fill the topology
PxU32 *id = (PxU32*)meshDesc.triangles.data;  
for (i = 0; i < numY; i++) {        
   for (j = 0; j < numX; j++) {            
   PxU32 i0 = i * (numX+1) + j;            
   PxU32 i1 = i0 + 1;            
   PxU32 i2 = i0 + (numX+1);            
   PxU32 i3 = i2 + 1;            
   if ((j+i)%2) {                
  *id++ = i0; *id++ = i2; *id++ = i1;                
  *id++ = i1; *id++ = i2; *id++ = i3;            
   } else {                
  *id++ = i0; *id++ = i2; *id++ = i3;                
  *id++ = i0; *id++ = i3; *id++ = i1;            
   }      
   }    
}
 
memcpy(&indices[0], meshDesc.triangles.data, sizeof(PxU32)*meshDesc.triangles.count*3);

//Make sure everything is fine so far
if(!(meshDesc.isValid()))
   cerr << "Mesh invalid."<< endl;
In the above lines, we generate the cloth mesh descriptor and fill its relevant fields and then give the geometry and topology of our cloth mesh. Within these lines we also store the positions and indices for rendering the cloth later. To make sure that everything that is required has been given,we call the isValid function.
//Start cooking of fibres
PxCookingParams cp; 
PxCooking* cooking = PxCreateCooking(PX_PHYSICS_VERSION, &(gPhysicsSDK->getFoundation()), cp);
MemoryWriteBuffer buf;
bool status = cooking->cookClothFabric(meshDesc,sceneDesc.gravity, buf);
if(!status) {
   cerr << "Problem cooking mesh.\nExiting ..."<< endl;
   exit(1);
}
     
PxClothFabric* fabric=gPhysicsSDK->createClothFabric(MemoryReadBuffer(buf.data));
In the above lines, we cook the meshDesc and then create the Cloth Fabric using the cooked buffer data.
PxTransform tr;
tr.p = PxVec3(0,10,0); tr.q = PxQuat::createIdentity();

PxClothParticle* points=(PxClothParticle*)malloc(sizeof(PxClothParticle)*meshDesc.points.count);
p = (PxVec3*)meshDesc.points.data;  
for(size_t i=0;i < meshDesc.points.count;i++) {
    points[i].pos = *p;
 //Fixing the top corner points
 if(i==0 || i==numX) 
     points[i].invWeight =0;
else 
     points[i].invWeight = 1.f;
     p++;
}
In the above lines, we allocate the ClothParticles buffer filling it with positions and mass weights. To fix the top corner masses, their inverse weights are set as 0.
PxClothCollisionData cd;
cd.setToDefault();
 
cd.numSpheres=1;
cd.pairIndexBuffer=0;
 
box_collider.pos= PxVec3(0.0f,2.0f,0.0f);
box_collider.radius=1;
cd.spheres=&box_collider;

cloth = gPhysicsSDK->createCloth(tr,*fabric,points, cd, PxClothFlag::eSWEPT_CONTACT);
In the above lines, we generate the PxClothCollisionData which is used to performa collision with our box rigid body. In the new PhysX3.1 API, the cloth can only collide with capsules or spheres. We fill in the PxClothCollisionData giving it the sphere primitive's buffer. The cloth global pose, the cloth fabric, the cloth's particles and the collision data are then passed to the gPhysicsSDK::createCloth function that returns the cloth actor.
if(cloth) { 
    PxClothPhaseSolverConfig bendCfg;  
    bendCfg.solverType= PxClothPhaseSolverConfig::eFAST;
    bendCfg.stiffness = 1;
    bendCfg.stretchStiffness = 0.5; 

    cloth->setPhaseSolverConfig(PxClothFabricPhaseType::eBENDING,  bendCfg) ; 
    cloth->setPhaseSolverConfig(PxClothFabricPhaseType::eSTRETCHING, bendCfg) ; 
    cloth->setPhaseSolverConfig(PxClothFabricPhaseType::eSHEARING, bendCfg) ; 
    cloth->setPhaseSolverConfig(PxClothFabricPhaseType::eSTRETCHING_HORIZONTAL, bendCfg) ;
  
    cloth->setDampingCoefficient(0.125f);    
    gScene->addActor(*cloth); 
}
else
    cerr << "Cannot create cloth" << endl;
If we have a valid cloth object, we assign the bending, shearing and stretching stiffness for the three kinds of cloth fibers.Then we assign the cloths damping value and finally add the cloth to the scene.
Extracting the PhysX cloth particle positions for rendering:
Now that we know how the cloth may be generated, we can look into how to extract the modified positions from the PhysX cloth API. Like in the previous tutorials, the render function calls the StepPhysX(); function. For cloth, we can apply some more steps after this function call. First we set the rigid bodies collision sphere as the current clothCollisionSphere. The box_collider object is updated with the box's transform in the box's rendering function. This is done in the following code.
if (gScene) 
{ 
    StepPhysX(); 

    //update collider position based on the new position of box;  
    cloth->setCollisionSpheres(&box_collider); 
Next, we extract the current cloth particle positions using a call to PxCloth::lockClothReadData function.
//update the cloth data
   PxClothReadData* pData = cloth->lockClothReadData();
   PxClothParticle* pParticles = const_cast(pData->particles);
  
   //update the positions
   for(size_t i=0;i < pos.size();i++) {
      pos[i] = pParticles[i].pos;
      if(pos[i].y < 0) {
   pos[i].y=0;
   pParticles[i].pos.y=0;
   }
}
pData->unlock();
The above lines lock the particle positions and then apply the floor collision constraint. Next we generate the cloth normals.
   //update normals
   for(size_t i=0;i < indices.size();i+=3) {
  PxVec3 p1 = pos[indices[i]];
 PxVec3 p2 = pos[indices[i+1]];
 PxVec3 p3 = pos[indices[i+2]];
 PxVec3 n  = (p2-p1).cross(p3-p1);

 normal[indices[i]]    += n/3.0f ; 
 normal[indices[i+1]]  += n/3.0f ; 
 normal[indices[i+2]]  += n/3.0f ;    
   }

   for(size_t i=0;i < normal.size();i++) { 
 PxVec3& n  = normal[i];
 n= n.getNormalized();
   }

Cloth rendering:
For rendering, we simply pass the positions and normals to the vertex arrays and then use the indices to render the cloth as shown in the following lines of code.
void RenderCloth() {
 glEnableClientState(GL_VERTEX_ARRAY);
 glEnableClientState(GL_NORMAL_ARRAY);

 glVertexPointer(3, GL_FLOAT, sizeof(PxVec3), &(pos[0].x));
 glNormalPointer(GL_FLOAT, sizeof(PxVec3), &(normal[0].x));

 glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, &indices[0]);

 glDisableClientState(GL_NORMAL_ARRAY);
 glDisableClientState(GL_VERTEX_ARRAY);
}

That's it. You can now render the cloth in PhysX3.1 using the new cloth API. The performance of the new API is significantly better as compared to the previous PhysX sdks. Running the code gives the following output.
Cloth in PhysX 3.1
PhysX 3.1 version source code of this tutorial
PhysX 3.2.1 version source code of this tutorial

Friday, October 14, 2011

Converting the existing PhysX3.0 Tutorials to PhysX3.1

Recently, NVIDIA has released the new PhysX 3.1 sdk which has revamped the API a little bit. The support for cloth has been improved plus a lot of improvements.

In the new PhsyX 3.1 sdk, the classes have now been added into a namespace called physx so to get any object, you need to either add physx:: to it or you can add the following directive at the top of the code.

using namespace physx;

Once you add the namespace directive, my exisiting PhysX 3.0 tutorials would work in the new sdk. Thats it, the codes should run fine. In a later tutorial, I will be detail how to do cloth and soft bodies in PhysX 3.1 sdk.

You can download all of my PhysX3 tutorials ported to PhysX 3.1 in a single zip file here
Please make sure to copy the PhysX3_x86.dll into the folder containing the .vcproj file of the solution you are working on.

Popular Posts

Copyright (C) 2011 - Movania Muhammad Mobeen. Awesome Inc. theme. Powered by Blogger.