I. Prérequis▲
#include
<irrlicht.h>
#if defined ( _IRR_WINDOWS_ )
#include
<windows.h>
#endif
using
namespace
irr;
using
namespace
core;
using
namespace
scene;
using
namespace
video;
using
namespace
io;
using
namespace
gui;
#pragma comment(lib,
"Irrlicht.lib"
)
class
EventReceiver_basic : public
IEventReceiver
{
private
:
IrrlichtDevice *
Device;
public
:
EventReceiver_basic ( IrrlichtDevice *
device ): Device ( device ) {}
virtual
bool
OnEvent(const
SEvent&
event)
{
if
(event.EventType ==
EET_GUI_EVENT)
{
s32 id =
event.GUIEvent.Caller->
getID();
switch
(event.GUIEvent.EventType)
{
case
EGET_BUTTON_CLICKED:
if
(id ==
2
)
{
Device->
closeDevice();
return
true
;
}
break
;
}
}
return
false
;
}
}
;
class
CSampleSceneNode : public
ISceneNode
{
aabbox3d<
f32>
Box;
S3DVertex Vertices[4
];
SMaterial Material;
public
:
CSampleSceneNode(ISceneNode*
parent, ISceneManager*
mgr, s32 id)
:
ISceneNode(parent, mgr, id)
{
Material.Wireframe =
false
;
Material.Lighting =
false
;
Vertices[0
] =
S3DVertex(0
,0
,10
, 1
,1
,0
, SColor(255
,0
,255
,255
), 0
, 1
);
Vertices[1
] =
S3DVertex(10
,0
,-
10
, 1
,0
,0
, SColor(255
,255
,0
,255
), 1
, 1
);
Vertices[2
] =
S3DVertex(0
,20
,0
, 0
,1
,1
, SColor(255
,255
,255
,0
), 1
, 0
);
Vertices[3
] =
S3DVertex(-
10
,0
,-
10
, 0
,0
,1
, SColor(255
,0
,255
,0
), 0
, 0
);
Box.reset(Vertices[0
].Pos);
for
(s32 i=
1
; i<
4
; ++
i)
Box.addInternalPoint(Vertices[i].Pos);
}
virtual
void
OnRegisterSceneNode()
{
if
(IsVisible)
SceneManager->
registerNodeForRendering(this
);
ISceneNode::
OnRegisterSceneNode();
}
virtual
void
render()
{
u16 indices[] =
{
0
,2
,3
, 2
,1
,3
, 1
,0
,3
, 2
,0
,1
}
;
IVideoDriver*
driver =
SceneManager->
getVideoDriver();
driver->
setMaterial(Material);
driver->
setTransform(ETS_WORLD, AbsoluteTransformation);
driver->
drawIndexedTriangleList(&
Vertices[0
], 4
, &
indices[0
], 4
);
}
virtual
const
aabbox3d<
f32>&
getBoundingBox() const
{
return
Box;
}
virtual
u32 getMaterialCount()
{
return
1
;
}
virtual
SMaterial&
getMaterial(u32 i)
{
return
Material;
}
}
;
II. Création du moteur Windows Mobile▲
Démarre un moteur Windows Mobile.
IrrlichtDevice *
startup()
{
// EDT_SOFTWARE et EDT_BURNINGSVIDEO peuvent tout deux être utilisés
E_DRIVER_TYPE driverType =
EDT_SOFTWARE; // EDT_BURNINGSVIDEO;
// Crée le moteur.
IrrlichtDevice *
device =
0
;
#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
// On se met en mode plein écran standard pour les mobiles : 240x320.
device =
createDevice(driverType, dimension2d<
u32>
(240
, 320
), 16
, true
);
#else
// Sur un PC, utilise le mode fenêtré.
device =
createDevice(driverType, dimension2d<
u32>
(240
, 320
), 16
, false
);
#endif
if
( 0
==
device )
return
0
;
IVideoDriver*
driver =
device->
getVideoDriver();
ISceneManager*
smgr =
device->
getSceneManager();
IGUIEnvironment*
guienv =
device->
getGUIEnvironment();
// Définit un système de fichier relatif reposant sur l'exécutable.
#if defined (_IRR_WINDOWS_)
{
wchar_t
buf[255
];
GetModuleFileNameW ( 0
, buf, 255
);
io::
path base =
buf;
base =
base.subString ( 0
, base.findLast ( '
\\
'
) +
1
);
device->
getFileSystem()->
addFileArchive ( base );
}
#endif
IGUIStaticText *
text =
guienv->
addStaticText(L"FPS: 25"
,
rect<
s32>
(140
,15
,200
,30
), false
, false
, 0
, 100
);
guienv->
addButton(core::
rect<
int
>
(200
,10
,238
,30
), 0
, 2
, L"Quit"
);
// Ajoute le logo Irrlicht.
guienv->
addImage(driver->
getTexture("../../media/irrlichtlogo3.png"
),
core::
position2d<
s32>
(0
,-
2
));
return
device;
}
III. Boucle principale▲
int
run ( IrrlichtDevice *
device )
{
while
(device->
run())
if
(device->
isWindowActive())
{
device->
getVideoDriver()->
beginScene(true
, true
, SColor(0
,100
,100
,100
));
device->
getSceneManager()->
drawAll();
device->
getGUIEnvironment()->
drawAll();
device->
getVideoDriver()->
endScene ();
IGUIElement *
stat =
device->
getGUIEnvironment()->
getRootGUIElement()->
getElementFromId ( 100
);
if
( stat )
{
stringw str =
L"FPS: "
;
str +=
(s32)device->
getVideoDriver()->
getFPS();
stat->
setText ( str.c_str() );
}
}
device->
drop();
return
0
;
}
IV. Exemple nœud de scène personnalisé▲
int
example_customscenenode()
{
// Crée le moteur.
IrrlichtDevice *
device =
startup();
if
(device ==
0
)
return
1
; // Ne peut pas créer le pilote sélectionné.
// Crée le moteur et la caméra.
EventReceiver_basic receiver(device);
device->
setEventReceiver(&
receiver);
IVideoDriver*
driver =
device->
getVideoDriver();
ISceneManager*
smgr =
device->
getSceneManager();
IGUIEnvironment*
guienv =
device->
getGUIEnvironment();
smgr->
addCameraSceneNode(0
, vector3df(0
,-
40
,0
), vector3df(0
,0
,0
));
CSampleSceneNode *
myNode =
new
CSampleSceneNode(smgr->
getRootSceneNode(), smgr, 666
);
ISceneNodeAnimator*
anim =
smgr->
createRotationAnimator(vector3df(0.8
f, 0
, 0.8
f));
if
(anim)
{
myNode->
addAnimator(anim);
anim->
drop();
anim =
0
; // Comme je ne devrais plus l'utiliser à nouveau, je m'assure que je ne le pourrai pas.
}
myNode->
drop();
myNode =
0
; // Comme je ne devrais plus l'utiliser à nouveau, je m'assure que je ne le pourrai pas.
return
run ( device );
}
class
EventReceiver_terrain : public
IEventReceiver
{
public
:
EventReceiver_terrain(IrrlichtDevice *
device, scene::
ISceneNode*
terrain, scene::
ISceneNode*
skybox, scene::
ISceneNode*
skydome) :
Device ( device ), Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true
)
{
Skybox->
setVisible(true
);
Skydome->
setVisible(false
);
}
bool
OnEvent(const
SEvent&
event)
{
if
(event.EventType ==
EET_GUI_EVENT)
{
s32 id =
event.GUIEvent.Caller->
getID();
switch
(event.GUIEvent.EventType)
{
case
EGET_BUTTON_CLICKED:
if
(id ==
2
)
{
Device->
closeDevice();
return
true
;
}
break
;
}
}
// Vérifie si l'utilisateur presse les touches 'W' ou 'D'.
if
(event.EventType ==
irr::
EET_KEY_INPUT_EVENT &&
!
event.KeyInput.PressedDown)
{
switch
(event.KeyInput.Key)
{
case
irr::
KEY_KEY_W: // Passe en mode fil de fer.
Terrain->
setMaterialFlag(video::
EMF_WIREFRAME,
!
Terrain->
getMaterial(0
).Wireframe);
Terrain->
setMaterialFlag(video::
EMF_POINTCLOUD, false
);
return
true
;
case
irr::
KEY_KEY_P: // Passe en mode fil de fer.
Terrain->
setMaterialFlag(video::
EMF_POINTCLOUD,
!
Terrain->
getMaterial(0
).PointCloud);
Terrain->
setMaterialFlag(video::
EMF_WIREFRAME, false
);
return
true
;
case
irr::
KEY_KEY_D: // Change le détail de la carte.
Terrain->
setMaterialType(
Terrain->
getMaterial(0
).MaterialType ==
video::
EMT_SOLID ?
video::
EMT_DETAIL_MAP : video::
EMT_SOLID);
return
true
;
case
irr::
KEY_KEY_S: // Change le ciel.
showBox=!
showBox;
Skybox->
setVisible(showBox);
Skydome->
setVisible(!
showBox);
return
true
;
default
:
break
;
}
}
return
false
;
}
private
:
IrrlichtDevice *
Device;
scene::
ISceneNode*
Terrain;
scene::
ISceneNode*
Skybox;
scene::
ISceneNode*
Skydome;
bool
showBox;
}
;
V. Exemple rendu de terrain▲
Le début de la fonction main commence comme dans la plupart des autres exemples. Nous demandons à l'utilisateur le moteur de rendu à utiliser et nous le démarrons. Cette fois-ci avec le gestionnaire de paramètres avancés.
int
example_terrain()
{
// Crée le moteur.
IrrlichtDevice *
device =
startup();
if
(device ==
0
)
return
1
; // Ne peut pas créer le pilote sélectionné.
En premier lieu, nous ajoutons les choses habituelles à la scène : un joli logo du moteur Irrlicht, un petit texte d'aide, une caméra contrôlée par l'utilisateur et nous cachons le curseur de la souris.
video::
IVideoDriver*
driver =
device->
getVideoDriver();
scene::
ISceneManager*
smgr =
device->
getSceneManager();
gui::
IGUIEnvironment*
env =
device->
getGUIEnvironment();
// Définit une autre police d'écriture.
//env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));
// Ajoute un texte d'aide.
env->
addStaticText(
L"Press 'W' to change wireframe mode
\n
Press 'D' to toggle detail map
\n
Press 'S' to toggle skybox/skydome"
,
core::
rect<
s32>
(5
,250
,235
,320
), true
, true
, 0
, -
1
, true
);
// Ajoute la caméra.
scene::
ICameraSceneNode*
camera =
smgr->
addCameraSceneNodeFPS(0
,100.0
f,1.2
f);
camera->
setPosition(core::
vector3df(2700
*
2
,255
*
2
,2600
*
2
));
camera->
setTarget(core::
vector3df(2397
*
2
,343
*
2
,2700
*
2
));
camera->
setFarValue(42000.0
f);
// Cache le curseur de la souris.
device->
getCursorControl()->
setVisible(false
);
Maintenant, voici venu le nœud de scène de rendu de terrain : nous l'ajoutons à la scène comme n'importe quel autre nœud de scène avec IsceneManager::addTerrainSceneNode().
Le seul paramètre que nous utilisons est le nom de fichier de la texture de relief que nous utilisons. Une texture de relief est une simple texture en nuances de gris. Le dessinateur de terrain charge et crée le terrain 3D à partir de ce fichier.
Pour rendre le terrain plus grand, nous changeons le facteur de redimensionnement à (40, 4.4, 40). Parce que nous n'avons aucune lumière dynamique dans la scène, nous désactivons l'éclairage et nous mettons le fichier terrain-texture.jpg comme texture pour le terrain et detailmap3.jpg comme seconde texture appelée carte de détails. Enfin, nous définissons les valeurs de redimensionnement pour les textures : la première texture sera répétée une seule fois dans tout le terrain et la seconde (carte de détails) vingt fois.
// Ajoute le nœud de scène de terrain.
scene::
ITerrainSceneNode*
terrain =
smgr->
addTerrainSceneNode(
"../../media/terrain-heightmap.bmp"
,
0
, // nœud parent
-
1
, // identifiant du nœud
core::
vector3df(0.
f, 0.
f, 0.
f), // position
core::
vector3df(0.
f, 0.
f, 0.
f), // rotation
core::
vector3df(40.
f, 4.4
f, 40.
f), // échelle
video::
SColor ( 255
, 255
, 255
, 255
), // couleur du vertex
5
, // LOD maximum
scene::
ETPS_17, // taille de la pièce
4
// facteur de lissage
);
terrain->
setMaterialFlag(video::
EMF_LIGHTING, false
);
terrain->
setMaterialTexture(0
,
driver->
getTexture("../../media/terrain-texture.jpg"
));
terrain->
setMaterialTexture(1
,
driver->
getTexture("../../media/detailmap3.jpg"
));
terrain->
setMaterialType(video::
EMT_DETAIL_MAP);
terrain->
scaleTexture(1.0
f, 20.0
f);
Pour être capable de faire des collisions avec le terrain, nous créons un sélectionneur de triangles. Si vous voulez savoir ce que fait le sélectionneur de triangles, jetez un coup d'œil au tutoriel Collision. Le sélectionneur de triangles travaille avec le terrain. Pour expérimenter, nous créons un animateur répondant aux collisions et l'attachons à la caméra, ainsi la caméra ne sera pas capable de voler à travers le terrain.
// Crée un sélectionneur de triangles pour le terrain.
scene::
ITriangleSelector*
selector
=
smgr->
createTerrainTriangleSelector(terrain, 0
);
terrain->
setTriangleSelector(selector);
// Crée l'animateur répondant aux collisions et l'attache à la caméra.
scene::
ISceneNodeAnimator*
anim =
smgr->
createCollisionResponseAnimator(
selector, camera, core::
vector3df(60
,100
,60
),
core::
vector3df(0
,0
,0
),
core::
vector3df(0
,50
,0
));
selector->
drop();
camera->
addAnimator(anim);
anim->
drop();
Si vous avez besoin d'accéder aux données du terrain, vous pouvez aussi le faire via le bout de code suivant :
scene::
CDynamicMeshBuffer*
buffer =
new
scene::
CDynamicMeshBuffer(video::
EVT_2TCOORDS, video::
EIT_16BIT);
terrain->
getMeshBufferForLOD(*
buffer, 0
);
video::
S3DVertex2TCoords*
data =
(video::
S3DVertex2TCoords*
)buffer->
getVertexBuffer().getData();
// Travaille sur les donnée ou obtient un IndexBuffer avec un appel similaire.
buffer->
drop(); // Quand on a fini, jette le buffer encore une fois
Pour permettre à l'utilisateur de pouvoir passer du mode fil de fer au mode normal et vice-versa, nous créons une instance du receveur d'événements ci-dessus et nous en avertissons Irrlicht. De plus, nous ajoutons la « skybox » que nous avons déjà utilisée dans un bon nombre d'exemples d'Irrlicht ainsi qu'un « skydome » qui est alterné avec la « skybox » en pressant 'S'.
// Crée la « skybox » et le « skydome ».
driver->
setTextureCreationFlag(video::
ETCF_CREATE_MIP_MAPS, false
);
scene::
ISceneNode*
skybox=
smgr->
addSkyBoxSceneNode(
driver->
getTexture("../../media/irrlicht2_up.jpg"
),
driver->
getTexture("../../media/irrlicht2_dn.jpg"
),
driver->
getTexture("../../media/irrlicht2_lf.jpg"
),
driver->
getTexture("../../media/irrlicht2_rt.jpg"
),
driver->
getTexture("../../media/irrlicht2_ft.jpg"
),
driver->
getTexture("../../media/irrlicht2_bk.jpg"
));
scene::
ISceneNode*
skydome=
smgr->
addSkyDomeSceneNode(driver->
getTexture("../../media/skydome.jpg"
),16
,8
,0.95
f,2.0
f);
driver->
setTextureCreationFlag(video::
ETCF_CREATE_MIP_MAPS, true
);
// Crée le receveur d'événements.
MyEventReceiver receiver(terrain, skybox, skydome);
device->
setEventReceiver(&
receiver);
return
run ( device );
}
int
example_helloworld()
{
// Crée le moteur.
IrrlichtDevice *
device =
startup();
if
(device ==
0
)
return
1
; // Ne peut pas créer le pilote sélectionné.
IVideoDriver*
driver =
device->
getVideoDriver();
ISceneManager*
smgr =
device->
getSceneManager();
IGUIEnvironment*
guienv =
device->
getGUIEnvironment();
IAnimatedMesh*
mesh =
smgr->
getMesh("../../media/sydney.md2"
);
if
(!
mesh)
{
device->
drop();
return
1
;
}
IAnimatedMeshSceneNode*
node =
smgr->
addAnimatedMeshSceneNode( mesh );
Pour rendre le mesh un peu plus joli, nous changeons son matériel. Nous désactivons l'éclairage parce que nous n'avons pas de lumière dynamique à l'intérieur et le mesh serait totalement noir sinon. Puis nous choisissons de boucler pour que l'animation prédéfinie STAND soit utilisée. Et enfin, nous appliquons une texture au mesh. Sans cela, le mesh serait dessiné avec une seule couleur.
if
(node)
{
node->
setMaterialFlag(EMF_LIGHTING, false
);
node->
setMD2Animation(scene::
EMAT_STAND);
node->
setMaterialTexture( 0
, driver->
getTexture("../../media/sydney.bmp"
) );
}
Pour regarder vers le mesh, nous plaçons une caméra dans l'espace 3D à la position (0,30,-40). La caméra regarde vers le point (0,5,0) qui est approximativement la position de notre modèle md2.
smgr->
addCameraSceneNode(0
, vector3df(0
,30
,-
40
), vector3df(0
,5
,0
));
EventReceiver_basic receiver(device);
device->
setEventReceiver(&
receiver);
return
run ( device );
}
#if defined (_IRR_USE_WINDOWS_CE_DEVICE_)
#pragma comment(linker,
"/subsystem:WINDOWSCE /ENTRY:main"
)
#elif defined (_IRR_WINDOWS_)
#pragma comment(linker,
"/subsystem:windows /ENTRY:mainCRTStartup"
)
#endif
VI. Fonction main▲
int
main()
{
example_helloworld ();
example_customscenenode();
//example_terrain();
}
VII. Conclusion▲
Vous pouvez désormais utiliser Irrlicht sur votre mobile.
Dans le prochain tutoriel Écran divisé, nous verrons comment diviser l'écran en 4 partie.
VIII. Remerciements▲
Merci à Nikolaus Gebhardt de nous permettre de traduire ce tutoriel.
Merci à LittleWhite pour sa relecture technique ainsi qu'à ced pour sa relecture orthographique.