Loading model from .obj file problem

Hello!

I have written a small .obj file loader, to be able to draw the model with JOGL. Im able to parse the .obj file and retrieve all the vertices and faces, storing them in two ArrayLists (vertices and faces). These ArrayLists contains two object classes, Vertex and Face:

Vertex class:
This class contains three public float variables: x, y and z.

Face class:
This class contains an int array of the index numbers of the vertices for a polygon.

The .obj file im trying to load is a cube. I got this code from (http://www.eg-models.de/formats/Format_Obj.html).

#Cube.obj

Vertices…

v 1 1 1
v 1 1 -1
v 1 -1 1
v 1 -1 -1
v -1 1 1
v -1 1 -1
v -1 -1 1
v -1 -1 -1

Faces…

f 1 3 4 2
f 5 7 8 6
f 1 5 6 2
f 3 7 8 4
f 1 5 7 3
f 2 6 8 4

Now, the parsing goes fine, and all the values in the ArrayLists are correct. Although, when trying to display the cube, and rotate it, the cube looks weird.

(Ive created an applet, which can be viewed here: http://www.geocities.com/java_12345x/ ).

Im using some lighting code which i got form a tutorial, had to convert it from C code to Java, but this code seems to work.

But Im not sure if Ive understood the .obj format correctly. Each line in an .obj file starting with a ‘v’ is a vertex. My understanding is, that the vertices are numbered in the sequence they appear in the file.

The lines starting with ‘f’ are faces, containing a reference to vertices, indexed by the sequence they appear in the file.

Example .obj file

v 1 1 1
v 0 1 1
v 0 1 0

f 3 1 2

If you look at the applet above, there is clearly something wrong. The loader can display all the .obj files I have been able to download from the net, but they dont look too good. Seems like some of the faces are drawn the “wrong way”, They are either completely black or transparent.

So my question is, have I completely misunderstood the .obj file format, or am I drawing them wrong? Im no 3D expert and most of the openGL code is not written by me, but Ive got fragments of it from different tutorials on the net. So I dont claim to have written any of this OpenGL code.

The code I use to display is below:
#################################
public void display(GLDrawable glDrawable)
{
GL myGL = glDrawable.getGL();

    myGL.glMatrixMode(GL.GL_PROJECTION);
    myGL.glLoadIdentity();
    
    int width = canvas.getWidth();
    int height = canvas.getHeight();
    float aspect = height / (float) width;
    myGL.glFrustum(-10,10,-10*aspect,10*aspect,20,2000);
    myGL.glTranslatef(0, 0, -25);
    
    myGL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
    
    /**
     * Light...
     */
    float light_position[]={ 10.0f, 10.0f, -10.0f, 1.0f };
    float light_color[]={ 1.0f, 1.0f, 1.0f, 1.0f };
    float ambient_color[]={ 0.2f, 0.2f, 0.2f, 1.0f };
    float mat_specular[]={ 1.0f, 1.0f, 1.0f, 1.0f };

    myGL.glClearColor( 0.0f, 0.0f, 1.0f, 0.0f );
    myGL.glShadeModel(GL.GL_SMOOTH); // use gourand shading
    myGL.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, mat_specular );
    myGL.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_position );
    myGL.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambient_color );
    myGL.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light_color );
    myGL.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light_color );

    myGL.glLightModeli(GL.GL_LIGHT_MODEL_TWO_SIDE, 1 ); // two-sided lighting

    myGL.glEnable(GL.GL_LIGHTING);      // so lighting models are used
    myGL.glEnable(GL.GL_LIGHT0);        // we'll use LIGHT0
    /**
     * Light end..
     */
    
    //myGL.glColor3f(1.0f,0.5f,0.0f);
    
    //myGL.glRotatef(-90, 1, 0, 0);
    //myGL.glRotatef(45, 0, 0, 1);
    myGL.glRotatef(angle, 0, 1, 0);
    myGL.glScalef( 3, 3, 3 );
    
    /**
     * Draw the .obj file..
     */
    for( Face face : faces )
    {
        int[] vertexNumbers = face.getVertexNumbers();
        
        myGL.glBegin( GL.GL_POLYGON );
        
        for( int vertexNumber : vertexNumbers )
        {
            Vertex vertex = vertices.get( vertexNumber -1 );
            
            myGL.glVertex3f( vertex.x, vertex.y, vertex.z );
        }
        
        myGL.glEnd();
    }
}

