123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- using System;
- using UnityEngine;
- namespace Unity.XRTools.Rendering
- {
- /// <summary>
- /// A unified base class for the XR Line Renderer and XR Trail Renderer
- /// </summary>
- [RequireComponent(typeof(MeshRenderer))]
- [RequireComponent(typeof(MeshFilter))]
- [ExecuteInEditMode]
- [DisallowMultipleComponent]
- public abstract class MeshChainRenderer : MonoBehaviour
- {
- static readonly GradientColorKey k_DefaultStartColor = new GradientColorKey(Color.white, 0);
- static readonly GradientColorKey k_DefaultEndColor = new GradientColorKey(Color.white, 1);
- static readonly GradientAlphaKey k_DefaultStartAlpha = new GradientAlphaKey(1, 0);
- static readonly GradientAlphaKey k_DefaultEndAlpha = new GradientAlphaKey(1, 1);
- /// <summary>
- /// Materials to use when rendering.
- /// </summary>
- [SerializeField]
- [Tooltip("Materials to use when rendering.")]
- protected Material[] m_Materials;
- /// <summary>
- /// The multiplier applied to the curve, describing the width (in world space) along the line.
- /// </summary>
- [SerializeField]
- [Tooltip("The multiplier applied to the curve, describing the width (in world space) along the line.")]
- protected float m_Width = 1.0f;
- /// <summary>
- /// The curve describing the width of the line at various points along its length.
- /// </summary>
- [SerializeField]
- [Tooltip("The curve describing the width of the line at various points along its length.")]
- protected AnimationCurve m_WidthCurve = new AnimationCurve();
- /// <summary>
- /// The gradient describing color along the line.
- /// </summary>
- [SerializeField]
- [Tooltip("The gradient describing color along the line.")]
- protected Gradient m_Color = new Gradient();
- /// <summary>
- /// The MeshRenderer used to render the line
- /// </summary>
- [SerializeField]
- [HideInInspector]
- protected MeshRenderer m_MeshRenderer;
- /// <summary>
- /// Cached mesh data
- /// </summary>
- protected XRMeshChain m_XRMeshData;
- /// <summary>
- /// Whether the mesh data needs to be refreshed
- /// </summary>
- protected bool m_MeshNeedsRefreshing;
- /// <summary>
- /// The step size
- /// </summary>
- protected float m_StepSize = 1.0f;
- /// <summary>
- /// Returns the first instantiated Material assigned to the renderer.
- /// </summary>
- public virtual Material material
- {
- get { return m_MeshRenderer.material; }
- set { m_MeshRenderer.material = value; }
- }
- /// <summary>
- /// Returns all the instantiated materials of this object.
- /// </summary>
- public virtual Material[] materials
- {
- get { return m_MeshRenderer.materials; }
- set { m_MeshRenderer.materials = value; }
- }
- /// <summary>
- /// Returns the shared material of this object.
- /// </summary>
- public virtual Material sharedMaterial
- {
- get { return m_MeshRenderer.sharedMaterial; }
- set { m_MeshRenderer.sharedMaterial = value; }
- }
- /// <summary>
- /// Returns all shared materials of this object.
- /// </summary>
- public virtual Material[] SharedMaterials
- {
- get { return m_MeshRenderer.materials; }
- set { m_MeshRenderer.sharedMaterials = value; }
- }
- /// <summary>
- /// Set the width at the start of the line.
- /// </summary>
- public float widthStart
- {
- get { return m_WidthCurve.Evaluate(0) * m_Width; }
- set
- {
- if (m_Width == 0 || float.IsNaN(m_Width))
- return;
- var keys = m_WidthCurve.keys;
- keys[0].value = value / m_Width;
- m_WidthCurve.keys = keys;
- UpdateWidth();
- }
- }
- /// <summary>
- /// Set the width at the end of the line.
- /// </summary>
- public float widthEnd
- {
- get { return m_WidthCurve.Evaluate(1) * m_Width; }
- set
- {
- if (m_Width == 0 || float.IsNaN(m_Width))
- return;
- var keys = m_WidthCurve.keys;
- var lastIndex = keys.Length - 1;
- keys[lastIndex].value = value / m_Width;
- m_WidthCurve.keys = keys;
- UpdateWidth();
- }
- }
- /// <summary>
- /// Set an overall multiplier that is applied to the LineRenderer.widthCurve to get the final width of the line.
- /// </summary>
- public float widthMultiplier
- {
- get { return m_Width; }
- set
- {
- m_Width = value;
- UpdateWidth();
- }
- }
- /// <summary>
- /// Set the curve describing the width of the line at various points along its length.
- /// </summary>
- public AnimationCurve widthCurve
- {
- get { return m_WidthCurve; }
- set
- {
- m_WidthCurve = value ?? new AnimationCurve(new Keyframe(0, 1.0f));
- UpdateWidth();
- }
- }
- /// <summary>
- /// Set the color gradient describing the color of the line at various points along its length.
- /// </summary>
- public Gradient colorGradient
- {
- get { return m_Color; }
- set
- {
- if (m_Color == value)
- {
- return;
- }
- m_Color = value ?? new Gradient
- {
- alphaKeys = new[] { k_DefaultStartAlpha, k_DefaultEndAlpha },
- colorKeys = new[] { k_DefaultStartColor, k_DefaultEndColor },
- mode = GradientMode.Blend
- };
- UpdateColors();
- }
- }
- /// <summary>
- /// Set the color at the start of the line.
- /// </summary>
- public Color colorStart
- {
- get { return m_Color.Evaluate(0); }
- set
- {
- var colorKeys = m_Color.colorKeys;
- var alphaKeys = m_Color.alphaKeys;
- var flatColor = value;
- flatColor.a = 1.0f;
- colorKeys[0].color = flatColor;
- alphaKeys[0].alpha = value.a;
- m_Color.colorKeys = colorKeys;
- m_Color.alphaKeys = alphaKeys;
- UpdateColors();
- }
- }
- /// <summary>
- /// Set the color at the end of the line.
- /// </summary>
- public Color colorEnd
- {
- get { return m_Color.Evaluate(1); }
- set
- {
- var colorKeys = m_Color.colorKeys;
- var alphaKeys = m_Color.alphaKeys;
- var lastColorIndex = colorKeys.Length - 1;
- var lastAlphaIndex = alphaKeys.Length - 1;
- var flatColor = value;
- flatColor.a = 1.0f;
- colorKeys[lastColorIndex].color = flatColor;
- alphaKeys[lastAlphaIndex].alpha = value.a;
- m_Color.colorKeys = colorKeys;
- m_Color.alphaKeys = alphaKeys;
- UpdateColors();
- }
- }
- /// <summary>
- /// Resets the width to a straight curve at the given value
- /// </summary>
- /// <param name="newWidth">The new width for the curve to display</param>
- public void SetTotalWidth(float newWidth)
- {
- m_Width = newWidth;
- m_WidthCurve = new AnimationCurve(new Keyframe(0, 1.0f));
- UpdateWidth();
- }
- /// <summary>
- /// Resets the color to a single value
- /// </summary>
- /// <param name="newColor">The new color for the curve to display</param>
- public void SetTotalColor(Color newColor)
- {
- var flatColor = newColor;
- flatColor.a = 1.0f;
- m_Color = new Gradient
- {
- alphaKeys = new[] { new GradientAlphaKey(newColor.a, 0), new GradientAlphaKey(newColor.a, 1), },
- colorKeys = new[] { new GradientColorKey(flatColor, 0), new GradientColorKey(flatColor, 1) },
- mode = GradientMode.Blend
- };
- UpdateColors();
- }
- void OnValidate()
- {
- SetupMeshBackend();
- if (NeedsReinitialize())
- {
- #if UNITY_EDITOR
- UnityEditor.EditorApplication.delayCall += EditorCheckForUpdate;
- #endif
- }
- else
- {
- EditorCheckForUpdate();
- }
- }
- /// <summary>
- /// Manually trigger the mesh data to be refreshed. This is automatically done in LateUpdate if anything has changed.
- /// </summary>
- public void RefreshMesh()
- {
- m_XRMeshData.RefreshMesh();
- m_MeshNeedsRefreshing = false;
- }
- /// <summary>
- /// Cleans up the visible interface of the MeshRenderer by hiding unneeded components
- /// Also makes sure the animation curves are set up properly to defaults
- /// </summary>
- void SetupMeshBackend()
- {
- if (m_MeshRenderer == null)
- m_MeshRenderer = GetComponent<MeshRenderer>();
- if (m_MeshRenderer.hideFlags == HideFlags.None)
- m_MeshRenderer.hideFlags = HideFlags.HideInInspector;
- var meshFilter = GetComponent<MeshFilter>();
- if (meshFilter.hideFlags == HideFlags.None)
- meshFilter.hideFlags = HideFlags.HideInInspector;
- if (m_Materials == null || m_Materials.Length == 0)
- {
- m_Materials = m_MeshRenderer.sharedMaterials;
- }
- var sharedMaterials = m_MeshRenderer.sharedMaterials;
- var length = sharedMaterials.Length;
- var materialsEqual = true;
- for (var i = 0; i < length; i++)
- {
- if (sharedMaterials[i] != m_Materials[i])
- {
- materialsEqual = false;
- break;
- }
- }
- if (!materialsEqual)
- {
- m_MeshRenderer.sharedMaterials = m_Materials;
- }
- if (m_WidthCurve == null || m_WidthCurve.keys == null || m_WidthCurve.keys.Length == 0)
- {
- m_WidthCurve = new AnimationCurve(new Keyframe(0, 1.0f));
- }
- if (m_Color == null)
- {
- m_Color = new Gradient
- {
- alphaKeys = new[] { k_DefaultStartAlpha, k_DefaultEndAlpha },
- colorKeys = new[] { k_DefaultStartColor, k_DefaultEndColor },
- mode = GradientMode.Blend
- };
- }
- }
- /// <summary>
- /// Makes the sure mesh renderer reference is initialized before any functions try to access it
- /// </summary>
- protected virtual void Awake()
- {
- SetupMeshBackend();
- Initialize();
- }
- /// <summary>
- /// Makes the sure mesh renderer reference is initialized on reset of the component
- /// </summary>
- void Reset()
- {
- SetupMeshBackend();
- Initialize();
- }
- /// <summary>
- /// Ensures the lines have all their data precached upon loading
- /// </summary>
- void Start()
- {
- Initialize();
- }
- #if UNITY_EDITOR
- /// <summary>
- /// Ensures the hidden mesh/meshfilters are destroyed if users are messing with the components in edit mode
- /// </summary>
- void OnDestroy()
- {
- if (!Application.isPlaying)
- {
- var rendererToDestroy = m_MeshRenderer;
- var filterToDestroy = gameObject.GetComponent<MeshFilter>();
- UnityEditor.EditorApplication.delayCall += () =>
- {
- if (!Application.isPlaying)
- {
- if (rendererToDestroy != null)
- {
- DestroyImmediate(rendererToDestroy);
- }
- if (filterToDestroy != null)
- {
- DestroyImmediate(filterToDestroy);
- }
- }
- };
- }
- }
- #endif
- /// <summary>
- /// Does the actual internal mesh updating as late as possible so nothing ends up a frame behind
- /// </summary>
- protected virtual void LateUpdate()
- {
- if (m_MeshNeedsRefreshing == true)
- {
- m_XRMeshData.RefreshMesh();
- m_MeshNeedsRefreshing = false;
- }
- }
- /// <summary>
- /// Allows the component to be referenced as a renderer, forwarding the MeshRenderer ahead
- /// </summary>
- /// <param name="meshChainRenderer"></param>
- /// <returns>The MeshChainRenderer's MeshRenderer</returns>
- public static implicit operator Renderer(MeshChainRenderer meshChainRenderer)
- {
- return meshChainRenderer.m_MeshRenderer;
- }
- /// <summary>
- /// Triggered by validation - forced initialization to make sure data changed
- /// in the editor is reflected immediately to the user.
- /// </summary>
- void EditorCheckForUpdate()
- {
- // Because this gets delay-called, it can be referring to a destroyed component when a scene starts
- if (this != null)
- {
- // If we did not initialize, refresh all the properties instead
- Initialize(false);
- }
- }
- /// <summary>
- /// Updates any internal variables to represent the new color that has been applied
- /// </summary>
- protected virtual void UpdateColors() { }
- /// <summary>
- /// Updates any internal variables to represent the new width that has been applied
- /// </summary>
- protected virtual void UpdateWidth() { }
- /// <summary>
- /// Creates or updates the underlying mesh data
- /// </summary>
- protected virtual void Initialize(bool generate = true) { }
- /// <summary>
- /// Tests if the mesh data needs to be created or rebuilt
- /// </summary>
- /// <returns>true if the mesh data needs recreation, false if it is already set up properly</returns>
- protected virtual bool NeedsReinitialize()
- {
- return true;
- }
- /// <summary>
- /// Enables the internal mesh representing the line
- /// </summary>
- protected virtual void OnEnable()
- {
- m_MeshRenderer.enabled = true;
- }
- /// <summary>
- /// Disables the internal mesh representing the line
- /// </summary>
- protected virtual void OnDisable()
- {
- m_MeshRenderer.enabled = false;
- }
- }
- }
|