babylon.camera.ts 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  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<Nullable<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. /**
  199. * Is this camera ready to be used/rendered
  200. * @param completeCheck defines if a complete check (including post processes) has to be done (false by default)
  201. * @return true if the camera is ready
  202. */
  203. public isReady(completeCheck = false): boolean {
  204. if (completeCheck) {
  205. for (var pp of this._postProcesses) {
  206. if (pp && !pp.isReady()) {
  207. return false;
  208. }
  209. }
  210. }
  211. return super.isReady(completeCheck);
  212. }
  213. //Cache
  214. public _initCache() {
  215. super._initCache();
  216. this._cache.position = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  217. this._cache.upVector = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  218. this._cache.mode = undefined;
  219. this._cache.minZ = undefined;
  220. this._cache.maxZ = undefined;
  221. this._cache.fov = undefined;
  222. this._cache.fovMode = undefined;
  223. this._cache.aspectRatio = undefined;
  224. this._cache.orthoLeft = undefined;
  225. this._cache.orthoRight = undefined;
  226. this._cache.orthoBottom = undefined;
  227. this._cache.orthoTop = undefined;
  228. this._cache.renderWidth = undefined;
  229. this._cache.renderHeight = undefined;
  230. }
  231. public _updateCache(ignoreParentClass?: boolean): void {
  232. if (!ignoreParentClass) {
  233. super._updateCache();
  234. }
  235. this._cache.position.copyFrom(this.position);
  236. this._cache.upVector.copyFrom(this.upVector);
  237. }
  238. // Synchronized
  239. public _isSynchronized(): boolean {
  240. return this._isSynchronizedViewMatrix() && this._isSynchronizedProjectionMatrix();
  241. }
  242. public _isSynchronizedViewMatrix(): boolean {
  243. if (!super._isSynchronized())
  244. return false;
  245. return this._cache.position.equals(this.position)
  246. && this._cache.upVector.equals(this.upVector)
  247. && this.isSynchronizedWithParent();
  248. }
  249. public _isSynchronizedProjectionMatrix(): boolean {
  250. var check = this._cache.mode === this.mode
  251. && this._cache.minZ === this.minZ
  252. && this._cache.maxZ === this.maxZ;
  253. if (!check) {
  254. return false;
  255. }
  256. var engine = this.getEngine();
  257. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  258. check = this._cache.fov === this.fov
  259. && this._cache.fovMode === this.fovMode
  260. && this._cache.aspectRatio === engine.getAspectRatio(this);
  261. }
  262. else {
  263. check = this._cache.orthoLeft === this.orthoLeft
  264. && this._cache.orthoRight === this.orthoRight
  265. && this._cache.orthoBottom === this.orthoBottom
  266. && this._cache.orthoTop === this.orthoTop
  267. && this._cache.renderWidth === engine.getRenderWidth()
  268. && this._cache.renderHeight === engine.getRenderHeight();
  269. }
  270. return check;
  271. }
  272. // Controls
  273. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  274. }
  275. public detachControl(element: HTMLElement): void {
  276. }
  277. public update(): void {
  278. this._checkInputs();
  279. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  280. this._updateRigCameras();
  281. }
  282. }
  283. public _checkInputs(): void {
  284. this.onAfterCheckInputsObservable.notifyObservers(this);
  285. }
  286. public get rigCameras(): Camera[] {
  287. return this._rigCameras;
  288. }
  289. public get rigPostProcess(): Nullable<PostProcess> {
  290. return this._rigPostProcess;
  291. }
  292. /**
  293. * Internal, gets the first post proces.
  294. * @returns the first post process to be run on this camera.
  295. */
  296. public _getFirstPostProcess():Nullable<PostProcess>{
  297. for(var ppIndex = 0; ppIndex < this._postProcesses.length; ppIndex++){
  298. if(this._postProcesses[ppIndex] !== null){
  299. return this._postProcesses[ppIndex];
  300. }
  301. }
  302. return null;
  303. }
  304. private _cascadePostProcessesToRigCams(): void {
  305. // invalidate framebuffer
  306. var firstPostProcess = this._getFirstPostProcess();
  307. if (firstPostProcess) {
  308. firstPostProcess.markTextureDirty();
  309. }
  310. // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
  311. for (var i = 0, len = this._rigCameras.length; i < len; i++) {
  312. var cam = this._rigCameras[i];
  313. var rigPostProcess = cam._rigPostProcess;
  314. // for VR rig, there does not have to be a post process
  315. if (rigPostProcess) {
  316. var isPass = rigPostProcess instanceof PassPostProcess;
  317. if (isPass) {
  318. // any rig which has a PassPostProcess for rig[0], cannot be isIntermediate when there are also user postProcesses
  319. cam.isIntermediate = this._postProcesses.length === 0;
  320. }
  321. cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
  322. rigPostProcess.markTextureDirty();
  323. } else {
  324. cam._postProcesses = this._postProcesses.slice(0);
  325. }
  326. }
  327. }
  328. public attachPostProcess(postProcess: PostProcess, insertAt: Nullable<number> = null): number {
  329. if (!postProcess.isReusable() && this._postProcesses.indexOf(postProcess) > -1) {
  330. Tools.Error("You're trying to reuse a post process not defined as reusable.");
  331. return 0;
  332. }
  333. if (insertAt == null || insertAt < 0) {
  334. this._postProcesses.push(postProcess);
  335. } else if(this._postProcesses[insertAt] === null) {
  336. this._postProcesses[insertAt] = postProcess;
  337. }else{
  338. this._postProcesses.splice(insertAt, 0, postProcess);
  339. }
  340. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  341. return this._postProcesses.indexOf(postProcess);
  342. }
  343. public detachPostProcess(postProcess: PostProcess): void {
  344. var idx = this._postProcesses.indexOf(postProcess);
  345. if (idx !== -1) {
  346. this._postProcesses[idx] = null;
  347. }
  348. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  349. }
  350. public getWorldMatrix(): Matrix {
  351. if (!this._worldMatrix) {
  352. this._worldMatrix = Matrix.Identity();
  353. }
  354. var viewMatrix = this.getViewMatrix();
  355. viewMatrix.invertToRef(this._worldMatrix);
  356. return this._worldMatrix;
  357. }
  358. public _getViewMatrix(): Matrix {
  359. return Matrix.Identity();
  360. }
  361. public getViewMatrix(force?: boolean): Matrix {
  362. if (!force && this._isSynchronizedViewMatrix()) {
  363. return this._computedViewMatrix;
  364. }
  365. this.updateCache();
  366. this._computedViewMatrix = this._getViewMatrix();
  367. this._currentRenderId = this.getScene().getRenderId();
  368. this._childRenderId = this._currentRenderId;
  369. this._refreshFrustumPlanes = true;
  370. if (!this.parent || !this.parent.getWorldMatrix) {
  371. this._globalPosition.copyFrom(this.position);
  372. } else {
  373. if (!this._worldMatrix) {
  374. this._worldMatrix = Matrix.Identity();
  375. }
  376. this._computedViewMatrix.invertToRef(this._worldMatrix);
  377. this._worldMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._computedViewMatrix);
  378. this._globalPosition.copyFromFloats(this._computedViewMatrix.m[12], this._computedViewMatrix.m[13], this._computedViewMatrix.m[14]);
  379. this._computedViewMatrix.invert();
  380. this._markSyncedWithParent();
  381. }
  382. if (this._cameraRigParams && this._cameraRigParams.vrPreViewMatrix) {
  383. this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
  384. }
  385. this.onViewMatrixChangedObservable.notifyObservers(this);
  386. return this._computedViewMatrix;
  387. }
  388. public freezeProjectionMatrix(projection?: Matrix): void {
  389. this._doNotComputeProjectionMatrix = true;
  390. if (projection !== undefined) {
  391. this._projectionMatrix = projection;
  392. }
  393. };
  394. public unfreezeProjectionMatrix(): void {
  395. this._doNotComputeProjectionMatrix = false;
  396. };
  397. public getProjectionMatrix(force?: boolean): Matrix {
  398. if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
  399. return this._projectionMatrix;
  400. }
  401. // Cache
  402. this._cache.mode = this.mode;
  403. this._cache.minZ = this.minZ;
  404. this._cache.maxZ = this.maxZ;
  405. // Matrix
  406. this._refreshFrustumPlanes = true;
  407. var engine = this.getEngine();
  408. var scene = this.getScene();
  409. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  410. this._cache.fov = this.fov;
  411. this._cache.fovMode = this.fovMode;
  412. this._cache.aspectRatio = engine.getAspectRatio(this);
  413. if (this.minZ <= 0) {
  414. this.minZ = 0.1;
  415. }
  416. if (scene.useRightHandedSystem) {
  417. Matrix.PerspectiveFovRHToRef(this.fov,
  418. engine.getAspectRatio(this),
  419. this.minZ,
  420. this.maxZ,
  421. this._projectionMatrix,
  422. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  423. } else {
  424. Matrix.PerspectiveFovLHToRef(this.fov,
  425. engine.getAspectRatio(this),
  426. this.minZ,
  427. this.maxZ,
  428. this._projectionMatrix,
  429. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  430. }
  431. } else {
  432. var halfWidth = engine.getRenderWidth() / 2.0;
  433. var halfHeight = engine.getRenderHeight() / 2.0;
  434. if (scene.useRightHandedSystem) {
  435. Matrix.OrthoOffCenterRHToRef(this.orthoLeft || -halfWidth,
  436. this.orthoRight || halfWidth,
  437. this.orthoBottom || -halfHeight,
  438. this.orthoTop || halfHeight,
  439. this.minZ,
  440. this.maxZ,
  441. this._projectionMatrix);
  442. } else {
  443. Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth,
  444. this.orthoRight || halfWidth,
  445. this.orthoBottom || -halfHeight,
  446. this.orthoTop || halfHeight,
  447. this.minZ,
  448. this.maxZ,
  449. this._projectionMatrix);
  450. }
  451. this._cache.orthoLeft = this.orthoLeft;
  452. this._cache.orthoRight = this.orthoRight;
  453. this._cache.orthoBottom = this.orthoBottom;
  454. this._cache.orthoTop = this.orthoTop;
  455. this._cache.renderWidth = engine.getRenderWidth();
  456. this._cache.renderHeight = engine.getRenderHeight();
  457. }
  458. this.onProjectionMatrixChangedObservable.notifyObservers(this);
  459. return this._projectionMatrix;
  460. }
  461. public getTranformationMatrix(): Matrix {
  462. this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  463. return this._transformMatrix;
  464. }
  465. private updateFrustumPlanes(): void {
  466. if (!this._refreshFrustumPlanes) {
  467. return;
  468. }
  469. this.getTranformationMatrix();
  470. if (!this._frustumPlanes) {
  471. this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
  472. } else {
  473. Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
  474. }
  475. this._refreshFrustumPlanes = false;
  476. }
  477. public isInFrustum(target: ICullable): boolean {
  478. this.updateFrustumPlanes();
  479. return target.isInFrustum(this._frustumPlanes);
  480. }
  481. public isCompletelyInFrustum(target: ICullable): boolean {
  482. this.updateFrustumPlanes();
  483. return target.isCompletelyInFrustum(this._frustumPlanes);
  484. }
  485. public getForwardRay(length = 100, transform?: Matrix, origin?: Vector3): Ray {
  486. if (!transform) {
  487. transform = this.getWorldMatrix();
  488. }
  489. if (!origin) {
  490. origin = this.position;
  491. }
  492. var forward = new Vector3(0, 0, 1);
  493. var forwardWorld = Vector3.TransformNormal(forward, transform);
  494. var direction = Vector3.Normalize(forwardWorld);
  495. return new Ray(origin, direction, length);
  496. }
  497. /**
  498. * Releases resources associated with this node.
  499. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  500. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  501. */
  502. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  503. // Observables
  504. this.onViewMatrixChangedObservable.clear();
  505. this.onProjectionMatrixChangedObservable.clear();
  506. this.onAfterCheckInputsObservable.clear();
  507. this.onRestoreStateObservable.clear();
  508. // Inputs
  509. if (this.inputs) {
  510. this.inputs.clear();
  511. }
  512. // Animations
  513. this.getScene().stopAnimation(this);
  514. // Remove from scene
  515. this.getScene().removeCamera(this);
  516. while (this._rigCameras.length > 0) {
  517. let camera = this._rigCameras.pop();
  518. if (camera) {
  519. camera.dispose();
  520. }
  521. }
  522. // Postprocesses
  523. if (this._rigPostProcess) {
  524. this._rigPostProcess.dispose(this);
  525. this._rigPostProcess = null;
  526. this._postProcesses = [];
  527. }
  528. else if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  529. this._rigPostProcess = null;
  530. this._postProcesses = [];
  531. } else {
  532. var i = this._postProcesses.length;
  533. while (--i >= 0) {
  534. var postProcess = this._postProcesses[i]
  535. if(postProcess){
  536. postProcess.dispose(this);
  537. }
  538. }
  539. }
  540. // Render targets
  541. var i = this.customRenderTargets.length;
  542. while (--i >= 0) {
  543. this.customRenderTargets[i].dispose();
  544. }
  545. this.customRenderTargets = [];
  546. // Active Meshes
  547. this._activeMeshes.dispose();
  548. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  549. }
  550. // ---- Camera rigs section ----
  551. public get leftCamera(): Nullable<FreeCamera> {
  552. if (this._rigCameras.length < 1) {
  553. return null;
  554. }
  555. return (<FreeCamera>this._rigCameras[0]);
  556. }
  557. public get rightCamera(): Nullable<FreeCamera> {
  558. if (this._rigCameras.length < 2) {
  559. return null;
  560. }
  561. return (<FreeCamera>this._rigCameras[1]);
  562. }
  563. public getLeftTarget(): Nullable<Vector3> {
  564. if (this._rigCameras.length < 1) {
  565. return null;
  566. }
  567. return (<TargetCamera>this._rigCameras[0]).getTarget();
  568. }
  569. public getRightTarget(): Nullable<Vector3> {
  570. if (this._rigCameras.length < 2) {
  571. return null;
  572. }
  573. return (<TargetCamera>this._rigCameras[1]).getTarget();
  574. }
  575. public setCameraRigMode(mode: number, rigParams: any): void {
  576. if (this.cameraRigMode === mode) {
  577. return;
  578. }
  579. while (this._rigCameras.length > 0) {
  580. let camera = this._rigCameras.pop();
  581. if (camera) {
  582. camera.dispose();
  583. }
  584. }
  585. this.cameraRigMode = mode;
  586. this._cameraRigParams = {};
  587. //we have to implement stereo camera calcultating left and right viewpoints from interaxialDistance and target,
  588. //not from a given angle as it is now, but until that complete code rewriting provisional stereoHalfAngle value is introduced
  589. this._cameraRigParams.interaxialDistance = rigParams.interaxialDistance || 0.0637;
  590. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(this._cameraRigParams.interaxialDistance / 0.0637);
  591. // create the rig cameras, unless none
  592. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  593. let leftCamera = this.createRigCamera(this.name + "_L", 0);
  594. let rightCamera = this.createRigCamera(this.name + "_R", 1);
  595. if (leftCamera && rightCamera) {
  596. this._rigCameras.push(leftCamera);
  597. this._rigCameras.push(rightCamera);
  598. }
  599. }
  600. switch (this.cameraRigMode) {
  601. case Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH:
  602. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  603. this._rigCameras[1]._rigPostProcess = new AnaglyphPostProcess(this.name + "_anaglyph", 1.0, this._rigCameras);
  604. break;
  605. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:
  606. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
  607. case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
  608. var isStereoscopicHoriz = this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL || this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
  609. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  610. this._rigCameras[1]._rigPostProcess = new StereoscopicInterlacePostProcess(this.name + "_stereoInterlace", this._rigCameras, isStereoscopicHoriz);
  611. break;
  612. case Camera.RIG_MODE_VR:
  613. var metrics = rigParams.vrCameraMetrics || VRCameraMetrics.GetDefault();
  614. this._rigCameras[0]._cameraRigParams.vrMetrics = metrics;
  615. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  616. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  617. this._rigCameras[0]._cameraRigParams.vrHMatrix = metrics.leftHMatrix;
  618. this._rigCameras[0]._cameraRigParams.vrPreViewMatrix = metrics.leftPreViewMatrix;
  619. this._rigCameras[0].getProjectionMatrix = this._rigCameras[0]._getVRProjectionMatrix;
  620. this._rigCameras[1]._cameraRigParams.vrMetrics = metrics;
  621. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  622. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  623. this._rigCameras[1]._cameraRigParams.vrHMatrix = metrics.rightHMatrix;
  624. this._rigCameras[1]._cameraRigParams.vrPreViewMatrix = metrics.rightPreViewMatrix;
  625. this._rigCameras[1].getProjectionMatrix = this._rigCameras[1]._getVRProjectionMatrix;
  626. if (metrics.compensateDistortion) {
  627. this._rigCameras[0]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Left", this._rigCameras[0], false, metrics);
  628. this._rigCameras[1]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Right", this._rigCameras[1], true, metrics);
  629. }
  630. break;
  631. case Camera.RIG_MODE_WEBVR:
  632. if (rigParams.vrDisplay) {
  633. var leftEye = rigParams.vrDisplay.getEyeParameters('left');
  634. var rightEye = rigParams.vrDisplay.getEyeParameters('right');
  635. //Left eye
  636. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  637. this._rigCameras[0].setCameraRigParameter("left", true);
  638. //leaving this for future reference
  639. this._rigCameras[0].setCameraRigParameter("specs", rigParams.specs);
  640. this._rigCameras[0].setCameraRigParameter("eyeParameters", leftEye);
  641. this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
  642. this._rigCameras[0].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  643. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  644. this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
  645. this._rigCameras[0].parent = this;
  646. this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
  647. //Right eye
  648. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  649. this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
  650. this._rigCameras[1].setCameraRigParameter("specs", rigParams.specs);
  651. this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
  652. this._rigCameras[1].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  653. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  654. this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
  655. this._rigCameras[1].parent = this;
  656. this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
  657. if (Camera.UseAlternateWebVRRendering) {
  658. this._rigCameras[1]._skipRendering = true;
  659. this._rigCameras[0]._alternateCamera = this._rigCameras[1];
  660. }
  661. }
  662. break;
  663. }
  664. this._cascadePostProcessesToRigCams();
  665. this.update();
  666. }
  667. private _getVRProjectionMatrix(): Matrix {
  668. Matrix.PerspectiveFovLHToRef(this._cameraRigParams.vrMetrics.aspectRatioFov, this._cameraRigParams.vrMetrics.aspectRatio, this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
  669. this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix, this._projectionMatrix);
  670. return this._projectionMatrix;
  671. }
  672. protected _updateCameraRotationMatrix() {
  673. //Here for WebVR
  674. }
  675. protected _updateWebVRCameraRotationMatrix() {
  676. //Here for WebVR
  677. }
  678. /**
  679. * This function MUST be overwritten by the different WebVR cameras available.
  680. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  681. */
  682. protected _getWebVRProjectionMatrix(): Matrix {
  683. return Matrix.Identity();
  684. }
  685. /**
  686. * This function MUST be overwritten by the different WebVR cameras available.
  687. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  688. */
  689. protected _getWebVRViewMatrix(): Matrix {
  690. return Matrix.Identity();
  691. }
  692. public setCameraRigParameter(name: string, value: any) {
  693. if (!this._cameraRigParams) {
  694. this._cameraRigParams = {};
  695. }
  696. this._cameraRigParams[name] = value;
  697. //provisionnally:
  698. if (name === "interaxialDistance") {
  699. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(value / 0.0637);
  700. }
  701. }
  702. /**
  703. * needs to be overridden by children so sub has required properties to be copied
  704. */
  705. public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
  706. return null;
  707. }
  708. /**
  709. * May need to be overridden by children
  710. */
  711. public _updateRigCameras() {
  712. for (var i = 0; i < this._rigCameras.length; i++) {
  713. this._rigCameras[i].minZ = this.minZ;
  714. this._rigCameras[i].maxZ = this.maxZ;
  715. this._rigCameras[i].fov = this.fov;
  716. }
  717. // only update viewport when ANAGLYPH
  718. if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
  719. this._rigCameras[0].viewport = this._rigCameras[1].viewport = this.viewport;
  720. }
  721. }
  722. public _setupInputs() {
  723. }
  724. public serialize(): any {
  725. var serializationObject = SerializationHelper.Serialize(this);
  726. // Type
  727. serializationObject.type = this.getClassName();
  728. // Parent
  729. if (this.parent) {
  730. serializationObject.parentId = this.parent.id;
  731. }
  732. if (this.inputs) {
  733. this.inputs.serialize(serializationObject);
  734. }
  735. // Animations
  736. Animation.AppendSerializedAnimations(this, serializationObject);
  737. serializationObject.ranges = this.serializeAnimationRanges();
  738. return serializationObject;
  739. }
  740. public clone(name: string): Camera {
  741. return SerializationHelper.Clone(Camera.GetConstructorFromName(this.getClassName(), name, this.getScene(), this.interaxialDistance, this.isStereoscopicSideBySide), this);
  742. }
  743. public getDirection(localAxis: Vector3): Vector3 {
  744. var result = Vector3.Zero();
  745. this.getDirectionToRef(localAxis, result);
  746. return result;
  747. }
  748. public getDirectionToRef(localAxis: Vector3, result: Vector3): void {
  749. Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
  750. }
  751. static GetConstructorFromName(type: string, name: string, scene: Scene, interaxial_distance: number = 0, isStereoscopicSideBySide: boolean = true): () => Camera {
  752. switch (type) {
  753. case "ArcRotateCamera":
  754. return () => new ArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), scene);
  755. case "DeviceOrientationCamera":
  756. return () => new DeviceOrientationCamera(name, Vector3.Zero(), scene);
  757. case "FollowCamera":
  758. return () => new FollowCamera(name, Vector3.Zero(), scene);
  759. case "ArcFollowCamera":
  760. return () => new ArcFollowCamera(name, 0, 0, 1.0, null, scene);
  761. case "GamepadCamera":
  762. return () => new GamepadCamera(name, Vector3.Zero(), scene);
  763. case "TouchCamera":
  764. return () => new TouchCamera(name, Vector3.Zero(), scene);
  765. case "VirtualJoysticksCamera":
  766. return () => new VirtualJoysticksCamera(name, Vector3.Zero(), scene);
  767. case "WebVRFreeCamera":
  768. return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
  769. case "WebVRGamepadCamera":
  770. return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
  771. case "VRDeviceOrientationFreeCamera":
  772. return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
  773. case "VRDeviceOrientationGamepadCamera":
  774. return () => new VRDeviceOrientationGamepadCamera(name, Vector3.Zero(), scene);
  775. case "AnaglyphArcRotateCamera":
  776. return () => new AnaglyphArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), interaxial_distance, scene);
  777. case "AnaglyphFreeCamera":
  778. return () => new AnaglyphFreeCamera(name, Vector3.Zero(), interaxial_distance, scene);
  779. case "AnaglyphGamepadCamera":
  780. return () => new AnaglyphGamepadCamera(name, Vector3.Zero(), interaxial_distance, scene);
  781. case "AnaglyphUniversalCamera":
  782. return () => new AnaglyphUniversalCamera(name, Vector3.Zero(), interaxial_distance, scene);
  783. case "StereoscopicArcRotateCamera":
  784. return () => new StereoscopicArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  785. case "StereoscopicFreeCamera":
  786. return () => new StereoscopicFreeCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  787. case "StereoscopicGamepadCamera":
  788. return () => new StereoscopicGamepadCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  789. case "StereoscopicUniversalCamera":
  790. return () => new StereoscopicUniversalCamera(name, Vector3.Zero(), interaxial_distance, isStereoscopicSideBySide, scene);
  791. case "FreeCamera": // Forcing Universal here
  792. return () => new UniversalCamera(name, Vector3.Zero(), scene);
  793. default: // Universal Camera is the default value
  794. return () => new UniversalCamera(name, Vector3.Zero(), scene);
  795. }
  796. }
  797. public computeWorldMatrix(): Matrix {
  798. return this.getWorldMatrix();
  799. }
  800. public static Parse(parsedCamera: any, scene: Scene): Camera {
  801. var type = parsedCamera.type;
  802. var construct = Camera.GetConstructorFromName(type, parsedCamera.name, scene, parsedCamera.interaxial_distance, parsedCamera.isStereoscopicSideBySide);
  803. var camera = SerializationHelper.Parse(construct, parsedCamera, scene);
  804. // Parent
  805. if (parsedCamera.parentId) {
  806. camera._waitingParentId = parsedCamera.parentId;
  807. }
  808. //If camera has an input manager, let it parse inputs settings
  809. if (camera.inputs) {
  810. camera.inputs.parse(parsedCamera);
  811. camera._setupInputs();
  812. }
  813. if ((<any>camera).setPosition) { // need to force position
  814. camera.position.copyFromFloats(0, 0, 0);
  815. (<any>camera).setPosition(Vector3.FromArray(parsedCamera.position));
  816. }
  817. // Target
  818. if (parsedCamera.target) {
  819. if ((<any>camera).setTarget) {
  820. (<any>camera).setTarget(Vector3.FromArray(parsedCamera.target));
  821. }
  822. }
  823. // Apply 3d rig, when found
  824. if (parsedCamera.cameraRigMode) {
  825. var rigParams = (parsedCamera.interaxial_distance) ? { interaxialDistance: parsedCamera.interaxial_distance } : {};
  826. camera.setCameraRigMode(parsedCamera.cameraRigMode, rigParams);
  827. }
  828. // Animations
  829. if (parsedCamera.animations) {
  830. for (var animationIndex = 0; animationIndex < parsedCamera.animations.length; animationIndex++) {
  831. var parsedAnimation = parsedCamera.animations[animationIndex];
  832. camera.animations.push(Animation.Parse(parsedAnimation));
  833. }
  834. Node.ParseAnimationRanges(camera, parsedCamera, scene);
  835. }
  836. if (parsedCamera.autoAnimate) {
  837. scene.beginAnimation(camera, parsedCamera.autoAnimateFrom, parsedCamera.autoAnimateTo, parsedCamera.autoAnimateLoop, parsedCamera.autoAnimateSpeed || 1.0);
  838. }
  839. return camera;
  840. }
  841. }
  842. }