NullPointerException NullPointerException - 6 months ago 38
Android Question

How to fill each side of a cube with different textures on OpenGL ES 1.1?

Please, I need tutorials/code examples of how to fill each side of a cube with different textures on OpenGL ES 1.1

I found a lot of tutorials but none of them explain clearly how to put different textures in each face and none of them gives easy code examples of how to do it.

My actual code (from nehe examples) that draws a cube with the same texture on each face:

public class Cube {

/** The buffer holding the vertices */
private FloatBuffer vertexBuffer;
/** The buffer holding the texture coordinates */
private FloatBuffer textureBuffer;
/** The buffer holding the indices */
private ByteBuffer indexBuffer;

/** Our texture pointer */
private int[] textures = new int[1];

/**
* The initial vertex definition
*
* Note that each face is defined, even
* if indices are available, because
* of the texturing we want to achieve
*/
private float vertices[] = {
//Vertices according to faces
-1.0f, -1.0f, 1.0f, //Vertex 0
1.0f, -1.0f, 1.0f, //v1
-1.0f, 1.0f, 1.0f, //v2
1.0f, 1.0f, 1.0f, //v3

1.0f, -1.0f, 1.0f, //...
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,

1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,

-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,

-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,

-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
};

/** The initial texture coordinates (u, v) */
private float texture[] = {
//Mapping coordinates for the vertices
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f,

};

/** The initial indices definition */
private byte indices[] = {
//Faces definition
0,1,3, 0,3,2, //Face front
4,5,7, 4,7,6, //Face right
8,9,11, 8,11,10, //...
12,13,15, 12,15,14,
16,17,19, 16,19,18,
20,21,23, 20,23,22,
};

/**
* The Cube constructor.
*
* Initiate the buffers.
*/
public Cube() {
//
ByteBuffer byteBuf = ByteBuffer.allocateDirect(vertices.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuf.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

//
byteBuf = ByteBuffer.allocateDirect(texture.length * 4);
byteBuf.order(ByteOrder.nativeOrder());
textureBuffer = byteBuf.asFloatBuffer();
textureBuffer.put(texture);
textureBuffer.position(0);

//
indexBuffer = ByteBuffer.allocateDirect(indices.length);
indexBuffer.put(indices);
indexBuffer.position(0);
}

/**
* The object own drawing function.
* Called from the renderer to redraw this instance
* with possible changes in values.
*
* @param gl - The GL Context
*/
public void draw(GL10 gl) {
//Bind our only previously generated texture in this case
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

//Point to our buffers
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

//Set the face rotation
gl.glFrontFace(GL10.GL_CCW);

//Enable the vertex and texture state
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);

//Draw the vertices as triangles, based on the Index Buffer information
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer);

//Disable the client state before leaving
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}

/**
* Load the textures
*
* @param gl - The GL Context
* @param context - The Activity context
*/
public void loadGLTexture(GL10 gl, Context context) {
//Get the texture from the Android resource directory
InputStream is = context.getResources().openRawResource(R.drawable.nehe);
Bitmap bitmap = null;
try {
//BitmapFactory is an Android graphics utility for images
bitmap = BitmapFactory.decodeStream(is);

} finally {
//Always clear and close
try {
is.close();
is = null;
} catch (IOException e) {
}
}

//Generate one texture pointer...
gl.glGenTextures(1, textures, 0);
//...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);

//Create Nearest Filtered Texture
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);

//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);

//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

//Clean up
bitmap.recycle();
}
}

Answer

To make each face have a different texture, you need to render each face of the cube individually. This means for each face you need to set the texture and then render the face (using glDrawArrays or glDrawElements). So it would look something like:

glEnable(GL_TEXTURE_2D);
...                        //maybe other state setup (like buffer bindings)
glVertexPointer(...);
glEnableClientState(GL_VERTEX_ARRAY);
...

for each(face of cube)
{
    glBindTexture(GL_TEXTURE_2D, <face_texture>);
    glDrawArrays(...) or glDrawElements(...);      //draw only a single face
}

glDisableClientState(GL_VERTEX_ARRAY);
...
glDisable(GL_TEXTURE_2D);
...                         //maybe other state cleanup

You cannot render all the faces of the cube in one call if they need different textures. But you can of course still hold them all in a single array/VBO and just use the arguments to glDrawArrays or glDrawElements to select the corresponding face, like done above.

This is a rather simplified pseudo-code example and if all this sounds very alien to you, you should delve a little deeper into OpenGL and applying a different texture to each face of a cube is your least problem.

EDIT: Ok, according to your updated code: First of all, since all your vertices' positions and texCoords are stored in the same arrays, we don't need to change these per face. Furthermore, your index array seems to contain all faces stored contigously as 6 indices (2 triangles) for each face. All this makes the whole situation very easy. Just replace your existing glDrawElements call with this loop over all faces:

for(i=0; i<6; ++i)
{
    gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[i]);   //use texture of ith face
    indexBuffer.position(6*i);                          //select ith face

    //draw 2 triangles making up this face
    gl.glDrawElements(GL10.GL_TRIANGLES, 6, GL10.GL_UNSIGNED_BYTE, indexBuffer);
}

So for each face we select its texture and draw only the 2 triangles that correspond to this face.

In general when learning from code samples, instead of a book or something similar, you should at least make sure that you understand the meaning of each line of code and each function parameter. Only then you are able to freely adapt the code to your needs and develop solutions for problems.