Files
VIVE-OpenXR-Unity/com.htc.upm.vive.openxr/OpenXRHandTracking/Script/HandTracking_OpenXR_API.cs
2023-06-15 10:10:25 +08:00

429 lines
21 KiB
C#

using System;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEngine.XR.OpenXR.Features;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace VIVE
{
namespace HandTracking
{
#if UNITY_EDITOR
[OpenXRFeature(UiName = "Hand Tracking",
BuildTargetGroups = new[] { BuildTargetGroup.Standalone },
Company = "HTC",
Desc = "Hand Tracking OpenXR Feature",
DocumentationLink = "https://developer.vive.com/resources/openxr/openxr-pcvr/tutorials/unity/how-integrate-hand-tracking-data-your-hand-model/",
OpenxrExtensionStrings = "XR_EXT_hand_tracking",
Version = "0.0.1",
FeatureId = featureId)]
#endif
public class HandTracking_OpenXR_API : OpenXRFeature
{
/// <summary>
/// The feature id string. This is used to give the feature a well known id for reference.
/// </summary>
public const string featureId = "com.company.openxr.handtracking.feature";
private IntPtr oldxrGetInstanceProcAddr;
private IntPtr oldWaitFrame;
private ulong m_xrInstance;
private ulong m_xrSession;
private ulong m_systemId;
private XrFrameWaitInfo m_frameWaitInfo;
private XrFrameState m_frameState;
[Obsolete] public ulong m_space;
[Obsolete] public XrSystemProperties systemProperties;
[Obsolete] public ulong m_leftHandle;
[Obsolete] public ulong m_rightHandle;
public bool IsInitialized { get { return m_xrGetSystemProperties != null; } }
public bool IsEnabledAndInitialized { get { return enabled && IsInitialized; } }
public bool IsSessionCreated { get { return XrSession != default; } }
public ulong XrInstance { get { return m_xrInstance; } }
public ulong XrSession { get { return m_xrSession; } }
public ulong SystemId { get { return m_systemId; } }
public event Action<ulong> onSessionCreate;
public event Action<ulong> onSessionDestroy;
public event Action<ulong> onSystemChange;
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
oldxrGetInstanceProcAddr = func;
m_intercept_xrWaitFrame_xrGetInstanceProcAddr = intercept_xrWaitFrame_xrGetInstanceProcAddr;
return Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame_xrGetInstanceProcAddr);
}
private int intercept_xrWaitFrame_xrGetInstanceProcAddr(ulong instance, string name, out IntPtr function)
{
if (oldxrGetInstanceProcAddr == null || oldxrGetInstanceProcAddr == IntPtr.Zero)
{
UnityEngine.Debug.LogError("oldxrGetInstanceProcAddr is null");
function = IntPtr.Zero;
return -1;
}
// Get delegate of old xrGetInstanceProcAddr.
var xrGetProc = Marshal.GetDelegateForFunctionPointer<xrGetInstanceProcDelegate>(oldxrGetInstanceProcAddr);
int result = xrGetProc(instance, name,out function);
if(name == "xrWaitFrame")
{
oldWaitFrame = function;
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
UnityEngine.Debug.Log("Getting xrWaitFrame func");
}
return result;
}
private int intercepted_xrWaitFrame(ulong session,ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
{
// Get delegate of old xrWaitFrame.
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<xrWaitFrameDelegate>(oldWaitFrame);
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
m_frameWaitInfo = frameWaitInfo;
m_frameState = frameState;
return res;
}
/// <inheritdoc />
protected override bool OnInstanceCreate(ulong xrInstance)
{
UnityEngine.Debug.Log($"OnInstanceCreate({xrInstance:X})");
m_xrInstance = xrInstance;
return GetXrFunctionDelegates(xrInstance);
}
/// <inheritdoc />
protected override void OnInstanceDestroy(ulong xrInstance)
{
UnityEngine.Debug.Log($"OnInstanceDestroy({xrInstance:X})");
m_xrInstance = default;
//ClearXrFunctionDelegates();
}
/// <inheritdoc />
protected override void OnSessionCreate(ulong xrSession)
{
UnityEngine.Debug.Log($"OnSessionCreate({xrSession:X})");
m_xrSession = xrSession;
try { onSessionCreate?.Invoke(xrSession); }
catch (Exception e) { UnityEngine.Debug.LogError(e); }
}
/// <inheritdoc />
protected override void OnSystemChange(ulong xrSystem)
{
UnityEngine.Debug.Log($"OnSystemChange({xrSystem:X})");
m_systemId = xrSystem;
try { onSystemChange?.Invoke(xrSystem); }
catch (Exception e) { UnityEngine.Debug.LogError(e); }
}
/// <inheritdoc />
protected override void OnSessionDestroy(ulong xrSession)
{
UnityEngine.Debug.Log($"OnSessionDestroy({xrSession:X})");
m_xrSession = default;
try { onSessionDestroy?.Invoke(xrSession); }
catch (Exception e) { UnityEngine.Debug.LogError(e); }
}
/// <summary>Return true if the result equals zero. </summary>
private bool GetXrFunctionDelegates(ulong xrInstance)
{
if (xrGetInstanceProcAddr == null || xrGetInstanceProcAddr == IntPtr.Zero)
{
UnityEngine.Debug.LogError("xrGetInstanceProcAddr is null");
return false;
}
// Get delegate of xrGetInstanceProcAddr.
var xrGetProc = Marshal.GetDelegateForFunctionPointer<xrGetInstanceProcDelegate>(xrGetInstanceProcAddr);
if (!MarshelFunc(xrInstance, xrGetProc, "xrGetSystemProperties", ref m_xrGetSystemProperties)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrEnumerateReferenceSpaces", ref m_xrEnumerateReferenceSpaces)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrEnumerateReferenceSpaces", ref m_xrEnumerateReferenceSpaces2)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrCreateReferenceSpace", ref m_xrCreateReferenceSpace)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrDestroySpace", ref m_xrDestroySpace)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrCreateHandTrackerEXT", ref m_xrCreateHandTrackerEXT)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrDestroyHandTrackerEXT", ref m_xrDestroyHandTrackerEXT)) { return false; }
if (!MarshelFunc(xrInstance, xrGetProc, "xrLocateHandJointsEXT", ref m_xrLocateHandJointsEXT)) { return false; }
return true;
}
private void ClearXrFunctionDelegates()
{
m_xrGetSystemProperties = null;
m_xrEnumerateReferenceSpaces = null;
m_xrEnumerateReferenceSpaces2 = null;
m_xrCreateReferenceSpace = null;
m_xrDestroySpace = null;
m_xrCreateHandTrackerEXT = null;
m_xrDestroyHandTrackerEXT = null;
m_xrLocateHandJointsEXT = null;
}
private static bool MarshelFunc<T>(ulong instance, xrGetInstanceProcDelegate instanceProc, string funcName, ref T func, bool verbose = true)
where T : Delegate
{
if (instanceProc(instance, funcName, out var fp) != 0)
{
if (verbose)
{
UnityEngine.Debug.LogError("Fail getting function " + funcName);
}
return false;
}
func = Marshal.GetDelegateForFunctionPointer<T>(fp);
return true;
}
private xrGetInstanceProcDelegate m_intercept_xrWaitFrame_xrGetInstanceProcAddr;
private delegate int xrGetSystemPropertiesDelegate(ulong instance, ulong systemId, ref XrSystemProperties properties);
private xrGetSystemPropertiesDelegate m_xrGetSystemProperties;
public int xrGetSystemProperties(ref XrSystemProperties properties) =>
m_xrGetSystemProperties(XrInstance, SystemId, ref properties);
public int xrGetSystemProperties(ulong instance, ulong systemId, ref XrSystemProperties properties) =>
m_xrGetSystemProperties(instance, systemId, ref properties);
private delegate int xrWaitFrameDelegate(ulong session,ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState);
private xrWaitFrameDelegate m_intercept_xrWaitFrame;
#region space
private delegate int xrEnumerateReferenceSpacesDelegate(ulong session, int spaceCapacityInput, out int spaceCountOutput, out XrReferenceSpaceType spaces);
private xrEnumerateReferenceSpacesDelegate m_xrEnumerateReferenceSpaces;
public int xrEnumerateReferenceSpaces(int spaceCapacityInput, out int spaceCountOutput, out XrReferenceSpaceType spaces) =>
m_xrEnumerateReferenceSpaces(XrSession, spaceCapacityInput, out spaceCountOutput, out spaces);
private delegate int xrEnumerateReferenceSpacesDelegate2(ulong session, int spaceCapacityInput, out int spaceCountOutput, IntPtr spaces);
private xrEnumerateReferenceSpacesDelegate2 m_xrEnumerateReferenceSpaces2;
public int xrEnumerateReferenceSpaces(ulong session, int spaceCapacityInput, out int spaceCountOutput, IntPtr spaces) =>
m_xrEnumerateReferenceSpaces2(session, spaceCapacityInput, out spaceCountOutput, spaces);
private delegate int xrCreateReferenceSpaceDelegate(ulong session, ref XrReferenceSpaceCreateInfo createInfo, out ulong space);
private xrCreateReferenceSpaceDelegate m_xrCreateReferenceSpace;
public int xrCreateReferenceSpace(ref XrReferenceSpaceCreateInfo createInfo, out ulong space) =>
m_xrCreateReferenceSpace(XrSession, ref createInfo, out space);
public int xrCreateReferenceSpace(ulong session, ref XrReferenceSpaceCreateInfo createInfo, out ulong space) =>
m_xrCreateReferenceSpace(session, ref createInfo, out space);
private delegate int xrDestroySpaceDelegate(ulong space);
private xrDestroySpaceDelegate m_xrDestroySpace;
public int xrDestroySpace(ulong space) =>
m_xrDestroySpace(space);
#endregion
private delegate int xrCreateHandTrackerEXTDelegate(ulong session, XrHandTrackerCreateInfoEXT createInfo, out ulong handTracker);
private xrCreateHandTrackerEXTDelegate m_xrCreateHandTrackerEXT;
public int xrCreateHandTrackerEXT(XrHandTrackerCreateInfoEXT createInfo, out ulong handle) =>
m_xrCreateHandTrackerEXT(XrSession, createInfo, out handle);
public int xrCreateHandTrackerEXT(ulong session, XrHandTrackerCreateInfoEXT createInfo, out ulong handle) =>
m_xrCreateHandTrackerEXT(session, createInfo, out handle);
private delegate int xrDestroyHandTrackerEXTDelegate(ulong handTracker);
private xrDestroyHandTrackerEXTDelegate m_xrDestroyHandTrackerEXT;
public int xrDestroyHandTrackerEXT(ulong handle) =>
m_xrDestroyHandTrackerEXT(handle);
private delegate int xrLocateHandJointsEXTDelegate(ulong handTracker,ref XrHandJointsLocateInfoEXT locateInfo, ref XrHandJointLocationsEXT locations);
private xrLocateHandJointsEXTDelegate m_xrLocateHandJointsEXT;
public int xrLocateHandJointsEXT(ulong handTracker,ref XrHandJointsLocateInfoEXT locateInfo, ref XrHandJointLocationsEXT locations) =>
m_xrLocateHandJointsEXT(handTracker,ref locateInfo, ref locations);
public unsafe bool SystemSupportsHandTracking(out XrResult result)
{
var handTrackingSystemProp = new XrSystemHandTrackingPropertiesEXT()
{
type = XrStructureType.XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT,
next = IntPtr.Zero,
};
var systemProp = new XrSystemProperties()
{
type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES,
next = (IntPtr)(&handTrackingSystemProp),
};
result = (XrResult)xrGetSystemProperties(XrInstance, SystemId, ref systemProp);
return result == XrResult.XR_SUCCESS && handTrackingSystemProp.supportsHandTracking != 0u;
}
public bool TryGetSupportedReferenceSpaceTypeCount(out int count, out XrResult result)
{
result = (XrResult)m_xrEnumerateReferenceSpaces2(XrSession, 0, out count, IntPtr.Zero);
return result == XrResult.XR_SUCCESS;
}
public bool TryGetSupportedReferenceSpaceTypes(XrReferenceSpaceType[] spaces, out XrResult result)
{
if (spaces == null || spaces.Length == 0)
{
result = default;
return true;
}
result = (XrResult)m_xrEnumerateReferenceSpaces2(XrSession, spaces.Length, out _, ArrayPtr(spaces));
return result == XrResult.XR_SUCCESS;
}
public bool TryGetSupportedReferenceSpaceType(XrReferenceSpaceType preferType, out XrReferenceSpaceType supportedType, out XrResult result)
{
supportedType = default;
if (!TryGetSupportedReferenceSpaceTypeCount(out var count, out result)) { return false; }
if (count == 0) { return false; } // FIXME: error code?
var spaces = new XrReferenceSpaceType[count];
if (!TryGetSupportedReferenceSpaceTypes(spaces, out result)) { return false; }
for (int i = 0; i < count; ++i)
{
supportedType = spaces[i];
if (supportedType == preferType) { break; }
}
return true;
}
public bool TryCreateReferenceSpace(XrReferenceSpaceType refSpaceType, XrVector3f position, XrQuaternionf orientation, out ulong handle, out XrResult result)
{
var createInfo = new XrReferenceSpaceCreateInfo()
{
type = XrStructureType.XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
next = IntPtr.Zero,
referencespacetype = refSpaceType,
poseInReferenceSpace = new XrPosef()
{
position = position,
orientation = orientation,
},
};
result = (XrResult)xrCreateReferenceSpace(XrSession, ref createInfo, out handle);
return result == XrResult.XR_SUCCESS;
}
public bool TryDestroyReferenceSpace(ulong handle, out XrResult result)
{
result = (XrResult)xrDestroySpace(handle);
return result == XrResult.XR_SUCCESS;
}
public bool TryCreateHandTracker(XrHandEXT hand, out ulong handle, out XrResult result, XrHandJointSetEXT jointSet = XrHandJointSetEXT.XR_HAND_JOINT_SET_DEFAULT_EXT)
{
var createInfo = new XrHandTrackerCreateInfoEXT()
{
type = XrStructureType.XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT,
next = IntPtr.Zero,
hand = hand,
handJointSet = XrHandJointSetEXT.XR_HAND_JOINT_SET_DEFAULT_EXT,
};
result = (XrResult)xrCreateHandTrackerEXT(XrSession, createInfo, out handle);
return result == XrResult.XR_SUCCESS;
}
public bool TryDestroyHandTracker(ulong handle, out XrResult result)
{
result = (XrResult)xrDestroyHandTrackerEXT(handle);
return result == XrResult.XR_SUCCESS;
}
public bool TryLocateHandJoints(ulong handle, ulong space, out bool isActive, XrHandJointLocationEXT[] locationArray, out XrResult result)
{
return TryLocateHandJoints(handle, space, out isActive, locationArray, null, out result);
}
public bool TryLocateHandJoints(ulong handle, ulong space, out bool isActive, XrHandJointLocationEXT[] locationArray, XrHandJointVelocityEXT[] velocityArray, out XrResult result)
{
return TryLocateHandJoints(handle, space, IntPtr.Zero, out isActive, locationArray, velocityArray, out result);
}
public bool TryLocateHandJoints(ulong handle, ulong space, ref XrHandJointsMotionRangeEXT motionRange, out bool isActive, XrHandJointLocationEXT[] locationArray, out XrResult result)
{
return TryLocateHandJoints(handle, space, ref motionRange, out isActive, locationArray, null, out result);
}
public unsafe bool TryLocateHandJoints(ulong handle, ulong space, ref XrHandJointsMotionRangeEXT motionRange, out bool isActive, XrHandJointLocationEXT[] locationArray, XrHandJointVelocityEXT[] velocityArray, out XrResult result)
{
var motionRangeInfo = new XrHandJointsMotionRangeInfoEXT()
{
type = XrStructureType.XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT,
next = IntPtr.Zero,
handJointsMotionRange = motionRange,
};
bool res = TryLocateHandJoints(handle, space, (IntPtr)(&motionRangeInfo), out isActive, locationArray, velocityArray, out result);
motionRange = motionRangeInfo.handJointsMotionRange;
return res;
}
private unsafe bool TryLocateHandJoints(ulong handle, ulong space, IntPtr motionRangeInfo, out bool isActive, XrHandJointLocationEXT[] locationArray, XrHandJointVelocityEXT[] velocityArray, out XrResult result)
{
var locateInfo = new XrHandJointsLocateInfoEXT()
{
type = XrStructureType.XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT,
next = motionRangeInfo,
baseSpace = space,
time = m_frameState.predictedDisplayTime, //An arbitrary number greater than 0
};
var locVelocitiesPtr = IntPtr.Zero;
if (velocityArray != null)
{
var locVelocities = new XrHandJointVelocitiesEXT()
{
type = XrStructureType.XR_TYPE_HAND_JOINT_VELOCITIES_EXT,
next = IntPtr.Zero,
jointCount = (uint)velocityArray.Length,
jointVelocities = ArrayPtr(velocityArray)
};
locVelocitiesPtr = (IntPtr)(&locVelocities);
}
var locations = new XrHandJointLocationsEXT()
{
type = XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT,
next = locVelocitiesPtr,
jointCount = locationArray == null ? 0 : locationArray.Length,
jointLocations = ArrayPtr(locationArray),
};
result = (XrResult)xrLocateHandJointsEXT(handle,ref locateInfo, ref locations);
isActive = locations.isActive != 0u;
return result == XrResult.XR_SUCCESS;
}
private unsafe static IntPtr ArrayPtr(XrReferenceSpaceType[] array)
{
if (array == null) { return IntPtr.Zero; }
fixed (XrReferenceSpaceType* p = array) { return (IntPtr)p; }
}
private unsafe static IntPtr ArrayPtr(XrHandJointLocationEXT[] array)
{
if (array == null) { return IntPtr.Zero; }
fixed (XrHandJointLocationEXT* p = array) { return (IntPtr)p; }
}
private unsafe static IntPtr ArrayPtr(XrHandJointVelocityEXT[] array)
{
if (array == null) { return IntPtr.Zero; }
fixed (XrHandJointVelocityEXT* p = array) { return (IntPtr)p; }
}
}
}
}