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