The ProgrammersTalk Community
Forum Register Search Today's Posts Mark Forums Read
Register

Go Back   The ProgrammersTalk Community > Graphic & Game Programming > OpenGL


Welcome to the The ProgrammersTalk Community forums.

You are currently viewing our boards as a guest which gives you limited access to view most discussions and access our other features. By joining our free community you will have access to post topics, communicate privately with other members (PM), respond to polls, upload content and access many other special features. Registration is fast, simple and absolutely free so please, join our community today!

If you have any problems with the registration process or your account login, please contact contact us.
Tags: ,

Reply
 
LinkBack Thread Tools    Display Modes   
  #1 (permalink)  
Old 03-16-2008, 05:48 AM
MrPickle's Avatar
MrPickle MrPickle is offline
Sr. Programmer
Join Date: Nov 2007
Location: England, Lincolnshire
Posts: 255
iTrader: (0)
MrPickle is on a distinguished roadMrPickle is on a distinguished roadMrPickle is on a distinguished road
How to make a basic .obj loader

First I'll start with the basic layout of an .obj file. If you don't have some basic knowledge of OpenGL it's best that you get some, you'll only really need knowledge on how to draw basic polygons. You should also know how to setup a basic OpenGL window. You'll also need the glut libary which can be found here: http://www.xmission.com/~nate/glut.html

# a comment
Normally at the top of the file there's a comment saying which exporter made the file and what not, we just ignore these because well they're comments.

v x y z
This tells us that the following information is a vertex, one point of our face. If it's the first vertex in our file, it's vertex 1, if it's the 10th vertex in the file, it's vertex 10. We'll see why this is relevant later on.

vt u v [w]
This tells us that the following information is a texture coordinate, I haven't tried to get these working as I haven't needed them so you'll have to use your own brains to work them out. Again there position corresponds to what vertex they effect.

vn x y z
This tells us that the following information is a vertex normal. A normal is a vector perpendicular to another vector. These are needed for correct lighting. Again if it's the first normal in our file it's the normal of vertex 1, if it's the 100th normal, it's the normal of vertex 100.

f v1 v2 v3 or f v1//vn1 v2//vn1 v3//vn3 or f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3
This tells us information about the face, this is what brings everything together, I will only be covering f v1 v2 v3 because well I haven't worked out a way to use the others yet (hehe). It's basically a whole number saying which vertex to use, eg: f 5 2 6 is telling us that one corner of our triangle is vertex 5 another corner is vertex 2 and the last is vertex 6. (Remember our vertices are just points, when we specify 3 points together in OpenGL after glBegin(GL_TRIANGLES) it connects them up to make a triangle.)

Here's a basic example of a .obj file, a simple cube:
Code:
# Blender3D v245 OBJ File: 
# www.blender3d.org

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 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
f 5 1 4
f 5 4 8
f 3 7 8
f 3 8 4
f 2 6 3
f 6 7 3
f 1 5 2
f 5 6 2
f 5 8 6
f 8 7 6
f 1 2 3
f 1 3 4
Now for some code!

First we need a way to get the information from the line, there's other ways, but I prefer using this (Thanks to Bench):

Code:
#include <vector>
#include <sstream>

using namespace std;

string getWord(string Line, int Pos)
{
    vector<string> Vec;
    stringstream ss(Line);
    string Word, a;
    while(ss >> Word)
    Vec.push_back(Word);

    if(Pos > Vec.size()-1) //Check whether it's in range
        return "";

    else
    {
        try { a = Vec.at(Pos); } //Just to be safe
        catch(...) { a = ""; } //I'm not sure what to catch
        return a; //Return the word
    }
}
But that returns a string, we need to convert that to a float or int!

Code:
int convertToInt(string Num)
{
    istringstream i(Num);
    int x;

    if(!(i >> x))
        throw 0;

    return x;
}

float convertToFloat(string Num)
{
    istringstream i(Num);
    float x;

    if(!(i >> x))
        throw 0;

    return x;
}
Now we want to open our file, but wait, you don't have any .obj files? I took the liberty of uploading you some, here and here. Now that's sorted we can finally start. I am going to assume that you already have all the code to setup your basic window, if not you can download it here because I'm nice :D I forgot to put "using namespace std;" in, just place it before "bool Grid"

