From fb2db55c9b6d74c578c5ce61c62b79a992f17905 Mon Sep 17 00:00:00 2001 From: sunag Date: Sun, 12 Mar 2023 02:09:05 -0300 Subject: [PATCH] TSL: if, elseif, else syntax (#25653) * WebGLNodeBuilder: Remove redundant code. * Nodes: Added if/else --- .../jsm/nodes/accessors/CubeTextureNode.js | 2 +- examples/jsm/nodes/accessors/TextureNode.js | 2 +- examples/jsm/nodes/core/BypassNode.js | 2 +- examples/jsm/nodes/core/ExpressionNode.js | 2 +- examples/jsm/nodes/core/Node.js | 4 +- examples/jsm/nodes/core/NodeBuilder.js | 110 ++++++++++++------ examples/jsm/nodes/core/StackNode.js | 38 +++++- examples/jsm/nodes/core/TempNode.js | 2 +- examples/jsm/nodes/core/VarNode.js | 2 +- examples/jsm/nodes/gpgpu/ComputeNode.js | 2 +- .../nodes/materials/MeshNormalNodeMaterial.js | 2 +- .../nodes/materials/MeshPhongNodeMaterial.js | 2 +- .../materials/MeshStandardNodeMaterial.js | 2 +- examples/jsm/nodes/materials/NodeMaterial.js | 34 +++--- examples/jsm/nodes/math/CondNode.js | 19 ++- examples/jsm/nodes/math/OperatorNode.js | 2 +- examples/jsm/nodes/shadernode/ShaderNode.js | 10 +- .../renderers/webgl/nodes/WebGLNodeBuilder.js | 12 -- .../webgpu/nodes/WebGPUNodeBuilder.js | 9 +- examples/webgpu_audio_processing.html | 2 +- examples/webgpu_compute.html | 4 +- 21 files changed, 169 insertions(+), 95 deletions(-) diff --git a/examples/jsm/nodes/accessors/CubeTextureNode.js b/examples/jsm/nodes/accessors/CubeTextureNode.js index 0d27c2d745fd5c..5734f884eb278d 100644 --- a/examples/jsm/nodes/accessors/CubeTextureNode.js +++ b/examples/jsm/nodes/accessors/CubeTextureNode.js @@ -77,7 +77,7 @@ class CubeTextureNode extends TextureNode { } - builder.addFlowCode( `${propertyName} = ${snippet}` ); + builder.addLineFlowCode( `${propertyName} = ${snippet}` ); nodeData.snippet = snippet; nodeData.propertyName = propertyName; diff --git a/examples/jsm/nodes/accessors/TextureNode.js b/examples/jsm/nodes/accessors/TextureNode.js index 02360c98129e0e..ddad9b17b2c96a 100644 --- a/examples/jsm/nodes/accessors/TextureNode.js +++ b/examples/jsm/nodes/accessors/TextureNode.js @@ -118,7 +118,7 @@ class TextureNode extends UniformNode { } - builder.addFlowCode( `${propertyName} = ${snippet}` ); + builder.addLineFlowCode( `${propertyName} = ${snippet}` ); nodeData.snippet = snippet; nodeData.propertyName = propertyName; diff --git a/examples/jsm/nodes/core/BypassNode.js b/examples/jsm/nodes/core/BypassNode.js index b1658fc191913c..e3c27cead41933 100644 --- a/examples/jsm/nodes/core/BypassNode.js +++ b/examples/jsm/nodes/core/BypassNode.js @@ -26,7 +26,7 @@ class BypassNode extends Node { if ( snippet !== '' ) { - builder.addFlowCode( snippet ); + builder.addLineFlowCode( snippet ); } diff --git a/examples/jsm/nodes/core/ExpressionNode.js b/examples/jsm/nodes/core/ExpressionNode.js index ee7ce611b7f3cc..bb4dfa31411e87 100644 --- a/examples/jsm/nodes/core/ExpressionNode.js +++ b/examples/jsm/nodes/core/ExpressionNode.js @@ -18,7 +18,7 @@ class ExpressionNode extends Node { if ( type === 'void' ) { - builder.addFlowCode( snippet ); + builder.addLineFlowCode( snippet ); } else { diff --git a/examples/jsm/nodes/core/Node.js b/examples/jsm/nodes/core/Node.js index 0dea4378e87d8d..33dba30f82068b 100644 --- a/examples/jsm/nodes/core/Node.js +++ b/examples/jsm/nodes/core/Node.js @@ -168,7 +168,7 @@ class Node { } builder.addNode( this ); - builder.addStack( this ); + builder.addChain( this ); /* Build stages expected results: - "construct" -> Node @@ -233,7 +233,7 @@ class Node { } - builder.removeStack( this ); + builder.removeChain( this ); return result; diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index b174e6b6c84de1..aa356eb076005a 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -57,7 +57,9 @@ class NodeBuilder { this.varyings = []; this.vars = { vertex: [], fragment: [], compute: [] }; this.flow = { code: '' }; - this.stack = []; + this.chaining = []; + this.stack = stack(); + this.tab = '\t'; this.context = { keywords: new NodeKeywords(), @@ -75,59 +77,59 @@ class NodeBuilder { } - get node() { + setHashNode( node, hash ) { - return this.stack[ this.stack.length - 1 ]; + this.hashNodes[ hash ] = node; } - addStack( node ) { - - /* - if ( this.stack.indexOf( node ) !== - 1 ) { - - console.warn( 'Recursive node: ', node ); + addNode( node ) { - } - */ + if ( this.nodes.indexOf( node ) === - 1 ) { - this.stack.push( node ); + const updateType = node.getUpdateType( this ); - } + if ( updateType !== NodeUpdateType.NONE ) { - removeStack( node ) { + this.updateNodes.push( node ); - const lastStack = this.stack.pop(); + } - if ( lastStack !== node ) { + this.nodes.push( node ); - throw new Error( 'NodeBuilder: Invalid node stack!' ); + this.setHashNode( node, node.getHash( this ) ); } } - setHashNode( node, hash ) { + get currentNode() { - this.hashNodes[ hash ] = node; + return this.chaining[ this.chaining.length - 1 ]; } - addNode( node ) { + addChain( node ) { - if ( this.nodes.indexOf( node ) === - 1 ) { + /* + if ( this.chaining.indexOf( node ) !== - 1 ) { - const updateType = node.getUpdateType( this ); + console.warn( 'Recursive node: ', node ); - if ( updateType !== NodeUpdateType.NONE ) { + } + */ - this.updateNodes.push( node ); + this.chaining.push( node ); - } + } - this.nodes.push( node ); + removeChain( node ) { - this.setHashNode( node, node.getHash( this ) ); + const lastChain = this.chaining.pop(); + + if ( lastChain !== node ) { + + throw new Error( 'NodeBuilder: Invalid node chaining!' ); } @@ -447,9 +449,21 @@ class NodeBuilder { } - createStack() { + addStack() { + + this.stack = stack( this.stack ); + + return this.stack; + + } + + removeStack() { + + const currentStack = this.stack; - return stack(); + this.stack = currentStack.parent; + + return currentStack; } @@ -570,16 +584,46 @@ class NodeBuilder { } - addFlowCode( code, breakline = true ) { + addLineFlowCode( code ) { + + if ( code === '' ) return this; - if ( breakline && ! /;\s*$/.test( code ) ) { + code = this.tab + code; - code += ';\n\t'; + if ( ! /;\s*$/.test( code ) ) { + + code = code + ';\n'; } this.flow.code += code; + return this; + + } + + addFlowCode( code ) { + + this.flow.code += code; + + return this; + + } + + addFlowTab() { + + this.tab += '\t'; + + return this; + + } + + removeFlowTab() { + + this.tab = this.tab.slice( 0, - 1 ); + + return this; + } getFlowData( node/*, shaderStage*/ ) { @@ -628,7 +672,7 @@ class NodeBuilder { if ( propertyName !== null ) { - flowData.code += `${propertyName} = ${flowData.result};\n\t`; + flowData.code += `${propertyName} = ${flowData.result};\n` + this.tab; } diff --git a/examples/jsm/nodes/core/StackNode.js b/examples/jsm/nodes/core/StackNode.js index f22d33d8021559..6b4a4a531a639a 100644 --- a/examples/jsm/nodes/core/StackNode.js +++ b/examples/jsm/nodes/core/StackNode.js @@ -2,17 +2,22 @@ import Node, { addNodeClass } from './Node.js'; import { assign } from '../math/OperatorNode.js'; import { bypass } from '../core/BypassNode.js'; import { expression } from '../core/ExpressionNode.js'; -import { nodeProxy } from '../shadernode/ShaderNode.js'; +import { cond } from '../math/CondNode.js'; +import { nodeProxy, shader } from '../shadernode/ShaderNode.js'; class StackNode extends Node { - constructor() { + constructor( parent = null ) { super(); this.nodes = []; this.outputNode = null; + this.parent = parent; + + this._currentCond = null; + this.isStackNode = true; } @@ -31,6 +36,35 @@ class StackNode extends Node { } + if( boolNode, method ) { + + const methodNode = shader( method ); + this._currentCond = cond( boolNode, methodNode ); + + return this.add( this._currentCond ); + + } + + elseif( boolNode, method ) { + + const methodNode = shader( method ); + const ifNode = cond( boolNode, methodNode ); + + this._currentCond.elseNode = ifNode; + this._currentCond = ifNode; + + return this; + + } + + else( method ) { + + this._currentCond.elseNode = shader( method ); + + return this; + + } + assign( targetNode, sourceValue ) { return this.add( assign( targetNode, sourceValue ) ); diff --git a/examples/jsm/nodes/core/TempNode.js b/examples/jsm/nodes/core/TempNode.js index 6fb348853590cd..107f1e65b9dd84 100644 --- a/examples/jsm/nodes/core/TempNode.js +++ b/examples/jsm/nodes/core/TempNode.js @@ -36,7 +36,7 @@ class TempNode extends Node { const nodeVar = builder.getVarFromNode( this, type ); const propertyName = builder.getPropertyName( nodeVar ); - builder.addFlowCode( `${propertyName} = ${snippet}` ); + builder.addLineFlowCode( `${propertyName} = ${snippet}` ); nodeData.snippet = snippet; nodeData.propertyName = propertyName; diff --git a/examples/jsm/nodes/core/VarNode.js b/examples/jsm/nodes/core/VarNode.js index 61cef25512c041..ecd190c4e3ca08 100644 --- a/examples/jsm/nodes/core/VarNode.js +++ b/examples/jsm/nodes/core/VarNode.js @@ -70,7 +70,7 @@ class VarNode extends Node { const propertyName = builder.getPropertyName( nodeVar ); - builder.addFlowCode( `${propertyName} = ${snippet}` ); + builder.addLineFlowCode( `${propertyName} = ${snippet}` ); return propertyName; diff --git a/examples/jsm/nodes/gpgpu/ComputeNode.js b/examples/jsm/nodes/gpgpu/ComputeNode.js index ed16e394e4211c..8f7a911e737ecd 100644 --- a/examples/jsm/nodes/gpgpu/ComputeNode.js +++ b/examples/jsm/nodes/gpgpu/ComputeNode.js @@ -53,7 +53,7 @@ class ComputeNode extends Node { if ( snippet !== '' ) { - builder.addFlowCode( snippet ); + builder.addLineFlowCode( snippet ); } diff --git a/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js b/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js index 5dccd40b809422..9710f3f47f841a 100644 --- a/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshNormalNodeMaterial.js @@ -27,7 +27,7 @@ class MeshNormalNodeMaterial extends NodeMaterial { } - constructDiffuseColor( builder, stack ) { + constructDiffuseColor( { stack } ) { const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity; diff --git a/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js b/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js index 6ad01c719f1874..6ae1fb6f5a0f69 100644 --- a/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshPhongNodeMaterial.js @@ -42,7 +42,7 @@ class MeshPhongNodeMaterial extends NodeMaterial { } - constructVariants( builder, stack ) { + constructVariants( { stack } ) { // SHININESS diff --git a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js index 8f27028c7c9a7d..e4239b3ae4285b 100644 --- a/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js +++ b/examples/jsm/nodes/materials/MeshStandardNodeMaterial.js @@ -48,7 +48,7 @@ class MeshStandardNodeMaterial extends NodeMaterial { } - constructVariants( builder, stack ) { + constructVariants( { stack } ) { // METALNESS diff --git a/examples/jsm/nodes/materials/NodeMaterial.js b/examples/jsm/nodes/materials/NodeMaterial.js index 888fbf6fead138..929aa77d126a1c 100644 --- a/examples/jsm/nodes/materials/NodeMaterial.js +++ b/examples/jsm/nodes/materials/NodeMaterial.js @@ -52,30 +52,28 @@ class NodeMaterial extends ShaderMaterial { construct( builder ) { - // < STACKS > + // < VERTEX STAGE > - const vertexStack = builder.createStack(); - const fragmentStack = builder.createStack(); + builder.addStack(); - // < VERTEX STAGE > + builder.stack.outputNode = this.constructPosition( builder ); - vertexStack.outputNode = this.constructPosition( builder, vertexStack ); + builder.addFlow( 'vertex', builder.removeStack() ); // < FRAGMENT STAGE > - if ( this.normals === true ) this.constructNormal( builder, fragmentStack ); + builder.addStack(); - this.constructDiffuseColor( builder, fragmentStack ); - this.constructVariants( builder, fragmentStack ); + if ( this.normals === true ) this.constructNormal( builder ); - const outgoingLightNode = this.constructLighting( builder, fragmentStack ); + this.constructDiffuseColor( builder ); + this.constructVariants( builder ); - fragmentStack.outputNode = this.constructOutput( builder, fragmentStack, outgoingLightNode, diffuseColor.a ); + const outgoingLightNode = this.constructLighting( builder ); - // < FLOW > + builder.stack.outputNode = this.constructOutput( builder, outgoingLightNode, diffuseColor.a ); - builder.addFlow( 'vertex', vertexStack ); - builder.addFlow( 'fragment', fragmentStack ); + builder.addFlow( 'fragment', builder.removeStack() ); } @@ -109,13 +107,13 @@ class NodeMaterial extends ShaderMaterial { } - constructDiffuseColor( builder, stack ) { + constructDiffuseColor( { stack, geometry } ) { let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor; // VERTEX COLORS - if ( this.vertexColors === true && builder.geometry.hasAttribute( 'color' ) ) { + if ( this.vertexColors === true && geometry.hasAttribute( 'color' ) ) { colorNode = vec4( colorNode.xyz.mul( attribute( 'color' ) ), colorNode.a ); @@ -148,7 +146,7 @@ class NodeMaterial extends ShaderMaterial { } - constructNormal( builder, stack ) { + constructNormal( { stack } ) { // NORMAL VIEW @@ -226,7 +224,7 @@ class NodeMaterial extends ShaderMaterial { } - constructOutput( builder, stack, outgoingLight, opacity ) { + constructOutput( builder, outgoingLight, opacity ) { const renderer = builder.renderer; @@ -417,6 +415,6 @@ export function createNodeMaterialFromType( type ) { } -}; +} addNodeMaterial( NodeMaterial ); diff --git a/examples/jsm/nodes/math/CondNode.js b/examples/jsm/nodes/math/CondNode.js index 2bd1792bcc1c40..142d17e76f768d 100644 --- a/examples/jsm/nodes/math/CondNode.js +++ b/examples/jsm/nodes/math/CondNode.js @@ -41,26 +41,33 @@ class CondNode extends Node { const type = this.getNodeType( builder ); const context = { tempWrite: false }; - const needsProperty = this.ifNode.getNodeType( builder ) !== 'void' || ( this.elseNode && this.elseNode.getNodeType( builder ) !== 'void' ); + const { ifNode, elseNode } = this; + + const needsProperty = ifNode.getNodeType( builder ) !== 'void' || ( elseNode && elseNode.getNodeType( builder ) !== 'void' ); const nodeProperty = needsProperty ? property( type ).build( builder ) : ''; const nodeSnippet = contextNode( this.condNode/*, context*/ ).build( builder, 'bool' ); - builder.addFlowCode( `if ( ${nodeSnippet} ) {\n\n\t\t`, false ); + builder.addFlowCode( `\n${ builder.tab }if ( ${ nodeSnippet } ) {\n\n` ).addFlowTab(); let ifSnippet = contextNode( this.ifNode, context ).build( builder, type ); ifSnippet = needsProperty ? nodeProperty + ' = ' + ifSnippet + ';' : ifSnippet; - builder.addFlowCode( ifSnippet + '\n\n\t}', false ); + builder.removeFlowTab().addFlowCode( builder.tab + '\t' + ifSnippet + '\n\n' + builder.tab + '}' ); - let elseSnippet = this.elseNode ? contextNode( this.elseNode, context ).build( builder, type ) : null; + if ( elseNode !== null ) { - if ( elseSnippet ) { + builder.addFlowCode( ' else {\n\n' ).addFlowTab(); + let elseSnippet = contextNode( elseNode, context ).build( builder, type ); elseSnippet = nodeProperty ? nodeProperty + ' = ' + elseSnippet + ';' : elseSnippet; - builder.addFlowCode( 'else {\n\n\t\t' + elseSnippet + '\n\n\t}', false ); + builder.removeFlowTab().addFlowCode( builder.tab + '\t' + elseSnippet + '\n\n' + builder.tab + '}\n\n' ); + + } else { + + builder.addFlowCode( '\n\n' ); } diff --git a/examples/jsm/nodes/math/OperatorNode.js b/examples/jsm/nodes/math/OperatorNode.js index f81bb608eb3bcb..6a8ae5f3d47787 100644 --- a/examples/jsm/nodes/math/OperatorNode.js +++ b/examples/jsm/nodes/math/OperatorNode.js @@ -172,7 +172,7 @@ class OperatorNode extends TempNode { if ( op === '=' ) { - builder.addFlowCode( `${a} ${this.op} ${b}` ); + builder.addLineFlowCode( `${a} ${this.op} ${b}` ); return a; diff --git a/examples/jsm/nodes/shadernode/ShaderNode.js b/examples/jsm/nodes/shadernode/ShaderNode.js index 8972b7281d49ff..0f402da9e3ee3d 100644 --- a/examples/jsm/nodes/shadernode/ShaderNode.js +++ b/examples/jsm/nodes/shadernode/ShaderNode.js @@ -205,9 +205,11 @@ class ShaderNodeInternal extends Node { construct( builder ) { - const stackNode = builder.createStack(); - stackNode.outputNode = this.call( {}, stackNode, builder ); - return stackNode; + builder.addStack(); + + builder.stack.outputNode = nodeObject( this._jsFunc( builder.stack, builder ) ); + + return builder.removeStack(); } @@ -311,6 +313,8 @@ export const nodeArray = ( val ) => new ShaderNodeArray( val ); export const nodeProxy = ( ...val ) => new ShaderNodeProxy( ...val ); export const nodeImmutable = ( ...val ) => new ShaderNodeImmutable( ...val ); +export const shader = ( ...val ) => new ShaderNode( ...val ); + addNodeClass( ShaderNode ); // types diff --git a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js index bec1b28b715efe..cfadf2214d34ce 100644 --- a/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js +++ b/examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js @@ -64,18 +64,6 @@ class WebGLNodeBuilder extends NodeBuilder { } - addFlowCode( code ) { - - if ( ! /;\s*$/.test( code ) ) { - - code += ';'; - - } - - super.addFlowCode( code + '\n\t' ); - - } - _parseShaderLib() { const material = this.material; diff --git a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js index 1f8d0087c6d8e0..1b3afaff613601 100644 --- a/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js @@ -600,9 +600,8 @@ class WebGPUNodeBuilder extends NodeBuilder { for ( const shaderStage in shadersData ) { - let flow = '// code\n'; - flow += `\t${ this.flowCode[ shaderStage ] }`; - flow += '\n\t'; + let flow = '// code\n\n'; + flow += this.flowCode[ shaderStage ]; const flowNodes = this.flowNodes[ shaderStage ]; const mainNode = flowNodes[ flowNodes.length - 1 ]; @@ -616,7 +615,7 @@ class WebGPUNodeBuilder extends NodeBuilder { if ( flow.length > 0 ) flow += '\n'; - flow += `\t// FLOW -> ${ slotName }\n\t`; + flow += `\t// flow -> ${ slotName }\n\t`; } @@ -624,7 +623,7 @@ class WebGPUNodeBuilder extends NodeBuilder { if ( node === mainNode && shaderStage !== 'compute' ) { - flow += '// FLOW RESULT\n\t'; + flow += '// result\n\t'; if ( shaderStage === 'vertex' ) { diff --git a/examples/webgpu_audio_processing.html b/examples/webgpu_audio_processing.html index 510a7f10a0a9d9..088826a3d480a3 100644 --- a/examples/webgpu_audio_processing.html +++ b/examples/webgpu_audio_processing.html @@ -136,7 +136,7 @@ // compute (shader-node) - const computeShaderNode = new ShaderNode( ( inputs, stack ) => { + const computeShaderNode = new ShaderNode( ( stack ) => { const index = float( instanceIndex ); diff --git a/examples/webgpu_compute.html b/examples/webgpu_compute.html index 3b93a410135b75..90cd3b3260f8de 100644 --- a/examples/webgpu_compute.html +++ b/examples/webgpu_compute.html @@ -74,7 +74,7 @@ // create function - const computeShaderNode = new ShaderNode( ( inputs, stack ) => { + const computeShaderNode = new ShaderNode( ( stack ) => { const particle = particleBufferNode.element( instanceIndex ); const velocity = velocityBufferNode.element( instanceIndex ); @@ -101,7 +101,7 @@ computeNode = computeShaderNode.compute( particleNum ); computeNode.onInit = ( { renderer } ) => { - const precomputeShaderNode = new ShaderNode( ( inputs, stack ) => { + const precomputeShaderNode = new ShaderNode( ( stack ) => { const particleIndex = float( instanceIndex );