CinemachineNewFreeLookEditor.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #if CINEMACHINE_EXPERIMENTAL_VCAM
  2. using UnityEngine;
  3. using UnityEditor;
  4. using Cinemachine.Editor;
  5. using System.Collections.Generic;
  6. using Cinemachine.Utility;
  7. namespace Cinemachine
  8. {
  9. [CustomEditor(typeof(CinemachineNewFreeLook))]
  10. sealed class CinemachineNewFreeLookEditor
  11. : CinemachineVirtualCameraBaseEditor<CinemachineNewFreeLook>
  12. {
  13. GUIContent[] mRigNames = new GUIContent[]
  14. { new GUIContent("Top Rig"), new GUIContent("Bottom Rig") };
  15. GUIContent[] mOrbitNames = new GUIContent[]
  16. { new GUIContent("Top Rig"), new GUIContent("Main Rig"), new GUIContent("Bottom Rig") };
  17. GUIContent mAllLensLabel = new GUIContent(
  18. "Customize", "Custom settings for this rig. If unchecked, main rig settins will be used");
  19. VcamPipelineStageSubeditorSet mPipelineSet = new VcamPipelineStageSubeditorSet();
  20. /// <summary>Get the property names to exclude in the inspector.</summary>
  21. /// <param name="excluded">Add the names to this list</param>
  22. protected override void GetExcludedPropertiesInInspector(List<string> excluded)
  23. {
  24. base.GetExcludedPropertiesInInspector(excluded);
  25. excluded.Add(FieldPath(x => x.m_Rigs)); // can't use HideInInspector for this
  26. excluded.Add(FieldPath(x => x.m_Orbits));
  27. excluded.Add(FieldPath(x => x.m_SplineCurvature));
  28. }
  29. protected override void OnEnable()
  30. {
  31. base.OnEnable();
  32. mPipelineSet.CreateSubeditors(this);
  33. Target.UpdateInputAxisProvider();
  34. }
  35. protected override void OnDisable()
  36. {
  37. mPipelineSet.Shutdown();
  38. base.OnDisable();
  39. }
  40. public override void OnInspectorGUI()
  41. {
  42. // Ordinary properties
  43. BeginInspector();
  44. DrawHeaderInInspector();
  45. DrawPropertyInInspector(FindProperty(x => x.m_Priority));
  46. DrawTargetsInInspector(FindProperty(x => x.m_Follow), FindProperty(x => x.m_LookAt));
  47. DrawRemainingPropertiesInInspector();
  48. // Orbits
  49. EditorGUILayout.Space();
  50. EditorGUILayout.LabelField("Orbits", EditorStyles.boldLabel);
  51. SerializedProperty orbits = FindProperty(x => x.m_Orbits);
  52. EditorGUI.BeginChangeCheck();
  53. for (int i = 0; i < 3; ++i)
  54. {
  55. var o = orbits.GetArrayElementAtIndex(i);
  56. Rect rect = EditorGUILayout.GetControlRect(true);
  57. InspectorUtility.MultiPropertyOnLine(
  58. rect, mOrbitNames[i],
  59. new [] { o.FindPropertyRelative(() => Target.m_Orbits[i].m_Height),
  60. o.FindPropertyRelative(() => Target.m_Orbits[i].m_Radius) },
  61. null);
  62. }
  63. EditorGUILayout.PropertyField(FindProperty(x => x.m_SplineCurvature));
  64. if (EditorGUI.EndChangeCheck())
  65. serializedObject.ApplyModifiedProperties();
  66. // Pipeline Stages
  67. EditorGUILayout.Space();
  68. EditorGUILayout.LabelField("Main Rig", EditorStyles.boldLabel);
  69. var components = Target.ComponentCache;
  70. for (int i = 0; i < mPipelineSet.m_subeditors.Length; ++i)
  71. {
  72. var ed = mPipelineSet.m_subeditors[i];
  73. if (ed == null)
  74. continue;
  75. if (!ed.HasImplementation)
  76. continue;
  77. if ((CinemachineCore.Stage)i == CinemachineCore.Stage.Body)
  78. ed.TypeIsLocked = true;
  79. ed.OnInspectorGUI(components[i]); // may destroy component
  80. }
  81. // Rigs
  82. EditorGUILayout.Space();
  83. SerializedProperty rigs = FindProperty(x => x.m_Rigs);
  84. for (int i = 0; i < 2; ++i)
  85. {
  86. EditorGUILayout.Separator();
  87. DrawRigEditor(i, rigs.GetArrayElementAtIndex(i));
  88. }
  89. // Extensions
  90. DrawExtensionsWidgetInInspector();
  91. }
  92. Vector3 mPreviousPosition; // for position dragging
  93. private void OnSceneGUI()
  94. {
  95. if (!Target.UserIsDragging)
  96. mPreviousPosition = Target.transform.position;
  97. if (Selection.Contains(Target.gameObject) && Tools.current == Tool.Move
  98. && Event.current.type == EventType.MouseDrag)
  99. {
  100. // User might be dragging our position handle
  101. Target.UserIsDragging = true;
  102. Vector3 delta = Target.transform.position - mPreviousPosition;
  103. if (!delta.AlmostZero())
  104. {
  105. mPipelineSet.OnPositionDragged(delta);
  106. mPreviousPosition = Target.transform.position;
  107. // Adjust the rigs height and scale
  108. Transform follow = Target.Follow;
  109. if (follow != null)
  110. {
  111. Undo.RegisterCompleteObjectUndo(Target, "Camera drag");
  112. Vector3 up = Target.State.ReferenceUp;
  113. float heightDelta = Vector3.Dot(up, delta);
  114. Vector3 fwd = (Target.State.FinalPosition - follow.position).normalized;
  115. float oldRadius = Target.GetLocalPositionForCameraFromInput(
  116. Target.m_VerticalAxis.Value).magnitude;
  117. float newRadius = Mathf.Max(0.01f, oldRadius + Vector3.Dot(fwd, delta));
  118. for (int i = 0; i < 3; ++i)
  119. {
  120. Target.m_Orbits[i].m_Height += heightDelta;
  121. if (oldRadius > 0.001f)
  122. Target.m_Orbits[i].m_Radius *= newRadius / oldRadius;
  123. }
  124. }
  125. }
  126. }
  127. else if (GUIUtility.hotControl == 0 && Target.UserIsDragging)
  128. {
  129. // We're not dragging anything now, but we were
  130. InspectorUtility.RepaintGameView();
  131. Target.UserIsDragging = false;
  132. }
  133. }
  134. void DrawRigEditor(int rigIndex, SerializedProperty rig)
  135. {
  136. const float kBoxMargin = 3;
  137. CinemachineNewFreeLook.Rig def = new CinemachineNewFreeLook.Rig(); // for properties
  138. EditorGUILayout.BeginVertical(GUI.skin.box);
  139. EditorGUIUtility.labelWidth -= kBoxMargin;
  140. EditorGUILayout.LabelField(new GUIContent(mRigNames[rigIndex]), EditorStyles.boldLabel);
  141. ++EditorGUI.indentLevel;
  142. var components = Target.ComponentCache;
  143. if (DrawFoldoutPropertyWithEnabledCheckbox(
  144. rig.FindPropertyRelative(() => def.m_CustomLens),
  145. rig.FindPropertyRelative(() => def.m_Lens)))
  146. {
  147. Target.m_Rigs[rigIndex].m_Lens = Target.m_Lens;
  148. }
  149. int index = (int)CinemachineCore.Stage.Body;
  150. if (components[index] is CinemachineTransposer)
  151. {
  152. if (DrawFoldoutPropertyWithEnabledCheckbox(
  153. rig.FindPropertyRelative(() => def.m_CustomBody),
  154. rig.FindPropertyRelative(() => def.m_Body)))
  155. {
  156. Target.m_Rigs[rigIndex].m_Body.PullFrom(
  157. components[index] as CinemachineTransposer);
  158. }
  159. }
  160. index = (int)CinemachineCore.Stage.Aim;
  161. if (components[index] is CinemachineComposer)
  162. {
  163. if (DrawFoldoutPropertyWithEnabledCheckbox(
  164. rig.FindPropertyRelative(() => def.m_CustomAim),
  165. rig.FindPropertyRelative(() => def.m_Aim)))
  166. {
  167. Target.m_Rigs[rigIndex].m_Aim.PullFrom(
  168. components[index] as CinemachineComposer);
  169. }
  170. }
  171. index = (int)CinemachineCore.Stage.Noise;
  172. if (components[index] is CinemachineBasicMultiChannelPerlin)
  173. {
  174. if (DrawFoldoutPropertyWithEnabledCheckbox(
  175. rig.FindPropertyRelative(() => def.m_CustomNoise),
  176. rig.FindPropertyRelative(() => def.m_Noise)))
  177. {
  178. Target.m_Rigs[rigIndex].m_Noise.PullFrom(
  179. components[index] as CinemachineBasicMultiChannelPerlin);
  180. }
  181. }
  182. --EditorGUI.indentLevel;
  183. EditorGUILayout.EndVertical();
  184. EditorGUIUtility.labelWidth += kBoxMargin;
  185. }
  186. // Returns true if default value should be applied
  187. bool DrawFoldoutPropertyWithEnabledCheckbox(
  188. SerializedProperty enabledProperty, SerializedProperty property)
  189. {
  190. GUIContent label = new GUIContent(property.displayName, property.tooltip);
  191. Rect rect = EditorGUILayout.GetControlRect(true,
  192. (enabledProperty.boolValue && property.isExpanded)
  193. ? EditorGUI.GetPropertyHeight(property)
  194. : EditorGUIUtility.singleLineHeight);
  195. Rect r = rect; r.height = EditorGUIUtility.singleLineHeight;
  196. if (!enabledProperty.boolValue)
  197. EditorGUI.LabelField(r, label);
  198. float labelWidth = EditorGUIUtility.labelWidth;
  199. bool newValue = EditorGUI.ToggleLeft(
  200. new Rect(labelWidth, r.y, r.width - labelWidth, r.height),
  201. mAllLensLabel, enabledProperty.boolValue);
  202. if (newValue != enabledProperty.boolValue)
  203. {
  204. enabledProperty.boolValue = newValue;
  205. enabledProperty.serializedObject.ApplyModifiedProperties();
  206. property.isExpanded = newValue;
  207. return true;
  208. }
  209. if (newValue == true)
  210. {
  211. EditorGUI.BeginChangeCheck();
  212. EditorGUI.PropertyField(rect, property, property.isExpanded);
  213. if (EditorGUI.EndChangeCheck())
  214. enabledProperty.serializedObject.ApplyModifiedProperties();
  215. }
  216. return false;
  217. }
  218. [DrawGizmo(GizmoType.Active | GizmoType.Selected, typeof(CinemachineNewFreeLook))]
  219. private static void DrawFreeLookGizmos(CinemachineNewFreeLook vcam, GizmoType selectionType)
  220. {
  221. // Standard frustum and logo
  222. CinemachineBrainEditor.DrawVirtualCameraBaseGizmos(vcam, selectionType);
  223. Color originalGizmoColour = Gizmos.color;
  224. bool isActiveVirtualCam = CinemachineCore.Instance.IsLive(vcam);
  225. Gizmos.color = isActiveVirtualCam
  226. ? CinemachineSettings.CinemachineCoreSettings.ActiveGizmoColour
  227. : CinemachineSettings.CinemachineCoreSettings.InactiveGizmoColour;
  228. if (vcam.Follow != null)
  229. {
  230. Vector3 pos = vcam.Follow.position;
  231. Vector3 up = Vector3.up;
  232. CinemachineBrain brain = CinemachineCore.Instance.FindPotentialTargetBrain(vcam);
  233. if (brain != null)
  234. up = brain.DefaultWorldUp;
  235. var middleRig = vcam.GetComponent<CinemachineTransposer>();
  236. if (middleRig != null)
  237. {
  238. float scale = vcam.m_RadialAxis.Value;
  239. Quaternion orient = middleRig.GetReferenceOrientation(up);
  240. up = orient * Vector3.up;
  241. var orbital = middleRig as CinemachineOrbitalTransposer;
  242. if (orbital != null)
  243. {
  244. float rotation = orbital.m_XAxis.Value + orbital.m_Heading.m_Bias;
  245. orient = Quaternion.AngleAxis(rotation, up) * orient;
  246. }
  247. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  248. pos + up * vcam.m_Orbits[0].m_Height * scale,
  249. orient, vcam.m_Orbits[0].m_Radius * scale);
  250. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  251. pos + up * vcam.m_Orbits[1].m_Height * scale,
  252. orient, vcam.m_Orbits[1].m_Radius * scale);
  253. CinemachineOrbitalTransposerEditor.DrawCircleAtPointWithRadius(
  254. pos + up * vcam.m_Orbits[2].m_Height * scale,
  255. orient, vcam.m_Orbits[2].m_Radius * scale);
  256. DrawCameraPath(pos, orient, vcam);
  257. }
  258. }
  259. Gizmos.color = originalGizmoColour;
  260. }
  261. private static void DrawCameraPath(
  262. Vector3 atPos, Quaternion orient, CinemachineNewFreeLook vcam)
  263. {
  264. Matrix4x4 prevMatrix = Gizmos.matrix;
  265. Gizmos.matrix = Matrix4x4.TRS(atPos, orient, Vector3.one);
  266. const int kNumSteps = 20;
  267. Vector3 currPos = vcam.GetLocalPositionForCameraFromInput(0f);
  268. for (int i = 1; i < kNumSteps + 1; ++i)
  269. {
  270. float t = (float)i / (float)kNumSteps;
  271. Vector3 nextPos = vcam.GetLocalPositionForCameraFromInput(t);
  272. Gizmos.DrawLine(currPos, nextPos);
  273. currPos = nextPos;
  274. }
  275. Gizmos.matrix = prevMatrix;
  276. }
  277. }
  278. }
  279. #endif