Screen to volume transform?
#1
So I've started using AB4D to draw 3D lines using Ab3d.Visuals.PolyLineVisual3D rendered in a Viewport3D . . .

Code:
<dxControls:DXViewportView Name="MainViewportView" PresentationType="DirectXImage">
            <Viewport3D Name="MainViewport" ClipToBounds="true" Margin="230,70,20,170" >
            </Viewport3D>
        </dxControls:DXViewportView>

Everything is working very well but the next thing I need to do is draw a rectangle in screen-coordinates (which I can do with the regular Windows System.Windows.Shapes.Line) and use that as a bounding box for the geometry in 3D-space.

This is easily done if I know the transform, including the perspective component, from the points in the 3D space to the screen (or vice versa). How do I get that transform? What is it called in AB4D or WPF?

Thanks in advance!
#2
You can get the camera matrices with using GetCameraMatrixes method on the Ab3d.PowerToys Camera.

On the existing camera instance, you can use the following method:
bool GetCameraMatrixes(out Matrix3D viewMatrix, out Matrix3D projectionMatrix)

Or you can use any of the following static methods on Ab3d.Cameras.BaseCamera:

bool GetCameraMatrixes(Viewport3D viewport3D, out Matrix3D viewMatrix, out Matrix3D projectionMatrix)

bool GetCameraMatrixes(Camera camera, double viewportAspectRatio, out Matrix3D viewMatrix, out Matrix3D projectionMatrix)

bool GetCameraMatrixes(BaseCamera camera, double viewportAspectRatio, out Matrix3D viewMatrix, out Matrix3D projectionMatrix)
Andrej Benedik
#3
(01-23-2017, 09:45 PM)abenedik Wrote: You can get the camera matrices with using GetCameraMatrixes method on the Ab3d.PowerToys Camera.

On the existing camera instance, you can use the following method:
bool GetCameraMatrixes(out Matrix3D viewMatrix, out Matrix3D projectionMatrix)

I'm sorry for being so dense about this, but I'm not explicitly instantiating a PowerToys camera - I'm using a Viewport3D camera . . .

Code:
<dxControls:DXViewportView Name="MainViewportView" PresentationType="DirectXImage" Margin="230,70,20,170" ClipToBounds="true">
            <Viewport3D Name="MainViewport" >
                <Viewport3D.Camera>
                    <OrthographicCamera />
                </Viewport3D.Camera>
            </Viewport3D>
        </dxControls:DXViewportView>

How do I relate or convert these in my code or should my XAML explicitly use PowerToys classes?
#4
In this case you can use SharpDX Matrix class.

For example:
MatrixviewMatrix;
Matrix.LookAtRH(ref cameraPosition, ref target, ref up, out viewMatrix);

var orthoMatrix = Matrix.OrthoRH(width, height, zNear, zFar);

var viewProj = viewMatrix * orthoMatrix ;

// To get WPF Matrix3D you can use extension method from DXEngine
var wpfMatix = viewProj .ToWpfMatrix3D()
Andrej Benedik
#5
I'm still not "getting" what you're trying to explain.

I have a Viewport3D. It has a certain physical size on the screen - X pixels by Y pixels. The Viewport3D has an orthographic camera with a position, an up direction a look direction etc. Here's the XAML describing my Viewport3D...

Code:
<dxControls:DXViewportView Name="MainViewportView" PresentationType="DirectXImage" Margin="230,70,20,170" ClipToBounds="true">
            <Viewport3D Name="MainViewport" >
                <Viewport3D.Camera>
                    <OrthographicCamera LookDirection="0,0,-1" Position="0,0,10"  UpDirection="0, 1, 0"  />
                </Viewport3D.Camera>
            </Viewport3D>
        </dxControls:DXViewportView>

Is there an implicit view volume from which I can determine the width and height for the OrthoRH Matrix in your previous answer?

And here's some sample C# to draw some lines . . .

