0

I am working on a game with some friends, and to make life easier I decided all out 3D models are going to be made using .obj files, exported in programs like 3DSMax, Maya, and Blender3D.

So I have written a .obj file reader and tried it out in a simple scene, some .obj files rendered good (like a simple cube), some rendered really strangely, and then some didn't render at all. I was hoping someone could point out to me what I did wrong, the following code contains one class, with 2 embedded classed, one of which contains another embedded class. It may get confusing, so you can copy and paste into a file for easier reading if needed.

The script reads line by line in the file and if it starts with "v" (vertex) it splits the line by space, and takes index 1 2 and 3 (x y and z) and stores the integer values in a class called Vertex, and adds it to an array of Vertex objects. a Vertex object acts just like a vector, only it contains two vectors, one for position, and one for the normals.

If the line starts with "vn" (Vertex Normal) it splits the line by " " and takes the indexes 1 2 and 3 and adds then to a Vertex object, which acts just like a vector so far, and then that Vertex is added to a different Vertex array specifically for normals.

Now here is the interesting part, when the line starts with "f" (Face) the line might look like:

f 1//3 5//3 6//1 2//4

each #1//#2, #1 is the index of the correct vertex, and #2 is the index for the proper normal. So I take each part of the line, splitted by " " and split it by "//" and took the vertex from the vertices array and the vertex from the normals array, set the vertices normals to the x y and z of the normal, made a Face object, and added it to a list of faces, which faces just hold 3 or 4 Vertex objects.

Hopefully this explaination might make the file not seem as messy.

Well, here is the code:

package org.ic3d.utils;

import java.io.*;
import org.ic3d.utils.ObjReader.Face.FaceStateException;
import org.lwjgl.opengl.GL11;

public class ObjReader
{   
    public ObjReader(String file)
    {
        try
        {
            BufferedReader reader = new BufferedReader(new FileReader(new File(file)));
            parse(reader);
        }
        catch(Exception e)
        {
            System.out.println(file+" Failed To Load! Can't continue.");
            System.exit(0);
        }
    }

    /**
     * Parse all lines in the BufferedReader to convert the .obj file
     * 
     * @param br - A BufferedReader object pointing to the desired .obj file
     * @throws IOException If the obj file can not be read for any reason.
     * @throws FaceStateException If the obj file is malformed and vertice are added to a face of different shape (tri - quad)
     */
    public void parse(BufferedReader br) throws IOException, FaceStateException
    {
        String s="";

        Vertex[] v1 = new Vertex[15000];
        Vertex[] n1 = new Vertex[15000];

        while((s = br.readLine())!=null)
        {
            if(s.startsWith("v"))
            {
                String[] pv = s.split(" ");

                Vertex vert_0x = new Vertex(Float.parseFloat(pv[1]), Float.parseFloat(pv[2]), Float.parseFloat(pv[3]));

                v1 = appendVert(v1, vert_0x);
            }
            if(s.startsWith("vn"))
            {
                String[] pv = s.split(" ");

                Vertex vert_0x = new Vertex(Float.parseFloat(pv[1]), Float.parseFloat(pv[2]), Float.parseFloat(pv[3]));

                n1 = appendVert(n1, vert_0x);
            }
            if(s.startsWith("f"))
            {
                String[] pv = s.split(" ");

                Vertex[] temp = new Vertex[pv.length-1];

                for(int i=1;i<pv.length;i++)
                {
                    String[] vn = pv[i].split("//");

                    Vertex v = v1[Integer.parseInt(vn[0])];

                    Vertex n = n1[Integer.parseInt(vn[1])];

                    v.setNormals(n.getX(), n.getY(), n.getZ());

                    temp = appendVert(temp, v);
                }
                try
                {
                    Face f = new Face(temp.length==3?Face.GL_FACE_TRI:Face.GL_FACE_QUAD, temp);
                    faces = appendFace(faces, f);
                }
                catch(FaceStateException e)
                {
                    throw e;
                }
            }
        }
    }
    private Vertex[] appendVert(Vertex[] l, Vertex v)
    {
        for(int i=0;i<l.length;i++)
        {
            if(l[i]==null)
            {
                l[i] = v;
                return l;
            }
        }
        System.out.println("Vertex[] can only hold "+l.length+" Vertices at one time");
        return l;
    }
    private Face[] appendFace(Face[] l, Face f)
    {
        for(int i=0;i<l.length;i++)
        {
            if(l[i]==null)
            {
                l[i] = f;
                return l;
            }
        }
        System.out.println("Face[] can only hold "+faces.length+" Faces at one time");
        return l;
    }
    public void renderTri(Face f, float x, float y, float z)
    {
        Vertex[] v = f.getVerts();

        GL11.glBegin(GL11.GL_TRIANGLES);

        Vertex cv = v[0];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        cv = v[1];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        cv = v[2];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        GL11.glEnd();
    }
    public void renderQuad(Face f, float x, float y, float z)
    {
        Vertex[] v = f.getVerts();

        GL11.glBegin(GL11.GL_QUADS);

        Vertex cv = v[0];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        cv = v[2];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        cv = v[2];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        cv = v[3];
        GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
        GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());

