Détails de l´implémentation
Partie cliente imposée pour la compatibilité avec le serveur d’affichage (fichier fourni aux étudiants):
#ifndef __world_h__ #define __world_h__ #include <iostream> /********************************************************************* * TYPES *********************************************************************/ /* Pavé [X,X+Width[x[Y,Y+Height[ du plan */ typedef struct { int X,Y,Width,Height; } rect; rect Rect(const int x,const int y,const int width,const int height); std::ostream& operator << (std::ostream& Stream,const rect& R); /* Vecteur de l'espace */ typedef struct { float X,Y,Z; } vect; vect Vect(float x,float y,float z); std::ostream& operator << (std::ostream& Stream,const vect& V); template <typename T> vect operator * (const T K,const vect& U); template <typename T> vect operator * (const vect& U,const T K); template <typename T> vect operator *= (vect& U,const T K); template <typename T> vect operator / (const vect& U,const T K); template <typename T> vect operator /= (vect& U,const T K); vect operator ! (const vect& U); vect operator + (const vect& U); vect operator - (const vect& U); vect operator + (const vect& U,const vect& V); vect operator += (vect& U,const vect& V); vect operator - (const vect& U,const vect& V); vect operator -= (vect& U,const vect& V); float operator * (const vect& U,const vect& V); vect operator ^ (const vect& U,const vect& V); float norm(const vect& V); /********************************************************************* * CONSTANTS *********************************************************************/ /* Quelques vecteurs utiles... */ const vect VO={0,0,0}, VX={1,0,0}, VY={0,1,0}, VZ={0,0,1}; #define PI 3.141592653589793 /********************************************************************* * CLASS WORLD *********************************************************************/ class world { public: /* Initialisation du client, appelée dès que le système démarre. Les méthodes draw et trace ne seront jamais appelées avant. Cette méthode est appelée dans un contexte OpenGL valide, et permet par exemple au client de charger des textures. Le client doit définir certaines propriétés physiques de la modélisation: + la référence start doit être initialisée à la position de départ de la caméra; + la référence radius doit être initialisée à la valeur du rayon de contact minimal entre la caméra et les objets. Le moteur devra garantir qu'il n'est pas possible de s'approcher d'un objet au delà de ce rayon en utilisant les méthodes trace fournies par le client; + la référence speed doit être initialisée à la vitesse (horizontale) de marche de l'acteur; + la référence gravity doit être initialisée à la valeur de la gravité. Si gravity vaut zéro, le mouvement sera de type "jetpack"; + la référence friction doit être initialisée à la valeur du coefficient de friction du mouvement horizontal sur le sol. Friction correspond à la pente minimale à partir de laquelle l'acteur peut commencer à glisser sur le sol. Les instances de la classe world doivent s'assurer de donner une position de départ garantissant qu'initialement il n'y ait aucun objet à l'intérieur de la sphère de contact */ virtual void initialize(vect& start,float& radius,float& speed,float& gravity,float& friction){ start=Vect(0,1,0); /* Par défaut, l'acteur commence à la position (0,1,0) */ radius=1; /* Par défaut, le rayon de contact vaut 1 */ speed=1; /* Par défaut l'acteur marche à la vitesse 0.5 */ gravity=10; /* Par défaut la gravité vaut 10 */ }; /* Finalisation de la classe, appelée lorsque le système s'arrête. Les méthodes draw et trace ne seront plus appelées après. Cette méthode est appelée dans le même contexte OpenGL que lors de l'initialisation, et permet par exemple de libérer les textures utilisées. */ virtual void finalize(void){}; /* Méthode pour indiquer au client qu'une position a été atteinte par l'acteur: si la fonction renvoie true le moteur doit appeler la méthode finalize et stopper l'affichage, sinon la modélisation continue. */ virtual bool reach(const vect& position){ return false; /* Par défaut continuer la visualisation */ }; /* Méthode pour indiquer au client qu'une commande de l'interface a été actionnée. Le paramètre index identifie le numéro de la commande. */ virtual void command(const int index){}; /* Méthode pour indiquer au client le rapport entre le FPS en cours et le FPS désiré */ virtual void FPSHint(const float ratio){}; /* Méthode qui déclenche l'affichage de la scène depuis une caméra perspective planaire située à la position eye, regardant dans la direction center. Doit être appelée dans le même contexte OpenGL que celui qui a servi pour l'initialisation. + viewport est la zone de tracé exprimée en pixels dans la surface OpenGL associée au contexte courant; + angle est l'angle vertical de la caméra; + ratio est le rapport largeur/hauteur de la caméra; + eye est la position de la caméra dans le repère global; + center est une position visée par la caméra dans le repère global; + up est un vecteur qui définit la verticale du plan de la caméra dans le repère global. */ virtual void draw(const rect& viewport,const float angle,const float ratio,const vect& eye,const vect& center,const vect& up){}; /* Méthode pour dessiner des éléments d'interface utilisateur. A priori ceux ci ne devraient pas être déformés par la projection sphérique (ou du moins différemment des éléments 3D, donc il faut une méthode séparée). */ virtual void drawHUD(const rect& viewport){}; /* Détection de collision avec le segment d'extrémités from et from+direction. Renvoie true si au moins une collision détectée, false sinon. Si distance!=NULL et qu'au moins une collision a été détectée, distance doit contenir une valeur comprise entre 0 et 1 telle que la collision la plus proche de from ait lieu en from+distance*direction. Si normal!=NULL et qu'au moins une collision a été détectée, normal doit contenir la normale à la surface ayant engendré la collision la plus proche de from. */ virtual bool trace(const vect& from,const vect& direction,float* distance,vect* normal){ return false; /* Par défaut, pas de collision détectée */ }; /* Détection de collision avec le segment d'extrémités from et to. Renvoie true si au moins une collision détectée, false sinon. Si intersect!=NULL et qu'au moins une collision a été détectée, intersect doit contenir le point de collision le plus proche de from. Si normal!=NULL et qu'au moins une collision a été détectée, normal doit contenir la normale à la surface ayant engendré la collision la plus proche de from. */ bool trace(const vect& from,const vect& to,vect* intersect,vect* normal){ if (intersect){ float r; if (trace(from,to-from,&r,normal)){ *intersect=r*to+(1-r)*from; return true; } else return false; } else return trace(from,to-from,(float*) NULL,normal); }; }; /********************************************************************* * RECT FUNCTIONS *********************************************************************/ /* Construction d'un rectangle */ rect Rect(const int x,const int y,const int width,const int height){ rect R={x,y,width,height}; return R; } /* Affichage d'un rectangle */ std::ostream& operator << (std::ostream& Stream,const rect& R){ return Stream << "[" << R.X << "," << R.X+R.Width << "] x [" << R.Y << "," << R.Y+R.Height << "]"; } /********************************************************************* * VECT FUNCTIONS *********************************************************************/ /* Construction d'un vecteur */ vect Vect(const float x,const float y,const float z){ vect V={x,y,z}; return V; } /* Affichage des coordonnées d'un vecteur */ std::ostream& operator << (std::ostream& Stream,const vect& V){ return Stream << "(" << V.X << " , " << V.Y << " , " << V.Z << ")"; } template <typename T> vect operator * (const T K,const vect& U){ vect V={K*U.X,K*U.Y,K*U.Z}; return V; } template <typename T> vect operator * (const vect& U,const T K){ vect V={K*U.X,K*U.Y,K*U.Z}; return V; } template <typename T> vect operator *= (vect& U,const T K){ U.X*=K; U.Y*=K; U.Z*=K; return U; } template <typename T> vect operator / (const vect& U,const T K){ vect V={U.X/K,U.Y/K,U.Z/K}; return V; } template <typename T> vect operator /= (vect& U,const T K){ U.X/=K; U.Y/=K; U.Z/=K; return U; } /* Normalisation d'un vecteur. Renvoie le vecteur nul si U est nul. */ vect operator ! (const vect& U){ return U/(1E-10+norm(U)); } vect operator + (const vect& U){ return U; } vect operator - (const vect& U){ vect V={-U.X,-U.Y,-U.Z}; return V; } vect operator + (const vect& U,const vect& V){ vect W={U.X+V.X,U.Y+V.Y,U.Z+V.Z}; return W; } vect operator += (vect& U,const vect& V){ U.X+=V.X; U.Y+=V.Y; U.Z+=V.Z; return U; } vect operator - (const vect& U,const vect& V){ vect W={U.X-V.X,U.Y-V.Y,U.Z-V.Z}; return W; } vect operator -= (vect& U,const vect& V){ U.X-=V.X; U.Y-=V.Y; U.Z-=V.Z; return U; } /* Produit scalaire */ float operator * (const vect& U,const vect& V){ return U.X*V.X+U.Y*V.Y+U.Z*V.Z; } /* Produit vectoriel */ vect operator ^ (const vect& U,const vect& V){ vect W={U.Y*V.Z-U.Z*V.Y,U.Z*V.X-U.X*V.Z,U.X*V.Y-U.Y*V.X}; return W; } /* Norme euclidienne */ float norm(const vect& V){ return sqrt(V.X*V.X+V.Y*V.Y+V.Z*V.Z); } #endif

