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

11 comments:

Edgar said...

Hi Mobeen, did you get a soft body works in Physx 3.0 or 3.1 with a deformable classes? Thanks

Mobeen said...

Hi Edgar,
Yeah it works fine for me I did not have time to write out the tutorial on it but it works exactly as in the previous versions.

enigma said...

Hi Mobeen, great tutorial, it was very useful for me to start with cloth.
I've just a question about PhysX Visual Debugger: I've seen taht the cloth isn't visible in the render window of the debbugger, there is only the cube. There is a solution for that? Thanks.

Mobeen said...

Hi Enigma,
AFAIK, the current Physx 3.1 API does not provide debug visualization of cloth. That is exactly the reason why there is no tutorial/sample on cloth in the API.

enigma said...

Hi Mobeen,
I've modified the sample in order to have a sort of net under the sphere (simply putting to 0 the weight of the other two corner of the mesh).
But the sphere passes through the net and falls on the ground.
Is right this behavior? There isn't the possibility of maintain the sphere over the net and have the sphere that reacts based on the net movement?
I don't understand if is a limit of the new PhysX SDK.
Thanks.

Mobeen said...

Hi enigma,
I have not tried this but may be you can either
1) set the mass of the sphere to a very small value and see whether it stick on the net or
2) set the stiffness of the cloth to a higher value.

I tried to play with these but I was also not able to make the sphere stick on the cloth, may be u can try asking on the NVIDIA PhysX forums and let me know if u find a solution to this.

Abhinav Sharma said...

Hello Mobeen, Thank you for your tutorial. I modified your tutorial a bit so as to make a bullet (sphere) go through the cloth. It works fine but the bullet leaves no hole. Do you know how I could simulate the hole as the bullet goes through it?
Thanks.

Muhammad Mobeen Movania said...

Hi Abhinav,
As far as the PhysX3.2 is concerned, it does not support tearing hence I dont think API supports this. You can instead find the candidate triangles/edges/vertices and simply delete them. You will need to cast a ray from the camera to the scene and get the nearest triangle. Then remove that triangle.

Hope this helps,
Mobeen

连志勇 said...

Hi Mobeen, I try to download the source code of "cloth in PhysX3.1", but it is not successful. The reason is the system cannot find the path specified, could you post the source code again?

Muhammad Mobeen Movania said...

Hi,
I think my university account has been disabled.
PhysX 3.2.1 version is here: https://dl.dropbox.com/u/5513476/PhysX_Stuff/Cloth%28PhysX_3.2.1%29.zip
PhysX 3.1 version is here:
https://dl.dropbox.com/u/5513476/PhysX_Stuff/SimpleCloth%28PhysX3.1%29.zip

See if this helps.
Regards,
Mobeen

AMJAD N said...

Hi,
Thanks for the detailed tutorials.

It will be of huge help if you can share the code you created. Current DropBox links are not working.

Thanks in advance.

Amjad

Popular Posts

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