webXRController.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. /**
  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. private _uniqueId: string;
  34. /**
  35. * Creates the controller
  36. * @see https://doc.babylonjs.com/how_to/webxr
  37. * @param scene the scene which the controller should be associated to
  38. * @param inputSource the underlying input source for the controller
  39. * @param controllerProfile An optional controller profile for this input. This will override the xrInput profile.
  40. */
  41. constructor(
  42. private scene: Scene,
  43. /** The underlying input source for the controller */
  44. public inputSource: XRInputSource,
  45. controllerProfile?: string) {
  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, controllerProfile);
  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. /**
  63. * Get this controllers unique id
  64. */
  65. public get uniqueId() {
  66. return this._uniqueId;
  67. }
  68. /**
  69. * Updates the controller pose based on the given XRFrame
  70. * @param xrFrame xr frame to update the pose with
  71. * @param referenceSpace reference space to use
  72. */
  73. public updateFromXRFrame(xrFrame: XRFrame, referenceSpace: XRReferenceSpace) {
  74. let pose = xrFrame.getPose(this.inputSource.targetRaySpace, referenceSpace);
  75. // Update the pointer mesh
  76. if (pose) {
  77. this.pointer.position.copyFrom(<any>(pose.transform.position));
  78. this.pointer.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
  79. if (!this.scene.useRightHandedSystem) {
  80. this.pointer.position.z *= -1;
  81. this.pointer.rotationQuaternion!.z *= -1;
  82. this.pointer.rotationQuaternion!.w *= -1;
  83. }
  84. }
  85. // Update the grip mesh if it exists
  86. if (this.inputSource.gripSpace && this.grip) {
  87. let pose = xrFrame.getPose(this.inputSource.gripSpace, referenceSpace);
  88. if (pose) {
  89. this.grip.position.copyFrom(<any>(pose.transform.position));
  90. this.grip.rotationQuaternion!.copyFrom(<any>(pose.transform.orientation));
  91. if (!this.scene.useRightHandedSystem) {
  92. this.grip.position.z *= -1;
  93. this.grip.rotationQuaternion!.z *= -1;
  94. this.grip.rotationQuaternion!.w *= -1;
  95. }
  96. }
  97. }
  98. if (this.gamepadController) {
  99. // either update buttons only or also position, if in gamepad mode
  100. this.gamepadController.updateFromXRFrame(xrFrame);
  101. }
  102. }
  103. /**
  104. * Gets a world space ray coming from the controller
  105. * @param result the resulting ray
  106. */
  107. public getWorldPointerRayToRef(result: Ray) {
  108. // Force update to ensure picked point is synced with ray
  109. let worldMatrix = this.pointer.computeWorldMatrix();
  110. worldMatrix.decompose(undefined, this._tmpQuaternion, undefined);
  111. this._tmpVector.set(0, 0, 1);
  112. this._tmpVector.rotateByQuaternionToRef(this._tmpQuaternion, this._tmpVector);
  113. result.origin.copyFrom(this.pointer.absolutePosition);
  114. result.direction.copyFrom(this._tmpVector);
  115. result.length = 1000;
  116. }
  117. /**
  118. * Get the scene associated with this controller
  119. * @returns the scene object
  120. */
  121. public getScene() {
  122. return this.scene;
  123. }
  124. /**
  125. * Disposes of the object
  126. */
  127. dispose() {
  128. if (this.grip) {
  129. this.grip.dispose();
  130. }
  131. if (this.gamepadController) {
  132. this.gamepadController.dispose();
  133. }
  134. this.pointer.dispose();
  135. this.onDisposeObservable.notifyObservers({});
  136. }
  137. }