0

I've recently been programming an .obj model loader, integrated with C++. So far I've successfully managed to adapt data from a file, acquiring its four attribute types (v, vn, vt and f) in order to set up my vertices and indices for an IBO structure. The problem is, the vertex positions seem to cross over each other more than once, causing a cluster of vertices that scatter within the form of the model.

(Demonstration is shown in the image link below.)

Click me to view the current result!

I've been thoroughly debugging many many times and still can't pin point this problem. Bearing in mind that I'm setting each normal and texture coordinate value to zero (temporarily) for testing purposes. Also, culling is disabled so it simply can't be a normal orientated problem.

Anyhow, getting to the code - I first have a Vertex struct that stores all of the attribute data for positions, normals and tex-coords:

struct  Vertex
    {
        CBfloat v1, v2, v3;
        CBfloat vn1, vn2, vn3;
        CBfloat vt1, vt2;

        Vertex(vec3 _position, vec3 _normal, vec2 _texCoord)
        {
            v1 = _position[0];
            v2 = _position[1];
            v3 = _position[2];
            vn1 = _normal[0];
            vn2 = _normal[1];
            vn3 = _normal[2];
            vt1 = _texCoord[0];
            vt2 = _texCoord[1];
        }
    };

I use these containers to collect all of the data needed to render an IBO:

   std::vector <Vertex>     _vertices;
   std::vector <CBuint>     _indices;

Then, inside my loadOBJ function I retrieve data from the .obj and assign it to four temporary vector lists that contains vertex positions, vertex normals and vertex texture coordinates:

        // Assign vertex data
        std::vector <vec3>  _v;
        std::vector <vec3>  _vn;
        std::vector <vec2>  _vt;
        std::vector <Face>  _f;
        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);
                    _vn.push_back(vec3(x, y, z));
                }
                if ((line[i])[1] == 't')
                {
                    sscanf_s((&line[i])[0].c_str(), "vt %f %f", &x, &y);
                    _vt.push_back(vec2(x, y));
                }
                else if ((line[i])[1] == ' ')
                {
                    sscanf_s((&line[i])[0].c_str(), "v %f %f %f", &x, &y, &z);
                    _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));

                // Assign new material to element
                if (_material_element.size() > 1)
                    _f[_f.size() - 1].mat_id = _material_element.size();

                _material_element.push_back(material_element);
                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]);

                // Faces (IGNORE!)
                _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]));

                // Indices
                _indices.push_back(v_i[0] - 1);
                _indices.push_back(vt_i[0] - 1);
                _indices.push_back(vn_i[0] - 1);

                _indices.push_back(v_i[1] - 1);
                _indices.push_back(vt_i[1] - 1);
                _indices.push_back(vn_i[1] - 1);

                _indices.push_back(v_i[2] - 1);
                _indices.push_back(vt_i[2] - 1);
                _indices.push_back(vn_i[2] - 1);

                break;
            }
        }

Next, I assign the vertices in relation to the number of vertex positions (temporary method for testing purposes):

        // Optimise vertices (TEMP!!)
        for (CBuint i = 0; i < _v.size(); i++)
        {
            if (i <= _vn.size() - 1 && i <= _vt.size() - 1)
                _vertices.push_back(Vertex(_v[i], _vn[i], _vt[i]));
            else if (i > _vn.size() - 1 && i <= _vt.size() - 1)
                _vertices.push_back(Vertex(_v[i], vec3(0.0f, 0.0f, 0.0f), _vt[i]));
            else
                _vertices.push_back(Vertex(_v[i], vec3(0.0f, 0.0f, 0.0f), vec2(0.0f, 0.0f)));
        }

And finally, the buffers. I begin by defining "BUFFER_OFFSET" as:

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

Then set up everything else as follows:

            glGenVertexArrays(1, &_vao);
            glBindVertexArray(_vao);

            // Generate
            glGenBuffers(1, &vbo);
            glGenBuffers(1, &ebo);

            // Bind
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

            // Buffer
            glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(Vertex), &_vertices[0], GL_STATIC_DRAW);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size() * sizeof(CBuint), &_indices[0], GL_STATIC_DRAW);

            // Set attribs
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 3));
            glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 6));

            // Enable attribs
            glEnableVertexAttribArray(0);
            glEnableVertexAttribArray(1);
            glEnableVertexAttribArray(2);

            // Clean up
            glBindVertexArray(0);

Which then of course leads on to the rendering stage:

virtual void render()
    {
        glUniformMatrix4fv(_u_model, 1, GL_FALSE, _model);

        _materials[0]->bind();

        glBindVertexArray(_vao);
        glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
    }

And just for curiosity sake, the cube.obj file to which I'm testing on contains the following source:

v  -0.5000 -0.5000 0.5000
v  -0.5000 -0.5000 -0.5000
v  0.5000 -0.5000 -0.5000
v  0.5000 -0.5000 0.5000
v  -0.5000 0.5000 0.5000
v  0.5000 0.5000 0.5000
v  0.5000 0.5000 -0.5000
v  -0.5000 0.5000 -0.5000
# 8 vertices

vn 0.0000 -1.0000 -0.0000
vn 0.0000 1.0000 -0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
vn -1.0000 0.0000 -0.0000
# 6 vertex normals

vt 1.0000 0.0000 0.0000
vt 1.0000 1.0000 0.0000
vt 0.0000 1.0000 0.0000
vt 0.0000 0.0000 0.0000
# 4 texture coords

g Box001
s 2
f 1/1/1 2/2/1 3/3/1 
f 3/3/1 4/4/1 1/1/1 
s 4
f 5/4/2 6/1/2 7/2/2 
f 7/2/2 8/3/2 5/4/2 
s 8
f 1/4/3 4/1/3 6/2/3 
f 6/2/3 5/3/3 1/4/3 
s 16
f 4/4/4 3/1/4 7/2/4 
f 7/2/4 6/3/4 4/4/4 
s 32
f 3/4/5 2/1/5 8/2/5 
f 8/2/5 7/3/5 3/4/5 
s 64
f 2/4/6 1/1/6 5/2/6 
f 5/2/6 8/3/6 2/4/6 
# 12 faces

If anyone has any ideas on what's causing this vertex / index rendering bug, I'd much appreciate your enlightenment.

Thank you, William.

DrStrange
  • 64
  • 7
  • This is not a duplicate - the "answer" to the other thread does not solve this problem, nor does it hardly relate to the query. – DrStrange Oct 04 '16 at 22:46
  • Yes, it does solve your problem. Your problem is that you're building your indices wrongly, because your code seems to think that every attribute can have a separate index the way that the .OBJ format allows. OpenGL can't do that, so you must deal with this properly in your code. – Nicol Bolas Oct 05 '16 at 22:18

0 Answers0