转载:OGRE一起学(七)场景节点和实体
第七章 场景节点和实体
示例代码 :
点击下载本节示例代码
预备知识 :
OGRE 的场景组织方式,就像一棵树,树有根、有枝干、有叶子和果实。最基本的两个概念,是 场景节点 (SceneNode) 和 实体 (Entity) 。场景节点就像树枝,实体就像枝头的果实。我们可以画个概念图:
场景节点就像根、树干和树枝,它可以再分枝,或者挂上果实(实体); 实体就像果实,它总是被绑定 (Attach) 在一个场景节点上并且总是处于分枝的末端;
场景节点控制实体的位置、方向、缩放等空间属性; 实体具有自己所使用的模型、材质、阴影、骨骼、动画、渲染状态等各种个体属性;
整个场景节点拥有唯一的一个根场景节点(RootSceneNode),如果一个实体不是根场景节点的“后代”,也就是所谓的“游离”实体或被绑定在一个"游离"节点上的实体,它不会被显示。
场景节点类 Ogre:: SceneNode 在头文件 OgreSceneNode.h 中定义,下面是它的关联图表:
点击看大图
实体类 Ogre:: Entity 在头文件 OgreEntity.h 中定义,下面是它的关联图表:
点击看大图
示例解说 :
这个示例的核心代码,在源文件 SceneNodeAndEntity.cpp 的
void SceneNodeAndEntityApplication::createScene( void ) 方法定义中。
- (1)
// 设置环境光 mSceneMgr->setAmbientLight(ColourValue(1.0f, 1.0f, 1.0f));
mSceneMgr 是在公用示例基类 OGRE::ExampleApplication 中已经定义好的一个保护成员,作为场景管理器 (SceneManager) 的一个实例来使用。 场景管理器类 Ogre:: SceneManager 在头文件 OgreSceneManager.h 中定义,场景管理器,顾名思义,它是整个 OGRE 场景的总管,管理整个场景中的所有场景节点 (SceneNode) 、实体 (Entity) 、光源 (Light) 、镜头 (Camera) 、材质 (Material) 、阴影 (Shadow) 、公告板 (Billboard) 、动画 (Animation) 、天空 (Sky) 、雾 (Fog) 、盖层 (Overlay) 等等大量元素。下面是它的关联图表:
点击看大图
setAmbientLight() (设置环境光) 的作用是给整个场景设置一个统一的散射光,散射光没有光源,没有方向性,不会在物体表面留下阴影,仅仅是照亮场景。默认的环境光颜色是黑色,所以我们要设置一个较亮的颜色,要不模型看上去漆黑一团。
setAmbientLight() 方法的原型在头文件 OgreSceneManager.h 中定义:
void Ogre::SceneManager:: setAmbientLight ( const ColourValue & colour )
其中的 ColourValue (颜色值)是 OGRE 颜色类,在头文件 OgreColourValue.h 中定义:
ColourValue (Real red=1.0f, Real green=1.0f, Real blue=1.0f, Real alpha=1.0f)
除了直接用 RGBA 值来表示颜色, ColourValue 还具有几个预先设定好的颜色值:
ColourValue Black = ColourValue(0.0,0.0,0.0) ColourValue White = ColourValue(1.0,1.0,1.0) ColourValue Red = ColourValue(1.0,0.0,0.0) ColourValue Green = ColourValue(0.0,1.0,0.0) ColourValue Blue = ColourValue(0.0,0.0,1.0) // 设置环境光 mSceneMgr->setAmbientLight(ColourValue(1.0f, 1.0f, 1.0f));
现在你可以试着为 ColourValue 更换不同的颜色值来看看实际效果。
- (2)
// 获取场景根节点 SceneNode* rootNode = mSceneMgr->getRootSceneNode();
getRootSceneNode() (获取根节点) 的作用是获取并返回当前场景的根节点的对象指针,它作为 SceneManager 类的一个成员函数:
SceneNode * Ogre::SceneManager:: getRootSceneNode ( void ) const [virtual]
- (3)
// 创建模型实体 Entity* entObject = mSceneMgr->createEntity("object", "ogrehead.mesh");
createEntity() (创建实体) 在当前场景中创建一个实体实例并返回它的对象指针,作为 SceneManager 类的一个成员函数,它有两个重载:
Entity * Ogre::SceneManager::createEntity ( const String & entityName, // 实体名称(同一场景中不能有相同的实体名称) PrefabType ptype // 预制类型(不需要读取模型,直接在程序中创建简单几何体, // 目前只有平面 PT_PLANE 一种预制类型) ) [virtual] Entity * Ogre::SceneManager::createEntity ( const String & entityName, // 实体名称(同一场景中不能有相同的实体名称) const String & meshName // 模型名称(需要从硬盘上读取的模型的文件名) ) [virtual]
这里用的是第二个重载,也就是从硬盘上读取模型,模型文件名 "ogrehead.mesh" ,这个文件在 OGRE\Samples\Media\models 目录下。查看一下这个目录,里面有一些 *.mesh 文件,还有一些 *.skeleton 文件 。
- .mesh 文件包含了静态模型和材质的信息,*.skeleton 文件对应一个同名的 *.mesh 文件,保存这个模型的骨骼和动画信息(如果有)。使用 createEntity() 方法,不管是不是带骨骼带动画的 *.mesh ,都一样读取,但是要想播放模型的动画,还需要做一些别的工作,如果现在你直接读取 ninja.mesh 这个带骨骼动画的模型,它不会动。
到了这里,还有一个问题,就是 OGRE 是怎么找到的这个 *.mesh 文件?它怎么知道这个 *.mesh 文件是放在 OGRE\Samples\Media\models 目录下的?还有我们在示例中看到的模型是带纹理贴图的,那么纹理贴图都在哪里呢? OGRE 又是怎么找到这个模型需要的纹理贴图的呢?
在 E:\Desktop\Learning\OGRE\Samples\Common\bin 目录、还有这个目录的 Debug 和 Release 两个子目录下,存在着一系列的 *.cfg 文件,它们都是些配置文件, OGRE 程序在打开以后,第一件事情就是检查并读取这些配置文件,从中了解一些关键信息。其中的 resources.cfg 就是用来指定所有的游戏资源所在的目录路径, OGRE 要读取一些资源,就会从这些路径中搜索。
resources.cfg 文件的内容: FileSystem=http://www.cnblogs.com/../Media FileSystem=http://www.cnblogs.com/../Media/fonts // 字体 FileSystem=http://www.cnblogs.com/../Media/materials/programs // 材质程序 (CG、HLSH、GLSL 等) FileSystem=http://www.cnblogs.com/../Media/materials/scripts // 材质定义脚本 FileSystem=http://www.cnblogs.com/../Media/materials/textures // 纹理贴图 FileSystem=http://www.cnblogs.com/../Media/models // 模型 FileSystem=http://www.cnblogs.com/../Media/overlays // 盖层(界面)定义脚本 FileSystem=http://www.cnblogs.com/../Media/particle // 粒子定义脚本 Zip=http://www.cnblogs.com/../Media/packs/cubemap.zip Zip=http://www.cnblogs.com/../Media/packs/cubemapsJS.zip Zip=http://www.cnblogs.com/../Media/packs/dragon.zip Zip=http://www.cnblogs.com/../Media/packs/fresneldemo.zip Zip=http://www.cnblogs.com/../Media/packs/OgreCore.zip Zip=http://www.cnblogs.com/../Media/packs/ogretestmap.zip Zip=http://www.cnblogs.com/../Media/packs/skybox.zip
你可能会留意到每一条语句前面有 FileSystem 和 Zip 的区别,查看一下 OGRE\Samples\Media\packs 里面的那些 *.zip 文件,用 WinRAR 把它们打开来看,比方说打开 dragon.zip ,可以看到里面有这样几个文件:
dragon.mesh body.jpg head4.jpg legs.jpg
一个模型,带三张贴图,就是这么简单。 这个意味着,OGRE 在搜索所需资源的时候,不但能搜索系统目录 (FileSystem) ,还能在压缩文件 (Zip) 中搜索。
最后是模型的纹理贴图,这个在 *.mesh 文件自身中定义,我们在使用 3dsmax 、 Maya 等输出 *.mesh 模型的同时,纹理贴图的相对路径也会被同时包含输出。
// 创建模型实体 Entity* entObject = mSceneMgr->createEntity("object", "ogrehead.mesh");
现在你可以试着让它读取不同的模型,就把这个示例当作一个模型浏览器来用吧 ,几个 *.zip 文件里的资源也试试,比方说,读取 "dragon.mesh" 。
- (4)
// 在场景根节点下创建一个子节点用于绑定这个模型实体 SceneNode* objectNode = rootNode->createChildSceneNode();
createChildSceneNode() (创建子场景节点) 为当前节点创建一个子节点并返回它的对象指针,作为 SceneNode 类的成员函数,它也有两个重载:
SceneNode * Ogre::SceneNode::createChildSceneNode ( const Vector3 & translate = Vector3::ZERO, // 移动向量(相对于父节点) const Quaternion & rotate = Quaternion::IDENTITY // 旋转向量(相对于父节点) ) [virtual] SceneNode * Ogre::SceneNode::createChildSceneNode ( const String & name, // 场景节点名称(同一场景中不能有相同的节点名称) const Vector3 & translate = Vector3::ZERO, // 移动向量(相对于父节点) const Quaternion & rotate = Quaternion::IDENTITY // 旋转向量(相对于父节点) ) [virtual]
这里采用的是第一个重载,创建了一个“无名节点”,并且接受了默认的初始化参数:零位移、零旋转。
其中的 Vector3 (三维向量) 是 OGRE 向量类,在头文件 OgreVector3.h 中定义,有若干重载:
Vector3 (Real fX, Real fY, Real fZ) Vector3 (Real afCoordinate[3]) Vector3 (int afCoordinate[3]) Vector3 (const Real *const r) Vector3 (const Vector3 &rkVector)
和颜色类 ColourValue 一样它还有一些预置三维向量:
const Vector3 Vector3::ZERO( 0, 0, 0 ); const Vector3 Vector3::UNIT_X( 1, 0, 0 ); const Vector3 Vector3::UNIT_Y( 0, 1, 0 ); const Vector3 Vector3::UNIT_Z( 0, 0, 1 ); const Vector3 Vector3::NEGATIVE_UNIT_X( -1, 0, 0 ); const Vector3 Vector3::NEGATIVE_UNIT_Y( 0, -1, 0 ); const Vector3 Vector3::NEGATIVE_UNIT_Z( 0, 0, -1 ); const Vector3 Vector3::UNIT_SCALE(1, 1, 1);
还有 Quaternion (四元数) ,是 OGRE 的四元数类,在头文件 OgreQuaternion.h 中定义,也有若干重载:
Quaternion (Real fW=1.0, Real fX=0.0, Real fY=0.0, Real fZ=0.0) Quaternion (const Quaternion &rkQ) Quaternion (const Matrix3 &rot) // 由旋转矩阵构成的四元数 Quaternion (const Radian &rfAngle, const Vector3 &rkAxis) // 由 角-轴 对构成的四元数 Quaternion (const Vector3 &xAxis, const Vector3 &yAxis, const Vector3 &zAxis) // 由三个正交轴构成的四元数 Quaternion (Vector3 *akAxis) // 由三个正交轴构成的四元数
它也有两个预置四元数:
const Quaternion Quaternion::ZERO(0.0,0.0,0.0,0.0); const Quaternion Quaternion::IDENTITY(1.0,0.0,0.0,0.0);
// 在场景根节点下创建一个子节点用于绑定这个模型实体 SceneNode* objectNode = rootNode->createChildSceneNode();
现在你可以给 createChildSceneNode() 试试各种不同的参数。但是因为没有参照物,只有一个模型,现在我们还不能明显地看到这些参数的影响,学完本章你可以试着多放置几个模型。
- (5)
// 把模型实体绑定到这个子节点 objectNode->attachObject(entObject); attachObject (绑定对象) 绑定实体到场景节点,作为 SceneNode 类的成员函数: void Ogre::SceneNode:: attachObject ( MovableObject * obj ) [virtual]
其中, MovableObject 是一个包含了 Entity 的大类, 我们看它的世系图谱,可以看到 Entity 也是它的一个继承类,包括公告板、光源、镜头视锥、可移动面、风景、地形等等都是 MovableObject 的继承类:
最后,如果觉得程序中镜头移动太快或太慢,请参照 OGRE 一起学 (6) —— 最简单的游戏窗口代码 ,修改 OGRE\Samples\Common\include\ ExampleFrameListener.h :
mMoveSpeed = 500; // 镜头移动速度
建议 100 。