WebXREyeTracking.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager.js";
  2. import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
  3. import { Observable } from "../../Misc/observable.js";
  4. import { Vector3, TmpVectors } from "../../Maths/math.vector.js";
  5. import { Ray } from "../../Culling/ray.js";
  6. /**
  7. * The WebXR Eye Tracking feature grabs eye data from the device and provides it in an easy-access format.
  8. * Currently only enabled for BabylonNative applications.
  9. */
  10. export class WebXREyeTracking extends WebXRAbstractFeature {
  11. /**
  12. * Creates a new instance of the XR eye tracking feature.
  13. * @param _xrSessionManager An instance of WebXRSessionManager.
  14. */
  15. constructor(_xrSessionManager) {
  16. super(_xrSessionManager);
  17. /**
  18. * This observable will notify registered observers when eye tracking starts
  19. */
  20. this.onEyeTrackingStartedObservable = new Observable();
  21. /**
  22. * This observable will notify registered observers when eye tracking ends
  23. */
  24. this.onEyeTrackingEndedObservable = new Observable();
  25. /**
  26. * This observable will notify registered observers on each frame that has valid tracking
  27. */
  28. this.onEyeTrackingFrameUpdateObservable = new Observable();
  29. this._eyeTrackingStartListener = (event) => {
  30. this._latestEyeSpace = event.gazeSpace;
  31. this._gazeRay = new Ray(Vector3.Zero(), Vector3.Forward());
  32. this.onEyeTrackingStartedObservable.notifyObservers(this._gazeRay);
  33. };
  34. this._eyeTrackingEndListener = () => {
  35. this._latestEyeSpace = null;
  36. this._gazeRay = null;
  37. this.onEyeTrackingEndedObservable.notifyObservers();
  38. };
  39. this.xrNativeFeatureName = "eye-tracking";
  40. if (this._xrSessionManager.session) {
  41. this._init();
  42. }
  43. else {
  44. this._xrSessionManager.onXRSessionInit.addOnce(() => {
  45. this._init();
  46. });
  47. }
  48. }
  49. /**
  50. * Dispose this feature and all of the resources attached.
  51. */
  52. dispose() {
  53. super.dispose();
  54. this._xrSessionManager.session.removeEventListener("eyetrackingstart", this._eyeTrackingStartListener);
  55. this._xrSessionManager.session.removeEventListener("eyetrackingend", this._eyeTrackingEndListener);
  56. this.onEyeTrackingStartedObservable.clear();
  57. this.onEyeTrackingEndedObservable.clear();
  58. this.onEyeTrackingFrameUpdateObservable.clear();
  59. }
  60. /**
  61. * Returns whether the gaze data is valid or not
  62. * @returns true if the data is valid
  63. */
  64. get isEyeGazeValid() {
  65. return !!this._gazeRay;
  66. }
  67. /**
  68. * Get a reference to the gaze ray. This data is valid while eye tracking persists, and will be set to null when gaze data is no longer available
  69. * @returns a reference to the gaze ray if it exists and is valid, returns null otherwise.
  70. */
  71. getEyeGaze() {
  72. return this._gazeRay;
  73. }
  74. _onXRFrame(frame) {
  75. if (!this.attached || !frame) {
  76. return;
  77. }
  78. if (this._latestEyeSpace && this._gazeRay) {
  79. const pose = frame.getPose(this._latestEyeSpace, this._xrSessionManager.referenceSpace);
  80. if (pose) {
  81. this._gazeRay.origin.set(pose.transform.position.x, pose.transform.position.y, pose.transform.position.z).scaleInPlace(this._xrSessionManager.worldScalingFactor);
  82. const quat = pose.transform.orientation;
  83. TmpVectors.Quaternion[0].set(quat.x, quat.y, quat.z, quat.w);
  84. if (!this._xrSessionManager.scene.useRightHandedSystem) {
  85. this._gazeRay.origin.z *= -1;
  86. TmpVectors.Quaternion[0].z *= -1;
  87. TmpVectors.Quaternion[0].w *= -1;
  88. Vector3.LeftHandedForwardReadOnly.rotateByQuaternionToRef(TmpVectors.Quaternion[0], this._gazeRay.direction);
  89. }
  90. else {
  91. Vector3.RightHandedForwardReadOnly.rotateByQuaternionToRef(TmpVectors.Quaternion[0], this._gazeRay.direction);
  92. }
  93. this.onEyeTrackingFrameUpdateObservable.notifyObservers(this._gazeRay);
  94. }
  95. }
  96. }
  97. _init() {
  98. // Only supported by BabylonNative
  99. if (this._xrSessionManager.isNative) {
  100. this._xrSessionManager.session.addEventListener("eyetrackingstart", this._eyeTrackingStartListener);
  101. this._xrSessionManager.session.addEventListener("eyetrackingend", this._eyeTrackingEndListener);
  102. }
  103. }
  104. }
  105. /**
  106. * The module's name
  107. */
  108. WebXREyeTracking.Name = WebXRFeatureName.EYE_TRACKING;
  109. /**
  110. * The (Babylon) version of this module.
  111. * This is an integer representing the implementation version.
  112. * This number does not correspond to the WebXR specs version
  113. */
  114. WebXREyeTracking.Version = 1;
  115. WebXRFeaturesManager.AddWebXRFeature(WebXREyeTracking.Name, (xrSessionManager) => {
  116. return () => new WebXREyeTracking(xrSessionManager);
  117. }, WebXREyeTracking.Version, false);
  118. //# sourceMappingURL=WebXREyeTracking.js.map