-- HOME -- CONTROL -- ABOUT --

2009年3月18日星期三

0

radio的用法

先为对话框加上2个radio button,分别是Radio1和Radio2。

问题1:如何让Radio1或者Radio2默认选上?如何知道哪个被选上了?

关键是选上,“默认”只要放在OnInitDialog()即可。三种方法可以让它选上,
第一种:
((CButton *)GetDlgItem(IDC_RADIO1))->SetCheck(TRUE);//选上
((CButton *)GetDlgItem(IDC_RADIO1))->SetCheck(FALSE);//不选上
((CButton *)GetDlgItem(IDC_RADIO1))->GetCheck();返回1表示选上,0表示没选上
第二种:
关 联一个congtrol型变量(子类化),好ctrl+W(即打开classwizard),点开 Member Variables,咦?怎么没有IDC_RADIO1这个ID?原来是没有分组。因为radio button通常都是成组使用的,在一组里面是互斥的。取消,回到对话框资源面板,右键Radio1查看属性把Group选上,那么,Radio1和 Radio2就是一组了(怎么知道他们是一组的?后面说)。此时,就可以为Radio1增加一congtrol型变量m_ctrlRadio1了。如下:
m_ctrlRadio1.SetCheck(TRUE);
同样可以使用GetCheck()获取状态。
第三种:
关联一个int型变量(同样需要先分组)m_nRadio1,打开对话框构造函数,你会发现有:
m_nRadio1 = -1;m_nRadio1别赋值-1表示哪个都没有选上。如果你把-1改成0,就会发现Radio1默认被选上了,依此类推,m_nRadio1的值为1 就是第二个被选上了(这里同样有问题,哪个是第一个?哪个是第二个?)。获取状态很简单,UpdateData(TRUE)后判断m_nRadio1的值 即可。

问题2:如何使用多组?

多组和一组是一样的使用,只要搞清楚哪个是哪一组的就行了。再为对话框添加Radio3和Radio4。很简单,先为这些Radio Button排个顺序(这 个必须要做,比如你的一组控件有Radio1,Radio2,Radio3,就把它们的TAB顺序分别设为1,2,3,并将Radio1的Group属性 设为True,这样,当选中Radio1的时候和它关联的变量就是0,当选中Radio2的时候和它关联的变量就是1,依此类推),就是排 列他们的TAB ORDER。在对话框资源面板上Ctrl+D,然后按你自己的理想顺序用鼠标逐个点击就可以了。不妨假设Radio1、Radio2、Radio3、 Radio4分别是1、2、3、4。Radio1和Radio3都选上Group属性,那么,1、2是一组,3、4是另外一组,因为分组的原则是在选上 Group属性的这一个开始直到碰到下一个选上Group属性的。你不妨再Ctrl+D,令Radio1、Radio2、Radio3、Radio4分别 是1、3、2、4,那么Radio1和Radio3是一组,如果m_nRadio1=1,此时是Radio3被选上而不是Radio2被选上。分好了组就 分别使用它们吧。

嗯,也许你还要为它们添加鼠标单击事件,非常简单。


单选按钮控件(Radio Button)的使用

关键词: 单选按钮控件 使用方法



一、对单选按钮进行分组:
每组的第一个单选按钮设置属性:Group,Tabstop,Auto;其余按钮设置属性Tabstop,Auto。如:
Radio1、Radio2、Radio3为一组,Radio4、Radio5为一组

设定Radio1属性:Group,Tabstop,Auto
设定Radio2属性:Tabstop,Auto
设定Radio3属性:Tabstop,Auto

设定Radio4属性:Group,Tabstop,Auto
设定Radio5属性:Tabstop,Auto

二、用ClassWizard为单选控件定义变量,每组只能定义一个。如:m_Radio1、m_Radio4。

三、用ClassWizard生成各单选按钮的单击消息函数,并加入内容:

void CWEditView::OnRadio1()
{
m_Radio1 = 0; //第一个单选按钮被选中
}

void CWEditView::OnRadio2()
{
m_Radio1 = 1; //第二个单选按钮被选中
}

void CWEditView::OnRadio3()
{
m_Radio1 = 2; //第三个单选按钮被选中
}

void CWEditView::OnRadio4()
{
m_Radio4 = 0; //第四个单选按钮被选中
}

void CWEditView::OnRadio5()
{
m_Radio4 = 1; //第五个单选按钮被选中
}

四、设置默认按钮:
在定义控件变量时,ClassWizard在构造函数中会把变量初值设为-1,只需把它改为其它值即可。
如:
//{{AFX_DATA_INIT(CUnitBlockTypeFlankPublicAdd)
m_Radio1 = 0; //初始时第一个单选按钮被选中
m_Radio4 = 0; //初始时第四个单选按钮被选中
//}}

//Radio Button 使用
使用方法一:
在工程中添加控件后,直接在控件的Click事件中写入需要的代码即可

使用方法二:
1.建立一个基于对话框的用用程序,在其中加入三个Radio Button,ID分别为:
IDC_RADIO1,IDC_RADIO2,IDC_RADIO3

2.控件的初始化:
在对话框类的OnInitDialog中加入代码:
CheckRadioButton(IDC_RADIO1,IDC_RADIO3,IDC_RADIO3); //第一个参数为该组的第一个单选按钮的ID
//第二个参数为该组的最后一个单选按钮的ID
//第三个参数为该组中被选中的单选按钮的ID


3.在加入一个Button控件,并为其写入Click事件代码:
int iRadioButton;

iRadioButton="GetCheckedRadioButton"(IDC_RADIO1,IDC_RADIO3);
if(iRadioButton==IDC_RADIO1)
MessageBox("Click Button1");
if(iRadioButton==IDC_RADIO2)
MessageBox("Click Button2");
if(iRadioButton==IDC_RADIO3)
MessageBox("Click Button3");

其他说明:
Radio Button成组使用时,只需保证在添加控件时资源号连续,并且第一个Radio Button的属性中Group被选中即可.
这样就可以给这一组控件添加成员变量使用
例:
1.建立一个基于对话框的应用程序,添加三个Radio Button,和一个Button
选中第一个Radio Button属性中的Group

2.在Button的Click事件中添加代码:
UpdateData(TRUE);
if(m_radio==0)
MessageBox("0");
if(m_radio==1)
MessageBox("1");
if (m_radio==2)
MessageBox("2");
UpdateData(false);

2009年3月17日星期二

0

SetTimer函数的用法

什么时候我们需要用到SetTimer函数呢?当你需要每个一段时间执行一件事的 的时候就需要使用SetTimer函数了。 使用定时器的方法比较简单,通常告诉WINDOWS一个时间间隔,然后WINDOWS以此时间间隔周期性触发程序。通常有两种方法来实现:发送 WM_TIMER消息和调用应用程序定义的回调函数。

1.1 用WM_TIMER来设置定时器

先请看SetTimer这个API函数的原型

UINT_PTR SetTimer(
HWND hWnd, // 窗口句柄
UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器
UINT uElapse, // 时间间隔,单位为毫秒
TIMERPROC lpTimerFunc // 回调函数
);

例如
SetTimer(m_hWnd,1,1000,NULL); //一个1秒触发一次的定时器
在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了

于是SetTimer函数的原型变为:

UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,YINT ,DWORD))

当 使用SetTimer函数的时候,就会生成一个计时器。函数中nIDEvent指的是计时器的标识,也就是名字。nElapse指的是时间间隔,也就是每 隔多长时间触发一次事件。第三个参数是一个回调函数,在这个函数里,放入你想要做的事情的代码,你可以将它设定为NULL,也就是使用系统默认的回调函 数,系统默认认的是onTime函数。这个函数怎么生成的呢?你需要在需要计时器的类的生成onTime函数:在ClassWizard里,选择需要计时 器的类,添加WM_TIME消息映射,就自动生成onTime函数了。然后在函数里添加代码,让代码实现功能。每隔一段时间就会自动执行一次。

例:

SetTimer(1,1000,NULL);

1:计时器的名称;

1000:时间间隔,单位是毫秒;

NULL:使用onTime函数。

当不需要计时器的时候调用KillTimer(nIDEvent);

例如:KillTimer(1);

1.2 调用回调函数

此方法首先写一个如下格式的回调函数

void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);
然后再用SetTimer(1,100,TimerProc)函数来建一个定时器,第三个参数就是回调函数地址。

二. 或许你会问,如果我要加入两个或者两个以上的 timer怎么办?

继续用SetTimer函数吧,上次的timer的ID是1,这次可以是2,3,4。。。。

SetTimer(2,1000,NULL);

SetTimer(3,500,NULL);

嗯,WINDOWS会协调他们的。当然onTimer函数体也要发生变化,要在函数体内添加每一个timer的处理代码:

onTimer(nIDEvent)

{

switch(nIDEvent)

{

case 1:........;

break;

case 2:.......;

break;

case 3:......;

break;

}

}

2009年3月16日星期一

0

PhysX物理引擎(入门教程)

一、安装
在国际上,出名的物理引擎有Havok,Vortex,ODE,Novodex,Takamak等等,其中ode是一个免费开源的物理引 擎,而Novodex就是PhysX的前身,被Ageia收购之后,改名为PhysX,是一个可以免费用于非商品用途的引擎。在这里选用PhysX来作为 入门教程,主要是因为,它的帮助比较丰富,而且开发包可以免费获得。



关于PhysX sdk的安装.首先要进入http://support.ageia.com下载SDK,网站http://support.ageia.com下載安裝 文件.請注意的是Ageia的SDK只对注册用户开放下载。注册是免费的,但好像要经过审核才会开通,不过一般都会通过的。我注册的时候好像是第二天才收 到开通邮件。有两个安装文件是必须下载的System Software.exe和PhysX 2.3.3 SDK Core.exe前一个是底层驱动,后一个是程序内核,最新的SDK是2.4.1,但是只针对商业客户开放。对于初学者来说,最好把PhysX 2.3.3 SDK Training Pragrams.exe也一起下载,里面包含了从初级到高级的一系列教程,对学习这个引擎很有帮助。把所有东西下载下来之后,接着是安装了,安装很简 单,一路next下去就可以了,但是为了让VC中设置方便一点,建设把PhysX 2.3.3 SDK Core.exe的安装路径改短一点,例如我的就是安装在D:\PhysX中。



安装好了之后,后开始对VC编译环境进行设置。



首先,在Tools→Options→Directories→Inclund Fik中加入以下目录.

D:\PhysX\SDKS\Physics\include

D:\PhysX\SDKS\Founddation\include

D:\PhysX\SDKS\PhysXLoader\include

然后在…àLibrary Fiks中加入以下目录:

D:\PhysX\sdks\LIB\Win32



以 上用到的“D:\PhysX”指的是sdk安装目录,以你机器中的安装路径为准,本教程的示例程序用到了opengl和glut作为渲染引擎,你的计算机 如何没有安装glut库,那也请先到www.opengl.org上www.opengl.org下载一个安装上去。在这里就不打算深入讨论glut了, 没有基础的朋友可以先自学一下。



二.、PhysX概述
首先来介绍一下PhysX编程的几个术语以及它们之间的相互联系。

1. Scene场景:就像演员表演都需要一个舞台一样, PhysX的所有物理运动都在这个scene中进行。

2. Actor角色:在场景中,所有参与运算的实体都是一个角色或许我这样表达不是很正确,大家慢慢体会吧!

3. bosy刚体:用来记录物体之间世界交互的各种系数,如速度,阻尼等.

4. shape形状:描述和表达某一角色的外形,PhysX中提供4种基本形状,盒子,球,胶囊以及平面。



从 上面图可以看到,PhysX编程其实很简单,首先,定义各种不同的角色(actor),然后指定每个角色的形状(shape)属性和刚体(body)属 性,最后是把这些角色都加入到场景(scene)空间中去,这样就可以构造出一个完整的物理世界。下面我将详细描述编程的步骤.

三.编程实现
1.创建scene,



NxsceDesc sceneDesc:

SceneDesc.grauity = gDefaultGravity;//指定重力加速度(-9.81f)

SceneDesc.broadphase = NX_BROADPHASE_COHERENT;

SceneDesc.collisionDetection= true; //是否开启碰撞检测

Gscene =gPhysicsSDK→createScene(sceneDesc);



首先我们要创建一个场景的描述(Descriptor),PhysX SDK就利用这个场景描述结构来创建生成一个场景实例.

描述(Descriptor)在整个SDK编程过程中,会被广泛地使用。描述其实就是一个数据结构,主要是用来保存各种在创建实体时所需要的相关信息。你可以调整描述体中各种参数来达到不同的效果,当然你可以不作任何修改,这样的话实体在创建时会使用描述体的默认值。



在本例子中,我们创建一个指定了重力加速以及碰撞检测算法的场景实例。PhysX SDK中提拱了三种碰撞检测算法提拱给大家选择.这里选用的是“broad phase-coheret collison detoction”。



