diff --git a/src/Three.TSL.js b/src/Three.TSL.js index 0ea6849fbe2758..650f0c2418e642 100644 --- a/src/Three.TSL.js +++ b/src/Three.TSL.js @@ -417,6 +417,9 @@ export const output = TSL.output; export const outputStruct = TSL.outputStruct; export const overlay = TSL.overlay; export const overloadingFn = TSL.overloadingFn; +export const packHalf2x16 = TSL.packHalf2x16; +export const packSnorm2x16 = TSL.packSnorm2x16; +export const packUnorm2x16 = TSL.packUnorm2x16; export const parabola = TSL.parabola; export const parallaxDirection = TSL.parallaxDirection; export const parallaxUV = TSL.parallaxUV; @@ -583,6 +586,9 @@ export const uniformCubeTexture = TSL.uniformCubeTexture; export const uniformGroup = TSL.uniformGroup; export const uniformFlow = TSL.uniformFlow; export const uniformTexture = TSL.uniformTexture; +export const unpackHalf2x16 = TSL.unpackHalf2x16; +export const unpackSnorm2x16 = TSL.unpackSnorm2x16; +export const unpackUnorm2x16 = TSL.unpackUnorm2x16; export const unpremultiplyAlpha = TSL.unpremultiplyAlpha; export const userData = TSL.userData; export const uv = TSL.uv; diff --git a/src/nodes/TSL.js b/src/nodes/TSL.js index da2f0ca507dd54..10af3cd93f32ab 100644 --- a/src/nodes/TSL.js +++ b/src/nodes/TSL.js @@ -23,6 +23,8 @@ export * from './math/BitcastNode.js'; export * from './math/BitcountNode.js'; export * from './math/Hash.js'; export * from './math/MathUtils.js'; +export * from './math/PackFloatNode.js'; +export * from './math/UnpackFloatNode.js'; export * from './math/TriNoise3D.js'; // utils diff --git a/src/nodes/math/PackFloatNode.js b/src/nodes/math/PackFloatNode.js new file mode 100644 index 00000000000000..3a71450bb33bc0 --- /dev/null +++ b/src/nodes/math/PackFloatNode.js @@ -0,0 +1,98 @@ +import TempNode from '../core/TempNode.js'; +import { nodeProxyIntent } from '../tsl/TSLCore.js'; + +/** + * This node represents an operation that packs floating-point values of a vector into an unsigned 32-bit integer + * + * @augments TempNode + */ +class PackFloatNode extends TempNode { + + static get type() { + + return 'PackFloatNode'; + + } + + /** + * + * @param {'snorm' | 'unorm' | 'float16'} encoding - The numeric encoding that describes how the float values are mapped to the integer range. + * @param {Node} vectorNode - The vector node to be packed + */ + constructor( encoding, vectorNode ) { + + super(); + + /** + * The vector to be packed. + * + * @type {Node} + */ + this.vectorNode = vectorNode; + + /** + * The numeric encoding. + * + * @type {string} + */ + this.encoding = encoding; + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isPackFloatNode = true; + + } + + getNodeType() { + + return 'uint'; + + } + + generate( builder ) { + + const inputType = this.vectorNode.getNodeType( builder ); + return `${ builder.getFloatPackingMethod( this.encoding ) }(${ this.vectorNode.build( builder, inputType )})`; + + } + +} + +export default PackFloatNode; + +/** + * Converts each component of the normalized float to 16-bit integer values. The results are packed into a single unsigned integer. + * round(clamp(c, -1, +1) * 32767.0) + * + * @tsl + * @function + * @param {Node} value - The 2-component vector to be packed + * @returns {Node} + */ +export const packSnorm2x16 = /*@__PURE__*/ nodeProxyIntent( PackFloatNode, 'snorm' ).setParameterLength( 1 ); + +/** + * Converts each component of the normalized float to 16-bit integer values. The results are packed into a single unsigned integer. + * round(clamp(c, 0, +1) * 65535.0) + * + * @tsl + * @function + * @param {Node} value - The 2-component vector to be packed + * @returns {Node} + */ +export const packUnorm2x16 = /*@__PURE__*/ nodeProxyIntent( PackFloatNode, 'unorm' ).setParameterLength( 1 ); + +/** + * Converts each component of the vec2 to 16-bit floating-point values. The results are packed into a single unsigned integer. + * + * @tsl + * @function + * @param {Node} value - The 2-component vector to be packed + * @returns {Node} + */ +export const packHalf2x16 = /*@__PURE__*/ nodeProxyIntent( PackFloatNode, 'float16' ).setParameterLength( 1 ); diff --git a/src/nodes/math/UnpackFloatNode.js b/src/nodes/math/UnpackFloatNode.js new file mode 100644 index 00000000000000..05c70afcf7ad41 --- /dev/null +++ b/src/nodes/math/UnpackFloatNode.js @@ -0,0 +1,96 @@ +import TempNode from '../core/TempNode.js'; +import { nodeProxyIntent } from '../tsl/TSLCore.js'; + +/** + * This node represents an operation that unpacks values from a 32-bit unsigned integer, reinterpreting the results as a floating-point vector + * + * @augments TempNode + */ +class UnpackFloatNode extends TempNode { + + static get type() { + + return 'UnpackFloatNode'; + + } + + /** + * + * @param {'snorm' | 'unorm' | 'float16'} encoding - The numeric encoding that describes how the integer values are mapped to the float range + * @param {Node} uintNode - The uint node to be unpacked + */ + constructor( encoding, uintNode ) { + + super(); + + /** + * The unsigned integer to be unpacked. + * + * @type {Node} + */ + this.uintNode = uintNode; + + /** + * The numeric encoding. + * + * @type {string} + */ + this.encoding = encoding; + + /** + * This flag can be used for type testing. + * + * @type {boolean} + * @readonly + * @default true + */ + this.isUnpackFloatNode = true; + + } + + getNodeType() { + + return 'vec2'; + + } + + generate( builder ) { + + const inputType = this.uintNode.getNodeType( builder ); + return `${ builder.getFloatUnpackingMethod( this.encoding ) }(${ this.uintNode.build( builder, inputType )})`; + + } + +} + +export default UnpackFloatNode; + +/** + * Unpacks a 32-bit unsigned integer into two 16-bit values, interpreted as normalized signed integers. Returns a vec2 with both values. + * + * @tsl + * @function + * @param {Node} value - The unsigned integer to be unpacked + * @returns {Node} + */ +export const unpackSnorm2x16 = /*@__PURE__*/ nodeProxyIntent( UnpackFloatNode, 'snorm' ).setParameterLength( 1 ); + +/** + * Unpacks a 32-bit unsigned integer into two 16-bit values, interpreted as normalized unsigned integers. Returns a vec2 with both values. + * + * @tsl + * @function + * @param {Node} value - The unsigned integer to be unpacked + * @returns {Node} + */ +export const unpackUnorm2x16 = /*@__PURE__*/ nodeProxyIntent( UnpackFloatNode, 'unorm' ).setParameterLength( 1 ); + +/** + * Unpacks a 32-bit unsigned integer into two 16-bit values, interpreted as 16-bit floating-point numbers. Returns a vec2 with both values. + * + * @tsl + * @function + * @param {Node} value - The unsigned integer to be unpacked + * @returns {Node} + */ +export const unpackHalf2x16 = /*@__PURE__*/ nodeProxyIntent( UnpackFloatNode, 'float16' ).setParameterLength( 1 ); diff --git a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js index 73667df5577f98..6e1d58f512b571 100644 --- a/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js +++ b/src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js @@ -22,7 +22,13 @@ const glslMethods = { bitcast_uint_float: 'uintBitsToFloat', bitcast_float_uint: 'floatBitsToUint', bitcast_uint_int: 'tsl_bitcast_uint_to_int', - bitcast_int_uint: 'tsl_bitcast_int_to_uint' + bitcast_int_uint: 'tsl_bitcast_int_to_uint', + floatpack_snorm_2x16: 'packSnorm2x16', + floatpack_unorm_2x16: 'packUnorm2x16', + floatpack_float16_2x16: 'packHalf2x16', + floatunpack_snorm_2x16: 'unpackSnorm2x16', + floatunpack_unorm_2x16: 'unpackUnorm2x16', + floatunpack_float16_2x16: 'unpackHalf2x16' }; const precisionLib = { @@ -184,6 +190,30 @@ class GLSLNodeBuilder extends NodeBuilder { } + /** + * Returns the float packing method name for a given numeric encoding. + * + * @param {string} encoding - The numeric encoding that describes how the float values are mapped to the integer range. + * @returns {string} The resolved GLSL float packing method name. + */ + getFloatPackingMethod( encoding ) { + + return this.getMethod( `floatpack_${ encoding }_2x16` ); + + } + + /** + * Returns the float unpacking method name for a given numeric encoding. + * + * @param {string} encoding - The numeric encoding that describes how the integer values are mapped to the float range. + * @returns {string} The resolved GLSL float unpacking method name. + */ + getFloatUnpackingMethod( encoding ) { + + return this.getMethod( `floatunpack_${ encoding }_2x16` ); + + } + /** * Returns the native snippet for a ternary operation. * diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index 3d8722b4284cb8..0b11a860d7a157 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -128,7 +128,13 @@ const wgslMethods = { equals_bvec3: 'tsl_equals_bvec3', equals_bvec4: 'tsl_equals_bvec4', inversesqrt: 'inverseSqrt', - bitcast: 'bitcast' + bitcast: 'bitcast', + floatpack_snorm_2x16: 'pack2x16snorm', + floatpack_unorm_2x16: 'pack2x16unorm', + floatpack_float16_2x16: 'pack2x16float', + floatunpack_snorm_2x16: 'unpack2x16snorm', + floatunpack_unorm_2x16: 'unpack2x16unorm', + floatunpack_float16_2x16: 'unpack2x16float' }; // @@ -1990,6 +1996,30 @@ ${ flowData.code } } + /** + * Returns the float packing method name for a given numeric encoding. + * + * @param {string} encoding - The numeric encoding that describes how the float values are mapped to the integer range. + * @returns {string} The resolve WGSL float packing method name. + */ + getFloatPackingMethod( encoding ) { + + return this.getMethod( `floatpack_${ encoding }_2x16` ); + + } + + /** + * Returns the float unpacking method name for a given numeric encoding. + * + * @param {string} encoding - The numeric encoding that describes how the integer values are mapped to the float range. + * @returns {string} The resolve WGSL float unpacking method name. + */ + getFloatUnpackingMethod( encoding ) { + + return this.getMethod( `floatunpack_${ encoding }_2x16` ); + + } + /** * Returns the native snippet for a ternary operation. *