Tutoriel Irrlicht 22

Visualiseur de matériels

| Nikolaus Gebhart | Laurent Alexandre |

Traducteur

Neckara

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

Ce programme peut être utilisé pour tester les options de matériels et observer les résultats. Seuls les matériels par défaut sans shader sont utilisés à l'intérieur.

Commentez

- Titre : Tutoriel Irrlicht 22

- Auteurs : Nikolaus Gebhart, Laurent Alexandre

- Traducteur : Neckara

- Parution : 10 juillet 2013

- Mise à jour : 10 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. Prérequis

- II. Contrôleur d'interface

- II-A. Édition des couleurs

- II-B. Sélection des textures

- II-C. Configuration de matériel

- II-D. Configuration du gestionnaire de lumières

- III. Configuration de l'application

- IV. Classe principale

- V. Main

- VI. Conclusion

- VII. Remerciements

I - Prérequis

Vous avez deux nœuds pour rendre plus facilement visibles les différences entre deux configurations. De plus, vous avez un lightscenenode et vous pouvez modifier les valeurs de l'ambiance globale.

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

using namespace irr;

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

Les variables à l'intérieur de cet espace de nom anonyme sont globales et sont restreintes à ce fichier.

namespace
{
const wchar_t* const DriverTypeNames[] =
{
L"NULL",
L"SOFTWARE",
L"BURNINGSVIDEO",
L"DIRECT3D8",
L"DIRECT3D9",
L"OPENGL",
0,
};

// Pour les identifiants d'interface utilisateur.
enum EGUI_IDS
{
GUI_ID_OPEN_TEXTURE = 1,
GUI_ID_QUIT,
GUI_ID_MAX
};

// Nom utilisé dans la sélection de texture pour effacer les textures dans un nœud.
const core::stringw CLEAR_TEXTURE = L"CLEAR texture";

// Quelques constantes de couleur utiles.
const video::SColor SCOL_BLACK     = video::SColor(255, 0,   0,   0);
const video::SColor SCOL_BLUE      = video::SColor(255, 0,   0,  255);
const video::SColor SCOL_CYAN      = video::SColor(255, 0,  255, 255);
const video::SColor SCOL_GRAY      = video::SColor(255, 128,128, 128);
const video::SColor SCOL_GREEN     = video::SColor(255, 0,  255,  0);
const video::SColor SCOL_MAGENTA   = video::SColor(255, 255, 0,  255);
const video::SColor SCOL_RED       = video::SColor(255, 255, 0,   0);
const video::SColor SCOL_YELLOW    = video::SColor(255, 255, 255, 0);
const video::SColor SCOL_WHITE     = video::SColor(255, 255, 255, 255);
};  // namespace

Retourne un nouveau nombre unique à chaque appel.

s32 makeUniqueId()
{
static int unique = GUI_ID_MAX;
++unique;
return unique;
}

Recherche quel type de vertex est nécessaire pour le type de matériel donné.

video::E_VERTEX_TYPE getVertexTypeForMaterialType(video::E_MATERIAL_TYPE materialType)
{
using namespace video;

switch ( materialType )
{
case EMT_SOLID:
return EVT_STANDARD;

case EMT_SOLID_2_LAYER:
return EVT_STANDARD;

case EMT_LIGHTMAP:
case EMT_LIGHTMAP_ADD:
case EMT_LIGHTMAP_M2:
case EMT_LIGHTMAP_M4:
case EMT_LIGHTMAP_LIGHTING:
case EMT_LIGHTMAP_LIGHTING_M2:
case EMT_LIGHTMAP_LIGHTING_M4:
return EVT_2TCOORDS;

case EMT_DETAIL_MAP:
return EVT_2TCOORDS;

case EMT_SPHERE_MAP:
return EVT_STANDARD;

case EMT_REFLECTION_2_LAYER:
return EVT_2TCOORDS;

case EMT_TRANSPARENT_ADD_COLOR:
return EVT_STANDARD;

case EMT_TRANSPARENT_ALPHA_CHANNEL:
return EVT_STANDARD;

case EMT_TRANSPARENT_ALPHA_CHANNEL_REF:
return EVT_STANDARD;

case EMT_TRANSPARENT_VERTEX_ALPHA:
return EVT_STANDARD;

case EMT_TRANSPARENT_REFLECTION_2_LAYER:
return EVT_2TCOORDS;

case EMT_NORMAL_MAP_SOLID:
case EMT_NORMAL_MAP_TRANSPARENT_ADD_COLOR:
case EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA:
case EMT_PARALLAX_MAP_SOLID:
case EMT_PARALLAX_MAP_TRANSPARENT_ADD_COLOR:
case EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA:
return EVT_TANGENTS;

case EMT_ONETEXTURE_BLEND:
return EVT_STANDARD;

case EMT_FORCE_32BIT:
return EVT_STANDARD;
}
return EVT_STANDARD;
}

