OpenGL ES 2.0 Sample Code

satya - Mon Sep 10 2012 12:44:48 GMT-0400 (Eastern Daylight Time)

This will demonstrate


OpenGL ES 2.0 features and API
How to show a cube
How to texture a cube
How rotate a textured cube
How to apply other model transformations
Order of applying transforms
How to write shaders
  vertex
  fragment
How to load shader source code from assets
How to mix GL views with regular views
How to use buttons to control GL behavior

satya - Mon Sep 10 2012 12:52:20 GMT-0400 (Eastern Daylight Time)

How does Android 2.0 interact with GL


//Create a GLSurfaceView and set it up for 2.0
GLSurfaceView glview = new GLSurfaceView(this);
glView.setEGLConfigChooser(false);
glView.setEGLContextClientVersion(2);

//Get your renderer
glView.setRenderer(new SomeES20Renderer(...));
glView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
//Or use
//setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);

satya - Mon Sep 10 2012 12:55:38 GMT-0400 (Eastern Daylight Time)

Don't forget to do this in your activity: onPause and onResume


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

Of course make sure you check the glView if it is null or not.

satya - Mon Sep 10 2012 12:58:57 GMT-0400 (Eastern Daylight Time)

Here is the base level Renderer interface


public abstract static interface Renderer 
{
  public abstract void onSurfaceCreated(GL10 arg0, EGLConfig arg1);
  public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) ;
  public void onDrawFrame(GL10 gl);
}

satya - Mon Sep 10 2012 13:01:44 GMT-0400 (Eastern Daylight Time)

An abstract renderer: ES20AbstractRenderer.java


//filename: ES20AbstractRenderer.java
/*
 * Responsibilities
 * *****************
 * 1. Load vertex and shader programs from asset files
 * 2. Provide model transformations
 * 3. provide default vertex/shader programs
 * 4. Provide default frustum and camera settings
 * 5. Compile, link, and create a shader program

 * Abstract features
 * ******************
 * 1. allow derived shader programs
 * 2. allow derived draw
 * 3. allow derived figures
 * 4. allow derived frustum/viewing volume settings
 * 5. Act as a base class, if needed, for abstracting textures
 */
public abstract class ES20AbstractRenderer implements Renderer
{
   public static String TAG = "ES20AbstractRenderer";

   //Class level or global variables are usually 
   //unhealthy. Try to minimize them!!
   
   //The target matrix that holds the end result
   //of all model transformations
   private float[] mCurrentModelMatrix = new float[16];

   //A matrix that is a result of setting the camera/eye
   private float[] mVMatrix = new float[16];
   
   //A matrix that is the result of setting the frustum
   private float[] mProjMatrix = new float[16];
   
   //A matrix that is a multiple of current model, view, 
   //and projection matrices.
   private float[] mMVPMatrix = new float[16];

   //GLSL program object with both the shaders compiled, 
   //linked, and attached.
   private int mProgram;
   
   //A handle for the uniform variable identifying the MVP matrix
   private int muMVPMatrixHandle;
   
   //An attribute handle in the vertex shader
   //for passing the vertex arrays.
   private int maPositionHandle;
   
   //Name of the default vertex shader 
   //source code file in the asset directory.
   private static final String DEF_VERTEX_SHADER_FILENAME 
         = "def_vertex_shader.txt";
   
   //Name of the default fragment shader 
   //source code file in the asset directory.
   private static final String DEF_FRAGMENT_SHADER_FILENAME 
         = "def_fragment_shader.txt";
   
   /*
    * This class relies on virtual methods to specialize.
    * Doesn't use construction arguments for specialization. 
    */
   public ES20AbstractRenderer()
   {
      initializeMatrices();
   }
   
   public void initializeMatrices()
   {
      //Set the model matrix to identity
       //Subsequent scaling, rotation, etc will update this
       //in a stateful manner. So starting state matters.
      Matrix.setIdentityM(this.mCurrentModelMatrix, 0);
      
      //Although we use this matrix only once, 
      //it is good to start with a known state.
      Matrix.setIdentityM(mMVPMatrix, 0);
   }
   
   //@Override the interface method of Renderer
   //JDK 1.5 doesn't allow this override tag on ab
   public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) 
   {
         prepareSurface(gl,eglConfig);
   }
   /**
    * 1. Create the GLSL program object by passing vertex
    * and shader code. Derived classes can supply their own shader programs. 
    * 2. Get vertex position hanndle
    * 3. get the uniform mvp matrix handle
    */
   public void prepareSurface(GL10 gl, EGLConfig eglConfig) 
   {
      Log.d(TAG,"preparing surface");
       mProgram = createProgram(
             this.getVertexShaderCodeString(), 
             this.getFragmentShaderCodeString());
       if (mProgram == 0) {
           return;
       }
      Log.d(TAG,"Getting position handle:aPosition");
       //maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
       maPositionHandle = getAttributeHandle("aPosition", "Getting Position Handle");
       
      Log.d(TAG,"Getting matrix handle:uMVPMatrix");
       //muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
       muMVPMatrixHandle = getUniformHandle("uMVPMatrix", "Getting MVP uniform matrix handle");
   }
   
    //Override this method to specify your 
    //your own frustum or the dimensions of a viewing volume.
    protected FrustumDimensions getFrustumDimensions()
    {
       //Get default dimensions in this base class
       return FrustumDimensions.getDefault();
    }
    
    //@Override the interface method of Renderer
    //JDK 1.5 doesn't allow this override tag on absolute methods
    //Based on width and height of the window set the 
    //viewport and the frustum.
    public void onSurfaceChanged(GL10 gl, int w, int h) 
    {
       Log.d(TAG,"surface changed. Setting matrix frustum: projection matrix");
       GLES20.glViewport(0, 0, w, h);
        float ratio = (float) w / h;
        FrustumDimensions fd = this.getFrustumDimensions();
       //Matrix.setIdentityM(mProjMatrix, 0);
        Matrix.frustumM(mProjMatrix, 0, ratio * fd.bottom, ratio * fd.top, 
              fd.bottom, fd.top, fd.near, fd.far);
    }
    
    //@Override the interface method of Renderer
    //JDK 1.5 doesn't allow this override tag on absolute methods
    //1. Set your camera. You can place this method while creating
    //the surface or changing the surface. Or you can choose to
    //vary it during the draw method.
    //2. Do some basic drawing methods like clear the palletee
    //3. Use the program of choice
    public void onDrawFrame(GL10 gl) 
    {
          Log.d(TAG,"set look at matrix: view matrix");
       //Matrix.setIdentityM(mVMatrix, 0);
       Matrix.setLookAtM(mVMatrix, 0, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
       
         Log.d(TAG,"base drawframe");
        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");
        
        //Allow a derived class to setup drawing
        //for further down the chain.
        //the default doesn't do anything.
        preDraw(gl,this.maPositionHandle);
        
        //Real abstract method
        draw(gl,this.maPositionHandle);
    }
    
    
    /*
     * 1. Load vertex shader
     * 2. load fragment shader
     * 3. create program
     * 4. attach shaders
     * 5. link program and return it
     */
    private int createProgram(String vertexSource, String fragmentSource) {
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
        if (vertexShader == 0) {
            return 0;
        }
          Log.d(TAG,"vertex shader created");
        int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
        if (pixelShader == 0) {
            return 0;
        }
        Log.d(TAG,"fragment shader created");
        int program = GLES20.glCreateProgram();
        if (program != 0) {
           Log.d(TAG,"program created");
            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;
    }
    /*
     * Load a given type of shader and check for any errors
     */
    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;
    }
    //Use this method to check and log GL errors 
    protected 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);
        }
    }

    /*
     * The following three methods update the mCurrentModelMatrix
     * with the given model transformation.
     * These are stateful accumulative methods.
     */
    public void trnslate(float x, float y, float z)
    {
       float[] tempModelMatrix = new float[16];       
       Matrix.setIdentityM(tempModelMatrix, 0);
       Matrix.translateM(tempModelMatrix,0,x,y,z);
        Matrix.multiplyMM(this.mCurrentModelMatrix, 0, 
              tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
    }
    public void rotate(float angle, float x, float y, float z)
    {
       float[] tempModelMatrix = new float[16];       
       Matrix.setIdentityM(tempModelMatrix, 0);
       Matrix.rotateM(tempModelMatrix,0,angle,x,y,z);
        Matrix.multiplyMM(this.mCurrentModelMatrix, 0, 
              tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
    }
    public void scale(float xFactor, float yFactor, float zFactor)
    {
       float[] tempModelMatrix = new float[16];       
       Matrix.setIdentityM(tempModelMatrix, 0);
       Matrix.scaleM(tempModelMatrix,0,xFactor,yFactor,zFactor);
        Matrix.multiplyMM(this.mCurrentModelMatrix, 0, 
              tempModelMatrix, 0, this.mCurrentModelMatrix, 0);
    }
    
    /*
     * Calculaute the final model view matrix
     * 1. Order of matrix multiplication is important
     * 2. MVPmatrix = proj * view * model;
     * 3. Setup the MVP matrix in the vertex shader memory
     */
    protected void setupMatrices()
    {
       float[] tempModelMatrix = new float[16];
       Matrix.setIdentityM(tempModelMatrix, 0);
       
        //translate the model combo next
        Matrix.multiplyMM(mMVPMatrix, 0, //matrix and offset 
              mCurrentModelMatrix, 0, 
              tempModelMatrix, 0);
       
       //translate eye coordinates first
        Matrix.multiplyMM(mMVPMatrix, 0, 
              this.mVMatrix, 0, 
              mMVPMatrix, 0);
        
        //Project it: screen coordinates
        Matrix.multiplyMM(mMVPMatrix, 0, 
              mProjMatrix, 0, 
              mMVPMatrix, 0);
        
        //Set the vertex uniform handler representing the MVP matrix
        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, //uniform handle 
              1, //number of uniforms. 1 if it is not an array
              false, //transpose: must be false
              mMVPMatrix, //client matrix memory pointer
              0); //offset
    }
    
    //Override this method to continue the onDrawframe callback
    //from the renderer.
    protected abstract void draw(GL10 gl, int positionHandle);
    
    //Override this to implement preDraw
    //useful for derived classes to specialize pre-draws
    protected void preDraw(GL10 gl, int positionHandle)
    {
       //none for this class
    }

    //Use this method if your intent is to return
    //a default vertex shader.
    public String getDefaultVertexShaderCodeString()
    {
       return this.getStringFromAssetFile(DEF_VERTEX_SHADER_FILENAME);
    }
    
    //Use this method if your intent is to return
    //a default fragment shader.
    public String getDefaultFragmentShaderCodeString()
    {
       return this.getStringFromAssetFile(DEF_FRAGMENT_SHADER_FILENAME);
    }
    
    //Override this method if you want to provide
    //a different vertex shader program.
    protected String getVertexShaderCodeString()
    {
        String vertexShader =
            "uniform mat4 uMVPMatrix;\n" +
            "attribute vec4 aPosition;\n" +
            "void main() {\n" +
            "  gl_Position = uMVPMatrix * aPosition;\n" +
            "}\n";
        return vertexShader;
    }
    
    //Override this method if you want to provide
    //a different vertex shader program.
    //In a derived method call getStringFromAssetFile(filename)
    //to read as string to be returned from here.
    protected String getFragmentShaderCodeString()
    {
        String fragmentShader =
            "void main() {\n" +
            "  gl_FragColor = vec4(0.5, 0.25, 0.5, 1.0);\n" +
            "}\n";
        return fragmentShader;
    }
    
    //How to to read a text file from an asset
    //directory. In this approach you will need to 
    //create your application object and provide a static
    //variable to create the context.
    //See MyApplication implementation to see how this works.
    //Or see http://androidbook.com/item/4224
    public String getStringFromAssetFile(String filename)
    {
       Context ctx = MyApplication.m_appContext; 
       if ( ctx == null)
       {
          throw new RuntimeException("Sorry your app context is null");
       }
       try
       {
         AssetManager am = ctx.getAssets();
         InputStream is = am.open(filename);
         String s = convertStreamToString(is);
         is.close();
         return s;
       }
       catch (IOException x)
       {
          throw new RuntimeException("Sorry not able to read filename:" + filename,x);
       }
    }
    
    //Converting a file stream to a string
    //Optimize as you see fit. This may not be an efficient read
    private String convertStreamToString(InputStream is)
    throws IOException
    {   
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       int i = is.read();
       while (i != -1)
       {
          baos.write(i);
          i = is.read();
       }
       return baos.toString();
    }
    
    //Use this if you need to use a program object
    //directly. Make sure you call this after the 
    //surface is created and prepared in this base class.
    //otherwise it will be null.
    public int getGLSProgramObjectReference() 
    {
       return this.mProgram;
    }
    public int getAttributeHandle(String GLSLAttributeName, String comment)
    {
       String logComment = comment + ":" + GLSLAttributeName;
        //Get texture handle
       Log.d(TAG,comment);
       int attributeHandle = 
          GLES20.glGetAttribLocation(mProgram, GLSLAttributeName);
        checkGlError(logComment);
        if (attributeHandle == -1) {
            throw new RuntimeException(logComment);
        }
       return attributeHandle;
    }
    public int getUniformHandle(String GLSLUniformName, String comment)
    {
       String logComment = comment + ":" + GLSLUniformName;
        //Get texture handle
       Log.d(TAG,comment);
       int uniformHandle = 
          GLES20.glGetUniformLocation(mProgram, GLSLUniformName);
        checkGlError(logComment);
        if (uniformHandle == -1) {
            throw new RuntimeException(logComment);
        }
       return uniformHandle;
    }
}

