这个例子告诉我们如何去做一些特效。我会告诉大家如何去使用深度缓冲进行阴影计算,粒子系统,公告版,动态光照以及制作水表场景节点.

/*
这个例子告诉我们如何去做一些特效。我会告诉大家如何去使用深度缓冲进行阴影计算,粒子系统,公告版,动态光照以及制作水表场景节点。

我们依旧象之前的例子一样开始,但是注意,因为我们想为角色创建一个动态光影,
所以这次我们在创建设备的时候CreateDevice()应当把最后一个参数是否开启深度缓冲这项设置为“打开”。
如果你嫌进行动态光影计算之后渲染太慢了,你就设置它为false关闭。Irr引擎会自动检查
你的显卡是否支持深度缓冲,如果你的电脑硬件不支持深度缓冲,它会自动将你关闭掉它。
*/

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

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")

int main()
{
// 询问用户是否要使用动态阴影

char i;
printf("Please press 'y' if you want to use realtime shadows.\n");

std::cin >> i;
bool shadows = (i == 'y');

// 询问用户希望创建的设备类型

video::E_DRIVER_TYPE driverType;

printf("Please select the driver you want for this example:\n"\
   " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
   " (d) Software Renderer\n (e) Burning's Software Renderer\n"\
   " (f) NullDevice\n (otherKey) exit\n\n");

std::cin >> i;

switch(i)
{
   case 'a': driverType = video::EDT_DIRECT3D9;break;
   case 'b': driverType = video::EDT_DIRECT3D8;break;
   case 'c': driverType = video::EDT_OPENGL;   break;
   case 'd': driverType = video::EDT_SOFTWARE; break;
   case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
   case 'f': driverType = video::EDT_NULL;     break;
   default: return 1;
}


IrrlichtDevice *device =
   createDevice(driverType, core::dimension2d<s32>(640, 480),
   16, false, shadows);

if (device == 0)
   return 1;

video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();

/*
在本例中,我们读取一个.3ds文件。这是我使用Anim08or创建的一个小房子模型,
因为Irr引擎不支持.an8格式,于是我把它导出为.3ds格式。我的3D美术实在很糟糕,
所以我制作的纹理贴图看起来肯定会不太漂亮。幸好我是一个程序员,而非美术人员,
更幸运的是Irr引擎可以帮我提供一个很绚的材质贴图,方法是:只要使用Mesh管理器
为Mesh创建一个平面材质贴图就可以了。如果你想看我用Anim08or做的采制贴图的话,
那只要把makePlanarTextureMapping()这行注释掉就可以了。我同样不会讲述如何使用Anim8or
创建材质,因为Anim8or中有我非常不喜欢的灯光设置。
*/

scene::IAnimatedMesh* mesh = smgr->getMesh(
   "../../media/room.3ds");

smgr->getMeshManipulator()->makePlanarTextureMapping(
   mesh->getMesh(0), 0.004f);

scene::ISceneNode* node = 0;

node = smgr->addAnimatedMeshSceneNode(mesh);
node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
node->getMaterial(0).SpecularColor.set(0,0,0,0);

/*
现在,我们开始制作第一个特效:生动的水。
它是这样工作的:将一个Mesh制作成水表场景节点,然后使它象水面一样起波纹。
如果我们让这个场景节点用一个象EMT_REFLECTION_2_LAYER这样的优秀的材质,水表看起来就很酷了。
象导入Mesh一样,我们创建一个丘陵平面状的Mesh,用它来做为水面。
之后我们为水面设置两层纹理,一层做为水表,一层做为水底物体透视纹理。
最后我们为水面设置一个很优秀的材质使它看起来更象一些。
(译者注:用户在阅读该部分时,请多进行参数修改调试以了解各函数作用)
*/


mesh = smgr->addHillPlaneMesh("myHill",
   core::dimension2d<f32>(20,20),
   core::dimension2d<s32>(40,40), 0, 0,
   core::dimension2d<f32>(0,0),
   core::dimension2d<f32>(10,10));

node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
node->setPosition(core::vector3df(0,7,0));

node->setMaterialTexture(0, driver->getTexture("../../media/stones.jpg"));
node->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));

node->setMaterialType(video::EMT_REFLECTION_2_LAYER);

/*
第二个特效是非常简单基础的,我敢打赌你在一些Irr引擎例子中一定见过这样的情景:
一个透明公告板和一个动态光线结合起来实现一个动态的灯光。我们创建一个灯光场景节点,
使它旋转飞行。并为它绑定一个公告板场景节点,用公告版来表示当前灯光光源所在地。
*/

// 创建灯光

node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
   video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 600.0f);
scene::ISceneNodeAnimator* anim = 0;
anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
node->addAnimator(anim);
anim->drop();

// 在灯光上绑定一个公告版以表示光源点

node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));

