babylon.dds.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. module BABYLON.Internals {
  2. // Based on demo done by Brandon Jones - http://media.tojicode.com/webgl-samples/dds.html
  3. // All values and structures referenced from:
  4. // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
  5. var DDS_MAGIC = 0x20534444;
  6. var
  7. //DDSD_CAPS = 0x1,
  8. //DDSD_HEIGHT = 0x2,
  9. //DDSD_WIDTH = 0x4,
  10. //DDSD_PITCH = 0x8,
  11. //DDSD_PIXELFORMAT = 0x1000,
  12. DDSD_MIPMAPCOUNT = 0x20000
  13. //DDSD_LINEARSIZE = 0x80000,
  14. //DDSD_DEPTH = 0x800000;
  15. // var DDSCAPS_COMPLEX = 0x8,
  16. // DDSCAPS_MIPMAP = 0x400000,
  17. // DDSCAPS_TEXTURE = 0x1000;
  18. var DDSCAPS2_CUBEMAP = 0x200
  19. // DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
  20. // DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
  21. // DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
  22. // DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
  23. // DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
  24. // DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
  25. // DDSCAPS2_VOLUME = 0x200000;
  26. var
  27. //DDPF_ALPHAPIXELS = 0x1,
  28. //DDPF_ALPHA = 0x2,
  29. DDPF_FOURCC = 0x4,
  30. DDPF_RGB = 0x40,
  31. //DDPF_YUV = 0x200,
  32. DDPF_LUMINANCE = 0x20000;
  33. function FourCCToInt32(value: string) {
  34. return value.charCodeAt(0) +
  35. (value.charCodeAt(1) << 8) +
  36. (value.charCodeAt(2) << 16) +
  37. (value.charCodeAt(3) << 24);
  38. }
  39. function Int32ToFourCC(value: number) {
  40. return String.fromCharCode(
  41. value & 0xff,
  42. (value >> 8) & 0xff,
  43. (value >> 16) & 0xff,
  44. (value >> 24) & 0xff
  45. );
  46. }
  47. var FOURCC_DXT1 = FourCCToInt32("DXT1");
  48. var FOURCC_DXT3 = FourCCToInt32("DXT3");
  49. var FOURCC_DXT5 = FourCCToInt32("DXT5");
  50. var FOURCC_DX10 = FourCCToInt32("DX10");
  51. var FOURCC_D3DFMT_R16G16B16A16F = 113;
  52. var FOURCC_D3DFMT_R32G32B32A32F = 116;
  53. var DXGI_FORMAT_R16G16B16A16_FLOAT = 10;
  54. var DXGI_FORMAT_B8G8R8X8_UNORM = 88;
  55. var headerLengthInt = 31; // The header length in 32 bit ints
  56. // Offsets into the header array
  57. var off_magic = 0;
  58. var off_size = 1;
  59. var off_flags = 2;
  60. var off_height = 3;
  61. var off_width = 4;
  62. var off_mipmapCount = 7;
  63. var off_pfFlags = 20;
  64. var off_pfFourCC = 21;
  65. var off_RGBbpp = 22;
  66. // var off_RMask = 23;
  67. // var off_GMask = 24;
  68. // var off_BMask = 25;
  69. // var off_AMask = 26;
  70. // var off_caps1 = 27;
  71. var off_caps2 = 28;
  72. // var off_caps3 = 29;
  73. // var off_caps4 = 30;
  74. var off_dxgiFormat = 32
  75. export interface DDSInfo {
  76. width: number;
  77. height: number;
  78. mipmapCount: number;
  79. isFourCC: boolean;
  80. isRGB: boolean;
  81. isLuminance: boolean;
  82. isCube: boolean;
  83. isCompressed: boolean;
  84. dxgiFormat: number;
  85. textureType: number;
  86. };
  87. export class DDSTools {
  88. public static StoreLODInAlphaChannel = false;
  89. public static GetDDSInfo(arrayBuffer: any): DDSInfo {
  90. var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
  91. var extendedHeader = new Int32Array(arrayBuffer, 0, headerLengthInt + 4);
  92. var mipmapCount = 1;
  93. if (header[off_flags] & DDSD_MIPMAPCOUNT) {
  94. mipmapCount = Math.max(1, header[off_mipmapCount]);
  95. }
  96. var fourCC = header[off_pfFourCC];
  97. var dxgiFormat = (fourCC === FOURCC_DX10) ? extendedHeader[off_dxgiFormat] : 0;
  98. var textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  99. switch (fourCC) {
  100. case FOURCC_D3DFMT_R16G16B16A16F:
  101. textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  102. break;
  103. case FOURCC_D3DFMT_R32G32B32A32F:
  104. textureType = Engine.TEXTURETYPE_FLOAT;
  105. break;
  106. case FOURCC_DX10:
  107. if (dxgiFormat === DXGI_FORMAT_R16G16B16A16_FLOAT) {
  108. textureType = Engine.TEXTURETYPE_HALF_FLOAT;
  109. break;
  110. }
  111. }
  112. return {
  113. width: header[off_width],
  114. height: header[off_height],
  115. mipmapCount: mipmapCount,
  116. isFourCC: (header[off_pfFlags] & DDPF_FOURCC) === DDPF_FOURCC,
  117. isRGB: (header[off_pfFlags] & DDPF_RGB) === DDPF_RGB,
  118. isLuminance: (header[off_pfFlags] & DDPF_LUMINANCE) === DDPF_LUMINANCE,
  119. isCube: (header[off_caps2] & DDSCAPS2_CUBEMAP) === DDSCAPS2_CUBEMAP,
  120. isCompressed: (fourCC === FOURCC_DXT1 || fourCC === FOURCC_DXT3 || FOURCC_DXT1 === FOURCC_DXT5),
  121. dxgiFormat: dxgiFormat,
  122. textureType: textureType
  123. };
  124. }
  125. // ref: http://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript
  126. private static _FloatView: Float32Array;
  127. private static _Int32View: Int32Array;
  128. private static _ToHalfFloat(value: number): number {
  129. if (!DDSTools._FloatView) {
  130. DDSTools._FloatView = new Float32Array(1);
  131. DDSTools._Int32View = new Int32Array(DDSTools._FloatView.buffer);
  132. }
  133. DDSTools._FloatView[0] = value;
  134. var x = DDSTools._Int32View[0];
  135. var bits = (x >> 16) & 0x8000; /* Get the sign */
  136. var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
  137. var e = (x >> 23) & 0xff; /* Using int is faster here */
  138. /* If zero, or denormal, or exponent underflows too much for a denormal
  139. * half, return signed zero. */
  140. if (e < 103) {
  141. return bits;
  142. }
  143. /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
  144. if (e > 142) {
  145. bits |= 0x7c00;
  146. /* If exponent was 0xff and one mantissa bit was set, it means NaN,
  147. * not Inf, so make sure we set one mantissa bit too. */
  148. bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
  149. return bits;
  150. }
  151. /* If exponent underflows but not too much, return a denormal */
  152. if (e < 113) {
  153. m |= 0x0800;
  154. /* Extra rounding may overflow and set mantissa to 0 and exponent
  155. * to 1, which is OK. */
  156. bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
  157. return bits;
  158. }
  159. bits |= ((e - 112) << 10) | (m >> 1);
  160. bits += m & 1;
  161. return bits;
  162. }
  163. private static _FromHalfFloat(value: number): number {
  164. var s = (value & 0x8000) >> 15;
  165. var e = (value & 0x7C00) >> 10;
  166. var f = value & 0x03FF;
  167. if(e === 0) {
  168. return (s ? -1 : 1) * Math.pow(2, -14) * (f / Math.pow(2, 10));
  169. } else if (e == 0x1F) {
  170. return f ? NaN : ((s ? -1 : 1) * Infinity);
  171. }
  172. return (s ? -1 : 1) * Math.pow(2, e-15) * (1 + (f / Math.pow(2, 10)));
  173. }
  174. private static _GetHalfFloatAsFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
  175. var destArray = new Float32Array(dataLength);
  176. var srcData = new Uint16Array(arrayBuffer, dataOffset);
  177. var index = 0;
  178. for (var y = 0; y < height; y++) {
  179. for (var x = 0; x < width; x++) {
  180. var srcPos = (x + y * width) * 4;
  181. destArray[index] = DDSTools._FromHalfFloat(srcData[srcPos]);
  182. destArray[index + 1] = DDSTools._FromHalfFloat(srcData[srcPos + 1]);
  183. destArray[index + 2] = DDSTools._FromHalfFloat(srcData[srcPos + 2]);
  184. if (DDSTools.StoreLODInAlphaChannel) {
  185. destArray[index + 3] = lod;
  186. } else {
  187. destArray[index + 3] = DDSTools._FromHalfFloat(srcData[srcPos + 3]);
  188. }
  189. index += 4;
  190. }
  191. }
  192. return destArray;
  193. }
  194. private static _GetHalfFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Uint16Array {
  195. if (DDSTools.StoreLODInAlphaChannel) {
  196. var destArray = new Uint16Array(dataLength);
  197. var srcData = new Uint16Array(arrayBuffer, dataOffset);
  198. var index = 0;
  199. for (var y = 0; y < height; y++) {
  200. for (var x = 0; x < width; x++) {
  201. var srcPos = (x + y * width) * 4;
  202. destArray[index] = srcData[srcPos];
  203. destArray[index + 1] = srcData[srcPos + 1];
  204. destArray[index + 2] = srcData[srcPos + 2];
  205. destArray[index + 3] = DDSTools._ToHalfFloat(lod)
  206. index += 4;
  207. }
  208. }
  209. return destArray;
  210. }
  211. return new Uint16Array(arrayBuffer, dataOffset, dataLength);
  212. }
  213. private static _GetFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
  214. if (DDSTools.StoreLODInAlphaChannel) {
  215. var destArray = new Float32Array(dataLength);
  216. var srcData = new Float32Array(arrayBuffer, dataOffset);
  217. var index = 0;
  218. for (var y = 0; y < height; y++) {
  219. for (var x = 0; x < width; x++) {
  220. var srcPos = (x + y * width) * 4;
  221. destArray[index] = srcData[srcPos];
  222. destArray[index + 1] = srcData[srcPos + 1];
  223. destArray[index + 2] = srcData[srcPos + 2];
  224. destArray[index + 3] = lod;
  225. index += 4;
  226. }
  227. }
  228. return destArray;
  229. }
  230. return new Float32Array(arrayBuffer, dataOffset, dataLength);
  231. }
  232. private static _GetFloatAsUIntRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
  233. var destArray = new Uint8Array(dataLength);
  234. var srcData = new Float32Array(arrayBuffer, dataOffset);
  235. var index = 0;
  236. for (var y = 0; y < height; y++) {
  237. for (var x = 0; x < width; x++) {
  238. var srcPos = (x + y * width) * 4;
  239. destArray[index] = Scalar.Clamp(srcData[srcPos]) * 255;
  240. destArray[index + 1] = Scalar.Clamp(srcData[srcPos + 1]) * 255;
  241. destArray[index + 2] = Scalar.Clamp(srcData[srcPos + 2]) * 255;
  242. if (DDSTools.StoreLODInAlphaChannel) {
  243. destArray[index + 3] = lod;
  244. } else {
  245. destArray[index + 3] = Scalar.Clamp(srcData[srcPos + 3]) * 255;
  246. }
  247. index += 4;
  248. }
  249. }
  250. return destArray;
  251. }
  252. private static _GetHalfFloatAsUIntRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
  253. var destArray = new Uint8Array(dataLength);
  254. var srcData = new Uint16Array(arrayBuffer, dataOffset);
  255. var index = 0;
  256. for (var y = 0; y < height; y++) {
  257. for (var x = 0; x < width; x++) {
  258. var srcPos = (x + y * width) * 4;
  259. destArray[index] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos])) * 255;
  260. destArray[index + 1] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 1])) * 255;
  261. destArray[index + 2] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 2])) * 255;
  262. if (DDSTools.StoreLODInAlphaChannel) {
  263. destArray[index + 3] = lod;
  264. } else {
  265. destArray[index + 3] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 3])) * 255;
  266. }
  267. index += 4;
  268. }
  269. }
  270. return destArray;
  271. }
  272. private static _GetRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
  273. var byteArray = new Uint8Array(dataLength);
  274. var srcData = new Uint8Array(arrayBuffer, dataOffset);
  275. var index = 0;
  276. for (var y = 0; y < height; y++) {
  277. for (var x = 0; x < width; x++) {
  278. var srcPos = (x + y * width) * 4;
  279. byteArray[index] = srcData[srcPos + 2];
  280. byteArray[index + 1] = srcData[srcPos + 1];
  281. byteArray[index + 2] = srcData[srcPos];
  282. byteArray[index + 3] = srcData[srcPos + 3];
  283. index += 4;
  284. }
  285. }
  286. return byteArray;
  287. }
  288. private static _GetRGBArrayBuffer(width: number, height: number, dataOffset:number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
  289. var byteArray = new Uint8Array(dataLength);
  290. var srcData = new Uint8Array(arrayBuffer, dataOffset);
  291. var index = 0;
  292. for (var y = 0; y < height; y++) {
  293. for (var x = 0; x < width; x++) {
  294. var srcPos = (x + y * width) * 3;
  295. byteArray[index] = srcData[srcPos + 2];
  296. byteArray[index + 1] = srcData[srcPos + 1];
  297. byteArray[index + 2] = srcData[srcPos];
  298. index += 3;
  299. }
  300. }
  301. return byteArray;
  302. }
  303. private static _GetLuminanceArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
  304. var byteArray = new Uint8Array(dataLength);
  305. var srcData = new Uint8Array(arrayBuffer, dataOffset);
  306. var index = 0;
  307. for (var y = 0; y < height; y++) {
  308. for (var x = 0; x < width; x++) {
  309. var srcPos = (x + y * width);
  310. byteArray[index] = srcData[srcPos];
  311. index++;
  312. }
  313. }
  314. return byteArray;
  315. }
  316. public static UploadDDSLevels(engine: Engine, gl:WebGLRenderingContext, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex = -1): void {
  317. var ext = engine.getCaps().s3tc;
  318. var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
  319. fourCC, width, height, dataLength, dataOffset,
  320. byteArray, mipmapCount, mip;
  321. let internalFormat = 0;
  322. let format = 0;
  323. let blockBytes = 1;
  324. if (header[off_magic] != DDS_MAGIC) {
  325. Tools.Error("Invalid magic number in DDS header");
  326. return;
  327. }
  328. if (!info.isFourCC && !info.isRGB && !info.isLuminance) {
  329. Tools.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");
  330. return;
  331. }
  332. if (info.isCompressed && !ext) {
  333. Tools.Error("Compressed textures are not supported on this platform.");
  334. return;
  335. }
  336. var bpp = header[off_RGBbpp];
  337. dataOffset = header[off_size] + 4;
  338. let computeFormats = false;
  339. if (info.isFourCC && ext) {
  340. fourCC = header[off_pfFourCC];
  341. switch (fourCC) {
  342. case FOURCC_DXT1:
  343. blockBytes = 8;
  344. internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
  345. break;
  346. case FOURCC_DXT3:
  347. blockBytes = 16;
  348. internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT3_EXT;
  349. break;
  350. case FOURCC_DXT5:
  351. blockBytes = 16;
  352. internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
  353. break;
  354. case FOURCC_D3DFMT_R16G16B16A16F:
  355. computeFormats = true;
  356. break;
  357. case FOURCC_D3DFMT_R32G32B32A32F:
  358. computeFormats = true;
  359. break;
  360. case FOURCC_DX10:
  361. // There is an additionnal header so dataOffset need to be changed
  362. dataOffset += 5 * 4; // 5 uints
  363. let supported = false;
  364. switch (info.dxgiFormat) {
  365. case DXGI_FORMAT_R16G16B16A16_FLOAT:
  366. computeFormats = true;
  367. supported = true;
  368. break;
  369. case DXGI_FORMAT_B8G8R8X8_UNORM:
  370. info.isRGB = true;
  371. info.isFourCC = false;
  372. bpp = 32;
  373. supported = true;
  374. break;
  375. }
  376. if (supported) {
  377. break;
  378. }
  379. default:
  380. console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
  381. return;
  382. }
  383. }
  384. if (computeFormats) {
  385. format = engine._getWebGLTextureType(info.textureType);
  386. internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
  387. }
  388. mipmapCount = 1;
  389. if (header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
  390. mipmapCount = Math.max(1, header[off_mipmapCount]);
  391. }
  392. for (var face = 0; face < faces; face++) {
  393. var sampler = faces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
  394. width = header[off_width];
  395. height = header[off_height];
  396. for (mip = 0; mip < mipmapCount; ++mip) {
  397. if (lodIndex === -1 || lodIndex === mip) {
  398. // In case of fixed LOD, if the lod has just been uploaded, early exit.
  399. const i = (lodIndex === -1) ? mip : 0;
  400. if (!info.isCompressed && info.isFourCC) {
  401. dataLength = width * height * 4;
  402. var FloatArray: Nullable<ArrayBufferView> = null;
  403. if (engine.badOS || engine.badDesktopOS || (!engine.getCaps().textureHalfFloat && !engine.getCaps().textureFloat)) { // Required because iOS has many issues with float and half float generation
  404. if (bpp === 128) {
  405. FloatArray = DDSTools._GetFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
  406. }
  407. else if (bpp === 64) {
  408. FloatArray = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
  409. }
  410. info.textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  411. format = engine._getWebGLTextureType(info.textureType);
  412. internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
  413. }
  414. else {
  415. if (bpp === 128) {
  416. FloatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
  417. } else if (bpp === 64 && !engine.getCaps().textureHalfFloat) {
  418. FloatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
  419. info.textureType = Engine.TEXTURETYPE_FLOAT;
  420. format = engine._getWebGLTextureType(info.textureType);
  421. internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
  422. } else { // 64
  423. FloatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
  424. }
  425. }
  426. if (FloatArray) {
  427. engine._uploadDataToTexture(sampler, i, internalFormat, width, height, gl.RGBA, format, FloatArray);
  428. }
  429. } else if (info.isRGB) {
  430. if (bpp === 24) {
  431. dataLength = width * height * 3;
  432. byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
  433. engine._uploadDataToTexture(sampler, i, gl.RGB, width, height, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
  434. } else { // 32
  435. dataLength = width * height * 4;
  436. byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
  437. engine._uploadDataToTexture(sampler, i, gl.RGBA, width, height, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
  438. }
  439. } else if (info.isLuminance) {
  440. var unpackAlignment = gl.getParameter(gl.UNPACK_ALIGNMENT);
  441. var unpaddedRowSize = width;
  442. var paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
  443. dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
  444. byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
  445. engine._uploadDataToTexture(sampler, i, gl.LUMINANCE, width, height, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
  446. } else {
  447. dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
  448. byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
  449. engine._uploadCompressedDataToTexture(sampler, i, internalFormat, width, height, byteArray);
  450. }
  451. }
  452. dataOffset += width * height * (bpp / 8);
  453. width *= 0.5;
  454. height *= 0.5;
  455. width = Math.max(1.0, width);
  456. height = Math.max(1.0, height);
  457. }
  458. }
  459. }
  460. }
  461. }