Aller au menu - Aller au contenu

Les images


Informations sur le tutoriel

Avatar
Auteur : Kevin Leonhart
Visualisations : 8 591
Licence : Creative Commons BY-SA


Plus d'informations Plus d'informations

Historique des mises à jour

  • Le 16/11/2009 à 02:33:53
    Création de la partie 4. Ajout du chapitre "le driver vidéo".
  • Le 13/11/2009 à 15:32:14
    Ajout du chapitre "des caméras et des animators"
  • Le 09/11/2009 à 02:04:36
    Ajout du chapitre "introduction aux shaders"
Comme vous le savez, Irrlicht est un moteur 3D (comment ça vous ne le savez pas ? :p ). Mais ça ne veut pas dire pour autant qu'il est limité à gérer des objets en 3 dimensions. Ce chapitre a pour but de vous présenter une de ses fonctionnalités 2D : l'affichage d'images. Et nous verrons au passage quelques types de variables un peu spéciales utilisées par le moteur.
Chapitre précédent Sommaire Chapitre suivant

Récupérer une image

La première chose à faire est de mettre la main sur une image à afficher. Histoire de donner dans l'originalité, on va aller faire un tour dans le SDK (le dossier irrlicht-1.5.1 pour la version 1.5.1).

Toujours dans le dossier media, il y a un fichier qui s'appelle "2ddemo.bmp". D'ailleurs il n'est pas placé là par hasard. Si vous êtes un peu curieux vous vous apercevrez que tous les fichiers dans le dossier media sont utilisés pour faire tourner des applications de démo correspondantes aux tutos officiels d'Irrlicht. ;)

Une fois que vous l'avez, copiez-le à un endroit pas trop loin de votre exécutable et commençons. Pour démarrer, rien d'inhabituel : on crée un device, un driver, et un scene manager. Entamons tout de suite les hostilités avec le type de variable qui permet de stocker un fichier image :

Code : C++
1
irr::video::ITexture *image;

C'est ce pointeur qui va contenir l'adresse de notre image. Maintenant récupérons ladite image :

Code : C++
1
driver->getTexture ("le_path_du_fichier");

Irrlicht peut gérer beaucoup de formats de fichiers image différents :
BMP, JPG, TGA, PCX, PNG, etc...



Soyez maintenant attentifs, ça devient intéressant. Vous l'aurez remarqué au nom, la fonction qui permet de charger une image permet en réalité de charger une texture. Ce qui veut strictement dire la même chose. Après tout, la 2D n'est qu'un cas particulier de la 3D. Comme on dit, qui peut le plus peut le moins. Et donc, Irrlicht qui peut gérer des textures dans un espace en 3 dimensions peut tout naturellement gérer des textures dans un espace en 2 dimensions. Textures auxquelles on donne alors le nom d'images ou encore de sprites selon les cas.

Il est intéressant de savoir qu'il existe des tas d'options paramétrables pour le chargement d'une texture. Avant de les passer en revue, voyons l'instruction qui permet de modifier ces options :

Code : C++
1
driver->setTextureCreationFlag (irr::video::ETCF_ALWAYS_16_BIT, true);

Analysons ces deux paramètres. Le premier est une valeur d'énumération, elle représente justement ce qui est paramétrable. Les différentes valeurs possibles sont (je ne remets pas le irr::video:: à chaque fois) les suivantes.
  • ETCF_ALWAYS_16_BIT permet de ne créer que des textures de 16 bits par pixels. Plus rapides que les 32 bits, et plus petites en mémoire aussi.
  • ETCF_ALWAYS_32_BIT permet de ne créer que des textures de 32 bits par pixel.
  • ETCF_OPTIMIZED_FOR_QUALITY permet d'optimiser pour la qualité. Donc le plus souvent les formats d'origine sont laissés tels quels. En effet ça n'a pas grand intérêt de convertir une 16 bits en 32. les détails manquants ne vont pas apparaître comme par magie...
  • ETCF_OPTIMIZED_FOR_SPEED permet d'optimiser pour la vitesse.
  • ETCF_CREATE_MIP_MAPS permet au driver de créer automatiquement des niveaux de MIP map sur la texture.

Quant au deuxième paramètre, il s'agit d'un booléen tout ce qu'il y a de plus classique. Il suffit de le mettre à true pour activer ce qui est passé en premier paramètre. Une petite remarque avant d'aller plus loin :

Vous pouvez tout à fait ne rien activer du tout. Auquel cas le driver choisira lui même ce qui lui semble le plus adapté. Et c'est son boulot d'ailleurs, il le fait très bien. ;)

Les variables made in Irrlicht

Nous allons maintenant devoir faire un petit interlude musical pour voir les différents types de variables utilisés dans Irrlicht. J'avoue que si je coupe le chapitre en plein milieu c'est parce que nous en aurons besoin pour la deuxième partie sur les images. Mais bon de toute façon il fallait bien caser ça quelque part. Pourquoi pas là ? ^^

Je vous préviens tout de suite, pas la peine de rêver. Je ne vais pas vous faire une liste exhaustive de toutes les structures et classes d'Irrlicht ! Il y a un formidable outil pour ça qui s'appelle la doc. ;)



irr::core::dimension2d< T >


Vous l'aurez reconnu, il y a un template qui traîne par là. Ce qui veut dire que dans le code cette classe s'utilisera comme ceci :

Code : C++
1
irr::core::dimension2d<irr::s32> taille;


Ce qu'il faut retenir ici, c'est le irr::s32. Il s'agit tout bêtement d'une variable de 32 bits signée. Il existe plein d'autres types de variables de base de ce genre propres à Irrlicht, et comme il serait long, ennuyeux et inutile que je vous les énumère tous, voici un lien vers la page de la documentation qui le fait.

Revenons à nos moutons. Comme son nom l'indique, cette classe contient les dimensions d'une surface 2D. Elle contient précisément deux attributs appelés Height et Width (en français ça donne hauteur et largeur). Au niveau de l'utilisation, c'est donc simplissime. Imaginons par exemple que ma variable "taille" de tout à l'heure contienne les dimensions d'une image, pour récupérer la hauteur il suffit de faire :

Code : C++
1
irr::s32 hauteur = taille.Height;

Et voilà, on vient de stocker la hauteur de l'image dans une variable de type irr::s32.



irr::core::position2d< T >


C'est quasiment la même à peu de choses près. Ici aussi il n'y a que deux attributs :
  • La position en x notée X.
  • La position en y notée Y.

Donc au niveau du code, cela nous donne :

Code : C++
1
2
3
irr::core::position2d<irr::s32> position;
position.X = 50;
position.Y = 50;

Et voilà, on vient de créer une instance contenant les positions 50 (pixels) sur les 2 axes.



irr::core::rect< T >


Celle-ci est un poil plus complexe que les 2 autres. Notamment car ses attributs sont eux-mêmes des classes. Mais bonne nouvelle, des classes que nous venons juste d'étudier (comme le hasard fait bien les choses). En réalité, irr::core::rect<T> permet de définir les coordonnées d'un rectangle. Et pour ce faire, il suffit de connaître les coordonnées de 2 de ses coins symétriques par le point central.

Un petit schéma :
Image utilisateur
On donne les coordonnées du coin haut gauche et celle du coin bas droit. Et devinez quoi... pour définir ces coordonnées, on utilise irr::core::position2d<T>.

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// On cree les positions 2D des deux coins
irr::core::position2d<irr::s32> position0;
position0.X = 0;
position0.Y = 0;
irr::core::position2d<irr::s32> position1;
position1.X = 300;
position1.Y = 300;
 
// Puis on les associe aux coins d'un rectangle
irr::core::rect<irr::s32> rectangle;
rectangle.UpperLeftCorner = position0;
rectangle.LowerRightCorner = position1;

Vous l'aurez remarqué, les deux attributs de core::rect<T> sont :
  • UpperLeftCorner pour le coin en haut à gauche
  • LowerRightCorner pour le coin en bas à droite

Afficher une image

Maintenant que nous savons comment stocker une image et que nous sommes au point sur les types de variables pouvant nous être utiles, il ne nous reste plus qu'à l'afficher. Voici la ligne de code à placer dans la boucle de rendu pour se faire :

Code : C++
1
2
3
driver->draw2DImage(image, irr::core::position2d<irr::s32>(20,20),
                    irr::core::rect<irr::s32>(0,0,300,300), 0,
                    irr::video::SColor(255, 255, 255, 255), true);


Beaucoup de paramètres, n'est-ce pas ? C'est parce qu'il y a beaucoup de choses à définir.
  • Le premier désigne l'image à afficher.
  • Le deuxième est la position du coin haut gauche de l'image par rapport à l'origine : le coin haut gauche de la fenêtre
  • Le troisième désigne la partie de l'image de base que l'on veut dessiner.
  • Le quatrième permet de n'afficher qu'une partie du rectangle qu'on a sélectionné au paramètre précédent. Pour 0, tout le rectangle sera affiché
  • Le cinquième représente la couleur avec laquelle l'image sera altérée
  • Le sixième et dernier va nous permettre de ne rendre transparentes que certaines parties de l'image

