Vertex Arrays, Am I Doing This Right?

Started by
12 comments, last by swiftcoder 16 years, 9 months ago
Hello, I am converting my rendering code over to Vertex Arrays, to improve performance. However, I have encountered a problem, because there is a "This program needs to close" error that comes up after the render code runs once. I've just learned about vertex arrays so I could be doing it completely wrong. I'll show you how I build the arrays, and show you how I am rendering. Look:

Model::Model() : ObjHandle("")
    {
        OutVertexArray = NULL;
        OutNormalArray = NULL;
        OutTexCoordArray = NULL;
    }
    Model::Model(const std::string& file) : ObjHandle(file)
    {
        OutVertexArray = NULL;
        OutNormalArray = NULL;
        OutTexCoordArray = NULL;
        LoadObj(file);
    }
    Model::~Model()
    {
        delete [] OutVertexArray;
        delete [] OutNormalArray;
        delete [] OutTexCoordArray;
        VertexArray.clear();
        NormalArray.clear();
        TexCoordArray.clear();
    }
    void Model::LoadObj(const std::string& file)
    {
        ObjHandle.SetFilename(file);
        ObjLoaderHandle.LoadObj(ObjHandle);
        for(int a = 0; a < ObjHandle.GetNumVertices(); a++)
        {
            VertexArray.push_back(ObjHandle.GetVertex(a));
        }
        for(int b = 0; b < ObjHandle.GetNumNormals(); b++)
        {
            NormalArray.push_back(ObjHandle.GetNormal(b));
        }
        for(int c = 0; c < ObjHandle.GetNumTexCoords(); c++)
        {
            TexCoordArray.push_back(ObjHandle.GetTexCoords(c));
        }
        OutVertexArray = new double[VertexArray.size() * 3];
        OutNormalArray = new double[NormalArray.size() * 3];
        OutTexCoordArray = new double[TexCoordArray.size() * 2];
        int e = 0;
        int f = 0;
        Triangle temptri;
        for(int z = 0; z < ObjHandle.GetNumTriangles(); z++)
        {
            if(VertexArray.size() <= 0)
            {
                temptri = ObjHandle.GetIndices(z);
                OutVertexArray[e] = VertexArray[temptri.Vertices[0]].x;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[0]].x;
                if(TexCoordArray.size() !=0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[0]].x;
                e++;
                f++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[0]].y;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[0]].y;
                if(TexCoordArray.size() !=0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[0]].y;
                e++;
                f++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[0]].z;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[0]].z;
                e++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[1]].x;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[1]].x;
                if(TexCoordArray.size() !=0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[1]].x;
                e++;
                f++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[1]].y;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[1]].y;
                if(TexCoordArray.size() !=0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[1]].y;
                e++;
                f++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[1]].z;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[1]].z;
                e++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[2]].x;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[2]].x;
                if(TexCoordArray.size() != 0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[2]].x;
                e++;
                f++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[2]].y;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[2]].y;
                if(TexCoordArray.size() !=0) OutTexCoordArray[f] = TexCoordArray[temptri.TexCoords[2]].y;
                e++;
                OutVertexArray[e] = VertexArray[temptri.Vertices[2]].z;
                if(NormalArray.size() != 0) OutNormalArray[e] = NormalArray[temptri.Normals[2]].z;
            }
        }
        ModelMaterial = ObjHandle.GetMaterial();
    }
    void Model::Render()
    {
        glPushAttrib(GL_LIGHTING_BIT);
        glMaterialfv(GL_FRONT, GL_AMBIENT, ModelMaterial.Ambient.GetArray());
        glMaterialfv(GL_FRONT, GL_DIFFUSE, ModelMaterial.Diffuse.GetArray());
        glMaterialfv(GL_FRONT, GL_SPECULAR, ModelMaterial.Specular.GetArray());
        glMaterialf(GL_FRONT, GL_SHININESS, ModelMaterial.Shininess);
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        glVertexPointer(3, GL_DOUBLE, 0, OutVertexArray);
        glNormalPointer(GL_DOUBLE, 0, OutNormalArray);
        glTexCoordPointer(2, GL_DOUBLE, 0, OutTexCoordArray);
        glDrawArrays(GL_TRIANGLES, 0, VertexArray.size());
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);
        glDisableClientState(GL_VERTEX_ARRAY);
        glPopAttrib();
    }

Advertisement
Your rendering code is fine, so I would guess your problem lies in the loading code, particularly where you weld the vertices together. Check and make sure that you are ending up with the vertex, normal and texcoord arrays all the same length (otherwise one is going out of bounds).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

