Ogre中级教程(五): 静态图元

来源:互联网 发布:基因对比软件 编辑:程序博客网 时间:2024/04/29 09:16

    中级教程五
      静态图元 目录
            [隐藏]
              1 内容
              2 必要条件
              3 创建场景
                3.1 从ManualObject创建Mesh
                3.2 添加Static Geometry
              4 结论
                4.1 Modifying the StaticGeometry Object
                4.2 Advanced Object Batching


      内容
      很多情况,你需要在场景中添加物体,但是却根本不需要移动它们.比如,除非你加入了物理因素,一块石头或者一棵树将永远不会被移动.Ogre为这种情况提供了StaticGeometry类,它允许你批渲染很多物体.这个通常要比手动在SceneNodes添加要快多了.在这个教程里我们要涵盖StaticGeometry的基本使用方法,另外还会再提及ManualObject的使用.请在前一个教程中获取ManualObject的使用方法.

      在这个教程中,我们将手动创建一个草地mesh,然后在我们的场景中的StaticGeometry 实例中添加许多这样的草地.
      本教程的代码在这里下载.

 

      必要条件
      创建一个cpp 文件,添加下面代码:
#include "ExampleApplication.h"
class TutorialApplication : public ExampleApplication
{
protected:
public:
    TutorialApplication()
    {
    }

    ~TutorialApplication()
    {
    }
protected:
    MeshPtr mGrassMesh;

    void createGrassMesh()
    {
     }

    void createScene(void)
    {
        createGrassMesh();
        mSceneMgr->setAmbientLight(ColourValue(1, 1, 1));

        mCamera->setPosition(150, 50, 150);
        mCamera->lookAt(0, 0, 0);

        Entity *robot = mSceneMgr->createEntity("robot", "robot.mesh");
        mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(robot);

        Plane plane;
        plane.normal = Vector3::UNIT_Y;
        plane.d = 0;
        MeshManager::getSingleton().createPlane("floor",
            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
            450,450,10,10,true,1,50,50,Vector3::UNIT_Z);
        Entity* pPlaneEnt = mSceneMgr->createEntity("plane", "floor");
        pPlaneEnt->setMaterialName("Examples/GrassFloor");
        pPlaneEnt->setCastShadows(false);
        mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(pPlaneEnt);
    }
};

#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 application object
    TutorialApplication 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: %s/n",
            e.getFullDescription().c_str());
#endif
    }

    return 0;
}
   在继续教程之前,确信你的代码可以编译。你可以看到一个机器人站在一个平面上。
创建场景
      从ManualObject创建Mesh
      我们要做的第一件事是创建要渲染的草地.创建3个交错的正方形,每个正方形都贴上草的材质,这样无论你从哪个角度看都是3D的效果.比较简单的创建方式是创建一个方型,第2个旋转60度,然后第3个再转60度.
      和上一个教程一样,我们会用ManualObject建立我们的物体,但是不同的是我们要创建的是一个Mesh而不是线列表.
      首先定义一些变量.我们要创建一个Vector3在其中定义X和Z的坐标,用它们来创建一个正方型,然后用Quaternion来转个方向.找到createGrassMesh函数,添加下面代码:

      const float width = 25;
      const float height = 30;
      ManualObject mo("GrassObject");

      Vector3 vec(width/2, 0, 0);
      Quaternion rot;
      rot.FromAngleAxis(Degree(60), Vector3::UNIT_Y);    //绕Y轴旋转60度
现在已经设定好变量了,接着需要定义ManualObject.RenderOperation需要设定为OT_TRIANGLE_LIST,意味着在定义好顶点之后,需要创建面.

      mo.begin("Examples/GrassBlades", RenderOperation::OT_TRIANGLE_LIST);
      for (int i = 0; i < 3; ++i)
      {
每个方形需要定义4个顶点.每个顶点要设定texture坐标,它告诉Ogre如何使用Examples/GrassBlades
      material中定义的材质.设定左上角点为(0,0),右下角点为(1,1).
          mo.position(-vec.x, height, -vec.z);  
          mo.textureCoord(0, 0);

          mo.position(vec.x, height, vec.z);
          mo.textureCoord(1, 0);

          mo.position(-vec.x, 0, -vec.z);
          mo.textureCoord(0, 1);

          mo.position(vec.x, 0, vec.z);
          mo.textureCoord(1, 1);
现在已经定义了方形的4个顶点.上一个教程简要提到,要定义面需要创建三角形,并且面对你按逆时针旋转.第1个方形要建2个三角形.第一个是(0th,
      3rd, 1st)顶点(按照上面定义的顺序),第二个(0th, 2nd,
      3rd).因为每次只创建4个顶点,所以需要用offset来取得正确的开始顶点数字.
          int offset = i * 4;
          mo.triangle(offset, offset+3, offset+1);
          mo.triangle(offset, offset+2, offset+3);
接着需要旋转.(旋转矩阵*矩阵)
          vec = rot * vec;
      }
      mo.end();