satya - Mon Sep 10 2012 13:02:40 GMT-0400 (Eastern Daylight Time)

A utility class FrustumDimensions


public class FrustumDimensions 
{
   //These are usually set by figuring out
   //the window ration. Base class may ignore these.
   public float left;
   public float right;
   
   public float bottom;
   public float top;
   
   public float near;
   public float far;
   public FrustumDimensions(float left, float right, 
         float bottom, float top,
         float near, float far) 
   {
      this.left = left;
      this.right = right;
      this.bottom = bottom;
      this.top = top;
      this.near = near;
      this.far = far;
   }
   
   static public FrustumDimensions getDefault()
   {
      return new FrustumDimensions(-1, 1, -1, 1, 3, 7);
   }
   static public FrustumDimensions getMedium()
   {
      return new FrustumDimensions(-1, 1, -2, 2, 3, 15);
   }
}

satya - Mon Sep 10 2012 13:03:45 GMT-0400 (Eastern Daylight Time)

ES20SingleTextureAbstractRenderer: Abstracting the texturing


public abstract class ES20SingleTextureAbstractRenderer
extends ES20AbstractRenderer
{
   public static String TAG = "ES20SingleTextureAbstractRenderer";

   //Handle to the texture attribute in the vertex shader
   private int maTextureHandle;
   //handle to the texture Sampler Uniform variable in the fragment shader
   private int mu2DSamplerTexture;
   
   //Client assigned name of the texture
   int mTextureID;
   //default texture ImageResourceId
   int mDefTextureImageResourceId = R.raw.robot;
   
   public ES20SingleTextureAbstractRenderer1()   {
      super();
   }
   //give out the texture attribute handle
   //if needed.
   protected int getTextureHandle()   {
      return maTextureHandle;
   }
   
   //You can prepare and load your texture here
   //so that you dont have to do this everty time
   //a surface just changed changing the height and width.
   public void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) 
   {
      //Give a chance to have the parent prepare the surface
         super.prepareSurface(gl,eglConfig);
         prepareSurfaceForTexture(gl,eglConfig);
   }
   public void prepareSurfaceForTexture(GL10 gl, EGLConfig eglConfig) 
   {
       //Get texture attribute handle
      Log.d(TAG,"Getting texture handle:aTextureCoordinate");
       maTextureHandle = getAttributeHandle("aTextureCoordinate",
             "Getting Texture handle");
       
       //Get uniform texture handle
      Log.d(TAG,"Getting texture 2D sampler handle");
       mu2DSamplerTexture = getUniformHandle("s_2DtextureSampler",
             "Getting 2D sampler for texture");
       
       this.prepareTexture();
   }

   @Override
    protected void preDraw(GL10 gl, int positionHandle)
   {
      //Call the parent's method first
       super.preDraw(gl, positionHandle);
       
        //texture support
        //Make texture unit 0 as the active texture unit
       //This is the default as well
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        
        //Make texture target 2D and texture name mTextureId
        //as the target texture for the active texture unit
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureID);
        
        //Tell the texture sampler in GLSL that the texture to
        //sample belongs to teh texture unit 0.
        //This is also the default
        GLES20.glUniform1i(mu2DSamplerTexture, 0);
        
    }
   //Get the texture name that is initialized
    //Make sure it is initialized before calling this
   //method.
   public int getTextureID()   {
      return mTextureID;
   }
   //Ultimately this code prepares the
   //texture ID mTextureID, 
   //creates it, and binds it to teh texture target 2D.
    public void prepareTexture()
    {
       //GLES20.glEnable(GLES20.GL_TEXTURE_2D);
        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.glTexParameterf(GLES20.GL_TEXTURE_2D, 
              GLES20.GL_TEXTURE_WRAP_S,
              GLES20.GL_REPEAT);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, 
              GLES20.GL_TEXTURE_WRAP_T,
              GLES20.GL_REPEAT);

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;   // No pre-scaling
         
        int texturImageReourceId = getTextureImageResourceId();
        // Read in the resource
        final Bitmap bitmap = BitmapFactory.decodeResource(
              MyApplication.m_appContext.getResources(), 
              texturImageReourceId, options);        

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }
    
    //override this to give your own texture image resource id
    protected int getTextureImageResourceId()    {
       return this.mDefTextureImageResourceId;
    }
}//eof-main-class

satya - Mon Sep 10 2012 13:05:28 GMT-0400 (Eastern Daylight Time)

A rotating cube that can be controlled with a stop/start command


public class ES20ControlledAnimatedTexturedCubeRenderer
extends ES20SingleTextureAbstractRenderer
{
    //A raw native buffer to hold the point coordinates
   //for the cube.
    private FloatBuffer mFVertexBuffer;
    //A raw native buffer to hold the texture coordinates
    //for the cube.
    private FloatBuffer mFTextureBuffer;
    private static final int FLOAT_SIZE_BYTES = 4;
    
    //variables to control rotation
    //if stopFlag = true stop the rotation
    private boolean stopFlag = false;
    //what is the current angle of rotation
    private float curAngle = 0;
    //At the last stop what was the angle 
    //to restart.
    private float stoppedAtAngle = 0;
    
    //What are the vertex and fragment shader
    //source code files
    private static final String VERTEX_SHADER_FILENAME 
       = "tex_vertex_shader_1.txt";
    private static final String FRAGMENT_SHADER_FILENAME 
       = "tex_fragment_shader_1.txt";
    

//front-face: f1,f2,f3,f4
//starting top-right-anti-clockwise    
//f1(1,1) f2(-1,1) f3(-1,-1) f4(1,-1): z plane 0
    
//back-face: b1,b2,b3,b4
//starting bottom-right-anti-clockwise    
//b1(1,-1) b2(1,1) b3(-1,1) b4(-1,-1) : z plane 2
    private float z = 2.0f;
    private final float[] mTriangleVerticesData = {
          //1. front-triangles
          //f1,f2,f3
          1,1,0,        -1,1,0,          -1,-1,0,
          
          //f3,f4,f1
          -1,-1,0,       1,-1,0,          1,1,0,          
          
          //2. back-triangles
          //b1,b2,b3
          1,-1,z,          1,1,z,          -1,1,z,
          //b3,b4,b1
          -1,1,z,          -1,-1,z,       1,-1,z,
          
          //3. right-triangles
          //b2,f1,f4
          1,1,z,          1,1,0,          1,-1,0,
          //b2,f4,b1
          1,1,z,         1,-1,0,         1,-1,z,
          
          //4. left-triangles
          //b3, f2, b4
          -1,1,z,          -1,1,0,       -1,-1,z,
          //b4 f2 f3
          -1,-1,z,       -1,1,0,       -1,-1,0,
          
          //5. top-triangles
          //b2, b3, f2
          1,1,z,         -1,1,z,      -1,1,0,
          //b2, f2, f1
          1,1,z,         -1,1,0,      1,1,0,
          
          //6. bottom-triangles
          //b1, b4, f3
          1,-1,z,      -1,-1,z,   -1,-1,0,
          //b1, f3, f4
          1,-1,z,      -1,-1,0,   1,-1,0
/*
 */          
            };

    //f1(1,1) f2(0,1) f3(0,0) f4(1,0) 
    //b1(1,0) b2(1,1) b3(0,1) b4(0,0)
    private final float[] mTextureData = {
          //1. front-triangles
          //f1,f2,f3
          1,0,   0,0,   0,1,
          //f3,f4,f1
          0,1,    1,1,   1,0,                      
          
          //2. back-triangles
          //b1,b2,b3
          1,0,   1,1,   0,1,
          //b3,b4,b1
          0,1,   0,0,   1,0,
          
          //3. right-triangles
          //b2,f1,f4
          1,1,   0,1,   0,0,
          //b2,f4,b1
          1,1,   0,0,   1,0,
          
          //4. left-triangles
          //b3, f2, b4
          0,1,    1,1,    0,0,
          //b4 f2 f3
          0,0,    1,1,    1,0,
          
          //5. top-triangles
          //b2, b3, f2
          1,1,    0,1,    0,0,
          //b2, f2, f1
          1,1,    0,0,    1,0,
          
          //6. bottom-triangles
          //b1, b4, f3
          1,1,   0,1,   0,0,
          //b1, f3, f4
          1,1,   0,0,   1,0
          /*          
*/          
            };
    public ES20ControlledAnimatedTexturedCubeRenderer1() 
    {
       //Turn java points to native buffer points
       setupVertexBuffer();
       //Turn java texture points to native buffer texture
       //points.
       setupTextureBuffer();
    }
    
    //Convert to a native buffer
    private void setupTextureBuffer()
    {
       //Allocate and handle texture buffer
        ByteBuffer vbb1 = ByteBuffer.allocateDirect(mTextureData.length
                * FLOAT_SIZE_BYTES);
        vbb1.order(ByteOrder.nativeOrder());
        mFTextureBuffer = vbb1.asFloatBuffer();
        mFTextureBuffer.put(mTextureData);
        mFTextureBuffer.position(0);
    }
    //Convert to a native buffer
    private void setupVertexBuffer()
    {
       //Allocate and handle vertex buffer
        ByteBuffer vbb = ByteBuffer.allocateDirect(mTriangleVerticesData.length
                * FLOAT_SIZE_BYTES);
        vbb.order(ByteOrder.nativeOrder());
        mFVertexBuffer = vbb.asFloatBuffer();
        mFVertexBuffer.put(mTriangleVerticesData);
        mFVertexBuffer.position(0);
    }
    //Trasfer the vertices from the vertex buffer
    //to the shader.
    private void transferVertexPoints(int vertexPositionHandle)
    {
        GLES20.glVertexAttribPointer(
              vertexPositionHandle, //bound address in the vertex shader 
              3, //how may floats for this attribute: x, y, z
              GLES20.GL_FLOAT, //what is type of each attribute? 
              false, // not normalized
                0, //stride
                mFVertexBuffer); //local client pointer to data
        //Check to see if this caused any errors
        checkGlError("glVertexAttribPointer maPosition");
        
        //You have to call this to enable the arrays to be 
        //used by glDrawArrays or glDrawElements.
        GLES20.glEnableVertexAttribArray(vertexPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle");
    }

    //Same as above but for transfering texture attributes
    //Notice how textures and vertices use the same concept
    //of attributes and the same APIs.
    private void transferTexturePoints(int texturePositionHandle)
    {
        GLES20.glVertexAttribPointer(texturePositionHandle, 2, 
              GLES20.GL_FLOAT, false,
                0, mFTextureBuffer);
        checkGlError("glVertexAttribPointer texture array");
        //mFVertexBuffer.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glEnableVertexAttribArray(texturePositionHandle);
        checkGlError("glEnableVertexAttribArray textures");
        //this.bindTextureSamplerToTextureId();
    }
    
    //Drawing operation
    @Override
    protected void draw(GL10 gl, int positionHandle)
    {
       //Hide the hidden surfaces using these APIs
       GLES20.glEnable(GLES20.GL_DEPTH_TEST);
       GLES20.glDepthFunc(GLES20.GL_LESS);
       
       //Transfer vertices to the shader
       transferVertexPoints(positionHandle);
       //Transfer texture points to the shader
       transferTexturePoints(getTextureHandle());

       //Implement rotation from 0 to 360 degrees
       //Stop when asked and restart when the stopFlag
       //is set to false.
       //Decide what the current angle to apply
       //for rotation is.
       if (stopFlag == true)
       {
          //stop rotation
          curAngle = stoppedAtAngle;
       }
       else
       {
            curAngle += 1.0f;
       }
       if (curAngle > 360)
       {
          curAngle = 0;
       }
        
       //Tell the base class to start their
       //matrices to unit matrices.
        this.initializeMatrices();
        
        //The order of these model transformations matter
        //Each model transformation is specified with 
        //respect to the last one, and not the very first.
        
        //Center the cube
        this.trnslate(0,0,-1);
        //Rotate it around y axis
        this.rotate(curAngle, 0,-1,0);
        //Decenter it to where ever you want
        this.trnslate(0,-2,2);
        
        //Go ahead calculate the ModelViewMatrix as 
        //we are done with ALL of our model transformations
        this.setupMatrices();
        
        //Call glDrawArrays to use the vertices and draw
        int vertexCount = mTriangleVerticesData.length/3;
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, //what primitives to use
              0, //at what point to start
              vertexCount); //Starting there how many points to use
        //Check if there are errors
        checkGlError("glDrawArrays");
    }
    
    //Indicate how big of a viewing volume we desire
    //This is just a simple homegrown class
    //@see FrustumDimensions
    @Override
    protected FrustumDimensions getFrustumDimensions() 
    {
       return FrustumDimensions.getMedium();
    }
    //Indicate the fragment shader source code
    @Override
    protected String getFragmentShaderCodeString()
    {
       return this.getStringFromAssetFile(FRAGMENT_SHADER_FILENAME);
    }
    //Give out the vertex shader source code
    @Override
    protected String getVertexShaderCodeString()
    {
       return this.getStringFromAssetFile(VERTEX_SHADER_FILENAME);
       //return this.getDefaultVertexShaderCodeString();
    }
    
    //Stop the rotation. Called by a client
    //on a button click or other user actions.
    public void stop()
    {
       this.stopFlag = true;
       this.stoppedAtAngle = curAngle;
    }
    //Restart the rotation
    public void start()
    {
       this.stopFlag = false;
       this.curAngle = this.stoppedAtAngle;
    }
}

satya - Mon Sep 10 2012 13:06:22 GMT-0400 (Eastern Daylight Time)

Here is how a composite view can be setup


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/shape2"
    android:id="@+id/outerViewId"
    >
<TextView  android:id="@+id/glViewId"
    android:layout_width="match_parent" 
    android:layout_height="0sp" 
    android:layout_weight="3"
    android:text="Place holder for GL"
    android:background="@drawable/shape2"
    />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="0sp"
    android:layout_weight="1"
    android:background="@drawable/shape2"
    android:id="@+id/contrlViewId"
    >
    <Button 
       android:id="@+id/rotateButtonId" 
       android:text="Rotate" 
       android:onClick="stopRotation"
       android:layout_width="wrap_content" 
       android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>

satya - Mon Sep 10 2012 13:08:03 GMT-0400 (Eastern Daylight Time)

Activity class: Here is how you can replace the GL view at run time with a GLSurfaceView


public class ES20GLCompositeActivity1 extends Activity
{
   private static String tag = "ES20GLCompositeActivity1";
   
   GLSurfaceView mTestHarness = null;
   ES20ControlledAnimatedTexturedCubeRenderer1 mRenderer = null;
   
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mRenderer = createRenderer();
        mTestHarness = createGLView(mRenderer);
        setContentView(R.layout.gl_composite);
        
