What's wrong with my OBJ Loader Code and a few things explained

Hello,

I am a newbe to OpenGL but have been coding C/C++ for years now and have decided to try my hand at Opengl :slight_smile:

I am currently trying to make my own obj model loader and viewer, have decided to ensure that I also learn as much as possible in regards to OpenGL I want to code the obj loader myself. Unfortunatly I am a bit confused about if I am doing it right and also how I would utilize the data once it is loaded into memory… I have been reading LOADS of documentation on loading wavefront files however all of them seem to use 3rd party loaders and tools to do so and I want to do it all myself…

Here is the class I created that loads and stores the data:

#include <fstream>
#include <stdlib.h>
const unsigned int MAXVERTEXES = 10000;
const unsigned int MAXUVS = 10000;
const unsigned int MAXNORMALS = 10000;
const unsigned int MAXFACES = 100;
const unsigned int SCALER = 10;

class GraphicsStruc_Model
{
private:
    bool IsModelLoaded;
    unsigned int numVertexes, numUVS, numNormals, numFaces;
    float VERTEXES[MAXVERTEXES][2];
    float UVS[MAXUVS][2];
    float NORMALS[MAXNORMALS][2];

    unsigned int VERTEXES_INDEX[MAXFACES][2];
    unsigned int UVS_INDEX[MAXFACES][2];
    unsigned int NORMALS_INDEX[MAXFACES][2];
public:
    GraphicsStruc_Model()
    {
        IsModelLoaded = false;
    }

    float GetVertex(int vertexNum, int xYZ)
    {
        return VERTEXES[vertexNum][xYZ]/SCALER;
    }

    float GetUV(int uvNum, int xYZ)
    {
        return UVS[uvNum][xYZ]/SCALER;
    }

    float GetNormal(int normalNum, int xYZ)
    {
        return NORMALS[normalNum][xYZ]/SCALER;
    }

    unsigned int GetFaces_Verticies(int faceNum, int xYZ)
    {
        return VERTEXES_INDEX[faceNum][xYZ];
    }

    unsigned int GetFaces_Uvs(int faceNum, int xYZ)
    {
        return UVS_INDEX[faceNum][xYZ];
    }

    unsigned int GetFaces_Normals(int faceNum, int xYZ)
    {
        return NORMALS_INDEX[faceNum][xYZ];
    }

    bool GetIsModelLoaded()
    {
        return IsModelLoaded;
    }

    unsigned int GetNumberVertextes()
    {
       return numVertexes;
    }

    unsigned int GetNumberNormals()
    {
       return numNormals;
    }

    unsigned int GetNumberFaces()
    {
        return numFaces;
    }

    bool LoadModel(char* modelLocation)
    {

        std::ifstream fileHandle;
        std::string readStr;
        unsigned int index, CountN, CountM, vertexCount, uvCount, normalCount, faceCount;

        fileHandle.open(modelLocation);

        if (fileHandle)
        {
            CountN = vertexCount = uvCount = normalCount = 0;
            while (std::getline(fileHandle, readStr))
            {
                //Load Vertexes first
                if (readStr.substr(0, 2) == "v ")   //Vertexes
                {
                    readStr.erase(0,2);

                    CountN = 0;
                    while (CountN < 3)
                    {
                        if (CountN == 2)
                            index = readStr.length();
                        else
                            index = readStr.find(" ", 0);

                        VERTEXES[vertexCount][CountN] = atof(readStr.substr(0, index).c_str());
                        readStr.erase(0, index+1);
                        ++CountN;
                    }
                    ++vertexCount;
                }
                else if (readStr.substr(0,2) == "vt")   //UVS
                {
                    readStr.erase(0,2);

                    CountN = 0;
                    while (CountN < 3)
                    {
                        if (CountN == 2)
                            index = readStr.length();
                        else
                            index = readStr.find(" ", 0);

                        UVS[uvCount][CountN] = atof(readStr.substr(0, index).c_str());
                        readStr.erase(0, index+1);
                        ++CountN;
                    }
                    ++uvCount;
                }
                else if (readStr.substr(0,2) == "vn")   //NORMALS
                {
                    readStr.erase(0,2);

                    CountN = 0;
                    while (CountN < 3)
                    {
                        if (CountN == 2)
                            index = readStr.length();
                        else
                            index = readStr.find(" ", 0);

                        NORMALS[normalCount][CountN] = atof(readStr.substr(0, index).c_str());
                        readStr.erase(0, index+1);
                        ++CountN;
                    }
                    ++normalCount;
                }
                else if (readStr.substr(0,2) == "f ")   //FACES
                {
                    readStr.erase(0,2);

                    CountN = CountM = 0;

                    while (CountN < 3)
                    {
                        while (CountM < 3)
                        {

                            if (CountM == 0) //VERTEXES
                            {
                                index = readStr.find("/", 0);
                                VERTEXES_INDEX[vertexCount][CountN] = atoi(readStr.substr(0, index).c_str());
                            }
                            else if (CountM == 1) //UVS
                            {
                                index = readStr.find("/", 0);
                                UVS_INDEX[vertexCount][CountN] = atoi(readStr.substr(0, index).c_str());
                            }
                            else if (CountM == 2) //NORMALS
                            {
                                index = readStr.find(" ", 0);
                                NORMALS_INDEX[vertexCount][CountN] = atoi(readStr.substr(0, index).c_str());
                            }
                            readStr.erase(0, index+1);
                            ++CountM;
                            ++faceCount;
                        }
                        ++CountN;
                    }
                }
            }
            fileHandle.close();
            IsModelLoaded = true;

            numVertexes = vertexCount;
            numUVS = uvCount;
            numNormals = normalCount;
            numFaces = faceCount;
        }
        else
            IsModelLoaded = false;
        return IsModelLoaded;
    }
};

And also the wavefront file I am trying to load (it should be a simple cube I explorted from Blender):

# Blender v2.67 (sub 1) OBJ File: ''
# www.blender.org
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vn 0.000000 -1.000000 0.000000
vn -0.000000 1.000000 0.000000
vn 1.000000 -0.000000 0.000001
vn -0.000000 -0.000000 1.000000
vn -1.000000 -0.000000 -0.000000
vn 0.000000 0.000000 -1.000000
vn 1.000000 0.000000 -0.000000
s off
f 1//1 2//1 3//1
f 5//2 8//2 7//2
f 1//3 5//3 6//3
f 2//4 6//4 3//4
f 3//5 7//5 4//5
f 5//6 1//6 4//6
f 4//1 1//1 3//1
f 6//2 5//2 7//2
f 2//7 1//7 6//7
f 6//4 7//4 3//4
f 7//5 8//5 4//5
f 8//6 5//6 4//6

I have tried quite a few exaples of how to implement it online without 3rd party tools however everything I have tried renders incorrectly…

If someone would be kind enough to verify that this code is correct and tell me how to implement the data into opengl I would be eternally greatful.

Many Thanks

Doctrorzeus


   float VERTEXES[MAXVERTEXES][2];
    float UVS[MAXUVS][2];
    float NORMALS[MAXNORMALS][2];

Why do positions and normals only have two components, you need three to store x,y,z.

There is one difficulty with OBJ files in that they use multiple indices (one for each vertex attribute) where OpenGL only supports a single index that is used for all vertex attributes. There are many posts (e.g. here) on this forum with solutions.
Without seeing how you use that data to actually render anything it is hard to say why it does not render correctly - have you verified that the data is loaded the way you expect it to be?

[QUOTE=carsten neumann;1252581]


   float VERTEXES[MAXVERTEXES][2];
    float UVS[MAXUVS][2];
    float NORMALS[MAXNORMALS][2];

Why do positions and normals only have two components, you need three to store x,y,z.

There is one difficulty with OBJ files in that they use multiple indices (one for each vertex attribute) where OpenGL only supports a single index that is used for all vertex attributes. There are many posts on this forum with solutions.
Without seeing how you use that data to actually render anything it is hard to say why it does not render correctly - have you verified that the data is loaded the way you expect it to be?[/QUOTE]

