Tutoriel Irrlicht 21

Explorateur Quake 3

| Nikolaus Gebhart | Alexandre Laurent |

Traducteur

Neckara

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

Il montre comment charger différentes cartes Quake 3.

Commentez

- Titre : Tutoriel Irrlicht 21

- Auteurs : Nikolaus Gebhart, Alexandre Laurent

- Traducteur : Neckara

- Parution : 5 juillet 2013

- Mise à jour : 5 juillet 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. Configuration

- II. Joueur

- III. Position de départ et caméra

- IV. Construction de la scène

- V. Interface utilisateur

- VI. Cartes

- VII. Nœuds de scènes

- VIII. Interactions

- IX. Objets

- X. Rendu

- XI. Boucle principale

- XII. Conclusion

- XIII. Remerciements

I - Configuration

Fonctionnalités :

- charge une archive BSP pendant l'exécution à partir du menu ;

- charge une carte à partir du menu et affiche un aperçu ;

- met le pilote vidéo pendant l'exécution suivant le choix dans le menu ;

- ajuste le niveau Gamma à l'exécution ;

- crée des SceneNodes pour les ombres ;

- charge EntryList et crée les SceneNodes entité ;

- crée les joueurs avec des armes et gestion des collisions ;

- joue de la musique.

Vous pouvez télécharger la démonstration de Quake III Arena (copyright id software) à partir du lien suivant : https://www.moddb.com/games/quake-iii-arena/downloads/quake-3-demo

Copyright 2006-2011 Burningwater, Thomas Alten.

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

GameData est utilisé pour regrouper les données nécessaires à l'exécution du jeu.

struct GameData
{
GameData ( const path &startupDir) :
retVal(0), StartupDir(startupDir), createExDevice(0), Device(0)
{
setDefault ();
}

void setDefault ();
s32 save ( const path &filename );
s32 load ( const path &filename );

s32 debugState;
s32 gravityState;
s32 flyTroughState;
s32 wireFrame;
s32 guiActive;
s32 guiInputActive;
f32 GammaValue;
s32 retVal;
s32 sound;

path StartupDir;
stringw CurrentMapName;
array<path> CurrentArchiveList;

vector3df PlayerPosition;
vector3df PlayerRotation;

tQ3EntityList Variable;

Q3LevelLoadParameter loadParam;
SIrrlichtCreationParameters deviceParam;
funcptr_createDeviceEx createExDevice;
IrrlichtDevice *Device;
};

Définit la configuration par défaut.

void GameData::setDefault ()
{
debugState = EDS_OFF;
gravityState = 1;
flyTroughState = 0;
wireFrame = 0;
guiActive = 1;
guiInputActive = 0;
GammaValue = 1.f;

// deviceParam par défaut;
#if defined ( _IRR_WINDOWS_ )
deviceParam.DriverType = EDT_DIRECT3D9;
#else
deviceParam.DriverType = EDT_OPENGL;
#endif
deviceParam.WindowSize.Width = 800;
deviceParam.WindowSize.Height = 600;
deviceParam.Fullscreen = false;
deviceParam.Bits = 24;
deviceParam.ZBufferBits = 16;
deviceParam.Vsync = false;
deviceParam.AntiAlias = false;

// loadParam par défaut de Quake 3
loadParam.defaultLightMapMaterial = EMT_LIGHTMAP;
loadParam.defaultModulate = EMFN_MODULATE_1X;
loadParam.defaultFilter = EMF_ANISOTROPIC_FILTER;
loadParam.verbose = 2;
loadParam.mergeShaderBuffer = 1;        // Mélange les meshbuffers ayant le même matériel.
loadParam.cleanUnResolvedMeshes = 1;    // Les meshs irrésolues devraient être nettoyées. Sinon texture bleue.
loadParam.loadAllShaders = 1;           // Charge tous les scripts du dossier de script.
loadParam.loadSkyShader = 0;            // Charge skyShader
loadParam.alpharef = 1;

sound = 0;

CurrentMapName = "";
CurrentArchiveList.clear ();

// Dossier média de l'explorateur.
CurrentArchiveList.push_back ( StartupDir + "../../media/" );

// Ajoute les fichiers originaux de Quake 3 avant que vous ne chargiez vos cartes personnalisées.
// La plupart des mods utilisent les shaders, modèles, objets et armes originaux.
CurrentArchiveList.push_back("/q/baseq3/");

CurrentArchiveList.push_back(StartupDir + "../../media/map-20kdm2.pk3");
}

Charge l'état du jeu à partir d'un fichier cfg typique de Quake3.

s32 GameData::load ( const path &filename )
{
if (!Device)
return 0;

// Le chargeur de mesh Quake 3 peut aussi gérer les fichiers *.shader et *.cfg.
IQ3LevelMesh* mesh = (IQ3LevelMesh*) Device->getSceneManager()->getMesh ( filename );
if (!mesh)
return 0;

tQ3EntityList &entityList = mesh->getEntityList ();

stringc s;
u32 pos;

for ( u32 e = 0; e != entityList.size (); ++e )
{
//dumpShader ( s, &entityList[e], false );
//printf ( s.c_str () );

for ( u32 g = 0; g != entityList[e].getGroupSize (); ++g )
{
const SVarGroup *group = entityList[e].getGroup ( g );

for ( u32 index = 0; index < group->Variable.size (); ++index )
{
const SVariable &v = group->Variable[index];
pos = 0;
if ( v.name == "playerposition" )
{
PlayerPosition = getAsVector3df ( v.content, pos );
}
else
if ( v.name == "playerrotation" )
{
PlayerRotation = getAsVector3df ( v.content, pos );
}
}
}
}

return 1;
}

Stocke l'état courant du jeu dans un fichier de configuration de Quake 3.

s32 GameData::save ( const path &filename )
{
return 0;
if (!Device)
return 0;

c8 buf[128];
u32 i;

// Stocke l'archive courante pour recommencer.
CurrentArchiveList.clear();
IFileSystem *fs = Device->getFileSystem();
for ( i = 0; i != fs->getFileArchiveCount(); ++i )
{
CurrentArchiveList.push_back ( fs->getFileArchive(i)->getFileList()->getPath() );
}

// Stocke la position et la rotation du joueur.
ICameraSceneNode * camera = Device->getSceneManager()->getActiveCamera ();
if ( camera )
{
PlayerPosition = camera->getPosition ();
PlayerRotation = camera->getRotation ();
}

IWriteFile *file = fs->createAndWriteFile ( filename );
if (!file)
return 0;

snprintf ( buf, 128, "playerposition %.f %.f %.f\nplayerrotation %.f %.f %.f\n",
PlayerPosition.X, PlayerPosition.Z, PlayerPosition.Y,
PlayerRotation.X, PlayerRotation.Z, PlayerRotation.Y);
file->write ( buf, (s32) strlen ( buf ) );
for ( i = 0; i != fs->getFileArchiveCount(); ++i )
{
snprintf ( buf, 128, "archive %s\n",stringc ( fs->getFileArchive(i)->getFileList()->getPath() ).c_str () );
file->write ( buf, (s32) strlen ( buf ) );
}

file->drop ();
return 1;
}

II - Joueur

Représente un joueur.

struct Q3Player : public IAnimationEndCallBack
{
Q3Player ()
: Device(0), MapParent(0), Mesh(0), WeaponNode(0), StartPositionCurrent(0)
{
animation[0] = 0;
memset(Anim, 0, sizeof(TimeFire)*4);
}

virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node);

void create (   IrrlichtDevice *device,
IQ3LevelMesh* mesh,
ISceneNode *mapNode,
IMetaTriangleSelector *meta
);
void shutdown ();
void setAnim ( const c8 *name );
void respawn ();
void setpos ( const vector3df &pos, const vector3df& rotation );

ISceneNodeAnimatorCollisionResponse * cam() { return camCollisionResponse ( Device ); }

IrrlichtDevice *Device;
ISceneNode* MapParent;
IQ3LevelMesh* Mesh;
IAnimatedMeshSceneNode* WeaponNode;
s32 StartPositionCurrent;
TimeFire Anim[4];
c8 animation[64];
c8 buf[64];
};

Fin du joueur.

void Q3Player::shutdown ()
{
setAnim ( 0 );

dropElement (WeaponNode);

if ( Device )
{
ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
dropElement ( camera );
Device = 0;
}

MapParent = 0;
Mesh = 0;
}

Crée un nouveau joueur.

