#if XAPI_REGISTRY_EXISTS using System; using System.ComponentModel; using OmiLAXR.Components; using OmiLAXR.Composers; using OmiLAXR.TrackingBehaviours.Learner.Gaze; using UnityEngine; namespace OmiLAXR.xAPI.Composers.Attention { /// /// xAPI composer for camera-based gaze tracking. /// Emits statements for gaze enter (gazed), fixation (fixated), and gaze leave (averted) /// with spatial context, target metadata, and gaze geometry. /// [AddComponentMenu("OmiLAXR / 4) Composers / [xAPI] Camera Gaze Composer")] [Description("Creates statements:" + "\n- actor gazed vrObject with vrObjectName(String), frustum(Frustum), position(vec3), rotation(vec3), scale(vec3), startGazeCoordinates(vec3), endGazeCoordinates(vec3), viewingAngle(deg), gazeIncidenceAngle(deg)" + "\n- actor fixated vrObject with ref(prior gazed), duration(Duration), timestamp(DateTime), aoiName(String), totalFixations(Int), frustum(Frustum), startGazeCoordinates(vec3), endGazeCoordinates(vec3), fixationStart(DateTime), fixationEnd(DateTime), viewingAngle(deg), gazeIncidenceAngle(deg)" + "\n- actor averted vrObject with ref(prior gazed), vrObjectName(String), frustum(Frustum), position(vec3), rotation(vec3), scale(vec3), startGazeCoordinates(vec3), endGazeCoordinates(vec3), viewingAngle(deg), gazeIncidenceAngle(deg)")] public sealed class CameraGazeComposer : xApiComposer { /// /// Categorizes this composer under attention analytics. /// public override ComposerGroup GetGroup() => ComposerGroup.Attention; /// /// Author info for attribution/support. /// public override Author GetAuthor() => new Author("Sergej Görzen", "goerzen@cs.rwth-aachen.de"); /// /// Binds xAPI statement generation to gaze tracking events: gazed, fixated, averted. /// protected override void Compose(CameraGazeTrackingBehaviour tb) { // Gaze entered a VR object. tb.OnGazeEntered.AddHandler((sender, cameraData) => { var gazeHit = cameraData.Hit; var t = gazeHit.Source.transform; var go = gazeHit.Target.gameObject; var stmt = actor.Does(xapi.attention.verbs.gazed) .Activity(xapi.virtualReality.activities.vrObject) .WithValue(xapi.virtualReality.extensions.result .frustum(cameraData.Frustum) .position(t.position) .rotation(t.eulerAngles) .scale(t.localScale)) .WithValue(xapi.virtualReality.extensions.activity .vrObjectName(go.GetTrackingName())) .WithResult(xapi.attention.extensions.result .startGazeCoordinates(cameraData.GazeOriginWorld) // origin of gaze ray (world) .endGazeCoordinates(cameraData.GazePointWorld)) // gaze hit point (world) .WithResult(xapi.attention.extensions.result .viewingAngle(cameraData.Hit.ViewingAngleDeg) .gazeIncidenceAngle(cameraData.Hit.IncidenceAngleDeg)); // Store to reference from fixation/averted. StoreStatement(sender, cameraData.Hit.Source, stmt); SendStatement(sender, stmt); }); // Sustained fixation on the current VR object. tb.OnFixated.AddHandler((sender, cameraData, fixationData) => { var t = fixationData.Hit.Source.transform; // get reference id from enter statement var go = fixationData.Hit.Target.gameObject; var refStmt = RestoreStatement(sender, fixationData.Hit.Source); // temp end time var endTime = fixationData.EndTime!.Value; // create fixation statement var stmt = actor.Does(xapi.attention.verbs.fixated) .Activity(xapi.virtualReality.activities.vrObject) .WithRef(refStmt) .WithDuration(fixationData.Duration) .WithTimestamp(endTime) .WithValue(xapi.attention.extensions.activity .aoiName(go.GetTrackingName()) .totalFixations(fixationData.TargetFixationCounts)) .WithValue(xapi.virtualReality.extensions.result .frustum(cameraData.Frustum) .position(t.position) .rotation(t.eulerAngles) .scale(t.localScale)) .WithResult(xapi.attention.extensions.result .startGazeCoordinates(cameraData.GazeOriginWorld) // origin of gaze ray (world) .endGazeCoordinates(cameraData.GazePointWorld)) // gaze hit point (world) .WithResult(xapi.attention.extensions.result .fixationStart(fixationData.StartTime!.Value) .fixationEnd(endTime) .viewingAngle(cameraData.Hit.ViewingAngleDeg) .gazeIncidenceAngle(cameraData.Hit.IncidenceAngleDeg)); SendStatement(sender, stmt); }); // Gaze left the VR object. tb.OnGazeLeft.AddHandler((sender, cameraData) => { var t = cameraData.Hit.Source.transform; var go = cameraData.Hit.Target.gameObject; // get reference id from enter statement var refStmt = RestoreStatement(sender, cameraData.Hit.Source); // temp end time var endTime = DateTime.Now; var stmt = actor.Does(xapi.attention.verbs.averted) .Activity(xapi.virtualReality.activities.vrObject) .WithValue(xapi.virtualReality.extensions.result .frustum(cameraData.Frustum) .position(t.position) .rotation(t.eulerAngles) .scale(t.localScale)) .WithValue(xapi.virtualReality.extensions.activity .vrObjectName(go.GetTrackingName())) .WithResult(xapi.attention.extensions.result .startGazeCoordinates(cameraData.GazeOriginWorld) // origin of gaze ray (world) .endGazeCoordinates(cameraData.GazePointWorld)) // gaze hit point (world) .WithResult(xapi.attention.extensions.result .viewingAngle(cameraData.Hit.ViewingAngleDeg) .gazeIncidenceAngle(cameraData.Hit.IncidenceAngleDeg)) .WithRef(refStmt); SendStatement(sender, stmt); }); } } } #endif