OpenGL ES for iPhone tutorial Basic - 0 : Hello EAGL

Kongfu.Yang kongfu.yang(a)gmail.com http://www.play3d.net

Warning: these article were written in iPhone SDK beta3, example doesn't work with final SDK yet!
Thanks Muhammad Ishaq (http://blogs.kahaf.com/ishaq) for notify. I will update these article to final SDK when I have time.


Yes, Apple has provided some example for OpenGL ES. If just to say hello, this article is not so neccesary. Here what I want to do is to create a template project and make it possible to write pure c code with OpenGL ES API.

To use EAGL, we need following steps:

1. eaglCreateContext(NULL, &context)

2. eaglCreateWindowSurface(pixelFormat, nativeWindow, &surface)

3. eaglMakeCurrent(surface, context)

4. rendering, eaglSwapBuffers(surface)

5. cleanup:

     eaglGetCurrentSurface(),  if the surface is current, eaglMakeCurrent(NULL, NULL) to unuse it

     eaglDestroySurface(surface)

     eaglDestroyContext(context)


The key point is to get nativeWindow from CAEAGLLayer of the view. Here is the code of the view:
 1 //
2 // MyView.m
3 // eagl
4 //
5 // Created by pin xue on 4/8/08.
6 // Copyright http://www.play3d.net 2008. All rights reserved.
7 //
8
9 #import "eglView.h"
10 #import "eglSetup.h"
11
12 #include "callbacks.h"
13
14 @implementation eglView
15
16 + (Class) layerClass
17 {
18 return [CAEAGLLayer class];
19 }
20
21 - (EAGLNativeWindow) getNativeWindow
22 {
23 CAEAGLLayer * eaglLayer = (CAEAGLLayer *)[self layer];
24 return [eaglLayer nativeWindow];
25 }
26
27 - (id) initWithFrame:(CGRect)frame
28 {
29 self = [super initWithFrame:frame];
30 if ( self != nil )
31 {
32 init_egl([self getNativeWindow], frame.size.width, frame.size.height);
33 }
34
35 return self;
36 }
37
38 -(void) layoutSubviews
39 {
40 CGRect bounds = [self bounds];
41 resize(bounds.size.width, bounds.size.height);
42 }
43
44
45 @end
We force the view to back with CAEAGLLayer by overriding the class method layerClass and ask the layer for nativeWindow. You see the callback.h and some c functions, ignore it at this moment, you will get it later.

Here is how to setup the OpenGL ES with the nativeWindow: (only init_egl() is important for now)
  1 /*
2 * eglSetup.c
3 * eagl clue and OpenGL environment setup routines
4 * All eagl specific knowledge is wrapped here,
5 * other part of the project can be pure OpenGL ES.
6 *
7 * Created by pin xue on 4/8/08.
8 * Copyright 2008 http://www.play3d.net. All rights reserved.
9 *
10 */
11
12 #include "eglSetup.h"
13
14 #include <math.h>
15
16 #ifndef USE_FIXED_POINT
17 #define glClearColorx glClearColor
18 #define glFrustumx glFrustumf
19 #define glTranslatex glTranslatef
20 #endif
21
22 #define NULL 0
23
24 static EAGLContext g_context = NULL;
25 static EAGLSurface g_surface = NULL;
26
27 int init_egl(EAGLNativeWindow nativeWindow, int width, int height)
28 {
29
30 EAGLError ret;
31
32 // create surface
33 ret = eaglCreateWindowSurface(kEAGLPixelFormat_RGB565_D24, nativeWindow, &g_surface);
34 if ( ret != kEAGLErrorSuccess || g_surface == NULL )
35 {
36 return -1;
37 }
38
39 // create context
40 ret = eaglCreateContext(NULL, &g_context);
41 if ( ret != kEAGLErrorSuccess || g_context == NULL )
42 {
43 return -2;
44 }
45
46 // active context (make current)
47 makeCurrent();
48
49 resize(width, height);
50
51 default3DEnv();
52
53 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
54
55 return 0;
56 }
57
58 void default3DEnv(void)
59 {
60 glClearColorx(Float2Fixed(0.5f), Float2Fixed(0.5f), Float2Fixed(0.5f), Float2Fixed(1.0f));
61
62 glShadeModel(GL_SMOOTH);
63 glEnable(GL_CULL_FACE);
64 glCullFace(GL_BACK);
65 glEnable(GL_DEPTH_TEST);
66 glEnable(GL_TEXTURE_2D);
67
68 glMatrixMode(GL_PROJECTION);
69 glLoadIdentity();
70
71
72 glMatrixMode(GL_MODELVIEW);
73 glLoadIdentity();
74 }
75
76 void resize(int width, int height)
77 {
78 glViewport( 0, 0, width, height );
79
80 glMatrixMode(GL_PROJECTION);
81 glLoadIdentity();
82
83 float top = tan(45.0f*3.14159f/360.0f) * 0.005f;
84 float right = (float)width/(float)height * top;
85
86 glFrustumx( Float2Fixed(-right), Float2Fixed(right),
Float2Fixed(-top), Float2Fixed(top),
Float2Fixed(0.1f), Float2Fixed(100.0f) );
87
88 glMatrixMode(GL_MODELVIEW);
89 glLoadIdentity();
90 glTranslatex( 0, 0, Float2Fixed(-60.0f) ); // fixpoint : hi 16 bits are integer part, low 16 bits are fragmental part.
91 }
92
93
94 int makeCurrent(void)
95 {
96 EAGLError ret = eaglMakeCurrent(g_surface, g_context);
97 return ret != kEAGLErrorSuccess;
98 }
99
100 int swapBuffers(void)
101 {
102 return kEAGLErrorSuccess != eaglSwapBuffers(g_surface);
103 }
104
105 void clean(void)
106 {
107 swapBuffers();
108
109 // deactive context
110 // eglMakeCurrent( dpy, NULL, NULL, NULL );
111 eaglMakeCurrent(NULL, NULL);
112
113 // destroy context
114 if ( g_context != NULL )
115 eaglDestroyContext( g_context );
116
117 // destroy suerface
118 if ( g_surface != NULL )
119 eaglDestroySurface( g_surface );
120
121 }
122
The init code is simple, easier thant standard EGL way. But it is a bit confuse that why Apple doesn't follow the EGL standard.  Here you also see the callback called by the view.

The world is initialized by the application delegate, standard Coco Touch way. In fact, I use the xcoder generate a standard project, and remove all code file except the precompiled header. And the fill the project with new files.
 1 //
2 // eaglAppDelegate.m
3 // eagl
4 //
5 // Created by pin xue on 4/8/08.
6 // Copyright http://www.play3d.net 2008. All rights reserved.
7 //
8
9 #import "eaglAppDelegate.h"
10 #import "eglView.h"
11
12 #include "callbacks.h"
13
14 @implementation eaglAppDelegate
15
16 @synthesize window;
17 @synthesize contentView;
18
19 - (void)applicationDidFinishLaunching:(UIApplication *)application {
20 // Create window
21 self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
22
23 // Set up content view
24 UIView * glview = [[[eglView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
25 [window addSubview:glview];
26
27 // Show window
28 [window makeKeyAndVisible];
29
30 rendering();
31
32 //Configure and start accelerometer
33 [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / 10)];
34 [[UIAccelerometer sharedAccelerometer] setDelegate:self];
35
36 }
37
38 - (void)dealloc {
39 [contentView release];
40 [window release];
41 [super dealloc];
42 }
43
44 - (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration
45 {
46 accelerating(acceleration.x, acceleration.y, acceleration.z);
47 }
48
49
50 @end
Note, to accept accelerometer events, we have to make the app delegate to comform UIAccelerometerDelegate protocol and configure UIAccelerometer as line 33, 34. Then the accelerometer method is called by 10 times per second.

Then here is the meat, the two callbacks rendering() and accelerating() in pure C.
 1 #include "eglSetup.h"
2 #include "callbacks.h"
3 #include "helloMesh.h"
4
5 static GLfixed g_rx, g_ry;
6
7 static void drawHello()
8 {
9 glEnableClientState( GL_VERTEX_ARRAY );
10 glVertexPointer( 2, GL_FIXED, 0, helloVertex ); // 2 means (x,y) for one vertex, coord is in float number, stride is 0
11
12 glEnableClientState( GL_COLOR_ARRAY );
13 glColorPointer( 4, GL_FIXED, 0, helloColor); // 4 means (R,G,B, A) for color of one vertex,
color is in float number, stride is 0

14
15 glDrawArrays(GL_TRIANGLES, 0, sizeof(helloVertex)/sizeof(GLfixed)/2 );
16
17 }
18
19
20 void rendering(void)
21 {
22 glEnable(GL_DEPTH_TEST);
23 glDepthFunc(GL_LEQUAL);
24
25 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
26
27 glPushMatrix();
28 glTranslatex( Float2Fixed(-1.0f), 0, 0 );
29 glScalex( Float2Fixed(0.5),Float2Fixed(0.5),Float2Fixed(0.5) );
30
31 glRotatex( g_rx, 1, 0, 0 );
32 glRotatex( g_ry, 0, 1, 0 );
33
34 // draw here
35 drawHello();
36
37 glPopMatrix();
38 swapBuffers();
39 }
40
41 void accelerating(float x, float y, float z)
42 {
43 }
The helloMesh.h is composed by hand according:

helloegl vertex layout

I will skip other code in this article. Please view following links or download the whole project archive for reference.
Info.plist
main.m
callbacks.h
helloMesh.h
helloegl.c
Classes/eglSetup.c
Classes/eaglAppDelegate.m
Classes/eaglAppDelegate.h
Classes/eglView.m
Classes/eglView.h
Classes/eglSetup.h

If you feel this article is helpful, please find an AD interesting for you to click, it is helpful for author :-)