using System; using UnityEditor; namespace UnityEngine.Rendering.Universal { /// The scaling mode to apply to decals that use the Decal Projector. public enum DecalScaleMode { /// Ignores the transformation hierarchy and uses the scale values in the Decal Projector component directly. ScaleInvariant, /// Multiplies the lossy scale of the Transform with the Decal Projector's own scale then applies this to the decal. [InspectorName("Inherit from Hierarchy")] InheritFromHierarchy, } /// /// Decal Projector component. /// [ExecuteAlways] #if UNITY_EDITOR [CanEditMultipleObjects] #endif [AddComponentMenu("Rendering/URP Decal Projector")] public class DecalProjector : MonoBehaviour { internal delegate void DecalProjectorAction(DecalProjector decalProjector); internal static event DecalProjectorAction onDecalAdd; internal static event DecalProjectorAction onDecalRemove; internal static event DecalProjectorAction onDecalPropertyChange; internal static event DecalProjectorAction onDecalMaterialChange; internal static Material defaultMaterial { get; set; } internal static bool isSupported => onDecalAdd != null; internal DecalEntity decalEntity { get; set; } [SerializeField] private Material m_Material = null; /// /// The material used by the decal. /// public Material material { get { return m_Material; } set { m_Material = value; OnValidate(); } } [SerializeField] private float m_DrawDistance = 1000.0f; /// /// Distance from camera at which the Decal is not rendered anymore. /// public float drawDistance { get { return m_DrawDistance; } set { m_DrawDistance = Mathf.Max(0f, value); OnValidate(); } } [SerializeField] [Range(0, 1)] private float m_FadeScale = 0.9f; /// /// Percent of the distance from the camera at which this Decal start to fade off. /// public float fadeScale { get { return m_FadeScale; } set { m_FadeScale = Mathf.Clamp01(value); OnValidate(); } } [SerializeField] [Range(0, 180)] private float m_StartAngleFade = 180.0f; /// /// Angle between decal backward orientation and vertex normal of receiving surface at which the Decal start to fade off. /// public float startAngleFade { get { return m_StartAngleFade; } set { m_StartAngleFade = Mathf.Clamp(value, 0.0f, 180.0f); OnValidate(); } } [SerializeField] [Range(0, 180)] private float m_EndAngleFade = 180.0f; /// /// Angle between decal backward orientation and vertex normal of receiving surface at which the Decal end to fade off. /// public float endAngleFade { get { return m_EndAngleFade; } set { m_EndAngleFade = Mathf.Clamp(value, m_StartAngleFade, 180.0f); OnValidate(); } } [SerializeField] private Vector2 m_UVScale = new Vector2(1, 1); /// /// Tilling of the UV of the projected texture. /// public Vector2 uvScale { get { return m_UVScale; } set { m_UVScale = value; OnValidate(); } } [SerializeField] private Vector2 m_UVBias = new Vector2(0, 0); /// /// Offset of the UV of the projected texture. /// public Vector2 uvBias { get { return m_UVBias; } set { m_UVBias = value; OnValidate(); } } [SerializeField] private DecalScaleMode m_ScaleMode = DecalScaleMode.ScaleInvariant; /// /// The scaling mode to apply to decals that use this Decal Projector. /// public DecalScaleMode scaleMode { get => m_ScaleMode; set { m_ScaleMode = value; OnValidate(); } } [SerializeField] internal Vector3 m_Offset = new Vector3(0, 0, 0.5f); /// /// Change the offset position. /// Do not expose: Could be changed by the inspector when manipulating the gizmo. /// public Vector3 pivot { get { return m_Offset; } set { m_Offset = value; OnValidate(); } } [SerializeField] internal Vector3 m_Size = new Vector3(1, 1, 1); /// /// The size of the projection volume. /// public Vector3 size { get { return m_Size; } set { m_Size = value; OnValidate(); } } [SerializeField] [Range(0, 1)] private float m_FadeFactor = 1.0f; /// /// Controls the transparency of the decal. /// public float fadeFactor { get { return m_FadeFactor; } set { m_FadeFactor = Mathf.Clamp01(value); OnValidate(); } } private Material m_OldMaterial = null; /// A scale that should be used for rendering and handles. internal Vector3 effectiveScale => m_ScaleMode == DecalScaleMode.InheritFromHierarchy ? transform.lossyScale : Vector3.one; /// current size in a way the DecalSystem will be able to use it internal Vector3 decalSize => new Vector3(m_Size.x, m_Size.z, m_Size.y); /// current size in a way the DecalSystem will be able to use it internal Vector3 decalOffset => new Vector3(m_Offset.x, -m_Offset.z, m_Offset.y); /// current uv parameters in a way the DecalSystem will be able to use it internal Vector4 uvScaleBias => new Vector4(m_UVScale.x, m_UVScale.y, m_UVBias.x, m_UVBias.y); void InitMaterial() { if (m_Material == null) { #if UNITY_EDITOR m_Material = defaultMaterial; #endif } } void OnEnable() { InitMaterial(); m_OldMaterial = m_Material; onDecalAdd?.Invoke(this); #if UNITY_EDITOR // Handle scene visibility UnityEditor.SceneVisibilityManager.visibilityChanged += UpdateDecalVisibility; #endif } #if UNITY_EDITOR void UpdateDecalVisibility() { // Fade out the decal when it is hidden by the scene visibility if (UnityEditor.SceneVisibilityManager.instance.IsHidden(gameObject)) { onDecalRemove?.Invoke(this); } else { onDecalAdd?.Invoke(this); onDecalPropertyChange?.Invoke(this); // Scene culling mask may have changed. } } #endif void OnDisable() { onDecalRemove?.Invoke(this); #if UNITY_EDITOR UnityEditor.SceneVisibilityManager.visibilityChanged -= UpdateDecalVisibility; #endif } internal void OnValidate() { if (!isActiveAndEnabled) return; if (m_Material != m_OldMaterial) { onDecalMaterialChange?.Invoke(this); m_OldMaterial = m_Material; } else onDecalPropertyChange?.Invoke(this); } public bool IsValid() { if (material == null) return false; if (material.FindPass(DecalShaderPassNames.DBufferProjector) != -1) return true; if (material.FindPass(DecalShaderPassNames.DecalProjectorForwardEmissive) != -1) return true; if (material.FindPass(DecalShaderPassNames.DecalScreenSpaceProjector) != -1) return true; if (material.FindPass(DecalShaderPassNames.DecalGBufferProjector) != -1) return true; return false; } } }