添加关照、全局等高线、修改图层问题

This commit is contained in:
2025-07-17 18:54:05 +08:00
parent c781d38c0c
commit b274b62671
4594 changed files with 791769 additions and 4921 deletions

View File

@ -0,0 +1,128 @@
import { addNodeClass } from '../core/Node.js';
import TempNode from '../core/TempNode.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
import { vectorComponents } from '../core/constants.js';
class AssignNode extends TempNode {
constructor( targetNode, sourceNode ) {
super();
this.targetNode = targetNode;
this.sourceNode = sourceNode;
}
hasDependencies() {
return false;
}
getNodeType( builder, output ) {
return output !== 'void' ? this.targetNode.getNodeType( builder ) : 'void';
}
needsSplitAssign( builder ) {
const { targetNode } = this;
if ( builder.isAvailable( 'swizzleAssign' ) === false && targetNode.isSplitNode && targetNode.components.length > 1 ) {
const targetLength = builder.getTypeLength( targetNode.node.getNodeType( builder ) );
const assignDiferentVector = vectorComponents.join( '' ).slice( 0, targetLength ) !== targetNode.components;
return assignDiferentVector;
}
return false;
}
generate( builder, output ) {
const { targetNode, sourceNode } = this;
const needsSplitAssign = this.needsSplitAssign( builder );
const targetType = targetNode.getNodeType( builder );
const target = targetNode.context( { assign: true } ).build( builder );
const source = sourceNode.build( builder, targetType );
const sourceType = sourceNode.getNodeType( builder );
const nodeData = builder.getDataFromNode( this );
//
let snippet;
if ( nodeData.initialized === true ) {
if ( output !== 'void' ) {
snippet = target;
}
} else if ( needsSplitAssign ) {
const sourceVar = builder.getVarFromNode( this, null, targetType );
const sourceProperty = builder.getPropertyName( sourceVar );
builder.addLineFlowCode( `${ sourceProperty } = ${ source }` );
const targetRoot = targetNode.node.context( { assign: true } ).build( builder );
for ( let i = 0; i < targetNode.components.length; i ++ ) {
const component = targetNode.components[ i ];
builder.addLineFlowCode( `${ targetRoot }.${ component } = ${ sourceProperty }[ ${ i } ]` );
}
if ( output !== 'void' ) {
snippet = target;
}
} else {
snippet = `${ target } = ${ source }`;
if ( output === 'void' || sourceType === 'void' ) {
builder.addLineFlowCode( snippet );
if ( output !== 'void' ) {
snippet = target;
}
}
}
nodeData.initialized = true;
return builder.format( snippet, targetType, output );
}
}
export default AssignNode;
export const assign = nodeProxy( AssignNode );
addNodeClass( 'AssignNode', AssignNode );
addNodeElement( 'assign', assign );

View File

@ -0,0 +1,108 @@
import Node, { addNodeClass } from './Node.js';
import { varying } from './VaryingNode.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
class AttributeNode extends Node {
constructor( attributeName, nodeType = null ) {
super( nodeType );
this._attributeName = attributeName;
}
isGlobal() {
return true;
}
getHash( builder ) {
return this.getAttributeName( builder );
}
getNodeType( builder ) {
let nodeType = super.getNodeType( builder );
if ( nodeType === null ) {
const attributeName = this.getAttributeName( builder );
if ( builder.hasGeometryAttribute( attributeName ) ) {
const attribute = builder.geometry.getAttribute( attributeName );
nodeType = builder.getTypeFromAttribute( attribute );
} else {
nodeType = 'float';
}
}
return nodeType;
}
setAttributeName( attributeName ) {
this._attributeName = attributeName;
return this;
}
getAttributeName( /*builder*/ ) {
return this._attributeName;
}
generate( builder ) {
const attributeName = this.getAttributeName( builder );
const nodeType = this.getNodeType( builder );
const geometryAttribute = builder.hasGeometryAttribute( attributeName );
if ( geometryAttribute === true ) {
const attribute = builder.geometry.getAttribute( attributeName );
const attributeType = builder.getTypeFromAttribute( attribute );
const nodeAttribute = builder.getAttribute( attributeName, attributeType );
if ( builder.shaderStage === 'vertex' ) {
return builder.format( nodeAttribute.name, attributeType, nodeType );
} else {
const nodeVarying = varying( this );
return nodeVarying.build( builder, nodeType );
}
} else {
console.warn( `AttributeNode: Vertex attribute "${ attributeName }" not found on geometry.` );
return builder.generateConst( nodeType );
}
}
}
export default AttributeNode;
export const attribute = ( name, nodeType ) => nodeObject( new AttributeNode( name, nodeType ) );
addNodeClass( 'AttributeNode', AttributeNode );

View File

@ -0,0 +1,45 @@
import Node, { addNodeClass } from './Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class BypassNode extends Node {
constructor( returnNode, callNode ) {
super();
this.isBypassNode = true;
this.outputNode = returnNode;
this.callNode = callNode;
}
getNodeType( builder ) {
return this.outputNode.getNodeType( builder );
}
generate( builder ) {
const snippet = this.callNode.build( builder, 'void' );
if ( snippet !== '' ) {
builder.addLineFlowCode( snippet );
}
return this.outputNode.build( builder );
}
}
export default BypassNode;
export const bypass = nodeProxy( BypassNode );
addNodeElement( 'bypass', bypass );
addNodeClass( 'BypassNode', BypassNode );

View File

@ -0,0 +1,49 @@
import Node, { addNodeClass } from './Node.js';
import NodeCache from './NodeCache.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class CacheNode extends Node {
constructor( node, cache = new NodeCache() ) {
super();
this.isCacheNode = true;
this.node = node;
this.cache = cache;
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
build( builder, ...params ) {
const previousCache = builder.getCache();
const cache = this.cache || builder.globalCache;
builder.setCache( cache );
const data = this.node.build( builder, ...params );
builder.setCache( previousCache );
return data;
}
}
export default CacheNode;
export const cache = nodeProxy( CacheNode );
export const globalCache = ( node ) => cache( node, null );
addNodeElement( 'cache', cache );
addNodeElement( 'globalCache', globalCache );
addNodeClass( 'CacheNode', CacheNode );

View File

@ -0,0 +1,32 @@
import InputNode from './InputNode.js';
import { addNodeClass } from './Node.js';
class ConstNode extends InputNode {
constructor( value, nodeType = null ) {
super( value, nodeType );
this.isConstNode = true;
}
generateConst( builder ) {
return builder.generateConst( this.getNodeType( builder ), this.value );
}
generate( builder, output ) {
const type = this.getNodeType( builder );
return builder.format( this.generateConst( builder ), type, output );
}
}
export default ConstNode;
addNodeClass( 'ConstNode', ConstNode );

View File

@ -0,0 +1,61 @@
import Node, { addNodeClass } from './Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class ContextNode extends Node {
constructor( node, context = {} ) {
super();
this.isContextNode = true;
this.node = node;
this.context = context;
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
setup( builder ) {
const previousContext = builder.getContext();
builder.setContext( { ...builder.context, ...this.context } );
const node = this.node.build( builder );
builder.setContext( previousContext );
return node;
}
generate( builder, output ) {
const previousContext = builder.getContext();
builder.setContext( { ...builder.context, ...this.context } );
const snippet = this.node.build( builder, output );
builder.setContext( previousContext );
return snippet;
}
}
export default ContextNode;
export const context = nodeProxy( ContextNode );
export const label = ( node, name ) => context( node, { label: name } );
addNodeElement( 'context', context );
addNodeElement( 'label', label );
addNodeClass( 'ContextNode', ContextNode );

View File

@ -0,0 +1,66 @@
import Node, { addNodeClass } from './Node.js';
import { varying } from './VaryingNode.js';
import { nodeImmutable } from '../shadernode/ShaderNode.js';
class IndexNode extends Node {
constructor( scope ) {
super( 'uint' );
this.scope = scope;
this.isInstanceIndexNode = true;
}
generate( builder ) {
const nodeType = this.getNodeType( builder );
const scope = this.scope;
let propertyName;
if ( scope === IndexNode.VERTEX ) {
propertyName = builder.getVertexIndex();
} else if ( scope === IndexNode.INSTANCE ) {
propertyName = builder.getInstanceIndex();
} else {
throw new Error( 'THREE.IndexNode: Unknown scope: ' + scope );
}
let output;
if ( builder.shaderStage === 'vertex' || builder.shaderStage === 'compute' ) {
output = propertyName;
} else {
const nodeVarying = varying( this );
output = nodeVarying.build( builder, nodeType );
}
return output;
}
}
IndexNode.VERTEX = 'vertex';
IndexNode.INSTANCE = 'instance';
export default IndexNode;
export const vertexIndex = nodeImmutable( IndexNode, IndexNode.VERTEX );
export const instanceIndex = nodeImmutable( IndexNode, IndexNode.INSTANCE );
addNodeClass( 'IndexNode', IndexNode );

View File

@ -0,0 +1,83 @@
import Node, { addNodeClass } from './Node.js';
import { getValueType, getValueFromType, arrayBufferToBase64 } from './NodeUtils.js';
class InputNode extends Node {
constructor( value, nodeType = null ) {
super( nodeType );
this.isInputNode = true;
this.value = value;
this.precision = null;
}
getNodeType( /*builder*/ ) {
if ( this.nodeType === null ) {
return getValueType( this.value );
}
return this.nodeType;
}
getInputType( builder ) {
return this.getNodeType( builder );
}
setPrecision( precision ) {
this.precision = precision;
return this;
}
serialize( data ) {
super.serialize( data );
data.value = this.value;
if ( this.value && this.value.toArray ) data.value = this.value.toArray();
data.valueType = getValueType( this.value );
data.nodeType = this.nodeType;
if ( data.valueType === 'ArrayBuffer' ) data.value = arrayBufferToBase64( data.value );
data.precision = this.precision;
}
deserialize( data ) {
super.deserialize( data );
this.nodeType = data.nodeType;
this.value = Array.isArray( data.value ) ? getValueFromType( data.valueType, ...data.value ) : data.value;
this.precision = data.precision || null;
if ( this.value && this.value.fromArray ) this.value = this.value.fromArray( data.value );
}
generate( /*builder, output*/ ) {
console.warn( 'Abstract function.' );
}
}
export default InputNode;
addNodeClass( 'InputNode', InputNode );

View File

@ -0,0 +1,17 @@
class LightingModel {
start( /*input, stack, builder*/ ) { }
finish( /*input, stack, builder*/ ) { }
direct( /*input, stack, builder*/ ) { }
indirectDiffuse( /*input, stack, builder*/ ) { }
indirectSpecular( /*input, stack, builder*/ ) { }
ambientOcclusion( /*input, stack, builder*/ ) { }
}
export default LightingModel;