First we need a way to store all the information about our class!

We'll need something to hold our x, y and z coordinates!

Code:
struct Vector {
    float x, y, z;
};
That'll do nicely!
Now we need something to hold our Vertex information, remember we have the vertex position and then the normal!

Code:
struct Vertex {
    Vector Pos;
    Vector Normal;
};
What do we need now? Something to hold the face information did I here you say?

Code:
struct Triangle {
    int v1, v2, v3; //One for each of our corners!
};
Well, that was easy, now we have a way to store the infromation! But how do we bring this all together, I used a class, you can use whatever you feel like.

First we declare our class
Code:
class Model
{
Now, the variables, we don't want everything to be able to edit them so we'll make them private
Code:
    private: //So we can't change them willy nilly
        Triangle Triangles[50000]; //As .obj files don't tell us how many faces
                                   //we have, we have to take a guess, feel free to change the amount for
                                  //bigger models, the most I've come across is 130k
        Vertex Verticies[50000];  //Same as above
        int TriangleCount; //Keep track of the amount of triangles we have
        int VertexCount; //Keep track of the amount of Vertices we have
        int NormalCount; //Keep track of the amount of normals we have
        bool Loaded; //So we know whether it's been loaded of not
Great! Now for the functions!

Code:
public: //So we can access the functions
        Model(); //Constructor
        int Load(const char* Filename);//The function to load, it's int so we can exit the function easily if there's an error!
        void Draw(void); //The function to draw
};
There! Now we've got a nice way to store the information about or model, lets get to the actual code now!

Code:
Model::Model()
{
    VertexCount = 1; //Start on 1
    TriangleCount = 1; //Start on 1
    NormalCount = 1; //Start on 1
    Loaded = false; //We haven't loaded a model yet!
}
That's our constructor, we start on one so that our first vertex isn't vertex 0 but vertex 1 as the faces indicate vertex 1 as vertex 1 not vertex 0. Strange really, everything else in the world of coding starts on 0, so why should they be special :D

Now for the loading! It's really quite simple so don't run away now!
Code:
int Model::Load(const char *Filename)
{
First declare our function, now to declare the variables

Code:
    ifstream F; //Our file
    string Line; //The current line of the file
Nothing special there, just some everyday variables.
Next, we open our file and check if it opened!

Code:
    F.open(Filename, ios::in);
    if(F.fail()) //Has it failed to open
    {
        cout << "Error opening \"" << Filename << "\"" << endl; //Send an error message
        return 0; //Exit the function
    }
Again, nothing to special.
Next we check if we've already got a model loaded, if so, we reset the Triangle, Vertex and Normal count so we don't have both models loaded at once! You can remove this line and see what happens if you want, it won't break, promise!

Code:
if(Loaded) //If loaded is true
    {
        VertexCount = 1; //Reset
        TriangleCount = 1; //Reset
        NormalCount = 1; //Reset
    }
Put your pitchforks away, we're finally here, time to read the file!
First, we start our while loop, standard stuff really.

Code:
    while(!F.eof())
    {
Next we get the current line

Code:
getline(F, Line);
Now to check what we're dealing with, a vertex, a normal, a face?
Thanks to the functions I mentioned earlier we can do that easily!

Code:
if(getWord(Line, 0) == "v") //If the first word is v
        {
Now we know we're dealing with a vertex, we can assign this information to out Verticies variable!

Code:
            if(VertexCount > 50000) break; //If we're exceeding our 50000 we'd better stop
            Verticies[VertexCount].Pos.x = convertToFloat(getWord(Line, 1)); //Set our x location to the second word of the line.
            Verticies[VertexCount].Pos.y = convertToFloat(getWord(Line, 2)); //Set our y variable to the thrid word of the line.
            Verticies[VertexCount].Pos.z = convertToFloat(getWord(Line, 3)); //Set our z variable to the fourth word of the line.
            VertexCount++; //Next vertex please!
It's that simple! We're not done yet mind you! Next we check for normals or faces! If we do have a face or normal we basically do the same!

Code:
        }
        else if(getWord(Line, 0) == "vn") //If it wasn't v is it vn?
        {
            if(NormalCount > 50000) break; //Are we about to exceed the limit?
            Verticies[NormalCount].Normal.x = convertToFloat(getWord(Line, 1));
            Verticies[NormalCount].Normal.y = convertToFloat(getWord(Line, 2));
            Verticies[NormalCount].Normal.z = convertToFloat(getWord(Line, 3));
            NormalCount++; //Next Normal Please!
        }
        else if(getWord(Line, 0) == "f") //If it wasn't v or vn then is it a face?
        {
            if(TriangleCount > 50000) break; //Check to see if we're exceeding limit again
            Triangles[TriangleCount].v1 = convertToInt(getWord(Line, 1));
            Triangles[TriangleCount].v2 = convertToInt(getWord(Line, 2));
            Triangles[TriangleCount].v3 = convertToInt(getWord(Line, 3));
            TriangleCount++; //Next triangle please!
        }
} //Close Loop!
Simple really huh! Next we close the file and set loaded to true!
Code:
    F.close();
    Loaded = true;
    cout << "Triangles: " << TriangleCount-1 << endl;
    cout << "Vertecies: " << VertexCount-1 << endl;
    cout << "Normals: " << NormalCount-1 << endl;
}
Drawing function in next post, it was too long.

__________________

Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!

Last edited by MrPickle : 03-16-2008 at 06:21 AM.
Reply With Quote
  #2 (permalink)  
Old 03-16-2008, 05:50 AM
MrPickle's Avatar
MrPickle MrPickle is offline
Sr. Programmer
Join Date: Nov 2007
Location: England, Lincolnshire
Posts: 255
iTrader: (0)
MrPickle is on a distinguished roadMrPickle is on a distinguished roadMrPickle is on a distinguished road
That's our loading function done! Hold your horses, don't try and compile it yet we're not done! We still have to draw our model!

Code:
void Model::Draw(void) //declare our function
{
    if(Loaded) //Check if it's loaded!
    {
Now we can do the actual drawing!

Code:
glBegin(GL_TRIANGLES); //Say we're using triangles!
        for(int i = 1; i < TriangleCount; i++) //For each face
        {
            glNormal3f(Verticies[Triangles[i].v1].Normal.x, Verticies[Triangles[i].v1].Normal.y, Verticies[Triangles[i].v1].Normal.z);
            glVertex3f(Verticies[Triangles[i].v1].Pos.x, Verticies[Triangles[i].v1].Pos.y, Verticies[Triangles[i].v1].Pos.z);
            
            glNormal3f(Verticies[Triangles[i].v2].Normal.x, Verticies[Triangles[i].v2].Normal.y, Verticies[Triangles[i].v2].Normal.z);
            glVertex3f(Verticies[Triangles[i].v2].Pos.x, Verticies[Triangles[i].v2].Pos.y, Verticies[Triangles[i].v2].Pos.z);
            
            glNormal3f(Verticies[Triangles[i].v3].Normal.x, Verticies[Triangles[i].v3].Normal.y, Verticies[Triangles[i].v3].Normal.z);
            glVertex3f(Verticies[Triangles[i].v3].Pos.x, Verticies[Triangles[i].v3].Pos.
        }
glEnd(); //Stop drawing!
Now, that may look complicated but it really isn't! All of our vertices are stored in Vertices[50000] and they're in the order of what they were in the file. What the above is basically saying is "Draw the Vertex (Verticies[) of the current triangle's (Triangle[i]) point 1, 2 and 3 (.v1/2/3])"

Now we must close the function and if

Code:
    }
}
Now you can compile it! Load in one of the provided models and the outcome should be whatever model you loaded! Cool huh!

If you used the starting code I provided you can toggle the grid on and off by pressing g and load a new model with l.

Download here!

Tell me if you're having problems and I'll help you!

__________________

Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!

Last edited by MrPickle : 03-16-2008 at 06:18 AM.
Reply With Quote
The Following User Says Thank You to MrPickle For This Useful Post:
ccoonen (03-16-2008)
  #3 (permalink)  
Old 03-16-2008, 07:37 PM
ccoonen ccoonen is offline
PT Staff
Awards Showcase
Quality Tutorial Quality Tutorial Quality Tutorial Quality Tutorial 
Total Awards: 4
Join Date: Jun 2007
Location: Wisconsin
Posts: 308
iTrader: (0)
ccoonen is on a distinguished roadccoonen is on a distinguished roadccoonen is on a distinguished roadccoonen is on a distinguished road
Excellent, thanks buddy! I can't wait
Reply With Quote
  #4 (permalink)  
Old 03-17-2008, 04:56 PM
MrPickle's Avatar
MrPickle MrPickle is offline
Sr. Programmer
Join Date: Nov 2007
Location: England, Lincolnshire
Posts: 255
iTrader: (0)
MrPickle is on a distinguished roadMrPickle is on a distinguished roadMrPickle is on a distinguished road
Was you able to follow it easily?

__________________

Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!
Reply With Quote
  #5 (permalink)  
Old 06-19-2008, 07:45 AM
hansen hansen is offline
Novice
Join Date: Jun 2008
Posts: 2
iTrader: (0)
hansen is on a distinguished road
errors in compilation

Hi

I am using Visual Studio 2005 v8.0. and I have some errors in the routine
-> OpenFileDialog(char FileName[])

Here is the errors

Warning 1 warning C4018: '>' : signed/unsigned mismatch 58
Error 2 error C2440: '=' : cannot convert from 'char [512]' to 'LPWSTR' 178
Error 3 error C2440: '=' : cannot convert from 'const char [10]' to 'LPCWSTR' 180
Error 4 error C2664: 'GetCurrentDirectoryW' : cannot convert parameter 2 from 'char [512]' to 'LPWSTR' 183
Error 5 error C2664: 'SetCurrentDirectoryW' : cannot convert parameter 1 from 'char [512]' to 'LPCWSTR' 193
Error 6 error C2664: 'SetCurrentDirectoryW' : cannot convert parameter 1 from 'char [512]' to 'LPCWSTR' 197

The comments in the code mention to not ask you how it works, but I would like to know if it works for you, what version do u have and where did you get that code,
thanks a lot in advance

__________________

Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!
Reply With Quote
  #6 (permalink)  
Old 06-19-2008, 08:18 AM
hansen hansen is offline
Novice
Join Date: Jun 2008
Posts: 2
iTrader: (0)
hansen is on a distinguished road
it is fixed with ->

Configuration Properties > General. Switch Character Set to "Use Multi-Byte Character Set".

__________________

Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!
Reply With Quote
  #7 (permalink)  
Old 06-19-2008, 09:18 AM
MrPickle's Avatar
MrPickle MrPickle is offline
Sr. Programmer
Join Date: Nov 2007
Location: England, Lincolnshire
Posts: 255
iTrader: (0)
MrPickle is on a distinguished roadMrPickle is on a distinguished roadMrPickle is on a distinguished road
I found the for the open file dialogue code in a tutorial ages ago. I found it handy and kept it.

I'm not sure how it works though as that wasn't explained in the tutorial.

Sorry I can't help more.

__________________
PM me and tell me your name or a phrase and I shall write it in Elf for you. English Tengwar to be precise.
Digg this Post! Del.Icio.Us this Post! Technorati this Post! Furl this Post! Mister Wong this Post! Newsvine this Post! Spurl this Post! Reddit this Post! Netscape this Post!
Reply With Quote
Reply


Thread Tools
Display Modes

   Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



All times are GMT -7. The time now is 04:15 PM. Powered by vBulletin
Copyright © 2000 - 2007, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO © 2007 ProgrammersTalk Sedo - Buy and Sell Domain Names and Websites project info: programmerstalk.net Statistics for project programmerstalk.net etracker® web controlling instead of log file analysis


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50