// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace Microsoft.MixedReality.Toolkit.Utilities { /// /// Implements Unity's built in line renderer component, and applies the line data to it. /// [RequireComponent(typeof(LineRenderer))] [AddComponentMenu("Scripts/MRTK/Core/MixedRealityLineRenderer")] public class MixedRealityLineRenderer : BaseMixedRealityLineRenderer { [Header("Mixed Reality Line Renderer Settings")] [SerializeField] [Tooltip("The material to use for the Unity MixedRealityLineRenderer.")] private Material lineMaterial = null; public Material LineMaterial { get => lineMaterial; set => lineMaterial = value; } [SerializeField] private bool roundedEdges = true; public bool RoundedEdges { get => roundedEdges; set => roundedEdges = value; } [SerializeField] private bool roundedCaps = true; public bool RoundedCaps { get => roundedCaps; set => roundedCaps = value; } [SerializeField] [Tooltip("Sets whether the pointer line will animate to a lower brightness level after a hand/controller is recognized.")] private bool fadeLineBrightnessOnEnable = true; /// /// Sets whether the ray line will animate to a lower brightness level on enable of this component /// public bool FadeLineBrightnessOnEnable { get => fadeLineBrightnessOnEnable; set => fadeLineBrightnessOnEnable = value; } [SerializeField, Range(0f, 1f)] [Tooltip("The amount the pointer line will fade if FadeLineBrightnessOnEnable is true.")] private float fadeLinePercentage = 0.55f; /// /// The amount the pointer line will fade if FadeLineBrightnessOnEnable is true" /// public float FadeLinePercentage { get => fadeLinePercentage; set => fadeLinePercentage = value; } [SerializeField] [Tooltip("The length of the animation if fadeLineBrightnessOnEnable is true.")] private float fadeLineAnimationTime = 0.65f; /// /// The amount the pointer line will fade if fadeLineBrightnessOnEnable is true" /// public float FadeLineAnimationTime { get => fadeLineAnimationTime; set => fadeLineAnimationTime = value; } /// /// Gets the LineRenderer points /// public IReadOnlyList Positions => positions; [SerializeField] [HideInInspector] private LineRenderer lineRenderer = null; [Header("Texture Tiling")] [SerializeField] [Tooltip("Tiles the material on the line renderer by world length. Use if you want the texture size to remain constant regardless of a line's length.")] private bool tileMaterialByWorldLength = false; private MaterialPropertyBlock tilingPropertyBlock; private Vector4 tilingPropertyVector = Vector4.one; [SerializeField] private float tileMaterialScale = 1f; private Vector3[] positions; private Coroutine fadeLine = null; private GradientAlphaKey[] cachedKeys; private void OnEnable() { lineRenderer = gameObject.EnsureComponent(); if (LineMaterial == null) { LineMaterial = lineRenderer.sharedMaterial; } // mafinc - Start the line renderer off disabled (invisible), we'll enable it // when we have enough data for it to render properly. if (lineRenderer != null) { lineRenderer.enabled = false; } if (LineMaterial == null) { Debug.LogError("MixedRealityLineRenderer needs a material."); enabled = false; } if (FadeLineBrightnessOnEnable && Application.isPlaying) { fadeLine = StartCoroutine(FadeLine(FadeLinePercentage, FadeLineAnimationTime)); } } private void OnDisable() { lineRenderer.enabled = false; if (fadeLine != null) { StopCoroutine(fadeLine); fadeLine = null; } } /// protected override void UpdateLine() { if (LineDataSource == null) { enabled = false; lineRenderer.enabled = false; return; } lineRenderer.enabled = lineDataSource.enabled; lineRenderer.positionCount = StepMode == StepMode.FromSource ? lineDataSource.PointCount : LineStepCount; if (positions == null || positions.Length != lineRenderer.positionCount) { positions = new Vector3[lineRenderer.positionCount]; } for (int i = 0; i < positions.Length; i++) { if (StepMode == StepMode.FromSource) { positions[i] = lineDataSource.GetPoint(i); } else { float normalizedDistance = GetNormalizedPointAlongLine(i); positions[i] = lineDataSource.GetPoint(normalizedDistance); } } // Set line renderer properties lineRenderer.loop = lineDataSource.Loops; lineRenderer.numCapVertices = roundedCaps ? 8 : 0; lineRenderer.numCornerVertices = roundedEdges ? 8 : 0; lineRenderer.useWorldSpace = true; lineRenderer.startWidth = 1; lineRenderer.endWidth = 1; lineRenderer.startColor = Color.white; lineRenderer.endColor = Color.white; lineRenderer.sharedMaterial = lineMaterial; lineRenderer.widthCurve = LineWidth; lineRenderer.widthMultiplier = WidthMultiplier; lineRenderer.colorGradient = LineColor; lineRenderer.shadowCastingMode = ShadowCastingMode.Off; lineRenderer.lightProbeUsage = LightProbeUsage.Off; // Set positions lineRenderer.positionCount = positions.Length; lineRenderer.SetPositions(positions); // Update texture tiling, if applicable if (tileMaterialByWorldLength) { if (tilingPropertyBlock == null) { tilingPropertyBlock = new MaterialPropertyBlock(); } tilingPropertyVector.x = lineDataSource.UnClampedWorldLength * tileMaterialScale; tilingPropertyBlock.SetVector("_MainTex_ST", tilingPropertyVector); lineRenderer.SetPropertyBlock(tilingPropertyBlock); } else { if (tilingPropertyBlock != null) { tilingPropertyBlock.Clear(); lineRenderer.SetPropertyBlock(tilingPropertyBlock); } } } private IEnumerator FadeLine(float targetAlphaPercentage, float animationLength) { float currentTime = 0f; if (cachedKeys == null) { cachedKeys = LineColor.alphaKeys; } GradientAlphaKey[] fadedKeys = new GradientAlphaKey[cachedKeys.Length]; Array.Copy(cachedKeys, fadedKeys, cachedKeys.Length); float startAlpha = 1f; while (currentTime != animationLength) { currentTime += Time.deltaTime; if (currentTime > animationLength) { currentTime = animationLength; } float percentageComplete = currentTime / animationLength; float scalar = Mathf.Lerp(startAlpha, targetAlphaPercentage, percentageComplete); for (int i = 0; i < fadedKeys.Length; i++) { fadedKeys[i].alpha = cachedKeys[i].alpha * scalar; } LineColor.alphaKeys = fadedKeys; yield return null; } fadeLine = null; } } }