Code:
Point3D point3Da = new Point3D(-0.99, 0.6, -5);
                Point3D point3Db = new Point3D(-0.99, -0.6, -5);
                Point3D point3Dc = new Point3D(0.99, -0.6, -5);
                Point3D point3Dd = new Point3D(0.99, 0.6, -5);
                Point3D point3De = new Point3D(0.0, 0.0, -5);
                allPositions.Add(point3Da);
                allPositions.Add(point3Db);
                allPositions.Add(point3Dc);
                allPositions.Add(point3Dd);
                allPositions.Add(point3De);

                var simplePositionCollection = new Point3DCollection(allPositions);
                int size = simplePositionCollection.Count;
                MainViewport.BeginInit();
                MainViewport.Children.Clear();
                var polyLineVisual3D = new Ab3d.Visuals.PolyLineVisual3D()
                {
                    Positions = simplePositionCollection,
                    LineColor = Colors.DarkOrange,
                    LineThickness = 3,   // was 1 -  made it 3 so see better on screen shots
                    Transform = new TranslateTransform3D(0, 0, 0)  
                };
                MainViewport.Children.Add(polyLineVisual3D);
                MainViewport.EndInit();

... now these particular lines just outline the Viewport3D (see the attachment). So what I want to know is, for a given Point3D in the coordinate system of the polyLineVisual3D's in the code, where will that appear in pixel-space in the Viewport3D? Thanks in advance.
   
#6
When using cameras from Ab3d.PowerToys library, you can easily get the 2D point on the screen from the 3D position with using the Point3DTo2D method on a Ab3d.PowerToys camera.

You can also convert a 2D point on the screen into a 3D ray (3D position and 3D vector) - in this case you cannot get only one 3D point but a ray. You can get this with calling CreateMouseRay3D method.


If you are not using Ab3d.PowerToys cameras, you can get the 2D screen position with first calculating the view-projection matrix (see viewProj in my previous post).

Then you need to need to multiply the 3D position (converted into Point4D) with viewProjection and divide by w:

Code:
Point4D point4d = ((Point4D)point3D) * viewProj;

if (point4d.W != 0)
     screenPosition = new Point(point4d.X / point4d.W, point4d.Y / point4d.W);
else
     screenPosition = new Point(double.NaN, double.NaN);

To calculate the ray from 2D position to 3D space, you can use the Ab3d.Cameras.BaseCamera.CreateMouseRay3D static method - the following is the method signature:

public static bool CreateMouseRay3D(Point mousePosition, Size viewportSize, ref Matrix3D viewMatrix, ref Matrix3D projectionMatrix, out Point3D rayOrigin, out Vector3D rayDirection)
Andrej Benedik
#7
(02-14-2017, 10:54 AM)abenedik Wrote: When using cameras from Ab3d.PowerToys library, you can easily get the 2D point on the screen from the 3D position with using the Point3DTo2D method on a Ab3d.PowerToys camera.

Then should I switch to using the Ab3dPowerToys camera instead of the Viewport3D camera? How easy is it to replace the Viewport3D camera, what does the XAML and c# look like and what are the consequences?

Quote:If you are not using Ab3d.PowerToys cameras, you can get the 2D screen position with first calculating the view-projection matrix (see viewProj in my previous post).

I didn't understand that matrix, which depends on viewProj because I didn't understand width and height as mentioned in that post . . .
Quote:Matrix.LookAtRH(ref cameraPosition, ref target, ref up, out viewMatrix);

var orthoMatrix = Matrix.OrthoRH(width, height, zNear, zFar);

var viewProj = viewMatrix * orthoMatrix ;