II - Contrôleur d'interface

Un contrôleur d'interface utilisateur personnalisé pour éditer les valeurs des couleurs.

class CColorControl : public gui::IGUIElement
{
public:
// Constructeur.
CColorControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, IGUIElement* parent, s32 id=-1 )
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect< s32 >(pos, pos+core::dimension2d<s32>(80, 75)))
, DirtyFlag(true)
, ColorStatic(0)
, EditAlpha(0)
, EditRed(0)
, EditGreen(0)
, EditBlue(0)
{
using namespace gui;
ButtonSetId = makeUniqueId();

const core::rect< s32 > rectControls(0,0,AbsoluteRect.getWidth(),AbsoluteRect.getHeight() );
IGUIStaticText * groupElement = guiEnv->addStaticText (L"", rectControls, true, false, this, -1, false);
groupElement->setNotClipped(true);

guiEnv->addStaticText (text, core::rect<s32>(0,0,80,15), false, false, groupElement, -1, false);

EditAlpha = addEditForNumbers(guiEnv, core::position2d<s32>(0,15), L"a", -1, groupElement );
EditRed = addEditForNumbers(guiEnv, core::position2d<s32>(0,30), L"r", -1, groupElement );
EditGreen = addEditForNumbers(guiEnv, core::position2d<s32>(0,45), L"g", -1, groupElement );
EditBlue = addEditForNumbers(guiEnv, core::position2d<s32>(0,60), L"b", -1, groupElement );

ColorStatic = guiEnv->addStaticText (L"", core::rect<s32>(60,15,80,75), true, false, groupElement, -1, true);

guiEnv->addButton (core::rect<s32>(60,35,80,50), groupElement, ButtonSetId, L"set");
SetEditsFromColor(Color);
}

// Receveur d'événements.
virtual bool OnEvent(const SEvent &event)
{
if ( event.EventType != EET_GUI_EVENT )
return false;

if ( event.GUIEvent.Caller->getID() == ButtonSetId && event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED )
{
Color = GetColorFromEdits();
SetEditsFromColor(Color);
}

return false;
}

// Change les valeurs de la couleur.
void setColor(const video::SColor& col)
{
DirtyFlag = true;
Color = col;
SetEditsFromColor(Color);
}

// Donne les valeurs de la couleur.
const video::SColor& getColor() const
{
return Color;
}

// Réinitialise le fanion sale.
void resetDirty()
{
DirtyFlag = false;
}

// Quand la couleur a été changée, le fanion sale est posé.
bool isDirty() const
{
return DirtyFlag;
};

protected:

// Ajoute une boîte statique pour une description et une zone de texte ainsi les utilisateurs peuvent entrer des nombres.
gui::IGUIEditBox* addEditForNumbers(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t *text, s32 id, gui::IGUIElement * parent)
{
using namespace gui;

core::rect< s32 > rect(pos, pos+core::dimension2d<s32>(10, 15));
guiEnv->addStaticText (text, rect, false, false, parent, -1, false);
rect += core::position2d<s32>( 20, 0 );
rect.LowerRightCorner.X += 20;
gui::IGUIEditBox* edit = guiEnv->addEditBox(L"0", rect, true, parent, id);
return edit;
}

// Donne la valeur de la couleur à partir des champs d'édition.
video::SColor GetColorFromEdits()
{
video::SColor col;

if (EditAlpha)
{
u32 alpha = core::strtoul10(core::stringc(EditAlpha->getText()).c_str());
if (alpha > 255)
alpha = 255;
col.setAlpha(alpha);
}

if (EditRed)
{
u32 red = core::strtoul10(core::stringc(EditRed->getText()).c_str());
if (red > 255)
red = 255;
col.setRed(red);
}

if (EditGreen)
{
u32 green = core::strtoul10(core::stringc(EditGreen->getText()).c_str());
if (green > 255)
green = 255;
col.setGreen(green);
}

if (EditBlue)
{
u32 blue = core::strtoul10(core::stringc(EditBlue->getText()).c_str());
if (blue > 255)
blue = 255;
col.setBlue(blue);
}

return col;
}

