Skip to content

Commit 1f5fdda

Browse files
sunagRuthySheffi
authored andcommitted
TSL: Add expression support for loop( { update: ... } ) (mrdoob#30947)
* add expression support for `loop()` update * Raymarching: update with new approach * TSLEncoder: update with new approach * rev * rev
1 parent 792f652 commit 1f5fdda

File tree

3 files changed

+87
-18
lines changed

3 files changed

+87
-18
lines changed

examples/jsm/transpiler/TSLEncoder.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,12 +466,34 @@ ${ this.tab }} )`;
466466
const name = node.initialization.name;
467467
const type = node.initialization.type;
468468
const condition = node.condition.type;
469-
const update = node.afterthought.type;
470469

471470
const nameParam = name !== 'i' ? `, name: '${ name }'` : '';
472471
const typeParam = type !== 'int' ? `, type: '${ type }'` : '';
473472
const conditionParam = condition !== '<' ? `, condition: '${ condition }'` : '';
474-
const updateParam = update !== '++' ? `, update: '${ update }'` : '';
473+
474+
let updateParam = '';
475+
476+
if ( node.afterthought.isUnary ) {
477+
478+
if ( node.afterthought.type !== '++' ) {
479+
480+
updateParam = `, update: '${ node.afterthought.type }'`;
481+
482+
}
483+
484+
} else if ( node.afterthought.isOperator ) {
485+
486+
if ( node.afterthought.right.isAccessor || node.afterthought.right.isNumber ) {
487+
488+
updateParam = `, update: ${ this.emitExpression( node.afterthought.right ) }`;
489+
490+
} else {
491+
492+
updateParam = `, update: ( { i } ) => ${ this.emitExpression( node.afterthought ) }`;
493+
494+
}
495+
496+
}
475497

476498
let loopStr = `Loop( { start: ${ start }, end: ${ end + nameParam + typeParam + conditionParam + updateParam } }, ( { ${ name } } ) => {\n\n`;
477499

examples/jsm/tsl/utils/Raymarching.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ export const RaymarchingBox = ( steps, callback ) => {
5353
bounds.assign( vec2( max( bounds.x, 0.0 ), bounds.y ) );
5454

5555
const inc = vec3( rayDir.abs().reciprocal() ).toVar();
56-
const delta = float( min( inc.x, min( inc.y, inc.z ) ) ).toVar( 'rayDelta' ); // used 'rayDelta' name in loop
56+
const delta = float( min( inc.x, min( inc.y, inc.z ) ) ).toVar();
5757

5858
delta.divAssign( float( steps ) );
5959

6060
const positionRay = vec3( vOrigin.add( bounds.x.mul( rayDir ) ) ).toVar();
6161

62-
Loop( { type: 'float', start: bounds.x, end: bounds.y, update: '+= rayDelta' }, () => {
62+
Loop( { type: 'float', start: bounds.x, end: bounds.y, update: delta }, () => {
6363

6464
callback( { positionRay } );
6565

src/nodes/utils/LoopNode.js

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Node from '../core/Node.js';
22
import { expression } from '../code/ExpressionNode.js';
3-
import { nodeObject, nodeArray } from '../tsl/TSLBase.js';
3+
import { nodeObject, nodeArray, Fn } from '../tsl/TSLBase.js';
44

55
/**
66
* This module offers a variety of ways to implement loops in TSL. In it's basic form it's:
@@ -101,9 +101,17 @@ class LoopNode extends Node {
101101

102102
const stack = builder.addStack(); // TODO: cache() it
103103

104-
properties.returnsNode = this.params[ this.params.length - 1 ]( inputs, stack, builder );
104+
properties.returnsNode = this.params[ this.params.length - 1 ]( inputs, builder );
105105
properties.stackNode = stack;
106106

107+
const baseParam = this.params[ 0 ];
108+
109+
if ( baseParam.isNode !== true && typeof baseParam.update === 'function' ) {
110+
111+
properties.updateNode = Fn( this.params[ 0 ].update )( inputs );
112+
113+
}
114+
107115
builder.removeStack();
108116

109117
return properties;
@@ -222,30 +230,69 @@ class LoopNode extends Node {
222230
const startSnippet = internalParam.start;
223231
const endSnippet = internalParam.end;
224232

225-
let declarationSnippet = '';
226-
let conditionalSnippet = '';
227-
let updateSnippet = '';
233+
let updateSnippet;
234+
235+
const deltaOperator = () => condition.includes( '<' ) ? '+=' : '-=';
236+
237+
if ( update !== undefined && update !== null ) {
238+
239+
switch ( typeof update ) {
240+
241+
case 'function':
242+
243+
const flow = builder.flowStagesNode( properties.updateNode, 'void' );
244+
const snippet = flow.code.replace( /\t|;/g, '' );
245+
246+
updateSnippet = snippet;
247+
248+
break;
249+
250+
case 'number':
251+
252+
updateSnippet = name + ' ' + deltaOperator() + ' ' + builder.generateConst( type, update );
253+
254+
break;
228255

229-
if ( ! update ) {
256+
case 'string':
257+
258+
updateSnippet = name + ' ' + update;
259+
260+
break;
261+
262+
default:
263+
264+
if ( update.isNode ) {
265+
266+
updateSnippet = name + ' ' + deltaOperator() + ' ' + update.build( builder );
267+
268+
} else {
269+
270+
console.error( 'THREE.TSL: \'Loop( { update: ... } )\' is not a function, string or number.' );
271+
272+
updateSnippet = 'break /* invalid update */';
273+
274+
}
275+
276+
}
277+
278+
} else {
230279

231280
if ( type === 'int' || type === 'uint' ) {
232281

233-
if ( condition.includes( '<' ) ) update = '++';
234-
else update = '--';
282+
update = condition.includes( '<' ) ? '++' : '--';
235283

236284
} else {
237285

238-
if ( condition.includes( '<' ) ) update = '+= 1.';
239-
else update = '-= 1.';
286+
update = deltaOperator() + ' 1.';
240287

241288
}
242289

243-
}
290+
updateSnippet = name + ' ' + update;
244291

245-
declarationSnippet += builder.getVar( type, name ) + ' = ' + startSnippet;
292+
}
246293

247-
conditionalSnippet += name + ' ' + condition + ' ' + endSnippet;
248-
updateSnippet += name + ' ' + update;
294+
const declarationSnippet = builder.getVar( type, name ) + ' = ' + startSnippet;
295+
const conditionalSnippet = name + ' ' + condition + ' ' + endSnippet;
249296

250297
loopSnippet = `for ( ${ declarationSnippet }; ${ conditionalSnippet }; ${ updateSnippet } )`;
251298

0 commit comments

Comments
 (0)