targetCamera.ts 19 KB

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