View File

@ -0,0 +1,552 @@
import { EventDispatcher } from 'three';
import { NodeUpdateType } from './constants.js';
import { getNodeChildren, getCacheKey } from './NodeUtils.js';
import { MathUtils } from 'three';
const NodeClasses = new Map();
let _nodeId = 0;
class Node extends EventDispatcher {
constructor( nodeType = null ) {
super();
this.nodeType = nodeType;
this.updateType = NodeUpdateType.NONE;
this.updateBeforeType = NodeUpdateType.NONE;
this.uuid = MathUtils.generateUUID();
this.version = 0;
this._cacheKey = null;
this._cacheKeyVersion = 0;
this.isNode = true;
Object.defineProperty( this, 'id', { value: _nodeId ++ } );
}
set needsUpdate( value ) {
if ( value === true ) {
this.version ++;
}
}
get type() {
return this.constructor.type;
}
onUpdate( callback, updateType ) {
this.updateType = updateType;
this.update = callback.bind( this.getSelf() );
return this;
}
onFrameUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.FRAME );
}
onRenderUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.RENDER );
}
onObjectUpdate( callback ) {
return this.onUpdate( callback, NodeUpdateType.OBJECT );
}
onReference( callback ) {
this.updateReference = callback.bind( this.getSelf() );
return this;
}
getSelf() {
// Returns non-node object.
return this.self || this;
}
updateReference( /*state*/ ) {
return this;
}
isGlobal( /*builder*/ ) {
return false;
}
* getChildren() {
for ( const { childNode } of getNodeChildren( this ) ) {
yield childNode;
}
}
dispose() {
this.dispatchEvent( { type: 'dispose' } );
}
traverse( callback ) {
callback( this );
for ( const childNode of this.getChildren() ) {
childNode.traverse( callback );
}
}
getCacheKey( force = false ) {
force = force || this.version !== this._cacheKeyVersion;
if ( force === true || this._cacheKey === null ) {
this._cacheKey = getCacheKey( this, force );
this._cacheKeyVersion = this.version;
}
return this._cacheKey;
}
getHash( /*builder*/ ) {
return this.uuid;
}
getUpdateType() {
return this.updateType;
}
getUpdateBeforeType() {
return this.updateBeforeType;
}
getNodeType( builder ) {
const nodeProperties = builder.getNodeProperties( this );
if ( nodeProperties.outputNode ) {
return nodeProperties.outputNode.getNodeType( builder );
}
return this.nodeType;
}
getShared( builder ) {
const hash = this.getHash( builder );
const nodeFromHash = builder.getNodeFromHash( hash );
return nodeFromHash || this;
}
setup( builder ) {
const nodeProperties = builder.getNodeProperties( this );
for ( const childNode of this.getChildren() ) {
nodeProperties[ '_node' + childNode.id ] = childNode;
}
// return a outputNode if exists
return null;
}
construct( builder ) { // @deprecated, r157
console.warn( 'THREE.Node: construct() is deprecated. Use setup() instead.' );
return this.setup( builder );
}
increaseUsage( builder ) {
const nodeData = builder.getDataFromNode( this );
nodeData.usageCount = nodeData.usageCount === undefined ? 1 : nodeData.usageCount + 1;
return nodeData.usageCount;
}
analyze( builder ) {
const usageCount = this.increaseUsage( builder );
if ( usageCount === 1 ) {
// node flow children
const nodeProperties = builder.getNodeProperties( this );
for ( const childNode of Object.values( nodeProperties ) ) {
if ( childNode && childNode.isNode === true ) {
childNode.build( builder );
}
}
}
}
generate( builder, output ) {
const { outputNode } = builder.getNodeProperties( this );
if ( outputNode && outputNode.isNode === true ) {
return outputNode.build( builder, output );
}
}
updateBefore( /*frame*/ ) {
console.warn( 'Abstract function.' );
}
update( /*frame*/ ) {
console.warn( 'Abstract function.' );
}
build( builder, output = null ) {
const refNode = this.getShared( builder );
if ( this !== refNode ) {
return refNode.build( builder, output );
}
builder.addNode( this );
builder.addChain( this );
/* Build stages expected results:
- "setup" -> Node
- "analyze" -> null
- "generate" -> String
*/
let result = null;
const buildStage = builder.getBuildStage();
if ( buildStage === 'setup' ) {
this.updateReference( builder );
const properties = builder.getNodeProperties( this );
if ( properties.initialized !== true || builder.context.tempRead === false ) {
const stackNodesBeforeSetup = builder.stack.nodes.length;
properties.initialized = true;
properties.outputNode = this.setup( builder );
if ( properties.outputNode !== null && builder.stack.nodes.length !== stackNodesBeforeSetup ) {
properties.outputNode = builder.stack;
}
for ( const childNode of Object.values( properties ) ) {
if ( childNode && childNode.isNode === true ) {
childNode.build( builder );
}
}
}
} else if ( buildStage === 'analyze' ) {
this.analyze( builder );
} else if ( buildStage === 'generate' ) {
const isGenerateOnce = this.generate.length === 1;
if ( isGenerateOnce ) {
const type = this.getNodeType( builder );
const nodeData = builder.getDataFromNode( this );
result = nodeData.snippet;
if ( result === undefined /*|| builder.context.tempRead === false*/ ) {
result = this.generate( builder ) || '';
nodeData.snippet = result;
}
result = builder.format( result, type, output );
} else {
result = this.generate( builder, output ) || '';
}
}
builder.removeChain( this );
return result;
}
getSerializeChildren() {
return getNodeChildren( this );
}
serialize( json ) {
const nodeChildren = this.getSerializeChildren();
const inputNodes = {};
for ( const { property, index, childNode } of nodeChildren ) {
if ( index !== undefined ) {
if ( inputNodes[ property ] === undefined ) {
inputNodes[ property ] = Number.isInteger( index ) ? [] : {};
}
inputNodes[ property ][ index ] = childNode.toJSON( json.meta ).uuid;
} else {
inputNodes[ property ] = childNode.toJSON( json.meta ).uuid;
}
}
if ( Object.keys( inputNodes ).length > 0 ) {
json.inputNodes = inputNodes;
}
}
deserialize( json ) {
if ( json.inputNodes !== undefined ) {
const nodes = json.meta.nodes;
for ( const property in json.inputNodes ) {
if ( Array.isArray( json.inputNodes[ property ] ) ) {
const inputArray = [];
for ( const uuid of json.inputNodes[ property ] ) {
inputArray.push( nodes[ uuid ] );
}
this[ property ] = inputArray;
} else if ( typeof json.inputNodes[ property ] === 'object' ) {
const inputObject = {};
for ( const subProperty in json.inputNodes[ property ] ) {
const uuid = json.inputNodes[ property ][ subProperty ];
inputObject[ subProperty ] = nodes[ uuid ];
}
this[ property ] = inputObject;
} else {
const uuid = json.inputNodes[ property ];
this[ property ] = nodes[ uuid ];
}
}
}
}
toJSON( meta ) {
const { uuid, type } = this;
const isRoot = ( meta === undefined || typeof meta === 'string' );
if ( isRoot ) {
meta = {
textures: {},
images: {},
nodes: {}
};
}
// serialize
let data = meta.nodes[ uuid ];
if ( data === undefined ) {
data = {
uuid,
type,
meta,
metadata: {
version: 4.6,
type: 'Node',
generator: 'Node.toJSON'
}
};
if ( isRoot !== true ) meta.nodes[ data.uuid ] = data;
this.serialize( data );
delete data.meta;
}
// TODO: Copied from Object3D.toJSON
function extractFromCache( cache ) {
const values = [];
for ( const key in cache ) {
const data = cache[ key ];
delete data.metadata;
values.push( data );
}
return values;
}
if ( isRoot ) {
const textures = extractFromCache( meta.textures );
const images = extractFromCache( meta.images );
const nodes = extractFromCache( meta.nodes );
if ( textures.length > 0 ) data.textures = textures;
if ( images.length > 0 ) data.images = images;
if ( nodes.length > 0 ) data.nodes = nodes;
}
return data;
}
}
export default Node;
export function addNodeClass( type, nodeClass ) {
if ( typeof nodeClass !== 'function' || ! type ) throw new Error( `Node class ${ type } is not a class` );
if ( NodeClasses.has( type ) ) {
console.warn( `Redefinition of node class ${ type }` );
return;
}
NodeClasses.set( type, nodeClass );
nodeClass.type = type;
}
export function createNodeFromType( type ) {
const Class = NodeClasses.get( type );
if ( Class !== undefined ) {
return new Class();
}
}

