[noob]How to Draw from .OBJ in memory?

Started by
5 comments, last by mynameisnafe 11 years, 6 months ago
Hi There

I read this a while ago and implemented my own version, as part of a model class. It's a really useful thread!

http://www.gamedev.n...ding-in-detail/

However, as someone in the thread points out: 'How do I go about drawing all this sh*t in OpenGL?'

I have learned that .obj indices start at 1, not 0. Can anybody help me apply this nugget of information to the following render method, which draws triangles from memory (stored in memory as in the thread linked above).

Many thanks in advance, I've been stuck on this forever and can't seem to find anything online about it.

Once I/we figure this out, I can get on with my project. smile.png

As Turvold said, 'talk is cheap, show me the code'.


void GLModel::RenderMesh( Colour3 c ) {
glPushMatrix();

glTranslatef(pos.x, pos.y, pos.z);
glColor3f( c.r, c.g, c.b );
glLineWidth(1.0);

glBegin(GL_TRIANGLES);
for ( int i = 0; i < mesh.noTriangles; i++ ) { //for each triangle in the list
Vector3D vpos; Normal3D npos;
// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[0] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[1] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[2] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[0] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[1] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[2] ].z;

glVertex3f(vpos.x, vpos.y, vpos.z);
//glNormal3f(npos.x, npos.y, npos.z);
} // i++
glEnd();
glPopMatrix();
}


Please help me! - Apparently I can't upload .cpp files, if anyone wants to see the whole class, leave a comment and I'll post it smile.png
Advertisement
I see a couple of problems here:

1. The glNormal3f should be put before the call to glVertex3f, otherwise all the normals will be off by 1.
2. For each triangle you are only pushing 1 vertex. You wrote:


// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[0] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[1] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[2] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[0] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[1] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[2] ].z;

glVertex3f(vpos.x, vpos.y, vpos.z);
//glNormal3f(npos.x, npos.y, npos.z);


and I think you meant:


for (j=0;j<3;j++)
{
// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[j] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[j] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[j] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[j] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[j] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[j] ].z;
glNormal3f(npos.x, npos.y, npos.z);
glVertex3f(vpos.x, vpos.y, vpos.z);
}


What you did was take the x coord of the 1st vertex, and y coord of the 2nd vertex and the z coord of the 3rd vertex!

Also, BTW, as soon as you get this rendering correctly, the 1st thing you should do is render with a VBO, or, at a minimum, a vertex array.
The format you have already is kind of similar to a vertex array, you just need to rearrange the data a little bit.

edit:

What I do when I load on obj file: In many cases, obj files use the same vertex/normal/texcoord index: i.e. 1/1/1 4/4/4 10/10/10, etc. Not always! Sometimes some models will re-use texcoord indices, resulting in off vertices like 10/5/10, 11/6/11, etc. OpenGL Vertex Arrays / VBO doesn't allow mis-matched vertices like this. So when I load an OBJ file, I make a map from 'obj file' vertex spec to opengl vertex index, copying when necessary. So, 1/1/1 and 1/1/2 are 2 completely different vertices in GL.

Also, BTW, as soon as you get this rendering correctly, the 1st thing you should do is render with a VBO, or, at a minimum, a vertex array.
The format you have already is kind of similar to a vertex array, you just need to rearrange the data a little bit.

I agree. Notice that you are using deprecated legacy OpenGL: http://www.opengl.org/wiki/Legacy_OpenGL. Better get it right now, until it is too late.
[size=2]Current project: Ephenation.
[size=2]Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/
Thank you guys! You are heros! Sorry it's taken me so long to get back to you.

Omg I see my error now. I'm just going to try it as you suggest before I post this..

Here's the code I'm trying:

