You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

415 lines
14 KiB

5 years ago
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Assertions;
  5. public class FogVolumePrimitiveManager : MonoBehaviour
  6. {
  7. public int CurrentPrimitiveCount { get; private set; }
  8. public int VisiblePrimitiveCount { get; private set; }
  9. public bool AlreadyUsesTransformForPoI { get { return m_pointOfInterestTf != null; } }
  10. public void FindPrimitivesInFogVolume()
  11. {
  12. CurrentPrimitiveCount = 0;
  13. VisiblePrimitiveCount = 0;
  14. m_primitives.Clear();
  15. m_primitivesInFrustum.Clear();
  16. for (int i = 0; i < MaxPrimitivesCount; i++)
  17. {
  18. m_primitives.Add(new PrimitiveData());
  19. m_primitivesInFrustum.Add(new PrimitiveData());
  20. }
  21. if (m_boxCollider == null) { m_boxCollider = gameObject.GetComponent<BoxCollider>(); }
  22. Bounds boundingBox = m_boxCollider.bounds;
  23. FogVolumePrimitive[] primitives = FindObjectsOfType<FogVolumePrimitive>();
  24. for (int i = 0; i < primitives.Length; i++)
  25. {
  26. var data = primitives[i];
  27. if (boundingBox.Intersects(data.Bounds))
  28. {
  29. if (data.BoxColl != null) { data.Type = EFogVolumePrimitiveType.Box; }
  30. else if (data.SphereColl != null) { data.Type = EFogVolumePrimitiveType.Sphere; }
  31. else
  32. {
  33. data.BoxColl = data.GetTransform.gameObject.AddComponent<BoxCollider>();
  34. data.Type = EFogVolumePrimitiveType.Box;
  35. }
  36. if (data.Type == EFogVolumePrimitiveType.Box)
  37. {
  38. AddPrimitiveBox(data);
  39. }
  40. else if (data.Type == EFogVolumePrimitiveType.Sphere)
  41. {
  42. AddPrimitiveSphere(data);
  43. }
  44. }
  45. }
  46. }
  47. public bool AddPrimitiveBox(FogVolumePrimitive _box)
  48. {
  49. Assert.IsTrue(CurrentPrimitiveCount < MaxPrimitivesCount,
  50. "The maximum amount of primitives is already reached!");
  51. int index = _FindFirstFreePrimitive();
  52. if (index != InvalidIndex)
  53. {
  54. PrimitiveData data = m_primitives[index];
  55. CurrentPrimitiveCount++;
  56. data.PrimitiveType = EFogVolumePrimitiveType.Box;
  57. data.Transform = _box.transform;
  58. data.Renderer = _box.GetComponent<Renderer>();
  59. data.Primitive = _box;
  60. data.Bounds = new Bounds(data.Transform.position, _box.GetPrimitiveScale);
  61. return true;
  62. }
  63. return false;
  64. }
  65. public bool AddPrimitiveSphere(FogVolumePrimitive _sphere)
  66. {
  67. Assert.IsTrue(CurrentPrimitiveCount < MaxPrimitivesCount,
  68. "The maximum amount of primitives is already reached!");
  69. int index = _FindFirstFreePrimitive();
  70. if (index != InvalidIndex)
  71. {
  72. PrimitiveData data = m_primitives[index];
  73. CurrentPrimitiveCount++;
  74. data.PrimitiveType = EFogVolumePrimitiveType.Sphere;
  75. data.Transform = _sphere.transform;
  76. data.Renderer = _sphere.GetComponent<Renderer>();
  77. data.Primitive = _sphere;
  78. data.Bounds = new Bounds(data.Transform.position, _sphere.GetPrimitiveScale);
  79. return true;
  80. }
  81. return false;
  82. }
  83. public bool RemovePrimitive(Transform _primitiveToRemove)
  84. {
  85. int count = m_primitives.Count;
  86. for (int i = 0; i < count; i++)
  87. {
  88. PrimitiveData data = m_primitives[i];
  89. if (ReferenceEquals(m_primitives[i].Transform, _primitiveToRemove))
  90. {
  91. data.Reset();
  92. CurrentPrimitiveCount--;
  93. return true;
  94. }
  95. }
  96. return false;
  97. }
  98. //=============================================================================================
  99. /**
  100. * @brief Sets the point of interest to a fixed position.
  101. *
  102. * @param _pointOfInterest The point that will be used for prioritizing which primitives need
  103. * to be rendered.
  104. *
  105. *********************************************************************************************/
  106. public void SetPointOfInterest(Vector3 _pointOfInterest)
  107. {
  108. m_pointOfInterestTf = null;
  109. m_pointOfInterest = _pointOfInterest;
  110. }
  111. //=============================================================================================
  112. /**
  113. * @brief Sets the point of interest to the specified transform.
  114. *
  115. * The point of interest will be updated from the position of the transform. It is therefore
  116. * not necessary to call this method more than once.
  117. *
  118. *********************************************************************************************/
  119. public void SetPointOfInterest(Transform _pointOfInterest)
  120. {
  121. Assert.IsTrue(_pointOfInterest != null, "_pointOfInterest must not be null!");
  122. m_pointOfInterestTf = _pointOfInterest;
  123. }
  124. public void OnDrawGizmos() { hideFlags = HideFlags.HideInInspector; }
  125. public void ManualUpdate(ref Plane[] _frustumPlanes)
  126. {
  127. m_camera = m_fogVolumeData != null ? m_fogVolumeData.GameCamera : null;
  128. if (m_camera == null) { return; }
  129. FrustumPlanes = _frustumPlanes;
  130. if (m_boxCollider == null) { m_boxCollider = m_fogVolume.GetComponent<BoxCollider>(); }
  131. _UpdateBounds();
  132. _FindPrimitivesInFrustum();
  133. if (m_primitivesInFrustum.Count > MaxVisiblePrimitives) { _SortPrimitivesInFrustum(); }
  134. _PrepareShaderArrays();
  135. }
  136. public void SetVisibility(bool _enabled)
  137. {
  138. int count = m_primitives.Count;
  139. for (int i = 0; i < count; i++)
  140. {
  141. if (m_primitives[i].Renderer != null) { m_primitives[i].Renderer.enabled = _enabled; }
  142. }
  143. }
  144. public void Initialize()
  145. {
  146. m_fogVolume = gameObject.GetComponent<FogVolume>();
  147. m_fogVolumeData = FindObjectOfType<FogVolumeData>();
  148. m_camera = null;
  149. m_boxCollider = null;
  150. CurrentPrimitiveCount = 0;
  151. if (m_primitives == null)
  152. {
  153. m_primitives = new List<PrimitiveData>(MaxPrimitivesCount);
  154. m_primitivesInFrustum = new List<PrimitiveData>();
  155. for (int i = 0; i < MaxPrimitivesCount; i++)
  156. {
  157. m_primitives.Add(new PrimitiveData());
  158. m_primitivesInFrustum.Add(new PrimitiveData());
  159. }
  160. }
  161. }
  162. public void Deinitialize() { VisiblePrimitiveCount = 0; }
  163. public Vector4[] GetPrimitivePositionArray() { return m_primitivePos; }
  164. public Vector4[] GetPrimitiveScaleArray() { return m_primitiveScale; }
  165. public Matrix4x4[] GetPrimitiveTransformArray() { return m_primitiveTf; }
  166. public Vector4[] GetPrimitiveDataArray() { return m_primitiveData; }
  167. private void _UpdateBounds()
  168. {
  169. int count = m_primitives.Count;
  170. for (int i = 0; i < count; i++)
  171. {
  172. PrimitiveData data = m_primitives[i];
  173. if (data.PrimitiveType == EFogVolumePrimitiveType.None) { continue; }
  174. if (data.Primitive == null)
  175. {
  176. RemovePrimitive(data.Transform);
  177. continue;
  178. }
  179. if (data.PrimitiveType == EFogVolumePrimitiveType.Box)
  180. {
  181. // Check if collider was removed by the user and add it again.
  182. if (data.Primitive.BoxColl == null)
  183. {
  184. Debug.LogWarning("FogVolumePrimitive requires a collider.\nThe collider will be automatically created.");
  185. data.Primitive.AddColliderIfNeccessary(EFogVolumePrimitiveType.Box);
  186. }
  187. data.Bounds = data.Primitive.BoxColl.bounds;
  188. }
  189. else if (data.PrimitiveType == EFogVolumePrimitiveType.Sphere)
  190. {
  191. // Check if collider was removed by the user and add it again.
  192. if (data.Primitive.SphereColl == null)
  193. {
  194. Debug.LogWarning("FogVolumePrimitive requires a collider.\nThe collider will be automatically created.");
  195. data.Primitive.AddColliderIfNeccessary(EFogVolumePrimitiveType.Sphere);
  196. }
  197. data.Bounds = data.Primitive.SphereColl.bounds;
  198. }
  199. }
  200. }
  201. private int _FindFirstFreePrimitive()
  202. {
  203. if (CurrentPrimitiveCount < MaxPrimitivesCount)
  204. {
  205. int count = m_primitives.Count;
  206. for (int i = 0; i < count; i++)
  207. {
  208. if (m_primitives[i].PrimitiveType == EFogVolumePrimitiveType.None) { return i; }
  209. }
  210. }
  211. return InvalidIndex;
  212. }
  213. private void _FindPrimitivesInFrustum()
  214. {
  215. m_inFrustumCount = 0;
  216. Vector3 cameraPos = m_camera.gameObject.transform.position;
  217. int count = m_primitives.Count;
  218. for (int i = 0; i < count; i++)
  219. {
  220. PrimitiveData primitive = m_primitives[i];
  221. if (primitive.Transform == null)
  222. {
  223. primitive.PrimitiveType = EFogVolumePrimitiveType.None;
  224. }
  225. if (primitive.PrimitiveType == EFogVolumePrimitiveType.None) { continue; }
  226. if (primitive.Primitive.IsPersistent)
  227. {
  228. Vector3 pos = primitive.Transform.position;
  229. primitive.SqDistance = (pos - m_pointOfInterest).sqrMagnitude;
  230. primitive.Distance2Camera = (pos - cameraPos).magnitude;
  231. m_primitivesInFrustum[m_inFrustumCount++] = primitive;
  232. }
  233. else if (GeometryUtility.TestPlanesAABB(FrustumPlanes, m_primitives[i].Bounds))
  234. {
  235. Vector3 pos = primitive.Transform.position;
  236. primitive.SqDistance = (pos - m_pointOfInterest).sqrMagnitude;
  237. primitive.Distance2Camera = (pos - cameraPos).magnitude;
  238. m_primitivesInFrustum[m_inFrustumCount++] = primitive;
  239. }
  240. }
  241. }
  242. private void _SortPrimitivesInFrustum()
  243. {
  244. bool finishedSorting = false;
  245. do
  246. {
  247. finishedSorting = true;
  248. for (int i = 0; i < m_inFrustumCount - 1; i++)
  249. {
  250. if (m_primitivesInFrustum[i].SqDistance > m_primitivesInFrustum[i + 1].SqDistance)
  251. {
  252. PrimitiveData tempData = m_primitivesInFrustum[i];
  253. m_primitivesInFrustum[i] = m_primitivesInFrustum[i + 1];
  254. m_primitivesInFrustum[i + 1] = tempData;
  255. finishedSorting = false;
  256. }
  257. }
  258. }
  259. while (!finishedSorting);
  260. }
  261. private void _PrepareShaderArrays()
  262. {
  263. VisiblePrimitiveCount = 0;
  264. Quaternion fogVolumeRotation = m_fogVolume.gameObject.transform.rotation;
  265. for (int i = 0; i < MaxVisiblePrimitives; i++)
  266. {
  267. if (i >= m_inFrustumCount) { break; }
  268. PrimitiveData data = m_primitivesInFrustum[i];
  269. Vector3 position = data.Transform.position;
  270. m_primitivePos[i] = gameObject.transform.InverseTransformPoint(position);
  271. m_primitiveTf[i].SetTRS(position,
  272. Quaternion.Inverse(data.Transform.rotation) * fogVolumeRotation,
  273. Vector3.one);
  274. m_primitiveScale[i] = data.Primitive.GetPrimitiveScale;
  275. m_primitiveData[i] =
  276. new Vector4(data.PrimitiveType == EFogVolumePrimitiveType.Box ? 0.5f : 1.5f,
  277. data.Primitive.IsSubtractive ? 1.5f : 0.5f,
  278. 0.0f,
  279. 0.0f);
  280. VisiblePrimitiveCount++;
  281. }
  282. }
  283. #if UNITY_EDITOR
  284. private void Update() { hideFlags = HideFlags.HideInInspector; }
  285. #endif
  286. private FogVolume m_fogVolume = null;
  287. private FogVolumeData m_fogVolumeData = null;
  288. private Camera m_camera = null;
  289. private BoxCollider m_boxCollider = null;
  290. private Transform m_pointOfInterestTf = null;
  291. private Vector3 m_pointOfInterest = Vector3.zero;
  292. private List<PrimitiveData> m_primitives = null;
  293. private List<PrimitiveData> m_primitivesInFrustum = null;
  294. private int m_inFrustumCount = 0;
  295. private Plane[] FrustumPlanes = null;
  296. private readonly Vector4[] m_primitivePos = new Vector4[MaxVisiblePrimitives];
  297. private readonly Vector4[] m_primitiveScale = new Vector4[MaxVisiblePrimitives];
  298. private readonly Matrix4x4[] m_primitiveTf = new Matrix4x4[MaxVisiblePrimitives];
  299. private readonly Vector4[] m_primitiveData = new Vector4[MaxVisiblePrimitives];
  300. private const int InvalidIndex = -1;
  301. private const int MaxVisiblePrimitives = 20;
  302. private const int MaxPrimitivesCount = 1000;
  303. protected class PrimitiveData
  304. {
  305. public PrimitiveData()
  306. {
  307. PrimitiveType = EFogVolumePrimitiveType.None;
  308. Primitive = null;
  309. Transform = null;
  310. Renderer = null;
  311. SqDistance = 0.0f;
  312. Distance2Camera = 0.0f;
  313. Bounds = new Bounds();
  314. }
  315. public EFogVolumePrimitiveType PrimitiveType { get; set; }
  316. public FogVolumePrimitive Primitive { get; set; }
  317. public Transform Transform { get; set; }
  318. public Renderer Renderer { get; set; }
  319. public float SqDistance { get; set; }
  320. public float Distance2Camera { get; set; }
  321. public Bounds Bounds { get; set; }
  322. public void Reset()
  323. {
  324. PrimitiveType = EFogVolumePrimitiveType.None;
  325. Primitive = null;
  326. Transform = null;
  327. Renderer = null;
  328. SqDistance = 0.0f;
  329. Distance2Camera = 0.0f;
  330. }
  331. }
  332. }