2.给场景(scene)增加物理材质(Materials)



物理材质指的是某一具体物体的表面属性和碰撞属性,这些属性可以确定一个物体和另一个物体发生碰撞时,是如何在该的物体上反弹,滑动或者滚动的。



你可以给场景中的所有物体指定一个相同的默认物理材质。



//创建默认材质

Nxmaterial* defaultMaterial=gscene → getMaterialFromIndex(0);

Default Material→setRestitution(0.9);//还原系数为0的时候没有还原.

DefaultMaterial→setStaticFriction(0.5);//静摩擦系数.

DefaultMaterial→setDynamicFricfion(0.5);//动摩擦系数.



以上材质的系数最小值都是0,最大值是1,如果要实现一个物体落在地上会自动弹跳,那就得把还原系数设得大一点。



3.创建地面



在本程序例子中,只有两个角色实体,地面和盒子.我们首先来看如何创建地面.



NxPlane shapeDesc planeDesc;

NxActorDesc actorDesc;

actorDesc.shapes.pushBack(&phane Desc);

gscene→createActor(AcforDesc);



创建一个地面角色,这可能是角色创建的最简单的方法了,只用到了四行代码,首先分别创建一个平面形状描述和角色描述,两个描述都不作任何修改,也就是使用它们的默认值.平面的中心位于世界坐标原点(0,0,0)处,而法线则是指向y轴的正方向。



第二步,把平面描述添加到角色描述中的形状列表中去,从这里我们也可以看到,一个角色是可以包含多个形状物体的。



第 三步,就是把角色加到场景(scene)中去,也许你会留意到,前面我们所说的一个角色实体必须包括形状描述和刚体描述,两大部份,为什么这里只有形状描 述呢?其实,刚体描述也是存在的,当你没有为它指定的时候,角色创建时会自动生成一个默认的刚体描述。一个刚体的默认值是这样的:它不会移动但是会把与它 发生碰撞的物体反弹回去。因为它的质量是无限大的。





4、 创建盒子

前面介绍了如何创建一个地面,这是场景中最简单的一个角色了,下面我们将要创建一个稍为复杂一点的角色,一个盒子。



Int size=5

NxBodyDesc BodyDesc;

BodyDesc.angularDamping=0.5f;

BodyDesc.linearVelocity=NxUec3(0.0f,0.0f,0.0f)

NxBoxShapeDesc BoxDesc;

BoxDesc.dinesions=NxUec3(float(size),float(size),float(size));

NxActorDesc BoxActorDesc;

BoxActorDesc.shapes.pushBack(&BosDesc);

BosActorDsec.body= &BodyDesc;

BoxActorDesc.desity=0.10f;

BoxActorDesc.globalpose.t=NxVec3(0.0.20.0.0.0);

Gscene→createActor(BoxActorDesc)→userData=(viud*)size;



这 里我们创建了一个叫“Box”的场景角我。我们可以看到,盒子角色完整地包含了形状和刚体两大部份。和创建平面角色不同的是盒子角色描述中多了 “desity”,“globalpose”两个分量,分别指的是密度和初始位置,SDK会根据密度和体积来自动计算角色的质量。

“globalpose”指的是在世界位标中的相对位置,值得注意的是:

PhysX中,与坐标尺寸相关的数值,其单位都是“米”(m)。





5.绘制与运动



完成了以上的准备工作之后,接下来便是检验成果的最后冲刺了.



Whik(nbActors--)



NxActor*actor=*actors++;

If(!actor->userData) continue;

glpushMatrix();

float glamat[16];

actor->getGlobalPose().getColumnMajor44(glmat);

glColor4f(1.0f,1.0f,1.0f,1.0f);

glMultMatrix(glmat);

glutWireCube(float(int(actor→userData))*2.0f);

glPopMatrix();





上面是绘制场景的程序,这里因为不需要绘制地面,因此第一行跳过平面角色,直接绘制盒子.

OK,现在我们可以让程序运行起来了,在窗口可以看见生成的一个立方体盒子.但是为什么那个盒子不会落下来,不会运动呢?这是因为我们还没有加入实时运算函数。在绘制盒子之前加入以下三行:



Gscene→fetchResults(NX_RIGID_BODY_FINFSHED);

gsceng→Simulate(1/60.0f);

gscene→flushstream();



这样,盒子就会产生自由落体运动,其中simulate(1/60.0)是一个积分函数,用来求位移.这里用到了固定间隔时间1/60.0秒,其实最好是使用一些系统时间函数,来计算上一次刷屏到现在的时间,这样会让物体运动更加逼真。

四.总结
这 是一个PhysX物理引擎的Hello World入门程序,为了让大家更清晰地看到程序总体框架,我把程序的功能尽量写得简单。在接下来的一段时间里,我会写一些复杂的相关教程,希望各位网友 友持。当然,我也是一边学一边写,难免会出现错差,如果你们发现我的文章有问题的话,请E-mail:huawenguang@sina.com 告诉知我,也欢迎在这方面有共同兴趣的朋友来信交流.

特别感谢我身边一个朋友的支持!



五、源代码



// A minimal Novodex application test.

// 以下代码,先安装好PhysX SDK,及按要求配置好路径之后才能编译。

// 建义用使用VC2003以上版本,VC6.0在我这里有一个“return”错误,把“return”去掉就可以编译通过。

// 运行的时候如果提示缺少DLL文件,请在/bin/win32 目录中找到相应的DLL文件把它拷贝到工程文件夹中,

// 或者拷贝到系统systems32/ 文件夹中

// NxBoxes by Pierre Terdiman (01.01.04)

// author: huawenguang@sina.com



#define NOMINMAX

#ifdef WIN32

#include

#include

#include

#elif LINUX

#include

#include

#elif __APPLE__

#include

#include

#elif __CELLOS_LV2__

#include

#endif



#include



// Physics code

#undef random

#include "NxPhysics.h"

//#include "ErrorStream.h"

#pragma comment( lib, "PhysXLoader.lib" )





static bool gPause = false;

static NxPhysicsSDK* gPhysicsSDK = NULL;

static NxScene* gScene = NULL;

static NxVec3 gDefaultGravity(0.0f, -9.81f, 0.0f);

static float gRatio=1.0f;





static void InitNx()

{

// Initialize PhysicsSDK

gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, 0, NULL);

if(!gPhysicsSDK) return;



