44using System . Collections . Generic ;
55using Unity . XR . CoreUtils ;
66using UnityEngine ;
7+ using UnityEngine . SubsystemsImplementation . Extensions ;
78using UnityEngine . XR ;
9+ using UnityEngine . XR . Hands ;
10+ using UnityEngine . XR . Hands . Meshing ;
11+ using UnityEngine . XR . Hands . OpenXR ;
812
913#if MROPENXR_PRESENT && ( UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID )
1014using Microsoft . MixedReality . OpenXR ;
@@ -33,8 +37,14 @@ public class PlatformHandMeshVisualizer : HandMeshVisualizer
3337 private bool initializedUVs = false ;
3438#endif
3539
36- private XRMeshSubsystem meshSubsystem = null ;
37- private readonly List < MeshInfo > meshInfos = new List < MeshInfo > ( ) ;
40+ // Share these among all instances to only query once per frame
41+ private static int lastUpdatedFrame = - 1 ;
42+ private static XRHandSubsystem handSubsystem = null ;
43+ private static XRHandMeshDataQueryResult result ;
44+ private static XRHandMeshDataQueryParams queryParams = new ( )
45+ {
46+ allocator = Unity . Collections . Allocator . Temp ,
47+ } ;
3848
3949 // The property block used to modify the wrist position property on the material
4050 private MaterialPropertyBlock propertyBlock = null ;
@@ -47,40 +57,39 @@ protected override void OnEnable()
4757 handRenderer . enabled = false ;
4858 propertyBlock ??= new MaterialPropertyBlock ( ) ;
4959
50- #if UNITY_OPENXR_PRESENT
51- if ( UnityEngine . XR . OpenXR . OpenXRRuntime . IsExtensionEnabled ( "XR_ANDROID_hand_mesh" ) )
60+ if ( handSubsystem != null )
61+ {
62+ return ;
63+ }
64+
65+ List < XRHandSubsystem > subsystems = XRSubsystemHelpers . GetAllSubsystems < XRHandSubsystem > ( ) ;
66+ foreach ( XRHandSubsystem subsystem in subsystems )
5267 {
53- List < XRMeshSubsystem > meshSubsystems = new List < XRMeshSubsystem > ( ) ;
54- SubsystemManager . GetSubsystems ( meshSubsystems ) ;
55- foreach ( XRMeshSubsystem subsystem in meshSubsystems )
68+ if ( subsystem . GetProvider ( ) is OpenXRHandProvider provider && provider . handMeshDataSupplier != null )
5669 {
57- if ( subsystem . subsystemDescriptor . id == "AndroidXRHandMeshProvider"
58- || subsystem . subsystemDescriptor . id == "AndroidXRMeshProvider" )
59- {
60- Debug . Log ( $ "Using XR_ANDROID_hand_mesh for { HandNode } visualization.") ;
61- meshSubsystem = subsystem ;
62- break ;
63- }
70+ Debug . Log ( $ "Using { provider . handMeshDataSupplier . GetType ( ) } for hand visualization.") ;
71+ handSubsystem = subsystem ;
72+ return ;
6473 }
6574 }
66- else if ( UnityEngine . XR . OpenXR . OpenXRRuntime . IsExtensionEnabled ( "XR_MSFT_hand_tracking_mesh" ) )
67- {
75+
6876#if MROPENXR_PRESENT && ( UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID )
77+ if ( UnityEngine . XR . OpenXR . OpenXRRuntime . IsExtensionEnabled ( "XR_MSFT_hand_tracking_mesh" ) )
78+ {
6979 Debug . Log ( $ "Using XR_MSFT_hand_tracking_mesh for { HandNode } visualization.") ;
7080 handMeshTracker = HandNode == XRNode . LeftHand ? HandMeshTracker . Left : HandMeshTracker . Right ;
7181
7282 if ( neutralPoseMesh == null )
7383 {
7484 neutralPoseMesh = new Mesh ( ) ;
7585 }
76- #endif
86+
87+ return ;
7788 }
78- else
7989#endif
80- {
81- Debug . Log ( $ "No active hand mesh extension was found for { HandNode } visualization.") ;
82- enabled = false ;
83- }
90+
91+ Debug . Log ( $ "No active hand mesh extension was found for { HandNode } visualization.") ;
92+ enabled = false ;
8493 }
8594
8695 protected void Update ( )
@@ -95,26 +104,26 @@ protected void Update()
95104 return ;
96105 }
97106
98- if ( meshSubsystem != null
99- && meshSubsystem . running
100- && meshSubsystem . TryGetMeshInfos ( meshInfos ) )
107+ if ( handSubsystem != null
108+ && handSubsystem . running
109+ && ( lastUpdatedFrame == Time . frameCount || handSubsystem . TryGetMeshData ( out result , ref queryParams ) ) )
101110 {
102- int handMeshIndex = HandNode == XRNode . LeftHand ? 0 : 1 ;
111+ lastUpdatedFrame = Time . frameCount ;
112+ XRHandMeshData handMeshData = HandNode == XRNode . LeftHand ? result . leftHand : result . rightHand ;
103113
104- MeshInfo meshInfo = meshInfos [ handMeshIndex ] ;
105- if ( meshInfo . ChangeState == MeshChangeState . Added
106- || meshInfo . ChangeState == MeshChangeState . Updated )
107- {
108- meshSubsystem . GenerateMeshAsync ( meshInfo . MeshId , meshFilter . mesh ,
109- null , MeshVertexAttributes . Normals , result => { } ) ;
114+ meshFilter . mesh . Clear ( ) ;
115+ meshFilter . mesh . SetVertices ( handMeshData . positions ) ;
116+ meshFilter . mesh . SetUVs ( 0 , handMeshData . uvs ) ;
117+ meshFilter . mesh . SetIndices ( handMeshData . indices , MeshTopology . Triangles , 0 ) ;
110118
111- handRenderer . enabled = true ;
119+ handRenderer . enabled = true ;
112120
113- // This hand mesh is provided pre-translated from the world origin,
114- // so we want to ensure the mesh is "centered" at the world origin
115- PlayspaceUtilities . XROrigin . CameraFloorOffsetObject . transform . GetPositionAndRotation ( out Vector3 position , out Quaternion rotation ) ;
116- transform . SetPositionAndRotation ( position , rotation ) ;
121+ if ( ! handMeshData . TryGetRootPose ( out Pose rootPose ) )
122+ {
123+ rootPose = Pose . identity ;
117124 }
125+
126+ transform . SetWorldPose ( PlayspaceUtilities . TransformPose ( rootPose ) ) ;
118127 }
119128#if MROPENXR_PRESENT && ( UNITY_STANDALONE_WIN || UNITY_WSA || UNITY_ANDROID )
120129 else if ( handMeshTracker ! = null
0 commit comments