|
@@ -13,6 +13,7 @@
|
|
|
|
|
|
import {
|
|
import {
|
|
CompressedTexture,
|
|
CompressedTexture,
|
|
|
|
+ CompressedArrayTexture,
|
|
Data3DTexture,
|
|
Data3DTexture,
|
|
DataTexture,
|
|
DataTexture,
|
|
FileLoader,
|
|
FileLoader,
|
|
@@ -35,7 +36,7 @@ import {
|
|
RGBAFormat,
|
|
RGBAFormat,
|
|
RGFormat,
|
|
RGFormat,
|
|
sRGBEncoding,
|
|
sRGBEncoding,
|
|
- UnsignedByteType
|
|
|
|
|
|
+ UnsignedByteType,
|
|
} from 'three';
|
|
} from 'three';
|
|
import { WorkerPool } from '../utils/WorkerPool.js';
|
|
import { WorkerPool } from '../utils/WorkerPool.js';
|
|
import {
|
|
import {
|
|
@@ -236,16 +237,21 @@ class KTX2Loader extends Loader {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- _createTextureFrom( transcodeResult ) {
|
|
|
|
|
|
+ _createTextureFrom( transcodeResult, container ) {
|
|
|
|
|
|
const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
|
|
const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
|
|
|
|
|
|
if ( type === 'error' ) return Promise.reject( error );
|
|
if ( type === 'error' ) return Promise.reject( error );
|
|
|
|
|
|
- const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
|
|
|
|
|
|
+ const texture = container.layerCount > 1
|
|
|
|
+ ? new CompressedArrayTexture( mipmaps, width, height, container.layerCount, format, UnsignedByteType )
|
|
|
|
+ : new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
|
|
|
|
+
|
|
|
|
+
|
|
texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
|
|
texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
|
|
texture.magFilter = LinearFilter;
|
|
texture.magFilter = LinearFilter;
|
|
texture.generateMipmaps = false;
|
|
texture.generateMipmaps = false;
|
|
|
|
+
|
|
texture.needsUpdate = true;
|
|
texture.needsUpdate = true;
|
|
texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
|
|
texture.encoding = dfdTransferFn === KHR_DF_TRANSFER_SRGB ? sRGBEncoding : LinearEncoding;
|
|
texture.premultiplyAlpha = !! ( dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED );
|
|
texture.premultiplyAlpha = !! ( dfdFlags & KHR_DF_FLAG_ALPHA_PREMULTIPLIED );
|
|
@@ -257,7 +263,7 @@ class KTX2Loader extends Loader {
|
|
/**
|
|
/**
|
|
* @param {ArrayBuffer} buffer
|
|
* @param {ArrayBuffer} buffer
|
|
* @param {object?} config
|
|
* @param {object?} config
|
|
- * @return {Promise<CompressedTexture|DataTexture|Data3DTexture>}
|
|
|
|
|
|
+ * @return {Promise<CompressedTexture|CompressedArrayTexture|DataTexture|Data3DTexture>}
|
|
*/
|
|
*/
|
|
async _createTexture( buffer, config = {} ) {
|
|
async _createTexture( buffer, config = {} ) {
|
|
|
|
|
|
@@ -270,13 +276,12 @@ class KTX2Loader extends Loader {
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
-
|
|
|
|
const taskConfig = config;
|
|
const taskConfig = config;
|
|
const texturePending = this.init().then( () => {
|
|
const texturePending = this.init().then( () => {
|
|
|
|
|
|
return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );
|
|
return this.workerPool.postMessage( { type: 'transcode', buffer, taskConfig: taskConfig }, [ buffer ] );
|
|
|
|
|
|
- } ).then( ( e ) => this._createTextureFrom( e.data ) );
|
|
|
|
|
|
+ } ).then( ( e ) => this._createTextureFrom( e.data, container ) );
|
|
|
|
|
|
// Cache the task result.
|
|
// Cache the task result.
|
|
_taskCache.set( buffer, { promise: texturePending } );
|
|
_taskCache.set( buffer, { promise: texturePending } );
|
|
@@ -437,6 +442,7 @@ KTX2Loader.BasisWorker = function () {
|
|
const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
|
|
const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
|
|
const width = ktx2File.getWidth();
|
|
const width = ktx2File.getWidth();
|
|
const height = ktx2File.getHeight();
|
|
const height = ktx2File.getHeight();
|
|
|
|
+ const layers = ktx2File.getLayers() || 1;
|
|
const levels = ktx2File.getLevels();
|
|
const levels = ktx2File.getLevels();
|
|
const hasAlpha = ktx2File.getHasAlpha();
|
|
const hasAlpha = ktx2File.getHasAlpha();
|
|
const dfdTransferFn = ktx2File.getDFDTransferFunc();
|
|
const dfdTransferFn = ktx2File.getDFDTransferFunc();
|
|
@@ -462,30 +468,39 @@ KTX2Loader.BasisWorker = function () {
|
|
|
|
|
|
for ( let mip = 0; mip < levels; mip ++ ) {
|
|
for ( let mip = 0; mip < levels; mip ++ ) {
|
|
|
|
|
|
- const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 );
|
|
|
|
- const mipWidth = levelInfo.origWidth;
|
|
|
|
- const mipHeight = levelInfo.origHeight;
|
|
|
|
- const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) );
|
|
|
|
-
|
|
|
|
- const status = ktx2File.transcodeImage(
|
|
|
|
- dst,
|
|
|
|
- mip,
|
|
|
|
- 0,
|
|
|
|
- 0,
|
|
|
|
- transcoderFormat,
|
|
|
|
- 0,
|
|
|
|
- - 1,
|
|
|
|
- - 1,
|
|
|
|
- );
|
|
|
|
|
|
+ const layerMips = [];
|
|
|
|
+
|
|
|
|
+ let mipWidth, mipHeight;
|
|
|
|
+
|
|
|
|
+ for ( let layer = 0; layer < layers; layer ++ ) {
|
|
|
|
+
|
|
|
|
+ const levelInfo = ktx2File.getImageLevelInfo( mip, layer, 0 );
|
|
|
|
+ mipWidth = levelInfo.origWidth;
|
|
|
|
+ mipHeight = levelInfo.origHeight;
|
|
|
|
+ const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, layer, 0, transcoderFormat ) );
|
|
|
|
+ const status = ktx2File.transcodeImage(
|
|
|
|
+ dst,
|
|
|
|
+ mip,
|
|
|
|
+ layer,
|
|
|
|
+ 0,
|
|
|
|
+ transcoderFormat,
|
|
|
|
+ 0,
|
|
|
|
+ - 1,
|
|
|
|
+ - 1,
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ if ( ! status ) {
|
|
|
|
|
|
- if ( ! status ) {
|
|
|
|
|
|
+ cleanup();
|
|
|
|
+ throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
|
|
|
|
|
|
- cleanup();
|
|
|
|
- throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ layerMips.push( dst );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
|
|
|
|
|
|
+ mipmaps.push( { data: concat( layerMips ), width: mipWidth, height: mipHeight } );
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
@@ -612,6 +627,33 @@ KTX2Loader.BasisWorker = function () {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /** Concatenates N byte arrays. */
|
|
|
|
+ function concat( arrays ) {
|
|
|
|
+
|
|
|
|
+ let totalByteLength = 0;
|
|
|
|
+
|
|
|
|
+ for ( const array of arrays ) {
|
|
|
|
+
|
|
|
|
+ totalByteLength += array.byteLength;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const result = new Uint8Array( totalByteLength );
|
|
|
|
+
|
|
|
|
+ let byteOffset = 0;
|
|
|
|
+
|
|
|
|
+ for ( const array of arrays ) {
|
|
|
|
+
|
|
|
|
+ result.set( array, byteOffset );
|
|
|
|
+
|
|
|
|
+ byteOffset += array.byteLength;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return result;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
};
|
|
};
|
|
|
|
|
|
//
|
|
//
|
|
@@ -673,8 +715,6 @@ async function createDataTexture( container ) {
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- //
|
|
|
|
-
|
|
|
|
const level = container.levels[ 0 ];
|
|
const level = container.levels[ 0 ];
|
|
|
|
|
|
let levelData;
|
|
let levelData;
|
|
@@ -731,7 +771,6 @@ async function createDataTexture( container ) {
|
|
view = levelData;
|
|
view = levelData;
|
|
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
//
|
|
//
|
|
|
|
|
|
const texture = pixelDepth === 0
|
|
const texture = pixelDepth === 0
|