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 onDrawFrame(GL10 gl)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 onSurfaceChanged(GL10 gl, int width, int height)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 onSurfaceCreated(GL10 gl, EGLConfig config)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 generateCubeMap(GL10 gl, int[] resourceIds)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 generateTorusGrid(GL gl, int uSteps, int vSteps, float majorRadius, float minorRadius)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 checkIfContextSupportsCubeMap(GL10 gl)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 */ checkIfContextSupportsExtension(GL10 gl, String extension)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 Grid(int w, int h)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 set(int i, int j, float x, float y, float z, float nx, float ny, float nz)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 createBufferObjects(GL gl)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 draw(GL10 gl)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 checkGLError(GL gl)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 onCreate(Bundle savedInstanceState)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 onResume()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 onPause()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