/*
下一个特效就更加有意思了:它就是粒子系统。Irr引擎中的粒子系统很容易扩展而且非常容易使用。
我们可以创建一个粒子系统场景节点,然后在其中创建一个粒子发射器,用它来发射粒子。
粒子发射器有一些通用而且灵活的参数,例如粒子的方向,个数,颜色等。而且我们还有不同的
粒子发射器可供选择,例如点状粒子发射器会让粒子从一个点进行发射。如果Irr引擎中的粒子
发射器类型不能满足你的需要,你当然也可以很容易创建一个你所需要的发射器。只要你继承
IParticleEmitter接口实现一个自定义类就可以了。在这里例子中我们创建一个盒子状的粒子发射器,
就是说粒子在一个盒子形状内随机生成。创建函数的参数分别定义了发射器盒子属性,粒子方向,
每秒生成粒子的个数范围,粒子颜色,粒子存活时间等。

因为把所有属性都放置在粒子发射器系统中,会使粒子发射器创建时令人厌烦。
所以在粒子飞行的时候,也有些粒子属性可以被加入粒子系统中。例如,风向,重力等。
在这个例子中我们加入了一个影响器,让它对粒子进行印象,它改变了粒子的颜色属性:
使粒子颜色拥有了淡出的属性。就象粒子发射器一样,你可以创建自己的粒子影响器类型,只要你
继承一个IParticleAffector的类,并实现其AddAffector()函数就OK了。

在我们为粒子设置一个优秀的材质之后,我们就获得了一个很逼真的火焰效果了。
经过调整材质,纹理,粒子发射器和粒子影响器的参数,我们就很轻松的可以创建出烟雾,
雨水,爆炸,雪这种效果了。
*/

// 创建一个粒子系统

scene::IParticleSystemSceneNode* ps = 0;
ps = smgr->addParticleSystemSceneNode(false);
ps->setPosition(core::vector3df(-70,60,40));
ps->setScale(core::vector3df(2,2,2));

ps->setParticleSize(core::dimension2d<f32>(20.0f, 20.0f));

scene::IParticleEmitter* em = ps->createBoxEmitter(
   core::aabbox3d<f32>(-7,0,-7,7,1,7),
   core::vector3df(0.0f,0.06f,0.0f),
   80,100,
   video::SColor(0,255,255,255), video::SColor(0,255,255,255),
   800,2000);

ps->setEmitter(em);
em->drop();

scene::IParticleAffector* paf =
   ps->createFadeOutParticleAffector();

ps->addAffector(paf);
paf->drop();

ps->setMaterialFlag(video::EMF_LIGHTING, false);
ps->setMaterialTexture(0, driver->getTexture("../../media/fire.bmp"));
ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);

/*
我们最后一个特效就是,我们为一个动画模型设置一个动态阴影。
我们读取一个DirectX的.x格式模型。并将它放置到我们的世界中。之后我们需要简单的
调用addShadowVolumeSceneNode()函数,我们全局所有的阴影都可以调用ISceneManager::setShadowColor()
来设置它们的颜色,瞧,这就是我们的动态阴影。

因为我们的角色模型有点小,我们调用setScale()函数使它看起来大一些。
又因为我们的角色模型被一个动态光线照着,我们需要将其法线进行格式化,保证光照计算的正确性。
如果一个动态光线照射下的模型缩放比例不是(1, 1, 1)的话,就必须做这一步。
否则的话,你就会发现由于法线的缩放而导致阴影也变的过亮或者过暗。
*/

// 为场景增加一个动画模型

mesh = smgr->getMesh("../../media/dwarf.x");
scene::IAnimatedMeshSceneNode* anode = 0;

anode = smgr->addAnimatedMeshSceneNode(mesh);
anode->setPosition(core::vector3df(-50,20,-60));
anode->setAnimationSpeed(15);

// 增加一个阴影场景节点
anode->addShadowVolumeSceneNode();
smgr->setShadowColor(video::SColor(150,0,0,0));

// 把模型进行一下放大,并且将其法线格式化,以保证正确的光照计算。
anode->setScale(core::vector3df(2,2,2));
anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);

/*
最后我们简单的将一些绘制出来,就OK了
*/

scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(core::vector3df(-50,50,-150));

// 关闭鼠标显示
device->getCursorControl()->setVisible(false);


int lastFPS = -1;

while(device->run())
if (device->isWindowActive())
{
   driver->beginScene(true, true, 0);

   smgr->drawAll();

   driver->endScene();

   int fps = driver->getFPS();

   if (lastFPS != fps)
   {
    core::stringw str = L"Irrlicht Engine - SpecialFX example [";
    str += driver->getName();
    str += "] FPS:";
    str += fps;

    device->setWindowCaption(str.c_str());
    lastFPS = fps;
   }
}

device->drop();

return 0;
}