Génération automatique d’un labyrinthe planaire avec une entrée et une sortie (partie commune):

#ifndef __laby_h__ #define __laby_h__ #include <iostream> #include <sstream> #include <fstream> #include <string> #include <vector> #include <algorithm> #include <cassert> #include <climits> #include <ctime> #include "bitmap.h" #include "common.h" #include "matrix.h" #include "inifile.h" class laby: public matrix, public world { friend std::ostream& operator << (std::ostream& Stream,laby& Laby); private: void randomChange(const int add){ int s=0; point p; for (p.X=0;p.X<matrix::Width;p.X++) for (p.Y=0;p.Y<matrix::Height;p.Y++) if (((*this)[p] ^ add) && p!=Entry && p!=Exit) s++; assert(s>0); s=rand() % s; for (p.X=0;p.X<matrix::Width;p.X++) for (p.Y=0;p.Y<matrix::Height;p.Y++) if (((*this)[p] ^ add) && p!=Entry && p!=Exit && !s--){ (*this)[p]=add; return; } } protected: point Entry,Exit; std::vector<quad> Faces; void saveToStream(std::ostream& Stream){ matrix::saveToStream(Stream); Stream.write((char*) &Entry,sizeof(Entry)); Stream.write((char*) &Exit,sizeof(Exit)); } void loadFromStream(std::istream& Stream){ matrix::loadFromStream(Stream); Stream.read((char*) &Entry,sizeof(Entry)); Stream.read((char*) &Exit,sizeof(Exit)); } GLuint loadTexture(const bitmap<unsigned char>& image){ GLuint index; glGenTextures(1,&index); glBindTexture(GL_TEXTURE_2D,index); glPixelStorei(GL_PACK_ALIGNMENT,1); /* Génération automatique des mip-maps */ gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGB,image.getWidth(),image.getHeight(),GL_RGB,GL_UNSIGNED_BYTE,image.getData()); /* Filtre de sous-échantillonnage bilinéaire */ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR); /* Filtre de sur-échantillonnage linéaire */ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); return index; } GLuint loadTexture(const char* fileName){ bitmap<unsigned char> image; try { image.loadFile(fileName); } catch(...){ std::cout << "Error while loading " << fileName << std::endl; return 0; } return loadTexture(image); } public: laby(const int width=0,const int height=0): matrix(width,height){ loadParams("maze.ini"); Entry=Point(0,0); Exit=Point(0,0); }; int solve(matrix& m){ static std::vector<point> v1(10000),v2(10000); std::vector<point> *vLast=&v1,*vNew=&v2; int i,l=0; point p,delta[4]={{-1,0},{1,0},{0,-1},{0,1}}; vLast->clear(); vLast->push_back(Entry); m.resize(matrix::Width,matrix::Height); m.fill(INT_MAX); m[Entry]=0; do { vNew->clear(); while (!vLast->empty()){ p=vLast->back(); vLast->pop_back(); for (i=0;i<4;i++){ point q=p+delta[i]; if ((*this)[q] && m[q]>l){ m[q]=l; if (find(vNew->begin(),vNew->end(),q)==vNew->end()) vNew->push_back(q); } } } swap(vNew,vLast); l++; } while(!vLast->empty() && m[Exit]==INT_MAX); return (i=m[Exit])==INT_MAX?-1:i; } void generate(const int nIterations=10000){ assert(matrix::Width>1 && matrix::Height>1); matrix dist(matrix::Width,matrix::Height),temp(matrix::Width,matrix::Height); float best=0,s=1,r=0.05/(matrix::Width+matrix::Height); srand(time(NULL)); Entry=Point(matrix::Width/4,matrix::Height/4); Exit=Point(3*matrix::Width/4,3*matrix::Height/4); fill(1); for (int i=nIterations;i;i--){ int l=solve(dist),j; if (l+s*r>best){ best=l+s*r; copyTo(temp); } j=(l==-1); randomChange(j); s+=2*j-1; } temp.copyTo(*this); std::cout << *this; } point getEntry(void) const{ return Entry; } point getExit(void) const{ return Exit; } /* Voir gadget.h pour les méthodes FPSHint et command */ #include "gadget.h" /* Voir amine.h pour les méthodes initialize, reach et finalize */ #include "amine.h" /* Voir ghilaine.h pour la méthode draw */ #include "ghilaine.h" /* Voir philippe.h pour la méthode trace */ #include "philippe.h" }; std::ostream& operator << (std::ostream& Stream,laby& Laby){ #if defined(__WIN32__) || defined(WIN32) || defined(win32) || defined(_MSC_VER) char C[4][3]={{0xDB,0xDB,0},{' ',' ',0},{'I','n',0},{'O','u',0}}; #else char C[4][3]={{'@','@',0},{' ',' ',0},{'I','n',0},{'O','u',0}}; #endif point p; for (p.Y=-1;p.Y<=Laby.Height;p.Y++){ for (p.X=-1;p.X<=Laby.Width;p.X++){ int k=Laby[p]; if (p==Laby.Entry) k=2; if (p==Laby.Exit) k=3; Stream<<C[k]; } Stream<<"\n"; } return Stream; } #endif

Génération et stockage des objets 3D à partir de la carte planaire du labyrinthe (Amine Harzli):

//*constantes qui carecterise le labyrinthe--------------------- int WallTexIndex; void makeHorizontalQuad(int x1,int x2,int y,bool positive){ if (!positive){ quad q={{x1*LARGEUR,0,y*LARGEUR},{VX,VY,-VZ},(x2-x1)*LARGEUR,HAUTEUR,WallTexIndex,{{0,0},{x2-x1,1}},Vect(x1*LARGEUR,0,y*LARGEUR),Vect(x2*LARGEUR,HAUTEUR,y*LARGEUR)}; Faces.push_back(q); } else { // quad q={{x1*LARGEUR,0,y*LARGEUR},{VY,VX,VZ},HAUTEUR,(x2-x1)*LARGEUR,WallTexIndex,{{0,0},{1,x2-x1}},Vect(x1*LARGEUR,0,y*LARGEUR),Vect(x2*LARGEUR,HAUTEUR,y*LARGEUR)}; quad q={{x2*LARGEUR,0,y*LARGEUR},{-VX,VY,VZ},(x2-x1)*LARGEUR,HAUTEUR,WallTexIndex,{{x2-x1,0},{0,1}},Vect(x1*LARGEUR,0,y*LARGEUR),Vect(x2*LARGEUR,HAUTEUR,y*LARGEUR)}; Faces.push_back(q); } } void makeVerticalQuad(int x1,int x2,int y,bool positive){ if (positive){ quad q={{y*LARGEUR,0,x1*LARGEUR},{VZ,VY,VX},(x2-x1)*LARGEUR,HAUTEUR,WallTexIndex,{{0,0},{x2-x1,1}},Vect(y*LARGEUR,0,x1*LARGEUR),Vect(y*LARGEUR,HAUTEUR,x2*LARGEUR)}; Faces.push_back(q); } else { // quad q={{y*LARGEUR,0,x1*LARGEUR},{VY,VZ,-VX},HAUTEUR,(x2-x1)*LARGEUR,WallTexIndex,{{0,0},{1,x2-x1}},Vect(y*LARGEUR,0,x1*LARGEUR),Vect(y*LARGEUR,HAUTEUR,x2*LARGEUR)}; quad q={{y*LARGEUR,0,x2*LARGEUR},{-VZ,VY,-VX},(x2-x1)*LARGEUR,HAUTEUR,WallTexIndex,{{x2-x1,0},{0,1}},Vect(y*LARGEUR,0,x1*LARGEUR),Vect(y*LARGEUR,HAUTEUR,x2*LARGEUR)}; Faces.push_back(q); } } void makeFaces(void){ int j1,j2;//coordoneé de départ int coeff1,coeff2; bool b1,b2; for(int i=0;i<matrix::Height+1;i++){ //parcourir toute les ligne //dans cette boucle on trouvera les faces orienter horizontalement c-a-d les x positifs //k1=0;k2=0; b1=false;b2=false; j1=0;j2=0; for(int j=0;j<matrix::Width+1;j++){ coeff1=(*this)(j,i-1); coeff2=(*this)(j,i); if (coeff2==1 && coeff1==0){ if (!b2){ b2=true;j2=j; } } else { if (b2){ makeHorizontalQuad(j2,j,i,true); b2=false; } } if (coeff2==0 && coeff1==1){ if (!b1){ b1=true;j1=j; } } else { if (b1){ makeHorizontalQuad(j1,j,i,false); b1=false; } } } } for(int i=0;i<matrix::Width+1;i++){ b1=false;b2=false; j1=0;j2=0; for(int j=0;j<matrix::Height+1;j++){ coeff1=(*this)(i-1,j); coeff2=(*this)(i,j); if (coeff2==1 && coeff1==0){ if (!b2){ b2=true;j2=j; } } else { if (b2){ makeVerticalQuad(j2,j,i,true); b2=false; } } if (coeff2==0 && coeff1==1){ if (!b1){ b1=true;j1=j; } } else { if (b1){ makeVerticalQuad(j1,j,i,false); b1=false; } } } } } void initialize(vect& start,float& radius,float& speed,float& gravity,float& friction){ generate(); start=Vect((Entry.X+0.5)*LARGEUR,1,(Entry.Y+0.5)*LARGEUR); radius=1; speed=5; gravity=10; quad q={ {0,0,0}, {VX,VZ,VY}, matrix::Width*LARGEUR,matrix::Height*LARGEUR, loadTexture("textures/floor.bmp"), {{0,0},{matrix::Width,matrix::Height}} }; WallTexIndex=loadTexture("textures/wall.bmp"); loadSky(SKYTEXTUREPACK.c_str()); Faces.push_back(q); makeFaces(); ki(Exit.X,0,Exit.Y); ki(laby::getEntry().X,0,laby::getEntry().Y); } bool reach(const vect& position){ if((abs(int(position.X-(Exit.X+0.5)*LARGEUR))+abs(int(position.Z-(Exit.Y+0.5)*LARGEUR)))<1){ std::cout<<"félicitation\n"; return true; } else return false; } void finalize(void){ Faces.clear(); } void ki(int x,int y,int z){ glPushMatrix(); glTranslatef(x,y,z); glScalef(0.5,0.5,1); glutSolidCube(1); glPopMatrix(); } /* void SkyBox_CreatTexture(void) { int i=1; TexIDSkyBox[0] =CreateGLTexture("Data/ciel01.jpg"); TexIDSkyBox[1] =CreateGLTexture("Data/ciel01.jpg"); TexIDSkyBox[2] =CreateGLTexture("Data/ciel01.jpg"); TexIDSkyBox[3] =CreateGLTexture("Data/ciel01.jpg"); TexIDSkyBox[4] =CreateGLTexture("Data/ciel01.jpg"); TexIDSkyBox[5] =CreateGLTexture("Data/ciel01.jpg"); for(i=0;i<=5;++i) { glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[i]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 0x812F); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 0x812F); } } void SkyBox_Draw(float x, float y, float z, float width, float height, float length) { x = x - width / 2; // Calcul l'emplacement d'un coin du cube y = y - height / 2; z = z - length / 2; glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[BACK_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0f, 0.0f); glVertex3f(x,y,z); glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y,z); glTexCoord2f(0.0f, 1.0f); glVertex3f(x,y + height, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z); glEnd(); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[FRONT_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(1.0f, 0.0f); glVertex3f(x,y,z + length); glTexCoord2f(1.0f, 1.0f); glVertex3f(x,y + height, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z + length); glEnd(); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[BOTTOM_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(1.0f, 0.0f); glVertex3f(x,y,z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x,y, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y,z); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y,z + length); glEnd(); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[TOP_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(1.0f, 1.0f); glVertex3f(x,y + height,z); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z); glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y + height, z + length); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y + height, z + length); glEnd(); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[RIGHT_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(1.0f, 0.0f); glVertex3f(x, y, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x, y + height, z); glTexCoord2f(0.0f, 0.0f); glVertex3f(x, y, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x, y + height, z + length); glEnd(); glBindTexture(GL_TEXTURE_2D, TexIDSkyBox[LEFT_ID]); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0f, 0.0f); glVertex3f(x + width, y, z); glTexCoord2f(1.0f, 0.0f); glVertex3f(x + width, y, z + length); glTexCoord2f(0.0f, 1.0f); glVertex3f(x + width, y + height, z); glTexCoord2f(1.0f, 1.0f); glVertex3f(x + width, y + height, z + length); glEnd(); glDepthMask(GL_TRUE); glEnable(GL_DEPTH_TEST); }*/

