Android Tutorial - 2D Graphics : OpenGL
Wrapper activity demonstrating the use of GLSurfaceView, a view that uses OpenGL drawing into a dedicated surface.
package app.test; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; /** * A vertex shaded cube. */ class Cube { public Cube() { int one = 0x10000; int vertices[] = { -one, -one, -one, one, -one, -one, one, one, -one, -one, one, -one, -one, -one, one, one, -one, one, one, one, one, -one, one, one, }; int colors[] = { 0, 0, 0, one, one, 0, 0, one, one, one, 0, one, 0, one, 0, one, 0, 0, one, one, one, 0, one, one, one, one, one, one, 0, one, one, one, }; byte indices[] = { 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3, 0, 1, 3, 1, 2 }; // 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(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asIntBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4); cbb.order(ByteOrder.nativeOrder()); mColorBuffer = cbb.asIntBuffer(); mColorBuffer.put(colors); mColorBuffer.position(0); mIndexBuffer = ByteBuffer.allocateDirect(indices.length); mIndexBuffer.put(indices); mIndexBuffer.position(0); } public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CW); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer); } private IntBuffer mVertexBuffer; private IntBuffer mColorBuffer; private ByteBuffer mIndexBuffer; } /** * Render a pair of tumbling cubes. */ class CubeRenderer implements GLSurfaceView.Renderer { public CubeRenderer(boolean useTranslucentBackground) { mTranslucentBackground = useTranslucentBackground; mCube = new Cube(); } public void onDrawFrame(GL10 gl) { /* * 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(); gl.glTranslatef(0, 0, -3.0f); gl.glRotatef(mAngle, 0, 1, 0); gl.glRotatef(mAngle * 0.25f, 1, 0, 0); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); mCube.draw(gl); gl.glRotatef(mAngle * 2.0f, 0, 1, 1); gl.glTranslatef(0.5f, 0.5f, 0.5f); mCube.draw(gl); mAngle += 1.2f; } 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, 1, 10); } 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); if (mTranslucentBackground) { gl.glClearColor(0, 0, 0, 0); } else { gl.glClearColor(1, 1, 1, 1); } gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); } private boolean mTranslucentBackground; private Cube mCube; private float mAngle; } /** * Wrapper activity demonstrating the use of {@link GLSurfaceView}, a view that * uses OpenGL drawing into a dedicated surface. */ public class Test extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create our Preview view and set it as the content of our // Activity mGLSurfaceView = new GLSurfaceView(this); mGLSurfaceView.setRenderer(new CubeRenderer(false)); setContentView(mGLSurfaceView); } @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onPause(); mGLSurfaceView.onPause(); } private GLSurfaceView mGLSurfaceView; }
OpenGL Utils
//package com.akjava.lib.android.opengl; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.opengles.GL10; //import javax.swing.text.html.Option; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.opengl.GLUtils; import android.util.Log; public class OpenGLUtils { public static FloatBuffer allocateFloatBuffer(int capacity){ ByteBuffer vbb = ByteBuffer.allocateDirect(capacity); vbb.order(ByteOrder.nativeOrder()); return vbb.asFloatBuffer(); } public static IntBuffer allocateInttBuffer(int capacity){ ByteBuffer vbb = ByteBuffer.allocateDirect(capacity); vbb.order(ByteOrder.nativeOrder()); return vbb.asIntBuffer(); } public static ShortBuffer allocateShortBuffer(int capacity){ ByteBuffer vbb = ByteBuffer.allocateDirect(capacity); vbb.order(ByteOrder.nativeOrder()); return vbb.asShortBuffer(); } public static void addVertex3f(FloatBuffer buffer,float x,float y,float z){ buffer.put(x); buffer.put(y); buffer.put(z); } public static void addIndex(ShortBuffer buffer,int index1,int index2,int index3){ buffer.put((short) index1); buffer.put((short) index2); buffer.put((short) index3); } public static void addCoord2f(FloatBuffer buffer,float x,float y){ buffer.put(x); buffer.put(y); } public static void addColorf(FloatBuffer buffer,float r,float g,float b,float a){ buffer.put(r); buffer.put(g); buffer.put(b); buffer.put(a); } public static FloatBuffer toFloatBufferPositionZero(float[] values) { ByteBuffer vbb = ByteBuffer.allocateDirect(values.length*4); vbb.order(ByteOrder.nativeOrder()); FloatBuffer buffer=vbb.asFloatBuffer(); buffer.put(values); buffer.position(0); return buffer; } public static ShortBuffer toShortBuffer(short[] values) { ByteBuffer vbb = ByteBuffer.allocateDirect(values.length*2); vbb.order(ByteOrder.nativeOrder()); ShortBuffer buffer=vbb.asShortBuffer(); buffer.put(values); buffer.position(0); return buffer; } public static Bitmap loadBitmap(Context mContext,int id){ Options opt=new Options(); Bitmap bitmap= BitmapFactory.decodeResource(mContext.getResources(), id,opt); System.out.println(bitmap.getConfig()); if(!sizeCheck(bitmap)){ throw new RuntimeException("width or height not 2x size,it make invalid error on OpenGL:"+id); } return bitmap; } private static boolean sizeCheck(Bitmap bitmap){ boolean ret=true; int t=2; int w=bitmap.getWidth(); while(w!=t){ if(w%t==1){ Log.e("glutils w=",w+","+t); return false; } t*=2; if(t>w){ return false; } } t=2; int h=bitmap.getHeight(); while(h!=t){ if(h%t==1){ Log.e("glutils h=",h+","+t); return false; } t*=2; if(t>h){ return false; } } return ret; } /** * this is for resized * GLU.gluOrtho2D (gl,-1f, 1.0f, -1f, 1.0f); * @param x * @param y * @param screenWidth * @param screenHeight * @return */ public static float[] toOpenGLCordinate(float x,float y,int screenWidth,int screenHeight){ float sx=((float)x/screenWidth)*2-1.0f; float sy=((float)y/screenHeight)*2-1.0f; sy*=-1; return new float[]{sx,sy}; } /* * y should *=-1; */ public static float toOpenGLCordinate(float x,int screenWidth){ Log.i("myapp","x="+x+","+screenWidth); float sx=((float)x/screenWidth)*2-1.0f; return sx; } public static float realToVirtialValue(int x,int real,float virtial){ return virtial/real*x; } public static int virtualToRealvalue(float x,int real,float virtial){ //using DecimalFormat make gc return (int) ((float)x/(virtial/real)); } private static FloatBuffer mTextureBuffer; private static FloatBuffer mFVertexBuffer; private static ShortBuffer mIndexBuffer; public static FloatBuffer getBoxTriangleTextureBuffer(){ if(mTextureBuffer==null){ mTextureBuffer=OpenGLUtils.allocateFloatBuffer(4*6*2); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 0.0f); mTextureBuffer.position(0); } return mTextureBuffer; } public static FloatBuffer getBoxTriangleFlipVerticalTextureBuffer(){ if(mTextureBuffer==null){ mTextureBuffer=OpenGLUtils.allocateFloatBuffer(4*6*2); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 1.0f); mTextureBuffer.position(0); } return mTextureBuffer; } public static FloatBuffer getBoxTextureBuffer(){ if(mTextureBuffer==null){ mTextureBuffer=OpenGLUtils.allocateFloatBuffer(4*4*2); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,0.0f, 1.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 0.0f); OpenGLUtils.addCoord2f(mTextureBuffer,1.0f, 1.0f); mTextureBuffer.position(0); } return mTextureBuffer; } public static ShortBuffer getBoxIndexBuffer(){ if(mIndexBuffer==null){ mIndexBuffer=OpenGLUtils.allocateShortBuffer(6*2); mIndexBuffer.put((short)0); mIndexBuffer.put((short)1); mIndexBuffer.put((short)2); mIndexBuffer.put((short)2); mIndexBuffer.put((short)3); mIndexBuffer.put((short)1); mIndexBuffer.position(0); } return mIndexBuffer; } public static FloatBuffer getBoxVertexBuffer(){ if(mFVertexBuffer==null){ mFVertexBuffer=OpenGLUtils.allocateFloatBuffer(4*4*3); OpenGLUtils.addVertex3f(mFVertexBuffer, -1,-1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, -1,1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, 1,-1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, 1,1f,0); mFVertexBuffer.position(0); } return mFVertexBuffer; } public static FloatBuffer getBoxTriangleVertexBuffer(){ if(mFVertexBuffer==null){ mFVertexBuffer=OpenGLUtils.allocateFloatBuffer(4*6*3); OpenGLUtils.addVertex3f(mFVertexBuffer, -1,-1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, -1,1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, 1,-1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, 1,-1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, 1,1f,0); OpenGLUtils.addVertex3f(mFVertexBuffer, -1,1f,0); mFVertexBuffer.position(0); } return mFVertexBuffer; } //TODO ?????????????? /* * bitmap??????????? */ public static void bindTextureImage(GL10 gl,final int id,final Bitmap bitmap){ gl.glBindTexture(GL10.GL_TEXTURE_2D, id); 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.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); } }
An OpenGL ES renderer based on the GLSurfaceView rendering framework.
import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Semaphore; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11Ext; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * An OpenGL ES renderer based on the GLSurfaceView rendering framework. This * class is responsible for drawing a list of renderables to the screen every * frame. It also manages loading of textures and (when VBOs are used) the * allocation of vertex buffer objects. */ public class SimpleGLRenderer implements GLSurfaceView.Renderer { // Specifies the format our textures should be converted to upon load. private static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options(); // An array of things to draw every frame. private GLSprite[] mSprites; // Pre-allocated arrays to use at runtime so that allocation during the // test can be avoided. private int[] mTextureNameWorkspace; private int[] mCropWorkspace; // A reference to the application context. private Context mContext; // Determines the use of vertex arrays. // Determines the use of vertex buffer objects. public SimpleGLRenderer(Context context) { // Pre-allocate and store these objects so we can use them at runtime // without allocating memory mid-frame. mTextureNameWorkspace = new int[1]; mCropWorkspace = new int[4]; // Set our bitmaps to 16-bit, 565 format. sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; mContext = context; } public int[] getConfigSpec() { // We don't need a depth buffer, and don't care about our // color depth. int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 0, EGL10.EGL_NONE }; return configSpec; } public void setSprites(GLSprite[] sprites) { mSprites = sprites; } /** * Changes the vertex mode used for drawing. * * @param useVerts * Specifies whether to use a vertex array. If false, the * DrawTexture extension is used. * @param useHardwareBuffers * Specifies whether to store vertex arrays in main memory or on * the graphics card. Ignored if useVerts is false. */ /** Draws the sprites. */ public void drawFrame(GL10 gl) { if (mSprites != null) { gl.glMatrixMode(GL10.GL_MODELVIEW); for (int x = 0; x < mSprites.length; x++) { mSprites[x].draw(gl); } } } /* Called when the size of the window changes. */ public void sizeChanged(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. */ gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glOrthof(0.0f, width, 0.0f, height, 0.0f, 1.0f); 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.glEnable(GL10.GL_TEXTURE_2D); } /** * Called whenever the surface is created. This happens at startup, and may * be called again at runtime if the device context is lost (the screen goes * to sleep, etc). This function must fill the contents of vram with texture * data and (when using VBOs) hardware vertex arrays. */ public void surfaceCreated(GL10 gl) { /* * 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(0.5f, 0.5f, 0.5f, 1); gl.glShadeModel(GL10.GL_FLAT); gl.glDisable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); /* * 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.glDisable(GL10.GL_LIGHTING); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); if (mSprites != null) { // If we are using hardware buffers and the screen lost context // then the buffer indexes that we recorded previously are now // invalid. Forget them here and recreate them below. // Load our texture and set its texture name on all sprites. // To keep this sample simple we will assume that sprites that share // the same texture are grouped together in our sprite list. A real // app would probably have another level of texture management, // like a texture hash. int lastLoadedResource = -1; int lastTextureId = -1; for (int x = 0; x < mSprites.length; x++) { int resource = mSprites[x].getResourceId(); if (resource != lastLoadedResource) { lastTextureId = loadBitmap(mContext, gl, resource); lastLoadedResource = resource; } mSprites[x].setTextureName(lastTextureId); // mSprites[x].getGrid().generateHardwareBuffers(gl); } } } /** * Called when the rendering thread shuts down. This is a good place to * release OpenGL ES resources. * * @param gl */ public void shutdown(GL10 gl) { if (mSprites != null) { int lastFreedResource = -1; int[] textureToDelete = new int[1]; for (int x = 0; x < mSprites.length; x++) { int resource = mSprites[x].getResourceId(); if (resource != lastFreedResource) { textureToDelete[0] = mSprites[x].getTextureName(); gl.glDeleteTextures(1, textureToDelete, 0); mSprites[x].setTextureName(0); } } } } /** * Loads a bitmap into OpenGL and sets up the common parameters for 2D * texture maps. */ protected int loadBitmap(Context context, GL10 gl, int resourceId) { int textureName = -1; if (context != null && gl != null) { gl.glGenTextures(1, mTextureNameWorkspace, 0); textureName = mTextureNameWorkspace[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName); 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 = context.getResources().openRawResource(resourceId); Bitmap bitmap; try { bitmap = BitmapFactory.decodeStream(is, null, sBitmapOptions); } finally { try { is.close(); } catch (IOException e) { // Ignore. } } GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); mCropWorkspace[0] = 0; mCropWorkspace[1] = bitmap.getHeight(); mCropWorkspace[2] = bitmap.getWidth(); mCropWorkspace[3] = -bitmap.getHeight(); bitmap.recycle(); ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, mCropWorkspace, 0); int error = gl.glGetError(); if (error != GL10.GL_NO_ERROR) { Log.e("SpriteMethodTest", "Texture Load GLError: " + error); } } return textureName; } } /** * Base class defining the core set of information necessary to render (and move * an object on the screen. This is an abstract type and must be derived to add * methods to actually draw (see CanvasSprite and GLSprite). */ abstract class Renderable { // Position. public float x; public float y; public float z; // Velocity. public float velocityX; public float velocityY; public float velocityZ; // Size. public float width; public float height; } /** * This is the OpenGL ES version of a sprite. It is more complicated than the * CanvasSprite class because it can be used in more than one way. This class * can draw using a grid of verts, a grid of verts stored in VBO objects, or * using the DrawTexture extension. */ class GLSprite extends Renderable { // The OpenGL ES texture handle to draw. private int mTextureName; // The id of the original resource that mTextureName is based on. private int mResourceId; // If drawing with verts or VBO verts, the grid object defining those verts. public GLSprite(int resourceId) { super(); mResourceId = resourceId; } public void setTextureName(int name) { mTextureName = name; } public int getTextureName() { return mTextureName; } public void setResourceId(int id) { mResourceId = id; } public int getResourceId() { return mResourceId; } public void draw(GL10 gl) { gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureName); // Draw using the DrawTexture extension. ((GL11Ext) gl).glDrawTexfOES(x, y, z, width, height); } } /** * An implementation of SurfaceView that uses the dedicated surface for * displaying an OpenGL animation. This allows the animation to run in a * separate thread, without requiring that it be driven by the update mechanism * of the view hierarchy. * * The application-specific rendering code is delegated to a GLView.Renderer * instance. */ class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { public GLSurfaceView(Context context) { super(context); init(); } public GLSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU); } public SurfaceHolder getSurfaceHolder() { return mHolder; } public void setGLWrapper(GLWrapper glWrapper) { mGLWrapper = glWrapper; } public void setRenderer(Renderer renderer) { mGLThread = new GLThread(renderer); mGLThread.start(); } public void surfaceCreated(SurfaceHolder holder) { mGLThread.surfaceCreated(); } public void surfaceDestroyed(SurfaceHolder holder) { // Surface will be destroyed when we return mGLThread.surfaceDestroyed(); } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // Surface size or format has changed. This should not happen in this // example. mGLThread.onWindowResize(w, h); } /** * Inform the view that the activity is paused. */ public void onPause() { mGLThread.onPause(); } /** * Inform the view that the activity is resumed. */ public void onResume() { mGLThread.onResume(); } /** * Inform the view that the window focus has changed. */ @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mGLThread.onWindowFocusChanged(hasFocus); } /** * Set an "event" to be run on the GL rendering thread. * * @param r * the runnable to be run on the GL rendering thread. */ public void setEvent(Runnable r) { mGLThread.setEvent(r); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mGLThread.requestExitAndWait(); } // ---------------------------------------------------------------------- public interface GLWrapper { GL wrap(GL gl); } // ---------------------------------------------------------------------- /** * A generic renderer interface. */ public interface Renderer { /** * @return the EGL configuration specification desired by the renderer. */ int[] getConfigSpec(); /** * Surface created. Called when the surface is created. Called when the * application starts, and whenever the GPU is reinitialized. This will * typically happen when the device awakes after going to sleep. Set * your textures here. */ void surfaceCreated(GL10 gl); /** * Called when the rendering thread is about to shut down. This is a * good place to release OpenGL ES resources (textures, buffers, etc). * * @param gl */ void shutdown(GL10 gl); /** * Surface changed size. Called after the surface is created and * whenever the OpenGL ES surface size changes. Set your viewport here. * * @param gl * @param width * @param height */ void sizeChanged(GL10 gl, int width, int height); /** * Draw the current frame. * * @param gl */ void drawFrame(GL10 gl); } /** * An EGL helper class. */ private class EglHelper { public EglHelper() { } /** * Initialize EGL for a given configuration spec. * * @param configSpec */ public void start(int[] configSpec) { /* * Get an EGL instance */ mEgl = (EGL10) EGLContext.getEGL(); /* * Get to the default display. */ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); /* * We can now initialize EGL for that display */ int[] version = new int[2]; mEgl.eglInitialize(mEglDisplay, version); EGLConfig[] configs = new EGLConfig[1]; int[] num_config = new int[1]; mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, num_config); mEglConfig = configs[0]; /* * Create an OpenGL ES context. This must be done only once, an * OpenGL context is a somewhat heavy object. */ mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null); mEglSurface = null; } /* * Create and return an OpenGL surface */ public GL createSurface(SurfaceHolder holder) { /* * The window size has changed, so we need to create a new surface. */ if (mEglSurface != null) { /* * Unbind and destroy the old EGL surface, if there is one. */ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); mEgl.eglDestroySurface(mEglDisplay, mEglSurface); } /* * Create an EGL surface we can render into. */ mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null); /* * Before we can issue GL commands, we need to make sure the context * is current and bound to a surface. */ mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); GL gl = mEglContext.getGL(); if (mGLWrapper != null) { gl = mGLWrapper.wrap(gl); } return gl; } /** * Display the current render surface. * * @return false if the context has been lost. */ public boolean swap() { mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); /* * Always check for EGL_CONTEXT_LOST, which means the context and * all associated data were lost (For instance because the device * went to sleep). We need to sleep until we get a new surface. */ return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST; } public void finish() { if (mEglSurface != null) { mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); mEgl.eglDestroySurface(mEglDisplay, mEglSurface); mEglSurface = null; } if (mEglContext != null) { mEgl.eglDestroyContext(mEglDisplay, mEglContext); mEglContext = null; } if (mEglDisplay != null) { mEgl.eglTerminate(mEglDisplay); mEglDisplay = null; } } EGL10 mEgl; EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLConfig mEglConfig; EGLContext mEglContext; } /** * A generic GL Thread. Takes care of initializing EGL and GL. Delegates to * a Renderer instance to do the actual drawing. * */ class GLThread extends Thread { GLThread(Renderer renderer) { super(); mDone = false; mWidth = 0; mHeight = 0; mRenderer = renderer; setName("GLThread"); } @Override public void run() { /* * When the android framework launches a second instance of an * activity, the new instance's onCreate() method may be called * before the first instance returns from onDestroy(). * * This semaphore ensures that only one instance at a time accesses * EGL. */ try { try { sEglSemaphore.acquire(); } catch (InterruptedException e) { return; } guardedRun(); } catch (InterruptedException e) { // fall thru and exit normally } finally { sEglSemaphore.release(); } } private void guardedRun() throws InterruptedException { mEglHelper = new EglHelper(); /* * Specify a configuration for our opengl session and grab the first * configuration that matches is */ int[] configSpec = mRenderer.getConfigSpec(); mEglHelper.start(configSpec); GL10 gl = null; boolean tellRendererSurfaceCreated = true; boolean tellRendererSurfaceChanged = true; /* * This is our main activity thread's loop, we go until asked to * quit. */ while (!mDone) { /* * Update the asynchronous state (window size) */ int w, h; boolean changed; boolean needStart = false; synchronized (this) { if (mEvent != null) { mEvent.run(); } if (mPaused) { mEglHelper.finish(); needStart = true; } if (needToWait()) { while (needToWait()) { wait(); } } if (mDone) { break; } changed = mSizeChanged; w = mWidth; h = mHeight; mSizeChanged = false; } if (needStart) { mEglHelper.start(configSpec); tellRendererSurfaceCreated = true; changed = true; } if (changed) { gl = (GL10) mEglHelper.createSurface(mHolder); tellRendererSurfaceChanged = true; } if (tellRendererSurfaceCreated) { mRenderer.surfaceCreated(gl); tellRendererSurfaceCreated = false; } if (tellRendererSurfaceChanged) { mRenderer.sizeChanged(gl, w, h); tellRendererSurfaceChanged = false; } if ((w > 0) && (h > 0)) { /* draw a frame here */ mRenderer.drawFrame(gl); /* * Once we're done with GL, we need to call swapBuffers() to * instruct the system to display the rendered frame */ mEglHelper.swap(); } } /* * clean-up everything... */ if (gl != null) { mRenderer.shutdown(gl); } mEglHelper.finish(); } private boolean needToWait() { return (mPaused || (!mHasFocus) || (!mHasSurface) || mContextLost) && (!mDone); } public void surfaceCreated() { synchronized (this) { mHasSurface = true; mContextLost = false; notify(); } } public void surfaceDestroyed() { synchronized (this) { mHasSurface = false; notify(); } } public void onPause() { synchronized (this) { mPaused = true; } } public void onResume() { synchronized (this) { mPaused = false; notify(); } } public void onWindowFocusChanged(boolean hasFocus) { synchronized (this) { mHasFocus = hasFocus; if (mHasFocus == true) { notify(); } } } public void onWindowResize(int w, int h) { synchronized (this) { mWidth = w; mHeight = h; mSizeChanged = true; } } public void requestExitAndWait() { // don't call this from GLThread thread or it is a guaranteed // deadlock! synchronized (this) { mDone = true; notify(); } try { join(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } /** * Queue an "event" to be run on the GL rendering thread. * * @param r * the runnable to be run on the GL rendering thread. */ public void setEvent(Runnable r) { synchronized (this) { mEvent = r; } } public void clearEvent() { synchronized (this) { mEvent = null; } } private boolean mDone; private boolean mPaused; private boolean mHasFocus; private boolean mHasSurface; private boolean mContextLost; private int mWidth; private int mHeight; private Renderer mRenderer; private Runnable mEvent; private EglHelper mEglHelper; } private static final Semaphore sEglSemaphore = new Semaphore(1); private boolean mSizeChanged = true; private SurfaceHolder mHolder; private GLThread mGLThread; private GLWrapper mGLWrapper; }
OpenGL Sprite Text Activity
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; }
OpenGL objects
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(); } }
Demonstrate how to use the OES_texture_cube_map extension, available on some high-end OpenGL ES 1.x GPUs.
package app.test; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL; import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL11; import javax.microedition.khronos.opengles.GL11Ext; import javax.microedition.khronos.opengles.GL11ExtensionPack; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.opengl.GLUtils; import android.os.Bundle; import android.util.Log; /** * Demonstrate how to use the OES_texture_cube_map extension, available on some * high-end OpenGL ES 1.x GPUs. */ public class CubeMapActivity extends Activity { private GLSurfaceView mGLSurfaceView; private class Renderer implements GLSurfaceView.Renderer { private boolean mContextSupportsCubeMap; private Grid mGrid; private int mCubeMapTextureID; private boolean mUseTexGen = false; private float mAngle; public void onDrawFrame(GL10 gl) { checkGLError(gl); if (mContextSupportsCubeMap) { gl.glClearColor(0,0,1,0); } else { // Current context doesn't support cube maps. // Indicate this by drawing a red background. gl.glClearColor(1,0,0,0); } gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); gl.glRotatef(mAngle, 0, 1, 0); gl.glRotatef(mAngle*0.25f, 1, 0, 0); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); checkGLError(gl); if (mContextSupportsCubeMap) { gl.glActiveTexture(GL10.GL_TEXTURE0); checkGLError(gl); gl.glEnable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP); checkGLError(gl); gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mCubeMapTextureID); checkGLError(gl); GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR, GL11ExtensionPack.GL_TEXTURE_GEN_MODE, GL11ExtensionPack.GL_REFLECTION_MAP); checkGLError(gl); gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR); checkGLError(gl); gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL); } checkGLError(gl); mGrid.draw(gl); if (mContextSupportsCubeMap) { gl.glDisable(GL11ExtensionPack.GL_TEXTURE_GEN_STR); } checkGLError(gl); mAngle += 1.2f; } public void onSurfaceChanged(GL10 gl, int width, int height) { checkGLError(gl); gl.glViewport(0, 0, width, height); float ratio = (float) width / height; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); checkGLError(gl); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { checkGLError(gl); // This test needs to be done each time a context is created, // because different contexts may support different extensions. mContextSupportsCubeMap = checkIfContextSupportsCubeMap(gl); mGrid = generateTorusGrid(gl, 60, 60, 3.0f, 0.75f); if (mContextSupportsCubeMap) { int[] cubeMapResourceIds = new int[]{ R.raw.skycubemap0, R.raw.skycubemap1, R.raw.skycubemap2, R.raw.skycubemap3, R.raw.skycubemap4, R.raw.skycubemap5};//jpg files mCubeMapTextureID = generateCubeMap(gl, cubeMapResourceIds); } checkGLError(gl); } private int generateCubeMap(GL10 gl, int[] resourceIds) { checkGLError(gl); int[] ids = new int[1]; gl.glGenTextures(1, ids, 0); int cubeMapTextureId = ids[0]; gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, cubeMapTextureId); gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); for (int face = 0; face < 6; face++) { InputStream is = getResources().openRawResource(resourceIds[face]); Bitmap bitmap; try { bitmap = BitmapFactory.decodeStream(is); } finally { try { is.close(); } catch(IOException e) { Log.e("CubeMap", "Could not decode texture for face " + Integer.toString(face)); } } GLUtils.texImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, bitmap, 0); bitmap.recycle(); } checkGLError(gl); return cubeMapTextureId; } private Grid generateTorusGrid(GL gl, int uSteps, int vSteps, float majorRadius, float minorRadius) { Grid grid = new Grid(uSteps + 1, vSteps + 1); for (int j = 0; j <= vSteps; j++) { double angleV = Math.PI * 2 * j / vSteps; float cosV = (float) Math.cos(angleV); float sinV = (float) Math.sin(angleV); for (int i = 0; i <= uSteps; i++) { double angleU = Math.PI * 2 * i / uSteps; float cosU = (float) Math.cos(angleU); float sinU = (float) Math.sin(angleU); float d = majorRadius+minorRadius*cosU; float x = d*cosV; float y = d*(-sinV); float z = minorRadius * sinU; float nx = cosV * cosU; float ny = -sinV * cosU; float nz = sinU; float length = (float) Math.sqrt(nx*nx + ny*ny + nz*nz); nx /= length; ny /= length; nz /= length; grid.set(i, j, x, y, z, nx, ny, nz); } } grid.createBufferObjects(gl); return grid; } private boolean checkIfContextSupportsCubeMap(GL10 gl) { return checkIfContextSupportsExtension(gl, "GL_OES_texture_cube_map"); } /** * This is not the fastest way to check for an extension, but fine if * we are only checking for a few extensions each time a context is created. * @param gl * @param extension * @return true if the extension is present in the current context. */ private boolean checkIfContextSupportsExtension(GL10 gl, String extension) { String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " "; // The extensions string is padded with spaces between extensions, but not // necessarily at the beginning or end. For simplicity, add spaces at the // beginning and end of the extensions string and the extension string. // This means we can avoid special-case checks for the first or last // extension, as well as avoid special-case checks when an extension name // is the same as the first part of another extension name. return extensions.indexOf(" " + extension + " ") >= 0; } } /** A grid is a topologically rectangular array of vertices. * * This grid class is customized for the vertex data required for this * example. * * The vertex and index data are held in VBO objects because on most * GPUs VBO objects are the fastest way of rendering static vertex * and index data. * */ private static class Grid { // Size of vertex data elements in bytes: final static int FLOAT_SIZE = 4; final static int CHAR_SIZE = 2; // Vertex structure: // float x, y, z; // float nx, ny, nx; final static int VERTEX_SIZE = 6 * FLOAT_SIZE; final static int VERTEX_NORMAL_BUFFER_INDEX_OFFSET = 3; private int mVertexBufferObjectId; private int mElementBufferObjectId; // These buffers are used to hold the vertex and index data while // constructing the grid. Once createBufferObjects() is called // the buffers are nulled out to save memory. private ByteBuffer mVertexByteBuffer; private FloatBuffer mVertexBuffer; private CharBuffer mIndexBuffer; private int mW; private int mH; private int mIndexCount; 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; mVertexByteBuffer = ByteBuffer.allocateDirect(VERTEX_SIZE * size) .order(ByteOrder.nativeOrder()); mVertexBuffer = mVertexByteBuffer.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++, c); mIndexBuffer.put(i++, b); mIndexBuffer.put(i++, b); mIndexBuffer.put(i++, c); mIndexBuffer.put(i++, d); } } } } public void set(int i, int j, float x, float y, float z, float nx, float ny, float nz) { if (i < 0 || i >= mW) { throw new IllegalArgumentException("i"); } if (j < 0 || j >= mH) { throw new IllegalArgumentException("j"); } int index = mW * j + i; mVertexBuffer.position(index * VERTEX_SIZE / FLOAT_SIZE); mVertexBuffer.put(x); mVertexBuffer.put(y); mVertexBuffer.put(z); mVertexBuffer.put(nx); mVertexBuffer.put(ny); mVertexBuffer.put(nz); } public void createBufferObjects(GL gl) { checkGLError(gl); // Generate a the vertex and element buffer IDs int[] vboIds = new int[2]; GL11 gl11 = (GL11) gl; gl11.glGenBuffers(2, vboIds, 0); mVertexBufferObjectId = vboIds[0]; mElementBufferObjectId = vboIds[1]; // Upload the vertex data gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); mVertexByteBuffer.position(0); gl11.glBufferData(GL11.GL_ARRAY_BUFFER, mVertexByteBuffer.capacity(), mVertexByteBuffer, GL11.GL_STATIC_DRAW); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); mIndexBuffer.position(0); gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer.capacity() * CHAR_SIZE, mIndexBuffer, GL11.GL_STATIC_DRAW); // We don't need the in-memory data any more mVertexBuffer = null; mVertexByteBuffer = null; mIndexBuffer = null; checkGLError(gl); } public void draw(GL10 gl) { checkGLError(gl); GL11 gl11 = (GL11) gl; gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertexBufferObjectId); gl11.glVertexPointer(3, GL10.GL_FLOAT, VERTEX_SIZE, 0); gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); gl11.glNormalPointer(GL10.GL_FLOAT, VERTEX_SIZE, VERTEX_NORMAL_BUFFER_INDEX_OFFSET * FLOAT_SIZE); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mElementBufferObjectId); gl11.glDrawElements(GL10.GL_TRIANGLES, mIndexCount, GL10.GL_UNSIGNED_SHORT, 0); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_NORMAL_ARRAY); gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0); gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0); checkGLError(gl); } } static void checkGLError(GL gl) { int error = ((GL10) gl).glGetError(); if (error != GL10.GL_NO_ERROR) { throw new RuntimeException("GLError 0x" + Integer.toHexString(error)); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create our surface view and set it as the content of our // Activity mGLSurfaceView = new GLSurfaceView(this); mGLSurfaceView.setRenderer(new Renderer()); setContentView(mGLSurfaceView); } @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onPause(); mGLSurfaceView.onPause(); } }
Wrapper activity demonstrating the use of {@link GLSurfaceView}, a view that uses OpenGL drawing into a dedicated surface.
package app.test; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.content.Context; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.MotionEvent; /** * Wrapper activity demonstrating the use of {@link GLSurfaceView}, a view that * uses OpenGL drawing into a dedicated surface. * * Shows: + How to redraw in response to user input. */ public class Test extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Create our Preview view and set it as the content of our // Activity mGLSurfaceView = new TouchSurfaceView(this); setContentView(mGLSurfaceView); mGLSurfaceView.requestFocus(); mGLSurfaceView.setFocusableInTouchMode(true); } @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onPause(); mGLSurfaceView.onPause(); } private GLSurfaceView mGLSurfaceView; } /** * A vertex shaded cube. */ class Cube { public Cube() { int one = 0x10000; int vertices[] = { -one, -one, -one, one, -one, -one, one, one, -one, -one, one, -one, -one, -one, one, one, -one, one, one, one, one, -one, one, one, }; int colors[] = { 0, 0, 0, one, one, 0, 0, one, one, one, 0, one, 0, one, 0, one, 0, 0, one, one, one, 0, one, one, one, one, one, one, 0, one, one, one, }; byte indices[] = { 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3, 0, 1, 3, 1, 2 }; // 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(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); mVertexBuffer = vbb.asIntBuffer(); mVertexBuffer.put(vertices); mVertexBuffer.position(0); ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4); cbb.order(ByteOrder.nativeOrder()); mColorBuffer = cbb.asIntBuffer(); mColorBuffer.put(colors); mColorBuffer.position(0); mIndexBuffer = ByteBuffer.allocateDirect(indices.length); mIndexBuffer.put(indices); mIndexBuffer.position(0); } public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CW); gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer); gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer); } private IntBuffer mVertexBuffer; private IntBuffer mColorBuffer; private ByteBuffer mIndexBuffer; } /** * Implement a simple rotation control. * */ class TouchSurfaceView extends GLSurfaceView { public TouchSurfaceView(Context context) { super(context); mRenderer = new CubeRenderer(); setRenderer(mRenderer); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); } @Override public boolean onTrackballEvent(MotionEvent e) { mRenderer.mAngleX += e.getX() * TRACKBALL_SCALE_FACTOR; mRenderer.mAngleY += e.getY() * TRACKBALL_SCALE_FACTOR; requestRender(); return true; } @Override public boolean onTouchEvent(MotionEvent e) { float x = e.getX(); float y = e.getY(); switch (e.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - mPreviousX; float dy = y - mPreviousY; mRenderer.mAngleX += dx * TOUCH_SCALE_FACTOR; mRenderer.mAngleY += dy * TOUCH_SCALE_FACTOR; requestRender(); } mPreviousX = x; mPreviousY = y; return true; } /** * Render a cube. */ private class CubeRenderer implements GLSurfaceView.Renderer { public CubeRenderer() { mCube = new Cube(); } public void onDrawFrame(GL10 gl) { /* * 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(); gl.glTranslatef(0, 0, -3.0f); gl.glRotatef(mAngleX, 0, 1, 0); gl.glRotatef(mAngleY, 1, 0, 0); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); mCube.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, 1, 10); } 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(1, 1, 1, 1); gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); } private Cube mCube; public float mAngleX; public float mAngleY; } private final float TOUCH_SCALE_FACTOR = 180.0f / 320; private final float TRACKBALL_SCALE_FACTOR = 36.0f; private CubeRenderer mRenderer; private float mPreviousX; private float mPreviousY; }
A GLSurfaceView.Renderer that uses the Android-specific android.opengl.GLESXXX static OpenGL ES APIs.
package app.test; import static android.opengl.GLES10.*; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.opengl.GLUtils; import android.os.Bundle; import android.os.SystemClock; /** * A GLSurfaceView.Renderer that uses the Android-specific * android.opengl.GLESXXX static OpenGL ES APIs. The static APIs expose more of * the OpenGL ES features than the javax.microedition.khronos.opengles APIs, and * also provide a programming model that is closer to the C OpenGL ES APIs, * which may make it easier to reuse code and documentation written for the C * OpenGL ES APIs. * */ class StaticTriangleRenderer implements GLSurfaceView.Renderer { public interface TextureLoader { /** * Load a texture into the currently bound OpenGL texture. */ void load(GL10 gl); } public StaticTriangleRenderer(Context context) { init(context, new RobotTextureLoader()); } public StaticTriangleRenderer(Context context, TextureLoader loader) { init(context, loader); } private void init(Context context, TextureLoader loader) { mContext = context; mTriangle = new Triangle(); mTextureLoader = loader; } 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. */ glDisable(GL_DITHER); /* * Some one-time OpenGL initialization can be made here probably based * on features of this particular context */ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glClearColor(.5f, .5f, .5f, 1); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); /* * Create our texture. This has to be done each time the surface is * created. */ int[] textures = new int[1]; glGenTextures(1, textures, 0); mTextureID = textures[0]; glBindTexture(GL_TEXTURE_2D, mTextureID); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); mTextureLoader.load(gl); } 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. */ glDisable(GL_DITHER); glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 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(). */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* * Now we're ready to draw some 3D objects */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, mTextureID); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); glRotatef(angle, 0, 0, 1.0f); mTriangle.draw(gl); } public void onSurfaceChanged(GL10 gl, int w, int h) { glViewport(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; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustumf(-ratio, ratio, -1, 1, 3, 7); } private Context mContext; private Triangle mTriangle; private int mTextureID; private TextureLoader mTextureLoader; private class RobotTextureLoader implements TextureLoader { public void load(GL10 gl) { 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(GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); } } static 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(); // A unit-sided equilateral triangle centered on the origin. float[] coords = { // X, Y, Z -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 }; for (int i = 0; i < VERTS; i++) { for (int j = 0; j < 3; j++) { mFVertexBuffer.put(coords[i * 3 + j] * 2.0f); } } for (int i = 0; i < VERTS; i++) { for (int j = 0; j < 2; j++) { mTexBuffer.put(coords[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) { glFrontFace(GL_CCW); glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer); glEnable(GL_TEXTURE_2D); glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer); glDrawElements(GL_TRIANGLE_STRIP, VERTS, GL_UNSIGNED_SHORT, mIndexBuffer); } private final static int VERTS = 3; private FloatBuffer mFVertexBuffer; private FloatBuffer mTexBuffer; private ShortBuffer mIndexBuffer; } } public class Test extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLView = new GLSurfaceView(this); mGLView.setRenderer(new StaticTriangleRenderer(this)); /* * ImageView imageView = new ImageView(this); * imageView.setImageResource(R.raw.robot); setContentView(imageView); */ setContentView(mGLView); } @Override protected void onPause() { super.onPause(); mGLView.onPause(); } @Override protected void onResume() { super.onResume(); mGLView.onResume(); } private GLSurfaceView mGLView; }
Check for OpenGL ES 2.0 support at runtime, and then use either OpenGL ES 1.0 or OpenGL ES 2.0, as appropriate.
package app.test; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.content.pm.ConfigurationInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLES20; 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; class GLES20TriangleRenderer implements GLSurfaceView.Renderer { public GLES20TriangleRenderer(Context context) { mContext = context; mTriangleVertices = ByteBuffer .allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mTriangleVertices.put(mTriangleVerticesData).position(0); } public void onDrawFrame(GL10 glUnused) { // Ignore the passed-in GL10 interface, and use the GLES20 // class's static methods instead. GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgram); checkGlError("glUseProgram"); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maPosition"); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGlError("glEnableVertexAttribArray maPositionHandle"); GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maTextureHandle"); GLES20.glEnableVertexAttribArray(maTextureHandle); checkGlError("glEnableVertexAttribArray maTextureHandle"); long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); Matrix.setRotateM(mMMatrix, 0, angle, 0, 0, 1.0f); Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); checkGlError("glDrawArrays"); } public void onSurfaceChanged(GL10 glUnused, int width, int height) { // Ignore the passed-in GL10 interface, and use the GLES20 // class's static methods instead. GLES20.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); } public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { // Ignore the passed-in GL10 interface, and use the GLES20 // class's static methods instead. mProgram = createProgram(mVertexShader, mFragmentShader); if (mProgram == 0) { return; } maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); checkGlError("glGetAttribLocation aPosition"); if (maPositionHandle == -1) { throw new RuntimeException( "Could not get attrib location for aPosition"); } maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); checkGlError("glGetAttribLocation aTextureCoord"); if (maTextureHandle == -1) { throw new RuntimeException( "Could not get attrib location for aTextureCoord"); } muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); checkGlError("glGetUniformLocation uMVPMatrix"); if (muMVPMatrixHandle == -1) { throw new RuntimeException( "Could not get attrib location for uMVPMatrix"); } /* * Create our texture. This has to be done each time the surface is * created. */ int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); mTextureID = textures[0]; GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); 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(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); } private int loadShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); if (shader != 0) { GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); int[] compiled = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } private int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } int program = GLES20.glCreateProgram(); if (program != 0) { GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); GLES20.glLinkProgram(program); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } private void checkGlError(String op) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, op + ": glError " + error); throw new RuntimeException(op + ": glError " + error); } } private static final int FLOAT_SIZE_BYTES = 4; private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; private final float[] mTriangleVerticesData = { // X, Y, Z, U, V -1.0f, -0.5f, 0, -0.5f, 0.0f, 1.0f, -0.5f, 0, 1.5f, -0.0f, 0.0f, 1.11803399f, 0, 0.5f, 1.61803399f }; private FloatBuffer mTriangleVertices; private final String mVertexShader = "uniform mat4 uMVPMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec2 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = uMVPMatrix * aPosition;\n" + " vTextureCoord = aTextureCoord;\n" + "}\n"; private final String mFragmentShader = "precision mediump float;\n" + "varying vec2 vTextureCoord;\n" + "uniform sampler2D sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n"; private float[] mMVPMatrix = new float[16]; private float[] mProjMatrix = new float[16]; private float[] mMMatrix = new float[16]; private float[] mVMatrix = new float[16]; private int mProgram; private int mTextureID; private int muMVPMatrixHandle; private int maPositionHandle; private int maTextureHandle; private Context mContext; private static String TAG = "GLES20TriangleRenderer"; } class TriangleRenderer implements GLSurfaceView.Renderer { public TriangleRenderer(Context context) { mContext = context; mTriangle = new Triangle(); } 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(); } 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, 0, -5, 0f, 0f, 0f, 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); long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); gl.glRotatef(angle, 0, 0, 1.0f); mTriangle.draw(gl); } public void onSurfaceChanged(GL10 gl, int w, int h) { gl.glViewport(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, 3, 7); } private Context mContext; private Triangle mTriangle; private int mTextureID; } 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(); // A unit-sided equalateral triangle centered on the origin. float[] coords = { // X, Y, Z -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 }; for (int i = 0; i < VERTS; i++) { for (int j = 0; j < 3; j++) { mFVertexBuffer.put(coords[i * 3 + j] * 2.0f); } } for (int i = 0; i < VERTS; i++) { for (int j = 0; j < 2; j++) { mTexBuffer.put(coords[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); } private final static int VERTS = 3; private FloatBuffer mFVertexBuffer; private FloatBuffer mTexBuffer; private ShortBuffer mIndexBuffer; } /** * This sample shows how to check for OpenGL ES 2.0 support at runtime, and then * use either OpenGL ES 1.0 or OpenGL ES 2.0, as appropriate. */ public class Test extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLSurfaceView = new GLSurfaceView(this); if (detectOpenGLES20()) { // Tell the surface view we want to create an OpenGL ES // 2.0-compatible // context, and set an OpenGL ES 2.0-compatible renderer. mGLSurfaceView.setEGLContextClientVersion(2); mGLSurfaceView.setRenderer(new GLES20TriangleRenderer(this)); } else { // Set an OpenGL ES 1.x-compatible renderer. In a real application // this renderer might approximate the same output as the 2.0 // renderer. mGLSurfaceView.setRenderer(new TriangleRenderer(this)); } setContentView(mGLSurfaceView); } private boolean detectOpenGLES20() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo info = am.getDeviceConfigurationInfo(); return (info.reqGlEsVersion >= 0x20000); } @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() // to take appropriate action when the activity looses focus super.onPause(); mGLSurfaceView.onPause(); } private GLSurfaceView mGLSurfaceView; }