Irrlicht学习笔记(3)--CustomSceneNode


1.说明



这个例程涉及:如何创建一个定制场景节点.

适用于:如果要使用irrlicht不支持的渲染技术的情况下.
比如:你可以通过它写一个基于渲染器的室内入口和高级地形场景节点
(原文是:an indoor portal based renderer or an advanced terrain scene
node)

.....通过定制场景节点可以扩展irrlicht引擎并适应个性需求.



2.编写定制的:CSampleSceneNode



目标:要定制场景节点绘制一个旋转的四面体.

CSampleSceneNode需要继承 irr::scene::ISceneNode类,并且重写一些方法.

1)需要新增的参数包括:

core::aabbox3d<f32> Box;//包围盒
video::S3DVertex Vertices[4];//四个顶点
video::SMaterial Material;//材质

2)在构造函数中,初始初始化上述三个参数,还需要额外的:

scene::ISceneNode* parent,//指向父节点的指针
scene::ISceneManager* mgr,//指向场景管理器的指针,内部用SceneManager保存
s32 id.//唯一的id

3)     1)处新增参数的参数设置:

(1)Material

material有很多参数,可以用.来设置,因为参数都为 public , 要是用某个功能一般需要开启相应状态,
所有状态参数参数都可以通过方法i rr::video::SMaterial::setFlag(EMF_*_*,bool value)来设置, irr::video::SMagerial::getFlag(E_MATERIAL_FLAG flag)来获得.
比如这里要使物体可见(关闭灯光,因为没有设置灯)且渲染成面:
Material.Wirefram = false;//默认为false,也可以:Material.setFlag(EMF_WIREFRAM,false);
Material.Lighting = false;//默认为true,也可以:Material.setFlag(EMF_LIGHTING,false);

(2)Vertex

参数列表:
core::vector3df Pos;//坐标
core::vector3df Normal;//法向量
SColor Color;//颜色
core::vector2d<f32> Tcoords;//纹理坐标
需要通过构造函数为参数初始化值,然后通过 = 复制到已经有的对象.
比如:

Vertices[0] = video::S3DVertex(
                       x,y,z,//Pos
                      nx,ny,nz,//Normal
                      video::SColor(a,r,g,b),//Color
                      tu,tv//Tcoord
);

irr::video::S3DVertex内部提供有一些有用的方法,比如位置比较,判断


(3)Box(irr::core::aabbox3d<T>)


参数列表:
vector3d<T> MinEdge;
vector3d<T> MaxEdge;

原理:输入一群点,找到一个立方体盒子使它包围所有的点.立方体由上述参数保存.
可以通过(2)的方法设置一个包围盒.
但这个模版类内部提供了大量实用的方法用于设置和使用包围盒.

设置包围盒的核心函数:

void addInternalPoint(T x,T y ,T z);

还有一些判断点,线,面与包围盒的关系的方法.
这里我们用如下方法设置包围盒,先用一个点初始化,再把其他店插入包围盒,其内部自动处理.

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


4)注册


我们要达到的目标是通过这个定制场景节点绘制一个四面体,那么就需要场景管理器 sgmr->drawAll()自动调用场景节点的绘制方法.
因此需要提前注册( irr::scene::ISceneNode::OnRegisterSceneNode()),这也就是告诉smgr,绘制的时候请调用我们的定制节点的 irr::scene::ISceneNode::render()方法.
同时绘制是有顺序的,我们也可以设置我们的场景节点的优先顺序,这可以在注册的时候进行.
要用到的注册函数( irr::scene::ISceneManager内):

virtual u32 registerNodeForRendering(ISceneNode* node,//一般为this
E_SCENE_NODE_RENDER_PASS pass = ESNRP_AUTOMATIC
                 /*第二个参数用于设置渲染顺序的,参数为:
                  * irr::scene::E_SCENE_NODE_RENDER_PASS中的一个(enum)
                */
          ) = 0;

//重写 OnRegisterSceneNode方法,官方格式.
virtual void OnRegisterScene()
{
    if(IsVisible)
        SceneManager->registerNodeForRendering(this);
    ISceneNode::OnRegisterSceneNode();//这个方法内部实现的是为所有子节点注册,
    //当内部调用这个回调函数的时候会调用registerNodeForRendering(this);
}


5)重写定制节点的render方法


三步:设置材质,获得变换矩阵,绘制.

video::IVideoDriver是Irrlicht中非常重要的接口,所有跟 渲染纹理操纵都是通过这个接口完成.

它管理了我们使用的渲染器,场景绘制需要通过它完成.

重写 render方法:

virtual void render()
{
    u16 indices[] = {0,2,3,/*第一个面*/
            2,1,3,/*第二个面*/
            1,0,3,/*第三个面*/
            2,0,1/*第四个面*/};
    video::IVideoDriver* driver = SceneManager->getVideoDriver();//保存在smgr中的driver
    
    driver->setMaterial(Material);//启用当前材质,内部根据渲染器不同
    //设置变换
    driver->setTransform(video::ETS_WORLD,/*view, world, projection*/
                          AbsoluteTransformation/*scene::ISceneNode内:
                           core::matrix AbsoluteTransformation,保存矩阵用*/
       );
    //绘制
    dirver->drawVertexPrimitiveList(
         &Vertices[0],/*最多65535个顶点*/
         4,/*顶点总数*/
         &indices[0],/*绘制顶点的顺序表*/
         4,/* 图元数量*/
v       video::EVT_STANDARD,/*顶点格式*/
         scene::EPT_TRIANGLES,/*绘制的图元种类*/
         EIT_16BIT/*indices的数据类型*/
         );
}


   

6)三个附加的方法


获得包围盒,获得材质总数,获得材质句柄

virtual const aabbox3df& getBoundingBox() const 
{
     return Box;
}
virtual u32 getMaterialCount() const
{
    return 1;
}
virtual video::SMaterial& getMaterial(u32 i)
{
    return Material;
}
 




3.在主程序中加入定制部分:



1)添加节点对象


方法很简单,就是创建一个定制场景节点对象.

比如:
CSampleSceneNode *myNode  = new CSampleSceneNode(
             smgr->getRootSceneNode(),
             smgr,
             123
);


Irrlicht引擎会管理我们的节点.
为了更规范,我们需要释放引用:

myNode->drop();
myNode = 0;


2)加入动画



为我们的myNode加入一个动画: 旋转.

步骤:
      (1).创建一个动画
      (2).为myNode加入此动画

动画类: scene::ISceneNodeAnimator

(1)创建旋转动画:



通过场景管理器的接口创建.

scene::ISceneManager::createRotationAnimation(core::vector3df(rx,ry,rz));

参数代表每秒每个方向旋转多少度.

注意:vector3d可以用来表示旋转角度,X:pitch,Y:yaw,Z:roll.这个不是跟坐标系的xyz一一对应的.


(2)为myNode绑定一个动画


myNode->addAnimator(anim);


函数 内部:
    将anim推入Animators链表中( core::list<ISceneNodeAnimator*> Animators;),
    然后 anim->grab();(irr::IReferenceCounted引用计数器)攥取对象,
    之后(函数 外部)必须调用anim->drop().



4.全部代码



#include <irrlicht.h>

using namespace irr;

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

class CSampleSceneNode :public scene::ISceneNode
{
	/*成员:包围盒,四个顶点,材质*/
	core::aabbox3d<f32> Box;
	video::S3DVertex Vertices[4];
	video::SMaterial Material;

public:
	/*构造函数:父节点,场景管理器的指针,场景节点的id*/
	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::scene::ISceneNode::setAutomaticCulling(irr::scene::EAC_OFF);
		Box.reset(Vertices[0].Pos);
		for (s32 i = 1; i < 4; i++)
			Box.addInternalPoint(Vertices[i].Pos);

	}

	//注册场景节点,
	virtual void OnRegisterSceneNode()
	{
		if (IsVisible)
			SceneManager->registerNodeForRendering(this);

		ISceneNode::OnRegisterSceneNode();
	}

	//重载渲染函数
	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);
	}

	//添加一些额外的方法
	virtual const core::aabbox3d<f32>& getBoundingBox() const
	{
		return Box;
	}
	virtual u32 getMaterialCount() const
	{
		return 1;
	}
	virtual video::SMaterial& getMaterial(u32 i)
	{
		return Material;
	}

};
int main()
{
	IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
		core::dimension2d<u32>(720, 455), 16, false, false, false, 0);

	if (!device)
		return 1;
	device->setWindowCaption(L"CustomSceneNode ");
	video::IVideoDriver *driver = device->getVideoDriver();
	scene::ISceneManager *smgr = device->getSceneManager();


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

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

	if (anim)
	{
		myNode->addAnimator(anim);

		anim->drop();
		anim = 0;
	}
	
	myNode->drop();
	myNode = 0;


	smgr->addCameraSceneNode(0, core::vector3df(0, -40, 0), core::vector3df(0, 0, 0));
	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"CustomeSceneNode [";
			str += driver->getName();
			str += L"] FPS: ";
			str += (s32)driver->getFPS();

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

	device->drop();
	return 0;
}
  


你可能感兴趣的:(3D,三维,Irrlicht)