gPhysicsSDK->setParameter(NX_MIN_SEPARATION_FOR_PENALTY, -0.05f);



// Create a scene

NxSceneDesc sceneDesc;

sceneDesc.gravity = gDefaultGravity;

sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;

sceneDesc.collisionDetection = true;

gScene = gPhysicsSDK->createScene(sceneDesc);



NxMaterial * defaultMaterial = gScene->getMaterialFromIndex(0);

defaultMaterial->setRestitution(0.9f);

defaultMaterial->setStaticFriction(0.1f);

defaultMaterial->setDynamicFriction(0.1f);



// Create ground plane

NxPlaneShapeDesc PlaneDesc;

PlaneDesc.d = -5.0f;

NxActorDesc ActorDesc;

ActorDesc.shapes.pushBack(&PlaneDesc);

gScene->createActor(ActorDesc);



//CreateCube(NxVec3(0.0,20.0,0.0),5);

// Create body

//////////////////////////////////////////////////////////////

int size = 5;

NxBodyDesc BodyDesc;

BodyDesc.angularDamping = 0.5f;

// BodyDesc.maxAngularVelocity = 10.0f;



BodyDesc.linearVelocity = NxVec3(0.0f,0.0f,0.0f);



NxBoxShapeDesc BoxDesc;

BoxDesc.dimensions = NxVec3(float(size), float(size), float(size));



NxActorDesc BoxActorDesc;

BoxActorDesc.shapes.pushBack(&BoxDesc);

BoxActorDesc.body = &BodyDesc;

BoxActorDesc.density = 0.10f;

BoxActorDesc.globalPose.t = NxVec3(0.0,20.0,0.0);



gScene->createActor(BoxActorDesc)->userData = (void*)size;



}





static void RenderCallback()

{





// Clear buffers

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



// Setup camera

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(60.0f, 1.0, 1.0f, 10000.0f);



glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt(0.0, 5.1, 50.0, 0.0, 0.0, 0.0, 0.0f, 1.0f, 0.0f);



gScene->fetchResults(NX_RIGID_BODY_FINISHED);

gScene->simulate(1/60.0f);

gScene->flushStream();





// Keep physics & graphics in sync

int nbActors = gScene->getNbActors();

NxActor** actors = gScene->getActors();

while(nbActors--)

{

NxActor* actor = *actors++;

if(!actor->userData) continue;



glPushMatrix();

float glmat[16];

actor->getGlobalPose().getColumnMajor44(glmat);

glMultMatrixf(glmat);

glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

glutWireCube(float(int(actor->userData))*2.0f);

glPopMatrix();



}



glutSwapBuffers();

}





int main(int argc, char** argv)

{

// Initialize Glut

printf("PhysX, Hello World!");

glutInit(&argc, argv);

glutInitWindowSize(512, 512);

glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);

int mainHandle = glutCreateWindow("PhysX, Hello World!");

glutSetWindow(mainHandle);

glutDisplayFunc(RenderCallback);

glutIdleFunc(RenderCallback);



// Setup default render states

glClearColor(0.3f, 0.4f, 0.5f, 1.0);

glEnable(GL_DEPTH_TEST);

glEnable(GL_COLOR_MATERIAL);

glEnable(GL_CULL_FACE);

glEnable(GL_LIGHTING);



// Physics code

InitNx();

// ~Physics code



// Run

glutMainLoop();



if(gPhysicsSDK && gScene) gPhysicsSDK->releaseScene(*gScene);

gPhysicsSDK->release();

return 0;

}

2009年3月6日星期五

0

Ogre 相关工具代码摘录(翻译)

实时变形分页地形场景管理器(Geomipmapping paging landscape scenemanager with real time deformation)

这里是 Ogre 1.2 SDK 兼容的 PLSM2 :

(以上下载都需要数据源服务器在工作)

Demo里有4个地图可供选择, 源代码甚至是CVS : 皮吉特puget(1024x1024), 大峡谷, 阿尔卑斯山(Alpes), 和一个16位的高度场图从特拉根(Terragen)download 可以下载到, 把它解压到 media 文件夹, 使得 "media/paginglandscape2/terrains/datasrcs/" 成为plsm2 的一个子目录文件夹(为了运行这个demo)

解压, 运行 mapsplitter, 运行 demo,按F1 得到帮助.

看看Ogre 维基百科 以便得到更加详细的阐述,并且看看Ogre插件论坛的一般性问题

感谢william de boer, antiarc, janders, jeff leigh, sinbad, spoke(都是人名), 和许多不知道名字的贡献者.

CVS服务器上的plsm2

(ps: demo 支持的是ogre1.2.2版本的 我的是ogre1.6 没运行成功的。。。)

Ogre Mesh文件查看器(Ogre Mesh Viewer Next Gen)

可以查看, 优化, 播放动画在 ogre进制文件和 XML 文件的支持下.

源代码: zip文件 或者 安装包 (推荐)

SDK 1.4.x 兼容版本: zip文件 or 安装包

(忍着片段)


论坛说明贴 这里

Ogre子弹(OgreBullet (26 -10- 2007)

子弹物理引擎 和 Demos

子弹是新一代开源物理引擎

论坛

demo安装包

win32 SDK和源代码 安装包

源代码(zip文件)

(盒子)


OgreOde (26 -10- 2007)

ODE物理引擎 以及 Demo

论坛
安装包
SDK源代码安装文件
源代码(zip文件)

(布娃娃)


Impostor Demo

实时模拟使用公告板和压缩渲染纹理.(跟新至 10 -5 -2006)

Impostor: 源代码

Impostor: 可执行demo

(3000 三角形 运行在 200 fps)


论坛说明贴 这里

Flag Demo

在风里实时的旗帜飘扬,使用的是原始自然模拟算法.

Flag: 代码


论坛说明贴 这里

Spring Grass Ogre

动态草坪

源代码


论坛说明贴 这里 and 新的源代码 跟新作者 "atsakir"

OGRE外部托管代码仓库

天空散开

Caleum Sky : 源代码| 媒体 | 例子


论坛说明贴 这里

Web Cam 插件

Web Cam Plugin : | Demo文件 | 插件和Demo源代码


论坛说明贴 这里

0

OgreODE 翻译wiki百科

OgreODE 在 ogreaddons 的 SVN 代码库(repository)可以下载到.

OgreODE

为ogre提供的ODE封装

Ogre Ode 是一个物理引擎(ODE)的封装, 最先由“怪物”开发(monster). 这里 可以下载到ODE . 这个封装支持所有的ODE的特性, 而且添加了一些预置物体像布娃娃 和车辆. 当前版本支持Win32 和linux.

OgreODE当前可用版本 Dagon 1.2 (also 1.2.2) 且你可以从Tuan Kuranes site下载到. (Win32 Demo安装包, SDK安装包 还有源代码压缩文件)

一个教新的版本 Eihort 1.4 (现在跟新到了1.4.5 ) 可以从CVS下载到

点击 论坛 以得到新的消息.

在源代码版本里我们可以找到三个教程:

  • 简单场景: 一个简单的文件展示了所有在OgreODE里的函数 (简单碰撞, 连接(joint), 玩具娃娃(ragdolls), 汽车)
  • 全景: 在OgreODE里 Terrain Scene Manager 的使用方法
  • 赛车教程 (被称作GranTurismOgre): 一个用了OgreODE的赛车Demo(car + trimesh)

ODE的 ODE wiki 包含了所有有用信息.

下面就是一步一步的让大家了解和使用OgreODE了. 你也可以点击Ode 官方网站 ,它有一套健全的文档, 信息, 和一个非常活跃ODE特殊问题列表.

关于OgreODE

OgreODE,基础理论 - OgreODE 和ODE: 许可证, 基础和 一体化

编译 OgreOde使用Visual C++ 或者 Code::Blocks

开始开发

OgreODE的第一步 - 初始化, 步进(steppers), 原(primitives )

在OgreODE设置高级物体 设置射线

在OgreODE里使用地形

在 OgreODE里自然话模型运动

OgreOde 行走特性 - 密封模型 和 球星模型

OgreOde 摄像机 - OgreOde 第三人称摄像机

TriangleMeshRayListener Demo - 创建射线属性,交通工具等.

Using OgreOde prefabs - 交通工具,布娃娃等.

高级主题

OgreOde 碰撞处理 - 在OgreODe中处理碰撞的一个方法就是给你的物体增加特殊行为.

代码摘录

忍着行走 - 一个Demo_SimpleScenes 的例子

来自 "http://www.ogre3d.org/wiki/index.php/OgreODE"

A Jeep driving in the Landscape Demo

吉普车在地形上驾驶的demo

The SimpleScenes Ragdoll demo showing several ragdolls

行走布偶的 demo 展示了一些布偶

0

Oogst’s How To

From Ogre Wiki

Jump to: navigation, search

How to-list是由Oogst写的,最后更新于25-05-2004,在Oogst-site上可以查找更多的Oogst素材资料。

关于这个列表
这是一个在Ogre中怎样做简单的事情的列表。它包含主要的简单代码范例,这是我在学习Ogre基础时真正缺少的东西。这里提供的解释都是简要的。有关提到的东西的更多细节,参见:

Ogre-basicsScript-definitions的官方手册;

Ogre中描述各个classfunctionAPI

Ogre提供的指南;

Ogre论坛中的search-function

我为什么做这个表
我制作这个列表有四个原因:

我在学校的一个团队从事工作,团队中的其他人想能够快速地找到代码例子;

我在自学Ogre时确实缺少这类东西,它本可以节省我很多时间;

我经常忘记函数名字,这样我就可以快速地查找它们;

我想为做些事来报答Ogre团队提供的这个妙极了的引擎,我可以免费使用它而且它的创造者不期望得到回报。

ExampleApplication
这个列表产生于ExampleApplication并且支持用户使用ExampleApplicationExampleFrameListener。一些变量(variable),像mSceneMgr 只能通过ExampleApplication使用。如果你不使用ExampleApplication,你只能用不同的变量来替代。

另外,所有的例子都为变量等使用专有的名字;当然你可以改为更适合你自己的应用程序的名字。


目录

Oogst’s How To. 1

如何用ExampleApplication建立一个应用程序.. 3

如何在场景(scene)中放置一个3D模型.. 4

如何从场景中移除一个实体.. 4

如何移动、重置、缩放及旋转一个场景节点.. 5

如何在场景那个中放置光源.. 5

如何设置环境光.. 5

如何控制相机.. 6

用鼠标控制相机.. 6

如何向场景中添加布告板.. 7

如何用ExampleFrameListener创建基本的FrameListener 7

如何在FrameListener中从scene中控制对象.. 8

如何得到自前一帧以后的时间.. 8

如何响应按键.. 9

如何确保按键在彼此太短时间内不响应.. 9

如何退出应用程序.. 10

如何在运行过程中从场景中高效地添加和移除3D对象(比如发射火箭).. 10

如何显示一个Overlay(及隐藏).. 12

如何改变TextArea中的文本.. 12

如何显示鼠标指针(mouse-cursor.. 13

如何创建一个工作按钮(working button.. 13

如何找出哪个按钮被按下.. 14

如何使用ActionListener退出应用程序.. 15

如何获得一个不同的SceneManager 16

如何有效地获得所有可能碰撞的列表.. 16

如何找出一个MoveableObject属于你的哪个对象.. 17

如何从碰撞检测中排除对象.. 18


如何用ExampleApplication建立一个应用程序

如果你还不知道这个是如何工作的,你必须Ogre指南章节中读更多关于这个的知识:完全理解它确实很重要。简言之,它需要创建一个继承自ExampleApplication的类(class)。这个类能够执行函数createScene()createFrameListener()。它大致上看上去如下:

#include "ExampleApplication.h"

class MyClass: public ExampleApplication,

{

public:

MyClass(void);

~MyClass(void);

protected:

void createScene(void);

void createFrameListener(void);

};

你还会需要一个创建MyClass实例的.cpp文件并运行它。通常是这个样子(取自于“Guide To Setting Up Application Project Files-tutorial):

#include "Ogre.h"

#include "MyClass.h"


#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char **argv)

#endif

{

MyClass app;

try

{

app.go();

}

catch( Exception& e )

{

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!",

MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

fprintf(stderr, "An exception has occured: %s\n", e.getFullDescription().c_str());

#endif

}

return 0;

}

如何在场景(scene)中放置一个3D模型

一个3D模型是一个实体(Entity)并且它必须附属于一个场景结点(SceneNode)以被放置在场景中。场景结点能够由SceneManager中的rootScnenNode产生。创建一个实体和场景结点并把实体挂在场景结点上是这个样子:

Entity* thisEntity = mSceneMgr->createEntity("nameInTheScene", "FileName.mesh");

SceneNode* thisSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

thisSceneNode->attachObject(thisEntity);

如何从场景中移除一个实体

为了移除一个实体(Entity),你必须把实体从它的场景结点中分开,删除它如果需要的话还要销毁场景结点,这必须用SceneManager来完成。如果你有一个叫做myNodeSceneNode*,你可以用下面的代码完全地删除它的内容(MovableObjects和子场景结点)及结点本身:

SceneNode* parent = entity->getParentSceneNode();

parent->detachObject(entity);

mSceneMgr->destroyEntity(entity->getName());

// entity is now destroyed, don't try to use the pointer anymore!

// optionally destroy node

mSceneMgr->destroySceneNode(parent->getName());

如何移动、重置、缩放及旋转一个场景节点

如果你有一个连接有多个可移动对象(MovableObject),像实体、灯光及相机的场景结点,你可以用多个函数移动它,查看这些的API。下面的函数分别是移动(move)、重置(reposition)、缩放(scale)及绕它的X-Y-Z-轴进行旋转:

thisSceneNode->translate(10, 20, 30);

thisSceneNode->setPosition(1.8, 20.1, 10.5);

thisSceneNode->scale(0.5, 0.8, 1.3);

thisSceneNode->pitch(45);

thisSceneNode->yaw(90);

thisSceneNode->roll(180);

如何在场景那个中放置光源

为了向场景中添加一个光源,你必须向SceneManager请求一个。然后你可以对它进行设置,下面给出一些设置的例子:

Light* myLight = mSceneMgr->createLight("nameOfTheLight");

myLight->setType(Light::LT_POINT);

myLight->setPosition(200, 300, 400);

myLight->setDiffuseColour(1, 1, 0.7);

myLight->setSpecularColour(1, 1, 0.7);

你还可以把一个光源连接到一个场景结点上。下面的代码创建一个场景结点并把myLight连接到上面:

SceneNode* thisSceneNode = static_cast(mSceneMgr->getRootSceneNode()->createChild());

thisSceneNode->attachObject(myLight);

如何设置环境光

环境光由SceneManager进行控制,因此你可以用SceneManager来对它进行设置:

mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));

如何控制相机

ExampleApplication中的标准相机叫做mCamera并且它在继承自ExampleApplication的类中是可用的。下面的代码改变它的位置,改变它的朝向点,创建一个SceneNode并把相机连接到上面:

mCamera->setPosition(0, 130, -400);

mCamera->lookAt(0, 40, 0);

SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

camNode->attachObject(mCamera);

用鼠标控制相机

这个代码使得为RTS(译者注:Realtime Strategy Game,即时战略类游戏,对应回合制战略游戏)风格的游戏使用标准相机移动接口变的容易。当按下左键时相机围绕着一个固定的点移动。拉伸(zooming)通过滚轮或点击中键及上下移动鼠标来完成。

Real MoveFactor = 60.0 * evt.timeSinceLastFrame;

// Move the camera around with the left button

if (mInputDevice->getMouseButton(1)) {

SceneNode *camNode = mCamera->getParentSceneNode();

if (camNode == 0) {

std::cerr << "mCamera isn't attached to any SceneNode !" <<>

}

camNode->yaw(Degree(mInputDevice->getMouseRelativeX() * MoveFactor * -0.1));

camNode->pitch(Degree(mInputDevice->getMouseRelativeY() * MoveFactor * -0.1));

}

// Zoom with the middle button...

if (mInputDevice->getMouseButton(2)) {

mCamera->moveRelative(Vector3(0.0, 0.0, -0.5)

* mInputDevice->getMouseRelativeY() * MoveFactor);

}

// ... and the wheel ;-)

mCamera->moveRelative(Vector3(0.0, 0.0, -0.1)

* mInputDevice->getMouseRelativeZ() * MoveFactor);

如何向场景中添加布告板

布告板(Billboard)是一个总是面向相机的正方形。它被认为是一个精灵(sprite)。为产生一个布告板,你必须首先产生一个BillboardSet。然后billboard能被添加到上面一个给定的位置。BillboardSet是一个MovableObject,因此应该添加到SceneNode上。整个程序如下:

SceneNode* myNode = static_cast(mSceneMgr->getRootSceneNode()->createChild());

BillboardSet* mySet = mSceneMgr->createBillboardSet("mySet");

Billboard* myBillboard = mySet->createBillboard(Vector3(100, 0, 200));

myNode->attachObject(mySet);

如何用ExampleFrameListener创建基本的FrameListener

帧监听器(FrameListener)给你在每帧开始和结束时做一些事情的机会。首先你必须创建一个继承自ExampleFrameListener的类并且在这个类中你可以执行frameStarted()frameEnded()。看起来就像这样子:

#include "ExampleFrameListener.h"



class myFrameListener: public ExampleFrameListener

{

public:

myFrameListener(RenderWindow* win, Camera* cam);

~myFrameListener(void);

bool frameStarted(const FrameEvent& evt);

bool frameEnded(const FrameEvent& evt);

};

构造函数应该调用它的父构造函数,像这样子:

myFrameListener::myFrameListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam){}

