您的位置:首页 > 其它

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

2016-07-10 11:11 351 查看


说明:

这个粒子是为了解释灯光管理

涉及:

允许使用超过硬件支持的数量的动态灯光
以及灯光管理器申请
每个节点的回调

详情请看源码及注释.


源码及注释:

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Irrlicht