// Copyright HTC Corporation All Rights Reserved. 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; using UnityEditor.XR.OpenXR.Features; #endif namespace VIVE.OpenXR { #if UNITY_EDITOR [OpenXRFeature(UiName = "VIVE XR Path Enumeration", 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", Version = "1.0.6", OpenxrExtensionStrings = kOpenxrExtensionString, FeatureId = featureId)] #endif public class VivePathEnumeration : OpenXRFeature { const string LOG_TAG = "VIVE.OpenXR.VivePathEnumeration "; StringBuilder m_sb = null; StringBuilder sb { get { if (m_sb == null) { m_sb = new StringBuilder(); } return m_sb; } } void DEBUG(StringBuilder msg) { Debug.Log(msg); } void WARNING(StringBuilder msg) { Debug.LogWarning(msg); } void ERROR(StringBuilder msg) { Debug.LogError(msg); } /// /// OpenXR specification 12.1. XR_HTC_path_enumeration. /// public const string kOpenxrExtensionString = "XR_HTC_path_enumeration"; /// /// The feature id string. This is used to give the feature a well known id for reference. /// public const string featureId = "vive.wave.openxr.feature.pathenumeration"; #region OpenXR Life Cycle #pragma warning disable private bool m_XrInstanceCreated = false; #pragma warning enable private XrInstance m_XrInstance = 0; /// /// Called when xrCreateInstance is done. /// /// The created instance. /// True for valid XrInstance protected override bool OnInstanceCreate(ulong xrInstance) { if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString)) { sb.Clear().Append(LOG_TAG).Append("OnInstanceCreate() ").Append(kOpenxrExtensionString).Append(" is NOT enabled."); WARNING(sb); return false; } 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); } /// /// Called when xrDestroyInstance is done. /// /// The instance to destroy. protected override void OnInstanceDestroy(ulong xrInstance) { if (m_XrInstance == xrInstance) { m_XrInstanceCreated = false; m_XrInstance = 0; } sb.Clear().Append(LOG_TAG).Append("OnInstanceDestroy() ").Append(xrInstance); DEBUG(sb); } private XrSystemId m_XrSystemId = 0; /// /// Called when the XrSystemId retrieved by xrGetSystem is changed. /// /// The system id. protected override void OnSystemChange(ulong xrSystem) { m_XrSystemId = xrSystem; sb.Clear().Append(LOG_TAG).Append("OnSystemChange() ").Append(m_XrSystemId); DEBUG(sb); } private bool m_XrSessionCreated = false; private XrSession m_XrSession = 0; /// /// Called when xrCreateSession is done. /// /// The created session ID. protected override void OnSessionCreate(ulong xrSession) { m_XrSession = xrSession; m_XrSessionCreated = true; sb.Clear().Append(LOG_TAG).Append("OnSessionCreate() ").Append(m_XrSession); DEBUG(sb); } /// /// Called when xrDestroySession is done. /// /// The session ID to destroy. protected override void OnSessionDestroy(ulong xrSession) { sb.Clear().Append(LOG_TAG).Append("OnSessionDestroy() ").Append(xrSession); DEBUG(sb); m_XrSession = 0; 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 CreateList(UInt32 count, T initialValue) { List list = new List(); 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) { userPaths = null; XrPathsForInteractionProfileEnumerateInfoHTC enumerateInfo; if (!m_XrInstanceCreated) { return false; } string func = "GetUserPaths() "; 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 sepecified profile. UInt32 trackerCount = 0; enumerateInfo.type = XrStructureType.XR_TYPE_UNKNOWN; 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); sb.Clear().Append(LOG_TAG).Append(func).Append("xrEnumeratePathsForInteractionProfileHTC result: ").Append(result) .Append(", profile: ").Append(interactionProfileString) .Append(", trackerCount: ").Append(trackerCount); DEBUG(sb); if (result != XrResult.XR_SUCCESS || trackerCount <= 0) { return false; } // 2. Get user paths of sepecified profile. List trackerList = CreateList(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; } userPaths = trackers; return true; } 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 pathlist = CreateList(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 } }