I started working on the Android platform a few days ago and there were things I liked already. Few clients from around Houston approached Softway Solutions to get some mobile application development done on the IPhone and Android. The application had some 3D aspects. At Softway Solutions we had started prototyping on Android since the early days. So I started working on a prototype that utilizes the OpenGL ES framework. This example is built upon the OpenGL ES samples that come with the Android SDK. So let us begin.
First we start off by creating an opengl texture class. Lets call it GLTexture.java.
[code lang="java"]
public class GLTexture implements ITexture {
/*
* OpenGL ES context for drawing etc....
*/
GL10 mGl;
/*
* ID of the texture created. Its used for OpenGL internal texture binding function.
*/
int mTextureID;
/*
* Name of the texture to identify it if its is placed in a texture cache.
* Helps in reusing a certain texture.
*/
String mName;
/*
* Constructor for creating the texture. Parameters are the Opengl handle, Application Context
* and Android resource id. This is if we want to load the texture from an android application resource.
*/
public GLTexture(GL10 gl, Context context, int resource){
mGl = gl;
surfaceCreated(context, resource);
}
/*
* Constructor for creating the texture. Parameters are the Opengl handle, Application Context
* and the URl of the texture. This is if we want to load the texture from a url on the internet.
*/
public GLTexture(GL10 gl, Context context, String url){
mGl = gl;
createTextureFromUrl(context, url);
}
/*
* Constructor for creating the texture. Parameters are the Opengl handle, Application Context
* and the Bitmap Object. This is if we want to load the texture from an already exisiting bitmap object.
*/
public GLTexture(GL10 gl, Context context, Bitmap bmp) {
setGLParams();
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
}
[/code]
Then the next step is to add the necessary functions to create the Opengl Texture
/*
* This functions sets the basic state for an opengl texture to be created.
*/
private void setGLParams() {
/*
* By default, OpenGL enables features that improve quality
* but reduce performance. One might want to tweak that
* especially on software renderer.
*/
mGl.glDisable(GL10.GL_DITHER);
/*
* Some one-time OpenGL initialization can be made here
* probably based on features of this particular context
*/
mGl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
GL10.GL_FASTEST);
mGl.glClearColor(.5f, .5f, .5f, 1);
mGl.glShadeModel(GL10.GL_SMOOTH);
mGl.glEnable(GL10.GL_DEPTH_TEST);
mGl.glEnable(GL10.GL_TEXTURE_2D);
/*
* Create our texture. This has to be done each time the
* surface is created.
*/
int[] textures = new int[1];
mGl.glGenTextures(1, textures, 0);
mTextureID = textures[0];
mGl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
mGl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
mGl.glTexParameterf(GL10.GL_TEXTURE_2D,
GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_LINEAR);
mGl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
mGl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
mGl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
}
This sets up the OpenGL pipeline to create and bind the texture. Next step would be to have a function which allows us to load the texture data from different sources. Lets first get the texture loaded from a resource.
/*
* This function is called to create a texture from a resource file.
*/
protected void surfaceCreated(Context context, int resource) {
// Sets the OpenGL parameteres
setGLParams();
// Fetches the input stream from the context to get the data from the packed Android resources
InputStream is = context.getResources()
.openRawResource(resource);
Bitmap bitmap;
try {
// Decodes the bitmap stream to give us the final bitmap data. This function also takes care of the filetype for the resource
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch(IOException e) {
// Ignore.
}
}
// Creates the acutal texture.
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Free the memory used by the bitmap object. The texture data has now been passed to the opengl pipeline.
bitmap.recycle();
}
Now we shall create a texture from an internet resource i.e. a texture from a URL.
/*
* This function takes the context and the string address of the texture.
*/
protected void createTextureFromUrl(Context context, String address) {
// Sets the OpenGL parameteres
setGLParams();
Bitmap bitmap = null;
try{
// Creates the URL object to load up the data from the internet.
URL url = new URL(address);
try{
// This function gets the content of the URL. We typecast it to an InputStream Object as that is what will be returned. If the file is missing it will throw an exception which will be caught in the try catch block.
InputStream is = (InputStream)url.getContent();
// Decodes the bitmap stream to give us the final bitmap data. This function also takes care of the filetype for the resource
bitmap = BitmapFactory.decodeStream(is);
}catch(IOException e){
Log.e("Bitmap streaming failed", "failed");
}
}
catch(MalformedURLException e) {
Log.e("malformed url", "failed");
}
// Create the texture
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
// Clean up and release memory used by bitmap object.
bitmap.recycle();
}
Now we shall implement the texture setting function which will apply this texture tothe primitive objects we want to draw.
[code lang="java"]
/*
* This function sets the texture just before drawing a set of primitives.
* This helps in applying textures to certain objects only.
*/
public void setTexture(){
mGl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
mGl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
mGl.glActiveTexture(GL10.GL_TEXTURE0);
mGl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
mGl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_REPEAT);
mGl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_REPEAT);
}
[/code]
Now that our class is created. Lets test it in our test bed application derived from the API Demos from the Android SDK.
public class GLTestBed implements GLSurfaceView.Renderer{
private Context mContext;
private Triangle mTriangle;
// We declare our texture object.
private GLTexture mTexture;
Now we instantiate the texture object in the callback for the OpenGL intialization gets completed.
public void surfaceCreated(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
// We create the texture and pass it the url address of the image we want as the texture.
mTexture = new GLTexture(gl, mContext, "http://farm1.static.flickr.com/168/373377433_da7194d612.jpg?v=0");
mTriangle = new Triangle();
}
We set the texture and call the draw method of our primitive.
public void drawFrame(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_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 90.0f, 0.7f, -5, 100);
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);
// Set the current texture unit to the texture we want to apply
mTexture.setTexture();
// Do some object animation
long upTime = SystemClock.uptimeMillis();
long time = upTime% 4000L;
float angle = 0.090f * ((int) time);
gl.glRotatef(angle, 0, 1.0f, 1.0f);
// Draw the primitive with the texture.
mTriangle.draw(gl);
}
You can find the code for this tutorial here.