Tu veux promouvoir ton application ici ? Ce service est gratuit pour le moment alors profites en !


Tutoriel OpenGL ES 4 - S'amuser avec un cube !

Bonjour,

Pour ce quatrième tuto, nous allons nous amuser un peu et mettre en pratique ce que nous avons appris jusqu'à maintenant.
Le but, arriver à ceci :

http://www.ipup.fr/forum/userimages/Image-14-2.jpg



Nous allons créer un cube que nous pourrons déplacer, tourner et grossir avec nos doigts...

Prêt ? C'est parti !

1) Créons notre cube


Partons de ce source. Enlevez tout ce qui ne sert pas au cube.
Lancez, vous devriez obtenir ceci :

http://www.ipup.fr/forum/userimages/Image-12-3.jpg



Ensuite, on va centrer notre carré au centre.

    glTranslatef(0.0, 0.0, -5.0);

Au final, notre méthode drawView devrait ressembler à ça :


- (void)drawView {
    
    const GLfloat cubeVertices[] = {
        
        // face de devant
        -1.0, 1.0, 1.0,             
        -1.0, -1.0, 1.0,           
        1.0, -1.0, 1.0,             
        1.0, 1.0, 1.0,              
        
        // haut
        -1.0, 1.0, -1.0,           
        -1.0, 1.0, 1.0,             
        1.0, 1.0, 1.0,              
        1.0, 1.0, -1.0,             
        
        // arrière
        1.0, 1.0, -1.0,         
        1.0, -1.0, -1.0,           
        -1.0, -1.0, -1.0,           
        -1.0, 1.0, -1.0,            
        
        // dessous
        -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, -1.0, 1.0,
        
        // gauche
        -1.0, 1.0, -1.0,
        -1.0, 1.0, 1.0,
        -1.0, -1.0, 1.0,
        -1.0, -1.0, -1.0,
        
        // droit
        1.0, 1.0, 1.0,
        1.0, 1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, -1.0, 1.0
    };  
    
    const GLshort cubeTextureCoords[] = {
        // devant
        0, 1,     
        0, 0,     
        1, 0,      
        1, 1,     
        
        // dessus
        0, 1,    
        0, 0,      
        1, 0,     
        1, 1,     
        
        // derrière
        0, 1,     
        0, 0,      
        1, 0,     
        1, 1,     
        
        // dessous
        0, 1,    
        0, 0,      
        1, 0,     
        1, 1,    
        
        // gauche
        0, 1,   
        0, 0,      
        1, 0,       
        1, 1,       
        
        // droite
        0, 1,      
        0, 0,       
        1, 0,       
        1, 1,       
    };
    
    [EAGLContext setCurrentContext:context];    
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
    glViewport(0, 0, backingWidth, backingHeight);
    
    /*************** Debut du nouveau code ******************/
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_MODELVIEW);
    
    // dessin du cube
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);
    glRotatef(rotation, 0.0, 1.0, 0.0);

    glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
    glEnableClientState(GL_VERTEX_ARRAY);
    
    glTexCoordPointer(2, GL_SHORT, 0, cubeTextureCoords); // nouvelle ligne
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);          // nouvelle ligne
    
    glColor4f(0.0, 0.0, 0.8, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    
    glColor4f(0.0, 0.8, 0.0, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
    
    glColor4f(0.8, 0.0, 0.0, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
    
    glColor4f(0.8, 0.0, 0.8, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
    
    glColor4f(0.8, 0.8, 0.8, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
    
    glColor4f(0.8, 0.8, 0.0, 1.0); // R V B Alpha
    glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
    
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    rotation += 1.0;
    
    /*************** Fin du nouveau code ********************/ 
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
}

2) Faisons le tourner

Pour faire tourner le cube, nous allons utiliser la méthode


- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    
}

Cette méthode sera appelée automatiquement lorsque un ou plusieurs de vos doigts touchera l'écran.
Il faut maintenant l'implémenter :

Ajoutez


// On récupère les informations du touch
    UITouch *touch = [[touches allObjects] objectAtIndex:0];
    
    CGPoint location = [touch locationInView:touch.view];
    CGPoint previousLocation = [touch previousLocationInView:touch.view];

Les deux dernières lignes permettent de créer :
- un point (location) qui contient les données de l'endroit où on touche
- un autre point (previousLocation) correspondant à l'endroit où on a touché la dernière fois notre vue.

Notez qu'ici, on n'a qu'une vue. On prend donc en compte n'importe quelle touche sur l'écran entier. On peut choisir de gérer les touches sur vue spécifiée :

if([touch view] == cubeView)
{
// faire quelque chose pour cette vue
}

Revenons à nos moutons ! big_smile

Ajoutez ces lignes :


GLfloat difLocationX, difLocationY;
    difLocationX = (location.x - previousLocation.x);
    difLocationY = (location.y - previousLocation.y);

Elles nous permettent d'avoir la différence... Laquelle ?

Gardez à l'esprit que cette méthode est appelée à chaque fois qu'un de vos doigts sur l'écran bouge.  Vous aurez donc un delta de votre déplacement.

Notez que nous créons des GLfloat pour maximiser la compatibilité avec OpenGL.

Il nous faut utiliser ceci pour faire une rotation de l'objet :

Définir deux variables dans le .h :

GLfloat xRotate, yRotate;

NB : dans le source fourni, ces variables sont déjà définies !

Continuez d'ajouter ces lignes dans le .m, toujours dans la même méthode :

    xRotate += difLocationX;
    yRotate += difLocationY;
    // On met à jour l'affichage
    [self drawView];

Remarquez que l'on écrit xRotate = xRotate + difLocationX car on souhaite avoir le delta de tout le mouvement, et non pas d'un fragment du mouvement donné par difLocationX. De plus, cela nous permet de garder la valeur précédente de la rotation. Si vous ne comprenez pas, remplacez :

    xRotate += difLocationX;
    yRotate += difLocationY;
    // On met à jour l'affichage
    [self drawView];

par


    xRotate = difLocationX;
    yRotate = difLocationY;
    // On met à jour l'affichage
    [self drawView];

Maintenant, retour dans la méthode drawView :

Remplacer le glRotatef(rotation, 0.0, 1.0, 0.0);

Par :

    glLoadIdentity();
    glTranslatef(0.0, 0.0, -5.0);
    glRotatef(xRotate, 0.0, 1.0, 0.0); // nouvelle ligne
    glRotatef(yRotate, 1.0, 0.0, 0.0); // nouvelle ligne

Prenons la première nouvelle ligne : on demande à OpenGL de faire une rotation de l'objet de la valeur xRotate. Si ce qui est écrit ne vous semble pas logique, voici coment j'ai raisonné pour nommer ma variable : un mouvement sur l'axe x va me faire tourner l'objet autour de l'axe y. D'où le nom xRotate qui me fait tourner l'objet autour de l'axe y (glRotatef(xRotate, 0.0, 1.0, 0.0).

Petit problème : si vous mettez deux doigts (alt + clic sur simulateur), vous faites aussi tourner votre cube... Or, ce n'est pas ce que l'on veut !

Il nous faut donc modifier notre méthode comme suit :

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // On récupère les informations sur la touche
    UITouch *touch = [[touches allObjects] objectAtIndex:0];
    

    CGPoint location = [touch locationInView:touch.view];
    CGPoint previousLocation = [touch previousLocationInView:touch.view];
    
    CGFloat difLocationX, difLocationY;
    difLocationX = (location.x - previousLocation.x);
    difLocationY = (location.y - previousLocation.y);
    
    // si un doigt -> on fait rotation
    if([touches count] == 1) {
        xRotate += difLocationX;
        yRotate += difLocationY;
        // On met à jour l'affichage
            [self drawView];
    }
}

Lancez, ... Ca ne marche pas ? Rooo. Il faut aller dans IB, en ouvrant MainWindow.xib, puis cliquez dans la fenêtre et autorisez le multitouch :

http://www.ipup.fr/forum/userimages/Image-13-2.jpg



Et voilà !

3) Déplaçons-le

Nous allons déplacer de la même manière que la rotation notre cube. Nous autoriserons la rotation lorsque l'utilisateur double tapera sur l'écran.

Dans la méthode touchesMoved, on ajoute cette ligne :

NSInteger nombreTap = [touch tapCount];

Elle nous renvoie le nombre de tapes réalisées par l'utilisateur.

Ensuite, ajouter

    if (nombreTap == 2)
    {
        xTranslation += difLocationX;
        yTranslation += difLocationY;
        // On met à jour l'affichage
        [self drawView];
    }

N'oubliez pas de définir xTranslation et yTranslation dans votre .h !

Retournez dans la méthode drawView, et modifiez    

glTranslatef(0.0, 0.0, -5.0);

par

glTranslatef(xTranslation, yTranslation, -5.0);

Compilez, lancez, double cliquez et déplacez votre souris ou doigt, c'est selon... Votre cube bouge ! un peu vite... Qu'à cela ne tienne, dans votre .m :

#define USE_DEPTH_BUFFER 1
#define DEGREES_TO_RADIANS(__ANGLE) ((__ANGLE) / 180.0 * M_PI)
#define kNombrePas 40.0 // nouvelle ligne

et modifiez :

    if (nombreTap == 2)
    {
        xTranslation += difLocationX / kNombrePas; // cette ligne
        yTranslation += difLocationY / kNombrePas; // cette ligne
        // On met à jour l'affichage
        [self drawView];
    }

Lancez, votre cube reste plus longtemps dans l'écran !

Notez que votre cube ne suit pas votre doigt... Pour ce faire :

glTranslatef(xTranslation, [b]-yTranslation[/b], -5.0);

4) Zoom - dézoom

