Vertices Class

Let's make our code easier to write by creating a Vertices class that can hold a maximum number of vertices and, optionally, indices to be used for rendering. It should also take care of enabling all the states needed for rendering, as well as cleaning up the states after rendering has finished, so that other code can rely on a clean set of OpenGL ES states. Listing 7-10 shows our easy-to-use Vertices class.

Listing 7-10. Vertices.java; Encapsulating (Indexed) Vertices package com.badlogic.androidgames.framework.gl;

import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer;

import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import com.badlogic.androidgames.framework.impl.GLGraphics;

public class Vertices {

final GLGraphics glGraphics; final boolean hasColor; final boolean hasTexCoords; final int vertexSize; final FloatBuffer vertices; final ShortBuffer indices;

The Vertices class has a reference to the GLGraphics instance, so we can get ahold of the GL10 instance when we need it. We also store whether the vertices have colors and texture coordinates. This gives us great flexibility, as we can choose the minimal set of attributes we need for rendering. We also store a FloatBuffer that holds our vertices and a ShortBuffer that holds the optional indices.

public Vertices(GLGraphics glGraphics, int maxVertices, int maxIndices, boolean hasColor, boolean hasTexCoords) {

this.glGraphics = glGraphics; this.hasColor = hasColor; this.hasTexCoords = hasTexCoords;

this.vertexSize = (2 + (hasColor?4:0) + (hasTexCoords?2:0)) * 4;

ByteBuffer buffer = ByteBuffer.allocateDirect(maxVertices * vertexSize); buffer.order(ByteOrder.nativeOrder()); vertices = buffer.asFloatBuffer();

buffer = ByteBuffer.aIIocateDirect(maxIndices * Short.SIZE / 8); buffer.order(ByteOrder.nativeOrder()); indices = buffer.asShortBuffer(); } else {

indices = null;

In the constructor, we specify how many vertices and indices our Vertices instance can hold maximally, as well as whether the vertices have colors or texture coordinates. Inside the constructor, we then set the members accordingly, and instantiate the buffers. Note that the ShortBuffer will be set to null if maxIndices is zero. Our rendering will be performed nonindexed in that case.

public void setVertices(float[] vertices, int offset, int length) { this.vertices.clear();

this.vertices.put(vertices, offset, length); this.vertices.flip();

public void setIndices(short[] indices, int offset, int length) { this.indices.clear();

this.indices.put(indices, offset, length);

this.indices.flip();

Next up are the setVertices() and setIndices() methods. The latter will throw a NullPointerException in case the Vertices instance does not store indices. All we do is clear the buffers and copy the contents of the arrays.

public void draw(int primitiveType, int offset, int numVertices) { GL10 gl = glGraphics.getGL();

gl.glEnableClientState(GL10. GL_VERTEX_ARRAY); vertices.position(0);

gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices);

if(hasColor) {

gl.glEnableClientState(GL10.GL_COLOR_ARRAY); vertices.position(2);

gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices);

if(hasTexCoords) {

gl.glEnableClientState(GL10. GL_TEXTURE_COORD_ARRAY); vertices.position(hasColor?6:2);

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

indices.position(offset);

gl.glDrawElements(primitiveType, numVertices, GL10.GL_UNSIGNED_SHORT, indices);

gl.glDrawArrays(primitiveType, offset, numVertices);

if(hasTexCoords)

gl.glDisableClientState(GL10. GL_TEXTURE_COORD_ARRAY);

if(hasColor)

gl.glDisableClientState(GL10. GL_COLOR_ARRAY);

The final method of the Vertices class is draw(). It takes the type of the primitive (e.g., GL10.GL_TRIANGLES), the offset into the vertices buffer (or the indices buffer if we use indices), and the number of vertices to use for rendering. Depending on whether the vertices have colors and texture coordinates, we enable the relevant OpenGL ES states and tell OpenGL ES where to find the data. We do the same for the vertex positions, of course, which are always needed. Depending on whether indices are used or not, we either call glDrawElements() or glDrawArrays() with the parameters passed to the method. Note that the offset parameter can also be used in case of indexed rendering: we simply set the position of the indices buffer accordingly so that OpenGL ES starts reading the indices from that offset instead of the first index of the indices buffer. The last thing we do in the draw() method is clean up the OpenGL ES state a little. We call glDisableClientState() with either GL10.GL_C0L0R_ARRAY or

GL10.GL_TEXTURE_C00RD_ARRAY in case our vertices have these attributes. We need to do this, as another instance of Vertices might not use those attributes. If we rendered that other Vertices instance, OpenGL ES would still look for colors and/or texture coordinates.

We could replace all the tedious code in the constructor of our preceding example with the following snippet:

Vertices vertices = new

Vertices(glGraphic:

4, 6,

false,

1 true);

vertices.setVertices(new float[] { 100.0f,

100.0f,

0.0f,

1.0f,

228.0f,

100.0f,

1.0f,

1.0f,

228.0f,

228.0f,

1.0f,

0.0f,

100.0f,

228.0f,

0.0f,

0.0f }, 0, 16);

vertices.setIndices(new

short[] { 0, 1, 2,

2, 3, 0

}, 0,

6);

Likewise, we could replace all the calls for setting up our vertex attribute arrays and rendering with a single call to the following:

vertices.draw(GL10.GL_TRIANGLES, 0, 6);

Together with our Texture class we have a pretty nice basis for all our 2D OpenGL ES rendering now. One of the things we are still missing to be able to completely reproduce all our Canvas rendering abilities is, though, blending. Let's have a look at that.

0 0

Post a comment