Using glu.lookAt messes up my code

Hello! I got it to work for 5 mins, thenit stopped… argh I am tearing my hair right now… Why won´t this code work (look at the render method). Basically I am drawing all quads on zAxis = 0.0f and then I want to look at point 0,0,0 with the camera… Why does this display just black? Anyone?



import org.lwjgl.Display;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.*;

public class Schlug implements Runnable {

  private boolean fullscreen, finished;
  private org.lwjgl.DisplayMode displayMode = null;
  private String displayName;
  private float camX, camY, camZ;
  private double frameTime, temp;
  public static GL gl;
  private static GLU glu;
 
  private Player you;
  private Structure repair;
  private Vehicle Tank;
  
  private java.util.Vector textures;
  
  private java.util.Vector textureNames; 
  
  // Starting it all
  
  public static void main(String args[]) { new Schlug(); }
  
  
  // Creates a new Game Object
  
  public Schlug() {    
      
      camZ = -15;


      gl = new GL();
      glu = new org.lwjgl.opengl.GLU(gl);
      textures = new java.util.Vector();
      textureNames = new java.util.Vector();

      displayName = "Schlug Attuck";

      Thread mainThread = new Thread(this, "Main");
      mainThread.start();
  }

  
  // Thread start

  public void run() {

      try {
          initGL();

          you = new Player(getTexID("soldier"));
          repair = new Structure(gl, getTexID("repairBay"));
          Tank = new Vehicle(getTexID("tank"));
          
          // TEMP enter the tank
          you.setVehicle(Tank);
          
          System.gc();
 
          while(!finished) {
              
              // Calculate frame Time
              frameTime = (Sys.getTime() - temp)/Sys.getTimerResolution();        
              temp = Sys.getTime();
 
              Keyboard.poll();
              org.lwjgl.input.Mouse.poll();
              processControls();
              render();
              gl.swapBuffers();
          }
      }

      catch (Exception e) { }
      finally { cleanup(); }
}


  // Render method aka paint
  
  private void render() {
      
    gl.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
    
    gl.loadIdentity();
    
    glu.lookAt(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, camZ, 0.0f, 0.0f, 1.0f);

    gl.enable(GL.BLEND); 
    gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
    gl.texEnvf(GL.TEXTURE_ENV, GL.TEXTURE_ENV_MODE, GL.MODULATE);
    
    gl.enable(GL.TEXTURE_2D);
    
    
    
    you.setPos(you.getX(), you.getY(), 0.0f);    
    // you.draw(frameTime);
    gl.loadIdentity();
    
    Tank.draw(frameTime);
    
    
    
    
    // drawBackground();
    
    
    
    // repair.setPos(0.0f, 0.0f, camZ);
    // repair.draw();
    
    
    
        
    gl.disable(GL.TEXTURE_2D);
    gl.disable(GL.BLEND);
  }
  
  private void drawBackground() {
      
    gl.loadIdentity();
      
    gl.color3ub((byte) 39, (byte) 47, (byte) 41);
    
    gl.begin(GL.QUADS);
        gl.vertex3f(0.0f, 0.0f, 0.0f);
        gl.vertex3f(200.0f, 0.0f, 0.0f);
        gl.vertex3f(200.0f, 200.0f, 0.0f);
        gl.vertex3f(0.0f, 200.0f, 0.0f);
    gl.end();
    
    gl.color3ub((byte) 255, (byte) 255, (byte) 255);
  }
  
  private void setViewCenter(float x, float y) {
      camX = -x;
      camY = -y;
  }
  
  // Initialize Opengl
  
  public void initGL() throws Exception {
      
    org.lwjgl.DisplayMode[] modes = Display.getAvailableDisplayModes();
    for (int i = 0; i < modes.length; i++) {
        if (modes[i].width == 1024 && modes[i].height == 768 && modes[i].bpp == 32) {
            displayMode = modes[i];
            break;
        }
    }
      
    Display.create(displayMode, fullscreen, displayName);
    gl.create();
    org.lwjgl.input.Mouse.create();
    Keyboard.create();

    gl.shadeModel(GL.SMOOTH); // Enable Smooth Shading
    gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
    gl.clearDepth(1.0); // Depth Buffer Setup
    gl.enable(GL.DEPTH_TEST); // Enables Depth Testing
    gl.depthFunc(GL.LEQUAL); // The Type Of Depth Testing To Do
    gl.matrixMode(GL.PROJECTION); // Select The Projection Matrix

    // Calculate The Aspect Ratio Of The Window
    
    glu.perspective(45.0f, (float) Display.getWidth() / (float) Display.getHeight(), 0.1f, 100.0f);
    gl.matrixMode(GL.MODELVIEW); // Select The Modelview Matrix

    gl.hint(GL.PERSPECTIVE_CORRECTION_HINT, GL.NICEST); // Really Nice Perspective Calculations

    gl.wglSwapIntervalEXT(1); // Minimum number of frames before swapping
    
    
    loadTexture("data/soldier.png", "soldier", true);
    loadTexture("data/repair.png", "repairBay", true);
    loadTexture("data/tank.png", "tank", true);
  }
  
