irrlicht 引擎例子 003 Custom SceneNode

/** Example 003 Custom SceneNode

这个例子是为更高级一些的开发者设计的,如果你仅仅是简单的了解irr引擎,
请优先看看其它的例子。
这个例子里演示了如何手工创建(而非从文件加载)一个场景节点,并在引擎
中将其进行显示。首先,我们需要一个手工创建的场景节点。如果你想实现一个
渲染技巧,irr引擎并不支持的时候,你就必须自己手工创建了。
例如,你想在一个大场景地形节点中增加一个户内场景的入口时,就可以自己
手工创建节点了。在创建这些自定义节点时,你会发现你能够狠轻松的对irr引擎
进行延伸,使其适合你的需求。
在这个例子以至今后的例子中我依旧保持我的简朴风格,保证代码的简介,
并将一切代码仅写在一个.cpp源文件中。
最开始,依旧是包含头文件以及链接库,声明命名空间。
*/
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

/*
从这里开始进入了例子中最重要的部分:
我们自己定义的场景节点类。为了简单,我们不像前文所说,在巨大的场景地形
中增加一个户内场景入口了,我们仅简单的做一个4个顶点连接处3D物体,
虽然这个显的很简单,实际上两者的原理是一样的,我们也仅仅负责将3D物绘制出来,
并不做其他逻辑。
当我们自己定制的场景节点插入irr引擎,
首先我们将自定义场景节点继承于ISCeneNode接口,并重载一些函数。

*/

class CSampleSceneNode : public scene::ISceneNode
{

	/*
	首先,我们声明一些成员变量,开辟一些空间来存储数据。包括:
	1个包围盒,4个顶点,以及3D物体的材质。
	*/
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	video::SMaterial Material;

	/*
    构造函数的参数包含了:场景节点的父节点指针,场景管理器的指针,以及一个场景
	节点的ID号。
	在构造函数内部,我们调用了父类的构造函数,设置了一些我们绘制场景节点
	和创建3D物体的材质和顶点等属性。
	*/

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);

	/*
	irr引擎需要你定义的场景节点的包围盒。它将会使用这个包围盒进行一些自动
	的剪裁等工作。因此我们需要使用3D物体的4个顶点来创建它。
	如果你不希望引擎使用包围盒进行自动剪裁,或则不想创建包围盒,你可以这么
	写
	AutomaticCullingEnable=false;它可以帮你关闭包围盒。
	*/
		Box.reset(Vertices[0].Pos);
		for (s32 i=1; i<4; ++i)
			Box.addInternalPoint(Vertices[i].Pos);
	}

	/*
	在绘制之前,irr的场景管理器会调用每个场景节点的OnRegisterSceneNode()方法。
	如果你想场景能被自动加载,那么你就将它注册到场景管理器中,它就会在场景管理器
	进行Render的时候被自动调用绘制。
	那么引擎为什么不默认的将全部场景节点都注册进去呢?
	这是因为渲染顺序不好决定的原因,个别时间还是需要用户进行特殊设置。
	例如,在一般的场景节点被渲染的时候,深度缓冲区的阴影需要在其他所有场景节点
	渲染之后进行,绘制,或者像摄像机,光线场景节点这些却需要在其他普通场景节点
	渲染之前进行渲染。
	不过我们在这里仅需要普通渲染,所以可以直接将其注册到场景管理器中去。
	如果我们需要提前或拖后进行渲染的话,可以这么写
	SceneManager->registerNodeForRendering(this,SNRT_LIGHT_AND_CAMERA);
	这样就是告诉场景管理器,我们这次注册的场景节点,是和灯光和摄像机一样需要
	提前渲染的。
	值得庆幸的是在本场景节点被注册的同时,它的所有子节点也会默认得到注册。
	*/
	virtual void OnRegisterSceneNode()
	{
		if (IsVisible)
			SceneManager->registerNodeForRendering(this);

		ISceneNode::OnRegisterSceneNode();
	}

	/*
	在render()方法中,最有趣的事情发生了;场景节点开始渲染它们自己了。
	我们重载了这个函数进行3D的绘制工作。
	*/
	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->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4, video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT);
	}

	/*
	我们在重载IScene之外,至少还是新创建了三个额外的方法。
	GetBoundingBox()可以获得场景节点的包围盒
	GetMaterialCount()返回了这个场景节点的材质数量。(我们这个3D物体仅一套材质)
	GetMaterial()返回指定编号的材质。(因为这里我们只有一个材质,我们可以return
	 Material;这样写下去,并且在本代码中,该函数的参数也不应当大于0,
	 我们只有一个材质嘛,编号最大当然是0咯)
	*/
	virtual const core::aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}

	virtual u32 getMaterialCount() const
	{
		return 1;
	}

	virtual video::SMaterial& getMaterial(u32 i)
	{
		return Material;
	}	
};

/*
就这样,我们自定义的场景节点就OK了。现在开始我们运行引擎,
来创建这个场景节点和一个摄像机,再看看结果。
*/
int main()
{
	// 首先允许用户选择设备类型
	video::E_DRIVER_TYPE driverType=driverChoiceConsole();
	if (driverType==video::EDT_COUNT)
		return 1;

	// create device

	IrrlichtDevice *device = createDevice(driverType,
			core::dimension2d<u32>(640, 480), 16, false);
		
	if (device == 0)
		return 1; // could not create selected driver.

	// create engine and camera

	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()。
	这并没有错误,是合法的。因为我们的场景管理器已经控制了它,
	当然,你怕不安全还需要用,非要在代码最后(设备释放之前)在drop()
	它,也没什么问题。
	*/
	CSampleSceneNode *myNode =
		new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

	/*
	现在你应该知道了如果创建一个我们自定义的场景节点了吧,并且让它像其他的
	场景节点一样被渲染绘制,为了让画面更有意思一些。我们为场景节点加了旋转动画起
	让我们的3D物体所在场景节点旋转起来吧。
	*/
	scene::ISceneNodeAnimator* anim =
		smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));

	if(anim)
	{
		myNode->addAnimator(anim);
		
		/*
		I'm done referring to anim, so must
		irr::IReferenceCounted::drop() this reference now because it
		was produced by a createFoo() function. As I shouldn't refer to
		it again, ensure that I can't by setting to 0.
		*/
		anim->drop();
		anim = 0;
	}

	/*
	I'm done with my CSampleSceneNode object, and so must drop my reference.
	This won't delete the object, yet, because it is still attached to the
	scene graph, which prevents the deletion until the graph is deleted or the
	custom scene node is removed from it.
	*/
	myNode->drop();
	myNode = 0; // As I shouldn't refer to it again, ensure that I can't

	/*
	Now draw everything and finish.
	*/
	u32 frames=0;
	while(device->run())
	{
		driver->beginScene(true, true, video::SColor(0,100,100,100));

		smgr->drawAll();

		driver->endScene();
		if (++frames==100)
		{
			core::stringw str = L"Irrlicht Engine [";
			str += driver->getName();
			str += L"] FPS: ";
			str += (s32)driver->getFPS();

			device->setWindowCaption(str.c_str());
			frames=0;
		}
	}

	device->drop();
	
	return 0;
}

/*
That's it. Compile and play around with the program.
**/

你可能感兴趣的:(irrlicht 引擎例子 003 Custom SceneNode)