瀏覽代碼

WebGPURenderer: MaterialX (#27294)

* MathNode: Add `bitcast()`

* WGSLNodeBuilder: Add `greaterThan` for vec3

* WGSLNodeBuilder: XOR

* NodeBuilder: .buildFunctionNode() and getFunctionOperator()

* TSL: Function Overloading

* SplitNode: Fix expected output if used uvec or ivec

* TSL Transpiler: Improvements and fixes

* TSL: Fix expected output if used a.assign( b ), returns a

* FIX & TODO: Check reasons

* Fix clearcoat op sequence

* WebGPURenderer: MaterialX Support

* add webgpu materialx examples

* fix backend name

* unknown diff screenshot problem

* testing e2e response
sunag 1 年之前
父節點
當前提交
2d38c5dbf0
共有 28 個文件被更改,包括 2536 次插入805 次删除
  1. 2 0
      examples/files.json
  2. 53 53
      examples/jsm/loaders/MaterialXLoader.js
  3. 2 1
      examples/jsm/nodes/Nodes.js
  4. 3 3
      examples/jsm/nodes/code/CodeNode.js
  5. 26 3
      examples/jsm/nodes/core/NodeBuilder.js
  6. 2 0
      examples/jsm/nodes/core/VarNode.js
  7. 1 1
      examples/jsm/nodes/functions/PhysicalLightingModel.js
  8. 10 10
      examples/jsm/nodes/materialx/MaterialXNodes.js
  9. 128 54
      examples/jsm/nodes/materialx/lib/mx_hsv.js
  10. 1430 618
      examples/jsm/nodes/materialx/lib/mx_noise.js
  11. 24 14
      examples/jsm/nodes/materialx/lib/mx_transform_color.js
  12. 2 0
      examples/jsm/nodes/math/MathNode.js
  13. 19 6
      examples/jsm/nodes/math/OperatorNode.js
  14. 25 2
      examples/jsm/nodes/shadernode/ShaderNode.js
  15. 95 0
      examples/jsm/nodes/utils/FunctionOverloadingNode.js
  16. 8 2
      examples/jsm/nodes/utils/SplitNode.js
  17. 30 0
      examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js
  18. 3 3
      examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
  19. 50 5
      examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
  20. 40 1
      examples/jsm/transpiler/AST.js
  21. 50 8
      examples/jsm/transpiler/GLSLDecoder.js
  22. 4 4
      examples/jsm/transpiler/ShaderToyDecoder.js
  23. 121 17
      examples/jsm/transpiler/TSLEncoder.js
  24. 二進制
      examples/screenshots/webgpu_loader_materialx.jpg
  25. 二進制
      examples/screenshots/webgpu_materialx_noise.jpg
  26. 192 0
      examples/webgpu_loader_materialx.html
  27. 210 0
      examples/webgpu_materialx_noise.html
  28. 6 0
      test/e2e/puppeteer.js

+ 2 - 0
examples/files.json

@@ -340,8 +340,10 @@
 		"webgpu_loader_gltf_compressed",
 		"webgpu_loader_gltf_iridescence",
 		"webgpu_loader_gltf_sheen",
+		"webgpu_loader_materialx",
 		"webgpu_materials",
 		"webgpu_materials_video",
+		"webgpu_materialx_noise",
 		"webgpu_multiple_rendertargets",
 		"webgpu_morphtargets",
 		"webgpu_morphtargets_face",

+ 53 - 53
examples/jsm/loaders/MaterialXLoader.js

@@ -25,7 +25,7 @@ const colorSpaceLib = {
 	mx_srgb_texture_to_lin_rec709
 };
 
-class MtlXElement {
+class MXElement {
 
 	constructor( name, nodeFunc, params = null ) {
 
@@ -39,41 +39,41 @@ class MtlXElement {
 
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 
-const MtlXElements = [
+const MXElements = [
 
 	// << Math >>
-	new MtlXElement( 'add', add, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'subtract', sub, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'multiply', mul, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'divide', div, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'modulo', mod, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'absval', abs, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'sign', sign, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'floor', floor, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'ceil', ceil, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'round', round, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'power', pow, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'sin', sin, [ 'in' ] ),
-	new MtlXElement( 'cos', cos, [ 'in' ] ),
-	new MtlXElement( 'tan', tan, [ 'in' ] ),
-	new MtlXElement( 'asin', asin, [ 'in' ] ),
-	new MtlXElement( 'acos', acos, [ 'in' ] ),
-	new MtlXElement( 'atan2', atan2, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'sqrt', sqrt, [ 'in' ] ),
+	new MXElement( 'add', add, [ 'in1', 'in2' ] ),
+	new MXElement( 'subtract', sub, [ 'in1', 'in2' ] ),
+	new MXElement( 'multiply', mul, [ 'in1', 'in2' ] ),
+	new MXElement( 'divide', div, [ 'in1', 'in2' ] ),
+	new MXElement( 'modulo', mod, [ 'in1', 'in2' ] ),
+	new MXElement( 'absval', abs, [ 'in1', 'in2' ] ),
+	new MXElement( 'sign', sign, [ 'in1', 'in2' ] ),
+	new MXElement( 'floor', floor, [ 'in1', 'in2' ] ),
+	new MXElement( 'ceil', ceil, [ 'in1', 'in2' ] ),
+	new MXElement( 'round', round, [ 'in1', 'in2' ] ),
+	new MXElement( 'power', pow, [ 'in1', 'in2' ] ),
+	new MXElement( 'sin', sin, [ 'in' ] ),
+	new MXElement( 'cos', cos, [ 'in' ] ),
+	new MXElement( 'tan', tan, [ 'in' ] ),
+	new MXElement( 'asin', asin, [ 'in' ] ),
+	new MXElement( 'acos', acos, [ 'in' ] ),
+	new MXElement( 'atan2', atan2, [ 'in1', 'in2' ] ),
+	new MXElement( 'sqrt', sqrt, [ 'in' ] ),
 	//new MtlXElement( 'ln', ... ),
-	new MtlXElement( 'exp', exp, [ 'in' ] ),
-	new MtlXElement( 'clamp', clamp, [ 'in', 'low', 'high' ] ),
-	new MtlXElement( 'min', min, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'max', max, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'normalize', normalize, [ 'in' ] ),
-	new MtlXElement( 'magnitude', length, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'dotproduct', dot, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'crossproduct', cross, [ 'in' ] ),
+	new MXElement( 'exp', exp, [ 'in' ] ),
+	new MXElement( 'clamp', clamp, [ 'in', 'low', 'high' ] ),
+	new MXElement( 'min', min, [ 'in1', 'in2' ] ),
+	new MXElement( 'max', max, [ 'in1', 'in2' ] ),
+	new MXElement( 'normalize', normalize, [ 'in' ] ),
+	new MXElement( 'magnitude', length, [ 'in1', 'in2' ] ),
+	new MXElement( 'dotproduct', dot, [ 'in1', 'in2' ] ),
+	new MXElement( 'crossproduct', cross, [ 'in' ] ),
 	//new MtlXElement( 'transformpoint', ... ),
 	//new MtlXElement( 'transformvector', ... ),
 	//new MtlXElement( 'transformnormal', ... ),
 	//new MtlXElement( 'transformmatrix', ... ),
-	new MtlXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ),
+	new MXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ),
 	//new MtlXElement( 'transpose', ... ),
 	//new MtlXElement( 'determinant', ... ),
 	//new MtlXElement( 'invertmatrix', ... ),
@@ -83,44 +83,44 @@ const MtlXElements = [
 	//new MtlXElement( 'dot', ... ),
 
 	// << Adjustment >>
-	new MtlXElement( 'remap', remap, [ 'in', 'inlow', 'inhigh', 'outlow', 'outhigh' ] ),
-	new MtlXElement( 'smoothstep', smoothstep, [ 'in', 'low', 'high' ] ),
+	new MXElement( 'remap', remap, [ 'in', 'inlow', 'inhigh', 'outlow', 'outhigh' ] ),
+	new MXElement( 'smoothstep', smoothstep, [ 'in', 'low', 'high' ] ),
 	//new MtlXElement( 'curveadjust', ... ),
 	//new MtlXElement( 'curvelookup', ... ),
-	new MtlXElement( 'luminance', luminance, [ 'in', 'lumacoeffs' ] ),
-	new MtlXElement( 'rgbtohsv', mx_rgbtohsv, [ 'in' ] ),
-	new MtlXElement( 'hsvtorgb', mx_hsvtorgb, [ 'in' ] ),
+	new MXElement( 'luminance', luminance, [ 'in', 'lumacoeffs' ] ),
+	new MXElement( 'rgbtohsv', mx_rgbtohsv, [ 'in' ] ),
+	new MXElement( 'hsvtorgb', mx_hsvtorgb, [ 'in' ] ),
 
 	// << Mix >>
-	new MtlXElement( 'mix', mix, [ 'bg', 'fg', 'mix' ] ),
+	new MXElement( 'mix', mix, [ 'bg', 'fg', 'mix' ] ),
 
 	// << Channel >>
-	new MtlXElement( 'combine2', vec2, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'combine3', vec3, [ 'in1', 'in2', 'in3' ] ),
-	new MtlXElement( 'combine4', vec4, [ 'in1', 'in2', 'in3', 'in4' ] ),
+	new MXElement( 'combine2', vec2, [ 'in1', 'in2' ] ),
+	new MXElement( 'combine3', vec3, [ 'in1', 'in2', 'in3' ] ),
+	new MXElement( 'combine4', vec4, [ 'in1', 'in2', 'in3', 'in4' ] ),
 
 	// << Procedural >>
-	new MtlXElement( 'ramplr', mx_ramplr, [ 'valuel', 'valuer', 'texcoord' ] ),
-	new MtlXElement( 'ramptb', mx_ramptb, [ 'valuet', 'valueb', 'texcoord' ] ),
-	new MtlXElement( 'splitlr', mx_splitlr, [ 'valuel', 'valuer', 'texcoord' ] ),
-	new MtlXElement( 'splittb', mx_splittb, [ 'valuet', 'valueb', 'texcoord' ] ),
-	new MtlXElement( 'noise2d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ),
-	new MtlXElement( 'noise3d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ),
-	new MtlXElement( 'fractal3d', mx_fractal_noise_float, [ 'position', 'octaves', 'lacunarity', 'diminish', 'amplitude' ] ),
-	new MtlXElement( 'cellnoise2d', mx_cell_noise_float, [ 'texcoord' ] ),
-	new MtlXElement( 'cellnoise3d', mx_cell_noise_float, [ 'texcoord' ] ),
-	new MtlXElement( 'worleynoise2d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ),
-	new MtlXElement( 'worleynoise3d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ),
+	new MXElement( 'ramplr', mx_ramplr, [ 'valuel', 'valuer', 'texcoord' ] ),
+	new MXElement( 'ramptb', mx_ramptb, [ 'valuet', 'valueb', 'texcoord' ] ),
+	new MXElement( 'splitlr', mx_splitlr, [ 'valuel', 'valuer', 'texcoord' ] ),
+	new MXElement( 'splittb', mx_splittb, [ 'valuet', 'valueb', 'texcoord' ] ),
+	new MXElement( 'noise2d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ),
+	new MXElement( 'noise3d', mx_noise_float, [ 'texcoord', 'amplitude', 'pivot' ] ),
+	new MXElement( 'fractal3d', mx_fractal_noise_float, [ 'position', 'octaves', 'lacunarity', 'diminish', 'amplitude' ] ),
+	new MXElement( 'cellnoise2d', mx_cell_noise_float, [ 'texcoord' ] ),
+	new MXElement( 'cellnoise3d', mx_cell_noise_float, [ 'texcoord' ] ),
+	new MXElement( 'worleynoise2d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ),
+	new MXElement( 'worleynoise3d', mx_worley_noise_float, [ 'texcoord', 'jitter' ] ),
 
 	// << Supplemental >>
 	//new MtlXElement( 'tiledimage', ... ),
 	//new MtlXElement( 'triplanarprojection', triplanarTextures, [ 'filex', 'filey', 'filez' ] ),
 	//new MtlXElement( 'ramp4', ... ),
 	//new MtlXElement( 'place2d', mx_place2d, [ 'texcoord', 'pivot', 'scale', 'rotate', 'offset' ] ),
-	new MtlXElement( 'safepower', mx_safepower, [ 'in1', 'in2' ] ),
-	new MtlXElement( 'contrast', mx_contrast, [ 'in', 'amount', 'pivot' ] ),
+	new MXElement( 'safepower', mx_safepower, [ 'in1', 'in2' ] ),
+	new MXElement( 'contrast', mx_contrast, [ 'in', 'amount', 'pivot' ] ),
 	//new MtlXElement( 'hsvadjust', ... ),
-	new MtlXElement( 'saturate', saturation, [ 'in', 'amount' ] ),
+	new MXElement( 'saturate', saturation, [ 'in', 'amount' ] ),
 	//new MtlXElement( 'extract', ... ),
 	//new MtlXElement( 'separate2', ... ),
 	//new MtlXElement( 'separate3', ... ),
@@ -129,7 +129,7 @@ const MtlXElements = [
 ];
 
 const MtlXLibrary = {};
-MtlXElements.forEach( element => MtlXLibrary[ element.name ] = element );
+MXElements.forEach( element => MtlXLibrary[ element.name ] = element );
 
 class MaterialXLoader extends Loader {
 

+ 2 - 1
examples/jsm/nodes/Nodes.js

@@ -39,7 +39,7 @@ import * as NodeUtils from './core/NodeUtils.js';
 export { NodeUtils };
 
 // math
-export { default as MathNode, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward } from './math/MathNode.js';
+export { default as MathNode, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, bitcast, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward } from './math/MathNode.js';
 export { default as OperatorNode, add, sub, mul, div, remainder, equal, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, xor, bitAnd, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js';
 export { default as CondNode, cond } from './math/CondNode.js';
 export { default as HashNode, hash } from './math/HashNode.js';
@@ -49,6 +49,7 @@ export { default as ArrayElementNode } from './utils/ArrayElementNode.js';
 export { default as ConvertNode } from './utils/ConvertNode.js';
 export { default as DiscardNode, discard } from './utils/DiscardNode.js';
 export { default as EquirectUVNode, equirectUV } from './utils/EquirectUVNode.js';
+export { default as FunctionOverloadingNode, overloadingFn } from './utils/FunctionOverloadingNode.js';
 export { default as JoinNode } from './utils/JoinNode.js';
 export { default as LoopNode, loop } from './utils/LoopNode.js';
 export { default as MatcapUVNode, matcapUV } from './utils/MatcapUVNode.js';

+ 3 - 3
examples/jsm/nodes/code/CodeNode.js

@@ -12,13 +12,13 @@ class CodeNode extends Node {
 		this.code = code;
 		this.language = language;
 
-		this._includes = includes;
+		this.includes = includes;
 
 	}
 
 	setIncludes( includes ) {
 
-		this._includes = includes;
+		this.includes = includes;
 
 		return this;
 
@@ -26,7 +26,7 @@ class CodeNode extends Node {
 
 	getIncludes( /*builder*/ ) {
 
-		return this._includes;
+		return this.includes;
 
 	}
 

+ 26 - 3
examples/jsm/nodes/core/NodeBuilder.js

@@ -6,6 +6,7 @@ import NodeCode from './NodeCode.js';
 import NodeKeywords from './NodeKeywords.js';
 import NodeCache from './NodeCache.js';
 import ParameterNode from './ParameterNode.js';
+import FunctionNode from '../code/FunctionNode.js';
 import { createNodeMaterialFromType } from '../materials/NodeMaterial.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 
@@ -42,8 +43,6 @@ const typeFromArray = new Map( [
 	[ Float32Array, 'float' ]
 ] );
 
-const isNonPaddingElementArray = new Set( [ Int32Array, Uint32Array, Float32Array ] );
-
 const toFloat = ( value ) => {
 
 	value = Number( value );
@@ -95,6 +94,8 @@ class NodeBuilder {
 		this.stacks = [];
 		this.tab = '\t';
 
+		this.currentFunctionNode = null;
+
 		this.context = {
 			keywords: new NodeKeywords(),
 			material: this.material
@@ -552,7 +553,7 @@ class NodeBuilder {
 		if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data;
 
 		const array = dataAttribute.array;
-		const itemSize = isNonPaddingElementArray.has( array.constructor ) ? attribute.itemSize : dataAttribute.stride || attribute.itemSize;
+		const itemSize = attribute.itemSize;
 		const normalized = attribute.normalized;
 
 		let arrayType;
@@ -848,6 +849,22 @@ class NodeBuilder {
 
 	}
 
+	buildFunctionNode( shaderNode ) {
+
+		const fn = new FunctionNode();
+
+		const previous = this.currentFunctionNode;
+
+		this.currentFunctionNode = fn;
+
+		fn.code = this.buildFunctionCode( shaderNode );
+
+		this.currentFunctionNode = previous;
+
+		return fn;
+
+	}
+
 	flowShaderNode( shaderNode ) {
 
 		const layout = shaderNode.layout;
@@ -920,6 +937,12 @@ class NodeBuilder {
 
 	}
 
+	getFunctionOperator() {
+
+		return null;
+
+	}
+
 	flowChildNode( node, output = null ) {
 
 		const previousFlow = this.flow;

+ 2 - 0
examples/jsm/nodes/core/VarNode.js

@@ -10,6 +10,8 @@ class VarNode extends Node {
 		this.node = node;
 		this.name = name;
 
+		this.isVarNode = true;
+
 	}
 
 	isGlobal() {

+ 1 - 1
examples/jsm/nodes/functions/PhysicalLightingModel.js

@@ -371,7 +371,7 @@ class PhysicalLightingModel extends LightingModel {
 				f90: clearcoatF90
 			} );
 
-			const clearcoatLight = outgoingLight.mul( clearcoat.mul( Fcc ).oneMinus() ).add( this.clearcoatSpecularDirect, this.clearcoatSpecularIndirect ).mul( clearcoat );
+			const clearcoatLight = outgoingLight.mul( clearcoat.mul( Fcc ).oneMinus() ).add( this.clearcoatSpecularDirect.add( this.clearcoatSpecularIndirect ).mul( clearcoat ) );
 
 			outgoingLight.assign( clearcoatLight );
 

+ 10 - 10
examples/jsm/nodes/materialx/MaterialXNodes.js

@@ -1,5 +1,5 @@
 import {
-	mx_perlin_noise_float, mx_perlin_noise_vec2, mx_perlin_noise_vec3,
+	mx_perlin_noise_float, mx_perlin_noise_vec3,
 	mx_worley_noise_float as worley_noise_float, mx_worley_noise_vec2 as worley_noise_vec2, mx_worley_noise_vec3 as worley_noise_vec3,
 	mx_cell_noise_float as cell_noise_float,
 	mx_fractal_noise_float as fractal_noise_float, mx_fractal_noise_vec2 as fractal_noise_vec2, mx_fractal_noise_vec3 as fractal_noise_vec3, mx_fractal_noise_vec4 as fractal_noise_vec4
@@ -8,7 +8,7 @@ import { mx_hsvtorgb, mx_rgbtohsv } from './lib/mx_hsv.js';
 import { mx_srgb_texture_to_lin_rec709 } from './lib/mx_transform_color.js';
 import { mix, smoothstep } from '../math/MathNode.js';
 import { uv } from '../accessors/UVNode.js';
-import { float, vec2, vec4 } from '../shadernode/ShaderNode.js';
+import { float, vec2, vec4, int } from '../shadernode/ShaderNode.js';
 
 export const mx_aastep = ( threshold, value ) => {
 
@@ -42,7 +42,7 @@ export const mx_safepower = ( in1, in2 = 1 ) => {
 export const mx_contrast = ( input, amount = 1, pivot = .5 ) => float( input ).sub( pivot ).mul( amount ).add( pivot );
 
 export const mx_noise_float = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_float( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
-export const mx_noise_vec2 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_vec2( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
+//export const mx_noise_vec2 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_vec3( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
 export const mx_noise_vec3 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_vec3( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
 export const mx_noise_vec4 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => {
 
@@ -54,15 +54,15 @@ export const mx_noise_vec4 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => {
 
 };
 
-export const mx_worley_noise_float = ( texcoord = uv(), jitter = 1 ) => worley_noise_float( texcoord.convert( 'vec2|vec3' ), jitter, 1 );
-export const mx_worley_noise_vec2 = ( texcoord = uv(), jitter = 1 ) => worley_noise_vec2( texcoord.convert( 'vec2|vec3' ), jitter, 1 );
-export const mx_worley_noise_vec3 = ( texcoord = uv(), jitter = 1 ) => worley_noise_vec3( texcoord.convert( 'vec2|vec3' ), jitter, 1 );
+export const mx_worley_noise_float = ( texcoord = uv(), jitter = 1 ) => worley_noise_float( texcoord.convert( 'vec2|vec3' ), jitter, int( 1 ) );
+export const mx_worley_noise_vec2 = ( texcoord = uv(), jitter = 1 ) => worley_noise_vec2( texcoord.convert( 'vec2|vec3' ), jitter, int( 1 ) );
+export const mx_worley_noise_vec3 = ( texcoord = uv(), jitter = 1 ) => worley_noise_vec3( texcoord.convert( 'vec2|vec3' ), jitter, int( 1 ) );
 
 export const mx_cell_noise_float = ( texcoord = uv() ) => cell_noise_float( texcoord.convert( 'vec2|vec3' ) );
 
-export const mx_fractal_noise_float = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_float( position, octaves, lacunarity, diminish ).mul( amplitude );
-export const mx_fractal_noise_vec2 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec2( position, octaves, lacunarity, diminish ).mul( amplitude );
-export const mx_fractal_noise_vec3 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec3( position, octaves, lacunarity, diminish ).mul( amplitude );
-export const mx_fractal_noise_vec4 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec4( position, octaves, lacunarity, diminish ).mul( amplitude );
+export const mx_fractal_noise_float = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_float( position, int( octaves ), lacunarity, diminish ).mul( amplitude );
+export const mx_fractal_noise_vec2 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec2( position, int( octaves ), lacunarity, diminish ).mul( amplitude );
+export const mx_fractal_noise_vec3 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec3( position, int( octaves ), lacunarity, diminish ).mul( amplitude );
+export const mx_fractal_noise_vec4 = ( position = uv(), octaves = 3, lacunarity = 2, diminish = .5, amplitude = 1 ) => fractal_noise_vec4( position, int( octaves ), lacunarity, diminish ).mul( amplitude );
 
 export { mx_hsvtorgb, mx_rgbtohsv, mx_srgb_texture_to_lin_rec709 };

+ 128 - 54
examples/jsm/nodes/materialx/lib/mx_hsv.js

@@ -1,56 +1,130 @@
-import { glslFn } from '../../code/FunctionNode.js';
-
-// Original shader code from:
+// Three.js Transpiler
 // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_hsv.glsl
 
-export const mx_hsvtorgb = glslFn( `vec3 mx_hsvtorgb(vec3 hsv)
-{
-    // Reference for this technique: Foley & van Dam
-    float h = hsv.x; float s = hsv.y; float v = hsv.z;
-    if (s < 0.0001f) {
-      return vec3 (v, v, v);
-    } else {
-        h = 6.0f * (h - floor(h));  // expand to [0..6)
-        int hi = int(trunc(h));
-        float f = h - float(hi);
-        float p = v * (1.0f-s);
-        float q = v * (1.0f-s*f);
-        float t = v * (1.0f-s*(1.0f-f));
-        if (hi == 0)
-            return vec3 (v, t, p);
-        else if (hi == 1)
-            return vec3 (q, v, p);
-        else if (hi == 2)
-            return vec3 (p, v, t);
-        else if (hi == 3)
-            return vec3 (p, q, v);
-        else if (hi == 4)
-            return vec3 (t, p, v);
-        return vec3 (v, p, q);
-    }
-}` );
-
-export const mx_rgbtohsv = glslFn( `vec3 mx_rgbtohsv(vec3 c)
-{
-    // See Foley & van Dam
-    float r = c.x; float g = c.y; float b = c.z;
-    float mincomp = min (r, min(g, b));
-    float maxcomp = max (r, max(g, b));
-    float delta = maxcomp - mincomp;  // chroma
-    float h, s, v;
-    v = maxcomp;
-    if (maxcomp > 0.0f)
-        s = delta / maxcomp;
-    else s = 0.0f;
-    if (s <= 0.0f)
-        h = 0.0f;
-    else {
-        if      (r >= maxcomp) h = (g-b) / delta;
-        else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
-        else                   h = 4.0f + (r-g) / delta;
-        h *= (1.0f/6.0f);
-        if (h < 0.0f)
-            h += 1.0f;
-    }
-    return vec3(h, s, v);
-}` );
+import { int, float, vec3, If, tslFn } from '../../shadernode/ShaderNode.js';
+import { add, sub, mul } from '../../math/OperatorNode.js';
+import { floor, trunc, max, min } from '../../math/MathNode.js';
+
+const mx_hsvtorgb = tslFn( ( [ hsv_immutable ] ) => {
+
+	const hsv = vec3( hsv_immutable ).toVar();
+	const h = float( hsv.x ).toVar();
+	const s = float( hsv.y ).toVar();
+	const v = float( hsv.z ).toVar();
+
+	If( s.lessThan( 0.0001 ), () => {
+
+		return vec3( v, v, v );
+
+	} ).else( () => {
+
+		h.assign( mul( 6.0, h.sub( floor( h ) ) ) );
+		const hi = int( trunc( h ) ).toVar();
+		const f = float( h.sub( float( hi ) ) ).toVar();
+		const p = float( v.mul( sub( 1.0, s ) ) ).toVar();
+		const q = float( v.mul( sub( 1.0, s.mul( f ) ) ) ).toVar();
+		const t = float( v.mul( sub( 1.0, s.mul( sub( 1.0, f ) ) ) ) ).toVar();
+
+		If( hi.equal( int( 0 ) ), () => {
+
+			return vec3( v, t, p );
+
+		} ).elseif( hi.equal( int( 1 ) ), () => {
+
+			return vec3( q, v, p );
+
+		} ).elseif( hi.equal( int( 2 ) ), () => {
+
+			return vec3( p, v, t );
+
+		} ).elseif( hi.equal( int( 3 ) ), () => {
+
+			return vec3( p, q, v );
+
+		} ).elseif( hi.equal( int( 4 ) ), () => {
+
+			return vec3( t, p, v );
+
+		} );
+
+		return vec3( v, p, q );
+
+	} );
+
+} );
+
+const mx_rgbtohsv = tslFn( ( [ c_immutable ] ) => {
+
+	const c = vec3( c_immutable ).toVar();
+	const r = float( c.x ).toVar();
+	const g = float( c.y ).toVar();
+	const b = float( c.z ).toVar();
+	const mincomp = float( min( r, min( g, b ) ) ).toVar();
+	const maxcomp = float( max( r, max( g, b ) ) ).toVar();
+	const delta = float( maxcomp.sub( mincomp ) ).toVar();
+	const h = float().toVar(), s = float().toVar(), v = float().toVar();
+	v.assign( maxcomp );
+
+	If( maxcomp.greaterThan( 0.0 ), () => {
+
+		s.assign( delta.div( maxcomp ) );
+
+	} ).else( () => {
+
+		s.assign( 0.0 );
+
+	} );
+
+	If( s.lessThanEqual( 0.0 ), () => {
+
+		h.assign( 0.0 );
+
+	} ).else( () => {
+
+		If( r.greaterThanEqual( maxcomp ), () => {
+
+			h.assign( g.sub( b ).div( delta ) );
+
+		} ).elseif( g.greaterThanEqual( maxcomp ), () => {
+
+			h.assign( add( 2.0, b.sub( r ).div( delta ) ) );
+
+		} ).else( () => {
+
+			h.assign( add( 4.0, r.sub( g ).div( delta ) ) );
+
+		} );
+
+		h.mulAssign( 1.0 / 6.0 );
+
+		If( h.lessThan( 0.0 ), () => {
+
+			h.addAssign( 1.0 );
+
+		} );
+
+	} );
+
+	return vec3( h, s, v );
+
+} );
+
+// layouts
+
+mx_hsvtorgb.setLayout( {
+	name: 'mx_hsvtorgb',
+	type: 'vec3',
+	inputs: [
+		{ name: 'hsv', type: 'vec3' }
+	]
+} );
+
+mx_rgbtohsv.setLayout( {
+	name: 'mx_rgbtohsv',
+	type: 'vec3',
+	inputs: [
+		{ name: 'c', type: 'vec3' }
+	]
+} );
+
+export { mx_hsvtorgb, mx_rgbtohsv };

+ 1430 - 618
examples/jsm/nodes/materialx/lib/mx_noise.js

@@ -1,618 +1,1430 @@
-import { glsl } from '../../code/CodeNode.js';
-import { glslFn } from '../../code/FunctionNode.js';
-
-// Original shader code from:
-// https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl
-
-export const mx_noise = glsl( `float mx_select(bool b, float t, float f)
-{
-    return b ? t : f;
-}
-
-float mx_negate_if(float val, bool b)
-{
-    return b ? -val : val;
-}
-
-int mx_floor(float x)
-{
-    return int(floor(x));
-}
-
-// return mx_floor as well as the fractional remainder
-float mx_floorfrac(float x, out int i)
-{
-    i = mx_floor(x);
-    return x - float(i);
-}
-
-float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
-{
-    float s1 = 1.0 - s;
-    return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
-}
-vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
-{
-    float s1 = 1.0 - s;
-    return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
-}
-float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
-{
-    float s1 = 1.0 - s;
-    float t1 = 1.0 - t;
-    float r1 = 1.0 - r;
-    return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
-            r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
-}
-vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
-{
-    float s1 = 1.0 - s;
-    float t1 = 1.0 - t;
-    float r1 = 1.0 - r;
-    return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
-            r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
-}
-
-// 2 and 3 dimensional gradient functions - perform a dot product against a
-// randomly chosen vector. Note that the gradient vector is not normalized, but
-// this only affects the overal "scale" of the result, so we simply account for
-// the scale by multiplying in the corresponding "perlin" function.
-float mx_gradient_float(uint hash, float x, float y)
-{
-    // 8 possible directions (+-1,+-2) and (+-2,+-1)
-    uint h = hash & 7u;
-    float u = mx_select(h<4u, x, y);
-    float v = 2.0 * mx_select(h<4u, y, x);
-    // compute the dot product with (x,y).
-    return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
-}
-float mx_gradient_float(uint hash, float x, float y, float z)
-{
-    // use vectors pointing to the edges of the cube
-    uint h = hash & 15u;
-    float u = mx_select(h<8u, x, y);
-    float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
-    return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
-}
-vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
-{
-    return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
-}
-vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
-{
-    return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
-}
-// Scaling factors to normalize the result of gradients above.
-// These factors were experimentally calculated to be:
-//    2D:   0.6616
-//    3D:   0.9820
-float mx_gradient_scale2d(float v) { return 0.6616 * v; }
-float mx_gradient_scale3d(float v) { return 0.9820 * v; }
-vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
-vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
-
-/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
-uint mx_rotl32(uint x, int k)
-{
-    return (x<<k) | (x>>(32-k));
-}
-
-void mx_bjmix(inout uint a, inout uint b, inout uint c)
-{
-    a -= c; a ^= mx_rotl32(c, 4); c += b;
-    b -= a; b ^= mx_rotl32(a, 6); a += c;
-    c -= b; c ^= mx_rotl32(b, 8); b += a;
-    a -= c; a ^= mx_rotl32(c,16); c += b;
-    b -= a; b ^= mx_rotl32(a,19); a += c;
-    c -= b; c ^= mx_rotl32(b, 4); b += a;
-}
-
-// Mix up and combine the bits of a, b, and c (doesn't change them, but
-// returns a hash of those three original values).
-uint mx_bjfinal(uint a, uint b, uint c)
-{
-    c ^= b; c -= mx_rotl32(b,14);
-    a ^= c; a -= mx_rotl32(c,11);
-    b ^= a; b -= mx_rotl32(a,25);
-    c ^= b; c -= mx_rotl32(b,16);
-    a ^= c; a -= mx_rotl32(c,4);
-    b ^= a; b -= mx_rotl32(a,14);
-    c ^= b; c -= mx_rotl32(b,24);
-    return c;
-}
-
-// Convert a 32 bit integer into a floating point number in [0,1]
-float mx_bits_to_01(uint bits)
-{
-    return float(bits) / float(uint(0xffffffff));
-}
-
-float mx_fade(float t)
-{
-   return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
-}
-
-uint mx_hash_int(int x)
-{
-    uint len = 1u;
-    uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
-    return mx_bjfinal(seed+uint(x), seed, seed);
-}
-
-uint mx_hash_int(int x, int y)
-{
-    uint len = 2u;
-    uint a, b, c;
-    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
-    a += uint(x);
-    b += uint(y);
-    return mx_bjfinal(a, b, c);
-}
-
-uint mx_hash_int(int x, int y, int z)
-{
-    uint len = 3u;
-    uint a, b, c;
-    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
-    a += uint(x);
-    b += uint(y);
-    c += uint(z);
-    return mx_bjfinal(a, b, c);
-}
-
-uint mx_hash_int(int x, int y, int z, int xx)
-{
-    uint len = 4u;
-    uint a, b, c;
-    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
-    a += uint(x);
-    b += uint(y);
-    c += uint(z);
-    mx_bjmix(a, b, c);
-    a += uint(xx);
-    return mx_bjfinal(a, b, c);
-}
-
-uint mx_hash_int(int x, int y, int z, int xx, int yy)
-{
-    uint len = 5u;
-    uint a, b, c;
-    a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
-    a += uint(x);
-    b += uint(y);
-    c += uint(z);
-    mx_bjmix(a, b, c);
-    a += uint(xx);
-    b += uint(yy);
-    return mx_bjfinal(a, b, c);
-}
-
-uvec3 mx_hash_vec3(int x, int y)
-{
-    uint h = mx_hash_int(x, y);
-    // we only need the low-order bits to be random, so split out
-    // the 32 bit result into 3 parts for each channel
-    uvec3 result;
-    result.x = (h      ) & 0xFFu;
-    result.y = (h >> 8 ) & 0xFFu;
-    result.z = (h >> 16) & 0xFFu;
-    return result;
-}
-
-uvec3 mx_hash_vec3(int x, int y, int z)
-{
-    uint h = mx_hash_int(x, y, z);
-    // we only need the low-order bits to be random, so split out
-    // the 32 bit result into 3 parts for each channel
-    uvec3 result;
-    result.x = (h      ) & 0xFFu;
-    result.y = (h >> 8 ) & 0xFFu;
-    result.z = (h >> 16) & 0xFFu;
-    return result;
-}
-
-float mx_perlin_noise_float(vec2 p)
-{
-    int X, Y;
-    float fx = mx_floorfrac(p.x, X);
-    float fy = mx_floorfrac(p.y, Y);
-    float u = mx_fade(fx);
-    float v = mx_fade(fy);
-    float result = mx_bilerp(
-        mx_gradient_float(mx_hash_int(X  , Y  ), fx    , fy     ),
-        mx_gradient_float(mx_hash_int(X+1, Y  ), fx-1.0, fy     ),
-        mx_gradient_float(mx_hash_int(X  , Y+1), fx    , fy-1.0),
-        mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
-        u, v);
-    return mx_gradient_scale2d(result);
-}
-
-float mx_perlin_noise_float(vec3 p)
-{
-    int X, Y, Z;
-    float fx = mx_floorfrac(p.x, X);
-    float fy = mx_floorfrac(p.y, Y);
-    float fz = mx_floorfrac(p.z, Z);
-    float u = mx_fade(fx);
-    float v = mx_fade(fy);
-    float w = mx_fade(fz);
-    float result = mx_trilerp(
-        mx_gradient_float(mx_hash_int(X  , Y  , Z  ), fx    , fy    , fz     ),
-        mx_gradient_float(mx_hash_int(X+1, Y  , Z  ), fx-1.0, fy    , fz     ),
-        mx_gradient_float(mx_hash_int(X  , Y+1, Z  ), fx    , fy-1.0, fz     ),
-        mx_gradient_float(mx_hash_int(X+1, Y+1, Z  ), fx-1.0, fy-1.0, fz     ),
-        mx_gradient_float(mx_hash_int(X  , Y  , Z+1), fx    , fy    , fz-1.0),
-        mx_gradient_float(mx_hash_int(X+1, Y  , Z+1), fx-1.0, fy    , fz-1.0),
-        mx_gradient_float(mx_hash_int(X  , Y+1, Z+1), fx    , fy-1.0, fz-1.0),
-        mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
-        u, v, w);
-    return mx_gradient_scale3d(result);
-}
-
-vec3 mx_perlin_noise_vec3(vec2 p)
-{
-    int X, Y;
-    float fx = mx_floorfrac(p.x, X);
-    float fy = mx_floorfrac(p.y, Y);
-    float u = mx_fade(fx);
-    float v = mx_fade(fy);
-    vec3 result = mx_bilerp(
-        mx_gradient_vec3(mx_hash_vec3(X  , Y  ), fx    , fy     ),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y  ), fx-1.0, fy     ),
-        mx_gradient_vec3(mx_hash_vec3(X  , Y+1), fx    , fy-1.0),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
-        u, v);
-    return mx_gradient_scale2d(result);
-}
-
-vec3 mx_perlin_noise_vec3(vec3 p)
-{
-    int X, Y, Z;
-    float fx = mx_floorfrac(p.x, X);
-    float fy = mx_floorfrac(p.y, Y);
-    float fz = mx_floorfrac(p.z, Z);
-    float u = mx_fade(fx);
-    float v = mx_fade(fy);
-    float w = mx_fade(fz);
-    vec3 result = mx_trilerp(
-        mx_gradient_vec3(mx_hash_vec3(X  , Y  , Z  ), fx    , fy    , fz     ),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y  , Z  ), fx-1.0, fy    , fz     ),
-        mx_gradient_vec3(mx_hash_vec3(X  , Y+1, Z  ), fx    , fy-1.0, fz     ),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z  ), fx-1.0, fy-1.0, fz     ),
-        mx_gradient_vec3(mx_hash_vec3(X  , Y  , Z+1), fx    , fy    , fz-1.0),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y  , Z+1), fx-1.0, fy    , fz-1.0),
-        mx_gradient_vec3(mx_hash_vec3(X  , Y+1, Z+1), fx    , fy-1.0, fz-1.0),
-        mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
-        u, v, w);
-    return mx_gradient_scale3d(result);
-}
-
-float mx_cell_noise_float(float p)
-{
-    int ix = mx_floor(p);
-    return mx_bits_to_01(mx_hash_int(ix));
-}
-
-float mx_cell_noise_float(vec2 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    return mx_bits_to_01(mx_hash_int(ix, iy));
-}
-
-float mx_cell_noise_float(vec3 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    int iz = mx_floor(p.z);
-    return mx_bits_to_01(mx_hash_int(ix, iy, iz));
-}
-
-float mx_cell_noise_float(vec4 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    int iz = mx_floor(p.z);
-    int iw = mx_floor(p.w);
-    return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
-}
-
-vec3 mx_cell_noise_vec3(float p)
-{
-    int ix = mx_floor(p);
-    return vec3(
-            mx_bits_to_01(mx_hash_int(ix, 0)),
-            mx_bits_to_01(mx_hash_int(ix, 1)),
-            mx_bits_to_01(mx_hash_int(ix, 2))
-    );
-}
-
-vec3 mx_cell_noise_vec3(vec2 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    return vec3(
-            mx_bits_to_01(mx_hash_int(ix, iy, 0)),
-            mx_bits_to_01(mx_hash_int(ix, iy, 1)),
-            mx_bits_to_01(mx_hash_int(ix, iy, 2))
-    );
-}
-
-vec3 mx_cell_noise_vec3(vec3 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    int iz = mx_floor(p.z);
-    return vec3(
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
-    );
-}
-
-vec3 mx_cell_noise_vec3(vec4 p)
-{
-    int ix = mx_floor(p.x);
-    int iy = mx_floor(p.y);
-    int iz = mx_floor(p.z);
-    int iw = mx_floor(p.w);
-    return vec3(
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
-            mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
-    );
-}
-
-float mx_fractal_noise_float(vec3 p, int octaves, float lacunarity, float diminish)
-{
-    float result = 0.0;
-    float amplitude = 1.0;
-    for (int i = 0;  i < octaves; ++i)
-    {
-        result += amplitude * mx_perlin_noise_float(p);
-        amplitude *= diminish;
-        p *= lacunarity;
-    }
-    return result;
-}
-
-vec3 mx_fractal_noise_vec3(vec3 p, int octaves, float lacunarity, float diminish)
-{
-    vec3 result = vec3(0.0);
-    float amplitude = 1.0;
-    for (int i = 0;  i < octaves; ++i)
-    {
-        result += amplitude * mx_perlin_noise_vec3(p);
-        amplitude *= diminish;
-        p *= lacunarity;
-    }
-    return result;
-}
-
-vec2 mx_fractal_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish)
-{
-    return vec2(mx_fractal_noise_float(p, octaves, lacunarity, diminish),
-                mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish));
-}
-
-vec4 mx_fractal_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish)
-{
-    vec3  c = mx_fractal_noise_vec3(p, octaves, lacunarity, diminish);
-    float f = mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish);
-    return vec4(c, f);
-}
-
-float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric)
-{
-    vec3  tmp = mx_cell_noise_vec3(vec2(x+xoff, y+yoff));
-    vec2  off = vec2(tmp.x, tmp.y);
-
-    off -= 0.5f;
-    off *= jitter;
-    off += 0.5f;
-
-    vec2 cellpos = vec2(float(x), float(y)) + off;
-    vec2 diff = cellpos - p;
-    if (metric == 2)
-        return abs(diff.x) + abs(diff.y);       // Manhattan distance
-    if (metric == 3)
-        return max(abs(diff.x), abs(diff.y));   // Chebyshev distance
-    // Either Euclidian or Distance^2
-    return dot(diff, diff);
-}
-
-float mx_worley_distance(vec3 p, int x, int y, int z, int xoff, int yoff, int zoff, float jitter, int metric)
-{
-    vec3  off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff));
-
-    off -= 0.5f;
-    off *= jitter;
-    off += 0.5f;
-
-    vec3 cellpos = vec3(float(x), float(y), float(z)) + off;
-    vec3 diff = cellpos - p;
-    if (metric == 2)
-        return abs(diff.x) + abs(diff.y) + abs(diff.z); // Manhattan distance
-    if (metric == 3)
-        return max(max(abs(diff.x), abs(diff.y)), abs(diff.z)); // Chebyshev distance
-    // Either Euclidian or Distance^2
-    return dot(diff, diff);
-}
-
-float mx_worley_noise_float(vec2 p, float jitter, int metric)
-{
-    int X, Y;
-    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
-    float sqdist = 1e6f;        // Some big number for jitter > 1 (not all GPUs may be IEEE)
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
-            sqdist = min(sqdist, dist);
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}
-
-vec2 mx_worley_noise_vec2(vec2 p, float jitter, int metric)
-{
-    int X, Y;
-    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
-    vec2 sqdist = vec2(1e6f, 1e6f);
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
-            if (dist < sqdist.x)
-            {
-                sqdist.y = sqdist.x;
-                sqdist.x = dist;
-            }
-            else if (dist < sqdist.y)
-            {
-                sqdist.y = dist;
-            }
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}
-
-vec3 mx_worley_noise_vec3(vec2 p, float jitter, int metric)
-{
-    int X, Y;
-    vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
-    vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
-            if (dist < sqdist.x)
-            {
-                sqdist.z = sqdist.y;
-                sqdist.y = sqdist.x;
-                sqdist.x = dist;
-            }
-            else if (dist < sqdist.y)
-            {
-                sqdist.z = sqdist.y;
-                sqdist.y = dist;
-            }
-            else if (dist < sqdist.z)
-            {
-                sqdist.z = dist;
-            }
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}
-
-float mx_worley_noise_float(vec3 p, float jitter, int metric)
-{
-    int X, Y, Z;
-    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
-    float sqdist = 1e6f;
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            for (int z = -1; z <= 1; ++z)
-            {
-                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
-                sqdist = min(sqdist, dist);
-            }
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}
-
-vec2 mx_worley_noise_vec2(vec3 p, float jitter, int metric)
-{
-    int X, Y, Z;
-    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
-    vec2 sqdist = vec2(1e6f, 1e6f);
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            for (int z = -1; z <= 1; ++z)
-            {
-                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
-                if (dist < sqdist.x)
-                {
-                    sqdist.y = sqdist.x;
-                    sqdist.x = dist;
-                }
-                else if (dist < sqdist.y)
-                {
-                    sqdist.y = dist;
-                }
-            }
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}
-
-vec3 mx_worley_noise_vec3(vec3 p, float jitter, int metric)
-{
-    int X, Y, Z;
-    vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
-    vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
-    for (int x = -1; x <= 1; ++x)
-    {
-        for (int y = -1; y <= 1; ++y)
-        {
-            for (int z = -1; z <= 1; ++z)
-            {
-                float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
-                if (dist < sqdist.x)
-                {
-                    sqdist.z = sqdist.y;
-                    sqdist.y = sqdist.x;
-                    sqdist.x = dist;
-                }
-                else if (dist < sqdist.y)
-                {
-                    sqdist.z = sqdist.y;
-                    sqdist.y = dist;
-                }
-                else if (dist < sqdist.z)
-                {
-                    sqdist.z = dist;
-                }
-            }
-        }
-    }
-    if (metric == 0)
-        sqdist = sqrt(sqdist);
-    return sqdist;
-}` );
-
-const includes = [ mx_noise ];
-
-export const mx_perlin_noise_float = glslFn( 'float mx_perlin_noise_float( any p )', includes );
-export const mx_perlin_noise_vec2 = glslFn( 'vec2 mx_perlin_noise_vec2( any p )', includes );
-export const mx_perlin_noise_vec3 = glslFn( 'vec3 mx_perlin_noise_vec3( any p )', includes );
-
-export const mx_cell_noise_float = glslFn( 'float mx_cell_noise_float( vec3 p )', includes );
-
-export const mx_worley_noise_float = glslFn( 'float mx_worley_noise_float( any p, float jitter, int metric )', includes );
-export const mx_worley_noise_vec2 = glslFn( 'vec2 mx_worley_noise_vec2( any p, float jitter, int metric )', includes );
-export const mx_worley_noise_vec3 = glslFn( 'vec3 mx_worley_noise_vec3( any p, float jitter, int metric )', includes );
-
-export const mx_fractal_noise_float = glslFn( 'float mx_fractal_noise_float( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec2 = glslFn( 'vec2 mx_fractal_noise_vec2( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec3 = glslFn( 'vec3 mx_fractal_noise_vec3( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec4 = glslFn( 'vec4 mx_fractal_noise_vec4( vec3 p, int octaves, float lacunarity, float diminish )', includes );
+// Three.js Transpiler
+// https://raw.githubusercontent.com/AcademySoftwareFoundation/MaterialX/main/libraries/stdlib/genglsl/lib/mx_noise.glsl
+
+import { int, uint, float, vec3, bool, uvec3, vec2, vec4, If, tslFn } from '../../shadernode/ShaderNode.js';
+import { cond } from '../../math/CondNode.js';
+import { sub, mul } from '../../math/OperatorNode.js';
+import { floor, abs, max, dot, min, sqrt } from '../../math/MathNode.js';
+import { overloadingFn } from '../../utils/FunctionOverloadingNode.js';
+import { loop } from '../../utils/LoopNode.js';
+
+const mx_select = tslFn( ( [ b_immutable, t_immutable, f_immutable ] ) => {
+
+	const f = float( f_immutable ).toVar();
+	const t = float( t_immutable ).toVar();
+	const b = bool( b_immutable ).toVar();
+
+	return cond( b, t, f );
+
+} );
+
+const mx_negate_if = tslFn( ( [ val_immutable, b_immutable ] ) => {
+
+	const b = bool( b_immutable ).toVar();
+	const val = float( val_immutable ).toVar();
+
+	return cond( b, val.negate(), val );
+
+} );
+
+const mx_floor = tslFn( ( [ x_immutable ] ) => {
+
+	const x = float( x_immutable ).toVar();
+
+	return int( floor( x ) );
+
+} );
+
+const mx_floorfrac = tslFn( ( [ x_immutable, i ] ) => {
+
+	const x = float( x_immutable ).toVar();
+	i.assign( mx_floor( x ) );
+
+	return x.sub( float( i ) );
+
+} );
+
+const mx_bilerp_0 = tslFn( ( [ v0_immutable, v1_immutable, v2_immutable, v3_immutable, s_immutable, t_immutable ] ) => {
+
+	const t = float( t_immutable ).toVar();
+	const s = float( s_immutable ).toVar();
+	const v3 = float( v3_immutable ).toVar();
+	const v2 = float( v2_immutable ).toVar();
+	const v1 = float( v1_immutable ).toVar();
+	const v0 = float( v0_immutable ).toVar();
+	const s1 = float( sub( 1.0, s ) ).toVar();
+
+	return sub( 1.0, t ).mul( v0.mul( s1 ).add( v1.mul( s ) ) ).add( t.mul( v2.mul( s1 ).add( v3.mul( s ) ) ) );
+
+} );
+
+const mx_bilerp_1 = tslFn( ( [ v0_immutable, v1_immutable, v2_immutable, v3_immutable, s_immutable, t_immutable ] ) => {
+
+	const t = float( t_immutable ).toVar();
+	const s = float( s_immutable ).toVar();
+	const v3 = vec3( v3_immutable ).toVar();
+	const v2 = vec3( v2_immutable ).toVar();
+	const v1 = vec3( v1_immutable ).toVar();
+	const v0 = vec3( v0_immutable ).toVar();
+	const s1 = float( sub( 1.0, s ) ).toVar();
+
+	return sub( 1.0, t ).mul( v0.mul( s1 ).add( v1.mul( s ) ) ).add( t.mul( v2.mul( s1 ).add( v3.mul( s ) ) ) );
+
+} );
+
+const mx_bilerp = overloadingFn( [ mx_bilerp_0, mx_bilerp_1 ] );
+
+const mx_trilerp_0 = tslFn( ( [ v0_immutable, v1_immutable, v2_immutable, v3_immutable, v4_immutable, v5_immutable, v6_immutable, v7_immutable, s_immutable, t_immutable, r_immutable ] ) => {
+
+	const r = float( r_immutable ).toVar();
+	const t = float( t_immutable ).toVar();
+	const s = float( s_immutable ).toVar();
+	const v7 = float( v7_immutable ).toVar();
+	const v6 = float( v6_immutable ).toVar();
+	const v5 = float( v5_immutable ).toVar();
+	const v4 = float( v4_immutable ).toVar();
+	const v3 = float( v3_immutable ).toVar();
+	const v2 = float( v2_immutable ).toVar();
+	const v1 = float( v1_immutable ).toVar();
+	const v0 = float( v0_immutable ).toVar();
+	const s1 = float( sub( 1.0, s ) ).toVar();
+	const t1 = float( sub( 1.0, t ) ).toVar();
+	const r1 = float( sub( 1.0, r ) ).toVar();
+
+	return r1.mul( t1.mul( v0.mul( s1 ).add( v1.mul( s ) ) ).add( t.mul( v2.mul( s1 ).add( v3.mul( s ) ) ) ) ).add( r.mul( t1.mul( v4.mul( s1 ).add( v5.mul( s ) ) ).add( t.mul( v6.mul( s1 ).add( v7.mul( s ) ) ) ) ) );
+
+} );
+
+const mx_trilerp_1 = tslFn( ( [ v0_immutable, v1_immutable, v2_immutable, v3_immutable, v4_immutable, v5_immutable, v6_immutable, v7_immutable, s_immutable, t_immutable, r_immutable ] ) => {
+
+	const r = float( r_immutable ).toVar();
+	const t = float( t_immutable ).toVar();
+	const s = float( s_immutable ).toVar();
+	const v7 = vec3( v7_immutable ).toVar();
+	const v6 = vec3( v6_immutable ).toVar();
+	const v5 = vec3( v5_immutable ).toVar();
+	const v4 = vec3( v4_immutable ).toVar();
+	const v3 = vec3( v3_immutable ).toVar();
+	const v2 = vec3( v2_immutable ).toVar();
+	const v1 = vec3( v1_immutable ).toVar();
+	const v0 = vec3( v0_immutable ).toVar();
+	const s1 = float( sub( 1.0, s ) ).toVar();
+	const t1 = float( sub( 1.0, t ) ).toVar();
+	const r1 = float( sub( 1.0, r ) ).toVar();
+
+	return r1.mul( t1.mul( v0.mul( s1 ).add( v1.mul( s ) ) ).add( t.mul( v2.mul( s1 ).add( v3.mul( s ) ) ) ) ).add( r.mul( t1.mul( v4.mul( s1 ).add( v5.mul( s ) ) ).add( t.mul( v6.mul( s1 ).add( v7.mul( s ) ) ) ) ) );
+
+} );
+
+const mx_trilerp = overloadingFn( [ mx_trilerp_0, mx_trilerp_1 ] );
+
+const mx_gradient_float_0 = tslFn( ( [ hash_immutable, x_immutable, y_immutable ] ) => {
+
+	const y = float( y_immutable ).toVar();
+	const x = float( x_immutable ).toVar();
+	const hash = uint( hash_immutable ).toVar();
+	const h = uint( hash.bitAnd( uint( 7 ) ) ).toVar();
+	const u = float( mx_select( h.lessThan( uint( 4 ) ), x, y ) ).toVar();
+	const v = float( mul( 2.0, mx_select( h.lessThan( uint( 4 ) ), y, x ) ) ).toVar();
+
+	return mx_negate_if( u, bool( h.bitAnd( uint( 1 ) ) ) ).add( mx_negate_if( v, bool( h.bitAnd( uint( 2 ) ) ) ) );
+
+} );
+
+const mx_gradient_float_1 = tslFn( ( [ hash_immutable, x_immutable, y_immutable, z_immutable ] ) => {
+
+	const z = float( z_immutable ).toVar();
+	const y = float( y_immutable ).toVar();
+	const x = float( x_immutable ).toVar();
+	const hash = uint( hash_immutable ).toVar();
+	const h = uint( hash.bitAnd( uint( 15 ) ) ).toVar();
+	const u = float( mx_select( h.lessThan( uint( 8 ) ), x, y ) ).toVar();
+	const v = float( mx_select( h.lessThan( uint( 4 ) ), y, mx_select( h.equal( uint( 12 ) ).or( h.equal( uint( 14 ) ) ), x, z ) ) ).toVar();
+
+	return mx_negate_if( u, bool( h.bitAnd( uint( 1 ) ) ) ).add( mx_negate_if( v, bool( h.bitAnd( uint( 2 ) ) ) ) );
+
+} );
+
+const mx_gradient_float = overloadingFn( [ mx_gradient_float_0, mx_gradient_float_1 ] );
+
+const mx_gradient_vec3_0 = tslFn( ( [ hash_immutable, x_immutable, y_immutable ] ) => {
+
+	const y = float( y_immutable ).toVar();
+	const x = float( x_immutable ).toVar();
+	const hash = uvec3( hash_immutable ).toVar();
+
+	return vec3( mx_gradient_float( hash.x, x, y ), mx_gradient_float( hash.y, x, y ), mx_gradient_float( hash.z, x, y ) );
+
+} );
+
+const mx_gradient_vec3_1 = tslFn( ( [ hash_immutable, x_immutable, y_immutable, z_immutable ] ) => {
+
+	const z = float( z_immutable ).toVar();
+	const y = float( y_immutable ).toVar();
+	const x = float( x_immutable ).toVar();
+	const hash = uvec3( hash_immutable ).toVar();
+
+	return vec3( mx_gradient_float( hash.x, x, y, z ), mx_gradient_float( hash.y, x, y, z ), mx_gradient_float( hash.z, x, y, z ) );
+
+} );
+
+const mx_gradient_vec3 = overloadingFn( [ mx_gradient_vec3_0, mx_gradient_vec3_1 ] );
+
+const mx_gradient_scale2d_0 = tslFn( ( [ v_immutable ] ) => {
+
+	const v = float( v_immutable ).toVar();
+
+	return mul( 0.6616, v );
+
+} );
+
+const mx_gradient_scale3d_0 = tslFn( ( [ v_immutable ] ) => {
+
+	const v = float( v_immutable ).toVar();
+
+	return mul( 0.9820, v );
+
+} );
+
+const mx_gradient_scale2d_1 = tslFn( ( [ v_immutable ] ) => {
+
+	const v = vec3( v_immutable ).toVar();
+
+	return mul( 0.6616, v );
+
+} );
+
+const mx_gradient_scale2d = overloadingFn( [ mx_gradient_scale2d_0, mx_gradient_scale2d_1 ] );
+
+const mx_gradient_scale3d_1 = tslFn( ( [ v_immutable ] ) => {
+
+	const v = vec3( v_immutable ).toVar();
+
+	return mul( 0.9820, v );
+
+} );
+
+const mx_gradient_scale3d = overloadingFn( [ mx_gradient_scale3d_0, mx_gradient_scale3d_1 ] );
+
+const mx_rotl32 = tslFn( ( [ x_immutable, k_immutable ] ) => {
+
+	const k = int( k_immutable ).toVar();
+	const x = uint( x_immutable ).toVar();
+
+	return x.shiftLeft( k ).bitOr( x.shiftRight( int( 32 ).sub( k ) ) );
+
+} );
+
+const mx_bjmix = tslFn( ( [ a, b, c ] ) => {
+
+	a.subAssign( c );
+	a.bitXorAssign( mx_rotl32( c, int( 4 ) ) );
+	c.addAssign( b );
+	b.subAssign( a );
+	b.bitXorAssign( mx_rotl32( a, int( 6 ) ) );
+	a.addAssign( c );
+	c.subAssign( b );
+	c.bitXorAssign( mx_rotl32( b, int( 8 ) ) );
+	b.addAssign( a );
+	a.subAssign( c );
+	a.bitXorAssign( mx_rotl32( c, int( 16 ) ) );
+	c.addAssign( b );
+	b.subAssign( a );
+	b.bitXorAssign( mx_rotl32( a, int( 19 ) ) );
+	a.addAssign( c );
+	c.subAssign( b );
+	c.bitXorAssign( mx_rotl32( b, int( 4 ) ) );
+	b.addAssign( a );
+
+} );
+
+const mx_bjfinal = tslFn( ( [ a_immutable, b_immutable, c_immutable ] ) => {
+
+	const c = uint( c_immutable ).toVar();
+	const b = uint( b_immutable ).toVar();
+	const a = uint( a_immutable ).toVar();
+	c.bitXorAssign( b );
+	c.subAssign( mx_rotl32( b, int( 14 ) ) );
+	a.bitXorAssign( c );
+	a.subAssign( mx_rotl32( c, int( 11 ) ) );
+	b.bitXorAssign( a );
+	b.subAssign( mx_rotl32( a, int( 25 ) ) );
+	c.bitXorAssign( b );
+	c.subAssign( mx_rotl32( b, int( 16 ) ) );
+	a.bitXorAssign( c );
+	a.subAssign( mx_rotl32( c, int( 4 ) ) );
+	b.bitXorAssign( a );
+	b.subAssign( mx_rotl32( a, int( 14 ) ) );
+	c.bitXorAssign( b );
+	c.subAssign( mx_rotl32( b, int( 24 ) ) );
+
+	return c;
+
+} );
+
+const mx_bits_to_01 = tslFn( ( [ bits_immutable ] ) => {
+
+	const bits = uint( bits_immutable ).toVar();
+
+	return float( bits ).div( float( uint( int( 0xffffffff ) ) ) );
+
+} );
+
+const mx_fade = tslFn( ( [ t_immutable ] ) => {
+
+	const t = float( t_immutable ).toVar();
+
+	return t.mul( t.mul( t.mul( t.mul( t.mul( 6.0 ).sub( 15.0 ) ).add( 10.0 ) ) ) );
+
+} );
+
+const mx_hash_int_0 = tslFn( ( [ x_immutable ] ) => {
+
+	const x = int( x_immutable ).toVar();
+	const len = uint( uint( 1 ) ).toVar();
+	const seed = uint( uint( int( 0xdeadbeef ) ).add( len.shiftLeft( uint( 2 ) ).add( uint( 13 ) ) ) ).toVar();
+
+	return mx_bjfinal( seed.add( uint( x ) ), seed, seed );
+
+} );
+
+const mx_hash_int_1 = tslFn( ( [ x_immutable, y_immutable ] ) => {
+
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const len = uint( uint( 2 ) ).toVar();
+	const a = uint().toVar(), b = uint().toVar(), c = uint().toVar();
+	a.assign( b.assign( c.assign( uint( int( 0xdeadbeef ) ).add( len.shiftLeft( uint( 2 ) ).add( uint( 13 ) ) ) ) ) );
+	a.addAssign( uint( x ) );
+	b.addAssign( uint( y ) );
+
+	return mx_bjfinal( a, b, c );
+
+} );
+
+const mx_hash_int_2 = tslFn( ( [ x_immutable, y_immutable, z_immutable ] ) => {
+
+	const z = int( z_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const len = uint( uint( 3 ) ).toVar();
+	const a = uint().toVar(), b = uint().toVar(), c = uint().toVar();
+	a.assign( b.assign( c.assign( uint( int( 0xdeadbeef ) ).add( len.shiftLeft( uint( 2 ) ).add( uint( 13 ) ) ) ) ) );
+	a.addAssign( uint( x ) );
+	b.addAssign( uint( y ) );
+	c.addAssign( uint( z ) );
+
+	return mx_bjfinal( a, b, c );
+
+} );
+
+const mx_hash_int_3 = tslFn( ( [ x_immutable, y_immutable, z_immutable, xx_immutable ] ) => {
+
+	const xx = int( xx_immutable ).toVar();
+	const z = int( z_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const len = uint( uint( 4 ) ).toVar();
+	const a = uint().toVar(), b = uint().toVar(), c = uint().toVar();
+	a.assign( b.assign( c.assign( uint( int( 0xdeadbeef ) ).add( len.shiftLeft( uint( 2 ) ).add( uint( 13 ) ) ) ) ) );
+	a.addAssign( uint( x ) );
+	b.addAssign( uint( y ) );
+	c.addAssign( uint( z ) );
+	mx_bjmix( a, b, c );
+	a.addAssign( uint( xx ) );
+
+	return mx_bjfinal( a, b, c );
+
+} );
+
+const mx_hash_int_4 = tslFn( ( [ x_immutable, y_immutable, z_immutable, xx_immutable, yy_immutable ] ) => {
+
+	const yy = int( yy_immutable ).toVar();
+	const xx = int( xx_immutable ).toVar();
+	const z = int( z_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const len = uint( uint( 5 ) ).toVar();
+	const a = uint().toVar(), b = uint().toVar(), c = uint().toVar();
+	a.assign( b.assign( c.assign( uint( int( 0xdeadbeef ) ).add( len.shiftLeft( uint( 2 ) ).add( uint( 13 ) ) ) ) ) );
+	a.addAssign( uint( x ) );
+	b.addAssign( uint( y ) );
+	c.addAssign( uint( z ) );
+	mx_bjmix( a, b, c );
+	a.addAssign( uint( xx ) );
+	b.addAssign( uint( yy ) );
+
+	return mx_bjfinal( a, b, c );
+
+} );
+
+const mx_hash_int = overloadingFn( [ mx_hash_int_0, mx_hash_int_1, mx_hash_int_2, mx_hash_int_3, mx_hash_int_4 ] );
+
+const mx_hash_vec3_0 = tslFn( ( [ x_immutable, y_immutable ] ) => {
+
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const h = uint( mx_hash_int( x, y ) ).toVar();
+	const result = uvec3().toVar();
+	result.x.assign( h.bitAnd( int( 0xFF ) ) );
+	result.y.assign( h.shiftRight( int( 8 ) ).bitAnd( int( 0xFF ) ) );
+	result.z.assign( h.shiftRight( int( 16 ) ).bitAnd( int( 0xFF ) ) );
+
+	return result;
+
+} );
+
+const mx_hash_vec3_1 = tslFn( ( [ x_immutable, y_immutable, z_immutable ] ) => {
+
+	const z = int( z_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const h = uint( mx_hash_int( x, y, z ) ).toVar();
+	const result = uvec3().toVar();
+	result.x.assign( h.bitAnd( int( 0xFF ) ) );
+	result.y.assign( h.shiftRight( int( 8 ) ).bitAnd( int( 0xFF ) ) );
+	result.z.assign( h.shiftRight( int( 16 ) ).bitAnd( int( 0xFF ) ) );
+
+	return result;
+
+} );
+
+const mx_hash_vec3 = overloadingFn( [ mx_hash_vec3_0, mx_hash_vec3_1 ] );
+
+const mx_perlin_noise_float_0 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec2( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar();
+	const fx = float( mx_floorfrac( p.x, X ) ).toVar();
+	const fy = float( mx_floorfrac( p.y, Y ) ).toVar();
+	const u = float( mx_fade( fx ) ).toVar();
+	const v = float( mx_fade( fy ) ).toVar();
+	const result = float( mx_bilerp( mx_gradient_float( mx_hash_int( X, Y ), fx, fy ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y ), fx.sub( 1.0 ), fy ), mx_gradient_float( mx_hash_int( X, Y.add( int( 1 ) ) ), fx, fy.sub( 1.0 ) ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y.add( int( 1 ) ) ), fx.sub( 1.0 ), fy.sub( 1.0 ) ), u, v ) ).toVar();
+
+	return mx_gradient_scale2d( result );
+
+} );
+
+const mx_perlin_noise_float_1 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec3( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar(), Z = int().toVar();
+	const fx = float( mx_floorfrac( p.x, X ) ).toVar();
+	const fy = float( mx_floorfrac( p.y, Y ) ).toVar();
+	const fz = float( mx_floorfrac( p.z, Z ) ).toVar();
+	const u = float( mx_fade( fx ) ).toVar();
+	const v = float( mx_fade( fy ) ).toVar();
+	const w = float( mx_fade( fz ) ).toVar();
+	const result = float( mx_trilerp( mx_gradient_float( mx_hash_int( X, Y, Z ), fx, fy, fz ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y, Z ), fx.sub( 1.0 ), fy, fz ), mx_gradient_float( mx_hash_int( X, Y.add( int( 1 ) ), Z ), fx, fy.sub( 1.0 ), fz ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y.add( int( 1 ) ), Z ), fx.sub( 1.0 ), fy.sub( 1.0 ), fz ), mx_gradient_float( mx_hash_int( X, Y, Z.add( int( 1 ) ) ), fx, fy, fz.sub( 1.0 ) ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y, Z.add( int( 1 ) ) ), fx.sub( 1.0 ), fy, fz.sub( 1.0 ) ), mx_gradient_float( mx_hash_int( X, Y.add( int( 1 ) ), Z.add( int( 1 ) ) ), fx, fy.sub( 1.0 ), fz.sub( 1.0 ) ), mx_gradient_float( mx_hash_int( X.add( int( 1 ) ), Y.add( int( 1 ) ), Z.add( int( 1 ) ) ), fx.sub( 1.0 ), fy.sub( 1.0 ), fz.sub( 1.0 ) ), u, v, w ) ).toVar();
+
+	return mx_gradient_scale3d( result );
+
+} );
+
+const mx_perlin_noise_float = overloadingFn( [ mx_perlin_noise_float_0, mx_perlin_noise_float_1 ] );
+
+const mx_perlin_noise_vec3_0 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec2( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar();
+	const fx = float( mx_floorfrac( p.x, X ) ).toVar();
+	const fy = float( mx_floorfrac( p.y, Y ) ).toVar();
+	const u = float( mx_fade( fx ) ).toVar();
+	const v = float( mx_fade( fy ) ).toVar();
+	const result = vec3( mx_bilerp( mx_gradient_vec3( mx_hash_vec3( X, Y ), fx, fy ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y ), fx.sub( 1.0 ), fy ), mx_gradient_vec3( mx_hash_vec3( X, Y.add( int( 1 ) ) ), fx, fy.sub( 1.0 ) ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y.add( int( 1 ) ) ), fx.sub( 1.0 ), fy.sub( 1.0 ) ), u, v ) ).toVar();
+
+	return mx_gradient_scale2d( result );
+
+} );
+
+const mx_perlin_noise_vec3_1 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec3( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar(), Z = int().toVar();
+	const fx = float( mx_floorfrac( p.x, X ) ).toVar();
+	const fy = float( mx_floorfrac( p.y, Y ) ).toVar();
+	const fz = float( mx_floorfrac( p.z, Z ) ).toVar();
+	const u = float( mx_fade( fx ) ).toVar();
+	const v = float( mx_fade( fy ) ).toVar();
+	const w = float( mx_fade( fz ) ).toVar();
+	const result = vec3( mx_trilerp( mx_gradient_vec3( mx_hash_vec3( X, Y, Z ), fx, fy, fz ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y, Z ), fx.sub( 1.0 ), fy, fz ), mx_gradient_vec3( mx_hash_vec3( X, Y.add( int( 1 ) ), Z ), fx, fy.sub( 1.0 ), fz ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y.add( int( 1 ) ), Z ), fx.sub( 1.0 ), fy.sub( 1.0 ), fz ), mx_gradient_vec3( mx_hash_vec3( X, Y, Z.add( int( 1 ) ) ), fx, fy, fz.sub( 1.0 ) ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y, Z.add( int( 1 ) ) ), fx.sub( 1.0 ), fy, fz.sub( 1.0 ) ), mx_gradient_vec3( mx_hash_vec3( X, Y.add( int( 1 ) ), Z.add( int( 1 ) ) ), fx, fy.sub( 1.0 ), fz.sub( 1.0 ) ), mx_gradient_vec3( mx_hash_vec3( X.add( int( 1 ) ), Y.add( int( 1 ) ), Z.add( int( 1 ) ) ), fx.sub( 1.0 ), fy.sub( 1.0 ), fz.sub( 1.0 ) ), u, v, w ) ).toVar();
+
+	return mx_gradient_scale3d( result );
+
+} );
+
+const mx_perlin_noise_vec3 = overloadingFn( [ mx_perlin_noise_vec3_0, mx_perlin_noise_vec3_1 ] );
+
+const mx_cell_noise_float_0 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = float( p_immutable ).toVar();
+	const ix = int( mx_floor( p ) ).toVar();
+
+	return mx_bits_to_01( mx_hash_int( ix ) );
+
+} );
+
+const mx_cell_noise_float_1 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec2( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+
+	return mx_bits_to_01( mx_hash_int( ix, iy ) );
+
+} );
+
+const mx_cell_noise_float_2 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec3( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+	const iz = int( mx_floor( p.z ) ).toVar();
+
+	return mx_bits_to_01( mx_hash_int( ix, iy, iz ) );
+
+} );
+
+const mx_cell_noise_float_3 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec4( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+	const iz = int( mx_floor( p.z ) ).toVar();
+	const iw = int( mx_floor( p.w ) ).toVar();
+
+	return mx_bits_to_01( mx_hash_int( ix, iy, iz, iw ) );
+
+} );
+
+const mx_cell_noise_float = overloadingFn( [ mx_cell_noise_float_0, mx_cell_noise_float_1, mx_cell_noise_float_2, mx_cell_noise_float_3 ] );
+
+const mx_cell_noise_vec3_0 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = float( p_immutable ).toVar();
+	const ix = int( mx_floor( p ) ).toVar();
+
+	return vec3( mx_bits_to_01( mx_hash_int( ix, int( 0 ) ) ), mx_bits_to_01( mx_hash_int( ix, int( 1 ) ) ), mx_bits_to_01( mx_hash_int( ix, int( 2 ) ) ) );
+
+} );
+
+const mx_cell_noise_vec3_1 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec2( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+
+	return vec3( mx_bits_to_01( mx_hash_int( ix, iy, int( 0 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, int( 1 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, int( 2 ) ) ) );
+
+} );
+
+const mx_cell_noise_vec3_2 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec3( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+	const iz = int( mx_floor( p.z ) ).toVar();
+
+	return vec3( mx_bits_to_01( mx_hash_int( ix, iy, iz, int( 0 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, iz, int( 1 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, iz, int( 2 ) ) ) );
+
+} );
+
+const mx_cell_noise_vec3_3 = tslFn( ( [ p_immutable ] ) => {
+
+	const p = vec4( p_immutable ).toVar();
+	const ix = int( mx_floor( p.x ) ).toVar();
+	const iy = int( mx_floor( p.y ) ).toVar();
+	const iz = int( mx_floor( p.z ) ).toVar();
+	const iw = int( mx_floor( p.w ) ).toVar();
+
+	return vec3( mx_bits_to_01( mx_hash_int( ix, iy, iz, iw, int( 0 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, iz, iw, int( 1 ) ) ), mx_bits_to_01( mx_hash_int( ix, iy, iz, iw, int( 2 ) ) ) );
+
+} );
+
+const mx_cell_noise_vec3 = overloadingFn( [ mx_cell_noise_vec3_0, mx_cell_noise_vec3_1, mx_cell_noise_vec3_2, mx_cell_noise_vec3_3 ] );
+
+const mx_fractal_noise_float = tslFn( ( [ p_immutable, octaves_immutable, lacunarity_immutable, diminish_immutable ] ) => {
+
+	const diminish = float( diminish_immutable ).toVar();
+	const lacunarity = float( lacunarity_immutable ).toVar();
+	const octaves = int( octaves_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const result = float( 0.0 ).toVar();
+	const amplitude = float( 1.0 ).toVar();
+
+	loop( { start: int( 0 ), end: octaves }, ( { i } ) => {
+
+		result.addAssign( amplitude.mul( mx_perlin_noise_float( p ) ) );
+		amplitude.mulAssign( diminish );
+		p.mulAssign( lacunarity );
+
+	} );
+
+	return result;
+
+} );
+
+const mx_fractal_noise_vec3 = tslFn( ( [ p_immutable, octaves_immutable, lacunarity_immutable, diminish_immutable ] ) => {
+
+	const diminish = float( diminish_immutable ).toVar();
+	const lacunarity = float( lacunarity_immutable ).toVar();
+	const octaves = int( octaves_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const result = vec3( 0.0 ).toVar();
+	const amplitude = float( 1.0 ).toVar();
+
+	loop( { start: int( 0 ), end: octaves }, ( { i } ) => {
+
+		result.addAssign( amplitude.mul( mx_perlin_noise_vec3( p ) ) );
+		amplitude.mulAssign( diminish );
+		p.mulAssign( lacunarity );
+
+	} );
+
+	return result;
+
+} );
+
+const mx_fractal_noise_vec2 = tslFn( ( [ p_immutable, octaves_immutable, lacunarity_immutable, diminish_immutable ] ) => {
+
+	const diminish = float( diminish_immutable ).toVar();
+	const lacunarity = float( lacunarity_immutable ).toVar();
+	const octaves = int( octaves_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+
+	return vec2( mx_fractal_noise_float( p, octaves, lacunarity, diminish ), mx_fractal_noise_float( p.add( vec3( int( 19 ), int( 193 ), int( 17 ) ) ), octaves, lacunarity, diminish ) );
+
+} );
+
+const mx_fractal_noise_vec4 = tslFn( ( [ p_immutable, octaves_immutable, lacunarity_immutable, diminish_immutable ] ) => {
+
+	const diminish = float( diminish_immutable ).toVar();
+	const lacunarity = float( lacunarity_immutable ).toVar();
+	const octaves = int( octaves_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const c = vec3( mx_fractal_noise_vec3( p, octaves, lacunarity, diminish ) ).toVar();
+	const f = float( mx_fractal_noise_float( p.add( vec3( int( 19 ), int( 193 ), int( 17 ) ) ), octaves, lacunarity, diminish ) ).toVar();
+
+	return vec4( c, f );
+
+} );
+
+const mx_worley_distance_0 = tslFn( ( [ p_immutable, x_immutable, y_immutable, xoff_immutable, yoff_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const yoff = int( yoff_immutable ).toVar();
+	const xoff = int( xoff_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const p = vec2( p_immutable ).toVar();
+	const tmp = vec3( mx_cell_noise_vec3( vec2( x.add( xoff ), y.add( yoff ) ) ) ).toVar();
+	const off = vec2( tmp.x, tmp.y ).toVar();
+	off.subAssign( 0.5 );
+	off.mulAssign( jitter );
+	off.addAssign( 0.5 );
+	const cellpos = vec2( vec2( float( x ), float( y ) ).add( off ) ).toVar();
+	const diff = vec2( cellpos.sub( p ) ).toVar();
+
+	If( metric.equal( int( 2 ) ), () => {
+
+		return abs( diff.x ).add( abs( diff.y ) );
+
+	} );
+
+	If( metric.equal( int( 3 ) ), () => {
+
+		return max( abs( diff.x ), abs( diff.y ) );
+
+	} );
+
+	return dot( diff, diff );
+
+} );
+
+const mx_worley_distance_1 = tslFn( ( [ p_immutable, x_immutable, y_immutable, z_immutable, xoff_immutable, yoff_immutable, zoff_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const zoff = int( zoff_immutable ).toVar();
+	const yoff = int( yoff_immutable ).toVar();
+	const xoff = int( xoff_immutable ).toVar();
+	const z = int( z_immutable ).toVar();
+	const y = int( y_immutable ).toVar();
+	const x = int( x_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const off = vec3( mx_cell_noise_vec3( vec3( x.add( xoff ), y.add( yoff ), z.add( zoff ) ) ) ).toVar();
+	off.subAssign( 0.5 );
+	off.mulAssign( jitter );
+	off.addAssign( 0.5 );
+	const cellpos = vec3( vec3( float( x ), float( y ), float( z ) ).add( off ) ).toVar();
+	const diff = vec3( cellpos.sub( p ) ).toVar();
+
+	If( metric.equal( int( 2 ) ), () => {
+
+		return abs( diff.x ).add( abs( diff.y ).add( abs( diff.z ) ) );
+
+	} );
+
+	If( metric.equal( int( 3 ) ), () => {
+
+		return max( max( abs( diff.x ), abs( diff.y ) ), abs( diff.z ) );
+
+	} );
+
+	return dot( diff, diff );
+
+} );
+
+const mx_worley_distance = overloadingFn( [ mx_worley_distance_0, mx_worley_distance_1 ] );
+
+const mx_worley_noise_float_0 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec2( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar();
+	const localpos = vec2( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ) ).toVar();
+	const sqdist = float( 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			const dist = float( mx_worley_distance( localpos, x, y, X, Y, jitter, metric ) ).toVar();
+			sqdist.assign( min( sqdist, dist ) );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_vec2_0 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec2( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar();
+	const localpos = vec2( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ) ).toVar();
+	const sqdist = vec2( 1e6, 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			const dist = float( mx_worley_distance( localpos, x, y, X, Y, jitter, metric ) ).toVar();
+
+			If( dist.lessThan( sqdist.x ), () => {
+
+				sqdist.y.assign( sqdist.x );
+				sqdist.x.assign( dist );
+
+			} ).elseif( dist.lessThan( sqdist.y ), () => {
+
+				sqdist.y.assign( dist );
+
+			} );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_vec3_0 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec2( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar();
+	const localpos = vec2( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ) ).toVar();
+	const sqdist = vec3( 1e6, 1e6, 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			const dist = float( mx_worley_distance( localpos, x, y, X, Y, jitter, metric ) ).toVar();
+
+			If( dist.lessThan( sqdist.x ), () => {
+
+				sqdist.z.assign( sqdist.y );
+				sqdist.y.assign( sqdist.x );
+				sqdist.x.assign( dist );
+
+			} ).elseif( dist.lessThan( sqdist.y ), () => {
+
+				sqdist.z.assign( sqdist.y );
+				sqdist.y.assign( dist );
+
+			} ).elseif( dist.lessThan( sqdist.z ), () => {
+
+				sqdist.z.assign( dist );
+
+			} );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_float_1 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar(), Z = int().toVar();
+	const localpos = vec3( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ), mx_floorfrac( p.z, Z ) ).toVar();
+	const sqdist = float( 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			loop( { start: - 1, end: int( 1 ), name: 'z', condition: '<=' }, ( { z } ) => {
+
+				const dist = float( mx_worley_distance( localpos, x, y, z, X, Y, Z, jitter, metric ) ).toVar();
+				sqdist.assign( min( sqdist, dist ) );
+
+			} );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_float = overloadingFn( [ mx_worley_noise_float_0, mx_worley_noise_float_1 ] );
+
+const mx_worley_noise_vec2_1 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar(), Z = int().toVar();
+	const localpos = vec3( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ), mx_floorfrac( p.z, Z ) ).toVar();
+	const sqdist = vec2( 1e6, 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			loop( { start: - 1, end: int( 1 ), name: 'z', condition: '<=' }, ( { z } ) => {
+
+				const dist = float( mx_worley_distance( localpos, x, y, z, X, Y, Z, jitter, metric ) ).toVar();
+
+				If( dist.lessThan( sqdist.x ), () => {
+
+					sqdist.y.assign( sqdist.x );
+					sqdist.x.assign( dist );
+
+				} ).elseif( dist.lessThan( sqdist.y ), () => {
+
+					sqdist.y.assign( dist );
+
+				} );
+
+			} );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_vec2 = overloadingFn( [ mx_worley_noise_vec2_0, mx_worley_noise_vec2_1 ] );
+
+const mx_worley_noise_vec3_1 = tslFn( ( [ p_immutable, jitter_immutable, metric_immutable ] ) => {
+
+	const metric = int( metric_immutable ).toVar();
+	const jitter = float( jitter_immutable ).toVar();
+	const p = vec3( p_immutable ).toVar();
+	const X = int().toVar(), Y = int().toVar(), Z = int().toVar();
+	const localpos = vec3( mx_floorfrac( p.x, X ), mx_floorfrac( p.y, Y ), mx_floorfrac( p.z, Z ) ).toVar();
+	const sqdist = vec3( 1e6, 1e6, 1e6 ).toVar();
+
+	loop( { start: - 1, end: int( 1 ), name: 'x', condition: '<=' }, ( { x } ) => {
+
+		loop( { start: - 1, end: int( 1 ), name: 'y', condition: '<=' }, ( { y } ) => {
+
+			loop( { start: - 1, end: int( 1 ), name: 'z', condition: '<=' }, ( { z } ) => {
+
+				const dist = float( mx_worley_distance( localpos, x, y, z, X, Y, Z, jitter, metric ) ).toVar();
+
+				If( dist.lessThan( sqdist.x ), () => {
+
+					sqdist.z.assign( sqdist.y );
+					sqdist.y.assign( sqdist.x );
+					sqdist.x.assign( dist );
+
+				} ).elseif( dist.lessThan( sqdist.y ), () => {
+
+					sqdist.z.assign( sqdist.y );
+					sqdist.y.assign( dist );
+
+				} ).elseif( dist.lessThan( sqdist.z ), () => {
+
+					sqdist.z.assign( dist );
+
+				} );
+
+			} );
+
+		} );
+
+	} );
+
+	If( metric.equal( int( 0 ) ), () => {
+
+		sqdist.assign( sqrt( sqdist ) );
+
+	} );
+
+	return sqdist;
+
+} );
+
+const mx_worley_noise_vec3 = overloadingFn( [ mx_worley_noise_vec3_0, mx_worley_noise_vec3_1 ] );
+
+// layouts
+
+mx_select.setLayout( {
+	name: 'mx_select',
+	type: 'float',
+	inputs: [
+		{ name: 'b', type: 'bool' },
+		{ name: 't', type: 'float' },
+		{ name: 'f', type: 'float' }
+	]
+} );
+
+mx_negate_if.setLayout( {
+	name: 'mx_negate_if',
+	type: 'float',
+	inputs: [
+		{ name: 'val', type: 'float' },
+		{ name: 'b', type: 'bool' }
+	]
+} );
+
+mx_floor.setLayout( {
+	name: 'mx_floor',
+	type: 'int',
+	inputs: [
+		{ name: 'x', type: 'float' }
+	]
+} );
+
+mx_bilerp_0.setLayout( {
+	name: 'mx_bilerp_0',
+	type: 'float',
+	inputs: [
+		{ name: 'v0', type: 'float' },
+		{ name: 'v1', type: 'float' },
+		{ name: 'v2', type: 'float' },
+		{ name: 'v3', type: 'float' },
+		{ name: 's', type: 'float' },
+		{ name: 't', type: 'float' }
+	]
+} );
+
+mx_bilerp_1.setLayout( {
+	name: 'mx_bilerp_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'v0', type: 'vec3' },
+		{ name: 'v1', type: 'vec3' },
+		{ name: 'v2', type: 'vec3' },
+		{ name: 'v3', type: 'vec3' },
+		{ name: 's', type: 'float' },
+		{ name: 't', type: 'float' }
+	]
+} );
+
+mx_trilerp_0.setLayout( {
+	name: 'mx_trilerp_0',
+	type: 'float',
+	inputs: [
+		{ name: 'v0', type: 'float' },
+		{ name: 'v1', type: 'float' },
+		{ name: 'v2', type: 'float' },
+		{ name: 'v3', type: 'float' },
+		{ name: 'v4', type: 'float' },
+		{ name: 'v5', type: 'float' },
+		{ name: 'v6', type: 'float' },
+		{ name: 'v7', type: 'float' },
+		{ name: 's', type: 'float' },
+		{ name: 't', type: 'float' },
+		{ name: 'r', type: 'float' }
+	]
+} );
+
+mx_trilerp_1.setLayout( {
+	name: 'mx_trilerp_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'v0', type: 'vec3' },
+		{ name: 'v1', type: 'vec3' },
+		{ name: 'v2', type: 'vec3' },
+		{ name: 'v3', type: 'vec3' },
+		{ name: 'v4', type: 'vec3' },
+		{ name: 'v5', type: 'vec3' },
+		{ name: 'v6', type: 'vec3' },
+		{ name: 'v7', type: 'vec3' },
+		{ name: 's', type: 'float' },
+		{ name: 't', type: 'float' },
+		{ name: 'r', type: 'float' }
+	]
+} );
+
+mx_gradient_float_0.setLayout( {
+	name: 'mx_gradient_float_0',
+	type: 'float',
+	inputs: [
+		{ name: 'hash', type: 'uint' },
+		{ name: 'x', type: 'float' },
+		{ name: 'y', type: 'float' }
+	]
+} );
+
+mx_gradient_float_1.setLayout( {
+	name: 'mx_gradient_float_1',
+	type: 'float',
+	inputs: [
+		{ name: 'hash', type: 'uint' },
+		{ name: 'x', type: 'float' },
+		{ name: 'y', type: 'float' },
+		{ name: 'z', type: 'float' }
+	]
+} );
+
+mx_gradient_vec3_0.setLayout( {
+	name: 'mx_gradient_vec3_0',
+	type: 'vec3',
+	inputs: [
+		{ name: 'hash', type: 'uvec3' },
+		{ name: 'x', type: 'float' },
+		{ name: 'y', type: 'float' }
+	]
+} );
+
+mx_gradient_vec3_1.setLayout( {
+	name: 'mx_gradient_vec3_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'hash', type: 'uvec3' },
+		{ name: 'x', type: 'float' },
+		{ name: 'y', type: 'float' },
+		{ name: 'z', type: 'float' }
+	]
+} );
+
+mx_gradient_scale2d_0.setLayout( {
+	name: 'mx_gradient_scale2d_0',
+	type: 'float',
+	inputs: [
+		{ name: 'v', type: 'float' }
+	]
+} );
+
+mx_gradient_scale3d_0.setLayout( {
+	name: 'mx_gradient_scale3d_0',
+	type: 'float',
+	inputs: [
+		{ name: 'v', type: 'float' }
+	]
+} );
+
+mx_gradient_scale2d_1.setLayout( {
+	name: 'mx_gradient_scale2d_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'v', type: 'vec3' }
+	]
+} );
+
+mx_gradient_scale3d_1.setLayout( {
+	name: 'mx_gradient_scale3d_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'v', type: 'vec3' }
+	]
+} );
+
+mx_rotl32.setLayout( {
+	name: 'mx_rotl32',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'uint' },
+		{ name: 'k', type: 'int' }
+	]
+} );
+
+mx_bjfinal.setLayout( {
+	name: 'mx_bjfinal',
+	type: 'uint',
+	inputs: [
+		{ name: 'a', type: 'uint' },
+		{ name: 'b', type: 'uint' },
+		{ name: 'c', type: 'uint' }
+	]
+} );
+
+mx_bits_to_01.setLayout( {
+	name: 'mx_bits_to_01',
+	type: 'float',
+	inputs: [
+		{ name: 'bits', type: 'uint' }
+	]
+} );
+
+mx_fade.setLayout( {
+	name: 'mx_fade',
+	type: 'float',
+	inputs: [
+		{ name: 't', type: 'float' }
+	]
+} );
+
+mx_hash_int_0.setLayout( {
+	name: 'mx_hash_int_0',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'int' }
+	]
+} );
+
+mx_hash_int_1.setLayout( {
+	name: 'mx_hash_int_1',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' }
+	]
+} );
+
+mx_hash_int_2.setLayout( {
+	name: 'mx_hash_int_2',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'z', type: 'int' }
+	]
+} );
+
+mx_hash_int_3.setLayout( {
+	name: 'mx_hash_int_3',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'z', type: 'int' },
+		{ name: 'xx', type: 'int' }
+	]
+} );
+
+mx_hash_int_4.setLayout( {
+	name: 'mx_hash_int_4',
+	type: 'uint',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'z', type: 'int' },
+		{ name: 'xx', type: 'int' },
+		{ name: 'yy', type: 'int' }
+	]
+} );
+
+mx_hash_vec3_0.setLayout( {
+	name: 'mx_hash_vec3_0',
+	type: 'uvec3',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' }
+	]
+} );
+
+mx_hash_vec3_1.setLayout( {
+	name: 'mx_hash_vec3_1',
+	type: 'uvec3',
+	inputs: [
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'z', type: 'int' }
+	]
+} );
+
+mx_perlin_noise_float_0.setLayout( {
+	name: 'mx_perlin_noise_float_0',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec2' }
+	]
+} );
+
+mx_perlin_noise_float_1.setLayout( {
+	name: 'mx_perlin_noise_float_1',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec3' }
+	]
+} );
+
+mx_perlin_noise_vec3_0.setLayout( {
+	name: 'mx_perlin_noise_vec3_0',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec2' }
+	]
+} );
+
+mx_perlin_noise_vec3_1.setLayout( {
+	name: 'mx_perlin_noise_vec3_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec3' }
+	]
+} );
+
+mx_cell_noise_float_0.setLayout( {
+	name: 'mx_cell_noise_float_0',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'float' }
+	]
+} );
+
+mx_cell_noise_float_1.setLayout( {
+	name: 'mx_cell_noise_float_1',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec2' }
+	]
+} );
+
+mx_cell_noise_float_2.setLayout( {
+	name: 'mx_cell_noise_float_2',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec3' }
+	]
+} );
+
+mx_cell_noise_float_3.setLayout( {
+	name: 'mx_cell_noise_float_3',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec4' }
+	]
+} );
+
+mx_cell_noise_vec3_0.setLayout( {
+	name: 'mx_cell_noise_vec3_0',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'float' }
+	]
+} );
+
+mx_cell_noise_vec3_1.setLayout( {
+	name: 'mx_cell_noise_vec3_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec2' }
+	]
+} );
+
+mx_cell_noise_vec3_2.setLayout( {
+	name: 'mx_cell_noise_vec3_2',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec3' }
+	]
+} );
+
+mx_cell_noise_vec3_3.setLayout( {
+	name: 'mx_cell_noise_vec3_3',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec4' }
+	]
+} );
+
+mx_fractal_noise_float.setLayout( {
+	name: 'mx_fractal_noise_float',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'octaves', type: 'int' },
+		{ name: 'lacunarity', type: 'float' },
+		{ name: 'diminish', type: 'float' }
+	]
+} );
+
+mx_fractal_noise_vec3.setLayout( {
+	name: 'mx_fractal_noise_vec3',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'octaves', type: 'int' },
+		{ name: 'lacunarity', type: 'float' },
+		{ name: 'diminish', type: 'float' }
+	]
+} );
+
+mx_fractal_noise_vec2.setLayout( {
+	name: 'mx_fractal_noise_vec2',
+	type: 'vec2',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'octaves', type: 'int' },
+		{ name: 'lacunarity', type: 'float' },
+		{ name: 'diminish', type: 'float' }
+	]
+} );
+
+mx_fractal_noise_vec4.setLayout( {
+	name: 'mx_fractal_noise_vec4',
+	type: 'vec4',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'octaves', type: 'int' },
+		{ name: 'lacunarity', type: 'float' },
+		{ name: 'diminish', type: 'float' }
+	]
+} );
+
+mx_worley_distance_0.setLayout( {
+	name: 'mx_worley_distance_0',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec2' },
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'xoff', type: 'int' },
+		{ name: 'yoff', type: 'int' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_distance_1.setLayout( {
+	name: 'mx_worley_distance_1',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'x', type: 'int' },
+		{ name: 'y', type: 'int' },
+		{ name: 'z', type: 'int' },
+		{ name: 'xoff', type: 'int' },
+		{ name: 'yoff', type: 'int' },
+		{ name: 'zoff', type: 'int' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_float_0.setLayout( {
+	name: 'mx_worley_noise_float_0',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec2' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_vec2_0.setLayout( {
+	name: 'mx_worley_noise_vec2_0',
+	type: 'vec2',
+	inputs: [
+		{ name: 'p', type: 'vec2' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_vec3_0.setLayout( {
+	name: 'mx_worley_noise_vec3_0',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec2' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_float_1.setLayout( {
+	name: 'mx_worley_noise_float_1',
+	type: 'float',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_vec2_1.setLayout( {
+	name: 'mx_worley_noise_vec2_1',
+	type: 'vec2',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+mx_worley_noise_vec3_1.setLayout( {
+	name: 'mx_worley_noise_vec3_1',
+	type: 'vec3',
+	inputs: [
+		{ name: 'p', type: 'vec3' },
+		{ name: 'jitter', type: 'float' },
+		{ name: 'metric', type: 'int' }
+	]
+} );
+
+export { mx_select, mx_negate_if, mx_floor, mx_floorfrac, mx_bilerp, mx_trilerp, mx_gradient_float, mx_gradient_vec3, mx_gradient_scale2d, mx_gradient_scale3d, mx_rotl32, mx_bjmix, mx_bjfinal, mx_bits_to_01, mx_fade, mx_hash_int, mx_hash_vec3, mx_perlin_noise_float, mx_perlin_noise_vec3, mx_cell_noise_float, mx_cell_noise_vec3, mx_fractal_noise_float, mx_fractal_noise_vec3, mx_fractal_noise_vec2, mx_fractal_noise_vec4, mx_worley_distance, mx_worley_noise_float, mx_worley_noise_vec2, mx_worley_noise_vec3 };

+ 24 - 14
examples/jsm/nodes/materialx/lib/mx_transform_color.js

@@ -1,19 +1,29 @@
-import { glsl } from '../../code/CodeNode.js';
-import { glslFn } from '../../code/FunctionNode.js';
-
-// Original shader code from:
+// Three.js Transpiler
 // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_transform_color.glsl
 
-export const mx_transform_color = glsl( `#define M_AP1_TO_REC709 mat3(1.705079555511475, -0.1297005265951157, -0.02416634373366833, -0.6242334842681885, 1.138468623161316, -0.1246141716837883, -0.0808461606502533, -0.008768022060394287, 1.148780584335327)
+import { bvec3, vec3, tslFn } from '../../shadernode/ShaderNode.js';
+import { greaterThan } from '../../math/OperatorNode.js';
+import { max, pow, mix } from '../../math/MathNode.js';
+
+const mx_srgb_texture_to_lin_rec709 = tslFn( ( [ color_immutable ] ) => {
+
+	const color = vec3( color_immutable ).toVar();
+	const isAbove = bvec3( greaterThan( color, vec3( 0.04045 ) ) ).toVar();
+	const linSeg = vec3( color.div( 12.92 ) ).toVar();
+	const powSeg = vec3( pow( max( color.add( vec3( 0.055 ) ), vec3( 0.0 ) ).div( 1.055 ), vec3( 2.4 ) ) ).toVar();
+
+	return mix( linSeg, powSeg, isAbove );
+
+} );
 
-vec3 mx_srgb_texture_to_lin_rec709(vec3 color)
-{
-    bvec3 isAbove = greaterThan(color, vec3(0.04045));
-    vec3 linSeg = color / 12.92;
-    vec3 powSeg = pow(max(color + vec3(0.055), vec3(0.0)) / 1.055, vec3(2.4));
-    return mix(linSeg, powSeg, isAbove);
-}` );
+// layouts
 
-const includes = [ mx_transform_color ];
+mx_srgb_texture_to_lin_rec709.setLayout( {
+	name: 'mx_srgb_texture_to_lin_rec709',
+	type: 'vec3',
+	inputs: [
+		{ name: 'color', type: 'vec3' }
+	]
+} );
 
-export const mx_srgb_texture_to_lin_rec709 = glslFn( 'vec3 mx_srgb_texture_to_lin_rec709( vec3 color )', includes );
+export { mx_srgb_texture_to_lin_rec709 };

+ 2 - 0
examples/jsm/nodes/math/MathNode.js

@@ -220,6 +220,7 @@ MathNode.ROUND = 'round';
 MathNode.RECIPROCAL = 'reciprocal';
 MathNode.TRUNC = 'trunc';
 MathNode.FWIDTH = 'fwidth';
+MathNode.BITCAST = 'bitcast';
 
 // 2 inputs
 
@@ -278,6 +279,7 @@ export const round = nodeProxy( MathNode, MathNode.ROUND );
 export const reciprocal = nodeProxy( MathNode, MathNode.RECIPROCAL );
 export const trunc = nodeProxy( MathNode, MathNode.TRUNC );
 export const fwidth = nodeProxy( MathNode, MathNode.FWIDTH );
+export const bitcast = nodeProxy( MathNode, MathNode.BITCAST );
 
 export const atan2 = nodeProxy( MathNode, MathNode.ATAN2 );
 export const min = nodeProxy( MathNode, MathNode.MIN );

+ 19 - 6
examples/jsm/nodes/math/OperatorNode.js

@@ -157,34 +157,47 @@ class OperatorNode extends TempNode {
 		const b = bNode.build( builder, typeB );
 
 		const outputLength = builder.getTypeLength( output );
+		const fnOpSnippet = builder.getFunctionOperator( op );
 
 		if ( output !== 'void' ) {
 
 			if ( op === '<' && outputLength > 1 ) {
 
-				return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${a}, ${b} )`, type, output );
+				return builder.format( `${ builder.getMethod( 'lessThan' ) }( ${ a }, ${ b } )`, type, output );
 
 			} else if ( op === '<=' && outputLength > 1 ) {
 
-				return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${a}, ${b} )`, type, output );
+				return builder.format( `${ builder.getMethod( 'lessThanEqual' ) }( ${ a }, ${ b } )`, type, output );
 
 			} else if ( op === '>' && outputLength > 1 ) {
 
-				return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${a}, ${b} )`, type, output );
+				return builder.format( `${ builder.getMethod( 'greaterThan' ) }( ${ a }, ${ b } )`, type, output );
 
 			} else if ( op === '>=' && outputLength > 1 ) {
 
-				return builder.format( `${ builder.getMethod( 'greaterThanEqual' ) }( ${a}, ${b} )`, type, output );
+				return builder.format( `${ builder.getMethod( 'greaterThanEqual' ) }( ${ a }, ${ b } )`, type, output );
+
+			} else if ( fnOpSnippet ) {
+
+				return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output );
 
 			} else {
 
-				return builder.format( `( ${a} ${this.op} ${b} )`, type, output );
+				return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );
 
 			}
 
 		} else if ( typeA !== 'void' ) {
 
-			return builder.format( `${a} ${this.op} ${b}`, type, output );
+			if ( fnOpSnippet ) {
+
+				return builder.format( `${ fnOpSnippet }( ${ a }, ${ b } )`, type, output );
+
+			} else {
+
+				return builder.format( `${ a } ${ op } ${ b }`, type, output );
+
+			}
 
 		}
 

+ 25 - 2
examples/jsm/nodes/shadernode/ShaderNode.js

@@ -40,7 +40,13 @@ const shaderNodeHandler = {
 
 			if ( node.isStackNode !== true && prop === 'assign' ) {
 
-				return ( ...params ) => currentStack.assign( nodeObj, ...params );
+				return ( ...params ) => {
+
+					currentStack.assign( nodeObj, ...params );
+
+					return nodeObj;
+
+				};
 
 			} else if ( NodeElements.has( prop ) ) {
 
@@ -271,6 +277,12 @@ class ShaderCallNodeInternal extends Node {
 
 			}
 
+			if ( builder.currentFunctionNode !== null ) {
+
+				builder.currentFunctionNode.includes.push( functionNode );
+
+			}
+
 			return nodeObject( functionNode.call( inputNodes ) );
 
 		}
@@ -505,7 +517,18 @@ addNodeClass( 'ShaderNode', ShaderNode );
 
 //
 
-export const setCurrentStack = stack => currentStack = stack;
+export const setCurrentStack = ( stack ) => {
+
+	if ( currentStack === stack ) {
+
+		//throw new Error( 'Stack already defined.' );
+
+	}
+
+	currentStack = stack;
+
+};
+
 export const getCurrentStack = () => currentStack;
 
 export const If = ( ...params ) => currentStack.if( ...params );

+ 95 - 0
examples/jsm/nodes/utils/FunctionOverloadingNode.js

@@ -0,0 +1,95 @@
+import Node, { addNodeClass } from '../core/Node.js';
+import { nodeProxy } from '../shadernode/ShaderNode.js';
+
+class FunctionOverloadingNode extends Node {
+
+	constructor( functionNodes = [], ...parametersNodes ) {
+
+		super();
+
+		this.functionNodes = functionNodes;
+		this.parametersNodes = parametersNodes;
+
+		this._candidateFnCall = null;
+
+	}
+
+	getNodeType() {
+
+		return this.functionNodes[ 0 ].shaderNode.layout.type;
+
+	}
+
+	setup( builder ) {
+
+		const params = this.parametersNodes;
+
+		let candidateFnCall = this._candidateFnCall;
+
+		if ( candidateFnCall === null ) {
+
+			let candidateFn = null;
+			let candidateScore = - 1;
+
+			for ( const functionNode of this.functionNodes ) {
+
+				const shaderNode = functionNode.shaderNode;
+				const layout = shaderNode.layout;
+
+				if ( layout === null ) {
+
+					throw new Error( 'FunctionOverloadingNode: FunctionNode must be a layout.' );
+
+				}
+
+				const inputs = layout.inputs;
+
+				if ( params.length === inputs.length ) {
+
+					let score = 0;
+
+					for ( let i = 0; i < params.length; i ++ ) {
+
+						const param = params[ i ];
+						const input = inputs[ i ];
+
+						if ( param.getNodeType( builder ) === input.type ) {
+
+							score ++;
+
+						} else {
+
+							score = 0;
+
+						}
+
+					}
+
+					if ( score > candidateScore ) {
+
+						candidateFn = functionNode;
+						candidateScore = score;
+
+					}
+
+				}
+
+			}
+
+			this._candidateFnCall = candidateFnCall = candidateFn( ...params );
+
+		}
+
+		return candidateFnCall;
+
+	}
+
+}
+
+export default FunctionOverloadingNode;
+
+const overloadingBaseFn = nodeProxy( FunctionOverloadingNode );
+
+export const overloadingFn = ( functionNodes ) => ( ...params ) => overloadingBaseFn( functionNodes, ...params );
+
+addNodeClass( 'FunctionOverloadingNode', FunctionOverloadingNode );

+ 8 - 2
examples/jsm/nodes/utils/SplitNode.js

@@ -30,9 +30,15 @@ class SplitNode extends Node {
 
 	}
 
+	getPrimitiveType( builder ) {
+
+		return builder.getPrimitiveType( this.node.getNodeType( builder ) );
+
+	}
+
 	getNodeType( builder ) {
 
-		return builder.getTypeFromLength( this.components.length );
+		return builder.getTypeFromLength( this.components.length, this.getPrimitiveType( builder ) );
 
 	}
 
@@ -53,7 +59,7 @@ class SplitNode extends Node {
 
 				// needed expand the input node
 
-				type = builder.getTypeFromLength( this.getVectorLength() );
+				type = builder.getTypeFromLength( this.getVectorLength(), this.getPrimitiveType( builder ) );
 
 			}
 

+ 30 - 0
examples/jsm/renderers/webgl-legacy/nodes/WebGLNodeBuilder.js

@@ -439,6 +439,36 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
+	buildFunctionCode( shaderNode ) {
+
+		const layout = shaderNode.layout;
+		const flowData = this.flowShaderNode( shaderNode );
+
+		const parameters = [];
+
+		for ( const input of layout.inputs ) {
+
+			parameters.push( this.getType( input.type ) + ' ' + input.name );
+
+		}
+
+		//
+
+		const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) {
+
+	${ flowData.vars }
+
+${ flowData.code }
+	return ${ flowData.result };
+
+}`;
+
+		//
+
+		return code;
+
+	}
+
 	getUniforms( shaderStage ) {
 
 		const uniforms = this.uniforms[ shaderStage ];

+ 3 - 3
examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js

@@ -1,4 +1,4 @@
-import { MathNode, GLSLNodeParser, NodeBuilder, NodeMaterial, FunctionNode } from '../../../nodes/Nodes.js';
+import { MathNode, GLSLNodeParser, NodeBuilder, NodeMaterial } from '../../../nodes/Nodes.js';
 
 import UniformBuffer from '../../common/UniformBuffer.js';
 import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js';
@@ -53,7 +53,7 @@ class GLSLNodeBuilder extends NodeBuilder {
 
 	}
 
-	buildFunctionNode( shaderNode ) {
+	buildFunctionCode( shaderNode ) {
 
 		const layout = shaderNode.layout;
 		const flowData = this.flowShaderNode( shaderNode );
@@ -79,7 +79,7 @@ ${ flowData.code }
 
 		//
 
-		return new FunctionNode( code );
+		return code;
 
 	}
 

+ 50 - 5
examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -9,7 +9,7 @@ import UniformBuffer from '../../common/UniformBuffer.js';
 import StorageBuffer from '../../common/StorageBuffer.js';
 import { getVectorLength, getStrideLength } from '../../common/BufferUtils.js';
 
-import { NodeBuilder, CodeNode, NodeMaterial, FunctionNode } from '../../../nodes/Nodes.js';
+import { NodeBuilder, CodeNode, NodeMaterial } from '../../../nodes/Nodes.js';
 
 import { getFormat } from '../utils/WebGPUTextureUtils.js';
 
@@ -25,6 +25,10 @@ const supports = {
 	instance: true
 };
 
+const wgslFnOpLib = {
+	'^^': 'threejs_xor'
+};
+
 const wgslTypeLib = {
 	float: 'f32',
 	int: 'i32',
@@ -63,15 +67,31 @@ const wgslMethods = {
 	dFdy: '- dpdy',
 	mod: 'threejs_mod',
 	lessThanEqual: 'threejs_lessThanEqual',
-	inversesqrt: 'inverseSqrt'
+	greaterThan: 'threejs_greaterThan',
+	inversesqrt: 'inverseSqrt',
+	bitcast: 'bitcast<f32>'
 };
 
 const wgslPolyfill = {
+	threejs_xor: new CodeNode( `
+fn threejs_xor( a : bool, b : bool ) -> bool {
+
+	return ( a || b ) && !( a && b );
+
+}
+` ),
 	lessThanEqual: new CodeNode( `
 fn threejs_lessThanEqual( a : vec3<f32>, b : vec3<f32> ) -> vec3<bool> {
 
 	return vec3<bool>( a.x <= b.x, a.y <= b.y, a.z <= b.z );
 
+}
+` ),
+	greaterThan: new CodeNode( `
+fn threejs_greaterThan( a : vec3<f32>, b : vec3<f32> ) -> vec3<bool> {
+
+	return vec3<bool>( a.x > b.x, a.y > b.y, a.z > b.z );
+
 }
 ` ),
 	mod: new CodeNode( `
@@ -303,6 +323,22 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 	}
 
+	getFunctionOperator( op ) {
+
+		const fnOp = wgslFnOpLib[ op ];
+
+		if ( fnOp !== undefined ) {
+
+			this._include( fnOp );
+
+			return fnOp;
+
+		}
+
+		return null;
+
+	}
+
 	getUniformFromNode( node, type, shaderStage, name = null ) {
 
 		const uniformNode = super.getUniformFromNode( node, type, shaderStage, name );
@@ -456,7 +492,7 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 	}
 
-	buildFunctionNode( shaderNode ) {
+	buildFunctionCode( shaderNode ) {
 
 		const layout = shaderNode.layout;
 		const flowData = this.flowShaderNode( shaderNode );
@@ -480,7 +516,7 @@ ${ flowData.code }
 
 		//
 
-		return new FunctionNode( code );
+		return code;
 
 	}
 
@@ -926,7 +962,16 @@ ${ flowData.code }
 
 	_include( name ) {
 
-		wgslPolyfill[ name ].build( this );
+		const codeNode = wgslPolyfill[ name ];
+		codeNode.build( this );
+
+		if ( this.currentFunctionNode !== null ) {
+
+			this.currentFunctionNode.includes.push( codeNode );
+
+		}
+
+		return codeNode;
 
 	}
 

+ 40 - 1
examples/jsm/transpiler/AST.js

@@ -27,6 +27,32 @@ export class VariableDeclaration {
 
 }
 
+export class Uniform {
+
+	constructor( type, name ) {
+
+		this.type = type;
+		this.name = name;
+
+		this.isUniform = true;
+
+	}
+
+}
+
+export class Varying {
+
+	constructor( type, name ) {
+
+		this.type = type;
+		this.name = name;
+
+		this.isVarying = true;
+
+	}
+
+}
+
 export class FunctionParameter {
 
 	constructor( type, name, qualifier = null, immutable = true ) {
@@ -125,6 +151,19 @@ export class Number {
 
 }
 
+export class String {
+
+	constructor( value ) {
+
+		this.value = value;
+
+		this.isString = true;
+
+	}
+
+}
+
+
 export class Conditional {
 
 	constructor( cond = null ) {
@@ -228,4 +267,4 @@ export class For {
 
 	}
 
-}
+}

+ 50 - 8
examples/jsm/transpiler/GLSLDecoder.js

@@ -1,4 +1,4 @@
-import { Program, FunctionDeclaration, For, AccessorElements, Ternary, DynamicElement, StaticElement, FunctionParameter, Unary, Conditional, VariableDeclaration, Operator, Number, FunctionCall, Return, Accessor } from './AST.js';
+import { Program, FunctionDeclaration, For, AccessorElements, Ternary, Varying, DynamicElement, StaticElement, FunctionParameter, Unary, Conditional, VariableDeclaration, Operator, Number, String, FunctionCall, Return, Accessor, Uniform } from './AST.js';
 
 const unaryOperators = [
 	'+', '-', '~', '!', '++', '--'
@@ -180,7 +180,7 @@ class Tokenizer {
 
 				const token = new Token( this, parser.type, result[ parser.group || 0 ], this.position );
 
-				this.position += token.str.length;
+				this.position += result[ 0 ].length;
 
 				if ( parser.isTag ) {
 
@@ -218,11 +218,11 @@ class GLSLDecoder {
 
 		this._currentFunction = null;
 
-		this.addKeyword( 'gl_FragCoord', 'vec2 gl_FragCoord = vec2( viewportCoordinate.x, viewportCoordinate.y.oneMinus() );' );
+		this.addPolyfill( 'gl_FragCoord', 'vec2 gl_FragCoord = vec2( viewportCoordinate.x, viewportCoordinate.y.oneMinus() );' );
 
 	}
 
-	addKeyword( name, polyfill ) {
+	addPolyfill( name, polyfill ) {
 
 		this.keywords.push( { name, polyfill } );
 
@@ -406,15 +406,27 @@ class GLSLDecoder {
 
 			let type;
 
-			if ( /^(0x)/.test( firstToken.str ) ) type = 'int';
+			const isHex = /^(0x)/.test( firstToken.str );
+
+			if ( isHex ) type = 'int';
 			else if ( /u$/.test( firstToken.str ) ) type = 'uint';
 			else if ( /f|e|\./.test( firstToken.str ) ) type = 'float';
 			else type = 'int';
 
-			const str = firstToken.str.replace( /u$/, '' );
+			let str = firstToken.str.replace( /u|i$/, '' );
+
+			if ( isHex === false ) {
+
+				str = str.replace( /f$/, '' );
+
+			}
 
 			return new Number( str, type );
 
+		} else if ( firstToken.isString ) {
+
+			return new String( firstToken.str );
+
 		} else if ( firstToken.isLiteral ) {
 
 			if ( firstToken.str === 'return' ) {
@@ -629,6 +641,28 @@ class GLSLDecoder {
 
 	}
 
+	parseUniform() {
+
+		const tokens = this.readTokensUntil( ';' );
+
+		const type = tokens[ 1 ].str;
+		const name = tokens[ 2 ].str;
+
+		return new Uniform( type, name );
+
+	}
+
+	parseVarying() {
+
+		const tokens = this.readTokensUntil( ';' );
+
+		const type = tokens[ 1 ].str;
+		const name = tokens[ 2 ].str;
+
+		return new Varying( type, name );
+
+	}
+
 	parseReturn() {
 
 		this.readToken(); // skip 'return'
@@ -716,7 +750,7 @@ class GLSLDecoder {
 
 		let current = conditional;
 
-		while ( this.getToken().str === 'else' ) {
+		while ( this.getToken() && this.getToken().str === 'else' ) {
 
 			this.readToken(); // skip 'else'
 
@@ -778,6 +812,14 @@ class GLSLDecoder {
 
 					statement = this.parseVariables();
 
+				} else if ( token.str === 'uniform' ) {
+
+					statement = this.parseUniform();
+
+				} else if ( token.str === 'varying' ) {
+
+					statement = this.parseVarying();
+
 				} else if ( isType( token.str ) ) {
 
 					if ( this.getToken( 2 ).str === '(' ) {
@@ -896,4 +938,4 @@ class GLSLDecoder {
 
 }
 
-export default GLSLDecoder;
+export default GLSLDecoder;

+ 4 - 4
examples/jsm/transpiler/ShaderToyDecoder.js

@@ -7,9 +7,9 @@ class ShaderToyDecoder extends GLSLDecoder {
 
 		super();
 
-		this.addKeyword( 'iTime', 'float iTime = timerGlobal();' );
-		this.addKeyword( 'iResolution', 'vec2 iResolution = viewportResolution;' );
-		this.addKeyword( 'fragCoord', 'vec2 fragCoord = vec2( viewportCoordinate.x, viewportResolution.y - viewportCoordinate.y );' );
+		this.addPolyfill( 'iTime', 'float iTime = timerGlobal();' );
+		this.addPolyfill( 'iResolution', 'vec2 iResolution = viewportResolution;' );
+		this.addPolyfill( 'fragCoord', 'vec2 fragCoord = vec2( viewportCoordinate.x, viewportResolution.y - viewportCoordinate.y );' );
 
 	}
 
@@ -46,4 +46,4 @@ class ShaderToyDecoder extends GLSLDecoder {
 
 }
 
-export default ShaderToyDecoder;
+export default ShaderToyDecoder;

+ 121 - 17
examples/jsm/transpiler/TSLEncoder.js

@@ -1,6 +1,6 @@
 import { REVISION } from 'three';
 import { VariableDeclaration, Accessor } from './AST.js';
-import * as Nodes from 'three/nodes';
+import * as Nodes from '../nodes/Nodes.js';
 
 const opLib = {
 	'=': 'assign',
@@ -27,7 +27,7 @@ const opLib = {
 	'*=': 'mulAssign',
 	'/=': 'divAssign',
 	'%=': 'remainderAssign',
-	'^=': 'xorAssign',
+	'^=': 'bitXorAssign',
 	'&=': 'bitAndAssign',
 	'|=': 'bitOrAssign',
 	'<<=': 'shiftLeftAssign',
@@ -51,10 +51,12 @@ class TSLEncoder {
 
 		this.tab = '';
 		this.imports = new Set();
-		this.functions = new Set();
+		this.global = new Set();
+		this.overloadings = new Map();
 		this.layoutsCode = '';
 		this.iife = false;
 		this.uniqueNames = false;
+		this.reference = false;
 
 		this._currentProperties = {};
 		this._lastStatement = null;
@@ -67,7 +69,7 @@ class TSLEncoder {
 
 		name = name.split( '.' )[ 0 ];
 
-		if ( Nodes[ name ] !== undefined && this.functions.has( name ) === false && this._currentProperties[ name ] === undefined ) {
+		if ( Nodes[ name ] !== undefined && this.global.has( name ) === false && this._currentProperties[ name ] === undefined ) {
 
 			this.imports.add( name );
 
@@ -75,10 +77,45 @@ class TSLEncoder {
 
 	}
 
+	emitUniform( node ) {
+
+		let code = `const ${ node.name } = `;
+
+		if ( this.reference === true ) {
+
+			this.addImport( 'reference' );
+
+			this.global.add( node.name );
+
+			//code += `reference( '${ node.name }', '${ node.type }', uniforms )`;
+
+			// legacy
+			code += `reference( 'value', '${ node.type }', uniforms[ '${ node.name }' ] )`;
+
+		} else {
+
+			this.addImport( 'uniform' );
+
+			this.global.add( node.name );
+
+			code += `uniform( '${ node.type }' )`;
+
+		}
+
+		return code;
+
+	}
+
 	emitExpression( node ) {
 
 		let code;
 
+		/*@TODO: else if ( node.isVarying ) {
+
+			code = this.emitVarying( node );
+
+		}*/
+
 		if ( node.isAccessor ) {
 
 			this.addImport( node.property );
@@ -99,6 +136,10 @@ class TSLEncoder {
 
 			}
 
+		} else if ( node.isString ) {
+
+			code = '\'' + node.value + '\'';
+
 		} else if ( node.isOperator ) {
 
 			const opFn = opLib[ node.type ] || node.type;
@@ -194,6 +235,10 @@ class TSLEncoder {
 
 			code = this.emitVariables( node );
 
+		} else if ( node.isUniform ) {
+
+			code = this.emitUniform( node );
+
 		} else if ( node.isTernary ) {
 
 			code = this.emitTernary( node );
@@ -204,7 +249,7 @@ class TSLEncoder {
 
 		} else if ( node.isUnary && node.expression.isNumber ) {
 
-			code = node.type + node.expression.value;
+			code = node.type + ' ' + node.expression.value;
 
 		} else if ( node.isUnary ) {
 
@@ -232,7 +277,7 @@ class TSLEncoder {
 
 		} else {
 
-			console.error( 'Unknown node type', node );
+			console.warn( 'Unknown node type', node );
 
 		}
 
@@ -291,7 +336,7 @@ class TSLEncoder {
 
 		let ifStr = `If( ${ condStr }, () => {
 
-${ bodyStr } 
+${ bodyStr }
 
 ${ this.tab }} )`;
 
@@ -305,7 +350,7 @@ ${ this.tab }} )`;
 
 				const elseCondStr = this.emitExpression( current.elseConditional.cond );
 
-				ifStr += `.elseif( ( ${ elseCondStr } ) => {
+				ifStr += `.elseif( ${ elseCondStr }, () => {
 
 ${ elseBodyStr }
 
@@ -449,6 +494,18 @@ ${ this.tab }} )`;
 
 	}
 
+	/*emitVarying( node ) { }*/
+
+	emitOverloadingFunction( nodes ) {
+
+		const { name } = nodes[ 0 ];
+
+		this.addImport( 'overloadingFn' );
+
+		return `const ${ name } = overloadingFn( [ ${ nodes.map( node => node.name + '_' + nodes.indexOf( node ) ).join( ', ' ) } ] );\n`;
+
+	}
+
 	emitFunction( node ) {
 
 		const { name, type } = node;
@@ -503,7 +560,30 @@ ${ this.tab }} )`;
 		const paramsStr = params.length > 0 ? ' [ ' + params.join( ', ' ) + ' ] ' : '';
 		const bodyStr = this.emitBody( node.body );
 
-		const funcStr = `const ${ name } = tslFn( (${ paramsStr }) => {
+		let fnName = name;
+		let overloadingNodes = null;
+
+		if ( this.overloadings.has( name ) ) {
+
+			const overloadings = this.overloadings.get( name );
+
+			if ( overloadings.length > 1 ) {
+
+				const index = overloadings.indexOf( node );
+
+				fnName += '_' + index;
+
+				if ( index === overloadings.length - 1 ) {
+
+					overloadingNodes = overloadings;
+
+				}
+
+			}
+
+		}
+
+		let funcStr = `const ${ fnName } = tslFn( (${ paramsStr }) => {
 
 ${ bodyStr }
 
@@ -513,9 +593,9 @@ ${ this.tab }} );\n`;
 
 		if ( node.layout !== false && hasPointer === false ) {
 
-			const uniqueName = this.uniqueNames ? name + '_' + Math.random().toString( 36 ).slice( 2 ) : name;
+			const uniqueName = this.uniqueNames ? fnName + '_' + Math.random().toString( 36 ).slice( 2 ) : fnName;
 
-			this.layoutsCode += `${ this.tab + name }.setLayout( {
+			this.layoutsCode += `${ this.tab + fnName }.setLayout( {
 ${ this.tab }\tname: '${ uniqueName }',
 ${ this.tab }\ttype: '${ type }',
 ${ this.tab }\tinputs: [${ layoutInput }]
@@ -525,7 +605,13 @@ ${ this.tab }} );\n\n`;
 
 		this.imports.add( 'tslFn' );
 
-		this.functions.add( node.name );
+		this.global.add( node.name );
+
+		if ( overloadingNodes !== null ) {
+
+			funcStr += '\n' + this.emitOverloadingFunction( overloadingNodes );
+
+		}
 
 		return funcStr;
 
@@ -560,6 +646,24 @@ ${ this.tab }} );\n\n`;
 
 		if ( this.iife ) this.tab += '\t';
 
+		const overloadings = this.overloadings;
+
+		for ( const statement of ast.body ) {
+
+			if ( statement.isFunctionDeclaration ) {
+
+				if ( overloadings.has( statement.name ) === false ) {
+
+					overloadings.set( statement.name, [] );
+
+				}
+
+				overloadings.get( statement.name ).push( statement );
+
+			}
+
+		}
+
 		for ( const statement of ast.body ) {
 
 			code += this.emitExtraLine( statement );
@@ -579,7 +683,7 @@ ${ this.tab }} );\n\n`;
 		}
 
 		const imports = [ ...this.imports ];
-		const functions = [ ...this.functions ];
+		const exports = [ ...this.global ];
 
 		const layouts = this.layoutsCode.length > 0 ? `\n${ this.tab }// layouts\n\n` + this.layoutsCode : '';
 
@@ -588,17 +692,17 @@ ${ this.tab }} );\n\n`;
 
 		if ( this.iife ) {
 
-			header += '( function ( TSL ) {\n\n';
+			header += '( function ( TSL, uniforms ) {\n\n';
 
 			header += imports.length > 0 ? '\tconst { ' + imports.join( ', ' ) + ' } = TSL;\n' : '';
-			footer += functions.length > 0 ? '\treturn { ' + functions.join( ', ' ) + ' };\n' : '';
+			footer += exports.length > 0 ? '\treturn { ' + exports.join( ', ' ) + ' };\n' : '';
 
 			footer += '\n} );';
 
 		} else {
 
 			header += imports.length > 0 ? 'import { ' + imports.join( ', ' ) + ' } from \'three/nodes\';\n' : '';
-			footer += functions.length > 0 ? 'export { ' + functions.join( ', ' ) + ' };\n' : '';
+			footer += exports.length > 0 ? 'export { ' + exports.join( ', ' ) + ' };\n' : '';
 
 		}
 
@@ -608,4 +712,4 @@ ${ this.tab }} );\n\n`;
 
 }
 
-export default TSLEncoder;
+export default TSLEncoder;

二進制
examples/screenshots/webgpu_loader_materialx.jpg


二進制
examples/screenshots/webgpu_materialx_noise.jpg


+ 192 - 0
examples/webgpu_loader_materialx.html

@@ -0,0 +1,192 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - MaterialX loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+		<style>
+			.dg .property-name {
+				width: 20% !important;
+			}
+		</style>
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - MaterialXLoader<br />
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/",
+					"three/nodes": "./jsm/nodes/Nodes.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { MaterialXLoader } from './jsm/loaders/MaterialXLoader.js';
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
+			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
+
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+
+			import { nodeFrame } from './jsm/renderers/webgl-legacy/nodes/WebGLNodes.js';
+
+			const SAMPLE_PATH = 'https://raw.githubusercontent.com/materialx/MaterialX/main/resources/Materials/Examples/StandardSurface/';
+
+			const samples = [
+				'standard_surface_brass_tiled.mtlx',
+				//'standard_surface_brick_procedural.mtlx',
+				'standard_surface_carpaint.mtlx',
+				//'standard_surface_chess_set.mtlx',
+				'standard_surface_chrome.mtlx',
+				'standard_surface_copper.mtlx',
+				//'standard_surface_default.mtlx',
+				//'standard_surface_glass.mtlx',
+				//'standard_surface_glass_tinted.mtlx',
+				'standard_surface_gold.mtlx',
+				'standard_surface_greysphere.mtlx',
+				//'standard_surface_greysphere_calibration.mtlx',
+				'standard_surface_jade.mtlx',
+				//'standard_surface_look_brass_tiled.mtlx',
+				//'standard_surface_look_wood_tiled.mtlx',
+				'standard_surface_marble_solid.mtlx',
+				'standard_surface_metal_brushed.mtlx',
+				'standard_surface_plastic.mtlx',
+				//'standard_surface_thin_film.mtlx',
+				'standard_surface_velvet.mtlx',
+				'standard_surface_wood_tiled.mtlx'
+			];
+
+			let camera, scene, renderer, prefab;
+			const models = [];
+
+			init();
+
+			function init() {
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 50 );
+				camera.position.set( 0, 3, 20 );
+
+				scene = new THREE.Scene();
+
+				renderer = new WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.toneMapping = THREE.LinearToneMapping;
+				renderer.toneMappingExposure = .5;
+				renderer.setAnimationLoop( render );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.minDistance = 2;
+				controls.maxDistance = 40;
+
+				//
+
+				new RGBELoader()
+					.setPath( 'textures/equirectangular/' )
+					.load( 'san_giuseppe_bridge_2k.hdr', async ( texture ) => {
+
+						texture.mapping = THREE.EquirectangularReflectionMapping;
+
+						scene.background = texture;
+						scene.environment = texture;
+
+						prefab = ( await new GLTFLoader().loadAsync( './models/gltf/ShaderBall.glb' ) ).scene;
+
+						for ( const sample of samples ) {
+
+							addSample( sample );
+
+						}
+
+					} );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function updateModelsAlign() {
+
+				const COLUMN_COUNT = 6;
+				const DIST_X = 3;
+				const DIST_Y = 4;
+
+				const lineCount = Math.floor( models.length / COLUMN_COUNT ) - 1.5;
+
+				const offsetX = ( DIST_X * ( COLUMN_COUNT - 1 ) ) * - .5;
+				const offsetY = ( DIST_Y * lineCount ) * .5;
+
+				for ( let i = 0; i < models.length; i ++ ) {
+
+					const model = models[ i ];
+
+					model.position.x = ( ( i % COLUMN_COUNT ) * DIST_X ) + offsetX;
+					model.position.y = ( Math.floor( i / COLUMN_COUNT ) * - DIST_Y ) + offsetY;
+
+				}
+
+			}
+
+			async function addSample( sample ) {
+
+				const model = prefab.clone();
+
+				models.push( model );
+
+				scene.add( model );
+
+				updateModelsAlign();
+
+				//
+
+				const material = await new MaterialXLoader()
+					.setPath( SAMPLE_PATH )
+					.loadAsync( sample )
+					.then( ( { materials } ) => Object.values( materials ).pop() );
+
+				const calibrationMesh = model.getObjectByName( 'Calibration_Mesh' );
+				calibrationMesh.material = material;
+
+				const Preview_Mesh = model.getObjectByName( 'Preview_Mesh' );
+				Preview_Mesh.material = material;
+
+			}
+
+			//
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function render() {
+
+				nodeFrame.update();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 210 - 0
examples/webgpu_materialx_noise.html

@@ -0,0 +1,210 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - materials - materialx nodes</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - MaterialX - Noise
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/",
+					"three/nodes": "./jsm/nodes/Nodes.js"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { MeshPhysicalNodeMaterial, normalWorld, timerLocal, mx_noise_vec3, mx_worley_noise_vec3, mx_cell_noise_float, mx_fractal_noise_vec3 } from 'three/nodes';
+
+			import Stats from 'three/addons/libs/stats.module.js';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { HDRCubeTextureLoader } from 'three/addons/loaders/HDRCubeTextureLoader.js';
+
+			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
+
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
+			import WebGL from 'three/addons/capabilities/WebGL.js';
+
+			let container, stats;
+
+			let camera, scene, renderer;
+
+			let particleLight;
+			let group;
+
+			init();
+
+			function init() {
+
+				if ( WebGPU.isAvailable() === false && WebGL.isWebGL2Available() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw new Error( 'No WebGPU or WebGL2 support' );
+
+				}
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 100;
+
+				scene = new THREE.Scene();
+
+				group = new THREE.Group();
+				scene.add( group );
+
+				new HDRCubeTextureLoader()
+					.setPath( 'textures/cube/pisaHDR/' )
+					.load( [ 'px.hdr', 'nx.hdr', 'py.hdr', 'ny.hdr', 'pz.hdr', 'nz.hdr' ],
+						function ( hdrTexture ) {
+
+							const geometry = new THREE.SphereGeometry( 8, 64, 32 );
+
+							const offsetNode = timerLocal();
+							const customUV = normalWorld.mul( 10 ).add( offsetNode );
+
+							// left top
+
+							let material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_noise_vec3( customUV );
+
+							let mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = - 10;
+							mesh.position.y = 10;
+							group.add( mesh );
+
+							// right top
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_cell_noise_float( customUV );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = 10;
+							mesh.position.y = 10;
+							group.add( mesh );
+
+							// left bottom
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_worley_noise_vec3( customUV );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = - 10;
+							mesh.position.y = - 10;
+							group.add( mesh );
+
+							// right bottom
+
+							material = new MeshPhysicalNodeMaterial();
+							material.colorNode = mx_fractal_noise_vec3( customUV.mul( .2 ) );
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.position.x = 10;
+							mesh.position.y = - 10;
+							group.add( mesh );
+
+							//
+
+							scene.background = hdrTexture;
+							scene.environment = hdrTexture;
+
+						}
+
+					);
+
+				// LIGHTS
+
+				particleLight = new THREE.Mesh(
+					new THREE.SphereGeometry( 0.4, 8, 8 ),
+					new THREE.MeshBasicMaterial( { color: 0xffffff } )
+				);
+				scene.add( particleLight );
+
+				particleLight.add( new THREE.PointLight( 0xffffff, 1000 ) );
+
+				renderer = new WebGPURenderer( { antialias: true } );
+				renderer.setAnimationLoop( animate );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = 1.25;
+
+				//
+
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				// EVENTS
+
+				new OrbitControls( camera, renderer.domElement );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			//
+
+			function onWindowResize() {
+
+				const width = window.innerWidth;
+				const height = window.innerHeight;
+
+				camera.aspect = width / height;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( width, height );
+
+			}
+
+			//
+
+			function animate() {
+
+				render();
+
+				stats.update();
+
+			}
+
+			function render() {
+
+				const timer = Date.now() * 0.00025;
+
+				particleLight.position.x = Math.sin( timer * 7 ) * 30;
+				particleLight.position.y = Math.cos( timer * 5 ) * 40;
+				particleLight.position.z = Math.cos( timer * 3 ) * 30;
+
+				for ( let i = 0; i < group.children.length; i ++ ) {
+
+					const child = group.children[ i ];
+					child.rotation.y += 0.005;
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 6 - 0
test/e2e/puppeteer.js

@@ -89,7 +89,9 @@ const exceptionList = [
 	'webgl_materials_blending',
 	'webgl_mirror',
 	'webgl_morphtargets_face',
+	'webgl_nodes_loader_materialx',
 	'webgl_nodes_materials_standard',
+	'webgl_nodes_materialx_noise',
 	'webgl_postprocessing_crossfade',
 	'webgl_postprocessing_dof2',
 	'webgl_raymarching_reflect',
@@ -126,9 +128,13 @@ const exceptionList = [
 
 	// WebGPURenderer: Unknown problem
 	'webgpu_camera_logarithmicdepthbuffer',
+	'webgpu_loader_materialx',
 	'webgpu_materials_video',
+	'webgpu_materialx_noise',
+	'webgpu_occlusion',
 	'webgpu_particles',
 	'webgpu_shadertoy',
+	'webgpu_shadowmap',
 	'webgpu_tsl_editor',
 	'webgpu_tsl_transpiler'