Try this :wink:


myGL.glBegin( GL.GL_TRIANGLES );
for( Face face : faces ) {
      int[] vertexNumbers = face.getVertexNumbers();
      Vertex v1 = vertices.get( vertexNumbers[0] );
      Vertex v2 = vertices.get( vertexNumbers[1] );
      Vertex v3 = vertices.get( vertexNumbers[2] );
      myGL.glVertex3f( v1.x, v1.y, v1.z );
      myGL.glVertex3f( v2.x, v2.y, v2.z );
      myGL.glVertex3f( v3.x, v3.y, v3.z );
}
myGL.glEnd();

Normally you should send all your face’s as triangles. I can’t remember why you should use POLYGON but I never do.

Secondly, this approach is easier to compile into a display list (which is much faster).

You may not have some states enabled as well which would render the triangles correctly (i.e. lighting, smoothing, and auto-normals).

Let me know if this fixes it.

I’m assuming here that your OBJs are well formed, and you’re not making some other rendering errors, but IIRC The obj format doesn’t guarantee the winding of the polygons to be in any consistent order. So it really depends on how the model was originally constructed as to whether some of your polygons are going to be clockwise or anticlockwise winding.

Essentially what you have to do is preprocess your models either on load or with some seperate utility to ensure all the faces are of the same ordering.

I wrote some code to do this ages ago but can’t seem to find it anywhere, but Java3D has an OBJ importer which is probably pretty robust, I’d look at its source , might help out.

D.

Thank you for your replies!

To mustang:
########
I have enabled glShadeModel( GL_FASTEST ). All the light-stuff is done in the code above. Also, if I add the glEnable( GL_DEPTH_TEST ), the box no longer has “invisible” sides, but now the sides that were invisible are black. Strange.

And the problem I found using GL_TRIANGLES and “hardcoding” the vertices, was that in the .obj file there is no limit on how many vertices there can be for each face. But then again… it should be possible to convert a face with 4 vertices into two triangles with 6 vertices, as they obviously share 2 vertices… and so on. So you are right, I will try to write some code that does that, and then “hardcode” each GL_TRIANGLE to have three vertices, losing that one nested for-loop. Please correct me if this is completely wrong.

To Daire Quinlan:
###########

I will also look at the Java3D code for loading .obj files, this might hopefully give me some good hints. Because right now, I am not sure how I can determine from the .obj file in what way I should draw the vertices… I mean, if the obj file doesnt guarantee that the winding is correct, how would I be able to tell whats the right or wrong way? Think I need to do some reading about this clockwise/anti-clockwise drawing stuff though… :wink:

Thank you!

Btw… Found this great page for determining the winding of the polygons. ( http://astronomy.swin.edu.au/~pbourke/geometry/clockwise/ )

If you compile to a display list, then it may not matter if you use polygon or triangles. I’m not use if traingles would be faster.

Just a quick question. have you got back face culling enabled

gl.glEnable(GL.GL_CULL_FACE);

? I tried to look at your applet to discover exactly what the problem is because you don’t make it entirely clear whats wrong. From your description its a couple of different things it could be, either you have the incorrect CCW/CW setup in OpenGL which doesn’t match the cube or you don’t have backface culling enabled, or the problem is as i described above. I can’t run the applet BTW, Did you compile with 1.5 ? Also I don’t think JOGL and applets work really well together :slight_smile: stick up a screeny on the same page so i can take a look at it.

As regards the multiple vertices in the face listing , you can decompose them into triangles easily enough. Each face in the OBJ is guaranteed to be convex, so given a listing like the following …

f 1 3 4 2 5

then the resultant triangles would be :

1 3 4
1 4 2
1 2 5

Now whether or not the face is actually PLANAR and that triangle listing will result in the best triangulation is a question best left for another day :slight_smile:

and yeah Paul Bourkes site is pretty useful sometimes !

D.

Thank you for your reply!

Sorry about that java1.5 stuff, Ive now build it using 1.4 instead. I uploaded three applets, one without GL_DEPTH_TEST and GL_CULL_FACE, one with GL_DEPTH_TEST and one with them both enabled. Ive also added screenshots, and source code for the entire thing (packaged in a .jar file). With GL_CULL_FACE enabled, only three faces are visible. Strange. Obviously Im doing something wrong.

Its still here:
http://www.geocities.com/java_12345x/

Looking at the Paul Bourkes article now, to see if I can make some sense of the polygon winding.

yah, looking at the screenshots (your applet didn’t work, JOGL & applets don’t like one another, sandbox problems) It definately looks like a winding problem. OpenGL determines visibility purely by winding. IIRC It determines the signed area of the triangle after transformation, if its negative the triangle is culled (presuming you have culling turned on) . The problem with incorrectly wound polys is that they will be displayed but the normal actually points away from the viewer/light so the face will be black. So some of the faces are wound correctly (whichever you have setup, CCW is the default) and are lit correctly, some are wound incorrectly and either show up when they shouldn’t and get lit incorrectly, or are culled when they should be shown.

heres a face/vertex listing that ought to be consistent:

v -1 1 -1
v 1 1 -1
v -1 1 1
v 1 1 1
v -1 -1 -1
v 1 -1 -1
v -1 -1 1
v 1 -1 1

f 4 2 1 3
f 8 7 5 6
f 6 5 1 2
f 8 6 2 4
f 7 8 4 3
f 5 7 3 1

Hi!

Thank you for the good information. Im currently working on an algorithm for determining the winding, and Ive run into a problem. Ive been looking all over the internet, but cannot seem to find a solution. See, the formula I have only works if I calculate the winding of the triangles in 2D. I drew a picture below to illustrate the problem:

http://www.geocities.com/java_12345x/winding.png

On the triangle above the calculation below results in 0 no matter which way the vertices are drawn.


/**
 * This is anti-clockwise..
 */
float[] v1 = new float[]{1,1,0};
float[] v2 = new float[]{1,0,0};
float[] v3 = new float[]{1,0,1};

int x = 0;
int y = 1;
int z = 2;

float result = (v2[x] - v1[x])*(v3[y] - v2[y]) - (v2[y] - v1[y])*(v3[x] - v1[x]);


And the result is 0, no matter which way you draw the vertices. The formula I found is this:
(x2 - x1).(y3 - y2) - (y2 - y1).(x3 - x1)

Obviously I need to consider the z value in the calculation, but Im not sure how to do that…

That’ll only work in 2D.

Simplest way to do it in 3D is to calculate the face normal by getting the cross product of two vectors constructed using the polygon vertices (google it for references). Then compare with another vector. For example, in the case of a cube or other convex shape, use the center of the object -> one of the vertices of the face as a reference vector. Doing a dot product between the reference vector and the face normal will give you either a positive number or a negative number. If positive then the face points ‘toward’ the center of the object, if negative then the face points ‘away’ from the center of the object.

D.

The OpenGL culling I believe works more or less like this:

  1. Calculates a facenormal, n, by calculating e1 x e2 where e1 and e2 are two of the edge vectors of the face concerned in the winding order defined and ‘x’ is the vector cross product

  2. Calculates n.r where r is the position vector of any one of the vertices of the face. and ‘.’ is the vector dot product. The sign of this gives the sign of the cosine of the angle between n and r, and hence determines if the face normal is pointing to the viewpoint or away from the viewpoint.

Don’t confuse the face normal with any vertex normals you may have defined, btw, they are completely different things.

Rob

Nope, that’d be far too expensive to calculate per-poly. OpenGL bases its face culling off of the winding order of the polys. Pre-calculated normals are only used for lighting and some texgen modes.

[quote]Nope, that’d be far too expensive to calculate per-poly. OpenGL bases its face culling off of the winding order of the polys. Pre-calculated normals are only used for lighting and some texgen modes.
[/quote]
You are correct. However, I was suggesting he do this as part of a pre-process to ensure his polygons are wound correctly, In the loader or something, not every frame.

D.

-edit- Apologies to Orangey Tang, I thought you were replying to my post, not the one from Rob Nugent :slight_smile: -edit-

Thank you for your replies!

This has been very informative for me! I will try to put together some code, and post the results here, as soon as I get something working. Might take some time, as my math skills are really not helping me out here ;D , a bit rusty after years of J2EE programming. Still, Im finding JOGL and 3D extremely addictive and fun to code. Challenging, but fun. Really appriciate your help on this!

Alright. Now I have gone through a Vector math tutorial, and put together some code to get the winding of the vertices right (Thanx for the how-to Daire Quinlan). This works well on convex shapes, but I suspect that doing something like this on concave shapes is a LOT worse… I now split every face in the .obj file into a triangle, and then check the dot product from the cross product and the reference vector from the center to one of the vertices of the triangle.



/**
         * Cross Product..
         */
        int x = 0;
        int y = 1;
        int z = 2;
        
        float[] vector1 = new float[] {0,0,2};
        float[] vector2 = new float[] {2,0,0};
        
        float[] crossproduct = new float[3];
        
        crossproduct[x] = (vector1[y]*vector2[z] - vector1[z]*vector2[y]);
        crossproduct[y] = (vector1[z]*vector2[x] - vector1[x]*vector2[z]);
        crossproduct[z] = (vector1[x]*vector2[y] - vector1[y]*vector2[x]);
        
        System.out.println("Cross Product:");
        System.out.println("x=" + crossproduct[x] );
        System.out.println("y=" + crossproduct[y] );
        System.out.println("z=" + crossproduct[z] );
        System.out.println("----");
        
        /**
         * Reference vector from the center point to one of the vertices to be drawn..
         */
        
        float startx = 2;
        float starty = -2;
        float startz = 2;
        
        float endx = 1;
        float endy = 0;
        float endz = 1;
        
        float[] center = new float[] {endx - startx, endy - starty, endz - startz};
        System.out.println("center[x]=" + center[x] );
        System.out.println("center[y]=" + center[y] );
        System.out.println("center[z]=" + center[z] );
        System.out.println("-------------");
        
        /**
         * Dot product of the reference vector and the cross product vector..
         */
        float dotProduct = (crossproduct[x]*center[x]) + (crossproduct[y]*center[y]) + (crossproduct[z]*center[z]);
        System.out.println("Dot Product=" + dotProduct );


Btw… How hard is it exactly to do something similar on concave shapes?

yeah, that’ll only work for convex shapes. If you consider a torus for example, which is about as concave as you can get, all the faces on the inside of the torus will be pointing toward the center of the object, but thats actually correct.

The method that i mentioned earlier on (that i couldn’t find any code for) actually went through the face list and made sure all the polygons were wound consistently with respect to each other . But unfortunately there actually is no way of taking an arbitary mesh and being able to say ‘well these polygons are wound CCW’ or ‘all of them are wound CW’, but you can ensure that all of them are wound the same way at least. I used to do it on a per-model basis with a switch to flip the winding either way. Another alternative you might want to consider is writing a small utility (this’ll be good practice too !) that loads in an OBJ file and allows you to view it and correct the winding and reverse the direction of the winding if appropriate, and then write it out again as a correctly formatted OBJ which you can load in with no difficulty at all.

Though really, this sort of thing is a complete PITA to deal with. The current importer I’m using for my models, both animated and static, doesn’t have ANY of these checks, because I assume the model exported from my modeller is going to be of the correct format.

D.

Agree 100%. It’s not worth it really. My model importers assumes certain formats, which is OK because the modelling tools (MAX, etc.) have their own standards anyway.

After I convert the faces in the OBJ to triangles, all the complicated models look 100%. I obviously have to turn off the winding algorithm presented above, otherwise it winds some of the vertices the wrong way, since the model is not convex.

Here is a screenshot of the loader in action (Using a .obj file I found on the net, temple.obj):

http://www.geocities.com/java_12345x/temple.png

Seems like the loader is working now, and I think I will leave it to that. :slight_smile: Once again, thank you for all your help!

Next step… Textures :smiley: