iPhone SDK et le framework OpenGL ES — Partie 2
3 September 2008 par Paul Arthur HenrionBonjour à tous,
comme promis après l'introduction précédente, voici l'article qui va vous faire mettre les mains dans le cambouis !
Je vais tâcher de vous faire implémenter une application très simple affichant un cube que nous pourrons faire tourner avec le doigt.
![]() |
+ | ![]() |
Let's rock!
Pour suivre ce tutoriel il vous faut :
- un Mac Intel sous Léopard (pour la dernière version du SDK il faut la dernière mise à jour de l'OS) — et oui, pas de développement pour iPhone possible sous une autre plateforme
- l'iPhone SDK, version supérieure ou égale à la bêta 4
- quelques connaissances, car je risque de faire quelques raccourcis techn(olog)iques. Je vous conseille de lire mon introduction sur le sujet : iPhone SDK et OpenGL|ES — Partie 1
Création d'un projet pour iPhone OS
- Commençons par ouvrir XCode.
- Choisissez ensuite le menu "File -> New Project..." (ou ⌘ + Shift + N).
- Histoire de faire ça bien (à l'arrache), nous allons choisir "Window-based Application".
Ce que l'on peut y voir
Là s'affiche le contenu de votre projet. Sur la gauche, vous avez un explorateur de sources (sous le bandeau "Groups and Files") ; sur la droite le contenu du groupe que vous parcourez actuellement (donc là, tout de suite : tout ce qu'il y a dans votre projet, car vous êtes normalement à la racine de celui-ci).
Les icônes "caisse à out's" (caisse à outils) représentent les frameworks utilisés par votre application (donc là, tout de suite : Foundation et UIKit — le plus rapides d'entre vous auront vu qu'il n'y a pour l'instant pas d'OpenGL ES), celles représentant une fiche avec un gros "M" bleu sont les fichiers sources en Objective-C, celles avec un "H" rouge, les headers, celle avec une succession de lignes écrites en tout petit est une PropertyList (fichier XML) et enfin, celle avec "A" stylisé avec un pinceau et deux crayons est le fichier .XIB qui est un "paquet" définissant l'interface de votre application. L'autre fichier avec un "A" comme application, suffixé par ".app" est le paquet de votre application (en gros, c'est l'application exécutable par le système).
Examen du contenu d'un projet vierge
Commençons par ouvrir le fichier "main.m". Comme tout programme en C (ou dérivé, comme en Objective-C), il existe une fonction mère, de prototype int main(int argc, char *argv[]), étant le point d'entrée de l'exécution.
Dans le cas d'une application pour iPhone OS, deux choses diffèrent d'une application pour Mac OS.
La première est la création d'une NSAutoreleasePool. Cet objet va servir à libérer les zones de mémoire des objets sur lesquels vous appellerez la méthode autorelease.
La seconde est l'appel à la fonction UIApplicationMain. Celle-ci n'existe pas dans votre code et pourtant c'est bien de là que s'exécute toute votre application... magique hein ? Vous allez vous demander comment faire alors pour faire des choses avec un programme si on ne peut éditer la fonction qui l'appelle. Et bien tout simplement en utilisant un delegate (ou délégué ;')).
Une petite compilation pour voir
Bien, il est temps de faire quelque chose. Si on commençait à exécuter notre application ? Pour se faire, il faut aller dans le menu "Build" -> "Build and Run" (ou bien simplement ⌘ + R).
Que voyez-vous ? Un iPhone qui a un écran blanc... cool, isn't it? OK, c'est nul, on va y mettre un peu d'OpenGL ;')
Ajoutons nos frameworks amicaux
Commençons par intégrer le framework OpenGL ES à notre projet. Pour se faire, il faut faire un clic droit sur le groupe "Frameworks" de votre projet, choisir "Add" -> "Existing Frameworks...". Une fênêtre apparaît dans laquelle sont listés, dans le panneau de droite, tous les frameworks disponibles.
Si vous ne voyez pas "OpenGLES.framework" c'est que XCode n'a pas pensé que vous développiez pour iPhone et il vous montre les frameworks compatibles avec Mac OS 10.5, le con. Il faut donc remonter à la racine du dossier "Developer", puis choisir :
"Platforms" -> "iPhoneOS.platform" -> "Developer" -> "SDKs" -> "iPhoneOS2.0.sdk" -> "System" -> "Library" -> "Frameworks" (ouf).
Vous pouvez maintenant choisir "OpenGLES.framework".
Refaîtes pareil pour intégrer le framework "QuartzCore.framework". Ce framework définit les classes qui vous permettront d'encapsuler la vue OpenGL dans la vue de votre application.
De la manière d'afficher de l'OpenGL ES dans notre application...
Comme je vous l'ai indiqué dans mon précédent sujet, pour pouvoir afficher de l'OpenGL ES dans notre application, il nous faut une classe pouvant s'intégrer dans les éléments haut niveau de l'interface, "compréhensible" et "comprenant" ses voisins et le système, mais dans laquelle on va faire notre tambouille.
C'est donc ici que commencent les choses marrantes...
UIView est une classe représentant une vue dans l'application — on s'en serait douté. Une vue affiche ce que l'on veut dans l'application et interagit aux actions de l'utilisateur (par exemple quand il pose ses gros doigts boudinés sur l'écran). Dans notre vue, ce que nous voulons faire c'est afficher de l'OpenGL ES, or, de base il n'existe pas de type de vue le gérant. Nous devons donc en créer une !
Création d'une sous-classe de UIView pour gérer notre OpenGL
Alors c'est parti mon kiki — si tu n'es pas encore mort de fatigue après avoir lu tout ce qu'il y a au-dessus !
Ce coup-ci c'est sur le groupe "Classes" qu'on va faire un clic droit, puis choisir "Add" -> "New File..." ; puis on choisit dans le panneau de gauche de la fenêtre qui vient de s'ouvrir "Cocoa Touch Classes" et à droite "UIView subclass". Appelez-la comme vous le voulez, moi je l'appellerai "EAGLView" – vous verrez plus tard pourquoi.
Dans le fichier header
Yeah ! On a un fichier qui s'est créé et qui est beau ! Normalement, vous êtes maintenant de le header de la classe. Histoire d'avoir les outils qu'il faut pour travailler plus tard, il faut importer (ou inclure) les headers d'OpenGLES. Rajoutez donc ceci en dessous du #import <UIKit/UIKit.h>.
#import &lt;OpenGLES/EAGL.h&gt;; #import &lt;OpenGLES/ES1/gl.h&gt;; #import &lt;OpenGLES/ES1/glext.h&gt;; #import &lt;QuartzCore/CAEAGLLayer.h&gt;;
Déclarons la classe !
Hé oui, car une classe avec rien dedans, bah c'est assez nul. Nous allons donc ajouter des attributs à celle-ci pour qu'elle ait les super-pouvoirs permettant d'afficher des trucs en OpenGL.
Le contexte EAGLContext
Cette fameuse classe gère à peu près tout lorsque l'on veut faire de l'OpenGL ES... c'est elle qui va "encapsuler" notre OpenGL au sein de l'interface et de son monde haut niveau. C'est très vague comme définition, mais je ne trouve pas d'image pour la décrire. Dîtes vous juste c'est "DA SHIZNIT" pour l'OpenGL ES sur iPhone !
D'autres trucs utiles pour la route
On va stocker la taille de la fenêtre dans laquelle on affiche (ie : les dimensions de l'écran de l'iPhone), ainsi que les tampons pour le rendu de la vue (viewRenderBuffer), les images de la vue (viewFrameBuffer) et celui de la profondeur (depthRenderBuffer). On y rajoute deux entiers enregistrant les "coordonnées" de touché de l'utilisateur.
@interface EAGLView : UIView
{
// Ce @private n'est pas obligatoire, il est juste là pour le style ;')
@private
EAGLContext *context;
// Nos fameux b(l)uffers
GLuint viewRenderbuffer;
GLuint viewFramebuffer;
GLuint depthRenderbuffer;
// Les dimensions de l'écran
GLint backingWidth;
GLint backingHeight;
// Les enregistreurs de coordonnées
GLfloat xRotate;
GLfloat yRotate;
}
// Afin de se faciliter la vie, on déclare le contexte comme propriété
@property (nonatomic, retain) EAGLContext *context;Dans le code source maintenant...
Ouvrez moi donc le fichier source (suffixé par ".m") pour que nous y ajoutions quelques trucs — l'objet de ce tutoriel. Un raccourci pratique quand vous êtes dans le header ou le fichier source d'une classe : ⌘ + ⌥ + ↑ ; pour switcher de l'un vers l'autre.
Pour la propriété context
Ajoutez @synthesize context; au-dessous de la balise @implementation, histoire de dire au compilateur de tout faire pour nous ("pasque nous sommes feignants", exact).
@implementation EAGLView @synthesize context;
Un petit cheat pour continuer
Parfois, les objets sont curieux. Dans notre cas, la fenêtre dans laquelle notre vue va s'afficher, va lui demander quel est le type de ce que l'on affiche (grosso modo). Il faut donc répondre que c'est de l'OpenGL, or la seule classe qui permet d'afficher de l'OpenGL est CAEAGLLayer. Ainsi, on ajoute la méthode de classe :
+ (Class) layerClass
{
return ([CAEAGLLayer class]);
}Avant de foutre le bordel, on va s'assurer que l'on sait nettoyer
Comme vous l'avez sans doute remarqué, une méthode - (void) dealloc; a été générée automatiquement, avec trois fois rien dedans. Celle-ci est appelée par le système, et jamais par votre code si vous avez bien suivi, afin de libérer la mémoire que notre EAGLView squatte. C'est donc ici qu'il faut libérer tout ce que nous avons alloué pendant l'utilisation de notre classe, comme par exemple context, mais aussi indiquer ce que nous n'utilisons plus.
- (void) dealloc
{
// On teste si le contexte de rendu OpenGL qui est actif et est le notre
if (context == [EAGLContext currentContext])
// Si c'est le cas, alors on le passe à la trappe
[EAGLContext setCurrentContext:nil];
[context release];
// On appelle la méthode de désaffectation de la super classe
[super dealloc];
}Méthode d'initialisation
Vous aurez noté qu'il y a une méthode - (id) initWithFrame:(CGRect)frame; qui a été générée dans le code. Elle est bien jolie, mais nous allons utiliser une autre manière d'initialiser notre tintouin, mais pour se faire, il faut savoir par qui et quand sera initialisée notre classe.
En fait, notre vue va être instanciée au tout début de l'application, lorsque les objets contenus dans le paquet XIB seront dépaquetés pour être chargés en mémoire et affichés.
Pour l'instant, notre classe n'est pas dans le XIB me direz-vous, mais nous allons y remédier de ce pas.
Avant d'ouvrir le fichier "MainWindow.xib", on va ajouter trois lignes dans le fichier "LeNomDeProjetAppDelegate.h" et deux lignes dans son source.
- Tout d'abord on importe le fichier "EAGLView.h", en ajoutant
#import "EAGLView.h". - Puis on va indiquer qu'il existe désormais un nouvel attribut de type
EAGLView *, en ajoutantIBOutlet EAGLView *glview;dans le corps de l'interface de la classe LeNomDeVotreProjetAppDelegate — leIBOutletindique qu'Interface Builder devra détecter cette attribut pour l'utiliser. - Enfin, on définit la propriété ad-hoc (comme le poisson), en ajoutant
@property (nonatomic, retain) EAGLView *glView;.
Dans le fichier source "LeNomDeVotreProjet.m" :
- On ajoute l'indication
@synthesize glView;> - Puis on s'assure de sa désaffection, en ajoutant
[glView release];dans le corp de la méthode- (void) dealloc;.
On a donc dans le fichier "LeNomDeProjetAppDelegate.h" :
#import
#import "EAGLView.h"
@interface LeNomDeProjetAppDelegate : NSObject
{
IBOutlet UIWindow *window;
IBOutlet EAGLView *glView;
}
@property (nonatomic, retain) UIWindow *window;
@property (nonatomic, retain) EAGLView *glView;
@endEt dans le fichier "LeNomDeProjetAppDelegate.m" :
#import "LeNomDeProjetAppDelegate.h"
@implementation LeNomDeProjetAppDelegate
@synthesize window;
@synthesize glView;
- (void) applicationDidFinishLaunching:(UIApplication *)application
{
[window makeKeyAndVisible];
}
- (void) dealloc
{
[window release];
[glView release];
[super dealloc];
}
@endOuvrez donc le fichier "MainWindow.xib". Interface Builder va charger l'interface de votre projet.
Trouvez dans la fenêtre "Library" (pour la faire apparaître : ⌘ + ⇧ + L) le composant "UIView" (voir icône). Faîtes glisser l'élément (c'est beau !) SUR l'élément "Window" dans la fenêtre intitulée "MainWindow". La vue doit alors apparaître en dessous de l'élément Window et légèrement décalée sur la droite
Sélectionnez maintenant l'onglet "Identity" (icône avec un "i" dans un disque bleu) du panneau "Inspector" (pour le faire apparaître : ⌘ + (alt ou option ou ⇧) + i).
Saisissez "EAGLView" dans le champ "Class" de "Class identity". Cela indique que l'on veut que cette vue soit de ce type.
Enfin, cliquez sur l'élément "LeNomDeVotreProjet App Delegate" en appuyant simultanément sur CTRL (ou ⌃) et, tout en restant le doigt sur le clic de votre souris, tirez un trait jusqu'à l'élément "View".
Choisissez alors "glView" dans fenêtre noire qui est apparue.
Bien, maintenant notre classe est chargée lors du dépaquetage de notre fichier .XIB. Bonne nouvelle, on va pouvoir enfin faire un peu d'OpenGL.
Pour se faire, rouvrez le fichier "EAGLView.m", nous allons bazarder la méthode - (id) initWithFrame:(CGRect)frame; et allons la remplacer par - (id) initWithCoder:(NSCoder *)coder;. Cette méthode est appelée lors du fameux dépaquetage de notre fichier .XIB.
- (id) initWithCoder:(NSCoder *)coder
{
if (nil != (self = [super initWithCoder:coder]))
{
// On va paramétrer un petit peu la couche qui s'occuppe du rendu de l'affichage
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
// On indique que l'on ne va pas retenir ce que l'on a affiché, histoire de ne pas faire ramer encore plus l'affichage
[NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
// On indique que l'on veut des couleurs en 32 bits, ce qui correspond au format OpenGL GL_RGBA8888
kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
// On dit que notre contexte est en OpenGL ES 1.x et qui si on n'arrive pas à l'assigner à notre environnement graphique, on se casse !
if ((nil == (context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1])) || (NO == [EAGLContext setCurrentContext:context]))
{
[self release];
return (nil);
}
// Puis on met nos variables à 0.
xRotate = 0.0f;
yRotate = 0.0f;
}
return (self);
}Méthodes d'affichage
Lors de l'affichage d'une vue, plusieurs méthodes sont appelées. La méthode - (void) layoutSubviews; en fait partie. Son rôle est de mettre à jour l'affichage des vues contenues dans notre vue. Dans notre cas, nous n'en avons pas, mais c'est bien ici que nous allons mettre à jour des données pour l'affichage OpenGL.
- (void) layoutSubviews
{
// On bascule dans notre contexte maison, car c'est lui qui va rendre notre code
[EAGLContext setCurrentContext:context];
// On appelle une méthode que l'on va définir apres, detruisant nos buffers pour en assurer la mise a jour
[self destroyBuffers];
// On appelle la méthode qui va (re)créer les tampons
[self createBuffers];
// Enfin, la méthode qui dessine ce qu'il y a à afficher
[self drawView];
}On détruit donc les buffers :
- (void) destroyBuffers
{
// Les méthodes pour les destructions des tampons...
glDeleteFramebuffersOES(1, &viewFramebuffer);
// et une mise à zéro pour être sûr :')
viewFramebuffer = 0;
glDeleteRenderbuffersOES(1, &viewRenderbuffer);
viewRenderbuffer = 0;
glDeleteRenderbuffersOES(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}Et enfin, on les (re)crée :
- (BOOL) createBuffers
{
// On génère les tampons
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
// Et on indique au système quelles variables les représentent
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
// On relie notre tampon de rendu à la couche où il va s'afficher
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer *)self.layer];
// On paramètre le tampon de rendu :
// - avec le niveau de couleur que l'on veut
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
// - avec les dimensions de l'écran
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
// On génère le tampon de profondeur -- bah oui, on fait de la 3D
glGenRenderbuffersOES(1, &depthRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
// On paramètre le tampon :
// - avec les dimensions que l'on veut
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight);
// - avec la profondeur que l'on veut
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
// Si ça n'a pas réussi à créer notre bouzin, on dégage !
if (GL_FRAMEBUFFER_COMPLETE_OES != glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES))
{
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES));
return (NO);
}
return (YES);
}Affichons donc un cube !
Bien, on s'est marré en apprenant plein de choses intéressantes, mais maintenant on va (enfin) dessiner à coup d'OpenGL ! Youpi, sortez les langues de belle-mère, c'est la fête !
Il faut placer des constantes définissant un cube dans le fichier.
Placez donc le code ci-après sous @implementation EAGLView.
// Définition des faces d'un cube en suivant la manière GL_TRIANGLE_FAN
// (les points d'une face sont donnés dans le sens horaire ou anti-horaire)
static const GLfloat cubeFan[] =
{
// Face avant
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
// Face droite
0.5f, 0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// Face arrière
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// Face gauche
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
// Face au-dessus
-0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
// Face en dessous
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
};
// Définition des faces d'un cube en suivant la manière GL_TRIANGLE_STRIP
// (les points d'une face sont donnés en zig-zag)
static const GLfloat cubeStrip[] =
{
// Face au-dessus
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// Face arrière
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
// Face en dessous
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
// Face gauche
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
// Face avant
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
// Face droite
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
};
// Là on définit nos couleurs pour les faces
static const GLubyte cubeColors[] =
{
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
255, 0, 0, 255,
255, 255, 0, 255,
255, 255, 0, 255,
255, 255, 0, 255,
255, 255, 0, 255,
255, 0, 255, 255,
255, 0, 255, 255,
255, 0, 255, 255,
255, 0, 255, 255,
0, 0, 255, 255,
0, 0, 255, 255,
0, 0, 255, 255,
0, 0, 255, 255,
0, 255, 255, 255,
0, 255, 255, 255,
0, 255, 255, 255,
0, 255, 255, 255,
0, 255, 0, 255,
0, 255, 0, 255,
0, 255, 0, 255,
0, 255, 0, 255,
};Puis on va mettre le code dessinant le cube dans la méthode - (void) drawView;. On pourrait le mettre dans la méthode - (void) drawRect:(CGRect)rect;, qui, comme la méthode - (void) layoutSubviews;, fait partie des méthodes de la classe UIView appelées par l'application lorsque notre vue est affichée, mais on a déjà implémenté - (void) layoutSubviews; et dedans on appelle la fonction qui suit.
Le rôle de cette méthode est, comme son nom l'indique, de dessiner le contenu de la vue.
- (void) drawView
{
// On bascule dans le contexte qu'on s'est défini
[EAGLContext setCurrentContext:context];
// On lie au tampon viewFramebuffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
// On charge la matrice de projection pour définir ce que l'on va voir et comment
glMatrixMode(GL_PROJECTION);
// On centre
glLoadIdentity();
// On définit la position et la taille de la zone à afficher
// donc ici : on part du coin bas-gauche et la surface est la taille de l'écran
glViewport(0, 0, backingWidth, backingHeight);
// On prend du recul
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -10.0f, 10.0f);
// On efface ce qui a été dessiné auparavant
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// On va maintenant définir ce que l'on veut dessiner
glMatrixMode(GL_MODELVIEW);
// On indique que l'on va dessiner un tableau de points...
glEnableClientState(GL_VERTEX_ARRAY);
// ...des points à 3 coordonnées (x, y, z) qui sont des flottants sans décalage d'octet et stockés dans cubeFan
glVertexPointer(3, GL_FLOAT, 0, cubeFan);
// On indique que l'on va donner les couleurs à placer sur le cube sous forme de tableau
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, cubeColors);
// On va faire de la 3D donc il faut tester ce qui est derrière ou devant
glEnable(GL_DEPTH_TEST);
// Avant de commencer à dessiner, on va affecter de deux rotations le repère des coordonnées du cube pour le faire "bouger"
glRotatef(yRotate, 0.0f, 0.0f, 1.0f);
glRotatef(xRotate, 0.0f, 1.0f, 0.0f);
// On dessine chaque face du cube avec la méthode GL_TRIANGLE_FAN
// Si vous voulez utiliser GL_TRIANGLE_STRIP, il faut l'appliquer à cubeStrip
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
glDrawArrays(GL_TRIANGLE_FAN, 8, 4);
glDrawArrays(GL_TRIANGLE_FAN, 12, 4);
glDrawArrays(GL_TRIANGLE_FAN, 16, 4);
glDrawArrays(GL_TRIANGLE_FAN, 20, 4);
// On a fini, on quitte les modes dans l'ordre inverse de leurs appels
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// On lie au tampon viewRenderbuffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
// Et on le présente dans le contexte pour affichage
[context presentRenderbuffer:GL_RENDERBUFFER_OES];
}Résultat des courses
Si vous compilez dès maintenant votre projet (dans le menu "Build" -> "Build and Run" ou bien ⌘ + R), vous devriez voir un iPhone affichant un carré rouge sur un fond noir. Si ce n'est pas le cas, vérifiez que votre projet se compile bien pour iPhoneOS 2.0 en allant dans le menu "Project" -> "Edit Project Settings" et, dans l'onglet "General", dans le menu dropdown intitulé "Base SDK for All Configurations", en choisissant "Simulator — iPhone OS 2.0".
Ajoutons un peu d'interaction à ce cube
Comme vous le savez sans doute, sur l'iPhone on peut utiliser nos doigts boudinés pour faire des choses avec. Et bien utilisons-les pour faire tourner le cube qui s'affice !
Pour se faire, rien de plus simple, il suffit d'ajouter la méthode - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event à notre classe EAGLView. Cette méthode est appelée par le système lorsqu'une touche a été détectée et qu'elle se déplace. On va l'utiliser pour faire que quand l'utilisateur déplace son doigt, il fait tourner le cube.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// On récupère les informations sur la touche
UITouch *touch = [[touches allObjects] objectAtIndex:0];
// On récupère les différents endroits du déplacement
CGPoint location = [touch locationInView:touch.view];
CGPoint previousLocation = [touch previousLocationInView:touch.view];
// On fait un calcul savant
xRotate += (location.x - previousLocation.x);
yRotate += (location.y - previousLocation.y);
// On met à jour l'affichage
[self drawView];
}Voilà
Mon article est fini. Si vous avez des questions, n'hésitez pas à poster un commentaire :')
Références
Apple Developer connection : http://developer.apple.com
OpenGL ES : http://www.khronos.org/opengles
Tags: CoreAnimation, CoreGraphics, iphone, iPod Touch, OpenGL ES


19 November 2008 à 0:11
Dans l’article, des ” sont codes en language HTML, ce n’est pas beau…
11 December 2008 à 19:16
Excellent tutoriel : clair, pédagogique, et abordable.
Peut-être un bémol pour la forme : la partie Interface Builder (notamment le Ctrl + glisser) est un petit peu vague. Mais c’est bien une critique de principe!
Un grand merci en tous les cas!
17 January 2009 à 1:51
Merci pour ce tres bon tuto qui m’a permis de comprendre pleins de trucs et d’attaquer comme il faut.
Juste un petit truc cela dit
Je trouve que le resultat manque de profondeur, ca ressemble a de la 3d isometrique comme dans simcity …
Y a t’il des choses a ajouter ou a changer pour modifier la pespective, angle de vision …
21 January 2009 à 12:25
Merci pour ce bon tuto
suivi de A à Z et pas de soucis
je me suis juste planté sur l’interface builder. je n’ai pas placé la View directement sur la fenetre windows du coup rien ne s’affichait.
Merci d’avoir pris du temps pour faire quelque chose qui tient vraiment la route.
24 January 2009 à 16:46
Très bon tuto, mais la mise en page du code fait peur et de plus, quand on copie/colle, on doit se taper jusqu’à 115 lignes à effacer (le nombre de ligne est aussi copié…)
Faudrait donc penser à installer http://wordpress.org/extend/plugins/google-syntax-highlighter/ sur le blog
15 February 2009 à 16:23
Bonjour,
Tout d’abord un grand merci pour ce tuto qui est à mon gout parfait, sauf que je n’arrive pas à voir le carré rouge sur fond noir.
Moi je me retrouve avec un écran blanc…
Une idée ? Merci
28 February 2009 à 13:58
Super,
Le ton et les les commentaires du codes sont un vrai plus par rapport à ce que l’on trouve ailleurs ou dans les docs …. du vrai langage de codeur quoi !
Pour info, sur le SDK 2.2 Xcode génére ce projet de façon quasi identique mais sans les explications et juste en 2D.
Peut etre pourrais tu nous faire le tome 3 en allant cette fois beaucoup plus loin dans les spécificités de l’environnement ?
En tout cas merci beaucoup pour ton temps.
amicalement,
cesar
24 July 2009 à 16:06
Super ce tuto
J’aimerais savoir quelle instruction il faut pour déclencher le clavier virtuel depuis une vue opengl?
Merci.
23 January 2010 à 19:52
Merci pour ce tuto,
Je n’en suis qu’au début car j’ai un petit soucis, au début juste avant d’utiliser interface builder, j’ai une erreur “Synthesized property ‘glView’ must either be named the same as a compatible ivar or must explicitly name an ivar” lorsque je compile. De plus, lorsque je continue sans me soucier de cette erreur et que j’utilise Interface builder, je ne peux pas saisir le texte “EAGLView” dans Class de Class Indentify, je peux y mettre ce que je veux sauf ca …
Merci de votre aide, j’aimerais bien finir ce tuto afin de découvrir la programmation sur iphone.