... but I haven't explicitly declared a view volume anywhere and I don't see how a camera with near and far plane distances and a look direction can implicitly specify a volume (because there are no explicit right/left sides or top/bottom, so what goes in width and height for the orthoRH? The MainViewport has a Height and ActualHeight and Width and ActualWith in pixels space - 564 and 936 - but that's clearly not the coordinate system the lines are in. Orthographic cameras have a Width property (but no Height) too but I'm not specifying it in my example.

Quote:Then you need to need to multiply the 3D position (converted into Point4D) with viewProjection and divide by w:

Code:
Point4D point4d = ((Point4D)point3D) * viewProj;

if (point4d.W != 0)
     screenPosition = new Point(point4d.X / point4d.W, point4d.Y / point4d.W);
else
     screenPosition = new Point(double.NaN, double.NaN);

To calculate the ray from 2D position to 3D space, you can use the Ab3d.Cameras.BaseCamera.CreateMouseRay3D static method - the following is the method signature:

public static bool CreateMouseRay3D(Point mousePosition, Size viewportSize, ref Matrix3D viewMatrix, ref Matrix3D projectionMatrix, out Point3D rayOrigin, out Vector3D rayDirection)

Summary:
1. I need to decide if it would be easier to use a power toys camera instead of the one I have now
2. If I stick with the camera I have now I need to understand how to build the matrices
#8
(02-14-2017, 07:05 PM)pnelsonAG Wrote: Then should I switch to using the Ab3dPowerToys camera instead of the Viewport3D camera? How easy is it to replace the Viewport3D camera, what does the XAML and c# look like and what are the consequences?

If you already have written a lot of code that works with existing WPF cameras, then you do not need to throw all this away and use Ab3d.PowerToys cameras. But if you do not require WPF cameras, then my advice is to use Ab3d.PowerToys cameras. They are much easier to be used because they use angles to define view direction instead of vectors. Also they provide many additional methods (such as Point3DTo2D, etc.). What is more, with Ab3d.PowerToys camera you can use MouseCameraController to allow rotating and moving the camera with mouse.

The usage is very simple:

If you will define the camera in xaml, you first need to add xaml namespace declaration (the second namespace declaration is used for MouseCameraController - see below):

Code:
xmlns:cameras="clr-namespace:Ab3d.Cameras;assembly=Ab3d.PowerToys"
xmlns:ab3d="clr-namespace:Ab3d.Controls;assembly=Ab3d.PowerToys"

Then you can define the Ab3d.PowerToys camera somewhere near the Viewport3D (because it is not possible to derive out classes from WPF cameras, you cannot set Ab3d.PowerToys camera to the Viewport3D.Camera property):

Code:
<Viewport3D Name="MainViewport">
              <!-- you can define 3D objects here--->
        </Viewport3D>

        <cameras:TargetPositionCamera Name="Camera1"
                                      Heading="30" Attitude="-20" Bank="0" TargetPosition="0 0 0"
                                      CameraType="OrthographicCamera"
                                      CameraWidth="300"
                                      ShowCameraLight="Always"
                                      TargetViewport3D="{Binding ElementName=MainViewport}"/>

        <ab3d:MouseCameraController Name="MouseCameraController1"
                                    RotateCameraConditions="LeftMouseButtonPressed"
                                    MoveCameraConditions="LeftMouseButtonPressed, ControlKey"
                                    TargetCamera="{Binding ElementName=Camera1}"/>

Note that in this case you do not need to define WPF camera inside Viewport3D.
Also see how binding is used in TargetViewport3D property to attach the TargetPositionCamera to the Viewport3D. This sample code also shows how to add MouseCameraController.

This shows only how to use TargetPositionCamera. There are other types of cameras also. Please see the samples that come with Ab3d.PowerToys library for more info.


Quote:I didn't understand that matrix, which depends on viewProj because I didn't understand width and height as mentioned in that post . . .

... but I haven't explicitly declared a view volume anywhere and I don't see how a camera with near and far plane distances and a look direction can implicitly specify a volume (because there are no explicit right/left sides or top/bottom, so what goes in width and height for the orthoRH? The MainViewport has a Height and ActualHeight and Width and ActualWith in pixels space - 564 and 936 - but that's clearly not the coordinate system the lines are in. Orthographic cameras have a Width property (but no Height) too but I'm not specifying it in my example.

OrthographicCamera have Width property - if you create a new instance of OrthographicCamera, you will see that its value is set to 2 by default. This defines the width of the view volume.

The height of the view volume can then be calculated with using the ActualWidth and ActualHeight:

Code:
double orthographicHeight = orthographicCamera.Width * MainViewport.ActualHeight / MainViewport.ActualWidth;

The OrthographicCamera also define default values for NearPlaneDistance and FarPlaneDistance - NearPlaneDistance is set to 0.125 and FarPlaneDistance is set to Infinity. Because Infinity is not a valid value that can be used in the matrix calculation (in Matrix.OrthoRH), this value most probably means that WPF is dynamically adjusting the value based on the size of all objects in the 3D scene.

You probably know how big your 3D scene is, so you can also define the FarPlaneDistance - for example 1000. Be careful there - you should not define any very big value because the difference between FarPlaneDistance and NearPlaneDistance define the 3D depth resolution of your 3D scene - if depth resolution is low (the difference between FarPlaneDistance and NearPlaneDistance
is very big), this can lead to z-fighting problems (two 3D positions have the same depth) - see internet for more info.

I hope that now you will know how to get all the properties that are needed to get the camera matrices manually.

But as you see, if you use Ab3d.PowerToys library you do not need to worry about many of the internals about 3D graphics.
Andrej Benedik
  


Forum Jump:


Users browsing this thread:
1 Guest(s)