// Copyright HTC Corporation All Rights Reserved. using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR.Features; using UnityEngine; using System.Runtime.InteropServices; using System; using AOT; #if UNITY_EDITOR using UnityEditor; using UnityEditor.XR.OpenXR.Features; #endif namespace VIVE.OpenXR.EyeTracker { #if UNITY_EDITOR [OpenXRFeature(UiName = "VIVE XR Eye Tracker (Beta)", BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }, Company = "HTC", Desc = "Support the eye tracker extension.", DocumentationLink = "..\\Documentation", OpenxrExtensionStrings = kOpenxrExtensionString, Version = "1.0.0", FeatureId = featureId)] #endif public class ViveEyeTracker : OpenXRFeature { const string LOG_TAG = "VIVE.OpenXR.Eye.ViveEyeTracker"; void DEBUG(string msg) { Debug.Log(LOG_TAG + " " + msg); } void WARNING(string msg) { Debug.LogWarning(LOG_TAG + " " + msg); } void ERROR(string msg) { Debug.LogError(LOG_TAG + " " + msg); } public const string kOpenxrExtensionString = "XR_HTC_eye_tracker"; /// /// The feature id string. This is used to give the feature a well known id for reference. /// public const string featureId = "vive.openxr.feature.eye.tracker"; #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(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(WaitFrame_prev); int res = xrWaitFrame(session, ref frameWaitInfo, ref frameState); m_frameWaitInfo = frameWaitInfo; m_frameState = frameState; return res; } /// /// Called when xrCreateInstance is done. /// /// The created instance. /// True for valid XrInstance protected override bool OnInstanceCreate(ulong xrInstance) { if (!OpenXRRuntime.IsExtensionEnabled(kOpenxrExtensionString)) { WARNING("OnInstanceCreate() " + kOpenxrExtensionString + " is NOT enabled."); return false; } m_XrInstanceCreated = true; m_XrInstance = xrInstance; DEBUG("OnInstanceCreate() " + m_XrInstance); return GetXrFunctionDelegates(m_XrInstance); } /// /// Called when xrDestroyInstance is done. /// /// The instance to destroy. protected override void OnInstanceDestroy(ulong xrInstance) { m_XrInstanceCreated = false; m_XrInstance = 0; DEBUG("OnInstanceDestroy() " + xrInstance); } 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; DEBUG("OnSystemChange() " + m_XrSystemId); } private bool m_XrSessionCreated = false; private XrSession m_XrSession = 0; private bool hasEyeTracker = false; private XrEyeTrackerHTC m_EyeTracker = 0; /// /// Called when xrCreateSession is done. /// /// The created session ID. protected override void OnSessionCreate(ulong xrSession) { m_XrSession = xrSession; m_XrSessionCreated = true; DEBUG("OnSessionCreate() " + m_XrSession); if (CreateEyeTracker()) { DEBUG("OnSessionCreate() m_EyeTracker " + m_EyeTracker); } } /// /// Called when xrDestroySession is done. /// /// The session ID to destroy. protected override void OnSessionDestroy(ulong xrSession) { DEBUG("OnSessionDestroy() " + xrSession); // Eye Tracking is binding with xrSession so we destroy the trackers when xrSession is destroyed. DestroyEyeTracker(); m_XrSession = 0; m_XrSessionCreated = false; } #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; /// xrGetSystemProperties OpenXRHelper.xrGetSystemPropertiesDelegate xrGetSystemProperties; private XrResult GetSystemProperties(ref XrSystemProperties properties) { if (!m_XrSessionCreated) { ERROR("GetSystemProperties() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("GetSystemProperties() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } return xrGetSystemProperties(m_XrInstance, m_XrSystemId, ref properties); } /// xrDestroySpace OpenXRHelper.xrDestroySpaceDelegate xrDestroySpace; private XrResult DestroySpace(XrSpace space) { if (!m_XrSessionCreated) { ERROR("DestroySpace() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("DestroySpace() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } return xrDestroySpace(space); } ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate xrCreateEyeTrackerHTC; private XrResult CreateEyeTrackerHTC(ref XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker) { if (!m_XrSessionCreated) { ERROR("CreateEyeTrackerHTC() XR_ERROR_SESSION_LOST."); eyeTracker = 0; return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("CreateEyeTrackerHTC() XR_ERROR_INSTANCE_LOST."); eyeTracker = 0; return XrResult.XR_ERROR_INSTANCE_LOST; } return xrCreateEyeTrackerHTC(m_XrSession, ref createInfo, out eyeTracker); } ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate xrDestroyEyeTrackerHTC; private XrResult DestroyEyeTrackerHTC(XrEyeTrackerHTC eyeTracker) { if (!m_XrSessionCreated) { ERROR("DestroyEyeTrackerHTC() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("DestroyEyeTrackerHTC() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } return xrDestroyEyeTrackerHTC(eyeTracker); } ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate xrGetEyeGazeDataHTC; private XrResult GetEyeGazeDataHTC(XrEyeTrackerHTC eyeTracker,ref XrEyeGazeDataInfoHTC gazeInfo, ref XrEyeGazeDataHTC eyeGazes) { if (!m_XrSessionCreated) { ERROR("GetEyeGazeDataHTC() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("GetEyeGazeDataHTC() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } XrResult res = xrGetEyeGazeDataHTC(eyeTracker,ref gazeInfo,ref eyeGazes); return res; } ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate xrGetEyePupilDataHTC; private XrResult GetEyePupilDataHTC(XrEyeTrackerHTC eyeTracker,ref XrEyePupilDataInfoHTC pupilDataInfo,ref XrEyePupilDataHTC pupilData) { if (!m_XrSessionCreated) { ERROR("GetEyePupilData() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("GetEyePupilData() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } return xrGetEyePupilDataHTC(eyeTracker,ref pupilDataInfo, ref pupilData); } ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC xrGetEyeGeometricDataHTC; private XrResult GetEyeGeometricDataHTC(XrEyeTrackerHTC eyeTracker, ref XrEyeGeometricDataInfoHTC info, ref XrEyeGeometricDataHTC eyeGeometricData) { if (!m_XrSessionCreated) { ERROR("GetEyeGeometricData() XR_ERROR_SESSION_LOST."); return XrResult.XR_ERROR_SESSION_LOST; } if (!m_XrInstanceCreated) { ERROR("GetEyeGeometricData() XR_ERROR_INSTANCE_LOST."); return XrResult.XR_ERROR_INSTANCE_LOST; } return xrGetEyeGeometricDataHTC(eyeTracker,ref info, ref eyeGeometricData); } private bool GetXrFunctionDelegates(XrInstance xrInstance) { /// xrGetInstanceProcAddr if (xrGetInstanceProcAddr != null && xrGetInstanceProcAddr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetInstanceProcAddr."); XrGetInstanceProcAddr = Marshal.GetDelegateForFunctionPointer( xrGetInstanceProcAddr, typeof(OpenXRHelper.xrGetInstanceProcAddrDelegate)) as OpenXRHelper.xrGetInstanceProcAddrDelegate; } else { ERROR("xrGetInstanceProcAddr is null"); return false; } IntPtr funcPtr = IntPtr.Zero; /// xrGetSystemProperties if (XrGetInstanceProcAddr(xrInstance, "xrGetSystemProperties", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetSystemProperties."); xrGetSystemProperties = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(OpenXRHelper.xrGetSystemPropertiesDelegate)) as OpenXRHelper.xrGetSystemPropertiesDelegate; } } else { ERROR("xrGetSystemProperties"); return false; } /// xrDestroySpace if (XrGetInstanceProcAddr(xrInstance, "xrDestroySpace", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrDestroySpace."); xrDestroySpace = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(OpenXRHelper.xrDestroySpaceDelegate)) as OpenXRHelper.xrDestroySpaceDelegate; } } else { ERROR("xrDestroySpace"); return false; } /// xrCreateEyeTrackerHTC if (XrGetInstanceProcAddr(xrInstance, "xrCreateEyeTrackerHTC", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrCreateEyeTrackerHTC."); xrCreateEyeTrackerHTC = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate)) as ViveEyeTrackerHelper.xrCreateEyeTrackerHTCDelegate; } } else { ERROR("xrCreateEyeTrackerHTC"); return false; } /// xrDestroyEyeTrackerHTC if (XrGetInstanceProcAddr(xrInstance, "xrDestroyEyeTrackerHTC", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrDestroyEyeTrackerHTC."); xrDestroyEyeTrackerHTC = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate)) as ViveEyeTrackerHelper.xrDestroyEyeTrackerHTCDelegate; } } else { ERROR("xrDestroyEyeTrackerHTC"); return false; } /// xrGetEyeGazeDataHTC if (XrGetInstanceProcAddr(xrInstance, "xrGetEyeGazeDataHTC", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetEyeGazeDataHTC."); xrGetEyeGazeDataHTC = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate)) as ViveEyeTrackerHelper.xrGetEyeGazeDataHTCDelegate; } } else { ERROR("xrGetEyeGazeDataHTC"); return false; } /// xrGetEyePupilDataHTC if (XrGetInstanceProcAddr(xrInstance, "xrGetEyePupilDataHTC", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetEyePupilDataHTC."); xrGetEyePupilDataHTC = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate)) as ViveEyeTrackerHelper.xrGetEyePupilDataHTCDelegate; } } else { ERROR("xrGetEyePupilDataHTC"); return false; } /// xrGetEyeGeometricDataHTC if (XrGetInstanceProcAddr(xrInstance, "xrGetEyeGeometricDataHTC", out funcPtr) == XrResult.XR_SUCCESS) { if (funcPtr != IntPtr.Zero) { DEBUG("Get function pointer of xrGetEyeGeometricDataHTC."); xrGetEyeGeometricDataHTC = Marshal.GetDelegateForFunctionPointer( funcPtr, typeof(ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC)) as ViveEyeTrackerHelper.xrGetEyeGeometricDataHTC; } } else { ERROR("xrGetEyeGeometricDataHTC"); return false; } return true; } #endregion XrSystemEyeTrackingPropertiesHTC eyeTrackingSystemProperties; XrSystemProperties systemProperties; private bool IsEyeTrackingSupported() { if (!m_XrSessionCreated) { ERROR("IsEyeTrackingSupported() session is not created."); return false; } eyeTrackingSystemProperties.type = XrStructureType.XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_HTC; systemProperties.type = XrStructureType.XR_TYPE_SYSTEM_PROPERTIES; systemProperties.next = Marshal.AllocHGlobal(Marshal.SizeOf(eyeTrackingSystemProperties)); long offset = 0; if (IntPtr.Size == 4) offset = systemProperties.next.ToInt32(); else offset = systemProperties.next.ToInt64(); IntPtr sys_eye_tracking_prop_ptr = new IntPtr(offset); Marshal.StructureToPtr(eyeTrackingSystemProperties, sys_eye_tracking_prop_ptr, false); if (GetSystemProperties(ref systemProperties) == XrResult.XR_SUCCESS) { if (IntPtr.Size == 4) offset = systemProperties.next.ToInt32(); else offset = systemProperties.next.ToInt64(); sys_eye_tracking_prop_ptr = new IntPtr(offset); eyeTrackingSystemProperties = (XrSystemEyeTrackingPropertiesHTC)Marshal.PtrToStructure(sys_eye_tracking_prop_ptr, typeof(XrSystemEyeTrackingPropertiesHTC)); DEBUG("IsEyeTrackingSupported() XrSystemEyeTrackingPropertiesHTC.supportsEyeTracking: " + eyeTrackingSystemProperties.supportsEyeTracking); return (eyeTrackingSystemProperties.supportsEyeTracking > 0); } else { ERROR("IsEyeTrackingSupported() GetSystemProperties failed."); } return false; } /// /// An application can create an XrEyeTrackerHTC handle using CreateEyeTracker. /// /// The XrEyeTrackerCreateInfoHTC used to specify the eye tracker. /// The returned XrEyeTrackerHTC handle. /// XR_SUCCESS for success. public XrResult CreateEyeTracker(XrEyeTrackerCreateInfoHTC createInfo, out XrEyeTrackerHTC eyeTracker) { if (hasEyeTracker) { eyeTracker = m_EyeTracker; DEBUG("CreateEyeTracker() m_EyeTracker: " + eyeTracker + " already created before."); return XrResult.XR_SUCCESS; } if (!IsEyeTrackingSupported()) { ERROR("CreateEyeTracker() is NOT supported."); eyeTracker = 0; return XrResult.XR_ERROR_VALIDATION_FAILURE; } var result = CreateEyeTrackerHTC(ref createInfo, out eyeTracker); DEBUG("CreateEyeTracker() " + result + ", eyeTracker: " + eyeTracker); if (result == XrResult.XR_SUCCESS) { hasEyeTracker = true; m_EyeTracker = eyeTracker; DEBUG("CreateEyeTracker() m_EyeTracker " + m_EyeTracker); } return result; } /// /// An application can create an XrEyeTrackerHTC handle using CreateEyeTracker. /// /// True for success. public bool CreateEyeTracker() { XrEyeTrackerCreateInfoHTC createInfo = new XrEyeTrackerCreateInfoHTC( in_type: XrStructureType.XR_TYPE_EYE_TRACKER_CREATE_INFO_HTC, in_next: IntPtr.Zero); var result = CreateEyeTracker(createInfo, out XrEyeTrackerHTC value); DEBUG("CreateEyeTracker() " + " tracker: " + value); return result == XrResult.XR_SUCCESS; } /// /// Releases the eye tracker and the underlying resources when the eye tracking experience is over. /// /// An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC. /// XR_SUCCESS for success. public XrResult DestroyEyeTracker(XrEyeTrackerHTC eyeTracker) { XrResult result = DestroyEyeTrackerHTC(eyeTracker); DEBUG("DestroyEyeTracker() " + eyeTracker + ", result: " + result); return result; } /// /// Releases the eye tracker and the underlying resources when the eye tracking experience is over. /// /// True for success. public bool DestroyEyeTracker() { if (!hasEyeTracker) { DEBUG("DestroyEyeTracker() no " + "tracker."); return true; } XrResult ret = XrResult.XR_ERROR_VALIDATION_FAILURE; ret = DestroyEyeTracker(m_EyeTracker); hasEyeTracker = false; m_EyeTracker = 0; return ret == XrResult.XR_SUCCESS; } private XrEyeGazeDataHTC m_gazes = new XrEyeGazeDataHTC();// = new XrEyeGazeDataHTC(XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC, IntPtr.Zero, 0); /// /// Retrieves an array of XrSingleEyeGazeDataHTC containing the returned eye gaze directions. /// /// Output parameter to retrieve an array of XrSingleEyeGazeDataHTC. /// True for success. public bool GetEyeGazeData(out XrSingleEyeGazeDataHTC[] out_gazes) { m_gazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC; m_gazes.next = IntPtr.Zero; m_gazes.time = m_frameState.predictedDisplayTime; out_gazes = m_gazes.gaze; XrEyeGazeDataInfoHTC gazeInfo = new XrEyeGazeDataInfoHTC( in_type: XrStructureType.XR_TYPE_EYE_GAZE_DATA_INFO_HTC, in_next: IntPtr.Zero, in_baseSpace: GetCurrentAppSpace(), in_time: m_frameState.predictedDisplayTime); if (GetEyeGazeData(m_EyeTracker, gazeInfo, out m_gazes) == XrResult.XR_SUCCESS) { out_gazes = m_gazes.gaze; return true; } return false; } /// /// Retrieves the XrEyeGazeDataHTC data of a XrEyeTrackerHTC. /// /// An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC. /// The information to get eye gaze. /// Output parameter to retrieve a pointer to XrEyeGazeDataHTC receiving the returned eye poses. /// XR_SUCCESS for success. public XrResult GetEyeGazeData(XrEyeTrackerHTC eyeTracker, XrEyeGazeDataInfoHTC gazeInfo, out XrEyeGazeDataHTC eyeGazes) { m_gazes.type = XrStructureType.XR_TYPE_EYE_GAZE_DATA_HTC; m_gazes.next = IntPtr.Zero; m_gazes.time = m_frameState.predictedDisplayTime; eyeGazes = m_gazes; XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE; result = GetEyeGazeDataHTC(eyeTracker,ref gazeInfo,ref m_gazes); if (result == XrResult.XR_SUCCESS) { eyeGazes = m_gazes; } return result; } private XrEyePupilDataHTC m_eyePupilData = new XrEyePupilDataHTC(); /// /// Retrieves an array of XrSingleEyePupilDataHTC containing the returned data for user's pupils. /// /// Output parameter to retrieve an array of XrSingleEyePupilDataHTC. /// XR_SUCCESS for success. public bool GetEyePupilData(out XrSingleEyePupilDataHTC[] pupilData) { m_eyePupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC; m_eyePupilData.next = IntPtr.Zero; m_eyePupilData.time = m_frameState.predictedDisplayTime; pupilData = m_eyePupilData.pupilData; XrEyePupilDataInfoHTC pupilDataInfo = new XrEyePupilDataInfoHTC( in_type: XrStructureType.XR_TYPE_EYE_PUPIL_DATA_INFO_HTC, in_next: IntPtr.Zero); if (GetEyePupilData(m_EyeTracker, pupilDataInfo, out m_eyePupilData) == XrResult.XR_SUCCESS) { pupilData = m_eyePupilData.pupilData; return true; } return false; } /// /// Retrieves the XrEyePupilDataHTC data of a XrEyeTrackerHTC. /// /// An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC. /// The information to get pupil data. /// A pointer to XrEyePupilDataHTC returned by the runtime. /// XR_SUCCESS for success. public XrResult GetEyePupilData(XrEyeTrackerHTC eyeTracker, XrEyePupilDataInfoHTC pupilDataInfo, out XrEyePupilDataHTC pupilData) { m_eyePupilData.type = XrStructureType.XR_TYPE_EYE_PUPIL_DATA_HTC; m_eyePupilData.next = IntPtr.Zero; m_eyePupilData.time = m_frameState.predictedDisplayTime; pupilData = m_eyePupilData; XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE; result = GetEyePupilDataHTC(eyeTracker,ref pupilDataInfo, ref m_eyePupilData); if (result == XrResult.XR_SUCCESS) { pupilData = m_eyePupilData; } return result; } private XrEyeGeometricDataHTC m_eyeGeometricData = new XrEyeGeometricDataHTC();//XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC, IntPtr.Zero, 0); /// Output parameter to retrieve an array of XrSingleEyeGeometricDataHTC. /// XR_SUCCESS for success. public bool GetEyeGeometricData(out XrSingleEyeGeometricDataHTC[] geometricData) { m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC; m_eyeGeometricData.next = IntPtr.Zero; m_eyeGeometricData.time = m_frameState.predictedDisplayTime; geometricData = m_eyeGeometricData.eyeGeometricData; XrEyeGeometricDataInfoHTC eyeGeometricDataInfo = new XrEyeGeometricDataInfoHTC( in_type: XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_INFO_HTC, in_next: IntPtr.Zero); if (GetEyeGeometricData(m_EyeTracker, eyeGeometricDataInfo, out m_eyeGeometricData) == XrResult.XR_SUCCESS) { geometricData = m_eyeGeometricData.eyeGeometricData; return true; } return false; } /// An XrEyeTrackerHTC previously created by xrCreateEyeTrackerHTC. /// A pointer to XrEyeGeometricDataInfoHTC structure. /// A pointer to XrEyeGeometricDataHTC returned by the runtime. /// XR_SUCCESS for success. public XrResult GetEyeGeometricData(XrEyeTrackerHTC eyeTracker, XrEyeGeometricDataInfoHTC eyeGeometricDataInfo, out XrEyeGeometricDataHTC eyeGeometricData) { m_eyeGeometricData.type = XrStructureType.XR_TYPE_EYE_GEOMETRIC_DATA_HTC; m_eyeGeometricData.next = IntPtr.Zero; m_eyeGeometricData.time = m_frameState.predictedDisplayTime; eyeGeometricData = m_eyeGeometricData; XrResult result = XrResult.XR_ERROR_VALIDATION_FAILURE; result = GetEyeGeometricDataHTC(eyeTracker,ref eyeGeometricDataInfo, ref m_eyeGeometricData); if (result == XrResult.XR_SUCCESS) { eyeGeometricData = m_eyeGeometricData; } return result; } } }