Positional lighting seems to have a direction

Started by hemoid, October 05, 2011, 05:35:31

Previous topic - Next topic

hemoid

Hello there,

I've been using LWJGL in order to learn OpenGL and I have developed a simple application. Basically it allows a first person perspective in a 3D world. This world consists of a the three axes (x,y,z) going through the origin, a floor on the x-z plane, and a shape hovering somewhere above the floor.

This all works fine and I'm very happy with the result, though it lacks proper lighting. This is where I meet my troubles. I have read the lighting sections of three books, browsed through forums and the like, but without success.

I have enabled GL_LIGHTING as well as GL_LIGHT1, initialized the ambient light color and diffuse, and I am repositioning the light to position (0,0,0) after every call to gluLookAt(...). It does look like the light is originating from (0,0,0), however it seems as though the direction of the light is (0,0,-1) as there is no light on the +z side of the x-axis.

I'll provide screenshots and relevant code. Thanks to anyone who provides assistance!

hemoid

Screenshots:
http://imgur.com/a/YmSJw

public static void main(String[] args) {
       Game game = new Game();
        try {
            Display.setDisplayMode(new DisplayMode((int) game.getWidth(), (int) game.getHeight()));
            Display.setTitle(WINDOW_TITLE);
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }
        game.init();

        while(!Display.isCloseRequested()) {
            
            poll(getDelta());
            draw();
            Display.sync(60);
            Display.update();
        }

        Display.destroy();
}

public void initGL() {
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glEnable(GL_LIGHTING);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(LOOK_FIELD, width / height, 1.0f, Player.LOOK_DISTANCE);

        glMatrixMode(GL_MODELVIEW);

        initLighting();
}

public void draw() {
        glLoadIdentity();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        drawHUD(width, height);

        gluLookAt(player.getX(), player.getY(), player.getZ(),
                player.getTargetX(), player.getTargetY(), player.getTargetZ(),
                0.0f, 1.0f, 0.0f);

        drawAxes();
        drawFloor();
        drawMainShape();

        positionLight();
}

public static void initLighting() {
        float[] ambientV = {0.5f, 0.5f, 0.5f, 1.0f};
        float[] diffuseV = {1.0f, 0.0f, 0.0f, 1.0f};
        FloatBuffer ambient = BufferUtils.createFloatBuffer(4).put(ambientV);
        FloatBuffer diffuse = BufferUtils.createFloatBuffer(4).put(diffuseV);
        glLight(GL_LIGHT1, GL_AMBIENT, (FloatBuffer)ambient.flip());
        glLight(GL_LIGHT1, GL_DIFFUSE, (FloatBuffer)diffuse.flip());

        glEnable(GL_LIGHT1);
}

public static void positionLight() {
        float[] position = {0.0f, 0.0f, 0.0f, 1.0f};
        FloatBuffer lightPosition = BufferUtils.createFloatBuffer(4).put(position);
        glLight(GL_LIGHT1, GL_POSITION, (FloatBuffer)lightPosition.flip());
}

Fool Running

My best guess (without being able to compile and run your code), is to call positionLight() before drawAxes().

It's also possible that the normals on your floor are wrong (causing the light to look like it is facing that direction).

Other then that, it's hard for us to guess without the rest of your code.  ;D
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

hemoid

QuoteMy best guess (without being able to compile and run your code), is to call positionLight() before drawAxes().

I tried not drawing the axes at all, but the effect is the same.

QuoteIt's also possible that the normals on your floor are wrong (causing the light to look like it is facing that direction).

I have enabled GL_CULL_FACE, so my assumption is that if I can see the shape, then the normal is in the right direction. Is this a correct assumption?

QuoteOther then that, it's hard for us to guess without the rest of your code.

If it will help the cause, I will gladly post all my code so that you may run this.

// WINDOW class
package com.bacon.lwjgl.executable;

import com.bacon.lwjgl.Game;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;

public class Window {

    public static final String WINDOW_TITLE = "TEST-lwjgl";

    public void start() {
        Game game = new Game();
        try {
            Display.setDisplayMode(new DisplayMode((int) game.getWidth(), (int) game.getHeight()));
            Display.setTitle(WINDOW_TITLE);
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }
        game.init();

        while(!Display.isCloseRequested()) {
            game.gameLoopBody();
            Display.sync(60);
            Display.update();
        }

        Display.destroy();
    }

