forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			324 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
namespace UnityEngine.XR.Content.Rendering
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// Draws an outline on an object when highlighting. Can either transition the color and or size of the out line
 | 
						|
    /// as selected or be instant on.
 | 
						|
    /// </summary>
 | 
						|
    public class OutlineHighlight : MonoBehaviour, IMaterialHighlight
 | 
						|
    {
 | 
						|
        enum OutlineSource
 | 
						|
        {
 | 
						|
            Shader = 0,
 | 
						|
            Material
 | 
						|
        }
 | 
						|
 | 
						|
        const float k_OutlineWidth = 0.005f;
 | 
						|
        const string k_ShaderColorParameter = "_Color";
 | 
						|
        const string k_ShaderWidthParameter = "g_flOutlineWidth";
 | 
						|
        static readonly int k_GFlOutlineWidth = Shader.PropertyToID(k_ShaderWidthParameter);
 | 
						|
        static readonly int k_Color = Shader.PropertyToID(k_ShaderColorParameter);
 | 
						|
 | 
						|
#pragma warning disable 649
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("How the highlight material will be applied to the renderer's material array.")]
 | 
						|
        MaterialHighlightMode m_HighlightMode = MaterialHighlightMode.Replace;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Selects source for the highlight material. Either using a shader or material.")]
 | 
						|
        OutlineSource m_OutlineSource = OutlineSource.Shader;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Outline highlight shader to use for highlight material.")]
 | 
						|
        Shader m_Shader;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Material used for drawing the outline highlight.")]
 | 
						|
        Material m_HighlightMaterial;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Transition outline width over time")]
 | 
						|
        bool m_TransitionWidth;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("The outline width used if no transition or the end value for transition width of outline")]
 | 
						|
        [Range(0f, 1f)]
 | 
						|
        float m_OutlineScale = 1f;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Starting value for transition width of outline")]
 | 
						|
        [Range(0f, 1f)]
 | 
						|
        float m_StartingOutlineScale;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Transition outline color over time")]
 | 
						|
        bool m_TransitionColor;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("The outline color used if no transition or the end value for transition color of outline")]
 | 
						|
        Color m_OutlineColor = new Color(0.3f, 0.6f, 1f, 1f);
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Starting value for transition color of outline")]
 | 
						|
        Color m_StartingOutlineColor = Color.black;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Time it takes to transition from start to end on highlight")]
 | 
						|
        float m_TransitionDuration = 0.3f;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Use material values for starting color and width")]
 | 
						|
        bool m_StartWithMaterialValues;
 | 
						|
