PhysX之旅(一)
-- NxBoxes的逐行分析
学习新东西免不了走弯路,这个时候能得到指引的话,实在是很幸运的事情。所以我想说:能有SDK真是太好了!
NxBoxes是PhysX的第1个例子,对它进行逐行分析是很有必要的。当我们完完全全了解每一行代码有什么用的时候,我们也就入门了。
#include <stdio.h> #include <GL/glut.h>
#include "NxPhysics.h" #pragma comment(lib, "C:/Program Files/NVIDIA Corporation/NVIDIA PhysX SDK/v2.8.1/SDKs/lib/Win32/PhysXLoader.lib") #pragma comment(lib, "E:/work/test_bin/extern_library/lib/debug/freeglut.lib")
#define NOMINMAX #include <windows.h>
//#include "ErrorStream.h" #include <windows.h> //#include "PerfRenderer.h" //#include "Utilities.h" //#include "SamplesVRDSettings.h"
// Physics static NxPhysicsSDK* gPhysicsSDK = NULL; // PhysX接口指针 static NxScene* gScene = NULL; // 场景指针 //static PerfRenderer gPerfRenderer; /* 渲染前统计用的类 该类只有2个对外方法: bool toggleEnable(); // 显示/隐藏切换 void render(const NxProfileData* prof, int width, int height);
关于NxProfileData: /brief Array of profiling data. 性能数据数组。 profileZones points to an array of numZones profile zones. Zones are sorted such that the parent zones always come before their children. Some zones have multiple parents (code called from multiple places) in which case only the relationship to the first parent is displayed. returned by NxScene::readProfileData().
关于ProfileZones /brief Names for a number of profile zones that should always be available. Can be used with NxProfileData::getNamedZone()
<b>Platform:</b> /li PC SW: Yes /li PPU : Yes /li PS3 : No /li XB360: No enum NxProfileZoneName { NX_PZ_CLIENT_FRAME, //!< Clock start is in client thread, just before scene thread was kicked off; clock end is when client calls fetchResults(). NX_PZ_CPU_SIMULATE, //!< Maximum time of simulation thread ((Nf)Scene::simulate(NxU32)), over all CPU scenes inside the NxScene. NX_PZ_PPU0_SIMULATE, //!< Maximum time of simulation thread over all PPU# scenes inside the NxScene. NX_PZ_PPU1_SIMULATE, NX_PZ_PPU2_SIMULATE, NX_PZ_PPU3_SIMULATE, NX_PZ_TOTAL_SIMULATION = 0x10, //!< Clock start is in client thread, just before scene thread was kicked off; clock end is in simulation thread when it finishes. }; */ // Rendering static NxVec3 gEye(50.0f, 50.0f, 50.0f); // 摄象机位置 static NxVec3 gDir(-0.6f,-0.2f,-0.7f); // 摄象机方向 static NxVec3 gViewY; // 摄象机“左平移”的方向 static int gMouseX = 0; // 鼠标当前X坐标(abs) static int gMouseY = 0; // 鼠标当前Y坐标(abs)
static bool InitNx() // 该函数负责初始化PhysX { // Initialize PhysicsSDK NxPhysicsSDKDesc desc; // 设备描述符,主要用来指定硬件和场景之间“对数据的共享程度”? NxSDKCreateError errorCode = NXCE_NO_ERROR; // 调用NxCreatePhysicsSDK()返回的错误信息 /* enum NxSDKCreateError { NXCE_NO_ERROR = 0, // 没错误 NXCE_PHYSX_NOT_FOUND = 1, // 找不到PhysX库,未安装PhysX驱动 NXCE_WRONG_VERSION = 2, // SDK版本和Driver版本不匹配 NXCE_DESCRIPTOR_INVALID = 3, // 无效的描述符 NXCE_CONNECTION_ERROR = 4, // 与设备通信出现故障。 NXCE_RESET_ERROR = 5, // 未重置(初始化)的设备 NXCE_IN_USE_ERROR = 6, // 设备正在被其他程序使用 NXCE_BUNDLE_ERROR = 7 // 恭喜,你的物理加速卡坏了 }; */ gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL, /*new ErrorStream()*/NULL, desc, &errorCode); // 创建接口指针。 if(gPhysicsSDK == NULL) // 如果出错了,打印出错误信息 { //printf("/nSDK create error (%d - %s)./nUnable to initialize the PhysX SDK, exiting the sample./n/n", errorCode, getNxSDKCreateError(errorCode)); return false; } // 可视化远程调试器的设置...从来没玩过... //#if SAMPLES_USE_VRD // // 设置Visual Remote Debugger的IP和port。(在SampleCommonCode/SamplesVRDSettings.h文件中定义) // if (gPhysicsSDK->getFoundationSDK().getRemoteDebugger()) // gPhysicsSDK->getFoundationSDK().getRemoteDebugger()->connect(SAMPLES_VRD_HOST, SAMPLES_VRD_PORT, SAMPLES_VRD_EVENTMASK); //#endif
// 设置变量,皮肤厚度为0.05,实际结果就是角色的碰撞几何体被缩小了0.05。 gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.05f);
// Create a scene NxSceneDesc sceneDesc; // 声明一个场景描述符 sceneDesc.gravity = NxVec3(0.0f, -9.81f, 0.0f); // 设置重力加速度 gScene = gPhysicsSDK->createScene(sceneDesc); // 使用刚才的场景描述符来创建场景 if(gScene == NULL) // 错误处理 { printf("/nError: Unable to create a PhysX scene, exiting the sample./n/n"); return false; }
// Set default material // 设置材质 // 总是存在一个默认材质(index = 0),该材质也不能被释放,调用releaseMaterial(defaultMaterial)将没有任何效果 NxMaterial* defaultMaterial = gScene->getMaterialFromIndex(0); defaultMaterial->setRestitution(0.0f); // 设置还原系数 defaultMaterial->setStaticFriction(0.5f); // 设置静摩擦系数 defaultMaterial->setDynamicFriction(0.5f); // 设置动摩擦系数
// Create ground plane // 创建一个片,作为地板 NxPlaneShapeDesc planeDesc; // 很明显,片描述符 NxActorDesc actorDesc; // 角色描述符 actorDesc.shapes.pushBack(&planeDesc); // 将片描述符添加到角色描述符中 gScene->createActor(actorDesc); // 创建角色
return true; }
// 该函数做清理工作 static void ExitNx() { if(gPhysicsSDK != NULL) { if(gScene != NULL) gPhysicsSDK->releaseScene(*gScene); // 释放场景 gScene = NULL; NxReleasePhysicsSDK(gPhysicsSDK); // 释放PhysX gPhysicsSDK = NULL; } }
// 创建一个cube static void CreateCube(const NxVec3& pos, int size=2, const NxVec3* initialVelocity=NULL) { if(gScene == NULL) return;
// Create body // 创建刚体 NxBodyDesc bodyDesc; // 刚体描述符 bodyDesc.angularDamping = 0.5f; // 旋转衰减 if(initialVelocity) bodyDesc.linearVelocity = *initialVelocity; // (沿着某个方向的)速度
NxBoxShapeDesc boxDesc; // 盒子描述符 boxDesc.dimensions = NxVec3((float)size, (float)size, (float)size); // 设置盒子的体积
NxActorDesc actorDesc; // 角色描述符 actorDesc.shapes.pushBack(&boxDesc); // 将盒子描述符添加到角色描述符中 actorDesc.body = &bodyDesc; // 设置刚体描述符 actorDesc.density = 10.0f; // 设置密度 actorDesc.globalPose.t = pos; // 设置物体当前坐标(世界坐标系) gScene->createActor(actorDesc)->userData = (void*)size_t(size); // 创建角色 //printf("Total: %d actors/n", gScene->getNbActors()); }
// 创建大方块 static void CreateCubeFromEye(int size) { NxVec3 t = gEye; // 点 NxVec3 vel = gDir; // 方向 vel.normalize(); // 归一化 vel*=200.0f; // 该方向上的速度(200个单位向量) CreateCube(t, size, &vel); // 创建方块 }
// 创建金字塔 static void CreateStack(int size) { const float cubeSize = 2.0f; const float spacing = -2.0f*gPhysicsSDK->getParameter(NX_SKIN_WIDTH); // 缩放产生的尺寸减少的值。 // 只要知道“皮肤厚度”代表的意义之后,以下的代码不过是为了让这些方块紧密排列的算法而已。 NxVec3 pos(0.0f, cubeSize, 0.0f); float offset = -size * (cubeSize * 2.0f + spacing) * 0.5f; while(size) { for(int i=0;i<size;i++) { pos.x = offset + (float)i * (cubeSize * 2.0f + spacing); CreateCube(pos, (int)cubeSize); }
offset += cubeSize; pos.y += (cubeSize * 2.0f + spacing); size--; } }
// 创建棍子 static void CreateTower(int size) { const float cubeSize = 2.0f; const float spacing = 0.01f; NxVec3 pos(0.0f, cubeSize, 0.0f); while(size) { CreateCube(pos, (int)cubeSize); pos.y += (cubeSize * 2.0f + spacing); size--; } }
// 键盘回调函数 static void KeyboardCallback(unsigned char key, int x, int y) { switch(key) { case 27: // ESC的ASCII码为27 exit(0); break; case '0': //gPerfRenderer.toggleEnable(); break; case ' ': CreateCube(NxVec3(0.0f, 20.0f, 0.0f),1); // 在 (0,20,0)的位置创建 大小为1,速度为0的方块 break; case 's': case 'S': CreateStack(10); break; case 'b': case 'B': CreateStack(30); break; case 't': case 'T': CreateTower(30); break; case 'w': case 'W': CreateCubeFromEye(8); break; case 'q': case 'Q': { NxActor** actors = gScene->getActors(); if(gScene->getNbActors() > 1){ gScene->releaseActor(*actors[gScene->getNbActors()-1]); } } break; case GLUT_KEY_UP: case '8': gEye += gDir*2.0f; break; case GLUT_KEY_DOWN: case '2': gEye -= gDir*2.0f; break; case GLUT_KEY_LEFT: case '4': gEye -= gViewY*2.0f; break; case GLUT_KEY_RIGHT: case '6': gEye += gViewY*2.0f; break; } }
// 键盘回调函数 static void ArrowKeyCallback(int key, int x, int y) { KeyboardCallback(key,x,y); }
// 鼠标回调函数 static void MouseCallback(int button, int state, int x, int y) { gMouseX = x; gMouseY = y; }
// 运动回调函数? static void MotionCallback(int x, int y) { int dx = gMouseX - x; int dy = gMouseY - y; gDir.normalize(); gViewY.cross(gDir, NxVec3(0,1,0));
NxQuat qx(NxPiF32 * dx * 20/ 180.0f, NxVec3(0,1,0)); qx.rotate(gDir); NxQuat qy(NxPiF32 * dy * 20/ 180.0f, gViewY); qy.rotate(gDir);
gMouseX = x; gMouseY = y; }
// 渲染回调函数 // 当把上一贞的结果提交到渲染队列进行渲染的时候,CPU就空闲了,这个时候再 static void RenderCallback() { static DWORD elapsedTime = 0;
if(gScene == NULL) return; // Start simulation (non blocking) if( elapsedTime!= 0 ) { elapsedTime = ::GetTickCount() - elapsedTime; } gScene->simulate( elapsedTime *0.001f ); // 进行模拟,所有的物理相关的计算都由该函数处理了。
#pragma region OpenGL相关 // Clear buffers glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Setup projection matrix glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0f, (float)glutGet(GLUT_WINDOW_WIDTH)/(float)glutGet(GLUT_WINDOW_HEIGHT), 1.0f, 10000.0f); gluLookAt(gEye.x, gEye.y, gEye.z, gEye.x + gDir.x, gEye.y + gDir.y, gEye.z + gDir.z, 0.0f, 1.0f, 0.0f);
// Setup modelview matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity();
// Render all actors int nbActors = gScene->getNbActors(); // 获得角色总数 NxActor** actors = gScene->getActors(); // 获得角色队列指针 while(nbActors--) // 不渲染地板,因为可以从代码的编写上来保证第1个一定是地板,所以这里不做检测 { NxActor* actor = *actors++; // 做迭代 if(!actor->userData) continue; // 如果角色的“用户自定义数据”指针非空,也不渲染它
// 渲染角色 // Render actor glPushMatrix(); float glMat[16]; actor->getGlobalPose().getColumnMajor44(glMat); glMultMatrixf(glMat); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // 设置颜色 glutSolidCube(float(size_t(actor->userData))*2.0f); // 进行渲染 glPopMatrix();
// Render shadow glPushMatrix(); const static float shadowMat[]={ 1,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,1 }; glMultMatrixf(shadowMat); glMultMatrixf(glMat); glDisable(GL_LIGHTING); glColor4f(0.1f, 0.2f, 0.3f, 1.0f); glutSolidCube(float(size_t(actor->userData))*2.0f); glEnable(GL_LIGHTING); glPopMatrix(); } #pragma endregion OpenGL相关
// Fetch simulation results gScene->flushStream(); gScene->fetchResults(NX_RIGID_BODY_FINISHED, true); elapsedTime = ::GetTickCount(); // Print profile results (if enabled) //gPerfRenderer.render(gScene->readProfileData(true), glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
glutSwapBuffers(); }
static void ReshapeCallback(int width, int height) { glViewport(0, 0, width, height); }
static void IdleCallback() { glutPostRedisplay(); }
int main(int argc, char** argv) { // Initialize glut printf("Use the arrow keys or 2, 4, 6 and 8 to move the camera./n"); printf("Use the mouse to rotate the camera./n"); printf("Press the keys w, t, s, b and space to create various things./n"); printf("Press q to destroy the last thing created./n"); printf("Press 0 to toggle the frame rate display./n"); glutInit(&argc, argv); glutInitWindowSize(512, 512); glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH); int mainHandle = glutCreateWindow("SampleBoxes"); glutSetWindow(mainHandle); glutDisplayFunc(RenderCallback); glutReshapeFunc(ReshapeCallback); glutIdleFunc(IdleCallback); glutKeyboardFunc(KeyboardCallback); glutSpecialFunc(ArrowKeyCallback); glutMouseFunc(MouseCallback); glutMotionFunc(MotionCallback); MotionCallback(0,0); atexit(ExitNx);
// Setup default render states glClearColor(0.3f, 0.4f, 0.5f, 1.0); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL);
// Setup lighting glEnable(GL_LIGHTING); float ambientColor[] = { 0.0f, 0.1f, 0.2f, 0.0f }; float diffuseColor[] = { 1.0f, 1.0f, 1.0f, 0.0f }; float specularColor[] = { 0.0f, 0.0f, 0.0f, 0.0f }; float position[] = { 100.0f, 100.0f, 400.0f, 1.0f }; glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor); glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor); glLightfv(GL_LIGHT0, GL_POSITION, position); glEnable(GL_LIGHT0);
// Initialize physics scene and start the application main loop if scene was created if (InitNx()) glutMainLoop();
return 0; }