DynamicOcclusionAbstractBase.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. using UnityEngine;
  2. using UnityEngine.Serialization;
  3. namespace VLB
  4. {
  5. [AddComponentMenu("")] // hide it from Component search
  6. [DisallowMultipleComponent]
  7. [RequireComponent(typeof(VolumetricLightBeam))]
  8. public abstract class DynamicOcclusionAbstractBase : MonoBehaviour
  9. {
  10. /// <summary>
  11. /// How often will the occlusion be processed?
  12. /// Try to update the occlusion as rarely as possible to keep good performance.
  13. /// </summary>
  14. public DynamicOcclusionUpdateRate updateRate = Consts.DynamicOcclusionUpdateRateDefault;
  15. /// <summary>
  16. /// How many frames we wait between 2 occlusion tests?
  17. /// If you want your beam to be super responsive to the changes of your environment, update it every frame by setting 1.
  18. /// If you want to save on performance, we recommend to wait few frames between each update by setting a higher value.
  19. /// </summary>
  20. [FormerlySerializedAs("waitFrameCount")]
  21. public int waitXFrames = Consts.DynOcclusionWaitFramesCountDefault;
  22. /// <summary>
  23. /// Manually process the occlusion.
  24. /// You have to call this function in order to update the occlusion when using DynamicOcclusionUpdateRate.Never.
  25. /// </summary>
  26. public void ProcessOcclusionManually() { ProcessOcclusion(ProcessOcclusionSource.User); }
  27. public event System.Action onOcclusionProcessed;
  28. public static bool _INTERNAL_ApplyRandomFrameOffset = true;
  29. protected enum ProcessOcclusionSource
  30. {
  31. RenderLoop,
  32. OnEnable,
  33. EditorUpdate,
  34. User,
  35. }
  36. protected void ProcessOcclusion(ProcessOcclusionSource source)
  37. {
  38. if (!Config.Instance.featureEnabledDynamicOcclusion)
  39. return;
  40. Debug.Assert(!Application.isPlaying || m_LastFrameRendered != Time.frameCount, "ProcessOcclusion has been called twice on the same frame, which is forbidden");
  41. Debug.Assert(m_Master);
  42. bool occlusionSuccess = OnProcessOcclusion(source);
  43. if(onOcclusionProcessed != null)
  44. onOcclusionProcessed();
  45. if (m_Master)
  46. m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), occlusionSuccess ? OnModifyMaterialCallback : (MaterialModifier.Callback)(null));
  47. if (updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
  48. m_TransformPacked = transform.GetWorldPacked();
  49. bool firstTime = m_LastFrameRendered < 0;
  50. m_LastFrameRendered = Time.frameCount;
  51. if (firstTime && _INTERNAL_ApplyRandomFrameOffset)
  52. {
  53. m_LastFrameRendered += Random.Range(0, waitXFrames); // add a random offset to prevent from updating texture for all beams having the same wait value
  54. }
  55. }
  56. TransformUtils.Packed m_TransformPacked = null;
  57. int m_LastFrameRendered = int.MinValue;
  58. public int _INTERNAL_LastFrameRendered { get { return m_LastFrameRendered; } } // for unit tests
  59. protected VolumetricLightBeam m_Master = null;
  60. protected abstract string GetShaderKeyword();
  61. protected abstract MaterialManager.DynamicOcclusion GetDynamicOcclusionMode();
  62. protected abstract bool OnProcessOcclusion(ProcessOcclusionSource source);
  63. protected abstract void OnModifyMaterialCallback(MaterialModifier.Interface owner);
  64. protected abstract void OnEnablePostValidate();
  65. protected virtual void OnValidateProperties()
  66. {
  67. waitXFrames = Mathf.Clamp(waitXFrames, 1, 60);
  68. }
  69. protected virtual void Awake()
  70. {
  71. m_Master = GetComponent<VolumetricLightBeam>();
  72. Debug.Assert(m_Master);
  73. m_Master._INTERNAL_DynamicOcclusionMode = GetDynamicOcclusionMode();
  74. }
  75. protected virtual void OnDestroy()
  76. {
  77. m_Master._INTERNAL_DynamicOcclusionMode = MaterialManager.DynamicOcclusion.Off;
  78. DisableOcclusion();
  79. }
  80. protected virtual void OnEnable()
  81. {
  82. OnValidateProperties();
  83. OnEnablePostValidate();
  84. #if UNITY_EDITOR
  85. if (Application.isPlaying)
  86. #endif
  87. {
  88. m_Master.onWillCameraRenderThisBeam += OnWillCameraRender;
  89. if (!updateRate.HasFlag(DynamicOcclusionUpdateRate.Never))
  90. m_Master.RegisterOnBeamGeometryInitializedCallback(() => ProcessOcclusion(ProcessOcclusionSource.OnEnable));
  91. }
  92. }
  93. protected virtual void OnDisable()
  94. {
  95. #if UNITY_EDITOR
  96. if (Application.isPlaying)
  97. #endif
  98. {
  99. m_Master.onWillCameraRenderThisBeam -= OnWillCameraRender;
  100. }
  101. DisableOcclusion();
  102. }
  103. #if UNITY_EDITOR
  104. protected virtual void OnValidate()
  105. {
  106. OnValidateProperties();
  107. }
  108. #endif
  109. void OnWillCameraRender(Camera cam)
  110. {
  111. Debug.Assert(Application.isPlaying);
  112. if (cam != null && cam.enabled
  113. && Time.frameCount != m_LastFrameRendered) // prevent from updating multiple times if there are more than 1 camera
  114. {
  115. bool shouldUpdate = false;
  116. if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
  117. {
  118. if (!transform.IsSame(m_TransformPacked))
  119. shouldUpdate = true;
  120. }
  121. if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.EveryXFrames))
  122. {
  123. if (Time.frameCount >= m_LastFrameRendered + waitXFrames)
  124. shouldUpdate = true;
  125. }
  126. if (shouldUpdate)
  127. ProcessOcclusion(ProcessOcclusionSource.RenderLoop);
  128. }
  129. }
  130. void DisableOcclusion()
  131. {
  132. m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), null);
  133. }
  134. }
  135. }