//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System.Collections.Generic; using bs; namespace bs.Editor { /** @addtogroup Handles * @{ */ /// /// Manages the default set of built-in handles like move, rotate and scale. Handles switching between the three handle /// types, as well as switching between coordinate and pivot modes, while tracking and updating active scene object /// selection. Essentially it detects which handles need to be displayed and where, and then forwards the data /// to the specific handle for processing. /// internal sealed class DefaultHandleManager : Handle { /// /// Representation of a scene object currently being modified by a handle. /// private struct HandledObject { /// /// Creates a new scene object representation by recording the current object transform. /// /// Scene object that is being modified by a handle. public HandledObject(SceneObject so) { this.so = so; initialPosition = so.LocalPosition; initialRotation = so.LocalRotation; initialScale = so.LocalScale; } public SceneObject so; public Vector3 initialPosition; public Quaternion initialRotation; public Vector3 initialScale; } private SceneViewTool activeHandleType = SceneViewTool.View; private DefaultHandle activeHandle; private HandledObject[] activeSelection; private bool isDragged; private Vector3 initialHandlePosition; private Quaternion initialHandleRotation; /// protected internal override void PreInput() { SceneObject[] selectedSceneObjects = Selection.SceneObjects; if (selectedSceneObjects.Length == 0) { if (activeHandle != null) { activeHandle.Destroy(); activeHandle = null; } } else { if (activeHandleType != EditorApplication.ActiveSceneTool || activeHandle == null) { if (activeHandle != null) { activeHandle.Destroy(); activeHandle = null; } switch (EditorApplication.ActiveSceneTool) { case SceneViewTool.Move: activeHandle = new MoveHandle(); break; case SceneViewTool.Rotate: activeHandle = new RotateHandle(); break; case SceneViewTool.Scale: activeHandle = new ScaleHandle(); break; } activeHandleType = EditorApplication.ActiveSceneTool; } } if (activeHandle != null) { // In case the object moved programmatically, make the handle reflect its current transform UpdateActiveHandleTransform(selectedSceneObjects); activeHandle.PreInput(); } } /// protected internal override void PostInput() { if (activeHandle != null) { if (activeHandle.IsDragged()) { if (!isDragged) { isDragged = true; SceneObject[] selectedSceneObjects = Selection.SceneObjects; GameObjectUndo.RecordSceneObjectHeader(selectedSceneObjects); activeSelection = new HandledObject[selectedSceneObjects.Length]; for (int i = 0; i < selectedSceneObjects.Length; i++) activeSelection[i] = new HandledObject(selectedSceneObjects[i]); initialHandlePosition = activeHandle.Position; initialHandleRotation = activeHandle.Rotation; } } else { if (isDragged) GameObjectUndo.ResolveDiffs(); isDragged = false; activeSelection = null; } activeHandle.PostInput(); if (activeHandle.IsDragged()) { switch (activeHandleType) { case SceneViewTool.Move: MoveHandle moveHandle = (MoveHandle) activeHandle; foreach (var selectedObj in activeSelection) { SceneObject parentSO = selectedObj.so.Parent; if (parentSO == null) selectedObj.so.LocalPosition = selectedObj.initialPosition + moveHandle.Delta; else { Vector3 parentRelativeDelta = parentSO.Rotation.Inverse.Rotate(moveHandle.Delta); selectedObj.so.LocalPosition = selectedObj.initialPosition + parentRelativeDelta; } } break; case SceneViewTool.Rotate: { RotateHandle rotateHandle = (RotateHandle) activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = initialHandlePosition; temporarySO.LocalRotation = initialHandleRotation; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalRotation *= rotateHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) activeSelection[i].so.Parent = originalParents[i]; temporarySO.Destroy(); } break; case SceneViewTool.Scale: { ScaleHandle scaleHandle = (ScaleHandle) activeHandle; // Make sure we transform relative to the handle position SceneObject temporarySO = new SceneObject("Temp"); temporarySO.Position = activeHandle.Position; SceneObject[] originalParents = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) { originalParents[i] = activeSelection[i].so.Parent; activeSelection[i].so.LocalPosition = activeSelection[i].initialPosition; activeSelection[i].so.LocalRotation = activeSelection[i].initialRotation; activeSelection[i].so.LocalScale = activeSelection[i].initialScale; activeSelection[i].so.Parent = temporarySO; } temporarySO.LocalScale += scaleHandle.Delta; for (int i = 0; i < activeSelection.Length; i++) activeSelection[i].so.Parent = originalParents[i]; temporarySO.Destroy(); } break; } SceneObject[] selectedSceneObjects = new SceneObject[activeSelection.Length]; for (int i = 0; i < activeSelection.Length; i++) selectedSceneObjects[i] = activeSelection[i].so; // Make sure to update handle positions for the drawing method (otherwise they lag one frame) UpdateActiveHandleTransform(selectedSceneObjects); EditorApplication.SetSceneDirty(); } } else { isDragged = false; activeSelection = null; } } /// protected internal override void Draw() { if (activeHandle != null) activeHandle.Draw(); } /// /// Updates active handle position/rotation based on the currently selected object(s). /// private void UpdateActiveHandleTransform(SceneObject[] selectedSceneObjects) { if (activeHandle == null) return; Quaternion rotation; if (EditorApplication.ActiveCoordinateMode == HandleCoordinateMode.World) rotation = Quaternion.Identity; else rotation = selectedSceneObjects[0].Rotation; // We don't average rotation in case of multi-selection Vector3 position; if (EditorApplication.ActivePivotMode == HandlePivotMode.Pivot) position = selectedSceneObjects[0].Position; // Just take pivot from the first one, no averaging else { List flatenedHierarchy = new List(); foreach (var so in selectedSceneObjects) flatenedHierarchy.AddRange(EditorUtility.FlattenHierarchy(so)); position = EditorUtility.CalculateCenter(flatenedHierarchy.ToArray()); } activeHandle.Position = position; activeHandle.Rotation = rotation; } } /** @} */ }