/* * PVRLoader * Author: pierre lepers * Date: 17/09/2014 11:09 * * PVR v2 (legacy) parser * TODO : Add Support for PVR v3 format * TODO : implement loadMipmaps option */ THREE.PVRLoader = function () { this._parser = THREE.PVRLoader.parse; }; THREE.PVRLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype ); THREE.PVRLoader.prototype.constructor = THREE.PVRLoader; THREE.PVRLoader.parse = function ( buffer, loadMipmaps ) { var headerLengthInt = 13; var header = new Uint32Array( buffer, 0, headerLengthInt ); var pvrDatas = { buffer: buffer, header : header, loadMipmaps : loadMipmaps }; // PVR v3 if ( header[0] === 0x03525650 ) { return THREE.PVRLoader._parseV3( pvrDatas ); } // PVR v2 else if ( header[11] === 0x21525650) { return THREE.PVRLoader._parseV2( pvrDatas ); } else { throw new Error( "[THREE.PVRLoader] Unknown PVR format" ); } }; THREE.PVRLoader._parseV3 = function ( pvrDatas ) { var header = pvrDatas.header; var bpp, format; var metaLen = header[12], pixelFormat = header[2], height = header[6], width = header[7], numSurfs = header[9], numFaces = header[10], numMipmaps = header[11]; switch ( pixelFormat ) { case 0 : // PVRTC 2bpp RGB bpp = 2; format = THREE.RGB_PVRTC_2BPPV1_Format; break; case 1 : // PVRTC 2bpp RGBA bpp = 2 format = THREE.RGBA_PVRTC_2BPPV1_Format; break; case 2 : // PVRTC 4bpp RGB bpp = 4 format = THREE.RGB_PVRTC_4BPPV1_Format; break; case 3 : // PVRTC 4bpp RGBA bpp = 4 format = THREE.RGBA_PVRTC_4BPPV1_Format; break; default : throw new Error( "pvrtc - unsupported PVR format " + pixelFormat); } pvrDatas.dataPtr = 52 + metaLen; pvrDatas.bpp = bpp; pvrDatas.format = format; pvrDatas.width = width; pvrDatas.height = height; pvrDatas.numSurfaces = numFaces; pvrDatas.numMipmaps = numMipmaps; pvrDatas.isCubemap = (numFaces === 6); return THREE.PVRLoader._extract( pvrDatas ); }; THREE.PVRLoader._parseV2 = function ( pvrDatas ) { var header = pvrDatas.header; var headerLength = header[0], height = header[1], width = header[2], numMipmaps = header[3], flags = header[4], dataLength = header[5], bpp = header[6], bitmaskRed = header[7], bitmaskGreen = header[8], bitmaskBlue = header[9], bitmaskAlpha = header[10], pvrTag = header[11], numSurfs = header[12]; var TYPE_MASK = 0xff var PVRTC_2 = 24, PVRTC_4 = 25 var formatFlags = flags & TYPE_MASK; var bpp, format; var _hasAlpha = bitmaskAlpha > 0; if (formatFlags === PVRTC_4 ) { format = _hasAlpha ? THREE.RGBA_PVRTC_4BPPV1_Format : THREE.RGB_PVRTC_4BPPV1_Format; bpp = 4; } else if ( formatFlags === PVRTC_2) { format = _hasAlpha ? THREE.RGBA_PVRTC_2BPPV1_Format : THREE.RGB_PVRTC_2BPPV1_Format; bpp = 2; } else throw new Error( "pvrtc - unknown format " + formatFlags); pvrDatas.dataPtr = headerLength; pvrDatas.bpp = bpp; pvrDatas.format = format; pvrDatas.width = width; pvrDatas.height = height; pvrDatas.numSurfaces = numSurfs; pvrDatas.numMipmaps = numMipmaps + 1; // guess cubemap type seems tricky in v2 // it juste a pvr containing 6 surface (no explicit cubemap type) pvrDatas.isCubemap = (numSurfs === 6); return THREE.PVRLoader._extract( pvrDatas ); }; THREE.PVRLoader._extract = function ( pvrDatas ) { var pvr = { mipmaps: [], width: pvrDatas.width, height: pvrDatas.height, format: pvrDatas.format, mipmapCount: pvrDatas.numMipmaps, isCubemap : pvrDatas.isCubemap }; var buffer = pvrDatas.buffer; // console.log( "--------------------------" ); // console.log( "headerLength ", headerLength); // console.log( "height ", height ); // console.log( "width ", width ); // console.log( "numMipmaps ", numMipmaps ); // console.log( "flags ", flags ); // console.log( "dataLength ", dataLength ); // console.log( "bpp ", bpp ); // console.log( "bitmaskRed ", bitmaskRed ); // console.log( "bitmaskGreen ", bitmaskGreen); // console.log( "bitmaskBlue ", bitmaskBlue ); // console.log( "bitmaskAlpha ", bitmaskAlpha); // console.log( "pvrTag ", pvrTag ); // console.log( "numSurfs ", numSurfs ); var dataOffset = pvrDatas.dataPtr, bpp = pvrDatas.bpp, numSurfs = pvrDatas.numSurfaces, dataSize = 0, blockSize = 0, blockWidth = 0, blockHeight = 0, widthBlocks = 0, heightBlocks = 0; if ( bpp === 2 ) { blockWidth = 8; blockHeight = 4; } else { blockWidth = 4; blockHeight = 4; } blockSize = (blockWidth * blockHeight) * bpp / 8; pvr.mipmaps.length = pvrDatas.numMipmaps * numSurfs; var mipLevel = 0; while (mipLevel < pvrDatas.numMipmaps) { var sWidth = pvrDatas.width >> mipLevel, sHeight = pvrDatas.height >> mipLevel; widthBlocks = sWidth / blockWidth; heightBlocks = sHeight / blockHeight; // Clamp to minimum number of blocks if (widthBlocks < 2) widthBlocks = 2; if (heightBlocks < 2) heightBlocks = 2; dataSize = widthBlocks * heightBlocks * blockSize; for ( var surfIndex = 0; surfIndex < numSurfs; surfIndex ++ ) { var byteArray = new Uint8Array( buffer, dataOffset, dataSize ); var mipmap = { data: byteArray, width: sWidth, height: sHeight }; pvr.mipmaps[ surfIndex * pvrDatas.numMipmaps + mipLevel] = mipmap; dataOffset += dataSize; } mipLevel ++; } return pvr; }