        GL11.glEnd();
    }
    public void render(float x, float y, float z)
    {
        GL11.glPushMatrix();
        for(Face f : faces)
        {
            if(f==null)
            {
                GL11.glPopMatrix();
                return;
            }
            else
            {
                switch(f.getType())
                {
                    case(3):
                    {
                        renderTri(f, x, y, z);
                    }
                    case(4):
                    {
                        renderQuad(f, x, y, z);
                    }
                }
            }
        }
        GL11.glPopMatrix();
    }
    public int listid=0;
    public Face[] faces = new Face[15000];
    public class Face
    {
        /**
         * Create a new Face object, Faces Hold 3 or 4 Vertex Objects, Polygons not accepted.
         * @param shape
         * @param verts
         * @throws FaceStateException - If the number of vertice in the Vertex[] is not equal to the face type set.
         */
        public Face(int shape, Vertex[] vertlist) throws FaceStateException{

            int vert_n = GL_FACE_NONE-shape;

            if(vertlist.length>vert_n)
            {
                throw new FaceStateException(vert_n+" Vertice faces can not hold "+verts.length+" vertices");
            }
            if(vertlist.length<vert_n)
            {
                throw new FaceStateException(vert_n+" Vertice faces must hold "+vert_n+" vertice, not "+verts.length+" vertices");
            }
            if(vert_n!=3 && vert_n!=4)
            {
                throw new FaceStateException("Faces can only be 3 or 4 vertice. Shapes besides QUAD and TRI are not allowed.");
            }
            type=vert_n;

            verts=vertlist;
        }
        public Vertex[] getVerts()
        {
            return verts;
        }
        public int getType()
        {
            return type;
        }
        public String getType(int i)
        {
            if(i==1)
            {
                return(type==3?"TRI":"QUAD");
            }
            else
            {
                return(type==3?"TRIANGLE":"QUAD");
            }
        }
        private Vertex[] verts;
        public static final int GL_FACE_QUAD = 3;
        public static final int GL_FACE_TRI = 4;
        public static final int GL_FACE_NONE = 7;
        private int type=7;
        public class FaceStateException extends Exception
        {
            public FaceStateException(String s)
            {
                super(s);
            }
            private static final long serialVersionUID = 1L;
        }
    }
    public class Vertex
    {
        public Vertex(float x, float y, float z)
        {
            _x=x;
            _y=y;
            _z=z;
        }
        public void setNormals(float x, float y, float z)
        {
            _nx=x;
            _ny=y;
            _nz=z;
        }
        public float getX()
        {
            return _x;
        }
        public float getY()
        {
            return _y;
        }
        public float getZ()
        {
            return _z;
        }
        public float getNormalX()
        {
            return _nx;
        }
        public float getNormalY()
        {
            return _ny;
        }
        public float getNormalZ()
        {
            return _nz;
        }
        public float[] getNormalXYZ()
        {
            return new float[]{_nx, _ny, _nz};
        }
        public void setXYZ(float x, float y, float z)
        {
            _x=x;
            _y=y;
            _z=z;
        }
        public float[] getXYZ()
        {
            return new float[]{_x, _y, _z};
        }
        private float _x;
        private float _y;
        private float _z;
        private float _nx;
        private float _ny;
        private float _nz;
    }
}
D3_JMultiply
  • 1,022
  • 2
  • 12
  • 22
  • I don't know if you've experienced this, but when using your code, I had to decrement the index lookups when building the faces in order to get this to work. – Bryan Johnson Oct 09 '12 at 01:37

1 Answers1

1

I believe it's fairly simple fix, GL11.glEnd() is really not needed, you can start another render type using begin, without even ending it first. Try cutting the GL11.glEnd() to the end of your entire render script, see if that helps any:

public void render()
{
    Object1.render( 5, 0, 5);
    Object1.render(-5, 0,-5);
    Object2.render( 5, 0,-5);
    Object2.render(-5, 0, 5);
    GL11.glEnd();
}
Jasper
  • 75,717
  • 14
  • 151
  • 146
Travexii
  • 26
  • 2
  • thank you, that helped a lot, I was hoping I could end it inside the method though, but I didn't know I could start rendering trigons right from quads and vise-versa. thank you. – D3_JMultiply Dec 12 '11 at 21:12