One area where the Avalon platform blurs the line between 2D and 3D is with our hit testing services. It’s easy to discover that we support traditional ray/mesh hit testing via the Model3D.HitTest(…) API, but rarely does the user want to initiate hit testing in world 3-space (this is especially true for the 3D scenarios targeted by Avalon). Consider this simple demo (binary, source) which displays a rotating teapot which changes color when the user clicks on it. (Demo works on Longhorn build 4074).
Mouse input arrives to us a MouseButtonEventArgs. Using e.GetPosition(myViewPort) we get a 2D point relative to the bounds of our ViewPort3D. How does one go from a 2D point like (327, 216) to knowing which part of the teapot is under the mouse pointer? Further more, we do not just want to know if the teapot was hit, we want to know exactly where (in model space) the teapot was hit and in the case of multiple intersections we want distance information so we can sort our results. (And just to be clear, this is is not bounding box testing. Clicking in the “hole” of the teapot’s handle should not register as a hit.)
Don’t Do This
It turns out that this is a platform feature of Avalon so what I am about to describe is exactly what you do not need to know, but ignoring this for the moment the first step would be to use the bounds of the ViewPort3D to normalize the 2D point to the projection plane in post-projective homogeneous space (figure 1).
Of course, in 3-space we are not interested in hit testing a single point. We want to know everything that is under the mouse pointer so we need to project a ray through the point in the viewing direction (figure 2). In post-projective homogeneous space this is along the Z axis. Next we need to transform our ray back into world space. The implementation varies by camera, but is equivalent to transforming by the inverse of the projection transform and view transforms.
This gives us a ray in world space which is exactly the ray you need to provide to Model3D.HitTest(…) at the root of your model hierarchy which will then walk the Model3D hierachy and report which Model3Ds were intersected by the ray.
What You Want To Do
As I mentioned earlier, integrated 2D/3D is a platform feature of Avalon. If you call the 2D hit testing service on IVisual:
PointHitTestResult result2D = visual.HitTest(point);
and if a ViewPort3D is hit during the visual tree walk the point is automatically projected into the ray for the ViewPort3D’s bounds and camera and hit testing continues seamlessly into 3D (figure 3).
If a Model3D is hit during the 3D portion of the hit test the PointHitTestResult object returned by IVisual.HitTest(…) can be cast into a Model3DHitTestResult from which you can find out which Model3D was hit and the details of the intersection.