void GLModel::RenderMesh( Colour3 c ) {
glPushMatrix();

glTranslatef(pos.x, pos.y, pos.z);
glColor3f( c.r, c.g, c.b );
glLineWidth(1.0);

glBegin(GL_TRIANGLES);
for ( int i = 0; i < mesh.noTriangles; i++ ) { //for each triangle in the list
Vector3D vpos; Normal3D npos;
for( int j = 0; j < 3; j++ ) {
// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[j] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[j] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[j] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[j] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[j] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[j] ].z;
glNormal3f(npos.x, npos.y, npos.z);
glVertex3f(vpos.x, vpos.y, vpos.z);
} //j++
} // i++
glEnd();
glPopMatrix();
}


For a model of a car i have it renders nothing? For a box I have it is a bit crazy-looking.

I'm using legacy OGL? What's the latest version these days?

Okay I'll move the normals to be processed before hand - I hadn't been using them ( :/ )

Okay

as soon as you get this rendering correctly, the 1st thing you should do is render with a VBO, or, at a minimum, a vertex array.


I thought I was rendering from a vertex array?

Okay, if you guys are willing to delve deeper, here is the code thats responsible for this, from file load to render. I really appreciate any help as I know I need tris before I can texture, have any use for lighting, worry about mtls, etc etc. I'm stuck on the first hurdle sad.png

In winmain, we have a call to this function, with the path to the model:


void GLModel::AllocateMesh( string path ){
// Step 1 - Count the Elements in the Mesh
string line; // our line to read
ifstream rfile(path); // open it up from filepath given
if( rfile.is_open() ) { //while open
while ( rfile.good() ) { //while not EOF or Error
getline (rfile, line); //get content..
// update the count held by the mesh given to us
if( line.substr(0,1) == "#" ) {/* do nothing for comments */ }
else if( line.substr(0,5) == "mtllib") {/* this is the objects material */ }
else if( line.substr(0,2) == "g ") {/* and do nowt for groups */ }
else if( line.substr(0,2) == "vn") { mesh.noNormals++; }
else if( line.substr(0,2) == "vt") { mesh.noTexCoords++; }
else if( line.substr(0,2) == "v ") { mesh.noVerts++; }
else if( line.substr(0,2) == "f ") { mesh.noTriangles++; }
}
}

rfile.close(); //housekeeping
// Step 2 - Allocate the memory according to the count
mesh.vertices = (Vertex3D*) malloc(sizeof(Vertex3D)* mesh.noVerts);
mesh.normals = (Normal3D*) malloc(sizeof(Normal3D)* mesh.noNormals);
mesh.texcoords = (UV*) malloc(sizeof(UV) * mesh.noTexCoords);
mesh.triangleList = (Triangle*) malloc(sizeof(Triangle)* mesh.noTriangles);
}


This is followed directly by a call to this function which reads the file:


