1、NO64OGRE中级教程4OGRE中级教程四From OGRE 3D 中文Jump to: navigation, search目录 1 介绍 2 先决条件 3 ManualObject对象 3.1 3D对象的快速入门 3.2 介绍 3.3 代码 4 体积选取 4.1 设置 4.2 鼠标处理 4.3 PlaneBoundedVolumeListSceneQuery 5 最后关于包围盒的注意事项 介绍在这一课里,我们将涉及如何进行体积选取。意思就是,当你在屏幕上点击并且拖拽鼠标时,一个白色矩形会追踪你正在选择的区域。当鼠标移动时,所有在选择区域里的物体都会被高亮。为了实现它,我们将学习两种对象:
2、ManualObject(创建矩形)和PlaneBoundedVolumeListSceneQuery。注意,当我们涉及ManualObject的基本用法时,只是对它的简单介绍,而不是教你如何完全用它创建3D物体。我们只会涉及我们所需要的。 你能在这里找到本课的代码。当你学习本课时,你应该逐个地往你的工程里添加代码,编译后观察相应的结果。 先决条件用你喜欢的IDE创建一个cpp,并添加以下代码: #include #include #include ExampleApplication.h class SelectionRectangle: public ManualObject public
3、: SelectionRectangle(const String &name) : ManualObject(name) /* * Sets the corners of the SelectionRectangle. Every parameter should be in the * range 0, 1 representing a percentage of the screen the SelectionRectangle * should take up. */ void setCorners(float left, float top, float right, float b
4、ottom) void setCorners(const Vector2 &topLeft, const Vector2 &bottomRight) setCorners(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); ; class DemoListener: public ExampleFrameListener, public OIS:MouseListener public: DemoListener(RenderWindow* win, Camera* cam, SceneManager *sceneManager) : Ex
5、ampleFrameListener(win, cam, false, true), mSceneMgr(sceneManager), mSelecting(false) mMouse-setEventCallback(this); / DemoListener DemoListener() /* MouseListener callbacks. */ bool mouseMoved(const OIS:MouseEvent &arg) CEGUI:System:getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);
6、return true; bool mousePressed(const OIS:MouseEvent &arg, OIS:MouseButtonID id) return true; bool mouseReleased(const OIS:MouseEvent &arg, OIS:MouseButtonID id) return true; void performSelection(const Vector2 &first, const Vector2 &second) void deselectObjects() std:list:iterator itr; for (itr = mS
7、elected.begin(); itr!= mSelected.end(); +itr) (*itr)-getParentSceneNode()-showBoundingBox(false); void selectObject(MovableObject *obj) obj-getParentSceneNode()-showBoundingBox(true); mSelected.push_back(obj); private: Vector2 mStart, mStop; SceneManager *mSceneMgr; PlaneBoundedVolumeListSceneQuery
8、*mVolQuery; std:list mSelected; SelectionRectangle *mRect; bool mSelecting; static void swap(float &x, float &y) float tmp = x; x = y; y = tmp; ; class DemoApplication: public ExampleApplication public: DemoApplication() : mRenderer(0), mSystem(0) DemoApplication() if (mSystem) delete mSystem; if (m
9、Renderer) delete mRenderer; protected: CEGUI:OgreCEGUIRenderer *mRenderer; CEGUI:System *mSystem; void createScene(void) mRenderer = new CEGUI:OgreCEGUIRenderer(mWindow, Ogre:RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr); mSystem = new CEGUI:System(mRenderer); CEGUI:SchemeManager:getSingleton().load
10、Scheme(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 (int i = 0; i 10; +i) for (int j = 0; j createEnti
11、ty(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); void createFrameListener(void) mFrameListener = new DemoListener(mWindow, mCamera, mScen
12、eMgr); mFrameListener-showDebugOverlay(true); mRoot-addFrameListener(mFrameListener); ; #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 / Create ap
13、plication object DemoApplication app; try app.go(); catch(Exception& e) #if OGRE_PLATFORM = OGRE_PLATFORM_WIN32 MessageBoxA(NULL, e.getFullDescription().c_str(), An exception has occurred!, MB_OK | MB_ICONERROR | MB_TASKMODAL); #else fprintf(stderr, An exception has occurred: %sn, e.getFullDescripti
14、on().c_str(); #endif return 0; 继续之前,请确保这段代码能够编译。当你运行它时,你应该能够移动鼠标指针,但程序目前不能做其它的。按ESC退出。 ManualObject对象3D对象的快速入门在我们开始进入制作网格(mesh)之前,有必要讲一下mesh是什么,以及它的构成。尽管非常简化,mesh大致由两部分组成:顶点缓存(Vertex buffer)和索引缓存(Index buffer)。 顶点缓存在3D空间里定义点集。顶点缓存里的每一个元素由若干你能设置的属性来定义。唯一一个你必须设置的属性就是顶点的坐标。除了这个,你还有设置其它可选的属性,比如顶点颜色、纹理坐标
15、等。你实际需要的属性取决于mesh的用途。 索引缓存通过从顶点缓存选取顶点,以“把点连起来”。索引缓存里每三个顶点定义了一个由GPU绘制的三角形。你在索引缓存里选取顶点的顺序,告诉了显卡这个三角形的朝向。逆时针绘制的三角形是朝向你的,顺时针绘制的三角形是背向你的。一般情况下只有三角形的正面才被绘制,所以确保你的三角形被正确载入是很重要的。 虽然所有的mesh都有顶点缓存,但不一定都有索引缓存。比如,我们只要创建一个空的三角形(而不是实心的),我们创建的mesh就不需要索引缓存。最后要注意,顶点缓存和索引缓存通常保存在显卡自己的内存里,所以你的软件只要发送一些离散的命令,告诉它使用预定义的缓存来
16、一口气渲染整个3D网格。 介绍在Ogre里有两种方法来创建你自己的网格。第一种是继承SimpleRenderable,并直接提供给它顶点和索引缓存。这是最直接的创建方式,但也是最不直观的。为了使事情更简单,Ogre提供一个更棒的接口叫做ManualObject,它能让你用一些简单的函数来定义一个网格,而不用往缓存里写原始数据。你仅仅调用position和colour函数,而不用往缓存里丢位置、颜色等数据。 在本课里,当我们拖动鼠标去选择物体时,我们要创建并显示一个白色矩形。在Ogre里并没有真正的用来显示2D矩形的类。我们必须自己找一个解决办法。我们可以使用一个Overlay并缩放它,以显示一
17、个矩形选择框,但这样做带来的问题是,选择框的图像可能会随着拉升而难看变形。取而代之,我们将生成一个非常简单的2D网格,来作为我们的选择矩形。 代码当我们创建选择矩形的时候,我们想让它以2D的形式呈现。我们还想保证当在屏幕里发生重叠时,它显示在所有其它物体之上。实现这个非常简单。找到SelectionRectangle的构造器,并添加如下代码: setRenderQueueGroup(RENDER_QUEUE_OVERLAY); setUseIdentityProjection(true); setUseIdentityView(true);第一个函数把这个物体的渲染队列设置成重叠队列(Over
18、lay queue)。接下来的两个函数把投影矩阵(projection matrix)和视图矩阵(view matrix)设置成identity。投影矩阵和视图矩阵被很多渲染系统所使用(比如OpenGL和DirectX),以定义物体在世界中的坐标。既然Ogre为我们做了抽象,我们不必深究这些矩阵是什么样的或他们干了些什么。然而,你需要知道如果你把投影矩阵和视图矩阵设置成identity,就像刚才那样,我们基本上就是在绘制2D物体。这样定义之后,坐标系统发生了一些改变。我们不再需要Z轴(若你被要求提供Z轴,设置成-1)。取而代之,我们有一个新的坐标系统,X和Y的范围分别都是-1到1。最后,我们将
19、把这个物体的查询标记设置成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 =
20、 1 - bottom * 2;现在转换成新坐标系统了。下面,我们来真正创建这个对象。为此,我们首先调用begin方法。它需要两个参数,物体的这一部分所使用的材质,以及它所使用的渲染操作。因为我们不使用纹理,把这个材质置空。第二个参数是渲染操作(RenderOperation)。我们可以使用点、线、三角形来渲染这个网格。如果我们要渲染一个实心的网格,可以用三角形。但我们只需要一个空的矩形,所以我们使用线条(line strip)。从你定义的前一个顶点到现在的顶点,线条绘制一条直线。所以为了创建我们的矩形,需要定义5个点(第一个和最后一个是相同的,这样才能连接成整个矩形): clear(); b
21、egin(, 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,因为我们
22、只定义一个2D对象而不必使用Z轴。把它设置为-1,可以保证当渲染时我们不处在摄像机之上或之后。 最后我们还要为这个物体设置包围盒。许多场景管理器会把远离屏幕的物体剔除掉。尽管我们创建的差不多是一个2D物体,但Ogre仍是一个3D引擎,它把2D物体当作在3D空间里对待。这意味着,如果我们创建这个物体,并把它绑在场景节点上(正如我们下面要做的那样),当我们远一点观看时会消失。为了修正这个问题,我们将把这个物体的包围盒设置成无限大,这样摄像机就永远在它里面: AxisAlignedBox box; box.setInfinite(); setBoundingBox(box);请注意,我们在调用cle
23、ar()之后添加这段代码的。当每你调用ManualObject:clear,包围盒都会被重置,所以当你创建经常清空的ManualObject时要格外小心,每当你重新创建它的时候,也要重新设置包围盒。 好了,我们要为SelectionRectangle类所做的全部就是这些。继续下去之前请保证能编译你的代码,但目前还没有为程序添加功能。 体积选取设置在我们进入选取操作的代码之前,先来设置一些东西。首先,我们要创建一个SelectionRectangle类的实例,然后让SceneManager来为我们创建一个体积查询: mRect = new SelectionRectangle(Selection
24、 SelectionRectangle); mSceneMgr-getRootSceneNode()-createChildSceneNode()-attachObject(mRect); mVolQuery = mSceneMgr-createPlaneBoundedVolumeQuery(PlaneBoundedVolumeList();再来,我们要保证结束时帧监听器做一些清理。把下面的代码加到DemoListener: mSceneMgr-destroyQuery(mVolQuery); delete mRect;注意,我们让SceneManager为我们进行清理,而不是直接删除。 鼠标
25、处理我们要展示的特性是体积选取。这意味着当用户点击鼠标并拖拽时,屏幕里将绘制一个矩形。随着鼠标的移动,所有在矩形内的物体将被选取。首先,我们要处理鼠标的点击事件。我们要保存鼠标的起始位置,并且把SelectionRectangle设置成可见的。找到mousePressed函数并添加如下代码: if (id = OIS:MB_Left) CEGUI:MouseCursor *mouse = CEGUI:MouseCursor:getSingletonPtr(); mStart.x = mouse-getPosition().d_x / (float)arg.state.width; mStart
26、.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里加入以下代码: i
27、f (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 / (
28、float)arg.state.height; mRect-setCorners(mStart, mStop); 每当鼠标移动时,我们都调整mStop向量,这样我们就能轻松地使用setCorners成员函数了。编译并运行你的程序,现在你能用鼠标绘制一个矩形了。 PlaneBoundedVolumeListSceneQuery 现在,我们可以让SelectionRectangle正确地渲染了,我们还想执行一个体积选择。找到performSelection函数,并添加如下代码: float left = first.x, right = second.x, top = first.y, botto
29、m = second.y; if (left right) swap(left, right); if (top bottom) swap(top, bottom);在这段代码里,我们分别为left、right、top、botton变量赋予向量参数。if语句保证了我们实际的left和top值最小。(如果这个矩形是“反向”画出来的,意味着从右下角到左上角,我们就要进行这种交换。) 接下来,我们要检查并了解矩形区域的实际小大。如果这个矩形太小了,我们的创建平面包围体积的方法就会失败,并且导致选取太多或太少的物体。如果这个矩形小于屏幕的某个百分比,我们只将它返回而不执行这个选取。我随意地选择0.00
30、01作为取消查询的临界点,但在你的程序里你应该自己决定它的值。还有,在真实的应用里,你应该找到这个矩形的中心,并执行一个标准查询,而不是什么都不做: if (right - left) * (bottom - top) 0.0001) return;现在,我们进入了这个函数的核心,我们要执行这个查询本身。PlaneBoundedVolumeQueries使用平面来包围一个区域,所以所有在区域里的物体都被选取。我们将创建一个被五个平面包围的区域,它是朝向里面的。为了创建这些平面,我们建立了4条射线,每一条都是矩形的一个角产生的。一旦我们有四条射线, For this example we will build an area enclosed by five
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1