View File

@ -0,0 +1,15 @@
class NodeAttribute {
constructor( name, type, node = null ) {
this.isNodeAttribute = true;
this.name = name;
this.type = type;
this.node = node;
}
}
export default NodeAttribute;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
let id = 0;
class NodeCache {
constructor() {
this.id = id ++;
this.nodesData = new WeakMap();
}
getNodeData( node ) {
return this.nodesData.get( node );
}
setNodeData( node, data ) {
this.nodesData.set( node, data );
}
}
export default NodeCache;

View File

@ -0,0 +1,15 @@
class NodeCode {
constructor( name, type, code = '' ) {
this.name = name;
this.type = type;
this.code = code;
Object.defineProperty( this, 'isNodeCode', { value: true } );
}
}
export default NodeCode;

View File

@ -0,0 +1,143 @@
import { NodeUpdateType } from './constants.js';
class NodeFrame {
constructor() {
this.time = 0;
this.deltaTime = 0;
this.frameId = 0;
this.renderId = 0;
this.startTime = null;
this.updateMap = new WeakMap();
this.updateBeforeMap = new WeakMap();
this.renderer = null;
this.material = null;
this.camera = null;
this.object = null;
this.scene = null;
}
_getMaps( referenceMap, nodeRef ) {
let maps = referenceMap.get( nodeRef );
if ( maps === undefined ) {
maps = {
renderMap: new WeakMap(),
frameMap: new WeakMap()
};
referenceMap.set( nodeRef, maps );
}
return maps;
}
updateBeforeNode( node ) {
const updateType = node.getUpdateBeforeType();
const reference = node.updateReference( this );
if ( updateType === NodeUpdateType.FRAME ) {
const { frameMap } = this._getMaps( this.updateBeforeMap, reference );
if ( frameMap.get( reference ) !== this.frameId ) {
if ( node.updateBefore( this ) !== false ) {
frameMap.set( reference, this.frameId );
}
}
} else if ( updateType === NodeUpdateType.RENDER ) {
const { renderMap } = this._getMaps( this.updateBeforeMap, reference );
if ( renderMap.get( reference ) !== this.renderId ) {
if ( node.updateBefore( this ) !== false ) {
renderMap.set( reference, this.renderId );
}
}
} else if ( updateType === NodeUpdateType.OBJECT ) {
node.updateBefore( this );
}
}
updateNode( node ) {
const updateType = node.getUpdateType();
const reference = node.updateReference( this );
if ( updateType === NodeUpdateType.FRAME ) {
const { frameMap } = this._getMaps( this.updateMap, reference );
if ( frameMap.get( reference ) !== this.frameId ) {
if ( node.update( this ) !== false ) {
frameMap.set( reference, this.frameId );
}
}
} else if ( updateType === NodeUpdateType.RENDER ) {
const { renderMap } = this._getMaps( this.updateMap, reference );
if ( renderMap.get( reference ) !== this.renderId ) {
if ( node.update( this ) !== false ) {
renderMap.set( reference, this.renderId );
}
}
} else if ( updateType === NodeUpdateType.OBJECT ) {
node.update( this );
}
}
update() {
this.frameId ++;
if ( this.lastTime === undefined ) this.lastTime = performance.now();
this.deltaTime = ( performance.now() - this.lastTime ) / 1000;
this.lastTime = performance.now();
this.time += this.deltaTime;
}
}
export default NodeFrame;

View File

@ -0,0 +1,22 @@
class NodeFunction {
constructor( type, inputs, name = '', presicion = '' ) {
this.type = type;
this.inputs = inputs;
this.name = name;
this.presicion = presicion;
}
getCode( /*name = this.name*/ ) {
console.warn( 'Abstract function.' );
}
}
NodeFunction.isNodeFunction = true;
export default NodeFunction;

View File

@ -0,0 +1,17 @@
class NodeFunctionInput {
constructor( type, name, count = null, qualifier = '', isConst = false ) {
this.type = type;
this.name = name;
this.count = count;
this.qualifier = qualifier;
this.isConst = isConst;
}
}
NodeFunctionInput.isNodeFunctionInput = true;
export default NodeFunctionInput;

View File

@ -0,0 +1,80 @@
class NodeKeywords {
constructor() {
this.keywords = [];
this.nodes = [];
this.keywordsCallback = {};
}
getNode( name ) {
let node = this.nodes[ name ];
if ( node === undefined && this.keywordsCallback[ name ] !== undefined ) {
node = this.keywordsCallback[ name ]( name );
this.nodes[ name ] = node;
}
return node;
}
addKeyword( name, callback ) {
this.keywords.push( name );
this.keywordsCallback[ name ] = callback;
return this;
}
parse( code ) {
const keywordNames = this.keywords;
const regExp = new RegExp( `\\b${keywordNames.join( '\\b|\\b' )}\\b`, 'g' );
const codeKeywords = code.match( regExp );
const keywordNodes = [];
if ( codeKeywords !== null ) {
for ( const keyword of codeKeywords ) {
const node = this.getNode( keyword );
if ( node !== undefined && keywordNodes.indexOf( node ) === - 1 ) {
keywordNodes.push( node );
}
}
}
return keywordNodes;
}
include( builder, code ) {
const keywordNodes = this.parse( code );
for ( const keywordNode of keywordNodes ) {
keywordNode.build( builder );
}
}
}
export default NodeKeywords;

View File

@ -0,0 +1,11 @@
class NodeParser {
parseFunction( /*source*/ ) {
console.warn( 'Abstract function.' );
}
}
export default NodeParser;

View File

@ -0,0 +1,40 @@
class NodeUniform {
constructor( name, type, node, needsUpdate = undefined ) {
this.isNodeUniform = true;
this.name = name;
this.type = type;
this.node = node.getSelf();
this.needsUpdate = needsUpdate;
}
get value() {
return this.node.value;
}
set value( val ) {
this.node.value = val;
}
get id() {
return this.node.id;
}
get groupNode() {
return this.node.groupNode;
}
}
export default NodeUniform;

View File

@ -0,0 +1,210 @@
import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three';
export function getCacheKey( object, force = false ) {
let cacheKey = '{';
if ( object.isNode === true ) {
cacheKey += object.id;
}
for ( const { property, childNode } of getNodeChildren( object ) ) {
cacheKey += ',' + property.slice( 0, - 4 ) + ':' + childNode.getCacheKey( force );
}
cacheKey += '}';
return cacheKey;
}
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 ++ ) {
const child = object[ i ];
if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) {
yield { property, index: i, childNode: child };
}
}
} else if ( object && object.isNode === true ) {
yield { property, childNode: object };
} else if ( typeof object === 'object' ) {
for ( const subProperty in object ) {
const child = object[ subProperty ];
if ( child && ( child.isNode === true || toJSON && typeof child.toJSON === 'function' ) ) {
yield { property, index: subProperty, childNode: child };
}
}
}
}
}
export function getValueType( value ) {
if ( value === undefined || value === null ) return null;
const typeOf = typeof value;
if ( value.isNode === true ) {
return 'node';
} else if ( typeOf === 'number' ) {
return 'float';
} else if ( typeOf === 'boolean' ) {
return 'bool';
} else if ( typeOf === 'string' ) {
return 'string';
} else if ( typeOf === 'function' ) {
return 'shader';
} else if ( value.isVector2 === true ) {
return 'vec2';
} else if ( value.isVector3 === true ) {
return 'vec3';
} else if ( value.isVector4 === true ) {
return 'vec4';
} else if ( value.isMatrix3 === true ) {
return 'mat3';
} else if ( value.isMatrix4 === true ) {
return 'mat4';
} else if ( value.isColor === true ) {
return 'color';
} else if ( value instanceof ArrayBuffer ) {
return 'ArrayBuffer';
}
return null;
}
export function getValueFromType( type, ...params ) {
const last4 = type ? type.slice( - 4 ) : undefined;
if ( params.length === 1 ) { // ensure same behaviour as in NodeBuilder.format()
if ( last4 === 'vec2' ) params = [ params[ 0 ], params[ 0 ] ];
else if ( last4 === 'vec3' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ] ];
else if ( last4 === 'vec4' ) params = [ params[ 0 ], params[ 0 ], params[ 0 ], params[ 0 ] ];
}
if ( type === 'color' ) {
return new Color( ...params );
} else if ( last4 === 'vec2' ) {
return new Vector2( ...params );
} else if ( last4 === 'vec3' ) {
return new Vector3( ...params );
} else if ( last4 === 'vec4' ) {
return new Vector4( ...params );
} else if ( last4 === 'mat3' ) {
return new Matrix3( ...params );
} else if ( last4 === 'mat4' ) {
return new Matrix4( ...params );
} else if ( type === 'bool' ) {
return params[ 0 ] || false;
} else if ( ( type === 'float' ) || ( type === 'int' ) || ( type === 'uint' ) ) {
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;
}