// Remplit les champs d'édition avec les valeurs de la couleur donnée.
void SetEditsFromColor(video::SColor col)
{
DirtyFlag = true;
if ( EditAlpha )
EditAlpha->setText( core::stringw(col.getAlpha()).c_str() );
if ( EditRed )
EditRed->setText( core::stringw(col.getRed()).c_str() );
if ( EditGreen )
EditGreen->setText( core::stringw(col.getGreen()).c_str() );
if ( EditBlue )
EditBlue->setText( core::stringw(col.getBlue()).c_str() );
if ( ColorStatic )
ColorStatic->setBackgroundColor(col);
}

private:

bool DirtyFlag;
video::SColor Color;
s32 ButtonSetId;
gui::IGUIStaticText * ColorStatic;
gui::IGUIEditBox * EditAlpha;
gui::IGUIEditBox * EditRed;
gui::IGUIEditBox * EditGreen;
gui::IGUIEditBox * EditBlue;
};

II-A - Édition des couleurs

Un contrôleur d'interface utilisateur personnalisé pour éditer toutes les couleurs typiquement utilisées dans les matériels et les lumières.

class CAllColorsControl : public gui::IGUIElement
{
public:
// Constructeur.
CAllColorsControl(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description, bool hasEmissive, IGUIElement* parent, s32 id=-1)
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(60,250)))
, ControlAmbientColor(0), ControlDiffuseColor(0), ControlSpecularColor(0), ControlEmissiveColor(0)
{
core::rect<s32> rect(0, 0, 60, 15);
guiEnv->addStaticText (description, rect, false, false, this, -1, false);
createColorControls(guiEnv, core::position2d<s32>(0, 15), hasEmissive);
}

// Destructeur
virtual ~CAllColorsControl()
{
ControlAmbientColor->drop();
ControlDiffuseColor->drop();
if ( ControlEmissiveColor )
ControlEmissiveColor->drop();
ControlSpecularColor->drop();
}

// Change les valeurs des couleurs pour celles contenues dans le matériel.
void setColorsToMaterialColors(const video::SMaterial & material)
{
ControlAmbientColor->setColor(material.AmbientColor);
ControlDiffuseColor->setColor(material.DiffuseColor);
ControlEmissiveColor->setColor(material.EmissiveColor);
ControlSpecularColor->setColor(material.SpecularColor);
}

// Met à jour toutes les couleurs changées dans le matériel.
void updateMaterialColors(video::SMaterial & material)
{
if ( ControlAmbientColor->isDirty() )
material.AmbientColor = ControlAmbientColor->getColor();
if ( ControlDiffuseColor->isDirty() )
material.DiffuseColor = ControlDiffuseColor->getColor();
if ( ControlEmissiveColor->isDirty() )
material.EmissiveColor = ControlEmissiveColor->getColor();
if ( ControlSpecularColor->isDirty() )
material.SpecularColor = ControlSpecularColor->getColor();
}

// Change les valeurs des couleurs pour celles issues des données de la lumière.
void setColorsToLightDataColors(const video::SLight & lightData)
{
ControlAmbientColor->setColor(lightData.AmbientColor.toSColor());
ControlAmbientColor->setColor(lightData.DiffuseColor.toSColor());
ControlAmbientColor->setColor(lightData.SpecularColor.toSColor());
}

// Met à jour toutes les couleurs modifiées dans les données de la lumière.
void updateLightColors(video::SLight & lightData)
{
if ( ControlAmbientColor->isDirty() )
lightData.AmbientColor = video::SColorf( ControlAmbientColor->getColor() );
if ( ControlDiffuseColor->isDirty() )
lightData.DiffuseColor = video::SColorf( ControlDiffuseColor->getColor() );
if ( ControlSpecularColor->isDirty() )
lightData.SpecularColor = video::SColorf(ControlSpecularColor->getColor() );
}

// Réinitialise le fanion sale.
void resetDirty()
{
ControlAmbientColor->resetDirty();
ControlDiffuseColor->resetDirty();
ControlSpecularColor->resetDirty();
if ( ControlEmissiveColor )
ControlEmissiveColor->resetDirty();
}

protected:
void createColorControls(gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, bool hasEmissive)
{
ControlAmbientColor = new CColorControl( guiEnv, pos, L"ambient", this);
ControlDiffuseColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 75), L"diffuse", this );
ControlSpecularColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 150), L"specular", this );
if ( hasEmissive )
{
ControlEmissiveColor = new CColorControl( guiEnv, pos + core::position2d<s32>(0, 225), L"emissive", this );
}
}

private:
CColorControl*  ControlAmbientColor;
CColorControl*  ControlDiffuseColor;
CColorControl*  ControlSpecularColor;
CColorControl*  ControlEmissiveColor;
};

