targetCamera.ts 19 KB

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