followCamera.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { Nullable } from "../types";
  2. import { serialize, serializeAsMeshReference } from "../Misc/decorators";
  3. import { Tools } from "../Misc/tools";
  4. import { TargetCamera } from "./targetCamera";
  5. import { Scene } from "../scene";
  6. import { Matrix, Vector3 } from "../Maths/math";
  7. import { Node } from "../node";
  8. import { AbstractMesh } from "../Meshes/abstractMesh";
  9. import { FollowCameraInputsManager } from './followCameraInputsManager';
  10. Node.AddNodeConstructor("FollowCamera", (name, scene) => {
  11. return () => new FollowCamera(name, Vector3.Zero(), scene);
  12. });
  13. Node.AddNodeConstructor("ArcFollowCamera", (name, scene) => {
  14. return () => new ArcFollowCamera(name, 0, 0, 1.0, null, scene);
  15. });
  16. /**
  17. * A follow camera takes a mesh as a target and follows it as it moves. Both a free camera version followCamera and
  18. * an arc rotate version arcFollowCamera are available.
  19. * @see http://doc.babylonjs.com/features/cameras#follow-camera
  20. */
  21. export class FollowCamera extends TargetCamera {
  22. /**
  23. * Distance the follow camera should follow an object at
  24. */
  25. @serialize()
  26. public radius: number = 12;
  27. /**
  28. * Minimum allowed distance of the camera to the axis of rotation
  29. * (The camera can not get closer).
  30. * This can help limiting how the Camera is able to move in the scene.
  31. */
  32. @serialize()
  33. public lowerRadiusLimit: Nullable<number> = null;
  34. /**
  35. * Maximum allowed distance of the camera to the axis of rotation
  36. * (The camera can not get further).
  37. * This can help limiting how the Camera is able to move in the scene.
  38. */
  39. @serialize()
  40. public upperRadiusLimit: Nullable<number> = null;
  41. /**
  42. * Define a rotation offset between the camera and the object it follows
  43. */
  44. @serialize()
  45. public rotationOffset: number = 0;
  46. /**
  47. * Minimum allowed angle to camera position relative to target object.
  48. * This can help limiting how the Camera is able to move in the scene.
  49. */
  50. @serialize()
  51. public lowerRotationOffsetLimit: Nullable<number> = null;
  52. /**
  53. * Maximum allowed angle to camera position relative to target object.
  54. * This can help limiting how the Camera is able to move in the scene.
  55. */
  56. @serialize()
  57. public upperRotationOffsetLimit: Nullable<number> = null;
  58. /**
  59. * Define a height offset between the camera and the object it follows.
  60. * It can help following an object from the top (like a car chaing a plane)
  61. */
  62. @serialize()
  63. public heightOffset: number = 4;
  64. /**
  65. * Minimum allowed height of camera position relative to target object.
  66. * This can help limiting how the Camera is able to move in the scene.
  67. */
  68. @serialize()
  69. public lowerHeightOffsetLimit: Nullable<number> = null;
  70. /**
  71. * Maximum allowed height of camera position relative to target object.
  72. * This can help limiting how the Camera is able to move in the scene.
  73. */
  74. @serialize()
  75. public upperHeightOffsetLimit: Nullable<number> = null;
  76. /**
  77. * Define how fast the camera can accelerate to follow it s target.
  78. */
  79. @serialize()
  80. public cameraAcceleration: number = 0.05;
  81. /**
  82. * Define the speed limit of the camera following an object.
  83. */
  84. @serialize()
  85. public maxCameraSpeed: number = 20;
  86. /**
  87. * Define the target of the camera.
  88. */
  89. @serializeAsMeshReference("lockedTargetId")
  90. public lockedTarget: Nullable<AbstractMesh>;
  91. /**
  92. * Defines the input associated with the camera.
  93. */
  94. public inputs: FollowCameraInputsManager;
  95. /**
  96. * Instantiates the follow camera.
  97. * @see http://doc.babylonjs.com/features/cameras#follow-camera
  98. * @param name Define the name of the camera in the scene
  99. * @param position Define the position of the camera
  100. * @param scene Define the scene the camera belong to
  101. * @param lockedTarget Define the target of the camera
  102. */
  103. constructor(name: string, position: Vector3, scene: Scene, lockedTarget: Nullable<AbstractMesh> = null) {
  104. super(name, position, scene);
  105. this.lockedTarget = lockedTarget;
  106. this.inputs = new FollowCameraInputsManager(this);
  107. this.inputs.addKeyboard().addMouseWheel().addPointers();
  108. // Uncomment the following line when the relevant handlers have been implemented.
  109. // this.inputs.addKeyboard().addMouseWheel().addPointers().addVRDeviceOrientation();
  110. }
  111. private _follow(cameraTarget: AbstractMesh) {
  112. if (!cameraTarget) {
  113. return;
  114. }
  115. var yRotation;
  116. if (cameraTarget.rotationQuaternion) {
  117. var rotMatrix = new Matrix();
  118. cameraTarget.rotationQuaternion.toRotationMatrix(rotMatrix);
  119. yRotation = Math.atan2(rotMatrix.m[8], rotMatrix.m[10]);
  120. } else {
  121. yRotation = cameraTarget.rotation.y;
  122. }
  123. var radians = Tools.ToRadians(this.rotationOffset) + yRotation;
  124. var targetPosition = cameraTarget.getAbsolutePosition();
  125. var targetX: number = targetPosition.x + Math.sin(radians) * this.radius;
  126. var targetZ: number = targetPosition.z + Math.cos(radians) * this.radius;
  127. var dx: number = targetX - this.position.x;
  128. var dy: number = (targetPosition.y + this.heightOffset) - this.position.y;
  129. var dz: number = (targetZ) - this.position.z;
  130. var vx: number = dx * this.cameraAcceleration * 2; //this is set to .05
  131. var vy: number = dy * this.cameraAcceleration;
  132. var vz: number = dz * this.cameraAcceleration * 2;
  133. if (vx > this.maxCameraSpeed || vx < -this.maxCameraSpeed) {
  134. vx = vx < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
  135. }
  136. if (vy > this.maxCameraSpeed || vy < -this.maxCameraSpeed) {
  137. vy = vy < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
  138. }
  139. if (vz > this.maxCameraSpeed || vz < -this.maxCameraSpeed) {
  140. vz = vz < 1 ? -this.maxCameraSpeed : this.maxCameraSpeed;
  141. }
  142. this.position = new Vector3(this.position.x + vx, this.position.y + vy, this.position.z + vz);
  143. this.setTarget(targetPosition);
  144. }
  145. /**
  146. * Attached controls to the current camera.
  147. * @param element Defines the element the controls should be listened from
  148. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  149. */
  150. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  151. this.inputs.attachElement(element, noPreventDefault);
  152. this._reset = () => {
  153. };
  154. }
  155. /**
  156. * Detach the current controls from the camera.
  157. * The camera will stop reacting to inputs.
  158. * @param element Defines the element to stop listening the inputs from
  159. */
  160. public detachControl(element: HTMLElement): void {
  161. this.inputs.detachElement(element);
  162. if (this._reset) {
  163. this._reset();
  164. }
  165. }
  166. /** @hidden */
  167. public _checkInputs(): void {
  168. this.inputs.checkInputs();
  169. this._checkLimits();
  170. super._checkInputs();
  171. if (this.lockedTarget) {
  172. this._follow(this.lockedTarget);
  173. }
  174. }
  175. private _checkLimits() {
  176. if (this.lowerRadiusLimit !== null && this.radius < this.lowerRadiusLimit) {
  177. this.radius = this.lowerRadiusLimit;
  178. }
  179. if (this.upperRadiusLimit !== null && this.radius > this.upperRadiusLimit) {
  180. this.radius = this.upperRadiusLimit;
  181. }
  182. if (this.lowerHeightOffsetLimit !== null &&
  183. this.heightOffset < this.lowerHeightOffsetLimit) {
  184. this.heightOffset = this.lowerHeightOffsetLimit;
  185. }
  186. if (this.upperHeightOffsetLimit !== null &&
  187. this.heightOffset > this.upperHeightOffsetLimit) {
  188. this.heightOffset = this.upperHeightOffsetLimit;
  189. }
  190. if (this.lowerRotationOffsetLimit !== null &&
  191. this.rotationOffset < this.lowerRotationOffsetLimit) {
  192. this.rotationOffset = this.lowerRotationOffsetLimit;
  193. }
  194. if (this.upperRotationOffsetLimit !== null &&
  195. this.rotationOffset > this.upperRotationOffsetLimit) {
  196. this.rotationOffset = this.upperRotationOffsetLimit;
  197. }
  198. }
  199. /**
  200. * Gets the camera class name.
  201. * @returns the class name
  202. */
  203. public getClassName(): string {
  204. return "FollowCamera";
  205. }
  206. }
  207. /**
  208. * Arc Rotate version of the follow camera.
  209. * It still follows a Defined mesh but in an Arc Rotate Camera fashion.
  210. * @see http://doc.babylonjs.com/features/cameras#follow-camera
  211. */
  212. export class ArcFollowCamera extends TargetCamera {
  213. private _cartesianCoordinates: Vector3 = Vector3.Zero();
  214. /**
  215. * Instantiates a new ArcFollowCamera
  216. * @see http://doc.babylonjs.com/features/cameras#follow-camera
  217. * @param name Define the name of the camera
  218. * @param alpha Define the rotation angle of the camera around the logitudinal axis
  219. * @param beta Define the rotation angle of the camera around the elevation axis
  220. * @param radius Define the radius of the camera from its target point
  221. * @param target Define the target of the camera
  222. * @param scene Define the scene the camera belongs to
  223. */
  224. constructor(name: string,
  225. /** The longitudinal angle of the camera */
  226. public alpha: number,
  227. /** The latitudinal angle of the camera */
  228. public beta: number,
  229. /** The radius of the camera from its target */
  230. public radius: number,
  231. /** Define the camera target (the messh it should follow) */
  232. public target: Nullable<AbstractMesh>,
  233. scene: Scene) {
  234. super(name, Vector3.Zero(), scene);
  235. this._follow();
  236. }
  237. private _follow(): void {
  238. if (!this.target) {
  239. return;
  240. }
  241. this._cartesianCoordinates.x = this.radius * Math.cos(this.alpha) * Math.cos(this.beta);
  242. this._cartesianCoordinates.y = this.radius * Math.sin(this.beta);
  243. this._cartesianCoordinates.z = this.radius * Math.sin(this.alpha) * Math.cos(this.beta);
  244. var targetPosition = this.target.getAbsolutePosition();
  245. this.position = targetPosition.add(this._cartesianCoordinates);
  246. this.setTarget(targetPosition);
  247. }
  248. /** @hidden */
  249. public _checkInputs(): void {
  250. super._checkInputs();
  251. this._follow();
  252. }
  253. /**
  254. * Returns the class name of the object.
  255. * It is mostly used internally for serialization purposes.
  256. */
  257. public getClassName(): string {
  258. return "ArcFollowCamera";
  259. }
  260. }