DDSLoader.js
1 /* 2 * @author mrdoob / http://mrdoob.com/ 3 */ 4 5 THREE.DDSLoader = function () { 6 this._parser = THREE.DDSLoader.parse; 7 }; 8 9 THREE.DDSLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype ); 10 THREE.DDSLoader.prototype.constructor = THREE.DDSLoader; 11 12 THREE.DDSLoader.parse = function ( buffer, loadMipmaps ) { 13 14 var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; 15 16 // Adapted from @toji's DDS utils 17 // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js 18 19 // All values and structures referenced from: 20 // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ 21 22 var DDS_MAGIC = 0x20534444; 23 24 var DDSD_CAPS = 0x1, 25 DDSD_HEIGHT = 0x2, 26 DDSD_WIDTH = 0x4, 27 DDSD_PITCH = 0x8, 28 DDSD_PIXELFORMAT = 0x1000, 29 DDSD_MIPMAPCOUNT = 0x20000, 30 DDSD_LINEARSIZE = 0x80000, 31 DDSD_DEPTH = 0x800000; 32 33 var DDSCAPS_COMPLEX = 0x8, 34 DDSCAPS_MIPMAP = 0x400000, 35 DDSCAPS_TEXTURE = 0x1000; 36 37 var DDSCAPS2_CUBEMAP = 0x200, 38 DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, 39 DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, 40 DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, 41 DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, 42 DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, 43 DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, 44 DDSCAPS2_VOLUME = 0x200000; 45 46 var DDPF_ALPHAPIXELS = 0x1, 47 DDPF_ALPHA = 0x2, 48 DDPF_FOURCC = 0x4, 49 DDPF_RGB = 0x40, 50 DDPF_YUV = 0x200, 51 DDPF_LUMINANCE = 0x20000; 52 53 function fourCCToInt32( value ) { 54 55 return value.charCodeAt(0) + 56 (value.charCodeAt(1) << 8) + 57 (value.charCodeAt(2) << 16) + 58 (value.charCodeAt(3) << 24); 59 60 } 61 62 function int32ToFourCC( value ) { 63 64 return String.fromCharCode( 65 value & 0xff, 66 (value >> 8) & 0xff, 67 (value >> 16) & 0xff, 68 (value >> 24) & 0xff 69 ); 70 } 71 72 function loadARGBMip( buffer, dataOffset, width, height ) { 73 var dataLength = width * height * 4; 74 var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); 75 var byteArray = new Uint8Array( dataLength ); 76 var dst = 0; 77 var src = 0; 78 for ( var y = 0; y < height; y ++ ) { 79 for ( var x = 0; x < width; x ++ ) { 80 var b = srcBuffer[src]; src ++; 81 var g = srcBuffer[src]; src ++; 82 var r = srcBuffer[src]; src ++; 83 var a = srcBuffer[src]; src ++; 84 byteArray[dst] = r; dst ++; //r 85 byteArray[dst] = g; dst ++; //g 86 byteArray[dst] = b; dst ++; //b 87 byteArray[dst] = a; dst ++; //a 88 } 89 } 90 return byteArray; 91 } 92 93 var FOURCC_DXT1 = fourCCToInt32("DXT1"); 94 var FOURCC_DXT3 = fourCCToInt32("DXT3"); 95 var FOURCC_DXT5 = fourCCToInt32("DXT5"); 96 97 var headerLengthInt = 31; // The header length in 32 bit ints 98 99 // Offsets into the header array 100 101 var off_magic = 0; 102 103 var off_size = 1; 104 var off_flags = 2; 105 var off_height = 3; 106 var off_width = 4; 107 108 var off_mipmapCount = 7; 109 110 var off_pfFlags = 20; 111 var off_pfFourCC = 21; 112 var off_RGBBitCount = 22; 113 var off_RBitMask = 23; 114 var off_GBitMask = 24; 115 var off_BBitMask = 25; 116 var off_ABitMask = 26; 117 118 var off_caps = 27; 119 var off_caps2 = 28; 120 var off_caps3 = 29; 121 var off_caps4 = 30; 122 123 // Parse header 124 125 var header = new Int32Array( buffer, 0, headerLengthInt ); 126 127 if ( header[ off_magic ] !== DDS_MAGIC ) { 128 129 console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); 130 return dds; 131 132 } 133 134 if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { 135 136 console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); 137 return dds; 138 139 } 140 141 var blockBytes; 142 143 var fourCC = header[ off_pfFourCC ]; 144 145 var isRGBAUncompressed = false; 146 147 switch ( fourCC ) { 148 149 case FOURCC_DXT1: 150 151 blockBytes = 8; 152 dds.format = THREE.RGB_S3TC_DXT1_Format; 153 break; 154 155 case FOURCC_DXT3: 156 157 blockBytes = 16; 158 dds.format = THREE.RGBA_S3TC_DXT3_Format; 159 break; 160 161 case FOURCC_DXT5: 162 163 blockBytes = 16; 164 dds.format = THREE.RGBA_S3TC_DXT5_Format; 165 break; 166 167 default: 168 169 if ( header[off_RGBBitCount] == 32 170 && header[off_RBitMask]&0xff0000 171 && header[off_GBitMask]&0xff00 172 && header[off_BBitMask]&0xff 173 && header[off_ABitMask]&0xff000000 ) { 174 isRGBAUncompressed = true; 175 blockBytes = 64; 176 dds.format = THREE.RGBAFormat; 177 } else { 178 console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); 179 return dds; 180 } 181 } 182 183 dds.mipmapCount = 1; 184 185 if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { 186 187 dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); 188 189 } 190 191 //TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc. 192 193 dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false; 194 195 dds.width = header[ off_width ]; 196 dds.height = header[ off_height ]; 197 198 var dataOffset = header[ off_size ] + 4; 199 200 // Extract mipmaps buffers 201 202 var width = dds.width; 203 var height = dds.height; 204 205 var faces = dds.isCubemap ? 6 : 1; 206 207 for ( var face = 0; face < faces; face ++ ) { 208 209 for ( var i = 0; i < dds.mipmapCount; i ++ ) { 210 211 if ( isRGBAUncompressed ) { 212 var byteArray = loadARGBMip( buffer, dataOffset, width, height ); 213 var dataLength = byteArray.length; 214 } else { 215 var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; 216 var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); 217 } 218 219 var mipmap = { "data": byteArray, "width": width, "height": height }; 220 dds.mipmaps.push( mipmap ); 221 222 dataOffset += dataLength; 223 224 width = Math.max( width * 0.5, 1 ); 225 height = Math.max( height * 0.5, 1 ); 226 227 } 228 229 width = dds.width; 230 height = dds.height; 231 232 } 233 234 return dds; 235 236 }; 237