Skip to content

Commit

Permalink
KTX2Loader: Support loading uncompressed DataTexture and Data3DTexture (
Browse files Browse the repository at this point in the history
  • Loading branch information
donmccurdy authored Jun 1, 2022
1 parent eaa5973 commit cd583bc
Showing 1 changed file with 156 additions and 26 deletions.
182 changes: 156 additions & 26 deletions examples/jsm/loaders/KTX2Loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
*
* 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.
* a wide variety of GPU texture compression formats, as well as some
* uncompressed DataTexture and Data3DTexture formats.
*
* References:
* - KTX: http://github.khronos.org/KTX-Specification/
Expand All @@ -13,25 +13,47 @@

import {
CompressedTexture,
Data3DTexture,
DataTexture,
FileLoader,
FloatType,
HalfFloatType,
LinearEncoding,
LinearFilter,
LinearMipmapLinearFilter,
Loader,
RGBAFormat,
RedFormat,
RGB_ETC1_Format,
RGB_ETC2_Format,
RGB_PVRTC_4BPPV1_Format,
RGB_S3TC_DXT1_Format,
RGBA_ASTC_4x4_Format,
RGBA_BPTC_Format,
RGBA_ETC2_EAC_Format,
RGBA_PVRTC_4BPPV1_Format,
RGBA_S3TC_DXT5_Format,
RGB_ETC1_Format,
RGB_ETC2_Format,
RGB_PVRTC_4BPPV1_Format,
RGB_S3TC_DXT1_Format,
RGBAFormat,
RGFormat,
sRGBEncoding,
UnsignedByteType
} from 'three';
import { WorkerPool } from '../utils/WorkerPool.js';
import {
read,
VK_FORMAT_UNDEFINED,
VK_FORMAT_R16_SFLOAT,
VK_FORMAT_R16G16_SFLOAT,
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_R32_SFLOAT,
VK_FORMAT_R32G32_SFLOAT,
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_FORMAT_R8_SRGB,
VK_FORMAT_R8_UNORM,
VK_FORMAT_R8G8_SRGB,
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_FORMAT_R8G8B8A8_UNORM,
} from '../libs/ktx-parse.module.js';

const KTX2TransferSRGB = 2;
const KTX2_ALPHA_PREMULTIPLIED = 1;
Expand Down Expand Up @@ -189,8 +211,6 @@ class KTX2Loader extends Loader {
loader.setResponseType( 'arraybuffer' );
loader.setWithCredentials( this.withCredentials );

const texture = new CompressedTexture();

loader.load( url, ( buffer ) => {

// Check for an existing task using this buffer. A transferred buffer cannot be transferred
Expand All @@ -203,21 +223,12 @@ class KTX2Loader extends Loader {

}

this._createTexture( [ buffer ] )
.then( function ( _texture ) {

texture.copy( _texture );
texture.needsUpdate = true;

if ( onLoad ) onLoad( texture );

} )
this._createTexture( buffer )
.then( ( texture ) => onLoad ? onLoad( texture ) : null )
.catch( onError );

}, onProgress, onError );

return texture;

}

_createTextureFrom( transcodeResult ) {
Expand All @@ -239,21 +250,31 @@ class KTX2Loader extends Loader {
}

/**
* @param {ArrayBuffer[]} buffers
* @param {ArrayBuffer} buffer
* @param {object?} config
* @return {Promise<CompressedTexture>}
* @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
*/
_createTexture( buffers, config = {} ) {
_createTexture( buffer, config = {} ) {

const container = read( new Uint8Array( buffer ) );

if ( container.vkFormat !== VK_FORMAT_UNDEFINED ) {

return createDataTexture( container );

}

//

const taskConfig = config;
const texturePending = this.init().then( () => {

return this.workerPool.postMessage( { type: 'transcode', buffers, taskConfig: taskConfig }, buffers );
return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );

} ).then( ( e ) => this._createTextureFrom( e.data ) );

// Cache the task result.
_taskCache.set( buffers[ 0 ], { promise: texturePending } );
_taskCache.set( buffer, { promise: texturePending } );

return texturePending;

Expand Down Expand Up @@ -342,7 +363,7 @@ KTX2Loader.BasisWorker = function () {

try {

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

const buffers = [];

Expand Down Expand Up @@ -588,4 +609,113 @@ KTX2Loader.BasisWorker = function () {

};

//
// DataTexture and Data3DTexture parsing.

const FORMAT_MAP = {

[VK_FORMAT_R32G32B32A32_SFLOAT]: RGBAFormat,
[VK_FORMAT_R16G16B16A16_SFLOAT]: RGBAFormat,
[VK_FORMAT_R8G8B8A8_UNORM]: RGBAFormat,
[VK_FORMAT_R8G8B8A8_SRGB]: RGBAFormat,

[VK_FORMAT_R32G32_SFLOAT]: RGFormat,
[VK_FORMAT_R16G16_SFLOAT]: RGFormat,
[VK_FORMAT_R8G8_UNORM]: RGFormat,
[VK_FORMAT_R8G8_SRGB]: RGFormat,

[VK_FORMAT_R32_SFLOAT]: RedFormat,
[VK_FORMAT_R16_SFLOAT]: RedFormat,
[VK_FORMAT_R8_SRGB]: RedFormat,
[VK_FORMAT_R8_UNORM]: RedFormat,

};

const TYPE_MAP = {

[VK_FORMAT_R32G32B32A32_SFLOAT]: FloatType,
[VK_FORMAT_R16G16B16A16_SFLOAT]: HalfFloatType,
[VK_FORMAT_R8G8B8A8_UNORM]: UnsignedByteType,
[VK_FORMAT_R8G8B8A8_SRGB]: UnsignedByteType,

[VK_FORMAT_R32G32_SFLOAT]: FloatType,
[VK_FORMAT_R16G16_SFLOAT]: HalfFloatType,
[VK_FORMAT_R8G8_UNORM]: UnsignedByteType,
[VK_FORMAT_R8G8_SRGB]: UnsignedByteType,

[VK_FORMAT_R32_SFLOAT]: FloatType,
[VK_FORMAT_R16_SFLOAT]: HalfFloatType,
[VK_FORMAT_R8_SRGB]: UnsignedByteType,
[VK_FORMAT_R8_UNORM]: UnsignedByteType,

};

const ENCODING_MAP = {

[VK_FORMAT_R8G8B8A8_SRGB]: sRGBEncoding,
[VK_FORMAT_R8G8_SRGB]: sRGBEncoding,
[VK_FORMAT_R8_SRGB]: sRGBEncoding,

};

function createDataTexture( container ) {

const { vkFormat, pixelWidth, pixelHeight, pixelDepth } = container;

if ( FORMAT_MAP[ vkFormat ] === undefined ) {

throw new Error( 'THREE.KTX2Loader: Unsupported vkFormat.' );

}

//

let view;

const levelData = container.levels[ 0 ].levelData;

if ( TYPE_MAP[ vkFormat ] === FloatType ) {

view = new Float32Array(

levelData.buffer,
levelData.byteOffset,
levelData.byteLength / Float32Array.BYTES_PER_ELEMENT

);

} else if ( TYPE_MAP[ vkFormat ] === HalfFloatType ) {

view = new Uint16Array(

levelData.buffer,
levelData.byteOffset,
levelData.byteLength / Uint16Array.BYTES_PER_ELEMENT

);

} else {

view = levelData;

}

//

const texture = pixelDepth === 0
? new DataTexture( view, pixelWidth, pixelHeight )
: new Data3DTexture( view, pixelWidth, pixelHeight, pixelDepth );

texture.type = TYPE_MAP[ vkFormat ];
texture.format = FORMAT_MAP[ vkFormat ];
texture.encoding = ENCODING_MAP[ vkFormat ] || LinearEncoding;

texture.needsUpdate = true;

//

return Promise.resolve( texture );

}

export { KTX2Loader };

0 comments on commit cd583bc

Please sign in to comment.