    public static void main(String[] args) {
        Window window = new Window();
        window.start();
    }
}

// GAME class
package com.bacon.lwjgl;

import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.gluLookAt;
import static org.lwjgl.util.glu.GLU.gluPerspective;

public class Game {

   // defaults
    public static final float DEFAULT_WIDTH = 800.0f;
    public static final float DEFAULT_HEIGHT = 600.0f;
    public static final double MOUSE_SENSITIVITY = 0.0005;
    public static final float LOOK_FIELD = 70.0f;

    // time related fields
    private long lastTime;

    // other
    private Player player;
    private float width;
    private float height;

    public Game(float width, float height) {
        this.player = new Player();
        this.width = width;
        this.height = height;
    }

    public Game() {
        this(DEFAULT_WIDTH, DEFAULT_HEIGHT);
    }

    public float getWidth() {
        return width;
    }

    public float getHeight() {
        return height;
    }

    private int getDelta() {
        long time = getTime();
        int delta = (int) (time - lastTime);
        lastTime = time;

        return delta;
    }

    private long getTime() {
        return (Sys.getTime() * 1000) / Sys.getTimerResolution();
    }

    public void init() {
        initGL();
        lastTime = getTime();
    }

    public void gameLoopBody() {
        poll(getDelta());
        draw();
    }

    public void initGL() {
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        glEnable(GL_LIGHTING);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(LOOK_FIELD, width / height, 1.0f, Player.LOOK_DISTANCE);

        glMatrixMode(GL_MODELVIEW);

        Artist.initLighting();
        //glColorMaterial (GL_FRONT, GL_EMISSION) ;
        //glEnable (GL_COLOR_MATERIAL) ;
    }

    public void draw() {
        glLoadIdentity();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        Artist.drawHUD(width, height);

        gluLookAt(player.getX(), player.getY(), player.getZ(),
                player.getTargetX(), player.getTargetY(), player.getTargetZ(),
                0.0f, 1.0f, 0.0f);


        Artist.drawAxes();
        Artist.drawFloor();
        //Artist.drawCircleApproximation(4.0f, 20);
        Artist.drawMainShape();

        Artist.positionLight();
    }

    /**
     * Polls controller
     * @param delta - millis since last frame
     */
    public void poll(int delta) {
        // only perform operations if Mouse is grabbed
        if (Mouse.isGrabbed()) {
            // move player
            pollKeyboard(delta);
            // move target
            pollMouse(delta);
        } else if (Mouse.isButtonDown(0)) {
            Mouse.setGrabbed(true);
        }
    }

    /**
     * Poll for mouse movement
     * @param delta - millis since last frame
     */
    private void pollMouse(int delta) {
        int mouseDX = Mouse.getDX();
        int mouseDY = Mouse.getDY();
        if (mouseDX != 0) {
            player.rotateHorizontal(mouseDX * MOUSE_SENSITIVITY, delta);
        }
        if (mouseDY != 0) {
            player.rotateVertical(mouseDY * MOUSE_SENSITIVITY, delta);
        }
    }

    /**
     * Poll keyboard for key presses
     * @param delta - millis since last frame
     */
    private void pollKeyboard(int delta) {
        // hold keys
        if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
            Mouse.setGrabbed(false);
            return;
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
            player.moveForward(delta);
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
            player.moveBackward(delta);
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
            player.moveLeft(delta);
        }
        if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
            player.moveRight(delta);
        }
        // press once keys
        while(Keyboard.next()) {
            if (Keyboard.getEventKey() == Keyboard.KEY_F11) {
                if (Keyboard.getEventKeyState()) {
                        glPolygonMode(GL_FRONT, GL_LINE);
                }
            } else if (Keyboard.getEventKey() == Keyboard.KEY_F12) {
                if (Keyboard.getEventKeyState()) {
                        glPolygonMode(GL_FRONT, GL_FILL);
                }
            }
        }
    }
}

// PLAYER class
package com.bacon.lwjgl;

public class Player {