Si on met deux doigts sur l'écran, et que l'on fait le mouvement du zoom dézoom, on souhaite modifier la taille de l'objet. Nous allons donc voir une nouvelle fonction : glScale(). Mais tout d'abord, il nous faut modifier quelque peu notre code.

  1. Corrigeons nos erreurs

Je ne sais pas si vous avez remarqué, mais nous avons fait quelque chose de pas propre du tout... Si vous ne le voyez pas, c'est le bon moment pour en prendre conscience ! lol

En fait, lorsque vous déplacez votre cube, vous pouvez observer qu'il tourne en même temps. Ce n'est pas voulu du tout ! big_smile

Cela vient de nos tests, voici le déroulement de notre méthode touchesMoved :

Si un doigt sur l'écran alors rotation

Si double tap sur l'écran alors déplacement


Mais si on fait un double tap avec un doigt. Laquelle des deux conditions va être prise en compte ? Ben on ne sait pas vraiment.

Il nous faut donc réécrire nos conditions avec une architecture switch ... case comme suit :

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // On récupère les informations sur la touche
    UITouch *touch = [[touches allObjects] objectAtIndex:0];    
    
    CGPoint location = [touch locationInView:touch.view];
    CGPoint previousLocation = [touch previousLocationInView:touch.view];
    
    CGFloat difLocationX, difLocationY;
    difLocationX = (location.x - previousLocation.x);
    difLocationY = (location.y - previousLocation.y);
    
    switch ([touches count]) {
            
        case 1: { //Single touch
            
            switch ([touch tapCount])
            {
                case 1: {//Single Tap.
                    xRotate += difLocationX;
                    yRotate += difLocationY;
                    // On met à jour l'affichage
                    [self drawView];
                } break;
                case 2: {
                    xTranslation += difLocationX / kNombrePas;
                    yTranslation += difLocationY / kNombrePas;
                    // On met à jour l'affichage
                    [self drawView];
                } break;
            }
        }break;
            
        case 2: { //Double Touch
            
            switch ([touch tapCount])
            {
                case 1: //Single Tap.
                {
            
                } break;
                case 2: {//Double tap. 
                    
                } break;
                    
                    
            }
        } break;
            
        default:
            break;
    }
    
}

