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 }; |