WebXRHitTestLegacy.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { WebXRFeaturesManager, WebXRFeatureName } from "../webXRFeaturesManager.js";
  2. import { Observable } from "../../Misc/observable.js";
  3. import { Vector3, Matrix } from "../../Maths/math.vector.js";
  4. import { WebXRAbstractFeature } from "./WebXRAbstractFeature.js";
  5. import { Tools } from "../../Misc/tools.js";
  6. /**
  7. * The currently-working hit-test module.
  8. * Hit test (or Ray-casting) is used to interact with the real world.
  9. * For further information read here - https://github.com/immersive-web/hit-test
  10. */
  11. export class WebXRHitTestLegacy extends WebXRAbstractFeature {
  12. /**
  13. * Creates a new instance of the (legacy version) hit test feature
  14. * @param _xrSessionManager an instance of WebXRSessionManager
  15. * @param options options to use when constructing this feature
  16. */
  17. constructor(_xrSessionManager,
  18. /**
  19. * options to use when constructing this feature
  20. */
  21. options = {}) {
  22. super(_xrSessionManager);
  23. this.options = options;
  24. // in XR space z-forward is negative
  25. this._direction = new Vector3(0, 0, -1);
  26. this._mat = new Matrix();
  27. this._onSelectEnabled = false;
  28. this._origin = new Vector3(0, 0, 0);
  29. /**
  30. * Populated with the last native XR Hit Results
  31. */
  32. this.lastNativeXRHitResults = [];
  33. /**
  34. * Triggered when new babylon (transformed) hit test results are available
  35. */
  36. this.onHitTestResultObservable = new Observable();
  37. this._onHitTestResults = (xrResults) => {
  38. const mats = xrResults.map((result) => {
  39. const mat = Matrix.FromArray(result.hitMatrix);
  40. if (!this._xrSessionManager.scene.useRightHandedSystem) {
  41. mat.toggleModelMatrixHandInPlace();
  42. }
  43. // if (this.options.coordinatesSpace === Space.WORLD) {
  44. if (this.options.worldParentNode) {
  45. mat.multiplyToRef(this.options.worldParentNode.getWorldMatrix(), mat);
  46. }
  47. return {
  48. xrHitResult: result,
  49. transformationMatrix: mat,
  50. };
  51. });
  52. this.lastNativeXRHitResults = xrResults;
  53. this.onHitTestResultObservable.notifyObservers(mats);
  54. };
  55. // can be done using pointerdown event, and xrSessionManager.currentFrame
  56. this._onSelect = (event) => {
  57. if (!this._onSelectEnabled) {
  58. return;
  59. }
  60. WebXRHitTestLegacy.XRHitTestWithSelectEvent(event, this._xrSessionManager.referenceSpace);
  61. };
  62. this.xrNativeFeatureName = "hit-test";
  63. Tools.Warn("A newer version of this plugin is available");
  64. }
  65. /**
  66. * execute a hit test with an XR Ray
  67. *
  68. * @param xrSession a native xrSession that will execute this hit test
  69. * @param xrRay the ray (position and direction) to use for ray-casting
  70. * @param referenceSpace native XR reference space to use for the hit-test
  71. * @param filter filter function that will filter the results
  72. * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
  73. */
  74. static XRHitTestWithRay(xrSession, xrRay, referenceSpace, filter) {
  75. return xrSession.requestHitTest(xrRay, referenceSpace).then((results) => {
  76. const filterFunction = filter || ((result) => !!result.hitMatrix);
  77. return results.filter(filterFunction);
  78. });
  79. }
  80. /**
  81. * Execute a hit test on the current running session using a select event returned from a transient input (such as touch)
  82. * @param event the (select) event to use to select with
  83. * @param referenceSpace the reference space to use for this hit test
  84. * @returns a promise that resolves with an array of native XR hit result in xr coordinates system
  85. */
  86. static XRHitTestWithSelectEvent(event, referenceSpace) {
  87. const targetRayPose = event.frame.getPose(event.inputSource.targetRaySpace, referenceSpace);
  88. if (!targetRayPose) {
  89. return Promise.resolve([]);
  90. }
  91. const targetRay = new XRRay(targetRayPose.transform);
  92. return this.XRHitTestWithRay(event.frame.session, targetRay, referenceSpace);
  93. }
  94. /**
  95. * attach this feature
  96. * Will usually be called by the features manager
  97. *
  98. * @returns true if successful.
  99. */
  100. attach() {
  101. if (!super.attach()) {
  102. return false;
  103. }
  104. if (this.options.testOnPointerDownOnly) {
  105. this._xrSessionManager.session.addEventListener("select", this._onSelect, false);
  106. }
  107. return true;
  108. }
  109. /**
  110. * detach this feature.
  111. * Will usually be called by the features manager
  112. *
  113. * @returns true if successful.
  114. */
  115. detach() {
  116. if (!super.detach()) {
  117. return false;
  118. }
  119. // disable select
  120. this._onSelectEnabled = false;
  121. this._xrSessionManager.session.removeEventListener("select", this._onSelect);
  122. return true;
  123. }
  124. /**
  125. * Dispose this feature and all of the resources attached
  126. */
  127. dispose() {
  128. super.dispose();
  129. this.onHitTestResultObservable.clear();
  130. }
  131. _onXRFrame(frame) {
  132. // make sure we do nothing if (async) not attached
  133. if (!this.attached || this.options.testOnPointerDownOnly) {
  134. return;
  135. }
  136. const pose = frame.getViewerPose(this._xrSessionManager.referenceSpace);
  137. if (!pose) {
  138. return;
  139. }
  140. Matrix.FromArrayToRef(pose.transform.matrix, 0, this._mat);
  141. Vector3.TransformCoordinatesFromFloatsToRef(0, 0, 0, this._mat, this._origin);
  142. Vector3.TransformCoordinatesFromFloatsToRef(0, 0, -1, this._mat, this._direction);
  143. this._direction.subtractInPlace(this._origin);
  144. this._direction.normalize();
  145. const ray = new XRRay({ x: this._origin.x, y: this._origin.y, z: this._origin.z, w: 0 }, { x: this._direction.x, y: this._direction.y, z: this._direction.z, w: 0 });
  146. WebXRHitTestLegacy.XRHitTestWithRay(this._xrSessionManager.session, ray, this._xrSessionManager.referenceSpace).then(this._onHitTestResults);
  147. }
  148. }
  149. /**
  150. * The module's name
  151. */
  152. WebXRHitTestLegacy.Name = WebXRFeatureName.HIT_TEST;
  153. /**
  154. * The (Babylon) version of this module.
  155. * This is an integer representing the implementation version.
  156. * This number does not correspond to the WebXR specs version
  157. */
  158. WebXRHitTestLegacy.Version = 1;
  159. //register the plugin versions
  160. WebXRFeaturesManager.AddWebXRFeature(WebXRHitTestLegacy.Name, (xrSessionManager, options) => {
  161. return () => new WebXRHitTestLegacy(xrSessionManager, options);
  162. }, WebXRHitTestLegacy.Version, false);
  163. //# sourceMappingURL=WebXRHitTestLegacy.js.map