From b2320be805e60f68732ad678b518d6f222d87cd4 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Mon, 2 Dec 2024 18:24:19 +0100 Subject: [PATCH] Node: Document more modules. (#30012) * Node: Document more modules. * Docs: Fix typo. --- src/nodes/core/Node.js | 20 ++- src/nodes/core/NodeBuilder.js | 269 +++++++++++++++++++++++++++- src/nodes/core/NodeFunctionInput.js | 44 +++++ src/nodes/core/NodeUtils.js | 85 +++++++++ src/nodes/core/OutputStructNode.js | 3 +- src/nodes/core/StructTypeNode.js | 40 ++++- 6 files changed, 450 insertions(+), 11 deletions(-) diff --git a/src/nodes/core/Node.js b/src/nodes/core/Node.js index 1ffdd1c909f191..f6799e5fe81e83 100644 --- a/src/nodes/core/Node.js +++ b/src/nodes/core/Node.js @@ -98,7 +98,22 @@ class Node extends EventDispatcher { // private + /** + * The cache key of this node. + * + * @private + * @type {Number?} + * @default null + */ this._cacheKey = null; + + /** + * The cache key 's version. + * + * @private + * @type {Number} + * @default 0 + */ this._cacheKeyVersion = 0; Object.defineProperty( this, 'id', { value: _nodeId ++ } ); @@ -244,9 +259,10 @@ class Node extends EventDispatcher { } /** - * Returns a generator that can be used to iterate over the child nodes. + * Generator function that can be used to iterate over the child nodes. * - * @return {Generator} The generator. + * @generator + * @yields {Node} A child node. */ * getChildren() { diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index ea41a9ea3e1937..bdf40a6539329e 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -5,6 +5,7 @@ import NodeVar from './NodeVar.js'; import NodeCode from './NodeCode.js'; import NodeCache from './NodeCache.js'; import ParameterNode from './ParameterNode.js'; +import StructTypeNode from './StructTypeNode.js'; import FunctionNode from '../code/FunctionNode.js'; import NodeMaterial from '../../materials/nodes/NodeMaterial.js'; import { getTypeFromLength } from './NodeUtils.js'; @@ -154,6 +155,13 @@ class NodeBuilder { * @type {Object} */ this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 }; + + /** + * This dictionary holds the output structs of the builder. + * The structs are maintained in an array for each shader stage. + * + * @type {Object} + */ this.structs = { vertex: [], fragment: [], compute: [], index: 0 }; this.bindings = { vertex: {}, fragment: {}, compute: {} }; this.bindingsIndexes = {}; @@ -214,13 +222,29 @@ class NodeBuilder { this.flowsData = new WeakMap(); + /** + * The current shader stage. + * + * @type {('vertex'|'fragment'|'compute')?} + */ this.shaderStage = null; + + /** + * The current build stage. + * + * @type {('setup'|'analyze'|'generate')?} + */ this.buildStage = null; this.useComparisonMethod = false; } + /** + * Returns the bind groups of the current renderer. + * + * @return {ChainMap} The cache. + */ getBindGroupsCache() { let bindGroupsCache = rendererCache.get( this.renderer ); @@ -237,18 +261,40 @@ class NodeBuilder { } + /** + * Factory method for creating an instance of {@link RenderTarget} with the given + * dimensions and options. + * + * @param {Number} width - The width of the render target. + * @param {Number} height - The height of the render target. + * @param {Object} options - The options of the render target. + * @return {RenderTarget} The render target. + */ createRenderTarget( width, height, options ) { return new RenderTarget( width, height, options ); } + /** + * Factory method for creating an instance of {@link CubeRenderTarget} with the given + * dimensions and options. + * + * @param {Number} size - The size of the cube render target. + * @param {Object} options - The options of the cube render target. + * @return {CubeRenderTarget} The cube render target. + */ createCubeRenderTarget( size, options ) { return new CubeRenderTarget( size, options ); } + /** + * Factory method for creating an instance of {@link PMREMGenerator}. + * + * @return {PMREMGenerator} The PMREM generator. + */ createPMREMGenerator() { // TODO: Move Materials.js to outside of the Nodes.js in order to remove this function and improve tree-shaking support @@ -257,6 +303,12 @@ class NodeBuilder { } + /** + * Whether the given node is included in the internal array of nodes or not. + * + * @param {Node} node - The node to test. + * @return {Boolean} Whether the given node is included in the internal array of nodes or not. + */ includes( node ) { return this.nodes.includes( node ); @@ -667,6 +719,13 @@ class NodeBuilder { } + /** + * It might be necessary to convert certain data types to different ones + * so this method can be used to hide the conversion. + * + * @param {String} type - The type. + * @return {String} The updated type. + */ getType( type ) { if ( type === 'color' ) return 'vec3'; @@ -675,12 +734,25 @@ class NodeBuilder { } + /** + * Whether the given attribute name is defined in the geometry or not. + * + * @param {String} name - The attribute name. + * @return {Boolean} Whether the given attribute name is defined in the geometry. + */ hasGeometryAttribute( name ) { return this.geometry && this.geometry.getAttribute( name ) !== undefined; } + /** + * Returns a node attribute for the given name and type. + * + * @param {String} name - The attribute's name. + * @param {String} type - The attribute's type. + * @return {NodeAttribute} The node attribute. + */ getAttribute( name, type ) { const attributes = this.attributes; @@ -707,36 +779,74 @@ class NodeBuilder { } + /** + * Returns for the given node and shader stage the property name for the shader. + * + * @param {Node} node - The node. + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The property name. + */ getPropertyName( node/*, shaderStage*/ ) { return node.name; } + /** + * Whether the given type is a vector type or not. + * + * @param {String} type - The type to check. + * @return {Boolean} Whether the given type is a vector type or not. + */ isVector( type ) { return /vec\d/.test( type ); } + /** + * Whether the given type is a matrix type or not. + * + * @param {String} type - The type to check. + * @return {Boolean} Whether the given type is a matrix type or not. + */ isMatrix( type ) { return /mat\d/.test( type ); } + /** + * Whether the given type is a reference type or not. + * + * @param {String} type - The type to check. + * @return {Boolean} Whether the given type is a reference type or not. + */ isReference( type ) { return type === 'void' || type === 'property' || type === 'sampler' || type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'depthTexture' || type === 'texture3D'; } + /** + * Whether the given texture needs a conversion to working color space. + * + * @abstract + * @param {Texture} texture - The texture to check. + * @return {Boolean} Whether a color space conversion is required or not. + */ needsToWorkingColorSpace( /*texture*/ ) { return false; } + /** + * Returns the component type of a given texutre. + * + * @param {Texture} texture - The texture. + * @return {String} The component type. + */ getComponentTypeFromTexture( texture ) { const type = texture.type; @@ -752,6 +862,12 @@ class NodeBuilder { } + /** + * Returns the element type for a given type. + * + * @param {String} type - The type. + * @return {String} The element type. + */ getElementType( type ) { if ( type === 'mat2' ) return 'vec2'; @@ -762,6 +878,12 @@ class NodeBuilder { } + /** + * Returns the component type for a given type. + * + * @param {String} type - The type. + * @return {String} The component type. + */ getComponentType( type ) { type = this.getVectorType( type ); @@ -780,6 +902,12 @@ class NodeBuilder { } + /** + * Returns the vector type for a given type. + * + * @param {String} type - The type. + * @return {String} The vector type. + */ getVectorType( type ) { if ( type === 'color' ) return 'vec3'; @@ -789,6 +917,13 @@ class NodeBuilder { } + /** + * Returns the data type for the given the length and component type. + * + * @param {Number} length - The length. + * @param {String} [componentType='float'] - The component type. + * @return {String} The type. + */ getTypeFromLength( length, componentType = 'float' ) { if ( length === 1 ) return componentType; @@ -800,12 +935,24 @@ class NodeBuilder { } + /** + * Returns the type for a given typed array. + * + * @param {TypedArray} type - The typed array. + * @return {String} The type. + */ getTypeFromArray( array ) { return typeFromArray.get( array.constructor ); } + /** + * Returns the type for a given buffer attribute. + * + * @param {BufferAttribute} attribute - The buffer attribute. + * @return {String} The type. + */ getTypeFromAttribute( attribute ) { let dataAttribute = attribute; @@ -828,6 +975,12 @@ class NodeBuilder { } + /** + * Returns the length for the given data type. + * + * @param {String} type - The data type. + * @return {Number} The length. + */ getTypeLength( type ) { const vecType = this.getVectorType( type ); @@ -843,18 +996,39 @@ class NodeBuilder { } + /** + * Returns the vector type for a given matrix type. + * + * @param {String} type - The matrix type. + * @return {String} The vector type. + */ getVectorFromMatrix( type ) { return type.replace( 'mat', 'vec' ); } + /** + * For a given type this method changes the component type to the + * given value. E.g. `vec4` should be changed to the new component type + * `uint` which results in `uvec4`. + * + * @param {String} type - The type. + * @param {String} newComponentType - The new component type. + * @return {String} The new type. + */ changeComponentType( type, newComponentType ) { return this.getTypeFromLength( this.getTypeLength( type ), newComponentType ); } + /** + * Returns the integer type pendant for the given type. + * + * @param {String} type - The type. + * @return {String} The integer type. + */ getIntegerType( type ) { const componentType = this.getComponentType( type ); @@ -937,22 +1111,25 @@ class NodeBuilder { } - getStructTypeFromNode( node, shaderStage = this.shaderStage ) { + getStructTypeFromNode( node, types, shaderStage = this.shaderStage ) { const nodeData = this.getDataFromNode( node, shaderStage ); - if ( nodeData.structType === undefined ) { + let structType = nodeData.structType; + + if ( structType === undefined ) { const index = this.structs.index ++; - node.name = `StructType${ index }`; - this.structs[ shaderStage ].push( node ); + structType = new StructTypeNode( 'StructType' + index, types ); - nodeData.structType = node; + this.structs[ shaderStage ].push( structType ); + + nodeData.structType = structType; } - return node; + return structType; } @@ -1297,30 +1474,62 @@ class NodeBuilder { } + /** + * Returns an array holding all node attributes of this node builder. + * + * @return {Array} The node attributes of this builder. + */ getAttributesArray() { return this.attributes.concat( this.bufferAttributes ); } + /** + * Returns the attribute definitions as a shader string for the given shader stage. + * + * @abstract + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The attribute code section. + */ getAttributes( /*shaderStage*/ ) { console.warn( 'Abstract function.' ); } + /** + * Returns the varying definitions as a shader string for the given shader stage. + * + * @abstract + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The varying code section. + */ getVaryings( /*shaderStage*/ ) { console.warn( 'Abstract function.' ); } + /** + * Returns a single variable definition as a shader string for the given variable type and name. + * + * @param {String} type - The variable's type. + * @param {String} name - The variable's name. + * @return {String} The shader string. + */ getVar( type, name ) { return `${ this.getType( type ) } ${ name }`; } + /** + * Returns the variable definitions as a shader string for the given shader stage. + * + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The variable code section. + */ getVars( shaderStage ) { let snippet = ''; @@ -1341,12 +1550,25 @@ class NodeBuilder { } + /** + * Returns the uniform definitions as a shader string for the given shader stage. + * + * @abstract + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The uniform code section. + */ getUniforms( /*shaderStage*/ ) { console.warn( 'Abstract function.' ); } + /** + * Returns the native code definitions as a shader string for the given shader stage. + * + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The current shader stage. + * @return {String} The native code section. + */ getCodes( shaderStage ) { const codes = this.codes[ shaderStage ]; @@ -1373,36 +1595,66 @@ class NodeBuilder { } + /** + * Sets the current shader stage. + * + * @param {('vertex'|'fragment'|'compute')?} shaderStage - The shader stage to set. + */ setShaderStage( shaderStage ) { this.shaderStage = shaderStage; } + /** + * Returns the current shader stage. + * + * @return {('vertex'|'fragment'|'compute')?} The current shader stage. + */ getShaderStage() { return this.shaderStage; } + /** + * Sets the current build stage. + * + * @param {('setup'|'analyze'|'generate')?} buildStage - The build stage to set. + */ setBuildStage( buildStage ) { this.buildStage = buildStage; } + /** + * Returns the current build stage. + * + * @return {('setup'|'analyze'|'generate')?} The current build stage. + */ getBuildStage() { return this.buildStage; } + /** + * Controls the code build of the shader stages. + * + * @abstract + */ buildCode() { console.warn( 'Abstract function.' ); } + /** + * Central build method which controls the build for the given object. + * + * @return {NodeBuilder} A reference to this node builder. + */ build() { const { object, material, renderer } = this; @@ -1577,6 +1829,11 @@ class NodeBuilder { } + /** + * Returns a signature with the engine's current revision. + * + * @return {String} The signature. + */ getSignature() { return `// Three.js r${ REVISION } - Node System\n`; diff --git a/src/nodes/core/NodeFunctionInput.js b/src/nodes/core/NodeFunctionInput.js index 09ac2c4d57c02d..e035185a9df06b 100644 --- a/src/nodes/core/NodeFunctionInput.js +++ b/src/nodes/core/NodeFunctionInput.js @@ -1,11 +1,55 @@ +/** + * Describes the input of a {@link NodeFunction}. + */ class NodeFunctionInput { + /** + * Constructs a new node function input. + * + * @param {String} type - The input type. + * @param {String} name - The input name. + * @param {Number?} [count=null] - TODO (only relevant for GLSL). + * @param {('in'|'out'|'inout')} [qualifier=''] - The parameter qualifier (only relevant for GLSL). + * @param {Boolean} [isConst=false] - Whether the input uses a const qualifier or not (only relevant for GLSL). + */ constructor( type, name, count = null, qualifier = '', isConst = false ) { + /** + * The input type. + * + * @type {String} + */ this.type = type; + + /** + * The input name. + * + * @type {String} + */ this.name = name; + + /** + * TODO (only relevant for GLSL). + * + * @type {Number?} + * @default null + */ this.count = count; + + /** + *The parameter qualifier (only relevant for GLSL). + * + * @type {('in'|'out'|'inout')} + * @default '' + */ this.qualifier = qualifier; + + /** + * Whether the input uses a const qualifier or not (only relevant for GLSL). + * + * @type {Boolean} + * @default false + */ this.isConst = isConst; } diff --git a/src/nodes/core/NodeUtils.js b/src/nodes/core/NodeUtils.js index 70d715b6f038b4..a4f0f813ac0cf7 100644 --- a/src/nodes/core/NodeUtils.js +++ b/src/nodes/core/NodeUtils.js @@ -5,6 +5,8 @@ import { Vector2 } from '../../math/Vector2.js'; import { Vector3 } from '../../math/Vector3.js'; import { Vector4 } from '../../math/Vector4.js'; +/** @module NodeUtils **/ + // cyrb53 (c) 2018 bryc (github.com/bryc). License: Public domain. Attribution appreciated. // A fast and simple 64-bit (or 53-bit) string hash function with decent collision resistance. // Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity. @@ -45,10 +47,41 @@ function cyrb53( value, seed = 0 ) { } +/** + * Computes a hash for the given string. + * + * @method + * @param {String} str - The string to be hashed. + * @return {Number} The hash. + */ export const hashString = ( str ) => cyrb53( str ); + +/** + * Computes a hash for the given array. + * + * @method + * @param {Array} array - The array to be hashed. + * @return {Number} The hash. + */ export const hashArray = ( array ) => cyrb53( array ); + +/** + * Computes a hash for the given list of parameters. + * + * @method + * @param {...Number} params - A list of parameters. + * @return {Number} The hash. + */ export const hash = ( ...params ) => cyrb53( params ); +/** + * Computes a cache key for the given node. + * + * @method + * @param {Object} object - The object to be hashed. + * @param {Boolean} [force=false] - Whether to force a cache key computation or not. + * @return {Number} The hash. + */ export function getCacheKey( object, force = false ) { const values = []; @@ -70,6 +103,15 @@ export function getCacheKey( object, force = false ) { } +/** + * This generator function can be used to iterate over the node children + * of the given object. + * + * @generator + * @param {Object} node - The object to be hashed. + * @param {Boolean} [toJSON=false] - Whether to return JSON or not. + * @yields {Object} A result node holding the property, index (if available) and the child node. + */ export function* getNodeChildren( node, toJSON = false ) { for ( const property in node ) { @@ -126,12 +168,26 @@ const typeFromLength = /*@__PURE__*/ new Map( [ [ 16, 'mat4' ] ] ); +/** + * Returns the data type for the given the length. + * + * @method + * @param {Number} length - The length. + * @return {String} The data type. + */ export function getTypeFromLength( length ) { return typeFromLength.get( length ); } +/** + * Returns the length for the given data type. + * + * @method + * @param {String} type - The data type. + * @return {Number} The length. + */ export function getLengthFromType( type ) { if ( /float|int|uint/.test( type ) ) return 1; @@ -145,6 +201,13 @@ export function getLengthFromType( type ) { } +/** + * Returns the data type for the given value. + * + * @method + * @param {Any} value - The value. + * @return {String?} The data type. + */ export function getValueType( value ) { if ( value === undefined || value === null ) return null; @@ -205,6 +268,14 @@ export function getValueType( value ) { } +/** + * Returns the value/object for the given data type and parameters. + * + * @method + * @param {String} type - The given type. + * @param {...Any} params - A parameter list. + * @return {Any} The value/object. + */ export function getValueFromType( type, ...params ) { const last4 = type ? type.slice( - 4 ) : undefined; @@ -263,6 +334,13 @@ export function getValueFromType( type, ...params ) { } +/** + * Converts the given array buffer to a Base64 string. + * + * @method + * @param {ArrayBuffer} arrayBuffer - The array buffer. + * @return {String} The Base64 string. + */ export function arrayBufferToBase64( arrayBuffer ) { let chars = ''; @@ -279,6 +357,13 @@ export function arrayBufferToBase64( arrayBuffer ) { } +/** + * Converts the given Base64 string to an array buffer. + * + * @method + * @param {String} base64 - The Base64 string. + * @return {ArrayBuffer} The array buffer. + */ export function base64ToArrayBuffer( base64 ) { return Uint8Array.from( atob( base64 ), c => c.charCodeAt( 0 ) ).buffer; diff --git a/src/nodes/core/OutputStructNode.js b/src/nodes/core/OutputStructNode.js index e7117092284599..804f77c97725e7 100644 --- a/src/nodes/core/OutputStructNode.js +++ b/src/nodes/core/OutputStructNode.js @@ -1,5 +1,4 @@ import Node from './Node.js'; -import StructTypeNode from './StructTypeNode.js'; import { nodeProxy } from '../tsl/TSLBase.js'; /** @@ -56,7 +55,7 @@ class OutputStructNode extends Node { } - this.nodeType = builder.getStructTypeFromNode( new StructTypeNode( types ) ).name; + this.nodeType = builder.getStructTypeFromNode( this, types ).name; } diff --git a/src/nodes/core/StructTypeNode.js b/src/nodes/core/StructTypeNode.js index 288ea9535096aa..3952f7f5c41bc2 100644 --- a/src/nodes/core/StructTypeNode.js +++ b/src/nodes/core/StructTypeNode.js @@ -1,5 +1,11 @@ import Node from './Node.js'; +/** + * {@link NodeBuilder} is going to create instances of this class during the build process + * of nodes. They represent the final shader struct data that are going to be generated + * by the builder. A dictionary of struct types is maintained in {@link NodeBuilder#structs} + * for this purpose. + */ class StructTypeNode extends Node { static get type() { @@ -8,15 +14,47 @@ class StructTypeNode extends Node { } - constructor( types ) { + /** + * Constructs a new struct type node. + * + * @param {String} name - The name of the struct. + * @param {Array} types - An array of types. + */ + constructor( name, types ) { super(); + /** + * The name of the struct. + * + * @type {String} + */ + this.name = name; + + + /** + * An array of types. + * + * @type {Array} + */ this.types = types; + + /** + * This flag can be used for type testing. + * + * @type {Boolean} + * @readonly + * @default true + */ this.isStructTypeNode = true; } + /** + * Returns the member types. + * + * @return {Array} The types. + */ getMemberTypes() { return this.types;