Cube
- Pour "passer" 3D (en 2D il s'agit en fait de 2D en z = 0), faire les
modifications suivantes :
void CTutorialView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
GLsizei width, height;
GLdouble aspect;
width = cx;
height = cy;
if (cy==0)
aspect = (GLdouble)width;
else
aspect = (GLdouble)width/(GLdouble)height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, aspect, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDrawBuffer(GL_BACK);
glEnable(GL_DEPTH_TEST);
}
void CTutorialView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
static DWORD last_t = GetTickCount();
DWORD t = GetTickCount() - last_t;
if (t == 0)
return;
last_t = GetTickCount();
glMatrixMode(GL_MODELVIEW);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
...
Il y a des fonctions commencant par "gl" : ce sont les fonctions des bases.
Comme certaines operations, comme le calcul d'une matrice de projection en perspective,
sont courantes et fastidieuses, des fonctions gl utilities dechargent par exemple le
programmeur de calculs matriciels complexes. Elles commencent par "glu", et
appelent des fonctions "gl" (elles n'apportent rien techniquement). A titre
d'indication, une autre extension, glut, offre des fonctions pour ecrire un programme
marchant sur n'importe quelle plateforme sans aucune modification de code. Le probleme est
que le support est limite a l'ouverture d'une fenetre, gestion menu, clavier et souris et
surement d'autres choses (je n'ai pas etudie en details).
La fonction glEnable est utilisee tres souvent, elle permet comme son nom l'indique
d'activer des options. Son oppose est glDisable. Le depth-test est comme son nom l'indique
un test de profondeur : un point n'est pas affiche lorsqu'un autre, plus pres de la vue, a
ete affiche.
- Le cube va reservir dans plusieurs etapes de ce tutorial, une classe de cube est loin
d'etre superflue. Voici la declaration de la classe (cube.h) avec tous les messages
limpides de Visual :
// Cube.h: interface for the CCube class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_CUBE_H__DA15C8C2_3D38_11D3_A9DB_525400E10345__INCLUDED_)
#define AFX_CUBE_H__DA15C8C2_3D38_11D3_A9DB_525400E10345__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class CCube
{
public:
CCube(double r = 1, double x = 0, double y = 0, double z = 0, double rx
= 0, double ry = 0, double rz = 0, double vx = 0, double vy = 0, double vz = 0, double vrx
= 0, double vry = 0, double vrz = 0, bool changeable = false);
virtual ~CCube();
void Update(int t);
// calculer nouvelle position du cube apres t millisecondes
void Draw(float x, float y, float z); //
afficher le cube
float m_r;
// rayon du cube
double m_x,m_y,m_z;
// position
double m_vx,m_vy,m_vz;
// vitesse de translation
double m_rx, m_ry, m_rz;
// angles
double m_vrx,m_vry,m_vrz;
// vitesse de rotation
protected:
bool m_changeable;
// rayon du cube variable ?
GLint m_num_list;
// liste pour afficher le cube
};
#endif // !defined(AFX_CUBE_H__DA15C8C2_3D38_11D3_A9DB_525400E10345__INCLUDED_)
Cube.cpp :
// Cube.cpp: implementation of the CCube class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Cube.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCube::CCube(double r, double x, double y, double z, double rx, double ry, double rz,
double vx, double vy, double vz, double vrx, double vry, double vrz, bool changeable)
{
m_r = float(r);
m_rx = rx;
m_ry = ry;
m_rz = rz;
m_x = x;
m_y = y;
m_z = z;
m_vrx = vrx;
m_vry = vry;
m_vrz = vrz;
m_vx = vx;
m_vy = vy;
m_vz = vz;
m_changeable = changeable;
if (!m_changeable)
{
m_num_list = glGenLists(1);
glNewList(m_num_list, GL_COMPILE);
glBegin(GL_POLYGON);
glVertex3d(-m_r, m_r, m_r);
glVertex3d(-m_r, -m_r, m_r);
glVertex3d( m_r, -m_r, m_r);
glVertex3d( m_r, m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( m_r, m_r, -m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( -m_r, m_r, -m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( m_r, m_r, m_r);
glVertex3d( m_r, -m_r, m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( m_r, m_r, -m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, m_r, m_r);
glVertex3d( -m_r, m_r, -m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( -m_r, -m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, -m_r, m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( m_r, -m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, m_r, m_r);
glVertex3d( m_r, m_r, m_r);
glVertex3d( m_r, m_r, -m_r);
glVertex3d( -m_r, m_r, -m_r);
glEnd();
glEndList();
}
}
CCube::~CCube()
{
}
void CCube::Draw(float x, float y, float z)
{
glPushMatrix();
glTranslated(m_x - x, m_y - y, m_z - z);
glRotated(m_rx, 1.0, 0.0, 0.0);
glRotated(m_ry, 0.0, 1.0, 0.0);
glRotated(m_rz, 0.0, 0.0, 1.0);
if (m_changeable)
{
glBegin(GL_POLYGON);
glVertex3d(-m_r, m_r, m_r);
glVertex3d(-m_r, -m_r, m_r);
glVertex3d( m_r, -m_r, m_r);
glVertex3d( m_r, m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( m_r, m_r, -m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( -m_r, m_r, -m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( m_r, m_r, m_r);
glVertex3d( m_r, -m_r, m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( m_r, m_r, -m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, m_r, m_r);
glVertex3d( -m_r, m_r, -m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( -m_r, -m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, -m_r, m_r);
glVertex3d( -m_r, -m_r, -m_r);
glVertex3d( m_r, -m_r, -m_r);
glVertex3d( m_r, -m_r, m_r);
glEnd();
glBegin(GL_POLYGON);
glVertex3d( -m_r, m_r, m_r);
glVertex3d( m_r, m_r, m_r);
glVertex3d( m_r, m_r, -m_r);
glVertex3d( -m_r, m_r, -m_r);
glEnd();
}
else
glCallList(m_num_list);
glPopMatrix();
}
void CCube::Update(int t)
{
m_x += m_vx * t;
m_y += m_vy * t;
m_z += m_vz * t;
m_rx += m_vrx * t;
m_ry += m_vry * t;
m_rz += m_vrz * t;
}
Ceci n'est pas la premiere version de la classe, alors voici quelques explications :
- les listes : lorsqu'une suite de commandes est fixe, il est possible de creer des
listes. Cela presente plusieurs interet, dont celui de clarifier le code et d'accelerer
les caculs. Pour creer une liste, il faut d'abord obtenir un numero grace a la fonction
glGenLists, puis faire un appel a glNewList : tout ce qui suit est enregistre dans la
liste (hormis certaines fonctions, voir les pages man). L'enregistrement est arrete pas
glEndList. Pour effectuer toutes les operations dans la liste, il suffit d'appeler
glCallList.
- les transformations : la matrice MODELVIEW est applique a chaque point (vertex). Je ne
vais pas faire un cours d'espace vectoriel, mais certaines notions peuvent etre utiles,
alors si vous en avez le courage, plongez-vous dans un bouquin de maths ou cherchez un peu
sur le net. Pour les transformations basiques, il existe des fonctions toutes faites (ce
n'est pas grave si vous ne connaissez pas par coeur la matrice de rotation autour d'un
vecteur en 3 dimensions) :
- translation : glTranslate
- rotation : glRotate
Attention a l'ordre d'appel de ces fonctions : elles ne sont pas commutatives ! Une
exception bien pratique : les rotations de meme centre sont commutatives (je crois).
De plus ces tranformations sont effectuees dans l'ordre inverse de leur entree : a chaque
appel d'une de ces fonctions la matrice MODELVIEW est multipliee par la matrice de
transformation, or l'application d'une matrice M, correspondant a la composistion de M1 o
M2 o M3 o ... o Mi, a un vecteur V, calculera d'abord Mi(V), puis M(i-1)(Mi(V)), ... ,
jusqu'a M1(M2(...Mi(V))).
En clair, pour faire tourner un cube (de centre (0, 0, 0)) sur lui meme, les rotations
doivent etre les dernieres transformations entrees, et donc les premieres appliquees.
- la pile de matrice : il existe une pile pour sauvegarder la martrice (sinon il faudrait
calculer la matrice inverse a la fin, qui n'existe pas forcement...). Une sauvegarde se
fait par glPushMatrix, la restauration par glPopMatrix. Il existe en fait trois piles
independantes : une par matrice (projection, transformation et texture).
Pour l'instant il n'y a qu'un cube blanc. Il est possible de changer la couleurs des
sommets pour le rendre plus visible, mais l'etape suivante rajoute une source lumineuse.
Code source : Tutorial3.zip
Etape precedente Etape suivante