using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using System; namespace Enviro { [Serializable] public class EnviroReflections { public enum GlobalReflectionResolution { R16, R32, R64, R128, R256, R512, R1024, R2048 } public bool globalReflections = true; [Tooltip("Set if enviro reflection probe should use custom rendering setup. For example to include post effectsin birp.")] public bool customRendering = true; [Tooltip("Set to use custom timeslicing when rendered in custom mode.")] public bool customRenderingTimeSlicing = true; [Tooltip("Set if enviro reflection probe should update faces individual on different frames.")] public ReflectionProbeTimeSlicingMode globalReflectionTimeSlicingMode = ReflectionProbeTimeSlicingMode.IndividualFaces; [Tooltip("Enable/disable enviro reflection probe updates based on gametime changes..")] public bool globalReflectionsUpdateOnGameTime = true; [Tooltip("Enable/disable enviro reflection probe updates based on transform position changes..")] public bool globalReflectionsUpdateOnPosition = true; [Tooltip("Reflection probe intensity.")] [Range(0f, 2f)] public float globalReflectionsIntensity = 1.0f; [Tooltip("Reflection probe update rate based on game time.")] public float globalReflectionsTimeTreshold = 0.025f; [Tooltip("Reflection probe update rate based on camera position.")] public float globalReflectionsPositionTreshold = 0.5f; [Tooltip("Reflection probe scale. Increase that one to increase the area where reflection probe will influence your scene.")] [Range(10f, 10000f)] public float globalReflectionsScale = 10000f; [Tooltip("Reflection probe resolution.")] public GlobalReflectionResolution globalReflectionResolution = GlobalReflectionResolution.R256; [Tooltip("Reflection probe rendered Layers.")] public LayerMask globalReflectionLayers; [Tooltip("Enable this option to update the default reflection with global reflection probes cubemap. This can be needed for material that might not support direct reflection probes. (Instanced Indirect Rendering)")] public bool updateDefaultEnvironmentReflections = true; [Tooltip("Reflection cubemap used for default scene sky reflections in < Unity 2022.1 versions.")] public Cubemap defaultSkyReflectionTex; } [Serializable] [ExecuteInEditMode] public class EnviroReflectionsModule : EnviroModule { public Enviro.EnviroReflections Settings; public EnviroReflectionsModule preset; // Inspector public bool showReflectionControls; public float lastReflectionUpdate; public Vector3 lastReflectionUpdatePos; private Coroutine renderReflectionCoroutine; private Coroutine waitForProbeCoroutine; private Coroutine copyDefaultReflectionCoroutine; public override void Enable () { if(EnviroManager.instance == null) return; Setup(); // Update global reflections once on enable. if(EnviroManager.instance.Objects.globalReflectionProbe != null && Settings.globalReflections) EnviroManager.instance.StartCoroutine(WaitToRefreshReflection()); } public override void Disable () { if(EnviroManager.instance == null) return; Cleanup(); } private void Cleanup() { if(EnviroManager.instance == null) return; if(EnviroManager.instance.Objects.globalReflectionProbe != null) DestroyImmediate(EnviroManager.instance.Objects.globalReflectionProbe.gameObject); } // Unity warns with "Attempting to update a disabled Reflection Probe" even though the probe is enabled. // We have to wait a frame before interacting with reflection probes to allow Unity time to do any // setup in its internal OnEnable(). Otherwise, we will receive a warning: // "Attempting to update a disabled Reflection Probe. Action will be ignored." private IEnumerator WaitToRefreshReflection() { yield return null; RenderGlobalReflectionProbe(true, false); UpdateDefaultReflectionTextureMode (); } private void Setup() { if(EnviroManager.instance.Objects.globalReflectionProbe == null) { GameObject newReflectionProbe = new GameObject(); newReflectionProbe.name = "Global Reflection Probe"; newReflectionProbe.transform.SetParent(EnviroManager.instance.transform); newReflectionProbe.transform.localPosition = Vector3.zero; EnviroManager.instance.Objects.globalReflectionProbe = newReflectionProbe.AddComponent(); } } public override void UpdateModule () { if(EnviroManager.instance == null) return; if(EnviroManager.instance.Objects.globalReflectionProbe != null) UpdateReflection(); } private void UpdateReflection() { if(!Settings.globalReflections) { EnviroManager.instance.Objects.globalReflectionProbe.myProbe.enabled = false; UpdateDefaultReflectionTextureMode (); return; } else { EnviroManager.instance.Objects.globalReflectionProbe.myProbe.enabled = true; } EnviroReflectionProbe probe = EnviroManager.instance.Objects.globalReflectionProbe; SetupProbeSettings(probe); if(EnviroManager.instance.Time != null) { if ((lastReflectionUpdate < EnviroManager.instance.Time.Settings.timeOfDay || lastReflectionUpdate > EnviroManager.instance.Time.Settings.timeOfDay + (Settings.globalReflectionsTimeTreshold + 0.01f)) && Settings.globalReflectionsUpdateOnGameTime) { RenderGlobalReflectionProbe(false,Settings.customRenderingTimeSlicing); lastReflectionUpdate = EnviroManager.instance.Time.Settings.timeOfDay + Settings.globalReflectionsTimeTreshold; } } if ((probe.transform.position.magnitude > lastReflectionUpdatePos.magnitude + Settings.globalReflectionsPositionTreshold || probe.transform.position.magnitude < lastReflectionUpdatePos.magnitude - Settings.globalReflectionsPositionTreshold) && Settings.globalReflectionsUpdateOnPosition) { RenderGlobalReflectionProbe(false,Settings.customRenderingTimeSlicing); lastReflectionUpdatePos = probe.transform.position; } UpdateDefaultReflectionTextureMode (); } public void RenderGlobalReflectionProbe(bool forced = false, bool timeslice = false) { EnviroReflectionProbe probe = EnviroManager.instance.Objects.globalReflectionProbe; if (probe == null) return; if(renderReflectionCoroutine != null) { EnviroManager.instance.StopCoroutine(renderReflectionCoroutine); renderReflectionCoroutine = null; } #if !ENVIRO_HDRP renderReflectionCoroutine = EnviroManager.instance.StartCoroutine(RenderGlobalReflectionProbeTimed(probe,timeslice)); if(Settings.updateDefaultEnvironmentReflections) { #if UNITY_2022_1_OR_NEWER // We don't need to copy the texture to a cubemap anmyore #else // Prevent multiple coroutines from running at the same time if (copyDefaultReflectionCoroutine != null) { EnviroManager.instance.StopCoroutine(copyDefaultReflectionCoroutine); copyDefaultReflectionCoroutine = null; } if(Settings.customRendering) copyDefaultReflectionCoroutine = EnviroManager.instance.StartCoroutine(CopyDefaultReflectionCustom(probe, timeslice)); else CopyDefaultReflectionUnity(probe); #endif } #else renderReflectionCoroutine = EnviroManager.instance.StartCoroutine(RenderGlobalReflectionProbeTimed(probe,timeslice)); #endif } //Copy reflection probe to cubemap and assign as default reflections. private void CopyDefaultReflectionCubemap (EnviroReflectionProbe probe) { if(Settings.defaultSkyReflectionTex == null || Settings.defaultSkyReflectionTex.height != probe.myProbe.texture.height || Settings.defaultSkyReflectionTex.width != probe.myProbe.texture.width) { if(Settings.defaultSkyReflectionTex != null) DestroyImmediate(Settings.defaultSkyReflectionTex); Settings.defaultSkyReflectionTex = new Cubemap(probe.myProbe.resolution, probe.myProbe.hdr ? TextureFormat.RGBAHalf : TextureFormat.RGBA32, true); Settings.defaultSkyReflectionTex.name = "Enviro Default Sky Reflection"; } if(probe.myProbe.texture != null) Graphics.CopyTexture(probe.myProbe.texture, Settings.defaultSkyReflectionTex as Texture); } public void UpdateDefaultReflectionTextureMode () { if(Settings.updateDefaultEnvironmentReflections && Settings.globalReflections) { RenderSettings.defaultReflectionMode = UnityEngine.Rendering.DefaultReflectionMode.Custom; #if UNITY_2022_1_OR_NEWER RenderSettings.customReflectionTexture = EnviroManager.instance.Objects.globalReflectionProbe.myProbe.texture; #else if(Settings.defaultSkyReflectionTex != null) RenderSettings.customReflection = Settings.defaultSkyReflectionTex; #endif } else { RenderSettings.defaultReflectionMode = UnityEngine.Rendering.DefaultReflectionMode.Skybox; } } //Update all probe settings. private void SetupProbeSettings(EnviroReflectionProbe probe) { int res = 128; switch (Settings.globalReflectionResolution) { case EnviroReflections.GlobalReflectionResolution.R16: res = 16; break; case EnviroReflections.GlobalReflectionResolution.R32: res = 32; break; case EnviroReflections.GlobalReflectionResolution.R64: res = 64; break; case EnviroReflections.GlobalReflectionResolution.R128: res = 128; break; case EnviroReflections.GlobalReflectionResolution.R256: res = 256; break; case EnviroReflections.GlobalReflectionResolution.R512: res = 512; break; case EnviroReflections.GlobalReflectionResolution.R1024: res = 1024; break; case EnviroReflections.GlobalReflectionResolution.R2048: res = 2048; break; } #if !ENVIRO_HDRP probe.myProbe.cullingMask = Settings.globalReflectionLayers; probe.myProbe.intensity = Settings.globalReflectionsIntensity; probe.myProbe.size = new Vector3 (Settings.globalReflectionsScale,Settings.globalReflectionsScale,Settings.globalReflectionsScale); probe.myProbe.resolution = res; #if ENVIRO_URP probe.customRendering = false; probe.myProbe.timeSlicingMode = Settings.globalReflectionTimeSlicingMode; #else probe.customRendering = Settings.customRendering; probe.useTimeSlicing = Settings.customRenderingTimeSlicing; probe.myProbe.timeSlicingMode = Settings.globalReflectionTimeSlicingMode; #endif RenderSettings.reflectionIntensity = Settings.globalReflectionsIntensity; #else probe.customRendering = false; probe.myProbe.resolution = res; if(probe.hdprobe != null) { probe.hdprobe.settingsRaw.cameraSettings.culling.cullingMask = Settings.globalReflectionLayers; probe.hdprobe.settingsRaw.influence.boxSize = new Vector3 (Settings.globalReflectionsScale,Settings.globalReflectionsScale,Settings.globalReflectionsScale); probe.hdprobe.settingsRaw.influence.sphereRadius = Settings.globalReflectionsScale; probe.hdprobe.settingsRaw.lighting.multiplier = Settings.globalReflectionsIntensity; } #endif } private IEnumerator CopyDefaultReflectionCustom(EnviroReflectionProbe probe, bool timeslice) { if (timeslice) { // Wait for seven frames so probe finished rendering for (int i = 0; i < 8; i++) { yield return null; } CopyDefaultReflectionCubemap(probe); } else { yield return null; yield return null; CopyDefaultReflectionCubemap(probe); } } private void CopyDefaultReflectionUnity(EnviroReflectionProbe probe) { if(probe.renderId == -1 || probe.myProbe.IsFinishedRendering(probe.renderId)) { CopyDefaultReflectionCubemap(probe); } else { if (waitForProbeCoroutine != null) { EnviroManager.instance.StopCoroutine(waitForProbeCoroutine); waitForProbeCoroutine = null; } waitForProbeCoroutine = EnviroManager.instance.StartCoroutine(WaitForUnityProbe(probe)); } } private IEnumerator WaitForUnityProbe(EnviroReflectionProbe probe) { yield return null; CopyDefaultReflectionUnity(probe); } private IEnumerator RenderGlobalReflectionProbeTimed (EnviroReflectionProbe probe, bool timeslice) { #if ENVIRO_HDRP if(EnviroManager.instance.Lighting.Settings.setDirectLighting) EnviroManager.instance.Lighting.UpdateDirectLightingHDRP (); if(EnviroManager.instance.Lighting.Settings.setAmbientLighting) EnviroManager.instance.Lighting.UpdateAmbientLightingHDRP(); yield return null; probe.RefreshReflection(timeslice); yield return null; EnviroManager.instance.Lighting.UpdateExposureHDRP (); #else if(EnviroManager.instance.Lighting != null) { //Force a lighting update before rendering the probe as it might has not updated yet. if(EnviroManager.instance.Lighting.Settings.setDirectLighting) EnviroManager.instance.Lighting.UpdateDirectLighting (); if(EnviroManager.instance.Lighting.Settings.setAmbientLighting) EnviroManager.instance.Lighting.UpdateAmbientLighting(true); yield return null; if(EnviroManager.instance.Reflections.Settings.globalReflections) probe.RefreshReflection(timeslice); } else { if(EnviroManager.instance.Reflections.Settings.globalReflections) probe.RefreshReflection(timeslice); } #endif } //Save and Load public void LoadModuleValues () { if(preset != null) { Settings = JsonUtility.FromJson(JsonUtility.ToJson(preset.Settings)); } else { Debug.Log("Please assign a saved module to load from!"); } } public void SaveModuleValues () { #if UNITY_EDITOR EnviroReflectionsModule t = ScriptableObject.CreateInstance(); t.name = "Reflections Module"; t.Settings = JsonUtility.FromJson(JsonUtility.ToJson(Settings)); string assetPathAndName = UnityEditor.AssetDatabase.GenerateUniqueAssetPath(EnviroHelper.assetPath + "/New " + t.name + ".asset"); UnityEditor.AssetDatabase.CreateAsset(t, assetPathAndName); UnityEditor.AssetDatabase.SaveAssets(); UnityEditor.AssetDatabase.Refresh(); #endif } public void SaveModuleValues (EnviroReflectionsModule module) { module.Settings = JsonUtility.FromJson(JsonUtility.ToJson(Settings)); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(module); UnityEditor.AssetDatabase.SaveAssets(); #endif } } }