irrwowview

Tutorial 3: Custom Scene Node

Tutorial 3: Custom Scene Node
本教程适用于更高级的开发人员。如果你是刚开始接触IrrLicht引擎,请先看其他教程。这个教程淙如何创建一个定制的场景节点并要引擎中使用。如果你想实现一种IrrLicht引擎不支持的渲染技术,则需要自定义场景节点了。例如,我们可以用它写一个基于入口的室内渲染和一个高级地形场景节点。有了创建自定义场景节点,能够根据你的需要容易地扩展IrrLicht引擎。

我会保持代码短小和只有一个*.cpp文件, 和在其它教程中使用这个引擎。在教程的最后,效果如下图所示。虽然这个看起来不怎么样,但它是一个完全自定义场景节点和是一个创建场景节点好的开始。

 

Lets start!

首先,我包含那头文件,使用irr命名空间,和连接要使用的*.lib文件。

#include <irrlicht.h>

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")

这里是本教程最复杂的部份:我们制定一个自定义场景节点类。为了简单,我们的场景节点既不是基于入口的室内渲染也不是地形场景节点,而是一个简单的四面体。

为了使自定义的场景节点在IrrLicht引擎中使用,我们需要创建继承自ISceneNode的类并重载一些方法。

class CSampleSceneNode : public scene::ISceneNode
{

首先,我们定义一些成员变量,保存四面体的数据:四面体的包围体,四个顶点和材质。

core::aabbox3d<f32> Box;
video::S3DVertex Vertices[4];
video::SMaterial Material;

构造函数的参数要指定父场景节点,场景管理器指针和场景节点的编号(ID)。在构造函数里,我们调用了父类的构造函数,并设置四面体的材质和四个顶点的属性。

public:

CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)

 

 : scene::ISceneNode(parent, mgr, id)

 

{

 

  Material.Wireframe = false;

  Material.Lighting = false;

  Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255),0,1);

  Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255),1,1);

  Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0),1,0);

  Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0),0,0);

 


IrrLicht
引擎需要知道场景节点的包围体。包围体用于自动剪裁和其它。因此我们需要根据四个顶点创建包围体。如果你不想引擎使用包围体进行自动剪裁,或者不想创建包围体,你可以这样写
AutomaticCullingEnabled = false;.

  Box.reset(Vertices[0].Pos);
                    for (s32 i=1; i<4; ++i)
                               Box.addInternalPoint(Vertices[i].Pos);

}

 

在绘制之前,场景管理器会调用每一个场景节点的OnRegisterSceneNode()方法。如果场景节点希望绘制自己,可以把类的this指针注册到场景管理器里。这是必要的告诉场景节点调用合适的::render方法。例如一个个地渲染普通场景节点的内容时,模板缓存阴影会在其它场景节点后渲染。而摄像机或灯光场景节点会在其它场景节点前渲染。
因此这里简单地注册场景节点来渲染。如果想好似摄像机或灯光那样渲染场景节点,我们也可以调用SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);
然后,我们可以调用基于ISceneNode类的OnRegisterSceneNode方法,转而调用子类的方法注册自己。

virtual void OnRegisterSceneNode()
{
  if (IsVisible)
    SceneManager->registerNodeForRendering(this);

 

  ISceneNode::OnPreRender();

}

Render()方法用于渲染场景节点自己。我们重载这个方法来绘制四面体。

virtual void render()
{
  u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };

 

  video::IVideoDriver* driver = SceneManager->getVideoDriver();

  driver->setMaterial(Material);

  driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);

  driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);

}

最后,我们为自定义的场景节点添加三个方法。GetBoundingBox()返回场景节点的包围体,
GetMaterialCount()
返回场景节点的材质数(这个四面体只有一个材质),和getMaterial()指定索引的材质。这里只有一个材质,而getMaterial方法的参数要求为大于0的索引。

  virtual const core::aabbox3d<f32>& getBoundingBox() const
  {
    return Box;
  }

  virtual s32 getMaterialCount()

 

  {

 

    return 1;

 

  }

  virtual video::SMaterial& getMaterial(s32 i)

  {

    return Material;

  }

 

};

这样就完成的场景节点的定义。现在要在引擎中创建这种场景节点和摄像机并观看结果。

int main()
{

  IrrlichtDevice *device =

       createDevice(video::EDT_OPENGL, core::dimension2d<s32>(640, 480), 16, false);

  device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");

  video::IVideoDriver* driver = device->getVideoDriver();

           scene::ISceneManager* smgr = device->getSceneManager();

  smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));

创建一个自定义场景节点对象。注意,我们会在创建后立即销毁(->drop())这个对象。这样做是因为这个对象由场景管理器接管了。这不是必需的,对象也可以在程序结束时销毁。

CSampleSceneNode *myNode =
  new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

 

myNode->drop();

要使只有一个四面体的场景活动,可以像引擎中的其它节点那样添加一个使场景节点活动的动画。

scene::ISceneNodeAnimator* anim =
   smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));

 

myNode->addAnimator(anim);

 

anim->drop();

实现开始绘制和结束程序。

  while(device->run())
  {
    driver->beginScene(true, true, video::SColor(0,100,100,100));

 

    smgr->drawAll();

 

    driver->endScene();

  }

 

 

device->drop();

 

return 0;

}

就这样,编译和演示一下这程序吧。

 

 

posted on 2008-07-04 11:02 shjy 阅读(469) 评论(1)  编辑 收藏 引用

Feedback

# re: Tutorial 3: Custom Scene Node 2011-06-15 16:21 guafeng

这里只有一个材质,而getMaterial方法的参数要求为大于0的索引。—— 这句翻译的有点问题,原文:Because we have only one material here, we can return the only one material, assuming that no one ever calls getMaterial() with an index greater than 0.
我的翻译:因为我们这个节点类只用了一个材质,所以只简单的返回这个材质,我们这里做一个假设:没有人用大于0的索引值调用getMaterial()函数。
  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理