void Q3Player::create ( IrrlichtDevice *device, IQ3LevelMesh* mesh, ISceneNode *mapNode, IMetaTriangleSelector *meta )
{
setTimeFire ( Anim + 0, 200, FIRED );
setTimeFire ( Anim + 1, 5000 );

if (!device)
return;
// Attache l'arme FPS à la caméra.
Device = device;
Mesh = mesh;
MapParent = mapNode;

ISceneManager *smgr = device->getSceneManager ();
IVideoDriver * driver = device->getVideoDriver();

ICameraSceneNode* camera = 0;

SKeyMap keyMap[10];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_UP;
keyMap[1].Action = EKA_MOVE_FORWARD;
keyMap[1].KeyCode = KEY_KEY_W;

keyMap[2].Action = EKA_MOVE_BACKWARD;
keyMap[2].KeyCode = KEY_DOWN;
keyMap[3].Action = EKA_MOVE_BACKWARD;
keyMap[3].KeyCode = KEY_KEY_S;

keyMap[4].Action = EKA_STRAFE_LEFT;
keyMap[4].KeyCode = KEY_LEFT;
keyMap[5].Action = EKA_STRAFE_LEFT;
keyMap[5].KeyCode = KEY_KEY_A;

keyMap[6].Action = EKA_STRAFE_RIGHT;
keyMap[6].KeyCode = KEY_RIGHT;
keyMap[7].Action = EKA_STRAFE_RIGHT;
keyMap[7].KeyCode = KEY_KEY_D;

keyMap[8].Action = EKA_JUMP_UP;
keyMap[8].KeyCode = KEY_KEY_J;

keyMap[9].Action = EKA_CROUCH;
keyMap[9].KeyCode = KEY_KEY_C;

camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.6f, -1, keyMap, 10, false, 0.6f);
camera->setName ( "First Person Camera" );
//camera->setFOV ( 100.f * core::DEGTORAD );
camera->setFarValue( 20000.f );

IAnimatedMeshMD2* weaponMesh = (IAnimatedMeshMD2*) smgr->getMesh("gun.md2");
if ( 0 == weaponMesh )
return;

if ( weaponMesh->getMeshType() == EAMT_MD2 )
{
s32 count = weaponMesh->getAnimationCount();
for ( s32 i = 0; i != count; ++i )
{
snprintf ( buf, 64, "Animation: %s", weaponMesh->getAnimationName(i) );
device->getLogger()->log(buf, ELL_INFORMATION);
}
}

WeaponNode = smgr->addAnimatedMeshSceneNode(
weaponMesh,
smgr->getActiveCamera(),
10,
vector3df( 0, 0, 0),
vector3df(-90,-90,90)
);
WeaponNode->setMaterialFlag(EMF_LIGHTING, false);
WeaponNode->setMaterialTexture(0, driver->getTexture( "gun.jpg"));
WeaponNode->setLoopMode ( false );
WeaponNode->setName ( "tommi the gun man" );

// Crée un animateur automatique de réponse aux collisions.
ISceneNodeAnimator* anim =
smgr->createCollisionResponseAnimator( meta, camera,
vector3df(30,45,30),
getGravity ( "earth" ),
vector3df(0,40,0),
0.0005f
);

camera->addAnimator( anim );
anim->drop();

if ( meta )
{
meta->drop ();
}

respawn ();
setAnim ( "idle" );
}

III - Position de départ et caméra

Nous avons besoin d'une bonne position de départ dans le niveau. Nous pouvons demander au chargeur Quake3 toutes les entités avec class_name valant "info_player_deathmatch".

void Q3Player::respawn ()
{
if (!Device)
return;
ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();

Device->getLogger()->log( "respawn" );

if ( StartPositionCurrent >= Q3StartPosition (
Mesh, camera,StartPositionCurrent++,
cam ()->getEllipsoidTranslation() )
)
{
StartPositionCurrent = 0;
}
}

Détermine la position du joueur à partir des coordonnées enregistrées.

void Q3Player::setpos ( const vector3df &pos, const vector3df &rotation )
{
if (!Device)
return;
Device->getLogger()->log( "setpos" );

ICameraSceneNode* camera = Device->getSceneManager()->getActiveCamera();
if ( camera )
{
camera->setPosition ( pos );
camera->setRotation ( rotation );
camera->OnAnimate ( 0 );
}
}

Définit l'animation du joueur et de l'arme.

void Q3Player::setAnim ( const c8 *name )
{
if ( name )
{
snprintf ( animation, 64, "%s", name );
if ( WeaponNode )
{
WeaponNode->setAnimationEndCallback ( this );
WeaponNode->setMD2Animation ( animation );
}
}
else
{
animation[0] = 0;
if ( WeaponNode )
{
WeaponNode->setAnimationEndCallback ( 0 );
}
}
}

// Callback
void Q3Player::OnAnimationEnd(IAnimatedMeshSceneNode* node)
{
setAnim ( 0 );
}

Éléments d'interface utilisateur.

struct GUI
{
GUI ()
{
memset ( this, 0, sizeof ( *this ) );
}

void drop()
{
dropElement ( Window );
dropElement ( Logo );
}

IGUIComboBox* VideoDriver;
IGUIComboBox* VideoMode;
IGUICheckBox* FullScreen;
IGUICheckBox* Bit32;
IGUIScrollBar* MultiSample;
IGUIButton* SetVideoMode;

IGUIScrollBar* Tesselation;
IGUIScrollBar* Gamma;
IGUICheckBox* Collision;
IGUICheckBox* Visible_Map;
IGUICheckBox* Visible_Shader;
IGUICheckBox* Visible_Fog;
IGUICheckBox* Visible_Unresolved;
IGUICheckBox* Visible_Skydome;
IGUIButton* Respawn;

IGUITable* ArchiveList;
IGUIButton* ArchiveAdd;
IGUIButton* ArchiveRemove;
IGUIFileOpenDialog* ArchiveFileOpen;
IGUIButton* ArchiveUp;
IGUIButton* ArchiveDown;

IGUIListBox* MapList;
IGUITreeView* SceneTree;
IGUIStaticText* StatusLine;
IGUIImage* Logo;
IGUIWindow* Window;
};

IV - Construction de la scène

CQuake3EventHandler contrôle le jeu.

class CQuake3EventHandler : public IEventReceiver
{
public:

CQuake3EventHandler( GameData *gameData );
virtual ~CQuake3EventHandler ();

void Animate();
void Render();

void AddArchive ( const path& archiveName );
void LoadMap ( const stringw& mapName, s32 collision );
void CreatePlayers();
void AddSky( u32 dome, const c8 *texture );
Q3Player *GetPlayer ( u32 index ) { return &Player[index]; }

void CreateGUI();
void SetGUIActive( s32 command);

bool OnEvent(const SEvent& eve);

private:

GameData *Game;

IQ3LevelMesh* Mesh;
ISceneNode* MapParent;
ISceneNode* ShaderParent;
ISceneNode* ItemParent;
ISceneNode* UnresolvedParent;
ISceneNode* BulletParent;
ISceneNode* FogParent;
ISceneNode * SkyNode;
IMetaTriangleSelector *Meta;

c8 buf[256];

Q3Player Player[2];

struct SParticleImpact
{
u32 when;
vector3df pos;
vector3df outVector;
};
array<SParticleImpact> Impacts;
void useItem( Q3Player * player);
void createParticleImpacts( u32 now );

void createTextures ();
void addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent);

GUI gui;
void dropMap ();
};

Constructeur.

CQuake3EventHandler::CQuake3EventHandler( GameData *game )
: Game(game), Mesh(0), MapParent(0), ShaderParent(0), ItemParent(0), UnresolvedParent(0),
BulletParent(0), FogParent(0), SkyNode(0), Meta(0)
{
buf[0]=0;
// Utilise aussi des textures sur 16 bits pour un RenderDevice 16 bits.
if ( Game->deviceParam.Bits == 16 )
{
game->Device->getVideoDriver()->setTextureCreationFlag(ETCF_ALWAYS_16_BIT, true);
}

// Contrôle l'écriture du tampon de profondeur pour les shaders de Quake3.
game->Device->getSceneManager()->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);

// Crée des textures internes.
createTextures ();

sound_init ( game->Device );

Game->Device->setEventReceiver ( this );
}

// Destructeur
CQuake3EventHandler::~CQuake3EventHandler ()
{
Player[0].shutdown ();
sound_shutdown ();

Game->save( "explorer.cfg" );

Game->Device->drop();
}

// Crée des textures de nuages et de brouillard à l'exécution.
void CQuake3EventHandler::createTextures()
{
IVideoDriver * driver = Game->Device->getVideoDriver();

dimension2du dim(64, 64);

video::IImage* image;
u32 i;
u32 x;
u32 y;
u32 * data;
for ( i = 0; i != 8; ++i )
{
image = driver->createImage ( video::ECF_A8R8G8B8, dim);
data = (u32*) image->lock ();
for ( y = 0; y != dim.Height; ++y )
{
for ( x = 0; x != dim.Width; ++x )
{
data [x] = 0xFFFFFFFF;
}
data = (u32*) ( (u8*) data + image->getPitch() );
}
image->unlock();
snprintf ( buf, 64, "smoke_%02d", i );
driver->addTexture( buf, image );
image->drop ();
}

// Brouillard
for ( i = 0; i != 1; ++i )
{
image = driver->createImage ( video::ECF_A8R8G8B8, dim);
data = (u32*) image->lock ();
for ( y = 0; y != dim.Height; ++y )
{
for ( x = 0; x != dim.Width; ++x )
{
data [x] = 0xFFFFFFFF;
}
data = (u32*) ( (u8*) data + image->getPitch() );
}
image->unlock();
snprintf ( buf, 64, "fog_%02d", i );
driver->addTexture( buf, image );
image->drop ();
}
}

V - Interface utilisateur

Crée l'interface utilisateur.

void CQuake3EventHandler::CreateGUI()
{

IGUIEnvironment *env = Game->Device->getGUIEnvironment();
IVideoDriver * driver = Game->Device->getVideoDriver();

gui.drop();

// Met la police du skin.
IGUIFont* font = env->getFont("fontlucida.png");
if (font)
env->getSkin()->setFont(font);
env->getSkin()->setColor ( EGDC_BUTTON_TEXT, video::SColor(240,0xAA,0xAA,0xAA) );
env->getSkin()->setColor ( EGDC_3D_HIGH_LIGHT, video::SColor(240,0x22,0x22,0x22) );
env->getSkin()->setColor ( EGDC_3D_FACE, video::SColor(240,0x44,0x44,0x44) );
env->getSkin()->setColor ( EGDC_EDITABLE, video::SColor(240,0x44,0x44,0x44) );
env->getSkin()->setColor ( EGDC_FOCUSED_EDITABLE, video::SColor(240,0x54,0x54,0x54) );
env->getSkin()->setColor ( EGDC_WINDOW, video::SColor(240,0x66,0x66,0x66) );

// Taille minimale de l'interface utilisateur 800x600
dimension2d<u32> dim ( 800, 600 );
dimension2d<u32> vdim ( Game->Device->getVideoDriver()->getScreenSize() );

if ( vdim.Height >= dim.Height && vdim.Width >= dim.Width )
{
//dim = vdim;
}
else
{
}

gui.Window = env->addWindow ( rect<s32> ( 0, 0, dim.Width, dim.Height ), false, L"Quake3 Explorer" );
gui.Window->setToolTipText ( L"Quake3Explorer. Loads and show various BSP File Format and Shaders." );
gui.Window->getCloseButton()->setToolTipText ( L"Quit Quake3 Explorer" );

// Ajoute une ligne de statut pour un texte d'aide.
gui.StatusLine = env->addStaticText( 0, rect<s32>( 5,dim.Height - 30,dim.Width - 5,dim.Height - 10),
false, false, gui.Window, -1, true
);

env->addStaticText ( L"VideoDriver:", rect<s32>( dim.Width - 400, 24, dim.Width - 310, 40 ),false, false, gui.Window, -1, false );
gui.VideoDriver = env->addComboBox(rect<s32>( dim.Width - 300, 24, dim.Width - 10, 40 ),gui.Window);
gui.VideoDriver->addItem(L"Direct3D 9.0c", EDT_DIRECT3D9 );
gui.VideoDriver->addItem(L"Direct3D 8.1", EDT_DIRECT3D8 );
gui.VideoDriver->addItem(L"OpenGL 1.5", EDT_OPENGL);
gui.VideoDriver->addItem(L"Software Renderer", EDT_SOFTWARE);
gui.VideoDriver->addItem(L"Burning's Video (TM) Thomas Alten", EDT_BURNINGSVIDEO);
gui.VideoDriver->setSelected ( gui.VideoDriver->getIndexForItemData ( Game->deviceParam.DriverType ) );
gui.VideoDriver->setToolTipText ( L"Use a VideoDriver" );

env->addStaticText ( L"VideoMode:", rect<s32>( dim.Width - 400, 44, dim.Width - 310, 60 ),false, false, gui.Window, -1, false );
gui.VideoMode = env->addComboBox(rect<s32>( dim.Width - 300, 44, dim.Width - 10, 60 ),gui.Window);
gui.VideoMode->setToolTipText ( L"Supported Screenmodes" );
IVideoModeList *modeList = Game->Device->getVideoModeList();
if ( modeList )
{
s32 i;
for ( i = 0; i != modeList->getVideoModeCount (); ++i )
{
u16 d = modeList->getVideoModeDepth ( i );
if ( d < 16 )
continue;

u16 w = modeList->getVideoModeResolution ( i ).Width;
u16 h = modeList->getVideoModeResolution ( i ).Height;
u32 val = w << 16 | h;

if ( gui.VideoMode->getIndexForItemData ( val ) >= 0 )
continue;

f32 aspect = (f32) w / (f32) h;
const c8 *a = "";
if ( core::equals ( aspect, 1.3333333333f ) ) a = "4:3";
else if ( core::equals ( aspect, 1.6666666f ) ) a = "15:9 widescreen";
else if ( core::equals ( aspect, 1.7777777f ) ) a = "16:9 widescreen";
else if ( core::equals ( aspect, 1.6f ) ) a = "16:10 widescreen";
else if ( core::equals ( aspect, 2.133333f ) ) a = "20:9 widescreen";

snprintf ( buf, sizeof ( buf ), "%d x %d, %s",w, h, a );
gui.VideoMode->addItem ( stringw ( buf ).c_str(), val );
}
}
gui.VideoMode->setSelected ( gui.VideoMode->getIndexForItemData (
Game->deviceParam.WindowSize.Width << 16 |
Game->deviceParam.WindowSize.Height ) );

gui.FullScreen = env->addCheckBox ( Game->deviceParam.Fullscreen, rect<s32>( dim.Width - 400, 64, dim.Width - 300, 80 ), gui.Window,-1, L"Fullscreen" );
gui.FullScreen->setToolTipText ( L"Set Fullscreen or Window Mode" );

gui.Bit32 = env->addCheckBox ( Game->deviceParam.Bits == 32, rect<s32>( dim.Width - 300, 64, dim.Width - 240, 80 ), gui.Window,-1, L"32Bit" );
gui.Bit32->setToolTipText ( L"Use 16 or 32 Bit" );

env->addStaticText ( L"MultiSample:", rect<s32>( dim.Width - 235, 64, dim.Width - 150, 80 ),false, false, gui.Window, -1, false );
gui.MultiSample = env->addScrollBar( true, rect<s32>( dim.Width - 150, 64, dim.Width - 70, 80 ), gui.Window,-1 );
gui.MultiSample->setMin ( 0 );
gui.MultiSample->setMax ( 8 );
gui.MultiSample->setSmallStep ( 1 );
gui.MultiSample->setLargeStep ( 1 );
gui.MultiSample->setPos ( Game->deviceParam.AntiAlias );
gui.MultiSample->setToolTipText ( L"Set the MultiSample (disable, 1x, 2x, 4x, 8x )" );

gui.SetVideoMode = env->addButton (rect<s32>( dim.Width - 60, 64, dim.Width - 10, 80 ), gui.Window, -1,L"set" );
gui.SetVideoMode->setToolTipText ( L"Set Video Mode with current values" );

env->addStaticText ( L"Gamma:", rect<s32>( dim.Width - 400, 104, dim.Width - 310, 120 ),false, false, gui.Window, -1, false );
gui.Gamma = env->addScrollBar( true, rect<s32>( dim.Width - 300, 104, dim.Width - 10, 120 ), gui.Window,-1 );
gui.Gamma->setMin ( 50 );
gui.Gamma->setMax ( 350 );
gui.Gamma->setSmallStep ( 1 );
gui.Gamma->setLargeStep ( 10 );
gui.Gamma->setPos ( core::floor32 ( Game->GammaValue * 100.f ) );
gui.Gamma->setToolTipText ( L"Adjust Gamma Ramp ( 0.5 - 3.5)" );
Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );

env->addStaticText ( L"Tesselation:", rect<s32>( dim.Width - 400, 124, dim.Width - 310, 140 ),false, false, gui.Window, -1, false );
gui.Tesselation = env->addScrollBar( true, rect<s32>( dim.Width - 300, 124, dim.Width - 10, 140 ), gui.Window,-1 );
gui.Tesselation->setMin ( 2 );
gui.Tesselation->setMax ( 12 );
gui.Tesselation->setSmallStep ( 1 );
gui.Tesselation->setLargeStep ( 1 );
gui.Tesselation->setPos ( Game->loadParam.patchTesselation );
gui.Tesselation->setToolTipText ( L"How smooth should curved surfaces be rendered" );

gui.Collision = env->addCheckBox ( true, rect<s32>( dim.Width - 400, 150, dim.Width - 300, 166 ), gui.Window,-1, L"Collision" );
gui.Collision->setToolTipText ( L"Set collision on or off ( flythrough ). \nPress F7 on your Keyboard" );
gui.Visible_Map = env->addCheckBox ( true, rect<s32>( dim.Width - 300, 150, dim.Width - 240, 166 ), gui.Window,-1, L"Map" );
gui.Visible_Map->setToolTipText ( L"Show or not show the static part the Level. \nPress F3 on your Keyboard" );
gui.Visible_Shader = env->addCheckBox ( true, rect<s32>( dim.Width - 240, 150, dim.Width - 170, 166 ), gui.Window,-1, L"Shader" );
gui.Visible_Shader->setToolTipText ( L"Show or not show the Shader Nodes. \nPress F4 on your Keyboard" );
gui.Visible_Fog = env->addCheckBox ( true, rect<s32>( dim.Width - 170, 150, dim.Width - 110, 166 ), gui.Window,-1, L"Fog" );
gui.Visible_Fog->setToolTipText ( L"Show or not show the Fog Nodes. \nPress F5 on your Keyboard" );
gui.Visible_Unresolved = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 150, dim.Width - 10, 166 ), gui.Window,-1, L"Unresolved" );
gui.Visible_Unresolved->setToolTipText ( L"Show the or not show the Nodes the Engine can't handle. \nPress F6 on your Keyboard" );
gui.Visible_Skydome = env->addCheckBox ( true, rect<s32>( dim.Width - 110, 180, dim.Width - 10, 196 ), gui.Window,-1, L"Skydome" );
gui.Visible_Skydome->setToolTipText ( L"Show the or not show the Skydome." );

//Respawn = env->addButton ( rect<s32>( dim.Width - 260, 90, dim.Width - 10, 106 ), 0,-1, L"Respawn" );

env->addStaticText ( L"Archives:", rect<s32>( 5, dim.Height - 530, dim.Width - 600,dim.Height - 514 ),false, false, gui.Window, -1, false );

gui.ArchiveAdd = env->addButton ( rect<s32>( dim.Width - 725, dim.Height - 530, dim.Width - 665, dim.Height - 514 ), gui.Window,-1, L"add" );
gui.ArchiveAdd->setToolTipText ( L"Add an archive, usually packed zip-archives (*.pk3) to the Filesystem" );
gui.ArchiveRemove = env->addButton ( rect<s32>( dim.Width - 660, dim.Height - 530, dim.Width - 600, dim.Height - 514 ), gui.Window,-1, L"del" );
gui.ArchiveRemove->setToolTipText ( L"Remove the selected archive from the FileSystem." );
gui.ArchiveUp = env->addButton ( rect<s32>( dim.Width - 575, dim.Height - 530, dim.Width - 515, dim.Height - 514 ), gui.Window,-1, L"up" );
gui.ArchiveUp->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive up" );
gui.ArchiveDown = env->addButton ( rect<s32>( dim.Width - 510, dim.Height - 530, dim.Width - 440, dim.Height - 514 ), gui.Window,-1, L"down" );
gui.ArchiveDown->setToolTipText ( L"Arrange Archive Look-up Hirachy. Move the selected Archive down" );

gui.ArchiveList = env->addTable ( rect<s32>( 5,dim.Height - 510, dim.Width - 450,dim.Height - 410 ), gui.Window  );
gui.ArchiveList->addColumn ( L"Type", 0 );
gui.ArchiveList->addColumn ( L"Real File Path", 1 );
gui.ArchiveList->setColumnWidth ( 0, 60 );
gui.ArchiveList->setColumnWidth ( 1, 284 );
gui.ArchiveList->setToolTipText ( L"Show the attached Archives" );

env->addStaticText ( L"Maps:", rect<s32>( 5, dim.Height - 400, dim.Width - 450,dim.Height - 380 ),false, false, gui.Window, -1, false );
gui.MapList = env->addListBox ( rect<s32>( 5,dim.Height - 380, dim.Width - 450,dim.Height - 40  ), gui.Window, -1, true  );
gui.MapList->setToolTipText ( L"Show the current Maps in all Archives.\n Double-Click the Map to start the level" );

// Crée un arbre de scène visible.
env->addStaticText ( L"Scenegraph:", rect<s32>( dim.Width - 400, dim.Height - 400, dim.Width - 5,dim.Height - 380 ),false, false, gui.Window, -1, false );
gui.SceneTree = env->addTreeView(   rect<s32>( dim.Width - 400, dim.Height - 380, dim.Width - 5, dim.Height - 40 ),
gui.Window, -1, true, true, false );
gui.SceneTree->setToolTipText ( L"Show the current Scenegraph" );
gui.SceneTree->getRoot()->clearChildren();
addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );

IGUIImageList* imageList = env->createImageList(    driver->getTexture ( "iconlist.png" ),
dimension2di( 32, 32 ), true );

if ( imageList )
{
gui.SceneTree->setImageList( imageList );
imageList->drop ();
}

// Charge le logo du moteur.
gui.Logo = env->addImage( driver->getTexture("irrlichtlogo3.png"), position2d<s32>(5, 16 ), true, 0 );
gui.Logo->setToolTipText ( L"The great Irrlicht Engine" );

AddArchive ( "" );
}

Ajoute une archive au système de fichier et met à jour l'interface utilisateur.

void CQuake3EventHandler::AddArchive ( const path& archiveName )
{
IFileSystem *fs = Game->Device->getFileSystem();
u32 i;

if ( archiveName.size () )
{
bool exists = false;
for ( i = 0; i != fs->getFileArchiveCount(); ++i )
{
if ( fs->getFileArchive(i)->getFileList()->getPath() == archiveName )
{
exists = true;
break;
}
}

if (!exists)
{
fs->addFileArchive(archiveName, true, false);
}
}

// Stocke l'archive courante dans les données de jeux.
// Montre l'archive attachée dans le bon ordre.
if ( gui.ArchiveList )
{
gui.ArchiveList->clearRows();

for ( i = 0; i != fs->getFileArchiveCount(); ++i )
{
IFileArchive * archive = fs->getFileArchive ( i );

u32 index = gui.ArchiveList->addRow(i);

core::stringw typeName;
switch(archive->getType())
{
case io::EFAT_ZIP:
typeName = "ZIP";
break;
case io::EFAT_GZIP:
typeName = "gzip";
break;
case io::EFAT_FOLDER:
typeName = "Mount";
break;
case io::EFAT_PAK:
typeName = "PAK";
break;
case io::EFAT_TAR:
typeName = "TAR";
break;
default:
typeName = "archive";
}

gui.ArchiveList->setCellText ( index, 0, typeName );
gui.ArchiveList->setCellText ( index, 1, archive->getFileList()->getPath() );
}
}

// Parcourt les archives des cartes.
if ( gui.MapList )
{
gui.MapList->clear();

IGUISpriteBank *bank = Game->Device->getGUIEnvironment()->getSpriteBank("sprite_q3map");
if ( 0 == bank )
bank = Game->Device->getGUIEnvironment()->addEmptySpriteBank("sprite_q3map");

SGUISprite sprite;
SGUISpriteFrame frame;
core::rect<s32> r;

bank->getSprites().clear();
bank->getPositions().clear ();
gui.MapList->setSpriteBank ( bank );

u32 g = 0;
core::stringw s;

// Parcourt le système de fichiers attachés.
fs->setFileListSystem ( FILESYSTEM_VIRTUAL );
fs->changeWorkingDirectoryTo ( "/maps/" );
IFileList *fileList = fs->createFileList ();
fs->setFileListSystem ( FILESYSTEM_NATIVE );

for ( i=0; i< fileList->getFileCount(); ++i)
{
s = fileList->getFullFileName(i);
if ( s.find ( ".bsp" ) >= 0 )
{
// Obtient la capture d'écran du niveau, le redimensionne en une texture de 128x128.
path c ( s );
deletePathFromFilename ( c );
cutFilenameExtension ( c, c );
c = path ( "levelshots/" ) + c;

dimension2du dim ( 128, 128 );
IVideoDriver * driver = Game->Device->getVideoDriver();
IImage* image = 0;
ITexture *tex = 0;
path filename;

filename = c + ".jpg";
if ( fs->existFile ( filename ) )
image = driver->createImageFromFile( filename );
if ( 0 == image )
{
filename = c + ".tga";
if ( fs->existFile ( filename ) )
image = driver->createImageFromFile( filename );
}

if ( image )
{
IImage* filter = driver->createImage ( video::ECF_R8G8B8, dim );
image->copyToScalingBoxFilter ( filter, 0 );
image->drop ();
image = filter;
}

if ( image )
{
tex = driver->addTexture ( filename, image );
image->drop ();
}

bank->setTexture ( g, tex );

r.LowerRightCorner.X = dim.Width;
r.LowerRightCorner.Y = dim.Height;
gui.MapList->setItemHeight ( r.LowerRightCorner.Y + 4 );
frame.rectNumber = bank->getPositions().size();
frame.textureNumber = g;

bank->getPositions().push_back(r);

sprite.Frames.set_used ( 0 );
sprite.Frames.push_back(frame);
sprite.frameTime = 0;
bank->getSprites().push_back(sprite);

gui.MapList->addItem ( s.c_str (), g );
g += 1;
}
}
fileList->drop ();

gui.MapList->setSelected ( -1 );
IGUIScrollBar * bar = (IGUIScrollBar*)gui.MapList->getElementFromId( 0 );
if ( bar )
bar->setPos ( 0 );

}

}

VI - Cartes

Efface la carte en mémoire.

