mixedreality/com.microsoft.mixedreality..../Providers/OpenXR/Scripts/OpenXRHandMeshProvider.cs

162 lines
6.2 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#if MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
using Microsoft.MixedReality.OpenXR;
using System.Collections.Generic;
using UnityEngine;
#endif // MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
using Microsoft.MixedReality.Toolkit.Input;
using Unity.Profiling;
namespace Microsoft.MixedReality.Toolkit.XRSDK.OpenXR
{
/// <summary>
/// Provides the ability to load a hand mesh from OpenXR for the corresponding hand.
/// </summary>
internal class OpenXRHandMeshProvider
{
/// <summary>
/// The user's left hand.
/// </summary>
public static OpenXRHandMeshProvider Left { get; } =
#if MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
new OpenXRHandMeshProvider(HandMeshTracker.Left, Utilities.Handedness.Left);
#else
null;
#endif // MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
/// <summary>
/// The user's right hand.
/// </summary>
public static OpenXRHandMeshProvider Right { get; } =
#if MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
new OpenXRHandMeshProvider(HandMeshTracker.Right, Utilities.Handedness.Right);
#else
null;
#endif // MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
#if MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
private OpenXRHandMeshProvider(HandMeshTracker handMeshTracker, Utilities.Handedness handedness)
{
this.handMeshTracker = handMeshTracker;
this.handedness = handedness;
mesh = new Mesh();
neutralPoseMesh = new Mesh();
}
private readonly HandMeshTracker handMeshTracker;
private readonly Utilities.Handedness handedness;
private readonly Mesh mesh;
private readonly Mesh neutralPoseMesh;
private readonly List<Vector3> vertices = new List<Vector3>();
private readonly List<Vector3> normals = new List<Vector3>();
private readonly List<int> triangles = new List<int>();
private Vector2[] handMeshUVs = null;
#endif // MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
private IMixedRealityInputSource inputSource = null;
/// <summary>
/// Sets the <see cref="IMixedRealityInputSource"/> that represents the current hand for this mesh.
/// </summary>
/// <param name="inputSource">Implementation of the hand input source.</param>
public void SetInputSource(IMixedRealityInputSource inputSource) => this.inputSource = inputSource;
private static readonly ProfilerMarker UpdateHandMeshPerfMarker = new ProfilerMarker($"[MRTK] {nameof(OpenXRHandMeshProvider)}.UpdateHandMesh");
/// <summary>
/// Updates the hand mesh based on the current state of the hand.
/// </summary>
public void UpdateHandMesh()
{
#if MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
using (UpdateHandMeshPerfMarker.Auto())
{
MixedRealityInputSystemProfile inputSystemProfile = CoreServices.InputSystem?.InputSystemProfile;
MixedRealityHandTrackingProfile handTrackingProfile = inputSystemProfile != null ? inputSystemProfile.HandTrackingProfile : null;
if (handTrackingProfile == null || !handTrackingProfile.EnableHandMeshVisualization)
{
// If hand mesh visualization is disabled make sure to clean up if we've already initialized
if (handMeshUVs != null)
{
// Notify that hand mesh has been updated (cleared)
CoreServices.InputSystem?.RaiseHandMeshUpdated(inputSource, handedness, new HandMeshInfo());
handMeshUVs = null;
}
return;
}
if (handMeshUVs == null && handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, neutralPoseMesh, HandPoseType.ReferenceOpenPalm))
{
handMeshUVs = InitializeUVs(neutralPoseMesh.vertices);
}
if (handMeshTracker.TryGetHandMesh(FrameTime.OnUpdate, mesh) && handMeshTracker.TryLocateHandMesh(FrameTime.OnUpdate, out Pose pose))
{
mesh.GetVertices(vertices);
mesh.GetNormals(normals);
mesh.GetTriangles(triangles, 0);
HandMeshInfo handMeshInfo = new HandMeshInfo
{
vertices = vertices.ToArray(),
normals = normals.ToArray(),
triangles = triangles.ToArray(),
uvs = handMeshUVs,
position = MixedRealityPlayspace.TransformPoint(pose.position),
rotation = MixedRealityPlayspace.Rotation * pose.rotation
};
CoreServices.InputSystem?.RaiseHandMeshUpdated(inputSource, handedness, handMeshInfo);
}
}
}
private Vector2[] InitializeUVs(Vector3[] neutralPoseVertices)
{
if (neutralPoseVertices.Length == 0)
{
Debug.LogError("Loaded 0 vertices for neutralPoseVertices");
return System.Array.Empty<Vector2>();
}
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 // MSFT_OPENXR && (UNITY_STANDALONE_WIN || UNITY_WSA)
}
}
}