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

2

15

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();
}
}
Myrmecology answered 14/10, 2011 at 12:10 Comment(1)
I don't get how you would manage to change the image for each cube side as neither in the question or the answer can I see code to indicate more the 1 img file is used, or am I missing something?Aachen
D
12

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.

Davies answered 14/10, 2011 at 18:11 Comment(21)
As a note, glBegin() and glEnd() aren't present in OpenGL ES, so the above pseudocode won't work in this case.Shinar
@BradLarson Ah, you're right. I was a bit confused about the specification containing a section about the Begin/End paradigm (only to say that it is not supported as I found out by looking more thoroughly at it. Sorry, and thanks for pointing it out.Davies
Hi christian, thanks for your help, i tryed to understand how to apply your solution into my code but i can't apply it. I edited my question with my code. My code draws a cube with the same texture on each face, it's from nehe opengl tutorials. I dont understand how to apply your solution into that code, please can you explain it a little more? I supose that my problem is how to make the call to gl.glDrawElements, with my code but adapting it to your solution.Myrmecology
Thanks mate, i'm starting to understand it now, but i have one problem more. How can i put different textures on each Texture[i] element ?Myrmecology
@Pableras84 Huh? Create 6 textures instead of just 1. Keep in mind that all texture commands like glTexParameter and also this texImage2D helper function always work on the currently bound texture object, that can be set using glBindTexture. So just create 6 texture IDs instead of 1 and repeat the procedure in loadGLTexture 6 times.Davies
I've tried to do this too but I just don't see where or how your assigning the different images and I can't figure out how you implement the "for each" and "for code blocks. But if I change - public void draw(GL10 gl, int i) { and gl.glBindTexture(GL10.GL_TEXTURE_2D, (int) texture[i]); and cube.draw(gl, 0); then it displays the cube as a white cube without my imgs which not to mention I don't see how they change since InputStream is = context.getResources().openRawResource(R.drawable.img1); is the only place where imgs get assigned and I don't see where this is changed to find all imgsAachen
I've also tried for(int i = 0; i<6; ++i) { gl.glBindTexture(GL10.GL_TEXTURE_2D, (int) texture[i]); which is 1 of the other options eclipse recommends but then I just just a blank white cube so I rly think that what I'm missing is how the other imgs are loaded.Aachen
@Aachen Sounds like you want to post a question instead of some non-understandable comment tangle?Davies
It's the same code as what Pableras84 used as it seems we're looking at the same sample project insanitydesign.com/wp/projects/nehe-android-ports lesson 6 , I'm sorry if it didn't seem understandable so I hope this helps as that's the exact same project as what I'm trying to implement this with the only code I can see indicating how you assign which img to use being InputStream is = context.getResources().openRawResource(R.drawable.nehe); if I try any of your code it either causes errors which after implementing suggested fixed then it will only display a blank white cube.Aachen
If you want me to make to make another question about this I can but it's the same issue as this question only it's not functioning the same way as Pableras84 must of got it to in order to mark it complete.Aachen
I've tried now to put all the images into an array - public int[] textureArray = { R.drawable.img1, R.drawable.img2, R.drawable.img3, R.drawable.img4, R.drawable.img5, R.drawable.img6, }; and changed InputStream is = context.getResources().openRawResource(textureArray[i]); in the hopes of gettingAachen
for(i = 0; i<6; ++i) { gl.glBindTexture(GL10.GL_TEXTURE_2D, (int) 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); } to work which is slightly altered as this was the only way it would work but then I also had to change a bunch of other things like public void draw(GL10 gl, int i) {Aachen
Or is there any extra info you think you need?Aachen
@Aachen Sorry, I'm just not gonna read those cryptic unformatted comment-questions. I tried it with the first few, but just didn't get the point. I also don't know if the rest of the OP's code works or where he got it from. He asked how to apply a different texture to each face of a cube and I answered how to do it. This answer is completely independent of how he loads any textures and such stuff. If you got problems using his parts of the code (which I don't know anything about), well, ask him or StackOverflow.Davies
I'm still trying to apply a different texture to each face but all I can see from the above code shows the same texture being used rather than many and there is only a single reference to a img being used which is why I wondering exactly how you were assigning different imgs to different faces of the cube. Also all of the code is the same code as was used in the topic question and your answer with just minor modification to try and get it to work on my system. Right now I have 1 img showing on all faces even when I have placed 6 in an array so it would seem the code I'm using to assign eachAachen
image to a different face isn't functioning which if I'm right should just be the added bit to your answer right? If this is the case then what I'm struggling with to get to work is the i assignment as putting that into my code breaks it and it requests a parameter be feed into public void draw(GL10 gl, int i) { in order for it to work and then the gl.glBindTexture(GL10.GL_TEXTURE_2D, (int) texture[i]); to be altered to add (int).Aachen
@Aachen My answer code doesn't show any image loading or such, though it uses different textures (notice the array syntax texture[i]). The only code that contains image loading is the OP's code in the question. This of course only loads one image as it's the question code waiting to be answered. How the OP adapted his posted code using my suggestions is up to him. Don't expect the code of the question to do what the question asks as there wouldn't be any reason to ask a question if it did.Davies
@Aachen Like I said to the OP, if you want your cube to show different textures on each face, you of course also need to load more than one texture, along with my posted adaptions. But what the exact problem of your code is I don't know, as I'm not gonna decipher your code from these unformatted comments above.Davies
I've since also tried out #7356423 which assigned the different textures to have different bitmap images but I was wondering I'm right in my understanding that your edit section of code goes into (loadGLTexture) and if so how did you intend the i value to be passed in?Aachen
@Aachen My code sample (the last one, the real code) of course subsitutes the single glDrawElements call from the original (like said in the answer), it's draw code and, like said, you don't see any texture loading in there. Its up to you to fill the texture array, which is achievable by simply doing the stuff from loadGLTexture multiple times (like said in the comments). Cm'on, you know the difference between texture loading and drawing, don't you? What does a glDrawElements call have to do in a texture loading function? Sorry for the harsh words, but well...Davies
Dunno why I had typed that, guess I guess mixed up with what I was talking about from the link and the issues I was having implementing that section of code in your edit, I tried to both set the textures from an array and by doing it 1 by 1 with the above link in my previous comment. I did find out that there is code explaining how to do this on a chinese site but then I don't speak chinese nor know exactly which site it is.Aachen
S
7

I managed to piece together the answer from Christians, for those interested, here is the full Cube.java code:

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[6];

/** Textures */
private int[] resourceIds = new int[]{
        R.drawable.img1,
        R.drawable.img2,
        R.drawable.img3,
        R.drawable.img4,
        R.drawable.img5,
        R.drawable.img6};

/**
 * 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, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,

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

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

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

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

        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.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) {

    //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);

    for (int i=0;i<6;i++){
        //Bind our only previously generated texture in this case
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);
        indexBuffer.position(6*i);
        //Draw the vertices as triangles, based on the Index Buffer information
        gl.glDrawElements(GL10.GL_TRIANGLES, 6, 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) {

    //Generate a 6 texture pointer...
    gl.glGenTextures(6, textures, 0);

    Bitmap bitmap = null;

    for (int i=0;i<6;i++)
    {
        // Create a bitmap
        bitmap = BitmapFactory.decodeResource(context.getResources(), resourceIds[i]);

        //...and bind it to our array
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[i]);

        //Create Nearest Filtered Texture
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);

        //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 = null;
    }
}
Striated answered 10/1, 2014 at 14:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.