OpenGL Graphics

The Android platform supports OpenGL graphics in roughly the same way that a silk hat supports rabbits. Although this is certainly among the most exciting technologies in Android, it is definitely at the edge of the map. It also appears that just before the final beta release, the interface underwent major changes. Much of the code and many of the suggestions found on the Web are obsolete and no longer work.

The API V1_r2 release is an implementation of OpenGL ES 1.0 and much of ES 1.1. It is, essentially, a domain-specific language embedded in Java. Someone who has been doing gaming UIs for a while is likely to be much more comfortable developing Android

OpenGL programs than a Java programmer, even a programmer who is a Java UI expert.

Before discussing the OpenGL graphics library itself, we should take a minute to consider exactly how pixels drawn with OpenGL appear on the display. The rest of this chapter has discussed the intricate View framework that Android uses to organize and represent objects on the screen. OpenGL is a language in which an application describes an entire scene that will be rendered by an engine that is not only outside the JVM, but possibly running on another processor altogether (the Graphics Processing Unit, or GPU). Coordinating the two processors' views of the screen is tricky.

The SurfaceView, discussed earlier, is nearly the right thing. Its purpose is to create a surface on which a thread other than the UI graphics thread can draw. The tool we'd like is an extension of SurfaceView that has a bit more support for concurrency, combined with support for OpenGL.

It turns out that there is exactly such a tool. All of the demo applications in the Android SDK distribution that do OpenGL animation depend on the utility class GLSurface View. Since the demo applications written by the creators of Android use this class, considering it for other applications seems advisable.

GLSurfaceView defines an interface, GLSurfaceView.Renderer, which dramatically simplifies the otherwise overwhelming complexity of using OpenGL and GLSurfaceView. GLSurfaceView calls the renderer method getConfigSpec to get its OpenGL configuration information. Two other methods, sizeChanged and surfaceCreated, are called by the GLSurfaceView to inform the renderer that its size has changed or that it should prepare to draw, respectively. Finally, drawFrame, the heart of the interface, is called to render a new OpenGL frame.

Example 12-16 shows the important methods from the implementation of an OpenGL renderer.

Example 12-16. Frame-by-frame animation with OpenGL // ... some state set up in the constructor

^Override public void surfaceCreated(GL10 gl) { // set up the surface gl.glDisable(GL10.GL_DITHER);

gl.glHint(

GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

gl.glShadeModel(GL10.GL_SMOOTH);

gl.glEnable(GL10.GL_DEPTH_TEST);

// fetch the checker-board initImage(gl);

@Override public void drawFrame(GL10 gl) {

gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity();

GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

// apply the checker-board to the shape gl.glActiveTexture(GL10.GL_TEXTURE0);

gl.glTexEnvx(

GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

// animation int t = (int) (SystemClock.uptimeMillis() % (10 * 1000L)); gl.glTranslatef(6.0f - (0.0013f * t), 0, 0);

// draw gl.glFrontFace(GL10.GL_CCW);

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf); gl.glEnable(GL10.GL_TEXTURE_2D);

gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuf); gl.glDrawElements(

GL10.GL_TRIANGLE_STRIP, 5,

GL10.GL_UNSIGNED_SHORT, indexBuf);

private void initImage(GL10 gl) { int[] textures = new int[1]; gl.glGenTextures(1, textures, 0); gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); gl.glTexParameterf(

GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); gl.glTexEnvf(

GL10.GL_TEXTURE_ENV,

GL10.GL_TEXTURE_ENV_MODE,

GL10.GL_REPLACE);

InputStream in

= context.getResources().openRawResource(R.drawable.cb); Bitmap image;

try { image = BitmapFactory.decodeStream(in); } finally {

GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, image, 0); image.recycle();

The surfaceCreated method prepares the scene. It sets several OpenGL attributes that need to be initialized only when the widget gets a new drawing surface. In addition, it calls initImage, which reads in a bitmap resource and stores it as a 2D texture. Finally, when drawFrame is called, everything is ready for drawing. The texture is applied to a plane whose vertices were set up in vertexBuf by the constructor, the animation phase is chosen, and the scene is redrawn.

CHAPTER 13

0 0

Post a comment