webXRInput.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { Nullable } from "../../types";
  2. import { Observer, Observable } from "../../Misc/observable";
  3. import { IDisposable } from "../../scene";
  4. import { WebXRExperienceHelper } from "./webXRExperienceHelper";
  5. import { WebXRController } from './webXRController';
  6. import { WebXRState } from './webXRTypes';
  7. /**
  8. * XR input used to track XR inputs such as controllers/rays
  9. */
  10. export class WebXRInput implements IDisposable {
  11. /**
  12. * XR controllers being tracked
  13. */
  14. public controllers: Array<WebXRController> = [];
  15. private _frameObserver: Nullable<Observer<any>>;
  16. private _stateObserver: Nullable<Observer<any>>;
  17. /**
  18. * Event when a controller has been connected/added
  19. */
  20. public onControllerAddedObservable = new Observable<WebXRController>();
  21. /**
  22. * Event when a controller has been removed/disconnected
  23. */
  24. public onControllerRemovedObservable = new Observable<WebXRController>();
  25. /**
  26. * Initializes the WebXRInput
  27. * @param baseExperience experience helper which the input should be created for
  28. */
  29. public constructor(
  30. /**
  31. * Base experience the input listens to
  32. */
  33. public baseExperience: WebXRExperienceHelper
  34. ) {
  35. // Remove controllers when exiting XR
  36. this._stateObserver = baseExperience.onStateChangedObservable.add((s) => {
  37. if (s === WebXRState.NOT_IN_XR) {
  38. this._addAndRemoveControllers([], this.controllers.map((c) => {return c.inputSource; }));
  39. }
  40. });
  41. this._frameObserver = baseExperience.sessionManager.onXRFrameObservable.add(() => {
  42. if (!baseExperience.sessionManager.currentFrame) {
  43. return;
  44. }
  45. // Start listing to input add/remove event
  46. if (this.controllers.length == 0 && baseExperience.sessionManager.session.inputSources && baseExperience.sessionManager.session.inputSources.length > 0) {
  47. this._addAndRemoveControllers(baseExperience.sessionManager.session.inputSources, []);
  48. baseExperience.sessionManager.session.addEventListener("inputsourceschange", this._onInputSourcesChange);
  49. }
  50. // Update controller pose info
  51. this.controllers.forEach((controller) => {
  52. controller.updateFromXRFrame(baseExperience.sessionManager.currentFrame!, baseExperience.sessionManager.referenceSpace);
  53. });
  54. });
  55. }
  56. private _onInputSourcesChange = (event: XRInputSourceChangeEvent) => {
  57. this._addAndRemoveControllers(event.added, event.removed);
  58. }
  59. private _addAndRemoveControllers(addInputs: Array<XRInputSource>, removeInputs: Array<XRInputSource>) {
  60. // Add controllers if they don't already exist
  61. let sources = this.controllers.map((c) => {return c.inputSource; });
  62. for (let input of addInputs) {
  63. if (sources.indexOf(input) === -1) {
  64. let controller = new WebXRController(this.baseExperience.camera._scene, input, this.baseExperience.container);
  65. this.controllers.push(controller);
  66. this.onControllerAddedObservable.notifyObservers(controller);
  67. }
  68. }
  69. // Remove and dispose of controllers to be disposed
  70. let keepControllers: Array<WebXRController> = [];
  71. let removedControllers: Array<WebXRController> = [];
  72. this.controllers.forEach((c) => {
  73. if (removeInputs.indexOf(c.inputSource) === -1) {
  74. keepControllers.push(c);
  75. }else {
  76. removedControllers.push(c);
  77. }
  78. });
  79. this.controllers = keepControllers;
  80. removedControllers.forEach((c) => {
  81. this.onControllerRemovedObservable.notifyObservers(c);
  82. c.dispose();
  83. });
  84. }
  85. /**
  86. * Disposes of the object
  87. */
  88. public dispose() {
  89. this.controllers.forEach((c) => {
  90. c.dispose();
  91. });
  92. this.baseExperience.sessionManager.onXRFrameObservable.remove(this._frameObserver);
  93. this.baseExperience.onStateChangedObservable.remove(this._stateObserver);
  94. }
  95. }