VFXGizmo.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.VFX;
  5. using System.Linq;
  6. using System.Reflection;
  7. using Type = System.Type;
  8. using Delegate = System.Delegate;
  9. namespace UnityEditor.VFX
  10. {
  11. class VFXGizmoAttribute : System.Attribute
  12. {
  13. public VFXGizmoAttribute(Type type)
  14. {
  15. this.type = type;
  16. }
  17. public readonly Type type;
  18. }
  19. abstract class VFXGizmo
  20. {
  21. public interface IProperty<T>
  22. {
  23. bool isEditable { get; }
  24. void SetValue(T value);
  25. }
  26. public interface IContext
  27. {
  28. IProperty<T> RegisterProperty<T>(string memberPath);
  29. }
  30. public abstract void RegisterEditableMembers(IContext context);
  31. public abstract void CallDrawGizmo(object value);
  32. public abstract Bounds CallGetGizmoBounds(object obj);
  33. protected const float handleSize = 0.1f;
  34. protected const float arcHandleSizeMultiplier = 1.25f;
  35. public VFXCoordinateSpace currentSpace { get; set; }
  36. public bool spaceLocalByDefault { get; set; }
  37. public VisualEffect component { get; set; }
  38. private static readonly int s_HandleColorID = Shader.PropertyToID("_HandleColor");
  39. private static readonly int s_HandleSizeID = Shader.PropertyToID("_HandleSize");
  40. private static readonly int s_HandleZTestID = Shader.PropertyToID("_HandleZTest");
  41. private static readonly int s_ObjectToWorldID = Shader.PropertyToID("_ObjectToWorld");
  42. static Matrix4x4 StartCapDrawRevertingScale(Vector3 position, Quaternion rotation, float size)
  43. {
  44. //See : https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/Handles.cs#L956
  45. var lossyScale = Handles.matrix.lossyScale;
  46. var invLossyScale = new Vector3(1.0f / lossyScale.x, 1.0f / lossyScale.y, 1.0f / lossyScale.z);
  47. var mat = Handles.matrix;
  48. //Remove scale from the current global matrix
  49. mat *= Matrix4x4.TRS(Vector3.zero, Quaternion.identity, invLossyScale);
  50. //Correct position according to previous scale
  51. var correctPosition = new Vector3(position.x * lossyScale.x, position.y * lossyScale.y, position.z * lossyScale.z);
  52. mat *= Matrix4x4.TRS(correctPosition, rotation, Vector3.one);
  53. Shader.SetGlobalMatrix(s_ObjectToWorldID, mat);
  54. Shader.SetGlobalColor(s_HandleColorID, Handles.color);
  55. Shader.SetGlobalFloat(s_HandleSizeID, size);
  56. HandleUtility.handleMaterial.SetFloat(s_HandleZTestID, (float)Handles.zTest);
  57. HandleUtility.handleMaterial.SetPass(0);
  58. return mat;
  59. }
  60. static Mesh s_CubeMesh;
  61. static Mesh cubeMesh
  62. {
  63. get
  64. {
  65. if (s_CubeMesh == null)
  66. s_CubeMesh = Resources.GetBuiltinResource<Mesh>("Cube.fbx");
  67. return s_CubeMesh;
  68. }
  69. }
  70. protected static void CustomCubeHandleCap(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  71. {
  72. switch (eventType)
  73. {
  74. case EventType.Layout:
  75. case EventType.MouseMove:
  76. HandleUtility.AddControl(controlID, HandleUtility.DistanceToCube(position, rotation, size));
  77. break;
  78. case (EventType.Repaint):
  79. Graphics.DrawMeshNow(cubeMesh, StartCapDrawRevertingScale(position, rotation, size));
  80. break;
  81. }
  82. }
  83. private Quaternion GetHandleRotation(Quaternion localRotation)
  84. {
  85. if (Tools.pivotRotation == PivotRotation.Local)
  86. return localRotation;
  87. return Handles.matrix.inverse.rotation;
  88. }
  89. public bool PositionGizmo(ref Vector3 position, Vector3 rotation, bool always)
  90. {
  91. if (always || Tools.current == Tool.Move || Tools.current == Tool.Transform || Tools.current == Tool.None)
  92. {
  93. EditorGUI.BeginChangeCheck();
  94. position = Handles.PositionHandle(position, GetHandleRotation(Quaternion.Euler(rotation)));
  95. return EditorGUI.EndChangeCheck();
  96. }
  97. return false;
  98. }
  99. public bool ScaleGizmo(Vector3 position, ref Vector3 scale, Quaternion rotation, bool always)
  100. {
  101. if (always || Tools.current == Tool.Scale || Tools.current == Tool.Transform || Tools.current == Tool.None)
  102. {
  103. EditorGUI.BeginChangeCheck();
  104. scale = Handles.ScaleHandle(scale, position, GetHandleRotation(rotation), Tools.current == Tool.Transform || Tools.current == Tool.None ? HandleUtility.GetHandleSize(position) * 0.75f : HandleUtility.GetHandleSize(position));
  105. return EditorGUI.EndChangeCheck();
  106. }
  107. return false;
  108. }
  109. public bool ScaleGizmo(Vector3 position, Vector3 scale, Quaternion rotation, IProperty<Vector3> scaleProperty, bool always)
  110. {
  111. if (scaleProperty != null && scaleProperty.isEditable && ScaleGizmo(position, ref scale, rotation, always))
  112. {
  113. scaleProperty.SetValue(scale);
  114. return true;
  115. }
  116. return false;
  117. }
  118. public bool PositionGizmo(Vector3 position, Vector3 rotation, IProperty<Vector3> positionProperty, bool always)
  119. {
  120. if (positionProperty != null && positionProperty.isEditable && PositionGizmo(ref position, rotation, always))
  121. {
  122. positionProperty.SetValue(position);
  123. return true;
  124. }
  125. return false;
  126. }
  127. public bool RotationGizmo(Vector3 position, Vector3 rotation, IProperty<Vector3> anglesProperty, bool always)
  128. {
  129. if (anglesProperty != null && anglesProperty.isEditable && RotationGizmo(position, ref rotation, always))
  130. {
  131. anglesProperty.SetValue(rotation);
  132. return true;
  133. }
  134. return false;
  135. }
  136. bool RotationGizmo(Vector3 position, ref Vector3 rotation, bool always)
  137. {
  138. Quaternion quaternion = Quaternion.Euler(rotation);
  139. bool result = RotationGizmo(position, ref quaternion, always);
  140. if (result)
  141. {
  142. rotation = quaternion.eulerAngles;
  143. return true;
  144. }
  145. return false;
  146. }
  147. public bool ScaleGizmo(Vector3 position, Vector3 rotation, Vector3 scale, IProperty<Vector3> scaleProperty, bool always)
  148. {
  149. if (scaleProperty != null && scaleProperty.isEditable && ScaleGizmo(position, rotation, ref scale, always))
  150. {
  151. scaleProperty.SetValue(scale);
  152. return true;
  153. }
  154. return false;
  155. }
  156. bool ScaleGizmo(Vector3 position, Vector3 rotation, ref Vector3 scale, bool always)
  157. {
  158. var quaternion = Quaternion.Euler(rotation);
  159. return ScaleGizmo(position, quaternion, ref scale, always);
  160. }
  161. static Color ToActiveColorSpace(Color color)
  162. {
  163. return (QualitySettings.activeColorSpace == ColorSpace.Linear) ? color.linear : color;
  164. }
  165. static readonly Color[] s_AxisColor = new Color[] { Handles.xAxisColor, Handles.yAxisColor, Handles.zAxisColor, Handles.centerColor };
  166. static Vector3[] s_AxisVector = { Vector3.right, Vector3.up, Vector3.forward, Vector3.zero };
  167. static int[] s_AxisId = { "VFX_RotateAxis_X".GetHashCode(), "VFX_RotateAxis_Y".GetHashCode(), "VFX_RotateAxis_Z".GetHashCode(), "VFX_RotateAxis_Camera".GetHashCode() };
  168. static Color s_DisabledHandleColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
  169. static Quaternion CustomRotationHandle(Quaternion rotation, Vector3 position, bool onlyCameraAxis = false)
  170. {
  171. //Equivalent of Rotation Handle but with explicit id & *without* free rotate.
  172. var evt = Event.current;
  173. var isRepaint = evt.type == EventType.Repaint;
  174. var camForward = Handles.inverseMatrix.MultiplyVector(Camera.current != null ? Camera.current.transform.forward : Vector3.forward);
  175. var size = HandleUtility.GetHandleSize(position);
  176. var isHot = s_AxisId.Any(id => id == GUIUtility.hotControl);
  177. var previousColor = Handles.color;
  178. for (var i = onlyCameraAxis ? 3 : 0; i < 4; ++i)
  179. {
  180. Handles.color = ToActiveColorSpace(s_AxisColor[i]);
  181. var axisDir = i == 3 ? camForward : rotation * s_AxisVector[i];
  182. rotation = Handles.Disc(s_AxisId[i], rotation, position, axisDir, size, true, EditorSnapSettings.rotate);
  183. }
  184. if (isHot && evt.type == EventType.Repaint)
  185. {
  186. Handles.color = ToActiveColorSpace(s_DisabledHandleColor);
  187. Handles.DrawWireDisc(position, camForward, size, Handles.lineThickness);
  188. }
  189. Handles.color = previousColor;
  190. return rotation;
  191. }
  192. static int s_FreeRotationID = "VFX_FreeRotation_Id".GetHashCode();
  193. static Quaternion CustomFreeRotationHandle(Quaternion rotation, Vector3 position)
  194. {
  195. var previousColor = Handles.color;
  196. Handles.color = ToActiveColorSpace(s_DisabledHandleColor);
  197. var newRotation = Handles.FreeRotateHandle(s_FreeRotationID, rotation, position, HandleUtility.GetHandleSize(position));
  198. Handles.color = previousColor;
  199. return newRotation;
  200. }
  201. bool m_IsRotating = false;
  202. Quaternion m_StartRotation = Quaternion.identity;
  203. int m_HotControlRotation = -1;
  204. public bool RotationGizmo(Vector3 position, ref Quaternion rotation, bool always)
  205. {
  206. if (always || Tools.current == Tool.Rotate || Tools.current == Tool.Transform || Tools.current == Tool.None)
  207. {
  208. bool usingFreeRotation = GUIUtility.hotControl == s_FreeRotationID;
  209. var handleRotation = GetHandleRotation(rotation);
  210. EditorGUI.BeginChangeCheck();
  211. var rotationFromFreeHandle = CustomFreeRotationHandle(rotation, position);
  212. var rotationFromAxis = CustomRotationHandle(handleRotation, position);
  213. var newRotation = usingFreeRotation ? rotationFromFreeHandle : rotationFromAxis;
  214. if (EditorGUI.EndChangeCheck())
  215. {
  216. if (!m_IsRotating)
  217. {
  218. //Save first rotation state to avoid rotation accumulation while dragging in global space.
  219. m_StartRotation = rotation;
  220. m_HotControlRotation = GUIUtility.hotControl;
  221. }
  222. if (!usingFreeRotation /* Free rotation are always in local */ && Tools.pivotRotation == PivotRotation.Global)
  223. rotation = newRotation * Handles.matrix.rotation * m_StartRotation;
  224. else
  225. rotation = newRotation;
  226. m_IsRotating = true;
  227. return true;
  228. }
  229. if (GUIUtility.hotControl != m_HotControlRotation)
  230. {
  231. //If hotControl has changed, the dragging has been terminated.
  232. m_StartRotation = Quaternion.identity;
  233. m_IsRotating = false;
  234. m_HotControlRotation = -1;
  235. }
  236. }
  237. return false;
  238. }
  239. public bool ScaleGizmo(Vector3 position, Quaternion rotation, ref Vector3 scale, bool always)
  240. {
  241. if (always || Tools.current == Tool.Scale || Tools.current == Tool.Transform || Tools.current == Tool.None)
  242. {
  243. EditorGUI.BeginChangeCheck();
  244. var bckpColor = Handles.color;
  245. scale = Handles.ScaleHandle(scale, position, rotation);
  246. Handles.color = bckpColor; //Scale Handle modifies color without restoring it
  247. return EditorGUI.EndChangeCheck();
  248. }
  249. return false;
  250. }
  251. private static readonly int s_ArcGizmoName = "VFX_ArcGizmo".GetHashCode();
  252. public void ArcGizmo(Vector3 center, float radius, float degArc, IProperty<float> arcProperty, Quaternion rotation)
  253. {
  254. // Arc handle control
  255. if (arcProperty.isEditable)
  256. {
  257. using (new Handles.DrawingScope(Handles.matrix * Matrix4x4.Translate(center) * Matrix4x4.Rotate(rotation)))
  258. {
  259. EditorGUI.BeginChangeCheck();
  260. Vector3 arcHandlePosition = Quaternion.AngleAxis(degArc, Vector3.up) * Vector3.forward * radius;
  261. arcHandlePosition = Handles.Slider2D(
  262. s_ArcGizmoName,
  263. arcHandlePosition,
  264. Vector3.up,
  265. Vector3.forward,
  266. Vector3.right,
  267. handleSize * arcHandleSizeMultiplier * HandleUtility.GetHandleSize(arcHandlePosition),
  268. DefaultAngleHandleDrawFunction,
  269. Vector2.zero
  270. );
  271. if (EditorGUI.EndChangeCheck())
  272. {
  273. float newArc = Vector3.Angle(Vector3.forward, arcHandlePosition) * Mathf.Sign(Vector3.Dot(Vector3.right, arcHandlePosition));
  274. degArc += Mathf.DeltaAngle(degArc, newArc);
  275. degArc = Mathf.Repeat(degArc, 360.0f);
  276. arcProperty.SetValue(degArc * Mathf.Deg2Rad);
  277. }
  278. }
  279. }
  280. }
  281. static Vector3 m_InitialNormal;
  282. public bool NormalGizmo(Vector3 position, ref Vector3 normal, bool always)
  283. {
  284. if (Event.current.type == EventType.MouseDown)
  285. {
  286. m_InitialNormal = normal;
  287. }
  288. EditorGUI.BeginChangeCheck();
  289. Quaternion delta = Quaternion.identity;
  290. RotationGizmo(position, ref delta, always);
  291. if (EditorGUI.EndChangeCheck())
  292. {
  293. normal = delta * m_InitialNormal;
  294. return true;
  295. }
  296. return false;
  297. }
  298. public static void DefaultAngleHandleDrawFunction(int controlID, Vector3 position, Quaternion rotation, float size, EventType eventType)
  299. {
  300. Handles.DrawLine(Vector3.zero, position);
  301. // draw a cylindrical "hammer head" to indicate the direction the handle will move
  302. Vector3 worldPosition = Handles.matrix.MultiplyPoint3x4(position);
  303. Vector3 normal = worldPosition - Handles.matrix.MultiplyPoint3x4(Vector3.zero);
  304. Vector3 tangent = Handles.matrix.MultiplyVector(Quaternion.AngleAxis(90f, Vector3.up) * position);
  305. rotation = Quaternion.LookRotation(tangent, normal);
  306. Matrix4x4 matrix = Matrix4x4.TRS(worldPosition, rotation, (Vector3.one + Vector3.forward * arcHandleSizeMultiplier));
  307. using (new Handles.DrawingScope(matrix))
  308. Handles.CylinderHandleCap(controlID, Vector3.zero, Quaternion.identity, size, eventType);
  309. }
  310. public virtual bool needsComponent { get { return false; } }
  311. }
  312. abstract class VFXGizmo<T> : VFXGizmo
  313. {
  314. public override void CallDrawGizmo(object value)
  315. {
  316. if (value is T)
  317. OnDrawGizmo((T)value);
  318. }
  319. public override Bounds CallGetGizmoBounds(object value)
  320. {
  321. if (value is T)
  322. {
  323. return OnGetGizmoBounds((T)value);
  324. }
  325. return new Bounds();
  326. }
  327. public abstract void OnDrawGizmo(T value);
  328. public abstract Bounds OnGetGizmoBounds(T value);
  329. }
  330. abstract class VFXSpaceableGizmo<T> : VFXGizmo<T>
  331. {
  332. public override void OnDrawGizmo(T value)
  333. {
  334. Matrix4x4 oldMatrix = Handles.matrix;
  335. if (currentSpace == VFXCoordinateSpace.Local)
  336. {
  337. if (component == null) return;
  338. Handles.matrix = component.transform.localToWorldMatrix;
  339. }
  340. else
  341. {
  342. Handles.matrix = Matrix4x4.identity;
  343. }
  344. OnDrawSpacedGizmo(value);
  345. Handles.matrix = oldMatrix;
  346. }
  347. public override Bounds OnGetGizmoBounds(T value)
  348. {
  349. Bounds bounds = OnGetSpacedGizmoBounds(value);
  350. if (currentSpace == VFXCoordinateSpace.Local)
  351. {
  352. if (component == null)
  353. return new Bounds();
  354. return UnityEditorInternal.InternalEditorUtility.TransformBounds(bounds, component.transform);
  355. }
  356. return bounds;
  357. }
  358. public override bool needsComponent
  359. {
  360. get { return (currentSpace == VFXCoordinateSpace.Local) != spaceLocalByDefault; }
  361. }
  362. public abstract void OnDrawSpacedGizmo(T value);
  363. public abstract Bounds OnGetSpacedGizmoBounds(T value);
  364. }
  365. }