three
Version:
JavaScript 3D library
154 lines (115 loc) • 4.25 kB
JavaScript
import ContextNode from './ContextNode.js';
import { addMethodChaining } from '../tsl/TSLCore.js';
/**
* A specialized context node designed to override specific target nodes within a
* node sub-graph or flow. This allows replacing specific inputs (e.g., normal
* and position vectors) dynamically during compilation for a specific flow node,
* without having to reconstruct or duplicate the source nodes.
*
* ```js
* // Method chaining example:
* node.overrideNode( positionLocal, () => positionLocal.add( vec3( 1, 0, 0 ) ) );
*
* // Context assignment example:
* material.contextNode = overrideNode( positionLocal, () => positionLocal.add( vec3( 1, 0, 0 ) ) );
* ```
*
* @augments ContextNode
*/
class OverrideContextNode extends ContextNode {
/**
* Returns the type of the node.
*
* @type {string}
* @readonly
* @static
*/
static get type() {
return 'OverrideContextNode';
}
/**
* Constructs a new override context node.
*
* @param {Map<Node, Function>} overrideNodes - A map mapping target nodes to their respective override callback functions.
* @param {Node|null} [flowNode=null] - The node whose context should be modified.
*/
constructor( overrideNodes, flowNode = null ) {
super( flowNode, {
overrideNodes
} );
/**
* This flag can be used for type testing.
*
* @type {boolean}
* @readonly
* @default true
*/
this.isOverrideContextNode = true;
}
/**
* Gathers the context data from all parent context nodes by traversing the hierarchy,
* merging the `overrideNodes` maps from all encountered `OverrideContextNode` instances.
*
* @return {Object} The gathered context data, containing the merged `overrideNodes` map.
*/
getFlowContextData() {
const children = [];
this.traverse( ( node ) => {
if ( node.isOverrideContextNode === true ) {
children.push( node.value.overrideNodes );
}
} );
const overrideNodes = new Map( children.flatMap( ( map ) => Array.from( map.entries() ) ) );
const data = super.getFlowContextData();
data.overrideNodes = overrideNodes;
return data;
}
}
export default OverrideContextNode;
/**
* TSL function for creating an `OverrideContextNode` to override a single target node.
*
* ```js
* material.contextNode = overrideNode( positionLocal, ( builder ) => positionLocal.add( vec3( 1, 0, 0 ) ) );
* ```
*
* @tsl
* @function
* @param {Node} targetNode - The target node that should be overridden.
* @param {Function|Node|null} [callback=null] - A callback function returning the overriding node (which receives the builder as its argument), or the overriding node itself.
* @param {Node|null} [flowNode=null] - The node whose context should be modified.
* @return {OverrideContextNode} The created override context node.
*/
export function overrideNode( targetNode, callback = null, flowNode = null ) {
if ( callback && callback.isNode ) {
const node = callback;
callback = () => node;
}
return new OverrideContextNode( new Map( [[ targetNode, callback ]] ), flowNode );
}
addMethodChaining( 'overrideNode', ( flowNode, node, callback ) => overrideNode( node, callback, flowNode ) );
/**
* TSL function for creating an `OverrideContextNode` to override multiple target nodes.
*
* ```js
* material.contextNode = overrideNodes( [
* [ positionView, customPositionView ],
* [ positionViewDirection, ( builder ) => customPositionViewDirection ]
* ] );
* ```
*
* @tsl
* @function
* @param {Map<Node, (Function|Node)>|Array<Array<Node|Function|Node>>} overrides - The overrides mapping target nodes to callback functions or overriding nodes.
* @param {Node|null} [flowNode=null] - The node whose context should be modified.
* @return {OverrideContextNode} The created override context node.
*/
export function overrideNodes( overrides, flowNode = null ) {
const overrideNodesMap = new Map();
for ( const [ node, value ] of overrides ) {
const callback = value !== null ? typeof value === 'function' ? value : () => value : null;
overrideNodesMap.set( node, callback );
}
return new OverrideContextNode( overrideNodesMap, flowNode );
}
addMethodChaining( 'overrideNodes', ( flowNode, overrides ) => overrideNodes( overrides, flowNode ) );