targetCamera.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. import { serialize, serializeAsVector3, serializeAsMeshReference } from "../Misc/decorators";
  2. import { Nullable } from "../types";
  3. import { Camera } from "./camera";
  4. import { Scene } from "../scene";
  5. import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "../Maths/math";
  6. /**
  7. * A target camera takes a mesh or position as a target and continues to look at it while it moves.
  8. * This is the base of the follow, arc rotate cameras and Free camera
  9. * @see http://doc.babylonjs.com/features/cameras
  10. */
  11. export class TargetCamera extends Camera {
  12. private static _RigCamTransformMatrix = new Matrix();
  13. private static _TargetTransformMatrix = new Matrix();
  14. private static _TargetFocalPoint = new Vector3();
  15. /**
  16. * Define the current direction the camera is moving to
  17. */
  18. public cameraDirection = new Vector3(0, 0, 0);
  19. /**
  20. * Define the current rotation the camera is rotating to
  21. */
  22. public cameraRotation = new Vector2(0, 0);
  23. /**
  24. * When set, the up vector of the camera will be updated by the rotation of the camera
  25. */
  26. public updateUpVectorFromRotation = false;
  27. private _tmpQuaternion = new Quaternion();
  28. /**
  29. * Define the current rotation of the camera
  30. */
  31. @serializeAsVector3()
  32. public rotation = new Vector3(0, 0, 0);
  33. /**
  34. * Define the current rotation of the camera as a quaternion to prevent Gimbal lock
  35. */
  36. public rotationQuaternion: Quaternion;
  37. /**
  38. * Define the current speed of the camera
  39. */
  40. @serialize()
  41. public speed = 2.0;
  42. /**
  43. * Add cconstraint to the camera to prevent it to move freely in all directions and
  44. * around all axis.
  45. */
  46. public noRotationConstraint = false;
  47. /**
  48. * Define the current target of the camera as an object or a position.
  49. */
  50. @serializeAsMeshReference("lockedTargetId")
  51. public lockedTarget: any = null;
  52. /** @hidden */
  53. public _currentTarget = Vector3.Zero();
  54. /** @hidden */
  55. public _initialFocalDistance = 1;
  56. /** @hidden */
  57. public _viewMatrix = Matrix.Zero();
  58. /** @hidden */
  59. public _camMatrix = Matrix.Zero();
  60. /** @hidden */
  61. public _cameraTransformMatrix = Matrix.Zero();
  62. /** @hidden */
  63. public _cameraRotationMatrix = Matrix.Zero();
  64. /** @hidden */
  65. public _referencePoint = new Vector3(0, 0, 1);
  66. /** @hidden */
  67. public _transformedReferencePoint = Vector3.Zero();
  68. protected _globalCurrentTarget = Vector3.Zero();
  69. protected _globalCurrentUpVector = Vector3.Zero();
  70. /** @hidden */
  71. public _reset: () => void;
  72. private _defaultUp = Vector3.Up();
  73. /**
  74. * Instantiates a target camera that takes a meshor position as a target and continues to look at it while it moves.
  75. * This is the base of the follow, arc rotate cameras and Free camera
  76. * @see http://doc.babylonjs.com/features/cameras
  77. * @param name Defines the name of the camera in the scene
  78. * @param position Defines the start position of the camera in the scene
  79. * @param scene Defines the scene the camera belongs to
  80. * @param setActiveOnSceneIfNoneActive Defines wheter the camera should be marked as active if not other active cameras have been defined
  81. */
  82. constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
  83. super(name, position, scene, setActiveOnSceneIfNoneActive);
  84. }
  85. /**
  86. * Gets the position in front of the camera at a given distance.
  87. * @param distance The distance from the camera we want the position to be
  88. * @returns the position
  89. */
  90. public getFrontPosition(distance: number): Vector3 {
  91. this.getWorldMatrix();
  92. var direction = this.getTarget().subtract(this.position);
  93. direction.normalize();
  94. direction.scaleInPlace(distance);
  95. return this.globalPosition.add(direction);
  96. }
  97. /** @hidden */
  98. public _getLockedTargetPosition(): Nullable<Vector3> {
  99. if (!this.lockedTarget) {
  100. return null;
  101. }
  102. if (this.lockedTarget.absolutePosition) {
  103. this.lockedTarget.computeWorldMatrix();
  104. }
  105. return this.lockedTarget.absolutePosition || this.lockedTarget;
  106. }
  107. private _storedPosition: Vector3;
  108. private _storedRotation: Vector3;
  109. private _storedRotationQuaternion: Quaternion;
  110. /**
  111. * Store current camera state of the camera (fov, position, rotation, etc..)
  112. * @returns the camera
  113. */
  114. public storeState(): Camera {
  115. this._storedPosition = this.position.clone();
  116. this._storedRotation = this.rotation.clone();
  117. if (this.rotationQuaternion) {
  118. this._storedRotationQuaternion = this.rotationQuaternion.clone();
  119. }
  120. return super.storeState();
  121. }
  122. /**
  123. * Restored camera state. You must call storeState() first
  124. * @returns whether it was successful or not
  125. * @hidden
  126. */
  127. public _restoreStateValues(): boolean {
  128. if (!super._restoreStateValues()) {
  129. return false;
  130. }
  131. this.position = this._storedPosition.clone();
  132. this.rotation = this._storedRotation.clone();
  133. if (this.rotationQuaternion) {
  134. this.rotationQuaternion = this._storedRotationQuaternion.clone();
  135. }
  136. this.cameraDirection.copyFromFloats(0, 0, 0);
  137. this.cameraRotation.copyFromFloats(0, 0);
  138. return true;
  139. }
  140. /** @hidden */
  141. public _initCache() {
  142. super._initCache();
  143. this._cache.lockedTarget = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  144. this._cache.rotation = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  145. this._cache.rotationQuaternion = new Quaternion(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  146. }
  147. /** @hidden */
  148. public _updateCache(ignoreParentClass?: boolean): void {
  149. if (!ignoreParentClass) {
  150. super._updateCache();
  151. }
  152. var lockedTargetPosition = this._getLockedTargetPosition();
  153. if (!lockedTargetPosition) {
  154. this._cache.lockedTarget = null;
  155. }
  156. else {
  157. if (!this._cache.lockedTarget) {
  158. this._cache.lockedTarget = lockedTargetPosition.clone();
  159. }
  160. else {
  161. this._cache.lockedTarget.copyFrom(lockedTargetPosition);
  162. }
  163. }
  164. this._cache.rotation.copyFrom(this.rotation);
  165. if (this.rotationQuaternion) {
  166. this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
  167. }
  168. }
  169. // Synchronized
  170. /** @hidden */
  171. public _isSynchronizedViewMatrix(): boolean {
  172. if (!super._isSynchronizedViewMatrix()) {
  173. return false;
  174. }
  175. var lockedTargetPosition = this._getLockedTargetPosition();
  176. return (this._cache.lockedTarget ? this._cache.lockedTarget.equals(lockedTargetPosition) : !lockedTargetPosition)
  177. && (this.rotationQuaternion ? this.rotationQuaternion.equals(this._cache.rotationQuaternion) : this._cache.rotation.equals(this.rotation));
  178. }
  179. // Methods
  180. /** @hidden */
  181. public _computeLocalCameraSpeed(): number {
  182. var engine = this.getEngine();
  183. return this.speed * Math.sqrt((engine.getDeltaTime() / (engine.getFps() * 100.0)));
  184. }
  185. // Target
  186. /**
  187. * Defines the target the camera should look at.
  188. * This will automatically adapt alpha beta and radius to fit within the new target.
  189. * @param target Defines the new target as a Vector or a mesh
  190. */
  191. public setTarget(target: Vector3): void {
  192. this.upVector.normalize();
  193. this._initialFocalDistance = target.subtract(this.position).length();
  194. if (this.position.z === target.z) {
  195. this.position.z += Epsilon;
  196. }
  197. Matrix.LookAtLHToRef(this.position, target, this._defaultUp, this._camMatrix);
  198. this._camMatrix.invert();
  199. this.rotation.x = Math.atan(this._camMatrix.m[6] / this._camMatrix.m[10]);
  200. var vDir = target.subtract(this.position);
  201. if (vDir.x >= 0.0) {
  202. this.rotation.y = (-Math.atan(vDir.z / vDir.x) + Math.PI / 2.0);
  203. } else {
  204. this.rotation.y = (-Math.atan(vDir.z / vDir.x) - Math.PI / 2.0);
  205. }
  206. this.rotation.z = 0;
  207. if (isNaN(this.rotation.x)) {
  208. this.rotation.x = 0;
  209. }
  210. if (isNaN(this.rotation.y)) {
  211. this.rotation.y = 0;
  212. }
  213. if (isNaN(this.rotation.z)) {
  214. this.rotation.z = 0;
  215. }
  216. if (this.rotationQuaternion) {
  217. Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
  218. }
  219. }
  220. /**
  221. * Return the current target position of the camera. This value is expressed in local space.
  222. * @returns the target position
  223. */
  224. public getTarget(): Vector3 {
  225. return this._currentTarget;
  226. }
  227. /** @hidden */
  228. public _decideIfNeedsToMove(): boolean {
  229. return Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
  230. }
  231. /** @hidden */
  232. public _updatePosition(): void {
  233. if (this.parent) {
  234. this.parent.getWorldMatrix().invertToRef(Tmp.Matrix[0]);
  235. Vector3.TransformNormalToRef(this.cameraDirection, Tmp.Matrix[0], Tmp.Vector3[0]);
  236. this.position.addInPlace(Tmp.Vector3[0]);
  237. return;
  238. }
  239. this.position.addInPlace(this.cameraDirection);
  240. }
  241. /** @hidden */
  242. public _checkInputs(): void {
  243. var needToMove = this._decideIfNeedsToMove();
  244. var needToRotate = Math.abs(this.cameraRotation.x) > 0 || Math.abs(this.cameraRotation.y) > 0;
  245. // Move
  246. if (needToMove) {
  247. this._updatePosition();
  248. }
  249. // Rotate
  250. if (needToRotate) {
  251. this.rotation.x += this.cameraRotation.x;
  252. this.rotation.y += this.cameraRotation.y;
  253. //rotate, if quaternion is set and rotation was used
  254. if (this.rotationQuaternion) {
  255. var len = this.rotation.lengthSquared();
  256. if (len) {
  257. Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
  258. }
  259. }
  260. if (!this.noRotationConstraint) {
  261. var limit = (Math.PI / 2) * 0.95;
  262. if (this.rotation.x > limit) {
  263. this.rotation.x = limit;
  264. }
  265. if (this.rotation.x < -limit) {
  266. this.rotation.x = -limit;
  267. }
  268. }
  269. }
  270. // Inertia
  271. if (needToMove) {
  272. if (Math.abs(this.cameraDirection.x) < this.speed * Epsilon) {
  273. this.cameraDirection.x = 0;
  274. }
  275. if (Math.abs(this.cameraDirection.y) < this.speed * Epsilon) {
  276. this.cameraDirection.y = 0;
  277. }
  278. if (Math.abs(this.cameraDirection.z) < this.speed * Epsilon) {
  279. this.cameraDirection.z = 0;
  280. }
  281. this.cameraDirection.scaleInPlace(this.inertia);
  282. }
  283. if (needToRotate) {
  284. if (Math.abs(this.cameraRotation.x) < this.speed * Epsilon) {
  285. this.cameraRotation.x = 0;
  286. }
  287. if (Math.abs(this.cameraRotation.y) < this.speed * Epsilon) {
  288. this.cameraRotation.y = 0;
  289. }
  290. this.cameraRotation.scaleInPlace(this.inertia);
  291. }
  292. super._checkInputs();
  293. }
  294. protected _updateCameraRotationMatrix() {
  295. if (this.rotationQuaternion) {
  296. this.rotationQuaternion.toRotationMatrix(this._cameraRotationMatrix);
  297. } else {
  298. Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  299. }
  300. }
  301. /**
  302. * Update the up vector to apply the rotation of the camera (So if you changed the camera rotation.z this will let you update the up vector as well)
  303. * @returns the current camera
  304. */
  305. private _rotateUpVectorWithCameraRotationMatrix(): TargetCamera {
  306. Vector3.TransformNormalToRef(this._defaultUp, this._cameraRotationMatrix, this.upVector);
  307. return this;
  308. }
  309. private _cachedRotationZ = 0;
  310. private _cachedQuaternionRotationZ = 0;
  311. /** @hidden */
  312. public _getViewMatrix(): Matrix {
  313. if (this.lockedTarget) {
  314. this.setTarget(this._getLockedTargetPosition()!);
  315. }
  316. // Compute
  317. this._updateCameraRotationMatrix();
  318. // Apply the changed rotation to the upVector
  319. if (this.rotationQuaternion && this._cachedQuaternionRotationZ != this.rotationQuaternion.z) {
  320. this._rotateUpVectorWithCameraRotationMatrix();
  321. this._cachedQuaternionRotationZ = this.rotationQuaternion.z;
  322. } else if (this._cachedRotationZ != this.rotation.z) {
  323. this._rotateUpVectorWithCameraRotationMatrix();
  324. this._cachedRotationZ = this.rotation.z;
  325. }
  326. Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
  327. // Computing target and final matrix
  328. this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
  329. if (this.updateUpVectorFromRotation) {
  330. if (this.rotationQuaternion) {
  331. Axis.Y.rotateByQuaternionToRef(this.rotationQuaternion, this.upVector);
  332. } else {
  333. Quaternion.FromEulerVectorToRef(this.rotation, this._tmpQuaternion);
  334. Axis.Y.rotateByQuaternionToRef(this._tmpQuaternion, this.upVector);
  335. }
  336. }
  337. this._computeViewMatrix(this.position, this._currentTarget, this.upVector);
  338. return this._viewMatrix;
  339. }
  340. protected _computeViewMatrix(position: Vector3, target: Vector3, up: Vector3): void {
  341. if (this.parent) {
  342. const parentWorldMatrix = this.parent.getWorldMatrix();
  343. Vector3.TransformCoordinatesToRef(position, parentWorldMatrix, this._globalPosition);
  344. Vector3.TransformCoordinatesToRef(target, parentWorldMatrix, this._globalCurrentTarget);
  345. Vector3.TransformNormalToRef(up, parentWorldMatrix, this._globalCurrentUpVector);
  346. this._markSyncedWithParent();
  347. } else {
  348. this._globalPosition.copyFrom(position);
  349. this._globalCurrentTarget.copyFrom(target);
  350. this._globalCurrentUpVector.copyFrom(up);
  351. }
  352. if (this.getScene().useRightHandedSystem) {
  353. Matrix.LookAtRHToRef(this._globalPosition, this._globalCurrentTarget, this._globalCurrentUpVector, this._viewMatrix);
  354. } else {
  355. Matrix.LookAtLHToRef(this._globalPosition, this._globalCurrentTarget, this._globalCurrentUpVector, this._viewMatrix);
  356. }
  357. }
  358. /**
  359. * @hidden
  360. */
  361. public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
  362. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  363. var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
  364. if (this.cameraRigMode === Camera.RIG_MODE_VR || this.cameraRigMode === Camera.RIG_MODE_WEBVR) {
  365. if (!this.rotationQuaternion) {
  366. this.rotationQuaternion = new Quaternion();
  367. }
  368. rigCamera._cameraRigParams = {};
  369. rigCamera.rotationQuaternion = new Quaternion();
  370. }
  371. return rigCamera;
  372. }
  373. return null;
  374. }
  375. /**
  376. * @hidden
  377. */
  378. public _updateRigCameras() {
  379. var camLeft = <TargetCamera>this._rigCameras[0];
  380. var camRight = <TargetCamera>this._rigCameras[1];
  381. this.computeWorldMatrix();
  382. switch (this.cameraRigMode) {
  383. case Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH:
  384. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:
  385. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
  386. case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
  387. //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
  388. var leftSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
  389. var rightSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
  390. this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
  391. this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
  392. break;
  393. case Camera.RIG_MODE_VR:
  394. if (camLeft.rotationQuaternion) {
  395. camLeft.rotationQuaternion.copyFrom(this.rotationQuaternion);
  396. camRight.rotationQuaternion.copyFrom(this.rotationQuaternion);
  397. } else {
  398. camLeft.rotation.copyFrom(this.rotation);
  399. camRight.rotation.copyFrom(this.rotation);
  400. }
  401. camLeft.position.copyFrom(this.position);
  402. camRight.position.copyFrom(this.position);
  403. break;
  404. }
  405. super._updateRigCameras();
  406. }
  407. private _getRigCamPositionAndTarget(halfSpace: number, rigCamera: TargetCamera) {
  408. var target = this.getTarget();
  409. target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
  410. TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
  411. var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
  412. Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
  413. TargetCamera._TargetTransformMatrix.multiplyToRef(Matrix.RotationY(halfSpace), TargetCamera._RigCamTransformMatrix);
  414. Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
  415. TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
  416. Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
  417. rigCamera.setTarget(newFocalTarget);
  418. }
  419. /**
  420. * Gets the current object class name.
  421. * @return the class name
  422. */
  423. public getClassName(): string {
  424. return "TargetCamera";
  425. }
  426. }