TrackballCamera

From Unify Community Wiki
(Difference between revisions)
Jump to: navigation, search
m (update for unity 5)
(Improved code formatting.)
 
Line 1: Line 1:
Author: Simon Waite [[User:Opless]]
+
A non-gimbal locking camera. Created by Simon Waite ([[User:Opless]]).
== Description ==
+
  
A non-gimbal locking camera.
+
=== TrackballCamera.cs ===
 
+
== Code ==
+
 
<syntaxhighlight lang="csharp">
 
<syntaxhighlight lang="csharp">
 
using UnityEngine;
 
using UnityEngine;
using System.Collections;
 
  
 
public class TrackballCamera : MonoBehaviour
 
public class TrackballCamera : MonoBehaviour
 
{
 
{
+
    public float distance = 15f;
public float distance = 15f;
+
    public float virtualTrackballDistance = 0.25f;
+
    public GameObject target;
public float virtualTrackballDistance = 0.25f; // distance of the virtual trackball.
+
    private Vector3? lastMousePosition;
  
public GameObject target;
+
 
private Vector3? lastMousePosition;
+
    private void Start()
// Use this for initialization
+
    {
void Start ()
+
        var startPos = (this.transform.position - target.transform.position).normalized * distance;
{
+
        var position = startPos + target.transform.position;
var startPosn= (this.transform.position - target.transform.position).normalized *distance;
+
        transform.position = position;
var position = startPosn + target.transform.position;
+
        transform.LookAt(target.transform.position);
transform.position = position;
+
    }
transform.LookAt (target.transform.position);
+
 
}
+
 
+
    private void LateUpdate()
// Update is called once per frame
+
    {
void LateUpdate ()
+
        var mousePos = Input.mousePosition;
{
+
 
var mousePosn = Input.mousePosition;
+
        var mouseBtn = Input.GetMouseButton(0);
+
        if (mouseBtn)
var mouseBtn = Input.GetMouseButton (0);
+
        {
if (mouseBtn) {
+
            if (lastMousePosition.HasValue)
if (lastMousePosition.HasValue) {
+
            {
// we are moving from here
+
                // We are moving from here.
var lastPosn = this.transform.position;
+
                var lastPos = this.transform.position;
var targetPosn = target.transform.position;
+
                var targetPos = target.transform.position;
+
 
// we have traced out this distance on a sphere from lastPosn
+
                // we have traced out this distance on a sphere from lastPos
/*
+
                /*
 
var rotation = TrackBall(
 
var rotation = TrackBall(
lastMousePosition.Value.x,  
+
lastMousePosition.Value.x,
 
lastMousePosition.Value.y,
 
lastMousePosition.Value.y,
mousePosn.x,
+
mousePos.x,
mousePosn.y );
+
mousePos.y );
 
*/
 
*/
var rotation = FigureOutAxisAngleRotation(lastMousePosition.Value, mousePosn);
+
                var rotation = FigureOutAxisAngleRotation(lastMousePosition.Value, mousePos);
+
var vecPos = (targetPosn - lastPosn).normalized * -distance;
+
  
this.transform.position = rotation * vecPos + targetPosn;
+
                var vecPos = (targetPos - lastPos).normalized * -distance;
this.transform.LookAt(targetPosn);
+
+
lastMousePosition = mousePosn;
+
} else {
+
lastMousePosition = mousePosn;
+
}
+
} else {
+
lastMousePosition = null;
+
}
+
+
}
+
+
+
  
Quaternion FigureOutAxisAngleRotation (Vector3 lastMousePosn, Vector3 mousePosn)
+
                this.transform.position = rotation * vecPos + targetPos;
{
+
                this.transform.LookAt(targetPos);
if(lastMousePosn.x == mousePosn.x && lastMousePosn.y == mousePosn.y)
+
return Quaternion.identity;
+
+
Vector3 near = new Vector3(0,0,Camera.main.nearClipPlane);
+
+
Vector3 p1 = Camera.main.ScreenToWorldPoint( lastMousePosn + near );
+
Vector3 p2 = Camera.main.ScreenToWorldPoint( mousePosn + near);
+
+
//WriteLine("## {0} {1}", p1,p2);
+
var axisOfRotation = Vector3.Cross(p2,p1);
+
+
var twist = (p2-p1).magnitude / (2.0f * virtualTrackballDistance);
+
+
if(twist > 1.0f)
+
twist = 1.0f;
+
if(twist < -1.0f)
+
twist = -1.0f;
+
+
var phi = (2.0f * Mathf.Asin(twist)) * 180/Mathf.PI ;
+
+
//WriteLine("AA: {0} angle: {1}",axisOfRotation, phi);
+
+
return Quaternion.AngleAxis(phi, axisOfRotation);
+
}
+
+
+
Quaternion TrackBall(float p1x, float p1y, float p2x, float p2y, float radius)
+
{
+
// if there has been no drag, then return "no rotation"
+
if(p1x == p2x && p1y == p2y)
+
{
+
return Quaternion.identity;
+
}
+
var p1 = ProjectToSphere( radius, p1x,p1y );
+
var p2 = ProjectToSphere( radius, p2x,p2y );
+
+
var a = Vector3.Cross(p2,p1); // axis of rotation
+
// how much to rotate around above axis
+
var d = p1 - p2;
+
var t = d.magnitude / (2.0f * radius);
+
// clamp values to stop things going out of control.
+
if(t > 1.0f) t = 1.0f;
+
if(t < -1.0f) t = -1.0f;
+
var phi = 2.0f * Mathf.Asin(t);
+
phi = phi * 180/Mathf.PI; // to degrees
+
+
return Quaternion.AngleAxis(phi, a);
+
}
+
+
/// <summary>
+
/// Projects an x,y pair onto a sphere of radius distance.
+
/// OR onto a hyperbolic sheet if we are away from the center
+
/// of the sphere
+
/// </summary>
+
/// <returns>
+
/// The point on the sphere.
+
/// </returns>
+
/// <param name='distance'>
+
/// Distance.
+
/// </param>
+
/// <param name='x'>
+
/// X.
+
/// </param>
+
/// <param name='y'>
+
/// Y.
+
/// </param>
+
Vector3 ProjectToSphere(float distance, float x, float y)
+
{
+
float z;
+
float  d = Mathf.Sqrt(x*x + y*y);
+
if(d < distance * 0.707f)
+
{
+
// inside sphere
+
z= Mathf.Sqrt(distance*distance - d*d);
+
}
+
else
+
{
+
// on hyperbola
+
var t = distance / 1.4142f;
+
z = t*t /d;
+
}
+
+
return new Vector3(x,y,z);
+
}
+
}
+
  
 +
                lastMousePosition = mousePos;
 +
            }
 +
            else
 +
            {
 +
                lastMousePosition = mousePos;
 +
            }
 +
        }
 +
        else
 +
        {
 +
            lastMousePosition = null;
 +
        }
 +
    }
  
