1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.replica.replicaisland;
18
19import java.nio.Buffer;
20import java.nio.ByteBuffer;
21import java.nio.ByteOrder;
22import java.nio.CharBuffer;
23import java.nio.FloatBuffer;
24import java.nio.IntBuffer;
25
26import javax.microedition.khronos.opengles.GL10;
27import javax.microedition.khronos.opengles.GL11;
28
29import android.util.Log;
30
31/**
32 * A 2D rectangular mesh. Can be drawn textured or untextured.
33 * This version is modified from the original Grid.java (found in
34 * the SpriteText package in the APIDemos Android sample) to support hardware
35 * vertex buffers and to insert edges between grid squares for tiling.
36 */
37class Grid {
38	private static final int FLOAT_SIZE = 4;
39	private static final int FIXED_SIZE = 4;
40	private static final int CHAR_SIZE = 2;
41
42    private FloatBuffer mFloatVertexBuffer;
43    private FloatBuffer mFloatTexCoordBuffer;
44    private IntBuffer mFixedVertexBuffer;
45    private IntBuffer mFixedTexCoordBuffer;
46    private CharBuffer mIndexBuffer;
47
48    private Buffer mVertexBuffer;
49    private Buffer mTexCoordBuffer;
50    private int mCoordinateSize;
51    private int mCoordinateType;
52
53    private int mVertsAcross;
54    private int mVertsDown;
55    private int mIndexCount;
56    private boolean mUseHardwareBuffers;
57    private int mVertBufferIndex;
58    private int mIndexBufferIndex;
59    private int mTextureCoordBufferIndex;
60
61    public Grid(int quadsAcross, int quadsDown, boolean useFixedPoint) {
62    	final int vertsAcross = quadsAcross * 2;
63    	final int vertsDown = quadsDown * 2;
64        if (vertsAcross < 0 || vertsAcross >= 65536) {
65            throw new IllegalArgumentException("quadsAcross");
66        }
67        if (vertsDown < 0 || vertsDown >= 65536) {
68            throw new IllegalArgumentException("quadsDown");
69        }
70        if (vertsAcross * vertsDown >= 65536) {
71            throw new IllegalArgumentException("quadsAcross * quadsDown >= 32768");
72        }
73
74        mUseHardwareBuffers = false;
75
76        mVertsAcross = vertsAcross;
77        mVertsDown = vertsDown;
78        int size = vertsAcross * vertsDown;
79
80
81        if (useFixedPoint) {
82        	mFixedVertexBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 3)
83            	.order(ByteOrder.nativeOrder()).asIntBuffer();
84        	mFixedTexCoordBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 2)
85            	.order(ByteOrder.nativeOrder()).asIntBuffer();
86
87        	mVertexBuffer = mFixedVertexBuffer;
88        	mTexCoordBuffer = mFixedTexCoordBuffer;
89        	mCoordinateSize = FIXED_SIZE;
90        	mCoordinateType = GL10.GL_FIXED;
91
92        } else {
93        	mFloatVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3)
94            	.order(ByteOrder.nativeOrder()).asFloatBuffer();
95        	mFloatTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2)
96            	.order(ByteOrder.nativeOrder()).asFloatBuffer();
97
98        	mVertexBuffer = mFloatVertexBuffer;
99        	mTexCoordBuffer = mFloatTexCoordBuffer;
100        	mCoordinateSize = FLOAT_SIZE;
101        	mCoordinateType = GL10.GL_FLOAT;
102        }
103
104
105
106
107        int quadCount = quadsAcross * quadsDown;
108        int indexCount = quadCount * 6;
109        mIndexCount = indexCount;
110        mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
111            .order(ByteOrder.nativeOrder()).asCharBuffer();
112
113        /*
114         * Initialize triangle list mesh.
115         *
116         *     [0]------[1]   [2]------[3] ...
117         *      |    /   |     |    /   |
118         *      |   /    |     |   /    |
119         *      |  /     |     |  /     |
120         *     [w]-----[w+1] [w+2]----[w+3]...
121         *      |       |
122         *
123         */
124
125        {
126            int i = 0;
127            for (int y = 0; y < quadsDown; y++) {
128            	final int indexY = y * 2;
129                for (int x = 0; x < quadsAcross; x++) {
130                	final int indexX = x * 2;
131                    char a = (char) (indexY * mVertsAcross + indexX);
132                    char b = (char) (indexY * mVertsAcross + indexX + 1);
133                    char c = (char) ((indexY + 1) * mVertsAcross + indexX);
134                    char d = (char) ((indexY + 1) * mVertsAcross + indexX + 1);
135
136                    mIndexBuffer.put(i++, a);
137                    mIndexBuffer.put(i++, b);
138                    mIndexBuffer.put(i++, c);
139
140                    mIndexBuffer.put(i++, b);
141                    mIndexBuffer.put(i++, c);
142                    mIndexBuffer.put(i++, d);
143                }
144            }
145        }
146
147        mVertBufferIndex = 0;
148    }
149
150    public void set(int quadX, int quadY, float[][] positions, float[][] uvs) {
151        if (quadX < 0 || quadX * 2 >= mVertsAcross) {
152            throw new IllegalArgumentException("quadX");
153        }
154        if (quadY < 0 || quadY * 2 >= mVertsDown) {
155            throw new IllegalArgumentException("quadY");
156        }
157        if (positions.length < 4) {
158            throw new IllegalArgumentException("positions");
159        }
160        if (uvs.length < 4) {
161            throw new IllegalArgumentException("quadY");
162        }
163
164        int i = quadX * 2;
165        int j = quadY * 2;
166
167        setVertex(i, j, 		positions[0][0], positions[0][1], positions[0][2], uvs[0][0], uvs[0][1]);
168        setVertex(i + 1, j, 	positions[1][0], positions[1][1], positions[1][2], uvs[1][0], uvs[1][1]);
169        setVertex(i, j + 1, 	positions[2][0], positions[2][1], positions[2][2], uvs[2][0], uvs[2][1]);
170        setVertex(i + 1, j + 1, positions[3][0], positions[3][1], positions[3][2], uvs[3][0], uvs[3][1]);
171    }
172
173
174    private void setVertex(int i, int j, float x, float y, float z, float u, float v) {
175	  if (i < 0 || i >= mVertsAcross) {
176	       throw new IllegalArgumentException("i");
177	   }
178	   if (j < 0 || j >= mVertsDown) {
179	       throw new IllegalArgumentException("j");
180	   }
181
182	   final int index = mVertsAcross * j + i;
183
184	   final int posIndex = index * 3;
185	   final int texIndex = index * 2;
186
187
188	   if (mCoordinateType == GL10.GL_FLOAT) {
189	    mFloatVertexBuffer.put(posIndex, x);
190	    mFloatVertexBuffer.put(posIndex + 1, y);
191	    mFloatVertexBuffer.put(posIndex + 2, z);
192
193	    mFloatTexCoordBuffer.put(texIndex, u);
194	    mFloatTexCoordBuffer.put(texIndex + 1, v);
195	   } else {
196	    mFixedVertexBuffer.put(posIndex, (int)(x * (1 << 16)));
197	    mFixedVertexBuffer.put(posIndex + 1, (int)(y * (1 << 16)));
198	    mFixedVertexBuffer.put(posIndex + 2, (int)(z * (1 << 16)));
199
200	    mFixedTexCoordBuffer.put(texIndex, (int)(u * (1 << 16)));
201	    mFixedTexCoordBuffer.put(texIndex + 1, (int)(v * (1 << 16)));
202	   }
203	}
204
205    public static void beginDrawing(GL10 gl, boolean useTexture) {
206        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
207
208        if (useTexture) {
209            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
210            gl.glEnable(GL10.GL_TEXTURE_2D);
211        } else {
212            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
213            gl.glDisable(GL10.GL_TEXTURE_2D);
214        }
215    }
216
217    public void beginDrawingStrips(GL10 gl, boolean useTexture) {
218        beginDrawing(gl, useTexture);
219        if (!mUseHardwareBuffers) {
220            gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer);
221
222            if (useTexture) {
223                gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer);
224            }
225
226        } else {
227            GL11 gl11 = (GL11)gl;
228            // draw using hardware buffers
229            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
230            gl11.glVertexPointer(3, mCoordinateType, 0, 0);
231
232            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex);
233            gl11.glTexCoordPointer(2, mCoordinateType, 0, 0);
234
235            gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
236        }
237    }
238
239    // Assumes beginDrawingStrips() has been called before this.
240    public void drawStrip(GL10 gl, boolean useTexture, int startIndex, int indexCount) {
241    	int count = indexCount;
242    	if (startIndex + indexCount >= mIndexCount) {
243    		count = mIndexCount - startIndex;
244    	}
245    	if (!mUseHardwareBuffers) {
246            gl.glDrawElements(GL10.GL_TRIANGLES, count,
247                    GL10.GL_UNSIGNED_SHORT, mIndexBuffer.position(startIndex));
248        } else {
249        	GL11 gl11 = (GL11)gl;
250            gl11.glDrawElements(GL11.GL_TRIANGLES, count,
251                    GL11.GL_UNSIGNED_SHORT, startIndex * CHAR_SIZE);
252
253        }
254    }
255
256    public void draw(GL10 gl, boolean useTexture) {
257        if (!mUseHardwareBuffers) {
258            gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer);
259
260            if (useTexture) {
261                gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer);
262            }
263
264            gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount,
265                    GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
266        } else {
267            GL11 gl11 = (GL11)gl;
268            // draw using hardware buffers
269            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
270            gl11.glVertexPointer(3, mCoordinateType, 0, 0);
271
272            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex);
273            gl11.glTexCoordPointer(2, mCoordinateType, 0, 0);
274
275            gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
276            gl11.glDrawElements(GL11.GL_TRIANGLES, mIndexCount,
277                    GL11.GL_UNSIGNED_SHORT, 0);
278
279            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
280            gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
281
282
283        }
284    }
285
286    public static void endDrawing(GL10 gl) {
287        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
288    }
289
290    public boolean usingHardwareBuffers() {
291        return mUseHardwareBuffers;
292    }
293
294    /**
295     * When the OpenGL ES device is lost, GL handles become invalidated.
296     * In that case, we just want to "forget" the old handles (without
297     * explicitly deleting them) and make new ones.
298     */
299    public void invalidateHardwareBuffers() {
300        mVertBufferIndex = 0;
301        mIndexBufferIndex = 0;
302        mTextureCoordBufferIndex = 0;
303        mUseHardwareBuffers = false;
304    }
305
306    /**
307     * Deletes the hardware buffers allocated by this object (if any).
308     */
309    public void releaseHardwareBuffers(GL10 gl) {
310        if (mUseHardwareBuffers) {
311            if (gl instanceof GL11) {
312                GL11 gl11 = (GL11)gl;
313                int[] buffer = new int[1];
314                buffer[0] = mVertBufferIndex;
315                gl11.glDeleteBuffers(1, buffer, 0);
316
317                buffer[0] = mTextureCoordBufferIndex;
318                gl11.glDeleteBuffers(1, buffer, 0);
319
320                buffer[0] = mIndexBufferIndex;
321                gl11.glDeleteBuffers(1, buffer, 0);
322            }
323
324            invalidateHardwareBuffers();
325        }
326    }
327
328    /**
329     * Allocates hardware buffers on the graphics card and fills them with
330     * data if a buffer has not already been previously allocated.  Note that
331     * this function uses the GL_OES_vertex_buffer_object extension, which is
332     * not guaranteed to be supported on every device.
333     * @param gl  A pointer to the OpenGL ES context.
334     */
335    public void generateHardwareBuffers(GL10 gl) {
336        if (!mUseHardwareBuffers) {
337        	DebugLog.i("Grid", "Using Hardware Buffers");
338            if (gl instanceof GL11) {
339                GL11 gl11 = (GL11)gl;
340                int[] buffer = new int[1];
341
342                // Allocate and fill the vertex buffer.
343                gl11.glGenBuffers(1, buffer, 0);
344                mVertBufferIndex = buffer[0];
345                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
346                final int vertexSize = mVertexBuffer.capacity() * mCoordinateSize;
347                // too fast task switching leaves buffers in the middle pos which
348                // crashes app
349                mVertexBuffer.position(0);
350                gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize,
351                        mVertexBuffer, GL11.GL_STATIC_DRAW);
352
353                // Allocate and fill the texture coordinate buffer.
354                gl11.glGenBuffers(1, buffer, 0);
355                mTextureCoordBufferIndex = buffer[0];
356                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER,
357                        mTextureCoordBufferIndex);
358                final int texCoordSize =
359                    mTexCoordBuffer.capacity() * mCoordinateSize;
360                mTexCoordBuffer.position(0);
361                gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize,
362                        mTexCoordBuffer, GL11.GL_STATIC_DRAW);
363
364                // Unbind the array buffer.
365                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
366
367                // Allocate and fill the index buffer.
368                gl11.glGenBuffers(1, buffer, 0);
369                mIndexBufferIndex = buffer[0];
370                gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER,
371                        mIndexBufferIndex);
372                // A char is 2 bytes.
373                final int indexSize = mIndexBuffer.capacity() * 2;
374
375                mIndexBuffer.position(0);
376                gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, indexSize, mIndexBuffer,
377                        GL11.GL_STATIC_DRAW);
378
379                // Unbind the element array buffer.
380                gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
381
382                mUseHardwareBuffers = true;
383
384                assert mVertBufferIndex != 0;
385                assert mTextureCoordBufferIndex != 0;
386                assert mIndexBufferIndex != 0;
387                assert gl11.glGetError() == 0;
388
389
390            }
391        }
392    }
393
394}
395