babylon.camera.ts 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. module BABYLON {
  2. /**
  3. * This is the base class of all the camera used in the application.
  4. * @see http://doc.babylonjs.com/features/cameras
  5. */
  6. export class Camera extends Node {
  7. /**
  8. * This is the default projection mode used by the cameras.
  9. * It helps recreating a feeling of perspective and better appreciate depth.
  10. * This is the best way to simulate real life cameras.
  11. */
  12. public static readonly PERSPECTIVE_CAMERA = 0;
  13. /**
  14. * This helps creating camera with an orthographic mode.
  15. * Orthographic is commonly used in engineering as a means to produce object specifications that communicate dimensions unambiguously, each line of 1 unit length (cm, meter..whatever) will appear to have the same length everywhere on the drawing. This allows the drafter to dimension only a subset of lines and let the reader know that other lines of that length on the drawing are also that length in reality. Every parallel line in the drawing is also parallel in the object.
  16. */
  17. public static readonly ORTHOGRAPHIC_CAMERA = 1;
  18. /**
  19. * This is the default FOV mode for perspective cameras.
  20. * This setting aligns the upper and lower bounds of the viewport to the upper and lower bounds of the camera frustum.
  21. */
  22. public static readonly FOVMODE_VERTICAL_FIXED = 0;
  23. /**
  24. * This setting aligns the left and right bounds of the viewport to the left and right bounds of the camera frustum.
  25. */
  26. public static readonly FOVMODE_HORIZONTAL_FIXED = 1;
  27. /**
  28. * This specifies ther is no need for a camera rig.
  29. * Basically only one eye is rendered corresponding to the camera.
  30. */
  31. public static readonly RIG_MODE_NONE = 0;
  32. /**
  33. * Simulates a camera Rig with one blue eye and one red eye.
  34. * This can be use with 3d blue and red glasses.
  35. */
  36. public static readonly RIG_MODE_STEREOSCOPIC_ANAGLYPH = 10;
  37. /**
  38. * Defines that both eyes of the camera will be rendered side by side with a parallel target.
  39. */
  40. public static readonly RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL = 11;
  41. /**
  42. * Defines that both eyes of the camera will be rendered side by side with a none parallel target.
  43. */
  44. public static readonly RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED = 12;
  45. /**
  46. * Defines that both eyes of the camera will be rendered over under each other.
  47. */
  48. public static readonly RIG_MODE_STEREOSCOPIC_OVERUNDER = 13;
  49. /**
  50. * Defines that both eyes of the camera should be renderered in a VR mode (carbox).
  51. */
  52. public static readonly RIG_MODE_VR = 20;
  53. /**
  54. * Defines that both eyes of the camera should be renderered in a VR mode (webVR).
  55. */
  56. public static readonly RIG_MODE_WEBVR = 21;
  57. /**
  58. * Custom rig mode allowing rig cameras to be populated manually with any number of cameras
  59. */
  60. public static readonly RIG_MODE_CUSTOM = 22;
  61. /**
  62. * Defines if by default attaching controls should prevent the default javascript event to continue.
  63. */
  64. public static ForceAttachControlToAlwaysPreventDefault = false;
  65. /**
  66. * @hidden
  67. * Might be removed once multiview will be a thing
  68. */
  69. public static UseAlternateWebVRRendering = false;
  70. /**
  71. * Define the input manager associated with the camera.
  72. */
  73. public inputs: CameraInputsManager<Camera>;
  74. /**
  75. * Define the current local position of the camera in the scene
  76. */
  77. @serializeAsVector3()
  78. public position: Vector3;
  79. /**
  80. * The vector the camera should consider as up.
  81. * (default is Vector3(0, 1, 0) aka Vector3.Up())
  82. */
  83. @serializeAsVector3()
  84. public upVector = Vector3.Up();
  85. /**
  86. * Define the current limit on the left side for an orthographic camera
  87. * In scene unit
  88. */
  89. @serialize()
  90. public orthoLeft: Nullable<number> = null;
  91. /**
  92. * Define the current limit on the right side for an orthographic camera
  93. * In scene unit
  94. */
  95. @serialize()
  96. public orthoRight: Nullable<number> = null;
  97. /**
  98. * Define the current limit on the bottom side for an orthographic camera
  99. * In scene unit
  100. */
  101. @serialize()
  102. public orthoBottom: Nullable<number> = null;
  103. /**
  104. * Define the current limit on the top side for an orthographic camera
  105. * In scene unit
  106. */
  107. @serialize()
  108. public orthoTop: Nullable<number> = null;
  109. /**
  110. * Field Of View is set in Radians. (default is 0.8)
  111. */
  112. @serialize()
  113. public fov = 0.8;
  114. /**
  115. * Define the minimum distance the camera can see from.
  116. * This is important to note that the depth buffer are not infinite and the closer it starts
  117. * the more your scene might encounter depth fighting issue.
  118. */
  119. @serialize()
  120. public minZ = 1;
  121. /**
  122. * Define the maximum distance the camera can see to.
  123. * This is important to note that the depth buffer are not infinite and the further it end
  124. * the more your scene might encounter depth fighting issue.
  125. */
  126. @serialize()
  127. public maxZ = 10000.0;
  128. /**
  129. * Define the default inertia of the camera.
  130. * This helps giving a smooth feeling to the camera movement.
  131. */
  132. @serialize()
  133. public inertia = 0.9;
  134. /**
  135. * Define the mode of the camera (Camera.PERSPECTIVE_CAMERA or Camera.PERSPECTIVE_ORTHOGRAPHIC)
  136. */
  137. @serialize()
  138. public mode = Camera.PERSPECTIVE_CAMERA;
  139. /**
  140. * Define wether the camera is intermediate.
  141. * This is usefull to not present the output directly to the screen in case of rig without post process for instance
  142. */
  143. public isIntermediate = false;
  144. /**
  145. * Define the viewport of the camera.
  146. * This correspond to the portion of the screen the camera will render to in normalized 0 to 1 unit.
  147. */
  148. public viewport = new Viewport(0, 0, 1.0, 1.0);
  149. /**
  150. * Restricts the camera to viewing objects with the same layerMask.
  151. * A camera with a layerMask of 1 will render mesh.layerMask & camera.layerMask!== 0
  152. */
  153. @serialize()
  154. public layerMask: number = 0x0FFFFFFF;
  155. /**
  156. * fovMode sets the camera frustum bounds to the viewport bounds. (default is FOVMODE_VERTICAL_FIXED)
  157. */
  158. @serialize()
  159. public fovMode: number = Camera.FOVMODE_VERTICAL_FIXED;
  160. /**
  161. * Rig mode of the camera.
  162. * This is usefull to create the camera with two "eyes" instead of one to create VR or stereoscopic scenes.
  163. * This is normally controlled byt the camera themselves as internal use.
  164. */
  165. @serialize()
  166. public cameraRigMode = Camera.RIG_MODE_NONE;
  167. /**
  168. * Defines the distance between both "eyes" in case of a RIG
  169. */
  170. @serialize()
  171. public interaxialDistance: number;
  172. /**
  173. * Defines if stereoscopic rendering is done side by side or over under.
  174. */
  175. @serialize()
  176. public isStereoscopicSideBySide: boolean;
  177. /**
  178. * Defines the list of custom render target which are rendered to and then used as the input to this camera's render. Eg. display another camera view on a TV in the main scene
  179. * This is pretty helpfull if you wish to make a camera render to a texture you could reuse somewhere
  180. * else in the scene.
  181. */
  182. public customRenderTargets = new Array<RenderTargetTexture>();
  183. /**
  184. * When set, the camera will render to this render target instead of the default canvas
  185. */
  186. public outputRenderTarget: Nullable<RenderTargetTexture> = null;
  187. /**
  188. * Observable triggered when the camera view matrix has changed.
  189. */
  190. public onViewMatrixChangedObservable = new Observable<Camera>();
  191. /**
  192. * Observable triggered when the camera Projection matrix has changed.
  193. */
  194. public onProjectionMatrixChangedObservable = new Observable<Camera>();
  195. /**
  196. * Observable triggered when the inputs have been processed.
  197. */
  198. public onAfterCheckInputsObservable = new Observable<Camera>();
  199. /**
  200. * Observable triggered when reset has been called and applied to the camera.
  201. */
  202. public onRestoreStateObservable = new Observable<Camera>();
  203. /** @hidden */
  204. public _cameraRigParams: any;
  205. /** @hidden */
  206. public _rigCameras = new Array<Camera>();
  207. /** @hidden */
  208. public _rigPostProcess: Nullable<PostProcess>;
  209. protected _webvrViewMatrix = Matrix.Identity();
  210. /** @hidden */
  211. public _skipRendering = false;
  212. /** @hidden */
  213. public _alternateCamera: Camera;
  214. /** @hidden */
  215. public _projectionMatrix = new Matrix();
  216. /** @hidden */
  217. public _postProcesses = new Array<Nullable<PostProcess>>();
  218. /** @hidden */
  219. public _activeMeshes = new SmartArray<AbstractMesh>(256);
  220. protected _globalPosition = Vector3.Zero();
  221. /** hidden */
  222. public _computedViewMatrix = Matrix.Identity();
  223. private _doNotComputeProjectionMatrix = false;
  224. private _transformMatrix = Matrix.Zero();
  225. private _frustumPlanes: Plane[];
  226. private _refreshFrustumPlanes = true;
  227. private _storedFov: number;
  228. private _stateStored: boolean;
  229. /**
  230. * Instantiates a new camera object.
  231. * This should not be used directly but through the inherited cameras: ArcRotate, Free...
  232. * @see http://doc.babylonjs.com/features/cameras
  233. * @param name Defines the name of the camera in the scene
  234. * @param position Defines the position of the camera
  235. * @param scene Defines the scene the camera belongs too
  236. * @param setActiveOnSceneIfNoneActive Defines if the camera should be set as active after creation if no other camera have been defined in the scene
  237. */
  238. constructor(name: string, position: Vector3, scene: Scene, setActiveOnSceneIfNoneActive = true) {
  239. super(name, scene);
  240. this.getScene().addCamera(this);
  241. if (setActiveOnSceneIfNoneActive && !this.getScene().activeCamera) {
  242. this.getScene().activeCamera = this;
  243. }
  244. this.position = position;
  245. }
  246. /**
  247. * Store current camera state (fov, position, etc..)
  248. * @returns the camera
  249. */
  250. public storeState(): Camera {
  251. this._stateStored = true;
  252. this._storedFov = this.fov;
  253. return this;
  254. }
  255. /**
  256. * Restores the camera state values if it has been stored. You must call storeState() first
  257. */
  258. protected _restoreStateValues(): boolean {
  259. if (!this._stateStored) {
  260. return false;
  261. }
  262. this.fov = this._storedFov;
  263. return true;
  264. }
  265. /**
  266. * Restored camera state. You must call storeState() first.
  267. * @returns true if restored and false otherwise
  268. */
  269. public restoreState(): boolean {
  270. if (this._restoreStateValues()) {
  271. this.onRestoreStateObservable.notifyObservers(this);
  272. return true;
  273. }
  274. return false;
  275. }
  276. /**
  277. * Gets the class name of the camera.
  278. * @returns the class name
  279. */
  280. public getClassName(): string {
  281. return "Camera";
  282. }
  283. /**
  284. * Gets a string representation of the camera usefull for debug purpose.
  285. * @param fullDetails Defines that a more verboe level of logging is required
  286. * @returns the string representation
  287. */
  288. public toString(fullDetails?: boolean): string {
  289. var ret = "Name: " + this.name;
  290. ret += ", type: " + this.getClassName();
  291. if (this.animations) {
  292. for (var i = 0; i < this.animations.length; i++) {
  293. ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
  294. }
  295. }
  296. if (fullDetails) {
  297. }
  298. return ret;
  299. }
  300. /**
  301. * Gets the current world space position of the camera.
  302. */
  303. public get globalPosition(): Vector3 {
  304. return this._globalPosition;
  305. }
  306. /**
  307. * Gets the list of active meshes this frame (meshes no culled or excluded by lod s in the frame)
  308. * @returns the active meshe list
  309. */
  310. public getActiveMeshes(): SmartArray<AbstractMesh> {
  311. return this._activeMeshes;
  312. }
  313. /**
  314. * Check wether a mesh is part of the current active mesh list of the camera
  315. * @param mesh Defines the mesh to check
  316. * @returns true if active, false otherwise
  317. */
  318. public isActiveMesh(mesh: Mesh): boolean {
  319. return (this._activeMeshes.indexOf(mesh) !== -1);
  320. }
  321. /**
  322. * Is this camera ready to be used/rendered
  323. * @param completeCheck defines if a complete check (including post processes) has to be done (false by default)
  324. * @return true if the camera is ready
  325. */
  326. public isReady(completeCheck = false): boolean {
  327. if (completeCheck) {
  328. for (var pp of this._postProcesses) {
  329. if (pp && !pp.isReady()) {
  330. return false;
  331. }
  332. }
  333. }
  334. return super.isReady(completeCheck);
  335. }
  336. /** @hidden */
  337. public _initCache() {
  338. super._initCache();
  339. this._cache.position = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  340. this._cache.upVector = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  341. this._cache.mode = undefined;
  342. this._cache.minZ = undefined;
  343. this._cache.maxZ = undefined;
  344. this._cache.fov = undefined;
  345. this._cache.fovMode = undefined;
  346. this._cache.aspectRatio = undefined;
  347. this._cache.orthoLeft = undefined;
  348. this._cache.orthoRight = undefined;
  349. this._cache.orthoBottom = undefined;
  350. this._cache.orthoTop = undefined;
  351. this._cache.renderWidth = undefined;
  352. this._cache.renderHeight = undefined;
  353. }
  354. /** @hidden */
  355. public _updateCache(ignoreParentClass?: boolean): void {
  356. if (!ignoreParentClass) {
  357. super._updateCache();
  358. }
  359. this._cache.position.copyFrom(this.position);
  360. this._cache.upVector.copyFrom(this.upVector);
  361. }
  362. /** @hidden */
  363. public _isSynchronized(): boolean {
  364. return this._isSynchronizedViewMatrix() && this._isSynchronizedProjectionMatrix();
  365. }
  366. /** @hidden */
  367. public _isSynchronizedViewMatrix(): boolean {
  368. if (!super._isSynchronized()) {
  369. return false;
  370. }
  371. return this._cache.position.equals(this.position)
  372. && this._cache.upVector.equals(this.upVector)
  373. && this.isSynchronizedWithParent();
  374. }
  375. /** @hidden */
  376. public _isSynchronizedProjectionMatrix(): boolean {
  377. var check = this._cache.mode === this.mode
  378. && this._cache.minZ === this.minZ
  379. && this._cache.maxZ === this.maxZ;
  380. if (!check) {
  381. return false;
  382. }
  383. var engine = this.getEngine();
  384. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  385. check = this._cache.fov === this.fov
  386. && this._cache.fovMode === this.fovMode
  387. && this._cache.aspectRatio === engine.getAspectRatio(this);
  388. }
  389. else {
  390. check = this._cache.orthoLeft === this.orthoLeft
  391. && this._cache.orthoRight === this.orthoRight
  392. && this._cache.orthoBottom === this.orthoBottom
  393. && this._cache.orthoTop === this.orthoTop
  394. && this._cache.renderWidth === engine.getRenderWidth()
  395. && this._cache.renderHeight === engine.getRenderHeight();
  396. }
  397. return check;
  398. }
  399. /**
  400. * Attach the input controls to a specific dom element to get the input from.
  401. * @param element Defines the element the controls should be listened from
  402. * @param noPreventDefault Defines whether event caught by the controls should call preventdefault() (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  403. */
  404. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  405. }
  406. /**
  407. * Detach the current controls from the specified dom element.
  408. * @param element Defines the element to stop listening the inputs from
  409. */
  410. public detachControl(element: HTMLElement): void {
  411. }
  412. /**
  413. * Update the camera state according to the different inputs gathered during the frame.
  414. */
  415. public update(): void {
  416. this._checkInputs();
  417. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  418. this._updateRigCameras();
  419. }
  420. }
  421. /** @hidden */
  422. public _checkInputs(): void {
  423. this.onAfterCheckInputsObservable.notifyObservers(this);
  424. }
  425. /** @hidden */
  426. public get rigCameras(): Camera[] {
  427. return this._rigCameras;
  428. }
  429. /**
  430. * Gets the post process used by the rig cameras
  431. */
  432. public get rigPostProcess(): Nullable<PostProcess> {
  433. return this._rigPostProcess;
  434. }
  435. /**
  436. * Internal, gets the first post proces.
  437. * @returns the first post process to be run on this camera.
  438. */
  439. public _getFirstPostProcess(): Nullable<PostProcess> {
  440. for (var ppIndex = 0; ppIndex < this._postProcesses.length; ppIndex++) {
  441. if (this._postProcesses[ppIndex] !== null) {
  442. return this._postProcesses[ppIndex];
  443. }
  444. }
  445. return null;
  446. }
  447. private _cascadePostProcessesToRigCams(): void {
  448. // invalidate framebuffer
  449. var firstPostProcess = this._getFirstPostProcess();
  450. if (firstPostProcess) {
  451. firstPostProcess.markTextureDirty();
  452. }
  453. // glue the rigPostProcess to the end of the user postprocesses & assign to each sub-camera
  454. for (var i = 0, len = this._rigCameras.length; i < len; i++) {
  455. var cam = this._rigCameras[i];
  456. var rigPostProcess = cam._rigPostProcess;
  457. // for VR rig, there does not have to be a post process
  458. if (rigPostProcess) {
  459. var isPass = rigPostProcess instanceof PassPostProcess;
  460. if (isPass) {
  461. // any rig which has a PassPostProcess for rig[0], cannot be isIntermediate when there are also user postProcesses
  462. cam.isIntermediate = this._postProcesses.length === 0;
  463. }
  464. cam._postProcesses = this._postProcesses.slice(0).concat(rigPostProcess);
  465. rigPostProcess.markTextureDirty();
  466. } else {
  467. cam._postProcesses = this._postProcesses.slice(0);
  468. }
  469. }
  470. }
  471. /**
  472. * Attach a post process to the camera.
  473. * @see http://doc.babylonjs.com/how_to/how_to_use_postprocesses#attach-postprocess
  474. * @param postProcess The post process to attach to the camera
  475. * @param insertAt The position of the post process in case several of them are in use in the scene
  476. * @returns the position the post process has been inserted at
  477. */
  478. public attachPostProcess(postProcess: PostProcess, insertAt: Nullable<number> = null): number {
  479. if (!postProcess.isReusable() && this._postProcesses.indexOf(postProcess) > -1) {
  480. Tools.Error("You're trying to reuse a post process not defined as reusable.");
  481. return 0;
  482. }
  483. if (insertAt == null || insertAt < 0) {
  484. this._postProcesses.push(postProcess);
  485. } else if (this._postProcesses[insertAt] === null) {
  486. this._postProcesses[insertAt] = postProcess;
  487. } else {
  488. this._postProcesses.splice(insertAt, 0, postProcess);
  489. }
  490. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  491. return this._postProcesses.indexOf(postProcess);
  492. }
  493. /**
  494. * Detach a post process to the camera.
  495. * @see http://doc.babylonjs.com/how_to/how_to_use_postprocesses#attach-postprocess
  496. * @param postProcess The post process to detach from the camera
  497. */
  498. public detachPostProcess(postProcess: PostProcess): void {
  499. var idx = this._postProcesses.indexOf(postProcess);
  500. if (idx !== -1) {
  501. this._postProcesses[idx] = null;
  502. }
  503. this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
  504. }
  505. /**
  506. * Gets the current world matrix of the camera
  507. */
  508. public getWorldMatrix(): Matrix {
  509. if (this._isSynchronizedViewMatrix()) {
  510. return this._worldMatrix;
  511. }
  512. // Getting the the view matrix will also compute the world matrix.
  513. this.getViewMatrix();
  514. return this._worldMatrix;
  515. }
  516. /** @hidden */
  517. protected _getViewMatrix(): Matrix {
  518. return Matrix.Identity();
  519. }
  520. /**
  521. * Gets the current view matrix of the camera.
  522. * @param force forces the camera to recompute the matrix without looking at the cached state
  523. * @returns the view matrix
  524. */
  525. public getViewMatrix(force?: boolean): Matrix {
  526. if (!force && this._isSynchronizedViewMatrix()) {
  527. return this._computedViewMatrix;
  528. }
  529. this.updateCache();
  530. this._computedViewMatrix = this._getViewMatrix();
  531. this._currentRenderId = this.getScene().getRenderId();
  532. this._childRenderId = this._currentRenderId;
  533. this._refreshFrustumPlanes = true;
  534. if (this._cameraRigParams && this._cameraRigParams.vrPreViewMatrix) {
  535. this._computedViewMatrix.multiplyToRef(this._cameraRigParams.vrPreViewMatrix, this._computedViewMatrix);
  536. }
  537. this.onViewMatrixChangedObservable.notifyObservers(this);
  538. this._computedViewMatrix.invertToRef(this._worldMatrix);
  539. return this._computedViewMatrix;
  540. }
  541. /**
  542. * Freeze the projection matrix.
  543. * It will prevent the cache check of the camera projection compute and can speed up perf
  544. * if no parameter of the camera are meant to change
  545. * @param projection Defines manually a projection if necessary
  546. */
  547. public freezeProjectionMatrix(projection?: Matrix): void {
  548. this._doNotComputeProjectionMatrix = true;
  549. if (projection !== undefined) {
  550. this._projectionMatrix = projection;
  551. }
  552. }
  553. /**
  554. * Unfreeze the projection matrix if it has previously been freezed by freezeProjectionMatrix.
  555. */
  556. public unfreezeProjectionMatrix(): void {
  557. this._doNotComputeProjectionMatrix = false;
  558. }
  559. /**
  560. * Gets the current projection matrix of the camera.
  561. * @param force forces the camera to recompute the matrix without looking at the cached state
  562. * @returns the projection matrix
  563. */
  564. public getProjectionMatrix(force?: boolean): Matrix {
  565. if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
  566. return this._projectionMatrix;
  567. }
  568. // Cache
  569. this._cache.mode = this.mode;
  570. this._cache.minZ = this.minZ;
  571. this._cache.maxZ = this.maxZ;
  572. // Matrix
  573. this._refreshFrustumPlanes = true;
  574. var engine = this.getEngine();
  575. var scene = this.getScene();
  576. if (this.mode === Camera.PERSPECTIVE_CAMERA) {
  577. this._cache.fov = this.fov;
  578. this._cache.fovMode = this.fovMode;
  579. this._cache.aspectRatio = engine.getAspectRatio(this);
  580. if (this.minZ <= 0) {
  581. this.minZ = 0.1;
  582. }
  583. if (scene.useRightHandedSystem) {
  584. Matrix.PerspectiveFovRHToRef(this.fov,
  585. engine.getAspectRatio(this),
  586. this.minZ,
  587. this.maxZ,
  588. this._projectionMatrix,
  589. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  590. } else {
  591. Matrix.PerspectiveFovLHToRef(this.fov,
  592. engine.getAspectRatio(this),
  593. this.minZ,
  594. this.maxZ,
  595. this._projectionMatrix,
  596. this.fovMode === Camera.FOVMODE_VERTICAL_FIXED);
  597. }
  598. } else {
  599. var halfWidth = engine.getRenderWidth() / 2.0;
  600. var halfHeight = engine.getRenderHeight() / 2.0;
  601. if (scene.useRightHandedSystem) {
  602. Matrix.OrthoOffCenterRHToRef(this.orthoLeft || -halfWidth,
  603. this.orthoRight || halfWidth,
  604. this.orthoBottom || -halfHeight,
  605. this.orthoTop || halfHeight,
  606. this.minZ,
  607. this.maxZ,
  608. this._projectionMatrix);
  609. } else {
  610. Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth,
  611. this.orthoRight || halfWidth,
  612. this.orthoBottom || -halfHeight,
  613. this.orthoTop || halfHeight,
  614. this.minZ,
  615. this.maxZ,
  616. this._projectionMatrix);
  617. }
  618. this._cache.orthoLeft = this.orthoLeft;
  619. this._cache.orthoRight = this.orthoRight;
  620. this._cache.orthoBottom = this.orthoBottom;
  621. this._cache.orthoTop = this.orthoTop;
  622. this._cache.renderWidth = engine.getRenderWidth();
  623. this._cache.renderHeight = engine.getRenderHeight();
  624. }
  625. this.onProjectionMatrixChangedObservable.notifyObservers(this);
  626. return this._projectionMatrix;
  627. }
  628. /**
  629. * Gets the transformation matrix (ie. the multiplication of view by projection matrices)
  630. * @returns a Matrix
  631. */
  632. public getTransformationMatrix(): Matrix {
  633. this._computedViewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
  634. return this._transformMatrix;
  635. }
  636. private _updateFrustumPlanes(): void {
  637. if (!this._refreshFrustumPlanes) {
  638. return;
  639. }
  640. this.getTransformationMatrix();
  641. if (!this._frustumPlanes) {
  642. this._frustumPlanes = Frustum.GetPlanes(this._transformMatrix);
  643. } else {
  644. Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
  645. }
  646. this._refreshFrustumPlanes = false;
  647. }
  648. /**
  649. * Checks if a cullable object (mesh...) is in the camera frustum
  650. * This checks the bounding box center. See isCompletelyInFrustum for a full bounding check
  651. * @param target The object to check
  652. * @returns true if the object is in frustum otherwise false
  653. */
  654. public isInFrustum(target: ICullable): boolean {
  655. this._updateFrustumPlanes();
  656. return target.isInFrustum(this._frustumPlanes);
  657. }
  658. /**
  659. * Checks if a cullable object (mesh...) is in the camera frustum
  660. * Unlike isInFrustum this cheks the full bounding box
  661. * @param target The object to check
  662. * @returns true if the object is in frustum otherwise false
  663. */
  664. public isCompletelyInFrustum(target: ICullable): boolean {
  665. this._updateFrustumPlanes();
  666. return target.isCompletelyInFrustum(this._frustumPlanes);
  667. }
  668. /**
  669. * Gets a ray in the forward direction from the camera.
  670. * @param length Defines the length of the ray to create
  671. * @param transform Defines the transform to apply to the ray, by default the world matrx is used to create a workd space ray
  672. * @param origin Defines the start point of the ray which defaults to the camera position
  673. * @returns the forward ray
  674. */
  675. public getForwardRay(length = 100, transform?: Matrix, origin?: Vector3): Ray {
  676. if (!transform) {
  677. transform = this.getWorldMatrix();
  678. }
  679. if (!origin) {
  680. origin = this.position;
  681. }
  682. var forward = this._scene.useRightHandedSystem ? new Vector3(0, 0, -1) : new Vector3(0, 0, 1);
  683. var forwardWorld = Vector3.TransformNormal(forward, transform);
  684. var direction = Vector3.Normalize(forwardWorld);
  685. return new Ray(origin, direction, length);
  686. }
  687. /**
  688. * Releases resources associated with this node.
  689. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  690. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  691. */
  692. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  693. // Observables
  694. this.onViewMatrixChangedObservable.clear();
  695. this.onProjectionMatrixChangedObservable.clear();
  696. this.onAfterCheckInputsObservable.clear();
  697. this.onRestoreStateObservable.clear();
  698. // Inputs
  699. if (this.inputs) {
  700. this.inputs.clear();
  701. }
  702. // Animations
  703. this.getScene().stopAnimation(this);
  704. // Remove from scene
  705. this.getScene().removeCamera(this);
  706. while (this._rigCameras.length > 0) {
  707. let camera = this._rigCameras.pop();
  708. if (camera) {
  709. camera.dispose();
  710. }
  711. }
  712. // Postprocesses
  713. if (this._rigPostProcess) {
  714. this._rigPostProcess.dispose(this);
  715. this._rigPostProcess = null;
  716. this._postProcesses = [];
  717. }
  718. else if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  719. this._rigPostProcess = null;
  720. this._postProcesses = [];
  721. } else {
  722. var i = this._postProcesses.length;
  723. while (--i >= 0) {
  724. var postProcess = this._postProcesses[i];
  725. if (postProcess) {
  726. postProcess.dispose(this);
  727. }
  728. }
  729. }
  730. // Render targets
  731. var i = this.customRenderTargets.length;
  732. while (--i >= 0) {
  733. this.customRenderTargets[i].dispose();
  734. }
  735. this.customRenderTargets = [];
  736. // Active Meshes
  737. this._activeMeshes.dispose();
  738. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  739. }
  740. /**
  741. * Gets the left camera of a rig setup in case of Rigged Camera
  742. */
  743. public get leftCamera(): Nullable<FreeCamera> {
  744. if (this._rigCameras.length < 1) {
  745. return null;
  746. }
  747. return (<FreeCamera>this._rigCameras[0]);
  748. }
  749. /**
  750. * Gets the right camera of a rig setup in case of Rigged Camera
  751. */
  752. public get rightCamera(): Nullable<FreeCamera> {
  753. if (this._rigCameras.length < 2) {
  754. return null;
  755. }
  756. return (<FreeCamera>this._rigCameras[1]);
  757. }
  758. /**
  759. * Gets the left camera target of a rig setup in case of Rigged Camera
  760. * @returns the target position
  761. */
  762. public getLeftTarget(): Nullable<Vector3> {
  763. if (this._rigCameras.length < 1) {
  764. return null;
  765. }
  766. return (<TargetCamera>this._rigCameras[0]).getTarget();
  767. }
  768. /**
  769. * Gets the right camera target of a rig setup in case of Rigged Camera
  770. * @returns the target position
  771. */
  772. public getRightTarget(): Nullable<Vector3> {
  773. if (this._rigCameras.length < 2) {
  774. return null;
  775. }
  776. return (<TargetCamera>this._rigCameras[1]).getTarget();
  777. }
  778. /**
  779. * @hidden
  780. */
  781. public setCameraRigMode(mode: number, rigParams: any): void {
  782. if (this.cameraRigMode === mode) {
  783. return;
  784. }
  785. while (this._rigCameras.length > 0) {
  786. let camera = this._rigCameras.pop();
  787. if (camera) {
  788. camera.dispose();
  789. }
  790. }
  791. this.cameraRigMode = mode;
  792. this._cameraRigParams = {};
  793. //we have to implement stereo camera calcultating left and right viewpoints from interaxialDistance and target,
  794. //not from a given angle as it is now, but until that complete code rewriting provisional stereoHalfAngle value is introduced
  795. this._cameraRigParams.interaxialDistance = rigParams.interaxialDistance || 0.0637;
  796. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(this._cameraRigParams.interaxialDistance / 0.0637);
  797. // create the rig cameras, unless none
  798. if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
  799. let leftCamera = this.createRigCamera(this.name + "_L", 0);
  800. let rightCamera = this.createRigCamera(this.name + "_R", 1);
  801. if (leftCamera && rightCamera) {
  802. this._rigCameras.push(leftCamera);
  803. this._rigCameras.push(rightCamera);
  804. }
  805. }
  806. switch (this.cameraRigMode) {
  807. case Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH:
  808. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  809. this._rigCameras[1]._rigPostProcess = new AnaglyphPostProcess(this.name + "_anaglyph", 1.0, this._rigCameras);
  810. break;
  811. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL:
  812. case Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED:
  813. case Camera.RIG_MODE_STEREOSCOPIC_OVERUNDER:
  814. var isStereoscopicHoriz = this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_PARALLEL || this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED;
  815. this._rigCameras[0]._rigPostProcess = new PassPostProcess(this.name + "_passthru", 1.0, this._rigCameras[0]);
  816. this._rigCameras[1]._rigPostProcess = new StereoscopicInterlacePostProcess(this.name + "_stereoInterlace", this._rigCameras, isStereoscopicHoriz);
  817. break;
  818. case Camera.RIG_MODE_VR:
  819. var metrics = rigParams.vrCameraMetrics || VRCameraMetrics.GetDefault();
  820. this._rigCameras[0]._cameraRigParams.vrMetrics = metrics;
  821. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  822. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  823. this._rigCameras[0]._cameraRigParams.vrHMatrix = metrics.leftHMatrix;
  824. this._rigCameras[0]._cameraRigParams.vrPreViewMatrix = metrics.leftPreViewMatrix;
  825. this._rigCameras[0].getProjectionMatrix = this._rigCameras[0]._getVRProjectionMatrix;
  826. this._rigCameras[1]._cameraRigParams.vrMetrics = metrics;
  827. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  828. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  829. this._rigCameras[1]._cameraRigParams.vrHMatrix = metrics.rightHMatrix;
  830. this._rigCameras[1]._cameraRigParams.vrPreViewMatrix = metrics.rightPreViewMatrix;
  831. this._rigCameras[1].getProjectionMatrix = this._rigCameras[1]._getVRProjectionMatrix;
  832. if (metrics.compensateDistortion) {
  833. this._rigCameras[0]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Left", this._rigCameras[0], false, metrics);
  834. this._rigCameras[1]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Right", this._rigCameras[1], true, metrics);
  835. }
  836. break;
  837. case Camera.RIG_MODE_WEBVR:
  838. if (rigParams.vrDisplay) {
  839. var leftEye = rigParams.vrDisplay.getEyeParameters('left');
  840. var rightEye = rigParams.vrDisplay.getEyeParameters('right');
  841. //Left eye
  842. this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
  843. this._rigCameras[0].setCameraRigParameter("left", true);
  844. //leaving this for future reference
  845. this._rigCameras[0].setCameraRigParameter("specs", rigParams.specs);
  846. this._rigCameras[0].setCameraRigParameter("eyeParameters", leftEye);
  847. this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
  848. this._rigCameras[0].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  849. this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
  850. this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
  851. this._rigCameras[0].parent = this;
  852. this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
  853. //Right eye
  854. this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
  855. this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
  856. this._rigCameras[1].setCameraRigParameter("specs", rigParams.specs);
  857. this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
  858. this._rigCameras[1].setCameraRigParameter("parentCamera", rigParams.parentCamera);
  859. this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
  860. this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
  861. this._rigCameras[1].parent = this;
  862. this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
  863. if (Camera.UseAlternateWebVRRendering) {
  864. this._rigCameras[1]._skipRendering = true;
  865. this._rigCameras[0]._alternateCamera = this._rigCameras[1];
  866. }
  867. }
  868. break;
  869. }
  870. this._cascadePostProcessesToRigCams();
  871. this.update();
  872. }
  873. private _getVRProjectionMatrix(): Matrix {
  874. Matrix.PerspectiveFovLHToRef(this._cameraRigParams.vrMetrics.aspectRatioFov, this._cameraRigParams.vrMetrics.aspectRatio, this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
  875. this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix, this._projectionMatrix);
  876. return this._projectionMatrix;
  877. }
  878. protected _updateCameraRotationMatrix() {
  879. //Here for WebVR
  880. }
  881. protected _updateWebVRCameraRotationMatrix() {
  882. //Here for WebVR
  883. }
  884. /**
  885. * This function MUST be overwritten by the different WebVR cameras available.
  886. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  887. */
  888. protected _getWebVRProjectionMatrix(): Matrix {
  889. return Matrix.Identity();
  890. }
  891. /**
  892. * This function MUST be overwritten by the different WebVR cameras available.
  893. * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
  894. */
  895. protected _getWebVRViewMatrix(): Matrix {
  896. return Matrix.Identity();
  897. }
  898. /** @hidden */
  899. public setCameraRigParameter(name: string, value: any) {
  900. if (!this._cameraRigParams) {
  901. this._cameraRigParams = {};
  902. }
  903. this._cameraRigParams[name] = value;
  904. //provisionnally:
  905. if (name === "interaxialDistance") {
  906. this._cameraRigParams.stereoHalfAngle = Tools.ToRadians(value / 0.0637);
  907. }
  908. }
  909. /**
  910. * needs to be overridden by children so sub has required properties to be copied
  911. * @hidden
  912. */
  913. public createRigCamera(name: string, cameraIndex: number): Nullable<Camera> {
  914. return null;
  915. }
  916. /**
  917. * May need to be overridden by children
  918. * @hidden
  919. */
  920. public _updateRigCameras() {
  921. for (var i = 0; i < this._rigCameras.length; i++) {
  922. this._rigCameras[i].minZ = this.minZ;
  923. this._rigCameras[i].maxZ = this.maxZ;
  924. this._rigCameras[i].fov = this.fov;
  925. }
  926. // only update viewport when ANAGLYPH
  927. if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
  928. this._rigCameras[0].viewport = this._rigCameras[1].viewport = this.viewport;
  929. }
  930. }
  931. /** @hidden */
  932. public _setupInputs() {
  933. }
  934. /**
  935. * Serialiaze the camera setup to a json represention
  936. * @returns the JSON representation
  937. */
  938. public serialize(): any {
  939. var serializationObject = SerializationHelper.Serialize(this);
  940. // Type
  941. serializationObject.type = this.getClassName();
  942. // Parent
  943. if (this.parent) {
  944. serializationObject.parentId = this.parent.id;
  945. }
  946. if (this.inputs) {
  947. this.inputs.serialize(serializationObject);
  948. }
  949. // Animations
  950. Animation.AppendSerializedAnimations(this, serializationObject);
  951. serializationObject.ranges = this.serializeAnimationRanges();
  952. return serializationObject;
  953. }
  954. /**
  955. * Clones the current camera.
  956. * @param name The cloned camera name
  957. * @returns the cloned camera
  958. */
  959. public clone(name: string): Camera {
  960. return SerializationHelper.Clone(Camera.GetConstructorFromName(this.getClassName(), name, this.getScene(), this.interaxialDistance, this.isStereoscopicSideBySide), this);
  961. }
  962. /**
  963. * Gets the direction of the camera relative to a given local axis.
  964. * @param localAxis Defines the reference axis to provide a relative direction.
  965. * @return the direction
  966. */
  967. public getDirection(localAxis: Vector3): Vector3 {
  968. var result = Vector3.Zero();
  969. this.getDirectionToRef(localAxis, result);
  970. return result;
  971. }
  972. /**
  973. * Gets the direction of the camera relative to a given local axis into a passed vector.
  974. * @param localAxis Defines the reference axis to provide a relative direction.
  975. * @param result Defines the vector to store the result in
  976. */
  977. public getDirectionToRef(localAxis: Vector3, result: Vector3): void {
  978. Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
  979. }
  980. /**
  981. * Gets a camera constructor for a given camera type
  982. * @param type The type of the camera to construct (should be equal to one of the camera class name)
  983. * @param name The name of the camera the result will be able to instantiate
  984. * @param scene The scene the result will construct the camera in
  985. * @param interaxial_distance In case of stereoscopic setup, the distance between both eyes
  986. * @param isStereoscopicSideBySide In case of stereoscopic setup, should the sereo be side b side
  987. * @returns a factory method to construc the camera
  988. */
  989. static GetConstructorFromName(type: string, name: string, scene: Scene, interaxial_distance: number = 0, isStereoscopicSideBySide: boolean = true): () => Camera {
  990. let constructorFunc = Node.Construct(type, name, scene, {
  991. interaxial_distance: interaxial_distance,
  992. isStereoscopicSideBySide: isStereoscopicSideBySide
  993. });
  994. if (constructorFunc) {
  995. return <() => Camera>constructorFunc;
  996. }
  997. // Default to universal camera
  998. return () => new UniversalCamera(name, Vector3.Zero(), scene);
  999. }
  1000. /**
  1001. * Compute the world matrix of the camera.
  1002. * @returns the camera workd matrix
  1003. */
  1004. public computeWorldMatrix(): Matrix {
  1005. return this.getWorldMatrix();
  1006. }
  1007. /**
  1008. * Parse a JSON and creates the camera from the parsed information
  1009. * @param parsedCamera The JSON to parse
  1010. * @param scene The scene to instantiate the camera in
  1011. * @returns the newly constructed camera
  1012. */
  1013. public static Parse(parsedCamera: any, scene: Scene): Camera {
  1014. var type = parsedCamera.type;
  1015. var construct = Camera.GetConstructorFromName(type, parsedCamera.name, scene, parsedCamera.interaxial_distance, parsedCamera.isStereoscopicSideBySide);
  1016. var camera = SerializationHelper.Parse(construct, parsedCamera, scene);
  1017. // Parent
  1018. if (parsedCamera.parentId) {
  1019. camera._waitingParentId = parsedCamera.parentId;
  1020. }
  1021. //If camera has an input manager, let it parse inputs settings
  1022. if (camera.inputs) {
  1023. camera.inputs.parse(parsedCamera);
  1024. camera._setupInputs();
  1025. }
  1026. if ((<any>camera).setPosition) { // need to force position
  1027. camera.position.copyFromFloats(0, 0, 0);
  1028. (<any>camera).setPosition(Vector3.FromArray(parsedCamera.position));
  1029. }
  1030. // Target
  1031. if (parsedCamera.target) {
  1032. if ((<any>camera).setTarget) {
  1033. (<any>camera).setTarget(Vector3.FromArray(parsedCamera.target));
  1034. }
  1035. }
  1036. // Apply 3d rig, when found
  1037. if (parsedCamera.cameraRigMode) {
  1038. var rigParams = (parsedCamera.interaxial_distance) ? { interaxialDistance: parsedCamera.interaxial_distance } : {};
  1039. camera.setCameraRigMode(parsedCamera.cameraRigMode, rigParams);
  1040. }
  1041. // Animations
  1042. if (parsedCamera.animations) {
  1043. for (var animationIndex = 0; animationIndex < parsedCamera.animations.length; animationIndex++) {
  1044. var parsedAnimation = parsedCamera.animations[animationIndex];
  1045. camera.animations.push(Animation.Parse(parsedAnimation));
  1046. }
  1047. Node.ParseAnimationRanges(camera, parsedCamera, scene);
  1048. }
  1049. if (parsedCamera.autoAnimate) {
  1050. scene.beginAnimation(camera, parsedCamera.autoAnimateFrom, parsedCamera.autoAnimateTo, parsedCamera.autoAnimateLoop, parsedCamera.autoAnimateSpeed || 1.0);
  1051. }
  1052. return camera;
  1053. }
  1054. }
  1055. }