Differences in specified and actual coordinates of primitives

Started by Simeon, August 06, 2007, 22:26:33

Previous topic - Next topic

Simeon

Hey all, I'm having a problem with the coordinates of primitives that I'm drawing in orthogonal mode. I noticed this when I started using the FengGUI library (it results in misaligned widgets, see topic at FengGUI forums). They thought it could be a problem with LWJGL so I'll give it a try here. I'm running LWJGL 1.1 and Windows Vista 32-bit. I'm using Java 6 but changing that to Java 5 didn't produce different results.

When I draw lines (or other primitives) I noticed differences in the specified coordinates and the coordinates that LWJGL's Mouse class returns when I click on them. For example, when I run the following code:

GL11.glBegin(GL11.GL_LINE_LOOP);
			{
				GL11.glVertex2i(10, 10);
				GL11.glVertex2i(100, 10);
				GL11.glVertex2i(100, 100);
				GL11.glVertex2i(10, 100);
			}
			GL11.glEnd();


and then I click on the corners of this square, it reports the following coordinates respectively: (9,9), (99, 9), (99, 99) and (9, 99). Also, the bottomleft pixel of that square isn't there so it's almost a square. But when I run the the same code in GL_LINES, the coordinates become (10, 9), (99, 9), (99, 99) and (10, 99). And when I run using GL_QUADS, the coordinates become (10, 10), (99, 10), (99, 99), (10, 99).

Anyone have an idea what could be causing this?

Below is an example class that exhibits the aforementioned behaviour on my machine. Take note that I've just gathered the code from various classes in my program so it might look a bit messy ;)

import org.fenggui.Button;
import org.fenggui.composites.Window;
import org.fenggui.render.lwjgl.LWJGLBinding;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

final public class Program {

	private boolean running = true;
	private int screenWidth = 1024;
	private int screenHeight = 768;
	private boolean leftDown = false; 
	private org.fenggui.Display fengDisplay;
	
	public static void main(String[] args) {
		try {
			Program program = new Program();
			program.init();
			program.gameLoop();		
			program.terminateProgram();
		} catch (LWJGLException e) {
			e.printStackTrace();
		}
	}
	
	public void init() throws LWJGLException {
		Display.setTitle("Placeholder");
		DisplayMode displayMode = getDisplayMode(screenWidth, screenHeight, 60, 32);
		Display.setDisplayMode(displayMode);
		Display.setVSyncEnabled(true);
		Display.setFullscreen(false);
		Display.create();
		
		GL11.glViewport(0, 0, screenWidth, screenHeight);
		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GL11.glOrtho(-32.0f, 32.0f, -32.0f, 32.0f, -512.0f, 512.0f);
        
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        
        GL11.glClearColor(0.45f, 0.0f, 0.0f, 0.0f);
        
        GL11.glEnable(GL11.GL_CULL_FACE);
        
        GL11.glClearDepth(1.0f);
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glDepthFunc(GL11.GL_LEQUAL);
        
        GL11.glEnable(GL11.GL_CULL_FACE);
        
        fengDisplay = new org.fenggui.Display(new LWJGLBinding());
        Button button = new Button("Button");
        button.setXY(200, 200);
        button.setSizeToMinSize();
        fengDisplay.addWidget(button);
        
        Window testWindow = new Window();
        testWindow.setXY(300, 300);
        testWindow.setWidth(100);
        testWindow.setHeight(100);
        fengDisplay.addWidget(testWindow);
        
        fengDisplay.layout();
	}
	
	private void gameLoop(){
		while (running) {
			Display.update();
			if (Display.isCloseRequested()) {
				terminateProgram();
			} else if (Display.isActive()) {
				logic();
				render();
				Display.sync(60);
			} else {
				try {
					Thread.sleep(1 / 60);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				logic();
				if (Display.isVisible() || Display.isDirty()) {
					render();
				}
			}			
		}
	}
	
	private void terminateProgram() {
		Display.destroy();
		System.exit(0);
	}
	
	public void logic() {
		if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
			running = false;
		}
		if (Mouse.isButtonDown(0) && !leftDown) {
			System.out.println("(" + Mouse.getX() + ", " + Mouse.getY() + ")");
			leftDown = true;
		} else if (!Mouse.isButtonDown(0) && leftDown) {
			leftDown = false;
		}
	}
	
	public void render() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		GL11.glLoadIdentity();
		
		GL11.glDisable(GL11.GL_DEPTH_TEST);
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, screenWidth, 0, screenHeight, -1, 1);
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		
		GL11.glColor3f(1.0f, 1.0f, 0.0f);
		
		// here's where I get different results when I change GL11.GL_QUADS to something else
		GL11.glBegin(GL11.GL_QUADS);
		{
			GL11.glVertex2i(10, 10);
			GL11.glVertex2i(100, 10);
			GL11.glVertex2i(100, 100);
			GL11.glVertex2i(10, 100);
		}
		GL11.glEnd();
		
		fengDisplay.display();
	}
	
	public DisplayMode getDisplayMode(int width, int height, int frequency, int bitsPerPixel) throws LWJGLException {
		DisplayMode[] displayModes = Display.getAvailableDisplayModes();
		for (int i = 0; i < displayModes.length; i++) {
			DisplayMode k = displayModes[i];
			if (k.getWidth() == width
					&& k.getHeight() == height
					&& k.getFrequency() == frequency
					&& k.getBitsPerPixel() == bitsPerPixel) {
				return k;
			}
		}
		return null;
	}
}
Weeks of programming can save you hours of planning.

Simeon

Take note that I'm not saying the Mouse class is reporting wrong coordinates because when I take screenshots and compare the number of pixels between the primitive and the edge of the window, there's an actual difference of 1 pixel.
Weeks of programming can save you hours of planning.

Orangy Tang

Getting exact rasterisation can be a little tricky, since exactly how coords are mapped to pixels is different depending on the primative being drawn. This ( http://msdn2.microsoft.com/en-us/library/ms537007.aspx ) is a pretty good explaination of one workaround.

Also see 14.120 and 14.090 from here: http://www.opengl.org/resources/faq/technical/rasterization.htm

Simeon

Thanks for the links! The second one gave me an idea of why this is happening. The workarounds (using 0.375 rotations and drawing at half coordinates) mentioned in the first one did have effect on primitives that I'm drawing myself yet the main reason I'm asking this is because of the FengGUI library. The other parts in the game don't need exact pixel precision when drawing but in this case, it screws the drawing of the GUI widgets. Here's the screenshot that I've posted on their forums:



Someone else on their forums reported a similar problem as well (and a problem with the text of the widgets, see here and notice the similar close button and bottom line of the window).
Weeks of programming can save you hours of planning.

Fool Running

What graphics card are you using? I just tested the code you pasted, on my machine, and it works fine (although I did notice that the mouse coordinates were off like you said ;D)
I ran into the same problem when trying to get FengGUI to scale with the screen resolution (when scaled the border didn't line up correctly).
Right now I'm in the same boat you are... trying to figure out what to do about it. ;D
I'm trying PixmapBorders right now to see if that helps at all...

EDIT:
Well it seems like FengGUI handles PixmapBorders a lot better then line borders. I didn't see any artifacts from stretching the guis so you might try that instead. The only problem is that now you have to create a pixmap border for everything. :-\
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D