freeCameraMouseInput.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { __decorate } from "../../tslib.es6.js";
  2. import { Observable } from "../../Misc/observable.js";
  3. import { serialize } from "../../Misc/decorators.js";
  4. import { CameraInputTypes } from "../../Cameras/cameraInputsManager.js";
  5. import { PointerEventTypes } from "../../Events/pointerEvents.js";
  6. import { Tools } from "../../Misc/tools.js";
  7. /**
  8. * Manage the mouse inputs to control the movement of a free camera.
  9. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  10. */
  11. export class FreeCameraMouseInput {
  12. /**
  13. * Manage the mouse inputs to control the movement of a free camera.
  14. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  15. * @param touchEnabled Defines if touch is enabled or not
  16. */
  17. constructor(
  18. /**
  19. * Define if touch is enabled in the mouse input
  20. */
  21. touchEnabled = true) {
  22. this.touchEnabled = touchEnabled;
  23. /**
  24. * Defines the buttons associated with the input to handle camera move.
  25. */
  26. this.buttons = [0, 1, 2];
  27. /**
  28. * Defines the pointer angular sensibility along the X and Y axis or how fast is the camera rotating.
  29. */
  30. this.angularSensibility = 2000.0;
  31. this._previousPosition = null;
  32. /**
  33. * Observable for when a pointer move event occurs containing the move offset
  34. */
  35. this.onPointerMovedObservable = new Observable();
  36. /**
  37. * @internal
  38. * If the camera should be rotated automatically based on pointer movement
  39. */
  40. this._allowCameraRotation = true;
  41. this._currentActiveButton = -1;
  42. this._activePointerId = -1;
  43. }
  44. /**
  45. * Attach the input controls to a specific dom element to get the input from.
  46. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  47. */
  48. attachControl(noPreventDefault) {
  49. // eslint-disable-next-line prefer-rest-params
  50. noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
  51. const engine = this.camera.getEngine();
  52. const element = engine.getInputElement();
  53. if (!this._pointerInput) {
  54. this._pointerInput = (p) => {
  55. const evt = p.event;
  56. const isTouch = evt.pointerType === "touch";
  57. if (!this.touchEnabled && isTouch) {
  58. return;
  59. }
  60. if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
  61. return;
  62. }
  63. const srcElement = evt.target;
  64. if (p.type === PointerEventTypes.POINTERDOWN) {
  65. // If the input is touch with more than one touch OR if the input is mouse and there is already an active button, return
  66. if ((isTouch && this._activePointerId !== -1) || (!isTouch && this._currentActiveButton !== -1)) {
  67. return;
  68. }
  69. this._activePointerId = evt.pointerId;
  70. try {
  71. srcElement?.setPointerCapture(evt.pointerId);
  72. }
  73. catch (e) {
  74. //Nothing to do with the error. Execution will continue.
  75. }
  76. if (this._currentActiveButton === -1) {
  77. this._currentActiveButton = evt.button;
  78. }
  79. this._previousPosition = {
  80. x: evt.clientX,
  81. y: evt.clientY,
  82. };
  83. if (!noPreventDefault) {
  84. evt.preventDefault();
  85. element && element.focus();
  86. }
  87. // This is required to move while pointer button is down
  88. if (engine.isPointerLock && this._onMouseMove) {
  89. this._onMouseMove(p.event);
  90. }
  91. }
  92. else if (p.type === PointerEventTypes.POINTERUP) {
  93. // If input is touch with a different touch id OR if input is mouse with a different button, return
  94. if ((isTouch && this._activePointerId !== evt.pointerId) || (!isTouch && this._currentActiveButton !== evt.button)) {
  95. return;
  96. }
  97. try {
  98. srcElement?.releasePointerCapture(evt.pointerId);
  99. }
  100. catch (e) {
  101. //Nothing to do with the error.
  102. }
  103. this._currentActiveButton = -1;
  104. this._previousPosition = null;
  105. if (!noPreventDefault) {
  106. evt.preventDefault();
  107. }
  108. this._activePointerId = -1;
  109. }
  110. else if (p.type === PointerEventTypes.POINTERMOVE && (this._activePointerId === evt.pointerId || !isTouch)) {
  111. if (engine.isPointerLock && this._onMouseMove) {
  112. this._onMouseMove(p.event);
  113. }
  114. else if (this._previousPosition) {
  115. const handednessMultiplier = this.camera._calculateHandednessMultiplier();
  116. const offsetX = (evt.clientX - this._previousPosition.x) * handednessMultiplier;
  117. const offsetY = evt.clientY - this._previousPosition.y;
  118. if (this._allowCameraRotation) {
  119. this.camera.cameraRotation.y += offsetX / this.angularSensibility;
  120. this.camera.cameraRotation.x += offsetY / this.angularSensibility;
  121. }
  122. this.onPointerMovedObservable.notifyObservers({ offsetX: offsetX, offsetY: offsetY });
  123. this._previousPosition = {
  124. x: evt.clientX,
  125. y: evt.clientY,
  126. };
  127. if (!noPreventDefault) {
  128. evt.preventDefault();
  129. }
  130. }
  131. }
  132. };
  133. }
  134. this._onMouseMove = (evt) => {
  135. if (!engine.isPointerLock) {
  136. return;
  137. }
  138. const handednessMultiplier = this.camera._calculateHandednessMultiplier();
  139. const offsetX = evt.movementX * handednessMultiplier;
  140. this.camera.cameraRotation.y += offsetX / this.angularSensibility;
  141. const offsetY = evt.movementY;
  142. this.camera.cameraRotation.x += offsetY / this.angularSensibility;
  143. this._previousPosition = null;
  144. if (!noPreventDefault) {
  145. evt.preventDefault();
  146. }
  147. };
  148. this._observer = this.camera
  149. .getScene()
  150. ._inputManager._addCameraPointerObserver(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
  151. if (element) {
  152. this._contextMenuBind = (evt) => this.onContextMenu(evt);
  153. element.addEventListener("contextmenu", this._contextMenuBind, false); // TODO: We need to figure out how to handle this for Native
  154. }
  155. }
  156. /**
  157. * Called on JS contextmenu event.
  158. * Override this method to provide functionality.
  159. * @param evt the context menu event
  160. */
  161. onContextMenu(evt) {
  162. evt.preventDefault();
  163. }
  164. /**
  165. * Detach the current controls from the specified dom element.
  166. */
  167. detachControl() {
  168. if (this._observer) {
  169. this.camera.getScene()._inputManager._removeCameraPointerObserver(this._observer);
  170. if (this._contextMenuBind) {
  171. const engine = this.camera.getEngine();
  172. const element = engine.getInputElement();
  173. element && element.removeEventListener("contextmenu", this._contextMenuBind);
  174. }
  175. if (this.onPointerMovedObservable) {
  176. this.onPointerMovedObservable.clear();
  177. }
  178. this._observer = null;
  179. this._onMouseMove = null;
  180. this._previousPosition = null;
  181. }
  182. this._activePointerId = -1;
  183. this._currentActiveButton = -1;
  184. }
  185. /**
  186. * Gets the class name of the current input.
  187. * @returns the class name
  188. */
  189. getClassName() {
  190. return "FreeCameraMouseInput";
  191. }
  192. /**
  193. * Get the friendly name associated with the input class.
  194. * @returns the input friendly name
  195. */
  196. getSimpleName() {
  197. return "mouse";
  198. }
  199. }
  200. __decorate([
  201. serialize()
  202. ], FreeCameraMouseInput.prototype, "buttons", void 0);
  203. __decorate([
  204. serialize()
  205. ], FreeCameraMouseInput.prototype, "angularSensibility", void 0);
  206. CameraInputTypes["FreeCameraMouseInput"] = FreeCameraMouseInput;
  207. //# sourceMappingURL=freeCameraMouseInput.js.map