diff --git a/Assets~/StandardAssets/Interactors/LineInteractorVisualizer.prefab b/Assets~/StandardAssets/Interactors/LineInteractorVisualizer.prefab index 06399c6f..5e9bddb8 100644 --- a/Assets~/StandardAssets/Interactors/LineInteractorVisualizer.prefab +++ b/Assets~/StandardAssets/Interactors/LineInteractorVisualizer.prefab @@ -47,6 +47,68 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: a0c2a07ed1df1094ba9ac469febef61e, type: 3} m_Name: m_EditorClassIdentifier: + lineBase: {fileID: 4233110557611249510} + defaultLineColor: + serializedVersion: 2 + key0: {r: 1, g: 1, b: 1, a: 1} + key1: {r: 1, g: 1, b: 1, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + lineColorInputDown: + serializedVersion: 2 + key0: {r: 0, g: 0.9806142, b: 1, a: 0} + key1: {r: 0, g: 0.9806142, b: 1, a: 1} + key2: {r: 0, g: 0, b: 0, a: 1} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 3277 + atime2: 49151 + atime3: 65535 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 4 + lineCastResolution: 2 --- !u!114 &4233110557611249510 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Runtime/Input/Interactors/BaseControllerInteractor.cs b/Runtime/Input/Interactors/BaseControllerInteractor.cs index a9446ac9..07791a6f 100644 --- a/Runtime/Input/Interactors/BaseControllerInteractor.cs +++ b/Runtime/Input/Interactors/BaseControllerInteractor.cs @@ -94,10 +94,8 @@ public abstract class BaseControllerInteractor : ControllerPoseSynchronizer, ICo /// protected bool IsSelectPressed { get; set; } = false; - /// - /// true, if any is down on this . - /// - protected bool IsInputDown => inputDownActions.Count > 0; + /// + public bool IsInputDown => inputDownActions.Count > 0; /// /// True if select has been pressed once since this component was enabled @@ -387,6 +385,11 @@ protected override void Start() { base.Start(); SetCursor(); + + if (visualizer.IsNotNull()) + { + visualizer.Interactor = this; + } } /// @@ -422,12 +425,34 @@ protected override void OnDisable() } } + /// + protected override void OnDestroy() + { + if (visualizer.IsNotNull()) + { + visualizer.gameObject.Destroy(); + } + + base.OnDestroy(); + } + /// - public virtual void OnPreRaycast() { } + public virtual void OnPreRaycast() + { + if (visualizer.IsNotNull()) + { + visualizer.OnPreRaycast(); + } + } /// public virtual void OnPostRaycast() { + if (visualizer.IsNotNull()) + { + visualizer.OnPostRaycast(); + } + if (grabAction != InputAction.None) { if (IsGrabPressed) @@ -440,7 +465,6 @@ public virtual void OnPostRaycast() if (IsSelectPressed) { DragHandler(pointerAction); - } } } diff --git a/Runtime/Input/Interactors/BaseInteractorVisualizer.cs b/Runtime/Input/Interactors/BaseInteractorVisualizer.cs index 76dece37..ba954fe7 100644 --- a/Runtime/Input/Interactors/BaseInteractorVisualizer.cs +++ b/Runtime/Input/Interactors/BaseInteractorVisualizer.cs @@ -7,6 +7,13 @@ namespace RealityToolkit.Input.Interactors { public abstract class BaseInteractorVisualizer : MonoBehaviour, IInteractorVisualizer { + /// + public virtual IInteractor Interactor { get; set; } + /// + public virtual void OnPreRaycast() { } + + /// + public virtual void OnPostRaycast() { } } } diff --git a/Runtime/Input/Interactors/FarInteractor.cs b/Runtime/Input/Interactors/FarInteractor.cs index cd2d1436..7401a97d 100644 --- a/Runtime/Input/Interactors/FarInteractor.cs +++ b/Runtime/Input/Interactors/FarInteractor.cs @@ -2,167 +2,45 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using RealityToolkit.Definitions.Physics; -using RealityToolkit.Utilities.Lines.DataProviders; -using RealityToolkit.Utilities.Lines.Renderers; using UnityEngine; -using UnityEngine.Serialization; namespace RealityToolkit.Input.Interactors { /// /// A simple line pointer for drawing lines from the input source origin to the current pointer position. /// + [AddComponentMenu("")] public class FarInteractor : BaseControllerInteractor { - [SerializeField] - private Gradient defaultLineColor = new Gradient(); - - protected Gradient DefaultLineColor - { - get => defaultLineColor; - set => defaultLineColor = value; - } - - [SerializeField] - private Gradient lineColorInputDown = new Gradient(); - - protected Gradient LineColorInputDown - { - get => lineColorInputDown; - set => lineColorInputDown = value; - } - [Range(2, 50)] [SerializeField] - [FormerlySerializedAs("LineCastResolution")] [Tooltip("This setting has a high performance cost. Values above 20 are not recommended.")] - private int lineCastResolution = 10; - - protected int LineCastResolution - { - get => lineCastResolution; - set => lineCastResolution = value; - } - - [SerializeField] - private BaseLineDataProvider lineBase; - - /// - /// The Line Data Provider driving this pointer. - /// - public BaseLineDataProvider LineBase => lineBase; - - [SerializeField] - [Tooltip("If no line renderers are specified, this array will be auto-populated on startup.")] - private BaseLineRenderer[] lineRenderers; + private int lineCastResolution = 2; /// - /// The current line renderers that this pointer is utilizing. + /// The amount of rays to cast. /// /// - /// If no line renderers are specified, this array will be auto-populated on startup. + /// This setting has a high performance cost. Values above 20 are not recommended. /// - public BaseLineRenderer[] LineRenderers + public int LineCastResolution { - get => lineRenderers; - set => lineRenderers = value; + get => lineCastResolution; + set => lineCastResolution = value; } /// public override bool IsFarInteractor => true; - private void CheckInitialization() - { - if (lineBase == null) - { - lineBase = GetComponent(); - } - - if (lineBase == null) - { - Debug.LogError($"No Mixed Reality Line Data Provider found on {gameObject.name}. Did you forget to add a Line Data provider?"); - } - - if (lineBase != null && (lineRenderers == null || lineRenderers.Length == 0)) - { - lineRenderers = lineBase.GetComponentsInChildren(); - } - - if (lineRenderers == null || lineRenderers.Length == 0) - { - Debug.LogError($"No Mixed Reality Line Renderers found on {gameObject.name}. Did you forget to add a Mixed Reality Line Renderer?"); - } - } - - #region MonoBehaviour Implementation - - protected virtual void OnValidate() - { - CheckInitialization(); - } - - protected override void OnEnable() - { - base.OnEnable(); - CheckInitialization(); - } - - #endregion MonoBehaviour Implementation - - #region IPointer Implementation - - /// public override void OnPreRaycast() { - Debug.Assert(lineBase != null); - - lineBase.UpdateMatrix(); - - if (RayStabilizer != null) - { - RayStabilizer.UpdateStability(Rays[0].Origin, Rays[0].Direction); - Rays[0].CopyRay(RayStabilizer.StableRay, Extent); - } - - TryGetPointerPosition(out var pointerPosition); - TryGetPointerRotation(out var pointerRotation); - - // Set our first and last points - lineBase.FirstPoint = pointerPosition; - - if (IsFocusLocked && Result.CurrentTarget != null) - { - if (SyncedTarget != null) - { - // Now raycast out like nothing happened so we can get an updated pointer position. - lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Extent); - } - else - { - // Set the line to the locked position. - lineBase.LastPoint = Result.EndPoint; - } - } - else - { - lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Extent); - } - // Make sure our array will hold if (Rays == null || Rays.Length != lineCastResolution) { Rays = new RayStep[lineCastResolution]; } - var stepSize = 1f / Rays.Length; - var lastPoint = lineBase.GetUnClampedPoint(0f); - - for (int i = 0; i < Rays.Length; i++) - { - var currentPoint = lineBase.GetUnClampedPoint(stepSize * (i + 1)); - Rays[i].UpdateRayStep(ref lastPoint, ref currentPoint); - lastPoint = currentPoint; - } + base.OnPreRaycast(); } /// @@ -170,8 +48,6 @@ public override void OnPostRaycast() { if (!IsInteractionEnabled) { - lineBase.enabled = false; - if (BaseCursor != null) { BaseCursor.IsVisible = false; @@ -180,64 +56,12 @@ public override void OnPostRaycast() return; } - base.OnPostRaycast(); - - Gradient lineColor; - - lineBase.enabled = true; - if (BaseCursor != null) { BaseCursor.IsVisible = true; } - // Used to ensure the line doesn't extend beyond the cursor - float cursorOffsetWorldLength = BaseCursor?.SurfaceCursorDistance ?? 0f; - - // The distance the ray travels through the world before it hits something. - // Measured in world-units (as opposed to normalized distance). - var clearWorldLength = (Result?.CurrentTarget != null) ? Result.RayDistance : Extent; - - lineColor = IsInputDown ? LineColorInputDown : DefaultLineColor; - - int maxClampLineSteps = lineCastResolution; - - for (var i = 0; i < lineRenderers.Length; i++) - { - var lineRenderer = lineRenderers[i]; - // Renderers are enabled by default if line is enabled - lineRenderer.enabled = true; - maxClampLineSteps = Mathf.Max(maxClampLineSteps, lineRenderer.LineStepCount); - lineRenderer.LineColor = lineColor; - } - - // If focus is locked, we're sticking to the target - // So don't clamp the world length - if (IsFocusLocked && Result.CurrentTarget != null) - { - if (SyncedTarget != null) - { - if (Result.GrabPoint == Vector3.zero) - { - LineBase.LastPoint = Result.EndPoint; - } - else - { - LineBase.LastPoint = Result.GrabPoint; - } - } - - float cursorOffsetLocalLength = LineBase.GetNormalizedLengthFromWorldLength(cursorOffsetWorldLength); - LineBase.LineEndClamp = 1 - cursorOffsetLocalLength; - } - else - { - // Otherwise clamp the line end by the clear distance - float clearLocalLength = lineBase.GetNormalizedLengthFromWorldLength(clearWorldLength - cursorOffsetWorldLength, maxClampLineSteps); - lineBase.LineEndClamp = clearLocalLength; - } + base.OnPostRaycast(); } - - #endregion IPointer Implementation } } \ No newline at end of file diff --git a/Runtime/Input/Interactors/GenericPointer.cs b/Runtime/Input/Interactors/GenericPointer.cs index 448a6473..7f23ef5d 100644 --- a/Runtime/Input/Interactors/GenericPointer.cs +++ b/Runtime/Input/Interactors/GenericPointer.cs @@ -63,6 +63,9 @@ public virtual IController Controller /// public bool IsOverUI { get; } = false; + /// + public bool IsInputDown { get; private set; } + /// public virtual IInputSource InputSource { diff --git a/Runtime/Input/Interactors/IInteractor.cs b/Runtime/Input/Interactors/IInteractor.cs index fbc2ca9b..e7222591 100644 --- a/Runtime/Input/Interactors/IInteractor.cs +++ b/Runtime/Input/Interactors/IInteractor.cs @@ -36,6 +36,11 @@ public interface IInteractor : IEqualityComparer /// bool IsFarInteractor { get; } + /// + /// true, if any is down on this . + /// + bool IsInputDown { get; } + /// /// This pointer's input source parent. /// diff --git a/Runtime/Input/Interactors/IInteractorVisualizer.cs b/Runtime/Input/Interactors/IInteractorVisualizer.cs index d9b31501..d66cd6d5 100644 --- a/Runtime/Input/Interactors/IInteractorVisualizer.cs +++ b/Runtime/Input/Interactors/IInteractorVisualizer.cs @@ -5,6 +5,19 @@ namespace RealityToolkit.Input.Interactors { public interface IInteractorVisualizer { + /// + /// The visualized. + /// + IInteractor Interactor { get; } + /// + /// Called before all rays have casted on the . + /// + void OnPreRaycast(); + + /// + /// Called after all rays have casted on the . + /// + void OnPostRaycast(); } } diff --git a/Runtime/Input/Interactors/LineInteractorVisualizer.cs b/Runtime/Input/Interactors/LineInteractorVisualizer.cs index 4131a44d..b53d10fc 100644 --- a/Runtime/Input/Interactors/LineInteractorVisualizer.cs +++ b/Runtime/Input/Interactors/LineInteractorVisualizer.cs @@ -1,10 +1,201 @@ // Copyright (c) Reality Collective. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +using RealityCollective.Utilities.Extensions; +using RealityToolkit.Utilities.Lines.DataProviders; +using RealityToolkit.Utilities.Lines.Renderers; +using UnityEngine; + namespace RealityToolkit.Input.Interactors { public class LineInteractorVisualizer : BaseInteractorVisualizer { + [SerializeField] + private BaseLineDataProvider lineBase = null; + + [SerializeField] + [Tooltip("If no line renderers are specified, this array will be auto-populated on startup.")] + private BaseLineRenderer[] lineRenderers = null; + + [SerializeField] + private Gradient defaultLineColor = new Gradient(); + + public Gradient DefaultLineColor + { + get => defaultLineColor; + set => defaultLineColor = value; + } + + [SerializeField] + private Gradient lineColorInputDown = new Gradient(); + + public Gradient LineColorInputDown + { + get => lineColorInputDown; + set => lineColorInputDown = value; + } + + private FarInteractor farInteractor; + /// + public override IInteractor Interactor + { + get => farInteractor; + set + { + if (value is FarInteractor farInteractor) + { + this.farInteractor = farInteractor; + return; + } + + Debug.LogError($"{nameof(LineInteractorVisualizer)} is only meant to be used with {nameof(FarInteractor)}s."); + } + } + + protected virtual void OnValidate() + { + CheckInitialization(); + } + + protected virtual void OnEnable() + { + CheckInitialization(); + } + + private void CheckInitialization() + { + if (lineBase.IsNull()) + { + lineBase = GetComponent(); + } + + if (lineBase.IsNull()) + { + Debug.LogError($"No {nameof(BaseLineDataProvider)} found on {gameObject.name}."); + } + + if (lineBase.IsNotNull() && (lineRenderers == null || lineRenderers.Length == 0)) + { + lineRenderers = lineBase.GetComponentsInChildren(); + } + + if (lineRenderers == null || lineRenderers.Length == 0) + { + Debug.LogError($"No {nameof(BaseLineRenderer)}s found on {gameObject.name}."); + } + } + + /// + public override void OnPreRaycast() + { + Debug.Assert(lineBase.IsNotNull()); + + lineBase.UpdateMatrix(); + + if (Interactor == null || !Interactor.IsInteractionEnabled) + { + lineBase.enabled = false; + return; + } + + if (Interactor.RayStabilizer != null) + { + Interactor.RayStabilizer.UpdateStability(Interactor.Rays[0].Origin, Interactor.Rays[0].Direction); + Interactor.Rays[0].CopyRay(Interactor.RayStabilizer.StableRay, Interactor.Extent); + } + + Interactor.TryGetPointerPosition(out var pointerPosition); + Interactor.TryGetPointerRotation(out var pointerRotation); + + // Set our first and last points + lineBase.FirstPoint = pointerPosition; + + if (Interactor.IsFocusLocked && Interactor.Result.CurrentTarget != null) + { + if (Interactor.SyncedTarget != null) + { + // Now raycast out like nothing happened so we can get an updated pointer position. + lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Interactor.Extent); + } + else + { + // Set the line to the locked position. + lineBase.LastPoint = Interactor.Result.EndPoint; + } + } + else + { + lineBase.LastPoint = pointerPosition + pointerRotation * (Vector3.forward * Interactor.Extent); + } + + var stepSize = 1f / Interactor.Rays.Length; + var lastPoint = lineBase.GetUnClampedPoint(0f); + + for (int i = 0; i < Interactor.Rays.Length; i++) + { + var currentPoint = lineBase.GetUnClampedPoint(stepSize * (i + 1)); + Interactor.Rays[i].UpdateRayStep(ref lastPoint, ref currentPoint); + lastPoint = currentPoint; + } + } + + /// + public override void OnPostRaycast() + { + if (!Interactor.IsInteractionEnabled) + { + lineBase.enabled = false; + return; + } + + lineBase.enabled = true; + + Gradient lineColor; + + lineColor = Interactor.IsInputDown ? LineColorInputDown : DefaultLineColor; + var maxClampLineSteps = farInteractor.LineCastResolution; + + // Used to ensure the line doesn't extend beyond the cursor + float cursorOffsetWorldLength = Interactor.BaseCursor?.SurfaceCursorDistance ?? 0f; + + // The distance the ray travels through the world before it hits something. + // Measured in world-units (as opposed to normalized distance). + var clearWorldLength = (Interactor.Result?.CurrentTarget != null) ? Interactor.Result.RayDistance : Interactor.Extent; + + for (var i = 0; i < lineRenderers.Length; i++) + { + var lineRenderer = lineRenderers[i]; + // Renderers are enabled by default if line is enabled + lineRenderer.enabled = true; + maxClampLineSteps = Mathf.Max(maxClampLineSteps, lineRenderer.LineStepCount); + lineRenderer.LineColor = lineColor; + } + + // If focus is locked, we're sticking to the target + // So don't clamp the world length + if (Interactor.IsFocusLocked && Interactor.Result.CurrentTarget != null) + { + if (Interactor.SyncedTarget != null) + { + if (Interactor.Result.GrabPoint == Vector3.zero) + { + lineBase.LastPoint = Interactor.Result.EndPoint; + } + else + { + lineBase.LastPoint = Interactor.Result.GrabPoint; + } + } + var cursorOffsetLocalLength = lineBase.GetNormalizedLengthFromWorldLength(cursorOffsetWorldLength); + lineBase.LineEndClamp = 1 - cursorOffsetLocalLength; + } + else + { + // Otherwise clamp the line end by the clear distance + float clearLocalLength = lineBase.GetNormalizedLengthFromWorldLength(clearWorldLength - cursorOffsetWorldLength, maxClampLineSteps); + lineBase.LineEndClamp = clearLocalLength; + } + } } } diff --git a/Runtime/Utilities/Lines/Modules/BaseLineDataProvider.cs b/Runtime/Utilities/Lines/Modules/BaseLineDataProvider.cs index 61422f73..9a407931 100644 --- a/Runtime/Utilities/Lines/Modules/BaseLineDataProvider.cs +++ b/Runtime/Utilities/Lines/Modules/BaseLineDataProvider.cs @@ -1,6 +1,7 @@ // Copyright (c) Reality Collective. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. +using RealityCollective.Utilities.Extensions; using RealityToolkit.Definitions.Lines; using RealityToolkit.Utilities.Physics.Distorters; using System.Collections.Generic; @@ -60,7 +61,7 @@ public float LineEndClamp /// public Transform LineTransform { - get => customLineTransform != null ? customLineTransform : transform; + get => customLineTransform.IsNotNull() ? customLineTransform : transform; set => customLineTransform = value; }