@@ -1939,6 +1939,16 @@ class Node extends EventDispatcher {
1939
1939
1940
1940
}
1941
1941
1942
+ if ( result === '' && output !== null && output !== 'void' && output !== 'OutputType' ) {
1943
+
1944
+ // if no snippet is generated, return a default value
1945
+
1946
+ console.error( `THREE.TSL: Invalid generated code, expected a "${ output }".` );
1947
+
1948
+ result = builder.generateConst( output );
1949
+
1950
+ }
1951
+
1942
1952
}
1943
1953
1944
1954
builder.removeChain( this );
@@ -3528,25 +3538,57 @@ class ShaderCallNodeInternal extends Node {
3528
3538
let index = 0;
3529
3539
3530
3540
inputs = new Proxy( inputs, {
3541
+
3531
3542
get: ( target, property, receiver ) => {
3532
3543
3544
+ let value;
3545
+
3533
3546
if ( target[ property ] === undefined ) {
3534
3547
3535
- return target[ index ++ ];
3548
+ value = target[ index ++ ];
3536
3549
3537
3550
} else {
3538
3551
3539
- return Reflect.get( target, property, receiver );
3552
+ value = Reflect.get( target, property, receiver );
3540
3553
3541
3554
}
3542
3555
3556
+ return value;
3557
+
3543
3558
}
3559
+
3544
3560
} );
3545
3561
3546
3562
}
3547
3563
3564
+ const secureNodeBuilder = new Proxy( builder, {
3565
+
3566
+ get: ( target, property, receiver ) => {
3567
+
3568
+ let value;
3569
+
3570
+ if ( Symbol.iterator === property ) {
3571
+
3572
+ value = function* () {
3573
+
3574
+ yield undefined;
3575
+
3576
+ };
3577
+
3578
+ } else {
3579
+
3580
+ value = Reflect.get( target, property, receiver );
3581
+
3582
+ }
3583
+
3584
+ return value;
3585
+
3586
+ }
3587
+
3588
+ } );
3589
+
3548
3590
const jsFunc = shaderNode.jsFunc;
3549
- const outputNode = inputs !== null || jsFunc.length > 1 ? jsFunc( inputs || [], builder ) : jsFunc( builder );
3591
+ const outputNode = inputs !== null || jsFunc.length > 1 ? jsFunc( inputs || [], secureNodeBuilder ) : jsFunc( secureNodeBuilder );
3550
3592
3551
3593
result = nodeObject( outputNode );
3552
3594
@@ -3732,6 +3774,18 @@ const ConvertType = function ( type, cacheMap = null ) {
3732
3774
3733
3775
return ( ...params ) => {
3734
3776
3777
+ for ( const param of params ) {
3778
+
3779
+ if ( param === undefined ) {
3780
+
3781
+ console.error( `THREE.TSL: Invalid parameter for the type "${ type }".` );
3782
+
3783
+ return nodeObject( new ConstNode( 0, type ) );
3784
+
3785
+ }
3786
+
3787
+ }
3788
+
3735
3789
if ( params.length === 0 || ( ! [ 'bool', 'float', 'int', 'uint' ].includes( type ) && params.every( param => {
3736
3790
3737
3791
const paramType = typeof param;
@@ -3913,7 +3967,7 @@ class FnNode extends Node {
3913
3967
3914
3968
const type = this.getNodeType( builder );
3915
3969
3916
- console.warn ( 'THREE.TSL: "Fn()" was declared but not invoked. Try calling it like "Fn()( ...params )".' );
3970
+ console.error ( 'THREE.TSL: "Fn()" was declared but not invoked. Try calling it like "Fn()( ...params )".' );
3917
3971
3918
3972
return builder.generateConst( type );
3919
3973
@@ -4780,16 +4834,24 @@ class UniformNode extends InputNode {
4780
4834
*
4781
4835
* @tsl
4782
4836
* @function
4783
- * @param {any} arg1 - The value of this node . Usually a JS primitive or three.js object (vector, matrix, color, texture).
4784
- * @param {string} [arg2 ] - The node type. If no explicit type is defined, the node tries to derive the type from its value.
4837
+ * @param {any|string} value - The value of this uniform or your type . Usually a JS primitive or three.js object (vector, matrix, color, texture).
4838
+ * @param {string} [type ] - The node type. If no explicit type is defined, the node tries to derive the type from its value.
4785
4839
* @returns {UniformNode}
4786
4840
*/
4787
- const uniform = ( arg1, arg2 ) => {
4841
+ const uniform = ( value, type ) => {
4842
+
4843
+ const nodeType = getConstNodeType( type || value );
4844
+
4845
+ if ( nodeType === value ) {
4846
+
4847
+ // if the value is a type but no having a value
4788
4848
4789
- const nodeType = getConstNodeType( arg2 || arg1 );
4849
+ value = getValueFromType( nodeType );
4850
+
4851
+ }
4790
4852
4791
4853
// @TODO: get ConstNode from .traverse() in the future
4792
- const value = ( arg1 && arg1 .isNode === true ) ? ( arg1 .node && arg1 .node.value ) || arg1 .value : arg1 ;
4854
+ value = ( value && value .isNode === true ) ? ( value .node && value .node.value ) || value .value : value ;
4793
4855
4794
4856
return nodeObject( new UniformNode( value, nodeType ) );
4795
4857
@@ -5055,11 +5117,10 @@ class AssignNode extends TempNode {
5055
5117
5056
5118
const needsSplitAssign = this.needsSplitAssign( builder );
5057
5119
5120
+ const target = targetNode.build( builder );
5058
5121
const targetType = targetNode.getNodeType( builder );
5059
5122
5060
- const target = targetNode.build( builder );
5061
5123
const source = sourceNode.build( builder, targetType );
5062
-
5063
5124
const sourceType = sourceNode.getNodeType( builder );
5064
5125
5065
5126
const nodeData = builder.getDataFromNode( this );
@@ -7309,10 +7370,12 @@ class ConditionalNode extends Node {
7309
7370
7310
7371
//
7311
7372
7373
+ const isUniformFlow = builder.context.uniformFlow;
7374
+
7312
7375
const properties = builder.getNodeProperties( this );
7313
7376
properties.condNode = condNode;
7314
- properties.ifNode = ifNode.context( { nodeBlock: ifNode } );
7315
- properties.elseNode = elseNode ? elseNode.context( { nodeBlock: elseNode } ) : null;
7377
+ properties.ifNode = isUniformFlow ? ifNode : ifNode.context( { nodeBlock: ifNode } );
7378
+ properties.elseNode = elseNode ? ( isUniformFlow ? elseNode : elseNode .context( { nodeBlock: elseNode } ) ) : null;
7316
7379
7317
7380
}
7318
7381
@@ -7337,6 +7400,20 @@ class ConditionalNode extends Node {
7337
7400
nodeData.nodeProperty = nodeProperty;
7338
7401
7339
7402
const nodeSnippet = condNode.build( builder, 'bool' );
7403
+ const isUniformFlow = builder.context.uniformFlow;
7404
+
7405
+ if ( isUniformFlow && elseNode !== null ) {
7406
+
7407
+ const ifSnippet = ifNode.build( builder, type );
7408
+ const elseSnippet = elseNode.build( builder, type );
7409
+
7410
+ const mathSnippet = builder.getTernary( nodeSnippet, ifSnippet, elseSnippet );
7411
+
7412
+ // TODO: If node property already exists return something else
7413
+
7414
+ return builder.format( mathSnippet, type, output );
7415
+
7416
+ }
7340
7417
7341
7418
builder.addFlowCode( `\n${ builder.tab }if ( ${ nodeSnippet } ) {\n\n` ).addFlowTab();
7342
7419
@@ -7550,6 +7627,16 @@ class ContextNode extends Node {
7550
7627
*/
7551
7628
const context = /*@__PURE__*/ nodeProxy( ContextNode ).setParameterLength( 1, 2 );
7552
7629
7630
+ /**
7631
+ * TSL function for defining a uniformFlow context value for a given node.
7632
+ *
7633
+ * @tsl
7634
+ * @function
7635
+ * @param {Node} node - The node whose dependencies should all execute within a uniform control-flow path.
7636
+ * @returns {ContextNode}
7637
+ */
7638
+ const uniformFlow = ( node ) => context( node, { uniformFlow: true } );
7639
+
7553
7640
/**
7554
7641
* TSL function for defining a name for the context value for a given node.
7555
7642
*
@@ -7581,6 +7668,7 @@ function label( node, name ) {
7581
7668
7582
7669
addMethodChaining( 'context', context );
7583
7670
addMethodChaining( 'label', label );
7671
+ addMethodChaining( 'uniformFlow', uniformFlow );
7584
7672
addMethodChaining( 'setName', setName );
7585
7673
7586
7674
/**
@@ -30936,13 +31024,13 @@ class StackNode extends Node {
30936
31024
30937
31025
getNodeType( builder ) {
30938
31026
30939
- return this.outputNode ? this.outputNode.getNodeType( builder ) : 'void';
31027
+ return this.hasOutput ? this.outputNode.getNodeType( builder ) : 'void';
30940
31028
30941
31029
}
30942
31030
30943
31031
getMemberType( builder, name ) {
30944
31032
30945
- return this.outputNode ? this.outputNode.getMemberType( builder, name ) : 'void';
31033
+ return this.hasOutput ? this.outputNode.getMemberType( builder, name ) : 'void';
30946
31034
30947
31035
}
30948
31036
@@ -30954,6 +31042,13 @@ class StackNode extends Node {
30954
31042
*/
30955
31043
add( node ) {
30956
31044
31045
+ if ( node.isNode !== true ) {
31046
+
31047
+ console.error( 'THREE.TSL: Invalid node added to stack.' );
31048
+ return this;
31049
+
31050
+ }
31051
+
30957
31052
this.nodes.push( node );
30958
31053
30959
31054
return this;
@@ -31047,7 +31142,7 @@ class StackNode extends Node {
31047
31142
31048
31143
} else {
31049
31144
31050
- throw new Error ( 'TSL: Invalid parameter length. Case() requires at least two parameters.' );
31145
+ console.error ( 'THREE. TSL: Invalid parameter length. Case() requires at least two parameters.' );
31051
31146
31052
31147
}
31053
31148
@@ -31131,6 +31226,12 @@ class StackNode extends Node {
31131
31226
31132
31227
}
31133
31228
31229
+ get hasOutput() {
31230
+
31231
+ return this.outputNode && this.outputNode.isNode;
31232
+
31233
+ }
31234
+
31134
31235
build( builder, ...params ) {
31135
31236
31136
31237
const previousBuildStack = builder.currentStack;
@@ -31181,7 +31282,19 @@ class StackNode extends Node {
31181
31282
31182
31283
}
31183
31284
31184
- const result = this.outputNode ? this.outputNode.build( builder, ...params ) : super.build( builder, ...params );
31285
+ //
31286
+
31287
+ let result;
31288
+
31289
+ if ( this.hasOutput ) {
31290
+
31291
+ result = this.outputNode.build( builder, ...params );
31292
+
31293
+ } else {
31294
+
31295
+ result = super.build( builder, ...params );
31296
+
31297
+ }
31185
31298
31186
31299
setCurrentStack( previousStack );
31187
31300
@@ -43380,6 +43493,7 @@ var TSL = /*#__PURE__*/Object.freeze({
43380
43493
uniform: uniform,
43381
43494
uniformArray: uniformArray,
43382
43495
uniformCubeTexture: uniformCubeTexture,
43496
+ uniformFlow: uniformFlow,
43383
43497
uniformGroup: uniformGroup,
43384
43498
uniformTexture: uniformTexture,
43385
43499
unpremultiplyAlpha: unpremultiplyAlpha,
@@ -45781,6 +45895,22 @@ class NodeBuilder {
45781
45895
45782
45896
}
45783
45897
45898
+ /**
45899
+ * Returns the native snippet for a ternary operation. E.g. GLSL would output
45900
+ * a ternary op as `cond ? x : y` whereas WGSL would output it as `select(y, x, cond)`
45901
+ *
45902
+ * @abstract
45903
+ * @param {string} condSnippet - The condition determining which expression gets resolved.
45904
+ * @param {string} ifSnippet - The expression to resolve to if the condition is true.
45905
+ * @param {string} elseSnippet - The expression to resolve to if the condition is false.
45906
+ * @return {string} The resolved method name.
45907
+ */
45908
+ getTernary( /* condSnippet, ifSnippet, elseSnippet*/ ) {
45909
+
45910
+ return null;
45911
+
45912
+ }
45913
+
45784
45914
/**
45785
45915
* Returns a node for the given hash, see {@link NodeBuilder#setHashNode}.
45786
45916
*
@@ -46097,7 +46227,6 @@ class NodeBuilder {
46097
46227
46098
46228
}
46099
46229
46100
-
46101
46230
/**
46102
46231
* Generates the shader string for the given type and value.
46103
46232
*
@@ -47876,11 +48005,6 @@ class NodeBuilder {
47876
48005
47877
48006
}
47878
48007
47879
- /**
47880
- * Prevents the node builder from being used as an iterable in TSL.Fn(), avoiding potential runtime errors.
47881
- */
47882
- *[ Symbol.iterator ]() { }
47883
-
47884
48008
}
47885
48009
47886
48010
/**
@@ -56502,6 +56626,20 @@ class GLSLNodeBuilder extends NodeBuilder {
56502
56626
56503
56627
}
56504
56628
56629
+ /**
56630
+ * Returns the native snippet for a ternary operation.
56631
+ *
56632
+ * @param {string} condSnippet - The condition determining which expression gets resolved.
56633
+ * @param {string} ifSnippet - The expression to resolve to if the condition is true.
56634
+ * @param {string} elseSnippet - The expression to resolve to if the condition is false.
56635
+ * @return {string} The resolved method name.
56636
+ */
56637
+ getTernary( condSnippet, ifSnippet, elseSnippet ) {
56638
+
56639
+ return `${condSnippet} ? ${ifSnippet} : ${elseSnippet}`;
56640
+
56641
+ }
56642
+
56505
56643
/**
56506
56644
* Returns the output struct name. Not relevant for GLSL.
56507
56645
*
@@ -69029,6 +69167,21 @@ ${ flowData.code }
69029
69167
69030
69168
}
69031
69169
69170
+ /**
69171
+ * Returns the native snippet for a ternary operation.
69172
+ *
69173
+ * @param {string} condSnippet - The condition determining which expression gets resolved.
69174
+ * @param {string} ifSnippet - The expression to resolve to if the condition is true.
69175
+ * @param {string} elseSnippet - The expression to resolve to if the condition is false.
69176
+ * @return {string} The resolved method name.
69177
+ */
69178
+ getTernary( condSnippet, ifSnippet, elseSnippet ) {
69179
+
69180
+ return `select( ${elseSnippet}, ${ifSnippet}, ${condSnippet} )`;
69181
+
69182
+ }
69183
+
69184
+
69032
69185
/**
69033
69186
* Returns the WGSL type of the given node data type.
69034
69187
*
0 commit comments