    // constants
    public static final float LOOK_DISTANCE = 100.0f;
    public static final float MOVE_SPEED = 0.02f;

    // defaults
    public static final float DEFAULT_STARTING_X = 5.0f;
    public static final float DEFAULT_STARTING_Z = 1.0f;
    public static final float DEFAULT_PLAYER_HEIGHT = 2.0f;
    public static final double DEFAULT_STARTING_HOR_ANGLE = Math.PI;
    public static final double DEFAULT_STARTING_VER_ANGLE = 0.0;

    // position fields
    private float x;
    private float y;
    private float z;

    // lookingAt fields
    private double theta;
    private double sigma;
    private float targetX;
    private float targetY;
    private float targetZ;

    // player properties
    private int hp;

    public Player() {
        x = DEFAULT_STARTING_X;
        y = DEFAULT_PLAYER_HEIGHT;
        z = DEFAULT_STARTING_Z;

        theta = DEFAULT_STARTING_HOR_ANGLE;
        sigma = DEFAULT_STARTING_VER_ANGLE;
        updateTarget();
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }

    public float getZ() {
        return z;
    }

    public float getTargetX() {
        return targetX;
    }

    public float getTargetY() {
        return targetY;
    }

    public float getTargetZ() {
        return targetZ;
    }

    // moving commands

    public void rotateHorizontal(double theta, int delta) {
        this.theta += theta * delta;
        // keep theta in range 0 - 2Pi
        double twoPI = 2.0 * Math.PI;
        if (this.theta < 0 || this.theta >= twoPI) {
            this.theta = this.theta % twoPI;
        }
        updateTarget();
    }

    public void rotateVertical(double sigma, int delta) {
        this.sigma += sigma * delta;
        if (this.sigma >= Math.PI/2.0) {
            // surpassed maximum vertical rotation
            this.sigma = Math.PI/2.0 - 0.001;
        } else if (this.sigma <= -Math.PI/2.0) {
            // surpassed minimum vertical rotation
            this.sigma = -Math.PI/2.0 + 0.001;
        }
        updateTarget();
    }

    public void moveForward(int delta) {
        x += Math.cos(theta) * MOVE_SPEED * delta;
        z += Math.sin(theta) * MOVE_SPEED * delta;
        updateTarget();
    }

    public void moveBackward(int delta) {
        x -= Math.cos(theta) * MOVE_SPEED * delta;
        z -= Math.sin(theta) * MOVE_SPEED * delta;
        updateTarget();
    }

    public void moveLeft(int delta) {
        double theta = this.theta + Math.PI/2; // move 90 degrees the left

        x -= Math.cos(theta) * MOVE_SPEED * delta;
        z -= Math.sin(theta) * MOVE_SPEED * delta;
        updateTarget();
    }

    public void moveRight(int delta) {
        double theta = this.theta + Math.PI/2; // move 90 degrees the right

        x += Math.cos(theta) * MOVE_SPEED * delta;
        z += Math.sin(theta) * MOVE_SPEED * delta;
        updateTarget();
    }

    private void updateTarget() {
        /*
        x = r * cos(theta) * cos(sigma)
        y = r * sin(theta) * cos(sigma)
        z = r * sin(sigma)
         */
        targetX = x + LOOK_DISTANCE * new Float(Math.cos(theta) * Math.cos(sigma));
        targetY = LOOK_DISTANCE * new Float(Math.sin(sigma)) + DEFAULT_PLAYER_HEIGHT;
        targetZ = z + LOOK_DISTANCE * new Float(Math.sin(theta) * Math.cos(sigma));
    }

}

// ARTIST class (bad name)

package com.bacon.lwjgl;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

import java.nio.FloatBuffer;

import static org.lwjgl.opengl.GL11.*;

public class Artist {

    public static void drawHUD(float width, float height) {
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        glOrtho(0, width, height, 0, -1, 1);
        glMatrixMode(GL_MODELVIEW);

        float midX = width/2.0f;
        float midY = height/2.0f;
        float lineLength = height/50.0f;
        glBegin(GL_LINES);
            glColor3f(1.0f, 1.0f, 1.0f);
            // x
            glVertex2f(midX-lineLength/2-lineLength, midY);
            glVertex2f(midX-lineLength/2, midY);
            glVertex2f(midX+lineLength/2+lineLength, midY);
            glVertex2f(midX+lineLength/2, midY);
            // y
            glVertex2f(midX, midY-lineLength/2-lineLength);
            glVertex2f(midX, midY-lineLength/2);
            glVertex2f(midX, midY+lineLength/2+lineLength);
            glVertex2f(midX, midY+lineLength/2);
        glEnd();

        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
    }

