天天看點

Irrlicht學習筆記(3)--CustomSceneNode1.說明 2.編寫定制的:CSampleSceneNode 3.在主程式中加入定制部分: 4.全部代碼

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