Rasagar/Library/PackageCache/com.unity.cinemachine/Editor/Utility/CinemachineSceneTools.cs
2024-08-26 23:07:20 +03:00

773 lines
32 KiB
C#

#if UNITY_2021_2_OR_NEWER
using System;
using System.Collections.Generic;
using Cinemachine.Utility;
using UnityEditor;
using UnityEditor.EditorTools;
using UnityEngine;
namespace Cinemachine.Editor
{
/// <summary>
/// Static class that manages Cinemachine Tools. It knows which tool is active,
/// and ensures that exclusive tools are not active at the same time.
/// The tools and editors requiring tools register/unregister themselves here.
/// </summary>
static class CinemachineSceneToolUtility
{
#if UNITY_2022_1_OR_NEWER
/// <summary>
/// Checks whether tool is the currently active exclusive tool.
/// </summary>
/// <param name="tool">Tool to check.</param>
/// <returns>True, when the tool is the active exclusive tool. False, otherwise.</returns>
public static bool IsToolActive(Type tool)
{
return s_ActiveExclusiveTool == tool;
}
static Type s_ActiveExclusiveTool;
/// <summary>
/// Register your Type from the editor script's OnEnable function.
/// This way CinemachineTools will know which tools to display.
/// </summary>
/// <param name="tool">Tool to register</param>
public static void RegisterTool(Type tool)
{
if (s_RequiredTools.ContainsKey(tool))
{
s_RequiredTools[tool]++;
}
else
{
s_RequiredTools.Add(tool, 1);
}
s_TriggerToolBarRefresh = true;
}
/// <summary>
/// Unregister your Type from the editor script's OnDisable function.
/// This way CinemachineTools will know which tools to display.
/// </summary>
/// <param name="tool">Tool to register</param>
public static void UnregisterTool(Type tool)
{
if (s_RequiredTools.ContainsKey(tool))
{
s_RequiredTools[tool]--;
if (s_RequiredTools[tool] <= 0)
{
s_RequiredTools.Remove(tool);
}
}
s_TriggerToolBarRefresh = true;
}
internal static bool IsToolRequired(Type tool)
{
return s_RequiredTools.ContainsKey(tool);
}
static Dictionary<Type, int> s_RequiredTools;
internal static void SetTool(bool active, Type tool)
{
if (active)
{
s_ActiveExclusiveTool = tool;
}
else
{
s_ActiveExclusiveTool = s_ActiveExclusiveTool == tool ? null : s_ActiveExclusiveTool;
}
s_TriggerToolBarRefresh = true;
}
static CinemachineSceneToolUtility()
{
s_RequiredTools = new Dictionary<Type, int>();
EditorApplication.update += RefreshToolbar;
}
static bool s_TriggerToolBarRefresh;
static void RefreshToolbar()
{
if (s_TriggerToolBarRefresh)
{
#if UNITY_2022_2_OR_NEWER
ToolManager.RefreshAvailableTools();
#else
// The following is a hack to refresh the toolbars
foreach (var scene in SceneView.sceneViews)
{
if (((SceneView)scene).TryGetOverlay("unity-transform-toolbar", out var tools))
{
if (tools.displayed)
{
tools.displayed = false;
tools.displayed = true;
break;
}
}
}
#endif
s_TriggerToolBarRefresh = false;
}
}
#else
/// <summary>
/// Checks whether tool is the currently active exclusive tool.
/// </summary>
/// <param name="tool">Tool to check.</param>
/// <returns>True, when the tool is the active exclusive tool. False, otherwise.</returns>
public static bool IsToolActive(Type tool)
{
return s_ActiveExclusiveTool == tool;
}
static Type s_ActiveExclusiveTool;
/// <summary>
/// Register your Type from the editor script's OnEnable function.
/// This way CinemachineTools will know which tools to display.
/// </summary>
/// <param name="tool">Tool to register</param>
public static void RegisterTool(Type tool)
{
if (s_RequiredTools.ContainsKey(tool))
{
s_RequiredTools[tool]++;
}
else
{
s_RequiredTools.Add(tool, 1);
}
}
/// <summary>
/// Unregister your Type from the editor script's OnDisable function.
/// This way CinemachineTools will know which tools to display.
/// </summary>
/// <param name="tool">Tool to register</param>
public static void UnregisterTool(Type tool)
{
if (s_RequiredTools.ContainsKey(tool))
{
s_RequiredTools[tool]--;
if (s_RequiredTools[tool] <= 0)
{
s_RequiredTools.Remove(tool);
}
}
}
static Dictionary<Type, int> s_RequiredTools;
public delegate void ToolHandler(bool v);
struct CinemachineSceneToolDelegates
{
public ToolHandler ToggleSetter;
public ToolHandler IsDisplayedSetter;
}
static Dictionary<Type, CinemachineSceneToolDelegates> s_ExclusiveTools; // tools that can't be active at the same time
static Dictionary<Type, CinemachineSceneToolDelegates> s_Tools; // tools without restrictions
/// <summary>
/// Use for registering tool handlers for tools that are exclusive with each other,
/// meaning they cannot be active at the same time.
/// </summary>
/// <param name="tool">The tool to register.</param>
/// <param name="toggleSetter">The tool's toggle value setter.</param>
/// <param name="isDisplayedSetter">The tool's isDisplayed setter.</param>
internal static void RegisterExclusiveToolHandlers(Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
{
RegisterToolHandlers(ref s_ExclusiveTools, tool, toggleSetter, isDisplayedSetter);
}
/// <summary>
/// Use for registering tool handlers for tools that can be active anytime
/// without taking other tools into consideration.
/// </summary>
/// <param name="tool">The tool to register.</param>
/// <param name="toggleSetter">The tool's toggle value setter.</param>
/// <param name="isDisplayedSetter">The tool's isDisplayed setter.</param>
internal static void RegisterToolHandlers(Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
{
RegisterToolHandlers(ref s_Tools, tool, toggleSetter, isDisplayedSetter);
}
static void RegisterToolHandlers(ref Dictionary<Type, CinemachineSceneToolDelegates> tools,
Type tool, ToolHandler toggleSetter, ToolHandler isDisplayedSetter)
{
if (tools.ContainsKey(tool))
{
tools.Remove(tool);
}
tools.Add(tool, new CinemachineSceneToolDelegates
{
ToggleSetter = toggleSetter,
IsDisplayedSetter = isDisplayedSetter,
});
}
internal delegate bool ToolbarHandler();
static ToolbarHandler s_ToolBarIsDisplayed;
internal static void RegisterToolbarIsDisplayedHandler(ToolbarHandler handler)
{
s_ToolBarIsDisplayed = handler;
}
static bool s_ToolbarTurnOffByMe;
internal delegate bool ToolbarTurnOnOffHandler(bool on);
static ToolbarTurnOnOffHandler s_ToolBarDisplay;
internal static void RegisterToolbarDisplayHandler(ToolbarTurnOnOffHandler handler)
{
s_ToolBarDisplay = handler;
}
internal static void SetTool(bool active, Type tool)
{
if (active)
{
s_ActiveExclusiveTool = tool;
EnsureCinemachineToolsAreExclusiveWithUnityTools();
}
else
{
s_ActiveExclusiveTool = s_ActiveExclusiveTool == tool ? null : s_ActiveExclusiveTool;
}
}
static void EnsureCinemachineToolsAreExclusiveWithUnityTools()
{
foreach (var toolHandle in s_ExclusiveTools)
{
toolHandle.Value.ToggleSetter(toolHandle.Key == s_ActiveExclusiveTool);
}
if (s_ActiveExclusiveTool != null)
{
Tools.current = Tool.None; // Cinemachine tools are exclusive with unity tools
}
}
static CinemachineSceneToolUtility()
{
s_ExclusiveTools = new Dictionary<Type, CinemachineSceneToolDelegates>();
s_Tools = new Dictionary<Type, CinemachineSceneToolDelegates>();
s_RequiredTools = new Dictionary<Type, int>();
EditorApplication.update += () =>
{
if (s_RequiredTools.Count <= 0)
{
s_ToolbarTurnOffByMe |= s_ToolBarDisplay(false);
}
else if (s_ToolbarTurnOffByMe)
{
s_ToolBarDisplay(true);
s_ToolbarTurnOffByMe = false;
}
var cmToolbarIsHidden = !s_ToolBarIsDisplayed();
// if a unity tool is selected or cmToolbar is hidden, unselect our tools.
if (s_ActiveExclusiveTool != null && (Tools.current != Tool.None || cmToolbarIsHidden))
{
SetTool(true, null);
}
if (!cmToolbarIsHidden)
{
// only display cm tools that are relevant for the current selection
foreach (var toolHandle in s_ExclusiveTools)
{
toolHandle.Value.IsDisplayedSetter(s_RequiredTools.ContainsKey(toolHandle.Key));
}
foreach (var toolHandle in s_Tools)
{
toolHandle.Value.IsDisplayedSetter(s_RequiredTools.ContainsKey(toolHandle.Key));
}
}
};
}
#endif
}
static class CinemachineSceneToolHelpers
{
public const float LineThickness = 2f;
public static readonly Color HelperLineDefaultColor = new Color(255, 255, 255, 25);
const float k_DottedLineSpacing = 4f;
static GUIStyle s_LabelStyle = new GUIStyle
{
normal =
{
background = AssetDatabase.LoadAssetAtPath<Texture2D>(ScriptableObjectUtility.
CinemachineRealativeInstallPath + "/Editor/EditorResources/SceneToolsLabelBackground.png"),
textColor = Handles.selectedColor,
},
fontStyle = FontStyle.Bold,
padding = new RectOffset(5, 0, 5, 0)
};
public static float SliderHandleDelta(Vector3 newPos, Vector3 oldPos, Vector3 forward)
{
var delta = newPos - oldPos;
return Mathf.Sign(Vector3.Dot(delta, forward)) * delta.magnitude;
}
/// <summary>
/// Calculate delta and discard imprecision.
/// </summary>
public static Vector3 PositionHandleDelta(Quaternion rot, Vector3 newPos, Vector3 oldPos)
{
var delta =
Quaternion.Inverse(rot) * (newPos - oldPos);
delta = new Vector3(
Mathf.Abs(delta.x) < UnityVectorExtensions.Epsilon ? 0 : delta.x,
Mathf.Abs(delta.y) < UnityVectorExtensions.Epsilon ? 0 : delta.y,
Mathf.Abs(delta.z) < UnityVectorExtensions.Epsilon ? 0 : delta.z);
return delta;
}
public static void DrawLabel(Vector3 position, string text)
{
var labelOffset = HandleUtility.GetHandleSize(position) / 5f;
Handles.Label(position + new Vector3(0, -labelOffset, 0), text, s_LabelStyle);
}
public static float CubeHandleCapSize(Vector3 position) => HandleUtility.GetHandleSize(position) / 10f;
static int s_ScaleSliderHash = "ScaleSliderHash".GetHashCode();
static float s_FOVAfterLastToolModification;
public static void FovToolHandle(CinemachineVirtualCameraBase vcam, SerializedProperty lensProperty,
in LensSettings lens, bool isLensHorizontal)
{
var orthographic = lens.Orthographic;
if (GUIUtility.hotControl == 0)
{
s_FOVAfterLastToolModification = orthographic ? lens.OrthographicSize : lens.FieldOfView;
}
var originalColor = Handles.color;
Handles.color = Handles.preselectionColor;
var camPos = vcam.State.FinalPosition;
var camRot = vcam.State.FinalOrientation;
var camForward = camRot * Vector3.forward;
EditorGUI.BeginChangeCheck();
#if UNITY_2022_2_OR_NEWER
var fovHandleId = GUIUtility.GetControlID(s_ScaleSliderHash, FocusType.Passive);
var newFov = Handles.ScaleSlider(fovHandleId, s_FOVAfterLastToolModification,
camPos, camForward, camRot, HandleUtility.GetHandleSize(camPos), 0.1f);
#else
var fovHandleId = GUIUtility.GetControlID(s_ScaleSliderHash, FocusType.Passive) + 1;
var newFov = Handles.ScaleSlider(
s_FOVAfterLastToolModification, camPos, camForward, camRot, HandleUtility.GetHandleSize(camPos), 0.1f);
#endif
if (EditorGUI.EndChangeCheck())
{
if (orthographic)
{
lensProperty.FindPropertyRelative("OrthographicSize").floatValue +=
(s_FOVAfterLastToolModification - newFov);
}
else
{
lensProperty.FindPropertyRelative("FieldOfView").floatValue +=
(s_FOVAfterLastToolModification - newFov);
lensProperty.FindPropertyRelative("FieldOfView").floatValue =
Mathf.Clamp(lensProperty.FindPropertyRelative("FieldOfView").floatValue, 1f, 179f);
}
lensProperty.serializedObject.ApplyModifiedProperties();
}
s_FOVAfterLastToolModification = newFov;
var fovHandleDraggedOrHovered =
GUIUtility.hotControl == fovHandleId || HandleUtility.nearestControl == fovHandleId;
if (fovHandleDraggedOrHovered)
{
var labelPos = camPos + camForward * HandleUtility.GetHandleSize(camPos);
if (lens.IsPhysicalCamera)
{
DrawLabel(labelPos, "Focal Length (" +
Camera.FieldOfViewToFocalLength(lens.FieldOfView, lens.SensorSize.y).ToString("F1") + ")");
}
else if (orthographic)
{
DrawLabel(labelPos, "Orthographic Size (" +
lens.OrthographicSize.ToString("F1") + ")");
}
else if (isLensHorizontal)
{
DrawLabel(labelPos, "Horizontal FOV (" +
Camera.VerticalToHorizontalFieldOfView(lens.FieldOfView, lens.Aspect).ToString("F1") + ")");
}
else
{
DrawLabel(labelPos, "Vertical FOV (" +
lens.FieldOfView.ToString("F1") + ")");
}
}
Handles.color = fovHandleDraggedOrHovered ? Handles.selectedColor : HelperLineDefaultColor;
var vcamLocalToWorld = Matrix4x4.TRS(camPos, camRot, Vector3.one);
DrawFrustum(vcamLocalToWorld, lens);
SoloOnDrag(GUIUtility.hotControl == fovHandleId, vcam, fovHandleId);
Handles.color = originalColor;
}
public static void NearFarClipHandle(CinemachineVirtualCameraBase vcam, SerializedProperty lens)
{
var originalColor = Handles.color;
Handles.color = Handles.preselectionColor;
var vcamState = vcam.State;
var camPos = vcamState.FinalPosition;
var camRot = vcamState.FinalOrientation;
var camForward = camRot * Vector3.forward;
var nearClipPlane = lens.FindPropertyRelative("NearClipPlane");
var farClipPlane = lens.FindPropertyRelative("FarClipPlane");
var nearClipPos = camPos + camForward * nearClipPlane.floatValue;
var farClipPos = camPos + camForward * farClipPlane.floatValue;
var vcamLens = vcamState.Lens;
EditorGUI.BeginChangeCheck();
var ncHandleId = GUIUtility.GetControlID(FocusType.Passive);
var newNearClipPos = Handles.Slider(ncHandleId, nearClipPos, camForward,
CubeHandleCapSize(nearClipPos), Handles.CubeHandleCap, 0.5f);
var fcHandleId = GUIUtility.GetControlID(FocusType.Passive);
var newFarClipPos = Handles.Slider(fcHandleId, farClipPos, camForward,
CubeHandleCapSize(farClipPos), Handles.CubeHandleCap, 0.5f);
if (EditorGUI.EndChangeCheck())
{
nearClipPlane.floatValue +=
SliderHandleDelta(newNearClipPos, nearClipPos, camForward);
if (!vcamLens.Orthographic)
{
nearClipPlane.floatValue = Mathf.Max(0.01f, nearClipPlane.floatValue);
}
farClipPlane.floatValue +=
SliderHandleDelta(newFarClipPos, farClipPos, camForward);
lens.serializedObject.ApplyModifiedProperties();
}
var vcamLocalToWorld = Matrix4x4.TRS(camPos, camRot, Vector3.one);
Handles.color = HelperLineDefaultColor;
DrawFrustum(vcamLocalToWorld, vcamLens);
if (GUIUtility.hotControl == ncHandleId || HandleUtility.nearestControl == ncHandleId)
{
DrawPreFrustum(vcamLocalToWorld, vcamLens);
DrawLabel(nearClipPos, "Near Clip Plane (" + nearClipPlane.floatValue.ToString("F1") + ")");
}
if (GUIUtility.hotControl == fcHandleId || HandleUtility.nearestControl == fcHandleId)
{
DrawPreFrustum(vcamLocalToWorld, vcamLens);
DrawLabel(farClipPos, "Far Clip Plane (" + farClipPlane.floatValue.ToString("F1") + ")");
}
SoloOnDrag(GUIUtility.hotControl == ncHandleId || GUIUtility.hotControl == fcHandleId,
vcam, Mathf.Min(ncHandleId, fcHandleId));
Handles.color = originalColor;
}
static void DrawPreFrustum(Matrix4x4 transform, LensSettings lens)
{
if (!lens.Orthographic && lens.NearClipPlane >= 0)
{
DrawPerspectiveFrustum(transform, lens.FieldOfView,
lens.NearClipPlane, 0, lens.Aspect, true);
}
}
static void DrawFrustum(Matrix4x4 transform, LensSettings lens)
{
if (lens.Orthographic)
{
DrawOrthographicFrustum(transform, lens.OrthographicSize,
lens.FarClipPlane, lens.NearClipPlane, lens.Aspect);
}
else
{
DrawPerspectiveFrustum(transform, lens.FieldOfView,
lens.FarClipPlane, lens.NearClipPlane, lens.Aspect, false);
}
}
static void DrawOrthographicFrustum(Matrix4x4 transform,
float orthographicSize, float farClipPlane, float nearClipRange, float aspect)
{
var originalMatrix = Handles.matrix;
Handles.matrix = transform;
var size = new Vector3(aspect * orthographicSize * 2,
orthographicSize * 2, farClipPlane - nearClipRange);
Handles.DrawWireCube(new Vector3(0, 0, (size.z / 2) + nearClipRange), size);
Handles.matrix = originalMatrix;
}
static void DrawPerspectiveFrustum(Matrix4x4 transform,
float fov, float farClipPlane, float nearClipRange, float aspect, bool dottedLine)
{
var originalMatrix = Handles.matrix;
Handles.matrix = transform;
fov = fov * 0.5f * Mathf.Deg2Rad;
var tanfov = Mathf.Tan(fov);
var farEnd = new Vector3(0, 0, farClipPlane);
var endSizeX = new Vector3(farClipPlane * tanfov * aspect, 0, 0);
var endSizeY = new Vector3(0, farClipPlane * tanfov, 0);
Vector3 s1, s2, s3, s4;
var e1 = farEnd + endSizeX + endSizeY;
var e2 = farEnd - endSizeX + endSizeY;
var e3 = farEnd - endSizeX - endSizeY;
var e4 = farEnd + endSizeX - endSizeY;
if (nearClipRange <= 0.0f)
{
s1 = s2 = s3 = s4 = Vector3.zero;
}
else
{
var startSizeX = new Vector3(nearClipRange * tanfov * aspect, 0, 0);
var startSizeY = new Vector3(0, nearClipRange * tanfov, 0);
var startPoint = new Vector3(0, 0, nearClipRange);
s1 = startPoint + startSizeX + startSizeY;
s2 = startPoint - startSizeX + startSizeY;
s3 = startPoint - startSizeX - startSizeY;
s4 = startPoint + startSizeX - startSizeY;
if (dottedLine)
{
Handles.DrawDottedLine(s1, s2, k_DottedLineSpacing);
Handles.DrawDottedLine(s2, s3, k_DottedLineSpacing);
Handles.DrawDottedLine(s3, s4, k_DottedLineSpacing);
Handles.DrawDottedLine(s4, s1, k_DottedLineSpacing);
}
else
{
Handles.DrawLine(s1, s2);
Handles.DrawLine(s2, s3);
Handles.DrawLine(s3, s4);
Handles.DrawLine(s4, s1);
}
}
if (dottedLine)
{
Handles.DrawDottedLine(e1, e2, k_DottedLineSpacing);
Handles.DrawDottedLine(e2, e3, k_DottedLineSpacing);
Handles.DrawDottedLine(e3, e4, k_DottedLineSpacing);
Handles.DrawDottedLine(e4, e1, k_DottedLineSpacing);
Handles.DrawDottedLine(s1, e1, k_DottedLineSpacing);
Handles.DrawDottedLine(s2, e2, k_DottedLineSpacing);
Handles.DrawDottedLine(s3, e3, k_DottedLineSpacing);
Handles.DrawDottedLine(s4, e4, k_DottedLineSpacing);
}
else
{
Handles.DrawLine(e1, e2);
Handles.DrawLine(e2, e3);
Handles.DrawLine(e3, e4);
Handles.DrawLine(e4, e1);
Handles.DrawLine(s1, e1);
Handles.DrawLine(s2, e2);
Handles.DrawLine(s3, e3);
Handles.DrawLine(s4, e4);
}
Handles.matrix = originalMatrix;
}
public static void TrackedObjectOffsetTool(
CinemachineComponentBase cmComponent, SerializedProperty trackedObjectOffset)
{
var originalColor = Handles.color;
var lookAtPos = cmComponent.LookAtTargetPosition;
var lookAtRot = cmComponent.LookAtTargetRotation;
var trackedObjectPos = lookAtPos + lookAtRot * trackedObjectOffset.vector3Value;
EditorGUI.BeginChangeCheck();
#if UNITY_2022_2_OR_NEWER
var tooHandleIds = Handles.PositionHandleIds.@default;
var newTrackedObjectPos = Handles.PositionHandle(tooHandleIds, trackedObjectPos, lookAtRot);
var tooHandleMinId = tooHandleIds.x - 1;
var tooHandleMaxId = tooHandleIds.xyz + 1;
#else
var tooHandleMinId = GUIUtility.GetControlID(FocusType.Passive);
var newTrackedObjectPos = Handles.PositionHandle(trackedObjectPos, lookAtRot);
var tooHandleMaxId = GUIUtility.GetControlID(FocusType.Passive);
#endif
if (EditorGUI.EndChangeCheck())
{
trackedObjectOffset.vector3Value +=
PositionHandleDelta(lookAtRot, newTrackedObjectPos, trackedObjectPos);
trackedObjectOffset.serializedObject.ApplyModifiedProperties();
}
var trackedObjectOffsetHandleIsDragged =
tooHandleMinId < GUIUtility.hotControl && GUIUtility.hotControl < tooHandleMaxId;
var trackedObjectOffsetHandleIsUsedOrHovered = trackedObjectOffsetHandleIsDragged ||
tooHandleMinId < HandleUtility.nearestControl && HandleUtility.nearestControl < tooHandleMaxId;
if (trackedObjectOffsetHandleIsUsedOrHovered)
{
DrawLabel(trackedObjectPos, "(" + cmComponent.Stage + ") Tracked Object Offset "
+ trackedObjectOffset.vector3Value.ToString("F1"));
}
Handles.color = trackedObjectOffsetHandleIsUsedOrHovered ?
Handles.selectedColor : HelperLineDefaultColor;
Handles.DrawDottedLine(lookAtPos, trackedObjectPos, k_DottedLineSpacing);
Handles.DrawLine(trackedObjectPos, cmComponent.VcamState.FinalPosition);
SoloOnDrag(trackedObjectOffsetHandleIsDragged, cmComponent.VirtualCamera, tooHandleMaxId);
Handles.color = originalColor;
}
public static void TransposerFollowOffsetTool(CinemachineTransposer cmComponent)
{
var originalColor = Handles.color;
var brain = CinemachineCore.Instance.FindPotentialTargetBrain(cmComponent.VirtualCamera);
var up = brain != null ? brain.DefaultWorldUp : Vector3.up;
var camPos = cmComponent.GetTargetCameraPosition(up);
var camRot = cmComponent.GetReferenceOrientation(up);
EditorGUI.BeginChangeCheck();
#if UNITY_2022_2_OR_NEWER
var foHandleIds = Handles.PositionHandleIds.@default;
var newPos = Handles.PositionHandle(foHandleIds, camPos, camRot);
var foHandleMinId = foHandleIds.x - 1;
var foHandleMaxId = foHandleIds.xyz + 1;
#else
var foHandleMinId = GUIUtility.GetControlID(FocusType.Passive);
var newPos = Handles.PositionHandle(camPos, camRot);
var foHandleMaxId = GUIUtility.GetControlID(FocusType.Passive);
#endif
if (EditorGUI.EndChangeCheck())
{
var so = new SerializedObject(cmComponent);
var followOffset = so.FindProperty(() => cmComponent.m_FollowOffset);
followOffset.vector3Value += PositionHandleDelta(camRot, newPos, camPos);
so.ApplyModifiedProperties();
followOffset.vector3Value = cmComponent.EffectiveOffset;
so.ApplyModifiedProperties();
}
var followOffsetHandleIsDragged =
foHandleMinId < GUIUtility.hotControl && GUIUtility.hotControl < foHandleMaxId;
var followOffsetHandleIsDraggedOrHovered = followOffsetHandleIsDragged ||
foHandleMinId < HandleUtility.nearestControl && HandleUtility.nearestControl < foHandleMaxId;
if (followOffsetHandleIsDraggedOrHovered)
{
DrawLabel(camPos, "Follow offset " + cmComponent.m_FollowOffset.ToString("F1"));
}
Handles.color = followOffsetHandleIsDraggedOrHovered ? Handles.selectedColor : HelperLineDefaultColor;
Handles.DrawDottedLine(cmComponent.FollowTargetPosition, camPos, k_DottedLineSpacing);
SoloOnDrag(followOffsetHandleIsDragged, cmComponent.VirtualCamera, foHandleMaxId);
Handles.color = originalColor;
}
/// <summary>
/// Draws Orbit handles (e.g. for freelook)
/// </summary>
/// <returns>Index of the rig being edited, or -1 if none</returns>
public static int OrbitControlHandle(
CinemachineVirtualCameraBase vcam, SerializedProperty orbits)
{
var originalColor = Handles.color;
var followPos = vcam.Follow.position;
var draggedRig = -1;
var minIndex = 1;
for (var rigIndex = 0; rigIndex < orbits.arraySize; ++rigIndex)
{
var orbit = orbits.GetArrayElementAtIndex(rigIndex);
var orbitHeight = orbit.FindPropertyRelative("m_Height");
var orbitRadius = orbit.FindPropertyRelative("m_Radius");
Handles.color = Handles.preselectionColor;
EditorGUI.BeginChangeCheck();
var heightHandleId = GUIUtility.GetControlID(FocusType.Passive);
var heightHandlePos = followPos + Vector3.up * orbitHeight.floatValue;
var newHeightHandlePos = Handles.Slider(heightHandleId, heightHandlePos, Vector3.up,
CubeHandleCapSize(heightHandlePos), Handles.CubeHandleCap, 0.5f);
var radiusHandleOffset = Vector3.right;
var radiusHandleId = GUIUtility.GetControlID(FocusType.Passive);
var radiusHandlePos = followPos + Vector3.up * orbitHeight.floatValue
+ radiusHandleOffset * orbitRadius.floatValue;
var newRadiusHandlePos = Handles.Slider(radiusHandleId, radiusHandlePos, radiusHandleOffset,
CubeHandleCapSize(radiusHandlePos), Handles.CubeHandleCap, 0.5f);
if (EditorGUI.EndChangeCheck())
{
orbitHeight.floatValue +=
SliderHandleDelta(newHeightHandlePos, heightHandlePos, Vector3.up);
orbitRadius.floatValue +=
SliderHandleDelta(newRadiusHandlePos, radiusHandlePos, radiusHandleOffset);
orbits.serializedObject.ApplyModifiedProperties();
}
var isDragged = GUIUtility.hotControl == heightHandleId || GUIUtility.hotControl == radiusHandleId;
Handles.color = isDragged || HandleUtility.nearestControl == heightHandleId ||
HandleUtility.nearestControl == radiusHandleId ? Handles.selectedColor : HelperLineDefaultColor;
if (GUIUtility.hotControl == heightHandleId || HandleUtility.nearestControl == heightHandleId)
{
DrawLabel(heightHandlePos, "Height: " + orbitHeight.floatValue);
}
if (GUIUtility.hotControl == radiusHandleId || HandleUtility.nearestControl == radiusHandleId)
{
DrawLabel(radiusHandlePos, "Radius: " + orbitRadius.floatValue);
}
Handles.DrawWireDisc(newHeightHandlePos, Vector3.up, orbitRadius.floatValue);
if (isDragged)
{
draggedRig = rigIndex;
minIndex = Mathf.Min(Mathf.Min(heightHandleId), radiusHandleId);
}
}
SoloOnDrag(draggedRig != -1, vcam, minIndex);
Handles.color = originalColor;
return draggedRig;
}
static bool s_IsDragging;
static ICinemachineCamera s_UserSolo;
public static void SoloOnDrag(bool isDragged, ICinemachineCamera vcam, int handleMaxId)
{
if (isDragged)
{
if (!s_IsDragging)
{
s_UserSolo = CinemachineBrain.SoloCamera;
s_IsDragging = true;
}
CinemachineBrain.SoloCamera = vcam;
}
else if (s_IsDragging && handleMaxId != -1) // Handles sometimes return -1 as id, ignore those frames
{
CinemachineBrain.SoloCamera = s_UserSolo;
InspectorUtility.RepaintGameView();
s_IsDragging = false;
s_UserSolo = null;
}
}
}
}
#endif