3D Hit Testing in WPF
I'm writing a WPF application that displays terrain in 3D.
When I perform hit testing, the wrong 3D point is returned (not the point I clicked on).
I tried highlighting the triangle that was hit (by creating a new mesh, taking the coordinates from the RayMeshGeometry3DHitTestResult
object). I see that the wrong triangle gets hit (a triangle is highlighted, but it is not under the cursor).
I'm using a perspective camera with field of view of 60, and the near and far planes are of 3 and 35000 respectively.
Any idea why it might happen and what I can do to solve it?
Let me know if you need any more data.
Edit: This is the code I use to perform the hit testing:
private void m_viewport3d_MouseDown(object sender, MouseButtonEventArgs e)
{
Point mousePos = e.GetPosition(m_viewport3d);
PointHitTestParameters hitParams = new PointHitTestParameters(mousePos);
HitTestResult result = VisualTre开发者_C百科eHelper.HitTest(m_viewport3d, mousePos);
RayMeshGeometry3DHitTestResult rayMeshResult = result as RayMeshGeometry3DHitTestResult;
if (rayMeshResult != null)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex1]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex2]);
mesh.Positions.Add(rayMeshResult.MeshHit.Positions[rayMeshResult.VertexIndex3]);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
GeometryModel3D marker = new GeometryModel3D(mesh, new DiffuseMaterial(Brushes.Blue));
//...add marker to the scene...
}
}
Something that caught me was that the points were in model coords. I had to transform to world coords. Here is my code that does the hit test (this will return all hits under the cursor, not just the first):
// This will cast a ray from the point (on _viewport) along the direction that the camera is looking, and returns hits
private List<RayMeshGeometry3DHitTestResult> CastRay(Point clickPoint, IEnumerable<Visual3D> ignoreVisuals)
{
List<RayMeshGeometry3DHitTestResult> retVal = new List<RayMeshGeometry3DHitTestResult>();
// This gets called every time there is a hit
HitTestResultCallback resultCallback = delegate(HitTestResult result)
{
if (result is RayMeshGeometry3DHitTestResult) // It could also be a RayHitTestResult, which isn't as exact as RayMeshGeometry3DHitTestResult
{
RayMeshGeometry3DHitTestResult resultCast = (RayMeshGeometry3DHitTestResult)result;
if (ignoreVisuals == null || !ignoreVisuals.Any(o => o == resultCast.VisualHit))
{
retVal.Add(resultCast);
}
}
return HitTestResultBehavior.Continue;
};
// Get hits against existing models
VisualTreeHelper.HitTest(grdViewPort, null, resultCallback, new PointHitTestParameters(clickPoint));
// Exit Function
return retVal;
}
And some logic that consumes a hit:
if (hit.VisualHit.Transform != null)
{
return hit.VisualHit.Transform.Transform(hit.PointHit);
}
else
{
return hit.PointHit;
}
You need to provide the ray to hit test along in order for this to work in 3d. Use the correct overload of VisualTreeHelper.HitTest which takes a Visual3D and a RayHitTestParameters: http://msdn.microsoft.com/en-us/library/ms608751.aspx
Figures out it was a Normalize
issue. I shouldn't have normalized the camera's look and up vectors. In the scales I'm using, the distortion is too big for the hit test to work correctly.
精彩评论