View of /trunk/DefaultRenderer/Trackball3D.cs
Parent Directory
|
Revision Log
Revision 10 -
(download)
(annotate)
Thu Aug 21 10:54:21 2008 UTC (4 years, 9 months ago) by albert
File size: 11566 byte(s)
Thu Aug 21 10:54:21 2008 UTC (4 years, 9 months ago) by albert
File size: 11566 byte(s)
//------------------------------------------------------------------ // // The following article discusses the mechanics behind this // trackball implementation: http://viewport3d.com/trackball.htm // // Reading the article is not required to use this sample code, // but skimming it might be useful. // // For licensing information and to get the latest version go to: // http://workspaces.gotdotnet.com/3dtools // //------------------------------------------------------------------ using System; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Controls; using Petzold.Media3D; namespace Xenki.DefaultRenderer { /// <summary> /// Trackball is a utility class which observes the mouse events /// on a specified FrameworkElement and produces a Transform3D /// with the resultant rotation and scale. /// /// Example Usage: /// /// Trackball trackball = new Trackball(); /// trackball.EventSource = myElement; /// myViewport3D.Camera.Transform = trackball.Transform; /// /// Because Viewport3Ds only raise events when the mouse is over the /// rendered 3D geometry (as opposed to not when the mouse is within /// the layout bounds) you usually want to use another element as /// your EventSource. For example, a transparent border placed on /// top of your Viewport3D works well: /// /// <Grid> /// <ColumnDefinition /> /// <RowDefinition /> /// <Viewport3D Name="myViewport" ClipToBounds="True" Grid.Row="0" Grid.Column="0" /> /// <Border Name="myElement" Background="Transparent" Grid.Row="0" Grid.Column="0" /> /// </Grid> /// /// NOTE: The Transform property may be shared by multiple Cameras /// if you want to have auxilary views following the trackball. /// /// It can also be useful to share the Transform property with /// models in the scene that you want to move with the camera. /// (For example, the Trackport3D's headlight is implemented /// this way.) /// /// You may also use a Transform3DGroup to combine the /// Transform property with additional Transforms. /// </summary> public class Trackball { private FrameworkElement _eventSource; private Point _previousPosition2D; private Vector3D _previousPosition3D = new Vector3D(0, 0, 1); private Transform3DGroup _transform; private ScaleTransform3D _scale = new ScaleTransform3D(); private AxisAngleRotation3D _rotation = new AxisAngleRotation3D(); private TranslateTransform3D _translate = new TranslateTransform3D(); private Vector3D _cameraLookDirection= new Vector3D(128,128,10); private PerspectiveCamera _camera; private Point3D _cameraLookPoint; private RotateTransform3D _rotationTransform =new RotateTransform3D(); public Trackball(PerspectiveCamera camera) { _camera = camera; _rotationTransform.Rotation = _rotation; _transform = new Transform3DGroup(); _transform.Children.Add(_scale); _transform.Children.Add(_rotationTransform); _transform.Children.Add(_translate); } /// <summary> /// A transform to move the camera or scene to the trackball's /// current orientation and scale. /// </summary> public Transform3D Transform { get { return _transform; } } #region Event Handling /// <summary> /// The FrameworkElement we listen to for mouse events. /// </summary> public FrameworkElement EventSource { get { return _eventSource; } set { if (_eventSource != null) { _eventSource.MouseDown -= this.OnMouseDown; _eventSource.MouseUp -= this.OnMouseUp; _eventSource.MouseMove -= this.OnMouseMove; _eventSource.KeyDown -= this.OnKeyDown; } _eventSource = value; _eventSource.MouseDown += this.OnMouseDown; _eventSource.MouseUp += this.OnMouseUp; _eventSource.MouseMove += this.OnMouseMove; _eventSource.KeyDown += OnKeyDown; } } private void OnKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Left) _translate.OffsetX = -1; else if (e.Key == Key.Right) _translate.OffsetX = 1; else if (e.Key == Key.Up) _translate.OffsetY = 1; else if (e.Key == Key.Down) _translate.OffsetY = -1; } private void OnMouseDown(object sender, MouseEventArgs e) { Mouse.Capture(EventSource, CaptureMode.Element); Point pclick = e.GetPosition(EventSource); HitTestResult hitresult = VisualTreeHelper.HitTest(EventSource, pclick); RayMeshGeometry3DHitTestResult result3d = hitresult as RayMeshGeometry3DHitTestResult; if (result3d == null) return; ModelVisual3D visul3d = result3d.VisualHit as ModelVisual3D; if (visul3d == null) return; _cameraLookPoint = new Point3D(visul3d.Content.Bounds.X, visul3d.Content.Bounds.Y, visul3d.Content.Bounds.Z); //TranslateTransform3D tempTranslate = visul3d.Transform as TranslateTransform3D; //if (tempTranslate == null) // return; //Petzold.Media3D.LineRange line; //Petzold.Media3D.ViewportInfo.Point2DtoPoint3D(_eventSource as Viewport3D, pclick, out line); //_cameraLookPoint = line.PointFromZ(visul3d.Content.Bounds.Z); _camera.LookDirection = _cameraLookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position); _rotationTransform.CenterX = _cameraLookPoint.X; _rotationTransform.CenterY = _cameraLookPoint.Y; _rotationTransform.CenterZ = _cameraLookPoint.Z; } private void OnMouseUp(object sender, MouseEventArgs e) { Mouse.Capture(EventSource, CaptureMode.None); } private void OnMouseMove(object sender, MouseEventArgs e) { Point currentPosition = e.GetPosition(EventSource); if (e.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))) { Look(currentPosition); } else if (e.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))) { Focus(currentPosition); } else if (e.LeftButton == MouseButtonState.Pressed) { // Pan(currentPosition); } else if (e.RightButton == MouseButtonState.Pressed) { // Zoom(currentPosition); } _previousPosition2D = currentPosition; } #endregion Event Handling private void Look(Point currentPosition) { Vector3D currentPosition3D = ProjectToTrackball( EventSource.ActualWidth, EventSource.ActualHeight, currentPosition); Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D); double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D); Quaternion delta = new Quaternion(axis, -angle); // Get the current orientantion from the RotateTransform3D AxisAngleRotation3D r = _rotation; Quaternion q = new Quaternion(_rotation.Axis, _rotation.Angle); // Compose the delta with the previous orientation q *= delta; // Write the new orientation back to the Rotation3D _rotation.Axis = q.Axis; _rotation.Angle = q.Angle; _previousPosition3D = currentPosition3D; } private void Pan(Point currentPosition) { Vector3D currentPosition3D = ProjectToTrackball( EventSource.ActualWidth, EventSource.ActualHeight, currentPosition); Vector change = Point.Subtract(_previousPosition2D, currentPosition); Vector3D changeVector = new Vector3D(change.X, change.Y, 0); _translate.OffsetX += changeVector.X * .104; _translate.OffsetY -= changeVector.Y * .104; _translate.OffsetZ += changeVector.Z * .104; _previousPosition3D = currentPosition3D; } private Vector3D ProjectToTrackball(double width, double height, Point point) { double x = point.X / (width / 2); // Scale so bounds map to [0,0] - [2,2] double 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 double z2 = 1 - x * x - y * y; // z^2 = 1 - x^2 - y^2 double z = z2 > 0 ? Math.Sqrt(z2) : 0; return new Vector3D(x, y, z); } private void Zoom(Point currentPosition) { double yDelta = currentPosition.Y - _previousPosition2D.Y; double scale = Math.Exp(yDelta / 100); // e^(yDelta/100) is fairly arbitrary. _scale.ScaleX *= scale; _scale.ScaleY *= scale; _scale.ScaleZ *= scale; } //Point focusPoint = new Point(); private void Focus(Point currentPosition) { //about zoom: //set the camera to lookat the position of current position //then, calculate the subcontract of the previous position and current pos that is the distance of zoomin/zoomout //then, set teh camera's position which can zoom in/out encircle object(position of mouse clicked) double yDelta = currentPosition.Y - _previousPosition2D.Y; double scale = yDelta / 100; double distance = Point3D.Subtract(_cameraLookPoint, _camera.Position).Length; if (distance >= 1024 || distance <= 0.01) return; _translate.OffsetX -= scale * _cameraLookDirection.X; _translate.OffsetY -= scale * _cameraLookDirection.Y; _translate.OffsetZ -= scale * _cameraLookDirection.Z; //about rotation double xDelta = currentPosition.X - _previousPosition2D.X; if (xDelta > 360 || xDelta < -360) return; if (xDelta + _rotation.Angle >= 360) _rotation.Angle = 0; _rotation.Axis = new Vector3D(0, 0, 1); _rotation.Angle += xDelta; } } }
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |

