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;
}