II-B - Sélection des textures

Un contrôleur d'interface utilisateur personnalisé pour offrir une sélection des textures disponibles.

class CTextureControl : public gui::IGUIElement
{
public:
CTextureControl(gui::IGUIEnvironment* guiEnv, video::IVideoDriver * driver, const core::position2d<s32> & pos, IGUIElement* parent, s32 id=-1)
: gui::IGUIElement(gui::EGUIET_ELEMENT, guiEnv, parent,id, core::rect<s32>(pos,pos+core::dimension2d<s32>(100,15)))
, DirtyFlag(true), ComboTexture(0)
{
core::rect<s32> rectCombo(0, 0, AbsoluteRect.getWidth(),AbsoluteRect.getHeight());
ComboTexture = guiEnv->addComboBox (rectCombo, this);
updateTextures(driver);
}

virtual bool OnEvent(const SEvent &event)
{
if ( event.EventType != EET_GUI_EVENT )
return false;

if ( event.GUIEvent.Caller == ComboTexture && event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED )
{
DirtyFlag = true;
}

return false;
}

// Contournement pour un problème avec les listes déroulantes.
// Nous devons obtenir le premier plan quand les listes déroulantes veulent aller au premier plan ou la liste déroulante peut être dessinée sous les autres éléments.
virtual bool bringToFront(IGUIElement* element)
{
bool result = gui::IGUIElement::bringToFront(element);
if ( Parent && element == ComboTexture )
result &= Parent->bringToFront(this);
return result;
}

// Retourne le nom de la texture sélectionnée, (si aucun retourne 0).
const wchar_t * getSelectedTextureName() const
{
s32 selected = ComboTexture->getSelected();
if ( selected < 0 )
return 0;
return ComboTexture->getItem(selected);
}

// Réinitialise le fanion sale.
void resetDirty()
{
DirtyFlag = false;
}

// Quand la texture a été changée, le fanion sale est posé.
bool isDirty() const
{
return DirtyFlag;
};

// Met les noms de toutes les textures chargées dans une liste déroulante.
void updateTextures(video::IVideoDriver * driver)
{
s32 oldSelected = ComboTexture->getSelected();
s32 selectNew = -1;
const wchar_t * oldTextureName = 0;
if ( oldSelected >= 0 )
{
oldTextureName = ComboTexture->getItem(oldSelected);
}
ComboTexture->clear();
for ( u32 i=0; i < driver->getTextureCount(); ++i )
{
video::ITexture * texture = driver->getTextureByIndex(i);
core::stringw name( texture->getName() );
ComboTexture->addItem( name.c_str() );
if ( oldTextureName && selectNew < 0 && name == oldTextureName )
selectNew = i;
}

// Ajoute un autre nom qui peut être utilisé pour effacer la texture.
ComboTexture->addItem( CLEAR_TEXTURE.c_str() );
if ( CLEAR_TEXTURE == oldTextureName )
selectNew = ComboTexture->getItemCount()-1;

if ( selectNew >= 0 )
ComboTexture->setSelected(selectNew);

DirtyFlag = true;
}

private:
bool DirtyFlag;
gui::IGUIComboBox * ComboTexture;
};

II-C - Configuration de matériel

Contrôleur qui autorise la configuration de certaines valeurs de matériel d'un MeshSceneNode.

struct SMeshNodeControl
{
// Constructeur.
SMeshNodeControl()
: Initialized(false), Driver(0), MeshManipulator(0), SceneNode(0), SceneNode2T(0), SceneNodeTangents(0)
, AllColorsControl(0), ButtonLighting(0), InfoLighting(0), ComboMaterial(0), TextureControl1(0), TextureControl2(0), ControlVertexColors(0)
{
}

// Destructeur.
virtual ~SMeshNodeControl()
{
if ( TextureControl1 )
TextureControl1->drop();
if ( TextureControl2 )
TextureControl2->drop();
if ( ControlVertexColors )
ControlVertexColors->drop();
if ( AllColorsControl )
AllColorsControl->drop();
}

void init(scene::IMeshSceneNode* node, IrrlichtDevice * device, const core::position2d<s32> & pos, const wchar_t * description)
{
if ( Initialized || !node || !device) // initialisé deux fois ou avec une donnée invalide non autorisée.
return;

Driver = device->getVideoDriver ();
gui::IGUIEnvironment* guiEnv = device->getGUIEnvironment();
scene::ISceneManager* smgr = device->getSceneManager();
MeshManipulator = smgr->getMeshManipulator();

SceneNode = node;
scene::IMeshManipulator * meshManip = smgr->getMeshManipulator();

scene::IMesh * mesh2T = meshManip->createMeshWith2TCoords(node->getMesh());
SceneNode2T = smgr->addMeshSceneNode(mesh2T, 0, -1, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
mesh2T->drop();

scene::IMesh * meshTangents = meshManip->createMeshWithTangents(node->getMesh(), false, false, false);
SceneNodeTangents = smgr->addMeshSceneNode(meshTangents, 0, -1
, SceneNode->getPosition(), SceneNode->getRotation(), SceneNode->getScale() );
meshTangents->drop();

video::SMaterial & material = SceneNode->getMaterial(0);
material.Lighting = true;
AllColorsControl = new CAllColorsControl(guiEnv, pos, description, true, guiEnv->getRootGUIElement());
AllColorsControl->setColorsToMaterialColors(material);

core::rect<s32> rectBtn(pos + core::position2d<s32>(0, 320), core::dimension2d<s32>(60, 15));
ButtonLighting = guiEnv->addButton (rectBtn, 0, -1, L"Lighting");
ButtonLighting->setIsPushButton(true);
ButtonLighting->setPressed(material.Lighting);
core::rect<s32> rectInfo( rectBtn.LowerRightCorner.X, rectBtn.UpperLeftCorner.Y, rectBtn.LowerRightCorner.X+40, rectBtn.UpperLeftCorner.Y+15 );
InfoLighting = guiEnv->addStaticText(L"", rectInfo, true, false );
InfoLighting->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER );

core::rect<s32> rectCombo(pos.X, rectBtn.LowerRightCorner.Y, pos.X+100, rectBtn.LowerRightCorner.Y+15);
ComboMaterial = guiEnv->addComboBox (rectCombo);
for ( int i=0; i <= (int)video::EMT_ONETEXTURE_BLEND; ++i )
{
ComboMaterial->addItem( core::stringw(video::sBuiltInMaterialTypeNames[i]).c_str() );
}
ComboMaterial->setSelected( (s32)material.MaterialType );

core::position2d<s32> posTex(rectCombo.UpperLeftCorner.X,rectCombo.LowerRightCorner.Y);
TextureControl1 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());
posTex.Y += 15;
TextureControl2 = new CTextureControl(guiEnv, Driver, posTex, guiEnv->getRootGUIElement());

core::position2d<s32> posVertexColors( posTex.X, posTex.Y + 15);
ControlVertexColors = new CColorControl( guiEnv, posVertexColors, L"Vertex colors", guiEnv->getRootGUIElement());

video::S3DVertex * vertices =  (video::S3DVertex *)node->getMesh()->getMeshBuffer(0)->getVertices();
if ( vertices )
{
ControlVertexColors->setColor(vertices[0].Color);
}

Initialized = true;
}

void update()
{
if ( !Initialized )
return;

video::SMaterial & material = SceneNode->getMaterial(0);
video::SMaterial & material2T = SceneNode2T->getMaterial(0);
video::SMaterial & materialTangents = SceneNodeTangents->getMaterial(0);

s32 selectedMaterial = ComboMaterial->getSelected();
if ( selectedMaterial >= (s32)video::EMT_SOLID && selectedMaterial <= (s32)video::EMT_ONETEXTURE_BLEND)
{
video::E_VERTEX_TYPE vertexType = getVertexTypeForMaterialType((video::E_MATERIAL_TYPE)selectedMaterial);
switch ( vertexType )
{
case video::EVT_STANDARD:
material.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
SceneNode->setVisible(true);
SceneNode2T->setVisible(false);
SceneNodeTangents->setVisible(false);
break;
case video::EVT_2TCOORDS:
material2T.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
SceneNode->setVisible(false);
SceneNode2T->setVisible(true);
SceneNodeTangents->setVisible(false);
break;
case video::EVT_TANGENTS:
materialTangents.MaterialType = (video::E_MATERIAL_TYPE)selectedMaterial;
SceneNode->setVisible(false);
SceneNode2T->setVisible(false);
SceneNodeTangents->setVisible(true);
break;
}
}

updateMaterial(material);
updateMaterial(material2T);
updateMaterial(materialTangents);

if ( ButtonLighting->isPressed() )
InfoLighting->setText(L"on");
else
InfoLighting->setText(L"off");

AllColorsControl->resetDirty();
TextureControl1->resetDirty();
TextureControl2->resetDirty();
ControlVertexColors->resetDirty();
}

void updateTextures()
{
TextureControl1->updateTextures(Driver);
TextureControl2->updateTextures(Driver);
}

protected:

