0
\$\begingroup\$

I'm currently trying to texture a cube, but I am having no luck determining what the next step should be. I can load an .obj model from Blender into my LWJGL program just fine, and I can even make a an image appear from the information. However, I don't know how to use the extra data for textures (or even the normal information). Any help would be greatly appreciated.

This is my OBJLoader:

//Loads .obj files (created from Blender)
public static Model2 loadOBJModel(String name){
    Model2 m = new Model2();
    Material material = null;


    String [] lines = Utilities.readAllLines(name);
    String[] values;
    for(String line : lines){
        values = line.split(" ");
        if(values[0].equals("v")){
            //Adds a new vertex position
            m.getVertices().add(new Vector3f(Float.parseFloat(values[1]),
                                             Float.parseFloat(values[2]),
                                             Float.parseFloat(values[3]))); 
        }
        else if(values[0].equals("vn")){
            //Adds a new normal Vector
            m.getNormals().add(new Vector3f(Float.parseFloat(values[1]),
                                            Float.parseFloat(values[2]),
                                            Float.parseFloat(values[3])));  
        }
        else if(values[0].equals("vt")){
            //Adds a new Texture Coordinate
            m.getTexCoords().add(new Vector2f(Float.parseFloat(values[1]),
                                              Float.parseFloat(values[2])));
            m.setIsTextured(true);
        }
        else if(values[0].equals("f")){
            //Adds the Index of the vertex
            int[] vertexIndice = new int[]{Integer.parseInt(values[1].split("/")[0]) - 1, 
                                           Integer.parseInt(values[2].split("/")[0]) - 1,
                                           Integer.parseInt(values[3].split("/")[0]) - 1};
            //Adds the Index of the normal
            int[] normalIndice = new int[]{Integer.parseInt(values[1].split("/")[2]) - 1, 
                                           Integer.parseInt(values[2].split("/")[2]) - 1,
                                           Integer.parseInt(values[3].split("/")[2]) - 1};
            if(m.getIsTextured()){
                //Adds the Index of the Texture Coordinates
                int[] texIndice = new int[]{Integer.parseInt(values[1].split("/")[1]) - 1, 
                                            Integer.parseInt(values[2].split("/")[1]) - 1,
                                            Integer.parseInt(values[3].split("/")[1]) - 1};
                m.getIndices().add(new Face(vertexIndice, normalIndice, texIndice, material));
            }
            else{
                m.getIndices().add(new Face(vertexIndice, normalIndice, null, material));
            }
        }
        else if(values[0].equals("mtllib")){
            //Loads the material 
            parseMaterial(m, Utilities.getFileInSameLevelOf(name, line.replaceAll("mtllib ",  "").trim()));
        }
        else if(values[0].equals("usemtl")){
            //Sets the material
            material = m.getMaterials().get(line.replaceAll("usemtl ", "").trim());
        }
    }

    m.prepareVBO();

    return m;
}

This is where I get lost... the prepareVBO method

private void prepareVBO(){
    //vertices holds the locations of all the vertices for the object
    Vector3f[] vertexData = new Vector3f[vertices.size()];
    vertices.toArray(vertexData);

    //normals holds the values of all the normal vectors at each vertex
    Vector3f[] normalData = new Vector3f[normals.size()];
    normals.toArray(normalData);

    //texCoords holds the values of all the texture Coordinates 
    Vector2f[] texData = new Vector2f[texCoords.size()];
    texCoords.toArray(texData);

    //this section loads each vertex's/normal's/texCoord's index location
    int[] indexVertexData = new int[indices.size() * 3];
    int[] indexNormalData = new int[indices.size() * 3];
    int[] indexTexData = new int[indices.size() * 3];

    int count = 0;
    for (Face face : indices) {
        indexVertexData[count++] = face.getVertexIndice()[0];
        indexVertexData[count++] = face.getVertexIndice()[1];
        indexVertexData[count++] = face.getVertexIndice()[2];
        count -= 3;
        indexNormalData[count++] = face.getNormalIndice()[0];
        indexNormalData[count++] = face.getNormalIndice()[1];
        indexNormalData[count++] = face.getNormalIndice()[2];
        if(isTextured){
            count -= 3;
            indexTexData[count++] = face.getTexIndice()[0];
            indexTexData[count++] = face.getTexIndice()[1];
            indexTexData[count++] = face.getTexIndice()[2];
        }
    }

    //This is what I do for not using textures nor the normals... I don't know how to utilize the new data
    vboHandler = glGenBuffers();
    iboHandler = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboHandler);
    glBufferData(GL_ARRAY_BUFFER, Utilities.createFlippedBuffer(vertexData), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboHandler);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, Utilities.createFlippedBuffer(indexVertexData), GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

}

Finally, I draw with this:

public void draw(){
    glPushMatrix();
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, vboHandler);
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 3*4, 0);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboHandler);
    glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);
    glPopMatrix();
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

You are on the right track, but there is one problem in the provided code. You have separate indices for positions, normals and texture coordinates. In OpenGL there are only one list of indices, which are used for all vertex attributes. That means that when you are reading the normal and texture coordinate indices, you just need to ignore them or assert that they match the position indices. If they don't match, the vertex data is not compatible with OpenGL and would need reconstruction.

Then how to use these attributes:

vboHandler = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, vboHandler);
glBufferData(GL_ARRAY_BUFFER, Utilities.createFlippedBuffer(vertexData), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);

The code above specifies the position vertex attributes. I recommend to rename vertexData to positionData and vboHandler to vboPositions. Then just duplicate this code for normals and texture coordinates.

glEnableVertexAttribArray(0);

glBindBuffer(GL_ARRAY_BUFFER, vboHandler);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 3*4, 0);

This code sets up the rendering using vertex positions a the attribute with index 0. You can also duplicate that for the normals and texture coordinates, e.g.

glEnableVertexAttribArray(1); // Index 1

glBindBuffer(GL_ARRAY_BUFFER, vboNormals);
glVertexAttribPointer(1, 3, GL_FLOAT, false, 3*4, 0); // Here also index 1

Finally you need to map the vertex attribute indices to the names in the shader code. There are multiple ways for doing that. You can specify them with glBindAttribLocation from the code, use Layout Qualifiers in the vertex shader or let OpenGL allocate them and use glGetAttribLocation to query the indices.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .