|
@@ -9,10 +9,10 @@ import { WebGPUNodeSampledTexture } from './WebGPUNodeSampledTexture.js';
|
|
|
import WebGPUUniformBuffer from '../WebGPUUniformBuffer.js';
|
|
|
import { getVectorLength, getStrideLength } from '../WebGPUBufferUtils.js';
|
|
|
|
|
|
-import NodeSlot from '../../nodes/core/NodeSlot.js';
|
|
|
import VarNode from '../../nodes/core/VarNode.js';
|
|
|
-import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
|
|
|
+import CodeNode from '../../nodes/core/CodeNode.js';
|
|
|
import BypassNode from '../../nodes/core/BypassNode.js';
|
|
|
+import ExpressionNode from '../../nodes/core/ExpressionNode.js';
|
|
|
import NodeBuilder from '../../nodes/core/NodeBuilder.js';
|
|
|
import MaterialNode from '../../nodes/accessors/MaterialNode.js';
|
|
|
import PositionNode from '../../nodes/accessors/PositionNode.js';
|
|
@@ -20,13 +20,55 @@ import NormalNode from '../../nodes/accessors/NormalNode.js';
|
|
|
import ModelViewProjectionNode from '../../nodes/accessors/ModelViewProjectionNode.js';
|
|
|
import SkinningNode from '../../nodes/accessors/SkinningNode.js';
|
|
|
import LightContextNode from '../../nodes/lights/LightContextNode.js';
|
|
|
-import ShaderLib from './ShaderLib.js';
|
|
|
+import OperatorNode from '../../nodes/math/OperatorNode.js';
|
|
|
+
|
|
|
+const wgslTypeLib = {
|
|
|
+ float: 'f32',
|
|
|
+ int: 'i32',
|
|
|
+ vec2: 'vec2<f32>',
|
|
|
+ vec3: 'vec3<f32>',
|
|
|
+ vec4: 'vec4<f32>',
|
|
|
+ uvec4: 'vec4<u32>',
|
|
|
+ mat3: 'mat3x3<f32>',
|
|
|
+ mat4: 'mat4x4<f32>'
|
|
|
+};
|
|
|
+
|
|
|
+const wgslMethods = {
|
|
|
+ dFdx: 'dpdx',
|
|
|
+ dFdy: 'dpdy'
|
|
|
+};
|
|
|
+
|
|
|
+const wgslPolyfill = {
|
|
|
+ mod: new CodeNode( `
|
|
|
+fn mod( x : f32, y : f32 ) -> f32 {
|
|
|
+
|
|
|
+ return x - y * floor( x / y );
|
|
|
+
|
|
|
+}
|
|
|
+` ),
|
|
|
+ repeatWrapping: new CodeNode( `
|
|
|
+fn repeatWrapping( uv : vec2<f32>, dimension : vec2<i32> ) -> vec2<i32> {
|
|
|
+
|
|
|
+ let uvScaled = vec2<i32>( uv * vec2<f32>( dimension ) );
|
|
|
+
|
|
|
+ return ( ( uvScaled % dimension ) + dimension ) % dimension;
|
|
|
+
|
|
|
+}
|
|
|
+` ),
|
|
|
+ inversesqrt: new CodeNode( `
|
|
|
+fn inversesqrt( x : f32 ) -> f32 {
|
|
|
+
|
|
|
+ return 1.0 / sqrt( x );
|
|
|
+
|
|
|
+}
|
|
|
+` )
|
|
|
+};
|
|
|
|
|
|
class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
constructor( object, renderer, lightNode = null ) {
|
|
|
|
|
|
- super( object, renderer, new GLSLNodeParser() );
|
|
|
+ super( object, renderer );
|
|
|
|
|
|
this.lightNode = lightNode;
|
|
|
|
|
@@ -35,8 +77,6 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
this.uniformsGroup = {};
|
|
|
|
|
|
- this.nativeShader = null;
|
|
|
-
|
|
|
this._parseObject();
|
|
|
|
|
|
}
|
|
@@ -46,28 +86,14 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
const object = this.object;
|
|
|
const material = this.material;
|
|
|
|
|
|
- // get shader
|
|
|
-
|
|
|
- let shader = null;
|
|
|
-
|
|
|
- if ( material.isMeshStandardMaterial ) {
|
|
|
-
|
|
|
- shader = ShaderLib.standard;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- shader = ShaderLib.common;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- this.nativeShader = shader;
|
|
|
-
|
|
|
// parse inputs
|
|
|
|
|
|
if ( material.isMeshStandardMaterial || material.isMeshBasicMaterial || material.isPointsMaterial || material.isLineBasicMaterial ) {
|
|
|
|
|
|
let lightNode = material.lightNode;
|
|
|
|
|
|
+ // VERTEX STAGE
|
|
|
+
|
|
|
let vertex = new PositionNode( PositionNode.GEOMETRY );
|
|
|
|
|
|
if ( lightNode === null && this.lightNode && this.lightNode.hasLights === true ) {
|
|
@@ -76,9 +102,11 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( material.positionNode !== undefined ) {
|
|
|
+ if ( material.positionNode && material.positionNode.isNode ) {
|
|
|
+
|
|
|
+ const assignPositionNode = new OperatorNode( '=', new PositionNode( PositionNode.LOCAL ), material.positionNode );
|
|
|
|
|
|
- vertex = material.positionNode;
|
|
|
+ vertex = new BypassNode( vertex, assignPositionNode );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -90,60 +118,106 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
this.context.vertex = vertex;
|
|
|
|
|
|
- this.addSlot( 'vertex', new NodeSlot( new ModelViewProjectionNode(), 'MVP', 'vec4' ) );
|
|
|
+ this.addFlow( 'vertex', new VarNode( new ModelViewProjectionNode(), 'MVP', 'vec4' ) );
|
|
|
|
|
|
- if ( material.alphaTestNode && material.alphaTestNode.isNode ) {
|
|
|
+ // COLOR
|
|
|
+
|
|
|
+ let colorNode = null;
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.alphaTestNode, 'ALPHA_TEST', 'float' ) );
|
|
|
+ if ( material.colorNode && material.colorNode.isNode ) {
|
|
|
+
|
|
|
+ colorNode = material.colorNode;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.ALPHA_TEST ), 'ALPHA_TEST', 'float' ) );
|
|
|
+ colorNode = new MaterialNode( MaterialNode.COLOR );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( material.colorNode && material.colorNode.isNode ) {
|
|
|
+ colorNode = this.addFlow( 'fragment', new VarNode( colorNode, 'DiffuseColor', 'vec4' ) );
|
|
|
+
|
|
|
+ // OPACITY
|
|
|
+
|
|
|
+ let opacityNode = null;
|
|
|
+
|
|
|
+ if ( material.opacityNode && material.opacityNode.isNode ) {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );
|
|
|
+ opacityNode = material.opacityNode;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.COLOR ), 'COLOR', 'vec4' ) );
|
|
|
+ opacityNode = new VarNode( new MaterialNode( MaterialNode.OPACITY ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( material.opacityNode && material.opacityNode.isNode ) {
|
|
|
+ this.addFlow( 'fragment', new VarNode( opacityNode, 'OPACITY', 'float' ) );
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.opacityNode, 'OPACITY', 'float' ) );
|
|
|
+ this.addFlow( 'fragment', new ExpressionNode( 'DiffuseColor.a = DiffuseColor.a * OPACITY;' ) );
|
|
|
|
|
|
- } else {
|
|
|
+ // ALPHA TEST
|
|
|
+
|
|
|
+ let alphaTest = null;
|
|
|
+
|
|
|
+ if ( material.alphaTestNode && material.alphaTestNode.isNode ) {
|
|
|
+
|
|
|
+ alphaTest = material.alphaTestNode;
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.OPACITY ), 'OPACITY', 'float' ) );
|
|
|
+ } else if ( material.alphaTest > 0 ) {
|
|
|
+
|
|
|
+ alphaTest = new MaterialNode( MaterialNode.ALPHA_TEST );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( alphaTest !== null ) {
|
|
|
+
|
|
|
+ alphaTest = this.addFlow( 'fragment', new VarNode( alphaTest, 'AlphaTest', 'float' ) );
|
|
|
+
|
|
|
+ this.addFlow( 'fragment', new ExpressionNode( 'if ( DiffuseColor.a <= AlphaTest ) { discard; }' ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( material.isMeshStandardMaterial ) {
|
|
|
|
|
|
+ // METALNESS
|
|
|
+
|
|
|
+ let metalnessNode = null;
|
|
|
+
|
|
|
if ( material.metalnessNode && material.metalnessNode.isNode ) {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.metalnessNode, 'METALNESS', 'float' ) );
|
|
|
+ metalnessNode = material.metalnessNode;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.METALNESS ), 'METALNESS', 'float' ) );
|
|
|
+ metalnessNode = new MaterialNode( MaterialNode.METALNESS );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ this.addFlow( 'fragment', new VarNode( metalnessNode, 'Metalness', 'float' ) );
|
|
|
+
|
|
|
+ this.addFlow( 'fragment', new ExpressionNode( 'DiffuseColor = vec4<f32>( DiffuseColor.rgb * ( 1.0 - Metalness ), DiffuseColor.a );' ) );
|
|
|
+
|
|
|
+ // ROUGHNESS
|
|
|
+
|
|
|
+ let roughnessNode = null;
|
|
|
+
|
|
|
if ( material.roughnessNode && material.roughnessNode.isNode ) {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.roughnessNode, 'ROUGHNESS', 'float' ) );
|
|
|
+ roughnessNode = material.roughnessNode;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.ROUGHNESS ), 'ROUGHNESS', 'float' ) );
|
|
|
+ roughnessNode = new MaterialNode( MaterialNode.ROUGHNESS );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ this.addFlow( 'fragment', new VarNode( roughnessNode, 'Roughness', 'float' ) );
|
|
|
+
|
|
|
+ // SPECULAR_TINT
|
|
|
+
|
|
|
+ this.addFlow( 'fragment', new VarNode( new ExpressionNode( 'mix( vec3<f32>( 0.04 ), DiffuseColor.rgb, Metalness )', 'vec3' ), 'SpecularTint', 'color' ) );
|
|
|
+
|
|
|
+ // NORMAL_VIEW
|
|
|
+
|
|
|
let normalNode = null;
|
|
|
|
|
|
if ( material.normalNode && material.normalNode.isNode ) {
|
|
@@ -156,61 +230,71 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
}
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new VarNode( normalNode, 'TransformedNormalView', 'vec3' ), 'NORMAL', 'vec3' ) );
|
|
|
+ this.addFlow( 'fragment', new VarNode( normalNode, 'TransformedNormalView', 'vec3' ) );
|
|
|
|
|
|
- } else if ( material.isMeshPhongMaterial ) {
|
|
|
-
|
|
|
- if ( material.specularNode && material.specularNode.isNode ) {
|
|
|
+ }
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.specularNode, 'SPECULAR', 'vec3' ) );
|
|
|
+ // LIGHT
|
|
|
|
|
|
- } else {
|
|
|
+ let outputNode = colorNode;
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.SPECULAR ), 'SPECULAR', 'vec3' ) );
|
|
|
+ if ( lightNode && lightNode.isNode ) {
|
|
|
|
|
|
- }
|
|
|
+ const lightContextNode = new LightContextNode( lightNode );
|
|
|
|
|
|
- if ( material.shininessNode && material.shininessNode.isNode ) {
|
|
|
+ outputNode = this.addFlow( 'fragment', new VarNode( lightContextNode, 'Light', 'vec3' ) );
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( material.shininessNode, 'SHININESS', 'float' ) );
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
+ // RESULT
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( new MaterialNode( MaterialNode.SHININESS ), 'SHININESS', 'float' ) );
|
|
|
+ this.addFlow( 'fragment', new VarNode( outputNode, 'Output', 'vec4' ) );
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- if ( lightNode && lightNode.isNode ) {
|
|
|
+ }
|
|
|
|
|
|
- const lightContextNode = new LightContextNode( lightNode );
|
|
|
+ addFlowCode( code ) {
|
|
|
|
|
|
- this.addSlot( 'fragment', new NodeSlot( lightContextNode, 'LIGHT', 'vec3' ) );
|
|
|
+ if ( ! /;\s*$/.test( code ) ) {
|
|
|
|
|
|
- }
|
|
|
+ code += ';';
|
|
|
|
|
|
}
|
|
|
|
|
|
+ super.addFlowCode( code + '\n\t' );
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- getTexture( textureProperty, uvSnippet, biasSnippet = null ) {
|
|
|
+ getTexture( textureProperty, uvSnippet, biasSnippet = null, shaderStage = this.shaderStage ) {
|
|
|
|
|
|
- if ( biasSnippet !== null ) {
|
|
|
+ if ( shaderStage === 'fragment' ) {
|
|
|
|
|
|
- return `texture( sampler2D( ${textureProperty}, ${textureProperty}_sampler ), ${uvSnippet}, ${biasSnippet} )`;
|
|
|
+ return `textureSample( ${textureProperty}, ${textureProperty}_sampler, ${uvSnippet} )`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- return `texture( sampler2D( ${textureProperty}, ${textureProperty}_sampler ), ${uvSnippet} )`;
|
|
|
+ this._include( 'repeatWrapping' );
|
|
|
+
|
|
|
+ const dimension = `textureDimensions( ${textureProperty}, 0 )`;
|
|
|
+
|
|
|
+ return `textureLoad( ${textureProperty}, repeatWrapping( ${uvSnippet}, ${dimension} ), 0 )`;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
- getPropertyName( node ) {
|
|
|
+ getPropertyName( node, shaderStage = this.shaderStage ) {
|
|
|
+
|
|
|
+ if ( node.isNodeVary === true ) {
|
|
|
|
|
|
- if ( node.isNodeUniform === true ) {
|
|
|
+ if ( shaderStage === 'vertex' ) {
|
|
|
+
|
|
|
+ return `NodeVarys.${ node.name }`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if ( node.isNodeUniform === true ) {
|
|
|
|
|
|
const name = node.name;
|
|
|
const type = node.type;
|
|
@@ -221,11 +305,11 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
} else if ( type === 'buffer' ) {
|
|
|
|
|
|
- return `${name}.value`;
|
|
|
+ return `NodeBuffer.${name}`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- return `nodeUniforms.${name}`;
|
|
|
+ return `NodeUniforms.${name}`;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -263,9 +347,21 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
const lastBinding = bindings[ bindings.length - 1 ];
|
|
|
const index = lastBinding && lastBinding.isUniformsGroup ? bindings.length - 1 : bindings.length;
|
|
|
|
|
|
- bindings.splice( index, 0, sampler, texture );
|
|
|
+ if ( shaderStage === 'fragment' ) {
|
|
|
+
|
|
|
+ bindings.splice( index, 0, sampler, texture );
|
|
|
+
|
|
|
+ uniformGPU = [ sampler, texture ];
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ bindings.splice( index, 0, texture );
|
|
|
+
|
|
|
+ uniformGPU = [ texture ];
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- uniformGPU = [ sampler, texture ];
|
|
|
|
|
|
} else if ( type === 'buffer' ) {
|
|
|
|
|
@@ -297,7 +393,7 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
uniformGPU = [];
|
|
|
|
|
|
- for ( const inputNode of node.value ) {
|
|
|
+ for ( const inputNode of node.nodes ) {
|
|
|
|
|
|
const uniformNodeGPU = this._getNodeUniform( inputNode, type );
|
|
|
|
|
@@ -342,15 +438,49 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
if ( shaderStage === 'vertex' ) {
|
|
|
|
|
|
const attributes = this.attributes;
|
|
|
+ const length = attributes.length;
|
|
|
+
|
|
|
+ snippet += `\n`;
|
|
|
|
|
|
- for ( let index = 0; index < attributes.length; index ++ ) {
|
|
|
+ for ( let index = 0; index < length; index ++ ) {
|
|
|
|
|
|
const attribute = attributes[ index ];
|
|
|
+ const name = attribute.name;
|
|
|
+ const type = this.getType( attribute.type );
|
|
|
+
|
|
|
+ snippet += `\t[[ location( ${index} ) ]] ${ name } : ${ type }`;
|
|
|
|
|
|
- snippet += `layout(location = ${index}) in ${attribute.type} ${attribute.name}; `;
|
|
|
+ if ( index + 1 < length ) {
|
|
|
+
|
|
|
+ snippet += `,\n`;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
+ snippet += `\n`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return snippet;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getVars( shaderStage ) {
|
|
|
+
|
|
|
+ let snippet = '';
|
|
|
+
|
|
|
+ const vars = this.vars[ shaderStage ];
|
|
|
+
|
|
|
+ for ( let index = 0; index < vars.length; index ++ ) {
|
|
|
+
|
|
|
+ const variable = vars[ index ];
|
|
|
+
|
|
|
+ const name = variable.name;
|
|
|
+ const type = this.getType( variable.type );
|
|
|
+
|
|
|
+ snippet += `var ${name} : ${type}; `;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
return snippet;
|
|
@@ -361,15 +491,43 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
let snippet = '';
|
|
|
|
|
|
- const varys = this.varys;
|
|
|
+ if ( shaderStage === 'vertex' ) {
|
|
|
+
|
|
|
+ snippet += `\t[[ builtin( position ) ]] Vertex: vec4<f32>;\n`;
|
|
|
+
|
|
|
+ const varys = this.varys;
|
|
|
|
|
|
- const ioStage = shaderStage === 'vertex' ? 'out' : 'in';
|
|
|
+ for ( let index = 0; index < varys.length; index ++ ) {
|
|
|
|
|
|
- for ( let index = 0; index < varys.length; index ++ ) {
|
|
|
+ const vary = varys[ index ];
|
|
|
|
|
|
- const vary = varys[ index ];
|
|
|
+ snippet += `\t[[ location( ${index} ) ]] ${ vary.name } : ${ this.getType( vary.type ) };\n`;
|
|
|
|
|
|
- snippet += `layout(location = ${index}) ${ioStage} ${vary.type} ${vary.name}; `;
|
|
|
+ }
|
|
|
+
|
|
|
+ snippet = this._getWGSLStruct( 'NodeVarysStruct', snippet );
|
|
|
+
|
|
|
+ } else if ( shaderStage === 'fragment' ) {
|
|
|
+
|
|
|
+ const varys = this.varys;
|
|
|
+
|
|
|
+ snippet += `\n`;
|
|
|
+
|
|
|
+ for ( let index = 0; index < varys.length; index ++ ) {
|
|
|
+
|
|
|
+ const vary = varys[ index ];
|
|
|
+
|
|
|
+ snippet += `\t[[ location( ${index} ) ]] ${ vary.name } : ${ this.getType( vary.type ) }`;
|
|
|
+
|
|
|
+ if ( index + 1 < varys.length ) {
|
|
|
+
|
|
|
+ snippet += `,\n`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ snippet += `\n`;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -390,20 +548,27 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
if ( uniform.type === 'texture' ) {
|
|
|
|
|
|
- snippet += `layout(set = 0, binding = ${index ++}) uniform sampler ${uniform.name}_sampler; `;
|
|
|
- snippet += `layout(set = 0, binding = ${index ++}) uniform texture2D ${uniform.name}; `;
|
|
|
+ if ( shaderStage === 'fragment' ) {
|
|
|
+
|
|
|
+ snippet += `[[ group( 0 ), binding( ${index ++} ) ]] var ${uniform.name}_sampler : sampler; `;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ snippet += `[[ group( 0 ), binding( ${index ++} ) ]] var ${uniform.name} : texture_2d<f32>; `;
|
|
|
|
|
|
} else if ( uniform.type === 'buffer' ) {
|
|
|
|
|
|
const bufferNode = uniform.node;
|
|
|
- const bufferType = bufferNode.bufferType;
|
|
|
+ const bufferType = this.getType( bufferNode.bufferType );
|
|
|
const bufferCount = bufferNode.bufferCount;
|
|
|
|
|
|
- snippet += `layout(set = 0, binding = ${index ++}) uniform NodeBuffer { uniform ${bufferType}[ ${bufferCount} ] value; } ${uniform.name}; `;
|
|
|
+ const bufferSnippet = `\t${uniform.name} : array< ${bufferType}, ${bufferCount} >;\n`;
|
|
|
+
|
|
|
+ snippet += this._getWGSLUniforms( 'NodeBuffer', bufferSnippet, index ++ ) + '\n\n';
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- const vectorType = this.getVectorType( uniform.type );
|
|
|
+ const vectorType = this.getType( this.getVectorType( uniform.type ) );
|
|
|
|
|
|
if ( Array.isArray( uniform.value ) === true ) {
|
|
|
|
|
@@ -413,7 +578,7 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- groupSnippet += `uniform ${vectorType} ${uniform.name}; `;
|
|
|
+ groupSnippet += `\t${uniform.name} : ${ vectorType};\n`;
|
|
|
|
|
|
}
|
|
|
|
|
@@ -423,7 +588,7 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
if ( groupSnippet ) {
|
|
|
|
|
|
- snippet += `layout(set = 0, binding = ${index ++}) uniform NodeUniforms { ${groupSnippet} } nodeUniforms; `;
|
|
|
+ snippet += this._getWGSLUniforms( 'NodeUniforms', groupSnippet, index ++ );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -431,39 +596,91 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
}
|
|
|
|
|
|
- build() {
|
|
|
+ buildCode() {
|
|
|
|
|
|
- const keywords = this.getContextValue( 'keywords' );
|
|
|
+ const shadersData = { fragment: {}, vertex: {} };
|
|
|
|
|
|
- for ( const shaderStage of [ 'fragment', 'vertex' ] ) {
|
|
|
+ for ( const shaderStage in shadersData ) {
|
|
|
|
|
|
- this.shaderStage = shaderStage;
|
|
|
+ let flow = `// code\n`;
|
|
|
+ flow += `\t${ this.flowCode[ shaderStage ] }`;
|
|
|
+ flow += '\n';
|
|
|
|
|
|
- keywords.include( this, this.nativeShader[ shaderStage + 'Shader' ] );
|
|
|
+ const flowNodes = this.flowNodes[ shaderStage ];
|
|
|
+ const mainNode = flowNodes[ flowNodes.length - 1 ];
|
|
|
|
|
|
- }
|
|
|
+ for ( const node of flowNodes ) {
|
|
|
+
|
|
|
+ const flowSlotData = this.getFlowData( shaderStage, node );
|
|
|
+ const slotName = node.name;
|
|
|
+
|
|
|
+ if ( slotName ) {
|
|
|
+
|
|
|
+ if ( flow.length > 0 ) flow += `\n`;
|
|
|
+
|
|
|
+ flow += `\t// FLOW -> ${ slotName }\n\t`;
|
|
|
|
|
|
- super.build();
|
|
|
+ }
|
|
|
+
|
|
|
+ flow += `${ flowSlotData.code }\n\t`;
|
|
|
+
|
|
|
+ if ( node === mainNode ) {
|
|
|
+
|
|
|
+ flow += `// FLOW RESULT\n\t`;
|
|
|
+
|
|
|
+ if ( shaderStage === 'vertex' ) {
|
|
|
+
|
|
|
+ flow += `NodeVarys.Vertex = `;
|
|
|
|
|
|
- this.vertexShader = this._composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
|
|
|
- this.fragmentShader = this._composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
|
|
|
+ } else if ( shaderStage === 'fragment' ) {
|
|
|
|
|
|
- return this;
|
|
|
+ flow += `return `;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ flow += `${ flowSlotData.result };`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const stageData = shadersData[ shaderStage ];
|
|
|
+
|
|
|
+ stageData.uniforms = this.getUniforms( shaderStage );
|
|
|
+ stageData.attributes = this.getAttributes( shaderStage );
|
|
|
+ stageData.varys = this.getVarys( shaderStage );
|
|
|
+ stageData.vars = this.getVars( shaderStage );
|
|
|
+ stageData.codes = this.getCodes( shaderStage );
|
|
|
+ stageData.flow = flow;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this.vertexShader = this._getWGSLVertexCode( shadersData.vertex );
|
|
|
+ this.fragmentShader = this._getWGSLFragmentCode( shadersData.fragment );
|
|
|
|
|
|
}
|
|
|
|
|
|
- _composeShaderCode( code, snippet ) {
|
|
|
+ getMethod( method, shaderStage = this.shaderStage ) {
|
|
|
+
|
|
|
+ if ( wgslPolyfill[ method ] !== undefined ) {
|
|
|
+
|
|
|
+ this._include( method );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return wgslMethods[ method ] || method;
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- // use regex maybe for security?
|
|
|
- const versionStrIndex = code.indexOf( '\n' );
|
|
|
+ getType( type ) {
|
|
|
|
|
|
- let finalCode = code.substr( 0, versionStrIndex ) + '\n\n';
|
|
|
+ return wgslTypeLib[ type ] || type;
|
|
|
|
|
|
- finalCode += snippet;
|
|
|
+ }
|
|
|
|
|
|
- finalCode += code.substr( versionStrIndex );
|
|
|
+ _include( name ) {
|
|
|
|
|
|
- return finalCode;
|
|
|
+ wgslPolyfill[ name ].build( this );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -481,6 +698,82 @@ class WebGPUNodeBuilder extends NodeBuilder {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ _getWGSLVertexCode( shaderData ) {
|
|
|
+
|
|
|
+ return `${ this.getSignature() }
|
|
|
+
|
|
|
+// uniforms
|
|
|
+${shaderData.uniforms}
|
|
|
+
|
|
|
+// varys
|
|
|
+${shaderData.varys}
|
|
|
+
|
|
|
+// codes
|
|
|
+${shaderData.codes}
|
|
|
+
|
|
|
+[[ stage( vertex ) ]]
|
|
|
+fn main( ${shaderData.attributes} ) -> NodeVarysStruct {
|
|
|
+
|
|
|
+ // system
|
|
|
+ var NodeVarys: NodeVarysStruct;
|
|
|
+
|
|
|
+ // vars
|
|
|
+ ${shaderData.vars}
|
|
|
+
|
|
|
+ // flow
|
|
|
+ ${shaderData.flow}
|
|
|
+
|
|
|
+ return NodeVarys;
|
|
|
+
|
|
|
+}
|
|
|
+`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ _getWGSLFragmentCode( shaderData ) {
|
|
|
+
|
|
|
+ return `${ this.getSignature() }
|
|
|
+
|
|
|
+// uniforms
|
|
|
+${shaderData.uniforms}
|
|
|
+
|
|
|
+// codes
|
|
|
+${shaderData.codes}
|
|
|
+
|
|
|
+[[ stage( fragment ) ]]
|
|
|
+fn main( ${shaderData.varys} ) -> [[ location( 0 ) ]] vec4<f32> {
|
|
|
+
|
|
|
+ // vars
|
|
|
+ ${shaderData.vars}
|
|
|
+
|
|
|
+ // flow
|
|
|
+ ${shaderData.flow}
|
|
|
+
|
|
|
+}
|
|
|
+`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ _getWGSLStruct( name, vars ) {
|
|
|
+
|
|
|
+ return `[[ block ]]
|
|
|
+struct ${name} {
|
|
|
+\n${vars}
|
|
|
+};`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ _getWGSLUniforms( name, vars, binding = 0, group = 0 ) {
|
|
|
+
|
|
|
+ const structName = name + 'Struct';
|
|
|
+ const structSnippet = this._getWGSLStruct( structName, vars );
|
|
|
+
|
|
|
+ return `${structSnippet}
|
|
|
+[[ binding( ${binding} ), group( ${group} ) ]]
|
|
|
+var<uniform> ${name} : ${structName};`;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
export default WebGPUNodeBuilder;
|