    public static void drawMainShape() {
        glBegin(GL_TRIANGLES);
            // face 1
            glColor3f(1.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 10.0f, 5.0f);
            glColor3f(1.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 10.0f, 10.0f);
            glColor3f(1.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 5.0f, 10.0f);
            // face 2
            glColor3f(0.0f, 1.0f, 1.0f);
            glVertex3f(-2.5f, 10.0f, 5.0f);
            glColor3f(0.0f, 1.0f, 1.0f);
            glVertex3f(-2.5f, 5.0f, 10.0f);
            glColor3f(0.0f, 1.0f, 1.0f);
            glVertex3f(-2.5f, 10.0f, 10.0f);
            // squ 1a
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 10.0f, 5.0f);
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 5.0f, 10.0f);
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(-2.5f, 5.0f, 10.0f);
            // are 1b
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(-2.5f, 5.0f, 10.0f);
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(-2.5f, 10.0f, 5.0f);
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 10.0f, 5.0f);
            // squ 2a
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(-2.5f, 5.0f, 10.0f);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(0.0f, 5.0f, 10.0f);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(0.0f, 10.0f, 10.0f);
            // are 2b
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(0.0f, 10.0f, 10.0f);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(-2.5f, 10.0f, 10.0f);
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(-2.5f, 5.0f, 10.0f);
        glEnd();
    }

    public static void drawAxes() {
            glBegin(GL_LINES);
            // x-axis
            glColor3f(1.0f, 0.0f, 0.0f);
            glVertex3f(-10000.0f, 0.0f, 0.0f);
            glVertex3f(10000.0f, 0.0f, 0.0f);
            // y-axis
            glColor3f(0.0f, 1.0f, 0.0f);
            glVertex3f(0.0f, -10000.0f, 0.0f);
            glVertex3f(0.0f, 10000.0f, 0.0f);
            // z-axis
            glColor3f(0.0f, 0.0f, 1.0f);
            glVertex3f(0.0f, 0.0f, -10000.0f);
            glVertex3f(0.0f, 0.0f, 10000.0f);
        glEnd();
    }

    public static void drawFloor() {
        float sizeOfEdge = 1.0f;
        float red = 0.0f;
        float green = 0.0f;
        glBegin(GL_QUADS);
            for (float x = 50.0f; x > -50.0f; x -= sizeOfEdge) {
                for (float z = 50.0f; z > -50.0f; z -= sizeOfEdge) {
                    glColor3f(red, green, 1.0f);
                    glVertex3f(x, 0.0f, z);
                    glVertex3f(x, 0.0f, z-sizeOfEdge);
                    glVertex3f(x-sizeOfEdge, 0.0f, z-sizeOfEdge);
                    glVertex3f(x-sizeOfEdge, 0.0f, z);
                    //red = (red+0.1f) % 1.1f;
                }
                //red = 0.0f;
                //green = (green+0.1f) % 1.1f;
            }
        glEnd();
    }

    public static void drawCircleApproximation(float radius, int numberOfSides) {
        glBegin(GL_TRIANGLE_FAN);
            glVertex2f(0.0f, 0.0f);
            for(double theta = 0.0; theta < 2*Math.PI; theta += 2*Math.PI/numberOfSides) {
                glVertex2f(new Float(Math.cos(theta))*radius, new Float(Math.sin(theta))*radius);
            }
            glVertex2f(new Float(Math.cos(0.0)) * radius, new Float(Math.sin(0.0)) * radius);
        glEnd();
    }

    public static void initLighting() {
        float[] ambientV = {0.5f, 0.5f, 0.5f, 1.0f};
        float[] diffuseV = {1.0f, 0.0f, 0.0f, 1.0f};
        //float[] blackV = {0.0f, 0.0f, 0.0f, 1.0f};
        FloatBuffer ambient = BufferUtils.createFloatBuffer(4).put(ambientV);
        FloatBuffer diffuse = BufferUtils.createFloatBuffer(4).put(diffuseV);
        //FloatBuffer specular = BufferUtils.createFloatBuffer(4).put(whiteV);
        glLight(GL_LIGHT1, GL_AMBIENT, (FloatBuffer)ambient.flip());
        glLight(GL_LIGHT1, GL_DIFFUSE, (FloatBuffer)diffuse.flip());
        //glLight(GL_LIGHT0, GL_SPECULAR, (FloatBuffer)specular.flip());

        glEnable(GL_LIGHT1);
    }

    public static void positionLight() {
        float[] position = {0.0f, 0.0f, 0.0f, 1.0f};
        FloatBuffer lightPosition = BufferUtils.createFloatBuffer(4).put(position);
        glLight(GL_LIGHT1, GL_POSITION, (FloatBuffer)lightPosition.flip());
    }
}


