1 /** Example 008 SpecialFX
2 
3 This tutorials describes how to do special effects. It shows how to use stencil
4 buffer shadows, the particle system, billboards, dynamic light, and the water
5 surface scene node.
6 
7 We start like in some tutorials before. Please note that this time, the
8 'shadows' flag in createDevice() is set to true, for we want to have a dynamic
9 shadow casted from an animated character. If this example runs too slow,
10 set it to false. The Irrlicht Engine checks if your hardware doesn't support
11 the stencil buffer, and disables shadows by itself, but just in case the demo
12 runs slow on your hardware.
13 */
14 
15 #include <irrlicht.h>
16 #include <iostream>
17 #include "driverChoice.h"
18 
19 using namespace irr;
20 
21 #ifdef _MSC_VER
22 #pragma comment(lib, "Irrlicht.lib")
23 #endif
24 
main()25 int main()
26 {
27 	// ask if user would like shadows
28 	char i;
29 	printf("Please press 'y' if you want to use realtime shadows.\n");
30 
31 	std::cin >> i;
32 
33 	const bool shadows = (i == 'y');
34 
35 	// ask user for driver
36 	video::E_DRIVER_TYPE driverType=driverChoiceConsole();
37 	if (driverType==video::EDT_COUNT)
38 		return 1;
39 
40 
41 	/*
42 	Create device and exit if creation failed. We make the stencil flag
43 	optional to avoid slow screen modes for runs without shadows.
44 	*/
45 
46 	IrrlichtDevice *device =
47 		createDevice(driverType, core::dimension2d<u32>(640, 480),
48 		16, false, shadows);
49 
50 	if (device == 0)
51 		return 1; // could not create selected driver.
52 
53 	video::IVideoDriver* driver = device->getVideoDriver();
54 	scene::ISceneManager* smgr = device->getSceneManager();
55 
56 	/*
57 	For our environment, we load a .3ds file. It is a small room I modelled
58 	with Anim8or and exported into the 3ds format because the Irrlicht
59 	Engine does not support the .an8 format. I am a very bad 3d graphic
60 	artist, and so the texture mapping is not very nice in this model.
61 	Luckily I am a better programmer than artist, and so the Irrlicht
62 	Engine is able to create a cool texture mapping for me: Just use the
63 	mesh manipulator and create a planar texture mapping for the mesh. If
64 	you want to see the mapping I made with Anim8or, uncomment this line. I
65 	also did not figure out how to set the material right in Anim8or, it
66 	has a specular light color which I don't really like. I'll switch it
67 	off too with this code.
68 	*/
69 
70 	scene::IAnimatedMesh* mesh = smgr->getMesh("../../media/room.3ds");
71 
72 	smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);
73 
74 	scene::ISceneNode* node = 0;
75 
76 	node = smgr->addAnimatedMeshSceneNode(mesh);
77 	node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
78 	node->getMaterial(0).SpecularColor.set(0,0,0,0);
79 
80 	/*
81 	Now, for the first special effect: Animated water. It works like this:
82 	The WaterSurfaceSceneNode takes a mesh as input and makes it wave like
83 	a water surface. And if we let this scene node use a nice material like
84 	the EMT_REFLECTION_2_LAYER, it looks really cool. We are doing this
85 	with the next few lines of code. As input mesh, we create a hill plane
86 	mesh, without hills. But any other mesh could be used for this, you
87 	could even use the room.3ds (which would look really strange) if you
88 	want to.
89 	*/
90 
91 	mesh = smgr->addHillPlaneMesh( "myHill",
92 		core::dimension2d<f32>(20,20),
93 		core::dimension2d<u32>(40,40), 0, 0,
94 		core::dimension2d<f32>(0,0),
95 		core::dimension2d<f32>(10,10));
96 
97 	node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f, 30.0f);
98 	node->setPosition(core::vector3df(0,7,0));
99 
100 	node->setMaterialTexture(0, driver->getTexture("../../media/stones.jpg"));
101 	node->setMaterialTexture(1, driver->getTexture("../../media/water.jpg"));
102 
103 	node->setMaterialType(video::EMT_REFLECTION_2_LAYER);
104 
105 	/*
106 	The second special effect is very basic, I bet you saw it already in
107 	some Irrlicht Engine demos: A transparent billboard combined with a
108 	dynamic light. We simply create a light scene node, let it fly around,
109 	and to make it look more cool, we attach a billboard scene node to it.
110 	*/
111 
112 	// create light
113 
114 	node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
115 		video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f);
116 	scene::ISceneNodeAnimator* anim = 0;
117 	anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
118 	node->addAnimator(anim);
119 	anim->drop();
120 
121 	// attach billboard to light
122 
123 	node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
124 	node->setMaterialFlag(video::EMF_LIGHTING, false);
125 	node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
126 	node->setMaterialTexture(0, driver->getTexture("../../media/particlewhite.bmp"));
127 
128 	/*
129 	The next special effect is a lot more interesting: A particle system.
130 	The particle system in the Irrlicht Engine is quite modular and
131 	extensible, but yet easy to use. There is a particle system scene node
132 	into which you can put a particle emitter, which makes particles come out
133 	of nothing. These emitters are quite flexible and usually have lots of
134 	parameters like direction, amount, and color of the particles they
135 	create.
136 
137 	There are different emitters, for example a point emitter which lets
138 	particles pop out at a fixed point. If the particle emitters available
139 	in the engine are not enough for you, you can easily create your own
140 	ones, you'll simply have to create a class derived from the
141 	IParticleEmitter interface and attach it to the particle system using
142 	setEmitter(). In this example we create a box particle emitter, which
143 	creates particles randomly inside a box. The parameters define the box,
144 	direction of the particles, minimal and maximal new particles per
145 	second, color, and minimal and maximal lifetime of the particles.
146 
147 	Because only with emitters particle system would be a little bit
148 	boring, there are particle affectors which modify particles while
149 	they fly around. Affectors can be added to a particle system for
150 	simulating additional effects like gravity or wind.
151 	The particle affector we use in this example is an affector which
152 	modifies the color of the particles: It lets them fade out. Like the
153 	particle emitters, additional particle affectors can also be
154 	implemented by you, simply derive a class from IParticleAffector and
155 	add it with addAffector().
156 
157 	After we set a nice material to the particle system, we have a cool
158 	looking camp fire. By adjusting material, texture, particle emitter,
159 	and affector parameters, it is also easily possible to create smoke,
160 	rain, explosions, snow, and so on.
161 	*/
162 
163 	// create a particle system
164 
165 	scene::IParticleSystemSceneNode* ps =
166 		smgr->addParticleSystemSceneNode(false);
167 
168 	scene::IParticleEmitter* em = ps->createBoxEmitter(
169 		core::aabbox3d<f32>(-7,0,-7,7,1,7), // emitter size
170 		core::vector3df(0.0f,0.06f,0.0f),   // initial direction
171 		80,100,                             // emit rate
172 		video::SColor(0,255,255,255),       // darkest color
173 		video::SColor(0,255,255,255),       // brightest color
174 		800,2000,0,                         // min and max age, angle
175 		core::dimension2df(10.f,10.f),         // min size
176 		core::dimension2df(20.f,20.f));        // max size
177 
178 	ps->setEmitter(em); // this grabs the emitter
179 	em->drop(); // so we can drop it here without deleting it
180 
181 	scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
182 
183 	ps->addAffector(paf); // same goes for the affector
184 	paf->drop();
185 
186 	ps->setPosition(core::vector3df(-70,60,40));
187 	ps->setScale(core::vector3df(2,2,2));
188 	ps->setMaterialFlag(video::EMF_LIGHTING, false);
189 	ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
190 	ps->setMaterialTexture(0, driver->getTexture("../../media/fire.bmp"));
191 	ps->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
192 
193 	/*
194 	Next we add a volumetric light node, which adds a glowing fake area light to
195 	the scene. Like with the billboards and particle systems we also assign a
196 	texture for the desired effect, though this time we'll use a texture animator
197 	to create the illusion of a magical glowing area effect.
198 	*/
199 	scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(0, -1,
200 				32,                              // Subdivisions on U axis
201 				32,                              // Subdivisions on V axis
202 				video::SColor(0, 255, 255, 255), // foot color
203 				video::SColor(0, 0, 0, 0));      // tail color
204 
205 	if (n)
206 	{
207 		n->setScale(core::vector3df(56.0f, 56.0f, 56.0f));
208 		n->setPosition(core::vector3df(-120,50,40));
209 
210 		// load textures for animation
211 		core::array<video::ITexture*> textures;
212 		for (s32 g=7; g > 0; --g)
213 		{
214 			core::stringc tmp;
215 			tmp = "../../media/portal";
216 			tmp += g;
217 			tmp += ".bmp";
218 			video::ITexture* t = driver->getTexture( tmp.c_str() );
219 			textures.push_back(t);
220 		}
221 
222 		// create texture animator
223 		scene::ISceneNodeAnimator* glow = smgr->createTextureAnimator(textures, 150);
224 
225 		// add the animator
226 		n->addAnimator(glow);
227 
228 		// drop the animator because it was created with a create() function
229 		glow->drop();
230 	}
231 
232 	/*
233 	As our last special effect, we want a dynamic shadow be casted from an
234 	animated character. For this we load a DirectX .x model and place it
235 	into our world. For creating the shadow, we simply need to call
236 	addShadowVolumeSceneNode(). The color of shadows is only adjustable
237 	globally for all shadows, by calling ISceneManager::setShadowColor().
238 	Voila, here is our dynamic shadow.
239 
240 	Because the character is a little bit too small for this scene, we make
241 	it bigger using setScale(). And because the character is lighted by a
242 	dynamic light, we need to normalize the normals to make the lighting on
243 	it correct. This is always necessary if the scale of a dynamic lighted
244 	model is not (1,1,1). Otherwise it would get too dark or too bright
245 	because the normals will be scaled too.
246 	*/
247 
248 	// add animated character
249 
250 	mesh = smgr->getMesh("../../media/dwarf.x");
251 	scene::IAnimatedMeshSceneNode* anode = 0;
252 
253 	anode = smgr->addAnimatedMeshSceneNode(mesh);
254 	anode->setPosition(core::vector3df(-50,20,-60));
255 	anode->setAnimationSpeed(15);
256 
257 	// add shadow
258 	anode->addShadowVolumeSceneNode();
259 	smgr->setShadowColor(video::SColor(150,0,0,0));
260 
261 	// make the model a little bit bigger and normalize its normals
262 	// because of the scaling, for correct lighting
263 	anode->setScale(core::vector3df(2,2,2));
264 	anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
265 
266 	/*
267 	Finally we simply have to draw everything, that's all.
268 	*/
269 
270 	scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
271 	camera->setPosition(core::vector3df(-50,50,-150));
272 	camera->setFarValue(10000.0f); // this increase a shadow visible range.
273 
274 	// disable mouse cursor
275 	device->getCursorControl()->setVisible(false);
276 
277 	s32 lastFPS = -1;
278 
279 	while(device->run())
280 	if (device->isWindowActive())
281 	{
282 		driver->beginScene(true, true, 0);
283 
284 		smgr->drawAll();
285 
286 		driver->endScene();
287 
288 		const s32 fps = driver->getFPS();
289 
290 		if (lastFPS != fps)
291 		{
292 			core::stringw str = L"Irrlicht Engine - SpecialFX example [";
293 			str += driver->getName();
294 			str += "] FPS:";
295 			str += fps;
296 
297 			device->setWindowCaption(str.c_str());
298 			lastFPS = fps;
299 		}
300 	}
301 
302 	device->drop();
303 
304 	return 0;
305 }
306 
307 /*
308 **/
309