Skip to content

Instantly share code, notes, and snippets.

@julenka
Created February 3, 2020 23:12
Show Gist options
  • Save julenka/874ce481287dd49e59cc3f1fab4465cf to your computer and use it in GitHub Desktop.
Save julenka/874ce481287dd49e59cc3f1fab4465cf to your computer and use it in GitHub Desktop.
Get Eye Gaze from HoloLens, thanks to @maluoi
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
}
@sostel
Copy link

sostel commented Feb 4, 2020

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.

@sostel
Copy link

sostel commented Feb 4, 2020

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