DirectX Cursor vs World Coordinates

Hello again cplusplus.com peeps.

I'm making a 2D platformer with DirectX. I have a sprite for my player's arm that I want to rotate to always be pointing at the cursor. I managed to get it working using some basic (and probably ghetto-rigged) trig, but only when the arm was in a static position, by itself.

Using getCursorPos(), I moused over the arm for the coordinates (vec2 m_Arm.pos) of the pivot point and used those with the current cursor position (vec2 m_Cursor) to calculate the rotation. So I had an arbitrarily placed arm that would rotate to point at the cursor. So far so good.

Next, I changed the pivot point (m_Arm.pos) to the position of my player (vec2 m_oPlayer.pos) so the rotating arm would actually move around with the player's body.

The problem is, I use the player's vector for world coordinates like so:

1
2
3
4
5
D3DXMatrixScaling(&scaleMat, 1.0f, 1.0f, 1.0f);
D3DXMatrixTranslation(&transMat, m_oPlayer.pos.x, m_oPlayer.pos.y, 0.0f);
D3DXMatrixMultiply(&worldMat, &scaleMat, &transMat);

m_pD3DSprite->SetTransform(&worldMat);




...and here's how I calculate the rotation of the arm:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//Cursor updates
GetCursorPos(&m_Cursor);
ScreenToClient(m_hWnd, &m_Cursor);

//Dynamic arm updates
m_Arm.worldPos.x = m_oPlayer.pos.x-340;
m_Arm.worldPos.y = m_oPlayer.pos.y-58;

//m_Arm.cursorPos.x = 135;
//m_Arm.cursorPos.y = 450;
m_Arm.cursorPos.x = m_oPlayer.pos.x * 0.281f;
m_Arm.cursorPos.y = m_oPlayer.pos.y * 0.9f;

m_Arm.deltaCursor.x = m_Cursor.x <= m_Arm.cursorPos.x ? (m_Cursor.x - m_Arm.cursorPos.x) : (m_Arm.cursorPos.x - m_Cursor.x);
m_Arm.deltaCursor.y = m_Cursor.y <= m_Arm.cursorPos.y ? (m_Cursor.y - m_Arm.cursorPos.y) : (m_Arm.cursorPos.y - m_Cursor.y);

if(m_Cursor.y <= m_Arm.cursorPos.y)
{
	//Quadrant 1
	if(m_Cursor.x >= m_Arm.cursorPos.x)
	{
		m_Arm.rot = atan((double)m_Arm.deltaCursor.x/m_Arm.deltaCursor.y);
		m_Arm.rot += D3DXToRadian(180);

		if(m_Cursor.y == m_Arm.cursorPos.y)
			m_Arm.rot = D3DXToRadian(-90);
	}


	//Quadrant 2
	else
	{
		m_Arm.rot = atan((double)m_Arm.deltaCursor.y/m_Arm.deltaCursor.x);
		m_Arm.rot += D3DXToRadian(90);	
	}

}
else
{
	//Quadrant 3
	if(m_Cursor.x < m_Arm.cursorPos.x)
		m_Arm.rot = atan((double)m_Arm.deltaCursor.x/m_Arm.deltaCursor.y);
		

	//Quadrant 4
	else
	{
		m_Arm.rot = atan((double)m_Arm.deltaCursor.y/m_Arm.deltaCursor.x);
		m_Arm.rot -= D3DXToRadian(90);
	}
}


Lines 9 and 10 are using the coordinates I got from mousing over the arm. Lines 11 and 12 are my attempt at converting the world coordinates of the arm to the moused-over coordinates, but those multipliers only work in the arm's initial position.

So I need to either convert the mouse coordinates to world coordinates, or the other way around. Despite a solid 2.5 hours of googling and reading other people's seemingly relevant problems, I'm still stuck.
I don't have time to look in detail right now... but I will say that atan2() will take care of all the quadrant stuff for you.... it would simplify your code greatly.
Okay... I'm not familiar with D3D (I'm more of an OpenGL guy) so you'll have to bear with me.

To get this to work you really only need 2 things:

1) The cursor position
2) The arm position


The arm position is the origin of the arm graphic. This will also be the point at which the rotation will "pivot around", so it probably should not be the center of the graphic... but rather should be all the way to one side:

1
2
3
4
5
6
7
8
    ===============
    |             |
+=> X             |     <-  graphic quad
|   |             |
|   ===============
|
|
origin  (0,0)



Now it doesn't really matter what coordinate system you have these in... as long as they're both in the same coordinates. I would probably do the calculations in camera coordinates (which for a 2D game would be the same as screen coordinates, only the Y axis might be inverted depending on how you set up the view).

Doing the math in world coordinates is an option as well, but then you have to translate the cursor which is more work than you probably want to do.


Matrix operations operate like a 'FILO' effect. That is, the first matrix effect you employ will be the last one you see. So since we want the arm rotation around the origin to be the FIRST effect... that needs to be the LAST matrix we multiply.

Here's pseudo code to give you the idea. Note: untested

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
matrix m = identity_matrix;

m *= arm_model_to_camera;  // translation matrix to convert model coordinates to
   // camera coordinates.  Or... if you are doing this as a 2 step process, you might be
   // doing model->world->camera  (although you shouldn't do that because it increases
   // probability of rounding errors)


vector cursor = cursor_pos_in_camera_coords;
vector armpos = origin;  // (0, 0)

armpos = m * armpos;  // translate the arm position, so now it's in camera coords

// at this point, 'cursor' and 'armpos' are both in camera coords.  We can now use them

double angle = std::atan2(cursor.y - armpos.y, cursor.x - armpos.x);

// now we have our angle.  rotate our matrix

m *= rotation_matrix( angle );


// all done!  use 'm' to draw the arm graphic 
Topic archived. No new replies allowed.