Sélectionner et déplacer des objets dans une scène en OpenGL

Sommaire :

- Introduction

- Installation de la SDL

- Les évènements SDL

- Sélectionner et déplacer en OpenGL

- Conclusion

 

 

Introduction :

Dans cette article nous allons voir comment sélectionner et déplacer des objets dans une scène OpenGL, pour cela nous allons utiliser la bibliothèque SDL2 qui nous facilitera la tâche.

Installation de la SDL :

Pour pouvoir créer un nouveau projet SDL, vous aurez besoin de télécharger la bibliothèque à cette adresse : https://www.libsdl.org/download-2.0.php

Sur cette page vous aurez plusieurs choix séparés en trois catégories :

- Source code : Cette partie vous permet de télécharger le code source mais cela ne nous intéresse pas.

- Runtime libraries : Qui contient les fichiers qui doivent être distribués avec l’exécutable lorsque vous donnerez votre programme à d’autres personnes.

- Development libraries : Cette partie contient les fichiers qui permettent de créer des programmes SDL. Elle n’est nécessaire que pour le programmeur.

A noter que la partie « Development libraries » contient aussi la « SDL.dll » à distribuer avec votre application et la documentation SDL.

Vous n’avez donc qu’à télécharger les « Development libraries ».

Maintenant nous allons voir comment créer un projet SDL sur Windows avec Code::Blocks.

Création d’un projet SDL sous Code::Blocks :

Une fois que vous avez téléchargé le fichier compressé, vous devez l’extraire sur votre disque dur, vous pouvez par exemple extraire dans le dossier de Code::Blocks.

Maintenant allez dans le sous-dossier « include/SDL », si vous aviez extrait l’archive de SDL dans le dossier Code::Blocks le chemin devrait ressembler à ceci : « C:\Program Files (x86)\CodeBlocks\SDL2-2.0.0\i686-w64-mingw32\include\SDL ».

Une fois dans ce dossier, copiez tous les fichiers qui s’y trouve et collez-les dans le dossier parent : «  C:\Program Files (x86)\CodeBlocks\SDL2-2.0.0\i686-w64-mingw32\include ».

Et voilà maintenant il ne nous reste plus qu’à créer un nouveau projet SDL.

Ouvrez Code::Blocks et créez un nouveau projet, quand le type de projet vous sera demandé sélectionnez « SDL project ».

Type de projet

 

Ensuite on vous demandera le nom de votre projet et le dossier dans lequel il doit être placé.

Nom et chemin du projet

 

Une fois le nom et le chemin du projet spécifiés, vous allez devoir indiquer où se trouve SDL.

Location de SDL2

 

Pour spécifier où se trouve SDL cliquez sur le bouton « ... » à côté du champ.

Location de SDL2

 

Vous devriez voir apparaître une nouvelle fenêtre dans laquelle vous n’avez qu’à remplir le champ « base » en spécifiant le dossier où vous avez décompressé la SDL, par exemple : « C:\Program Files (x86)\CodeBlocks\SDL2-2.0.0\i686-w64-mingw32 ».

Vous pouvez maintenant fermer la fenêtre en cliquant sur le bouton « Close », si une nouvelle fenêtre vous demandant un dossier apparaît fermez-la en cliquant sur « Annuler ».

Ensuite cliquez sur « Next » puis choisissez le mode de compilation et cliquez sur « Finish ».

Mode de compilation

 

Vous vous retrouvez maintenant avec un projet SDL qui contient un fichier « main.cpp », si vous essayez de le compiler vous vous retrouverez avec une fenêtre qui vous affichera « Cette application n'a pas pu démarrer car SDL.dll est introuvable » pour palier à ce problème il faut copier le fichier « SDL.dll » qui se trouve dans « C:\Program Files (x86)\CodeBlocks\SDL2-2.0.0\i686-w64-mingw32\bin » dans le dossier de votre projet.

Maintenant si vous essayer de compiler, une fenêtre se remplissant de différentes couleurs apparaît.

Les évènements SDL :

Pour gérer les évènements nous allons avoir besoin d’une boucle en effet une boucle nous permettra de gérer par exemple :

- Des objets qui seront bougés

- La gestion des entrées

- Le détection des collisions

- etc...

bool loop = true;

while (loop)

{

     // Gestion des évènements

}

Tous les évènements font partie de la structure « SDL_Event » qui contient notamment les évènements suivants :

- Évènement quitter

- Évènements de souris

- Évènements de clavier

- Évènements de fenêtre (redimensionner, minimiser, déplacer, focus, …)

- Évènements téléphone (toucher, échelle, …)

- etc...

Le type de l’évènement est contenu dans le « .event ». C’est une énumération de type « SDL_EventType ».

Maintenant que nous connaissons « SDL_Event », nous allons voir comment obtenir l’évènement avec cette méthode :

