targetCamera.ts 21 KB

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