I. Prérequis▲
Nous commençons comme les autres tutoriels précédents. Veuillez noter que cette fois le fanion « shadow » dans createDevice() est mis à true, car nous voulons avoir un ombrage dynamique provenant des personnages animés. Si cet exemple s'exécute trop lentement, mettez-le à false. Le moteur Irrlicht vérifie si votre matériel supporte le « stencil buffer » et désactive les ombres par lui-même seulement dans le cas où la démo s'exécute trop lentement sur votre matériel.
#include
<irrlicht.h>
#include
<iostream>
#include
"driverChoice.h"
using
namespace
irr;
#ifdef _MSC_VER
#pragma comment(lib,
"Irrlicht.lib"
)
#endif
int
main()
{
// demande à l'utilisateur s'il veut l'ombrage dynamique.
char
i;
printf("Please press 'y' if you want to use realtime shadows.
\n
"
);
std::
cin >>
i;
const
bool
shadows =
(i ==
'y'
);
// demande à l'utilisateur un pilote.
video::
E_DRIVER_TYPE driverType=
driverChoiceConsole();
if
(driverType==
video::
EDT_COUNT)
return
1
;
Créons un moteur et quittons si la création échoue. Nous rendons le fanion « stencil buffer » optionnel pour éviter les ralentissements et exécuter sans les ombres.
IrrlichtDevice *
device =
createDevice(driverType, core::
dimension2d<
u32>
(640
, 480
),
16
, false
, shadows);
if
(device ==
0
)
return
1
; // ne peut pas créer le pilote sélectionné
video::
IVideoDriver*
driver =
device->
getVideoDriver();
scene::
ISceneManager*
smgr =
device->
getSceneManager();
Pour notre environnement, nous chargeons un fichier .3ds. C'est une petite pièce que j'ai modélisée avec Anim8or(1) et que j'ai exportée au format 3ds parce que le moteur Irrlicht ne supporte pas le format .an8. Je suis un très mauvais graphiste 3D, c'est pourquoi la texture appliquée au modèle n'est pas très jolie. Heureusement je suis meilleur programmeur qu'artiste, et donc le moteur Irrlicht est capable d'appliquer joliment la texture pour moi : juste en utilisant le manipulateur de mesh et en créant une application planaire de texture pour le mesh. Si vous voulez voir l'application que j'ai effectuée avec Anim8or, décommentez cette ligne. Aussi, je n'ai pas compris comment mettre correctement le matériel avec Anim8or, il a une couleur lumière spéculaire que je n'aime pas vraiment. Je le désactiverais aussi dans ce code.
scene::
IAnimatedMesh*
mesh =
smgr->
getMesh("../../media/room.3ds"
);
smgr->
getMeshManipulator()->
makePlanarTextureMapping(mesh->
getMesh(0
), 0.004
f);
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
);
II. Eau animée▲
Maintenant, le premier effet : l'eau animée. Le nœud WaterSurfaceSceneNode prend un mesh en entrée et le fait onduler comme une surface d'eau. Et si nous laissons ce nœud de scène utiliser un joli matériel comme le EMT_REFLECTION_2_LAYER, cela devient vraiment cool. Nous l'avons fait avec les quelques lignes de codes suivantes. Comme mesh d'entrée, nous créons un objet « Hill » plat, sans relief. Mais d'autres mesh peuvent être utilisés pour cela, si vous le souhaitez, vous pouvez même utiliser le room.3ds (ce qui serait visuellement assez étrange).
mesh =
smgr->
addHillPlaneMesh( "myHill"
,
core::
dimension2d<
f32>
(20
,20
),
core::
dimension2d<
u32>
(40
,40
), 0
, 0
,
core::
dimension2d<
f32>
(0
,0
),
core::
dimension2d<
f32>
(10
,10
));
node =
smgr->
addWaterSurfaceSceneNode(mesh->
getMesh(0
), 3.0
f, 300.0
f, 30.0
f);
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);
III. Panneau transparent et lumière dynamique▲
Le second effet est très basique, je parie que vous l'avez déjà vu dans quelques démonstrations du moteur Irrlicht : un panneau transparent combiné à une lumière dynamique. Nous créons simplement un nœud de scène lumière, le laissons voler autour et pour le rendre plus cool, nous y attachons un nœud de scène panneau.
// crée la lumière
node =
smgr->
addLightSceneNode(0
, core::
vector3df(0
,0
,0
),
video::
SColorf(1.0
f, 0.6
f, 0.7
f, 1.0
f), 800.0
f);
scene::
ISceneNodeAnimator*
anim =
0
;
anim =
smgr->
createFlyCircleAnimator (core::
vector3df(0
,150
,0
),250.0
f);
node->
addAnimator(anim);
anim->
drop();
// attache le panneau à la lumière
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"
));
IV. Système de particules▲
L'effet suivant est un peu plus intéressant : un système de particules. Le système de particules dans le moteur Irrlicht est assez modulaire et extensible, mais reste facile à utiliser. Il y a un nœud de scène « système de particules » dans lequel vous pouvez mettre un émetteur de particules qui fait sortir des particules de nulle part. Ces émetteurs sont assez flexibles et généralement ont beaucoup de paramètres comme la direction, la quantité, la couleur des particules qu'ils créent.
Il y a différents émetteurs, par exemple, un point émetteur qui laisse des particules sortir d'un point fixe. S'il n'y a pas suffisamment d'émetteurs de particules disponibles dans le moteur pour vous, vous pouvez facilement créer les vôtres. Vous n'avez qu'à créer une classe héritant de l'interface IparticleEmitter et de l'attacher au système de particules en utilisant setEmitter(). Dans cet exemple, nous créons un émetteur de particules « boîte » qui crée des particules aléatoirement dans une boîte. Les paramètres définissent la boîte, la direction des particules, le minimum et le maximum de nouvelles particules par seconde, la couleur et le maximum et le minimum de la durée de vie des particules.
Parce qu'avec seulement le système d'émetteurs de particules ce serait un petit peu ennuyant, il y a des affecteurs de particules qui modifient les particules pendant qu'elles volent. Les affecteurs peuvent être ajoutés à un système de particules pour simuler des effets supplémentaires comme la gravité ou le vent. L'affecteur de particules utilisé dans cet exemple est un affecteur qui modifie la couleur des particules : il les laisse s'estomper. Tout comme les émetteurs de particules, vous pouvez aussi créer d'autres affecteurs de particules en créant une classe héritant de IParticleAffector et en l'ajoutant avec addAffector().
Après, nous mettons une jolie texture au système de particules, nous avons un joli feu de camp. En ajustant le matériel, la texture, l'émetteur de particules et les paramètres de l'affecteur, il est aussi très facile de créer de la fumée, de la pluie, des explosions, de la neige, etc.
// crée un système de particule
scene::
IParticleSystemSceneNode*
ps =
smgr->
addParticleSystemSceneNode(false
);
scene::
IParticleEmitter*
em =
ps->
createBoxEmitter(
core::
aabbox3d<
f32>
(-
7
,0
,-
7
,7
,1
,7
), // taille de l'émetteur
core::
vector3df(0.0
f,0.06
f,0.0
f), // position initiale
80
,100
, // taux d'émission
video::
SColor(0
,255
,255
,255
), // la couleur la plus sombre
video::
SColor(0
,255
,255
,255
), // la couleur la plus lumineuse
800
,2000
,0
, // minimum et maximum âge, angle
core::
dimension2df(10.
f,10.
f), // taille minimum
core::
dimension2df(20.
f,20.
f)); // taille maximum
ps->
setEmitter(em); // Ceci prend l'émetteur
em->
drop(); // Donc on peut le jeter sans qu'il soit supprimé.
scene::
IParticleAffector*
paf =
ps->
createFadeOutParticleAffector();
ps->
addAffector(paf); // de même pour l'affecteur
paf->
drop();
ps->
setPosition(core::
vector3df(-
70
,60
,40
));
ps->
setScale(core::
vector3df(2
,2
,2
));
ps->
setMaterialFlag(video::
EMF_LIGHTING, false
);
ps->
setMaterialFlag(video::
EMF_ZWRITE_ENABLE, false
);
ps->
setMaterialTexture(0
, driver->
getTexture("../../media/fire.bmp"
));
ps->
setMaterialType(video::
EMT_TRANSPARENT_ADD_COLOR);
V. Nœud de scène de lumière volumétrique▲
Ensuite nous ajoutons un nœud de lumière volumétrique, qui ajoute une fausse aire lumineuse illuminant la scène. Comme avec les panneaux et les systèmes de particules, nous assignons aussi une texture pour l'effet désiré. Désormais, nous utiliserons un animateur de textures pour créer l'illusion d'un effet magique d'aire lumineuse.
scene::
IVolumeLightSceneNode *
n =
smgr->
addVolumeLightSceneNode(0
, -
1
,
32
, // Subdivisions sur l'axe U
32
, // Subdivisions sur l'axe V
video::
SColor(0
, 255
, 255
, 255
), // couleur de démarrage
video::
SColor(0
, 0
, 0
, 0
)); // couleur de fin
if
(n)
{
n->
setScale(core::
vector3df(56.0
f, 56.0
f, 56.0
f));
n->
setPosition(core::
vector3df(-
120
,50
,40
));
// charge les textures pour les animations
core::
array<
video::
ITexture*>
textures;
for
(s32 g=
7
; g >
0
; --
g)
{
core::
stringc tmp;
tmp =
"../../media/portal"
;
tmp +=
g;
tmp +=
".bmp"
;
video::
ITexture*
t =
driver->
getTexture( tmp.c_str() );
textures.push_back(t);
}
// crée un animateur de textures
scene::
ISceneNodeAnimator*
glow =
smgr->
createTextureAnimator(textures, 150
);
// ajoute l'animateur
n->
addAnimator(glow);
// jette l'animateur parce qu'il a été créé avec une fonction create()
glow->
drop();
}
VI. Ombre dynamique▲
Comme dernier effet, nous voulons une ombre dynamique correspondant à un personnage. Pour cela nous chargeons un modèle DirectX .x et le plaçons dans notre monde. Pour créer l'ombre, nous avons simplement besoin d'appeler addShadowVolumeSceneNode(). La couleur de l'ombre est seulement ajustable globalement pour toutes les ombres en appelant IsceneManager::setShadowColor(). Voilà notre ombre dynamique.
Parce que le personnage est un petit peu trop petit pour cette scène, nous le rendons plus grand en utilisant setScale(). Et parce que le personnage est éclairé par une lumière dynamique, nous avons besoin de normaliser les normales pour rendre l'éclairage correct. C'est toujours nécessaire si l'échelle du personnage n'est pas (1,1,1). Sinon, ce serait trop sombre ou trop lumineux parce que la norme sera aussi mise à l'échelle.
scene::
ICameraSceneNode*
camera =
smgr->
addCameraSceneNodeFPS();
camera->
setPosition(core::
vector3df(-
50
,50
,-
150
));
camera->
setFarValue(10000.0
f); // ceci augmente la distance de visibilité d'une ombre
// désactive le curseur de la souris
device->
getCursorControl()->
setVisible(false
);
s32 lastFPS =
-
1
;
while
(device->
run())
if
(device->
isWindowActive())
{
driver->
beginScene(true
, true
, 0
);
smgr->
drawAll();
driver->
endScene();
const
s32 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
;
}
VII. Conclusion▲
Vous pouvez désormais faire quelques effets spéciaux avec Irrlicht.
Dans le prochain tutoriel Visualiseur de mesh, nous verrons comment construire des applications plus complexes avec Irrlicht, nous reverrons les éléments d'interface utilisateur plus en détail et nous découvrirons comment utiliser le format XML avec Irrlicht.
VIII. Remerciements▲
Merci à Nikolaus Gebhardt de nous permettre de traduire ce tutoriel.
Merci à LittleWhite pour sa relecture technique ainsi qu'à ClaudeLELOUP pour sa relecture orthographique.