Screen Coordinates from 3D Vector

Started by
7 comments, last by Pyronaught 13 years, 6 months ago
I'm using Managed Direct3D and need to draw some text labels above some mesh objects in a scene. Since the DrawText function needs X and Y screen coordinates, I need a way to figure out the screen coordinate given a Vector3 for the centerpoint of the mesh. Seems like the conversion of 3d coordinates to screen coordinates is all handled behind the scenes but I'm sure there's got to be some simple way of going this. I just can't find any code samples from anyone who has done it.

-Kyle
Advertisement
There should be a Project method for your Vector3. Vector3.Project. Give it your world, view and projection matrices, and it will turn a 3D point into a point on the screen.
I did try that using the code below, but the screen coordinates are off by some some scale factor. At position 0,0,0 it is correct, but then the farther away from the origin the world coordinates are, the farther away from being at the correct screen coordinates the results of the Vector3.Project() call is.


Vector3 v = Vector3.Project(new Vector3(pos.m_x, pos.m_y, pos.m_z), device.Viewport, device.Transform.Projection, device.Transform.View, device.Transform.World);

drawingFontSmall.DrawText(v.X, v.Y, System.Drawing.Color.White, pos.m_name);


-Kyle
How do you set the position of your object, and are the matrices exactly the same as they are when drawing it?
If your object is centered on the origin locally, and you move it around using the World matrix, and the same World matrix is passed to Project, then the position you use in Project should be (0, 0, 0).
I call the Project() function right after drawing the object. The entire code block looks like this:

m_green_arrow.position = new Vector3(pos.m_x, pos.m_y, pos.m_z);
m_green_arrow.rotate_y = pos.m_x_angle*Utility.c_rad;
m_green_arrow.rotate_z = pos.m_z_angle*Utility.c_rad;
m_green_arrow.DrawPolarMesh(device);

// now label it
Vector3 v = Vector3.Project(new Vector3(pos.m_x, pos.m_y, pos.m_z), device.Viewport, device.Transform.Projection, device.Transform.View, device.Transform.World);

drawingFontSmall.DrawText( v.X, v.Y, System.Drawing.Color.White, pos.m_name );

If I set the camera location at the origin, then it works. The farther away from the origin the camera is, the larger the error on the screen coordinates. It's like the View tranform is not right or something. But if any of the transforms in the device were not correct then the mesh object wouldn't get drawn right either, but it does. Maybe I'm not using the results stored in the return vector correctly?

Quote:Original post by Erik Rufelt

If your object is centered on the origin locally, and you move it around using the World matrix, and the same World matrix is passed to Project, then the position you use in Project should be (0, 0, 0).



Bingo! That didn't sink in the first time I read your response, but using the position of (0,0,0) in the Project was the solution.

THANKS A LOT! I would have spent days trying to figure that out.

Another(and maybe better, depending on your situation) method would be to pass an identity matrix in as the world transform, and then pass in your models world coordinates as before. the world transformation can then be used instead to translate your projected point some amount above or below your model.
The other thing I'm trying to do is first click-select an object to make it active and then change it's position by dragging the mouse. I'm successfully using the Project method above to do the selection, but I'm having problems with the dragging operation not staying synched to the mouse pointer. I'm using the Unproject function now to figure out the change in world position that corresponds to a change in screen position required to keep the model aligned with the mouse pointer. The code below almost works except that the model always gets either ahead or behind the mouse pointer depending on how zoomed in the camera is:

device.Transform.World = Matrix.Scaling(model.scale, model.scale, model.scale) * Matrix.RotationZ(model.rotate_z) * Matrix.RotationY(model.rotate_y) * Matrix.Translation(model.position);

Vector3 world1_v = Vector3.Unproject( new Vector3( last_mouse_pos.X, last_mouse_pos.Y, 1f ), device.Viewport, device.Transform.Projection, device.Transform.View, device.Transform.World);

Vector3 world2_v = Vector3.Unproject(new Vector3( current_mouse_pos.X, current_mouse_pos.Y, 1f), device.Viewport, device.Transform.Projection, device.Transform.View, device.Transform.World);

float deltaZ = world2_v.Z - world1_v.Z;
float deltaX = world2_v.X - world1_v.X;
float deltaY = world2_v.Y - world1_v.Y;

model.position += new Vector3( deltaX, deltaY, deltaZ );
I got it working. Here's the code that keeps the model movement tied to the mouse pointer movement:

device.Transform.World = Matrix.Scaling(model.scale, model.scale, model.scale) * Matrix.RotationZ(model.rotate_z) * Matrix.RotationY(model.rotate_y) * Matrix.Translation(model.position);

Vector3 screen_v = Vector3.Project(new Vector3(0, 0, 0), device.Viewport, device.Transform.Projection, device.Transform.View, device.Transform.World);
float dx = screen_v.X – current_mouse_pos.X;
float dy = screen_v.Y – current_mouse_pos.Y;

Vector3 world1_v = Vector3.Unproject(new Vector3( last_mouse_pos.X + dx, last_mouse_pos.Y + dy, screen_v.Z), device.Viewport, device.Transform.Projection, device.Transform.View, Matrix.Identity);

Vector3 world2_v = Vector3.Unproject(new Vector3(current_mouse_pos.X + dx, current_mouse_pos.Y + dy, screen_v.Z), device.Viewport, device.Transform.Projection, device.Transform.View, Matrix.Identity);


float deltaZ = world2_v.Z - world1_v.Z;
float deltaX = world2_v.X - world1_v.X;
float deltaY = world2_v.Y - world1_v.Y;

model.position += new Vector3( deltaX, deltaY, deltaZ );

This topic is closed to new replies.

Advertisement