void CQuake3EventHandler::dropMap ()
{
IVideoDriver * driver = Game->Device->getVideoDriver();

driver->removeAllHardwareBuffers ();
driver->removeAllTextures ();

Player[0].shutdown ();

dropElement ( ItemParent );
dropElement ( ShaderParent );
dropElement ( UnresolvedParent );
dropElement ( FogParent );
dropElement ( BulletParent );

Impacts.clear();

if ( Meta )
{
Meta = 0;
}

dropElement ( MapParent );
dropElement ( SkyNode );

// Nettoie les meshs parce que les textures sont invalides.
// TODO: meilleur gestionnaire de textures;-)
IMeshCache *cache = Game->Device->getSceneManager ()->getMeshCache();
cache->clear ();
Mesh = 0;
}

Charge une nouvelle carte.

void CQuake3EventHandler::LoadMap ( const stringw &mapName, s32 collision )
{
if ( 0 == mapName.size() )
return;

dropMap ();

IFileSystem *fs = Game->Device->getFileSystem();
ISceneManager *smgr = Game->Device->getSceneManager ();

IReadFile* file = fs->createMemoryReadFile(&Game->loadParam,
sizeof(Game->loadParam), L"levelparameter.cfg", false);

// Charge un fichier cfg.
smgr->getMesh( file );
file->drop ();

// Charge la carte actuelle.
Mesh = (IQ3LevelMesh*) smgr->getMesh(mapName);
if ( 0 == Mesh )
return;

Ajoute le mesh de géométrie à la scène (polygones et patchs). Le mesh de géométrie est optimisé pour le dessin rapide.

IMesh *geometry = Mesh->getMesh(E_Q3_MESH_GEOMETRY);
if ( 0 == geometry || geometry->getMeshBufferCount() == 0)
return;

Game->CurrentMapName = mapName;

// Crée une liste de collisions.
Meta = 0;

ITriangleSelector * selector = 0;
if (collision)
Meta = smgr->createMetaTriangleSelector();

//IMeshBuffer *b0 = geometry->getMeshBuffer(0);
//s32 minimalNodes = b0 ? core::s32_max ( 2048, b0->getVertexCount() / 32 ) : 2048;
s32 minimalNodes = 2048;

MapParent = smgr->addOctreeSceneNode(geometry, 0, -1, minimalNodes);
MapParent->setName ( mapName );
if ( Meta )
{
selector = smgr->createOctreeTriangleSelector( geometry,MapParent, minimalNodes);
//selector = smgr->createTriangleSelector ( geometry, MapParent );
Meta->addTriangleSelector( selector);
selector->drop ();
}

// Parent logique pour les objets.
ItemParent = smgr->addEmptySceneNode();
if ( ItemParent )
ItemParent->setName ( "Item Container" );

ShaderParent = smgr->addEmptySceneNode();
if ( ShaderParent )
ShaderParent->setName ( "Shader Container" );

UnresolvedParent = smgr->addEmptySceneNode();
if ( UnresolvedParent )
UnresolvedParent->setName ( "Unresolved Container" );

FogParent = smgr->addEmptySceneNode();
if ( FogParent )
FogParent->setName ( "Fog Container" );

// Parent logique pour les projectiles.
BulletParent = smgr->addEmptySceneNode();
if ( BulletParent )
BulletParent->setName ( "Bullet Container" );

VII - Nœuds de scènes

Maintenant, construisons un nœud de scène pour chaque shader. Les objets sont stockés dans le mesh de Quake E_Q3_MESH_ITEMS et les identifiants de shaders sont stockés dans le MaterialParameters surtout des crânes sombres et de la lave mouvante… ou des tubes vert flashy ?

Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_ITEMS,ShaderParent, Meta, false );
Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_FOG,FogParent, 0, false );
Q3ShaderFactory ( Game->loadParam, Game->Device, Mesh, E_Q3_MESH_UNRESOLVED,UnresolvedParent, Meta, true );

Maintenant construit les modèles à partir de la liste d'entrées.

Q3ModelFactory ( Game->loadParam, Game->Device, Mesh, ItemParent, false );
}

Ajoute un nœud de scène avec une icône à l'arbre de scène.

void CQuake3EventHandler::addSceneTreeItem( ISceneNode * parent, IGUITreeViewNode* nodeParent)
{
IGUITreeViewNode* node;
wchar_t msg[128];

s32 imageIndex;
list<ISceneNode*>::ConstIterator it = parent->getChildren().begin();
for (; it != parent->getChildren().end(); ++it)
{
switch ( (*it)->getType () )
{
case ESNT_Q3SHADER_SCENE_NODE: imageIndex = 0; break;
case ESNT_CAMERA: imageIndex = 1; break;
case ESNT_EMPTY: imageIndex = 2; break;
case ESNT_MESH: imageIndex = 3; break;
case ESNT_OCTREE: imageIndex = 3; break;
case ESNT_ANIMATED_MESH: imageIndex = 4; break;
case ESNT_SKY_BOX: imageIndex = 5; break;
case ESNT_BILLBOARD: imageIndex = 6; break;
case ESNT_PARTICLE_SYSTEM: imageIndex = 7; break;
case ESNT_TEXT: imageIndex = 8; break;
default:imageIndex = -1; break;
}

if ( imageIndex < 0 )
{
swprintf ( msg, 128, L"%hs,%hs",
Game->Device->getSceneManager ()->getSceneNodeTypeName ( (*it)->getType () ),
(*it)->getName()
);
}
else
{
swprintf ( msg, 128, L"%hs",(*it)->getName() );
}

node = nodeParent->addChildBack( msg, 0, imageIndex );

// Ajoute tous les animateurs.
list<ISceneNodeAnimator*>::ConstIterator ait = (*it)->getAnimators().begin();
for (; ait != (*it)->getAnimators().end(); ++ait)
{
imageIndex = -1;
swprintf ( msg, 128, L"%hs",
Game->Device->getSceneManager ()->getAnimatorTypeName ( (*ait)->getType () )
);

switch ( (*ait)->getType () )
{
case ESNAT_FLY_CIRCLE:
case ESNAT_FLY_STRAIGHT:
case ESNAT_FOLLOW_SPLINE:
case ESNAT_ROTATION:
case ESNAT_TEXTURE:
case ESNAT_DELETION:
case ESNAT_COLLISION_RESPONSE:
case ESNAT_CAMERA_FPS:
case ESNAT_CAMERA_MAYA:
default:
break;
}
node->addChildBack( msg, 0, imageIndex );
}

addSceneTreeItem ( *it, node );
}
}

// Ajoute la vie !
void CQuake3EventHandler::CreatePlayers()
{
Player[0].create ( Game->Device, Mesh, MapParent, Meta );
}

// Ajoute un skydome à la scène.
void CQuake3EventHandler::AddSky( u32 dome, const c8 *texture)
{
ISceneManager *smgr = Game->Device->getSceneManager ();
IVideoDriver * driver = Game->Device->getVideoDriver();

bool oldMipMapState = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

if ( 0 == dome )
{
// Ordre irrlicht.
//static const c8*p[] = { "ft", "lf", "bk", "rt", "up", "dn" };
// Ordre quake3.
static const c8*p[] = { "ft", "rt", "bk", "lf", "up", "dn" };

u32 i = 0;
snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
SkyNode = smgr->addSkyBoxSceneNode( driver->getTexture ( buf ), 0, 0, 0, 0, 0 );

if (SkyNode)
{
for ( i = 0; i < 6; ++i )
{
snprintf ( buf, 64, "%s_%s.jpg", texture, p[i] );
SkyNode->getMaterial(i).setTexture ( 0, driver->getTexture ( buf ) );
}
}
}
else
if ( 1 == dome )
{
snprintf ( buf, 64, "%s.jpg", texture );
SkyNode = smgr->addSkyDomeSceneNode(
driver->getTexture( buf ), 32,32,
1.f, 1.f, 1000.f, 0, 11);
}
else
if ( 2 == dome )
{
snprintf ( buf, 64, "%s.jpg", texture );
SkyNode = smgr->addSkyDomeSceneNode(
driver->getTexture( buf ), 16,8,
0.95f, 2.f, 1000.f, 0, 11);
}

if (SkyNode)
SkyNode->setName("Skydome");
//SkyNode->getMaterial(0).ZBuffer = video::EMDF_DEPTH_LESS_EQUAL;

driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState);
}

// Active les éléments d'interface utilisateur.
void CQuake3EventHandler::SetGUIActive( s32 command)
{
bool inputState = false;

ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();

switch ( command )
{
case 0: Game->guiActive = 0; inputState = !Game->guiActive; break;
case 1: Game->guiActive = 1; inputState = !Game->guiActive;;break;
case 2: Game->guiActive ^= 1; inputState = !Game->guiActive;break;
case 3:
if ( camera )
inputState = !camera->isInputReceiverEnabled();
break;
}

if ( camera )
{
camera->setInputReceiverEnabled ( inputState );
Game->Device->getCursorControl()->setVisible( !inputState );
}

if ( gui.Window )
{
gui.Window->setVisible ( Game->guiActive != 0 );
}

if ( Game->guiActive &&
gui.SceneTree && Game->Device->getGUIEnvironment()->getFocus() != gui.SceneTree
)
{
gui.SceneTree->getRoot()->clearChildren();
addSceneTreeItem ( Game->Device->getSceneManager()->getRootSceneNode(), gui.SceneTree->getRoot() );
}

Game->Device->getGUIEnvironment()->setFocus ( Game->guiActive ? gui.Window: 0 );
}

