//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
using bs;
namespace bs.Editor
{
/** @addtogroup Handles
* @{
*/
///
/// Handle that allows an object to be rotated around the three primary axes, as well as a free axis currently
/// facing the camera.
///
public sealed class RotateHandle : DefaultHandle
{
private Quaternion delta;
private HandleSliderDisc xAxis;
private HandleSliderDisc yAxis;
private HandleSliderDisc zAxis;
private HandleSliderDisc freeAxis;
private bool isDragged;
private Quaternion dragStartRotation;
///
/// Amount of rotation applied since the last frame. Only valid while the handle is being dragged.
///
public Quaternion Delta
{
get { return delta; }
}
///
internal override bool IsDragged()
{
return xAxis.State == HandleSlider.StateType.Active ||
yAxis.State == HandleSlider.StateType.Active ||
zAxis.State == HandleSlider.StateType.Active ||
freeAxis.State == HandleSlider.StateType.Active;
}
///
/// Creates a new rotation handle.
///
public RotateHandle()
{
xAxis = new HandleSliderDisc(this, Vector3.XAxis, 1.0f);
yAxis = new HandleSliderDisc(this, Vector3.YAxis, 1.0f);
zAxis = new HandleSliderDisc(this, Vector3.ZAxis, 1.0f);
freeAxis = new HandleSliderDisc(this, -Vector3.ZAxis, 1.2f);
}
///
protected internal override void PreInput()
{
xAxis.Position = position;
yAxis.Position = position;
zAxis.Position = position;
freeAxis.Position = position;
Quaternion handleRotation = isDragged ? dragStartRotation : Rotation;
xAxis.Rotation = handleRotation;
yAxis.Rotation = handleRotation;
zAxis.Rotation = handleRotation;
freeAxis.Rotation = EditorApplication.SceneViewCamera.SceneObject.Rotation;
xAxis.SetCutoffPlane(GetXStartAngle(isDragged), true);
yAxis.SetCutoffPlane(GetYStartAngle(isDragged), true);
zAxis.SetCutoffPlane(GetZStartAngle(isDragged), true);
}
///
protected internal override void PostInput()
{
if (IsDragged())
{
if (!isDragged)
{
isDragged = true;
dragStartRotation = Rotation;
}
}
else
{
isDragged = false;
dragStartRotation = Quaternion.Identity;
}
Degree xValue = (Degree)0.0f;
Degree yValue = (Degree)0.0f;
Degree zValue = (Degree)0.0f;
Degree freeAxisValue = (Degree)0.0f;
if (Handles.RotateHandleSnapActive)
{
xValue = Handles.SnapValue(xAxis.Delta, Handles.RotateSnapAmount);
yValue = Handles.SnapValue(yAxis.Delta, Handles.RotateSnapAmount);
zValue = Handles.SnapValue(zAxis.Delta, Handles.RotateSnapAmount);
freeAxisValue = Handles.SnapValue(freeAxis.Delta, Handles.RotateSnapAmount);
}
else
{
xValue = xAxis.Delta;
yValue = yAxis.Delta;
zValue = zAxis.Delta;
freeAxisValue = freeAxis.Delta;
}
Vector3 cameraForward = -(dragStartRotation.Inverse * EditorApplication.SceneViewCamera.SceneObject.Rotation).Forward;
delta = Quaternion.FromEuler(xValue, yValue, zValue);
delta *= Quaternion.FromAxisAngle(cameraForward, freeAxisValue);
}
///
protected internal override void Draw()
{
HandleDrawing.Layer = 1;
HandleDrawing.Transform = Matrix4.TRS(Position, Rotation, Vector3.One);
float handleSize = Handles.GetHandleSize(EditorApplication.SceneViewCamera, position);
// Draw arcs
if (xAxis.State == HandleSlider.StateType.Active)
HandleDrawing.Color = Color.White;
else if(xAxis.State == HandleSlider.StateType.Hover)
HandleDrawing.Color = Color.BansheeOrange;
else
HandleDrawing.Color = Color.Red;
HandleDrawing.DrawWireArc(Vector3.Zero, Vector3.XAxis, 1.0f, GetXStartAngle(false), new Degree(180.0f), handleSize);
if (yAxis.State == HandleSlider.StateType.Active)
HandleDrawing.Color = Color.White;
else if (yAxis.State == HandleSlider.StateType.Hover)
HandleDrawing.Color = Color.BansheeOrange;
else
HandleDrawing.Color = Color.Green;
HandleDrawing.DrawWireArc(Vector3.Zero, Vector3.YAxis, 1.0f, GetYStartAngle(false), new Degree(180.0f), handleSize);
if (zAxis.State == HandleSlider.StateType.Active)
HandleDrawing.Color = Color.White;
else if (zAxis.State == HandleSlider.StateType.Hover)
HandleDrawing.Color = Color.BansheeOrange;
else
HandleDrawing.Color = Color.Blue;
HandleDrawing.DrawWireArc(Vector3.Zero, Vector3.ZAxis, 1.0f, GetZStartAngle(false), new Degree(180.0f), handleSize);
// Draw "bounds" and free handle
Color gray = new Color(1.0f, 1.0f, 1.0f, 0.3f);
Vector3 cameraNormal = EditorApplication.SceneViewCamera.SceneObject.Rotation.Rotate(Vector3.ZAxis);
HandleDrawing.Transform = Matrix4.TRS(Position, Quaternion.Identity, Vector3.One);
HandleDrawing.Color = gray;
HandleDrawing.DrawWireDisc(cameraNormal * 0.1f, cameraNormal, 1.0f, handleSize);
if (freeAxis.State == HandleSlider.StateType.Active)
HandleDrawing.Color = Color.White;
else if (freeAxis.State == HandleSlider.StateType.Hover)
HandleDrawing.Color = Color.BansheeOrange;
else
HandleDrawing.Color = gray;
HandleDrawing.DrawWireDisc(Vector3.Zero, cameraNormal, 1.2f, handleSize);
// Draw active rotation pie
HandleDrawing.Color = gray;
HandleDrawing.Transform = Matrix4.TRS(Position, EditorApplication.SceneViewCamera.SceneObject.Rotation, Vector3.One);
if (freeAxis.State == HandleSlider.StateType.Active)
HandleDrawing.DrawArc(Vector3.Zero, -Vector3.ZAxis, 1.2f, freeAxis.StartAngle, -freeAxis.Delta, handleSize);
HandleDrawing.Transform = Matrix4.TRS(Position, dragStartRotation, Vector3.One);
if (xAxis.State == HandleSlider.StateType.Active)
HandleDrawing.DrawArc(Vector3.Zero, Vector3.XAxis, 1.0f, xAxis.StartAngle, -xAxis.Delta, handleSize);
else if (yAxis.State == HandleSlider.StateType.Active)
HandleDrawing.DrawArc(Vector3.Zero, Vector3.YAxis, 1.0f, yAxis.StartAngle, -yAxis.Delta, handleSize);
else if (zAxis.State == HandleSlider.StateType.Active)
HandleDrawing.DrawArc(Vector3.Zero, Vector3.ZAxis, 1.0f, zAxis.StartAngle, -zAxis.Delta, handleSize);
}
///
/// The rotate handle only displays the 180 degree arc facing the camera and this method returns the angle at which
/// the arc starts for the X axis.
///
/// Determines should the local handle rotation be taken into account, or should it be frozen
/// to the value when handle drag started. This is useful because we do not want the visible
/// arc to change while the user is in the process of rotating the handle.
/// Angle at which to display the visible arc for the X axis rotations.
private Degree GetXStartAngle(bool frozen)
{
Quaternion handleRotation = frozen ? dragStartRotation : Rotation;
Vector3 xStartDir = Vector3.Cross(handleRotation.Inverse.Rotate(EditorApplication.SceneViewCamera.SceneObject.Forward), Vector3.XAxis);
return PointOnCircleToAngle(Vector3.XAxis, xStartDir);
}
///
/// The rotate handle only displays the 180 degree arc facing the camera and this method returns the angle at which
/// the arc starts for the Y axis.
///
/// Determines should the local handle rotation be taken into account, or should it be frozen
/// to the value when handle drag started. This is useful because we do not want the visible
/// arc to change while the user is in the process of rotating the handle.
/// Angle at which to display the visible arc for the Y axis rotations.
private Degree GetYStartAngle(bool frozen)
{
Quaternion handleRotation = frozen ? dragStartRotation : Rotation;
Vector3 yStartDir = Vector3.Cross(handleRotation.Inverse.Rotate(EditorApplication.SceneViewCamera.SceneObject.Forward), Vector3.YAxis);
return PointOnCircleToAngle(Vector3.YAxis, yStartDir);
}
///
/// The rotate handle only displays the 180 degree arc facing the camera and this method returns the angle at which
/// the arc starts for the Z axis.
///
/// Determines should the local handle rotation be taken into account, or should it be frozen
/// to the value when handle drag started. This is useful because we do not want the visible
/// arc to change while the user is in the process of rotating the handle.
/// Angle at which to display the visible arc for the Z axis rotations.
private Degree GetZStartAngle(bool frozen)
{
Quaternion handleRotation = frozen ? dragStartRotation : Rotation;
Vector3 zStartDir = Vector3.Cross(handleRotation.Inverse.Rotate(EditorApplication.SceneViewCamera.SceneObject.Forward), Vector3.ZAxis);
return PointOnCircleToAngle(Vector3.ZAxis, zStartDir);
}
///
/// Converts a point on the circle to an angle on the circle.
///
/// Up vector determining the orientation of the circle.
/// Point along a unit circle centered around the origin.
/// Angle at which the provided point is located on the circle.
private Degree PointOnCircleToAngle(Vector3 up, Vector3 point)
{
Quaternion rot = Quaternion.FromToRotation(up, Vector3.YAxis);
Matrix4 worldToPlane = Matrix4.TRS(Vector3.Zero, rot, Vector3.One);
point = worldToPlane.MultiplyDirection(point);
return (MathEx.Atan2(-point.z, -point.x) + MathEx.Pi);
}
}
/** @} */
}