cameraInputsManager.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { Logger } from "../Misc/logger.js";
  2. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  3. import { Camera } from "./camera.js";
  4. /**
  5. * @ignore
  6. * This is a list of all the different input types that are available in the application.
  7. * Fo instance: ArcRotateCameraGamepadInput...
  8. */
  9. // eslint-disable-next-line no-var, @typescript-eslint/naming-convention
  10. export var CameraInputTypes = {};
  11. /**
  12. * This represents the input manager used within a camera.
  13. * It helps dealing with all the different kind of input attached to a camera.
  14. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  15. */
  16. export class CameraInputsManager {
  17. /**
  18. * Instantiate a new Camera Input Manager.
  19. * @param camera Defines the camera the input manager belongs to
  20. */
  21. constructor(camera) {
  22. /**
  23. * Defines the dom element the camera is collecting inputs from.
  24. * This is null if the controls have not been attached.
  25. */
  26. this.attachedToElement = false;
  27. this.attached = {};
  28. this.camera = camera;
  29. this.checkInputs = () => { };
  30. }
  31. /**
  32. * Add an input method to a camera
  33. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/customizingCameraInputs
  34. * @param input Camera input method
  35. */
  36. add(input) {
  37. const type = input.getSimpleName();
  38. if (this.attached[type]) {
  39. Logger.Warn("camera input of type " + type + " already exists on camera");
  40. return;
  41. }
  42. this.attached[type] = input;
  43. input.camera = this.camera;
  44. // for checkInputs, we are dynamically creating a function
  45. // the goal is to avoid the performance penalty of looping for inputs in the render loop
  46. if (input.checkInputs) {
  47. this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
  48. }
  49. if (this.attachedToElement) {
  50. input.attachControl(this.noPreventDefault);
  51. }
  52. }
  53. /**
  54. * Remove a specific input method from a camera
  55. * example: camera.inputs.remove(camera.inputs.attached.mouse);
  56. * @param inputToRemove camera input method
  57. */
  58. remove(inputToRemove) {
  59. for (const cam in this.attached) {
  60. const input = this.attached[cam];
  61. if (input === inputToRemove) {
  62. input.detachControl();
  63. input.camera = null;
  64. delete this.attached[cam];
  65. this.rebuildInputCheck();
  66. return;
  67. }
  68. }
  69. }
  70. /**
  71. * Remove a specific input type from a camera
  72. * example: camera.inputs.remove("ArcRotateCameraGamepadInput");
  73. * @param inputType the type of the input to remove
  74. */
  75. removeByType(inputType) {
  76. for (const cam in this.attached) {
  77. const input = this.attached[cam];
  78. if (input.getClassName() === inputType) {
  79. input.detachControl();
  80. input.camera = null;
  81. delete this.attached[cam];
  82. this.rebuildInputCheck();
  83. }
  84. }
  85. }
  86. _addCheckInputs(fn) {
  87. const current = this.checkInputs;
  88. return () => {
  89. current();
  90. fn();
  91. };
  92. }
  93. /**
  94. * Attach the input controls to the currently attached dom element to listen the events from.
  95. * @param input Defines the input to attach
  96. */
  97. attachInput(input) {
  98. if (this.attachedToElement) {
  99. input.attachControl(this.noPreventDefault);
  100. }
  101. }
  102. /**
  103. * Attach the current manager inputs controls to a specific dom element to listen the events from.
  104. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  105. */
  106. attachElement(noPreventDefault = false) {
  107. if (this.attachedToElement) {
  108. return;
  109. }
  110. noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
  111. this.attachedToElement = true;
  112. this.noPreventDefault = noPreventDefault;
  113. for (const cam in this.attached) {
  114. this.attached[cam].attachControl(noPreventDefault);
  115. }
  116. }
  117. /**
  118. * Detach the current manager inputs controls from a specific dom element.
  119. * @param disconnect Defines whether the input should be removed from the current list of attached inputs
  120. */
  121. detachElement(disconnect = false) {
  122. for (const cam in this.attached) {
  123. this.attached[cam].detachControl();
  124. if (disconnect) {
  125. this.attached[cam].camera = null;
  126. }
  127. }
  128. this.attachedToElement = false;
  129. }
  130. /**
  131. * Rebuild the dynamic inputCheck function from the current list of
  132. * defined inputs in the manager.
  133. */
  134. rebuildInputCheck() {
  135. this.checkInputs = () => { };
  136. for (const cam in this.attached) {
  137. const input = this.attached[cam];
  138. if (input.checkInputs) {
  139. this.checkInputs = this._addCheckInputs(input.checkInputs.bind(input));
  140. }
  141. }
  142. }
  143. /**
  144. * Remove all attached input methods from a camera
  145. */
  146. clear() {
  147. if (this.attachedToElement) {
  148. this.detachElement(true);
  149. }
  150. this.attached = {};
  151. this.attachedToElement = false;
  152. this.checkInputs = () => { };
  153. }
  154. /**
  155. * Serialize the current input manager attached to a camera.
  156. * This ensures than once parsed,
  157. * the input associated to the camera will be identical to the current ones
  158. * @param serializedCamera Defines the camera serialization JSON the input serialization should write to
  159. */
  160. serialize(serializedCamera) {
  161. const inputs = {};
  162. for (const cam in this.attached) {
  163. const input = this.attached[cam];
  164. const res = SerializationHelper.Serialize(input);
  165. inputs[input.getClassName()] = res;
  166. }
  167. serializedCamera.inputsmgr = inputs;
  168. }
  169. /**
  170. * Parses an input manager serialized JSON to restore the previous list of inputs
  171. * and states associated to a camera.
  172. * @param parsedCamera Defines the JSON to parse
  173. */
  174. parse(parsedCamera) {
  175. const parsedInputs = parsedCamera.inputsmgr;
  176. if (parsedInputs) {
  177. this.clear();
  178. for (const n in parsedInputs) {
  179. const construct = CameraInputTypes[n];
  180. if (construct) {
  181. const parsedinput = parsedInputs[n];
  182. const input = SerializationHelper.Parse(() => {
  183. return new construct();
  184. }, parsedinput, null);
  185. this.add(input);
  186. }
  187. }
  188. }
  189. else {
  190. //2016-03-08 this part is for managing backward compatibility
  191. for (const n in this.attached) {
  192. const construct = CameraInputTypes[this.attached[n].getClassName()];
  193. if (construct) {
  194. const input = SerializationHelper.Parse(() => {
  195. return new construct();
  196. }, parsedCamera, null);
  197. this.remove(this.attached[n]);
  198. this.add(input);
  199. }
  200. }
  201. }
  202. }
  203. }
  204. //# sourceMappingURL=cameraInputsManager.js.map