begin process at 2008 08 21 21:25:31
1 229 662 membres
473 nouveaux aujourd'hui
14 265 membres club

Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum.
Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

OPENGL-GLUT: CRÉATION D'UNE FENETRE ET ROTATION D'UNE PYRAMIDE 3D


Information sur le tutorial

Catégorie :OpenGL Date de création : 28/03/2006 21:36:41 Vu : 16 945 fois

Note :
9 / 10 - par 1 personne
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (5)
Ajouter un commentaire et/ou une note

Description

Ce tutorial part du début: si vous n'avez jamais fait d'opengl et que vous êtes intéressé, il peut être intéressant pour vous!!
Pour avoir le code du projet, regardez la source suivante: http://www.cppfrance.com/code.aspx?ID=36790

Bonne continuation!!!

Tutorial

Tuto 1 : principes de GLUT

 

 

Si vous n’avez jamais fait encored’opengl sur Visual Studio.NET, lancez ce wizard fait par NeHe pour qu’ilinstalle tout le nécessaire : http://nehe.gamedev.net/counter.asp?file=files/appwizards/nehe_opengl_appwizard_vs_net.zip

 

Puis, mettez le fichier de GLUT(glut.h) que vous pouvez trouver ici : http://www.xmission.com/~nate/glut/glut-3.7.6-bin.zip dans le dossier Program Files\MicrosoftVisual Studio 8\VC\PlatformSDK\Include\gl\ et vous serez capables de programmeren glut sur visual studio !!!

Lancez Visual studio et créez unnouveau projet (empty project) ou ouvrez le projet que je vous envoie.

Si votre projet est nouveau,allez dans les options du projet et dans configuration properties/linker/input,tapez : glu32.lib opengl32.lib glut32.lib dans additional dependencies.Toujours dans linker, allez dans debugging et dans ‘generate debug info’,choisissez Yes(\DEBUG) ettout en bas, choisissez : Runtime tracking anddisable optimizations (/ASSEMBLYDEBUG). Si vous ne voulez pas que la console s’affichelorsque vous lancez votre programme, ajoutez la ligne : /SUBSYSTEM:WINDOWS/ENTRY:mainCRTStartup   dans ‘commandline’ de ‘linker’.

 

La première étape consiste en ladéclaration et implémentation de main(), qui se placera à la fin du code, ouaprès la déclaration des prototypes. Avec GLUT, il faut utiliser un main()normal et non pas WinMain(), contrairement à l’opengl normal sous windows.Analysons cette fonction :

 

int main(int arg, char **argc)

{

      //ici, on vainitialiser les propriétés de la fenetre que l'on va créer

      //commen on vaafficher (le '|' permet d'utiliser plusieurs éléments dans

      //un seulargument grace au code binaire: chaque élément a le '1' dans une rangéé,

      //et avec '|'(or), plusieurs rangées auront des 1 pour avoir plusieurs fonctionnalités

      glutInitDisplayMode( GLUT_DOUBLE |GLUT_DEPTH | GLUT_RGBA | GLUT_MULTISAMPLE );

      //ici, ondéfinit les coordonnées de la position de la fenetre dans l'écran

    glutInitWindowPosition( 0, 0 );

      //ici, la taillede la fenetre

    glutInitWindowSize( 800, 600 );

     

      //ici oninitialize le tout par rapport aux ressources (args) et on crée la fenetre

      glutInit(&arg,argc);

      glutCreateWindow("première fenetre glut");

     

      //ici, on appelle la fonction InitGL(), que l'on va implémenter plus tard

      // et qui vacontenir toutes les initialisations de l'opengl: emplacement des textures,

      //des 'lights',du 'blending' etc...

      InitGL();

     

      //ici, on dit àglut quelles fonction il devra utiliser dans la boucle principale (glutMainLoop())

      //en mettantcomme argument le pointeur de la fonction écrite. Elles seront appelées dans laboucle

      //principalequand il y en aura besoin

      glutDisplayFunc(DrawGLScene); //fonctiond'affichage: c'est le code principal de votre programme

      glutReshapeFunc(glutResize); //appelée en cas de'resize' de la fenetre

      glutKeyboardFunc(keydown);         //appeléequand l'utilisateur appuie sur une touche normale

      glutKeyboardUpFunc(keyup);         //relache latouche normale

      glutSpecialFunc(specdown);         //appuie surune touche spéciale (F1, flèches...)

      glutSpecialUpFunc(specup);         //relache latouche spéciale

 

      glutMainLoop();                    //boucle du jeu

 

      return 1;

}

 

 