View File

@ -0,0 +1,14 @@
class NodeVar {
constructor( name, type ) {
this.isNodeVar = true;
this.name = name;
this.type = type;
}
}
export default NodeVar;

View File

@ -0,0 +1,17 @@
import NodeVar from './NodeVar.js';
class NodeVarying extends NodeVar {
constructor( name, type ) {
super( name, type );
this.needsInterpolation = false;
this.isNodeVarying = true;
}
}
export default NodeVarying;

View File

@ -0,0 +1,63 @@
import Node, { addNodeClass } from './Node.js';
import StructTypeNode from './StructTypeNode.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
class OutputStructNode extends Node {
constructor( ...members ) {
super();
this.members = members;
this.isOutputStructNode = true;
}
setup( builder ) {
super.setup( builder );
const members = this.members;
const types = [];
for ( let i = 0; i < members.length; i ++ ) {
types.push( members[ i ].getNodeType( builder ) );
}
this.nodeType = builder.getStructTypeFromNode( new StructTypeNode( types ) ).name;
}
generate( builder, output ) {
const nodeVar = builder.getVarFromNode( this );
nodeVar.isOutputStructVar = true;
const propertyName = builder.getPropertyName( nodeVar );
const members = this.members;
const structPrefix = propertyName !== '' ? propertyName + '.' : '';
for ( let i = 0; i < members.length; i ++ ) {
const snippet = members[ i ].build( builder, output );
builder.addLineFlowCode( `${ structPrefix }m${ i } = ${ snippet }` );
}
return propertyName;
}
}
export default OutputStructNode;
export const outputStruct = nodeProxy( OutputStructNode );
addNodeClass( 'OutputStructNode', OutputStructNode );

View File

@ -0,0 +1,33 @@
import { addNodeClass } from './Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
import PropertyNode from './PropertyNode.js';
class ParameterNode extends PropertyNode {
constructor( nodeType, name = null ) {
super( nodeType, name );
this.isParameterNode = true;
}
getHash() {
return this.uuid;
}
generate() {
return this.name;
}
}
export default ParameterNode;
export const parameter = ( type, name ) => nodeObject( new ParameterNode( type, name ) );
addNodeClass( 'ParameterNode', ParameterNode );

View File

