Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is going to be a bit long, so bear with me... </p> <p><strong>1st</strong>, you need to modify the 3DTools library so you can apply a transformation to the <code>TrackballDecorator</code> as follow: </p> <pre><code>using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Input; namespace _3DTools { public class TrackballDecorator : Viewport3DDecorator { #region Private Members private Point m_PreviousPosition2D; private Vector3D m_PreviousPosition3D = new Vector3D(0, 0, 1); private Transform3DGroup m_Transform; private ScaleTransform3D m_Scale = new ScaleTransform3D(); private AxisAngleRotation3D m_Rotation = new AxisAngleRotation3D(); private TranslateTransform3D m_Translate = new TranslateTransform3D(); private readonly Border m_EventSource; #endregion #region Constructor public TrackballDecorator() { TranslateScale = 10; ZoomScale = 1; RotateScale = 1; // the transform that will be applied to the viewport 3d's camera m_Transform = new Transform3DGroup(); m_Transform.Children.Add(m_Scale); m_Transform.Children.Add(new RotateTransform3D(m_Rotation)); m_Transform.Children.Add(m_Translate); // used so that we always get events while activity occurs within // the viewport3D m_EventSource = new Border { Background = Brushes.Transparent }; PreViewportChildren.Add(m_EventSource); } #endregion #region Properties /// &lt;summary&gt; /// A transform to move the camera or scene to the trackball's /// current orientation and scale. /// &lt;/summary&gt; public Transform3DGroup Transform { get { return m_Transform; } set { m_Transform = value; m_Scale = m_Transform.GetScaleTransform3D(); m_Translate = m_Transform.GetTranslateTransform3D(); m_Rotation = m_Transform.GetRotateTransform3D().Rotation as AxisAngleRotation3D; ApplyTransform(); } } public double TranslateScale { get; set; } public double RotateScale { get; set; } public double ZoomScale { get; set; } #endregion #region Event Handling protected override void OnMouseDown(MouseButtonEventArgs e) { base.OnMouseDown(e); m_PreviousPosition2D = e.GetPosition(this); m_PreviousPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, m_PreviousPosition2D); if (Mouse.Captured == null) { Mouse.Capture(this, CaptureMode.Element); } } protected override void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); if (IsMouseCaptured) { Mouse.Capture(this, CaptureMode.None); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (IsMouseCaptured) { Point currentPosition = e.GetPosition(this); // avoid any zero axis conditions if (currentPosition == m_PreviousPosition2D) return; // Prefer tracking to zooming if both buttons are pressed. if (e.LeftButton == MouseButtonState.Pressed) { Track(currentPosition); } else if (e.RightButton == MouseButtonState.Pressed) { Zoom(currentPosition); } else if (e.MiddleButton == MouseButtonState.Pressed) { Translate(currentPosition); } m_PreviousPosition2D = currentPosition; ApplyTransform(); } } private void ApplyTransform() { Viewport3D viewport3D = Viewport3D; if (viewport3D != null) { if (viewport3D.Camera != null) { if (viewport3D.Camera.IsFrozen) { viewport3D.Camera = viewport3D.Camera.Clone(); } if (viewport3D.Camera.Transform != m_Transform) { viewport3D.Camera.Transform = m_Transform; } } } } #endregion Event Handling private void Track(Point currentPosition) { var currentPosition3D = ProjectToTrackball(ActualWidth, ActualHeight, currentPosition); var axis = Vector3D.CrossProduct(m_PreviousPosition3D, currentPosition3D); var angle = Vector3D.AngleBetween(m_PreviousPosition3D, currentPosition3D); // quaterion will throw if this happens - sometimes we can get 3D positions that // are very similar, so we avoid the throw by doing this check and just ignoring // the event if (axis.Length == 0) return; var delta = new Quaternion(axis, -angle); // Get the current orientantion from the RotateTransform3D var r = m_Rotation; var q = new Quaternion(m_Rotation.Axis, m_Rotation.Angle); // Compose the delta with the previous orientation q *= delta; // Write the new orientation back to the Rotation3D m_Rotation.Axis = q.Axis; m_Rotation.Angle = q.Angle; m_PreviousPosition3D = currentPosition3D; } private static Vector3D ProjectToTrackball(double width, double height, Point point) { var x = point.X / (width / 2); // Scale so bounds map to [0,0] - [2,2] var y = point.Y / (height / 2); x = x - 1; // Translate 0,0 to the center y = 1 - y; // Flip so +Y is up instead of down var z2 = 1 - x * x - y * y; // z^2 = 1 - x^2 - y^2 var z = z2 &gt; 0 ? Math.Sqrt(z2) : 0; return new Vector3D(x, y, z); } private void Zoom(Point currentPosition) { var yDelta = currentPosition.Y - m_PreviousPosition2D.Y; var scale = Math.Exp(yDelta / 100) / ZoomScale; // e^(yDelta/100) is fairly arbitrary. m_Scale.ScaleX *= scale; m_Scale.ScaleY *= scale; m_Scale.ScaleZ *= scale; } private void Translate(Point currentPosition) { // Calculate the panning vector from screen(the vector component of the Quaternion // the division of the X and Y components scales the vector to the mouse movement var qV = new Quaternion(((m_PreviousPosition2D.X - currentPosition.X) / TranslateScale), ((currentPosition.Y - m_PreviousPosition2D.Y) / TranslateScale), 0, 0); // Get the current orientantion from the RotateTransform3D var q = new Quaternion(m_Rotation.Axis, m_Rotation.Angle); var qC = q; qC.Conjugate(); // Here we rotate our panning vector about the the rotaion axis of any current rotation transform // and then sum the new translation with any exisiting translation qV = q * qV * qC; m_Translate.OffsetX += qV.X; m_Translate.OffsetY += qV.Y; m_Translate.OffsetZ += qV.Z; } } } </code></pre> <p>The <code>GetXXXTransform3D</code> methods are extension methods defined as follow:</p> <pre><code>public static ScaleTransform3D GetScaleTransform3D(this Transform3DGroup transform3DGroup) { ScaleTransform3D scaleTransform3D = null; if (transform3DGroup != null) { foreach (var transform in transform3DGroup.Children) { scaleTransform3D = transform as ScaleTransform3D; if (scaleTransform3D != null) return scaleTransform3D; } } return scaleTransform3D; } public static RotateTransform3D GetRotateTransform3D(this Transform3DGroup transform3DGroup) { RotateTransform3D rotateTransform3D = null; if (transform3DGroup != null) { foreach (var transform in transform3DGroup.Children) { rotateTransform3D = transform as RotateTransform3D; if (rotateTransform3D != null) return rotateTransform3D; } } return rotateTransform3D; } public static TranslateTransform3D GetTranslateTransform3D(this Transform3DGroup transform3DGroup) { TranslateTransform3D translateTransform3D = null; if (transform3DGroup != null) { foreach (var transform in transform3DGroup.Children) { translateTransform3D = transform as TranslateTransform3D; if (translateTransform3D != null) return translateTransform3D; } } return translateTransform3D; } </code></pre> <p><strong>2nd</strong>, you need to declare a <code>Transform</code> to your <code>PerspectiveCamera</code> as follow:<br> (the example is taken from Sasha Barber's <a href="http://www.codeproject.com/KB/WPF/WPF3D_1.aspx?msg=2488254" rel="nofollow">Elements3D</a> project which I used to test this) </p> <pre><code>&lt;Tools:TrackballDecorator x:Name="tbViewPort"&gt; &lt;Viewport3D x:Name="vpFeeds"&gt; &lt;Viewport3D.Camera&gt; &lt;PerspectiveCamera x:Name="camera" Position="-2,2,40" LookDirection="2,-2,-40" FieldOfView="90"&gt; &lt;PerspectiveCamera.Transform&gt; &lt;Transform3DGroup /&gt; &lt;/PerspectiveCamera.Transform&gt; &lt;/PerspectiveCamera&gt; &lt;/Viewport3D.Camera&gt; &lt;ContainerUIElement3D x:Name="container" /&gt; &lt;ModelVisual3D x:Name="model"&gt; &lt;ModelVisual3D.Content&gt; &lt;DirectionalLight Color="White" Direction="-1,-1,-1" /&gt; &lt;/ModelVisual3D.Content&gt; &lt;/ModelVisual3D&gt; &lt;/Viewport3D&gt; &lt;/Tools:TrackballDecorator&gt; </code></pre> <p><strong>3rd</strong>, since we are going to store each part of the whole transformation in a separate value, you need to create the relevant properties in your settings file, i.e. <code>CameraScaleX</code>, <code>CameraScaleY</code>, <code>CameraScaleZ</code>, <code>CameraTranslateX</code>, <code>CameraTranslateY</code>, <code>CameraTranslateZ</code>, <code>CameraRotateAxisX</code>, <code>CameraRotateAxisY</code>, <code>CameraRotateAxisZ</code> and <code>CameraRotateAngle</code>. All are of type <code>double</code> and are stored in User scope.</p> <p><strong>4th</strong> and last step is to actually save and load these settings into the camera using the following code: </p> <pre><code>private void SaveCameraSettings() { var transform3DGroup = camera.Transform as Transform3DGroup; if (transform3DGroup != null) { foreach (var transform in transform3DGroup.Children) { var scale = transform as ScaleTransform3D; if (scale != null) SaveCameraSetting(scale); var rotate = transform as RotateTransform3D; if (rotate != null) SaveCameraSetting(rotate); var translate = transform as TranslateTransform3D; if (translate != null) SaveCameraSetting(translate); } Settings.Default.Save(); } } private static void SaveCameraSetting(ScaleTransform3D transform) { Properties.Settings.Default.CameraScaleX = transform.ScaleX; Properties.Settings.Default.CameraScaleY = transform.ScaleY; Properties.Settings.Default.CameraScaleZ = transform.ScaleZ; } private static void SaveCameraSetting(RotateTransform3D transform) { var axisAngleRotation3D = transform.Rotation as AxisAngleRotation3D; if (axisAngleRotation3D != null) { Properties.Settings.Default.CameraRotateAxisX = axisAngleRotation3D.Axis.X; Properties.Settings.Default.CameraRotateAxisY = axisAngleRotation3D.Axis.Y; Properties.Settings.Default.CameraRotateAxisZ = axisAngleRotation3D.Axis.Z; Properties.Settings.Default.CameraRotateAngle = axisAngleRotation3D.Angle; } } private static void SaveCameraSetting(TranslateTransform3D transform) { Properties.Settings.Default.CameraTranslateX = transform.OffsetX; Properties.Settings.Default.CameraTranslateY = transform.OffsetY; Properties.Settings.Default.CameraTranslateZ = transform.OffsetZ; } private void LoadCameraPosition() { var d = Settings.Default; var transform3DGroup = new Transform3DGroup(); var scaleTransform3D = new ScaleTransform3D(d.CameraScaleX, d.CameraScaleY, d.CameraScaleZ); var translateTransform3D = new TranslateTransform3D(d.CameraTranslateX, d.CameraTranslateY, d.CameraTranslateZ); var axisAngleRotation3D = new AxisAngleRotation3D(new Vector3D(d.CameraRotateAxisX, d.CameraRotateAxisY, d.CameraRotateAxisZ), d.CameraRotateAngle); var rotateTransform3D = new RotateTransform3D(axisAngleRotation3D); transform3DGroup.Children.Add(scaleTransform3D); transform3DGroup.Children.Add(translateTransform3D); transform3DGroup.Children.Add(rotateTransform3D); tbViewPort.Transform = transform3DGroup; } </code></pre> <p>Hopefully, I didn't forget anything. If you need more help or don't understand something, please don't hesitate to ask ;-)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload