Putting It Together

With that out of our way, we can now write a small example that puts all of this together. Listing 7-7 shows an excerpt of the TexturedTriangleTest.java source file, listing only the relevant parts of the TexturedTriangleScreen class contained in it.

Listing 7-7. Excerpt from TexturedTriangleTest.java; Texturing a Triangle class TexturedTriangleScreen extends Screen { final int VERTEX_SIZE = (2 + 2) * 4; GLGraphics glGraphics; FloatBuffer vertices; int textureId;

public TexturedTriangleScreen(Game game) { super(game);

glGraphics = ((GLGame) game).getGLGraphics();

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * VERTEX_SIZE); byteBuffer.order(ByteOrder.nativeOrder()); vertices = byteBuffer.asFloatBuffer(); vertices.put( new float[] { 0.0f, 0.0f, 0.0f, 1.0f,

319.0f, 0.0f, 1.0f, 1.0f, 160.0f, 479.0f, 0.5f, 0.0f});


textureId = loadTexture(Mbobrgb888.pngM);

public int loadTexture(String fileName) { try {

Bitmap bitmap =

BitmapFactory.decodeStream(game.getFileI0().readAsset(fileName)); GL10 gl = glGraphics.getGL(); int textureIds[] = new int[l]; gl.glGenTextures(l, textureIds, 0); int textureId = textureIds[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10. GL_NEAREST);


gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); bitmap.recycle(); return textureId; } catch(IOException e) {

Log.d("TexturedTriangleTest", "couldn't load asset 'bobrgb888.png'|M); throw new RuntimeException("couldn't load asset '" + fileName + );

^Override public void present(float deltaTime) { GL10 gl = glGraphics.getGL();

gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight());

gl.glClear(GL10. GL_COLOR_BUFFER_BIT);



gl.glEnable(GL10. GL_TEXTURE_2D); gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);

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


gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices); vertices.position(2);

gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);

gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

I took the freedom to put the texture loading into a method called loadTexture(), which simply takes the filename of a bitmap to be loaded. The method returns the texture object ID generated by OpenGL ES, which we'll use in the present() method to bind the texture.

The definition of our triangle shouldn't be a big surprise; we just added texture coordinates to each vertex.

The present() method does what it always does: it clears the screen and sets the projection matrix. Next we enable texture mapping via a call to glEnable() and bind our texture object. The rest is just what we did before: enabling the vertex attributes we want to use, telling OpenGL ES where it can find them and what strides to use, and finally drawing the triangle with a call to glDrawArrays(). Figure 7-13 shows the output of the preceding code.

Figure 7-13. Texture mapping Bob onto our triangle

There's one last thing I haven't mentioned yet, and it's of great importance: All bitmaps we load must have a width and height that is a power of two. Stick to it or else things will explode.

So what does this actually mean? The image of Bob that we used in our example has a size of 128x128 pixels. The value 128 is 2 to the power of 7 (2x2x2x2x2x2x2). Other valid image sizes would be 2x8, 32x16, 128x256, and so on. There's also a limit to how big our images can be. Sadly, it varies depending on the hardware our application is running on. The OpenGL ES 1.x standard doesn't specify a minimally supported texture size to my knowledge. However, from my experience it seems that 512x512-pixel textures work on all current Android devices (and most likely will work on all future devices as well). I'd even go so far to say that 1024x1024 is OK as well.

Another issue that we have pretty much ignored so far is the color depth of our textures. Luckily the method GLUtils.texImage2D(), which we used to upload our image data to the GPU, handles this for us pretty well. OpenGL ES can cope with color depths like RGBA8888, RGB565, and so on. We should always strive to use the lowest possible color depth to decrease bandwidth. For this we can employ the BitmapFactory.Options class, as in previous chapters, to load a RGB888 Bitmap to a RGB565 Bitmap in memory, for example. Once we have loaded our Bitmap instance with the color depth we want it to have, GLUtils.texImage2D() takes over and makes sure that OpenGL ES gets the image data in the correct format. Of course, you should always check whether the reduction in color depth has a negative impact on the visual fidelity of your game.

0 0

Post a comment