|
@@ -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/
|
|
@@ -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;
|
|
@@ -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
|
|
@@ -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 ) {
|
|
@@ -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;
|
|
|
|
|
@@ -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 = [];
|
|
|
|
|
@@ -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 };
|