OpenGL Sprite Text Activity : OpenGL « 2D Graphics « Android






OpenGL Sprite Text Activity

   
/*
 * 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.spritetext;

import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL10Ext;
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.opengl.GLUtils;
import android.opengl.Matrix;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;

/**
 * An OpenGL text label maker.
 * 
 * 
 * OpenGL labels are implemented by creating a Bitmap, drawing all the labels
 * into the Bitmap, converting the Bitmap into an Alpha texture, and drawing
 * portions of the texture using glDrawTexiOES.
 * 
 * The benefits of this approach are that the labels are drawn using the high
 * quality anti-aliased font rasterizer, full character set support, and all the
 * text labels are stored on a single texture, which makes it faster to use.
 * 
 * The drawbacks are that you can only have as many labels as will fit onto one
 * texture, and you have to recreate the whole texture if any label text
 * changes.
 * 
 */
class LabelMaker {
  /**
   * Create a label maker or maximum compatibility with various OpenGL ES
   * implementations, the strike width and height must be powers of two, We
   * want the strike width to be at least as wide as the widest window.
   * 
   * @param fullColor
   *            true if we want a full color backing store (4444), otherwise
   *            we generate a grey L8 backing store.
   * @param strikeWidth
   *            width of strike
   * @param strikeHeight
   *            height of strike
   */
  public LabelMaker(boolean fullColor, int strikeWidth, int strikeHeight) {
    mFullColor = fullColor;
    mStrikeWidth = strikeWidth;
    mStrikeHeight = strikeHeight;
    mTexelWidth = (float) (1.0 / mStrikeWidth);
    mTexelHeight = (float) (1.0 / mStrikeHeight);
    mClearPaint = new Paint();
    mClearPaint.setARGB(0, 0, 0, 0);
    mClearPaint.setStyle(Style.FILL);
    mState = STATE_NEW;
  }

  /**
   * Call to initialize the class. Call whenever the surface has been created.
   * 
   * @param gl
   */
  public void initialize(GL10 gl) {
    mState = STATE_INITIALIZED;
    int[] textures = new int[1];
    gl.glGenTextures(1, textures, 0);
    mTextureID = textures[0];
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);

    // Use Nearest for performance.
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
        GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
        GL10.GL_NEAREST);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
        GL10.GL_CLAMP_TO_EDGE);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
        GL10.GL_CLAMP_TO_EDGE);

    gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
        GL10.GL_REPLACE);
  }

  /**
   * Call when the surface has been destroyed
   */
  public void shutdown(GL10 gl) {
    if (gl != null) {
      if (mState > STATE_NEW) {
        int[] textures = new int[1];
        textures[0] = mTextureID;
        gl.glDeleteTextures(1, textures, 0);
        mState = STATE_NEW;
      }
    }
  }

  /**
   * Call before adding labels. Clears out any existing labels.
   * 
   * @param gl
   */
  public void beginAdding(GL10 gl) {
    checkState(STATE_INITIALIZED, STATE_ADDING);
    mLabels.clear();
    mU = 0;
    mV = 0;
    mLineHeight = 0;
    Bitmap.Config config = mFullColor ? Bitmap.Config.ARGB_4444
        : Bitmap.Config.ALPHA_8;
    mBitmap = Bitmap.createBitmap(mStrikeWidth, mStrikeHeight, config);
    mCanvas = new Canvas(mBitmap);
    mBitmap.eraseColor(0);
  }

  /**
   * Call to add a label
   * 
   * @param gl
   * @param text
   *            the text of the label
   * @param textPaint
   *            the paint of the label
   * @return the id of the label, used to measure and draw the label
   */
  public int add(GL10 gl, String text, Paint textPaint) {
    return add(gl, null, text, textPaint);
  }

  /**
   * Call to add a label
   * 
   * @param gl
   * @param text
   *            the text of the label
   * @param textPaint
   *            the paint of the label
   * @return the id of the label, used to measure and draw the label
   */
  public int add(GL10 gl, Drawable background, String text, Paint textPaint) {
    return add(gl, background, text, textPaint, 0, 0);
  }

  /**
   * Call to add a label
   * 
   * @return the id of the label, used to measure and draw the label
   */
  public int add(GL10 gl, Drawable drawable, int minWidth, int minHeight) {
    return add(gl, drawable, null, null, minWidth, minHeight);
  }

  /**
   * Call to add a label
   * 
   * @param gl
   * @param text
   *            the text of the label
   * @param textPaint
   *            the paint of the label
   * @return the id of the label, used to measure and draw the label
   */
  public int add(GL10 gl, Drawable background, String text, Paint textPaint,
      int minWidth, int minHeight) {
    checkState(STATE_ADDING, STATE_ADDING);
    boolean drawBackground = background != null;
    boolean drawText = (text != null) && (textPaint != null);

    Rect padding = new Rect();
    if (drawBackground) {
      background.getPadding(padding);
      minWidth = Math.max(minWidth, background.getMinimumWidth());
      minHeight = Math.max(minHeight, background.getMinimumHeight());
    }

    int ascent = 0;
    int descent = 0;
    int measuredTextWidth = 0;
    if (drawText) {
      // Paint.ascent is negative, so negate it.
      ascent = (int) Math.ceil(-textPaint.ascent());
      descent = (int) Math.ceil(textPaint.descent());
      measuredTextWidth = (int) Math.ceil(textPaint.measureText(text));
    }
    int textHeight = ascent + descent;
    int textWidth = Math.min(mStrikeWidth, measuredTextWidth);

    int padHeight = padding.top + padding.bottom;
    int padWidth = padding.left + padding.right;
    int height = Math.max(minHeight, textHeight + padHeight);
    int width = Math.max(minWidth, textWidth + padWidth);
    int effectiveTextHeight = height - padHeight;
    int effectiveTextWidth = width - padWidth;

    int centerOffsetHeight = (effectiveTextHeight - textHeight) / 2;
    int centerOffsetWidth = (effectiveTextWidth - textWidth) / 2;

    // Make changes to the local variables, only commit them
    // to the member variables after we've decided not to throw
    // any exceptions.

    int u = mU;
    int v = mV;
    int lineHeight = mLineHeight;

    if (width > mStrikeWidth) {
      width = mStrikeWidth;
    }

    // Is there room for this string on the current line?
    if (u + width > mStrikeWidth) {
      // No room, go to the next line:
      u = 0;
      v += lineHeight;
      lineHeight = 0;
    }
    lineHeight = Math.max(lineHeight, height);
    if (v + lineHeight > mStrikeHeight) {
      throw new IllegalArgumentException("Out of texture space.");
    }

    int u2 = u + width;
    int vBase = v + ascent;
    int v2 = v + height;

    if (drawBackground) {
      background.setBounds(u, v, u + width, v + height);
      background.draw(mCanvas);
    }

    if (drawText) {
      mCanvas.drawText(text, u + padding.left + centerOffsetWidth, vBase
          + padding.top + centerOffsetHeight, textPaint);
    }

    // We know there's enough space, so update the member variables
    mU = u + width;
    mV = v;
    mLineHeight = lineHeight;
    mLabels.add(new Label(width, height, ascent, u, v + height, width,
        -height));
    return mLabels.size() - 1;
  }

  /**
   * Call to end adding labels. Must be called before drawing starts.
   * 
   * @param gl
   */
  public void endAdding(GL10 gl) {
    checkState(STATE_ADDING, STATE_INITIALIZED);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);
    // Reclaim storage used by bitmap and canvas.
    mBitmap.recycle();
    mBitmap = null;
    mCanvas = null;
  }

  /**
   * Get the width in pixels of a given label.
   * 
   * @param labelID
   * @return the width in pixels
   */
  public float getWidth(int labelID) {
    return mLabels.get(labelID).width;
  }

  /**
   * Get the height in pixels of a given label.
   * 
   * @param labelID
   * @return the height in pixels
   */
  public float getHeight(int labelID) {
    return mLabels.get(labelID).height;
  }

  /**
   * Get the baseline of a given label. That's how many pixels from the top of
   * the label to the text baseline. (This is equivalent to the negative of
   * the label's paint's ascent.)
   * 
   * @param labelID
   * @return the baseline in pixels.
   */
  public float getBaseline(int labelID) {
    return mLabels.get(labelID).baseline;
  }

  /**
   * Begin drawing labels. Sets the OpenGL state for rapid drawing.
   * 
   * @param gl
   * @param viewWidth
   * @param viewHeight
   */
  public void beginDrawing(GL10 gl, float viewWidth, float viewHeight) {
    checkState(STATE_INITIALIZED, STATE_DRAWING);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
    gl.glShadeModel(GL10.GL_FLAT);
    gl.glEnable(GL10.GL_BLEND);
    gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
    gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glPushMatrix();
    gl.glLoadIdentity();
    gl.glOrthof(0.0f, viewWidth, 0.0f, viewHeight, 0.0f, 1.0f);
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glPushMatrix();
    gl.glLoadIdentity();
    // Magic offsets to promote consistent rasterization.
    gl.glTranslatef(0.375f, 0.375f, 0.0f);
  }

  /**
   * Draw a given label at a given x,y position, expressed in pixels, with the
   * lower-left-hand-corner of the view being (0,0).
   * 
   * @param gl
   * @param x
   * @param y
   * @param labelID
   */
  public void draw(GL10 gl, float x, float y, int labelID) {
    checkState(STATE_DRAWING, STATE_DRAWING);
    Label label = mLabels.get(labelID);
    gl.glEnable(GL10.GL_TEXTURE_2D);
    ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
        GL11Ext.GL_TEXTURE_CROP_RECT_OES, label.mCrop, 0);
    ((GL11Ext) gl).glDrawTexiOES((int) x, (int) y, 0, (int) label.width,
        (int) label.height);
  }

  /**
   * Ends the drawing and restores the OpenGL state.
   * 
   * @param gl
   */
  public void endDrawing(GL10 gl) {
    checkState(STATE_DRAWING, STATE_INITIALIZED);
    gl.glDisable(GL10.GL_BLEND);
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glPopMatrix();
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glPopMatrix();
  }

  private void checkState(int oldState, int newState) {
    if (mState != oldState) {
      throw new IllegalArgumentException("Can't call this method now.");
    }
    mState = newState;
  }

  private static class Label {
    public Label(float width, float height, float baseLine, int cropU,
        int cropV, int cropW, int cropH) {
      this.width = width;
      this.height = height;
      this.baseline = baseLine;
      int[] crop = new int[4];
      crop[0] = cropU;
      crop[1] = cropV;
      crop[2] = cropW;
      crop[3] = cropH;
      mCrop = crop;
    }

    public float width;
    public float height;
    public float baseline;
    public int[] mCrop;
  }

  private int mStrikeWidth;
  private int mStrikeHeight;
  private boolean mFullColor;
  private Bitmap mBitmap;
  private Canvas mCanvas;
  private Paint mClearPaint;

  private int mTextureID;

  private float mTexelWidth; // Convert texel to U
  private float mTexelHeight; // Convert texel to V
  private int mU;
  private int mV;
  private int mLineHeight;
  private ArrayList<Label> mLabels = new ArrayList<Label>();

  private static final int STATE_NEW = 0;
  private static final int STATE_INITIALIZED = 1;
  private static final int STATE_ADDING = 2;
  private static final int STATE_DRAWING = 3;
  private int mState;
}

class SpriteTextRenderer implements GLSurfaceView.Renderer {

  public SpriteTextRenderer(Context context) {
    mContext = context;
    mTriangle = new Triangle();
    mProjector = new Projector();
    mLabelPaint = new Paint();
    mLabelPaint.setTextSize(32);
    mLabelPaint.setAntiAlias(true);
    mLabelPaint.setARGB(0xff, 0x00, 0x00, 0x00);
  }

  public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    /*
     * 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);

    /*
     * Some one-time OpenGL initialization can be made here probably based
     * on features of this particular context
     */
    gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);

    gl.glClearColor(.5f, .5f, .5f, 1);
    gl.glShadeModel(GL10.GL_SMOOTH);
    gl.glEnable(GL10.GL_DEPTH_TEST);
    gl.glEnable(GL10.GL_TEXTURE_2D);

    /*
     * Create our texture. This has to be done each time the surface is
     * created.
     */

    int[] textures = new int[1];
    gl.glGenTextures(1, textures, 0);

    mTextureID = textures[0];
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
        GL10.GL_NEAREST);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
        GL10.GL_LINEAR);

    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
        GL10.GL_CLAMP_TO_EDGE);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
        GL10.GL_CLAMP_TO_EDGE);

    gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
        GL10.GL_REPLACE);

    InputStream is = mContext.getResources().openRawResource(R.raw.robot);
    Bitmap bitmap;
    try {
      bitmap = BitmapFactory.decodeStream(is);
    } finally {
      try {
        is.close();
      } catch (IOException e) {
        // Ignore.
      }
    }

    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    bitmap.recycle();

    if (mLabels != null) {
      mLabels.shutdown(gl);
    } else {
      mLabels = new LabelMaker(true, 256, 64);
    }
    mLabels.initialize(gl);
    mLabels.beginAdding(gl);
    mLabelA = mLabels.add(gl, "A", mLabelPaint);
    mLabelB = mLabels.add(gl, "B", mLabelPaint);
    mLabelC = mLabels.add(gl, "C", mLabelPaint);
    mLabelMsPF = mLabels.add(gl, "ms/f", mLabelPaint);
    mLabels.endAdding(gl);

    if (mNumericSprite != null) {
      mNumericSprite.shutdown(gl);
    } else {
      mNumericSprite = new NumericSprite();
    }
    mNumericSprite.initialize(gl, mLabelPaint);
  }

  public void onDrawFrame(GL10 gl) {
    /*
     * 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.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
        GL10.GL_MODULATE);

    /*
     * 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().
     */

    gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

    /*
     * Now we're ready to draw some 3D objects
     */

    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();

    GLU.gluLookAt(gl, 0.0f, 0.0f, -2.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

    gl.glActiveTexture(GL10.GL_TEXTURE0);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
    gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
        GL10.GL_REPEAT);
    gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
        GL10.GL_REPEAT);

    if (false) {
      long time = SystemClock.uptimeMillis();
      if (mLastTime != 0) {
        long delta = time - mLastTime;
        Log.w("time", Long.toString(delta));
      }
      mLastTime = time;
    }

    long time = SystemClock.uptimeMillis() % 4000L;
    float angle = 0.090f * ((int) time);

    gl.glRotatef(angle, 0, 0, 1.0f);
    gl.glScalef(2.0f, 2.0f, 2.0f);

    mTriangle.draw(gl);

    mProjector.getCurrentModelView(gl);
    mLabels.beginDrawing(gl, mWidth, mHeight);
    drawLabel(gl, 0, mLabelA);
    drawLabel(gl, 1, mLabelB);
    drawLabel(gl, 2, mLabelC);
    float msPFX = mWidth - mLabels.getWidth(mLabelMsPF) - 1;
    mLabels.draw(gl, msPFX, 0, mLabelMsPF);
    mLabels.endDrawing(gl);

    drawMsPF(gl, msPFX);
  }

  private void drawMsPF(GL10 gl, float rightMargin) {
    long time = SystemClock.uptimeMillis();
    if (mStartTime == 0) {
      mStartTime = time;
    }
    if (mFrames++ == SAMPLE_PERIOD_FRAMES) {
      mFrames = 0;
      long delta = time - mStartTime;
      mStartTime = time;
      mMsPerFrame = (int) (delta * SAMPLE_FACTOR);
    }
    if (mMsPerFrame > 0) {
      mNumericSprite.setValue(mMsPerFrame);
      float numWidth = mNumericSprite.width();
      float x = rightMargin - numWidth;
      mNumericSprite.draw(gl, x, 0, mWidth, mHeight);
    }
  }

  private void drawLabel(GL10 gl, int triangleVertex, int labelId) {
    float x = mTriangle.getX(triangleVertex);
    float y = mTriangle.getY(triangleVertex);
    mScratch[0] = x;
    mScratch[1] = y;
    mScratch[2] = 0.0f;
    mScratch[3] = 1.0f;
    mProjector.project(mScratch, 0, mScratch, 4);
    float sx = mScratch[4];
    float sy = mScratch[5];
    float height = mLabels.getHeight(labelId);
    float width = mLabels.getWidth(labelId);
    float tx = sx - width * 0.5f;
    float ty = sy - height * 0.5f;
    mLabels.draw(gl, tx, ty, labelId);
  }

  public void onSurfaceChanged(GL10 gl, int w, int h) {
    mWidth = w;
    mHeight = h;
    gl.glViewport(0, 0, w, h);
    mProjector.setCurrentView(0, 0, w, h);

    /*
     * 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) w / h;
    gl.glMatrixMode(GL10.GL_PROJECTION);
    gl.glLoadIdentity();
    gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    mProjector.getCurrentProjection(gl);
  }

  private int mWidth;
  private int mHeight;
  private Context mContext;
  private Triangle mTriangle;
  private int mTextureID;
  private int mFrames;
  private int mMsPerFrame;
  private final static int SAMPLE_PERIOD_FRAMES = 12;
  private final static float SAMPLE_FACTOR = 1.0f / SAMPLE_PERIOD_FRAMES;
  private long mStartTime;
  private LabelMaker mLabels;
  private Paint mLabelPaint;
  private int mLabelA;
  private int mLabelB;
  private int mLabelC;
  private int mLabelMsPF;
  private Projector mProjector;
  private NumericSprite mNumericSprite;
  private float[] mScratch = new float[8];
  private long mLastTime;
}

class Triangle {
  public Triangle() {

    // Buffers to be passed to gl*Pointer() functions
    // must be direct, i.e., they must be placed on the
    // native heap where the garbage collector cannot
    // move them.
    //
    // Buffers with multi-byte datatypes (e.g., short, int, float)
    // must have their byte order set to native order

    ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
    vbb.order(ByteOrder.nativeOrder());
    mFVertexBuffer = vbb.asFloatBuffer();

    ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
    tbb.order(ByteOrder.nativeOrder());
    mTexBuffer = tbb.asFloatBuffer();

    ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
    ibb.order(ByteOrder.nativeOrder());
    mIndexBuffer = ibb.asShortBuffer();

    for (int i = 0; i < VERTS; i++) {
      for (int j = 0; j < 3; j++) {
        mFVertexBuffer.put(sCoords[i * 3 + j]);
      }
    }

    for (int i = 0; i < VERTS; i++) {
      for (int j = 0; j < 2; j++) {
        mTexBuffer.put(sCoords[i * 3 + j] * 2.0f + 0.5f);
      }
    }

    for (int i = 0; i < VERTS; i++) {
      mIndexBuffer.put((short) i);
    }

    mFVertexBuffer.position(0);
    mTexBuffer.position(0);
    mIndexBuffer.position(0);
  }

  public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
    gl.glEnable(GL10.GL_TEXTURE_2D);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);
    gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS,
        GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
  }

  public float getX(int vertex) {
    return sCoords[3 * vertex];
  }

  public float getY(int vertex) {
    return sCoords[3 * vertex + 1];
  }

  private final static int VERTS = 3;

  private FloatBuffer mFVertexBuffer;
  private FloatBuffer mTexBuffer;
  private ShortBuffer mIndexBuffer;
  // A unit-sided equalateral triangle centered on the origin.
  private final static float[] sCoords = {
      // X, Y, Z
      -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 };
}

/**
 * A utility that projects
 * 
 */
class Projector {
  public Projector() {
    mMVP = new float[16];
    mV = new float[4];
    mGrabber = new MatrixGrabber();
  }

  public void setCurrentView(int x, int y, int width, int height) {
    mX = x;
    mY = y;
    mViewWidth = width;
    mViewHeight = height;
  }

  public void project(float[] obj, int objOffset, float[] win, int winOffset) {
    if (!mMVPComputed) {
      Matrix.multiplyMM(mMVP, 0, mGrabber.mProjection, 0,
          mGrabber.mModelView, 0);
      mMVPComputed = true;
    }

    Matrix.multiplyMV(mV, 0, mMVP, 0, obj, objOffset);

    float rw = 1.0f / mV[3];

    win[winOffset] = mX + mViewWidth * (mV[0] * rw + 1.0f) * 0.5f;
    win[winOffset + 1] = mY + mViewHeight * (mV[1] * rw + 1.0f) * 0.5f;
    win[winOffset + 2] = (mV[2] * rw + 1.0f) * 0.5f;
  }

  /**
   * Get the current projection matrix. Has the side-effect of setting current
   * matrix mode to GL_PROJECTION
   * 
   * @param gl
   */
  public void getCurrentProjection(GL10 gl) {
    mGrabber.getCurrentProjection(gl);
    mMVPComputed = false;
  }

  /**
   * Get the current model view matrix. Has the side-effect of setting current
   * matrix mode to GL_MODELVIEW
   * 
   * @param gl
   */
  public void getCurrentModelView(GL10 gl) {
    mGrabber.getCurrentModelView(gl);
    mMVPComputed = false;
  }

  private MatrixGrabber mGrabber;
  private boolean mMVPComputed;
  private float[] mMVP;
  private float[] mV;
  private int mX;
  private int mY;
  private int mViewWidth;
  private int mViewHeight;
}

/**
 * Allows retrieving the current matrix even if the current OpenGL ES driver
 * does not support retrieving the current matrix.
 * 
 * Note: the actual matrix may differ from the retrieved matrix, due to
 * differences in the way the math is implemented by GLMatrixWrapper as compared
 * to the way the math is implemented by the OpenGL ES driver.
 */
class MatrixTrackingGL implements GL, GL10, GL10Ext, GL11, GL11Ext {
  private GL10 mgl;
  private GL10Ext mgl10Ext;
  private GL11 mgl11;
  private GL11Ext mgl11Ext;
  private int mMatrixMode;
  private MatrixStack mCurrent;
  private MatrixStack mModelView;
  private MatrixStack mTexture;
  private MatrixStack mProjection;

  private final static boolean _check = false;
  ByteBuffer mByteBuffer;
  FloatBuffer mFloatBuffer;
  float[] mCheckA;
  float[] mCheckB;

  public MatrixTrackingGL(GL gl) {
    mgl = (GL10) gl;
    if (gl instanceof GL10Ext) {
      mgl10Ext = (GL10Ext) gl;
    }
    if (gl instanceof GL11) {
      mgl11 = (GL11) gl;
    }
    if (gl instanceof GL11Ext) {
      mgl11Ext = (GL11Ext) gl;
    }
    mModelView = new MatrixStack();
    mProjection = new MatrixStack();
    mTexture = new MatrixStack();
    mCurrent = mModelView;
    mMatrixMode = GL10.GL_MODELVIEW;
  }

  // ---------------------------------------------------------------------
  // GL10 methods:

  public void glActiveTexture(int texture) {
    mgl.glActiveTexture(texture);
  }

  public void glAlphaFunc(int func, float ref) {
    mgl.glAlphaFunc(func, ref);
  }

  public void glAlphaFuncx(int func, int ref) {
    mgl.glAlphaFuncx(func, ref);
  }

  public void glBindTexture(int target, int texture) {
    mgl.glBindTexture(target, texture);
  }

  public void glBlendFunc(int sfactor, int dfactor) {
    mgl.glBlendFunc(sfactor, dfactor);
  }

  public void glClear(int mask) {
    mgl.glClear(mask);
  }

  public void glClearColor(float red, float green, float blue, float alpha) {
    mgl.glClearColor(red, green, blue, alpha);
  }

  public void glClearColorx(int red, int green, int blue, int alpha) {
    mgl.glClearColorx(red, green, blue, alpha);
  }

  public void glClearDepthf(float depth) {
    mgl.glClearDepthf(depth);
  }

  public void glClearDepthx(int depth) {
    mgl.glClearDepthx(depth);
  }

  public void glClearStencil(int s) {
    mgl.glClearStencil(s);
  }

  public void glClientActiveTexture(int texture) {
    mgl.glClientActiveTexture(texture);
  }

  public void glColor4f(float red, float green, float blue, float alpha) {
    mgl.glColor4f(red, green, blue, alpha);
  }

  public void glColor4x(int red, int green, int blue, int alpha) {
    mgl.glColor4x(red, green, blue, alpha);
  }

  public void glColorMask(boolean red, boolean green, boolean blue,
      boolean alpha) {
    mgl.glColorMask(red, green, blue, alpha);
  }

  public void glColorPointer(int size, int type, int stride, Buffer pointer) {
    mgl.glColorPointer(size, type, stride, pointer);
  }

  public void glCompressedTexImage2D(int target, int level,
      int internalformat, int width, int height, int border,
      int imageSize, Buffer data) {
    mgl.glCompressedTexImage2D(target, level, internalformat, width,
        height, border, imageSize, data);
  }

  public void glCompressedTexSubImage2D(int target, int level, int xoffset,
      int yoffset, int width, int height, int format, int imageSize,
      Buffer data) {
    mgl.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width,
        height, format, imageSize, data);
  }

  public void glCopyTexImage2D(int target, int level, int internalformat,
      int x, int y, int width, int height, int border) {
    mgl.glCopyTexImage2D(target, level, internalformat, x, y, width,
        height, border);
  }

  public void glCopyTexSubImage2D(int target, int level, int xoffset,
      int yoffset, int x, int y, int width, int height) {
    mgl.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width,
        height);
  }

  public void glCullFace(int mode) {
    mgl.glCullFace(mode);
  }

  public void glDeleteTextures(int n, int[] textures, int offset) {
    mgl.glDeleteTextures(n, textures, offset);
  }

  public void glDeleteTextures(int n, IntBuffer textures) {
    mgl.glDeleteTextures(n, textures);
  }

  public void glDepthFunc(int func) {
    mgl.glDepthFunc(func);
  }

  public void glDepthMask(boolean flag) {
    mgl.glDepthMask(flag);
  }

  public void glDepthRangef(float near, float far) {
    mgl.glDepthRangef(near, far);
  }

  public void glDepthRangex(int near, int far) {
    mgl.glDepthRangex(near, far);
  }

  public void glDisable(int cap) {
    mgl.glDisable(cap);
  }

  public void glDisableClientState(int array) {
    mgl.glDisableClientState(array);
  }

  public void glDrawArrays(int mode, int first, int count) {
    mgl.glDrawArrays(mode, first, count);
  }

  public void glDrawElements(int mode, int count, int type, Buffer indices) {
    mgl.glDrawElements(mode, count, type, indices);
  }

  public void glEnable(int cap) {
    mgl.glEnable(cap);
  }

  public void glEnableClientState(int array) {
    mgl.glEnableClientState(array);
  }

  public void glFinish() {
    mgl.glFinish();
  }

  public void glFlush() {
    mgl.glFlush();
  }

  public void glFogf(int pname, float param) {
    mgl.glFogf(pname, param);
  }

  public void glFogfv(int pname, float[] params, int offset) {
    mgl.glFogfv(pname, params, offset);
  }

  public void glFogfv(int pname, FloatBuffer params) {
    mgl.glFogfv(pname, params);
  }

  public void glFogx(int pname, int param) {
    mgl.glFogx(pname, param);
  }

  public void glFogxv(int pname, int[] params, int offset) {
    mgl.glFogxv(pname, params, offset);
  }

  public void glFogxv(int pname, IntBuffer params) {
    mgl.glFogxv(pname, params);
  }

  public void glFrontFace(int mode) {
    mgl.glFrontFace(mode);
  }

  public void glFrustumf(float left, float right, float bottom, float top,
      float near, float far) {
    mCurrent.glFrustumf(left, right, bottom, top, near, far);
    mgl.glFrustumf(left, right, bottom, top, near, far);
    if (_check)
      check();
  }

  public void glFrustumx(int left, int right, int bottom, int top, int near,
      int far) {
    mCurrent.glFrustumx(left, right, bottom, top, near, far);
    mgl.glFrustumx(left, right, bottom, top, near, far);
    if (_check)
      check();
  }

  public void glGenTextures(int n, int[] textures, int offset) {
    mgl.glGenTextures(n, textures, offset);
  }

  public void glGenTextures(int n, IntBuffer textures) {
    mgl.glGenTextures(n, textures);
  }

  public int glGetError() {
    int result = mgl.glGetError();
    return result;
  }

  public void glGetIntegerv(int pname, int[] params, int offset) {
    mgl.glGetIntegerv(pname, params, offset);
  }

  public void glGetIntegerv(int pname, IntBuffer params) {
    mgl.glGetIntegerv(pname, params);
  }

  public String glGetString(int name) {
    String result = mgl.glGetString(name);
    return result;
  }

  public void glHint(int target, int mode) {
    mgl.glHint(target, mode);
  }

  public void glLightModelf(int pname, float param) {
    mgl.glLightModelf(pname, param);
  }

  public void glLightModelfv(int pname, float[] params, int offset) {
    mgl.glLightModelfv(pname, params, offset);
  }

  public void glLightModelfv(int pname, FloatBuffer params) {
    mgl.glLightModelfv(pname, params);
  }

  public void glLightModelx(int pname, int param) {
    mgl.glLightModelx(pname, param);
  }

  public void glLightModelxv(int pname, int[] params, int offset) {
    mgl.glLightModelxv(pname, params, offset);
  }

  public void glLightModelxv(int pname, IntBuffer params) {
    mgl.glLightModelxv(pname, params);
  }

  public void glLightf(int light, int pname, float param) {
    mgl.glLightf(light, pname, param);
  }

  public void glLightfv(int light, int pname, float[] params, int offset) {
    mgl.glLightfv(light, pname, params, offset);
  }

  public void glLightfv(int light, int pname, FloatBuffer params) {
    mgl.glLightfv(light, pname, params);
  }

  public void glLightx(int light, int pname, int param) {
    mgl.glLightx(light, pname, param);
  }

  public void glLightxv(int light, int pname, int[] params, int offset) {
    mgl.glLightxv(light, pname, params, offset);
  }

  public void glLightxv(int light, int pname, IntBuffer params) {
    mgl.glLightxv(light, pname, params);
  }

  public void glLineWidth(float width) {
    mgl.glLineWidth(width);
  }

  public void glLineWidthx(int width) {
    mgl.glLineWidthx(width);
  }

  public void glLoadIdentity() {
    mCurrent.glLoadIdentity();
    mgl.glLoadIdentity();
    if (_check)
      check();
  }

  public void glLoadMatrixf(float[] m, int offset) {
    mCurrent.glLoadMatrixf(m, offset);
    mgl.glLoadMatrixf(m, offset);
    if (_check)
      check();
  }

  public void glLoadMatrixf(FloatBuffer m) {
    int position = m.position();
    mCurrent.glLoadMatrixf(m);
    m.position(position);
    mgl.glLoadMatrixf(m);
    if (_check)
      check();
  }

  public void glLoadMatrixx(int[] m, int offset) {
    mCurrent.glLoadMatrixx(m, offset);
    mgl.glLoadMatrixx(m, offset);
    if (_check)
      check();
  }

  public void glLoadMatrixx(IntBuffer m) {
    int position = m.position();
    mCurrent.glLoadMatrixx(m);
    m.position(position);
    mgl.glLoadMatrixx(m);
    if (_check)
      check();
  }

  public void glLogicOp(int opcode) {
    mgl.glLogicOp(opcode);
  }

  public void glMaterialf(int face, int pname, float param) {
    mgl.glMaterialf(face, pname, param);
  }

  public void glMaterialfv(int face, int pname, float[] params, int offset) {
    mgl.glMaterialfv(face, pname, params, offset);
  }

  public void glMaterialfv(int face, int pname, FloatBuffer params) {
    mgl.glMaterialfv(face, pname, params);
  }

  public void glMaterialx(int face, int pname, int param) {
    mgl.glMaterialx(face, pname, param);
  }

  public void glMaterialxv(int face, int pname, int[] params, int offset) {
    mgl.glMaterialxv(face, pname, params, offset);
  }

  public void glMaterialxv(int face, int pname, IntBuffer params) {
    mgl.glMaterialxv(face, pname, params);
  }

  public void glMatrixMode(int mode) {
    switch (mode) {
    case GL10.GL_MODELVIEW:
      mCurrent = mModelView;
      break;
    case GL10.GL_TEXTURE:
      mCurrent = mTexture;
      break;
    case GL10.GL_PROJECTION:
      mCurrent = mProjection;
      break;
    default:
      throw new IllegalArgumentException("Unknown matrix mode: " + mode);
    }
    mgl.glMatrixMode(mode);
    mMatrixMode = mode;
    if (_check)
      check();
  }

  public void glMultMatrixf(float[] m, int offset) {
    mCurrent.glMultMatrixf(m, offset);
    mgl.glMultMatrixf(m, offset);
    if (_check)
      check();
  }

  public void glMultMatrixf(FloatBuffer m) {
    int position = m.position();
    mCurrent.glMultMatrixf(m);
    m.position(position);
    mgl.glMultMatrixf(m);
    if (_check)
      check();
  }

  public void glMultMatrixx(int[] m, int offset) {
    mCurrent.glMultMatrixx(m, offset);
    mgl.glMultMatrixx(m, offset);
    if (_check)
      check();
  }

  public void glMultMatrixx(IntBuffer m) {
    int position = m.position();
    mCurrent.glMultMatrixx(m);
    m.position(position);
    mgl.glMultMatrixx(m);
    if (_check)
      check();
  }

  public void glMultiTexCoord4f(int target, float s, float t, float r, float q) {
    mgl.glMultiTexCoord4f(target, s, t, r, q);
  }

  public void glMultiTexCoord4x(int target, int s, int t, int r, int q) {
    mgl.glMultiTexCoord4x(target, s, t, r, q);
  }

  public void glNormal3f(float nx, float ny, float nz) {
    mgl.glNormal3f(nx, ny, nz);
  }

  public void glNormal3x(int nx, int ny, int nz) {
    mgl.glNormal3x(nx, ny, nz);
  }

  public void glNormalPointer(int type, int stride, Buffer pointer) {
    mgl.glNormalPointer(type, stride, pointer);
  }

  public void glOrthof(float left, float right, float bottom, float top,
      float near, float far) {
    mCurrent.glOrthof(left, right, bottom, top, near, far);
    mgl.glOrthof(left, right, bottom, top, near, far);
    if (_check)
      check();
  }

  public void glOrthox(int left, int right, int bottom, int top, int near,
      int far) {
    mCurrent.glOrthox(left, right, bottom, top, near, far);
    mgl.glOrthox(left, right, bottom, top, near, far);
    if (_check)
      check();
  }

  public void glPixelStorei(int pname, int param) {
    mgl.glPixelStorei(pname, param);
  }

  public void glPointSize(float size) {
    mgl.glPointSize(size);
  }

  public void glPointSizex(int size) {
    mgl.glPointSizex(size);
  }

  public void glPolygonOffset(float factor, float units) {
    mgl.glPolygonOffset(factor, units);
  }

  public void glPolygonOffsetx(int factor, int units) {
    mgl.glPolygonOffsetx(factor, units);
  }

  public void glPopMatrix() {
    mCurrent.glPopMatrix();
    mgl.glPopMatrix();
    if (_check)
      check();
  }

  public void glPushMatrix() {
    mCurrent.glPushMatrix();
    mgl.glPushMatrix();
    if (_check)
      check();
  }

  public void glReadPixels(int x, int y, int width, int height, int format,
      int type, Buffer pixels) {
    mgl.glReadPixels(x, y, width, height, format, type, pixels);
  }

  public void glRotatef(float angle, float x, float y, float z) {
    mCurrent.glRotatef(angle, x, y, z);
    mgl.glRotatef(angle, x, y, z);
    if (_check)
      check();
  }

  public void glRotatex(int angle, int x, int y, int z) {
    mCurrent.glRotatex(angle, x, y, z);
    mgl.glRotatex(angle, x, y, z);
    if (_check)
      check();
  }

  public void glSampleCoverage(float value, boolean invert) {
    mgl.glSampleCoverage(value, invert);
  }

  public void glSampleCoveragex(int value, boolean invert) {
    mgl.glSampleCoveragex(value, invert);
  }

  public void glScalef(float x, float y, float z) {
    mCurrent.glScalef(x, y, z);
    mgl.glScalef(x, y, z);
    if (_check)
      check();
  }

  public void glScalex(int x, int y, int z) {
    mCurrent.glScalex(x, y, z);
    mgl.glScalex(x, y, z);
    if (_check)
      check();
  }

  public void glScissor(int x, int y, int width, int height) {
    mgl.glScissor(x, y, width, height);
  }

  public void glShadeModel(int mode) {
    mgl.glShadeModel(mode);
  }

  public void glStencilFunc(int func, int ref, int mask) {
    mgl.glStencilFunc(func, ref, mask);
  }

  public void glStencilMask(int mask) {
    mgl.glStencilMask(mask);
  }

  public void glStencilOp(int fail, int zfail, int zpass) {
    mgl.glStencilOp(fail, zfail, zpass);
  }

  public void glTexCoordPointer(int size, int type, int stride, Buffer pointer) {
    mgl.glTexCoordPointer(size, type, stride, pointer);
  }

  public void glTexEnvf(int target, int pname, float param) {
    mgl.glTexEnvf(target, pname, param);
  }

  public void glTexEnvfv(int target, int pname, float[] params, int offset) {
    mgl.glTexEnvfv(target, pname, params, offset);
  }

  public void glTexEnvfv(int target, int pname, FloatBuffer params) {
    mgl.glTexEnvfv(target, pname, params);
  }

  public void glTexEnvx(int target, int pname, int param) {
    mgl.glTexEnvx(target, pname, param);
  }

  public void glTexEnvxv(int target, int pname, int[] params, int offset) {
    mgl.glTexEnvxv(target, pname, params, offset);
  }

  public void glTexEnvxv(int target, int pname, IntBuffer params) {
    mgl.glTexEnvxv(target, pname, params);
  }

  public void glTexImage2D(int target, int level, int internalformat,
      int width, int height, int border, int format, int type,
      Buffer pixels) {
    mgl.glTexImage2D(target, level, internalformat, width, height, border,
        format, type, pixels);
  }

  public void glTexParameterf(int target, int pname, float param) {
    mgl.glTexParameterf(target, pname, param);
  }

  public void glTexParameterx(int target, int pname, int param) {
    mgl.glTexParameterx(target, pname, param);
  }

  public void glTexParameteriv(int target, int pname, int[] params, int offset) {
    mgl11.glTexParameteriv(target, pname, params, offset);
  }

  public void glTexParameteriv(int target, int pname, IntBuffer params) {
    mgl11.glTexParameteriv(target, pname, params);
  }

  public void glTexSubImage2D(int target, int level, int xoffset,
      int yoffset, int width, int height, int format, int type,
      Buffer pixels) {
    mgl.glTexSubImage2D(target, level, xoffset, yoffset, width, height,
        format, type, pixels);
  }

  public void glTranslatef(float x, float y, float z) {
    mCurrent.glTranslatef(x, y, z);
    mgl.glTranslatef(x, y, z);
    if (_check)
      check();
  }

  public void glTranslatex(int x, int y, int z) {
    mCurrent.glTranslatex(x, y, z);
    mgl.glTranslatex(x, y, z);
    if (_check)
      check();
  }

  public void glVertexPointer(int size, int type, int stride, Buffer pointer) {
    mgl.glVertexPointer(size, type, stride, pointer);
  }

  public void glViewport(int x, int y, int width, int height) {
    mgl.glViewport(x, y, width, height);
  }

  public void glClipPlanef(int plane, float[] equation, int offset) {
    mgl11.glClipPlanef(plane, equation, offset);
  }

  public void glClipPlanef(int plane, FloatBuffer equation) {
    mgl11.glClipPlanef(plane, equation);
  }

  public void glClipPlanex(int plane, int[] equation, int offset) {
    mgl11.glClipPlanex(plane, equation, offset);
  }

  public void glClipPlanex(int plane, IntBuffer equation) {
    mgl11.glClipPlanex(plane, equation);
  }

  // Draw Texture Extension

  public void glDrawTexfOES(float x, float y, float z, float width,
      float height) {
    mgl11Ext.glDrawTexfOES(x, y, z, width, height);
  }

  public void glDrawTexfvOES(float[] coords, int offset) {
    mgl11Ext.glDrawTexfvOES(coords, offset);
  }

  public void glDrawTexfvOES(FloatBuffer coords) {
    mgl11Ext.glDrawTexfvOES(coords);
  }

  public void glDrawTexiOES(int x, int y, int z, int width, int height) {
    mgl11Ext.glDrawTexiOES(x, y, z, width, height);
  }

  public void glDrawTexivOES(int[] coords, int offset) {
    mgl11Ext.glDrawTexivOES(coords, offset);
  }

  public void glDrawTexivOES(IntBuffer coords) {
    mgl11Ext.glDrawTexivOES(coords);
  }

  public void glDrawTexsOES(short x, short y, short z, short width,
      short height) {
    mgl11Ext.glDrawTexsOES(x, y, z, width, height);
  }

  public void glDrawTexsvOES(short[] coords, int offset) {
    mgl11Ext.glDrawTexsvOES(coords, offset);
  }

  public void glDrawTexsvOES(ShortBuffer coords) {
    mgl11Ext.glDrawTexsvOES(coords);
  }

  public void glDrawTexxOES(int x, int y, int z, int width, int height) {
    mgl11Ext.glDrawTexxOES(x, y, z, width, height);
  }

  public void glDrawTexxvOES(int[] coords, int offset) {
    mgl11Ext.glDrawTexxvOES(coords, offset);
  }

  public void glDrawTexxvOES(IntBuffer coords) {
    mgl11Ext.glDrawTexxvOES(coords);
  }

  public int glQueryMatrixxOES(int[] mantissa, int mantissaOffset,
      int[] exponent, int exponentOffset) {
    return mgl10Ext.glQueryMatrixxOES(mantissa, mantissaOffset, exponent,
        exponentOffset);
  }

  public int glQueryMatrixxOES(IntBuffer mantissa, IntBuffer exponent) {
    return mgl10Ext.glQueryMatrixxOES(mantissa, exponent);
  }

  // Unsupported GL11 methods

  public void glBindBuffer(int target, int buffer) {
    throw new UnsupportedOperationException();
  }

  public void glBufferData(int target, int size, Buffer data, int usage) {
    throw new UnsupportedOperationException();
  }

  public void glBufferSubData(int target, int offset, int size, Buffer data) {
    throw new UnsupportedOperationException();
  }

  public void glColor4ub(byte red, byte green, byte blue, byte alpha) {
    throw new UnsupportedOperationException();
  }

  public void glDeleteBuffers(int n, int[] buffers, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glDeleteBuffers(int n, IntBuffer buffers) {
    throw new UnsupportedOperationException();
  }

  public void glGenBuffers(int n, int[] buffers, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGenBuffers(int n, IntBuffer buffers) {
    throw new UnsupportedOperationException();
  }

  public void glGetBooleanv(int pname, boolean[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetBooleanv(int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetBufferParameteriv(int target, int pname, int[] params,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetBufferParameteriv(int target, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetClipPlanef(int pname, float[] eqn, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetClipPlanef(int pname, FloatBuffer eqn) {
    throw new UnsupportedOperationException();
  }

  public void glGetClipPlanex(int pname, int[] eqn, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetClipPlanex(int pname, IntBuffer eqn) {
    throw new UnsupportedOperationException();
  }

  public void glGetFixedv(int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetFixedv(int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetFloatv(int pname, float[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetFloatv(int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetLightfv(int light, int pname, float[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetLightfv(int light, int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetLightxv(int light, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetLightxv(int light, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetMaterialfv(int face, int pname, float[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetMaterialfv(int face, int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetMaterialxv(int face, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetMaterialxv(int face, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexEnviv(int env, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexEnviv(int env, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexEnvxv(int env, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexEnvxv(int env, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameterfv(int target, int pname, float[] params,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameterfv(int target, int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameteriv(int target, int pname, int[] params,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameteriv(int target, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameterxv(int target, int pname, int[] params,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetTexParameterxv(int target, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public boolean glIsBuffer(int buffer) {
    throw new UnsupportedOperationException();
  }

  public boolean glIsEnabled(int cap) {
    throw new UnsupportedOperationException();
  }

  public boolean glIsTexture(int texture) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterf(int pname, float param) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterfv(int pname, float[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterfv(int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterx(int pname, int param) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterxv(int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glPointParameterxv(int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glPointSizePointerOES(int type, int stride, Buffer pointer) {
    throw new UnsupportedOperationException();
  }

  public void glTexEnvi(int target, int pname, int param) {
    throw new UnsupportedOperationException();
  }

  public void glTexEnviv(int target, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glTexEnviv(int target, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glTexParameterfv(int target, int pname, float[] params,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glTexParameterfv(int target, int pname, FloatBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glTexParameteri(int target, int pname, int param) {
    throw new UnsupportedOperationException();
  }

  public void glTexParameterxv(int target, int pname, int[] params, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glTexParameterxv(int target, int pname, IntBuffer params) {
    throw new UnsupportedOperationException();
  }

  public void glColorPointer(int size, int type, int stride, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glDrawElements(int mode, int count, int type, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glGetPointerv(int pname, Buffer[] params) {
    throw new UnsupportedOperationException();
  }

  public void glNormalPointer(int type, int stride, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glTexCoordPointer(int size, int type, int stride, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glVertexPointer(int size, int type, int stride, int offset) {
    throw new UnsupportedOperationException();
  }

  public void glCurrentPaletteMatrixOES(int matrixpaletteindex) {
    throw new UnsupportedOperationException();
  }

  public void glLoadPaletteFromModelViewMatrixOES() {
    throw new UnsupportedOperationException();
  }

  public void glMatrixIndexPointerOES(int size, int type, int stride,
      Buffer pointer) {
    throw new UnsupportedOperationException();
  }

  public void glMatrixIndexPointerOES(int size, int type, int stride,
      int offset) {
    throw new UnsupportedOperationException();
  }

  public void glWeightPointerOES(int size, int type, int stride,
      Buffer pointer) {
    throw new UnsupportedOperationException();
  }

  public void glWeightPointerOES(int size, int type, int stride, int offset) {
    throw new UnsupportedOperationException();
  }

  /**
   * Get the current matrix
   */

  public void getMatrix(float[] m, int offset) {
    mCurrent.getMatrix(m, offset);
  }

  /**
   * Get the current matrix mode
   */

  public int getMatrixMode() {
    return mMatrixMode;
  }

  private void check() {
    int oesMode;
    switch (mMatrixMode) {
    case GL_MODELVIEW:
      oesMode = GL11.GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES;
      break;
    case GL_PROJECTION:
      oesMode = GL11.GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES;
      break;
    case GL_TEXTURE:
      oesMode = GL11.GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES;
      break;
    default:
      throw new IllegalArgumentException("Unknown matrix mode");
    }

    if (mByteBuffer == null) {
      mCheckA = new float[16];
      mCheckB = new float[16];
      mByteBuffer = ByteBuffer.allocateDirect(64);
      mByteBuffer.order(ByteOrder.nativeOrder());
      mFloatBuffer = mByteBuffer.asFloatBuffer();
    }
    mgl.glGetIntegerv(oesMode, mByteBuffer.asIntBuffer());
    for (int i = 0; i < 16; i++) {
      mCheckB[i] = mFloatBuffer.get(i);
    }
    mCurrent.getMatrix(mCheckA, 0);

    boolean fail = false;
    for (int i = 0; i < 16; i++) {
      if (mCheckA[i] != mCheckB[i]) {
        Log.d("GLMatWrap", "i:" + i + " a:" + mCheckA[i] + " a:"
            + mCheckB[i]);
        fail = true;
      }
    }
    if (fail) {
      throw new IllegalArgumentException("Matrix math difference.");
    }
  }

}

/**
 * A 2D rectangular mesh. Can be drawn textured or untextured.
 * 
 */
class Grid {

  public Grid(int w, int h) {
    if (w < 0 || w >= 65536) {
      throw new IllegalArgumentException("w");
    }
    if (h < 0 || h >= 65536) {
      throw new IllegalArgumentException("h");
    }
    if (w * h >= 65536) {
      throw new IllegalArgumentException("w * h >= 65536");
    }

    mW = w;
    mH = h;
    int size = w * h;
    final int FLOAT_SIZE = 4;
    final int CHAR_SIZE = 2;
    mVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3)
        .order(ByteOrder.nativeOrder()).asFloatBuffer();
    mTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2)
        .order(ByteOrder.nativeOrder()).asFloatBuffer();

    int quadW = mW - 1;
    int quadH = mH - 1;
    int quadCount = quadW * quadH;
    int indexCount = quadCount * 6;
    mIndexCount = indexCount;
    mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
        .order(ByteOrder.nativeOrder()).asCharBuffer();

    /*
     * Initialize triangle list mesh.
     * 
     * [0]-----[ 1] ... | / | | / | | / | [w]-----[w+1] ... | |
     */

    {
      int i = 0;
      for (int y = 0; y < quadH; y++) {
        for (int x = 0; x < quadW; x++) {
          char a = (char) (y * mW + x);
          char b = (char) (y * mW + x + 1);
          char c = (char) ((y + 1) * mW + x);
          char d = (char) ((y + 1) * mW + x + 1);

          mIndexBuffer.put(i++, a);
          mIndexBuffer.put(i++, b);
          mIndexBuffer.put(i++, c);

          mIndexBuffer.put(i++, b);
          mIndexBuffer.put(i++, c);
          mIndexBuffer.put(i++, d);
        }
      }
    }

  }

  void set(int i, int j, float x, float y, float z, float u, float v) {
    if (i < 0 || i >= mW) {
      throw new IllegalArgumentException("i");
    }
    if (j < 0 || j >= mH) {
      throw new IllegalArgumentException("j");
    }

    int index = mW * j + i;

    int posIndex = index * 3;
    mVertexBuffer.put(posIndex, x);
    mVertexBuffer.put(posIndex + 1, y);
    mVertexBuffer.put(posIndex + 2, z);

    int texIndex = index * 2;
    mTexCoordBuffer.put(texIndex, u);
    mTexCoordBuffer.put(texIndex + 1, v);
  }

  public void draw(GL10 gl, boolean useTexture) {
    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);

    if (useTexture) {
      gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
      gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexCoordBuffer);
      gl.glEnable(GL10.GL_TEXTURE_2D);
    } else {
      gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
      gl.glDisable(GL10.GL_TEXTURE_2D);
    }

    gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount,
        GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
  }

  private FloatBuffer mVertexBuffer;
  private FloatBuffer mTexCoordBuffer;
  private CharBuffer mIndexBuffer;

  private int mW;
  private int mH;
  private int mIndexCount;
}

class MatrixGrabber {
  public MatrixGrabber() {
    mModelView = new float[16];
    mProjection = new float[16];
  }

  /**
   * Record the current modelView and projection matrix state. Has the side
   * effect of setting the current matrix state to GL_MODELVIEW
   * 
   * @param gl
   */
  public void getCurrentState(GL10 gl) {
    getCurrentProjection(gl);
    getCurrentModelView(gl);
  }

  /**
   * Record the current modelView matrix state. Has the side effect of setting
   * the current matrix state to GL_MODELVIEW
   * 
   * @param gl
   */
  public void getCurrentModelView(GL10 gl) {
    getMatrix(gl, GL10.GL_MODELVIEW, mModelView);
  }

  /**
   * Record the current projection matrix state. Has the side effect of
   * setting the current matrix state to GL_PROJECTION
   * 
   * @param gl
   */
  public void getCurrentProjection(GL10 gl) {
    getMatrix(gl, GL10.GL_PROJECTION, mProjection);
  }

  private void getMatrix(GL10 gl, int mode, float[] mat) {
    MatrixTrackingGL gl2 = (MatrixTrackingGL) gl;
    gl2.glMatrixMode(mode);
    gl2.getMatrix(mat, 0);
  }

  public float[] mModelView;
  public float[] mProjection;
}

/**
 * A matrix stack, similar to OpenGL ES's internal matrix stack.
 */
class MatrixStack {
  public MatrixStack() {
    commonInit(DEFAULT_MAX_DEPTH);
  }

  public MatrixStack(int maxDepth) {
    commonInit(maxDepth);
  }

  private void commonInit(int maxDepth) {
    mMatrix = new float[maxDepth * MATRIX_SIZE];
    mTemp = new float[MATRIX_SIZE * 2];
    glLoadIdentity();
  }

  public void glFrustumf(float left, float right, float bottom, float top,
      float near, float far) {
    Matrix.frustumM(mMatrix, mTop, left, right, bottom, top, near, far);
  }

  public void glFrustumx(int left, int right, int bottom, int top, int near,
      int far) {
    glFrustumf(fixedToFloat(left), fixedToFloat(right),
        fixedToFloat(bottom), fixedToFloat(top), fixedToFloat(near),
        fixedToFloat(far));
  }

  public void glLoadIdentity() {
    Matrix.setIdentityM(mMatrix, mTop);
  }

  public void glLoadMatrixf(float[] m, int offset) {
    System.arraycopy(m, offset, mMatrix, mTop, MATRIX_SIZE);
  }

  public void glLoadMatrixf(FloatBuffer m) {
    m.get(mMatrix, mTop, MATRIX_SIZE);
  }

  public void glLoadMatrixx(int[] m, int offset) {
    for (int i = 0; i < MATRIX_SIZE; i++) {
      mMatrix[mTop + i] = fixedToFloat(m[offset + i]);
    }
  }

  public void glLoadMatrixx(IntBuffer m) {
    for (int i = 0; i < MATRIX_SIZE; i++) {
      mMatrix[mTop + i] = fixedToFloat(m.get());
    }
  }

  public void glMultMatrixf(float[] m, int offset) {
    System.arraycopy(mMatrix, mTop, mTemp, 0, MATRIX_SIZE);
    Matrix.multiplyMM(mMatrix, mTop, mTemp, 0, m, offset);
  }

  public void glMultMatrixf(FloatBuffer m) {
    m.get(mTemp, MATRIX_SIZE, MATRIX_SIZE);
    glMultMatrixf(mTemp, MATRIX_SIZE);
  }

  public void glMultMatrixx(int[] m, int offset) {
    for (int i = 0; i < MATRIX_SIZE; i++) {
      mTemp[MATRIX_SIZE + i] = fixedToFloat(m[offset + i]);
    }
    glMultMatrixf(mTemp, MATRIX_SIZE);
  }

  public void glMultMatrixx(IntBuffer m) {
    for (int i = 0; i < MATRIX_SIZE; i++) {
      mTemp[MATRIX_SIZE + i] = fixedToFloat(m.get());
    }
    glMultMatrixf(mTemp, MATRIX_SIZE);
  }

  public void glOrthof(float left, float right, float bottom, float top,
      float near, float far) {
    Matrix.orthoM(mMatrix, mTop, left, right, bottom, top, near, far);
  }

  public void glOrthox(int left, int right, int bottom, int top, int near,
      int far) {
    glOrthof(fixedToFloat(left), fixedToFloat(right), fixedToFloat(bottom),
        fixedToFloat(top), fixedToFloat(near), fixedToFloat(far));
  }

  public void glPopMatrix() {
    preflight_adjust(-1);
    adjust(-1);
  }

  public void glPushMatrix() {
    preflight_adjust(1);
    System.arraycopy(mMatrix, mTop, mMatrix, mTop + MATRIX_SIZE,
        MATRIX_SIZE);
    adjust(1);
  }

  public void glRotatef(float angle, float x, float y, float z) {
    Matrix.setRotateM(mTemp, 0, angle, x, y, z);
    System.arraycopy(mMatrix, mTop, mTemp, MATRIX_SIZE, MATRIX_SIZE);
    Matrix.multiplyMM(mMatrix, mTop, mTemp, MATRIX_SIZE, mTemp, 0);
  }

  public void glRotatex(int angle, int x, int y, int z) {
    glRotatef(angle, fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
  }

  public void glScalef(float x, float y, float z) {
    Matrix.scaleM(mMatrix, mTop, x, y, z);
  }

  public void glScalex(int x, int y, int z) {
    glScalef(fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
  }

  public void glTranslatef(float x, float y, float z) {
    Matrix.translateM(mMatrix, mTop, x, y, z);
  }

  public void glTranslatex(int x, int y, int z) {
    glTranslatef(fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
  }

  public void getMatrix(float[] dest, int offset) {
    System.arraycopy(mMatrix, mTop, dest, offset, MATRIX_SIZE);
  }

  private float fixedToFloat(int x) {
    return x * (1.0f / 65536.0f);
  }

  private void preflight_adjust(int dir) {
    int newTop = mTop + dir * MATRIX_SIZE;
    if (newTop < 0) {
      throw new IllegalArgumentException("stack underflow");
    }
    if (newTop + MATRIX_SIZE > mMatrix.length) {
      throw new IllegalArgumentException("stack overflow");
    }
  }

  private void adjust(int dir) {
    mTop += dir * MATRIX_SIZE;
  }

  private final static int DEFAULT_MAX_DEPTH = 32;
  private final static int MATRIX_SIZE = 16;
  private float[] mMatrix;
  private int mTop;
  private float[] mTemp;
}

class NumericSprite {
  public NumericSprite() {
    mText = "";
    mLabelMaker = null;
  }

  public void initialize(GL10 gl, Paint paint) {
    int height = roundUpPower2((int) paint.getFontSpacing());
    final float interDigitGaps = 9 * 1.0f;
    int width = roundUpPower2((int) (interDigitGaps + paint
        .measureText(sStrike)));
    mLabelMaker = new LabelMaker(true, width, height);
    mLabelMaker.initialize(gl);
    mLabelMaker.beginAdding(gl);
    for (int i = 0; i < 10; i++) {
      String digit = sStrike.substring(i, i + 1);
      mLabelId[i] = mLabelMaker.add(gl, digit, paint);
      mWidth[i] = (int) Math.ceil(mLabelMaker.getWidth(i));
    }
    mLabelMaker.endAdding(gl);
  }

  public void shutdown(GL10 gl) {
    mLabelMaker.shutdown(gl);
    mLabelMaker = null;
  }

  /**
   * Find the smallest power of two >= the input value. (Doesn't work for
   * negative numbers.)
   */
  private int roundUpPower2(int x) {
    x = x - 1;
    x = x | (x >> 1);
    x = x | (x >> 2);
    x = x | (x >> 4);
    x = x | (x >> 8);
    x = x | (x >> 16);
    return x + 1;
  }

  public void setValue(int value) {
    mText = format(value);
  }

  public void draw(GL10 gl, float x, float y, float viewWidth,
      float viewHeight) {
    int length = mText.length();
    mLabelMaker.beginDrawing(gl, viewWidth, viewHeight);
    for (int i = 0; i < length; i++) {
      char c = mText.charAt(i);
      int digit = c - '0';
      mLabelMaker.draw(gl, x, y, mLabelId[digit]);
      x += mWidth[digit];
    }
    mLabelMaker.endDrawing(gl);
  }

  public float width() {
    float width = 0.0f;
    int length = mText.length();
    for (int i = 0; i < length; i++) {
      char c = mText.charAt(i);
      width += mWidth[c - '0'];
    }
    return width;
  }

  private String format(int value) {
    return Integer.toString(value);
  }

  private LabelMaker mLabelMaker;
  private String mText;
  private int[] mWidth = new int[10];
  private int[] mLabelId = new int[10];
  private final static String sStrike = "0123456789";
}

public class SpriteTextActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mGLSurfaceView = new GLSurfaceView(this);
    mGLSurfaceView.setGLWrapper(new GLSurfaceView.GLWrapper() {
      public GL wrap(GL gl) {
        return new MatrixTrackingGL(gl);
      }
    });
    mGLSurfaceView.setRenderer(new SpriteTextRenderer(this));
    setContentView(mGLSurfaceView);
  }

  @Override
  protected void onPause() {
    super.onPause();
    mGLSurfaceView.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mGLSurfaceView.onResume();
  }

  private GLSurfaceView mGLSurfaceView;
}

   
    
    
  








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 objects
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.