252 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import {
 | 
						|
	CompressedTextureLoader,
 | 
						|
	RGBA_PVRTC_2BPPV1_Format,
 | 
						|
	RGBA_PVRTC_4BPPV1_Format,
 | 
						|
	RGB_PVRTC_2BPPV1_Format,
 | 
						|
	RGB_PVRTC_4BPPV1_Format
 | 
						|
} from 'three';
 | 
						|
 | 
						|
/*
 | 
						|
 *	 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 };
 |