diff --git a/examples/example.css b/examples/example.css
index e7e2093563cb9e..8640f424d00d5a 100644
--- a/examples/example.css
+++ b/examples/example.css
@@ -28,7 +28,7 @@ a {
pointer-events: none;
}
-#info > a {
+#info a {
pointer-events: auto;
}
diff --git a/examples/jsm/transpiler/AST.js b/examples/jsm/transpiler/AST.js
index 549c116c0b74b0..c7d52f92374c4c 100644
--- a/examples/jsm/transpiler/AST.js
+++ b/examples/jsm/transpiler/AST.js
@@ -612,12 +612,13 @@ export class For extends ASTNode {
export class While extends ASTNode {
- constructor( condition, body = [] ) {
+ constructor( condition, body = [], doWhile = false ) {
super();
this.condition = condition;
this.body = body;
+ this.doWhile = doWhile;
this.isWhile = true;
diff --git a/examples/jsm/transpiler/GLSLDecoder.js b/examples/jsm/transpiler/GLSLDecoder.js
index 3810eb90a4b718..0df82098c4c832 100644
--- a/examples/jsm/transpiler/GLSLDecoder.js
+++ b/examples/jsm/transpiler/GLSLDecoder.js
@@ -921,6 +921,45 @@ class GLSLDecoder {
}
+ parseDoWhile() {
+
+ this.readToken(); // skip 'do'
+
+ let body;
+
+ if ( this.getToken().str === '{' ) {
+
+ body = this.parseBlock();
+
+ } else {
+
+ body = [ this.parseExpression() ];
+
+ }
+
+ if ( this.getToken() && this.getToken().str === 'while' ) {
+
+ this.readToken(); // skip 'while'
+
+ } else {
+
+ throw new Error( 'THREE.GLSLDecoder: Expected \'while\' after \'do\' block' + this.getTokenPosition( this.getToken() ) );
+
+ }
+
+ const condTokens = this.readTokensUntil( ')' );
+ const condition = this.parseExpressionFromTokens( condTokens.slice( 1, - 1 ) );
+
+ if ( this.getToken() && this.getToken().str === ';' ) {
+
+ this.readToken(); // skip trailing ';'
+
+ }
+
+ return new While( condition, body, true );
+
+ }
+
parseFor() {
this.readToken(); // skip 'for'
@@ -1219,6 +1258,10 @@ class GLSLDecoder {
statement = this.parseWhile();
+ } else if ( token.str === 'do' ) {
+
+ statement = this.parseDoWhile();
+
} else if ( token.str === 'switch' ) {
statement = this.parseSwitch();
diff --git a/examples/jsm/transpiler/TSLEncoder.js b/examples/jsm/transpiler/TSLEncoder.js
index 1f977828df8346..44233bfdffb0f0 100644
--- a/examples/jsm/transpiler/TSLEncoder.js
+++ b/examples/jsm/transpiler/TSLEncoder.js
@@ -481,6 +481,38 @@ ${ this.tab }} )`;
}
+ isVariableUsed( node, varName ) {
+
+ if ( ! node ) return false;
+
+ if ( node.isAccessor && node.property === varName ) return true;
+
+ for ( const key in node ) {
+
+ if ( key === 'parent' ) continue;
+
+ const child = node[ key ];
+
+ if ( Array.isArray( child ) ) {
+
+ for ( const item of child ) {
+
+ if ( this.isVariableUsed( item, varName ) ) return true;
+
+ }
+
+ } else if ( child && child.isASTNode ) {
+
+ if ( this.isVariableUsed( child, varName ) ) return true;
+
+ }
+
+ }
+
+ return false;
+
+ }
+
emitLoop( node ) {
const start = this.emitExpression( node.initialization.value );
@@ -518,7 +550,22 @@ ${ this.tab }} )`;
}
- let loopStr = `Loop( { start: ${ start }, end: ${ end + nameParam + typeParam + conditionParam + updateParam } }, ( { ${ name } } ) => {\n\n`;
+ let loopParams;
+
+ if ( start === '0' && nameParam === '' && typeParam === '' && conditionParam === '' && updateParam === '' ) {
+
+ loopParams = end;
+
+ } else {
+
+ loopParams = `{ start: ${ start }, end: ${ end + nameParam + typeParam + conditionParam + updateParam } }`;
+
+ }
+
+ const isUsed = this.isVariableUsed( node.body, name );
+ const callbackParams = isUsed ? `( { ${ name } } ) =>` : '() =>';
+
+ let loopStr = `Loop( ${ loopParams }, ${ callbackParams } {\n\n`;
loopStr += this.emitBody( node.body ) + '\n\n';
@@ -634,8 +681,31 @@ ${ this.tab }} )`;
}
+ emitDoWhile( node ) {
+
+ const condition = this.emitExpression( node.condition );
+ const body = this.emitBody( node.body );
+
+ let loopStr = `Loop( () => {\n\n${ body }\n\n${ this.tab }\tIf( ${ condition }.not(), () => {\n\n${ this.tab }\t\tBreak();\n\n${ this.tab }\t} );\n\n`;
+
+ loopStr += this.tab + '} )';
+
+ this.imports.add( 'Loop' );
+ this.imports.add( 'If' );
+ this.imports.add( 'Break' );
+
+ return loopStr;
+
+ }
+
emitWhile( node ) {
+ if ( node.doWhile ) {
+
+ return this.emitDoWhile( node );
+
+ }
+
const condition = this.emitExpression( node.condition );
let whileStr = `Loop( ${ condition }, () => {\n\n`;
diff --git a/examples/jsm/transpiler/WGSLEncoder.js b/examples/jsm/transpiler/WGSLEncoder.js
index 7995860711eca7..5738f1e580c0e6 100644
--- a/examples/jsm/transpiler/WGSLEncoder.js
+++ b/examples/jsm/transpiler/WGSLEncoder.js
@@ -509,6 +509,12 @@ class WGSLEncoder {
const cond = this.emitExpression( node.condition );
const body = this.emitBody( node.body );
+ if ( node.doWhile ) {
+
+ return `loop {\n\n${ body }\n\n${ this.tab }\tcontinuing {\n${ this.tab }\t\tbreak if ( ! ( ${ cond } ) );\n${ this.tab }\t}\n${ this.tab }}`;
+
+ }
+
return `while ( ${ cond } ) {\n\n${ body }\n\n${ this.tab }}`;
}
diff --git a/examples/webgpu_gaussian_splatting.html b/examples/webgpu_gaussian_splatting.html
index da5d78ee29713f..785fae52dc4208 100644
--- a/examples/webgpu_gaussian_splatting.html
+++ b/examples/webgpu_gaussian_splatting.html
@@ -24,7 +24,7 @@
three.jsGaussian Splatting
- WebGPU TSL Gaussian splat renderer by Ben Houston.
+ WebGPU TSL Gaussian splat renderer by Ben Houston.
diff --git a/src/nodes/utils/LoopNode.js b/src/nodes/utils/LoopNode.js
index 5f3a5906c50eda..451345b6db00ef 100644
--- a/src/nodes/utils/LoopNode.js
+++ b/src/nodes/utils/LoopNode.js
@@ -1,6 +1,6 @@
import Node from '../core/Node.js';
import { expression } from '../code/ExpressionNode.js';
-import { nodeArray, Fn } from '../tsl/TSLBase.js';
+import { nodeArray, Fn, bool } from '../tsl/TSLBase.js';
import { error } from '../../utils.js';
/**
@@ -88,10 +88,11 @@ class LoopNode extends Node {
//
const inputs = {};
+ const params = this._getInternalParams();
- for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
+ for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
- const param = this.params[ i ];
+ const param = params[ i ];
const name = ( param.isNode !== true && param.name ) || this.getVarName( i );
const type = ( param.isNode !== true && param.type ) || 'int';
@@ -102,16 +103,16 @@ class LoopNode extends Node {
const stack = builder.addStack();
- const fnCall = this.params[ this.params.length - 1 ]( inputs );
+ const fnCall = params[ params.length - 1 ]( inputs );
properties.returnsNode = fnCall.context( { nodeLoop: fnCall } );
properties.stackNode = stack;
- const baseParam = this.params[ 0 ];
+ const baseParam = params[ 0 ];
if ( baseParam.isNode !== true && typeof baseParam.update === 'function' ) {
- const fnUpdateCall = Fn( this.params[ 0 ].update )( inputs );
+ const fnUpdateCall = Fn( baseParam.update )( inputs );
properties.updateNode = fnUpdateCall.context( { nodeLoop: fnUpdateCall } );
@@ -123,6 +124,20 @@ class LoopNode extends Node {
}
+ _getInternalParams() {
+
+ const params = this.params;
+
+ if ( typeof params[ 0 ] === 'function' ) {
+
+ return [ bool( true ), params[ 0 ] ];
+
+ }
+
+ return params;
+
+ }
+
setup( builder ) {
// setup properties
@@ -142,7 +157,7 @@ class LoopNode extends Node {
const properties = this.getProperties( builder );
- const params = this.params;
+ const params = this._getInternalParams();
const stackNode = properties.stackNode;
for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
@@ -306,7 +321,7 @@ class LoopNode extends Node {
builder.removeFlowTab().addFlowCode( '\n' + builder.tab + stackSnippet );
- for ( let i = 0, l = this.params.length - 1; i < l; i ++ ) {
+ for ( let i = 0, l = params.length - 1; i < l; i ++ ) {
builder.addFlowCode( ( i === 0 ? '' : builder.tab ) + '}\n\n' ).removeFlowTab();