• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

[solved] d3d camera angles and correct position of mouse_x and _y

lets say that i have a camera offset from a room using d3d_set_prospective and im tilting it down a little bit, so instead of being directly overhead of the room at 90 degrees, lets put it at 60 degrees for the sake of this thread.

now once i have moved the camera, if i try to click on a location on the board, the place where the mouse is and the location that is shown do night align, near the bottom of the screen its fairly close but the further up the screen my finger goes the less aligned it is. also to the left and right the mouse ends up going clear outside of the camera.

now it should be mentioned, i have a "fake canvas" in the room, a strip with its width reduced so that, when looked through the camera at the 60 degree angle, it looks like it is just inside the room but in reality its width to height ratio is greatly increased. the fake canvas has the same height as the room but the width is less than the room width.

how would i go about compensating for the prospective and angle of the camera so that the position of the mouse will correlate to the position on the screen, making sure that the mouse position stays on the fake canvas?

thank you.
 
First get your 3d mouse vector.

To avoid having to reproduce the math that is used to build your view matrix, it is helpful to have your view matrix already computed.

mouse_vector is not unit length here, so don't try to use it in other calculations that require it to be, without normalizing it.
Code:
    x = a * f * ( 2 * mx / w - 1 );
    y =     f * ( 1 - 2 * my / h );
    mouse_vector_x = x * v[0] + y * v[1] + v[2];
    mouse_vector_y = x * v[4] + y * v[5] + v[6];
    mouse_vector_z = x * v[8] + y * v[9] + v[10];
a = camera aspect ratio
f = dtan(camera_fov/2)
w = width of view port
h = height of view port
mx = mouse x position in view port (i.e., 0 would be left side of view port)
my = mouse y position in view port (0 would be top side of view port)
v = current view matrix

If you are using GMS2 and you notice your mouse vector is mirrored vertically, then substitute y with:
Code:
    y =     f * ( 2 * my / h - 1 );
Then use your mouse vector to find the intersection point with the ground.

Assuming -z or +z is up, and that ground altitude is zero.
Code:
    if (mouse_vector_z != 0){  //if mouse vector is not parallel with ground.
        t = -camera_position_z / mouse_vector_z;
        if (t >= 0) {  //if mouse vector points toward ground (instead of away from it)
           intersection_x = camera_position_x + mouse_vector_x * t;
           intersection_y = camera_position_y + mouse_vector_y * t;
        }
    }
 
Last edited:
@flyingsaucerinvasion

ok i finally got around to working on this and i cant make heads or tails. so let me start from the beginning.

how do you compute the view matrix? this is the code i am using to get the perspective, but i dont know what v[0] is or why their is no v[3] or v[7].

Code:
d3d_set_projection_ext(
    room_width/2 , 2000, -dtan(30)*2000,
    room_width/2, 845, 0,
    0, 0, -1,
    30, -750/1334, 1, 10000
);
from the code you posted, im pretty sure "a = camera aspect ratio" would be "a = -750/1334" but for "f" i dont know what the variable "camera_fov" represents and "v = current view matrix" i dont know where to begin.

is camera_position_z the same as "zfrom" in d3d_set_projection_ext?

and looping all the way back to the beginning of your post how do you "normalize" the mouse vectors? how could i take intersection_x and _y and convert them into normal x and y so i could use them to measure things like distance?

thank you.
 
There are different ways of computing the view matrix. Yes, you can just use d3d_set_projection_ext at any time, and then from that you can get the view matrix with:

view_matrix = matrix_get(matrix_view);

Yes, the camera's aspect ratio should be -750/1334. Presuming that 750 is the width of your render target, and 1334 is the height. (make sure you haven't got that the other way around). However, I did suggest maybe it would be better after all to use a positive aspect ratio, and simply put the camera at a +z position instead of -z. The original rational for looking from -z and using a negative aspect ratio was that I was worried that if the camera was on the opposite side of the xy plane (at a +z position) that it might change the way in which certain 2d things would appear to be drawn. But after thinking about it for a while, I'm not sure that makes sense, so I would suggest use a positive aspect ratio, and put the camera at a +z position. This way, we won't need to remember that we've used a negative aspect ratio, and we won't have to worry about the effects that it will have, such as reversing texture orientation, and flipping backface culling.

The camera's field of view (fov) is the angular size of the vertical axis of your perspective projection. So in the d3d_set_projection_ext function call that you posted above, you used 30 degrees for the fov argument. In the mouse vector script, "f" is dtan(camera_fov/2), where camera_fov should be the same value you provided as the fov argument to d3d_set_projection_ext.

the camera's z position should have the same value as the "z from" argument provided to the d3d_set_projection_ext function.

You don't need to normalize the mouse vector for this function, but if you wanted to do it for other reasons, normalizing any vector is simple. All you have to do is divide each component of the vector by the length of the vector. So for example, say you had the vector (a,b,c). length=sqrt(a*a+b*b+c*c); And to normalize: a/=length; b/=length; c/=length;
----------------------------------------------------------------------------------------------------------------------------------------
You don't need to really know the following section in order to get things working, so just ignore it if you aren't interested.

The view matrix is stored as a 1d array of 16 elements in gml, hence v[0], v[3], etc....
However it is better to think of it as if it were mapped out as a 4x4 matrix:
Code:
0  4  8  12
1  5  9  13
2  6  10 14
3  7  11 15
In that diagram the number indicates the array index.
Ignore the rightmost column and the bottommost row for now, and just look at the upper 3x3 section of that matrix.
Encoded into that section of the matrix is the orientation of the camera.
The top row (0,4,8) is the xaxis, and is a vector which points to the camera's right (or left if you've used a negative aspect-ratio to flip coordinate systems).
The middle row (1,5,9) is the yaxis and is a vector which points the the camera's top.
And the bottom row (2,6,10) is the zaxis and is a vector which points forward toward whatever the camera is looking directly at.

All we're doing is taking the mouse vector in view space (x,y,1), and then using the transpose of that 3x3 section of the view matrix, to rotate the vector into world space. Normally, a view matrix would be used to transform things from world space to view space. But the transpose of a rotation matrix encodes a rotation in the opposite direction. So by multiplying the column vector (x,y,1) by the transpose of that part of the view matrix, we are bringing the mouse vector from view space into world space. The transpose of a matrix is just swapping rows for columns.

If you want to know how the x, y, and z axes are computed from the "from", "to" and "up" arguments provided to d3d_set_projection_ext, have a look here:
zaxis = normalize(cam_to - cam_from)
xaxis = normalize(cross(cam_up, zaxis))
yaxis = cross(zaxis, xaxis)
To normalize a vector is to divide each component of a vector by the length of the vector. The result is to change the vector into a vector that still points in the same direction, but has a length equal to 1.
The "cross" product of two vectors is computed like this:
c = cross(a,b) =
cx = ay*bz - az*by
cy = az*bx - ax*bz
cz = ax*by - ay*bx
----------------------------------------------------------------------------------------------------------------------------------------
 
Last edited:
@flyingsaucerinvasion

it took a better part of the day to figure out what you said but with a bit of tinkering i finally got it working.

lots of new information im learning and yes, very much am interested and appreciate that you were exhaustive.

one last question before i stop, what does 3,7,and 11-15 represent? i tried putting all of them up on screen while i was moving variables around and although i saw some of them change i could not figure out what they were doing.

thank you for your help.
 
In the view matrix...

(0, 4, 8) are the xyz components of the xaxis, and the xaxis is the direction in world space that the right side of your camera points to.
(1, 5, 9) is the yaxis, and that is the direction that the top side of your camera points to.
(2, 5, 10) is the zaxis, and it is the forward direction of your camera.

What I mean is if you were standing directly where the camera was located, and you oriented yourself in exactly the same way as your camera, then the xaxis would be to your right, yaxis is up, and zaxis is directly forward.

(12, 13, 14) is computed like this:

view_matrix[12] = - cam_x * xaxis_x - cam_y * xaxis_y - cam_z * xaxis_z;
view_matrix[12] = - cam_x * yaxis_x - cam_y * yaxis_y - cam_z * yaxis_z;
view_matrix[12] = - cam_x * zaxis_x - cam_y * zaxis_y - cam_z * zaxis_z;

And what that does is change the camera's position to the origin (0,0,0), in view space. It's kind of hard to explain exaclty how that works without going into matrix multiplication. Essentially what's going on is if you use the view matrix to transform a position vector from world into view space, what happens is you subtract the camera's position from the vector, and then you rotate the vector according to the camera's orientation. You can think of it like: View_Pos = Rotate*(World_Pos-Cam_Pos). And actually, you can distribute that rotation, and that is literally what is happening in the view matrix, and it produces the exact same results: View_Pos = Rotate*World_Pos - Rotate*Cam_Pos. And the reason it is done that way is so the transformation can be done in one step using a matrix / column vector multiplication, without having to do a substraction first.

(3, 7, 11, 15), the bottom row, always has values of (0, 0, 0, 1).

In view space, the location of everything is measured relative to the camera, the camera being at the origin (0,0,0), with the camera's right direction being +x, and the camera's up direciton being +y, and the camera's forward direction being +z. The purpose of the view matrix is to transform a position (or sometimes just a direction) from world, into view space.
 
Last edited:
Top