flyCameraMouseInput.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { __decorate } from "../../tslib.es6.js";
  2. import { serialize } from "../../Misc/decorators.js";
  3. import { CameraInputTypes } from "../../Cameras/cameraInputsManager.js";
  4. import { PointerEventTypes } from "../../Events/pointerEvents.js";
  5. import { Quaternion } from "../../Maths/math.vector.js";
  6. import { Axis } from "../../Maths/math.axis.js";
  7. import { Tools } from "../../Misc/tools.js";
  8. /**
  9. * Listen to mouse events to control the camera.
  10. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  11. */
  12. export class FlyCameraMouseInput {
  13. /**
  14. * Listen to mouse events to control the camera.
  15. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  16. */
  17. constructor() {
  18. /**
  19. * Defines the buttons associated with the input to handle camera rotation.
  20. */
  21. this.buttons = [0, 1, 2];
  22. /**
  23. * Assign buttons for Yaw control.
  24. */
  25. this.buttonsYaw = [-1, 0, 1];
  26. /**
  27. * Assign buttons for Pitch control.
  28. */
  29. this.buttonsPitch = [-1, 0, 1];
  30. /**
  31. * Assign buttons for Roll control.
  32. */
  33. this.buttonsRoll = [2];
  34. /**
  35. * Detect if any button is being pressed while mouse is moved.
  36. * -1 = Mouse locked.
  37. * 0 = Left button.
  38. * 1 = Middle Button.
  39. * 2 = Right Button.
  40. */
  41. this.activeButton = -1;
  42. /**
  43. * Defines the pointer's angular sensibility, to control the camera rotation speed.
  44. * Higher values reduce its sensitivity.
  45. */
  46. this.angularSensibility = 1000.0;
  47. this._previousPosition = null;
  48. }
  49. /**
  50. * Attach the mouse control to the HTML DOM element.
  51. * @param noPreventDefault Defines whether events caught by the controls should call preventdefault().
  52. */
  53. attachControl(noPreventDefault) {
  54. // eslint-disable-next-line prefer-rest-params
  55. noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
  56. this._noPreventDefault = noPreventDefault;
  57. this._observer = this.camera.getScene()._inputManager._addCameraPointerObserver((p) => {
  58. this._pointerInput(p);
  59. }, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
  60. // Correct Roll by rate, if enabled.
  61. this._rollObserver = this.camera.getScene().onBeforeRenderObservable.add(() => {
  62. if (this.camera.rollCorrect) {
  63. this.camera.restoreRoll(this.camera.rollCorrect);
  64. }
  65. });
  66. }
  67. /**
  68. * Detach the current controls from the specified dom element.
  69. */
  70. detachControl() {
  71. if (this._observer) {
  72. this.camera.getScene()._inputManager._removeCameraPointerObserver(this._observer);
  73. this.camera.getScene().onBeforeRenderObservable.remove(this._rollObserver);
  74. this._observer = null;
  75. this._rollObserver = null;
  76. this._previousPosition = null;
  77. this._noPreventDefault = undefined;
  78. }
  79. }
  80. /**
  81. * Gets the class name of the current input.
  82. * @returns the class name.
  83. */
  84. getClassName() {
  85. return "FlyCameraMouseInput";
  86. }
  87. /**
  88. * Get the friendly name associated with the input class.
  89. * @returns the input's friendly name.
  90. */
  91. getSimpleName() {
  92. return "mouse";
  93. }
  94. // Track mouse movement, when the pointer is not locked.
  95. _pointerInput(p) {
  96. const e = p.event;
  97. const camera = this.camera;
  98. const engine = camera.getEngine();
  99. if (!this.touchEnabled && e.pointerType === "touch") {
  100. return;
  101. }
  102. // Mouse is moved but an unknown mouse button is pressed.
  103. if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(e.button) === -1) {
  104. return;
  105. }
  106. const srcElement = e.target;
  107. // Mouse down.
  108. if (p.type === PointerEventTypes.POINTERDOWN) {
  109. try {
  110. srcElement?.setPointerCapture(e.pointerId);
  111. }
  112. catch (e) {
  113. // Nothing to do with the error. Execution continues.
  114. }
  115. this._previousPosition = {
  116. x: e.clientX,
  117. y: e.clientY,
  118. };
  119. this.activeButton = e.button;
  120. if (!this._noPreventDefault) {
  121. e.preventDefault();
  122. this._element.focus();
  123. }
  124. // This is required to move while pointer button is down
  125. if (engine.isPointerLock) {
  126. this._onMouseMove(p.event);
  127. }
  128. }
  129. // Mouse up.
  130. else if (p.type === PointerEventTypes.POINTERUP) {
  131. try {
  132. srcElement?.releasePointerCapture(e.pointerId);
  133. }
  134. catch (e) {
  135. // Nothing to do with the error. Execution continues.
  136. }
  137. this.activeButton = -1;
  138. this._previousPosition = null;
  139. if (!this._noPreventDefault) {
  140. e.preventDefault();
  141. }
  142. }
  143. // Mouse move.
  144. else if (p.type === PointerEventTypes.POINTERMOVE) {
  145. if (!this._previousPosition) {
  146. if (engine.isPointerLock) {
  147. this._onMouseMove(p.event);
  148. }
  149. return;
  150. }
  151. const offsetX = e.clientX - this._previousPosition.x;
  152. const offsetY = e.clientY - this._previousPosition.y;
  153. this._rotateCamera(offsetX, offsetY);
  154. this._previousPosition = {
  155. x: e.clientX,
  156. y: e.clientY,
  157. };
  158. if (!this._noPreventDefault) {
  159. e.preventDefault();
  160. }
  161. }
  162. }
  163. // Track mouse movement, when pointer is locked.
  164. _onMouseMove(e) {
  165. const camera = this.camera;
  166. const engine = camera.getEngine();
  167. if (!engine.isPointerLock) {
  168. return;
  169. }
  170. const offsetX = e.movementX;
  171. const offsetY = e.movementY;
  172. this._rotateCamera(offsetX, offsetY);
  173. this._previousPosition = null;
  174. if (!this._noPreventDefault) {
  175. e.preventDefault();
  176. }
  177. }
  178. /**
  179. * Rotate camera by mouse offset.
  180. * @param offsetX
  181. * @param offsetY
  182. */
  183. _rotateCamera(offsetX, offsetY) {
  184. const camera = this.camera;
  185. const handednessMultiplier = camera._calculateHandednessMultiplier();
  186. offsetX *= handednessMultiplier;
  187. const x = offsetX / this.angularSensibility;
  188. const y = offsetY / this.angularSensibility;
  189. // Initialize to current rotation.
  190. const currentRotation = Quaternion.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, camera.rotation.z);
  191. let rotationChange;
  192. // Pitch.
  193. if (this.buttonsPitch.some((v) => {
  194. return v === this.activeButton;
  195. })) {
  196. // Apply change in Radians to vector Angle.
  197. rotationChange = Quaternion.RotationAxis(Axis.X, y);
  198. // Apply Pitch to quaternion.
  199. currentRotation.multiplyInPlace(rotationChange);
  200. }
  201. // Yaw.
  202. if (this.buttonsYaw.some((v) => {
  203. return v === this.activeButton;
  204. })) {
  205. // Apply change in Radians to vector Angle.
  206. rotationChange = Quaternion.RotationAxis(Axis.Y, x);
  207. // Apply Yaw to quaternion.
  208. currentRotation.multiplyInPlace(rotationChange);
  209. // Add Roll, if banked turning is enabled, within Roll limit.
  210. const limit = camera.bankedTurnLimit + camera._trackRoll; // Defaults to 90° plus manual roll.
  211. if (camera.bankedTurn && -limit < camera.rotation.z && camera.rotation.z < limit) {
  212. const bankingDelta = camera.bankedTurnMultiplier * -x;
  213. // Apply change in Radians to vector Angle.
  214. rotationChange = Quaternion.RotationAxis(Axis.Z, bankingDelta);
  215. // Apply Yaw to quaternion.
  216. currentRotation.multiplyInPlace(rotationChange);
  217. }
  218. }
  219. // Roll.
  220. if (this.buttonsRoll.some((v) => {
  221. return v === this.activeButton;
  222. })) {
  223. // Apply change in Radians to vector Angle.
  224. rotationChange = Quaternion.RotationAxis(Axis.Z, -x);
  225. // Track Rolling.
  226. camera._trackRoll -= x;
  227. // Apply Pitch to quaternion.
  228. currentRotation.multiplyInPlace(rotationChange);
  229. }
  230. // Apply rotationQuaternion to Euler camera.rotation.
  231. currentRotation.toEulerAnglesToRef(camera.rotation);
  232. }
  233. }
  234. __decorate([
  235. serialize()
  236. ], FlyCameraMouseInput.prototype, "buttons", void 0);
  237. __decorate([
  238. serialize()
  239. ], FlyCameraMouseInput.prototype, "angularSensibility", void 0);
  240. CameraInputTypes["FlyCameraMouseInput"] = FlyCameraMouseInput;
  241. //# sourceMappingURL=flyCameraMouseInput.js.map