/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.replica.replicaisland; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import android.util.Log; /** * A 2D rectangular mesh. Can be drawn textured or untextured. * This version is modified from the original Grid.java (found in * the SpriteText package in the APIDemos Android sample) to support hardware * vertex buffers and to insert edges between grid squares for tiling. */ class Grid { private static final int FLOAT_SIZE = 4; private static final int FIXED_SIZE = 4; private static final int CHAR_SIZE = 2; private FloatBuffer mFloatVertexBuffer; private FloatBuffer mFloatTexCoordBuffer; private IntBuffer mFixedVertexBuffer; private IntBuffer mFixedTexCoordBuffer; private CharBuffer mIndexBuffer; private Buffer mVertexBuffer; private Buffer mTexCoordBuffer; private int mCoordinateSize; private int mCoordinateType; private int mVertsAcross; private int mVertsDown; private int mIndexCount; private boolean mUseHardwareBuffers; private int mVertBufferIndex; private int mIndexBufferIndex; private int mTextureCoordBufferIndex; public Grid(int quadsAcross, int quadsDown, boolean useFixedPoint) { final int vertsAcross = quadsAcross * 2; final int vertsDown = quadsDown * 2; if (vertsAcross < 0 || vertsAcross >= 65536) { throw new IllegalArgumentException("quadsAcross"); } if (vertsDown < 0 || vertsDown >= 65536) { throw new IllegalArgumentException("quadsDown"); } if (vertsAcross * vertsDown >= 65536) { throw new IllegalArgumentException("quadsAcross * quadsDown >= 32768"); } mUseHardwareBuffers = false; mVertsAcross = vertsAcross; mVertsDown = vertsDown; int size = vertsAcross * vertsDown; if (useFixedPoint) { mFixedVertexBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 3) .order(ByteOrder.nativeOrder()).asIntBuffer(); mFixedTexCoordBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 2) .order(ByteOrder.nativeOrder()).asIntBuffer(); mVertexBuffer = mFixedVertexBuffer; mTexCoordBuffer = mFixedTexCoordBuffer; mCoordinateSize = FIXED_SIZE; mCoordinateType = GL10.GL_FIXED; } else { mFloatVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mFloatTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mVertexBuffer = mFloatVertexBuffer; mTexCoordBuffer = mFloatTexCoordBuffer; mCoordinateSize = FLOAT_SIZE; mCoordinateType = GL10.GL_FLOAT; } int quadCount = quadsAcross * quadsDown; int indexCount = quadCount * 6; mIndexCount = indexCount; mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount) .order(ByteOrder.nativeOrder()).asCharBuffer(); /* * Initialize triangle list mesh. * * [0]------[1] [2]------[3] ... * | / | | / | * | / | | / | * | / | | / | * [w]-----[w+1] [w+2]----[w+3]... * | | * */ { int i = 0; for (int y = 0; y < quadsDown; y++) { final int indexY = y * 2; for (int x = 0; x < quadsAcross; x++) { final int indexX = x * 2; char a = (char) (indexY * mVertsAcross + indexX); char b = (char) (indexY * mVertsAcross + indexX + 1); char c = (char) ((indexY + 1) * mVertsAcross + indexX); char d = (char) ((indexY + 1) * mVertsAcross + indexX + 1); mIndexBuffer.put(i++, a); mIndexBuffer.put(i++, b); mIndexBuffer.put(i++, c); mIndexBuffer.put(i++, b); mIndexBuffer.put(i++, c); mIndexBuffer.put(i++, d); } } } mVertBufferIndex = 0; } public void set(int quadX, int quadY, float[][] positions, float[][] uvs) { if (quadX < 0 || quadX * 2 >= mVertsAcross) { throw new IllegalArgumentException("quadX"); } if (quadY < 0 || quadY * 2 >= mVertsDown) { throw new IllegalArgumentException("quadY"); } if (positions.length < 4) { throw new IllegalArgumentException("positions"); } if (uvs.length < 4) { throw new IllegalArgumentException("quadY"); } int i = quadX * 2; int j = quadY * 2; setVertex(i, j, positions[0][0], positions[0][1], positions[0][2], uvs[0][0], uvs[0][1]); setVertex(i + 1, j, positions[1][0], positions[1][1], positions[1][2], uvs[1][0], uvs[1][1]); setVertex(i, j + 1, positions[2][0], positions[2][1], positions[2][2], uvs[2][0], uvs[2][1]); setVertex(i + 1, j + 1, positions[3][0], positions[3][1], positions[3][2], uvs[3][0], uvs[3][1]); } private void setVertex(int i, int j, float x, float y, float z, float u, float v) { if (i < 0 || i >= mVertsAcross) { throw new IllegalArgumentException("i"); } if (j < 0 || j >= mVertsDown) { throw new IllegalArgumentException("j"); } final int index = mVertsAcross * j + i; final int posIndex = index * 3; final int texIndex = index * 2; if (mCoordinateType == GL10.GL_FLOAT) { mFloatVertexBuffer.put(posIndex, x); mFloatVertexBuffer.put(posIndex + 1, y); mFloatVertexBuffer.put(posIndex + 2, z); mFloatTexCoordBuffer.put(texIndex, u); mFloatTexCoordBuffer.put(texIndex + 1, v); } else { mFixedVertexBuffer.put(posIndex, (int)(x * (1 << 16))); mFixedVertexBuffer.put(posIndex + 1, (int)(y * (1 << 16))); mFixedVertexBuffer.put(posIndex + 2, (int)(z * (1 << 16))); mFixedTexCoordBuffer.put(texIndex, (int)(u * (1 << 16))); mFixedTexCoordBuffer.put(texIndex + 1, (int)(v * (1 << 16))); } } public static void beginDrawing(GL10 gl, boolean useTexture) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); if (useTexture) { gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glEnable(GL10.GL_TEXTURE_2D); } else { gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisable(GL10.GL_TEXTURE_2D); } } public void beginDrawingStrips(GL10 gl, boolean useTexture) { beginDrawing(gl, useTexture); if (!mUseHardwareBuffers) { gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer); if (useTexture) { gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer); } } else { GL11 gl11 = (GL11)gl; // draw using hardware buffers gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex); gl11.glVertexPointer(3, mCoordinateType, 0, 0); gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex); gl11.glTexCoordPointer(2, mCoordinateType, 0, 0); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex); } } // Assumes beginDrawingStrips() has been called before this. public void drawStrip(GL10 gl, boolean useTexture, int startIndex, int indexCount) { int count = indexCount; if (startIndex + indexCount >= mIndexCount) { count = mIndexCount - startIndex; } if (!mUseHardwareBuffers) { gl.glDrawElements(GL10.GL_TRIANGLES, count, GL10.GL_UNSIGNED_SHORT, mIndexBuffer.position(startIndex)); } else { GL11 gl11 = (GL11)gl; gl11.glDrawElements(GL11.GL_TRIANGLES, count, GL11.GL_UNSIGNED_SHORT, startIndex * CHAR_SIZE); } } public void draw(GL10 gl, boolean useTexture) { if (!mUseHardwareBuffers) { gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer); if (useTexture) { gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer); } gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, mIndexBuffer); } else { GL11 gl11 = (GL11)gl; // draw using hardware buffers gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex); gl11.glVertexPointer(3, mCoordinateType, 0, 0); gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex); gl11.glTexCoordPointer(2, mCoordinateType, 0, 0); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex); gl11.glDrawElements(GL11.GL_TRIANGLES, mIndexCount, GL11.GL_UNSIGNED_SHORT, 0); gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0); } } public static void endDrawing(GL10 gl) { gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } public boolean usingHardwareBuffers() { return mUseHardwareBuffers; } /** * When the OpenGL ES device is lost, GL handles become invalidated. * In that case, we just want to "forget" the old handles (without * explicitly deleting them) and make new ones. */ public void invalidateHardwareBuffers() { mVertBufferIndex = 0; mIndexBufferIndex = 0; mTextureCoordBufferIndex = 0; mUseHardwareBuffers = false; } /** * Deletes the hardware buffers allocated by this object (if any). */ public void releaseHardwareBuffers(GL10 gl) { if (mUseHardwareBuffers) { if (gl instanceof GL11) { GL11 gl11 = (GL11)gl; int[] buffer = new int[1]; buffer[0] = mVertBufferIndex; gl11.glDeleteBuffers(1, buffer, 0); buffer[0] = mTextureCoordBufferIndex; gl11.glDeleteBuffers(1, buffer, 0); buffer[0] = mIndexBufferIndex; gl11.glDeleteBuffers(1, buffer, 0); } invalidateHardwareBuffers(); } } /** * Allocates hardware buffers on the graphics card and fills them with * data if a buffer has not already been previously allocated. Note that * this function uses the GL_OES_vertex_buffer_object extension, which is * not guaranteed to be supported on every device. * @param gl A pointer to the OpenGL ES context. */ public void generateHardwareBuffers(GL10 gl) { if (!mUseHardwareBuffers) { DebugLog.i("Grid", "Using Hardware Buffers"); if (gl instanceof GL11) { GL11 gl11 = (GL11)gl; int[] buffer = new int[1]; // Allocate and fill the vertex buffer. gl11.glGenBuffers(1, buffer, 0); mVertBufferIndex = buffer[0]; gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex); final int vertexSize = mVertexBuffer.capacity() * mCoordinateSize; // too fast task switching leaves buffers in the middle pos which // crashes app mVertexBuffer.position(0); gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize, mVertexBuffer, GL11.GL_STATIC_DRAW); // Allocate and fill the texture coordinate buffer. gl11.glGenBuffers(1, buffer, 0); mTextureCoordBufferIndex = buffer[0]; gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex); final int texCoordSize = mTexCoordBuffer.capacity() * mCoordinateSize; mTexCoordBuffer.position(0); gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mTexCoordBuffer, GL11.GL_STATIC_DRAW); // Unbind the array buffer. gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); // Allocate and fill the index buffer. gl11.glGenBuffers(1, buffer, 0); mIndexBufferIndex = buffer[0]; gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex); // A char is 2 bytes. final int indexSize = mIndexBuffer.capacity() * 2; mIndexBuffer.position(0); gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, indexSize, mIndexBuffer, GL11.GL_STATIC_DRAW); // Unbind the element array buffer. gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0); mUseHardwareBuffers = true; assert mVertBufferIndex != 0; assert mTextureCoordBufferIndex != 0; assert mIndexBufferIndex != 0; assert gl11.glGetError() == 0; } } } }