Explorar o código

NodeMaterial: NodeParser and GLSLNodeParser (#22641)

* GLSLNodeParser

* NodeFunctionInput: add .isConst property and remove .isCount

* NodeFunctionInput: change signature
sunag %!s(int64=3) %!d(string=hai) anos
pai
achega
ac3ce47122

+ 17 - 133
examples/jsm/renderers/nodes/core/FunctionNode.js

@@ -1,12 +1,6 @@
 import CodeNode from './CodeNode.js';
-import NodeFunctionInput from './NodeFunctionInput.js';
 import FunctionCallNode from './FunctionCallNode.js';
 
-const declarationRegexp = /^\s*(highp|mediump|lowp)?\s*([a-z_0-9]+)\s*([a-z_0-9]+)?\s*\((.*?)\)/i;
-const propertiesRegexp = /[a-z_0-9]+/ig;
-
-const pragmaMain = '#pragma main';
-
 class FunctionNode extends CodeNode {
 
 	constructor( code = '' ) {
@@ -14,130 +8,33 @@ class FunctionNode extends CodeNode {
 		super( code );
 
 		this.inputs = [];
-
-		this.name = '';
-		this.needsUpdate = true;
+		this.nodeFunction = null;
 
 		this.useKeywords = true;
 
-		this.presicion = '';
-
-		this._includeCode = '';
-		this._internalCode = '';
-
 	}
 
 	getNodeType( builder ) {
 
-		if ( this.needsUpdate === true ) {
-
-			this.parse();
-
-		}
-
-		return super.getNodeType( builder );
+		return this.getNodeFunction( builder ).type;
 
 	}
 
-	getInputs( /*builder*/ ) {
-
-		if ( this.needsUpdate === true ) {
-
-			this.parse();
-
-		}
+	getInputs( builder ) {
 
-		return this.inputs;
+		return this.getNodeFunction( builder ).inputs;
 
 	}
 
-	parse() {
-
-		const code = this.code;
-
-		const pragmaMainIndex = code.indexOf( pragmaMain );
-
-		const mainCode = pragmaMainIndex !== - 1 ? code.substr( pragmaMainIndex + pragmaMain.length ) : code;
-
-		const declaration = mainCode.match( declarationRegexp );
-
-		if ( declaration !== null && declaration.length === 5 ) {
-
-			// tokenizer
-
-			const paramsCode = declaration[ 4 ];
-			const propsMatches = [];
-
-			let nameMatch = null;
-
-			while ( ( nameMatch = propertiesRegexp.exec( paramsCode ) ) !== null ) {
-
-				propsMatches.push( nameMatch );
-
-			}
-
-			// parser
-
-			const inputs = [];
-
-			let i = 0;
-
-			while ( i < propsMatches.length ) {
-
-				const isConst = propsMatches[ i ][ 0 ] === 'const';
-
-				if ( isConst === true ) {
-
-					i ++;
-
-				}
+	getNodeFunction( builder ) {
 
-				let qualifier = propsMatches[ i ][ 0 ];
+		if ( this.nodeFunction === null ) {
 
-				if ( qualifier === 'in' || qualifier === 'out' || qualifier === 'inout' ) {
-
-					i ++;
-
-				} else {
-
-					qualifier = '';
-
-				}
-
-				const type = propsMatches[ i ++ ][ 0 ];
-
-				let count = Number.parseInt( propsMatches[ i ][ 0 ] );
-
-				if ( Number.isNaN( count ) === false ) i ++;
-				else count = 0;
-
-				const name = propsMatches[ i ++ ][ 0 ];
-
-				inputs.push( new NodeFunctionInput( type, name, qualifier, isConst, count ) );
-
-			}
-
-			const blockCode = mainCode.substring( declaration[ 0 ].length );
-
-			this.name = declaration[ 3 ] !== undefined ? declaration[ 3 ] : '';
-			this.nodeType = declaration[ 2 ];
-
-			this.presicion = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
-
-			this.inputs = inputs;
-
-			this._includeCode = pragmaMainIndex !== - 1 ? code.substr( 0, pragmaMainIndex ) : '';
-			this._internalCode = `( ${paramsCode} ) ${blockCode}`;
-
-		} else {
-
-			throw new Error( 'FunctionNode: Function is not a GLSL code.' );
+			this.nodeFunction = builder.parser.parseFunction( this.code );
 
 		}
 
-		this.code = code;
-
-		this.needsUpdate = false;
+		return this.nodeFunction;
 
 	}
 
@@ -151,37 +48,24 @@ class FunctionNode extends CodeNode {
 
 		super.generate( builder );
 
-		const type = this.getNodeType( builder );
+		const nodeFunction = this.getNodeFunction( builder );
+
+		const name = nodeFunction.name;
+		const type = nodeFunction.type;
+
 		const nodeCode = builder.getCodeFromNode( this, type );
 
-		if ( this.name !== '' ) {
+		if ( name !== '' ) {
 
 			// use a custom property name
 
-			nodeCode.name = this.name;
+			nodeCode.name = name;
 
 		}
 
 		const propertyName = builder.getPropertyName( nodeCode );
 
-		const presicion = this.presicion;
-		const includeCode = this._includeCode;
-
-		let code = `${type} ${propertyName} ${this._internalCode}`;
-
-		if ( presicion !== '' ) {
-
-			code = `${presicion} ${code}`;
-
-		}
-
-		if ( includeCode !== '' ) {
-
-			code = `${includeCode} ${code}`;
-
-		}
-
-		nodeCode.code = code;
+		nodeCode.code = this.getNodeFunction( builder ).getCode( propertyName );
 
 		if ( output === 'property' ) {
 
@@ -189,7 +73,7 @@ class FunctionNode extends CodeNode {
 
 		} else {
 
-			return builder.format( `${propertyName}()`, type, output );
+			return builder.format( `${ propertyName }()`, type, output );
 
 		}
 

+ 2 - 1
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -10,11 +10,12 @@ import { LinearEncoding } from 'three';
 
 class NodeBuilder {
 
-	constructor( object, renderer ) {
+	constructor( object, renderer, parser ) {
 
 		this.object = object;
 		this.material = object.material;
 		this.renderer = renderer;
+		this.parser = parser;
 
 		this.nodes = [];
 		this.updateNodes = [];

+ 22 - 0
examples/jsm/renderers/nodes/core/NodeFunction.js

@@ -0,0 +1,22 @@
+class NodeFunction {
+
+	constructor( type, inputs, name = '', presicion = '' ) {
+
+		this.type = type;
+		this.inputs = inputs;
+		this.name = name;
+		this.presicion = presicion;
+
+	}
+
+	getCode( /*name = this.name*/ ) {
+
+		console.warn( 'Abstract function.' );
+
+	}
+
+}
+
+NodeFunction.isNodeFunction = true;
+
+export default NodeFunction;

+ 4 - 4
examples/jsm/renderers/nodes/core/NodeFunctionInput.js

@@ -1,17 +1,17 @@
 class NodeFunctionInput {
 
-	constructor( type, name, qualifier = '', isConst = false, count = 0 ) {
+	constructor( type, name, count = null, qualifier = '', isConst = false ) {
 
 		this.type = type;
 		this.name = name;
+		this.count = count;
 		this.qualifier = qualifier;
 		this.isConst = isConst;
-		this.count = count;
-
-		Object.defineProperty( this, 'isNodeFunction', { value: true } );
 
 	}
 
 }
 
+NodeFunctionInput.isNodeFunctionInput = true;
+
 export default NodeFunctionInput;

+ 11 - 0
examples/jsm/renderers/nodes/core/NodeParser.js

@@ -0,0 +1,11 @@
+class NodeParser {
+
+	parseFunction( /*source*/ ) {
+
+		console.warn( 'Abstract function.' );
+
+	}
+
+}
+
+export default NodeParser;

+ 135 - 0
examples/jsm/renderers/nodes/parsers/GLSLNodeFunction.js

@@ -0,0 +1,135 @@
+import NodeFunction from '../core/NodeFunction.js';
+import NodeFunctionInput from '../core/NodeFunctionInput.js';
+
+const declarationRegexp = /^\s*(highp|mediump|lowp)?\s*([a-z_0-9]+)\s*([a-z_0-9]+)?\s*\((.*?)\)/i;
+const propertiesRegexp = /[a-z_0-9]+/ig;
+
+const pragmaMain = '#pragma main';
+
+const parse = ( source ) => {
+
+	const pragmaMainIndex = source.indexOf( pragmaMain );
+
+	const mainCode = pragmaMainIndex !== - 1 ? source.substr( pragmaMainIndex + pragmaMain.length ) : source;
+
+	const declaration = mainCode.match( declarationRegexp );
+
+	if ( declaration !== null && declaration.length === 5 ) {
+
+		// tokenizer
+
+		const inputsCode = declaration[ 4 ];
+		const propsMatches = [];
+
+		let nameMatch = null;
+
+		while ( ( nameMatch = propertiesRegexp.exec( inputsCode ) ) !== null ) {
+
+			propsMatches.push( nameMatch );
+
+		}
+
+		// parser
+
+		const inputs = [];
+
+		let i = 0;
+
+		while ( i < propsMatches.length ) {
+
+			const isConst = propsMatches[ i ][ 0 ] === 'const';
+
+			if ( isConst === true ) {
+
+				i ++;
+
+			}
+
+			let qualifier = propsMatches[ i ][ 0 ];
+
+			if ( qualifier === 'in' || qualifier === 'out' || qualifier === 'inout' ) {
+
+				i ++;
+
+			} else {
+
+				qualifier = '';
+
+			}
+
+			const type = propsMatches[ i ++ ][ 0 ];
+
+			let count = Number.parseInt( propsMatches[ i ][ 0 ] );
+
+			if ( Number.isNaN( count ) === false ) i ++;
+			else count = null;
+
+			const name = propsMatches[ i ++ ][ 0 ];
+
+			inputs.push( new NodeFunctionInput( type, name, count, qualifier, isConst ) );
+
+		}
+
+		//
+
+		const blockCode = mainCode.substring( declaration[ 0 ].length );
+
+		const name = declaration[ 3 ] !== undefined ? declaration[ 3 ] : '';
+		const type = declaration[ 2 ];
+
+		const presicion = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
+
+		const headerCode = pragmaMainIndex !== - 1 ? source.substr( 0, pragmaMainIndex ) : '';
+
+		return {
+			type,
+			inputs,
+			name,
+			presicion,
+			inputsCode,
+			blockCode,
+			headerCode
+		};
+
+	} else {
+
+		throw new Error( 'FunctionNode: Function is not a GLSL code.' );
+
+	}
+
+};
+
+class GLSLNodeFunction extends NodeFunction {
+
+	constructor( source ) {
+
+		const { type, inputs, name, presicion, inputsCode, blockCode, headerCode } = parse( source );
+
+		super( type, inputs, name, presicion );
+
+		this.inputsCode = inputsCode;
+		this.blockCode = blockCode;
+		this.headerCode = headerCode;
+
+	}
+
+	getCode( name = this.name ) {
+
+		const headerCode = this.headerCode;
+		const presicion = this.presicion;
+
+		let declarationCode = `${ this.type } ${ name } ( ${ this.inputsCode.trim() } )`;
+
+		if ( presicion !== '' ) {
+
+			declarationCode = `${ presicion } ${ declarationCode }`;
+
+		}
+
+		return headerCode + declarationCode + this.blockCode;
+
+	}
+
+}
+
+export default GLSLNodeFunction;

+ 14 - 0
examples/jsm/renderers/nodes/parsers/GLSLNodeParser.js

@@ -0,0 +1,14 @@
+import NodeParser from '../core/NodeParser.js';
+import GLSLNodeFunction from './GLSLNodeFunction.js';
+
+class GLSLNodeParser extends NodeParser {
+
+	parseFunction( source ) {
+
+		return new GLSLNodeFunction( source );
+
+	}
+
+}
+
+export default GLSLNodeParser;

+ 2 - 1
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -1,5 +1,6 @@
 import NodeBuilder from '../../nodes/core/NodeBuilder.js';
 import NodeSlot from '../../nodes/core/NodeSlot.js';
+import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
 import { ShaderChunk, LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
@@ -22,7 +23,7 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	constructor( object, renderer, shader ) {
 
-		super( object, renderer );
+		super( object, renderer, new GLSLNodeParser() );
 
 		this.shader = shader;
 

+ 2 - 1
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -11,6 +11,7 @@ import { getVectorLength, getStrideLength } from '../WebGPUBufferUtils.js';
 
 import NodeSlot from '../../nodes/core/NodeSlot.js';
 import VarNode from '../../nodes/core/VarNode.js';
+import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import BypassNode from '../../nodes/core/BypassNode.js';
 import NodeBuilder from '../../nodes/core/NodeBuilder.js';
 import MaterialNode from '../../nodes/accessors/MaterialNode.js';
@@ -25,7 +26,7 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	constructor( object, renderer, lightNode = null ) {
 
-		super( object, renderer );
+		super( object, renderer, new GLSLNodeParser() );
 
 		this.lightNode = lightNode;