Kaynağa Gözat

TSL: Introduce `array()` (#30386)

* introduce array

* updates

* added `.toArray()`
sunag 1 yıl önce
ebeveyn
işleme
7c01f6e41b

+ 1 - 0
src/Three.TSL.js

@@ -43,6 +43,7 @@ export const anisotropyB = TSL.anisotropyB;
 export const anisotropyT = TSL.anisotropyT;
 export const any = TSL.any;
 export const append = TSL.append;
+export const array = TSL.array;
 export const arrayBuffer = TSL.arrayBuffer;
 export const asin = TSL.asin;
 export const assign = TSL.assign;

+ 1 - 0
src/nodes/Nodes.js

@@ -2,6 +2,7 @@
 export * from './core/constants.js';
 
 // core
+export { default as ArrayNode } from './core/ArrayNode.js';
 export { default as AssignNode } from './core/AssignNode.js';
 export { default as AttributeNode } from './core/AttributeNode.js';
 export { default as BypassNode } from './core/BypassNode.js';

+ 125 - 0
src/nodes/core/ArrayNode.js

@@ -0,0 +1,125 @@
+import TempNode from './TempNode.js';
+import { addMethodChaining, nodeObject } from '../tsl/TSLCore.js';
+
+/** @module ArrayNode **/
+
+/**
+ * ArrayNode represents a collection of nodes, typically created using the {@link module:TSL~array} function.
+ * ```js
+ * const colors = array( [
+ * 	vec3( 1, 0, 0 ),
+ * 	vec3( 0, 1, 0 ),
+ * 	vec3( 0, 0, 1 )
+ * ] );
+ *
+ * const redColor = tintColors.element( 0 );
+ *
+ * @augments Node
+ */
+class ArrayNode extends TempNode {
+
+	static get type() {
+
+		return 'ArrayNode';
+
+	}
+
+	/**
+	 * Constructs a new array node.
+	 *
+	 * @param {String} [nodeType] - The data type of the elements.
+	 * @param {Number} [count] - Size of the array.
+	 * @param {Array<Node>?} [values=null] - Array default values.
+	 */
+	constructor( nodeType, count, values = null ) {
+
+		super( nodeType );
+
+		/**
+		 * Array size.
+		 *
+		 * @type {Array<Node>}
+		 */
+		this.count = count;
+
+		/**
+		 * Array default values.
+		 *
+		 * @type {Array<Node>}
+		 */
+		this.values = values;
+
+		/**
+		 * This flag can be used for type testing.
+		 *
+		 * @type {Boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isArrayNode = true;
+
+	}
+
+	getNodeType( builder ) {
+
+		if ( this.nodeType === null ) {
+
+			this.nodeType = this.values[ 0 ].getNodeType( builder );
+
+		}
+
+		return this.nodeType;
+
+	}
+
+	getElementType( builder ) {
+
+		return this.getNodeType( builder );
+
+	}
+
+	generate( builder ) {
+
+		const type = this.getNodeType( builder );
+
+		return builder.generateArray( type, this.count, this.values );
+
+	}
+
+}
+
+export default ArrayNode;
+
+/**
+ * TSL function for creating an array node.
+ *
+ * @function
+ * @param {String|Array<Node>} nodeTypeOrValues - A string representing the element type (e.g., 'vec3')
+ * or an array containing the default values (e.g., [ vec3() ]).
+ * @param {Number?} [count] - Size of the array.
+ * @returns {ArrayNode}
+ */
+export const array = ( ...params ) => {
+
+	let node;
+
+	if ( params.length === 1 ) {
+
+		const values = params[ 0 ];
+
+		node = new ArrayNode( null, values.length, values );
+
+	} else {
+
+		const nodeType = params[ 0 ];
+		const count = params[ 1 ];
+
+		node = new ArrayNode( nodeType, count );
+
+	}
+
+	return nodeObject( node );
+
+};
+
+addMethodChaining( 'toArray', ( node, count ) => array( Array( count ).fill( node ) ) );

+ 92 - 3
src/nodes/core/NodeBuilder.js

@@ -1053,6 +1053,55 @@ class NodeBuilder {
 
 	}
 
+	/**
+	 * Generates the array declaration string.
+	 *
+	 * @param {String} type - The type.
+	 * @param {Number?} [count] - The count.
+	 * @return {String} The generated value as a shader string.
+	 */
+	generateArrayDeclaration( type, count ) {
+
+		return this.getType( type ) + '[ ' + count + ' ]';
+
+	}
+
+	/**
+	 * Generates the array shader string for the given type and value.
+	 *
+	 * @param {String} type - The type.
+	 * @param {Number?} [count] - The count.
+	 * @param {Array<Node>?} [values=null] - The default values.
+	 * @return {String} The generated value as a shader string.
+	 */
+	generateArray( type, count, values = null ) {
+
+		let snippet = this.generateArrayDeclaration( type, count ) + '( ';
+
+		for ( let i = 0; i < count; i ++ ) {
+
+			const value = values ? values[ i ] : null;
+
+			if ( value !== null ) {
+
+				snippet += value.build( this, type );
+
+			} else {
+
+				snippet += this.generateConst( type );
+
+			}
+
+			if ( i < count - 1 ) snippet += ', ';
+
+		}
+
+		snippet += ' )';
+
+		return snippet;
+
+	}
+
 	/**
 	 * Generates the shader string for the given type and value.
 	 *
@@ -1604,6 +1653,23 @@ class NodeBuilder {
 
 	}
 
+	/**
+	 * Returns the array length.
+	 *
+	 * @param {Node} node - The node.
+	 * @return {Number?} The array length.
+	 */
+	getArrayCount( node ) {
+
+		let count = null;
+
+		if ( node.isArrayNode ) count = node.count;
+		else if ( node.isVarNode && node.node.isArrayNode ) count = node.node.count;
+
+		return count;
+
+	}
+
 	/**
 	 * Returns an instance of {@link NodeVar} for the given variable node.
 	 *
@@ -1636,7 +1702,11 @@ class NodeBuilder {
 
 			}
 
-			nodeVar = new NodeVar( name, type, readOnly );
+			//
+
+			const count = this.getArrayCount( node );
+
+			nodeVar = new NodeVar( name, type, readOnly, count );
 
 			if ( ! readOnly ) {
 
@@ -1671,6 +1741,24 @@ class NodeBuilder {
 			return this.isDeterministic( node.aNode ) &&
 				( node.bNode ? this.isDeterministic( node.bNode ) : true );
 
+		} else if ( node.isArrayNode ) {
+
+			if ( node.values !== null ) {
+
+				for ( const n of node.values ) {
+
+					if ( ! this.isDeterministic( n ) ) {
+
+						return false;
+
+					}
+
+				}
+
+			}
+
+			return true;
+
 		} else if ( node.isConstNode ) {
 
 			return true;
@@ -2134,11 +2222,12 @@ class NodeBuilder {
 	 *
 	 * @param {String} type - The variable's type.
 	 * @param {String} name - The variable's name.
+	 * @param {Number?} [count=null] - The array length.
 	 * @return {String} The shader string.
 	 */
-	getVar( type, name ) {
+	getVar( type, name, count = null ) {
 
-		return `${ this.getType( type ) } ${ name }`;
+		return `${ count !== null ? this.generateArrayDeclaration( type, count ) : this.getType( type ) } ${ name }`;
 
 	}
 

+ 10 - 2
src/nodes/core/NodeVar.js

@@ -12,8 +12,9 @@ class NodeVar {
 	 * @param {String} name - The name of the variable.
 	 * @param {String} type - The type of the variable.
 	 * @param {Boolean} [readOnly=false] - The read-only flag.
+	 * @param {Number?} [count=null] - The size.
 	 */
-	constructor( name, type, readOnly = false ) {
+	constructor( name, type, readOnly = false, count = null ) {
 
 		/**
 		 * This flag can be used for type testing.
@@ -41,10 +42,17 @@ class NodeVar {
 		/**
 		 *  The read-only flag.
 		 *
-		 * @type {boolean}
+		 * @type {Boolean}
 		 */
 		this.readOnly = readOnly;
 
+		/**
+		 * The size.
+		 *
+		 * @type {Number?}
+		 */
+		this.count = count;
+
 	}
 
 }

+ 1 - 1
src/nodes/core/TempNode.js

@@ -68,7 +68,7 @@ class TempNode extends Node {
 				const nodeVar = builder.getVarFromNode( this, null, type );
 				const propertyName = builder.getPropertyName( nodeVar );
 
-				builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
+				builder.addLineFlowCode( `${ propertyName } = ${ snippet }`, this );
 
 				nodeData.snippet = snippet;
 				nodeData.propertyName = propertyName;

+ 9 - 3
src/nodes/core/VarNode.js

@@ -82,6 +82,12 @@ class VarNode extends Node {
 
 	}
 
+	getElementType( builder ) {
+
+		return this.node.getElementType( builder );
+
+	}
+
 	getNodeType( builder ) {
 
 		return this.node.getNodeType( builder );
@@ -117,8 +123,6 @@ class VarNode extends Node {
 
 		if ( shouldTreatAsReadOnly ) {
 
-			const type = builder.getType( nodeVar.type );
-
 			if ( isWebGPUBackend ) {
 
 				declarationPrefix = isDeterministic
@@ -127,7 +131,9 @@ class VarNode extends Node {
 
 			} else {
 
-				declarationPrefix = `const ${ type } ${ propertyName }`;
+				const count = builder.getArrayCount( node );
+
+				declarationPrefix = `const ${ builder.getVar( nodeVar.type, propertyName, count ) }`;
 
 			}
 

+ 1 - 0
src/nodes/tsl/TSLBase.js

@@ -2,6 +2,7 @@
 // TSL Base Syntax
 
 export * from './TSLCore.js'; // float(), vec2(), vec3(), vec4(), mat3(), mat4(), Fn(), If(), element(), nodeObject(), nodeProxy(), ...
+export * from '../core/ArrayNode.js'; // array(), .toArray()
 export * from '../core/UniformNode.js'; // uniform()
 export * from '../core/PropertyNode.js'; // property()  <-> TODO: Separate Material Properties in other file
 export * from '../core/AssignNode.js'; // .assign()

+ 1 - 1
src/nodes/utils/ArrayElementNode.js

@@ -66,7 +66,7 @@ class ArrayElementNode extends Node { // @TODO: If extending from TempNode it br
 		const nodeSnippet = this.node.build( builder );
 		const indexSnippet = this.indexNode.build( builder, 'uint' );
 
-		return `${nodeSnippet}[ ${indexSnippet} ]`;
+		return `${ nodeSnippet }[ ${ indexSnippet } ]`;
 
 	}
 

+ 1 - 1
src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js

@@ -496,7 +496,7 @@ ${ flowData.code }
 
 			for ( const variable of vars ) {
 
-				snippets.push( `${ this.getVar( variable.type, variable.name ) };` );
+				snippets.push( `${ this.getVar( variable.type, variable.name, variable.count ) };` );
 
 			}
 

+ 29 - 3
src/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -388,6 +388,19 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 	}
 
+	/**
+	 * Generates the array declaration string.
+	 *
+	 * @param {String} type - The type.
+	 * @param {Number?} [count] - The count.
+	 * @return {String} The generated value as a shader string.
+	 */
+	generateArrayDeclaration( type, count ) {
+
+		return `array< ${ this.getType( type ) }, ${ count } >`;
+
+	}
+
 	/**
 	 * Generates a WGSL variable that holds the texture dimension of the given texture.
 	 * It also returns information about the the number of layers (elements) of an arrayed
@@ -1464,11 +1477,24 @@ ${ flowData.code }
 	 *
 	 * @param {String} type - The variable's type.
 	 * @param {String} name - The variable's name.
+	 * @param {Number?} [count=null] - The array length.
 	 * @return {String} The WGSL snippet that defines a variable.
 	 */
-	getVar( type, name ) {
+	getVar( type, name, count = null ) {
+
+		let snippet = `var ${ name } : `;
 
-		return `var ${ name } : ${ this.getType( type ) }`;
+		if ( count !== null ) {
+
+			snippet += this.generateArrayDeclaration( type, count );
+
+		} else {
+
+			snippet += this.getType( type );
+
+		}
+
+		return snippet;
 
 	}
 
@@ -1487,7 +1513,7 @@ ${ flowData.code }
 
 			for ( const variable of vars ) {
 
-				snippets.push( `\t${ this.getVar( variable.type, variable.name ) };` );
+				snippets.push( `\t${ this.getVar( variable.type, variable.name, variable.count ) };` );
 
 			}
 

粤ICP备19079148号