//********************************** 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); } } /** @} */ }