three 基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
5.4 KiB

2 years ago
/**
* Loader for KTX 2.0 GPU Texture containers.
*
* KTX 2.0 is a container format for various GPU texture formats. The loader
* supports Basis Universal GPU textures, which can be quickly transcoded to
* a wide variety of GPU texture compression formats. While KTX 2.0 also allows
* other hardware-specific formats, this loader does not yet parse them.
*
* This loader parses the KTX 2.0 container and then relies on
* THREE.BasisTextureLoader to complete the transcoding process.
*
* References:
* - KTX: http://github.khronos.org/KTX-Specification/
* - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
*/
import {
CompressedTexture,
CompressedTextureLoader,
FileLoader,
LinearEncoding,
sRGBEncoding,
} from '../../../build/three.module.js';
import {
read as readKTX,
KTX2ChannelETC1S,
KTX2ChannelUASTC,
KTX2Flags,
KTX2Model,
KTX2SupercompressionScheme,
KTX2Transfer
} from '../libs/ktx-parse.module.js';
import { BasisTextureLoader } from './BasisTextureLoader.js';
import { ZSTDDecoder } from '../libs/zstddec.module.js';
class KTX2Loader extends CompressedTextureLoader {
constructor( manager ) {
super( manager );
this.basisLoader = new BasisTextureLoader( manager );
this.zstd = new ZSTDDecoder();
this.zstd.init();
if ( typeof MSC_TRANSCODER !== 'undefined' ) {
console.warn(
'THREE.KTX2Loader: Please update to latest "basis_transcoder".'
+ ' "msc_basis_transcoder" is no longer supported in three.js r125+.'
);
}
}
setTranscoderPath( path ) {
this.basisLoader.setTranscoderPath( path );
return this;
}
setWorkerLimit( path ) {
this.basisLoader.setWorkerLimit( path );
return this;
}
detectSupport( renderer ) {
this.basisLoader.detectSupport( renderer );
return this;
}
dispose() {
this.basisLoader.dispose();
return this;
}
load( url, onLoad, onProgress, onError ) {
var scope = this;
var texture = new CompressedTexture();
var bufferPending = new Promise( function ( resolve, reject ) {
new FileLoader( scope.manager )
.setPath( scope.path )
.setResponseType( 'arraybuffer' )
.load( url, resolve, onProgress, reject );
} );
bufferPending
.then( function ( buffer ) {
scope.parse( buffer, function ( _texture ) {
texture.copy( _texture );
texture.needsUpdate = true;
if ( onLoad ) onLoad( texture );
}, onError );
} )
.catch( onError );
return texture;
}
parse( buffer, onLoad, onError ) {
var scope = this;
var ktx = readKTX( new Uint8Array( buffer ) );
if ( ktx.pixelDepth > 0 ) {
throw new Error( 'THREE.KTX2Loader: Only 2D textures are currently supported.' );
}
if ( ktx.layerCount > 1 ) {
throw new Error( 'THREE.KTX2Loader: Array textures are not currently supported.' );
}
if ( ktx.faceCount > 1 ) {
throw new Error( 'THREE.KTX2Loader: Cube textures are not currently supported.' );
}
var dfd = KTX2Utils.getBasicDFD( ktx );
KTX2Utils.createLevels( ktx, this.zstd ).then( function ( levels ) {
var basisFormat = dfd.colorModel === KTX2Model.UASTC
? BasisTextureLoader.BasisFormat.UASTC_4x4
: BasisTextureLoader.BasisFormat.ETC1S;
var parseConfig = {
levels: levels,
width: ktx.pixelWidth,
height: ktx.pixelHeight,
basisFormat: basisFormat,
hasAlpha: KTX2Utils.getAlpha( ktx ),
};
if ( basisFormat === BasisTextureLoader.BasisFormat.ETC1S ) {
parseConfig.globalData = ktx.globalData;
}
return scope.basisLoader.parseInternalAsync( parseConfig );
} ).then( function ( texture ) {
texture.encoding = dfd.transferFunction === KTX2Transfer.SRGB
? sRGBEncoding
: LinearEncoding;
texture.premultiplyAlpha = KTX2Utils.getPremultiplyAlpha( ktx );
onLoad( texture );
} ).catch( onError );
return this;
}
}
var KTX2Utils = {
createLevels: async function ( ktx, zstd ) {
if ( ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD ) {
await zstd.init();
}
var levels = [];
var width = ktx.pixelWidth;
var height = ktx.pixelHeight;
for ( var levelIndex = 0; levelIndex < ktx.levels.length; levelIndex ++ ) {
var levelWidth = Math.max( 1, Math.floor( width / Math.pow( 2, levelIndex ) ) );
var levelHeight = Math.max( 1, Math.floor( height / Math.pow( 2, levelIndex ) ) );
var levelData = ktx.levels[ levelIndex ].levelData;
if ( ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD ) {
levelData = zstd.decode( levelData, ktx.levels[ levelIndex ].uncompressedByteLength );
}
levels.push( {
index: levelIndex,
width: levelWidth,
height: levelHeight,
data: levelData,
} );
}
return levels;
},
getBasicDFD: function ( ktx ) {
// Basic Data Format Descriptor Block is always the first DFD.
return ktx.dataFormatDescriptor[ 0 ];
},
getAlpha: function ( ktx ) {
var dfd = this.getBasicDFD( ktx );
// UASTC
if ( dfd.colorModel === KTX2Model.UASTC ) {
if ( ( dfd.samples[ 0 ].channelID & 0xF ) === KTX2ChannelUASTC.RGBA ) {
return true;
}
return false;
}
// ETC1S
if ( dfd.samples.length === 2
&& ( dfd.samples[ 1 ].channelID & 0xF ) === KTX2ChannelETC1S.AAA ) {
return true;
}
return false;
},
getPremultiplyAlpha: function ( ktx ) {
var dfd = this.getBasicDFD( ktx );
return !! ( dfd.flags & KTX2Flags.ALPHA_PREMULTIPLIED );
},
};
export { KTX2Loader };