From 70eb0607d6c131e545c45d41a80ffb553af7ee4c Mon Sep 17 00:00:00 2001 From: Matt Schoen Date: Sun, 24 Nov 2019 23:43:12 -0800 Subject: [PATCH 1/4] Move script files into Editor/Runtime folders, with assembly definitions; Add setter to XRLineRenderer.useWorldSpace --- Scripts/Editor.meta => Editor.meta | 0 .../Editor => Editor}/MeshChainShaderGUI.cs | 0 .../MeshChainShaderGUI.cs.meta | 0 Editor/Unity.Labs.XRLineRenderer.Editor.asmdef | 17 +++++++++++++++++ ...Unity.Labs.XRLineRenderer.Editor.asmdef.meta | 7 +++++++ .../Editor => Editor}/XRLineRendererEditor.cs | 0 .../XRLineRendererEditor.cs.meta | 0 .../Editor => Editor}/XRTrailRendererEditor.cs | 0 .../XRTrailRendererEditor.cs.meta | 0 Scripts.meta => Runtime.meta | 0 {Scripts => Runtime}/MeshChainRenderer.cs | 0 {Scripts => Runtime}/MeshChainRenderer.cs.meta | 0 Runtime/Unity.Labs.XRLineRenderer.asmdef | 3 +++ Runtime/Unity.Labs.XRLineRenderer.asmdef.meta | 7 +++++++ {Scripts => Runtime}/XRLineRenderer.cs | 16 ++++++++++------ {Scripts => Runtime}/XRLineRenderer.cs.meta | 0 {Scripts => Runtime}/XRMeshChain.cs | 0 {Scripts => Runtime}/XRMeshChain.cs.meta | 0 {Scripts => Runtime}/XRTrailRenderer.cs | 0 {Scripts => Runtime}/XRTrailRenderer.cs.meta | 0 20 files changed, 44 insertions(+), 6 deletions(-) rename Scripts/Editor.meta => Editor.meta (100%) rename {Scripts/Editor => Editor}/MeshChainShaderGUI.cs (100%) rename {Scripts/Editor => Editor}/MeshChainShaderGUI.cs.meta (100%) create mode 100644 Editor/Unity.Labs.XRLineRenderer.Editor.asmdef create mode 100644 Editor/Unity.Labs.XRLineRenderer.Editor.asmdef.meta rename {Scripts/Editor => Editor}/XRLineRendererEditor.cs (100%) rename {Scripts/Editor => Editor}/XRLineRendererEditor.cs.meta (100%) rename {Scripts/Editor => Editor}/XRTrailRendererEditor.cs (100%) rename {Scripts/Editor => Editor}/XRTrailRendererEditor.cs.meta (100%) rename Scripts.meta => Runtime.meta (100%) rename {Scripts => Runtime}/MeshChainRenderer.cs (100%) rename {Scripts => Runtime}/MeshChainRenderer.cs.meta (100%) create mode 100644 Runtime/Unity.Labs.XRLineRenderer.asmdef create mode 100644 Runtime/Unity.Labs.XRLineRenderer.asmdef.meta rename {Scripts => Runtime}/XRLineRenderer.cs (98%) rename {Scripts => Runtime}/XRLineRenderer.cs.meta (100%) rename {Scripts => Runtime}/XRMeshChain.cs (100%) rename {Scripts => Runtime}/XRMeshChain.cs.meta (100%) rename {Scripts => Runtime}/XRTrailRenderer.cs (100%) rename {Scripts => Runtime}/XRTrailRenderer.cs.meta (100%) diff --git a/Scripts/Editor.meta b/Editor.meta similarity index 100% rename from Scripts/Editor.meta rename to Editor.meta diff --git a/Scripts/Editor/MeshChainShaderGUI.cs b/Editor/MeshChainShaderGUI.cs similarity index 100% rename from Scripts/Editor/MeshChainShaderGUI.cs rename to Editor/MeshChainShaderGUI.cs diff --git a/Scripts/Editor/MeshChainShaderGUI.cs.meta b/Editor/MeshChainShaderGUI.cs.meta similarity index 100% rename from Scripts/Editor/MeshChainShaderGUI.cs.meta rename to Editor/MeshChainShaderGUI.cs.meta diff --git a/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef new file mode 100644 index 0000000..0addc78 --- /dev/null +++ b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef @@ -0,0 +1,17 @@ +{ + "name": "Unity.Labs.XRLineRenderer.Editor", + "references": [ + "GUID:507fb7fe0a2794ac481eab674e8b99cf" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef.meta b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef.meta new file mode 100644 index 0000000..dbb8571 --- /dev/null +++ b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e484b69e484584edd9f645b14ea9e149 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/XRLineRendererEditor.cs b/Editor/XRLineRendererEditor.cs similarity index 100% rename from Scripts/Editor/XRLineRendererEditor.cs rename to Editor/XRLineRendererEditor.cs diff --git a/Scripts/Editor/XRLineRendererEditor.cs.meta b/Editor/XRLineRendererEditor.cs.meta similarity index 100% rename from Scripts/Editor/XRLineRendererEditor.cs.meta rename to Editor/XRLineRendererEditor.cs.meta diff --git a/Scripts/Editor/XRTrailRendererEditor.cs b/Editor/XRTrailRendererEditor.cs similarity index 100% rename from Scripts/Editor/XRTrailRendererEditor.cs rename to Editor/XRTrailRendererEditor.cs diff --git a/Scripts/Editor/XRTrailRendererEditor.cs.meta b/Editor/XRTrailRendererEditor.cs.meta similarity index 100% rename from Scripts/Editor/XRTrailRendererEditor.cs.meta rename to Editor/XRTrailRendererEditor.cs.meta diff --git a/Scripts.meta b/Runtime.meta similarity index 100% rename from Scripts.meta rename to Runtime.meta diff --git a/Scripts/MeshChainRenderer.cs b/Runtime/MeshChainRenderer.cs similarity index 100% rename from Scripts/MeshChainRenderer.cs rename to Runtime/MeshChainRenderer.cs diff --git a/Scripts/MeshChainRenderer.cs.meta b/Runtime/MeshChainRenderer.cs.meta similarity index 100% rename from Scripts/MeshChainRenderer.cs.meta rename to Runtime/MeshChainRenderer.cs.meta diff --git a/Runtime/Unity.Labs.XRLineRenderer.asmdef b/Runtime/Unity.Labs.XRLineRenderer.asmdef new file mode 100644 index 0000000..bd77b95 --- /dev/null +++ b/Runtime/Unity.Labs.XRLineRenderer.asmdef @@ -0,0 +1,3 @@ +{ + "name": "Unity.Labs.XRLineRenderer" +} diff --git a/Runtime/Unity.Labs.XRLineRenderer.asmdef.meta b/Runtime/Unity.Labs.XRLineRenderer.asmdef.meta new file mode 100644 index 0000000..187b7b1 --- /dev/null +++ b/Runtime/Unity.Labs.XRLineRenderer.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 507fb7fe0a2794ac481eab674e8b99cf +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/XRLineRenderer.cs b/Runtime/XRLineRenderer.cs similarity index 98% rename from Scripts/XRLineRenderer.cs rename to Runtime/XRLineRenderer.cs index c6c2394..9124652 100644 --- a/Scripts/XRLineRenderer.cs +++ b/Runtime/XRLineRenderer.cs @@ -4,7 +4,7 @@ using UnityEngine.Serialization; /// /// An XR-Focused drop-in replacement for the Line Renderer /// This renderer draws fixed-width lines with simulated volume and glow. -/// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, +/// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, /// and volumetric (a linked series of capsules or cubes) rendering /// [RequireComponent(typeof(MeshRenderer))] @@ -40,12 +40,16 @@ public class XRLineRenderer : MeshChainRenderer /// /// Draw lines in worldspace (or local space) /// - public bool useWorldSpace { get { return m_UseWorldSpace; } } + public bool useWorldSpace + { + get { return m_UseWorldSpace; } + set { m_UseWorldSpace = value; } + } public override Material material { get { return m_MeshRenderer.material; } - set + set { m_MeshRenderer.material = value; CopyWorldSpaceDataFromMaterial(); @@ -240,7 +244,7 @@ public class XRLineRenderer : MeshChainRenderer // Do an initialization, this changes everything Initialize(); } - + /// /// Get the number of billboard-line chains. /// @@ -317,7 +321,7 @@ public class XRLineRenderer : MeshChainRenderer var pointCounter = 0; var elementCounter = 0; var stepPercent = 0.0f; - + // We go through the element list, much like initialization, but only update the width part of the variables var lastWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; m_XRMeshData.SetElementSize(elementCounter, lastWidth); @@ -366,7 +370,7 @@ public class XRLineRenderer : MeshChainRenderer // If we're looping, then we do need one more pipe var neededPoints = m_Loop ? 1 : 0; neededPoints = Mathf.Max(neededPoints + (m_Positions.Length * 2) - 1, 0); - + if (m_XRMeshData == null) { m_XRMeshData = new XRMeshChain(); diff --git a/Scripts/XRLineRenderer.cs.meta b/Runtime/XRLineRenderer.cs.meta similarity index 100% rename from Scripts/XRLineRenderer.cs.meta rename to Runtime/XRLineRenderer.cs.meta diff --git a/Scripts/XRMeshChain.cs b/Runtime/XRMeshChain.cs similarity index 100% rename from Scripts/XRMeshChain.cs rename to Runtime/XRMeshChain.cs diff --git a/Scripts/XRMeshChain.cs.meta b/Runtime/XRMeshChain.cs.meta similarity index 100% rename from Scripts/XRMeshChain.cs.meta rename to Runtime/XRMeshChain.cs.meta diff --git a/Scripts/XRTrailRenderer.cs b/Runtime/XRTrailRenderer.cs similarity index 100% rename from Scripts/XRTrailRenderer.cs rename to Runtime/XRTrailRenderer.cs diff --git a/Scripts/XRTrailRenderer.cs.meta b/Runtime/XRTrailRenderer.cs.meta similarity index 100% rename from Scripts/XRTrailRenderer.cs.meta rename to Runtime/XRTrailRenderer.cs.meta From 3cc3f901152c7820ef7a5478d9bb5ef54a6c0693 Mon Sep 17 00:00:00 2001 From: Matt Schoen Date: Sun, 24 Nov 2019 23:56:38 -0800 Subject: [PATCH 2/4] Add namespaces; Update assembly definitions to use named references --- Editor/MeshChainShaderGUI.cs | 5 +- .../Unity.Labs.XRLineRenderer.Editor.asmdef | 2 +- Editor/XRLineRendererEditor.cs | 9 +- Editor/XRTrailRendererEditor.cs | 7 +- Runtime/MeshChainRenderer.cs | 703 +++++++------- Runtime/Unity.Labs.XRLineRenderer.asmdef | 11 +- Runtime/XRLineRenderer.cs | 889 +++++++++--------- Runtime/XRMeshChain.cs | 596 ++++++------ Runtime/XRTrailRenderer.cs | 624 ++++++------ 9 files changed, 1454 insertions(+), 1392 deletions(-) diff --git a/Editor/MeshChainShaderGUI.cs b/Editor/MeshChainShaderGUI.cs index 48193da..4187e0d 100644 --- a/Editor/MeshChainShaderGUI.cs +++ b/Editor/MeshChainShaderGUI.cs @@ -1,6 +1,7 @@ -using UnityEngine; +using UnityEditor; +using UnityEngine; -namespace UnityEditor +namespace Unity.Labs.XRLineRenderer { public class MeshChainShaderGUI : ShaderGUI { diff --git a/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef index 0addc78..388ccee 100644 --- a/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef +++ b/Editor/Unity.Labs.XRLineRenderer.Editor.asmdef @@ -1,7 +1,7 @@ { "name": "Unity.Labs.XRLineRenderer.Editor", "references": [ - "GUID:507fb7fe0a2794ac481eab674e8b99cf" + "Unity.Labs.XRLineRenderer" ], "includePlatforms": [ "Editor" diff --git a/Editor/XRLineRendererEditor.cs b/Editor/XRLineRendererEditor.cs index 2befc4d..d28b5d6 100644 --- a/Editor/XRLineRendererEditor.cs +++ b/Editor/XRLineRendererEditor.cs @@ -1,6 +1,7 @@ -using UnityEngine; +using UnityEditor; +using UnityEngine; -namespace UnityEditor +namespace Unity.Labs.XRLineRenderer { [CustomEditor(typeof(XRLineRenderer))] [CanEditMultipleObjects] @@ -35,7 +36,7 @@ namespace UnityEditor { EditorGUILayout.PropertyField(m_UseWorldSpace, true); } - + EditorGUILayout.PropertyField(m_Loop, true); EditorGUILayout.PropertyField(m_Width, true); EditorGUILayout.CurveField(m_WidthCurve, Color.red, new Rect(0, 0, 1, 1)); @@ -43,4 +44,4 @@ namespace UnityEditor serializedObject.ApplyModifiedProperties(); } } -} \ No newline at end of file +} diff --git a/Editor/XRTrailRendererEditor.cs b/Editor/XRTrailRendererEditor.cs index 05de18e..80f2317 100644 --- a/Editor/XRTrailRendererEditor.cs +++ b/Editor/XRTrailRendererEditor.cs @@ -1,6 +1,7 @@ -using UnityEngine; +using UnityEditor; +using UnityEngine; -namespace UnityEditor +namespace Unity.Labs.XRLineRenderer { [CustomEditor(typeof(XRTrailRenderer))] [CanEditMultipleObjects] @@ -47,4 +48,4 @@ namespace UnityEditor serializedObject.ApplyModifiedProperties(); } } -} \ No newline at end of file +} diff --git a/Runtime/MeshChainRenderer.cs b/Runtime/MeshChainRenderer.cs index 2dcdfd6..5c34b8b 100644 --- a/Runtime/MeshChainRenderer.cs +++ b/Runtime/MeshChainRenderer.cs @@ -1,390 +1,409 @@ using System; using UnityEngine; -/// -/// A unified base class for the XR Line Renderer and XR Trail Renderer -/// -[RequireComponent(typeof(MeshRenderer))] -[RequireComponent(typeof(MeshFilter))] -[ExecuteInEditMode] -[DisallowMultipleComponent] -public abstract class MeshChainRenderer : MonoBehaviour +namespace Unity.Labs.XRLineRenderer { - 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); - - [SerializeField] - [Tooltip("Materials to use when rendering.")] - protected Material[] m_Materials; - - [SerializeField] - [Tooltip("The multiplier applied to the curve, describing the width (in world space) along the line.")] - protected float m_Width = 1.0f; - - [SerializeField] - [Tooltip("The curve describing the width of the line at various points along its length.")] - protected AnimationCurve m_WidthCurve = new AnimationCurve(); - - [SerializeField] - [Tooltip("The gradient describing color along the line.")] - protected Gradient m_Color = new Gradient(); - - [SerializeField] - [HideInInspector] - protected MeshRenderer m_MeshRenderer; - - // Cached Data - protected XRMeshChain m_XRMeshData; - protected bool m_MeshNeedsRefreshing; - protected float m_StepSize = 1.0f; - /// - /// Returns the first instantiated Material assigned to the renderer. + /// A unified base class for the XR Line Renderer and XR Trail Renderer /// - public virtual Material material + [RequireComponent(typeof(MeshRenderer))] + [RequireComponent(typeof(MeshFilter))] + [ExecuteInEditMode] + [DisallowMultipleComponent] + public abstract class MeshChainRenderer : MonoBehaviour { - get { return m_MeshRenderer.material; } - set { m_MeshRenderer.material = value; } - } + 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); - /// - /// Returns all the instantiated materials of this object. - /// - public virtual Material[] materials - { - get { return m_MeshRenderer.materials; } - set { m_MeshRenderer.materials = value; } - } + [SerializeField] + [Tooltip("Materials to use when rendering.")] + protected Material[] m_Materials; - /// - /// Returns the shared material of this object. - /// - public virtual Material sharedMaterial - { - get { return m_MeshRenderer.sharedMaterial; } - set { m_MeshRenderer.sharedMaterial = value; } - } + [SerializeField] + [Tooltip("The multiplier applied to the curve, describing the width (in world space) along the line.")] + protected float m_Width = 1.0f; - /// - /// Returns all shared materials of this object. - /// - public virtual Material[] SharedMaterials - { - get { return m_MeshRenderer.materials; } - set { m_MeshRenderer.sharedMaterials = value; } - } + [SerializeField] + [Tooltip("The curve describing the width of the line at various points along its length.")] + protected AnimationCurve m_WidthCurve = new AnimationCurve(); - /// - /// Set the width at the start of the line. - /// - public float widthStart - { - get { return m_WidthCurve.Evaluate(0) * m_Width; } - set + [SerializeField] + [Tooltip("The gradient describing color along the line.")] + protected Gradient m_Color = new Gradient(); + + [SerializeField] + [HideInInspector] + protected MeshRenderer m_MeshRenderer; + + // Cached Data + protected XRMeshChain m_XRMeshData; + protected bool m_MeshNeedsRefreshing; + protected float m_StepSize = 1.0f; + + /// + /// Returns the first instantiated Material assigned to the renderer. + /// + public virtual Material material { - var keys = m_WidthCurve.keys; - keys[0].value = value; - m_WidthCurve.keys = keys; - UpdateWidth(); + get { return m_MeshRenderer.material; } + set { m_MeshRenderer.material = value; } } - } - /// - /// Set the width at the end of the line. - /// - public float widthEnd - { - get { return m_WidthCurve.Evaluate(1) * m_Width; } - set + /// + /// Returns all the instantiated materials of this object. + /// + public virtual Material[] materials { - var keys = m_WidthCurve.keys; - var lastIndex = keys.Length - 1; - keys[lastIndex].value = value; - m_WidthCurve.keys = keys; - UpdateWidth(); + get { return m_MeshRenderer.materials; } + set { m_MeshRenderer.materials = value; } } - } - /// - /// Set an overall multiplier that is applied to the LineRenderer.widthCurve to get the final width of the line. - /// - public float widthMultiplier - { - get { return m_Width; } - set + /// + /// Returns the shared material of this object. + /// + public virtual Material sharedMaterial { - m_Width = value; - UpdateWidth(); + get { return m_MeshRenderer.sharedMaterial; } + set { m_MeshRenderer.sharedMaterial = value; } } - } - /// - /// Set the curve describing the width of the line at various points along its length. - /// - public AnimationCurve widthCurve - { - get { return m_WidthCurve; } - set + /// + /// Returns all shared materials of this object. + /// + public virtual Material[] SharedMaterials { - m_WidthCurve = value ?? new AnimationCurve(new Keyframe(0,1.0f)); - UpdateWidth(); + get { return m_MeshRenderer.materials; } + set { m_MeshRenderer.sharedMaterials = value; } } - } - /// - /// Set the color gradient describing the color of the line at various points along its length. - /// - public Gradient colorGradient - { - get { return m_Color; } - set + /// + /// Set the width at the start of the line. + /// + public float widthStart { - if (m_Color == value) + get { return m_WidthCurve.Evaluate(0) * m_Width; } + set { - return; + var keys = m_WidthCurve.keys; + keys[0].value = value; + m_WidthCurve.keys = keys; + UpdateWidth(); } - m_Color = value ?? new Gradient { alphaKeys = new []{ k_DefaultStartAlpha, k_DefaultEndAlpha }, - colorKeys = new []{ k_DefaultStartColor, k_DefaultEndColor }, - mode = GradientMode.Blend }; - UpdateColors(); } - } - /// - /// Set the color at the start of the line. - /// - public Color colorStart - { - get { return m_Color.Evaluate(0); } - set + /// + /// Set the width at the end of the line. + /// + public float widthEnd { - 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(); - } - } - - /// - /// Set the color at the end of the line. - /// - 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(); - } - } - - /// - /// Resets the width to a straight curve at the given value - /// - /// The new width for the curve to display - public void SetTotalWidth(float newWidth) - { - m_Width = newWidth; - m_WidthCurve = new AnimationCurve(new Keyframe(0,1.0f)); - UpdateWidth(); - } - - /// - /// Resets the color to a single value - /// - /// The new color for the curve to display - 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(); - } - } - - /// - /// Cleans up the visible interface of the meshrenderer by hiding unneeded components - /// Also makes sure the animation curves are set up properly to defualts - /// - void SetupMeshBackend() - { - m_MeshRenderer = GetComponent(); - m_MeshRenderer.hideFlags = HideFlags.HideInInspector; - var meshFilter = GetComponent(); - meshFilter.hideFlags = HideFlags.HideInInspector; - - if (m_Materials == null || m_Materials.Length == 0) - { - m_Materials = m_MeshRenderer.sharedMaterials; - } - 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)); - } - m_Color = m_Color ?? new Gradient { alphaKeys = new[] { k_DefaultStartAlpha, k_DefaultEndAlpha }, - colorKeys = new[] { k_DefaultStartColor, k_DefaultEndColor }, - mode = GradientMode.Blend }; - } - - /// - /// Makes the sure mesh renderer reference is initialized before any functions try to access it - /// - protected virtual void Awake() - { - SetupMeshBackend(); - Initialize(); - } - - /// - /// Makes the sure mesh renderer reference is initialized on reset of the component - /// - void Reset() - { - SetupMeshBackend(); - Initialize(); - } - - /// - /// Ensures the lines have all their data precached upon loading - /// - void Start() - { - Initialize(); - } - -#if UNITY_EDITOR - /// - /// Ensures the hidden mesh/meshfilters are destroyed if users are messing with the components in edit mode - /// - void OnDestroy() - { - if(!Application.isPlaying) - { - var rendererToDestroy = m_MeshRenderer; - var filterToDestroy = gameObject.GetComponent(); - - UnityEditor.EditorApplication.delayCall += ()=> + get { return m_WidthCurve.Evaluate(1) * m_Width; } + set { - if (!Application.isPlaying) + var keys = m_WidthCurve.keys; + var lastIndex = keys.Length - 1; + keys[lastIndex].value = value; + m_WidthCurve.keys = keys; + UpdateWidth(); + } + } + + /// + /// Set an overall multiplier that is applied to the LineRenderer.widthCurve to get the final width of the line. + /// + public float widthMultiplier + { + get { return m_Width; } + set + { + m_Width = value; + UpdateWidth(); + } + } + + /// + /// Set the curve describing the width of the line at various points along its length. + /// + public AnimationCurve widthCurve + { + get { return m_WidthCurve; } + set + { + m_WidthCurve = value ?? new AnimationCurve(new Keyframe(0, 1.0f)); + UpdateWidth(); + } + } + + /// + /// Set the color gradient describing the color of the line at various points along its length. + /// + public Gradient colorGradient + { + get { return m_Color; } + set + { + if (m_Color == value) { - if (rendererToDestroy != null) - { - DestroyImmediate(rendererToDestroy); - } - if (filterToDestroy != null) - { - DestroyImmediate(filterToDestroy); - } + return; } + + m_Color = value ?? new Gradient + { + alphaKeys = new[] { k_DefaultStartAlpha, k_DefaultEndAlpha }, + colorKeys = new[] { k_DefaultStartColor, k_DefaultEndColor }, + mode = GradientMode.Blend + }; + UpdateColors(); + } + } + + /// + /// Set the color at the start of the line. + /// + 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(); + } + } + + /// + /// Set the color at the end of the line. + /// + 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(); + } + } + + /// + /// Resets the width to a straight curve at the given value + /// + /// The new width for the curve to display + public void SetTotalWidth(float newWidth) + { + m_Width = newWidth; + m_WidthCurve = new AnimationCurve(new Keyframe(0, 1.0f)); + UpdateWidth(); + } + + /// + /// Resets the color to a single value + /// + /// The new color for the curve to display + 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 - - /// - /// Does the actual internal mesh updating as late as possible so nothing ends up a frame behind - /// - protected virtual void LateUpdate() - { - if (m_MeshNeedsRefreshing == true) - { - m_XRMeshData.RefreshMesh(); - m_MeshNeedsRefreshing = false; + } + else + { + EditorCheckForUpdate(); + } } - } - /// - /// Allows the component to be referenced as a renderer, forwarding the MeshRenderer ahead - /// - public static implicit operator Renderer(MeshChainRenderer lr) - { - return lr.m_MeshRenderer; - } - - - /// - /// Triggered by validation - forced initialization to make sure data changed - /// in the editor is reflected immediately to the user. - /// - void EditorCheckForUpdate() - { - // Because this gets delay-called, it can be referring to a destroyed component when a scene starts - if (this != null) + /// + /// Cleans up the visible interface of the meshrenderer by hiding unneeded components + /// Also makes sure the animation curves are set up properly to defualts + /// + void SetupMeshBackend() { - // If we did not initialize, refresh all the properties instead + m_MeshRenderer = GetComponent(); + m_MeshRenderer.hideFlags = HideFlags.HideInInspector; + var meshFilter = GetComponent(); + meshFilter.hideFlags = HideFlags.HideInInspector; + + if (m_Materials == null || m_Materials.Length == 0) + { + m_Materials = m_MeshRenderer.sharedMaterials; + } + + 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)); + } + + m_Color = m_Color ?? new Gradient + { + alphaKeys = new[] { k_DefaultStartAlpha, k_DefaultEndAlpha }, + colorKeys = new[] { k_DefaultStartColor, k_DefaultEndColor }, + mode = GradientMode.Blend + }; + } + + /// + /// Makes the sure mesh renderer reference is initialized before any functions try to access it + /// + protected virtual void Awake() + { + SetupMeshBackend(); Initialize(); } - } - /// - /// Updates any internal variables to represent the new color that has been applied - /// - protected virtual void UpdateColors() {} + /// + /// Makes the sure mesh renderer reference is initialized on reset of the component + /// + void Reset() + { + SetupMeshBackend(); + Initialize(); + } - /// - /// Updates any internal variables to represent the new width that has been applied - /// - protected virtual void UpdateWidth() {} + /// + /// Ensures the lines have all their data precached upon loading + /// + void Start() + { + Initialize(); + } - /// - /// Creates or updates the underlying mesh data - /// - protected virtual void Initialize() { } +#if UNITY_EDITOR + /// + /// Ensures the hidden mesh/meshfilters are destroyed if users are messing with the components in edit mode + /// + void OnDestroy() + { + if (!Application.isPlaying) + { + var rendererToDestroy = m_MeshRenderer; + var filterToDestroy = gameObject.GetComponent(); - /// - /// Tests if the mesh data needs to be created or rebuilt - /// - /// true if the mesh data needs recreation, false if it is already set up properly - protected virtual bool NeedsReinitialize() { return true; } + UnityEditor.EditorApplication.delayCall += () => + { + if (!Application.isPlaying) + { + if (rendererToDestroy != null) + { + DestroyImmediate(rendererToDestroy); + } - /// - /// Enables the internal mesh representing the line - /// - protected virtual void OnEnable() - { - m_MeshRenderer.enabled = true; - } + if (filterToDestroy != null) + { + DestroyImmediate(filterToDestroy); + } + } + }; - /// - /// Disables the internal mesh representing the line - /// - protected virtual void OnDisable() - { - m_MeshRenderer.enabled = false; + } + } +#endif + + /// + /// Does the actual internal mesh updating as late as possible so nothing ends up a frame behind + /// + protected virtual void LateUpdate() + { + if (m_MeshNeedsRefreshing == true) + { + m_XRMeshData.RefreshMesh(); + m_MeshNeedsRefreshing = false; + } + } + + /// + /// Allows the component to be referenced as a renderer, forwarding the MeshRenderer ahead + /// + public static implicit operator Renderer(MeshChainRenderer lr) + { + return lr.m_MeshRenderer; + } + + + /// + /// Triggered by validation - forced initialization to make sure data changed + /// in the editor is reflected immediately to the user. + /// + 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(); + } + } + + /// + /// Updates any internal variables to represent the new color that has been applied + /// + protected virtual void UpdateColors() { } + + /// + /// Updates any internal variables to represent the new width that has been applied + /// + protected virtual void UpdateWidth() { } + + /// + /// Creates or updates the underlying mesh data + /// + protected virtual void Initialize() { } + + /// + /// Tests if the mesh data needs to be created or rebuilt + /// + /// true if the mesh data needs recreation, false if it is already set up properly + protected virtual bool NeedsReinitialize() + { + return true; + } + + /// + /// Enables the internal mesh representing the line + /// + protected virtual void OnEnable() + { + m_MeshRenderer.enabled = true; + } + + /// + /// Disables the internal mesh representing the line + /// + protected virtual void OnDisable() + { + m_MeshRenderer.enabled = false; + } } } diff --git a/Runtime/Unity.Labs.XRLineRenderer.asmdef b/Runtime/Unity.Labs.XRLineRenderer.asmdef index bd77b95..e31ccdc 100644 --- a/Runtime/Unity.Labs.XRLineRenderer.asmdef +++ b/Runtime/Unity.Labs.XRLineRenderer.asmdef @@ -1,3 +1,8 @@ -{ - "name": "Unity.Labs.XRLineRenderer" -} +{ + "name": "Unity.Labs.XRLineRenderer", + "references": [], + "optionalUnityReferences": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false +} \ No newline at end of file diff --git a/Runtime/XRLineRenderer.cs b/Runtime/XRLineRenderer.cs index 9124652..d5f4591 100644 --- a/Runtime/XRLineRenderer.cs +++ b/Runtime/XRLineRenderer.cs @@ -1,468 +1,479 @@ using UnityEngine; using UnityEngine.Serialization; -/// -/// An XR-Focused drop-in replacement for the Line Renderer -/// This renderer draws fixed-width lines with simulated volume and glow. -/// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, -/// and volumetric (a linked series of capsules or cubes) rendering -/// -[RequireComponent(typeof(MeshRenderer))] -[RequireComponent(typeof(MeshFilter))] -[ExecuteInEditMode] -public class XRLineRenderer : MeshChainRenderer +namespace Unity.Labs.XRLineRenderer { - // Stored Line Data - [SerializeField] - [Tooltip("All of the connected points to render as a line.")] - Vector3[] m_Positions; - - [SerializeField] - [FormerlySerializedAs("m_WorldSpaceData")] - [Tooltip("Draw lines in worldspace (or local space) - driven via shader.")] - bool m_UseWorldSpace; - - [SerializeField] - [Tooltip("Connect the first and last vertices, to create a loop.")] - bool m_Loop; - - public bool loop - { - get { return m_Loop; } - set - { - m_Loop = value; - if (NeedsReinitialize()) - Initialize(); - } - } - /// - /// Draw lines in worldspace (or local space) + /// An XR-Focused drop-in replacement for the Line Renderer + /// This renderer draws fixed-width lines with simulated volume and glow. + /// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, + /// and volumetric (a linked series of capsules or cubes) rendering /// - public bool useWorldSpace + [RequireComponent(typeof(MeshRenderer))] + [RequireComponent(typeof(MeshFilter))] + [ExecuteInEditMode] + public class XRLineRenderer : MeshChainRenderer { - get { return m_UseWorldSpace; } - set { m_UseWorldSpace = value; } - } + // Stored Line Data + [SerializeField] + [Tooltip("All of the connected points to render as a line.")] + Vector3[] m_Positions; - public override Material material - { - get { return m_MeshRenderer.material; } - set + [SerializeField] + [FormerlySerializedAs("m_WorldSpaceData")] + [Tooltip("Draw lines in worldspace (or local space) - driven via shader.")] + bool m_UseWorldSpace; + + [SerializeField] + [Tooltip("Connect the first and last vertices, to create a loop.")] + bool m_Loop; + + public bool loop { - m_MeshRenderer.material = value; - CopyWorldSpaceDataFromMaterial(); - } - } - - public override Material[] materials - { - get { return m_MeshRenderer.materials; } - set - { - m_MeshRenderer.materials = value; - CopyWorldSpaceDataFromMaterial(); - } - } - - public override Material sharedMaterial - { - get { return m_MeshRenderer.sharedMaterial; } - set - { - m_MeshRenderer.sharedMaterial = value; - CopyWorldSpaceDataFromMaterial(); - } - } - - public override Material[] SharedMaterials - { - get { return m_MeshRenderer.materials; } - set - { - m_MeshRenderer.sharedMaterials = value; - CopyWorldSpaceDataFromMaterial(); - } - } - - /// - /// Makes sure that the internal world space flag of the line renderer - /// matches the world space flag of the first material on the object - /// - void CopyWorldSpaceDataFromMaterial() - { - var firstMaterial = m_MeshRenderer.sharedMaterial; - if (firstMaterial == null) - { - return; - } - if (firstMaterial.HasProperty("_WorldData")) - { - m_UseWorldSpace = !Mathf.Approximately(firstMaterial.GetFloat("_WorldData"), 0.0f); - } - else - { - m_UseWorldSpace = false; - } - } - - /// - /// Gets the position of the vertex in the line. - /// - /// The index of the position to retrieve - /// The position at the specified index of the array - public Vector3 GetPosition(int index) - { - return m_Positions[index]; - } - - /// - /// Sets the position of the vertex in the line. - /// - /// Which vertex to set - /// The new location in space of this vertex - public void SetPosition(int index, Vector3 position) - { - // Update internal data - m_Positions[index] = position; - - // See if the data needs initializing - if (NeedsReinitialize()) - { - Initialize(); - return; - } - - // Otherwise, do fast setting - var prevIndex = (index - 1 + m_Positions.Length) % m_Positions.Length; - var endIndex = (index + 1) % m_Positions.Length; - - if (index > 0 || m_Loop) - { - m_XRMeshData.SetElementPipe((index * 2) - 1, ref m_Positions[prevIndex], ref m_Positions[index]); - } - - m_XRMeshData.SetElementPosition(index * 2, ref m_Positions[index]); - if (index < (m_Positions.Length - 1) || m_Loop) - { - m_XRMeshData.SetElementPipe((index * 2) + 1, ref m_Positions[index], ref m_Positions[endIndex]); - } - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Positions); - m_MeshNeedsRefreshing = true; - } - - /// - /// Get the position of all vertices in the line. - /// - /// The array of positions to retrieve. The array passed should be of at least numPositions in size. - /// How many positions were actually stored in the output array. - public int GetPositions(Vector3[] positions) - { - if (m_Positions != null) - { - m_Positions.CopyTo(positions, 0); - return m_Positions.Length; - } - return 0; - } - - /// - /// Sets all positions in the line. Cheaper than calling SetPosition repeatedly - /// - /// All of the new endpoints of the line - /// Turn on to run a safety check to make sure the number of endpoints does not change (bad for garbage collection) - public void SetPositions(Vector3[] newPositions, bool knownSizeChange = false) - { - // Update internal data - m_Positions = newPositions; - if (NeedsReinitialize()) - { - if (knownSizeChange == false) + get { return m_Loop; } + set { - Debug.LogWarning("New positions does not match size of existing array. Adjusting vertex count as well"); + m_Loop = value; + if (NeedsReinitialize()) + Initialize(); } - Initialize(); - return; } - if (m_Positions.Length <= 0) + /// + /// Draw lines in worldspace (or local space) + /// + public bool useWorldSpace { - return; + get { return m_UseWorldSpace; } + set { m_UseWorldSpace = value; } } - // Otherwise, do fast setting - var pointCounter = 0; - var elementCounter = 0; - m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); - elementCounter++; - pointCounter++; - while (pointCounter < m_Positions.Length) + public override Material material { - m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[pointCounter]); - elementCounter++; + get { return m_MeshRenderer.material; } + set + { + m_MeshRenderer.material = value; + CopyWorldSpaceDataFromMaterial(); + } + } + + public override Material[] materials + { + get { return m_MeshRenderer.materials; } + set + { + m_MeshRenderer.materials = value; + CopyWorldSpaceDataFromMaterial(); + } + } + + public override Material sharedMaterial + { + get { return m_MeshRenderer.sharedMaterial; } + set + { + m_MeshRenderer.sharedMaterial = value; + CopyWorldSpaceDataFromMaterial(); + } + } + + public override Material[] SharedMaterials + { + get { return m_MeshRenderer.materials; } + set + { + m_MeshRenderer.sharedMaterials = value; + CopyWorldSpaceDataFromMaterial(); + } + } + + /// + /// Makes sure that the internal world space flag of the line renderer + /// matches the world space flag of the first material on the object + /// + void CopyWorldSpaceDataFromMaterial() + { + var firstMaterial = m_MeshRenderer.sharedMaterial; + if (firstMaterial == null) + { + return; + } + + if (firstMaterial.HasProperty("_WorldData")) + { + m_UseWorldSpace = !Mathf.Approximately(firstMaterial.GetFloat("_WorldData"), 0.0f); + } + else + { + m_UseWorldSpace = false; + } + } + + /// + /// Gets the position of the vertex in the line. + /// + /// The index of the position to retrieve + /// The position at the specified index of the array + public Vector3 GetPosition(int index) + { + return m_Positions[index]; + } + + /// + /// Sets the position of the vertex in the line. + /// + /// Which vertex to set + /// The new location in space of this vertex + public void SetPosition(int index, Vector3 position) + { + // Update internal data + m_Positions[index] = position; + + // See if the data needs initializing + if (NeedsReinitialize()) + { + Initialize(); + return; + } + + // Otherwise, do fast setting + var prevIndex = (index - 1 + m_Positions.Length) % m_Positions.Length; + var endIndex = (index + 1) % m_Positions.Length; + + if (index > 0 || m_Loop) + { + m_XRMeshData.SetElementPipe((index * 2) - 1, ref m_Positions[prevIndex], ref m_Positions[index]); + } + + m_XRMeshData.SetElementPosition(index * 2, ref m_Positions[index]); + if (index < (m_Positions.Length - 1) || m_Loop) + { + m_XRMeshData.SetElementPipe((index * 2) + 1, ref m_Positions[index], ref m_Positions[endIndex]); + } + + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Positions); + m_MeshNeedsRefreshing = true; + } + + /// + /// Get the position of all vertices in the line. + /// + /// The array of positions to retrieve. The array passed should be of at least numPositions in size. + /// How many positions were actually stored in the output array. + public int GetPositions(Vector3[] positions) + { + if (m_Positions != null) + { + m_Positions.CopyTo(positions, 0); + return m_Positions.Length; + } + + return 0; + } + + /// + /// Sets all positions in the line. Cheaper than calling SetPosition repeatedly + /// + /// All of the new endpoints of the line + /// Turn on to run a safety check to make sure the number of endpoints does not change (bad for garbage collection) + public void SetPositions(Vector3[] newPositions, bool knownSizeChange = false) + { + // Update internal data + m_Positions = newPositions; + if (NeedsReinitialize()) + { + if (knownSizeChange == false) + { + Debug.LogWarning("New positions does not match size of existing array. Adjusting vertex count as well"); + } + + Initialize(); + return; + } + + if (m_Positions.Length <= 0) + { + return; + } + + // Otherwise, do fast setting + var pointCounter = 0; + var elementCounter = 0; m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); - elementCounter++; pointCounter++; - } - if (m_Loop) - { - m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[0]); - } + while (pointCounter < m_Positions.Length) + { + m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[pointCounter]); + elementCounter++; + m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); - // Dirty all the VRMeshChain flags so everything gets refreshed - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Positions); - m_MeshNeedsRefreshing = true; - } + elementCounter++; + pointCounter++; + } - /// - /// Sets the number of billboard-line chains. This function regenerates the point list if the - /// number of vertex points changes, so use it sparingly. - /// - /// The new number of vertices in the line - public void SetVertexCount(int count) - { - // See if anything needs updating - if (m_Positions.Length == count) - { - return; - } - - // Adjust this array - var newPositions = new Vector3[count]; - var copyCount = Mathf.Min(m_Positions.Length, count); - var copyIndex = 0; - - while (copyIndex < copyCount) - { - newPositions[copyIndex] = m_Positions[copyIndex]; - copyIndex++; - } - m_Positions = newPositions; - - // Do an initialization, this changes everything - Initialize(); - } - - /// - /// Get the number of billboard-line chains. - /// - public int GetVertexCount() - { - return m_Positions.Length; - } - - protected override void UpdateColors() - { - // See if the data needs initializing - if (NeedsReinitialize()) - { - Initialize(); - return; - } - - if (m_Positions.Length <= 0) - { - return; - } - - // If it doesn't, go through each point and set the data - var pointCounter = 0; - var elementCounter = 0; - var stepPercent = 0.0f; - - var lastColor = m_Color.Evaluate(stepPercent); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor); - elementCounter++; - pointCounter++; - stepPercent += m_StepSize; - - while (pointCounter < m_Positions.Length) - { - var currentColor = m_Color.Evaluate(stepPercent); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); - elementCounter++; - - m_XRMeshData.SetElementColor(elementCounter, ref currentColor); - - lastColor = currentColor; - elementCounter++; - pointCounter++; - stepPercent += m_StepSize; - } - - if (m_Loop) - { - lastColor = m_Color.Evaluate(stepPercent); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor); - } - - // Dirty the color meshChain flags so the mesh gets new data - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Colors); - m_MeshNeedsRefreshing = true; - } - - protected override void UpdateWidth() - { - // See if the data needs initializing - if (NeedsReinitialize()) - { - Initialize(); - return; - } - - if (m_Positions.Length <= 0) - { - return; - } - - // Otherwise, do fast setting - var pointCounter = 0; - var elementCounter = 0; - var stepPercent = 0.0f; - - // We go through the element list, much like initialization, but only update the width part of the variables - var lastWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - m_XRMeshData.SetElementSize(elementCounter, lastWidth); - elementCounter++; - pointCounter++; - - stepPercent += m_StepSize; - - while (pointCounter < m_Positions.Length) - { - var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - - m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); - elementCounter++; - m_XRMeshData.SetElementSize(elementCounter, currentWidth); - lastWidth = currentWidth; - elementCounter++; - pointCounter++; - stepPercent += m_StepSize; - } - - if (m_Loop) - { - var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); - } - - // Dirty all the VRMeshChain flags so everything gets refreshed - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Sizes); - m_MeshNeedsRefreshing = true; - } - - protected override void Initialize() - { - base.Initialize(); - CopyWorldSpaceDataFromMaterial(); - - if (m_Positions == null) - { - m_Positions = new Vector3[0]; - } - - // For a line renderer we assume one big chain - // We need a control point for each billboard and a control point for each pipe connecting them together - // Except for the end, which must be capped with another billboard. This gives us (positions * 2) - 1 - // If we're looping, then we do need one more pipe - var neededPoints = m_Loop ? 1 : 0; - neededPoints = Mathf.Max(neededPoints + (m_Positions.Length * 2) - 1, 0); - - if (m_XRMeshData == null) - { - m_XRMeshData = new XRMeshChain(); - } - if (m_XRMeshData.reservedElements != neededPoints) - { - m_XRMeshData.worldSpaceData = useWorldSpace; - m_XRMeshData.GenerateMesh(gameObject, true, neededPoints); - } - - // If we have no points, then just assume that stepping through a single point would take us through the whole line - if (neededPoints == 0) - { - m_StepSize = 1.0f; - return; - } - m_StepSize = 1.0f / Mathf.Max(m_Loop ? m_Positions.Length : m_Positions.Length - 1, 1.0f); - - var pointCounter = 0; - var elementCounter = 0; - var stepPercent = 0.0f; - - var lastColor = m_Color.Evaluate(stepPercent); - var lastWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - - // Initialize the single starting point - m_XRMeshData.SetElementSize(elementCounter, lastWidth); - m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor); - elementCounter++; - pointCounter++; - - stepPercent += m_StepSize; - - // Now do the chain - while (pointCounter < m_Positions.Length) - { - var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - var currentColor = m_Color.Evaluate(stepPercent); - - // Create a pipe from the previous point to here - m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); - m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[pointCounter]); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); - elementCounter++; - - // Now record our own point data - m_XRMeshData.SetElementSize(elementCounter, currentWidth); - m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); - m_XRMeshData.SetElementColor(elementCounter, ref currentColor); - - // Go onto the next point while retaining previous values we might need to lerp between - lastWidth = currentWidth; - lastColor = currentColor; - elementCounter++; - pointCounter++; - stepPercent += m_StepSize; - } - - if (m_Loop) - { - var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; - var currentColor = m_Color.Evaluate(stepPercent); - m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); - m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[0]); - m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); - } - - // Dirty all the VRMeshChain flags so everything gets refreshed - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); - m_MeshNeedsRefreshing = true; - } - - protected override bool NeedsReinitialize() - { - // No mesh data means we definately need to reinitialize - if (m_XRMeshData == null) - { - return true; - } - - // If we have any positions, figure out how many points we need to render a line along it - var neededPoints = 0; - if (m_Positions != null) - { - neededPoints = Mathf.Max((m_Positions.Length * 2) - 1, 0); if (m_Loop) { - neededPoints++; + m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[0]); } + + // Dirty all the VRMeshChain flags so everything gets refreshed + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Positions); + m_MeshNeedsRefreshing = true; } - return (m_XRMeshData.reservedElements != neededPoints); + /// + /// Sets the number of billboard-line chains. This function regenerates the point list if the + /// number of vertex points changes, so use it sparingly. + /// + /// The new number of vertices in the line + public void SetVertexCount(int count) + { + // See if anything needs updating + if (m_Positions.Length == count) + { + return; + } + + // Adjust this array + var newPositions = new Vector3[count]; + var copyCount = Mathf.Min(m_Positions.Length, count); + var copyIndex = 0; + + while (copyIndex < copyCount) + { + newPositions[copyIndex] = m_Positions[copyIndex]; + copyIndex++; + } + + m_Positions = newPositions; + + // Do an initialization, this changes everything + Initialize(); + } + + /// + /// Get the number of billboard-line chains. + /// + public int GetVertexCount() + { + return m_Positions.Length; + } + + protected override void UpdateColors() + { + // See if the data needs initializing + if (NeedsReinitialize()) + { + Initialize(); + return; + } + + if (m_Positions.Length <= 0) + { + return; + } + + // If it doesn't, go through each point and set the data + var pointCounter = 0; + var elementCounter = 0; + var stepPercent = 0.0f; + + var lastColor = m_Color.Evaluate(stepPercent); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor); + elementCounter++; + pointCounter++; + stepPercent += m_StepSize; + + while (pointCounter < m_Positions.Length) + { + var currentColor = m_Color.Evaluate(stepPercent); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); + elementCounter++; + + m_XRMeshData.SetElementColor(elementCounter, ref currentColor); + + lastColor = currentColor; + elementCounter++; + pointCounter++; + stepPercent += m_StepSize; + } + + if (m_Loop) + { + lastColor = m_Color.Evaluate(stepPercent); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor); + } + + // Dirty the color meshChain flags so the mesh gets new data + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Colors); + m_MeshNeedsRefreshing = true; + } + + protected override void UpdateWidth() + { + // See if the data needs initializing + if (NeedsReinitialize()) + { + Initialize(); + return; + } + + if (m_Positions.Length <= 0) + { + return; + } + + // Otherwise, do fast setting + var pointCounter = 0; + var elementCounter = 0; + var stepPercent = 0.0f; + + // We go through the element list, much like initialization, but only update the width part of the variables + var lastWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + m_XRMeshData.SetElementSize(elementCounter, lastWidth); + elementCounter++; + pointCounter++; + + stepPercent += m_StepSize; + + while (pointCounter < m_Positions.Length) + { + var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + + m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); + elementCounter++; + m_XRMeshData.SetElementSize(elementCounter, currentWidth); + lastWidth = currentWidth; + elementCounter++; + pointCounter++; + stepPercent += m_StepSize; + } + + if (m_Loop) + { + var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); + } + + // Dirty all the VRMeshChain flags so everything gets refreshed + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.Sizes); + m_MeshNeedsRefreshing = true; + } + + protected override void Initialize() + { + base.Initialize(); + CopyWorldSpaceDataFromMaterial(); + + if (m_Positions == null) + { + m_Positions = new Vector3[0]; + } + + // For a line renderer we assume one big chain + // We need a control point for each billboard and a control point for each pipe connecting them together + // Except for the end, which must be capped with another billboard. This gives us (positions * 2) - 1 + // If we're looping, then we do need one more pipe + var neededPoints = m_Loop ? 1 : 0; + neededPoints = Mathf.Max(neededPoints + (m_Positions.Length * 2) - 1, 0); + + if (m_XRMeshData == null) + { + m_XRMeshData = new XRMeshChain(); + } + + if (m_XRMeshData.reservedElements != neededPoints) + { + m_XRMeshData.worldSpaceData = useWorldSpace; + m_XRMeshData.GenerateMesh(gameObject, true, neededPoints); + } + + // If we have no points, then just assume that stepping through a single point would take us through the whole line + if (neededPoints == 0) + { + m_StepSize = 1.0f; + return; + } + + m_StepSize = 1.0f / Mathf.Max(m_Loop ? m_Positions.Length : m_Positions.Length - 1, 1.0f); + + var pointCounter = 0; + var elementCounter = 0; + var stepPercent = 0.0f; + + var lastColor = m_Color.Evaluate(stepPercent); + var lastWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + + // Initialize the single starting point + m_XRMeshData.SetElementSize(elementCounter, lastWidth); + m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor); + elementCounter++; + pointCounter++; + + stepPercent += m_StepSize; + + // Now do the chain + while (pointCounter < m_Positions.Length) + { + var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + var currentColor = m_Color.Evaluate(stepPercent); + + // Create a pipe from the previous point to here + m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); + m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[pointCounter]); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); + elementCounter++; + + // Now record our own point data + m_XRMeshData.SetElementSize(elementCounter, currentWidth); + m_XRMeshData.SetElementPosition(elementCounter, ref m_Positions[pointCounter]); + m_XRMeshData.SetElementColor(elementCounter, ref currentColor); + + // Go onto the next point while retaining previous values we might need to lerp between + lastWidth = currentWidth; + lastColor = currentColor; + elementCounter++; + pointCounter++; + stepPercent += m_StepSize; + } + + if (m_Loop) + { + var currentWidth = m_WidthCurve.Evaluate(stepPercent) * m_Width; + var currentColor = m_Color.Evaluate(stepPercent); + m_XRMeshData.SetElementSize(elementCounter, lastWidth, currentWidth); + m_XRMeshData.SetElementPipe(elementCounter, ref m_Positions[pointCounter - 1], ref m_Positions[0]); + m_XRMeshData.SetElementColor(elementCounter, ref lastColor, ref currentColor); + } + + // Dirty all the VRMeshChain flags so everything gets refreshed + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); + m_MeshNeedsRefreshing = true; + } + + protected override bool NeedsReinitialize() + { + // No mesh data means we definately need to reinitialize + if (m_XRMeshData == null) + { + return true; + } + + // If we have any positions, figure out how many points we need to render a line along it + var neededPoints = 0; + if (m_Positions != null) + { + neededPoints = Mathf.Max((m_Positions.Length * 2) - 1, 0); + if (m_Loop) + { + neededPoints++; + } + } + + return (m_XRMeshData.reservedElements != neededPoints); + } } } diff --git a/Runtime/XRMeshChain.cs b/Runtime/XRMeshChain.cs index 45f9bb5..7b0a7f9 100644 --- a/Runtime/XRMeshChain.cs +++ b/Runtime/XRMeshChain.cs @@ -1,309 +1,321 @@ using System.Collections.Generic; using UnityEngine; -/// -/// The mesh chain handles all the translation between Unity's mesh class, -/// what the line renderers want to do, and what the billboard-pipe based shaders expect -/// If you need more custom/optimized access to this kind of mesh information, feel -/// free to hook into this structure directly. -/// -public class XRMeshChain +namespace Unity.Labs.XRLineRenderer { - [System.Flags] - public enum MeshRefreshFlag + /// + /// The mesh chain handles all the translation between Unity's mesh class, + /// what the line renderers want to do, and what the billboard-pipe based shaders expect + /// If you need more custom/optimized access to this kind of mesh information, feel + /// free to hook into this structure directly. + /// + public class XRMeshChain { - None = 0, - Positions = 1, - Colors = 2, - Sizes = 4, - All = 7 - } - - Vector3[] m_Verts; - Color32[] m_Colors; - List m_ShapeData; // xy: UV coordinates for GPU expansion zw: Size of this vertex, size of the neighbor - List m_NeighborPoints; // Location of the next point this pipe connects to, or itself if it is a billboard - - // Update flags to prevent unncessary mesh data generation - MeshRefreshFlag m_DataThatNeedsUpdate = MeshRefreshFlag.All; - - // Cached runtime data - Mesh m_Mesh; - Transform m_OwnerTransform; - - bool m_WorldSpaceData; - - /// - /// Whether the control points of this mesh chain have been referenced in world or local space - /// This makes sure the bounding box of the mesh is updated appropriately for culling - /// - public bool worldSpaceData { get { return m_WorldSpaceData; } set { m_WorldSpaceData = value; } } - - /// - /// How many primitives/quads this mesh chain supports and has reserved memory for - /// - public int reservedElements { get; private set; } - - /// - /// If using world space data, this option will force the center of the bounding box to be the root - /// - public bool centerAtRoot { get; set; } - - public XRMeshChain() - { - reservedElements = 0; - } - - /// - /// Creates or re-creates the mesh with all the data needed for billboard-pipe based line rendering - /// - /// The gameobject that will own the created mesh - /// Whether this mesh is going to updated frequently or not - /// How many total billboards and pipes are needed for this renderer - public void GenerateMesh(GameObject owner, bool dynamic, int totalElements) - { - // Precache neccessary data - // The mesh, vertex and triangle counts - if (m_Mesh == null) + [System.Flags] + public enum MeshRefreshFlag { - m_Mesh = new Mesh(); - } - if (dynamic == true) - { - m_Mesh.MarkDynamic(); - } - owner.GetComponent().mesh = m_Mesh; - m_OwnerTransform = owner.transform; - - reservedElements = totalElements; - - var vertCount = 4 * reservedElements; - var triCount = 6 * reservedElements; - - m_Verts = new Vector3[vertCount]; - m_Colors = new Color32[vertCount]; - m_ShapeData = new List(vertCount); - m_NeighborPoints = new List(vertCount); - - var triangles = new int[triCount]; - - var defaultWhite = new Color32(255, 255, 255, 255); - - var uvSet1 = new Vector4(0, 0, 1, 1); - var uvSet2 = new Vector4(1, 0, 1, 1); - var uvSet3 = new Vector4(1, 1, 1, 1); - var uvSet4 = new Vector4(0, 1, 1, 1); - - // Set up the basic data for all of our geometry - var pointCounter = 0; - while (pointCounter < reservedElements) - { - // Get where in the various indices we need to write - var vertOffset = pointCounter * 4; - var triOffset = pointCounter * 6; - - // Store default color - m_Colors[vertOffset] = defaultWhite; - m_Colors[vertOffset + 1] = defaultWhite; - m_Colors[vertOffset + 2] = defaultWhite; - m_Colors[vertOffset + 3] = defaultWhite; - - // Write traditional billboard coordinates - // We use the UV coordinates to determine direction each - // individual vertex will expand in, in screen space - // Last two coordinates are size expansion - m_ShapeData.Add(uvSet1); - m_ShapeData.Add(uvSet2); - m_ShapeData.Add(uvSet3); - m_ShapeData.Add(uvSet4); - - // Zero out neighbor points - m_NeighborPoints.Add(Vector3.zero); - m_NeighborPoints.Add(Vector3.zero); - m_NeighborPoints.Add(Vector3.zero); - m_NeighborPoints.Add(Vector3.zero); - - // And a proper index buffer for this element - triangles[triOffset] = vertOffset; - triangles[triOffset + 1] = vertOffset + 1; - triangles[triOffset + 2] = vertOffset + 2; - triangles[triOffset + 3] = vertOffset; - triangles[triOffset + 4] = vertOffset + 2; - triangles[triOffset + 5] = vertOffset + 3; - - pointCounter++; + None = 0, + Positions = 1, + Colors = 2, + Sizes = 4, + All = 7 } - // Now set any values we can - m_Mesh.triangles = null; - m_Mesh.vertices = m_Verts; - m_Mesh.SetUVs(0, m_ShapeData); - m_Mesh.SetUVs(1, m_NeighborPoints); - m_Mesh.triangles = triangles; - } + Vector3[] m_Verts; + Color32[] m_Colors; + List m_ShapeData; // xy: UV coordinates for GPU expansion zw: Size of this vertex, size of the neighbor + List m_NeighborPoints; // Location of the next point this pipe connects to, or itself if it is a billboard - /// - /// Updates any mesh vertex data that is marked as dirty - /// - public void RefreshMesh() - { - if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Positions) != 0) + // Update flags to prevent unncessary mesh data generation + MeshRefreshFlag m_DataThatNeedsUpdate = MeshRefreshFlag.All; + + // Cached runtime data + Mesh m_Mesh; + Transform m_OwnerTransform; + + bool m_WorldSpaceData; + + /// + /// Whether the control points of this mesh chain have been referenced in world or local space + /// This makes sure the bounding box of the mesh is updated appropriately for culling + /// + public bool worldSpaceData { + get { return m_WorldSpaceData; } + set { m_WorldSpaceData = value; } + } + + /// + /// How many primitives/quads this mesh chain supports and has reserved memory for + /// + public int reservedElements { get; private set; } + + /// + /// If using world space data, this option will force the center of the bounding box to be the root + /// + public bool centerAtRoot { get; set; } + + public XRMeshChain() + { + reservedElements = 0; + } + + /// + /// Creates or re-creates the mesh with all the data needed for billboard-pipe based line rendering + /// + /// The gameobject that will own the created mesh + /// Whether this mesh is going to updated frequently or not + /// How many total billboards and pipes are needed for this renderer + public void GenerateMesh(GameObject owner, bool dynamic, int totalElements) + { + // Precache neccessary data + // The mesh, vertex and triangle counts + if (m_Mesh == null) + { + m_Mesh = new Mesh(); + } + + if (dynamic == true) + { + m_Mesh.MarkDynamic(); + } + + owner.GetComponent().mesh = m_Mesh; + m_OwnerTransform = owner.transform; + + reservedElements = totalElements; + + var vertCount = 4 * reservedElements; + var triCount = 6 * reservedElements; + + m_Verts = new Vector3[vertCount]; + m_Colors = new Color32[vertCount]; + m_ShapeData = new List(vertCount); + m_NeighborPoints = new List(vertCount); + + var triangles = new int[triCount]; + + var defaultWhite = new Color32(255, 255, 255, 255); + + var uvSet1 = new Vector4(0, 0, 1, 1); + var uvSet2 = new Vector4(1, 0, 1, 1); + var uvSet3 = new Vector4(1, 1, 1, 1); + var uvSet4 = new Vector4(0, 1, 1, 1); + + // Set up the basic data for all of our geometry + var pointCounter = 0; + while (pointCounter < reservedElements) + { + // Get where in the various indices we need to write + var vertOffset = pointCounter * 4; + var triOffset = pointCounter * 6; + + // Store default color + m_Colors[vertOffset] = defaultWhite; + m_Colors[vertOffset + 1] = defaultWhite; + m_Colors[vertOffset + 2] = defaultWhite; + m_Colors[vertOffset + 3] = defaultWhite; + + // Write traditional billboard coordinates + // We use the UV coordinates to determine direction each + // individual vertex will expand in, in screen space + // Last two coordinates are size expansion + m_ShapeData.Add(uvSet1); + m_ShapeData.Add(uvSet2); + m_ShapeData.Add(uvSet3); + m_ShapeData.Add(uvSet4); + + // Zero out neighbor points + m_NeighborPoints.Add(Vector3.zero); + m_NeighborPoints.Add(Vector3.zero); + m_NeighborPoints.Add(Vector3.zero); + m_NeighborPoints.Add(Vector3.zero); + + // And a proper index buffer for this element + triangles[triOffset] = vertOffset; + triangles[triOffset + 1] = vertOffset + 1; + triangles[triOffset + 2] = vertOffset + 2; + triangles[triOffset + 3] = vertOffset; + triangles[triOffset + 4] = vertOffset + 2; + triangles[triOffset + 5] = vertOffset + 3; + + pointCounter++; + } + + // Now set any values we can + m_Mesh.triangles = null; m_Mesh.vertices = m_Verts; - m_Mesh.SetUVs(1, m_NeighborPoints); - } - if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Colors) != 0) - { - m_Mesh.colors32 = m_Colors; - } - if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Sizes) != 0) - { m_Mesh.SetUVs(0, m_ShapeData); + m_Mesh.SetUVs(1, m_NeighborPoints); + m_Mesh.triangles = triangles; } - m_DataThatNeedsUpdate = MeshRefreshFlag.None; - m_Mesh.RecalculateBounds(); - if (m_WorldSpaceData == true) + /// + /// Updates any mesh vertex data that is marked as dirty + /// + public void RefreshMesh() { - var newBounds = m_Mesh.bounds; - newBounds.center = centerAtRoot ? Vector3.zero : m_OwnerTransform.InverseTransformPoint(newBounds.center); - m_Mesh.bounds = newBounds; + if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Positions) != 0) + { + m_Mesh.vertices = m_Verts; + m_Mesh.SetUVs(1, m_NeighborPoints); + } + + if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Colors) != 0) + { + m_Mesh.colors32 = m_Colors; + } + + if ((m_DataThatNeedsUpdate & MeshRefreshFlag.Sizes) != 0) + { + m_Mesh.SetUVs(0, m_ShapeData); + } + + m_DataThatNeedsUpdate = MeshRefreshFlag.None; + + m_Mesh.RecalculateBounds(); + if (m_WorldSpaceData == true) + { + var newBounds = m_Mesh.bounds; + newBounds.center = centerAtRoot ? Vector3.zero : m_OwnerTransform.InverseTransformPoint(newBounds.center); + m_Mesh.bounds = newBounds; + } + } + + /// + /// Used by external classes to alert the mesh chain that they have modified its data + /// + /// Which type of data (position, color, size) has been changed + public void SetMeshDataDirty(MeshRefreshFlag dataThatNeedsUpdate) + { + m_DataThatNeedsUpdate |= dataThatNeedsUpdate; + } + + /// + /// Sets the position of a specific point in the chain + /// + /// Which control point to update + /// The updated position of the control point + public void SetElementPosition(int elementIndex, ref Vector3 position) + { + var offset = elementIndex * 4; + m_Verts[offset] = position; + m_Verts[offset + 1] = position; + m_Verts[offset + 2] = position; + m_Verts[offset + 3] = position; + + m_NeighborPoints[offset] = position; + m_NeighborPoints[offset + 1] = position; + m_NeighborPoints[offset + 2] = position; + m_NeighborPoints[offset + 3] = position; + } + + /// + /// Sets the endpoints of a pipe in the chain - The pipe equivalent of SetElementPosition + /// + /// Which control pipe to update + /// The position of the previous control point being connected to + /// The position of the next control point being connected to + public void SetElementPipe(int elementIndex, ref Vector3 startPoint, ref Vector3 endPoint) + { + var offset = elementIndex * 4; + m_Verts[offset] = startPoint; + m_Verts[offset + 1] = startPoint; + m_Verts[offset + 2] = endPoint; + m_Verts[offset + 3] = endPoint; + + m_NeighborPoints[offset] = endPoint; + m_NeighborPoints[offset + 1] = endPoint; + m_NeighborPoints[offset + 2] = startPoint; + m_NeighborPoints[offset + 3] = startPoint; + } + + + /// + /// Sets the size of the billboard or pipe being rendered + /// + /// The index of the control point to update + /// What the radius or width of the element should be + public void SetElementSize(int elementIndex, float sizeModification) + { + var offset = elementIndex * 4; + m_ShapeData[offset] = new Vector4(0, 0, sizeModification, sizeModification); + m_ShapeData[offset + 1] = new Vector4(1, 0, sizeModification, sizeModification); + m_ShapeData[offset + 2] = new Vector4(1, 1, sizeModification, sizeModification); + m_ShapeData[offset + 3] = new Vector4(0, 1, sizeModification, sizeModification); + } + + /// + /// Sets the size of the pipe being rendered + /// + /// The index of the pipe control point to update + /// What the width of the pipe should be + public void SetElementSize(int elementIndex, float startSize, float endSize) + { + var offset = elementIndex * 4; + + m_ShapeData[offset] = new Vector4(0, 0, startSize, endSize); + m_ShapeData[offset + 1] = new Vector4(1, 0, startSize, endSize); + m_ShapeData[offset + 2] = new Vector4(1, 1, endSize, startSize); + m_ShapeData[offset + 3] = new Vector4(0, 1, endSize, startSize); + } + + /// + /// Sets the color of a billboard or pipe in the chain + /// + /// The index of the element we are coloring + /// What the color of this element should be + public void SetElementColor(int elementIndex, ref Color color) + { + var offset = elementIndex * 4; + m_Colors[offset] = color; + m_Colors[offset + 1] = m_Colors[offset]; + m_Colors[offset + 2] = m_Colors[offset]; + m_Colors[offset + 3] = m_Colors[offset]; + } + + /// + /// Sets the color of a billboard or pipe in the chain + /// + /// The index of the element we are coloring + /// What the color of this element should be + public void SetElementColor32(int elementIndex, ref Color32 color) + { + var offset = elementIndex * 4; + m_Colors[offset] = color; + m_Colors[offset + 1] = color; + m_Colors[offset + 2] = color; + m_Colors[offset + 3] = color; + } + + /// + /// Sets the colors of a pipe in the chain + /// + /// The index of the pipe we are coloring + /// The color of the startpoint of the pipe + /// The color of the endpoint of the pipe + public void SetElementColor(int elementIndex, ref Color startColor, ref Color endColor) + { + var offset = elementIndex * 4; + m_Colors[offset] = startColor; + m_Colors[offset + 1] = m_Colors[offset]; + m_Colors[offset + 2] = endColor; + m_Colors[offset + 3] = m_Colors[offset + 2]; + } + + /// + /// Sets the colors of a pipe in the chain + /// + /// The index of the pipe we are coloring + /// The color of the startpoint of the pipe + /// The color of the endpoint of the pipe + public void SetElementColor32(int elementIndex, ref Color32 startColor, ref Color32 endColor) + { + var offset = elementIndex * 4; + m_Colors[offset] = startColor; + m_Colors[offset + 1] = m_Colors[offset]; + m_Colors[offset + 2] = endColor; + m_Colors[offset + 3] = m_Colors[offset + 2]; } } - - /// - /// Used by external classes to alert the mesh chain that they have modified its data - /// - /// Which type of data (position, color, size) has been changed - public void SetMeshDataDirty(MeshRefreshFlag dataThatNeedsUpdate) - { - m_DataThatNeedsUpdate |= dataThatNeedsUpdate; - } - - /// - /// Sets the position of a specific point in the chain - /// - /// Which control point to update - /// The updated position of the control point - public void SetElementPosition(int elementIndex, ref Vector3 position) - { - var offset = elementIndex * 4; - m_Verts[offset] = position; - m_Verts[offset + 1] = position; - m_Verts[offset + 2] = position; - m_Verts[offset + 3] = position; - - m_NeighborPoints[offset] = position; - m_NeighborPoints[offset + 1] = position; - m_NeighborPoints[offset + 2] = position; - m_NeighborPoints[offset + 3] = position; - } - - /// - /// Sets the endpoints of a pipe in the chain - The pipe equivalent of SetElementPosition - /// - /// Which control pipe to update - /// The position of the previous control point being connected to - /// The position of the next control point being connected to - public void SetElementPipe(int elementIndex, ref Vector3 startPoint, ref Vector3 endPoint) - { - var offset = elementIndex * 4; - m_Verts[offset] = startPoint; - m_Verts[offset + 1] = startPoint; - m_Verts[offset + 2] = endPoint; - m_Verts[offset + 3] = endPoint; - - m_NeighborPoints[offset] = endPoint; - m_NeighborPoints[offset + 1] = endPoint; - m_NeighborPoints[offset + 2] = startPoint; - m_NeighborPoints[offset + 3] = startPoint; - } - - - /// - /// Sets the size of the billboard or pipe being rendered - /// - /// The index of the control point to update - /// What the radius or width of the element should be - public void SetElementSize(int elementIndex, float sizeModification) - { - var offset = elementIndex * 4; - m_ShapeData[offset] = new Vector4(0, 0, sizeModification, sizeModification); - m_ShapeData[offset + 1] = new Vector4(1, 0, sizeModification, sizeModification); - m_ShapeData[offset + 2] = new Vector4(1, 1, sizeModification, sizeModification); - m_ShapeData[offset + 3] = new Vector4(0, 1, sizeModification, sizeModification); - } - - /// - /// Sets the size of the pipe being rendered - /// - /// The index of the pipe control point to update - /// What the width of the pipe should be - public void SetElementSize(int elementIndex, float startSize, float endSize) - { - var offset = elementIndex * 4; - - m_ShapeData[offset] = new Vector4(0, 0, startSize, endSize); - m_ShapeData[offset + 1] = new Vector4(1, 0, startSize, endSize); - m_ShapeData[offset + 2] = new Vector4(1, 1, endSize, startSize); - m_ShapeData[offset + 3] = new Vector4(0, 1, endSize, startSize); - } - - /// - /// Sets the color of a billboard or pipe in the chain - /// - /// The index of the element we are coloring - /// What the color of this element should be - public void SetElementColor(int elementIndex, ref Color color) - { - var offset = elementIndex * 4; - m_Colors[offset] = color; - m_Colors[offset + 1] = m_Colors[offset]; - m_Colors[offset + 2] = m_Colors[offset]; - m_Colors[offset + 3] = m_Colors[offset]; - } - - /// - /// Sets the color of a billboard or pipe in the chain - /// - /// The index of the element we are coloring - /// What the color of this element should be - public void SetElementColor32(int elementIndex, ref Color32 color) - { - var offset = elementIndex * 4; - m_Colors[offset] = color; - m_Colors[offset + 1] = color; - m_Colors[offset + 2] = color; - m_Colors[offset + 3] = color; - } - - /// - /// Sets the colors of a pipe in the chain - /// - /// The index of the pipe we are coloring - /// The color of the startpoint of the pipe - /// The color of the endpoint of the pipe - public void SetElementColor(int elementIndex, ref Color startColor, ref Color endColor) - { - var offset = elementIndex * 4; - m_Colors[offset] = startColor; - m_Colors[offset + 1] = m_Colors[offset]; - m_Colors[offset + 2] = endColor; - m_Colors[offset + 3] = m_Colors[offset + 2]; - } - - /// - /// Sets the colors of a pipe in the chain - /// - /// The index of the pipe we are coloring - /// The color of the startpoint of the pipe - /// The color of the endpoint of the pipe - public void SetElementColor32(int elementIndex, ref Color32 startColor, ref Color32 endColor) - { - var offset = elementIndex * 4; - m_Colors[offset] = startColor; - m_Colors[offset + 1] = m_Colors[offset]; - m_Colors[offset + 2] = endColor; - m_Colors[offset + 3] = m_Colors[offset + 2]; - } -} \ No newline at end of file +} diff --git a/Runtime/XRTrailRenderer.cs b/Runtime/XRTrailRenderer.cs index c0a46dc..94b6305 100644 --- a/Runtime/XRTrailRenderer.cs +++ b/Runtime/XRTrailRenderer.cs @@ -1,364 +1,376 @@ using System; using UnityEngine; -/// -/// An XR-Focused drop-in replacement for the Trail Renderer -/// This renderer draws fixed-width lines with simulated volume and glow. -/// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, -/// and volumetric (a linked series of capsules or cubes) rendering -/// -[RequireComponent(typeof(MeshRenderer))] -[RequireComponent(typeof(MeshFilter))] -[ExecuteInEditMode] -public class XRTrailRenderer : MeshChainRenderer +namespace Unity.Labs.XRLineRenderer { - const float k_AbsoluteMinVertexDistance = 0.01f; - - // Stored Trail Data - [SerializeField] - [Tooltip("How many points to store for tracing.")] - int m_MaxTrailPoints = 20; - - [SerializeField] - [Tooltip("Whether to use the last point or the first point of the trail when more are needed and none are available.")] - bool m_StealLastPointWhenEmpty = true; - - [SerializeField] - [Tooltip("How long the tail should be (second) [ 0, infinity ].")] - float m_Time = 5.0f; - - [SerializeField] - [Tooltip("The minimum distance to spawn a new point on the trail [ 0, infinity ].")] - float m_MinVertexDistance = 0.1f; - - [SerializeField] - [Tooltip("Destroy GameObject when there is no trail?")] - bool m_Autodestruct = false; - - [SerializeField] - [Tooltip("With this enabled, the last point will smooth lerp between the last recorded anchor point and the one after it")] - bool m_SmoothInterpolation = false; - - // Circular array support for trail point recording - Vector3[] m_Points; - float[] m_PointTimes; - int m_PointIndexStart = 0; - int m_PointIndexEnd = 0; - - // Cached Data - Vector3 m_LastRecordedPoint = Vector3.zero; - float m_LastPointTime; - - float m_EditorDeltaHelper; // This lets us have access to a time data while not in play mode - - /// - /// How long does the trail take to fade out. + /// An XR-Focused drop-in replacement for the Trail Renderer + /// This renderer draws fixed-width lines with simulated volume and glow. + /// This has many of the advantages of the traditional Line Renderer, old-school system-level line rendering functions, + /// and volumetric (a linked series of capsules or cubes) rendering /// - public float time + [RequireComponent(typeof(MeshRenderer))] + [RequireComponent(typeof(MeshFilter))] + [ExecuteInEditMode] + public class XRTrailRenderer : MeshChainRenderer { - get { return m_Time; } - set { m_Time = Mathf.Max(value, 0); } - } + const float k_AbsoluteMinVertexDistance = 0.01f; - /// - /// Set the minimum distance the trail can travel before a new vertex is added to it. - /// - public float minVertexDistance - { - get { return m_MinVertexDistance; } - set { m_MinVertexDistance = Mathf.Max(value, k_AbsoluteMinVertexDistance); } - } + // Stored Trail Data + [SerializeField] + [Tooltip("How many points to store for tracing.")] + int m_MaxTrailPoints = 20; - /// - /// Get the number of line segments in the trail - /// - public int positionCount { get; private set; } + [SerializeField] + [Tooltip("Whether to use the last point or the first point of the trail when more are needed and none are available.")] + bool m_StealLastPointWhenEmpty = true; - /// - /// Destroy GameObject when there is no trail? - /// - public bool autodestruct - { - get { return m_Autodestruct; } - set { m_Autodestruct = value; } - } + [SerializeField] + [Tooltip("How long the tail should be (second) [ 0, infinity ].")] + float m_Time = 5.0f; - /// - /// Set if the last point will smooth lerp between the last recorded anchor point and the one after it - /// - public bool smoothInterpolation - { - get { return m_SmoothInterpolation; } - set { m_SmoothInterpolation = value; } - } + [SerializeField] + [Tooltip("The minimum distance to spawn a new point on the trail [ 0, infinity ].")] + float m_MinVertexDistance = 0.1f; - /// - /// Updates the built-in mesh data for each control point of the trail - /// - protected override void LateUpdate() - { - // We do the actual internal mesh updating as late as possible so nothing ends up a frame behind - var deltaTime = Time.deltaTime; + [SerializeField] + [Tooltip("Destroy GameObject when there is no trail?")] + bool m_Autodestruct = false; - // We give the editor a little help with handling delta time in edit mode - if (Application.isPlaying == false) + [SerializeField] + [Tooltip("With this enabled, the last point will smooth lerp between the last recorded anchor point and the one after it")] + bool m_SmoothInterpolation = false; + + // Circular array support for trail point recording + Vector3[] m_Points; + float[] m_PointTimes; + int m_PointIndexStart = 0; + int m_PointIndexEnd = 0; + + // Cached Data + Vector3 m_LastRecordedPoint = Vector3.zero; + float m_LastPointTime; + + float m_EditorDeltaHelper; // This lets us have access to a time data while not in play mode + + + /// + /// How long does the trail take to fade out. + /// + public float time { - deltaTime = Time.realtimeSinceStartup - m_EditorDeltaHelper; - m_EditorDeltaHelper = Time.realtimeSinceStartup; + get { return m_Time; } + set { m_Time = Mathf.Max(value, 0); } } - - // Get the current position of the renderer - var currentPoint = transform.position; - var pointDistance = (currentPoint - m_LastRecordedPoint).sqrMagnitude; - var shrunkThisFrame = false; - // Is it more than minVertexDistance from the last position? - if (pointDistance > (m_MinVertexDistance*m_MinVertexDistance)) + /// + /// Set the minimum distance the trail can travel before a new vertex is added to it. + /// + public float minVertexDistance { - // In the situation we have no points, we need to record the start point as well - if (m_PointIndexStart == m_PointIndexEnd) + get { return m_MinVertexDistance; } + set { m_MinVertexDistance = Mathf.Max(value, k_AbsoluteMinVertexDistance); } + } + + /// + /// Get the number of line segments in the trail + /// + public int positionCount { get; private set; } + + /// + /// Destroy GameObject when there is no trail? + /// + public bool autodestruct + { + get { return m_Autodestruct; } + set { m_Autodestruct = value; } + } + + /// + /// Set if the last point will smooth lerp between the last recorded anchor point and the one after it + /// + public bool smoothInterpolation + { + get { return m_SmoothInterpolation; } + set { m_SmoothInterpolation = value; } + } + + /// + /// Updates the built-in mesh data for each control point of the trail + /// + protected override void LateUpdate() + { + // We do the actual internal mesh updating as late as possible so nothing ends up a frame behind + var deltaTime = Time.deltaTime; + + // We give the editor a little help with handling delta time in edit mode + if (Application.isPlaying == false) { - m_Points[m_PointIndexStart] = m_LastRecordedPoint; - m_PointTimes[m_PointIndexStart] = m_Time; + deltaTime = Time.realtimeSinceStartup - m_EditorDeltaHelper; + m_EditorDeltaHelper = Time.realtimeSinceStartup; } - // Make space for a new point - var newEndIndex = (m_PointIndexEnd + 1) % m_MaxTrailPoints; - - // In the situation that we are rendering all available vertices - // We can either keep using the current point, or take the last point, depending on the user's preference - if (newEndIndex != m_PointIndexStart) + // Get the current position of the renderer + var currentPoint = transform.position; + var pointDistance = (currentPoint - m_LastRecordedPoint).sqrMagnitude; + var shrunkThisFrame = false; + + // Is it more than minVertexDistance from the last position? + if (pointDistance > (m_MinVertexDistance * m_MinVertexDistance)) { - m_PointIndexEnd = newEndIndex; - m_PointTimes[m_PointIndexEnd] = 0; - positionCount++; + // In the situation we have no points, we need to record the start point as well + if (m_PointIndexStart == m_PointIndexEnd) + { + m_Points[m_PointIndexStart] = m_LastRecordedPoint; + m_PointTimes[m_PointIndexStart] = m_Time; + } + + // Make space for a new point + var newEndIndex = (m_PointIndexEnd + 1) % m_MaxTrailPoints; + + // In the situation that we are rendering all available vertices + // We can either keep using the current point, or take the last point, depending on the user's preference + if (newEndIndex != m_PointIndexStart) + { + m_PointIndexEnd = newEndIndex; + m_PointTimes[m_PointIndexEnd] = 0; + positionCount++; + } + else + { + if (m_StealLastPointWhenEmpty) + { + m_XRMeshData.SetElementSize(m_PointIndexStart * 2, 0); + m_XRMeshData.SetElementSize((m_PointIndexStart * 2) + 1, 0); + m_PointIndexStart = (m_PointIndexStart + 1) % m_MaxTrailPoints; + m_PointIndexEnd = newEndIndex; + m_PointTimes[m_PointIndexEnd] = 0; + m_LastPointTime = m_PointTimes[m_PointIndexStart]; + } + } + + m_Points[m_PointIndexEnd] = currentPoint; + + // Update the last recorded point + m_LastRecordedPoint = currentPoint; } - else + + // Do time processing + // The end point counts up to a maximum of 'time' + m_PointTimes[m_PointIndexEnd] = Mathf.Min(m_PointTimes[m_PointIndexEnd] + deltaTime, m_Time); + + if (m_PointIndexStart != m_PointIndexEnd) { - if (m_StealLastPointWhenEmpty) + // Run down the counter on the start point + m_PointTimes[m_PointIndexStart] -= deltaTime; + + // If we've hit 0, this point is done for + if (m_PointTimes[m_PointIndexStart] <= 0.0f) { m_XRMeshData.SetElementSize(m_PointIndexStart * 2, 0); m_XRMeshData.SetElementSize((m_PointIndexStart * 2) + 1, 0); m_PointIndexStart = (m_PointIndexStart + 1) % m_MaxTrailPoints; - m_PointIndexEnd = newEndIndex; - m_PointTimes[m_PointIndexEnd] = 0; m_LastPointTime = m_PointTimes[m_PointIndexStart]; + positionCount--; + shrunkThisFrame = true; } } - m_Points[m_PointIndexEnd] = currentPoint; - // Update the last recorded point - m_LastRecordedPoint = currentPoint; - } - // Do time processing - // The end point counts up to a maximum of 'time' - m_PointTimes[m_PointIndexEnd] = Mathf.Min(m_PointTimes[m_PointIndexEnd] + deltaTime, m_Time); - - if (m_PointIndexStart != m_PointIndexEnd) - { - // Run down the counter on the start point - m_PointTimes[m_PointIndexStart] -= deltaTime; - - // If we've hit 0, this point is done for - if (m_PointTimes[m_PointIndexStart] <= 0.0f) + if (m_PointIndexStart != m_PointIndexEnd) { - m_XRMeshData.SetElementSize(m_PointIndexStart * 2, 0); - m_XRMeshData.SetElementSize((m_PointIndexStart * 2) + 1, 0); - m_PointIndexStart = (m_PointIndexStart + 1) % m_MaxTrailPoints; - m_LastPointTime = m_PointTimes[m_PointIndexStart]; - positionCount--; - shrunkThisFrame = true; - } - } - - if (m_PointIndexStart != m_PointIndexEnd) - { - m_MeshNeedsRefreshing = true; - m_MeshRenderer.enabled = true; - } - else - { - m_MeshNeedsRefreshing = false; - m_MeshRenderer.enabled = false; - if (m_Autodestruct && Application.isPlaying && shrunkThisFrame) - { - Destroy(gameObject); - } - } - - if (m_MeshNeedsRefreshing) - { - m_MeshRenderer.enabled = true; - - // Update first and last points position-wise - var nextIndex = (m_PointIndexStart + 1) % m_MaxTrailPoints; - if (m_SmoothInterpolation) - { - var toNextPoint = 1.0f - (m_PointTimes[m_PointIndexStart] / m_LastPointTime); - var lerpPoint = Vector3.Lerp(m_Points[m_PointIndexStart], m_Points[nextIndex], toNextPoint); - m_XRMeshData.SetElementPosition((m_PointIndexStart * 2), ref lerpPoint); - m_XRMeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref lerpPoint, ref m_Points[nextIndex]); + m_MeshNeedsRefreshing = true; + m_MeshRenderer.enabled = true; } else { - m_XRMeshData.SetElementPosition((m_PointIndexStart * 2), ref m_Points[m_PointIndexStart]); - m_XRMeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref m_Points[m_PointIndexStart], ref m_Points[nextIndex]); + m_MeshNeedsRefreshing = false; + m_MeshRenderer.enabled = false; + if (m_Autodestruct && Application.isPlaying && shrunkThisFrame) + { + Destroy(gameObject); + } } - - var prevIndex = m_PointIndexEnd - 1; - if (prevIndex < 0) + + if (m_MeshNeedsRefreshing) { - prevIndex = m_MaxTrailPoints - 1; - } - m_XRMeshData.SetElementPipe((prevIndex * 2) + 1, ref m_Points[prevIndex], ref m_Points[m_PointIndexEnd]); - m_XRMeshData.SetElementPosition((m_PointIndexEnd * 2), ref m_Points[m_PointIndexEnd]); - + m_MeshRenderer.enabled = true; - // Go through all points and update size and color - var pointUpdateCounter = m_PointIndexStart; - var pointCount = 0; - m_StepSize = (positionCount > 0) ? (1.0f / positionCount) : 1.0f; + // Update first and last points position-wise + var nextIndex = (m_PointIndexStart + 1) % m_MaxTrailPoints; + if (m_SmoothInterpolation) + { + var toNextPoint = 1.0f - (m_PointTimes[m_PointIndexStart] / m_LastPointTime); + var lerpPoint = Vector3.Lerp(m_Points[m_PointIndexStart], m_Points[nextIndex], toNextPoint); + m_XRMeshData.SetElementPosition((m_PointIndexStart * 2), ref lerpPoint); + m_XRMeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref lerpPoint, ref m_Points[nextIndex]); + } + else + { + m_XRMeshData.SetElementPosition((m_PointIndexStart * 2), ref m_Points[m_PointIndexStart]); + m_XRMeshData.SetElementPipe((m_PointIndexStart * 2) + 1, ref m_Points[m_PointIndexStart], ref m_Points[nextIndex]); + } - var percent = 0.0f; - var lastWidth = m_WidthCurve.Evaluate(percent) * m_Width; - var lastColor = m_Color.Evaluate(percent); - percent += m_StepSize; + var prevIndex = m_PointIndexEnd - 1; + if (prevIndex < 0) + { + prevIndex = m_MaxTrailPoints - 1; + } - while (pointUpdateCounter != m_PointIndexEnd) - { - var nextWidth = m_WidthCurve.Evaluate(percent) * m_Width; - m_XRMeshData.SetElementSize(pointUpdateCounter * 2, lastWidth); - m_XRMeshData.SetElementSize((pointUpdateCounter * 2) + 1, lastWidth, nextWidth); - lastWidth = nextWidth; + m_XRMeshData.SetElementPipe((prevIndex * 2) + 1, ref m_Points[prevIndex], ref m_Points[m_PointIndexEnd]); + m_XRMeshData.SetElementPosition((m_PointIndexEnd * 2), ref m_Points[m_PointIndexEnd]); - var nextColor = m_Color.Evaluate(percent); - m_XRMeshData.SetElementColor(pointUpdateCounter * 2, ref lastColor); - m_XRMeshData.SetElementColor((pointUpdateCounter * 2) + 1, ref lastColor, ref nextColor); - lastColor = nextColor; - pointUpdateCounter = (pointUpdateCounter + 1) % m_MaxTrailPoints; - pointCount++; + // Go through all points and update size and color + var pointUpdateCounter = m_PointIndexStart; + var pointCount = 0; + m_StepSize = (positionCount > 0) ? (1.0f / positionCount) : 1.0f; + + var percent = 0.0f; + var lastWidth = m_WidthCurve.Evaluate(percent) * m_Width; + var lastColor = m_Color.Evaluate(percent); percent += m_StepSize; + + while (pointUpdateCounter != m_PointIndexEnd) + { + var nextWidth = m_WidthCurve.Evaluate(percent) * m_Width; + m_XRMeshData.SetElementSize(pointUpdateCounter * 2, lastWidth); + m_XRMeshData.SetElementSize((pointUpdateCounter * 2) + 1, lastWidth, nextWidth); + lastWidth = nextWidth; + + var nextColor = m_Color.Evaluate(percent); + m_XRMeshData.SetElementColor(pointUpdateCounter * 2, ref lastColor); + m_XRMeshData.SetElementColor((pointUpdateCounter * 2) + 1, ref lastColor, ref nextColor); + lastColor = nextColor; + + pointUpdateCounter = (pointUpdateCounter + 1) % m_MaxTrailPoints; + pointCount++; + percent += m_StepSize; + } + + lastWidth = m_WidthCurve.Evaluate(1) * m_Width; + m_XRMeshData.SetElementSize((m_PointIndexEnd * 2), lastWidth); + lastColor = m_Color.Evaluate(1); + m_XRMeshData.SetElementColor((m_PointIndexEnd * 2), ref lastColor); + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); + m_XRMeshData.RefreshMesh(); } - lastWidth = m_WidthCurve.Evaluate(1) * m_Width; - m_XRMeshData.SetElementSize((m_PointIndexEnd * 2), lastWidth); - lastColor = m_Color.Evaluate(1); - m_XRMeshData.SetElementColor((m_PointIndexEnd * 2), ref lastColor); - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); - m_XRMeshData.RefreshMesh(); - } - } - - /// - /// Editor helper function to ensure changes are reflected in edit-mode - /// - public void EditorCheckForUpdate() - { - // If we did not initialize, refresh all the properties instead - Initialize(); - } - - /// - /// Removes all points from the TrailRenderer. Useful for restarting a trail from a new position. - /// - public void Clear() - { - var zeroVec = Vector3.zero; - var zeroColor = Color.clear; - - var elementCounter = 0; - var pointCounter = 0; - while (pointCounter < m_Points.Length) - { - // Start point - m_XRMeshData.SetElementSize(elementCounter, 0); - m_XRMeshData.SetElementPosition(elementCounter, ref zeroVec); - m_XRMeshData.SetElementColor(elementCounter, ref zeroColor); - elementCounter++; - - // Pipe to the next point - m_XRMeshData.SetElementSize(elementCounter, 0); - m_XRMeshData.SetElementPipe(elementCounter, ref zeroVec, ref zeroVec); - m_XRMeshData.SetElementColor(elementCounter, ref zeroColor); - - // Go onto the next point while retaining previous values we might need to lerp between - elementCounter++; - pointCounter++; } - m_PointIndexStart = 0; - m_PointIndexEnd = 0; - positionCount = 0; - m_LastRecordedPoint = transform.position; - } - - protected override void Initialize() - { - base.Initialize(); - - m_MaxTrailPoints = Mathf.Max(m_MaxTrailPoints, 3); - - // If we already have the right amount of points and mesh, then we can get away with just clearing the curve out - if (m_Points != null && m_MaxTrailPoints == m_Points.Length && m_XRMeshData != null) + /// + /// Editor helper function to ensure changes are reflected in edit-mode + /// + public void EditorCheckForUpdate() { - Clear(); - return; + // If we did not initialize, refresh all the properties instead + Initialize(); } - - m_Points = new Vector3[m_MaxTrailPoints]; - m_PointTimes = new float[m_MaxTrailPoints]; - // For a trail renderer we assume one big chain - // We need a control point for each billboard and a control point for each pipe connecting them together - // We make this a circular trail so the update logic is easier. This gives us (position * 2) - var neededPoints = Mathf.Max((m_MaxTrailPoints * 2), 0); - - if (m_XRMeshData == null) + /// + /// Removes all points from the TrailRenderer. Useful for restarting a trail from a new position. + /// + public void Clear() { - m_XRMeshData = new XRMeshChain(); - } - if (m_XRMeshData.reservedElements != neededPoints) - { - m_XRMeshData.worldSpaceData = true; - m_XRMeshData.centerAtRoot = true; - m_XRMeshData.GenerateMesh(gameObject, true, neededPoints); + var zeroVec = Vector3.zero; + var zeroColor = Color.clear; - if (neededPoints == 0) + var elementCounter = 0; + var pointCounter = 0; + while (pointCounter < m_Points.Length) { + // Start point + m_XRMeshData.SetElementSize(elementCounter, 0); + m_XRMeshData.SetElementPosition(elementCounter, ref zeroVec); + m_XRMeshData.SetElementColor(elementCounter, ref zeroColor); + elementCounter++; + + // Pipe to the next point + m_XRMeshData.SetElementSize(elementCounter, 0); + m_XRMeshData.SetElementPipe(elementCounter, ref zeroVec, ref zeroVec); + m_XRMeshData.SetElementColor(elementCounter, ref zeroColor); + + // Go onto the next point while retaining previous values we might need to lerp between + elementCounter++; + pointCounter++; + } + + m_PointIndexStart = 0; + m_PointIndexEnd = 0; + positionCount = 0; + m_LastRecordedPoint = transform.position; + } + + protected override void Initialize() + { + base.Initialize(); + + m_MaxTrailPoints = Mathf.Max(m_MaxTrailPoints, 3); + + // If we already have the right amount of points and mesh, then we can get away with just clearing the curve out + if (m_Points != null && m_MaxTrailPoints == m_Points.Length && m_XRMeshData != null) + { + Clear(); return; } - // Dirty all the VRMeshChain flags so everything gets refreshed - m_MeshRenderer.enabled = false; - m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); - m_MeshNeedsRefreshing = true; - } - Clear(); - } - protected override bool NeedsReinitialize() - { - // No mesh data means we definately need to reinitialize - if (m_XRMeshData == null) - { - return true; - } - // Mismatched point data means we definately need to reinitialize - if (m_Points == null || m_MaxTrailPoints != m_Points.Length) - { - return true; - } - m_MaxTrailPoints = Mathf.Max(m_MaxTrailPoints, 3); - var neededPoints = Mathf.Max((m_MaxTrailPoints * 2), 0); + m_Points = new Vector3[m_MaxTrailPoints]; + m_PointTimes = new float[m_MaxTrailPoints]; - return (m_XRMeshData.reservedElements != neededPoints); - } - - /// - /// Enables the internal mesh representing the line - /// - protected override void OnEnable() - { - m_MeshRenderer.enabled = (m_PointIndexStart != m_PointIndexEnd); + // For a trail renderer we assume one big chain + // We need a control point for each billboard and a control point for each pipe connecting them together + // We make this a circular trail so the update logic is easier. This gives us (position * 2) + var neededPoints = Mathf.Max((m_MaxTrailPoints * 2), 0); + + if (m_XRMeshData == null) + { + m_XRMeshData = new XRMeshChain(); + } + + if (m_XRMeshData.reservedElements != neededPoints) + { + m_XRMeshData.worldSpaceData = true; + m_XRMeshData.centerAtRoot = true; + m_XRMeshData.GenerateMesh(gameObject, true, neededPoints); + + if (neededPoints == 0) + { + return; + } + + // Dirty all the VRMeshChain flags so everything gets refreshed + m_MeshRenderer.enabled = false; + m_XRMeshData.SetMeshDataDirty(XRMeshChain.MeshRefreshFlag.All); + m_MeshNeedsRefreshing = true; + } + + Clear(); + } + + protected override bool NeedsReinitialize() + { + // No mesh data means we definately need to reinitialize + if (m_XRMeshData == null) + { + return true; + } + + // Mismatched point data means we definately need to reinitialize + if (m_Points == null || m_MaxTrailPoints != m_Points.Length) + { + return true; + } + + m_MaxTrailPoints = Mathf.Max(m_MaxTrailPoints, 3); + var neededPoints = Mathf.Max((m_MaxTrailPoints * 2), 0); + + return (m_XRMeshData.reservedElements != neededPoints); + } + + /// + /// Enables the internal mesh representing the line + /// + protected override void OnEnable() + { + m_MeshRenderer.enabled = (m_PointIndexStart != m_PointIndexEnd); + } } } From 9c956ebd77f14724cbd98d32ff19ae3f010849b1 Mon Sep 17 00:00:00 2001 From: Matt Schoen Date: Mon, 25 Nov 2019 01:00:01 -0800 Subject: [PATCH 3/4] Add documentation folder, update license, add changelog, update package display name with spaces; Add missing XML comments and make classes internal --- CHANGELOG.md | 9 ++++++ LICENSE.txt.meta => CHANGELOG.md.meta | 5 ++-- Documentation~/com.unity.xrlinerenderer.md | 30 ++++++++++++++++++++ Editor/MeshChainShaderGUI.cs | 13 ++++----- Editor/XRLineRendererEditor.cs | 2 +- Editor/XRTrailRendererEditor.cs | 2 +- LICENSE.md | 5 ++++ LICENSE.md.meta | 7 +++++ LICENSE.txt | 32 --------------------- Runtime/MeshChainRenderer.cs | 33 ++++++++++++++++++++-- Runtime/XRLineRenderer.cs | 33 ++++++++++++++++++++-- Runtime/XRMeshChain.cs | 30 ++++++++++++++++++-- Runtime/XRTrailRenderer.cs | 11 ++++++-- Shaders/MeshChainAdd.shader | 6 ++-- Shaders/MeshChainAlpha.shader | 6 ++-- Shaders/MeshChainMax.shader | 4 +-- Shaders/MeshChainMin.shader | 4 +-- Shaders/MeshChainSubtract.shader | 6 ++-- package.json | 2 +- 19 files changed, 173 insertions(+), 67 deletions(-) create mode 100644 CHANGELOG.md rename LICENSE.txt.meta => CHANGELOG.md.meta (54%) create mode 100644 Documentation~/com.unity.xrlinerenderer.md create mode 100644 LICENSE.md create mode 100644 LICENSE.md.meta delete mode 100644 LICENSE.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c9a589e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +All notable changes to this package will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [0.1.0] - 2019-11-25 + +### This is the first release of *List View Framework*. diff --git a/LICENSE.txt.meta b/CHANGELOG.md.meta similarity index 54% rename from LICENSE.txt.meta rename to CHANGELOG.md.meta index adce0e1..701cdb3 100644 --- a/LICENSE.txt.meta +++ b/CHANGELOG.md.meta @@ -1,8 +1,7 @@ fileFormatVersion: 2 -guid: 7678c616003e2c34f905fa62e8206550 -timeCreated: 1486447834 -licenseType: Pro +guid: 7c2a55c0f54054f4d8093d6c66d7946b TextScriptImporter: + externalObjects: {} userData: assetBundleName: assetBundleVariant: diff --git a/Documentation~/com.unity.xrlinerenderer.md b/Documentation~/com.unity.xrlinerenderer.md new file mode 100644 index 0000000..36e5031 --- /dev/null +++ b/Documentation~/com.unity.xrlinerenderer.md @@ -0,0 +1,30 @@ +# XR Line Renderer +An XR-Optimized line renderer that is also capable of producing very inexpensive glow effects. The XRLineRenderer mimics rendering with 3d capsules while only using two quads worth of geometry. + +## Setup and usage + +1. Place the XRLineRenderer folder into Assets\XR Utilities\XRLineRenderer in your project. + +2. Add a XRLineRenderer or XRTrailRenderer component to your gameobject. The interface is nearly identical to the built in Unity Line and Trail Renderers. + +3. Create a new material using the XRLineRenderer shaders. You can find some examples in XRLineRenderer\Materials + +4. Apply this material to the mesh renderer of your XRLineRenderer or XRTrailRenderer. + + +## VRLineRenderer Shader +You will find five shader variants under the XRLineRenderer category. Each of these corresponds to a shader blend mode. +Max Color and Min Color are the cheapest variants - if you are using the line renderer to mimic glow effects these variants also are stable in that color will not blow out. + +Explanation of interesting shader parameters: +Line Rendering Levels - This allows control over the blend between the inner(most opaque/intense) part of the line and outer(transparent) area. Adjust the level curve to 0 will give a very glow-like effect while setting the cruve to 1 will make the line completely solid. + +Line Scaled by Depth - Turning this option off means the line will stay the same thickness regardless of your distance from it. This is excellent for drafting lines and also for simulating glow. Radius minimum and maximum allow you to clamp this size adjustment. + + +## Custom VR Line Rendering +The Scripts\Meshchain class provides everything you need to make your own custom line rendering constructs. XRLineRenderer and XRTrailRenderer emulate what the classic Unity components provide, but there are many more use cases out there. + + +### Project Settings +If you plan on making changes to The XR Line Renderer and/or contributing back, then you'll need to set the `Asset Serialization` property under Edit->Project Settings->Editor to `Force Text` \ No newline at end of file diff --git a/Editor/MeshChainShaderGUI.cs b/Editor/MeshChainShaderGUI.cs index 4187e0d..2589909 100644 --- a/Editor/MeshChainShaderGUI.cs +++ b/Editor/MeshChainShaderGUI.cs @@ -3,11 +3,10 @@ using UnityEngine; namespace Unity.Labs.XRLineRenderer { - public class MeshChainShaderGUI : ShaderGUI + class MeshChainShaderGUI : ShaderGUI { - protected static class Styles + static class Styles { - public static string emptyTootip = ""; public static GUIContent colorText = new GUIContent("Line Tint", "Line Color (RGB) and Transparency (A)"); public static GUIContent lineDataSpaceText = new GUIContent("World Space Data", "If true, the data " + "will not be transformed before rendering"); @@ -41,9 +40,9 @@ namespace Unity.Labs.XRLineRenderer MaterialProperty m_LineDataSpace; MaterialProperty m_LineDepthScaleMode; - public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) + public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) { - FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly + FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly m_MaterialEditor = materialEditor; Material material = materialEditor.target as Material; @@ -57,7 +56,7 @@ namespace Unity.Labs.XRLineRenderer } } - public void FindProperties(MaterialProperty[] props) + void FindProperties(MaterialProperty[] props) { m_LineColor = FindProperty("_Color", props); m_LineSettings = FindProperty("_lineSettings", props); @@ -68,7 +67,7 @@ namespace Unity.Labs.XRLineRenderer m_LineDepthScaleMode = FindProperty("_LineDepthScale", props, false); } - public void ShaderPropertiesGUI(Material material) + void ShaderPropertiesGUI(Material material) { // Use default labelWidth EditorGUIUtility.labelWidth = 0f; diff --git a/Editor/XRLineRendererEditor.cs b/Editor/XRLineRendererEditor.cs index d28b5d6..9b58ed3 100644 --- a/Editor/XRLineRendererEditor.cs +++ b/Editor/XRLineRendererEditor.cs @@ -5,7 +5,7 @@ namespace Unity.Labs.XRLineRenderer { [CustomEditor(typeof(XRLineRenderer))] [CanEditMultipleObjects] - public class XRLineRendererEditor : Editor + class XRLineRendererEditor : Editor { SerializedProperty m_Materials; SerializedProperty m_Positions; diff --git a/Editor/XRTrailRendererEditor.cs b/Editor/XRTrailRendererEditor.cs index 80f2317..e6cde7a 100644 --- a/Editor/XRTrailRendererEditor.cs +++ b/Editor/XRTrailRendererEditor.cs @@ -5,7 +5,7 @@ namespace Unity.Labs.XRLineRenderer { [CustomEditor(typeof(XRTrailRenderer))] [CanEditMultipleObjects] - public class XRTrailRendererEditor : Editor + class XRTrailRendererEditor : Editor { SerializedProperty m_Materials; SerializedProperty m_Time; diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..579df60 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,5 @@ +XR Line Renderer copyright © 2019 Unity Technologies ApS + +Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). + +Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions. diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..5dd8763 --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9d7c689de92b8439a8ba59e4ebe085ab +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index ed90c10..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,32 +0,0 @@ - -Unity Companion License 1.0 ("License") -Copyright (C) 2017 Unity Technologies ApS ("Unity") - -Unity hereby grants to you a worldwide, non-exclusive, no-charge, and royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute the software that is made available with this License ("Software"), subject to the following terms and conditions: - -1. Unity Companion Use Only. Exercise of the license granted herein is limited to exercise for the creation, use, and/or distribution of applications, software, or other content pursuant to a valid Unity development engine software license ("Engine License"). That means while use of the Software is not limited to use in the software licensed under the Engine License, the Software may not be used for any purpose other than the creation, use, and/or distribution of Engine License-dependent applications, software, or other content. No other exercise of the license granted herein is permitted. - -2. No Modification of Engine License. Neither this License nor any exercise of the license granted herein modifies the Engine License in any way. - -3. Ownership & Grant Back to You. - -3.1 You own your content. In this License, "derivative works" means derivatives of the Software itself--works derived only from the Software by you under this License (for example, modifying the code of the Software itself to improve its efficacy); "derivative works" of the Software do not include, for example, games, apps, or content that you create using the Software. You keep all right, title, and interest to your own content. - -3.2 Unity owns its content. While you keep all right, title, and interest to your own content per the above, as between Unity and you, Unity will own all right, title, and interest to all intellectual property rights (including patent, trademark, and copyright) in the Software and derivative works of the Software, and you hereby assign and agree to assign all such rights in those derivative works to Unity. - -3.3 You have a license to those derivative works. Subject to this License, Unity grants to you the same worldwide, non-exclusive, no-charge, and royalty-free copyright license to derivative works of the Software you create as is granted to you for the Software under this License. - -4. Trademarks. You are not granted any right or license under this License to use any trademarks, service marks, trade names, products names, or branding of Unity or its affiliates ("Trademarks"). Descriptive uses of Trademarks are permitted; see, for example, Unity's Branding Usage Guidelines at https://unity3d.com/public-relations/brand. - -5. Notices & Third-Party Rights. This License, including the copyright notice above, must be provided in all substantial portions of the Software and derivative works thereof (or, if that is impracticable, in any other location where such notices are customarily placed). Further, if the Software is accompanied by a Unity "third-party notices" or similar file, you acknowledge and agree that software identified in that file is governed by those separate license terms. - -6. DISCLAIMER, LIMITATION OF LIABILITY. THE SOFTWARE AND ANY DERIVATIVE WORKS THEREOF IS PROVIDED ON AN "AS IS" BASIS, AND IS PROVIDED WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR NONINFRINGEMENT. IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES (WHETHER DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL, INCLUDING PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR PROFITS, AND BUSINESS INTERRUPTION), OR OTHER LIABILITY WHATSOEVER, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM OR OUT OF, OR IN CONNECTION WITH, THE SOFTWARE OR ANY DERIVATIVE WORKS THEREOF OR THE USE OF OR OTHER DEALINGS IN SAME, EVEN WHERE ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -7. USE IS ACCEPTANCE and License Versions. Your receipt and use of the Software constitutes your acceptance of this License and its terms and conditions. Software released by Unity under this License may be modified or updated and the License with it; upon any such modification or update, you will comply with the terms of the updated License for any use of any of the Software under the updated License. - -8. Use in Compliance with Law and Termination. Your exercise of the license granted herein will at all times be in compliance with applicable law and will not infringe any proprietary rights (including intellectual property rights); this License will terminate immediately on any breach by you of this License. - -9. Severability. If any provision of this License is held to be unenforceable or invalid, that provision will be enforced to the maximum extent possible and the other provisions will remain in full force and effect. - -10. Governing Law and Venue. This License is governed by and construed in accordance with the laws of Denmark, except for its conflict of laws rules; the United Nations Convention on Contracts for the International Sale of Goods will not apply. If you reside (or your principal place of business is) within the United States, you and Unity agree to submit to the personal and exclusive jurisdiction of and venue in the state and federal courts located in San Francisco County, California concerning any dispute arising out of this License ("Dispute"). If you reside (or your principal place of business is) outside the United States, you and Unity agree to submit to the personal and exclusive jurisdiction of and venue in the courts located in Copenhagen, Denmark concerning any Dispute. - diff --git a/Runtime/MeshChainRenderer.cs b/Runtime/MeshChainRenderer.cs index 5c34b8b..438e90b 100644 --- a/Runtime/MeshChainRenderer.cs +++ b/Runtime/MeshChainRenderer.cs @@ -17,29 +17,54 @@ namespace Unity.Labs.XRLineRenderer static readonly GradientAlphaKey k_DefaultStartAlpha = new GradientAlphaKey(1, 0); static readonly GradientAlphaKey k_DefaultEndAlpha = new GradientAlphaKey(1, 1); + /// + /// Materials to use when rendering. + /// [SerializeField] [Tooltip("Materials to use when rendering.")] protected Material[] m_Materials; + /// + /// The multiplier applied to the curve, describing the width (in world space) along the line. + /// [SerializeField] [Tooltip("The multiplier applied to the curve, describing the width (in world space) along the line.")] protected float m_Width = 1.0f; + /// + /// The curve describing the width of the line at various points along its length. + /// [SerializeField] [Tooltip("The curve describing the width of the line at various points along its length.")] protected AnimationCurve m_WidthCurve = new AnimationCurve(); + /// + /// The gradient describing color along the line. + /// [SerializeField] [Tooltip("The gradient describing color along the line.")] protected Gradient m_Color = new Gradient(); + /// + /// The MeshRenderer used to render the line + /// [SerializeField] [HideInInspector] protected MeshRenderer m_MeshRenderer; - // Cached Data + /// + /// Cached mesh data + /// protected XRMeshChain m_XRMeshData; + + /// + /// Whether the mesh data needs to be refreshed + /// protected bool m_MeshNeedsRefreshing; + + /// + /// The step size + /// protected float m_StepSize = 1.0f; /// @@ -346,9 +371,11 @@ namespace Unity.Labs.XRLineRenderer /// /// Allows the component to be referenced as a renderer, forwarding the MeshRenderer ahead /// - public static implicit operator Renderer(MeshChainRenderer lr) + /// + /// The MeshChainRenderer's MeshRenderer + public static implicit operator Renderer(MeshChainRenderer meshChainRenderer) { - return lr.m_MeshRenderer; + return meshChainRenderer.m_MeshRenderer; } diff --git a/Runtime/XRLineRenderer.cs b/Runtime/XRLineRenderer.cs index d5f4591..014f9b3 100644 --- a/Runtime/XRLineRenderer.cs +++ b/Runtime/XRLineRenderer.cs @@ -28,6 +28,9 @@ namespace Unity.Labs.XRLineRenderer [Tooltip("Connect the first and last vertices, to create a loop.")] bool m_Loop; + /// + /// Connect the first and last vertices, to create a loop. + /// public bool loop { get { return m_Loop; } @@ -48,6 +51,9 @@ namespace Unity.Labs.XRLineRenderer set { m_UseWorldSpace = value; } } + /// + /// Returns the first instantiated Material assigned to the renderer. + /// public override Material material { get { return m_MeshRenderer.material; } @@ -58,6 +64,9 @@ namespace Unity.Labs.XRLineRenderer } } + /// + /// Returns all the instantiated materials of this object. + /// public override Material[] materials { get { return m_MeshRenderer.materials; } @@ -68,6 +77,9 @@ namespace Unity.Labs.XRLineRenderer } } + /// + /// Returns the shared material of this object. + /// public override Material sharedMaterial { get { return m_MeshRenderer.sharedMaterial; } @@ -78,6 +90,9 @@ namespace Unity.Labs.XRLineRenderer } } + /// + /// Returns all shared materials of this object. + /// public override Material[] SharedMaterials { get { return m_MeshRenderer.materials; } @@ -255,12 +270,16 @@ namespace Unity.Labs.XRLineRenderer /// /// Get the number of billboard-line chains. - /// + /// + /// The number of chains public int GetVertexCount() { return m_Positions.Length; } + /// + /// Updates any internal variables to represent the new color that has been applied + /// protected override void UpdateColors() { // See if the data needs initializing @@ -311,6 +330,9 @@ namespace Unity.Labs.XRLineRenderer m_MeshNeedsRefreshing = true; } + /// + /// Updates any internal variables to represent the new width that has been applied + /// protected override void UpdateWidth() { // See if the data needs initializing @@ -362,6 +384,9 @@ namespace Unity.Labs.XRLineRenderer m_MeshNeedsRefreshing = true; } + /// + /// Creates or updates the underlying mesh data + /// protected override void Initialize() { base.Initialize(); @@ -454,9 +479,13 @@ namespace Unity.Labs.XRLineRenderer m_MeshNeedsRefreshing = true; } + /// + /// Tests if the mesh data needs to be created or rebuilt + /// + /// true if the mesh data needs recreation, false if it is already set up properly protected override bool NeedsReinitialize() { - // No mesh data means we definately need to reinitialize + // No mesh data means we definitely need to reinitialize if (m_XRMeshData == null) { return true; diff --git a/Runtime/XRMeshChain.cs b/Runtime/XRMeshChain.cs index 7b0a7f9..9c2b826 100644 --- a/Runtime/XRMeshChain.cs +++ b/Runtime/XRMeshChain.cs @@ -11,13 +11,35 @@ namespace Unity.Labs.XRLineRenderer /// public class XRMeshChain { + /// + /// What part of the mesh to refresh + /// [System.Flags] public enum MeshRefreshFlag { + /// + /// Don't refresh any of the mesh + /// None = 0, + + /// + /// Refresh positions + /// Positions = 1, + + /// + /// Refresh colors + /// Colors = 2, + + /// + /// Refresh sizes + /// Sizes = 4, + + /// + /// Refresh all mesh components + /// All = 7 } @@ -26,7 +48,7 @@ namespace Unity.Labs.XRLineRenderer List m_ShapeData; // xy: UV coordinates for GPU expansion zw: Size of this vertex, size of the neighbor List m_NeighborPoints; // Location of the next point this pipe connects to, or itself if it is a billboard - // Update flags to prevent unncessary mesh data generation + // Update flags to prevent unnecessary mesh data generation MeshRefreshFlag m_DataThatNeedsUpdate = MeshRefreshFlag.All; // Cached runtime data @@ -55,6 +77,9 @@ namespace Unity.Labs.XRLineRenderer /// public bool centerAtRoot { get; set; } + /// + /// Initialize a new XRMeshChain + /// public XRMeshChain() { reservedElements = 0; @@ -249,7 +274,8 @@ namespace Unity.Labs.XRLineRenderer /// Sets the size of the pipe being rendered /// /// The index of the pipe control point to update - /// What the width of the pipe should be + /// The start size of the pipe + /// The end size of the pipe public void SetElementSize(int elementIndex, float startSize, float endSize) { var offset = elementIndex * 4; diff --git a/Runtime/XRTrailRenderer.cs b/Runtime/XRTrailRenderer.cs index 94b6305..b4b69be 100644 --- a/Runtime/XRTrailRenderer.cs +++ b/Runtime/XRTrailRenderer.cs @@ -299,6 +299,9 @@ namespace Unity.Labs.XRLineRenderer m_LastRecordedPoint = transform.position; } + /// + /// Creates or updates the underlying mesh data + /// protected override void Initialize() { base.Initialize(); @@ -345,15 +348,19 @@ namespace Unity.Labs.XRLineRenderer Clear(); } + /// + /// Tests if the mesh data needs to be created or rebuilt + /// + /// true if the mesh data needs recreation, false if it is already set up properly protected override bool NeedsReinitialize() { - // No mesh data means we definately need to reinitialize + // No mesh data means we definitely need to reinitialize if (m_XRMeshData == null) { return true; } - // Mismatched point data means we definately need to reinitialize + // Mismatched point data means we definitely need to reinitialize if (m_Points == null || m_MaxTrailPoints != m_Points.Length) { return true; diff --git a/Shaders/MeshChainAdd.shader b/Shaders/MeshChainAdd.shader index ecb8f1f..4ebb25c 100644 --- a/Shaders/MeshChainAdd.shader +++ b/Shaders/MeshChainAdd.shader @@ -1,6 +1,6 @@ Shader "XRLineRenderer/MeshChain - Additive" { - Properties + Properties { _Color("Color Tint", COLOR) = (1,1,1,1) _lineSettings ("Line Thickness Settings", VECTOR) = (0, 1, .5, 1) @@ -24,7 +24,7 @@ Shader "XRLineRenderer/MeshChain - Additive" Pass { // In the first pass we write only to the alpha channel. - // This lets us punch a hole in the background that our + // This lets us punch a hole in the background that our // line color then shows through Blend One One BlendOp Min @@ -73,5 +73,5 @@ Shader "XRLineRenderer/MeshChain - Additive" } } FallBack "Diffuse" - CustomEditor "MeshChainShaderGUI" + CustomEditor "Unity.Labs.XRLineRenderer.MeshChainShaderGUI" } diff --git a/Shaders/MeshChainAlpha.shader b/Shaders/MeshChainAlpha.shader index bc91035..e0af98c 100644 --- a/Shaders/MeshChainAlpha.shader +++ b/Shaders/MeshChainAlpha.shader @@ -1,6 +1,6 @@ Shader "XRLineRenderer/MeshChain - Alpha Blended" { - Properties + Properties { _Color("Color Tint", COLOR) = (1,1,1,1) _lineSettings ("Line Thickness Settings", VECTOR) = (0, 1, .5, 1) @@ -24,7 +24,7 @@ Shader "XRLineRenderer/MeshChain - Alpha Blended" Pass { // In the first pass we write only to the alpha channel. - // This lets us punch a hole in the background that our + // This lets us punch a hole in the background that our // line color then shows through Blend One One BlendOp Min @@ -73,5 +73,5 @@ Shader "XRLineRenderer/MeshChain - Alpha Blended" } } FallBack "Diffuse" - CustomEditor "MeshChainShaderGUI" + CustomEditor "Unity.Labs.XRLineRenderer.MeshChainShaderGUI" } diff --git a/Shaders/MeshChainMax.shader b/Shaders/MeshChainMax.shader index c820af8..46d151b 100644 --- a/Shaders/MeshChainMax.shader +++ b/Shaders/MeshChainMax.shader @@ -1,6 +1,6 @@ Shader "XRLineRenderer/MeshChain - Max Color" { - Properties + Properties { _Color("Color Tint", COLOR) = (1,1,1,1) _lineSettings ("Line Thickness Settings", VECTOR) = (0, 1, .5, 1) @@ -43,5 +43,5 @@ Shader "XRLineRenderer/MeshChain - Max Color" } } FallBack "Diffuse" - CustomEditor "MeshChainShaderGUI" + CustomEditor "Unity.Labs.XRLineRenderer.MeshChainShaderGUI" } diff --git a/Shaders/MeshChainMin.shader b/Shaders/MeshChainMin.shader index 4403bd3..b843f41 100644 --- a/Shaders/MeshChainMin.shader +++ b/Shaders/MeshChainMin.shader @@ -1,6 +1,6 @@ Shader "XRLineRenderer/MeshChain - Min Color" { - Properties + Properties { _Color("Color Tint", COLOR) = (1,1,1,1) _lineSettings ("Line Thickness Settings", VECTOR) = (0, 1, .5, 1) @@ -43,5 +43,5 @@ Shader "XRLineRenderer/MeshChain - Min Color" } } FallBack "Diffuse" - CustomEditor "MeshChainShaderGUI" + CustomEditor "Unity.Labs.XRLineRenderer.MeshChainShaderGUI" } diff --git a/Shaders/MeshChainSubtract.shader b/Shaders/MeshChainSubtract.shader index 84e013c..b38378a 100644 --- a/Shaders/MeshChainSubtract.shader +++ b/Shaders/MeshChainSubtract.shader @@ -1,6 +1,6 @@ Shader "XRLineRenderer/MeshChain - Subtractive" { - Properties + Properties { _Color("Color Tint", COLOR) = (1,1,1,1) _lineSettings ("Line Thickness Settings", VECTOR) = (0, 1, .5, 1) @@ -24,7 +24,7 @@ Shader "XRLineRenderer/MeshChain - Subtractive" Pass { // In the first pass we write only to the alpha channel. - // This lets us punch a hole in the background that our + // This lets us punch a hole in the background that our // line color then shows through Blend One One BlendOp Min @@ -73,5 +73,5 @@ Shader "XRLineRenderer/MeshChain - Subtractive" } } FallBack "Diffuse" - CustomEditor "MeshChainShaderGUI" + CustomEditor "Unity.Labs.XRLineRenderer.MeshChainShaderGUI" } diff --git a/package.json b/package.json index af661f7..43e202b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.unity.labs.xrlinerenderer", - "displayName": "XRLineRenderer", + "displayName": "XR Line Renderer", "version": "0.1.0-preview", "unity": "2019.1", "unityRelease": "14f1", From ea9fdeb5ec19b26fbbdae90e5c5f1d84bbbf5b49 Mon Sep 17 00:00:00 2001 From: Matt Schoen Date: Mon, 25 Nov 2019 03:31:57 -0800 Subject: [PATCH 4/4] Change namespace to Unity.Labs.XR to avoid conflict with LineRenderer in other Unity.Labs namespaces --- Editor/MeshChainShaderGUI.cs | 2 +- Editor/XRLineRendererEditor.cs | 2 +- Editor/XRTrailRendererEditor.cs | 2 +- Runtime/MeshChainRenderer.cs | 2 +- Runtime/XRLineRenderer.cs | 2 +- Runtime/XRMeshChain.cs | 2 +- Runtime/XRTrailRenderer.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Editor/MeshChainShaderGUI.cs b/Editor/MeshChainShaderGUI.cs index 2589909..0ac539f 100644 --- a/Editor/MeshChainShaderGUI.cs +++ b/Editor/MeshChainShaderGUI.cs @@ -1,7 +1,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { class MeshChainShaderGUI : ShaderGUI { diff --git a/Editor/XRLineRendererEditor.cs b/Editor/XRLineRendererEditor.cs index 9b58ed3..367b8d8 100644 --- a/Editor/XRLineRendererEditor.cs +++ b/Editor/XRLineRendererEditor.cs @@ -1,7 +1,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { [CustomEditor(typeof(XRLineRenderer))] [CanEditMultipleObjects] diff --git a/Editor/XRTrailRendererEditor.cs b/Editor/XRTrailRendererEditor.cs index e6cde7a..f7d029a 100644 --- a/Editor/XRTrailRendererEditor.cs +++ b/Editor/XRTrailRendererEditor.cs @@ -1,7 +1,7 @@ using UnityEditor; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { [CustomEditor(typeof(XRTrailRenderer))] [CanEditMultipleObjects] diff --git a/Runtime/MeshChainRenderer.cs b/Runtime/MeshChainRenderer.cs index 438e90b..5828790 100644 --- a/Runtime/MeshChainRenderer.cs +++ b/Runtime/MeshChainRenderer.cs @@ -1,7 +1,7 @@ using System; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { /// /// A unified base class for the XR Line Renderer and XR Trail Renderer diff --git a/Runtime/XRLineRenderer.cs b/Runtime/XRLineRenderer.cs index 014f9b3..8c6198b 100644 --- a/Runtime/XRLineRenderer.cs +++ b/Runtime/XRLineRenderer.cs @@ -1,7 +1,7 @@ using UnityEngine; using UnityEngine.Serialization; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { /// /// An XR-Focused drop-in replacement for the Line Renderer diff --git a/Runtime/XRMeshChain.cs b/Runtime/XRMeshChain.cs index 9c2b826..871f28a 100644 --- a/Runtime/XRMeshChain.cs +++ b/Runtime/XRMeshChain.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { /// /// The mesh chain handles all the translation between Unity's mesh class, diff --git a/Runtime/XRTrailRenderer.cs b/Runtime/XRTrailRenderer.cs index b4b69be..1f66dcc 100644 --- a/Runtime/XRTrailRenderer.cs +++ b/Runtime/XRTrailRenderer.cs @@ -1,7 +1,7 @@ using System; using UnityEngine; -namespace Unity.Labs.XRLineRenderer +namespace Unity.Labs.XR { /// /// An XR-Focused drop-in replacement for the Trail Renderer