Просмотр исходного кода

TSL: Introduce `namespace` (#31168)

* Nodes: use `.global`

* introduce `namespace`

* update description

* use namespace to `material.positionNode`

* add `docs`
sunag 7 месяцев назад
Родитель
Сommit
ba6091f4a3

+ 1 - 0
src/Three.TSL.js

@@ -327,6 +327,7 @@ 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;

+ 2 - 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 { float, vec3, vec4, bool } from '../../nodes/tsl/TSLBase.js';
+import { namespace, 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';
@@ -767,7 +767,7 @@ class NodeMaterial extends Material {
 
 		if ( this.positionNode !== null ) {
 
-			positionLocal.assign( this.positionNode.context( { isPositionNodeInput: true } ) );
+			positionLocal.assign( namespace( this.positionNode, 'POSITION' ) );
 
 		}
 

+ 14 - 4
src/nodes/accessors/Position.js

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

+ 8 - 11
src/nodes/code/CodeNode.js

@@ -36,6 +36,14 @@ class CodeNode extends Node {
 		 */
 		this.isCodeNode = true;
 
+		/**
+		 * This flag is used for global cache.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
+		this.global = true;
+
 		/**
 		 * The native code.
 		 *
@@ -62,17 +70,6 @@ class CodeNode extends Node {
 
 	}
 
-	/**
-	 * The method is overwritten so it always returns `true`.
-	 *
-	 * @return {boolean} Whether this node is global or not.
-	 */
-	isGlobal() {
-
-		return true;
-
-	}
-
 	/**
 	 * Sets the includes of this code node.
 	 *

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

@@ -97,4 +97,18 @@ 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

@@ -261,7 +261,7 @@ class Node extends EventDispatcher {
 	/**
 	 * By default this method returns the value of the {@link Node#global} flag. This method
 	 * can be overwritten in derived classes if an analytical way is required to determine the
-	 * global status.
+	 * global cache referring to the current shader-stage.
 	 *
 	 * @param {NodeBuilder} builder - The current node builder.
 	 * @return {boolean} Whether this node is global or not.

+ 52 - 2
src/nodes/core/NodeBuilder.js

@@ -1862,6 +1862,58 @@ class NodeBuilder {
 
 	}
 
+	/**
+	 * 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' );
+
+	}
+
+	/**
+	 * 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 ) {
+
+			nsName = property ? ( ns + '_' + property ) : ns;
+
+		} else {
+
+			nsName = property;
+
+		}
+
+		return nsName;
+
+	}
+
 	/**
 	 * Registers a node declaration in the current shader stage.
 	 *
@@ -1885,7 +1937,6 @@ class NodeBuilder {
 
 		}
 
-
 		if ( index > 1 ) {
 
 			node.name = name;
@@ -1894,7 +1945,6 @@ class NodeBuilder {
 
 		}
 
-
 		declarations[ name ] = node;
 
 	}

+ 8 - 12
src/nodes/core/PropertyNode.js

@@ -58,6 +58,14 @@ class PropertyNode extends Node {
 		 */
 		this.isPropertyNode = true;
 
+		/**
+		 * This flag is used for global cache.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
+		this.global = true;
+
 	}
 
 	getHash( builder ) {
@@ -66,18 +74,6 @@ class PropertyNode extends Node {
 
 	}
 
-	/**
-	 * The method is overwritten so it always returns `true`.
-	 *
-	 * @param {NodeBuilder} builder - The current node builder.
-	 * @return {boolean} Whether this node is global or not.
-	 */
-	isGlobal( /*builder*/ ) {
-
-		return true;
-
-	}
-
 	generate( builder ) {
 
 		let nodeVar;

+ 8 - 26
src/nodes/core/VaryingNode.js

@@ -71,21 +71,16 @@ class VaryingNode extends Node {
 		 */
 		this.interpolationSampling = null;
 
-	}
-
-	/**
-	 * The method is overwritten so it always returns `true`.
-	 *
-	 * @param {NodeBuilder} builder - The current node builder.
-	 * @return {boolean} Whether this node is global or not.
-	 */
-	isGlobal( /*builder*/ ) {
-
-		return true;
+		/**
+		 * This flag is used for global cache.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
+		this.global = true;
 
 	}
 
-
 	/**
 	 * Defines the interpolation type of the varying.
 	 *
@@ -168,9 +163,7 @@ class VaryingNode extends Node {
 		const properties = builder.getNodeProperties( this );
 		const varying = this.setupVarying( builder );
 
-		const needsReassign = builder.shaderStage === 'fragment' && properties.reassignPosition === true && builder.context.needsPositionReassign;
-
-		if ( properties.propertyName === undefined || needsReassign ) {
+		if ( properties.propertyName === undefined ) {
 
 			const type = this.getNodeType( builder );
 			const propertyName = builder.getPropertyName( varying, NodeShaderStage.VERTEX );
@@ -180,17 +173,6 @@ class VaryingNode extends Node {
 
 			properties.propertyName = propertyName;
 
-			if ( needsReassign ) {
-
-				// once reassign varying in fragment stage
-				properties.reassignPosition = false;
-
-			} else if ( properties.reassignPosition === undefined && builder.context.isPositionNodeInput ) {
-
-				properties.reassignPosition = true;
-
-			}
-
 		}
 
 		return builder.getPropertyName( varying );

+ 8 - 11
src/nodes/display/PassNode.js

@@ -336,6 +336,14 @@ class PassNode extends TempNode {
 		 */
 		this.updateBeforeType = NodeUpdateType.FRAME;
 
+		/**
+		 * This flag is used for global cache.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
+		this.global = true;
+
 	}
 
 	/**
@@ -404,17 +412,6 @@ class PassNode extends TempNode {
 
 	}
 
-	/**
-	 * The method is overwritten so it always returns `true`.
-	 *
-	 * @return {boolean} Whether this node is global or not.
-	 */
-	isGlobal() {
-
-		return true;
-
-	}
-
 	/**
 	 * Returns the texture for the given output name.
 	 *

+ 49 - 29
src/nodes/tsl/TSLCore.js

@@ -325,7 +325,13 @@ class ShaderCallNodeInternal extends Node {
 		const { shaderNode, inputNodes } = this;
 
 		const properties = builder.getNodeProperties( shaderNode );
-		if ( properties.onceOutput ) return properties.onceOutput;
+		const onceNS = shaderNode.namespace && shaderNode.namespace === builder.namespace ? builder.getNamespace( 'once' ) : 'once';
+
+		if ( properties[ onceNS ] ) {
+
+			return properties[ onceNS ];
+
+		}
 
 		//
 
@@ -368,7 +374,7 @@ class ShaderCallNodeInternal extends Node {
 
 		if ( shaderNode.once ) {
 
-			properties.onceOutput = result;
+			properties[ onceNS ] = result;
 
 		}
 
@@ -376,41 +382,63 @@ class ShaderCallNodeInternal extends Node {
 
 	}
 
+	setupOutput( builder ) {
+
+		builder.addStack();
+
+		builder.stack.outputNode = this.call( builder );
+
+		return builder.removeStack();
+
+	}
+
 	getOutputNode( builder ) {
 
 		const properties = builder.getNodeProperties( this );
+		const outputNamespace = builder.getOutputNamespace();
 
-		if ( properties.outputNode === null ) {
+		properties[ outputNamespace ] = properties[ outputNamespace ] || this.setupOutput( builder );
 
-			properties.outputNode = this.setupOutput( builder );
+		return properties[ outputNamespace ];
 
-		}
+	}
 
-		return properties.outputNode;
+	build( builder, output = null ) {
 
-	}
+		let result = null;
 
-	setup( builder ) {
+		const buildStage = builder.getBuildStage();
+		const properties = builder.getNodeProperties( this );
 
-		return this.getOutputNode( builder );
+		const outputNamespace = builder.getOutputNamespace();
+		const outputNode = this.getOutputNode( builder );
 
-	}
+		if ( buildStage === 'setup' ) {
 
-	setupOutput( builder ) {
+			const initializedNamespace = builder.getNamespace( 'initialized' );
 
-		builder.addStack();
+			if ( properties[ initializedNamespace ] !== true ) {
 
-		builder.stack.outputNode = this.call( builder );
+				properties[ initializedNamespace ] = true;
 
-		return builder.removeStack();
+				properties[ outputNamespace ] = this.getOutputNode( builder );
+				properties[ outputNamespace ].build( builder );
 
-	}
+			}
 
-	generate( builder, output ) {
+			result = properties[ outputNamespace ];
 
-		const outputNode = this.getOutputNode( builder );
+		} else if ( buildStage === 'analyze' ) {
+
+			outputNode.build( builder, output );
+
+		} else if ( buildStage === 'generate' ) {
 
-		return outputNode.build( builder, output );
+			result = outputNode.build( builder, output ) || '';
+
+		}
+
+		return result;
 
 	}
 
@@ -428,6 +456,7 @@ class ShaderNodeInternal extends Node {
 		this.global = true;
 
 		this.once = false;
+		this.namespace = null;
 
 	}
 
@@ -632,9 +661,10 @@ export const Fn = ( jsFunc, layout = null ) => {
 
 	};
 
-	fn.once = () => {
+	fn.once = ( namespace = null ) => {
 
 		shaderNode.once = true;
+		shaderNode.namespace = namespace;
 
 		return fn;
 
@@ -675,16 +705,6 @@ export const Fn = ( jsFunc, layout = null ) => {
 
 //
 
-addMethodChaining( 'toGlobal', ( node ) => {
-
-	node.global = true;
-
-	return node;
-
-} );
-
-//
-
 export const setCurrentStack = ( stack ) => {
 
 	if ( currentStack === stack ) {

粤ICP备19079148号