NO64OGRE中级教程4.docx
- 文档编号:6598803
- 上传时间:2023-05-10
- 格式:DOCX
- 页数:16
- 大小:24.17KB
NO64OGRE中级教程4.docx
《NO64OGRE中级教程4.docx》由会员分享,可在线阅读,更多相关《NO64OGRE中级教程4.docx(16页珍藏版)》请在冰点文库上搜索。
NO64OGRE中级教程4
OGRE中级教程四
FromOGRE3D中文
Jumpto:
navigation,search
目录
1介绍
2先决条件
3ManualObject对象
3.13D对象的快速入门
3.2介绍
3.3代码
4体积选取
4.1设置
4.2鼠标处理
4.3PlaneBoundedVolumeListSceneQuery
5最后关于包围盒的注意事项
介绍
在这一课里,我们将涉及如何进行体积选取。
意思就是,当你在屏幕上点击并且拖拽鼠标时,一个白色矩形会追踪你正在选择的区域。
当鼠标移动时,所有在选择区域里的物体都会被高亮。
为了实现它,我们将学习两种对象:
ManualObject(创建矩形)和PlaneBoundedVolumeListSceneQuery。
注意,当我们涉及ManualObject的基本用法时,只是对它的简单介绍,而不是教你如何完全用它创建3D物体。
我们只会涉及我们所需要的。
你能在这里找到本课的代码。
当你学习本课时,你应该逐个地往你的工程里添加代码,编译后观察相应的结果。
先决条件
用你喜欢的IDE创建一个cpp,并添加以下代码:
#include
#include
#include"ExampleApplication.h"
classSelectionRectangle :
publicManualObject
{
public:
SelectionRectangle(constString&name)
:
ManualObject(name)
{
}
/**
*SetsthecornersoftheSelectionRectangle.Everyparametershouldbeinthe
*range[0,1]representingapercentageofthescreentheSelectionRectangle
*shouldtakeup.
*/
voidsetCorners(floatleft,floattop,floatright,floatbottom)
{
}
voidsetCorners(constVector2&topLeft,constVector2&bottomRight)
{
setCorners(topLeft.x,topLeft.y,bottomRight.x,bottomRight.y);
}
};
classDemoListener :
publicExampleFrameListener,publicOIS:
:
MouseListener
{
public:
DemoListener(RenderWindow*win,Camera*cam,SceneManager*sceneManager)
:
ExampleFrameListener(win,cam,false,true),mSceneMgr(sceneManager),mSelecting(false)
{
mMouse->setEventCallback(this);
}//DemoListener
~DemoListener()
{
}
/*MouseListenercallbacks.*/
boolmouseMoved(constOIS:
:
MouseEvent&arg)
{
CEGUI:
:
System:
:
getSingleton().injectMouseMove(arg.state.X.rel,arg.state.Y.rel);
returntrue;
}
boolmousePressed(constOIS:
:
MouseEvent&arg,OIS:
:
MouseButtonIDid)
{
returntrue;
}
boolmouseReleased(constOIS:
:
MouseEvent&arg,OIS:
:
MouseButtonIDid)
{
returntrue;
}
voidperformSelection(constVector2&first,constVector2&second)
{
}
voiddeselectObjects()
{
std:
:
list
:
iteratoritr;
for(itr=mSelected.begin();itr !
=mSelected.end();++itr)
(*itr)->getParentSceneNode()->showBoundingBox(false);
}
voidselectObject(MovableObject*obj)
{
obj->getParentSceneNode()->showBoundingBox(true);
mSelected.push_back(obj);
}
private:
Vector2mStart,mStop;
SceneManager*mSceneMgr;
PlaneBoundedVolumeListSceneQuery*mVolQuery;
std:
:
list
SelectionRectangle*mRect;
boolmSelecting;
staticvoidswap(float&x,float&y)
{
floattmp=x;
x=y;
y=tmp;
}
};
classDemoApplication :
publicExampleApplication
{
public:
DemoApplication()
:
mRenderer(0),mSystem(0)
{
}
~DemoApplication()
{
if(mSystem)
deletemSystem;
if(mRenderer)
deletemRenderer;
}
protected:
CEGUI:
:
OgreCEGUIRenderer*mRenderer;
CEGUI:
:
System*mSystem;
voidcreateScene(void)
{
mRenderer=newCEGUI:
:
OgreCEGUIRenderer(mWindow,Ogre:
:
RENDER_QUEUE_OVERLAY,false,3000,mSceneMgr);
mSystem=newCEGUI:
:
System(mRenderer);
CEGUI:
:
SchemeManager:
:
getSingleton().loadScheme((CEGUI:
:
utf8*)"TaharezLookSkin.scheme");
CEGUI:
:
MouseCursor:
:
getSingleton().setImage((CEGUI:
:
utf8*)"TaharezLook",(CEGUI:
:
utf8*)"MouseArrow");
mCamera->setPosition(-60,100,-60);
mCamera->lookAt(60,0,60);
mSceneMgr->setAmbientLight(ColourValue:
:
White);
for(inti=0;i<10;++i)
for(intj=0;j<10;++j)
{
Entity*ent=mSceneMgr->createEntity("Robot"+StringConverter:
:
toString(i+j*10),"robot.mesh");
SceneNode*node=mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(i*15,0,j*15));
node->attachObject(ent);
node->setScale(0.1,0.1,0.1);
}
}
voidcreateFrameListener(void)
{
mFrameListener=newDemoListener(mWindow,mCamera,mSceneMgr);
mFrameListener->showDebugOverlay(true);
mRoot->addFrameListener(mFrameListener);
}
};
#ifOGRE_PLATFORM==OGRE_PLATFORM_WIN32
#defineWIN32_LEAN_AND_MEAN
#include"windows.h"
INTWINAPIWinMain(HINSTANCEhInst,HINSTANCE,LPSTRstrCmdLine,INT)
#else
intmain(intargc,char**argv)
#endif
{
//Createapplicationobject
DemoApplicationapp;
try{
app.go();
}catch(Exception&e){
#ifOGRE_PLATFORM==OGRE_PLATFORM_WIN32
MessageBoxA(NULL,e.getFullDescription().c_str(),"Anexceptionhasoccurred!
",
MB_OK|MB_ICONERROR|MB_TASKMODAL);
#else
fprintf(stderr,"Anexceptionhasoccurred:
%s\n",
e.getFullDescription().c_str());
#endif
}
return0;
}
继续之前,请确保这段代码能够编译。
当你运行它时,你应该能够移动鼠标指针,但程序目前不能做其它的。
按ESC退出。
ManualObject对象
3D对象的快速入门
在我们开始进入制作网格(mesh)之前,有必要讲一下mesh是什么,以及它的构成。
尽管非常简化,mesh大致由两部分组成:
顶点缓存(Vertexbuffer)和索引缓存(Indexbuffer)。
顶点缓存在3D空间里定义点集。
顶点缓存里的每一个元素由若干你能设置的属性来定义。
唯一一个你必须设置的属性就是顶点的坐标。
除了这个,你还有设置其它可选的属性,比如顶点颜色、纹理坐标等。
你实际需要的属性取决于mesh的用途。
索引缓存通过从顶点缓存选取顶点,以“把点连起来”。
索引缓存里每三个顶点定义了一个由GPU绘制的三角形。
你在索引缓存里选取顶点的顺序,告诉了显卡这个三角形的朝向。
逆时针绘制的三角形是朝向你的,顺时针绘制的三角形是背向你的。
一般情况下只有三角形的正面才被绘制,所以确保你的三角形被正确载入是很重要的。
虽然所有的mesh都有顶点缓存,但不一定都有索引缓存。
比如,我们只要创建一个空的三角形(而不是实心的),我们创建的mesh就不需要索引缓存。
最后要注意,顶点缓存和索引缓存通常保存在显卡自己的内存里,所以你的软件只要发送一些离散的命令,告诉它使用预定义的缓存来一口气渲染整个3D网格。
介绍
在Ogre里有两种方法来创建你自己的网格。
第一种是继承SimpleRenderable,并直接提供给它顶点和索引缓存。
这是最直接的创建方式,但也是最不直观的。
为了使事情更简单,Ogre提供一个更棒的接口叫做ManualObject,它能让你用一些简单的函数来定义一个网格,而不用往缓存里写原始数据。
你仅仅调用"position"和"colour"函数,而不用往缓存里丢位置、颜色等数据。
在本课里,当我们拖动鼠标去选择物体时,我们要创建并显示一个白色矩形。
在Ogre里并没有真正的用来显示2D矩形的类。
我们必须自己找一个解决办法。
我们可以使用一个Overlay并缩放它,以显示一个矩形选择框,但这样做带来的问题是,选择框的图像可能会随着拉升而难看变形。
取而代之,我们将生成一个非常简单的2D网格,来作为我们的选择矩形。
代码
当我们创建选择矩形的时候,我们想让它以2D的形式呈现。
我们还想保证当在屏幕里发生重叠时,它显示在所有其它物体之上。
实现这个非常简单。
找到SelectionRectangle的构造器,并添加如下代码:
setRenderQueueGroup(RENDER_QUEUE_OVERLAY);
setUseIdentityProjection(true);
setUseIdentityView(true);
第一个函数把这个物体的渲染队列设置成重叠队列(Overlayqueue)。
接下来的两个函数把投影矩阵(projectionmatrix)和视图矩阵(viewmatrix)设置成identity。
投影矩阵和视图矩阵被很多渲染系统所使用(比如OpenGL和DirectX),以定义物体在世界中的坐标。
既然Ogre为我们做了抽象,我们不必深究这些矩阵是什么样的或他们干了些什么。
然而,你需要知道如果你把投影矩阵和视图矩阵设置成identity,就像刚才那样,我们基本上就是在绘制2D物体。
这样定义之后,坐标系统发生了一些改变。
我们不再需要Z轴(若你被要求提供Z轴,设置成-1)。
取而代之,我们有一个新的坐标系统,X和Y的范围分别都是-1到1。
最后,我们将把这个物体的查询标记设置成0,如下:
setQueryFlags(0);
现在,对象设置好了,我们来实际构建这个矩形。
我们开始之前还有一个小小阻碍,我们将使用鼠标坐标来调用这个函数。
也就是,传给我们一个0到1之间的数字为每个坐标轴,然而我们需要把这个数字转换成范围[-1,1]的。
还有更复杂的,y坐标要反向。
在CEGUI里,鼠标指针在屏幕顶部时,值为+1,在底部时,值为-1。
感谢上帝,用一个快速转换就能解决这个问题。
找到setCorners函数并添加如下代码:
left=left*2-1;
right=right*2-1;
top=1-top*2;
bottom=1-bottom*2;
现在转换成新坐标系统了。
下面,我们来真正创建这个对象。
为此,我们首先调用begin方法。
它需要两个参数,物体的这一部分所使用的材质,以及它所使用的渲染操作。
因为我们不使用纹理,把这个材质置空。
第二个参数是渲染操作(RenderOperation)。
我们可以使用点、线、三角形来渲染这个网格。
如果我们要渲染一个实心的网格,可以用三角形。
但我们只需要一个空的矩形,所以我们使用线条(linestrip)。
从你定义的前一个顶点到现在的顶点,线条绘制一条直线。
所以为了创建我们的矩形,需要定义5个点(第一个和最后一个是相同的,这样才能连接成整个矩形):
clear();
begin("",RenderOperation:
:
OT_LINE_STRIP);
position(left,top,-1);
position(right,top,-1);
position(right,bottom,-1);
position(left,bottom,-1);
position(left,top,-1);
end();
注意,因为我们将在后面多次调用它,我们在最前面加入clear函数,在重新绘制矩形之前移除上次的矩形。
当定义一个手动物体时,你可能要多次调用begin/end来创建多个子网格(它们可能有不同的材质/渲染操作)。
注意,我们把Z参数设成-1,因为我们只定义一个2D对象而不必使用Z轴。
把它设置为-1,可以保证当渲染时我们不处在摄像机之上或之后。
最后我们还要为这个物体设置包围盒。
许多场景管理器会把远离屏幕的物体剔除掉。
尽管我们创建的差不多是一个2D物体,但Ogre仍是一个3D引擎,它把2D物体当作在3D空间里对待。
这意味着,如果我们创建这个物体,并把它绑在场景节点上(正如我们下面要做的那样),当我们远一点观看时会消失。
为了修正这个问题,我们将把这个物体的包围盒设置成无限大,这样摄像机就永远在它里面:
AxisAlignedBoxbox;
box.setInfinite();
setBoundingBox(box);
请注意,我们在调用clear()之后添加这段代码的。
当每你调用ManualObject:
:
clear,包围盒都会被重置,所以当你创建经常清空的ManualObject时要格外小心,每当你重新创建它的时候,也要重新设置包围盒。
好了,我们要为SelectionRectangle类所做的全部就是这些。
继续下去之前请保证能编译你的代码,但目前还没有为程序添加功能。
体积选取
设置
在我们进入选取操作的代码之前,先来设置一些东西。
首先,我们要创建一个SelectionRectangle类的实例,然后让SceneManager来为我们创建一个体积查询:
mRect=newSelectionRectangle("SelectionSelectionRectangle");
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(mRect);
mVolQuery=mSceneMgr->createPlaneBoundedVolumeQuery(PlaneBoundedVolumeList());
再来,我们要保证结束时帧监听器做一些清理。
把下面的代码加到~DemoListener:
mSceneMgr->destroyQuery(mVolQuery);
deletemRect;
注意,我们让SceneManager为我们进行清理,而不是直接删除。
鼠标处理
我们要展示的特性是体积选取。
这意味着当用户点击鼠标并拖拽时,屏幕里将绘制一个矩形。
随着鼠标的移动,所有在矩形内的物体将被选取。
首先,我们要处理鼠标的点击事件。
我们要保存鼠标的起始位置,并且把SelectionRectangle设置成可见的。
找到mousePressed函数并添加如下代码:
if(id==OIS:
:
MB_Left)
{
CEGUI:
:
MouseCursor*mouse=CEGUI:
:
MouseCursor:
:
getSingletonPtr();
mStart.x=mouse->getPosition().d_x/(float)arg.state.width;
mStart.y=mouse->getPosition().d_y/(float)arg.state.height;
mStop=mStart;
mSelecting=true;
mRect->clear();
mRect->setVisible(true);
}
注意,我们使用的是CEGUI:
:
MouseCursor的x和y坐标,而不是OIS的鼠标坐标。
这是因为有时OIS反映的坐标与CEGUI实际显示的不一样。
为了保证我们与用户所看到的相一致,我们使用CEGUI的鼠标坐标。
接下来我们要做的是,当用户释放鼠标按钮时,停止显示选择框,并执行这个选取查询。
在mouseReleased里加入以下代码:
if(id==OIS:
:
MB_Left)
{
performSelection(mStart,mStop);
mSelecting=false;
mRect->setVisible(false);
}
最后,每当鼠标移动时,我们需要更新矩形的坐标:
if(mSelecting)
{
CEGUI:
:
MouseCursor*mouse=CEGUI:
:
MouseCursor:
:
getSingletonPtr();
mStop.x=mouse->getPosition().d_x/(float)arg.state.width;
mStop.y=mouse->getPosition().d_y/(float)arg.state.height;
mRect->setCorners(mStart,mStop);
}
每当鼠标移动时,我们都调整mStop向量,这样我们就能轻松地使用setCorners成员函数了。
编译并运行你的程序,现在你能用鼠标绘制一个矩形了。
PlaneBoundedVolumeListSceneQuery
现在,我们可以让SelectionRectangle正确地渲染了,我们还想执行一个体积选择。
找到performSelection函数,并添加如下代码:
floatleft=first.x,right=second.x,
top=first.y,bottom=second.y;
if(left>right)
swap(left,right);
if(top>bottom)
swap(top,bottom);
在这段代码里,我们分别为left、right、top、botton变量赋予向量参数。
if语句保证了我们实际的left和top值最小。
(如果这个矩形是“反向”画出来的,意味着从右下角到左上角,我们就要进行这种交换。
)
接下来,我们要检查并了解矩形区域的实际小大。
如果这个矩形太小了,我们的创建平面包围体积的方法就会失败,并且导致选取太多或太少的物体。
如果这个矩形小于屏幕的某个百分比,我们只将它返回而不执行这个选取。
我随意地选择0.0001作为取消查询的临界点,但在你的程序里你应该自己决定它的值。
还有,在真实的应用里,你应该找到这个矩形的中心,并执行一个标准查询,而不是什么都不做:
if((right-left)*(bottom-top)<0.0001)
return;
现在,我们进入了这个函数的核心,我们要执行这个查询本身。
PlaneBoundedVolumeQueries使用平面来包围一个区域,所以所有在区域里的物体都被选取。
我们将创建一个被五个平面包围的区域,它是朝向里面的。
为了创建这些平面,我们建立了4条射线,每一条都是矩形的一个角产生的。
一旦我们有四条射线,
Forthisexamplewewillbuildanareaenclosedbyfive
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NO64OGRE 中级 教程