-
Notifications
You must be signed in to change notification settings - Fork 161
Add support for XR_MSFT_hand_tracking_mesh and XR_ANDROID_hand_mesh #993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
keveleigh
merged 49 commits into
MixedRealityToolkit:feature/XRI3
from
keveleigh:platform-hand-mesh
Feb 14, 2026
Merged
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
6b03ef7
Initial drop
keveleigh ae61e24
Iterate
keveleigh 6c1fd65
Update PlatformHandMeshVisualizer.cs
keveleigh 920b4e4
Update PlatformHandMeshVisualizer.cs
keveleigh e5d3cee
Docs update
keveleigh 6777560
Move prefabs into own folder
keveleigh 5dd2b45
Update PlatformHandMeshVisualizer.cs
keveleigh b146ea9
Remove temp hand mesh prefabs
keveleigh 464f0a1
Add hand mesh manager
keveleigh 04c91a9
Iterate
keveleigh 6bfd6fe
Iterate
keveleigh eb16746
Update PlatformHandMeshVisualizer.cs
keveleigh dae1ea2
Add AXR hand mesh support
keveleigh b02dc18
Update PlatformHandMeshVisualizer.cs
keveleigh 941b07e
Update PlatformHandMeshVisualizer.cs
keveleigh 9660491
Update PlatformHandMeshVisualizer.cs
keveleigh 6cb69de
Ensure the bounds are recalculated
keveleigh 3d68798
Ensure we transform from the playspace pose
keveleigh f4be443
Update CHANGELOG.md
keveleigh c2322a1
Update PlatformHandMeshVisualizer.cs
keveleigh aa77219
Update PlatformHandMeshVisualizer.cs
keveleigh bcf3c89
Update shader to use wrist position for fade, if given
keveleigh 6bae25e
Iterate back to sphere
keveleigh 523e67a
Iterate shaders again
keveleigh bc8ba5b
Iterate shaders again
keveleigh 8594aac
Update to include outline
keveleigh 0df4acf
Update material defaults
keveleigh 96b1371
Iterate to using Unity's API instead of Google's
keveleigh 0385ca6
Update AndroidXRConfig.cs
keveleigh 221b560
Update HandMeshVisualizer.cs
keveleigh aea0616
Don't update more than once per frame
keveleigh 74938c0
Update PlatformHandMeshVisualizer.cs
keveleigh 7b37a39
Revert "Iterate to using Unity's API instead of Google's"
keveleigh 96e6235
Update PlatformHandMeshVisualizer.cs
keveleigh e67fa3b
Use input action references for tracked state
keveleigh c995946
Reapply "Iterate to using Unity's API instead of Google's"
keveleigh c84fcff
Update Hands version
keveleigh 13467cf
Some optimizations and improvements
keveleigh 03662cf
Another iteration
keveleigh 222f74a
Revert "Reapply "Iterate to using Unity's API instead of Google's""
keveleigh 4f1690f
Mark the hand mesh as dynamic
keveleigh 4caf3ed
Iterate visualizers
keveleigh 08f070c
Update PlatformHandMeshVisualizer.cs
keveleigh fa27087
Reapply "Reapply "Iterate to using Unity's API instead of Google's""
keveleigh 092ea4b
Update PlatformHandMeshVisualizer.cs
keveleigh c10a28c
Update PlatformHandMeshVisualizer.cs
keveleigh c3253ed
Move to event handler?
keveleigh 5d3ab69
Move back to polling
keveleigh 5f57f02
Update PlatformHandMeshVisualizer.cs
keveleigh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
232 changes: 232 additions & 0 deletions
232
org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| // Copyright (c) Mixed Reality Toolkit Contributors | ||
| // Licensed under the BSD 3-Clause | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using UnityEngine; | ||
| using UnityEngine.XR; | ||
| using UnityEngine.XR.Interaction.Toolkit; | ||
| using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; | ||
|
|
||
| namespace MixedReality.Toolkit.Input | ||
| { | ||
| public abstract class HandMeshVisualizer : MonoBehaviour, ISelectInputVisualizer | ||
| { | ||
| [SerializeField] | ||
| [Tooltip("The XRNode on which this hand is located.")] | ||
| private XRNode handNode = XRNode.LeftHand; | ||
|
|
||
| /// <summary> The XRNode on which this hand is located. </summary> | ||
| public XRNode HandNode { get => handNode; set => handNode = value; } | ||
|
|
||
| [SerializeField] | ||
| [Tooltip("When true, this visualizer will render rigged hands even on XR devices " + | ||
| "with transparent displays or with passthrough enabled. When false, the rigged hands will only render " + | ||
| "on devices with opaque displays. This behavior uses XRDisplaySubsystem.displayOpaque.")] | ||
| private bool showHandsOnTransparentDisplays; | ||
|
|
||
| /// <summary> | ||
| /// When true, this visualizer will render rigged hands even on XR devices with transparent displays or with passthrough enabled. | ||
| /// When false, the rigged hands will only render on devices with opaque displays. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This behavior uses <see cref="XRDisplaySubsystem.displayOpaque"/>. | ||
| /// Usually, it's recommended not to show hand visualization on transparent displays as it can | ||
| /// distract from the user's real hands, and cause a "double image" effect that can be disconcerting. | ||
| /// </remarks> | ||
| public bool ShowHandsOnTransparentDisplays | ||
| { | ||
| get => showHandsOnTransparentDisplays; | ||
| set => showHandsOnTransparentDisplays = value; | ||
| } | ||
|
|
||
| [SerializeField] | ||
| [Tooltip("Name of the shader property used to drive pinch-amount-based visual effects. " + | ||
| "Generally, maps to something like a glow or an outline color!")] | ||
| private string pinchAmountMaterialProperty = "_PinchAmount"; | ||
|
|
||
| [SerializeField] | ||
| [Tooltip("The input reader used when pinch selecting an interactable.")] | ||
| private XRInputButtonReader selectInput = new XRInputButtonReader("Select"); | ||
|
|
||
| #region ISelectInputVisualizer implementation | ||
|
|
||
| /// <summary> | ||
| /// Input reader used when pinch selecting an interactable. | ||
| /// </summary> | ||
| public XRInputButtonReader SelectInput | ||
| { | ||
| get => selectInput; | ||
| set => SetInputProperty(ref selectInput, value); | ||
| } | ||
|
|
||
| #endregion ISelectInputVisualizer implementation | ||
|
|
||
| // The property block used to modify the pinch amount property on the material | ||
| private MaterialPropertyBlock propertyBlock = null; | ||
|
|
||
| // Scratch list for checking for the presence of display subsystems. | ||
| private readonly List<XRDisplaySubsystem> displaySubsystems = new List<XRDisplaySubsystem>(); | ||
|
|
||
| // The XRController that is used to determine the pinch strength (i.e., select value!) | ||
| [Obsolete("This field has been deprecated in version 4.0.0 and will be removed in a future version. Use the SelectInput property instead.")] | ||
| private XRBaseController controller; | ||
|
|
||
| /// <summary> | ||
| /// The list of button input readers used by this interactor. This interactor will automatically enable or disable direct actions | ||
| /// if that mode is used during <see cref="OnEnable"/> and <see cref="OnDisable"/>. | ||
| /// </summary> | ||
| /// <seealso cref="XRInputButtonReader.EnableDirectActionIfModeUsed"/> | ||
| /// <seealso cref="XRInputButtonReader.DisableDirectActionIfModeUsed"/> | ||
| private readonly List<XRInputButtonReader> buttonReaders = new List<XRInputButtonReader>(); | ||
|
|
||
| /// <summary> | ||
| /// Whether this visualizer currently has a loaded and visible hand mesh or not. | ||
| /// </summary> | ||
| protected internal bool IsRendering => HandRenderer != null && HandRenderer.enabled; | ||
|
|
||
| /// <summary> | ||
| /// The renderer for this visualizer, to use to visualize the pinch amount. | ||
| /// </summary> | ||
| protected abstract Renderer HandRenderer { get; } | ||
|
|
||
| /// <summary> | ||
| /// A Unity event function that is called when an enabled script instance is being loaded. | ||
| /// </summary> | ||
| protected virtual void Awake() | ||
| { | ||
| propertyBlock = new MaterialPropertyBlock(); | ||
| buttonReaders.Add(selectInput); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A Unity event function that is called when the script component has been enabled. | ||
| /// </summary> | ||
| protected virtual void OnEnable() | ||
| { | ||
| buttonReaders.ForEach(reader => reader?.EnableDirectActionIfModeUsed()); | ||
|
|
||
| // Ensure hand is not visible until we can update position first time. | ||
| HandRenderer.enabled = false; | ||
|
|
||
| Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, | ||
| $"HandVisualizer has an invalid XRNode ({handNode})!"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// A Unity event function that is called when the script component has been disabled. | ||
| /// </summary> | ||
| protected virtual void OnDisable() | ||
| { | ||
| buttonReaders.ForEach(reader => reader?.DisableDirectActionIfModeUsed()); | ||
|
|
||
| // Disable the rigged hand renderer when this component is disabled | ||
| HandRenderer.enabled = false; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Helper method for setting an input property. | ||
| /// </summary> | ||
| /// <param name="property">The <see langword="ref"/> to the field.</param> | ||
| /// <param name="value">The new value being set.</param> | ||
| /// <remarks> | ||
| /// If the application is playing, this method will also enable or disable directly embedded input actions | ||
| /// serialized by the input if that mode is used. It will also add or remove the input from the list of button inputs | ||
| /// to automatically manage enabling and disabling direct actions with this behavior. | ||
| /// </remarks> | ||
| /// <seealso cref="buttonReaders"/> | ||
| protected void SetInputProperty(ref XRInputButtonReader property, XRInputButtonReader value) | ||
| { | ||
| if (value == null) | ||
| { | ||
| Debug.LogError("Setting XRInputButtonReader property to null is disallowed and has therefore been ignored."); | ||
| return; | ||
| } | ||
|
|
||
| if (Application.isPlaying && property != null) | ||
| { | ||
| buttonReaders?.Remove(property); | ||
| property.DisableDirectActionIfModeUsed(); | ||
| } | ||
|
|
||
| property = value; | ||
|
|
||
| if (Application.isPlaying) | ||
| { | ||
| buttonReaders?.Add(property); | ||
| if (isActiveAndEnabled) | ||
| { | ||
| property.EnableDirectActionIfModeUsed(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| protected virtual bool ShouldRenderHand() | ||
| { | ||
| if (displaySubsystems.Count == 0) | ||
| { | ||
| SubsystemManager.GetSubsystems(displaySubsystems); | ||
| } | ||
|
|
||
| // Are we running on an XR display and it happens to be transparent? | ||
| // Probably shouldn't be showing rigged hands! (Users can | ||
| // specify showHandsOnTransparentDisplays if they disagree.) | ||
| if (displaySubsystems.Count > 0 && | ||
| displaySubsystems[0].running && | ||
| !displaySubsystems[0].displayOpaque && | ||
| !showHandsOnTransparentDisplays) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // All checks out! | ||
| return true; | ||
| } | ||
|
|
||
| protected virtual void UpdateHandMaterial() | ||
| { | ||
| if (HandRenderer == null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| // Update the hand material | ||
| float pinchAmount = TryGetSelectionValue(out float selectionValue) ? Mathf.Pow(selectionValue, 2.0f) : 0; | ||
| HandRenderer.GetPropertyBlock(propertyBlock); | ||
MaxPalmer-UH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| propertyBlock.SetFloat(pinchAmountMaterialProperty, pinchAmount); | ||
| HandRenderer.SetPropertyBlock(propertyBlock); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Try to obtain the tracked devices selection value from the provided input reader. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// For backwards compatibility, this method will also attempt to get the selection amount from a | ||
| /// legacy XRI controller if the input reader is not set. | ||
| /// </remarks> | ||
| private bool TryGetSelectionValue(out float value) | ||
| { | ||
| if (selectInput != null && selectInput.TryReadValue(out value)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| bool success = false; | ||
| value = 0.0f; | ||
|
|
||
| #pragma warning disable CS0618 // XRBaseController is obsolete | ||
| if (controller == null) | ||
| { | ||
| controller = GetComponentInParent<XRBaseController>(); | ||
| } | ||
| if (controller != null) | ||
| { | ||
| value = controller.selectInteractionState.value; | ||
| success = true; | ||
| } | ||
| #pragma warning restore CS0618 // XRBaseController is obsolete | ||
|
|
||
| return success; | ||
| } | ||
| } | ||
| } | ||
11 changes: 11 additions & 0 deletions
11
org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer.meta
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.