// 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;
}
}
}