babylon.texture.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. module BABYLON {
  2. export class Texture extends BaseTexture {
  3. // Constants
  4. public static NEAREST_SAMPLINGMODE = 1;
  5. public static NEAREST_NEAREST_MIPLINEAR = 1; // nearest is mag = nearest and min = nearest and mip = linear
  6. public static BILINEAR_SAMPLINGMODE = 2;
  7. public static LINEAR_LINEAR_MIPNEAREST = 2; // Bilinear is mag = linear and min = linear and mip = nearest
  8. public static TRILINEAR_SAMPLINGMODE = 3;
  9. public static LINEAR_LINEAR_MIPLINEAR = 3; // Trilinear is mag = linear and min = linear and mip = linear
  10. public static NEAREST_NEAREST_MIPNEAREST = 4;
  11. public static NEAREST_LINEAR_MIPNEAREST = 5;
  12. public static NEAREST_LINEAR_MIPLINEAR = 6;
  13. public static NEAREST_LINEAR = 7;
  14. public static NEAREST_NEAREST = 8;
  15. public static LINEAR_NEAREST_MIPNEAREST = 9;
  16. public static LINEAR_NEAREST_MIPLINEAR = 10;
  17. public static LINEAR_LINEAR = 11;
  18. public static LINEAR_NEAREST = 12;
  19. public static EXPLICIT_MODE = 0;
  20. public static SPHERICAL_MODE = 1;
  21. public static PLANAR_MODE = 2;
  22. public static CUBIC_MODE = 3;
  23. public static PROJECTION_MODE = 4;
  24. public static SKYBOX_MODE = 5;
  25. public static INVCUBIC_MODE = 6;
  26. public static EQUIRECTANGULAR_MODE = 7;
  27. public static FIXED_EQUIRECTANGULAR_MODE = 8;
  28. public static FIXED_EQUIRECTANGULAR_MIRRORED_MODE = 9;
  29. public static CLAMP_ADDRESSMODE = 0;
  30. public static WRAP_ADDRESSMODE = 1;
  31. public static MIRROR_ADDRESSMODE = 2;
  32. /**
  33. * 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
  34. */
  35. public static UseSerializedUrlIfAny = false;
  36. // Members
  37. @serialize()
  38. public url: Nullable<string>;
  39. @serialize()
  40. public uOffset = 0;
  41. @serialize()
  42. public vOffset = 0;
  43. @serialize()
  44. public uScale = 1.0;
  45. @serialize()
  46. public vScale = 1.0;
  47. @serialize()
  48. public uAng = 0;
  49. @serialize()
  50. public vAng = 0;
  51. @serialize()
  52. public wAng = 0;
  53. /**
  54. * Defines the center of rotation (U)
  55. */
  56. @serialize()
  57. public uRotationCenter = 0.5;
  58. /**
  59. * Defines the center of rotation (V)
  60. */
  61. @serialize()
  62. public vRotationCenter = 0.5;
  63. /**
  64. * Defines the center of rotation (W)
  65. */
  66. @serialize()
  67. public wRotationCenter = 0.5;
  68. get noMipmap(): boolean {
  69. return this._noMipmap;
  70. }
  71. private _noMipmap: boolean;
  72. public _invertY: boolean;
  73. private _rowGenerationMatrix: Matrix;
  74. private _cachedTextureMatrix: Matrix;
  75. private _projectionModeMatrix: Matrix;
  76. private _t0: Vector3;
  77. private _t1: Vector3;
  78. private _t2: Vector3;
  79. private _cachedUOffset: number;
  80. private _cachedVOffset: number;
  81. private _cachedUScale: number;
  82. private _cachedVScale: number;
  83. private _cachedUAng: number;
  84. private _cachedVAng: number;
  85. private _cachedWAng: number;
  86. private _cachedProjectionMatrixId: number;
  87. private _cachedCoordinatesMode: number;
  88. public _samplingMode: number;
  89. /** @hidden */
  90. public _buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob>;
  91. private _deleteBuffer: boolean;
  92. protected _format: Nullable<number>;
  93. private _delayedOnLoad: Nullable<() => void>;
  94. private _delayedOnError: Nullable<() => void>;
  95. protected _onLoadObservable: Nullable<Observable<Texture>>;
  96. protected _isBlocking: boolean = true;
  97. public set isBlocking(value: boolean) {
  98. this._isBlocking = value;
  99. }
  100. @serialize()
  101. public get isBlocking(): boolean {
  102. return this._isBlocking;
  103. }
  104. public get samplingMode(): number {
  105. return this._samplingMode;
  106. }
  107. constructor(url: Nullable<string>, scene: Nullable<Scene>, 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 | HTMLImageElement | Blob> = null, deleteBuffer: boolean = false, format?: number) {
  108. super(scene);
  109. this.name = url || "";
  110. this.url = url;
  111. this._noMipmap = noMipmap;
  112. this._invertY = invertY;
  113. this._samplingMode = samplingMode;
  114. this._buffer = buffer;
  115. this._deleteBuffer = deleteBuffer;
  116. if (format) {
  117. this._format = format;
  118. }
  119. scene = this.getScene();
  120. if (!scene) {
  121. return;
  122. }
  123. scene.getEngine().onBeforeTextureInitObservable.notifyObservers(this);
  124. let load = () => {
  125. if (this._onLoadObservable && this._onLoadObservable.hasObservers()) {
  126. this.onLoadObservable.notifyObservers(this);
  127. }
  128. if (onLoad) {
  129. onLoad();
  130. }
  131. if (!this.isBlocking && scene) {
  132. scene.resetCachedMaterial();
  133. }
  134. }
  135. if (!this.url) {
  136. this._delayedOnLoad = load;
  137. this._delayedOnError = onError;
  138. return;
  139. }
  140. this._texture = this._getFromCache(this.url, noMipmap, samplingMode);
  141. if (!this._texture) {
  142. if (!scene.useDelayedTextureLoading) {
  143. this._texture = scene.getEngine().createTexture(this.url, noMipmap, invertY, scene, this._samplingMode, load, onError, this._buffer, undefined, this._format);
  144. if (deleteBuffer) {
  145. delete this._buffer;
  146. }
  147. } else {
  148. this.delayLoadState = Engine.DELAYLOADSTATE_NOTLOADED;
  149. this._delayedOnLoad = load;
  150. this._delayedOnError = onError;
  151. }
  152. } else {
  153. if (this._texture.isReady) {
  154. Tools.SetImmediate(() => load());
  155. } else {
  156. this._texture.onLoadedObservable.add(load);
  157. }
  158. }
  159. }
  160. /**
  161. * Update the url (and optional buffer) of this texture if url was null during construction.
  162. * @param url the url of the texture
  163. * @param buffer the buffer of the texture (defaults to null)
  164. */
  165. public updateURL(url: string, buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null): void {
  166. if (this.url) {
  167. throw new Error("URL is already set");
  168. }
  169. this.url = url;
  170. this._buffer = buffer;
  171. this.delayLoadState = Engine.DELAYLOADSTATE_NOTLOADED;
  172. this.delayLoad();
  173. }
  174. public delayLoad(): void {
  175. if (this.delayLoadState !== Engine.DELAYLOADSTATE_NOTLOADED) {
  176. return;
  177. }
  178. let scene = this.getScene();
  179. if (!scene) {
  180. return;
  181. }
  182. this.delayLoadState = Engine.DELAYLOADSTATE_LOADED;
  183. this._texture = this._getFromCache(this.url, this._noMipmap, this._samplingMode);
  184. if (!this._texture) {
  185. this._texture = scene.getEngine().createTexture(this.url, this._noMipmap, this._invertY, scene, this._samplingMode, this._delayedOnLoad, this._delayedOnError, this._buffer, null, this._format);
  186. if (this._deleteBuffer) {
  187. delete this._buffer;
  188. }
  189. } else {
  190. if (this._delayedOnLoad) {
  191. if (this._texture.isReady) {
  192. Tools.SetImmediate(this._delayedOnLoad);
  193. } else {
  194. this._texture.onLoadedObservable.add(this._delayedOnLoad);
  195. }
  196. }
  197. }
  198. this._delayedOnLoad = null;
  199. this._delayedOnError = null;
  200. }
  201. public updateSamplingMode(samplingMode: number): void {
  202. if (!this._texture) {
  203. return;
  204. }
  205. let scene = this.getScene();
  206. if (!scene) {
  207. return;
  208. }
  209. this._samplingMode = samplingMode;
  210. scene.getEngine().updateTextureSamplingMode(samplingMode, this._texture);
  211. }
  212. private _prepareRowForTextureGeneration(x: number, y: number, z: number, t: Vector3): void {
  213. x *= this.uScale;
  214. y *= this.vScale;
  215. x -= this.uRotationCenter * this.uScale;
  216. y -= this.vRotationCenter * this.vScale;
  217. z -= this.wRotationCenter;
  218. Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, this._rowGenerationMatrix, t);
  219. t.x += this.uRotationCenter * this.uScale + this.uOffset;
  220. t.y += this.vRotationCenter * this.vScale + this.vOffset;
  221. t.z += this.wRotationCenter;
  222. }
  223. public getTextureMatrix(): Matrix {
  224. if (
  225. this.uOffset === this._cachedUOffset &&
  226. this.vOffset === this._cachedVOffset &&
  227. this.uScale === this._cachedUScale &&
  228. this.vScale === this._cachedVScale &&
  229. this.uAng === this._cachedUAng &&
  230. this.vAng === this._cachedVAng &&
  231. this.wAng === this._cachedWAng) {
  232. return this._cachedTextureMatrix;
  233. }
  234. this._cachedUOffset = this.uOffset;
  235. this._cachedVOffset = this.vOffset;
  236. this._cachedUScale = this.uScale;
  237. this._cachedVScale = this.vScale;
  238. this._cachedUAng = this.uAng;
  239. this._cachedVAng = this.vAng;
  240. this._cachedWAng = this.wAng;
  241. if (!this._cachedTextureMatrix) {
  242. this._cachedTextureMatrix = Matrix.Zero();
  243. this._rowGenerationMatrix = new Matrix();
  244. this._t0 = Vector3.Zero();
  245. this._t1 = Vector3.Zero();
  246. this._t2 = Vector3.Zero();
  247. }
  248. Matrix.RotationYawPitchRollToRef(this.vAng, this.uAng, this.wAng, this._rowGenerationMatrix);
  249. this._prepareRowForTextureGeneration(0, 0, 0, this._t0);
  250. this._prepareRowForTextureGeneration(1.0, 0, 0, this._t1);
  251. this._prepareRowForTextureGeneration(0, 1.0, 0, this._t2);
  252. this._t1.subtractInPlace(this._t0);
  253. this._t2.subtractInPlace(this._t0);
  254. Matrix.IdentityToRef(this._cachedTextureMatrix);
  255. this._cachedTextureMatrix.m[0] = this._t1.x; this._cachedTextureMatrix.m[1] = this._t1.y; this._cachedTextureMatrix.m[2] = this._t1.z;
  256. this._cachedTextureMatrix.m[4] = this._t2.x; this._cachedTextureMatrix.m[5] = this._t2.y; this._cachedTextureMatrix.m[6] = this._t2.z;
  257. this._cachedTextureMatrix.m[8] = this._t0.x; this._cachedTextureMatrix.m[9] = this._t0.y; this._cachedTextureMatrix.m[10] = this._t0.z;
  258. let scene = this.getScene();
  259. if (!scene) {
  260. return this._cachedTextureMatrix;
  261. }
  262. scene.markAllMaterialsAsDirty(Material.TextureDirtyFlag, (mat) => {
  263. return mat.hasTexture(this);
  264. });
  265. return this._cachedTextureMatrix;
  266. }
  267. public getReflectionTextureMatrix(): Matrix {
  268. let scene = this.getScene();
  269. if (!scene) {
  270. return this._cachedTextureMatrix;
  271. }
  272. if (
  273. this.uOffset === this._cachedUOffset &&
  274. this.vOffset === this._cachedVOffset &&
  275. this.uScale === this._cachedUScale &&
  276. this.vScale === this._cachedVScale &&
  277. this.coordinatesMode === this._cachedCoordinatesMode) {
  278. if (this.coordinatesMode === Texture.PROJECTION_MODE) {
  279. if (this._cachedProjectionMatrixId === scene.getProjectionMatrix().updateFlag) {
  280. return this._cachedTextureMatrix;
  281. }
  282. } else {
  283. return this._cachedTextureMatrix;
  284. }
  285. }
  286. if (!this._cachedTextureMatrix) {
  287. this._cachedTextureMatrix = Matrix.Zero();
  288. }
  289. if (!this._projectionModeMatrix) {
  290. this._projectionModeMatrix = Matrix.Zero();
  291. }
  292. this._cachedUOffset = this.uOffset;
  293. this._cachedVOffset = this.vOffset;
  294. this._cachedUScale = this.uScale;
  295. this._cachedVScale = this.vScale;
  296. this._cachedCoordinatesMode = this.coordinatesMode;
  297. switch (this.coordinatesMode) {
  298. case Texture.PLANAR_MODE:
  299. Matrix.IdentityToRef(this._cachedTextureMatrix);
  300. (<any>this._cachedTextureMatrix)[0] = this.uScale;
  301. (<any>this._cachedTextureMatrix)[5] = this.vScale;
  302. (<any>this._cachedTextureMatrix)[12] = this.uOffset;
  303. (<any>this._cachedTextureMatrix)[13] = this.vOffset;
  304. break;
  305. case Texture.PROJECTION_MODE:
  306. Matrix.IdentityToRef(this._projectionModeMatrix);
  307. this._projectionModeMatrix.m[0] = 0.5;
  308. this._projectionModeMatrix.m[5] = -0.5;
  309. this._projectionModeMatrix.m[10] = 0.0;
  310. this._projectionModeMatrix.m[12] = 0.5;
  311. this._projectionModeMatrix.m[13] = 0.5;
  312. this._projectionModeMatrix.m[14] = 1.0;
  313. this._projectionModeMatrix.m[15] = 1.0;
  314. let projectionMatrix = scene.getProjectionMatrix();
  315. this._cachedProjectionMatrixId = projectionMatrix.updateFlag;
  316. projectionMatrix.multiplyToRef(this._projectionModeMatrix, this._cachedTextureMatrix);
  317. break;
  318. default:
  319. Matrix.IdentityToRef(this._cachedTextureMatrix);
  320. break;
  321. }
  322. scene.markAllMaterialsAsDirty(Material.TextureDirtyFlag, (mat) => {
  323. return (mat.getActiveTextures().indexOf(this) !== -1);
  324. });
  325. return this._cachedTextureMatrix;
  326. }
  327. public clone(): Texture {
  328. return SerializationHelper.Clone(() => {
  329. return new Texture(this._texture ? this._texture.url : null, this.getScene(), this._noMipmap, this._invertY, this._samplingMode);
  330. }, this);
  331. }
  332. public get onLoadObservable(): Observable<Texture> {
  333. if (!this._onLoadObservable) {
  334. this._onLoadObservable = new Observable<Texture>();
  335. }
  336. return this._onLoadObservable;
  337. }
  338. public serialize(): any {
  339. var serializationObject = super.serialize();
  340. if (typeof this._buffer === "string" && (this._buffer as string).substr(0, 5) === "data:") {
  341. serializationObject.base64String = this._buffer;
  342. serializationObject.name = serializationObject.name.replace("data:", "");
  343. }
  344. serializationObject.invertY = this._invertY;
  345. serializationObject.samplingMode = this.samplingMode;
  346. return serializationObject;
  347. }
  348. public getClassName(): string {
  349. return "Texture";
  350. }
  351. public dispose(): void {
  352. super.dispose();
  353. if (this._onLoadObservable) {
  354. this._onLoadObservable.clear();
  355. this._onLoadObservable = null;
  356. }
  357. this._delayedOnLoad = null;
  358. this._delayedOnError = null;
  359. }
  360. // Statics
  361. public static CreateFromBase64String(data: string, name: string, scene: Scene, noMipmap?: boolean, invertY?: boolean, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
  362. onLoad: Nullable<() => void> = null, onError: Nullable<() => void> = null, format: number = Engine.TEXTUREFORMAT_RGBA): Texture {
  363. return new Texture("data:" + name, scene, noMipmap, invertY, samplingMode, onLoad, onError, data, false, format);
  364. }
  365. public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): Nullable<BaseTexture> {
  366. if (parsedTexture.customType) {
  367. var customTexture = Tools.Instantiate(parsedTexture.customType);
  368. // Update Sampling Mode
  369. var parsedCustomTexture: any = customTexture.Parse(parsedTexture, scene, rootUrl);
  370. if (parsedTexture.samplingMode && parsedCustomTexture.updateSamplingMode && parsedCustomTexture._samplingMode) {
  371. if (parsedCustomTexture._samplingMode !== parsedTexture.samplingMode) {
  372. parsedCustomTexture.updateSamplingMode(parsedTexture.samplingMode);
  373. }
  374. }
  375. return parsedCustomTexture;
  376. }
  377. if (parsedTexture.isCube) {
  378. return CubeTexture.Parse(parsedTexture, scene, rootUrl);
  379. }
  380. if (!parsedTexture.name && !parsedTexture.isRenderTarget) {
  381. return null;
  382. }
  383. var texture = SerializationHelper.Parse(() => {
  384. var generateMipMaps: boolean = true;
  385. if (parsedTexture.noMipmap) {
  386. generateMipMaps = false;
  387. }
  388. if (parsedTexture.mirrorPlane) {
  389. var mirrorTexture = new MirrorTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps);
  390. mirrorTexture._waitingRenderList = parsedTexture.renderList;
  391. mirrorTexture.mirrorPlane = Plane.FromArray(parsedTexture.mirrorPlane);
  392. return mirrorTexture;
  393. } else if (parsedTexture.isRenderTarget) {
  394. var renderTargetTexture = new RenderTargetTexture(parsedTexture.name, parsedTexture.renderTargetSize, scene, generateMipMaps);
  395. renderTargetTexture._waitingRenderList = parsedTexture.renderList;
  396. return renderTargetTexture;
  397. } else {
  398. var texture: Texture;
  399. if (parsedTexture.base64String) {
  400. texture = Texture.CreateFromBase64String(parsedTexture.base64String, parsedTexture.name, scene, !generateMipMaps);
  401. } else {
  402. let url = rootUrl + parsedTexture.name;
  403. if (Texture.UseSerializedUrlIfAny && parsedTexture.url ) {
  404. url = parsedTexture.url;
  405. }
  406. texture = new Texture(url, scene, !generateMipMaps, parsedTexture.invertY);
  407. }
  408. return texture;
  409. }
  410. }, parsedTexture, scene);
  411. // Update Sampling Mode
  412. if (parsedTexture.samplingMode) {
  413. var sampling: number = parsedTexture.samplingMode;
  414. if (texture._samplingMode !== sampling) {
  415. texture.updateSamplingMode(sampling);
  416. }
  417. }
  418. // Animations
  419. if (parsedTexture.animations) {
  420. for (var animationIndex = 0; animationIndex < parsedTexture.animations.length; animationIndex++) {
  421. var parsedAnimation = parsedTexture.animations[animationIndex];
  422. texture.animations.push(Animation.Parse(parsedAnimation));
  423. }
  424. }
  425. return texture;
  426. }
  427. public static LoadFromDataString(name: string, buffer: any, scene: Scene, deleteBuffer: boolean = false, noMipmap: boolean = false, invertY: boolean = true, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
  428. onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null, format: number = Engine.TEXTUREFORMAT_RGBA): Texture {
  429. if (name.substr(0, 5) !== "data:") {
  430. name = "data:" + name;
  431. }
  432. return new Texture(name, scene, noMipmap, invertY, samplingMode, onLoad, onError, buffer, deleteBuffer, format);
  433. }
  434. }
  435. }