понедельник, 13 февраля 2012 г.

DirectX + PhysX (English)

This is a copy (from another blog) of lesson for English-speaking people and it`s just a copy of the same lesson on Russian language. First of all – sorry for my bad English, but as I think, it is better to read this than text on Russian or text from Google translator. I’ve wrote this translation in a cause of interest from other counties.

Its about how to setup and work with PhysX with your DirectX based application and what problems you need to solve on this way. Also I’ll give you information about how to create NxConvexMesh and NxTriangleMesh from DirectX mesh (.x) file data.

Introduction.

For this time everybody know physics engine named Nvidia PhysX (also Ageia PhysX some time ago), so Ill cut away briefing about it. One thing that you really need to understand – PhysX is a free for you! But sources of it is not. If you don’t want to change PhysX core you can use it for free. All that you need is to insert some Nvidia logos in your product if it is commercial. More about licenses. Go here to download latest PhysX sdk.

About lesson.

There are some difficults for DirectX developers because there is no examples for DirectX in sdk (there is only one and bad example with dxut). Google can help you to find some few examples but they are for Ageia PhysX, and there are some troubles for newbie. People in forums ofen answering like “Examples for DirectX and OpenGL are the same!”, but I say NO! They are wrong!)

What do you need? I am using MSVS C++ 2005 for coding, so you can try it too.

With what to start?

Try to run examples in sdk, to look how the magic makes -) Below you can see code of scene update.

 RenderCallback() {  
 ...  
 gScene->simulate(1.0f/60.0f);  
 ...  
 }  


Simply order of it looks like: making of scene object, making of physic object, translate objects to scene, then calling update in step.

Problems. Matrices.

First of all some words about VRD – Visual Remote Debugger (also there is better program PVD – PhysX Visual Debuger). With this utillites you can catch bugs in your program “on fly” and manipulate PhysX objects. It is really cool. In VRD you can find some diffuculties because it`s using RHCS – Right Hand Coordinate System, but in DX we are using LHCS, don’t forget it!

