reflectionProbe.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. import { serializeAsMeshReference, serializeAsVector3, SerializationHelper } from "../Misc/decorators";
  2. import { RenderTargetTexture } from "../Materials/Textures/renderTargetTexture";
  3. import { Matrix, Vector3 } from "../Maths/math.vector";
  4. import { AbstractMesh } from "../Meshes/abstractMesh";
  5. import { Nullable } from "../types";
  6. import { AbstractScene } from "../abstractScene";
  7. import { Scene } from "../scene";
  8. import { Constants } from "../Engines/constants";
  9. declare module "../abstractScene" {
  10. export interface AbstractScene {
  11. /**
  12. * The list of reflection probes added to the scene
  13. * @see https://doc.babylonjs.com/how_to/how_to_use_reflection_probes
  14. */
  15. reflectionProbes: Array<ReflectionProbe>;
  16. /**
  17. * Removes the given reflection probe from this scene.
  18. * @param toRemove The reflection probe to remove
  19. * @returns The index of the removed reflection probe
  20. */
  21. removeReflectionProbe(toRemove: ReflectionProbe): number;
  22. /**
  23. * Adds the given reflection probe to this scene.
  24. * @param newReflectionProbe The reflection probe to add
  25. */
  26. addReflectionProbe(newReflectionProbe: ReflectionProbe): void;
  27. }
  28. }
  29. AbstractScene.prototype.removeReflectionProbe = function(toRemove: ReflectionProbe): number {
  30. if (!this.reflectionProbes) {
  31. return -1;
  32. }
  33. var index = this.reflectionProbes.indexOf(toRemove);
  34. if (index !== -1) {
  35. this.reflectionProbes.splice(index, 1);
  36. }
  37. return index;
  38. };
  39. AbstractScene.prototype.addReflectionProbe = function(newReflectionProbe: ReflectionProbe): void {
  40. if (!this.reflectionProbes) {
  41. this.reflectionProbes = [];
  42. }
  43. this.reflectionProbes.push(newReflectionProbe);
  44. };
  45. /**
  46. * Class used to generate realtime reflection / refraction cube textures
  47. * @see https://doc.babylonjs.com/how_to/how_to_use_reflection_probes
  48. */
  49. export class ReflectionProbe {
  50. private _scene: Scene;
  51. private _renderTargetTexture: RenderTargetTexture;
  52. private _projectionMatrix: Matrix;
  53. private _viewMatrix = Matrix.Identity();
  54. private _target = Vector3.Zero();
  55. private _add = Vector3.Zero();
  56. @serializeAsMeshReference()
  57. private _attachedMesh: Nullable<AbstractMesh>;
  58. private _invertYAxis = false;
  59. /** Gets or sets probe position (center of the cube map) */
  60. @serializeAsVector3()
  61. public position = Vector3.Zero();
  62. /**
  63. * Creates a new reflection probe
  64. * @param name defines the name of the probe
  65. * @param size defines the texture resolution (for each face)
  66. * @param scene defines the hosting scene
  67. * @param generateMipMaps defines if mip maps should be generated automatically (true by default)
  68. * @param useFloat defines if HDR data (flaot data) should be used to store colors (false by default)
  69. */
  70. constructor(
  71. /** defines the name of the probe */
  72. public name: string,
  73. size: number, scene: Scene, generateMipMaps = true, useFloat = false) {
  74. this._scene = scene;
  75. // Create the scene field if not exist.
  76. if (!this._scene.reflectionProbes) {
  77. this._scene.reflectionProbes = new Array<ReflectionProbe>();
  78. }
  79. this._scene.reflectionProbes.push(this);
  80. let textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE;
  81. if (useFloat) {
  82. const caps = this._scene.getEngine().getCaps();
  83. if (caps.textureHalfFloatRender) {
  84. textureType = Constants.TEXTURETYPE_HALF_FLOAT;
  85. }
  86. else if (caps.textureFloatRender) {
  87. textureType = Constants.TEXTURETYPE_FLOAT;
  88. }
  89. }
  90. this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, textureType, true);
  91. this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
  92. switch (faceIndex) {
  93. case 0:
  94. this._add.copyFromFloats(1, 0, 0);
  95. break;
  96. case 1:
  97. this._add.copyFromFloats(-1, 0, 0);
  98. break;
  99. case 2:
  100. this._add.copyFromFloats(0, this._invertYAxis ? 1 : -1, 0);
  101. break;
  102. case 3:
  103. this._add.copyFromFloats(0, this._invertYAxis ? -1 : 1, 0);
  104. break;
  105. case 4:
  106. this._add.copyFromFloats(0, 0, scene.useRightHandedSystem ? -1 : 1);
  107. break;
  108. case 5:
  109. this._add.copyFromFloats(0, 0, scene.useRightHandedSystem ? 1 : -1);
  110. break;
  111. }
  112. if (this._attachedMesh) {
  113. this.position.copyFrom(this._attachedMesh.getAbsolutePosition());
  114. }
  115. this.position.addToRef(this._add, this._target);
  116. if (scene.useRightHandedSystem) {
  117. Matrix.LookAtRHToRef(this.position, this._target, Vector3.Up(), this._viewMatrix);
  118. if (scene.activeCamera) {
  119. this._projectionMatrix = Matrix.PerspectiveFovRH(Math.PI / 2, 1, scene.activeCamera.minZ, scene.activeCamera.maxZ);
  120. scene.setTransformMatrix(this._viewMatrix, this._projectionMatrix);
  121. }
  122. }
  123. else {
  124. Matrix.LookAtLHToRef(this.position, this._target, Vector3.Up(), this._viewMatrix);
  125. if (scene.activeCamera) {
  126. this._projectionMatrix = Matrix.PerspectiveFovLH(Math.PI / 2, 1, scene.activeCamera.minZ, scene.activeCamera.maxZ);
  127. scene.setTransformMatrix(this._viewMatrix, this._projectionMatrix);
  128. }
  129. }
  130. scene._forcedViewPosition = this.position;
  131. });
  132. this._renderTargetTexture.onAfterUnbindObservable.add(() => {
  133. scene._forcedViewPosition = null;
  134. scene.updateTransformMatrix(true);
  135. });
  136. }
  137. /** Gets or sets the number of samples to use for multi-sampling (0 by default). Required WebGL2 */
  138. public get samples(): number {
  139. return this._renderTargetTexture.samples;
  140. }
  141. public set samples(value: number) {
  142. this._renderTargetTexture.samples = value;
  143. }
  144. /** Gets or sets the refresh rate to use (on every frame by default) */
  145. public get refreshRate(): number {
  146. return this._renderTargetTexture.refreshRate;
  147. }
  148. public set refreshRate(value: number) {
  149. this._renderTargetTexture.refreshRate = value;
  150. }
  151. /**
  152. * Gets the hosting scene
  153. * @returns a Scene
  154. */
  155. public getScene(): Scene {
  156. return this._scene;
  157. }
  158. /** Gets the internal CubeTexture used to render to */
  159. public get cubeTexture(): RenderTargetTexture {
  160. return this._renderTargetTexture;
  161. }
  162. /** Gets the list of meshes to render */
  163. public get renderList(): Nullable<AbstractMesh[]> {
  164. return this._renderTargetTexture.renderList;
  165. }
  166. /**
  167. * Attach the probe to a specific mesh (Rendering will be done from attached mesh's position)
  168. * @param mesh defines the mesh to attach to
  169. */
  170. public attachToMesh(mesh: Nullable<AbstractMesh>): void {
  171. this._attachedMesh = mesh;
  172. }
  173. /**
  174. * Specifies whether or not the stencil and depth buffer are cleared between two rendering groups
  175. * @param renderingGroupId The rendering group id corresponding to its index
  176. * @param autoClearDepthStencil Automatically clears depth and stencil between groups if true.
  177. */
  178. public setRenderingAutoClearDepthStencil(renderingGroupId: number, autoClearDepthStencil: boolean): void {
  179. this._renderTargetTexture.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
  180. }
  181. /**
  182. * Clean all associated resources
  183. */
  184. public dispose() {
  185. var index = this._scene.reflectionProbes.indexOf(this);
  186. if (index !== -1) {
  187. // Remove from the scene if found
  188. this._scene.reflectionProbes.splice(index, 1);
  189. }
  190. if (this._renderTargetTexture) {
  191. this._renderTargetTexture.dispose();
  192. (<any>this._renderTargetTexture) = null;
  193. }
  194. }
  195. /**
  196. * Converts the reflection probe information to a readable string for debug purpose.
  197. * @param fullDetails Supports for multiple levels of logging within scene loading
  198. * @returns the human readable reflection probe info
  199. */
  200. public toString(fullDetails?: boolean): string {
  201. var ret = "Name: " + this.name;
  202. if (fullDetails) {
  203. ret += ", position: " + this.position.toString();
  204. if (this._attachedMesh) {
  205. ret += ", attached mesh: " + this._attachedMesh.name;
  206. }
  207. }
  208. return ret;
  209. }
  210. /**
  211. * Get the class name of the relfection probe.
  212. * @returns "ReflectionProbe"
  213. */
  214. public getClassName(): string {
  215. return "ReflectionProbe";
  216. }
  217. /**
  218. * Serialize the reflection probe to a JSON representation we can easily use in the resepective Parse function.
  219. * @returns The JSON representation of the texture
  220. */
  221. public serialize(): any {
  222. const serializationObject = SerializationHelper.Serialize(this, this._renderTargetTexture.serialize());
  223. serializationObject.isReflectionProbe = true;
  224. return serializationObject;
  225. }
  226. /**
  227. * Parse the JSON representation of a reflection probe in order to recreate the reflection probe in the given scene.
  228. * @param parsedReflectionProbe Define the JSON representation of the reflection probe
  229. * @param scene Define the scene the parsed reflection probe should be instantiated in
  230. * @param rootUrl Define the root url of the parsing sequence in the case of relative dependencies
  231. * @returns The parsed reflection probe if successful
  232. */
  233. public static Parse(parsedReflectionProbe: any, scene: Scene, rootUrl: string): Nullable<ReflectionProbe> {
  234. let reflectionProbe: Nullable<ReflectionProbe> = null;
  235. if (scene.reflectionProbes) {
  236. for (let index = 0; index < scene.reflectionProbes.length; index++) {
  237. const rp = scene.reflectionProbes[index];
  238. if (rp.name === parsedReflectionProbe.name) {
  239. reflectionProbe = rp;
  240. break;
  241. }
  242. }
  243. }
  244. reflectionProbe = SerializationHelper.Parse(() => reflectionProbe || new ReflectionProbe(parsedReflectionProbe.name, parsedReflectionProbe.renderTargetSize, scene, parsedReflectionProbe._generateMipMaps), parsedReflectionProbe, scene, rootUrl);
  245. reflectionProbe.cubeTexture._waitingRenderList = parsedReflectionProbe.renderList;
  246. if (parsedReflectionProbe._attachedMesh) {
  247. reflectionProbe.attachToMesh(scene.getMeshByID(parsedReflectionProbe._attachedMesh));
  248. }
  249. return reflectionProbe;
  250. }
  251. }