webXRController.ts 5.7 KB

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