babylon.camera.ts 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995
  1. module BABYLON {
  2. export class Camera extends Node {
  3. public inputs: CameraInputsManager<Camera>;
  4. // Statics
  5. private static _PERSPECTIVE_CAMERA = 0;
  6. private static _ORTHOGRAPHIC_CAMERA = 1;
  7. private static _FOVMODE_VERTICAL_FIXED = 0;
  8. private static _FOVMODE_HORIZONTAL_FIXED = 1;
  9. private static _RIG_MODE_NONE = 0;
  10. private static _RIG_MODE_STEREOSCOPIC_ANAGLYPH = 10;
  11. private static _RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL = 11;
  12. private static _RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED = 12;
  13. private static _RIG_MODE_STEREOSCOPIC_OVERUNDER = 13;
  14. private static _RIG_MODE_VR = 20;
  15. private static _RIG_MODE_WEBVR = 21;
  16. public static get PERSPECTIVE_CAMERA(): number {
  17. return Camera._PERSPECTIVE_CAMERA;
  18. }
  19. public static get ORTHOGRAPHIC_CAMERA(): number {
  20. return Camera._ORTHOGRAPHIC_CAMERA;
  21. }
  22. /**
  23. * This is the default FOV mode for perspective cameras.
  24. * This setting aligns the upper and lower bounds of the viewport to the upper and lower bounds of the camera frustum.
  25. *
  26. */
  27. public static get FOVMODE_VERTICAL_FIXED(): number {
  28. return Camera._FOVMODE_VERTICAL_FIXED;
  29. }
  30. /**
  31. * This setting aligns the left and right bounds of the viewport to the left and right bounds of the camera frustum.
  32. *
  33. */
  34. public static get FOVMODE_HORIZONTAL_FIXED(): number {
  35. return Camera._FOVMODE_HORIZONTAL_FIXED;
  36. }
  37. public static get RIG_MODE_NONE(): number {
  38. return Camera._RIG_MODE_NONE;
  39. }
  40. public static get RIG_MODE_STEREOSCOPIC_ANAGLYPH(): number {
  41. return Camera._RIG_MODE_STEREOSCOPIC_ANAGLYPH;
  42. }
  43. public static get RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL(): number {
  44. return Camera._RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL;
  45. }
  46. public static get RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED(): number {
  47. return Camera._RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
  48. }
  49. public static get RIG_MODE_STEREOSCOPIC_OVERUNDER(): number {
  50. return Camera._RIG_MODE_STEREOSCOPIC_OVERUNDER;
  51. }
  52. public static get RIG_MODE_VR(): number {
  53. return Camera._RIG_MODE_VR;
  54. }
  55. public static get RIG_MODE_WEBVR(): number {
  56. return Camera._RIG_MODE_WEBVR;
  57. }
  58. public static ForceAttachControlToAlwaysPreventDefault = false;
  59. public static UseAlternateWebVRRendering = false;
  60. // Members
  61. @serializeAsVector3()
  62. public position: Vector3;
  63. /**
  64. * The vector the camera should consider as up.
  65. * (default is Vector3(0, 1, 0) aka Vector3.Up())
  66. */
  67. @serializeAsVector3()
  68. public upVector = Vector3.Up();
  69. @serialize()
  70. public orthoLeft: Nullable<number> = null;
  71. @serialize()
  72. public orthoRight: Nullable<number> = null;
  73. @serialize()
  74. public orthoBottom: Nullable<number> = null;
  75. @serialize()
  76. public orthoTop: Nullable<number> = null;
  77. /**
  78. * FOV is set in Radians. (default is 0.8)
  79. */
  80. @serialize()
  81. public fov = 0.8;
  82. @serialize()
  83. public minZ = 1;
  84. @serialize()
  85. public maxZ = 10000.0;
  86. @serialize()
  87. public inertia = 0.9;
  88. @serialize()
  89. public mode = Camera.PERSPECTIVE_CAMERA;
  90. public isIntermediate = false;
  91. public viewport = new Viewport(0, 0, 1.0, 1.0);
  92. /**
  93. * Restricts the camera to viewing objects with the same layerMask.
  94. * A camera with a layerMask of 1 will render mesh.layerMask & camera.layerMask!== 0
  95. */
  96. @serialize()
  97. public layerMask: number = 0x0FFFFFFF;
  98. /**
  99. * fovMode sets the camera frustum bounds to the viewport bounds. (default is FOVMODE_VERTICAL_FIXED)
  100. */
  101. @serialize()
  102. public fovMode: number = Camera.FOVMODE_VERTICAL_FIXED;
  103. // Camera rig members
  104. @serialize()
  105. public cameraRigMode = Camera.RIG_MODE_NONE;
  106. @serialize()
  107. public interaxialDistance: number
  108. @serialize()
  109. public isStereoscopicSideBySide: boolean
  110. public _cameraRigParams: any;
  111. public _rigCameras = new Array<Camera>();
  112. public _rigPostProcess: Nullable<PostProcess>;
  113. protected _webvrViewMatrix = Matrix.Identity();
  114. public _skipRendering = false;
  115. public _alternateCamera: Camera;
  116. public customRenderTargets = new Array<RenderTargetTexture>();
  117. // Observables
  118. public onViewMatrixChangedObservable = new Observable<Camera>();
  119. public onProjectionMatrixChangedObservable = new Observable<Camera>();
  120. public onAfterCheckInputsObservable = new Observable<Camera>();
  121. public onRestoreStateObservable = new Observable<Camera>();
  122. // Cache
  123. private _computedViewMatrix = Matrix.Identity();
  124. public _projectionMatrix = new Matrix();
  125. private _doNotComputeProjectionMatrix = false;
  126. private _worldMatrix: Matrix;
  127. public _postProcesses = new Array<PostProcess>();
  128. private _transformMatrix = Matrix.Zero();
  129. public _activeMeshes = new SmartArray<AbstractMesh>(256);
  130. private _globalPosition = Vector3.Zero();
  131. private _frustumPlanes: Plane[];
  132. private _refreshFrustumPlanes = true;
  133. constructor(name: string, position: Vector3, scene: Scene) {
  134. super(name, scene);
  135. this.getScene().addCamera(this);
  136. if (!this.getScene().activeCamera) {
  137. this.getScene().activeCamera = this;
  138. }
  139. this.position = position;
  140. }
  141. private _storedFov: number;
  142. private _stateStored: boolean;
  143. /**
  144. * Store current camera state (fov, position, etc..)
  145. */
  146. public storeState(): Camera {
  147. this._stateStored = true;
  148. this._storedFov = this.fov;
  149. return this;
  150. }
  151. /**
  152. * Restores the camera state values if it has been stored. You must call storeState() first
  153. */
  154. protected _restoreStateValues(): boolean {
  155. if (!this._stateStored) {
  156. return false;
  157. }
  158. this.fov = this._storedFov;
  159. return true;
  160. }
  161. /**
  162. * Restored camera state. You must call storeState() first
  163. */
  164. public restoreState(): boolean {
  165. if (this._restoreStateValues()) {
  166. this.onRestoreStateObservable.notifyObservers(this);
  167. return true;
  168. }
  169. return false;
  170. }
  171. public getClassName(): string {
  172. return "Camera";
  173. }
  174. /**
  175. * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
  176. */
  177. public toString(fullDetails?: boolean): string {
  178. var ret = "Name: " + this.name;
  179. ret += ", type: " + this.getClassName();
  180. if (this.animations) {
  181. for (var i = 0; i < this.animations.length; i++) {
  182. ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
  183. }
  184. }
  185. if (fullDetails) {
  186. }
  187. return ret;
  188. }
  189. public get globalPosition(): Vector3 {
  190. return this._globalPosition;
  191. }
  192. public getActiveMeshes(): SmartArray<AbstractMesh> {
  193. return this._activeMeshes;
  194. }
  195. public isActiveMesh(mesh: Mesh): boolean {
  196. return (this._activeMeshes.indexOf(mesh) !== -1);
  197. }
  198. //Cache
  199. public _initCache() {
  200. super._initCache();
  201. this._cache.position = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  202. this._cache.upVector = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  203. this._cache.mode = undefined;
  204. this._cache.minZ = undefined;
  205. this._cache.maxZ = undefined;
  206. this._cache.fov = undefined;
  207. this._cache.fovMode = undefined;
  208. this._cache.aspectRatio = undefined;
  209. this._cache.orthoLeft = undefined;
  210. this._cache.orthoRight = undefined;
  211. this._cache.orthoBottom = undefined;
  212. this._cache.orthoTop = undefined;
  213. this._cache.renderWidth = undefined;
  214. this._cache.renderHeight = undefined;
  215. }
  216. public _updateCache(ignoreParentClass?: boolean): void {
  217. if (!ignoreParentClass) {
  218. super._updateCache();
  219. }
  220. this._cache.position.copyFrom(this.position);
  221. this._cache.upVector.copyFrom(this.upVector);
  222. }
  223. // Synchronized
  224. public _isSynchronized(): boolean {
  225. return this._isSynchronizedViewMatrix() && this._isSynchronizedProjectionMatrix();
  226. }
  227. public _isSynchronizedViewMatrix(): boolean {
  228. if (!super._isSynchronized())
  229. return false;
  230. return this._cache.position.equals(this.position)
  231. && this._cache.upVector.equals(this.upVector)
  232. && this.isSynchronizedWithParent();
  233. }
  234. public _isSynchronizedProjectionMatrix(): boolean {
  235. var check = this._cache.mode === this.mode
  236. && this._cache.minZ === this.minZ
  237. && this._cache.maxZ === this.maxZ;
  238. if (!check) {
  239. return false;
  240. }
  241. var engine = this.getEngine();
  242. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  243. check = this._cache.fov === this.fov
  244. && this._cache.fovMode === this.fovMode
  245. && this._cache.aspectRatio === engine.getAspectRatio(this);
  246. }
  247. else {
  248. check = this._cache.orthoLeft === this.orthoLeft
  249. && this._cache.orthoRight === this.orthoRight
  250. && this._cache.orthoBottom === this.orthoBottom
  251. && this._cache.orthoTop === this.orthoTop
  252. && this._cache.renderWidth === engine.getRenderWidth()
  253. && this._cache.renderHeight === engine.getRenderHeight();
  254. }
  255. return check;
  256. }
  257. // Controls
  258. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  259. }
  260. public detachControl(element: HTMLElement): void {
  261. }
  262. public update(): void {
  263. this._checkInputs();
  264. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  265. this._updateRigCameras();
  266. }
  267. }
  268. public _checkInputs(): void {
  269. this.onAfterCheckInputsObservable.notifyObservers(this);
  270. }
  271. public get rigCameras(): Camera[] {
  272. return this._rigCameras;
  273. }
  274. public get rigPostProcess(): Nullable<PostProcess> {
  275. return this._rigPostProcess;
  276. }
  277. private _cascadePostProcessesToRigCams(): void {
  278. // invalidate framebuffer
  279. if (this._postProcesses.length > 0) {
  280. this._postProcesses[0].markTextureDirty();
  281. }
  282. // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
  283. for (var i = 0, len = this._rigCameras.length; i < len; i++) {
  284. var cam = this._rigCameras[i];
  285. var rigPostProcess = cam._rigPostProcess;
  286. // for VR rig, there does not have to be a post process
  287. if (rigPostProcess) {
  288. var isPass = rigPostProcess instanceof PassPostProcess;
  289. if (isPass) {
  290. // any rig which has a PassPostProcess for rig[0], cannot be isIntermediate when there are also user postProcesses
  291. cam.isIntermediate = this._postProcesses.length === 0;
  292. }
  293. cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
  294. rigPostProcess.markTextureDirty();
  295. } else {
  296. cam._postProcesses = this._postProcesses.slice(0);
  297. }
  298. }
  299. }
  300. public attachPostProcess(postProcess: PostProcess, insertAt: Nullable<number> = null): number {
  301. if (!postProcess.isReusable() && this._postProcesses.indexOf(postProcess) > -1) {
  302. Tools.Error("You're trying to reuse a post process not defined as reusable.");
  303. return 0;
  304. }
  305. if (insertAt == null || insertAt < 0) {
  306. this._postProcesses.push(postProcess);
  307. } else {
  308. this._postProcesses.splice(insertAt, 0, postProcess);
  309. }
  310. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  311. return this._postProcesses.indexOf(postProcess);
  312. }
  313. public detachPostProcess(postProcess: PostProcess): void {
  314. var idx = this._postProcesses.indexOf(postProcess);
  315. if (idx !== -1) {
  316. this._postProcesses.splice(idx, 1);
  317. }
  318. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  319. }
  320. public getWorldMatrix(): Matrix {
  321. if (!this._worldMatrix) {
  322. this._worldMatrix = Matrix.Identity();
  323. }
  324. var viewMatrix = this.getViewMatrix();
  325. viewMatrix.invertToRef(this._worldMatrix);
  326. return this._worldMatrix;
  327. }
  328. public _getViewMatrix(): Matrix {
  329. return Matrix.Identity();
  330. }
  331. public getViewMatrix(force?: boolean): Matrix {
  332. if (!force && this._isSynchronizedViewMatrix()) {
  333. return this._computedViewMatrix;
  334. }
  335. this.updateCache();
  336. this._computedViewMatrix = this._getViewMatrix();
  337. this._currentRenderId = this.getScene().getRenderId();
  338. this._refreshFrustumPlanes = true;
  339. if (!this.parent || !this.parent.getWorldMatrix) {
  340. this._globalPosition.copyFrom(this.position);
  341. } else {
  342. if (!this._worldMatrix) {
  343. this._worldMatrix = Matrix.Identity();
  344. }
  345. this._computedViewMatrix.invertToRef(this._worldMatrix);
  346. this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._computedViewMatrix);
  347. this._globalPosition.copyFromFloats(this._computedViewMatrix.m[12], this._computedViewMatrix.m[13], this._computedViewMatrix.m[14]);
  348. this._computedViewMatrix.invert();
  349. this._markSyncedWithParent();
  350. }
  351. if (this._cameraRigParams && this._cameraRigParams.vrPreViewMatrix) {
  352. this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
  353. }
  354. this.onViewMatrixChangedObservable.notifyObservers(this);
  355. return this._computedViewMatrix;
  356. }
  357. public freezeProjectionMatrix(projection?: Matrix): void {
  358. this._doNotComputeProjectionMatrix = true;
  359. if (projection !== undefined) {
  360. this._projectionMatrix = projection;
  361. }
  362. };
  363. public unfreezeProjectionMatrix(): void {
  364. this._doNotComputeProjectionMatrix = false;
  365. };
  366. public getProjectionMatrix(force?: boolean): Matrix {
  367. if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
  368. return this._projectionMatrix;
  369. }
  370. // Cache
  371. this._cache.mode = this.mode;
  372. this._cache.minZ = this.minZ;
  373. this._cache.maxZ = this.maxZ;
  374. // Matrix
  375. this._refreshFrustumPlanes = true;
  376. var engine = this.getEngine();
  377. var scene = this.getScene();
  378. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  379. this._cache.fov = this.fov;
  380. this._cache.fovMode = this.fovMode;
  381. this._cache.aspectRatio = engine.getAspectRatio(this);
  382. if (this.minZ <= 0) {
  383. this.minZ = 0.1;
  384. }
  385. if (scene.useRightHandedSystem) {
  386. Matrix.PerspectiveFovRHToRef(this.fov,
  387. engine.getAspectRatio(this),
  388. this.minZ,
  389. this.maxZ,
  390. this._projectionMatrix,
  391. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  392. } else {
  393. Matrix.PerspectiveFovLHToRef(this.fov,
  394. engine.getAspectRatio(this),
  395. this.minZ,
  396. this.maxZ,
  397. this._projectionMatrix,
  398. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  399. }
  400. } else {
  401. var halfWidth = engine.getRenderWidth() / 2.0;
  402. var halfHeight = engine.getRenderHeight() / 2.0;
  403. if (scene.useRightHandedSystem) {
  404. Matrix.OrthoOffCenterRHToRef(this.orthoLeft || -halfWidth,
  405. this.orthoRight || halfWidth,
  406. this.orthoBottom || -halfHeight,
  407. this.orthoTop || halfHeight,
  408. this.minZ,
  409. this.maxZ,
  410. this._projectionMatrix);
  411. } else {
  412. Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth,
  413. this.orthoRight || halfWidth,
  414. this.orthoBottom || -halfHeight,
  415. this.orthoTop || halfHeight,
  416. this.minZ,
  417. this.maxZ,
  418. this._projectionMatrix);
  419. }
  420. this._cache.orthoLeft = this.orthoLeft;
  421. this._cache.orthoRight = this.orthoRight;
  422. this._cache.orthoBottom = this.orthoBottom;
  423. this._cache.orthoTop = this.orthoTop;
  424. this._cache.renderWidth = engine.getRenderWidth();
  425. this._cache.renderHeight = engine.getRenderHeight();
  426. }
  427. this.onProjectionMatrixChangedObservable.notifyObservers(this);
  428. return this._projectionMatrix;
  429. }
  430. public getTranformationMatrix(): Matrix {
  431. this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  432. return this._transformMatrix;
  433. }
  434. private updateFrustumPlanes(): void {
  435. if (!this._refreshFrustumPlanes) {
  436. return;
  437. }
  438. this.getTranformationMatrix();
  439. if (!this._frustumPlanes) {
  440. this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
  441. } else {
  442. Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
  443. }
  444. this._refreshFrustumPlanes = false;
  445. }
  446. public isInFrustum(target: ICullable): boolean {
  447. this.updateFrustumPlanes();
  448. return target.isInFrustum(this._frustumPlanes);
  449. }
  450. public isCompletelyInFrustum(target: ICullable): boolean {
  451. this.updateFrustumPlanes();
  452. return target.isCompletelyInFrustum(this._frustumPlanes);
  453. }
  454. public getForwardRay(length = 100, transform?: Matrix, origin?: Vector3): Ray {
  455. if (!transform) {
  456. transform = this.getWorldMatrix();
  457. }
  458. if (!origin) {
  459. origin = this.position;
  460. }
  461. var forward = new Vector3(0, 0, 1);
  462. var forwardWorld = Vector3.TransformNormal(forward, transform);
  463. var direction = Vector3.Normalize(forwardWorld);
  464. return new Ray(origin, direction, length);
  465. }
  466. /**
  467. * Releases resources associated with this node.
  468. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  469. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  470. */
  471. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  472. // Observables
  473. this.onViewMatrixChangedObservable.clear();
  474. this.onProjectionMatrixChangedObservable.clear();
  475. this.onAfterCheckInputsObservable.clear();
  476. this.onRestoreStateObservable.clear();
  477. // Inputs
  478. if (this.inputs) {
  479. this.inputs.clear();
  480. }
  481. // Animations
  482. this.getScene().stopAnimation(this);
  483. // Remove from scene
  484. this.getScene().removeCamera(this);
  485. while (this._rigCameras.length > 0) {
  486. let camera = this._rigCameras.pop();
  487. if (camera) {
  488. camera.dispose();
  489. }
  490. }
  491. // Postprocesses
  492. if (this._rigPostProcess) {
  493. this._rigPostProcess.dispose(this);
  494. this._rigPostProcess = null;
  495. this._postProcesses = [];
  496. }
  497. else if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  498. this._rigPostProcess = null;
  499. this._postProcesses = [];
  500. } else {
  501. var i = this._postProcesses.length;
  502. while (--i >= 0) {
  503. this._postProcesses[i].dispose(this);
  504. }
  505. }
  506. // Render targets
  507. var i = this.customRenderTargets.length;
  508. while (--i >= 0) {
  509. this.customRenderTargets[i].dispose();
  510. }
  511. this.customRenderTargets = [];
  512. // Active Meshes
  513. this._activeMeshes.dispose();
  514. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  515. }
  516. // ---- Camera rigs section ----
  517. public get leftCamera(): Nullable<FreeCamera> {
  518. if (this._rigCameras.length < 1) {
  519. return null;
  520. }
  521. return (<FreeCamera>this._rigCameras[0]);
  522. }
  523. public get rightCamera(): Nullable<FreeCamera> {
  524. if (this._rigCameras.length < 2) {
  525. return null;
  526. }
  527. return (<FreeCamera>this._rigCameras[1]);
  528. }
  529. public getLeftTarget(): Nullable<Vector3> {
  530. if (this._rigCameras.length < 1) {
  531. return null;
  532. }
  533. return (<TargetCamera>this._rigCameras[0]).getTarget();
  534. }
  535. public getRightTarget(): Nullable<Vector3> {
  536. if (this._rigCameras.length < 2) {
  537. return null;
  538. }
  539. return (<TargetCamera>this._rigCameras[1]).getTarget();
  540. }
  541. public setCameraRigMode(mode: number, rigParams: any): void {
  542. if (this.cameraRigMode === mode) {
  543. return;
  544. }
  545. while (this._rigCameras.length > 0) {
  546. let camera = this._rigCameras.pop();
  547. if (camera) {
  548. camera.dispose();
  549. }
  550. }
  551. this.cameraRigMode = mode;
  552. this._cameraRigParams = {};
  553. //we have to implement stereo camera calcultating left and right viewpoints from interaxialDistance and target,
  554. //not from a given angle as it is now, but until that complete code rewriting provisional stereoHalfAngle value is introduced
  555. this._cameraRigParams.interaxialDistance = rigParams.interaxialDistance || 0.0637;
  556. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(this._cameraRigParams.interaxialDistance / 0.0637);
  557. // create the rig cameras, unless none
  558. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  559. let leftCamera = this.createRigCamera(this.name + "_L", 0);
  560. let rightCamera = this.createRigCamera(this.name + "_R", 1);
  561. if (leftCamera && rightCamera) {
  562. this._rigCameras.push(leftCamera);
  563. this._rigCameras.push(rightCamera);
  564. }
  565. }
  566. switch (this.cameraRigMode) {
  567. case Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH:
  568. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  569. this._rigCameras[1]._rigPostProcess = new AnaglyphPostProcess(this.name + "_anaglyph", 1.0, this._rigCameras);
  570. break;
  571. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:
  572. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
  573. case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
  574. var isStereoscopicHoriz = this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL || this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
  575. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  576. this._rigCameras[1]._rigPostProcess = new StereoscopicInterlacePostProcess(this.name + "_stereoInterlace", this._rigCameras, isStereoscopicHoriz);
  577. break;
  578. case Camera.RIG_MODE_VR:
  579. var metrics = rigParams.vrCameraMetrics || VRCameraMetrics.GetDefault();
  580. this._rigCameras[0]._cameraRigParams.vrMetrics = metrics;
  581. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  582. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  583. this._rigCameras[0]._cameraRigParams.vrHMatrix = metrics.leftHMatrix;
  584. this._rigCameras[0]._cameraRigParams.vrPreViewMatrix = metrics.leftPreViewMatrix;
  585. this._rigCameras[0].getProjectionMatrix = this._rigCameras[0]._getVRProjectionMatrix;
  586. this._rigCameras[1]._cameraRigParams.vrMetrics = metrics;
  587. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  588. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  589. this._rigCameras[1]._cameraRigParams.vrHMatrix = metrics.rightHMatrix;
  590. this._rigCameras[1]._cameraRigParams.vrPreViewMatrix = metrics.rightPreViewMatrix;
  591. this._rigCameras[1].getProjectionMatrix = this._rigCameras[1]._getVRProjectionMatrix;
  592. if (metrics.compensateDistortion) {
  593. this._rigCameras[0]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Left", this._rigCameras[0], false, metrics);
  594. this._rigCameras[1]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Right", this._rigCameras[1], true, metrics);
  595. }
  596. break;
  597. case Camera.RIG_MODE_WEBVR:
  598. if (rigParams.vrDisplay) {
  599. var leftEye = rigParams.vrDisplay.getEyeParameters('left');
  600. var rightEye = rigParams.vrDisplay.getEyeParameters('right');
  601. //Left eye
  602. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  603. this._rigCameras[0].setCameraRigParameter("left", true);
  604. //leaving this for future reference
  605. this._rigCameras[0].setCameraRigParameter("specs", rigParams.specs);
  606. this._rigCameras[0].setCameraRigParameter("eyeParameters", leftEye);
  607. this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
  608. this._rigCameras[0].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  609. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  610. this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
  611. this._rigCameras[0].parent = this;
  612. this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
  613. //Right eye
  614. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  615. this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
  616. this._rigCameras[1].setCameraRigParameter("specs", rigParams.specs);
  617. this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
  618. this._rigCameras[1].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  619. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  620. this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
  621. this._rigCameras[1].parent = this;
  622. this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
  623. if (Camera.UseAlternateWebVRRendering) {
  624. this._rigCameras[1]._skipRendering = true;
  625. this._rigCameras[0]._alternateCamera = this._rigCameras[1];
  626. }
  627. }
  628. break;
  629. }
  630. this._cascadePostProcessesToRigCams();
  631. this.update();
  632. }
  633. private _getVRProjectionMatrix(): Matrix {
  634. Matrix.PerspectiveFovLHToRef(this._cameraRigParams.vrMetrics.aspectRatioFov, this._cameraRigParams.vrMetrics.aspectRatio, this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
  635. this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix, this._projectionMatrix);
  636. return this._projectionMatrix;
  637. }
  638. protected _updateCameraRotationMatrix() {
  639. //Here for WebVR
  640. }
  641. protected _updateWebVRCameraRotationMatrix() {
  642. //Here for WebVR
  643. }
  644. /**
  645. * This function MUST be overwritten by the different WebVR cameras available.
  646. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  647. */
  648. protected _getWebVRProjectionMatrix(): Matrix {
  649. return Matrix.Identity();
  650. }
  651. /**
  652. * This function MUST be overwritten by the different WebVR cameras available.
  653. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  654. */
  655. protected _getWebVRViewMatrix(): Matrix {
  656. return Matrix.Identity();
  657. }
  658. public setCameraRigParameter(name: string, value: any) {
  659. if (!this._cameraRigParams) {
  660. this._cameraRigParams = {};
  661. }
  662. this._cameraRigParams[name] = value;
  663. //provisionnally:
  664. if (name === "interaxialDistance") {
  665. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(value / 0.0637);
  666. }
  667. }
  668. /**
  669. * needs to be overridden by children so sub has required properties to be copied
  670. */
  671. public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
  672. return null;
  673. }
  674. /**
  675. * May need to be overridden by children
  676. */
  677. public _updateRigCameras() {
  678. for (var i = 0; i < this._rigCameras.length; i++) {
  679. this._rigCameras[i].minZ = this.minZ;
  680. this._rigCameras[i].maxZ = this.maxZ;
  681. this._rigCameras[i].fov = this.fov;
  682. }
  683. // only update viewport when ANAGLYPH
  684. if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
  685. this._rigCameras[0].viewport = this._rigCameras[1].viewport = this.viewport;
  686. }
  687. }
  688. public _setupInputs() {
  689. }
  690. public serialize(): any {
  691. var serializationObject = SerializationHelper.Serialize(this);
  692. // Type
  693. serializationObject.type = this.getClassName();
  694. // Parent
  695. if (this.parent) {
  696. serializationObject.parentId = this.parent.id;
  697. }
  698. if (this.inputs) {
  699. this.inputs.serialize(serializationObject);
  700. }
  701. // Animations
  702. Animation.AppendSerializedAnimations(this, serializationObject);
  703. serializationObject.ranges = this.serializeAnimationRanges();
  704. return serializationObject;
  705. }
  706. public clone(name: string): Camera {
  707. return SerializationHelper.Clone(Camera.GetConstructorFromName(this.getClassName(), name, this.getScene(), this.interaxialDistance, this.isStereoscopicSideBySide), this);
  708. }
  709. public getDirection(localAxis: Vector3): Vector3 {
  710. var result = Vector3.Zero();
  711. this.getDirectionToRef(localAxis, result);
  712. return result;
  713. }
  714. public getDirectionToRef(localAxis: Vector3, result: Vector3): void {
  715. Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
  716. }
  717. static GetConstructorFromName(type: string, name: string, scene: Scene, interaxial_distance: number = 0, isStereoscopicSideBySide: boolean = true): () => Camera {
  718. switch (type) {
  719. case "ArcRotateCamera":
  720. return () => new ArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), scene);
  721. case "DeviceOrientationCamera":
  722. return () => new DeviceOrientationCamera(name, Vector3.Zero(), scene);
  723. case "FollowCamera":
  724. return () => new FollowCamera(name, Vector3.Zero(), scene);
  725. case "ArcFollowCamera":
  726. return () => new ArcFollowCamera(name, 0, 0, 1.0, null, scene);
  727. case "GamepadCamera":
  728. return () => new GamepadCamera(name, Vector3.Zero(), scene);
  729. case "TouchCamera":
  730. return () => new TouchCamera(name, Vector3.Zero(), scene);
  731. case "VirtualJoysticksCamera":
  732. return () => new VirtualJoysticksCamera(name, Vector3.Zero(), scene);
  733. case "WebVRFreeCamera":
  734. return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
  735. case "WebVRGamepadCamera":
  736. return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
  737. case "VRDeviceOrientationFreeCamera":
  738. return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
  739. case "VRDeviceOrientationGamepadCamera":
  740. return () => new VRDeviceOrientationGamepadCamera(name, Vector3.Zero(), scene);
  741. case "AnaglyphArcRotateCamera":
  742. return () => new AnaglyphArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), interaxial_distance, scene);
  743. case "AnaglyphFreeCamera":
  744. return () => new AnaglyphFreeCamera(name, Vector3.Zero(), interaxial_distance, scene);
  745. case "AnaglyphGamepadCamera":
  746. return () => new AnaglyphGamepadCamera(name, Vector3.Zero(), interaxial_distance, scene);
  747. case "AnaglyphUniversalCamera":
  748. return () => new AnaglyphUniversalCamera(name, Vector3.Zero(), interaxial_distance, scene);
  749. case "StereoscopicArcRotateCamera":
  750. return () => new StereoscopicArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  751. case "StereoscopicFreeCamera":
  752. return () => new StereoscopicFreeCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  753. case "StereoscopicGamepadCamera":
  754. return () => new StereoscopicGamepadCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  755. case "StereoscopicUniversalCamera":
  756. return () => new StereoscopicUniversalCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  757. case "FreeCamera": // Forcing Universal here
  758. return () => new UniversalCamera(name, Vector3.Zero(), scene);
  759. default: // Universal Camera is the default value
  760. return () => new UniversalCamera(name, Vector3.Zero(), scene);
  761. }
  762. }
  763. public computeWorldMatrix(): Matrix {
  764. return this.getWorldMatrix();
  765. }
  766. public static Parse(parsedCamera: any, scene: Scene): Camera {
  767. var type = parsedCamera.type;
  768. var construct = Camera.GetConstructorFromName(type, parsedCamera.name, scene, parsedCamera.interaxial_distance, parsedCamera.isStereoscopicSideBySide);
  769. var camera = SerializationHelper.Parse(construct, parsedCamera, scene);
  770. // Parent
  771. if (parsedCamera.parentId) {
  772. camera._waitingParentId = parsedCamera.parentId;
  773. }
  774. //If camera has an input manager, let it parse inputs settings
  775. if (camera.inputs) {
  776. camera.inputs.parse(parsedCamera);
  777. camera._setupInputs();
  778. }
  779. if ((<any>camera).setPosition) { // need to force position
  780. camera.position.copyFromFloats(0, 0, 0);
  781. (<any>camera).setPosition(Vector3.FromArray(parsedCamera.position));
  782. }
  783. // Target
  784. if (parsedCamera.target) {
  785. if ((<any>camera).setTarget) {
  786. (<any>camera).setTarget(Vector3.FromArray(parsedCamera.target));
  787. }
  788. }
  789. // Apply 3d rig, when found
  790. if (parsedCamera.cameraRigMode) {
  791. var rigParams = (parsedCamera.interaxial_distance) ? { interaxialDistance: parsedCamera.interaxial_distance } : {};
  792. camera.setCameraRigMode(parsedCamera.cameraRigMode, rigParams);
  793. }
  794. // Animations
  795. if (parsedCamera.animations) {
  796. for (var animationIndex = 0; animationIndex < parsedCamera.animations.length; animationIndex++) {
  797. var parsedAnimation = parsedCamera.animations[animationIndex];
  798. camera.animations.push(Animation.Parse(parsedAnimation));
  799. }
  800. Node.ParseAnimationRanges(camera, parsedCamera, scene);
  801. }
  802. if (parsedCamera.autoAnimate) {
  803. scene.beginAnimation(camera, parsedCamera.autoAnimateFrom, parsedCamera.autoAnimateTo, parsedCamera.autoAnimateLoop, parsedCamera.autoAnimateSpeed || 1.0);
  804. }
  805. return camera;
  806. }
  807. }
  808. }