dds.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /* eslint-disable @typescript-eslint/naming-convention */
  2. import { Scalar } from "../Maths/math.scalar.js";
  3. import { SphericalPolynomial } from "../Maths/sphericalPolynomial.js";
  4. import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture.js";
  5. import { Logger } from "../Misc/logger.js";
  6. import { CubeMapToSphericalPolynomialTools } from "../Misc/HighDynamicRange/cubemapToSphericalPolynomial.js";
  7. import { BaseTexture } from "../Materials/Textures/baseTexture.js";
  8. import { FromHalfFloat, ToHalfFloat } from "./textureTools.js";
  9. import "../Engines/AbstractEngine/abstractEngine.cubeTexture.js";
  10. import "../Engines/Extensions/engine.cubeTexture.js";
  11. import { ThinEngine } from "../Engines/thinEngine.js";
  12. // Based on demo done by Brandon Jones - http://media.tojicode.com/webgl-samples/dds.html
  13. // All values and structures referenced from:
  14. // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
  15. const DDS_MAGIC = 0x20534444;
  16. const //DDSD_CAPS = 0x1,
  17. //DDSD_HEIGHT = 0x2,
  18. //DDSD_WIDTH = 0x4,
  19. //DDSD_PITCH = 0x8,
  20. //DDSD_PIXELFORMAT = 0x1000,
  21. DDSD_MIPMAPCOUNT = 0x20000;
  22. //DDSD_LINEARSIZE = 0x80000,
  23. //DDSD_DEPTH = 0x800000;
  24. // var DDSCAPS_COMPLEX = 0x8,
  25. // DDSCAPS_MIPMAP = 0x400000,
  26. // DDSCAPS_TEXTURE = 0x1000;
  27. const DDSCAPS2_CUBEMAP = 0x200;
  28. // DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
  29. // DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
  30. // DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
  31. // DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
  32. // DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
  33. // DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
  34. // DDSCAPS2_VOLUME = 0x200000;
  35. const //DDPF_ALPHAPIXELS = 0x1,
  36. //DDPF_ALPHA = 0x2,
  37. DDPF_FOURCC = 0x4, DDPF_RGB = 0x40,
  38. //DDPF_YUV = 0x200,
  39. DDPF_LUMINANCE = 0x20000;
  40. function FourCCToInt32(value) {
  41. return value.charCodeAt(0) + (value.charCodeAt(1) << 8) + (value.charCodeAt(2) << 16) + (value.charCodeAt(3) << 24);
  42. }
  43. function Int32ToFourCC(value) {
  44. return String.fromCharCode(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, (value >> 24) & 0xff);
  45. }
  46. const FOURCC_DXT1 = FourCCToInt32("DXT1");
  47. const FOURCC_DXT3 = FourCCToInt32("DXT3");
  48. const FOURCC_DXT5 = FourCCToInt32("DXT5");
  49. const FOURCC_DX10 = FourCCToInt32("DX10");
  50. const FOURCC_D3DFMT_R16G16B16A16F = 113;
  51. const FOURCC_D3DFMT_R32G32B32A32F = 116;
  52. const DXGI_FORMAT_R32G32B32A32_FLOAT = 2;
  53. const DXGI_FORMAT_R16G16B16A16_FLOAT = 10;
  54. const DXGI_FORMAT_B8G8R8X8_UNORM = 88;
  55. const headerLengthInt = 31; // The header length in 32 bit ints
  56. // Offsets into the header array
  57. const off_magic = 0;
  58. const off_size = 1;
  59. const off_flags = 2;
  60. const off_height = 3;
  61. const off_width = 4;
  62. const off_mipmapCount = 7;
  63. const off_pfFlags = 20;
  64. const off_pfFourCC = 21;
  65. const off_RGBbpp = 22;
  66. const off_RMask = 23;
  67. const off_GMask = 24;
  68. const off_BMask = 25;
  69. const off_AMask = 26;
  70. // var off_caps1 = 27;
  71. const off_caps2 = 28;
  72. // var off_caps3 = 29;
  73. // var off_caps4 = 30;
  74. const off_dxgiFormat = 32;
  75. /**
  76. * Class used to provide DDS decompression tools
  77. */
  78. export class DDSTools {
  79. /**
  80. * Gets DDS information from an array buffer
  81. * @param data defines the array buffer view to read data from
  82. * @returns the DDS information
  83. */
  84. static GetDDSInfo(data) {
  85. const header = new Int32Array(data.buffer, data.byteOffset, headerLengthInt);
  86. const extendedHeader = new Int32Array(data.buffer, data.byteOffset, headerLengthInt + 4);
  87. let mipmapCount = 1;
  88. if (header[off_flags] & DDSD_MIPMAPCOUNT) {
  89. mipmapCount = Math.max(1, header[off_mipmapCount]);
  90. }
  91. const fourCC = header[off_pfFourCC];
  92. const dxgiFormat = fourCC === FOURCC_DX10 ? extendedHeader[off_dxgiFormat] : 0;
  93. let textureType = 0;
  94. switch (fourCC) {
  95. case FOURCC_D3DFMT_R16G16B16A16F:
  96. textureType = 2;
  97. break;
  98. case FOURCC_D3DFMT_R32G32B32A32F:
  99. textureType = 1;
  100. break;
  101. case FOURCC_DX10:
  102. if (dxgiFormat === DXGI_FORMAT_R16G16B16A16_FLOAT) {
  103. textureType = 2;
  104. break;
  105. }
  106. if (dxgiFormat === DXGI_FORMAT_R32G32B32A32_FLOAT) {
  107. textureType = 1;
  108. break;
  109. }
  110. }
  111. return {
  112. width: header[off_width],
  113. height: header[off_height],
  114. mipmapCount: mipmapCount,
  115. isFourCC: (header[off_pfFlags] & DDPF_FOURCC) === DDPF_FOURCC,
  116. isRGB: (header[off_pfFlags] & DDPF_RGB) === DDPF_RGB,
  117. isLuminance: (header[off_pfFlags] & DDPF_LUMINANCE) === DDPF_LUMINANCE,
  118. isCube: (header[off_caps2] & DDSCAPS2_CUBEMAP) === DDSCAPS2_CUBEMAP,
  119. isCompressed: fourCC === FOURCC_DXT1 || fourCC === FOURCC_DXT3 || fourCC === FOURCC_DXT5,
  120. dxgiFormat: dxgiFormat,
  121. textureType: textureType,
  122. };
  123. }
  124. static _GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  125. const destArray = new Float32Array(dataLength);
  126. const srcData = new Uint16Array(arrayBuffer, dataOffset);
  127. let index = 0;
  128. for (let y = 0; y < height; y++) {
  129. for (let x = 0; x < width; x++) {
  130. const srcPos = (x + y * width) * 4;
  131. destArray[index] = FromHalfFloat(srcData[srcPos]);
  132. destArray[index + 1] = FromHalfFloat(srcData[srcPos + 1]);
  133. destArray[index + 2] = FromHalfFloat(srcData[srcPos + 2]);
  134. if (DDSTools.StoreLODInAlphaChannel) {
  135. destArray[index + 3] = lod;
  136. }
  137. else {
  138. destArray[index + 3] = FromHalfFloat(srcData[srcPos + 3]);
  139. }
  140. index += 4;
  141. }
  142. }
  143. return destArray;
  144. }
  145. static _GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  146. if (DDSTools.StoreLODInAlphaChannel) {
  147. const destArray = new Uint16Array(dataLength);
  148. const srcData = new Uint16Array(arrayBuffer, dataOffset);
  149. let index = 0;
  150. for (let y = 0; y < height; y++) {
  151. for (let x = 0; x < width; x++) {
  152. const srcPos = (x + y * width) * 4;
  153. destArray[index] = srcData[srcPos];
  154. destArray[index + 1] = srcData[srcPos + 1];
  155. destArray[index + 2] = srcData[srcPos + 2];
  156. destArray[index + 3] = ToHalfFloat(lod);
  157. index += 4;
  158. }
  159. }
  160. return destArray;
  161. }
  162. return new Uint16Array(arrayBuffer, dataOffset, dataLength);
  163. }
  164. static _GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  165. if (DDSTools.StoreLODInAlphaChannel) {
  166. const destArray = new Float32Array(dataLength);
  167. const srcData = new Float32Array(arrayBuffer, dataOffset);
  168. let index = 0;
  169. for (let y = 0; y < height; y++) {
  170. for (let x = 0; x < width; x++) {
  171. const srcPos = (x + y * width) * 4;
  172. destArray[index] = srcData[srcPos];
  173. destArray[index + 1] = srcData[srcPos + 1];
  174. destArray[index + 2] = srcData[srcPos + 2];
  175. destArray[index + 3] = lod;
  176. index += 4;
  177. }
  178. }
  179. return destArray;
  180. }
  181. return new Float32Array(arrayBuffer, dataOffset, dataLength);
  182. }
  183. static _GetFloatAsHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  184. const destArray = new Uint16Array(dataLength);
  185. const srcData = new Float32Array(arrayBuffer, dataOffset);
  186. let index = 0;
  187. for (let y = 0; y < height; y++) {
  188. for (let x = 0; x < width; x++) {
  189. destArray[index] = ToHalfFloat(srcData[index]);
  190. destArray[index + 1] = ToHalfFloat(srcData[index + 1]);
  191. destArray[index + 2] = ToHalfFloat(srcData[index + 2]);
  192. if (DDSTools.StoreLODInAlphaChannel) {
  193. destArray[index + 3] = ToHalfFloat(lod);
  194. }
  195. else {
  196. destArray[index + 3] = ToHalfFloat(srcData[index + 3]);
  197. }
  198. index += 4;
  199. }
  200. }
  201. return destArray;
  202. }
  203. static _GetFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  204. const destArray = new Uint8Array(dataLength);
  205. const srcData = new Float32Array(arrayBuffer, dataOffset);
  206. let index = 0;
  207. for (let y = 0; y < height; y++) {
  208. for (let x = 0; x < width; x++) {
  209. const srcPos = (x + y * width) * 4;
  210. destArray[index] = Scalar.Clamp(srcData[srcPos]) * 255;
  211. destArray[index + 1] = Scalar.Clamp(srcData[srcPos + 1]) * 255;
  212. destArray[index + 2] = Scalar.Clamp(srcData[srcPos + 2]) * 255;
  213. if (DDSTools.StoreLODInAlphaChannel) {
  214. destArray[index + 3] = lod;
  215. }
  216. else {
  217. destArray[index + 3] = Scalar.Clamp(srcData[srcPos + 3]) * 255;
  218. }
  219. index += 4;
  220. }
  221. }
  222. return destArray;
  223. }
  224. static _GetHalfFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, lod) {
  225. const destArray = new Uint8Array(dataLength);
  226. const srcData = new Uint16Array(arrayBuffer, dataOffset);
  227. let index = 0;
  228. for (let y = 0; y < height; y++) {
  229. for (let x = 0; x < width; x++) {
  230. const srcPos = (x + y * width) * 4;
  231. destArray[index] = Scalar.Clamp(FromHalfFloat(srcData[srcPos])) * 255;
  232. destArray[index + 1] = Scalar.Clamp(FromHalfFloat(srcData[srcPos + 1])) * 255;
  233. destArray[index + 2] = Scalar.Clamp(FromHalfFloat(srcData[srcPos + 2])) * 255;
  234. if (DDSTools.StoreLODInAlphaChannel) {
  235. destArray[index + 3] = lod;
  236. }
  237. else {
  238. destArray[index + 3] = Scalar.Clamp(FromHalfFloat(srcData[srcPos + 3])) * 255;
  239. }
  240. index += 4;
  241. }
  242. }
  243. return destArray;
  244. }
  245. static _GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset, aOffset) {
  246. const byteArray = new Uint8Array(dataLength);
  247. const srcData = new Uint8Array(arrayBuffer, dataOffset);
  248. let index = 0;
  249. for (let y = 0; y < height; y++) {
  250. for (let x = 0; x < width; x++) {
  251. const srcPos = (x + y * width) * 4;
  252. byteArray[index] = srcData[srcPos + rOffset];
  253. byteArray[index + 1] = srcData[srcPos + gOffset];
  254. byteArray[index + 2] = srcData[srcPos + bOffset];
  255. byteArray[index + 3] = srcData[srcPos + aOffset];
  256. index += 4;
  257. }
  258. }
  259. return byteArray;
  260. }
  261. static _ExtractLongWordOrder(value) {
  262. if (value === 0 || value === 255 || value === -16777216) {
  263. return 0;
  264. }
  265. return 1 + DDSTools._ExtractLongWordOrder(value >> 8);
  266. }
  267. static _GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset) {
  268. const byteArray = new Uint8Array(dataLength);
  269. const srcData = new Uint8Array(arrayBuffer, dataOffset);
  270. let index = 0;
  271. for (let y = 0; y < height; y++) {
  272. for (let x = 0; x < width; x++) {
  273. const srcPos = (x + y * width) * 3;
  274. byteArray[index] = srcData[srcPos + rOffset];
  275. byteArray[index + 1] = srcData[srcPos + gOffset];
  276. byteArray[index + 2] = srcData[srcPos + bOffset];
  277. index += 3;
  278. }
  279. }
  280. return byteArray;
  281. }
  282. static _GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer) {
  283. const byteArray = new Uint8Array(dataLength);
  284. const srcData = new Uint8Array(arrayBuffer, dataOffset);
  285. let index = 0;
  286. for (let y = 0; y < height; y++) {
  287. for (let x = 0; x < width; x++) {
  288. const srcPos = x + y * width;
  289. byteArray[index] = srcData[srcPos];
  290. index++;
  291. }
  292. }
  293. return byteArray;
  294. }
  295. /**
  296. * Uploads DDS Levels to a Babylon Texture
  297. * @internal
  298. */
  299. static UploadDDSLevels(engine, texture, data, info, loadMipmaps, faces, lodIndex = -1, currentFace, destTypeMustBeFilterable = true) {
  300. let sphericalPolynomialFaces = null;
  301. if (info.sphericalPolynomial) {
  302. sphericalPolynomialFaces = [];
  303. }
  304. const ext = !!engine.getCaps().s3tc;
  305. // TODO WEBGPU Once generateMipMaps is split into generateMipMaps + hasMipMaps in InternalTexture this line can be removed
  306. texture.generateMipMaps = loadMipmaps;
  307. const header = new Int32Array(data.buffer, data.byteOffset, headerLengthInt);
  308. let fourCC, width, height, dataLength = 0, dataOffset;
  309. let byteArray, mipmapCount, mip;
  310. let internalCompressedFormat = 0;
  311. let blockBytes = 1;
  312. if (header[off_magic] !== DDS_MAGIC) {
  313. Logger.Error("Invalid magic number in DDS header");
  314. return;
  315. }
  316. if (!info.isFourCC && !info.isRGB && !info.isLuminance) {
  317. Logger.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");
  318. return;
  319. }
  320. if (info.isCompressed && !ext) {
  321. Logger.Error("Compressed textures are not supported on this platform.");
  322. return;
  323. }
  324. let bpp = header[off_RGBbpp];
  325. dataOffset = header[off_size] + 4;
  326. let computeFormats = false;
  327. if (info.isFourCC) {
  328. fourCC = header[off_pfFourCC];
  329. switch (fourCC) {
  330. case FOURCC_DXT1:
  331. blockBytes = 8;
  332. internalCompressedFormat = 33777;
  333. break;
  334. case FOURCC_DXT3:
  335. blockBytes = 16;
  336. internalCompressedFormat = 33778;
  337. break;
  338. case FOURCC_DXT5:
  339. blockBytes = 16;
  340. internalCompressedFormat = 33779;
  341. break;
  342. case FOURCC_D3DFMT_R16G16B16A16F:
  343. computeFormats = true;
  344. bpp = 64;
  345. break;
  346. case FOURCC_D3DFMT_R32G32B32A32F:
  347. computeFormats = true;
  348. bpp = 128;
  349. break;
  350. case FOURCC_DX10: {
  351. // There is an additionnal header so dataOffset need to be changed
  352. dataOffset += 5 * 4; // 5 uints
  353. let supported = false;
  354. switch (info.dxgiFormat) {
  355. case DXGI_FORMAT_R16G16B16A16_FLOAT:
  356. computeFormats = true;
  357. bpp = 64;
  358. supported = true;
  359. break;
  360. case DXGI_FORMAT_R32G32B32A32_FLOAT:
  361. computeFormats = true;
  362. bpp = 128;
  363. supported = true;
  364. break;
  365. case DXGI_FORMAT_B8G8R8X8_UNORM:
  366. info.isRGB = true;
  367. info.isFourCC = false;
  368. bpp = 32;
  369. supported = true;
  370. break;
  371. }
  372. if (supported) {
  373. break;
  374. }
  375. }
  376. // eslint-disable-next-line no-fallthrough
  377. default:
  378. Logger.Error(["Unsupported FourCC code:", Int32ToFourCC(fourCC)]);
  379. return;
  380. }
  381. }
  382. const rOffset = DDSTools._ExtractLongWordOrder(header[off_RMask]);
  383. const gOffset = DDSTools._ExtractLongWordOrder(header[off_GMask]);
  384. const bOffset = DDSTools._ExtractLongWordOrder(header[off_BMask]);
  385. const aOffset = DDSTools._ExtractLongWordOrder(header[off_AMask]);
  386. if (computeFormats) {
  387. internalCompressedFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
  388. }
  389. mipmapCount = 1;
  390. if (header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
  391. mipmapCount = Math.max(1, header[off_mipmapCount]);
  392. }
  393. const startFace = currentFace || 0;
  394. const caps = engine.getCaps();
  395. for (let face = startFace; face < faces; face++) {
  396. width = header[off_width];
  397. height = header[off_height];
  398. for (mip = 0; mip < mipmapCount; ++mip) {
  399. if (lodIndex === -1 || lodIndex === mip) {
  400. // In case of fixed LOD, if the lod has just been uploaded, early exit.
  401. const i = lodIndex === -1 ? mip : 0;
  402. if (!info.isCompressed && info.isFourCC) {
  403. texture.format = 5;
  404. dataLength = width * height * 4;
  405. let floatArray = null;
  406. if (engine._badOS || engine._badDesktopOS || (!caps.textureHalfFloat && !caps.textureFloat)) {
  407. // Required because iOS has many issues with float and half float generation
  408. if (bpp === 128) {
  409. floatArray = DDSTools._GetFloatAsUIntRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
  410. if (sphericalPolynomialFaces && i == 0) {
  411. sphericalPolynomialFaces.push(DDSTools._GetFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i));
  412. }
  413. }
  414. else if (bpp === 64) {
  415. floatArray = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
  416. if (sphericalPolynomialFaces && i == 0) {
  417. sphericalPolynomialFaces.push(DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i));
  418. }
  419. }
  420. texture.type = 0;
  421. }
  422. else {
  423. const floatAvailable = caps.textureFloat && ((destTypeMustBeFilterable && caps.textureFloatLinearFiltering) || !destTypeMustBeFilterable);
  424. const halfFloatAvailable = caps.textureHalfFloat && ((destTypeMustBeFilterable && caps.textureHalfFloatLinearFiltering) || !destTypeMustBeFilterable);
  425. const destType = (bpp === 128 || (bpp === 64 && !halfFloatAvailable)) && floatAvailable
  426. ? 1
  427. : (bpp === 64 || (bpp === 128 && !floatAvailable)) && halfFloatAvailable
  428. ? 2
  429. : 0;
  430. let dataGetter;
  431. let dataGetterPolynomial = null;
  432. switch (bpp) {
  433. case 128: {
  434. switch (destType) {
  435. case 1:
  436. dataGetter = DDSTools._GetFloatRGBAArrayBuffer;
  437. dataGetterPolynomial = null;
  438. break;
  439. case 2:
  440. dataGetter = DDSTools._GetFloatAsHalfFloatRGBAArrayBuffer;
  441. dataGetterPolynomial = DDSTools._GetFloatRGBAArrayBuffer;
  442. break;
  443. case 0:
  444. dataGetter = DDSTools._GetFloatAsUIntRGBAArrayBuffer;
  445. dataGetterPolynomial = DDSTools._GetFloatRGBAArrayBuffer;
  446. break;
  447. }
  448. break;
  449. }
  450. default: {
  451. // 64 bpp
  452. switch (destType) {
  453. case 1:
  454. dataGetter = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer;
  455. dataGetterPolynomial = null;
  456. break;
  457. case 2:
  458. dataGetter = DDSTools._GetHalfFloatRGBAArrayBuffer;
  459. dataGetterPolynomial = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer;
  460. break;
  461. case 0:
  462. dataGetter = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer;
  463. dataGetterPolynomial = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer;
  464. break;
  465. }
  466. break;
  467. }
  468. }
  469. texture.type = destType;
  470. floatArray = dataGetter(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i);
  471. if (sphericalPolynomialFaces && i == 0) {
  472. sphericalPolynomialFaces.push(dataGetterPolynomial ? dataGetterPolynomial(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, i) : floatArray);
  473. }
  474. }
  475. if (floatArray) {
  476. engine._uploadDataToTextureDirectly(texture, floatArray, face, i);
  477. }
  478. }
  479. else if (info.isRGB) {
  480. texture.type = 0;
  481. if (bpp === 24) {
  482. texture.format = 4;
  483. dataLength = width * height * 3;
  484. byteArray = DDSTools._GetRGBArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, rOffset, gOffset, bOffset);
  485. engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
  486. }
  487. else {
  488. // 32
  489. texture.format = 5;
  490. dataLength = width * height * 4;
  491. byteArray = DDSTools._GetRGBAArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer, rOffset, gOffset, bOffset, aOffset);
  492. engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
  493. }
  494. }
  495. else if (info.isLuminance) {
  496. const unpackAlignment = engine._getUnpackAlignement();
  497. const unpaddedRowSize = width;
  498. const paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
  499. dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
  500. byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, data.byteOffset + dataOffset, dataLength, data.buffer);
  501. texture.format = 1;
  502. texture.type = 0;
  503. engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
  504. }
  505. else {
  506. dataLength = (((Math.max(4, width) / 4) * Math.max(4, height)) / 4) * blockBytes;
  507. byteArray = new Uint8Array(data.buffer, data.byteOffset + dataOffset, dataLength);
  508. texture.type = 0;
  509. engine._uploadCompressedDataToTextureDirectly(texture, internalCompressedFormat, width, height, byteArray, face, i);
  510. }
  511. }
  512. dataOffset += bpp ? width * height * (bpp / 8) : dataLength;
  513. width *= 0.5;
  514. height *= 0.5;
  515. width = Math.max(1.0, width);
  516. height = Math.max(1.0, height);
  517. }
  518. if (currentFace !== undefined) {
  519. // Loading a single face
  520. break;
  521. }
  522. }
  523. if (sphericalPolynomialFaces && sphericalPolynomialFaces.length > 0) {
  524. info.sphericalPolynomial = CubeMapToSphericalPolynomialTools.ConvertCubeMapToSphericalPolynomial({
  525. size: header[off_width],
  526. right: sphericalPolynomialFaces[0],
  527. left: sphericalPolynomialFaces[1],
  528. up: sphericalPolynomialFaces[2],
  529. down: sphericalPolynomialFaces[3],
  530. front: sphericalPolynomialFaces[4],
  531. back: sphericalPolynomialFaces[5],
  532. format: 5,
  533. type: 1,
  534. gammaSpace: false,
  535. });
  536. }
  537. else {
  538. info.sphericalPolynomial = undefined;
  539. }
  540. }
  541. }
  542. /**
  543. * Gets or sets a boolean indicating that LOD info is stored in alpha channel (false by default)
  544. */
  545. DDSTools.StoreLODInAlphaChannel = false;
  546. /**
  547. * Create a cube texture from prefiltered data (ie. the mipmaps contain ready to use data for PBR reflection)
  548. * @param rootUrl defines the url where the file to load is located
  549. * @param scene defines the current scene
  550. * @param lodScale defines scale to apply to the mip map selection
  551. * @param lodOffset defines offset to apply to the mip map selection
  552. * @param onLoad defines an optional callback raised when the texture is loaded
  553. * @param onError defines an optional callback raised if there is an issue to load the texture
  554. * @param format defines the format of the data
  555. * @param forcedExtension defines the extension to use to pick the right loader
  556. * @param createPolynomials defines wheter or not to create polynomails harmonics for the texture
  557. * @returns the cube texture as an InternalTexture
  558. */
  559. ThinEngine.prototype.createPrefilteredCubeTexture = function (rootUrl, scene, lodScale, lodOffset, onLoad = null, onError = null, format, forcedExtension = null, createPolynomials = true) {
  560. const callback = (loadData) => {
  561. if (!loadData) {
  562. if (onLoad) {
  563. onLoad(null);
  564. }
  565. return;
  566. }
  567. const texture = loadData.texture;
  568. if (!createPolynomials) {
  569. texture._sphericalPolynomial = new SphericalPolynomial();
  570. }
  571. else if (loadData.info.sphericalPolynomial) {
  572. texture._sphericalPolynomial = loadData.info.sphericalPolynomial;
  573. }
  574. texture._source = InternalTextureSource.CubePrefiltered;
  575. if (this.getCaps().textureLOD) {
  576. // Do not add extra process if texture lod is supported.
  577. if (onLoad) {
  578. onLoad(texture);
  579. }
  580. return;
  581. }
  582. const mipSlices = 3;
  583. const gl = this._gl;
  584. const width = loadData.width;
  585. if (!width) {
  586. return;
  587. }
  588. const textures = [];
  589. for (let i = 0; i < mipSlices; i++) {
  590. //compute LOD from even spacing in smoothness (matching shader calculation)
  591. const smoothness = i / (mipSlices - 1);
  592. const roughness = 1 - smoothness;
  593. const minLODIndex = lodOffset; // roughness = 0
  594. const maxLODIndex = Scalar.Log2(width) * lodScale + lodOffset; // roughness = 1
  595. const lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
  596. const mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
  597. const glTextureFromLod = new InternalTexture(this, InternalTextureSource.Temp);
  598. glTextureFromLod.type = texture.type;
  599. glTextureFromLod.format = texture.format;
  600. glTextureFromLod.width = Math.pow(2, Math.max(Scalar.Log2(width) - mipmapIndex, 0));
  601. glTextureFromLod.height = glTextureFromLod.width;
  602. glTextureFromLod.isCube = true;
  603. glTextureFromLod._cachedWrapU = 0;
  604. glTextureFromLod._cachedWrapV = 0;
  605. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, glTextureFromLod, true);
  606. glTextureFromLod.samplingMode = 2;
  607. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  608. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  609. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  610. gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  611. if (loadData.isDDS) {
  612. const info = loadData.info;
  613. const data = loadData.data;
  614. this._unpackFlipY(info.isCompressed);
  615. DDSTools.UploadDDSLevels(this, glTextureFromLod, data, info, true, 6, mipmapIndex);
  616. }
  617. else {
  618. Logger.Warn("DDS is the only prefiltered cube map supported so far.");
  619. }
  620. this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
  621. // Wrap in a base texture for easy binding.
  622. const lodTexture = new BaseTexture(scene);
  623. lodTexture._isCube = true;
  624. lodTexture._texture = glTextureFromLod;
  625. glTextureFromLod.isReady = true;
  626. textures.push(lodTexture);
  627. }
  628. texture._lodTextureHigh = textures[2];
  629. texture._lodTextureMid = textures[1];
  630. texture._lodTextureLow = textures[0];
  631. if (onLoad) {
  632. onLoad(texture);
  633. }
  634. };
  635. return this.createCubeTexture(rootUrl, scene, null, false, callback, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset);
  636. };
  637. //# sourceMappingURL=dds.js.map