diff --git a/examples/jsm/nodes/Nodes.js b/examples/jsm/nodes/Nodes.js index 5473d588d1829d..ab101b1ed60d3c 100644 --- a/examples/jsm/nodes/Nodes.js +++ b/examples/jsm/nodes/Nodes.js @@ -9,12 +9,8 @@ export { default as ArrayUniformNode /* @TODO: arrayUniform */ } from './core/Ar 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'; -export { default as CodeNode, code, js } from './core/CodeNode.js'; export { default as ConstNode } from './core/ConstNode.js'; export { default as ContextNode, context } from './core/ContextNode.js'; -export { default as ExpressionNode, expression } from './core/ExpressionNode.js'; -export { default as FunctionCallNode, call } from './core/FunctionCallNode.js'; -export { default as FunctionNode, func, fn } from './core/FunctionNode.js'; export { default as InstanceIndexNode, instanceIndex } from './core/InstanceIndexNode.js'; export { default as LightingModel, lightingModel } from './core/LightingModel.js'; export { default as Node, addNodeClass, createNodeFromType } from './core/Node.js'; @@ -97,6 +93,14 @@ export { default as PosterizeNode, posterize } from './display/PosterizeNode.js' export { default as ToneMappingNode, toneMapping } from './display/ToneMappingNode.js'; export { default as ViewportNode, viewportCoordinate, viewportResolution, viewportTopLeft, viewportBottomLeft, viewportTopRight, viewportBottomRight } from './display/ViewportNode.js'; +// code +export { default as ExpressionNode, expression } from './code/ExpressionNode.js'; +export { default as CodeNode, code, js } from './code/CodeNode.js'; +export { default as FunctionCallNode, call } from './code/FunctionCallNode.js'; +export { default as FunctionNode, func, fn } from './code/FunctionNode.js'; +export { default as ScriptableNode, scriptable, global } from './code/ScriptableNode.js'; +export { default as ScriptableValueNode, scriptableValue } from './code/ScriptableValueNode.js'; + // fog export { default as FogNode, fog } from './fog/FogNode.js'; export { default as FogRangeNode, rangeFog } from './fog/FogRangeNode.js'; diff --git a/examples/jsm/nodes/core/CodeNode.js b/examples/jsm/nodes/code/CodeNode.js similarity index 95% rename from examples/jsm/nodes/core/CodeNode.js rename to examples/jsm/nodes/code/CodeNode.js index a0e116861aecb2..7aba2d967ad9d9 100644 --- a/examples/jsm/nodes/core/CodeNode.js +++ b/examples/jsm/nodes/code/CodeNode.js @@ -1,4 +1,4 @@ -import Node, { addNodeClass } from './Node.js'; +import Node, { addNodeClass } from '../core/Node.js'; import { nodeProxy } from '../shadernode/ShaderNode.js'; class CodeNode extends Node { diff --git a/examples/jsm/nodes/core/ExpressionNode.js b/examples/jsm/nodes/code/ExpressionNode.js similarity index 91% rename from examples/jsm/nodes/core/ExpressionNode.js rename to examples/jsm/nodes/code/ExpressionNode.js index bb4dfa31411e87..3a10ad33748c96 100644 --- a/examples/jsm/nodes/core/ExpressionNode.js +++ b/examples/jsm/nodes/code/ExpressionNode.js @@ -1,4 +1,4 @@ -import Node, { addNodeClass } from './Node.js'; +import Node, { addNodeClass } from '../core/Node.js'; import { nodeProxy } from '../shadernode/ShaderNode.js'; class ExpressionNode extends Node { diff --git a/examples/jsm/nodes/core/FunctionCallNode.js b/examples/jsm/nodes/code/FunctionCallNode.js similarity index 94% rename from examples/jsm/nodes/core/FunctionCallNode.js rename to examples/jsm/nodes/code/FunctionCallNode.js index fc0b0c4e1633a1..b397c929f6e2c6 100644 --- a/examples/jsm/nodes/core/FunctionCallNode.js +++ b/examples/jsm/nodes/code/FunctionCallNode.js @@ -1,5 +1,5 @@ -import TempNode from './TempNode.js'; -import { addNodeClass } from './Node.js'; +import TempNode from '../core/TempNode.js'; +import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeArray, nodeObject, nodeObjects } from '../shadernode/ShaderNode.js'; class FunctionCallNode extends TempNode { diff --git a/examples/jsm/nodes/core/FunctionNode.js b/examples/jsm/nodes/code/FunctionNode.js similarity index 97% rename from examples/jsm/nodes/core/FunctionNode.js rename to examples/jsm/nodes/code/FunctionNode.js index 3c762ed6741157..f6009848cb1d5f 100644 --- a/examples/jsm/nodes/core/FunctionNode.js +++ b/examples/jsm/nodes/code/FunctionNode.js @@ -1,5 +1,5 @@ import CodeNode from './CodeNode.js'; -import { addNodeClass } from './Node.js'; +import { addNodeClass } from '../core/Node.js'; import { nodeObject } from '../shadernode/ShaderNode.js'; class FunctionNode extends CodeNode { diff --git a/examples/jsm/nodes/code/ScriptableNode.js b/examples/jsm/nodes/code/ScriptableNode.js new file mode 100644 index 00000000000000..6ad98e21029fec --- /dev/null +++ b/examples/jsm/nodes/code/ScriptableNode.js @@ -0,0 +1,488 @@ +import Node, { addNodeClass } from '../core/Node.js'; +import { scriptableValue } from './ScriptableValueNode.js'; +import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; + +class Resources extends Map { + + get( key, callback = null, ...params ) { + + if ( this.has( key ) ) return super.get( key ); + + if ( callback !== null ) { + + const value = callback( ...params ); + this.set( key, value ); + return value; + + } + + } + +} + +class Parameters { + + constructor( scriptableNode ) { + + this.scriptableNode = scriptableNode; + + } + + get parameters() { + + return this.scriptableNode.parameters; + + } + + get layout() { + + return this.scriptableNode.getLayout(); + + } + + getInputLayout( id ) { + + return this.scriptableNode.getInputLayout( id ); + + } + + get( name ) { + + const param = this.parameters[ name ]; + const value = param ? param.getValue() : null; + + return value; + + } + +} + +export const global = new Resources(); + +class ScriptableNode extends Node { + + constructor( codeNode = null, parameters = {} ) { + + super(); + + this.codeNode = codeNode; + this.parameters = parameters; + + this._local = new Resources(); + this._output = scriptableValue(); + this._outputs = {}; + this._source = this.source; + this._method = null; + this._object = null; + this._value = null; + this._needsOutputUpdate = true; + + this.onRefresh = this.onRefresh.bind( this ); + + this.isScriptableNode = true; + + } + + get source() { + + return this.codeNode ? this.codeNode.code : ''; + + } + + setLocal( name, value ) { + + return this._local.set( name, value ); + + } + + getLocal( name ) { + + return this._local.get( name ); + + } + + onRefresh() { + + this._refresh(); + + } + + getInputLayout( id ) { + + for ( const element of this.getLayout() ) { + + if ( element.inputType && ( element.id === id || element.name === id ) ) { + + return element; + + } + + } + + } + + getOutputLayout( id ) { + + for ( const element of this.getLayout() ) { + + if ( element.outputType && ( element.id === id || element.name === id ) ) { + + return element; + + } + + } + + } + + setOutput( name, value ) { + + const outputs = this._outputs; + + if ( outputs[ name ] === undefined ) { + + outputs[ name ] = scriptableValue( value ); + + } else { + + outputs[ name ].value = value; + + } + + return this; + + } + + getOutput( name ) { + + return this._outputs[ name ]; + + } + + getParameter( name ) { + + return this.parameters[ name ]; + + } + + setParameter( name, value ) { + + const parameters = this.parameters; + + if ( value && value.isScriptableNode ) { + + this.deleteParameter( name ); + + parameters[ name ] = value; + parameters[ name ].getDefaultOutput().events.addEventListener( 'refresh', this.onRefresh ); + + } else if ( value && value.isScriptableValueNode ) { + + this.deleteParameter( name ); + + parameters[ name ] = value; + parameters[ name ].events.addEventListener( 'refresh', this.onRefresh ); + + } else if ( parameters[ name ] === undefined ) { + + parameters[ name ] = scriptableValue( value ); + parameters[ name ].events.addEventListener( 'refresh', this.onRefresh ); + + } else { + + parameters[ name ].value = value; + + } + + return this; + + } + + getValue() { + + return this.getDefaultOutput().getValue(); + + } + + deleteParameter( name ) { + + let valueNode = this.parameters[ name ]; + + if ( valueNode ) { + + if ( valueNode.isScriptableNode ) valueNode = valueNode.getDefaultOutput(); + + valueNode.events.removeEventListener( 'refresh', this.onRefresh ); + + } + + return this; + + } + + clearParameters() { + + for ( const name of Object.keys( this.parameters ) ) { + + this.deleteParameter( name ); + + } + + this.needsUpdate = true; + + return this; + + } + + call( name, ...params ) { + + const object = this.getObject(); + const method = object[ name ]; + + if ( typeof method === 'function' ) { + + return method( ...params ); + + } + + } + + async callAsync( name, ...params ) { + + const object = this.getObject(); + const method = object[ name ]; + + if ( typeof method === 'function' ) { + + return method.constructor.name === 'AsyncFunction' ? await method( ...params ) : method( ...params ); + + } + + } + + getNodeType( builder ) { + + return this.getDefaultOutputNode().getNodeType( builder ); + + } + + refresh( output = null ) { + + if ( output !== null ) { + + this.getOutput( output ).refresh(); + + } else { + + this._refresh(); + + } + + } + + getObject() { + + if ( this.needsUpdate ) this.dispose(); + if ( this._object !== null ) return this._object; + + // + + const refresh = () => this.refresh(); + const setOutput = ( id, value ) => this.setOutput( id, value ); + + const parameters = new Parameters( this ); + + const THREE = global.get( 'THREE' ); + const TSL = global.get( 'TSL' ); + + const method = this.getMethod( this.codeNode ); + const params = [ parameters, this._local, global, refresh, setOutput, THREE, TSL ]; + + this._object = method( ...params ); + + const layout = this._object.layout; + + if ( layout ) { + + if ( layout.cache === false ) { + + this._local.clear(); + + } + + // default output + this._output.outputType = layout.outputType || null; + + if ( Array.isArray( layout.elements ) ) { + + for ( const element of layout.elements ) { + + const id = element.id || element.name; + + if ( element.inputType ) { + + if ( this.getParameter( id ) === undefined ) this.setParameter( id, null ); + + this.getParameter( id ).inputType = element.inputType; + + } + + if ( element.outputType ) { + + if ( this.getOutput( id ) === undefined ) this.setOutput( id, null ); + + this.getOutput( id ).outputType = element.outputType; + + } + + } + + } + + } + + return this._object; + + } + + deserialize( data ) { + + super.deserialize( data ); + + for ( const name in this.parameters ) { + + let valueNode = this.parameters[ name ]; + + if ( valueNode.isScriptableNode ) valueNode = valueNode.getDefaultOutput(); + + valueNode.events.addEventListener( 'refresh', this.onRefresh ); + + } + + } + + getLayout() { + + return this.getObject().layout; + + } + + getDefaultOutputNode() { + + const output = this.getDefaultOutput().value; + + if ( output && output.isNode ) { + + return output; + + } + + return float(); + + } + + getDefaultOutput() { + + return this._exec()._output; + + } + + getMethod() { + + if ( this.needsUpdate ) this.dispose(); + if ( this._method !== null ) return this._method; + + // + + const parametersProps = [ 'parameters', 'local', 'global', 'refresh', 'setOutput', 'THREE', 'TSL' ]; + const interfaceProps = [ 'layout', 'init', 'main', 'dispose' ]; + + const properties = interfaceProps.join( ', ' ); + const declarations = 'var ' + properties + '; var output = {};\n'; + const returns = '\nreturn { ...output, ' + properties + ' };'; + + const code = declarations + this.codeNode.code + returns; + + // + + this._method = new Function( ...parametersProps, code ); + + return this._method; + + } + + dispose() { + + if ( this._method === null ) return; + + if ( this._object && typeof this._object.dispose === 'function' ) { + + this._object.dispose(); + + } + + this._method = null; + this._object = null; + this._source = null; + this._value = null; + this._needsOutputUpdate = true; + this._output.value = null; + this._outputs = {}; + + } + + construct() { + + return this.getDefaultOutputNode(); + + } + + set needsUpdate( value ) { + + if ( value === true ) this.dispose(); + + } + + get needsUpdate() { + + return this.source !== this._source; + + } + + _exec() { + + if ( this.codeNode === null ) return this; + + if ( this._needsOutputUpdate === true ) { + + this._value = this.call( 'main' ); + + this._needsOutputUpdate = false; + + } + + this._output.value = this._value; + + return this; + + } + + _refresh() { + + this.needsUpdate = true; + + this._exec(); + + this._output.refresh(); + + } + +} + +export default ScriptableNode; + +export const scriptable = nodeProxy( ScriptableNode ); + +addNodeElement( 'scriptable', scriptable ); + +addNodeClass( ScriptableNode ); diff --git a/examples/jsm/nodes/code/ScriptableValueNode.js b/examples/jsm/nodes/code/ScriptableValueNode.js new file mode 100644 index 00000000000000..8ace150c680fb6 --- /dev/null +++ b/examples/jsm/nodes/code/ScriptableValueNode.js @@ -0,0 +1,167 @@ +import Node, { addNodeClass } from '../core/Node.js'; +import { arrayBufferToBase64, base64ToArrayBuffer } from '../core/NodeUtils.js'; +import { addNodeElement, nodeProxy, float } from '../shadernode/ShaderNode.js'; +import { EventDispatcher } from 'three'; + +class ScriptableValueNode extends Node { + + constructor( value = null ) { + + super(); + + this._value = value; + this._cache = null; + + this.inputType = null; + this.outpuType = null; + + this.events = new EventDispatcher(); + + this.isScriptableValueNode = true; + + } + + get isScriptableOutputNode() { + + return this.outputType !== null; + + } + + set value( val ) { + + if ( this._value === val ) return; + + if ( this._cache && this.inputType === 'URL' && this.value.value instanceof ArrayBuffer ) { + + URL.revokeObjectURL( this._cache ); + + this._cache = null; + + } + + this._value = val; + + this.events.dispatchEvent( { type: 'change' } ); + + this.refresh(); + + } + + get value() { + + return this._value; + + } + + refresh() { + + this.events.dispatchEvent( { type: 'refresh' } ); + + } + + getValue() { + + const value = this.value; + + if ( value && this._cache === null && this.inputType === 'URL' && value.value instanceof ArrayBuffer ) { + + this._cache = URL.createObjectURL( new Blob( [ value.value ] ) ); + + } else if ( value && value.value !== null && value.value !== undefined && ( + ( ( this.inputType === 'URL' || this.inputType === 'String' ) && typeof value.value === 'string' ) || + ( this.inputType === 'Number' && typeof value.value === 'number' ) || + ( this.inputType === 'Vector2' && value.value.isVector2 ) || + ( this.inputType === 'Vector3' && value.value.isVector3 ) || + ( this.inputType === 'Vector4' && value.value.isVector4 ) || + ( this.inputType === 'Color' && value.value.isColor ) || + ( this.inputType === 'Matrix3' && value.value.isMatrix3 ) || + ( this.inputType === 'Matrix4' && value.value.isMatrix4 ) + ) ) { + + return value.value; + + } + + return this._cache || value; + + } + + getNodeType( builder ) { + + return this.value && this.value.isNode ? this.value.getNodeType( builder ) : 'float'; + + } + + construct() { + + return this.value && this.value.isNode ? this.value : float(); + + } + + serialize( data ) { + + super.serialize( data ); + + if ( this.value !== null ) { + + if ( this.inputType === 'ArrayBuffer' ) { + + data.value = arrayBufferToBase64( this.value ); + + } else { + + data.value = this.value ? this.value.toJSON( data.meta ).uuid : null; + + } + + } else { + + data.value = null; + + } + + data.inputType = this.inputType; + data.outputType = this.outputType; + + } + + deserialize( data ) { + + super.deserialize( data ); + + let value = null; + + if ( data.value !== null ) { + + if ( data.inputType === 'ArrayBuffer' ) { + + value = base64ToArrayBuffer( data.value ); + + } else if ( data.inputType === 'Texture' ) { + + value = data.meta.textures[ data.value ]; + + } else { + + value = data.meta.nodes[ data.value ] || null; + + } + + } + + this.value = value; + + this.inputType = data.inputType; + this.outputType = data.outputType; + + } + +} + +export default ScriptableValueNode; + +export const scriptableValue = nodeProxy( ScriptableValueNode ); + +addNodeElement( 'scriptableValue', scriptableValue ); + +addNodeClass( ScriptableValueNode ); diff --git a/examples/jsm/nodes/core/InputNode.js b/examples/jsm/nodes/core/InputNode.js index 511b2a02c16222..3bcf14fe9fc898 100644 --- a/examples/jsm/nodes/core/InputNode.js +++ b/examples/jsm/nodes/core/InputNode.js @@ -1,5 +1,5 @@ import Node, { addNodeClass } from './Node.js'; -import { getValueType, getValueFromType } from './NodeUtils.js'; +import { getValueType, getValueFromType, arrayBufferToBase64 } from './NodeUtils.js'; class InputNode extends Node { @@ -51,6 +51,8 @@ class InputNode extends Node { data.valueType = getValueType( this.value ); data.nodeType = this.nodeType; + if ( data.valueType === 'ArrayBuffer' ) data.value = arrayBufferToBase64( data.value ); + data.precision = this.precision; } diff --git a/examples/jsm/nodes/core/Node.js b/examples/jsm/nodes/core/Node.js index 33dba30f82068b..a53a564e8d6c10 100644 --- a/examples/jsm/nodes/core/Node.js +++ b/examples/jsm/nodes/core/Node.js @@ -239,9 +239,15 @@ class Node { } + getSerializeChildren() { + + return getNodeChildren( this ); + + } + serialize( json ) { - const nodeChildren = getNodeChildren( this ); + const nodeChildren = this.getSerializeChildren(); const inputNodes = {}; diff --git a/examples/jsm/nodes/core/NodeUtils.js b/examples/jsm/nodes/core/NodeUtils.js index eb3f4b5aafc300..df38767fcbb867 100644 --- a/examples/jsm/nodes/core/NodeUtils.js +++ b/examples/jsm/nodes/core/NodeUtils.js @@ -1,6 +1,6 @@ import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three'; -export function getCacheKey( object ) { +export function getCacheKey( object ) { let cacheKey = '{'; @@ -26,19 +26,22 @@ export function getCacheKey( object ) { } -export function* getNodeChildren( node ) { +export function* getNodeChildren( node, toJSON = false ) { for ( const property in node ) { + // Ignore private properties. + if ( property.startsWith( '_' ) === true ) continue; + const object = node[ property ]; if ( Array.isArray( object ) === true ) { - for ( let i = 0; i < object.length; i++ ) { + for ( let i = 0; i < object.length; i ++ ) { const child = object[ i ]; - if ( child && child.isNode === true ) { + if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) { yield { property, index: i, childNode: child }; @@ -56,7 +59,7 @@ export function* getNodeChildren( node ) { const child = object[ subProperty ]; - if ( child && child.isNode === true ) { + if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) { yield { property, index: subProperty, childNode: child }; @@ -72,38 +75,50 @@ export function* getNodeChildren( node ) { export function getValueType( value ) { - if ( typeof value === 'number' ) { + if ( value === undefined || value === null ) return null; + + const typeOf = typeof value; + + if ( typeOf === 'number' ) { return 'float'; - } else if ( typeof value === 'boolean' ) { + } else if ( typeOf === 'boolean' ) { return 'bool'; - } else if ( value && value.isVector2 === true ) { + } else if ( typeOf === 'string' ) { + + return 'string'; + + } else if ( value.isVector2 === true ) { return 'vec2'; - } else if ( value && value.isVector3 === true ) { + } else if ( value.isVector3 === true ) { return 'vec3'; - } else if ( value && value.isVector4 === true ) { + } else if ( value.isVector4 === true ) { return 'vec4'; - } else if ( value && value.isMatrix3 === true ) { + } else if ( value.isMatrix3 === true ) { return 'mat3'; - } else if ( value && value.isMatrix4 === true ) { + } else if ( value.isMatrix4 === true ) { return 'mat4'; - } else if ( value && value.isColor === true ) { + } else if ( value.isColor === true ) { return 'color'; + } else if ( value instanceof ArrayBuffer ) { + + return 'ArrayBuffer'; + } return null; @@ -146,8 +161,38 @@ export function getValueFromType( type, ...params ) { return params[ 0 ] || 0; + } else if ( type === 'string' ) { + + return params[ 0 ] || ''; + + } else if ( type === 'ArrayBuffer' ) { + + return base64ToArrayBuffer( params[ 0 ] ); + } return null; } + +export function arrayBufferToBase64( arrayBuffer ) { + + let chars = ''; + + const array = new Uint8Array( arrayBuffer ); + + for ( let i = 0; i < array.length; i ++ ) { + + chars += String.fromCharCode( array[ i ] ); + + } + + return btoa( chars ); + +} + +export function base64ToArrayBuffer( base64 ) { + + return Uint8Array.from( atob( base64 ), c => c.charCodeAt( 0 ) ).buffer; + +} diff --git a/examples/jsm/nodes/core/StackNode.js b/examples/jsm/nodes/core/StackNode.js index 6b4a4a531a639a..8d7b2d99aaa994 100644 --- a/examples/jsm/nodes/core/StackNode.js +++ b/examples/jsm/nodes/core/StackNode.js @@ -1,7 +1,7 @@ import Node, { addNodeClass } from './Node.js'; import { assign } from '../math/OperatorNode.js'; import { bypass } from '../core/BypassNode.js'; -import { expression } from '../core/ExpressionNode.js'; +import { expression } from '../code/ExpressionNode.js'; import { cond } from '../math/CondNode.js'; import { nodeProxy, shader } from '../shadernode/ShaderNode.js'; diff --git a/examples/jsm/nodes/materialx/lib/mx_hsv.js b/examples/jsm/nodes/materialx/lib/mx_hsv.js index eda0b415ae2625..11c27f58f33d8c 100644 --- a/examples/jsm/nodes/materialx/lib/mx_hsv.js +++ b/examples/jsm/nodes/materialx/lib/mx_hsv.js @@ -1,4 +1,4 @@ -import { fn } from '../../core/FunctionNode.js'; +import { fn } from '../../code/FunctionNode.js'; // Original shader code from: // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_hsv.glsl diff --git a/examples/jsm/nodes/materialx/lib/mx_noise.js b/examples/jsm/nodes/materialx/lib/mx_noise.js index 7063c00be35f19..3442d512881198 100644 --- a/examples/jsm/nodes/materialx/lib/mx_noise.js +++ b/examples/jsm/nodes/materialx/lib/mx_noise.js @@ -1,5 +1,5 @@ -import { code } from '../../core/CodeNode.js'; -import { fn } from '../../core/FunctionNode.js'; +import { code } from '../../code/CodeNode.js'; +import { fn } from '../../code/FunctionNode.js'; // Original shader code from: // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl diff --git a/examples/jsm/nodes/materialx/lib/mx_transform_color.js b/examples/jsm/nodes/materialx/lib/mx_transform_color.js index 9aed929d4c9424..c99fa146e5c2e3 100644 --- a/examples/jsm/nodes/materialx/lib/mx_transform_color.js +++ b/examples/jsm/nodes/materialx/lib/mx_transform_color.js @@ -1,5 +1,5 @@ -import { code } from '../../core/CodeNode.js'; -import { fn } from '../../core/FunctionNode.js'; +import { code } from '../../code/CodeNode.js'; +import { fn } from '../../code/FunctionNode.js'; // Original shader code from: // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_transform_color.glsl diff --git a/examples/jsm/nodes/shadernode/ShaderNode.js b/examples/jsm/nodes/shadernode/ShaderNode.js index 0f402da9e3ee3d..6f5190e39876e9 100644 --- a/examples/jsm/nodes/shadernode/ShaderNode.js +++ b/examples/jsm/nodes/shadernode/ShaderNode.js @@ -4,7 +4,7 @@ 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'; +import { getValueFromType, getValueType } from '../core/NodeUtils.js'; const NodeElements = new Map(); // @TODO: Currently only a few nodes are added, probably also add others @@ -281,7 +281,7 @@ const ConvertType = function ( type, cacheMap = null ) { if ( nodes.length === 1 ) { - return nodeObject( nodes[ 0 ].nodeType === type ? nodes[ 0 ] : new ConvertNode( nodes[ 0 ], type ) ); + return nodeObject( nodes[ 0 ].nodeType === type || getValueType( nodes[ 0 ].value ) === type ? nodes[ 0 ] : new ConvertNode( nodes[ 0 ], type ) ); } @@ -352,6 +352,9 @@ export const imat4 = new ConvertType( 'imat4' ); export const umat4 = new ConvertType( 'umat4' ); export const bmat4 = new ConvertType( 'bmat4' ); +export const string = ( value = '' ) => nodeObject( new ConstNode( value, 'string' ) ); +export const arrayBuffer = ( value ) => nodeObject( new ConstNode( value, 'ArrayBuffer' ) ); + addNodeElement( 'color', color ); addNodeElement( 'float', float ); addNodeElement( 'int', int ); @@ -377,6 +380,8 @@ addNodeElement( 'mat4', mat4 ); addNodeElement( 'imat4', imat4 ); addNodeElement( 'umat4', umat4 ); addNodeElement( 'bmat4', bmat4 ); +addNodeElement( 'string', string ); +addNodeElement( 'arrayBuffer', arrayBuffer ); // basic nodes // HACK - we cannot export them from the corresponding files because of the cyclic dependency diff --git a/examples/jsm/nodes/utils/DiscardNode.js b/examples/jsm/nodes/utils/DiscardNode.js index b5ef1a89fb8de1..c52fdafcb490a1 100644 --- a/examples/jsm/nodes/utils/DiscardNode.js +++ b/examples/jsm/nodes/utils/DiscardNode.js @@ -1,5 +1,5 @@ import CondNode from '../math/CondNode.js'; -import { expression } from '../core/ExpressionNode.js'; +import { expression } from '../code/ExpressionNode.js'; import { addNodeClass } from '../core/Node.js'; import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js'; diff --git a/examples/webgpu_materials.html b/examples/webgpu_materials.html index 0aa8ec5017278f..f9473564471d58 100644 --- a/examples/webgpu_materials.html +++ b/examples/webgpu_materials.html @@ -27,7 +27,9 @@