PhysX3 学习笔记2.5

注意:本文标注为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);

它定义一个视图矩阵,并与当前矩阵相乘。

其中

第一组eyex, eyey,eyez   相机在世界坐标的位置

第二组centerx,centery,centerz 相机镜头对准的物体在世界坐标的位置

第三组upx,upy,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的研究。

时间: 2024-10-29 10:46:37

PhysX3 学习笔记2.5的相关文章

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu

jQuery学习笔记(一):入门

jQuery学习笔记(一):入门 一.JQuery是什么 JQuery是什么?始终是萦绕在我心中的一个问题: 借鉴网上同学们的总结,可以从以下几个方面观察. 不使用JQuery时获取DOM文本的操作如下: 1 document.getElementById('info').value = 'Hello World!'; 使用JQuery时获取DOM文本操作如下: 1 $('#info').val('Hello World!'); 嗯,可以看出,使用JQuery的优势之一是可以使代码更加简练,使开

[原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

Activiti 学习笔记记录(三)

上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件

HTML&CSS基础学习笔记8-预格式文本

<pre>标签的主要作用是预格式化文本.被包围在 pre 标签中的文本通常会保留空格和换行符.而文本也会呈现为等宽字体. <pre>标签的一个常见应用就是用来表示计算机的源代码.当然你也可以在你需要在网页中预显示格式时使用它. 会使你的文本换行的标签(例如<h>.<p>)绝不能包含在 <pre> 所定义的块里.尽管有些浏览器会把段落结束标签解释为简单地换行,但是这种行为在所有浏览器上并不都是一样的. 更多学习内容,就在码芽网http://www.

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

[原创]java WEB学习笔记48:其他的Servlet 监听器:域对象中属性的变更的事件监听器 (3 个),感知 Session 绑定的事件监听器(2个)

本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 ---------------------------------

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过