VIII - Interactions

Gère les interactions du joueur.

bool CQuake3EventHandler::OnEvent(const SEvent& eve)
{
if ( eve.EventType == EET_LOG_TEXT_EVENT )
{
return false;
}

if ( Game->guiActive && eve.EventType == EET_GUI_EVENT )
{
if ( eve.GUIEvent.Caller == gui.MapList && eve.GUIEvent.EventType == gui::EGET_LISTBOX_SELECTED_AGAIN )
{
s32 selected = gui.MapList->getSelected();
if ( selected >= 0 )
{
stringw loadMap = gui.MapList->getListItem ( selected );
if ( 0 == MapParent || loadMap != Game->CurrentMapName )
{
printf ( "Loading map %ls\n", loadMap.c_str() );
LoadMap ( loadMap , 1 );
if ( 0 == Game->loadParam.loadSkyShader )
{
AddSky ( 1, "skydome2" );
}
CreatePlayers ();
CreateGUI ();
SetGUIActive ( 0 );
return true;
}
}
}
else
if ( eve.GUIEvent.Caller == gui.ArchiveRemove && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
Game->Device->getFileSystem()->removeFileArchive( gui.ArchiveList->getSelected() );
Game->CurrentMapName = "";
AddArchive ( "" );
}
else
if ( eve.GUIEvent.Caller == gui.ArchiveAdd && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
if ( 0 == gui.ArchiveFileOpen )
{
Game->Device->getFileSystem()->setFileListSystem ( FILESYSTEM_NATIVE );
gui.ArchiveFileOpen = Game->Device->getGUIEnvironment()->addFileOpenDialog ( L"Add Game Archive" , false,gui.Window  );
}
}
else
if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_SELECTED )
{
AddArchive ( gui.ArchiveFileOpen->getFileName() );
gui.ArchiveFileOpen = 0;
}
else
if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_DIRECTORY_SELECTED )
{
AddArchive ( gui.ArchiveFileOpen->getDirectoryName() );
}
else
if ( eve.GUIEvent.Caller == gui.ArchiveFileOpen && eve.GUIEvent.EventType == gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED )
{
gui.ArchiveFileOpen = 0;
}
else
if ( ( eve.GUIEvent.Caller == gui.ArchiveUp || eve.GUIEvent.Caller == gui.ArchiveDown ) &&
eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
s32 rel = eve.GUIEvent.Caller == gui.ArchiveUp ? -1 : 1;
if ( Game->Device->getFileSystem()->moveFileArchive ( gui.ArchiveList->getSelected (), rel ) )
{
s32 newIndex = core::s32_clamp ( gui.ArchiveList->getSelected() + rel, 0, gui.ArchiveList->getRowCount() - 1 );
AddArchive ( "" );
gui.ArchiveList->setSelected ( newIndex );
Game->CurrentMapName = "";
}
}
else
if ( eve.GUIEvent.Caller == gui.VideoDriver && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
{
Game->deviceParam.DriverType = (E_DRIVER_TYPE) gui.VideoDriver->getItemData ( gui.VideoDriver->getSelected() );
}
else
if ( eve.GUIEvent.Caller == gui.VideoMode && eve.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
{
u32 val = gui.VideoMode->getItemData ( gui.VideoMode->getSelected() );
Game->deviceParam.WindowSize.Width = val >> 16;
Game->deviceParam.WindowSize.Height = val & 0xFFFF;
}
else
if ( eve.GUIEvent.Caller == gui.FullScreen && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
Game->deviceParam.Fullscreen = gui.FullScreen->isChecked();
}
else
if ( eve.GUIEvent.Caller == gui.Bit32 && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
Game->deviceParam.Bits = gui.Bit32->isChecked() ? 32 : 16;
}
else
if ( eve.GUIEvent.Caller == gui.MultiSample && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
{
Game->deviceParam.AntiAlias = gui.MultiSample->getPos();
}
else
if ( eve.GUIEvent.Caller == gui.Tesselation && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
{
Game->loadParam.patchTesselation = gui.Tesselation->getPos ();
}
else
if ( eve.GUIEvent.Caller == gui.Gamma && eve.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED )
{
Game->GammaValue = gui.Gamma->getPos () * 0.01f;
Game->Device->setGammaRamp ( Game->GammaValue, Game->GammaValue, Game->GammaValue, 0.f, 0.f );
}
else
if ( eve.GUIEvent.Caller == gui.SetVideoMode && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
Game->retVal = 2;
Game->Device->closeDevice();
}
else
if ( eve.GUIEvent.Caller == gui.Window && eve.GUIEvent.EventType == gui::EGET_ELEMENT_CLOSED )
{
Game->Device->closeDevice();
}
else
if ( eve.GUIEvent.Caller == gui.Collision && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
// Active le vol.
Game->flyTroughState ^= 1;
Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );

printf ( "collision %d\n", Game->flyTroughState == 0 );
}
else
if ( eve.GUIEvent.Caller == gui.Visible_Map && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
bool v = gui.Visible_Map->isChecked();

if ( MapParent )
{
printf ( "static node set visible %d\n",v );
MapParent->setVisible ( v );
}
}
else
if ( eve.GUIEvent.Caller == gui.Visible_Shader && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
bool v = gui.Visible_Shader->isChecked();

if ( ShaderParent )
{
printf ( "shader node set visible %d\n",v );
ShaderParent->setVisible ( v );
}
}
else
if ( eve.GUIEvent.Caller == gui.Visible_Skydome && eve.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED )
{
if ( SkyNode )
{
bool v = !SkyNode->isVisible();
printf ( "skynode set visible %d\n",v );
SkyNode->setVisible ( v );
}
}
else
if ( eve.GUIEvent.Caller == gui.Respawn && eve.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
Player[0].respawn ();
}

return false;
}

// Feux
if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_SPACE &&
eve.KeyInput.PressedDown == false) ||
(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
)
{
ICameraSceneNode * camera = Game->Device->getSceneManager()->getActiveCamera ();
if ( camera && camera->isInputReceiverEnabled () )
{
useItem( Player + 0 );
}
}

// Active l'interface utilisateur.
if ((eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_F1 &&
eve.KeyInput.PressedDown == false) ||
(eve.EventType == EET_MOUSE_INPUT_EVENT && eve.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
)
{
SetGUIActive ( 2 );
}

// Vérifie si l'utilisateur presse une touche.
if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.PressedDown == false)
{
// Échap alterne la caméra d'entrée.
if ( eve.KeyInput.Key == irr::KEY_ESCAPE )
{
SetGUIActive ( 3 );
}
else
if (eve.KeyInput.Key == KEY_F11)
{
// Les captures d'écran sont effectuées sans gamma !
IImage* image = Game->Device->getVideoDriver()->createScreenShot();
if (image)
{
core::vector3df pos;
core::vector3df rot;
ICameraSceneNode * cam = Game->Device->getSceneManager()->getActiveCamera ();
if ( cam )
{
pos = cam->getPosition ();
rot = cam->getRotation ();
}

static const c8 *dName[] = { "null", "software", "burning",
"d3d8", "d3d9", "opengl" };

snprintf(buf, 256, "%s_%ls_%.0f_%.0f_%.0f_%.0f_%.0f_%.0f.jpg",
dName[Game->Device->getVideoDriver()->getDriverType()],
Game->CurrentMapName.c_str(),
pos.X, pos.Y, pos.Z,
rot.X, rot.Y, rot.Z
);
path filename ( buf );
filename.replace ( '/', '_' );
printf ( "screenshot : %s\n", filename.c_str() );
Game->Device->getVideoDriver()->writeImageToFile(image, filename, 100 );
image->drop();
}
}
else
if (eve.KeyInput.Key == KEY_F9)
{
s32 value = EDS_OFF;

Game->debugState = ( Game->debugState + 1 ) & 3;

switch ( Game->debugState )
{
case 1: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL; break;
case 2: value = EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_SKELETON; break;
}
set debug map data on/off debugState = debugState == EDS_OFF ? EDS_NORMALS | EDS_MESH_WIRE_OVERLAY | EDS_BBOX_ALL: EDS_OFF;
if ( ItemParent )
{
list<ISceneNode*>::ConstIterator it = ItemParent->getChildren().begin();
for (; it != ItemParent->getChildren().end(); ++it)
{
(*it)->setDebugDataVisible ( value );
}
}

if ( ShaderParent )
{
list<ISceneNode*>::ConstIterator it = ShaderParent->getChildren().begin();
for (; it != ShaderParent->getChildren().end(); ++it)
{
(*it)->setDebugDataVisible ( value );
}
}

if ( UnresolvedParent )
{
list<ISceneNode*>::ConstIterator it = UnresolvedParent->getChildren().begin();
for (; it != UnresolvedParent->getChildren().end(); ++it)
{
(*it)->setDebugDataVisible ( value );
}
}

if ( FogParent )
{
list<ISceneNode*>::ConstIterator it = FogParent->getChildren().begin();
for (; it != FogParent->getChildren().end(); ++it)
{
(*it)->setDebugDataVisible ( value );
}
}

if ( SkyNode )
{
SkyNode->setDebugDataVisible ( value );
}

}
else
if (eve.KeyInput.Key == KEY_F8)
{
// Active/désactive la gravité.
Game->gravityState ^= 1;
Player[0].cam()->setGravity ( getGravity ( Game->gravityState ? "earth" : "none" ) );
printf ( "gravity %s\n", Game->gravityState ? "earth" : "none" );
}
else
if (eve.KeyInput.Key == KEY_F7)
{
// Active le vol.
Game->flyTroughState ^= 1;
Player[0].cam()->setAnimateTarget ( Game->flyTroughState == 0 );
if ( gui.Collision )
gui.Collision->setChecked ( Game->flyTroughState == 0 );

printf ( "collision %d\n", Game->flyTroughState == 0 );
}
else
if (eve.KeyInput.Key == KEY_F2)
{
Player[0].respawn ();
}
else
if (eve.KeyInput.Key == KEY_F3)
{
if ( MapParent )
{
bool v = !MapParent->isVisible ();
printf ( "static node set visible %d\n",v );
MapParent->setVisible ( v );
if ( gui.Visible_Map )
gui.Visible_Map->setChecked ( v );
}
}
else
if (eve.KeyInput.Key == KEY_F4)
{
if ( ShaderParent )
{
bool v = !ShaderParent->isVisible ();
printf ( "shader node set visible %d\n",v );
ShaderParent->setVisible ( v );
if ( gui.Visible_Shader )
gui.Visible_Shader->setChecked ( v );
}
}
else
if (eve.KeyInput.Key == KEY_F5)
{
if ( FogParent )
{
bool v = !FogParent->isVisible ();
printf ( "fog node set visible %d\n",v );
FogParent->setVisible ( v );
if ( gui.Visible_Fog )
gui.Visible_Fog->setChecked ( v );
}

}
else
if (eve.KeyInput.Key == KEY_F6)
{
if ( UnresolvedParent )
{
bool v = !UnresolvedParent->isVisible ();
printf ( "unresolved node set visible %d\n",v );
UnresolvedParent->setVisible ( v );
if ( gui.Visible_Unresolved )
gui.Visible_Unresolved->setChecked ( v );
}
}
}

// Vérifie si l'utilisateur presse la touche C (pour s'accroupir).
if ( eve.EventType == EET_KEY_INPUT_EVENT && eve.KeyInput.Key == KEY_KEY_C )
{
// s'accroupir
ISceneNodeAnimatorCollisionResponse *anim = Player[0].cam ();
if ( anim && 0 == Game->flyTroughState )
{
if ( false == eve.KeyInput.PressedDown )
{
// se lève
anim->setEllipsoidRadius (  vector3df(30,45,30) );
anim->setEllipsoidTranslation ( vector3df(0,40,0));

}
else
{
// sur vos genoux.
anim->setEllipsoidRadius (  vector3df(30,20,30) );
anim->setEllipsoidTranslation ( vector3df(0,20,0));
}
return true;
}
}
return false;
}

