Mega Code Archive

 
Categories / Android / 2D Graphics
 

OpenGL objects

/*  * Copyright (C) 2008 The Android Open Source Project  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *      http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ package com.example.android.apis.graphics.kube; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.Random; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.util.Log; import android.view.Window; public class Kube extends Activity implements KubeRenderer.AnimationCallback {   private GLWorld makeGLWorld() {     GLWorld world = new GLWorld();     int one = 0x10000;     int half = 0x08000;     GLColor red = new GLColor(one, 0, 0);     GLColor green = new GLColor(0, one, 0);     GLColor blue = new GLColor(0, 0, one);     GLColor yellow = new GLColor(one, one, 0);     GLColor orange = new GLColor(one, half, 0);     GLColor white = new GLColor(one, one, one);     GLColor black = new GLColor(0, 0, 0);     // coordinates for our cubes     float c0 = -1.0f;     float c1 = -0.38f;     float c2 = -0.32f;     float c3 = 0.32f;     float c4 = 0.38f;     float c5 = 1.0f;     // top back, left to right     mCubes[0] = new Cube(world, c0, c4, c0, c1, c5, c1);     mCubes[1] = new Cube(world, c2, c4, c0, c3, c5, c1);     mCubes[2] = new Cube(world, c4, c4, c0, c5, c5, c1);     // top middle, left to right     mCubes[3] = new Cube(world, c0, c4, c2, c1, c5, c3);     mCubes[4] = new Cube(world, c2, c4, c2, c3, c5, c3);     mCubes[5] = new Cube(world, c4, c4, c2, c5, c5, c3);     // top front, left to right     mCubes[6] = new Cube(world, c0, c4, c4, c1, c5, c5);     mCubes[7] = new Cube(world, c2, c4, c4, c3, c5, c5);     mCubes[8] = new Cube(world, c4, c4, c4, c5, c5, c5);     // middle back, left to right     mCubes[9] = new Cube(world, c0, c2, c0, c1, c3, c1);     mCubes[10] = new Cube(world, c2, c2, c0, c3, c3, c1);     mCubes[11] = new Cube(world, c4, c2, c0, c5, c3, c1);     // middle middle, left to right     mCubes[12] = new Cube(world, c0, c2, c2, c1, c3, c3);     mCubes[13] = null;     mCubes[14] = new Cube(world, c4, c2, c2, c5, c3, c3);     // middle front, left to right     mCubes[15] = new Cube(world, c0, c2, c4, c1, c3, c5);     mCubes[16] = new Cube(world, c2, c2, c4, c3, c3, c5);     mCubes[17] = new Cube(world, c4, c2, c4, c5, c3, c5);     // bottom back, left to right     mCubes[18] = new Cube(world, c0, c0, c0, c1, c1, c1);     mCubes[19] = new Cube(world, c2, c0, c0, c3, c1, c1);     mCubes[20] = new Cube(world, c4, c0, c0, c5, c1, c1);     // bottom middle, left to right     mCubes[21] = new Cube(world, c0, c0, c2, c1, c1, c3);     mCubes[22] = new Cube(world, c2, c0, c2, c3, c1, c3);     mCubes[23] = new Cube(world, c4, c0, c2, c5, c1, c3);     // bottom front, left to right     mCubes[24] = new Cube(world, c0, c0, c4, c1, c1, c5);     mCubes[25] = new Cube(world, c2, c0, c4, c3, c1, c5);     mCubes[26] = new Cube(world, c4, c0, c4, c5, c1, c5);     // paint the sides     int i, j;     // set all faces black by default     for (i = 0; i < 27; i++) {       Cube cube = mCubes[i];       if (cube != null) {         for (j = 0; j < 6; j++)           cube.setFaceColor(j, black);       }     }     // paint top     for (i = 0; i < 9; i++)       mCubes[i].setFaceColor(Cube.kTop, orange);     // paint bottom     for (i = 18; i < 27; i++)       mCubes[i].setFaceColor(Cube.kBottom, red);     // paint left     for (i = 0; i < 27; i += 3)       mCubes[i].setFaceColor(Cube.kLeft, yellow);     // paint right     for (i = 2; i < 27; i += 3)       mCubes[i].setFaceColor(Cube.kRight, white);     // paint back     for (i = 0; i < 27; i += 9)       for (j = 0; j < 3; j++)         mCubes[i + j].setFaceColor(Cube.kBack, blue);     // paint front     for (i = 6; i < 27; i += 9)       for (j = 0; j < 3; j++)         mCubes[i + j].setFaceColor(Cube.kFront, green);     for (i = 0; i < 27; i++)       if (mCubes[i] != null)         world.addShape(mCubes[i]);     // initialize our permutation to solved position     mPermutation = new int[27];     for (i = 0; i < mPermutation.length; i++)       mPermutation[i] = i;     createLayers();     updateLayers();     world.generate();     return world;   }   private void createLayers() {     mLayers[kUp] = new Layer(Layer.kAxisY);     mLayers[kDown] = new Layer(Layer.kAxisY);     mLayers[kLeft] = new Layer(Layer.kAxisX);     mLayers[kRight] = new Layer(Layer.kAxisX);     mLayers[kFront] = new Layer(Layer.kAxisZ);     mLayers[kBack] = new Layer(Layer.kAxisZ);     mLayers[kMiddle] = new Layer(Layer.kAxisX);     mLayers[kEquator] = new Layer(Layer.kAxisY);     mLayers[kSide] = new Layer(Layer.kAxisZ);   }   private void updateLayers() {     Layer layer;     GLShape[] shapes;     int i, j, k;     // up layer     layer = mLayers[kUp];     shapes = layer.mShapes;     for (i = 0; i < 9; i++)       shapes[i] = mCubes[mPermutation[i]];     // down layer     layer = mLayers[kDown];     shapes = layer.mShapes;     for (i = 18, k = 0; i < 27; i++)       shapes[k++] = mCubes[mPermutation[i]];     // left layer     layer = mLayers[kLeft];     shapes = layer.mShapes;     for (i = 0, k = 0; i < 27; i += 9)       for (j = 0; j < 9; j += 3)         shapes[k++] = mCubes[mPermutation[i + j]];     // right layer     layer = mLayers[kRight];     shapes = layer.mShapes;     for (i = 2, k = 0; i < 27; i += 9)       for (j = 0; j < 9; j += 3)         shapes[k++] = mCubes[mPermutation[i + j]];     // front layer     layer = mLayers[kFront];     shapes = layer.mShapes;     for (i = 6, k = 0; i < 27; i += 9)       for (j = 0; j < 3; j++)         shapes[k++] = mCubes[mPermutation[i + j]];     // back layer     layer = mLayers[kBack];     shapes = layer.mShapes;     for (i = 0, k = 0; i < 27; i += 9)       for (j = 0; j < 3; j++)         shapes[k++] = mCubes[mPermutation[i + j]];     // middle layer     layer = mLayers[kMiddle];     shapes = layer.mShapes;     for (i = 1, k = 0; i < 27; i += 9)       for (j = 0; j < 9; j += 3)         shapes[k++] = mCubes[mPermutation[i + j]];     // equator layer     layer = mLayers[kEquator];     shapes = layer.mShapes;     for (i = 9, k = 0; i < 18; i++)       shapes[k++] = mCubes[mPermutation[i]];     // side layer     layer = mLayers[kSide];     shapes = layer.mShapes;     for (i = 3, k = 0; i < 27; i += 9)       for (j = 0; j < 3; j++)         shapes[k++] = mCubes[mPermutation[i + j]];   }   @Override   protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     // We don't need a title either.     requestWindowFeature(Window.FEATURE_NO_TITLE);     mView = new GLSurfaceView(getApplication());     mRenderer = new KubeRenderer(makeGLWorld(), this);     mView.setRenderer(mRenderer);     setContentView(mView);   }   @Override   protected void onResume() {     super.onResume();     mView.onResume();   }   @Override   protected void onPause() {     super.onPause();     mView.onPause();   }   public void animate() {     // change our angle of view     mRenderer.setAngle(mRenderer.getAngle() + 1.2f);     if (mCurrentLayer == null) {       int layerID = mRandom.nextInt(9);       mCurrentLayer = mLayers[layerID];       mCurrentLayerPermutation = mLayerPermutations[layerID];       mCurrentLayer.startAnimation();       boolean direction = mRandom.nextBoolean();       int count = mRandom.nextInt(3) + 1;       count = 1;       direction = false;       mCurrentAngle = 0;       if (direction) {         mAngleIncrement = (float) Math.PI / 50;         mEndAngle = mCurrentAngle + ((float) Math.PI * count) / 2f;       } else {         mAngleIncrement = -(float) Math.PI / 50;         mEndAngle = mCurrentAngle - ((float) Math.PI * count) / 2f;       }     }     mCurrentAngle += mAngleIncrement;     if ((mAngleIncrement > 0f && mCurrentAngle >= mEndAngle)         || (mAngleIncrement < 0f && mCurrentAngle <= mEndAngle)) {       mCurrentLayer.setAngle(mEndAngle);       mCurrentLayer.endAnimation();       mCurrentLayer = null;       // adjust mPermutation based on the completed layer rotation       int[] newPermutation = new int[27];       for (int i = 0; i < 27; i++) {         newPermutation[i] = mPermutation[mCurrentLayerPermutation[i]];         // newPermutation[i] =         // mCurrentLayerPermutation[mPermutation[i]];       }       mPermutation = newPermutation;       updateLayers();     } else {       mCurrentLayer.setAngle(mCurrentAngle);     }   }   GLSurfaceView mView;   KubeRenderer mRenderer;   Cube[] mCubes = new Cube[27];   // a Layer for each possible move   Layer[] mLayers = new Layer[9];   // permutations corresponding to a pi/2 rotation of each layer about its   // axis   static int[][] mLayerPermutations = {       // permutation for UP layer       { 2, 5, 8, 1, 4, 7, 0, 3, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,           19, 20, 21, 22, 23, 24, 25, 26 },       // permutation for DOWN layer       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 20,           23, 26, 19, 22, 25, 18, 21, 24 },       // permutation for LEFT layer       { 6, 1, 2, 15, 4, 5, 24, 7, 8, 3, 10, 11, 12, 13, 14, 21, 16, 17,           0, 19, 20, 9, 22, 23, 18, 25, 26 },       // permutation for RIGHT layer       { 0, 1, 8, 3, 4, 17, 6, 7, 26, 9, 10, 5, 12, 13, 14, 15, 16, 23,           18, 19, 2, 21, 22, 11, 24, 25, 20 },       // permutation for FRONT layer       { 0, 1, 2, 3, 4, 5, 24, 15, 6, 9, 10, 11, 12, 13, 14, 25, 16, 7,           18, 19, 20, 21, 22, 23, 26, 17, 8 },       // permutation for BACK layer       { 18, 9, 0, 3, 4, 5, 6, 7, 8, 19, 10, 1, 12, 13, 14, 15, 16, 17,           20, 11, 2, 21, 22, 23, 24, 25, 26 },       // permutation for MIDDLE layer       { 0, 7, 2, 3, 16, 5, 6, 25, 8, 9, 4, 11, 12, 13, 14, 15, 22, 17,           18, 1, 20, 21, 10, 23, 24, 19, 26 },       // permutation for EQUATOR layer       { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 14, 17, 10, 13, 16, 9, 12, 15, 18,           19, 20, 21, 22, 23, 24, 25, 26 },       // permutation for SIDE layer       { 0, 1, 2, 21, 12, 3, 6, 7, 8, 9, 10, 11, 22, 13, 4, 15, 16, 17,           18, 19, 20, 23, 14, 5, 24, 25, 26 } };   // current permutation of starting position   int[] mPermutation;   // for random cube movements   Random mRandom = new Random(System.currentTimeMillis());   // currently turning layer   Layer mCurrentLayer = null;   // current and final angle for current Layer animation   float mCurrentAngle, mEndAngle;   // amount to increment angle   float mAngleIncrement;   int[] mCurrentLayerPermutation;   // names for our 9 layers (based on notation from   // http://www.cubefreak.net/notation.html)   static final int kUp = 0;   static final int kDown = 1;   static final int kLeft = 2;   static final int kRight = 3;   static final int kFront = 4;   static final int kBack = 5;   static final int kMiddle = 6;   static final int kEquator = 7;   static final int kSide = 8; } class Cube extends GLShape {   public Cube(GLWorld world, float left, float bottom, float back,       float right, float top, float front) {     super(world);     GLVertex leftBottomBack = addVertex(left, bottom, back);     GLVertex rightBottomBack = addVertex(right, bottom, back);     GLVertex leftTopBack = addVertex(left, top, back);     GLVertex rightTopBack = addVertex(right, top, back);     GLVertex leftBottomFront = addVertex(left, bottom, front);     GLVertex rightBottomFront = addVertex(right, bottom, front);     GLVertex leftTopFront = addVertex(left, top, front);     GLVertex rightTopFront = addVertex(right, top, front);     // vertices are added in a clockwise orientation (when viewed from the     // outside)     // bottom     addFace(new GLFace(leftBottomBack, leftBottomFront, rightBottomFront,         rightBottomBack));     // front     addFace(new GLFace(leftBottomFront, leftTopFront, rightTopFront,         rightBottomFront));     // left     addFace(new GLFace(leftBottomBack, leftTopBack, leftTopFront,         leftBottomFront));     // right     addFace(new GLFace(rightBottomBack, rightBottomFront, rightTopFront,         rightTopBack));     // back     addFace(new GLFace(leftBottomBack, rightBottomBack, rightTopBack,         leftTopBack));     // top     addFace(new GLFace(leftTopBack, rightTopBack, rightTopFront,         leftTopFront));   }   public static final int kBottom = 0;   public static final int kFront = 1;   public static final int kLeft = 2;   public static final int kRight = 3;   public static final int kBack = 4;   public static final int kTop = 5; } class GLColor {   public final int red;   public final int green;   public final int blue;   public final int alpha;   public GLColor(int red, int green, int blue, int alpha) {     this.red = red;     this.green = green;     this.blue = blue;     this.alpha = alpha;   }   public GLColor(int red, int green, int blue) {     this.red = red;     this.green = green;     this.blue = blue;     this.alpha = 0x10000;   }   @Override   public boolean equals(Object other) {     if (other instanceof GLColor) {       GLColor color = (GLColor) other;       return (red == color.red && green == color.green           && blue == color.blue && alpha == color.alpha);     }     return false;   } } class GLFace {   public GLFace() {   }   // for triangles   public GLFace(GLVertex v1, GLVertex v2, GLVertex v3) {     addVertex(v1);     addVertex(v2);     addVertex(v3);   }   // for quadrilaterals   public GLFace(GLVertex v1, GLVertex v2, GLVertex v3, GLVertex v4) {     addVertex(v1);     addVertex(v2);     addVertex(v3);     addVertex(v4);   }   public void addVertex(GLVertex v) {     mVertexList.add(v);   }   // must be called after all vertices are added   public void setColor(GLColor c) {     int last = mVertexList.size() - 1;     if (last < 2) {       Log.e("GLFace", "not enough vertices in setColor()");     } else {       GLVertex vertex = mVertexList.get(last);       // only need to do this if the color has never been set       if (mColor == null) {         while (vertex.color != null) {           mVertexList.add(0, vertex);           mVertexList.remove(last + 1);           vertex = mVertexList.get(last);         }       }       vertex.color = c;     }     mColor = c;   }   public int getIndexCount() {     return (mVertexList.size() - 2) * 3;   }   public void putIndices(ShortBuffer buffer) {     int last = mVertexList.size() - 1;     GLVertex v0 = mVertexList.get(0);     GLVertex vn = mVertexList.get(last);     // push triangles into the buffer     for (int i = 1; i < last; i++) {       GLVertex v1 = mVertexList.get(i);       buffer.put(v0.index);       buffer.put(v1.index);       buffer.put(vn.index);       v0 = v1;     }   }   private ArrayList<GLVertex> mVertexList = new ArrayList<GLVertex>();   private GLColor mColor; } class GLShape {   public GLShape(GLWorld world) {     mWorld = world;   }   public void addFace(GLFace face) {     mFaceList.add(face);   }   public void setFaceColor(int face, GLColor color) {     mFaceList.get(face).setColor(color);   }   public void putIndices(ShortBuffer buffer) {     Iterator<GLFace> iter = mFaceList.iterator();     while (iter.hasNext()) {       GLFace face = iter.next();       face.putIndices(buffer);     }   }   public int getIndexCount() {     int count = 0;     Iterator<GLFace> iter = mFaceList.iterator();     while (iter.hasNext()) {       GLFace face = iter.next();       count += face.getIndexCount();     }     return count;   }   public GLVertex addVertex(float x, float y, float z) {     // look for an existing GLVertex first     Iterator<GLVertex> iter = mVertexList.iterator();     while (iter.hasNext()) {       GLVertex vertex = iter.next();       if (vertex.x == x && vertex.y == y && vertex.z == z) {         return vertex;       }     }     // doesn't exist, so create new vertex     GLVertex vertex = mWorld.addVertex(x, y, z);     mVertexList.add(vertex);     return vertex;   }   public void animateTransform(M4 transform) {     mAnimateTransform = transform;     if (mTransform != null)       transform = mTransform.multiply(transform);     Iterator<GLVertex> iter = mVertexList.iterator();     while (iter.hasNext()) {       GLVertex vertex = iter.next();       mWorld.transformVertex(vertex, transform);     }   }   public void startAnimation() {   }   public void endAnimation() {     if (mTransform == null) {       mTransform = new M4(mAnimateTransform);     } else {       mTransform = mTransform.multiply(mAnimateTransform);     }   }   public M4 mTransform;   public M4 mAnimateTransform;   protected ArrayList<GLFace> mFaceList = new ArrayList<GLFace>();   protected ArrayList<GLVertex> mVertexList = new ArrayList<GLVertex>();   protected ArrayList<Integer> mIndexList = new ArrayList<Integer>(); // make                                     // more                                     // efficient?   protected GLWorld mWorld; } class GLVertex {   public float x;   public float y;   public float z;   final short index; // index in vertex table   GLColor color;   GLVertex() {     this.x = 0;     this.y = 0;     this.z = 0;     this.index = -1;   }   GLVertex(float x, float y, float z, int index) {     this.x = x;     this.y = y;     this.z = z;     this.index = (short) index;   }   @Override   public boolean equals(Object other) {     if (other instanceof GLVertex) {       GLVertex v = (GLVertex) other;       return (x == v.x && y == v.y && z == v.z);     }     return false;   }   static public int toFixed(float x) {     return (int) (x * 65536.0f);   }   public void put(IntBuffer vertexBuffer, IntBuffer colorBuffer) {     vertexBuffer.put(toFixed(x));     vertexBuffer.put(toFixed(y));     vertexBuffer.put(toFixed(z));     if (color == null) {       colorBuffer.put(0);       colorBuffer.put(0);       colorBuffer.put(0);       colorBuffer.put(0);     } else {       colorBuffer.put(color.red);       colorBuffer.put(color.green);       colorBuffer.put(color.blue);       colorBuffer.put(color.alpha);     }   }   public void update(IntBuffer vertexBuffer, M4 transform) {     // skip to location of vertex in mVertex buffer     vertexBuffer.position(index * 3);     if (transform == null) {       vertexBuffer.put(toFixed(x));       vertexBuffer.put(toFixed(y));       vertexBuffer.put(toFixed(z));     } else {       GLVertex temp = new GLVertex();       transform.multiply(this, temp);       vertexBuffer.put(toFixed(temp.x));       vertexBuffer.put(toFixed(temp.y));       vertexBuffer.put(toFixed(temp.z));     }   } } class GLWorld {   public void addShape(GLShape shape) {     mShapeList.add(shape);     mIndexCount += shape.getIndexCount();   }   public void generate() {     ByteBuffer bb = ByteBuffer.allocateDirect(mVertexList.size() * 4 * 4);     bb.order(ByteOrder.nativeOrder());     mColorBuffer = bb.asIntBuffer();     bb = ByteBuffer.allocateDirect(mVertexList.size() * 4 * 3);     bb.order(ByteOrder.nativeOrder());     mVertexBuffer = bb.asIntBuffer();     bb = ByteBuffer.allocateDirect(mIndexCount * 2);     bb.order(ByteOrder.nativeOrder());     mIndexBuffer = bb.asShortBuffer();     Iterator<GLVertex> iter2 = mVertexList.iterator();     while (iter2.hasNext()) {       GLVertex vertex = iter2.next();       vertex.put(mVertexBuffer, mColorBuffer);     }     Iterator<GLShape> iter3 = mShapeList.iterator();     while (iter3.hasNext()) {       GLShape shape = iter3.next();       shape.putIndices(mIndexBuffer);     }   }   public GLVertex addVertex(float x, float y, float z) {     GLVertex vertex = new GLVertex(x, y, z, mVertexList.size());     mVertexList.add(vertex);     return vertex;   }   public void transformVertex(GLVertex vertex, M4 transform) {     vertex.update(mVertexBuffer, transform);   }   int count = 0;   public void draw(GL10 gl) {     mColorBuffer.position(0);     mVertexBuffer.position(0);     mIndexBuffer.position(0);     gl.glFrontFace(GL10.GL_CW);     gl.glShadeModel(GL10.GL_FLAT);     gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);     gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);     gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount,         GL10.GL_UNSIGNED_SHORT, mIndexBuffer);     count++;   }   static public float toFloat(int x) {     return x / 65536.0f;   }   private ArrayList<GLShape> mShapeList = new ArrayList<GLShape>();   private ArrayList<GLVertex> mVertexList = new ArrayList<GLVertex>();   private int mIndexCount = 0;   private IntBuffer mVertexBuffer;   private IntBuffer mColorBuffer;   private ShortBuffer mIndexBuffer; } /**  * Example of how to use OpenGL|ES in a custom view  *   */ class KubeRenderer implements GLSurfaceView.Renderer {   public interface AnimationCallback {     void animate();   }   public KubeRenderer(GLWorld world, AnimationCallback callback) {     mWorld = world;     mCallback = callback;   }   public void onDrawFrame(GL10 gl) {     if (mCallback != null) {       mCallback.animate();     }     /*      * Usually, the first thing one might want to do is to clear the screen.      * The most efficient way of doing this is to use glClear(). However we      * must make sure to set the scissor correctly first. The scissor is      * always specified in window coordinates:      */     gl.glClearColor(0.5f, 0.5f, 0.5f, 1);     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);     /*      * Now we're ready to draw some 3D object      */     gl.glMatrixMode(GL10.GL_MODELVIEW);     gl.glLoadIdentity();     gl.glTranslatef(0, 0, -3.0f);     gl.glScalef(0.5f, 0.5f, 0.5f);     gl.glRotatef(mAngle, 0, 1, 0);     gl.glRotatef(mAngle * 0.25f, 1, 0, 0);     gl.glColor4f(0.7f, 0.7f, 0.7f, 1.0f);     gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);     gl.glEnableClientState(GL10.GL_COLOR_ARRAY);     gl.glEnable(GL10.GL_CULL_FACE);     gl.glShadeModel(GL10.GL_SMOOTH);     gl.glEnable(GL10.GL_DEPTH_TEST);     mWorld.draw(gl);   }   public void onSurfaceChanged(GL10 gl, int width, int height) {     gl.glViewport(0, 0, width, height);     /*      * Set our projection matrix. This doesn't have to be done each time we      * draw, but usually a new projection needs to be set when the viewport      * is resized.      */     float ratio = (float) width / height;     gl.glMatrixMode(GL10.GL_PROJECTION);     gl.glLoadIdentity();     gl.glFrustumf(-ratio, ratio, -1, 1, 2, 12);     /*      * By default, OpenGL enables features that improve quality but reduce      * performance. One might want to tweak that especially on software      * renderer.      */     gl.glDisable(GL10.GL_DITHER);     gl.glActiveTexture(GL10.GL_TEXTURE0);   }   public void onSurfaceCreated(GL10 gl, EGLConfig config) {     // Nothing special, don't have any textures we need to recreate.   }   public void setAngle(float angle) {     mAngle = angle;   }   public float getAngle() {     return mAngle;   }   private GLWorld mWorld;   private AnimationCallback mCallback;   private float mAngle; } class Layer {   public Layer(int axis) {     // start with identity matrix for transformation     mAxis = axis;     mTransform.setIdentity();   }   public void startAnimation() {     for (int i = 0; i < mShapes.length; i++) {       GLShape shape = mShapes[i];       if (shape != null) {         shape.startAnimation();       }     }   }   public void endAnimation() {     for (int i = 0; i < mShapes.length; i++) {       GLShape shape = mShapes[i];       if (shape != null) {         shape.endAnimation();       }     }   }   public void setAngle(float angle) {     // normalize the angle     float twopi = (float) Math.PI * 2f;     while (angle >= twopi)       angle -= twopi;     while (angle < 0f)       angle += twopi;     // mAngle = angle;     float sin = (float) Math.sin(angle);     float cos = (float) Math.cos(angle);     float[][] m = mTransform.m;     switch (mAxis) {     case kAxisX:       m[1][1] = cos;       m[1][2] = sin;       m[2][1] = -sin;       m[2][2] = cos;       m[0][0] = 1f;       m[0][1] = m[0][2] = m[1][0] = m[2][0] = 0f;       break;     case kAxisY:       m[0][0] = cos;       m[0][2] = sin;       m[2][0] = -sin;       m[2][2] = cos;       m[1][1] = 1f;       m[0][1] = m[1][0] = m[1][2] = m[2][1] = 0f;       break;     case kAxisZ:       m[0][0] = cos;       m[0][1] = sin;       m[1][0] = -sin;       m[1][1] = cos;       m[2][2] = 1f;       m[2][0] = m[2][1] = m[0][2] = m[1][2] = 0f;       break;     }     for (int i = 0; i < mShapes.length; i++) {       GLShape shape = mShapes[i];       if (shape != null) {         shape.animateTransform(mTransform);       }     }   }   GLShape[] mShapes = new GLShape[9];   M4 mTransform = new M4();   // float mAngle;   // which axis do we rotate around?   // 0 for X, 1 for Y, 2 for Z   int mAxis;   static public final int kAxisX = 0;   static public final int kAxisY = 1;   static public final int kAxisZ = 2; } class M4 {   public float[][] m = new float[4][4];   public M4() {   }   public M4(M4 other) {     for (int i = 0; i < 4; i++) {       for (int j = 0; j < 4; j++) {         m[i][j] = other.m[i][j];       }     }   }   public void multiply(GLVertex src, GLVertex dest) {     dest.x = src.x * m[0][0] + src.y * m[1][0] + src.z * m[2][0] + m[3][0];     dest.y = src.x * m[0][1] + src.y * m[1][1] + src.z * m[2][1] + m[3][1];     dest.z = src.x * m[0][2] + src.y * m[1][2] + src.z * m[2][2] + m[3][2];   }   public M4 multiply(M4 other) {     M4 result = new M4();     float[][] m1 = m;     float[][] m2 = other.m;     for (int i = 0; i < 4; i++) {       for (int j = 0; j < 4; j++) {         result.m[i][j] = m1[i][0] * m2[0][j] + m1[i][1] * m2[1][j]             + m1[i][2] * m2[2][j] + m1[i][3] * m2[3][j];       }     }     return result;   }   public void setIdentity() {     for (int i = 0; i < 4; i++) {       for (int j = 0; j < 4; j++) {         m[i][j] = (i == j ? 1f : 0f);       }     }   }   @Override   public String toString() {     StringBuilder builder = new StringBuilder("[ ");     for (int i = 0; i < 4; i++) {       for (int j = 0; j < 4; j++) {         builder.append(m[i][j]);         builder.append(" ");       }       if (i < 2)         builder.append("\n  ");     }     builder.append(" ]");     return builder.toString();   } }