You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
5.9 KiB
277 lines
5.9 KiB
import Node from '../core/Node.js';
|
|
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';
|
|
|
|
const shaderNodeHandler = {
|
|
|
|
construct( NodeClosure, params ) {
|
|
|
|
const inputs = params.shift();
|
|
|
|
return NodeClosure( nodeObjects( 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 nodeObject( new SplitNode( node, prop ) );
|
|
|
|
} else if ( /^\d+$/.test( prop ) === true ) {
|
|
|
|
// accessing array
|
|
|
|
return nodeObject( new ArrayElementNode( node, new ConstNode( Number( prop ), 'uint' ) ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node[ prop ];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const nodeObjectsCacheMap = new WeakMap();
|
|
|
|
const ShaderNodeObject = function ( obj ) {
|
|
|
|
const type = typeof obj;
|
|
|
|
if ( ( type === 'number' ) || ( type === 'boolean' ) ) {
|
|
|
|
return nodeObject( getAutoTypedConstNode( obj ) );
|
|
|
|
} else if ( type === 'object' ) {
|
|
|
|
if ( obj?.isNode === true ) {
|
|
|
|
let nodeObject = nodeObjectsCacheMap.get( obj );
|
|
|
|
if ( nodeObject === undefined ) {
|
|
|
|
nodeObject = new Proxy( obj, shaderNodeHandler );
|
|
nodeObjectsCacheMap.set( obj, nodeObject );
|
|
nodeObjectsCacheMap.set( nodeObject, nodeObject );
|
|
|
|
}
|
|
|
|
return nodeObject;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
};
|
|
|
|
const ShaderNodeObjects = function ( objects ) {
|
|
|
|
for ( const name in objects ) {
|
|
|
|
objects[ name ] = nodeObject( objects[ name ] );
|
|
|
|
}
|
|
|
|
return objects;
|
|
|
|
};
|
|
|
|
const ShaderNodeArray = function ( array ) {
|
|
|
|
const len = array.length;
|
|
|
|
for ( let i = 0; i < len; i ++ ) {
|
|
|
|
array[ i ] = nodeObject( array[ i ] );
|
|
|
|
}
|
|
|
|
return array;
|
|
|
|
};
|
|
|
|
const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null, settings = null ) {
|
|
|
|
const assignNode = ( node ) => nodeObject( settings !== null ? Object.assign( node, settings ) : node );
|
|
|
|
if ( scope === null ) {
|
|
|
|
return ( ...params ) => {
|
|
|
|
return assignNode( new NodeClass( ...nodeArray( params ) ) );
|
|
|
|
};
|
|
|
|
} else if ( factor !== null ) {
|
|
|
|
factor = nodeObject( factor );
|
|
|
|
return ( ...params ) => {
|
|
|
|
return assignNode( new NodeClass( scope, ...nodeArray( params ), factor ) );
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return ( ...params ) => {
|
|
|
|
return assignNode( new NodeClass( scope, ...nodeArray( params ) ) );
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const ShaderNodeImmutable = function ( NodeClass, ...params ) {
|
|
|
|
return nodeObject( new NodeClass( ...nodeArray( params ) ) );
|
|
|
|
};
|
|
|
|
class ShaderNodeInternal extends Node {
|
|
|
|
constructor( jsFunc ) {
|
|
|
|
super();
|
|
|
|
this._jsFunc = jsFunc;
|
|
|
|
}
|
|
|
|
call( inputs, builder ) {
|
|
|
|
inputs = nodeObjects( inputs );
|
|
|
|
return nodeObject( this._jsFunc( inputs, builder ) );
|
|
|
|
}
|
|
|
|
generate( builder, output ) {
|
|
|
|
const nodeCall = this.call( {}, builder );
|
|
|
|
if ( nodeCall === undefined ) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
return builder.format( nodeCall.build( builder ), nodeCall.getNodeType( builder ), output );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const ShaderNodeScript = function ( jsFunc ) {
|
|
|
|
return new ShaderNodeInternal( jsFunc );
|
|
|
|
};
|
|
|
|
export const ShaderNode = new Proxy( ShaderNodeScript, shaderNodeHandler );
|
|
|
|
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 );
|
|
export const nodeImmutable = ( ...val ) => new ShaderNodeImmutable( ...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 ];
|
|
|
|
const boolsCacheMap = new Map();
|
|
for ( const bool of bools ) boolsCacheMap.set( bool, new ConstNode( bool ) );
|
|
|
|
const uintsCacheMap = new Map();
|
|
for ( const uint of uints ) uintsCacheMap.set( uint, new ConstNode( uint, 'uint' ) );
|
|
|
|
const intsCacheMap = new Map( [ ...uintsCacheMap ].map( el => new ConstNode( el.value, 'int' ) ) );
|
|
for ( const int of ints ) intsCacheMap.set( int, new ConstNode( int, 'int' ) );
|
|
|
|
const floatsCacheMap = new Map( [ ...intsCacheMap ].map( el => new ConstNode( el.value ) ) );
|
|
for ( const float of floats ) floatsCacheMap.set( float, new ConstNode( float ) );
|
|
for ( const float of floats ) floatsCacheMap.set( - float, new ConstNode( - float ) );
|
|
|
|
export const cacheMaps = { bool: boolsCacheMap, uint: uintsCacheMap, ints: intsCacheMap, float: floatsCacheMap };
|
|
|
|
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 JoinNode( nodes, type ) );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
export const getConstNodeType = ( value ) => value.nodeType || value.convertTo || ( typeof value === 'string' ? value : null );
|
|
|