diff --git a/crest/Assets/Crest/Crest-Examples/Shared/Scripts/UnderwaterEnvironmentalLighting.cs b/crest/Assets/Crest/Crest-Examples/Shared/Scripts/UnderwaterEnvironmentalLighting.cs
index 67bdff489..2abe1d30b 100644
--- a/crest/Assets/Crest/Crest-Examples/Shared/Scripts/UnderwaterEnvironmentalLighting.cs
+++ b/crest/Assets/Crest/Crest-Examples/Shared/Scripts/UnderwaterEnvironmentalLighting.cs
@@ -40,13 +40,18 @@ public class UnderwaterEnvironmentalLighting : MonoBehaviour
public const float DEPTH_OUTSCATTER_CONSTANT = 0.25f;
+ OceanRendererLifeCycleHelper _oceanRendererLifeCycle;
bool _isInitialised = false;
+ void Awake()
+ {
+ _oceanRendererLifeCycle = new OceanRendererLifeCycleHelper(this);
+ }
+
void OnEnable()
{
- if (OceanRenderer.Instance == null)
+ if (!_oceanRendererLifeCycle.OnEnable())
{
- enabled = false;
return;
}
@@ -76,6 +81,8 @@ void OnEnable()
void OnDisable()
{
+ _oceanRendererLifeCycle.OnDisable();
+
if (!_isInitialised)
{
return;
@@ -93,6 +100,11 @@ void OnDisable()
_isInitialised = false;
}
+ void OnDestroy()
+ {
+ _oceanRendererLifeCycle.OnDestroy();
+ }
+
void LateUpdate()
{
if (OceanRenderer.Instance == null)
diff --git a/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs b/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs
new file mode 100644
index 000000000..ca7cc71ef
--- /dev/null
+++ b/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs
@@ -0,0 +1,155 @@
+// Crest Ocean System
+
+// This file is subject to the MIT License as seen in the root of this folder structure (LICENSE)
+
+namespace Crest
+{
+ using UnityEngine;
+
+ ///
+ /// Enables/disables a component based on the OceanRenderer's life cycle. It will allow a component that depend on
+ /// the OceanRenderer to initialise after the OceanRenderer has initialised (i.e. delayed initialisation).
+ ///
+ /// It stores the "enabled" state set by the user as it enables or disables the component if the OceanRenderer is
+ /// enabled or disabled respectively using event subscription broadcasted from the OceanRenderer.
+ ///
+ /// It supports [ExecuteAlways/InEditMode] by storing the "enabled" state, but does not enable or disable the
+ /// component as that would change serialized data.
+ ///
+ /// Components must call OnEnable, OnDisable and OnDestroy from their MonoBehaviour counterparts.
+ ///
+ public class OceanRendererLifeCycleHelper
+ {
+ // The mono behaviour we want to track the enabled/disabled preference.
+ readonly MonoBehaviour _monoBehaviour;
+
+ // Store the "enabled" state set by the user so we can respect it.
+ internal bool _enabledValueSetByTheUser;
+
+ // Whether the state change came from the user or from the ocean life cycle event. Generally, this should always
+ // be true since state changes from users are unknown.
+ bool _isStateChangeFromUser = false;
+
+ public OceanRendererLifeCycleHelper(MonoBehaviour monoBehaviour)
+ {
+ _monoBehaviour = monoBehaviour;
+ // Store the "enabled" state set by the user.
+ _enabledValueSetByTheUser = _monoBehaviour.enabled;
+
+ OceanRenderer.OnOceanRendererEnabled -= OnOceanRendererEnabled;
+ OceanRenderer.OnOceanRendererEnabled += OnOceanRendererEnabled;
+ OceanRenderer.OnOceanRendererDisabled -= OnOceanRendererDisabled;
+ OceanRenderer.OnOceanRendererDisabled += OnOceanRendererDisabled;
+ }
+
+ ~OceanRendererLifeCycleHelper()
+ {
+ OceanRenderer.OnOceanRendererEnabled -= OnOceanRendererEnabled;
+ OceanRenderer.OnOceanRendererDisabled -= OnOceanRendererDisabled;
+ }
+
+ ///
+ /// Needs to be called to clean up resources. Destructors do not work when domain reload is disabled.
+ ///
+ public void OnDestroy()
+ {
+ OceanRenderer.OnOceanRendererEnabled -= OnOceanRendererEnabled;
+ OceanRenderer.OnOceanRendererDisabled -= OnOceanRendererDisabled;
+ }
+
+ ///
+ /// Sets enabled based on OceanRenderer's life cycle. Callee must return early if false is returned.
+ ///
+ public bool OnEnable()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ {
+ // In edit mode just store "enabled" as we do not disable the component anyway.
+ _enabledValueSetByTheUser = _monoBehaviour.enabled;
+ return OceanRenderer.Instance != null;
+ }
+#endif
+
+ // This will be "false" when we know when the user has not changed the state which is something we can
+ // more easily determine like when we change the state ourselves.
+ if (_isStateChangeFromUser)
+ {
+ // Flip value as it could be either the user enabling or disabling the component. For the case where the
+ // ocean renderer is not active and this component is set to be enabled, the component will be disabled.
+ // The user will see an unchecked checkbox for "enable" as they should. When they toggle it, it will
+ // enable the component but since the ocean is disabled it will disable itself after recording the
+ // user's preference.
+ _enabledValueSetByTheUser = !_enabledValueSetByTheUser;
+ }
+
+ // Always revert to "true" as whether the change comes from a user is unknown.
+ _isStateChangeFromUser = true;
+
+ if (OceanRenderer.Instance == null)
+ {
+ // Mark "false" as we will now disable the component and the subsequent OnDisable is not from the user.
+ _isStateChangeFromUser = false;
+ _monoBehaviour.enabled = false;
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Sets enabled based on OceanRenderer's life cycle.
+ ///
+ public void OnDisable()
+ {
+#if UNITY_EDITOR
+ if (!Application.isPlaying)
+ {
+ // In edit mode just store "enabled" as we do not disable the component anyway.
+ _enabledValueSetByTheUser = _monoBehaviour.enabled;
+ return;
+ }
+#endif
+
+ if (_isStateChangeFromUser)
+ {
+ _enabledValueSetByTheUser = false;
+ }
+
+ // Always revert to "true" as whether the change comes from a user is unknown.
+ _isStateChangeFromUser = true;
+ }
+
+ // This will be called by the OceanRenderer through an event. If during the component's OnEnable event the
+ // OceanRenderer is already ready, then this will be skipped.
+ void OnOceanRendererEnabled()
+ {
+#if UNITY_EDITOR
+ // We do not want to change a serialised value in edit mode.
+ if (!Application.isPlaying)
+ {
+ return;
+ }
+#endif
+
+ // If these two are equal then this event will do nothing and next OnEnable/OnDisable could be the user.
+ _isStateChangeFromUser = _monoBehaviour.enabled == _enabledValueSetByTheUser;
+ // Apply the "enabled" state that the user wants now that OceanRenderer is ready.
+ _monoBehaviour.enabled = _enabledValueSetByTheUser;
+ }
+
+ // This will be called by the OceanRenderer through an event.
+ void OnOceanRendererDisabled()
+ {
+ if (!Application.isPlaying)
+ {
+ return;
+ }
+
+ // If the user has set the component to disabled, then this event will do nothing and next
+ // OnEnable/OnDisable could be the user.
+ _isStateChangeFromUser = _enabledValueSetByTheUser == false;
+ _monoBehaviour.enabled = false;
+ }
+ }
+}
diff --git a/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs.meta b/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs.meta
new file mode 100644
index 000000000..b5783661b
--- /dev/null
+++ b/crest/Assets/Crest/Crest/Scripts/Helpers/OceanRendererLifeCycleHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e5719391a5a244f85b470c23e28befe9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs b/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs
index 05982db18..08156ce2d 100644
--- a/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs
+++ b/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs
@@ -88,13 +88,32 @@ public enum OceanDepthCacheRefreshMode
Camera _camDepthCache;
Material _copyDepthMaterial;
+ OceanRendererLifeCycleHelper _oceanRendererLifeCycle;
+
+ bool _isInitialized = false;
+
+ void Awake()
+ {
+ _oceanRendererLifeCycle = new OceanRendererLifeCycleHelper(this);
+ }
+
+ void OnEnable()
+ {
+ _oceanRendererLifeCycle.OnEnable();
+ }
+
+ void OnDisable()
+ {
+ _oceanRendererLifeCycle.OnDisable();
+ }
+
+ void OnDestroy()
+ {
+ _oceanRendererLifeCycle.OnDestroy();
+ }
+
void Start()
{
- if (OceanRenderer.Instance == null)
- {
- enabled = false;
- return;
- }
#if UNITY_EDITOR
if (EditorApplication.isPlaying && _runValidationOnStart)
@@ -111,11 +130,18 @@ void Start()
{
PopulateCache();
}
+
+ _isInitialized = true;
}
#if UNITY_EDITOR
void Update()
{
+ if (!_isInitialized)
+ {
+ Start();
+ }
+
// We need to switch the quad texture if the user changes the cache type in the editor.
InitCacheQuad();
diff --git a/crest/Assets/Crest/Crest/Scripts/OceanRenderer.cs b/crest/Assets/Crest/Crest/Scripts/OceanRenderer.cs
index 4b47a16ae..b35c99adb 100644
--- a/crest/Assets/Crest/Crest/Scripts/OceanRenderer.cs
+++ b/crest/Assets/Crest/Crest/Scripts/OceanRenderer.cs
@@ -338,6 +338,8 @@ public enum DefaultClippingState
SampleHeightHelper _sampleHeightHelper = new SampleHeightHelper();
public static OceanRenderer Instance { get; private set; }
+ public static System.Action OnOceanRendererEnabled;
+ public static System.Action OnOceanRendererDisabled;
// A hash of the settings used to generate the ocean, used to regenerate when necessary
int _generatedSettingsHash = 0;
@@ -528,6 +530,8 @@ void OnEnable()
}
_canSkipCulling = false;
+
+ OnOceanRendererEnabled?.Invoke();
}
private void OnDisable()
@@ -548,6 +552,8 @@ private void OnDisable()
}
#endif
+ OnOceanRendererDisabled?.Invoke();
+
CleanUp();
Instance = null;
diff --git a/crest/Assets/Crest/Crest/Scripts/Reflection/OceanPlanarReflection.cs b/crest/Assets/Crest/Crest/Scripts/Reflection/OceanPlanarReflection.cs
index 77f6974b5..44a0287d3 100644
--- a/crest/Assets/Crest/Crest/Scripts/Reflection/OceanPlanarReflection.cs
+++ b/crest/Assets/Crest/Crest/Scripts/Reflection/OceanPlanarReflection.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using UnityEditor;
using UnityEngine;
@@ -125,11 +126,17 @@ public class OceanPlanarReflection : MonoBehaviour
const int CULL_DISTANCE_COUNT = 32;
float[] _cullDistances = new float[CULL_DISTANCE_COUNT];
- private void Start()
+ internal OceanRendererLifeCycleHelper _oceanRendererLifeCycle;
+
+ private void Awake()
{
- if (OceanRenderer.Instance == null)
+ _oceanRendererLifeCycle = new OceanRendererLifeCycleHelper(this);
+ }
+
+ private void OnEnable()
+ {
+ if (!_oceanRendererLifeCycle.OnEnable())
{
- enabled = false;
return;
}
@@ -383,6 +390,8 @@ static void CalculateReflectionMatrix(ref Matrix4x4 reflectionMat, Vector4 plane
private void OnDisable()
{
+ _oceanRendererLifeCycle.OnDisable();
+
if (_camViewpoint != null)
{
PreparedReflections.Remove(_camViewpoint.GetHashCode());
@@ -400,5 +409,31 @@ private void OnDisable()
_camReflections = null;
}
}
+
+ private void OnDestroy()
+ {
+ _oceanRendererLifeCycle.OnDestroy();
+ }
}
+
+#if UNITY_EDITOR
+ [CustomEditor(typeof(OceanPlanarReflection)), CanEditMultipleObjects]
+ public class OceanPlanarReflectionEditor : Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ var target = this.target as OceanPlanarReflection;
+
+ if (OceanRenderer.Instance == null && target._oceanRendererLifeCycle._enabledValueSetByTheUser)
+ {
+ EditorGUILayout.Space();
+ EditorGUILayout.HelpBox("The component is disabled as there is no active OceanRenderer in " +
+ "the scene. It will be enabled once there is one.", MessageType.Info);
+ EditorGUILayout.Space();
+ }
+
+ base.OnInspectorGUI();
+ }
+ }
+#endif
}