现在我们定义了一个manual object,最后要做的是将它转换为mesh.
      mo.convertToMesh("GrassBladesMesh");
注意如果你用这种方法创建了一个超级复杂的mesh.你需要将它存储为文件,然后在以后读回来,以便不用每次调用程序都重新创建一次.
      // 在该例子中不要添加下面代码
      MeshPtr ptr = mo.convertToMesh("GrassBladesMesh");
      MeshSerializer ser;
      ser.exportMesh(ptr.getPointer(), "grass.mesh");
现在让我们开始创建StaticGeomety吧.
      添加Static Geometry
      第一件事是为我们刚才建立的Mesh创建一个Entity,然后创建StaticGeometry实例.注意:我们只为StaticGeometry创建了一个Entity.找到createScene方法,在最后添加下面代码:

      Entity *grass = mSceneMgr->createEntity("grass", "GrassBladesMesh");
      StaticGeometry *sg = mSceneMgr->createStaticGeometry("GrassArea");  //相当于节点

      const int size = 375;
      const int amount = 20;
size变量定义了要在多大面积之内种草. Amount变量定义了在每一行要放多少个物体.
      接着要定义size和origin.一旦创建了实例(通过调用StaticGeometry::build),我们就不能再修改origin和size了.如果你要在一个点周围放这些物体,需要将origin的x和z值设定为size
      x和z数值的一半.
      sg->setRegionDimensions(Vector3(size, size, size));
      sg->setOrigin(Vector3(-size/2, 0, -size/2));
上面将该物体放在点(0,0,0)周围.如果要在3D空间中其它点放置,用下面代码:
      //在该例子中不要添加下面代码
      sg->setOrigin(Vector3(-size/2, -size/2, -size/2) + Vector3(x, y, z));
注意:我们在创建mesh的时候定义过物体高度,在setRegionDimensions
      y数值要大于mesh的高度。下面要做的是将这个物体加入StaticGeomety.下面代码有点复杂,因为我们将上面的草变成草垛,并且随机赋予x和z的值/旋转角度和垂直放大倍数.

      for (int x = -size/2; x < size/2; x += (size/amount))
          for (int z = -size/2; z < size/2; z += (size/amount))
          {
              Real r = size / (float)amount / 2;
              Vector3 pos(x + Math::RangeRandom(-r, r), 0, z + Math::RangeRandom(-r, r));
              Vector3 scale(1, Math::RangeRandom(0.9, 1.1), 1);
              Quaternion orientation;
              orientation.FromAngleAxis(Degree(Math::RangeRandom(0, 359)), Vector3::UNIT_Y);

              sg->addEntity(grass, pos, orientation, scale);
          }
当你定义了StaticGeomety之后你需要调用addEntity或者addSceneNodt.addSceneNode会将Entity加到Static
      Geomety的所有子节点中,用每个子节点的位置,方向和缩放率.注意:要用addSceneNode的话,要将它从节点中挪走.否则,Ogre会渲染你创建的StaticGeomety以及原来你不想被渲染的节点.
      最后,我们需要创建StaticGeomery令它被显示.
      sg->build();
现在运行你的程序,你可以看到一个站在草地上的机器人.
      结论
      Modifying the StaticGeometry Object
      Once the StaticGeometry is created, you are not supposed to do too much
      with it, since that would mostly defeat the purpose. You can, however, do
      things like wave the grass with the wind. If you are interested in how to
      do this, take a look at the grass demo which comes with Ogre. The
      GrassListener::waveGrass function modifies the grass to perform a
      wave-like motion.
      Advanced Object Batching
      This is, of course, just the beginnings of object batching. You should use
      StaticGeometry any time you have objects that are grouped together and
      will not move. If you are trying to create something as intensive or as
      expansive as a forest or trying to add grass to a huge amount of terrain,
      you should take a look at one of the more advanced batching techniques,
      like the PagedGeometry Engine.