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
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.
Next we get the current 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.