Thanks!

hemoid

3DWalker

If you can see that side of the face the normals are at least close, but for example 0.2F 1F 0.2F would still make the top look solid but the normals should be facing a little diagonal. Since you havn't used GL11.glNormal3f(float xnormal, float ynormal, float znormal) you may have a situation where the normals are not right. you may not be though. but before setting your vertices in DrawFloor, add GL11.glNormal3f(0.0F, 1.0F, 0.0F).
The statement below is true.

The statement above is false.

Fool Running

Quote from: hemoid on October 05, 2011, 18:11:54
QuoteMy best guess (without being able to compile and run your code), is to call positionLight() before drawAxes().

I tried not drawing the axes at all, but the effect is the same.

Sorry, what I meant, is to put the setting of the light position before you draw stuff. OpenGL is a state machine, so if the position isn't set when you draw, then it will be at it's default value. You are probably getting lucky because the position you are setting the light to is probably the default position, but if you ever want to move it, then you need to set it before drawing stuff.

I'm guessing 3DWalker is right. You are not setting any normals for the floor, so they are all pointing in some default direction (which is probably along the +z axis).
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

hemoid

Thank you 3DWalker. Setting the normal in the +y direction solved the problem. Does this mean that for every shape I draw, I have to ensure each vertex's normal is to be set to point perpendicular to the face I'm drawing? Why isn't openGL able to figure that out based on the order in which I create the vertexes?

Great, thanks for the advice Fool Running. I will make sure to position my light right after gluLookAt to avoid any future issues.

Chuck

Yep, you do have to set the normal for every vertex.  Winding determines front or back facing, which intuitively makes you think of normals, but it's not actually the same thing.  The only thing that sets normals automatically is evaluator meshes.

hemoid

Ok perfect. I did not realize that normal direction was unrelated to winding. I suppose the next step is looking into evaluator meshes. Thank you for the clarification and direction!

Chuck

Quote from: hemoid on October 05, 2011, 23:10:56
I suppose the next step is looking into evaluator meshes.

Evaluators are seriously hairy things that were dropped in Opengl 3.1+.  It's fun to play with Bézier curves and NURBS, but long run you're better off using or writing some utility that generates a VBO for you with the normals included, using triangle strips for maximum compatibility.



3DWalker

I make my models in a 3d progaram and export as .obj format. Then I use a OBJLoader to take that mesh and render it in the game. I have one that's not really working the best, however I'm editing it. I may post the code what it's done. Most OBJ format loaders use the GL11.glGenList(1) and GL11.glNewList() to store all the data. That was giving me errors with the GenList part, so I made one that just stores it in arrays, which is just as fast as rendering the mesh normally because the only other speed decrease is a "for(face f:faces)"

Here is a rudementary version of how to load an object file and render it:

package org.ic3d.render;

import java.io.*;

import org.lwjgl.opengl.GL11;

public class ObjReader {
	
	public ObjReader(String file){
		try{
			BufferedReader reader = new BufferedReader(new FileReader(new File(file)));
			parseFile(reader);
		}catch(IOException 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 IllegalStateException If the obj file is malformed and vertice are added to a face of different shape (tri - quad)
	 * @throws IOException - If the obj file can not be read for any reason.
	 */
	public void parseFile(BufferedReader br) throws IllegalStateException, IOException
	{
		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]));
				
				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]));
				
				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());
					
					appendVert(temp, v);
				}
				try
				{
					Face f = new Face(temp.length==3?Face.GL_FACE_TRI:Face.GL_FACE_QUAD, temp);
					if(temp.length==3){
						appendFace(t_faces, f);
					}else{
						appendFace(q_faces, f);
					}
				}
				catch(IllegalStateException e)
				{
					throw e;
				}
			}
		}
	}
	private void appendVert(Vertex[] l, Vertex v)
	{
		for(int i=0;i<l.length;i++)
		{
			if(l[i]==null)
			{
				l[i] = v;
			}
		}
		
		System.out.println("Vertex[] can only hold "+l.length+" Vertices at one time");
		
	}
	private void appendFace(Face[] l, Face f)
	{
		for(int i=0;i<l.length;i++)
		{
			if(l[i]==null)
			{
				l[i] = f;
			}
		}
		
		System.out.println("Vertex[] can only hold "+l.length+" Vertices at one time");
		
	}
	
	public void renderTri(Face f, float x, float y, float z)
	{
		
		Vertex[] v = f.getVerts();
		
		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());
		
	}
	
	public void renderQuad(Face f, float x, float y, float z)
	{
		
		Vertex[] v = f.getVerts();
		
		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());
		
	}
	
	public void render(float x, float y, float z)
	{
		//Push matrix out of the way so we can edit the scene and then load
		//The current matrix settings
		GL11.glPushMatrix();
		GL11.glBegin(GL11.GL_QUADS)l
		for(Face f : q_faces)
		{
			if(f==null)
			{
				break;
			}
			else
			{
				renderQuad(f, x, y, z);
			}
		}
		GL11.glBegin(GL11.GL_TRIANGLES)l
		for(Face f : T_faces)
		{
			if(f==null)
			{
				break;
			}
			else
			{
				renderTri(f, x, y, z);
			}
		}
		GL11.glEnd();
		GL11.glPopMatrix();
	}
	
	public int listid=0;
	
	public Face[] q_faces = new Face[15000];
	public Face[] t_faces = new Face[15000];
	
	public class Face{
		/**
		 * 
		 * 
		 * @param shape
		 * @param verts
		 * @throws IllegalStateException - If the number of vertice in the Vertex[] is not equal to the face type set.
		 */
		public Face(int shape, Vertex[] vertlist) throws IllegalStateException{
			
			int vert_n = GL_FACE_NONE-shape;
			
			if(vertlist.length>vert_n){
				throw new IllegalStateException(vert_n+" Vertice faces can not hold "+verts.length+" vertices");
			}
			if(vertlist.length<vert_n){
				throw new IllegalStateException(vert_n+" Vertice faces must hold "+vert_n+" vertice, not "+verts.length+" vertices");
			}
			if(vert_n!=3 && vert_n!=4){
				throw new IllegalStateException("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 int GL_FACE_QUAD = 3;
		public static int GL_FACE_TRI = 4;
		public static int GL_FACE_NONE = 7;
		
		private int type=7;
		
	}
	
	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;
		
	}
}


Hope this helps some, just put that in and then either use external files, or modify the constructor to use internal files. Only load the object once somewhere around the GL initializing, then just call objectname.render().
The statement below is true.

The statement above is false.

hemoid

Quote from: Chuck on October 05, 2011, 23:43:18
Evaluators are seriously hairy things that were dropped in Opengl 3.1+.

Oh wow. In that case looks like I should start investing time in building my library of utils. Thanks for the advice.

3DWalker: Thanks for that bit of code. I had previously browsed through methods of getting OBJ files working into openGL. I'm definitely going to move in that direction very soon, so I will make great use of this OBJLoader. If you could post the completed code when you get around to finishing, that would be additionally helpful. (Off topic: Could you also recommend a 3d modeling program for game dev?)

hemoid

3DWalker

Actually. What I did was while I was writing that post I edited the code. So that really is the completed one. Tested it and it worked very well for me. To avoid needing multiple classes I combined all classes in one file, so Face and Vertex classes are already existent from that one script.
The statement below is true.

The statement above is false.