Roaming camera implementation

Topics: Developer Forum
Feb 16, 2008 at 10:02 PM
Edited Feb 16, 2008 at 10:32 PM
The following is based on the TrackballDecorator, and is a drop-in replacement for it. Create a new class in the 3DTools project, paste in this code and compile. Replace your project's <TrackballDecorator> xaml tag(s) w/ <Roam3DDecorator> and run.

Rather than the camera be on a trackball that orbits about the origin, the camera will be free-roaming.

Hold the left mouse button to spin, the right mouse button to move forward and back, and hold the left+right mouse buttons to slide left/right/up/down.

All movements are relative to the camera's current location, so the camera moves around the 3D world, rather than just orbiting about the origin.

NOTE: The code assumes the use of a PerspectiveCamera.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using System.Windows.Media.Media3D;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace _3DTools
{
public class Roam3DDecorator : Viewport3DDecorator
{
#region Fields

private Point _previousPosition2D;
private Border _eventSource;

#endregion

public Roam3DDecorator()
{
_eventSource = new Border();
_eventSource.Background = Brushes.Transparent;

PreViewportChildren.Add(_eventSource);
}

#region Event Handling

protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);

_previousPosition2D = e.GetPosition(this);

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 == _previousPosition2D) return;

if (e.RightButton == MouseButtonState.Pressed
&& e.LeftButton == MouseButtonState.Pressed)
{
SlideCamera(currentPosition);
}
else if (e.LeftButton == MouseButtonState.Pressed)
{
RotateCamera(currentPosition);
}
else if (e.RightButton == MouseButtonState.Pressed)
{
ZoomCamera(currentPosition);
}

_previousPosition2D = currentPosition;
}
}

#endregion Event Handling

#region Camera movement

private void RotateCamera(Point currentPosition)
{
PerspectiveCamera camera = (PerspectiveCamera)FindCamera();

Vector3D camZ = camera.LookDirection;
camZ.Normalize();
Vector3D camX = -Vector3D.CrossProduct(camZ, camera.UpDirection);
camX.Normalize();
Vector3D camY = Vector3D.CrossProduct(camZ, camX);
camY.Normalize();

double dX = currentPosition.X - _previousPosition2D.X;
double dY = currentPosition.Y - _previousPosition2D.Y;

dX /= -5;
dY /= 5;

AxisAngleRotation3D aarY = new AxisAngleRotation3D(
camY, dX);
AxisAngleRotation3D aarX = new AxisAngleRotation3D(
camX, dY);

RotateTransform3D rotY = new RotateTransform3D(aarY);
RotateTransform3D rotX = new RotateTransform3D(aarX);

camZ = camZ * rotY.Value * rotX.Value;
camZ.Normalize();
camY = camY * rotX.Value * rotY.Value;
camY.Normalize();

camera.LookDirection = camZ;
camera.UpDirection = camY;
}

private void ZoomCamera(Point currentPosition)
{
double yDelta = currentPosition.Y - _previousPosition2D.Y;

yDelta /= 3;

PerspectiveCamera camera = (PerspectiveCamera)FindCamera();

Point3D position = camera.Position;

position -= camera.LookDirection * yDelta;

camera.Position = position;
}

private void SlideCamera(Point currentPosition)
{
PerspectiveCamera camera = (PerspectiveCamera)FindCamera();

Vector3D camZ = camera.LookDirection;
camZ.Normalize();
Vector3D camX = -Vector3D.CrossProduct(camZ, camera.UpDirection);
camX.Normalize();
Vector3D camY = Vector3D.CrossProduct(camZ, camX);
camY.Normalize();

double dX = currentPosition.X - _previousPosition2D.X;
double dY = currentPosition.Y - _previousPosition2D.Y;

dX /= -30;
dY /= -30;

Vector3D vSlide = (camX * dX) + (camY * dY);

Point3D cameraPos = camera.Position;
cameraPos += vSlide;
camera.Position = cameraPos;
}

private Camera FindCamera()
{
Viewport3D viewport3D = this.Viewport3D;
if (viewport3D != null)
{
if (viewport3D.Camera != null)
{
if (viewport3D.Camera.IsFrozen)
{
viewport3D.Camera = viewport3D.Camera.Clone();
}
return viewport3D.Camera;
}
}
return null;
}

