Drawing
With the engines initialized, it's time to define some objects. How you do this depends on the kinds of objects you want to draw, of course, but you generally follow these steps:
- Define shapes as a series of vertexes.
- Apply textures to specific surfaces in the scene.
A simple example, drawn from Qualcomm sample code, illustrates this by defining a simple triangle:
GLfixed face[9] =
{
-ITOX(2), -ITOX(2), ITOX(2),
ITOX(2), -ITOX(2), ITOX(2),
-ITOX(2), ITOX(2), ITOX(2)
};
GLfixed color[12] =
{
0, ITOX(1), ITOX(1), 0,
ITOX(1), 0, ITOX(1), 0,
ITOX(1), 0, 0, 0
};
uint8 index[3] = {0,1,2};
// Clear everything
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// Triangle
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, GL_FIXED, 0, face);
glColorPointer(4, GL_FIXED, 0, color);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, index);
The face array contains an array of vertices for the specific shape to be drawn. For each vertex, there's a corresponding color in the color array. Note that colors are represented as RGBA, although instead of the byte RGBA values you're used to in doing BREW color specifications, they're again represented as fixed-point numbers. The
glEnableClientState calls enable drawing by vertex and color arrays, which are subsequently passed to the OpenGL engine using
glVertexPointer and
glColorPointer. Finally, the engine draws the three vertexes as directed by the index array when you call
glDrawElements (note that
glDrawElements determines which vertexes to draw in the vertex array, and in what order they're drawnthe counts passed to
glVertexPointer and
glColorPointer indicate the number of items per vertex or color).
Most of the time, you won't want to draw just a static image, you'll want to animate things. To do this in BREW, you need to periodically update your model (by shifting perspective, objects in the scene, or both), and then redraw the scene using glDrawElements. This is an excellent opportunity to use IThread (see my previous article), because you can then write your animation and update code using a simple set of loops and conditionals, and simply yield the processor using IThread_Sleep in AEETU_Sleep.c between each frame.
Cleanup
When you're done using the OpenGL ES interfaces, it's important to clean up everything you've allocated to avoid leaks. This includes the display bitmap, EGL display, context, and surface, as well as the actual interfaces:
if (m_pDDBitmap) IBITMAP_Release( m_pDDBitmap );
eglMakeCurrent( EGL_NO_DISPLAY,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT );
eglDestroyContext( pMe->eglDisplay, pMe->eglContext );
eglDestroySurface( pMe->eglDisplay, pMe->eglSurface );
eglTerminate( pMe->eglDisplay );
if (pMe->pIEGL) IEGL_Release( pMe->pIEGL );
if (pMe->pIGL) IGL_Release( pMe->pIGL );
Be sure you release the EGL and GL extensions last, as no gl or egl APIs work once they're released. This is obvious, as these interfaces rely on having instances of the extension, but it's an easy mistake to make.
A 3D Interface Wizard
Whether you're writing just for BREW, or looking at cross-platform development, if you need 3D interfaces, OpenGL ES should be your first choice. Although it's not yet supported by all handsets, Qualcomm was a major participant in the OpenGL ES development process, and you can expect OpenGL ES to be the definitive 3D API for BREW going forward.