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