int SDL_PollEvent

(

     SDL_Event* event

)

On passe simplement un pointeur à SDL_Event, si elle renvoie 0, il n’y a plus d’évènement. A noter qu’il y aura probablement plus d’un « SDL_Event » dans chaque itération, nous devrons donc mettre cette fonction dans une boucle.

bool loop = true;

while (loop)

{

     SDL_Event event;

     while (SDL_PollEvent(&event))

     {

         // Ici nous mettons nos évènements

     }

}

L’un des évènements que l’on va utiliser souvent est « SDL_QUIT », on l’utilisera quand un utilisateur voudra quitter notre programme.

bool loop = true;

while (loop)

{

     SDL_Event event;

     while (SDL_PollEvent(&event))

     {

         if (event.type == SDL_QUIT)

         {

             loop = false;

         }

     }

}

Ceci étant fait l’utilisateur pourra quitter le programme.

 

Sélectionner et déplacer en OpenGL :

Avant de commencer vous aurez besoin d’une image « .bmp » de dimension 64x64, voici celle que je vais utiliser :

Image .bmp

 

Vous pourrez placer cette image dans le répertoire « bin/Debug » à côté de l’exécutable de votre projet.

Dans cette partie nous allons créer un petit programme qui nous permettra de déplacer une image dans une scène, nous allons commencer par faire les inclusions nécessaires.

#include <sstream>

#include <SDL.h>

#include <iostream>

#include <stdlib.h>

Ensuite nous allons déclarer nos variables dans notre boucle « main ».

int main(int argc, char ** argv)

{

    // variables

    bool quit = false;

    SDL_Event event;

    int x = 288;

    int y = 208;

Dans le code ci-dessus nous déclarons une variable de type « bool » qui nous permettra de quitter la boucle dans laquelle nous mettrons nos évènements.

On déclare une variable de type « SDL_Event » et enfin on déclare deux variables « x » et « y » qui contiendront les coordonnées de notre image.

// init SDL

SDL_Init(SDL_INIT_VIDEO);

SDL_Window * window = SDL_CreateWindow("SDL2 Mouse events", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);

SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

SDL_Surface * image = SDL_LoadBMP("bin/Debug/object.bmp");

SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);

SDL_FreeSurface(image);

SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);

if (image == NULL)

{

     std::cout << "Erreur de chargement de l'image bitmap: \n" << SDL_GetError();

     return 1;

}

Dans cette partie nous initialisons SDL en commençant par la fonction « SDL_Init(SDL_INIT_VIDEO) » pour initialiser le sous-système vidéo, ensuite on crée une fenêtre avec la fonction « SDL_CreateWindow() » dans laquelle on passe en paramètre le nom de la fenêtre, la position initiale X, la position initiale Y, la largeur en pixels, la longueur en pixels et les options.

Ensuite on utilise la fonction « SDL_CreateRenderer » avec en paramètres la fenêtre à laquelle est associer le renderer, l’index et les options.

On utilise « SDL_LoadBMP » pour charger une image bmp en spécifiant son chemin.

La fonction « SDL_CreateTextureFromSurface » est utilisée avec comme paramètres le contexte de rendu et la structure contenant les données des pixels utilisés pour remplir la texture.

« SDL_FreeSurface » est utilisé pour libérer la structure « SDL_Surface ».

Enfin on utilise la fonction « SDL_SetRendererDrawColor » qui prend en paramètres le contexte de rendu, la valeur de rouge, la valeur de vert, la valeur de bleu et la valeur alpha utilisée pour dessiner sur la cible de rendu.

La dernière condition est là pour tester si l’image s’est correctement chargée.

// events

bool onclick = 0;

while (!quit)

