From d2d565163546d33c84cca45eb80608460aa6422a Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 27 Aug 2025 20:18:00 -0700 Subject: [PATCH 01/50] Don't overwrite the actual tracking state This introduced a bug in the XRI3 migration, because the old base class (ActionBasedController) read this value from the action every frame. The new base class (TrackedPoseDriver) only reads the events when the action state changes. --- .../Tracking/HandPoseDriver.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 } } From cef626584bc1ff30aaded5d28b2368bd09ad2eda Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 27 Feb 2025 15:33:12 -0800 Subject: [PATCH 02/50] Initial drop --- .../Visualizers/HandMeshVisualizer.cs | 42 +++++ .../Visualizers/HandMeshVisualizer.cs.meta | 11 ++ .../Visualizers/PlatformHandVisualizer.meta | 8 + .../LeftHandMesh.prefab | 63 ++++++++ .../LeftHandMesh.prefab.meta | 7 + .../PlatformHandMeshVisualizer.cs | 148 ++++++++++++++++++ .../PlatformHandMeshVisualizer.cs.meta | 11 ++ .../RightHandMesh.prefab | 105 +++++++++++++ .../RightHandMesh.prefab.meta | 7 + .../RiggedHandMeshVisualizer.cs | 42 +---- 10 files changed, 410 insertions(+), 34 deletions(-) create mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs create mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs.meta create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer.meta create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs.meta create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab create mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs new file mode 100644 index 000000000..89cf4f0e1 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -0,0 +1,42 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using UnityEngine; +using UnityEngine.XR; + +namespace MixedReality.Toolkit.Input +{ + public abstract class HandMeshVisualizer : MonoBehaviour + { + [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; + } + + protected virtual void OnEnable() + { + Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, + $"HandVisualizer has an invalid XRNode ({handNode})!"); + } + } +} 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/LeftHandMesh.prefab b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab new file mode 100644 index 000000000..08bb1f7a6 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1001 &8703204851570713327 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 0} + m_Modifications: + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7219515713117941393, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: m_Name + value: LeftHandMesh + objectReference: {fileID: 0} + - target: {fileID: 7614440470985866575, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} + propertyPath: handNode + value: 4 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta new file mode 100644 index 000000000..d0f885a00 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3fec8c75241ac744cbb623f1e3a842bf +PrefabImporter: + 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..512a86aed --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -0,0 +1,148 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using UnityEngine; +using UnityEngine.XR.Interaction.Toolkit; +using UnityEngine.Serialization; + +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) +using Microsoft.MixedReality.OpenXR; +#endif + +namespace MixedReality.Toolkit.Input +{ + public class PlatformHandMeshVisualizer : HandMeshVisualizer + { + [SerializeField] + private MeshFilter meshFilter; + + [SerializeField, FormerlySerializedAs("meshRenderer")] + private Renderer handRenderer; + + [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"; + +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + private HandMeshTracker handMeshTracker; +#endif + + private Mesh neutralPoseMesh; + private bool initializedUVs = false; + + // The property block used to modify the pinch amount property on the material + private MaterialPropertyBlock propertyBlock = null; + + // The XRController that is used to determine the pinch strength (i.e., select value!) + private XRBaseController controller; + + protected override void OnEnable() + { + base.OnEnable(); + + if (neutralPoseMesh == null) + { + neutralPoseMesh = new Mesh(); + } + + propertyBlock ??= new MaterialPropertyBlock(); + +#if UNITY_OPENXR_PRESENT + if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) + { +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + handMeshTracker = HandNode == UnityEngine.XR.XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; +#endif + } +#endif + } + + protected void Update() + { +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + if (handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh)) + { + if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) + { + meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); ; + initializedUVs = true; + } + + if (handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) + { + transform.SetPositionAndRotation(pose.position, pose.rotation); + } + + if (!handRenderer.enabled) + { + handRenderer.enabled = true; + } + } + else if (handRenderer.enabled) + { + handRenderer.enabled = false; + } +#endif + + UpdateHandMaterial(); + } + + private void UpdateHandMaterial() + { + if (controller == null) + { + controller = GetComponentInParent(); + } + + if (controller == null || handRenderer == null) { return; } + + // Update the hand material + float pinchAmount = Mathf.Pow(controller.selectInteractionState.value, 2.0f); + handRenderer.GetPropertyBlock(propertyBlock); + propertyBlock.SetFloat(pinchAmountMaterialProperty, pinchAmount); + handRenderer.SetPropertyBlock(propertyBlock); + } + +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + private 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/PlatformHandVisualizer/RightHandMesh.prefab b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab new file mode 100644 index 000000000..ad268f38f --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab @@ -0,0 +1,105 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7219515713117941393 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4027372128791689064} + - component: {fileID: 1785587026626609695} + - component: {fileID: 7119468259009842434} + - component: {fileID: 7614440470985866575} + m_Layer: 0 + m_Name: RightHandMesh + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4027372128791689064 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7219515713117941393} + 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: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1785587026626609695 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7219515713117941393} + m_Mesh: {fileID: 0} +--- !u!23 &7119468259009842434 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7219515713117941393} + 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 &7614440470985866575 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7219515713117941393} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f42ee07c35d33c44989eee5953f625d2, type: 3} + m_Name: + m_EditorClassIdentifier: + handNode: 5 + showHandsOnTransparentDisplays: 0 + meshFilter: {fileID: 1785587026626609695} + handRenderer: {fileID: 7119468259009842434} + pinchAmountMaterialProperty: _PinchAmount diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta new file mode 100644 index 000000000..b6ae484fb --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b3527ff5e99de014c8ebbb25fd22474e +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index 6fb8f0fe8..86884175e 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs @@ -22,33 +22,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, 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. 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; @@ -163,16 +138,15 @@ 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() { + base.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})!"); - handsSubsystem = XRSubsystemHelpers.GetFirstRunningSubsystem(); if (handsSubsystem == null) @@ -204,11 +178,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,7 +267,7 @@ 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(); @@ -365,7 +339,7 @@ private bool ShouldRenderHand() if (displaySubsystems.Count > 0 && displaySubsystems[0].running && !displaySubsystems[0].displayOpaque && - !showHandsOnTransparentDisplays) + !ShowHandsOnTransparentDisplays) { return false; } From b7d5fd602dac15d5156b626756714f58edddab47 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 14 Mar 2025 14:41:11 -0700 Subject: [PATCH 03/50] Iterate --- .../Visualizers/HandMeshVisualizer.cs | 26 +++++++++++++++++ .../PlatformHandMeshVisualizer.cs | 13 ++++++--- .../RiggedHandMeshVisualizer.cs | 29 ++----------------- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index 89cf4f0e1..dc518c335 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -1,6 +1,7 @@ // Copyright (c) Mixed Reality Toolkit Contributors // Licensed under the BSD 3-Clause +using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; @@ -33,10 +34,35 @@ public bool ShowHandsOnTransparentDisplays set => showHandsOnTransparentDisplays = value; } + // Scratch list for checking for the presence of display subsystems. + private readonly List displaySubsystems = new List(); + protected virtual void OnEnable() { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, $"HandVisualizer has an invalid XRNode ({handNode})!"); } + + 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; + } } } diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 512a86aed..0bc8646e7 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -3,7 +3,6 @@ using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; -using UnityEngine.Serialization; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) using Microsoft.MixedReality.OpenXR; @@ -16,7 +15,7 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer [SerializeField] private MeshFilter meshFilter; - [SerializeField, FormerlySerializedAs("meshRenderer")] + [SerializeField] private Renderer handRenderer; [SerializeField] @@ -61,11 +60,11 @@ protected override void OnEnable() protected void Update() { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh)) + if (ShouldRenderHand() && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh)) { if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) { - meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); ; + meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); initializedUVs = true; } @@ -105,6 +104,12 @@ private void UpdateHandMaterial() } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + protected override bool ShouldRenderHand() + { + // If we're missing anything, don't render the hand. + return handMeshTracker != null && meshFilter != null && handRenderer != null && base.ShouldRenderHand(); + } + private Vector2[] InitializeUVs(Vector3[] neutralPoseVertices) { if (neutralPoseVertices?.Length == 0) diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index 86884175e..4809caec7 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs @@ -64,9 +64,6 @@ public XRInputButtonReader SelectInput // 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; @@ -320,32 +317,10 @@ 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; + return handsSubsystem != null && wrist != null && handRenderer != null && base.ShouldRenderHand(); } private void UpdateHandMaterial() From 1c3ee5e36e523d38250e7ddf8fb03fe9b902faf6 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Sat, 15 Mar 2025 14:17:54 -0700 Subject: [PATCH 04/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 0bc8646e7..ae2e434fb 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -10,6 +10,7 @@ namespace MixedReality.Toolkit.Input { + [AddComponentMenu("MRTK/Input/Visualizers/Platform Hand Mesh Visualizer")] public class PlatformHandMeshVisualizer : HandMeshVisualizer { [SerializeField] @@ -60,7 +61,9 @@ protected override void OnEnable() protected void Update() { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (ShouldRenderHand() && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh)) + if (ShouldRenderHand() + && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) + && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) { if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) { @@ -68,10 +71,7 @@ protected void Update() initializedUVs = true; } - if (handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) - { - transform.SetPositionAndRotation(pose.position, pose.rotation); - } + transform.SetPositionAndRotation(pose.position, pose.rotation); if (!handRenderer.enabled) { From b5378ea0f635873cfacd0f9218322cad33f1b742 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Sat, 15 Mar 2025 14:21:21 -0700 Subject: [PATCH 05/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index ae2e434fb..80af36f81 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -61,27 +61,27 @@ protected override void OnEnable() protected void Update() { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (ShouldRenderHand() - && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) - && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) + if (!ShouldRenderHand() || + !handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) || + !handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) { - if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) - { - meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); - initializedUVs = true; - } + // 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; + } - transform.SetPositionAndRotation(pose.position, pose.rotation); + handRenderer.enabled = true; - if (!handRenderer.enabled) - { - handRenderer.enabled = true; - } - } - else if (handRenderer.enabled) + if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) { - handRenderer.enabled = false; + meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); + initializedUVs = true; } + + transform.SetPositionAndRotation(pose.position, pose.rotation); #endif UpdateHandMaterial(); From 268bda72dcad475fddb7e2f81056f01674ae32ef Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 09:53:10 -0700 Subject: [PATCH 06/50] Docs update --- .../Visualizers/HandMeshVisualizer.cs | 3 +++ .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 1 + .../RiggedHandVisualizer/RiggedHandMeshVisualizer.cs | 4 +--- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index dc518c335..8b062ca9d 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -37,6 +37,9 @@ public bool ShowHandsOnTransparentDisplays // Scratch list for checking for the presence of display subsystems. private readonly List displaySubsystems = new List(); + /// + /// A Unity event function that is called when the script component has been enabled. + /// protected virtual void OnEnable() { Debug.Assert(handNode == XRNode.LeftHand || handNode == XRNode.RightHand, diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 80af36f81..aaac9d565 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -37,6 +37,7 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer // The XRController that is used to determine the pinch strength (i.e., select value!) private XRBaseController controller; + /// protected override void OnEnable() { base.OnEnable(); diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index 4809caec7..0839aee4e 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs @@ -132,9 +132,7 @@ protected virtual void Awake() } } - /// - /// A Unity event function that is called when the script component has been enabled. - /// + /// protected override void OnEnable() { base.OnEnable(); From 5c1ac8dd326e671f76006aadc37bb69fe1729d54 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 10:04:25 -0700 Subject: [PATCH 07/50] Move prefabs into own folder Move back --- org.mixedrealitytoolkit.input/Visualizers/Prefabs.meta | 8 ++++++++ .../openxr_left_hand.prefab | 0 .../openxr_left_hand.prefab.meta | 0 .../openxr_right_hand.prefab | 0 .../openxr_right_hand.prefab.meta | 0 5 files changed, 8 insertions(+) create mode 100644 org.mixedrealitytoolkit.input/Visualizers/Prefabs.meta rename org.mixedrealitytoolkit.input/Visualizers/{RiggedHandVisualizer => Prefabs}/openxr_left_hand.prefab (100%) rename org.mixedrealitytoolkit.input/Visualizers/{RiggedHandVisualizer => Prefabs}/openxr_left_hand.prefab.meta (100%) rename org.mixedrealitytoolkit.input/Visualizers/{RiggedHandVisualizer => Prefabs}/openxr_right_hand.prefab (100%) rename org.mixedrealitytoolkit.input/Visualizers/{RiggedHandVisualizer => Prefabs}/openxr_right_hand.prefab.meta (100%) 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 100% rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_left_hand.prefab rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab 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 100% rename from org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/openxr_right_hand.prefab rename to org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab 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 From e3b5fe04720f507be165a018754c13704c0aac50 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 14:03:51 -0700 Subject: [PATCH 08/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index aaac9d565..c5bb9e3fa 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -42,6 +42,8 @@ protected override void OnEnable() { base.OnEnable(); + handRenderer.enabled = false; + if (neutralPoseMesh == null) { neutralPoseMesh = new Mesh(); @@ -59,6 +61,15 @@ protected override void OnEnable() #endif } + /// + /// A Unity event function that is called when the script component has been disabled. + /// + protected void OnDisable() + { + // Disable the rigged hand renderer when this component is disabled + handRenderer.enabled = false; + } + protected void Update() { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) @@ -104,13 +115,17 @@ private void UpdateHandMaterial() handRenderer.SetPropertyBlock(propertyBlock); } -#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) protected override bool ShouldRenderHand() { // If we're missing anything, don't render the hand. - return handMeshTracker != null && meshFilter != null && handRenderer != null && base.ShouldRenderHand(); + return +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + handMeshTracker != null && +#endif + meshFilter != null && handRenderer != null && base.ShouldRenderHand(); } +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) private Vector2[] InitializeUVs(Vector3[] neutralPoseVertices) { if (neutralPoseVertices?.Length == 0) From 0ff4a748c5d233512b575a431fd47ee25eb886d5 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 14:06:47 -0700 Subject: [PATCH 09/50] Remove temp hand mesh prefabs --- .../LeftHandMesh.prefab | 63 ----------- .../LeftHandMesh.prefab.meta | 7 -- .../RightHandMesh.prefab | 105 ------------------ .../RightHandMesh.prefab.meta | 7 -- 4 files changed, 182 deletions(-) delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab deleted file mode 100644 index 08bb1f7a6..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab +++ /dev/null @@ -1,63 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1001 &8703204851570713327 -PrefabInstance: - m_ObjectHideFlags: 0 - serializedVersion: 2 - m_Modification: - m_TransformParent: {fileID: 0} - m_Modifications: - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_RootOrder - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalPosition.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalPosition.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalPosition.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalRotation.w - value: 1 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalRotation.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalRotation.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalRotation.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalEulerAnglesHint.x - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalEulerAnglesHint.y - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 4027372128791689064, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_LocalEulerAnglesHint.z - value: 0 - objectReference: {fileID: 0} - - target: {fileID: 7219515713117941393, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: m_Name - value: LeftHandMesh - objectReference: {fileID: 0} - - target: {fileID: 7614440470985866575, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} - propertyPath: handNode - value: 4 - objectReference: {fileID: 0} - m_RemovedComponents: [] - m_SourcePrefab: {fileID: 100100000, guid: b3527ff5e99de014c8ebbb25fd22474e, type: 3} diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta deleted file mode 100644 index d0f885a00..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/LeftHandMesh.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 3fec8c75241ac744cbb623f1e3a842bf -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab deleted file mode 100644 index ad268f38f..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab +++ /dev/null @@ -1,105 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &7219515713117941393 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 4027372128791689064} - - component: {fileID: 1785587026626609695} - - component: {fileID: 7119468259009842434} - - component: {fileID: 7614440470985866575} - m_Layer: 0 - m_Name: RightHandMesh - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!4 &4027372128791689064 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7219515713117941393} - 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: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!33 &1785587026626609695 -MeshFilter: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7219515713117941393} - m_Mesh: {fileID: 0} ---- !u!23 &7119468259009842434 -MeshRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7219515713117941393} - 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 &7614440470985866575 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7219515713117941393} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f42ee07c35d33c44989eee5953f625d2, type: 3} - m_Name: - m_EditorClassIdentifier: - handNode: 5 - showHandsOnTransparentDisplays: 0 - meshFilter: {fileID: 1785587026626609695} - handRenderer: {fileID: 7119468259009842434} - pinchAmountMaterialProperty: _PinchAmount diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta deleted file mode 100644 index b6ae484fb..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/RightHandMesh.prefab.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: b3527ff5e99de014c8ebbb25fd22474e -PrefabImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: From 1e57c8f5fa39c14419b775ee171040c08299dd9d Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 14:18:06 -0700 Subject: [PATCH 10/50] Add hand mesh manager --- .../Visualizers/HandMeshVisualizer.cs | 5 + .../Visualizers/HandMeshVisualizerManager.cs | 49 ++++ .../HandMeshVisualizerManager.cs.meta | 11 + .../PlatformHandMeshVisualizer.cs | 3 + .../Prefabs/openxr_left_hand.prefab | 20 +- .../Prefabs/openxr_right_hand.prefab | 268 ++++++++++++++---- .../RiggedHandMeshVisualizer.cs | 3 + 7 files changed, 297 insertions(+), 62 deletions(-) create mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs create mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index 8b062ca9d..de33d7001 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -34,6 +34,11 @@ public bool ShowHandsOnTransparentDisplays set => showHandsOnTransparentDisplays = value; } + /// + /// Whether this visualizer currently has a loaded and visible hand mesh or not. + /// + public abstract bool IsRendering { get; } + // Scratch list for checking for the presence of display subsystems. private readonly List displaySubsystems = new List(); diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs new file mode 100644 index 000000000..c5bbabbf2 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs @@ -0,0 +1,49 @@ +// Copyright (c) Mixed Reality Toolkit Contributors +// Licensed under the BSD 3-Clause + +using UnityEngine; + +namespace MixedReality.Toolkit.Input +{ + /// + /// Manages the visibility of a set of hand mesh visualizers, all representing possible + /// visualization modes for a single hand (like platform-provided vs the built-in rigged hand mesh). + /// + public class HandMeshVisualizerManager : MonoBehaviour + { + [SerializeField, Tooltip("A priority-ordered visualizer list.")] + private HandMeshVisualizer[] visualizers; + + protected void Update() + { + int renderingVisualizer = -1; + + for (int i = 0; i < visualizers.Length; i++) + { + // If we've already found a rendering visualizer, we want to turn off the rest + if (renderingVisualizer > -1) + { + visualizers[i].enabled = false; + } + else if (visualizers[i].IsRendering) + { + renderingVisualizer = i; + } + } + + // Ensure any visualizers that preceded the rendering one are turned off + for (int i = 0; i < renderingVisualizer; i++) + { + visualizers[i].enabled = false; + } + + if (renderingVisualizer == -1) + { + for (int i = 0; i < visualizers.Length; i++) + { + visualizers[i].enabled = true; + } + } + } + } +} diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta new file mode 100644 index 000000000..34f526b29 --- /dev/null +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 595ce5ac335c53647a1f7270d450d94b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c5bb9e3fa..34e2e755e 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -37,6 +37,9 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer // The XRController that is used to determine the pinch strength (i.e., select value!) private XRBaseController controller; + /// + public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); + /// protected override void OnEnable() { diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab index 37c3a06e3..eeb1b0c4d 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/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} @@ -99,6 +100,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 +124,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 +169,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/Prefabs/openxr_right_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab index b5dbb75e4..f74556a81 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/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,8 +566,8 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 3973969148631863464} - - component: {fileID: 3507431783398283103} - component: {fileID: -7366846053233975685} + - component: {fileID: 9181990906488135268} m_Layer: 0 m_Name: openxr_right_hand m_TagString: Untagged @@ -582,33 +582,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 @@ -643,6 +626,21 @@ MonoBehaviour: m_Flags: 0 m_Flags: 0 m_Reference: {fileID: 0} +--- !u!114 &9181990906488135268 +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: 595ce5ac335c53647a1f7270d450d94b, type: 3} + m_Name: + m_EditorClassIdentifier: + visualizers: + - {fileID: 2263138549649042646} + - {fileID: 1800205545854781715} --- !u!1 &4657295888579565740 GameObject: m_ObjectHideFlags: 0 @@ -666,6 +664,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 +672,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 +696,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 +704,110 @@ 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 + meshFilter: {fileID: 3936565761691908697} + handRenderer: {fileID: 769216864695519206} + pinchAmountMaterialProperty: _PinchAmount --- !u!1 &5839269735351241340 GameObject: m_ObjectHideFlags: 0 @@ -730,8 +831,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 +842,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 +867,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 +875,58 @@ 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 + wrist: {fileID: 5446693289115484214} + handRenderer: {fileID: 5709336421934327412} + pinchAmountMaterialProperty: _PinchAmount --- !u!1 &6561479249547925302 GameObject: m_ObjectHideFlags: 0 @@ -798,13 +950,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 +981,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 +989,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 +1013,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 +1044,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 +1052,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 +1076,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 +1084,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 +1108,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 +1116,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 +1140,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 +1148,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 +1172,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 +1203,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 +1211,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 +1235,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 +1243,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 +1267,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 +1275,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 +1299,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 +1307,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/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index 0839aee4e..a2f93c6c6 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs @@ -76,6 +76,9 @@ public XRInputButtonReader SelectInput // at the end of a finger, which is discarded. private const string endJointName = "end"; + /// + public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); + /// /// 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 . From f7ed8c6b91de9b9d2c6a970d474d4708f8514b8b Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 17 Mar 2025 16:40:08 -0700 Subject: [PATCH 11/50] Iterate --- .../Controllers/HandModel.cs | 10 +- .../Visualizers/HandMeshVisualizer.cs | 154 ++++++++++++++++- .../Visualizers/HandMeshVisualizerManager.cs | 49 ------ .../HandMeshVisualizerManager.cs.meta | 11 -- .../PlatformHandMeshVisualizer.cs | 41 +---- .../Prefabs/openxr_right_hand.prefab | 81 +++++++-- .../RiggedHandMeshVisualizer.cs | 155 ++---------------- 7 files changed, 238 insertions(+), 263 deletions(-) delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs delete mode 100644 org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta 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/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index de33d7001..d2c53da36 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -1,13 +1,16 @@ // 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 + public abstract class HandMeshVisualizer : MonoBehaviour, ISelectInputVisualizer { [SerializeField] [Tooltip("The XRNode on which this hand is located.")] @@ -34,23 +37,126 @@ public bool 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 + /// /// Whether this visualizer currently has a loaded and visible hand mesh or not. /// public abstract bool IsRendering { get; } + protected abstract Renderer HandRenderer { get; } + + // 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 . + /// + /// + /// + 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() + { + 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) @@ -72,5 +178,51 @@ protected virtual bool ShouldRenderHand() // 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/HandMeshVisualizerManager.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs deleted file mode 100644 index c5bbabbf2..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Mixed Reality Toolkit Contributors -// Licensed under the BSD 3-Clause - -using UnityEngine; - -namespace MixedReality.Toolkit.Input -{ - /// - /// Manages the visibility of a set of hand mesh visualizers, all representing possible - /// visualization modes for a single hand (like platform-provided vs the built-in rigged hand mesh). - /// - public class HandMeshVisualizerManager : MonoBehaviour - { - [SerializeField, Tooltip("A priority-ordered visualizer list.")] - private HandMeshVisualizer[] visualizers; - - protected void Update() - { - int renderingVisualizer = -1; - - for (int i = 0; i < visualizers.Length; i++) - { - // If we've already found a rendering visualizer, we want to turn off the rest - if (renderingVisualizer > -1) - { - visualizers[i].enabled = false; - } - else if (visualizers[i].IsRendering) - { - renderingVisualizer = i; - } - } - - // Ensure any visualizers that preceded the rendering one are turned off - for (int i = 0; i < renderingVisualizer; i++) - { - visualizers[i].enabled = false; - } - - if (renderingVisualizer == -1) - { - for (int i = 0; i < visualizers.Length; i++) - { - visualizers[i].enabled = true; - } - } - } - } -} diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta deleted file mode 100644 index 34f526b29..000000000 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizerManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 595ce5ac335c53647a1f7270d450d94b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 34e2e755e..bd322e686 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -2,7 +2,6 @@ // Licensed under the BSD 3-Clause using UnityEngine; -using UnityEngine.XR.Interaction.Toolkit; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) using Microsoft.MixedReality.OpenXR; @@ -17,12 +16,9 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private MeshFilter meshFilter; [SerializeField] - private Renderer handRenderer; + private MeshRenderer handRenderer; - [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"; + protected override Renderer HandRenderer => handRenderer; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) private HandMeshTracker handMeshTracker; @@ -31,12 +27,6 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private Mesh neutralPoseMesh; private bool initializedUVs = false; - // The property block used to modify the pinch amount property on the material - private MaterialPropertyBlock propertyBlock = null; - - // The XRController that is used to determine the pinch strength (i.e., select value!) - private XRBaseController controller; - /// public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); @@ -52,8 +42,6 @@ protected override void OnEnable() neutralPoseMesh = new Mesh(); } - propertyBlock ??= new MaterialPropertyBlock(); - #if UNITY_OPENXR_PRESENT if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) { @@ -64,15 +52,6 @@ protected override void OnEnable() #endif } - /// - /// A Unity event function that is called when the script component has been disabled. - /// - protected void OnDisable() - { - // Disable the rigged hand renderer when this component is disabled - handRenderer.enabled = false; - } - protected void Update() { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) @@ -102,22 +81,6 @@ protected void Update() UpdateHandMaterial(); } - private void UpdateHandMaterial() - { - if (controller == null) - { - controller = GetComponentInParent(); - } - - if (controller == null || handRenderer == null) { return; } - - // Update the hand material - float pinchAmount = Mathf.Pow(controller.selectInteractionState.value, 2.0f); - handRenderer.GetPropertyBlock(propertyBlock); - propertyBlock.SetFloat(pinchAmountMaterialProperty, pinchAmount); - handRenderer.SetPropertyBlock(propertyBlock); - } - protected override bool ShouldRenderHand() { // If we're missing anything, don't render the hand. diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab index f74556a81..ed34ea8aa 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab +++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab @@ -567,7 +567,6 @@ GameObject: m_Component: - component: {fileID: 3973969148631863464} - component: {fileID: -7366846053233975685} - - component: {fileID: 9181990906488135268} m_Layer: 0 m_Name: openxr_right_hand m_TagString: Untagged @@ -626,21 +625,6 @@ MonoBehaviour: m_Flags: 0 m_Flags: 0 m_Reference: {fileID: 0} ---- !u!114 &9181990906488135268 -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: 595ce5ac335c53647a1f7270d450d94b, type: 3} - m_Name: - m_EditorClassIdentifier: - visualizers: - - {fileID: 2263138549649042646} - - {fileID: 1800205545854781715} --- !u!1 &4657295888579565740 GameObject: m_ObjectHideFlags: 0 @@ -805,9 +789,39 @@ MonoBehaviour: 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} - pinchAmountMaterialProperty: _PinchAmount --- !u!1 &5839269735351241340 GameObject: m_ObjectHideFlags: 0 @@ -924,9 +938,40 @@ MonoBehaviour: 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} - pinchAmountMaterialProperty: _PinchAmount + primaryMeshVisualizer: {fileID: 2263138549649042646} --- !u!1 &6561479249547925302 GameObject: m_ObjectHideFlags: 0 diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index a2f93c6c6..4eac8648f 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,7 +19,7 @@ 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 : HandMeshVisualizer, ISelectInputVisualizer + public class RiggedHandMeshVisualizer : HandMeshVisualizer { [SerializeField] [Tooltip("The transform of the wrist joint.")] @@ -33,41 +30,18 @@ public class RiggedHandMeshVisualizer : HandMeshVisualizer, ISelectInputVisualiz 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; - // 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]; @@ -79,21 +53,12 @@ public XRInputButtonReader SelectInput /// public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); - /// - /// 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) { @@ -140,11 +105,6 @@ protected override void OnEnable() { base.OnEnable(); - buttonReaders.ForEach(reader => reader?.EnableDirectActionIfModeUsed()); - - // Ensure hand is not visible until we can update position first time. - handRenderer.enabled = false; - handsSubsystem = XRSubsystemHelpers.GetFirstRunningSubsystem(); if (handsSubsystem == null) @@ -153,17 +113,6 @@ protected override 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. /// @@ -271,43 +220,6 @@ protected void Update() 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) @@ -321,53 +233,12 @@ private float JointError(Vector3 armatureJointPosition, Vector3 userJointPositio protected override bool ShouldRenderHand() { // If we're missing anything, don't render the hand. - return handsSubsystem != null && wrist != null && handRenderer != null && base.ShouldRenderHand(); - } - - 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(); } } } From 4e877c873fa5a344bf54774ca0eccef162dfb52d Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 18 Mar 2025 12:11:09 -0700 Subject: [PATCH 12/50] Iterate --- .../Visualizers/HandMeshVisualizer.cs | 17 ++++++++++------- .../PlatformHandMeshVisualizer.cs | 4 +--- .../RiggedHandMeshVisualizer.cs | 4 +--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index d2c53da36..2b0890235 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -59,13 +59,6 @@ public XRInputButtonReader SelectInput #endregion ISelectInputVisualizer implementation - /// - /// Whether this visualizer currently has a loaded and visible hand mesh or not. - /// - public abstract bool IsRendering { get; } - - protected abstract Renderer HandRenderer { get; } - // The property block used to modify the pinch amount property on the material private MaterialPropertyBlock propertyBlock = null; @@ -84,6 +77,16 @@ public XRInputButtonReader SelectInput /// protected List buttonReaders { get; } = 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. /// diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index bd322e686..c30bde27b 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -18,6 +18,7 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer [SerializeField] private MeshRenderer handRenderer; + /// protected override Renderer HandRenderer => handRenderer; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) @@ -27,9 +28,6 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private Mesh neutralPoseMesh; private bool initializedUVs = false; - /// - public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); - /// protected override void OnEnable() { diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs index 4eac8648f..4b039cb50 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHandMeshVisualizer.cs @@ -33,6 +33,7 @@ public class RiggedHandMeshVisualizer : HandMeshVisualizer [Tooltip("The primary visualizer. Rigged hand will not render if the primary is rendering.")] private HandMeshVisualizer primaryMeshVisualizer = null; + /// protected override Renderer HandRenderer => handRenderer; // Automatically calculated over time, based on the accumulated error @@ -50,9 +51,6 @@ public class RiggedHandMeshVisualizer : HandMeshVisualizer // at the end of a finger, which is discarded. private const string endJointName = "end"; - /// - public override bool IsRendering => handRenderer != null && handRenderer.enabled && ShouldRenderHand(); - /// /// A Unity event function that is called when an enabled script instance is being loaded. /// From d23d1ac182bddf1d84f709ff8469dcaadf04086d Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 18 Mar 2025 12:57:42 -0700 Subject: [PATCH 13/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c30bde27b..b62b61ddd 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -47,7 +47,11 @@ protected override void OnEnable() handMeshTracker = HandNode == UnityEngine.XR.XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; #endif } + else #endif + { + enabled = false; + } } protected void Update() From 408ef097b37e486b0993f92666a37c99f5d46b9d Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 6 Mar 2025 11:51:26 -0800 Subject: [PATCH 14/50] Add AXR hand mesh support stash Update OpenXRHandsSubsystem.cs --- .../Subsystems/Hands/UnityHandsSubsystem.cs | 3 +- .../PlatformHandMeshVisualizer.cs | 49 +++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) 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/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index b62b61ddd..35569431b 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -1,7 +1,10 @@ // Copyright (c) Mixed Reality Toolkit Contributors // Licensed under the BSD 3-Clause +using System.Collections.Generic; using UnityEngine; +using UnityEngine.XR; +using UnityEngine.XR.Interaction.Toolkit; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) using Microsoft.MixedReality.OpenXR; @@ -28,6 +31,8 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private Mesh neutralPoseMesh; private bool initializedUVs = false; + private XRMeshSubsystem meshSubsystem = null; + /// protected override void OnEnable() { @@ -41,10 +46,23 @@ protected override void OnEnable() } #if UNITY_OPENXR_PRESENT - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) + if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) + { + List meshSubsystems = new List(); + SubsystemManager.GetSubsystems(meshSubsystems); + foreach (XRMeshSubsystem subsystem in meshSubsystems) + { + if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider") + { + meshSubsystem = subsystem; + break; + } + } + } + else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - handMeshTracker = HandNode == UnityEngine.XR.XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; + handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; #endif } else @@ -56,8 +74,33 @@ protected override void OnEnable() protected void Update() { + if (meshSubsystem != null) + { + List meshInfos = new List(); + if (meshSubsystem.TryGetMeshInfos(meshInfos)) + { + int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; + + if (meshInfos[handMeshIndex].ChangeState == MeshChangeState.Added + || meshInfos[handMeshIndex].ChangeState == MeshChangeState.Updated) + { + meshSubsystem.GenerateMeshAsync(meshInfos[handMeshIndex].MeshId, meshFilter.mesh, + null, MeshVertexAttributes.Normals, result => { }); + + if (!handRenderer.enabled) + { + handRenderer.enabled = true; + } + } + } + else if (handRenderer.enabled) + { + handRenderer.enabled = false; + } + } + #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (!ShouldRenderHand() || + else if (!ShouldRenderHand() || !handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) || !handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) { From adf14ffbc3a14adf3b84955ac84613ccd22a016e Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 18 Mar 2025 13:45:32 -0700 Subject: [PATCH 15/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 35569431b..c77aa4c79 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using UnityEngine; using UnityEngine.XR; -using UnityEngine.XR.Interaction.Toolkit; #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) using Microsoft.MixedReality.OpenXR; @@ -32,6 +31,7 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private bool initializedUVs = false; private XRMeshSubsystem meshSubsystem = null; + private readonly List meshInfos = new List(); /// protected override void OnEnable() @@ -74,35 +74,47 @@ protected override void OnEnable() protected void Update() { - if (meshSubsystem != null) + if (!ShouldRenderHand()) { - List meshInfos = new List(); - if (meshSubsystem.TryGetMeshInfos(meshInfos)) - { - int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; + // 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 (meshInfos[handMeshIndex].ChangeState == MeshChangeState.Added - || meshInfos[handMeshIndex].ChangeState == MeshChangeState.Updated) - { - meshSubsystem.GenerateMeshAsync(meshInfos[handMeshIndex].MeshId, meshFilter.mesh, - null, MeshVertexAttributes.Normals, result => { }); + if (meshSubsystem != null && + meshSubsystem.TryGetMeshInfos(meshInfos)) + { + int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; - if (!handRenderer.enabled) - { - handRenderer.enabled = true; - } - } - } - else if (handRenderer.enabled) + if (meshInfos[handMeshIndex].ChangeState == MeshChangeState.Added + || meshInfos[handMeshIndex].ChangeState == MeshChangeState.Updated) { - handRenderer.enabled = false; + meshSubsystem.GenerateMeshAsync(meshInfos[handMeshIndex].MeshId, meshFilter.mesh, + null, MeshVertexAttributes.Normals, result => { }); + + handRenderer.enabled = true; } } - #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - else if (!ShouldRenderHand() || - !handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) || - !handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) + else if (handMeshTracker != null && + handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) && + handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) + { + handRenderer.enabled = true; + + if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) + { + meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); + initializedUVs = true; + } + + transform.SetPositionAndRotation(pose.position, pose.rotation); + } +#endif + else { // Hide the hand and abort if we shouldn't be // showing the hand, for whatever reason. @@ -112,28 +124,13 @@ protected void Update() return; } - handRenderer.enabled = true; - - if (!initializedUVs && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm)) - { - meshFilter.mesh.uv = InitializeUVs(neutralPoseMesh.vertices); - initializedUVs = true; - } - - transform.SetPositionAndRotation(pose.position, pose.rotation); -#endif - UpdateHandMaterial(); } protected override bool ShouldRenderHand() { // If we're missing anything, don't render the hand. - return -#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - handMeshTracker != null && -#endif - meshFilter != null && handRenderer != null && base.ShouldRenderHand(); + return meshFilter != null && handRenderer != null && base.ShouldRenderHand(); } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) From fd20dd81e33c7482629e2e131effc796c63312b4 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 18 Mar 2025 16:27:04 -0700 Subject: [PATCH 16/50] Update PlatformHandMeshVisualizer.cs Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c77aa4c79..f46b64772 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -54,6 +54,7 @@ protected override void OnEnable() { if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider") { + Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); meshSubsystem = subsystem; break; } @@ -62,6 +63,7 @@ protected override void OnEnable() else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) { #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) + Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; #endif } @@ -84,24 +86,30 @@ protected void Update() return; } - if (meshSubsystem != null && - meshSubsystem.TryGetMeshInfos(meshInfos)) + if (meshSubsystem != null + && meshSubsystem.running + && meshSubsystem.TryGetMeshInfos(meshInfos)) { int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; - if (meshInfos[handMeshIndex].ChangeState == MeshChangeState.Added - || meshInfos[handMeshIndex].ChangeState == MeshChangeState.Updated) + MeshInfo meshInfo = meshInfos[handMeshIndex]; + if (meshInfo.ChangeState == MeshChangeState.Added + || meshInfo.ChangeState == MeshChangeState.Updated) { - meshSubsystem.GenerateMeshAsync(meshInfos[handMeshIndex].MeshId, meshFilter.mesh, + handRenderer.enabled = true; + + meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, null, MeshVertexAttributes.Normals, result => { }); - handRenderer.enabled = true; + // This hand mesh is provided pre-translated from the world origin, + // so we want to ensure the mesh is "centered" at the world origin + transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); } } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - else if (handMeshTracker != null && - handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) && - handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) + else if (handMeshTracker != null + && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) + && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) { handRenderer.enabled = true; From 91f4909d951af6e07f1fb654cbbb99f33bb86796 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 19 Mar 2025 09:13:29 -0700 Subject: [PATCH 17/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index f46b64772..933c81ec2 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -25,10 +25,9 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) private HandMeshTracker handMeshTracker; -#endif - private Mesh neutralPoseMesh; private bool initializedUVs = false; +#endif private XRMeshSubsystem meshSubsystem = null; private readonly List meshInfos = new List(); @@ -40,11 +39,6 @@ protected override void OnEnable() handRenderer.enabled = false; - if (neutralPoseMesh == null) - { - neutralPoseMesh = new Mesh(); - } - #if UNITY_OPENXR_PRESENT if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) { @@ -65,6 +59,11 @@ protected override void OnEnable() #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) 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(); + } #endif } else From 29bae3b9b5d5a4a0aff5f96ba8c2cf204bafeb84 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 28 Mar 2025 14:02:32 -0700 Subject: [PATCH 18/50] Ensure the bounds are recalculated Otherwise, they'll stay vaguely at the origin on some platforms, which causes Unity to clip them as you look away from the origin Update PlatformHandMeshVisualizer.cs --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 933c81ec2..9230252f0 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -95,11 +95,11 @@ protected void Update() if (meshInfo.ChangeState == MeshChangeState.Added || meshInfo.ChangeState == MeshChangeState.Updated) { - handRenderer.enabled = true; - meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, null, MeshVertexAttributes.Normals, result => { }); + handRenderer.enabled = true; + // This hand mesh is provided pre-translated from the world origin, // so we want to ensure the mesh is "centered" at the world origin transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); @@ -110,6 +110,11 @@ protected void Update() && 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)) From 74e4e80e49ed8158a4f10dedc369b549e6d7d1d7 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Apr 2025 16:48:39 -0700 Subject: [PATCH 19/50] Ensure we transform from the playspace pose --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 9230252f0..8f0620748 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -2,6 +2,7 @@ // Licensed under the BSD 3-Clause using System.Collections.Generic; +using Unity.XR.CoreUtils; using UnityEngine; using UnityEngine.XR; @@ -102,7 +103,8 @@ protected void Update() // This hand mesh is provided pre-translated from the world origin, // so we want to ensure the mesh is "centered" at the world origin - transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); + PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); + transform.SetPositionAndRotation(position, rotation); } } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) @@ -123,7 +125,7 @@ protected void Update() initializedUVs = true; } - transform.SetPositionAndRotation(pose.position, pose.rotation); + transform.SetWorldPose(PlayspaceUtilities.TransformPose(pose)); } #endif else From d7b9035332843eed47ef9ec7758d547c26232179 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 11 Apr 2025 09:25:43 -0700 Subject: [PATCH 20/50] Update CHANGELOG.md --- org.mixedrealitytoolkit.input/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) 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 From 60351ddde83fe931edfca4ed41f230c8da61e7ee Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 14 Apr 2025 15:33:16 -0700 Subject: [PATCH 21/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 8f0620748..ecbeefdb7 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -148,7 +148,45 @@ protected override bool ShouldRenderHand() } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - private Vector2[] InitializeUVs(Vector3[] neutralPoseVertices) + private static Color32[] InitializeColors(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); + + Color32[] colors = new Color32[neutralPoseVertices.Length]; + + for (int i = 0; i < neutralPoseVertices.Length; i++) + { + Debug.Log($"{neutralPoseVertices[i].y} | {neutralPoseVertices[i].y * scale} | {Mathf.Clamp01(((neutralPoseVertices[i].y * scale) - 0.1f) * 10f)}"); + //colors[i] = new Color32(164, 25, 28, 255); + colors[i] = Color32.Lerp(Color.black, new Color32(164, 25, 28, 255), Mathf.Clamp01(((neutralPoseVertices[i].y * scale) - 0.1f) * 10f)); + } + + return colors; + } + { if (neutralPoseVertices?.Length == 0) { From 5351b699f8621aa10d0e36b012843cb64d84e782 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 14 Apr 2025 15:33:56 -0700 Subject: [PATCH 22/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index ecbeefdb7..0e92309b6 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -148,45 +148,7 @@ protected override bool ShouldRenderHand() } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - private static Color32[] InitializeColors(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); - - Color32[] colors = new Color32[neutralPoseVertices.Length]; - - for (int i = 0; i < neutralPoseVertices.Length; i++) - { - Debug.Log($"{neutralPoseVertices[i].y} | {neutralPoseVertices[i].y * scale} | {Mathf.Clamp01(((neutralPoseVertices[i].y * scale) - 0.1f) * 10f)}"); - //colors[i] = new Color32(164, 25, 28, 255); - colors[i] = Color32.Lerp(Color.black, new Color32(164, 25, 28, 255), Mathf.Clamp01(((neutralPoseVertices[i].y * scale) - 0.1f) * 10f)); - } - - return colors; - } - + private static Vector2[] InitializeUVs(Vector3[] neutralPoseVertices) { if (neutralPoseVertices?.Length == 0) { From c215e1d8ddf34656463b259a0b4982c838e0def6 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 14 Apr 2025 15:35:30 -0700 Subject: [PATCH 23/50] Update shader to use wrist position for fade, if given --- .../Visualizers/HandMeshVisualizer.cs | 2 -- .../PlatformHandMeshVisualizer.cs | 16 ++++++++++++++++ .../TransparentOutlinedHand-InvertedShell.shader | 13 ++++++++++--- .../TransparentOutlinedHand-Main.shader | 10 ++++++++-- 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index 2b0890235..1e4bda78d 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -110,8 +110,6 @@ protected virtual void OnEnable() $"HandVisualizer has an invalid XRNode ({handNode})!"); } - - /// /// A Unity event function that is called when the script component has been disabled. /// diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 0e92309b6..aa7f2e11a 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -33,12 +33,16 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private XRMeshSubsystem meshSubsystem = null; private readonly List meshInfos = new List(); + // 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; + propertyBlock ??= new MaterialPropertyBlock(); #if UNITY_OPENXR_PRESENT if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) @@ -147,6 +151,18 @@ protected override bool ShouldRenderHand() return meshFilter != null && handRenderer != null && base.ShouldRenderHand(); } + protected override void UpdateHandMaterial() + { + base.UpdateHandMaterial(); + + if (XRSubsystemHelpers.HandsAggregator?.TryGetJoint(TrackedHandJoint.Wrist, HandNode, out HandJointPose pose) ?? false) + { + HandRenderer.GetPropertyBlock(propertyBlock); + propertyBlock.SetVector("_WristPosition", pose.Position); + HandRenderer.SetPropertyBlock(propertyBlock); + } + } + #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) private static Vector2[] InitializeUVs(Vector3[] neutralPoseVertices) { diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader index 2e80117a6..cc99d5728 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader @@ -16,6 +16,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { _OutlineThickness ("_OutlineThickness", Range(0.0,0.00003)) = 0.000012 _HandThickness ("_HandThickness", Range(-0.0001,0.0001)) = 0.0 [PerRendererData]_PinchAmount ("Pinch Amount", Float) = 0 + [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) } SubShader { @@ -48,6 +49,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 +63,19 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + float4 objectPos = v.vertex + 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 _WristPosition; fixed4 frag(v2f i) : SV_Target { @@ -81,7 +86,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 * + ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * clamp((distance(i.worldPos, _WristPosition) - 0.025) * 50, 0, 1)))); } ENDCG diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader index 401bcd452..f01a64695 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader @@ -15,6 +15,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { _HandThickness ("_HandThickness", Range(-0.0001,0.0001)) = 0.0 _IlluminationExponent ("Illumination Exponent", Range(0,10)) = 1 _IlluminationAmount ("Illumination Amount", Range(0,10)) = 1 + [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) } SubShader { @@ -46,6 +47,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 +60,11 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + float4 objectPos = v.vertex + 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 +80,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { uniform float _IlluminationAmount; uniform float _IlluminationExponent; uniform float4 _HandColor; + uniform float4 _WristPosition; fixed4 frag(v2f i) : SV_Target { @@ -84,7 +89,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, + ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * clamp((distance(i.worldPos, _WristPosition) - 0.025) * 50, 0, 1)))); } ENDCG From fe6cf522ac9c41ede5dcf988129f78d8fd0efac3 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 17 Apr 2025 16:59:21 -0700 Subject: [PATCH 24/50] Iterate back to sphere --- .../RiggedHandVisualizer/RiggedHand-InvertedShell.mat | 5 +++++ .../Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat | 5 +++++ .../TransparentOutlinedHand-InvertedShell.shader | 8 +++++--- .../TransparentOutlinedHand-Main.shader | 6 ++++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat index 83dab5e11..f127a28a8 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,7 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 + - _FadeSphereRadius: 0.025 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 - _HandThickness: 0 @@ -50,4 +54,5 @@ Material: - _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} + - _WristPosition: {r: 0, g: 0, b: 0, a: 1} m_BuildTextureStacks: [] diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat index 60925901f..3c9c56cd3 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,7 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 + - _FadeSphereRadius: 0.025 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 - _HandThickness: 0 @@ -50,4 +54,5 @@ Material: - _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} + - _WristPosition: {r: 0, g: 0, b: 0, a: 1} m_BuildTextureStacks: [] diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader index cc99d5728..648a764dd 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader @@ -13,8 +13,9 @@ 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.00003)) = 0.000012 + _HandThickness ("Hand Thickness", Range(-0.0001,0.0001)) = 0.0 + _FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.025 [PerRendererData]_PinchAmount ("Pinch Amount", Float) = 0 [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) } @@ -75,6 +76,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { uniform float4 _OutlineColor; uniform float4 _OutlineColorPinching; uniform float _PinchAmount; + uniform float _FadeSphereRadius; uniform float4 _WristPosition; fixed4 frag(v2f i) : SV_Target @@ -88,7 +90,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { // 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 * - ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * clamp((distance(i.worldPos, _WristPosition) - 0.025) * 50, 0, 1)))); + ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * smoothstep(0.0, _FadeSphereRadius, distance(i.worldPos, _WristPosition) - _FadeSphereRadius)))); } ENDCG diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader index f01a64695..0d814b6c3 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader @@ -12,9 +12,10 @@ 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.0001,0.0001)) = 0.0 _IlluminationExponent ("Illumination Exponent", Range(0,10)) = 1 _IlluminationAmount ("Illumination Amount", Range(0,10)) = 1 + _FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.025 [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) } @@ -80,6 +81,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { uniform float _IlluminationAmount; uniform float _IlluminationExponent; uniform float4 _HandColor; + uniform float _FadeSphereRadius; uniform float4 _WristPosition; fixed4 frag(v2f i) : SV_Target @@ -90,7 +92,7 @@ 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, - ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * clamp((distance(i.worldPos, _WristPosition) - 0.025) * 50, 0, 1)))); + ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * smoothstep(0.0, _FadeSphereRadius, distance(i.worldPos, _WristPosition) - _FadeSphereRadius)))); } ENDCG From dbb4b3053d93cc98e5a9c76230315d7711ad7730 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 18 Apr 2025 12:23:35 -0700 Subject: [PATCH 25/50] Iterate shaders again Update RiggedHand-Main.mat --- .../RiggedHandVisualizer/RiggedHand-InvertedShell.mat | 2 +- .../Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat | 2 +- .../TransparentOutlinedHand-InvertedShell.shader | 8 ++++---- .../TransparentOutlinedHand-Main.shader | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat index f127a28a8..8d8060c62 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat @@ -51,8 +51,8 @@ Material: m_Colors: - _Center_: {r: 0.5, g: 0.5, b: 0, a: 0} - _Color_: {r: 1, g: 1, b: 1, a: 1} + - _FadeSpherePosition: {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} - - _WristPosition: {r: 0, g: 0, b: 0, a: 1} m_BuildTextureStacks: [] diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat index 3c9c56cd3..7bd9d9bc7 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat @@ -51,8 +51,8 @@ Material: m_Colors: - _Center_: {r: 0.5, g: 0.5, b: 0, a: 0} - _Color_: {r: 1, g: 1, b: 1, a: 1} + - _FadeSpherePosition: {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} - - _WristPosition: {r: 0, g: 0, b: 0, a: 1} m_BuildTextureStacks: [] diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader index 648a764dd..3c2593263 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader @@ -15,9 +15,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { _OutlineColorPinching ("Outline Color (Pinching)", Color) = (1,1,1,1) _OutlineThickness ("Outline Thickness", Range(0.0,0.00003)) = 0.000012 _HandThickness ("Hand Thickness", Range(-0.0001,0.0001)) = 0.0 - _FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.025 [PerRendererData]_PinchAmount ("Pinch Amount", Float) = 0 - [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) + [PerRendererData]_FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.05 + [PerRendererData]_FadeSpherePosition ("Fade Sphere Position", Vector) = (0,0,0,1) } SubShader { @@ -77,7 +77,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { uniform float4 _OutlineColorPinching; uniform float _PinchAmount; uniform float _FadeSphereRadius; - uniform float4 _WristPosition; + uniform float4 _FadeSpherePosition; fixed4 frag(v2f i) : SV_Target { @@ -90,7 +90,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { // 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 * - ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * smoothstep(0.0, _FadeSphereRadius, distance(i.worldPos, _WristPosition) - _FadeSphereRadius)))); + ((_FadeSpherePosition.w * i.color.r) + ((1 - _FadeSpherePosition.w) * smoothstep(0.5 * _FadeSphereRadius, _FadeSphereRadius, distance(i.worldPos, _FadeSpherePosition))))); } ENDCG diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader index 0d814b6c3..fbc6aa25a 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader @@ -15,8 +15,8 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { _HandThickness ("Hand Thickness", Range(-0.0001,0.0001)) = 0.0 _IlluminationExponent ("Illumination Exponent", Range(0,10)) = 1 _IlluminationAmount ("Illumination Amount", Range(0,10)) = 1 - _FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.025 - [PerRendererData]_WristPosition ("Wrist Position", Vector) = (0,0,0,1) + [PerRendererData]_FadeSphereRadius ("Fade Sphere Radius", Range(0,1)) = 0.05 + [PerRendererData]_FadeSpherePosition ("Fade Sphere Position", Vector) = (0,0,0,1) } SubShader { @@ -82,7 +82,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { uniform float _IlluminationExponent; uniform float4 _HandColor; uniform float _FadeSphereRadius; - uniform float4 _WristPosition; + uniform float4 _FadeSpherePosition; fixed4 frag(v2f i) : SV_Target { @@ -92,7 +92,7 @@ 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, - ((_WristPosition.w * i.color.r) + ((1 - _WristPosition.w) * smoothstep(0.0, _FadeSphereRadius, distance(i.worldPos, _WristPosition) - _FadeSphereRadius)))); + ((_FadeSpherePosition.w * i.color.r) + ((1 - _FadeSpherePosition.w) * smoothstep(_FadeSphereRadius, _FadeSphereRadius + 0.025, distance(i.worldPos, _FadeSpherePosition))))); } ENDCG From 6828cdfc40e8ea2d4ee626240546181c538019a7 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 22 Apr 2025 12:28:46 -0700 Subject: [PATCH 26/50] Iterate shaders again --- .../PlatformHandMeshVisualizer.cs | 12 ++++++++++-- .../Visualizers/Prefabs/openxr_right_hand.prefab | 1 + .../RiggedHand-InvertedShell.mat | 3 ++- .../RiggedHandVisualizer/RiggedHand-Main.mat | 3 ++- .../TransparentOutlinedHand-InvertedShell.shader | 8 +++++--- .../TransparentOutlinedHand-Main.shader | 8 +++++--- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index aa7f2e11a..afafdff6d 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -21,6 +21,9 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer [SerializeField] private MeshRenderer handRenderer; + [SerializeField, Range(0, 1)] + private float fadeDistance = 0.025f; + /// protected override Renderer HandRenderer => handRenderer; @@ -155,10 +158,15 @@ protected override void UpdateHandMaterial() { base.UpdateHandMaterial(); - if (XRSubsystemHelpers.HandsAggregator?.TryGetJoint(TrackedHandJoint.Wrist, HandNode, out HandJointPose pose) ?? false) + 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); - propertyBlock.SetVector("_WristPosition", pose.Position); + 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); } } diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab index ed34ea8aa..89cef9b03 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab +++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab @@ -822,6 +822,7 @@ MonoBehaviour: m_ManualQueueTargetFrame: 0 meshFilter: {fileID: 3936565761691908697} handRenderer: {fileID: 769216864695519206} + fadeDistance: 0.025 --- !u!1 &5839269735351241340 GameObject: m_ObjectHideFlags: 0 diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat index 8d8060c62..400b1e037 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat @@ -33,6 +33,7 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 + - _FadeDistance: 0.015 - _FadeSphereRadius: 0.025 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 @@ -51,7 +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} - - _FadeSpherePosition: {r: 0, g: 0, b: 0, 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 7bd9d9bc7..d03226e62 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat @@ -33,6 +33,7 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 + - _FadeDistance: 0.015 - _FadeSphereRadius: 0.025 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 @@ -51,7 +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} - - _FadeSpherePosition: {r: 0, g: 0, b: 0, 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/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader index 3c2593263..70bc8f8da 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader @@ -16,8 +16,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { _OutlineThickness ("Outline Thickness", Range(0.0,0.00003)) = 0.000012 _HandThickness ("Hand Thickness", Range(-0.0001,0.0001)) = 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]_FadeSpherePosition ("Fade Sphere Position", Vector) = (0,0,0,1) + [PerRendererData]_FadeDistance ("Fade Distance", Range(0,1)) = 0.025 } SubShader { @@ -76,8 +77,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { uniform float4 _OutlineColor; uniform float4 _OutlineColorPinching; uniform float _PinchAmount; + uniform float4 _FadeSphereCenter; uniform float _FadeSphereRadius; - uniform float4 _FadeSpherePosition; + uniform float _FadeDistance; fixed4 frag(v2f i) : SV_Target { @@ -90,7 +92,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { // 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 * - ((_FadeSpherePosition.w * i.color.r) + ((1 - _FadeSpherePosition.w) * smoothstep(0.5 * _FadeSphereRadius, _FadeSphereRadius, distance(i.worldPos, _FadeSpherePosition))))); + ((_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 fbc6aa25a..8a508ce50 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader @@ -15,8 +15,9 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { _HandThickness ("Hand Thickness", Range(-0.0001,0.0001)) = 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]_FadeSpherePosition ("Fade Sphere Position", Vector) = (0,0,0,1) + [PerRendererData]_FadeDistance ("Fade Distance", Range(0,1)) = 0.025 } SubShader { @@ -81,8 +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 float4 _FadeSpherePosition; + uniform float _FadeDistance; fixed4 frag(v2f i) : SV_Target { @@ -92,7 +94,7 @@ 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, - ((_FadeSpherePosition.w * i.color.r) + ((1 - _FadeSpherePosition.w) * smoothstep(_FadeSphereRadius, _FadeSphereRadius + 0.025, distance(i.worldPos, _FadeSpherePosition))))); + ((_FadeSphereCenter.w * i.color.r) + ((1 - _FadeSphereCenter.w) * smoothstep(_FadeSphereRadius, _FadeSphereRadius + _FadeDistance, distance(i.worldPos, _FadeSphereCenter))))); } ENDCG From 0f2a116fcb16541c6ffccbc510d65646224ac324 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 22 Apr 2025 16:53:53 -0700 Subject: [PATCH 27/50] Update to include outline --- .../RiggedHandVisualizer/RiggedHand-InvertedShell.mat | 2 +- .../TransparentOutlinedHand-InvertedShell.shader | 6 +++--- .../TransparentOutlinedHand-Main.shader | 4 ++-- .../TransparentOutlinedHand-PrepassZ.shader | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat index 400b1e037..8d0ddf00f 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat @@ -44,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 diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader index 70bc8f8da..4ccc98705 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-InvertedShell.shader @@ -13,8 +13,8 @@ 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 ("Outline Thickness", Range(0.0,0.00003)) = 0.000012 - _HandThickness ("Hand Thickness", 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 @@ -65,7 +65,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Inverted Shell)" { UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); - float4 objectPos = v.vertex + v.normal * (_HandThickness + _OutlineThickness); + float4 objectPos = v.vertex + normalize(v.normal) * (_HandThickness + _OutlineThickness); o.normal = UnityObjectToWorldNormal(v.normal); o.vertex = UnityObjectToClipPos(objectPos); o.color = v.color; diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader index 8a508ce50..f50ccd33b 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/TransparentOutlinedHand-Main.shader @@ -12,7 +12,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { Properties { _HandColor ("Hand Color", Color) = (1,1,1,1) - _HandThickness ("Hand Thickness", 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) @@ -62,7 +62,7 @@ Shader "Mixed Reality Toolkit/Transparent Outlined Hand (Main)" { UNITY_INITIALIZE_OUTPUT(v2f, o); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); - float4 objectPos = v.vertex + v.normal * _HandThickness; + float4 objectPos = v.vertex + normalize(v.normal) * _HandThickness; o.normal = UnityObjectToWorldNormal(v.normal); o.vertex = UnityObjectToClipPos(objectPos); o.color = v.color; 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; } From 1d59fb7f44fb51204515f4a4660c1ec175c31380 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 22 Apr 2025 20:06:17 -0700 Subject: [PATCH 28/50] Update material defaults --- .../RiggedHandVisualizer/RiggedHand-InvertedShell.mat | 4 ++-- .../Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat index 8d0ddf00f..b61238583 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-InvertedShell.mat @@ -33,8 +33,8 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 - - _FadeDistance: 0.015 - - _FadeSphereRadius: 0.025 + - _FadeDistance: 0.025 + - _FadeSphereRadius: 0.05 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 - _HandThickness: 0 diff --git a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat index d03226e62..933443742 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat +++ b/org.mixedrealitytoolkit.input/Visualizers/RiggedHandVisualizer/RiggedHand-Main.mat @@ -33,8 +33,8 @@ Material: - _Displacement_: 0 - _Enable_: 1 - _Exponent_: 2.16 - - _FadeDistance: 0.015 - - _FadeSphereRadius: 0.025 + - _FadeDistance: 0.025 + - _FadeSphereRadius: 0.05 - _Fade_In_End_: 0 - _Fade_In_Start_: 0 - _HandThickness: 0 From 1363a88b630e619f19bb80769d8e0e6a103091cb Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 6 May 2025 15:04:10 -0700 Subject: [PATCH 29/50] Iterate to using Unity's API instead of Google's Update PlatformHandMeshVisualizer.cs --- .../Packages/packages-lock.json | 5 +- .../PlatformHandMeshVisualizer.cs | 70 +++++++++---------- org.mixedrealitytoolkit.input/package.json | 2 +- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json index f282671d1..c0eff87aa 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-pre.2", "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-pre.2", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index afafdff6d..622d8f57e 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,7 +4,11 @@ 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; @@ -33,8 +37,11 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private bool initializedUVs = false; #endif - private XRMeshSubsystem meshSubsystem = null; - private readonly List meshInfos = new List(); + private XRHandSubsystem handSubsystem = null; + private XRHandMeshDataQueryParams queryParams = new() + { + allocator = Unity.Collections.Allocator.Persistent, + }; // The property block used to modify the wrist position property on the material private MaterialPropertyBlock propertyBlock = null; @@ -47,24 +54,20 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); -#if UNITY_OPENXR_PRESENT - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) + List subsystems = XRSubsystemHelpers.GetAllSubsystems(); + foreach (XRHandSubsystem subsystem in subsystems) { - List meshSubsystems = new List(); - SubsystemManager.GetSubsystems(meshSubsystems); - foreach (XRMeshSubsystem subsystem in meshSubsystems) + if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) { - if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider") - { - Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); - meshSubsystem = subsystem; - break; - } + Debug.Log($"Using {nameof(provider.handMeshDataSupplier)} for {HandNode} visualization."); + handSubsystem = subsystem; + return; } } - else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) - { + #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; @@ -72,13 +75,12 @@ protected override void OnEnable() { neutralPoseMesh = new Mesh(); } -#endif + + return; } - else #endif - { - enabled = false; - } + + enabled = false; } protected void Update() @@ -93,26 +95,24 @@ protected void Update() return; } - if (meshSubsystem != null - && meshSubsystem.running - && meshSubsystem.TryGetMeshInfos(meshInfos)) + if (handSubsystem != null + && handSubsystem.running + && handSubsystem.TryGetMeshData(out XRHandMeshDataQueryResult result, ref queryParams)) { - int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; + XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - MeshInfo meshInfo = meshInfos[handMeshIndex]; - if (meshInfo.ChangeState == MeshChangeState.Added - || meshInfo.ChangeState == MeshChangeState.Updated) - { - meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, - null, MeshVertexAttributes.Normals, result => { }); + meshFilter.mesh.SetVertices(handMeshData.positions); + meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); + meshFilter.mesh.SetUVs(0, handMeshData.uvs); - handRenderer.enabled = true; + handRenderer.enabled = true; - // This hand mesh is provided pre-translated from the world origin, - // so we want to ensure the mesh is "centered" at the world origin - PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); - transform.SetPositionAndRotation(position, rotation); + if (!handMeshData.TryGetRootPose(out Pose rootPose)) + { + rootPose = Pose.identity; } + + transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) else if (handMeshTracker != null diff --git a/org.mixedrealitytoolkit.input/package.json b/org.mixedrealitytoolkit.input/package.json index a8d509f42..9f15bd5b0 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-pre.2", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } From bb2a6c7a9b87de3043a6b4d078a17b1c470255d2 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 6 May 2025 16:35:29 -0700 Subject: [PATCH 30/50] Update AndroidXRConfig.cs --- .../Assets/Scripts/Editor/AndroidXRConfig.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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; } From 917bd5b2a2104d6f8efab449ec3319ace0789d40 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 6 May 2025 16:50:18 -0700 Subject: [PATCH 31/50] Update HandMeshVisualizer.cs --- .../Visualizers/HandMeshVisualizer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index 1e4bda78d..45f73fbfd 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -21,16 +21,19 @@ public abstract class HandMeshVisualizer : MonoBehaviour, ISelectInputVisualizer [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.")] + "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. + /// 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; From 302723405ed0ab8514abdc47e225c19f2819e0e3 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 8 May 2025 09:52:38 -0700 Subject: [PATCH 32/50] Don't update more than once per frame --- .../PlatformHandMeshVisualizer.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 622d8f57e..a2f7bf392 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -37,10 +37,13 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private bool initializedUVs = false; #endif - private XRHandSubsystem handSubsystem = null; - private XRHandMeshDataQueryParams queryParams = new() + // 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 static XRHandMeshDataQueryParams queryParams = new() { - allocator = Unity.Collections.Allocator.Persistent, + allocator = Unity.Collections.Allocator.Temp, }; // The property block used to modify the wrist position property on the material @@ -54,12 +57,17 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); + if (handSubsystem != null) + { + return; + } + List subsystems = XRSubsystemHelpers.GetAllSubsystems(); foreach (XRHandSubsystem subsystem in subsystems) { if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) { - Debug.Log($"Using {nameof(provider.handMeshDataSupplier)} for {HandNode} visualization."); + Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); handSubsystem = subsystem; return; } @@ -97,8 +105,9 @@ protected void Update() if (handSubsystem != null && handSubsystem.running - && handSubsystem.TryGetMeshData(out XRHandMeshDataQueryResult result, ref queryParams)) + && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { + lastUpdatedFrame = Time.frameCount; XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; meshFilter.mesh.SetVertices(handMeshData.positions); From f1ad83e7182d4a7e8f94d9a94f6ee68a49d61821 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 8 May 2025 10:36:45 -0700 Subject: [PATCH 33/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index a2f7bf392..c1b29a3c8 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -110,9 +110,10 @@ protected void Update() lastUpdatedFrame = Time.frameCount; XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; + meshFilter.mesh.Clear(); meshFilter.mesh.SetVertices(handMeshData.positions); - meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); meshFilter.mesh.SetUVs(0, handMeshData.uvs); + meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); handRenderer.enabled = true; From e8f21450084b5c1a3e5e9bbfefaf8ae1f2d13749 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 8 May 2025 10:37:35 -0700 Subject: [PATCH 34/50] Revert "Iterate to using Unity's API instead of Google's" This reverts commit 71c1f408f7a03ffe8f96b8b8891594df6b8e7bc0. This reverts commit d53c546b7dee1bf76d0c8bd1ac815f8aacd5edac. This reverts commit 62d178160d6e7ccc2f28a4211180ac20f25605aa. This reverts commit c196e265e95110dada8bd6dd018472b14c419a95. --- .../Assets/Scripts/Editor/AndroidXRConfig.cs | 6 +- .../Packages/packages-lock.json | 5 +- .../PlatformHandMeshVisualizer.cs | 80 ++++++++----------- org.mixedrealitytoolkit.input/package.json | 2 +- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs b/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs index 75e222a44..81bc871f5 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 the Unity OpenXR Android XR package..."); - request = Client.AddAndRemove(new[] { "com.unity.xr.androidxr-openxr" }); + 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" }); 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 c0eff87aa..f282671d1 100644 --- a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json +++ b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json @@ -264,13 +264,12 @@ "url": "https://packages.unity.com" }, "com.unity.xr.hands": { - "version": "1.6.0-pre.2", + "version": "1.3.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" }, @@ -390,7 +389,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.6.0-pre.2", + "com.unity.xr.hands": "1.3.0", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c1b29a3c8..afafdff6d 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,11 +4,7 @@ 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; @@ -37,14 +33,8 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer 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 static XRHandMeshDataQueryParams queryParams = new() - { - allocator = Unity.Collections.Allocator.Temp, - }; + private XRMeshSubsystem meshSubsystem = null; + private readonly List meshInfos = new List(); // The property block used to modify the wrist position property on the material private MaterialPropertyBlock propertyBlock = null; @@ -57,25 +47,24 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); - if (handSubsystem != null) - { - return; - } - - List subsystems = XRSubsystemHelpers.GetAllSubsystems(); - foreach (XRHandSubsystem subsystem in subsystems) +#if UNITY_OPENXR_PRESENT + if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) { - if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) + List meshSubsystems = new List(); + SubsystemManager.GetSubsystems(meshSubsystems); + foreach (XRMeshSubsystem subsystem in meshSubsystems) { - Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); - handSubsystem = subsystem; - return; + if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider") + { + Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); + meshSubsystem = subsystem; + break; + } } } - -#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) + else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) { +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; @@ -83,12 +72,13 @@ protected override void OnEnable() { neutralPoseMesh = new Mesh(); } - - return; +#endif } + else #endif - - enabled = false; + { + enabled = false; + } } protected void Update() @@ -103,26 +93,26 @@ protected void Update() return; } - if (handSubsystem != null - && handSubsystem.running - && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) + if (meshSubsystem != null + && meshSubsystem.running + && meshSubsystem.TryGetMeshInfos(meshInfos)) { - lastUpdatedFrame = Time.frameCount; - XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; + int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; - meshFilter.mesh.Clear(); - meshFilter.mesh.SetVertices(handMeshData.positions); - meshFilter.mesh.SetUVs(0, handMeshData.uvs); - meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); + MeshInfo meshInfo = meshInfos[handMeshIndex]; + if (meshInfo.ChangeState == MeshChangeState.Added + || meshInfo.ChangeState == MeshChangeState.Updated) + { + meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, + null, MeshVertexAttributes.Normals, result => { }); - handRenderer.enabled = true; + handRenderer.enabled = true; - if (!handMeshData.TryGetRootPose(out Pose rootPose)) - { - rootPose = Pose.identity; + // This hand mesh is provided pre-translated from the world origin, + // so we want to ensure the mesh is "centered" at the world origin + PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); + transform.SetPositionAndRotation(position, rotation); } - - transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) else if (handMeshTracker != null diff --git a/org.mixedrealitytoolkit.input/package.json b/org.mixedrealitytoolkit.input/package.json index 9f15bd5b0..a8d509f42 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.6.0-pre.2", + "com.unity.xr.hands": "1.3.0", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } From 1041792bba9fd92d1b73a8c201c67e5de1ac9bbb Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 8 May 2025 11:24:42 -0700 Subject: [PATCH 35/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index afafdff6d..952a9f849 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -54,7 +54,8 @@ protected override void OnEnable() SubsystemManager.GetSubsystems(meshSubsystems); foreach (XRMeshSubsystem subsystem in meshSubsystems) { - if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider") + if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider" + || subsystem.subsystemDescriptor.id == "AndroidXRMeshProvider") { Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); meshSubsystem = subsystem; @@ -77,6 +78,7 @@ protected override void OnEnable() else #endif { + Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); enabled = false; } } From c2656c4ba36c5e92bda01cacb23a505658cb5d2f Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 27 May 2025 14:51:25 -0700 Subject: [PATCH 36/50] Use input action references for tracked state --- .../Visualizers/Prefabs/openxr_left_hand.prefab | 4 ++++ .../Visualizers/Prefabs/openxr_right_hand.prefab | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab index eeb1b0c4d..2219e6279 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab +++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_left_hand.prefab @@ -48,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 diff --git a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab index 89cef9b03..f9a9b68b3 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab +++ b/org.mixedrealitytoolkit.input/Visualizers/Prefabs/openxr_right_hand.prefab @@ -606,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 @@ -624,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 From 3dc092a6048b2acecda2691e56295d8ec9680fc9 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 12:14:45 -0700 Subject: [PATCH 37/50] Reapply "Iterate to using Unity's API instead of Google's" This reverts commit d5e8004d848f7a7dba9c675b0716e5fc3d9a23b0. --- .../Assets/Scripts/Editor/AndroidXRConfig.cs | 6 +- .../Packages/packages-lock.json | 5 +- .../PlatformHandMeshVisualizer.cs | 83 ++++++++++--------- org.mixedrealitytoolkit.input/package.json | 2 +- 4 files changed, 53 insertions(+), 43 deletions(-) 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..c0eff87aa 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-pre.2", "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-pre.2", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 952a9f849..e30aaa090 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,7 +4,11 @@ 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; @@ -33,8 +37,14 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private bool initializedUVs = false; #endif - private XRMeshSubsystem meshSubsystem = null; - private readonly List meshInfos = new List(); + // 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 static XRHandMeshDataQueryParams queryParams = new() + { + allocator = Unity.Collections.Allocator.Temp, + }; // The property block used to modify the wrist position property on the material private MaterialPropertyBlock propertyBlock = null; @@ -47,25 +57,25 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); -#if UNITY_OPENXR_PRESENT - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) + if (handSubsystem != null) + { + return; + } + + List subsystems = XRSubsystemHelpers.GetAllSubsystems(); + foreach (XRHandSubsystem subsystem in subsystems) { - List meshSubsystems = new List(); - SubsystemManager.GetSubsystems(meshSubsystems); - foreach (XRMeshSubsystem subsystem in meshSubsystems) + if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) { - if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider" - || subsystem.subsystemDescriptor.id == "AndroidXRMeshProvider") - { - Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); - meshSubsystem = subsystem; - break; - } + Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); + handSubsystem = subsystem; + return; } } - else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) - { + #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; @@ -73,14 +83,13 @@ protected override void OnEnable() { neutralPoseMesh = new Mesh(); } -#endif + + return; } - else #endif - { - Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); - enabled = false; - } + + Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); + enabled = false; } protected void Update() @@ -95,26 +104,26 @@ protected void Update() return; } - if (meshSubsystem != null - && meshSubsystem.running - && meshSubsystem.TryGetMeshInfos(meshInfos)) + if (handSubsystem != null + && handSubsystem.running + && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { - int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; + lastUpdatedFrame = Time.frameCount; + XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - MeshInfo meshInfo = meshInfos[handMeshIndex]; - if (meshInfo.ChangeState == MeshChangeState.Added - || meshInfo.ChangeState == MeshChangeState.Updated) - { - meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, - null, MeshVertexAttributes.Normals, result => { }); + meshFilter.mesh.Clear(); + meshFilter.mesh.SetVertices(handMeshData.positions); + meshFilter.mesh.SetUVs(0, handMeshData.uvs); + meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); - handRenderer.enabled = true; + handRenderer.enabled = true; - // This hand mesh is provided pre-translated from the world origin, - // so we want to ensure the mesh is "centered" at the world origin - PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); - transform.SetPositionAndRotation(position, rotation); + if (!handMeshData.TryGetRootPose(out Pose rootPose)) + { + rootPose = Pose.identity; } + + transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) else if (handMeshTracker != null diff --git a/org.mixedrealitytoolkit.input/package.json b/org.mixedrealitytoolkit.input/package.json index a8d509f42..9f15bd5b0 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-pre.2", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } From 321fd1171bcc5dcae74c0882a8b113383da58491 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 12:15:50 -0700 Subject: [PATCH 38/50] Update Hands version --- UnityProjects/MRTKDevTemplate/Packages/packages-lock.json | 4 ++-- org.mixedrealitytoolkit.input/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json index c0eff87aa..18fbf5893 100644 --- a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json +++ b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json @@ -264,7 +264,7 @@ "url": "https://packages.unity.com" }, "com.unity.xr.hands": { - "version": "1.6.0-pre.2", + "version": "1.6.0", "depth": 1, "source": "registry", "dependencies": { @@ -390,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.6.0-pre.2", + "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/package.json b/org.mixedrealitytoolkit.input/package.json index 9f15bd5b0..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.6.0-pre.2", + "com.unity.xr.hands": "1.6.0", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } From 0bcedc82fc8875190ccebd2665feb07a3d0b144a Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 13:32:55 -0700 Subject: [PATCH 39/50] Some optimizations and improvements --- .../PlatformHandMeshVisualizer.cs | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index e30aaa090..c5c9639cd 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -45,6 +45,7 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer { allocator = Unity.Collections.Allocator.Temp, }; + private XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags; // The property block used to modify the wrist position property on the material private MaterialPropertyBlock propertyBlock = null; @@ -69,6 +70,15 @@ protected override void OnEnable() { Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); handSubsystem = subsystem; + + 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(); + return; } } @@ -79,6 +89,10 @@ protected override void OnEnable() Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; + // Since the hand mesh is likely to change every frame, we + // "optimize mesh for frequent updates" by marking it dynamic + meshFilter.mesh.MarkDynamic(); + if (neutralPoseMesh == null) { neutralPoseMesh = new Mesh(); @@ -106,15 +120,22 @@ protected void Update() if (handSubsystem != null && handSubsystem.running + && (handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { lastUpdatedFrame = Time.frameCount; XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - meshFilter.mesh.Clear(); - meshFilter.mesh.SetVertices(handMeshData.positions); - meshFilter.mesh.SetUVs(0, handMeshData.uvs); - meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); + if (handMeshData.positions.Length > 0 && handMeshData.indices.Length > 0) + { + meshFilter.mesh.SetVertices(handMeshData.positions); + meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); + } + + if (handMeshData.uvs.IsCreated && handMeshData.uvs.Length == meshFilter.mesh.vertexCount) + { + meshFilter.mesh.SetUVs(0, handMeshData.uvs); + } handRenderer.enabled = true; From a81eddb1beddf55d492da13a38386d2354c6a450 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 16:27:40 -0700 Subject: [PATCH 40/50] Another iteration --- .../PlatformHandMeshVisualizer.cs | 76 ++++++++++++------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c5c9639cd..506ed4363 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -41,10 +41,6 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private static int lastUpdatedFrame = -1; private static XRHandSubsystem handSubsystem = null; private static XRHandMeshDataQueryResult result; - private static XRHandMeshDataQueryParams queryParams = new() - { - allocator = Unity.Collections.Allocator.Temp, - }; private XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags; // The property block used to modify the wrist position property on the material @@ -60,6 +56,14 @@ protected override void OnEnable() if (handSubsystem != null) { + 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(); + return; } @@ -118,33 +122,53 @@ protected void Update() return; } - if (handSubsystem != null - && handSubsystem.running - && (handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 - && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) + if (handSubsystem != null && handSubsystem.running) { - lastUpdatedFrame = Time.frameCount; - XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - - if (handMeshData.positions.Length > 0 && handMeshData.indices.Length > 0) + XRHandMeshDataQueryParams queryParams = new() { - meshFilter.mesh.SetVertices(handMeshData.positions); - meshFilter.mesh.SetIndices(handMeshData.indices, MeshTopology.Triangles, 0); - } + allocator = Unity.Collections.Allocator.Temp, + }; - if (handMeshData.uvs.IsCreated && handMeshData.uvs.Length == meshFilter.mesh.vertexCount) + Debug.Log($"Success?? {HandNode} | {Time.frameCount} | {handSubsystem.updateSuccessFlags} | {updateSuccessFlags} | {(handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0}"); + if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 + && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { - meshFilter.mesh.SetUVs(0, handMeshData.uvs); + lastUpdatedFrame = Time.frameCount; + XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; + handRenderer.enabled = true; + + if (handMeshData.positions.IsCreated && handMeshData.indices.IsCreated) + { + meshFilter.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]); + } + meshFilter.mesh.SetIndices(indices, MeshTopology.Triangles, 0); + meshFilter.mesh.RecalculateBounds(); + } + + if (handMeshData.uvs.IsCreated) + { + meshFilter.mesh.SetUVs(0, handMeshData.uvs); + } + + if (handMeshData.normals.IsCreated) + { + meshFilter.mesh.SetNormals(handMeshData.normals); + } + else + { + meshFilter.mesh.RecalculateNormals(); + } + + if (handMeshData.TryGetRootPose(out Pose rootPose)) + { + transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); + } } - - handRenderer.enabled = true; - - if (!handMeshData.TryGetRootPose(out Pose rootPose)) - { - rootPose = Pose.identity; - } - - transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) else if (handMeshTracker != null From cb174d268c7ed0a3477f8127b12faa34cbfb589c Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 16:27:57 -0700 Subject: [PATCH 41/50] Revert "Reapply "Iterate to using Unity's API instead of Google's"" --- .../Assets/Scripts/Editor/AndroidXRConfig.cs | 6 +- .../Packages/packages-lock.json | 5 +- .../PlatformHandMeshVisualizer.cs | 126 +++++------------- org.mixedrealitytoolkit.input/package.json | 2 +- 4 files changed, 42 insertions(+), 97 deletions(-) diff --git a/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs b/UnityProjects/MRTKDevTemplate/Assets/Scripts/Editor/AndroidXRConfig.cs index 75e222a44..81bc871f5 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 the Unity OpenXR Android XR package..."); - request = Client.AddAndRemove(new[] { "com.unity.xr.androidxr-openxr" }); + 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" }); 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 18fbf5893..f282671d1 100644 --- a/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json +++ b/UnityProjects/MRTKDevTemplate/Packages/packages-lock.json @@ -264,13 +264,12 @@ "url": "https://packages.unity.com" }, "com.unity.xr.hands": { - "version": "1.6.0", + "version": "1.3.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" }, @@ -390,7 +389,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.6.0", + "com.unity.xr.hands": "1.3.0", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 506ed4363..952a9f849 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,11 +4,7 @@ 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; @@ -37,11 +33,8 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer 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; + private XRMeshSubsystem meshSubsystem = null; + private readonly List meshInfos = new List(); // The property block used to modify the wrist position property on the material private MaterialPropertyBlock propertyBlock = null; @@ -54,60 +47,40 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); - if (handSubsystem != null) +#if UNITY_OPENXR_PRESENT + if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) { - 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(); - - return; - } - - List subsystems = XRSubsystemHelpers.GetAllSubsystems(); - foreach (XRHandSubsystem subsystem in subsystems) - { - if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) + List meshSubsystems = new List(); + SubsystemManager.GetSubsystems(meshSubsystems); + foreach (XRMeshSubsystem subsystem in meshSubsystems) { - Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); - handSubsystem = subsystem; - - 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(); - - return; + if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider" + || subsystem.subsystemDescriptor.id == "AndroidXRMeshProvider") + { + Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); + meshSubsystem = subsystem; + break; + } } } - -#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) + else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) { +#if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; - // Since the hand mesh is likely to change every frame, we - // "optimize mesh for frequent updates" by marking it dynamic - meshFilter.mesh.MarkDynamic(); - if (neutralPoseMesh == null) { neutralPoseMesh = new Mesh(); } - - return; +#endif } + else #endif - - Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); - enabled = false; + { + Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); + enabled = false; + } } protected void Update() @@ -122,52 +95,25 @@ protected void Update() return; } - if (handSubsystem != null && handSubsystem.running) + if (meshSubsystem != null + && meshSubsystem.running + && meshSubsystem.TryGetMeshInfos(meshInfos)) { - XRHandMeshDataQueryParams queryParams = new() - { - allocator = Unity.Collections.Allocator.Temp, - }; + int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; - Debug.Log($"Success?? {HandNode} | {Time.frameCount} | {handSubsystem.updateSuccessFlags} | {updateSuccessFlags} | {(handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0}"); - if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 - && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) + MeshInfo meshInfo = meshInfos[handMeshIndex]; + if (meshInfo.ChangeState == MeshChangeState.Added + || meshInfo.ChangeState == MeshChangeState.Updated) { - lastUpdatedFrame = Time.frameCount; - XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - handRenderer.enabled = true; - - if (handMeshData.positions.IsCreated && handMeshData.indices.IsCreated) - { - meshFilter.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]); - } - meshFilter.mesh.SetIndices(indices, MeshTopology.Triangles, 0); - meshFilter.mesh.RecalculateBounds(); - } - - if (handMeshData.uvs.IsCreated) - { - meshFilter.mesh.SetUVs(0, handMeshData.uvs); - } + meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, + null, MeshVertexAttributes.Normals, result => { }); - if (handMeshData.normals.IsCreated) - { - meshFilter.mesh.SetNormals(handMeshData.normals); - } - else - { - meshFilter.mesh.RecalculateNormals(); - } + handRenderer.enabled = true; - if (handMeshData.TryGetRootPose(out Pose rootPose)) - { - transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); - } + // This hand mesh is provided pre-translated from the world origin, + // so we want to ensure the mesh is "centered" at the world origin + PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); + transform.SetPositionAndRotation(position, rotation); } } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) diff --git a/org.mixedrealitytoolkit.input/package.json b/org.mixedrealitytoolkit.input/package.json index 590b61790..a8d509f42 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.6.0", + "com.unity.xr.hands": "1.3.0", "com.unity.xr.interaction.toolkit": "3.0.4", "org.mixedrealitytoolkit.core": "4.0.0" } From 0bf1f2ba0c5771119170b8316fc0663d3c0c302a Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 9 Jul 2025 16:32:31 -0700 Subject: [PATCH 42/50] Mark the hand mesh as dynamic --- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 952a9f849..6f4aabf10 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -59,6 +59,11 @@ protected override void OnEnable() { Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); meshSubsystem = subsystem; + + // Since the hand mesh is likely to change every frame, we + // "optimize mesh for frequent updates" by marking it dynamic + meshFilter.mesh.MarkDynamic(); + break; } } @@ -69,6 +74,10 @@ protected override void OnEnable() Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; + // Since the hand mesh is likely to change every frame, we + // "optimize mesh for frequent updates" by marking it dynamic + meshFilter.mesh.MarkDynamic(); + if (neutralPoseMesh == null) { neutralPoseMesh = new Mesh(); From ae8444ba57f376b9e3871985feaec2875bdcc5b6 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Thu, 31 Jul 2025 12:14:33 -0700 Subject: [PATCH 43/50] Iterate visualizers --- .../Visualizers/HandMeshVisualizer.cs | 2 +- .../PlatformHandVisualizer/PlatformHandMeshVisualizer.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs index 45f73fbfd..cf1eae3c4 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/HandMeshVisualizer.cs @@ -78,7 +78,7 @@ public XRInputButtonReader SelectInput /// /// /// - protected List buttonReaders { get; } = new List(); + private readonly List buttonReaders = new List(); /// /// Whether this visualizer currently has a loaded and visible hand mesh or not. diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 6f4aabf10..c8c5a69c9 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -50,6 +50,9 @@ protected override void OnEnable() #if UNITY_OPENXR_PRESENT if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) { + // If we already found our subsystem, just return + if (meshSubsystem != null && meshSubsystem.running) { return; } + List meshSubsystems = new List(); SubsystemManager.GetSubsystems(meshSubsystems); foreach (XRMeshSubsystem subsystem in meshSubsystems) From 0b2ca26e3402b7601427b9ab622d215375239152 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 1 Aug 2025 11:48:32 -0700 Subject: [PATCH 44/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index c8c5a69c9..870ebe1a8 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -118,14 +118,20 @@ protected void Update() || meshInfo.ChangeState == MeshChangeState.Updated) { meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, - null, MeshVertexAttributes.Normals, result => { }); - - handRenderer.enabled = true; - - // This hand mesh is provided pre-translated from the world origin, - // so we want to ensure the mesh is "centered" at the world origin - PlayspaceUtilities.XROrigin.CameraFloorOffsetObject.transform.GetPositionAndRotation(out Vector3 position, out Quaternion rotation); - transform.SetPositionAndRotation(position, rotation); + null, MeshVertexAttributes.Normals, result => + { + if (result.Status == MeshGenerationStatus.Success) + { + transform.SetWorldPose(PlayspaceUtilities.TransformPose(new Pose(result.Position, result.Rotation))); + transform.localScale = result.Scale; + + handRenderer.enabled = true; + } + else if (result.Status != MeshGenerationStatus.GenerationAlreadyInProgress) + { + handRenderer.enabled = false; + } + }, MeshGenerationOptions.ConsumeTransform); } } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) From ca3631d869b34606ed161658ad3ada876ea34600 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 4 Aug 2025 13:36:05 -0700 Subject: [PATCH 45/50] Reapply "Reapply "Iterate to using Unity's API instead of Google's"" This reverts commit 43c86afb8be540d636c5485d7eaa15ebfea434a8. --- .../Assets/Scripts/Editor/AndroidXRConfig.cs | 6 +- .../Packages/packages-lock.json | 5 +- .../PlatformHandMeshVisualizer.cs | 133 +++++++++++------- org.mixedrealitytoolkit.input/package.json | 2 +- 4 files changed, 92 insertions(+), 54 deletions(-) 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/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 870ebe1a8..525513868 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -4,7 +4,11 @@ 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; @@ -33,8 +37,11 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer private bool initializedUVs = false; #endif - private XRMeshSubsystem meshSubsystem = null; - private readonly List meshInfos = new List(); + // 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; @@ -47,33 +54,43 @@ protected override void OnEnable() handRenderer.enabled = false; propertyBlock ??= new MaterialPropertyBlock(); -#if UNITY_OPENXR_PRESENT - if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_ANDROID_hand_mesh")) + // If we already found our subsystem, don't search again + if (handSubsystem != null && handSubsystem.running) { - // If we already found our subsystem, just return - if (meshSubsystem != null && meshSubsystem.running) { return; } + updateSuccessFlags = HandNode == XRNode.LeftHand ? + XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints | XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose : + XRHandSubsystem.UpdateSuccessFlags.RightHandJoints | XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose; - List meshSubsystems = new List(); - SubsystemManager.GetSubsystems(meshSubsystems); - foreach (XRMeshSubsystem subsystem in meshSubsystems) + // Since the hand mesh is likely to change every frame, we + // "optimize mesh for frequent updates" by marking it dynamic + meshFilter.mesh.MarkDynamic(); + + return; + } + + List subsystems = XRSubsystemHelpers.GetAllSubsystems(); + foreach (XRHandSubsystem subsystem in subsystems) + { + if (subsystem.GetProvider() is OpenXRHandProvider provider && provider.handMeshDataSupplier != null) { - if (subsystem.subsystemDescriptor.id == "AndroidXRHandMeshProvider" - || subsystem.subsystemDescriptor.id == "AndroidXRMeshProvider") - { - Debug.Log($"Using XR_ANDROID_hand_mesh for {HandNode} visualization."); - meshSubsystem = subsystem; + Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); + handSubsystem = subsystem; - // Since the hand mesh is likely to change every frame, we - // "optimize mesh for frequent updates" by marking it dynamic - meshFilter.mesh.MarkDynamic(); + updateSuccessFlags = HandNode == XRNode.LeftHand ? + XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints | XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose : + XRHandSubsystem.UpdateSuccessFlags.RightHandJoints | XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose; - break; - } + // Since the hand mesh is likely to change every frame, we + // "optimize mesh for frequent updates" by marking it dynamic + meshFilter.mesh.MarkDynamic(); + + return; } } - else if (UnityEngine.XR.OpenXR.OpenXRRuntime.IsExtensionEnabled("XR_MSFT_hand_tracking_mesh")) - { + #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; @@ -85,14 +102,13 @@ protected override void OnEnable() { neutralPoseMesh = new Mesh(); } -#endif + + return; } - else #endif - { - Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); - enabled = false; - } + + Debug.Log($"No active hand mesh extension was found for {HandNode} visualization."); + enabled = false; } protected void Update() @@ -107,31 +123,52 @@ protected void Update() return; } - if (meshSubsystem != null - && meshSubsystem.running - && meshSubsystem.TryGetMeshInfos(meshInfos)) + if (handSubsystem != null && handSubsystem.running) { - int handMeshIndex = HandNode == XRNode.LeftHand ? 0 : 1; + XRHandMeshDataQueryParams queryParams = new() + { + allocator = Unity.Collections.Allocator.Temp, + }; - MeshInfo meshInfo = meshInfos[handMeshIndex]; - if (meshInfo.ChangeState == MeshChangeState.Added - || meshInfo.ChangeState == MeshChangeState.Updated) + Debug.Log($"Success?? {HandNode} | {Time.frameCount} | {handSubsystem.updateSuccessFlags} | {updateSuccessFlags} | {(handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0}"); + if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 + && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { - meshSubsystem.GenerateMeshAsync(meshInfo.MeshId, meshFilter.mesh, - null, MeshVertexAttributes.Normals, result => + lastUpdatedFrame = Time.frameCount; + XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; + handRenderer.enabled = true; + + if (handMeshData.positions.IsCreated && handMeshData.indices.IsCreated) + { + meshFilter.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) { - if (result.Status == MeshGenerationStatus.Success) - { - transform.SetWorldPose(PlayspaceUtilities.TransformPose(new Pose(result.Position, result.Rotation))); - transform.localScale = result.Scale; - - handRenderer.enabled = true; - } - else if (result.Status != MeshGenerationStatus.GenerationAlreadyInProgress) - { - handRenderer.enabled = false; - } - }, MeshGenerationOptions.ConsumeTransform); + (indices[i + 1], indices[i + 2]) = (indices[i + 2], indices[i + 1]); + } + meshFilter.mesh.SetIndices(indices, MeshTopology.Triangles, 0); + meshFilter.mesh.RecalculateBounds(); + } + + if (handMeshData.uvs.IsCreated) + { + meshFilter.mesh.SetUVs(0, handMeshData.uvs); + } + + if (handMeshData.normals.IsCreated) + { + meshFilter.mesh.SetNormals(handMeshData.normals); + } + else + { + meshFilter.mesh.RecalculateNormals(); + } + + if (handMeshData.TryGetRootPose(out Pose rootPose)) + { + transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); + } } } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) 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" } From 5e329e1b763ba5b40e73646a37dd9b006c5f9974 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 4 Aug 2025 16:27:44 -0700 Subject: [PATCH 46/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 525513868..7e7621a10 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -52,11 +52,12 @@ protected override void OnEnable() base.OnEnable(); handRenderer.enabled = false; - propertyBlock ??= new MaterialPropertyBlock(); - // If we already found our subsystem, don't search again - if (handSubsystem != null && handSubsystem.running) + // 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; @@ -64,10 +65,11 @@ protected override void OnEnable() // Since the hand mesh is likely to change every frame, we // "optimize mesh for frequent updates" by marking it dynamic meshFilter.mesh.MarkDynamic(); - - return; } + // If we already found our subsystem, just return + if (handSubsystem != null && handSubsystem.running) { return; } + List subsystems = XRSubsystemHelpers.GetAllSubsystems(); foreach (XRHandSubsystem subsystem in subsystems) { @@ -75,15 +77,6 @@ protected override void OnEnable() { Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); handSubsystem = subsystem; - - 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(); - return; } } @@ -94,10 +87,6 @@ protected override void OnEnable() Debug.Log($"Using XR_MSFT_hand_tracking_mesh for {HandNode} visualization."); handMeshTracker = HandNode == XRNode.LeftHand ? HandMeshTracker.Left : HandMeshTracker.Right; - // Since the hand mesh is likely to change every frame, we - // "optimize mesh for frequent updates" by marking it dynamic - meshFilter.mesh.MarkDynamic(); - if (neutralPoseMesh == null) { neutralPoseMesh = new Mesh(); From 027cb812ae87c85dfc1981cfe0e90286c5836278 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Mon, 4 Aug 2025 21:44:25 -0700 Subject: [PATCH 47/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 7e7621a10..bc5e63b9b 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -119,39 +119,43 @@ protected void Update() allocator = Unity.Collections.Allocator.Temp, }; - Debug.Log($"Success?? {HandNode} | {Time.frameCount} | {handSubsystem.updateSuccessFlags} | {updateSuccessFlags} | {(handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0}"); if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) { lastUpdatedFrame = Time.frameCount; XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; handRenderer.enabled = true; + Mesh mesh = meshFilter.mesh; - if (handMeshData.positions.IsCreated && handMeshData.indices.IsCreated) + if (handMeshData.positions.Length > 0 && handMeshData.indices.Length > 0) { - meshFilter.mesh.SetVertices(handMeshData.positions); + 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]); } - meshFilter.mesh.SetIndices(indices, MeshTopology.Triangles, 0); - meshFilter.mesh.RecalculateBounds(); + mesh.SetIndices(indices, MeshTopology.Triangles, 0); + mesh.RecalculateBounds(); } - if (handMeshData.uvs.IsCreated) + if (handMeshData.uvs.IsCreated && handMeshData.uvs.Length == mesh.vertexCount) { - meshFilter.mesh.SetUVs(0, handMeshData.uvs); + mesh.SetUVs(0, handMeshData.uvs); + } + else + { + mesh.uv = null; } - if (handMeshData.normals.IsCreated) + if (handMeshData.normals.IsCreated && handMeshData.normals.Length == mesh.vertexCount) { - meshFilter.mesh.SetNormals(handMeshData.normals); + mesh.SetNormals(handMeshData.normals); } else { - meshFilter.mesh.RecalculateNormals(); + mesh.RecalculateNormals(); } if (handMeshData.TryGetRootPose(out Pose rootPose)) From 3bf5e4156517ef17db498e35aaa641594f8c8cc5 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 1 Oct 2025 15:07:12 -0700 Subject: [PATCH 48/50] Move to event handler? --- .../PlatformHandMeshVisualizer.cs | 142 +++++++++++------- 1 file changed, 85 insertions(+), 57 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index bc5e63b9b..ca7472dc9 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -68,7 +68,11 @@ protected override void OnEnable() } // If we already found our subsystem, just return - if (handSubsystem != null && handSubsystem.running) { return; } + if (handSubsystem != null && handSubsystem.running) + { + handSubsystem.updatedHands += OnHandsUpdated; + return; + } List subsystems = XRSubsystemHelpers.GetAllSubsystems(); foreach (XRHandSubsystem subsystem in subsystems) @@ -77,6 +81,7 @@ protected override void OnEnable() { Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); handSubsystem = subsystem; + handSubsystem.updatedHands += OnHandsUpdated; return; } } @@ -100,6 +105,16 @@ protected override void OnEnable() enabled = false; } + protected override void OnDisable() + { + base.OnDisable(); + + if (handSubsystem != null) + { + handSubsystem.updatedHands -= OnHandsUpdated; + } + } + protected void Update() { if (!ShouldRenderHand()) @@ -112,57 +127,10 @@ protected void Update() return; } + // This path is handled in the OnHandsUpdated event handler if (handSubsystem != null && handSubsystem.running) { - XRHandMeshDataQueryParams queryParams = new() - { - allocator = Unity.Collections.Allocator.Temp, - }; - - if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0 - && (lastUpdatedFrame == Time.frameCount || handSubsystem.TryGetMeshData(out result, ref queryParams))) - { - 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)); - } - } + return; } #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) else if (handMeshTracker != null @@ -183,19 +151,79 @@ protected void Update() } transform.SetWorldPose(PlayspaceUtilities.TransformPose(pose)); + UpdateHandMaterial(); + return; } #endif - else + + // Hide the hand if we weren't able to obtain a valid mesh + handRenderer.enabled = false; + } + + private void OnHandsUpdated(XRHandSubsystem subsystem, XRHandSubsystem.UpdateSuccessFlags successFlags, XRHandSubsystem.UpdateType updateType) + { + // Only update visualization on OnBeforeRender for the most accurate data + if (updateType == XRHandSubsystem.UpdateType.Dynamic) { return; } + + XRHandMeshDataQueryParams queryParams = new() + { + allocator = Unity.Collections.Allocator.Temp, + }; + + if (lastUpdatedFrame != Time.frameCount && !subsystem.TryGetMeshData(out result, ref queryParams)) { - // 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; } - UpdateHandMaterial(); + if ((successFlags & updateSuccessFlags) != 0) + { + 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; + } + + // Hide the hand if we weren't able to obtain a valid mesh + handRenderer.enabled = false; } protected override bool ShouldRenderHand() From 4d0557197bc37567af74acb28ce1a9ddad6d2c04 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 1 Oct 2025 15:09:49 -0700 Subject: [PATCH 49/50] Move back to polling --- .../PlatformHandMeshVisualizer.cs | 142 +++++++----------- 1 file changed, 58 insertions(+), 84 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index ca7472dc9..5c8425260 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -68,11 +68,7 @@ protected override void OnEnable() } // If we already found our subsystem, just return - if (handSubsystem != null && handSubsystem.running) - { - handSubsystem.updatedHands += OnHandsUpdated; - return; - } + if (handSubsystem != null && handSubsystem.running) { return; } List subsystems = XRSubsystemHelpers.GetAllSubsystems(); foreach (XRHandSubsystem subsystem in subsystems) @@ -81,7 +77,6 @@ protected override void OnEnable() { Debug.Log($"Using {provider.handMeshDataSupplier.GetType()} for hand visualization."); handSubsystem = subsystem; - handSubsystem.updatedHands += OnHandsUpdated; return; } } @@ -105,16 +100,6 @@ protected override void OnEnable() enabled = false; } - protected override void OnDisable() - { - base.OnDisable(); - - if (handSubsystem != null) - { - handSubsystem.updatedHands -= OnHandsUpdated; - } - } - protected void Update() { if (!ShouldRenderHand()) @@ -127,10 +112,65 @@ protected void Update() return; } - // This path is handled in the OnHandsUpdated event handler if (handSubsystem != null && handSubsystem.running) { - return; + XRHandMeshDataQueryParams queryParams = new() + { + allocator = Unity.Collections.Allocator.Temp, + }; + + // Sometimes, the mesh update will fail, but that doesn't mean we don't have a valid mesh + // So, reuse the last mesh until the subsystem tells us we're untracked + if (lastUpdatedFrame != Time.frameCount && !handSubsystem.TryGetMeshData(out result, ref queryParams)) + { + return; + } + + if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0) + { + 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) else if (handMeshTracker != null @@ -160,72 +200,6 @@ protected void Update() handRenderer.enabled = false; } - private void OnHandsUpdated(XRHandSubsystem subsystem, XRHandSubsystem.UpdateSuccessFlags successFlags, XRHandSubsystem.UpdateType updateType) - { - // Only update visualization on OnBeforeRender for the most accurate data - if (updateType == XRHandSubsystem.UpdateType.Dynamic) { return; } - - XRHandMeshDataQueryParams queryParams = new() - { - allocator = Unity.Collections.Allocator.Temp, - }; - - if (lastUpdatedFrame != Time.frameCount && !subsystem.TryGetMeshData(out result, ref queryParams)) - { - return; - } - - if ((successFlags & updateSuccessFlags) != 0) - { - 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; - } - - // 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. From c4eb4bc6073d2571ab210d2c9bea6f5c88f48288 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Wed, 1 Oct 2025 16:07:55 -0700 Subject: [PATCH 50/50] Update PlatformHandMeshVisualizer.cs --- .../PlatformHandMeshVisualizer.cs | 80 +++++++++---------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs index 5c8425260..14d6ed1da 100644 --- a/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs +++ b/org.mixedrealitytoolkit.input/Visualizers/PlatformHandVisualizer/PlatformHandMeshVisualizer.cs @@ -112,68 +112,66 @@ protected void Update() return; } - if (handSubsystem != null && handSubsystem.running) + if (handSubsystem != null && handSubsystem.running && (handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0) { XRHandMeshDataQueryParams queryParams = new() { allocator = Unity.Collections.Allocator.Temp, }; - // Sometimes, the mesh update will fail, but that doesn't mean we don't have a valid mesh + // 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; } - if ((handSubsystem.updateSuccessFlags & updateSuccessFlags) != 0) - { - lastUpdatedFrame = Time.frameCount; - XRHandMeshData handMeshData = HandNode == XRNode.LeftHand ? result.leftHand : result.rightHand; - handRenderer.enabled = true; - Mesh mesh = meshFilter.mesh; + 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) + 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) { - 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(); + (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.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.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 (handMeshData.TryGetRootPose(out Pose rootPose)) + { + transform.SetWorldPose(PlayspaceUtilities.TransformPose(rootPose)); } + UpdateHandMaterial(); + return; } + #if MROPENXR_PRESENT && (UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID) - else if (handMeshTracker != null + if (handMeshTracker != null && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, meshFilter.mesh) && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose)) {