293 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import {
 | |
| 	FloatType,
 | |
| 	HalfFloatType,
 | |
| 	UnsignedByteType,
 | |
| 	RGBAFormat,
 | |
| 	RGFormat,
 | |
| 	RGIntegerFormat,
 | |
| 	RedFormat,
 | |
| 	RedIntegerFormat,
 | |
| 	NoColorSpace,
 | |
| 	LinearSRGBColorSpace,
 | |
| 	SRGBColorSpace,
 | |
| 	DataTexture,
 | |
| 	REVISION,
 | |
| } from 'three';
 | |
| 
 | |
| import {
 | |
| 	write,
 | |
| 	KTX2Container,
 | |
| 	KHR_DF_CHANNEL_RGBSDA_ALPHA,
 | |
| 	KHR_DF_CHANNEL_RGBSDA_BLUE,
 | |
| 	KHR_DF_CHANNEL_RGBSDA_GREEN,
 | |
| 	KHR_DF_CHANNEL_RGBSDA_RED,
 | |
| 	KHR_DF_MODEL_RGBSDA,
 | |
| 	KHR_DF_PRIMARIES_BT709,
 | |
| 	KHR_DF_PRIMARIES_UNSPECIFIED,
 | |
| 	KHR_DF_SAMPLE_DATATYPE_FLOAT,
 | |
| 	KHR_DF_SAMPLE_DATATYPE_LINEAR,
 | |
| 	KHR_DF_SAMPLE_DATATYPE_SIGNED,
 | |
| 	KHR_DF_TRANSFER_LINEAR,
 | |
| 	KHR_DF_TRANSFER_SRGB,
 | |
| 	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 VK_FORMAT_MAP = {
 | |
| 
 | |
| 	[ RGBAFormat ]: {
 | |
| 		[ FloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
 | |
| 		},
 | |
| 		[ HalfFloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
 | |
| 		},
 | |
| 		[ UnsignedByteType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
 | |
| 			[ SRGBColorSpace ]: VK_FORMAT_R8G8B8A8_SRGB,
 | |
| 		},
 | |
| 	},
 | |
| 
 | |
| 	[ RGFormat ]: {
 | |
| 		[ FloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
 | |
| 		},
 | |
| 		[ HalfFloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
 | |
| 		},
 | |
| 		[ UnsignedByteType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R8G8_UNORM,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R8G8_UNORM,
 | |
| 			[ SRGBColorSpace ]: VK_FORMAT_R8G8_SRGB,
 | |
| 		},
 | |
| 	},
 | |
| 
 | |
| 	[ RedFormat ]: {
 | |
| 		[ FloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R32_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R32_SFLOAT,
 | |
| 		},
 | |
| 		[ HalfFloatType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R16_SFLOAT,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R16_SFLOAT,
 | |
| 		},
 | |
| 		[ UnsignedByteType ]: {
 | |
| 			[ NoColorSpace ]: VK_FORMAT_R8_UNORM,
 | |
| 			[ LinearSRGBColorSpace ]: VK_FORMAT_R8_UNORM,
 | |
| 			[ SRGBColorSpace ]: VK_FORMAT_R8_SRGB,
 | |
| 		},
 | |
| 	},
 | |
| 
 | |
| };
 | |
| 
 | |
| const KHR_DF_CHANNEL_MAP = {
 | |
| 
 | |
| 	0: KHR_DF_CHANNEL_RGBSDA_RED,
 | |
| 	1: KHR_DF_CHANNEL_RGBSDA_GREEN,
 | |
| 	2: KHR_DF_CHANNEL_RGBSDA_BLUE,
 | |
| 	3: KHR_DF_CHANNEL_RGBSDA_ALPHA,
 | |
| 
 | |
| };
 | |
| 
 | |
| const ERROR_INPUT = 'THREE.KTX2Exporter: Supported inputs are DataTexture, Data3DTexture, or WebGLRenderer and WebGLRenderTarget.';
 | |
| const ERROR_FORMAT = 'THREE.KTX2Exporter: Supported formats are RGBAFormat, RGFormat, or RedFormat.';
 | |
| const ERROR_TYPE = 'THREE.KTX2Exporter: Supported types are FloatType, HalfFloatType, or UnsignedByteType."';
 | |
| const ERROR_COLOR_SPACE = 'THREE.KTX2Exporter: Supported color spaces are SRGBColorSpace (UnsignedByteType only), LinearSRGBColorSpace, or NoColorSpace.';
 | |
| 
 | |
| export class KTX2Exporter {
 | |
| 
 | |
| 	parse( arg1, arg2 ) {
 | |
| 
 | |
| 		let texture;
 | |
| 
 | |
| 		if ( arg1.isDataTexture || arg1.isData3DTexture ) {
 | |
| 
 | |
| 			texture = arg1;
 | |
| 
 | |
| 		} else if ( arg1.isWebGLRenderer && arg2.isWebGLRenderTarget ) {
 | |
| 
 | |
| 			texture = toDataTexture( arg1, arg2 );
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			throw new Error( ERROR_INPUT );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		if ( VK_FORMAT_MAP[ texture.format ] === undefined ) {
 | |
| 
 | |
| 			throw new Error( ERROR_FORMAT );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		if ( VK_FORMAT_MAP[ texture.format ][ texture.type ] === undefined ) {
 | |
| 
 | |
| 			throw new Error( ERROR_TYPE );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		if ( VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ] === undefined ) {
 | |
| 
 | |
| 			throw new Error( ERROR_COLOR_SPACE );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		//
 | |
| 
 | |
| 		const array = texture.image.data;
 | |
| 		const channelCount = getChannelCount( texture );
 | |
| 		const container = new KTX2Container();
 | |
| 
 | |
| 		container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
 | |
| 		container.typeSize = array.BYTES_PER_ELEMENT;
 | |
| 		container.pixelWidth = texture.image.width;
 | |
| 		container.pixelHeight = texture.image.height;
 | |
| 
 | |
| 		if ( texture.isData3DTexture ) {
 | |
| 
 | |
| 			container.pixelDepth = texture.image.depth;
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		//
 | |
| 
 | |
| 		const basicDesc = container.dataFormatDescriptor[ 0 ];
 | |
| 
 | |
| 		basicDesc.colorModel = KHR_DF_MODEL_RGBSDA;
 | |
| 		basicDesc.colorPrimaries = texture.colorSpace === NoColorSpace
 | |
| 			? KHR_DF_PRIMARIES_UNSPECIFIED
 | |
| 			: KHR_DF_PRIMARIES_BT709;
 | |
| 		basicDesc.transferFunction = texture.colorSpace === SRGBColorSpace
 | |
| 			? KHR_DF_TRANSFER_SRGB
 | |
| 			: KHR_DF_TRANSFER_LINEAR;
 | |
| 
 | |
| 		basicDesc.texelBlockDimension = [ 0, 0, 0, 0 ];
 | |
| 
 | |
| 		basicDesc.bytesPlane = [
 | |
| 
 | |
| 			container.typeSize * channelCount, 0, 0, 0, 0, 0, 0, 0,
 | |
| 
 | |
| 		];
 | |
| 
 | |
| 		for ( let i = 0; i < channelCount; ++ i ) {
 | |
| 
 | |
| 			let channelType = KHR_DF_CHANNEL_MAP[ i ];
 | |
| 
 | |
| 			if ( texture.colorSpace === LinearSRGBColorSpace || texture.colorSpace === NoColorSpace ) {
 | |
| 
 | |
| 				channelType |= KHR_DF_SAMPLE_DATATYPE_LINEAR;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			if ( texture.type === FloatType || texture.type === HalfFloatType ) {
 | |
| 
 | |
| 				channelType |= KHR_DF_SAMPLE_DATATYPE_FLOAT;
 | |
| 				channelType |= KHR_DF_SAMPLE_DATATYPE_SIGNED;
 | |
| 
 | |
| 			}
 | |
| 
 | |
| 			basicDesc.samples.push( {
 | |
| 
 | |
| 				channelType: channelType,
 | |
| 				bitOffset: i * array.BYTES_PER_ELEMENT,
 | |
| 				bitLength: array.BYTES_PER_ELEMENT * 8 - 1,
 | |
| 				samplePosition: [ 0, 0, 0, 0 ],
 | |
| 				sampleLower: texture.type === UnsignedByteType ? 0 : - 1,
 | |
| 				sampleUpper: texture.type === UnsignedByteType ? 255 : 1,
 | |
| 
 | |
| 			} );
 | |
| 
 | |
| 		}
 | |
| 
 | |
| 		//
 | |
| 
 | |
| 		container.levels = [ {
 | |
| 
 | |
| 			levelData: new Uint8Array( array.buffer, array.byteOffset, array.byteLength ),
 | |
| 			uncompressedByteLength: array.byteLength,
 | |
| 
 | |
| 		} ];
 | |
| 
 | |
| 		//
 | |
| 
 | |
| 		container.keyValue[ 'KTXwriter' ] = `three.js ${ REVISION }`;
 | |
| 
 | |
| 		//
 | |
| 
 | |
| 		return write( container, { keepWriter: true } );
 | |
| 
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| function toDataTexture( renderer, rtt ) {
 | |
| 
 | |
| 	const channelCount = getChannelCount( rtt.texture );
 | |
| 
 | |
| 	let view;
 | |
| 
 | |
| 	if ( rtt.texture.type === FloatType ) {
 | |
| 
 | |
| 		view = new Float32Array( rtt.width * rtt.height * channelCount );
 | |
| 
 | |
| 	} else if ( rtt.texture.type === HalfFloatType ) {
 | |
| 
 | |
| 		view = new Uint16Array( rtt.width * rtt.height * channelCount );
 | |
| 
 | |
| 	} else if ( rtt.texture.type === UnsignedByteType ) {
 | |
| 
 | |
| 		view = new Uint8Array( rtt.width * rtt.height * channelCount );
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		throw new Error( ERROR_TYPE );
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	renderer.readRenderTargetPixels( rtt, 0, 0, rtt.width, rtt.height, view );
 | |
| 
 | |
| 	return new DataTexture( view, rtt.width, rtt.height, rtt.texture.format, rtt.texture.type );
 | |
| 
 | |
| }
 | |
| 
 | |
| function getChannelCount( texture ) {
 | |
| 
 | |
| 	switch ( texture.format ) {
 | |
| 
 | |
| 		case RGBAFormat:
 | |
| 
 | |
| 			return 4;
 | |
| 
 | |
| 		case RGFormat:
 | |
| 		case RGIntegerFormat:
 | |
| 
 | |
| 			return 2;
 | |
| 
 | |
| 		case RedFormat:
 | |
| 		case RedIntegerFormat:
 | |
| 
 | |
| 			return 1;
 | |
| 
 | |
| 		default:
 | |
| 
 | |
| 			throw new Error( ERROR_FORMAT );
 | |
| 
 | |
| 	}
 | |
| 
 | |
| }
 |