1 /* 2 * Copyright (C) 2007 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 import javax.microedition.khronos.opengles.GL11ExtensionPack; 32 33 import android.app.Activity; 34 import android.graphics.Bitmap; 35 import android.graphics.BitmapFactory; 36 import android.opengl.GLSurfaceView; 37 import android.opengl.GLU; 38 import android.opengl.GLUtils; 39 import android.os.Bundle; 40 import android.util.Log; 41 42 import com.example.android.apis.R; 43 44 /** 45 * Demonstrate how to use the OES_texture_cube_map extension, available on some 46 * high-end OpenGL ES 1.x GPUs. 47 */ 48 public class CubeMapActivity extends Activity { 49 private GLSurfaceView mGLSurfaceView; 50 private class Renderer implements GLSurfaceView.Renderer { 51 private boolean mContextSupportsCubeMap; 52 private Grid mGrid; 53 private int mCubeMapTextureID; 54 private boolean mUseTexGen = false; 55 private float mAngle; 56 57 public void onDrawFrame(GL10 gl) { 58 checkGLError(gl); 59 if (mContextSupportsCubeMap) { 60 gl.glClearColor(0,0,1,0); 61 } else { 62 // Current context doesn't support cube maps. 63 // Indicate this by drawing a red background. 64 gl.glClearColor(1,0,0,0); 65 } 66 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 67 gl.glEnable(GL10.GL_DEPTH_TEST); 68 gl.glMatrixMode(GL10.GL_MODELVIEW); 69 gl.glLoadIdentity(); 70 71 GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 72 gl.glRotatef(mAngle, 0, 1, 0); 73 gl.glRotatef(mAngle*0.25f, 1, 0, 0); 74 75 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 76 77 checkGLError(gl); 78 79 if (mContextSupportsCubeMap) { 80 gl.glActiveTexture(GL10.GL_TEXTURE0); 81 checkGLError(gl); 82 gl.glEnable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP); 83 checkGLError(gl); 84 gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mCubeMapTextureID); 85 checkGLError(gl); 86 GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; 87 gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR, 88 GL11ExtensionPack.GL_TEXTURE_GEN_MODE, 89 GL11ExtensionPack.GL_REFLECTION_MAP); 90 checkGLError(gl); 91 gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR); 92 checkGLError(gl); 93 gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL); 94 } 95 96 checkGLError(gl); 97 mGrid.draw(gl); 98 99 if (mContextSupportsCubeMap) { 100 gl.glDisable(GL11ExtensionPack.GL_TEXTURE_GEN_STR); 101 } 102 checkGLError(gl); 103 104 mAngle += 1.2f; 105 } 106 107 public void onSurfaceChanged(GL10 gl, int width, int height) { 108 checkGLError(gl); 109 gl.glViewport(0, 0, width, height); 110 float ratio = (float) width / height; 111 gl.glMatrixMode(GL10.GL_PROJECTION); 112 gl.glLoadIdentity(); 113 gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); 114 checkGLError(gl); 115 } 116 117 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 118 checkGLError(gl); 119 // This test needs to be done each time a context is created, 120 // because different contexts may support different extensions. 121 mContextSupportsCubeMap = checkIfContextSupportsCubeMap(gl); 122 123 mGrid = generateTorusGrid(gl, 60, 60, 3.0f, 0.75f); 124 125 if (mContextSupportsCubeMap) { 126 int[] cubeMapResourceIds = new int[]{ 127 R.raw.skycubemap0, R.raw.skycubemap1, R.raw.skycubemap2, 128 R.raw.skycubemap3, R.raw.skycubemap4, R.raw.skycubemap5}; 129 mCubeMapTextureID = generateCubeMap(gl, cubeMapResourceIds); 130 } 131 checkGLError(gl); 132 } 133 134 private int generateCubeMap(GL10 gl, int[] resourceIds) { 135 checkGLError(gl); 136 int[] ids = new int[1]; 137 gl.glGenTextures(1, ids, 0); 138 int cubeMapTextureId = ids[0]; 139 gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, cubeMapTextureId); 140 gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, 141 GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 142 gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, 143 GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 144 145 for (int face = 0; face < 6; face++) { 146 InputStream is = getResources().openRawResource(resourceIds[face]); 147 Bitmap bitmap; 148 try { 149 bitmap = BitmapFactory.decodeStream(is); 150 } finally { 151 try { 152 is.close(); 153 } catch(IOException e) { 154 Log.e("CubeMap", "Could not decode texture for face " + Integer.toString(face)); 155 } 156 } 157 GLUtils.texImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 158 bitmap, 0); 159 bitmap.recycle(); 160 } 161 checkGLError(gl); 162 return cubeMapTextureId; 163 } 164 165 private Grid generateTorusGrid(GL gl, int uSteps, int vSteps, float majorRadius, float minorRadius) { 166 Grid grid = new Grid(uSteps + 1, vSteps + 1); 167 for (int j = 0; j <= vSteps; j++) { 168 double angleV = Math.PI * 2 * j / vSteps; 169 float cosV = (float) Math.cos(angleV); 170 float sinV = (float) Math.sin(angleV); 171 for (int i = 0; i <= uSteps; i++) { 172 double angleU = Math.PI * 2 * i / uSteps; 173 float cosU = (float) Math.cos(angleU); 174 float sinU = (float) Math.sin(angleU); 175 float d = majorRadius+minorRadius*cosU; 176 float x = d*cosV; 177 float y = d*(-sinV); 178 float z = minorRadius * sinU; 179 180 float nx = cosV * cosU; 181 float ny = -sinV * cosU; 182 float nz = sinU; 183 184 float length = (float) Math.sqrt(nx*nx + ny*ny + nz*nz); 185 nx /= length; 186 ny /= length; 187 nz /= length; 188 189 grid.set(i, j, x, y, z, nx, ny, nz); 190 } 191 } 192 grid.createBufferObjects(gl); 193 return grid; 194 } 195 196 private boolean checkIfContextSupportsCubeMap(GL10 gl) { 197 return checkIfContextSupportsExtension(gl, "GL_OES_texture_cube_map"); 198 } 199 200 /** 201 * This is not the fastest way to check for an extension, but fine if 202 * we are only checking for a few extensions each time a context is created. 203 * @param gl 204 * @param extension 205 * @return true if the extension is present in the current context. 206 */ 207 private boolean checkIfContextSupportsExtension(GL10 gl, String extension) { 208 String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " "; 209 // The extensions string is padded with spaces between extensions, but not 210 // necessarily at the beginning or end. For simplicity, add spaces at the 211 // beginning and end of the extensions string and the extension string. 212 // This means we can avoid special-case checks for the first or last 213 // extension, as well as avoid special-case checks when an extension name 214 // is the same as the first part of another extension name. 215 return extensions.indexOf(" " + extension + " ") >= 0; 216 } 217 } 218 219 /** A grid is a topologically rectangular array of vertices. 220 * 221 * This grid class is customized for the vertex data required for this 222 * example. 223 * 224 * The vertex and index data are held in VBO objects because on most 225 * GPUs VBO objects are the fastest way of rendering static vertex 226 * and index data. 227 * 228 */ 229 230 private static class Grid { 231 // Size of vertex data elements in bytes: 232 final static int FLOAT_SIZE = 4; 233 final static int CHAR_SIZE = 2; 234 235 // Vertex structure: 236 // float x, y, z; 237 // float nx, ny, nx; 238 239 final static int VERTEX_SIZE = 6 * FLOAT_SIZE; 240 final static int VERTEX_NORMAL_BUFFER_INDEX_OFFSET = 3; 241 242 private int mVertexBufferObjectId; 243 private int mElementBufferObjectId; 244 245 // These buffers are used to hold the vertex and index data while 246 // constructing the grid. Once createBufferObjects() is called 247 // the buffers are nulled out to save memory. 248 249 private ByteBuffer mVertexByteBuffer; 250 private FloatBuffer mVertexBuffer; 251 private CharBuffer mIndexBuffer; 252 253 private int mW; 254 private int mH; 255 private int mIndexCount; 256 257 public Grid(int w, int h) { 258 if (w < 0 || w >= 65536) { 259 throw new IllegalArgumentException("w"); 260 } 261 if (h < 0 || h >= 65536) { 262 throw new IllegalArgumentException("h"); 263 } 264 if (w * h >= 65536) { 265 throw new IllegalArgumentException("w * h >= 65536"); 266 } 267 268 mW = w; 269 mH = h; 270 int size = w * h; 271 272 mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size) 273 .order(ByteOrder.nativeOrder()); 274 mVertexBuffer = mVertexByteBuffer.asFloatBuffer(); 275 276 int quadW = mW - 1; 277 int quadH = mH - 1; 278 int quadCount = quadW * quadH; 279 int indexCount = quadCount * 6; 280 mIndexCount = indexCount; 281 mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount) 282 .order(ByteOrder.nativeOrder()).asCharBuffer(); 283 284 /* 285 * Initialize triangle list mesh. 286 * 287 * [0]-----[ 1] ... 288 * | / | 289 * | / | 290 * | / | 291 * [w]-----[w+1] ... 292 * | | 293 * 294 */ 295 296 { 297 int i = 0; 298 for (int y = 0; y < quadH; y++) { 299 for (int x = 0; x < quadW; x++) { 300 char a = (char) (y * mW + x); 301 char b = (char) (y * mW + x + 1); 302 char c = (char) ((y + 1) * mW + x); 303 char d = (char) ((y + 1) * mW + x + 1); 304 305 mIndexBuffer.put(i++, a); 306 mIndexBuffer.put(i++, c); 307 mIndexBuffer.put(i++, b); 308 309 mIndexBuffer.put(i++, b); 310 mIndexBuffer.put(i++, c); 311 mIndexBuffer.put(i++, d); 312 } 313 } 314 } 315 } 316 317 public void set(int i, int j, float x, float y, float z, float nx, float ny, float nz) { 318 if (i < 0 || i >= mW) { 319 throw new IllegalArgumentException("i"); 320 } 321 if (j < 0 || j >= mH) { 322 throw new IllegalArgumentException("j"); 323 } 324 325 int index = mW * j + i; 326 327 mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE); 328 mVertexBuffer.put(x); 329 mVertexBuffer.put(y); 330 mVertexBuffer.put(z); 331 mVertexBuffer.put(nx); 332 mVertexBuffer.put(ny); 333 mVertexBuffer.put(nz); 334 } 335 336 public void createBufferObjects(GL gl) { 337 checkGLError(gl); 338 // Generate a the vertex and element buffer IDs 339 int[] vboIds = new int[2]; 340 GL11 gl11 = (GL11) gl; 341 gl11.glGenBuffers(2, vboIds, 0); 342 mVertexBufferObjectId = vboIds[0]; 343 mElementBufferObjectId = vboIds[1]; 344 345 // Upload the vertex data 346 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); 347 mVertexByteBuffer.position(0); 348 gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW); 349 350 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); 351 mIndexBuffer.position(0); 352 gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW); 353 354 // We don't need the in-memory data any more 355 mVertexBuffer = null; 356 mVertexByteBuffer = null; 357 mIndexBuffer = null; 358 checkGLError(gl); 359 } 360 361 public void draw(GL10 gl) { 362 checkGLError(gl); 363 GL11 gl11 = (GL11) gl; 364 365 gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); 366 367 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); 368 gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0); 369 370 gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); 371 gl11.glNormalPointer(GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_NORMAL_BUFFER_INDEX_OFFSET * FLOAT_SIZE); 372 373 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); 374 gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0); 375 gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); 376 gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); 377 gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); 378 gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0); 379 checkGLError(gl); 380 } 381 } 382 383 static void checkGLError(GL gl) { 384 int error = ((GL10) gl).glGetError(); 385 if (error != GL10.GL_NO_ERROR) { 386 throw new RuntimeException("GLError 0x" + Integer.toHexString(error)); 387 } 388 } 389 390 @Override 391 protected void onCreate(Bundle savedInstanceState) { 392 super.onCreate(savedInstanceState); 393 394 // Create our surface view and set it as the content of our 395 // Activity 396 mGLSurfaceView = new GLSurfaceView(this); 397 mGLSurfaceView.setRenderer(new Renderer()); 398 setContentView(mGLSurfaceView); 399 } 400 401 @Override 402 protected void onResume() { 403 // Ideally a game should implement onResume() and onPause() 404 // to take appropriate action when the activity looses focus 405 super.onResume(); 406 mGLSurfaceView.onResume(); 407 } 408 409 @Override 410 protected void onPause() { 411 // Ideally a game should implement onResume() and onPause() 412 // to take appropriate action when the activity looses focus 413 super.onPause(); 414 mGLSurfaceView.onPause(); 415 } 416 } 417