forked from cgvr/DeltaVR
		
	
		
			
				
	
	
		
			313 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Collections.Generic;
 | 
						|
using System.Linq;
 | 
						|
 | 
						|
namespace UnityEngine.XR.Content.Rendering
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// The HighlightController manages scripts that highlight objects in some way - those that inherit from IMaterialHighlight
 | 
						|
    /// It is in charge of locating all applicable renderers, and swapping/doing additional drawing passes as needed to represent the highlights
 | 
						|
    /// </summary>
 | 
						|
    [System.Serializable]
 | 
						|
    public class HighlightController
 | 
						|
    {
 | 
						|
        // Local method use only -- created here to reduce garbage collection. Collections must be cleared before use
 | 
						|
        static readonly List<Renderer> k_RendererComponents = new List<Renderer>();
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Holds additional data for renderers that need additional drawing passes
 | 
						|
        /// This is specically MeshRenderers with more than one submesh - we can't just extend the material array
 | 
						|
        /// to get additional drawing passes, so we use this data to draw them manually.
 | 
						|
        /// </summary>
 | 
						|
        class CustomHighlightLayer
 | 
						|
        {
 | 
						|
            internal Material[] m_HighlightMaterials;
 | 
						|
            internal Mesh m_SharedMesh;
 | 
						|
            internal Transform m_Transform;
 | 
						|
        }
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Used to set the mode of capturing renderers on an object or to use only manually set renderers.")]
 | 
						|
        RendererCaptureDepth m_RendererCaptureDepth = RendererCaptureDepth.AllChildRenderers;
 | 
						|
 | 
						|
        [SerializeField]
 | 
						|
        [Tooltip("Manually set renderers to be affected by the highlight")]
 | 
						|
        protected Renderer[] m_ManuallySetRenderers = new Renderer[0];
 | 
						|
 | 
						|
        // Cached data about any renderers that will be highlighted, and materials that are swapped in and out
 | 
						|
        int m_MaterialAdditions = 0;
 | 
						|
 | 
						|
        List<IMaterialHighlight> m_CacheUsers = new List<IMaterialHighlight>();
 | 
						|
        HashSet<Renderer> m_Renderers = new HashSet<Renderer>();
 | 
						|
        Dictionary<int, Material[]> m_OriginalMaterials = new Dictionary<int, Material[]>();
 | 
						|
        Dictionary<int, Material[]> m_HighlightMaterials = new Dictionary<int, Material[]>();
 | 
						|
        Dictionary<int, CustomHighlightLayer> m_CustomLayerMaterials = new Dictionary<int, CustomHighlightLayer>();
 | 
						|
 | 
						|
        bool m_DelayedUnhighlight = false;
 | 
						|
        bool m_Highlighting = false;
 | 
						|
        float m_UnhighlightTimer = 0.0f;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// The transform that will be highlighted - it is searched for any child renderers
 | 
						|
        /// </summary>
 | 
						|
        public Transform rendererSource { get; set; }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Registers a highlight script - this will provide materials to replace or layer when highlighting an object
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="cacheUser">The highlight script to apply to the cached child renderers</param>
 | 
						|
        public void RegisterCacheUser(IMaterialHighlight cacheUser)
 | 
						|
        {
 | 
						|
            if (cacheUser.highlightMode == MaterialHighlightMode.Layer)
 | 
						|
                m_MaterialAdditions++;
 | 
						|
 | 
						|
            // Set cache user to know about this
 | 
						|
            m_CacheUsers.Add(cacheUser);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Unregisters a highlight script so that it will no longer influence the cached renderers
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="cacheUser">The highlight script to remove from influencing renderers</param>
 | 
						|
        public void UnregisterCacheUser(IMaterialHighlight cacheUser)
 | 
						|
        {
 | 
						|
            if (cacheUser.highlightMode == MaterialHighlightMode.Layer)
 | 
						|
                m_MaterialAdditions--;
 | 
						|
 | 
						|
            m_CacheUsers.Remove(cacheUser);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Ensures that all renderers, materials, and highlight scripts have their materials ready
 | 
						|
        /// </summary>
 | 
						|
        public void Initialize()
 | 
						|
        {
 | 
						|
            if (rendererSource == null)
 | 
						|
            {
 | 
						|
                Debug.LogError("Trying to use a Highlight Controller before setting the root gameobject!");
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            foreach (var cacheUser in m_CacheUsers)
 | 
						|
            {
 | 
						|
                cacheUser.Initialize();
 | 
						|
            }
 | 
						|
 | 
						|
            // Cache the renderers
 | 
						|
            UpdateRendererCache();
 | 
						|
 | 
						|
            // Generate the original material list and implement the materials from the included highlights
 | 
						|
            UpdateMaterialCache();
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Ensures that any materials or other objects allocated by highlight scripts can be cleaned up
 | 
						|
        /// </summary>
 | 
						|
        public void Deinitialize()
 | 
						|
        {
 | 
						|
            foreach (var cacheUser in m_CacheUsers)
 | 
						|
            {
 | 
						|
                if (cacheUser != null)
 | 
						|
                    cacheUser.Deinitialize();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Handles fading out materials when highlights are disabled and also manually drawing layered highlights as needed
 | 
						|
        /// </summary>
 | 
						|
        public void Update()
 | 
						|
        {
 | 
						|
            if (m_DelayedUnhighlight)
 | 
						|
            {
 | 
						|
                m_UnhighlightTimer -= Time.deltaTime;
 | 
						|
                if (m_UnhighlightTimer <= 0.0f)
 | 
						|
                {
 | 
						|
                    m_DelayedUnhighlight = false;
 | 
						|
                    m_Highlighting = false;
 | 
						|
                    UpdateMaterialCache();
 | 
						|
                    foreach (var renderer in m_Renderers)
 | 
						|
                    {
 | 
						|
                        var rendererID = renderer.GetInstanceID();
 | 
						|
                        renderer.materials = m_OriginalMaterials[rendererID];
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (m_Highlighting && m_CustomLayerMaterials.Count > 0)
 | 
						|
            {
 | 
						|
                foreach (var customLayer in m_CustomLayerMaterials.Values)
 | 
						|
                {
 | 
						|
                    for (var matIndex = 0; matIndex < customLayer.m_HighlightMaterials.Length; ++matIndex)
 | 
						|
                    {
 | 
						|
                        for (var submeshIndex = 0; submeshIndex < customLayer.m_SharedMesh.subMeshCount; ++submeshIndex)
 | 
						|
                        {
 | 
						|
                            Graphics.DrawMesh(
 | 
						|
                                customLayer.m_SharedMesh,
 | 
						|
                                customLayer.m_Transform.localToWorldMatrix,
 | 
						|
                                customLayer.m_HighlightMaterials[matIndex],
 | 
						|
                                customLayer.m_Transform.gameObject.layer,
 | 
						|
                                null,
 | 
						|
                                submeshIndex);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Applies highlight materials to all the targeted renderers
 | 
						|
        /// </summary>
 | 
						|
        public void Highlight()
 | 
						|
        {
 | 
						|
            m_DelayedUnhighlight = false;
 | 
						|
            m_Highlighting = true;
 | 
						|
            UpdateMaterialCache();
 | 
						|
            foreach (var renderer in m_Renderers)
 | 
						|
            {
 | 
						|
                var rendererID = renderer.GetInstanceID();
 | 
						|
                renderer.materials = m_HighlightMaterials[rendererID];
 | 
						|
            }
 | 
						|
            foreach (var cacheUser in m_CacheUsers)
 | 
						|
            {
 | 
						|
                if (cacheUser != null)
 | 
						|
                    cacheUser.OnHighlight();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Restores the original materials to all the targeted renderers
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="force">If true, the original materials are restored instantly.  Otherwise, a fade can occur.</param>
 | 
						|
        public void Unhighlight(bool force = false)
 | 
						|
        {
 | 
						|
            UpdateMaterialCache();
 | 
						|
 | 
						|
            var maxDelay = 0.0f;
 | 
						|
            foreach (var cacheUser in m_CacheUsers)
 | 
						|
            {
 | 
						|
                if (cacheUser != null)
 | 
						|
                    maxDelay = Mathf.Max(cacheUser.OnUnhighlight());
 | 
						|
            }
 | 
						|
 | 
						|
            if (maxDelay <= 0.0f)
 | 
						|
            {
 | 
						|
                foreach (var renderer in m_Renderers)
 | 
						|
                {
 | 
						|
                    var rendererID = renderer.GetInstanceID();
 | 
						|
                    renderer.materials = m_OriginalMaterials[rendererID];
 | 
						|
                }
 | 
						|
                m_Highlighting = false;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                m_DelayedUnhighlight = true;
 | 
						|
                m_UnhighlightTimer = maxDelay;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void UpdateRendererCache()
 | 
						|
        {
 | 
						|
            m_Renderers.Clear();
 | 
						|
            m_Renderers.UnionWith(m_ManuallySetRenderers.Where(r => r != null));
 | 
						|
 | 
						|
            switch (m_RendererCaptureDepth)
 | 
						|
            {
 | 
						|
                case RendererCaptureDepth.AllChildRenderers:
 | 
						|
                    rendererSource.GetComponentsInChildren(true, k_RendererComponents);
 | 
						|
 | 
						|
                    foreach (var renderer in k_RendererComponents)
 | 
						|
                    {
 | 
						|
                        var textMesh = renderer.GetComponent<TextMesh>();
 | 
						|
                        var meshFilter = renderer.GetComponent<MeshFilter>();
 | 
						|
 | 
						|
                        if (textMesh == null)
 | 
						|
                            m_Renderers.Add(renderer);
 | 
						|
 | 
						|
                        if (meshFilter != null && meshFilter.mesh.subMeshCount > 1)
 | 
						|
                            m_CustomLayerMaterials.Add(renderer.GetInstanceID(), new CustomHighlightLayer { m_SharedMesh = meshFilter.sharedMesh, m_Transform = renderer.transform });
 | 
						|
                    }
 | 
						|
                    k_RendererComponents.Clear();
 | 
						|
 | 
						|
                    break;
 | 
						|
                case RendererCaptureDepth.CurrentRenderer:
 | 
						|
                    rendererSource.GetComponents(k_RendererComponents);
 | 
						|
 | 
						|
                    foreach (var renderer in k_RendererComponents)
 | 
						|
                    {
 | 
						|
                        var textMesh = renderer.GetComponent<TextMesh>();
 | 
						|
                        var meshFilter = renderer.GetComponent<MeshFilter>();
 | 
						|
 | 
						|
                        if (textMesh == null)
 | 
						|
                            m_Renderers.Add(renderer);
 | 
						|
 | 
						|
                        if (meshFilter != null && meshFilter.mesh.subMeshCount > 1)
 | 
						|
                            m_CustomLayerMaterials.Add(renderer.GetInstanceID(), new CustomHighlightLayer { m_SharedMesh = meshFilter.sharedMesh, m_Transform = renderer.transform });
 | 
						|
                    }
 | 
						|
                    k_RendererComponents.Clear();
 | 
						|
                    break;
 | 
						|
                case RendererCaptureDepth.ManualOnly:
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    Debug.LogError($"{rendererSource.name} highlight has an invalid renderer capture mode {m_RendererCaptureDepth}.", rendererSource);
 | 
						|
                    break;
 | 
						|
            }
 | 
						|
 | 
						|
            if (m_Renderers.Count == 0)
 | 
						|
                Debug.LogWarning($"{rendererSource.name} highlight has no renderers set.", rendererSource);
 | 
						|
        }
 | 
						|
 | 
						|
        void UpdateMaterialCache()
 | 
						|
        {
 | 
						|
            foreach (var renderer in m_Renderers)
 | 
						|
            {
 | 
						|
                var rendererID = renderer.GetInstanceID();
 | 
						|
                if (m_OriginalMaterials.ContainsKey(rendererID))
 | 
						|
                    continue;
 | 
						|
 | 
						|
                var sharedMaterials = renderer.sharedMaterials;
 | 
						|
                var sharedLength = sharedMaterials.Length;
 | 
						|
                m_OriginalMaterials[rendererID] = sharedMaterials;
 | 
						|
 | 
						|
                CustomHighlightLayer highlightLayer = null;
 | 
						|
                Material[] highlightMaterials;
 | 
						|
                Material[] layerMaterials;
 | 
						|
                var addOffset = sharedLength;
 | 
						|
 | 
						|
                if (m_CustomLayerMaterials.TryGetValue(rendererID, out highlightLayer))
 | 
						|
                {
 | 
						|
                    highlightMaterials = new Material[sharedLength];
 | 
						|
                    highlightLayer.m_HighlightMaterials = new Material[m_MaterialAdditions];
 | 
						|
                    layerMaterials = highlightLayer.m_HighlightMaterials;
 | 
						|
                    addOffset = 0;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    highlightMaterials = new Material[sharedLength + m_MaterialAdditions];
 | 
						|
                    layerMaterials = highlightMaterials;
 | 
						|
                }
 | 
						|
 | 
						|
                for (var matIndex = 0; matIndex < sharedLength; matIndex++)
 | 
						|
                {
 | 
						|
                    highlightMaterials[matIndex] = sharedMaterials[matIndex];
 | 
						|
                }
 | 
						|
 | 
						|
                for (var i = 0; i < m_CacheUsers.Count; i++)
 | 
						|
                {
 | 
						|
                    var cacheUser = m_CacheUsers[i];
 | 
						|
                    if (cacheUser.highlightMode == MaterialHighlightMode.Replace)
 | 
						|
                    {
 | 
						|
                        for (var matIndex = 0; matIndex < sharedLength; matIndex++)
 | 
						|
                        {
 | 
						|
                            highlightMaterials[matIndex] = cacheUser.highlightMaterial;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        layerMaterials[addOffset] = cacheUser.highlightMaterial;
 | 
						|
                        addOffset++;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                m_HighlightMaterials[rendererID] = highlightMaterials;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |