We seem to be having troubles rendering an EBO using indices from an OBJ model. As shown below, we first load and assign the vertex data by iterating through each key word in the OBJ and replicate them values:
CBint current_material = -1;
std::vector <vec3> temp_v;
std::vector <vec3> temp_vn;
std::vector <vec2> temp_vt;
for (unsigned int i = 0; i < line.size(); i++)
{
switch ((line[i])[0])
{
case '#':
continue;
case '\0':
continue;
case 'm':
char mtl_url[128];
sscanf_s((&line[i])[0].c_str(), "mtllib %s", mtl_url, sizeof(mtl_url));
_mtl_url = mtl_url;
_materialised = true;
break;
case 'v':
float x, y, z;
if ((line[i])[1] == 'n')
{
sscanf_s((&line[i])[0].c_str(), "vn %f %f %f", &x, &y, &z);
temp_vn.push_back(vec3(x, y, z));
}
if ((line[i])[1] == 't')
{
sscanf_s((&line[i])[0].c_str(), "vt %f %f", &x, &y);
temp_vt.push_back(vec2(x, y));
}
else
{
sscanf_s((&line[i])[0].c_str(), "v %f %f %f", &x, &y, &z);
temp_v.push_back(vec3(x, y, z));
}
break;
case 'u':
char material_element[128];
sscanf_s((&line[i])[0].c_str(), "usemtl %s", &material_element, sizeof(material_element));
if (_material_elements.size() > 1)
_f[_f.size() - 1].mat_id = _material_elements.size();
_material_elements.push_back(material_element);
current_material++;
break;
case 'f':
CBuint v_i[3];
CBuint vn_i[3];
CBuint vt_i[3];
sscanf_s((&line[i])[0].c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &v_i[0], &vt_i[0], &vn_i[0], &v_i[1], &vt_i[1], &vn_i[1], &v_i[2], &vt_i[2], &vn_i[2]);
_f.push_back(Face(v_i[0], vt_i[0], vn_i[0], v_i[1], vt_i[1], vn_i[1], v_i[2], vt_i[2], vn_i[2]));
break;
}
Then we simply optimize the indices by iterating through each face whilst iterating through each attribute to which are then assigned to the "_indices" container:
for (CBuint i = 0; i < _f.size(); i++)
{
for (CBuint j = 0; j < 3; j++)
{
CBuint v = _f[i].v[j];
CBuint vt = _f[i].vt[j];
CBuint vn = _f[i].vn[j];
out_v.push_back(temp_v[v - 1]);
out_vt.push_back(temp_vt[vt - 1]);
out_vn.push_back(temp_vn[vn - 1]);
_indices.push_back(v);
_indices.push_back(vt);
_indices.push_back(vn);
}
}
The "_f" is an identifier to a vector list of "Face"s which is a simple struct containing:
CBuint v[3];
CBuint vt[3];
CBuint vn[3];
CBuint mat_id;
This is another struct we use ("Mesh") for creating multiple sub-meshes that are defined by each material detected within the OBJ ("usemtl default__0"). We do this so we can render multiple sub-meshes within a model with their rightful material.
Mesh(std::vector <CBuint>& i, std::vector <vec3>& v, std::vector <vec3>& vn, std::vector <vec2>& vt)
{
glGenBuffers(1, &vbo_v);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo_v);
glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(Math::vec3), &v[0], GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glGenBuffers(1, &vbo_vn);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, vbo_vn);
glBufferData(GL_ARRAY_BUFFER, vn.size() * sizeof(vec3), &vn[0], GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glGenBuffers(1, &vbo_vt);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, vbo_vt);
glBufferData(GL_ARRAY_BUFFER, vt.size() * sizeof(vec2), &vt[0], GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, i.size() * sizeof(CBuint), &i[0], GL_STATIC_DRAW);
}
After optimising the array of indices ("f x/x/x x/x/x x/x/x"), we iterate through each material element and push_back a mesh for each material:
CBuint next_element = 0;
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
if (_materials.size() > 1)
{
for (CBuint i = 0; i < _f.size(); i++)
{
if (_f[i].mat_id == next_element)
{
_meshes.push_back(new Mesh(_indices, out_v, out_vn, out_vt));
next_element++;
}
}
}
else
_meshes.push_back(new Mesh(_indices, out_v, out_vn, out_vt));
glBindVertexArray(0);
Then finally we call glDrawElements instead of glDrawArrays to save 1/3rd of rendering time:
glUniformMatrix4fv(_u_model, 1, GL_FALSE, _model);
_materials[0]->bind();
glBindVertexArray(_vao);
glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_SHORT, 0);
glBindVertexArray(0);
Unfortunately, when using glDrawElements we get the following results:
http://s11.postimg.org/5qbsf2uzn/Untitled_1.jpg
However when using glDrawArrays, the results are perfect:
http://postimg.org/image/7wrq8icn3/
Can anyone tell us what we're doing wrong? Further, here is our vertex shader locations code:
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
If anyone has any idea, we'd appreciate a response. Thank you! DrStrange.