void updateMaterial(video::SMaterial & material)
{
AllColorsControl->updateMaterialColors(material);
material.Lighting = ButtonLighting->isPressed();
if ( TextureControl1->isDirty() )
{
material.TextureLayer[0].Texture = Driver->getTexture( io::path(TextureControl1->getSelectedTextureName()) );
}
if ( TextureControl2->isDirty() )
{
material.TextureLayer[1].Texture = Driver->getTexture( io::path(TextureControl2->getSelectedTextureName()) );
}
if ( ControlVertexColors->isDirty() )
{
MeshManipulator->setVertexColors (SceneNode->getMesh(), ControlVertexColors->getColor());
MeshManipulator->setVertexColors (SceneNode2T->getMesh(), ControlVertexColors->getColor());
MeshManipulator->setVertexColors (SceneNodeTangents->getMesh(), ControlVertexColors->getColor());
}
}

bool Initialized;
video::IVideoDriver *       Driver;
scene::IMeshManipulator*    MeshManipulator;
scene::IMeshSceneNode*      SceneNode;
scene::IMeshSceneNode*      SceneNode2T;
scene::IMeshSceneNode*      SceneNodeTangents;
CAllColorsControl*          AllColorsControl;
gui::IGUIButton *           ButtonLighting;
gui::IGUIStaticText*        InfoLighting;
gui::IGUIComboBox *         ComboMaterial;
CTextureControl*            TextureControl1;
CTextureControl*            TextureControl2;
CColorControl*              ControlVertexColors;
};

II-D - Configuration du gestionnaire de lumières

Contrôleur pour autoriser la configuration des valeurs des couleurs d'un LightSceneNode.

struct SLightNodeControl
{
// Constructeur.
SLightNodeControl() : Initialized(false), SceneNode(0), AllColorsControl(0)
{
}

virtual ~SLightNodeControl()
{
if ( AllColorsControl )
AllColorsControl->drop();
}

void init(scene::ILightSceneNode* node, gui::IGUIEnvironment* guiEnv, const core::position2d<s32> & pos, const wchar_t * description)
{
if ( Initialized || !node || !guiEnv) // initializing twice or with invalid data not allowed
return;
SceneNode = node;
AllColorsControl = new CAllColorsControl(guiEnv, pos, description, false, guiEnv->getRootGUIElement());
const video::SLight & lightData = SceneNode->getLightData();
AllColorsControl->setColorsToLightDataColors(lightData);
Initialized = true;
}

void update()
{
if ( !Initialized )
return;

video::SLight & lightData = SceneNode->getLightData();
AllColorsControl->updateLightColors(lightData);
}

protected:
bool Initialized;
scene::ILightSceneNode* SceneNode;
CAllColorsControl* AllColorsControl;
};

III - Configuration de l'application

Configuration de l'application.

struct SConfig
{
SConfig()
: RenderInBackground(true)
, DriverType(video::EDT_BURNINGSVIDEO)
, ScreenSize(640, 480)
{
}

bool RenderInBackground;
video::E_DRIVER_TYPE DriverType;
core::dimension2d<u32> ScreenSize;
};

IV - Classe principale

Classe principale de l'application.

class CApp : public IEventReceiver
{
friend int main(int argc, char *argv[]);

public:
// Constructeur.
CApp()
: IsRunning(false)
, Device(0)
, Camera(0)
, GlobalAmbient(0)
{
}

// Destructeur.
~CApp()
{
}

// Arrête l'exécution – quittera à la fin de la boucle principale.
void stopApp()
{
IsRunning = false;
}

// Gestionnaire d'événements.
virtual bool OnEvent(const SEvent &event)
{
if (event.EventType == EET_GUI_EVENT)
{
gui::IGUIEnvironment* env = Device->getGUIEnvironment();

switch(event.GUIEvent.EventType)
{
case gui::EGET_MENU_ITEM_SELECTED:
{
gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)event.GUIEvent.Caller;
s32 id = menu->getItemCommandId(menu->getSelectedItem());

switch(id)
{
case GUI_ID_OPEN_TEXTURE: // File -> Open Texture
env->addFileOpenDialog(L"Please select a texture file to open");
break;
case GUI_ID_QUIT: // File -> Quit
stopApp();
break;
}
}
break;

case gui::EGET_FILE_SELECTED:
{
// load the model file, selected in the file open dialog
gui::IGUIFileOpenDialog* dialog =
(gui::IGUIFileOpenDialog*)event.GUIEvent.Caller;
loadTexture(io::path(dialog->getFileName()).c_str());
}
break;

default:
break;
}
}

return false;
}

