Détails de l´implémentation
Partie cliente imposée pour la compatibilité avec le serveur d’affichage (fichier fourni aux étudiants):
Fichier : 2007/projet/world.h #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): Fichier : 2007/projet/laby.h #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): Fichier : 2007/projet/amine.h //*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): Fichier : 2007/projet/philippe.h 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): Fichier : 2007/projet/ghilaine.h #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): Fichier : 2007/projet/gadget.h 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): Fichier : 2007/projet/engine.h #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 Ce document a été traduit de LaTeX par HeVeA |