Last active
October 24, 2022 02:24
-
-
Save qxxxb/d1357828c16d27873751280d7222ea25 to your computer and use it in GitHub Desktop.
Render gaze rays for each eye on the HTC Vive Pro Eye.
This file contains 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 System.Runtime.InteropServices; | |
using UnityEngine; | |
using ViveSR.anipal.Eye; | |
public class GazeRays : MonoBehaviour | |
{ | |
private static EyeData eyeData = new EyeData(); | |
private bool eye_callback_registered = false; | |
// Render gaze rays. | |
// Press K to toggle. | |
// Left is blue, right is red | |
private bool renderVisuals = true; | |
// Freeze the gaze visuals so we can inspect them more easily. | |
// Press F to toggle. | |
private bool isFrozen = false; | |
private GameObject LeftVisual; | |
private GameObject RightVisual; | |
void InitLineRenderer(LineRenderer lr) | |
{ | |
lr.startWidth = 0.005f; | |
lr.endWidth = 0.005f; | |
lr.material = new Material(Shader.Find("Sprites/Default")); | |
} | |
void Start() | |
{ | |
if (!SRanipal_Eye_Framework.Instance.EnableEye) | |
{ | |
enabled = false; | |
return; | |
} | |
LeftVisual = new GameObject("Left gaze ray visual"); | |
RightVisual = new GameObject("Right gaze ray visual"); | |
LeftVisual.AddComponent<LineRenderer>(); | |
RightVisual.AddComponent<LineRenderer>(); | |
{ | |
LineRenderer lr = LeftVisual.GetComponent<LineRenderer>(); | |
InitLineRenderer(lr); | |
lr.startColor = Color.blue; | |
lr.endColor = Color.blue; | |
} | |
{ | |
LineRenderer lr = RightVisual.GetComponent<LineRenderer>(); | |
InitLineRenderer(lr); | |
lr.startColor = Color.red; | |
lr.endColor = Color.red; | |
} | |
} | |
void InitEyeData() | |
{ | |
if ( | |
SRanipal_Eye_Framework.Status != SRanipal_Eye_Framework.FrameworkStatus.WORKING && | |
SRanipal_Eye_Framework.Status != SRanipal_Eye_Framework.FrameworkStatus.NOT_SUPPORT | |
) return; | |
if ( | |
SRanipal_Eye_Framework.Instance.EnableEyeDataCallback == true && | |
eye_callback_registered == false | |
) { | |
SRanipal_Eye.WrapperRegisterEyeDataCallback( | |
Marshal.GetFunctionPointerForDelegate((SRanipal_Eye.CallbackBasic)EyeCallback) | |
); | |
eye_callback_registered = true; | |
} | |
else if ( | |
SRanipal_Eye_Framework.Instance.EnableEyeDataCallback == false && | |
eye_callback_registered == true | |
) { | |
SRanipal_Eye.WrapperUnRegisterEyeDataCallback( | |
Marshal.GetFunctionPointerForDelegate((SRanipal_Eye.CallbackBasic)EyeCallback) | |
); | |
eye_callback_registered = false; | |
} | |
} | |
// Store the results from GetGazeRay for both eyes | |
struct RawGazeRays | |
{ | |
public Vector3 leftOrigin; | |
public Vector3 leftDir; | |
public Vector3 rightOrigin; | |
public Vector3 rightDir; | |
// Gaze origin and direction are in local coordinates relative to the | |
// camera. Here we convert them to absolute coordinates. | |
public RawGazeRays Absolute(Transform t) | |
{ | |
var ans = new RawGazeRays(); | |
ans.leftOrigin = t.TransformPoint(leftOrigin); | |
ans.rightOrigin = t.TransformPoint(rightOrigin); | |
ans.leftDir = t.TransformDirection(leftDir); | |
ans.rightDir = t.TransformDirection(rightDir); | |
return ans; | |
} | |
} | |
void GetGazeRays(out RawGazeRays r) | |
{ | |
r = new RawGazeRays(); | |
if (eye_callback_registered) | |
{ | |
// These return a bool depending whether the gaze ray is available. | |
// We can ignore this return value for now. | |
SRanipal_Eye.GetGazeRay(GazeIndex.LEFT, out r.leftOrigin, out r.leftDir, eyeData); | |
SRanipal_Eye.GetGazeRay(GazeIndex.RIGHT, out r.rightOrigin, out r.rightDir, eyeData); | |
} | |
else | |
{ | |
SRanipal_Eye.GetGazeRay(GazeIndex.LEFT, out r.leftOrigin, out r.leftDir); | |
SRanipal_Eye.GetGazeRay(GazeIndex.RIGHT, out r.rightOrigin, out r.rightDir); | |
} | |
// Convert from right-handed to left-handed coordinate system. | |
// This fixes a bug in the SRanipal Unity package. | |
// r.leftOrigin.x *= -1; | |
// r.rightOrigin.x *= -1; | |
} | |
void RenderGazeRays(RawGazeRays gr) | |
{ | |
LineRenderer llr = LeftVisual.GetComponent<LineRenderer>(); | |
llr.SetPosition(0, gr.leftOrigin); | |
llr.SetPosition(1, gr.leftOrigin + gr.leftDir * 20); | |
LineRenderer rlr = RightVisual.GetComponent<LineRenderer>(); | |
rlr.SetPosition(0, gr.rightOrigin); | |
rlr.SetPosition(1, gr.rightOrigin + gr.rightDir * 20); | |
} | |
void SetRenderVisuals(bool value) | |
{ | |
renderVisuals = value; | |
LeftVisual.SetActive(value); | |
RightVisual.SetActive(value); | |
} | |
void Update() | |
{ | |
if (Input.GetKeyDown(KeyCode.K)) | |
{ | |
// Toggle visuals | |
SetRenderVisuals(!renderVisuals); | |
} | |
if (Input.GetKeyDown(KeyCode.F)) | |
{ | |
// Freeze visuals | |
isFrozen = !isFrozen; | |
} | |
if (renderVisuals && !isFrozen) | |
{ | |
InitEyeData(); | |
RawGazeRays localGazeRays; | |
GetGazeRays(out localGazeRays); | |
RawGazeRays gazeRays = localGazeRays.Absolute(Camera.main.transform); | |
RenderGazeRays(gazeRays); | |
} | |
} | |
void Release() | |
{ | |
if (eye_callback_registered == true) | |
{ | |
SRanipal_Eye.WrapperUnRegisterEyeDataCallback( | |
Marshal.GetFunctionPointerForDelegate((SRanipal_Eye.CallbackBasic)EyeCallback) | |
); | |
eye_callback_registered = false; | |
} | |
Destroy(LeftVisual); | |
Destroy(RightVisual); | |
} | |
private static void EyeCallback(ref EyeData eye_data) | |
{ | |
eyeData = eye_data; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great code! I tested it and it works fine. I was wondering. How do I output the new gaze origin and direction coordinates (world space coordinates)? I implemented your code onto mine (please find it below for your reference). However, I can only output the local coordinates of gaze origin and direction. Please let me know. Thank you!
`using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using System;
using System.IO;
using ViveSR.anipal.Eye;
using ViveSR.anipal;
using ViveSR;
///
/// Example usage for eye tracking callback
/// Note: Callback runs on a separate thread to report at ~120hz.
/// Unity is not threadsafe and cannot call any UnityEngine api from within callback thread.
///
public class GazeRays1 : MonoBehaviour
{
// ********************************************************************************************************************
//
// Define user ID information.
// - The developers can define the user ID format such as "ABC_001". The ID is used for the name of text file
// that records the measured eye movement data.
//
// ********************************************************************************************************************
public static int UserID = 11; // Always change Participant Number for every participant
public static int scenario = 22; // Always change Scenario Number for every scenario
//public string UserID; // Definte ID number such as 001, ABC001, etc.
public string Path = Directory.GetCurrentDirectory();
//string File_Path = Directory.GetCurrentDirectory() + "\video_" + UserID + ".txt";
public string File_Path = Directory.GetCurrentDirectory() + "P" + UserID + "_" + "S" + scenario + ".txt";
}`