babylon.targetCamera.ts 16 KB

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