Compilez et lancez, tout de suite ça va mieux !

  2. Récupérer la distance initiale entre vos deux doigts

Pour calculer notre facteur de zoom, il faut déjà connaître la distance initiale entre nos deux doigts lorsqu'ils touchent l'écran.
Voyez le déroulement d'un événements :

http://www.ipup.fr/forum/userimages/event-touch-time.jpg


source Apple



Il va donc falloir implémenter la méthode

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
}

Créons une méthode permettant de donner la distance entre 2 points :

- (CGFloat)distanceBetweenTwoPoints:(CGPoint)fromPoint toPoint:(CGPoint)toPoint {
    
    float x = toPoint.x - fromPoint.x;
    float y = toPoint.y - fromPoint.y;
    
    return sqrt(x * x + y * y);
}

Ne pas oublier de la déclarer dans le .h. Je ne pense pas avoir besoin d'expliquer ce qu'il se passe ici. C'est du pythagore tout simplement ! sqrt() renvoi la racine carrée.

Implémenter touchesBegan comme suit :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    
    // On prend le premier doigt qui touche
    UITouch *touch = [[touches allObjects] objectAtIndex:0];
    
    switch ([touches count]) {
        
        case 1: { //Single touch
            
            switch ([touch tapCount])
            {
                case 1: {//Single Tap.
                    
                } break;
                case 2: {
                    
                } break;
            }
        }break;
            
        case 2: { //Double Touch
            
            switch ([touch tapCount])
            {
                case 1: //Single Tap.
                {
                    //Recuperer la distance entre les deux doigts
                    UITouch *touch2 = [[touches allObjects] objectAtIndex:1]; // deuxieme doigt qui touche
                    
                    initialDistance = [self distanceBetweenTwoPoints:[touch locationInView:touch.view] 
                                                             toPoint:[touch2 locationInView:touch2.view]];
                } break;
                case 2: {//Double tap. 
                    
                } break;
                    
                    
            }
        } break;
            
        default:
            break;
    }
    
}

