Binding multiple textures to object

I’m loading 3D models from the .obj file with my loader class in Qt. It reads lists of vertices, texture coordinates, and faces, then it allocates this data in vertex and index QGLBuffers. Now I want to add materials to the models, so I’ve added a class to load data from the .mtl file to QHash. So far it stores only textures info, but here’s my problem - I can’t figure out how to display multiple textures on the object. It’s not a problem to load a single texture on a whole object, but I can’t grasp the correct way to separate the model’s “parts” and applying the correct textures on each of them. From my understanding, I need to add the check for lines that starts with the ‘o’ tag, parse the vertex, faces and texcord data until the next line with the ‘o’ tag or the end of the file, and then bind arrays to QGLBuffers and activate the correct texture. But is this the right way to do that? I haven’t seen any example of the .obj file loader that using object name as the key, so I have my doubts. Maybe I should work only with texture coordinates instead? I don’t want to rewrite the code and tie it around the object names because currently, my .obj loader class can parse models that don’t have any materials and grouping info, and I want to keep it universal.

The simple way is to divide your buffers, with one VAO per texture. Just bind the correct texture, the matching VAO, and the draw calls.
A more advanced way is to keep a single VAO and use the first and count parameters of DrawArrays, or the count and indices parameters of DrawElements.

But how exactly I can divide my buffers? As I’ve said, I’m currently binding all data from the .obj file in a single vertex array, this how it looks like:

glPushMatrix();
glPushAttrib (GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
{
if (vertex_buffer.create())
{
vertex_buffer.bind();
vertex_buffer.setUsagePattern(QGLBuffer::StaticDraw);
vertex_buffer.release();
}
else qDebug() << “Couldn’t create vertex_buffer”;
index_buffer = new QGLBuffer(QGLBuffer::IndexBuffer);
if (index_buffer&&(index_buffer->create()))
{
index_buffer->bind();
index_buffer->setUsagePattern(QGLBuffer::StaticDraw);
index_buffer->release();
}
glEnable(GL_COLOR_MATERIAL);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
///////////////////////////////////////////////////////////////////////////////////////////parsing the data and enabling textures
vertex_buffer.bind();
vertex_buffer.allocate(points.constData(),points.count()sizeof(Point));
vertex_buffer.release();
index_buffer->bind();
index_buffer->allocate(faces_points_indices.constData(),faces_points_indices.count()sizeof(PointIdxType));
index_buffer->release();
vertex_buffer.bind();
index_buffer->bind();
glVertexPointer(3,GL_FLOAT,sizeof(Point),0);
glNormalPointer(GL_FLOAT,sizeof(Point),(void
)12);
glTexCoordPointer(2,GL_FLOAT,sizeof(Point),(void
)24);
glDrawElements(GL_TRIANGLES,faces_points_indices.count(),GLPointIdxType,0);
index_buffer->release();
vertex_buffer.release();
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
glPopAttrib();
glPopMatrix();

But right now it just appends all coordinates in a single QVector, so from my understanding, I need to somehow divide this data for each part of the object with its own texture. Should I somehow divide buffers when I’m reading the .obj file with QTextStream or do that after I’ve read all the data from the file?

An OBJ file which has multiple objects, groups or materials will have o, g and usemtl statements to partition the faces according to object, group and material respectively. When you read the file, you should take note of the current object, group and material for each f (face) statement. Typically, you’d have a separate vector (QVector, std::vector, etc) of faces for each combination. Once you’ve finished reading the file, you’d concatenate all of those vectors to form the element (index) array, keeping track of where in the array each group starts and ends. So you can render all faces with the same object/group/material with a call to glDrawElements with the indices and count parameters specifying the appropriate region of the element array.

The vertex data (position, normal, texture coordinates) forms a single array. Faces which use different materials or which belong to different groups or even different objects can share vertices. So there’s no reason to partition the vertex data.

1 Like