        LinearLayout outerView = 
           (LinearLayout)this.findViewById(R.id.outerViewId);
        View view1 = 
           this.findViewById(R.id.glViewId);
        outerView.removeView(view1);
        outerView.addView(mTestHarness,0);
        
/*        LayoutInflater lif = this.getLayoutInflater();
        lif.inflate(R.layout.gl_composite, null);
*/        
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu){ 
       super.onCreateOptionsMenu(menu);
          //MenuInflater inflater = getMenuInflater(); //from activity
          //inflater.inflate(R.menu.main_menu, menu);
       return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
    {
       return true;
    }
    
    private ES20ControlledAnimatedTexturedCubeRenderer1 
    createRenderer()
    {
       return new ES20ControlledAnimatedTexturedCubeRenderer1();       
    }
    
    private GLSurfaceView createGLView(Renderer renderer)
    {
       GLSurfaceView glsView = new GLSurfaceView(this);
       ViewGroup.LayoutParams p = 
          new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                0,
                3);
       glsView.setLayoutParams(p);
       //glsView.setBackground(R.drawable.shape2);
       //glsView.setBackgroundResource(R.drawable.shape2);
       glsView.setEGLContextClientVersion(2);
      
       
       glsView.setRenderer(renderer);
        //mTestHarness.setRenderer(new GLES20TestRenderer(this));
        //mTestHarness.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
       glsView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
       return glsView;
    }
    @Override
    protected void onResume() 
    {
       super.onPause();
       if (mTestHarness != null)
       {
          mTestHarness.onResume();
       }
    }
    @Override
    protected void onPause() 
    {
       super.onPause();
       if (mTestHarness != null)
       {
          mTestHarness.onPause();
       }
    }
   public void showMessage(String tag, String message)
   {
      String s = tag + ":" + message;
      Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
      toast.show();
      Log.d(tag,message);
   }
   public void stopRotation(View stopButtonView)
   {
      Button stopButton = (Button)stopButtonView;
      String text = stopButton.getText().toString();
      if (text.equalsIgnoreCase("stop"))
      {
         this.mRenderer.stop();
         stopButton.setText("Start");
      }
      else
      {
         this.mRenderer.start();
         stopButton.setText("Stop");
      }
   }
}

satya - Mon Sep 10 2012 13:09:07 GMT-0400 (Eastern Daylight Time)

def_fragment_shader.txt


void main() 
{
   gl_FragColor = vec4(0.5, 0.25, 0.5, 1.0);
}

satya - Mon Sep 10 2012 13:09:35 GMT-0400 (Eastern Daylight Time)

assets/def_vertex_shader.txt


uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
void main() 
{
   gl_Position = uMVPMatrix * aPosition;
}

satya - Mon Sep 10 2012 13:10:05 GMT-0400 (Eastern Daylight Time)

assets/tex_vertex_shader.txt


uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec2 aTextureCoordinate;
varying vec2 v_TextureCoordinate;
void main() 
{
   gl_Position = uMVPMatrix * aPosition;
   v_TextureCoordinate = aTextureCoordinate;
}

satya - Mon Sep 10 2012 13:10:46 GMT-0400 (Eastern Daylight Time)

assets/tex_fragment_shader_1.txt


precision mediump float; //Appears Mandatory for this version of GLSL 
varying vec2 v_TextureCoordinate;
uniform sampler2D s_2DtextureSampler;
void main() 
{
   gl_FragColor = texture2D(s_2DtextureSampler, v_TextureCoordinate);
}