Selaa lähdekoodia

WebGPU and NodeMaterial: WGSL Support (#22688)

* WebGPURenderPipeline: Use attributes location defined by NodeMaterial

* cleanup

* update to unique flow

* WebGPU: Rewrite GLSL to WGSL and remove GLSL support and libs

* Delete unneeded nodes

* Delete ShaderLib.js

* NodeBuilder: Entire flow approach to generate code without ShaderLib

* NormalMapNode: Move workaround Adreno to JIT approach in ShaderNode

* Remove getContextValue and setContextValue

* ExpressionNode: void support

* NodeKeywords: Is now dynamic, use ShaderNode if possible

* ShaderNode: more swizzle support and keywords

* Nodes: Revision

* update examples

* WebGLRenderer: add .isWebGLRenderer property
sunag 3 vuotta sitten
vanhempi
commit
ceb6a137ac
53 muutettua tiedostoa jossa 1022 lisäystä ja 1154 poistoa
  1. 0 78
      examples/jsm/libs/glslang.js
  2. BIN
      examples/jsm/libs/glslang.wasm
  3. 0 8
      examples/jsm/renderers/nodes/Nodes.js
  4. 22 7
      examples/jsm/renderers/nodes/ShaderNode.js
  5. 2 2
      examples/jsm/renderers/nodes/accessors/MaterialNode.js
  6. 5 5
      examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
  7. 3 3
      examples/jsm/renderers/nodes/accessors/PointUVNode.js
  8. 0 1
      examples/jsm/renderers/nodes/accessors/SkinningNode.js
  9. 7 5
      examples/jsm/renderers/nodes/accessors/UVNode.js
  10. 3 3
      examples/jsm/renderers/nodes/core/ArrayInputNode.js
  11. 2 2
      examples/jsm/renderers/nodes/core/CodeNode.js
  12. 0 39
      examples/jsm/renderers/nodes/core/ConstNode.js
  13. 0 14
      examples/jsm/renderers/nodes/core/ContextNode.js
  14. 13 2
      examples/jsm/renderers/nodes/core/ExpressionNode.js
  15. 0 2
      examples/jsm/renderers/nodes/core/FunctionNode.js
  16. 0 6
      examples/jsm/renderers/nodes/core/Node.js
  17. 68 112
      examples/jsm/renderers/nodes/core/NodeBuilder.js
  18. 13 149
      examples/jsm/renderers/nodes/core/NodeKeywords.js
  19. 0 13
      examples/jsm/renderers/nodes/core/NodeSlot.js
  20. 2 2
      examples/jsm/renderers/nodes/core/NodeVar.js
  21. 10 4
      examples/jsm/renderers/nodes/core/PropertyNode.js
  22. 0 80
      examples/jsm/renderers/nodes/core/StructNode.js
  23. 0 75
      examples/jsm/renderers/nodes/core/StructVarNode.js
  24. 11 14
      examples/jsm/renderers/nodes/core/TempNode.js
  25. 6 7
      examples/jsm/renderers/nodes/core/VarNode.js
  26. 15 9
      examples/jsm/renderers/nodes/core/VaryNode.js
  27. 5 5
      examples/jsm/renderers/nodes/display/ColorSpaceNode.js
  28. 6 8
      examples/jsm/renderers/nodes/display/NormalMapNode.js
  29. 5 5
      examples/jsm/renderers/nodes/functions/BSDFs.js
  30. 10 2
      examples/jsm/renderers/nodes/inputs/TextureNode.js
  31. 5 5
      examples/jsm/renderers/nodes/lights/LightContextNode.js
  32. 12 16
      examples/jsm/renderers/nodes/lights/LightNode.js
  33. 17 5
      examples/jsm/renderers/nodes/math/CondNode.js
  34. 37 22
      examples/jsm/renderers/nodes/math/MathNode.js
  35. 38 14
      examples/jsm/renderers/nodes/math/OperatorNode.js
  36. 3 3
      examples/jsm/renderers/nodes/procedural/CheckerNode.js
  37. 7 7
      examples/jsm/renderers/nodes/utils/JoinNode.js
  38. 7 7
      examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js
  39. 22 0
      examples/jsm/renderers/webgl/nodes/SlotNode.js
  40. 184 88
      examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
  41. 2 2
      examples/jsm/renderers/webgl/nodes/WebGLPhysicalContextNode.js
  42. 1 1
      examples/jsm/renderers/webgpu/WebGPUBindings.js
  43. 2 4
      examples/jsm/renderers/webgpu/WebGPUComputePipelines.js
  44. 2 18
      examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js
  45. 3 5
      examples/jsm/renderers/webgpu/WebGPURenderPipelines.js
  46. 5 7
      examples/jsm/renderers/webgpu/WebGPURenderer.js
  47. 62 28
      examples/jsm/renderers/webgpu/WebGPUTextureUtils.js
  48. 2 3
      examples/jsm/renderers/webgpu/WebGPUTextures.js
  49. 0 152
      examples/jsm/renderers/webgpu/nodes/ShaderLib.js
  50. 393 100
      examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
  51. 5 2
      examples/webgl_points_nodes.html
  52. 3 3
      examples/webgpu_lights_custom.html
  53. 2 0
      src/renderers/WebGLRenderer.js

+ 0 - 78
examples/jsm/libs/glslang.js

@@ -1,78 +0,0 @@
-// 0.0.15
-var Module = (function() {
-  var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
-
-  return (
-function(Module) {
-  Module = Module || {};
-
-var c;c||(c=typeof Module !== 'undefined' ? Module : {});
-c.compileGLSLZeroCopy=function(a,b,d,e){d=!!d;switch(b){case "vertex":var g=0;break;case "fragment":g=4;break;case "compute":g=5;break;default:throw Error("shader_stage must be 'vertex', 'fragment', or 'compute'.");}switch(e||"1.0"){case "1.0":var f=65536;break;case "1.1":f=65792;break;case "1.2":f=66048;break;case "1.3":f=66304;break;case "1.4":f=66560;break;case "1.5":f=66816;break;default:throw Error("spirv_version must be '1.0' ~ '1.5'.");}e=c._malloc(4);b=c._malloc(4);var h=aa([a,g,d,f,e,b]);
-d=k(e);a=k(b);c._free(e);c._free(b);if(0===h)throw Error("GLSL compilation failed");e={};d/=4;e.data=c.HEAPU32.subarray(d,d+a);e.free=function(){c._destroy_output_buffer(h)};return e};c.compileGLSL=function(a,b,d,e){a=c.compileGLSLZeroCopy(a,b,d,e);b=a.data.slice();a.free();return b};var p={},q;for(q in c)c.hasOwnProperty(q)&&(p[q]=c[q]);var r="./this.program",t=!1,u=!1;t="object"===typeof window;u="function"===typeof importScripts;var v="",w;
-if(t||u)u?v=self.location.href:document.currentScript&&(v=document.currentScript.src),_scriptDir&&(v=_scriptDir),0!==v.indexOf("blob:")?v=v.substr(0,v.lastIndexOf("/")+1):v="",u&&(w=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)});var x=c.print||console.log.bind(console),y=c.printErr||console.warn.bind(console);for(q in p)p.hasOwnProperty(q)&&(c[q]=p[q]);p=null;c.thisProgram&&(r=c.thisProgram);var A;
-c.wasmBinary&&(A=c.wasmBinary);"object"!==typeof WebAssembly&&y("no native wasm support detected");function k(a){var b="i32";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return B[a>>0];case "i8":return B[a>>0];case "i16":return ba[a>>1];case "i32":return C[a>>2];case "i64":return C[a>>2];case "float":return ca[a>>2];case "double":return da[a>>3];default:D("invalid type for getValue: "+b)}return null}var E,ea=new WebAssembly.Table({initial:859,maximum:859,element:"anyfunc"}),fa=!1;
-function ha(){var a=c._convert_glsl_to_spirv;a||D("Assertion failed: Cannot call unknown function convert_glsl_to_spirv, make sure it is exported");return a}
-function aa(a){var b="string number boolean number number number".split(" "),d={string:function(a){var b=0;if(null!==a&&void 0!==a&&0!==a){var d=(a.length<<2)+1;b=G(d);ia(a,H,b,d)}return b},array:function(a){var b=G(a.length);B.set(a,b);return b}},e=ha(),g=[],f=0;if(a)for(var h=0;h<a.length;h++){var n=d[b[h]];n?(0===f&&(f=ja()),g[h]=n(a[h])):g[h]=a[h]}a=e.apply(null,g);0!==f&&ka(f);return a}var la="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;
-function I(a,b,d){var e=b+d;for(d=b;a[d]&&!(d>=e);)++d;if(16<d-b&&a.subarray&&la)return la.decode(a.subarray(b,d));for(e="";b<d;){var g=a[b++];if(g&128){var f=a[b++]&63;if(192==(g&224))e+=String.fromCharCode((g&31)<<6|f);else{var h=a[b++]&63;g=224==(g&240)?(g&15)<<12|f<<6|h:(g&7)<<18|f<<12|h<<6|a[b++]&63;65536>g?e+=String.fromCharCode(g):(g-=65536,e+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else e+=String.fromCharCode(g)}return e}
-function ia(a,b,d,e){if(0<e){e=d+e-1;for(var g=0;g<a.length;++g){var f=a.charCodeAt(g);if(55296<=f&&57343>=f){var h=a.charCodeAt(++g);f=65536+((f&1023)<<10)|h&1023}if(127>=f){if(d>=e)break;b[d++]=f}else{if(2047>=f){if(d+1>=e)break;b[d++]=192|f>>6}else{if(65535>=f){if(d+2>=e)break;b[d++]=224|f>>12}else{if(d+3>=e)break;b[d++]=240|f>>18;b[d++]=128|f>>12&63}b[d++]=128|f>>6&63}b[d++]=128|f&63}}b[d]=0}}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");var J,B,H,ba,C,ca,da;
-function ma(a){J=a;c.HEAP8=B=new Int8Array(a);c.HEAP16=ba=new Int16Array(a);c.HEAP32=C=new Int32Array(a);c.HEAPU8=H=new Uint8Array(a);c.HEAPU16=new Uint16Array(a);c.HEAPU32=new Uint32Array(a);c.HEAPF32=ca=new Float32Array(a);c.HEAPF64=da=new Float64Array(a)}var na=c.TOTAL_MEMORY||16777216;c.wasmMemory?E=c.wasmMemory:E=new WebAssembly.Memory({initial:na/65536});E&&(J=E.buffer);na=J.byteLength;ma(J);C[84916]=5582704;
-function K(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b();else{var d=b.J;"number"===typeof d?void 0===b.H?c.dynCall_v(d):c.dynCall_vi(d,b.H):d(void 0===b.H?null:b.H)}}}var oa=[],pa=[],qa=[],ra=[];function sa(){var a=c.preRun.shift();oa.unshift(a)}var L=0,M=null,N=null;c.preloadedImages={};c.preloadedAudios={};function D(a){if(c.onAbort)c.onAbort(a);x(a);y(a);fa=!0;throw new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");}
-function ta(){var a=O;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var O="glslang.wasm";if(!ta()){var ua=O;O=c.locateFile?c.locateFile(ua,v):v+ua}function wa(){try{if(A)return new Uint8Array(A);if(w)return w(O);throw"both async and sync fetching of the wasm failed";}catch(a){D(a)}}
-function xa(){return A||!t&&!u||"function"!==typeof fetch?new Promise(function(a){a(wa())}):fetch(O,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+O+"'";return a.arrayBuffer()}).catch(function(){return wa()})}pa.push({J:function(){ya()}});var za=[null,[],[]],P=0;function Aa(){P+=4;return C[P-4>>2]}var Q={},Ba={};
-function Ca(){if(!R){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:r},b;for(b in Ba)a[b]=Ba[b];var d=[];for(b in a)d.push(b+"="+a[b]);R=d}return R}var R;function S(a){return 0===a%4&&(0!==a%100||0===a%400)}function T(a,b){for(var d=0,e=0;e<=b;d+=a[e++]);return d}var U=[31,29,31,30,31,30,31,31,30,31,30,31],W=[31,28,31,30,31,30,31,31,30,31,30,31];
-function X(a,b){for(a=new Date(a.getTime());0<b;){var d=a.getMonth(),e=(S(a.getFullYear())?U:W)[d];if(b>e-a.getDate())b-=e-a.getDate()+1,a.setDate(1),11>d?a.setMonth(d+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
-function Da(a,b,d,e){function g(a,b,d){for(a="number"===typeof a?a.toString():a||"";a.length<b;)a=d[0]+a;return a}function f(a,b){return g(a,b,"0")}function h(a,b){function V(a){return 0>a?-1:0<a?1:0}var d;0===(d=V(a.getFullYear()-b.getFullYear()))&&0===(d=V(a.getMonth()-b.getMonth()))&&(d=V(a.getDate()-b.getDate()));return d}function n(a){switch(a.getDay()){case 0:return new Date(a.getFullYear()-1,11,29);case 1:return a;case 2:return new Date(a.getFullYear(),0,3);case 3:return new Date(a.getFullYear(),
-0,2);case 4:return new Date(a.getFullYear(),0,1);case 5:return new Date(a.getFullYear()-1,11,31);case 6:return new Date(a.getFullYear()-1,11,30)}}function z(a){a=X(new Date(a.A+1900,0,1),a.G);var b=n(new Date(a.getFullYear()+1,0,4));return 0>=h(n(new Date(a.getFullYear(),0,4)),a)?0>=h(b,a)?a.getFullYear()+1:a.getFullYear():a.getFullYear()-1}var m=C[e+40>>2];e={N:C[e>>2],M:C[e+4>>2],D:C[e+8>>2],C:C[e+12>>2],B:C[e+16>>2],A:C[e+20>>2],F:C[e+24>>2],G:C[e+28>>2],X:C[e+32>>2],L:C[e+36>>2],O:m?m?I(H,m,void 0):
-"":""};d=d?I(H,d,void 0):"";m={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var l in m)d=d.replace(new RegExp(l,"g"),m[l]);var F="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),
-va="January February March April May June July August September October November December".split(" ");m={"%a":function(a){return F[a.F].substring(0,3)},"%A":function(a){return F[a.F]},"%b":function(a){return va[a.B].substring(0,3)},"%B":function(a){return va[a.B]},"%C":function(a){return f((a.A+1900)/100|0,2)},"%d":function(a){return f(a.C,2)},"%e":function(a){return g(a.C,2," ")},"%g":function(a){return z(a).toString().substring(2)},"%G":function(a){return z(a)},"%H":function(a){return f(a.D,2)},
-"%I":function(a){a=a.D;0==a?a=12:12<a&&(a-=12);return f(a,2)},"%j":function(a){return f(a.C+T(S(a.A+1900)?U:W,a.B-1),3)},"%m":function(a){return f(a.B+1,2)},"%M":function(a){return f(a.M,2)},"%n":function(){return"\n"},"%p":function(a){return 0<=a.D&&12>a.D?"AM":"PM"},"%S":function(a){return f(a.N,2)},"%t":function(){return"\t"},"%u":function(a){return a.F||7},"%U":function(a){var b=new Date(a.A+1900,0,1),d=0===b.getDay()?b:X(b,7-b.getDay());a=new Date(a.A+1900,a.B,a.C);return 0>h(d,a)?f(Math.ceil((31-
-d.getDate()+(T(S(a.getFullYear())?U:W,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(d,b)?"01":"00"},"%V":function(a){var b=n(new Date(a.A+1900,0,4)),d=n(new Date(a.A+1901,0,4)),e=X(new Date(a.A+1900,0,1),a.G);return 0>h(e,b)?"53":0>=h(d,e)?"01":f(Math.ceil((b.getFullYear()<a.A+1900?a.G+32-b.getDate():a.G+1-b.getDate())/7),2)},"%w":function(a){return a.F},"%W":function(a){var b=new Date(a.A,0,1),d=1===b.getDay()?b:X(b,0===b.getDay()?1:7-b.getDay()+1);a=new Date(a.A+1900,a.B,a.C);return 0>h(d,a)?f(Math.ceil((31-
-d.getDate()+(T(S(a.getFullYear())?U:W,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(d,b)?"01":"00"},"%y":function(a){return(a.A+1900).toString().substring(2)},"%Y":function(a){return a.A+1900},"%z":function(a){a=a.L;var b=0<=a;a=Math.abs(a)/60;return(b?"+":"-")+String("0000"+(a/60*100+a%60)).slice(-4)},"%Z":function(a){return a.O},"%%":function(){return"%"}};for(l in m)0<=d.indexOf(l)&&(d=d.replace(new RegExp(l,"g"),m[l](e)));l=Ea(d);if(l.length>b)return 0;B.set(l,a);return l.length-1}
-function Ea(a){for(var b=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++d)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:b+4}b=Array(b+1);ia(a,b,0,b.length);return b}
-var Ga={f:function(){},c:function(){c.___errno_location&&(C[c.___errno_location()>>2]=63);return-1},n:function(a,b){P=b;try{var d=Aa();var e=Aa();if(-1===d||0===e)var g=-28;else{var f=Q.K[d];if(f&&e===f.U){var h=(void 0).T(f.S);Q.R(d,h,e,f.flags,f.offset);(void 0).W(h);Q.K[d]=null;f.P&&Fa(f.V)}g=0}return g}catch(n){return D(n),-n.I}},a:function(){},b:function(){D()},k:function(a,b,d){H.set(H.subarray(b,b+d),a)},l:function(a){var b=B.length;if(2147418112<a)return!1;for(var d=1;4>=d;d*=2){var e=b*(1+
-.2/d);e=Math.min(e,a+100663296);e=Math.max(16777216,a,e);0<e%65536&&(e+=65536-e%65536);a:{try{E.grow(Math.min(2147418112,e)-J.byteLength+65535>>16);ma(E.buffer);var g=1;break a}catch(f){}g=void 0}if(g)return!0}return!1},d:function(a,b){var d=0;Ca().forEach(function(e,g){var f=b+d;g=C[a+4*g>>2]=f;for(f=0;f<e.length;++f)B[g++>>0]=e.charCodeAt(f);B[g>>0]=0;d+=e.length+1});return 0},e:function(a,b){var d=Ca();C[a>>2]=d.length;var e=0;d.forEach(function(a){e+=a.length+1});C[b>>2]=e;return 0},h:function(){return 0},
-j:function(){return 0},g:function(a,b,d,e){try{for(var g=0,f=0;f<d;f++){for(var h=C[b+8*f>>2],n=C[b+(8*f+4)>>2],z=0;z<n;z++){var m=H[h+z],l=za[a];0===m||10===m?((1===a?x:y)(I(l,0)),l.length=0):l.push(m)}g+=n}C[e>>2]=g;return 0}catch(F){return D(F),F.I}},memory:E,o:function(){},i:function(){},m:function(a,b,d,e){return Da(a,b,d,e)},table:ea},Ha=function(){function a(a){c.asm=a.exports;L--;c.monitorRunDependencies&&c.monitorRunDependencies(L);0==L&&(null!==M&&(clearInterval(M),M=null),N&&(a=N,N=null,
-a()))}function b(b){a(b.instance)}function d(a){return xa().then(function(a){return WebAssembly.instantiate(a,e)}).then(a,function(a){y("failed to asynchronously prepare wasm: "+a);D(a)})}var e={env:Ga,wasi_snapshot_preview1:Ga};L++;c.monitorRunDependencies&&c.monitorRunDependencies(L);if(c.instantiateWasm)try{return c.instantiateWasm(e,a)}catch(g){return y("Module.instantiateWasm callback failed with error: "+g),!1}(function(){if(A||"function"!==typeof WebAssembly.instantiateStreaming||ta()||"function"!==
-typeof fetch)return d(b);fetch(O,{credentials:"same-origin"}).then(function(a){return WebAssembly.instantiateStreaming(a,e).then(b,function(a){y("wasm streaming compile failed: "+a);y("falling back to ArrayBuffer instantiation");d(b)})})})();return{}}();c.asm=Ha;var ya=c.___wasm_call_ctors=function(){return(ya=c.___wasm_call_ctors=c.asm.p).apply(null,arguments)};c._convert_glsl_to_spirv=function(){return(c._convert_glsl_to_spirv=c.asm.q).apply(null,arguments)};
-c._destroy_output_buffer=function(){return(c._destroy_output_buffer=c.asm.r).apply(null,arguments)};c._malloc=function(){return(c._malloc=c.asm.s).apply(null,arguments)};var Fa=c._free=function(){return(Fa=c._free=c.asm.t).apply(null,arguments)},ja=c.stackSave=function(){return(ja=c.stackSave=c.asm.u).apply(null,arguments)},G=c.stackAlloc=function(){return(G=c.stackAlloc=c.asm.v).apply(null,arguments)},ka=c.stackRestore=function(){return(ka=c.stackRestore=c.asm.w).apply(null,arguments)};
-c.dynCall_vi=function(){return(c.dynCall_vi=c.asm.x).apply(null,arguments)};c.dynCall_v=function(){return(c.dynCall_v=c.asm.y).apply(null,arguments)};c.asm=Ha;var Y;c.then=function(a){if(Y)a(c);else{var b=c.onRuntimeInitialized;c.onRuntimeInitialized=function(){b&&b();a(c)}}return c};N=function Ia(){Y||Z();Y||(N=Ia)};
-function Z(){function a(){if(!Y&&(Y=!0,!fa)){K(pa);K(qa);if(c.onRuntimeInitialized)c.onRuntimeInitialized();if(c.postRun)for("function"==typeof c.postRun&&(c.postRun=[c.postRun]);c.postRun.length;){var a=c.postRun.shift();ra.unshift(a)}K(ra)}}if(!(0<L)){if(c.preRun)for("function"==typeof c.preRun&&(c.preRun=[c.preRun]);c.preRun.length;)sa();K(oa);0<L||(c.setStatus?(c.setStatus("Running..."),setTimeout(function(){setTimeout(function(){c.setStatus("")},1);a()},1)):a())}}c.run=Z;
-if(c.preInit)for("function"==typeof c.preInit&&(c.preInit=[c.preInit]);0<c.preInit.length;)c.preInit.pop()();Z();
-
-
-  return Module
-}
-);
-})();
-if (typeof exports === 'object' && typeof module === 'object')
-      module.exports = Module;
-    else if (typeof define === 'function' && define['amd'])
-      define([], function() { return Module; });
-    else if (typeof exports === 'object')
-      exports["Module"] = Module;
-    export default (() => {
-    const initialize = () => {
-        return new Promise(resolve => {
-            Module({
-                locateFile() {
-                    const i = import.meta.url.lastIndexOf('/')
-                    return import.meta.url.substring(0, i) + '/glslang.wasm';
-                },
-                onRuntimeInitialized() {
-                    resolve({
-                        compileGLSLZeroCopy: this.compileGLSLZeroCopy,
-                        compileGLSL: this.compileGLSL,
-                    });
-                },
-            });
-        });
-    };
-
-    let instance;
-    return () => {
-        if (!instance) {
-            instance = initialize();
-        }
-        return instance;
-    };
-})();

BIN
examples/jsm/libs/glslang.wasm


+ 0 - 8
examples/jsm/renderers/nodes/Nodes.js

@@ -2,7 +2,6 @@
 import ArrayInputNode from './core/ArrayInputNode.js';
 import AttributeNode from './core/AttributeNode.js';
 import CodeNode from './core/CodeNode.js';
-import ConstNode from './core/ConstNode.js';
 import ContextNode from './core/ContextNode.js';
 import ExpressionNode from './core/ExpressionNode.js';
 import FunctionCallNode from './core/FunctionCallNode.js';
@@ -15,13 +14,10 @@ import NodeCode from './core/NodeCode.js';
 import NodeFrame from './core/NodeFrame.js';
 import NodeFunctionInput from './core/NodeFunctionInput.js';
 import NodeKeywords from './core/NodeKeywords.js';
-import NodeSlot from './core/NodeSlot.js';
 import NodeUniform from './core/NodeUniform.js';
 import NodeVar from './core/NodeVar.js';
 import NodeVary from './core/NodeVary.js';
 import PropertyNode from './core/PropertyNode.js';
-import StructNode from './core/StructNode.js';
-import StructVarNode from './core/StructVarNode.js';
 import TempNode from './core/TempNode.js';
 import VarNode from './core/VarNode.js';
 import VaryNode from './core/VaryNode.js';
@@ -90,7 +86,6 @@ export {
 	ArrayInputNode,
 	AttributeNode,
 	CodeNode,
-	ConstNode,
 	ContextNode,
 	ExpressionNode,
 	FunctionCallNode,
@@ -103,13 +98,10 @@ export {
 	NodeFrame,
 	NodeFunctionInput,
 	NodeKeywords,
-	NodeSlot,
 	NodeUniform,
 	NodeVar,
 	NodeVary,
 	PropertyNode,
-	StructNode,
-	StructVarNode,
 	TempNode,
 	VarNode,
 	VaryNode,

+ 22 - 7
examples/jsm/renderers/nodes/ShaderNode.js

@@ -40,13 +40,17 @@ const NodeHandler = {
 
 		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
 
-			const splitProps = prop.match( /^[xyzwst]{1,4}$/ );
-
-			if ( splitProps !== null ) {
+			if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true ) {
 
 				// accessing properties ( swizzle )
 
-				return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
+				prop = prop
+					.replace( /r|s/g, 'x' )
+					.replace( /g|t/g, 'y' )
+					.replace( /b|p/g, 'z' )
+					.replace( /a|q/g, 'w' );
+
+				return ShaderNodeObject( new SplitNode( node, prop ) );
 
 			} else if ( /^\d+$/.test( prop ) === true ) {
 
@@ -244,6 +248,14 @@ export const vec4 = ( ...params ) => {
 
 };
 
+export const addTo = ( varNode, ...params ) => {
+
+	varNode.node = add( varNode.node, ...ShaderNodeArray( params ) );
+
+	return ShaderNodeObject( varNode );
+
+};
+
 export const add = ShaderNodeProxy( OperatorNode, '+' );
 export const sub = ShaderNodeProxy( OperatorNode, '-' );
 export const mul = ShaderNodeProxy( OperatorNode, '*' );
@@ -269,9 +281,11 @@ export const PI = float( 3.141592653589793 );
 export const RECIPROCAL_PI = float( 0.3183098861837907 );
 export const EPSILON = float( 1e-6 );
 
-export const materialDiffuseColor = new PropertyNode( 'MaterialDiffuseColor', 'vec4' );
-export const materialRoughness = new PropertyNode( 'MaterialRoughness', 'float' );
-export const materialSpecularTint = new PropertyNode( 'MaterialSpecularTint', 'vec4' );
+export const diffuseColor = new PropertyNode( 'DiffuseColor', 'vec4' );
+export const roughness = new PropertyNode( 'Roughness', 'float' );
+export const metalness = new PropertyNode( 'Metalness', 'float' );
+export const alphaTest = new PropertyNode( 'AlphaTest', 'float' );
+export const specularTint = new PropertyNode( 'SpecularTint', 'color' );
 
 export const negate = ShaderNodeProxy( MathNode, 'negate' );
 export const floor = ShaderNodeProxy( MathNode, 'floor' );
@@ -290,6 +304,7 @@ export const pow = ShaderNodeProxy( MathNode, 'pow' );
 export const pow2 = ShaderNodeProxy( MathNode, 'pow', 2 );
 export const pow3 = ShaderNodeProxy( MathNode, 'pow', 3 );
 export const pow4 = ShaderNodeProxy( MathNode, 'pow', 4 );
+export const exp = ShaderNodeProxy( MathNode, 'exp' );
 export const exp2 = ShaderNodeProxy( MathNode, 'exp2' );
 export const saturate = ShaderNodeProxy( MathNode, 'saturate' );
 export const transformDirection = ShaderNodeProxy( MathNode, 'transformDirection' );

+ 2 - 2
examples/jsm/renderers/nodes/accessors/MaterialNode.js

@@ -22,7 +22,7 @@ class MaterialNode extends Node {
 	getNodeType( builder ) {
 
 		const scope = this.scope;
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 
 		if ( scope === MaterialNode.COLOR ) {
 
@@ -46,7 +46,7 @@ class MaterialNode extends Node {
 
 	generate( builder, output ) {
 
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 		const scope = this.scope;
 
 		let node = null;

+ 5 - 5
examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js

@@ -12,16 +12,16 @@ class ModelViewProjectionNode extends Node {
 
 		this.position = position;
 
-		this._mvpMatrix = new OperatorNode( '*', new CameraNode( CameraNode.PROJECTION_MATRIX ), new ModelNode( ModelNode.VIEW_MATRIX ) );
-
 	}
 
 	generate( builder ) {
 
-		const mvpSnipped = this._mvpMatrix.build( builder );
-		const positionSnipped = this.position.build( builder, 'vec3' );
+		const position = this.position;
+
+		const mvpMatrix = new OperatorNode( '*', new CameraNode( CameraNode.PROJECTION_MATRIX ), new ModelNode( ModelNode.VIEW_MATRIX ) );
+		const mvpNode = new OperatorNode( '*', mvpMatrix, position );
 
-		return `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`;
+		return mvpNode.build( builder );
 
 	}
 

+ 3 - 3
examples/jsm/renderers/nodes/accessors/PointUVNode.js

@@ -6,11 +6,9 @@ class PointUVNode extends Node {
 
 		super( 'vec2' );
 
-		Object.defineProperty( this, 'isPointUVNode', { value: true } );
-
 	}
 
-	generate( builder ) {
+	generate( /*builder*/ ) {
 
 		return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
 
@@ -18,4 +16,6 @@ class PointUVNode extends Node {
 
 }
 
+PointUVNode.prototype.isPointUVNode = true;
+
 export default PointUVNode;

+ 0 - 1
examples/jsm/renderers/nodes/accessors/SkinningNode.js

@@ -1,6 +1,5 @@
 import Node from '../core/Node.js';
 import AttributeNode from '../core/AttributeNode.js';
-import ConstNode from '../core/ConstNode.js';
 import PositionNode from '../accessors/PositionNode.js';
 import NormalNode from '../accessors/NormalNode.js';
 import Matrix4Node from '../inputs/Matrix4Node.js';

+ 7 - 5
examples/jsm/renderers/nodes/accessors/UVNode.js

@@ -2,22 +2,24 @@ import AttributeNode from '../core/AttributeNode.js';
 
 class UVNode extends AttributeNode {
 
-	constructor( index = 0 ) {
+	constructor( value = 0 ) {
 
 		super( null, 'vec2' );
 
-		this.index = index;
-
-		Object.defineProperty( this, 'isUVNode', { value: true } );
+		this.value = value;
 
 	}
 
 	getAttributeName( /*builder*/ ) {
 
-		return 'uv' + ( this.index > 0 ? this.index + 1 : '' );
+		const value = this.value;
+
+		return 'uv' + ( value > 0 ? value + 1 : '' );
 
 	}
 
 }
 
+UVNode.prototype.isUVNode = true;
+
 export default UVNode;

+ 3 - 3
examples/jsm/renderers/nodes/core/ArrayInputNode.js

@@ -2,17 +2,17 @@ import InputNode from './InputNode.js';
 
 class ArrayInputNode extends InputNode {
 
-	constructor( value = [] ) {
+	constructor( nodes = [] ) {
 
 		super();
 
-		this.value = value;
+		this.nodes = nodes;
 
 	}
 
 	getNodeType( builder ) {
 
-		return this.value[ 0 ].getNodeType( builder );
+		return this.nodes[ 0 ].getNodeType( builder );
 
 	}
 

+ 2 - 2
examples/jsm/renderers/nodes/core/CodeNode.js

@@ -12,8 +12,6 @@ class CodeNode extends Node {
 
 		this._includes = [];
 
-		Object.defineProperty( this, 'isCodeNode', { value: true } );
-
 	}
 
 	setIncludes( includes ) {
@@ -75,4 +73,6 @@ class CodeNode extends Node {
 
 }
 
+CodeNode.prototype.isCodeNode = true;
+
 export default CodeNode;

+ 0 - 39
examples/jsm/renderers/nodes/core/ConstNode.js

@@ -1,39 +0,0 @@
-import CodeNode from './CodeNode.js';
-
-class ConstNode extends CodeNode {
-
-	constructor( code = '', type = '', name = '' ) {
-
-		super( code, type );
-
-		this.includes = [];
-
-		this.name = name;
-
-	}
-
-	generate( builder ) {
-
-		const code = super.generate( builder );
-
-		const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
-
-		if ( this.name !== '' ) {
-
-			// use a custom property name
-
-			nodeCode.name = this.name;
-
-		}
-
-		const propertyName = builder.getPropertyName( nodeCode );
-
-		nodeCode.code = `#define ${propertyName} ${code}`;
-
-		return propertyName;
-
-	}
-
-}
-
-export default ConstNode;

+ 0 - 14
examples/jsm/renderers/nodes/core/ContextNode.js

@@ -11,20 +11,6 @@ class ContextNode extends Node {
 
 	}
 
-	setContextValue( name, value ) {
-
-		this.context[ name ] = value;
-
-		return this;
-
-	}
-
-	getContextValue( name ) {
-
-		return this.context[ name ];
-
-	}
-
 	getNodeType( builder ) {
 
 		return this.node.getNodeType( builder );

+ 13 - 2
examples/jsm/renderers/nodes/core/ExpressionNode.js

@@ -2,7 +2,7 @@ import TempNode from './TempNode.js';
 
 class ExpressionNode extends TempNode {
 
-	constructor( snipped = '', nodeType = null ) {
+	constructor( snipped = '', nodeType = 'void' ) {
 
 		super( nodeType );
 
@@ -12,7 +12,18 @@ class ExpressionNode extends TempNode {
 
 	generate( builder ) {
 
-		return `( ${ this.snipped } )`;
+		const type = this.getNodeType( builder );
+		const snipped = this.snipped;
+
+		if ( type === 'void' ) {
+
+			builder.addFlowCode( snipped );
+
+		} else {
+
+			return `( ${ snipped } )`;
+
+		}
 
 	}
 

+ 0 - 2
examples/jsm/renderers/nodes/core/FunctionNode.js

@@ -7,8 +7,6 @@ class FunctionNode extends CodeNode {
 
 		super( code );
 
-		this.inputs = [];
-
 		this.useKeywords = true;
 
 	}

+ 0 - 6
examples/jsm/renderers/nodes/core/Node.js

@@ -38,12 +38,6 @@ class Node {
 
 	}
 
-	getTypeLength( builder ) {
-
-		return builder.getTypeLength( this.getNodeType( builder ) );
-
-	}
-
 	update( /*frame*/ ) {
 
 		console.warn( 'Abstract function.' );

+ 68 - 112
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -6,7 +6,9 @@ import NodeCode from './NodeCode.js';
 import NodeKeywords from './NodeKeywords.js';
 import { NodeUpdateType } from './constants.js';
 
-import { LinearEncoding } from 'three';
+import { REVISION, LinearEncoding } from 'three';
+
+const shaderStages = [ 'fragment', 'vertex' ];
 
 class NodeBuilder {
 
@@ -24,8 +26,8 @@ class NodeBuilder {
 		this.vertexShader = null;
 		this.fragmentShader = null;
 
-		this.slots = { vertex: [], fragment: [] };
-		this.defines = { vertex: {}, fragment: {} };
+		this.flowNodes = { vertex: [], fragment: [] };
+		this.flowCode = { vertex: '', fragment: '' };
 		this.uniforms = { vertex: [], fragment: [], index: 0 };
 		this.codes = { vertex: [], fragment: [] };
 		this.attributes = [];
@@ -40,14 +42,16 @@ class NodeBuilder {
 		};
 
 		this.nodesData = new WeakMap();
+		this.flowsData = new WeakMap();
 
 		this.shaderStage = null;
-		this.slot = null;
+		this.node = null;
 
 	}
 
 	addStack( node ) {
-/*
+
+		/*
 		if ( this.stack.indexOf( node ) !== - 1 ) {
 
 			console.warn( 'Recursive node: ', node );
@@ -90,21 +94,23 @@ class NodeBuilder {
 
 	}
 
-	getNodeFromHash( hash ) {
+	getMethod( method ) {
 
-		return this.hashNodes[ hash ];
+		return method;
 
 	}
 
-	addSlot( shaderStage, slot ) {
+	getNodeFromHash( hash ) {
 
-		this.slots[ shaderStage ].push( slot );
+		return this.hashNodes[ hash ];
 
 	}
 
-	define( shaderStage, name, value = '' ) {
+	addFlow( shaderStage, node ) {
 
-		this.defines[ shaderStage ][ name ] = value;
+		this.flowNodes[ shaderStage ].push( node );
+
+		return node;
 
 	}
 
@@ -120,12 +126,6 @@ class NodeBuilder {
 
 	}
 
-	getContextValue( name ) {
-
-		return this.context[ name ];
-
-	}
-
 	getTexture( /* textureProperty, uvSnippet, biasSnippet = null */ ) {
 
 		console.warn( 'Abstract function.' );
@@ -138,6 +138,7 @@ class NodeBuilder {
 
 	}
 
+	// rename to generate
 	getConst( type, value ) {
 
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
@@ -156,6 +157,12 @@ class NodeBuilder {
 
 	}
 
+	generateMethod( method ) {
+
+		return method;
+
+	}
+
 	getAttribute( name, type ) {
 
 		const attributes = this.attributes;
@@ -182,7 +189,7 @@ class NodeBuilder {
 
 	}
 
-	getPropertyName( node ) {
+	getPropertyName( node/*, shaderStage*/ ) {
 
 		return node.name;
 
@@ -376,30 +383,33 @@ class NodeBuilder {
 
 	addFlowCode( code ) {
 
-		if ( ! /;\s*$/.test( code ) ) {
+		this.flow.code += code;
 
-			code += ';';
+	}
 
-		}
+	getFlowData( shaderStage, node ) {
 
-		this.flow.code += code + ' ';
+		return this.flowsData.get( node );
 
 	}
 
-	flowSlot( slot, shaderStage = this.shaderStage ) {
+	flowNode( node ) {
 
-		this.slot = slot;
+		this.node = node;
 
-		const flowData = this.flowNode( slot.node, slot.output );
+		const output = node.getNodeType( this );
 
-		this.define( shaderStage, `NODE_CODE_${slot.name}`, flowData.code );
-		this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
+		const flowData = this.flowChildNode( node, output );
 
-		this.slot = null;
+		this.flowsData.set( node, flowData );
+
+		this.node = null;
+
+		return flowData;
 
 	}
 
-	flowNode( node, output = null ) {
+	flowChildNode( node, output = null ) {
 
 		const previousFlow = this.flow;
 
@@ -423,17 +433,15 @@ class NodeBuilder {
 
 		this.setShaderStage( shaderStage );
 
-		const flowData = this.flowNode( node, output );
+		const flowData = this.flowChildNode( node, output );
 
 		if ( propertyName !== null ) {
 
-			flowData.code += `${propertyName} = ${flowData.result}; `;
+			flowData.code += `${propertyName} = ${flowData.result};\n\t`;
 
 		}
 
-		const shaderStageCode = this.defines[ shaderStage ][ 'NODE_CODE' ] + flowData.code;
-
-		this.define( shaderStage, 'NODE_CODE', shaderStageCode );
+		this.flowCode[ shaderStage ] = this.flowCode[ shaderStage ] + flowData.code;
 
 		this.setShaderStage( previousShaderStage );
 
@@ -441,22 +449,6 @@ class NodeBuilder {
 
 	}
 
-	getDefines( shaderStage ) {
-
-		const defines = this.defines[ shaderStage ];
-
-		let code = '';
-
-		for ( const name in defines ) {
-
-			code += `#define ${name} ${defines[ name ]}\n`;
-
-		}
-
-		return code;
-
-	}
-
 	getAttributes( shaderStage ) {
 
 		let snippet = '';
@@ -543,18 +535,15 @@ class NodeBuilder {
 
 	}
 
-	build() {
-
-		const shaderStages = [ 'fragment', 'vertex' ];
-		const shaderData = {};
+	buildCode() {
 
-		for ( const shaderStage of shaderStages ) {
+		console.warn( 'Abstract function.' );
 
-			this.define( shaderStage, 'NODE_CODE', '' );
+	}
 
-		}
+	build() {
 
-		if ( this.context.vertex && this.context.vertex.isNode === true ) {
+		if ( this.context.vertex && this.context.vertex.isNode ) {
 
 			this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
 
@@ -564,11 +553,11 @@ class NodeBuilder {
 
 			this.setShaderStage( shaderStage );
 
-			const slots = this.slots[ shaderStage ];
+			const flowNodes = this.flowNodes[ shaderStage ];
 
-			for ( const slot of slots ) {
+			for ( const node of flowNodes ) {
 
-				this.flowSlot( slot, shaderStage );
+				this.flowNode( node, shaderStage );
 
 			}
 
@@ -576,45 +565,7 @@ class NodeBuilder {
 
 		this.setShaderStage( null );
 
-		for ( const shaderStage of shaderStages ) {
-
-			const defines = this.getDefines( shaderStage );
-			const uniforms = this.getUniforms( shaderStage );
-			const attributes = this.getAttributes( shaderStage );
-			const varys = this.getVarys( shaderStage );
-			const vars = this.getVars( shaderStage );
-			const codes = this.getCodes( shaderStage );
-
-			shaderData[ shaderStage ] = `
-				// <node_builder>
-
-				#define NODE_MATERIAL
-
-				// defines
-				${defines}
-
-				// uniforms
-				${uniforms}
-
-				// attributes
-				${attributes}
-
-				// varys
-				${varys}
-
-				// vars
-				${vars}
-
-				// codes
-				${codes}
-
-				// </node_builder>
-				`;
-
-		}
-
-		this.vertexShader = shaderData.vertex;
-		this.fragmentShader = shaderData.fragment;
+		this.buildCode();
 
 		return this;
 
@@ -641,30 +592,30 @@ class NodeBuilder {
 
 			case 'vec2 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
 			case 'vec2 to float' : return `${ snippet }.x`;
-			case 'vec2 to vec3'  : return `${ this.getType( 'vec3' ) }( ${ snippet }, 0.0 )`;
-			case 'vec2 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet }.xy, 0.0, 1.0 )`;
+			case 'vec2 to vec3' : return `${ this.getType( 'vec3' ) }( ${ snippet }, 0.0 )`;
+			case 'vec2 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet }.xy, 0.0, 1.0 )`;
 
 			case 'vec3 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
 			case 'vec3 to float' : return `${ snippet }.x`;
-			case 'vec3 to vec2'  : return `${ snippet }.xy`;
-			case 'vec3 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet }, 1.0 )`;
+			case 'vec3 to vec2' : return `${ snippet }.xy`;
+			case 'vec3 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet }, 1.0 )`;
 
 			case 'vec4 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
 			case 'vec4 to float' : return `${ snippet }.x`;
-			case 'vec4 to vec2'  : return `${ snippet }.xy`;
-			case 'vec4 to vec3'  : return `${ snippet }.xyz`;
+			case 'vec4 to vec2' : return `${ snippet }.xy`;
+			case 'vec4 to vec3' : return `${ snippet }.xyz`;
 
 			case 'mat3 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
 			case 'mat3 to float' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
-			case 'mat3 to vec2'  : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xy`;
-			case 'mat3 to vec3'  : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xyz`;
-			case 'mat3 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ), 1.0 )`;
+			case 'mat3 to vec2' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xy`;
+			case 'mat3 to vec3' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xyz`;
+			case 'mat3 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ), 1.0 )`;
 
 			case 'mat4 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
 			case 'mat4 to float' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
-			case 'mat4 to vec2'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xy`;
-			case 'mat4 to vec3'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xyz`;
-			case 'mat4 to vec4'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) )`;
+			case 'mat4 to vec2' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xy`;
+			case 'mat4 to vec3' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xyz`;
+			case 'mat4 to vec4' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) )`;
 
 		}
 
@@ -672,6 +623,11 @@ class NodeBuilder {
 
 	}
 
+	getSignature() {
+
+		return `// Three.js r${ REVISION } • NodeMaterial System\n`;
+
+	}
 
 }
 

+ 13 - 149
examples/jsm/renderers/nodes/core/NodeKeywords.js

@@ -1,59 +1,9 @@
-import VarNode from './VarNode.js';
-import PropertyNode from './PropertyNode.js';
-import PositionNode from '../accessors/PositionNode.js';
-import NormalNode from '../accessors/NormalNode.js';
-
 class NodeKeywords {
 
-	static PositionLocal = 'PositionLocal';
-	static PositionWorld = 'PositionWorld';
-	static PositionView = 'PositionView';
-	static PositionViewDirection = 'PositionViewDirection';
-
-	static NormalLocal = 'NormalLocal';
-	static NormalWorld = 'NormalWorld';
-	static NormalView = 'NormalView';
-
-	static Irradiance = 'Irradiance';
-	static ReflectedLightIndirectDiffuse = 'ReflectedLightIndirectDiffuse';
-	static ReflectedLightIndirectSpecular = 'ReflectedLightIndirectSpecular';
-	static ReflectedLightDirectDiffuse = 'ReflectedLightDirectDiffuse';
-	static ReflectedLightDirectSpecular = 'ReflectedLightDirectSpecular';
-
-	static MaterialDiffuseColor = 'MaterialDiffuseColor';
-
-	// STANDARD
-	static MaterialRoughness = 'MaterialRoughness';
-	static MaterialMetalness = 'MaterialMetalness';
-	static MaterialSpecularTint = 'MaterialSpecularTint';
-
 	constructor() {
 
-		this.keywords = [
-			// nodes
-			NodeKeywords.PositionLocal,
-			NodeKeywords.PositionWorld,
-			NodeKeywords.PositionView,
-			NodeKeywords.PositionViewDirection,
-			NodeKeywords.NormalLocal,
-			NodeKeywords.NormalWorld,
-			NodeKeywords.NormalView,
-			// vars -> float
-			NodeKeywords.MaterialRoughness,
-			NodeKeywords.MaterialMetalness,
-			// vars -> vec3
-			NodeKeywords.Irradiance,
-			NodeKeywords.ReflectedLightIndirectDiffuse,
-			NodeKeywords.ReflectedLightIndirectSpecular,
-			NodeKeywords.ReflectedLightDirectDiffuse,
-			NodeKeywords.ReflectedLightDirectSpecular,
-			NodeKeywords.MaterialSpecularTint,
-			// vars -> vec4
-			NodeKeywords.MaterialDiffuseColor
-		];
-
+		this.keywords = [];
 		this.nodes = [];
-
 		this.keywordsCallback = {};
 
 	}
@@ -62,96 +12,11 @@ class NodeKeywords {
 
 		let node = this.nodes[ name ];
 
-		if ( node === undefined ) {
-
-			if ( this.keywordsCallback[ name ] !== undefined ) {
-				
-				node = this.keywordsCallback[ name ]( name );
-				
-				this.nodes[ name ] = node;
-				
-				return node;
-				
-			}
-
-			switch ( name ) {
-
-				case NodeKeywords.PositionLocal:
-
-					node = new VarNode( new PositionNode( PositionNode.GEOMETRY ), name );
-
-					break;
-
-				case NodeKeywords.PositionWorld:
-
-					node = new VarNode( new PositionNode( PositionNode.WORLD ), name );
-
-					break;
-
-				case NodeKeywords.PositionView:
-
-					node = new VarNode( new PositionNode( PositionNode.VIEW ), name );
-
-					break;
-
-				case NodeKeywords.PositionViewDirection:
-
-					node = new VarNode( new PositionNode( PositionNode.VIEW_DIRECTION ), name );
-
-					break;
-
-				case NodeKeywords.NormalLocal:
+		if ( node === undefined && this.keywordsCallback[ name ] !== undefined ) {
 
-					node = new VarNode( new NormalNode( NormalNode.GEOMETRY ), name );
+			node = this.keywordsCallback[ name ]( name );
 
-					break;
-
-				case NodeKeywords.NormalWorld:
-
-					node = new VarNode( new NormalNode( NormalNode.WORLD ), name );
-
-					break;
-
-				case NodeKeywords.NormalView:
-
-					node = new VarNode( new NormalNode( NormalNode.VIEW ), name );
-
-					break;
-
-				// floats properties
-				case NodeKeywords.MaterialRoughness:
-				case NodeKeywords.MaterialMetalness:
-
-					node = new PropertyNode( name, 'float' );
-
-					break;
-
-				// vec3 properties
-				case NodeKeywords.Irradiance:
-				case NodeKeywords.ReflectedLightIndirectDiffuse:
-				case NodeKeywords.ReflectedLightIndirectSpecular:
-				case NodeKeywords.ReflectedLightDirectDiffuse:
-				case NodeKeywords.ReflectedLightDirectSpecular:
-				case NodeKeywords.MaterialSpecularTint:
-
-					node = new PropertyNode( name, 'vec3' );
-
-					break;
-
-				// vec4 properties
-				case NodeKeywords.MaterialDiffuseColor:
-
-					node = new PropertyNode( name, 'vec4' );
-
-					break;
-
-			}
-
-			if ( node !== undefined ) {
-
-				this.nodes[ name ] = node;
-
-			}
+			this.nodes[ name ] = node;
 
 		}
 
@@ -160,13 +25,12 @@ class NodeKeywords {
 	}
 
 	addKeyword( name, callback ) {
-		
+
 		this.keywords.push( name );
-		
 		this.keywordsCallback[ name ] = callback;
-		
+
 		return this;
-		
+
 	}
 
 	parse( code ) {
@@ -177,7 +41,7 @@ class NodeKeywords {
 
 		const codeKeywords = code.match( regExp );
 
-		const keywords = [];
+		const keywordNodes = [];
 
 		if ( codeKeywords !== null ) {
 
@@ -185,9 +49,9 @@ class NodeKeywords {
 
 				const node = this.getNode( keyword );
 
-				if ( keywords.indexOf( node ) === - 1 ) {
+				if ( node !== undefined && keywordNodes.indexOf( node ) === - 1 ) {
 
-					keywords.push( node );
+					keywordNodes.push( node );
 
 				}
 
@@ -195,15 +59,15 @@ class NodeKeywords {
 
 		}
 
-		return keywords;
+		return keywordNodes;
 
 	}
 
 	include( builder, code ) {
 
-		const keywords = this.parse( code );
+		const keywordNodes = this.parse( code );
 
-		for ( const keywordNode of keywords ) {
+		for ( const keywordNode of keywordNodes ) {
 
 			keywordNode.build( builder );
 

+ 0 - 13
examples/jsm/renderers/nodes/core/NodeSlot.js

@@ -1,13 +0,0 @@
-class NodeSlot {
-
-	constructor( node, name, output ) {
-
-		this.node = node;
-		this.name = name;
-		this.output = output;
-
-	}
-
-}
-
-export default NodeSlot;

+ 2 - 2
examples/jsm/renderers/nodes/core/NodeVar.js

@@ -5,10 +5,10 @@ class NodeVar {
 		this.name = name;
 		this.type = type;
 
-		Object.defineProperty( this, 'isNodeVar', { value: true } );
-
 	}
 
 }
 
+NodeVar.prototype.isNodeVar = true;
+
 export default NodeVar;

+ 10 - 4
examples/jsm/renderers/nodes/core/PropertyNode.js

@@ -2,7 +2,7 @@ import Node from './Node.js';
 
 class PropertyNode extends Node {
 
-	constructor( name, nodeType ) {
+	constructor( name = null, nodeType = 'vec4' ) {
 
 		super( nodeType );
 
@@ -10,16 +10,22 @@ class PropertyNode extends Node {
 
 	}
 
-	getHash( /*builder*/ ) {
+	getHash( builder ) {
 
-		return this.name;
+		return this.name || super.getHash( builder );
 
 	}
 
 	generate( builder ) {
 
 		const nodeVary = builder.getVarFromNode( this, this.getNodeType( builder ) );
-		nodeVary.name = this.name;
+		const name = this.name;
+
+		if ( name !== null ) {
+
+			nodeVary.name = name;
+
+		}
 
 		return builder.getPropertyName( nodeVary );
 

+ 0 - 80
examples/jsm/renderers/nodes/core/StructNode.js

@@ -1,80 +0,0 @@
-import CodeNode from './CodeNode.js';
-import StructVarNode from './StructVarNode.js';
-
-class StructNode extends CodeNode {
-
-	constructor( inputs = {}, name = '' ) {
-
-		super();
-
-		this.inputs = inputs;
-		this.name = name;
-
-	}
-
-	getNodeType( builder ) {
-
-		if ( this.name !== '' ) {
-
-			return this.name;
-
-		} else {
-
-			const codeNode = builder.getCodeFromNode( this, 'code' );
-
-			return codeNode.name;
-
-		}
-
-	}
-
-	create( inputs = {} ) {
-
-		return new StructVarNode( this, inputs );
-
-	}
-
-	generate( builder, output ) {
-
-		const type = this.getNodeType( builder );
-		const inputs = this.inputs;
-
-		let code = `struct ${type} {\n`;
-
-		for ( const inputName in inputs ) {
-
-			const inputType = inputs[ inputName ];
-
-			code += `\t${inputType} ${inputName};\n`;
-
-		}
-
-		code += `};`;
-
-		this.code = code;
-
-		super.generate( builder, output );
-
-		if ( output === 'var' ) {
-
-			const nodeData = builder.getDataFromNode( this );
-
-			if ( nodeData.index === undefined ) {
-
-				nodeData.index = 0;
-
-			}
-
-			return `structVar${nodeData.index ++}`;
-
-		} else {
-
-			return code;
-
-		}
-
-	}
-
-}
-
-export default StructNode;

+ 0 - 75
examples/jsm/renderers/nodes/core/StructVarNode.js

@@ -1,75 +0,0 @@
-import Node from './Node.js';
-import FloatNode from '../inputs/FloatNode.js';
-
-const zeroValue = new FloatNode( 0 ).setConst( true );
-
-class StructVarNode extends Node {
-
-	constructor( struct, inputs = {} ) {
-
-		super();
-
-		this.struct = struct;
-		this.inputs = inputs;
-
-	}
-
-	getNodeType( builder ) {
-
-		return this.struct.getNodeType( builder );
-
-	}
-
-	generate( builder ) {
-
-		const type = this.getNodeType( builder );
-
-		const struct = this.struct;
-
-		const inputs = this.inputs;
-		const structInputs = this.struct.inputs;
-
-		const nodeData = builder.getDataFromNode( this );
-
-		let property = nodeData.property;
-
-		if ( property === undefined ) {
-
-			property = struct.build( builder, 'var' );
-
-			const inputsSnippets = [];
-
-			for ( const inputName in structInputs ) {
-
-				const inputType = structInputs[ inputName ];
-				const input = inputs[ inputName ];
-
-				let inputSnippet = null;
-
-				if ( input !== undefined ) {
-
-					inputSnippet = input.build( builder, inputType );
-
-				} else {
-
-					inputSnippet = zeroValue.build( builder, inputType );
-
-				}
-
-				inputsSnippets.push( inputSnippet );
-
-			}
-
-			builder.addFlowCode( `${type} ${property} = ${type}( ${inputsSnippets.join( ', ' )} )` );
-
-			nodeData.property = property;
-
-		}
-
-		return property;
-
-	}
-
-}
-
-export default StructVarNode;

+ 11 - 14
examples/jsm/renderers/nodes/core/TempNode.js

@@ -10,31 +10,28 @@ class TempNode extends Node {
 
 	build( builder, output ) {
 
-		if ( builder.context.cache !== false ) {
+		const type = builder.getVectorType( this.getNodeType( builder ) );
 
-			const type = builder.getVectorType( this.getNodeType( builder ) );
+		if ( builder.context.temp !== false && type !== 'void ' && output !== 'void' ) {
 
-			if ( type !== 'void' ) {
+			const nodeData = builder.getDataFromNode( this );
 
-				const nodeData = builder.getDataFromNode( this );
+			if ( nodeData.snippet === undefined ) {
+
+				const snippet = super.build( builder, type );
 
 				const nodeVar = builder.getVarFromNode( this, type );
 				const propertyName = builder.getPropertyName( nodeVar );
 
-				if ( nodeData.snippet === undefined ) {
-
-					const snippet = super.build( builder, type );
-
-					builder.addFlowCode( `${propertyName} = ${snippet}` );
+				builder.addFlowCode( `${propertyName} = ${snippet}` );
 
-					nodeData.snippet = snippet;
-
-				}
-
-				return builder.format( propertyName, type, output );
+				nodeData.snippet = snippet;
+				nodeData.propertyName = propertyName;
 
 			}
 
+			return builder.format( nodeData.propertyName, type, output );
+
 		}
 
 		return super.build( builder, output );

+ 6 - 7
examples/jsm/renderers/nodes/core/VarNode.js

@@ -2,11 +2,11 @@ import Node from './Node.js';
 
 class VarNode extends Node {
 
-	constructor( value, name = '', nodeType = null ) {
+	constructor( node, name = null, nodeType = null ) {
 
 		super( nodeType );
 
-		this.value = value;
+		this.node = node;
 		this.name = name;
 
 	}
@@ -19,21 +19,20 @@ class VarNode extends Node {
 
 	getNodeType( builder ) {
 
-		return super.getNodeType( builder ) || this.value.getNodeType( builder );
+		return super.getNodeType( builder ) || this.node.getNodeType( builder );
 
 	}
 
 	generate( builder ) {
 
 		const type = builder.getVectorType( this.getNodeType( builder ) );
+		const node = this.node;
 		const name = this.name;
-		const value = this.value;
 
+		const snippet = node.build( builder, type );
 		const nodeVar = builder.getVarFromNode( this, type );
 
-		const snippet = value.build( builder, type );
-
-		if ( name !== '' ) {
+		if ( name !== null ) {
 
 			nodeVar.name = name;
 

+ 15 - 9
examples/jsm/renderers/nodes/core/VaryNode.js

@@ -3,43 +3,49 @@ import { NodeShaderStage } from './constants.js';
 
 class VaryNode extends Node {
 
-	constructor( value, name = '' ) {
+	constructor( node, name = null ) {
 
 		super();
 
-		this.value = value;
+		this.node = node;
 		this.name = name;
 
 	}
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	getNodeType( builder ) {
 
 		// VaryNode is auto type
 
-		return this.value.getNodeType( builder );
+		return this.node.getNodeType( builder );
 
 	}
 
 	generate( builder ) {
 
 		const type = this.getNodeType( builder );
-		const value = this.value;
+		const node = this.node;
 		const name = this.name;
 
 		const nodeVary = builder.getVaryFromNode( this, type );
 
-		if ( name !== '' ) {
+		if ( name !== null ) {
 
 			nodeVary.name = name;
 
 		}
 
-		const propertyName = builder.getPropertyName( nodeVary );
+		const propertyName = builder.getPropertyName( nodeVary, NodeShaderStage.Vertex );
 
-		// force nodeVary.snippet work in vertex stage
-		builder.flowNodeFromShaderStage( NodeShaderStage.Vertex, value, type, propertyName );
+		// force node run in vertex stage
+		builder.flowNodeFromShaderStage( NodeShaderStage.Vertex, node, type, propertyName );
 
-		return propertyName;
+		return builder.getPropertyName( nodeVary );
 
 	}
 

+ 5 - 5
examples/jsm/renderers/nodes/display/ColorSpaceNode.js

@@ -59,13 +59,13 @@ class ColorSpaceNode extends TempNode {
 	static LINEAR_TO_LOG_LUV = 'LinearToLogLuv';
 	static LOG_LUV_TO_LINEAR = 'LogLuvToLinear';
 */
-	constructor( method, value ) {
+	constructor( method, node ) {
 
 		super( 'vec4' );
 
 		this.method = method;
 
-		this.value = value;
+		this.node = node;
 		this.factor = null;
 
 	}
@@ -97,7 +97,7 @@ class ColorSpaceNode extends TempNode {
 		const type = this.getNodeType( builder );
 
 		const method = this.method;
-		const value = this.value;
+		const node = this.node;
 
 		if ( method !== ColorSpaceNode.LINEAR_TO_LINEAR ) {
 
@@ -106,13 +106,13 @@ class ColorSpaceNode extends TempNode {
 			const factor = this.factor;
 
 			return encodingFunctionNode( {
-				value,
+				value: node,
 				factor
 			} ).build( builder, type );
 
 		} else {
 
-			return value.build( builder, type );
+			return node.build( builder, type );
 
 		}
 

+ 6 - 8
examples/jsm/renderers/nodes/display/NormalMapNode.js

@@ -6,7 +6,7 @@ import OperatorNode from '../math/OperatorNode.js';
 import FloatNode from '../inputs/FloatNode.js';
 import TempNode from '../core/TempNode.js';
 import ModelNode from '../accessors/ModelNode.js';
-import { ShaderNode, cond, add, mul, join, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equal } from '../ShaderNode.js';
+import { ShaderNode, cond, add, mul, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equal } from '../ShaderNode.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
 
@@ -17,10 +17,8 @@ const perturbNormal2ArbNode = new ShaderNode( ( inputs ) => {
 
 	const { eye_pos, surf_norm, mapN, faceDirection, uv } = inputs;
 
-	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
-
-	const q0 = join( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
-	const q1 = join( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
+	const q0 = dFdx( eye_pos.xyz );
+	const q1 = dFdy( eye_pos.xyz );
 	const st0 = dFdx( uv.st );
 	const st1 = dFdy( uv.st );
 
@@ -41,11 +39,11 @@ const perturbNormal2ArbNode = new ShaderNode( ( inputs ) => {
 
 class NormalMapNode extends TempNode {
 
-	constructor( value ) {
+	constructor( node ) {
 
 		super( 'vec3' );
 
-		this.value = value;
+		this.node = node;
 
 		this.normalMapType = TangentSpaceNormalMap;
 
@@ -57,7 +55,7 @@ class NormalMapNode extends TempNode {
 
 		const normalMapType = this.normalMapType;
 
-		const normalOP = new OperatorNode( '*', this.value, new FloatNode( 2.0 ).setConst( true ) );
+		const normalOP = new OperatorNode( '*', this.node, new FloatNode( 2.0 ).setConst( true ) );
 		const normalMap = new OperatorNode( '-', normalOP, new FloatNode( 1.0 ).setConst( true ) );
 
 		if ( normalMapType === ObjectSpaceNormalMap ) {

+ 5 - 5
examples/jsm/renderers/nodes/functions/BSDFs.js

@@ -1,8 +1,8 @@
 import { ShaderNode,
-	add, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
+	add, addTo, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
 	cond, greaterThan, and,
 	transformedNormalView, positionViewDirection,
-	materialDiffuseColor, materialSpecularTint, materialRoughness,
+	diffuseColor, specularTint, roughness,
 	PI, RECIPROCAL_PI, EPSILON
 } from '../ShaderNode.js';
 
@@ -17,7 +17,7 @@ export const F_Schlick = new ShaderNode( ( inputs ) => {
 	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
 	const fresnel = exp2( mul( sub( mul( - 5.55473, dotVH ), 6.98316 ), dotVH ) );
 
-	return mul( sub( f90, f0 ), add( fresnel, f0 ) );
+	return add( mul( f0, sub( 1.0, fresnel ) ), mul( f90, fresnel ) );
 
 } ); // validated
 
@@ -107,9 +107,9 @@ export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
 
 	irradiance = mul( irradiance, PI ); // punctual light
 
-	directDiffuse.value = add( directDiffuse.value, mul( irradiance, BRDF_Lambert( { diffuseColor: materialDiffuseColor } ) ) );
+	addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor } ) ) );
 
-	directSpecular.value = add( directSpecular.value, mul( irradiance, BRDF_Specular_GGX( { lightDirection, f0: materialSpecularTint, f90: 1, roughness: materialRoughness } ) ) );
+	addTo( directSpecular, mul( irradiance, BRDF_Specular_GGX( { lightDirection, f0: specularTint, f90: 1, roughness } ) ) );
 
 } );
 

+ 10 - 2
examples/jsm/renderers/nodes/inputs/TextureNode.js

@@ -17,6 +17,14 @@ class TextureNode extends InputNode {
 
 	generate( builder, output ) {
 
+		const texture = this.value;
+
+		if ( ! texture || texture.isTexture !== true ) {
+
+			throw new Error( `TextureNode: Need a three.js texture.` );
+
+		}
+
 		const type = this.getNodeType( builder );
 
 		const textureProperty = super.generate( builder, type );
@@ -51,8 +59,8 @@ class TextureNode extends InputNode {
 				const textureCallSnippet = builder.getTexture( textureProperty, uvSnippet, biasSnippet );
 
 				colorSpace = new ColorSpaceNode();
-				colorSpace.value = new ExpressionNode( textureCallSnippet, 'vec4' );
-				colorSpace.fromDecoding( builder.getTextureEncodingFromMap( this.value ) );
+				colorSpace.node = new ExpressionNode( textureCallSnippet, 'vec4' );
+				colorSpace.fromDecoding( builder.getTextureEncodingFromMap( texture ) );
 
 				nodeData.colorSpace = colorSpace;
 

+ 5 - 5
examples/jsm/renderers/nodes/lights/LightContextNode.js

@@ -30,15 +30,15 @@ class LightContextNode extends ContextNode {
 
 		}
 
-		const directDiffuse = new VarNode( new Vector3Node() );
-		const directSpecular = new VarNode( new Vector3Node() );
+		const directDiffuse = new VarNode( new Vector3Node(), 'DirectDiffuse', 'vec3' );
+		const directSpecular = new VarNode( new Vector3Node(), 'DirectSpecular', 'vec3' );
 
-		this.setContextValue( 'directDiffuse', directDiffuse );
-		this.setContextValue( 'directSpecular', directSpecular );
+		this.context.directDiffuse = directDiffuse;
+		this.context.directSpecular = directSpecular;
 
 		if ( lightingModel !== null ) {
 
-			this.setContextValue( 'lightingModel', lightingModel );
+			this.context.lightingModel = lightingModel;
 
 		}
 

+ 12 - 16
examples/jsm/renderers/nodes/lights/LightNode.js

@@ -20,25 +20,23 @@ class LightNode extends Node {
 
 		this.light = light;
 
-		this.color = new ColorNode( new Color() );
+		this.colorNode = new ColorNode( new Color() );
 
-		this.lightCutoffDistance = new FloatNode( 0 );
-		this.lightDecayExponent = new FloatNode( 0 );
+		this.lightCutoffDistanceNode = new FloatNode( 0 );
+		this.lightDecayExponentNode = new FloatNode( 0 );
 
 	}
 
 	update( /* frame */ ) {
 
-		this.color.value.copy( this.light.color ).multiplyScalar( this.light.intensity );
-		this.lightCutoffDistance.value = this.light.distance;
-		this.lightDecayExponent.value = this.light.decay;
+		this.colorNode.value.copy( this.light.color ).multiplyScalar( this.light.intensity );
+		this.lightCutoffDistanceNode.value = this.light.distance;
+		this.lightDecayExponentNode.value = this.light.decay;
 
 	}
 
 	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-
 		const lightPositionView = new Object3DNode( Object3DNode.VIEW_POSITION );
 		const positionView = new PositionNode( PositionNode.VIEW );
 
@@ -50,20 +48,20 @@ class LightNode extends Node {
 
 		const lightAttenuation = getDistanceAttenuation( {
 			lightDistance,
-			cutoffDistance: this.lightCutoffDistance,
-			decayExponent: this.lightDecayExponent
+			cutoffDistance: this.lightCutoffDistanceNode,
+			decayExponent: this.lightDecayExponentNode
 		} );
 
-		const lightColor = new OperatorNode( '*', this.color, lightAttenuation );
+		const lightColor = new OperatorNode( '*', this.colorNode, lightAttenuation );
 
 		lightPositionView.object3d = this.light;
 
-		const lightingModelFunction = builder.getContextValue( 'lightingModel' );
+		const lightingModelFunction = builder.context.lightingModel;
 
 		if ( lightingModelFunction !== undefined ) {
 
-			const directDiffuse = builder.getContextValue( 'directDiffuse' );
-			const directSpecular = builder.getContextValue( 'directSpecular' );
+			const directDiffuse = builder.context.directDiffuse;
+			const directSpecular = builder.context.directSpecular;
 
 			lightingModelFunction( {
 				lightDirection,
@@ -74,8 +72,6 @@ class LightNode extends Node {
 
 		}
 
-		return this.color.build( builder, type );
-
 	}
 
 }

+ 17 - 5
examples/jsm/renderers/nodes/math/CondNode.js

@@ -1,7 +1,8 @@
-import TempNode from '../core/TempNode.js';
+import Node from '../core/Node.js';
+import PropertyNode from '../core/PropertyNode.js';
 import ContextNode from '../core/ContextNode.js';
 
-class CondNode extends TempNode {
+class CondNode extends Node {
 
 	constructor( node, ifNode, elseNode ) {
 
@@ -33,13 +34,24 @@ class CondNode extends TempNode {
 
 		const type = this.getNodeType( builder );
 
-		const context = { cache: false };
+		const context = { temp: false };
+		const nodeProperty = new PropertyNode( null, type ).build( builder );
 
-		const nodeSnippet = this.node.build( builder, 'bool' ),
+		const nodeSnippet = new ContextNode( this.node/*, context*/ ).build( builder, 'bool' ),
 			ifSnippet = new ContextNode( this.ifNode, context ).build( builder, type ),
 			elseSnippet = new ContextNode( this.elseNode, context ).build( builder, type );
 
-		return `( ${ nodeSnippet } ? ${ ifSnippet } : ${ elseSnippet } )`;
+		builder.addFlowCode( `if ( ${nodeSnippet} ) {
+
+\t\t${nodeProperty} = ${ifSnippet};
+
+\t} else {
+
+\t\t${nodeProperty} = ${elseSnippet};
+
+\t}` );
+
+		return nodeProperty;
 
 	}
 

+ 37 - 22
examples/jsm/renderers/nodes/math/MathNode.js

@@ -1,5 +1,6 @@
 import TempNode from '../core/TempNode.js';
 import ExpressionNode from '../core/ExpressionNode.js';
+import JoinNode from '../utils/JoinNode.js';
 import SplitNode from '../utils/SplitNode.js';
 import OperatorNode from './OperatorNode.js';
 
@@ -56,39 +57,43 @@ class MathNode extends TempNode {
 	static SMOOTHSTEP = 'smoothstep';
 	static FACEFORWARD = 'faceforward';
 
-	constructor( method, a, b = null, c = null ) {
+	constructor( method, aNode, bNode = null, cNode = null ) {
 
 		super();
 
 		this.method = method;
 
-		this.a = a;
-		this.b = b;
-		this.c = c;
+		this.aNode = aNode;
+		this.bNode = bNode;
+		this.cNode = cNode;
 
 	}
 
 	getInputType( builder ) {
 
-		const aLen = this.a.getTypeLength( builder );
-		const bLen = this.b ? this.b.getTypeLength( builder ) : 0;
-		const cLen = this.c ? this.c.getTypeLength( builder ) : 0;
+		const aType = this.aNode.getNodeType( builder );
+		const bType = this.bNode ? this.bNode.getNodeType( builder ) : null;
+		const cType = this.cNode ? this.cNode.getNodeType( builder ) : null;
+
+		const aLen = builder.getTypeLength( aType );
+		const bLen = builder.getTypeLength( bType );
+		const cLen = builder.getTypeLength( cType );
 
 		if ( aLen > bLen && aLen > cLen ) {
 
-			return this.a.getNodeType( builder );
+			return aType;
 
 		} else if ( bLen > cLen ) {
 
-			return this.b.getNodeType( builder );
+			return bType;
 
 		} else if ( cLen > aLen ) {
 
-			this.c.getNodeType( builder )
+			return cType;
 
 		}
 
-		return this.a.getNodeType( builder );
+		return aType;
 
 	}
 
@@ -100,7 +105,7 @@ class MathNode extends TempNode {
 
 			return 'float';
 
-		} else if (method === MathNode.CROSS) {
+		} else if ( method === MathNode.CROSS ) {
 
 			return 'vec3';
 
@@ -112,18 +117,28 @@ class MathNode extends TempNode {
 
 	}
 
-	generate( builder ) {
+	generate( builder, output ) {
 
 		const method = this.method;
 
 		const type = this.getNodeType( builder );
 		const inputType = this.getInputType( builder );
 
-		const a = this.a;
-		const b = this.b;
-		const c = this.c;
+		const a = this.aNode;
+		const b = this.bNode;
+		const c = this.cNode;
+
+		if ( builder.renderer.isWebGLRenderer === true && ( method === MathNode.DFDX || method === MathNode.DFDY ) && output === 'vec3' ) {
+
+			// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+
+			return new JoinNode( [
+				new MathNode( method, new SplitNode( a, 'x' ) ),
+				new MathNode( method, new SplitNode( a, 'y' ) ),
+				new MathNode( method, new SplitNode( a, 'z' ) )
+			] ).build( builder );
 
-		if ( method === MathNode.TRANSFORM_DIRECTION ) {
+		} else if ( method === MathNode.TRANSFORM_DIRECTION ) {
 
 			// dir can be either a direction vector or a normal vector
 			// upper-left 3x3 of matrix is assumed to be orthogonal
@@ -171,7 +186,7 @@ class MathNode extends TempNode {
 			} else if ( method === MathNode.STEP ) {
 
 				params.push(
-					b.build( builder, a.getTypeLength( builder ) === 1 ? 'float' : inputType ),
+					b.build( builder, builder.getTypeLength( a.getNodeType( builder ) ) === 1 ? 'float' : inputType ),
 					b.build( builder, inputType )
 				);
 
@@ -179,7 +194,7 @@ class MathNode extends TempNode {
 
 				params.push(
 					a.build( builder, inputType ),
-					b.build( builder, b.getTypeLength( builder ) === 1 ? 'float' : inputType )
+					b.build( builder, builder.getTypeLength( b.getNodeType( builder ) ) === 1 ? 'float' : inputType )
 				);
 
 			} else if ( method === MathNode.REFRACT ) {
@@ -195,7 +210,7 @@ class MathNode extends TempNode {
 				params.push(
 					a.build( builder, inputType ),
 					b.build( builder, inputType ),
-					c.build( builder, c.getTypeLength( builder ) === 1 ? 'float' : inputType )
+					c.build( builder, builder.getTypeLength( c.getNodeType( builder ) ) === 1 ? 'float' : inputType )
 				);
 
 			} else {
@@ -206,7 +221,7 @@ class MathNode extends TempNode {
 
 					params.push( b.build( builder, inputType ), c.build( builder, inputType ) );
 
-				} else if ( this.b !== null ) {
+				} else if ( b !== null ) {
 
 					params.push( b.build( builder, inputType ) );
 
@@ -214,7 +229,7 @@ class MathNode extends TempNode {
 
 			}
 
-			return `${method}( ${params.join(', ')} )`;
+			return `${ builder.getMethod( method ) }( ${params.join( ', ' )} )`;
 
 		}
 

+ 38 - 14
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -2,7 +2,7 @@ import TempNode from '../core/TempNode.js';
 
 class OperatorNode extends TempNode {
 
-	constructor( op, a, b, ...params ) {
+	constructor( op, aNode, bNode, ...params ) {
 
 		super();
 
@@ -10,20 +10,20 @@ class OperatorNode extends TempNode {
 
 		if ( params.length > 0 ) {
 
-			let finalB = b;
+			let finalBNode = bNode;
 
 			for ( let i = 0; i < params.length; i ++ ) {
 
-				finalB = new OperatorNode( op, finalB, params[ i ] );
+				finalBNode = new OperatorNode( op, finalBNode, params[ i ] );
 
 			}
 
-			b = finalB;
+			bNode = finalBNode;
 
 		}
 
-		this.a = a;
-		this.b = b;
+		this.aNode = aNode;
+		this.bNode = bNode;
 
 	}
 
@@ -31,9 +31,12 @@ class OperatorNode extends TempNode {
 
 		const op = this.op;
 
+		const aNode = this.aNode;
+		const bNode = this.bNode;
+
 		if ( op === '=' ) {
 
-			return this.a.getNodeType( builder );
+			return aNode.getNodeType( builder );
 
 		} else if ( op === '==' || op === '>' || op === '&&' ) {
 
@@ -41,8 +44,8 @@ class OperatorNode extends TempNode {
 
 		} else {
 
-			const typeA = this.a.getNodeType( builder );
-			const typeB = this.b.getNodeType( builder );
+			const typeA = aNode.getNodeType( builder );
+			const typeB = bNode.getNodeType( builder );
 
 			if ( typeA === 'float' && builder.isMatrix( typeB ) ) {
 
@@ -78,8 +81,11 @@ class OperatorNode extends TempNode {
 
 		const op = this.op;
 
-		let typeA = this.a.getNodeType( builder );
-		let typeB = this.b.getNodeType( builder );
+		const aNode = this.aNode;
+		const bNode = this.bNode;
+
+		let typeA = aNode.getNodeType( builder );
+		let typeB = bNode.getNodeType( builder );
 
 		if ( op === '=' ) {
 
@@ -105,10 +111,28 @@ class OperatorNode extends TempNode {
 
 		}
 
-		const a = this.a.build( builder, typeA );
-		const b = this.b.build( builder, typeB );
+		const a = aNode.build( builder, typeA );
+		const b = bNode.build( builder, typeB );
+
+		if ( output !== 'void' ) {
+
+			if ( op === '=' ) {
+
+				builder.addFlowCode( `${a} ${this.op} ${b}` );
+
+				return a;
+
+			} else {
 
-		return `( ${a} ${this.op} ${b} )`;
+				return `( ${a} ${this.op} ${b} )`;
+
+			}
+
+		} else {
+
+			return `${a} ${this.op} ${b}`;
+
+		}
 
 	}
 

+ 3 - 3
examples/jsm/renderers/nodes/procedural/CheckerNode.js

@@ -17,17 +17,17 @@ const checkerShaderNode = new ShaderNode( ( inputs ) => {
 
 class CheckerNode extends Node {
 
-	constructor( uv = new UVNode() ) {
+	constructor( uvNode = new UVNode() ) {
 
 		super( 'float' );
 
-		this.uv = uv;
+		this.uvNode = uvNode;
 
 	}
 
 	generate( builder ) {
 
-		return checkerShaderNode( { uv: this.uv } ).build( builder );
+		return checkerShaderNode( { uv: this.uvNode } ).build( builder );
 
 	}
 

+ 7 - 7
examples/jsm/renderers/nodes/utils/JoinNode.js

@@ -2,30 +2,30 @@ import Node from '../core/Node.js';
 
 class JoinNode extends Node {
 
-	constructor( values = [] ) {
+	constructor( nodes = [] ) {
 
 		super();
 
-		this.values = values;
+		this.nodes = nodes;
 
 	}
 
 	getNodeType( builder ) {
 
-		return builder.getTypeFromLength( this.values.length );
+		return builder.getTypeFromLength( this.nodes.length );
 
 	}
 
 	generate( builder ) {
 
 		const type = this.getNodeType( builder );
-		const values = this.values;
+		const nodes = this.nodes;
 
 		const snippetValues = [];
 
-		for ( let i = 0; i < values.length; i ++ ) {
+		for ( let i = 0; i < nodes.length; i ++ ) {
 
-			const input = values[ i ];
+			const input = nodes[ i ];
 
 			const inputSnippet = input.build( builder, 'float' );
 
@@ -33,7 +33,7 @@ class JoinNode extends Node {
 
 		}
 
-		return `${type}( ${ snippetValues.join( ', ' ) } )`;
+		return `${ builder.getType( type ) }( ${ snippetValues.join( ', ' ) } )`;
 
 	}
 

+ 7 - 7
examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js

@@ -8,21 +8,21 @@ import JoinNode from '../utils/JoinNode.js';
 
 class SpriteSheetUVNode extends Node {
 
-	constructor( count, uv = new UVNode() ) {
+	constructor( countNode, uvNode = new UVNode(), frameNode = new FloatNode( 0 ).setConst( true ) ) {
 
 		super( 'vec2' );
 
-		this.count = count;
-		this.uv = uv;
-		this.frame = new FloatNode( 0 ).setConst( true );
+		this.countNode = countNode;
+		this.uvNode = uvNode;
+		this.frameNode = frameNode;
 
 	}
 
 	generate( builder ) {
 
-		const uv = this.uv;
-		const count = this.count;
-		const frame = this.frame;
+		const count = this.countNode;
+		const uv = this.uvNode;
+		const frame = this.frameNode;
 
 		const one = new FloatNode( 1 ).setConst( true );
 

+ 22 - 0
examples/jsm/renderers/webgl/nodes/SlotNode.js

@@ -0,0 +1,22 @@
+import Node from '../../nodes/core/Node.js';
+
+class SlotNode extends Node {
+
+	constructor( node, name, nodeType ) {
+
+		super( nodeType );
+
+		this.node = node;
+		this.name = name;
+
+	}
+
+	generate( builder ) {
+
+		return this.node.build( builder, this.getNodeType( builder ) );
+
+	}
+
+}
+
+export default SlotNode;

+ 184 - 88
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -1,5 +1,5 @@
 import NodeBuilder from '../../nodes/core/NodeBuilder.js';
-import NodeSlot from '../../nodes/core/NodeSlot.js';
+import SlotNode from './SlotNode.js';
 import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
@@ -26,11 +26,32 @@ class WebGLNodeBuilder extends NodeBuilder {
 		super( object, renderer, new GLSLNodeParser() );
 
 		this.shader = shader;
+		this.slots = { vertex: [], fragment: [] };
 
 		this._parseObject();
 
 	}
 
+	addSlot( shaderStage, slotNode ) {
+
+		this.slots[ shaderStage ].push( slotNode );
+
+		return this.addFlow( shaderStage, slotNode );
+
+	}
+
+	addFlowCode( code ) {
+
+		if ( ! /;\s*$/.test( code ) ) {
+
+			code += ';';
+
+		}
+
+		super.addFlowCode( code + '\n\t' );
+
+	}
+
 	_parseObject() {
 
 		const material = this.material;
@@ -39,49 +60,49 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		if ( material.colorNode && material.colorNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );
+			this.addSlot( 'fragment', new SlotNode( material.colorNode, 'COLOR', 'vec4' ) );
 
 		}
 
 		if ( material.opacityNode && material.opacityNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.opacityNode, 'OPACITY', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( material.opacityNode, 'OPACITY', 'float' ) );
 
 		}
 
 		if ( material.normalNode && material.normalNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.normalNode, 'NORMAL', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( material.normalNode, 'NORMAL', 'vec3' ) );
 
 		}
 
 		if ( material.emissiveNode && material.emissiveNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.emissiveNode, 'EMISSIVE', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( material.emissiveNode, 'EMISSIVE', 'vec3' ) );
 
 		}
 
 		if ( material.metalnessNode && material.metalnessNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.metalnessNode, 'METALNESS', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( material.metalnessNode, 'METALNESS', 'float' ) );
 
 		}
 
 		if ( material.roughnessNode && material.roughnessNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.roughnessNode, 'ROUGHNESS', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( material.roughnessNode, 'ROUGHNESS', 'float' ) );
 
 		}
 
 		if ( material.clearcoatNode && material.clearcoatNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.clearcoatNode, 'CLEARCOAT', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( material.clearcoatNode, 'CLEARCOAT', 'float' ) );
 
 		}
 
 		if ( material.clearcoatRoughnessNode && material.clearcoatRoughnessNode.isNode ) {
 
-			this.addSlot( 'fragment', new NodeSlot( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) );
+			this.addSlot( 'fragment', new SlotNode( material.clearcoatRoughnessNode, 'CLEARCOAT_ROUGHNESS', 'float' ) );
 
 		}
 
@@ -90,20 +111,20 @@ class WebGLNodeBuilder extends NodeBuilder {
 			const envRadianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.RADIANCE, material.envNode );
 			const envIrradianceNode = new WebGLPhysicalContextNode( WebGLPhysicalContextNode.IRRADIANCE, material.envNode );
 
-			this.addSlot( 'fragment', new NodeSlot( envRadianceNode, 'RADIANCE', 'vec3' ) );
-			this.addSlot( 'fragment', new NodeSlot( envIrradianceNode, 'IRRADIANCE', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( envRadianceNode, 'RADIANCE', 'vec3' ) );
+			this.addSlot( 'fragment', new SlotNode( envIrradianceNode, 'IRRADIANCE', 'vec3' ) );
 
 		}
 
 		if ( material.sizeNode && material.sizeNode.isNode ) {
 
-			this.addSlot( 'vertex', new NodeSlot( material.sizeNode, 'SIZE', 'float' ) );
+			this.addSlot( 'vertex', new SlotNode( material.sizeNode, 'SIZE', 'float' ) );
 
 		}
 
 		if ( material.positionNode && material.positionNode.isNode ) {
 
-			this.addSlot( 'vertex', new NodeSlot( material.positionNode, 'POSITION', 'vec3' ) );
+			this.addSlot( 'vertex', new SlotNode( material.positionNode, 'POSITION', 'vec3' ) );
 
 		}
 
@@ -217,7 +238,7 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		const shaderProperty = getShaderStageProperty( shaderStage );
 
-		let source = this.shader[ shaderProperty ];
+		let source = this[ shaderProperty ];
 
 		const index = source.indexOf( snippet );
 
@@ -230,7 +251,7 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 		}
 
-		this.shader[ shaderProperty ] = source;
+		this[ shaderProperty ] = source;
 
 	}
 
@@ -277,125 +298,208 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
+	buildCode() {
+
+		const shaderData = {};
+
+		for ( const shaderStage of shaderStages ) {
+
+			const uniforms = this.getUniforms( shaderStage );
+			const attributes = this.getAttributes( shaderStage );
+			const varys = this.getVarys( shaderStage );
+			const vars = this.getVars( shaderStage );
+			const codes = this.getCodes( shaderStage );
+
+			shaderData[ shaderStage ] = `${this.getSignature()}
+// <node_builder>
+
+// uniforms
+${uniforms}
+
+// attributes
+${attributes}
+
+// varys
+${varys}
+
+// vars
+${vars}
+
+// codes
+${codes}
+
+// </node_builder>
+
+${this.shader[ getShaderStageProperty( shaderStage ) ]}
+`;
+
+		}
+
+		this.vertexShader = shaderData.vertex;
+		this.fragmentShader = shaderData.fragment;
+
+
+	}
+
 	build() {
 
 		super.build();
 
 		this._addSnippets();
-		this._buildShader();
+		this._addUniforms();
+
+		this.shader.vertexShader = this.vertexShader;
+		this.shader.fragmentShader = this.fragmentShader;
 
 		return this;
 
 	}
 
-	_addSnippets() {
+	getSlot( shaderStage, name ) {
 
-		this.parseInclude( 'fragment', 'lights_physical_fragment' );
+		const slots = this.slots[ shaderStage ];
 
-		this.addCodeAfterInclude( 'fragment', 'normal_fragment_begin',
-			`#ifdef NODE_NORMAL
+		for ( const node of slots ) {
 
-				NODE_CODE_NORMAL
-				normal = NODE_NORMAL;
+			if ( node.name === name ) {
 
-			#endif` );
+				return this.getFlowData( shaderStage, node );
 
-		this.addCodeAfterInclude( 'fragment', 'color_fragment',
-			`#ifdef NODE_COLOR
+			}
 
-				NODE_CODE_COLOR
-				diffuseColor = NODE_COLOR;
+		}
 
-			#endif` );
+	}
 
-		this.addCodeAfterInclude( 'fragment', 'alphamap_fragment',
-			`#ifdef NODE_OPACITY
+	_addSnippets() {
+
+		this.parseInclude( 'fragment', 'lights_physical_fragment' );
+
+		const colorSlot = this.getSlot( 'fragment', 'COLOR' );
+		const normalSlot = this.getSlot( 'fragment', 'NORMAL' );
+		const opacityNode = this.getSlot( 'fragment', 'OPACITY' );
+		const emissiveNode = this.getSlot( 'fragment', 'EMISSIVE' );
+		const roughnessNode = this.getSlot( 'fragment', 'ROUGHNESS' );
+		const metalnessNode = this.getSlot( 'fragment', 'METALNESS' );
+		const clearcoatNode = this.getSlot( 'fragment', 'CLEARCOAT' );
+		const clearcoatRoughnessNode = this.getSlot( 'fragment', 'CLEARCOAT_ROUGHNESS' );
 
-				NODE_CODE_OPACITY
-				diffuseColor.a *= NODE_OPACITY;
+		const positionNode = this.getSlot( 'vertex', 'POSITION' );
+		const sizeNode = this.getSlot( 'vertex', 'SIZE' );
 
-			#endif` );
+		if ( colorSlot !== undefined ) {
+
+			this.addCodeAfterInclude(
+				'fragment',
+				'color_fragment',
+				`${colorSlot.code}\n\tdiffuseColor = ${colorSlot.result};`
+			);
+
+		}
 
-		this.addCodeAfterInclude( 'fragment', 'emissivemap_fragment',
-			`#ifdef NODE_EMISSIVE
+		if ( normalSlot !== undefined ) {
 
-				NODE_CODE_EMISSIVE
-				totalEmissiveRadiance = NODE_EMISSIVE;
+			this.addCodeAfterInclude(
+				'fragment',
+				'normal_fragment_begin',
+				`${normalSlot.code}\n\tnormal = ${normalSlot.result};`
+			);
 
-			#endif` );
+		}
 
-		this.addCodeAfterInclude( 'fragment', 'roughnessmap_fragment',
-			`#ifdef NODE_ROUGHNESS
+		if ( opacityNode !== undefined ) {
 
-				NODE_CODE_ROUGHNESS
-				roughnessFactor = NODE_ROUGHNESS;
+			this.addCodeAfterInclude(
+				'fragment',
+				'alphamap_fragment',
+				`${opacityNode.code}\n\tdiffuseColor.a = ${opacityNode.result};`
+			);
 
-			#endif` );
+		}
 
-		this.addCodeAfterInclude( 'fragment', 'metalnessmap_fragment',
-			`#ifdef NODE_METALNESS
+		if ( emissiveNode !== undefined ) {
 
-				NODE_CODE_METALNESS
-				metalnessFactor = NODE_METALNESS;
+			this.addCodeAfterInclude(
+				'fragment',
+				'emissivemap_fragment',
+				`${emissiveNode.code}\n\ttotalEmissiveRadiance = ${emissiveNode.result};`
+			);
 
-			#endif` );
+		}
 
-		this.addCodeAfterSnippet( 'fragment', 'material.clearcoatRoughness = clearcoatRoughness;',
-			`#ifdef NODE_CLEARCOAT
+		if ( roughnessNode !== undefined ) {
 
-				NODE_CODE_CLEARCOAT
-				material.clearcoat = NODE_CLEARCOAT;
+			this.addCodeAfterInclude(
+				'fragment',
+				'roughnessmap_fragment',
+				`${roughnessNode.code}\n\troughnessFactor = ${roughnessNode.result};`
+			);
 
-			#endif
+		}
 
-			#ifdef NODE_CLEARCOAT_ROUGHNESS
+		if ( metalnessNode !== undefined ) {
 
-				NODE_CODE_CLEARCOAT_ROUGHNESS
-				material.clearcoatRoughness = NODE_CLEARCOAT_ROUGHNESS;
+			this.addCodeAfterInclude(
+				'fragment',
+				'metalnessmap_fragment',
+				`${metalnessNode.code}\n\tmetalnessFactor = ${metalnessNode.result};`
+			);
 
-			#endif` );
+		}
 
-		this.addCodeAfterInclude( 'fragment', 'lights_fragment_begin',
-			`#ifdef NODE_RADIANCE
+		if ( clearcoatNode !== undefined ) {
 
-				NODE_CODE_RADIANCE
-				radiance += NODE_RADIANCE;
+			this.addCodeAfterSnippet(
+				'fragment',
+				'material.clearcoatRoughness = clearcoatRoughness;',
+				`${clearcoatNode.code}\n\tmaterial.clearcoat = ${clearcoatNode.result};`
+			);
 
-				NODE_CODE_IRRADIANCE
-				iblIrradiance += PI * NODE_IRRADIANCE;
+		}
 
-			#endif` );
+		if ( clearcoatRoughnessNode !== undefined ) {
 
-		this.addCodeAfterInclude( 'vertex', 'begin_vertex',
-			`#ifdef NODE_POSITION
+			this.addCodeAfterSnippet(
+				'fragment',
+				'material.clearcoatRoughness = clearcoatRoughness;',
+				`${clearcoatRoughnessNode.code}\n\tmaterial.clearcoatRoughness = ${clearcoatRoughnessNode.result};`
+			);
 
-				NODE_CODE_POSITION
-				transformed = NODE_POSITION;
+		}
 
-			#endif` );
+		if ( positionNode !== undefined ) {
 
-		this.addCodeAfterSnippet( 'vertex', 'gl_PointSize = size;',
-			`#ifdef NODE_SIZE
+			this.addCodeAfterInclude(
+				'vertex',
+				'begin_vertex',
+				`${positionNode.code}\n\ttransformed = ${positionNode.result};`
+			);
 
-				NODE_CODE_SIZE
-				gl_PointSize = NODE_SIZE;
+		}
 
-			#endif` );
+		if ( sizeNode !== undefined ) {
 
-		for ( const shaderStage of shaderStages ) {
+			this.addCodeAfterSnippet(
+				'vertex',
+				'gl_PointSize = size;',
+				`${sizeNode.code}\n\tgl_PointSize = ${sizeNode.result};`
+			);
 
-			this.addCodeAfterSnippet( shaderStage, 'main() {',
-				`#ifdef NODE_CODE
+		}
 
-					NODE_CODE
+		for ( const shaderStage of shaderStages ) {
 
-				#endif` );
+			this.addCodeAfterSnippet(
+				shaderStage,
+				'main() {',
+				this.flowCode[ shaderStage ]
+			);
 
 		}
 
 	}
 
-	_buildShader() {
+	_addUniforms() {
 
 		for ( const shaderStage of shaderStages ) {
 
@@ -407,14 +511,6 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 			}
 
-			// code
-
-			const shaderProperty = getShaderStageProperty( shaderStage );
-
-			const nodeCode = this[ shaderProperty ];
-
-			this.shader[ shaderProperty ] = nodeCode + this.shader[ shaderProperty ];
-
 		}
 
 	}

+ 2 - 2
examples/jsm/renderers/webgl/nodes/WebGLPhysicalContextNode.js

@@ -30,11 +30,11 @@ class WebGLPhysicalContextNode extends ContextNode {
 
 			roughness = new FloatNode( 1.0 ).setConst( true );
 
-			this.setContextValue( 'uv', new NormalNode( NormalNode.WORLD ) );
+			this.context.uv = new NormalNode( NormalNode.WORLD );
 
 		}
 
-		this.setContextValue( 'roughness', roughness );
+		this.context.roughness = roughness;
 
 		return super.generate( builder, output );
 

+ 1 - 1
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -144,7 +144,7 @@ class WebGPUBindings {
 				const forceUpdate = textures.updateTexture( texture );
 				const textureGPU = textures.getTextureGPU( texture );
 
-				if ( binding.textureGPU !== textureGPU || forceUpdate === true ) {
+				if ( textureGPU !== undefined && binding.textureGPU !== textureGPU || forceUpdate === true ) {
 
 					binding.textureGPU = textureGPU;
 					needsBindGroupRefresh = true;

+ 2 - 4
examples/jsm/renderers/webgpu/WebGPUComputePipelines.js

@@ -2,10 +2,9 @@ import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
 
 class WebGPUComputePipelines {
 
-	constructor( device, glslang ) {
+	constructor( device ) {
 
 		this.device = device;
-		this.glslang = glslang;
 
 		this.pipelines = new WeakMap();
 		this.stages = {
@@ -23,7 +22,6 @@ class WebGPUComputePipelines {
 		if ( pipeline === undefined ) {
 
 			const device = this.device;
-			const glslang = this.glslang;
 
 			const shader = {
 				computeShader: param.shader
@@ -35,7 +33,7 @@ class WebGPUComputePipelines {
 
 			if ( stageCompute === undefined ) {
 
- 				stageCompute = new WebGPUProgrammableStage( device, glslang, shader.computeShader, 'compute' );
+ 				stageCompute = new WebGPUProgrammableStage( device, shader.computeShader, 'compute' );
 
 				this.stages.compute.set( shader, stageCompute );
 

+ 2 - 18
examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js

@@ -2,7 +2,7 @@ let _id = 0;
 
 class WebGPUProgrammableStage {
 
-	constructor( device, glslang, code, type ) {
+	constructor( device, code, type ) {
 
 		this.id = _id ++;
 
@@ -10,24 +10,8 @@ class WebGPUProgrammableStage {
 		this.type = type;
 		this.usedTimes = 0;
 
-		let data = null;
-
-		if ( /^#version 450/.test( code ) === true ) {
-
-			// GLSL
-
-			data = glslang.compileGLSL( code, type );
-
-		} else {
-
-			// WGSL
-
-			data = code;
-
-		}
-
 		this.stage = {
-			module: device.createShaderModule( { code: data } ),
+			module: device.createShaderModule( { code } ),
 			entryPoint: 'main'
 		};
 

+ 3 - 5
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -3,12 +3,11 @@ import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
 
 class WebGPURenderPipelines {
 
-	constructor( renderer, properties, device, glslang, sampleCount, nodes ) {
+	constructor( renderer, properties, device, sampleCount, nodes ) {
 
 		this.renderer = renderer;
 		this.properties = properties;
 		this.device = device;
-		this.glslang = glslang;
 		this.sampleCount = sampleCount;
 		this.nodes = nodes;
 
@@ -25,7 +24,6 @@ class WebGPURenderPipelines {
 	get( object ) {
 
 		const device = this.device;
-		const glslang = this.glslang;
 		const properties = this.properties;
 
 		const material = object.material;
@@ -47,7 +45,7 @@ class WebGPURenderPipelines {
 
 			if ( stageVertex === undefined ) {
 
-				stageVertex = new WebGPUProgrammableStage( device, glslang, nodeBuilder.vertexShader, 'vertex' );
+				stageVertex = new WebGPUProgrammableStage( device, nodeBuilder.vertexShader, 'vertex' );
 				this.stages.vertex.set( nodeBuilder.vertexShader, stageVertex );
 
 			}
@@ -56,7 +54,7 @@ class WebGPURenderPipelines {
 
 			if ( stageFragment === undefined ) {
 
-				stageFragment = new WebGPUProgrammableStage( device, glslang, nodeBuilder.fragmentShader, 'fragment' );
+				stageFragment = new WebGPUProgrammableStage( device, nodeBuilder.fragmentShader, 'fragment' );
 				this.stages.fragment.set( nodeBuilder.fragmentShader, stageFragment );
 
 			}

+ 5 - 7
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -12,8 +12,6 @@ import WebGPUTextures from './WebGPUTextures.js';
 import WebGPUBackground from './WebGPUBackground.js';
 import WebGPUNodes from './nodes/WebGPUNodes.js';
 
-import glslang from '../../libs/glslang.js';
-
 import { Frustum, Matrix4, Vector3, Color, LinearEncoding } from 'three';
 
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
@@ -166,8 +164,6 @@ class WebGPURenderer {
 
 		const device = await adapter.requestDevice( deviceDescriptor );
 
-		const compiler = await glslang();
-
 		const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' );
 
 		const swapChain = context.configure( {
@@ -184,11 +180,11 @@ class WebGPURenderer {
 		this._properties = new WebGPUProperties();
 		this._attributes = new WebGPUAttributes( device );
 		this._geometries = new WebGPUGeometries( this._attributes, this._info );
-		this._textures = new WebGPUTextures( device, this._properties, this._info, compiler );
+		this._textures = new WebGPUTextures( device, this._properties, this._info );
 		this._objects = new WebGPUObjects( this._geometries, this._info );
 		this._nodes = new WebGPUNodes( this );
-		this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, compiler, parameters.sampleCount, this._nodes );
-		this._computePipelines = new WebGPUComputePipelines( device, compiler );
+		this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, parameters.sampleCount, this._nodes );
+		this._computePipelines = new WebGPUComputePipelines( device );
 		this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
 		this._renderLists = new WebGPURenderLists();
 		this._background = new WebGPUBackground( this );
@@ -963,4 +959,6 @@ class WebGPURenderer {
 
 }
 
+WebGPURenderer.prototype.isWebGPURenderer = true;
+
 export default WebGPURenderer;

+ 62 - 28
examples/jsm/renderers/webgpu/WebGPUTextureUtils.js

@@ -24,28 +24,60 @@ import { GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology } from './constants
 
 class WebGPUTextureUtils {
 
-	constructor( device, glslang ) {
+	constructor( device ) {
 
 		this.device = device;
 
-		const mipmapVertexSource = `#version 450
-			const vec2 pos[4] = vec2[4](vec2(-1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(-1.0f, -1.0f), vec2(1.0f, -1.0f));
-			const vec2 tex[4] = vec2[4](vec2(0.0f, 0.0f), vec2(1.0f, 0.0f), vec2(0.0f, 1.0f), vec2(1.0f, 1.0f));
-			layout(location = 0) out vec2 vTex;
-			void main() {
-				vTex = tex[gl_VertexIndex];
-				gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
-			}
-		`;
-
-		const mipmapFragmentSource = `#version 450
-			layout(set = 0, binding = 0) uniform sampler imgSampler;
-			layout(set = 0, binding = 1) uniform texture2D img;
-			layout(location = 0) in vec2 vTex;
-			layout(location = 0) out vec4 outColor;
-			void main() {
-				outColor = texture(sampler2D(img, imgSampler), vTex);
-			}`;
+		const mipmapVertexSource = `
+[[ block ]]
+struct VarysStruct {
+
+	[[ builtin( position ) ]] Position: vec4<f32>;
+	[[ location( 0 ) ]] vTex : vec2<f32>;
+
+};
+
+[[ stage( vertex ) ]]
+fn main( [[ builtin( vertex_index ) ]] vertexIndex : u32 ) -> VarysStruct {
+
+	var Varys: VarysStruct;
+
+	var pos = array< vec2<f32>, 4 >(
+		vec2<f32>( -1.0,  1.0 ),
+		vec2<f32>(  1.0,  1.0 ),
+		vec2<f32>( -1.0, -1.0 ),
+		vec2<f32>(  1.0, -1.0 )
+	);
+
+	var tex = array< vec2<f32>, 4 >(
+		vec2<f32>( 0.0, 0.0 ),
+		vec2<f32>( 1.0, 0.0 ),
+		vec2<f32>( 0.0, 1.0 ),
+		vec2<f32>( 1.0, 1.0 )
+	);
+
+	Varys.vTex = tex[ vertexIndex ];
+	Varys.Position = vec4<f32>( pos[ vertexIndex ], 0.0, 1.0 );
+
+	return Varys;
+
+}
+`;
+
+		const mipmapFragmentSource = `
+[[ group( 0 ), binding( 0 ) ]] 
+var imgSampler : sampler;
+
+[[ group( 0 ), binding( 1 ) ]] 
+var img : texture_2d<f32>;
+
+[[ stage( fragment ) ]]
+fn main( [[ location( 0 ) ]] vTex : vec2<f32> ) -> [[ location( 0 ) ]] vec4<f32> {
+
+	return textureSample( img, imgSampler, vTex );
+
+}
+`;
 
 		this.sampler = device.createSampler( { minFilter: GPUFilterMode.Linear } );
 
@@ -53,10 +85,11 @@ class WebGPUTextureUtils {
 		this.pipelines = {};
 
 		this.mipmapVertexShaderModule = device.createShaderModule( {
-			code: glslang.compileGLSL( mipmapVertexSource, 'vertex' ),
+			code: mipmapVertexSource
 		} );
+
 		this.mipmapFragmentShaderModule = device.createShaderModule( {
-			code: glslang.compileGLSL( mipmapFragmentSource, 'fragment' ),
+			code: mipmapFragmentSource
 		} );
 
 	}
@@ -82,6 +115,7 @@ class WebGPUTextureUtils {
 					stripIndexFormat: GPUIndexFormat.Uint32
 				}
 			} );
+
 			this.pipelines[ format ] = pipeline;
 
 		}
@@ -99,32 +133,32 @@ class WebGPUTextureUtils {
 
 		let srcView = textureGPU.createView( {
 			baseMipLevel: 0,
-			mipLevelCount: 1,
+			mipLevelCount: 1
 		} );
 
 		for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) {
 
 			const dstView = textureGPU.createView( {
 				baseMipLevel: i,
-				mipLevelCount: 1,
+				mipLevelCount: 1
 			} );
 
 			const passEncoder = commandEncoder.beginRenderPass( {
 				colorAttachments: [ {
 					view: dstView,
-					loadValue: [ 0, 0, 0, 0 ],
-				} ],
+					loadValue: [ 0, 0, 0, 0 ]
+				} ]
 			} );
 
 			const bindGroup = this.device.createBindGroup( {
 				layout: bindGroupLayout,
 				entries: [ {
 					binding: 0,
-					resource: this.sampler,
+					resource: this.sampler
 				}, {
 					binding: 1,
-					resource: srcView,
-				} ],
+					resource: srcView
+				} ]
 			} );
 
 			passEncoder.setPipeline( pipeline );

+ 2 - 3
examples/jsm/renderers/webgpu/WebGPUTextures.js

@@ -6,12 +6,11 @@ import WebGPUTextureUtils from './WebGPUTextureUtils.js';
 
 class WebGPUTextures {
 
-	constructor( device, properties, info, glslang ) {
+	constructor( device, properties, info ) {
 
 		this.device = device;
 		this.properties = properties;
 		this.info = info;
-		this.glslang = glslang;
 
 		this.defaultTexture = null;
 		this.defaultCubeTexture = null;
@@ -481,7 +480,7 @@ class WebGPUTextures {
 
 		if ( this.utils === null ) {
 
-			this.utils = new WebGPUTextureUtils( this.device, this.glslang ); // only create this helper if necessary
+			this.utils = new WebGPUTextureUtils( this.device ); // only create this helper if necessary
 
 		}
 

+ 0 - 152
examples/jsm/renderers/webgpu/nodes/ShaderLib.js

@@ -1,152 +0,0 @@
-const ShaderLib = {
-
-	common: {
-
-		vertexShader:
-			`#version 450
-
-			void main(){
-
-				NODE_CODE
-
-				NODE_CODE_MVP
-				gl_Position = NODE_MVP;
-
-			}`,
-
-		fragmentShader:
-			`#version 450
-
-			layout(location = 0) out vec4 outColor;
-
-			void main() {
-
-				NODE_CODE
-
-				MaterialDiffuseColor = vec4( 1.0 );
-
-				#ifdef NODE_COLOR
-
-					NODE_CODE_COLOR
-
-					MaterialDiffuseColor = NODE_COLOR;
-
-				#endif
-
-				#ifdef NODE_OPACITY
-
-					NODE_CODE_OPACITY
-
-					MaterialDiffuseColor.a *= NODE_OPACITY;
-
-				#endif
-
-				#ifdef NODE_ALPHA_TEST
-
-					NODE_CODE_ALPHA_TEST
-					if ( MaterialDiffuseColor.a < NODE_ALPHA_TEST ) discard;
-
-				#endif
-
-				#ifdef NODE_LIGHT
-
-					NODE_CODE_LIGHT
-
-					outColor.rgb = NODE_LIGHT;
-					outColor.a = MaterialDiffuseColor.a;
-
-				#else
-
-					outColor = MaterialDiffuseColor;
-
-				#endif
-
-			}`
-
-	},
-
-	standard: {
-
-		vertexShader:
-			`#version 450
-
-			void main(){
-
-				NODE_CODE
-
-				NODE_CODE_MVP
-				gl_Position = NODE_MVP;
-
-			}`,
-
-		fragmentShader:
-			`#version 450
-
-			layout(location = 0) out vec4 outColor;
-
-			void main() {
-
-				NODE_CODE
-
-				MaterialDiffuseColor = vec4( 1.0 );
-				MaterialMetalness = 1.0;
-				MaterialRoughness = 1.0;
-
-				#ifdef NODE_COLOR
-
-					NODE_CODE_COLOR
-
-					MaterialDiffuseColor = NODE_COLOR;
-
-				#endif
-
-				#ifdef NODE_OPACITY
-
-					NODE_CODE_OPACITY
-
-					MaterialDiffuseColor.a *= NODE_OPACITY;
-
-				#endif
-
-				#ifdef NODE_ALPHA_TEST
-
-					NODE_CODE_ALPHA_TEST
-					if ( MaterialDiffuseColor.a < NODE_ALPHA_TEST ) discard;
-
-				#endif
-
-				NODE_CODE_METALNESS
-				MaterialMetalness = NODE_METALNESS;
-
-				NODE_CODE_ROUGHNESS
-				MaterialRoughness = NODE_ROUGHNESS;
-
-				#ifdef NODE_NORMAL
-
-					NODE_CODE_NORMAL
-					TransformedNormalView = NODE_NORMAL;
-
-				#endif
-
-				MaterialDiffuseColor.rgb = MaterialDiffuseColor.rgb * ( 1.0 - MaterialMetalness );
-
-				#ifdef NODE_LIGHT
-
-					NODE_CODE_LIGHT
-
-					outColor.rgb = NODE_LIGHT;
-					outColor.a = MaterialDiffuseColor.a;
-
-				#else
-
-					outColor = MaterialDiffuseColor;
-
-				#endif
-
-			}`
-
-	},
-
-};
-
-export default ShaderLib;

+ 393 - 100
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -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;

+ 5 - 2
examples/webgl_points_nodes.html

@@ -99,8 +99,11 @@
 
 				const spriteSheetCount = new Nodes.Vector2Node( new THREE.Vector2( 6, 6 ) ).setConst( true );
 
-				const fireUV = new Nodes.SpriteSheetUVNode( spriteSheetCount, new Nodes.PointUVNode() );
-				fireUV.frame = new Nodes.OperatorNode( '*', time, particleSpeed );
+				const fireUV = new Nodes.SpriteSheetUVNode( 
+					spriteSheetCount, // count
+					new Nodes.PointUVNode(), // uv
+					new Nodes.OperatorNode( '*', time, particleSpeed ) // current frame
+				);
 
 				const fireSprite = new Nodes.TextureNode( fireMap, fireUV );
 				const fire = new Nodes.OperatorNode( '*', fireSprite, particleIntensity );

+ 3 - 3
examples/webgpu_lights_custom.html

@@ -29,7 +29,7 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 
 			import * as Nodes from './jsm/renderers/nodes/Nodes.js';
-			import { add } from './jsm/renderers/nodes/ShaderNode.js';
+			import { addTo } from './jsm/renderers/nodes/ShaderNode.js';
 
 			let camera, scene, renderer;
 
@@ -93,12 +93,12 @@
 
 					const { lightColor, directDiffuse } = inputs;
 
-					directDiffuse.value = add( directDiffuse.value, lightColor );
+					addTo( directDiffuse, lightColor );
 
 				} );
 
 				const lightingModelContext = new Nodes.ContextNode( allLightsNode );
-				lightingModelContext.setContextValue( 'lightingModel', customLightingModel );
+				lightingModelContext.context.lightingModel = customLightingModel;
 
 				materialPoints.lightNode = lightingModelContext;
 

+ 2 - 0
src/renderers/WebGLRenderer.js

@@ -2152,4 +2152,6 @@ function WebGLRenderer( parameters = {} ) {
 
 }
 
+WebGLRenderer.prototype.isWebGLRenderer = true;
+
 export { WebGLRenderer };