Knowledge is power. We love to share it.

News related to Mono products, services and latest developments in our community.

TProdanovic

OpenGL ES 1.1 for Android

10/08/2012Categories: Mobile

To follow this tutorial you need to install Eclipse and the Android SDK. I am quite happy with the new Eclipse Juno release, considering that previous versions often had problems executing OpenGL apps in the emulator. You can find detailed instructions for installing the SDK at the official Android developer Web - developer.android.com. After installing all required tools you have to configure a virtual device. This example works fine with API level 14. Now let's start with the code.

The main activity

The main activity class in the OpenGL app has some differences to a standard Android application. It is necessary to override the 'onCreate' event and create instances of OpenGL related classes. In this case, we don't use a standard layout as is usual in Android. The setContentView method contains a GLSurfaceView instance instead of a layout XML file. The GLSurfaceView is needed to draw and manipulate objects using the OpenGL API. It is also important to include a renderer.

public void onCreate(Bundle savedInstanceState) {
         
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        GLSurfaceView view = new GLSurfaceView(this);
        renderer = new OpenGLRenderer(this);
        view.setRenderer(renderer);
        setContentView(view);
    }
Graphic objects

This was the only class that shares some similarities with the standard Android development practices. In the next section, I am going to draw a 3D letter 'F' which requires a few more classes. First we need a class that draws a simple square with textures. We will use it to form a 3D cube with properly distributed squares. Finally, the cube can be used to build a 3D model.

The square class is responsible for positioning the vector and the texture at the screen. We define both the vector structure and the texture mapping in float arrays. They get transformed into buffers to be used in other methods. The two methods for drawing and textures contain standard operations to get our object on the screen.

public class Square {
 
    private FloatBuffer vertexBuffer; // buffer holding the vertices
    private float vertices[] = {
            -1.0f, -1.0f, 0.0f,
            -1.0f, 1.0f, 0.0f,
            1.0f, -1.0f, 0.0f,
            1.0f, 1.0f, 0.0f
    };
 
    private FloatBuffer textureBuffer; // buffer holding the texture coordinates
    private float texture[] = {
            // Mapping coordinates for the vertices
            0.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 1.0f,
            1.0f, 0.0f
    };
 
    /** The texture pointer */
    private int[] textures = new int[1];
 
    public Square() {
         
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
 
        // allocates the memory from the byte buffer
        vertexBuffer = byteBuffer.asFloatBuffer();
 
        // fill the vertexBuffer with the vertices
        vertexBuffer.put(vertices);
 
        vertexBuffer.position(0);
 
        byteBuffer = ByteBuffer.allocateDirect(texture.length * 4);
        byteBuffer.order(ByteOrder.nativeOrder());
        textureBuffer = byteBuffer.asFloatBuffer();
        textureBuffer.put(texture);
        textureBuffer.position(0);
    }
 
    public void draw(GL10 gl) {
        // bind the previously generated texture
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
 
        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
 
         
        gl.glFrontFace(GL10.GL_CW);
 
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
 
        // Draw the vertices as triangle strip
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
 
        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    }
 
 
    public void loadGLTexture(GL10 gl, Context context) {
        // loading texture
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.texture_cube1);
 
        gl.glGenTextures(1, textures, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
        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);
 
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();
    }  

http://obviam.net/index.php/texture-mapping-opengl-android-displaying-images-using-opengl-and-squares/

The next step is drawing a cube using the square class. This step is a little easer because we don't have to take care about how to display it on the screen. We just draw six squares (one for every surface) with translations to form a cube. All methods are just transforming the matrix to position the squares. The translate method has three parameters which represent the x,y and z axis. Depending on the parameter value, the object moves in a given direction. The rotate method has a fourth parameter for the rotation angle.
OpenGL has two very useful methods for drawing objects. Those are 'glPushMatrix' and 'glPopMatrix'. The first one remembers the current matrix value or the current coordinate system and puts it on the stack. The popMatrix method returns the latest stack value. Using these methods, we can construct separate models through a series of operations. Now take a look how the cube is constructed using the stack operations.

public class Cube {
 
    public Square square;
 
    public Cube() {
 
        square = new Square();
    }
 
    public void draw(GL10 gl, Context context) {
         
         
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, 1);
 
        // front surface
        square.draw(gl);
 
        // front surface
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, -1);
        gl.glTranslatef(1, 0, 0);
        gl.glRotatef(90, 0, 1, 0);
        square.draw(gl);
        gl.glPopMatrix();
 
        // back surface
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, -2);
        square.draw(gl);
        gl.glPopMatrix();
 
        // left surface
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, -1);
        gl.glTranslatef(-1, 0, 0);
        gl.glRotatef(90, 0, 1, 0);
        square.draw(gl);
        gl.glPopMatrix();
 
        // top surface
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, -1);
        gl.glTranslatef(0, 1, 0);
        gl.glRotatef(90, 1, 0, 0);
        square.draw(gl);
        gl.glPopMatrix();
 
        // bottom surface
        gl.glPushMatrix();
        gl.glTranslatef(0, 0, -1);
        gl.glTranslatef(0, -1, 0);
        gl.glRotatef(90, 1, 0, 0);
        square.draw(gl);
        gl.glPopMatrix();
         
        gl.glPopMatrix();
 
    }

Now we have a cube and we can use it to compose a 3D model. In this case I decided to draw a letter ‘F’ with a rotation animation. As you can see, the following code has a similar structure to the previous class, except the last line which increases the angle by 5. Since the draw method is called for every rendering, we can simulate image rotation by increasing the angle at every step.

public class Logo extends Cube{
 
    public Cube cube;
     
    private int angle;
    private int increase = 5;
     
    public Logo() {
        cube = new Cube();
    }
 
    @Override
    public void draw(GL10 gl, Context context) {
         
        gl.glPushMatrix();
        gl.glTranslatef(5, 0, 0);
        cube.draw(gl, context);
        gl.glTranslatef(0, 2, 0);
        cube.draw(gl, context);
        gl.glTranslatef(0, 2, 0);
        cube.draw(gl, context);
        gl.glTranslatef(0, 1, 0);
        cube.draw(gl, context);
        gl.glTranslatef(0, 3, 0);
        gl.glRotatef(angle, 0, 1, 0);
        cube.draw(gl, context);
        gl.glPopMatrix();
         
        angle+=increase;
         
    }

Now you can see the final result in the picture below. The graphic is fully generated from code and doesn't require any modeling. By following this technique, you can define other base objects as triangles or circles to build more complex graphics. This will be a topic for a future article. For now, we have covered some basics to understand how OpenGL works with Android OS.

Simulation preview
Rated 1.80, 10 vote(s). 
Nice architecture! :)