Nous allons maintenant avoir besoin de ce qu'on vient d'étudier plus haut. Vous aurez remarqué que certains paramètres sont du même type de ceux que nous venons de voir. Intéressons-nous au troisième, ce qui me donne l'occasion de faire une remarque :

Si la taille du rectangle passée en paramètre dépasse la taille de l'image à afficher, alors celle-ci sera répétée pour combler le vide. En prenant le coin haut gauche du rectangle comme origine.



Voilà le code complet qui récapitule tout ce qu'on a vu depuis le début du chapitre :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <IRR/irrlicht.h>
 
 
int main(void)
{
 
    irr::IrrlichtDevice* device = irr::createDevice(irr::video::EDT_OPENGL,
        irr::core::dimension2d<irr::u32>(640,480), 32);
    irr::video::IVideoDriver* driver = device->getVideoDriver ();
    irr::scene::ISceneManager *sceneManager = device->getSceneManager ();
 
    irr::video::ITexture *image = driver->getTexture ("2ddemo.bmp");  // chargement de la texture
    
    while(device->run ())                                             // la boucle de rendu
    {
        driver->beginScene(true, true,
            irr::video::SColor (0,120,120,120));
        driver->draw2DImage(image,                                    // dessin de l'image
            irr::core::position2d<irr::s32>(20,20),
            irr::core::rect<irr::s32>(0,0,300,300),
            0,
            irr::video::SColor (255,255,255,255),
            true);
        driver->endScene ();
    }
 
    device->drop ();
    return 0;
}

Image utilisateur

Alors, ça pique un peu non ? >_< Faisons abstraction de la couleur un instant pour remarquer quelque chose. L'image s'est répétée en hauteur. Vous pouvez voir qu'en bas on retrouve la même chose qu'en haut. Pour éviter d'avoir à regarder quelle est pile-poil la hauteur en pixels d'une image pour la passer en paramètre, on peut comme tout à l'heure, la récupérer dans une variable et s'en servir.

Pour récupérer les dimensions d'une image, rien de plus simple. Il existe une fonction juste pour ça : irr::video::ITexture::getSize(). Qui renvoie une variable de type irr::core::dimension2d<s32>.


Maintenant soyez attentifs le code qui suit est un méga-mix de tout ce qu'on vu dit avant :

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <IRR/irrlicht.h>

 
int main(void)
{
    irr::IrrlichtDevice* device = irr::createDevice(
        irr::video::EDT_OPENGL,
        irr::core::dimension2d<u32>(640,480),32);
    irr::video::IVideoDriver* driver = device->getVideoDriver ();
    irr::scene::ISceneManager *sceneManager = device->getSceneManager ();
 
    irr::video::ITexture *image = driver->getTexture("2ddemo.bmp");  // chargement image
    irr::core::dimension2d<irr::s32> taille = image->getSize ();     // recuperation dimensions image
 
    irr::core::position2d<irr::s32> position0;                        // creation position origine
    position0.X = 0;
    position0.Y = 0;
    irr::core::position2d<irr::s32> position1;                        // creation position coin
    position1.X = taille.Width;                                       // bas droit du rectangle
    position1.Y = taille.Height;
    irr::core::rect<irr::s32> rectangle;                              // creation rectangle
    rectangle.UpperLeftCorner = position0;                            // contenant l'image
    rectangle.LowerRightCorner = position1;
 
    while(device->run ())                                             // boucle de rendu
    {
      driver->beginScene(true, true,
          irr::video::SColor (0,120,120,120));
      driver->draw2DImage(image,                                      // dessin de l'image
          position0,                                                  // a la position origine
          rectangle,                                                  // dans le rectangle cree au dessus
          0,
          irr::video::SColor (255,255,255,255),
          true);
       driver->endScene ();
    }
    device->drop ();
    return 0;
}

Image utilisateur

