webXRController.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { Observable } from "../../Misc/observable";
  2. import { AbstractMesh } from "../../Meshes/abstractMesh";
  3. import { Quaternion, Vector3 } from '../../Maths/math.vector';
  4. import { Ray } from '../../Culling/ray';
  5. import { Scene } from '../../scene';
  6. import { WebXRAbstractMotionController } from './motionController/webXRAbstractController';
  7. import { WebXRMotionControllerManager } from './motionController/webXRMotionControllerManager';
  8. /**
  9. * Represents an XR input
  10. */
  11. export class WebXRController {
  12. /**
  13. * Represents the part of the controller that is held. This may not exist if the controller is the head mounted display itself, if thats the case only the pointer from the head will be availible
  14. */
  15. public grip?: AbstractMesh;
  16. /**
  17. * Pointer which can be used to select objects or attach a visible laser to
  18. */
  19. public pointer: AbstractMesh;
  20. private _gamepadMode = false;
  21. /**
  22. * If available, this is the gamepad object related to this controller.
  23. * Using this object it is possible to get click events and trackpad changes of the
  24. * webxr controller that is currently being used.
  25. */
  26. public gamepadController?: WebXRAbstractMotionController;
  27. /**
  28. * Event that fires when the controller is removed/disposed
  29. */
  30. public onDisposeObservable = new Observable<{}>();
  31. private _tmpQuaternion = new Quaternion();
  32. private _tmpVector = new Vector3();
  33. /**
  34. * Creates the controller
  35. * @see https://doc.babylonjs.com/how_to/webxr
  36. * @param scene the scene which the controller should be associated to
  37. * @param inputSource the underlying input source for the controller
  38. * @param parentContainer parent that the controller meshes should be children of
  39. */
  40. constructor(
  41. private scene: Scene,
  42. /** The underlying input source for the controller */
  43. public inputSource: XRInputSource) {
  44. this.pointer = new AbstractMesh("controllerPointer", scene);
  45. this.pointer.rotationQuaternion = new Quaternion();
  46. if (this.inputSource.gripSpace) {
  47. this.grip = new AbstractMesh("controllerGrip", this.scene);
  48. this.grip.rotationQuaternion = new Quaternion();
  49. }
  50. // for now only load motion controllers if gamepad available
  51. if (this.inputSource.gamepad) {
  52. this.gamepadController = WebXRMotionControllerManager.GetMotionControllerWithXRInput(inputSource, scene);
  53. // if the model is loaded, do your thing
  54. this.gamepadController.onModelLoadedObservable.addOnce(() => {
  55. this.gamepadController!.rootMesh!.parent = this.pointer;
  56. });
  57. }
  58. }
  59. /**
  60. * Updates the controller pose based on the given XRFrame
  61. * @param xrFrame xr frame to update the pose with
  62. * @param referenceSpace reference space to use
  63. */
  64. public updateFromXRFrame(xrFrame: XRFrame, referenceSpace: XRReferenceSpace) {
  65. let pose = xrFrame.getPose(this.inputSource.targetRaySpace, referenceSpace);
  66. // Update the pointer mesh
  67. if (pose) {
  68. this.pointer.position.copyFrom(<any>(pose.transform.position));
  69. this.pointer.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
  70. if (!this.scene.useRightHandedSystem) {
  71. this.pointer.position.z *= -1;
  72. this.pointer.rotationQuaternion!.z *= -1;
  73. this.pointer.rotationQuaternion!.w *= -1;
  74. }
  75. }
  76. // Update the grip mesh if it exists
  77. if (this.inputSource.gripSpace && this.grip) {
  78. let pose = xrFrame.getPose(this.inputSource.gripSpace, referenceSpace);
  79. if (pose) {
  80. this.grip.position.copyFrom(<any>(pose.transform.position));
  81. this.grip.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
  82. if (!this.scene.useRightHandedSystem) {
  83. this.grip.position.z *= -1;
  84. this.grip.rotationQuaternion!.z *= -1;
  85. this.grip.rotationQuaternion!.w *= -1;
  86. }
  87. }
  88. }
  89. if (this.gamepadController) {
  90. // either update buttons only or also position, if in gamepad mode
  91. this.gamepadController.update(xrFrame);
  92. }
  93. }
  94. /**
  95. * Gets a world space ray coming from the controller
  96. * @param result the resulting ray
  97. */
  98. public getWorldPointerRayToRef(result: Ray) {
  99. // Force update to ensure picked point is synced with ray
  100. let worldMatrix = this.pointer.computeWorldMatrix(true);
  101. worldMatrix.decompose(undefined, this._tmpQuaternion, undefined);
  102. this._tmpVector.set(0, 0, 1);
  103. this._tmpVector.rotateByQuaternionToRef(this._tmpQuaternion, this._tmpVector);
  104. result.origin = this.pointer.absolutePosition;
  105. result.direction.copyFrom(this._tmpVector);
  106. result.length = 1000;
  107. }
  108. /**
  109. * Get the scene associated with this controller
  110. * @returns the scene object
  111. */
  112. public getScene() {
  113. return this.scene;
  114. }
  115. /**
  116. * Disposes of the object
  117. */
  118. dispose() {
  119. if (this.grip) {
  120. this.grip.dispose();
  121. }
  122. if (this.gamepadController && this._gamepadMode) {
  123. this.gamepadController.dispose();
  124. }
  125. this.pointer.dispose();
  126. this.onDisposeObservable.notifyObservers({});
  127. }
  128. }