ah right, I have 296 Vertices, 588 normals, and 0 tex coords. So that is what is causing the problem. Alright, but since I am loading from an .obj file, what do I do with the extra normals, and also why is there almost 1:2 ration of vertices to normals?
Edit: Ohh, I need the size of OutVertexArray. Hmm, can you find the size of an array like that?
Quote:Original post by Niddles
ah right, I have 296 Vertices, 588 normals, and 0 tex coords. So that is what is causing the problem. Alright, but since I am loading from an .obj file, what do I do with the extra normals, and also why is there almost 1:2 ration of vertices to normals?


Having more normals than vertices is not indicative of a problem. Your problem likely lies elsewhere. I'd suggest stepping through your program with a debugger.

Obj files have their vertices, normals, and texture coordinates indexed starting at 1. Perhaps you have not compensated for that and you are attempting to access a coordinate out of bounds (ie, your max vertex index will be 295, but the obj file calls for vertex 296)?
Quote:Original post by Niddles
ah right, I have 296 Vertices, 588 normals, and 0 tex coords. So that is what is causing the problem. Alright, but since I am loading from an .obj file, what do I do with the extra normals, and also why is there almost 1:2 ration of vertices to normals?


Honestly, I have no idea what you are doing there in your welding stage... it seems hopelessly over-complicated for a simple operation :)
Just loop through each face, as you are already. Then inside that loop through the 3 vertices of the current face, and write out the vertex, normal and texcoord to their respective arrays.

A few cautions:
Indices in .obj files are 1 based (not zero)... this is a common mistake to make so watch out.
.obj files are not necessarily made from triangles, they may be arbitrary n-gons, so make sure you triangulate before exporting from your modeller.
To maximise the performance of vertex arrays, you need to 'interleave' your vertex elements. Allocate a single array, and then place each vertex into the array as a (vertex, normal, texcoord) triple. This takes advantage of cache coherency and can greatly improve render speed with large vertex arrays.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I have with DevC++ debugger, and this is the area where it shows me that there is a problem, with glDrawArrays().
Edit: I always make sure I triangulate, since my code is triangle-based.
And by interleave them, do you mean:
float a[] = {0, 0, 0,      0, 1, 0,    0, 0,              0, 1, 0,      0, 1, 0,    0, 1,              1, 1, 0,      0, 1, 0,    1, 1};//For one triangle? in vertex, normal, texcoord format//And then to do the array do it like so:glEnableClientState(GL_VERTEX_ARRAY | GL_NORMAL_ARRAY | GL_TEXTURE_COORD_ARRAY);glDrawArrays(GL_TRIANGLES, 0, 25);glDisableClientState();

Oh and I switched it so it subtracts one off of the array indices, I had it that way in my previous code, but I forgot to do it there.

On my load code:
I put all of the vertices, normals and texcoords into vectors, because I am planning for Culling, and then I have the out arrays to define what is actually being seen, at the moment I have it all being seen so it seems redundant, but I promise I have a plan for it =).
Ohhh, just found glInterleavedArrays(), converting to that now.
I now have this:
    void Model::LoadObj(const std::string& file)    {        ObjHandle.SetFilename(file);        ObjLoaderHandle.LoadObj(ObjHandle);        for(int a = 0; a < ObjHandle.GetNumVertices(); a++)        {            VertexArray.push_back(ObjHandle.GetVertex(a));        }        for(int b = 0; b < ObjHandle.GetNumNormals(); b++)        {            NormalArray.push_back(ObjHandle.GetNormal(b));        }        for(int c = 0; c < ObjHandle.GetNumTexCoords(); c++)        {            TexCoordArray.push_back(ObjHandle.GetTexCoords(c));        }        int mult = 0;        if(VertexArray.size() > 0)        {            mult+=3;            if(NormalArray.size() > 0) mult+=3;            if(TexCoordArray.size() > 0) mult+=2;            OutVertexArray = new double[ObjHandle.GetNumTriangles() * mult];            int iter = 0;            for(int a=0; a < ObjHandle.GetNumTriangles(); a++)            {                if(TexCoordArray.size() > 0)                {                    OutVertexArray[iter] = ObjHandle.GetTexCoords(ObjHandle.GetIndices(a).TexCoords.x - 1);                    iter++;                    OutVertexArray[iter] = ObjHandle.GetTexCoords(ObjHandle.GetIndices(a).TexCoords.y - 1);                    iter++;                }                if(NormalArray.size() > 0)                {                    OutVertexArray[iter] = ObjHandle.GetNormal(ObjHandle.GetIndices(a).Normals.x - 1);                    iter++;                    OutVertexArray[iter] = ObjHandle.GetNormal(ObjHandle.GetIndices(a).Normals.y - 1);                    iter++;                    OutVertexArray[iter] = ObjHandle.GetNormal(ObjHandle.GetIndices(a).Normals.z - 1);                    iter++;                }                OutVertexArray[iter] = ObjHandle.GetVertex(ObjHandle.GetIndices(a).Vertices.x - 1);                iter++;                OutVertexArray[iter] = ObjHandle.GetVertex(ObjHandle.GetIndices(a).Vertices.y - 1);                iter++;                OutVertexArray[iter] = ObjHandle.GetVertex(ObjHandle.GetIndices(a).Vertices.z - 1);                iter++;            }        }        ModelMaterial = ObjHandle.GetMaterial();    }    void Model::Render()    {        glPushAttrib(GL_LIGHTING_BIT);        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);        glMaterialfv(GL_FRONT, GL_AMBIENT, ModelMaterial.Ambient.GetArray());        glMaterialfv(GL_FRONT, GL_DIFFUSE, ModelMaterial.Diffuse.GetArray());        glMaterialfv(GL_FRONT, GL_SPECULAR, ModelMaterial.Specular.GetArray());        glMaterialf(GL_FRONT, GL_SHININESS, ModelMaterial.Shininess);        glInterleavedArrays(GL_T2F_N3F_V3F, 0, OutVertexArray);        glDrawArrays(GL_TRIANGLES, 0, );        glPopClientAttrib();        glPopAttrib();    }
Hm Interesting ...?
I suspect a bit that lines like these
OutVertexArray[iter] = ObjHandle.GetTexCoords(ObjHandle.GetIndices(a).TexCoords.x - 1);iter++;OutVertexArray[iter] = ObjHandle.GetTexCoords(ObjHandle.GetIndices(a).TexCoords.y - 1);iter++;

are what you actually want.

W/o knowledge of your ObjHandle code, I assume that ObjHandle.GetIndices(a) accesses the line that returns the indices for the a-th (0 based indexing) triangle (presumbly an instance of Triangle class). That structure should present a location index, a normal index and a texture co-ordinate index for each of the 3 vertices used by the triangle, like so:
struct Triangle {   int Locations[3]; // indices of locations for vertices 1,2,3   int Normals[3]; // indices of normals for vertices 1,2,3   int TexCoords[3]; // indices of texture co-ordinates for vertices 1,2,3];

Those indices, if fetched from an .obj file as are, are 1-based.

With that background, I assume what you want to do looks like this (w/o having proven it being workable):
          for(int a=0; a < ObjHandle.GetNumTriangles(); a++)          {              const Triangle& indices = ObjHandle.GetIndices(a);              int index;              for(int b=0; b<3; ++b) // inner iteration over the 3 vertices of the current triangle              {                if(TexCoordArray.size() > 0)                {                    index = indices.TexCoords - 1;                    OutVertexArray[iter] = ObjHandle.GetTexCoords(index).x;                    iter++;                    OutVertexArray[iter] = ObjHandle.GetTexCoords(index).y;                    iter++;                }                if(NormalArray.size() > 0)                {                    index = indices.Normals - 1;                    OutVertexArray[iter] = ObjHandle.GetNormal(index).x;                    iter++;                    OutVertexArray[iter] = ObjHandle.GetNormal(index).y;                    iter++;                    OutVertexArray[iter] = ObjHandle.GetNormal(index).z;                    iter++;                }                index = indices.Locations - 1;                OutVertexArray[iter] = ObjHandle.GetVertex(index).x;                iter++;                OutVertexArray[iter] = ObjHandle.GetVertex(index).y;                iter++;                OutVertexArray[iter] = ObjHandle.GetVertex(index).z;                iter++;              } // for b          } // for a

That code presumbly requires you to adapt the ObjHandle.GetVertex, ObjHandle.GetNormal, and ObjHandle.GetTexCoords routines accordingly.
However, you should consider to post code of the class of ObjHandle, especially the 3 routines above and ObjHandle.GetIndices, of course.

Although you mentioned the Triangle class in your OP, you may also use 3 consecutive index triples as an implicit triangle (what would be closer to how the .obj file stores the indices). In that case my code snippet above has to look different, of course. But nevertheless you have to push NumTriangles * 3 many vertices to the array.


Besides this, you are copying the locations, normals, and texture co-ordinates into arrays (VertexArray, ...) and later use ObjHandle.GetVertex, ... to fetch them again. That seems me a waste of resources.

This topic is closed to new replies.

Advertisement