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 17 package com.example.android.apis.graphics; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 import java.nio.CharBuffer; 24 import java.nio.FloatBuffer; 25 26 import javax.microedition.khronos.egl.EGLConfig; 27 import javax.microedition.khronos.opengles.GL; 28 import javax.microedition.khronos.opengles.GL10; 29 import javax.microedition.khronos.opengles.GL11; 30 import javax.microedition.khronos.opengles.GL11Ext; 31 32 import android.content.Context; 33 import android.graphics.Bitmap; 34 import android.graphics.BitmapFactory; 35 import android.opengl.GLSurfaceView; 36 import android.opengl.GLU; 37 import android.opengl.GLUtils; 38 import android.os.SystemClock; 39 40 import com.example.android.apis.R; 41 42 public class MatrixPaletteRenderer implements GLSurfaceView.Renderer{ 43 private Context mContext; 44 private Grid mGrid; 45 private int mTextureID; 46 47 /** A grid is a topologically rectangular array of vertices. 48 * 49 * This grid class is customized for the vertex data required for this 50 * example. 51 * 52 * The vertex and index data are held in VBO objects because on most 53 * GPUs VBO objects are the fastest way of rendering static vertex 54 * and index data. 55 * 56 */ 57 58 private static class Grid { 59 // Size of vertex data elements in bytes: 60 final static int FLOAT_SIZE = 4; 61 final static int CHAR_SIZE = 2; 62 63 // Vertex structure: 64 // float x, y, z; 65 // float u, v; 66 // float weight0, weight1; 67 // byte palette0, palette1, pad0, pad1; 68 69 final static int VERTEX_SIZE = 8 * FLOAT_SIZE; 70 final static int VERTEX_TEXTURE_BUFFER_INDEX_OFFSET = 3; 71 final static int VERTEX_WEIGHT_BUFFER_INDEX_OFFSET = 5; 72 final static int VERTEX_PALETTE_INDEX_OFFSET = 7 * FLOAT_SIZE; 73 74 private int mVertexBufferObjectId; 75 private int mElementBufferObjectId; 76 77 // These buffers are used to hold the vertex and index data while 78 // constructing the grid. Once createBufferObjects() is called 79 // the buffers are nulled out to save memory. 80 81 private ByteBuffer mVertexByteBuffer; 82 private FloatBuffer mVertexBuffer; 83 private CharBuffer mIndexBuffer; 84 85 private int mW; 86 private int mH; 87 private int mIndexCount; 88 Grid(int w, int h)89 public Grid(int w, int h) { 90 if (w < 0 || w >= 65536) { 91 throw new IllegalArgumentException("w"); 92 } 93 if (h < 0 || h >= 65536) { 94 throw new IllegalArgumentException("h"); 95 } 96 if (w * h >= 65536) { 97 throw new IllegalArgumentException("w * h >= 65536"); 98 } 99 100 mW = w; 101 mH = h; 102 int size = w * h; 103 104 mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size) 105 .order(ByteOrder.nativeOrder()); 106 mVertexBuffer = mVertexByteBuffer.asFloatBuffer(); 107 108 int quadW = mW - 1; 109 int quadH = mH - 1; 110 int quadCount = quadW * quadH; 111 int indexCount = quadCount * 6; 112 mIndexCount = indexCount; 113 mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount) 114 .order(ByteOrder.nativeOrder()).asCharBuffer(); 115 116 /* 117 * Initialize triangle list mesh. 118 * 119 * [0]-----[ 1] ... 120 * | / | 121 * | / | 122 * | / | 123 * [w]-----[w+1] ... 124 * | | 125 * 126 */ 127 128 { 129 int i = 0; 130 for (int y = 0; y < quadH; y++) { 131 for (int x = 0; x < quadW; x++) { 132 char a = (char) (y * mW + x); 133 char b = (char) (y * mW + x + 1); 134 char c = (char) ((y + 1) * mW + x); 135 char d = (char) ((y + 1) * mW + x + 1); 136 137 mIndexBuffer.put(i++, a); 138 mIndexBuffer.put(i++, c); 139 mIndexBuffer.put(i++, b); 140 141 mIndexBuffer.put(i++, b); 142 mIndexBuffer.put(i++, c); 143 mIndexBuffer.put(i++, d); 144 } 145 } 146 } 147 148 } 149 set(int i, int j, float x, float y, float z, float u, float v, float w0, float w1, int p0, int p1)150 public void set(int i, int j, float x, float y, float z, 151 float u, float v, 152 float w0, float w1, 153 int p0, int p1) { 154 if (i < 0 || i >= mW) { 155 throw new IllegalArgumentException("i"); 156 } 157 if (j < 0 || j >= mH) { 158 throw new IllegalArgumentException("j"); 159 } 160 161 if (w0 + w1 != 1.0f) { 162 throw new IllegalArgumentException("Weights must add up to 1.0f"); 163 } 164 165 int index = mW * j + i; 166 167 mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE); 168 mVertexBuffer.put(x); 169 mVertexBuffer.put(y); 170 mVertexBuffer.put(z); 171 mVertexBuffer.put(u); 172 mVertexBuffer.put(v); 173 mVertexBuffer.put(w0); 174 mVertexBuffer.put(w1); 175 176 mVertexByteBuffer.position(index * VERTEX_SIZE + VERTEX_PALETTE_INDEX_OFFSET); 177 mVertexByteBuffer.put((byte) p0); 178 mVertexByteBuffer.put((byte) p1); 179 } 180 createBufferObjects(GL gl)181 public void createBufferObjects(GL gl) { 182 // Generate a the vertex and element buffer IDs 183 int[] vboIds = new int[2]; 184 GL11 gl11 = (GL11) gl; 185 gl11.glGenBuffers(2, vboIds, 0); 186 mVertexBufferObjectId = vboIds[0]; 187 mElementBufferObjectId = vboIds[1]; 188 189 // Upload the vertex data 190 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); 191 mVertexByteBuffer.position(0); 192 gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW); 193 194 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); 195 mIndexBuffer.position(0); 196 gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW); 197 198 // We don't need the in-memory data any more 199 mVertexBuffer = null; 200 mVertexByteBuffer = null; 201 mIndexBuffer = null; 202 } 203 draw(GL10 gl)204 public void draw(GL10 gl) { 205 GL11 gl11 = (GL11) gl; 206 GL11Ext gl11Ext = (GL11Ext) gl; 207 208 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 209 210 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); 211 gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0); 212 gl11.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_TEXTURE_BUFFER_INDEX_OFFSET * FLOAT_SIZE); 213 214 gl.glEnableClientState(GL11Ext.GL_MATRIX_INDEX_ARRAY_OES); 215 gl.glEnableClientState(GL11Ext.GL_WEIGHT_ARRAY_OES); 216 217 gl11Ext.glWeightPointerOES(2, GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_WEIGHT_BUFFER_INDEX_OFFSET * FLOAT_SIZE); 218 gl11Ext.glMatrixIndexPointerOES(2, GL10.GL_UNSIGNED_BYTE, VERTEX_SIZE, VERTEX_PALETTE_INDEX_OFFSET ); 219 220 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); 221 gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0); 222 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 223 gl.glDisableClientState(GL11Ext.GL_MATRIX_INDEX_ARRAY_OES); 224 gl.glDisableClientState(GL11Ext.GL_WEIGHT_ARRAY_OES); 225 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); 226 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0); 227 } 228 } 229 MatrixPaletteRenderer(Context context)230 public MatrixPaletteRenderer(Context context) { 231 mContext = context; 232 } 233 onSurfaceCreated(GL10 gl, EGLConfig config)234 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 235 /* 236 * By default, OpenGL enables features that improve quality 237 * but reduce performance. One might want to tweak that 238 * especially on software renderer. 239 */ 240 gl.glDisable(GL10.GL_DITHER); 241 242 /* 243 * Some one-time OpenGL initialization can be made here 244 * probably based on features of this particular context 245 */ 246 gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, 247 GL10.GL_FASTEST); 248 249 gl.glClearColor(.5f, .5f, .5f, 1); 250 gl.glShadeModel(GL10.GL_SMOOTH); 251 gl.glEnable(GL10.GL_DEPTH_TEST); 252 gl.glEnable(GL10.GL_TEXTURE_2D); 253 254 /* 255 * Create our texture. This has to be done each time the 256 * surface is created. 257 */ 258 259 int[] textures = new int[1]; 260 gl.glGenTextures(1, textures, 0); 261 262 mTextureID = textures[0]; 263 gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); 264 265 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, 266 GL10.GL_NEAREST); 267 gl.glTexParameterf(GL10.GL_TEXTURE_2D, 268 GL10.GL_TEXTURE_MAG_FILTER, 269 GL10.GL_LINEAR); 270 271 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, 272 GL10.GL_CLAMP_TO_EDGE); 273 gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, 274 GL10.GL_CLAMP_TO_EDGE); 275 276 gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, 277 GL10.GL_REPLACE); 278 279 InputStream is = mContext.getResources() 280 .openRawResource(R.raw.robot); 281 Bitmap bitmap; 282 try { 283 bitmap = BitmapFactory.decodeStream(is); 284 } finally { 285 try { 286 is.close(); 287 } catch(IOException e) { 288 // Ignore. 289 } 290 } 291 292 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); 293 bitmap.recycle(); 294 295 mGrid = generateWeightedGrid(gl); 296 } 297 onDrawFrame(GL10 gl)298 public void onDrawFrame(GL10 gl) { 299 /* 300 * By default, OpenGL enables features that improve quality 301 * but reduce performance. One might want to tweak that 302 * especially on software renderer. 303 */ 304 gl.glDisable(GL10.GL_DITHER); 305 306 gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, 307 GL10.GL_MODULATE); 308 309 /* 310 * Usually, the first thing one might want to do is to clear 311 * the screen. The most efficient way of doing this is to use 312 * glClear(). 313 */ 314 315 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 316 317 gl.glEnable(GL10.GL_DEPTH_TEST); 318 319 gl.glEnable(GL10.GL_CULL_FACE); 320 321 /* 322 * Now we're ready to draw some 3D objects 323 */ 324 325 gl.glMatrixMode(GL10.GL_MODELVIEW); 326 gl.glLoadIdentity(); 327 328 GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 329 330 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 331 gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 332 333 gl.glActiveTexture(GL10.GL_TEXTURE0); 334 gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); 335 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, 336 GL10.GL_REPEAT); 337 gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, 338 GL10.GL_REPEAT); 339 340 long time = SystemClock.uptimeMillis() % 4000L; 341 342 // Rock back and forth 343 double animationUnit = ((double) time) / 4000; 344 float unitAngle = (float) Math.cos(animationUnit * 2 * Math.PI); 345 float angle = unitAngle * 135f; 346 347 gl.glEnable(GL11Ext.GL_MATRIX_PALETTE_OES); 348 gl.glMatrixMode(GL11Ext.GL_MATRIX_PALETTE_OES); 349 350 GL11Ext gl11Ext = (GL11Ext) gl; 351 352 // matrix 0: no transformation 353 gl11Ext.glCurrentPaletteMatrixOES(0); 354 gl11Ext.glLoadPaletteFromModelViewMatrixOES(); 355 356 357 // matrix 1: rotate by "angle" 358 gl.glRotatef(angle, 0, 0, 1.0f); 359 360 gl11Ext.glCurrentPaletteMatrixOES(1); 361 gl11Ext.glLoadPaletteFromModelViewMatrixOES(); 362 363 mGrid.draw(gl); 364 365 gl.glDisable(GL11Ext.GL_MATRIX_PALETTE_OES); 366 } 367 onSurfaceChanged(GL10 gl, int w, int h)368 public void onSurfaceChanged(GL10 gl, int w, int h) { 369 gl.glViewport(0, 0, w, h); 370 371 /* 372 * Set our projection matrix. This doesn't have to be done 373 * each time we draw, but usually a new projection needs to 374 * be set when the viewport is resized. 375 */ 376 377 float ratio = (float) w / h; 378 gl.glMatrixMode(GL10.GL_PROJECTION); 379 gl.glLoadIdentity(); 380 gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); 381 } 382 generateWeightedGrid(GL gl)383 private Grid generateWeightedGrid(GL gl) { 384 final int uSteps = 20; 385 final int vSteps = 20; 386 387 float radius = 0.25f; 388 float height = 2.0f; 389 Grid grid = new Grid(uSteps + 1, vSteps + 1); 390 391 for (int j = 0; j <= vSteps; j++) { 392 for (int i = 0; i <= uSteps; i++) { 393 double angle = Math.PI * 2 * i / uSteps; 394 float x = radius * (float) Math.cos(angle); 395 float y = height * ((float) j / vSteps - 0.5f); 396 float z = radius * (float) Math.sin(angle); 397 float u = -4.0f * (float) i / uSteps; 398 float v = -4.0f * (float) j / vSteps; 399 float w0 = (float) j / vSteps; 400 float w1 = 1.0f - w0; 401 grid.set(i, j, x, y, z, u, v, w0, w1, 0, 1); 402 } 403 } 404 405 grid.createBufferObjects(gl); 406 return grid; 407 } 408 } 409