void GLModel::LoadMeshData( string path ) {
string line;
ifstream rfile(path);
int ni, vi, ti, fi; //counters: normals, verts, texcoords, faces
ni = 0; vi = 0; ti = 0; fi = 0;
if ( rfile.is_open() ) { //if file is open
while ( !rfile.eof() ) { //til end of file:
getline(rfile, line);
istringstream currentline(line);
string temp, f1, f2, f3;
/* Vertex Normals */
if(line.substr(0,2) == "vn") {
currentline >> temp >>f1 >> f2 >> f3;
mesh.normals[ni].x = atof( f1.c_str() );
mesh.normals[ni].y = atof( f2.c_str() );
mesh.normals[ni].z = atof( f3.c_str() );
ni++; //next normal
}
/* Vertex texture coordinates */
else if(line.substr(0,2) == "vt") {
currentline >> temp >>f1 >> f2;
mesh.texcoords[ti].s = atof( f1.c_str() );
mesh.texcoords[ti].t = atof( f2.c_str() );
ti++; //next texcoords
}
/* Vertex */
else if(line.substr(0,1) == "v") {
currentline >> temp >>f1 >> f2 >> f3;
mesh.vertices[vi].x = atof( f1.c_str() );
mesh.vertices[vi].y = atof( f2.c_str() );
mesh.vertices[vi].z = atof( f3.c_str() );
vi++; //next vertex
}
/* Triangle/Face*/
else if(line.substr(0,1) == "f") {
string temp;
currentline >> temp >>f1 >> f2 >> f3;

int sPos = 0;
int ePos = 0;

ePos = f1.find_first_of("/");
//we have a line with the format of "f %d/%d/%d %d/%d/%d %d/%d/%d"
if(ePos != string::npos) {
temp = f1.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Vertex[0] = atoi( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f1.find( "/", sPos );
temp = f1.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Vertex[1] = atoi( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f1.length();
temp = f1.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Vertex[2] = atoi( temp.c_str() )- 1;
} //end triangle vertex data
sPos = 0;
ePos = f2.find_first_of("/");
if(ePos != string::npos) {
temp = f2.substr(sPos, ePos - sPos);
mesh.triangleList[fi].TexCoord[0] = atoi( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f2.find("/", sPos + 1);
temp = f2.substr(sPos, ePos - sPos);
mesh.triangleList[fi].TexCoord[1] = atoi( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f2.length();
temp = f2.substr(sPos, ePos - sPos);
mesh.triangleList[fi].TexCoord[2] = atoi( temp.c_str() ) - 1;
} //end triangle texcoord data
sPos = 0;
ePos = f3.find_first_of("/");
if(ePos != string::npos) {
temp = f3.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Normal[0] = atoi ( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f3.find("/", sPos + 1);
temp = f3.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Normal[1] = atoi( temp.c_str() ) - 1;
sPos = ePos + 1;
ePos = f3.length();
temp = f3.substr(sPos, ePos - sPos);
mesh.triangleList[fi].Normal[2] = atoi( temp.c_str() ) - 1;
} //end triangle normal data
fi++; //next face
} //end triangle data
} //end while not end of file
} //end if file is open
rfile.close(); //housekeeping
}//end LoadMeshData


Then the model is ready to draw in Render() with this code:


//draw
//theScene.theModel->RenderVerts( c ); //and render the model mesh vert by vert
theScene.theModel->RenderMesh( b ); //and render the model mesh tri by tri


Which calls (and which you've seen):


void GLModel::RenderMesh( Colour3 c ) {
glPushMatrix();

glTranslatef(pos.x, pos.y, pos.z);
glColor3f( c.r, c.g, c.b );
glLineWidth(1.0);

glBegin(GL_TRIANGLES);
for ( int i = 0; i < mesh.noTriangles; i++ ) { //for each triangle in the list
Vector3D vpos; Normal3D npos;
for( int j = 0; j < 3; j++ ) {
// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[j] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[j] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[j] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[j] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[j] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[j] ].z;
glNormal3f(npos.x, npos.y, npos.z);
glVertex3f(vpos.x, vpos.y, vpos.z);
} //j++
} // i++
glEnd();
glPopMatrix();
}


Edit: This is what the box looks like: Grey is the clear-colour, white is the triangles:


[attachment=11157:Capture1.PNG]
I thought I was rendering from a vertex array?[/quote]

You are rendering from an array of vertices, but that's not an OpenGL Vertex Array. If you arrange the data just right, you can call glVertexPointer, glNormalPointer, and glTexCoordPointer, and just hand it a pointer to your data. Then you just call glDrawElements or glDrawArrays, depending on what you really want to do. This is will speed things up just because instead of individual calls to glVertex3f, you it's all done internally. Later, the next step you can take is allocate a VBO and copy it to the graphics card. You will actually keep the calls to glVertexPointer, etc, except they will be offsets to inside the VBO.

I was going to say make sure you ' - 1' from all the indices, because OBJ files start with '1' and C starts with '0'. But it looks like you already did that.

Try rendering an OBJ file with just a single face in it. If that works, the rest should :)

And instead of rendering, just printf what 'it' thinks the data is. You will probably find that you did something slightly wrong when you cut up the strings you read from the file. Can you put up one of your obj files?
Looking back at the thread where the justinrwalsh dude says he saw a massive error in his code, looking at the DX11 rastertek tutorials (specifically his obj loading), and thinking about it,
I think I'm cutting the string up as v/v/v vt/vt/vt vn/vn/vn
and I think it should be v/vt/vn v/vt/vn v/vt/vn?

Thanks again for your help and advice I really appreciate it. :)

Heres the Obj file for the cube (can't upload so I'll copy paste it below) it should be fine because it's from a tutorial

# -----------------
# Start of obj file
g Box01
mtllib box.mat
usemtl box
v -62.0579 -41.4791 0.0
v 58.8424 -41.4791 0.0
v -62.0579 22.1865 0.0
v 58.8424 22.1865 0.0
v -62.0579 -41.4791 39.8714
v 58.8424 -41.4791 39.8714
v -62.0579 22.1865 39.8714
v 58.8424 22.1865 39.8714
vt 0.843206 0.405444 0.000499517
vt 0.482802 0.71377 0.9995
vt 0.478066 0.404023 0.000499636
vt 0.482802 0.716612 0.9995
vt 0.841627 0.688332 0.000499517
vt 0.482013 0.981029 0.9995
vt 0.480434 0.688332 0.000499636
vt 0.485959 0.978188 0.9995
vt 0.450102 0.00618343 0.000499547
vt 0.45247 0.509304 0.000499547
vt 0.000499517 0.512146 0.000499547
vt 0.000499517 0.512146 0.000499547
vt -0.0010791 0.00618302 0.000499547
vt 0.450102 0.00618343 0.000499547
vt 0.000499517 0.512009 0.9995
vt 0.450891 0.510588 0.9995
vt 0.45247 0.995237 0.9995
vt 0.45247 0.996658 0.9995
vt 0.000499636 0.9995 0.9995
vt 0.000499517 0.51343 0.9995
vt 0.478855 0.405444 0.000500023
vt 0.841627 0.408286 0.000499576
vt 0.83847 0.688332 0.000499576
vt 0.83847 0.688332 0.000499576
vt 0.477276 0.694016 0.000500023
vt 0.478855 0.405444 0.000500023
vt 0.482802 0.71377 0.9995
vt 0.845574 0.71377 0.999501
vt 0.844784 0.976767 0.999501
vt 0.844784 0.976767 0.999501
vt 0.482802 0.716612 0.9995
vt 0.842417 0.710929 0.9995
vt 0.843995 0.975346 0.9995
vt 0.843995 0.975346 0.9995
vt 0.478066 0.404023 0.000499636
vt 0.841627 0.688332 0.000499517
vn 0.0 0.0 -1.0
vn 0.0 0.0 -1.0
vn 0.0 0.0 1.0
vn 0.0 0.0 1.0
vn 0.0 -1.0 0.0
vn 0.0 -1.0 0.0
vn 1.0 0.0 0.0
vn 1.0 0.0 0.0
vn 0.0 1.0 0.0
vn 0.0 1.0 0.0
vn -1.0 0.0 0.0
vn -1.0 0.0 0.0
f 1/9/1 3/10/1 4/11/1
f 4/12/2 2/13/2 1/14/2
f 5/15/3 6/16/3 8/17/3
f 8/18/4 7/19/4 5/20/4
f 1/21/5 2/22/5 6/23/5
f 6/24/6 5/25/6 1/26/6
f 2/27/7 4/28/7 8/29/7
f 8/30/8 6/6/8 2/2/8
f 4/31/9 3/32/9 7/33/9
f 7/34/10 8/8/10 4/4/10
f 3/35/11 1/1/11 5/36/11
f 5/5/12 7/7/12 3/3/12
# end of obj file
# ---------------
Dude!

I totally got it working!

Sorry I didn't get back to you, or keep trying earlier, uni has just started - guess what my project is.. load a .3ds model and animate it in OpenGL.

I just opened the project, looked at the loader code, saw what it was that was wrong, ran it, et viola!

I wish I could screenshot it to here for you dude, thank you anyways!

If anybody wants obj laoding code let me know!

This topic is closed to new replies.

Advertisement