targetCamera.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. //rotate, if quaternion is set and rotation was used
  263. if (this.rotationQuaternion) {
  264. this.rotationQuaternion.toEulerAnglesToRef(this.rotation);
  265. }
  266. this.rotation.x += this.cameraRotation.x * directionMultiplier;
  267. this.rotation.y += this.cameraRotation.y * directionMultiplier;
  268. //rotate, if quaternion is set and rotation was used
  269. if (this.rotationQuaternion) {
  270. var len = this.rotation.lengthSquared();
  271. if (len) {
  272. Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
  273. }
  274. }
  275. if (!this.noRotationConstraint) {
  276. var limit = 1.570796;
  277. if (this.rotation.x > limit) {
  278. this.rotation.x = limit;
  279. }
  280. if (this.rotation.x < -limit) {
  281. this.rotation.x = -limit;
  282. }
  283. }
  284. }
  285. // Inertia
  286. if (needToMove) {
  287. if (Math.abs(this.cameraDirection.x) < this.speed * Epsilon) {
  288. this.cameraDirection.x = 0;
  289. }
  290. if (Math.abs(this.cameraDirection.y) < this.speed * Epsilon) {
  291. this.cameraDirection.y = 0;
  292. }
  293. if (Math.abs(this.cameraDirection.z) < this.speed * Epsilon) {
  294. this.cameraDirection.z = 0;
  295. }
  296. this.cameraDirection.scaleInPlace(this.inertia);
  297. }
  298. if (needToRotate) {
  299. if (Math.abs(this.cameraRotation.x) < this.speed * Epsilon) {
  300. this.cameraRotation.x = 0;
  301. }
  302. if (Math.abs(this.cameraRotation.y) < this.speed * Epsilon) {
  303. this.cameraRotation.y = 0;
  304. }
  305. this.cameraRotation.scaleInPlace(this.inertia);
  306. }
  307. super._checkInputs();
  308. }
  309. protected _updateCameraRotationMatrix() {
  310. if (this.rotationQuaternion) {
  311. this.rotationQuaternion.toRotationMatrix(this._cameraRotationMatrix);
  312. } else {
  313. Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this._cameraRotationMatrix);
  314. }
  315. }
  316. /**
  317. * 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)
  318. * @returns the current camera
  319. */
  320. private _rotateUpVectorWithCameraRotationMatrix(): TargetCamera {
  321. Vector3.TransformNormalToRef(this._defaultUp, this._cameraRotationMatrix, this.upVector);
  322. return this;
  323. }
  324. private _cachedRotationZ = 0;
  325. private _cachedQuaternionRotationZ = 0;
  326. /** @hidden */
  327. public _getViewMatrix(): Matrix {
  328. if (this.lockedTarget) {
  329. this.setTarget(this._getLockedTargetPosition()!);
  330. }
  331. // Compute
  332. this._updateCameraRotationMatrix();
  333. // Apply the changed rotation to the upVector
  334. if (this.rotationQuaternion && this._cachedQuaternionRotationZ != this.rotationQuaternion.z) {
  335. this._rotateUpVectorWithCameraRotationMatrix();
  336. this._cachedQuaternionRotationZ = this.rotationQuaternion.z;
  337. } else if (this._cachedRotationZ != this.rotation.z) {
  338. this._rotateUpVectorWithCameraRotationMatrix();
  339. this._cachedRotationZ = this.rotation.z;
  340. }
  341. Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
  342. // Computing target and final matrix
  343. this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
  344. if (this.updateUpVectorFromRotation) {
  345. if (this.rotationQuaternion) {
  346. Axis.Y.rotateByQuaternionToRef(this.rotationQuaternion, this.upVector);
  347. } else {
  348. Quaternion.FromEulerVectorToRef(this.rotation, this._tmpQuaternion);
  349. Axis.Y.rotateByQuaternionToRef(this._tmpQuaternion, this.upVector);
  350. }
  351. }
  352. this._computeViewMatrix(this.position, this._currentTarget, this.upVector);
  353. return this._viewMatrix;
  354. }
  355. protected _computeViewMatrix(position: Vector3, target: Vector3, up: Vector3): void {
  356. this._globalPosition.copyFrom(position);
  357. this._globalCurrentTarget.copyFrom(target);
  358. this._globalCurrentUpVector.copyFrom(up);
  359. if (this.getScene().useRightHandedSystem) {
  360. Matrix.LookAtRHToRef(this._globalPosition, this._globalCurrentTarget, this._globalCurrentUpVector, this._viewMatrix);
  361. } else {
  362. Matrix.LookAtLHToRef(this._globalPosition, this._globalCurrentTarget, this._globalCurrentUpVector, this._viewMatrix);
  363. }
  364. if (this.parent) {
  365. const parentWorldMatrix = this.parent.getWorldMatrix();
  366. this._viewMatrix.invert();
  367. this._viewMatrix.multiplyToRef(parentWorldMatrix, this._viewMatrix);
  368. this._viewMatrix.getTranslationToRef(this._globalPosition);
  369. this._viewMatrix.invert();
  370. this._markSyncedWithParent();
  371. }
  372. }
  373. /**
  374. * @hidden
  375. */
  376. public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
  377. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  378. var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
  379. rigCamera.isRigCamera = true;
  380. rigCamera.rigParent = this;
  381. if (this.cameraRigMode === Camera.RIG_MODE_VR || this.cameraRigMode === Camera.RIG_MODE_WEBVR) {
  382. if (!this.rotationQuaternion) {
  383. this.rotationQuaternion = new Quaternion();
  384. }
  385. rigCamera._cameraRigParams = {};
  386. rigCamera.rotationQuaternion = new Quaternion();
  387. }
  388. return rigCamera;
  389. }
  390. return null;
  391. }
  392. /**
  393. * @hidden
  394. */
  395. public _updateRigCameras() {
  396. var camLeft = <TargetCamera>this._rigCameras[0];
  397. var camRight = <TargetCamera>this._rigCameras[1];
  398. this.computeWorldMatrix();
  399. switch (this.cameraRigMode) {
  400. case Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH:
  401. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:
  402. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
  403. case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
  404. case Camera.RIG_MODE_STEREOSCOPIC_INTERLACED:
  405. //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
  406. var leftSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
  407. var rightSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
  408. this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
  409. this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
  410. break;
  411. case Camera.RIG_MODE_VR:
  412. if (camLeft.rotationQuaternion) {
  413. camLeft.rotationQuaternion.copyFrom(this.rotationQuaternion);
  414. camRight.rotationQuaternion.copyFrom(this.rotationQuaternion);
  415. } else {
  416. camLeft.rotation.copyFrom(this.rotation);
  417. camRight.rotation.copyFrom(this.rotation);
  418. }
  419. camLeft.position.copyFrom(this.position);
  420. camRight.position.copyFrom(this.position);
  421. break;
  422. }
  423. super._updateRigCameras();
  424. }
  425. private _getRigCamPositionAndTarget(halfSpace: number, rigCamera: TargetCamera) {
  426. var target = this.getTarget();
  427. target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
  428. TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
  429. var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
  430. Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
  431. TargetCamera._TargetTransformMatrix.multiplyToRef(Matrix.RotationAxis(rigCamera.upVector, halfSpace), TargetCamera._RigCamTransformMatrix);
  432. Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
  433. TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
  434. Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
  435. rigCamera.setTarget(newFocalTarget);
  436. }
  437. /**
  438. * Gets the current object class name.
  439. * @return the class name
  440. */
  441. public getClassName(): string {
  442. return "TargetCamera";
  443. }
  444. }