texture.ts 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. import { serialize, SerializationHelper } from "../../Misc/decorators";
  2. import { Observable } from "../../Misc/observable";
  3. import { Nullable } from "../../types";
  4. import { Matrix, Vector3 } from "../../Maths/math.vector";
  5. import { BaseTexture } from "../../Materials/Textures/baseTexture";
  6. import { Constants } from "../../Engines/constants";
  7. import { _TypeStore } from '../../Misc/typeStore';
  8. import { _DevTools } from '../../Misc/devTools';
  9. import { IInspectable } from '../../Misc/iInspectable';
  10. import { ThinEngine } from '../../Engines/thinEngine';
  11. import { TimingTools } from '../../Misc/timingTools';
  12. import { InstantiationTools } from '../../Misc/instantiationTools';
  13. import { Plane } from '../../Maths/math.plane';
  14. import { StringTools } from '../../Misc/stringTools';
  15. declare type CubeTexture = import("../../Materials/Textures/cubeTexture").CubeTexture;
  16. declare type MirrorTexture = import("../../Materials/Textures/mirrorTexture").MirrorTexture;
  17. declare type RenderTargetTexture = import("../../Materials/Textures/renderTargetTexture").RenderTargetTexture;
  18. declare type Scene = import("../../scene").Scene;
  19. /**
  20. * This represents a texture in babylon. It can be easily loaded from a network, base64 or html input.
  21. * @see https://doc.babylonjs.com/babylon101/materials#texture
  22. */
  23. export class Texture extends BaseTexture {
  24. /**
  25. * Gets or sets a general boolean used to indicate that textures containing direct data (buffers) must be saved as part of the serialization process
  26. */
  27. public static SerializeBuffers = true;
  28. /** @hidden */
  29. public static _CubeTextureParser = (jsonTexture: any, scene: Scene, rootUrl: string): CubeTexture => {
  30. throw _DevTools.WarnImport("CubeTexture");
  31. }
  32. /** @hidden */
  33. public static _CreateMirror = (name: string, renderTargetSize: number, scene: Scene, generateMipMaps: boolean): MirrorTexture => {
  34. throw _DevTools.WarnImport("MirrorTexture");
  35. }
  36. /** @hidden */
  37. public static _CreateRenderTargetTexture = (name: string, renderTargetSize: number, scene: Scene, generateMipMaps: boolean): RenderTargetTexture => {
  38. throw _DevTools.WarnImport("RenderTargetTexture");
  39. }
  40. /** nearest is mag = nearest and min = nearest and mip = linear */
  41. public static readonly NEAREST_SAMPLINGMODE = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
  42. /** nearest is mag = nearest and min = nearest and mip = linear */
  43. public static readonly NEAREST_NEAREST_MIPLINEAR = Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR; // nearest is mag = nearest and min = nearest and mip = linear
  44. /** Bilinear is mag = linear and min = linear and mip = nearest */
  45. public static readonly BILINEAR_SAMPLINGMODE = Constants.TEXTURE_BILINEAR_SAMPLINGMODE;
  46. /** Bilinear is mag = linear and min = linear and mip = nearest */
  47. public static readonly LINEAR_LINEAR_MIPNEAREST = Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST; // Bilinear is mag = linear and min = linear and mip = nearest
  48. /** Trilinear is mag = linear and min = linear and mip = linear */
  49. public static readonly TRILINEAR_SAMPLINGMODE = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE;
  50. /** Trilinear is mag = linear and min = linear and mip = linear */
  51. public static readonly LINEAR_LINEAR_MIPLINEAR = Constants.TEXTURE_LINEAR_LINEAR_MIPLINEAR; // Trilinear is mag = linear and min = linear and mip = linear
  52. /** mag = nearest and min = nearest and mip = nearest */
  53. public static readonly NEAREST_NEAREST_MIPNEAREST = Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST;
  54. /** mag = nearest and min = linear and mip = nearest */
  55. public static readonly NEAREST_LINEAR_MIPNEAREST = Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST;
  56. /** mag = nearest and min = linear and mip = linear */
  57. public static readonly NEAREST_LINEAR_MIPLINEAR = Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR;
  58. /** mag = nearest and min = linear and mip = none */
  59. public static readonly NEAREST_LINEAR = Constants.TEXTURE_NEAREST_LINEAR;
  60. /** mag = nearest and min = nearest and mip = none */
  61. public static readonly NEAREST_NEAREST = Constants.TEXTURE_NEAREST_NEAREST;
  62. /** mag = linear and min = nearest and mip = nearest */
  63. public static readonly LINEAR_NEAREST_MIPNEAREST = Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST;
  64. /** mag = linear and min = nearest and mip = linear */
  65. public static readonly LINEAR_NEAREST_MIPLINEAR = Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR;
  66. /** mag = linear and min = linear and mip = none */
  67. public static readonly LINEAR_LINEAR = Constants.TEXTURE_LINEAR_LINEAR;
  68. /** mag = linear and min = nearest and mip = none */
  69. public static readonly LINEAR_NEAREST = Constants.TEXTURE_LINEAR_NEAREST;
  70. /** Explicit coordinates mode */
  71. public static readonly EXPLICIT_MODE = Constants.TEXTURE_EXPLICIT_MODE;
  72. /** Spherical coordinates mode */
  73. public static readonly SPHERICAL_MODE = Constants.TEXTURE_SPHERICAL_MODE;
  74. /** Planar coordinates mode */
  75. public static readonly PLANAR_MODE = Constants.TEXTURE_PLANAR_MODE;
  76. /** Cubic coordinates mode */
  77. public static readonly CUBIC_MODE = Constants.TEXTURE_CUBIC_MODE;
  78. /** Projection coordinates mode */
  79. public static readonly PROJECTION_MODE = Constants.TEXTURE_PROJECTION_MODE;
  80. /** Inverse Cubic coordinates mode */
  81. public static readonly SKYBOX_MODE = Constants.TEXTURE_SKYBOX_MODE;
  82. /** Inverse Cubic coordinates mode */
  83. public static readonly INVCUBIC_MODE = Constants.TEXTURE_INVCUBIC_MODE;
  84. /** Equirectangular coordinates mode */
  85. public static readonly EQUIRECTANGULAR_MODE = Constants.TEXTURE_EQUIRECTANGULAR_MODE;
  86. /** Equirectangular Fixed coordinates mode */
  87. public static readonly FIXED_EQUIRECTANGULAR_MODE = Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MODE;
  88. /** Equirectangular Fixed Mirrored coordinates mode */
  89. public static readonly FIXED_EQUIRECTANGULAR_MIRRORED_MODE = Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE;
  90. /** Texture is not repeating outside of 0..1 UVs */
  91. public static readonly CLAMP_ADDRESSMODE = Constants.TEXTURE_CLAMP_ADDRESSMODE;
  92. /** Texture is repeating outside of 0..1 UVs */
  93. public static readonly WRAP_ADDRESSMODE = Constants.TEXTURE_WRAP_ADDRESSMODE;
  94. /** Texture is repeating and mirrored */
  95. public static readonly MIRROR_ADDRESSMODE = Constants.TEXTURE_MIRROR_ADDRESSMODE;
  96. /**
  97. * Gets or sets a boolean which defines if the texture url must be build from the serialized URL instead of just using the name and loading them side by side with the scene file
  98. */
  99. public static UseSerializedUrlIfAny = false;
  100. /**
  101. * Define the url of the texture.
  102. */
  103. @serialize()
  104. public url: Nullable<string> = null;
  105. /**
  106. * Define an offset on the texture to offset the u coordinates of the UVs
  107. * @see https://doc.babylonjs.com/how_to/more_materials#offsetting
  108. */
  109. @serialize()
  110. public uOffset = 0;
  111. /**
  112. * Define an offset on the texture to offset the v coordinates of the UVs
  113. * @see https://doc.babylonjs.com/how_to/more_materials#offsetting
  114. */
  115. @serialize()
  116. public vOffset = 0;
  117. /**
  118. * Define an offset on the texture to scale the u coordinates of the UVs
  119. * @see https://doc.babylonjs.com/how_to/more_materials#tiling
  120. */
  121. @serialize()
  122. public uScale = 1.0;
  123. /**
  124. * Define an offset on the texture to scale the v coordinates of the UVs
  125. * @see https://doc.babylonjs.com/how_to/more_materials#tiling
  126. */
  127. @serialize()
  128. public vScale = 1.0;
  129. /**
  130. * Define an offset on the texture to rotate around the u coordinates of the UVs
  131. * @see https://doc.babylonjs.com/how_to/more_materials
  132. */
  133. @serialize()
  134. public uAng = 0;
  135. /**
  136. * Define an offset on the texture to rotate around the v coordinates of the UVs
  137. * @see https://doc.babylonjs.com/how_to/more_materials
  138. */
  139. @serialize()
  140. public vAng = 0;
  141. /**
  142. * Define an offset on the texture to rotate around the w coordinates of the UVs (in case of 3d texture)
  143. * @see https://doc.babylonjs.com/how_to/more_materials
  144. */
  145. @serialize()
  146. public wAng = 0;
  147. /**
  148. * Defines the center of rotation (U)
  149. */
  150. @serialize()
  151. public uRotationCenter = 0.5;
  152. /**
  153. * Defines the center of rotation (V)
  154. */
  155. @serialize()
  156. public vRotationCenter = 0.5;
  157. /**
  158. * Defines the center of rotation (W)
  159. */
  160. @serialize()
  161. public wRotationCenter = 0.5;
  162. /**
  163. * Are mip maps generated for this texture or not.
  164. */
  165. get noMipmap(): boolean {
  166. return this._noMipmap;
  167. }
  168. /**
  169. * List of inspectable custom properties (used by the Inspector)
  170. * @see https://doc.babylonjs.com/how_to/debug_layer#extensibility
  171. */
  172. public inspectableCustomProperties: Nullable<IInspectable[]> = null;
  173. private _noMipmap: boolean = false;
  174. /** @hidden */
  175. public _invertY: boolean = false;
  176. private _rowGenerationMatrix: Nullable<Matrix> = null;
  177. private _cachedTextureMatrix: Nullable<Matrix> = null;
  178. private _projectionModeMatrix: Nullable<Matrix> = null;
  179. private _t0: Nullable<Vector3> = null;
  180. private _t1: Nullable<Vector3> = null;
  181. private _t2: Nullable<Vector3> = null;
  182. private _cachedUOffset: number = -1;
  183. private _cachedVOffset: number = -1;
  184. private _cachedUScale: number = 0;
  185. private _cachedVScale: number = 0;
  186. private _cachedUAng: number = -1;
  187. private _cachedVAng: number = -1;
  188. private _cachedWAng: number = -1;
  189. private _cachedProjectionMatrixId: number = -1;
  190. private _cachedCoordinatesMode: number = -1;
  191. /** @hidden */
  192. protected _initialSamplingMode = Texture.BILINEAR_SAMPLINGMODE;
  193. /** @hidden */
  194. public _buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null;
  195. private _deleteBuffer: boolean = false;
  196. protected _format: Nullable<number> = null;
  197. private _delayedOnLoad: Nullable<() => void> = null;
  198. private _delayedOnError: Nullable<() => void> = null;
  199. private _mimeType?: string;
  200. /**
  201. * Observable triggered once the texture has been loaded.
  202. */
  203. public onLoadObservable: Observable<Texture> = new Observable<Texture>();
  204. protected _isBlocking: boolean = true;
  205. /**
  206. * Is the texture preventing material to render while loading.
  207. * If false, a default texture will be used instead of the loading one during the preparation step.
  208. */
  209. public set isBlocking(value: boolean) {
  210. this._isBlocking = value;
  211. }
  212. @serialize()
  213. public get isBlocking(): boolean {
  214. return this._isBlocking;
  215. }
  216. /**
  217. * Get the current sampling mode associated with the texture.
  218. */
  219. public get samplingMode(): number {
  220. if (!this._texture) {
  221. return this._initialSamplingMode;
  222. }
  223. return this._texture.samplingMode;
  224. }
  225. /**
  226. * Gets a boolean indicating if the texture needs to be inverted on the y axis during loading
  227. */
  228. public get invertY(): boolean {
  229. return this._invertY;
  230. }
  231. /**
  232. * Instantiates a new texture.
  233. * This represents a texture in babylon. It can be easily loaded from a network, base64 or html input.
  234. * @see https://doc.babylonjs.com/babylon101/materials#texture
  235. * @param url defines the url of the picture to load as a texture
  236. * @param sceneOrEngine defines the scene or engine the texture will belong to
  237. * @param noMipmap defines if the texture will require mip maps or not
  238. * @param invertY defines if the texture needs to be inverted on the y axis during loading
  239. * @param samplingMode defines the sampling mode we want for the texture while fectching from it (Texture.NEAREST_SAMPLINGMODE...)
  240. * @param onLoad defines a callback triggered when the texture has been loaded
  241. * @param onError defines a callback triggered when an error occurred during the loading session
  242. * @param buffer defines the buffer to load the texture from in case the texture is loaded from a buffer representation
  243. * @param deleteBuffer defines if the buffer we are loading the texture from should be deleted after load
  244. * @param format defines the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  245. * @param mimeType defines an optional mime type information
  246. */
  247. constructor(url: Nullable<string>, sceneOrEngine: Nullable<Scene | ThinEngine>, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, deleteBuffer: boolean = false, format?: number, mimeType?: string) {
  248. super(sceneOrEngine);
  249. this.name = url || "";
  250. this.url = url;
  251. this._noMipmap = noMipmap;
  252. this._invertY = invertY;
  253. this._initialSamplingMode = samplingMode;
  254. this._buffer = buffer;
  255. this._deleteBuffer = deleteBuffer;
  256. this._mimeType = mimeType;
  257. if (format) {
  258. this._format = format;
  259. }
  260. var scene = this.getScene();
  261. var engine = this._getEngine();
  262. if (!engine) {
  263. return;
  264. }
  265. engine.onBeforeTextureInitObservable.notifyObservers(this);
  266. let load = () => {
  267. if (this._texture) {
  268. if (this._texture._invertVScale) {
  269. this.vScale *= -1;
  270. this.vOffset += 1;
  271. }
  272. // Update texutre to match internal texture's wrapping
  273. if (this._texture._cachedWrapU !== null) {
  274. this.wrapU = this._texture._cachedWrapU;
  275. this._texture._cachedWrapU = null;
  276. }
  277. if (this._texture._cachedWrapV !== null) {
  278. this.wrapV = this._texture._cachedWrapV;
  279. this._texture._cachedWrapV = null;
  280. }
  281. if (this._texture._cachedWrapR !== null) {
  282. this.wrapR = this._texture._cachedWrapR;
  283. this._texture._cachedWrapR = null;
  284. }
  285. }
  286. if (this.onLoadObservable.hasObservers()) {
  287. this.onLoadObservable.notifyObservers(this);
  288. }
  289. if (onLoad) {
  290. onLoad();
  291. }
  292. if (!this.isBlocking && scene) {
  293. scene.resetCachedMaterial();
  294. }
  295. };
  296. if (!this.url) {
  297. this._delayedOnLoad = load;
  298. this._delayedOnError = onError;
  299. return;
  300. }
  301. this._texture = this._getFromCache(this.url, noMipmap, samplingMode, invertY);
  302. if (!this._texture) {
  303. if (!scene || !scene.useDelayedTextureLoading) {
  304. this._texture = engine.createTexture(this.url, noMipmap, invertY, scene, samplingMode, load, onError, this._buffer, undefined, this._format, null, mimeType);
  305. if (deleteBuffer) {
  306. delete this._buffer;
  307. }
  308. } else {
  309. this.delayLoadState = Constants.DELAYLOADSTATE_NOTLOADED;
  310. this._delayedOnLoad = load;
  311. this._delayedOnError = onError;
  312. }
  313. } else {
  314. if (this._texture.isReady) {
  315. TimingTools.SetImmediate(() => load());
  316. } else {
  317. this._texture.onLoadedObservable.add(load);
  318. }
  319. }
  320. }
  321. /**
  322. * Update the url (and optional buffer) of this texture if url was null during construction.
  323. * @param url the url of the texture
  324. * @param buffer the buffer of the texture (defaults to null)
  325. * @param onLoad callback called when the texture is loaded (defaults to null)
  326. */
  327. public updateURL(url: string, buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob> = null, onLoad?: () => void): void {
  328. if (this.url) {
  329. this.releaseInternalTexture();
  330. this.getScene()!.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
  331. }
  332. if (!this.name || StringTools.StartsWith(this.name, "data:")) {
  333. this.name = url;
  334. }
  335. this.url = url;
  336. this._buffer = buffer;
  337. this.delayLoadState = Constants.DELAYLOADSTATE_NOTLOADED;
  338. if (onLoad) {
  339. this._delayedOnLoad = onLoad;
  340. }
  341. this.delayLoad();
  342. }
  343. /**
  344. * Finish the loading sequence of a texture flagged as delayed load.
  345. * @hidden
  346. */
  347. public delayLoad(): void {
  348. if (this.delayLoadState !== Constants.DELAYLOADSTATE_NOTLOADED) {
  349. return;
  350. }
  351. let scene = this.getScene();
  352. if (!scene) {
  353. return;
  354. }
  355. this.delayLoadState = Constants.DELAYLOADSTATE_LOADED;
  356. this._texture = this._getFromCache(this.url, this._noMipmap, this.samplingMode, this._invertY);
  357. if (!this._texture) {
  358. this._texture = scene.getEngine().createTexture(this.url, this._noMipmap, this._invertY, scene, this.samplingMode, this._delayedOnLoad, this._delayedOnError, this._buffer, null, this._format, null, this._mimeType);
  359. if (this._deleteBuffer) {
  360. delete this._buffer;
  361. }
  362. } else {
  363. if (this._delayedOnLoad) {
  364. if (this._texture.isReady) {
  365. TimingTools.SetImmediate(this._delayedOnLoad);
  366. } else {
  367. this._texture.onLoadedObservable.add(this._delayedOnLoad);
  368. }
  369. }
  370. }
  371. this._delayedOnLoad = null;
  372. this._delayedOnError = null;
  373. }
  374. private _prepareRowForTextureGeneration(x: number, y: number, z: number, t: Vector3): void {
  375. x *= this._cachedUScale;
  376. y *= this._cachedVScale;
  377. x -= this.uRotationCenter * this._cachedUScale;
  378. y -= this.vRotationCenter * this._cachedVScale;
  379. z -= this.wRotationCenter;
  380. Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, this._rowGenerationMatrix!, t);
  381. t.x += this.uRotationCenter * this._cachedUScale + this._cachedUOffset;
  382. t.y += this.vRotationCenter * this._cachedVScale + this._cachedVOffset;
  383. t.z += this.wRotationCenter;
  384. }
  385. /**
  386. * Get the current texture matrix which includes the requested offsetting, tiling and rotation components.
  387. * @returns the transform matrix of the texture.
  388. */
  389. public getTextureMatrix(uBase = 1): Matrix {
  390. if (
  391. this.uOffset === this._cachedUOffset &&
  392. this.vOffset === this._cachedVOffset &&
  393. this.uScale * uBase === this._cachedUScale &&
  394. this.vScale === this._cachedVScale &&
  395. this.uAng === this._cachedUAng &&
  396. this.vAng === this._cachedVAng &&
  397. this.wAng === this._cachedWAng) {
  398. return this._cachedTextureMatrix!;
  399. }
  400. this._cachedUOffset = this.uOffset;
  401. this._cachedVOffset = this.vOffset;
  402. this._cachedUScale = this.uScale * uBase;
  403. this._cachedVScale = this.vScale;
  404. this._cachedUAng = this.uAng;
  405. this._cachedVAng = this.vAng;
  406. this._cachedWAng = this.wAng;
  407. if (!this._cachedTextureMatrix) {
  408. this._cachedTextureMatrix = Matrix.Zero();
  409. this._rowGenerationMatrix = new Matrix();
  410. this._t0 = Vector3.Zero();
  411. this._t1 = Vector3.Zero();
  412. this._t2 = Vector3.Zero();
  413. }
  414. Matrix.RotationYawPitchRollToRef(this.vAng, this.uAng, this.wAng, this._rowGenerationMatrix!);
  415. this._prepareRowForTextureGeneration(0, 0, 0, this._t0!);
  416. this._prepareRowForTextureGeneration(1.0, 0, 0, this._t1!);
  417. this._prepareRowForTextureGeneration(0, 1.0, 0, this._t2!);
  418. this._t1!.subtractInPlace(this._t0!);
  419. this._t2!.subtractInPlace(this._t0!);
  420. Matrix.FromValuesToRef(
  421. this._t1!.x, this._t1!.y, this._t1!.z, 0.0,
  422. this._t2!.x, this._t2!.y, this._t2!.z, 0.0,
  423. this._t0!.x, this._t0!.y, this._t0!.z, 0.0,
  424. 0.0, 0.0, 0.0, 1.0,
  425. this._cachedTextureMatrix
  426. );
  427. let scene = this.getScene();
  428. if (!scene) {
  429. return this._cachedTextureMatrix;
  430. }
  431. scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag, (mat) => {
  432. return mat.hasTexture(this);
  433. });
  434. return this._cachedTextureMatrix;
  435. }
  436. /**
  437. * Get the current matrix used to apply reflection. This is useful to rotate an environment texture for instance.
  438. * @returns The reflection texture transform
  439. */
  440. public getReflectionTextureMatrix(): Matrix {
  441. let scene = this.getScene();
  442. if (!scene) {
  443. return this._cachedTextureMatrix!;
  444. }
  445. if (
  446. this.uOffset === this._cachedUOffset &&
  447. this.vOffset === this._cachedVOffset &&
  448. this.uScale === this._cachedUScale &&
  449. this.vScale === this._cachedVScale &&
  450. this.coordinatesMode === this._cachedCoordinatesMode) {
  451. if (this.coordinatesMode === Texture.PROJECTION_MODE) {
  452. if (this._cachedProjectionMatrixId === scene.getProjectionMatrix().updateFlag) {
  453. return this._cachedTextureMatrix!;
  454. }
  455. } else {
  456. return this._cachedTextureMatrix!;
  457. }
  458. }
  459. if (!this._cachedTextureMatrix) {
  460. this._cachedTextureMatrix = Matrix.Zero();
  461. }
  462. if (!this._projectionModeMatrix) {
  463. this._projectionModeMatrix = Matrix.Zero();
  464. }
  465. this._cachedUOffset = this.uOffset;
  466. this._cachedVOffset = this.vOffset;
  467. this._cachedUScale = this.uScale;
  468. this._cachedVScale = this.vScale;
  469. this._cachedCoordinatesMode = this.coordinatesMode;
  470. switch (this.coordinatesMode) {
  471. case Texture.PLANAR_MODE:
  472. Matrix.IdentityToRef(this._cachedTextureMatrix);
  473. (<any>this._cachedTextureMatrix)[0] = this.uScale;
  474. (<any>this._cachedTextureMatrix)[5] = this.vScale;
  475. (<any>this._cachedTextureMatrix)[12] = this.uOffset;
  476. (<any>this._cachedTextureMatrix)[13] = this.vOffset;
  477. break;
  478. case Texture.PROJECTION_MODE:
  479. Matrix.FromValuesToRef(
  480. 0.5, 0.0, 0.0, 0.0,
  481. 0.0, -0.5, 0.0, 0.0,
  482. 0.0, 0.0, 0.0, 0.0,
  483. 0.5, 0.5, 1.0, 1.0,
  484. this._projectionModeMatrix
  485. );
  486. let projectionMatrix = scene.getProjectionMatrix();
  487. this._cachedProjectionMatrixId = projectionMatrix.updateFlag;
  488. projectionMatrix.multiplyToRef(this._projectionModeMatrix, this._cachedTextureMatrix);
  489. break;
  490. default:
  491. Matrix.IdentityToRef(this._cachedTextureMatrix);
  492. break;
  493. }
  494. scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag, (mat) => {
  495. return (mat.getActiveTextures().indexOf(this) !== -1);
  496. });
  497. return this._cachedTextureMatrix;
  498. }
  499. /**
  500. * Clones the texture.
  501. * @returns the cloned texture
  502. */
  503. public clone(): Texture {
  504. return SerializationHelper.Clone(() => {
  505. return new Texture(this._texture ? this._texture.url : null, this.getScene(), this._noMipmap, this._invertY, this.samplingMode, undefined, undefined, this._texture ? this._texture._buffer : undefined);
  506. }, this);
  507. }
  508. /**
  509. * Serialize the texture to a JSON representation we can easily use in the resepective Parse function.
  510. * @returns The JSON representation of the texture
  511. */
  512. public serialize(): any {
  513. let savedName = this.name;
  514. if (!Texture.SerializeBuffers) {
  515. if (StringTools.StartsWith(this.name, "data:")) {
  516. this.name = "";
  517. }
  518. }
  519. if (StringTools.StartsWith(this.name, "data:") && this.url === this.name) {
  520. this.url = "";
  521. }
  522. var serializationObject = super.serialize();
  523. if (!serializationObject) {
  524. return null;
  525. }
  526. if (Texture.SerializeBuffers) {
  527. if (typeof this._buffer === "string" && (this._buffer as string).substr(0, 5) === "data:") {
  528. serializationObject.base64String = this._buffer;
  529. serializationObject.name = serializationObject.name.replace("data:", "");
  530. } else if (this.url && StringTools.StartsWith(this.url, "data:") && this._buffer instanceof Uint8Array) {
  531. serializationObject.base64String = "data:image/png;base64," + StringTools.EncodeArrayBufferToBase64(this._buffer);
  532. }
  533. }
  534. serializationObject.invertY = this._invertY;
  535. serializationObject.samplingMode = this.samplingMode;
  536. this.name = savedName;
  537. return serializationObject;
  538. }
  539. /**
  540. * Get the current class name of the texture useful for serialization or dynamic coding.
  541. * @returns "Texture"
  542. */
  543. public getClassName(): string {
  544. return "Texture";
  545. }
  546. /**
  547. * Dispose the texture and release its associated resources.
  548. */
  549. public dispose(): void {
  550. super.dispose();
  551. this.onLoadObservable.clear();
  552. this._delayedOnLoad = null;
  553. this._delayedOnError = null;
  554. }
  555. /**
  556. * Parse the JSON representation of a texture in order to recreate the texture in the given scene.
  557. * @param parsedTexture Define the JSON representation of the texture
  558. * @param scene Define the scene the parsed texture should be instantiated in
  559. * @param rootUrl Define the root url of the parsing sequence in the case of relative dependencies
  560. * @returns The parsed texture if successful
  561. */
  562. public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): Nullable<BaseTexture> {
  563. if (parsedTexture.customType) {
  564. var customTexture = InstantiationTools.Instantiate(parsedTexture.customType);
  565. // Update Sampling Mode
  566. var parsedCustomTexture: any = customTexture.Parse(parsedTexture, scene, rootUrl);
  567. if (parsedTexture.samplingMode && parsedCustomTexture.updateSamplingMode && parsedCustomTexture._samplingMode) {
  568. if (parsedCustomTexture._samplingMode !== parsedTexture.samplingMode) {
  569. parsedCustomTexture.updateSamplingMode(parsedTexture.samplingMode);
  570. }
  571. }
  572. return parsedCustomTexture;
  573. }
  574. if (parsedTexture.isCube && !parsedTexture.isRenderTarget) {
  575. return Texture._CubeTextureParser(parsedTexture, scene, rootUrl);
  576. }
  577. if (!parsedTexture.name && !parsedTexture.isRenderTarget) {
  578. return null;
  579. }
  580. var texture = SerializationHelper.Parse(() => {
  581. var generateMipMaps: boolean = true;
  582. if (parsedTexture.noMipmap) {
  583. generateMipMaps = false;
  584. }
  585. if (parsedTexture.mirrorPlane) {
  586. var mirrorTexture = Texture._CreateMirror(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps);
  587. mirrorTexture._waitingRenderList = parsedTexture.renderList;
  588. mirrorTexture.mirrorPlane = Plane.FromArray(parsedTexture.mirrorPlane);
  589. return mirrorTexture;
  590. } else if (parsedTexture.isRenderTarget) {
  591. let renderTargetTexture: Nullable<RenderTargetTexture> = null;
  592. if (parsedTexture.isCube) {
  593. // Search for an existing reflection probe (which contains a cube render target texture)
  594. if (scene.reflectionProbes) {
  595. for (var index = 0; index < scene.reflectionProbes.length; index++) {
  596. const probe = scene.reflectionProbes[index];
  597. if (probe.name === parsedTexture.name) {
  598. return probe.cubeTexture;
  599. }
  600. }
  601. }
  602. } else {
  603. renderTargetTexture = Texture._CreateRenderTargetTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps);
  604. renderTargetTexture._waitingRenderList = parsedTexture.renderList;
  605. }
  606. return renderTargetTexture;
  607. } else {
  608. var texture: Texture;
  609. if (parsedTexture.base64String) {
  610. texture = Texture.CreateFromBase64String(parsedTexture.base64String, parsedTexture.name, scene, !generateMipMaps, parsedTexture.invertY);
  611. } else {
  612. let url: string;
  613. if (parsedTexture.name && parsedTexture.name.indexOf("://") > 0) {
  614. url = parsedTexture.name;
  615. }
  616. else {
  617. url = rootUrl + parsedTexture.name;
  618. }
  619. if (StringTools.StartsWith(parsedTexture.url, "data:") || (Texture.UseSerializedUrlIfAny && parsedTexture.url)) {
  620. url = parsedTexture.url;
  621. }
  622. texture = new Texture(url, scene, !generateMipMaps, parsedTexture.invertY);
  623. }
  624. return texture;
  625. }
  626. }, parsedTexture, scene);
  627. // Clear cache
  628. if (texture && texture._texture) {
  629. texture._texture._cachedWrapU = null;
  630. texture._texture._cachedWrapV = null;
  631. texture._texture._cachedWrapR = null;
  632. }
  633. // Update Sampling Mode
  634. if (parsedTexture.samplingMode) {
  635. var sampling: number = parsedTexture.samplingMode;
  636. if (texture && texture.samplingMode !== sampling) {
  637. texture.updateSamplingMode(sampling);
  638. }
  639. }
  640. // Animations
  641. if (texture && parsedTexture.animations) {
  642. for (var animationIndex = 0; animationIndex < parsedTexture.animations.length; animationIndex++) {
  643. var parsedAnimation = parsedTexture.animations[animationIndex];
  644. const internalClass = _TypeStore.GetClass("BABYLON.Animation");
  645. if (internalClass) {
  646. texture.animations.push(internalClass.Parse(parsedAnimation));
  647. }
  648. }
  649. }
  650. return texture;
  651. }
  652. /**
  653. * Creates a texture from its base 64 representation.
  654. * @param data Define the base64 payload without the data: prefix
  655. * @param name Define the name of the texture in the scene useful fo caching purpose for instance
  656. * @param scene Define the scene the texture should belong to
  657. * @param noMipmap Forces the texture to not create mip map information if true
  658. * @param invertY define if the texture needs to be inverted on the y axis during loading
  659. * @param samplingMode define the sampling mode we want for the texture while fectching from it (Texture.NEAREST_SAMPLINGMODE...)
  660. * @param onLoad define a callback triggered when the texture has been loaded
  661. * @param onError define a callback triggered when an error occurred during the loading session
  662. * @param format define the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  663. * @returns the created texture
  664. */
  665. public static CreateFromBase64String(data: string, name: string, scene: Scene, noMipmap?: boolean, invertY?: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
  666. onLoad: Nullable<() => void> = null, onError: Nullable<() => void> = null, format: number = Constants.TEXTUREFORMAT_RGBA): Texture {
  667. return new Texture("data:" + name, scene, noMipmap, invertY, samplingMode, onLoad, onError, data, false, format);
  668. }
  669. /**
  670. * Creates a texture from its data: representation. (data: will be added in case only the payload has been passed in)
  671. * @param data Define the base64 payload without the data: prefix
  672. * @param name Define the name of the texture in the scene useful fo caching purpose for instance
  673. * @param buffer define the buffer to load the texture from in case the texture is loaded from a buffer representation
  674. * @param scene Define the scene the texture should belong to
  675. * @param deleteBuffer define if the buffer we are loading the texture from should be deleted after load
  676. * @param noMipmap Forces the texture to not create mip map information if true
  677. * @param invertY define if the texture needs to be inverted on the y axis during loading
  678. * @param samplingMode define the sampling mode we want for the texture while fectching from it (Texture.NEAREST_SAMPLINGMODE...)
  679. * @param onLoad define a callback triggered when the texture has been loaded
  680. * @param onError define a callback triggered when an error occurred during the loading session
  681. * @param format define the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  682. * @returns the created texture
  683. */
  684. public static LoadFromDataString(name: string, buffer: any, scene: Scene, deleteBuffer: boolean = false, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
  685. onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format: number = Constants.TEXTUREFORMAT_RGBA): Texture {
  686. if (name.substr(0, 5) !== "data:") {
  687. name = "data:" + name;
  688. }
  689. return new Texture(name, scene, noMipmap, invertY, samplingMode, onLoad, onError, buffer, deleteBuffer, format);
  690. }
  691. }
  692. // References the dependencies.
  693. _TypeStore.RegisteredTypes["BABYLON.Texture"] = Texture;
  694. SerializationHelper._TextureParser = Texture.Parse;