This tutorials describes how to do special effects. It shows how to use stencil buffer shadows, the particle system, billboards, dynamic light, and the water surface scene node.
We start like in some tutorials before. Please note that this time, the 'shadows' flag in createDevice() is set to true, for we want to have a dynamic shadow casted from an animated character. If this example runs too slow, set it to false. The Irrlicht Engine checks if your hardware doesn't support the stencil buffer, and disables shadows by itself, but just in case the demo runs slow on your hardware.
#include <iostream>
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
int main()
{
char i;
printf("Please press 'y' if you want to use realtime shadows.\n");
std::cin >> i;
const bool shadows = (i == 'y');
if (driverType==video::EDT_COUNT)
return 1;
Main header file of the irrlicht, the only file needed to include.
E_DRIVER_TYPE
An enum for all types of drivers the Irrlicht Engine supports.
Everything in the Irrlicht Engine can be found in this namespace.
Create device and exit if creation failed. We make the stencil flag optional to avoid slow screen modes for runs without shadows.
16, false, shadows);
if (device == 0)
return 1;
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
virtual scene::ISceneManager * getSceneManager()=0
Provides access to the scene manager.
virtual video::IVideoDriver * getVideoDriver()=0
Provides access to the video driver for drawing 3d and 2d geometry.
Axis aligned bounding box in 3d dimensional space.
The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff.
Interface to driver which is able to perform 2d and 3d graphics functions.
For our environment, we load a .3ds file. It is a small room I modelled with Anim8or and exported into the 3ds format because the Irrlicht Engine does not support the .an8 format. I am a very bad 3d graphic artist, and so the texture mapping is not very nice in this model. Luckily I am a better programmer than artist, and so the Irrlicht Engine is able to create a cool texture mapping for me: Just use the mesh manipulator and create a planar texture mapping for the mesh. If you want to see the mapping I made with Anim8or, uncomment this line. I also did not figure out how to set the material right in Anim8or, it has a specular light color which I don't really like. I'll switch it off too with this code.
Interface for an animated mesh.
virtual IMesh * getMesh(s32 frame, s32 detailLevel=255, s32 startFrameLoop=-1, s32 endFrameLoop=-1)=0
Returns the IMesh interface for a frame.
virtual void makePlanarTextureMapping(IMesh *mesh, f32 resolution=0.001f) const =0
Creates a planar texture mapping on the mesh.
virtual IAnimatedMesh * getMesh(const io::path &filename)=0
Get pointer to an animateable mesh. Loads the file if not loaded already.
virtual IAnimatedMeshSceneNode * addAnimatedMeshSceneNode(IAnimatedMesh *mesh, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f), bool alsoAddIfMeshPointerZero=false)=0
Adds a scene node for rendering an animated mesh model.
virtual IMeshManipulator * getMeshManipulator()=0
Get pointer to the mesh manipulator.
void setMaterialTexture(u32 textureLayer, video::ITexture *texture)
Sets the texture of the specified layer in all materials of this scene node to the new texture.
virtual video::SMaterial & getMaterial(u32 num)
Returns the material based on the zero based index i.
virtual ITexture * getTexture(const io::path &filename)=0
Get access to a named texture.
void set(u32 a, u32 r, u32 g, u32 b)
Sets all four components of the color at once.
SColor SpecularColor
How much specular light (highlights from a light) is reflected.
Now, for the first special effect: Animated water. It works like this: The WaterSurfaceSceneNode takes a mesh as input and makes it wave like a water surface. And if we let this scene node use a nice material like the EMT_REFLECTION_2_LAYER, it looks really cool. We are doing this with the next few lines of code. As input mesh, we create a hill plane mesh, without hills. But any other mesh could be used for this, you could even use the room.3ds (which would look really strange) if you want to.
virtual ISceneNode * addWaterSurfaceSceneNode(IMesh *mesh, f32 waveHeight=2.0f, f32 waveSpeed=300.0f, f32 waveLength=10.0f, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f))=0
Adds a scene node for rendering a animated water surface mesh.
virtual IAnimatedMesh * addHillPlaneMesh(const io::path &name, const core::dimension2d< f32 > &tileSize, const core::dimension2d< u32 > &tileCount, video::SMaterial *material=0, f32 hillHeight=0.0f, const core::dimension2d< f32 > &countHills=core::dimension2d< f32 >(0.0f, 0.0f), const core::dimension2d< f32 > &textureRepeatCount=core::dimension2d< f32 >(1.0f, 1.0f))=0
Adds a Hill Plane mesh to the mesh pool.
virtual void setPosition(const core::vector3df &newpos)
Sets the position of the node relative to its parent.
void setMaterialType(video::E_MATERIAL_TYPE newType)
Sets the material type of all materials in this scene node to a new material type.
The second special effect is very basic, I bet you saw it already in some Irrlicht Engine demos: A transparent billboard combined with a dynamic light. We simply create a light scene node, let it fly around, and to make it look more cool, we attach a billboard scene node to it.
bool drop() const
Drops the object. Decrements the reference counter by one.
virtual IBillboardSceneNode * addBillboardSceneNode(ISceneNode *parent=0, const core::dimension2d< f32 > &size=core::dimension2d< f32 >(10.0f, 10.0f), const core::vector3df &position=core::vector3df(0, 0, 0), s32 id=-1, video::SColor colorTop=0xFFFFFFFF, video::SColor colorBottom=0xFFFFFFFF)=0
Adds a billboard scene node to the scene graph.
virtual ILightSceneNode * addLightSceneNode(ISceneNode *parent=0, const core::vector3df &position=core::vector3df(0, 0, 0), video::SColorf color=video::SColorf(1.0f, 1.0f, 1.0f), f32 radius=100.0f, s32 id=-1)=0
Adds a dynamic light scene node to the scene graph.
virtual ISceneNodeAnimator * createFlyCircleAnimator(const core::vector3df ¢er=core::vector3df(0.f, 0.f, 0.f), f32 radius=100.f, f32 speed=0.001f, const core::vector3df &direction=core::vector3df(0.f, 1.f, 0.f), f32 startPosition=0.f, f32 radiusEllipsoid=0.f)=0
Creates a fly circle animator, which lets the attached scene node fly around a center.
Animates a scene node. Can animate position, rotation, material, and so on.
virtual void addAnimator(ISceneNodeAnimator *animator)
Adds an animator which should animate this node.
void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
Sets all material flags at once to a new value.
Class representing a color with four floats.
The next special effect is a lot more interesting: A particle system. The particle system in the Irrlicht Engine is quite modular and extensible, but yet easy to use. There is a particle system scene node into which you can put a particle emitter, which makes particles come out of nothing. These emitters are quite flexible and usually have lots of parameters like direction, amount, and color of the particles they create.
There are different emitters, for example a point emitter which lets particles pop out at a fixed point. If the particle emitters available in the engine are not enough for you, you can easily create your own ones, you'll simply have to create a class derived from the IParticleEmitter interface and attach it to the particle system using setEmitter(). In this example we create a box particle emitter, which creates particles randomly inside a box. The parameters define the box, direction of the particles, minimal and maximal new particles per second, color, and minimal and maximal lifetime of the particles.
Because only with emitters particle system would be a little bit boring, there are particle affectors which modify particles while they fly around. Affectors can be added to a particle system for simulating additional effects like gravity or wind. The particle affector we use in this example is an affector which modifies the color of the particles: It lets them fade out. Like the particle emitters, additional particle affectors can also be implemented by you, simply derive a class from IParticleAffector and add it with addAffector().
After we set a nice material to the particle system, we have a cool looking camp fire. By adjusting material, texture, particle emitter, and affector parameters, it is also easily possible to create smoke, rain, explosions, snow, and so on.
80,100,
800,2000,0,
A particle affector modifies particles.
A particle emitter for using with particle systems.
A particle system scene node for creating snow, fire, exlosions, smoke...
virtual IParticleFadeOutAffector * createFadeOutParticleAffector(const video::SColor &targetColor=video::SColor(0, 0, 0, 0), u32 timeNeededToFadeOut=1000)=0
Creates a fade out particle affector.
virtual void addAffector(IParticleAffector *affector)=0
Adds new particle effector to the particle system.
virtual IParticleBoxEmitter * createBoxEmitter(const core::aabbox3df &box=core::aabbox3df(-10, 28,-10, 10, 30, 10), const core::vector3df &direction=core::vector3df(0.0f, 0.03f, 0.0f), u32 minParticlesPerSecond=5, u32 maxParticlesPerSecond=10, const video::SColor &minStartColor=video::SColor(255, 0, 0, 0), const video::SColor &maxStartColor=video::SColor(255, 255, 255, 255), u32 lifeTimeMin=2000, u32 lifeTimeMax=4000, s32 maxAngleDegrees=0, const core::dimension2df &minStartSize=core::dimension2df(5.0f, 5.0f), const core::dimension2df &maxStartSize=core::dimension2df(5.0f, 5.0f))=0
Creates a box particle emitter.
virtual void setEmitter(IParticleEmitter *emitter)=0
Sets the particle emitter, which creates the particles.
virtual IParticleSystemSceneNode * addParticleSystemSceneNode(bool withDefaultEmitter=true, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f))=0
Adds a particle system scene node to the scene graph.
virtual void setScale(const core::vector3df &scale)
Sets the relative scale of the scene node.
Class representing a 32 bit ARGB color.
Next we add a volumetric light node, which adds a glowing fake area light to the scene. Like with the billboards and particle systems we also assign a texture for the desired effect, though this time we'll use a texture animator to create the illusion of a magical glowing area effect.
32,
32,
if (n)
{
for (
s32 g=7; g > 0; --g)
{
tmp = "../../media/portal";
tmp += g;
tmp += ".bmp";
}
}
void push_back(const T &element)
Adds an element at back of array.
virtual ISceneNodeAnimator * createTextureAnimator(const core::array< video::ITexture * > &textures, s32 timePerFrame, bool loop=true)=0
Creates a texture animator, which switches the textures of the target scene node based on a list of t...
virtual IVolumeLightSceneNode * addVolumeLightSceneNode(ISceneNode *parent=0, s32 id=-1, const u32 subdivU=32, const u32 subdivV=32, const video::SColor foot=video::SColor(51, 0, 230, 180), const video::SColor tail=video::SColor(0, 0, 0, 0), const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f))=0
adds Volume Lighting Scene Node.
Interface of a Video Driver dependent Texture.
signed int s32
32 bit signed variable.
As our last special effect, we want a dynamic shadow be casted from an animated character. For this we load a DirectX .x model and place it into our world. For creating the shadow, we simply need to call addShadowVolumeSceneNode(). The color of shadows is only adjustable globally for all shadows, by calling ISceneManager::setShadowColor(). Voila, here is our dynamic shadow.
Because the character is a little bit too small for this scene, we make it bigger using setScale(). And because the character is lighted by a dynamic light, we need to normalize the normals to make the lighting on it correct. This is always necessary if the scale of a dynamic lighted model is not (1,1,1). Otherwise it would get too dark or too bright because the normals will be scaled too.
mesh = smgr->
getMesh(
"../../media/dwarf.x");
Scene node capable of displaying an animated mesh and its shadow.
virtual IShadowVolumeSceneNode * addShadowVolumeSceneNode(const IMesh *shadowMesh=0, s32 id=-1, bool zfailmethod=true, f32 infinity=1000.0f)=0
Creates shadow volume scene node as child of this node.
virtual void setAnimationSpeed(f32 framesPerSecond)=0
Sets the speed with which the animation is played.
virtual void setShadowColor(video::SColor color=video::SColor(150, 0, 0, 0))=0
Sets the color of stencil buffers shadows drawn by the scene manager.
Finally we simply have to draw everything, that's all.
{
if (lastFPS != fps)
{
str += "] FPS:";
str += fps;
lastFPS = fps;
}
}
return 0;
}
virtual bool run()=0
Runs the device.
virtual void setWindowCaption(const wchar_t *text)=0
Sets the caption of the window.
virtual gui::ICursorControl * getCursorControl()=0
Provides access to the cursor control.
virtual bool isWindowActive() const =0
Returns if the window is active.
virtual void setVisible(bool visible)=0
Changes the visible state of the mouse cursor.
Scene Node which is a (controlable) camera.
virtual void setFarValue(f32 zf)=0
Sets the value of the far clipping plane (default: 2000.0f)
virtual void drawAll()=0
Draws all the scene nodes.
virtual ICameraSceneNode * addCameraSceneNodeFPS(ISceneNode *parent=0, f32 rotateSpeed=100.0f, f32 moveSpeed=0.5f, s32 id=-1, SKeyMap *keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, f32 jumpSpeed=0.f, bool invertMouse=false, bool makeActive=true)=0
Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for f...
virtual bool beginScene(bool backBuffer=true, bool zBuffer=true, SColor color=SColor(255, 0, 0, 0), const SExposedVideoData &videoData=SExposedVideoData(), core::rect< s32 > *sourceRect=0)=0
Applications must call this method before performing any rendering.
virtual s32 getFPS() const =0
Returns current frames per second value.
virtual const wchar_t * getName() const =0
Gets name of this video driver.
virtual bool endScene()=0
Presents the rendered image to the screen.