diff --git a/com.microsoft.mrtk.core/Interactors/IVariableReticle.cs b/com.microsoft.mrtk.core/Interactors/IReticleVisual.cs similarity index 55% rename from com.microsoft.mrtk.core/Interactors/IVariableReticle.cs rename to com.microsoft.mrtk.core/Interactors/IReticleVisual.cs index df0fdfdf14b..2f48dce7fb1 100644 --- a/com.microsoft.mrtk.core/Interactors/IVariableReticle.cs +++ b/com.microsoft.mrtk.core/Interactors/IReticleVisual.cs @@ -7,21 +7,26 @@ namespace Microsoft.MixedReality.Toolkit { /// - /// A reticle that implements some visual effect controllable by a single float value. + /// A customizable visual component of a reticle. /// - public interface IVariableReticle + /// + /// Implementations of can receive updates to the base reticle's + /// position and normal every frame, if the base reticle is shown. For more information on how + /// set a custom reticle, see . + /// c + public interface IReticleVisual { /// - /// Updates visuals as needed for the variable reticle. + /// Updates the visual parts of the reticle. /// - public void UpdateVisuals(VariableReticleUpdateArgs args); + public void UpdateVisual(ReticleVisualUpdateArgs args); } /// - /// A struct to store the arguments passed to UpdateVisuals + /// A struct to store the arguments passed to /// including the interactor associated with the reticle, and reticle position and normal. /// - public struct VariableReticleUpdateArgs + public struct ReticleVisualUpdateArgs { /// /// XRRayInteractor that the reticle serves as a visual for. @@ -39,9 +44,9 @@ public struct VariableReticleUpdateArgs public Vector3 ReticleNormal; /// - /// Initializes a struct. + /// Initializes a struct. /// - public VariableReticleUpdateArgs(IXRInteractor interactor, Vector3 reticlePosition, Vector3 reticleNormal) + public ReticleVisualUpdateArgs(IXRInteractor interactor, Vector3 reticlePosition, Vector3 reticleNormal) { Interactor = interactor; ReticlePosition = reticlePosition; diff --git a/com.microsoft.mrtk.core/Interactors/IReticleVisual.cs.meta b/com.microsoft.mrtk.core/Interactors/IReticleVisual.cs.meta new file mode 100644 index 00000000000..519b914151a --- /dev/null +++ b/com.microsoft.mrtk.core/Interactors/IReticleVisual.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 137841acaf4307541a62bdeeb10baa8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs b/com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs new file mode 100644 index 00000000000..7c97f18510c --- /dev/null +++ b/com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.MixedReality.Toolkit +{ + /// + /// A reticle that is capable of displaying interaction progress. + /// + /// + /// This may be used to show selection progress and touch proximity. + /// + /// + public interface IVariableProgressReticle + { + /// + /// Update the progress of the visual. + /// + public void UpdateProgress(VariableProgressReticleUpdateArgs args); + } + + /// + /// A struct to store the arguments passed to . + /// + public struct VariableProgressReticleUpdateArgs + { + /// + /// A value from 0 to 1 indicating interaction progress of an. + /// + /// + /// This may be used to show selection progress and touch proximity. + /// + public float Progress; + + /// + /// Initializes a struct. + /// + public VariableProgressReticleUpdateArgs(float progress) + { + Progress = progress; + } + } +} diff --git a/com.microsoft.mrtk.core/Interactors/IVariableReticle.cs.meta b/com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs.meta similarity index 100% rename from com.microsoft.mrtk.core/Interactors/IVariableReticle.cs.meta rename to com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs.meta diff --git a/com.microsoft.mrtk.core/Interactors/IVariableSelectInteractor.cs b/com.microsoft.mrtk.core/Interactors/IVariableSelectInteractor.cs index 47cf1180cec..64d0784bf9e 100644 --- a/com.microsoft.mrtk.core/Interactors/IVariableSelectInteractor.cs +++ b/com.microsoft.mrtk.core/Interactors/IVariableSelectInteractor.cs @@ -16,8 +16,8 @@ public interface IVariableSelectInteractor : IXRSelectInteractor, IXRHoverIntera /// amount of "selection" that this interactor is performing. /// /// - /// For gaze-pinch interactors, this is the pinch progress; - /// for motion controllers, this is the analog trigger press amount. + /// For gaze-pinch interactors, this is the pinch progress. + /// For motion controllers, this is the analog trigger press amount. /// float SelectProgress { get; } } diff --git a/com.microsoft.mrtk.input/Assets/Prefabs/MRTK LeftHand Controller.prefab b/com.microsoft.mrtk.input/Assets/Prefabs/MRTK LeftHand Controller.prefab index 34ec31e2b0b..51b87e5a704 100644 --- a/com.microsoft.mrtk.input/Assets/Prefabs/MRTK LeftHand Controller.prefab +++ b/com.microsoft.mrtk.input/Assets/Prefabs/MRTK LeftHand Controller.prefab @@ -312,8 +312,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 70ec73306ca3ef44a95bbb45b96f0538, type: 3} m_Name: m_EditorClassIdentifier: - baseReticle: {fileID: 8992338914735331379} reticleRoot: {fileID: 3988544559415115452} + baseReticle: {fileID: 8992338914735331379} rayInteractor: {fileID: 2940030942784507886} proximityLight: {fileID: 4448665028262160152} visibilitySettings: 0 @@ -1100,6 +1100,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 518e5c0e6d2c664478d85d0ceae60c36, type: 3} m_Name: m_EditorClassIdentifier: + reticleRoot: {fileID: 0} baseReticle: {fileID: 8086910922498750391} pokeInteractor: {fileID: 1948193616346090107} proximityLight: {fileID: 3552666654439622812} @@ -1846,6 +1847,10 @@ PrefabInstance: propertyPath: fadeEnabled value: 1 objectReference: {fileID: 0} + - target: {fileID: 7949002557058872435, guid: fddee8b412d753e40a02681891de4a7b, type: 3} + propertyPath: displaySelectionProgress + value: 0 + objectReference: {fileID: 0} - target: {fileID: 9095796513279534737, guid: fddee8b412d753e40a02681891de4a7b, type: 3} propertyPath: m_IsActive value: 1 diff --git a/com.microsoft.mrtk.input/Experimental/SpatialMouse/Interactor/SpatialMouseInteractorCursorVisual.cs b/com.microsoft.mrtk.input/Experimental/SpatialMouse/Interactor/SpatialMouseInteractorCursorVisual.cs index 5e2908439d4..572f23a9213 100644 --- a/com.microsoft.mrtk.input/Experimental/SpatialMouse/Interactor/SpatialMouseInteractorCursorVisual.cs +++ b/com.microsoft.mrtk.input/Experimental/SpatialMouse/Interactor/SpatialMouseInteractorCursorVisual.cs @@ -35,8 +35,10 @@ public class SpatialMouseInteractorCursorVisual : BaseReticleVisual /// /// A Unity event function that is called when the script component has been enabled. /// - protected virtual void OnEnable() + protected override void OnEnable() { + base.OnEnable(); + mouseInteractor.selectEntered.AddListener(LocateTargetHitPoint); Application.onBeforeRender += OnBeforeRenderCursor; @@ -121,10 +123,10 @@ private void OnBeforeRenderCursor() Reticle.transform.position = reticlePosition; Reticle.transform.forward = reticleNormal; - // If the reticle is an IVariableSelectReticle, have the reticle update based on selectedness - if (VariableReticle != null) + // If the reticle is an IReticleVisual, have the reticle update based on selectedness + if (Visual != null) { - VariableReticle.UpdateVisuals(new VariableReticleUpdateArgs(mouseInteractor, reticlePosition, reticleNormal)); + Visual.UpdateVisual(new ReticleVisualUpdateArgs(mouseInteractor, reticlePosition, reticleNormal)); } if (Reticle.activeSelf == false) diff --git a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/BaseReticleVisual.cs b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/BaseReticleVisual.cs index 42bf93da352..fb3e80c39dc 100644 --- a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/BaseReticleVisual.cs +++ b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/BaseReticleVisual.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; @@ -16,48 +17,86 @@ namespace Microsoft.MixedReality.Toolkit.Input [DisallowMultipleComponent] public class BaseReticleVisual : MonoBehaviour, IXRCustomReticleProvider { + [SerializeField] + [Tooltip("The root of the reticle visuals")] + private Transform reticleRoot; + + /// + /// The root of the reticle visuals. + /// + /// + /// This transform hold both the base and custom reticle. + /// + protected Transform ReticleRoot => reticleRoot; + [SerializeField] [Tooltip("The reticle model to use when the interactable doesn't specify a custom one.")] private GameObject baseReticle; + /// + /// The reticle model to use when the interactable doesn't specify a custom one. + /// + protected GameObject BaseReticle => baseReticle; + /// /// Staging area for custom reticles that interactors can attach to show unique visuals. /// - protected GameObject customReticle; + protected GameObject CustomReticle + { + get; + private set; + } /// /// Is there a custom reticle currently attached to this interactor? /// - protected bool customReticleAttached; + protected bool CustomReticleAttached + { + get; + private set; + } /// /// The current reticle that the interactor is using. /// - public GameObject Reticle => customReticleAttached ? customReticle : baseReticle; + public GameObject Reticle => CustomReticleAttached ? CustomReticle : baseReticle; - private IVariableReticle variableReticle; + private IReticleVisual visual; /// - /// Cached variable reticle reference. + /// Cached reference to the component on . /// - protected IVariableReticle VariableReticle + protected IReticleVisual Visual { get { - if (variableReticle == null) + if (visual == null) { - variableReticle = Reticle.GetComponent(); + visual = Reticle.GetComponent(); } - return variableReticle; + return visual; + } + } + + /// + /// A Unity event function that is called when the script component has been enabled. + /// + protected virtual void OnEnable() + { + // If no reticle root is specified, use the interactor's transform. + if (reticleRoot == null) + { + reticleRoot = transform; } } + #region IXRCustomReticleProvider /// public bool AttachCustomReticle(GameObject reticleInstance) { - if (!customReticleAttached) + if (!CustomReticleAttached) { if (baseReticle != null) { @@ -66,27 +105,27 @@ public bool AttachCustomReticle(GameObject reticleInstance) } else { - if (customReticle != null) + if (CustomReticle != null) { - customReticle.SetActive(false); + CustomReticle.SetActive(false); } } - customReticle = reticleInstance; - if (customReticle != null) + CustomReticle = reticleInstance; + if (CustomReticle != null) { - customReticle.SetActive(true); + CustomReticle.SetActive(true); - // Ensure the custom reticle is parented under this gameobject - customReticle.transform.parent = transform; - customReticle.transform.localPosition = Vector3.zero; - customReticle.transform.localRotation = Quaternion.identity; + // Ensure the custom reticle is parented under this game object + CustomReticle.transform.parent = reticleRoot; + CustomReticle.transform.localPosition = Vector3.zero; + CustomReticle.transform.localRotation = Quaternion.identity; } - customReticleAttached = true; + CustomReticleAttached = true; - // Make sure that the variable reticle now refers to the correct reticle - variableReticle = Reticle.GetComponent(); + // Clear old references to the old Reticle components. + visual = null; return true; } @@ -94,9 +133,9 @@ public bool AttachCustomReticle(GameObject reticleInstance) /// public bool RemoveCustomReticle() { - if (customReticle != null) + if (CustomReticle != null) { - customReticle.SetActive(false); + CustomReticle.SetActive(false); } // If we have a standard reticle, re-enable that one. @@ -105,9 +144,10 @@ public bool RemoveCustomReticle() baseReticle.SetActive(true); } - customReticle = null; - customReticleAttached = false; - variableReticle = null; + CustomReticle = null; + CustomReticleAttached = false; + visual = null; + return false; } diff --git a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKPokeReticleVisual.cs b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKPokeReticleVisual.cs index e6fdb2e8bf0..e6b9084162c 100644 --- a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKPokeReticleVisual.cs +++ b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKPokeReticleVisual.cs @@ -24,15 +24,16 @@ public class MRTKPokeReticleVisual : BaseReticleVisual /// /// A Unity event function that is called when the script component has been enabled. /// - protected void OnEnable() + protected override void OnEnable() { + base.OnEnable(); Application.onBeforeRender += UpdateReticle; } /// /// A Unity event function that is called when the script component has been disabled. /// - protected void OnDisable() + protected virtual void OnDisable() { UpdateReticle(); Application.onBeforeRender -= UpdateReticle; @@ -62,6 +63,12 @@ private void UpdateReticle() proximityLight.SetActive(Reticle.activeSelf); } } + + // If the reticle is an IReticleVisual, have the reticle update based on selectedness + if (Visual != null) + { + Visual.UpdateVisual(new ReticleVisualUpdateArgs(pokeInteractor, Reticle.transform.position, Reticle.transform.forward)); + } } } } diff --git a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKRayReticleVisual.cs b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKRayReticleVisual.cs index a9eab2a6f70..ffe3cc39c27 100644 --- a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKRayReticleVisual.cs +++ b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/MRTKRayReticleVisual.cs @@ -16,10 +16,6 @@ namespace Microsoft.MixedReality.Toolkit.Input [DefaultExecutionOrder(XRInteractionUpdateOrder.k_BeforeRenderLineVisual)] public class MRTKRayReticleVisual : BaseReticleVisual { - [SerializeField] - [Tooltip("The root of the reticle visuals")] - private Transform reticleRoot; - [SerializeField] [Tooltip("The interactor which this visual represents.")] private XRRayInteractor rayInteractor; @@ -48,23 +44,19 @@ public ReticleVisibilitySettings VisibilitySettings /// /// A Unity event function that is called when the script component has been enabled. /// - private void OnEnable() + protected override void OnEnable() { + base.OnEnable(); + rayInteractor.selectEntered.AddListener(LocateTargetHitPoint); Application.onBeforeRender += UpdateReticle; - - // If no custom reticle root is specified, just use the interactor's transform. - if (reticleRoot == null) - { - reticleRoot = transform; - } UpdateReticle(); } /// /// A Unity event function that is called when the script component has been disabled. /// - private void OnDisable() + protected virtual void OnDisable() { rayInteractor.selectEntered.RemoveListener(LocateTargetHitPoint); Application.onBeforeRender -= UpdateReticle; @@ -75,7 +67,7 @@ private void OnDisable() /// /// A Unity event function that is called every frame, if this object is enabled. /// - private void LateUpdate() + protected virtual void LateUpdate() { // if running in batch mode the onBeforeRender event doesn't fire so // we need to update the reticle here @@ -112,22 +104,22 @@ private void UpdateReticle() } // If we have a reticle, set its position and rotation. - if (reticleRoot != null) + if (ReticleRoot != null) { if (reticleNormal != Vector3.zero) { - reticleRoot.transform.SetPositionAndRotation(reticlePosition, Quaternion.LookRotation(reticleNormal, Vector3.up)); + ReticleRoot.transform.SetPositionAndRotation(reticlePosition, Quaternion.LookRotation(reticleNormal, Vector3.up)); } else { - reticleRoot.transform.position = reticlePosition; + ReticleRoot.transform.position = reticlePosition; } } - // If the reticle is an IVariableSelectReticle, have the reticle update based on selectedness - if (VariableReticle != null) + // If the reticle is an IReticleVisual, have the reticle update based on selectedness + if (Visual != null) { - VariableReticle.UpdateVisuals(new VariableReticleUpdateArgs(rayInteractor, reticlePosition, reticleNormal)); + Visual.UpdateVisual(new ReticleVisualUpdateArgs(rayInteractor, reticlePosition, reticleNormal)); } } else diff --git a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/ReticleMagnetism.cs b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/ReticleMagnetism.cs index de9f3835ace..af812b8b164 100644 --- a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/ReticleMagnetism.cs +++ b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/ReticleMagnetism.cs @@ -94,7 +94,7 @@ public ProximityDetector Detector public float RotationSmoothing { get => rotationSmoothing; set => rotationSmoothing = value; } // Reference to the variable visuals. - private IVariableReticle variableReticleVisuals; + private IVariableProgressReticle variableProgressVisual; // The smoothed magnetization point, usually the nearest point on the nearest collider. // Not necessarily the same as the current reticle rotation! @@ -126,7 +126,7 @@ public ProximityDetector Detector private void Awake() { // Optional. - variableReticleVisuals = GetComponentInChildren(includeInactive: true); + variableProgressVisual = GetComponentInChildren(includeInactive: true); pokeInteractor = gameObject.GetComponentInParent(includeInactive: true); } @@ -243,10 +243,10 @@ private void Update() } } - // If we're using variable reticle visuals, update the visuals with the progress/proximity. - if (variableReticleVisuals != null && variableReticleVisuals is RingReticle ringReticleVisuals) + // If we're using variable progress visuals, update the visuals with the progress/proximity. + if (variableProgressVisual != null) { - ringReticleVisuals.UpdateVisuals(1.0f - progressFraction); + variableProgressVisual.UpdateProgress(new VariableProgressReticleUpdateArgs(1.0f - progressFraction)); } } diff --git a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/RingReticle.cs b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/RingReticle.cs index bc1fb57a524..6455fe67a1d 100644 --- a/com.microsoft.mrtk.input/Interactors/InteractorVisuals/RingReticle.cs +++ b/com.microsoft.mrtk.input/Interactors/InteractorVisuals/RingReticle.cs @@ -10,14 +10,14 @@ namespace Microsoft.MixedReality.Toolkit.Input /// A ring-like reticle that expands/contracts. /// [AddComponentMenu("MRTK/Input/Ring Reticle")] - internal class RingReticle : MonoBehaviour, IVariableReticle + internal class RingReticle : MonoBehaviour, IReticleVisual, IVariableProgressReticle { [SerializeField] - [Tooltip("The amount of smoothing to apply to the reticle's grow/shrink effect.")] + [Tooltip("The amount of smoothing to apply to the reticle's grow and shrink effect.")] private float animationSmoothing = 0.000001f; /// - /// The amount of smoothing to apply to the reticle's grow/shrink effect. + /// The amount of smoothing to apply to the reticle's grow and shrink effect. /// public float AnimationSmoothing { @@ -26,11 +26,11 @@ public float AnimationSmoothing } [SerializeField] - [Tooltip("Should the ring fade when the value is small?")] + [Tooltip("Turn on or off the ring fade when the value is small.")] private bool fadeEnabled = false; /// - /// Should the ring fade when the value is small? + /// Turn on or off the ring fade when the value is small. /// public bool FadeEnabled { @@ -42,6 +42,19 @@ public bool FadeEnabled } } + [SerializeField] + [Tooltip("Turn on or off the handling of selection progress.")] + private bool displaySelectionProgress = true; + + /// + /// Turn on or off the handling of selection progress. + /// + public bool DisplaySelectionProgress + { + get => displaySelectionProgress; + set => displaySelectionProgress = value; + } + private MaterialPropertyBlock propertyBlock; private float smoothedValue = 0.0f; @@ -77,30 +90,35 @@ protected void OnEnable() } /// - public void UpdateVisuals(float value) + public void UpdateProgress(VariableProgressReticleUpdateArgs args) { - if (reticleRenderer == null || Mathf.Approximately(value, smoothedValue)) { return; } - - smoothedValue = Smoothing.SmoothTo(smoothedValue, value, animationSmoothing, Time.deltaTime); - SetReticleShrink(smoothedValue); + UpdateReticleProgressVisual(args.Progress); } - /// Extracts values from VariableReticleArgs to call UpdateVisuals - public void UpdateVisuals(VariableReticleUpdateArgs args) + /// + public void UpdateVisual(ReticleVisualUpdateArgs args) { - if (args.Interactor is XRRayInteractor rayInteractor) + if (displaySelectionProgress) { - if (rayInteractor is IVariableSelectInteractor variableSelectInteractor) + if (args.Interactor is IVariableSelectInteractor variableSelectInteractor) { - UpdateVisuals(variableSelectInteractor.SelectProgress); + UpdateReticleProgressVisual(variableSelectInteractor.SelectProgress); } - else + else if (args.Interactor is IXRSelectInteractor selectInteractor) { - UpdateVisuals(rayInteractor.isSelectActive ? 1 : 0); + UpdateReticleProgressVisual(selectInteractor.isSelectActive ? 1 : 0); } } } + private void UpdateReticleProgressVisual(float progress) + { + if (reticleRenderer == null || Mathf.Approximately(progress, smoothedValue)) { return; } + + smoothedValue = Smoothing.SmoothTo(smoothedValue, progress, animationSmoothing, Time.deltaTime); + SetReticleShrink(smoothedValue); + } + private void SetReticleShrink(float value) { reticleRenderer.GetPropertyBlock(propertyBlock); diff --git a/com.microsoft.mrtk.spatialmanipulation/BoundsControl/Visuals/SpatialManipulationReticle.cs b/com.microsoft.mrtk.spatialmanipulation/BoundsControl/Visuals/SpatialManipulationReticle.cs index 343b4ff3ed7..cdc387117e3 100644 --- a/com.microsoft.mrtk.spatialmanipulation/BoundsControl/Visuals/SpatialManipulationReticle.cs +++ b/com.microsoft.mrtk.spatialmanipulation/BoundsControl/Visuals/SpatialManipulationReticle.cs @@ -11,7 +11,7 @@ namespace Microsoft.MixedReality.Toolkit.SpatialManipulation /// A reticle used to visualize spatial manipulation capabilities when hovering over a bounding box handle. /// The reticle is oriented in relation to the bounding box, to indicate the direction for rotation or scaling. /// - public class SpatialManipulationReticle : MonoBehaviour, IVariableReticle + public class SpatialManipulationReticle : MonoBehaviour, IReticleVisual { /// /// The type of the reticle visuals. Scale or Rotate. @@ -28,7 +28,7 @@ public class SpatialManipulationReticle : MonoBehaviour, IVariableReticle /// /// Rotates the cursor reticle based on the hovered or selected handle's position relative to the box visuals. /// - public void UpdateVisuals(VariableReticleUpdateArgs args) + public void UpdateVisual(ReticleVisualUpdateArgs args) { if (args.Interactor is XRRayInteractor rayInteractor) {