using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace Gaia { [Serializable] public class GaiaLightCheckData { //Sun public Color m_sunColor; public float m_sunIntensity; public Vector3 m_sunRotation; //Ambient public Color m_skyAmbientColor; public Color m_equatorAmbientColor; public Color m_groundAmbientColor; public float m_ambientIntensity; //Skybox public Color m_skyTintColor; public float m_skyExposure; public float m_skyRotation; public Cubemap m_skyHDRITexture; public float m_skyAtmosphereThickness; public Color m_skyGroundColor; //Time of day public float m_minute; public float m_checkMinuteTime = 30f; public int m_hour; public int m_checkHourTime = 1; public bool m_snowEnabled; public bool m_rainEnabled; } [ExecuteAlways] public class ReflectionProbeManager : MonoBehaviour { public static ReflectionProbeManager Instance { get { return m_instance; } } [SerializeField] private static ReflectionProbeManager m_instance; #region Variables public bool EnableSystem { get { return m_enableSystem; } set { if (m_enableSystem != value) { m_enableSystem = value; if (!m_enableSystem) { CleanUp(); } } } } [SerializeField] private bool m_enableSystem = true; public bool UseReflectionProbeCuller { get { return m_useReflectionProbeCuller; } set { if (m_useReflectionProbeCuller != value) { m_useReflectionProbeCuller = value; if (!GaiaUtils.HasDynamicLoadedTerrains()) { SetReflectionProbeCuller(); } } } } [SerializeField] private bool m_useReflectionProbeCuller = true; public float ReflectionProbeCullingDistance { get { return m_reflectionProbeCullingDistance; } set { if (m_reflectionProbeCullingDistance != value) { m_reflectionProbeCullingDistance = value; if (!GaiaUtils.HasDynamicLoadedTerrains()) { SetReflectionProbeCuller(); } } } } [SerializeField] private float m_reflectionProbeCullingDistance = 650f; public LayerMask ProbeLayerMask { get { return m_probeLayerMask; } set { if (m_probeLayerMask != value) { m_probeLayerMask = value; SyncProbeLayerMask(); } } } [SerializeField] private LayerMask m_probeLayerMask = -1; public bool m_checkTODEveryFrame = true; public bool m_probeCullingNeedsUpdating = false; public bool m_isProcessing = false; public List m_sceneReflectionProbes = new List(); public List m_priorityReflectionProbes = new List(); public List m_nonPriorityReflectionProbes = new List(); public GaiaLightCheckData m_lightCheckData = new GaiaLightCheckData(); public Vector3 m_playerBoundsSize = new Vector3(600f, 600f, 600f); public Bounds m_playerBounds = new Bounds(Vector3.zero, Vector3.one); public Transform m_player; public float m_probePriorityCheck = 40f; public float m_probeProcessTime = 0.5f; [SerializeField] private Light m_sunLight; [SerializeField] private Material m_skyMaterial; [SerializeField] private ProceduralWorldsGlobalWeather WeatherSystem; private float m_currentProbePriorityCheck; private int m_priorityProbesCount; private int m_currentPriorityProbesCount; private int m_nonPriorityProbesCount; private int m_currentNonPriorityProbesCount; #endregion #region Unity Functions private void Start() { m_skyMaterial = RenderSettings.skybox; m_sceneReflectionProbes = GetAllSceneProbes(true); for (int i = 0; i < m_sceneReflectionProbes.Count; i++) { m_sceneReflectionProbes[i].RenderProbe(); } SetReflectionProbeCuller(); SetAllLightCheckData(); } private void OnEnable() { m_instance = this; WeatherSystem = ProceduralWorldsGlobalWeather.Instance; if (m_player == null) { m_player = GaiaUtils.GetPlayerTransform(); } m_currentProbePriorityCheck = m_probePriorityCheck; CleanUp(); } private void Update() { if (!Application.isPlaying) { return; } if (EnableSystem) { #if GAIA_PRO_PRESENT if (m_checkTODEveryFrame) { if (!m_isProcessing) { if (CheckTimeOfDay()) { m_currentProbePriorityCheck = -1; StopCoroutine(ProcessPriorityProbes()); } } } #endif if (!m_isProcessing) { m_currentProbePriorityCheck -= Time.deltaTime; if (m_currentProbePriorityCheck < 0) { m_sceneReflectionProbes = GetAllSceneProbes(); m_currentProbePriorityCheck = m_probePriorityCheck; m_priorityReflectionProbes.Clear(); m_nonPriorityReflectionProbes.Clear(); UpdateBounds(); CleanUp(); m_sunLight = GetSunOrMoonLight(); for (int i = 0; i < m_sceneReflectionProbes.Count; i++) { CheckProbeDistance(m_playerBounds, m_sceneReflectionProbes[i]); } m_currentNonPriorityProbesCount = m_nonPriorityReflectionProbes.Count; m_currentPriorityProbesCount = m_priorityReflectionProbes.Count; if (m_currentNonPriorityProbesCount < 1 || m_currentPriorityProbesCount < 1) { SetAllLightCheckData(); CleanUp(); } else { StartCoroutine(ProcessPriorityProbes()); } } } } } private IEnumerator ProcessPriorityProbes() { while (m_priorityProbesCount != m_currentPriorityProbesCount || m_currentPriorityProbesCount == 0) { m_isProcessing = true; yield return new WaitForEndOfFrame(); if (CheckIfHasChanged()) { for (int i = 0; i < m_priorityReflectionProbes.Count; i++) { m_priorityProbesCount++; if (m_priorityReflectionProbes[i] != null) { if (m_priorityReflectionProbes[i].isActiveAndEnabled) { m_priorityReflectionProbes[i].RenderProbe(); yield return new WaitForSecondsRealtime(m_probeProcessTime); } } if (m_priorityProbesCount == m_currentPriorityProbesCount) { StartCoroutine(ProcessNonPriorityProbes()); } } } else { SetAllLightCheckData(); CleanUp(); } } } private IEnumerator ProcessNonPriorityProbes() { while (m_nonPriorityProbesCount != m_currentNonPriorityProbesCount || m_currentNonPriorityProbesCount == 0) { StopCoroutine(ProcessPriorityProbes()); yield return new WaitForEndOfFrame(); for (int i = 0; i < m_nonPriorityReflectionProbes.Count; i++) { m_nonPriorityProbesCount++; if (m_nonPriorityReflectionProbes[i] != null) { if (m_nonPriorityReflectionProbes[i].isActiveAndEnabled) { m_nonPriorityReflectionProbes[i].RenderProbe(); yield return new WaitForSecondsRealtime(m_probeProcessTime); } } if (m_nonPriorityProbesCount == m_currentNonPriorityProbesCount) { SetAllLightCheckData(); CleanUp(); } } } } #endregion #region Private Funtions private Light GetSunOrMoonLight() { Light light = m_sunLight; if (WeatherSystem != null) { if (WeatherSystem.CheckIsNight()) { light = WeatherSystem.m_moonLight; } else { light = WeatherSystem.m_sunLight; } } return light; } /// /// Stops and resets IEnumerators /// private void CleanUp() { m_isProcessing = false; StopAllCoroutines(); m_priorityProbesCount = 0; m_nonPriorityProbesCount = 0; } /// /// Updates the bounds check for the player /// private void UpdateBounds() { if (m_playerBounds == null) { m_playerBounds = new Bounds(Vector3.zero, Vector3.one); } m_playerBounds.size = m_playerBoundsSize; if (m_player != null) { m_playerBounds.center = m_player.position; } } /// /// Gets all the reflection probes in the scene /// /// private List GetAllSceneProbes(bool renderProbes = false) { List reflectionProbes = new List(); ReflectionProbe[] probes = FindObjectsOfType(); for (int i = 0; i < probes.Length; i++) { if (probes[i].isActiveAndEnabled) { probes[i].mode = ReflectionProbeMode.Realtime; probes[i].refreshMode = ReflectionProbeRefreshMode.ViaScripting; reflectionProbes.Add(probes[i]); } } return reflectionProbes; } /// /// Checks if the probe is near the player /// private void CheckProbeDistance(Bounds playerBounds, ReflectionProbe probe) { if (playerBounds == null || probe == null) { return; } if (playerBounds.Contains(probe.transform.position)) { m_priorityReflectionProbes.Add(probe); } else { m_nonPriorityReflectionProbes.Add(probe); } } /// /// Sets all the lighting check data /// private void SetAllLightCheckData() { if (m_lightCheckData == null) { return; } //Set sun settings if (m_sunLight == null) { m_sunLight = GaiaUtils.GetMainDirectionalLight(); } if (m_sunLight != null) { m_lightCheckData.m_sunColor = m_sunLight.color; m_lightCheckData.m_sunIntensity = m_sunLight.intensity; m_lightCheckData.m_sunRotation = m_sunLight.transform.eulerAngles; } //Set ambient settings m_lightCheckData.m_ambientIntensity = RenderSettings.ambientIntensity; m_lightCheckData.m_skyAmbientColor = RenderSettings.ambientSkyColor; m_lightCheckData.m_equatorAmbientColor = RenderSettings.ambientEquatorColor; m_lightCheckData.m_groundAmbientColor = RenderSettings.ambientGroundColor; //Set skybox settings if (m_skyMaterial == null) { m_skyMaterial = RenderSettings.skybox; } if (m_skyMaterial != null) { if (m_skyMaterial.shader == Shader.Find(GaiaShaderID.m_unitySkyboxShaderHDRI)) { m_lightCheckData.m_skyTintColor = m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxTintHDRI); m_lightCheckData.m_skyExposure = m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxExposure); m_lightCheckData.m_skyRotation = m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxRotation); m_lightCheckData.m_skyHDRITexture = (Cubemap)m_skyMaterial.GetTexture(GaiaShaderID.m_unitySkyboxCubemap); } else if (m_skyMaterial.shader == Shader.Find(GaiaShaderID.m_unitySkyboxShader)) { m_lightCheckData.m_skyTintColor = m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxTint); m_lightCheckData.m_skyGroundColor = m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxGroundColor); m_lightCheckData.m_skyExposure = m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxExposure); m_lightCheckData.m_skyAtmosphereThickness = m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxAtmosphereThickness); } } //Set time of day settings #if GAIA_PRO_PRESENT if (GaiaUtils.CheckIfSceneProfileExists()) { m_lightCheckData.m_minute = GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todMinutes; m_lightCheckData.m_hour = GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todHour; if (WeatherSystem != null) { GaiaGlobal.GaiaGlobalNetworkSyncGetWeather(out m_lightCheckData.m_rainEnabled, out m_lightCheckData.m_snowEnabled); } } #endif } /// /// Checks if time of day has changed /// /// private bool CheckTimeOfDay() { #if GAIA_PRO_PRESENT //Time of day check if (GaiaUtils.CheckIfSceneProfileExists()) { if (GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todMinutes >= m_lightCheckData.m_minute + m_lightCheckData.m_checkMinuteTime) { return true; } if (GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todHour >= m_lightCheckData.m_hour + m_lightCheckData.m_checkHourTime) { return true; } if (WeatherSystem != null) { if (m_lightCheckData.m_rainEnabled != WeatherSystem.IsRaining) { return true; } if (m_lightCheckData.m_snowEnabled != WeatherSystem.IsSnowing) { return true; } } } #endif return false; } /// /// Syncs the probes layer mask /// private void SyncProbeLayerMask() { if (!Application.isPlaying) { m_sceneReflectionProbes = GetAllSceneProbes(); } else { if (m_sceneReflectionProbes.Count < 1) { m_sceneReflectionProbes = GetAllSceneProbes(); } } for (int i = 0; i < m_sceneReflectionProbes.Count; i++) { if (m_sceneReflectionProbes[i] != null) { m_sceneReflectionProbes[i].cullingMask = ProbeLayerMask; } } } #endregion #region Public Functions /// /// Sets up the reflection probe culling /// public void SetReflectionProbeCuller() { if (GaiaUtils.HasDynamicLoadedTerrains()) { if (!Application.isPlaying) { Action terrAction = (t) => ProcessProbeCuller(t); GaiaUtils.CallFunctionOnDynamicLoadedTerrains(terrAction, true, null, "Updating Reflection Probe Culling"); } } else { for (int i = 0; i < Terrain.activeTerrains.Length; i++) { ProcessProbeCuller(Terrain.activeTerrains[i]); } } } /// /// Updates the probe culling distance /// /// private void ProcessProbeCuller(Terrain terrain) { try { GameObject probeParent = GameObject.Find(terrain.name + " Reflection Probes"); if (probeParent != null) { ReflectionProbe[] allProbes = probeParent.GetComponentsInChildren(); if (allProbes.Length > 0) { foreach (var probe in allProbes) { ReflectionProbeCuller culler = probe.GetComponent(); if (UseReflectionProbeCuller) { if (culler == null) { culler = probe.gameObject.AddComponent(); culler.m_probe = probe; culler.Initialize(); } culler.m_maxRenderDistance = ReflectionProbeCullingDistance; } else { if (culler != null) { DestroyImmediate(culler); } } } } } } catch (Exception e) { Console.WriteLine(e); throw; } } /// /// Checks if any lighting changes have been made /// /// public bool CheckIfHasChanged() { if (m_lightCheckData == null) { Debug.LogError("Light Check Data was empty, pleae make sure you have created one and assigned it in the script before executing this function."); return false; } //Sun check if (m_sunLight != null) { if (m_sunLight.intensity != m_lightCheckData.m_sunIntensity) { return true; } if (m_sunLight.color != m_lightCheckData.m_sunColor) { return true; } if (m_sunLight.transform.eulerAngles != m_lightCheckData.m_sunRotation) { return true; } } //Ambient check if (RenderSettings.ambientIntensity != m_lightCheckData.m_ambientIntensity) { return true; } if (RenderSettings.ambientSkyColor != m_lightCheckData.m_skyAmbientColor) { return true; } if (RenderSettings.ambientEquatorColor != m_lightCheckData.m_equatorAmbientColor) { return true; } if (RenderSettings.ambientGroundColor != m_lightCheckData.m_groundAmbientColor) { return true; } //Skybox check if (m_skyMaterial != null) { if (m_skyMaterial.shader == Shader.Find(GaiaShaderID.m_unitySkyboxShaderHDRI)) { if (m_lightCheckData.m_skyTintColor != m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxTintHDRI)) { return true; } if (m_lightCheckData.m_skyExposure != m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxExposure)) { return true; } if (m_lightCheckData.m_skyRotation != m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxRotation)) { return true; } if (m_lightCheckData.m_skyHDRITexture != m_skyMaterial.GetTexture(GaiaShaderID.m_unitySkyboxCubemap)) { return true; } } else if (m_skyMaterial.shader == Shader.Find(GaiaShaderID.m_unitySkyboxShader)) { if (m_lightCheckData.m_skyTintColor != m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxTint)) { return true; } if (m_lightCheckData.m_skyGroundColor != m_skyMaterial.GetColor(GaiaShaderID.m_unitySkyboxGroundColor)) { return true; } if (m_lightCheckData.m_skyExposure != m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxExposure)) { return true; } if (m_lightCheckData.m_skyAtmosphereThickness != m_skyMaterial.GetFloat(GaiaShaderID.m_unitySkyboxAtmosphereThickness)) { return true; } } } #if GAIA_PRO_PRESENT //Time of day check if (GaiaUtils.CheckIfSceneProfileExists()) { if (m_lightCheckData.m_minute != GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todMinutes) { return true; } if (m_lightCheckData.m_hour != GaiaGlobal.Instance.SceneProfile.m_gaiaTimeOfDay.m_todHour) { return true; } } #endif return false; } /// /// Gets or creates the reflection probe manager /// /// public static ReflectionProbeManager GetOrCreateProbeManager() { ReflectionProbeManager manager = FindObjectOfType(); GameObject parent = GameObject.Find(GaiaConstants.gaiaLightingObject); if (manager != null) { if (parent != null) { manager.gameObject.transform.SetParent(parent.transform); } if (!GaiaUtils.HasDynamicLoadedTerrains()) { manager.SetReflectionProbeCuller(); } return manager; } else { GameObject probeGameObject = new GameObject("Reflection Probe Manager"); manager = probeGameObject.AddComponent(); if (parent != null) { probeGameObject.transform.SetParent(parent.transform); } manager.SetReflectionProbeCuller(); return manager; } } /// /// Removes the reflection probe manager /// public static void RemoveReflectionProbeManager() { GameObject probeManager = GameObject.Find("Reflection Probe Manager"); if (probeManager != null) { DestroyImmediate(probeManager); } } #endregion } }