webXRInput.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { Nullable } from "../types";
  2. import { Observer, Observable } from "../Misc/observable";
  3. import { IDisposable } from "../scene";
  4. import { WebXRInputSource, IWebXRControllerOptions } from './webXRInputSource';
  5. import { WebXRSessionManager } from './webXRSessionManager';
  6. import { WebXRCamera } from './webXRCamera';
  7. import { WebXRMotionControllerManager } from './motionController/webXRMotionControllerManager';
  8. /**
  9. * The schema for initialization options of the XR Input class
  10. */
  11. export interface IWebXRInputOptions {
  12. /**
  13. * If set to true no model will be automatically loaded
  14. */
  15. doNotLoadControllerMeshes?: boolean;
  16. /**
  17. * If set, this profile will be used for all controllers loaded (for example "microsoft-mixed-reality")
  18. * If not found, the xr input profile data will be used.
  19. * Profiles are defined here - https://github.com/immersive-web/webxr-input-profiles/
  20. */
  21. forceInputProfile?: string;
  22. /**
  23. * Do not send a request to the controller repository to load the profile.
  24. *
  25. * Instead, use the controllers available in babylon itself.
  26. */
  27. disableOnlineControllerRepository?: boolean;
  28. /**
  29. * A custom URL for the controllers repository
  30. */
  31. customControllersRepositoryURL?: string;
  32. /**
  33. * Should the controller model's components not move according to the user input
  34. */
  35. disableControllerAnimation?: boolean;
  36. /**
  37. * Optional options to pass to the controller. Will be overridden by the Input options where applicable
  38. */
  39. controllerOptions?: IWebXRControllerOptions;
  40. }
  41. /**
  42. * XR input used to track XR inputs such as controllers/rays
  43. */
  44. export class WebXRInput implements IDisposable {
  45. /**
  46. * XR controllers being tracked
  47. */
  48. public controllers: Array<WebXRInputSource> = [];
  49. private _frameObserver: Nullable<Observer<any>>;
  50. private _sessionEndedObserver: Nullable<Observer<any>>;
  51. private _sessionInitObserver: Nullable<Observer<any>>;
  52. /**
  53. * Event when a controller has been connected/added
  54. */
  55. public onControllerAddedObservable = new Observable<WebXRInputSource>();
  56. /**
  57. * Event when a controller has been removed/disconnected
  58. */
  59. public onControllerRemovedObservable = new Observable<WebXRInputSource>();
  60. /**
  61. * Initializes the WebXRInput
  62. * @param xrSessionManager the xr session manager for this session
  63. * @param xrCamera the WebXR camera for this session. Mainly used for teleportation
  64. * @param options = initialization options for this xr input
  65. */
  66. public constructor(
  67. /**
  68. * the xr session manager for this session
  69. */
  70. public xrSessionManager: WebXRSessionManager,
  71. /**
  72. * the WebXR camera for this session. Mainly used for teleportation
  73. */
  74. public xrCamera: WebXRCamera,
  75. private readonly options: IWebXRInputOptions = {}
  76. ) {
  77. // Remove controllers when exiting XR
  78. this._sessionEndedObserver = this.xrSessionManager.onXRSessionEnded.add(() => {
  79. this._addAndRemoveControllers([], this.controllers.map((c) => { return c.inputSource; }));
  80. });
  81. this._sessionInitObserver = this.xrSessionManager.onXRSessionInit.add((session) => {
  82. session.addEventListener("inputsourceschange", this._onInputSourcesChange);
  83. });
  84. this._frameObserver = this.xrSessionManager.onXRFrameObservable.add((frame) => {
  85. // Update controller pose info
  86. this.controllers.forEach((controller) => {
  87. controller.updateFromXRFrame(frame, this.xrSessionManager.referenceSpace);
  88. });
  89. });
  90. if (this.options.customControllersRepositoryURL) {
  91. WebXRMotionControllerManager.BaseRepositoryUrl = this.options.customControllersRepositoryURL;
  92. }
  93. if (!this.options.disableOnlineControllerRepository) {
  94. WebXRMotionControllerManager.UseOnlineRepository = true;
  95. // pre-load the profiles list to load the controllers quicker afterwards
  96. WebXRMotionControllerManager.UpdateProfilesList();
  97. } else {
  98. WebXRMotionControllerManager.UseOnlineRepository = false;
  99. }
  100. }
  101. private _onInputSourcesChange = (event: XRInputSourceChangeEvent) => {
  102. this._addAndRemoveControllers(event.added, event.removed);
  103. }
  104. private _addAndRemoveControllers(addInputs: Array<XRInputSource>, removeInputs: Array<XRInputSource>) {
  105. // Add controllers if they don't already exist
  106. let sources = this.controllers.map((c) => { return c.inputSource; });
  107. for (let input of addInputs) {
  108. if (sources.indexOf(input) === -1) {
  109. let controller = new WebXRInputSource(this.xrSessionManager.scene, input, {
  110. ...(this.options.controllerOptions || {}),
  111. forceControllerProfile: this.options.forceInputProfile,
  112. doNotLoadControllerMesh: this.options.doNotLoadControllerMeshes,
  113. disableMotionControllerAnimation: this.options.disableControllerAnimation
  114. });
  115. this.controllers.push(controller);
  116. this.onControllerAddedObservable.notifyObservers(controller);
  117. }
  118. }
  119. // Remove and dispose of controllers to be disposed
  120. let keepControllers: Array<WebXRInputSource> = [];
  121. let removedControllers: Array<WebXRInputSource> = [];
  122. this.controllers.forEach((c) => {
  123. if (removeInputs.indexOf(c.inputSource) === -1) {
  124. keepControllers.push(c);
  125. } else {
  126. removedControllers.push(c);
  127. }
  128. });
  129. this.controllers = keepControllers;
  130. removedControllers.forEach((c) => {
  131. this.onControllerRemovedObservable.notifyObservers(c);
  132. c.dispose();
  133. });
  134. }
  135. /**
  136. * Disposes of the object
  137. */
  138. public dispose() {
  139. this.controllers.forEach((c) => {
  140. c.dispose();
  141. });
  142. this.xrSessionManager.onXRFrameObservable.remove(this._frameObserver);
  143. this.xrSessionManager.onXRSessionInit.remove(this._sessionInitObserver);
  144. this.xrSessionManager.onXRSessionEnded.remove(this._sessionEndedObserver);
  145. this.onControllerAddedObservable.clear();
  146. this.onControllerRemovedObservable.clear();
  147. }
  148. }