 +
 +
    private Quaternion FigureOutAxisAngleRotation(Vector3 lastMousePos, Vector3 mousePos)
 +
    {
 +
        if (lastMousePos.x == mousePos.x && lastMousePos.y == mousePos.y)
 +
        {
 +
            return Quaternion.identity;
 +
        }
 +
 +
        var near = new Vector3(0, 0, Camera.main.nearClipPlane);
 +
        Vector3 p1 = Camera.main.ScreenToWorldPoint(lastMousePos + near);
 +
        Vector3 p2 = Camera.main.ScreenToWorldPoint(mousePos + near);
 +
 +
        //WriteLine("## {0} {1}", p1,p2);
 +
        var axisOfRotation = Vector3.Cross(p2, p1);
 +
        var twist = (p2 - p1).magnitude / (2.0f * virtualTrackballDistance);
 +
 +
        if (twist > 1.0f)
 +
        {
 +
            twist = 1.0f;
 +
        }
 +
 +
        if (twist < -1.0f)
 +
        {
 +
            twist = -1.0f;
 +
        }
 +
 +
        var phi = (2.0f * Mathf.Asin(twist)) * 180 / Mathf.PI;
 +
        //WriteLine("AA: {0} angle: {1}",axisOfRotation, phi);
 +
 +
        return Quaternion.AngleAxis(phi, axisOfRotation);
 +
    }
 +
 +
 +
    private Quaternion TrackBall(float p1x, float p1y, float p2x, float p2y, float radius)
 +
    {
 +
        // if there has been no drag, then return "no rotation"
 +
        if (p1x == p2x && p1y == p2y)
 +
        {
 +
            return Quaternion.identity;
 +
        }
 +
 +
        var p1 = ProjectToSphere(radius, p1x, p1y);
 +
        var p2 = ProjectToSphere(radius, p2x, p2y);
 +
        var a = Vector3.Cross(p2, p1); // axis of rotation
 +
                                      // how much to rotate around above axis
 +
        var d = p1 - p2;
 +
        var t = d.magnitude / (2.0f * radius);
 +
 +
        // clamp values to stop things going out of control.
 +
        if (t > 1.0f)
 +
        {
 +
            t = 1.0f;
 +
        }
 +
 +
        if (t < -1.0f)
 +
        {
 +
            t = -1.0f;
 +
        }
 +
 +
        var phi = 2.0f * Mathf.Asin(t);
 +
        phi = phi * 180 / Mathf.PI; // to degrees
 +
 +
        return Quaternion.AngleAxis(phi, a);
 +
    }
 +
 +
 +
    /// <summary>
 +
    /// Projects an x,y pair onto a sphere of radius distance.
 +
    /// OR onto a hyperbolic sheet if we are away from the center
 +
    /// of the sphere
 +
    /// </summary>
 +
    /// <returns>The point on the sphere</returns>
 +
    /// <param name='distance'>Distance</param>
 +
    /// <param name='x'>X</param>
 +
    /// <param name='y'>Y</param>
 +
    private Vector3 ProjectToSphere(float distance, float x, float y)
 +
    {
 +
        float z;
 +
        float d = Mathf.Sqrt(x * x + y * y);
 +
        if (d < distance * 0.707f)
 +
        {
 +
            // inside sphere
 +
            z = Mathf.Sqrt(distance * distance - d * d);
 +
        }
 +
        else
 +
        {
 +
            // on hyperbola
 +
            var t = distance / 1.4142f;
 +
            z = t * t / d;
 +
        }
 +
 +
        return new Vector3(x, y, z);
 +
    }
 +
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
[[Category: CSharp]]
 +
[[Category: MonoBehaviour]]
 +
[[Category: Camera]]

Latest revision as of 06:27, 14 November 2018

A non-gimbal locking camera. Created by Simon Waite (User:Opless).

[edit] TrackballCamera.cs

using UnityEngine;
 
public class TrackballCamera : MonoBehaviour
{
    public float distance = 15f;
    public float virtualTrackballDistance = 0.25f;
    public GameObject target;
    private Vector3? lastMousePosition;
 
 
    private void Start()
    {
        var startPos = (this.transform.position - target.transform.position).normalized * distance;
        var position = startPos + target.transform.position;
        transform.position = position;
        transform.LookAt(target.transform.position);
    }
 
 
    private void LateUpdate()
    {
        var mousePos = Input.mousePosition;
 
        var mouseBtn = Input.GetMouseButton(0);
        if (mouseBtn)
        {
            if (lastMousePosition.HasValue)
            {
                // We are moving from here.
                var lastPos = this.transform.position;
                var targetPos = target.transform.position;
 
                // we have traced out this distance on a sphere from lastPos
                /*
				var rotation = TrackBall(
										lastMousePosition.Value.x,
										lastMousePosition.Value.y,
										mousePos.x,
										mousePos.y );
				*/
                var rotation = FigureOutAxisAngleRotation(lastMousePosition.Value, mousePos);
 
                var vecPos = (targetPos - lastPos).normalized * -distance;
 
                this.transform.position = rotation * vecPos + targetPos;
                this.transform.LookAt(targetPos);
 
                lastMousePosition = mousePos;
            }
            else
            {
                lastMousePosition = mousePos;
            }
        }
        else
        {
            lastMousePosition = null;
        }
    }
 
 
    private Quaternion FigureOutAxisAngleRotation(Vector3 lastMousePos, Vector3 mousePos)
    {
        if (lastMousePos.x == mousePos.x && lastMousePos.y == mousePos.y)
        {
            return Quaternion.identity;
        }
 
        var near = new Vector3(0, 0, Camera.main.nearClipPlane);
        Vector3 p1 = Camera.main.ScreenToWorldPoint(lastMousePos + near);
        Vector3 p2 = Camera.main.ScreenToWorldPoint(mousePos + near);
 
        //WriteLine("## {0} {1}", p1,p2);
        var axisOfRotation = Vector3.Cross(p2, p1);
        var twist = (p2 - p1).magnitude / (2.0f * virtualTrackballDistance);
 
        if (twist > 1.0f)
        {
            twist = 1.0f;
        }
 
        if (twist < -1.0f)
        {
            twist = -1.0f;
        }
 
        var phi = (2.0f * Mathf.Asin(twist)) * 180 / Mathf.PI;
        //WriteLine("AA: {0} angle: {1}",axisOfRotation, phi);
 
        return Quaternion.AngleAxis(phi, axisOfRotation);
    }
 
 
    private Quaternion TrackBall(float p1x, float p1y, float p2x, float p2y, float radius)
    {
        // if there has been no drag, then return "no rotation"
        if (p1x == p2x && p1y == p2y)
        {
            return Quaternion.identity;
        }
 
        var p1 = ProjectToSphere(radius, p1x, p1y);
        var p2 = ProjectToSphere(radius, p2x, p2y);
        var a = Vector3.Cross(p2, p1); // axis of rotation
                                       // how much to rotate around above axis
        var d = p1 - p2;
        var t = d.magnitude / (2.0f * radius);
 
        // clamp values to stop things going out of control.
        if (t > 1.0f)
        {
            t = 1.0f;
        }
 
        if (t < -1.0f)
        {
            t = -1.0f;
        }
 
        var phi = 2.0f * Mathf.Asin(t);
        phi = phi * 180 / Mathf.PI; // to degrees
 
        return Quaternion.AngleAxis(phi, a);
    }
 
 
    /// <summary>
    /// Projects an x,y pair onto a sphere of radius distance.
    /// OR onto a hyperbolic sheet if we are away from the center
    /// of the sphere
    /// </summary>
    /// <returns>The point on the sphere</returns>
    /// <param name='distance'>Distance</param>
    /// <param name='x'>X</param>
    /// <param name='y'>Y</param>
    private Vector3 ProjectToSphere(float distance, float x, float y)
    {
        float z;
        float d = Mathf.Sqrt(x * x + y * y);
        if (d < distance * 0.707f)
        {
            // inside sphere
            z = Mathf.Sqrt(distance * distance - d * d);
        }
        else
        {
            // on hyperbola
            var t = distance / 1.4142f;
            z = t * t / d;
        }
 
        return new Vector3(x, y, z);
    }
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox