freeCamera.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. import { Nullable } from "../types";
  2. import { serializeAsVector3, serialize } from "../Misc/decorators";
  3. import { Vector3, Vector2 } from "../Maths/math.vector";
  4. import { AbstractMesh } from "../Meshes/abstractMesh";
  5. import { Scene } from "../scene";
  6. import { Engine } from "../Engines/engine";
  7. import { TargetCamera } from "./targetCamera";
  8. import { FreeCameraInputsManager } from "./freeCameraInputsManager";
  9. import { FreeCameraMouseInput } from "../Cameras/Inputs/freeCameraMouseInput";
  10. import { FreeCameraKeyboardMoveInput } from "../Cameras/Inputs/freeCameraKeyboardMoveInput";
  11. declare type Collider = import("../Collisions/collider").Collider;
  12. /**
  13. * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  14. * Please consider using the new UniversalCamera instead as it adds more functionality like the gamepad.
  15. * @see http://doc.babylonjs.com/features/cameras#universal-camera
  16. */
  17. export class FreeCamera extends TargetCamera {
  18. /**
  19. * Define the collision ellipsoid of the camera.
  20. * This is helpful to simulate a camera body like the player body around the camera
  21. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity#arcrotatecamera
  22. */
  23. @serializeAsVector3()
  24. public ellipsoid = new Vector3(0.5, 1, 0.5);
  25. /**
  26. * Define an offset for the position of the ellipsoid around the camera.
  27. * This can be helpful to determine the center of the body near the gravity center of the body
  28. * instead of its head.
  29. */
  30. @serializeAsVector3()
  31. public ellipsoidOffset = new Vector3(0, 0, 0);
  32. /**
  33. * Enable or disable collisions of the camera with the rest of the scene objects.
  34. */
  35. @serialize()
  36. public checkCollisions = false;
  37. /**
  38. * Enable or disable gravity on the camera.
  39. */
  40. @serialize()
  41. public applyGravity = false;
  42. /**
  43. * Define the input manager associated to the camera.
  44. */
  45. public inputs: FreeCameraInputsManager;
  46. /**
  47. * Gets the input sensibility for a mouse input. (default is 2000.0)
  48. * Higher values reduce sensitivity.
  49. */
  50. public get angularSensibility(): number {
  51. var mouse = <FreeCameraMouseInput>this.inputs.attached["mouse"];
  52. if (mouse) {
  53. return mouse.angularSensibility;
  54. }
  55. return 0;
  56. }
  57. /**
  58. * Sets the input sensibility for a mouse input. (default is 2000.0)
  59. * Higher values reduce sensitivity.
  60. */
  61. public set angularSensibility(value: number) {
  62. var mouse = <FreeCameraMouseInput>this.inputs.attached["mouse"];
  63. if (mouse) {
  64. mouse.angularSensibility = value;
  65. }
  66. }
  67. /**
  68. * Gets or Set the list of keyboard keys used to control the forward move of the camera.
  69. */
  70. public get keysUp(): number[] {
  71. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  72. if (keyboard) {
  73. return keyboard.keysUp;
  74. }
  75. return [];
  76. }
  77. public set keysUp(value: number[]) {
  78. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  79. if (keyboard) {
  80. keyboard.keysUp = value;
  81. }
  82. }
  83. /**
  84. * Gets or Set the list of keyboard keys used to control the backward move of the camera.
  85. */
  86. public get keysDown(): number[] {
  87. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  88. if (keyboard) {
  89. return keyboard.keysDown;
  90. }
  91. return [];
  92. }
  93. public set keysDown(value: number[]) {
  94. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  95. if (keyboard) {
  96. keyboard.keysDown = 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. public get keysLeft(): number[] {
  103. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  104. if (keyboard) {
  105. return keyboard.keysLeft;
  106. }
  107. return [];
  108. }
  109. public set keysLeft(value: number[]) {
  110. var keyboard = <FreeCameraKeyboardMoveInput>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. public get keysRight(): number[] {
  119. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  120. if (keyboard) {
  121. return keyboard.keysRight;
  122. }
  123. return [];
  124. }
  125. public set keysRight(value: number[]) {
  126. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  127. if (keyboard) {
  128. keyboard.keysRight = value;
  129. }
  130. }
  131. /**
  132. * Event raised when the camera collide with a mesh in the scene.
  133. */
  134. public onCollide: (collidedMesh: AbstractMesh) => void;
  135. private _collider: Collider;
  136. private _needMoveForGravity = false;
  137. private _oldPosition = Vector3.Zero();
  138. private _diffPosition = Vector3.Zero();
  139. private _newPosition = Vector3.Zero();
  140. /** @hidden */
  141. public _localDirection: Vector3;
  142. /** @hidden */
  143. public _transformedDirection: Vector3;
  144. /**
  145. * Instantiates a Free Camera.
  146. * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  147. * Please consider using the new UniversalCamera instead as it adds more functionality like touch to this camera.
  148. * @see http://doc.babylonjs.com/features/cameras#universal-camera
  149. * @param name Define the name of the camera in the scene
  150. * @param position Define the start position of the camera in the scene
  151. * @param scene Define the scene the camera belongs to
  152. * @param setActiveOnSceneIfNoneActive Defines wheter the camera should be marked as active if not other active cameras have been defined
  153. */
  154. constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
  155. super(name, position, scene, setActiveOnSceneIfNoneActive);
  156. this.inputs = new FreeCameraInputsManager(this);
  157. this.inputs.addKeyboard().addMouse();
  158. }
  159. /**
  160. * Attached controls to the current camera.
  161. * @param element Defines the element the controls should be listened from
  162. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  163. */
  164. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  165. this.inputs.attachElement(element, noPreventDefault);
  166. }
  167. /**
  168. * Detach the current controls from the camera.
  169. * The camera will stop reacting to inputs.
  170. * @param element Defines the element to stop listening the inputs from
  171. */
  172. public detachControl(element: HTMLElement): void {
  173. this.inputs.detachElement(element);
  174. this.cameraDirection = new Vector3(0, 0, 0);
  175. this.cameraRotation = new Vector2(0, 0);
  176. }
  177. // Collisions
  178. private _collisionMask = -1;
  179. /**
  180. * Define a collision mask to limit the list of object the camera can collide with
  181. */
  182. public get collisionMask(): number {
  183. return this._collisionMask;
  184. }
  185. public set collisionMask(mask: number) {
  186. this._collisionMask = !isNaN(mask) ? mask : -1;
  187. }
  188. /** @hidden */
  189. public _collideWithWorld(displacement: Vector3): void {
  190. var globalPosition: Vector3;
  191. if (this.parent) {
  192. globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
  193. } else {
  194. globalPosition = this.position;
  195. }
  196. globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
  197. this._oldPosition.addInPlace(this.ellipsoidOffset);
  198. let coordinator = this.getScene().collisionCoordinator;
  199. if (!this._collider) {
  200. this._collider = coordinator.createCollider();
  201. }
  202. this._collider._radius = this.ellipsoid;
  203. this._collider.collisionMask = this._collisionMask;
  204. //no need for clone, as long as gravity is not on.
  205. var actualDisplacement = displacement;
  206. //add gravity to the direction to prevent the dual-collision checking
  207. if (this.applyGravity) {
  208. //this prevents mending with cameraDirection, a global variable of the free camera class.
  209. actualDisplacement = displacement.add(this.getScene().gravity);
  210. }
  211. coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
  212. }
  213. private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
  214. var updatePosition = (newPos: Vector3) => {
  215. this._newPosition.copyFrom(newPos);
  216. this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
  217. if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
  218. this.position.addInPlace(this._diffPosition);
  219. if (this.onCollide && collidedMesh) {
  220. this.onCollide(collidedMesh);
  221. }
  222. }
  223. };
  224. updatePosition(newPosition);
  225. }
  226. /** @hidden */
  227. public _checkInputs(): void {
  228. if (!this._localDirection) {
  229. this._localDirection = Vector3.Zero();
  230. this._transformedDirection = Vector3.Zero();
  231. }
  232. this.inputs.checkInputs();
  233. super._checkInputs();
  234. }
  235. /** @hidden */
  236. public _decideIfNeedsToMove(): boolean {
  237. return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  238. }
  239. /** @hidden */
  240. public _updatePosition(): void {
  241. if (this.checkCollisions && this.getScene().collisionsEnabled) {
  242. this._collideWithWorld(this.cameraDirection);
  243. } else {
  244. super._updatePosition();
  245. }
  246. }
  247. /**
  248. * Destroy the camera and release the current resources hold by it.
  249. */
  250. public dispose(): void {
  251. this.inputs.clear();
  252. super.dispose();
  253. }
  254. /**
  255. * Gets the current object class name.
  256. * @return the class name
  257. */
  258. public getClassName(): string {
  259. return "FreeCamera";
  260. }
  261. }