version 2.3.0
This commit is contained in:
8
com.htc.upm.vive.openxr/Runtime/Features/Anchor.meta
Normal file
8
com.htc.upm.vive.openxr/Runtime/Features/Anchor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 673b5df0bff21a84c8b23a4f3c6a6268
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 046b5fd65fa996041a970e1fd193d213
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69ae1c3151561af42ba226f0e563ebc6
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5cbfbcf56aaffa4fab38659c00c3903
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,231 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
|
||||
// Remove FAKE_DATA if editor or windows is supported.
|
||||
#if UNITY_EDITOR
|
||||
#define FAKE_DATA
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using VIVE.OpenXR.Feature;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.Anchor
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Anchor",
|
||||
Desc = "VIVE's implementaion of the XR_HTC_anchor.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class ViveAnchor : OpenXRFeature
|
||||
{
|
||||
public const string kOpenxrExtensionString = "XR_HTC_anchor";
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.wave.openxr.feature.htcanchor";
|
||||
private XrInstance m_XrInstance = 0;
|
||||
private XrSession session = 0;
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
|
||||
#region struct, enum, const of this extensions
|
||||
|
||||
public struct XrSystemAnchorPropertiesHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrBool32 supportsAnchor;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
|
||||
public struct XrSpatialAnchorNameHTC
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||
public string name;
|
||||
}
|
||||
|
||||
public struct XrSpatialAnchorCreateInfoHTC
|
||||
{
|
||||
public XrStructureType type;
|
||||
public System.IntPtr next;
|
||||
public XrSpace space;
|
||||
public XrPosef poseInSpace;
|
||||
public XrSpatialAnchorNameHTC name;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region delegates and delegate instances
|
||||
delegate XrResult DelegateXrCreateSpatialAnchorHTC(XrSession session, ref XrSpatialAnchorCreateInfoHTC createInfo, ref XrSpace anchor);
|
||||
delegate XrResult DelegateXrGetSpatialAnchorNameHTC(XrSpace anchor, ref XrSpatialAnchorNameHTC name);
|
||||
|
||||
DelegateXrCreateSpatialAnchorHTC XrCreateSpatialAnchorHTC;
|
||||
DelegateXrGetSpatialAnchorNameHTC XrGetSpatialAnchorNameHTC;
|
||||
#endregion delegates and delegate instances
|
||||
|
||||
#region override functions
|
||||
/// <inheritdoc />
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
Debug.Log("ViveAnchor HookGetInstanceProcAddr() ");
|
||||
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
//Debug.Log("VIVEAnchor OnInstanceCreate() ");
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||
{
|
||||
Debug.LogWarning("ViveAnchor OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_XrInstance = xrInstance;
|
||||
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
||||
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, CommonWrapper.Instance.GetInstanceProcAddr);
|
||||
|
||||
return GetXrFunctionDelegates(m_XrInstance);
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
CommonWrapper.Instance.OnInstanceDestroy();
|
||||
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
Debug.Log("ViveAnchor OnSessionCreate() ");
|
||||
|
||||
// here's one way you can grab the session
|
||||
Debug.Log($"EXT: Got xrSession: {xrSession}");
|
||||
session = xrSession;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
Debug.Log("ViveAnchor OnSessionBegin() ");
|
||||
Debug.Log($"EXT: xrBeginSession: {xrSession}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
Debug.Log("ViveAnchor OnSessionEnd() ");
|
||||
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
|
||||
}
|
||||
|
||||
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||
//private ulong appSpace;
|
||||
//protected override void OnAppSpaceChange(ulong space)
|
||||
//{
|
||||
// //Debug.Log($"VIVEAnchor OnAppSpaceChange({appSpace} -> {space})");
|
||||
// appSpace = space;
|
||||
//}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
Debug.Log("ViveAnchor OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
|
||||
#endregion override functions
|
||||
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
Debug.Log("ViveAnchor GetXrFunctionDelegates() ");
|
||||
|
||||
bool ret = true;
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreateSpatialAnchorHTC", out XrCreateSpatialAnchorHTC);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetSpatialAnchorNameHTC", out XrGetSpatialAnchorNameHTC);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#region functions of extension
|
||||
/// <summary>
|
||||
/// Helper function to get this feature' properties.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
public XrResult GetProperties(out XrSystemAnchorPropertiesHTC anchorProperties)
|
||||
{
|
||||
anchorProperties = new XrSystemAnchorPropertiesHTC();
|
||||
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
anchorProperties.type = XrStructureType.XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC;
|
||||
anchorProperties.supportsAnchor = true;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref anchorProperties);
|
||||
}
|
||||
|
||||
public XrResult CreateSpatialAnchor(XrSpatialAnchorCreateInfoHTC createInfo, out XrSpace anchor)
|
||||
{
|
||||
anchor = default;
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
return XrResult.XR_SUCCESS;
|
||||
#endif
|
||||
var ret = XrCreateSpatialAnchorHTC(session, ref createInfo, ref anchor);
|
||||
Debug.Log("ViveAnchor CreateSpatialAnchor() r=" + ret + ", a=" + anchor + ", bs=" + createInfo.space +
|
||||
", pos=(" + createInfo.poseInSpace.position.x + "," + createInfo.poseInSpace.position.y + "," + createInfo.poseInSpace.position.z +
|
||||
"), rot=(" + createInfo.poseInSpace.orientation.x + "," + createInfo.poseInSpace.orientation.y + "," + createInfo.poseInSpace.orientation.z + "," + createInfo.poseInSpace.orientation.w +
|
||||
"), n=" + createInfo.name.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public XrResult GetSpatialAnchorName(XrSpace anchor, out XrSpatialAnchorNameHTC name)
|
||||
{
|
||||
name = default;
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
name.name = "fake anchor";
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return XrGetSpatialAnchorNameHTC(anchor, ref name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region tools for user
|
||||
|
||||
/// <summary>
|
||||
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrSpace GetTrackingSpace()
|
||||
{
|
||||
var s = GetCurrentAppSpace();
|
||||
Debug.Log("ViveAnchor GetTrackingSpace() s=" + s);
|
||||
return s;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c17aa731a6f4fb54bb9a2c28df667e5e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -22,7 +22,7 @@ using UnityEditor.XR.OpenXR.Features;
|
||||
namespace VIVE.OpenXR.DisplayRefreshRate
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "XR FB Display Refresh Rate",
|
||||
[OpenXRFeature(UiName = "VIVE XR Display Refresh Rate",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android},
|
||||
Company = "HTC",
|
||||
Desc = "Support the display refresh rate.",
|
||||
|
||||
@@ -8,7 +8,7 @@ using System;
|
||||
using System.Linq;
|
||||
using UnityEngine.XR;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using AOT;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
@@ -45,6 +45,51 @@ namespace VIVE.OpenXR.Hand
|
||||
#region OpenXR Life Cycle
|
||||
private bool m_XrInstanceCreated = false;
|
||||
private XrInstance m_XrInstance = 0;
|
||||
private static IntPtr xrGetInstanceProcAddr_prev;
|
||||
private static IntPtr WaitFrame_prev;
|
||||
private static XrFrameWaitInfo m_frameWaitInfo;
|
||||
private static XrFrameState m_frameState;
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
UnityEngine.Debug.Log("EXT: registering our own xrGetInstanceProcAddr");
|
||||
xrGetInstanceProcAddr_prev = func;
|
||||
return Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||
}
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate))]
|
||||
private static XrResult intercept_xrWaitFrame_xrGetInstanceProcAddr(XrInstance instance, string name, out IntPtr function)
|
||||
{
|
||||
if (xrGetInstanceProcAddr_prev == null || xrGetInstanceProcAddr_prev == IntPtr.Zero)
|
||||
{
|
||||
UnityEngine.Debug.LogError("xrGetInstanceProcAddr_prev is null");
|
||||
function = IntPtr.Zero;
|
||||
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
// Get delegate of old xrGetInstanceProcAddr.
|
||||
var xrGetProc = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrGetInstanceProcAddrDelegate>(xrGetInstanceProcAddr_prev);
|
||||
XrResult result = xrGetProc(instance, name, out function);
|
||||
if (name == "xrWaitFrame")
|
||||
{
|
||||
WaitFrame_prev = function;
|
||||
m_intercept_xrWaitFrame = intercepted_xrWaitFrame;
|
||||
function = Marshal.GetFunctionPointerForDelegate(m_intercept_xrWaitFrame); ;
|
||||
UnityEngine.Debug.Log("Getting xrWaitFrame func");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
[MonoPInvokeCallback(typeof(OpenXRHelper.xrWaitFrameDelegate))]
|
||||
private static int intercepted_xrWaitFrame(ulong session, ref XrFrameWaitInfo frameWaitInfo, ref XrFrameState frameState)
|
||||
{
|
||||
// Get delegate of prev xrWaitFrame.
|
||||
var xrWaitFrame = Marshal.GetDelegateForFunctionPointer<OpenXRHelper.xrWaitFrameDelegate>(WaitFrame_prev);
|
||||
int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState);
|
||||
m_frameWaitInfo = frameWaitInfo;
|
||||
m_frameState = frameState;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreateInstance">xrCreateInstance</see> is done.
|
||||
/// </summary>
|
||||
@@ -276,6 +321,9 @@ namespace VIVE.OpenXR.Hand
|
||||
#endregion
|
||||
|
||||
#region OpenXR function delegates
|
||||
private static readonly OpenXRHelper.xrGetInstanceProcAddrDelegate m_intercept_xrWaitFrame_xrGetInstanceProcAddr
|
||||
= new OpenXRHelper.xrGetInstanceProcAddrDelegate(intercept_xrWaitFrame_xrGetInstanceProcAddr);
|
||||
private static OpenXRHelper.xrWaitFrameDelegate m_intercept_xrWaitFrame;
|
||||
/// xrGetInstanceProcAddr
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
|
||||
@@ -747,7 +795,7 @@ namespace VIVE.OpenXR.Hand
|
||||
in_type: XrStructureType.XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT,
|
||||
in_next: IntPtr.Zero,
|
||||
in_baseSpace: baseSpace,
|
||||
in_time: 1);//
|
||||
in_time: m_frameState.predictedDisplayTime);
|
||||
|
||||
/// Configures XrHandJointLocationsEXT
|
||||
locations.type = XrStructureType.XR_TYPE_HAND_JOINT_LOCATIONS_EXT;
|
||||
|
||||
@@ -4,6 +4,9 @@ using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using UnityEngine;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
@@ -14,7 +17,7 @@ namespace VIVE.OpenXR
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Path Enumeration",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone },
|
||||
Company = "HTC",
|
||||
Desc = "The extension provides more flexibility for the user paths and input/output source paths related to an interaction profile. Developers can use this extension to obtain the path that the user has decided on.",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
@@ -34,7 +37,7 @@ namespace VIVE.OpenXR
|
||||
}
|
||||
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
|
||||
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
|
||||
|
||||
void ERROR(StringBuilder msg) { Debug.LogError(msg); }
|
||||
/// <summary>
|
||||
/// OpenXR specification <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTC_path_enumeration">12.1. XR_HTC_path_enumeration</see>.
|
||||
/// </summary>
|
||||
@@ -66,7 +69,7 @@ namespace VIVE.OpenXR
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(m_XrInstance); DEBUG(sb);
|
||||
|
||||
GetXrFunctionDelegates(m_XrInstance);
|
||||
return base.OnInstanceCreate(xrInstance);
|
||||
}
|
||||
|
||||
@@ -118,5 +121,183 @@ namespace VIVE.OpenXR
|
||||
m_XrSessionCreated = false;
|
||||
}
|
||||
#endregion
|
||||
#region OpenXR function delegates
|
||||
/// xrGetInstanceProcAddr
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate xrEnumeratePathsForInteractionProfileHTC;
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
// xrGetInstanceProcAddr
|
||||
if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrGetInstanceProcAddr."); DEBUG(sb);
|
||||
XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer(
|
||||
xrGetInstanceProcAddr,
|
||||
typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrGetInstanceProcAddr"); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
/// xrEnumeratePathsForInteractionProfileHTC
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrEnumeratePathsForInteractionProfileHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumeratePathsForInteractionProfileHTC."); DEBUG(sb);
|
||||
xrEnumeratePathsForInteractionProfileHTC = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate)) as VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC.");
|
||||
ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC");
|
||||
ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private List<T> CreateList<T>(UInt32 count, T initialValue)
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
for (int i = 0; i < count; i++)
|
||||
list.Add(initialValue);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public XrResult EnumeratePathsForInteractionProfileHTC(
|
||||
ref XrPathsForInteractionProfileEnumerateInfoHTC createInfo,
|
||||
UInt32 pathCapacityInput,
|
||||
ref UInt32 pathCountOutput,
|
||||
[In, Out] XrPath[] paths)
|
||||
{
|
||||
if (!m_XrInstanceCreated)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("EnumeratePathsForInteractionProfileHTC() XR_ERROR_INSTANCE_LOST."); ERROR(sb);
|
||||
paths = null;
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
return xrEnumeratePathsForInteractionProfileHTC(m_XrInstance,
|
||||
ref createInfo, pathCapacityInput,ref pathCountOutput, paths);
|
||||
}
|
||||
|
||||
public bool GetUserPaths(string interactionProfileString, out XrPath[] userPaths)
|
||||
{
|
||||
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||
if (!m_XrInstanceCreated) { userPaths = null; return false; }
|
||||
|
||||
string func = "GetUserPaths() ";
|
||||
|
||||
if (xrEnumeratePathsForInteractionProfileHTC == null)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC"); WARNING(sb);
|
||||
userPaths = null;
|
||||
return false;
|
||||
}
|
||||
// 1. Get user path count of sepecified profile.
|
||||
UInt32 trackerCount = 0;
|
||||
enumerateInfo.type = (XrStructureType)1000319000;//Todo : update openxr spec to prevent hot code.
|
||||
enumerateInfo.next = IntPtr.Zero;
|
||||
enumerateInfo.interactionProfile = StringToPath(interactionProfileString);
|
||||
enumerateInfo.userPath = OpenXRHelper.XR_NULL_PATH;
|
||||
|
||||
XrResult result = xrEnumeratePathsForInteractionProfileHTC(m_XrInstance, ref enumerateInfo, 0, ref trackerCount, null);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Retrieves trackerCount failed."); ERROR(sb);
|
||||
userPaths = null;
|
||||
return false;
|
||||
}
|
||||
//sb.Clear().Append(LOG_TAG).Append(func)
|
||||
// .Append("Get profile ").Append(interactionProfileString).Append(" user path count: ").Append(trackerCount); DEBUG(sb);
|
||||
if (trackerCount > 0)
|
||||
{
|
||||
// 2. Get user paths of sepecified profile.
|
||||
List<XrPath> trackerList = CreateList<XrPath>(trackerCount, OpenXRHelper.XR_NULL_PATH);
|
||||
XrPath[] trackers = trackerList.ToArray();
|
||||
result = xrEnumeratePathsForInteractionProfileHTC(
|
||||
m_XrInstance,
|
||||
ref enumerateInfo,
|
||||
pathCapacityInput: (UInt32)(trackers.Length & 0x7FFFFFFF),
|
||||
pathCountOutput: ref trackerCount,
|
||||
paths: trackers);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Retrieves trackers failed."); ERROR(sb);
|
||||
userPaths = null;
|
||||
return false;
|
||||
}
|
||||
userPaths = trackers;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
userPaths = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetInputPathsWithUserPath(string interactionProfileString, XrPath userPath, out XrPath[] inputPaths)
|
||||
{
|
||||
string func = "GetInputPathsWithUserPath() ";
|
||||
if (!m_XrInstanceCreated) { inputPaths = null; return false; }
|
||||
UInt32 trackerCount = 0;
|
||||
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||
enumerateInfo.type = (XrStructureType)1000319000;//Todo : update openxr spec and prevent hard-code.
|
||||
enumerateInfo.next = IntPtr.Zero;
|
||||
enumerateInfo.interactionProfile = StringToPath(interactionProfileString);
|
||||
enumerateInfo.userPath = userPath;
|
||||
UInt32 Count = 0;
|
||||
xrEnumeratePathsForInteractionProfileHTC(
|
||||
m_XrInstance,
|
||||
ref enumerateInfo,
|
||||
0,
|
||||
pathCountOutput: ref Count,
|
||||
paths: null);
|
||||
if (Count > 0)
|
||||
{
|
||||
List<XrPath> pathlist = CreateList<XrPath>(Count, OpenXRHelper.XR_NULL_PATH);
|
||||
inputPaths = pathlist.ToArray();
|
||||
XrResult result = xrEnumeratePathsForInteractionProfileHTC(
|
||||
m_XrInstance,
|
||||
ref enumerateInfo,
|
||||
pathCapacityInput: (UInt32)(inputPaths.Length & 0x7FFFFFFF),
|
||||
pathCountOutput: ref Count,
|
||||
paths: inputPaths);
|
||||
UnityEngine.Debug.Log("Get inputpath str : "+PathToString(inputPaths[0]));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputPaths = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public string xrPathToString(ulong path)
|
||||
{
|
||||
return PathToString(path);
|
||||
}
|
||||
|
||||
public ulong xrStringToPath(string str)
|
||||
{
|
||||
return StringToPath(str);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33a547f8c3209594c84b8a4a968d8073
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 226c5d25c53e5794baacc000d2eaf274
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9209a4fdd88b4bd4e88afcf05e69cdfd
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 174d574cfa752ab4e9c6354d2a6c14c4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,530 @@
|
||||
// Copyright HTC Corporation All Rights Reserved.
|
||||
#if UNITY_EDITOR
|
||||
#define FAKE_DATA
|
||||
#endif
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.OpenXR;
|
||||
using UnityEngine.XR.OpenXR.Features;
|
||||
using VIVE.OpenXR.Feature;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
#endif
|
||||
|
||||
namespace VIVE.OpenXR.PlaneDetection
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR PlaneDetection",
|
||||
Desc = "VIVE's implementaion of the XR_EXT_plane_detection.",
|
||||
Company = "HTC",
|
||||
DocumentationLink = "..\\Documentation",
|
||||
OpenxrExtensionStrings = kOpenxrExtensionString,
|
||||
Version = "1.0.0",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
FeatureId = featureId
|
||||
)]
|
||||
#endif
|
||||
public class VivePlaneDetection : OpenXRFeature
|
||||
{
|
||||
public const string kOpenxrExtensionString = "XR_EXT_plane_detection";
|
||||
/// <summary>
|
||||
/// The feature id string. This is used to give the feature a well known id for reference.
|
||||
/// </summary>
|
||||
public const string featureId = "vive.wave.openxr.feature.planedetection";
|
||||
private bool m_XrInstanceCreated = false;
|
||||
private XrInstance m_XrInstance = 0;
|
||||
private bool m_XrSessionCreated = false;
|
||||
private XrSession session = 0;
|
||||
private XrSystemId m_XrSystemId = 0;
|
||||
|
||||
|
||||
#region struct, enum, const of this extensions
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorOrientationEXT">XrPlaneDetectorOrientationEXT</see>
|
||||
/// </summary>
|
||||
public enum XrPlaneDetectorOrientationEXT
|
||||
{
|
||||
HORIZONTAL_UPWARD_EXT = 0,
|
||||
HORIZONTAL_DOWNWARD_EXT = 1,
|
||||
VERTICAL_EXT = 2,
|
||||
ARBITRARY_EXT = 3,
|
||||
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorSemanticTypeEXT">XrPlaneDetectorSemanticTypeEXT</see>
|
||||
/// </summary>
|
||||
public enum XrPlaneDetectorSemanticTypeEXT
|
||||
{
|
||||
UNDEFINED_EXT = 0,
|
||||
CEILING_EXT = 1,
|
||||
FLOOR_EXT = 2,
|
||||
WALL_EXT = 3,
|
||||
PLATFORM_EXT = 4,
|
||||
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectionStateEXT">XrPlaneDetectionStateEXT</see>
|
||||
/// </summary>
|
||||
public enum XrPlaneDetectionStateEXT
|
||||
{
|
||||
NONE_EXT = 0,
|
||||
PENDING_EXT = 1, // Try get plane detection state again
|
||||
DONE_EXT = 2, // Ready to get result
|
||||
ERROR_EXT = 3, // Can try begin again
|
||||
FATAL_EXT = 4, // Should destroy the plane detector
|
||||
MAX_ENUM_EXT = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
//XrFlags64 XrPlaneDetectionCapabilityFlagsEXT;
|
||||
|
||||
// Flag bits for XrPlaneDetectionCapabilityFlagsEXT
|
||||
public static XrFlags64 CAPABILITY_PLANE_DETECTION_BIT_EXT = 0x00000001;
|
||||
public static XrFlags64 CAPABILITY_PLANE_HOLES_BIT_EXT = 0x00000002;
|
||||
public static XrFlags64 CAPABILITY_SEMANTIC_CEILING_BIT_EXT = 0x00000004;
|
||||
public static XrFlags64 CAPABILITY_SEMANTIC_FLOOR_BIT_EXT = 0x00000008;
|
||||
public static XrFlags64 CAPABILITY_SEMANTIC_WALL_BIT_EXT = 0x00000010;
|
||||
public static XrFlags64 CAPABILITY_SEMANTIC_PLATFORM_BIT_EXT = 0x00000020;
|
||||
public static XrFlags64 CAPABILITY_ORIENTATION_BIT_EXT = 0x00000040;
|
||||
|
||||
//XrFlags64 XrPlaneDetectorFlagsEXT;
|
||||
|
||||
// Flag bits for XrPlaneDetectorFlagsEXT
|
||||
public static XrFlags64 XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT = 0x00000001;
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrSystemPlaneDetectionPropertiesEXT">XrSystemPlaneDetectionPropertiesEXT</see>
|
||||
/// </summary>
|
||||
public struct XrSystemPlaneDetectionPropertiesEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrFlags64 supportedFeatures; // XrPlaneDetectionCapabilityFlagsEXT
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorCreateInfoEXT">XrPlaneDetectorCreateInfoEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorCreateInfoEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrFlags64 flags; // XrPlaneDetectorFlagsEXT
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrExtent3DfEXT">XrExtent3DfEXT</see>
|
||||
/// </summary>
|
||||
public struct XrExtent3DfEXT
|
||||
{
|
||||
public float width;
|
||||
public float height;
|
||||
public float depth;
|
||||
public XrExtent3DfEXT(float width, float height, float depth)
|
||||
{
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
}
|
||||
public XrExtent3DfEXT One => new XrExtent3DfEXT(1, 1, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorBeginInfoEXT">XrPlaneDetectorBeginInfoEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorBeginInfoEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrSpace baseSpace;
|
||||
public XrTime time;
|
||||
public uint orientationCount;
|
||||
public IntPtr orientations; // XrPlaneDetectorOrientationEXT[]
|
||||
public uint semanticTypeCount;
|
||||
public IntPtr semanticTypes; // XrPlaneDetectorSemanticTypeEXT[]
|
||||
public uint maxPlanes;
|
||||
public float minArea;
|
||||
public XrPosef boundingBoxPose;
|
||||
public XrExtent3DfEXT boundingBoxExtent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorGetInfoEXT">XrPlaneDetectorGetInfoEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorGetInfoEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public XrSpace baseSpace;
|
||||
public XrTime time;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorLocationEXT">XrPlaneDetectorLocationEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorLocationEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public ulong planeId;
|
||||
public XrSpaceLocationFlags locationFlags;
|
||||
public XrPosef pose;
|
||||
public XrExtent2Df extents;
|
||||
public XrPlaneDetectorOrientationEXT orientation;
|
||||
public XrPlaneDetectorSemanticTypeEXT semanticType;
|
||||
public uint polygonBufferCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorLocationsEXT">XrPlaneDetectorLocationsEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorLocationsEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public uint planeLocationCapacityInput;
|
||||
public uint planeLocationCountOutput;
|
||||
public IntPtr planeLocations; // XrPlaneDetectorLocationEXT[]
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XrPlaneDetectorPolygonBufferEXT">XrPlaneDetectorPolygonBufferEXT</see>
|
||||
/// </summary>
|
||||
public struct XrPlaneDetectorPolygonBufferEXT
|
||||
{
|
||||
public XrStructureType type;
|
||||
public IntPtr next;
|
||||
public uint vertexCapacityInput;
|
||||
public uint vertexCountOutput;
|
||||
public IntPtr vertices; // XrVector2f[]
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region delegates and delegate instances
|
||||
|
||||
delegate XrResult DelegateXrCreatePlaneDetectorEXT(XrSession session, ref XrPlaneDetectorCreateInfoEXT createInfo, ref IntPtr/*XrPlaneDetectorEXT*/
|
||||
planeDetector);
|
||||
delegate XrResult DelegateXrDestroyPlaneDetectorEXT(IntPtr/*XrPlaneDetectorEXT*/ planeDetector);
|
||||
delegate XrResult DelegateXrBeginPlaneDetectionEXT(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectorBeginInfoEXT beginInfo);
|
||||
delegate XrResult DelegateXrGetPlaneDetectionStateEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ref XrPlaneDetectionStateEXT state);
|
||||
delegate XrResult DelegateXrGetPlaneDetectionsEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ref XrPlaneDetectorGetInfoEXT info, ref XrPlaneDetectorLocationsEXT locations);
|
||||
delegate XrResult DelegateXrGetPlanePolygonBufferEXT(IntPtr/*XrPlaneDetectorEXT*/planeDetector, ulong planeId, uint polygonBufferIndex, ref XrPlaneDetectorPolygonBufferEXT polygonBuffer);
|
||||
|
||||
DelegateXrCreatePlaneDetectorEXT XrCreatePlaneDetectorEXT;
|
||||
DelegateXrDestroyPlaneDetectorEXT XrDestroyPlaneDetectorEXT;
|
||||
DelegateXrBeginPlaneDetectionEXT XrBeginPlaneDetectionEXT;
|
||||
DelegateXrGetPlaneDetectionStateEXT XrGetPlaneDetectionStateEXT;
|
||||
DelegateXrGetPlaneDetectionsEXT XrGetPlaneDetectionsEXT;
|
||||
DelegateXrGetPlanePolygonBufferEXT XrGetPlanePolygonBufferEXT;
|
||||
#endregion delegates and delegate instances
|
||||
|
||||
#region override functions
|
||||
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
|
||||
{
|
||||
return ViveInterceptors.Instance.HookGetInstanceProcAddr(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool OnInstanceCreate(ulong xrInstance)
|
||||
{
|
||||
//Debug.Log("VIVEPD OnInstanceCreate() ");
|
||||
if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString))
|
||||
{
|
||||
Debug.LogWarning("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_XrInstanceCreated = true;
|
||||
m_XrInstance = xrInstance;
|
||||
//Debug.Log("OnInstanceCreate() " + m_XrInstance);
|
||||
|
||||
CommonWrapper.Instance.OnInstanceCreate(xrInstance, xrGetInstanceProcAddr);
|
||||
SpaceWrapper.Instance.OnInstanceCreate(xrInstance, CommonWrapper.Instance.GetInstanceProcAddr);
|
||||
|
||||
return GetXrFunctionDelegates(m_XrInstance);
|
||||
}
|
||||
|
||||
protected override void OnInstanceDestroy(ulong xrInstance)
|
||||
{
|
||||
CommonWrapper.Instance.OnInstanceDestroy();
|
||||
SpaceWrapper.Instance.OnInstanceDestroy();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionCreate(ulong xrSession)
|
||||
{
|
||||
Debug.Log("VIVEPD OnSessionCreate() ");
|
||||
|
||||
// here's one way you can grab the session
|
||||
Debug.Log($"EXT: Got xrSession: {xrSession}");
|
||||
session = xrSession;
|
||||
m_XrSessionCreated = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionBegin(ulong xrSession)
|
||||
{
|
||||
Debug.Log("VIVEPD OnSessionBegin() ");
|
||||
|
||||
Debug.Log($"EXT: xrBeginSession: {xrSession}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnSessionEnd(ulong xrSession)
|
||||
{
|
||||
Debug.Log("VIVEPD OnSessionEnd() ");
|
||||
|
||||
Debug.Log($"EXT: about to xrEndSession: {xrSession}");
|
||||
}
|
||||
|
||||
// XXX Every millisecond the AppSpace switched from one space to another space. I don't know what is going on.
|
||||
//private ulong appSpace;
|
||||
//protected override void OnAppSpaceChange(ulong space)
|
||||
//{
|
||||
// Debug.Log($"VIVEPD OnAppSpaceChange({appSpace} -> {space})");
|
||||
// appSpace = space;
|
||||
//}
|
||||
|
||||
protected override void OnSystemChange(ulong xrSystem)
|
||||
{
|
||||
m_XrSystemId = xrSystem;
|
||||
Debug.Log("OnSystemChange() " + m_XrSystemId);
|
||||
}
|
||||
|
||||
|
||||
#endregion override functions
|
||||
|
||||
private bool GetXrFunctionDelegates(XrInstance xrInstance)
|
||||
{
|
||||
Debug.Log("VIVEPD GetXrFunctionDelegates() ");
|
||||
|
||||
bool ret = true;
|
||||
IntPtr funcPtr = IntPtr.Zero;
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate GetAddr = CommonWrapper.Instance.GetInstanceProcAddr; // shorter name
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrCreatePlaneDetectorEXT", out XrCreatePlaneDetectorEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrDestroyPlaneDetectorEXT", out XrDestroyPlaneDetectorEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrBeginPlaneDetectionEXT", out XrBeginPlaneDetectionEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlaneDetectionStateEXT", out XrGetPlaneDetectionStateEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlaneDetectionsEXT", out XrGetPlaneDetectionsEXT);
|
||||
ret &= OpenXRHelper.GetXrFunctionDelegate(GetAddr, xrInstance, "xrGetPlanePolygonBufferEXT", out XrGetPlanePolygonBufferEXT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#region functions of extension
|
||||
/// <summary>
|
||||
/// Helper function to get this feature' properties.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetSystemProperties">xrGetSystemProperties</see>
|
||||
/// </summary>
|
||||
public XrResult GetProperties(out XrSystemPlaneDetectionPropertiesEXT properties)
|
||||
{
|
||||
properties = new XrSystemPlaneDetectionPropertiesEXT();
|
||||
properties.type = XrStructureType.XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT;
|
||||
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
properties.supportedFeatures = CAPABILITY_PLANE_DETECTION_BIT_EXT;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
if (!m_XrSessionCreated)
|
||||
{
|
||||
Debug.LogError("GetProperties() XR_ERROR_SESSION_LOST.");
|
||||
return XrResult.XR_ERROR_SESSION_LOST;
|
||||
}
|
||||
if (!m_XrInstanceCreated)
|
||||
{
|
||||
Debug.LogError("GetProperties() XR_ERROR_INSTANCE_LOST.");
|
||||
return XrResult.XR_ERROR_INSTANCE_LOST;
|
||||
}
|
||||
|
||||
return CommonWrapper.Instance.GetProperties(m_XrInstance, m_XrSystemId, ref properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PlaneDetector with <paramref name="createInfo"/>. XrSession is implied. The output handle need be destroyed.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrCreatePlaneDetectorEXT">xrCreatePlaneDetectorEXT</see>
|
||||
/// </summary>
|
||||
/// <param name="createInfo">Fill flags for detection engine.</param>
|
||||
/// <param name="planeDetector">The output detector's handle.</param>
|
||||
/// <seealso cref="DestroyPlaneDetector"/>
|
||||
public XrResult CreatePlaneDetector(XrPlaneDetectorCreateInfoEXT createInfo, out IntPtr/*XrPlaneDetectorEXT*/ planeDetector)
|
||||
{
|
||||
planeDetector = IntPtr.Zero;
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
return XrResult.XR_SUCCESS;
|
||||
#endif
|
||||
return XrCreatePlaneDetectorEXT(session, ref createInfo, ref planeDetector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroy the PlaneDetector handle.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrDestroyPlaneDetectorEXT">xrDestroyPlaneDetectorEXT</see>
|
||||
/// </summary>
|
||||
/// <param name="planeDetector">The detector's handle to be destroyed.</param>
|
||||
/// <seealso cref="CreatePlaneDetector"/>
|
||||
public XrResult DestroyPlaneDetector(IntPtr/*XrPlaneDetectorEXT*/ planeDetector)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
return XrResult.XR_SUCCESS;
|
||||
#endif
|
||||
return XrDestroyPlaneDetectorEXT(planeDetector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let Detector start to work.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrBeginPlaneDetectionEXT">xrBeginPlaneDetectionEXT</see>
|
||||
/// </summary>
|
||||
/// <param name="planeDetector">The detector's handle to be begined.</param>
|
||||
/// <param name="beginInfo">Fill flags for detection engine. You can use the result of MakeGetAllXrPlaneDetectorBeginInfoEXT.</param>
|
||||
/// <seealso cref="MakeGetAllXrPlaneDetectorBeginInfoEXT"/>
|
||||
public XrResult BeginPlaneDetection(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, XrPlaneDetectorBeginInfoEXT beginInfo)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
return XrResult.XR_SUCCESS;
|
||||
#endif
|
||||
return XrBeginPlaneDetectionEXT(planeDetector, ref beginInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check PlaneDetector's state. If the state is DONE_EXT, you can get the result by GetPlaneDetections.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlaneDetectionStateEXT">xrGetPlaneDetectionStateEXT</see>
|
||||
/// </summary>
|
||||
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||
/// <param name="state">Fill flags for detection engine. You can use the result of MakeGetAllXrPlaneDetectorBeginInfoEXT.</param>
|
||||
public XrResult GetPlaneDetectionState(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectionStateEXT state)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
state = XrPlaneDetectionStateEXT.DONE_EXT;
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return XrGetPlaneDetectionStateEXT(planeDetector, ref state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the result of PlaneDetector.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlaneDetectionsEXT">xrGetPlaneDetectionsEXT</see>
|
||||
/// </summary>
|
||||
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||
/// <param name="info">Use info to specify the data's space.</param>
|
||||
/// <param name="locations">The output data.</param>
|
||||
public XrResult GetPlaneDetections(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ref XrPlaneDetectorGetInfoEXT info, ref XrPlaneDetectorLocationsEXT locations)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
locations.planeLocationCountOutput = 1;
|
||||
if (locations.planeLocationCapacityInput == 0)
|
||||
return XrResult.XR_SUCCESS;
|
||||
if (locations.planeLocationCapacityInput < 1 || locations.planeLocations == IntPtr.Zero)
|
||||
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||
|
||||
locations.planeLocationCountOutput = 1;
|
||||
XrPlaneDetectorLocationEXT location = new XrPlaneDetectorLocationEXT();
|
||||
location.planeId = 1;
|
||||
location.extents = new XrExtent2Df(1, 1);
|
||||
location.locationFlags = XrSpaceLocationFlags.XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XrSpaceLocationFlags.XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
location.semanticType = XrPlaneDetectorSemanticTypeEXT.FLOOR_EXT;
|
||||
location.polygonBufferCount = 1;
|
||||
location.pose = new XrPosef(XrQuaternionf.Identity, new XrVector3f(0, 1, -1)); // This plane will face the Z axis
|
||||
location.orientation = XrPlaneDetectorOrientationEXT.VERTICAL_EXT;
|
||||
|
||||
Marshal.StructureToPtr(location, locations.planeLocations, false);
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return XrGetPlaneDetectionsEXT(planeDetector, ref info, ref locations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the vertex buffer of a plane.
|
||||
/// See <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrGetPlanePolygonBufferEXT">GetPlanePolygonBuffer</see>
|
||||
/// </summary>
|
||||
/// <param name="planeDetector">The detector's state to be check.</param>
|
||||
/// <param name="planeId">The target plane's planeId. Get it from <see cref="XrPlaneDetectorLocationEXT" />.</param>
|
||||
/// <param name="polygonBufferIndex">The buffer index in the plane. Get it from <see cref="XrPlaneDetectorLocationEXT" />. Currently VIVE will only return 1 buffer for each plane.</param>
|
||||
/// <param name="polygonBuffer">The output data.</param>
|
||||
public XrResult GetPlanePolygonBuffer(IntPtr/*XrPlaneDetectorEXT*/ planeDetector, ulong planeId, uint polygonBufferIndex, ref XrPlaneDetectorPolygonBufferEXT polygonBuffer)
|
||||
{
|
||||
#if FAKE_DATA
|
||||
if (Application.isEditor)
|
||||
{
|
||||
if (planeId != 1) return XrResult.XR_ERROR_NAME_INVALID;
|
||||
if (polygonBufferIndex != 0) return XrResult.XR_ERROR_INDEX_OUT_OF_RANGE;
|
||||
polygonBuffer.vertexCountOutput = 4;
|
||||
if (polygonBuffer.vertexCapacityInput == 0)
|
||||
return XrResult.XR_SUCCESS;
|
||||
if (polygonBuffer.vertexCapacityInput != 4 || polygonBuffer.vertices == IntPtr.Zero)
|
||||
return XrResult.XR_ERROR_SIZE_INSUFFICIENT;
|
||||
XrVector2f[] vertices = new XrVector2f[4];
|
||||
// Make a plane's contour
|
||||
vertices[0] = new XrVector2f(-0.5f, -0.5f);
|
||||
vertices[1] = new XrVector2f( 0.5f, -0.5f);
|
||||
vertices[2] = new XrVector2f( 0.5f, 0.5f);
|
||||
vertices[3] = new XrVector2f(-0.5f, 0.5f);
|
||||
|
||||
MemoryTools.CopyToRawMemory(polygonBuffer.vertices, vertices);
|
||||
|
||||
return XrResult.XR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return XrGetPlanePolygonBufferEXT(planeDetector, planeId, polygonBufferIndex, ref polygonBuffer);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region tools for user
|
||||
/// <summary>
|
||||
/// A helper function to generate XrPlaneDetectorBeginInfoEXT. VIVE didn't implement all possible features of this extension.
|
||||
/// Hardcode some parameters here.
|
||||
/// </summary>
|
||||
/// <seealso cref="BeginPlaneDetection"/>
|
||||
public XrPlaneDetectorBeginInfoEXT MakeGetAllXrPlaneDetectorBeginInfoEXT()
|
||||
{
|
||||
XrPlaneDetectorBeginInfoEXT beginInfo = new XrPlaneDetectorBeginInfoEXT
|
||||
{
|
||||
type = XrStructureType.XR_TYPE_PLANE_DETECTOR_BEGIN_INFO_EXT,
|
||||
baseSpace = new XrSpace(GetCurrentAppSpace()), // Cannot depend on GetCurrentAppSpace...
|
||||
//baseSpace = GetTrackingSpace(),
|
||||
time = ViveInterceptors.Instance.GetPredictTime(),
|
||||
orientationCount = 0, // Any orientation
|
||||
orientations = IntPtr.Zero, // XrPlaneDetectorOrientationEXT[]
|
||||
semanticTypeCount = 0, // Any semantic type
|
||||
semanticTypes = IntPtr.Zero, // XrPlaneDetectorSemanticTypeEXT[]
|
||||
maxPlanes = 10000, // Hopefully enough
|
||||
minArea = 0.1f, // 10cm^2
|
||||
boundingBoxPose = XrPosef.Identity,
|
||||
boundingBoxExtent = new XrExtent3DfEXT(1000, 1000, 1000),
|
||||
};
|
||||
return beginInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The time here is only used for info of GetPlaneDetections. Not the real predictTime of XrWaitFrame.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrTime GetPredictTime()
|
||||
{
|
||||
return ViveInterceptors.Instance.GetPredictTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// According to XRInputSubsystem's tracking origin mode, return the corresponding XrSpace.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public XrSpace GetTrackingSpace()
|
||||
{
|
||||
return GetCurrentAppSpace();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbc636a69caaad0418c5e52e22103f2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -20,6 +20,8 @@ using UnityEngine.XR.OpenXR.Input;
|
||||
using UnityEditor;
|
||||
using UnityEditor.XR.OpenXR.Features;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
#endif
|
||||
|
||||
#if USE_INPUT_SYSTEM_POSE_CONTROL // Scripting Define Symbol added by using OpenXR Plugin 1.6.0.
|
||||
@@ -34,7 +36,7 @@ namespace VIVE.OpenXR.Tracker
|
||||
/// This <see cref="OpenXRInteractionFeature"/> enables the use of tracker interaction profiles in OpenXR. It enables XR_HTC_vive_xr_tracker_interaction in the underyling runtime.
|
||||
/// </summary>
|
||||
#if UNITY_EDITOR
|
||||
[OpenXRFeature(UiName = "VIVE XR Tracker",
|
||||
[OpenXRFeature(UiName = "VIVE XR Tracker (Beta)",
|
||||
BuildTargetGroups = new[] { BuildTargetGroup.Android },
|
||||
Company = "HTC",
|
||||
Desc = "Support for enabling the vive xr tracker interaction profile. Will register the controller map for xr tracker if enabled.",
|
||||
@@ -72,16 +74,25 @@ namespace VIVE.OpenXR.Tracker
|
||||
|
||||
#region Tracker Product Name
|
||||
public const string kProductUltimateTracker = "VIVE Ultimate Tracker";
|
||||
public const string kProductTrackingTag = "VIVE Tracking Tag";
|
||||
public const string kProductUltimateTracker0 = "VIVE Ultimate Tracker 0";
|
||||
public const string kProductUltimateTracker1 = "VIVE Ultimate Tracker 1";
|
||||
public const string kProductUltimateTracker2 = "VIVE Ultimate Tracker 2";
|
||||
public const string kProductUltimateTracker3 = "VIVE Ultimate Tracker 3";
|
||||
public const string kProductUltimateTracker4 = "VIVE Ultimate Tracker 4";
|
||||
const string kProductTrackingTag = "VIVE Tracking Tag";
|
||||
private const string kProducts = "^(" + kProductUltimateTracker
|
||||
+ ")|^(" + kProductUltimateTracker0 + ")|^(" + kProductUltimateTracker1 + ")|^(" + kProductUltimateTracker2 + ")|^(" + kProductUltimateTracker3 + ")|^(" + kProductUltimateTracker4
|
||||
+ ")|^(" + kProductTrackingTag + ")";
|
||||
private readonly string[] s_UltimateTrackerProduct = { kProductUltimateTracker0, kProductUltimateTracker1, kProductUltimateTracker2, kProductUltimateTracker3, kProductUltimateTracker4 };
|
||||
private bool IsUltimateTracker(string product)
|
||||
{
|
||||
for (int i = 0; i < s_UltimateTrackerProduct.Length; i++)
|
||||
{
|
||||
if (s_UltimateTrackerProduct[i].Equals(product))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Tracker Action Map Name
|
||||
@@ -93,32 +104,11 @@ namespace VIVE.OpenXR.Tracker
|
||||
#endregion
|
||||
|
||||
#region Tracker Usage
|
||||
const string kTrackerUsage0 = "Tracker 0";
|
||||
const string kTrackerUsage1 = "Tracker 1";
|
||||
const string kTrackerUsage2 = "Tracker 2";
|
||||
const string kTrackerUsage3 = "Tracker 3";
|
||||
const string kTrackerUsage4 = "Tracker 4";
|
||||
|
||||
public const string kTrackerRoleUndefined = "Undefined";
|
||||
public const string kTrackerRoleWaist = "Waist";
|
||||
public const string kTrackerRoleChest = "Chest";
|
||||
public const string kTrackerRoleLeftElbow = "Left Elbow";
|
||||
public const string kTrackerRoleLeftWrist = "Left Wrist";
|
||||
public const string kTrackerRoleLeftKnee = "Left Knee";
|
||||
public const string kTrackerRoleLeftAnkle = "Left Ankle";
|
||||
public const string kTrackerRoleLeftFoot = "Left Foot";
|
||||
|
||||
public const string kTrackerRoleRightElbow = "Right Elbow";
|
||||
public const string kTrackerRoleRightWrist = "Right Wrist";
|
||||
public const string kTrackerRoleRightKnee = "Right Knee";
|
||||
public const string kTrackerRoleRightAnkle = "Right Ankle";
|
||||
public const string kTrackerRoleRightFoot = "Right Foot";
|
||||
private readonly List<string> s_TrackerRoleName = new List<string>()
|
||||
{
|
||||
kTrackerRoleWaist, kTrackerRoleChest,
|
||||
kTrackerRoleLeftElbow, kTrackerRoleLeftWrist, kTrackerRoleLeftKnee, kTrackerRoleLeftAnkle, kTrackerRoleLeftFoot,
|
||||
kTrackerRoleRightElbow, kTrackerRoleRightWrist, kTrackerRoleRightKnee, kTrackerRoleRightAnkle, kTrackerRoleRightFoot,
|
||||
};
|
||||
const string kUltimateTrackerUsage0 = "Ultimate Tracker 0";
|
||||
const string kUltimateTrackerUsage1 = "Ultimate Tracker 1";
|
||||
const string kUltimateTrackerUsage2 = "Ultimate Tracker 2";
|
||||
const string kUltimateTrackerUsage3 = "Ultimate Tracker 3";
|
||||
const string kUltimateTrackerUsage4 = "Ultimate Tracker 4";
|
||||
#endregion
|
||||
|
||||
#region Tracker User Path
|
||||
@@ -135,19 +125,13 @@ namespace VIVE.OpenXR.Tracker
|
||||
public const string kUltimateTrackerSN2 = "VIVE_Ultimate_Tracker_2";
|
||||
public const string kUltimateTrackerSN3 = "VIVE_Ultimate_Tracker_3";
|
||||
public const string kUltimateTrackerSN4 = "VIVE_Ultimate_Tracker_4";
|
||||
public const string kTrackingTagSN0 = "VIVE_Tracking_Tag_0";
|
||||
public const string kTrackingTagSN1 = "VIVE_Tracking_Tag_1";
|
||||
public const string kTrackingTagSN2 = "VIVE_Tracking_Tag_2";
|
||||
public const string kTrackingTagSN3 = "VIVE_Tracking_Tag_3";
|
||||
public const string kTrackingTagSN4 = "VIVE_Tracking_Tag_4";
|
||||
public const string k6DoFTrackerSN0 = "VIVE_6DoF_Tracker_a_0";
|
||||
public const string k6DoFTrackerSN1 = "VIVE_6DoF_Tracker_a_1";
|
||||
private readonly List<string> s_TrackerSerialNumber = new List<string>()
|
||||
{
|
||||
kUltimateTrackerSN0, kUltimateTrackerSN1, kUltimateTrackerSN2, kUltimateTrackerSN3, kUltimateTrackerSN4,
|
||||
kTrackingTagSN0, kTrackingTagSN1, kTrackingTagSN2, kTrackingTagSN3, kTrackingTagSN4,
|
||||
k6DoFTrackerSN0, k6DoFTrackerSN1
|
||||
};
|
||||
const string kTrackingTagSN0 = "VIVE_Tracking_Tag_0";
|
||||
const string kTrackingTagSN1 = "VIVE_Tracking_Tag_1";
|
||||
const string kTrackingTagSN2 = "VIVE_Tracking_Tag_2";
|
||||
const string kTrackingTagSN3 = "VIVE_Tracking_Tag_3";
|
||||
const string kTrackingTagSN4 = "VIVE_Tracking_Tag_4";
|
||||
const string k6DoFTrackerSN0 = "VIVE_6DoF_Tracker_a_0";
|
||||
const string k6DoFTrackerSN1 = "VIVE_6DoF_Tracker_a_1";
|
||||
#endregion
|
||||
|
||||
#region Tracker Product Maps
|
||||
@@ -160,11 +144,11 @@ namespace VIVE.OpenXR.Tracker
|
||||
};
|
||||
/// <summary> Mapping from product to tracker usage. </summary>
|
||||
private static Dictionary<string, string> m_UltimateTrackerUsageMap = new Dictionary<string, string>() {
|
||||
{ kProductUltimateTracker0, kTrackerUsage0 },
|
||||
{ kProductUltimateTracker1, kTrackerUsage1 },
|
||||
{ kProductUltimateTracker2, kTrackerUsage2 },
|
||||
{ kProductUltimateTracker3, kTrackerUsage3 },
|
||||
{ kProductUltimateTracker4, kTrackerUsage4 },
|
||||
{ kProductUltimateTracker0, kUltimateTrackerUsage0 },
|
||||
{ kProductUltimateTracker1, kUltimateTrackerUsage1 },
|
||||
{ kProductUltimateTracker2, kUltimateTrackerUsage2 },
|
||||
{ kProductUltimateTracker3, kUltimateTrackerUsage3 },
|
||||
{ kProductUltimateTracker4, kUltimateTrackerUsage4 },
|
||||
};
|
||||
/// <summary> Mapping from product to user path. </summary>
|
||||
private Dictionary<string, string> m_UltimateTrackerPathMap = new Dictionary<string, string>() {
|
||||
@@ -185,13 +169,11 @@ namespace VIVE.OpenXR.Tracker
|
||||
#endregion
|
||||
|
||||
[Preserve, InputControlLayout(displayName = "VIVE XR Tracker (OpenXR)", commonUsages = new[] {
|
||||
kTrackerUsage0, kTrackerUsage1, kTrackerUsage2, kTrackerUsage3, kTrackerUsage4,
|
||||
//kTrackerRoleWaist, kTrackerRoleChest,
|
||||
//kTrackerRoleLeftElbow, kTrackerRoleLeftWrist, kTrackerRoleLeftKnee, kTrackerRoleLeftAnkle, kTrackerRoleLeftFoot,
|
||||
//kTrackerRoleRightElbow, kTrackerRoleRightWrist, kTrackerRoleRightKnee, kTrackerRoleRightAnkle, kTrackerRoleRightFoot,
|
||||
kUltimateTrackerUsage0, kUltimateTrackerUsage1, kUltimateTrackerUsage2, kUltimateTrackerUsage3, kUltimateTrackerUsage4,
|
||||
}, isGenericTypeOfDevice = true)]
|
||||
public class XrTrackerDevice : OpenXRDevice, IInputUpdateCallbackReceiver
|
||||
public class XrTrackerDevice : OpenXRDevice//, IInputUpdateCallbackReceiver
|
||||
{
|
||||
#region Log
|
||||
const string LOG_TAG = "VIVE.OpenXR.Tracker.ViveXRTracker.XrTrackerDevice ";
|
||||
StringBuilder m_sb = null;
|
||||
StringBuilder sb {
|
||||
@@ -201,8 +183,9 @@ namespace VIVE.OpenXR.Tracker
|
||||
}
|
||||
}
|
||||
void DEBUG(StringBuilder msg) { Debug.Log(msg); }
|
||||
void WARNING(StringBuilder msg) { Debug.LogWarning(msg); }
|
||||
#endregion
|
||||
|
||||
#region Interactions
|
||||
/// <summary>
|
||||
/// A <see cref="PoseControl"/> that represents the <see cref="entityPose"/> OpenXR binding. The entity pose represents the location of the tracker.
|
||||
/// </summary>
|
||||
@@ -232,10 +215,12 @@ namespace VIVE.OpenXR.Tracker
|
||||
/// </summary>
|
||||
[Preserve, InputControl(offset = 20, alias = "gripOrientation")]
|
||||
public QuaternionControl deviceRotation { get; private set; }
|
||||
#endregion
|
||||
|
||||
#if DEBUG_CODE
|
||||
// Unity action binding path: <ViveXRTracker>{Tracker 0}/isTracked
|
||||
private bool UpdateInputDeviceInRuntime = true;
|
||||
private InputAction inputActionIsTracked = null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Internal call used to assign controls to the the correct element.
|
||||
@@ -244,50 +229,52 @@ namespace VIVE.OpenXR.Tracker
|
||||
{
|
||||
base.FinishSetup();
|
||||
|
||||
if (!m_UltimateTrackerUsageMap.ContainsKey(description.product))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("FinishSetup() Not support the product " + description.product); WARNING(sb);
|
||||
return;
|
||||
}
|
||||
|
||||
devicePose = GetChildControl<PoseControl>("devicePose");
|
||||
isTracked = GetChildControl<ButtonControl>("isTracked");
|
||||
trackingState = GetChildControl<IntegerControl>("trackingState");
|
||||
devicePosition = GetChildControl<Vector3Control>("devicePosition");
|
||||
deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");
|
||||
|
||||
/// After RegisterActionMapsWithRuntime finished, each XrTrackerDevice will have a product name (e.g. kProductUltimateTracker0)
|
||||
/// We have to assign the XrTrackerDeivce to a commonUsage (e.g. kTrackerUsage0)
|
||||
///
|
||||
/// Since we already established the m_UltimateTrackerUsageMap (kProductUltimateTracker0, kTrackerUsage0),
|
||||
/// we can simply call SetDeviceUsage(m_UltimateTrackerUsageMap[description.product])
|
||||
/// to set assign the XrTrackerDevice with product name kProductUltimateTracker0 to the commonUsage kTrackerUsage0.
|
||||
InputSystem.SetDeviceUsage(this, m_UltimateTrackerUsageMap[description.product]);
|
||||
|
||||
if (inputActionIsTracked == null)
|
||||
{
|
||||
//string actionBindingIsTracked = "<XRController>{LeftHand}/isTracked";
|
||||
string actionBindingIsTracked = "<" + kLayoutName + ">{" + m_UltimateTrackerUsageMap[description.product] + "}/isTracked";
|
||||
sb.Clear().Append(LOG_TAG).Append("FinishSetup() ").Append(m_UltimateTrackerUsageMap[description.product]).Append(", acion binding of IsTracked: ").Append(actionBindingIsTracked);
|
||||
DEBUG(sb);
|
||||
|
||||
inputActionIsTracked = new InputAction(
|
||||
type: InputActionType.Value,
|
||||
binding: actionBindingIsTracked);
|
||||
|
||||
inputActionIsTracked.Enable();
|
||||
}
|
||||
|
||||
sb.Clear().Append(LOG_TAG)
|
||||
.Append("FinishSetup() device interfaceName: ").Append(description.interfaceName)
|
||||
.Append(", deviceClass: ").Append(description.deviceClass)
|
||||
.Append(", product: ").Append(description.product)
|
||||
.Append(", serial: ").Append(description.serial)
|
||||
.Append(", usage: ").Append(m_UltimateTrackerUsageMap[description.product])
|
||||
.Append(", current profile: ").Append(profile);
|
||||
DEBUG(sb);
|
||||
|
||||
if (m_UltimateTrackerUsageMap.ContainsKey(description.product))
|
||||
{
|
||||
/// After RegisterActionMapsWithRuntime finished, each XrTrackerDevice will have a product name (e.g. kProductUltimateTracker0)
|
||||
/// We have to assign the XrTrackerDeivce to a commonUsage (e.g. kUltimateTrackerUsage0)
|
||||
///
|
||||
/// Since we already established the m_UltimateTrackerUsageMap (kProductUltimateTracker0, kUltimateTrackerUsage0),
|
||||
/// we can simply call SetDeviceUsage(m_UltimateTrackerUsageMap[description.product])
|
||||
/// to set assign the XrTrackerDevice with product name kProductUltimateTracker0 to the commonUsage kUltimateTrackerUsage0.
|
||||
InputSystem.SetDeviceUsage(this, m_UltimateTrackerUsageMap[description.product]);
|
||||
sb.Clear().Append(LOG_TAG).Append("FinishSetup() usage: ").Append(m_UltimateTrackerUsageMap[description.product]); DEBUG(sb);
|
||||
#if DEBUG_CODE
|
||||
/// We cannot update the ActionMap outside the RegisterActionMapsWithRuntime method so ignore this code.
|
||||
if (inputActionIsTracked == null)
|
||||
{
|
||||
//string actionBindingIsTracked = "<XRController>{LeftHand}/isTracked";
|
||||
string actionBindingIsTracked = "<" + kLayoutName + ">{" + m_UltimateTrackerUsageMap[description.product] + "}/isTracked";
|
||||
sb.Clear().Append(LOG_TAG).Append("FinishSetup() ").Append(m_UltimateTrackerUsageMap[description.product]).Append(", action binding of IsTracked: ").Append(actionBindingIsTracked);
|
||||
DEBUG(sb);
|
||||
|
||||
inputActionIsTracked = new InputAction(
|
||||
type: InputActionType.Value,
|
||||
binding: actionBindingIsTracked);
|
||||
|
||||
inputActionIsTracked.Enable();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_CODE
|
||||
/// We cannot update the ActionMap outside the RegisterActionMapsWithRuntime method so ignore this code.
|
||||
private bool UpdateInputDeviceInRuntime = true;
|
||||
private bool bRoleUpdated = false;
|
||||
public void OnUpdate()
|
||||
{
|
||||
@@ -298,14 +285,14 @@ namespace VIVE.OpenXR.Tracker
|
||||
/// Updates the Usage (tracker role) when IsTracked becomes true.
|
||||
if (inputActionIsTracked.ReadValue<float>() > 0 && !bRoleUpdated)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG)
|
||||
.Append("OnUpdate() Update the InputDevice with product: ").Append(description.product); DEBUG(sb);
|
||||
sb.Clear().Append(LOG_TAG).Append("OnUpdate() Update the InputDevice with product: ").Append(description.product); DEBUG(sb);
|
||||
|
||||
//m_Instance.UpdateInputDevice(description.product, entityPose);
|
||||
if (m_UltimateTrackerUsageMap.ContainsKey(description.product)) { m_Instance.UpdateInputDeviceUltimateTracker(description.product); }
|
||||
|
||||
bRoleUpdated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>The interaction profile string used to reference the wrist tracker interaction input device.</summary>
|
||||
@@ -370,7 +357,6 @@ namespace VIVE.OpenXR.Tracker
|
||||
m_XrSessionCreated = false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// "<" + kLayoutName + ">{" + m_UltimateTrackerUsageMap[description.product] + "}/isTracked"
|
||||
private const string kLayoutName = "ViveXRTracker";
|
||||
@@ -396,12 +382,11 @@ namespace VIVE.OpenXR.Tracker
|
||||
sb.Clear().Append(LOG_TAG).Append("UnregisterDeviceLayout() ").Append(kLayoutName); DEBUG(sb);
|
||||
InputSystem.RemoveLayout(kLayoutName);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OpenXR function delegates
|
||||
/// xrGetInstanceProcAddr
|
||||
OpenXRHelper.xrGetInstanceProcAddrDelegate XrGetInstanceProcAddr;
|
||||
/// xrEnumeratePathsForInteractionProfileHTC
|
||||
VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate xrEnumeratePathsForInteractionProfileHTC = null;
|
||||
/// xrGetInputSourceLocalizedName
|
||||
static OpenXRHelper.xrGetInputSourceLocalizedNameDelegate xrGetInputSourceLocalizedName = null;
|
||||
/// xrEnumerateInstanceExtensionProperties
|
||||
@@ -446,28 +431,6 @@ namespace VIVE.OpenXR.Tracker
|
||||
ERROR(sb);
|
||||
}
|
||||
|
||||
/// xrEnumeratePathsForInteractionProfileHTC
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrEnumeratePathsForInteractionProfileHTC", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (funcPtr != IntPtr.Zero)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("Get function pointer of xrEnumeratePathsForInteractionProfileHTC."); DEBUG(sb);
|
||||
xrEnumeratePathsForInteractionProfileHTC = Marshal.GetDelegateForFunctionPointer(
|
||||
funcPtr,
|
||||
typeof(VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate)) as VivePathEnumerationHelper.xrEnumeratePathsForInteractionProfileHTCDelegate;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC.");
|
||||
ERROR(sb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC");
|
||||
ERROR(sb);
|
||||
}
|
||||
|
||||
/// xrGetInputSourceLocalizedName
|
||||
if (XrGetInstanceProcAddr(xrInstance, "xrGetInputSourceLocalizedName", out funcPtr) == XrResult.XR_SUCCESS)
|
||||
{
|
||||
@@ -492,287 +455,7 @@ namespace VIVE.OpenXR.Tracker
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/*private List<T> CreateList<T>(UInt32 count, T initialValue)
|
||||
{
|
||||
List<T> list = new List<T>();
|
||||
for (int i = 0; i < count; i++)
|
||||
list.Add(initialValue);
|
||||
|
||||
return list;
|
||||
}
|
||||
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||
private bool EnumeratePath()
|
||||
{
|
||||
if (!m_XrInstanceCreated) { return false; }
|
||||
|
||||
string func = "EnumeratePath() ";
|
||||
|
||||
if (xrEnumeratePathsForInteractionProfileHTC == null)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("No function pointer of xrEnumeratePathsForInteractionProfileHTC"); WARNING(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. Get user path count of /interaction_profiles/htc/vive_xr_tracker profile.
|
||||
UInt32 trackerCount = 0;
|
||||
enumerateInfo.type = XrStructureType.XR_TYPE_UNKNOWN;
|
||||
enumerateInfo.next = IntPtr.Zero;
|
||||
enumerateInfo.interactionProfile = StringToPath(profile); // /interaction_profiles/htc/vive_xr_tracker
|
||||
enumerateInfo.userPath = OpenXRHelper.XR_NULL_PATH;
|
||||
|
||||
XrResult result = xrEnumeratePathsForInteractionProfileHTC(m_XrInstance, ref enumerateInfo, 0, ref trackerCount, null);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Retrieves trackerCount failed."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Get profile ").Append(profile).Append(" user path count: ").Append(trackerCount); DEBUG(sb);
|
||||
|
||||
if (trackerCount > 0)
|
||||
{
|
||||
// 2. Get user paths of /interaction_profiles/htc/vive_xr_tracker profile.
|
||||
List<XrPath> trackerList = CreateList<XrPath>(trackerCount, OpenXRHelper.XR_NULL_PATH);
|
||||
XrPath[] trackers = trackerList.ToArray();
|
||||
result = xrEnumeratePathsForInteractionProfileHTC(
|
||||
m_XrInstance,
|
||||
ref enumerateInfo,
|
||||
pathCapacityInput: (UInt32)(trackers.Length & 0x7FFFFFFF),
|
||||
pathCountOutput: ref trackerCount,
|
||||
paths: trackers);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Retrieves trackers failed."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateMapsUltimateTracker(trackers, (Int32)(trackerCount & 0x7FFFFFFF));
|
||||
}
|
||||
|
||||
return true;
|
||||
}*/
|
||||
|
||||
#region Usage Map of Ultimate Tracker
|
||||
/*private void UpdateMapsUltimateTracker(XrPath[] paths, int pathLength)
|
||||
{
|
||||
for (int i = 0; i < pathLength && i < s_UltimateTrackerProduct.Length; i++)
|
||||
{
|
||||
UpdateMapsUltimateTracker(paths[i], s_UltimateTrackerProduct[i]);
|
||||
}
|
||||
}
|
||||
private void UpdateMapsUltimateTracker(string[] paths, string[] products)
|
||||
{
|
||||
for (int i = 0; i < paths.Length; i++)
|
||||
{
|
||||
XrPath path = StringToPath(paths[i]);
|
||||
UpdateMapsUltimateTracker(path, products[i]);
|
||||
}
|
||||
}*/
|
||||
private bool UpdateMapsUltimateTracker(XrPath path, string product)
|
||||
{
|
||||
string func = "UpdateMapsUltimateTracker() ";
|
||||
string s_path = PathToString(path);
|
||||
|
||||
// -------------------- Tracker Role --------------------
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("path: ").Append(s_path)
|
||||
.Append(", product: ").Append(product).Append(" GetInputSourceName(TrackerRole)"); DEBUG(sb);
|
||||
|
||||
if (GetInputSourceName(path, InputSourceType.TrackerRole, out string role) != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("GetInputSourceName of TrackerRole failed."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append(s_path).Append(" has role: ").Append(role); DEBUG(sb);
|
||||
if (!s_TrackerRoleName.Contains(role))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("role ").Append(role).Append(" is NOT definied."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// For sample:
|
||||
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kTrackerRoleLeftWrist ("Left Wrist") which means
|
||||
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kTrackerRoleLeftWrist.
|
||||
/// So we have to set the usage of the product name kProductUltimateTracker0 to kTrackerRoleLeftWrist.
|
||||
if (!m_UltimateTrackerUsageMap.ContainsKey(product))
|
||||
m_UltimateTrackerUsageMap.Add(product, role);
|
||||
else
|
||||
m_UltimateTrackerUsageMap[product] = role;
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Sets product ").Append(product).Append(" with usage ").Append(role); DEBUG(sb);
|
||||
|
||||
// -------------------- Tracker Serial Number --------------------
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("path: ").Append(s_path)
|
||||
.Append(", product: ").Append(product).Append(" GetInputSourceName(SerialNumber)"); DEBUG(sb);
|
||||
|
||||
if (GetInputSourceName(path, InputSourceType.SerialNumber, out string sn) != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("GetInputSourceName of SerialNumber failed."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append(s_path).Append(" has serial number: ").Append(sn); DEBUG(sb);
|
||||
if (!s_TrackerSerialNumber.Contains(sn))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("serial number ").Append(sn).Append(" is NOT definied."); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// For sample:
|
||||
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kUltimateTrackerSN0 ("VIVE_Ultimate_Tracker_0") which means
|
||||
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kUltimateTrackerSN0.
|
||||
/// So we have to set the serial of the product name kProductUltimateTracker0 to kUltimateTrackerSN0.
|
||||
if (!m_UltimateTrackerSerialMap.ContainsKey(product))
|
||||
m_UltimateTrackerSerialMap.Add(product, sn);
|
||||
else
|
||||
m_UltimateTrackerSerialMap[product] = sn;
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Sets product ").Append(product).Append(" with serial number ").Append(sn); DEBUG(sb);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public enum InputSourceType : UInt64
|
||||
{
|
||||
SerialNumber = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC,
|
||||
TrackerRole = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT,
|
||||
}
|
||||
public static XrResult GetInputSourceName(XrPath path, InputSourceType sourceType, out string sourceName)
|
||||
{
|
||||
string func = "GetInputSourceName() ";
|
||||
|
||||
sourceName = "";
|
||||
if (!m_XrSessionCreated || xrGetInputSourceLocalizedName == null) { return XrResult.XR_ERROR_VALIDATION_FAILURE; }
|
||||
|
||||
string userPath = PathToString(path);
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("userPath: ").Append(userPath).Append(", flag: ").Append((UInt64)sourceType); DEBUG(sb);
|
||||
|
||||
XrInputSourceLocalizedNameGetInfo nameInfo = new XrInputSourceLocalizedNameGetInfo(
|
||||
XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO,
|
||||
IntPtr.Zero, path, (XrInputSourceLocalizedNameFlags)sourceType);
|
||||
UInt32 nameSizeIn = 0;
|
||||
UInt32 nameSizeOut = 0;
|
||||
char[] buffer = new char[0];
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||
DEBUG(sb);
|
||||
XrResult result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (nameSizeOut < 1)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append("bufferCountOutput size is invalid!");
|
||||
ERROR(sb);
|
||||
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
nameSizeIn = nameSizeOut;
|
||||
buffer = new char[nameSizeIn];
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||
DEBUG(sb);
|
||||
result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
sourceName = new string(buffer).TrimEnd('\0');
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut)
|
||||
.Append(", sourceName: ").Append(sourceName);
|
||||
DEBUG(sb);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut)
|
||||
.Append(" result: ").Append(result);
|
||||
ERROR(sb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut)
|
||||
.Append(" result: ").Append(result);
|
||||
ERROR(sb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/*private void UpdateInputDevice(string product, string actionPath)
|
||||
{
|
||||
string func = "UpdateInputDevice() ";
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("product: ").Append(product)
|
||||
.Append(", usage: ").Append(m_UltimateTrackerUsageMap[product])
|
||||
.Append(" with user path: ").Append(m_UltimateTrackerPathMap[product]);
|
||||
DEBUG(sb);
|
||||
|
||||
XrPath path = StringToPath(m_UltimateTrackerPathMap[product]);
|
||||
if (UpdateMapsUltimateTracker(path, product))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Maps of ").Append(product)
|
||||
.Append(" with user path ").Append(path).Append(" are updated.");
|
||||
DEBUG(sb);
|
||||
|
||||
bool foundProduct = false;
|
||||
for (int i = 0; i < InputSystem.devices.Count; i++)
|
||||
{
|
||||
if (InputSystem.devices[i] is XrTrackerDevice &&
|
||||
InputSystem.devices[i].description.product.Equals(product))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Removes the XrTrackerDevice product ").Append(product);
|
||||
DEBUG(sb);
|
||||
|
||||
InputSystem.RemoveDevice(InputSystem.devices[i]);
|
||||
foundProduct = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundProduct)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Adds a XrTrackerDevice product ").Append(product);
|
||||
RegisterActionMap(product);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
// Available Bindings
|
||||
/// <summary>
|
||||
@@ -780,28 +463,28 @@ namespace VIVE.OpenXR.Tracker
|
||||
/// </summary>
|
||||
public const string entityPose = "/input/entity_htc/pose";
|
||||
|
||||
private void RegisterActionMap(string product)
|
||||
private void RegisterActionMap(string in_name, string product, string in_sn, string in_path)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() Added ActionMapConfig of ").Append(m_UltimateTrackerPathMap[product])
|
||||
sb.Clear().Append(LOG_TAG).Append("RegisterActionMap() Added ActionMapConfig of ").Append(in_path)
|
||||
.Append(", localizedName = ").Append(product)
|
||||
.Append(" { name = ").Append(m_UltimateTrackerActionMap[product])
|
||||
.Append(" { name = ").Append(in_name)
|
||||
.Append(", desiredInteractionProfile = ").Append(profile)
|
||||
.Append(", serialNumber = ").Append(m_UltimateTrackerSerialMap[product]);
|
||||
.Append(", serialNumber = ").Append(in_sn);
|
||||
DEBUG(sb);
|
||||
|
||||
ActionMapConfig actionMap = new ActionMapConfig()
|
||||
{
|
||||
name = m_UltimateTrackerActionMap[product],
|
||||
name = in_name,
|
||||
localizedName = product,
|
||||
desiredInteractionProfile = profile,
|
||||
manufacturer = "HTC",
|
||||
serialNumber = m_UltimateTrackerSerialMap[product],
|
||||
serialNumber = in_sn,
|
||||
deviceInfos = new List<DeviceConfig>()
|
||||
{
|
||||
new DeviceConfig()
|
||||
{
|
||||
characteristics = InputDeviceCharacteristics.TrackedDevice,
|
||||
userPath = m_UltimateTrackerPathMap[product]
|
||||
userPath = in_path
|
||||
}
|
||||
},
|
||||
actions = new List<ActionConfig>()
|
||||
@@ -837,14 +520,311 @@ namespace VIVE.OpenXR.Tracker
|
||||
{
|
||||
if (OpenXRHelper.IsExtensionSupported(xrEnumerateInstanceExtensionProperties, kOpenxrExtensionString) != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() ").Append(kOpenxrExtensionString).Append(" is NOT supported."); DEBUG(sb);
|
||||
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() ").Append(kOpenxrExtensionString).Append(" is NOT supported."); ERROR(sb);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Updates m_UltimateTrackerPathMap.
|
||||
if (!EnumeratePath())
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append("RegisterActionMapsWithRuntime() EnumeratePath failed.");
|
||||
ERROR(sb);
|
||||
}
|
||||
|
||||
for (int userIndex = 0; userIndex < s_UltimateTrackerProduct.Length; userIndex++)
|
||||
{
|
||||
RegisterActionMap(s_UltimateTrackerProduct[userIndex]);
|
||||
string product = s_UltimateTrackerProduct[userIndex];
|
||||
RegisterActionMap(
|
||||
product: product,
|
||||
in_name: m_UltimateTrackerActionMap[product],
|
||||
in_sn: m_UltimateTrackerSerialMap[product],
|
||||
in_path: m_UltimateTrackerPathMap[product]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo;
|
||||
private bool EnumeratePath()
|
||||
{
|
||||
if (!m_XrInstanceCreated) { return false; }
|
||||
|
||||
string func = "EnumeratePath() ";
|
||||
|
||||
// 1. Get user path count of /interaction_profiles/htc/vive_xr_tracker profile.
|
||||
UInt32 trackerCount = 0;
|
||||
enumerateInfo.type = XrStructureType.XR_TYPE_UNKNOWN;
|
||||
enumerateInfo.next = IntPtr.Zero;
|
||||
enumerateInfo.interactionProfile = StringToPath(profile); // /interaction_profiles/htc/vive_xr_tracker
|
||||
enumerateInfo.userPath = OpenXRHelper.XR_NULL_PATH;
|
||||
|
||||
XrResult result = XR_HTC_path_enumeration.xrEnumeratePathsForInteractionProfileHTC(ref enumerateInfo, 0, ref trackerCount, null);
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("xrEnumeratePathsForInteractionProfileHTC result: ").Append(result)
|
||||
.Append(", profile: ").Append(profile)
|
||||
.Append(", trackerCount: ").Append(trackerCount);
|
||||
DEBUG(sb);
|
||||
if (result != XrResult.XR_SUCCESS) { return false; }
|
||||
|
||||
if (trackerCount > 0)
|
||||
{
|
||||
// 2. Get user paths of /interaction_profiles/htc/vive_xr_tracker profile.
|
||||
XrPath[] trackerPaths = new XrPath[trackerCount];
|
||||
for (int i = 0; i < trackerPaths.Length; i++) { trackerPaths[i] = OpenXRHelper.XR_NULL_PATH; }
|
||||
|
||||
result = XR_HTC_path_enumeration.xrEnumeratePathsForInteractionProfileHTC(
|
||||
ref enumerateInfo,
|
||||
pathCapacityInput: (UInt32)(trackerPaths.Length & 0x7FFFFFFF),
|
||||
pathCountOutput: ref trackerCount,
|
||||
paths: trackerPaths);
|
||||
if (result != XrResult.XR_SUCCESS)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("xrEnumeratePathsForInteractionProfileHTC result: ").Append(result); ERROR(sb);
|
||||
return false;
|
||||
}
|
||||
|
||||
int ultimate_tracker_index = 0;
|
||||
for (int i = 0; i < trackerCount; i++)
|
||||
{
|
||||
string userPath = PathToString(trackerPaths[i]);
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("xrEnumeratePathsForInteractionProfileHTC[").Append(i).Append("] ").Append(userPath); DEBUG(sb);
|
||||
|
||||
if (userPath.Contains("ultimate", StringComparison.OrdinalIgnoreCase) && ultimate_tracker_index < s_UltimateTrackerProduct.Length)
|
||||
{
|
||||
string product = s_UltimateTrackerProduct[ultimate_tracker_index];
|
||||
|
||||
m_UltimateTrackerPathMap[product] = userPath;
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("Updates ").Append(product).Append(" path to ").Append(m_UltimateTrackerPathMap[product]); DEBUG(sb);
|
||||
|
||||
m_UltimateTrackerSerialMap[product] = ConvertUserPathToSerialNumber(userPath);
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("Updates ").Append(product).Append(" serial number to ").Append(m_UltimateTrackerSerialMap[product]); DEBUG(sb);
|
||||
|
||||
ultimate_tracker_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// For example, user path "/user/xr_tracker_htc/vive_ultimate_tracker_0" will become serial number "VIVE_Ultimate_Tracker_0".
|
||||
/// </summary>
|
||||
/// <param name="userPath">The user path from <see cref="EnumeratePath"> EnumeratePath </see>.</param>
|
||||
/// <returns>Serial Number in string.</returns>
|
||||
private string ConvertUserPathToSerialNumber(string userPath)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
int lastSlashIndex = userPath.LastIndexOf('/');
|
||||
if (lastSlashIndex >= 0)
|
||||
{
|
||||
string[] parts = userPath.Substring(lastSlashIndex + 1).Split('_');
|
||||
parts[0] = parts[0].ToUpper();
|
||||
for (int i = 1; i < parts.Length - 1; i++)
|
||||
{
|
||||
parts[i] = char.ToUpper(parts[i][0]) + parts[i].Substring(1);
|
||||
}
|
||||
result = string.Join("_", parts);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[Obsolete("This enumeration is deprecated. Please use XrInputSourceLocalizedNameFlags instead.")]
|
||||
public enum InputSourceType : UInt64
|
||||
{
|
||||
SerialNumber = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC,
|
||||
TrackerRole = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT,
|
||||
}
|
||||
[Obsolete("This function is deprecated. Please use OpenXRHelper.GetInputSourceName instead.")]
|
||||
public static XrResult GetInputSourceName(XrPath path, InputSourceType sourceType, out string sourceName)
|
||||
{
|
||||
string func = "GetInputSourceName() ";
|
||||
|
||||
sourceName = "";
|
||||
if (!m_XrSessionCreated || xrGetInputSourceLocalizedName == null) { return XrResult.XR_ERROR_VALIDATION_FAILURE; }
|
||||
|
||||
string userPath = PathToString(path);
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("userPath: ").Append(userPath).Append(", flag: ").Append((UInt64)sourceType); DEBUG(sb);
|
||||
|
||||
XrInputSourceLocalizedNameGetInfo nameInfo = new XrInputSourceLocalizedNameGetInfo(
|
||||
XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO,
|
||||
IntPtr.Zero, path, (XrInputSourceLocalizedNameFlags)sourceType);
|
||||
|
||||
UInt32 nameSizeIn = 0;
|
||||
UInt32 nameSizeOut = 0;
|
||||
char[] buffer = new char[0];
|
||||
|
||||
XrResult result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("1.xrGetInputSourceLocalizedName(").Append(userPath).Append(") result: ").Append(result)
|
||||
.Append(", flag: ").Append((UInt64)nameInfo.whichComponents)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||
DEBUG(sb);
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (nameSizeOut < 1)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("xrGetInputSourceLocalizedName(").Append(userPath).Append(")")
|
||||
.Append(", flag: ").Append((UInt64)sourceType)
|
||||
.Append("bufferCountOutput size is invalid!");
|
||||
ERROR(sb);
|
||||
return XrResult.XR_ERROR_VALIDATION_FAILURE;
|
||||
}
|
||||
|
||||
nameSizeIn = nameSizeOut;
|
||||
buffer = new char[nameSizeIn];
|
||||
|
||||
result = xrGetInputSourceLocalizedName(m_XrSession, ref nameInfo, nameSizeIn, ref nameSizeOut, buffer);
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("2.xrGetInputSourceLocalizedName(").Append(userPath).Append(") result: ").Append(result)
|
||||
.Append(", flag: ").Append((UInt64)nameInfo.whichComponents)
|
||||
.Append(", bufferCapacityInput: ").Append(nameSizeIn)
|
||||
.Append(", bufferCountOutput: ").Append(nameSizeOut);
|
||||
DEBUG(sb);
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
sourceName = new string(buffer).TrimEnd('\0');
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DEBUG_CODE
|
||||
XrInputSourceLocalizedNameGetInfo nameInfo;
|
||||
private bool updateSerialNumber = true, updateUsage = false;
|
||||
private bool UpdateTrackerMaps(string product, XrPath path, ref Dictionary<string, string> serialMap, ref Dictionary<string, string> roleMap)
|
||||
{
|
||||
string func = "UpdateTrackerMaps() ";
|
||||
string s_path = PathToString(path);
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("product: ").Append(product).Append(", path: ").Append(s_path); DEBUG(sb);
|
||||
|
||||
// -------------------- Tracker Serial Number --------------------
|
||||
if (updateSerialNumber)
|
||||
{
|
||||
nameInfo.type = XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO;
|
||||
nameInfo.next = IntPtr.Zero;
|
||||
nameInfo.sourcePath = path;
|
||||
nameInfo.whichComponents = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_SERIAL_NUMBER_BIT_HTC;
|
||||
|
||||
XrResult result = OpenXRHelper.GetInputSourceName(
|
||||
xrGetInputSourceLocalizedName,
|
||||
m_XrSession,
|
||||
ref nameInfo,
|
||||
out string sn);
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("GetInputSourceName(").Append(s_path).Append(")")
|
||||
.Append(", sourceType: ").Append(nameInfo.whichComponents)
|
||||
.Append(", serial number: ").Append(sn)
|
||||
.Append(", result: ").Append(result);
|
||||
DEBUG(sb);
|
||||
|
||||
/// For sample:
|
||||
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kUltimateTrackerSN0 ("VIVE_Ultimate_Tracker_0") which means
|
||||
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kUltimateTrackerSN0.
|
||||
/// So we have to set the serial of the product name kProductUltimateTracker0 to kUltimateTrackerSN0.
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
if (!serialMap.ContainsKey(product))
|
||||
serialMap.Add(product, sn);
|
||||
else
|
||||
serialMap[product] = sn;
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("Sets product ").Append(product).Append(" with serial number ").Append(serialMap[product]); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
// -------------------- Tracker Role --------------------
|
||||
if (updateUsage)
|
||||
{
|
||||
nameInfo.type = XrStructureType.XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO;
|
||||
nameInfo.next = IntPtr.Zero;
|
||||
nameInfo.sourcePath = path;
|
||||
nameInfo.whichComponents = XrInputSourceLocalizedNameFlags.XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT;
|
||||
|
||||
XrResult result = OpenXRHelper.GetInputSourceName(
|
||||
xrGetInputSourceLocalizedName,
|
||||
m_XrSession,
|
||||
ref nameInfo,
|
||||
out string role);
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("GetInputSourceName(").Append(s_path).Append(")")
|
||||
.Append(", sourceType: ").Append(nameInfo.whichComponents)
|
||||
.Append(", role: ").Append(role)
|
||||
.Append(", result: ").Append(result);
|
||||
DEBUG(sb);
|
||||
|
||||
if (result == XrResult.XR_SUCCESS)
|
||||
{
|
||||
/// For sample:
|
||||
/// A user path (e.g. "/user/tracker_htc/index0") has the "sourceName" kTrackerRoleLeftWrist ("Left Wrist") which means
|
||||
/// the corresponding product (e.g. kProductUltimateTracker0 = "VIVE Ultimate Tracker 0") has the "sourceName" kTrackerRoleLeftWrist.
|
||||
/// So we have to set the usage of the product name kProductUltimateTracker0 to kTrackerRoleLeftWrist.
|
||||
if (!roleMap.ContainsKey(product))
|
||||
roleMap.Add(product, role);
|
||||
else
|
||||
roleMap[product] = role;
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("Sets product ").Append(product).Append(" with usage ").Append(roleMap[product]); DEBUG(sb);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Due to "ActionMap must be added from within the RegisterActionMapsWithRuntime method", this function is unusable.
|
||||
/// </summary>
|
||||
/// <param name="product">A tracker's product name.</param>
|
||||
private void UpdateInputDeviceUltimateTracker(string product)
|
||||
{
|
||||
if (!IsUltimateTracker(product)) { return; }
|
||||
string func = "UpdateInputDeviceUltimateTracker() ";
|
||||
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("product: ").Append(product)
|
||||
.Append(", serial number: ").Append(m_UltimateTrackerSerialMap[product])
|
||||
.Append(", user path: ").Append(m_UltimateTrackerPathMap[product])
|
||||
.Append(", usage: ").Append(m_UltimateTrackerUsageMap[product]);
|
||||
DEBUG(sb);
|
||||
|
||||
XrPath path = StringToPath(m_UltimateTrackerPathMap[product]);
|
||||
/// Updates tracker serial number (m_UltimateTrackerSerialMap) and role (m_UltimateTrackerUsageMap)
|
||||
if (UpdateTrackerMaps(product, path, ref m_UltimateTrackerSerialMap, ref m_UltimateTrackerUsageMap))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Maps of ").Append(product)
|
||||
.Append(" with user path ").Append(m_UltimateTrackerPathMap[product]).Append(" are updated.");
|
||||
DEBUG(sb);
|
||||
|
||||
bool foundProduct = false;
|
||||
for (int i = 0; i < InputSystem.devices.Count; i++)
|
||||
{
|
||||
if (InputSystem.devices[i] is XrTrackerDevice &&
|
||||
InputSystem.devices[i].description.product.Equals(product))
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func)
|
||||
.Append("Removes the XrTrackerDevice product ").Append(product);
|
||||
DEBUG(sb);
|
||||
|
||||
InputSystem.RemoveDevice(InputSystem.devices[i]);
|
||||
foundProduct = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundProduct)
|
||||
{
|
||||
sb.Clear().Append(LOG_TAG).Append(func).Append("Adds a XrTrackerDevice product ").Append(product); DEBUG(sb);
|
||||
RegisterActionMap(
|
||||
product: product,
|
||||
in_name: m_UltimateTrackerActionMap[product],
|
||||
in_sn: m_UltimateTrackerSerialMap[product],
|
||||
in_path: m_UltimateTrackerPathMap[product]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user