engine.rawTexture.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. import { InternalTexture, InternalTextureSource } from "../../Materials/Textures/internalTexture.js";
  2. import { Logger } from "../../Misc/logger.js";
  3. import { ThinEngine } from "../thinEngine.js";
  4. import { IsExponentOfTwo } from "../../Misc/tools.functions.js";
  5. ThinEngine.prototype.updateRawTexture = function (texture, data, format, invertY, compression = null, type = 0, useSRGBBuffer = false) {
  6. if (!texture) {
  7. return;
  8. }
  9. // Babylon's internalSizedFomat but gl's texImage2D internalFormat
  10. const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format, useSRGBBuffer);
  11. // Babylon's internalFormat but gl's texImage2D format
  12. const internalFormat = this._getInternalFormat(format);
  13. const textureType = this._getWebGLTextureType(type);
  14. this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
  15. this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
  16. if (!this._doNotHandleContextLost) {
  17. texture._bufferView = data;
  18. texture.format = format;
  19. texture.type = type;
  20. texture.invertY = invertY;
  21. texture._compression = compression;
  22. }
  23. if (texture.width % 4 !== 0) {
  24. this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
  25. }
  26. if (compression && data) {
  27. this._gl.compressedTexImage2D(this._gl.TEXTURE_2D, 0, this.getCaps().s3tc[compression], texture.width, texture.height, 0, data);
  28. }
  29. else {
  30. this._gl.texImage2D(this._gl.TEXTURE_2D, 0, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, data);
  31. }
  32. if (texture.generateMipMaps) {
  33. this._gl.generateMipmap(this._gl.TEXTURE_2D);
  34. }
  35. this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
  36. // this.resetTextureCache();
  37. texture.isReady = true;
  38. };
  39. ThinEngine.prototype.createRawTexture = function (data, width, height, format, generateMipMaps, invertY, samplingMode, compression = null, type = 0,
  40. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  41. creationFlags = 0, useSRGBBuffer = false) {
  42. const texture = new InternalTexture(this, InternalTextureSource.Raw);
  43. texture.baseWidth = width;
  44. texture.baseHeight = height;
  45. texture.width = width;
  46. texture.height = height;
  47. texture.format = format;
  48. texture.generateMipMaps = generateMipMaps;
  49. texture.samplingMode = samplingMode;
  50. texture.invertY = invertY;
  51. texture._compression = compression;
  52. texture.type = type;
  53. texture._useSRGBBuffer = this._getUseSRGBBuffer(useSRGBBuffer, !generateMipMaps);
  54. if (!this._doNotHandleContextLost) {
  55. texture._bufferView = data;
  56. }
  57. this.updateRawTexture(texture, data, format, invertY, compression, type, texture._useSRGBBuffer);
  58. this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
  59. // Filters
  60. const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
  61. this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
  62. this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
  63. if (generateMipMaps) {
  64. this._gl.generateMipmap(this._gl.TEXTURE_2D);
  65. }
  66. this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
  67. this._internalTexturesCache.push(texture);
  68. return texture;
  69. };
  70. ThinEngine.prototype.createRawCubeTexture = function (data, size, format, type, generateMipMaps, invertY, samplingMode, compression = null) {
  71. const gl = this._gl;
  72. const texture = new InternalTexture(this, InternalTextureSource.CubeRaw);
  73. texture.isCube = true;
  74. texture.format = format;
  75. texture.type = type;
  76. if (!this._doNotHandleContextLost) {
  77. texture._bufferViewArray = data;
  78. }
  79. const textureType = this._getWebGLTextureType(type);
  80. let internalFormat = this._getInternalFormat(format);
  81. if (internalFormat === gl.RGB) {
  82. internalFormat = gl.RGBA;
  83. }
  84. // Mipmap generation needs a sized internal format that is both color-renderable and texture-filterable
  85. if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) {
  86. generateMipMaps = false;
  87. samplingMode = 1;
  88. Logger.Warn("Float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
  89. }
  90. else if (textureType === this._gl.HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) {
  91. generateMipMaps = false;
  92. samplingMode = 1;
  93. Logger.Warn("Half float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
  94. }
  95. else if (textureType === gl.FLOAT && !this._caps.textureFloatRender) {
  96. generateMipMaps = false;
  97. Logger.Warn("Render to float textures is not supported. Mipmap generation forced to false.");
  98. }
  99. else if (textureType === gl.HALF_FLOAT && !this._caps.colorBufferFloat) {
  100. generateMipMaps = false;
  101. Logger.Warn("Render to half float textures is not supported. Mipmap generation forced to false.");
  102. }
  103. const width = size;
  104. const height = width;
  105. texture.width = width;
  106. texture.height = height;
  107. texture.invertY = invertY;
  108. texture._compression = compression;
  109. // Double check on POT to generate Mips.
  110. const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && IsExponentOfTwo(texture.height));
  111. if (!isPot) {
  112. generateMipMaps = false;
  113. }
  114. // Upload data if needed. The texture won't be ready until then.
  115. if (data) {
  116. this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
  117. }
  118. else {
  119. const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
  120. const level = 0;
  121. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
  122. for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
  123. if (compression) {
  124. gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture.width, texture.height, 0, undefined);
  125. }
  126. else {
  127. gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, null);
  128. }
  129. }
  130. this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
  131. }
  132. this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true);
  133. // Filters
  134. if (data && generateMipMaps) {
  135. this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
  136. }
  137. const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
  138. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
  139. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min);
  140. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  141. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  142. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
  143. texture.generateMipMaps = generateMipMaps;
  144. texture.samplingMode = samplingMode;
  145. texture.isReady = true;
  146. return texture;
  147. };
  148. ThinEngine.prototype.updateRawCubeTexture = function (texture, data, format, type, invertY, compression = null, level = 0) {
  149. texture._bufferViewArray = data;
  150. texture.format = format;
  151. texture.type = type;
  152. texture.invertY = invertY;
  153. texture._compression = compression;
  154. const gl = this._gl;
  155. const textureType = this._getWebGLTextureType(type);
  156. let internalFormat = this._getInternalFormat(format);
  157. const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
  158. let needConversion = false;
  159. if (internalFormat === gl.RGB) {
  160. internalFormat = gl.RGBA;
  161. needConversion = true;
  162. }
  163. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
  164. this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
  165. if (texture.width % 4 !== 0) {
  166. gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
  167. }
  168. // Data are known to be in +X +Y +Z -X -Y -Z
  169. for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
  170. let faceData = data[faceIndex];
  171. if (compression) {
  172. gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture.width, texture.height, 0, faceData);
  173. }
  174. else {
  175. if (needConversion) {
  176. faceData = _convertRGBtoRGBATextureData(faceData, texture.width, texture.height, type);
  177. }
  178. gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, faceData);
  179. }
  180. }
  181. const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && IsExponentOfTwo(texture.height));
  182. if (isPot && texture.generateMipMaps && level === 0) {
  183. this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
  184. }
  185. this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
  186. // this.resetTextureCache();
  187. texture.isReady = true;
  188. };
  189. ThinEngine.prototype.createRawCubeTextureFromUrl = function (url, scene, size, format, type, noMipmap, callback, mipmapGenerator, onLoad = null, onError = null, samplingMode = 3, invertY = false) {
  190. const gl = this._gl;
  191. const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
  192. scene?.addPendingData(texture);
  193. texture.url = url;
  194. texture.isReady = false;
  195. this._internalTexturesCache.push(texture);
  196. const onerror = (request, exception) => {
  197. scene?.removePendingData(texture);
  198. if (onError && request) {
  199. onError(request.status + " " + request.statusText, exception);
  200. }
  201. };
  202. const internalCallback = (data) => {
  203. const width = texture.width;
  204. const faceDataArrays = callback(data);
  205. if (!faceDataArrays) {
  206. return;
  207. }
  208. if (mipmapGenerator) {
  209. const textureType = this._getWebGLTextureType(type);
  210. let internalFormat = this._getInternalFormat(format);
  211. const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
  212. let needConversion = false;
  213. if (internalFormat === gl.RGB) {
  214. internalFormat = gl.RGBA;
  215. needConversion = true;
  216. }
  217. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
  218. this._unpackFlipY(false);
  219. const mipData = mipmapGenerator(faceDataArrays);
  220. for (let level = 0; level < mipData.length; level++) {
  221. const mipSize = width >> level;
  222. for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
  223. let mipFaceData = mipData[level][faceIndex];
  224. if (needConversion) {
  225. mipFaceData = _convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
  226. }
  227. gl.texImage2D(faceIndex, level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
  228. }
  229. }
  230. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
  231. }
  232. else {
  233. this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY);
  234. }
  235. texture.isReady = true;
  236. // this.resetTextureCache();
  237. scene?.removePendingData(texture);
  238. texture.onLoadedObservable.notifyObservers(texture);
  239. texture.onLoadedObservable.clear();
  240. if (onLoad) {
  241. onLoad();
  242. }
  243. };
  244. this._loadFile(url, (data) => {
  245. internalCallback(data);
  246. }, undefined, scene?.offlineProvider, true, onerror);
  247. return texture;
  248. };
  249. /**
  250. * @internal
  251. */
  252. // eslint-disable-next-line @typescript-eslint/naming-convention
  253. function _convertRGBtoRGBATextureData(rgbData, width, height, textureType) {
  254. // Create new RGBA data container.
  255. let rgbaData;
  256. let val1 = 1;
  257. if (textureType === 1) {
  258. rgbaData = new Float32Array(width * height * 4);
  259. }
  260. else if (textureType === 2) {
  261. rgbaData = new Uint16Array(width * height * 4);
  262. val1 = 15360; // 15360 is the encoding of 1 in half float
  263. }
  264. else if (textureType === 7) {
  265. rgbaData = new Uint32Array(width * height * 4);
  266. }
  267. else {
  268. rgbaData = new Uint8Array(width * height * 4);
  269. }
  270. // Convert each pixel.
  271. for (let x = 0; x < width; x++) {
  272. for (let y = 0; y < height; y++) {
  273. const index = (y * width + x) * 3;
  274. const newIndex = (y * width + x) * 4;
  275. // Map Old Value to new value.
  276. rgbaData[newIndex + 0] = rgbData[index + 0];
  277. rgbaData[newIndex + 1] = rgbData[index + 1];
  278. rgbaData[newIndex + 2] = rgbData[index + 2];
  279. // Add fully opaque alpha channel.
  280. rgbaData[newIndex + 3] = val1;
  281. }
  282. }
  283. return rgbaData;
  284. }
  285. /**
  286. * Create a function for createRawTexture3D/createRawTexture2DArray
  287. * @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
  288. * @internal
  289. */
  290. // eslint-disable-next-line @typescript-eslint/naming-convention
  291. function _makeCreateRawTextureFunction(is3D) {
  292. return function (data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = 0) {
  293. const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
  294. const source = is3D ? InternalTextureSource.Raw3D : InternalTextureSource.Raw2DArray;
  295. const texture = new InternalTexture(this, source);
  296. texture.baseWidth = width;
  297. texture.baseHeight = height;
  298. texture.baseDepth = depth;
  299. texture.width = width;
  300. texture.height = height;
  301. texture.depth = depth;
  302. texture.format = format;
  303. texture.type = textureType;
  304. texture.generateMipMaps = generateMipMaps;
  305. texture.samplingMode = samplingMode;
  306. if (is3D) {
  307. texture.is3D = true;
  308. }
  309. else {
  310. texture.is2DArray = true;
  311. }
  312. if (!this._doNotHandleContextLost) {
  313. texture._bufferView = data;
  314. }
  315. if (is3D) {
  316. this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
  317. }
  318. else {
  319. this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
  320. }
  321. this._bindTextureDirectly(target, texture, true);
  322. // Filters
  323. const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
  324. this._gl.texParameteri(target, this._gl.TEXTURE_MAG_FILTER, filters.mag);
  325. this._gl.texParameteri(target, this._gl.TEXTURE_MIN_FILTER, filters.min);
  326. if (generateMipMaps) {
  327. this._gl.generateMipmap(target);
  328. }
  329. this._bindTextureDirectly(target, null);
  330. this._internalTexturesCache.push(texture);
  331. return texture;
  332. };
  333. }
  334. ThinEngine.prototype.createRawTexture2DArray = _makeCreateRawTextureFunction(false);
  335. ThinEngine.prototype.createRawTexture3D = _makeCreateRawTextureFunction(true);
  336. /**
  337. * Create a function for updateRawTexture3D/updateRawTexture2DArray
  338. * @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
  339. * @internal
  340. */
  341. // eslint-disable-next-line @typescript-eslint/naming-convention
  342. function _makeUpdateRawTextureFunction(is3D) {
  343. return function (texture, data, format, invertY, compression = null, textureType = 0) {
  344. const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
  345. const internalType = this._getWebGLTextureType(textureType);
  346. const internalFormat = this._getInternalFormat(format);
  347. const internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format);
  348. this._bindTextureDirectly(target, texture, true);
  349. this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
  350. if (!this._doNotHandleContextLost) {
  351. texture._bufferView = data;
  352. texture.format = format;
  353. texture.invertY = invertY;
  354. texture._compression = compression;
  355. }
  356. if (texture.width % 4 !== 0) {
  357. this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
  358. }
  359. if (compression && data) {
  360. this._gl.compressedTexImage3D(target, 0, this.getCaps().s3tc[compression], texture.width, texture.height, texture.depth, 0, data);
  361. }
  362. else {
  363. this._gl.texImage3D(target, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
  364. }
  365. if (texture.generateMipMaps) {
  366. this._gl.generateMipmap(target);
  367. }
  368. this._bindTextureDirectly(target, null);
  369. // this.resetTextureCache();
  370. texture.isReady = true;
  371. };
  372. }
  373. ThinEngine.prototype.updateRawTexture2DArray = _makeUpdateRawTextureFunction(false);
  374. ThinEngine.prototype.updateRawTexture3D = _makeUpdateRawTextureFunction(true);
  375. //# sourceMappingURL=engine.rawTexture.js.map