//------------------------------------------------------------------
//
// 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;
using _3DTools;
using Xenki.Framework;
using System.Collections.Generic;
namespace Xenki.DefaultRenderer
{
public class CameraControl
{
private FrameworkElement _eventSource;
private Point _previousPosition2D;
private Vector3D _previousPosition3D = new Vector3D(0, 0, 1);
private PerspectiveCamera _camera;
private Point3D _cameraLookPoint;
ModelVisual3D visul3d;
Point3D _clickedCenterPosition;
bool isTracking = false;
bool isPanningObject = false;
System.Windows.Threading.DispatcherTimer frameTimer;
public CameraControl(PerspectiveCamera camera)
{
//dataControl = DefaultNetwork.DefaultNetwork.m_primitiveData;
_camera = camera;
frameTimer = new System.Windows.Threading.DispatcherTimer();
frameTimer.Tick += new EventHandler(frameTimer_Tick);
frameTimer.Interval = new TimeSpan(0, 0, 0, 1, 10);
frameTimer.Start();
}
void frameTimer_Tick(object sender, EventArgs e)
{
// FPSMouseMove();
}
[System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")]
[return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool SetCursorPos(int X, int Y);
#region Event Handling
///
/// The FrameworkElement we listen to for mouse events.
///
public FrameworkElement EventSource
{
get { return _eventSource; }
set
{
if (_eventSource != null)
{
_eventSource.MouseWheel -= _eventSource_MouseWheel;
_eventSource.MouseUp -= this.OnMouseUp;
_eventSource.MouseMove -= this.OnMouseMove;
_eventSource.MouseLeftButtonDown -= _eventSource_MouseLeftButtonDown;
_eventSource.MouseRightButtonDown -= _eventSource_MouseRightButtonDown;
_eventSource.KeyDown -= this._eventSource_KeyDown;
}
_eventSource = value;
_eventSource.MouseWheel += _eventSource_MouseWheel;
_eventSource.MouseLeftButtonDown += _eventSource_MouseLeftButtonDown;
_eventSource.MouseRightButtonDown += _eventSource_MouseRightButtonDown;
_eventSource.MouseUp += this.OnMouseUp;
_eventSource.MouseMove += this.OnMouseMove;
_eventSource.Focusable = true;
_eventSource.Focus();
Keyboard.Focus(_eventSource);
_eventSource.KeyDown += _eventSource_KeyDown;
}
}
void _eventSource_KeyDown(object sender, KeyEventArgs e)
{
if ((e.Key == Key.Left||e.Key == Key.A)&&Mouse.LeftButton == MouseButtonState.Released&&MouseButtonState.Released==Mouse.RightButton)
{
CameraLookLeftRight(-4.0);
e.Handled = true;
}
else if (e.Key == Key.Right||e.Key == Key.D)
{
CameraLookLeftRight(4.0);
e.Handled = true;
}
else if (e.Key == Key.Up || e.Key == Key.W)
{
//walk forward
CameraWalk(0.05);
e.Handled = true;
}
else if (e.Key == Key.Down || e.Key == Key.S)
{
//walk back
CameraWalk(-0.05);
e.Handled = true;
}
}
//camera walk should be setting according to the avatar's walking path
private void CameraWalk(double distanceTick)
{
//Vector3D cameraLookDirection = _camera.LookDirection;
//Vector3D vzAxis = new Vector3D(0, 0, 1);
//Vector3D walkDirection = Vector3D.Add(vzAxis, cameraLookDirection);
_camera.LookDirection = new Vector3D(_camera.LookDirection.X, _camera.LookDirection.Y, 0);
_camera.Position = Point3D.Add(_camera.Position, _camera.LookDirection * distanceTick);
}
private void CameraLookLeftRight(double angle)
{
Vector3D axis = new Vector3D(0, 0, 1);
Quaternion delta = new Quaternion(axis, -angle);
Matrix3D cameraMatrix3D = new Matrix3D();
cameraMatrix3D.RotateAt(delta, _camera.Position);
_camera.LookDirection = Vector3D.Multiply(_camera.LookDirection, cameraMatrix3D);// Point3D.Subtract(_cameraLookPoint, _camera.Position);
}
void _eventSource_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(_eventSource, CaptureMode.Element);
Point pclick = e.GetPosition(_eventSource);
originalMouseClickPos = _eventSource.PointToScreen(pclick);
HitTestResult hitresult = VisualTreeHelper.HitTest(EventSource, pclick);
RayMeshGeometry3DHitTestResult result3d = hitresult as RayMeshGeometry3DHitTestResult;
if (result3d == null)
return;
visul3d = result3d.VisualHit as ModelVisual3D;
if (visul3d == null)
return;
//for ALT + MOUSE + ROTATE
VisualTreeHelper.HitTest(_eventSource, null, HitTestCallback, new PointHitTestParameters(pclick));
_camera.LookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position);
isTracking = true;
_eventSource.CaptureMouse();
_eventSource.Focus();
e.Handled = true;
}
Point originalMouseClickPos = new Point();
void _eventSource_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(EventSource, CaptureMode.Element);
Point pclick = e.GetPosition(EventSource);
originalMouseClickPos = _eventSource.PointToScreen(pclick);
HitTestResult hitresult = VisualTreeHelper.HitTest(EventSource, pclick);
RayMeshGeometry3DHitTestResult result3d = hitresult as RayMeshGeometry3DHitTestResult;
if (result3d == null)
return;
visul3d = result3d.VisualHit as ModelVisual3D;
if (visul3d == null)
return;
//for ALT + MOUSE + ROTATE
VisualTreeHelper.HitTest(_eventSource, null, HitTestCallback, new PointHitTestParameters(pclick));
_camera.LookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position);
_clickedCenterPosition = GetObjectCenterPosition(visul3d);
//uint? key = DefaultNetwork.DefaultNetwork.m_primitiveData.GetKeyInSceneByValue(visul3d);
//if (key.HasValue)
//{
// OpenMetaverse.Primitive dataaaa =DefaultNetwork.DefaultNetwork.m_primitiveData.PrimModelsInScene[key.Value];
// uint id = dataaaa.ParentID;
// OpenMetaverse.Vector3 parentpos = DefaultNetwork.DefaultNetwork.m_primitiveData.GetModelsParentPosition(id);
// OpenMetaverse.Vector3 mypos = dataaaa.Position;
//}
//TRACK BALL
_previousPosition3D = ProjectToTrackball(
EventSource.ActualWidth,
EventSource.ActualHeight,
_previousPosition2D);
isTracking = true;
_eventSource.CaptureMouse();
_eventSource.Focus();
e.Handled = true;
}
private Point3D GetObjectCenterPosition(ModelVisual3D visual)
{
return Point3D.Add(visual.Content.Bounds.Location,
new Vector3D(visual.Content.Bounds.SizeX / 2, visual.Content.Bounds.SizeY / 2, visual.Content.Bounds.SizeZ / 2));
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
//set the mouse to the original position when clicked the object.
if (_eventSource.Cursor != Cursors.Arrow)
{
SetCursorPos((int)originalMouseClickPos.X, (int)originalMouseClickPos.Y);
_eventSource.Cursor = Cursors.Arrow;
}
Mouse.Capture(EventSource, CaptureMode.None);
isTracking = false;
if (isPanningObject == true)
{
isPanningObject = false;
_cameraLookPoint = GetObjectCenterPosition(visul3d);
}
_eventSource.ReleaseMouseCapture();
}
Point currentPosition;
private void OnMouseMove(object sender, MouseEventArgs e)
{
currentPosition = e.GetPosition(EventSource);
if (e.RightButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
{
LookUpDown(currentPosition);
}
else if (e.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
Focus(currentPosition);
}
}
else if (e.RightButton == MouseButtonState.Pressed && Keyboard.IsKeyDown(Key.A))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
CameraPanLeftRight(currentPosition);
}
}
else if (e.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
&& (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
RotateObject(currentPosition);
}
}
else if (e.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
isPanningObject = true;
PanObject(currentPosition);
}
}
else if (e.LeftButton == MouseButtonState.Pressed)
{
if(isTracking)
LookAround(currentPosition);
}
else if (e.RightButton == MouseButtonState.Pressed)
{
if (isTracking)
CameraPanUP(currentPosition);
}
_previousPosition2D = currentPosition;
}
private void LookAround(Point currentPosition)
{
if (_cameraLookPoint == new Point3D(0, 0, 0))
return;
Vector3D currentPosition3D = ProjectToTrackball(EventSource.ActualWidth, EventSource.ActualHeight, currentPosition);
if (_previousPosition3D == currentPosition3D)
return;
Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D);
angle *= 2;
// We negate the angle because we are rotating the camera.
// Do not do this if you are rotating the scene instead.
Quaternion delta = new Quaternion(axis, -angle);
Matrix3D cameraMatrix3D = new Matrix3D();
cameraMatrix3D.RotateAt(delta,_cameraLookPoint);
_camera.Position = Point3D.Multiply(_camera.Position, cameraMatrix3D);
_camera.LookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position);
_previousPosition3D = currentPosition3D;
}
void _eventSource_MouseWheel(object sender, MouseWheelEventArgs e)
{
double scale = -e.Delta / 800.000;
if(_cameraLookPoint!=null&&_cameraLookPoint!=new Point3D(0,0,0))
_camera.LookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position);
int count = Math.Abs((int)(scale / zoomFactor));
for (int i = 1; i <= count; i++)
{
if (scale > 0)
AdjustCameraPosition(_camera, _camera.LookDirection, zoomFactor);
else
AdjustCameraPosition(_camera, _camera.LookDirection, -zoomFactor);
}
}
private void RotateObject(Point currentPosition)
{
List children = DefaultNetwork.DefaultNetwork.m_primitiveData.GetChildrenByVisual3D(visul3d);
Vector3D currentPosition3D = ProjectToTrackball(_eventSource.ActualWidth, _eventSource.ActualHeight, currentPosition);
Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
double angle = Vector3D.AngleBetween(currentPosition3D,_previousPosition3D );
angle *= 3.00000;
Matrix3D mat = new Matrix3D();
mat.RotateAt(new Quaternion(axis, angle), _clickedCenterPosition);
RotateObject(mat,visul3d);
foreach (Visual3D v in children)
{
RotateObject(mat, v);
}
_previousPosition3D = currentPosition3D;
}
private Matrix3D RotateObject(Matrix3D mat,Visual3D visual)
{
GeometryModel3D geometryModel3D = (visual as ModelVisual3D).Content as GeometryModel3D;
if (geometryModel3D != null)
{
MeshGeometry3D meshGeometry3D = geometryModel3D.Geometry as MeshGeometry3D;
if (meshGeometry3D != null)
{
Point3DCollection points = meshGeometry3D.Positions;
for (int i = 0; i < points.Count; i++)
points[i] = Point3D.Multiply(points[i], mat);
}
}
return mat;
}
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;
Vector3D v = new Vector3D(x, z, y);
v.Normalize();
return v;
}
#endregion Event Handling
private void CameraPanLeftRight(Point currentPosition)
{
double scalex = currentPosition.X - _previousPosition2D.X;
Vector3D vector3D = GetTranslationVector3D(visul3d, _previousPosition2D, currentPosition);
_camera.Position += Math.Abs(scalex) * vector3D;
}
private void PanObject(Point currentPosition)
{
List children = DefaultNetwork.DefaultNetwork.m_primitiveData.GetChildrenByVisual3D(visul3d);
Vector3D vector3D = GetTranslationVector3D(visul3d, _previousPosition2D, currentPosition);
MoveObject(visul3d, vector3D);
foreach (Visual3D v in children)
{
MoveObject(v, vector3D);
}
}
private void MoveObject(Visual3D visual, Vector3D vector3D)
{
GeometryModel3D geometryModel3D = (visual as ModelVisual3D).Content as GeometryModel3D;
if (geometryModel3D != null)
{
MeshGeometry3D meshGeometry3D = geometryModel3D.Geometry as MeshGeometry3D;
if (meshGeometry3D != null)
{
Point3DCollection points = meshGeometry3D.Positions;
for (int i = 0; i < points.Count; i++)
points[i] = Point3D.Add(points[i], vector3D * 50);
}
}
}
string mm = "";
private Vector3D GetTranslationVector3D(DependencyObject modelHit, Point startPosition, Point endPosition)
{
Vector3D translationVector3D = new Vector3D();
Viewport3DVisual viewport = null;
bool success = false;
Matrix3D matrix3D = MathUtils.TryTransformTo2DAncestor(modelHit, out viewport, out success);
if (success && matrix3D.HasInverse)
{
matrix3D.Invert();
Point3D startPoint3D = new Point3D(startPosition.X, startPosition.Y, 0);
Point3D endPoint3D = new Point3D(endPosition.X, endPosition.Y, 0);
Vector3D vector3D = endPoint3D - startPoint3D;
translationVector3D = matrix3D.Transform(vector3D);
}
return translationVector3D;
}
private void LookUpDown(Point currentPosition)
{
double scale = currentPosition.Y- _previousPosition2D.Y;
// scale = scale;
_camera.LookDirection = new Vector3D(_camera.LookDirection.X, _camera.LookDirection.Y, _camera.LookDirection.Z + scale);
}
double zoomFactor = 0.0500;
private void Focus(Point currentPosition)
{
//zoom
double yDelta = currentPosition.Y - _previousPosition2D.Y;
double scale = yDelta/20 ;
int count = Math.Abs( (int)(scale / zoomFactor));
for (int i = 1; i <= count; i++)
{
if (yDelta > 0)
AdjustCameraPosition(_camera, _camera.LookDirection, zoomFactor);
else
AdjustCameraPosition(_camera, _camera.LookDirection, -zoomFactor);
}
//rotate the camera
double axisAngle = _previousPosition2D.X - currentPosition.X;
Matrix3D matrixRoateCamera = new Matrix3D();
matrixRoateCamera.RotateAt(new Quaternion(new Vector3D(0, 0, 1), axisAngle), _cameraLookPoint);
_camera.Position = Point3D.Multiply(_camera.Position, matrixRoateCamera);
_camera.LookDirection = Point3D.Subtract(_cameraLookPoint, _camera.Position);
}
private void AdjustCameraPosition(PerspectiveCamera camera,Vector3D lookDirection, double factor )
{
Point3D cameraPos = new Point3D(camera.Position.X - factor * lookDirection.X,
camera.Position.Y - factor * lookDirection.Y,
camera.Position.Z - factor * lookDirection.Z);
//too fast
//if (Point3D.Subtract(cameraPos, camera.Position).Length > 0.5000000000)
// cameraPos = new Point3D(camera.Position.X - factor / 10 * lookDirection.X,
// camera.Position.Y - factor / 10 * lookDirection.Y,
// camera.Position.Z - factor / 10 * lookDirection.Z);
double distance = Point3D.Subtract(_cameraLookPoint, cameraPos).Length;
if (distance > 512.0000 || distance <= 0.5000000)
{
}
else
camera.Position = cameraPos;
}
//get the clicked 3d position
public HitTestResultBehavior HitTestCallback(HitTestResult result)
{
if (result.VisualHit != null)
{
RayHitTestResult r = result as RayHitTestResult;
if (r != null)
{
RayMeshGeometry3DHitTestResult t = r as RayMeshGeometry3DHitTestResult;
_cameraLookPoint = t.PointHit;
return HitTestResultBehavior.Stop;
}
}
return HitTestResultBehavior.Continue;
}
private void CameraPanUP(Point currentPosition)
{
double yDelta = currentPosition.Y - _previousPosition2D.Y;
double xDelta = currentPosition.X - _previousPosition2D.X;
double scale = yDelta / 5.00;
_camera.Position = new Point3D(_camera.Position.X, _camera.Position.Y, _camera.Position.Z + scale);
}
//for test
/*
private void FPSMouseMove()
{
Point curr = Mouse.GetPosition(_eventSource);
currentPosition = curr;
if (isTracking)
{
if (Mouse.RightButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
{
LookUpDown(currentPosition);
}
else if (Mouse.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
Focus(currentPosition);
}
}
else if (Mouse.RightButton == MouseButtonState.Pressed && Keyboard.IsKeyDown(Key.Q))
{
if (isTracking)
CameraPanUP(currentPosition);
}
else if (Mouse.RightButton == MouseButtonState.Pressed && Keyboard.IsKeyDown(Key.A))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
CameraPanLeftRight(currentPosition);
}
}
else if (Mouse.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
&& (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
RotateObject(currentPosition);
}
}
else if (Mouse.LeftButton == MouseButtonState.Pressed && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
{
if (isTracking)
{
_eventSource.Cursor = Cursors.None;
isPanningObject = true;
PanObject(currentPosition);
}
}
else if (Mouse.LeftButton == MouseButtonState.Pressed)
{
LookAround(currentPosition);
}
}
_previousPosition2D = currentPosition;
}
*/
}
}