freeCamera.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  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 upward move of the camera.
  85. */
  86. public get keysUpward(): number[] {
  87. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  88. if (keyboard) {
  89. return keyboard.keysUpward;
  90. }
  91. return [];
  92. }
  93. public set keysUpward(value: number[]) {
  94. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  95. if (keyboard) {
  96. keyboard.keysUpward = value;
  97. }
  98. }
  99. /**
  100. * Gets or Set the list of keyboard keys used to control the backward move of the camera.
  101. */
  102. public get keysDown(): number[] {
  103. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  104. if (keyboard) {
  105. return keyboard.keysDown;
  106. }
  107. return [];
  108. }
  109. public set keysDown(value: number[]) {
  110. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  111. if (keyboard) {
  112. keyboard.keysDown = value;
  113. }
  114. }
  115. /**
  116. * Gets or Set the list of keyboard keys used to control the downward move of the camera.
  117. */
  118. public get keysDownward(): number[] {
  119. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  120. if (keyboard) {
  121. return keyboard.keysDownward;
  122. }
  123. return [];
  124. }
  125. public set keysDownward(value: number[]) {
  126. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  127. if (keyboard) {
  128. keyboard.keysDownward = value;
  129. }
  130. }
  131. /**
  132. * Gets or Set the list of keyboard keys used to control the left strafe move of the camera.
  133. */
  134. public get keysLeft(): number[] {
  135. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  136. if (keyboard) {
  137. return keyboard.keysLeft;
  138. }
  139. return [];
  140. }
  141. public set keysLeft(value: number[]) {
  142. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  143. if (keyboard) {
  144. keyboard.keysLeft = value;
  145. }
  146. }
  147. /**
  148. * Gets or Set the list of keyboard keys used to control the right strafe move of the camera.
  149. */
  150. public get keysRight(): number[] {
  151. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  152. if (keyboard) {
  153. return keyboard.keysRight;
  154. }
  155. return [];
  156. }
  157. public set keysRight(value: number[]) {
  158. var keyboard = <FreeCameraKeyboardMoveInput>this.inputs.attached["keyboard"];
  159. if (keyboard) {
  160. keyboard.keysRight = value;
  161. }
  162. }
  163. /**
  164. * Event raised when the camera collide with a mesh in the scene.
  165. */
  166. public onCollide: (collidedMesh: AbstractMesh) => void;
  167. private _collider: Collider;
  168. private _needMoveForGravity = false;
  169. private _oldPosition = Vector3.Zero();
  170. private _diffPosition = Vector3.Zero();
  171. private _newPosition = Vector3.Zero();
  172. /** @hidden */
  173. public _localDirection: Vector3;
  174. /** @hidden */
  175. public _transformedDirection: Vector3;
  176. /**
  177. * Instantiates a Free Camera.
  178. * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  179. * Please consider using the new UniversalCamera instead as it adds more functionality like touch to this camera.
  180. * @see http://doc.babylonjs.com/features/cameras#universal-camera
  181. * @param name Define the name of the camera in the scene
  182. * @param position Define the start position of the camera in the scene
  183. * @param scene Define the scene the camera belongs to
  184. * @param setActiveOnSceneIfNoneActive Defines wheter the camera should be marked as active if not other active cameras have been defined
  185. */
  186. constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
  187. super(name, position, scene, setActiveOnSceneIfNoneActive);
  188. this.inputs = new FreeCameraInputsManager(this);
  189. this.inputs.addKeyboard().addMouse();
  190. }
  191. /**
  192. * Attached controls to the current camera.
  193. * @param element Defines the element the controls should be listened from
  194. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  195. */
  196. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  197. this.inputs.attachElement(element, noPreventDefault);
  198. }
  199. /**
  200. * Detach the current controls from the camera.
  201. * The camera will stop reacting to inputs.
  202. * @param element Defines the element to stop listening the inputs from
  203. */
  204. public detachControl(element: HTMLElement): void {
  205. this.inputs.detachElement(element);
  206. this.cameraDirection = new Vector3(0, 0, 0);
  207. this.cameraRotation = new Vector2(0, 0);
  208. }
  209. // Collisions
  210. private _collisionMask = -1;
  211. /**
  212. * Define a collision mask to limit the list of object the camera can collide with
  213. */
  214. public get collisionMask(): number {
  215. return this._collisionMask;
  216. }
  217. public set collisionMask(mask: number) {
  218. this._collisionMask = !isNaN(mask) ? mask : -1;
  219. }
  220. /** @hidden */
  221. public _collideWithWorld(displacement: Vector3): void {
  222. var globalPosition: Vector3;
  223. if (this.parent) {
  224. globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
  225. } else {
  226. globalPosition = this.position;
  227. }
  228. globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
  229. this._oldPosition.addInPlace(this.ellipsoidOffset);
  230. let coordinator = this.getScene().collisionCoordinator;
  231. if (!this._collider) {
  232. this._collider = coordinator.createCollider();
  233. }
  234. this._collider._radius = this.ellipsoid;
  235. this._collider.collisionMask = this._collisionMask;
  236. //no need for clone, as long as gravity is not on.
  237. var actualDisplacement = displacement;
  238. //add gravity to the direction to prevent the dual-collision checking
  239. if (this.applyGravity) {
  240. //this prevents mending with cameraDirection, a global variable of the free camera class.
  241. actualDisplacement = displacement.add(this.getScene().gravity);
  242. }
  243. coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
  244. }
  245. private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
  246. var updatePosition = (newPos: Vector3) => {
  247. this._newPosition.copyFrom(newPos);
  248. this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
  249. if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
  250. this.position.addInPlace(this._diffPosition);
  251. if (this.onCollide && collidedMesh) {
  252. this.onCollide(collidedMesh);
  253. }
  254. }
  255. };
  256. updatePosition(newPosition);
  257. }
  258. /** @hidden */
  259. public _checkInputs(): void {
  260. if (!this._localDirection) {
  261. this._localDirection = Vector3.Zero();
  262. this._transformedDirection = Vector3.Zero();
  263. }
  264. this.inputs.checkInputs();
  265. super._checkInputs();
  266. }
  267. /** @hidden */
  268. public _decideIfNeedsToMove(): boolean {
  269. return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  270. }
  271. /** @hidden */
  272. public _updatePosition(): void {
  273. if (this.checkCollisions && this.getScene().collisionsEnabled) {
  274. this._collideWithWorld(this.cameraDirection);
  275. } else {
  276. super._updatePosition();
  277. }
  278. }
  279. /**
  280. * Destroy the camera and release the current resources hold by it.
  281. */
  282. public dispose(): void {
  283. this.inputs.clear();
  284. super.dispose();
  285. }
  286. /**
  287. * Gets the current object class name.
  288. * @return the class name
  289. */
  290. public getClassName(): string {
  291. return "FreeCamera";
  292. }
  293. }