#pragma warning restore 649
 | 
						|
 | 
						|
        Material m_InstanceOutlineMaterial;
 | 
						|
 | 
						|
        bool m_Animating = false;
 | 
						|
        bool m_AnimatingIn = false;
 | 
						|
        float m_TransitionTimer = 0.0f;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Time it takes to transition from start to end on highlight
 | 
						|
        /// </summary>
 | 
						|
        public float transitionDuration
 | 
						|
        {
 | 
						|
            get => m_TransitionDuration;
 | 
						|
            set => m_TransitionDuration = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Transition outline width over time
 | 
						|
        /// </summary>
 | 
						|
        public bool transitionWidth
 | 
						|
        {
 | 
						|
            get => m_TransitionWidth;
 | 
						|
            set => m_TransitionWidth = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Transition outline color over time
 | 
						|
        /// </summary>
 | 
						|
        public bool transitionColor
 | 
						|
        {
 | 
						|
            get => m_TransitionColor;
 | 
						|
            set => m_TransitionColor = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// The outline color used if no transition or the end value for transition color of outline
 | 
						|
        /// </summary>
 | 
						|
        public Color outlineColor
 | 
						|
        {
 | 
						|
            get => m_OutlineColor;
 | 
						|
            set => m_OutlineColor = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Starting value for transition color of outline
 | 
						|
        /// </summary>
 | 
						|
        public Color startingOutlineColor
 | 
						|
        {
 | 
						|
            get => m_StartingOutlineColor;
 | 
						|
            set => m_StartingOutlineColor = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// The outline width used if no transition or the end value for transition width of outline
 | 
						|
        /// </summary>
 | 
						|
        public float outlineScale
 | 
						|
        {
 | 
						|
            get => m_OutlineScale;
 | 
						|
            set => m_OutlineScale = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Starting value for transition width of outline
 | 
						|
        /// </summary>
 | 
						|
        public float startingOutlineScale
 | 
						|
        {
 | 
						|
            get => m_StartingOutlineScale;
 | 
						|
            set => m_StartingOutlineScale = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// A 0-1 relative outline scale that takes into account the ideal base outline width,
 | 
						|
        /// multiplied by the user specified value. This allows for more intuitive adjustment of the value.
 | 
						|
        /// This is the value used if there is no transition otherwise this is the end value of a transition.
 | 
						|
        /// </summary>
 | 
						|
        float relativeOutlineScale => outlineScale * k_OutlineWidth;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// A 0-1 relative outline scale that takes into account the ideal base outline width,
 | 
						|
        /// multiplied by the user specified value. This allows for more intuitive adjustment of the value.
 | 
						|
        /// This is the start value of a transition otherwise this value is not used.
 | 
						|
        /// </summary>
 | 
						|
        float startingRelativeOutlineScale => startingOutlineScale * k_OutlineWidth;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// How the highlight material will be applied to the renderer's material array.
 | 
						|
        /// </summary>
 | 
						|
        public MaterialHighlightMode highlightMode
 | 
						|
        {
 | 
						|
            get => m_HighlightMode;
 | 
						|
            set => m_HighlightMode = value;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Material to use for highlighting
 | 
						|
        /// </summary>
 | 
						|
        public Material highlightMaterial => m_InstanceOutlineMaterial;
 | 
						|
 | 
						|
        void IMaterialHighlight.Initialize()
 | 
						|
        {
 | 
						|
            InstantiateHighlightMaterial();
 | 
						|
 | 
						|
            if (m_StartWithMaterialValues)
 | 
						|
            {
 | 
						|
                startingOutlineScale = m_HighlightMaterial.GetFloat(k_GFlOutlineWidth) / k_OutlineWidth;
 | 
						|
                startingOutlineColor = m_HighlightMaterial.GetColor(k_Color);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void IMaterialHighlight.Deinitialize()
 | 
						|
        {
 | 
						|
            if (m_InstanceOutlineMaterial)
 | 
						|
            {
 | 
						|
                Destroy(m_InstanceOutlineMaterial);
 | 
						|
                m_InstanceOutlineMaterial = null;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected void OnDestroy()
 | 
						|
        {
 | 
						|
            ((IMaterialHighlight)(this)).Deinitialize();
 | 
						|
        }
 | 
						|
 | 
						|
        protected void Update()
 | 
						|
        {
 | 
						|
            if (m_Animating)
 | 
						|
            {
 | 
						|
                m_TransitionTimer += Time.unscaledDeltaTime;
 | 
						|
 | 
						|
                var transitionPercent = Mathf.Clamp01(m_TransitionTimer / m_TransitionDuration);
 | 
						|
                var alpha = m_AnimatingIn ? transitionPercent : (1.0f - transitionPercent);
 | 
						|
 | 
						|
                if (m_TransitionWidth)
 | 
						|
                {
 | 
						|
                    var size = Mathf.Lerp(startingRelativeOutlineScale, relativeOutlineScale, alpha);
 | 
						|
                    m_InstanceOutlineMaterial.SetFloat(k_GFlOutlineWidth, size);
 | 
						|
                }
 | 
						|
 | 
						|
                if (m_TransitionColor)
 | 
						|
                {
 | 
						|
                    var color = Color.Lerp(startingOutlineColor, outlineColor, alpha);
 | 
						|
                    m_InstanceOutlineMaterial.SetColor(k_Color, color);
 | 
						|
                }
 | 
						|
 | 
						|
                if (transitionPercent >= 1.0f)
 | 
						|
                {
 | 
						|
                    m_TransitionTimer = 0.0f;
 | 
						|
                    m_Animating = false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void IMaterialHighlight.OnHighlight()
 | 
						|
        {
 | 
						|
            if (m_InstanceOutlineMaterial == null)
 | 
						|
                return;
 | 
						|
 | 
						|
            PlayPulseAnimation();
 | 
						|
        }
 | 
						|
 | 
						|
        float IMaterialHighlight.OnUnhighlight()
 | 
						|
        {
 | 
						|
            if (m_InstanceOutlineMaterial == null)
 | 
						|
                return 0.0f;
 | 
						|
 | 
						|
            PlayPulseAnimation(false);
 | 
						|
 | 
						|
            if (!m_TransitionWidth && !m_TransitionColor || Mathf.Approximately(m_TransitionDuration, 0f))
 | 
						|
                return 0.0f;
 | 
						|
            else
 | 
						|
                return m_TransitionDuration;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Pulses the highlight - even if it is already active
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="pulseUp">Whether the highlight is fading in or out</param>
 | 
						|
        public void PlayPulseAnimation(bool pulseUp = true)
 | 
						|
        {
 | 
						|
            if (!m_TransitionWidth && !m_TransitionColor || Mathf.Approximately(m_TransitionDuration, 0f))
 | 
						|
            {
 | 
						|
                m_InstanceOutlineMaterial.SetFloat(k_GFlOutlineWidth, relativeOutlineScale);
 | 
						|
                m_InstanceOutlineMaterial.SetColor(k_Color, m_OutlineColor);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // If the same animation is already occurring, we just let it play.  If it is playing backwards, we seamlessly transition
 | 
						|
                if (m_Animating)
 | 
						|
                {
 | 
						|
                    if (m_AnimatingIn != pulseUp)
 | 
						|
                    {
 | 
						|
                        m_TransitionTimer = 1.0f - m_TransitionTimer;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    m_Animating = true;
 | 
						|
                    m_AnimatingIn = pulseUp;
 | 
						|
                    m_TransitionTimer = 0.0f;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void InstantiateHighlightMaterial()
 | 
						|
        {
 | 
						|
            if (m_Shader == null && m_HighlightMaterial == null)
 | 
						|
            {
 | 
						|
                Debug.LogError($"{gameObject.name} has no highlight material or shader set!", this);
 | 
						|
                enabled = false;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            const string outlineMaterialName = "Outline Material Instance";
 | 
						|
 | 
						|
            switch (m_OutlineSource)
 | 
						|
            {
 | 
						|
                case OutlineSource.Material:
 | 
						|
                    if (m_HighlightMaterial == null)
 | 
						|
                    {
 | 
						|
                        Debug.LogError($"{gameObject.name} Outline highlight has no material assigned. Please assign outline material.", this);
 | 
						|
                        enabled = false;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    m_InstanceOutlineMaterial = new Material(m_HighlightMaterial) { name = outlineMaterialName };
 | 
						|
                    break;
 | 
						|
                case OutlineSource.Shader:
 | 
						|
                    if (m_Shader == null)
 | 
						|
                    {
 | 
						|
                        Debug.LogError($"{gameObject.name} Outline highlight has no shader assigned. Please assign outline shader. ", this);
 | 
						|
                        enabled = false;
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
 | 
						|
                    m_InstanceOutlineMaterial = new Material(m_Shader) { name = outlineMaterialName };
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    Debug.LogError($"{gameObject.name} Outline highlight has an invalid highlight mode {m_OutlineSource}.", this);
 | 
						|
                    enabled = false;
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        protected void OnValidate()
 | 
						|
        {
 | 
						|
            if (m_TransitionDuration < 0f)
 | 
						|
            {
 | 
						|
                m_TransitionDuration = 0f;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |