tags:

views:

386

answers:

2

I'm new to WPF 3D, so I may just be missing something obvious, but how do I convert from 3D to 2D and (for a given z location) from 2D to 3D?

Specifically, I need two conversion methods:

  • Point3DToPoint - If I have an (x, y, z) coordinate in the 3D world, how do I determine the (x, y) coordinate on the projected 2D surface. Method signature: public Point Point3DToPoint(Point3D point3D)

  • PointAndZToPoint3D - If I have an (x, y) coordinate on the projected 2D surface and a z location in the 3D world, how do I determine the (x, y, z) coordinate in the 3D world? Method signature: public Point3D PointAndZToPoint3D(Point point, double z)

I'd like the 2D coordinate to be the location measured from the upper-left corner of Viewport3D and the 3D coordinate to be the location relative to the origin (0, 0, 0) of the 3D world.

Note 1: I found this related question, but it only addresses conversion from 3D to 2D (not the reverse), and I'm not sure if the answers are up-to-date.

Note 2: I'm currently using .NET 3.5, but if there are improvements in .NET 4.0 that would help me, please let me know.

A: 

You can't directly convert from a 2d point to a 3d point.

Imagine the case where you have a point on the ground.

If you know the direction you want to project the line in (say straight up) you will get a line through the point perpendicular to the ground.

If you know the distance you want to travel you will get a hemisphere (assuming you can't go underground) who's centre is the point.

If you know both you can get a point "in space".

In your case you probably want the direction to be the view direction, but as you don't specify the distance you want the solution is a line. To get the point you'll have to chose a plane in space and then calculate the intersection of the line and plane.

ChrisF
Chris, I'm aware of the problem you're describing...please see the specifications I provided in my question. You *can* convert from a 2D point to a 3D point *if* you provide a `worldZ` coordinate. Providing a `worldZ` eliminates all the ambiguity and allows the method to find the exact point. Now if I just knew how to write the method :)
DanM
+1  A: 

Charles Petzold's 3D Library, which can be downloaded here under "The Petzold.Media3D library", contains a class ViewportInfo with these two static methods:

  • public static Point Point3DToPoint2D(Viewport3D viewport, Point3d point)

  • public static bool Point2DToPoint3D(Viewport3D viewport, Point, ptIn, out LineRange range)

Point2DToPoint3D isn't an exact match for PointAndZToPoint3D() because it returns (via an out parameter) a LineRange rather than a specific point, but it just so happens that LineRange has a method PointFromZ(double zValue), which provides the point where the ray intersects the plane defined by z = zValue.

Code Sample:

using System.Windows;
using System.Windows.Input;
using Petzold.Media3D;

namespace _3DTester
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        /* This MouseDown event handler prints:
          (1) the current position of the mouse
          (2) the 3D mouse location on the ground plane (z = 0)
          (3) the 2D mouse location converted from the 3D location */

        private void Window_MouseDown(object sender, MouseEventArgs e)
        {
            var range = new LineRange();
            var isValid = ViewportInfo.Point2DtoPoint3D(Viewport, e.GetPosition(Viewport), out range);
            if (!isValid)
                MouseLabel.Content = "(no data)";
            else
            {
                var point3D = range.PointFromZ(0);
                var point = ViewportInfo.Point3DtoPoint2D(Viewport, point3D);
                MouseLabel.Content = e.GetPosition(Viewport) + "\n" + point3D + "\n" + point;
            }
        }
    }
}

XAML Code

<Window
    x:Class="_3DTester.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1"
    Height="300"
    Width="300"
    MouseDown="Window_MouseDown">
    <Grid>
        <Viewport3D Name="Viewport">
            <Viewport3D.Camera>
                <PerspectiveCamera
                    Position="0,0,30"
                    LookDirection="0,0,-1" 
                    UpDirection="0,1,0" />
            </Viewport3D.Camera>
            <ModelVisual3D>
                <ModelVisual3D.Content>
                    <Model3DGroup>
                        <DirectionalLight Color="White" Direction="1,-1,-1" />
                        <GeometryModel3D>
                            <GeometryModel3D.Geometry>
                                <MeshGeometry3D
                                    Positions="0,0,10 -5,-5,0 -5,5,0 5,5,0 5,-5,0"
                                    TriangleIndices="2 1 0  2 0 3  4 3 0  1 4 0" />
                            </GeometryModel3D.Geometry>
                            <GeometryModel3D.Material>
                                <DiffuseMaterial Brush="Red" />
                            </GeometryModel3D.Material>
                        </GeometryModel3D>
                    </Model3DGroup>
                </ModelVisual3D.Content>
            </ModelVisual3D>
        </Viewport3D>
        <Label Name="MouseLabel" Content="(no data)" />
    </Grid>
</Window>
DanM