2
votes

I have the following texture:

enter image description here

I'm trying to get each dice face in the texture to render on each face of the cube in OpenGL ES. However, with my current code, it renders like this:

enter image description here

I know I need to get texture co-ordinates for each face but everytime I try to expand the texture co-ordinates array, it breaks my code.

Ultimately, how do I get each face of the dice in the texture file, to be drawn on its on face on the 3D cube?

Here is the 3D cube object class:

public class Dice3D {
private FloatBuffer vertexBuffer; // Buffer for vertex-array
private FloatBuffer texBuffer;    // Buffer for texture-coords-array (NEW)

private float[] vertices = { // Vertices for a face
        -1.0f, -1.0f, 0.0f,  // 0. left-bottom-front
        1.0f, -1.0f, 0.0f,  // 1. right-bottom-front
        -1.0f,  1.0f, 0.0f,  // 2. left-top-front
        1.0f,  1.0f, 0.0f   // 3. right-top-front
};

float[] texCoords = { // Texture coords for the above face (NEW)
        0.0f, 1.0f,  // A. left-bottom (NEW)
        1.0f, 1.0f,  // B. right-bottom (NEW)
        0.0f, 0.0f,  // C. left-top (NEW)
        1.0f, 0.0f // D. right-top (NEW)
};

int[] textureIDs = new int[1];   // Array for 1 texture-ID (NEW)

// Constructor - Set up the buffers
public Dice3D(Context context) {
    // Setup vertex-array buffer. Vertices in float. An float has 4 bytes
    ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4 * 6);
    vbb.order(ByteOrder.nativeOrder()); // Use native byte order
    vertexBuffer = vbb.asFloatBuffer(); // Convert from byte to float

    for (int face = 0; face < 6; face++) {
        vertexBuffer.put(vertices);
    }

    vertexBuffer.position(0);

    /*vertexBuffer.put(vertices);         // Copy data into buffer
    vertexBuffer.position(0);           // Rewind*/

    // Setup texture-coords-array buffer, in float. An float has 4 bytes (NEW)
    ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4);
    tbb.order(ByteOrder.nativeOrder());
    texBuffer = tbb.asFloatBuffer();
    texBuffer.put(texCoords);
    texBuffer.position(0);
}

// Draw the shape
public void draw(GL10 gl) {
    gl.glFrontFace(GL10.GL_CCW);    // Front face in counter-clockwise orientation
    gl.glEnable(GL10.GL_CULL_FACE); // Enable cull face
    gl.glCullFace(GL10.GL_BACK);    // Cull the back face (don't display)

    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  // Enable texture-coords-array (NEW)
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer); // Define texture-coords buffer (NEW)

    // front
    gl.glPushMatrix();
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // left
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // back
    gl.glPushMatrix();
    gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // right
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // top
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // bottom
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);  // Disable texture-coords-array (NEW)
    gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glDisable(GL10.GL_CULL_FACE);


}

// Load an image into GL texture
public void loadTexture(GL10 gl, Context context) {
    gl.glGenTextures(1, textureIDs, 0); // Generate texture-ID array

    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);   // Bind to texture ID
    // Set up texture filters
    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);

    // Allocate texture buffer
    ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4 * 6);
    tbb.order(ByteOrder.nativeOrder());
    texBuffer = tbb.asFloatBuffer();
    // All the 6 faces have the same texture coords, repeat 6 times
    for (int face = 0; face < 6; face++) {

        Log.i("Face", String.valueOf(face));

        float[] texCoords = {        // Texture coords for the above face (NEW)
                0.0f, (5.0f-face)/6.0f,  // A. left-bottom (NEW)
                1.0f, (5.0f-face)/6.0f,  // B. right-bottom (NEW)
                0.0f, (6.0f-face)/6.0f,  // C. left-top (NEW)
                1.0f, (6.0f-face)/6.0f   // D. right-top (NEW)
        };
        texBuffer.put(texCoords);
    }
    texBuffer.position(0);     // Rewind


    gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer);

    for (int face = 0; face < 6; face++) {
        // Render each face in TRIANGLE_STRIP using 4 vertices
        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, face*4, 4);
    }

    // Construct an input stream to texture image "res\drawable\nehe.png"
    InputStream istream = context.getResources().openRawResource(R.drawable.completetexture);
    Bitmap bitmap;
    try {
        // Read and decode input as bitmap
        bitmap = BitmapFactory.decodeStream(istream);
    } finally {
        try {
            istream.close();
        } catch(IOException e) { }
    }

    // Build Texture from loaded bitmap for the currently-bind texture ID
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
    bitmap.recycle();
}

}

1

1 Answers

1
votes

You have to separated the texture coordinates for each face of the cube. Each part of the cube occupies one-sixth of the texture, along the vertical.

The texture coordiates for the faces are:

  • 1: (0, 5/6) - (1, 6/6)
  • 2: (0, 4/6) - (1, 5/6)
  • 3: (0, 3/6) - (1, 4/6)
  • 4: (0, 2/6) - (1, 3/6)
  • 5: (0, 1/6) - (1, 2/6)
  • 6: (0, 0/6) - (1, 1/6)

This means that you can caluclate the texture coordiantes for a face like this:

for (int face = 0; face < 6; face++) {

    float[] texCoords = {        // Texture coords for the above face (NEW)
        0.0f, (5.0f-face)/6.0f,  // A. left-bottom (NEW)
        1.0f, (5.0f-face)/6.0f,  // B. right-bottom (NEW)
        0.0f, (6.0f-face)/6.0f,  // C. left-top (NEW)
        1.0f, (6.0f-face)/6.0f   // D. right-top (NEW)
    };

    texBuffer.put(texCoords);
}

Of course you also have to add vertex coordinates for each face:

private float[] vertices = { // Vertices for a face
    -1.0f, -1.0f, 0.0f,  // 0. left-bottom-front
     1.0f, -1.0f, 0.0f,  // 1. right-bottom-front
    -1.0f,  1.0f, 0.0f,  // 2. left-top-front
     1.0f,  1.0f, 0.0f   // 3. right-top-front
};

for (int face = 0; face < 6; face++) {
    vertexBuffer.put(vertices);
} 

And you have to draw the proper part of the vertex array for each face:

    // front
    gl.glPushMatrix();
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    gl.glPopMatrix();

    // left
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
    gl.glPopMatrix();

    // back
    gl.glPushMatrix();
    gl.glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
    gl.glPopMatrix();

    // right
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
    gl.glPopMatrix();

    // top
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
    gl.glPopMatrix();

    // bottom
    gl.glPushMatrix();
    gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
    gl.glTranslatef(0.0f, 0.0f, 1.0f);
    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
    gl.glPopMatrix();


Note, you can discard all the matrix rotation and translation operations and you can draw the whole cube with a single call of gl.glDrawArrays, if you generate separate vertex coordinates for each face:

float[] vertices = { // Vertices for a face
    -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   
};

indices [] = {
    0, 1, 2, 3, // front
    4, 0, 6, 2, // left
    5, 4, 7, 6, // back
    1, 5, 3, 7, // right
    2, 3, 6, 7, // top   
    4, 5, 0, 1  // bottom
}

for (int i = 0; i < 24; i++) {
   int vi = indices[i]*3;
   vertexBuffer.put(vertices[vi + 0]);
   vertexBuffer.put(vertices[vi + 1]);
   vertexBuffer.put(vertices[vi + 2]);
}

gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 24);