注意:本文標註爲2.5的原因是因爲主要涉及OpenGL概念複習,並沒有完全涉及到PhysX物理模擬
上回說到,用PhysX搭建了一個基本的空框架。
今天要說的主要內容是,PhysX和OpenGL中的座標轉換。聲明一下有時候我會創建一些之後要用的變量,有可能一開始會看不懂,但堅持下去
最後你會明白的。
最後的結果將是屏幕中出現一個PhysX座標系的座標軸。在這過程中我們將會使用到Plane:
Plane將空間分爲上下兩部分,所有在Plane之上的物體都會和他發生碰撞。
1,創建我們的可視空間
添加一個新的類庫
#include <foundation\Mat33.h>
這個類庫中有一個函數,是用來將變換矩陣從PhysX中轉換到OpenGl的。因爲OpenGL和PhysX的世界座標不太相同
左側的OpenGL座標系爲笛卡爾座標系(從左向右x正,從內向外z正,從下向上y正)
右側PhysX中x和y的正軸方向和OpenGL相反。
關於這個函數的具體實現,我們下一篇文章中會提及。
創建可見的牆體
我們將使用Plane來創建上述物體。
我們先修改一下我們的全局變量:
#define MAX_PATH 16384
char buffer [MAX_PATH]
int start_time = 0,total_frames = 0,state = 1,oldX =0,oldY =0;
float fps =0,rX =0,rY =50,dist =0;
const int WINDOW_WIDTH = 800,WINDOW_HEIGHT =600,OBJ_NUM = 130;
const float Gravity = -9.8;
typedef GLfloat point3[3];
point3 planeVertice[4] = {{-10,0,10},{-10,0,-10},{10,0,-10},{10,0,10}};
</pre>接下來,我們要使用這些函數來繪製一個網格(作爲地面)<p></p><p></p><pre name="code" class="cpp">void drawGrid(int n)
{
glBegin(GL_LINES);
glColor3f(0.7f,0.7f,0.7f);
for(int i = -n;i<=n;i++)
{
glVertex3f((float)i,0,(float)-n);
glVertex3f((float)i,0,(float)n);
glVertex3f((float)-n,0,(float)i);
glVertex3f((float)n,0,(float)i);
}
glEnd();
}
很容易理解,畫了一個20x20的網格地面
有了地面不妨再畫個牆:
void drawPlane()
{
glBegin(GL_POLYGON);
glNormal3f(0,1,0);
glVertex3fv(planevertice[0]);
glVertex3fv(planevertice[1]);
glVertex3fv(planevertice[2]);
glVertex3fv(planevertice[3]);
glEnd();
}
2,調整視角
用我們的新函數來完善一下我們的onRender函數
void onRender()
{
GLdouble viewer[3] = {20*sin(0.0),20,20*cos(0.0)};
total_frames ++;
int current = glutGet(GLUT_ELAPSED_TIME);
if((current-start_time)>1000)
{
float elapsed_time = float(current - start_time);
fps = ((total_frames*1000.0f)/elapsed_time);
start_time=current;
total_frames =0;
}
if(gloable_scene)
StepPx();
glClearColor(0.1,0.1,0.1,1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0,0,dist);
gluLookAt(viewer[0],viewer[1],viewer[2],0,0,0,0,1,0);
glRotatef(rX,1,0,0);
glRotatef(rY,0,1,0);
drawGrid(10);
glPushMatrix();
glTranslatef(0,10,10);
glRotatef(90,1,0,0);
drawPlane()
glPopMatrix();
glPushMatrix();
glTranslatef(0,10,-10);
glRotatef(-90,1,0,0);
drawPlane()
glPopMatrix();
glPushMatrix();
glTranslatef(0,10,0);
glRotatef(90,0,1,0);
glRotatef(-90,1,0,0);
drawPlane()
glPopMatrix();
glutSwapBuffers();
}
在onRender中,我們使用了glulookAt函數進行了視角轉換
該函數原型爲
void gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
它定義一個視圖矩陣,並與當前矩陣相乘。
其中
在繪製OpenGL圖形的時候我們又用到了glTransform,和glRotate,前者比較好理解,讓當前的座標以(x,y,z)向量移動。
後者void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)可以理解爲繞着向量(x,y,z)旋轉angle值
實際上就是使當前矩陣乘上下面的變換矩陣
其中,c = cos(angle),s = sin(angle),並且||(x, y, z)|| = 1
如果不太明白,我們已繪製第一個Plane爲例。
translate之後座標變爲(x,y+10,z+10);
rotate矩陣爲(隨手寫的,懶得用電腦繪製..
這樣大概就能明白了吧。同時,因爲Plane的繪製函數告訴我們,Y正軸方向就是plane的上面,也就是會產生碰撞的一面。
鼠標控制器
每次調整視角都要改代碼多麻煩,我們乾脆新建一個鼠標控制視角變換的函數。
void onReshape(int nw,int nh)
{
glViewport(0,0,nw,nh);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50,nw/nh,0.1f,1000.0f);
glMatrixMode(GL_MODELVIEW);
}
void Mouse(int button,int s,int x,int y)
{
if(s == GLUT_DOWN)
{
oldX =x;
oldY =y;
}
if(button == GLUT_RIGHT_BUTTON)
state = 1;
else
state = 0;
}
void Motion(int x, int y)
{
if(state ==1 )
{
rX += (x-oldX)/5.0f;
rY += (y-oldY)/5.0f;
}
oldY =y;
oldX =x;
glutPostRedisplay();
}
3,修正函數
爲了讓上述的新功能生效,我們需要修改一下main函數
<pre name="code" class="cpp">void main(int argc,char**argv)
{
atexit(onShutdown);
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutInitWindowSize(WINDOW_WIDTH,WINDOW_HEIGHT);
glutCreateWindow("PxTest");
glutDisplayFunc(onRender);
glutIdleFunc(onIdle);
glutReshapeFunc(onReshape);
glutMouseFunc(Mouse);
glutMotionFunc(Motion);
InitPx();
glEnable(GL_DEPTH_TEST);
glDepthMask(GLU_TRUE);
glutMainLoop();
}
同時給我們的InitPx添加少許的容錯機制、同時吧gravity的參數改爲全局變量
void InitPx()
{
PxFoundation *foundation = PxCreateFoundation(PX_PHYSICS_VERSION,gloable_allocator,gloable_errorcallback);
if(!foundation)
cerr<<"PxCreateFoundation failed!"<<endl;
PxDirector = PxCreatePhysics(PX_PHYSICS_VERSION, *foundation,PxTolerancesScale() );
if(PxDirector == NULL)
{
cerr<<"Error creating PhysX3 device."<<endl;
cerr<<"Exiting..."<<endl;
exit(1);
}
PxInitExtensions(*PxDirector);
if(!PxInitExtensions(*PxDirector))
cerr<<"PxInitExtensions failed!"<<endl;
//創建場景
PxSceneDesc scene_desc(PxDirector->getTolerancesScale());
scene_desc.gravity= PxVec3(0.0f,Gravity,0.0f);
if(!scene_desc.cpuDispatcher)//調度員不存在
{
PxDefaultCpuDispatcher *cpu_dispatcher = PxDefaultCpuDispatcherCreate(1);
scene_desc.cpuDispatcher = cpu_dispatcher;
}
if(!scene_desc.filterShader)
scene_desc.filterShader = gloable_filtershader;
gloable_scene = PxDirector->createScene(scene_desc);
if(!gloable_scene)
cerr<<"createScene failed!"<<endl
}
這樣修正完全之後,我們就可以運行了,不出意外的話,你應該看到下面的工字形牆體
4,添加PhysX座標
是不是感覺很好玩?接下來我們要做的事情就是在這個世界中添加座標軸(由於PhysX的座標和OpenGL不一樣,所以有直觀的可視化座標有利於理解)
首先,爲了給座標騰出空間,我們在onRender函數中修改繪製第三個plane的代碼如下
glPushMatrix();
glTranslatef(0,10,0);
glRotatef(90,0,1,0);
glRotatef(-90,1,0,0);
glTranslatef(0,10,0);//new code
drawPlane();
glPopMatrix();
新建繪製座標的函數
void drawAxe()
{
glPushMatrix();
glColor3f(0,0,1);
glPushMatrix();
glTranslatef(0,0,0.8f);
glutSolidCone(0.0325,0.2, 4,1);
glPopMatrix();
glutSolidCone(0.0225,1, 4,1);
//Z軸
glColor3f(1,0,0);
glRotatef(90,0,1,0);
glPushMatrix();
glTranslatef(0,0,0.8f);
glutSolidCone(0.0325,0.2, 4,1);
glPopMatrix();
glutSolidCone(0.0225,1, 4,1);
//X軸
glColor3f(0,1,0);
glRotatef(90,-1,0,0);
glPushMatrix();
glTranslatef(0,0, 0.8f);
glutSolidCone(0.0325,0.2, 4,1);
glPopMatrix();
glutSolidCone(0.0225,1, 4,1);
//Y軸
glPopMatrix();
}
在onRender中調用,就可顯示三個座標了。
其中用到了glutSolidCone函數。讀者有可能對我標註的ZXY軸感到困惑,沒關係,看了函數原型就能理解
該函數原型爲
GLsolidCone(radius,height,slices,stacks);
生成一個底座在xy平面,沿着Z軸生長的圓錐。後兩個參數分別爲繞z軸的切割數和沿着Z軸的切割數。
考慮到PhysX座標並不是笛卡爾座標系,所以分別生成了ZXY軸。
當然,並不是所有人的空間想象能力都那麼好,有時候筆者自己都會搞糊塗(爲了寫這個教程,筆者花了整個週日複習了工程線性代數....)所以我們乾脆直接將XYZ標註在座標旁邊。
我們需要新建一個函數用於顯示文字
void RenderSpacingString(int x,int y,int spacing,void *font,char *string)
{
char *c;
int x1 =x;
for(c =string;*c !='\0';c++)
{
glRasterPos2i(x1,y);
glutBitmapCharacter(font ,*c);
x1 = x1+glutBitmapWidth(font, *c) +spacing;
}
}
當然這個只能設置平面文字,當做GUI text使用(讀者可以再onRender中嘗試一下),想要這些文字在三維空間中顯示,還需要設置正交和透視
void SetOrthoForFont()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT);
glScalef(1, -1, 1);
glTranslatef(0, -WINDOW_HEIGHT, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void ResetPerspectiveProjection()
{
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
}
修改drawAxe函數,我們以Z軸爲例
void drawAxe()
{
glPushMatrix();
glColor3f(0,0,1);
glPushMatrix();
glTranslatef(0,0,0.8f);
glutSolidCone(0.0325,0.2, 4,1);
glTranslatef(0,0.0625,0.225f);
RenderSpacingString(0,0,0,GLUT_BITMAP_HELVETICA_10, "Z");
glPopMatrix();
glutSolidCone(0.0225,1, 4,1);
}
最後不要忘了在onRender中,調用這兩個函數
drawAxe();
SetOrthoForFont();
ResetPerspectiveProjection();
//在此之前添加
glutSwapBuffers();
運行後得到了下圖
好嘞,今天的教程就到這裏結束了。下次我們將學習如何在場景中添加奇奇怪怪的Actors。
我是妖哲,微博@捲毛的呈秀波,咱們下期再見。
感謝http://blog.csdn.net/wangyuchun_799/article/details/7786031 關於GL的研究。