Browse Source

FunctionNode: Keywords support (#23766)

* add sampler

* add FunctionNode.keywords

* update WGSL example using keywords

* improve example

* update

* fix WebGPUNodeBuilder.isReference()
sunag 3 years ago
parent
commit
8cafdffeeb

+ 1 - 0
examples/jsm/nodes/ShaderNode.js

@@ -321,6 +321,7 @@ export const uv = ( ...params ) => nodeObject( new UVNode( ...params ) );
 export const attribute = ( ...params ) => nodeObject( new AttributeNode( ...params ) );
 export const attribute = ( ...params ) => nodeObject( new AttributeNode( ...params ) );
 export const buffer = ( ...params ) => nodeObject( new BufferNode( ...params ) );
 export const buffer = ( ...params ) => nodeObject( new BufferNode( ...params ) );
 export const texture = ( ...params ) => nodeObject( new TextureNode( ...params ) );
 export const texture = ( ...params ) => nodeObject( new TextureNode( ...params ) );
+export const sampler = ( texture ) => nodeObject( new ConvertNode( texture.isNode === true ? texture : new TextureNode( texture ), 'sampler' ) );
 
 
 export const cond = ( ...params ) => nodeObject( new CondNode( ...getShaderNodeArray( params ) ) );
 export const cond = ( ...params ) => nodeObject( new CondNode( ...getShaderNodeArray( params ) ) );
 
 

+ 4 - 4
examples/jsm/nodes/accessors/CubeTextureNode.js

@@ -28,13 +28,13 @@ class CubeTextureNode extends TextureNode {
 
 
 		const textureProperty = UniformNode.prototype.generate.call( this, builder, 'cubeTexture' );
 		const textureProperty = UniformNode.prototype.generate.call( this, builder, 'cubeTexture' );
 
 
-		if ( output === 'samplerCube' || output === 'textureCube' ) {
+		if ( output === 'sampler' ) {
 
 
-			return textureProperty;
+			return textureProperty + '_sampler';
 
 
-		} else if ( output === 'sampler' ) {
+		} else if ( builder.isReference( output ) ) {
 
 
-			return textureProperty + '_sampler';
+			return textureProperty;
 
 
 		} else {
 		} else {
 
 

+ 4 - 4
examples/jsm/nodes/accessors/TextureNode.js

@@ -36,13 +36,13 @@ class TextureNode extends UniformNode {
 
 
 		const textureProperty = super.generate( builder, 'texture' );
 		const textureProperty = super.generate( builder, 'texture' );
 
 
-		if ( output === 'sampler2D' || output === 'texture2D' ) {
+		if ( output === 'sampler' ) {
 
 
-			return textureProperty;
+			return textureProperty + '_sampler';
 
 
-		} else if ( output === 'sampler' ) {
+		} else if ( builder.isReference( output ) ) {
 
 
-			return textureProperty + '_sampler';
+			return textureProperty;
 
 
 		} else {
 		} else {
 
 

+ 0 - 28
examples/jsm/nodes/core/CodeNode.js

@@ -8,8 +8,6 @@ class CodeNode extends Node {
 
 
 		this.code = code;
 		this.code = code;
 
 
-		this.useKeywords = false;
-
 		this._includes = [];
 		this._includes = [];
 
 
 	}
 	}
@@ -30,32 +28,6 @@ class CodeNode extends Node {
 
 
 	generate( builder ) {
 	generate( builder ) {
 
 
-		if ( this.useKeywords === true ) {
-
-			const contextKeywords = builder.context.keywords;
-
-			if ( contextKeywords !== undefined ) {
-
-				const nodeData = builder.getDataFromNode( this, builder.shaderStage );
-
-				if ( nodeData.keywords === undefined ) {
-
-					nodeData.keywords = [];
-
-				}
-
-				if ( nodeData.keywords.indexOf( contextKeywords ) === - 1 ) {
-
-					contextKeywords.include( builder, this.code );
-
-					nodeData.keywords.push( contextKeywords );
-
-				}
-
-			}
-
-		}
-
 		const includes = this.getIncludes( builder );
 		const includes = this.getIncludes( builder );
 
 
 		for ( const include of includes ) {
 		for ( const include of includes ) {

+ 20 - 2
examples/jsm/nodes/core/FunctionNode.js

@@ -7,7 +7,7 @@ class FunctionNode extends CodeNode {
 
 
 		super( code );
 		super( code );
 
 
-		this.useKeywords = true;
+		this.keywords = {};
 
 
 	}
 	}
 
 
@@ -68,7 +68,25 @@ class FunctionNode extends CodeNode {
 
 
 		const propertyName = builder.getPropertyName( nodeCode );
 		const propertyName = builder.getPropertyName( nodeCode );
 
 
-		nodeCode.code = this.getNodeFunction( builder ).getCode( propertyName );
+		let code = this.getNodeFunction( builder ).getCode( propertyName );
+
+		const keywords = this.keywords;
+		const keywordsProperties = Object.keys( keywords );
+
+		if ( keywordsProperties.length > 0 ) {
+
+			for ( const property of keywordsProperties ) {
+
+				const propertyRegExp = new RegExp( `\\b${property}\\b`, 'g' );
+				const nodeProperty = keywords[ property ].build( builder, 'property' );
+
+				code = code.replace( propertyRegExp, nodeProperty );
+
+			}
+
+		}
+
+		nodeCode.code = code;
 
 
 		if ( output === 'property' ) {
 		if ( output === 'property' ) {
 
 

+ 7 - 1
examples/jsm/nodes/core/NodeBuilder.js

@@ -255,6 +255,12 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
+	isReference( type ) {
+
+		return type === 'void' || type === 'property' || type === 'sampler';
+
+	}
+
 	isShaderStage( shaderStage ) {
 	isShaderStage( shaderStage ) {
 
 
 		return this.shaderStage === shaderStage;
 		return this.shaderStage === shaderStage;
@@ -645,7 +651,7 @@ class NodeBuilder {
 		fromType = this.getVectorType( fromType );
 		fromType = this.getVectorType( fromType );
 		toType = this.getVectorType( toType );
 		toType = this.getVectorType( toType );
 
 
-		if ( fromType === toType || toType === 'void' || toType === null ) {
+		if ( fromType === toType || toType === null || this.isReference( toType ) ) {
 
 
 			return snippet;
 			return snippet;
 
 

+ 8 - 1
examples/jsm/nodes/parsers/WGSLNodeFunction.js

@@ -33,10 +33,17 @@ const parse = ( source ) => {
 
 
 		while ( i < propsMatches.length ) {
 		while ( i < propsMatches.length ) {
 
 
+			// default
+
 			const name = propsMatches[ i ++ ][ 0 ];
 			const name = propsMatches[ i ++ ][ 0 ];
 			const type = propsMatches[ i ++ ][ 0 ];
 			const type = propsMatches[ i ++ ][ 0 ];
 
 
-			propsMatches[ i ++ ][ 0 ]; // precision
+			// precision
+
+			if ( i < propsMatches.length && /^[fui]\d{2}$/.test( propsMatches[ i ][ 0 ] ) === true )
+				i ++;
+
+			// add input
 
 
 			inputs.push( new NodeFunctionInput( type, name ) );
 			inputs.push( new NodeFunctionInput( type, name ) );
 
 

+ 12 - 3
examples/jsm/nodes/utils/ConvertNode.js

@@ -20,11 +20,20 @@ class ConvertNode extends Node {
 	generate( builder ) {
 	generate( builder ) {
 
 
 		const convertTo = this.convertTo;
 		const convertTo = this.convertTo;
+		const node = this.node;
 
 
-		const convertToSnippet = builder.getType( convertTo );
-		const nodeSnippet = this.node.build( builder, convertTo );
+		if ( builder.isReference( convertTo ) === false ) {
 
 
-		return `${ builder.getVectorType( convertToSnippet ) }( ${ nodeSnippet } )`;
+			const convertToSnippet = builder.getType( convertTo );
+			const nodeSnippet = node.build( builder, convertTo );
+
+			return `${ builder.getVectorType( convertToSnippet ) }( ${ nodeSnippet } )`;
+
+		} else {
+
+			return node.build( builder, convertTo );
+
+		}
 
 
 	}
 	}
 
 

+ 6 - 0
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -549,6 +549,12 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 	}
 	}
 
 
+	isReference( type ) {
+
+		return super.isReference( type ) || type === 'texture_2d' || type === 'texture_cube';
+
+	}
+
 	getAttributes( shaderStage ) {
 	getAttributes( shaderStage ) {
 
 
 		let snippet = '';
 		let snippet = '';

+ 18 - 1
examples/webgpu_materials.html

@@ -34,7 +34,7 @@
 
 
 			import { TeapotGeometry } from './jsm/geometries/TeapotGeometry.js';
 			import { TeapotGeometry } from './jsm/geometries/TeapotGeometry.js';
 
 
-			import { ShaderNode, vec3, dot } from 'three-nodes/ShaderNode.js';
+			import { ShaderNode, vec3, dot, sampler } from 'three-nodes/ShaderNode.js';
 
 
 			import Stats from './jsm/libs/stats.module.js';
 			import Stats from './jsm/libs/stats.module.js';
 
 
@@ -160,6 +160,23 @@
 				material.colorNode = desaturateWGSLNode.call( { color: new Nodes.TextureNode( texture ) } );
 				material.colorNode = desaturateWGSLNode.call( { color: new Nodes.TextureNode( texture ) } );
 				materials.push( material );
 				materials.push( material );
 
 
+				// Custom WGSL ( get texture from keywords )
+
+				const getWGSLTextureSample = new Nodes.FunctionNode( `
+					fn getWGSLTextureSample( tex: texture_2d<f32>, tex_sampler: sampler, uv:vec2<f32> ) -> vec4<f32> {
+
+						return textureSample( tex, tex_sampler, uv ) * vec4<f32>( 0.0, 1.0, 0.0, 1.0 );
+
+					}
+				` );
+
+				const textureNode = new Nodes.TextureNode( texture );
+				//getWGSLTextureSample.keywords = { tex: textureNode, tex_sampler: sampler( textureNode ) };
+
+				material = new Nodes.MeshBasicNodeMaterial();
+				material.colorNode = getWGSLTextureSample.call( { tex: textureNode, tex_sampler: textureNode, uv: new Nodes.UVNode() } );
+				materials.push( material );
+
 				//
 				//
 				// Geometry
 				// Geometry
 				//
 				//