texture.js 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. import { __decorate } from "../../tslib.es6.js";
  2. import { serialize } from "../../Misc/decorators.js";
  3. import { Observable } from "../../Misc/observable.js";
  4. import { Matrix, TmpVectors, Vector3 } from "../../Maths/math.vector.js";
  5. import { BaseTexture } from "../../Materials/Textures/baseTexture.js";
  6. import { GetClass, RegisterClass } from "../../Misc/typeStore.js";
  7. import { _WarnImport } from "../../Misc/devTools.js";
  8. import { TimingTools } from "../../Misc/timingTools.js";
  9. import { InstantiationTools } from "../../Misc/instantiationTools.js";
  10. import { Plane } from "../../Maths/math.plane.js";
  11. import { EncodeArrayBufferToBase64 } from "../../Misc/stringTools.js";
  12. import { GenerateBase64StringFromTexture, GenerateBase64StringFromTextureAsync } from "../../Misc/copyTools.js";
  13. import { CompatibilityOptions } from "../../Compat/compatibilityOptions.js";
  14. import { SerializationHelper } from "../../Misc/decorators.serialization.js";
  15. /**
  16. * This represents a texture in babylon. It can be easily loaded from a network, base64 or html input.
  17. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction#texture
  18. */
  19. export class Texture extends BaseTexture {
  20. /**
  21. * @internal
  22. */
  23. static _CreateVideoTexture(name, src, scene, generateMipMaps = false, invertY = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, settings = {}, onError, format = 5) {
  24. throw _WarnImport("VideoTexture");
  25. }
  26. /**
  27. * Are mip maps generated for this texture or not.
  28. */
  29. get noMipmap() {
  30. return this._noMipmap;
  31. }
  32. /** Returns the texture mime type if it was defined by a loader (undefined else) */
  33. get mimeType() {
  34. return this._mimeType;
  35. }
  36. /**
  37. * Is the texture preventing material to render while loading.
  38. * If false, a default texture will be used instead of the loading one during the preparation step.
  39. */
  40. set isBlocking(value) {
  41. this._isBlocking = value;
  42. }
  43. get isBlocking() {
  44. return this._isBlocking;
  45. }
  46. /**
  47. * Gets a boolean indicating if the texture needs to be inverted on the y axis during loading
  48. */
  49. get invertY() {
  50. return this._invertY;
  51. }
  52. /**
  53. * Instantiates a new texture.
  54. * This represents a texture in babylon. It can be easily loaded from a network, base64 or html input.
  55. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/materials_introduction#texture
  56. * @param url defines the url of the picture to load as a texture
  57. * @param sceneOrEngine defines the scene or engine the texture will belong to
  58. * @param noMipmapOrOptions defines if the texture will require mip maps or not or set of all options to create the texture
  59. * @param invertY defines if the texture needs to be inverted on the y axis during loading
  60. * @param samplingMode defines the sampling mode we want for the texture while fetching from it (Texture.NEAREST_SAMPLINGMODE...)
  61. * @param onLoad defines a callback triggered when the texture has been loaded
  62. * @param onError defines a callback triggered when an error occurred during the loading session
  63. * @param buffer defines the buffer to load the texture from in case the texture is loaded from a buffer representation
  64. * @param deleteBuffer defines if the buffer we are loading the texture from should be deleted after load
  65. * @param format defines the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  66. * @param mimeType defines an optional mime type information
  67. * @param loaderOptions options to be passed to the loader
  68. * @param creationFlags specific flags to use when creating the texture (1 for storage textures, for eg)
  69. * @param forcedExtension defines the extension to use to pick the right loader
  70. */
  71. constructor(url, sceneOrEngine, noMipmapOrOptions, invertY, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, onLoad = null, onError = null, buffer = null, deleteBuffer = false, format, mimeType, loaderOptions, creationFlags, forcedExtension) {
  72. super(sceneOrEngine);
  73. /**
  74. * Define the url of the texture.
  75. */
  76. this.url = null;
  77. /**
  78. * Define an offset on the texture to offset the u coordinates of the UVs
  79. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials#offsetting
  80. */
  81. this.uOffset = 0;
  82. /**
  83. * Define an offset on the texture to offset the v coordinates of the UVs
  84. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials#offsetting
  85. */
  86. this.vOffset = 0;
  87. /**
  88. * Define an offset on the texture to scale the u coordinates of the UVs
  89. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials#tiling
  90. */
  91. this.uScale = 1.0;
  92. /**
  93. * Define an offset on the texture to scale the v coordinates of the UVs
  94. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials#tiling
  95. */
  96. this.vScale = 1.0;
  97. /**
  98. * Define an offset on the texture to rotate around the u coordinates of the UVs
  99. * The angle is defined in radians.
  100. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials
  101. */
  102. this.uAng = 0;
  103. /**
  104. * Define an offset on the texture to rotate around the v coordinates of the UVs
  105. * The angle is defined in radians.
  106. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials
  107. */
  108. this.vAng = 0;
  109. /**
  110. * Define an offset on the texture to rotate around the w coordinates of the UVs (in case of 3d texture)
  111. * The angle is defined in radians.
  112. * @see https://doc.babylonjs.com/features/featuresDeepDive/materials/using/moreMaterials
  113. */
  114. this.wAng = 0;
  115. /**
  116. * Defines the center of rotation (U)
  117. */
  118. this.uRotationCenter = 0.5;
  119. /**
  120. * Defines the center of rotation (V)
  121. */
  122. this.vRotationCenter = 0.5;
  123. /**
  124. * Defines the center of rotation (W)
  125. */
  126. this.wRotationCenter = 0.5;
  127. /**
  128. * Sets this property to true to avoid deformations when rotating the texture with non-uniform scaling
  129. */
  130. this.homogeneousRotationInUVTransform = false;
  131. /**
  132. * List of inspectable custom properties (used by the Inspector)
  133. * @see https://doc.babylonjs.com/toolsAndResources/inspector#extensibility
  134. */
  135. this.inspectableCustomProperties = null;
  136. /** @internal */
  137. this._noMipmap = false;
  138. /** @internal */
  139. this._invertY = false;
  140. this._rowGenerationMatrix = null;
  141. this._cachedTextureMatrix = null;
  142. this._projectionModeMatrix = null;
  143. this._t0 = null;
  144. this._t1 = null;
  145. this._t2 = null;
  146. this._cachedUOffset = -1;
  147. this._cachedVOffset = -1;
  148. this._cachedUScale = 0;
  149. this._cachedVScale = 0;
  150. this._cachedUAng = -1;
  151. this._cachedVAng = -1;
  152. this._cachedWAng = -1;
  153. this._cachedReflectionProjectionMatrixId = -1;
  154. this._cachedURotationCenter = -1;
  155. this._cachedVRotationCenter = -1;
  156. this._cachedWRotationCenter = -1;
  157. this._cachedHomogeneousRotationInUVTransform = false;
  158. this._cachedIdentity3x2 = true;
  159. this._cachedReflectionTextureMatrix = null;
  160. this._cachedReflectionUOffset = -1;
  161. this._cachedReflectionVOffset = -1;
  162. this._cachedReflectionUScale = 0;
  163. this._cachedReflectionVScale = 0;
  164. this._cachedReflectionCoordinatesMode = -1;
  165. /** @internal */
  166. this._buffer = null;
  167. this._deleteBuffer = false;
  168. this._format = null;
  169. this._delayedOnLoad = null;
  170. this._delayedOnError = null;
  171. /**
  172. * Observable triggered once the texture has been loaded.
  173. */
  174. this.onLoadObservable = new Observable();
  175. this._isBlocking = true;
  176. this.name = url || "";
  177. this.url = url;
  178. let noMipmap;
  179. let useSRGBBuffer = false;
  180. let internalTexture = null;
  181. let gammaSpace = true;
  182. if (typeof noMipmapOrOptions === "object" && noMipmapOrOptions !== null) {
  183. noMipmap = noMipmapOrOptions.noMipmap ?? false;
  184. invertY = noMipmapOrOptions.invertY ?? (CompatibilityOptions.UseOpenGLOrientationForUV ? false : true);
  185. samplingMode = noMipmapOrOptions.samplingMode ?? Texture.TRILINEAR_SAMPLINGMODE;
  186. onLoad = noMipmapOrOptions.onLoad ?? null;
  187. onError = noMipmapOrOptions.onError ?? null;
  188. buffer = noMipmapOrOptions.buffer ?? null;
  189. deleteBuffer = noMipmapOrOptions.deleteBuffer ?? false;
  190. format = noMipmapOrOptions.format;
  191. mimeType = noMipmapOrOptions.mimeType;
  192. loaderOptions = noMipmapOrOptions.loaderOptions;
  193. creationFlags = noMipmapOrOptions.creationFlags;
  194. useSRGBBuffer = noMipmapOrOptions.useSRGBBuffer ?? false;
  195. internalTexture = noMipmapOrOptions.internalTexture ?? null;
  196. gammaSpace = noMipmapOrOptions.gammaSpace ?? gammaSpace;
  197. }
  198. else {
  199. noMipmap = !!noMipmapOrOptions;
  200. }
  201. this._gammaSpace = gammaSpace;
  202. this._noMipmap = noMipmap;
  203. this._invertY = invertY === undefined ? (CompatibilityOptions.UseOpenGLOrientationForUV ? false : true) : invertY;
  204. this._initialSamplingMode = samplingMode;
  205. this._buffer = buffer;
  206. this._deleteBuffer = deleteBuffer;
  207. this._mimeType = mimeType;
  208. this._loaderOptions = loaderOptions;
  209. this._creationFlags = creationFlags;
  210. this._useSRGBBuffer = useSRGBBuffer;
  211. this._forcedExtension = forcedExtension;
  212. if (format) {
  213. this._format = format;
  214. }
  215. const scene = this.getScene();
  216. const engine = this._getEngine();
  217. if (!engine) {
  218. return;
  219. }
  220. engine.onBeforeTextureInitObservable.notifyObservers(this);
  221. const load = () => {
  222. if (this._texture) {
  223. if (this._texture._invertVScale) {
  224. this.vScale *= -1;
  225. this.vOffset += 1;
  226. }
  227. // Update texture to match internal texture's wrapping
  228. if (this._texture._cachedWrapU !== null) {
  229. this.wrapU = this._texture._cachedWrapU;
  230. this._texture._cachedWrapU = null;
  231. }
  232. if (this._texture._cachedWrapV !== null) {
  233. this.wrapV = this._texture._cachedWrapV;
  234. this._texture._cachedWrapV = null;
  235. }
  236. if (this._texture._cachedWrapR !== null) {
  237. this.wrapR = this._texture._cachedWrapR;
  238. this._texture._cachedWrapR = null;
  239. }
  240. }
  241. if (this.onLoadObservable.hasObservers()) {
  242. this.onLoadObservable.notifyObservers(this);
  243. }
  244. if (onLoad) {
  245. onLoad();
  246. }
  247. if (!this.isBlocking && scene) {
  248. scene.resetCachedMaterial();
  249. }
  250. };
  251. const errorHandler = (message, exception) => {
  252. this._loadingError = true;
  253. this._errorObject = { message, exception };
  254. if (onError) {
  255. onError(message, exception);
  256. }
  257. Texture.OnTextureLoadErrorObservable.notifyObservers(this);
  258. };
  259. if (!this.url && !internalTexture) {
  260. this._delayedOnLoad = load;
  261. this._delayedOnError = errorHandler;
  262. return;
  263. }
  264. this._texture = internalTexture ?? this._getFromCache(this.url, noMipmap, samplingMode, this._invertY, useSRGBBuffer, this.isCube);
  265. if (!this._texture) {
  266. if (!scene || !scene.useDelayedTextureLoading) {
  267. try {
  268. this._texture = engine.createTexture(this.url, noMipmap, this._invertY, scene, samplingMode, load, errorHandler, this._buffer, undefined, this._format, this._forcedExtension, mimeType, loaderOptions, creationFlags, useSRGBBuffer);
  269. }
  270. catch (e) {
  271. errorHandler("error loading", e);
  272. throw e;
  273. }
  274. if (deleteBuffer) {
  275. this._buffer = null;
  276. }
  277. }
  278. else {
  279. this.delayLoadState = 4;
  280. this._delayedOnLoad = load;
  281. this._delayedOnError = errorHandler;
  282. }
  283. }
  284. else {
  285. if (this._texture.isReady) {
  286. TimingTools.SetImmediate(() => load());
  287. }
  288. else {
  289. const loadObserver = this._texture.onLoadedObservable.add(load);
  290. this._texture.onErrorObservable.add((e) => {
  291. errorHandler(e.message, e.exception);
  292. this._texture?.onLoadedObservable.remove(loadObserver);
  293. });
  294. }
  295. }
  296. }
  297. /**
  298. * Update the url (and optional buffer) of this texture if url was null during construction.
  299. * @param url the url of the texture
  300. * @param buffer the buffer of the texture (defaults to null)
  301. * @param onLoad callback called when the texture is loaded (defaults to null)
  302. * @param forcedExtension defines the extension to use to pick the right loader
  303. */
  304. updateURL(url, buffer = null, onLoad, forcedExtension) {
  305. if (this.url) {
  306. this.releaseInternalTexture();
  307. this.getScene().markAllMaterialsAsDirty(1, (mat) => {
  308. return mat.hasTexture(this);
  309. });
  310. }
  311. if (!this.name || this.name.startsWith("data:")) {
  312. this.name = url;
  313. }
  314. this.url = url;
  315. this._buffer = buffer;
  316. this._forcedExtension = forcedExtension;
  317. this.delayLoadState = 4;
  318. if (onLoad) {
  319. this._delayedOnLoad = onLoad;
  320. }
  321. this.delayLoad();
  322. }
  323. /**
  324. * Finish the loading sequence of a texture flagged as delayed load.
  325. * @internal
  326. */
  327. delayLoad() {
  328. if (this.delayLoadState !== 4) {
  329. return;
  330. }
  331. const scene = this.getScene();
  332. if (!scene) {
  333. return;
  334. }
  335. this.delayLoadState = 1;
  336. this._texture = this._getFromCache(this.url, this._noMipmap, this.samplingMode, this._invertY, this._useSRGBBuffer, this.isCube);
  337. if (!this._texture) {
  338. this._texture = scene
  339. .getEngine()
  340. .createTexture(this.url, this._noMipmap, this._invertY, scene, this.samplingMode, this._delayedOnLoad, this._delayedOnError, this._buffer, null, this._format, this._forcedExtension, this._mimeType, this._loaderOptions, this._creationFlags, this._useSRGBBuffer);
  341. if (this._deleteBuffer) {
  342. this._buffer = null;
  343. }
  344. }
  345. else {
  346. if (this._delayedOnLoad) {
  347. if (this._texture.isReady) {
  348. TimingTools.SetImmediate(this._delayedOnLoad);
  349. }
  350. else {
  351. this._texture.onLoadedObservable.add(this._delayedOnLoad);
  352. }
  353. }
  354. }
  355. this._delayedOnLoad = null;
  356. this._delayedOnError = null;
  357. }
  358. _prepareRowForTextureGeneration(x, y, z, t) {
  359. x *= this._cachedUScale;
  360. y *= this._cachedVScale;
  361. x -= this.uRotationCenter * this._cachedUScale;
  362. y -= this.vRotationCenter * this._cachedVScale;
  363. z -= this.wRotationCenter;
  364. Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, this._rowGenerationMatrix, t);
  365. t.x += this.uRotationCenter * this._cachedUScale + this._cachedUOffset;
  366. t.y += this.vRotationCenter * this._cachedVScale + this._cachedVOffset;
  367. t.z += this.wRotationCenter;
  368. }
  369. /**
  370. * Get the current texture matrix which includes the requested offsetting, tiling and rotation components.
  371. * @param uBase The horizontal base offset multiplier (1 by default)
  372. * @returns the transform matrix of the texture.
  373. */
  374. getTextureMatrix(uBase = 1) {
  375. if (this.uOffset === this._cachedUOffset &&
  376. this.vOffset === this._cachedVOffset &&
  377. this.uScale * uBase === this._cachedUScale &&
  378. this.vScale === this._cachedVScale &&
  379. this.uAng === this._cachedUAng &&
  380. this.vAng === this._cachedVAng &&
  381. this.wAng === this._cachedWAng &&
  382. this.uRotationCenter === this._cachedURotationCenter &&
  383. this.vRotationCenter === this._cachedVRotationCenter &&
  384. this.wRotationCenter === this._cachedWRotationCenter &&
  385. this.homogeneousRotationInUVTransform === this._cachedHomogeneousRotationInUVTransform) {
  386. return this._cachedTextureMatrix;
  387. }
  388. this._cachedUOffset = this.uOffset;
  389. this._cachedVOffset = this.vOffset;
  390. this._cachedUScale = this.uScale * uBase;
  391. this._cachedVScale = this.vScale;
  392. this._cachedUAng = this.uAng;
  393. this._cachedVAng = this.vAng;
  394. this._cachedWAng = this.wAng;
  395. this._cachedURotationCenter = this.uRotationCenter;
  396. this._cachedVRotationCenter = this.vRotationCenter;
  397. this._cachedWRotationCenter = this.wRotationCenter;
  398. this._cachedHomogeneousRotationInUVTransform = this.homogeneousRotationInUVTransform;
  399. if (!this._cachedTextureMatrix || !this._rowGenerationMatrix) {
  400. this._cachedTextureMatrix = Matrix.Zero();
  401. this._rowGenerationMatrix = new Matrix();
  402. this._t0 = Vector3.Zero();
  403. this._t1 = Vector3.Zero();
  404. this._t2 = Vector3.Zero();
  405. }
  406. Matrix.RotationYawPitchRollToRef(this.vAng, this.uAng, this.wAng, this._rowGenerationMatrix);
  407. if (this.homogeneousRotationInUVTransform) {
  408. Matrix.TranslationToRef(-this._cachedURotationCenter, -this._cachedVRotationCenter, -this._cachedWRotationCenter, TmpVectors.Matrix[0]);
  409. Matrix.TranslationToRef(this._cachedURotationCenter, this._cachedVRotationCenter, this._cachedWRotationCenter, TmpVectors.Matrix[1]);
  410. Matrix.ScalingToRef(this._cachedUScale, this._cachedVScale, 0, TmpVectors.Matrix[2]);
  411. Matrix.TranslationToRef(this._cachedUOffset, this._cachedVOffset, 0, TmpVectors.Matrix[3]);
  412. TmpVectors.Matrix[0].multiplyToRef(this._rowGenerationMatrix, this._cachedTextureMatrix);
  413. this._cachedTextureMatrix.multiplyToRef(TmpVectors.Matrix[1], this._cachedTextureMatrix);
  414. this._cachedTextureMatrix.multiplyToRef(TmpVectors.Matrix[2], this._cachedTextureMatrix);
  415. this._cachedTextureMatrix.multiplyToRef(TmpVectors.Matrix[3], this._cachedTextureMatrix);
  416. // copy the translation row to the 3rd row of the matrix so that we don't need to update the shaders (which expects the translation to be on the 3rd row)
  417. this._cachedTextureMatrix.setRowFromFloats(2, this._cachedTextureMatrix.m[12], this._cachedTextureMatrix.m[13], this._cachedTextureMatrix.m[14], 1);
  418. }
  419. else {
  420. this._prepareRowForTextureGeneration(0, 0, 0, this._t0);
  421. this._prepareRowForTextureGeneration(1.0, 0, 0, this._t1);
  422. this._prepareRowForTextureGeneration(0, 1.0, 0, this._t2);
  423. this._t1.subtractInPlace(this._t0);
  424. this._t2.subtractInPlace(this._t0);
  425. Matrix.FromValuesToRef(this._t1.x, this._t1.y, this._t1.z, 0.0, this._t2.x, this._t2.y, this._t2.z, 0.0, this._t0.x, this._t0.y, this._t0.z, 0.0, 0.0, 0.0, 0.0, 1.0, this._cachedTextureMatrix);
  426. }
  427. const scene = this.getScene();
  428. if (!scene) {
  429. return this._cachedTextureMatrix;
  430. }
  431. const previousIdentity3x2 = this._cachedIdentity3x2;
  432. this._cachedIdentity3x2 = this._cachedTextureMatrix.isIdentityAs3x2();
  433. if (this.optimizeUVAllocation && previousIdentity3x2 !== this._cachedIdentity3x2) {
  434. // We flag the materials that are using this texture as "texture dirty" because depending on the fact that the matrix is the identity or not, some defines
  435. // will get different values (see PrepareDefinesForMergedUV), meaning we should regenerate the effect accordingly
  436. scene.markAllMaterialsAsDirty(1, (mat) => {
  437. return mat.hasTexture(this);
  438. });
  439. }
  440. return this._cachedTextureMatrix;
  441. }
  442. /**
  443. * Get the current matrix used to apply reflection. This is useful to rotate an environment texture for instance.
  444. * @returns The reflection texture transform
  445. */
  446. getReflectionTextureMatrix() {
  447. const scene = this.getScene();
  448. if (!scene) {
  449. return this._cachedReflectionTextureMatrix;
  450. }
  451. if (this.uOffset === this._cachedReflectionUOffset &&
  452. this.vOffset === this._cachedReflectionVOffset &&
  453. this.uScale === this._cachedReflectionUScale &&
  454. this.vScale === this._cachedReflectionVScale &&
  455. this.coordinatesMode === this._cachedReflectionCoordinatesMode) {
  456. if (this.coordinatesMode === Texture.PROJECTION_MODE) {
  457. if (this._cachedReflectionProjectionMatrixId === scene.getProjectionMatrix().updateFlag) {
  458. return this._cachedReflectionTextureMatrix;
  459. }
  460. }
  461. else {
  462. return this._cachedReflectionTextureMatrix;
  463. }
  464. }
  465. if (!this._cachedReflectionTextureMatrix) {
  466. this._cachedReflectionTextureMatrix = Matrix.Zero();
  467. }
  468. if (!this._projectionModeMatrix) {
  469. this._projectionModeMatrix = Matrix.Zero();
  470. }
  471. const flagMaterialsAsTextureDirty = this._cachedReflectionCoordinatesMode !== this.coordinatesMode;
  472. this._cachedReflectionUOffset = this.uOffset;
  473. this._cachedReflectionVOffset = this.vOffset;
  474. this._cachedReflectionUScale = this.uScale;
  475. this._cachedReflectionVScale = this.vScale;
  476. this._cachedReflectionCoordinatesMode = this.coordinatesMode;
  477. switch (this.coordinatesMode) {
  478. case Texture.PLANAR_MODE: {
  479. Matrix.IdentityToRef(this._cachedReflectionTextureMatrix);
  480. this._cachedReflectionTextureMatrix[0] = this.uScale;
  481. this._cachedReflectionTextureMatrix[5] = this.vScale;
  482. this._cachedReflectionTextureMatrix[12] = this.uOffset;
  483. this._cachedReflectionTextureMatrix[13] = this.vOffset;
  484. break;
  485. }
  486. case Texture.PROJECTION_MODE: {
  487. Matrix.FromValuesToRef(0.5, 0.0, 0.0, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 1.0, 1.0, this._projectionModeMatrix);
  488. const projectionMatrix = scene.getProjectionMatrix();
  489. this._cachedReflectionProjectionMatrixId = projectionMatrix.updateFlag;
  490. projectionMatrix.multiplyToRef(this._projectionModeMatrix, this._cachedReflectionTextureMatrix);
  491. break;
  492. }
  493. default:
  494. Matrix.IdentityToRef(this._cachedReflectionTextureMatrix);
  495. break;
  496. }
  497. if (flagMaterialsAsTextureDirty) {
  498. // We flag the materials that are using this texture as "texture dirty" if the coordinatesMode has changed.
  499. // Indeed, this property is used to set the value of some defines used to generate the effect (in material.isReadyForSubMesh), so we must make sure this code will be re-executed and the effect recreated if necessary
  500. scene.markAllMaterialsAsDirty(1, (mat) => {
  501. return mat.hasTexture(this);
  502. });
  503. }
  504. return this._cachedReflectionTextureMatrix;
  505. }
  506. /**
  507. * Clones the texture.
  508. * @returns the cloned texture
  509. */
  510. clone() {
  511. const options = {
  512. noMipmap: this._noMipmap,
  513. invertY: this._invertY,
  514. samplingMode: this.samplingMode,
  515. onLoad: undefined,
  516. onError: undefined,
  517. buffer: this._texture ? this._texture._buffer : undefined,
  518. deleteBuffer: this._deleteBuffer,
  519. format: this.textureFormat,
  520. mimeType: this.mimeType,
  521. loaderOptions: this._loaderOptions,
  522. creationFlags: this._creationFlags,
  523. useSRGBBuffer: this._useSRGBBuffer,
  524. };
  525. return SerializationHelper.Clone(() => {
  526. return new Texture(this._texture ? this._texture.url : null, this.getScene(), options);
  527. }, this);
  528. }
  529. /**
  530. * Serialize the texture to a JSON representation we can easily use in the respective Parse function.
  531. * @returns The JSON representation of the texture
  532. */
  533. serialize() {
  534. const savedName = this.name;
  535. if (!Texture.SerializeBuffers) {
  536. if (this.name.startsWith("data:")) {
  537. this.name = "";
  538. }
  539. }
  540. if (this.name.startsWith("data:") && this.url === this.name) {
  541. this.url = "";
  542. }
  543. const serializationObject = super.serialize(Texture._SerializeInternalTextureUniqueId);
  544. if (!serializationObject) {
  545. return null;
  546. }
  547. if (Texture.SerializeBuffers || Texture.ForceSerializeBuffers) {
  548. if (typeof this._buffer === "string" && this._buffer.substr(0, 5) === "data:") {
  549. serializationObject.base64String = this._buffer;
  550. serializationObject.name = serializationObject.name.replace("data:", "");
  551. }
  552. else if (this.url && this.url.startsWith("data:") && this._buffer instanceof Uint8Array) {
  553. serializationObject.base64String = "data:image/png;base64," + EncodeArrayBufferToBase64(this._buffer);
  554. }
  555. else if (Texture.ForceSerializeBuffers || (this.url && this.url.startsWith("blob:")) || this._forceSerialize) {
  556. serializationObject.base64String =
  557. !this._engine || this._engine._features.supportSyncTextureRead ? GenerateBase64StringFromTexture(this) : GenerateBase64StringFromTextureAsync(this);
  558. }
  559. }
  560. serializationObject.invertY = this._invertY;
  561. serializationObject.samplingMode = this.samplingMode;
  562. serializationObject._creationFlags = this._creationFlags;
  563. serializationObject._useSRGBBuffer = this._useSRGBBuffer;
  564. if (Texture._SerializeInternalTextureUniqueId) {
  565. serializationObject.internalTextureUniqueId = this._texture?.uniqueId ?? undefined;
  566. }
  567. serializationObject.noMipmap = this._noMipmap;
  568. this.name = savedName;
  569. return serializationObject;
  570. }
  571. /**
  572. * Get the current class name of the texture useful for serialization or dynamic coding.
  573. * @returns "Texture"
  574. */
  575. getClassName() {
  576. return "Texture";
  577. }
  578. /**
  579. * Dispose the texture and release its associated resources.
  580. */
  581. dispose() {
  582. super.dispose();
  583. this.onLoadObservable.clear();
  584. this._delayedOnLoad = null;
  585. this._delayedOnError = null;
  586. this._buffer = null;
  587. }
  588. /**
  589. * Parse the JSON representation of a texture in order to recreate the texture in the given scene.
  590. * @param parsedTexture Define the JSON representation of the texture
  591. * @param scene Define the scene the parsed texture should be instantiated in
  592. * @param rootUrl Define the root url of the parsing sequence in the case of relative dependencies
  593. * @returns The parsed texture if successful
  594. */
  595. static Parse(parsedTexture, scene, rootUrl) {
  596. if (parsedTexture.customType) {
  597. const customTexture = InstantiationTools.Instantiate(parsedTexture.customType);
  598. // Update Sampling Mode
  599. const parsedCustomTexture = customTexture.Parse(parsedTexture, scene, rootUrl);
  600. if (parsedTexture.samplingMode && parsedCustomTexture.updateSamplingMode && parsedCustomTexture._samplingMode) {
  601. if (parsedCustomTexture._samplingMode !== parsedTexture.samplingMode) {
  602. parsedCustomTexture.updateSamplingMode(parsedTexture.samplingMode);
  603. }
  604. }
  605. return parsedCustomTexture;
  606. }
  607. if (parsedTexture.isCube && !parsedTexture.isRenderTarget) {
  608. return Texture._CubeTextureParser(parsedTexture, scene, rootUrl);
  609. }
  610. const hasInternalTextureUniqueId = parsedTexture.internalTextureUniqueId !== undefined;
  611. if (!parsedTexture.name && !parsedTexture.isRenderTarget && !hasInternalTextureUniqueId) {
  612. return null;
  613. }
  614. let internalTexture;
  615. if (hasInternalTextureUniqueId) {
  616. const cache = scene.getEngine().getLoadedTexturesCache();
  617. for (const texture of cache) {
  618. if (texture.uniqueId === parsedTexture.internalTextureUniqueId) {
  619. internalTexture = texture;
  620. break;
  621. }
  622. }
  623. }
  624. const onLoaded = (texture) => {
  625. // Clear cache
  626. if (texture && texture._texture) {
  627. texture._texture._cachedWrapU = null;
  628. texture._texture._cachedWrapV = null;
  629. texture._texture._cachedWrapR = null;
  630. }
  631. // Update Sampling Mode
  632. if (parsedTexture.samplingMode) {
  633. const sampling = parsedTexture.samplingMode;
  634. if (texture && texture.samplingMode !== sampling) {
  635. texture.updateSamplingMode(sampling);
  636. }
  637. }
  638. // Animations
  639. if (texture && parsedTexture.animations) {
  640. for (let animationIndex = 0; animationIndex < parsedTexture.animations.length; animationIndex++) {
  641. const parsedAnimation = parsedTexture.animations[animationIndex];
  642. const internalClass = GetClass("BABYLON.Animation");
  643. if (internalClass) {
  644. texture.animations.push(internalClass.Parse(parsedAnimation));
  645. }
  646. }
  647. }
  648. if (hasInternalTextureUniqueId && !internalTexture) {
  649. texture?._texture?._setUniqueId(parsedTexture.internalTextureUniqueId);
  650. }
  651. };
  652. const texture = SerializationHelper.Parse(() => {
  653. let generateMipMaps = true;
  654. if (parsedTexture.noMipmap) {
  655. generateMipMaps = false;
  656. }
  657. if (parsedTexture.mirrorPlane) {
  658. const mirrorTexture = Texture._CreateMirror(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps);
  659. mirrorTexture._waitingRenderList = parsedTexture.renderList;
  660. mirrorTexture.mirrorPlane = Plane.FromArray(parsedTexture.mirrorPlane);
  661. onLoaded(mirrorTexture);
  662. return mirrorTexture;
  663. }
  664. else if (parsedTexture.isRenderTarget) {
  665. let renderTargetTexture = null;
  666. if (parsedTexture.isCube) {
  667. // Search for an existing reflection probe (which contains a cube render target texture)
  668. if (scene.reflectionProbes) {
  669. for (let index = 0; index < scene.reflectionProbes.length; index++) {
  670. const probe = scene.reflectionProbes[index];
  671. if (probe.name === parsedTexture.name) {
  672. return probe.cubeTexture;
  673. }
  674. }
  675. }
  676. }
  677. else {
  678. renderTargetTexture = Texture._CreateRenderTargetTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps, parsedTexture._creationFlags ?? 0);
  679. renderTargetTexture._waitingRenderList = parsedTexture.renderList;
  680. }
  681. onLoaded(renderTargetTexture);
  682. return renderTargetTexture;
  683. }
  684. else if (parsedTexture.isVideo) {
  685. const texture = Texture._CreateVideoTexture(rootUrl + (parsedTexture.url || parsedTexture.name), rootUrl + (parsedTexture.src || parsedTexture.url), scene, generateMipMaps, parsedTexture.invertY, parsedTexture.samplingMode, parsedTexture.settings || {});
  686. onLoaded(texture);
  687. return texture;
  688. }
  689. else {
  690. let texture;
  691. if (parsedTexture.base64String && !internalTexture) {
  692. // name and url are the same to ensure caching happens from the actual base64 string
  693. texture = Texture.CreateFromBase64String(parsedTexture.base64String, parsedTexture.base64String, scene, !generateMipMaps, parsedTexture.invertY, parsedTexture.samplingMode, () => {
  694. onLoaded(texture);
  695. }, parsedTexture._creationFlags ?? 0, parsedTexture._useSRGBBuffer ?? false);
  696. // prettier name to fit with the loaded data
  697. texture.name = parsedTexture.name;
  698. }
  699. else {
  700. let url;
  701. if (parsedTexture.name && (parsedTexture.name.indexOf("://") > 0 || parsedTexture.name.startsWith("data:"))) {
  702. url = parsedTexture.name;
  703. }
  704. else {
  705. url = rootUrl + parsedTexture.name;
  706. }
  707. if (parsedTexture.url && (parsedTexture.url.startsWith("data:") || Texture.UseSerializedUrlIfAny)) {
  708. url = parsedTexture.url;
  709. }
  710. const options = {
  711. noMipmap: !generateMipMaps,
  712. invertY: parsedTexture.invertY,
  713. samplingMode: parsedTexture.samplingMode,
  714. onLoad: () => {
  715. onLoaded(texture);
  716. },
  717. internalTexture,
  718. };
  719. texture = new Texture(url, scene, options);
  720. }
  721. return texture;
  722. }
  723. }, parsedTexture, scene);
  724. return texture;
  725. }
  726. /**
  727. * Creates a texture from its base 64 representation.
  728. * @param data Define the base64 payload without the data: prefix
  729. * @param name Define the name of the texture in the scene useful fo caching purpose for instance
  730. * @param scene Define the scene the texture should belong to
  731. * @param noMipmapOrOptions defines if the texture will require mip maps or not or set of all options to create the texture
  732. * @param invertY define if the texture needs to be inverted on the y axis during loading
  733. * @param samplingMode define the sampling mode we want for the texture while fetching from it (Texture.NEAREST_SAMPLINGMODE...)
  734. * @param onLoad define a callback triggered when the texture has been loaded
  735. * @param onError define a callback triggered when an error occurred during the loading session
  736. * @param format define the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  737. * @param creationFlags specific flags to use when creating the texture (1 for storage textures, for eg)
  738. * @param forcedExtension defines the extension to use to pick the right loader
  739. * @returns the created texture
  740. */
  741. static CreateFromBase64String(data, name, scene, noMipmapOrOptions, invertY, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, onLoad = null, onError = null, format = 5, creationFlags, forcedExtension) {
  742. return new Texture("data:" + name, scene, noMipmapOrOptions, invertY, samplingMode, onLoad, onError, data, false, format, undefined, undefined, creationFlags, forcedExtension);
  743. }
  744. /**
  745. * Creates a texture from its data: representation. (data: will be added in case only the payload has been passed in)
  746. * @param name Define the name of the texture in the scene useful fo caching purpose for instance
  747. * @param buffer define the buffer to load the texture from in case the texture is loaded from a buffer representation
  748. * @param scene Define the scene the texture should belong to
  749. * @param deleteBuffer define if the buffer we are loading the texture from should be deleted after load
  750. * @param noMipmapOrOptions defines if the texture will require mip maps or not or set of all options to create the texture
  751. * @param invertY define if the texture needs to be inverted on the y axis during loading
  752. * @param samplingMode define the sampling mode we want for the texture while fetching from it (Texture.NEAREST_SAMPLINGMODE...)
  753. * @param onLoad define a callback triggered when the texture has been loaded
  754. * @param onError define a callback triggered when an error occurred during the loading session
  755. * @param format define the format of the texture we are trying to load (Engine.TEXTUREFORMAT_RGBA...)
  756. * @param creationFlags specific flags to use when creating the texture (1 for storage textures, for eg)
  757. * @param forcedExtension defines the extension to use to pick the right loader
  758. * @returns the created texture
  759. */
  760. static LoadFromDataString(name, buffer, scene, deleteBuffer = false, noMipmapOrOptions, invertY = true, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, onLoad = null, onError = null, format = 5, creationFlags, forcedExtension) {
  761. if (name.substr(0, 5) !== "data:") {
  762. name = "data:" + name;
  763. }
  764. return new Texture(name, scene, noMipmapOrOptions, invertY, samplingMode, onLoad, onError, buffer, deleteBuffer, format, undefined, undefined, creationFlags, forcedExtension);
  765. }
  766. }
  767. /**
  768. * Gets or sets a general boolean used to indicate that textures containing direct data (buffers) must be saved as part of the serialization process
  769. */
  770. Texture.SerializeBuffers = true;
  771. /**
  772. * Gets or sets a general boolean used to indicate that texture buffers must be saved as part of the serialization process.
  773. * If no buffer exists, one will be created as base64 string from the internal webgl data.
  774. */
  775. Texture.ForceSerializeBuffers = false;
  776. /**
  777. * This observable will notify when any texture had a loading error
  778. */
  779. Texture.OnTextureLoadErrorObservable = new Observable();
  780. /** @internal */
  781. Texture._SerializeInternalTextureUniqueId = false;
  782. /**
  783. * @internal
  784. */
  785. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  786. Texture._CubeTextureParser = (jsonTexture, scene, rootUrl) => {
  787. throw _WarnImport("CubeTexture");
  788. };
  789. /**
  790. * @internal
  791. */
  792. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  793. Texture._CreateMirror = (name, renderTargetSize, scene, generateMipMaps) => {
  794. throw _WarnImport("MirrorTexture");
  795. };
  796. /**
  797. * @internal
  798. */
  799. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  800. Texture._CreateRenderTargetTexture = (name, renderTargetSize, scene, generateMipMaps, creationFlags) => {
  801. throw _WarnImport("RenderTargetTexture");
  802. };
  803. /** nearest is mag = nearest and min = nearest and no mip */
  804. Texture.NEAREST_SAMPLINGMODE = 1;
  805. /** nearest is mag = nearest and min = nearest and mip = linear */
  806. Texture.NEAREST_NEAREST_MIPLINEAR = 8; // nearest is mag = nearest and min = nearest and mip = linear
  807. /** Bilinear is mag = linear and min = linear and no mip */
  808. Texture.BILINEAR_SAMPLINGMODE = 2;
  809. /** Bilinear is mag = linear and min = linear and mip = nearest */
  810. Texture.LINEAR_LINEAR_MIPNEAREST = 11; // Bilinear is mag = linear and min = linear and mip = nearest
  811. /** Trilinear is mag = linear and min = linear and mip = linear */
  812. Texture.TRILINEAR_SAMPLINGMODE = 3;
  813. /** Trilinear is mag = linear and min = linear and mip = linear */
  814. Texture.LINEAR_LINEAR_MIPLINEAR = 3; // Trilinear is mag = linear and min = linear and mip = linear
  815. /** mag = nearest and min = nearest and mip = nearest */
  816. Texture.NEAREST_NEAREST_MIPNEAREST = 4;
  817. /** mag = nearest and min = linear and mip = nearest */
  818. Texture.NEAREST_LINEAR_MIPNEAREST = 5;
  819. /** mag = nearest and min = linear and mip = linear */
  820. Texture.NEAREST_LINEAR_MIPLINEAR = 6;
  821. /** mag = nearest and min = linear and mip = none */
  822. Texture.NEAREST_LINEAR = 7;
  823. /** mag = nearest and min = nearest and mip = none */
  824. Texture.NEAREST_NEAREST = 1;
  825. /** mag = linear and min = nearest and mip = nearest */
  826. Texture.LINEAR_NEAREST_MIPNEAREST = 9;
  827. /** mag = linear and min = nearest and mip = linear */
  828. Texture.LINEAR_NEAREST_MIPLINEAR = 10;
  829. /** mag = linear and min = linear and mip = none */
  830. Texture.LINEAR_LINEAR = 2;
  831. /** mag = linear and min = nearest and mip = none */
  832. Texture.LINEAR_NEAREST = 12;
  833. /** Explicit coordinates mode */
  834. Texture.EXPLICIT_MODE = 0;
  835. /** Spherical coordinates mode */
  836. Texture.SPHERICAL_MODE = 1;
  837. /** Planar coordinates mode */
  838. Texture.PLANAR_MODE = 2;
  839. /** Cubic coordinates mode */
  840. Texture.CUBIC_MODE = 3;
  841. /** Projection coordinates mode */
  842. Texture.PROJECTION_MODE = 4;
  843. /** Inverse Cubic coordinates mode */
  844. Texture.SKYBOX_MODE = 5;
  845. /** Inverse Cubic coordinates mode */
  846. Texture.INVCUBIC_MODE = 6;
  847. /** Equirectangular coordinates mode */
  848. Texture.EQUIRECTANGULAR_MODE = 7;
  849. /** Equirectangular Fixed coordinates mode */
  850. Texture.FIXED_EQUIRECTANGULAR_MODE = 8;
  851. /** Equirectangular Fixed Mirrored coordinates mode */
  852. Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE = 9;
  853. /** Texture is not repeating outside of 0..1 UVs */
  854. Texture.CLAMP_ADDRESSMODE = 0;
  855. /** Texture is repeating outside of 0..1 UVs */
  856. Texture.WRAP_ADDRESSMODE = 1;
  857. /** Texture is repeating and mirrored */
  858. Texture.MIRROR_ADDRESSMODE = 2;
  859. /**
  860. * 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
  861. */
  862. Texture.UseSerializedUrlIfAny = false;
  863. __decorate([
  864. serialize()
  865. ], Texture.prototype, "url", void 0);
  866. __decorate([
  867. serialize()
  868. ], Texture.prototype, "uOffset", void 0);
  869. __decorate([
  870. serialize()
  871. ], Texture.prototype, "vOffset", void 0);
  872. __decorate([
  873. serialize()
  874. ], Texture.prototype, "uScale", void 0);
  875. __decorate([
  876. serialize()
  877. ], Texture.prototype, "vScale", void 0);
  878. __decorate([
  879. serialize()
  880. ], Texture.prototype, "uAng", void 0);
  881. __decorate([
  882. serialize()
  883. ], Texture.prototype, "vAng", void 0);
  884. __decorate([
  885. serialize()
  886. ], Texture.prototype, "wAng", void 0);
  887. __decorate([
  888. serialize()
  889. ], Texture.prototype, "uRotationCenter", void 0);
  890. __decorate([
  891. serialize()
  892. ], Texture.prototype, "vRotationCenter", void 0);
  893. __decorate([
  894. serialize()
  895. ], Texture.prototype, "wRotationCenter", void 0);
  896. __decorate([
  897. serialize()
  898. ], Texture.prototype, "homogeneousRotationInUVTransform", void 0);
  899. __decorate([
  900. serialize()
  901. ], Texture.prototype, "isBlocking", null);
  902. // References the dependencies.
  903. RegisterClass("BABYLON.Texture", Texture);
  904. SerializationHelper._TextureParser = Texture.Parse;
  905. //# sourceMappingURL=texture.js.map