|
@@ -0,0 +1,320 @@
|
|
|
+import NodeUniform from './NodeUniform.js';
|
|
|
+
|
|
|
+class NodeBuilder {
|
|
|
+
|
|
|
+ constructor( material, renderer ) {
|
|
|
+
|
|
|
+ this.material = material;
|
|
|
+ this.renderer = renderer;
|
|
|
+
|
|
|
+ this.nodes = [];
|
|
|
+ this.updateNodes = [];
|
|
|
+
|
|
|
+ this.vertexShader = null;
|
|
|
+ this.fragmentShader = null;
|
|
|
+
|
|
|
+ this.slots = { vertex: [], fragment: [] };
|
|
|
+ this.defines = { vertex: {}, fragment: {} };
|
|
|
+ this.uniforms = { vertex: [], fragment: [] };
|
|
|
+ this.attributes = {};
|
|
|
+ this.attributeCount = 0;
|
|
|
+
|
|
|
+ this.nodesData = new WeakMap();
|
|
|
+
|
|
|
+ this.shaderStage = null;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ addNode( node ) {
|
|
|
+
|
|
|
+ if ( this.nodes.indexOf( node ) === - 1 ) {
|
|
|
+
|
|
|
+ if ( node.needsUpdate === true ) {
|
|
|
+
|
|
|
+ this.updateNodes.push( node );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.nodes.push( node );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ addSlot( shaderStage, slot ) {
|
|
|
+
|
|
|
+ this.slots[ shaderStage ].push( slot );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ define( shaderStage, name, value = '' ) {
|
|
|
+
|
|
|
+ this.defines[ shaderStage ][ name ] = value;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getTexture( textureProperty, uvSnippet ) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getConst( type, value ) {
|
|
|
+
|
|
|
+ if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
|
|
|
+ if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
|
|
|
+ if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
|
|
|
+ if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
|
|
|
+ if ( type === 'color' ) return `vec3( ${value.r}, ${value.g}, ${value.b} )`;
|
|
|
+
|
|
|
+ throw new Error(`Type '${type}' not found in generate constant attempt.`);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getAttribute( type, name, property = null ) {
|
|
|
+
|
|
|
+ let attribute = this.attributes[ name ];
|
|
|
+
|
|
|
+ if ( attribute === undefined ) {
|
|
|
+
|
|
|
+ const index = this.attributeCount ++;
|
|
|
+
|
|
|
+ if ( property === null ) {
|
|
|
+
|
|
|
+ property = `node_A${index}`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ attribute = {
|
|
|
+ type,
|
|
|
+ name,
|
|
|
+ index,
|
|
|
+ property
|
|
|
+ };
|
|
|
+
|
|
|
+ this.attributes[ name ] = attribute;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return attribute;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getPropertyName( nodeUniform ) {
|
|
|
+
|
|
|
+ return nodeUniform.name;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getVectorType( type ) {
|
|
|
+
|
|
|
+ if ( type === 'color' ) return 'vec3';
|
|
|
+ else if ( type === 'texture' ) return 'vec4';
|
|
|
+
|
|
|
+ return type;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getTypeFromLength( type ) {
|
|
|
+
|
|
|
+ if ( type === 1 ) return 'float';
|
|
|
+ if ( type === 2 ) return 'vec2';
|
|
|
+ if ( type === 3 ) return 'vec3';
|
|
|
+ if ( type === 4 ) return 'vec4';
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getTypeLength( type ) {
|
|
|
+
|
|
|
+ type = this.getVectorType( type );
|
|
|
+
|
|
|
+ if ( type === 'float' ) return 1;
|
|
|
+ if ( type === 'vec2' ) return 2;
|
|
|
+ if ( type === 'vec3' ) return 3;
|
|
|
+ if ( type === 'vec4' ) return 4;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getDataFromNode( node, shaderStage = null ) {
|
|
|
+
|
|
|
+ let nodeData = this.nodesData.get( node );
|
|
|
+
|
|
|
+ if ( nodeData === undefined ) {
|
|
|
+
|
|
|
+ nodeData = { vertex: {}, fragment: {} };
|
|
|
+
|
|
|
+ this.nodesData.set( node, nodeData );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return shaderStage ? nodeData[ shaderStage ] : nodeData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getUniformFromNode( node, shaderStage, type ) {
|
|
|
+
|
|
|
+ const nodeData = this.getDataFromNode( node, shaderStage );
|
|
|
+
|
|
|
+ let nodeUniform = nodeData.uniform;
|
|
|
+
|
|
|
+ if ( nodeUniform === undefined ) {
|
|
|
+
|
|
|
+ const uniforms = this.uniforms[ shaderStage ];
|
|
|
+ const index = uniforms.length;
|
|
|
+
|
|
|
+ nodeUniform = new NodeUniform( 'nodeU' + index, type, node );
|
|
|
+
|
|
|
+ uniforms.push( nodeUniform );
|
|
|
+
|
|
|
+ nodeData.uniform = nodeUniform;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return nodeUniform;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ analyzeNode( node ) {
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ flowNode( node, output ) {
|
|
|
+
|
|
|
+ let flowData = {};
|
|
|
+ flowData.result = node.build( this, output );
|
|
|
+
|
|
|
+ return flowData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ _buildDefines( shader ) {
|
|
|
+
|
|
|
+ const defines = this.defines[ shader ];
|
|
|
+
|
|
|
+ let code = '';
|
|
|
+
|
|
|
+ for ( let name in defines ) {
|
|
|
+
|
|
|
+ code += `#define ${name} ${defines[ name ]}\n`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return code;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getAttributesBodySnippet( /*shaderStage*/ ) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getAttributesHeaderSnippet( /*shaderStage*/ ) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getUniformsHeaderSnippet( shaderStage ) {
|
|
|
+
|
|
|
+ const uniforms = this.uniforms[ shaderStage ];
|
|
|
+
|
|
|
+ let snippet = '';
|
|
|
+
|
|
|
+ for ( let uniform of uniforms ) {
|
|
|
+
|
|
|
+ snippet += `${uniform.type} ${uniform.name}; `;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return snippet;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ format( snippet, fromType, toType ) {
|
|
|
+
|
|
|
+ fromType = this.getVectorType( fromType );
|
|
|
+ toType = this.getVectorType( toType );
|
|
|
+
|
|
|
+ const typeToType = `${fromType} to ${toType}`;
|
|
|
+
|
|
|
+ switch ( typeToType ) {
|
|
|
+
|
|
|
+ case 'float to vec2' : return `vec2( ${snippet} )`;
|
|
|
+ case 'float to vec3' : return `vec3( ${snippet} )`;
|
|
|
+ case 'float to vec4' : return `vec4( vec3( ${snippet} ), 1.0 )`;
|
|
|
+
|
|
|
+ case 'vec2 to float' : return `${snippet}.x`;
|
|
|
+ case 'vec2 to vec3' : return `vec3( ${snippet}.x, ${snippet}.y, 0.0 )`;
|
|
|
+ case 'vec2 to vec4' : return `vec4( ${snippet}.x, ${snippet}.y, 0.0, 1.0 )`;
|
|
|
+
|
|
|
+ case 'vec3 to float' : return `${snippet}.x`;
|
|
|
+ case 'vec3 to vec2' : return `${snippet}.xy`;
|
|
|
+ case 'vec3 to vec4' : return `vec4( ${snippet}.x, ${snippet}.y, ${snippet}.z, 1.0 )`;
|
|
|
+
|
|
|
+ case 'vec4 to float' : return `${snippet}.x`;
|
|
|
+ case 'vec4 to vec2' : return `${snippet}.xy`;
|
|
|
+ case 'vec4 to vec3' : return `${snippet}.xyz`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return snippet;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getHash() {
|
|
|
+
|
|
|
+ return this.vertexShader + this.fragmentShader;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ build() {
|
|
|
+
|
|
|
+ const shaderStages = [ 'vertex', 'fragment' ];
|
|
|
+ const shaderData = {};
|
|
|
+
|
|
|
+ for ( let shaderStage of shaderStages ) {
|
|
|
+
|
|
|
+ this.shaderStage = shaderStage;
|
|
|
+
|
|
|
+ let slots = this.slots[ shaderStage ];
|
|
|
+
|
|
|
+ for ( let slot of slots ) {
|
|
|
+
|
|
|
+ let flowData = this.flowNode( slot.node, slot.output );
|
|
|
+
|
|
|
+ this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.shaderStage = null;
|
|
|
+
|
|
|
+ for ( let shaderStage of shaderStages ) {
|
|
|
+
|
|
|
+ this.define( shaderStage, 'NODE_HEADER_UNIFORMS', this.getUniformsHeaderSnippet( shaderStage ) );
|
|
|
+ this.define( shaderStage, 'NODE_HEADER_ATTRIBUTES', this.getAttributesHeaderSnippet( shaderStage ) );
|
|
|
+ this.define( shaderStage, 'NODE_BODY_ATTRIBUTES', this.getAttributesBodySnippet( shaderStage ) );
|
|
|
+
|
|
|
+ shaderData[ shaderStage ] = this._buildDefines( shaderStage );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.vertexShader = shaderData.vertex;
|
|
|
+ this.fragmentShader = shaderData.fragment;
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+export default NodeBuilder;
|