{

    SDL_Delay(20);

    SDL_PollEvent(&event);

    SDL_Rect dstrect = { x, y, 64, 64 };

Dans la suite du code on initialise une variable booléenne à 0, c’est elle qui nous permettra de savoir quand on clique sur l’image.

Ensuite on commence notre boucle avec la fonction « SDL_Delay » avec en paramètre le nombre de millisecondes.

« SDL_PollEvent » avec en paramètre la variable « event » précédemment créée et « SDL_Rect » qui correspondra à la zone de notre image.

if (event.type == SDL_QUIT)

{

     quit = true;

     break;

}

Dans la condition ci-dessus on teste simplement si l’utilisateur quitte le programme et on change la valeur de notre variable « quit » pour remplir la condition de sortie de la boucle « while ».

if (event.type == SDL_MOUSEBUTTONDOWN)

{

     if (event.button.button == SDL_BUTTON_LEFT) {

     onclick = 1;

     }

}

Dans la première condition on vérifie si le bouton de la souris est pressé, dans la deuxième condition on vérifie si le bouton gauche de la souris est pressé, si il est pressé alors on passe la variable « onclick » à 1.

if (event.type == SDL_MOUSEBUTTONUP)

{

    onclick = 0;

}

Dans cette condition on vérifie si le bouton n’est pas pressé, si il ne l’est pas alors on met la variable « onclick » à 0.

if (onclick == 1)

{

     if (SDL_GetMouseState(&x, &y))

     {

         dstrect.x = event.motion.x;

         dstrect.y = event.motion.y;

     }

}

Maintenant on vérifie si « onclick » est égal à 1, si oui on passe à la condition suivante dans laquelle on récupère les coordonnées du curseur par rapport à la fenêtre focus et on donne les coordonnées X et Y à notre image.

if (event.type == SDL_MOUSEMOTION)

{

     int mouseX = event.motion.x;

     int mouseY = event.motion.y;

     std::stringstream ss;

     ss << "Cursor X: " << mouseX << " Cursor Y: " << mouseY << " || Rect X: " << dstrect.x << " Rect Y:" << dstrect.y;

     SDL_SetWindowTitle(window, ss.str().c_str());

}

Cette condition sert simplement à afficher les coordonnées de la souris et du rectangle dans le titre de la fenêtre ni plus ni moins.

    SDL_RenderClear(renderer);

    SDL_RenderCopy(renderer, texture, NULL, &dstrect);

    SDL_RenderPresent(renderer);

}

Ensuite on utilise la fonction « SDL_RenderClear » pour effacer la cible de rendu en cours, puis on utilise « SDL_RenderCopy » pour copier une partie du rendu (c’est comme ça que l’on va changer la position de notre rectangle).

Enfin on utilise « SDL_RenderPresent » pour mettre à jour l’écran avec le dernier rendu.

    // cleanup SDL

    SDL_DestroyTexture(texture);

    SDL_DestroyRenderer(renderer);

    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

La première fonction ci-dessus « SDL_DestroyTexture » sert à détruire une texture spécifique.

On utilise ensuite « SDL_DestroyRenderer » pour détruire le contexte de rendu.

On utilise « SDL_DestroyWindow » pour détruire la fenêtre.

Pour terminer on utilise « SDL_Quit() » pour nettoyer tous les sous-systèmes initialisés.

Au final vous devriez vous retrouver avec ce code :

#include <sstream>

#include <SDL.h>

#include <iostream>

#include <stdlib.h>

int main(int argc, char ** argv)

{

    // variables

    bool quit = false;

    SDL_Event event;

    int x = 288;

    int y = 208;

    // init SDL

    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window * window = SDL_CreateWindow("SDL2 Mouse events", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0);

    SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);

    SDL_Surface * image = SDL_LoadBMP("bin/Debug/object.bmp");

    SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);

    SDL_FreeSurface(image);

    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);

    if (image == NULL)

    {

        std::cout << "Erreur de chargement de l'image bitmap: \n" << SDL_GetError();

         return 1;

    }

    // events

    bool onclick = 0;

    while (!quit)

    {

        SDL_Delay(20);

        SDL_PollEvent(&event);

        SDL_Rect dstrect = { x, y, 64, 64 };

        if (event.type == SDL_QUIT)

        {

            quit = true;

            break;

        }

        if (event.type == SDL_MOUSEBUTTONDOWN)

        {

            if (event.button.button == SDL_BUTTON_LEFT)

            {

                onclick = 1;

            }

        }

        if (event.type == SDL_MOUSEBUTTONUP)

        {

            onclick = 0;

        }

        if (onclick == 1)

        {

            if (SDL_GetMouseState(&x, &y))

            {

                dstrect.x = event.motion.x;

                dstrect.y = event.motion.y;

            }

        }

        std::cout << "X: " << dstrect.x << std::endl << "Y: " << dstrect.y << std::endl;

        if (event.type == SDL_MOUSEMOTION)

        {

            int mouseX = event.motion.x;

            int mouseY = event.motion.y;

            std::stringstream ss;

            ss << "X: " << mouseX << " Y: " << mouseY;

            SDL_SetWindowTitle(window, ss.str().c_str());

        }

        SDL_RenderClear(renderer);

        SDL_RenderCopy(renderer, texture, NULL, &dstrect);

        SDL_RenderPresent(renderer);

    }

    // cleanup SDL

    SDL_DestroyTexture(texture);

    SDL_DestroyRenderer(renderer);

    SDL_DestroyWindow(window);

    SDL_Quit();

    return 0;

}

Le rendu final :

Rendu final

 

Et quand on change de position en déplaçant avec la souris :

Rendu final

 

Conclusion :

OpenGL couplé à SDL2 rend plus aisé la programmation grâce à une multitude de fonctions permettant de gérer les évènements et de les traiter, on peut donc facilement créer des programmes plus complexes.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *