Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TSL: Introduce array() #30386

Merged
merged 3 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Three.TSL.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const anisotropyB = TSL.anisotropyB;
export const anisotropyT = TSL.anisotropyT;
export const any = TSL.any;
export const append = TSL.append;
export const array = TSL.array;
export const arrayBuffer = TSL.arrayBuffer;
export const asin = TSL.asin;
export const assign = TSL.assign;
Expand Down
1 change: 1 addition & 0 deletions src/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
export * from './core/constants.js';

// core
export { default as ArrayNode } from './core/ArrayNode.js';
export { default as AssignNode } from './core/AssignNode.js';
export { default as AttributeNode } from './core/AttributeNode.js';
export { default as BypassNode } from './core/BypassNode.js';
Expand Down
125 changes: 125 additions & 0 deletions src/nodes/core/ArrayNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import TempNode from './TempNode.js';
import { addMethodChaining, nodeObject } from '../tsl/TSLCore.js';

/** @module ArrayNode **/

/**
* ArrayNode represents a collection of nodes, typically created using the {@link module:TSL~array} function.
* ```js
* const colors = array( [
* vec3( 1, 0, 0 ),
* vec3( 0, 1, 0 ),
* vec3( 0, 0, 1 )
* ] );
*
* const redColor = tintColors.element( 0 );
*
* @augments Node
*/
class ArrayNode extends TempNode {

static get type() {

return 'ArrayNode';

}

/**
* Constructs a new array node.
*
* @param {String} [nodeType] - The data type of the elements.
* @param {Number} [count] - Size of the array.
* @param {Array<Node>?} [values=null] - Array default values.
*/
constructor( nodeType, count, values = null ) {

super( nodeType );

/**
* Array size.
*
* @type {Array<Node>}
*/
this.count = count;

/**
* Array default values.
*
* @type {Array<Node>}
*/
this.values = values;

/**
* This flag can be used for type testing.
*
* @type {Boolean}
* @readonly
* @default true
*/
this.isArrayNode = true;

}

getNodeType( builder ) {

if ( this.nodeType === null ) {

this.nodeType = this.values[ 0 ].getNodeType( builder );

}

return this.nodeType;

}

getElementType( builder ) {

return this.getNodeType( builder );

}

generate( builder ) {

const type = this.getNodeType( builder );

return builder.generateArray( type, this.count, this.values );

}

}

export default ArrayNode;

/**
* TSL function for creating an array node.
*
* @function
* @param {String|Array<Node>} nodeTypeOrValues - A string representing the element type (e.g., 'vec3')
* or an array containing the default values (e.g., [ vec3() ]).
* @param {Number?} [count] - Size of the array.
* @returns {ArrayNode}
*/
export const array = ( ...params ) => {

let node;

if ( params.length === 1 ) {

const values = params[ 0 ];

node = new ArrayNode( null, values.length, values );

} else {

const nodeType = params[ 0 ];
const count = params[ 1 ];

node = new ArrayNode( nodeType, count );

}

return nodeObject( node );

};

addMethodChaining( 'toArray', ( node, count ) => array( Array( count ).fill( node ) ) );
95 changes: 92 additions & 3 deletions src/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,55 @@ class NodeBuilder {

}

/**
* Generates the array declaration string.
*
* @param {String} type - The type.
* @param {Number?} [count] - The count.
* @return {String} The generated value as a shader string.
*/
generateArrayDeclaration( type, count ) {

return this.getType( type ) + '[ ' + count + ' ]';

}

/**
* Generates the array shader string for the given type and value.
*
* @param {String} type - The type.
* @param {Number?} [count] - The count.
* @param {Array<Node>?} [values=null] - The default values.
* @return {String} The generated value as a shader string.
*/
generateArray( type, count, values = null ) {

let snippet = this.generateArrayDeclaration( type, count ) + '( ';

for ( let i = 0; i < count; i ++ ) {

const value = values ? values[ i ] : null;

if ( value !== null ) {

snippet += value.build( this, type );

} else {

snippet += this.generateConst( type );

}

if ( i < count - 1 ) snippet += ', ';

}

snippet += ' )';

return snippet;

}

/**
* Generates the shader string for the given type and value.
*
Expand Down Expand Up @@ -1604,6 +1653,23 @@ class NodeBuilder {

}

/**
* Returns the array length.
*
* @param {Node} node - The node.
* @return {Number?} The array length.
*/
getArrayCount( node ) {

let count = null;

if ( node.isArrayNode ) count = node.count;
else if ( node.isVarNode && node.node.isArrayNode ) count = node.node.count;

return count;

}

/**
* Returns an instance of {@link NodeVar} for the given variable node.
*
Expand Down Expand Up @@ -1636,7 +1702,11 @@ class NodeBuilder {

}

nodeVar = new NodeVar( name, type, readOnly );
//

const count = this.getArrayCount( node );

nodeVar = new NodeVar( name, type, readOnly, count );

if ( ! readOnly ) {

Expand Down Expand Up @@ -1671,6 +1741,24 @@ class NodeBuilder {
return this.isDeterministic( node.aNode ) &&
( node.bNode ? this.isDeterministic( node.bNode ) : true );

} else if ( node.isArrayNode ) {

if ( node.values !== null ) {

for ( const n of node.values ) {

if ( ! this.isDeterministic( n ) ) {

return false;

}

}

}

return true;

} else if ( node.isConstNode ) {

return true;
Expand Down Expand Up @@ -2134,11 +2222,12 @@ class NodeBuilder {
*
* @param {String} type - The variable's type.
* @param {String} name - The variable's name.
* @param {Number?} [count=null] - The array length.
* @return {String} The shader string.
*/
getVar( type, name ) {
getVar( type, name, count = null ) {

return `${ this.getType( type ) } ${ name }`;
return `${ count !== null ? this.generateArrayDeclaration( type, count ) : this.getType( type ) } ${ name }`;

}

Expand Down
12 changes: 10 additions & 2 deletions src/nodes/core/NodeVar.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class NodeVar {
* @param {String} name - The name of the variable.
* @param {String} type - The type of the variable.
* @param {Boolean} [readOnly=false] - The read-only flag.
* @param {Number?} [count=null] - The size.
*/
constructor( name, type, readOnly = false ) {
constructor( name, type, readOnly = false, count = null ) {

/**
* This flag can be used for type testing.
Expand Down Expand Up @@ -41,10 +42,17 @@ class NodeVar {
/**
* The read-only flag.
*
* @type {boolean}
* @type {Boolean}
*/
this.readOnly = readOnly;

/**
* The size.
*
* @type {Number?}
*/
this.count = count;

}

}
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/core/TempNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class TempNode extends Node {
const nodeVar = builder.getVarFromNode( this, null, type );
const propertyName = builder.getPropertyName( nodeVar );

builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
builder.addLineFlowCode( `${ propertyName } = ${ snippet }`, this );

nodeData.snippet = snippet;
nodeData.propertyName = propertyName;
Expand Down
12 changes: 9 additions & 3 deletions src/nodes/core/VarNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ class VarNode extends Node {

}

getElementType( builder ) {

return this.node.getElementType( builder );

}

getNodeType( builder ) {

return this.node.getNodeType( builder );
Expand Down Expand Up @@ -117,8 +123,6 @@ class VarNode extends Node {

if ( shouldTreatAsReadOnly ) {

const type = builder.getType( nodeVar.type );

if ( isWebGPUBackend ) {

declarationPrefix = isDeterministic
Expand All @@ -127,7 +131,9 @@ class VarNode extends Node {

} else {

declarationPrefix = `const ${ type } ${ propertyName }`;
const count = builder.getArrayCount( node );

declarationPrefix = `const ${ builder.getVar( nodeVar.type, propertyName, count ) }`;

}

Expand Down
1 change: 1 addition & 0 deletions src/nodes/tsl/TSLBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// TSL Base Syntax

export * from './TSLCore.js'; // float(), vec2(), vec3(), vec4(), mat3(), mat4(), Fn(), If(), element(), nodeObject(), nodeProxy(), ...
export * from '../core/ArrayNode.js'; // array(), .toArray()
export * from '../core/UniformNode.js'; // uniform()
export * from '../core/PropertyNode.js'; // property() <-> TODO: Separate Material Properties in other file
export * from '../core/AssignNode.js'; // .assign()
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/utils/ArrayElementNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ArrayElementNode extends Node { // @TODO: If extending from TempNode it br
const nodeSnippet = this.node.build( builder );
const indexSnippet = this.indexNode.build( builder, 'uint' );

return `${nodeSnippet}[ ${indexSnippet} ]`;
return `${ nodeSnippet }[ ${ indexSnippet } ]`;

}

Expand Down
2 changes: 1 addition & 1 deletion src/renderers/webgl-fallback/nodes/GLSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ ${ flowData.code }

for ( const variable of vars ) {

snippets.push( `${ this.getVar( variable.type, variable.name ) };` );
snippets.push( `${ this.getVar( variable.type, variable.name, variable.count ) };` );

}

Expand Down
Loading
Loading