Irrlicht学习备忘录——3 CustomSceneNode

来源:互联网 发布:龙卷风中心知乎 编辑:程序博客网 时间:2024/05/20 17:25

3 CustomSceneNode

官方代码($sdk)\examples\03.CustomSceneNode


这个例子,一看截图就很无聊,就一个五颜六色的三棱锥。好多浮躁的人看到这么没劲的例子,直接就忽略了。然而这个例子对我个人来说,感觉用途太大了,我后面自己做游戏时,经常在对引擎做扩展时,用到的就是这个例子教的东西。对于浮躁的天朝人,出现在这个位置太坑了,这例子应该反到后面些。不过会忽略这例子的人,估计是粗心大意或记忆力差的人,例子一开头就用英文写着“这个例子是为更高级一些的开发者设计的,如果你仅仅是简单的了解irr引擎,请优先看看其他的例子。”

不废话了,后面先介绍着例子。

在想实现一个渲染技巧,而irr并不支持的时候,就必须自己手工创建。要怎么创建呢?方法就是这个例子。例子里演示了如何手工创建一个场景节点,并在引擎中将其进行显示。

如何手工创建一个场景节点呢?自己好好看看前面用到场景节点类型的头文件就会受到启发了。它们都共同继承了一个基类——ISceneNodeISceneNode基类,就是手工创建节点必须的。ISceneNode类是个抽象类,它里面大部分公用的方法已经写好了,只有部分方法必须有派生类明确的写成了纯虚类,这些方法就是需要自己重写的了。

例子里,写了一个CSampleSceneNode类,它公有继承了ISceneNode类。私有成员有aabbox3d<f32>BoxS3DVertexVertices[4]SMaterialMaterial三个,他们分别为场景节点的包围盒、顶点数组,材质。

在构造函数内部,调用了父类的构造函数,设置了一些绘制场景节点和创建3D物体的材质和顶点等属性。irr引擎需要场景节点的包围盒,它将会使用这个包围盒进行一些自动的剪裁等工作。因此构造函数里使用3D物体的4个顶点创建它的包围盒。

virtualvoidOnRegisterSceneNode()这个函数,虽然在ISceneNode类里已经有了现成的方法,但是在自己手工创建的节点里,不重写的话不一定能达到想要的渲染顺序。在一般的场景节点被渲染的时候,深度缓冲区的阴影需要在其他所有场景节点渲染之后进行绘制;摄象机,光线场景节点这些却需要在其他普通场景节点渲染之前进行渲染。在irr每次绘制前,场景管理器会调用每个场景节点的OnRegisterSceneNode()方法。如果想场景能被自动加载,就得将它注册到场景管理器中,它就会在场景管理器进行Render的时候被自动调用绘制。例如想要自己创建的节点和灯光和摄象机一样提前渲染,就需要在OnRegisterSceneNode()方法里用SceneManager->registerNodeForRendering(this,SNRT_LIGHT_AND_CAMERA);注册场景节点和绘制顺序。而在本例中,因没特殊要求,使用的是默认的SceneManager->registerNodeForRendering(this)方法注册节点。

virtualvoid render()这个函数是必须重写的,它的功能就是渲染节点。说通俗点,就是在屏幕上绘制自己创建的节点。下面看下例子里的渲染代码。

u16indices[] = { 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->drawVertexPrimitiveList(&Vertices[0],4, &indices[0], 4, video::EVT_STANDARD,scene::EPT_TRIANGLES,video::EIT_16BIT);

绘制顶点

这里面没有见到DXOpenGL的渲染语句,而是使用了irr已经封装好的绘图语句,它会根据当前使用的渲染驱动自己渲染是使用DX还是OpenGL或者软件渲染。因此自己写渲染部分代码时,使用irr提供好的封装,可以免去自己还要针对不同的渲染环境分别用DXOpenGL写渲染代码。

IsceneNode中还有GetBoundingBoxgetMaterialCountgetMaterial三个方法需要自己写,在例子中只定义了一个包围盒、一个材质,这三方法需要返回的包围盒、材质数、材质,不用想都知道该怎么写了。

自己写的这个场景节点类要怎样使用呢?irr给的例子里,就是在前面例子中用场景管理器添加节点的地方,直接用new创建了一个自己写的场景节点。创建时,已经指定了父节点,这个节点的管理权已经交给了它的父节点,后面自己管理的代码中不再需要使用该节点里,因此在后面紧跟着一句该节点drop调用,将在此处的引用释放掉。

这个例子里还增加了一个前面例子里没有提过的新东西,场景节点动画器。

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

myNode->addAnimator(anim);

anim->drop();

使用irr的场景管理器创建了一个旋转动画器,参数里设置了围绕XYZ三个轴的旋转速度。然后使用场景节点的addAnimator方法将动画器添加到场景节点上,这样场景节点就会受着动画器的控制,进行运动了。最后释放动画器在该处的引用。

其余的部分,前面的例子已经有了,没什么特殊的。

在这个例子里drop出现了好几次,前面的例子里只有最后释放irr设备时用了一次drop,这让刚接触irr的很容易迷惑,什么时候需要drop呢?其实很简单,在irr里凡是使用Createnew出来的,都需要自己管理释放问题,当不需要时,就需要把它们drop掉。凡是使用getadd得到,都是irr自己管理的,不需要自己去多管闲事。irr的所有类都继承了irr的引用计数器,只有创建出来的需要自己管理在代码的哪里释放引用,交给irr管理时,irr会自动把引用计数器增加,因此自己创建出来的,只要交给irr管理后,自己不再使用就可以用drop把引用计数器减小。irr里不使用delete释放任何东西,一律使用drop。对引用计数器感兴趣,看IReferenceCounted.h文件。

Irr这个例子里直接在要使用的地方用new创建自己的节点的方法,虽然没有错,但也有点的坑。这方法使用起来很灵活,自己定义的构造函数可以有很多参数,但随着自己游戏设计的不断复杂后,需要LoadSave时,就会感觉非常麻烦了。irr其实早就对这些问题考虑过了,它设计了一个接口,让用户可以自行扩展irr,在创建用户自己的节点时跟创建irr自身的节点一样的方法,只是参数少一些。但在这例子里却一点不提,可能是为了简化例子的复杂度吧。这些官方没提的部分我就留到最后单独做总结。

原创粉丝点击