Déclarer initialDistance dans le .h

  3. Zoomez !

Modifiez touchesMoved comme suit

- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // On récupère les informations sur la touche
    UITouch *touch = [[touches allObjects] objectAtIndex:0];    
    
    CGPoint location = [touch locationInView:touch.view];
    CGPoint previousLocation = [touch previousLocationInView:touch.view];
    
    CGFloat difLocationX, difLocationY;
    difLocationX = (location.x - previousLocation.x);
    difLocationY = (location.y - previousLocation.y);
    
    switch ([touches count]) {
            
        case 1: { //Single touch
            
            switch ([touch tapCount])
            {
                case 1: {//Single Tap.
                    xRotate += difLocationX;
                    yRotate += difLocationY;
                    // On met à jour l'affichage
                    [self drawView];
                } break;
                case 2: {
                    xTranslation += difLocationX / kNombrePas;
                    yTranslation += difLocationY / kNombrePas;
                    // On met à jour l'affichage
                    [self drawView];
                } break;
            }
        }break;
            
        case 2: { //Double Touch
            
            switch ([touch tapCount])
            {
                case 1: //Single Tap.
                {
                    UITouch *touch2 = [[touches allObjects] objectAtIndex:1];
                    
                    CGFloat finalDistance = [self distanceBetweenTwoPoints:[touch locationInView:touch.view]
                                             
                                                                   toPoint:[touch2 locationInView:touch2.view]];
                    
                    // (kNombrePas * kNombrePas) pour zoomer moins vite
                    scale += (finalDistance - initialDistance) / (kNombrePas * kNombrePas);
                    
                    [self drawView];
                } break;
                case 2: {//Double tap. 
                    
                } break;
                    
                    
            }
        } break;
            
        default:
            break;
    }
    
}

Créer une variable CGfloat scale dans votre .h. L'initialiser à 1.0 dans setupView.

Dans notre méthode drawView, on va utiliser cette fameuse nouvelle méthode :

glScalef(GLfloat x, GLfloat y, GLfloat z)

Cette méthode nous permet de faire un zoom sur l'objet. Comme nous souhaitons garder les proportions de notre objet, nous allons appliquer le même facteur sur x, y et z :

glScalef(scale, scale, scale)

    // dessin du cube
    glLoadIdentity();
    glTranslatef(xTranslation, -yTranslation, -5.0);
    glRotatef(xRotate, 0.0, 1.0, 0.0);
    glRotatef(yRotate, 1.0, 0.0, 0.0);
    
    if(scale > 0.0)
        glScalef(scale, scale, scale);
    else
        glScalef(0.0, 0.0, 0.0); // pour faire disparaitre quand on arrive "au bout"
    
    glVertexPointer(3, GL_FLOAT, 0, cubeVertices);
// suite ...

Et bien voilà... Ce tutoriel est fini, j'espère qu'il vous a été utile et qu'il vous a permis de souffler dans ce monde de brute...

sources

exercice

A bientôt !

Ipodishima

Copyright © 2009 - ipup.fr • création de Jérémy Lagrue • design de Loann Fraillon • contact