Irrlicht引擎学习笔记(15)--LightManager

说明:

这个粒子是为了 解释灯光管理
涉及:
允许使用超过硬件支持的数量的动态灯光
以及灯光管理器申请
每个节点的回调
详情请看源码及注释.


源码及注释:

#include <iostream>
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "irrlicht.lib")
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
/**
*1.光源管理器
*为了使用超过8个的灯光,注册一个可选光源管理器,允许可以在渲染的时候开关特定灯光
*如果没有注册光源管理器,默认的基于距离(距离照相机)的流程将被激活.
*
*NO_MANAGEMENT:关闭光源管理器,展示引擎默认的光源行为.将依据照相机与灯光距离,
*选择最进的8个光源.
*LIGHTS_NEAREST_NODE:展示为每个场景节点启用有限数量的光源.如果为正在渲染的节点
*找到了三个光源,启用它们而关闭其他的光源,这里可行.但是应用到整个场景,就处理不好了.
*会看到闪烁现象.这是由于光源在切换其所关联的节点造成的.
*LIGHTS_IN_ZONE:展示依据"zone"来开关光源的技术.每个空的场景节点作为一个zone的父节点.
*当节点被渲染的时候,关闭所有光源,找到父"zone"节点并打开在其内的光源.这能为每个节点实现
*实现本地光源的效果.这可以用于单个房间内绑定本地光源,而每个房间光源与其他房间的光源是隔离的.
*
*光源管理器是一个事件接收器.
*/
class CMyLightManager :public scene::ILightManager, public IEventReceiver
{
typedef enum
{
NO_MANAGEMENT,
LIGHTS_NEAREST_NODE,
LIGHTS_IN_ZONE
}LightManagmentMode;
LightManagmentMode Mode;
LightManagmentMode RequestedMode;

//光源管理器需要
scene::ISceneManager* SceneManager;
core::array<scene::ILightSceneNode*>* SceneLightList;
scene::E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
scene::ISceneNode* CurrentSceneNode;

public:
CMyLightManager(scene::ISceneManager* sceneManager)
:Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT),
SceneManager(sceneManager), SceneLightList(0),
CurrentRenderPass(scene::ESNRP_NONE), CurrentSceneNode(0)
{

}
virtual ~CMyLightManager(void){}
//事件处理函数,开关光源管理状态
bool OnEvent(const SEvent& event)
{
bool handled = false;

if (event.EventType == irr::EET_KEY_INPUT_EVENT&&
event.KeyInput.PressedDown)
{
handled = true;
switch (event.KeyInput.Key)
{
case irr::KEY_KEY_1:
RequestedMode = NO_MANAGEMENT;
break;
case irr::KEY_KEY_2:
RequestedMode = LIGHTS_NEAREST_NODE;
break;
case irr::KEY_KEY_3:
RequestedMode = LIGHTS_IN_ZONE;
break;
default:
handled = false;
break;
}
if (NO_MANAGEMENT == RequestedMode)
SceneManager->setLightManager(0);
else
SceneManager->setLightManager(this);
}
return handled;
}

//在第一个场景节点被渲染前调用,初始化光源链表
virtual void OnPreRender(core::array<scene::ILightSceneNode*>& lightList)
{
//更新模式,
Mode = RequestedMode;
SceneLightList = &lightList;
}
//在最后一个节点被渲染后调用
virtual void OnPostRender()
{
//由于可能关闭光源管理器,所以需要打开所有光源确保其在正确状态
for (u32 i = 0; i < SceneLightList->size(); ++i)
{
(*SceneLightList)[i]->setVisible(true);
}
}
//记录当前renderpass
virtual void OnRenderPassPreRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
{
CurrentRenderPass = renderPass;
}
//在渲染完solid节点后关闭所有光源
virtual void OnRenderPassPostRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
{
if (scene::ESNRP_SOLID == renderPass)
{
for (u32 i = 0; i < SceneLightList->size(); ++i)
{
(*SceneLightList)[i]->setVisible(false);
}
}
}
//在规定的场景节点被渲染前调用
virtual void OnNodePreRender(scene::ISceneNode* node)
{
CurrentSceneNode = node;
//这里觉得至渲染solid物体
if (scene::ESNRP_SOLID != CurrentRenderPass)
return;
//这里只渲染立方体
if (node->getType() != scene::ESNT_CUBE)
return;
//需要处理两种模式下的光源管理
//LIGHTS_NEAREST_NODE和LIGHTS_IN_ZONE
if (LIGHTS_NEAREST_NODE == Mode)
{
//根据光源在关联节点里的优先顺序渲染,会造成闪烁
const core::vector3df nodePosition = node->getAbsolutePosition();
//排序,依据距离
core::array<LightDistanceElement> sortingArray;
sortingArray.reallocate(SceneLightList->size());

u32 i;
for (i = 0; i < SceneLightList->size(); ++i)
{
scene::ILightSceneNode* lightNode = (*SceneLightList)[i];
f64 distance = lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition);
sortingArray.push_back(LightDistanceElement(lightNode, distance));
}
sortingArray.sort();

//排序完成,开启距离最小的三个
for (i = 0; i < sortingArray.size(); ++i)
sortingArray[i].node->setVisible(i < 3);

}
else if (LIGHTS_IN_ZONE == Mode)
{
//关闭所有光源,找到zone父节点(这里为空),开启zone里的所有灯光
for (u32 i = 0; i < SceneLightList->size(); ++i)
{
scene::ILightSceneNode* lightNode = (*SceneLightList)[i];
video::SLight& lightData = lightNode->getLightData();

if (video::ELT_DIRECTIONAL != lightData.Type)
lightNode->setVisible(false);
}
scene::ISceneNode* parentZone = findZone(node);
if (parentZone)
turnOnZoneLights(parentZone);
}
}
//特定场景节点被渲染后调用
virtual void OnNodePostRender(scene::ISceneNode* node)
{

}
private:
//找到特定节点的空父节点
scene::ISceneNode* findZone(scene::ISceneNode* node)
{
if (!node)
return 0;
if (node->getType() == scene::ESNT_EMPTY)
return node;
return findZone(node->getParent());
}
//开启特定场景节点的所有光源
void turnOnZoneLights(scene::ISceneNode* node)
{
core::list<scene::ISceneNode*> const & children = node->getChildren();
for (core::list<scene::ISceneNode*>::ConstIterator child = children.begin();
child != children.end(); ++child)
{
if ((*child)->getType() == scene::ESNT_LIGHT)
static_cast<scene::ILightSceneNode*>(*child)->setVisible(true);
else//没有
turnOnZoneLights(*child);
}
}
//帮助场景节点根据距离排序
class LightDistanceElement
{
public:
LightDistanceElement(){};
LightDistanceElement(scene::ILightSceneNode* n, f64 d)
:node(n), distance(d){}
scene::ILightSceneNode* node;
f64 distance;
//从小到大排序
bool operator < (const LightDistanceElement& other)const
{
return (distance < other.distance);
}
};
};
int main(int argc, char** argv)
{
video::E_DRIVER_TYPE driverType = driverChoiceConsole();
IrrlichtDevice *device =
createDevice(driverType, core::dimension2d<u32>(720, 455), 32,
false, true, false, 0);
if (!device)
return 1;
device->setWindowCaption(L"quake3map");

video::IVideoDriver *driver = device->getVideoDriver();
scene::ISceneManager *smgr = device->getSceneManager();
gui::IGUIEnvironment *guiev = device->getGUIEnvironment();
//2准备
f32 const LightRadius = 60.f;//足够到达每个zone的边

gui::IGUISkin* skin = guiev->getSkin();
if (skin)
{
skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
skin->setFont(guiev->getFont("../media/fontlucida.png"));
}
guiev->addStaticText(L"1-No liht management", core::rect<s32>(10, 10, 200, 30));
guiev->addStaticText(L"2-Closest 3 lights", core::rect<s32>(10, 30, 200, 50));
guiev->addStaticText(L"3-Lights in zone", core::rect<s32>(10, 50, 200, 70));

//添加一些zones,
for (f32 zoneX = -100.f; zoneX <= 100.0f; zoneX += 50.0f)
for (f32 zoneY = -60.0f; zoneY <= 60.0f; zoneY += 60.0f)
{
//从一个空场景节点开始,它将被用来表示一个zone
scene::ISceneNode* zoneRoot = smgr->addEmptySceneNode();
zoneRoot->setPosition(core::vector3df(zoneX, zoneY, 0));

//每一个zone有一个旋转立方体
scene::IMeshSceneNode* node = smgr->addCubeSceneNode(15, zoneRoot);
scene::ISceneNodeAnimator* rotation = smgr->createRotationAnimator(core::vector3df(0.25, 0.25, 0.25));
node->addAnimator(rotation);
rotation->drop();


//每个立方体绑定三个光源,每个光源绑定到一个布告板,布告板绑定到立方体,
//这样光源可间接继承立方体一样的空场景节点
scene::IBillboardSceneNode* billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(core::vector3df(0, -14, 30));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
billboard->setMaterialTexture(0, driver->getTexture("../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
scene::ILightSceneNode* light = smgr->addLightSceneNode(billboard, core::vector3df(0, 0, 0),
video::SColorf(1, 0, 0), LightRadius);

billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(core::vector3df(-21, -14, -21));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
billboard->setMaterialTexture(0, driver->getTexture("../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
light = smgr->addLightSceneNode(billboard, core::vector3df(0, 0, 0),
video::SColorf(0, 1, 0), LightRadius);

billboard = smgr->addBillboardSceneNode(node);
billboard->setPosition(core::vector3df(21, -14, -21));
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
billboard->setMaterialTexture(0, driver->getTexture("../media/particle.bmp"));
billboard->setMaterialFlag(video::EMF_LIGHTING, false);
light = smgr->addLightSceneNode(billboard, core::vector3df(0, 0, 0),
video::SColorf(0, 0, 1), LightRadius);

//每个立方体有一个小的立方体在其周围旋转,展示同一个zone内的光照
node = smgr->addCubeSceneNode(5, node);
node->setPosition(core::vector3df(0, 21, 0));
}


smgr->addCameraSceneNode(0, core::vector3df(0, 0, -130), core::vector3df(0, 0, 0));


//3
CMyLightManager* myLightManager = new CMyLightManager(smgr);
smgr->setLightManager(0);//默认
device->setEventReceiver(myLightManager);
int lastFPS = -1;
while (device->run())
{
driver->beginScene(true, true, video::SColor(255, 100, 101, 140));

smgr->drawAll();
guiev->drawAll();

driver->endScene();

int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Campfire FX example [";
str += driver->getName();
str += "]FPS.",
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();

return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值