Spaces:
Running
Running
/* | |
* 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 ( manager ) { | |
THREE.CompressedTextureLoader.call( this, manager ); | |
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 | |
}; | |
if ( header[ 0 ] === 0x03525650 ) { | |
// PVR v3 | |
return THREE.PVRLoader._parseV3( pvrDatas ); | |
} else if ( header[ 11 ] === 0x21525650 ) { | |
// PVR v2 | |
return THREE.PVRLoader._parseV2( pvrDatas ); | |
} else { | |
console.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 : | |
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 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 { | |
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 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; | |
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; | |
}; | |