@ -0,0 +1,82 @@
import Node, { addNodeClass } from './Node.js';
import { nodeImmutable, nodeObject } from '../shadernode/ShaderNode.js';
class PropertyNode extends Node {
constructor( nodeType, name = null, varying = false ) {
super( nodeType );
this.name = name;
this.varying = varying;
this.isPropertyNode = true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
isGlobal( /*builder*/ ) {
return true;
}
generate( builder ) {
let nodeVar;
if ( this.varying === true ) {
nodeVar = builder.getVaryingFromNode( this, this.name );
nodeVar.needsInterpolation = true;
} else {
nodeVar = builder.getVarFromNode( this, this.name );
}
return builder.getPropertyName( nodeVar );
}
}
export default PropertyNode;
export const property = ( type, name ) => nodeObject( new PropertyNode( type, name ) );
export const varyingProperty = ( type, name ) => nodeObject( new PropertyNode( type, name, true ) );
export const diffuseColor = nodeImmutable( PropertyNode, 'vec4', 'DiffuseColor' );
export const roughness = nodeImmutable( PropertyNode, 'float', 'Roughness' );
export const metalness = nodeImmutable( PropertyNode, 'float', 'Metalness' );
export const clearcoat = nodeImmutable( PropertyNode, 'float', 'Clearcoat' );
export const clearcoatRoughness = nodeImmutable( PropertyNode, 'float', 'ClearcoatRoughness' );
export const sheen = nodeImmutable( PropertyNode, 'vec3', 'Sheen' );
export const sheenRoughness = nodeImmutable( PropertyNode, 'float', 'SheenRoughness' );
export const iridescence = nodeImmutable( PropertyNode, 'float', 'Iridescence' );
export const iridescenceIOR = nodeImmutable( PropertyNode, 'float', 'IridescenceIOR' );
export const iridescenceThickness = nodeImmutable( PropertyNode, 'float', 'IridescenceThickness' );
export const alphaT = nodeImmutable( PropertyNode, 'float', 'AlphaT' );
export const anisotropy = nodeImmutable( PropertyNode, 'float', 'Anisotropy' );
export const anisotropyT = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyT' );
export const anisotropyB = nodeImmutable( PropertyNode, 'vec3', 'AnisotropyB' );
export const specularColor = nodeImmutable( PropertyNode, 'color', 'SpecularColor' );
export const specularF90 = nodeImmutable( PropertyNode, 'float', 'SpecularF90' );
export const shininess = nodeImmutable( PropertyNode, 'float', 'Shininess' );
export const output = nodeImmutable( PropertyNode, 'vec4', 'Output' );
export const dashSize = nodeImmutable( PropertyNode, 'float', 'dashSize' );
export const gapSize = nodeImmutable( PropertyNode, 'float', 'gapSize' );
export const pointWidth = nodeImmutable( PropertyNode, 'float', 'pointWidth' );
export const ior = nodeImmutable( PropertyNode, 'float', 'IOR' );
export const transmission = nodeImmutable( PropertyNode, 'float', 'Transmission' );
export const thickness = nodeImmutable( PropertyNode, 'float', 'Thickness' );
export const attenuationDistance = nodeImmutable( PropertyNode, 'float', 'AttenuationDistance' );
export const attenuationColor = nodeImmutable( PropertyNode, 'color', 'AttenuationColor' );
addNodeClass( 'PropertyNode', PropertyNode );

View File

@ -0,0 +1,89 @@
import Node, { addNodeClass } from './Node.js';
import { cond } from '../math/CondNode.js';
import { ShaderNode, nodeProxy, getCurrentStack, setCurrentStack } from '../shadernode/ShaderNode.js';
class StackNode extends Node {
constructor( parent = null ) {
super();
this.nodes = [];
this.outputNode = null;
this.parent = parent;
this._currentCond = null;
this.isStackNode = true;
}
getNodeType( builder ) {
return this.outputNode ? this.outputNode.getNodeType( builder ) : 'void';
}
add( node ) {
this.nodes.push( node );
return this;
}
if( boolNode, method ) {
const methodNode = new ShaderNode( method );
this._currentCond = cond( boolNode, methodNode );
return this.add( this._currentCond );
}
elseif( boolNode, method ) {
const methodNode = new ShaderNode( method );
const ifNode = cond( boolNode, methodNode );
this._currentCond.elseNode = ifNode;
this._currentCond = ifNode;
return this;
}
else( method ) {
this._currentCond.elseNode = new ShaderNode( method );
return this;
}
build( builder, ...params ) {
const previousStack = getCurrentStack();
setCurrentStack( this );
for ( const node of this.nodes ) {
node.build( builder, 'void' );
}
setCurrentStack( previousStack );
return this.outputNode ? this.outputNode.build( builder, ...params ) : super.build( builder, ...params );
}
}
export default StackNode;
export const stack = nodeProxy( StackNode );
addNodeClass( 'StackNode', StackNode );

View File

@ -0,0 +1,24 @@
import Node, { addNodeClass } from './Node.js';
class StructTypeNode extends Node {
constructor( types ) {
super();
this.types = types;
this.isStructTypeNode = true;
}
getMemberTypes() {
return this.types;
}
}
export default StructTypeNode;
addNodeClass( 'StructTypeNode', StructTypeNode );

View File

@ -0,0 +1,58 @@
import Node, { addNodeClass } from './Node.js';
class TempNode extends Node {
constructor( type ) {
super( type );
this.isTempNode = true;
}
hasDependencies( builder ) {
return builder.getDataFromNode( this ).usageCount > 1;
}
build( builder, output ) {
const buildStage = builder.getBuildStage();
if ( buildStage === 'generate' ) {
const type = builder.getVectorType( this.getNodeType( builder, output ) );
const nodeData = builder.getDataFromNode( this );
if ( builder.context.tempRead !== false && nodeData.propertyName !== undefined ) {
return builder.format( nodeData.propertyName, type, output );
} else if ( builder.context.tempWrite !== false && type !== 'void' && output !== 'void' && this.hasDependencies( builder ) ) {
const snippet = super.build( builder, type );
const nodeVar = builder.getVarFromNode( this, null, type );
const propertyName = builder.getPropertyName( nodeVar );
builder.addLineFlowCode( `${propertyName} = ${snippet}` );
nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
return builder.format( nodeData.propertyName, type, output );
}
}
return super.build( builder, output );
}
}
export default TempNode;
addNodeClass( 'TempNode', TempNode );

View File

@ -0,0 +1,13 @@
class UniformGroup {
constructor( name ) {
this.name = name;
this.isUniformGroup = true;
}
}
export default UniformGroup;

View File

@ -0,0 +1,36 @@
import Node from './Node.js';
import { addNodeClass } from './Node.js';
class UniformGroupNode extends Node {
constructor( name, shared = false ) {
super( 'string' );
this.name = name;
this.version = 0;
this.shared = shared;
this.isUniformGroup = true;
}
set needsUpdate( value ) {
if ( value === true ) this.version ++;
}
}
export const uniformGroup = ( name ) => new UniformGroupNode( name );
export const sharedUniformGroup = ( name ) => new UniformGroupNode( name, true );
export const frameGroup = sharedUniformGroup( 'frame' );
export const renderGroup = sharedUniformGroup( 'render' );
export const objectGroup = uniformGroup( 'object' );
export default UniformGroupNode;
addNodeClass( 'UniformGroupNode', UniformGroupNode );

View File

@ -0,0 +1,100 @@
import InputNode from './InputNode.js';
import { objectGroup } from './UniformGroupNode.js';
import { addNodeClass } from './Node.js';
import { nodeObject, getConstNodeType } from '../shadernode/ShaderNode.js';
class UniformNode extends InputNode {
constructor( value, nodeType = null ) {
super( value, nodeType );
this.isUniformNode = true;
this.groupNode = objectGroup;
}
setGroup( group ) {
this.groupNode = group;
return this;
}
getGroup() {
return this.groupNode;
}
getUniformHash( builder ) {
return this.getHash( builder );
}
onUpdate( callback, updateType ) {
const self = this.getSelf();
callback = callback.bind( self );
return super.onUpdate( ( frame ) => {
const value = callback( frame, self );
if ( value !== undefined ) {
this.value = value;
}
}, updateType );
}
generate( builder, output ) {
const type = this.getNodeType( builder );
const hash = this.getUniformHash( builder );
let sharedNode = builder.getNodeFromHash( hash );
if ( sharedNode === undefined ) {
builder.setHashNode( this, hash );
sharedNode = this;
}
const sharedNodeType = sharedNode.getInputType( builder );
const nodeUniform = builder.getUniformFromNode( sharedNode, sharedNodeType, builder.shaderStage, builder.context.label );
const propertyName = builder.getPropertyName( nodeUniform );
if ( builder.context.label !== undefined ) delete builder.context.label;
return builder.format( propertyName, type, output );
}
}
export default UniformNode;
export const uniform = ( arg1, arg2 ) => {
const nodeType = getConstNodeType( arg2 || arg1 );
// @TODO: get ConstNode from .traverse() in the future
const value = ( arg1 && arg1.isNode === true ) ? ( arg1.node && arg1.node.value ) || arg1.value : arg1;
return nodeObject( new UniformNode( value, nodeType ) );
};
addNodeClass( 'UniformNode', UniformNode );

View File

@ -0,0 +1,60 @@
import Node, { addNodeClass } from './Node.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class VarNode extends Node {
constructor( node, name = null ) {
super();
this.node = node;
this.name = name;
this.isVarNode = true;
}
isGlobal() {
return true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
getNodeType( builder ) {
return this.node.getNodeType( builder );
}
generate( builder ) {
const { node, name } = this;
const nodeVar = builder.getVarFromNode( this, name, builder.getVectorType( this.getNodeType( builder ) ) );
const propertyName = builder.getPropertyName( nodeVar );
const snippet = node.build( builder, nodeVar.type );
builder.addLineFlowCode( `${propertyName} = ${snippet}` );
return propertyName;
}
}
export default VarNode;
export const temp = nodeProxy( VarNode );
addNodeElement( 'temp', temp ); // @TODO: Will be removed in the future
addNodeElement( 'toVar', ( ...params ) => temp( ...params ).append() );
addNodeClass( 'VarNode', VarNode );

View File

@ -0,0 +1,65 @@
import Node, { addNodeClass } from './Node.js';
import { NodeShaderStage } from './constants.js';
import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
class VaryingNode extends Node {
constructor( node, name = null ) {
super();
this.node = node;
this.name = name;
this.isVaryingNode = true;
}
isGlobal() {
return true;
}
getHash( builder ) {
return this.name || super.getHash( builder );
}
getNodeType( builder ) {
// VaryingNode is auto type
return this.node.getNodeType( builder );
}
generate( builder ) {
const { name, node } = this;
const type = this.getNodeType( builder );
const nodeVarying = builder.getVaryingFromNode( this, name, type );
// this property can be used to check if the varying can be optimized for a var
nodeVarying.needsInterpolation || ( nodeVarying.needsInterpolation = ( builder.shaderStage === 'fragment' ) );
const propertyName = builder.getPropertyName( nodeVarying, NodeShaderStage.VERTEX );
// force node run in vertex stage
builder.flowNodeFromShaderStage( NodeShaderStage.VERTEX, node, type, propertyName );
return builder.getPropertyName( nodeVarying );
}
}
export default VaryingNode;
export const varying = nodeProxy( VaryingNode );
addNodeElement( 'varying', varying );
addNodeClass( 'VaryingNode', VaryingNode );

View File

@ -0,0 +1,28 @@
export const NodeShaderStage = {
VERTEX: 'vertex',
FRAGMENT: 'fragment'
};
export const NodeUpdateType = {
NONE: 'none',
FRAME: 'frame',
RENDER: 'render',
OBJECT: 'object'
};
export const NodeType = {
BOOLEAN: 'bool',
INTEGER: 'int',
FLOAT: 'float',
VECTOR2: 'vec2',
VECTOR3: 'vec3',
VECTOR4: 'vec4',
MATRIX2: 'mat2',
MATRIX3: 'mat3',
MATRIX4: 'mat4'
};
export const defaultShaderStages = [ 'fragment', 'vertex' ];
export const defaultBuildStages = [ 'setup', 'analyze', 'generate' ];
export const shaderStages = [ ...defaultShaderStages, 'compute' ];
export const vectorComponents = [ 'x', 'y', 'z', 'w' ];