Utils.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using System;
  2. using UnityEngine;
  3. namespace VLB
  4. {
  5. public static class Utils
  6. {
  7. public static float ComputeConeRadiusEnd(float fallOffEnd, float spotAngle)
  8. {
  9. return fallOffEnd * Mathf.Tan(spotAngle * Mathf.Deg2Rad * 0.5f);
  10. }
  11. public static void Swap<T>(ref T a, ref T b)
  12. {
  13. var temp = a;
  14. a = b;
  15. b = temp;
  16. }
  17. public static string GetPath(Transform current)
  18. {
  19. if (current.parent == null)
  20. return "/" + current.name;
  21. return GetPath(current.parent) + "/" + current.name;
  22. }
  23. public static T NewWithComponent<T>(string name) where T : Component
  24. {
  25. return (new GameObject(name, typeof(T))).GetComponent<T>();
  26. }
  27. public static T GetOrAddComponent<T>(this GameObject self) where T : Component
  28. {
  29. var component = self.GetComponent<T>();
  30. if (component == null)
  31. component = self.AddComponent<T>();
  32. return component;
  33. }
  34. public static T GetOrAddComponent<T>(this MonoBehaviour self) where T : Component
  35. {
  36. return self.gameObject.GetOrAddComponent<T>();
  37. }
  38. /// <summary>
  39. /// true if the bit field or bit fields that are set in flags are also set in the current instance; otherwise, false.
  40. /// </summary>
  41. public static bool HasFlag(this Enum mask, Enum flags) // Same behavior than Enum.HasFlag is .NET 4
  42. {
  43. #if DEBUG
  44. if (mask.GetType() != flags.GetType())
  45. throw new System.ArgumentException(string.Format("The argument type, '{0}', is not the same as the enum type '{1}'.", flags.GetType(), mask.GetType()));
  46. #endif
  47. return ((int)(IConvertible)mask & (int)(IConvertible)flags) == (int)(IConvertible)flags;
  48. }
  49. public static Vector2 xy(this Vector3 aVector) { return new Vector2(aVector.x, aVector.y); }
  50. public static Vector2 xz(this Vector3 aVector) { return new Vector2(aVector.x, aVector.z); }
  51. public static Vector2 yz(this Vector3 aVector) { return new Vector2(aVector.y, aVector.z); }
  52. public static Vector2 yx(this Vector3 aVector) { return new Vector2(aVector.y, aVector.x); }
  53. public static Vector2 zx(this Vector3 aVector) { return new Vector2(aVector.z, aVector.x); }
  54. public static Vector2 zy(this Vector3 aVector) { return new Vector2(aVector.z, aVector.y); }
  55. public static bool Approximately(this Vector2 a, Vector2 b, float epsilon = 0.00001f) { return Vector2.SqrMagnitude(a - b) < epsilon; }
  56. public static bool Approximately(this Vector3 a, Vector3 b, float epsilon = 0.00001f) { return Vector3.SqrMagnitude(a - b) < epsilon; }
  57. public static bool Approximately(this Vector4 a, Vector4 b, float epsilon = 0.00001f) { return Vector4.SqrMagnitude(a - b) < epsilon; }
  58. public static Vector4 AsVector4(this Vector3 vec3, float w) { return new Vector4(vec3.x, vec3.y, vec3.z, w); }
  59. public static Vector4 PlaneEquation(Vector3 normalizedNormal, Vector3 pt) { return normalizedNormal.AsVector4(-Vector3.Dot(normalizedNormal, pt)); }
  60. public static float GetVolumeCubic(this Bounds self) { return self.size.x * self.size.y * self.size.z; }
  61. public static float GetMaxArea2D(this Bounds self) { return Mathf.Max(Mathf.Max(self.size.x * self.size.y, self.size.y * self.size.z), self.size.x * self.size.z); }
  62. public static Color Opaque(this Color self) { return new Color(self.r, self.g, self.b, 1f); }
  63. #if UNITY_EDITOR
  64. public static void GizmosDrawPlane(Vector3 normal, Vector3 position, Color color, Matrix4x4 mat, float size = 1f, float normalLength = 0.0f)
  65. {
  66. normal = normal.normalized;
  67. var prevMat = UnityEditor.Handles.matrix;
  68. var prevColor = UnityEditor.Handles.color;
  69. UnityEditor.Handles.matrix = mat;
  70. UnityEditor.Handles.color = color;
  71. UnityEditor.Handles.RectangleHandleCap(0, position, Quaternion.LookRotation(normal), size, EventType.Repaint);
  72. if (normalLength > 0.0f)
  73. {
  74. UnityEditor.Handles.DrawLine(position, position + normal * normalLength);
  75. UnityEditor.Handles.ConeHandleCap(0, position + normal * normalLength, Quaternion.LookRotation(normal), normalLength * 0.25f, EventType.Repaint);
  76. }
  77. UnityEditor.Handles.matrix = prevMat;
  78. UnityEditor.Handles.color = prevColor;
  79. }
  80. #endif // UNITY_EDITOR
  81. // Plane.Translate is not available in Unity 5
  82. public static Plane TranslateCustom(this Plane plane, Vector3 translation)
  83. {
  84. plane.distance += Vector3.Dot(translation.normalized, plane.normal) * translation.magnitude;
  85. return plane;
  86. }
  87. // Plane.ClosestPointOnPlaneCustom is not available in Unity 5
  88. public static Vector3 ClosestPointOnPlaneCustom(this Plane plane, Vector3 point)
  89. {
  90. return point - plane.GetDistanceToPoint(point) * plane.normal;
  91. }
  92. public static bool IsAlmostZero(float f) { return Mathf.Abs(f) < 0.001f; }
  93. public static bool IsValid(this Plane plane)
  94. {
  95. return plane.normal.sqrMagnitude > 0.5f;
  96. }
  97. public static void SetKeywordEnabled(this Material mat, string name, bool enabled)
  98. {
  99. if(enabled) mat.EnableKeyword(name);
  100. else mat.DisableKeyword(name);
  101. }
  102. public static void SetShaderKeywordEnabled(string name, bool enabled)
  103. {
  104. if (enabled) Shader.EnableKeyword(name);
  105. else Shader.DisableKeyword(name);
  106. }
  107. public static Matrix4x4 SampleInMatrix(this Gradient self, int floatPackingPrecision)
  108. {
  109. const int kSamplesCount = 16;
  110. var mat = new Matrix4x4();
  111. for (int i = 0; i < kSamplesCount; ++i)
  112. {
  113. var color = self.Evaluate(Mathf.Clamp01((float)(i) / (kSamplesCount - 1)));
  114. mat[i] = color.PackToFloat(floatPackingPrecision);
  115. }
  116. return mat;
  117. }
  118. public static Color[] SampleInArray(this Gradient self, int samplesCount)
  119. {
  120. var array = new Color[samplesCount];
  121. for (int i = 0; i < samplesCount; ++i)
  122. array[i] = self.Evaluate(Mathf.Clamp01((float)(i) / (samplesCount - 1)));
  123. return array;
  124. }
  125. static Vector4 Vector4_Floor(Vector4 vec) { return new Vector4(Mathf.Floor(vec.x), Mathf.Floor(vec.y), Mathf.Floor(vec.z), Mathf.Floor(vec.w)); }
  126. public static float PackToFloat(this Color color, int floatPackingPrecision)
  127. {
  128. Vector4 iVal = Vector4_Floor(color * (floatPackingPrecision - 1));
  129. float output = 0;
  130. output += iVal.x * floatPackingPrecision * floatPackingPrecision * floatPackingPrecision;
  131. output += iVal.y * floatPackingPrecision * floatPackingPrecision;
  132. output += iVal.z * floatPackingPrecision;
  133. output += iVal.w;
  134. return output;
  135. }
  136. public enum FloatPackingPrecision { High = 64, Low = 8, Undef = 0 }
  137. static FloatPackingPrecision ms_FloatPackingPrecision = FloatPackingPrecision.Undef;
  138. // OpenGL ES 2.0 GPU (graphicsShaderLevel = 30) usually have low float precision (16 bits on fragments)
  139. // So we lower the float packing precision on them (8 seems fine on Adreno (TM) 220, NVIDIA Tegra 3 and on Mali-450 MP)
  140. // https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
  141. const int kFloatPackingHighMinShaderLevel = 35;
  142. public static FloatPackingPrecision GetFloatPackingPrecision()
  143. {
  144. if (ms_FloatPackingPrecision == FloatPackingPrecision.Undef)
  145. {
  146. ms_FloatPackingPrecision = SystemInfo.graphicsShaderLevel >= kFloatPackingHighMinShaderLevel ? FloatPackingPrecision.High : FloatPackingPrecision.Low;
  147. }
  148. return ms_FloatPackingPrecision;
  149. }
  150. public static void MarkCurrentSceneDirty()
  151. {
  152. #if UNITY_EDITOR
  153. if (!Application.isPlaying)
  154. {
  155. #if UNITY_5_3_OR_NEWER
  156. UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
  157. #else
  158. UnityEditor.EditorApplication.MarkSceneDirty();
  159. #endif
  160. }
  161. #endif
  162. }
  163. public static void MarkObjectDirty(UnityEngine.Object obj)
  164. {
  165. #if UNITY_EDITOR
  166. if (!Application.isPlaying)
  167. {
  168. UnityEditor.EditorUtility.SetDirty(obj);
  169. }
  170. #endif
  171. }
  172. #if UNITY_EDITOR
  173. public static bool IsEditorCamera(Camera cam)
  174. {
  175. var sceneView = UnityEditor.SceneView.currentDrawingSceneView;
  176. if (sceneView)
  177. {
  178. return cam == sceneView.camera;
  179. }
  180. return false;
  181. }
  182. public static void SetSameSceneVisibilityStatesThan(this GameObject self, GameObject model)
  183. {
  184. // SceneVisibilityManager is a feature available from 2019.2, but fixed for transient objects only from 2019.3.14f1
  185. // https://issuetracker.unity3d.com/issues/toggling-of-picking-and-visibility-flags-of-a-gameobject-is-ignored-when-gameobject-dot-hideflags-is-set-to-hideflags-dot-dontsave
  186. #if UNITY_2019_3_OR_NEWER
  187. bool pickingDisabled = UnityEditor.SceneVisibilityManager.instance.IsPickingDisabled(model);
  188. if (pickingDisabled) UnityEditor.SceneVisibilityManager.instance.DisablePicking(self, true);
  189. else UnityEditor.SceneVisibilityManager.instance.EnablePicking(self, true);
  190. bool hidden = UnityEditor.SceneVisibilityManager.instance.IsHidden(model);
  191. if (hidden) UnityEditor.SceneVisibilityManager.instance.Hide(self, true);
  192. else UnityEditor.SceneVisibilityManager.instance.Show(self, true);
  193. #endif
  194. }
  195. #endif
  196. }
  197. }