NO64OGRE中级教程4.docx

上传人:b****5 文档编号:11797540 上传时间:2023-04-02 格式:DOCX 页数:16 大小:24.17KB
下载 相关 举报
NO64OGRE中级教程4.docx_第1页
第1页 / 共16页
NO64OGRE中级教程4.docx_第2页
第2页 / 共16页
NO64OGRE中级教程4.docx_第3页
第3页 / 共16页
NO64OGRE中级教程4.docx_第4页
第4页 / 共16页
NO64OGRE中级教程4.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

NO64OGRE中级教程4.docx

《NO64OGRE中级教程4.docx》由会员分享,可在线阅读,更多相关《NO64OGRE中级教程4.docx(16页珍藏版)》请在冰豆网上搜索。

NO64OGRE中级教程4.docx

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:

:

listmSelected;

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1