How to transform NxMat34 to D3DXMATRIX ? I`ve searched answer for a long time -) But its really simple:

 //NxMat34 a;  
 a.getColumnMajor44( d3dMat );  


And how to draw our graphic object at the right place, where PhysX object is? That’s simple too (if you know it!)):

 //NxActor* object; LPD3DXMESH* g_pObjectMesh;  
 D3DXMATRIX d3dMat;  
 taburet_object->getGlobalPose().getColumnMajor44( d3dMat );  
 g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );  
 g_pObjectMesh->DrawSubset(0);  


Problems. Meshes..

How to create mesh from model file? For newbies it is very strong problem cause many of them don’t really know how its works, many of them using DirecX meshes to forget about buffers\indexes\triangles so do I -) Thanks to Chillypacman, from Nvidia forum for help in understanding how it works and functions that are really work!) In example we will use type vector (из namespace std).

So the magic functions are:

1. Function for getting array of vertices from LPD3DXMESH


 vector<float> GetVertices(LPD3DXMESH  *g_pMesh )  
 {  
      vector<float> vertices;  
      DWORD stride = D3DXGetFVFVertexSize((*g_pMesh)->GetFVF());  
      BYTE* vbptr = NULL;  
      (*g_pMesh)->LockVertexBuffer(0, (LPVOID*)&vbptr);  
      int ii = -1;  
      for(int i = 0; i < (*g_pMesh)->GetNumVertices(); i++)  
      {  
           ii++;  
           D3DXVECTOR3* pos = (D3DXVECTOR3*)vbptr;  
           vertices.push_back(pos->x);  
           vertices.push_back(pos->y);  
           vertices.push_back(pos->z);  
           vbptr += stride;  
      }  
      (*g_pMesh)->UnlockVertexBuffer();  
      return vertices;  
 }  


2. Function for getting array of indices:


 vector<short> GetIndices(LPD3DXMESH   *g_pMesh)  
 {  
      LPVOID * ppData;  
      DWORD stride = sizeof(short);  
      BYTE* ibptr = NULL;  
      short* indices = new short[(*g_pMesh)->GetNumFaces() * 3];  
      vector<short> copy;  
      (*g_pMesh)->LockIndexBuffer(0, (LPVOID*)&indices);  
      for(int i = 0; i < (*g_pMesh)->GetNumFaces() * 3; i++)  
      {  
           copy.push_back(indices[i]);  
      }  
      (*g_pMesh)->UnlockIndexBuffer();  
      return copy;  
 }  


Before we continue lets speak about what Triangle Mesh and Convex Mesh is.

First object used only for static geometry and you cane use it for walls in your engine, but you need to know that Nvidia advice is to avoid of using it, its better to use simple objects like box everywhere you can. Second object type is simple rounded mesh with maximum of 256 vertices that can be used for both static and dynamic objects. Its can be generated automatically ore loaded from file formats like Collada.

Now we can return to magic functions:

3. Function for generation of NxTriangleMesh:

 NxActor* GenTriangleMesh(NxVec3 pos, vector<short> indices, vector<float> vertices )  
 {  
      int NumVerticies = vertices.size() / 3;  
      int NumTriangles = indices.size() / 3;  
      //Create pointer for vertices  
      NxVec3* verts = new NxVec3[NumVerticies];  
      int ii = -1;  
      for(int i = 0; i < NumVerticies; i++)  
      {  
           ++ii;  
           verts[i].x = vertices[ii];  
           verts[i].y = vertices[++ii];  
           verts[i].z = vertices[++ii];  
      }  
      //Create pointer for indices  
      NxU16 *tris = new NxU16[indices.size()];  
      for(int i = indices.size() - 1; i >= 0; i--)  
      tris[i] = i;  
      // Build physical model  
      NxTriangleMeshDesc TriMeshDesc;  
      TriMeshDesc.numVertices = NumVerticies;  
      TriMeshDesc.numTriangles = NumTriangles;  
      TriMeshDesc.pointStrideBytes = sizeof(NxVec3);  
      TriMeshDesc.triangleStrideBytes = 3*sizeof(NxU16);  
      TriMeshDesc.points = verts;  
      TriMeshDesc.triangles = tris;  
      TriMeshDesc.flags = NX_MF_16_BIT_INDICES ;//| NX_MF_FLIPNORMALS ;  
      NxTriangleMeshShapeDesc ShapeDesc;  
      NxInitCooking();  
      // Cooking from memory  
      MemoryWriteBuffer buf;  
      bool status = NxCookTriangleMesh(TriMeshDesc, buf);  
      ShapeDesc.meshData = g_pPhysicsSDK->createTriangleMesh(MemoryReadBuffer(buf.data));  
      NxActorDesc actorDesc;  
      actorDesc.shapes.pushBack(&ShapeDesc);  
      actorDesc.globalPose.t = pos;  
      NxActor* act = g_pScene->createActor(actorDesc);  
      delete[] verts;  
      delete[] tris;  
      return act;  
 }  


4. Function for generation of NxConvexMesh:


 NxActor* GenConvexMesh(const NxVec3& pos, vector<float> vertices, const NxReal density)  
 {  
      int i = 0;  
      NxActorDesc actorDesc;  
      NxBodyDesc bodyDesc;  
      int numVertices = vertices.size() / 3;  
      NxVec3* verts = new NxVec3[numVertices];  
      int ii = -1;  
      for(int i = 0; i < numVertices; i++)  
      {  
           ++ii;  
           verts[i].x = vertices[ii];  
           verts[i].y = vertices[++ii];  
           verts[i].z = vertices[++ii];  
      }  
      // Create descriptor for convex mesh  
      NxConvexMeshDesc convexDesc;  
      convexDesc.numVertices     = numVertices;  
      convexDesc.pointStrideBytes  = sizeof(NxVec3);  
      convexDesc.points          = verts;  
      convexDesc.flags           = NX_CF_COMPUTE_CONVEX;  
      NxActorDesc actorDesc;  
      NxInitCooking();  
      MemoryWriteBuffer buf;  
      bool status = NxCookConvexMesh(convexDesc, buf);  
      NxConvexShapeDesc* convexShapeDesc  =  new NxConvexShapeDesc();  
      convexShapeDesc->localPose.t     =  NxVec3(0,0,0);  
      convexShapeDesc->materialIndex    =  0;  
      convexShapeDesc->meshData = g_pPhysicsSDK->createConvexMesh(MemoryReadBuffer(buf.data));  
      actorDesc.shapes.pushBack(convexShapeDesc);  
      NxBodyDesc bodyDesc;  
      if(!stat)  
      {  
           actorDesc.body = &bodyDesc;  
           actorDesc.density = 1;  
      }  
      else  
           actorDesc.body = NULL;  
      actorDesc.globalPose.t = NxVec3(0, 0, 0);  
      actorDesc.name = "boo";  
      NxActor* actor = g_pScene->createActor(actorDesc);  
      return actor;  
 }  


Looks like this functions are the most common searched functions.

Problems with Cooking.h and Stream.h

Those headers are using in PhysX examples, but they are in different versions, and there are same problems with them, so I am giving you header (CookingStream.h) modified by my friend ASD (asddev.3dn.ru), his file is a mix of those two files and works pretty good. You can find it in archive to lesson.

Additional

How to use VRD (or PVD) in your program? Its simple indeed, copy this code in header where your PhysX initializing:

 #define SAMPLES_VRD_HOST "localhost"  
 #define SAMPLES_VRD_PORT NX_DBG_DEFAULT_PORT  
 #define SAMPLES_VRD_EVENTMASK NX_DBG_EVENTMASK_EVERYTHING  
 int  gAppData;  
 void vrd_init()  
 {  
  NxRemoteDebugger* pRemDeb = g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger();  
  pRemDeb->connect(SAMPLES_VRD_HOST,  SAMPLES_VRD_PORT,  SAMPLES_VRD_EVENTMASK);  
  if (pRemDeb->isConnected())  
  {  
    pRemDeb->createObject(&gAppData, NX_DBG_OBJECTTYPE_GENERIC,"AppData",  NX_DBG_EVENTMASK_EVERYTHING);  
    pRemDeb->writeParameter("Info text",  &gAppData,  true,"Info",   NX_DBG_EVENTMASK_EVERYTHING);  
    pRemDeb->createObject(&gAppData+1,  NX_DBG_OBJECTTYPE_VECTOR,"AVector",  NX_DBG_EVENTMASK_EVERYTHING);  
    pRemDeb->writeParameter(NxVec3(0, 0, 0), &gAppData+1,  true,"Origin", NX_DBG_EVENTMASK_EVERYTHING);  
    pRemDeb->writeParameter(NxVec3(1, 1, 1), &gAppData+1, true,"Extent", NX_DBG_EVENTMASK_EVERYTHING);  
    pRemDeb->addChild(&gAppData, &gAppData+1, NX_DBG_EVENTMASK_EVERYTHING);  
    // Tells our application that we have created the VRD object  
    // and that it is ok to set information in it  
    gAppData = 1;   
  }  
 }  


After PhysX initialization call vrd_init();. Now if you ran VRD before your program starts, your program will automatically be connected to VRD.

What is in the archive?

My sample based on example for Ageia NovodeX (first name of PhysX) from CodeSampler.com, there are few things that you need to change in their lesson:

1. from project properties you must remove Physics.lib. Also you need to copy PhysXLoader.lib,nxcooking.lib,NxCharacter.lib to project folder and too connect library’s use:

 #pragma comment(lib, "physxloader.lib")  
 #pragma comment(lib, "nxcooking.lib")  
 #pragma comment(lib, "NxCharacter.lib")  


2. In compilation you may see error in function initNovodeX() where you need to remove those lines:

sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;


So everything will run now!

P.S.

So there is a point where my lesson ends -) Enjoy it!
Thanks to ASD and Chillypacman for their help again.
Thanks to Nvidia for free PhysX!
And… sorry for my bad English, again)

Wow, and archive!! -> dx9_novodex_simple_derivedbycrol

Комментариев нет:

Отправить комментарий