using System;
using System.Windows.Media.Media3D;
namespace HelixToolkit
{
public static class StereoHelper
{
///
/// Find the focal length given the field of view and the format
/// http://en.wikipedia.org/wiki/Angle_of_view
///
/// field of view (degrees)
/// e.g. 36mm
/// The focal length in the same unit as the format
public static double FindFocalLength(double fov, double format)
{
return format / 2 / Math.Tan(fov / 2 * Math.PI / 180);
}
///
/// Calculate the stereo base using the full Bercovitz formula
///
/// Largest distance from the camera lens
/// Nearest distance from the camera lens
/// Width of screen
/// depth ratio 1/30
/// Horizontal field of view
/// The stereo base
public static double CalculateStereoBase(double L, double N, double screenWidth, double depthRatio, double hfov)
{
double formatHoriz = screenWidth;
double F = FindFocalLength(hfov, formatHoriz);
double P = depthRatio * formatHoriz;
return CalculateStereoBase(P, L, N, F);
}
///
/// Calculate the stereo base using the full Bercovitz formula
/// B = P(LN/(L-N)) (1/F - (L+N)/2LN)
/// http://nzphoto.tripod.com/stereo/3dtake/fbercowitz.htm
///
/// Parallax aimed for, in mm on the film
/// Largest distance from the camera lens, mm
/// Nearest distance from the camera lens, mm
/// Focal length of the lens, mm
/// The stereo base
public static double CalculateStereoBase(double P, double L, double N, double F)
{
return P * (L * N / (L - N)) * (1 / F - (L + N) / (2 * L * N));
}
///
/// Updates the left and right camera based on a center camera.
///
/// Center camera (input)
/// Left camera (is updated)
/// Right camera (is updated)
/// Stereo base
/// true for cross-viewingm false for parallel-viewing
/// use the same UpDirection for both cameras
/// use the same LookDirection for both cameras
public static void UpdateStereoCameras(PerspectiveCamera centerCamera, PerspectiveCamera leftCamera, PerspectiveCamera rightCamera,
double stereoBase, bool crossViewing, bool sameUpDirection, bool sameDirection)
{
if (centerCamera == null || leftCamera == null || rightCamera == null)
return;
double sb = stereoBase;
if (crossViewing)
sb *= -1;
var lookAt = centerCamera.Position + centerCamera.LookDirection;
var right = Vector3D.CrossProduct(centerCamera.LookDirection, centerCamera.UpDirection);
right.Normalize();
leftCamera.Position = centerCamera.Position - right * sb / 2;
if (sameDirection)
leftCamera.LookDirection = centerCamera.LookDirection;
else
leftCamera.LookDirection = lookAt - leftCamera.Position;
rightCamera.Position = centerCamera.Position + right * sb / 2;
if (sameDirection)
rightCamera.LookDirection = centerCamera.LookDirection;
else
rightCamera.LookDirection = lookAt - rightCamera.Position;
// TODO: not sure what is best here?
if (sameUpDirection)
{
leftCamera.UpDirection = centerCamera.UpDirection;
rightCamera.UpDirection = centerCamera.UpDirection;
}
else
{
leftCamera.UpDirection = Vector3D.CrossProduct(right, leftCamera.LookDirection);
rightCamera.UpDirection = Vector3D.CrossProduct(right, rightCamera.LookDirection);
}
leftCamera.FieldOfView = centerCamera.FieldOfView;
leftCamera.NearPlaneDistance = centerCamera.NearPlaneDistance;
leftCamera.FarPlaneDistance = centerCamera.FarPlaneDistance;
rightCamera.FieldOfView = centerCamera.FieldOfView;
rightCamera.NearPlaneDistance = centerCamera.NearPlaneDistance;
rightCamera.FarPlaneDistance = centerCamera.FarPlaneDistance;
}
///
/// Create a clone of a Visual3D
///
/// a Visual3D
/// the clone
public static Visual3D CreateClone(Visual3D v)
{
if (v is ModelUIElement3D)
{
var m = v as ModelUIElement3D;
if (m.Model!= null)
{
/*if (m.Model.CanFreeze)
m.Model.Freeze();
if (m.Model.IsFrozen)*/
{
var clonedModel = m.Model.Clone();
var clonedElement = new ModelUIElement3D();
clonedElement.Transform = m.Transform;
clonedElement.Model = clonedModel;
return clonedElement;
}
}
}
if (v is ModelVisual3D)
{
var m = v as ModelVisual3D;
var clone = new ModelVisual3D();
clone.Transform = m.Transform;
if (m.Content != null && m.Content.CanFreeze)
{
m.Content.Freeze();
var clonedModel = m.Content.Clone();
clone.Content = clonedModel;
}
if (m.Children.Count>0)
{
foreach (var child in m.Children)
{
var clonedChild = CreateClone(child);
clone.Children.Add(clonedChild);
}
}
return clone;
}
return null;
}
}
}