浏览代码

Rework how NodeMaterial is built and make other updates (#23797)

* Rework NodeMaterial and make other updates

* Fix DeepScan issues

* Fix DeepScan issues

* Cleanup

* NodeMaterial.fromMaterial()

* Fix

* Revert BypassNode removal

* Fix

* Split ShaderNode into two parts

* Fix

* Fix

* Another fix

* One more fix

* Apply suggestions

* One more fix

* Move ShaderNodeUtils to core

* move ShaderNode* to ./shadernode/ and fixes

* fix from deepscan

Co-authored-by: sunag <[email protected]>
Levi Pesin 3 年之前
父节点
当前提交
11546d74bf

+ 2 - 1
examples/jsm/nodes/Nodes.js

@@ -88,7 +88,8 @@ export * from './functions/BSDFs.js';
 export * from './materials/Materials.js';
 
 // shader node
-export * from './ShaderNode.js';
+export * from './shadernode/ShaderNode.js';
+export * from './shadernode/ShaderNodeElements.js';
 
 const nodeLib = {
 	// core

+ 0 - 432
examples/jsm/nodes/ShaderNode.js

@@ -1,432 +0,0 @@
-// core nodes
-import PropertyNode from './core/PropertyNode.js';
-import VarNode from './core/VarNode.js';
-import AttributeNode from './core/AttributeNode.js';
-import ConstNode from './core/ConstNode.js';
-import UniformNode from './core/UniformNode.js';
-
-// accessor nodes
-import BufferNode from './accessors/BufferNode.js';
-import PositionNode from './accessors/PositionNode.js';
-import NormalNode from './accessors/NormalNode.js';
-import CameraNode from './accessors/CameraNode.js';
-import ModelNode from './accessors/ModelNode.js';
-import TextureNode from './accessors/TextureNode.js';
-import UVNode from './accessors/UVNode.js';
-
-// math nodes
-import OperatorNode from './math/OperatorNode.js';
-import CondNode from './math/CondNode.js';
-import MathNode from './math/MathNode.js';
-
-// util nodes
-import ArrayElementNode from './utils/ArrayElementNode.js';
-import ConvertNode from './utils/ConvertNode.js';
-import JoinNode from './utils/JoinNode.js';
-import SplitNode from './utils/SplitNode.js';
-
-// utils
-import { getValueFromType } from './core/NodeUtils.js';
-
-const NodeHandler = {
-
-	construct( NodeClosure, params ) {
-
-		const inputs = params.shift();
-
-		return NodeClosure( new ShaderNodeObjects( inputs ), ...params );
-
-	},
-
-	get: function ( node, prop ) {
-
-		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
-
-			if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true ) {
-
-				// accessing properties ( swizzle )
-
-				prop = prop
-					.replace( /r|s/g, 'x' )
-					.replace( /g|t/g, 'y' )
-					.replace( /b|p/g, 'z' )
-					.replace( /a|q/g, 'w' );
-
-				return new ShaderNodeObject( new SplitNode( node, prop ) );
-
-			} else if ( /^\d+$/.test( prop ) === true ) {
-
-				// accessing array
-
-				return new ShaderNodeObject( new ArrayElementNode( node, uint( Number( prop ) ) ) );
-
-			}
-
-		}
-
-		return node[ prop ];
-
-	}
-
-};
-
-const nodeObjects = new WeakMap();
-
-const ShaderNodeObject = function ( obj ) {
-
-	const type = typeof obj;
-
-	if ( ( type === 'number' ) || ( type === 'boolean' ) ) {
-
-		return new ShaderNodeObject( getAutoTypedConstNode( obj ) );
-
-	} else if ( type === 'object' ) {
-
-		if ( obj.isNode === true ) {
-
-			let nodeObject = nodeObjects.get( obj );
-
-			if ( nodeObject === undefined ) {
-
-				nodeObject = new Proxy( obj, NodeHandler );
-				nodeObjects.set( obj, nodeObject );
-				nodeObjects.set( nodeObject, nodeObject );
-
-			}
-
-			return nodeObject;
-
-		}
-
-	}
-
-	return obj;
-
-};
-
-const ShaderNodeObjects = function ( objects ) {
-
-	for ( const name in objects ) {
-
-		objects[ name ] = new ShaderNodeObject( objects[ name ] );
-
-	}
-
-	return objects;
-
-};
-
-const getShaderNodeArray = ( array ) => {
-
-	const len = array.length;
-
-	for ( let i = 0; i < len; i ++ ) {
-
-		array[ i ] = new ShaderNodeObject( array[ i ] );
-
-	}
-
-	return array;
-
-};
-
-const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null ) {
-
-	if ( scope === null ) {
-
-		return ( ...params ) => {
-
-			return new ShaderNodeObject( new NodeClass( ...getShaderNodeArray( params ) ) );
-
-		};
-
-	} else if ( factor === null ) {
-
-		return ( ...params ) => {
-
-			return new ShaderNodeObject( new NodeClass( scope, ...getShaderNodeArray( params ) ) );
-
-		};
-
-	} else {
-
-		factor = new ShaderNodeObject( factor );
-
-		return ( ...params ) => {
-
-			return new ShaderNodeObject( new NodeClass( scope, ...getShaderNodeArray( params ), factor ) );
-
-		};
-
-	}
-
-};
-
-const ShaderNodeScript = function ( jsFunc ) {
-
-	return ( inputs, builder ) => {
-
-		new ShaderNodeObjects( inputs );
-
-		return new ShaderNodeObject( jsFunc( inputs, builder ) );
-
-	};
-
-};
-
-const bools = [ false, true ];
-const uints = [ 0, 1, 2, 3 ];
-const ints = [ -1, -2 ];
-const floats = [ 0.5, 1.5, 1 / 3, 1e-6, 1e6, Math.PI, Math.PI * 2, 1 / Math.PI, 2 / Math.PI, 1 / ( Math.PI * 2), Math.PI / 2 ];
-
-const boolsCacheMap = new Map();
-for ( let bool of bools ) boolsCacheMap.set( bool, new ConstNode( bool ) );
-
-const uintsCacheMap = new Map();
-for ( let uint of uints ) uintsCacheMap.set( uint, new ConstNode( uint, 'uint' ) );
-
-const intsCacheMap = new Map( [ ...uintsCacheMap ].map( el => new ConstNode( el.value, 'int' ) ) );
-for ( let int of ints ) intsCacheMap.set( int, new ConstNode( int, 'int' ) );
-
-const floatsCacheMap = new Map( [ ...intsCacheMap ].map( el => new ConstNode( el.value ) ) );
-for ( let float of floats ) floatsCacheMap.set( float, new ConstNode( float ) );
-for ( let float of floats ) floatsCacheMap.set( - float, new ConstNode( - float ) );
-
-const constNodesCacheMap = new Map( [ ...boolsCacheMap, ...floatsCacheMap ] );
-
-const getAutoTypedConstNode = ( value ) => {
-
-	if ( constNodesCacheMap.has( value ) ) {
-
-		return constNodesCacheMap.get( value );
-
-	} else if ( value.isNode === true ) {
-
-		return value;
-
-	} else {
-
-		return new ConstNode( value );
-
-	}
-
-};
-
-const ConvertType = function ( type, cacheMap = null ) {
-
-	return ( ...params ) => {
-
-		if ( params.length === 0 ) {
-
-			return nodeObject( new ConstNode( getValueFromType( type ), type ) );
-
-		} else {
-
-			if ( type === 'color' && params[ 0 ].isNode !== true ) {
-
-				params = [ getValueFromType( type, ...params ) ];
-
-			}
-
-			if ( params.length === 1 && cacheMap !== null && cacheMap.has( params[ 0 ] ) ) {
-
-				return cacheMap.get( params[ 0 ] );
-
-			}
-
-			const nodes = params.map( getAutoTypedConstNode );
-
-			if ( nodes.length === 1 ) {
-
-				return nodeObject( nodes[ 0 ].nodeType === type ? nodes[ 0 ] : new ConvertNode( nodes[ 0 ], type ) );
-
-			}
-
-			return nodeObject( new ConvertNode( new JoinNode( nodes ), type ) );
-
-		}
-
-	};
-
-};
-
-//
-// Node Material Shader Syntax
-//
-
-export const ShaderNode = new Proxy( ShaderNodeScript, NodeHandler );
-
-export const nodeObject = ( val ) => {
-
-	return new ShaderNodeObject( val );
-
-};
-
-export const uniform = ( value ) => {
-
-	// TODO: get ConstNode from .traverse() in the future
-	value = value.isNode === true ? value.node?.value || value.value : value;
-
-	return nodeObject( new UniformNode( value, value.nodeType ) );
-
-};
-
-export const label = ( node, name ) => {
-
-	node = nodeObject( node );
-
-	if ( node.isVarNode === true ) {
-
-		node.name = name;
-
-		return node;
-
-	}
-
-	return nodeObject( new VarNode( node, name ) );
-
-};
-
-export const temp = ( node ) => nodeObject( new VarNode( nodeObject( node ) ) );
-
-export const color = new ConvertType( 'color' );
-
-export const float = new ConvertType( 'float', floatsCacheMap );
-export const int = new ConvertType( 'int', intsCacheMap );
-export const uint = new ConvertType( 'uint', uintsCacheMap );
-export const bool = new ConvertType( 'bool', boolsCacheMap );
-
-export const vec2 = new ConvertType( 'vec2' );
-export const ivec2 = new ConvertType( 'ivec2' );
-export const uvec2 = new ConvertType( 'uvec2' );
-export const bvec2 = new ConvertType( 'bvec2' );
-
-export const vec3 = new ConvertType( 'vec3' );
-export const ivec3 = new ConvertType( 'ivec3' );
-export const uvec3 = new ConvertType( 'uvec3' );
-export const bvec3 = new ConvertType( 'bvec3' );
-
-export const vec4 = new ConvertType( 'vec4' );
-export const ivec4 = new ConvertType( 'ivec4' );
-export const uvec4 = new ConvertType( 'uvec4' );
-export const bvec4 = new ConvertType( 'bvec4' );
-
-export const mat3 = new ConvertType( 'mat3' );
-export const imat3 = new ConvertType( 'imat3' );
-export const umat3 = new ConvertType( 'umat3' );
-export const bmat3 = new ConvertType( 'bmat3' );
-
-export const mat4 = new ConvertType( 'mat4' );
-export const imat4 = new ConvertType( 'imat4' );
-export const umat4 = new ConvertType( 'umat4' );
-export const bmat4 = new ConvertType( 'bmat4' );
-
-export const join = ( ...params ) => nodeObject( new JoinNode( getShaderNodeArray( params ) ) );
-
-export const uv = ( ...params ) => nodeObject( new UVNode( ...params ) );
-export const attribute = ( ...params ) => nodeObject( new AttributeNode( ...params ) );
-export const buffer = ( ...params ) => nodeObject( new BufferNode( ...params ) );
-export const texture = ( ...params ) => nodeObject( new TextureNode( ...params ) );
-export const sampler = ( texture ) => nodeObject( new ConvertNode( texture.isNode === true ? texture : new TextureNode( texture ), 'sampler' ) );
-
-export const cond = ( ...params ) => nodeObject( new CondNode( ...getShaderNodeArray( params ) ) );
-
-export const addTo = ( varNode, ...params ) => {
-
-	varNode.node = add( varNode.node, ...getShaderNodeArray( params ) );
-
-	return nodeObject( varNode );
-
-};
-
-export const add = new ShaderNodeProxy( OperatorNode, '+' );
-export const sub = new ShaderNodeProxy( OperatorNode, '-' );
-export const mul = new ShaderNodeProxy( OperatorNode, '*' );
-export const div = new ShaderNodeProxy( OperatorNode, '/' );
-export const remainder = new ShaderNodeProxy( OperatorNode, '%' );
-export const equal = new ShaderNodeProxy( OperatorNode, '==' );
-export const assign = new ShaderNodeProxy( OperatorNode, '=' );
-export const lessThan = new ShaderNodeProxy( OperatorNode, '<' );
-export const greaterThan = new ShaderNodeProxy( OperatorNode, '>' );
-export const lessThanEqual = new ShaderNodeProxy( OperatorNode, '<=' );
-export const greaterThanEqual = new ShaderNodeProxy( OperatorNode, '>=' );
-export const and = new ShaderNodeProxy( OperatorNode, '&&' );
-export const or = new ShaderNodeProxy( OperatorNode, '||' );
-export const xor = new ShaderNodeProxy( OperatorNode, '^^' );
-export const bitAnd = new ShaderNodeProxy( OperatorNode, '&' );
-export const bitOr = new ShaderNodeProxy( OperatorNode, '|' );
-export const bitXor = new ShaderNodeProxy( OperatorNode, '^' );
-export const shiftLeft = new ShaderNodeProxy( OperatorNode, '<<' );
-export const shiftRight = new ShaderNodeProxy( OperatorNode, '>>' );
-
-export const element = new ShaderNodeProxy( ArrayElementNode );
-
-export const normalGeometry = new ShaderNodeObject( new NormalNode( NormalNode.GEOMETRY ) );
-export const normalLocal = new ShaderNodeObject( new NormalNode( NormalNode.LOCAL ) );
-export const normalWorld = new ShaderNodeObject( new NormalNode( NormalNode.WORLD ) );
-export const normalView = new ShaderNodeObject( new NormalNode( NormalNode.VIEW ) );
-export const transformedNormalView = new ShaderNodeObject( new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' ) );
-
-export const positionLocal = new ShaderNodeObject( new PositionNode( PositionNode.LOCAL ) );
-export const positionWorld = new ShaderNodeObject( new PositionNode( PositionNode.WORLD ) );
-export const positionView = new ShaderNodeObject( new PositionNode( PositionNode.VIEW ) );
-export const positionViewDirection = new ShaderNodeObject( new PositionNode( PositionNode.VIEW_DIRECTION ) );
-
-export const viewMatrix = new ShaderNodeObject( new ModelNode( ModelNode.VIEW_MATRIX ) );
-
-export const cameraPosition = new ShaderNodeObject( new CameraNode( CameraNode.POSITION ) );
-
-export const diffuseColor = new ShaderNodeObject( new PropertyNode( 'DiffuseColor', 'vec4' ) );
-export const roughness = new ShaderNodeObject( new PropertyNode( 'Roughness', 'float' ) );
-export const metalness = new ShaderNodeObject( new PropertyNode( 'Metalness', 'float' ) );
-export const alphaTest = new ShaderNodeObject( new PropertyNode( 'AlphaTest', 'float' ) );
-export const specularColor = new ShaderNodeObject( new PropertyNode( 'SpecularColor', 'color' ) );
-
-export const abs = new ShaderNodeProxy( MathNode, 'abs' );
-export const acos = new ShaderNodeProxy( MathNode, 'acos' );
-export const asin = new ShaderNodeProxy( MathNode, 'asin' );
-export const atan = new ShaderNodeProxy( MathNode, 'atan' );
-export const ceil = new ShaderNodeProxy( MathNode, 'ceil' );
-export const clamp = new ShaderNodeProxy( MathNode, 'clamp' );
-export const cos = new ShaderNodeProxy( MathNode, 'cos' );
-export const cross = new ShaderNodeProxy( MathNode, 'cross' );
-export const degrees = new ShaderNodeProxy( MathNode, 'degrees' );
-export const dFdx = new ShaderNodeProxy( MathNode, 'dFdx' );
-export const dFdy = new ShaderNodeProxy( MathNode, 'dFdy' );
-export const distance = new ShaderNodeProxy( MathNode, 'distance' );
-export const dot = new ShaderNodeProxy( MathNode, 'dot' );
-export const exp = new ShaderNodeProxy( MathNode, 'exp' );
-export const exp2 = new ShaderNodeProxy( MathNode, 'exp2' );
-export const faceforward = new ShaderNodeProxy( MathNode, 'faceforward' );
-export const floor = new ShaderNodeProxy( MathNode, 'floor' );
-export const fract = new ShaderNodeProxy( MathNode, 'fract' );
-export const invert = new ShaderNodeProxy( MathNode, 'invert' );
-export const inversesqrt = new ShaderNodeProxy( MathNode, 'inversesqrt' );
-export const length = new ShaderNodeProxy( MathNode, 'length' );
-export const log = new ShaderNodeProxy( MathNode, 'log' );
-export const log2 = new ShaderNodeProxy( MathNode, 'log2' );
-export const max = new ShaderNodeProxy( MathNode, 'max' );
-export const min = new ShaderNodeProxy( MathNode, 'min' );
-export const mix = new ShaderNodeProxy( MathNode, 'mix' );
-export const mod = new ShaderNodeProxy( MathNode, 'mod' );
-export const negate = new ShaderNodeProxy( MathNode, 'negate' );
-export const normalize = new ShaderNodeProxy( MathNode, 'normalize' );
-export const pow = new ShaderNodeProxy( MathNode, 'pow' );
-export const pow2 = new ShaderNodeProxy( MathNode, 'pow', 2 );
-export const pow3 = new ShaderNodeProxy( MathNode, 'pow', 3 );
-export const pow4 = new ShaderNodeProxy( MathNode, 'pow', 4 );
-export const radians = new ShaderNodeProxy( MathNode, 'radians' );
-export const reflect = new ShaderNodeProxy( MathNode, 'reflect' );
-export const refract = new ShaderNodeProxy( MathNode, 'refract' );
-export const round = new ShaderNodeProxy( MathNode, 'round' );
-export const saturate = new ShaderNodeProxy( MathNode, 'saturate' );
-export const sign = new ShaderNodeProxy( MathNode, 'sign' );
-export const sin = new ShaderNodeProxy( MathNode, 'sin' );
-export const smoothstep = new ShaderNodeProxy( MathNode, 'smoothstep' );
-export const sqrt = new ShaderNodeProxy( MathNode, 'sqrt' );
-export const step = new ShaderNodeProxy( MathNode, 'step' );
-export const tan = new ShaderNodeProxy( MathNode, 'tan' );
-export const transformDirection = new ShaderNodeProxy( MathNode, 'transformDirection' );
-
-export const EPSILON = float( 1e-6 );
-export const INFINITY = float( 1e6 );

+ 1 - 1
examples/jsm/nodes/accessors/ReflectNode.js

@@ -1,5 +1,5 @@
 import Node from '../core/Node.js';
-import { nodeObject, normalWorld, positionWorld, cameraPosition, sub, normalize, join, negate, reflect } from '../ShaderNode.js';
+import { nodeObject, normalWorld, positionWorld, cameraPosition, sub, normalize, join, negate, reflect } from '../shadernode/ShaderNodeElements.js';
 
 class ReflectNode extends Node {
 

+ 2 - 3
examples/jsm/nodes/accessors/SkinningNode.js

@@ -1,7 +1,6 @@
 import Node from '../core/Node.js';
-
+import ShaderNode from '../shadernode/ShaderNode.js';
 import {
-	ShaderNode,
 	attribute,
 	buffer,
 	mat4,
@@ -13,7 +12,7 @@ import {
 	add,
 	mul,
 	transformDirection
-} from '../ShaderNode.js';
+} from '../shadernode/ShaderNodeElements.js';
 
 import { NodeUpdateType } from '../core/constants.js';
 

+ 3 - 3
examples/jsm/nodes/core/VarNode.js

@@ -2,9 +2,9 @@ import Node from './Node.js';
 
 class VarNode extends Node {
 
-	constructor( node, name = null, nodeType = null ) {
+	constructor( node, name = null ) {
 
-		super( nodeType );
+		super();
 
 		this.node = node;
 		this.name = name;
@@ -19,7 +19,7 @@ class VarNode extends Node {
 
 	getNodeType( builder ) {
 
-		return super.getNodeType( builder ) || this.node.getNodeType( builder );
+		return this.node.getNodeType( builder );
 
 	}
 

+ 2 - 4
examples/jsm/nodes/display/ColorSpaceNode.js

@@ -1,8 +1,6 @@
 import TempNode from '../core/Node.js';
-import { ShaderNode,
-	vec3,
-	pow, mul, sub, mix, join,
-	lessThanEqual } from '../ShaderNode.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import { vec3, pow, mul, sub, mix, join, lessThanEqual } from '../shadernode/ShaderNodeElements.js';
 
 import { LinearEncoding, sRGBEncoding } from 'three';
 

+ 2 - 1
examples/jsm/nodes/display/NormalMapNode.js

@@ -1,6 +1,7 @@
 import TempNode from '../core/TempNode.js';
 import ModelNode from '../accessors/ModelNode.js';
-import { ShaderNode, positionView, normalView, uv, join, cond, add, sub, mul, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equal } from '../ShaderNode.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import { positionView, normalView, uv, join, cond, add, sub, mul, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equal } from '../shadernode/ShaderNodeElements.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
 

+ 1 - 1
examples/jsm/nodes/fog/FogRangeNode.js

@@ -1,5 +1,5 @@
 import FogNode from './FogNode.js';
-import { smoothstep, negate, positionView } from '../ShaderNode.js';
+import { smoothstep, negate, positionView } from '../shadernode/ShaderNodeElements.js';
 
 class FogRangeNode extends FogNode {
 

+ 3 - 2
examples/jsm/nodes/functions/BSDFs.js

@@ -1,10 +1,11 @@
-import { ShaderNode,
+import ShaderNode from '../shadernode/ShaderNode.js';
+import {
 	add, addTo, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
 	cond, greaterThan, and,
 	transformedNormalView, positionViewDirection,
 	diffuseColor, specularColor, roughness,
 	EPSILON
-} from '../ShaderNode.js';
+} from '../shadernode/ShaderNodeElements.js';
 
 export const F_Schlick = new ShaderNode( ( inputs ) => {
 

+ 2 - 4
examples/jsm/nodes/functions/PhysicalMaterialFunctions.js

@@ -1,7 +1,5 @@
-import { ShaderNode,
-	add, max, min, abs, dFdx, dFdy,
-	normalGeometry
-} from '../ShaderNode.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import { add, max, min, abs, dFdx, dFdy, normalGeometry } from '../shadernode/ShaderNodeElements.js';
 
 export const getGeometryRoughness = new ShaderNode( () => {
 

+ 29 - 0
examples/jsm/nodes/materials/Materials.js

@@ -1,3 +1,4 @@
+import NodeMaterial from './NodeMaterial.js';
 import LineBasicNodeMaterial from './LineBasicNodeMaterial.js';
 import MeshBasicNodeMaterial from './MeshBasicNodeMaterial.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
@@ -5,6 +6,7 @@ import PointsNodeMaterial from './PointsNodeMaterial.js';
 import { Material } from 'three';
 
 export {
+	NodeMaterial,
 	LineBasicNodeMaterial,
 	MeshBasicNodeMaterial,
 	MeshStandardNodeMaterial,
@@ -12,6 +14,7 @@ export {
 };
 
 const materialLib = {
+	NodeMaterial,
 	LineBasicNodeMaterial,
 	MeshBasicNodeMaterial,
 	MeshStandardNodeMaterial,
@@ -31,3 +34,29 @@ Material.fromType = function ( type ) {
 	return fromTypeFunction.call( this, type );
 
 };
+
+NodeMaterial.fromMaterial = function ( material ) {
+
+	const type = material.type.replace( 'Material', 'NodeMaterial' );
+
+	if ( materialLib[ type ] === undefined ) {
+
+		return material; // is already a node material or cannot be converted
+
+	}
+
+	const nodeMaterial = new materialLib[ type ]( material );
+
+	for ( let key in material ) {
+
+		if ( nodeMaterial[ key ] === undefined ) {
+
+			nodeMaterial[ key ] = material[ key ]; // currently this is needed only for material.alphaTest
+
+		}
+
+	}
+
+	return nodeMaterial;
+
+};

+ 51 - 0
examples/jsm/nodes/materials/MeshStandardNodeMaterial.js

@@ -1,4 +1,11 @@
 import NodeMaterial from './NodeMaterial.js';
+import {
+	float, vec3, vec4,
+	assign, label, mul, invert, mix,
+	normalView,
+	materialRoughness, materialMetalness
+} from '../shadernode/ShaderNodeElements.js';
+import { getRoughness } from '../functions/PhysicalMaterialFunctions.js';
 import { MeshStandardMaterial } from 'three';
 
 const defaultValues = new MeshStandardMaterial();
@@ -36,6 +43,50 @@ export default class MeshStandardNodeMaterial extends NodeMaterial {
 
 	}
 
+	build( builder ) {
+
+		const lightNode = this.lightNode || builder.lightNode; // use scene lights
+
+		let { colorNode, diffuseColorNode } = this.generateMain( builder );
+
+		diffuseColorNode = this.generateStandardMaterial( builder, { colorNode, diffuseColorNode } );
+
+		this.generateLight( builder, diffuseColorNode, lightNode );
+
+	}
+
+	generateStandardMaterial( builder, { colorNode, diffuseColorNode } ) {
+
+		// METALNESS
+
+		let metalnessNode = this.metalnessNode ? float( this.metalnessNode ) : materialMetalness;
+
+		metalnessNode = builder.addFlow( 'fragment', label( metalnessNode, 'Metalness' ) );
+		builder.addFlow( 'fragment', assign( diffuseColorNode, vec4( mul( diffuseColorNode.rgb, invert( metalnessNode ) ), diffuseColorNode.a ) ) );
+
+		// ROUGHNESS
+
+		let roughnessNode = this.roughnessNode ? float( this.roughnessNode ) : materialRoughness;
+		roughnessNode = getRoughness( { roughness: roughnessNode } );
+
+		builder.addFlow( 'fragment', label( roughnessNode, 'Roughness' ) );
+
+		// SPECULAR COLOR
+
+		const specularColorNode = mix( vec3( 0.04 ), colorNode.rgb, metalnessNode );
+
+		builder.addFlow( 'fragment', label( specularColorNode, 'SpecularColor' ) );
+
+		// NORMAL VIEW
+
+		const normalNode = this.normalNode ? vec3( this.normalNode ) : normalView;
+
+		builder.addFlow( 'fragment', label( normalNode, 'TransformedNormalView' ) );
+
+		return diffuseColorNode;
+
+	}
+
 	copy( source ) {
 
 		this.colorNode = source.colorNode;

+ 91 - 9
examples/jsm/nodes/materials/NodeMaterial.js

@@ -1,5 +1,12 @@
 import { Material, ShaderMaterial } from 'three';
 import { getNodesKeys } from '../core/NodeUtils.js';
+import ExpressionNode from '../core/ExpressionNode.js';
+import {
+	float, vec3, vec4,
+	assign, label, mul, add, mix, bypass,
+	positionLocal, skinning, modelViewProjection, lightContext, colorSpace,
+	materialAlphaTest, materialColor, materialOpacity
+} from '../shadernode/ShaderNodeElements.js';
 
 class NodeMaterial extends ShaderMaterial {
 
@@ -13,6 +20,87 @@ class NodeMaterial extends ShaderMaterial {
 
 	}
 
+	build( builder ) {
+
+		const { diffuseColorNode } = this.generateMain( builder );
+
+		this.generateLight( builder, diffuseColorNode, this.lightNode );
+
+	}
+
+	generateMain( builder ) {
+
+		// VERTEX STAGE
+
+		let vertex = positionLocal;
+
+		if ( this.positionNode ) vertex = bypass( vertex, assign( positionLocal, this.positionNode ) );
+		if ( builder.object.isSkinnedMesh ) vertex = bypass( vertex, skinning( builder.object ) );
+
+		builder.context.vertex = vertex;
+
+		builder.addFlow( 'vertex', modelViewProjection() );
+
+		// FRAGMENT STAGE
+
+		let colorNode = vec4( this.colorNode || materialColor );
+		let opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;
+
+		// COLOR
+
+		colorNode = builder.addFlow( 'fragment', label( colorNode, 'Color' ) );
+		const diffuseColorNode = builder.addFlow( 'fragment', label( colorNode, 'DiffuseColor' ) );
+
+		// OPACITY
+
+		opacityNode = builder.addFlow( 'fragment', label( opacityNode, 'OPACITY' ) );
+		builder.addFlow( 'fragment', assign( diffuseColorNode.a, mul( diffuseColorNode.a, opacityNode ) ) );
+
+		// ALPHA TEST
+
+		if ( this.alphaTestNode || this.alphaTest > 0 ) {
+
+			const alphaTestNode = this.alphaTestNode ? float( this.alphaTestNode ) : materialAlphaTest;
+
+			builder.addFlow( 'fragment', label( alphaTestNode, 'AlphaTest' ) );
+			builder.addFlow( 'fragment', new ExpressionNode( 'if ( DiffuseColor.a <= AlphaTest ) { discard; }' ) );
+																	// TODO: remove ExpressionNode here and then possibly remove it completely
+
+		}
+
+		return { colorNode, diffuseColorNode };
+
+	}
+
+	generateLight( builder, diffuseColorNode, lightNode ) {
+
+		// OUTGOING LIGHT
+
+		let outgoingLightNode = diffuseColorNode.xyz;
+		if ( lightNode && lightNode.hasLight !== false ) outgoingLightNode = builder.addFlow( 'fragment', label( lightContext( lightNode ), 'Light' ) );
+
+		// EMISSIVE
+
+		if ( this.emissiveNode ) outgoingLightNode = add( vec3( this.emissiveNode ), outgoingLightNode );
+
+		// OUTPUT
+
+		let outputNode = vec4( outgoingLightNode, diffuseColorNode.a );
+
+		// ENCODING
+
+		outputNode = colorSpace( outputNode, builder.renderer.outputEncoding );
+
+		// FOG
+
+		if ( builder.fogNode ) outputNode = mix( outputNode, builder.fogNode.colorNode, builder.fogNode );
+
+		// RESULT
+
+		builder.addFlow( 'fragment', label( outputNode, 'Output' ) );
+
+	}
+
 	setDefaultValues( values ) {
 
 		// This approach is to reuse the native refreshUniforms*
@@ -26,15 +114,7 @@ class NodeMaterial extends ShaderMaterial {
 
 			if ( this[ property ] === undefined ) {
 
-				if ( value && typeof value.clone === 'function' ) {
-
-					this[ property ] = value.clone();
-
-				} else {
-
-					this[ property ] = value;
-
-				}
+				this[ property ] = value?.clone?.() || value;
 
 			}
 
@@ -103,6 +183,8 @@ class NodeMaterial extends ShaderMaterial {
 
 	}
 
+	static fromMaterial( material ) { }
+
 }
 
 NodeMaterial.prototype.isNodeMaterial = true;

+ 2 - 2
examples/jsm/nodes/procedural/CheckerNode.js

@@ -1,6 +1,6 @@
 import Node from '../core/Node.js';
-
-import { ShaderNode, uv, add, mul, floor, mod, sign } from '../ShaderNode.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import { uv, add, mul, floor, mod, sign } from '../shadernode/ShaderNodeElements.js';
 
 const checkerShaderNode = new ShaderNode( ( inputs ) => {
 

+ 5 - 0
examples/jsm/nodes/shadernode/ShaderNode.js

@@ -0,0 +1,5 @@
+import { ShaderNodeScript, NodeHandler } from './ShaderNodeUtils.js';
+
+const ShaderNode = new Proxy( ShaderNodeScript, NodeHandler );
+
+export default ShaderNode;

+ 227 - 0
examples/jsm/nodes/shadernode/ShaderNodeElements.js

@@ -0,0 +1,227 @@
+// core nodes
+import PropertyNode from '../core/PropertyNode.js';
+import VarNode from '../core/VarNode.js';
+import AttributeNode from '../core/AttributeNode.js';
+import UniformNode from '../core/UniformNode.js';
+import BypassNode from '../core/BypassNode.js';
+
+// accessor nodes
+import BufferNode from '../accessors/BufferNode.js';
+import CameraNode from '../accessors/CameraNode.js';
+import MaterialNode from '../accessors/MaterialNode.js';
+import ModelNode from '../accessors/ModelNode.js';
+import ModelViewProjectionNode from '../accessors/ModelViewProjectionNode.js';
+import NormalNode from '../accessors/NormalNode.js';
+import PositionNode from '../accessors/PositionNode.js';
+import SkinningNode from '../accessors/SkinningNode.js';
+import TextureNode from '../accessors/TextureNode.js';
+import UVNode from '../accessors/UVNode.js';
+
+// math nodes
+import OperatorNode from '../math/OperatorNode.js';
+import CondNode from '../math/CondNode.js';
+import MathNode from '../math/MathNode.js';
+
+// util nodes
+import ArrayElementNode from '../utils/ArrayElementNode.js';
+import ConvertNode from '../utils/ConvertNode.js';
+import JoinNode from '../utils/JoinNode.js';
+
+// other nodes
+import ColorSpaceNode from '../display/ColorSpaceNode.js';
+import LightContextNode from '../lights/LightContextNode.js';
+
+// utils
+import { nodeObject, nodeObjects, nodeArray, nodeProxy, ConvertType, floatsCacheMap, intsCacheMap, uintsCacheMap, boolsCacheMap } from './ShaderNodeUtils.js';
+import ShaderNode from './ShaderNode.js';
+
+//
+// Node Material Shader Syntax
+//
+
+export { ShaderNode, nodeObject, nodeObjects, nodeArray, nodeProxy };
+
+export const float = new ConvertType( 'float', floatsCacheMap );
+export const int = new ConvertType( 'int', intsCacheMap );
+export const uint = new ConvertType( 'uint', uintsCacheMap );
+export const bool = new ConvertType( 'bool', boolsCacheMap );
+
+export const color = new ConvertType( 'color' );
+
+export const vec2 = new ConvertType( 'vec2' );
+export const ivec2 = new ConvertType( 'ivec2' );
+export const uvec2 = new ConvertType( 'uvec2' );
+export const bvec2 = new ConvertType( 'bvec2' );
+
+export const vec3 = new ConvertType( 'vec3' );
+export const ivec3 = new ConvertType( 'ivec3' );
+export const uvec3 = new ConvertType( 'uvec3' );
+export const bvec3 = new ConvertType( 'bvec3' );
+
+export const vec4 = new ConvertType( 'vec4' );
+export const ivec4 = new ConvertType( 'ivec4' );
+export const uvec4 = new ConvertType( 'uvec4' );
+export const bvec4 = new ConvertType( 'bvec4' );
+
+export const mat3 = new ConvertType( 'mat3' );
+export const imat3 = new ConvertType( 'imat3' );
+export const umat3 = new ConvertType( 'umat3' );
+export const bmat3 = new ConvertType( 'bmat3' );
+
+export const mat4 = new ConvertType( 'mat4' );
+export const imat4 = new ConvertType( 'imat4' );
+export const umat4 = new ConvertType( 'umat4' );
+export const bmat4 = new ConvertType( 'bmat4' );
+
+export const uniform = ( value ) => {
+
+	// TODO: get ConstNode from .traverse() in the future
+	value = value.isNode === true ? value.node?.value || value.value : value;
+
+	return nodeObject( new UniformNode( value, value.nodeType ) );
+
+};
+
+export const label = ( node, name ) => {
+
+	node = nodeObject( node );
+
+	if ( ( node.isVarNode === true ) && ( node.name === name ) ) {
+
+		return node;
+
+	}
+
+	return nodeObject( new VarNode( node, name ) );
+
+};
+
+export const temp = nodeProxy( VarNode );
+
+export const join = ( ...params ) => nodeObject( new JoinNode( nodeArray( params ) ) );
+
+export const uv = ( ...params ) => nodeObject( new UVNode( ...params ) );
+export const attribute = ( ...params ) => nodeObject( new AttributeNode( ...params ) );
+export const buffer = ( ...params ) => nodeObject( new BufferNode( ...params ) );
+export const texture = ( ...params ) => nodeObject( new TextureNode( ...params ) );
+export const sampler = ( texture ) => nodeObject( new ConvertNode( texture.isNode === true ? texture : new TextureNode( texture ), 'sampler' ) );
+
+export const cond = nodeProxy( CondNode );
+
+export const addTo = ( varNode, ...params ) => {
+
+	varNode.node = add( varNode.node, ...nodeArray( params ) );
+
+	return nodeObject( varNode );
+
+};
+
+export const add = nodeProxy( OperatorNode, '+' );
+export const sub = nodeProxy( OperatorNode, '-' );
+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, '<=' );
+export const greaterThanEqual = nodeProxy( OperatorNode, '>=' );
+export const and = nodeProxy( OperatorNode, '&&' );
+export const or = nodeProxy( OperatorNode, '||' );
+export const xor = nodeProxy( OperatorNode, '^^' );
+export const bitAnd = nodeProxy( OperatorNode, '&' );
+export const bitOr = nodeProxy( OperatorNode, '|' );
+export const bitXor = nodeProxy( OperatorNode, '^' );
+export const shiftLeft = nodeProxy( OperatorNode, '<<' );
+export const shiftRight = nodeProxy( OperatorNode, '>>' );
+
+export const element = nodeProxy( ArrayElementNode );
+
+export const modelViewProjection = nodeProxy( ModelViewProjectionNode );
+
+export const normalGeometry = nodeObject( new NormalNode( NormalNode.GEOMETRY ) );
+export const normalLocal = nodeObject( new NormalNode( NormalNode.LOCAL ) );
+export const normalWorld = nodeObject( new NormalNode( NormalNode.WORLD ) );
+export const normalView = nodeObject( new NormalNode( NormalNode.VIEW ) );
+export const transformedNormalView = nodeObject( new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' ) );
+
+export const positionGeometry = nodeObject( new PositionNode( PositionNode.GEOMETRY ) );
+export const positionLocal = nodeObject( new PositionNode( PositionNode.LOCAL ) );
+export const positionWorld = nodeObject( new PositionNode( PositionNode.WORLD ) );
+export const positionView = nodeObject( new PositionNode( PositionNode.VIEW ) );
+export const positionViewDirection = nodeObject( new PositionNode( PositionNode.VIEW_DIRECTION ) );
+
+export const viewMatrix = nodeObject( new ModelNode( ModelNode.VIEW_MATRIX ) );
+
+export const cameraPosition = nodeObject( new CameraNode( CameraNode.POSITION ) );
+
+export const diffuseColor = nodeObject( new PropertyNode( 'DiffuseColor', 'vec4' ) );
+export const roughness = nodeObject( new PropertyNode( 'Roughness', 'float' ) );
+export const metalness = nodeObject( new PropertyNode( 'Metalness', 'float' ) );
+export const alphaTest = nodeObject( new PropertyNode( 'AlphaTest', 'float' ) );
+export const specularColor = nodeObject( new PropertyNode( 'SpecularColor', 'color' ) );
+
+export const materialAlphaTest = nodeObject( new MaterialNode( MaterialNode.ALPHA_TEST ) );
+export const materialColor = nodeObject( new MaterialNode( MaterialNode.COLOR ) );
+export const materialOpacity = nodeObject( new MaterialNode( MaterialNode.OPACITY ) );
+export const materialSpecular = nodeObject( new MaterialNode( MaterialNode.SPECULAR ) );
+export const materialRoughness = nodeObject( new MaterialNode( MaterialNode.ROUGHNESS ) );
+export const materialMetalness = nodeObject( new MaterialNode( MaterialNode.METALNESS ) );
+
+export const skinning = nodeProxy( SkinningNode );
+
+export const lightContext = nodeProxy( LightContextNode );
+
+export const colorSpace = ( node, encoding ) => nodeObject( new ColorSpaceNode( null, nodeObject( node ) ).fromEncoding( encoding ) );
+
+export const bypass = nodeProxy( BypassNode );
+
+export const abs = nodeProxy( MathNode, 'abs' );
+export const acos = nodeProxy( MathNode, 'acos' );
+export const asin = nodeProxy( MathNode, 'asin' );
+export const atan = nodeProxy( MathNode, 'atan' );
+export const ceil = nodeProxy( MathNode, 'ceil' );
+export const clamp = nodeProxy( MathNode, 'clamp' );
+export const cos = nodeProxy( MathNode, 'cos' );
+export const cross = nodeProxy( MathNode, 'cross' );
+export const degrees = nodeProxy( MathNode, 'degrees' );
+export const dFdx = nodeProxy( MathNode, 'dFdx' );
+export const dFdy = nodeProxy( MathNode, 'dFdy' );
+export const distance = nodeProxy( MathNode, 'distance' );
+export const dot = nodeProxy( MathNode, 'dot' );
+export const exp = nodeProxy( MathNode, 'exp' );
+export const exp2 = nodeProxy( MathNode, 'exp2' );
+export const faceforward = nodeProxy( MathNode, 'faceforward' );
+export const floor = nodeProxy( MathNode, 'floor' );
+export const fract = nodeProxy( MathNode, 'fract' );
+export const invert = nodeProxy( MathNode, 'invert' );
+export const inversesqrt = nodeProxy( MathNode, 'inversesqrt' );
+export const length = nodeProxy( MathNode, 'length' );
+export const log = nodeProxy( MathNode, 'log' );
+export const log2 = nodeProxy( MathNode, 'log2' );
+export const max = nodeProxy( MathNode, 'max' );
+export const min = nodeProxy( MathNode, 'min' );
+export const mix = nodeProxy( MathNode, 'mix' );
+export const mod = nodeProxy( MathNode, 'mod' );
+export const negate = nodeProxy( MathNode, 'negate' );
+export const normalize = nodeProxy( MathNode, 'normalize' );
+export const pow = nodeProxy( MathNode, 'pow' );
+export const pow2 = nodeProxy( MathNode, 'pow', 2 );
+export const pow3 = nodeProxy( MathNode, 'pow', 3 );
+export const pow4 = nodeProxy( MathNode, 'pow', 4 );
+export const radians = nodeProxy( MathNode, 'radians' );
+export const reflect = nodeProxy( MathNode, 'reflect' );
+export const refract = nodeProxy( MathNode, 'refract' );
+export const round = nodeProxy( MathNode, 'round' );
+export const saturate = nodeProxy( MathNode, 'saturate' );
+export const sign = nodeProxy( MathNode, 'sign' );
+export const sin = nodeProxy( MathNode, 'sin' );
+export const smoothstep = nodeProxy( MathNode, 'smoothstep' );
+export const sqrt = nodeProxy( MathNode, 'sqrt' );
+export const step = nodeProxy( MathNode, 'step' );
+export const tan = nodeProxy( MathNode, 'tan' );
+export const transformDirection = nodeProxy( MathNode, 'transformDirection' );
+
+export const EPSILON = float( 1e-6 );
+export const INFINITY = float( 1e6 );

+ 234 - 0
examples/jsm/nodes/shadernode/ShaderNodeUtils.js

@@ -0,0 +1,234 @@
+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 ConstNode from '../core/ConstNode.js';
+import { getValueFromType } from '../core/NodeUtils.js';
+
+export const NodeHandler = {
+
+	construct( NodeClosure, params ) {
+
+		const inputs = params.shift();
+
+		return NodeClosure( new ShaderNodeObjects( inputs ), ...params );
+
+	},
+
+	get: function ( node, prop ) {
+
+		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
+
+			if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true ) {
+
+				// accessing properties ( swizzle )
+
+				prop = prop
+					.replace( /r|s/g, 'x' )
+					.replace( /g|t/g, 'y' )
+					.replace( /b|p/g, 'z' )
+					.replace( /a|q/g, 'w' );
+
+				return new ShaderNodeObject( new SplitNode( node, prop ) );
+
+			} else if ( /^\d+$/.test( prop ) === true ) {
+
+				// accessing array
+
+				return new ShaderNodeObject( new ArrayElementNode( node, uint( Number( prop ) ) ) );
+
+			}
+
+		}
+
+		return node[ prop ];
+
+	}
+
+};
+
+const nodeObjectsCacheMap = new WeakMap();
+
+const ShaderNodeObject = function ( obj ) {
+
+	const type = typeof obj;
+
+	if ( ( type === 'number' ) || ( type === 'boolean' ) ) {
+
+		return new ShaderNodeObject( getAutoTypedConstNode( obj ) );
+
+	} else if ( type === 'object' ) {
+
+		if ( obj.isNode === true ) {
+
+			let nodeObject = nodeObjectsCacheMap.get( obj );
+
+			if ( nodeObject === undefined ) {
+
+				nodeObject = new Proxy( obj, NodeHandler );
+				nodeObjectsCacheMap.set( obj, nodeObject );
+				nodeObjectsCacheMap.set( nodeObject, nodeObject );
+
+			}
+
+			return nodeObject;
+
+		}
+
+	}
+
+	return obj;
+
+};
+
+const ShaderNodeObjects = function ( objects ) {
+
+	for ( const name in objects ) {
+
+		objects[ name ] = new ShaderNodeObject( objects[ name ] );
+
+	}
+
+	return objects;
+
+};
+
+const ShaderNodeArray = function ( array ) {
+
+	const len = array.length;
+
+	for ( let i = 0; i < len; i ++ ) {
+
+		array[ i ] = new ShaderNodeObject( array[ i ] );
+
+	}
+
+	return array;
+
+};
+
+const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null ) {
+
+	if ( scope === null ) {
+
+		return ( ...params ) => {
+
+			return new ShaderNodeObject( new NodeClass( ...( new ShaderNodeArray( params ) ) ) );
+
+		};
+
+	} else if ( factor === null ) {
+
+		return ( ...params ) => {
+
+			return new ShaderNodeObject( new NodeClass( scope, ...( new ShaderNodeArray( params ) ) ) );
+
+		};
+
+	} else {
+
+		factor = new ShaderNodeObject( factor );
+
+		return ( ...params ) => {
+
+			return new ShaderNodeObject( new NodeClass( scope, ...( new ShaderNodeArray( params ) ), factor ) );
+
+		};
+
+	}
+
+};
+
+export const ShaderNodeScript = function ( jsFunc ) {
+
+	return ( inputs, builder ) => {
+
+		new ShaderNodeObjects( inputs );
+
+		return new ShaderNodeObject( jsFunc( inputs, builder ) );
+
+	};
+
+};
+
+export const nodeObject = ( val ) => new ShaderNodeObject( val );
+export const nodeObjects = ( val ) => new ShaderNodeObjects( val );
+export const nodeArray = ( val ) => new ShaderNodeArray( val );
+export const nodeProxy = ( ...val ) => new ShaderNodeProxy( ...val );
+
+const bools = [ false, true ];
+const uints = [ 0, 1, 2, 3 ];
+const ints = [ -1, -2 ];
+const floats = [ 0.5, 1.5, 1 / 3, 1e-6, 1e6, Math.PI, Math.PI * 2, 1 / Math.PI, 2 / Math.PI, 1 / ( Math.PI * 2), Math.PI / 2 ];
+
+export const boolsCacheMap = new Map();
+for ( let bool of bools ) boolsCacheMap.set( bool, new ConstNode( bool ) );
+
+export const uintsCacheMap = new Map();
+for ( let uint of uints ) uintsCacheMap.set( uint, new ConstNode( uint, 'uint' ) );
+
+export const intsCacheMap = new Map( [ ...uintsCacheMap ].map( el => new ConstNode( el.value, 'int' ) ) );
+for ( let int of ints ) intsCacheMap.set( int, new ConstNode( int, 'int' ) );
+
+export const floatsCacheMap = new Map( [ ...intsCacheMap ].map( el => new ConstNode( el.value ) ) );
+for ( let float of floats ) floatsCacheMap.set( float, new ConstNode( float ) );
+for ( let float of floats ) floatsCacheMap.set( - float, new ConstNode( - float ) );
+
+const constNodesCacheMap = new Map( [ ...boolsCacheMap, ...floatsCacheMap ] );
+
+const getAutoTypedConstNode = ( value ) => {
+
+	if ( constNodesCacheMap.has( value ) ) {
+
+		return constNodesCacheMap.get( value );
+
+	} else if ( value.isNode === true ) {
+
+		return value;
+
+	} else {
+
+		return new ConstNode( value );
+
+	}
+
+};
+
+export const ConvertType = function ( type, cacheMap = null ) {
+
+	return ( ...params ) => {
+
+		if ( params.length === 0 ) {
+
+			return nodeObject( new ConstNode( getValueFromType( type ), type ) );
+
+		} else {
+
+			if ( type === 'color' && params[ 0 ].isNode !== true ) {
+
+				params = [ getValueFromType( type, ...params ) ];
+
+			}
+
+			if ( params.length === 1 && cacheMap !== null && cacheMap.has( params[ 0 ] ) ) {
+
+				return cacheMap.get( params[ 0 ] );
+
+			}
+
+			const nodes = params.map( getAutoTypedConstNode );
+
+			if ( nodes.length === 1 ) {
+
+				return nodeObject( nodes[ 0 ].nodeType === type ? nodes[ 0 ] : new ConvertNode( nodes[ 0 ], type ) );
+
+			}
+
+			return nodeObject( new ConvertNode( new JoinNode( nodes ), type ) );
+
+		}
+
+	};
+
+};

+ 1 - 1
examples/jsm/nodes/utils/MatcapUVNode.js

@@ -1,5 +1,5 @@
 import TempNode from '../core/TempNode.js';
-import { join, negate, normalize, cross, dot, mul, add, transformedNormalView, positionViewDirection } from '../ShaderNode.js';
+import { join, negate, normalize, cross, dot, mul, add, transformedNormalView, positionViewDirection } from '../shadernode/ShaderNodeElements.js';
 
 class MatcapUVNode extends TempNode {
 

+ 1 - 1
examples/jsm/nodes/utils/OscNode.js

@@ -1,6 +1,6 @@
 import Node from '../core/Node.js';
 import TimerNode from './TimerNode.js';
-import { abs, fract, round, sin, add, sub, mul } from '../ShaderNode.js';
+import { abs, fract, round, sin, add, sub, mul } from '../shadernode/ShaderNodeElements.js';
 
 class OscNode extends Node {
 

+ 5 - 235
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -1,5 +1,3 @@
-import { LinearEncoding } from 'three';
-
 import WebGPUNodeUniformsGroup from './WebGPUNodeUniformsGroup.js';
 import {
 	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
@@ -11,22 +9,12 @@ import { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture } from './WebGPU
 import WebGPUUniformBuffer from '../WebGPUUniformBuffer.js';
 import { getVectorLength, getStrideLength } from '../WebGPUBufferUtils.js';
 
-import VarNode from 'three-nodes/core/VarNode.js';
-import CodeNode from 'three-nodes/core/CodeNode.js';
-import BypassNode from 'three-nodes/core/BypassNode.js';
-import ExpressionNode from 'three-nodes/core/ExpressionNode.js';
 import NodeBuilder from 'three-nodes/core/NodeBuilder.js';
-import MaterialNode from 'three-nodes/accessors/MaterialNode.js';
-import PositionNode from 'three-nodes/accessors/PositionNode.js';
-import NormalNode from 'three-nodes/accessors/NormalNode.js';
-import ModelViewProjectionNode from 'three-nodes/accessors/ModelViewProjectionNode.js';
-import SkinningNode from 'three-nodes/accessors/SkinningNode.js';
-import ColorSpaceNode from 'three-nodes/display/ColorSpaceNode.js';
-import LightContextNode from 'three-nodes/lights/LightContextNode.js';
-import OperatorNode from 'three-nodes/math/OperatorNode.js';
 import WGSLNodeParser from 'three-nodes/parsers/WGSLNodeParser.js';
-import { vec3, add, join, mix, nodeObject } from 'three-nodes/ShaderNode.js';
-import { getRoughness } from 'three-nodes/functions/PhysicalMaterialFunctions.js';
+
+import CodeNode from 'three-nodes/core/CodeNode.js';
+
+import { NodeMaterial } from 'three-nodes/materials/Materials.js';
 
 const wgslTypeLib = {
 	float: 'f32',
@@ -126,230 +114,12 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	build() {
 
-		this._parseObject();
+		NodeMaterial.fromMaterial( this.material ).build( this );
 
 		return super.build();
 
 	}
 
-	_parseObject() {
-
-		const object = this.object;
-		const material = this.material;
-
-		// parse inputs
-
-		if ( material.isMeshStandardMaterial || material.isMeshBasicMaterial || material.isPointsMaterial || material.isLineBasicMaterial ) {
-
-			let lightNode = material.lightNode;
-
-			if ( material.isMeshStandardMaterial && lightNode === null && this.lightNode ) {
-
-				// use scene lights
-
-				lightNode = this.lightNode;
-
-			}
-
-			// VERTEX STAGE
-
-			let vertex = new PositionNode( PositionNode.GEOMETRY );
-
-			if ( material.positionNode && material.positionNode.isNode ) {
-
-				const assignPositionNode = new OperatorNode( '=', new PositionNode( PositionNode.LOCAL ), material.positionNode );
-
-				vertex = new BypassNode( vertex, assignPositionNode );
-
-			}
-
-			if ( object.isSkinnedMesh === true ) {
-
-				vertex = new BypassNode( vertex, new SkinningNode( object ) );
-
-			}
-
-			this.context.vertex = vertex;
-
-			this.addFlow( 'vertex', new VarNode( new ModelViewProjectionNode(), 'MVP', 'vec4' ) );
-
-			// COLOR
-
-			let colorNode = null;
-
-			if ( material.colorNode && material.colorNode.isNode ) {
-
-				colorNode = material.colorNode;
-
-			} else {
-
-				colorNode = new MaterialNode( MaterialNode.COLOR );
-
-			}
-
-			colorNode = this.addFlow( 'fragment', new VarNode( colorNode, 'Color', 'vec4' ) );
-
-			const diffuseColorNode = this.addFlow( 'fragment', new VarNode( colorNode, 'DiffuseColor', 'vec4' ) );
-
-			// OPACITY
-
-			let opacityNode = null;
-
-			if ( material.opacityNode && material.opacityNode.isNode ) {
-
-				opacityNode = material.opacityNode;
-
-			} else {
-
-				opacityNode = new VarNode( new MaterialNode( MaterialNode.OPACITY ) );
-
-			}
-
-			this.addFlow( 'fragment', new VarNode( opacityNode, 'OPACITY', 'float' ) );
-
-			this.addFlow( 'fragment', new ExpressionNode( 'DiffuseColor.a = DiffuseColor.a * OPACITY;' ) );
-
-			// ALPHA TEST
-
-			let alphaTest = null;
-
-			if ( material.alphaTestNode && material.alphaTestNode.isNode ) {
-
-				alphaTest = material.alphaTestNode;
-
-			} else if ( material.alphaTest > 0 ) {
-
-				alphaTest = new MaterialNode( MaterialNode.ALPHA_TEST );
-
-			}
-
-			if ( alphaTest !== null ) {
-
-				this.addFlow( 'fragment', new VarNode( alphaTest, 'AlphaTest', 'float' ) );
-
-				this.addFlow( 'fragment', new ExpressionNode( 'if ( DiffuseColor.a <= AlphaTest ) { discard; }' ) );
-
-			}
-
-			if ( material.isMeshStandardMaterial ) {
-
-				// METALNESS
-
-				let metalnessNode = null;
-
-				if ( material.metalnessNode && material.metalnessNode.isNode ) {
-
-					metalnessNode = material.metalnessNode;
-
-				} else {
-
-					metalnessNode = new MaterialNode( MaterialNode.METALNESS );
-
-				}
-
-				this.addFlow( 'fragment', new VarNode( metalnessNode, 'Metalness', 'float' ) );
-
-				this.addFlow( 'fragment', new ExpressionNode( 'DiffuseColor = vec4<f32>( DiffuseColor.rgb * ( 1.0 - Metalness ), DiffuseColor.a );' ) );
-
-				// ROUGHNESS
-
-				let roughnessNode = null;
-
-				if ( material.roughnessNode && material.roughnessNode.isNode ) {
-
-					roughnessNode = material.roughnessNode;
-
-				} else {
-
-					roughnessNode = new MaterialNode( MaterialNode.ROUGHNESS );
-
-				}
-
-				roughnessNode = getRoughness( { roughness: roughnessNode } );
-
-				this.addFlow( 'fragment', new VarNode( roughnessNode, 'Roughness', 'float' ) );
-
-				// SPECULAR_TINT
-
-				this.addFlow( 'fragment', new VarNode( new ExpressionNode( 'mix( vec3<f32>( 0.04 ), Color.rgb, Metalness )', 'vec3' ), 'SpecularColor', 'color' ) );
-
-				// NORMAL_VIEW
-
-				let normalNode = null;
-
-				if ( material.normalNode && material.normalNode.isNode ) {
-
-					normalNode = material.normalNode;
-
-				} else {
-
-					normalNode = new NormalNode( NormalNode.VIEW );
-
-				}
-
-				this.addFlow( 'fragment', new VarNode( normalNode, 'TransformedNormalView', 'vec3' ) );
-
-			}
-
-			// LIGHT
-
-			let outputNode = diffuseColorNode;
-
-			if ( lightNode && lightNode.isNode && lightNode.hasLight !== false ) {
-
-				const lightContextNode = new LightContextNode( lightNode );
-
-				outputNode = this.addFlow( 'fragment', new VarNode( lightContextNode, 'Light', 'vec3' ) );
-
-			}
-
-			// OUTGOING LIGHT
-
-			let outgoingLightNode = vec3( outputNode );
-
-			// EMISSIVE
-
-			const emissiveNode = material.emissiveNode;
-
-			if ( emissiveNode && emissiveNode.isNode ) {
-
-				outgoingLightNode = add( emissiveNode, outgoingLightNode );
-
-			}
-
-			// OUTPUT
-
-			outputNode = join( vec3( outgoingLightNode ), nodeObject( diffuseColorNode ).w );
-
-			// ENCODING
-
-			const outputEncoding = this.renderer.outputEncoding;
-
-			if ( outputEncoding !== LinearEncoding ) {
-
-				outputNode = new ColorSpaceNode( ColorSpaceNode.LINEAR_TO_LINEAR, outputNode );
-				outputNode.fromEncoding( outputEncoding );
-
-			}
-
-			// FOG
-
-			const fogNode = this.fogNode;
-
-			if ( fogNode && fogNode.isFogNode ) {
-
-				outputNode = mix( outputNode, fogNode.colorNode, fogNode );
-
-			}
-
-			// RESULT
-
-			this.addFlow( 'fragment', new VarNode( outputNode, 'Output', 'vec4' ) );
-
-		}
-
-	}
-
 	addFlowCode( code ) {
 
 		if ( ! /;\s*$/.test( code ) ) {

+ 1 - 1
examples/webgl_materials_instance_uniform_nodes.html

@@ -28,7 +28,7 @@
 
 			import * as THREE from 'three';
 			import * as Nodes from 'three-nodes/Nodes.js';
-			import { add, mul } from 'three-nodes/ShaderNode.js';
+			import { add, mul } from 'three-nodes/Nodes.js';
 
 			import Stats from './jsm/libs/stats.module.js';
 

+ 1 - 1
examples/webgpu_depth_texture.html

@@ -32,7 +32,7 @@
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
 			import WebGPUTextureRenderer from './jsm/renderers/webgpu/WebGPUTextureRenderer.js';
 
-			import { smoothstep, negate, positionView, invert } from 'three-nodes/ShaderNode.js';
+			import { smoothstep, negate, positionView, invert } from 'three-nodes/Nodes.js';
 
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 

+ 1 - 1
examples/webgpu_instance_uniform.html

@@ -28,7 +28,7 @@
 
 			import * as THREE from 'three';
 			import * as Nodes from 'three-nodes/Nodes.js';
-			import { add, mul } from 'three-nodes/ShaderNode.js';
+			import { add, mul } from 'three-nodes/Nodes.js';
 
 			import WebGPU from './jsm/capabilities/WebGPU.js';
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';

+ 1 - 1
examples/webgpu_lights_custom.html

@@ -33,7 +33,7 @@
 
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 
-			import { addTo } from 'three-nodes/ShaderNode.js';
+			import { addTo } from 'three-nodes/Nodes.js';
 
 			let camera, scene, renderer;
 

+ 1 - 1
examples/webgpu_lights_selective.html

@@ -41,7 +41,7 @@
 			import WebGPU from './jsm/capabilities/WebGPU.js';
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
 
-			import { color, float } from 'three-nodes/ShaderNode.js';
+			import { color, float } from 'three-nodes/Nodes.js';
 
 			let camera, scene, renderer,
 				light1, light2, light3, light4,

+ 1 - 1
examples/webgpu_materials.html

@@ -34,7 +34,7 @@
 
 			import { TeapotGeometry } from './jsm/geometries/TeapotGeometry.js';
 
-			import { ShaderNode, vec3, dot, sampler } from 'three-nodes/ShaderNode.js';
+			import { ShaderNode, vec3, dot, sampler } from 'three-nodes/Nodes.js';
 
 			import Stats from './jsm/libs/stats.module.js';