OpenGL objects : OpenGL « 2D Graphics « Android






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();
  }
}

   
    
    
  








Related examples in the same category

1.Wrapper activity demonstrating the use of GLSurfaceView, a view that uses OpenGL drawing into a dedicated surface.
2.Check for OpenGL ES 2.0 support at runtime, and then use either OpenGL ES 1.0 or OpenGL ES 2.0, as appropriate.
3.A GLSurfaceView.Renderer that uses the Android-specific android.opengl.GLESXXX static OpenGL ES APIs.
4.Wrapper activity demonstrating the use of {@link GLSurfaceView}, a view that uses OpenGL drawing into a dedicated surface.
5.Demonstrate how to use the OES_texture_cube_map extension, available on some high-end OpenGL ES 1.x GPUs.
6.OpenGL Sprite Text Activity
7.An OpenGL ES renderer based on the GLSurfaceView rendering framework.
8.OpenGL Utils
9.Graphics API supports both OpenGL and Android 2D rendering targets efficiently through the same interface
10.OpenGL ES version of a sprite
11.OpenGL Utils 2
12.Demonstrate how to use ETC1 format compressed textures.