Comme vous pouvez le voir (sur le screenshot de droite, si ce n'est sur votre écran), c'est déjà un peu mieux. On vient d'afficher l'image dans sa totalité. Vous pouvez d'ailleurs ouvrir le fichier "manuellement" pour constater qu'on a exactement le même. Vous l'aurez sans doute deviné, cette image sert à faire des tests (ça tombe bien :) ). Il y a deux bestioles sur le coté et le logo Irrlicht juste en dessous.

La transparence

La première chose à faire est de faire en sorte que cette vilaine couleur de fond rose ne vienne plus nous agresser les pupilles. Nous allons donc rendre transparente cette couleur précisément.

Pour ce faire 2 méthodes :



La première


Il s'agit d'indiquer à Irrlicht la couleur à rendre transparente par ses composantes :

Code : C++
1
driver->makeColorKeyTexture (image, irr::video::SColor (0,255,255,255));

Le premier paramètre correspond à l'image qui contient la couleur qui va devenir transparente. Le deuxième représente les composantes de cette couleur. A noter que le premier nombre est la composante alpha, et que dans notre cas elle est inutile, que vous la mettiez à 0 ou à 255, le résultat sera le même. Si on reprend cette ligne, on s'aperçoit donc qu'elle va rendre transparents tous les pixels qui sont blancs.



La deuxième


Contrairement à la première méthode, nous n'allons pas indiquer directement les composantes de la couleur. L'idée est de donner les coordonnées d'un pixel et tous les pixels partageant la couleur de celui-ci seront transparents.

Code : C++
1
driver->makeColorKeyTexture (image, irr::core::position2d<s32> (0,0));

Et voilà. Le premier paramètre indique toujours l'image sur laquelle on travaille, et le deuxième indique les coordonnées 2D du pixel sur l'image. Donc cette ligne permet de rendre transparents tous les pixels qui ont la même couleur que celui à l'origine de l'image, le point le plus en haut à gauche.



Le rendu


Je crois que nous avons maintenant tout ce qu'il nous faut pour faire un truc sympa. :) Nous allons maintenant voir un code où la couleur de fond est transparente et où des parties de l'image vont être collées sur d'autres. Restez concentrés, rien de nouveau mais une mise en pratique de tout ce qu'on a appris dans ce chapitre.

Code : C++
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <IRR/irrlicht.h>
  
int main(void)
{
 
    irr::IrrlichtDevice* device = irr::createDevice(
        irr::video::EDT_OPENGL,
        irr::core::dimension2d<irr::u32>(640,480),
        32,false,false,false);
    irr::video::IVideoDriver* driver = device->getVideoDriver ();
    irr::scene::ISceneManager *sceneManager = device->getSceneManager ();
 
    irr::video::ITexture *image = driver->getTexture("2ddemo.bmp");    // chargement de l'image
    driver->makeColorKeyTexture (image,                                // transparence pour le fond
        irr::core::position2d<irr::s32> (0,0));       

    irr::video::SColor blanc;                                          // creation variable couleur blanche
    blanc.set(255,255,255,255);
 
    while(device->run ())                                              // la boucle de rendu
    {
        driver->beginScene(true, true,
            irr::video::SColor (0,120,120,120));
        driver->draw2DImage(image,                                     // dessin de la salle
            irr::core::position2d<irr::s32> (20,20),
            irr::core::rect<irr::s32> (0,0,342,224),
            0, blanc, true);
        driver->draw2DImage (image,                                    // dessin du premier truc rouge
            irr::core::position2d<irr::s32> (140,140),
            irr::core::rect<irr::s32> (349,15,385,78),
            0, blanc, true);
        driver->draw2DImage (image,                                    // dessin du deuxième truc rouge
            irr::core::position2d<irr::s32> (150,20),
            irr::core::rect<irr::s32> (387,15,423,78),
            0, blanc, true);
         driver->endScene ();
    }
 
    device->drop ();
    return 0;
}

Image utilisateur

Et voilà le travail ! Vous pouvez cliquer sur l'image de droite pour admirer le résultat de plus près.

Bon ok, j'avais dit rien de nouveau et finalement on a utilisé une variable video::SColor qu'on n'avait pas vue... Prenez le code comme argent comptant, on reviendra peut-être dessus dans un prochain chapitre. ;)

Et bien, nous étions partis pour faire un petit chapitre facile et voilà le résultat. ^^ Je suis sûr que vous avez appris plein de trucs, non ?

Si vous aimez les jeux 2D façon Old School, sachez qu'il est tout à fait possible d'en réaliser avec Irrlicht. Même si à la base ce n'est pas forcément prévu pour ça. Pour ceux qui seraient intéressés, voilà un site qui devrait vous aider. Il n'explique pas comment faire avec Irrlicht précisément, mais de manière générale tous les concepts pour faire des jeux 2D à base de tiles. Enjoy ! :D
Chapitre précédent Sommaire Chapitre suivant

Informations sur le tutoriel

Retour en haut Retour en haut

Créé : Le 19/01/2007 à 19:59:56
Modifié : Le 16/11/2009 à 02:33:52
Avancement : 100%

L'orthographe, la grammaire et la présentation de ce tutoriel ont été vérifiées par les zCorrecteurs.
7 commentaires