• OGRE教程SceneNode, Entity, SceneManager and Get start 的讲解


    目录

    1 读者对象
    2 简介
    3 如何开始
    4 OGRE如何工作
      4.1 SceneManager 基础
      4.2
    Entity 基础
      4.3 SceneNode 基础
    5
    你的第一个OGRE程序

    6 坐标和向量
    7 添加另一个物体
    8 Entities more in
    Depth

    9 SceneNodes more
    in Depth

    10 尝试
      10.1
    Scale(变换)
      10.2 旋转
    11 总结
    12 你的想法?


    1
    读者对象

      这篇文章是假设你有C++编程知识,并设置了OGRE在编译器中,(如果你不知道如何设置,请参看《OGRE初学者引导》),对OGRE一无所知的情况下。


    2 简介

      在这篇教程中,我将介绍一些基本的OGRE结构:SceneManager, SceneNode,
    and Entity
    。在这篇文章中,我不会使用太多的代码,而是讲解一些基本的理论。
      通过这篇文章,你将慢慢的添加代码到你的程序中,并观察他的运行结果。对于这些理论,并没有固定的代码,你也可以通过这些理论写出其他的代码。


    3
    如何开始


      对于这篇教程,我们使用了一段固定的代码,(也许你在《OGRE初学者引导》见过)。在这段代码中,你可以忽视其他的代码,但createScene中的代码应注意。在下一篇教程中,我们将深入讲解OGRE是如何工作的,因此这里的基本知识很重要。添加下面的代码到你的编译器中:

    #include
    "ExampleApplication.h"

    class TutorialApplication : public
    ExampleApplication
    {
    protected:
    public:
       
    TutorialApplication()
        {
        }

        ~TutorialApplication()

        {
       
    }
    protected:
        void
    createScene(void)
        {
       
    }
    };

    #if OGRE_PLATFORM == PLATFORM_WIN32 || 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 application
    object
        TutorialApplication app;

        try {
           
    app.go();
        } catch( Exception& e )
    {
    #if OGRE_PLATFORM == PLATFORM_WIN32
           
    MessageBox( NULL, e.getFullDescription().c_str(), "An exception has
    occured!", MB_OK | MB_IConERROR | MB_TASKMODAL);
    #else
            fprintf(stderr, "An exception has occured: %s\n",
    e.getFullDescription().c_str());
    #endif
       
    }

        return
    0;
    }

      如果你是使用WINDOWS下的OGRESDK,请确定添加"[OgreSDK_DIRECTORY]\samples\include"这个目录到这个项目中。如果是使用OGRE的源代码,请添加"[OgreSource_DIRECTORY]\Samples\Common\include"这个目录。然后,就可以编译和运行了,如果还遇到问题,请参看WIKI的有关这些信息的页面,如果任然不行,请到论坛中讨论。

      程序控制:用WASD移动,鼠标确定方向。ESC键退出。


    4 OGRE如何工作

      一个很宽的主题。我们将从他的基础 The SceneNode,
    Entity, and SceneManager 讲解。

    4.1 SceneManager
    基础


      SceneManager管理出现在屏幕上的所有物体。当你把一个物体放到场景中,SceneManager就将对这个物体的坐标进行跟踪。当你建立一个摄象机时,SceneManager就将对他们所有东西进行跟踪。当你建立木块,广告牌,灯光...,SceneManager
    还是会对他们进行跟踪。

      SceneManager也有许多的类型,有渲染地形的,有渲染BSP树的,等等。在这篇文章中,你将学到许多类型的SceneManager。

    4.2
    Entity
    基础


      Entity是你在场景中渲染的物体的形状。你能把他想象成3D网格。一个机器人有网格,一条鱼有网格,你的角色行走的地形有一个大的网格。而如灯光,广告牌(Billboards),粒子,摄象机等没有Entity。

      值得注意的一件事是,OGRE是根据物体的坐标和方向的可渲染性分别进行渲染的。这就意味着你不能直接到场景中的一个网格进行渲染。你必须将要渲染的物体的网格给予SceneNode
    ,SceneNode 包含诸如坐标和方向等信息。

    4.3 SceneNode
    基础


      在上面已经提到,SceneNode
    用于保持对所有与它联系的物体的坐标和方向进行跟踪。当你建立一个网格,他并不会在场景中进行渲染,除非你将这个网格赋予SceneNode
    。相似的,SceneNode
    不是你要在屏幕上显示的物体,只有当你建立一个SceneNode,并将一个网格赋予他,他才会在屏幕上显示一个物体。

      SceneNode
    能将许多的物体赋予他。例如,你在屏幕上有一个行走的物体,并且你想产生一个灯光环绕着他。首先,你需要建立一个SceneNode
    ,然后建立角色的网格,并将他赋予SceneNode 。下一步建立灯光,并将他赋予SceneNode
    。SceneNode也允许你将他赋予其他SceneNode,这样就建立了一个有等级的节点系统。在下一篇文章中,我们将更详细的讲解SceneNode的功能。

      一个重要的概念是,SceneNode的位置总是和他的父SceneNode有关,并且SceneManager包含所有被赋值的SceneNodes的根节点。


    5
    你的第一个OGRE程序


      现在回到我们开始建立的代码中,找到TutorialApplication::createScene
    成员函数。在这篇教程中,我们只将对这个函数进行操作。我们要做的第一件事是建立网格。我们可以通过调用 SceneManager's createEntity
    函数来实现。添加下面的行到createScene :

    Entity *ent1 = mSceneMgr->createEntity(
    "Robot", "robot.mesh"
    );

      到这里,有几个问题将弹出。首先,mSceneMgr来自哪里,我们用什么值去调用这个函数?
      mSceneMgr常量包含当前SceneManager物体(这是通过ExampleApplication类来实现的)。第一个参数是我们通过createEntity建立的网格的名字。所有的网格必须有唯一的名字。如果你试图建立两个有相同名字的网格,你将得到错误。“robot.mesh"
    唯一表明了我们要使用的网格的名字。在这里,我们使用的网格是通过ExampleApplication类导入的。

      到现在,我们建立了网格,我们还需要用SceneNode
    与他关联。又因为每个SceneManager有一个根 SceneNode,我们将用下面的代码建立他的一个子节点:

    SceneNode *node1
    = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode"
    );

      这条长长的语句首先调用当前SceneManager的getRootSceneNode方法。然后,他又调用根SceneNode的createChildSceneNode方法。createChildSceneNode方法中的参数是我们建立的SceneNode的名字。像前面所述一样,SceneNode也不允许名字相同。

      最后,我们需要将网格赋予SceneNode,以便于ROBOT有渲染的坐标:

    node1->attachObject(
    ent1 );

      一切OK!编译并运行,你将在屏幕上看到一个机器人。


    6
    坐标和向量


      在我们开始讲解之前,我们有必要讨论一下屏幕坐标和OGRE向量。OGRE像其他的图象引擎一样,用X,Z轴表示水平的面,Y表示垂直的轴。正如你看屏幕一样,X轴表示你的屏幕的左,右,右面是X轴正方向。Y轴表示你的屏幕的下到上,上面是Y轴正方向。Z轴表示你的屏幕的由里到外,外面是Z轴正方向。

      注意,我们的机器人如何面对X轴的正方向?这是网格的一个属性,他是如何设计的呢?OGRE并没有规定你的初始模型的方向,所以你导入的每个物体网格的方向都是不一定的。

      OGRE用向量类来表示坐标和方向。这些向量vectors可以定义为2(Vector2),3(Vector3),4(Vector4)维,其中Vector3最常用,如果你对向量不熟悉,我建议你看一下下面的网站:

    http://en.wikipedia.org/wiki/Vector_%28spatial%29


    笔者注:我建议大家在学习3D编程知识之前,看一下《线形代数》,《空间解析几何》。

    关于向量的数学知识在以后的学习中将发挥重要的作用。


    7
    添加另一个物体

      前面,我们讲解了坐标系统的作用,下面我们回到我们的代码中来。在前面,我们添加的代码中,我们并没有明确表明我们的机器人出现的坐标。但在OGRE中,有许多的函数可以初始坐标。例如:
      SceneNode::createChildSceneNode
    成员函数中,有三个参数:SceneNode的名字,SceneNode的坐标,和SceneNode的基本旋转。对于坐标,正如你所看到的,我们初始时为(0,0,0)。现在,我们建立另一个SceneNode,但是这次我们表明他的坐标为另一个值:

    Entity
    *ent2 = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
    SceneNode
    *node2 = mSceneMgr->getRootSceneNode()->createChildSceneNode(
    "RobotNode2", Vector3( 50, 0, 0 ) );
    node2->attachObject( ent2
    );

      这看起来和前面我们定义的一样,只是有轻微的变化。首先,我们命名网格和SceneNode时,有很小的不同。第二件不同的是我们初始网格开始的位置偏离了根SceneNode50个单位(记住SceneNode所有的坐标和他们的父节点有关)。编译并运行,现在,你有两个机器人。


    8 Entities more in
    Depth(深入Entities)


      Entities类功能非常强大,我在这里并不想覆盖的太宽,只讲一下他的基本。首先,我们不得不提一下Entities中的一些功能强大的成员函数。

      第一个是Entity::setVisible
    和Entity::isVisible.你能把任何Entity 设置成可视。如果你需要先隐藏Entity
    ,然后又显示它,你可以不调用这个函数,而采用先删除Entity
    ,显示时又从新建立他。物体的网格和文理自动拷贝到内存中,不需要你自己保存他们。你需要保存的是Entity物体的建立和删除的东西。

      getName函数用于返回Entity的名字,getParentSceneNode函数用于返回与Entity相关的SceneNode。


    9 SceneNodes more in
    Depth


      SceneNode 类非常复杂。在SceneNodes
    上有许多的事情可以做,因此,我们在这里只覆盖到一些通用的功能。

      你能用SceneNode的getPosition,setPosition函数得到和设置SceneNode的点(通常和SceneNode的父节点有关)。你能用translate函数移动物体。

      SceneNode不仅能设置位置,还能管理物体的变换大小和旋转。你能设置变换大小用scale函数。你能用yaw,roll,pitch旋转物体。你也能用resetOrientation函数设置物体的旋转参数。你还能用setOrientation,getOrientation,rotate函数实现高质量的旋转。

      我们再来看一下attachObject函数,如果你想把物体系到SceneNode点上,这些相关函数将非常有用:numAttachedObjects,getAttachedObject(这个函数有多个版本),detatchObject(也有多个版本),detatchAllObjects.也有整个一组函数处理父和子SceneNode。

      所有的坐标和父SceneNode节点相关,我们能做两个相互移动的SceneNode节点。下面有一段代码:

    Entity
    *ent1 = mSceneMgr->createEntity( "Robot", "robot.mesh" );
    SceneNode *node1
    = mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode"
    );
    node1->attachObject( ent1 );

    Entity *ent2 =
    mSceneMgr->createEntity( "Robot2", "robot.mesh" );
    SceneNode *node2 =
    mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2",
    Vector3( 50, 0, 0 ) );
    node2->attachObject( ent2
    );

    如果我们把第六行:
    SceneNode *node2 =
    mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2",
    Vector3( 50, 0, 0 ) );

    变化如下:

    SceneNode *node2 =
    node1->createChildSceneNode( "RobotNode2", Vector3( 50, 0, 0 )
    );

    然后把RobotNode2 作为RobotNode的子节点。如果你移动node1就将移动node2.例如,下面的代码移动RobotNode2

    node2->translate( Vector3( 10, 0, 10 )
    );

    下面的代码可以移动RobotNode,因为RobotNode2是RobotNode的子节点,RobotNode2也将跟随移动:
    node1->translate(
    Vector3( 25, 0, 0 ) );

      如果,你在这里有麻烦,我们可以理解成从根SceneNode
    自顶向下。在这里,我们开始node1从(0,0,0),然后转移到(25,0,0),因此,node1的坐标为(25,0,0)。node2开始在(50,0,0),加上(10,0,0)。因此新坐标为(60,0,10)。

      现在,让我们来计算这些物体的真正坐标。从根SceneNode
    节点开始,他的位置总是(0,0,0)。现在,node1的坐标为(root+node1):(0,0,0)+(25,0,0)=(25, 0, 0).
    而node2是node1子节点,因此,他的坐标为(root + node1 + node2): (0, 0, 0) + (25, 0, 0) + (60, 0,
    10) = (85, 0, 10).
    这只是描述关于SceneNode坐标层次的一个例子。

      你能通过getSceneNode,getEntity函数得到SceneNodes and
    Entities
    的名字。这两个函数是SceneManager中的方法,因此,你不需要在你建立的每个SceneNode中保持一个指针。你只需要悬挂你经常用的那个。


    10 尝试

      通过这章的学习,我们学习了Entities, SceneNodes, and the
    SceneManager. 的基本知识。下面,我将给出他们的一些例程。在这个例子中,我们在场景中建立一组机器人。

    Scale


    下面的代码是通过SceneNode中的scale函数旋转网格。

    Entity *ent =
    mSceneMgr->createEntity( "Robot", "robot.mesh" );
    SceneNode *node =
    mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode"
    );
    node->attachObject( ent );

    node->scale( .5, 1, 2
    );

    ent = mSceneMgr->createEntity( "Robot2", "robot.mesh" );
    node =
    mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode2",
    Vector3( 50, 0, 0 ) );
    node->attachObject( ent );

    node->scale(
    1, 2, 1 );

    旋转

    下面的代码是通过角度和半径,用the yaw, pitch, and roll
    函数旋转物体。

    Entity *ent = mSceneMgr->createEntity( "Robot", "robot.mesh"
    );
    SceneNode *node =
    mSceneMgr->getRootSceneNode()->createChildSceneNode( "RobotNode", Vector3(
    -100, 0, 0 ) );
    node->attachObject( ent );

    node->yaw( Degree(
    -90 ) );

    ent = mSceneMgr->createEntity( "Robot2", "robot.mesh"
    );
    node = mSceneMgr->getRootSceneNode()->createChildSceneNode(
    "RobotNode2");
    node->attachObject( ent );

    node->pitch( Degree(
    -90 ) );

    ent = mSceneMgr->createEntity( "Robot3", "robot.mesh"
    );
    node = mSceneMgr->getRootSceneNode()->createChildSceneNode(
    "RobotNode3", Vector3( 100, 0, 0 ) );
    node->attachObject( ent
    );

    node->roll( Degree( -90 ) );

    ——————————————————————————————————————————————————————————————————————————-

    1.SceneManagerEntitySceneNodeOGRE中三个基本模块。OGRE的应用程序从SceneManager入口。SceneManager控制出现在场景中的所有东西,包括其中的元素、摄像机(Camera)和平面等。OGRE中有很多种类的SceneManager

    Octree Scene Manager
    Terrain Scene Manager
    Nature Scene Manager (ogreaddons)
    Paging Scene Manager (ogreaddons)
    BSP Scene Manager
    DotSceneOctree Scene Manager (ogreaddons)
    可在创建SceneManager时传入一定的参数指定。
    2.实体(Entity
    实体(Entity)是我们可以在场景中绘制的对象。它可以使3D网格所表示的所有东西(包括山、树、地形等,但是不包括光、颗粒、摄像机等,因为这些不在网格中被表示)。注意,OGRE把可绘制的实体从方向、位置等因素中分离出来。这意味着,我们不能直接把一个实体绘制到场景中。如果要绘制一个实体,我们只能把它和一个节点关联起来,这个节点包含位置、方向等信息。
    实体可以通过函数 Entity::setVisible()来控制实体的可见性,也可以通过函数 Entity::isVisible()来判断实体的可见性。
    实体还可以通过 Entity::getName() 函数来获取实体的名字。用Entity::getParentSceneNode()函数来得到和该实体相关联的节点。
    可以通过SceneManagergetEntity函数来得到节点的名字
    3.节点(SceneNode
    上面已经提到,SceneNode保存着一个实体的位置、方向等信息。SceneNode并非一个实体,它不能被现实在场景中。实际现实在场景中的只是那些真正的实体。
    一个节点可以关联任意多个实体。它也可以关联光(Light)、摄像机(Camera)等的对象,从而控制它们的位置和方向。一个节点也可以作为一个子节点而关联到其它节点上去,然后得到一个具有一系列节点的层次结构。需要注意的是,一个节点的信息,总是相对于其父节点的信息的。每个SceneManager中包含一个根节点来关联由它创建的所有子节点。
    节点可以通过getPosition setPosition 函数(相对于它的父节点)来控制节点的位置。也可以通过translate函数来移动实体(注意函数参数的值)。
    除了控制节点位置,SceneNode还能够控制显示比例(用scale函数—有三个方向的比例调整用具体的比例值控制)和旋转
    void Ogre::Node::roll(const Radia& angle, TransformSpace relativeTo = TS_LOCAL)
    z轴旋转
    void Ogre::Node::yaw(const Radia& angle, TransformSpace relativeTo = TS_LOCAL)
    y轴旋转
    void Ogre::Node::pitch(const Radia& angle, TransformSpace relativeTo = TS_LOCAL)
    x轴旋转
    节点可以通过 resetOrientation函数来重置所有对于对象的旋转。也可以用setOrientationgetOrientation rotate函数来实现更高级的旋转
    节点可以通过numAttachedObjectsgetAttachedObject(这个函数有多种版本),detachObject(多版本)和detachAllObjects等函数来获取相关联的实体的信息,并控制关联。
    移动父节点将作用到子节点,但是移动子节点将不对父节点产生任何作用。
    可以通过SceneManagergetSceneNode函数来得到节点的名字。
    4.Get Start
    为了能够看到我们所创建的实体、设置的光线等,我们要做的第一件事是设置周围环境的颜色(默认SceneManager对象已经创建):
    void Ogre::SceneManager::SetAmbientLight(const ColourValue & colour)
    然后,就是通过SceneManager对象创建一个实体:
    Entity* Ogre::SceneManager::createEntity(const String& entityname.
    const String& meshname)
    第一个参数是要创建的实体的名字,每一个实体都必须有一个唯一的名字。
    第二个参数是我们将使用网格(.mesh文件),作为参数的mesh文件是需要被提前导入的。
    正如前面提到的,既然我们已经创建了一个实体,我们就必须有一个节点对象去关联它。因为SceneManager中有一个根节点,所以,我们只要创建它的子节点即可
    SceneNode* Ogre::SceneManager::getRootSceneNode()->
    createChildSceneNode( const String& nodename
    const Vector3& translate = Vector3::ZERO
    const Quaternion& rotate = Quaternion::IDENTITY )
    这个函数首先得到SceneManager中的根节点,然后由这个根节点去创建子节点。和创建实体的函数一样,我们必须为创建的节点指定一个唯一的名字。
    最后,我们需要把已经创建的节点和实体相关联
    void Ogre::SceneNode::attachObject(MovableObject *ogj)
    5.坐标和向量
    在深入了解OGRE的节点和实体相关知识前,我们要先了解什么是场景的坐标什么是向量(Vector)。在水平面上,OGRE的坐标轴为xz轴,而y轴则作为垂直线上的轴。从我们的显示器看过来,x轴是指向右边的,y轴是指向上边的,z轴是指向外边的。
    OGREVector类来表示实体的位置和方向。OGRE中有表示2维(Vector23维(Vector34维(Vector4)的向量,其中Vector3使用最普遍。

    由于.mesh文件中实体的面对方向是不定的,我们可以在创建节点的时候指定实体面对的方向。参照上面创建节点的函数。

  • 相关阅读:
    一个简单的投票功能
    快速删除.svn文件夹
    屏蔽关键字
    批量过滤POST GET数据
    JS识别ios & 安卓
    HashMap
    GC
    类与类之间的关系
    玩转大数据之Apache Pig如何与Apache Lucene集成
    大话设计模式-简单工厂模式
  • 原文地址:https://www.cnblogs.com/Ogre/p/3028486.html
Copyright © 2020-2023  润新知