#if HDPipeline #if UNITY_EDITOR using UnityEditor; #endif using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering.HighDefinition; namespace Gaia { public enum DensityVolumeResolution { VeryLow, Low, Medium, High, VeryHigh, Ultra, Custom } public enum DensityVolumeEffectType { VeryLightHase, LightHase, ModerateHase, HighHase, ExtremeHase, Custom } [System.Serializable] public class DensityVolumeProfile { public Color m_singleScatteringAlbedo = Color.white; public float m_fogDistance = 250f; public Vector3 m_size = new Vector3(20f, 20f, 20f); public float m_blendDistance = 0f; public bool m_invertBlend = false; public float m_distanceFadeStart = 10000f; public float m_distanceFadeEnd = 10000f; public Texture3D m_texture; public Vector3 m_scrollSpeed = Vector3.zero; public Vector3 m_tiling = Vector3.one; public DensityVolumeResolution m_resolution = DensityVolumeResolution.High; public DensityVolumeEffectType m_effectType = DensityVolumeEffectType.LightHase; } [ExecuteAlways] public class HDRPDensityVolumeController : MonoBehaviour { //Static public static HDRPDensityVolumeController Instance { get { return m_instance; } set { m_instance = value; } } [SerializeField] private static HDRPDensityVolumeController m_instance; //Public public DensityVolumeProfile DensityVolumeProfile { get { return m_densityVolumeProfile; } set { m_densityVolumeProfile = value; } } public Camera m_mainCamera; public List m_densityVolumes = new List(); public Vector2 m_densityVolumeCheck = new Vector2(0.4f, 1f); public float m_densityVolumeBlendTime = 2f; //Private private float m_densityVolumeCheckTimer; private float m_blendTime; private bool m_lastBoundsVolumeState = false; private bool m_processVolumeBlend = false; [SerializeField] private DensityVolumeProfile m_densityVolumeProfile = new DensityVolumeProfile(); #if UNITY_2021_2_OR_NEWER [SerializeField] private LocalVolumetricFog m_volume; #else [SerializeField] private DensityVolume m_volume; #endif [SerializeField] private Camera m_editorMainCamera; private const string DensityVolumeName = "Gaia World Density Volume"; #region Unity Functions /// /// Apply on enable /// private void OnEnable() { Initialize(); } /// /// Applies on disable /// private void OnDisable() { #if UNITY_EDITOR EditorApplication.update -= EditorUpdateVolumeController; #endif } /// /// Applies on destroy /// private void OnDestroy() { #if UNITY_EDITOR EditorApplication.update -= EditorUpdateVolumeController; #endif } /// /// Updates every frame /// private void Update() { if (!Application.isPlaying) { return; } if (m_mainCamera != null) { UpdateVolumeTransform(m_mainCamera.transform.position, Quaternion.identity); if (!m_processVolumeBlend) { m_densityVolumeCheckTimer -= Time.deltaTime; if (m_densityVolumeCheckTimer <= 0f) { m_densityVolumeCheckTimer = UnityEngine.Random.Range(m_densityVolumeCheck.x, m_densityVolumeCheck.y); bool newValue = IsInAnotherVolume(m_mainCamera); if (m_lastBoundsVolumeState != newValue) { m_blendTime = 0f; m_lastBoundsVolumeState = newValue; m_processVolumeBlend = true; } } } if (m_processVolumeBlend) { SetDensityVolumeState(m_lastBoundsVolumeState); } } } #endregion #region Utils /// /// Applies the density volume changes /// public void ApplyChanges() { m_instance = this; if (m_volume == null) { m_volume = CreateOrGetVolume(); } if (m_volume.isActiveAndEnabled) { m_volume.parameters.albedo = DensityVolumeProfile.m_singleScatteringAlbedo; } m_volume.parameters.meanFreePath = DensityVolumeProfile.m_fogDistance; m_volume.parameters.size = GetEffectSizeType(DensityVolumeProfile); m_volume.parameters.anisotropy = DensityVolumeProfile.m_blendDistance; m_volume.parameters.invertFade = DensityVolumeProfile.m_invertBlend; m_volume.parameters.distanceFadeStart = DensityVolumeProfile.m_distanceFadeStart; m_volume.parameters.distanceFadeEnd = DensityVolumeProfile.m_distanceFadeEnd; m_volume.parameters.volumeMask = DensityVolumeProfile.m_texture; m_volume.parameters.textureScrollingSpeed = DensityVolumeProfile.m_scrollSpeed; m_volume.parameters.textureTiling = GetResolution(DensityVolumeProfile); } /// /// Gets the tiling resolution /// /// /// private Vector3 GetResolution(DensityVolumeProfile profile) { Vector3 resolution = Vector3.one; if (profile == null) { return resolution; } switch (profile.m_resolution) { case DensityVolumeResolution.VeryLow: { resolution = new Vector3(128f, 128f, 128f); break; } case DensityVolumeResolution.Low: { resolution = new Vector3(256f, 256f, 256f); break; } case DensityVolumeResolution.Medium: { resolution = new Vector3(512f, 512f, 512f); break; } case DensityVolumeResolution.High: { resolution = new Vector3(1024f, 1024f, 1024f); break; } case DensityVolumeResolution.VeryHigh: { resolution = new Vector3(2048f, 2048f, 2048f); break; } case DensityVolumeResolution.Ultra: { resolution = new Vector3(4096f, 4096f, 4096f); break; } case DensityVolumeResolution.Custom: { resolution = profile.m_tiling; break; } } return resolution; } /// /// Gets the size effect type /// /// /// private Vector3 GetEffectSizeType(DensityVolumeProfile profile) { Vector3 size = new Vector3(5f, 5f, 5f); if (profile == null) { return size; } switch (profile.m_effectType) { case DensityVolumeEffectType.VeryLightHase: { size = new Vector3(10f, 10f, 10f); break; } case DensityVolumeEffectType.LightHase: { size = new Vector3(30f, 30f, 30f); break; } case DensityVolumeEffectType.ModerateHase: { size = new Vector3(55f, 55f, 55f); break; } case DensityVolumeEffectType.HighHase: { size = new Vector3(120f, 120f, 120f); break; } case DensityVolumeEffectType.ExtremeHase: { size = new Vector3(1000f, 1000f, 1000f); break; } case DensityVolumeEffectType.Custom: { size = profile.m_size; break; } } return size; } /// /// Used to Initialize /// private void Initialize() { transform.hideFlags = HideFlags.HideInInspector; m_densityVolumeCheckTimer = UnityEngine.Random.Range(m_densityVolumeCheck.x, m_densityVolumeCheck.y); GetDensityVolumes(); ApplyChanges(); m_mainCamera = GaiaUtils.GetCamera(); #if UNITY_EDITOR if (!Application.isPlaying) { EditorApplication.update -= EditorUpdateVolumeController; EditorApplication.update += EditorUpdateVolumeController; } else { EditorApplication.update -= EditorUpdateVolumeController; } #endif } /// /// Gets or creates a density volume /// /// #if UNITY_2021_2_OR_NEWER private LocalVolumetricFog CreateOrGetVolume() { GameObject DensityVolumeObject = GameObject.Find(DensityVolumeName); LocalVolumetricFog volume = FindObjectOfType(); if (DensityVolumeObject == null) { if (volume == null) { DensityVolumeObject = new GameObject(DensityVolumeName); volume = DensityVolumeObject.AddComponent(); } } else { volume = DensityVolumeObject.GetComponent(); } return volume; } #else private DensityVolume CreateOrGetVolume() { GameObject DensityVolumeObject = GameObject.Find(DensityVolumeName); DensityVolume volume = FindObjectOfType(); if (DensityVolumeObject == null) { if (volume == null) { DensityVolumeObject = new GameObject(DensityVolumeName); volume = DensityVolumeObject.AddComponent(); } } else { volume = DensityVolumeObject.GetComponent(); } return volume; } #endif /// /// Updates the density volume transform /// /// /// private void UpdateVolumeTransform(Vector3 position, Quaternion rotation) { m_volume.transform.SetPositionAndRotation(position, rotation); } /// /// Sets the volume state based on the isEnabled bool /// /// private void SetDensityVolumeState(bool inAnotherVolume) { if (m_volume != null) { m_blendTime += Time.deltaTime / m_densityVolumeBlendTime; if (inAnotherVolume) { m_volume.parameters.albedo = Color.Lerp(m_volume.parameters.albedo, Color.black, m_blendTime); if (m_blendTime >= 1f) { m_volume.enabled = false; m_processVolumeBlend = false; } } else { m_volume.enabled = true; m_volume.parameters.albedo = Color.Lerp(m_volume.parameters.albedo, DensityVolumeProfile.m_singleScatteringAlbedo, m_blendTime); if (m_blendTime >= 1f) { m_processVolumeBlend = false; } } } } /// /// Gets all other volumes in the scene /// private void GetDensityVolumes() { m_densityVolumes.Clear(); HDRPDensityVolumeComponent[] volumes = FindObjectsOfType(); if (volumes.Length > 1) { foreach (HDRPDensityVolumeComponent densityVolume in volumes) { densityVolume.Setup(); m_densityVolumes.Add(densityVolume); } } } /// /// Checks to see if you are in a volume /// /// private bool IsInAnotherVolume(Camera camera) { if (m_densityVolumes.Count > 0) { foreach (HDRPDensityVolumeComponent volume in m_densityVolumes) { if (volume.IsInBounds(camera)) { return true; } } } return false; } #endregion #region Editor Utils #if UNITY_EDITOR /// /// Gets the editor scene view camera /// /// private Camera GetEditorSceneViewCamera() { Camera sceneCamera = null; if (SceneView.lastActiveSceneView != null) { sceneCamera = SceneView.lastActiveSceneView.camera; } return sceneCamera; } /// /// Editor update function that is called when the application is not playing /// /// private void EditorUpdateVolumeController() { if (m_editorMainCamera == null) { m_editorMainCamera = GetEditorSceneViewCamera(); return; } if (m_volume == null) { return; } Vector3 position = m_editorMainCamera.transform.position; position += -m_editorMainCamera.transform.forward * 1.5f; UpdateVolumeTransform(position, Quaternion.identity); if (!m_processVolumeBlend) { bool newValue = IsInAnotherVolume(m_editorMainCamera); if (m_lastBoundsVolumeState != newValue) { m_blendTime = 0f; m_lastBoundsVolumeState = newValue; m_processVolumeBlend = true; } } if (m_processVolumeBlend) { SetDensityVolumeState(m_lastBoundsVolumeState); } UnityEditorInternal.InternalEditorUtility.SetIsInspectorExpanded(m_volume, false); } #endif #endregion #region Public Static Functions /// /// Global function to create the global density volume /// public static void CreateGaiaHDRPDensityVolume() { GameObject DensityVolumeObject = GameObject.Find(DensityVolumeName); #if UNITY_2021_2_OR_NEWER if (DensityVolumeObject == null) { LocalVolumetricFog volume = FindObjectOfType(); if (volume == null) { DensityVolumeObject = new GameObject(DensityVolumeName); DensityVolumeObject.AddComponent(); } else { DensityVolumeObject = volume.gameObject; DensityVolumeObject.name = DensityVolumeName; } } else { DensityVolumeObject.GetComponent(); } #else if (DensityVolumeObject == null) { DensityVolume volume = FindObjectOfType(); if (volume == null) { DensityVolumeObject = new GameObject(DensityVolumeName); DensityVolumeObject.AddComponent(); } else { DensityVolumeObject = volume.gameObject; DensityVolumeObject.name = DensityVolumeName; } } else { DensityVolumeObject.GetComponent(); } #endif HDRPDensityVolumeController controller = DensityVolumeObject.GetComponent(); if (controller == null) { controller = DensityVolumeObject.AddComponent(); } #if UNITY_EDITOR controller.DensityVolumeProfile.m_texture = AssetDatabase.LoadAssetAtPath(GaiaUtils.GetAssetPath("Fog Noise Texture 3D.asset")); #endif controller.DensityVolumeProfile.m_scrollSpeed = new Vector3(0f, 0f, 0.05f); Terrain terrain = Terrain.activeTerrain; float size = 512f; if (terrain != null) { size = terrain.terrainData.size.x; } controller.DensityVolumeProfile.m_tiling = new Vector3(size, size,size); controller.DensityVolumeProfile.m_size = new Vector3(40f, 40f, 40f); controller.ApplyChanges(); GameObject gaiaRuntime = GameObject.Find(GaiaConstants.gaiaLightingObject); if (gaiaRuntime != null) { DensityVolumeObject.transform.SetParent(gaiaRuntime.transform); } } /// /// Removes Gaia HDRP Density Volume from the scene /// public static void RemoveGaiaHDRPDensityVolume() { if (Instance != null) { DestroyImmediate(Instance.gameObject); } else { HDRPDensityVolumeController densityVolume = FindObjectOfType(); if (densityVolume != null) { DestroyImmediate(densityVolume.gameObject); } } } /// /// Applies the changes /// /// public static HDRPDensityVolumeController ApplyChanges(GaiaLightingProfileValues profile) { if (Instance != null) { Instance.DensityVolumeProfile.m_effectType = profile.m_densityVolumeEffectType; Instance.DensityVolumeProfile.m_size = profile.m_customDensityVolumeEffectType; Instance.DensityVolumeProfile.m_resolution = profile.m_densityVolumeResolution; Instance.DensityVolumeProfile.m_tiling = profile.m_customDensityVolumeResolution; Instance.ApplyChanges(); return Instance; } return null; } #endregion } } #endif