reflectionProbe.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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.onBeforeBindObservable.add(() => {
  133. scene.getEngine()._debugPushGroup(`reflection probe generation for ${name}`, 1);
  134. });
  135. this._renderTargetTexture.onAfterUnbindObservable.add(() => {
  136. scene._forcedViewPosition = null;
  137. scene.updateTransformMatrix(true);
  138. scene.getEngine()._debugPopGroup(1);
  139. });
  140. }
  141. /** Gets or sets the number of samples to use for multi-sampling (0 by default). Required WebGL2 */
  142. public get samples(): number {
  143. return this._renderTargetTexture.samples;
  144. }
  145. public set samples(value: number) {
  146. this._renderTargetTexture.samples = value;
  147. }
  148. /** Gets or sets the refresh rate to use (on every frame by default) */
  149. public get refreshRate(): number {
  150. return this._renderTargetTexture.refreshRate;
  151. }
  152. public set refreshRate(value: number) {
  153. this._renderTargetTexture.refreshRate = value;
  154. }
  155. /**
  156. * Gets the hosting scene
  157. * @returns a Scene
  158. */
  159. public getScene(): Scene {
  160. return this._scene;
  161. }
  162. /** Gets the internal CubeTexture used to render to */
  163. public get cubeTexture(): RenderTargetTexture {
  164. return this._renderTargetTexture;
  165. }
  166. /** Gets the list of meshes to render */
  167. public get renderList(): Nullable<AbstractMesh[]> {
  168. return this._renderTargetTexture.renderList;
  169. }
  170. /**
  171. * Attach the probe to a specific mesh (Rendering will be done from attached mesh's position)
  172. * @param mesh defines the mesh to attach to
  173. */
  174. public attachToMesh(mesh: Nullable<AbstractMesh>): void {
  175. this._attachedMesh = mesh;
  176. }
  177. /**
  178. * Specifies whether or not the stencil and depth buffer are cleared between two rendering groups
  179. * @param renderingGroupId The rendering group id corresponding to its index
  180. * @param autoClearDepthStencil Automatically clears depth and stencil between groups if true.
  181. */
  182. public setRenderingAutoClearDepthStencil(renderingGroupId: number, autoClearDepthStencil: boolean): void {
  183. this._renderTargetTexture.setRenderingAutoClearDepthStencil(renderingGroupId, autoClearDepthStencil);
  184. }
  185. /**
  186. * Clean all associated resources
  187. */
  188. public dispose() {
  189. var index = this._scene.reflectionProbes.indexOf(this);
  190. if (index !== -1) {
  191. // Remove from the scene if found
  192. this._scene.reflectionProbes.splice(index, 1);
  193. }
  194. if (this._renderTargetTexture) {
  195. this._renderTargetTexture.dispose();
  196. (<any>this._renderTargetTexture) = null;
  197. }
  198. }
  199. /**
  200. * Converts the reflection probe information to a readable string for debug purpose.
  201. * @param fullDetails Supports for multiple levels of logging within scene loading
  202. * @returns the human readable reflection probe info
  203. */
  204. public toString(fullDetails?: boolean): string {
  205. var ret = "Name: " + this.name;
  206. if (fullDetails) {
  207. ret += ", position: " + this.position.toString();
  208. if (this._attachedMesh) {
  209. ret += ", attached mesh: " + this._attachedMesh.name;
  210. }
  211. }
  212. return ret;
  213. }
  214. /**
  215. * Get the class name of the relfection probe.
  216. * @returns "ReflectionProbe"
  217. */
  218. public getClassName(): string {
  219. return "ReflectionProbe";
  220. }
  221. /**
  222. * Serialize the reflection probe to a JSON representation we can easily use in the resepective Parse function.
  223. * @returns The JSON representation of the texture
  224. */
  225. public serialize(): any {
  226. const serializationObject = SerializationHelper.Serialize(this, this._renderTargetTexture.serialize());
  227. serializationObject.isReflectionProbe = true;
  228. return serializationObject;
  229. }
  230. /**
  231. * Parse the JSON representation of a reflection probe in order to recreate the reflection probe in the given scene.
  232. * @param parsedReflectionProbe Define the JSON representation of the reflection probe
  233. * @param scene Define the scene the parsed reflection probe should be instantiated in
  234. * @param rootUrl Define the root url of the parsing sequence in the case of relative dependencies
  235. * @returns The parsed reflection probe if successful
  236. */
  237. public static Parse(parsedReflectionProbe: any, scene: Scene, rootUrl: string): Nullable<ReflectionProbe> {
  238. let reflectionProbe: Nullable<ReflectionProbe> = null;
  239. if (scene.reflectionProbes) {
  240. for (let index = 0; index < scene.reflectionProbes.length; index++) {
  241. const rp = scene.reflectionProbes[index];
  242. if (rp.name === parsedReflectionProbe.name) {
  243. reflectionProbe = rp;
  244. break;
  245. }
  246. }
  247. }
  248. reflectionProbe = SerializationHelper.Parse(() => reflectionProbe || new ReflectionProbe(parsedReflectionProbe.name, parsedReflectionProbe.renderTargetSize, scene, parsedReflectionProbe._generateMipMaps), parsedReflectionProbe, scene, rootUrl);
  249. reflectionProbe.cubeTexture._waitingRenderList = parsedReflectionProbe.renderList;
  250. if (parsedReflectionProbe._attachedMesh) {
  251. reflectionProbe.attachToMesh(scene.getMeshByID(parsedReflectionProbe._attachedMesh));
  252. }
  253. return reflectionProbe;
  254. }
  255. }