  // Takes care of ALL keyboard & mouse events
  
  public void processControls() {
      
      if (Keyboard.isKeyDown(Keyboard.KEY_R)) camZ -= 10 * (float)frameTime;      
      if (Keyboard.isKeyDown(Keyboard.KEY_F)) camZ += 10 * (float)frameTime;
      if (Keyboard.isKeyDown(Keyboard.KEY_W)) you.accelerate(true);
      if (Keyboard.isKeyDown(Keyboard.KEY_S)) you.accelerate(false);
      if (Keyboard.isKeyDown(Keyboard.KEY_A)) you.setAngle(you.getAngle() + you.getAngleSpeed());
      if (Keyboard.isKeyDown(Keyboard.KEY_D)) you.setAngle(you.getAngle() - you.getAngleSpeed());     
      if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) finished = true;

      if (Keyboard.isKeyDown(Keyboard.KEY_F1)) {
        fullscreen = !fullscreen;
        cleanup();
        try {  initGL(); }
        catch (Exception ex) {
            System.out.println("Error initializing Opengl: switching fullscreen mode.");
            System.exit(0);
        }
    }
  }
  
  public void loadTexture (String path, String name, boolean alpha) {
      
      java.awt.image.BufferedImage tex = null;
            
                try {
                    tex = javax.imageio.ImageIO.read(new java.io.File(path));
                }
                catch (Exception e) {
                
                    System.out.println("Error: " + e);
                    System.exit(0);
                }
      
                // Put Image In Memory
            java.nio.ByteBuffer scratch = java.nio.ByteBuffer.allocateDirect(4 * tex.getWidth() * tex.getHeight());
            int scratchAddress = org.lwjgl.Sys.getDirectBufferAddress(scratch);
                
                byte data[] = (byte[])tex.getRaster().getDataElements(0, 0, tex.getWidth(), tex.getHeight(), null);
            scratch.clear(); 
            scratch.put(data);
                
                 // Create A IntBuffer For Image Address In Memory            
            java.nio.IntBuffer buf = java.nio.ByteBuffer.allocateDirect(4).order(java.nio.ByteOrder.nativeOrder()).asIntBuffer();
            int bufPtr = org.lwjgl.Sys.getDirectBufferAddress(buf);
            gl.genTextures(1, bufPtr);
                
                gl.bindTexture(GL.TEXTURE_2D, buf.get(0));

            gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
            gl.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);

                if(alpha) gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, tex.getWidth(), tex.getHeight(), 0, GL.RGBA, GL.UNSIGNED_BYTE, scratchAddress);
                else gl.texImage2D(GL.TEXTURE_2D, 0, GL.RGB, tex.getWidth(), tex.getHeight(), 0, GL.RGB, GL.UNSIGNED_BYTE, scratchAddress);

            addTexID(name, buf.get(0));
  }
  
  private int getTexID(String name) {
      Integer temp = new Integer(((textures.get(textureNames.indexOf(name)))).toString());
      return temp.intValue();
  }
  
  private void addTexID(String name, int id) {
      textures.addElement(new Integer(id));
      textureNames.addElement(name);
  }
  
  
  public void cleanup() {
    Keyboard.destroy();
    org.lwjgl.input.Mouse.destroy();
    gl.destroy();
    Display.destroy();
  }
}



You’ll have to forgive me if I don’t have the time or energy to wade through your code to find out what’s wrong. What I do know is this:

Immediate mode APIs do not have the concept of a camera. Everything is rendered in relation to the 0,0,0 coordinates. Depending on how the 3D transformation matrix is set up, you may be “looking” in different directions other than the default 0,0,-1 or 0,0,1. Scenegraph APIs introduce the concept of a camera by taking the location and rotation coordinates of the “Camera” object, and applying them to each object before it’s rendered. In this way, you “feel” like you’re moving through the world when in fact, the world is moving around you (rather egotistical, eh?).

