VFXPropertyAttribute.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Text.RegularExpressions;
  6. using UnityEngine;
  7. using UnityEngine.Assertions.Must;
  8. using UnityEngine.UIElements;
  9. using UnityEngine.VFX;
  10. namespace UnityEditor.VFX
  11. {
  12. [System.AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
  13. sealed class EnumAttribute : PropertyAttribute
  14. {
  15. public EnumAttribute(string[] values)
  16. {
  17. this.values = values;
  18. }
  19. public readonly string[] values;
  20. }
  21. // Attribute used to normalize a Vector or float
  22. [System.AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
  23. sealed class NormalizeAttribute : PropertyAttribute
  24. {
  25. }
  26. // Attribute used to display a float in degrees in the UI
  27. [System.AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
  28. sealed class AngleAttribute : PropertyAttribute
  29. {
  30. }
  31. // Attribute used to constrain a property to a Regex query
  32. [System.AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
  33. sealed class RegexAttribute : PropertyAttribute
  34. {
  35. public RegexAttribute(string _pattern, int _maxLength = int.MaxValue) { pattern = _pattern; maxLength = _maxLength; }
  36. public string pattern { get; set; }
  37. public int maxLength { get; set; }
  38. }
  39. struct VFXPropertyAttributes
  40. {
  41. [Flags]
  42. public enum Type
  43. {
  44. Range = GraphAttribute | (1 << 0),
  45. Min = GraphAttribute | (1 << 1),
  46. Normalized = GraphAttribute | (1 << 2),
  47. Tooltip = 1 << 3,
  48. Angle = 1 << 4,
  49. Color = 1 << 5,
  50. Regex = 1 << 6,
  51. Delayed = 1 << 7,
  52. BitField = 1 << 8,
  53. Enum = GraphAttribute | 1 << 9,
  54. MinMax = GraphAttribute | 1 << 10,
  55. // Tells whether this attribute modifies the expression graph
  56. GraphAttribute = 1 << 31,
  57. }
  58. private static readonly Dictionary<System.Type, Type> s_RegisteredAttributes = new Dictionary<System.Type, Type>()
  59. {
  60. { typeof(RangeAttribute), Type.Range },
  61. { typeof(MinAttribute), Type.Min },
  62. { typeof(NormalizeAttribute), Type.Normalized },
  63. { typeof(TooltipAttribute), Type.Tooltip },
  64. { typeof(AngleAttribute), Type.Angle },
  65. { typeof(ShowAsColorAttribute), Type.Color },
  66. { typeof(RegexAttribute), Type.Regex },
  67. { typeof(DelayedAttribute), Type.Delayed },
  68. { typeof(BitFieldAttribute), Type.BitField },
  69. { typeof(EnumAttribute), Type.Enum },
  70. { typeof(MinMaxAttribute), Type.MinMax},
  71. };
  72. public VFXPropertyAttributes(params object[] attributes) : this()
  73. {
  74. if (attributes != null && attributes.Length != 0)
  75. {
  76. if (attributes.Any(a => !(a is Attribute)))
  77. throw new ArgumentException("Only C# attributes are allowed to be passed to this method");
  78. m_AllAttributes = attributes.Where(o => s_RegisteredAttributes.ContainsKey(o.GetType())).Cast<Attribute>().ToArray();
  79. m_GraphAttributes = m_AllAttributes.Where(o => (s_RegisteredAttributes[o.GetType()] & Type.GraphAttribute) != 0).ToArray();
  80. foreach (var attribute in m_AllAttributes)
  81. {
  82. Type attributeType = s_RegisteredAttributes[attribute.GetType()];
  83. // Check multi inclusion of the same attribute
  84. if (Is(attributeType))
  85. throw new ArgumentException($"The same property attribute type ({attribute.GetType()}) was added twice");
  86. m_Flag |= attributeType;
  87. }
  88. }
  89. }
  90. public bool IsEqual(VFXPropertyAttributes other)
  91. {
  92. if (m_Flag != other.m_Flag)
  93. return false;
  94. if (m_AllAttributes == null)
  95. return other.m_AllAttributes == null;
  96. if (other.m_AllAttributes == null)
  97. return false;
  98. if (m_AllAttributes.Length != other.m_AllAttributes.Length)
  99. return false;
  100. for (int i = 0; i < m_AllAttributes.Length; ++i)
  101. if (!m_AllAttributes[i].Equals(other.m_AllAttributes[i]))
  102. return false;
  103. return true;
  104. }
  105. public VFXExpression ApplyToExpressionGraph(VFXExpression exp)
  106. {
  107. if (m_GraphAttributes == null)
  108. return exp;
  109. foreach (PropertyAttribute attribute in m_GraphAttributes)
  110. {
  111. if (attribute is RangeAttribute)
  112. {
  113. var rangeAttribute = (RangeAttribute)attribute;
  114. switch (exp.valueType)
  115. {
  116. case VFXValueType.Int32:
  117. exp = VFXOperatorUtility.Clamp(exp, VFXValue.Constant((int)rangeAttribute.min), VFXValue.Constant((int)rangeAttribute.max), false);
  118. break;
  119. case VFXValueType.Uint32:
  120. exp = VFXOperatorUtility.Clamp(exp, VFXValue.Constant((uint)rangeAttribute.min), VFXValue.Constant((uint)rangeAttribute.max), false);
  121. break;
  122. case VFXValueType.Float:
  123. case VFXValueType.Float2:
  124. case VFXValueType.Float3:
  125. case VFXValueType.Float4:
  126. exp = VFXOperatorUtility.Clamp(exp, VFXValue.Constant(rangeAttribute.min), VFXValue.Constant(rangeAttribute.max));
  127. break;
  128. default:
  129. throw new NotImplementedException(string.Format("Cannot use RangeAttribute on value of type: {0}", exp.valueType));
  130. }
  131. }
  132. else if (attribute is MinAttribute)
  133. {
  134. var minAttribute = (MinAttribute)attribute;
  135. switch (exp.valueType)
  136. {
  137. case VFXValueType.Int32:
  138. exp = new VFXExpressionMax(exp, VFXValue.Constant((int)minAttribute.min));
  139. break;
  140. case VFXValueType.Uint32:
  141. exp = new VFXExpressionMax(exp, VFXValue.Constant((uint)minAttribute.min));
  142. break;
  143. case VFXValueType.Float:
  144. case VFXValueType.Float2:
  145. case VFXValueType.Float3:
  146. case VFXValueType.Float4:
  147. exp = new VFXExpressionMax(exp, VFXOperatorUtility.CastFloat(VFXValue.Constant(minAttribute.min), exp.valueType));
  148. break;
  149. default:
  150. throw new NotImplementedException(string.Format("Cannot use MinAttribute on value of type: {0}", exp.valueType));
  151. }
  152. }
  153. else if (attribute is NormalizeAttribute)
  154. {
  155. exp = VFXOperatorUtility.Normalize(exp);
  156. }
  157. else if (attribute is EnumAttribute enumAttribute)
  158. {
  159. exp = new VFXExpressionMin(exp, VFXValue.Constant((uint)enumAttribute.values.Length - 1));
  160. }
  161. else if (attribute is MinMaxAttribute minMaxAttribute)
  162. {
  163. exp = VFXOperatorUtility.Clamp(exp, VFXValue.Constant(minMaxAttribute.min), VFXValue.Constant(minMaxAttribute.max));
  164. }
  165. else
  166. throw new NotImplementedException("Unrecognized expression attribute: " + attribute);
  167. }
  168. return exp;
  169. }
  170. public void ApplyToGUI(ref string label, ref string tooltip)
  171. {
  172. string tooltipAddon = "";
  173. if (m_AllAttributes != null)
  174. foreach (var attribute in m_AllAttributes)
  175. {
  176. if (attribute is MinAttribute)
  177. tooltipAddon += string.Format(CultureInfo.InvariantCulture, " (Min: {0})", ((MinAttribute)attribute).min);
  178. else if (attribute is NormalizeAttribute)
  179. tooltipAddon += " (Normalized)";
  180. else if (attribute is TooltipAttribute)
  181. tooltip = ((TooltipAttribute)attribute).tooltip;
  182. else if (attribute is AngleAttribute)
  183. tooltipAddon += " (Angle)";
  184. }
  185. if (string.IsNullOrEmpty(tooltip))
  186. tooltip = label;
  187. tooltip = tooltip + tooltipAddon;
  188. }
  189. public Vector2 FindRange()
  190. {
  191. if (Is(Type.Range))
  192. {
  193. var attribute = m_AllAttributes.OfType<RangeAttribute>().First();
  194. return new Vector2(attribute.min, attribute.max);
  195. }
  196. else if (Is(Type.Min))
  197. {
  198. var attribute = m_AllAttributes.OfType<MinAttribute>().First();
  199. return new Vector2(attribute.min, Mathf.Infinity);
  200. }
  201. else if (Is(Type.MinMax))
  202. {
  203. var attribute = m_AllAttributes.OfType<MinMaxAttribute>()
  204. .First();
  205. return new Vector2(attribute.min, attribute.max);
  206. }
  207. return Vector2.zero;
  208. }
  209. public string[] FindEnum()
  210. {
  211. if (Is(Type.Enum))
  212. {
  213. return m_AllAttributes.OfType<EnumAttribute>().First().values;
  214. }
  215. return null;
  216. }
  217. public string ApplyRegex(object obj)
  218. {
  219. if (Is(Type.Regex))
  220. {
  221. var attribute = m_AllAttributes.OfType<RegexAttribute>().First();
  222. string str = (string)obj;
  223. str = Regex.Replace(str, attribute.pattern, "");
  224. return str.Substring(0, Math.Min(str.Length, attribute.maxLength));
  225. }
  226. return null;
  227. }
  228. public bool Is(VFXPropertyAttributes.Type type)
  229. {
  230. return (m_Flag & type) == type;
  231. }
  232. public IReadOnlyCollection<Attribute> attributes => m_AllAttributes != null ? m_AllAttributes : new Attribute[0];
  233. private Attribute[] m_GraphAttributes;
  234. private Attribute[] m_AllAttributes;
  235. private Type m_Flag;
  236. }
  237. }