你还必须向Root注册你的FrameListener,这可以在createFrameListener()中完成——继承自ExampleApplication的类的函数。你可以向root注册尽你希望多的FrameListener。这个过程如下:

void createFrameListener(void)

{

MyFrameListener listener = new MyFrameListener(mWindow, mCamera);

mRoot->addFrameListener(listener);

}

如何在FrameListener中从scene中控制对象

如果你想在FrameListener中控制场景中的一些对象,FrameListener有权访问它。一个简单方法是在构造FrameListener时提供一个指向它的指针。现在这个构造函数该是这样子:

myFrameListener(RenderWindow* win, Camera* cam, Car* car);

如何得到自前一帧以后的时间

FrameListenerframeEnded()frameStarted()函数中你可以用下面的方法得到以秒为单位的时间(这是一个浮点数):

bool frameStarted(const FrameEvent& evt)

{

float time = evt.timeSinceLastFrame;

return true;

}

现在你可以马上用每秒的速度乘以这个浮点数来得到自上一帧后的移动。这可以为游戏设定独立的帧率。

如何响应按键

你可以在FrameListener的函数frameEnded()frameStarted()中通过先让Ogre捕获然后再检查哪一个键被按下来响应按键。你只需要每帧捕获一次InputDevice。过程是这样子:

mInputDevice->capture();

if (mInputDevice->isKeyDown(Ogre::KC_DOWN))

{

//react however you like

}

if (mInputDevice->isKeyDown(Ogre::KC_UP))

{

//react however you like

}

如何确保按键在彼此太短时间内不响应

如果你用上面的方法响应按键,它们每帧都会响应。比如说你想每帧大约响应两次,你可以通过设置计时器(timer)来完成。为了能够使不同的函数调用来访问它,timer必须在它自己的类中而不是在函数中 。执行这个是这样子:

class myFrameListener: public ExampleFrameListener

{

protected:

float buttonTimer;

public:

myFrameListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam)

{

buttonTimer = 0;

}

bool frameStarted(const FrameEvent& evt)

{

float time = evt.timeSinceLastFrame;

buttonTimer -= time;

mInputDevice->capture();

if (mInputDevice->isKeyDown(Ogre::KC_DOWN) && buttonTimer <= 0)

{

buttonTimer = 0.5;

//react however you like

}

return true;

}

};

如何退出应用程序

你可以通过在FrameListenerframeStarted()frameEnded()中返回false来退出你的程序。如果你想把这个关联在键盘上的escape按钮上,用下面的方法:

bool frameStarted(const FrameEvent& evt)

{

mInputDevice->capture();

if (mInputDevice->isKeyDown(Ogre::KC_ESCAPE))

return false;

return true;

}

如何在运行过程中从场景中高效地添加和移除3D对象(比如发射火箭)

createScene()函数中你可以从一个文件中加载meshes,但是如果要在运行时添加一个新的对象这样做就太慢了(虽然在一个很简单的程序中看不出速度上的差别)。为了添加对象更快,你可以在createScene()函数中加载许多meshes然后在运行时从栈(stack)中把它们取出来。在对象不再被使用后,你可以把它们放回栈中为以后使用。这个的应用的一个好的例子是发射火箭:它们必须在开火的时候创建并在爆炸后被移除。

为了能够总是访问到这个栈,你可以使它是一个全局变量(global variable)或通过一个singleton访问它。后者更漂亮,但是要用更多的代码因此在这个例子中我不这样做。所以你在某处定义一个储存Entities的栈:

stack rocketEntities;

createScene()中用许多rockets填充这个栈。每一个Entity此时被设置为不可见并且必须有一个唯一的名字,这个可以用sprintf()函数来完成。这一切如下所示:

for (unsigned int t = 0; t <>

{

char tmp[20];

sprintf(tmp, "rocket_%d", t);

Entity* newRocketEntity = mSceneMgr->createEntity(tmp, "Rocket.mesh");

newRocketEntity->setVisible(false);

rocketEntities.push(newRocketEntity);

}

此刻当创建一个新的Rocket时我们可以从rocketEntities中取出一个mesh。在这个例子中我在Rocket构造函数中做这些,并在析构函数中把它压回栈中。在析构函数中为了能够从场景中取回Entity,我把它的名字存储在rocketEntityName中。而且我把rocket恰当地放在构造函数中。为了使这一切工作,SceneManager在整个程序中必须是可用的,为此通过让它是一个全局变量(在所有class的外面)来完成。我还在在构造函数和析构函数中执行创建和销毁SceneNode。至此这个Rocket-class看起来如此:

class Rocket

{

protected:

SceneNode* rocketNode;

string rocketEntityName;

public:

Rocket(const Vector3& position, const Quaternion& direction)

{

rocketNode = static_cast(sceneMgr->getRootSceneNode()->createChild());

Entity* rocketEntity = rocketEntities.top();

rocketEntities.pop();

rocketEntity->setVisible(true);

rocketEntityName = rocketEntity->getName();

rocketNode->attachObject(rocketEntity);

rocketNode->setOrientation(direction);

rocketNode->setPosition(position);

}

~Rocket()

{

Entity* rocketEntity = static_cast(rocketNode->detachObject(rocketEntityName));

rocketEntity->setVisible(false);

rocketEntities.push(rocketEntity);

sceneMgr->getRootSceneNode()->removeAndDestroyChild(rocketNode->getName());

}

};

如果你使用这个结构,你应该意识到这个事情:如果没有rockets剩余会发生错误。你可以通过检查这个Entities栈是否为空,如果为空就加载新的meshes到栈中来解决这个问题。

如何显示一个Overlay(及隐藏)

为了显示和隐藏一个Overlay,你首先必须用OverlayManager(这是一个singleton)得到一个指向它的指针。如果你定义了一个名字是“myOverlay”的.overlay脚本,你可以用下面的代码获取、显示并隐藏它:

Overlay* thisOverlay = static_cast(

OverlayManager::getSingleton().getByName("myOverlay"));

thisOverlay->show();

thisOverlay->hide();

如何改变TextArea中的文本

你可以在运行时间改变GUIContainersElements中所有的参数(parameter)。为此,首先你必须得到一个你想要的ElementContainer的指针,如果是基于你想做的东西的需要就把它设计成那种类型。得到一个指向在.overlay脚本中定义为“myTextArea”的TextArea的指针并改变其caption类似这样:

OverlayElement* thisTextArea = OverlayManager::getSingleton().getOverlayElement("myTextArea");

thisTextArea->setCaption("blaat");

这个例子中不需要casting,因为每个OverlayElement都有caption。如果你想设置一个OverlayElement类型的特殊设置,你必须把它设计为那种类型。改变一个textAreafont-name类似这样:

TextAreaGuiElement* thisTextArea = static_cast(

OverlayManager::getSingleton().getOverlayElement("myTextArea"));

thisTextArea->setFontName("RealCoolFont");

如何显示鼠标指针(mouse-cursor

这一条已经过时了。新的CEGUI方法的详述在这里: How To Show The Mouse Cursor, 以及这里: Intermediate Tutorial 3
如果你想在屏幕上显示鼠标指针,你须做两件事:设置它为显示并告诉FrameListener追踪它。令它显示可以如下做:

GuiContainer* cursor = OverlayManager::getSingleton().getCursorGui();

cursor->setMaterialName("Cursor/default");

cursor->setDimensions(32.0/640.0, 32.0/480.0);

cursor->show();

FrameListener追踪它应在它的父构造函数中通过设置它的最后的boolean参数为true来做。至此这个构造函数看起来是这个样子:

myFrameListener::myFrameListener(RenderWindow* win, Camera* cam)

: ExampleFrameListener(win, cam, false, true){}

注意这样做后,因为mInputDevicecapture()函数和isKeyPressed()函数不再正确地工作所以这个特殊的FrameListener对按下按钮操作不再反应。现在需要一个不同的FrameListener来执行键盘输入。

如何创建一个工作按钮(working button

这一条已经过时并由cegui方案替代。
你可以在Overlay脚本中用类似下面的句法定义一个button,这对设置所有的这些material很重要:

container Button(myButton)

{

metrics_mode relative

horz_align left

vert_align top

top 0.1

left 0.1

width 0.18

height 0.1

material NCG/GuiSession/RedMaterial

button_down_material NCG/GuiSession/RedMaterial

button_up_material NCG/GuiSession/RedMaterial

button_hilite_down_material NCG/GuiSession/RedMaterial

button_hilite_up_material NCG/GuiSession/RedMaterial

button_disabled_material NCG/GuiSession/RedMaterial

}

ButtonsOgre中不是标准的,因此你须确保它们能在文件夹ogrenew\PlugIns\GuiElements\Include中被编译器(compiler)找到。现在你可以包含它:

#include "OgreButtonGuiElement.h"

为使这个button工作,一个ActionListener必须被注册到它上面,这可以如下完成:

ActionTarget* thisButton = static_cast(

GuiManager::getSingleton().getGuiElement("myButton"));

thisButton->addActionListener(this);

此例中假设‘this’是一个ActionListener(默认不是这样子的)。当然任何ActionListener都可以,不必是‘this’。你可以通过继承ActionListener来使一个类是一个ActionListener并执行actionPerformed()。这个过程类似这样:

class Listener: public ActionListener

{

public:

void actionPerformed(ActionEvent *e);

};

如何找出哪个按钮被按下

这一条已经过时并由cegui方案来替代。
如果你注册一个ActionListener给几个button,它们会调用同一个函数actionPerformed()。你可以通过比较它的名字与ActionEvent e的名字来找出是哪个button被按下。如下:

#include

void actionPerformed(ActionEvent *e)

{

std::string action = e->getActionCommand();

if (action == "myButton")

{

//handle the button-press

}

}

如何使用ActionListener退出应用程序

这个条目已经过时,能用cegui方案代替。
退出应用程序可以在FrameListener中的frameStarted()函数和frameEnded()函数完成,而不是在actionPerformed()函数中。那么我们要怎么做呢?为此我们可以介绍一个简单的FrameListener,如果要求退出它只作退出而不做其他任务。这个FrameListener看起来如下(来自于Ogre中提供的Gui-demo)

#include "ExampleFrameListener.h"

class QuitListener: public ExampleFrameListener

{

public:

QuitListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam, false, false)

{

quit = false;

}

bool frameStarted(const FrameEvent& evt)

{

if (quit)

return false;

return true;

}

void scheduleQuit(void)

{

quit = true;

}

protected:

bool quit;

};

至此如果你有一个指向QuitListener的指针,你可以通过调用scheduleQuit()来确定退出时间并且QuitListener会在下一帧开始前执行它。

如何获得一个不同的SceneManager

场景是通过一个指示是哪种场景类型的标志符来自动选择的。这个过程是在ExampleApplication中完成的,因此要改变它,你必须改变ExampleApplication。通常,ExampleApplication包含下面的代码:

virtual void chooseSceneManager(void)

{

// Get the SceneManager, in this case a generic one

mSceneMgr = mRoot->getSceneManager(ST_GENERIC);

}

你可以更改ST_GENERIC为下面的任意标志符来获得一个适应你的特殊场景的场景管理器(SceneManager)

ST_GENERIC

ST_EXTERIOR_CLOSE

ST_EXTERIOR_FAR

ST_EXTERIOR_REAL_FAR

ST_INTERIOR

如何有效地获得所有可能碰撞的列表

Ogre可以为你提供一个可能碰撞的所有对象的列表。不在碰撞中的封闭的对象也在这个列表中,所以然后你须自己确定哪一个是碰撞对象。你可以用IntersectionSceneQuery来寻找这个碰撞列表,可以用下面的代码来得到:

IntersectionSceneQuery* intersectionQuery = sceneMgr->createIntersectionQuery();

现在你可以找到一个所有可能碰撞的列表:

IntersectionSceneQueryResult& queryResult = intersectionQuery->execute();

如果你想在更形场景前得到几次这个列表,你没必要让Ogre一次次地计算。你可以用下面的代码不用新的计算而再次得到相同的列表:

IntersectionSceneQueryResult& queryResult = intersectionQuery->getLastResults();

使用后,你可以存储IntersectionSceneQuery为以后使用或移除它。如果移除它,你须用下面的代码告诉SceneManager来完成:

mSceneMgr->destroyQuery(intersectionQuery);

IntersectionSceneQueryResult是一个可移动对象对(pairs of MovableObjects)的列表。它的应用的一个例子是获得发生第一次碰撞的两个对象的名字:

queryResult.movables2movables.begin()->first->getName();

有关结果类型的详细资料参看Ogre-API

如何找出一个MoveableObject属于你的哪个对象

IntersectionSceneQuery传递过来的碰撞对象列表是可移动对象(MovableObject)的,但是一般来说你可能想把这些关联到你自己的场景中的自定义对象。为此,你可以把指针连接到你自己的object中指向一个MovableObject,稍后取回指针。如果你有一个Entity(是一个MovableObject)和一个自定义object(就像下面的myRocket),你可以把你的自定义object挂到Entity上,如下:

Entity* myEntity = sceneMgr->createEntity("myEntity", "Rocket.mesh");

Rocket* myRocket = new Rocket();

myEntity->setUserObject(myRocket);

现在你可以用下面的代码从Entity中得到一个指向myRocket的指针:

myEntity->getUserobject();

为了使你自定义的类工作,它必须继承自UseDefinedObject。这还允许你找出返回的object是哪种class,这样你可以cast it back。现在Rocket的定义是这样子:

class Rocket: public UserDefinedObject

{

public:

const string& getTypeName(void) const

{

static const string typeName = "Rocket";

return typeName;

}

};

在检测出哪个MovableObjects与碰撞相关后,你也可以找出你自己的哪些objects与碰撞相关。

如何从碰撞检测中排除对象

你可以使用标记(flag)来从碰撞检测(collision detection)中排除某些物体。你可以给任何可移动物体添加标记,比如下面例子中的100:

Entity* ground = mSceneMgr->createEntity("ground", "Ground.mesh");

ground->setQueryFlags(100);

现在你可以告诉你的IntersectionSceneQuery来排除物体用这个标记从它的intersections列表中通过设置它的mask。如下所示:

IntersectionSceneQuery* intersectionQuery = sceneMgr->createIntersectionQuery();

intersectionQuery->setQueryMask(~100);