Maintenant, on va regarder le code de InitGL(), qui est déjàassez commentée ;). Cette fonction lancée au début du programme sert àinitialiser et régler tous les paramètres opengl que l’on va utiliser pendantle programme. Par exemple, si on veut mettre en place une lumière, ou le alphablending, effacer l’écran, mettre en place une display list etc.

Les fonctions de couleurs comme par exemple glClearColor()et glColor3f() utilisent comme arguments la quantité de chaque couleur :le rouge, le vert et le bleu (RGB) et parfois RGBA où le A correspond au niveaualpha (la transparence).

glColor3f(floatred, float green, float blue) ;

les arguments de cette fonction sont des ‘float’ qui vont de0.0f à 1.0f où 0.0f est le plus foncé et 1.0f le plus clair. Pour ne pasgaspiller du temps à faire des conversions implicites, on met directement le‘.0’ qui différencie des ‘int’ et le ‘f’ qui différencie des ‘double’. Enchangeant les arguments de glClearColor(), vous pouvez vous amuser à changer lacouleur de le fenêtre !

 

 

int InitGL(GLvoid)                                                          // All Setup For OpenGL Goes Here

{

      //glShadeModel(GL_SMOOTH);                                       //Enable Smooth Shading

      //glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                    // Black Background

      //glClearDepth(1.0f);                                                  //Depth Buffer Setup

      //glEnable(GL_DEPTH_TEST);                                       //Enables Depth Testing

      //glDepthFunc(GL_LEQUAL);                                              //The Type Of Depth Testing To Do

      //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations

      glMatrixMode(GL_PROJECTION);

   // Set the persepctive. View angel 45, aspect ration 1, near plance 1

   // and the far clipping plane at 200.  The last two mean to start drawing

   // from 1 unit in front of you up to 200 units far.

  gluPerspective(45, 1.3, 0.1, 200.0);

 

   // Change to the modelview matrix.

   glMatrixMode(GL_MODELVIEW);

   glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //Cette fonction permetd'effacer

                                         //l'écran avec la couleur spécifiée

 

 

      returnTRUE;                                                           // Initialization Went OK

}

 

L’utilisation d’un type de retour ‘int’ peut être utile pourvérifier le fonctionnement de cette fonction : dans main, on pourraitmettre :

if ( !InitGL()){

      MessageBox(...) ;  //pour dire où est le problème

      return 0 ;        //sortir de main() et l’exécution du programme

}

 

glutResize() est une sorte de InitGL, qui remet en place leseffets, la perspective etc. lorsqu’on modifie les dimensions de la fenêtre.

 

void glutResize(int width, int height)

{

      glViewport(0,0, width, height);

      glMatrixMode(GL_PROJECTION);

      glLoadIdentity();

      /* modify this line to change perspective values */

      gluPerspective(45.0,(float)width/(float)height,1.0, 300.0);

      glMatrixMode(GL_MODELVIEW);

      glLoadIdentity();

}

 

Maintenant, nous allons passer aux autres fonctionsCALLBACK, c'est-à-dire les fonctions qui sont prises en compte parglutMainLoop() après les avoir spécifiées dans les fonctions du typeglut…Func(void (*)(/*arguments nécessaires à respecter*/) func) ;

 

Les fonctions que j’ai appelées keydown() et keyup() sontcelles qui s’occupent de l’appui de touches avec un code ASCII, c'est-à-direles lettre, chiffres, entrée, ESC etc… sauf les flèches, les fonctions spéciales(F1, F2…) et d’autres. J’ai déclaré en variable globale un tableau de 256éléments de type ‘bool’ pour mettre cette touche en ‘true’ lorsquel’utilisateur appuie sur la touche et en ‘false’ quand il la relâche, comme çail pourra appuyer sur plusieurs touches en même temps. Si vous avez besoin d’unappui simple sur une touche (donc qu’il suffit d’appuyer une seule fois, parexemple pour changer le statut de la lumière quand on appuie) il vaut mieux demettre directement un switch(key) dans la fonction et y spécifier ce qu’il fautfaire. Ceci attendra un peu après l’appui pour éviter que le changementlumière/pas lumière ne soit pas trop rapide et difficile à bien gérer…

De même, pour specdown() et specup(), sauf que les touchessont des touches spéciales (F1, F2, flèches, pgDown etc…). Ici, le tableau quej’ai déclaré est skeys[].

Pour ensuite accéder aux touches, vous ferez parexemple : if (keys[‘l’]) {…} ou if ( !keys[‘l’]) {…}. Dans windows.h,il y a des macros (#define) pour les touches normales : par exemplekeys[VK_ESC]. Pour les spéciales, il y a des macros : GLUT_KEY_F1 ouGLUT_KEY_LEFT etc…

Dans le tableau skeys, je ne sais pas vraiment le nombre detouches qu’il y a donc j’ai mis 256 éléments :o|.

Les types des arguments sont à respecter !! dans lakeyboardFunc, key est un unsigned char alors que dans specialFunc ununsigned int !! Les autres arguments x et y correspondent aux coordonnéesde la positions de la souris au moment de l’appui.

void keydown(unsigned char key, int x, int y)

{

      keys[key] = true;

      switch(key)

      {

      case VK_ESCAPE: exit(0); break;

      }

}

 

void keyup(unsigned char key, int x, int y)

{

      keys[key] = false;

}

 

void specdown(int key, int x, int y)

{

      skeys[key] =true;

      switch(key)

      {

      case GLUT_KEY_F1:

            glutFullScreen();

 

      }

}

 

void specup(int key, int x, int y)

{

      skeys[key] =false;

      switch(key)

      {

     

      }

}

 

 

Nous passons maintenant à la displayFunc, que j’ai appeléeDrawGLScene() . C’est la fonction appelée à chaque boucle de jeu et oùl’on dessine le tout. glLoadIdentity() sert à remettre à zéro la matrice etannuler toute translation ou rotation exécutée préalablement. glutSwapBuffers()sert à changer de buffer : il y a 2 buffers (back buffers), quand l’untravaille, l’autre affiche : ce sont des tampons mémoire qui s’alternent.

 

void DrawGLScene(GLvoid)                                                    // Here's Where We Do All The Drawing

{

      glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);  // Clear Screen And Depth Buffer

      glLoadIdentity();       //on 'reset' lamatrice de l'écran: origine au centre de la fenetre

     

 

      glutSwapBuffers();           //changement des buffers

      glutPostRedisplay();    //reaffiche leplan

}

 

Et voilà !!! Notre fenêtre est créée et il nous resteplus qu’à dessiner !!!


Premiers affichages

 

 

Dans ce tutoriel, on va commencer à dessiner dans lafenetre.

 

Pour dessiner des triangles et d’autres polygones, onutilise le système glBegin() ; glEnd() ; . Entre ces parties de code,nous allons dessiner en utilisant des Vertex. Voici un exemple :

 

glBegin(GL_TRIANGLES);

      glVertex3f( 0.0f, 1.0f, 0.0f);

      glVertex3f( 1.0f, 1.0f, 0.0f);

      glVertex3f(-1.0f,-1.0f, 0.0f);

glEnd();

 

l’argument de glBegin() est un GLenum qui indique ce quel’on veut dessiner, par exemple GL_TRIANGLES pour des triangles, GL_QUADS pourdes quads (c'est-à-dire des quadrilatères), GL_POLYGON pour _un_ polygone etc…GL_POLYGON ne dessinera qu’un polygone, ayant le nombre de points situés entreglBegin() et glEnd(). Parcontre, GL_TRIANGLES dessinera un triangle tous les 3points présents dans le bloc et GL_QUADS un quadrilatère tous les 4 points.

Note : le ‘3f’ dans glVertex indique le type deparamètres utilisés : 3 indique qu’il a 3 coordonnées (il existe aussiavec 2 et 4 coordonnées, mais comme je capte pas trop l’utilisation de la 4ècoordonnée, j’en parle pas) et f qu’il s’agit d’un ‘float’ (avec ‘v’, on met unseul argument qui est un tableau (v pour vector) du nombre d’éléments requis).

Pour ajouter des couleurs, on utilise glColor3f() ou 4f sil’on veut le alpha. Quand on appelle cette fonction, elle sera gardée jusqu’àce que l’on ne change la couleur à nouveau. Ici, par exemple, on va dessinerune pyramide en couleur :

 

glBegin(GL_TRIANGLES);

            glColor3f(1.0f, 0.0f, 0.0f);       glVertex3f( 0.0f, 1.0f, 0.0f);

            glColor3f(0.0f, 1.0f, 0.0f);       glVertex3f(-1.0f,-1.0f, 1.0f);

            glColor3f(0.0f, 0.0f, 1.0f);       glVertex3f( 1.0f,-1.0f, 1.0f);

 

            glColor3f(1.0f, 0.0f, 0.0f);       glVertex3f( 0.0f, 1.0f, 0.0f);

            glColor3f(0.0f, 0.0f, 1.0f);       glVertex3f( 1.0f,-1.0f, 1.0f);

            glColor3f(0.1f, 0.1f, 0.1f);       glVertex3f( 1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f, 0.0f, 0.0f);       glVertex3f( 0.0f, 1.0f, 0.0f);

            glColor3f(0.1f, 0.1f, 0.1f);       glVertex3f( 1.0f,-1.0f,-1.0f);

            glColor3f(0.0f, 0.0f, 1.0f);       glVertex3f(-1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f, 0.0f, 0.0f);       glVertex3f( 0.0f, 1.0f, 0.0f);

            glColor3f(0.0f, 0.0f, 1.0f);       glVertex3f(-1.0f,-1.0f,-1.0f);

            glColor3f(0.0f, 1.0f, 0.0f);       glVertex3f(-1.0f,-1.0f, 1.0f);

glEnd();

 

Maintenant, on va ajouter des translations et des rotationspour faire des animations.

Pour voir un peux plus loin et pas trop grand, on effectueau début une translation de 6.0f sur l’axe z :

glTranslatef(0.0f,0.0f,-6.0f);

Cette fonction admet comme argument les coordonnées duvecteur de translation.

Si l’on voulait par exemple zoomer et dézoomer la pyramide,on peut utiliser une variable qui change lorsque l’on appuie sur les touchespage up et page down. Pour cela, on déclare un variable globale au début duprogramme :

float deep = -6.0f ;

Et ensuite on la change avec l’appuie des touches (ce codepeut être mis dans DrawGLScene()) :

 

if (skeys[GLUT_KEY_PAGE_UP])

{

      deep -= 0.02f;

}

if (skeys[GLUT_KEY_PAGE_DOWN])

{

      deep += 0.02f;

}

 

et on appellerait glTranslatef avec cette variable :

glTranslatef(0.0f,0.0f, deep) ;

 

Voilà la fonction en entier. La forme de la pyramide est unpeu étrange à cause de la perspectice, et parce qu’il lui manque le coté endessous que vous pouvez dessiner avec GL_QUADS (n’oubliez pas la variableglobale deep !!) :

 

void DrawGLScene(GLvoid)                                                    // Here's Where We Do All The Drawing

{

      if (skeys[GLUT_KEY_PAGE_UP])

      {

            deep-= 0.02f;

      }

      if (skeys[GLUT_KEY_PAGE_DOWN])

      {

            deep+= 0.02f;

      }

 

      glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);  // Clear Screen And Depth Buffer

      glLoadIdentity();       //on 'reset' lamatrice de l'écran: origine au centre de la fenetre

     

      glTranslatef(0.0f, 0.0f, deep);

 

      glBegin(GL_TRIANGLES);

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);

            glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.1f,0.1f, 0.1f); glVertex3f( 1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 1.0f);

      glEnd();

 

 

      glutSwapBuffers();           //changement des buffers

      glutPostRedisplay();    //reaffiche leplan

}

 

Maintenant, de même, on va ajouter des rotations avec lafonction glTranslatef(). Cette fonction a comme arguments l’angle de rotationet les coordonnées du vecteur autour duquel on fait la rotation. Par exemple :

glRotatef(xrot, 1.0f, 0.0f, 0.0f);  //faire une rotation de xrot degrés sur levecteur de

     //coordonnées (1, 0, 0) donc l’axe (Ox)

 

On va ajouter de la rotation à la pyramide sur l’appui desflèches. Pour cela, on va utiliser deux variables globales : xrot et yrotpour les rotations respectives sur x et sur y et on va ajouter le code suivantaprès les autres if :

 

if (skeys[GLUT_KEY_LEFT])

      yrot -= 0.04f;

if (skeys[GLUT_KEY_RIGHT])

      yrot += 0.04f;

if (skeys[GLUT_KEY_UP])

      xrot -= 0.04f;

if (skeys[GLUT_KEY_DOWN])

      xrot +=0.04f;

 

Et celui-là après le glTranslatef() :

 

glRotatef(yrot, 0.0f, 1.0f, 0.0f);

glRotatef(xrot, 1.0f, 0.0f, 0.0f);

 

Une autre possibilité est celle d’ajouter de la vitesse derotation lorsqu’on appuie sur ces touches:

Variables globales à ajouter : float xspeed =0.0f ;    float yspeed = 0.0f ;

On modifie les if précédents pour qu’il change lavitesse de rotation (si c’est trop rapide sur votre ordinateur, changezl’augmentation):

 

if (skeys[GLUT_KEY_LEFT])

      yspeed -=0.005f;

if (skeys[GLUT_KEY_RIGHT])

      yspeed +=0.005f;

if (skeys[GLUT_KEY_UP])

      xspeed -=0.005f;

if (skeys[GLUT_KEY_DOWN])

      xspeed +=0.005f;

 

Et enfin, on ajoute ce code àla fin de DrawGLScene() pour qu’a chaque boucle la pyramide fasse une rotationrelavive à la vitesse :

xrot +=xspeed;

yrot += yspeed;

 

Voilà le code final :

 

 

void DrawGLScene(GLvoid)                                                    // Here's Where We Do All The Drawing

{

      if (skeys[GLUT_KEY_PAGE_UP])

      {

            deep-= 0.02f;

      }

      if (skeys[GLUT_KEY_PAGE_DOWN])

      {

            deep+= 0.02f;

      }

      if (skeys[GLUT_KEY_LEFT])

            yspeed-= 0.005f;

      if (skeys[GLUT_KEY_RIGHT])

            yspeed+= 0.005f;

      if (skeys[GLUT_KEY_UP])

            xspeed-= 0.005f;

      if (skeys[GLUT_KEY_DOWN])

            xspeed+= 0.005f;

 

 

      glClear(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);  // Clear Screen And Depth Buffer

      glLoadIdentity();       //on 'reset' lamatrice de l'écran: origine au centre de la fenetre

     

      glTranslatef(0.0f, 0.0f, deep);         //translation (zoom)

      glRotatef(yrot,0.0f, 1.0f, 0.0f);

      glRotatef(xrot,1.0f, 0.0f, 0.0f);

 

      glBegin(GL_TRIANGLES);

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);

            glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);

 

            glColor3f(1.0f,0.0f, 0.0f); glVertex3f( 0.0f, 1.0f,0.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);

      glEnd();

 

      glBegin(GL_QUADS);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f( 1.0f,-1.0f,1.0f);

            glColor3f(0.1f,0.1f, 0.1f); glVertex3f(1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,0.0f, 1.0f); glVertex3f(-1.0f,-1.0f,-1.0f);

            glColor3f(0.0f,1.0f, 0.0f); glVertex3f(-1.0f,-1.0f,1.0f);

      glEnd();

 

      xrot +=xspeed;

      yrot +=yspeed;

 

      glutSwapBuffers();           //changementdes buffers

      glutPostRedisplay();    //reaffiche leplan

}

 

Pour vous entrainer, vouspouvez faire en sorte que le cube se remette en position initiale quand onappuie sur une touche.

  • signaler à un administrateur
    Commentaire de betchou le 12/04/2006 16:08:52

    T'as pas mis ton fichier a telecharger? On peut certes copier-coller masi bon...
    Sinon, ca a l'air tres sympa comme "overview".

  • signaler à un administrateur
    Commentaire de Bietz le 18/04/2006 15:13:06

    si je l'ai mis, mais j'ai oublié de mettre le lien de la source:
    http://www.cppfrance.com/codes/ROTATION-PYRAMIDE-3D-COLOREE-OPENGL-GLUT_36790.aspx
    @+

  • signaler à un administrateur
    Commentaire de BugcORE le 14/09/2006 23:20:55

    Perfecto ! Juste ce que je chercher ! Merci ^^

  • signaler à un administrateur
    Commentaire de yacjapan le 20/02/2007 11:22:54

    merci god job

  • signaler à un administrateur
    Commentaire de hasnafstm le 07/04/2008 02:41:32

    c vraiment génial c ce ke je cherchais

Ajouter un commentaire

Pub



Appels d'offres

CalendriCode

Août 2008
LMMJVSD
    123
45678910
11121314151617
18192021222324
25262728293031

Boutique

Boutique de goodies CodeS-SourceS