From 168f02de450ecf7d3636411e0072c9347726722a Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 01:30:12 +1100 Subject: [PATCH 1/7] Introduce NodeBuilder.formatOperation Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/nodes/Nodes.js | 2 +- .../jsm/nodes/accessors/TextureSizeNode.js | 2 +- examples/jsm/nodes/code/FunctionCallNode.js | 3 +- examples/jsm/nodes/core/AssignNode.js | 6 +- examples/jsm/nodes/core/NodeBuilder.js | 320 ++++++++++++++++-- examples/jsm/nodes/core/VarNode.js | 2 +- examples/jsm/nodes/math/MathNode.js | 7 +- examples/jsm/nodes/math/OperatorNode.js | 50 +-- examples/jsm/nodes/pmrem/PMREMUtils.js | 2 +- examples/jsm/nodes/utils/ArrayElementNode.js | 2 +- examples/jsm/nodes/utils/JoinNode.js | 3 +- examples/jsm/nodes/utils/SplitNode.js | 2 +- .../webgl-legacy/nodes/GLSL1NodeBuilder.js | 41 ++- .../webgl-legacy/nodes/WebGLNodeBuilder.js | 47 ++- .../renderers/webgl/nodes/GLSLNodeBuilder.js | 49 ++- .../renderers/webgpu/nodes/WGSLNodeBuilder.js | 163 +++------ 16 files changed, 460 insertions(+), 241 deletions(-) diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 514eabe2e4e15..87022d19414cb 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -38,7 +38,7 @@ import * as NodeUtils from './core/NodeUtils.js'; export { NodeUtils }; // math -export { default as MathNode, PI, PI2, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, lengthSq, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, bitcast, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward, cbrt, all, any, equals } from './math/MathNode.js'; +export { default as MathNode, PI, PI2, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, lengthSq, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, bitcast, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward, cbrt, all, any } from './math/MathNode.js'; export { default as OperatorNode, add, sub, mul, div, remainder, equal, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, not, xor, bitAnd, bitNot, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js'; export { default as CondNode, cond } from './math/CondNode.js'; diff --git a/examples/jsm/nodes/accessors/TextureSizeNode.js b/examples/jsm/nodes/accessors/TextureSizeNode.js index 88b0cf67f57c4..255791ef999a4 100644 --- a/examples/jsm/nodes/accessors/TextureSizeNode.js +++ b/examples/jsm/nodes/accessors/TextureSizeNode.js @@ -20,7 +20,7 @@ class TextureSizeNode extends Node { const textureProperty = this.textureNode.build( builder, 'property' ); const levelNode = this.levelNode.build( builder, 'int' ); - return builder.format( `${builder.getMethod( 'textureDimensions' )}( ${textureProperty}, ${levelNode} )`, this.getNodeType( builder ), output ); + return builder.format( builder.formatOperation( '()', 'textureDimensions', [ textureProperty, levelNode ] ), this.getNodeType( builder ), output ); } diff --git a/examples/jsm/nodes/code/FunctionCallNode.js b/examples/jsm/nodes/code/FunctionCallNode.js index b5cfd8e267462..0c6ec6321e97a 100644 --- a/examples/jsm/nodes/code/FunctionCallNode.js +++ b/examples/jsm/nodes/code/FunctionCallNode.js @@ -74,8 +74,7 @@ class FunctionCallNode extends TempNode { } const functionName = functionNode.build( builder, 'property' ); - - return `${functionName}( ${params.join( ', ' )} )`; + return builder.formatOperation( '()', functionName, params ); } diff --git a/examples/jsm/nodes/core/AssignNode.js b/examples/jsm/nodes/core/AssignNode.js index 79a66814ed31e..15a97e388dc16 100644 --- a/examples/jsm/nodes/core/AssignNode.js +++ b/examples/jsm/nodes/core/AssignNode.js @@ -75,7 +75,7 @@ class AssignNode extends TempNode { const sourceVar = builder.getVarFromNode( this, null, targetType ); const sourceProperty = builder.getPropertyName( sourceVar ); - builder.addLineFlowCode( `${ sourceProperty } = ${ source }` ); + builder.addLineFlowCode( builder.formatOperation( '=', sourceProperty, source ) ); const targetRoot = targetNode.node.context( { assign: true } ).build( builder ); @@ -83,7 +83,7 @@ class AssignNode extends TempNode { const component = targetNode.components[ i ]; - builder.addLineFlowCode( `${ targetRoot }.${ component } = ${ sourceProperty }[ ${ i } ]` ); + builder.addLineFlowCode( builder.formatOperation( '=', `${ targetRoot }.${ component }`, `${ sourceProperty }[ ${ i } ]` ) ); } @@ -95,7 +95,7 @@ class AssignNode extends TempNode { } else { - snippet = `${ target } = ${ source }`; + snippet = builder.formatOperation( '=', target, source ); if ( output === 'void' || sourceType === 'void' ) { diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 35ae6643716fd..08e0b2bdae010 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -7,6 +7,7 @@ import NodeKeywords from './NodeKeywords.js'; import NodeCache from './NodeCache.js'; import ParameterNode from './ParameterNode.js'; import FunctionNode from '../code/FunctionNode.js'; +import CodeNode from '../code/CodeNode.js'; import { createNodeMaterialFromType, default as NodeMaterial } from '../materials/NodeMaterial.js'; import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js'; @@ -265,12 +266,6 @@ class NodeBuilder { } - getMethod( method ) { - - return method; - - } - getNodeFromHash( hash ) { return this.hashNodes[ hash ]; @@ -374,7 +369,6 @@ class NodeBuilder { if ( type === 'int' ) return `${ Math.round( value ) }`; if ( type === 'uint' ) return value >= 0 ? `${ Math.round( value ) }u` : '0u'; if ( type === 'bool' ) return value ? 'true' : 'false'; - if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${ toFloat( value.r ) }, ${ toFloat( value.g ) }, ${ toFloat( value.b ) } )`; const typeLength = this.getTypeLength( type ); @@ -382,25 +376,17 @@ class NodeBuilder { const generateConst = value => this.generateConst( componentType, value ); - if ( typeLength === 2 ) { - - return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) } )`; + if ( typeLength >= 2 && typeLength <= 4 ) { - } else if ( typeLength === 3 ) { - - return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) }, ${ generateConst( value.z ) } )`; - - } else if ( typeLength === 4 ) { - - return `${ this.getType( type ) }( ${ generateConst( value.x ) }, ${ generateConst( value.y ) }, ${ generateConst( value.z ) }, ${ generateConst( value.w ) } )`; + return this.formatOperation( '()', this.getType( type ), [ ...value ].map( generateConst ) ); } else if ( typeLength > 4 && value && ( value.isMatrix3 || value.isMatrix4 ) ) { - return `${ this.getType( type ) }( ${ value.elements.map( generateConst ).join( ', ' ) } )`; + return this.formatOperation( '()', this.getType( type ), value.elements.map( generateConst ) ); } else if ( typeLength > 4 ) { - return `${ this.getType( type ) }()`; + return this.formatOperation( '()', this.getType( type ) ); } @@ -602,6 +588,42 @@ class NodeBuilder { } + _getPolyfills() { + + return {}; + + } + + _resolvePolyfill( name, type = null ) { + + return this._getPolyfills()[ name + '_' + type ] ? `threejs_${ name }_${ type }` : `threejs_${ name }`; + + } + + _include( name, type = null ) { + + const polyfills = this._getPolyfills(); + + if ( typeof polyfills[ name ] === 'function' && type !== null && ! polyfills[ name + '_' + type ] ) { // auto-generate polyfill for the required type + + polyfills[ name + '_' + type ] = new CodeNode( polyfills[ name ]( `threejs_${ name }_${ type }`, this.getType( type ) ) ); + + } + + const codeNode = polyfills[ name + '_' + type ] || polyfills[ name ]; + + codeNode.build( this ); + + if ( this.currentFunctionNode !== null ) { + + this.currentFunctionNode.includes.push( codeNode ); + + } + + return codeNode; + + } + addStack() { this.stack = stack( this.stack ); @@ -1209,25 +1231,25 @@ class NodeBuilder { if ( fromTypeLength === toTypeLength ) { - return `${ this.getType( toType ) }( ${ snippet } )`; + return this.formatOperation( '()', this.getType( toType ), snippet ); } if ( fromTypeLength > toTypeLength ) { - return this.format( `${ snippet }.${ 'xyz'.slice( 0, toTypeLength ) }`, this.getTypeFromLength( toTypeLength, this.getComponentType( fromType ) ), toType ); + return this.format( this.formatOperation( '.', snippet, 'xyz'.slice( 0, toTypeLength ) ), this.getTypeFromLength( toTypeLength, this.getComponentType( fromType ) ), toType ); } if ( toTypeLength === 4 && fromTypeLength > 1 ) { // toType is vec4-like - return `${ this.getType( toType ) }( ${ this.format( snippet, fromType, 'vec3' ) }, 1.0 )`; + return this.formatOperation( '()', this.getType( toType ), [ this.format( snippet, fromType, 'vec3' ), '1.0' ] ); } if ( fromTypeLength === 2 ) { // fromType is vec2-like and toType is vec3-like - return `${ this.getType( toType ) }( ${ this.format( snippet, fromType, 'vec2' ) }, 0.0 )`; + return this.formatOperation( '()', this.getType( toType ), [ this.format( snippet, fromType, 'vec2' ), '0.0' ] ); } @@ -1236,11 +1258,259 @@ class NodeBuilder { // convert a number value to vector type, e.g: // vec3( 1u ) -> vec3( float( 1u ) ) - snippet = `${ this.getType( this.getComponentType( toType ) ) }( ${ snippet } )`; + snippet = this.formatOperation( '()', this.getType( this.getComponentType( toType ) ), snippet ); } - return `${ this.getType( toType ) }( ${ snippet } )`; // fromType is float-like + return this.formatOperation( '()', this.getType( toType ), snippet ); // fromType is float-like + + } + + _parseTopLevel( str ) { + + const operators = [ ',', '.', '~', '!', '++', '--', '+', '-', '*', '/', '%', '<<', '>>', '<', '>', '<=', '>=', '==', '!=', '&', '^', '|', '&&', '^^', '||' ]; + + const list = []; + let level = 0; + + for ( let i = 0; i < str.length; i ++ ) { + + let opData = null; + + if ( str[ i ] === '(' || str[ i ] === '[' ) { + + level ++; + + } else if ( str[ i ] === ')' || str[ i ] === ']' ) { + + level --; + + } else if ( level === 0 && operators.includes( str[ i ] + str[ i + 1 ] ) ) { + + opData = { start: i, end: i + 2, op: str[ i ] + str[ i + 1 ] }; + i ++; + + } else if ( level === 0 && operators.includes( str[ i ] ) ) { + + opData = { start: i, end: i + 1, op: str[ i ] }; + + } + + if ( opData === null ) continue; + + // not an operator, a part of type + // @TODO: move this to WGSLNodeBuilder? + if ( opData.op === '<' && [ 'f32', 'i32', 'u32', 'bool' ].includes( str.slice( opData.start + 1, str.indexOf( '>', opData.start ) ) ) ) continue; + if ( opData.op === '>' && [ 'f32', 'i32', 'u32', 'bool' ].includes( str.slice( str.lastIndexOf( '<', opData.start ) + 1, opData.start ) ) ) continue; + + const lastEnd = list.length > 0 ? list[ list.length - 1 ].end : 0; + const arg = str.slice( lastEnd, opData.start ); + const argNonEmpty = [ ...arg ].some( x => x !== ' ' ); + + if ( argNonEmpty === true ) { + + list.push( { start: lastEnd, end: opData.start, arg } ); + + } else { + + // if we have two operators in row (or one directly in the start) and the previous one is not unary, mark this one as unary + if ( list.length === 0 || ! list[ list.length - 1 ].unary ) opData.unary = true; + + } + + if ( opData.op === '++' || opData.op === '--' ) { + + opData.unary = true; + + if ( argNonEmpty ) opData.postfix = true; + else opData.prefix = true; + + } + + list.push( opData ); + + } + + const lastEnd = list.length > 0 ? list[ list.length - 1 ].end : 0; + const arg = str.slice( lastEnd ); + if ( [ ...arg ].some( x => x !== ' ' ) ) list.push( { start: lastEnd, end: str.length, arg } ); + + return list; + + } + + _getOperators() { + + console.warn( 'Abstract function.' ); + + } + + formatOperation( op, arg1, arg2, output = null ) { + + const languageOperators = this._getOperators(); // @TODO: make this just a static property + + if ( op === '()' && languageOperators.replace[ arg1 + '()' ] !== undefined ) { // change function + + if ( ! languageOperators.replace[ arg1 + '()' ].endsWith( '()' ) ) { // function -> op + + op = languageOperators.replace[ arg1 + '()' ]; + arg1 = arg2; + + const argParsed = this._parseTopLevel( arg1 ); + const comma = argParsed.find( arg => arg.op === ',' ); + + if ( comma !== undefined ) { + + arg2 = arg1.slice( comma.end ).trimLeft(); + arg1 = arg1.slice( 0, comma.start ).trimRight(); + + } + + } else { // function -> function + + arg1 = languageOperators.replace[ arg1 + '()' ].slice( 0, - 2 ); + + } + + } + + if ( languageOperators.replace[ op ] !== undefined ) { // change operator + + if ( languageOperators.replace[ op ].endsWith( '()' ) ) { // op -> function + + if ( output !== null && this.getTypeLength( output ) > 1 ) { + + arg2 = [ arg1, arg2 ]; + arg1 = languageOperators.replace[ op ].slice( 0, - 2 ); + op = '()'; + + } + + } else { // op -> op + + op = languageOperators.replace[ op ]; + + } + + } + + if ( op === '()' && arg1.startsWith( 'threejs_' ) ) { // include function polyfill + + this._include( arg1.slice( 'threejs_'.length ), output ); + arg1 = this._resolvePolyfill( arg1.slice( 'threejs_'.length ), output ); + + } + + if ( arg2 === undefined && ! op.startsWith( 'pre' ) && ! op.startsWith( 'post' ) ) op = 'un' + op; + + const inversePrecedence = languageOperators.ops; + + const precedence = {}; + for ( let i = 0; i < inversePrecedence.length; i ++ ) { + + for ( const op of inversePrecedence[ i ].ops ) { + + precedence[ op ] = { prec: i, maxAllowed: inversePrecedence[ i ].maxPrec, allowEqual: inversePrecedence[ i ].allowSelf }; + + } + + } + + const operators = inversePrecedence.map( x => x.ops ).flat(); + + if ( ! operators.includes( op ) || op === ',' ) { + + console.warn( `${ this.constructor.name }: Operator ${ op } is not supported.` ); + + } + + if ( Array.isArray( arg2 ) ) arg2 = arg2.join( ', ' ); + + if ( op === '()' || op === '[]' ) { + + if ( arg2 === undefined ) arg2 = ''; + if ( arg2 !== '' ) arg2 = ` ${ arg2 } `; + return `${ arg1 }${ op[ 0 ] }${ arg2 }${ op[ 1 ] }`; + + } + + const arg1Parsed = this._parseTopLevel( arg1 ); + const arg2Parsed = arg2 !== undefined ? this._parseTopLevel( arg2 ) : []; + + if ( op === '=' && arg2.startsWith( arg1 ) ) { + + const rhsOp = arg2Parsed.find( arg => !! arg.op && arg.start >= arg1.length ); + + if ( rhsOp === undefined ) { // dummy assignment + + return ''; + + } + + if ( rhsOp.op !== '<' && rhsOp.op !== '>' && operators.includes( rhsOp.op + '=' ) ) { // use assignment statement + + op = rhsOp.op + '='; + arg2 = arg2.slice( rhsOp.end ).trimLeft(); + + if ( arg2.startsWith( '(' ) && arg2.endsWith( ')' ) ) { // RHS' second argument could be wrapped in brackets + + let i, diff = 0; + + for ( i = 0; i < arg2.length; i ++ ) { + + if ( arg2[ i ] === '(' ) diff ++; + if ( arg2[ i ] === ')' ) diff --; + + if ( diff === 0 ) break; + + } + + if ( i === arg2.length - 1 ) arg2 = arg2.slice( 1, - 1 ).trim(); // the brackets are indeed outmost, remove them + + } + + } + + } + + const cannotAssociate = left => data => { + + if ( ! data.op ) return false; + + return precedence[ data.op ].prec > precedence[ op ].prec || + ( precedence[ data.op ].prec > precedence[ op ].maxAllowed && precedence[ data.op ].prec !== precedence[ op ].prec ) || + ( precedence[ data.op ].prec === precedence[ op ].prec && precedence[ op ].allowEqual === false ) || + ( precedence[ data.op ].prec === precedence[ op ].prec && left === false && // assuming left-to-right associativity for all binary operators + ! ( ( ( data.op === op ) && [ '&&', '||', '^^', '&', '|', '^' ].includes( op ) ) || [ '+', '[]', '()' ].includes( op ) ) // operators that do allow RTL associativity with same-precedence operators + // TODO: if we could somehow determine whether * is used in context of mat * mat/vec or component-wise multiplication, we could use it in the second list (only in the second context) + ); + + }; + + if ( arg1Parsed.some( cannotAssociate( true ) ) ) arg1 = `( ${ arg1 } )`; + if ( arg2Parsed.some( cannotAssociate( false ) ) ) arg2 = `( ${ arg2 } )`; + + if ( op === '.' ) { + + return `${ arg1 }.${ arg2 }`; + + } else if ( op.startsWith( 'un' ) ) { + + return `${ op.slice( 2 ) } ${ arg1 }`; + + } else if ( op.startsWith( 'pre' ) ) { + + return `${ op.slice( 3 ) } ${ arg1 }`; + + } else if ( op.startsWith( 'post' ) ) { + + return `${ arg1 } ${ op.slice( 4 ) }`; + + } else { + + return `${ arg1 } ${ op } ${ arg2 }`; + + } } diff --git a/examples/jsm/nodes/core/VarNode.js b/examples/jsm/nodes/core/VarNode.js index d9a43a3927b51..39e253dec05b1 100644 --- a/examples/jsm/nodes/core/VarNode.js +++ b/examples/jsm/nodes/core/VarNode.js @@ -42,7 +42,7 @@ class VarNode extends Node { const snippet = node.build( builder, nodeVar.type ); - builder.addLineFlowCode( `${propertyName} = ${snippet}` ); + builder.addLineFlowCode( builder.formatOperation( '=', propertyName, snippet ) ); return propertyName; diff --git a/examples/jsm/nodes/math/MathNode.js b/examples/jsm/nodes/math/MathNode.js index 0f28d2c901654..8c0119e7a79ce 100644 --- a/examples/jsm/nodes/math/MathNode.js +++ b/examples/jsm/nodes/math/MathNode.js @@ -114,7 +114,7 @@ class MathNode extends TempNode { } else if ( method === MathNode.NEGATE ) { - return builder.format( '( - ' + a.build( builder, inputType ) + ' )', type, output ); + return builder.format( builder.formatOperation( '-', a.build( builder, inputType ) ), type, output ); } else if ( method === MathNode.ONE_MINUS ) { @@ -177,7 +177,7 @@ class MathNode extends TempNode { } - return builder.format( `${ builder.getMethod( method, type ) }( ${params.join( ', ' )} )`, type, output ); + return builder.format( builder.formatOperation( '()', method, params, type ), type, output ); } @@ -205,7 +205,6 @@ class MathNode extends TempNode { MathNode.ALL = 'all'; MathNode.ANY = 'any'; -MathNode.EQUALS = 'equals'; MathNode.RADIANS = 'radians'; MathNode.DEGREES = 'degrees'; @@ -270,7 +269,6 @@ export const PI2 = float( Math.PI * 2 ); export const all = nodeProxy( MathNode, MathNode.ALL ); export const any = nodeProxy( MathNode, MathNode.ANY ); -export const equals = nodeProxy( MathNode, MathNode.EQUALS ); export const radians = nodeProxy( MathNode, MathNode.RADIANS ); export const degrees = nodeProxy( MathNode, MathNode.DEGREES ); @@ -333,7 +331,6 @@ export const smoothstepElement = ( x, low, high ) => smoothstep( low, high, x ); addNodeElement( 'all', all ); addNodeElement( 'any', any ); -addNodeElement( 'equals', equals ); addNodeElement( 'radians', radians ); addNodeElement( 'degrees', degrees ); diff --git a/examples/jsm/nodes/math/OperatorNode.js b/examples/jsm/nodes/math/OperatorNode.js index 149b28ef07b9b..7c701629abdb3 100644 --- a/examples/jsm/nodes/math/OperatorNode.js +++ b/examples/jsm/nodes/math/OperatorNode.js @@ -156,54 +156,8 @@ class OperatorNode extends TempNode { const a = aNode.build( builder, typeA ); const b = typeof bNode !== 'undefined' ? bNode.build( builder, typeB ) : null; - const outputLength = builder.getTypeLength( output ); - const fnOpSnippet = builder.getFunctionOperator( op ); - - if ( output !== 'void' ) { - - if ( op === '<' && outputLength > 1 ) { - - return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${ a }, ${ b } )`, type, output ); - - } else if ( op === '<=' && outputLength > 1 ) { - - return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${ a }, ${ b } )`, type, output ); - - } else if ( op === '>' && outputLength > 1 ) { - - return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${ a }, ${ b } )`, type, output ); - - } else if ( op === '>=' && outputLength > 1 ) { - - return builder.format( `${ builder.getMethod( 'greaterThanEqual' ) }( ${ a }, ${ b } )`, type, output ); - - } else if ( op === '!' || op === '~' ) { - - return builder.format( `(${op}${a})`, typeA, output ); - - } else if ( fnOpSnippet ) { - - return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output ); - - } else { - - return builder.format( `( ${ a } ${ op } ${ b } )`, type, output ); - - } - - } else if ( typeA !== 'void' ) { - - if ( fnOpSnippet ) { - - return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output ); - - } else { - - return builder.format( `${ a } ${ op } ${ b }`, type, output ); - - } - - } + const snippet = builder.formatOperation( op, a, b, type ); + return builder.format( snippet, type, output ); } diff --git a/examples/jsm/nodes/pmrem/PMREMUtils.js b/examples/jsm/nodes/pmrem/PMREMUtils.js index b5dc14bddc5f8..7d68ef5512e17 100644 --- a/examples/jsm/nodes/pmrem/PMREMUtils.js +++ b/examples/jsm/nodes/pmrem/PMREMUtils.js @@ -258,7 +258,7 @@ export const blur = tslFn( ( { n, latitudinal, poleAxis, outputDirection, weight const axis = vec3( cond( latitudinal, poleAxis, cross( poleAxis, outputDirection ) ) ).toVar(); - If( all( axis.equals( vec3( 0.0 ) ) ), () => { + If( all( axis.equal( vec3( 0.0 ) ) ), () => { axis.assign( vec3( outputDirection.z, 0.0, outputDirection.x.negate() ) ); diff --git a/examples/jsm/nodes/utils/ArrayElementNode.js b/examples/jsm/nodes/utils/ArrayElementNode.js index dfba683dd9bca..da896fa2f4f44 100644 --- a/examples/jsm/nodes/utils/ArrayElementNode.js +++ b/examples/jsm/nodes/utils/ArrayElementNode.js @@ -24,7 +24,7 @@ class ArrayElementNode extends Node { // @TODO: If extending from TempNode it br const nodeSnippet = this.node.build( builder ); const indexSnippet = this.indexNode.build( builder, 'uint' ); - return `${nodeSnippet}[ ${indexSnippet} ]`; + return builder.formatOperation( '[]', nodeSnippet, indexSnippet ); } diff --git a/examples/jsm/nodes/utils/JoinNode.js b/examples/jsm/nodes/utils/JoinNode.js index a1df4b9449573..0c0fb2d98a54a 100644 --- a/examples/jsm/nodes/utils/JoinNode.js +++ b/examples/jsm/nodes/utils/JoinNode.js @@ -48,8 +48,7 @@ class JoinNode extends TempNode { } - const snippet = `${ builder.getType( type ) }( ${ snippetValues.join( ', ' ) } )`; - + const snippet = builder.formatOperation( '()', builder.getType( type ), snippetValues ); return builder.format( snippet, type, output ); } diff --git a/examples/jsm/nodes/utils/SplitNode.js b/examples/jsm/nodes/utils/SplitNode.js index f4cab3e238888..4b95b72a18732 100644 --- a/examples/jsm/nodes/utils/SplitNode.js +++ b/examples/jsm/nodes/utils/SplitNode.js @@ -73,7 +73,7 @@ class SplitNode extends Node { } else { - snippet = builder.format( `${nodeSnippet}.${this.components}`, this.getNodeType( builder ), output ); + snippet = builder.format( builder.formatOperation( '.', nodeSnippet, this.components ), this.getNodeType( builder ), output ); } diff --git a/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js b/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js index 73873ebd11da9..3e783f581be52 100644 --- a/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js +++ b/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js @@ -1,9 +1,5 @@ import { MathNode, GLSLNodeParser, NodeBuilder } from '../../../nodes/Nodes.js'; -const glslMethods = { - [ MathNode.ATAN2 ]: 'atan' -}; - const precisionLib = { low: 'lowp', medium: 'mediump', @@ -18,12 +14,6 @@ class GLSL1NodeBuilder extends NodeBuilder { } - getMethod( method ) { - - return glslMethods[ method ] || method; - - } - getTexture( texture, textureProperty, uvSnippet ) { if ( texture.isTextureCube ) { @@ -313,6 +303,37 @@ void main() { } + _getOperators() { + + return { // https://www.khronos.org/files/opengles_shading_language.pdf, section 5.1 + ops: [ + { ops: [ '[]', '()', '.', 'post++', 'post--' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ 'pre++', 'pre--', 'un+', 'un-', 'un!' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '*', '/' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '+', '-' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<', '>', '<=', '>=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '==', '!=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '&&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '^^' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '||' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '=', '+=', '-=', '*=', '/=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ ',' ], maxPrec: Infinity, allowSelf: true } + ], + replace: { + // section 5.9 + '<': 'lessThan()', + '<=': 'lessThanEqual()', + '>': 'greaterThan()', + '>=': 'greaterThanEqual()', + '==': 'equal()', + '!=': 'notEqual()', + + 'atan2()': 'atan()' + } + }; + + } + } export default GLSL1NodeBuilder; diff --git a/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js b/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js index e903e87395911..bcd2bd5453fbd 100644 --- a/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js +++ b/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js @@ -14,10 +14,6 @@ const nodeShaderLib = { MeshPhongNodeMaterial: ShaderLib.phong }; -const glslMethods = { - [ MathNode.ATAN2 ]: 'atan' -}; - const precisionLib = { low: 'lowp', medium: 'mediump', @@ -53,12 +49,6 @@ class WebGLNodeBuilder extends NodeBuilder { } - getMethod( method ) { - - return glslMethods[ method ] || method; - - } - addSlot( shaderStage, slotNode ) { this.slots[ shaderStage ].push( slotNode ); @@ -787,6 +777,43 @@ ${this.shader[ getShaderStageProperty( shaderStage ) ]} } + _getOperators() { + + return { // https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf, section 5.1 + ops: [ + { ops: [ '[]', '()', '.', 'post++', 'post--' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ 'pre++', 'pre--', 'un+', 'un-', 'un~', 'un!' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '*', '/', '%' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '+', '-' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<<', '>>' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<', '>', '<=', '>=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '==', '!=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '^' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '|' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '&&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '^^' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '||' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '&=', '^=', '|=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ ',' ], maxPrec: Infinity, allowSelf: true } + ], + replace: { + // section 5.9 + '<': 'lessThan()', + '<=': 'lessThanEqual()', + '>': 'greaterThan()', + '>=': 'greaterThanEqual()', + '==': 'equal()', + '!=': 'notEqual()', + + // functions under different names + 'atan2()': 'atan()', + 'textureDimensions()': 'textureSize()' + } + }; + + } + } export { WebGLNodeBuilder }; diff --git a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js index bcf09fd7a4a05..d3912b85f608b 100644 --- a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js +++ b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js @@ -7,12 +7,6 @@ import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/N import { RedFormat, RGFormat, IntType, DataTexture, RGBAFormat, FloatType } from 'three'; -const glslMethods = { - [ MathNode.ATAN2 ]: 'atan', - textureDimensions: 'textureSize', - equals: 'equal' -}; - const precisionLib = { low: 'lowp', medium: 'mediump', @@ -42,12 +36,6 @@ class GLSLNodeBuilder extends NodeBuilder { } - getMethod( method ) { - - return glslMethods[ method ] || method; - - } - getPropertyName( node, shaderStage ) { if ( node.isOutputStructVar ) return ''; @@ -799,6 +787,43 @@ void main() { } + _getOperators() { + + return { // https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf, section 5.1 + ops: [ + { ops: [ '[]', '()', '.', 'post++', 'post--' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ 'pre++', 'pre--', 'un+', 'un-', 'un~', 'un!' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '*', '/', '%' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '+', '-' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<<', '>>' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<', '>', '<=', '>=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '==', '!=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '^' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '|' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '&&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '^^' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '||' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '&=', '^=', '|=' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ ',' ], maxPrec: Infinity, allowSelf: true } + ], + replace: { + // section 5.9 + '<': 'lessThan()', + '<=': 'lessThanEqual()', + '>': 'greaterThan()', + '>=': 'greaterThanEqual()', + '==': 'equal()', + '!=': 'notEqual()', + + // functions under different names + 'atan2()': 'atan()', + 'textureDimensions()': 'textureSize()' + } + }; + + } + } export default GLSLNodeBuilder; diff --git a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js index 66eea30b2a0ad..c204f641004e8 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -28,10 +28,6 @@ const supports = { storageBuffer: true }; -const wgslFnOpLib = { - '^^': 'threejs_xor' -}; - const wgslTypeLib = { float: 'f32', int: 'i32', @@ -70,62 +66,10 @@ const wgslTypeLib = { bmat4: 'mat4x4' }; -const wgslMethods = { - dFdx: 'dpdx', - dFdy: '- dpdy', - mod_float: 'threejs_mod_float', - mod_vec2: 'threejs_mod_vec2', - mod_vec3: 'threejs_mod_vec3', - mod_vec4: 'threejs_mod_vec4', - equals_bool: 'threejs_equals_bool', - equals_bvec2: 'threejs_equals_bvec2', - equals_bvec3: 'threejs_equals_bvec3', - equals_bvec4: 'threejs_equals_bvec4', - lessThanEqual: 'threejs_lessThanEqual', - greaterThan: 'threejs_greaterThan', - inversesqrt: 'inverseSqrt', - bitcast: 'bitcast' -}; - const wgslPolyfill = { - threejs_xor: new CodeNode( ` -fn threejs_xor( a : bool, b : bool ) -> bool { - - return ( a || b ) && !( a && b ); - -} -` ), - lessThanEqual: new CodeNode( ` -fn threejs_lessThanEqual( a : vec3, b : vec3 ) -> vec3 { - - return vec3( a.x <= b.x, a.y <= b.y, a.z <= b.z ); - -} -` ), - greaterThan: new CodeNode( ` -fn threejs_greaterThan( a : vec3, b : vec3 ) -> vec3 { - - return vec3( a.x > b.x, a.y > b.y, a.z > b.z ); - -} -` ), - mod_float: new CodeNode( 'fn threejs_mod_float( x : f32, y : f32 ) -> f32 { return x - y * floor( x / y ); }' ), - mod_vec2: new CodeNode( 'fn threejs_mod_vec2( x : vec2f, y : vec2f ) -> vec2f { return x - y * floor( x / y ); }' ), - mod_vec3: new CodeNode( 'fn threejs_mod_vec3( x : vec3f, y : vec3f ) -> vec3f { return x - y * floor( x / y ); }' ), - mod_vec4: new CodeNode( 'fn threejs_mod_vec4( x : vec4f, y : vec4f ) -> vec4f { return x - y * floor( x / y ); }' ), - equals_bool: new CodeNode( 'fn threejs_equals_bool( a : bool, b : bool ) -> bool { return a == b; }' ), - equals_bvec2: new CodeNode( 'fn threejs_equals_bvec2( a : vec2f, b : vec2f ) -> vec2 { return vec2( a.x == b.x, a.y == b.y ); }' ), - equals_bvec3: new CodeNode( 'fn threejs_equals_bvec3( a : vec3f, b : vec3f ) -> vec3 { return vec3( a.x == b.x, a.y == b.y, a.z == b.z ); }' ), - equals_bvec4: new CodeNode( 'fn threejs_equals_bvec4( a : vec4f, b : vec4f ) -> vec4 { return vec4( a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w ); }' ), - repeatWrapping: new CodeNode( ` -fn threejs_repeatWrapping( uv : vec2, dimension : vec2 ) -> vec2 { - - let uvScaled = vec2( uv * vec2( dimension ) ); - - return ( ( uvScaled % dimension ) + dimension ) % dimension; - -} -` ) + xor: ( name, wgslType ) => `fn ${ name }( a : ${ wgslType }, b : ${ wgslType } ) -> ${ wgslType } { return ( a || b ) && !( a && b ); }`, + mod: ( name, wgslType ) => `fn ${ name }( x : ${ wgslType }, y : ${ wgslType } ) -> ${ wgslType } { return x - y * floor( x / y ); }`, + repeatWrapping: new CodeNode( 'fn threejs_repeatWrapping( uv : vec2f, dimension : vec2u ) -> vec2u { return vec2u( uv * vec2f( dimension ) ) % dimension; }' ) }; class WGSLNodeBuilder extends NodeBuilder { @@ -327,22 +271,6 @@ class WGSLNodeBuilder extends NodeBuilder { } - getFunctionOperator( op ) { - - const fnOp = wgslFnOpLib[ op ]; - - if ( fnOp !== undefined ) { - - this._include( fnOp ); - - return fnOp; - - } - - return null; - - } - getUniformFromNode( node, type, shaderStage, name = null ) { const uniformNode = super.getUniformFromNode( node, type, shaderStage, name ); @@ -910,26 +838,6 @@ ${ flowData.code } } - getMethod( method, output = null ) { - - let wgslMethod; - - if ( output !== null ) { - - wgslMethod = this._getWGSLMethod( method + '_' + output ); - - } - - if ( wgslMethod === undefined ) { - - wgslMethod = this._getWGSLMethod( method ); - - } - - return wgslMethod || method; - - } - getType( type ) { return wgslTypeLib[ type ] || type; @@ -942,30 +850,9 @@ ${ flowData.code } } - _getWGSLMethod( method ) { - - if ( wgslPolyfill[ method ] !== undefined ) { - - this._include( method ); - - } + _getPolyfills() { - return wgslMethods[ method ]; - - } - - _include( name ) { - - const codeNode = wgslPolyfill[ name ]; - codeNode.build( this ); - - if ( this.currentFunctionNode !== null ) { - - this.currentFunctionNode.includes.push( codeNode ); - - } - - return codeNode; + return wgslPolyfill; } @@ -1075,6 +962,46 @@ var<${access}> ${name} : ${structName};`; } + _getOperators() { + + return { // https://www.w3.org/TR/WGSL/#operator-precedence-associativity + ops: [ + { ops: [ '[]', '()', '.' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ 'un-', 'un!', 'un~', 'un*', 'un&' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ 'post++', 'post--' ], maxPrec: Infinity, allowSelf: true }, // https://www.w3.org/TR/WGSL/#increment-decrement + { ops: [ '*', '/', '%' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '+', '-' ], maxPrec: Infinity, allowSelf: true }, + { ops: [ '<<', '>>' ], maxPrec: 1, allowSelf: false }, + { ops: [ '<', '>', '<=', '>=', '==', '!=' ], maxPrec: Infinity, allowSelf: false }, + { ops: [ '&' ], maxPrec: 1, allowSelf: true }, + { ops: [ '^' ], maxPrec: 1, allowSelf: true }, + { ops: [ '|' ], maxPrec: 1, allowSelf: true }, + { ops: [ '&&' ], maxPrec: 6, allowSelf: true }, + { ops: [ '||' ], maxPrec: 6, allowSelf: true }, + { ops: [ '=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '>>=', '<<=' ], maxPrec: Infinity, allowSelf: true }, // https://www.w3.org/TR/WGSL/#compound-assignment-sec + { ops: [ ',' ], maxPrec: Infinity, allowSelf: true } + ], + replace: { + // missing functions in WGSL + '^^': 'threejs_xor()', + 'mod()': 'threejs_mod()', + + // functions with different names in WGSL + 'dFdx()': 'dpdx()', + 'dFdy()': '- dpdy()', + 'inversesqrt()': 'inverseSqrt()', + 'bitcast()': 'bitcast()', + + // there are no prefix increments/decrements in WGSL + '++': 'post++', + 'pre++': 'post++', + '--': 'post--', + 'pre--': 'post--' + } + }; + + } + } export default WGSLNodeBuilder; From ada11ac3b6bc871587c36d74e9a5ed95d2ffff9d Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 01:43:31 +1100 Subject: [PATCH 2/7] Remove unused Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/nodes/core/NodeBuilder.js | 6 ------ .../jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js | 2 +- .../jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js | 2 +- examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 08e0b2bdae010..7c151babbd57d 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -957,12 +957,6 @@ class NodeBuilder { } - getFunctionOperator() { - - return null; - - } - flowChildNode( node, output = null ) { const previousFlow = this.flow; diff --git a/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js b/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js index 3e783f581be52..2ecf98a547a26 100644 --- a/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js +++ b/examples/jsm/renderers/webgl-legacy/nodes/GLSL1NodeBuilder.js @@ -1,4 +1,4 @@ -import { MathNode, GLSLNodeParser, NodeBuilder } from '../../../nodes/Nodes.js'; +import { GLSLNodeParser, NodeBuilder } from '../../../nodes/Nodes.js'; const precisionLib = { low: 'lowp', diff --git a/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js b/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js index bcd2bd5453fbd..b62bea4158022 100644 --- a/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js +++ b/examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js @@ -1,4 +1,4 @@ -import { defaultShaderStages, NodeFrame, MathNode, GLSLNodeParser, NodeBuilder, normalView } from '../../../nodes/Nodes.js'; +import { defaultShaderStages, NodeFrame, GLSLNodeParser, NodeBuilder, normalView } from '../../../nodes/Nodes.js'; import SlotNode from './SlotNode.js'; import { PerspectiveCamera, ShaderChunk, ShaderLib, UniformsUtils, UniformsLib } from 'three'; diff --git a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js index d3912b85f608b..4ecc7ecf1b837 100644 --- a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js +++ b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js @@ -1,4 +1,4 @@ -import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js'; +import { GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js'; import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js'; import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js'; From 4962e69a15ce6baa1dfb3f073559600c4938ba31 Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 02:09:49 +1100 Subject: [PATCH 3/7] Rerun CI Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> From 2dd25e20cab7c70d6dba5cbeb88679e3e23b66e7 Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 03:31:08 +1100 Subject: [PATCH 4/7] Fix SetNode Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/nodes/utils/SetNode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/jsm/nodes/utils/SetNode.js b/examples/jsm/nodes/utils/SetNode.js index c1a8f172c2b3e..98a90097228c1 100644 --- a/examples/jsm/nodes/utils/SetNode.js +++ b/examples/jsm/nodes/utils/SetNode.js @@ -45,13 +45,13 @@ class SetNode extends TempNode { } else { - snippetValues.push( sourceSnippet + '.' + component ); + snippetValues.push( builder.formatOperation( '.', sourceSnippet, component ) ); } } - return `${ builder.getType( sourceType ) }( ${ snippetValues.join( ', ' ) } )`; + return builder.formatOperation( '()', builder.getType( sourceType ), snippetValues.join( ', ' ) ); } From 6f9b750d91a2fa6075068ba32db289566a84cc53 Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 04:13:48 +1100 Subject: [PATCH 5/7] Some fixes for MathNode and OperatorNode Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/nodes/math/MathNode.js | 6 +----- examples/jsm/nodes/math/OperatorNode.js | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/jsm/nodes/math/MathNode.js b/examples/jsm/nodes/math/MathNode.js index 8c0119e7a79ce..4670221a71f17 100644 --- a/examples/jsm/nodes/math/MathNode.js +++ b/examples/jsm/nodes/math/MathNode.js @@ -57,14 +57,10 @@ class MathNode extends TempNode { return 'vec3'; - } else if ( method === MathNode.ALL ) { + } else if ( method === MathNode.ALL || method === MathNode.ANY ) { return 'bool'; - } else if ( method === MathNode.EQUALS ) { - - return builder.changeComponentType( this.aNode.getNodeType( builder ), 'bool' ); - } else if ( method === MathNode.MOD ) { return this.aNode.getNodeType( builder ); diff --git a/examples/jsm/nodes/math/OperatorNode.js b/examples/jsm/nodes/math/OperatorNode.js index 7c701629abdb3..1f136fe6e7e56 100644 --- a/examples/jsm/nodes/math/OperatorNode.js +++ b/examples/jsm/nodes/math/OperatorNode.js @@ -51,11 +51,11 @@ class OperatorNode extends TempNode { return builder.getIntegerType( typeA ); - } else if ( op === '!' || op === '==' || op === '&&' || op === '||' || op === '^^' ) { + } else if ( op === '!' || op === '&&' || op === '||' || op === '^^' ) { return 'bool'; - } else if ( op === '<' || op === '>' || op === '<=' || op === '>=' ) { + } else if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) { const typeLength = output ? builder.getTypeLength( output ) : Math.max( builder.getTypeLength( typeA ), builder.getTypeLength( typeB ) ); @@ -110,7 +110,7 @@ class OperatorNode extends TempNode { typeA = aNode.getNodeType( builder ); typeB = typeof bNode !== 'undefined' ? bNode.getNodeType( builder ) : null; - if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' ) { + if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) { if ( builder.isVector( typeA ) ) { From 7147fafc25977c683269815d12b864935f192a67 Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 04:40:26 +1100 Subject: [PATCH 6/7] Rerun CI Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> From 406a6cb371a6e2b5a21c44da6fbad690e8239ef8 Mon Sep 17 00:00:00 2001 From: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> Date: Thu, 21 Mar 2024 20:35:18 +1100 Subject: [PATCH 7/7] Simplify check Signed-off-by: Levi Pesin <35454228+LeviPesin@users.noreply.github.com> --- examples/jsm/nodes/core/NodeBuilder.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 7c151babbd57d..15e6cacbded8c 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -1444,25 +1444,17 @@ class NodeBuilder { if ( rhsOp.op !== '<' && rhsOp.op !== '>' && operators.includes( rhsOp.op + '=' ) ) { // use assignment statement op = rhsOp.op + '='; - arg2 = arg2.slice( rhsOp.end ).trimLeft(); + let newArg2 = arg2.slice( rhsOp.end ).trimLeft(); - if ( arg2.startsWith( '(' ) && arg2.endsWith( ')' ) ) { // RHS' second argument could be wrapped in brackets + if ( newArg2.startsWith( '(' ) && newArg2.endsWith( ')' ) // RHS' second argument could be wrapped in brackets + && arg2Parsed.find( arg => arg.start >= rhsOp.end ).end === arg2.length ) { // the brackets are indeed outmost, remove them - let i, diff = 0; - - for ( i = 0; i < arg2.length; i ++ ) { - - if ( arg2[ i ] === '(' ) diff ++; - if ( arg2[ i ] === ')' ) diff --; - - if ( diff === 0 ) break; - - } - - if ( i === arg2.length - 1 ) arg2 = arg2.slice( 1, - 1 ).trim(); // the brackets are indeed outmost, remove them + newArg2 = newArg2.slice( 1, - 1 ).trim(); } + arg2 = newArg2; + } }