Tuesday, 31 August 2010

WPF 3D: Translating a 2D point into 3D world-space

Although WPF 3D is generally a great framework for doing simple 3D work, there are times when you want more facilities than the framework gives you up-front.

An example: WPF gives us the VisualTreeHelper.HitTest() method which takes a 2D coordinate and can be used to hit-test a 3D model. That's great as far as it goes, but if you want to do anything slightly more elaborate than a simple hit-test you'll be left scratching your head. What you commonly need in such situations is to be able to turn a 2D point into 3D ray in order to do calculations, but WPF provides no way to do this.

Except it does - it's just not public. The Camera class defines the following method:

internal abstract RayHitTestParameters RayFromViewportPoint(Point point, Size viewSize, Rect3D boundingRect, out double distanceAdjustment);

Because the method is internal you have to use reflection to call it. Looking at the code in Reflector, it seems the 'boundingRect' and 'distanceAdjustment' parameters are only used when you're using an OrthographicCamera, so for a PerspectiveCamera you'll just need to pass it your 2D point and the size of your Viewport3D and you'll receive a RayHitTestParameters object containing the Origin and Direction of the ray.

Here's the wrapper method I use to call it:

RayHitTestParameters RayFromViewportPoint(Viewport3DVisual viewport, Point point)
{
    MethodInfo method = typeof(Camera).GetMethod("RayFromViewportPoint", BindingFlags.NonPublic | BindingFlags.Instance);
    double distanceAdjustment = 0;
    object[] parameters = new object[] 
    { 
        point, viewport.Viewport.Size, null, distanceAdjustment 
    };

    return (RayHitTestParameters)method.Invoke(viewport.Camera, parameters);
}

1 comment: