GridQuad.java revision 3c02f2877dc2f8f0b5c01d03fa2b487c040e4000
1/*
2 * Copyright (C) 2009 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.cooliris.media;
18
19import java.nio.ByteBuffer;
20import java.nio.ByteOrder;
21import java.nio.CharBuffer;
22import java.nio.FloatBuffer;
23
24import javax.microedition.khronos.opengles.GL10;
25import javax.microedition.khronos.opengles.GL11;
26
27/**
28 * A 2D rectangular mesh. Can be drawn textured or untextured. This version is
29 * modified from the original Grid.java (found in the SpriteText package in the
30 * APIDemos Android sample) to support hardware vertex buffers.
31 */
32
33final class GridQuad {
34    private FloatBuffer mVertexBuffer;
35    private FloatBuffer mOverlayTexCoordBuffer;
36    private CharBuffer mIndexBuffer;
37
38    private int mW;
39    private int mH;
40    private static final int INDEX_COUNT = 4;
41    private static final int ORIENTATION_COUNT = 360;
42    private int mVertBufferIndex;
43    private int mIndexBufferIndex;
44    private int mOverlayTextureCoordBufferIndex;
45    private boolean mDynamicVBO;
46    private float mU;
47    private float mV;
48    private float mAnimU;
49    private float mAnimV;
50    private float mWidth;
51    private float mHeight;
52    private float mAnimWidth;
53    private float mAnimHeight;
54    private boolean mQuadChanged;
55    private float mDefaultAspectRatio;
56    private int mBaseTextureCoordBufferIndex;
57    private FloatBuffer mBaseTexCoordBuffer;
58    private final boolean mOrientedQuad;
59    private MatrixStack mMatrix;
60    private float[] mCoordsIn = new float[4];
61    private float[] mCoordsOut = new float[4];
62
63    public static GridQuad createGridQuad(float width, float height, float xOffset, float yOffset, float uExtents, float vExtents,
64            boolean generateOrientedQuads) {
65        // generateOrientedQuads = false;
66        GridQuad grid = new GridQuad(generateOrientedQuads);
67        grid.mWidth = width;
68        grid.mHeight = height;
69        grid.mAnimWidth = width;
70        grid.mAnimHeight = height;
71        grid.mDefaultAspectRatio = width / height;
72        float widthBy2 = width * 0.5f;
73        float heightBy2 = height * 0.5f;
74        final float v = vExtents;
75        final float u = uExtents;
76        if (!generateOrientedQuads) {
77            grid.set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v);
78            grid.set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v);
79            grid.set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f);
80            grid.set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f);
81        } else {
82            for (int i = 0; i < ORIENTATION_COUNT; ++i) {
83                grid.set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v, true, i);
84                grid.set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v, true, i);
85                grid.set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f, true, i);
86                grid.set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f, true, i);
87            }
88        }
89        grid.mU = uExtents;
90        grid.mV = uExtents;
91        return grid;
92    }
93
94    public GridQuad(boolean generateOrientedQuads) {
95        mOrientedQuad = generateOrientedQuads;
96        if (mOrientedQuad) {
97            mMatrix = new MatrixStack();
98            mMatrix.glLoadIdentity();
99        }
100        int vertsAcross = 2;
101        int vertsDown = 2;
102        mW = vertsAcross;
103        mH = vertsDown;
104        int size = vertsAcross * vertsDown;
105        final int FLOAT_SIZE = 4;
106        final int CHAR_SIZE = 2;
107        final int orientationCount = (!generateOrientedQuads) ? 1 : ORIENTATION_COUNT;
108        mVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3 * orientationCount).order(ByteOrder.nativeOrder())
109                .asFloatBuffer();
110        mOverlayTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2 * orientationCount).order(ByteOrder.nativeOrder())
111                .asFloatBuffer();
112        mBaseTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2 * orientationCount).order(ByteOrder.nativeOrder())
113                .asFloatBuffer();
114
115        int indexCount = INDEX_COUNT; // using tristrips
116        mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount * orientationCount).order(ByteOrder.nativeOrder())
117                .asCharBuffer();
118
119        /*
120         * Initialize triangle list mesh.
121         *
122         * [0]-----[ 1] ... | / | | / | | / | [w]-----[w+1] ... | |
123         */
124        CharBuffer buffer = mIndexBuffer;
125        for (int i = 0; i < INDEX_COUNT * orientationCount; ++i) {
126            buffer.put(i, (char) i);
127        }
128        mVertBufferIndex = 0;
129    }
130
131    public void setDynamic(boolean dynamic) {
132        mDynamicVBO = dynamic;
133        if (mOrientedQuad) {
134            throw new UnsupportedOperationException("Dynamic Quads can't have orientations");
135        }
136    }
137
138    public float getWidth() {
139        return mWidth;
140    }
141
142    public float getHeight() {
143        return mHeight;
144    }
145
146    public void update(float timeElapsed) {
147        mAnimWidth = FloatUtils.animate(mAnimWidth, mWidth, timeElapsed);
148        mAnimHeight = FloatUtils.animate(mAnimHeight, mHeight, timeElapsed);
149        mAnimU = FloatUtils.animate(mAnimU, mU, timeElapsed);
150        mAnimV = FloatUtils.animate(mAnimV, mV, timeElapsed);
151        recomputeQuad();
152    }
153
154    public void commit() {
155        mAnimWidth = mWidth;
156        mAnimHeight = mHeight;
157        mAnimU = mU;
158        mAnimV = mV;
159    }
160
161    public void recomputeQuad() {
162        mVertexBuffer.clear();
163        mBaseTexCoordBuffer.clear();
164        float widthBy2 = mAnimWidth * 0.5f;
165        float heightBy2 = mAnimHeight * 0.5f;
166        float xOffset = 0.0f;
167        float yOffset = 0.0f;
168        float u = mU;
169        float v = mV;
170        set(0, 0, -widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, u, v, false, 0);
171        set(1, 0, widthBy2 + xOffset, -heightBy2 + yOffset, 0.0f, 0.0f, v, false, 0);
172        set(0, 1, -widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, u, 0.0f, false, 0);
173        set(1, 1, widthBy2 + xOffset, heightBy2 + yOffset, 0.0f, 0.0f, 0.0f, false, 0);
174        mQuadChanged = true;
175    }
176
177    public void resizeQuad(float viewAspect, float u, float v, float imageWidth, float imageHeight) {
178        // given the u,v; we know the aspect ratio of the image
179        // we have to change one of the co-ords depending upon the image and
180        // viewport aspect ratio
181        mU = u;
182        mV = v;
183        float imageAspect = imageWidth / imageHeight;
184        float width = mDefaultAspectRatio;
185        float height = 1.0f;
186        if (viewAspect < 1.0f) {
187            height = height * (mDefaultAspectRatio / imageAspect);
188            float maxHeight = width / viewAspect;
189            if (height > maxHeight) {
190                // we need to reduce the width and height proportionately
191                float ratio = height / maxHeight;
192                height /= ratio;
193                width /= ratio;
194            }
195        } else {
196            width = width * (imageAspect / mDefaultAspectRatio);
197            float maxWidth = height * viewAspect;
198            if (width > maxWidth) {
199                float ratio = width / maxWidth;
200                width /= ratio;
201                height /= ratio;
202            }
203        }
204        mWidth = width;
205        mHeight = height;
206        commit();
207        recomputeQuad();
208    }
209
210    public void set(int i, int j, float x, float y, float z, float u, float v) {
211        set(i, j, x, y, z, u, v, true, 0);
212    }
213
214    private void set(int i, int j, float x, float y, float z, float u, float v, boolean modifyOverlay, int orientationId) {
215        if (i < 0 || i >= mW) {
216            throw new IllegalArgumentException("i");
217        }
218        if (j < 0 || j >= mH) {
219            throw new IllegalArgumentException("j");
220        }
221        int index = orientationId * INDEX_COUNT + mW * j + i;
222        int posIndex = index * 3;
223        mVertexBuffer.put(posIndex, x);
224        mVertexBuffer.put(posIndex + 1, y);
225        mVertexBuffer.put(posIndex + 2, z);
226        int baseTexIndex = index * 2;
227        // we can calculate the u,v for the orientation here
228        MatrixStack matrix = mMatrix;
229        if (matrix != null) {
230            orientationId *= 2;
231            matrix.glLoadIdentity();
232            matrix.glTranslatef(0.5f, 0.5f, 0.0f);
233            float itheta = (float) Math.toRadians(orientationId);
234            float sini = (float) Math.sin(itheta);
235            float scale = 1.0f + (sini * sini) * 0.33333333f;
236            scale = 1.0f / scale;
237            matrix.glRotatef(-orientationId, 0.0f, 0.0f, 1.0f);
238            matrix.glScalef(scale, scale, 1.0f);
239            matrix.glTranslatef(-0.5f + (float) (sini * 0.125f / scale), -0.5f
240                    + (float) (Math.abs(Math.sin(itheta * 0.5f) * 0.25f)), 0.0f);
241            // now we have the desired matrix
242            // populate s,t,r,q
243            // http://glprogramming.com/red/chapter09.html
244            mCoordsIn[0] = u;
245            mCoordsIn[1] = v;
246            mCoordsIn[2] = 0.0f;
247            mCoordsIn[3] = 1.0f;
248            matrix.apply(mCoordsIn, mCoordsOut);
249            u = mCoordsOut[0] / mCoordsOut[3];
250            v = mCoordsOut[1] / mCoordsOut[3];
251        }
252        mBaseTexCoordBuffer.put(baseTexIndex, u);
253        mBaseTexCoordBuffer.put(baseTexIndex + 1, v);
254        if (modifyOverlay) {
255            int texIndex = index * 2;
256            mOverlayTexCoordBuffer.put(texIndex, u);
257            mOverlayTexCoordBuffer.put(texIndex + 1, v);
258        }
259    }
260
261    public void bindArrays(GL10 gl) {
262        GL11 gl11 = (GL11) gl;
263        // draw using hardware buffers
264        gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
265        gl11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0);
266
267        gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mOverlayTextureCoordBufferIndex);
268        if (mDynamicVBO && mQuadChanged) {
269            final int texCoordSize = mOverlayTexCoordBuffer.capacity() * 4;
270            mOverlayTexCoordBuffer.position(0);
271            gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mOverlayTexCoordBuffer, GL11.GL_DYNAMIC_DRAW);
272        }
273        gl11.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
274        gl11.glClientActiveTexture(GL11.GL_TEXTURE1);
275        if (mDynamicVBO && mQuadChanged) {
276            final int texCoordSize = mBaseTexCoordBuffer.capacity() * 4;
277            mBaseTexCoordBuffer.position(0);
278            gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mBaseTexCoordBuffer, GL11.GL_DYNAMIC_DRAW);
279        }
280        gl11.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);
281        gl11.glClientActiveTexture(GL11.GL_TEXTURE0);
282        if (mDynamicVBO && mQuadChanged) {
283            mQuadChanged = false;
284            gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
285            final int vertexSize = mVertexBuffer.capacity() * 4;
286            mVertexBuffer.position(0);
287            gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize, mVertexBuffer, GL11.GL_DYNAMIC_DRAW);
288        }
289        gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
290    }
291
292    public static final void draw(GL11 gl11, float orientationDegrees) {
293        // don't call this method unless bindArrays was called
294        int orientation = (int) Shared.normalizePositive(orientationDegrees);
295        gl11.glDrawElements(GL11.GL_TRIANGLE_STRIP, INDEX_COUNT, GL11.GL_UNSIGNED_SHORT, orientation * INDEX_COUNT);
296    }
297
298    public void unbindArrays(GL10 gl) {
299        GL11 gl11 = (GL11) gl;
300        gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
301        gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
302    }
303
304    public boolean usingHardwareBuffers() {
305        return mVertBufferIndex != 0;
306    }
307
308    /**
309     * When the OpenGL ES device is lost, GL handles become invalidated. In that
310     * case, we just want to "forget" the old handles (without explicitly
311     * deleting them) and make new ones.
312     */
313    public void forgetHardwareBuffers() {
314        mVertBufferIndex = 0;
315        mIndexBufferIndex = 0;
316        mOverlayTextureCoordBufferIndex = 0;
317    }
318
319    /**
320     * Deletes the hardware buffers allocated by this object (if any).
321     */
322    public void freeHardwareBuffers(GL10 gl) {
323        if (mVertBufferIndex != 0) {
324            if (gl instanceof GL11) {
325                GL11 gl11 = (GL11) gl;
326                int[] buffer = new int[1];
327                buffer[0] = mVertBufferIndex;
328                gl11.glDeleteBuffers(1, buffer, 0);
329
330                buffer[0] = mOverlayTextureCoordBufferIndex;
331                gl11.glDeleteBuffers(1, buffer, 0);
332
333                buffer[0] = mIndexBufferIndex;
334                gl11.glDeleteBuffers(1, buffer, 0);
335            }
336
337            forgetHardwareBuffers();
338        }
339    }
340
341    /**
342     * Allocates hardware buffers on the graphics card and fills them with data
343     * if a buffer has not already been previously allocated. Note that this
344     * function uses the GL_OES_vertex_buffer_object extension, which is not
345     * guaranteed to be supported on every device.
346     *
347     * @param gl
348     *            A pointer to the OpenGL ES context.
349     */
350
351    public void generateHardwareBuffers(GL10 gl) {
352        if (mVertBufferIndex == 0) {
353            if (gl instanceof GL11) {
354                GL11 gl11 = (GL11) gl;
355                int[] buffer = new int[1];
356
357                // Allocate and fill the vertex buffer.
358                gl11.glGenBuffers(1, buffer, 0);
359                mVertBufferIndex = buffer[0];
360                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
361                final int vertexSize = mVertexBuffer.capacity() * 4;
362                int bufferType = (mDynamicVBO) ? GL11.GL_DYNAMIC_DRAW : GL11.GL_STATIC_DRAW;
363                mVertexBuffer.position(0);
364                gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize, mVertexBuffer, bufferType);
365
366                // Allocate and fill the texture coordinate buffer.
367                gl11.glGenBuffers(1, buffer, 0);
368                mOverlayTextureCoordBufferIndex = buffer[0];
369                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mOverlayTextureCoordBufferIndex);
370                final int texCoordSize = mOverlayTexCoordBuffer.capacity() * 4;
371                mOverlayTexCoordBuffer.position(0);
372                gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mOverlayTexCoordBuffer, bufferType);
373
374                gl11.glGenBuffers(1, buffer, 0);
375                mBaseTextureCoordBufferIndex = buffer[0];
376                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mBaseTextureCoordBufferIndex);
377                mBaseTexCoordBuffer.position(0);
378                gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize, mBaseTexCoordBuffer, bufferType);
379
380                // Unbind the array buffer.
381                gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
382
383                // Allocate and fill the index buffer.
384                gl11.glGenBuffers(1, buffer, 0);
385                mIndexBufferIndex = buffer[0];
386                gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
387                // A char is 2 bytes.
388                final int indexSize = mIndexBuffer.capacity() * 2;
389                mIndexBuffer.position(0);
390                gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, indexSize, mIndexBuffer, GL11.GL_STATIC_DRAW);
391
392                // Unbind the element array buffer.
393                gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
394            }
395        }
396    }
397
398}
399