cubeTexture.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. import { serialize, serializeAsMatrix, SerializationHelper } from "../../Misc/decorators";
  2. import { Tools } from "../../Misc/tools";
  3. import { Nullable } from "../../types";
  4. import { Scene } from "../../scene";
  5. import { Matrix, Vector3 } from "../../Maths/math.vector";
  6. import { BaseTexture } from "../../Materials/Textures/baseTexture";
  7. import { Texture } from "../../Materials/Textures/texture";
  8. import { _TimeToken } from "../../Instrumentation/timeToken";
  9. import { Constants } from "../../Engines/constants";
  10. import { _TypeStore } from '../../Misc/typeStore';
  11. import "../../Engines/Extensions/engine.cubeTexture";
  12. /**
  13. * Class for creating a cube texture
  14. */
  15. export class CubeTexture extends BaseTexture {
  16. private _delayedOnLoad: Nullable<() => void>;
  17. /**
  18. * The url of the texture
  19. */
  20. public url: string;
  21. /**
  22. * Gets or sets the center of the bounding box associated with the cube texture.
  23. * It must define where the camera used to render the texture was set
  24. * @see http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode
  25. */
  26. public boundingBoxPosition = Vector3.Zero();
  27. private _boundingBoxSize: Vector3;
  28. /**
  29. * Gets or sets the size of the bounding box associated with the cube texture
  30. * When defined, the cubemap will switch to local mode
  31. * @see https://community.arm.com/graphics/b/blog/posts/reflections-based-on-local-cubemaps-in-unity
  32. * @example https://www.babylonjs-playground.com/#RNASML
  33. */
  34. public set boundingBoxSize(value: Vector3) {
  35. if (this._boundingBoxSize && this._boundingBoxSize.equals(value)) {
  36. return;
  37. }
  38. this._boundingBoxSize = value;
  39. let scene = this.getScene();
  40. if (scene) {
  41. scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
  42. }
  43. }
  44. /**
  45. * Returns the bounding box size
  46. * @see http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode
  47. */
  48. public get boundingBoxSize(): Vector3 {
  49. return this._boundingBoxSize;
  50. }
  51. protected _rotationY: number = 0;
  52. /**
  53. * Sets texture matrix rotation angle around Y axis in radians.
  54. */
  55. @serialize("rotationY")
  56. public set rotationY(value: number) {
  57. this._rotationY = value;
  58. this.setReflectionTextureMatrix(Matrix.RotationY(this._rotationY));
  59. }
  60. /**
  61. * Gets texture matrix rotation angle around Y axis radians.
  62. */
  63. public get rotationY(): number {
  64. return this._rotationY;
  65. }
  66. /**
  67. * Are mip maps generated for this texture or not.
  68. */
  69. public get noMipmap(): boolean {
  70. return this._noMipmap;
  71. }
  72. private _noMipmap: boolean;
  73. @serialize("files")
  74. private _files: string[];
  75. private _extensions: string[];
  76. @serializeAsMatrix("textureMatrix")
  77. private _textureMatrix: Matrix;
  78. private _format: number;
  79. private _createPolynomials: boolean;
  80. /** @hidden */
  81. public _prefiltered: boolean = false;
  82. /**
  83. * Creates a cube texture from an array of image urls
  84. * @param files defines an array of image urls
  85. * @param scene defines the hosting scene
  86. * @param noMipmap specifies if mip maps are not used
  87. * @returns a cube texture
  88. */
  89. public static CreateFromImages(files: string[], scene: Scene, noMipmap?: boolean): CubeTexture {
  90. let rootUrlKey = "";
  91. files.forEach((url) => rootUrlKey += url);
  92. return new CubeTexture(rootUrlKey, scene, null, noMipmap, files);
  93. }
  94. /**
  95. * Creates and return a texture created from prefilterd data by tools like IBL Baker or Lys.
  96. * @param url defines the url of the prefiltered texture
  97. * @param scene defines the scene the texture is attached to
  98. * @param forcedExtension defines the extension of the file if different from the url
  99. * @param createPolynomials defines whether or not to create polynomial harmonics from the texture data if necessary
  100. * @return the prefiltered texture
  101. */
  102. public static CreateFromPrefilteredData(url: string, scene: Scene, forcedExtension: any = null, createPolynomials: boolean = true) {
  103. return new CubeTexture(url, scene, null, false, null, null, null, undefined, true, forcedExtension, createPolynomials);
  104. }
  105. /**
  106. * Creates a cube texture to use with reflection for instance. It can be based upon dds or six images as well
  107. * as prefiltered data.
  108. * @param rootUrl defines the url of the texture or the root name of the six images
  109. * @param scene defines the scene the texture is attached to
  110. * @param extensions defines the suffixes add to the picture name in case six images are in use like _px.jpg...
  111. * @param noMipmap defines if mipmaps should be created or not
  112. * @param files defines the six files to load for the different faces in that order: px, py, pz, nx, ny, nz
  113. * @param onLoad defines a callback triggered at the end of the file load if no errors occured
  114. * @param onError defines a callback triggered in case of error during load
  115. * @param format defines the internal format to use for the texture once loaded
  116. * @param prefiltered defines whether or not the texture is created from prefiltered data
  117. * @param forcedExtension defines the extensions to use (force a special type of file to load) in case it is different from the file name
  118. * @param createPolynomials defines whether or not to create polynomial harmonics from the texture data if necessary
  119. * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
  120. * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
  121. * @return the cube texture
  122. */
  123. constructor(rootUrl: string, scene: Scene, extensions: Nullable<string[]> = null, noMipmap: boolean = false, files: Nullable<string[]> = null,
  124. onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format: number = Constants.TEXTUREFORMAT_RGBA, prefiltered = false,
  125. forcedExtension: any = null, createPolynomials: boolean = false,
  126. lodScale: number = 0.8, lodOffset: number = 0) {
  127. super(scene);
  128. this.name = rootUrl;
  129. this.url = rootUrl;
  130. this._noMipmap = noMipmap;
  131. this.hasAlpha = false;
  132. this._format = format;
  133. this.isCube = true;
  134. this._textureMatrix = Matrix.Identity();
  135. this._createPolynomials = createPolynomials;
  136. this.coordinatesMode = Texture.CUBIC_MODE;
  137. if (!rootUrl && !files) {
  138. return;
  139. }
  140. const lastDot = rootUrl.lastIndexOf(".");
  141. const extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
  142. const isDDS = (extension === ".dds");
  143. const isEnv = (extension === ".env");
  144. if (isEnv) {
  145. this.gammaSpace = false;
  146. this._prefiltered = false;
  147. }
  148. else {
  149. this._prefiltered = prefiltered;
  150. if (prefiltered) {
  151. this.gammaSpace = false;
  152. }
  153. }
  154. this._texture = this._getFromCache(rootUrl, noMipmap);
  155. if (!files) {
  156. if (!isEnv && !isDDS && !extensions) {
  157. extensions = ["_px.jpg", "_py.jpg", "_pz.jpg", "_nx.jpg", "_ny.jpg", "_nz.jpg"];
  158. }
  159. files = [];
  160. if (extensions) {
  161. for (var index = 0; index < extensions.length; index++) {
  162. files.push(rootUrl + extensions[index]);
  163. }
  164. }
  165. }
  166. this._files = files;
  167. if (!this._texture) {
  168. if (!scene.useDelayedTextureLoading) {
  169. if (prefiltered) {
  170. this._texture = scene.getEngine().createPrefilteredCubeTexture(rootUrl, scene, lodScale, lodOffset, onLoad, onError, format, forcedExtension, this._createPolynomials);
  171. }
  172. else {
  173. this._texture = scene.getEngine().createCubeTexture(rootUrl, scene, files, noMipmap, onLoad, onError, this._format, forcedExtension, false, lodScale, lodOffset);
  174. }
  175. } else {
  176. this.delayLoadState = Constants.DELAYLOADSTATE_NOTLOADED;
  177. }
  178. } else if (onLoad) {
  179. if (this._texture.isReady) {
  180. Tools.SetImmediate(() => onLoad());
  181. } else {
  182. this._texture.onLoadedObservable.add(onLoad);
  183. }
  184. }
  185. }
  186. /**
  187. * Gets a boolean indicating if the cube texture contains prefiltered mips (used to simulate roughness with PBR)
  188. */
  189. public get isPrefiltered(): boolean {
  190. return this._prefiltered;
  191. }
  192. /**
  193. * Get the current class name of the texture useful for serialization or dynamic coding.
  194. * @returns "CubeTexture"
  195. */
  196. public getClassName(): string {
  197. return "CubeTexture";
  198. }
  199. /**
  200. * Update the url (and optional buffer) of this texture if url was null during construction.
  201. * @param url the url of the texture
  202. * @param forcedExtension defines the extension to use
  203. * @param onLoad callback called when the texture is loaded (defaults to null)
  204. */
  205. public updateURL(url: string, forcedExtension?: string, onLoad?: () => void): void {
  206. if (this.url) {
  207. this.releaseInternalTexture();
  208. this.getScene()!.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
  209. }
  210. this.name = url;
  211. this.url = url;
  212. this.delayLoadState = Constants.DELAYLOADSTATE_NOTLOADED;
  213. this._prefiltered = false;
  214. if (onLoad) {
  215. this._delayedOnLoad = onLoad;
  216. }
  217. this.delayLoad(forcedExtension);
  218. }
  219. /**
  220. * Delays loading of the cube texture
  221. * @param forcedExtension defines the extension to use
  222. */
  223. public delayLoad(forcedExtension?: string): void {
  224. if (this.delayLoadState !== Constants.DELAYLOADSTATE_NOTLOADED) {
  225. return;
  226. }
  227. let scene = this.getScene();
  228. if (!scene) {
  229. return;
  230. }
  231. this.delayLoadState = Constants.DELAYLOADSTATE_LOADED;
  232. this._texture = this._getFromCache(this.url, this._noMipmap);
  233. if (!this._texture) {
  234. if (this._prefiltered) {
  235. this._texture = scene.getEngine().createPrefilteredCubeTexture(this.url, scene, this.lodGenerationScale, this.lodGenerationOffset, this._delayedOnLoad, undefined, this._format, undefined, this._createPolynomials);
  236. }
  237. else {
  238. this._texture = scene.getEngine().createCubeTexture(this.url, scene, this._files, this._noMipmap, this._delayedOnLoad, null, this._format, forcedExtension);
  239. }
  240. }
  241. }
  242. /**
  243. * Returns the reflection texture matrix
  244. * @returns the reflection texture matrix
  245. */
  246. public getReflectionTextureMatrix(): Matrix {
  247. return this._textureMatrix;
  248. }
  249. /**
  250. * Sets the reflection texture matrix
  251. * @param value Reflection texture matrix
  252. */
  253. public setReflectionTextureMatrix(value: Matrix): void {
  254. if (value.updateFlag === this._textureMatrix.updateFlag) {
  255. return;
  256. }
  257. if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
  258. this.getScene()!.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag, (mat) => mat.getActiveTextures().indexOf(this) !== -1);
  259. }
  260. this._textureMatrix = value;
  261. }
  262. /**
  263. * Parses text to create a cube texture
  264. * @param parsedTexture define the serialized text to read from
  265. * @param scene defines the hosting scene
  266. * @param rootUrl defines the root url of the cube texture
  267. * @returns a cube texture
  268. */
  269. public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): CubeTexture {
  270. var texture = SerializationHelper.Parse(() => {
  271. var prefiltered: boolean = false;
  272. if (parsedTexture.prefiltered) {
  273. prefiltered = parsedTexture.prefiltered;
  274. }
  275. return new CubeTexture(rootUrl + parsedTexture.name, scene, parsedTexture.extensions, false, parsedTexture.files || null, null, null, undefined, prefiltered);
  276. }, parsedTexture, scene);
  277. // Local Cubemaps
  278. if (parsedTexture.boundingBoxPosition) {
  279. texture.boundingBoxPosition = Vector3.FromArray(parsedTexture.boundingBoxPosition);
  280. }
  281. if (parsedTexture.boundingBoxSize) {
  282. texture.boundingBoxSize = Vector3.FromArray(parsedTexture.boundingBoxSize);
  283. }
  284. // Animations
  285. if (parsedTexture.animations) {
  286. for (var animationIndex = 0; animationIndex < parsedTexture.animations.length; animationIndex++) {
  287. var parsedAnimation = parsedTexture.animations[animationIndex];
  288. const internalClass = _TypeStore.GetClass("BABYLON.Animation");
  289. if (internalClass) {
  290. texture.animations.push(internalClass.Parse(parsedAnimation));
  291. }
  292. }
  293. }
  294. return texture;
  295. }
  296. /**
  297. * Makes a clone, or deep copy, of the cube texture
  298. * @returns a new cube texture
  299. */
  300. public clone(): CubeTexture {
  301. let scene = this.getScene();
  302. let uniqueId = 0;
  303. let newCubeTexture = SerializationHelper.Clone(() => {
  304. if (!scene) {
  305. return this;
  306. }
  307. const cubeTexture = new CubeTexture(this.url, scene, this._extensions, this._noMipmap, this._files);
  308. uniqueId = cubeTexture.uniqueId;
  309. return cubeTexture;
  310. }, this);
  311. newCubeTexture.uniqueId = uniqueId;
  312. return newCubeTexture;
  313. }
  314. }
  315. Texture._CubeTextureParser = CubeTexture.Parse;
  316. // Some exporters relies on Tools.Instantiate
  317. _TypeStore.RegisteredTypes["BABYLON.CubeTexture"] = CubeTexture;