PDA

View Full Version : Translation within a translation



Tummy
08-15-2008, 03:22 AM
Hello,

I'm working on a 2D game with a playfield that extends beyond the screen. I also have 2D sprites that appear within the playfield. I want:
1 - when the user moves the playfield, the sprites move
2 - when the user moves the sprites, only the sprites move

So basically, the sprites are children of the playfield. I can get the above to work, except, when trying to move the sprite, it does not move to the exact location of the mouse cursor. And as I move the playfield, the sprite gets father away from where the cursor is placed. It appears that the translation of the playfield is affecting the movement of the sprite.

My code (simplified)

push the matrix
translate (playfieldX, playfieldY, 0)
draw the playfield
translate (spriteX, spriteY, 0)
draw the sprite
pop the matrix



I have not tried it yet, but I was wondering if instead of the second translate (for the sprite), I should just draw the sprite to the screen coordinates (320,150). Would this yield the correct result?

Thanks in advance

mikau
08-15-2008, 04:52 AM
I'm no expert, but your code there looks good, assuming thats how you draw one sprite. If you draw several sprites, you should call push and pop before drawing each sprite as well, otherwise your sprite translations will accumulate.

But I think I may know what the problem is. Its probably that the (x,y) values you are getting from the mouse are in window coordinates, and not eye coordinates. See if you can set up a sprite that you can drop exactly where you want with the mouse. If you can't, thats most likely the problem.

According to my understanding (which has allowed me to solve this issue in the past, but correct me if i'm wrong) the following occurs on each vertex you draw: the vertex is multiplied by the modelview matrix, which yields eye coordinates, the eye coordinates are multiplied by the projection matrix. The result of the projection matrix is that it puts everything you told openGL you wanted to see into a canonical viewing volume, a cube that reaches from (-1,-1,-1) to (1,1,1). Only objects that end up inside this region are rasterized (converted to pixels on the screen) all others are clipped out. Finally, the viewport transformation stretches and scales the 2d scene, (Which is a projection of the objects in the cube onto the xy plane, who's corners are at (+-1, +-1) )to fit into a rectangle of a given width and height so it can be drawn to a window.

So a lot happens to a vertex before it ends up as a window pixel. And so if you want to move an object with the mouse, you have to find out what eye coordinates correspond to the window coordinates of the mouse. Its really not so hard when you're just using an ortho2d projection (although the fact that mouse coordinates measure from the top down doesn't help). But there is a routine somewhere designed to help you 'reverse' the process ...somewhere.

anyway, if you want to know how I converted mouse coordinates back to eye coordinates, send me a pm. But other members here may be able to give you a better answer.

Tummy
08-15-2008, 12:53 PM
Thanks for the feedback. I'll give it a try and if I have more questions I'll send you a PM.

mikau
08-15-2008, 06:24 PM
Now that I have some time, here's the basic idea,

if you're using this as your projection transformation:
glOrtho(0, SCENE_WIDTH, 0, SCENE_HEIGHT, -1, 1);

this grabs the region from (0,0) to (SCENE_WIDTH, SCENE_HEIGHT) and linearly maps everything in there into the canonical view volume,the square stretching from (-1,-1,-1) to (1,1,1) (all objects will have a z coordinate of 0 since you're using an orthographic projection) your viewport transformation will linearly map that square region into a rectangle.

So we have that the mapping from eye coordinates to the cannonical view volume (which I believe are called normalized device coordinates) is linear, and the mapping from there to window coordinates is linear, therefore the mapping from eye coordinates to window coordinates is also linear.

So the bottom left corner of your window corresponds to (0,0) in eye coordinates, and the upper right corner, corresponds to (SCENE_WIDTH, SCENE_HEIGHT) in eye coordinates, and everything in between is proportional.

So you really just need to find out what fraction of the distance across the screen your cursor is, and then find what fraction of the distance across the width or height of your scene that is.

For x, thats easy:
fraction = MOUSE_X/VIEWPORT_WIDTH;
cursor_eye_x = SCENE_WIDTH*fraction;

for y, you have to be careful because MOUSE_Y measures from the top down, and so you have to subtract MOUSE_Y from VIEWPORT_HEIGHT to get the distance from the bottom of the window.

fraction = (VIEWPORT_HEIGHT - MOUSE_Y)/VIEWPORT_HEIGHT;
cursor_eye_y = SCENE_HEIGHT*fraction;

so (cursor_eye_x, cursor_eye_y) gives you where your mouse is currently hovering over in eye coordinates.

I'm going to take a few minutes to test what i've written above to make sure my reasoning is correct. But in any case, the important thing is you basically need to keep track of the width and height of both your scene, and of your viewport. I declare them as global variables, and set their values in the 'reshape' routine (when you resize the window).

(edit) yes, that seems to be correct. The important thing to remember is that your scene size should generally stay the same, while your window/viewport size can vary. In a 2d setting, I usually set my scene width to 640, and set the scene height so its proportional to the window, in otherwords: so SCENE_HEIGHT/SCENE_WIDTH = VIEWPORT_HEIGHT/VIEWPORT_WIDTH. Solving for SCENE_HEIGHT gives you SCENE_HEIGHT = SCENE_WIDTH*VIEWPORT_HEIGHT/VIEWPORT_WIDTH

all 4 of these values you need to save in order to determine the cursor position in eye coordinates. Thats why I make them global.

mikau
08-15-2008, 07:13 PM
for completeness, my reshape function looks like this (i use GLUT):


void reshape (int w, int h)
{
VIEWPORT_WIDTH = w;
VIEWPORT_HEIGHT = h;
SCENE_WIDTH = 640; // 640 is arbitrary
SCENE_HEIGHT = SCENE_WIDTH*h/((float)w);


glViewport (0, 0, (GLsizei) VIEWPORT_WIDTH, (GLsizei) VIEWPORT_HEIGHT);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();


glOrtho(0, SCENE_WIDTH, 0, SCENE_HEIGHT, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}


and my mouse function looks like this:


void mouse(int button, int state, int x, int y)
{

mouse_x = x*SCENE_WIDTH/(float) VIEWPORT_WIDTH;
mouse_y = SCENE_HEIGHT*(VIEWPORT_HEIGHT - y)/(float) VIEWPORT_HEIGHT;

...

}