/*
* 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 mVertexList = new ArrayList();
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 iter = mFaceList.iterator();
while (iter.hasNext()) {
GLFace face = iter.next();
face.putIndices(buffer);
}
}
public int getIndexCount() {
int count = 0;
Iterator 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 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 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 mFaceList = new ArrayList();
protected ArrayList mVertexList = new ArrayList();
protected ArrayList mIndexList = new ArrayList(); // 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 iter2 = mVertexList.iterator();
while (iter2.hasNext()) {
GLVertex vertex = iter2.next();
vertex.put(mVertexBuffer, mColorBuffer);
}
Iterator 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 mShapeList = new ArrayList();
private ArrayList mVertexList = new ArrayList();
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();
}
}