#endregion
}
}
Mar 3, 2008 at 10:11 PM
Great work! Thanks for sharing!!
Apr 30, 2008 at 5:06 PM
Fantastic! Thanks so much, I was just in need of camera panning ability and this works great.
Sep 26, 2008 at 3:48 PM
This seams exactly what I need!
My version of Visual Studio won't allow me to compile this...
Can anyone PLEASE upload this as a dll somewhere?!
PLEASE?!

Thanks in advance to anyone who can help me out!
Sep 29, 2008 at 4:05 PM
I got it, thanks anyway!
Feb 11, 2009 at 5:41 PM
I shamelessly copied karymcfadden's code to make something that works with an orthographic camera. Just put it in the same _3DTools namespace. The xaml tag is <Roam3DDecoratorOrtho>.

public class Roam3DDecoratorOrtho : Viewport3DDecorator
    {
        #region Fields

        private Point _previousPosition2D;
        private Border _eventSource;

        #endregion

        public Roam3DDecoratorOrtho()
        {
            _eventSource = new Border();
            _eventSource.Background = Brushes.Transparent;

            PreViewportChildren.Add(_eventSource);
        }

        #region Event Handling

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);

            _previousPosition2D = e.GetPosition(this);

            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 == _previousPosition2D) return;

                if (e.RightButton == MouseButtonState.Pressed
                && e.LeftButton == MouseButtonState.Pressed)
                {
                    SlideCamera(currentPosition);
                }
                else if (e.LeftButton == MouseButtonState.Pressed)
                {
                    RotateCamera(currentPosition);
                }
                else if (e.RightButton == MouseButtonState.Pressed)
                {
                    ZoomCamera(currentPosition);
                }

                _previousPosition2D = currentPosition;
            }
        }

        #endregion Event Handling

        #region Camera movement

        private void RotateCamera(Point currentPosition)
        {
            OrthographicCamera camera = (OrthographicCamera)FindCamera();

            Vector3D camZ = camera.LookDirection;
            camZ.Normalize();
            Vector3D camX = -Vector3D.CrossProduct(camZ, camera.UpDirection);
            camX.Normalize();
            Vector3D camY = Vector3D.CrossProduct(camZ, camX);
            camY.Normalize();

            double dX = currentPosition.X - _previousPosition2D.X;
            double dY = currentPosition.Y - _previousPosition2D.Y;

            dX /= -5;
            dY /= 5;

            AxisAngleRotation3D aarY = new AxisAngleRotation3D(
            camY, dX);
            AxisAngleRotation3D aarX = new AxisAngleRotation3D(
            camX, dY);

            RotateTransform3D rotY = new RotateTransform3D(aarY);
            RotateTransform3D rotX = new RotateTransform3D(aarX);

            camZ = camZ * rotY.Value * rotX.Value;
            camZ.Normalize();
            camY = camY * rotX.Value * rotY.Value;
            camY.Normalize();

            camera.LookDirection = camZ;
            camera.UpDirection = camY;
        }

        private void ZoomCamera(Point currentPosition)
        {
            double yDelta = currentPosition.Y - _previousPosition2D.Y;

            yDelta /= 10;

            OrthographicCamera camera = (OrthographicCamera)FindCamera();

            double width = camera.Width;

            width += camera.Width * yDelta;

            camera.Width = width;
        }

        private void SlideCamera(Point currentPosition)
        {
            OrthographicCamera camera = (OrthographicCamera)FindCamera();

            Vector3D camZ = camera.LookDirection;
            camZ.Normalize();
            Vector3D camX = -Vector3D.CrossProduct(camZ, camera.UpDirection);
            camX.Normalize();
            Vector3D camY = Vector3D.CrossProduct(camZ, camX);
            camY.Normalize();

            double dX = currentPosition.X - _previousPosition2D.X;
            double dY = currentPosition.Y - _previousPosition2D.Y;

            dX /= -30;
            dY /= -30;

            Vector3D vSlide = (camX * dX) + (camY * dY);

            Point3D cameraPos = camera.Position;
            cameraPos += vSlide;
            camera.Position = cameraPos;
        }

        private Camera FindCamera()
        {
            Viewport3D viewport3D = this.Viewport3D;
            if (viewport3D != null)
            {
                if (viewport3D.Camera != null)
                {
                    if (viewport3D.Camera.IsFrozen)
                    {
                        viewport3D.Camera = viewport3D.Camera.Clone();
                    }
                    return viewport3D.Camera;
                }
            }
            return null;
        }

        #endregion
    }
Jan 30, 2010 at 3:37 PM
it is buggy, i use charles petzold's wirepolyline and the line is seperated ??? which should be 1 single line