diff --git a/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs b/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs
index 81bc871f5..75e222a44 100644
--- a/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs
+++ b/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs
@@ -24,8 +24,8 @@ public static void InstallPackages()
return;
}
- Debug.Log("Adding com.unity.xr.androidxr-openxr and com.google.xr.extensions...");
- request = Client.AddAndRemove(new[] { "com.unity.xr.androidxr-openxr", "https://github.com/android/android-xr-unity-package.git" });
+ Debug.Log("Adding the Unity OpenXR Android XR package...");
+ request = Client.AddAndRemove(new[] { "com.unity.xr.androidxr-openxr" });
EditorApplication.update += Progress;
}
@@ -33,7 +33,7 @@ private static void Progress()
{
if (request.IsCompleted)
{
- Debug.Log($"Package install request complete ({request.Status})");
+ Debug.Log($"Package install request complete ({request.Status}).");
EditorApplication.update -= Progress;
request = null;
}
diff --git a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json
index f282671d1..18fbf5893 100644
--- a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json
+++ b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json
@@ -264,12 +264,13 @@
"url": "https://packages.unity.com"
},
"com.unity.xr.hands": {
- "version": "1.3.0",
+ "version": "1.6.0",
"depth": 1,
"source": "registry",
"dependencies": {
"com.unity.modules.xr": "1.0.0",
"com.unity.inputsystem": "1.3.0",
+ "com.unity.mathematics": "1.2.6",
"com.unity.xr.core-utils": "2.2.0",
"com.unity.xr.management": "4.0.1"
},
@@ -389,7 +390,7 @@
"com.unity.inputsystem": "1.6.1",
"com.unity.xr.arfoundation": "5.0.5",
"com.unity.xr.core-utils": "2.1.0",
- "com.unity.xr.hands": "1.3.0",
+ "com.unity.xr.hands": "1.6.0",
"com.unity.xr.interaction.toolkit": "3.0.4",
"org.mixedrealitytoolkit.core": "4.0.0"
}
diff --git a/org.mixedrealitytoolkit.input/CHANGELOG.md b/org.mixedrealitytoolkit.input/CHANGELOG.md
index bb0000f72..de95f8c87 100644
--- a/org.mixedrealitytoolkit.input/CHANGELOG.md
+++ b/org.mixedrealitytoolkit.input/CHANGELOG.md
@@ -2,6 +2,12 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
+## Unreleased
+
+### Added
+
+* Added support for XR_MSFT_hand_tracking_mesh and XR_ANDROID_hand_mesh on compatible runtimes. [PR #993](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/993)
+
## [4.0.0-pre.2] - 2025-12-05
### Changed
diff --git a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs
index 72d0c9548..32ed74ae3 100644
--- a/org.mixedrealitytoolkit.input/Controllers/HandModel.cs
+++ b/org.mixedrealitytoolkit.input/Controllers/HandModel.cs
@@ -57,7 +57,7 @@ public Transform ModelPrefab
/// the hand model prefab when implementing .
///
public XRInputButtonReader SelectInput => selectInput;
-
+
#endregion Associated hand select values
///
@@ -73,9 +73,13 @@ protected virtual void Start()
Debug.Assert(selectInput != null, $"The Select Input reader for {name} is not set and will not be used with the instantiated hand model.");
// Set the select input reader for the model if it implements ISelectInputVisualizer
- if (selectInput != null && model != null && model.TryGetComponent(out ISelectInputVisualizer selectInputVisualizer))
+ if (selectInput != null && model != null)
{
- selectInputVisualizer.SelectInput = selectInput;
+ ISelectInputVisualizer[] selectInputVisualizers = model.GetComponentsInChildren();
+ foreach (ISelectInputVisualizer selectInputVisualizer in selectInputVisualizers)
+ {
+ selectInputVisualizer.SelectInput = selectInput;
+ }
}
}
}
diff --git a/org.mixedrealitytoolkit.input/Subsystems/Hands/UnityHandsSubsystem.cs b/org.mixedrealitytoolkit.input/Subsystems/Hands/UnityHandsSubsystem.cs
index ef893321a..ee0ffb182 100644
--- a/org.mixedrealitytoolkit.input/Subsystems/Hands/UnityHandsSubsystem.cs
+++ b/org.mixedrealitytoolkit.input/Subsystems/Hands/UnityHandsSubsystem.cs
@@ -263,8 +263,7 @@ private XRHand GetTrackedHand()
}
}
- XRHand hand = HandNode == XRNode.LeftHand ? xrHandSubsystem.leftHand : xrHandSubsystem.rightHand;
- return hand;
+ return HandNode == XRNode.LeftHand ? xrHandSubsystem.leftHand : xrHandSubsystem.rightHand;
}
}
diff --git a/org.mixedrealitytoolkit.input/Tracking/HandPoseDriver.cs b/org.mixedrealitytoolkit.input/Tracking/HandPoseDriver.cs
index 0872c35d6..b0c9dc106 100644
--- a/org.mixedrealitytoolkit.input/Tracking/HandPoseDriver.cs
+++ b/org.mixedrealitytoolkit.input/Tracking/HandPoseDriver.cs
@@ -33,6 +33,7 @@ public class HandPoseDriver : TrackedPoseDriver
private bool m_firstUpdate = true;
private InputAction m_boundTrackingAction = null;
private InputTrackingState m_trackingState = InputTrackingState.None;
+ private const InputTrackingState m_polyfillTrackingState = InputTrackingState.Position | InputTrackingState.Rotation;
///
/// Expose the tracking state for the hand pose driver, to allow to query it.
@@ -40,7 +41,7 @@ public class HandPoseDriver : TrackedPoseDriver
///
/// Avoid exposing this publicly as this is a workaround solution to support hand tracking on devices without interaction profiles.
///
- internal InputTrackingState CachedTrackingState => m_trackingState;
+ internal InputTrackingState CachedTrackingState => IsPolyfillDevicePose ? m_polyfillTrackingState : m_trackingState;
///
/// Get if the last pose set was from a polyfill device pose. That is, if the last pose originated from the .
@@ -48,6 +49,7 @@ public class HandPoseDriver : TrackedPoseDriver
internal bool IsPolyfillDevicePose { get; private set; }
#region Serialized Fields
+
[Header("Hand Pose Driver Settings")]
[SerializeField, Tooltip("The XRNode associated with this Hand Controller. Expected to be XRNode.LeftHand or XRNode.RightHand.")]
@@ -58,9 +60,11 @@ public class HandPoseDriver : TrackedPoseDriver
///
/// Expected to be XRNode.LeftHand or XRNode.RightHand.
public XRNode HandNode => handNode;
+
#endregion Serialized Fields
#region TrackedPoseDriver Overrides
+
///
protected override void PerformUpdate()
{
@@ -94,7 +98,6 @@ protected override void PerformUpdate()
if ((missingPositionController || missingRotationController || IsTrackingNone()) &&
TryGetPolyfillDevicePose(out Pose devicePose))
{
- m_trackingState = InputTrackingState.Position | InputTrackingState.Rotation;
IsPolyfillDevicePose = true;
ForceSetLocalTransform(devicePose.position, devicePose.rotation);
}
@@ -103,9 +106,11 @@ protected override void PerformUpdate()
IsPolyfillDevicePose = false;
}
}
+
#endregion TrackedPoseDriver Overrides
#region Private Functions
+
///
/// Check the tracking state here to account for a bound but untracked interaction profile.
/// This could show up on runtimes where a controller is disconnected, hand tracking spins up,
@@ -278,6 +283,7 @@ private void OnTrackingStateInputCanceled(InputAction.CallbackContext context)
m_trackingState = InputTrackingState.None;
}
}
+
#endregion Private Functions
}
}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs
new file mode 100644
index 000000000..cf1eae3c4
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs
@@ -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;
+
+ /// The XRNode on which this hand is located.
+ 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;
+
+ ///
+ /// 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 .
+ /// 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.
+ ///
+ 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
+
+ ///
+ /// Input reader used when pinch selecting an interactable.
+ ///
+ 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 displaySubsystems = new List();
+
+ // 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;
+
+ ///
+ /// 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 and .
+ ///
+ ///
+ ///
+ private readonly List buttonReaders = new List();
+
+ ///
+ /// Whether this visualizer currently has a loaded and visible hand mesh or not.
+ ///
+ protected internal bool IsRendering => HandRenderer != null && HandRenderer.enabled;
+
+ ///
+ /// The renderer for this visualizer, to use to visualize the pinch amount.
+ ///
+ protected abstract Renderer HandRenderer { get; }
+
+ ///
+ /// A Unity event function that is called when an enabled script instance is being loaded.
+ ///
+ protected virtual void Awake()
+ {
+ propertyBlock = new MaterialPropertyBlock();
+ buttonReaders.Add(selectInput);
+ }
+
+ ///
+ /// A Unity event function that is called when the script component has been enabled.
+ ///
+ 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})!");
+ }
+
+ ///
+ /// A Unity event function that is called when the script component has been disabled.
+ ///
+ protected virtual void OnDisable()
+ {
+ buttonReaders.ForEach(reader => reader?.DisableDirectActionIfModeUsed());
+
+ // Disable the rigged hand renderer when this component is disabled
+ HandRenderer.enabled = false;
+ }
+
+ ///
+ /// Helper method for setting an input property.
+ ///
+ /// The to the field.
+ /// The new value being set.
+ ///
+ /// 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.
+ ///
+ ///
+ 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);
+ propertyBlock.SetFloat(pinchAmountMaterialProperty, pinchAmount);
+ HandRenderer.SetPropertyBlock(propertyBlock);
+ }
+
+ ///
+ /// Try to obtain the tracked devices selection value from the provided input reader.
+ ///
+ ///
+ /// 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.
+ ///
+ 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();
+ }
+ if (controller != null)
+ {
+ value = controller.selectInteractionState.value;
+ success = true;
+ }
+#pragma warning restore CS0618 // XRBaseController is obsolete
+
+ return success;
+ }
+ }
+}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs.meta b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs.meta
new file mode 100644
index 000000000..5efd406a4
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3a383c22545b8774b8e5891adaccecf4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer.meta
new file mode 100644
index 000000000..cfb1734fe
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 503070eeec7bc98479b1459387014145
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs
new file mode 100644
index 000000000..14d6ed1da
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs
@@ -0,0 +1,265 @@
+// Copyright (c) Mixed Reality Toolkit Contributors
+// Licensed under the BSD 3-Clause
+
+using System.Collections.Generic;
+using Unity.XR.CoreUtils;
+using UnityEngine;
+using UnityEngine.SubsystemsImplementation.Extensions;
+using UnityEngine.XR;
+using UnityEngine.XR.Hands;
+using UnityEngine.XR.Hands.Meshing;
+using UnityEngine.XR.Hands.OpenXR;
+
+#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID)
+using Microsoft.MixedReality.OpenXR;
+#endif
+
+namespace MixedReality.Toolkit.Input
+{
+ [AddComponentMenu("MRTK/Input/Visualizers/Platform Hand Mesh Visualizer")]
+ public class PlatformHandMeshVisualizer : HandMeshVisualizer
+ {
+ [SerializeField]
+ private MeshFilter meshFilter;
+
+ [SerializeField]
+ private MeshRenderer handRenderer;
+
+ [SerializeField, Range(0, 1)]
+ private float fadeDistance = 0.025f;
+
+ ///
+ protected override Renderer HandRenderer => handRenderer;
+
+#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID)
+ private HandMeshTracker handMeshTracker;
+ private Mesh neutralPoseMesh;
+ private bool initializedUVs = false;
+#endif
+
+ // Share these among all instances to only query once per frame
+ private static int lastUpdatedFrame = -1;
+ private static XRHandSubsystem handSubsystem = null;
+ private static XRHandMeshDataQueryResult result;
+ private XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags;
+
+ // The property block used to modify the wrist position property on the material
+ private MaterialPropertyBlock propertyBlock = null;
+
+ ///
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ handRenderer.enabled = false;
+
+ // Use propertyBlock as an indicator that this is our first time through OnEnable
+ if (propertyBlock == null)
+ {
+ propertyBlock = new MaterialPropertyBlock();
+
+ updateSuccessFlags = HandNode == XRNode.LeftHand ?
+ XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints | XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose :
+ XRHandSubsystem.UpdateSuccessFlags.RightHandJoints | XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose;
+
+ // Since the hand mesh is likely to change every frame, we
+ // "optimize mesh for frequent updates" by marking it dynamic
+ meshFilter.mesh.MarkDynamic();
+ }
+
+ // If we already found our subsystem, just return
+ if (handSubsystem != null && handSubsystem.running) { return; }
+
+ List subsystems = XRSubsystemHelpers.GetAllSubsystems();
+ foreach (XRHandSubsystem subsystem in subsystems)
+ {
+ if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null)
+ {
+ Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization.");
+ handSubsystem = subsystem;
+ return;
+ }
+ }
+
+#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID)
+ if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh"))
+ {
+ Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization.");
+ handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right;
+
+ if (neutralPoseMesh == null)
+ {
+ neutralPoseMesh = new Mesh();
+ }
+
+ return;
+ }
+#endif
+
+ Debug.Log($"No active hand mesh extension was found for {HandNode} visualization.");
+ enabled = false;
+ }
+
+ protected void Update()
+ {
+ if (!ShouldRenderHand())
+ {
+ // Hide the hand and abort if we shouldn't be
+ // showing the hand, for whatever reason.
+ // (Missing joint data, no subsystem, additive
+ // display, etc!)
+ handRenderer.enabled = false;
+ return;
+ }
+
+ if (handSubsystem != null && handSubsystem.running && (handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0)
+ {
+ XRHandMeshDataQueryParams queryParams = new()
+ {
+ allocator = Unity.Collections.Allocator.Temp,
+ };
+
+ // Sometimes, the mesh retrieval will fail, but that doesn't mean we aren't being tracked
+ // So, reuse the last mesh until the subsystem tells us we're untracked
+ if (lastUpdatedFrame != Time.frameCount && !handSubsystem.TryGetMeshData(out result, ref queryParams))
+ {
+ return;
+ }
+
+ lastUpdatedFrame = Time.frameCount;
+ XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand;
+ handRenderer.enabled = true;
+ Mesh mesh = meshFilter.mesh;
+
+ if (handMeshData.positions.Length > 0 && handMeshData.indices.Length > 0)
+ {
+ mesh.SetVertices(handMeshData.positions);
+ Unity.Collections.NativeArray indices = handMeshData.indices;
+ // This API appears to return CCW triangles, but Unity expects CW triangles
+ for (int i = 0; i < indices.Length; i += 3)
+ {
+ (indices[i + 1], indices[i + 2]) = (indices[i + 2], indices[i + 1]);
+ }
+ mesh.SetIndices(indices, MeshTopology.Triangles, 0);
+ mesh.RecalculateBounds();
+ }
+
+ if (handMeshData.uvs.IsCreated && handMeshData.uvs.Length == mesh.vertexCount)
+ {
+ mesh.SetUVs(0, handMeshData.uvs);
+ }
+ else
+ {
+ mesh.uv = null;
+ }
+
+ if (handMeshData.normals.IsCreated && handMeshData.normals.Length == mesh.vertexCount)
+ {
+ mesh.SetNormals(handMeshData.normals);
+ }
+ else
+ {
+ mesh.RecalculateNormals();
+ }
+
+ if (handMeshData.TryGetRootPose(out Pose rootPose))
+ {
+ transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose));
+ }
+ UpdateHandMaterial();
+ return;
+ }
+
+#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID)
+ if (handMeshTracker != null
+ && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh)
+ && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose))
+ {
+ // On some runtimes, the mesh is moved in its local space instead of world space,
+ // while its world space location is unchanged. In this case, we want to ensure the
+ // bounds follow the hand around by manually recalculating them.
+ meshFilter.mesh.RecalculateBounds();
+
+ handRenderer.enabled = true;
+
+ if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm))
+ {
+ meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices);
+ initializedUVs = true;
+ }
+
+ transform.SetWorldPose(PlayspaceUtilities.TransformPose(pose));
+ UpdateHandMaterial();
+ return;
+ }
+#endif
+
+ // Hide the hand if we weren't able to obtain a valid mesh
+ handRenderer.enabled = false;
+ }
+
+ protected override bool ShouldRenderHand()
+ {
+ // If we're missing anything, don't render the hand.
+ return meshFilter != null && handRenderer != null && base.ShouldRenderHand();
+ }
+
+ protected override void UpdateHandMaterial()
+ {
+ base.UpdateHandMaterial();
+
+ if ((XRSubsystemHelpers.HandsAggregator?.TryGetJoint(TrackedHandJoint.Wrist, HandNode, out HandJointPose wristPose) ?? false)
+ && XRSubsystemHelpers.HandsAggregator.TryGetJoint(TrackedHandJoint.LittleMetacarpal, HandNode, out HandJointPose littleMetaPose)
+ && XRSubsystemHelpers.HandsAggregator.TryGetJoint(TrackedHandJoint.ThumbMetacarpal, HandNode, out HandJointPose thumbMetaPose))
+ {
+ HandRenderer.GetPropertyBlock(propertyBlock);
+ float radius = Vector3.Distance(littleMetaPose.Position, thumbMetaPose.Position);
+ propertyBlock.SetVector("_FadeSphereCenter", wristPose.Position - (radius * 0.5f * wristPose.Forward));
+ propertyBlock.SetFloat("_FadeSphereRadius", radius);
+ propertyBlock.SetFloat("_FadeDistance", fadeDistance);
+ HandRenderer.SetPropertyBlock(propertyBlock);
+ }
+ }
+
+#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID)
+ private static Vector2[] InitializeUVs(Vector3[] neutralPoseVertices)
+ {
+ if (neutralPoseVertices?.Length == 0)
+ {
+ Debug.LogError("Loaded 0 vertices for neutralPoseVertices");
+ return System.Array.Empty();
+ }
+
+ float minY = neutralPoseVertices[0].y;
+ float maxY = minY;
+
+ for (int ix = 1; ix < neutralPoseVertices.Length; ix++)
+ {
+ Vector3 p = neutralPoseVertices[ix];
+
+ if (p.y < minY)
+ {
+ minY = p.y;
+ }
+ else if (p.y > maxY)
+ {
+ maxY = p.y;
+ }
+ }
+
+ float scale = 1.0f / (maxY - minY);
+
+ Vector2[] uvs = new Vector2[neutralPoseVertices.Length];
+
+ for (int ix = 0; ix < neutralPoseVertices.Length; ix++)
+ {
+ Vector3 p = neutralPoseVertices[ix];
+
+ uvs[ix] = new Vector2(p.x * scale + 0.5f, (p.y - minY) * scale);
+ }
+
+ return uvs;
+ }
+#endif
+ }
+}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs.meta
new file mode 100644
index 000000000..5f1d749f5
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f42ee07c35d33c44989eee5953f625d2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs.meta b/org.mixedrealitytoolkit.input/Visualizers/Prefabs.meta
new file mode 100644
index 000000000..8fe79ca76
--- /dev/null
+++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: db472eec1a7c5c34a874ee394c08966f
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab
similarity index 91%
rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab
rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab
index 37c3a06e3..2219e6279 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab
+++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab
@@ -5,6 +5,7 @@ PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
+ serializedVersion: 3
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: -7366846053233975685, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
@@ -47,6 +48,10 @@ PrefabInstance:
propertyPath: m_controllerDetectedAction.m_Name
value: Controller Detected
objectReference: {fileID: 0}
+ - target: {fileID: -7366846053233975685, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
+ propertyPath: controllerDetectedAction.m_Reference
+ value:
+ objectReference: {fileID: -7613329581162844239, guid: 18c412191cdc9274897f101c7fd5316f, type: 3}
- target: {fileID: -7366846053233975685, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
propertyPath: controllerDetectedAction.m_Action.m_Id
value: 4204bd93-73df-4026-97f6-48cc7932da0b
@@ -99,6 +104,18 @@ PrefabInstance:
propertyPath: controllerDetectedAction.m_Action.m_SingletonActionBindings.Array.data[0].m_Action
value: Controller Detected
objectReference: {fileID: 0}
+ - target: {fileID: 1493009138236857671, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
+ propertyPath: m_LocalScale.x
+ value: -1
+ objectReference: {fileID: 0}
+ - target: {fileID: 1800205545854781715, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
+ propertyPath: handNode
+ value: 4
+ objectReference: {fileID: 0}
+ - target: {fileID: 2263138549649042646, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
+ propertyPath: handNode
+ value: 4
+ objectReference: {fileID: 0}
- target: {fileID: 3507431783398283103, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
propertyPath: handNode
value: 4
@@ -111,10 +128,6 @@ PrefabInstance:
propertyPath: m_RootOrder
value: 0
objectReference: {fileID: 0}
- - target: {fileID: 3973969148631863464, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
- propertyPath: m_LocalScale.x
- value: -1
- objectReference: {fileID: 0}
- target: {fileID: 3973969148631863464, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
propertyPath: m_LocalPosition.x
value: 0
@@ -160,4 +173,7 @@ PrefabInstance:
value: openxr_left_hand
objectReference: {fileID: 0}
m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: da93d751ddc0f64468dfc02f18d02d00, type: 3}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab.meta
similarity index 100%
rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab.meta
rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab.meta
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab
similarity index 83%
rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab
rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab
index b5dbb75e4..f9a9b68b3 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab
+++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab
@@ -23,6 +23,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 16087387050962746}
+ serializedVersion: 2
m_LocalRotation: {x: -0.04134135, y: 0.020228544, z: 0.07561951, w: 0.996074}
m_LocalPosition: {x: 3.0267983e-11, y: 0, z: 0.0004089724}
m_LocalScale: {x: 1, y: 0.99999994, z: 0.9999998}
@@ -30,7 +31,6 @@ Transform:
m_Children:
- {fileID: 7427278357790150308}
m_Father: {fileID: 5458823143432554036}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &264115022450059308
GameObject:
@@ -55,6 +55,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 264115022450059308}
+ serializedVersion: 2
m_LocalRotation: {x: -0.09019473, y: -0.112213634, z: -0.082327574, w: 0.98615175}
m_LocalPosition: {x: 1.6298145e-11, y: 1.3969838e-11, z: 0.00039010096}
m_LocalScale: {x: 0.99999994, y: 1, z: 1}
@@ -62,7 +63,6 @@ Transform:
m_Children:
- {fileID: 9006935288519073739}
m_Father: {fileID: 498931685021915093}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &488343164757858754
GameObject:
@@ -87,6 +87,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 488343164757858754}
+ serializedVersion: 2
m_LocalRotation: {x: 0.018171376, y: -0.07332422, z: 0.0016571282, w: 0.99714124}
m_LocalPosition: {x: -1.2751115e-11, y: 4.4383342e-12, z: 0.00068246824}
m_LocalScale: {x: 0.99999994, y: 0.9999999, z: 0.99999994}
@@ -94,7 +95,6 @@ Transform:
m_Children:
- {fileID: 6541163434380589232}
m_Father: {fileID: 1077227819275269935}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &550855178322120980
GameObject:
@@ -119,6 +119,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 550855178322120980}
+ serializedVersion: 2
m_LocalRotation: {x: -0.051032774, y: 0.020922784, z: 0.0021605818, w: 0.9984755}
m_LocalPosition: {x: -1.8189894e-12, y: -1.4888427e-11, z: 0.00045456158}
m_LocalScale: {x: 1, y: 1, z: 0.9999999}
@@ -126,7 +127,6 @@ Transform:
m_Children:
- {fileID: 1828752077698417225}
m_Father: {fileID: 8089865949473389596}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &723288405160749330
GameObject:
@@ -151,6 +151,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 723288405160749330}
+ serializedVersion: 2
m_LocalRotation: {x: -0.018254701, y: 0.00089057756, z: 0.0014708434, w: 0.9998319}
m_LocalPosition: {x: -5.784386e-12, y: -1.1641532e-12, z: 0.00027976793}
m_LocalScale: {x: 0.99999994, y: 0.9999999, z: 0.9999998}
@@ -158,7 +159,6 @@ Transform:
m_Children:
- {fileID: 5817228883765955420}
m_Father: {fileID: 8748218160129236693}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &762971720275621074
GameObject:
@@ -183,6 +183,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 762971720275621074}
+ serializedVersion: 2
m_LocalRotation: {x: 0.009341898, y: -0.25212786, z: 0.0016238193, w: 0.9676475}
m_LocalPosition: {x: -0, y: 0, z: 0}
m_LocalScale: {x: 0.99999994, y: 1, z: 0.99999964}
@@ -190,7 +191,6 @@ Transform:
m_Children:
- {fileID: 6793337788569349462}
m_Father: {fileID: 5446693289115484214}
- m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1143263375470404621
GameObject:
@@ -215,13 +215,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1143263375470404621}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0, y: 0, z: 0.00013116258}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3640015180784389109}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1449011787537462570
GameObject:
@@ -246,6 +246,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1449011787537462570}
+ serializedVersion: 2
m_LocalRotation: {x: -0.008052746, y: 0.048775665, z: -0.006817606, w: 0.998754}
m_LocalPosition: {x: -2.9685907e-11, y: 6.6938807e-12, z: 0.0007289571}
m_LocalScale: {x: 1, y: 0.9999999, z: 0.99999994}
@@ -253,7 +254,6 @@ Transform:
m_Children:
- {fileID: 5921367078976266107}
m_Father: {fileID: 6793337788569349462}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2928499560571120432
GameObject:
@@ -278,6 +278,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2928499560571120432}
+ serializedVersion: 2
m_LocalRotation: {x: -0.02028584, y: 0.3819591, z: 0.004533123, w: 0.9239455}
m_LocalPosition: {x: -0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1.0000001, z: 1.0000001}
@@ -285,7 +286,6 @@ Transform:
m_Children:
- {fileID: 5334553598884166511}
m_Father: {fileID: 5446693289115484214}
- m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3184605578150545217
GameObject:
@@ -310,6 +310,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3184605578150545217}
+ serializedVersion: 2
m_LocalRotation: {x: -0.016732441, y: -0.41903344, z: 0.6049222, w: 0.67690486}
m_LocalPosition: {x: -0, y: 0, z: 0}
m_LocalScale: {x: 0.9999999, y: 0.99999976, z: 0.99999964}
@@ -317,7 +318,6 @@ Transform:
m_Children:
- {fileID: 5458823143432554036}
m_Father: {fileID: 5446693289115484214}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3331529553617436422
GameObject:
@@ -342,6 +342,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3331529553617436422}
+ serializedVersion: 2
m_LocalRotation: {x: 0.049683567, y: -0.0682403, z: 0.005375236, w: 0.99641657}
m_LocalPosition: {x: -2.6921044e-12, y: -1.0913936e-13, z: 0.00026415734}
m_LocalScale: {x: 1, y: 1, z: 0.99999994}
@@ -349,7 +350,6 @@ Transform:
m_Children:
- {fileID: 6402884463806494666}
m_Father: {fileID: 6356967279678601896}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3914579612821526105
GameObject:
@@ -374,6 +374,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3914579612821526105}
+ serializedVersion: 2
m_LocalRotation: {x: -0.03612786, y: 0.013062499, z: -0.0013056236, w: 0.99926096}
m_LocalPosition: {x: 2.52974e-11, y: 3.3469403e-12, z: 0.00018895004}
m_LocalScale: {x: 1.0000001, y: 0.9999999, z: 0.99999994}
@@ -381,7 +382,6 @@ Transform:
m_Children:
- {fileID: 3661087396984794893}
m_Father: {fileID: 4528600584950922117}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3957823326167938785
GameObject:
@@ -407,13 +407,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3957823326167938785}
- m_LocalRotation: {x: 0.000000021855694, y: 0, z: -0, w: 1}
- m_LocalPosition: {x: -0, y: 0, z: 0}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0.000000021855694, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 100, y: 100, z: 100}
m_ConstrainProportionsScale: 0
m_Children: []
- m_Father: {fileID: 3973969148631863464}
- m_RootOrder: 0
+ m_Father: {fileID: 1493009138236857671}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!137 &5709336421934327412
SkinnedMeshRenderer:
@@ -517,13 +517,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4042160540912683627}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0, y: 0, z: 0.00015126375}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5817228883765955420}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &4122089786851792721
GameObject:
@@ -548,6 +548,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4122089786851792721}
+ serializedVersion: 2
m_LocalRotation: {x: -0.04099412, y: -0.012571383, z: 0.00082643394, w: 0.99908}
m_LocalPosition: {x: -7.858469e-12, y: -4.574758e-12, z: 0.0002651471}
m_LocalScale: {x: 0.99999994, y: 0.9999999, z: 1}
@@ -555,7 +556,6 @@ Transform:
m_Children:
- {fileID: 9017335721188770727}
m_Father: {fileID: 6541163434380589232}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &4354293980546992658
GameObject:
@@ -566,7 +566,6 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 3973969148631863464}
- - component: {fileID: 3507431783398283103}
- component: {fileID: -7366846053233975685}
m_Layer: 0
m_Name: openxr_right_hand
@@ -582,33 +581,16 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4354293980546992658}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- - {fileID: 5499070473167639758}
- - {fileID: 5446693289115484214}
+ - {fileID: 7460324237988116690}
+ - {fileID: 1493009138236857671}
m_Father: {fileID: 0}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &3507431783398283103
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 4354293980546992658}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 4a4cc0663b372fd4795c4e6d942500fb, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- handNode: 5
- showHandsOnTransparentDisplays: 0
- wrist: {fileID: 5446693289115484214}
- handRenderer: {fileID: 5709336421934327412}
- pinchAmountMaterialProperty: _PinchAmount
--- !u!114 &-7366846053233975685
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -624,7 +606,7 @@ MonoBehaviour:
handNode: 5
fallbackControllerModel: {fileID: 3339478339056654981, guid: 3bd614f14cda63940b56faf0518b55f3, type: 3}
controllerDetectedAction:
- m_UseReference: 0
+ m_UseReference: 1
m_Action:
m_Name: Controller Detected
m_Type: 0
@@ -642,7 +624,7 @@ MonoBehaviour:
m_Action: Controller Detected
m_Flags: 0
m_Flags: 0
- m_Reference: {fileID: 0}
+ m_Reference: {fileID: 3239510804178183174, guid: 18c412191cdc9274897f101c7fd5316f, type: 3}
--- !u!1 &4657295888579565740
GameObject:
m_ObjectHideFlags: 0
@@ -666,6 +648,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4657295888579565740}
+ serializedVersion: 2
m_LocalRotation: {x: -0.012391907, y: -0.07663214, z: 0.01388792, w: 0.9968857}
m_LocalPosition: {x: 1.4151737e-11, y: -1.16415315e-11, z: 0.0006916037}
m_LocalScale: {x: 1, y: 1, z: 1}
@@ -673,7 +656,6 @@ Transform:
m_Children:
- {fileID: 4528600584950922117}
m_Father: {fileID: 5334553598884166511}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &5237677254925488276
GameObject:
@@ -698,6 +680,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5237677254925488276}
+ serializedVersion: 2
m_LocalRotation: {x: -0.02189448, y: 0.011967821, z: 0.000007822243, w: 0.9996887}
m_LocalPosition: {x: 4.5656634e-12, y: -2.2937456e-11, z: 0.00041979662}
m_LocalScale: {x: 0.99999994, y: 1, z: 1}
@@ -705,8 +688,141 @@ Transform:
m_Children:
- {fileID: 6273793641032179826}
m_Father: {fileID: 2464973069511612448}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5460435942034699144
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7460324237988116690}
+ - component: {fileID: 3936565761691908697}
+ - component: {fileID: 769216864695519206}
+ - component: {fileID: 2263138549649042646}
+ m_Layer: 0
+ m_Name: PlatformHandMesh
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &7460324237988116690
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5460435942034699144}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 3973969148631863464}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &3936565761691908697
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5460435942034699144}
+ m_Mesh: {fileID: 0}
+--- !u!23 &769216864695519206
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5460435942034699144}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: 4c63e230f530e6c4e8be8b25846c0989, type: 2}
+ - {fileID: 2100000, guid: 5138877b31bc96046b8d186cd276d724, type: 2}
+ - {fileID: 2100000, guid: d2ac3579866c33749b7ac30d8393d4e2, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!114 &2263138549649042646
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5460435942034699144}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: f42ee07c35d33c44989eee5953f625d2, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ handNode: 5
+ showHandsOnTransparentDisplays: 0
+ pinchAmountMaterialProperty: _PinchAmount
+ selectInput:
+ m_InputSourceMode: 2
+ m_InputActionPerformed:
+ m_Name: Select
+ m_Type: 1
+ m_ExpectedControlType:
+ m_Id: 5a174afe-679f-4e85-a45a-988046773313
+ m_Processors:
+ m_Interactions:
+ m_SingletonActionBindings: []
+ m_Flags: 0
+ m_InputActionValue:
+ m_Name: Select Value
+ m_Type: 0
+ m_ExpectedControlType: Axis
+ m_Id: 8594ff63-7803-4c60-a5c8-62dc67ba5c1a
+ m_Processors:
+ m_Interactions:
+ m_SingletonActionBindings: []
+ m_Flags: 0
+ m_InputActionReferencePerformed: {fileID: 0}
+ m_InputActionReferenceValue: {fileID: 0}
+ m_ObjectReferenceObject: {fileID: 0}
+ m_ManualPerformed: 0
+ m_ManualValue: 0
+ m_ManualQueuePerformed: 0
+ m_ManualQueueWasPerformedThisFrame: 0
+ m_ManualQueueWasCompletedThisFrame: 0
+ m_ManualQueueValue: 0
+ m_ManualQueueTargetFrame: 0
+ meshFilter: {fileID: 3936565761691908697}
+ handRenderer: {fileID: 769216864695519206}
+ fadeDistance: 0.025
--- !u!1 &5839269735351241340
GameObject:
m_ObjectHideFlags: 0
@@ -730,8 +846,9 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5839269735351241340}
- m_LocalRotation: {x: 0.000000021855694, y: 0, z: -0, w: 1}
- m_LocalPosition: {x: -0, y: 0, z: 0}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0.000000021855694, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 100, y: 100, z: 100}
m_ConstrainProportionsScale: 0
m_Children:
@@ -740,8 +857,7 @@ Transform:
- {fileID: 3103993496388122979}
- {fileID: 6356967279678601896}
- {fileID: 4523630147034299841}
- m_Father: {fileID: 3973969148631863464}
- m_RootOrder: 1
+ m_Father: {fileID: 1493009138236857671}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6303298619132591738
GameObject:
@@ -766,6 +882,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6303298619132591738}
+ serializedVersion: 2
m_LocalRotation: {x: 0.041502926, y: 0.1045213, z: -0.014521739, w: 0.9935502}
m_LocalPosition: {x: -1.1641532e-12, y: 9.3132255e-12, z: 0.00037181645}
m_LocalScale: {x: 1, y: 0.99999994, z: 0.9999998}
@@ -773,8 +890,89 @@ Transform:
m_Children:
- {fileID: 498931685021915093}
m_Father: {fileID: 1529682561352593866}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &6309398931433067091
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1493009138236857671}
+ - component: {fileID: 1800205545854781715}
+ m_Layer: 0
+ m_Name: RiggedHandMesh
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1493009138236857671
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6309398931433067091}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 5499070473167639758}
+ - {fileID: 5446693289115484214}
+ m_Father: {fileID: 3973969148631863464}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1800205545854781715
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6309398931433067091}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4a4cc0663b372fd4795c4e6d942500fb, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ handNode: 5
+ showHandsOnTransparentDisplays: 0
+ pinchAmountMaterialProperty: _PinchAmount
+ selectInput:
+ m_InputSourceMode: 2
+ m_InputActionPerformed:
+ m_Name: Select
+ m_Type: 1
+ m_ExpectedControlType:
+ m_Id: 455696a2-5c34-4e97-838a-bc2b6ac85675
+ m_Processors:
+ m_Interactions:
+ m_SingletonActionBindings: []
+ m_Flags: 0
+ m_InputActionValue:
+ m_Name: Select Value
+ m_Type: 0
+ m_ExpectedControlType: Axis
+ m_Id: de553aeb-f229-4e9f-af88-39757e695f7d
+ m_Processors:
+ m_Interactions:
+ m_SingletonActionBindings: []
+ m_Flags: 0
+ m_InputActionReferencePerformed: {fileID: 0}
+ m_InputActionReferenceValue: {fileID: 0}
+ m_ObjectReferenceObject: {fileID: 0}
+ m_ManualPerformed: 0
+ m_ManualValue: 0
+ m_ManualQueuePerformed: 0
+ m_ManualQueueWasPerformedThisFrame: 0
+ m_ManualQueueWasCompletedThisFrame: 0
+ m_ManualQueueValue: 0
+ m_ManualQueueTargetFrame: 0
+ wrist: {fileID: 5446693289115484214}
+ handRenderer: {fileID: 5709336421934327412}
+ primaryMeshVisualizer: {fileID: 2263138549649042646}
--- !u!1 &6561479249547925302
GameObject:
m_ObjectHideFlags: 0
@@ -798,13 +996,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6561479249547925302}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0, y: 0, z: 0.00015497528}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6273793641032179826}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6594130182080127020
GameObject:
@@ -829,6 +1027,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6594130182080127020}
+ serializedVersion: 2
m_LocalRotation: {x: 0.0032687124, y: 0.0924356, z: -0.00006233817, w: 0.99571335}
m_LocalPosition: {x: -2.0396662e-12, y: -4.206413e-14, z: 0.0002682099}
m_LocalScale: {x: 1, y: 1, z: 1}
@@ -836,7 +1035,6 @@ Transform:
m_Children:
- {fileID: 8089865949473389596}
m_Father: {fileID: 3103993496388122979}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6832599985986192355
GameObject:
@@ -861,13 +1059,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6832599985986192355}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0, y: 0, z: 0.00017717812}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1828752077698417225}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &6963568092490684223
GameObject:
@@ -892,6 +1090,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6963568092490684223}
+ serializedVersion: 2
m_LocalRotation: {x: 0.00078201893, y: -0.08185144, z: 0.000027665115, w: 0.99664426}
m_LocalPosition: {x: -0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 0.9999999}
@@ -899,7 +1098,6 @@ Transform:
m_Children:
- {fileID: 3556776435536246939}
m_Father: {fileID: 5446693289115484214}
- m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7416932990957639745
GameObject:
@@ -924,6 +1122,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7416932990957639745}
+ serializedVersion: 2
m_LocalRotation: {x: 0.06741448, y: -0.024361538, z: -0.0017286513, w: 0.9974261}
m_LocalPosition: {x: -1.0143708e-12, y: 2.046363e-14, z: 0.00072257634}
m_LocalScale: {x: 0.99999994, y: 1, z: 0.99999994}
@@ -931,7 +1130,6 @@ Transform:
m_Children:
- {fileID: 7322559479628339466}
m_Father: {fileID: 3556776435536246939}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7446434647064107278
GameObject:
@@ -956,6 +1154,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7446434647064107278}
+ serializedVersion: 2
m_LocalRotation: {x: 0.004451058, y: -0.005708242, z: -0.000026027026, w: 0.99997383}
m_LocalPosition: {x: -6.175469e-12, y: 2.6648193e-12, z: 0.00027607955}
m_LocalScale: {x: 1, y: 1, z: 1}
@@ -963,7 +1162,6 @@ Transform:
m_Children:
- {fileID: 2700898927078924524}
m_Father: {fileID: 7322559479628339466}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7522984908696370401
GameObject:
@@ -988,6 +1186,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7522984908696370401}
+ serializedVersion: 2
m_LocalRotation: {x: -0.04126447, y: 0.042427324, z: 0.00036548893, w: 0.99824697}
m_LocalPosition: {x: 1.2982326e-11, y: 9.363248e-12, z: 0.00024728195}
m_LocalScale: {x: 1, y: 0.9999999, z: 0.9999999}
@@ -995,7 +1194,6 @@ Transform:
m_Children:
- {fileID: 8610246813914124521}
m_Father: {fileID: 5921367078976266107}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7630331428036862893
GameObject:
@@ -1020,13 +1218,13 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7630331428036862893}
+ serializedVersion: 2
m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: -0, y: 0, z: 0.0002066094}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7427278357790150308}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7764930180859818638
GameObject:
@@ -1051,6 +1249,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7764930180859818638}
+ serializedVersion: 2
m_LocalRotation: {x: 0.115323275, y: -0.2855898, z: 0.024722293, w: 0.9510667}
m_LocalPosition: {x: 1.117769e-11, y: 0, z: 0.00028927124}
m_LocalScale: {x: 0.99999994, y: 0.99999994, z: 0.99999994}
@@ -1058,7 +1257,6 @@ Transform:
m_Children:
- {fileID: 8748218160129236693}
m_Father: {fileID: 4523630147034299841}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7922940236267228883
GameObject:
@@ -1083,6 +1281,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7922940236267228883}
+ serializedVersion: 2
m_LocalRotation: {x: 0.036102794, y: 0.18826051, z: -0.0076552806, w: 0.9814255}
m_LocalPosition: {x: 0.000009, y: -0.000002, z: 0.000321}
m_LocalScale: {x: 1, y: 0.99999994, z: 1}
@@ -1090,7 +1289,6 @@ Transform:
m_Children:
- {fileID: 2464973069511612448}
m_Father: {fileID: 872140809330182763}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &7960153440799608892
GameObject:
@@ -1115,6 +1313,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7960153440799608892}
+ serializedVersion: 2
m_LocalRotation: {x: -0.009721841, y: 0.13904916, z: 0.0008851419, w: 0.99023736}
m_LocalPosition: {x: -0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 0.99999994, z: 0.9999997}
@@ -1122,7 +1321,6 @@ Transform:
m_Children:
- {fileID: 1077227819275269935}
m_Father: {fileID: 5446693289115484214}
- m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &8261617224528375871
GameObject:
@@ -1147,6 +1345,7 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8261617224528375871}
+ serializedVersion: 2
m_LocalRotation: {x: -0.0051415353, y: 0.005024616, z: 0.0022183317, w: 0.9999717}
m_LocalPosition: {x: -2.0008884e-12, y: -7.639755e-12, z: 0.0004286098}
m_LocalScale: {x: 1, y: 1, z: 1}
@@ -1154,5 +1353,4 @@ Transform:
m_Children:
- {fileID: 3640015180784389109}
m_Father: {fileID: 6402884463806494666}
- m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab.meta
similarity index 100%
rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab.meta
rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab.meta
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat
index 83dab5e11..b61238583 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat
@@ -9,6 +9,8 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: RiggedHand-InvertedShell
m_Shader: {fileID: 4800000, guid: 1e78bf4fa2e9ae0459a4b1836f7a74b5, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
@@ -17,6 +19,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
+ m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -30,6 +33,8 @@ Material:
- _Displacement_: 0
- _Enable_: 1
- _Exponent_: 2.16
+ - _FadeDistance: 0.025
+ - _FadeSphereRadius: 0.05
- _Fade_In_End_: 0
- _Fade_In_Start_: 0
- _HandThickness: 0
@@ -39,7 +44,7 @@ Material:
- _Metallic: 0
- _OutlineExponent: 1
- _OutlineSmoothing: 0.05
- - _OutlineThickness: 0.00001337
+ - _OutlineThickness: 0.0012
- _OutlineThreshold: 0.474
- _PinchAmount: 0
- _Radius_: 0.09
@@ -47,6 +52,7 @@ Material:
m_Colors:
- _Center_: {r: 0.5, g: 0.5, b: 0, a: 0}
- _Color_: {r: 1, g: 1, b: 1, a: 1}
+ - _FadeSphereCenter: {r: 0, g: 0, b: 0, a: 1}
- _HandColor: {r: 0.14150941, g: 0.14150941, b: 0.14150941, a: 0.7137255}
- _OutlineColor: {r: 1, g: 1, b: 1, a: 0.03529412}
- _OutlineColorPinching: {r: 1, g: 1, b: 1, a: 1}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat
index 60925901f..933443742 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat
@@ -9,6 +9,8 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: RiggedHand-Main
m_Shader: {fileID: 4800000, guid: 618b0da8e4e6bb2459a1cc27acf0dd57, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
@@ -17,6 +19,7 @@ Material:
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
+ m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
@@ -30,6 +33,8 @@ Material:
- _Displacement_: 0
- _Enable_: 1
- _Exponent_: 2.16
+ - _FadeDistance: 0.025
+ - _FadeSphereRadius: 0.05
- _Fade_In_End_: 0
- _Fade_In_Start_: 0
- _HandThickness: 0
@@ -47,6 +52,7 @@ Material:
m_Colors:
- _Center_: {r: 0.5, g: 0.5, b: 0, a: 0}
- _Color_: {r: 1, g: 1, b: 1, a: 1}
+ - _FadeSphereCenter: {r: 0, g: 0, b: 0, a: 1}
- _HandColor: {r: 0.01886791, g: 0.01886791, b: 0.01886791, a: 0.8}
- _OutlineColor: {r: 1, g: 1, b: 1, a: 0.2901961}
- _OutlineColorPinching: {r: 1, g: 1, b: 1, a: 1}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs
index 6fb8f0fe8..4b039cb50 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs
@@ -2,13 +2,10 @@
// Licensed under the BSD 3-Clause
using MixedReality.Toolkit.Subsystems;
-using System;
using System.Collections;
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
{
@@ -22,33 +19,8 @@ namespace MixedReality.Toolkit.Input
/// can be more distracting than it's worth. However, for opaque platforms, this is a great solution.
///
[AddComponentMenu("MRTK/Input/Visualizers/Rigged Hand Mesh Visualizer")]
- public class RiggedHandMeshVisualizer : MonoBehaviour, ISelectInputVisualizer
+ public class RiggedHandMeshVisualizer : HandMeshVisualizer
{
- [SerializeField]
- [Tooltip("The XRNode on which this hand is located.")]
- private XRNode handNode = XRNode.LeftHand;
-
- /// The XRNode on which this hand is located.
- 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. When false, the rigged hands will only render " +
- "on devices with opaque displays.")]
- private bool showHandsOnTransparentDisplays;
-
- ///
- /// When true, this visualizer will render rigged hands even on XR devices with transparent displays.
- /// When false, the rigged hands will only render on devices with opaque displays.
- /// 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.
- ///
- public bool ShowHandsOnTransparentDisplays
- {
- get => showHandsOnTransparentDisplays;
- set => showHandsOnTransparentDisplays = value;
- }
-
[SerializeField]
[Tooltip("The transform of the wrist joint.")]
private Transform wrist;
@@ -58,44 +30,19 @@ public bool ShowHandsOnTransparentDisplays
private SkinnedMeshRenderer handRenderer = null;
[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";
+ [Tooltip("The primary visualizer. Rigged hand will not render if the primary is rendering.")]
+ private HandMeshVisualizer primaryMeshVisualizer = null;
- [SerializeField]
- [Tooltip("The input reader used when pinch selecting an interactable.")]
- XRInputButtonReader selectInput = new XRInputButtonReader("Select");
-
- #region ISelectInputVisualizer implementation
-
- ///
- /// Input reader used when pinch selecting an interactable.
- ///
- public XRInputButtonReader SelectInput
- {
- get => selectInput;
- set => SetInputProperty(ref selectInput, value);
- }
-
- #endregion ISelectInputVisualizer implementation
+ ///
+ protected override Renderer HandRenderer => handRenderer;
// Automatically calculated over time, based on the accumulated error
// between the user's actual joint locations and the armature's bones/joints.
private float handScale = 1.0f;
- // The property block used to modify the pinch amount property on the material
- private MaterialPropertyBlock propertyBlock = null;
-
- // Caching local references
+ // Caching local references
private HandsAggregatorSubsystem handsSubsystem;
- // Scratch list for checking for the presence of display subsystems.
- private List displaySubsystems = new List();
-
- // 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;
-
// The actual, physical, rigged joints that drive the skinned mesh.
// Otherwise referred to as "armature". Must be in OpenXR order.
private readonly Transform[] riggedVisualJointsArray = new Transform[(int)TrackedHandJoint.TotalJoints];
@@ -104,21 +51,12 @@ public XRInputButtonReader SelectInput
// at the end of a finger, which is discarded.
private const string endJointName = "end";
- ///
- /// 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 and .
- ///
- ///
- ///
- protected List buttonReaders { get; } = new List();
-
///
/// A Unity event function that is called when an enabled script instance is being loaded.
///
- protected virtual void Awake()
+ protected override void Awake()
{
- propertyBlock = new MaterialPropertyBlock();
- buttonReaders.Add(selectInput);
+ base.Awake();
if (handRenderer == null)
{
@@ -160,18 +98,10 @@ protected virtual void Awake()
}
}
- ///
- /// A Unity event function that is called when the script component has been enabled.
- ///
- protected void OnEnable()
+ ///
+ protected override 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})!");
+ base.OnEnable();
handsSubsystem = XRSubsystemHelpers.GetFirstRunningSubsystem();
@@ -181,17 +111,6 @@ protected void OnEnable()
}
}
- ///
- /// A Unity event function that is called when the script component has been disabled.
- ///
- protected void OnDisable()
- {
- buttonReaders.ForEach(reader => reader?.DisableDirectActionIfModeUsed());
-
- // Disable the rigged hand renderer when this component is disabled
- handRenderer.enabled = false;
- }
-
///
/// Coroutine to wait until subsystem becomes available.
///
@@ -204,11 +123,11 @@ private IEnumerator EnableWhenSubsystemAvailable()
///
/// A Unity event function that is called every frame, if this object is enabled.
///
- private void Update()
+ protected void Update()
{
// Query all joints in the hand.
if (!ShouldRenderHand() ||
- !handsSubsystem.TryGetEntireHand(handNode, out IReadOnlyList joints))
+ !handsSubsystem.TryGetEntireHand(HandNode, out IReadOnlyList joints))
{
// Hide the hand and abort if we shouldn't be
// showing the hand, for whatever reason.
@@ -293,49 +212,12 @@ private void Update()
// Apply.
handScale += -error * errorGainFactor;
handScale = Mathf.Clamp(handScale, minScale, maxScale);
- transform.localScale = new Vector3(handNode == XRNode.LeftHand ? -handScale : handScale, handScale, handScale);
+ transform.localScale = new Vector3(HandNode == XRNode.LeftHand ? -handScale : handScale, handScale, handScale);
// Update the hand material based on selectedness value
UpdateHandMaterial();
}
- ///
- /// Helper method for setting an input property.
- ///
- /// The to the field.
- /// The new value being set.
- ///
- /// 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.
- ///
- ///
- 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();
- }
- }
- }
-
// Computes the error between the rig's joint position and
// the user's joint position along the finger vector.
private float JointError(Vector3 armatureJointPosition, Vector3 userJointPosition, Vector3 fingerVector)
@@ -346,78 +228,15 @@ private float JointError(Vector3 armatureJointPosition, Vector3 userJointPositio
return Vector3.Dot((armatureJointPosition - userJointPosition), fingerVector);
}
- private bool ShouldRenderHand()
+ protected override bool ShouldRenderHand()
{
// If we're missing anything, don't render the hand.
- if (handsSubsystem == null || wrist == null || handRenderer == null)
- {
- return false;
- }
-
- 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;
- }
-
- private 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);
- propertyBlock.SetFloat(pinchAmountMaterialProperty, pinchAmount);
- handRenderer.SetPropertyBlock(propertyBlock);
- }
-
- ///
- /// Try to obtain the tracked devices selection value from the provided input reader.
- ///
- ///
- /// 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.
- ///
- 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();
- }
- if (controller != null)
- {
- value = controller.selectInteractionState.value;
- success = true;
- }
-#pragma warning restore CS0618 // XRBaseController is obsolete
-
- return success;
+ // Also don't render if the preferred visualizer is rendering.
+ return handsSubsystem != null
+ && wrist != null
+ && handRenderer != null
+ && (primaryMeshVisualizer == null || !primaryMeshVisualizer.IsRendering)
+ && base.ShouldRenderHand();
}
}
}
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader
index 2e80117a6..4ccc98705 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader
@@ -13,9 +13,12 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" {
Properties {
_OutlineColor ("Outline Color", Color) = (1,1,1,1)
_OutlineColorPinching ("Outline Color (Pinching)", Color) = (1,1,1,1)
- _OutlineThickness ("_OutlineThickness", Range(0.0,0.00003)) = 0.000012
- _HandThickness ("_HandThickness", Range(-0.0001,0.0001)) = 0.0
+ _OutlineThickness ("Outline Thickness", Range(0.0,0.003)) = 0.0012
+ _HandThickness ("Hand Thickness", Range(-0.01,0.01)) = 0.0
[PerRendererData]_PinchAmount ("Pinch Amount", Float) = 0
+ [PerRendererData]_FadeSphereCenter ("Fade Sphere Center", Vector) = (0,0,0,1)
+ [PerRendererData]_FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.05
+ [PerRendererData]_FadeDistance ("Fade Distance", Range(0,1)) = 0.025
}
SubShader {
@@ -48,6 +51,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" {
float4 vertex : POSITION;
float4 color : COLOR;
float3 normal : NORMAL;
+ float3 worldPos : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
@@ -61,16 +65,21 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" {
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+ float4 objectPos = v.vertex + normalize(v.normal) * (_HandThickness + _OutlineThickness);
o.normal = UnityObjectToWorldNormal(v.normal);
- o.vertex = UnityObjectToClipPos(v.vertex + v.normal * (_HandThickness + _OutlineThickness));
+ o.vertex = UnityObjectToClipPos(objectPos);
o.color = v.color;
-
+ o.worldPos = mul(unity_ObjectToWorld, objectPos).xyz;
+
return o;
}
uniform float4 _OutlineColor;
uniform float4 _OutlineColorPinching;
uniform float _PinchAmount;
+ uniform float4 _FadeSphereCenter;
+ uniform float _FadeSphereRadius;
+ uniform float _FadeDistance;
fixed4 frag(v2f i) : SV_Target
{
@@ -81,7 +90,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" {
// Fade the entire result based on the red channel. This is used to fade the hand
// out by the wrist, so the abrupt edge of the hand model is not visible.
- return float4(blendedOutlineColor.r, blendedOutlineColor.g, blendedOutlineColor.b, blendedOutlineColor.a * i.color.r);
+ return float4(blendedOutlineColor.r, blendedOutlineColor.g, blendedOutlineColor.b,
+ blendedOutlineColor.a *
+ ((_FadeSphereCenter.w * i.color.r) + ((1 - _FadeSphereCenter.w) * smoothstep(_FadeSphereRadius, _FadeSphereRadius + _FadeDistance, distance(i.worldPos, _FadeSphereCenter)))));
}
ENDCG
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader
index 401bcd452..f50ccd33b 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader
@@ -12,9 +12,12 @@
Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" {
Properties {
_HandColor ("Hand Color", Color) = (1,1,1,1)
- _HandThickness ("_HandThickness", Range(-0.0001,0.0001)) = 0.0
+ _HandThickness ("Hand Thickness", Range(-0.01,0.01)) = 0.0
_IlluminationExponent ("Illumination Exponent", Range(0,10)) = 1
_IlluminationAmount ("Illumination Amount", Range(0,10)) = 1
+ [PerRendererData]_FadeSphereCenter ("Fade Sphere Center", Vector) = (0,0,0,1)
+ [PerRendererData]_FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.05
+ [PerRendererData]_FadeDistance ("Fade Distance", Range(0,1)) = 0.025
}
SubShader {
@@ -46,6 +49,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" {
float4 color : COLOR;
float3 normal : NORMAL;
float3 viewDir: TEXCOORD1;
+ float3 worldPos : TEXCOORD2;
UNITY_VERTEX_OUTPUT_STEREO
};
@@ -58,9 +62,11 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" {
UNITY_INITIALIZE_OUTPUT(v2f, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+ float4 objectPos = v.vertex + normalize(v.normal) * _HandThickness;
o.normal = UnityObjectToWorldNormal(v.normal);
- o.vertex = UnityObjectToClipPos(v.vertex + v.normal * _HandThickness);
+ o.vertex = UnityObjectToClipPos(objectPos);
o.color = v.color;
+ o.worldPos = mul(unity_ObjectToWorld, objectPos).xyz;
// Distance-invariant view pos. Create "normalized view point"
// offset from object origin, then construct a new view vector
@@ -76,6 +82,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" {
uniform float _IlluminationAmount;
uniform float _IlluminationExponent;
uniform float4 _HandColor;
+ uniform float4 _FadeSphereCenter;
+ uniform float _FadeSphereRadius;
+ uniform float _FadeDistance;
fixed4 frag(v2f i) : SV_Target
{
@@ -84,7 +93,8 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" {
// Blend base color with the illumination/spotlight.
float4 hand = _HandColor + pow(spotlight, _IlluminationExponent) * _IlluminationAmount;
- return hand * float4(1,1,1,i.color.r);
+ return hand * float4(1,1,1,
+ ((_FadeSphereCenter.w * i.color.r) + ((1 - _FadeSphereCenter.w) * smoothstep(_FadeSphereRadius, _FadeSphereRadius + _FadeDistance, distance(i.worldPos, _FadeSphereCenter)))));
}
ENDCG
diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-PrepassZ.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-PrepassZ.shader
index a32b1ef18..db0065d1b 100644
--- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-PrepassZ.shader
+++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-PrepassZ.shader
@@ -50,7 +50,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (PrepassZ)" {
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// Inflate/deflate hand based on thickness.
- o.vertex = UnityObjectToClipPos(v.vertex + v.normal * _HandThickness);
+ o.vertex = UnityObjectToClipPos(v.vertex + normalize(v.normal) * _HandThickness);
return o;
}
diff --git a/org.mixedrealitytoolkit.input/package.json b/org.mixedrealitytoolkit.input/package.json
index a8d509f42..590b61790 100644
--- a/org.mixedrealitytoolkit.input/package.json
+++ b/org.mixedrealitytoolkit.input/package.json
@@ -21,7 +21,7 @@
"com.unity.inputsystem": "1.6.1",
"com.unity.xr.arfoundation": "5.0.5",
"com.unity.xr.core-utils": "2.1.0",
- "com.unity.xr.hands": "1.3.0",
+ "com.unity.xr.hands": "1.6.0",
"com.unity.xr.interaction.toolkit": "3.0.4",
"org.mixedrealitytoolkit.core": "4.0.0"
}