Détection de collisions pour le moteur physique (Philippe Tigréat):

bool trace(const vect& from,const vect& direction,float* distance,vect* normal){ //prendre en entree la distance maximale entre deux points du labyrinthe, soit la longueur //dune diagonale. vect I; vect O; vect F; bool coupe=false; /* Par defaut, pas de collision detectee */ float n=norm(direction),bestdistance; vect d=direction/n; for (std::vector<quad>::iterator i=Faces.begin();i!=Faces.end(); ++i){ float D1=(from-(*i).Origin)*(*i).Base.W; float D2=(from+direction-(*i).Origin)*(*i).Base.W; if (/*(D1<0 && D2>0) ||*/ (D1>=0 && D2<0)){//teste que le vecteur direction atteint le plan de la face; if N/D<=n // O=(*i).Origin-from; // float N=fabs(O*(*i).Base.W); // float D=fabs(((*i).Base.W)*direction); float r=fabs(D1/(D1-D2)); I=from+direction*r; F=I-(*i).Origin; float A=(F*(*i).Base.U); float B=(F*(*i).Base.V); float a=(*i).SizeU; float b=(*i).SizeV; if (0<=A && A<=a && 0<=B && B<=b){//teste que l'intersection avec le plan est dans la face if (!coupe || r<bestdistance){//teste si une precedente face intersectee serait plus proche bestdistance=r; if (normal) *normal=(*i).Base.W; coupe=true; } } } } if (distance && coupe) *distance=bestdistance; return coupe; };

Représentation 3D des objets avec OpenGL et découpage adaptatif des facettes pour des calculs d’éclairement précis (Ghilaine Abo El Einein):

#ifndef __GHILAINE_h__ #define __GHILAINE_h__ /*static const float ZMIN=0.01; static const float ZMAX=6*larg;*/ vect quadVect(const quad& q, float x,float y){ return q.Origin + x*q.SizeU*q.Base.U+y*q.SizeV*q.Base.V; } void quadVertex(const quad& q, float x,float y){ vect v=quadVect(q,x,y); glTexCoord2f((1-x)*q.TextureCoord[0][0]+x*q.TextureCoord[1][0],(1-y)*q.TextureCoord[0][1]+y*q.TextureCoord[1][1]); glVertex3f(v.X,v.Y,v.Z); } vect projVect(const vect& v){ double d1,d2,d3; gluProject(v.X,v.Y,v.Z,Camera.Modelview,Camera.Projection,Camera.Viewport,&d1,&d2,&d3); return Vect(d1,d2,0); } void drawTriangle(const quad& q,const triangle t){ /* On donne les 3 sommets du triangle */ for (int i=0;i<3;++i) quadVertex(q,t.U[i],t.V[i]); } /* une variable qui permet de stocker les 6 plans de la pyramide de vision, le viewport et les matrices de projection/modelisation */ struct { /* Le type plane est defini dans common.h */ plane Planes[6]; double Projection[16],Modelview[16]; int Viewport[4]; } Camera; /* fonction qui se charge du decoupage */ void recDrawTriangle(const quad& q,const triangle& t,const int degree=0){ if (degree<6){ /* Dans le cas o degree<6 on decoupe le triangle t selon l'un des 6 plans de la pyramide de vision. Il ya trois cas a considerer */ triangle u=t,v=t; int i=0,j=2,k,m=0; float r,s[3]; for (int l=0;l<3;l++){ r=quadVect(q,t.U[l],t.V[l])*Camera.Planes[degree].Normal-Camera.Planes[degree].Dist; if (r<=0){ k=i++; m+=l; } else k=j--; s[k]=r; u.U[k]=t.U[l]; u.V[k]=t.V[l]; } assert(i==j+1); switch (i){ case 0: recDrawTriangle(q,t,degree+1); break; case 1: v=u; r=s[0]/(s[2]-s[0]); u.U[0]-=r*(u.U[2]-u.U[0]); u.V[0]-=r*(u.V[2]-u.V[0]); r=s[0]/(s[1]-s[0]); v.U[0]-=r*(v.U[1]-v.U[0]); v.V[0]-=r*(v.V[1]-v.V[0]); v.U[2]=u.U[0]; v.V[2]=u.V[0]; if (m!=1){ flip(v); flip(u); } recDrawTriangle(q,u,degree+1); recDrawTriangle(q,v,degree+1); break; case 2: r=s[0]/(s[2]-s[0]); u.U[0]-=r*(u.U[2]-u.U[0]); u.V[0]-=r*(u.V[2]-u.V[0]); r=s[1]/(s[2]-s[1]); u.U[1]-=r*(u.U[2]-u.U[1]); u.V[1]-=r*(u.V[2]-u.V[1]); if (m==2) flip(u); recDrawTriangle(q,u,degree+1); break; } } else { /* Dans le cas o degree>=6, c'est ici que ton travail commence: on utilise glutProject avec les 3 sommets du triangle t inclus dans la face q pour calculer les coordonnees de ces sommets sur l'ecran. Puis, tu calcules la longueur (bidimensionnelle avec les coordonnees x et y calculees apres projection) des 3 cotes du triangle. Supposons que le triangle s'appelle ABC et que son cote le plus long soit [AB]. Si la longueur de AB est plus petite que par exemple 50, tu traces le triangle ABC avec drawTriangle (puisque dans ce cas le triangle est "petit", inutile de le deccouper d'avantage). Si elle est plus grande, on note M le milieu du cote AB. Il suffit alors d'appeler recursivement 2 fois la fonction recDrawTriangle avec les triangles AMC et MBC (remarque qu'en faisant cela on tourne toujours dans le meme sens, donc que l'orientation des faces est conservee), et avec degree=degree+1. */ float dmax=0; int j=0; for (int i=0;i<3;++i){ vect p1=projVect(quadVect(q,t.U[i],t.V[i])); vect p2=projVect(quadVect(q,t.U[(i+1)%3],t.V[(i+1)%3])); float d=sqrt((p1.X-p2.X)*(p1.X-p2.X)+(p1.Y-p2.Y)*(p1.Y-p2.Y)); if (d>dmax){ j=i; dmax=d; } } if (dmax<Quality || degree>MAXCUTDEGREE){ drawTriangle(q,t); } else { triangle u=t; triangle v=t; u.U[(j+1)%3]=(t.U[j]+t.U[(j+1)%3])*0.5; u.V[(j+1)%3]=(t.V[j]+t.V[(j+1)%3])*0.5; v.U[j]=(t.U[j]+t.U[(j+1)%3])*0.5; v.V[j]=(t.V[j]+t.V[(j+1)%3])*0.5; recDrawTriangle(q,u,degree+1); recDrawTriangle(q,v,degree+1); } } } /* Methode qui declenche l'affichage de la scene depuis une camera perspective planaire situee a la position eye, regardant dans la direction center. Doit etre appelee dans le meme contexte OpenGL que celui qui a servi pour l'initialisation. + viewport est la zone de trace exprimee en pixels dans la surface OpenGL associee au contexte courant; + angle est l'angle vertical de la camera; + ratio est le rapport largeur/hauteur de la camera; + eye est la position de la camera dans le repere global; + center est une position visee par la camera dans le repere global; + up est un vecteur qui definit la verticale du plan de la camera dans le repere global. */ void draw(const rect& viewPort,const float angle,const float ratio,const vect& eye,const vect& center,const vect& up){ glViewport(viewPort.X,viewPort.Y,viewPort.Width,viewPort.Height); drawSky(angle,ratio,eye,center,up); glMatrixMode(GL_MODELVIEW); // Remise à zéro des 2 matrices glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glEnable(GL_COLOR_MATERIAL); // activation du modèle de couleur simplifié glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); glColor3f(1,1,1); // couleur de base blanche glEnable(GL_LIGHT0); // activation de la lampe 0 float pos[4]={0,0,0,1}; glLightfv(GL_LIGHT0,GL_POSITION,pos); // position de la lampe, spécifiée AVANT la matrice de projection/visualisation (pour l'effet "lampe torche") glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1); // mode d'éclairement plus précis (mais plus lent) glEnable(GL_LIGHTING); // activation de l'eclairage glEnable(GL_DEPTH_TEST); // Activation du test de profondeur glEnable(GL_CULL_FACE); // Backface culling glMatrixMode(GL_PROJECTION); // Définition de la caméra gluPerspective(angle,ratio,ZMIN,ZMAX); glMatrixMode(GL_MODELVIEW); gluLookAt(eye.X,eye.Y,eye.Z,center.X,center.Y,center.Z,up.X,up.Y,up.Z); /* Definition des 6 plans de decoupage de la pyramide de vision */ base b=Base(center-eye,up); Camera.Planes[0]=Plane(b.W,eye,ZMIN); Camera.Planes[1]=Plane(-b.W,eye,-1*ZMAX); Camera.Planes[2]=Plane(b.W+1/tan(angle*PI/360)*b.V,eye); Camera.Planes[3]=Plane(b.W-1/tan(angle*PI/360)*b.V,eye); Camera.Planes[4]=Plane(b.W+1/tan(angle*PI/360)/ratio*b.U,eye); Camera.Planes[5]=Plane(b.W-1/tan(angle*PI/360)/ratio*b.U,eye); /* On recupere les matrices et le viewport */ glGetDoublev(GL_PROJECTION_MATRIX,Camera.Projection); glGetDoublev(GL_MODELVIEW_MATRIX,Camera.Modelview); glGetIntegerv(GL_VIEWPORT,Camera.Viewport); if (CUTQUADS){ for(std::vector<quad>::iterator i=Faces.begin();i!=Faces.end();++i){ glBindTexture(GL_TEXTURE_2D,(*i).Texture); glBegin(GL_TRIANGLES); glNormal3f((*i).Base.W.X,(*i).Base.W.Y,(*i).Base.W.Z); /* La constante T1 est un triangle qui correspond a un demi-rectangle decoupe selon l'une de ses diagonales, elle est definie dans common.h */ recDrawTriangle(*i,T1,0); /* La constante T2 correspond au demi-rectangle complementaire */ recDrawTriangle(*i,T2,0); glEnd(); } } else { for(std::vector<quad>::iterator i=Faces.begin();i!=Faces.end();++i){ glBindTexture(GL_TEXTURE_2D,(*i).Texture); glNormal3f((*i).Base.W.X,(*i).Base.W.Y,(*i).Base.W.Z); glBegin(GL_QUADS); quadVertex(*i,0,0); quadVertex(*i,0,1); quadVertex(*i,1,1); quadVertex(*i,1,0); glEnd(); } } drawBeacons(); if (SHOWARROW) drawArrow(eye,center,up); if (SHOWMINIMAP) drawMiniMap(viewPort,eye,center); } #endif

Affichage du ciel, du brouillard, de la boussole et de la carte (fichier fourni aux étudiants):

std::string SKYTEXTUREPACK; float LARGEUR,HAUTEUR,ZMIN,ZMAX,MAXQUALITY; int MAXCUTDEGREE; bool CUTQUADS,WIREFRAME,USETEXTURE,SHOWSKY,SHOWARROW,SHOWMINIMAP; int SkyTexIndex[5]; float FogColor[3],Quality; void command(const int index){ switch(index){ case 1: CUTQUADS^=true; break; case 2: WIREFRAME^=true; break; case 3: USETEXTURE^=true; break; case 4: SHOWSKY^=true; break; case 5: SHOWARROW^=true; break; case 6: SHOWMINIMAP^=true; break; } } void FPSHint(const float ratio){ if (!CUTQUADS){ Quality=1000; return; } if (ratio>1) Quality/=sqrt(ratio); else Quality/=ratio; if (Quality<MAXQUALITY) Quality=MAXQUALITY; if (Quality>1000) Quality=1000; } void loadParams(const char* filename){ inifile f(filename); f.read("Sky","TexturePack",SKYTEXTUREPACK,"textures\\red"); f.read("Dimensions","Largeur",LARGEUR,3); f.read("Dimensions","Hauteur",HAUTEUR,2); f.read("Camera","ZMIN",ZMIN,0.1); f.read("Camera","ZMAX",ZMAX,20); f.read("Camera","MaxQuality",MAXQUALITY,50); f.read("Camera","MaxCutDegree",MAXCUTDEGREE,15); f.read("Features","CutQuads",CUTQUADS,true); f.read("Features","WireFrame",WIREFRAME,false); f.read("Features","UseTexture",USETEXTURE,true); f.read("Features","Sky",SHOWSKY,true); f.read("Features","Arrow",SHOWARROW,true); f.read("Features","MiniMap",SHOWMINIMAP,false); Quality=1000; } void saveParams(const char* filename){ inifile f; f.write("Sky","TexturePack",SKYTEXTUREPACK); f.write("Dimensions","Largeur",LARGEUR); f.write("Dimensions","Hauteur",HAUTEUR); f.write("Camera","ZMIN",ZMIN); f.write("Camera","ZMAX",ZMAX); f.write("Camera","MaxQuality",MAXQUALITY); f.write("Camera","MaxCutDegree",MAXCUTDEGREE); f.write("Features","CutQuads",CUTQUADS); f.write("Features","WireFrame",WIREFRAME); f.write("Features","UseTexture",USETEXTURE); f.write("Features","Sky",SHOWSKY); f.write("Features","Arrow",SHOWARROW); f.write("Features","MiniMap",SHOWMINIMAP); f.writeFile(filename); } void loadSky(const char* prefix){ bitmap<unsigned char> images[5]; char* names[5]={ "_up.bmp", "_north.bmp", "_west.bmp", "_south.bmp", "_east.bmp" }; RGB<float> q; float h=HAUTEUR*0.5/ZMAX; float s=0; for (int i=0;i<3;++i) FogColor[i]=0; for (int i=0;i<5;++i){ try { images[i].loadFile((std::string(prefix)+names[i]).c_str()); } catch(...){ std::cout << "Error while loading " << (std::string(prefix)+names[i]).c_str() << std::endl; } } for (int i=1;i<5;++i){ for (int u=images[i].getWidth()-1;u>=0;--u){ float r=(2.*u)/(images[i].getWidth()-1)-1; r=sqrt(1+r*r); int h1=(int) ((0.5+h*r)*images[i].getHeight()); int h2=(int) ((0.5+(h+0.1)*r)*images[i].getHeight()); for (int v=h1;v<=h2;++v){ RGB<unsigned char> p=images[i](u,v); float r=((float) (v-h1)/(h2-h1-1)); r=0.5*(1+cos(PI*r)); for (int j=0;j<3;++j) FogColor[j]+=r*p[j]; s+=r; } } } for (int i=0;i<3;++i){ q[i]=FogColor[i]/s; FogColor[i]/=(s*255); } for (int i=1;i<5;++i){ for (int u=images[i].getWidth()-1;u>=0;--u){ float r=(2.*u)/(images[i].getWidth()-1)-1; r=sqrt(1+r*r); int h1=(int) ((0.5+h*r)*images[i].getHeight()); int h2=(int) ((0.5+(h+0.1)*r)*images[i].getHeight()); for (int v=0;v<h2;++v) if (v<h1) images[i](u,v)=q; else{ float r=((float) (v-h1)/(h2-h1-1)); RGB<float> p; r=0.5*(1-cos(PI*r)); p=images[i](u,v); p=(1-r)*q+r*p; images[i](u,v)=p; } } } for (int i=0;i<5;++i) SkyTexIndex[i]=loadTexture(images[i]); } void drawSky(const float angle,const float ratio,const vect& eye,const vect& center,const vect& up){ const float D=1; vect u=center-eye; vect Vertices[5][3]={ {{-D,D,-D},{2*D,0,0},{0,0,2*D}}, {{D,-D,D},{-2*D,0,0},{0,2*D,0}}, {{D,-D,-D},{0,0,2*D},{0,2*D,0}}, {{-D,-D,-D},{2*D,0,0},{0,2*D,0}}, {{-D,-D,D},{0,0,-2*D},{0,2*D,0}} }; float Map[4][2]={ {0,0}, {0,1}, {1,1}, {1,0} }; /* Paramètres pour le brouillard */ glFogfv(GL_FOG_COLOR,FogColor); // Couleur glFogf(GL_FOG_START,0.7*ZMAX); // Début du brouillard glFogf(GL_FOG_END,ZMAX); // Fin du brouillard glFogi(GL_FOG_MODE,GL_LINEAR); // Type de variation glEnable(GL_FOG); // Activation if (USETEXTURE) glEnable(GL_TEXTURE_2D); // Activation des textures else glDisable(GL_TEXTURE_2D); // Activation des textures if (WIREFRAME) glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); if (!SHOWSKY){ glDisable(GL_FOG); return; } glPushAttrib(GL_ALL_ATTRIB_BITS); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); gluLookAt(0,0,0,u.X,u.Y,u.Z,up.X,up.Y,up.Z); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPerspective(angle,ratio,0.1,100*D); glEnable(GL_TEXTURE_2D); glDisable(GL_LIGHTING); glDisable(GL_CULL_FACE); glDisable(GL_FOG); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glColor3f(0,0,0); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); for (int i=0;i<5;++i){ glBindTexture(GL_TEXTURE_2D,SkyTexIndex[i]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); glBegin(GL_QUADS); for (int j=0;j<4;j++){ vect v=Vertices[i][0]+Map[j][0]*Vertices[i][1]+Map[j][1]*Vertices[i][2]; glTexCoord2f(Map[j][0],Map[j][1]); glVertex3f(v.X,v.Y,v.Z); } glEnd(); } glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); } void drawMiniMap(const rect& win,const vect& eye,const vect& center){ vect u=center/LARGEUR,v=center-eye; point p; float o=atan2(v.Z,v.X),c=0.5*cos(o),s=0.5*sin(o); int w=(int) (win.Height*0.5),h=(int) (win.Height*0.5*matrix::Height/matrix::Width); glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glDisable(GL_FOG); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glViewport(win.X+win.Width-w-10,win.Y+win.Height-h-10,w,h); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(-0.5,matrix::Width+0.5,matrix::Height+0.5,-0.5); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glColor3f(0.5,0.5,0.7); glRecti(0,0,matrix::Width,matrix::Height); glColor3f(0.5,0,0); for (p.X=-1;p.X<=matrix::Width;p.X++) for (p.Y=-1;p.Y<=matrix::Height;p.Y++) if (!(*this)[p]){ glRecti(p.X,p.Y,p.X+1,p.Y+1); } glPointSize(3); glBegin(GL_POINTS); glColor3f(0,1,0); glVertex2f(Exit.X+0.5,Exit.Y+0.5); glColor3f(0,0,1); glVertex2f(Entry.X+0.5,Entry.Y+0.5); glEnd(); glColor3f(1,0.8,0); glBegin(GL_LINE_STRIP); glVertex2f(u.X-c-0.6*s,u.Z-s+0.6*c); glVertex2f(u.X,u.Z); glVertex2f(u.X-c+0.6*s,u.Z-s-0.6*c); glEnd(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } void drawArrow(const vect& eye,const vect& center,const vect& up){ vect u=!(center-eye),v=eye+0.5*LARGEUR*u; float o=atan2(eye.X-LARGEUR*(Exit.X+0.5),eye.Z-LARGEUR*(Exit.Y+0.5)); glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_BLEND); glDisable(GL_FOG); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(0,-4,-8); gluLookAt(0,0,0,u.X,u.Y,u.Z,up.X,up.Y,up.Z); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.9,0.2,0.1,0.4); glRotatef(180+o*180/PI,0,1,0); glTranslatef(0,0,1.5); glPushMatrix(); glScalef(0.1,0.1,3); glTranslatef(0,0,-0.5); glutSolidCube(1); glPopMatrix(); glPushMatrix(); glRotatef(45,0,1,0); glScalef(0.1,0.1,1.5); glTranslatef(0,0,-0.5); glutSolidCube(1); glPopMatrix(); glPushMatrix(); glRotatef(-45,0,1,0); glScalef(0.1,0.1,1.5); glTranslatef(0,0,-0.5); glutSolidCube(1); glPopMatrix(); glPopMatrix(); glPopAttrib(); } void drawBeacon(const point& position){ glPushAttrib(GL_ALL_ATTRIB_BITS); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef((position.X+0.5)*LARGEUR,0,(position.Y+0.5)*LARGEUR); glPushMatrix(); glRotatef(-90,1,0,0); glutSolidCone(0.2,0.8,20,20); glPopMatrix(); glPushMatrix(); glTranslatef(0,0.8,0); glutSolidSphere(0.15,20,20); glPopMatrix(); glPopMatrix(); glPopAttrib(); } void drawBeacons(void){ glColor3f(0,0,1); drawBeacon(Entry); glColor3f(0,1,0); drawBeacon(Exit); }

Serveur d’affichage sur écran plat (fichier fourni aux étudiants):

#ifndef __engine_h__ #define __engine_h__ #include <vector> #include <cassert> #include <cmath> #include <cstdio> #include "glut_import.h" #include "world.h" #include "common.h" /********************************************************************* * CONSISTENT __WIN32__ DEFINE *********************************************************************/ #ifndef __WIN32__ # if defined(__WIN__) || defined(WIN32) || defined(__WINDOWS__) || defined(_MSC_VER) # define __WIN32__ # endif #endif /********************************************************************* * CONSTANTS *********************************************************************/ #define INVALID_WINDOW_INDEX (-1) #define H_THICK_SAMPLE_COUNT 5 #define V_THICK_SAMPLE_COUNT 5 #define TANGENT_MOVE_REC_LEVEL 4 #ifndef __BORLANDC__ # define MOVE_LEFT 0x1 # define MOVE_RIGHT 0x2 # define MOVE_UP 0x4 # define MOVE_DOWN 0x8 #endif #define FPS_COUNTER_TRIGGER 0x07 #define FPS_COUNTER_SMOOTH 0.3 /********************************************************************* * CLASS ENGINE *********************************************************************/ class engine { friend void reshapeFunc(int,int); friend void displayFunc(void); friend void mouseFunc(int,int,int,int); friend void passiveMotionFunc(int,int); friend void keyboardFunc(unsigned char,int,int); friend void specialFunc(int,int,int); #ifndef __BORLANDC__ friend void specialUpFunc(int,int,int); #endif friend void idleFunc(void); private: typedef struct { vect Position,Speed; } particle; static std::vector<particle> Particles; typedef struct { struct { int Index,Width,Height; bool FullScreen,Cube; #ifndef __BORLANDC__ int Move; #endif } Window; struct { vect Position,Speed; float Theta,Phi,Thickness,WalkingSpeed,Gravity,Friction; } Actor; struct { int Tick,TickTime; float FPS; } Time; world* World; } enginedata; static enginedata Data; static void registerCallbacks(void); static void thick(const base& b,const float x,const float y,vect& side,float& forward){ float i=x*x,j=y*y,r=i<j?1/sqrt(1+i/(j+0.0000001)):1/sqrt(1+j/(i+0.0000001)),u=x*r,v=y*r; side=(u*b.U+v*b.V)*Data.Actor.Thickness; forward=sqrt(1.0000001-u*u-v*v)*Data.Actor.Thickness; } static bool thickTrace(const vect& position,const base& b,float& distance,vect& normal){ float first=distance; bool result=false; for (int i=-H_THICK_SAMPLE_COUNT;i<=H_THICK_SAMPLE_COUNT;i++) for (int j=-V_THICK_SAMPLE_COUNT;j<=V_THICK_SAMPLE_COUNT;j++){ vect intersect,side,n; float r,forward,x=0.99*i/H_THICK_SAMPLE_COUNT,y=0.99*j/V_THICK_SAMPLE_COUNT; thick(b,x,y,side,forward); if (Data.World->trace(position+side,(forward+distance)*b.W,&r,&n)){ r=r*(forward+distance)-forward; if (r<first || !result){ result=true; normal=n; first=r; } } } distance=first; return result; } static bool moveRec(const int degree,const vect& delta,float distance,const bool friction){ base b=Base(delta); vect normal,v; float r=distance; if (thickTrace(Data.Actor.Position,b,r,normal) && degree){ Data.Actor.Position+=r*b.W; if (friction && fabs(normal*delta)>Data.Actor.Friction) return true; v=b.W-(b.W*normal)*normal; distance*=norm(v); return moveRec(degree-1,v,distance,friction); } else Data.Actor.Position+=r*b.W; return false; } static bool move(const vect& delta,const float distance,const bool friction){ bool b=moveRec(TANGENT_MOVE_REC_LEVEL,delta,distance,friction); glutPostRedisplay(); if (Data.World->reach(Data.Actor.Position)) recreateWindow(); return b; } static bool move(const float theta,const float distance,const bool friction){ return move(Vect(sin(theta),0,cos(theta)),distance,friction); } static void createWindow(void){ int a=800,b=600; int x=glutGet(GLUT_SCREEN_WIDTH); int y=glutGet(GLUT_SCREEN_HEIGHT); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_MULTISAMPLE | GLUT_DEPTH); glutInitWindowPosition((x-a)/2,(y-b)/2); glutInitWindowSize(a,b); #ifndef __BORLANDC__ if (Data.Window.FullScreen && glutGet(GLUT_GAME_MODE_POSSIBLE)){ Data.Window.Index=glutEnterGameMode(); glutSetCursor(GLUT_CURSOR_NONE); } else #endif { Data.Window.Index=glutCreateWindow("Reality engine"); #ifdef __BORLANDC__ if (Data.Window.FullScreen){ glutFullScreen(); glutSetCursor(GLUT_CURSOR_NONE); } else #endif glutSetCursor(GLUT_CURSOR_CROSSHAIR); } registerCallbacks(); #ifndef __BORLANDC__ Data.Window.Move=0; #endif Data.World->initialize(Data.Actor.Position,Data.Actor.Thickness,Data.Actor.WalkingSpeed,Data.Actor.Gravity,Data.Actor.Friction); } static void destroyWindow(void){ if (Data.Window.Index!=INVALID_WINDOW_INDEX){ Data.World->finalize(); glutDestroyWindow(Data.Window.Index); Data.Window.Index=INVALID_WINDOW_INDEX; } } static void recreateWindow(void){ destroyWindow(); createWindow(); } static void draw(const rect r,const float theta,const float phi,const float angle,const float ratio,bool clip){ float cPhi=cos(phi+Data.Actor.Phi),cTheta=cos(Data.Actor.Theta), sPhi=sin(phi+Data.Actor.Phi),sTheta=sin(Data.Actor.Theta); vect up=Vect(-sPhi*sTheta,cPhi,-sPhi*cTheta), x=Vect(cTheta,0,-sTheta), z=Vect(cPhi*sTheta,sPhi,cPhi*cTheta), aim=cos(theta)*z+sin(theta)*x; if (clip){ glScissor(r.X,r.Y,r.Width,r.Height); glEnable(GL_SCISSOR_TEST); glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT); } Data.World->draw(r,angle,ratio,Data.Actor.Position,Data.Actor.Position+aim,up); glDisable(GL_TEXTURE_2D); glMatrixMode(GL_MODELVIEW); for (int i=0;i<Particles.size();i++){ particle& p=Particles[i]; glColor3f(1,0,0); glPushMatrix(); glTranslatef(p.Position.X,p.Position.Y,p.Position.Z); glutSolidSphere(0.1,10,10); glPopMatrix(); } } static void onReshape(const int width,const int height){ Data.Window.Width=width; Data.Window.Height=height; } static void onDisplay(void){ glDisable(GL_SCISSOR_TEST); if (Data.Window.Cube){ rect r={0,0,(Data.Window.Width/3-1)-1,(Data.Window.Height-1)/3-1}; glClearColor(0,1,0,0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); for (int i=-1;i<=1;i++) for (int j=-1;j<=1;j++) if (!i || !j){ r.X=1+(i+1)*(r.Width+1); r.Y=1+(j+1)*(r.Height+1); draw(r,-i*0.5*PI,j*0.5*PI,90,1,true); } } else { rect r={0,0,Data.Window.Width,Data.Window.Height}; glClearColor(0,0,0,0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(r,0,0,70,(float) Data.Window.Width/Data.Window.Height,false); } glFlush(); glutSwapBuffers(); } static void onMouse(const int button,const int state,const int x,const int y){ float cPhi=cos(Data.Actor.Phi),cTheta=cos(Data.Actor.Theta), sPhi=sin(Data.Actor.Phi),sTheta=sin(Data.Actor.Theta); particle p={ {Data.Actor.Position.X,Data.Actor.Position.Y,Data.Actor.Position.Z}, {6*cPhi*sTheta,15*sPhi,6*cPhi*cTheta} }; if (state==GLUT_DOWN) switch (button){ case GLUT_LEFT_BUTTON: Particles.push_back(p); break; case GLUT_RIGHT_BUTTON: Particles.clear(); break; } } static void onPassiveMotion(int x,int y){ #ifdef __WIN32__ int w=Data.Window.Width/2,h=Data.Window.Height/2; if (x!=w || y!=h){ Data.Actor.Theta-=0.005*(x-w); Data.Actor.Phi-=0.003*(y-h); if (Data.Actor.Phi<-0.5*PI) Data.Actor.Phi=-0.5*PI; if (Data.Actor.Phi>0.5*PI) Data.Actor.Phi=0.5*PI; SetCursorPos(glutGet(GLUT_WINDOW_X)+w,glutGet(GLUT_WINDOW_Y)+h); } #else Data.Actor.Theta=PI-PI*(2*x-Data.Window.Width)/Data.Window.Width; Data.Actor.Phi=-PI*0.5*(2*y-Data.Window.Height)/Data.Window.Height; #endif glutPostRedisplay(); } static void onKeyboard(const unsigned char key,const int x,const int y){ if (key>='1' && key<='9') Data.World->command(key-'0'); switch(key){ case ' ': case '0': Data.Actor.Speed=6*VY; break; case 'c': case 'C': Data.Window.Cube=!Data.Window.Cube; break; case 'f': case 'F': Data.Window.FullScreen=!Data.Window.FullScreen; destroyWindow(); createWindow(); break; case 27: case 'q': case 'Q': exit(0); } } static void onSpecial(const int key,const int x,const int y){ #ifdef __BORLANDC__ switch(key){ case GLUT_KEY_LEFT: move(Data.Actor.Theta+0.5*PI,0.1,false); break; case GLUT_KEY_RIGHT: move(Data.Actor.Theta-0.5*PI,0.1,false); break; case GLUT_KEY_UP: move(Data.Actor.Theta,0.1,false); break; case GLUT_KEY_DOWN: move(Data.Actor.Theta+PI,0.1,false); break; } #else switch(key){ case GLUT_KEY_LEFT: Data.Window.Move|=MOVE_LEFT; break; case GLUT_KEY_RIGHT: Data.Window.Move|=MOVE_RIGHT; break; case GLUT_KEY_UP: Data.Window.Move|=MOVE_UP; break; case GLUT_KEY_DOWN: Data.Window.Move|=MOVE_DOWN; break; } #endif } #ifndef __BORLANDC__ static void onSpecialUp(const int key,const int x,const int y){ switch(key){ case GLUT_KEY_LEFT: Data.Window.Move&=~MOVE_LEFT; break; case GLUT_KEY_RIGHT: Data.Window.Move&=~MOVE_RIGHT; break; case GLUT_KEY_UP: Data.Window.Move&=~MOVE_UP; break; case GLUT_KEY_DOWN: Data.Window.Move&=~MOVE_DOWN; break; } } #endif static void onIdle(void){ if ((++Data.Time.Tick & FPS_COUNTER_TRIGGER)==FPS_COUNTER_TRIGGER){ char s[128]; int t=glutGet(GLUT_ELAPSED_TIME); float f=1000.*(1+FPS_COUNTER_TRIGGER)/(t+1-Data.Time.TickTime); Data.Time.FPS=(1-FPS_COUNTER_SMOOTH)*f+FPS_COUNTER_SMOOTH*Data.Time.FPS; Data.World->FPSHint(Data.Time.FPS/40); if (Data.Time.FPS<1) Data.Time.FPS=1; Data.Time.TickTime=t; sprintf(s,"Reality engine (%f FPS)",Data.Time.FPS); glutSetWindowTitle(s); } Data.Actor.Speed-=Data.Actor.Gravity*VY/Data.Time.FPS; if (move(Data.Actor.Speed,norm(Data.Actor.Speed)/Data.Time.FPS,true)) Data.Actor.Speed=VO; #ifndef __BORLANDC__ vect v=VO; if (Data.Window.Move){ float cTheta=cos(Data.Actor.Theta),sTheta=sin(Data.Actor.Theta); vect forward=Vect(sTheta,0,cTheta); vect right=Vect(-cTheta,0,sTheta); int T[3][4]={{MOVE_LEFT,MOVE_RIGHT,MOVE_UP,MOVE_DOWN},{-1,1,0,0},{0,0,1,-1}}; for (int i=0;i<4;i++) if (T[0][i] & Data.Window.Move) v+=right*T[1][i]+forward*T[2][i]; } move(v,norm(v)*Data.Actor.WalkingSpeed/Data.Time.FPS,false); #endif for (int i=0;i<Particles.size();i++){ particle& p=Particles[i]; p.Speed-=Data.Actor.Gravity*VY/Data.Time.FPS; vect normal,w=(!p.Speed)*0.1+p.Speed/Data.Time.FPS; float distance; if (Data.World->trace(p.Position,w,&distance,&normal)){ p.Position+=distance*w-0.1*(!p.Speed); p.Speed-=2*p.Speed*normal*normal; } else { p.Position+=p.Speed/Data.Time.FPS; } } } public: static void init(int* argc,char* argv[]){ glutInit(argc,argv); } static void launch(world* World){ Data.World=World; createWindow(); glutMainLoop(); } }; engine::enginedata engine::Data={ {INVALID_WINDOW_INDEX,1,1, false,false #ifndef __BORLANDC__ ,0 #endif }, {{0,2,5},{0,0,0},0,0,1,1,10,0.5}, {0,0,1}, NULL }; std::vector<engine::particle> engine::Particles; /********************************************************************* * CALLBACKS *********************************************************************/ void reshapeFunc(int width,int height){ engine::onReshape(width,height); } void displayFunc(void){ engine::onDisplay(); } void mouseFunc(int button,int state,int x,int y){ engine::onMouse(button,state,x,y); }; void passiveMotionFunc(int x,int y){ engine::onPassiveMotion(x,y); } void keyboardFunc(unsigned char key,int x,int y){ engine::onKeyboard(key,x,y); } void specialFunc(int key,int x,int y){ engine::onSpecial(key,x,y); } #ifndef __BORLANDC__ void specialUpFunc(int key,int x,int y){ engine::onSpecialUp(key,x,y); } #endif void idleFunc(void){ engine::onIdle(); } /********************************************************************* * ENGINE EXTERNAL *********************************************************************/ void engine::registerCallbacks(void){ glutReshapeFunc(reshapeFunc); glutDisplayFunc(displayFunc); glutMouseFunc(mouseFunc); glutPassiveMotionFunc(passiveMotionFunc); glutKeyboardFunc(keyboardFunc); glutSpecialFunc(specialFunc); #ifndef __BORLANDC__ glutSpecialUpFunc(specialUpFunc); #endif glutIdleFunc(idleFunc); } #endif
Dernière modification le 18/3/2010
Ce document a été traduit de LaTeX par HeVeA