10-14-2017, 08:23 PM
One way to mark a location in 3D space is to use 2D overlay Canvas. For this you create a Canvas over the Viewport3D object (or DXViewportView if you are using DXEngine). Then on each camera change or Viewport3D size change, you call Point3DTo2D method on the camera to get a 2D position of a 3D object. Then you can put any WPF Shape, Border or other element to the overlay canvas to the calculated 2D coordinates. There are a few samples in the Ab3d.PowerToys samples project - just do a text search for "OverlayCanvas".
A drawback of this solution is that the elements on OverlayCanvas is that those elements are always on top of 3D scene and cannot be hidden behind other 3D objects.
For such situations you will need to create a real 3D object. I have developed a few helper methods that can be used to get the size of 3D object based on the desired screen size and position in 3D world. Those methods will be part of the next version of Ab3d.PowerToys, but for you convenience I am publishing two of the methods here:
See remarks to see how to calculate the lookDirectionDistance for the GetPerspectiveWorldSize method.
A drawback of this solution is that the elements on OverlayCanvas is that those elements are always on top of 3D scene and cannot be hidden behind other 3D objects.
For such situations you will need to create a real 3D object. I have developed a few helper methods that can be used to get the size of 3D object based on the desired screen size and position in 3D world. Those methods will be part of the next version of Ab3d.PowerToys, but for you convenience I am publishing two of the methods here:
Code:
/// <summary>
/// GetPerspectiveWorldSize calculates a size in 3D world from
/// a screen Size (screenSize in screen coordinates) that is at lookDirectionDistance and shown with perspective camera with fieldOfView and in Viewport3D with viewport3DSize.
/// </summary>
/// <param name="screenSize">Size on screen (in same units as size of Viewport3D.Width - without DPI scale)</param>
/// <param name="lookDirectionDistance">distance from camera in the camera's look direction (see remarks for more info)</param>
/// <param name="fieldOfView">camera's field of view</param>
/// <param name="viewport3DSize">Viewport3D's size</param>
/// <returns>3D world size</returns>
/// <remarks>
/// <para>
/// <b>GetPerspectiveWorldSize</b> calculates a size in 3D world from
/// a screen Size (screenSize in screen coordinates) that is at distanceFromCamera and shown with perspective camera with fieldOfView and in Viewport3D with viewport3DSize.
/// </para>
/// <para>
/// To get the most accurate results the <b>lookDirectionDistance</b> must not be the direct distance from the camera but the distance in the camera's look direction.
/// The lookDirectionDistance can be calculated with the following code:
/// </para>
/// <code>
/// var targetPosition = new Point3D(x,y,z);
/// var cameraPosition = Camera1.GetCameraPosition();
///
/// var distanceVector = targetPosition - cameraPosition;
///
/// var lookDirection = Camera1.LookDirection;
/// lookDirection.Normalize();
///
/// // To get look direction distance we project the distanceVector to the look direction vector
/// var lookDirectionDistance = Vector3D.DotProduct(distanceVector, lookDirection);
///
/// var screenSize = GetPerspectiveScreenSize(worldSize, lookDirectionDistance, Camera1.FieldOfView, viewport3DSize)
/// </code>
/// </remarks>
public static Size GetPerspectiveWorldSize(Size screenSize, double lookDirectionDistance, double fieldOfView, Size viewport3DSize)
{
double aspectRatio = viewport3DSize.Width / viewport3DSize.Height;
//double xScale = 1.0 / Math.Tan(fieldOfView * Math.PI / 360);
//double yScale = xScale * aspectRatio;
//Size worldSize = new Size((2 * lookDirectionDistance * screenSize.Width) / (viewport3DSize.Width * xScale),
// (2 * lookDirectionDistance * screenSize.Height) / (viewport3DSize.Height * yScale));
double xScale = Math.Tan(fieldOfView * Math.PI / 360);
var worldSize = new Size((2 * lookDirectionDistance * screenSize.Width * xScale) / (viewport3DSize.Width),
(2 * lookDirectionDistance * screenSize.Height * xScale) / (viewport3DSize.Height * aspectRatio));
return worldSize;
}
/// <summary>
/// GetOrthographicWorldSize calculates a size in 3D world from
/// a screen Size (screenSize in screen coordinates) that is at distanceFromCamera and shown with orthographic camera with cameraWidth and in Viewport3D with viewport3DSize.
/// </summary>
/// <param name="screenSize">Size on screen (in same units as size of Viewport3D.Width - without DPI scale)</param>
/// <param name="cameraWidth">camera's width</param>
/// <param name="viewport3DSize">Viewport3D's size</param>
/// <returns>3D world size</returns>
public static Size GetOrthographicWorldSize(Size screenSize, double cameraWidth, Size viewport3DSize)
{
double aspectRatio = viewport3DSize.Width / viewport3DSize.Height;
// The equations here are derived from the one in GetOrthographicScreenSize
Size worldSize = new Size((screenSize.Width * cameraWidth) / viewport3DSize.Width,
(screenSize.Height * cameraWidth) / (viewport3DSize.Height * aspectRatio));
return worldSize;
}See remarks to see how to calculate the lookDirectionDistance for the GetPerspectiveWorldSize method.
Andrej Benedik

