Skip to content

Commit 7e75003

Browse files
committed
Fully implement eye tracking evaluation
1 parent 49970ce commit 7e75003

8 files changed

Lines changed: 1559 additions & 1540 deletions

File tree

Assets/Prefabs/ETAssessment.prefab

Lines changed: 1062 additions & 54 deletions
Large diffs are not rendered by default.

Assets/Scenes/AttentionExperiment.unity

Lines changed: 418 additions & 1449 deletions
Large diffs are not rendered by default.

Assets/Scripts/CalibrationAssessment.cs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
using System.Collections;
33

44
using System.Collections.Generic;
5+
using System.Linq;
6+
using ScriptableObjects;
7+
using ScriptableObjects.Variables;
58
using UnityEngine;
69
using UXF;
710
using UnityEngine.UI;
11+
using UnityEngine.Windows.WebCam;
812

913
[ExecuteInEditMode]
1014
public class CalibrationAssessment : MonoBehaviour
@@ -27,20 +31,22 @@ public class CalibrationAssessment : MonoBehaviour
2731
[NonSerialized] public Transform CurrentTargetTransform;
2832

2933
private bool _presentingTarget;
30-
private float _gazeToTargetDist;
34+
public float gazeToTargetDist;
3135

3236
private readonly List<int> _remainingTargets = new List<int>() {0,1,2,3,4,5,6,7,8};
3337
private int _targetIdx;
3438

3539
public Text gazeErrorText;
36-
37-
private Block _practiceBlock;
40+
3841
private MeshRenderer[] _targetRenderers;
3942
private static readonly int ColorProperty = Shader.PropertyToID("_Color");
4043

4144
[SerializeField] private SelectEyeTracker eyeTracker;
4245
[SerializeField] private Camera vrCamera;
4346
[SerializeField] private GameObject[] targets;
47+
[SerializeField] private Tracker assessmentTracker;
48+
[SerializeField] private SessionSettings settings;
49+
[SerializeField] private IntVariable trialCount;
4450
private GameObject[][] _targets2D;
4551

4652
public void OnValidate(){
@@ -54,22 +60,23 @@ public void OnEnable()
5460
_targets2D[1] = new [] {targets[3], targets[4], targets[5]};
5561
_targets2D[2] = new [] {targets[6], targets[7], targets[8]};
5662
_targetRenderers = new MeshRenderer[targets.Length];
57-
// _practiceBlock = Session.instance.CreateBlock();
63+
for (var i = 0; i < targets.Length; i++)
64+
{
65+
_targetRenderers[i] = targets[i].GetComponent<MeshRenderer>();
66+
_targetRenderers[i].enabled = false;
67+
}
68+
_targetRenderers[0].enabled = true;
69+
_targetIdx = 0;
70+
targetDistance = settings.stimulusDepth;
71+
CurrentTargetTransform = GETTargTransformByIndex(0);
72+
5873
RepositionTargets();
5974

6075
if(randomizeTargetOrder){
6176
ShuffleTargetList();
6277
}
6378
}
64-
65-
public void Start()
66-
{
67-
for (var i = 0; i < targets.Length; i++)
68-
{
69-
_targetRenderers[i] = targets[i].GetComponent<MeshRenderer>();
70-
}
71-
}
72-
79+
7380
private void RepositionTargets(){
7481
var halfAzRad = (azimuthWidth/2.0f) * Mathf.Deg2Rad;
7582
var halfElRad = (elevationHeight/2.0f) * Mathf.Deg2Rad;
@@ -96,6 +103,9 @@ private void RepositionTargets(){
96103
}
97104

98105
var targetBacking = transform.Find("backing");
106+
targetBacking.GetComponent<MeshFilter>().mesh.triangles =
107+
targetBacking.GetComponent<MeshFilter>().mesh.triangles.Reverse().ToArray();
108+
targetBacking.gameObject.AddComponent<MeshCollider>();
99109
targetBacking.localScale = new Vector3(targetDistance*2.0f, targetDistance*2.0f, targetDistance*2.0f);
100110
}
101111

@@ -150,7 +160,6 @@ private Transform GETTargTransformByIndex(int targetIdx){
150160

151161

152162
private void RecordFixation(){
153-
var aTrial = _practiceBlock.CreateTrial();
154163
var mr = CurrentTargetTransform.gameObject.GetComponent<MeshRenderer>();
155164

156165
StartCoroutine(PresentTarget());
@@ -161,13 +170,14 @@ IEnumerator PresentTarget()
161170
mr.material.SetColor(ColorProperty, Color.yellow);
162171
_presentingTarget = true;
163172

164-
aTrial.Begin();
165-
173+
174+
assessmentTracker.StartRecording();
166175
yield return new WaitForSeconds(fixationTime);
176+
assessmentTracker.StopRecording();
167177

168-
aTrial.End();
169-
aTrial.result["trialType"] = "CalibrationAssessment";
170-
178+
mr.material.SetColor(ColorProperty, Color.black);
179+
Session.instance.SaveDataTable(assessmentTracker.data, "EyeTrackerAssessmentTrial" + trialCount.value);
180+
Debug.Log("Data recorded for target");
171181
_presentingTarget = false;
172182
}
173183
}
@@ -224,12 +234,15 @@ private void ToggleHeadFixed(){
224234
}
225235
}
226236

227-
public void Update()
237+
public void LateUpdate()
228238
{
229-
//var gazeInHead = Camera.main.transform.InverseTransformPoint(eyeInHeadTransform.position);
230-
//_gazeToTargetDist = Vector3.Angle(CurrentTargetTransform.localPosition, gazeInHead);
231-
_gazeToTargetDist = 0.0f;
232-
gazeErrorText.text = _gazeToTargetDist.ToString("0.#");
239+
if (Physics.Raycast(vrCamera.transform.position,
240+
vrCamera.transform.TransformDirection(eyeTracker.ChosenTracker.GetLocalGazeDirection()), out var hit))
241+
{
242+
gazeToTargetDist = Vector3.Angle(CurrentTargetTransform.localPosition, vrCamera.transform.InverseTransformPoint(hit.point));
243+
}
244+
245+
gazeErrorText.text = "Current Error: " + gazeToTargetDist.ToString("0.#") + " degrees";
233246
}
234247
}
235248

Assets/Scripts/SessionManager.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,17 +168,15 @@ public void CalibratePupilLabs()
168168

169169
private void CalibrationSuccessful()
170170
{
171-
if (_isPaused)
172-
pauseUI.SetActive(true);
171+
pauseUI.SetActive(true);
173172
infoText.gameObject.SetActive(true);
174173
infoText.text = "Calibration successful!";
175174
infoText.color = Color.green;
176175
}
177176

178177
private void CalibrationFailed()
179178
{
180-
if (_isPaused)
181-
pauseUI.SetActive(true);
179+
pauseUI.SetActive(true);
182180
infoText.gameObject.SetActive(true);
183181
infoText.text = "Calibration failed.\n Ensure that Pupil Capture is running with both eye cameras!";
184182
infoText.color = Color.red;

Assets/Scripts/Trial Manager/TrialManager.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ public class TrialManager : MonoBehaviour
7474
// Handles the management and interleaving of staircases
7575
public StaircaseManager StaircaseManager { get; private set; }
7676

77+
public void OnValidate()
78+
{
79+
trialCount.value = 0;
80+
}
81+
7782
public void Start()
7883
{
7984
trialCount.value = 1;

Assets/StreamingAssets/TEMPLATE.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161

6262
"FailOnTimeout": true,
6363

64-
"EyeTracker": "Dummy",
64+
"EyeTracker": "dum",
6565
"AttentionCueType": "Feature-based",
6666

6767
"EnableDirectionalStaircase": true,

Assets/UXF/Scripts/AssessmentTracker.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using UnityEngine;
1+
using ScriptableObjects.Variables;
2+
using UnityEngine;
23

34
namespace UXF
45
{
@@ -9,10 +10,13 @@ namespace UXF
910
public class AssessmentTracker : Tracker
1011
{
1112
private CalibrationAssessment _target;
13+
[SerializeField] private IntVariable trialCount;
1214

13-
private void Start()
15+
private void OnEnable()
1416
{
1517
_target = gameObject.GetComponent<CalibrationAssessment>();
18+
SetupDescriptorAndHeader();
19+
data = new UXFDataTable(header);
1620
}
1721

1822
/// <summary>
@@ -36,7 +40,9 @@ protected override void SetupDescriptorAndHeader()
3640
objectName + "TargetPositionZ",
3741
objectName + "TargetLocalPositionX",
3842
objectName + "TargetLocalPositionY",
39-
objectName + "TargetLocalPositionZ"
43+
objectName + "TargetLocalPositionZ",
44+
objectName + "GazeErrorDegrees",
45+
objectName + "TrialEvaluated"
4046
};
4147
}
4248

@@ -66,11 +72,31 @@ protected override UXFDataRow GetCurrentValues()
6672
(customHeader[9], position.z.ToString(format)),
6773
(customHeader[10], localPosition.x.ToString(format)),
6874
(customHeader[11], localPosition.y.ToString(format)),
69-
(customHeader[12], localPosition.z.ToString(format))
75+
(customHeader[12], localPosition.z.ToString(format)),
76+
(customHeader[13], _target.gazeToTargetDist),
77+
(customHeader[14], trialCount.value)
7078
};
7179

7280
dataRow.AddRange(values);
7381
return dataRow;
7482
}
83+
84+
/// <summary>
85+
/// Records a new row of data at current time. Can run independently of a running trial.
86+
/// </summary>
87+
public override void RecordRow()
88+
{
89+
UXFDataRow newRow = GetCurrentValues();
90+
newRow.Add(("time", Time.time));
91+
data.AddCompleteRow(newRow);
92+
}
93+
94+
public override void StartRecording()
95+
{
96+
if (data == null)
97+
data = new UXFDataTable(header);
98+
99+
recording = true;
100+
}
75101
}
76102
}

Assets/UXF/Scripts/Etc/Tracker.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ public string dataName
3939
}
4040
}
4141

42-
private bool recording;
42+
protected bool recording;
4343

4444
public bool Recording { get { return recording; } }
4545

46-
public UXFDataTable data { get; private set; } = new UXFDataTable();
46+
public UXFDataTable data { get; protected set; } = new UXFDataTable();
4747

4848
/// <summary>
4949
/// The header that will go at the top of the output file associated with this tracker
@@ -88,7 +88,7 @@ void FixedUpdate()
8888
/// <summary>
8989
/// Records a new row of data at current time.
9090
/// </summary>
91-
public void RecordRow()
91+
public virtual void RecordRow()
9292
{
9393
if (!recording) throw new System.InvalidOperationException("Tracker measurements cannot be taken when not in a trial!");
9494

@@ -100,7 +100,7 @@ public void RecordRow()
100100
/// <summary>
101101
/// Begins recording.
102102
/// </summary>
103-
public void StartRecording()
103+
public virtual void StartRecording()
104104
{
105105
data = new UXFDataTable(header);
106106
recording = true;

0 commit comments

Comments
 (0)