IX - Objets

Utilisation d'objet.

void CQuake3EventHandler::useItem( Q3Player * player)
{
ISceneManager* smgr = Game->Device->getSceneManager();
ICameraSceneNode* camera = smgr->getActiveCamera();

if (!camera)
return;

SParticleImpact imp;
imp.when = 0;

// Obtient la ligne de la caméra.

vector3df start = camera->getPosition();

if ( player->WeaponNode )
{
start.X += 0.f;
start.Y += 0.f;
start.Z += 0.f;
}

vector3df end = (camera->getTarget() - start);
end.normalize();
start += end*20.0f;

end = start + (end * camera->getFarValue());

triangle3df triangle;
line3d<f32> line(start, end);

// Obtient le point d'intersection avec la carte.
scene::ISceneNode* hitNode;
if (smgr->getSceneCollisionManager()->getCollisionPoint(
line, Meta, end, triangle,hitNode))
{
// Collisions avec les murs.
vector3df out = triangle.getNormal();
out.setLength(0.03f);

imp.when = 1;
imp.outVector = out;
imp.pos = end;

player->setAnim ( "pow" );
player->Anim[1].next += player->Anim[1].delta;
}
else
{
// Pas de collision avec les murs.
vector3df start = camera->getPosition();
if ( player->WeaponNode )
{
//start.X += 10.f;
//start.Y += -5.f;
//start.Z += 1.f;
}

vector3df end = (camera->getTarget() - start);
end.normalize();
start += end*20.0f;
end = start + (end * camera->getFarValue());
}

// Crée une boule de feu.
ISceneNode* node = 0;
node = smgr->addBillboardSceneNode( BulletParent,dimension2d<f32>(10,10), start);

node->setMaterialFlag(EMF_LIGHTING, false);
node->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture("fireball.bmp"));
node->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
node->setMaterialType(EMT_TRANSPARENT_ADD_COLOR);

f32 length = (f32)(end - start).getLength();
const f32 speed = 5.8f;
u32 time = (u32)(length / speed);

ISceneNodeAnimator* anim = 0;

// Met la ligne de vol.

anim = smgr->createFlyStraightAnimator(start, end, time);
node->addAnimator(anim);
anim->drop();

snprintf ( buf, 64, "bullet: %s on %.1f,%1.f,%1.f",
imp.when ? "hit" : "nohit", end.X, end.Y, end.Z );
node->setName ( buf );

anim = smgr->createDeleteAnimator(time);
node->addAnimator(anim);
anim->drop();

if (imp.when)
{
// Crée une note d'impact.
imp.when = Game->Device->getTimer()->getTime() +
(time + (s32) ( ( 1.f + Noiser::get() ) * 250.f ));
Impacts.push_back(imp);
}

// Joue le son
}

// dessiné quand les boules touchent quelque chose.
void CQuake3EventHandler::createParticleImpacts( u32 now )
{
ISceneManager* sm = Game->Device->getSceneManager();

struct smokeLayer
{
const c8 * texture;
f32 scale;
f32 minparticleSize;
f32 maxparticleSize;
f32 boxSize;
u32 minParticle;
u32 maxParticle;
u32 fadeout;
u32 lifetime;
};

smokeLayer smoke[] =
{
{ "smoke2.jpg", 0.4f, 1.5f, 18.f, 20.f, 20, 50, 2000, 10000 },
{ "smoke3.jpg", 0.2f, 1.2f, 15.f, 20.f, 10, 30, 1000, 12000 }
};

u32 i;
u32 g;
s32 factor = 1;
for ( g = 0; g != 2; ++g )
{
smoke[g].minParticle *= factor;
smoke[g].maxParticle *= factor;
smoke[g].lifetime *= factor;
smoke[g].boxSize *= Noiser::get() * 0.5f;
}

for ( i=0; i < Impacts.size(); ++i)
{
if (now < Impacts[i].when)
continue;

// Crée un système de particules de fumée.
IParticleSystemSceneNode* pas = 0;

for ( g = 0; g != 2; ++g )
{
pas = sm->addParticleSystemSceneNode(false, BulletParent, -1, Impacts[i].pos);

snprintf ( buf, 64, "bullet impact smoke at %.1f,%.1f,%1.f",
Impacts[i].pos.X,Impacts[i].pos.Y,Impacts[i].pos.Z);
pas->setName ( buf );

// Crée un nuage plat.
vector3df direction = Impacts[i].outVector;
direction *= smoke[g].scale;
IParticleEmitter* em = pas->createBoxEmitter(
aabbox3d<f32>(-4.f,0.f,-4.f,20.f,smoke[g].minparticleSize,20.f),
direction,smoke[g].minParticle, smoke[g].maxParticle,
video::SColor(0,0,0,0),video::SColor(0,128,128,128),
250,4000, 60);

em->setMinStartSize (dimension2d<f32>( smoke[g].minparticleSize, smoke[g].minparticleSize));
em->setMaxStartSize (dimension2d<f32>( smoke[g].maxparticleSize, smoke[g].maxparticleSize));

pas->setEmitter(em);
em->drop();

// Les particules deviennent invisibles.
IParticleAffector* paf = pas->createFadeOutParticleAffector(
video::SColor ( 0, 0, 0, 0 ), smoke[g].fadeout);
pas->addAffector(paf);
paf->drop();

// Temps de vie du système de particules.
ISceneNodeAnimator* anim = sm->createDeleteAnimator( smoke[g].lifetime);
pas->addAnimator(anim);
anim->drop();

pas->setMaterialFlag(video::EMF_LIGHTING, false);
pas->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
pas->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
pas->setMaterialTexture(0, Game->Device->getVideoDriver()->getTexture( smoke[g].texture ));
}

// Joue le son d'impact.
#ifdef USE_IRRKLANG
if (irrKlang) { audio::ISound* sound = irrKlang->play3D(impactSound, Impacts[i].pos, false, false, true);
if (sound) { adjust max value a bit to make to sound of an impact louder sound->setMinDistance(400); sound->drop(); } }
#endif

// Supprime l'entrée.
Impacts.erase(i);
i--;
}
}

X - Rendu

Système de rendu.

void CQuake3EventHandler::Render()
{
IVideoDriver * driver = Game->Device->getVideoDriver();
if ( 0 == driver )
return;

// TODO: Ceci ne fonctionne pas pour le moment.
const bool anaglyph=false;
if (anaglyph)
{
scene::ICameraSceneNode* cameraOld = Game->Device->getSceneManager()->getActiveCamera();
driver->beginScene(true, true, SColor(0,0,0,0));
driver->getOverrideMaterial().Material.ColorMask = ECP_NONE;
driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
driver->getOverrideMaterial().EnablePasses = ESNRP_SKY_BOX +
ESNRP_SOLID +
ESNRP_TRANSPARENT +
ESNRP_TRANSPARENT_EFFECT +
ESNRP_SHADOW;
Game->Device->getSceneManager()->drawAll();
driver->clearZBuffer();

const vector3df oldPosition = cameraOld->getPosition();
const vector3df oldTarget   = cameraOld->getTarget();
const matrix4 startMatrix   = cameraOld->getAbsoluteTransformation();
const vector3df focusPoint  = (oldTarget -
cameraOld->getAbsolutePosition()).setLength(10000) +
cameraOld->getAbsolutePosition() ;

scene::ICameraSceneNode* camera = cameraOld;//Game->Device->getSceneManager()->addCameraSceneNode();

// Oeil gauche...
vector3df pos;
matrix4   move;

move.setTranslation( vector3df(-1.5f,0.0f,0.0f) );
pos=(startMatrix*move).getTranslation();

driver->getOverrideMaterial().Material.ColorMask = ECP_RED;
driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
driver->getOverrideMaterial().EnablePasses =
ESNRP_SKY_BOX|ESNRP_SOLID|ESNRP_TRANSPARENT|
ESNRP_TRANSPARENT_EFFECT|ESNRP_SHADOW;

camera->setPosition(pos);
camera->setTarget(focusPoint);

Game->Device->getSceneManager()->drawAll();
driver->clearZBuffer();

// Oeil droit...
move.setTranslation( vector3df(1.5f,0.0f,0.0f) );
pos=(startMatrix*move).getTranslation();

driver->getOverrideMaterial().Material.ColorMask = ECP_GREEN + ECP_BLUE;
driver->getOverrideMaterial().EnableFlags  = EMF_COLOR_MASK;
driver->getOverrideMaterial().EnablePasses =
ESNRP_SKY_BOX|ESNRP_SOLID|ESNRP_TRANSPARENT|
ESNRP_TRANSPARENT_EFFECT|ESNRP_SHADOW;

camera->setPosition(pos);
camera->setTarget(focusPoint);

Game->Device->getSceneManager()->drawAll();

driver->getOverrideMaterial().Material.ColorMask=ECP_ALL;
driver->getOverrideMaterial().EnableFlags=0;
driver->getOverrideMaterial().EnablePasses=0;

if (camera != cameraOld)
{
Game->Device->getSceneManager()->setActiveCamera(cameraOld);
camera->remove();
}
else
{
camera->setPosition(oldPosition);
camera->setTarget(oldTarget);
}
}
else
{
driver->beginScene(true, true, SColor(0,0,0,0));
Game->Device->getSceneManager()->drawAll();
}
Game->Device->getGUIEnvironment()->drawAll();
driver->endScene();
}

Met à jour le nœud de scène générique.

void CQuake3EventHandler::Animate()
{
u32 now = Game->Device->getTimer()->getTime();

Q3Player * player = Player + 0;

checkTimeFire ( player->Anim, 4, now );

// Demande les attributs du gestionnaire de scène.
if ( player->Anim[0].flags & FIRED )
{
ISceneManager *smgr = Game->Device->getSceneManager ();
wchar_t msg[128];
IVideoDriver * driver = Game->Device->getVideoDriver();

IAttributes * attr = smgr->getParameters();
swprintf ( msg, 128,
L"Q3 %s [%ls], FPS:%03d Tri:%.03fm Cull %d/%d nodes (%d,%d,%d)",
Game->CurrentMapName.c_str(),
driver->getName(),
driver->getFPS (),
(f32) driver->getPrimitiveCountDrawn( 0 ) * ( 1.f / 1000000.f ),
attr->getAttributeAsInt ( "culled" ),
attr->getAttributeAsInt ( "calls" ),
attr->getAttributeAsInt ( "drawn_solid" ),
attr->getAttributeAsInt ( "drawn_transparent" ),
attr->getAttributeAsInt ( "drawn_transparent_effect" )
);
Game->Device->setWindowCaption( msg );

swprintf ( msg, 128,
L"%03d fps, F1 GUI on/off, F2 respawn, F3-F6 toggle Nodes, F7 Collision on/off"
L", F8 Gravity on/off, Right Mouse Toggle GUI",
Game->Device->getVideoDriver()->getFPS ()
);
if ( gui.StatusLine )
gui.StatusLine->setText ( msg );
player->Anim[0].flags &= ~FIRED;
}

// à l'arrêt&#8230;
if ( player->Anim[1].flags & FIRED )
{
if ( strcmp ( player->animation, "idle" ) )
player->setAnim ( "idle" );

player->Anim[1].flags &= ~FIRED;
}

createParticleImpacts ( now );

}

XI - Boucle principale

Boucle principale du jeu.

void runGame ( GameData *game )
{
if ( game->retVal >= 3 )
return;

game->Device = (*game->createExDevice) ( game->deviceParam );
if ( 0 == game->Device)
{
// Ne peut pas créer le pilote sélectionné.
game->retVal = 0;
return;
}

// Crée un receveur d'événements basé sur les données du jeu.
CQuake3EventHandler *eventHandler = new CQuake3EventHandler( game );

// Charge les configurations stockées.
game->load ( "explorer.cfg" );

// Ajoute nos propres dossiers des médias et des archives au système de fichiers.
for ( u32 i = 0; i < game->CurrentArchiveList.size(); ++i )
{
eventHandler->AddArchive ( game->CurrentArchiveList[i] );
}

// Charge une carte ou démarre l'interface utilisateur.
if ( game->CurrentMapName.size () )
{
eventHandler->LoadMap ( game->CurrentMapName, 1 );
if ( 0 == game->loadParam.loadSkyShader )
eventHandler->AddSky ( 1, "skydome2" );
eventHandler->CreatePlayers ();
eventHandler->CreateGUI ();
eventHandler->SetGUIActive ( 0 );

// Met le joueur à la dernière position lors du redémarrage.
if ( game->retVal == 2 )
{
eventHandler->GetPlayer( 0 )->setpos ( game->PlayerPosition, game->PlayerRotation );
}
}
else
{
// Démarrage vide.
eventHandler->AddSky ( 1, "skydome2" );
eventHandler->CreatePlayers ();
eventHandler->CreateGUI ();
eventHandler->SetGUIActive ( 1 );
background_music ( "IrrlichtTheme.ogg" );
}

game->retVal = 3;
while( game->Device->run() )
{
eventHandler->Animate ();
eventHandler->Render ();
//if ( !game->Device->isWindowActive() )
game->Device->yield();
}

game->Device->setGammaRamp ( 1.f, 1.f, 1.f, 0.f, 0.f );
delete eventHandler;
}

#if defined (_IRR_WINDOWS_) && 0
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif

La fonction main, fait toutes les initialisations.

int IRRCALLCONV main(int argc, char* argv[])
{
path prgname(argv[0]);
GameData game ( deletePathFromPath ( prgname, 1 ) );

// Charge dynamiquement Irrlicht.
const c8 * dllName = argc > 1 ? argv[1] : "irrlicht.dll";
game.createExDevice = load_createDeviceEx ( dllName );
if ( 0 == game.createExDevice )
{
game.retVal = 3;
printf ( "Could not load %s.\n", dllName );
return game.retVal; // Ne peut pas charger le dll.
}

// Commence sans demander le pilote.
game.retVal = 1;
do
{
// Si le pilote ne peut pas être créé, demande un autre pilote.
if ( game.retVal == 0 )
{
game.setDefault ();
// demande à l'utilisateur un pilote.
game.deviceParam.DriverType=driverChoiceConsole();
if (game.deviceParam.DriverType==video::EDT_COUNT)
game.retVal = 3;
}
runGame ( &game );
} while ( game.retVal < 3 );

return game.retVal;
}

XII - Conclusion

Vous pouvez désormais créer votre propre chargeur de cartes en vous inspirant de celui-ci.

Dans le prochain tutoriel Visualiseur de matériels, nous verrons comment jouer avec les paramètres des matériels et nous observerons les résultats.

XIII - Remerciements

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

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