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