Ogre2中级教材.docx
- 文档编号:23883430
- 上传时间:2023-05-21
- 格式:DOCX
- 页数:17
- 大小:22.63KB
Ogre2中级教材.docx
《Ogre2中级教材.docx》由会员分享,可在线阅读,更多相关《Ogre2中级教材.docx(17页珍藏版)》请在冰豆网上搜索。
Ogre2中级教材
文档:
教程:
中级教程:
中级教程二
出自Ogre3D开放资源地带
跳转到:
导航,搜索
中级教程2:
射线场景查询及基础鼠标用法
有关这篇教程,无论遇到任何问题,都可以到论坛发帖寻求帮助。
目录
[隐藏]
∙1介绍
∙2前期准备
∙3开始
∙4创建场景
∙5帧监听器介绍
∙6创建帧监听器
∙7增加鼠标查看
∙8地形碰撞检测
∙9地形选择
∙10进阶练习
o10.1简单练习
o10.2中级练习
o10.3高级练习
o10.4进阶练习
介绍
本教程中,我们会初步创建一个基础场景编辑器。
在过程之中,我们会涉及到:
1.如何使用RaySceneQueries阻止镜头穿透地面
2.如何使用MouseListener和MouseMotionListener接口
3.使用鼠标选取地面上的x和y坐标
你可以在这里找到完整代码。
跟随着教程,你会慢慢地向你自己的工程项目中增加代码,并且随着编译看到结果。
前期准备
本教程假设你已经知道了如何创建Ogre工程,并且可以成功编译。
假设你已经了解了基本的Ogre对象(场景节点,实体,等等)。
你也应该熟悉STL迭代器基本的使用方法,因为本教程会用到。
(Ogre也大量用到STL,如果你还不熟悉STL,那么你需要花些时间学习一下。
)
开始
首先,你需要为此演示程序创建一个新工程。
在创建工程时,选空工程、自己的框架,以及初始化进度条和CEGUI支持,不选编译后拷贝。
向工程中,增加一个名叫“MouseQuery.cpp”的文件,并向其中添加如下代码:
#include
#include
#include
#include"ExampleApplication.h"
classMouseQueryListener :
publicExampleFrameListener,publicOIS:
:
MouseListener
{
public:
MouseQueryListener(RenderWindow*win,Camera*cam,SceneManager*sceneManager,CEGUI:
:
Renderer*renderer)
:
ExampleFrameListener(win,cam,false,true),mGUIRenderer(renderer)
{
}//MouseQueryListener
~MouseQueryListener()
{
}
boolframeStarted(constFrameEvent&evt)
{
returnExampleFrameListener:
:
frameStarted(evt);
}
/*MouseListenercallbacks.*/
boolmouseMoved(constOIS:
:
MouseEvent&arg)
{
returntrue;
}
boolmousePressed(constOIS:
:
MouseEvent&arg,OIS:
:
MouseButtonIDid)
{
returntrue;
}
boolmouseReleased(constOIS:
:
MouseEvent&arg,OIS:
:
MouseButtonIDid)
{
returntrue;
}
protected:
RaySceneQuery*mRaySceneQuery;//Therayscenequerypointer
boolmLMouseDown,mRMouseDown;//Trueifthemousebuttonsaredown
intmCount;//Thenumberofrobotsonthescreen
SceneManager*mSceneMgr;//Apointertothescenemanager
SceneNode*mCurrentObject;//Thenewlycreatedobject
CEGUI:
:
Renderer*mGUIRenderer;//CEGUIrenderer
};
classMouseQueryApplication :
publicExampleApplication
{
protected:
CEGUI:
:
OgreCEGUIRenderer*mGUIRenderer;
CEGUI:
:
System*mGUISystem;//ceguisystem
public:
MouseQueryApplication()
{
}
~MouseQueryApplication()
{
}
protected:
voidchooseSceneManager(void)
{
//Usetheterrainscenemanager.
mSceneMgr=mRoot->createSceneManager(ST_EXTERIOR_CLOSE);
}
voidcreateScene(void)
{
}
voidcreateFrameListener(void)
{
mFrameListener=newMouseQueryListener(mWindow,mCamera,mSceneMgr,mGUIRenderer);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
};
#ifOGRE_PLATFORM==PLATFORM_WIN32||OGRE_PLATFORM==OGRE_PLATFORM_WIN32
#defineWIN32_LEAN_AND_MEAN
#include"windows.h"
INTWINAPIWinMain(HINSTANCEhInst,HINSTANCE,LPSTRstrCmdLine,INT)
#else
intmain(intargc,char**argv)
#endif
{
//Createapplicationobject
MouseQueryApplicationapp;
try{
app.go();
}catch(Exception&e){
#ifOGRE_PLATFORM==OGRE_PLATFORM_WIN32
MessageBox(NULL,e.getFullDescription().c_str(),"Anexceptionhasoccurred!
",MB_OK|MB_ICONERROR|MB_TASKMODAL);
#else
fprintf(stderr,"Anexceptionhasoccurred:
%s\n",
e.getFullDescription().c_str());
#endif
}
return0;
}
在继续下面教程以前,先确保上面代码可以正常编译。
创建场景
找到MouseQueryApplication:
:
createScene方法。
下面的代码应该都很熟悉了。
如果你不知道其中某些是做什么用的,请在继续本教程前,参考OgreAPI。
向createScene中,增加如下代码:
//Setambientlight
mSceneMgr->setAmbientLight(ColourValue(0.5,0.5,0.5));
mSceneMgr->setSkyDome(true,"Examples/CloudySky",5,8);
//Worldgeometry
mSceneMgr->setWorldGeometry("terrain.cfg");
//Setcameralookpoint
mCamera->setPosition(40,100,580);
mCamera->pitch(Degree(-30));
mCamera->yaw(Degree(-45));
既然我们建立了基本的世界空间,那么就要打开光标。
打开光标,要使用CEGUI函数调用。
不过在此之前,我们需要启用CEGUI。
我们首先创建一个OgreCEGUIRenderer,然后创建系统对象并将刚创建的Renderer传给它。
创建CEGUI我们会专门留待后续教程介绍,现在只要知道创建mGUIRenderer时必须以最后一个参数告诉CEGUI你要用那个场景管理器。
//CEGUIsetup
mGUIRenderer=newCEGUI:
:
OgreCEGUIRenderer(mWindow,Ogre:
:
RENDER_QUEUE_OVERLAY,false,3000,mSceneMgr);
mGUISystem=newCEGUI:
:
System(mGUIRenderer);
现在我们需要实际显示光标了。
同样地,我不打算过多解释这些代码。
我们会在后面的教程中详细介绍。
(其实也没什么,就是设置了一下CEGUI的窗口和鼠标的样式。
——Aaron注释)
//Mouse
CEGUI:
:
SchemeManager:
:
getSingleton().loadScheme((CEGUI:
:
utf8*)"TaharezLookSkin.scheme");
CEGUI:
:
MouseCursor:
:
getSingleton().setImage("TaharezLook","MouseArrow");
如果你编译并运行这个程序,你会发现一个光标出现在屏幕中央,但它还动不了。
帧监听器介绍
这是程序要做的全部事情。
FrameListener是代码中复杂的部分,所以我会花一些时间强调我们要完成的东西,以便在我们开始实现它之前,使你有一个大体的印象。
∙首先,我们想要将鼠标右键绑定到“鼠标观察”模式。
不能使用鼠标四下看看是相当郁闷的,所以我们首先对程序增加鼠标控制(尽管只是在我们保持鼠标右键按下时)。
∙第二,我们想要让镜头不会穿过地表。
这会使它更接近我们期望的样子。
∙第三,我们想要在地表上用鼠标左键点击一下,就在那里增加一个实体。
∙最后,我们想要能“拖拽”实体。
即选中我们想要看到的实体,按住鼠标左键不放,将它移动到我们想要放置的地方。
松开鼠标左键,就又会将它锁定在原地。
要做到这几点,我们要使用几个受保护的变量(这些已经加到类中了):
RaySceneQuery*mRaySceneQuery;//射线场景查询指针
boolmLMouseDown,mRMouseDown;//如果按下鼠标按钮,返回True
intmCount;//屏幕上机器人的数量
SceneManager*mSceneMgr;//指向场景管理器的指针
SceneNode*mCurrentObject;//新创建的物休
CEGUI:
:
Renderer*mGUIRenderer;//CEGUI渲染器
变量mRaySceneQuery握有RaySceneQuery的一个拷贝,我们会它来寻找地面上的坐标。
变量mLMouseDown和mRMouseDon会追踪我们是否按下鼠标键(例如:
如果按下鼠标左键,则mLMouseDown为true;否则,为false)。
mCount计数屏幕上有的实体数。
mCurrentObject握有指向最近创建的场景节点的指针(我们将用这个“拖拽”实体)。
最后,mGUIRenderer握有指向CEGUIRenderer的指针,我们将用它更新CEGUI。
还要注意的是,有许多和鼠标监听器相关的函数。
在本演示程序中,我们不会全部用到,但是它们必须全部在那儿,否则编译会报错说你没定义它们。
创建帧监听器
找到MouseQueryListener构造函数,增加如下初始化代码。
注意,由于地形相当小,所以我们也要减少移动和旋转速度。
//Setupdefaultvariables
mCount=0;
mCurrentObject=NULL;
mLMouseDown=false;
mRMouseDown=false;
mSceneMgr=sceneManager;
//Reducemovespeed
mMoveSpeed=50;
mRotateSpeed/=500;
为了MouseQueryListener能收到鼠标事件,我们必须把它注册为一个鼠标监听器。
如果对此不太熟悉,请参考基础教程5。
//Registerthissothatwegetmouseevents.
mMouse->setEventCallback(this);
最后,在构造函数中我们需要创建一个RaySceneQuery对象。
用场景管理器的一个调用创建:
//CreateRaySceneQuery
mRaySceneQuery=mSceneMgr->createRayQuery(Ray());
这是我们需要的全部构造函数了,但是如果我们创建一个RaySceneQuery,以后我们就必须销毁它。
找到MouseQueryListener析构函数(~MouseQueryListener),增加如下代码:
//Wecreatedthequery,andwearealsoresponsiblefordeletingit.
mSceneMgr->destroyQuery(mRaySceneQuery);
在进入下一阶段前,请确保你的代码可以正常编译。
增加鼠标查看
我们要将鼠标查看模式绑定到鼠标右键上,需要:
∙当鼠标被移动时,更新CEGUI(以便光标也移动)
∙当鼠标右键被按下时,设置mRMouseButton为true
∙当鼠标右键被松开时,设置mRMouseButton为false
∙当鼠标被“拖拽”时,改变视图
∙当鼠标被“拖拽”时,隐藏鼠标光标
找到MouseQueryListener:
:
mouseMoved方法。
我们将要增加代码使每次鼠标移动时移动鼠标光标。
向函数中增加代码:
//UpdateCEGUIwiththemousemotion
CEGUI:
:
System:
:
getSingleton().injectMouseMove(arg.state.X.rel,arg.state.Y.rel);
现在找到MouseQueryListener:
:
mousePressed方法。
这段代码当鼠标右键按下时,隐藏光标,并设置变量mRMouseDown为true。
//Leftmousebuttondown
if(id==OIS:
:
MB_Left)
{
mLMouseDown=true;
}//if
//Rightmousebuttondown
elseif(id==OIS:
:
MB_Right)
{
CEGUI:
:
MouseCursor:
:
getSingleton().hide();
mRMouseDown=true;
}//elseif
接下来,当鼠标右键抬起时,我们需要再次显示光标,并将mRMouseDown设置为false。
找到mouseReleased函数,增加如下代码:
//Leftmousebuttonup
if(id==OIS:
:
MB_Left)
{
mLMouseDown=false;
}//if
//Rightmousebuttonup
elseif(id==OIS:
:
MB_Right)
{
CEGUI:
:
MouseCursor:
:
getSingleton().show();
mRMouseDown=false;
}//elseif
现在,我们有了全部准备好的代码,我们想要在按住鼠标右键移动鼠标时改变视图。
我们要做的就是,读取他自上次调用方法后移动的距离。
这可以用与基础教程5中旋转摄像机镜头一样的方法实现。
找到TutorialFrameListener:
:
mouseMoved函数,就在返回状态前,增加如下代码:
//Ifwearedraggingtheleftmousebutton.
if(mLMouseDown)
{
}//if
//Ifwearedraggingtherightmousebutton.
elseif(mRMouseDown)
{
mCamera->yaw(Degree(-arg.state.X.rel*mRotateSpeed));
mCamera->pitch(Degree(-arg.state.Y.rel*mRotateSpeed));
}//elseif
现在如果你编译并运行这些代码,你将能够通过按住鼠标右键控制摄像机往哪里看。
地形碰撞检测
我们现在要实现它,以便当我们向着地面移动时,能够不穿过地面。
因为BaseFrameListener已经处理了摄像机移动,所以我们就不用碰那些代码了。
替代地,在BaseFrameListener移动了摄像机后,我们要确保摄像机在地面以上10个单位处。
如果它不在,我们要把它移到那儿。
请跟紧这段代码。
我们将在本教程结束前使用RaySceneQuery做几件别的事,而且在这段结束后,我不会再做如此详细的介绍。
找到MouseQueryListener:
:
frameStarted方法,移除该方法的全部代码。
我们首先要做的事是调用ExampleFrameListener:
:
frameStarted方法。
如果它返回false,则我们也会返回false。
//Processthebaseframelistenercode.Sincewearegoingtobe
//manipulatingthetranslatevector,weneedthistohappenfirst.
if(!
ExampleFrameListener:
:
frameStarted(evt))
returnfalse;
我们在frameStarted函数的最开始处做这些,是因为ExampleFrameListener的frameStarted成员函数移动摄像机,并且在此发生后我们需要在函数中安排我们的剩余行动。
我们的目标及时找到摄像机的当前位置,并沿着它向地面发射一条射线。
这被称为射线场景查询,它会告诉我们我们下面的地面的高度。
得到了摄像机的当前位置后,我们需要创建一条射线。
这条射线有一个起点(射线开始的地方),和一个方向。
在本教程的情况下,我们的方向是Y轴负向,因为我们指定射线一直向下。
一旦我们创建了射线,我们就告诉RaySceneQuery对象使用它。
//Setupthescenequery
Vector3camPos=mCamera->getPosition();
RaycameraRay(Vector3(camPos.x,5000.0f,camPos.z),Vector3:
:
NEGATIVE_UNIT_Y);
mRaySceneQuery->setRay(cameraRay);
注意,我们已经使用了5000.0f高度代替了摄像机的实际位置。
如果我们使用摄像机的Y坐标代替这个高度,如果摄像机在地面以下,我们会错过整个地面。
现在我们需要执行查询,得到结果。
查询结果是std:
:
iterator类型的。
//Performthescenequery
RaySceneQueryResult&result=mRaySceneQuery->execute();
RaySceneQueryResult:
:
iteratoritr=result.begin();
在本教程中的这个地形条件下,查询结果基本上是一个worldFragment的列表和一个可移动物体(稍后的教程会介绍到)的列表。
如果你对STL迭代器不太熟悉,只要知道调用begin方法获得迭代器的第一个元素。
如果result.begin()==result.end(),那么无返回结果。
在下一个演示程序里,我们将处理SceneQuery的多个返回值。
目前,我们只要挥挥手,在其间移动。
下面的这行代码保证了至少返回一个查询结果(itr !
=result.end()),那个结果是地面(itr->worldFragment)。
//Gettheresults,setthecameraheight
if(itr !
=result.end()&&itr->worldFragment)
{
worldFragment结构包含有在变量singleIntersection(一个Vector3)中射线击中地面的位置。
我们要得到地面的高度,依靠将这个向量的Y值赋值给一个本地变量。
一旦我们有了高度,我们就要检查摄像机是否低于这一高度,如果低于这一高度,那么我们要将摄像机向上移动至地面高度。
注意,我们实际将摄像机多移动了10个单位。
这样保证我们不能由于太靠近地面而看穿地面。
RealterrainHeight=itr->worldFragment->singleIntersection.y;
if((terrainHeight+10.0f)>camPos.y)
mCamera->setPosition(camPos.x,terrainHeight+10.0f,camPos.z);
}
returntrue;
最后,我们返回true,继续渲染。
此时,你应该编译测试你的程序了。
地形选择
在这部分中,每次点击鼠标左键,我们将向屏幕上创建和添加对象。
每次你点击、按住鼠标左键,就会创建一个对象并跟随你的光标。
你可以移动对象,直到你松开鼠标左键,同时对象也锁定在那一点上。
要做到这些,我们需要改变mousePressed函数。
在MouseQueryLlistener:
:
mousePressed函数中,找到如下代码。
我们将要在这个if语句中增加一些代码。
//Leftmousebuttondown
if(id==OIS:
:
MB_Left)
{
mLMouseDown=true;
}//if
第一段代码看起来会很熟悉。
我们会创建一条射线以供mRaySceneQuery对象使用,设置射线。
Ogre给我们提供了Camera:
:
getCameraToVie
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Ogre2 中级 教材
![提示](https://static.bdocx.com/images/bang_tan.gif)