I'm having a bit of trouble with 3D picking, at first I thought my ray was inaccurate but it turns out that the picking is happening on faces facing the camera and faces facing away from the camera which I'm currently culling.
Here's my ray creation code, I'm pretty sure the problem isn't here but I've been wrong before.
private uint Pick()
{
Ray cursorRay = CalculateCursorRay();
Vector3? point = Control.Mesh.RayCast(cursorRay);
if (point != null)
{
Tile hitTile = Control.TileMesh.GetTileAtPoint(point);
return hitTile == null ? uint.MaxValue : (uint)(hitTile.X + hitTile.Y * Control.Generator.TilesWide);
}
return uint.MaxValue;
}
private Ray CalculateCursorRay()
{
Vector3 nearPoint = Control.Camera.Unproject(new Vector3(Cursor.Position.X, Control.ClientRectangle.Height - Cursor.Position.Y, 0f));
Vector3 farPoint = Control.Camera.Unproject(new Vector3(Cursor.Position.X, Control.ClientRectangle.Height - Cursor.Position.Y, 1f));
Vector3 direction = farPoint - nearPoint;
direction.Normalize();
return new Ray(nearPoint, direction);
}
public Vector3 Camera.Unproject(Vector3 source)
{
Vector4 result;
result.X = (source.X - _control.ClientRectangle.X) * 2 / _control.ClientRectangle.Width - 1;
result.Y = (source.Y - _control.ClientRectangle.Y) * 2 / _control.ClientRectangle.Height - 1;
result.Z = source.Z - 1;
if (_farPlane - 1 == 0)
result.Z = 0;
else
result.Z = result.Z / (_farPlane - 1);
result.W = 1f;
result = Vector4.Transform(result, Matrix4.Invert(ProjectionMatrix));
result = Vector4.Transform(result, Matrix4.Invert(ViewMatrix));
result = Vector4.Transform(result, Matrix4.Invert(_world));
result = Vector4.Divide(result, result.W);
return new Vector3(result.X, result.Y, result.Z);
}
And my triangle intersection code. Ripped mainly from the XNA picking sample.
public float? Intersects(Ray ray)
{
float? closestHit = Bounds.Intersects(ray);
if (closestHit != null && Vertices.Length == 3)
{
Vector3 e1, e2;
Vector3.Subtract(ref Vertices[1].Position, ref Vertices[0].Position, out e1);
Vector3.Subtract(ref Vertices[2].Position, ref Vertices[0].Position, out e2);
Vector3 directionCrossEdge2;
Vector3.Cross(ref ray.Direction, ref e2, out directionCrossEdge2);
float determinant;
Vector3.Dot(ref e1, ref directionCrossEdge2, out determinant);
if (determinant > -float.Epsilon && determinant < float.Epsilon)
return null;
float inverseDeterminant = 1.0f/determinant;
Vector3 distanceVector;
Vector3.Subtract(ref ray.Position, ref Vertices[0].Position, out distanceVector);
float triangleU;
Vector3.Dot(ref distanceVector, ref directionCrossEdge2, out triangleU);
triangleU *= inverseDeterminant;
if (triangleU < 0 || triangleU > 1)
return null;
Vector3 distanceCrossEdge1;
Vector3.Cross(ref distanceVector, ref e1, out distanceCrossEdge1);
float triangleV;
Vector3.Dot(ref ray.Direction, ref distanceCrossEdge1, out triangleV);
triangleV *= inverseDeterminant;
if (triangleV < 0 || triangleU + triangleV > 1)
return null;
float rayDistance;
Vector3.Dot(ref e2, ref distanceCrossEdge1, out rayDistance);
rayDistance *= inverseDeterminant;
if (rayDistance < 0)
return null;
return rayDistance;
}
return closestHit;
}
I'll admit I don't fully understand all of the math behind the intersection and that is something I'm working on, but my understanding was that if rayDistance was less than 0 the face was facing away from the camera, and shouldn't be counted as a hit.
So my question is, is there an issue with my intersection or ray creation code, or is there another check I need to perform to tell if the face is facing away from the camera, and if so any hints on what that check might contain would be appreciated.