Prechádzať zdrojové kódy

TSL: Introduce `subBuild()` (#31260)

* TSL: Introduce `subBuild()`

* add optional type

* add `subBuildFn`

* add docs

* cleanup

* add type

* add docs

* cleanup
sunag 7 mesiacov pred
rodič
commit
4437d86368

+ 1 - 1
src/Three.TSL.js

@@ -327,7 +327,6 @@ export const mx_transform_uv = TSL.mx_transform_uv;
 export const mx_worley_noise_float = TSL.mx_worley_noise_float;
 export const mx_worley_noise_vec2 = TSL.mx_worley_noise_vec2;
 export const mx_worley_noise_vec3 = TSL.mx_worley_noise_vec3;
-export const namespace = TSL.namespace;
 export const negate = TSL.negate;
 export const neutralToneMapping = TSL.neutralToneMapping;
 export const nodeArray = TSL.nodeArray;
@@ -462,6 +461,7 @@ export const storageTexture = TSL.storageTexture;
 export const string = TSL.string;
 export const struct = TSL.struct;
 export const sub = TSL.sub;
+export const subBuild = TSL.subBuild;
 export const subgroupIndex = TSL.subgroupIndex;
 export const subgroupSize = TSL.subgroupSize;
 export const tan = TSL.tan;

+ 3 - 2
src/materials/nodes/NodeMaterial.js

@@ -13,7 +13,7 @@ import { positionLocal, positionView } from '../../nodes/accessors/Position.js';
 import { skinning } from '../../nodes/accessors/SkinningNode.js';
 import { morphReference } from '../../nodes/accessors/MorphNode.js';
 import { mix } from '../../nodes/math/MathNode.js';
-import { namespace, float, vec3, vec4, bool } from '../../nodes/tsl/TSLBase.js';
+import { float, vec3, vec4, bool } from '../../nodes/tsl/TSLBase.js';
 import AONode from '../../nodes/lighting/AONode.js';
 import { lightingContext } from '../../nodes/lighting/LightingContextNode.js';
 import IrradianceNode from '../../nodes/lighting/IrradianceNode.js';
@@ -25,6 +25,7 @@ import getAlphaHashThreshold from '../../nodes/functions/material/getAlphaHashTh
 import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
 import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
 import { premultiplyAlpha } from '../../nodes/display/BlendModes.js';
+import { subBuild } from '../../nodes/core/SubBuildNode.js';
 
 /**
  * Base class for all node materials.
@@ -768,7 +769,7 @@ class NodeMaterial extends Material {
 
 		if ( this.positionNode !== null ) {
 
-			positionLocal.assign( namespace( this.positionNode, 'POSITION' ) );
+			positionLocal.assign( subBuild( this.positionNode, 'POSITION', 'vec3' ) );
 
 		}
 

+ 1 - 0
src/nodes/Nodes.js

@@ -33,6 +33,7 @@ export { default as StructNode } from './core/StructNode.js';
 export { default as StructTypeNode } from './core/StructTypeNode.js';
 export { default as OutputStructNode } from './core/OutputStructNode.js';
 export { default as MRTNode } from './core/MRTNode.js';
+export { default as SubBuildNode } from './core/SubBuildNode.js';
 
 import * as NodeUtils from './core/NodeUtils.js';
 export { NodeUtils };

+ 7 - 7
src/nodes/accessors/Position.js

@@ -35,9 +35,9 @@ export const positionPrevious = /*@__PURE__*/ positionGeometry.toVarying( 'posit
  */
 export const positionWorld = /*@__PURE__*/ ( Fn( ( builder ) => {
 
-	return modelWorldMatrix.mul( positionLocal ).xyz.toVarying( builder.getNamespace( 'v_positionWorld' ) );
+	return modelWorldMatrix.mul( positionLocal ).xyz.toVarying( builder.getSubBuildProperty( 'v_positionWorld' ) );
 
-}, 'vec3' ).once( 'POSITION' ) )();
+}, 'vec3' ).once( [ 'POSITION' ] ) )();
 
 /**
  * TSL object that represents the position world direction of the current rendered object.
@@ -45,13 +45,13 @@ export const positionWorld = /*@__PURE__*/ ( Fn( ( builder ) => {
  * @tsl
  * @type {Node<vec3>}
  */
-export const positionWorldDirection = /*@__PURE__*/ ( Fn( ( builder ) => {
+export const positionWorldDirection = /*@__PURE__*/ ( Fn( () => {
 
-	const vertexPWD = positionLocal.transformDirection( modelWorldMatrix ).toVarying( builder.getNamespace( 'v_positionWorldDirection' ) );
+	const vertexPWD = positionLocal.transformDirection( modelWorldMatrix ).toVarying( 'v_positionWorldDirection' );
 
 	return vertexPWD.normalize().toVar( 'positionWorldDirection' );
 
-}, 'vec3' ).once( 'POSITION' ) )();
+}, 'vec3' ).once( [ 'POSITION' ] ) )();
 
 /**
  * TSL object that represents the vertex position in view space of the current rendered object.
@@ -61,9 +61,9 @@ export const positionWorldDirection = /*@__PURE__*/ ( Fn( ( builder ) => {
  */
 export const positionView = /*@__PURE__*/ ( Fn( ( builder ) => {
 
-	return builder.context.setupPositionView().toVarying( builder.getNamespace( 'v_positionView' ) );
+	return builder.context.setupPositionView().toVarying( 'v_positionView' );
 
-}, 'vec3' ).once( 'POSITION' ) )();
+}, 'vec3' ).once( [ 'POSITION' ] ) )();
 
 /**
  * TSL object that represents the position view direction of the current rendered object.

+ 0 - 14
src/nodes/core/CacheNode.js

@@ -97,18 +97,4 @@ export default CacheNode;
  */
 export const cache = ( node, parent ) => nodeObject( new CacheNode( nodeObject( node ), parent ) );
 
-/**
- * Assigns a namespace to the given node by updating its context.
- *
- * Important for TSL functions that use `.once( namespace )` to ensure that the namespace will run twice,
- * once when the node is build in the specific namespace and once when the node is built in the others namespace.
- *
- * This is useful for nodes like `positionWorld` that need to be re-updated if used in `material.positionNode` and outside of it in the same material.
- *
- * @param {Object} node - The node to which the namespace will be assigned.
- * @param {string} namespace - The namespace to be assigned to the node.
- * @returns {Object} The updated node with the new namespace in its context.
- */
-export const namespace = ( node, namespace ) => node.context( { namespace } );
-
 addMethodChaining( 'cache', cache );

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

@@ -735,7 +735,7 @@ class Node extends EventDispatcher {
 
 						console.warn( 'THREE.Node: Recursion detected.', this );
 
-						result = '';
+						result = '/* Recursion detected. */';
 
 					}
 

+ 203 - 56
src/nodes/core/NodeBuilder.js

@@ -419,6 +419,30 @@ class NodeBuilder {
 		 */
 		this.buildStage = null;
 
+		/**
+		 * The sub-build layers.
+		 *
+		 * @type {Array<SubBuildNode>}
+		 * @default []
+		 */
+		this.subBuildLayers = [];
+
+		/**
+		 * The current stack of nodes.
+		 *
+		 * @type {?StackNode}
+		 * @default null
+		 */
+		this.currentStack = null;
+
+		/**
+		 * The current sub-build TSL function(Fn).
+		 *
+		 * @type {?string}
+		 * @default null
+		 */
+		this.subBuildFn = null;
+
 	}
 
 	/**
@@ -1577,7 +1601,23 @@ class NodeBuilder {
 
 		if ( nodeData[ shaderStage ] === undefined ) nodeData[ shaderStage ] = {};
 
-		return nodeData[ shaderStage ];
+		//
+
+		let data = nodeData[ shaderStage ];
+
+		const subBuilds = nodeData.any ? nodeData.any.subBuilds : null;
+		const subBuild = this.getClosestSubBuild( subBuilds );
+
+		if ( subBuild ) {
+
+			if ( data.subBuildsCache === undefined ) data.subBuildsCache = {};
+
+			data = data.subBuildsCache[ subBuild ] || ( data.subBuildsCache[ subBuild ] = {} );
+			data.subBuilds = subBuilds;
+
+		}
+
+		return data;
 
 	}
 
@@ -1738,8 +1778,9 @@ class NodeBuilder {
 	getVarFromNode( node, name = null, type = node.getNodeType( this ), shaderStage = this.shaderStage, readOnly = false ) {
 
 		const nodeData = this.getDataFromNode( node, shaderStage );
+		const subBuildVariable = this.getSubBuildProperty( 'variable', nodeData.subBuilds );
 
-		let nodeVar = nodeData.variable;
+		let nodeVar = nodeData[ subBuildVariable ];
 
 		if ( nodeVar === undefined ) {
 
@@ -1758,6 +1799,14 @@ class NodeBuilder {
 
 			//
 
+			if ( subBuildVariable !== 'variable' ) {
+
+				name = this.getSubBuildProperty( name, nodeData.subBuilds );
+
+			}
+
+			//
+
 			const count = this.getArrayCount( node );
 
 			nodeVar = new NodeVar( name, type, readOnly, count );
@@ -1770,7 +1819,7 @@ class NodeBuilder {
 
 			this.registerDeclaration( nodeVar );
 
-			nodeData.variable = nodeVar;
+			nodeData[ subBuildVariable ] = nodeVar;
 
 		}
 
@@ -1838,8 +1887,9 @@ class NodeBuilder {
 	getVaryingFromNode( node, name = null, type = node.getNodeType( this ), interpolationType = null, interpolationSampling = null ) {
 
 		const nodeData = this.getDataFromNode( node, 'any' );
+		const subBuildVarying = this.getSubBuildProperty( 'varying', nodeData.subBuilds );
 
-		let nodeVarying = nodeData.varying;
+		let nodeVarying = nodeData[ subBuildVarying ];
 
 		if ( nodeVarying === undefined ) {
 
@@ -1848,69 +1898,27 @@ class NodeBuilder {
 
 			if ( name === null ) name = 'nodeVarying' + index;
 
-			nodeVarying = new NodeVarying( name, type, interpolationType, interpolationSampling );
-
-			varyings.push( nodeVarying );
-
-			this.registerDeclaration( nodeVarying );
-
-			nodeData.varying = nodeVarying;
-
-		}
-
-		return nodeVarying;
-
-	}
-
-	/**
-	 * Returns the current namespace for the node builder.
-	 *
-	 * @return {string} The current namespace.
-	 */
-	get namespace() {
-
-		return this.context.namespace;
-
-	}
-
-	/**
-	 * Returns the output namespace for the node builder, which is used for the current output node.
-	 *
-	 * @return {string} The output namespace.
-	 */
-	getOutputNamespace() {
+			//
 
-		return this.getNamespace( 'outputNode' );
+			if ( subBuildVarying !== 'varying' ) {
 
-	}
+				name = this.getSubBuildProperty( name, nodeData.subBuilds );
 
-	/**
-	 * Returns the namespace for the given property.
-	 *
-	 * If the property name is not set, it returns the namespace only.
-	 * If the namespace is not set, it returns the property name.
-	 * If the namespace is set, it returns the namespace concatenated with the property name.
-	 *
-	 * @param {string} [property=''] - The property name.
-	 * @return {string} The namespace for the property.
-	 */
-	getNamespace( property = '' ) {
-
-		const ns = this.namespace;
+			}
 
-		let nsName;
+			//
 
-		if ( ns ) {
+			nodeVarying = new NodeVarying( name, type, interpolationType, interpolationSampling );
 
-			nsName = property ? ( ns + '_' + property ) : ns;
+			varyings.push( nodeVarying );
 
-		} else {
+			this.registerDeclaration( nodeVarying );
 
-			nsName = property;
+			nodeData[ subBuildVarying ] = nodeVarying;
 
 		}
 
-		return nsName;
+		return nodeVarying;
 
 	}
 
@@ -2567,6 +2575,145 @@ class NodeBuilder {
 
 	}
 
+	/**
+	 * Returns the current sub-build layer.
+	 *
+	 * @return {SubBuildNode} The current sub-build layers.
+	 */
+	get subBuild() {
+
+		return this.subBuildLayers[ this.subBuildLayers.length - 1 ] || null;
+
+	}
+
+	/**
+	 * Adds a sub-build layer to the node builder.
+	 *
+	 * @param {SubBuildNode} subBuild - The sub-build layer to add.
+	 */
+	addSubBuild( subBuild ) {
+
+		this.subBuildLayers.push( subBuild );
+
+	}
+
+	/**
+	 * Removes the last sub-build layer from the node builder.
+	 *
+	 * @return {SubBuildNode} The removed sub-build layer.
+	 */
+	removeSubBuild() {
+
+		return this.subBuildLayers.pop();
+
+	}
+
+	/**
+	 * Returns the closest sub-build layer for the given data.
+	 *
+	 * @param {Node|Set|Array} data - The data to get the closest sub-build layer from.
+	 * @return {?string} The closest sub-build name or null if none found.
+	 */
+	getClosestSubBuild( data ) {
+
+		let subBuilds;
+
+		if ( data && data.isNode ) {
+
+			if ( data.isShaderCallNodeInternal ) {
+
+				subBuilds = data.shaderNode.subBuilds;
+
+			} else if ( data.isStackNode ) {
+
+				subBuilds = [ data.subBuild ];
+
+			} else {
+
+				subBuilds = this.getDataFromNode( data, 'any' ).subBuilds;
+
+			}
+
+		} else if ( data instanceof Set ) {
+
+			subBuilds = [ ...data ];
+
+		} else {
+
+			subBuilds = data;
+
+		}
+
+		if ( ! subBuilds ) return null;
+
+		const subBuildLayers = this.subBuildLayers;
+
+		for ( let i = subBuilds.length - 1; i >= 0; i -- ) {
+
+			const subBuild = subBuilds[ i ];
+
+			if ( subBuildLayers.includes( subBuild ) ) {
+
+				return subBuild;
+
+			}
+
+		}
+
+		return null;
+
+	}
+
+
+	/**
+	 * Returns the output node of a sub-build layer.
+	 *
+	 * @param {Node} node - The node to get the output from.
+	 * @return {string} The output node name.
+	 */
+	getSubBuildOutput( node ) {
+
+		return this.getSubBuildProperty( 'outputNode', node );
+
+	}
+
+	/**
+	 * Returns the sub-build property name for the given property and node.
+	 *
+	 * @param {string} [property=''] - The property name.
+	 * @param {?Node} [node=null] - The node to get the sub-build from.
+	 * @return {string} The sub-build property name.
+	 */
+	getSubBuildProperty( property = '', node = null ) {
+
+		let subBuild;
+
+		if ( node !== null ) {
+
+			subBuild = this.getClosestSubBuild( node );
+
+		} else {
+
+			subBuild = this.subBuildFn;
+
+		}
+
+		let result;
+
+		if ( subBuild ) {
+
+			result = property ? ( subBuild + '_' + property ) : subBuild;
+
+		} else {
+
+			result = property;
+
+		}
+
+		return result;
+
+	}
+
 	/**
 	 * Central build method which controls the build for the given object.
 	 *

+ 8 - 1
src/nodes/core/StackNode.js

@@ -247,10 +247,13 @@ class StackNode extends Node {
 
 	build( builder, ...params ) {
 
+		const previousBuildStack = builder.currentStack;
 		const previousStack = getCurrentStack();
 
 		setCurrentStack( this );
 
+		builder.currentStack = this;
+
 		const buildStage = builder.buildStage;
 
 		for ( const node of this.nodes ) {
@@ -280,9 +283,13 @@ class StackNode extends Node {
 
 		}
 
+		const result = this.outputNode ? this.outputNode.build( builder, ...params ) : super.build( builder, ...params );
+
 		setCurrentStack( previousStack );
 
-		return this.outputNode ? this.outputNode.build( builder, ...params ) : super.build( builder, ...params );
+		builder.currentStack = previousBuildStack;
+
+		return result;
 
 	}
 

+ 89 - 0
src/nodes/core/SubBuildNode.js

@@ -0,0 +1,89 @@
+import Node from './Node.js';
+import { nodeObject } from '../tsl/TSLCore.js';
+
+/**
+ * This node is used to build a sub-build in the node system.
+ *
+ * @augments Node
+ * @param {Node} node - The node to be built in the sub-build.
+ * @param {string} name - The name of the sub-build.
+ * @param {string|null} [nodeType=null] - The type of the node, if known.
+ */
+class SubBuildNode extends Node {
+
+	static get type() {
+
+		return 'SubBuild';
+
+	}
+
+	constructor( node, name, nodeType = null ) {
+
+		super( nodeType );
+
+		/**
+		 * The node to be built in the sub-build.
+		 *
+		 * @type {Node}
+		 */
+		this.node = node;
+
+		/**
+		 * The name of the sub-build.
+		 *
+		 * @type {string}
+		 */
+		this.name = name;
+
+		/**
+		 * This flag can be used for type testing.
+		 *
+		 * @type {boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isSubBuildNode = true;
+
+	}
+
+	getNodeType( builder ) {
+
+		if ( this.nodeType !== null ) return this.nodeType;
+
+		builder.addSubBuild( this.name );
+
+		const nodeType = this.node.getNodeType( builder );
+
+		builder.removeSubBuild();
+
+		return nodeType;
+
+	}
+
+	build( builder, ...params ) {
+
+		builder.addSubBuild( this.name );
+
+		const data = this.node.build( builder, ...params );
+
+		builder.removeSubBuild();
+
+		return data;
+
+	}
+
+}
+
+export default SubBuildNode;
+
+/**
+ * Creates a new sub-build node.
+ *
+ * @tsl
+ * @function
+ * @param {Node} node - The node to be built in the sub-build.
+ * @param {string} name - The name of the sub-build.
+ * @param {string|null} [type=null] - The type of the node, if known.
+ * @returns {Node} A node object wrapping the SubBuildNode instance.
+ */
+export const subBuild = ( node, name, type = null ) => nodeObject( new SubBuildNode( nodeObject( node ), name, type ) );

+ 3 - 2
src/nodes/core/VaryingNode.js

@@ -160,10 +160,11 @@ class VaryingNode extends Node {
 
 	generate( builder ) {
 
+		const propertyKey = builder.getSubBuildProperty( 'property', builder.currentStack );
 		const properties = builder.getNodeProperties( this );
 		const varying = this.setupVarying( builder );
 
-		if ( properties.propertyName === undefined ) {
+		if ( properties[ propertyKey ] === undefined ) {
 
 			const type = this.getNodeType( builder );
 			const propertyName = builder.getPropertyName( varying, NodeShaderStage.VERTEX );
@@ -171,7 +172,7 @@ class VaryingNode extends Node {
 			// force node run in vertex stage
 			builder.flowNodeFromShaderStage( NodeShaderStage.VERTEX, this.node, type, propertyName );
 
-			properties.propertyName = propertyName;
+			properties[ propertyKey ] = propertyName;
 
 		}
 

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

@@ -24,6 +24,7 @@ export * from '../code/ExpressionNode.js'; // expression()
 export * from '../utils/Discard.js'; // Discard(), Return()
 export * from '../display/RenderOutputNode.js'; // .renderOutput()
 export * from '../utils/DebugNode.js'; // debug()
+export * from '../core/SubBuildNode.js'; // subBuild()
 
 export function addNodeElement( name/*, nodeElement*/ ) {
 

+ 47 - 17
src/nodes/tsl/TSLCore.js

@@ -325,16 +325,22 @@ class ShaderCallNodeInternal extends Node {
 		const { shaderNode, inputNodes } = this;
 
 		const properties = builder.getNodeProperties( shaderNode );
-		const onceNS = shaderNode.namespace && shaderNode.namespace === builder.namespace ? builder.getNamespace( 'once' ) : 'once';
 
-		if ( properties[ onceNS ] ) {
+		const subBuild = builder.getClosestSubBuild( shaderNode.subBuilds ) || '';
+		const subBuildProperty = subBuild || 'default';
 
-			return properties[ onceNS ];
+		if ( properties[ subBuildProperty ] ) {
+
+			return properties[ subBuildProperty ];
 
 		}
 
 		//
 
+		const previousSubBuildFn = builder.subBuildFn;
+
+		builder.subBuildFn = subBuild;
+
 		let result = null;
 
 		if ( shaderNode.layout ) {
@@ -372,9 +378,11 @@ class ShaderCallNodeInternal extends Node {
 
 		}
 
+		builder.subBuildFn = previousSubBuildFn;
+
 		if ( shaderNode.once ) {
 
-			properties[ onceNS ] = result;
+			properties[ subBuildProperty ] = result;
 
 		}
 
@@ -395,11 +403,12 @@ class ShaderCallNodeInternal extends Node {
 	getOutputNode( builder ) {
 
 		const properties = builder.getNodeProperties( this );
-		const outputNamespace = builder.getOutputNamespace();
+		const subBuildOutput = builder.getSubBuildOutput( this );
 
-		properties[ outputNamespace ] = properties[ outputNamespace ] || this.setupOutput( builder );
+		properties[ subBuildOutput ] = properties[ subBuildOutput ] || this.setupOutput( builder );
+		properties[ subBuildOutput ].subBuild = builder.getClosestSubBuild( this );
 
-		return properties[ outputNamespace ];
+		return properties[ subBuildOutput ];
 
 	}
 
@@ -410,23 +419,45 @@ class ShaderCallNodeInternal extends Node {
 		const buildStage = builder.getBuildStage();
 		const properties = builder.getNodeProperties( this );
 
-		const outputNamespace = builder.getOutputNamespace();
+		const subBuildOutput = builder.getSubBuildOutput( this );
 		const outputNode = this.getOutputNode( builder );
 
 		if ( buildStage === 'setup' ) {
 
-			const initializedNamespace = builder.getNamespace( 'initialized' );
+			const subBuildInitialized = builder.getSubBuildProperty( 'initialized', this );
+
+			if ( properties[ subBuildInitialized ] !== true ) {
+
+				properties[ subBuildInitialized ] = true;
+
+				properties[ subBuildOutput ] = this.getOutputNode( builder );
+				properties[ subBuildOutput ].build( builder );
+
+				// If the shaderNode has subBuilds, add them to the chaining nodes
+				// so they can be built later in the build process.
+
+				if ( this.shaderNode.subBuilds ) {
+
+					for ( const node of builder.chaining ) {
+
+						const nodeData = builder.getDataFromNode( node, 'any' );
+						nodeData.subBuilds = nodeData.subBuilds || new Set();
+
+						for ( const subBuild of this.shaderNode.subBuilds ) {
+
+							nodeData.subBuilds.add( subBuild );
+
+						}
 
-			if ( properties[ initializedNamespace ] !== true ) {
+						//builder.getDataFromNode( node ).subBuilds = nodeData.subBuilds;
 
-				properties[ initializedNamespace ] = true;
+					}
 
-				properties[ outputNamespace ] = this.getOutputNode( builder );
-				properties[ outputNamespace ].build( builder );
+				}
 
 			}
 
-			result = properties[ outputNamespace ];
+			result = properties[ subBuildOutput ];
 
 		} else if ( buildStage === 'analyze' ) {
 
@@ -456,7 +487,6 @@ class ShaderNodeInternal extends Node {
 		this.global = true;
 
 		this.once = false;
-		this.namespace = null;
 
 	}
 
@@ -665,10 +695,10 @@ export const Fn = ( jsFunc, layout = null ) => {
 
 	};
 
-	fn.once = ( namespace = null ) => {
+	fn.once = ( subBuilds = null ) => {
 
 		shaderNode.once = true;
-		shaderNode.namespace = namespace;
+		shaderNode.subBuilds = subBuilds;
 
 		return fn;
 

粤ICP备19079148号