Cube

  1. 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.
  2. 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.

    Tutorial3.jpg (9135 bytes)

 

    Code source : Tutorial3.zip

    Etape precedente     Etape suivante