opengl-esopengl-es-2.0

How to use a VAO with position, texcoord and normal


I have got a Wavefront OBJ Parser up running but after spending endless hours trying to figure out how I load the data in OpenGL I finally had to give up.

This is how the code is built:

struct ObjMeshVertex{
    Vector3f pos;
    Vector2f texcoord;
    Vector3f normal;
};

struct ObjMeshFace{
    ObjMeshVertex vertices[3];
};

struct ObjMesh{
    std::vector<ObjMeshFace> faces;
};

ObjMesh myMesh;

for(size_t i = 0; i < faces.size(); ++i){
    ObjMeshFace face;
    for(size_t j = 0; j < 3; ++j){
        face.vertices[j].pos        = positions[faces[i].pos_index[j] - 1];
        face.vertices[j].texcoord   = texcoords[faces[i].tex_index[j] - 1];
        face.vertices[j].normal     = normals[faces[i].nor_index[j] - 1];
    }
    myMesh.faces.push_back(face);
}

Normally when I create a simple cube, the array looks something like this: {posX, posY, posZ, normX, normY, normZ}.

And by using the offset with glVertexAttribPointer it's pretty simple to load the data.

But I have no clue how this is done?

EDIT Setup:

glGenVertexArraysOES(1, &_boxVAO);
glBindVertexArrayOES(_boxVAO);

int sizeOfFaces = myMesh.faces.size() * sizeof(ObjMeshFace);
glGenBuffers(1, &_boxBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _boxBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeOfFaces, &(myMesh.faces[0]), GL_STATIC_DRAW);


glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(ObjMeshVertex), 0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(ObjMeshVertex), (void*)offsetof(ObjMeshVertex, texcoord));        
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE ,sizeof(ObjMeshVertex), (void*)offsetof(ObjMeshVertex, normal));

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

glBindVertexArrayOES(0);

Draw:

glBindVertexArrayOES(_boxVAO);
glDrawArrays(GL_TRIANGLES, 0, indicesize);  

Where indicesize = myMesh.faces.size().


Solution

  • If you're just asking how to send the data to glBufferData, then you just get the pointer to the first face in myMesh, and call glBufferData on that (assuming that everything is tightly packed).

    int sizeOfFaces = myMesh.faces.size() * sizeof(ObjMeshFace);
    glBufferData(GL_ARRAY_BUFFER, sizeOfFaces, &(myMesh.faces[0]), usage);
    

    Then you can just set up the pointers to this interleaved structure:

    glVertexPointer(3, GL_FLOAT, sizeof(ObjMeshVertex), 0);
    glTexCoordPointer(2, GL_FLOAT, sizeof(ObjMeshVertex), (void*)(sizeof(Vector3f)));
    glNormalPointer(3, GL_FLOAT, sizeof(ObjMeshVertex), (void*)(sizeof(Vector3f) + sizeof(Vector2f)));
    

    One thing you might need to check is that your structs aren't being padded (I'm less sure about how to enable/disable this).

    Just double check and assert that:

    sizeof(ObjMeshVertex) == 2 * sizeOf(Vector3f) + sizeOf(Vector2f);
    sizeof(ObjMeshFace) == 3 * sizeOf(ObjMeshVertex);
    

    If this isn't true you might need to make some adjustments for struct padding.