using System; using UnityEngine; namespace Popcron { public class Gizmos { private static string _prefsKey = null; private static int? _bufferSize = null; private static bool? _enabled = null; private static float? _dashGap = null; private static bool? _cull = null; private static int? _pass = null; private static Vector3? _offset = null; private static Vector3[] buffer = new Vector3[BufferSize]; /// /// By default, it will always render to scene view camera and the main camera. /// Subscribing to this allows you to whitelist your custom cameras. /// public static Func CameraFilter = cam => false; private static string PrefsKey { get { if (string.IsNullOrEmpty(_prefsKey)) { _prefsKey = $"{SystemInfo.deviceUniqueIdentifier}.{Application.companyName}.{Application.productName}.{Constants.UniqueIdentifier}"; } return _prefsKey; } } /// /// The size of the total gizmos buffer. /// Default is 4096. /// public static int BufferSize { get { if (_bufferSize == null) { _bufferSize = PlayerPrefs.GetInt($"{PrefsKey}.BufferSize", 4096); } return _bufferSize.Value; } set { value = Mathf.Clamp(value, 0, int.MaxValue); if (_bufferSize != value) { _bufferSize = value; PlayerPrefs.SetInt($"{PrefsKey}.BufferSize", value); //buffer size changed, so recreate the buffer array too buffer = new Vector3[value]; } } } /// /// Toggles wether the gizmos could be drawn or not. /// public static bool Enabled { get { if (_enabled == null) { _enabled = PlayerPrefs.GetInt($"{PrefsKey}.Enabled", 1) == 1; } return _enabled.Value; } set { if (_enabled != value) { _enabled = value; PlayerPrefs.SetInt($"{PrefsKey}.Enabled", value ? 1 : 0); } } } /// /// The size of the gap when drawing dashed elements. /// Default gap size is 0.1 /// public static float DashGap { get { if (_dashGap == null) { _dashGap = PlayerPrefs.GetFloat($"{PrefsKey}.DashGap", 0.1f); } return _dashGap.Value; } set { if (_dashGap != value) { _dashGap = value; PlayerPrefs.SetFloat($"{PrefsKey}.DashGap", value); } } } [Obsolete("This property is obsolete. Use FrustumCulling instead.", false)] public static bool Cull { get { return FrustumCulling; } set { FrustumCulling = value; } } [Obsolete("This property is obsolete. Subscribe to CameraFilter predicate instead and return true for your custom camera.", false)] public static Camera Camera { get => null; set { } } /// /// Should the camera not draw elements that are not visible? /// public static bool FrustumCulling { get { if (_cull == null) { _cull = PlayerPrefs.GetInt($"{PrefsKey}.FrustumCulling", 1) == 1; } return _cull.Value; } set { if (_cull != value) { _cull = value; PlayerPrefs.SetInt($"{PrefsKey}.FrustumCulling", value ? 1 : 0); } } } /// /// The material being used to render. /// public static Material Material { get => GizmosInstance.Material; set => GizmosInstance.Material = value; } /// /// Rendering pass to activate. /// public static int Pass { get { if (_pass == null) { _pass = PlayerPrefs.GetInt($"{PrefsKey}.Pass", 0); } return _pass.Value; } set { if (_pass != value) { _pass = value; PlayerPrefs.SetInt($"{PrefsKey}.Pass", value); } } } /// /// Global offset for all points. Default is (0, 0, 0). /// public static Vector3 Offset { get { const string Delim = ","; if (_offset == null) { string data = PlayerPrefs.GetString($"{PrefsKey}.Offset", 0 + Delim + 0 + Delim + 0); int indexOf = data.IndexOf(Delim); int lastIndexOf = data.LastIndexOf(Delim); if (indexOf + lastIndexOf > 0) { string[] arr = data.Split(Delim[0]); _offset = new Vector3(float.Parse(arr[0]), float.Parse(arr[1]), float.Parse(arr[2])); } else { return Vector3.zero; } } return _offset.Value; } set { const string Delim = ","; if (_offset != value) { _offset = value; PlayerPrefs.SetString($"{PrefsKey}.Offset", value.x + Delim + value.y + Delim + value.y); } } } /// /// Draws an element onto the screen. /// public static void Draw(Color? color, bool dashed, params object[] args) where T : Drawer { if (!Enabled) { return; } Drawer drawer = Drawer.Get(); if (drawer != null) { int points = drawer.Draw(ref buffer, args); //copy from buffer and add to the queue Vector3[] array = new Vector3[points]; Array.Copy(buffer, array, points); GizmosInstance.Submit(array, color, dashed); } } /// /// Draws an array of lines. Useful for things like paths. /// public static void Lines(Vector3[] lines, Color? color = null, bool dashed = false) { if (!Enabled) { return; } GizmosInstance.Submit(lines, color, dashed); } /// /// Draw line in world space. /// public static void Line(Vector3 a, Vector3 b, Color? color = null, bool dashed = false) { Draw(color, dashed, a, b); } /// /// Draw square in world space. /// public static void Square(Vector2 position, Vector2 size, Color? color = null, bool dashed = false) { Square(position, Quaternion.identity, size, color, dashed); } /// /// Draw square in world space with float diameter parameter. /// public static void Square(Vector2 position, float diameter, Color? color = null, bool dashed = false) { Square(position, Quaternion.identity, Vector2.one * diameter, color, dashed); } /// /// Draw square in world space with a rotation parameter. /// public static void Square(Vector2 position, Quaternion rotation, Vector2 size, Color? color = null, bool dashed = false) { Draw(color, dashed, position, rotation, size); } /// /// Draws a cube in world space. /// public static void Cube(Vector3 position, Quaternion rotation, Vector3 size, Color? color = null, bool dashed = false) { Draw(color, dashed, position, rotation, size); } /// /// Draws a rectangle in screen space. /// public static void Rect(Rect rect, Camera camera, Color? color = null, bool dashed = false) { rect.y = Screen.height - rect.y; Vector2 corner = camera.ScreenToWorldPoint(new Vector2(rect.x, rect.y - rect.height)); Draw(color, dashed, corner + rect.size * 0.5f, Quaternion.identity, rect.size); } /// /// Draws a representation of a bounding box. /// public static void Bounds(Bounds bounds, Color? color = null, bool dashed = false) { Draw(color, dashed, bounds.center, Quaternion.identity, bounds.size); } /// /// Draws a cone similar to the one that spot lights draw. /// public static void Cone(Vector3 position, Quaternion rotation, float length, float angle, Color? color = null, bool dashed = false, int pointsCount = 16) { //draw the end of the cone float endAngle = Mathf.Tan(angle * 0.5f * Mathf.Deg2Rad) * length; Vector3 forward = rotation * Vector3.forward; Vector3 endPosition = position + forward * length; float offset = 0f; Draw(color, dashed, endPosition, pointsCount, endAngle, offset, rotation); //draw the 4 lines for (int i = 0; i < 4; i++) { float a = i * 90f * Mathf.Deg2Rad; Vector3 point = rotation * new Vector3(Mathf.Cos(a), Mathf.Sin(a)) * endAngle; Line(position, position + point + forward * length, color, dashed); } } /// /// Draws a sphere at position with specified radius. /// public static void Sphere(Vector3 position, float radius, Color? color = null, bool dashed = false, int pointsCount = 16) { float offset = 0f; Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 0f, 0f)); Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(90f, 0f, 0f)); Draw(color, dashed, position, pointsCount, radius, offset, Quaternion.Euler(0f, 90f, 90f)); } /// /// Draws a circle in world space and billboards towards the camera. /// public static void Circle(Vector3 position, float radius, Camera camera, Color? color = null, bool dashed = false, int pointsCount = 16) { float offset = 0f; Quaternion rotation = Quaternion.LookRotation(position - camera.transform.position); Draw(color, dashed, position, pointsCount, radius, offset, rotation); } /// /// Draws a circle in world space with a specified rotation. /// public static void Circle(Vector3 position, float radius, Quaternion rotation, Color? color = null, bool dashed = false, int pointsCount = 16) { float offset = 0f; Draw(color, dashed, position, pointsCount, radius, offset, rotation); } } }