So what does this all mean? It means that serious 3D engines need to be a some form of a scenegraph. This way they can translate and rotate every world object during GL or D3D drawing. To learn more on the subject, try writing a software renderer and you’ll learn all about the math behind 3D rendering.

I’ve not been through your code either ::slight_smile: but I think you might need to change your gluLookAt parameters slightly. You’ve got the camera placed at (0, 0, 0), looking towards (0, 0, 1), with the “up” direction being towards (0, 0, 1) as well. The up direction should usually be perpendicular to the direction the camera is looking in.

I don’t know if this is the problem and don’t know how GLU handles an up direction in the same direction as the target, but it’s something you might want to look at (no pun intended).

Ah, just noticed something. You say you want to look at point (0, 0, 0), yet your eye is defined at (0, 0, 0). Maybe you want to move it a bit further away as well?

Also be wary of the near plane of your viewing volume getting behind the drawn quads - that would stop anything being drawn at all.

Yes I know that there is no such thing as a camera :slight_smile: Thing is that there is a method glu.lookAt that transforms the matrices for me so instead of moving the origo, lookAt() helps me with that. And the direction of the up-vector doesnt really matter (I saw that when it did work, before it all messed up).

Yes, I know about GLU. GLU is a set of wrappers around the GL library that are generally less than helpful. I was just pointing out that GLU is most likely attempting to change your default matricies for you and that the best way of handling 3D code is to create a scenegraph and camera implementation of your own.

[quote]And the direction of the up-vector doesnt really matter (I saw that when it did work, before it all messed up).
[/quote]
I’d try fixing the issue I raise in my second post if I were you. Your “virtual camera” is placed on the plane of the quads, with the near plane beyond them. There’s no way that’s going to work.

Edit: Oh, and the up direction (in general) does matter - else how would the drivers know which way up to draw the world? ;D Maybe there’s a rounding error or something that makes it work in the situation you have here, but I really wouldn’t rely on it! :wink:

Ok, thank you you have been quite helpful :slight_smile:
I think I am going to try to use the GLU now as I am not going to do any really advanced 3d app :slight_smile:

Hey i noticed one problem. your calling lookAt after loadIdentity, which is the wrong way round. loadIdentity reverses any changes from lookAt.

Also the parameters in lookAt might be wrong.

glu.lookAt(0.0f, 0.0f, camZ, 0.0f, 0.0f, camZ, 0.0f, 1.0f, 0.0f);

which changes the position and view vectors to the same depth, and sets the up vector to the normal settings.

That should help :slight_smile:

Well, when I do gl.loadIdentity() I load the identity matrix right? Basically I erase everything and put up a new matrix to work with. Then instead of translating the matrix, use the lookAt() to look at a point … Why is this wrong?

 gl.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT); 
     
    gl.loadIdentity(); 
     
    glu.lookAt(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, camZ, 0.0f, 0.0f, 1.0f); 
 
    gl.enable(GL.BLEND);  
    gl.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA); 
    gl.texEnvf(GL.TEXTURE_ENV, GL.TEXTURE_ENV_MODE, GL.MODULATE); 
     
    gl.enable(GL.TEXTURE_2D); 
     
     
     
    you.setPos(you.getX(), you.getY(), 0.0f);     
    // you.draw(frameTime); 
    gl.loadIdentity(); 
     
    Tank.draw(frameTime); 

You call .loadIdentity before lookAt, which is fine. Except before you draw your tank you call load identity again, which wipes you current camera setup. You probably do translatef etc. calls in Tank.draw so you probably want instead to do:

  • loadIdentity
  • lookAt
  • gl.pushMatrix
  • Tank.draw
  • gl.popMatrix

Then Tank.draw can use gl.translatef, rotatef etc. freely and not worry about corrupting the current camera setup for the next object…

Ahh, care to give me an explanation of what push and pop matrix does? I understand what you mean with it, and I did the same thing but just reversed my translations and rotations in tank.draw() …

Each matrix has a stack associated with it, with a depth which depends on your hardware/drivers. Minimum is (off the top of my head) 32 for the MODEL_VIEW matrix (what you’re using for camera and objects) and 2 for texture and projection matrices.
When you push the stack, a copy of the current matrix is pushed onto of the stack, so the topmost and the one below it are now identical. You can then mess with the current transform to setup object position, rotation etc. and once you’re done pop the matrix stack and the matrix is in the same state as before you pushed it (leaving your camera transform as it was originally, ready for the next object).