Thankyou very much for the reply :slight_smile:

I use each array as the following e.g:

VERTEXES[VertixNumber][xyz];

The second array (2) is used as x,y and z… I.e. x=0, y=1, z=2…

I ran the data through a breakpoint when it was loaded and it all seemed to look fine, the issue seems to be just using it.

Thanks for the link, I will take a look and have a search! :slight_smile:

DoctorZeus

Sorry but after doing a bit of searching I am still unsure about faces…

I know how to plot the verticies and normals (UVS are for textures which in this case is N/A because there are none).

By using glVertex3f() and glNormal3f().

However I am unsure exacly how faces can be used with verticies and normals?

Sorry to be a bother but I still don’t quite see the relationship… :frowning:

Thanks

DoctorZeus

You can for example take a look at OpenGL Programming/Modern OpenGL Tutorial Load OBJ - Wikibooks, open books for an open world or GLM: an Alias Wavefront OBJ file library about how to implement the handling of the .OBJ format

In which case, the second dimension needs to be 3, not 2. The value used in the declaration is the number of elements, not the highest valid index. The latter is one less than the former (e.g. if the number of elements is 3, valid indices are 0, 1 and 2).

If I understand your question, you do not seem to know what a line like f 1//1 2//1 4//2 means in an obj file. It defines one of the triangles making up the cube. ‘f’ stands for ‘face’. This particular face is a triangle made up of vertices 1,2, and 4 (indices into the vertex data you’ve read in). Since you seem to be able to plot points, I assume you understand what these indices mean. To plot points you use glBegin (GL_POINTS) followed by glVertex commands. To plot a triangle you could use glBegin (GL_TRIANGLES) or glBegin (GL_POLYGON). This is the most basic way to plot a polygon. Once you get this down and start dealing with large amounts of polygons you may want to use other ways to display your polygonal objects.

Thanks for the replies, been very helpful and the obj loader works properly now :slight_smile: , however I am still having a few difficulties implementing it…

Why doesn’t this work?

void AddObjectToRender(GraphicsStruc_Model *pTR)
    {
                for (unsigned int i=0; i < pTR->GetNumberFaces()-3; i++)
		{
			glBegin(GL_TRIANGLES);
			glVertex3f(pTR->GetVertex(pTR->GetFaces_Verticies(i,0)-1,0), pTR->GetVertex(pTR->GetFaces_Verticies(i,1)-1,1), pTR->GetVertex(pTR->GetFaces_Verticies(i,2)-1,2));
			glVertex3f(pTR->GetVertex(pTR->GetFaces_Verticies(i+1,0)-1,0), pTR->GetVertex(pTR->GetFaces_Verticies(i+1,1)-1,1), pTR->GetVertex(pTR->GetFaces_Verticies(i+1,2)-1,2));
			glVertex3f(pTR->GetVertex(pTR->GetFaces_Verticies(i+2,0)-1,0), pTR->GetVertex(pTR->GetFaces_Verticies(i+2,1)-1,1), pTR->GetVertex(pTR->GetFaces_Verticies(i+2,2)-1,2));
			glEnd();
		}
}

Many Thanks

DoctorZeus

Why doesn’t this work?

It’s very difficult to say anything other than “because apparently it’s wrong” in reply to such a question :wink: :wink: - please give details what does not work. One thing I can see from looking at the code is that the third glVertex call should probably use i+2 instead of i+1. Also, why are you using glVertex3d when your values are floats? That just causes a conversion to double for the glVertex3d call and then is converted back to float by OpenGL.

Thanks for the Reply… :slight_smile:

Oops yes I just realized that I kinda made that sound like a quiz question (which was not my intent) :stuck_out_tongue:

I corrected those thanks for realizing that…

Basically all the triangles when rendered are clearly connected in an incorrect way i.e:
[ATTACH=CONFIG]474[/ATTACH]
(suppose to be three wine glasses…

Which leads me to assume that I am implementing the data incorrectly through the AddObjectToRender() method…

Many Thanks

DoctorZeus

bump
bump

Anyone?

Thanks

DoctorZeus