123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- module BABYLON.Internals {
- // Based on demo done by Brandon Jones - http://media.tojicode.com/webgl-samples/dds.html
- // All values and structures referenced from:
- // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
- var DDS_MAGIC = 0x20534444;
- var
- //DDSD_CAPS = 0x1,
- //DDSD_HEIGHT = 0x2,
- //DDSD_WIDTH = 0x4,
- //DDSD_PITCH = 0x8,
- //DDSD_PIXELFORMAT = 0x1000,
- DDSD_MIPMAPCOUNT = 0x20000
- //DDSD_LINEARSIZE = 0x80000,
- //DDSD_DEPTH = 0x800000;
- // var DDSCAPS_COMPLEX = 0x8,
- // DDSCAPS_MIPMAP = 0x400000,
- // DDSCAPS_TEXTURE = 0x1000;
- var DDSCAPS2_CUBEMAP = 0x200
- // DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
- // DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
- // DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
- // DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
- // DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
- // DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
- // DDSCAPS2_VOLUME = 0x200000;
- var
- //DDPF_ALPHAPIXELS = 0x1,
- //DDPF_ALPHA = 0x2,
- DDPF_FOURCC = 0x4,
- DDPF_RGB = 0x40,
- //DDPF_YUV = 0x200,
- DDPF_LUMINANCE = 0x20000;
- function FourCCToInt32(value: string) {
- return value.charCodeAt(0) +
- (value.charCodeAt(1) << 8) +
- (value.charCodeAt(2) << 16) +
- (value.charCodeAt(3) << 24);
- }
- function Int32ToFourCC(value: number) {
- return String.fromCharCode(
- value & 0xff,
- (value >> 8) & 0xff,
- (value >> 16) & 0xff,
- (value >> 24) & 0xff
- );
- }
- var FOURCC_DXT1 = FourCCToInt32("DXT1");
- var FOURCC_DXT3 = FourCCToInt32("DXT3");
- var FOURCC_DXT5 = FourCCToInt32("DXT5");
- var FOURCC_DX10 = FourCCToInt32("DX10");
- var FOURCC_D3DFMT_R16G16B16A16F = 113;
- var FOURCC_D3DFMT_R32G32B32A32F = 116;
- var DXGI_FORMAT_R16G16B16A16_FLOAT = 10;
- var DXGI_FORMAT_B8G8R8X8_UNORM = 88;
- var headerLengthInt = 31; // The header length in 32 bit ints
- // Offsets into the header array
- var off_magic = 0;
- var off_size = 1;
- var off_flags = 2;
- var off_height = 3;
- var off_width = 4;
- var off_mipmapCount = 7;
- var off_pfFlags = 20;
- var off_pfFourCC = 21;
- var off_RGBbpp = 22;
- // var off_RMask = 23;
- // var off_GMask = 24;
- // var off_BMask = 25;
- // var off_AMask = 26;
- // var off_caps1 = 27;
- var off_caps2 = 28;
- // var off_caps3 = 29;
- // var off_caps4 = 30;
- var off_dxgiFormat = 32
- export interface DDSInfo {
- width: number;
- height: number;
- mipmapCount: number;
- isFourCC: boolean;
- isRGB: boolean;
- isLuminance: boolean;
- isCube: boolean;
- isCompressed: boolean;
- dxgiFormat: number;
- textureType: number;
- };
- export class DDSTools {
- public static StoreLODInAlphaChannel = false;
- public static GetDDSInfo(arrayBuffer: any): DDSInfo {
- var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
- var extendedHeader = new Int32Array(arrayBuffer, 0, headerLengthInt + 4);
- var mipmapCount = 1;
- if (header[off_flags] & DDSD_MIPMAPCOUNT) {
- mipmapCount = Math.max(1, header[off_mipmapCount]);
- }
- var fourCC = header[off_pfFourCC];
- var dxgiFormat = (fourCC === FOURCC_DX10) ? extendedHeader[off_dxgiFormat] : 0;
- var textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
- switch (fourCC) {
- case FOURCC_D3DFMT_R16G16B16A16F:
- textureType = Engine.TEXTURETYPE_HALF_FLOAT;
- break;
- case FOURCC_D3DFMT_R32G32B32A32F:
- textureType = Engine.TEXTURETYPE_FLOAT;
- break;
- case FOURCC_DX10:
- if (dxgiFormat === DXGI_FORMAT_R16G16B16A16_FLOAT) {
- textureType = Engine.TEXTURETYPE_HALF_FLOAT;
- break;
- }
- }
-
- return {
- width: header[off_width],
- height: header[off_height],
- mipmapCount: mipmapCount,
- isFourCC: (header[off_pfFlags] & DDPF_FOURCC) === DDPF_FOURCC,
- isRGB: (header[off_pfFlags] & DDPF_RGB) === DDPF_RGB,
- isLuminance: (header[off_pfFlags] & DDPF_LUMINANCE) === DDPF_LUMINANCE,
- isCube: (header[off_caps2] & DDSCAPS2_CUBEMAP) === DDSCAPS2_CUBEMAP,
- isCompressed: (fourCC === FOURCC_DXT1 || fourCC === FOURCC_DXT3 || FOURCC_DXT1 === FOURCC_DXT5),
- dxgiFormat: dxgiFormat,
- textureType: textureType
- };
- }
- // ref: http://stackoverflow.com/questions/32633585/how-do-you-convert-to-half-floats-in-javascript
- private static _FloatView: Float32Array;
- private static _Int32View: Int32Array;
- private static _ToHalfFloat(value: number): number {
- if (!DDSTools._FloatView) {
- DDSTools._FloatView = new Float32Array(1);
- DDSTools._Int32View = new Int32Array(DDSTools._FloatView.buffer);
- }
-
- DDSTools._FloatView[0] = value;
- var x = DDSTools._Int32View[0];
- var bits = (x >> 16) & 0x8000; /* Get the sign */
- var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
- var e = (x >> 23) & 0xff; /* Using int is faster here */
- /* If zero, or denormal, or exponent underflows too much for a denormal
- * half, return signed zero. */
- if (e < 103) {
- return bits;
- }
- /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
- if (e > 142) {
- bits |= 0x7c00;
- /* If exponent was 0xff and one mantissa bit was set, it means NaN,
- * not Inf, so make sure we set one mantissa bit too. */
- bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
- return bits;
- }
- /* If exponent underflows but not too much, return a denormal */
- if (e < 113) {
- m |= 0x0800;
- /* Extra rounding may overflow and set mantissa to 0 and exponent
- * to 1, which is OK. */
- bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
- return bits;
- }
- bits |= ((e - 112) << 10) | (m >> 1);
- bits += m & 1;
- return bits;
- }
- private static _FromHalfFloat(value: number): number {
- var s = (value & 0x8000) >> 15;
- var e = (value & 0x7C00) >> 10;
- var f = value & 0x03FF;
- if(e === 0) {
- return (s ? -1 : 1) * Math.pow(2, -14) * (f / Math.pow(2, 10));
- } else if (e == 0x1F) {
- return f ? NaN : ((s ? -1 : 1) * Infinity);
- }
- return (s ? -1 : 1) * Math.pow(2, e-15) * (1 + (f / Math.pow(2, 10)));
- }
- private static _GetHalfFloatAsFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
- var destArray = new Float32Array(dataLength);
- var srcData = new Uint16Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- destArray[index] = DDSTools._FromHalfFloat(srcData[srcPos]);
- destArray[index + 1] = DDSTools._FromHalfFloat(srcData[srcPos + 1]);
- destArray[index + 2] = DDSTools._FromHalfFloat(srcData[srcPos + 2]);
- if (DDSTools.StoreLODInAlphaChannel) {
- destArray[index + 3] = lod;
- } else {
- destArray[index + 3] = DDSTools._FromHalfFloat(srcData[srcPos + 3]);
- }
- index += 4;
- }
- }
- return destArray;
- }
-
- private static _GetHalfFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Uint16Array {
- if (DDSTools.StoreLODInAlphaChannel) {
- var destArray = new Uint16Array(dataLength);
- var srcData = new Uint16Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- destArray[index] = srcData[srcPos];
- destArray[index + 1] = srcData[srcPos + 1];
- destArray[index + 2] = srcData[srcPos + 2];
- destArray[index + 3] = DDSTools._ToHalfFloat(lod)
- index += 4;
- }
- }
- return destArray;
- }
- return new Uint16Array(arrayBuffer, dataOffset, dataLength);
- }
- private static _GetFloatRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
- if (DDSTools.StoreLODInAlphaChannel) {
- var destArray = new Float32Array(dataLength);
- var srcData = new Float32Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- destArray[index] = srcData[srcPos];
- destArray[index + 1] = srcData[srcPos + 1];
- destArray[index + 2] = srcData[srcPos + 2];
- destArray[index + 3] = lod;
- index += 4;
- }
- }
- return destArray;
- }
- return new Float32Array(arrayBuffer, dataOffset, dataLength);
- }
- private static _GetFloatAsUIntRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
- var destArray = new Uint8Array(dataLength);
- var srcData = new Float32Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- destArray[index] = Scalar.Clamp(srcData[srcPos]) * 255;
- destArray[index + 1] = Scalar.Clamp(srcData[srcPos + 1]) * 255;
- destArray[index + 2] = Scalar.Clamp(srcData[srcPos + 2]) * 255;
- if (DDSTools.StoreLODInAlphaChannel) {
- destArray[index + 3] = lod;
- } else {
- destArray[index + 3] = Scalar.Clamp(srcData[srcPos + 3]) * 255;
- }
- index += 4;
- }
- }
- return destArray;
- }
- private static _GetHalfFloatAsUIntRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer, lod: number): Float32Array {
- var destArray = new Uint8Array(dataLength);
- var srcData = new Uint16Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- destArray[index] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos])) * 255;
- destArray[index + 1] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 1])) * 255;
- destArray[index + 2] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 2])) * 255;
- if (DDSTools.StoreLODInAlphaChannel) {
- destArray[index + 3] = lod;
- } else {
- destArray[index + 3] = Scalar.Clamp(DDSTools._FromHalfFloat(srcData[srcPos + 3])) * 255;
- }
- index += 4;
- }
- }
- return destArray;
- }
- private static _GetRGBAArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
- var byteArray = new Uint8Array(dataLength);
- var srcData = new Uint8Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 4;
- byteArray[index] = srcData[srcPos + 2];
- byteArray[index + 1] = srcData[srcPos + 1];
- byteArray[index + 2] = srcData[srcPos];
- byteArray[index + 3] = srcData[srcPos + 3];
- index += 4;
- }
- }
- return byteArray;
- }
- private static _GetRGBArrayBuffer(width: number, height: number, dataOffset:number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
- var byteArray = new Uint8Array(dataLength);
- var srcData = new Uint8Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width) * 3;
- byteArray[index] = srcData[srcPos + 2];
- byteArray[index + 1] = srcData[srcPos + 1];
- byteArray[index + 2] = srcData[srcPos];
- index += 3;
- }
- }
- return byteArray;
- }
- private static _GetLuminanceArrayBuffer(width: number, height: number, dataOffset: number, dataLength: number, arrayBuffer: ArrayBuffer): Uint8Array {
- var byteArray = new Uint8Array(dataLength);
- var srcData = new Uint8Array(arrayBuffer, dataOffset);
- var index = 0;
- for (var y = 0; y < height; y++) {
- for (var x = 0; x < width; x++) {
- var srcPos = (x + y * width);
- byteArray[index] = srcData[srcPos];
- index++;
- }
- }
- return byteArray;
- }
- public static UploadDDSLevels(engine: Engine, gl:WebGLRenderingContext, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex = -1): void {
- var ext = engine.getCaps().s3tc;
- var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
- fourCC, width, height, dataLength, dataOffset,
- byteArray, mipmapCount, mip;
- let internalFormat = 0;
- let format = 0;
- let blockBytes = 1;
- if (header[off_magic] != DDS_MAGIC) {
- Tools.Error("Invalid magic number in DDS header");
- return;
- }
- if (!info.isFourCC && !info.isRGB && !info.isLuminance) {
- Tools.Error("Unsupported format, must contain a FourCC, RGB or LUMINANCE code");
- return;
- }
- if (info.isCompressed && !ext) {
- Tools.Error("Compressed textures are not supported on this platform.");
- return;
- }
- var bpp = header[off_RGBbpp];
- dataOffset = header[off_size] + 4;
- let computeFormats = false;
- if (info.isFourCC && ext) {
- fourCC = header[off_pfFourCC];
- switch (fourCC) {
- case FOURCC_DXT1:
- blockBytes = 8;
- internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT;
- break;
- case FOURCC_DXT3:
- blockBytes = 16;
- internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT3_EXT;
- break;
- case FOURCC_DXT5:
- blockBytes = 16;
- internalFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT;
- break;
- case FOURCC_D3DFMT_R16G16B16A16F:
- computeFormats = true;
- break;
- case FOURCC_D3DFMT_R32G32B32A32F:
- computeFormats = true;
- break;
- case FOURCC_DX10:
- // There is an additionnal header so dataOffset need to be changed
- dataOffset += 5 * 4; // 5 uints
- let supported = false;
- switch (info.dxgiFormat) {
- case DXGI_FORMAT_R16G16B16A16_FLOAT:
- computeFormats = true;
- supported = true;
- break;
- case DXGI_FORMAT_B8G8R8X8_UNORM:
- info.isRGB = true;
- info.isFourCC = false;
- bpp = 32;
- supported = true;
- break;
- }
- if (supported) {
- break;
- }
- default:
- console.error("Unsupported FourCC code:", Int32ToFourCC(fourCC));
- return;
- }
- }
- if (computeFormats) {
- format = engine._getWebGLTextureType(info.textureType);
- internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
- }
- mipmapCount = 1;
- if (header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
- mipmapCount = Math.max(1, header[off_mipmapCount]);
- }
-
- for (var face = 0; face < faces; face++) {
- var sampler = faces === 1 ? gl.TEXTURE_2D : (gl.TEXTURE_CUBE_MAP_POSITIVE_X + face);
- width = header[off_width];
- height = header[off_height];
- for (mip = 0; mip < mipmapCount; ++mip) {
- if (lodIndex === -1 || lodIndex === mip) {
- // In case of fixed LOD, if the lod has just been uploaded, early exit.
- const i = (lodIndex === -1) ? mip : 0;
- if (!info.isCompressed && info.isFourCC) {
- dataLength = width * height * 4;
- var FloatArray: Nullable<ArrayBufferView> = null;
- if (engine.badOS || engine.badDesktopOS || (!engine.getCaps().textureHalfFloat && !engine.getCaps().textureFloat)) { // Required because iOS has many issues with float and half float generation
- if (bpp === 128) {
- FloatArray = DDSTools._GetFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
- }
- else if (bpp === 64) {
- FloatArray = DDSTools._GetHalfFloatAsUIntRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
- }
- info.textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
- format = engine._getWebGLTextureType(info.textureType);
- internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
- }
- else {
- if (bpp === 128) {
- FloatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
- } else if (bpp === 64 && !engine.getCaps().textureHalfFloat) {
- FloatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
- info.textureType = Engine.TEXTURETYPE_FLOAT;
- format = engine._getWebGLTextureType(info.textureType);
- internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);
- } else { // 64
- FloatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
- }
- }
- if (FloatArray) {
- engine._uploadDataToTexture(sampler, i, internalFormat, width, height, gl.RGBA, format, FloatArray);
- }
- } else if (info.isRGB) {
- if (bpp === 24) {
- dataLength = width * height * 3;
- byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
- engine._uploadDataToTexture(sampler, i, gl.RGB, width, height, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
- } else { // 32
- dataLength = width * height * 4;
- byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
- engine._uploadDataToTexture(sampler, i, gl.RGBA, width, height, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
- }
- } else if (info.isLuminance) {
- var unpackAlignment = gl.getParameter(gl.UNPACK_ALIGNMENT);
- var unpaddedRowSize = width;
- var paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
- dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
- byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
- engine._uploadDataToTexture(sampler, i, gl.LUMINANCE, width, height, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
- } else {
- dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
- byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
- engine._uploadCompressedDataToTexture(sampler, i, internalFormat, width, height, byteArray);
- }
- }
- dataOffset += width * height * (bpp / 8);
- width *= 0.5;
- height *= 0.5;
- width = Math.max(1.0, width);
- height = Math.max(1.0, height);
- }
- }
- }
- }
- }
|