freeCamera.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. import { __decorate } from "../tslib.es6.js";
  2. import { serializeAsVector3, serialize } from "../Misc/decorators.js";
  3. import { Vector3, Vector2 } from "../Maths/math.vector.js";
  4. import { Engine } from "../Engines/engine.js";
  5. import { TargetCamera } from "./targetCamera.js";
  6. import { FreeCameraInputsManager } from "./freeCameraInputsManager.js";
  7. import { Tools } from "../Misc/tools.js";
  8. /**
  9. * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  10. * Please consider using the new UniversalCamera instead as it adds more functionality like the gamepad.
  11. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction#universal-camera
  12. */
  13. export class FreeCamera extends TargetCamera {
  14. /**
  15. * Gets the input sensibility for a mouse input. (default is 2000.0)
  16. * Higher values reduce sensitivity.
  17. */
  18. get angularSensibility() {
  19. const mouse = this.inputs.attached["mouse"];
  20. if (mouse) {
  21. return mouse.angularSensibility;
  22. }
  23. return 0;
  24. }
  25. /**
  26. * Sets the input sensibility for a mouse input. (default is 2000.0)
  27. * Higher values reduce sensitivity.
  28. */
  29. set angularSensibility(value) {
  30. const mouse = this.inputs.attached["mouse"];
  31. if (mouse) {
  32. mouse.angularSensibility = value;
  33. }
  34. }
  35. /**
  36. * Gets or Set the list of keyboard keys used to control the forward move of the camera.
  37. */
  38. get keysUp() {
  39. const keyboard = this.inputs.attached["keyboard"];
  40. if (keyboard) {
  41. return keyboard.keysUp;
  42. }
  43. return [];
  44. }
  45. set keysUp(value) {
  46. const keyboard = this.inputs.attached["keyboard"];
  47. if (keyboard) {
  48. keyboard.keysUp = value;
  49. }
  50. }
  51. /**
  52. * Gets or Set the list of keyboard keys used to control the upward move of the camera.
  53. */
  54. get keysUpward() {
  55. const keyboard = this.inputs.attached["keyboard"];
  56. if (keyboard) {
  57. return keyboard.keysUpward;
  58. }
  59. return [];
  60. }
  61. set keysUpward(value) {
  62. const keyboard = this.inputs.attached["keyboard"];
  63. if (keyboard) {
  64. keyboard.keysUpward = value;
  65. }
  66. }
  67. /**
  68. * Gets or Set the list of keyboard keys used to control the backward move of the camera.
  69. */
  70. get keysDown() {
  71. const keyboard = this.inputs.attached["keyboard"];
  72. if (keyboard) {
  73. return keyboard.keysDown;
  74. }
  75. return [];
  76. }
  77. set keysDown(value) {
  78. const keyboard = this.inputs.attached["keyboard"];
  79. if (keyboard) {
  80. keyboard.keysDown = value;
  81. }
  82. }
  83. /**
  84. * Gets or Set the list of keyboard keys used to control the downward move of the camera.
  85. */
  86. get keysDownward() {
  87. const keyboard = this.inputs.attached["keyboard"];
  88. if (keyboard) {
  89. return keyboard.keysDownward;
  90. }
  91. return [];
  92. }
  93. set keysDownward(value) {
  94. const keyboard = this.inputs.attached["keyboard"];
  95. if (keyboard) {
  96. keyboard.keysDownward = value;
  97. }
  98. }
  99. /**
  100. * Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
  101. */
  102. get keysLeft() {
  103. const keyboard = this.inputs.attached["keyboard"];
  104. if (keyboard) {
  105. return keyboard.keysLeft;
  106. }
  107. return [];
  108. }
  109. set keysLeft(value) {
  110. const keyboard = this.inputs.attached["keyboard"];
  111. if (keyboard) {
  112. keyboard.keysLeft = value;
  113. }
  114. }
  115. /**
  116. * Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
  117. */
  118. get keysRight() {
  119. const keyboard = this.inputs.attached["keyboard"];
  120. if (keyboard) {
  121. return keyboard.keysRight;
  122. }
  123. return [];
  124. }
  125. set keysRight(value) {
  126. const keyboard = this.inputs.attached["keyboard"];
  127. if (keyboard) {
  128. keyboard.keysRight = value;
  129. }
  130. }
  131. /**
  132. * Gets or Set the list of keyboard keys used to control the left rotation move of the camera.
  133. */
  134. get keysRotateLeft() {
  135. const keyboard = this.inputs.attached["keyboard"];
  136. if (keyboard) {
  137. return keyboard.keysRotateLeft;
  138. }
  139. return [];
  140. }
  141. set keysRotateLeft(value) {
  142. const keyboard = this.inputs.attached["keyboard"];
  143. if (keyboard) {
  144. keyboard.keysRotateLeft = value;
  145. }
  146. }
  147. /**
  148. * Gets or Set the list of keyboard keys used to control the right rotation move of the camera.
  149. */
  150. get keysRotateRight() {
  151. const keyboard = this.inputs.attached["keyboard"];
  152. if (keyboard) {
  153. return keyboard.keysRotateRight;
  154. }
  155. return [];
  156. }
  157. set keysRotateRight(value) {
  158. const keyboard = this.inputs.attached["keyboard"];
  159. if (keyboard) {
  160. keyboard.keysRotateRight = value;
  161. }
  162. }
  163. /**
  164. * Gets or Set the list of keyboard keys used to control the up rotation move of the camera.
  165. */
  166. get keysRotateUp() {
  167. const keyboard = this.inputs.attached["keyboard"];
  168. if (keyboard) {
  169. return keyboard.keysRotateUp;
  170. }
  171. return [];
  172. }
  173. set keysRotateUp(value) {
  174. const keyboard = this.inputs.attached["keyboard"];
  175. if (keyboard) {
  176. keyboard.keysRotateUp = value;
  177. }
  178. }
  179. /**
  180. * Gets or Set the list of keyboard keys used to control the down rotation move of the camera.
  181. */
  182. get keysRotateDown() {
  183. const keyboard = this.inputs.attached["keyboard"];
  184. if (keyboard) {
  185. return keyboard.keysRotateDown;
  186. }
  187. return [];
  188. }
  189. set keysRotateDown(value) {
  190. const keyboard = this.inputs.attached["keyboard"];
  191. if (keyboard) {
  192. keyboard.keysRotateDown = value;
  193. }
  194. }
  195. /**
  196. * Instantiates a Free Camera.
  197. * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  198. * Please consider using the new UniversalCamera instead as it adds more functionality like touch to this camera.
  199. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction#universal-camera
  200. * @param name Define the name of the camera in the scene
  201. * @param position Define the start position of the camera in the scene
  202. * @param scene Define the scene the camera belongs to
  203. * @param setActiveOnSceneIfNoneActive Defines whether the camera should be marked as active if not other active cameras have been defined
  204. */
  205. constructor(name, position, scene, setActiveOnSceneIfNoneActive = true) {
  206. super(name, position, scene, setActiveOnSceneIfNoneActive);
  207. /**
  208. * Define the collision ellipsoid of the camera.
  209. * This is helpful to simulate a camera body like the player body around the camera
  210. * @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_collisions#arcrotatecamera
  211. */
  212. this.ellipsoid = new Vector3(0.5, 1, 0.5);
  213. /**
  214. * Define an offset for the position of the ellipsoid around the camera.
  215. * This can be helpful to determine the center of the body near the gravity center of the body
  216. * instead of its head.
  217. */
  218. this.ellipsoidOffset = new Vector3(0, 0, 0);
  219. /**
  220. * Enable or disable collisions of the camera with the rest of the scene objects.
  221. */
  222. this.checkCollisions = false;
  223. /**
  224. * Enable or disable gravity on the camera.
  225. */
  226. this.applyGravity = false;
  227. this._needMoveForGravity = false;
  228. this._oldPosition = Vector3.Zero();
  229. this._diffPosition = Vector3.Zero();
  230. this._newPosition = Vector3.Zero();
  231. // Collisions
  232. this._collisionMask = -1;
  233. this._onCollisionPositionChange = (collisionId, newPosition, collidedMesh = null) => {
  234. this._newPosition.copyFrom(newPosition);
  235. this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
  236. if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
  237. this.position.addToRef(this._diffPosition, this._deferredPositionUpdate);
  238. if (!this._deferOnly) {
  239. this.position.copyFrom(this._deferredPositionUpdate);
  240. }
  241. else {
  242. this._deferredUpdated = true;
  243. }
  244. // call onCollide, if defined. Note that in case of deferred update, the actual position change might happen in the next frame.
  245. if (this.onCollide && collidedMesh) {
  246. this.onCollide(collidedMesh);
  247. }
  248. }
  249. };
  250. this.inputs = new FreeCameraInputsManager(this);
  251. this.inputs.addKeyboard().addMouse();
  252. }
  253. /**
  254. * Attached controls to the current camera.
  255. * @param ignored defines an ignored parameter kept for backward compatibility.
  256. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  257. */
  258. attachControl(ignored, noPreventDefault) {
  259. // eslint-disable-next-line prefer-rest-params
  260. noPreventDefault = Tools.BackCompatCameraNoPreventDefault(arguments);
  261. this.inputs.attachElement(noPreventDefault);
  262. }
  263. /**
  264. * Detach the current controls from the specified dom element.
  265. */
  266. detachControl() {
  267. this.inputs.detachElement();
  268. this.cameraDirection = new Vector3(0, 0, 0);
  269. this.cameraRotation = new Vector2(0, 0);
  270. }
  271. /**
  272. * Define a collision mask to limit the list of object the camera can collide with
  273. */
  274. get collisionMask() {
  275. return this._collisionMask;
  276. }
  277. set collisionMask(mask) {
  278. this._collisionMask = !isNaN(mask) ? mask : -1;
  279. }
  280. /**
  281. * @internal
  282. */
  283. _collideWithWorld(displacement) {
  284. let globalPosition;
  285. if (this.parent) {
  286. globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
  287. }
  288. else {
  289. globalPosition = this.position;
  290. }
  291. globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
  292. this._oldPosition.addInPlace(this.ellipsoidOffset);
  293. const coordinator = this.getScene().collisionCoordinator;
  294. if (!this._collider) {
  295. this._collider = coordinator.createCollider();
  296. }
  297. this._collider._radius = this.ellipsoid;
  298. this._collider.collisionMask = this._collisionMask;
  299. //no need for clone, as long as gravity is not on.
  300. let actualDisplacement = displacement;
  301. //add gravity to the direction to prevent the dual-collision checking
  302. if (this.applyGravity) {
  303. //this prevents mending with cameraDirection, a global variable of the free camera class.
  304. actualDisplacement = displacement.add(this.getScene().gravity);
  305. }
  306. coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
  307. }
  308. /** @internal */
  309. _checkInputs() {
  310. if (!this._localDirection) {
  311. this._localDirection = Vector3.Zero();
  312. this._transformedDirection = Vector3.Zero();
  313. }
  314. this.inputs.checkInputs();
  315. super._checkInputs();
  316. }
  317. /**
  318. * Enable movement without a user input. This allows gravity to always be applied.
  319. */
  320. set needMoveForGravity(value) {
  321. this._needMoveForGravity = value;
  322. }
  323. /**
  324. * When true, gravity is applied whether there is user input or not.
  325. */
  326. get needMoveForGravity() {
  327. return this._needMoveForGravity;
  328. }
  329. /** @internal */
  330. _decideIfNeedsToMove() {
  331. return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  332. }
  333. /** @internal */
  334. _updatePosition() {
  335. if (this.checkCollisions && this.getScene().collisionsEnabled) {
  336. this._collideWithWorld(this.cameraDirection);
  337. }
  338. else {
  339. super._updatePosition();
  340. }
  341. }
  342. /**
  343. * Destroy the camera and release the current resources hold by it.
  344. */
  345. dispose() {
  346. this.inputs.clear();
  347. super.dispose();
  348. }
  349. /**
  350. * Gets the current object class name.
  351. * @returns the class name
  352. */
  353. getClassName() {
  354. return "FreeCamera";
  355. }
  356. }
  357. __decorate([
  358. serializeAsVector3()
  359. ], FreeCamera.prototype, "ellipsoid", void 0);
  360. __decorate([
  361. serializeAsVector3()
  362. ], FreeCamera.prototype, "ellipsoidOffset", void 0);
  363. __decorate([
  364. serialize()
  365. ], FreeCamera.prototype, "checkCollisions", void 0);
  366. __decorate([
  367. serialize()
  368. ], FreeCamera.prototype, "applyGravity", void 0);
  369. //# sourceMappingURL=freeCamera.js.map