Skip to content

Commit

Permalink
[MRKT3] Making custom reticles a bit easier in MRTK3. (#11758)
Browse files Browse the repository at this point in the history
## Overview

The intent of this change is to make attaching a custom reticle easier in MRTK3. The custom reticle is now attached to the base reticle's root transform, removing the need for the custom reticle to position itself.

Also, reverting changes made to `RecticleMagnetism`. With this change, `RecticleMagnetisms` once again uses an interface for setting progress, instead of requiring a `RingReticle` component.  Also, instead of using the old `IVariableReticle`, `RecticleMagnetisms` is now using `IVariableProgressReticle` to better indicate the propose of the interface.

I also rename the newer `IVariableReticle` to `IRecticleVisual` to better indicate its purpose.

This change is in response to #11751

## Changes
- Fixes: #11751
  • Loading branch information
AMollis authored Aug 4, 2023
1 parent 03e9b2b commit c86b0e9
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A reticle that implements some visual effect controllable by a single float value.
/// A customizable visual component of a reticle.
/// </summary>
public interface IVariableReticle
/// <remarks>
/// Implementations of <see cref="IReticleVisual"/> 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 <see cref="XRBaseInteractable.AttachCustomReticle(IXRInteractor)"/>.
/// </remarks>c
public interface IReticleVisual
{
/// <summary>
/// Updates visuals as needed for the variable reticle.
/// Updates the visual parts of the reticle.
/// </summary>
public void UpdateVisuals(VariableReticleUpdateArgs args);
public void UpdateVisual(ReticleVisualUpdateArgs args);
}

/// <summary>
/// A struct to store the arguments passed to UpdateVisuals
/// A struct to store the arguments passed to <see cref="IReticleVisual.UpdateVisual"/>
/// including the interactor associated with the reticle, and reticle position and normal.
/// </summary>
public struct VariableReticleUpdateArgs
public struct ReticleVisualUpdateArgs
{
/// <summary>
/// XRRayInteractor that the reticle serves as a visual for.
Expand All @@ -39,9 +44,9 @@ public struct VariableReticleUpdateArgs
public Vector3 ReticleNormal;

/// <summary>
/// Initializes a <see cref="VariableReticleUpdateArgs"/> struct.
/// Initializes a <see cref="ReticleVisualUpdateArgs"/> struct.
/// </summary>
public VariableReticleUpdateArgs(IXRInteractor interactor, Vector3 reticlePosition, Vector3 reticleNormal)
public ReticleVisualUpdateArgs(IXRInteractor interactor, Vector3 reticlePosition, Vector3 reticleNormal)
{
Interactor = interactor;
ReticlePosition = reticlePosition;
Expand Down
11 changes: 11 additions & 0 deletions com.microsoft.mrtk.core/Interactors/IReticleVisual.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions com.microsoft.mrtk.core/Interactors/IVariableProgressReticle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A reticle that is capable of displaying interaction progress.
/// </summary>
/// <remarks>
/// This may be used to show selection progress and touch proximity.
/// </remarks>
/// <seealso cref="VariableProgressReticleUpdateArgs"/>
public interface IVariableProgressReticle
{
/// <summary>
/// Update the progress of the visual.
/// </summary>
public void UpdateProgress(VariableProgressReticleUpdateArgs args);
}

/// <summary>
/// A struct to store the arguments passed to <see cref="IVariableProgressReticle.UpdateProgress"/>.
/// </summary>
public struct VariableProgressReticleUpdateArgs
{
/// <summary>
/// A value from 0 to 1 indicating interaction progress of an.
/// </summary>
/// <remarks>
/// This may be used to show selection progress and touch proximity.
/// </remarks>
public float Progress;

/// <summary>
/// Initializes a <see cref="VariableReticleUpdateArgs"/> struct.
/// </summary>
public VariableProgressReticleUpdateArgs(float progress)
{
Progress = progress;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public interface IVariableSelectInteractor : IXRSelectInteractor, IXRHoverIntera
/// amount of "selection" that this interactor is performing.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
float SelectProgress { get; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ public class SpatialMouseInteractorCursorVisual : BaseReticleVisual
/// <summary>
/// A Unity event function that is called when the script component has been enabled.
/// </summary>
protected virtual void OnEnable()
protected override void OnEnable()
{
base.OnEnable();

mouseInteractor.selectEntered.AddListener(LocateTargetHitPoint);

Application.onBeforeRender += OnBeforeRenderCursor;
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

Expand All @@ -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;

/// <summary>
/// The root of the reticle visuals.
/// </summary>
/// <remarks>
/// This transform hold both the base and custom reticle.
/// </remarks>
protected Transform ReticleRoot => reticleRoot;

[SerializeField]
[Tooltip("The reticle model to use when the interactable doesn't specify a custom one.")]
private GameObject baseReticle;

/// <summary>
/// The reticle model to use when the interactable doesn't specify a custom one.
/// </summary>
protected GameObject BaseReticle => baseReticle;

/// <summary>
/// Staging area for custom reticles that interactors can attach to show unique visuals.
/// </summary>
protected GameObject customReticle;
protected GameObject CustomReticle
{
get;
private set;
}

/// <summary>
/// Is there a custom reticle currently attached to this interactor?
/// </summary>
protected bool customReticleAttached;
protected bool CustomReticleAttached
{
get;
private set;
}

/// <summary>
/// The current reticle that the interactor is using.
/// </summary>
public GameObject Reticle => customReticleAttached ? customReticle : baseReticle;
public GameObject Reticle => CustomReticleAttached ? CustomReticle : baseReticle;

private IVariableReticle variableReticle;
private IReticleVisual visual;

/// <summary>
/// Cached variable reticle reference.
/// Cached reference to the <see cref="IReticleVisual"/> component on <see cref="Reticle"/>.
/// </summary>
protected IVariableReticle VariableReticle
protected IReticleVisual Visual
{
get
{
if (variableReticle == null)
if (visual == null)
{
variableReticle = Reticle.GetComponent<IVariableReticle>();
visual = Reticle.GetComponent<IReticleVisual>();
}

return variableReticle;
return visual;
}
}

/// <summary>
/// A Unity event function that is called when the script component has been enabled.
/// </summary>
protected virtual void OnEnable()
{
// If no reticle root is specified, use the interactor's transform.
if (reticleRoot == null)
{
reticleRoot = transform;
}
}

#region IXRCustomReticleProvider

/// <inheritdoc />
public bool AttachCustomReticle(GameObject reticleInstance)
{
if (!customReticleAttached)
if (!CustomReticleAttached)
{
if (baseReticle != null)
{
Expand All @@ -66,37 +105,37 @@ 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<IVariableReticle>();
// Clear old references to the old Reticle components.
visual = null;

return true;
}

/// <inheritdoc />
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.
Expand All @@ -105,9 +144,10 @@ public bool RemoveCustomReticle()
baseReticle.SetActive(true);
}

customReticle = null;
customReticleAttached = false;
variableReticle = null;
CustomReticle = null;
CustomReticleAttached = false;
visual = null;

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ public class MRTKPokeReticleVisual : BaseReticleVisual
/// <summary>
/// A Unity event function that is called when the script component has been enabled.
/// </summary>
protected void OnEnable()
protected override void OnEnable()
{
base.OnEnable();
Application.onBeforeRender += UpdateReticle;
}

/// <summary>
/// A Unity event function that is called when the script component has been disabled.
/// </summary>
protected void OnDisable()
protected virtual void OnDisable()
{
UpdateReticle();
Application.onBeforeRender -= UpdateReticle;
Expand Down Expand Up @@ -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));
}
}
}
}
Expand Down
Loading

0 comments on commit c86b0e9

Please sign in to comment.