ECM2
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VLB
|
||||
{
|
||||
[AddComponentMenu("")] // hide it from Component search
|
||||
[DisallowMultipleComponent]
|
||||
[RequireComponent(typeof(VolumetricLightBeamSD))]
|
||||
public abstract class DynamicOcclusionAbstractBase : MonoBehaviour
|
||||
{
|
||||
public const string ClassName = "DynamicOcclusionAbstractBase";
|
||||
|
||||
/// <summary>
|
||||
/// How often will the occlusion be processed?
|
||||
/// Try to update the occlusion as rarely as possible to keep good performance.
|
||||
/// </summary>
|
||||
public DynamicOcclusionUpdateRate updateRate = Consts.DynOcclusion.UpdateRateDefault;
|
||||
|
||||
/// <summary>
|
||||
/// How many frames we wait between 2 occlusion tests?
|
||||
/// If you want your beam to be super responsive to the changes of your environment, update it every frame by setting 1.
|
||||
/// If you want to save on performance, we recommend to wait few frames between each update by setting a higher value.
|
||||
/// </summary>
|
||||
[FormerlySerializedAs("waitFrameCount")]
|
||||
public int waitXFrames = Consts.DynOcclusion.WaitFramesCountDefault;
|
||||
|
||||
/// <summary>
|
||||
/// Manually process the occlusion.
|
||||
/// You have to call this function in order to update the occlusion when using DynamicOcclusionUpdateRate.Never.
|
||||
/// </summary>
|
||||
public void ProcessOcclusionManually() { ProcessOcclusion(ProcessOcclusionSource.User); }
|
||||
|
||||
public event System.Action onOcclusionProcessed;
|
||||
|
||||
public static bool _INTERNAL_ApplyRandomFrameOffset = true;
|
||||
|
||||
|
||||
protected enum ProcessOcclusionSource
|
||||
{
|
||||
RenderLoop,
|
||||
OnEnable,
|
||||
EditorUpdate,
|
||||
User,
|
||||
}
|
||||
|
||||
protected void ProcessOcclusion(ProcessOcclusionSource source)
|
||||
{
|
||||
if (!Config.Instance.featureEnabledDynamicOcclusion)
|
||||
return;
|
||||
|
||||
if (m_LastFrameRendered == Time.frameCount && Application.isPlaying && source == ProcessOcclusionSource.OnEnable)
|
||||
return; // allow to call ProcessOcclusion from OnEnable (when disabling/enabling multiple a beam on the same frame) without generating an error
|
||||
|
||||
Debug.Assert(!Application.isPlaying || m_LastFrameRendered != Time.frameCount, "ProcessOcclusion has been called twice on the same frame, which is forbidden");
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
bool occlusionSuccess = OnProcessOcclusion(source);
|
||||
|
||||
if(onOcclusionProcessed != null)
|
||||
onOcclusionProcessed();
|
||||
|
||||
if (m_Master)
|
||||
{
|
||||
Debug.Assert(m_MaterialModifierCallbackCached != null);
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), occlusionSuccess ? m_MaterialModifierCallbackCached : (MaterialModifier.Callback)(null));
|
||||
}
|
||||
|
||||
if (updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
|
||||
m_TransformPacked = transform.GetWorldPacked();
|
||||
|
||||
bool firstTime = m_LastFrameRendered < 0;
|
||||
m_LastFrameRendered = Time.frameCount;
|
||||
|
||||
if (firstTime && _INTERNAL_ApplyRandomFrameOffset)
|
||||
{
|
||||
m_LastFrameRendered += Random.Range(0, waitXFrames); // add a random offset to prevent from updating texture for all beams having the same wait value
|
||||
}
|
||||
}
|
||||
|
||||
TransformUtils.Packed m_TransformPacked;
|
||||
int m_LastFrameRendered = int.MinValue;
|
||||
public int _INTERNAL_LastFrameRendered { get { return m_LastFrameRendered; } } // for unit tests
|
||||
protected VolumetricLightBeamSD m_Master = null;
|
||||
protected MaterialModifier.Callback m_MaterialModifierCallbackCached = null;
|
||||
|
||||
protected abstract string GetShaderKeyword();
|
||||
protected abstract MaterialManager.SD.DynamicOcclusion GetDynamicOcclusionMode();
|
||||
|
||||
protected abstract bool OnProcessOcclusion(ProcessOcclusionSource source);
|
||||
protected abstract void OnModifyMaterialCallback(MaterialModifier.Interface owner);
|
||||
protected abstract void OnEnablePostValidate();
|
||||
|
||||
|
||||
protected virtual void OnValidateProperties()
|
||||
{
|
||||
waitXFrames = Mathf.Clamp(waitXFrames, 1, 60);
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
m_Master = GetComponent<VolumetricLightBeamSD>();
|
||||
Debug.Assert(m_Master);
|
||||
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = GetDynamicOcclusionMode();
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
m_Master._INTERNAL_DynamicOcclusionMode = MaterialManager.SD.DynamicOcclusion.Off;
|
||||
DisableOcclusion();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
// cache the delegate to prevent from being inlined as '() => OnModifyMaterialCallback' when calling _INTERNAL_SetDynamicOcclusionCallback and from generating GC garbage
|
||||
m_MaterialModifierCallbackCached = OnModifyMaterialCallback;
|
||||
|
||||
OnValidateProperties();
|
||||
|
||||
OnEnablePostValidate();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
m_Master.onWillCameraRenderThisBeam += OnWillCameraRender;
|
||||
|
||||
if (!updateRate.HasFlag(DynamicOcclusionUpdateRate.Never))
|
||||
m_Master.RegisterOnBeamGeometryInitializedCallback(() => ProcessOcclusion(ProcessOcclusionSource.OnEnable));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
#endif
|
||||
{
|
||||
m_Master.onWillCameraRenderThisBeam -= OnWillCameraRender;
|
||||
}
|
||||
|
||||
DisableOcclusion();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
OnValidateProperties();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void OnWillCameraRender(Camera cam)
|
||||
{
|
||||
Debug.Assert(Application.isPlaying);
|
||||
|
||||
if (cam != null && cam.enabled
|
||||
&& Time.frameCount != m_LastFrameRendered) // prevent from updating multiple times if there are more than 1 camera
|
||||
{
|
||||
bool shouldUpdate = false;
|
||||
|
||||
if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.OnBeamMove))
|
||||
{
|
||||
if (!m_TransformPacked.IsSame(transform))
|
||||
shouldUpdate = true;
|
||||
}
|
||||
|
||||
if (!shouldUpdate && updateRate.HasFlag(DynamicOcclusionUpdateRate.EveryXFrames))
|
||||
{
|
||||
if (Time.frameCount >= m_LastFrameRendered + waitXFrames)
|
||||
shouldUpdate = true;
|
||||
}
|
||||
|
||||
if (shouldUpdate)
|
||||
ProcessOcclusion(ProcessOcclusionSource.RenderLoop);
|
||||
}
|
||||
}
|
||||
|
||||
void DisableOcclusion()
|
||||
{
|
||||
m_Master._INTERNAL_SetDynamicOcclusionCallback(GetShaderKeyword(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user