Tutoriel Irrlicht 12

Rendu de terrain

| Nikolaus Gebhart | Laurent Alexandre |

Traducteur

Neckara

Ce tutoriel est une traduction de l'anglais des tutoriels officiels d'Irrlicht.

Il montre brièvement comment utiliser le moteur de rendu de terrain d'Irrlicht. Il montrera aussi le sélectionneur de triangles du moteur de rendu de terrain pour être capable de faire des détections de collisions avec le terrain.

Commentez

- Titre : Tutoriel Irrlicht 12

- Auteurs : Nikolaus Gebhart, Laurent Alexandre

- Traducteur : Neckara

- Parution : 24 mai 2013

- Mise à jour : 24 mai 2013

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Neckara. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Prérequis

- II. Ajout du terrain

- III. Conclusion

- IV. Remerciements

I - Prérequis

Notez que le moteur de rendu de terrain d'Irrlicht est basé sur le GeoMipMapSceneNode de Spintz que je remercie de tout cœur. DuesXL fournit une nouvelle solution élégante pour construire des aires plus grandes sur des petites textures de relief ? le lissage de terrain.

Pour commencer, il n'y a rien de spécial. Nous incluons les fichiers d'en-têtes nécessaires et créons un détecteur d'événements pour détecter si l'utilisateur appuie sur une touche : la touche 'W' passe au mode fil de fer, la touche 'P' passe au mode nuage de points et la touche 'D' permet d'alterner entre les niveaux de détails.

#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

class MyEventReceiver : public IEventReceiver
{
public:

MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox, scene::ISceneNode* skydome) :
Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true), showDebug(false)
{
Skybox->setVisible(showBox);
Skydome->setVisible(!showBox);
}

bool OnEvent(const SEvent& event)
{
// Vérifie si l'utilisateur appuie sur 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 au 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 au mode nuage de points.
Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
!Terrain->getMaterial(0).PointCloud);
Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
return true;
case irr::KEY_KEY_D: // Alterne entre les niveaux de détails.
Terrain->setMaterialType(
Terrain->getMaterial(0).MaterialType == video::EMT_SOLID ?
video::EMT_DETAIL_MAP : video::EMT_SOLID);
return true;
case irr::KEY_KEY_S: // Alterne les cieux.
showBox=!showBox;
Skybox->setVisible(showBox);
Skydome->setVisible(!showBox);
return true;
case irr::KEY_KEY_X: // Alterne les informations de débogage.
showDebug=!showDebug;
Terrain->setDebugDataVisible(showDebug?scene::EDS_BBOX_ALL:scene::EDS_OFF);
return true;
default:
break;
}
}

return false;
}

private:
scene::ISceneNode* Terrain;
scene::ISceneNode* Skybox;
scene::ISceneNode* Skydome;
bool showBox;
bool showDebug;
};

La fonction main commence comme dans une grande partie des autres tutoriels. Nous demandons à l'utilisateur le rendu désiré et nous le démarrons. Cette fois avec le gestionnaire de paramètres avancés.

int main()
{
// Demande à l'utilisateur un pilote.
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
if (driverType==video::EDT_COUNT)
return 1;

// Crée le moteur avec tous les paramètres de création.
// Vous pouvez ajouter plus de paramètres si vous le souhaitez, regardez irr::SIrrlichtCreationParameters.
irr::SIrrlichtCreationParameters params;
params.DriverType=driverType;
params.WindowSize=core::dimension2d<u32>(640, 480);
IrrlichtDevice* device = createDeviceEx(params);

if (device == 0)
return 1; // Ne peut pas créer le pilote sélectionné.

Premièrement, nous ajoutons tous les objets standards à 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();

driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

// Ajoute le logo d'Irrlicht.
env->addImage(driver->getTexture("../../media/irrlichtlogo2.png"),
core::position2d<s32>(10,10));

// Fixe les autres polices.
env->getSkin()->setFont(env->getFont("../../media/fontlucida.png"));

// Ajoute un texte d'aide.
env->addStaticText(
L"Press 'W' to change wireframe mode\nPress 'D' to toggle detail map\nPress 'S' to toggle skybox/skydome",
core::rect<s32>(10,421,250,475), true, true, 0, -1, true);

// Ajoute la caméra.
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);

camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(42000.0f);

// Cache le curseur de la souris.
device->getCursorControl()->setVisible(false);

II - Ajout du terrain

Maintenant, voici venu le nœud de scène du 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 nuance 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 fixons les valeurs de redimensionnement pour la texture : la première texture sera répétée une seule fois dans tout le terrain et la seconde (la 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.4f, 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.0f, 20.0f);

Pour être capables 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 sur les collisions. Le sélectionneur de triangles travaille avec le terrain. Pour l'expérimenter, nous créons un animateur de réponses 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 de réponses 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ées ou obtient un IndexBuffer avec un appel similaire.
buffer->drop(); // Quand on a fini, jette le tampon une nouvelle 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.95f,2.0f);

driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);

// Crée le receveur d'événements.
MyEventReceiver receiver(terrain, skybox, skydome);
device->setEventReceiver(&receiver);

Voilà, dessinons le tout.

int lastFPS = -1;

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

smgr->drawAll();
env->drawAll();

driver->endScene();

// Montre le nombre d'images par seconde dans le titre de la fenêtre.
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Terrain Renderer - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;
// Affiche aussi la hauteur actuelle du terrain par rapport à la position de la caméra.
// Nous pouvons utiliser la position de la camera, car le terrain est placé à l'origine du système de coordonnées.
str += " Height: ";
str += terrain->getHeight(camera->getAbsolutePosition().X,
camera->getAbsolutePosition().Z);

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

device->drop();

return 0;
}

Maintenant, vous savez utiliser les terrains dans Irrlicht.

III - Conclusion

Vous pouvez désormais afficher des terrains avec Irrlicht.

Dans le prochain tutoriel Rendu dans une texture, nous verrons comment dessiner sur des textures.

IV - Remerciements

Merci à Nikolaus Gebhardt de nous permettre de traduire ce tutoriel.

Merci à LittleWhite pour sa relecture technique ainsi qu'à ClaudeLELOUP pour sa relecture orthographique.