GizmosInstance.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.Rendering;
  6. using UnityEngine.SceneManagement;
  7. #if UNITY_EDITOR
  8. using UnityEditor;
  9. using UnityEditor.SceneManagement;
  10. #endif
  11. #if !UNITY_2019_1_OR_NEWER
  12. using System;
  13. public struct ScriptableRenderContext {}
  14. public static class RenderPipelineManager
  15. {
  16. public static event Action<ScriptableRenderContext, Camera> endCameraRendering;
  17. }
  18. #endif
  19. namespace Popcron
  20. {
  21. [ExecuteInEditMode]
  22. [AddComponentMenu("")]
  23. public class GizmosInstance : MonoBehaviour
  24. {
  25. private const int DefaultQueueSize = 4096;
  26. private static GizmosInstance instance;
  27. private static bool hotReloaded = true;
  28. private static Material defaultMaterial;
  29. private static Plane[] cameraPlanes = new Plane[6];
  30. private Material overrideMaterial;
  31. private int queueIndex = 0;
  32. private int lastFrame;
  33. private Element[] queue = new Element[DefaultQueueSize];
  34. /// <summary>
  35. /// The material being used to render
  36. /// </summary>
  37. public static Material Material
  38. {
  39. get
  40. {
  41. GizmosInstance inst = GetOrCreate();
  42. if (inst.overrideMaterial)
  43. {
  44. return inst.overrideMaterial;
  45. }
  46. return DefaultMaterial;
  47. }
  48. set
  49. {
  50. GizmosInstance inst = GetOrCreate();
  51. inst.overrideMaterial = value;
  52. }
  53. }
  54. /// <summary>
  55. /// The default line renderer material
  56. /// </summary>
  57. public static Material DefaultMaterial
  58. {
  59. get
  60. {
  61. if (!defaultMaterial)
  62. {
  63. // Unity has a built-in shader that is useful for drawing
  64. // simple colored things.
  65. Shader shader = Shader.Find("Hidden/Internal-Colored");
  66. defaultMaterial = new Material(shader)
  67. {
  68. hideFlags = HideFlags.HideAndDontSave
  69. };
  70. // Turn on alpha blending
  71. defaultMaterial.SetInt("_SrcBlend", (int)BlendMode.SrcAlpha);
  72. defaultMaterial.SetInt("_DstBlend", (int)BlendMode.OneMinusSrcAlpha);
  73. defaultMaterial.SetInt("_Cull", (int)CullMode.Off);
  74. defaultMaterial.SetInt("_ZWrite", 0);
  75. }
  76. return defaultMaterial;
  77. }
  78. }
  79. internal static GizmosInstance GetOrCreate()
  80. {
  81. if (hotReloaded || !instance)
  82. {
  83. bool markDirty = false;
  84. GizmosInstance[] gizmosInstances = FindObjectsOfType<GizmosInstance>();
  85. for (int i = 0; i < gizmosInstances.Length; i++)
  86. {
  87. instance = gizmosInstances[i];
  88. //destroy any extra gizmo instances
  89. if (i > 0)
  90. {
  91. if (Application.isPlaying)
  92. {
  93. Destroy(gizmosInstances[i]);
  94. }
  95. else
  96. {
  97. DestroyImmediate(gizmosInstances[i]);
  98. markDirty = true;
  99. }
  100. }
  101. }
  102. //none were found, create a new one
  103. if (!instance)
  104. {
  105. instance = new GameObject(typeof(GizmosInstance).FullName).AddComponent<GizmosInstance>();
  106. instance.gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
  107. markDirty = true;
  108. }
  109. #if UNITY_EDITOR
  110. //mark scene as dirty
  111. if (markDirty && !Application.isPlaying)
  112. {
  113. Scene scene = SceneManager.GetActiveScene();
  114. EditorSceneManager.MarkSceneDirty(scene);
  115. }
  116. #endif
  117. hotReloaded = false;
  118. }
  119. return instance;
  120. }
  121. private float CurrentTime
  122. {
  123. get
  124. {
  125. float time = 0f;
  126. if (Application.isPlaying)
  127. {
  128. time = Time.time;
  129. }
  130. else
  131. {
  132. #if UNITY_EDITOR
  133. time = (float)EditorApplication.timeSinceStartup;
  134. #endif
  135. }
  136. return time;
  137. }
  138. }
  139. /// <summary>
  140. /// Submits an array of points to draw into the queue.
  141. /// </summary>
  142. internal static void Submit(Vector3[] points, Color? color, bool dashed)
  143. {
  144. GizmosInstance inst = GetOrCreate();
  145. //if new frame, reset index
  146. if (inst.lastFrame != Time.frameCount)
  147. {
  148. inst.lastFrame = Time.frameCount;
  149. inst.queueIndex = 0;
  150. }
  151. //excedeed the length, so make it even bigger
  152. if (inst.queueIndex >= inst.queue.Length)
  153. {
  154. Element[] bigger = new Element[inst.queue.Length + DefaultQueueSize];
  155. for (int i = inst.queue.Length; i < bigger.Length; i++)
  156. {
  157. bigger[i] = new Element();
  158. }
  159. Array.Copy(inst.queue, 0, bigger, 0, inst.queue.Length);
  160. inst.queue = bigger;
  161. }
  162. inst.queue[inst.queueIndex].color = color ?? Color.white;
  163. inst.queue[inst.queueIndex].points = points;
  164. inst.queue[inst.queueIndex].dashed = dashed;
  165. inst.queueIndex++;
  166. }
  167. private void OnEnable()
  168. {
  169. //populate queue with empty elements
  170. queue = new Element[DefaultQueueSize];
  171. for (int i = 0; i < DefaultQueueSize; i++)
  172. {
  173. queue[i] = new Element();
  174. }
  175. if (GraphicsSettings.renderPipelineAsset == null)
  176. {
  177. Camera.onPostRender += OnRendered;
  178. }
  179. else
  180. {
  181. RenderPipelineManager.endCameraRendering += OnRendered;
  182. }
  183. }
  184. private void OnDisable()
  185. {
  186. if (GraphicsSettings.renderPipelineAsset == null)
  187. {
  188. Camera.onPostRender -= OnRendered;
  189. }
  190. else
  191. {
  192. RenderPipelineManager.endCameraRendering -= OnRendered;
  193. }
  194. }
  195. private void OnRendered(ScriptableRenderContext context, Camera camera) => OnRendered(camera);
  196. private bool ShouldRenderCamera(Camera camera)
  197. {
  198. if (!camera)
  199. {
  200. return false;
  201. }
  202. //allow the scene and main camera always
  203. bool isSceneCamera = false;
  204. #if UNITY_EDITOR
  205. SceneView sceneView = SceneView.currentDrawingSceneView;
  206. if (sceneView == null)
  207. {
  208. sceneView = SceneView.lastActiveSceneView;
  209. }
  210. if (sceneView != null && sceneView.camera == camera)
  211. {
  212. isSceneCamera = true;
  213. }
  214. #endif
  215. if (isSceneCamera || camera.CompareTag("MainCamera"))
  216. {
  217. return true;
  218. }
  219. //it passed through the filter
  220. if (Gizmos.CameraFilter?.Invoke(camera) == true)
  221. {
  222. return true;
  223. }
  224. return false;
  225. }
  226. private bool IsVisibleByCamera(Element points, Camera camera)
  227. {
  228. if (!camera)
  229. {
  230. return false;
  231. }
  232. //essentially check if at least 1 point is visible by the camera
  233. for (int i = 0; i < points.points.Length; i++)
  234. {
  235. Vector3 vp = camera.WorldToViewportPoint(points.points[i], camera.stereoActiveEye);
  236. if (vp.x >= 0 && vp.x <= 1 && vp.y >= 0 && vp.y <= 1)
  237. {
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. private void OnRendered(Camera camera)
  244. {
  245. //shouldnt be rendering
  246. if (!Gizmos.Enabled)
  247. {
  248. queueIndex = 0;
  249. }
  250. //check if this camera is ok to render with
  251. if (!ShouldRenderCamera(camera))
  252. {
  253. return;
  254. }
  255. Vector3 offset = Gizmos.Offset;
  256. Material.SetPass(Gizmos.Pass);
  257. GL.PushMatrix();
  258. GL.Begin(GL.LINES);
  259. bool alt = CurrentTime % 1 > 0.5f;
  260. float dashGap = Mathf.Clamp(Gizmos.DashGap, 0.01f, 32f);
  261. bool frustumCull = Gizmos.FrustumCulling;
  262. List<Vector3> points = new List<Vector3>();
  263. //draw le elements
  264. for (int e = 0; e < queueIndex; e++)
  265. {
  266. //just in case
  267. if (queue.Length <= e)
  268. {
  269. break;
  270. }
  271. Element element = queue[e];
  272. //dont render this thingy if its not inside the frustum
  273. if (frustumCull)
  274. {
  275. if (!IsVisibleByCamera(element, camera))
  276. {
  277. continue;
  278. }
  279. }
  280. points.Clear();
  281. if (element.dashed)
  282. {
  283. //subdivide
  284. for (int i = 0; i < element.points.Length - 1; i++)
  285. {
  286. Vector3 pointA = element.points[i];
  287. Vector3 pointB = element.points[i + 1];
  288. Vector3 direction = pointB - pointA;
  289. if (direction.sqrMagnitude > dashGap * dashGap * 2f)
  290. {
  291. float magnitude = direction.magnitude;
  292. int amount = Mathf.RoundToInt(magnitude / dashGap);
  293. direction /= magnitude;
  294. for (int p = 0; p < amount - 1; p++)
  295. {
  296. if (p % 2 == (alt ? 1 : 0))
  297. {
  298. float startLerp = p / (amount - 1f);
  299. float endLerp = (p + 1) / (amount - 1f);
  300. Vector3 start = Vector3.Lerp(pointA, pointB, startLerp);
  301. Vector3 end = Vector3.Lerp(pointA, pointB, endLerp);
  302. points.Add(start);
  303. points.Add(end);
  304. }
  305. }
  306. }
  307. else
  308. {
  309. points.Add(pointA);
  310. points.Add(pointB);
  311. }
  312. }
  313. }
  314. else
  315. {
  316. points.AddRange(element.points);
  317. }
  318. GL.Color(element.color);
  319. for (int i = 0; i < points.Count; i++)
  320. {
  321. GL.Vertex(points[i] + offset);
  322. }
  323. }
  324. GL.End();
  325. GL.PopMatrix();
  326. }
  327. }
  328. }