import { CompressedTextureLoader, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format } from '../../../build/three.module.js'; /* * PVR v2 (legacy) parser * TODO : Add Support for PVR v3 format * TODO : implement loadMipmaps option */ class PVRLoader extends CompressedTextureLoader { constructor( manager ) { super( manager ); } parse( buffer, loadMipmaps ) { const headerLengthInt = 13; const header = new Uint32Array( buffer, 0, headerLengthInt ); const pvrDatas = { buffer: buffer, header: header, loadMipmaps: loadMipmaps }; if ( header[ 0 ] === 0x03525650 ) { // PVR v3 return _parseV3( pvrDatas ); } else if ( header[ 11 ] === 0x21525650 ) { // PVR v2 return _parseV2( pvrDatas ); } else { console.error( 'THREE.PVRLoader: Unknown PVR format.' ); } } } function _parseV3( pvrDatas ) { const header = pvrDatas.header; let bpp, format; const 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 = RGB_PVRTC_2BPPV1_Format; break; case 1 : // PVRTC 2bpp RGBA bpp = 2; format = RGBA_PVRTC_2BPPV1_Format; break; case 2 : // PVRTC 4bpp RGB bpp = 4; format = RGB_PVRTC_4BPPV1_Format; break; case 3 : // PVRTC 4bpp RGBA bpp = 4; format = RGBA_PVRTC_4BPPV1_Format; break; default : console.error( 'THREE.PVRLoader: 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 _extract( pvrDatas ); } function _parseV2( pvrDatas ) { const header = pvrDatas.header; const 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 ]; const TYPE_MASK = 0xff; const PVRTC_2 = 24, PVRTC_4 = 25; const formatFlags = flags & TYPE_MASK; let bpp, format; const _hasAlpha = bitmaskAlpha > 0; if ( formatFlags === PVRTC_4 ) { format = _hasAlpha ? RGBA_PVRTC_4BPPV1_Format : RGB_PVRTC_4BPPV1_Format; bpp = 4; } else if ( formatFlags === PVRTC_2 ) { format = _hasAlpha ? RGBA_PVRTC_2BPPV1_Format : RGB_PVRTC_2BPPV1_Format; bpp = 2; } else { console.error( 'THREE.PVRLoader: Unknown PVR 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 _extract( pvrDatas ); } function _extract( pvrDatas ) { const pvr = { mipmaps: [], width: pvrDatas.width, height: pvrDatas.height, format: pvrDatas.format, mipmapCount: pvrDatas.numMipmaps, isCubemap: pvrDatas.isCubemap }; const buffer = pvrDatas.buffer; let dataOffset = pvrDatas.dataPtr, dataSize = 0, blockSize = 0, blockWidth = 0, blockHeight = 0, widthBlocks = 0, heightBlocks = 0; const bpp = pvrDatas.bpp, numSurfs = pvrDatas.numSurfaces; if ( bpp === 2 ) { blockWidth = 8; blockHeight = 4; } else { blockWidth = 4; blockHeight = 4; } blockSize = ( blockWidth * blockHeight ) * bpp / 8; pvr.mipmaps.length = pvrDatas.numMipmaps * numSurfs; let mipLevel = 0; while ( mipLevel < pvrDatas.numMipmaps ) { const 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 ( let surfIndex = 0; surfIndex < numSurfs; surfIndex ++ ) { const byteArray = new Uint8Array( buffer, dataOffset, dataSize ); const mipmap = { data: byteArray, width: sWidth, height: sHeight }; pvr.mipmaps[ surfIndex * pvrDatas.numMipmaps + mipLevel ] = mipmap; dataOffset += dataSize; } mipLevel ++; } return pvr; } export { PVRLoader };