How to convert mouse movements to rotation of an element - math

I'm building a wheel menu control. The idea is you spin the wheel until the item you want to act on is in view, then you click on it or whatever. I'm trying to figure out how to translate the user's mouse movements (x & y) into the number of degrees to spin the wheel. I can implement it all, I just am missing the math to do the conversion. Any help or pointers are appreciated!

If the user moves the mouse from [x1,y1] to [x2,y2], what you basically want is to find
θ=θ1-θ2
Where:
θ1 = Math.atan2(y1, x1);
θ2 = Math.atan2(y2, x2);
Now all of this depends on where you define your origin (center of your wheel). If your origin is [x0,y0], then just subtract those values from the actual mouse co-ordinates.
Also on screen, the co-ordinate system is upside down, so 0,0 is in the top-left instead of bottom-left, so you'd need to flip that, but the math is essentially the same.
Also note that the angle is measured in radians and not degrees.

Take the atan2() of two subsequent positions offset from the center of the control and change the value by the delta of the angles multiplied by a constant.

Related

proper way of calculating mouse 'z' position (Unity 3d)

Whenever I develop games that use mouse input, I will get confused of calculating the mouse position. Especially the z position.
The ways I saw many using.
mouse position z = mouse position y.
z = distance between camera and object.
z = difference b/w object z and camera z. (I am using. Doesn't work when camera and object is rotated).
z = some arbitrary value. (many use 0 and some other values).
others.
Which method is correct? Is there any other method which is correct?
Please let me know.
The same answer as in your second mouse based question applies here too: Mouse based aiming Unity3d
TL;DR: use a raycast from camera to intersect the plane that the action is on.
Vector3 pz = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pz.z should be what you are asking for if I'm understanding right. Tell me if it works
The mouse position is in theory external to the game world itself. Therefore, the mouse position relates simply to X and Y co-ordinates of the screen space in which you are interacting with your game (IE width:height of your game).
What you're asking seems to be more of "How do I model the mouse position in my game world?"
As noted by Mario, Camera.main.ScreenToWorldPoint(Input.mousePosition) will convert your mouse position to world co-ordinates. However, this assumes that your main camera is where you want to convert to. In reality, you want to call the ScreenToWorldPoint method on the Camera that is rendering whatever space it is you are wanting to interact with. For example, you may have your main game world at (0, 0, 0) but you may be rendering your GUI on top using a separate camera that renders objects at (-5000, 0, 0).
To answer your question, to model the mouse z position it should simply be the same z value as your Camera. You can then perform calculations on that value to suit your particular needs.
IE:
1) mouse.position.z = mouse.position.y - These are entirely different. Now you're just using an arbitrary value
2) Distance between camera and object - That's a calculation made from your original object.position.z and original mouse.position.z. Not your actual z value.
3) See 2.
4) See 1.

how to translate 3d mesh, given a view direction and a change in cursor position

My question is similar to 3D Scene Panning in perspective projection (OpenGL) except I don't know how to compute the direction in which to move the mesh.
I have a program in which various meshes can be selected. Once a mesh is selected I want it to translate when click-dragging the cursor. When the cursor moves up, I want the mesh to move up, and so on for the appropriate direction. In other words, I want the mesh to translate in directions along the plane that is perpendicular to the viewing direction.
I have the Vector2 for the Delta (x,y) in cursor postion, and I have the Vector3 viewDirection of the camera and the center of the mesh. How can I figure out which way to translate the mesh in 3d space with the Delta and viewDirection? Will I need other information in order to to this calculation (such as the up, or eye)?
It doesn't matter if if the scale of the translation is off, I'm just trying to figure out the direction right now.
EDIT: for some reason I had a confusion about getting the up direction. Clearly it can be calculated by applying the camera rotation to the specified perspective up vector.
You'll need an additional vector, upDirection, which is the unit vector pointing "up" from your camera. You can now cross-product viewDirection and upDirection to get rightDirection, the vector pointing "right" from your camera.
You want to map y deltas to motion along upDirection (or -upDirection) and x deltas to motion in rightDirection. These vectors are in world-space.
You may want to scale the translation speed to match the mouse speed. If you are using perspective projection you'll want to scale the translation speed with your model's depth with respect to your camera (The further the object is from your camera, the faster you will need to move it if you want it to match the mouse.)

3D perspective 'grab' panning with DirectX

