Created
February 3, 2020 23:12
-
-
Save julenka/874ce481287dd49e59cc3f1fab4465cf to your computer and use it in GitHub Desktop.
Get Eye Gaze from HoloLens, thanks to @maluoi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
#if WINDOWS_UWP | |
using System; | |
using System.Runtime.InteropServices; | |
using UnityEngine.XR.WSA; | |
using Windows.UI.Input; | |
using Windows.UI.Input.Spatial; | |
using Windows.Perception; | |
using Windows.Perception.Spatial; | |
using Windows.Perception.People; | |
#endif | |
/// <summary> | |
/// Usage: EyeGaze is a static class, just call EyeGaze.Ray for the gaze information! First | |
/// use will also request user permission, and ray information will default to head-gaze until | |
/// permission is provided. If you need to prime the permission, just access any of the public | |
/// properties to trigger the permission request. Check CommentDocs on Initialized and | |
/// EyesAvailable for usage and details there. | |
/// </summary> | |
public class EyeGaze { | |
#region Singleton | |
static EyeGaze instance = null; | |
static EyeGaze Instance { get { | |
if (instance == null) | |
instance = new EyeGaze(); | |
return instance; | |
} } | |
#endregion | |
#region Fields | |
Ray ray; | |
float lastUpdate = 0; | |
bool eyesAvailable = false; | |
bool initialized = false; | |
#if WINDOWS_UWP | |
SpatialCoordinateSystem spatialCoordinateSystem; | |
#endif | |
#endregion | |
#region Public Interface | |
/// <summary> | |
/// Get the eye ray! If this isn't initialized, or if eyes aren't available, this will be head | |
/// position and direction instead. | |
/// </summary> | |
public static Ray Ray { get { | |
EyeGaze inst = Instance; | |
inst.Update(); | |
return inst.ray; | |
} } | |
/// <summary> | |
/// Has the system been initialized yet? On-device, this is mostly just asking for permission | |
/// to use the eye data. If this is true, then EyesAvailable will tell us for sure if we'll have | |
/// access to the eyes. | |
/// </summary> | |
public static bool Initialized { get { return Instance.initialized; } } | |
/// <summary> | |
/// If this is not initialize, then this will be false. If it is initialized, then this will tell | |
/// us if we're using eye information for the Ray, or if we're using just plain old head gaze. | |
/// </summary> | |
public static bool EyesAvailable { get { return Instance.eyesAvailable; } } | |
#endregion | |
#region Methods | |
EyeGaze() { | |
#if WINDOWS_UWP | |
// Grab a coordinate system from the native side | |
spatialCoordinateSystem = Marshal.GetObjectForIUnknown(WorldManager.GetNativeISpatialCoordinateSystemPtr()) as SpatialCoordinateSystem; | |
// Ask for permission to grab the eye information | |
if (EyesPose.IsSupported()) { | |
EyesPose.RequestAccessAsync().Completed = (info, status) => { | |
eyesAvailable = info.GetResults() == GazeInputAccessStatus.Allowed; | |
initialized = true; | |
}; | |
} else { | |
eyesAvailable = false; | |
initialized = true; | |
} | |
#else | |
// No eyes, use fallback | |
eyesAvailable = false; | |
initialized = true; | |
#endif | |
} | |
void Update() { | |
// Only update every tick | |
if (lastUpdate == Time.time) | |
return; | |
lastUpdate = Time.time; | |
// Update from eyes if they're available, otherwise, fall back to head gaze | |
if (eyesAvailable) UpdateEyes(); | |
else UpdateGaze(); | |
} | |
void UpdateGaze() { | |
Camera main = Camera.main; | |
ray.origin = main.transform.position; | |
ray.direction = main.transform.forward; | |
} | |
void UpdateEyes() { | |
#if WINDOWS_UWP | |
SpatialPointerPose pointerPose = SpatialPointerPose.TryGetAtTimestamp(spatialCoordinateSystem, PerceptionTimestampHelper.FromHistoricalTargetTime(DateTimeOffset.Now)); | |
if (pointerPose != null && pointerPose.Eyes != null && pointerPose.Eyes.Gaze.HasValue) { | |
EyesPose eyes = pointerPose.Eyes; | |
// Copy vectors, and convert to the correct coordinate space | |
ray.origin = new Vector3(eyes.Gaze.Value.Origin.X, eyes.Gaze.Value.Origin.Y, -eyes.Gaze.Value.Origin.Z); | |
ray.direction = new Vector3(eyes.Gaze.Value.Direction.X, eyes.Gaze.Value.Direction.Y, -eyes.Gaze.Value.Direction.Z); | |
} | |
#endif | |
} | |
#endregion | |
} |
Even when hand rays are active, the following should provide you with a consistently handled eye gaze signal: CoreServices.InputSystem?.EyeGazeProvider. The FollowEyeGaze.cs or EyeTrackingTarget.cs scripts in MRTK provide examples for how to use eye gaze input even if not acting as a primary input.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I would not recommend this approach as this pulls the raw eye gaze data and circumvents any filtering handled by the Mixed Reality Eye Gaze Provider.