Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KTX2Loader: Support compressed cubemaps #25909

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 55 additions & 43 deletions examples/jsm/loaders/KTX2Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,30 @@ class KTX2Loader extends Loader {

_createTextureFrom( transcodeResult, container ) {

const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
const { faces, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;

if ( type === 'error' ) return Promise.reject( error );

const texture = container.layerCount > 1
? new CompressedArrayTexture( mipmaps, width, height, container.layerCount, format, UnsignedByteType )
: new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
let texture;

if ( container.faceCount === 6 ) {

texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
texture = new CompressedTexture();
texture.image = faces;
texture.format = format;
texture.type = UnsignedByteType;

} else {

const mipmaps = faces[ 0 ].mipmaps;

texture = container.layerCount > 1
? new CompressedArrayTexture( mipmaps, width, height, container.layerCount, format, UnsignedByteType )
: new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );

}

texture.minFilter = faces[ 0 ].mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
texture.magFilter = LinearFilter;
texture.generateMipmaps = false;

Expand Down Expand Up @@ -388,17 +402,9 @@ KTX2Loader.BasisWorker = function () {

try {

const { width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags } = transcode( message.buffer );

const buffers = [];

for ( let i = 0; i < mipmaps.length; ++ i ) {

buffers.push( mipmaps[ i ].data.buffer );
const { faces, buffers, width, height, hasAlpha, format, dfdTransferFn, dfdFlags } = transcode( message.buffer );

}

self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags }, buffers );
self.postMessage( { type: 'transcode', id: message.id, faces, width, height, hasAlpha, format, dfdTransferFn, dfdFlags }, buffers );

} catch ( error ) {

Expand Down Expand Up @@ -457,15 +463,16 @@ KTX2Loader.BasisWorker = function () {
const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
const width = ktx2File.getWidth();
const height = ktx2File.getHeight();
const layers = ktx2File.getLayers() || 1;
const levels = ktx2File.getLevels();
const layerCount = ktx2File.getLayers() || 1;
const levelCount = ktx2File.getLevels();
const faceCount = ktx2File.getFaces();
const hasAlpha = ktx2File.getHasAlpha();
const dfdTransferFn = ktx2File.getDFDTransferFunc();
const dfdFlags = ktx2File.getDFDFlags();

const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha );

if ( ! width || ! height || ! levels ) {
if ( ! width || ! height || ! levelCount ) {

cleanup();
throw new Error( 'THREE.KTX2Loader: Invalid texture' );
Expand All @@ -479,49 +486,52 @@ KTX2Loader.BasisWorker = function () {

}

const mipmaps = [];
const faces = [];
const buffers = [];

for ( let mip = 0; mip < levels; mip ++ ) {
for ( let face = 0; face < faceCount; face ++ ) {

const layerMips = [];
const mipmaps = [];

let mipWidth, mipHeight;
for ( let mip = 0; mip < levelCount; mip ++ ) {

for ( let layer = 0; layer < layers; layer ++ ) {
const layerMips = [];

const levelInfo = ktx2File.getImageLevelInfo( mip, layer, 0 );
mipWidth = levelInfo.origWidth < 4 ? levelInfo.origWidth : levelInfo.width;
mipHeight = levelInfo.origHeight < 4 ? levelInfo.origHeight : levelInfo.height;
const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) );
const status = ktx2File.transcodeImage(
dst,
mip,
layer,
0,
transcoderFormat,
0,
- 1,
- 1,
);
let mipWidth, mipHeight;

for ( let layer = 0; layer < layerCount; layer ++ ) {

if ( ! status ) {
const levelInfo = ktx2File.getImageLevelInfo( mip, layer, face );
mipWidth = levelInfo.origWidth < 4 ? levelInfo.origWidth : levelInfo.width;
mipHeight = levelInfo.origHeight < 4 ? levelInfo.origHeight : levelInfo.height;
const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) );
const status = ktx2File.transcodeImage( dst, mip, layer, face, transcoderFormat, 0, - 1, - 1 );

cleanup();
throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
if ( ! status ) {

cleanup();
throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );

}

layerMips.push( dst );

}

layerMips.push( dst );
const mipData = concat( layerMips );

mipmaps.push( { data: mipData, width: mipWidth, height: mipHeight } );
buffers.push( mipData.buffer );

}

mipmaps.push( { data: concat( layerMips ), width: mipWidth, height: mipHeight } );
faces.push( { mipmaps, width, height, format: engineFormat } );

}

cleanup();

return { width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags };
return { faces, buffers, width, height, hasAlpha, format: engineFormat, dfdTransferFn, dfdFlags };

}

Expand Down Expand Up @@ -645,6 +655,8 @@ KTX2Loader.BasisWorker = function () {
/** Concatenates N byte arrays. */
function concat( arrays ) {

if ( arrays.length === 1 ) return arrays[ 0 ];

let totalByteLength = 0;

for ( const array of arrays ) {
Expand Down