I just tried my old code from xna, that would turn mouse positions into world coordinates.
It basically used Viewport.Unproject() Im not sure i’ve ever tried to use it in monogame.
Well it doesn’t seem to work as expected any sort of rotation to the view doesn’t translate into world coordinates.
I looked at the code and it appears to be using the xna 2.0 code which was faulty if i remember right.
Either way… I decided to take a look at it and too me it seems that the whole thing is overly complicated.
So i wrote my own version which seems to give usable world positions though the z is slightly off.
Here’s how i was using the original if someone see’s anything wrong.
// The unproject used inside this is improper
public Ray GetScreenVector2AsRayInto3dWorld(Vector2 screenPosition, Matrix projectionMatrix, Matrix viewMatrix, Matrix world, float near, float far, GraphicsDevice device)
{
Vector3 farScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, far); // the projection matrice's far plane value.
Vector3 nearScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, near); // must be more then zero.
Vector3 nearWorldPoint = Unproject(nearScreenPoint, projectionMatrix, viewMatrix, world, device);
Vector3 farWorldPoint = Unproject(farScreenPoint, projectionMatrix, viewMatrix, world, device);
Vector3 worldRaysNormal = Vector3.Normalize(farWorldPoint - nearWorldPoint);
return new Ray(nearWorldPoint, worldRaysNormal);
}
// This is worthless the coordinates aren't rotated in world space by the view matrix.
// so the whole thing becomes junk.
public Vector3 Unproject(Vector3 source, Matrix projection, Matrix view, Matrix world, GraphicsDevice gd)
{
Matrix wvp = Matrix.Multiply(Matrix.Multiply(world, view), projection);
Matrix inv = Matrix.Invert(wvp);
Vector3 clipSpace = source;
clipSpace.X = (((source.X - gd.Viewport.X) / ((float)gd.Viewport.Width)) * 2f) - 1f;
clipSpace.Y = -((((source.Y - gd.Viewport.Y) / ((float)gd.Viewport.Height)) * 2f) - 1f);
clipSpace.Z = (source.Z - gd.Viewport.MinDepth) / (gd.Viewport.MaxDepth - gd.Viewport.MinDepth);
Vector3 invsrc = Vector3.Transform(clipSpace, inv);
// k but were is my translation from view space.
float a = (((clipSpace.X * inv.M14) + (clipSpace.Y * inv.M24)) + (clipSpace.Z * inv.M34)) + inv.M44;
if (!WithinEpsilon(a, 1f))
{
invsrc.X = invsrc.X / a;
invsrc.Y = invsrc.Y / a;
invsrc.Z = invsrc.Z / a;
}
return invsrc;
}
Here is the version i made with comments.
Which makes me wonder why they are doing all the extra stuff.
// uses my unproject.
public Ray MineGetScreenVector2AsRayInto3dWorld(Vector2 screenPosition, Matrix projectionMatrix, Matrix viewMatrix, Matrix world, float near, float far, GraphicsDevice device)
{
Vector3 farScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, far); // the projection matrice's far plane value.
Vector3 nearScreenPoint = new Vector3(screenPosition.X, screenPosition.Y, near); // must be more then zero.
Vector3 nearWorldPoint = UnprojectMine(nearScreenPoint, projectionMatrix, viewMatrix, world, device);
Vector3 farWorldPoint = UnprojectMine(farScreenPoint, projectionMatrix, viewMatrix, world, device);
Vector3 worldRaysNormal = Vector3.Normalize(farWorldPoint - nearWorldPoint);
return new Ray(nearWorldPoint, worldRaysNormal);
}
// We don't even need the projection or view matrix for world coordinates.
// Just the camera world matrix. Basically a CreateWorld(...) using the cameras values.
public Vector3 UnprojectMine(Vector3 screenPosition, Matrix projection, Matrix view, Matrix world, GraphicsDevice gd)
{
// Gonna do this the easy hard way piece by piece.
var vp = gd.Viewport;
Vector3 s = screenPosition;
// Well jump out of screen space to clip space.
var cx = (((s.X - vp.X) / vp.Width) * 2f) - 1f;
var cy = -((((s.Y - vp.Y) / vp.Height) * 2f) - 1f);
// this looks slightly off to me im not sure this is completely proper to think of it like this.
// i mean the user sees the near plane as basically zero depth even if its not so i dunno.
// then again maybe im not to sure yet.
var cz = (s.Z - vp.MinDepth) / (vp.MaxDepth - vp.MinDepth);
//
// Ok at this point we have cliped space mouse coordinates ... ? right .... ? hummm....
//
// However... we haven't reversed the w ...
// In truth we haven't ever applied the w either...
// We never had to go thru view projection space.
// Which means, the coordinate system we are actually within is ? ...
// ModelSpace... ?
// lets see if that is true.
// If c xyz is actually in model space then our screens offset currently is actually the camera space but not the view space.
// Such that the camera world multiplied by these values will yeild ...
// world space coordinates that are defined by the camera as the origin.
//
Vector4 v = new Vector4(cx, cy, cz, 1.0f);
var vw = Vector4.Transform(v, world);
// Are we there yet ? Let's see.
// if we are then, We can pass this to create world. Make a final wvp matrix. Multiply a forward pointing quad to get a proper visual mouse point at a specified z depth.
return new Vector3(vw.X, vw.Y, vw.Z);
}
Ironically i wrote this straight out and it seems to be nearly working right off the bat the z depth is off it seems though.
The worst part off the monogame one is that weird stuff happens when the system coordinates changes as the camera crosses into positive z system coordinates. Unless im doing something wrong, if anyone knows how to use the monogame version properly let me know, cause either im misunderstanding something or its messed up.