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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
| /*
** Par Yves Desgraupes
** yves.desgraupes@free.fr
** bddy.free.fr
** 2008 tous droits pas reservés :)
** ------------------------------
** Test de picking sur une scène 3D (2 carrés superposés)
** Système de fenetrage : SDL
** API graphique : OpenGL
** Librairies nécessaires : SDL && gl && glu
** Fichiers nécessaires : SDL.dll || SDL.so
** Langage : C++ (bien que ca soit du C+ :s)
*/
#include <iostream> //Pour écrire sur la sortie standard/erreur
#include <SDL/SDL.h> //Pour créer une fenetre et gérer les évènements
#include <GL/gl.h> //On fait de l'OpenGL, non ?
#include <GL/glu.h> //Parce qu'elle est bien pratique cette lib (un conseil : recodez 'gluPerspective' et 'gluLookAt' pour gagner en perf)
/*
** Déclaration du prototype de toutes les fonctions meme si là, ca ne sert pas à grand chose
*/
void checkevents();
void draw();
void processHits(GLint hits, GLuint *buffer);
void picking();
void drawsquare(bool cas);
void taketime(bool cas);
bool initGL();
/*
** Déclaration de variables globales, c'est mieux que des #define, le type des variables étant déclaré
*/
const int WIN_X = 800; //Largeur de la fenetre
const int WIN_Y = 600; //Hauteur de la fenetre
const int BUFSIZE = 1024; //Taille du buffer de selection
/*
** Déclaration de variables globales, mais cette fois c'est TRES MOCHE
** pour une raison de clarté, je les laisse ici plutot que de les mettre dans plusieurs struct
** ou une éventuelle class.
** mais ca reste très moche !!! donc, recodez sans global de ce genre !!!
*/
int sTime;
int cTime;
int eTime;
int mx = 0;
int my = 0;
bool go = true;
bool click;
SDL_Event event;
SDL_Surface *screen;
/*
** Initialisation de la fenetre qui contiendra notre scène
*/
bool initGL()
{
if (SDL_Init(SDL_INIT_VIDEO)) //On démarre la SDL avec le système d'affichage
{ //et on vérifie qu'elle se soit bien chargée
std::cerr << "SDL_Init : [FAILED]" << std::endl; //On écrit d'ou vient la panne
return (false); //et on s'en va
}
SDL_WM_SetCaption("OpenGL Rox", NULL); //On ajoute un titre à notre future fenetre
if (!(screen = SDL_SetVideoMode(WIN_X, WIN_Y, 32, SDL_OPENGL))) //On set le système d'affichage
{ //et on vérifie que tout se soit bien passé
std::cerr << "SDL_SetVideoMode : [FAILED]" << std::endl; //On écrit d'ou vient la panne
return (false); //et on s'en va
}
glEnable(GL_DEPTH_TEST); //Activation des tests de profondeur du z-buffer
SDL_WarpMouse(screen->w / 2, screen->h / 2); //On centre le pointeur de la souris au milieu de notre fenetre
return (true);
}
/*
** Gestion du temps pour chaque tour de boucle de notre programme
*/
void taketime(bool cas) //cas : différencier le moment d'appel dans la boucle
{
if (!cas) //cas 0 : on est en début de boucle
sTime = SDL_GetTicks(); //alors on prend le temps au départ de la boucle
else //cas 1 : on est en fin de boucle
{
cTime = SDL_GetTicks(); //alors on prend le temps en fin de boucle
eTime = cTime - sTime; //On calcule le temps écoulé entre le début et la fin
if (eTime < 10) //et si la boucle a duré moins de 10ms
SDL_Delay(10 - eTime); //On met en pause le programme pour 10ms moins le temps déjà écoulé
}
}
/*
** On déclare les objets de notre scène
*/
void drawsquare(bool cas) //cas : différencier le mode de rendu (RENDER/SELECT)
{
if (cas) //Cas 1 : on est en mode SELECT, le picking est actif
glLoadName(1); //alors on attribut un nom (1) à l'objet qui suit
glColor3ub(255, 255, 255); //On déclare la couleur de l'objet (pour le coup : blanc)
glBegin(GL_QUADS); //On déclare le type de l'objet
glVertex3d(0, 0, 0); //On décrit chacun des 4 points que comprend notre carré
glVertex3d(0, 10, 0);
glVertex3d(10, 10, 0);
glVertex3d(10, 0, 0);
glEnd(); //On déclare la fin de notre objet
//On recommence avec un carré rouge
if (cas) //Cas 1 : on est en mode SELECT, le picking est actif
glLoadName(2); //alors on attribut un nom (2) à l'objet qui suit
glColor3ub(255, 0, 0); //On déclare la couleur de l'objet (pour le coup : rouge)
glBegin(GL_QUADS); //On déclare le type de l'objet
glVertex3d(3, 3, 0.2); //On décrit chacun des 4 points que comprend notre carré
glVertex3d(3, 7, 0.2);
glVertex3d(7, 7, 0.2);
glVertex3d(7, 3, 0.2);
glEnd(); //On déclare la fin de notre objet
}
/*
** On décortique ce que le picking nous renvoie
*/
void processHits(GLint hits, GLuint *buffer) //hits : nombre d'objets touchés ; buffer : pointeur sur le buffer de selection
{
GLuint names; //Sauvegardera le nombre de noms d'un Hit
GLuint *ptr; //Pointeur pour parcourir le buffer
std::cout << "hits = " << hits << std::endl; //On affiche le nombre de Hit
ptr = (GLuint *)buffer; //On fait pointer notre pointeur sur celui du buffer
for (int i = 0; i < hits; i++) //Et on parcourt tous nos Hits
{
names = *ptr; //Sauvegarde du nombre de noms du Hit
std::cout << " number of names for this hit = " << names << std::endl; //Affichage du nombre de noms du Hit
ptr++; //On déplace le pointeur dans la mémoire
std::cout << " z1 is " << (float)*ptr / 0x7fffffff; //Affichage de la valeur minimale de la coordonnée Z
ptr++; //On déplace le pointeur dans la mémoire
std::cout << " z2 is " << (float)*ptr / 0x7fffffff << std::endl; //Affichage de la valeur maximale de la coordonnée Z
ptr++; //On déplace le pointeur dans la mémoire
std::cout << " NAMES "; //Affichage pour faire joli
for (unsigned int j = 0; j < names; j++) //On parcourt tous les noms du Hit
{
std::cout << *ptr << std::endl; //Affichage du nom
ptr++; //On déplace le pointeur dans la mémoire
}
std::cout << std::endl; //Affichage pour faire joli
}
std::cout << std::endl << "--------------------" << std::endl << std::endl; //Affichage pas pour faire joli mais pour y voir plus clair
}
/*
** On regarde ce qu'il se cache sous le pointeur de la souris
*/
void picking()
{
GLuint selectBuf[BUFSIZE]; //Création d'un buffer de sélection de la taille de BUFSIZE
GLint hits; //Contiendra le nombre d'objet touchés par le pointeur de la souris
GLint viewport[4]; //Contiendra les valeurs du viewport
glGetIntegerv(GL_VIEWPORT, viewport); //Récupération des valeurs du viewport
glSelectBuffer(BUFSIZE, selectBuf); //Etablit un buffer pour les valeurs du mode SELECT
(void)glRenderMode(GL_SELECT); //Sélection de la méthode de rastérisation
glInitNames(); //Initialisation de la pile de nom
glPushName(0); //On initialise la pile de nom avec (0)
glMatrixMode(GL_PROJECTION); //On passe par la matrice de projection
glPushMatrix(); //On sauvegarde la matrice courante
glLoadIdentity(); //et on la réinitialise
gluPickMatrix((GLdouble) mx, (GLdouble) (viewport[3] - my), 2.0, 2.0, viewport); //On définit la région du picking et sa précision
gluPerspective(70, float(WIN_X / WIN_Y), 0.1, 1000); //On donne à notre matrice les proportions de la scène
drawsquare(true); //On affiche les 2 carrés et on leur attribut un (ou plusieurs) nom(s)
glPopMatrix(); //On recharge la matrice précédemment sauvegardée
glPopName(); //On supprime le nom de la pile
glFlush(); //On flush les commandes OpenGL
hits = glRenderMode(GL_RENDER); //On récupère le nombre d'objets touchés tout en sortant du mode SELECT
processHits(hits, selectBuf); //On traite notre sélection
}
/*
** On crée un rendu graphique
*/
void draw()
{
glViewport(0, 0, WIN_X, WIN_Y); //Création de la taille du ViewPort pour indiquer les frontières du cadrage actif
glMatrixMode(GL_PROJECTION); //On passe par la matrice de projection
glLoadIdentity(); //et on la réinitialise pour éviter de cumuler les transformations
gluPerspective(70, float(WIN_X / WIN_Y), 0.1, 1000); //On donne les proportions de notre scène en modifiant la matrice courante
glMatrixMode(GL_MODELVIEW); //On passe par la matrice modelview
glLoadIdentity(); //et on la réinitialise pour éviter de cumuler les transformations
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Effacement du tampon d'affichage
gluLookAt(-10, -10, 10, 0, 0, 0, 0, 0, 1); //Mise en place du point de vue
drawsquare(false); //On affiche seulement les 2 carrés sans leur attribuer de nom
glFlush(); //Flushage des commandes OpenGL
SDL_GL_SwapBuffers(); //Affichage du buffer dans la fenetre
}
/*
** On gère les évènements (clavier / souris)
*/
void checkevents()
{
if (event.type == SDL_QUIT) //Si on clique sur la croix pour fermer la fenetre
go = false; //on passe go a false
else if (event.type == SDL_KEYDOWN) //Si un bouton du clavier est appuyé
if (event.key.keysym.sym == SDLK_ESCAPE || event.key.keysym.sym == SDLK_DELETE) //et que c'est l'un de ceux-là (Echap ou Suppr)
go = false; //on passe go a false
if (event.type == SDL_MOUSEMOTION) //Si on bouge la souris
{
mx = event.motion.x; //On sauvegarde sa position en x
my = event.motion.y; //On sauvegarde sa position en y
}
else if (event.type == SDL_MOUSEBUTTONDOWN) //Si on clique sur l'un des boutons de la souris (molette haut/bas compris)
{
if (event.button.button == SDL_BUTTON_LEFT) //et que c'est le click gauche
click = true; //on passe le click a true
}
else if (event.type == SDL_MOUSEBUTTONUP) //Si on relache le click sur l'un des boutons de la souris
{
if (event.button.button == SDL_BUTTON_LEFT) //et que c'est le click gauche
click = false; //on passe le click a true
}
}
/*
** Faut bien commencer quelque part
*/
int main(int argc, char **argv)
{
#ifdef WIN32 //SDL redirige de lui-meme les sorties standard/erreur
freopen("CON","w", stderr); //En fonction de l'OS il y aura besoin de réouvrir les sorties
freopen("CON","w", stdout); //pour afficher sur la console et non plus dans des fichiers
#endif
if (!initGL()) //Initialisation de la fenetre SDL/OpenGL
return (1); //si ca loupe, on quitte en failure
while (go) //Notre programme tourne tant que (go == true)
{
taketime(false); //Prise du temps du départ de la boucle
while (SDL_PollEvent(&event)) //SDL parcourt pour nous les évènements clavier/souris et les stocks dans 'event'
checkevents(); //On fait notre gestion des évènements
draw(); //On affiche la scène
if (click) //Si le click gauche est appuyé
picking(); //on tente de picker la scène
taketime(true); //Prise du temps de la fin de boucle et pause du programme
}
SDL_Quit(); //On ferme la fenetre SDL et toute autre initialisation liée à elle
return (0); //Le programme quitte correctement
} |
Partager