Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nodes: Allow JS-like assigns, implement AssignNode #26795

Merged
merged 14 commits into from Oct 8, 2023
3 changes: 2 additions & 1 deletion examples/jsm/nodes/Nodes.js
Expand Up @@ -6,6 +6,7 @@ export * from './core/constants.js';

// core
export { default as ArrayUniformNode /* @TODO: arrayUniform */ } from './core/ArrayUniformNode.js';
export { default as AssignNode } from './core/AssignNode.js';
export { default as AttributeNode, attribute } from './core/AttributeNode.js';
export { default as BypassNode, bypass } from './core/BypassNode.js';
export { default as CacheNode, cache } from './core/CacheNode.js';
Expand Down Expand Up @@ -37,7 +38,7 @@ export { NodeUtils };

// math
export { default as MathNode, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward } from './math/MathNode.js';
export { default as OperatorNode, add, sub, mul, div, remainder, equal, assign, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, xor, bitAnd, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js';
export { default as OperatorNode, add, sub, mul, div, remainder, equal, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, xor, bitAnd, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js';
export { default as CondNode, cond } from './math/CondNode.js';
export { default as HashNode, hash } from './math/HashNode.js';

Expand Down
64 changes: 64 additions & 0 deletions examples/jsm/nodes/core/AssignNode.js
@@ -0,0 +1,64 @@
import { addNodeClass } from '../core/Node.js';
import TempNode from '../core/TempNode.js';

class AssignNode extends TempNode {

constructor( aNode, bNode ) {

super();

this.aNode = aNode;
this.bNode = bNode;

}

hasDependencies( builder ) {

return false;

}

getNodeType( builder, output ) {

const aNode = this.aNode;
const bNode = this.bNode;

const typeA = aNode.getNodeType( builder );
const typeB = bNode.getNodeType( builder );

return typeB === 'void' ? 'void' : typeA;

}

generate( builder, output ) {

const op = this.op;
Fixed Show fixed Hide fixed

const aNode = this.aNode;
const bNode = this.bNode;

const type = this.getNodeType( builder, output );

const a = aNode.build( builder, type );
const b = bNode.build( builder, type );

const outputLength = builder.getTypeLength( output );
Fixed Show fixed Hide fixed

if ( output !== 'void' ) {

builder.addLineFlowCode( `${a} = ${b}` );
return a;

} else if ( type !== 'void' ) {

return builder.format( `${a} = ${b}`, type, output );

}

}

}

export default AssignNode;

addNodeClass( AssignNode );
3 changes: 1 addition & 2 deletions examples/jsm/nodes/core/StackNode.js
@@ -1,10 +1,9 @@
import Node, { addNodeClass } from './Node.js';
import { assign } from '../math/OperatorNode.js';
import { bypass } from '../core/BypassNode.js';
import { expression } from '../code/ExpressionNode.js';
import { cond } from '../math/CondNode.js';
import { loop } from '../utils/LoopNode.js';
import { ShaderNode, nodeProxy } from '../shadernode/ShaderNode.js';
import { ShaderNode, nodeProxy, assign } from '../shadernode/ShaderNode.js';

class StackNode extends Node {

Expand Down
24 changes: 3 additions & 21 deletions examples/jsm/nodes/math/OperatorNode.js
Expand Up @@ -29,12 +29,6 @@ class OperatorNode extends TempNode {

}

hasDependencies( builder ) {

return this.op !== '=' ? super.hasDependencies( builder ) : false;

}

getNodeType( builder, output ) {

const op = this.op;
Expand All @@ -49,7 +43,7 @@ class OperatorNode extends TempNode {

return 'void';

} else if ( op === '=' || op === '%' ) {
} else if ( op === '%' ) {

return typeA;

Expand Down Expand Up @@ -116,11 +110,7 @@ class OperatorNode extends TempNode {
typeA = aNode.getNodeType( builder );
typeB = bNode.getNodeType( builder );

if ( op === '=' ) {

typeB = typeA;

} else if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' ) {
if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' ) {

if ( builder.isVector( typeA ) ) {

Expand Down Expand Up @@ -170,13 +160,7 @@ class OperatorNode extends TempNode {

if ( output !== 'void' ) {

if ( op === '=' ) {

builder.addLineFlowCode( `${a} ${this.op} ${b}` );

return a;

} else if ( op === '<' && outputLength > 1 ) {
if ( op === '<' && outputLength > 1 ) {

return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${a}, ${b} )`, type, output );

Expand Down Expand Up @@ -232,7 +216,6 @@ export const mul = nodeProxy( OperatorNode, '*' );
export const div = nodeProxy( OperatorNode, '/' );
export const remainder = nodeProxy( OperatorNode, '%' );
export const equal = nodeProxy( OperatorNode, '==' );
export const assign = nodeProxy( OperatorNode, '=' );
export const lessThan = nodeProxy( OperatorNode, '<' );
export const greaterThan = nodeProxy( OperatorNode, '>' );
export const lessThanEqual = nodeProxy( OperatorNode, '<=' );
Expand All @@ -252,7 +235,6 @@ addNodeElement( 'mul', mul );
addNodeElement( 'div', div );
addNodeElement( 'remainder', remainder );
addNodeElement( 'equal', equal );
addNodeElement( 'assign', assign );
addNodeElement( 'lessThan', lessThan );
addNodeElement( 'greaterThan', greaterThan );
addNodeElement( 'lessThanEqual', lessThanEqual );
Expand Down
33 changes: 28 additions & 5 deletions examples/jsm/nodes/shadernode/ShaderNode.js
Expand Up @@ -3,6 +3,7 @@ import ArrayElementNode from '../utils/ArrayElementNode.js';
import ConvertNode from '../utils/ConvertNode.js';
import JoinNode from '../utils/JoinNode.js';
import SplitNode from '../utils/SplitNode.js';
import AssignNode from '../core/AssignNode.js';
import ConstNode from '../core/ConstNode.js';
import { getValueFromType, getValueType } from '../core/NodeUtils.js';

Expand All @@ -27,7 +28,7 @@ const shaderNodeHandler = {

},

get: function ( node, prop, nodeObj ) {
get( node, prop, nodeObj ) {

if ( typeof prop === 'string' && node[ prop ] === undefined ) {

Expand Down Expand Up @@ -57,25 +58,45 @@ const shaderNodeHandler = {
.replace( /b|p/g, 'z' )
.replace( /a|q/g, 'w' );

return nodeObject( new SplitNode( node, prop ) );
return nodeObject( new SplitNode( nodeObj, prop ) );

} else if ( prop === 'width' || prop === 'height' ) {

// accessing property

return nodeObject( new SplitNode( node, prop === 'width' ? 'x' : 'y' ) );
return nodeObject( new SplitNode( nodeObj, prop === 'width' ? 'x' : 'y' ) );

} else if ( /^\d+$/.test( prop ) === true ) {

// accessing array

return nodeObject( new ArrayElementNode( node, new ConstNode( Number( prop ), 'uint' ) ) );
return nodeObject( new ArrayElementNode( nodeObj, new ConstNode( Number( prop ), 'uint' ) ) );

}

}

return node[ prop ];
return Reflect.get( node, prop, nodeObj );

},

set( node, prop, value, nodeObj ) {

if ( typeof prop === 'string' && node[ prop ] === undefined && node.type === 'VarNode' ) {

// setting properties

if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true || prop === 'width' || prop === 'height' || /^\d+$/.test( prop ) === true ) {

nodeObj.assign( nodeObj[ prop ].assign( value ) );

return true;

}

}

return Reflect.set( node, prop, value, nodeObj );

}

Expand Down Expand Up @@ -417,8 +438,10 @@ addNodeElement( 'arrayBuffer', arrayBuffer );
// basic nodes
// HACK - we cannot export them from the corresponding files because of the cyclic dependency
export const element = nodeProxy( ArrayElementNode );
export const assign = nodeProxy( AssignNode );
export const convert = ( node, types ) => nodeObject( new ConvertNode( nodeObject( node ), types ) );
export const split = ( node, channels ) => nodeObject( new SplitNode( nodeObject( node ), channels ) );

addNodeElement( 'element', element );
addNodeElement( 'assign', assign );
addNodeElement( 'convert', convert );
8 changes: 8 additions & 0 deletions examples/jsm/nodes/utils/ArrayElementNode.js
@@ -1,4 +1,5 @@
import Node, { addNodeClass } from '../core/Node.js';
import AssignNode from '../core/AssignNode.js';

class ArrayElementNode extends Node { // @TODO: If extending from TempNode it breaks webgpu_compute

Expand All @@ -17,6 +18,13 @@ class ArrayElementNode extends Node { // @TODO: If extending from TempNode it br

}

assign( node ) {

this.node = this.node.temp();
return this.node.bypass( new AssignNode( this, node ) );

}

generate( builder ) {

const nodeSnippet = this.node.build( builder );
Expand Down
8 changes: 8 additions & 0 deletions examples/jsm/nodes/utils/SplitNode.js
@@ -1,4 +1,5 @@
import Node, { addNodeClass } from '../core/Node.js';
import AssignNode from '../core/AssignNode.js';
import { vectorComponents } from '../core/constants.js';

const stringVectorComponents = vectorComponents.join( '' );
Expand Down Expand Up @@ -34,6 +35,13 @@ class SplitNode extends Node {

}

assign( node ) {

this.node = this.node.temp();
return this.node.bypass( new AssignNode( this, node ) );

}

generate( builder, output ) {

const node = this.node;
Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/renderers/common/Background.js
@@ -1,6 +1,6 @@
import DataMap from './DataMap.js';
import { Color, Mesh, SphereGeometry, BackSide } from 'three';
import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
import { context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';

let _clearAlpha;
const _clearColor = new Color();
Expand Down Expand Up @@ -59,8 +59,8 @@ class Background extends DataMap {
getSamplerLevelNode: () => backgroundBlurriness
} ).mul( backgroundIntensity );

let viewProj = modelViewProjection();
viewProj = vec4( viewProj.x, viewProj.y, viewProj.w, viewProj.w );
const viewProj = modelViewProjection().temp();
viewProj.z = viewProj.w;

const nodeMaterial = new NodeMaterial();
nodeMaterial.outputNode = this.backgroundMeshNode;
Expand Down