I am implementing a pan tool in our software's 3D view which is supposed to work much like the grab tool of, say, Photoshop or Acrobat Reader. That is, the point the user grabs onto with the mouse (clicks and holds, then moves the mouse) stays under the mouse cursor as the mouse moves.
This is a common paradigm and one that's been asked about on SO before, the best answer being to this question about the technique in OpenGL. There is another that also has some hints, and I have been reading this very informative CodeProject article. (It doesn't explain many of its code examples' variables etc, but from reading the text I think I understand the technique.) But, I have some implementation issues because my 3D environment's navigation is set up quite differently to those articles, and I am seeking some guidance.
My technique - and this might be fundamentally flawed, so please say so - is:
The scene 'camera' is stored as two D3DXVECTOR3 points: the eye position and a look point. The view matrix is constructed using D3DXMatrixLookAtLH like so:
const D3DXVECTOR3 oUpVector(0.0f, 1.0f, 0.0f); // Keep up "up", always.
D3DXMatrixLookAtLH(&m_oViewMatrix, &m_oEyePos, &m_oLook, &oUpVector);
When the mouse button is pressed, shoot a ray through that pixel and find: the coordinate (in unprojected scene / world space) of the pixel that was clicked on; the intersection of that ray with the near plane; and the distance between the near-plane point and object, which is the length between those two points. Store this and the mouse position, and the original navigation (eye and look).
// Get the clicked-on point in unprojected (normal) world space
D3DXVECTOR3 o3DPos;
if (Get3DPositionAtMouse(roMousePos, o3DPos)) { // fails if nothing under the mouse
// Mouse location when panning started
m_oPanMouseStartPos = roMousePos;
// Intersection at near plane (z = 0) of the ray from camera to clicked spot
D3DXVECTOR3 oRayVector;
CalculateRayFromPixel(m_oPanMouseStartPos, m_oPanPlaneZ0StartPos, oRayVector);
// Store original eye and look points
m_oPanOriginalEyePos = m_oEyePos;
m_oPanOriginalLook = m_oLook;
// Store the distance between near plane and the object, and the object position
m_dPanPlaneZ0ObjectDist = fabs(D3DXVec3Length(&(o3DPos - m_oPanPlaneZ0StartPos)));
m_oPanOriginalObjectPos = o3DPos;
Get3DPositionAtMouse is a known-ok method which picks a 3D coordinate under the mouse. CalculateRayFromPixel is a known-ok method which takes in a screen-space mouse coordinate and casts a ray, and fills the other two parameters with the ray intersection at the near plane (Z = 0) and the normalised ray vector.
When the mouse moves, cast another ray at the new position, but using the old (original) view matrix. (Thanks to Nico below for pointing this out.) Calculate where the object should be by extending the ray from the near plane the distance between the object and near plane (this way, the original object and new object points should be in parallel plane to the near plane.) Move the eye and look coordinates by this much. Eye and Look are set from their original (when panning started) values, with the difference being from the original mouse and new mouse positions. This is to reduce any precision loss from incrementing or decrementing by granular (integer) pixel movements as the mouse moves, ie it calculates the whole difference in navigation every time.
// Set navigation back to original (as it was when started panning) and cast a ray for the mouse
m_oEyePos = m_oPanOriginalEyePos;
m_oLook = m_oPanOriginalLook;
UpdateView();
D3DXVECTOR3 oRayVector;
D3DXVECTOR3 oNewPlaneZPos;
CalculateRayFromPixel(roMousePos, oNewPlaneZPos, oRayVector);
// Now intersect that ray (ray through the mouse pixel, using the original navigation)
// to hit the plane the object is in. Function uses a "line", so start at near plane
// and the line is of the length of the far plane away
D3DXVECTOR3 oNew3DPos;
D3DXPlaneIntersectLine(&oNew3DPos, &m_oPanObjectPlane, &oNewPlaneZPos, &(oRayVector * GetScene().GetFarPlane()));
// The eye/look difference /should/ be as simple as:
// const D3DXVECTOR3 oDiff = (m_oPanOriginalObjectPos - oNew3DPos);
// But that lags and is slow, ie the objects trail behind. I don't know why. What does
// work is to scale the from-to difference by the distance from the camera relative to
// the whole scene distance
const double dDist = D3DXVec3Length(&(oNew3DPos - m_oPanOriginalEyePos));
const double dTotalDist = GetScene().GetFarPlane() - GetScene().GetNearPlane();
const D3DXVECTOR3 oDiff = (m_oPanOriginalObjectPos - oNew3DPos) * (1.0 + (dDist / dTotalDist));
// Adjust the eye and look points by the same amount, so orthogonally changed
m_oEyePos = m_oPanOriginalEyePos + oDiff;
m_oLook = m_oPanOriginalLook + oDiff;
Diagram
This diagram is my working sketch for implementing this:
and hopefully explains the above much more simply than the text. You can see a moving point, and where the camera has to move to keep that point at the same relative position. The clicked-on point (the ray from the camera to the object) is just to the right of the straight-ahead ray representing the center pixel.
The problem
But, as you've probably guessed, this doesn't work as I hope. What I wanted to see was the clicked-on object moving with the mouse cursor. What I actually see is that the object moves in the direction of the mouse, but not enough, ie it does not keep the clicked-on point under the cursor. Secondly, the movement flickers and jumps around, jittering by up to twenty or thirty pixels sometimes, then flickers back. If I replace oDiff with something constant this doesn't occur.
Any ideas, or code samples showing how to implement this with DirectX (D3DX, DX matrix order, etc) will be gratefully read.
Edit
Commenter Nico below pointed out that when calculating the new position using the mouse cursor's moved position, I needed to use the original view matrix. Doing so helps a lot, and the objects stay near the mouse position. However, it's still not exact. What I've noticed is that at the center of the screen, it is exact; as the mouse moves further from the center, it gets out by more and more. This seemed to change based on how far away the object was, too. By pure 'I have no idea what I'm doing' guesswork, I scaled this by a factor of the near/far plane and how far away the object was, and this brings it very close to the mouse cursor, but still a few pixels away (1 to, say, 30 at the extreme edge of the screen, which is enough to make it feel wrong.)
Here's how i solve this problem.
float fieldOfView = 45.0f;
float halfFOV = (fieldOfView / 2.0f) * (DEGREES_TO_RADIANS);
float distanceToObject = // compute the world space distance from the camera to the object you want to pan
float projectionToWorldScale = distanceToObject * tan( halfFov );
Vector mouseDeltaInScreenSpace = // the delta mouse in pixels that we want to pan
Vector mouseDeltaInProjectionSpace = Vector( mouseDeltaInScreenSpace.x * 2 / windowPixelSizeX, mouseDeltaInScreenSpace.y * 2 / windowPixelSizeY ); // ( the "*2" is because the projection space is from -1 to 1)
// go from normalized device coordinate space to world space (at origin)
Vector cameraDelta = -mouseDeltaInProjectionSpace * projectionToWorldScale;
// now translate your camera by "cameraDelta".
Note this works for an field of view apsect ratio of 1, i think you would have to break up the "scale" into separate x and y components if they vertical field of view was different than the horizontal field of view
Also, you mentioned a "look at" vector. I'm not sure how my math would need to change for that since my camera is always looking straight down the z-axis.
One problem is your calculation of the new 3d position. I am not sure if this is the root cause, but you might try it. If it doesn't help, just post a comment.
The problem is that your offset vector is not parallel to the znear plane. This is because the two rays are not parallel. Therefore, if the have the same length behind znear, the distance of the end point to the znear plane cannot be equal.
You can calculate the offset vector with the theorem of intersecting lines. If zNearA and zNearB are the intersection points of the znear plane with ray A and ray B respectively, then the theorem states:
Length(original_position - cam_position) / Length(offset_vector) = Length(zNearA - cam_position) / Length(zNearB - zNearA)
And therefore
offset_vector = Length(original_position - cam_position) / Length(zNearA - cam_position) * (zNearB - zNearA)
Then you can be sure to move on a line that is parallel to the znear plane.
Just try it out and see if it helps.

Vector math, finding the angle

I am trying to learn XNA by writing a small 2D game, it's a Top-Down perspective and Im trying to have double movement, moving along the axis using Left-Right and Up-Down keys, as well as looking right at the mouse cursor, so that the player can run and aim at the same time.
I have one vector for the player position (m_PlayerPos), and one vector for the mouse position (m_MousePos), and im trying to get the correct angle towards the mouse position.
Im using the formula method:
public static float Angle(Vector2 from, Vector2 to)
{
return (float)Math.Atan2(from.X - to.X, from.Y - to.Y);
}
This works, but for some reason the method only works half-way, along the x-axis. When the mouse is to the exact left of right of the player, the player looks right at the mouse.
But if I move to the top of the player, it looks down, and if the mouse is below the player, the player looks up. So I need to inverse the Y axis, but Im not sure how.
Thanks in advance for any feedback.
Use to.Y - from.Y.
Multiply it with (0.0, -1.0) (or just multiply the Y component by -1.0). This will mirror the vector along the horizontal axis and should achieve the result you want.
In screen space the origin is in the top-left corner with the Y axis pointing downward whereas in eucledean space the Y axis points upwards. That's why you observe the Y axis being "flipped".

moving items in JPanel

I am working on a pong game and I am working on the mechanism to move the ball. If I add 1 to x the ball moves 1 pixel to the right, if i add 1 to y the ball moves 1 pixel to the bottom. What if I want to move the ball at a certain angle how can 1 calculate the coordinates.
Trying to work with angles will get a bit more complicated than you need to get. For this kind of animation you generally want to use floats to store your objects x and y coordinates and move it by applying x and y deltas (The floats will preserve the detail of the position which is lost to rounding when drawn on the screen). The deltas represent the speed your object is moving in each axis and can be negative or positive.
For each iteration of your animation, add xdelta to your x coordinate and add ydelta to your y coordinate. Round them off to position them on the screen.
When you hit the top or bottom border, you would swap the sign on your ydelta component and likewise for side borders.
You wouldn't want to keep the same x and y delta all the time so when the objects hits a paddle, modify the x or y delta a little bit to change up the angle.
you are looking for line drawing algorithms, something like Bresenham or DDA you can find some reasonable implementations here ofcourse instead of drawing a complete line you would move your ball along that line but the way of finding the set of lines to move on is the same.
You might find these resources helpful.
for something like Pong you should be investigating vector math, but if all you want is to know an angle all you really need is SOHCAHTOA.

Resources