protected:

// Initialisation de l'application.
// Retourne true quand elle a été correctement initialisée, sinon retourne false.
bool init(int argc, char *argv[])
{
// Demande à l'utilisateur un pilote.
Config.DriverType=driverChoiceConsole();
if (Config.DriverType==video::EDT_COUNT)
return false;

// Crée le moteur avec les options issues de notre configuration.
Device = createDevice(Config.DriverType, Config.ScreenSize);
if (!Device)
return false;
Device->setWindowCaption( DriverTypeNames[Config.DriverType] );
Device->setEventReceiver(this);

scene::ISceneManager* smgr = Device->getSceneManager();
video::IVideoDriver * driver = Device->getVideoDriver ();
gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();

// Met une jolie police.
gui::IGUISkin* skin = guiEnv->getSkin();
gui::IGUIFont* font = guiEnv->getFont("../../media/fonthaettenschweiler.bmp");
if (font)
skin->setFont(font);

// Supprime quelques valeurs alpha rendant les menus plus difficiles à lire.
video::SColor col3dHighLight( skin->getColor(gui::EGDC_APP_WORKSPACE) );
col3dHighLight.setAlpha(255);
video::SColor colHighLight( col3dHighLight );
skin->setColor(gui::EGDC_HIGH_LIGHT, colHighLight );
skin->setColor(gui::EGDC_3D_HIGH_LIGHT, col3dHighLight );

// Ajoute quelques textures qui sont utiles pour tester les options des matériels.
createDefaultTextures(driver);

// Crée un menu.
gui::IGUIContextMenu * menuBar = guiEnv->addMenu();
menuBar->addItem(L"File", -1, true, true);

gui::IGUIContextMenu* subMenuFile = menuBar->getSubMenu(0);
subMenuFile->addItem(L"Open texture ...", GUI_ID_OPEN_TEXTURE);
subMenuFile->addSeparator();
subMenuFile->addItem(L"Quit", GUI_ID_QUIT);

// Une caméra statique.
Camera = smgr->addCameraSceneNode (0, core::vector3df(0, 0, 0),
core::vector3df(0, 0, 100),
-1);

// Ajoute les nœuds qui sont utilisés pour montrer les matériels.
scene::IMeshSceneNode* nodeL = smgr->addCubeSceneNode (30.0f, 0, -1,
core::vector3df(-35, 0, 100),
core::vector3df(0, 0, 0),
core::vector3df(1.0f, 1.0f, 1.0f));
NodeLeft.init( nodeL, Device, core::position2d<s32>(10,20), L"left node" );

scene::IMeshSceneNode* nodeR = smgr->addCubeSceneNode (30.0f, 0, -1,
core::vector3df(35, 0, 100),
core::vector3df(0, 0, 0),
core::vector3df(1.0f, 1.0f, 1.0f));
NodeRight.init( nodeR, Device, core::position2d<s32>(530,20), L"right node" );

// Ajoute une lumière.
scene::ILightSceneNode* nodeLight = smgr->addLightSceneNode(0, core::vector3df(0, 0, 0),
video::SColorf(1.0f, 1.0f, 1.0f),
100.0f);
LightControl.init(nodeLight, guiEnv, core::position2d<s32>(270,20), L"light" );

// Un large cube englobant le tout. C'est principalement pour rendre la lumière plus évidente.
scene::IMeshSceneNode* backgroundCube = smgr->addCubeSceneNode (200.0f, 0, -1, core::vector3df(0, 0, 0),
core::vector3df(45, 0, 0),
core::vector3df(1.0f, 1.0f, 1.0f));
backgroundCube->getMaterial(0).BackfaceCulling = false;         // Nous sommes à l'intérieur du cube donc nous devons désactiver le « backface culling » pour le voir.
backgroundCube->getMaterial(0).EmissiveColor.set(255,50,50,50); // Nous conservons quelques lumières pour garder les textes visibles.

// Met la valeur de la lumière ambiante.
GlobalAmbient = new CColorControl( guiEnv, core::position2d<s32>(270, 300), L"global ambient", guiEnv->getRootGUIElement());
GlobalAmbient->setColor( smgr->getAmbientLight().toSColor() );

return true;
}

// Met à jour une image.
bool update()
{
using namespace irr;

video::IVideoDriver* videoDriver =  Device->getVideoDriver();
if ( !Device->run() )
return false;

if ( Device->isWindowActive() || Config.RenderInBackground )
{
gui::IGUIEnvironment* guiEnv = Device->getGUIEnvironment();
scene::ISceneManager* smgr = Device->getSceneManager();
gui::IGUISkin * skin = guiEnv->getSkin();

// Met à jour nos contrôleurs.
NodeLeft.update();
NodeRight.update();
LightControl.update();

// Met à jour la configuration de la lumière ambiante.
if ( GlobalAmbient->isDirty() )
{
smgr->setAmbientLight( GlobalAmbient->getColor() );
GlobalAmbient->resetDirty();
}

// Dessine tout.
video::SColor bkColor( skin->getColor(gui::EGDC_APP_WORKSPACE) );
videoDriver->beginScene(true, true, bkColor);

smgr->drawAll();
guiEnv->drawAll();

videoDriver->endScene();
}

return true;
}

// Exécute l'application. Notre boucle principale.
void run()
{
IsRunning = true;

if ( !Device )
return;

// Boucle principale de l'application.
while(IsRunning)
{
if ( !update() )
break;

Device->sleep( 5 );
}
}

// Ferme l'application.
void quit()
{
IsRunning = false;
GlobalAmbient->drop();
GlobalAmbient = NULL;
if ( Device )
{
Device->closeDevice();
Device->drop();
Device = NULL;
}
}

// Crée quelques textures utiles.
// Notez que la fonction peut privilégier la lisibilité sur la vitesse de rendu, vous ne devez pas utiliser setPixel pendant l'exécution mais à l'initialisation.
void createDefaultTextures(video::IVideoDriver * driver)
{
const u32 width = 256;
const u32 height = 256;
video::IImage * imageA8R8G8B8 = driver->createImage (video::ECF_A8R8G8B8, core::dimension2d<u32>(width, height));
if ( !imageA8R8G8B8 )
return;
const u32 pitch = imageA8R8G8B8->getPitch();

// Quelques modèles carrés sympathiques avec neuf couleurs typiques.
for ( u32 y = 0; y < height; ++ y )
{
for ( u32 x = 0; x < pitch; ++x )
{
if ( y < height/3 )
{
if ( x < width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_BLACK);
else if ( x < 2*width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_BLUE);
else
imageA8R8G8B8->setPixel (x, y, SCOL_CYAN);
}
else if ( y < 2*height/3 )
{
if ( x < width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_GRAY);
else if ( x < 2*width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_GREEN);
else
imageA8R8G8B8->setPixel (x, y, SCOL_MAGENTA);
}
else
{
if ( x < width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_RED);
else if ( x < 2*width/3 )
imageA8R8G8B8->setPixel (x, y, SCOL_YELLOW);
else
imageA8R8G8B8->setPixel (x, y, SCOL_WHITE);
}
}
}
driver->addTexture (io::path("CARO_A8R8G8B8"), imageA8R8G8B8);

// Tout blanc.
imageA8R8G8B8->fill(SCOL_WHITE);
driver->addTexture (io::path("WHITE_A8R8G8B8"), imageA8R8G8B8);

// Tout noir.
imageA8R8G8B8->fill(SCOL_BLACK);
driver->addTexture (io::path("BLACK_A8R8G8B8"), imageA8R8G8B8);

// Nuances de gris.
for ( u32 y = 0; y < height; ++ y )
{
for ( u32 x = 0; x < pitch; ++x )
{
imageA8R8G8B8->setPixel (x, y, video::SColor(y, x,x,x) );
}
}
driver->addTexture (io::path("GRAYSCALE_A8R8G8B8"), imageA8R8G8B8);

imageA8R8G8B8->drop();
}

// Charge la texture et s'assure que les nœuds savent lorsque d'autres textures sont disponibles.
void loadTexture(const io::path &name)
{
Device->getVideoDriver()->getTexture(name);
NodeLeft.updateTextures();
NodeRight.updateTextures();
}

private:
SConfig                     Config;
volatile bool               IsRunning;
IrrlichtDevice *            Device;
scene::ICameraSceneNode *   Camera;
SMeshNodeControl            NodeLeft;
SMeshNodeControl            NodeRight;
SLightNodeControl           LightControl;
CColorControl *             GlobalAmbient;
};

V - Main

Un main vraiment court comme nous avons tout fait dans les classes.

int main(int argc, char *argv[])
{
CApp APP;

if ( !APP.init(argc, argv) )
{
printf("init failed\n");
return 1;
}

APP.run();
APP.quit();

return 0;
}

VI - Conclusion

Vous pouvez désormais visualiser des matériels, tester leurs options et regarder les résultats.

Dans le prochain tutoriel SMeshBufferHandling, nous verrons comment créer des meshs personnalisés et les utiliser avec Irrlicht.

VII - Remerciements

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

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