Mr.doob 1 year ago
parent
commit
4f8101b415
100 changed files with 3186 additions and 1268 deletions
  1. 29 28
      build/three.cjs
  2. 29 28
      build/three.js
  3. 0 0
      build/three.min.js
  4. 29 28
      build/three.module.js
  5. 0 0
      build/three.module.min.js
  6. 2 1
      docs/api/ar/constants/Renderer.html
  7. 2 2
      docs/api/ar/materials/ShaderMaterial.html
  8. 3 0
      docs/api/ar/math/Sphere.html
  9. 1 2
      docs/api/ar/objects/SkinnedMesh.html
  10. 2 1
      docs/api/en/constants/Renderer.html
  11. 4 3
      docs/api/en/materials/Material.html
  12. 2 2
      docs/api/en/materials/ShaderMaterial.html
  13. 3 0
      docs/api/en/math/Sphere.html
  14. 2 2
      docs/api/en/math/Triangle.html
  15. 1 2
      docs/api/en/objects/SkinnedMesh.html
  16. 2 1
      docs/api/fr/constants/Renderer.html
  17. 2 1
      docs/api/fr/materials/ShaderMaterial.html
  18. 2 1
      docs/api/it/constants/Renderer.html
  19. 2 1
      docs/api/it/materials/ShaderMaterial.html
  20. 5 0
      docs/api/it/math/Sphere.html
  21. 1 2
      docs/api/it/objects/SkinnedMesh.html
  22. 2 0
      docs/api/ko/constants/Renderer.html
  23. 2 1
      docs/api/pt-br/constants/Renderer.html
  24. 2 0
      docs/api/zh/constants/Renderer.html
  25. 2 1
      docs/api/zh/materials/ShaderMaterial.html
  26. 1 1
      docs/api/zh/math/Quaternion.html
  27. 5 0
      docs/api/zh/math/Sphere.html
  28. 1 1
      docs/api/zh/objects/SkinnedMesh.html
  29. 4 4
      docs/api/zh/renderers/WebGLRenderer.html
  30. 5 5
      docs/examples/en/loaders/3DMLoader.html
  31. 109 0
      docs/examples/en/misc/Timer.html
  32. 1 1
      docs/examples/en/postprocessing/EffectComposer.html
  33. 1 1
      docs/examples/en/utils/SceneUtils.html
  34. 1 1
      docs/examples/zh/postprocessing/EffectComposer.html
  35. 1 1
      docs/examples/zh/utils/SceneUtils.html
  36. 4 0
      docs/list.json
  37. 1 0
      docs/manual/ar/introduction/Libraries-and-Plugins.html
  38. 1 0
      docs/manual/en/introduction/Libraries-and-Plugins.html
  39. 1 0
      docs/manual/fr/introduction/Libraries-and-Plugins.html
  40. 1 0
      docs/manual/it/introduction/Libraries-and-Plugins.html
  41. 1 0
      docs/manual/ja/introduction/Libraries-and-Plugins.html
  42. 1 0
      docs/manual/pt-br/introduction/Libraries-and-Plugins.html
  43. 1 0
      docs/manual/ru/introduction/Libraries-and-Plugins.html
  44. 1 1
      docs/manual/zh/introduction/Libraries-and-Plugins.html
  45. 1 1
      editor/js/Script.js
  46. 8 11
      editor/js/Sidebar.Material.BooleanProperty.js
  47. 9 12
      editor/js/Sidebar.Material.ColorProperty.js
  48. 8 11
      editor/js/Sidebar.Material.ConstantProperty.js
  49. 12 10
      editor/js/Sidebar.Material.MapProperty.js
  50. 8 11
      editor/js/Sidebar.Material.NumberProperty.js
  51. 7 10
      editor/js/Sidebar.Material.Program.js
  52. 8 11
      editor/js/Sidebar.Material.RangeValueProperty.js
  53. 5 1
      editor/js/Sidebar.Material.js
  54. 2 1
      editor/js/Sidebar.Project.Renderer.js
  55. 4 2
      editor/js/commands/SetMaterialColorCommand.js
  56. 4 2
      editor/js/commands/SetMaterialCommand.js
  57. 4 2
      editor/js/commands/SetMaterialMapCommand.js
  58. 4 2
      editor/js/commands/SetMaterialRangeCommand.js
  59. 4 2
      editor/js/commands/SetMaterialValueCommand.js
  60. 4 2
      editor/js/commands/SetMaterialVectorCommand.js
  61. 8 1
      examples/files.json
  62. 50 38
      examples/jsm/controls/OrbitControls.js
  63. 52 47
      examples/jsm/csm/CSMShader.js
  64. 23 8
      examples/jsm/exporters/USDZExporter.js
  65. 16 33
      examples/jsm/lines/LineMaterial.js
  66. 4 3
      examples/jsm/loaders/DRACOLoader.js
  67. 2 2
      examples/jsm/loaders/GLTFLoader.js
  68. 73 54
      examples/jsm/loaders/MaterialXLoader.js
  69. 119 0
      examples/jsm/misc/Timer.js
  70. 20 2
      examples/jsm/modifiers/CurveModifier.js
  71. 4 1
      examples/jsm/nodes/Nodes.js
  72. 7 2
      examples/jsm/nodes/accessors/CubeTextureNode.js
  73. 22 15
      examples/jsm/nodes/accessors/TextureNode.js
  74. 3 3
      examples/jsm/nodes/code/CodeNode.js
  75. 6 0
      examples/jsm/nodes/core/AttributeNode.js
  76. 4 1
      examples/jsm/nodes/core/CacheNode.js
  77. 6 1
      examples/jsm/nodes/core/Node.js
  78. 29 6
      examples/jsm/nodes/core/NodeBuilder.js
  79. 2 0
      examples/jsm/nodes/core/VarNode.js
  80. 2 2
      examples/jsm/nodes/display/ColorAdjustmentNode.js
  81. 170 0
      examples/jsm/nodes/display/GaussianBlurNode.js
  82. 182 0
      examples/jsm/nodes/display/PassNode.js
  83. 1 1
      examples/jsm/nodes/functions/PhysicalLightingModel.js
  84. 7 1
      examples/jsm/nodes/lighting/LightsNode.js
  85. 7 2
      examples/jsm/nodes/materials/NodeMaterial.js
  86. 10 10
      examples/jsm/nodes/materialx/MaterialXNodes.js
  87. 128 54
      examples/jsm/nodes/materialx/lib/mx_hsv.js
  88. 1430 618
      examples/jsm/nodes/materialx/lib/mx_noise.js
  89. 24 14
      examples/jsm/nodes/materialx/lib/mx_transform_color.js
  90. 2 0
      examples/jsm/nodes/math/MathNode.js
  91. 19 6
      examples/jsm/nodes/math/OperatorNode.js
  92. 32 3
      examples/jsm/nodes/shadernode/ShaderNode.js
  93. 95 0
      examples/jsm/nodes/utils/FunctionOverloadingNode.js
  94. 2 2
      examples/jsm/nodes/utils/RemapNode.js
  95. 8 2
      examples/jsm/nodes/utils/SplitNode.js
  96. 3 3
      examples/jsm/nodes/utils/TriplanarTexturesNode.js
  97. 60 0
      examples/jsm/objects/QuadMesh.js
  98. 180 109
      examples/jsm/postprocessing/GTAOPass.js
  99. 2 0
      examples/jsm/postprocessing/OutputPass.js
  100. 8 10
      examples/jsm/renderers/common/Background.js

File diff suppressed because it is too large
+ 29 - 28
build/three.cjs


File diff suppressed because it is too large
+ 29 - 28
build/three.js


File diff suppressed because it is too large
+ 0 - 0
build/three.min.js


File diff suppressed because it is too large
+ 29 - 28
build/three.module.js


File diff suppressed because it is too large
+ 0 - 0
build/three.module.min.js


+ 2 - 1
docs/api/ar/constants/Renderer.html

@@ -50,13 +50,14 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping 
 		THREE.CineonToneMapping 
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
 		THREE.CustomToneMapping
 		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
 			هذه الخيارات تحدد خاصية [page:WebGLRenderer.toneMapping toneMapping] في WebGLRenderer. يتم استخدام هذا لتقريب مظهر نطاق الإضاءة العالي (HDR) على الوسط الذي يحتوي على نطاق إضاءة منخفض على شاشة الكمبيوتر القياسية أو شاشة الجوال.
 			هذه الخيارات تحدد خاصية [page:WebGLRenderer.toneMapping toneMapping] في WebGLRenderer. يتم استخدام هذا لتقريب مظهر نطاق الإضاءة العالي (HDR) على الوسط الذي يحتوي على نطاق إضاءة منخفض على شاشة الكمبيوتر القياسية أو شاشة الجوال.
 		</p>
 		</p>
 		<p>
 		<p>
-			THREE.LinearToneMapping، THREE.ReinhardToneMapping، THREE.CineonToneMapping و THREE.ACESFilmicToneMapping هي تنفيذات مدمجة لتقريب مظهر نطاق الإضاءة العالي (HDR). يتوقع THREE.CustomToneMapping تنفيذًا مخصصًا عن طريق تعديل شفرة GLSL لبرنامج تظليل مقطع المواد. راجع [example:webgl_tonemapping WebGL / tonemapping] مثالًا. 
+			THREE.LinearToneMapping، THREE.ReinhardToneMapping، THREE.CineonToneMapping، THREE.ACESFilmicToneMapping و THREE.AgXToneMapping هي تنفيذات مدمجة لتقريب مظهر نطاق الإضاءة العالي (HDR). يتوقع THREE.CustomToneMapping تنفيذًا مخصصًا عن طريق تعديل شفرة GLSL لبرنامج تظليل مقطع المواد. راجع [example:webgl_tonemapping WebGL / tonemapping] مثالًا. 
 		</p>
 		</p>
 
 
 		<h2>المصدر (Source)</h2>
 		<h2>المصدر (Source)</h2>

+ 2 - 2
docs/api/ar/materials/ShaderMaterial.html

@@ -327,8 +327,8 @@ this.extensions = {
 	derivatives: false, // set to use derivatives
 	derivatives: false, // set to use derivatives
 	fragDepth: false, // set to use fragment depth values 
 	fragDepth: false, // set to use fragment depth values 
 	drawBuffers: false, // set to use draw buffers 
 	drawBuffers: false, // set to use draw buffers 
-	shaderTextureLOD: false // set to use
-	shader texture LOD 
+	shaderTextureLOD: false, // set to use shader texture LOD
+	clipCullDistance: false // set to use vertex shader clipping
 };
 };
 			</code>
 			</code>
 		</p>
 		</p>

+ 3 - 0
docs/api/ar/math/Sphere.html

@@ -28,6 +28,9 @@
 		[page:Vector3] يحدد مركز الكرة. الافتراضي هو `(0، 0،
 		[page:Vector3] يحدد مركز الكرة. الافتراضي هو `(0، 0،
 		0)`.
 		0)`.
 		</p>
 		</p>
+
+		<h3>[property:Boolean isSphere]</h3>
+		<p>علامة للقراءة فقط للتحقق مما إذا كان كائنًا معينًا من نوع [name].</p>
 	 
 	 
 		<h3>[property:Float radius]</h3>
 		<h3>[property:Float radius]</h3>
 		<p>نصف قطر الكرة. الافتراضي هو -1.</p>
 		<p>نصف قطر الكرة. الافتراضي هو -1.</p>

+ 1 - 2
docs/api/ar/objects/SkinnedMesh.html

@@ -15,8 +15,7 @@
 		شبكة لديها [page:Skeleton] مع [page:Bone bones] يمكن استخدامها بعد ذلك
 		شبكة لديها [page:Skeleton] مع [page:Bone bones] يمكن استخدامها بعد ذلك
 		لتحريك رؤوس الهندسة.<br /> <br />
 		لتحريك رؤوس الهندسة.<br /> <br />
 			
 			
-		يمكن استخدام [name] فقط مع WebGL 2. مع WebGL 1 `OES_texture_float` و
-		مطلوب دعم نسيج الرأس.
+		يمكن استخدام [name] فقط مع WebGL 2.
 		</p>
 		</p>
 			
 			
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>

+ 2 - 1
docs/api/en/constants/Renderer.html

@@ -52,6 +52,7 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping 
 		THREE.CineonToneMapping 
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
 		THREE.CustomToneMapping
 		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
@@ -61,7 +62,7 @@
 		</p>
 		</p>
 		<p>
 		<p>
 			THREE.LinearToneMapping, THREE.ReinhardToneMapping,
 			THREE.LinearToneMapping, THREE.ReinhardToneMapping,
-			THREE.CineonToneMapping and THREE.ACESFilmicToneMapping are built-in
+			THREE.CineonToneMapping, THREE.ACESFilmicToneMapping, and THREE.AgXToneMapping are built-in
 			implementations of tone mapping. THREE.CustomToneMapping expects a custom
 			implementations of tone mapping. THREE.CustomToneMapping expects a custom
 			implementation by modyfing GLSL code of the material's fragment shader.
 			implementation by modyfing GLSL code of the material's fragment shader.
 			See the [example:webgl_tonemapping WebGL / tonemapping] example.
 			See the [example:webgl_tonemapping WebGL / tonemapping] example.

+ 4 - 3
docs/api/en/materials/Material.html

@@ -338,8 +338,9 @@
 
 
 		<h3>[property:Boolean toneMapped]</h3>
 		<h3>[property:Boolean toneMapped]</h3>
 		<p>
 		<p>
-			Defines whether this material is tone mapped according to the renderer's
-			[page:WebGLRenderer.toneMapping toneMapping] setting. Default is `true`.
+			Defines whether this material is tone mapped according to the renderer's			
+			[page:WebGLRenderer.toneMapping toneMapping] setting. It is ignored when rendering to a render target.
+			Default is `true`.
 		</p>
 		</p>
 
 
 		<h3>[property:Boolean transparent]</h3>
 		<h3>[property:Boolean transparent]</h3>
@@ -348,7 +349,7 @@
 			rendering as transparent objects need special treatment and are rendered
 			rendering as transparent objects need special treatment and are rendered
 			after non-transparent objects. <br />
 			after non-transparent objects. <br />
 			When set to true, the extent to which the material is transparent is
 			When set to true, the extent to which the material is transparent is
-			controlled by setting its [page:Float opacity] property. <br />
+			controlled by setting its [page:Float opacity] property.
 			Default is `false`.
 			Default is `false`.
 		</p>
 		</p>
 
 

+ 2 - 2
docs/api/en/materials/ShaderMaterial.html

@@ -345,8 +345,8 @@ this.extensions = {
 	derivatives: false, // set to use derivatives
 	derivatives: false, // set to use derivatives
 	fragDepth: false, // set to use fragment depth values 
 	fragDepth: false, // set to use fragment depth values 
 	drawBuffers: false, // set to use draw buffers 
 	drawBuffers: false, // set to use draw buffers 
-	shaderTextureLOD: false // set to use
-	shader texture LOD 
+	shaderTextureLOD: false, // set to use shader texture LOD
+	clipCullDistance: false // set to use vertex shader clipping
 };
 };
 			</code>
 			</code>
 		</p>
 		</p>

+ 3 - 0
docs/api/en/math/Sphere.html

@@ -29,6 +29,9 @@
 			0)`.
 			0)`.
 		</p>
 		</p>
 
 
+		<h3>[property:Boolean isSphere]</h3>
+		<p>Read-only flag to check if a given object is of type [name].</p>
+
 		<h3>[property:Float radius]</h3>
 		<h3>[property:Float radius]</h3>
 		<p>The radius of the sphere. Default is -1.</p>
 		<p>The radius of the sphere. Default is -1.</p>
 
 

+ 2 - 2
docs/api/en/math/Triangle.html

@@ -97,7 +97,7 @@
 			[page:Vector3 target] — the result will be copied into this Vector3.<br /><br />
 			[page:Vector3 target] — the result will be copied into this Vector3.<br /><br />
 
 
 			Return a [link:https://en.wikipedia.org/wiki/Barycentric_coordinate_system barycentric coordinate] 
 			Return a [link:https://en.wikipedia.org/wiki/Barycentric_coordinate_system barycentric coordinate] 
-			from the given vector. <br /><br />
+			from the given vector. Returns `null` if the triangle is degenerate.<br /><br />
 
 
 			[link:http://commons.wikimedia.org/wiki/File:Barycentric_coordinates_1.png Picture of barycentric coordinates]
 			[link:http://commons.wikimedia.org/wiki/File:Barycentric_coordinates_1.png Picture of barycentric coordinates]
 		</p>
 		</p>
@@ -137,7 +137,7 @@
 			[page:Vector target] — Result will be copied into this Vector.<br /><br />
 			[page:Vector target] — Result will be copied into this Vector.<br /><br />
 
 
 			Returns the value barycentrically interpolated for the given point on the
 			Returns the value barycentrically interpolated for the given point on the
-			triangle.
+			triangle. Returns `null` if the triangle is degenerate.
 		</p>
 		</p>
 
 
 		<h3>[method:Boolean intersectsBox]( [param:Box3 box] )</h3>
 		<h3>[method:Boolean intersectsBox]( [param:Box3 box] )</h3>

+ 1 - 2
docs/api/en/objects/SkinnedMesh.html

@@ -15,8 +15,7 @@
 			A mesh that has a [page:Skeleton] with [page:Bone bones] that can then be
 			A mesh that has a [page:Skeleton] with [page:Bone bones] that can then be
 			used to animate the vertices of the geometry.<br /><br />
 			used to animate the vertices of the geometry.<br /><br />
 
 
-			[name] can only be used with WebGL 2. With WebGL 1 `OES_texture_float` and
-			vertex textures support is required.
+			[name] can only be used with WebGL 2.
 		</p>
 		</p>
 
 
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>

+ 2 - 1
docs/api/fr/constants/Renderer.html

@@ -46,6 +46,7 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping
 		THREE.CineonToneMapping
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
 		THREE.CustomToneMapping
 		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
@@ -54,7 +55,7 @@
 		milieu de plage dynamique faible d'un écran d'ordinateur standard ou d'un écran d'appareil mobile.
 		milieu de plage dynamique faible d'un écran d'ordinateur standard ou d'un écran d'appareil mobile.
 		</p>
 		</p>
 		<p>
 		<p>
-		THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping et THREE.ACESFilmicToneMapping sont des implémentations intégrées à la cartographie des tons.
+		THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping, THREE.ACESFilmicToneMapping et THREE.AgXToneMapping sont des implémentations intégrées à la cartographie des tons.
 		THREE.CustomToneMapping attend une implémentation personnalisée en modifiant le code GLSL du fragment shader du matériau.
 		THREE.CustomToneMapping attend une implémentation personnalisée en modifiant le code GLSL du fragment shader du matériau.
 		Voir l'exemple [example:webgl_tonemapping WebGL / tonemapping].
 		Voir l'exemple [example:webgl_tonemapping WebGL / tonemapping].
 		</p>
 		</p>

+ 2 - 1
docs/api/fr/materials/ShaderMaterial.html

@@ -316,7 +316,8 @@ et la page [page:BufferAttribute] pour un aperçu détaillé de l'API `BufferAtt
 				derivatives: false, // set to use derivatives
 				derivatives: false, // set to use derivatives
 				fragDepth: false, // set to use fragment depth values
 				fragDepth: false, // set to use fragment depth values
 				drawBuffers: false, // set to use draw buffers
 				drawBuffers: false, // set to use draw buffers
-				shaderTextureLOD: false // set to use shader texture LOD
+				shaderTextureLOD: false, // set to use shader texture LOD
+				clipCullDistance: false // set to use vertex shader clipping
 			};
 			};
 		</code>
 		</code>
 		</p>
 		</p>

+ 2 - 1
docs/api/it/constants/Renderer.html

@@ -48,6 +48,7 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping
 		THREE.CineonToneMapping
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
 		THREE.CustomToneMapping
 		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
@@ -56,7 +57,7 @@
       gamma dinamica bassa del monitor di un computer o dello schermo di un dispositivo mobile.
       gamma dinamica bassa del monitor di un computer o dello schermo di un dispositivo mobile.
 		</p>
 		</p>
 		<p>
 		<p>
-      THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping e THREE.ACESFilmicToneMapping sono implementazioni 
+      THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping, THREE.ACESFilmicToneMapping e THREE.AgXToneMapping sono implementazioni 
       integrate della mappatura dei toni.
       integrate della mappatura dei toni.
       THREE.CustomToneMapping prevede un'implementazione personalizzata modificando il codice GLSL dello shader di frammenti del materiale.
       THREE.CustomToneMapping prevede un'implementazione personalizzata modificando il codice GLSL dello shader di frammenti del materiale.
       Vedi l'esempio [example:webgl_tonemapping WebGL / tonemapping].
       Vedi l'esempio [example:webgl_tonemapping WebGL / tonemapping].

+ 2 - 1
docs/api/it/materials/ShaderMaterial.html

@@ -321,7 +321,8 @@ this.extensions = {
 	derivatives: false, // impostato per utilizzare le direttive
 	derivatives: false, // impostato per utilizzare le direttive
 	fragDepth: false, // impostato per utilizzare i valori di profondità del frammento
 	fragDepth: false, // impostato per utilizzare i valori di profondità del frammento
 	drawBuffers: false, // impostato per utilizzare i buffer di disegno
 	drawBuffers: false, // impostato per utilizzare i buffer di disegno
-	shaderTextureLOD: false // impostato per utilizzare la texture dello shader LOD
+	shaderTextureLOD: false, // impostato per utilizzare la texture dello shader LOD
+	clipCullDistance: false // set to use vertex shader clipping
 };
 };
 		</code>
 		</code>
 		</p>
 		</p>

+ 5 - 0
docs/api/it/math/Sphere.html

@@ -28,6 +28,11 @@
 		<h3>[property:Vector3 center]</h3>
 		<h3>[property:Vector3 center]</h3>
 		<p>Un [page:Vector3] che definisce il centro della sfera. Il valore predefinito è `(0, 0, 0)`.</p>
 		<p>Un [page:Vector3] che definisce il centro della sfera. Il valore predefinito è `(0, 0, 0)`.</p>
 
 
+		<h3>[property:Boolean isSphere]</h3>
+		<p>
+			Flag di sola lettura per verificare se l'oggetto dato è di tipo [name].
+		</p>
+
 		<h3>[property:Float radius]</h3>
 		<h3>[property:Float radius]</h3>
 		<p>Il raggio della sfera. Il valore predefinito è -1.</p>
 		<p>Il raggio della sfera. Il valore predefinito è -1.</p>
 
 

+ 1 - 2
docs/api/it/objects/SkinnedMesh.html

@@ -15,8 +15,7 @@
 			Una mesh che ha uno [page:Skeleton scheletro] con [page:Bone ossa] che può essere 
 			Una mesh che ha uno [page:Skeleton scheletro] con [page:Bone ossa] che può essere 
 			utilizzata per animare i vertici della geometria.<br /><br />
 			utilizzata per animare i vertici della geometria.<br /><br />
 
 
-			[name] può essere utilizzata solo con WebGL 2. Con WebGL 1 è necessario il supporto delle texture del vertice
-			e `OES_texture_float`.
+			[name] può essere utilizzata solo con WebGL 2.
 		</p>
 		</p>
 
 
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>

+ 2 - 0
docs/api/ko/constants/Renderer.html

@@ -46,6 +46,8 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping
 		THREE.CineonToneMapping
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
+		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
 		WebGLRenderer의 [page:WebGLRenderer.toneMapping toneMapping] 프로퍼티를 정의합니다.
 		WebGLRenderer의 [page:WebGLRenderer.toneMapping toneMapping] 프로퍼티를 정의합니다.

+ 2 - 1
docs/api/pt-br/constants/Renderer.html

@@ -46,6 +46,7 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping
 		THREE.CineonToneMapping
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
 		THREE.CustomToneMapping
 		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
@@ -54,7 +55,7 @@
 		médio da faixa dinâmica baixa de um monitor de computador padrão ou tela de dispositivo móvel.
 		médio da faixa dinâmica baixa de um monitor de computador padrão ou tela de dispositivo móvel.
 		</p>
 		</p>
 		<p>
 		<p>
-		THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping e THREE.ACESFilmicToneMapping são implementações internas de mapeamento de tom.
+		THREE.LinearToneMapping, THREE.ReinhardToneMapping, THREE.CineonToneMapping, THREE.ACESFilmicToneMapping e THREE.AgXToneMapping são implementações internas de mapeamento de tom.
 		THREE.CustomToneMapping espera uma implementação personalizada modificando o código GLSL do sombreador de fragmento do material.  
 		THREE.CustomToneMapping espera uma implementação personalizada modificando o código GLSL do sombreador de fragmento do material.  
 		Vejo o exemplo [example:webgl_tonemapping WebGL / tonemapping].
 		Vejo o exemplo [example:webgl_tonemapping WebGL / tonemapping].
 		</p>
 		</p>

+ 2 - 0
docs/api/zh/constants/Renderer.html

@@ -46,6 +46,8 @@
 		THREE.ReinhardToneMapping
 		THREE.ReinhardToneMapping
 		THREE.CineonToneMapping
 		THREE.CineonToneMapping
 		THREE.ACESFilmicToneMapping
 		THREE.ACESFilmicToneMapping
+		THREE.AgXToneMapping
+		THREE.CustomToneMapping
 		</code>
 		</code>
 		<p>
 		<p>
 		这些常量定义了WebGLRenderer中[page:WebGLRenderer.toneMapping toneMapping]的属性。
 		这些常量定义了WebGLRenderer中[page:WebGLRenderer.toneMapping toneMapping]的属性。

+ 2 - 1
docs/api/zh/materials/ShaderMaterial.html

@@ -283,7 +283,8 @@ this.extensions = {
 	derivatives: false, // set to use derivatives
 	derivatives: false, // set to use derivatives
 	fragDepth: false, // set to use fragment depth values
 	fragDepth: false, // set to use fragment depth values
 	drawBuffers: false, // set to use draw buffers
 	drawBuffers: false, // set to use draw buffers
-	shaderTextureLOD: false // set to use shader texture LOD
+	shaderTextureLOD: false, // set to use shader texture LOD
+	clipCullDistance: false // set to use vertex shader clipping
 };
 };
 		</code>
 		</code>
 		</p>
 		</p>

+ 1 - 1
docs/api/zh/math/Quaternion.html

@@ -19,7 +19,7 @@
 		</p>
 		</p>
 
 
 		<p>
 		<p>
-			請注意, Three.js 期望四元數標準化。
+			请注意,three.js 期望四元数被归一化。
 		</p>
 		</p>
 
 
 		<h2>代码示例</h2>
 		<h2>代码示例</h2>

+ 5 - 0
docs/api/zh/math/Sphere.html

@@ -28,6 +28,11 @@
 		<h3>[property:Vector3 center]</h3>
 		<h3>[property:Vector3 center]</h3>
 		<p>A [page:Vector3]定义了球心的位置,默认值位于(0, 0, 0)。</p>
 		<p>A [page:Vector3]定义了球心的位置,默认值位于(0, 0, 0)。</p>
 
 
+		<h3>[property:Boolean isSphere]</h3>
+		<p>
+			Read-only flag to check if a given object is of type [name].
+		</p>
+
 		<h3>[property:Float radius]</h3>
 		<h3>[property:Float radius]</h3>
 		<p>球的半径,默认值为-1。</p>
 		<p>球的半径,默认值为-1。</p>
 
 

+ 1 - 1
docs/api/zh/objects/SkinnedMesh.html

@@ -14,7 +14,7 @@
 		<p class="desc">
 		<p class="desc">
 			具有[page:Skeleton](骨架)和[page:Bone bones](骨骼)的网格,可用于给几何体上的顶点添加动画。<br /><br />
 			具有[page:Skeleton](骨架)和[page:Bone bones](骨骼)的网格,可用于给几何体上的顶点添加动画。<br /><br />
 
 
-			[name] can only be used with WebGL 2. With WebGL 1 *OES_texture_float* and vertex textures support is required.
+			[name] can only be used with WebGL 2.
 		</p>
 		</p>
 
 
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>
 		<iframe id="scene" src="scenes/bones-browser.html"></iframe>

+ 4 - 4
docs/api/zh/renderers/WebGLRenderer.html

@@ -351,11 +351,11 @@
 		<p>
 		<p>
 			用相机([page:Camera camera])渲染一个场景([page:Scene scene])或是其它类型的[page:Object3D object]。<br />
 			用相机([page:Camera camera])渲染一个场景([page:Scene scene])或是其它类型的[page:Object3D object]。<br />
 
 
-			渲染一般是在canvas上完成的,或者是[page:WebGLRenderTarget renderTarget](如果有指定)<br />
+			渲染一般是在canvas上完成的,或者是[page:WebGLRenderTarget renderTarget](通过[page:WebGLRenderer.setRenderTarget .setRenderTarget]指定)。<br />
 
 
-			如果[page:Boolean forceClear]值是*true*,那么颜色、深度及模板缓存将会在渲染之前清除,即使渲染器的[page:WebGLRenderer.autoClear autoClear]属性值是false<br />
-
-			即便forceClear设为true, 也可以通过将[page:WebGLRenderer.autoClearColor autoClearColor]、[page:WebGLRenderer.autoClearStencil autoClearStencil]或[page:WebGLRenderer.autoClearDepth autoClearDepth]属性的值设为false来阻止对应缓存被清除
+			默认情况下渲染缓存是会被清除的,但是你可以通过设置[page:WebGLRenderer.autoClear autoClear] 属性的值为false来阻止渲染缓存被清除。
+			如果你想阻止某个指定的缓存被清空,可以设置[page:WebGLRenderer.autoClearColor autoClearColor]、[page:WebGLRenderer.autoClearStencil autoClearStencil]或[page:WebGLRenderer.autoClearDepth autoClearDepth]属性的值为false来阻止其被清除。
+			如果想要强制清除一个或多个缓存,可以调用[page:WebGLRenderer.clear .clear]
 		</p>
 		</p>
 
 
 		<h3>[method:undefined resetState]()</h3>
 		<h3>[method:undefined resetState]()</h3>

+ 5 - 5
docs/examples/en/loaders/3DMLoader.html

@@ -14,7 +14,7 @@
 			A loader for Rhinoceros 3d files and objects. <br /><br />
 			A loader for Rhinoceros 3d files and objects. <br /><br />
 			Rhinoceros is a 3D modeler used to create, edit, analyze, document, render, animate, and translate NURBS curves, surfaces, breps, extrusions, point clouds, as well as polygon meshes and SubD objects.
 			Rhinoceros is a 3D modeler used to create, edit, analyze, document, render, animate, and translate NURBS curves, surfaces, breps, extrusions, point clouds, as well as polygon meshes and SubD objects.
 			[link:https://github.com/mcneel/rhino3dm rhino3dm.js] is compiled to WebAssembly from the open source geometry library [link:https://github.com/mcneel/opennurbs openNURBS].
 			[link:https://github.com/mcneel/rhino3dm rhino3dm.js] is compiled to WebAssembly from the open source geometry library [link:https://github.com/mcneel/opennurbs openNURBS].
-			The loader currently uses [link:https://www.npmjs.com/package/rhino3dm/v/8.0.0-beta2 rhino3dm.js 8.0.0-beta2.]
+			The loader currently uses [link:https://www.npmjs.com/package/rhino3dm/v/8.0.1 rhino3dm.js 8.0.1.]
 		</p>
 		</p>
 
 
 		<h2>Import</h2>
 		<h2>Import</h2>
@@ -166,7 +166,7 @@
 			// Specify path to a folder containing WASM/JS libraries or a CDN.
 			// Specify path to a folder containing WASM/JS libraries or a CDN.
 			// For example, /jsm/libs/rhino3dm/ is the location of the library inside the three.js repository
 			// For example, /jsm/libs/rhino3dm/ is the location of the library inside the three.js repository
 			// loader.setLibraryPath( '/path_to_library/rhino3dm/' );
 			// loader.setLibraryPath( '/path_to_library/rhino3dm/' );
-			loader.setLibraryPath( 'https://unpkg.com/[email protected].0-beta2/' );
+			loader.setLibraryPath( 'https://unpkg.com/[email protected].1/' );
 	
 	
 			// Load a 3DM file
 			// Load a 3DM file
 			loader.load(
 			loader.load(
@@ -205,13 +205,13 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-		import rhino3dm from 'https://unpkg.com/[email protected].0-beta2/'
+		import rhino3dm from 'https://unpkg.com/[email protected].1'
 
 
 		// Instantiate a loader
 		// Instantiate a loader
 		const loader = new Rhino3dmLoader();
 		const loader = new Rhino3dmLoader();
 
 
 		// Specify path to a folder containing WASM/JS libraries or a CDN.
 		// Specify path to a folder containing WASM/JS libraries or a CDN.
-		loader.setLibraryPath( 'https://unpkg.com/[email protected].0-beta2/' );
+		loader.setLibraryPath( 'https://unpkg.com/[email protected].1' );
 
 
 		const rhino = await rhino3dm();
 		const rhino = await rhino3dm();
 		console.log('Loaded rhino3dm.');
 		console.log('Loaded rhino3dm.');
@@ -244,7 +244,7 @@
 		// Specify path to a folder containing the WASM/JS library:
 		// Specify path to a folder containing the WASM/JS library:
 		loader.setLibraryPath( '/path_to_library/rhino3dm/' );
 		loader.setLibraryPath( '/path_to_library/rhino3dm/' );
 		// or from a CDN:
 		// or from a CDN:
-		loader.setLibraryPath( 'https://unpkg.com/[email protected].0-beta2/' );
+		loader.setLibraryPath( 'https://unpkg.com/[email protected].1' );
 		</code>
 		</code>
 
 
 		<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>
 		<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>

+ 109 - 0
docs/examples/en/misc/Timer.html

@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			This class is an alternative to [page:Clock] with a different API design and behavior.
+			The goal is to avoid the conceptual flaws that became apparent in [page:Clock] over time.
+
+			<ul>
+				<li>[name] has an [page:.update]() method that updates its internal state. That makes it possible to call [page:.getDelta]() and [page:.getElapsed]() multiple times per simulation step without getting different values.</li>
+				<li>The class uses the Page Visibility API to avoid large time delta values when the app is inactive (e.g. tab switched or browser hidden).</li>
+			</ul>
+		</p>
+
+		<h2>Import</h2>
+
+		<p>
+			[name] is an add-on, and must be imported explicitly.
+			See [link:#manual/introduction/Installation Installation / Addons].
+		</p>
+
+		<code>
+			import { Timer } from 'three/addons/misc/Timer.js';
+		</code>
+
+		<h2>Code Example</h2>
+
+		<code>
+		const timer = new Timer();
+
+		function animate( timestamp ) {
+
+			requestAnimationFrame( animate );
+
+			// timestamp is optional
+			timer.update( timestamp );
+
+			const delta = timer.getDelta();
+
+			// do something with delta
+
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+		<h2>Examples</h2>
+
+		<p>
+			[example:webgl_morphtargets_sphere WebGL / morphtargets / sphere]
+		</p>
+
+		<h2>Constructor</h2>
+
+		<h3>Timer()</h3>
+
+		<h2>Methods</h2>
+
+		<h3>[method:Number getDelta]()</h3>
+		<p>
+			Returns the time delta in seconds.
+		</p>
+
+		<h3>[method:Number getElapsed]()</h3>
+		<p>
+			Returns the elapsed time in seconds.
+		</p>
+
+		<h3>[method:this setTimescale]( [param:Number timescale] )</h3>
+		<p>
+			Sets a time scale that scales the time delta in [page:.update]().
+		</p>
+
+		<h3>[method:this reset]()</h3>
+		<p>
+			Resets the time computation for the current simulation step.
+		</p>
+
+		<h3>[method:this dispose]()</h3>
+		<p>
+			Can be used to free all internal resources. Usually called when the timer instance isn't required anymore.
+		</p>
+
+		<h3>[method:this update]( [param:Number timestamp] )</h3>
+		<p>
+			timestamp -- (optional) The current time in milliseconds. Can be obtained from the
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame requestAnimationFrame]
+			callback argument. If not provided, the current time will be determined with
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now performance.now].<br /><br />
+
+			Updates the internal state of the timer. This method should be called once per simulation step
+			and before you perform queries against the timer (e.g. via [page:.getDelta]()).
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/misc/Timer.js examples/jsm/misc/Timer.js]
+		</p>
+	</body>
+</html>

+ 1 - 1
docs/examples/en/postprocessing/EffectComposer.html

@@ -39,7 +39,7 @@
 			[example:webgl_postprocessing_fxaa postprocessing fxaa]<br />
 			[example:webgl_postprocessing_fxaa postprocessing fxaa]<br />
 			[example:webgl_postprocessing_glitch postprocessing glitch]<br />
 			[example:webgl_postprocessing_glitch postprocessing glitch]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
-			[example:webgl_postprocessing_hbao postprocessing hbao]<br />
+			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />

+ 1 - 1
docs/examples/en/utils/SceneUtils.html

@@ -21,7 +21,7 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-			import { SceneUtils } from 'three/addons/utils/SceneUtils.js';
+			import * as SceneUtils from 'three/addons/utils/SceneUtils.js';
 		</code>
 		</code>
 
 
 		<h2>Methods</h2>
 		<h2>Methods</h2>

+ 1 - 1
docs/examples/zh/postprocessing/EffectComposer.html

@@ -38,7 +38,7 @@
 			[example:webgl_postprocessing_fxaa postprocessing fxaa]<br />
 			[example:webgl_postprocessing_fxaa postprocessing fxaa]<br />
 			[example:webgl_postprocessing_glitch postprocessing glitch]<br />
 			[example:webgl_postprocessing_glitch postprocessing glitch]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
-			[example:webgl_postprocessing_hbao postprocessing hbao]<br />
+			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />

+ 1 - 1
docs/examples/zh/utils/SceneUtils.html

@@ -19,7 +19,7 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-			import { SceneUtils } from 'three/addons/utils/SceneUtils.js';
+			import * as SceneUtils from 'three/addons/utils/SceneUtils.js';
 		</code>
 		</code>
 
 
 		<h2>方法</h2>
 		<h2>方法</h2>

+ 4 - 0
docs/list.json

@@ -400,6 +400,10 @@
 				"OBB": "examples/en/math/OBB"
 				"OBB": "examples/en/math/OBB"
 			},
 			},
 
 
+			"Misc": {
+				"Timer": "examples/en/misc/Timer"
+			},
+
 			"ConvexHull": {
 			"ConvexHull": {
 				"Face": "examples/en/math/convexhull/Face",
 				"Face": "examples/en/math/convexhull/Face",
 				"HalfEdge": "examples/en/math/convexhull/HalfEdge",
 				"HalfEdge": "examples/en/math/convexhull/HalfEdge",

+ 1 - 0
docs/manual/ar/introduction/Libraries-and-Plugins.html

@@ -21,6 +21,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 			
 			
 		</ul>
 		</ul>
 
 

+ 1 - 0
docs/manual/en/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 			
 			
 		</ul>
 		</ul>
 
 

+ 1 - 0
docs/manual/fr/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 		</ul>
 		</ul>
 
 
 		<h3>Postprocessing</h3>
 		<h3>Postprocessing</h3>

+ 1 - 0
docs/manual/it/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 		</ul>
 		</ul>
 
 
 		<h3>Post-processing</h3>
 		<h3>Post-processing</h3>

+ 1 - 0
docs/manual/ja/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
         <li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
         <li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
         <li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
         <li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
         <li>[link:https://rapier.rs/ rapier]</li>
         <li>[link:https://rapier.rs/ rapier]</li>
+        <li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
     </ul>
     </ul>
 
 
     <h3>Postprocessing(後処理)</h3>
     <h3>Postprocessing(後処理)</h3>

+ 1 - 0
docs/manual/pt-br/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 		</ul>
 		</ul>
 
 
 		<h3>Pós-processamento</h3>
 		<h3>Pós-processamento</h3>

+ 1 - 0
docs/manual/ru/introduction/Libraries-and-Plugins.html

@@ -23,6 +23,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 		</ul>
 		</ul>
 
 
 		<h3>Постобработка</h3>
 		<h3>Постобработка</h3>

+ 1 - 1
docs/manual/zh/introduction/Libraries-and-Plugins.html

@@ -21,7 +21,7 @@
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/kripken/ammo.js/ ammo.js]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://github.com/pmndrs/cannon-es cannon-es]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
 			<li>[link:https://rapier.rs/ rapier]</li>
-			
+			<li>[link:https://github.com/jrouwe/JoltPhysics.js Jolt]</li>
 		</ul>
 		</ul>
 
 
 		<h3>后期处理(Postprocessing)</h3>
 		<h3>后期处理(Postprocessing)</h3>

+ 1 - 1
editor/js/Script.js

@@ -223,7 +223,7 @@ function Script( editor ) {
 
 
 					currentObject.material[ currentScript ] = string;
 					currentObject.material[ currentScript ] = string;
 					currentObject.material.needsUpdate = true;
 					currentObject.material.needsUpdate = true;
-					signals.materialChanged.dispatch( currentObject.material );
+					signals.materialChanged.dispatch( currentObject, 0 ); // TODO: Add multi-material support
 
 
 					const programs = renderer.info.programs;
 					const programs = renderer.info.programs;
 
 

+ 8 - 11
editor/js/Sidebar.Material.BooleanProperty.js

@@ -12,24 +12,28 @@ function SidebarMaterialBooleanProperty( editor, property, name ) {
 	container.add( boolean );
 	container.add( boolean );
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
 
 
 		if ( material[ property ] !== boolean.getValue() ) {
 		if ( material[ property ] !== boolean.getValue() ) {
 
 
-			editor.execute( new SetMaterialValueCommand( editor, object, property, boolean.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialValueCommand( editor, object, property, boolean.getValue(), materialSlot ) );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -46,14 +50,7 @@ function SidebarMaterialBooleanProperty( editor, property, name ) {
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 9 - 12
editor/js/Sidebar.Material.ColorProperty.js

@@ -22,13 +22,14 @@ function SidebarMaterialColorProperty( editor, property, name ) {
 	}
 	}
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
 
 
 		if ( material[ property ].getHex() !== color.getHexValue() ) {
 		if ( material[ property ].getHex() !== color.getHexValue() ) {
 
 
-			editor.execute( new SetMaterialColorCommand( editor, object, property, color.getHexValue(), 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialColorCommand( editor, object, property, color.getHexValue(), materialSlot ) );
 
 
 		}
 		}
 
 
@@ -36,7 +37,7 @@ function SidebarMaterialColorProperty( editor, property, name ) {
 
 
 			if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
 			if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
 
 
-				editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), /* TODO: currentMaterialSlot*/ 0 ) );
+				editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), materialSlot ) );
 
 
 			}
 			}
 
 
@@ -44,12 +45,15 @@ function SidebarMaterialColorProperty( editor, property, name ) {
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -73,14 +77,7 @@ function SidebarMaterialColorProperty( editor, property, name ) {
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 8 - 11
editor/js/Sidebar.Material.ConstantProperty.js

@@ -12,6 +12,7 @@ function SidebarMaterialConstantProperty( editor, property, name, options ) {
 	container.add( constant );
 	container.add( constant );
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
@@ -20,18 +21,21 @@ function SidebarMaterialConstantProperty( editor, property, name, options ) {
 
 
 		if ( material[ property ] !== value ) {
 		if ( material[ property ] !== value ) {
 
 
-			editor.execute( new SetMaterialValueCommand( editor, object, property, value, 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialValueCommand( editor, object, property, value, materialSlot ) );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -48,14 +52,7 @@ function SidebarMaterialConstantProperty( editor, property, name, options ) {
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 12 - 10
editor/js/Sidebar.Material.MapProperty.js

@@ -85,6 +85,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 	}
 	}
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
@@ -103,7 +104,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 			}
 			}
 
 
-			editor.execute( new SetMaterialMapCommand( editor, object, property, newMap, 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialMapCommand( editor, object, property, newMap, materialSlot ) );
 
 
 		}
 		}
 
 
@@ -132,7 +133,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 		if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
 		if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
 
 
-			editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), materialSlot ) );
 
 
 		}
 		}
 
 
@@ -142,7 +143,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 		if ( material[ `${ mapType }Scale` ] !== scale.getValue() ) {
 		if ( material[ `${ mapType }Scale` ] !== scale.getValue() ) {
 
 
-			editor.execute( new SetMaterialValueCommand( editor, object, `${ mapType }Scale`, scale.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialValueCommand( editor, object, `${ mapType }Scale`, scale.getValue(), materialSlot ) );
 
 
 		}
 		}
 
 
@@ -154,7 +155,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 		if ( material[ `${ mapType }Scale` ].x !== value[ 0 ] || material[ `${ mapType }Scale` ].y !== value[ 1 ] ) {
 		if ( material[ `${ mapType }Scale` ].x !== value[ 0 ] || material[ `${ mapType }Scale` ].y !== value[ 1 ] ) {
 
 
-			editor.execute( new SetMaterialVectorCommand( editor, object, `${ mapType }Scale`, value, 0 /* TODOL currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialVectorCommand( editor, object, `${ mapType }Scale`, value, materialSlot ) );
 
 
 		}
 		}
 
 
@@ -166,18 +167,21 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 		if ( material[ `${ mapType }Range` ][ 0 ] !== value[ 0 ] || material[ `${ mapType }Range` ][ 1 ] !== value[ 1 ] ) {
 		if ( material[ `${ mapType }Range` ][ 0 ] !== value[ 0 ] || material[ `${ mapType }Range` ][ 1 ] !== value[ 1 ] ) {
 
 
-			editor.execute( new SetMaterialRangeCommand( editor, object, `${ mapType }Range`, value[ 0 ], value[ 1 ], 0 /* TODOL currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialRangeCommand( editor, object, `${ mapType }Range`, value[ 0 ], value[ 1 ], materialSlot ) );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -230,11 +234,9 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 	signals.objectSelected.add( function ( selected ) {
 	signals.objectSelected.add( function ( selected ) {
 
 
-		object = selected;
-
 		map.setValue( null );
 		map.setValue( null );
 
 
-		update();
+		update( selected );
 
 
 	} );
 	} );
 
 

+ 8 - 11
editor/js/Sidebar.Material.NumberProperty.js

@@ -12,24 +12,28 @@ function SidebarMaterialNumberProperty( editor, property, name, range = [ - Infi
 	container.add( number );
 	container.add( number );
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
 
 
 		if ( material[ property ] !== number.getValue() ) {
 		if ( material[ property ] !== number.getValue() ) {
 
 
-			editor.execute( new SetMaterialValueCommand( editor, object, property, number.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialValueCommand( editor, object, property, number.getValue(), materialSlot ) );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -46,14 +50,7 @@ function SidebarMaterialNumberProperty( editor, property, name, range = [ - Infi
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 7 - 10
editor/js/Sidebar.Material.Program.js

@@ -6,6 +6,7 @@ function SidebarMaterialProgram( editor, property ) {
 	const strings = editor.strings;
 	const strings = editor.strings;
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	const container = new UIRow();
 	const container = new UIRow();
@@ -38,12 +39,15 @@ function SidebarMaterialProgram( editor, property ) {
 	} );
 	} );
 	container.add( programFragment );
 	container.add( programFragment );
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -59,14 +63,7 @@ function SidebarMaterialProgram( editor, property ) {
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 8 - 11
editor/js/Sidebar.Material.RangeValueProperty.js

@@ -12,6 +12,7 @@ function SidebarMaterialRangeValueProperty( editor, property, name, isMin, range
 	container.add( number );
 	container.add( number );
 
 
 	let object = null;
 	let object = null;
+	let materialSlot = null;
 	let material = null;
 	let material = null;
 
 
 	function onChange() {
 	function onChange() {
@@ -21,18 +22,21 @@ function SidebarMaterialRangeValueProperty( editor, property, name, isMin, range
 			const minValue = isMin ? number.getValue() : material[ property ][ 0 ];
 			const minValue = isMin ? number.getValue() : material[ property ][ 0 ];
 			const maxValue = isMin ? material[ property ][ 1 ] : number.getValue();
 			const maxValue = isMin ? material[ property ][ 1 ] : number.getValue();
 
 
-			editor.execute( new SetMaterialRangeCommand( editor, object, property, minValue, maxValue, 0 /* TODO: currentMaterialSlot */ ) );
+			editor.execute( new SetMaterialRangeCommand( editor, object, property, minValue, maxValue, materialSlot ) );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	function update() {
+	function update( currentObject, currentMaterialSlot = 0 ) {
+
+		object = currentObject;
+		materialSlot = currentMaterialSlot;
 
 
 		if ( object === null ) return;
 		if ( object === null ) return;
 		if ( object.material === undefined ) return;
 		if ( object.material === undefined ) return;
 
 
-		material = object.material;
+		material = editor.getObjectMaterial( object, materialSlot );
 
 
 		if ( property in material ) {
 		if ( property in material ) {
 
 
@@ -49,14 +53,7 @@ function SidebarMaterialRangeValueProperty( editor, property, name, isMin, range
 
 
 	//
 	//
 
 
-	signals.objectSelected.add( function ( selected ) {
-
-		object = selected;
-
-		update();
-
-	} );
-
+	signals.objectSelected.add( update );
 	signals.materialChanged.add( update );
 	signals.materialChanged.add( update );
 
 
 	return container;
 	return container;

+ 5 - 1
editor/js/Sidebar.Material.js

@@ -421,7 +421,11 @@ function SidebarMaterial( editor ) {
 
 
 		currentMaterialSlot = parseInt( materialSlotSelect.getValue() );
 		currentMaterialSlot = parseInt( materialSlotSelect.getValue() );
 
 
-		if ( currentMaterialSlot !== previousSelectedSlot ) refreshUI();
+		if ( currentMaterialSlot !== previousSelectedSlot ) {
+
+			editor.signals.materialChanged.dispatch( currentObject, currentMaterialSlot );
+
+		}
 
 
 		let material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
 		let material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
 
 

+ 2 - 1
editor/js/Sidebar.Project.Renderer.js

@@ -67,7 +67,8 @@ function SidebarProjectRenderer( editor ) {
 		1: 'Linear',
 		1: 'Linear',
 		2: 'Reinhard',
 		2: 'Reinhard',
 		3: 'Cineon',
 		3: 'Cineon',
-		4: 'ACESFilmic'
+		4: 'ACESFilmic',
+		6: 'AgX'
 	} ).setWidth( '120px' ).onChange( updateToneMapping );
 	} ).setWidth( '120px' ).onChange( updateToneMapping );
 	toneMappingSelect.setValue( config.getKey( 'project/renderer/toneMapping' ) );
 	toneMappingSelect.setValue( config.getKey( 'project/renderer/toneMapping' ) );
 	toneMappingRow.add( toneMappingSelect );
 	toneMappingRow.add( toneMappingSelect );

+ 4 - 2
editor/js/commands/SetMaterialColorCommand.js

@@ -18,6 +18,8 @@ class SetMaterialColorCommand extends Command {
 		this.updatable = true;
 		this.updatable = true;
 
 
 		this.object = object;
 		this.object = object;
+		this.materialSlot = materialSlot;
+
 		this.material = ( this.object !== undefined ) ? this.editor.getObjectMaterial( object, materialSlot ) : undefined;
 		this.material = ( this.object !== undefined ) ? this.editor.getObjectMaterial( object, materialSlot ) : undefined;
 
 
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ].getHex() : undefined;
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ].getHex() : undefined;
@@ -31,7 +33,7 @@ class SetMaterialColorCommand extends Command {
 
 
 		this.material[ this.attributeName ].setHex( this.newValue );
 		this.material[ this.attributeName ].setHex( this.newValue );
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
@@ -39,7 +41,7 @@ class SetMaterialColorCommand extends Command {
 
 
 		this.material[ this.attributeName ].setHex( this.oldValue );
 		this.material[ this.attributeName ].setHex( this.oldValue );
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 4 - 2
editor/js/commands/SetMaterialCommand.js

@@ -27,14 +27,16 @@ class SetMaterialCommand extends Command {
 	execute() {
 	execute() {
 
 
 		this.editor.setObjectMaterial( this.object, this.materialSlot, this.newMaterial );
 		this.editor.setObjectMaterial( this.object, this.materialSlot, this.newMaterial );
-		this.editor.signals.materialChanged.dispatch( this.newMaterial );
+
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
 	undo() {
 	undo() {
 
 
 		this.editor.setObjectMaterial( this.object, this.materialSlot, this.oldMaterial );
 		this.editor.setObjectMaterial( this.object, this.materialSlot, this.oldMaterial );
-		this.editor.signals.materialChanged.dispatch( this.oldMaterial );
+
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 4 - 2
editor/js/commands/SetMaterialMapCommand.js

@@ -18,6 +18,8 @@ class SetMaterialMapCommand extends Command {
 		this.name = `Set Material.${mapName}`;
 		this.name = `Set Material.${mapName}`;
 
 
 		this.object = object;
 		this.object = object;
+		this.materialSlot = materialSlot;
+
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 
 
 		this.oldMap = ( object !== undefined ) ? this.material[ mapName ] : undefined;
 		this.oldMap = ( object !== undefined ) ? this.material[ mapName ] : undefined;
@@ -34,7 +36,7 @@ class SetMaterialMapCommand extends Command {
 		this.material[ this.mapName ] = this.newMap;
 		this.material[ this.mapName ] = this.newMap;
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
@@ -43,7 +45,7 @@ class SetMaterialMapCommand extends Command {
 		this.material[ this.mapName ] = this.oldMap;
 		this.material[ this.mapName ] = this.oldMap;
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 4 - 2
editor/js/commands/SetMaterialRangeCommand.js

@@ -19,6 +19,8 @@ class SetMaterialRangeCommand extends Command {
 		this.updatable = true;
 		this.updatable = true;
 
 
 		this.object = object;
 		this.object = object;
+		this.materialSlot = materialSlot;
+
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 
 
 		this.oldRange = ( this.material !== undefined && this.material[ attributeName ] !== undefined ) ? [ ...this.material[ attributeName ] ] : undefined;
 		this.oldRange = ( this.material !== undefined && this.material[ attributeName ] !== undefined ) ? [ ...this.material[ attributeName ] ] : undefined;
@@ -34,7 +36,7 @@ class SetMaterialRangeCommand extends Command {
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
 		this.editor.signals.objectChanged.dispatch( this.object );
 		this.editor.signals.objectChanged.dispatch( this.object );
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
@@ -44,7 +46,7 @@ class SetMaterialRangeCommand extends Command {
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
 		this.editor.signals.objectChanged.dispatch( this.object );
 		this.editor.signals.objectChanged.dispatch( this.object );
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 4 - 2
editor/js/commands/SetMaterialValueCommand.js

@@ -18,6 +18,8 @@ class SetMaterialValueCommand extends Command {
 		this.updatable = true;
 		this.updatable = true;
 
 
 		this.object = object;
 		this.object = object;
+		this.materialSlot = materialSlot;
+
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 
 
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ] : undefined;
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ] : undefined;
@@ -33,7 +35,7 @@ class SetMaterialValueCommand extends Command {
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
 		this.editor.signals.objectChanged.dispatch( this.object );
 		this.editor.signals.objectChanged.dispatch( this.object );
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
@@ -43,7 +45,7 @@ class SetMaterialValueCommand extends Command {
 		this.material.needsUpdate = true;
 		this.material.needsUpdate = true;
 
 
 		this.editor.signals.objectChanged.dispatch( this.object );
 		this.editor.signals.objectChanged.dispatch( this.object );
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 4 - 2
editor/js/commands/SetMaterialVectorCommand.js

@@ -11,6 +11,8 @@ class SetMaterialVectorCommand extends Command {
 		this.updatable = true;
 		this.updatable = true;
 
 
 		this.object = object;
 		this.object = object;
+		this.materialSlot = materialSlot;
+
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 		this.material = this.editor.getObjectMaterial( object, materialSlot );
 
 
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ].toArray() : undefined;
 		this.oldValue = ( this.material !== undefined ) ? this.material[ attributeName ].toArray() : undefined;
@@ -24,7 +26,7 @@ class SetMaterialVectorCommand extends Command {
 
 
 		this.material[ this.attributeName ].fromArray( this.newValue );
 		this.material[ this.attributeName ].fromArray( this.newValue );
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 
@@ -32,7 +34,7 @@ class SetMaterialVectorCommand extends Command {
 
 
 		this.material[ this.attributeName ].fromArray( this.oldValue );
 		this.material[ this.attributeName ].fromArray( this.oldValue );
 
 
-		this.editor.signals.materialChanged.dispatch( this.material );
+		this.editor.signals.materialChanged.dispatch( this.object, this.materialSlot );
 
 
 	}
 	}
 
 

+ 8 - 1
examples/files.json

@@ -246,7 +246,7 @@
 		"webgl_postprocessing_fxaa",
 		"webgl_postprocessing_fxaa",
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_godrays",
 		"webgl_postprocessing_godrays",
-		"webgl_postprocessing_hbao",
+		"webgl_postprocessing_gtao",
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa",
@@ -299,6 +299,7 @@
 	"webgl2": [
 	"webgl2": [
 		"webgl2_buffergeometry_attributes_integer",
 		"webgl2_buffergeometry_attributes_integer",
 		"webgl2_buffergeometry_attributes_none",
 		"webgl2_buffergeometry_attributes_none",
+		"webgl2_clipculldistance",
 		"webgl2_materials_texture2darray",
 		"webgl2_materials_texture2darray",
 		"webgl2_materials_texture3d",
 		"webgl2_materials_texture3d",
 		"webgl2_materials_texture3d_partialupdate",
 		"webgl2_materials_texture3d_partialupdate",
@@ -307,6 +308,7 @@
 		"webgl2_rendertarget_texture2darray",
 		"webgl2_rendertarget_texture2darray",
 		"webgl2_texture2darray_compressed",
 		"webgl2_texture2darray_compressed",
 		"webgl2_ubo",
 		"webgl2_ubo",
+		"webgl2_ubo_arrays",
 		"webgl2_volume_cloud",
 		"webgl2_volume_cloud",
 		"webgl2_volume_instancing",
 		"webgl2_volume_instancing",
 		"webgl2_volume_perlin"
 		"webgl2_volume_perlin"
@@ -314,11 +316,13 @@
 	"webgpu (wip)": [
 	"webgpu (wip)": [
 		"webgpu_backdrop",
 		"webgpu_backdrop",
 		"webgpu_backdrop_area",
 		"webgpu_backdrop_area",
+		"webgpu_backdrop_water",
 		"webgpu_camera_logarithmicdepthbuffer",
 		"webgpu_camera_logarithmicdepthbuffer",
 		"webgpu_clearcoat",
 		"webgpu_clearcoat",
 		"webgpu_compute_audio",
 		"webgpu_compute_audio",
 		"webgpu_compute_particles",
 		"webgpu_compute_particles",
 		"webgpu_compute_particles_rain",
 		"webgpu_compute_particles_rain",
+		"webgpu_compute_particles_snow",
 		"webgpu_compute_points",
 		"webgpu_compute_points",
 		"webgpu_compute_texture",
 		"webgpu_compute_texture",
 		"webgpu_compute_texture_pingpong",
 		"webgpu_compute_texture_pingpong",
@@ -339,13 +343,16 @@
 		"webgpu_loader_gltf_compressed",
 		"webgpu_loader_gltf_compressed",
 		"webgpu_loader_gltf_iridescence",
 		"webgpu_loader_gltf_iridescence",
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_sheen",
+		"webgpu_loader_materialx",
 		"webgpu_materials",
 		"webgpu_materials",
 		"webgpu_materials_video",
 		"webgpu_materials_video",
+		"webgpu_materialx_noise",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets",
 		"webgpu_morphtargets",
 		"webgpu_morphtargets",
 		"webgpu_morphtargets_face",
 		"webgpu_morphtargets_face",
 		"webgpu_occlusion",
 		"webgpu_occlusion",
 		"webgpu_particles",
 		"webgpu_particles",
+		"webgpu_portal",
 		"webgpu_rtt",
 		"webgpu_rtt",
 		"webgpu_sandbox",
 		"webgpu_sandbox",
 		"webgpu_shadertoy",
 		"webgpu_shadertoy",

+ 50 - 38
examples/jsm/controls/OrbitControls.js

@@ -492,9 +492,10 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function getZoomScale() {
+		function getZoomScale( delta ) {
 
 
-			return Math.pow( 0.95, scope.zoomSpeed );
+			const normalized_delta = Math.abs( delta ) / ( 100 * ( window.devicePixelRatio | 0 ) );
+			return Math.pow( 0.95, scope.zoomSpeed * normalized_delta );
 
 
 		}
 		}
 
 
@@ -621,7 +622,7 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function updateMouseParameters( event ) {
+		function updateZoomParameters( x, y ) {
 
 
 			if ( ! scope.zoomToCursor ) {
 			if ( ! scope.zoomToCursor ) {
 
 
@@ -632,13 +633,13 @@ class OrbitControls extends EventDispatcher {
 			performCursorZoom = true;
 			performCursorZoom = true;
 
 
 			const rect = scope.domElement.getBoundingClientRect();
 			const rect = scope.domElement.getBoundingClientRect();
-			const x = event.clientX - rect.left;
-			const y = event.clientY - rect.top;
+			const dx = x - rect.left;
+			const dy = y - rect.top;
 			const w = rect.width;
 			const w = rect.width;
 			const h = rect.height;
 			const h = rect.height;
 
 
-			mouse.x = ( x / w ) * 2 - 1;
-			mouse.y = - ( y / h ) * 2 + 1;
+			mouse.x = ( dx / w ) * 2 - 1;
+			mouse.y = - ( dy / h ) * 2 + 1;
 
 
 			dollyDirection.set( mouse.x, mouse.y, 1 ).unproject( scope.object ).sub( scope.object.position ).normalize();
 			dollyDirection.set( mouse.x, mouse.y, 1 ).unproject( scope.object ).sub( scope.object.position ).normalize();
 
 
@@ -662,7 +663,7 @@ class OrbitControls extends EventDispatcher {
 
 
 		function handleMouseDownDolly( event ) {
 		function handleMouseDownDolly( event ) {
 
 
-			updateMouseParameters( event );
+			updateZoomParameters( event.clientX, event.clientX );
 			dollyStart.set( event.clientX, event.clientY );
 			dollyStart.set( event.clientX, event.clientY );
 
 
 		}
 		}
@@ -699,11 +700,11 @@ class OrbitControls extends EventDispatcher {
 
 
 			if ( dollyDelta.y > 0 ) {
 			if ( dollyDelta.y > 0 ) {
 
 
-				dollyOut( getZoomScale() );
+				dollyOut( getZoomScale( dollyDelta.y ) );
 
 
 			} else if ( dollyDelta.y < 0 ) {
 			} else if ( dollyDelta.y < 0 ) {
 
 
-				dollyIn( getZoomScale() );
+				dollyIn( getZoomScale( dollyDelta.y ) );
 
 
 			}
 			}
 
 
@@ -729,15 +730,15 @@ class OrbitControls extends EventDispatcher {
 
 
 		function handleMouseWheel( event ) {
 		function handleMouseWheel( event ) {
 
 
-			updateMouseParameters( event );
+			updateZoomParameters( event.clientX, event.clientY );
 
 
 			if ( event.deltaY < 0 ) {
 			if ( event.deltaY < 0 ) {
 
 
-				dollyIn( getZoomScale() );
+				dollyIn( getZoomScale( event.deltaY ) );
 
 
 			} else if ( event.deltaY > 0 ) {
 			} else if ( event.deltaY > 0 ) {
 
 
-				dollyOut( getZoomScale() );
+				dollyOut( getZoomScale( event.deltaY ) );
 
 
 			}
 			}
 
 
@@ -825,16 +826,18 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function handleTouchStartRotate() {
+		function handleTouchStartRotate( event ) {
 
 
 			if ( pointers.length === 1 ) {
 			if ( pointers.length === 1 ) {
 
 
-				rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
+				rotateStart.set( event.pageX, event.pageY );
 
 
 			} else {
 			} else {
 
 
-				const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
-				const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
+				const position = getSecondPointerPosition( event );
+
+				const x = 0.5 * ( event.pageX + position.x );
+				const y = 0.5 * ( event.pageY + position.y );
 
 
 				rotateStart.set( x, y );
 				rotateStart.set( x, y );
 
 
@@ -842,16 +845,18 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function handleTouchStartPan() {
+		function handleTouchStartPan( event ) {
 
 
 			if ( pointers.length === 1 ) {
 			if ( pointers.length === 1 ) {
 
 
-				panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
+				panStart.set( event.pageX, event.pageY );
 
 
 			} else {
 			} else {
 
 
-				const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
-				const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
+				const position = getSecondPointerPosition( event );
+
+				const x = 0.5 * ( event.pageX + position.x );
+				const y = 0.5 * ( event.pageY + position.y );
 
 
 				panStart.set( x, y );
 				panStart.set( x, y );
 
 
@@ -859,10 +864,12 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function handleTouchStartDolly() {
+		function handleTouchStartDolly( event ) {
 
 
-			const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX;
-			const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY;
+			const position = getSecondPointerPosition( event );
+
+			const dx = event.pageX - position.x;
+			const dy = event.pageY - position.y;
 
 
 			const distance = Math.sqrt( dx * dx + dy * dy );
 			const distance = Math.sqrt( dx * dx + dy * dy );
 
 
@@ -870,19 +877,19 @@ class OrbitControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function handleTouchStartDollyPan() {
+		function handleTouchStartDollyPan( event ) {
 
 
-			if ( scope.enableZoom ) handleTouchStartDolly();
+			if ( scope.enableZoom ) handleTouchStartDolly( event );
 
 
-			if ( scope.enablePan ) handleTouchStartPan();
+			if ( scope.enablePan ) handleTouchStartPan( event );
 
 
 		}
 		}
 
 
-		function handleTouchStartDollyRotate() {
+		function handleTouchStartDollyRotate( event ) {
 
 
-			if ( scope.enableZoom ) handleTouchStartDolly();
+			if ( scope.enableZoom ) handleTouchStartDolly( event );
 
 
-			if ( scope.enableRotate ) handleTouchStartRotate();
+			if ( scope.enableRotate ) handleTouchStartRotate( event );
 
 
 		}
 		}
 
 
@@ -957,6 +964,11 @@ class OrbitControls extends EventDispatcher {
 
 
 			dollyStart.copy( dollyEnd );
 			dollyStart.copy( dollyEnd );
 
 
+			const centerX = ( event.pageX + position.x ) * 0.5;
+			const centerY = ( event.pageY + position.y ) * 0.5;
+
+			updateZoomParameters( centerX, centerY );
+
 		}
 		}
 
 
 		function handleTouchMoveDollyPan( event ) {
 		function handleTouchMoveDollyPan( event ) {
@@ -1208,7 +1220,7 @@ class OrbitControls extends EventDispatcher {
 
 
 							if ( scope.enableRotate === false ) return;
 							if ( scope.enableRotate === false ) return;
 
 
-							handleTouchStartRotate();
+							handleTouchStartRotate( event );
 
 
 							state = STATE.TOUCH_ROTATE;
 							state = STATE.TOUCH_ROTATE;
 
 
@@ -1218,7 +1230,7 @@ class OrbitControls extends EventDispatcher {
 
 
 							if ( scope.enablePan === false ) return;
 							if ( scope.enablePan === false ) return;
 
 
-							handleTouchStartPan();
+							handleTouchStartPan( event );
 
 
 							state = STATE.TOUCH_PAN;
 							state = STATE.TOUCH_PAN;
 
 
@@ -1240,7 +1252,7 @@ class OrbitControls extends EventDispatcher {
 
 
 							if ( scope.enableZoom === false && scope.enablePan === false ) return;
 							if ( scope.enableZoom === false && scope.enablePan === false ) return;
 
 
-							handleTouchStartDollyPan();
+							handleTouchStartDollyPan( event );
 
 
 							state = STATE.TOUCH_DOLLY_PAN;
 							state = STATE.TOUCH_DOLLY_PAN;
 
 
@@ -1250,7 +1262,7 @@ class OrbitControls extends EventDispatcher {
 
 
 							if ( scope.enableZoom === false && scope.enableRotate === false ) return;
 							if ( scope.enableZoom === false && scope.enableRotate === false ) return;
 
 
-							handleTouchStartDollyRotate();
+							handleTouchStartDollyRotate( event );
 
 
 							state = STATE.TOUCH_DOLLY_ROTATE;
 							state = STATE.TOUCH_DOLLY_ROTATE;
 
 
@@ -1342,7 +1354,7 @@ class OrbitControls extends EventDispatcher {
 
 
 		function addPointer( event ) {
 		function addPointer( event ) {
 
 
-			pointers.push( event );
+			pointers.push( event.pointerId );
 
 
 		}
 		}
 
 
@@ -1352,7 +1364,7 @@ class OrbitControls extends EventDispatcher {
 
 
 			for ( let i = 0; i < pointers.length; i ++ ) {
 			for ( let i = 0; i < pointers.length; i ++ ) {
 
 
-				if ( pointers[ i ].pointerId == event.pointerId ) {
+				if ( pointers[ i ] == event.pointerId ) {
 
 
 					pointers.splice( i, 1 );
 					pointers.splice( i, 1 );
 					return;
 					return;
@@ -1380,9 +1392,9 @@ class OrbitControls extends EventDispatcher {
 
 
 		function getSecondPointerPosition( event ) {
 		function getSecondPointerPosition( event ) {
 
 
-			const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ];
+			const pointerId = ( event.pointerId === pointers[ 0 ] ) ? pointers[ 1 ] : pointers[ 0 ];
 
 
-			return pointerPositions[ pointer.pointerId ];
+			return pointerPositions[ pointerId ];
 
 
 		}
 		}
 
 

+ 52 - 47
examples/jsm/csm/CSMShader.js

@@ -62,7 +62,7 @@ IncidentLight directLight;
  	vec4 spotColor;
  	vec4 spotColor;
 	vec3 spotLightCoord;
 	vec3 spotLightCoord;
 	bool inSpotLightMap;
 	bool inSpotLightMap;
- 
+
 	#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
 	#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
 	SpotLightShadow spotLightShadow;
 	SpotLightShadow spotLightShadow;
 	#endif
 	#endif
@@ -112,57 +112,57 @@ IncidentLight directLight;
 	#endif
 	#endif
 
 
 	#if defined( USE_SHADOWMAP ) && defined( CSM_FADE )
 	#if defined( USE_SHADOWMAP ) && defined( CSM_FADE )
-	vec2 cascade;
-	float cascadeCenter;
-	float closestEdge;
-	float margin;
-	float csmx;
-	float csmy;
+		vec2 cascade;
+		float cascadeCenter;
+		float closestEdge;
+		float margin;
+		float csmx;
+		float csmy;
 
 
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
+		#pragma unroll_loop_start
+		for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 
-		directionalLight = directionalLights[ i ];
-		getDirectionalLightInfo( directionalLight, directLight );
+			directionalLight = directionalLights[ i ];
+			getDirectionalLightInfo( directionalLight, directLight );
 
 
-	  	#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-			// NOTE: Depth gets larger away from the camera.
-			// cascade.x is closer, cascade.y is further
-			cascade = CSM_cascades[ i ];
-			cascadeCenter = ( cascade.x + cascade.y ) / 2.0;
-			closestEdge = linearDepth < cascadeCenter ? cascade.x : cascade.y;
-			margin = 0.25 * pow( closestEdge, 2.0 );
-			csmx = cascade.x - margin / 2.0;
-			csmy = cascade.y + margin / 2.0;
-			if( linearDepth >= csmx && ( linearDepth < csmy || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 ) ) {
-
-				float dist = min( linearDepth - csmx, csmy - linearDepth );
-				float ratio = clamp( dist / margin, 0.0, 1.0 );
-
-				vec3 prevColor = directLight.color;
-				directionalLightShadow = directionalLightShadows[ i ];
-				directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
+			#if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
+				// NOTE: Depth gets larger away from the camera.
+				// cascade.x is closer, cascade.y is further
+				cascade = CSM_cascades[ i ];
+				cascadeCenter = ( cascade.x + cascade.y ) / 2.0;
+				closestEdge = linearDepth < cascadeCenter ? cascade.x : cascade.y;
+				margin = 0.25 * pow( closestEdge, 2.0 );
+				csmx = cascade.x - margin / 2.0;
+				csmy = cascade.y + margin / 2.0;
+				if( linearDepth >= csmx && ( linearDepth < csmy || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 ) ) {
 
 
-				bool shouldFadeLastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth > cascadeCenter;
-				directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
+					float dist = min( linearDepth - csmx, csmy - linearDepth );
+					float ratio = clamp( dist / margin, 0.0, 1.0 );
 
 
-				ReflectedLight prevLight = reflectedLight;
-				RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
+					vec3 prevColor = directLight.color;
+					directionalLightShadow = directionalLightShadows[ i ];
+					directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
 
 
-				bool shouldBlend = UNROLLED_LOOP_INDEX != CSM_CASCADES - 1 || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth < cascadeCenter;
-				float blendRatio = shouldBlend ? ratio : 1.0;
+					bool shouldFadeLastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth > cascadeCenter;
+					directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
 
 
-				reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
-				reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
-				reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
-				reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
+					ReflectedLight prevLight = reflectedLight;
+					RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
 
 
-			}
-	  	#endif
+					bool shouldBlend = UNROLLED_LOOP_INDEX != CSM_CASCADES - 1 || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth < cascadeCenter;
+					float blendRatio = shouldBlend ? ratio : 1.0;
 
 
-	}
-	#pragma unroll_loop_end
-	#else
+					reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
+					reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
+					reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
+					reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
+
+				}
+			#endif
+
+		}
+		#pragma unroll_loop_end
+	#elif defined (USE_SHADOWMAP)
 
 
 		#pragma unroll_loop_start
 		#pragma unroll_loop_start
 		for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 		for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
@@ -170,18 +170,23 @@ IncidentLight directLight;
 			directionalLight = directionalLights[ i ];
 			directionalLight = directionalLights[ i ];
 			getDirectionalLightInfo( directionalLight, directLight );
 			getDirectionalLightInfo( directionalLight, directLight );
 
 
-			#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
+			#if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
 
-			directionalLightShadow = directionalLightShadows[ i ];
-			if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
+				directionalLightShadow = directionalLightShadows[ i ];
+				if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
 
 
-			if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
+				if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
 
 
 			#endif
 			#endif
 
 
 		}
 		}
 		#pragma unroll_loop_end
 		#pragma unroll_loop_end
 
 
+	#elif ( NUM_DIR_LIGHT_SHADOWS > 0 )
+		// note: no loop here - all CSM lights are in fact one light only
+		getDirectionalLightInfo( directionalLights[0], directLight );
+		RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
+
 	#endif
 	#endif
 
 
 	#if ( NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS)
 	#if ( NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS)

+ 23 - 8
examples/jsm/exporters/USDZExporter.js

@@ -1,5 +1,14 @@
-import * as THREE from 'three';
-import * as fflate from '../libs/fflate.module.js';
+import {
+  NoColorSpace,
+  DoubleSide,
+} from 'three';
+
+import {
+  strToU8,
+  zipSync,
+} from '../libs/fflate.module.js';
+
+import { decompress } from './../utils/TextureUtils.js';
 
 
 class USDZExporter {
 class USDZExporter {
 
 
@@ -71,12 +80,18 @@ class USDZExporter {
 
 
 		output += buildMaterials( materials, textures, options.quickLookCompatible );
 		output += buildMaterials( materials, textures, options.quickLookCompatible );
 
 
-		files[ modelFileName ] = fflate.strToU8( output );
+		files[ modelFileName ] = strToU8( output );
 		output = null;
 		output = null;
 
 
 		for ( const id in textures ) {
 		for ( const id in textures ) {
 
 
-			const texture = textures[ id ];
+			let texture = textures[ id ];
+
+			if ( texture.isCompressedTexture === true ) {
+
+				texture = decompress( texture );
+
+			}
 
 
 			const canvas = imageToCanvas( texture.image, texture.flipY );
 			const canvas = imageToCanvas( texture.image, texture.flipY );
 			const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
 			const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
@@ -112,7 +127,7 @@ class USDZExporter {
 
 
 		}
 		}
 
 
-		return fflate.zipSync( files, { level: 0 } );
+		return zipSync( files, { level: 0 } );
 
 
 	}
 	}
 
 
@@ -212,7 +227,7 @@ function buildUSDFileAsString( dataToInsert ) {
 
 
 	let output = buildHeader();
 	let output = buildHeader();
 	output += dataToInsert;
 	output += dataToInsert;
-	return fflate.strToU8( output );
+	return strToU8( output );
 
 
 }
 }
 
 
@@ -499,7 +514,7 @@ function buildMaterial( material, textures, quickLookCompatible = false ) {
 			asset inputs:file = @textures/Texture_${ id }.png@
 			asset inputs:file = @textures/Texture_${ id }.png@
 			float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
 			float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
 			${ color !== undefined ? 'float4 inputs:scale = ' + buildColor4( color ) : '' }
 			${ color !== undefined ? 'float4 inputs:scale = ' + buildColor4( color ) : '' }
-			token inputs:sourceColorSpace = "${ texture.colorSpace === THREE.NoColorSpace ? 'raw' : 'sRGB' }"
+			token inputs:sourceColorSpace = "${ texture.colorSpace === NoColorSpace ? 'raw' : 'sRGB' }"
 			token inputs:wrapS = "${ WRAPPINGS[ texture.wrapS ] }"
 			token inputs:wrapS = "${ WRAPPINGS[ texture.wrapS ] }"
 			token inputs:wrapT = "${ WRAPPINGS[ texture.wrapT ] }"
 			token inputs:wrapT = "${ WRAPPINGS[ texture.wrapT ] }"
 			float outputs:r
 			float outputs:r
@@ -512,7 +527,7 @@ function buildMaterial( material, textures, quickLookCompatible = false ) {
 	}
 	}
 
 
 
 
-	if ( material.side === THREE.DoubleSide ) {
+	if ( material.side === DoubleSide ) {
 
 
 		console.warn( 'THREE.USDZExporter: USDZ does not support double sided materials', material );
 		console.warn( 'THREE.USDZExporter: USDZ does not support double sided materials', material );
 
 

+ 16 - 33
examples/jsm/lines/LineMaterial.js

@@ -169,51 +169,34 @@ ShaderLib[ 'line' ] = {
 
 
 			#ifdef WORLD_UNITS
 			#ifdef WORLD_UNITS
 
 
-				// get the offset direction as perpendicular to the view vector
 				vec3 worldDir = normalize( end.xyz - start.xyz );
 				vec3 worldDir = normalize( end.xyz - start.xyz );
-				vec3 offset;
-				if ( position.y < 0.5 ) {
+				vec3 tmpFwd = normalize( mix( start.xyz, end.xyz, 0.5 ) );
+				vec3 worldUp = normalize( cross( worldDir, tmpFwd ) );
+				vec3 worldFwd = cross( worldDir, worldUp );
+				worldPos = position.y < 0.5 ? start: end;
 
 
-					offset = normalize( cross( start.xyz, worldDir ) );
-
-				} else {
-
-					offset = normalize( cross( end.xyz, worldDir ) );
-
-				}
-
-				// sign flip
-				if ( position.x < 0.0 ) offset *= - 1.0;
-
-				float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );
+				// height offset
+				float hw = linewidth * 0.5;
+				worldPos.xyz += position.x < 0.0 ? hw * worldUp : - hw * worldUp;
 
 
 				// don't extend the line if we're rendering dashes because we
 				// don't extend the line if we're rendering dashes because we
 				// won't be rendering the endcaps
 				// won't be rendering the endcaps
 				#ifndef USE_DASH
 				#ifndef USE_DASH
 
 
-					// extend the line bounds to encompass  endcaps
-					start.xyz += - worldDir * linewidth * 0.5;
-					end.xyz += worldDir * linewidth * 0.5;
+					// cap extension
+					worldPos.xyz += position.y < 0.5 ? - hw * worldDir : hw * worldDir;
 
 
-					// shift the position of the quad so it hugs the forward edge of the line
-					offset.xy -= dir * forwardOffset;
-					offset.z += 0.5;
+					// add width to the box
+					worldPos.xyz += worldFwd * hw;
 
 
-				#endif
-
-				// endcaps
-				if ( position.y > 1.0 || position.y < 0.0 ) {
-
-					offset.xy += dir * 2.0 * forwardOffset;
+					// endcaps
+					if ( position.y > 1.0 || position.y < 0.0 ) {
 
 
-				}
+						worldPos.xyz -= worldFwd * 2.0 * hw;
 
 
-				// adjust for linewidth
-				offset *= linewidth * 0.5;
+					}
 
 
-				// set the world position
-				worldPos = ( position.y < 0.5 ) ? start : end;
-				worldPos.xyz += offset;
+				#endif
 
 
 				// project the worldpos
 				// project the worldpos
 				vec4 clip = projectionMatrix * worldPos;
 				vec4 clip = projectionMatrix * worldPos;

+ 4 - 3
examples/jsm/loaders/DRACOLoader.js

@@ -82,13 +82,14 @@ class DRACOLoader extends Loader {
 
 
 	}
 	}
 
 
-	parse( buffer, onLoad, onError ) {
+
+	parse( buffer, onLoad, onError = ()=>{} ) {
 
 
 		this.decodeDracoFile( buffer, onLoad, null, null, SRGBColorSpace ).catch( onError );
 		this.decodeDracoFile( buffer, onLoad, null, null, SRGBColorSpace ).catch( onError );
 
 
 	}
 	}
 
 
-	decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = LinearSRGBColorSpace ) {
+	decodeDracoFile( buffer, callback, attributeIDs, attributeTypes, vertexColorSpace = LinearSRGBColorSpace, onError = () => {} ) {
 
 
 		const taskConfig = {
 		const taskConfig = {
 			attributeIDs: attributeIDs || this.defaultAttributeIDs,
 			attributeIDs: attributeIDs || this.defaultAttributeIDs,
@@ -97,7 +98,7 @@ class DRACOLoader extends Loader {
 			vertexColorSpace: vertexColorSpace,
 			vertexColorSpace: vertexColorSpace,
 		};
 		};
 
 
-		return this.decodeGeometry( buffer, taskConfig ).then( callback );
+		return this.decodeGeometry( buffer, taskConfig ).then( callback ).catch( onError );
 
 
 	}
 	}
 
 

+ 2 - 2
examples/jsm/loaders/GLTFLoader.js

@@ -1923,7 +1923,7 @@ class GLTFDracoMeshCompressionExtension {
 
 
 		return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
 		return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
 
 
-			return new Promise( function ( resolve ) {
+			return new Promise( function ( resolve, reject ) {
 
 
 				dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
 				dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
 
 
@@ -1938,7 +1938,7 @@ class GLTFDracoMeshCompressionExtension {
 
 
 					resolve( geometry );
 					resolve( geometry );
 
 
-				}, threeAttributeMap, attributeTypeMap );
+				}, threeAttributeMap, attributeTypeMap, LinearSRGBColorSpace, reject );
 
 
 			} );
 			} );
 
 

+ 73 - 54
examples/jsm/loaders/MaterialXLoader.js

@@ -19,13 +19,13 @@ import {
 	mx_safepower, mx_contrast,
 	mx_safepower, mx_contrast,
 	mx_srgb_texture_to_lin_rec709,
 	mx_srgb_texture_to_lin_rec709,
 	saturation
 	saturation
-} from 'three/nodes';
+} from '../nodes/Nodes.js';
 
 
 const colorSpaceLib = {
 const colorSpaceLib = {
 	mx_srgb_texture_to_lin_rec709
 	mx_srgb_texture_to_lin_rec709
 };
 };
 
 
-class MtlXElement {
+class MXElement {
 
 
 	constructor( name, nodeFunc, params = null ) {
 	constructor( name, nodeFunc, params = null ) {
 
 
@@ -39,41 +39,41 @@ class MtlXElement {
 
 
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 
 
-const MtlXElements = [
+const MXElements = [
 
 
 	// << Math >>
 	// << 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( '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( 'transformpoint', ... ),
 	//new MtlXElement( 'transformvector', ... ),
 	//new MtlXElement( 'transformvector', ... ),
 	//new MtlXElement( 'transformnormal', ... ),
 	//new MtlXElement( 'transformnormal', ... ),
 	//new MtlXElement( 'transformmatrix', ... ),
 	//new MtlXElement( 'transformmatrix', ... ),
-	new MtlXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ),
+	new MXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ),
 	//new MtlXElement( 'transpose', ... ),
 	//new MtlXElement( 'transpose', ... ),
 	//new MtlXElement( 'determinant', ... ),
 	//new MtlXElement( 'determinant', ... ),
 	//new MtlXElement( 'invertmatrix', ... ),
 	//new MtlXElement( 'invertmatrix', ... ),
@@ -83,44 +83,44 @@ const MtlXElements = [
 	//new MtlXElement( 'dot', ... ),
 	//new MtlXElement( 'dot', ... ),
 
 
 	// << Adjustment >>
 	// << 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( 'curveadjust', ... ),
 	//new MtlXElement( 'curvelookup', ... ),
 	//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 >>
 	// << Mix >>
-	new MtlXElement( 'mix', mix, [ 'bg', 'fg', 'mix' ] ),
+	new MXElement( 'mix', mix, [ 'bg', 'fg', 'mix' ] ),
 
 
 	// << Channel >>
 	// << 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 >>
 	// << 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 >>
 	// << Supplemental >>
 	//new MtlXElement( 'tiledimage', ... ),
 	//new MtlXElement( 'tiledimage', ... ),
 	//new MtlXElement( 'triplanarprojection', triplanarTextures, [ 'filex', 'filey', 'filez' ] ),
 	//new MtlXElement( 'triplanarprojection', triplanarTextures, [ 'filex', 'filey', 'filez' ] ),
 	//new MtlXElement( 'ramp4', ... ),
 	//new MtlXElement( 'ramp4', ... ),
 	//new MtlXElement( 'place2d', mx_place2d, [ 'texcoord', 'pivot', 'scale', 'rotate', 'offset' ] ),
 	//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( 'hsvadjust', ... ),
-	new MtlXElement( 'saturate', saturation, [ 'in', 'amount' ] ),
+	new MXElement( 'saturate', saturation, [ 'in', 'amount' ] ),
 	//new MtlXElement( 'extract', ... ),
 	//new MtlXElement( 'extract', ... ),
 	//new MtlXElement( 'separate2', ... ),
 	//new MtlXElement( 'separate2', ... ),
 	//new MtlXElement( 'separate3', ... ),
 	//new MtlXElement( 'separate3', ... ),
@@ -129,7 +129,7 @@ const MtlXElements = [
 ];
 ];
 
 
 const MtlXLibrary = {};
 const MtlXLibrary = {};
-MtlXElements.forEach( element => MtlXLibrary[ element.name ] = element );
+MXElements.forEach( element => MtlXLibrary[ element.name ] = element );
 
 
 class MaterialXLoader extends Loader {
 class MaterialXLoader extends Loader {
 
 
@@ -588,11 +588,30 @@ class MaterialXNode {
 
 
 		//
 		//
 
 
+		let normalNode = null;
+
+		if ( inputs.normal ) normalNode = inputs.normal;
+
+		//
+
+		let emissiveNode = null;
+
+		if ( inputs.emission ) emissiveNode = inputs.emission;
+		if ( inputs.emissionColor )  {
+
+			emissiveNode = emissiveNode ? mul( emissiveNode, inputs.emissionColor ) : emissiveNode;
+
+		}
+
+		//
+
 		material.colorNode = colorNode || color( 0.8, 0.8, 0.8 );
 		material.colorNode = colorNode || color( 0.8, 0.8, 0.8 );
 		material.roughnessNode = roughnessNode || float( 0.2 );
 		material.roughnessNode = roughnessNode || float( 0.2 );
 		material.metalnessNode = metalnessNode || float( 0 );
 		material.metalnessNode = metalnessNode || float( 0 );
 		material.clearcoatNode = clearcoatNode || float( 0 );
 		material.clearcoatNode = clearcoatNode || float( 0 );
 		material.clearcoatRoughnessNode = clearcoatRoughnessNode || float( 0 );
 		material.clearcoatRoughnessNode = clearcoatRoughnessNode || float( 0 );
+		if ( normalNode ) material.normalNode = normalNode;
+		if ( emissiveNode ) material.emissiveNode = emissiveNode;
 
 
 	}
 	}
 
 

+ 119 - 0
examples/jsm/misc/Timer.js

@@ -0,0 +1,119 @@
+class Timer {
+
+	constructor() {
+
+		this._previousTime = 0;
+		this._currentTime = 0;
+		this._startTime = now();
+
+		this._delta = 0;
+		this._elapsed = 0;
+
+		this._timescale = 1;
+
+		// use Page Visibility API to avoid large time delta values
+
+		this._usePageVisibilityAPI = ( typeof document !== 'undefined' && document.hidden !== undefined );
+
+		if ( this._usePageVisibilityAPI === true ) {
+
+			this._pageVisibilityHandler = handleVisibilityChange.bind( this );
+
+			document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false );
+
+		}
+
+	}
+
+	getDelta() {
+
+		return this._delta / 1000;
+
+	}
+
+	getElapsed() {
+
+		return this._elapsed / 1000;
+
+	}
+
+	getTimescale() {
+
+		return this._timescale;
+
+	}
+
+	setTimescale( timescale ) {
+
+		this._timescale = timescale;
+
+		return this;
+
+	}
+
+	reset() {
+
+		this._currentTime = now() - this._startTime;
+
+		return this;
+
+	}
+
+	dispose() {
+
+		if ( this._usePageVisibilityAPI === true ) {
+
+			document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler );
+
+		}
+
+		return this;
+
+	}
+
+	update( timestamp ) {
+
+		this._previousTime = this._currentTime;
+		this._currentTime = ( timestamp !== undefined ? timestamp : now() ) - this._startTime;
+
+		this._delta = ( this._currentTime - this._previousTime ) * this._timescale;
+		this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas
+
+		return this;
+
+	}
+
+}
+
+class FixedTimer extends Timer {
+
+	constructor( fps = 60 ) {
+
+		super();
+		this._delta = ( 1 / fps ) * 1000;
+
+	}
+
+	update() {
+
+		this._elapsed += ( this._delta * this._timescale ); // _elapsed is the accumulation of all previous deltas
+
+		return this;
+
+	}
+
+}
+
+function now() {
+
+	return ( typeof performance === 'undefined' ? Date : performance ).now();
+
+}
+
+function handleVisibilityChange() {
+
+	if ( document.hidden === false ) this.reset();
+
+}
+
+export { Timer, FixedTimer };

+ 20 - 2
examples/jsm/modifiers/CurveModifier.js

@@ -214,8 +214,26 @@ export class Flow {
 				child instanceof InstancedMesh
 				child instanceof InstancedMesh
 			) {
 			) {
 
 
-				child.material = child.material.clone();
-				modifyShader( child.material, uniforms, numberOfCurves );
+				if ( Array.isArray( child.material ) ) {
+
+					const materials = [];
+
+					for ( const material of child.material ) {
+
+						const newMaterial = material.clone();
+						modifyShader( newMaterial, uniforms, numberOfCurves );
+						materials.push( newMaterial );
+
+					}
+
+					child.material = materials;
+
+				} else {
+
+					child.material = child.material.clone();
+					modifyShader( child.material, uniforms, numberOfCurves );
+
+				}
 
 
 			}
 			}
 
 

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

@@ -39,7 +39,7 @@ import * as NodeUtils from './core/NodeUtils.js';
 export { NodeUtils };
 export { NodeUtils };
 
 
 // math
 // 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 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 CondNode, cond } from './math/CondNode.js';
 export { default as HashNode, hash } from './math/HashNode.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 ConvertNode } from './utils/ConvertNode.js';
 export { default as DiscardNode, discard } from './utils/DiscardNode.js';
 export { default as DiscardNode, discard } from './utils/DiscardNode.js';
 export { default as EquirectUVNode, equirectUV } from './utils/EquirectUVNode.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 JoinNode } from './utils/JoinNode.js';
 export { default as LoopNode, loop } from './utils/LoopNode.js';
 export { default as LoopNode, loop } from './utils/LoopNode.js';
 export { default as MatcapUVNode, matcapUV } from './utils/MatcapUVNode.js';
 export { default as MatcapUVNode, matcapUV } from './utils/MatcapUVNode.js';
@@ -109,6 +110,8 @@ export { default as ViewportTextureNode, viewportTexture, viewportMipTexture } f
 export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
 export { default as ViewportSharedTextureNode, viewportSharedTexture } from './display/ViewportSharedTextureNode.js';
 export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
 export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
 export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
 export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
+export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlurNode.js';
+export { default as PassNode, pass, depthPass } from './display/PassNode.js';
 
 
 // code
 // code
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';

+ 7 - 2
examples/jsm/nodes/accessors/CubeTextureNode.js

@@ -27,9 +27,14 @@ class CubeTextureNode extends TextureNode {
 
 
 	setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
 	setUpdateMatrix( /*updateMatrix*/ ) { } // Ignore .updateMatrix for CubeTextureNode
 
 
-	generateUV( builder, uvNode ) {
+	setupUV( builder, uvNode ) {
+
+		return vec3( uvNode.x.negate(), uvNode.yz );
+
+	}
+
+	generateUV( builder, cubeUV ) {
 
 
-		const cubeUV = vec3( uvNode.x.negate(), uvNode.yz );
 		return cubeUV.build( builder, 'vec3' );
 		return cubeUV.build( builder, 'vec3' );
 
 
 	}
 	}

+ 22 - 15
examples/jsm/nodes/accessors/TextureNode.js

@@ -78,6 +78,20 @@ class TextureNode extends UniformNode {
 
 
 	}
 	}
 
 
+	setupUV( builder, uvNode ) {
+
+		const texture = this.value;
+
+		if ( builder.isFlipY() && ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) {
+
+			uvNode = uvNode.setY( uvNode.y.oneMinus() );
+
+		}
+
+		return uvNode;
+
+	}
+
 	setup( builder ) {
 	setup( builder ) {
 
 
 		const properties = builder.getNodeProperties( this );
 		const properties = builder.getNodeProperties( this );
@@ -100,6 +114,8 @@ class TextureNode extends UniformNode {
 
 
 		}
 		}
 
 
+		uvNode = this.setupUV( builder, uvNode );
+
 		//
 		//
 
 
 		let levelNode = this.levelNode;
 		let levelNode = this.levelNode;
@@ -125,6 +141,12 @@ class TextureNode extends UniformNode {
 
 
 	}
 	}
 
 
+	generateUV( builder, uvNode ) {
+
+		return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
+
+	}
+
 	generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet ) {
 	generateSnippet( builder, textureProperty, uvSnippet, levelSnippet, depthSnippet, compareSnippet ) {
 
 
 		const texture = this.value;
 		const texture = this.value;
@@ -153,21 +175,6 @@ class TextureNode extends UniformNode {
 
 
 	}
 	}
 
 
-	generateUV( builder, uvNode ) {
-
-		const texture = this.value;
-
-		if ( ( builder.isFlipY() && ( texture.isFramebufferTexture === true || texture.isDepthTexture === true ) ) ||
-			( builder.isFlipY() === false && texture.isRenderTargetTexture === true ) ) {
-
-			uvNode = uvNode.setY( uvNode.y.fract().oneMinus() );
-
-		}
-
-		return uvNode.build( builder, this.sampler === true ? 'vec2' : 'ivec2' );
-
-	}
-
 	generate( builder, output ) {
 	generate( builder, output ) {
 
 
 		const properties = builder.getNodeProperties( this );
 		const properties = builder.getNodeProperties( this );

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

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

+ 6 - 0
examples/jsm/nodes/core/AttributeNode.js

@@ -12,6 +12,12 @@ class AttributeNode extends Node {
 
 
 	}
 	}
 
 
+	isGlobal() {
+
+		return true;
+
+	}
+
 	getHash( builder ) {
 	getHash( builder ) {
 
 
 		return this.getAttributeName( builder );
 		return this.getAttributeName( builder );

+ 4 - 1
examples/jsm/nodes/core/CacheNode.js

@@ -24,8 +24,9 @@ class CacheNode extends Node {
 	build( builder, ...params ) {
 	build( builder, ...params ) {
 
 
 		const previousCache = builder.getCache();
 		const previousCache = builder.getCache();
+		const cache = this.cache || builder.globalCache;
 
 
-		builder.setCache( this.cache );
+		builder.setCache( cache );
 
 
 		const data = this.node.build( builder, ...params );
 		const data = this.node.build( builder, ...params );
 
 
@@ -40,7 +41,9 @@ class CacheNode extends Node {
 export default CacheNode;
 export default CacheNode;
 
 
 export const cache = nodeProxy( CacheNode );
 export const cache = nodeProxy( CacheNode );
+export const globalCache = ( node ) => cache( node, null );
 
 
 addNodeElement( 'cache', cache );
 addNodeElement( 'cache', cache );
+addNodeElement( 'globalCache', globalCache );
 
 
 addNodeClass( 'CacheNode', CacheNode );
 addNodeClass( 'CacheNode', CacheNode );

+ 6 - 1
examples/jsm/nodes/core/Node.js

@@ -458,7 +458,12 @@ export default Node;
 export function addNodeClass( type, nodeClass ) {
 export function addNodeClass( type, nodeClass ) {
 
 
 	if ( typeof nodeClass !== 'function' || ! type ) throw new Error( `Node class ${ type } is not a class` );
 	if ( typeof nodeClass !== 'function' || ! type ) throw new Error( `Node class ${ type } is not a class` );
-	if ( NodeClasses.has( type ) ) throw new Error( `Redefinition of node class ${ type }` );
+	if ( NodeClasses.has( type ) ) {
+
+		console.warn( `Redefinition of node class ${ type }` );
+		return;
+
+	}
 
 
 	NodeClasses.set( type, nodeClass );
 	NodeClasses.set( type, nodeClass );
 	nodeClass.type = type;
 	nodeClass.type = type;

+ 29 - 6
examples/jsm/nodes/core/NodeBuilder.js

@@ -6,6 +6,7 @@ import NodeCode from './NodeCode.js';
 import NodeKeywords from './NodeKeywords.js';
 import NodeKeywords from './NodeKeywords.js';
 import NodeCache from './NodeCache.js';
 import NodeCache from './NodeCache.js';
 import ParameterNode from './ParameterNode.js';
 import ParameterNode from './ParameterNode.js';
+import FunctionNode from '../code/FunctionNode.js';
 import { createNodeMaterialFromType } from '../materials/NodeMaterial.js';
 import { createNodeMaterialFromType } from '../materials/NodeMaterial.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 
 
@@ -42,8 +43,6 @@ const typeFromArray = new Map( [
 	[ Float32Array, 'float' ]
 	[ Float32Array, 'float' ]
 ] );
 ] );
 
 
-const isNonPaddingElementArray = new Set( [ Int32Array, Uint32Array, Float32Array ] );
-
 const toFloat = ( value ) => {
 const toFloat = ( value ) => {
 
 
 	value = Number( value );
 	value = Number( value );
@@ -95,6 +94,8 @@ class NodeBuilder {
 		this.stacks = [];
 		this.stacks = [];
 		this.tab = '\t';
 		this.tab = '\t';
 
 
+		this.currentFunctionNode = null;
+
 		this.context = {
 		this.context = {
 			keywords: new NodeKeywords(),
 			keywords: new NodeKeywords(),
 			material: this.material
 			material: this.material
@@ -552,7 +553,7 @@ class NodeBuilder {
 		if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data;
 		if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data;
 
 
 		const array = dataAttribute.array;
 		const array = dataAttribute.array;
-		const itemSize = isNonPaddingElementArray.has( array.constructor ) ? attribute.itemSize : dataAttribute.stride || attribute.itemSize;
+		const itemSize = attribute.itemSize;
 		const normalized = attribute.normalized;
 		const normalized = attribute.normalized;
 
 
 		let arrayType;
 		let arrayType;
@@ -625,9 +626,9 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
-	getDataFromNode( node, shaderStage = this.shaderStage ) {
+	getDataFromNode( node, shaderStage = this.shaderStage, cache = null ) {
 
 
-		const cache = node.isGlobal( this ) ? this.globalCache : this.cache;
+		cache = cache === null ? ( node.isGlobal( this ) ? this.globalCache : this.cache ) : cache;
 
 
 		let nodeData = cache.getNodeData( node );
 		let nodeData = cache.getNodeData( node );
 
 
@@ -696,7 +697,7 @@ class NodeBuilder {
 
 
 	getUniformFromNode( node, type, shaderStage = this.shaderStage, name = null ) {
 	getUniformFromNode( node, type, shaderStage = this.shaderStage, name = null ) {
 
 
-		const nodeData = this.getDataFromNode( node, shaderStage );
+		const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache );
 
 
 		let nodeUniform = nodeData.uniform;
 		let nodeUniform = nodeData.uniform;
 
 
@@ -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 ) {
 	flowShaderNode( shaderNode ) {
 
 
 		const layout = shaderNode.layout;
 		const layout = shaderNode.layout;
@@ -920,6 +937,12 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
+	getFunctionOperator() {
+
+		return null;
+
+	}
+
 	flowChildNode( node, output = null ) {
 	flowChildNode( node, output = null ) {
 
 
 		const previousFlow = this.flow;
 		const previousFlow = this.flow;

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

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

+ 2 - 2
examples/jsm/nodes/display/ColorAdjustmentNode.js

@@ -6,7 +6,7 @@ import { addNodeElement, tslFn, nodeProxy, float, vec3 } from '../shadernode/Sha
 
 
 const saturationNode = tslFn( ( { color, adjustment } ) => {
 const saturationNode = tslFn( ( { color, adjustment } ) => {
 
 
-	return adjustment.mix( luminance( color ), color );
+	return adjustment.mix( luminance( color.rgb ), color.rgb );
 
 
 } );
 } );
 
 
@@ -17,7 +17,7 @@ const vibranceNode = tslFn( ( { color, adjustment } ) => {
 	const mx = color.r.max( color.g.max( color.b ) );
 	const mx = color.r.max( color.g.max( color.b ) );
 	const amt = mx.sub( average ).mul( adjustment ).mul( - 3.0 );
 	const amt = mx.sub( average ).mul( adjustment ).mul( - 3.0 );
 
 
-	return mix( color, mx, amt );
+	return mix( color.rgb, mx, amt );
 
 
 } );
 } );
 
 

+ 170 - 0
examples/jsm/nodes/display/GaussianBlurNode.js

@@ -0,0 +1,170 @@
+import TempNode from '../core/TempNode.js';
+import { nodeObject, addNodeElement, tslFn, float, vec2, vec3, vec4 } from '../shadernode/ShaderNode.js';
+import { NodeUpdateType } from '../core/constants.js';
+import { mul } from '../math/OperatorNode.js';
+import { uv } from '../accessors/UVNode.js';
+import { texture } from '../accessors/TextureNode.js';
+import { uniform } from '../core/UniformNode.js';
+import { Vector2, RenderTarget } from 'three';
+import QuadMesh from '../../objects/QuadMesh.js';
+
+const quadMesh = new QuadMesh();
+
+class GaussianBlurNode extends TempNode {
+
+	constructor( textureNode, sigma = 2 ) {
+
+		super( textureNode );
+
+		this.textureNode = textureNode;
+		this.sigma = sigma;
+
+		this.directionNode = vec2( 1 );
+
+		this._invSize = uniform( new Vector2() );
+		this._passDirection = uniform( new Vector2() );
+
+		this._horizontalRT = new RenderTarget();
+		this._verticalRT = new RenderTarget();
+
+		this.updateBeforeType = NodeUpdateType.RENDER;
+
+		this.resolution = new Vector2( 1, 1 );
+
+	}
+
+	setSize( width, height ) {
+
+		width = Math.max( Math.round( width * this.resolution.x ), 1 );
+		height = Math.max( Math.round( height * this.resolution.y ), 1 );
+
+		this._invSize.value.set( 1 / width, 1 / height );
+		this._horizontalRT.setSize( width, height );
+		this._verticalRT.setSize( width, height );
+
+	}
+
+	updateBefore( frame ) {
+
+		const { renderer } = frame;
+
+		const textureNode = this.textureNode;
+		const map = textureNode.value;
+
+		const currentRenderTarget = renderer.getRenderTarget();
+		const currentTexture = textureNode.value;
+
+		quadMesh.material = this._material;
+
+		this.setSize( map.image.width, map.image.height );
+
+		// horizontal
+
+		renderer.setRenderTarget( this._horizontalRT );
+
+		this._passDirection.value.set( 1, 0 );
+
+		quadMesh.render( renderer );
+
+		// vertical
+
+		textureNode.value = this._horizontalRT.texture;
+		renderer.setRenderTarget( this._verticalRT );
+
+		this._passDirection.value.set( 0, 1 );
+
+		quadMesh.render( renderer );
+
+		// restore
+
+		renderer.setRenderTarget( currentRenderTarget );
+		textureNode.value = currentTexture;
+
+	}
+
+	setup( builder ) {
+
+		const textureNode = this.textureNode;
+
+		if ( textureNode.isTextureNode !== true ) {
+
+			console.error( 'GaussianBlurNode requires a TextureNode.' );
+
+			return vec4();
+
+		}
+
+		//
+
+		const uvNode = textureNode.uvNode || uv();
+
+		const sampleTexture = ( uv ) => textureNode.cache().context( { getUV: () => uv, forceUVContext: true } );
+
+		const blur = tslFn( () => {
+
+			const kernelSize = 3 + ( 2 * this.sigma );
+			const gaussianCoefficients = this._getCoefficients( kernelSize );
+
+			const invSize = this._invSize;
+			const direction = vec2( this.directionNode ).mul( this._passDirection );
+
+			const weightSum = float( gaussianCoefficients[ 0 ] ).toVar();
+			const diffuseSum = vec3( sampleTexture( uvNode ).mul( weightSum ) ).toVar();
+
+			for ( let i = 1; i < kernelSize; i ++ ) {
+
+				const x = float( i );
+				const w = float( gaussianCoefficients[ i ] );
+
+				const uvOffset = vec2( direction.mul( invSize.mul( x ) ) ).toVar();
+
+				const sample1 = vec3( sampleTexture( uvNode.add( uvOffset ) ) );
+				const sample2 = vec3( sampleTexture( uvNode.sub( uvOffset ) ) );
+
+				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
+				weightSum.addAssign( mul( 2.0, w ) );
+
+			}
+
+			return vec4( diffuseSum.div( weightSum ), 1.0 );
+
+		} );
+
+		//
+
+		const material = this._material || ( this._material = builder.createNodeMaterial( 'MeshBasicNodeMaterial' ) );
+		material.fragmentNode = blur();
+
+		//
+
+		const properties = builder.getNodeProperties( this );
+		properties.textureNode = textureNode;
+
+		//
+
+		return texture( this._verticalRT.texture );
+
+	}
+
+	_getCoefficients( kernelRadius ) {
+
+		const coefficients = [];
+
+		for ( let i = 0; i < kernelRadius; i ++ ) {
+
+			coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
+
+		}
+
+		return coefficients;
+
+	}
+
+}
+
+export const gaussianBlur = ( node, sigma ) => nodeObject( new GaussianBlurNode( nodeObject( node ), sigma ) );
+
+addNodeElement( 'gaussianBlur', gaussianBlur );
+
+export default GaussianBlurNode;
+

+ 182 - 0
examples/jsm/nodes/display/PassNode.js

@@ -0,0 +1,182 @@
+import { addNodeClass } from '../core/Node.js';
+import TempNode from '../core/TempNode.js';
+import TextureNode from '../accessors/TextureNode.js';
+import { NodeUpdateType } from '../core/constants.js';
+import { nodeObject } from '../shadernode/ShaderNode.js';
+import { uniform } from '../core/UniformNode.js';
+import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
+import { RenderTarget, Vector2, HalfFloatType, DepthTexture, FloatType, NoToneMapping } from 'three';
+
+class PassTextureNode extends TextureNode {
+
+	constructor( passNode, texture ) {
+
+		super( texture );
+
+		this.passNode = passNode;
+
+		this.setUpdateMatrix( false );
+
+	}
+
+	setup( builder ) {
+
+		this.passNode.build( builder );
+
+		return super.setup( builder );
+
+	}
+
+	clone() {
+
+		return new this.constructor( this.passNode, this.value );
+
+	}
+
+}
+
+class PassNode extends TempNode {
+
+	constructor( scope, scene, camera ) {
+
+		super( 'vec4' );
+
+		this.scope = scope;
+		this.scene = scene;
+		this.camera = camera;
+
+		this._pixelRatio = 1;
+		this._width = 1;
+		this._height = 1;
+
+		const depthTexture = new DepthTexture();
+		depthTexture.isRenderTargetTexture = true;
+		depthTexture.type = FloatType;
+		depthTexture.name = 'PostProcessingDepth';
+
+		const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType } );
+		renderTarget.texture.name = 'PostProcessing';
+		renderTarget.depthTexture = depthTexture;
+
+		this.renderTarget = renderTarget;
+
+		this.updateBeforeType = NodeUpdateType.FRAME;
+
+		this._textureNode = nodeObject( new PassTextureNode( this, renderTarget.texture ) );
+		this._depthTextureNode = nodeObject( new PassTextureNode( this, depthTexture ) );
+
+		this._depthNode = null;
+		this._cameraNear = uniform( 0 );
+		this._cameraFar = uniform( 0 );
+
+		this.isPassNode = true;
+
+	}
+
+	isGlobal() {
+
+		return true;
+
+	}
+
+	getTextureNode() {
+
+		return this._textureNode;
+
+	}
+
+	getTextureDepthNode() {
+
+		return this._depthTextureNode;
+
+	}
+
+	getDepthNode() {
+
+		if ( this._depthNode === null ) {
+
+			const cameraNear = this._cameraNear;
+			const cameraFar = this._cameraFar;
+
+			this._depthNode = viewZToOrthographicDepth( perspectiveDepthToViewZ( this._depthTextureNode, cameraNear, cameraFar ), cameraNear, cameraFar );
+
+		}
+
+		return this._depthNode;
+
+	}
+
+	setup() {
+
+		return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getDepthNode();
+
+	}
+
+	updateBefore( frame ) {
+
+		const { renderer } = frame;
+		const { scene, camera } = this;
+
+		this._pixelRatio = renderer.getPixelRatio();
+
+		const size = renderer.getSize( new Vector2() );
+
+		this.setSize( size.width, size.height );
+
+		const currentToneMapping = renderer.toneMapping;
+		const currentToneMappingNode = renderer.toneMappingNode;
+		const currentRenderTarget = renderer.getRenderTarget();
+
+		this._cameraNear.value = camera.near;
+		this._cameraFar.value = camera.far;
+
+		renderer.toneMapping = NoToneMapping;
+		renderer.toneMappingNode = null;
+		renderer.setRenderTarget( this.renderTarget );
+
+		renderer.render( scene, camera );
+
+		renderer.toneMapping = currentToneMapping;
+		renderer.toneMappingNode = currentToneMappingNode;
+		renderer.setRenderTarget( currentRenderTarget );
+
+	}
+
+	setSize( width, height ) {
+
+		this._width = width;
+		this._height = height;
+
+		const effectiveWidth = this._width * this._pixelRatio;
+		const effectiveHeight = this._height * this._pixelRatio;
+
+		this.renderTarget.setSize( effectiveWidth, effectiveHeight );
+
+	}
+
+	setPixelRatio( pixelRatio ) {
+
+		this._pixelRatio = pixelRatio;
+
+		this.setSize( this._width, this._height );
+
+	}
+
+	dispose() {
+
+		this.renderTarget.dispose();
+
+	}
+
+
+}
+
+PassNode.COLOR = 'color';
+PassNode.DEPTH = 'depth';
+
+export default PassNode;
+
+export const pass = ( scene, camera ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera ) );
+export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );
+
+addNodeClass( 'PassNode', PassNode );

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

@@ -371,7 +371,7 @@ class PhysicalLightingModel extends LightingModel {
 				f90: clearcoatF90
 				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 );
 			outgoingLight.assign( clearcoatLight );
 
 

+ 7 - 1
examples/jsm/nodes/lighting/LightsNode.js

@@ -173,7 +173,13 @@ export const lightNodes = nodeProxy( LightsNode );
 
 
 export function addLightNode( lightClass, lightNodeClass ) {
 export function addLightNode( lightClass, lightNodeClass ) {
 
 
-	if ( LightNodes.has( lightClass ) ) throw new Error( `Redefinition of light node ${ lightNodeClass.type }` );
+	if ( LightNodes.has( lightClass ) ) {
+
+		console.warn( `Redefinition of light node ${ lightNodeClass.type }` );
+		return;
+
+	}
+
 	if ( typeof lightClass !== 'function' ) throw new Error( `Light ${ lightClass.name } is not a class` );
 	if ( typeof lightClass !== 'function' ) throw new Error( `Light ${ lightClass.name } is not a class` );
 	if ( typeof lightNodeClass !== 'function' || ! lightNodeClass.type ) throw new Error( `Light node ${ lightNodeClass.type } is not a class` );
 	if ( typeof lightNodeClass !== 'function' || ! lightNodeClass.type ) throw new Error( `Light node ${ lightNodeClass.type } is not a class` );
 
 

+ 7 - 2
examples/jsm/nodes/materials/NodeMaterial.js

@@ -352,7 +352,7 @@ class NodeMaterial extends ShaderMaterial {
 
 
 		const toneMappingNode = builder.toneMappingNode;
 		const toneMappingNode = builder.toneMappingNode;
 
 
-		if ( toneMappingNode ) {
+		if ( this.toneMapped === true && toneMappingNode ) {
 
 
 			outputNode = vec4( toneMappingNode.context( { color: outputNode.rgb } ), outputNode.a );
 			outputNode = vec4( toneMappingNode.context( { color: outputNode.rgb } ), outputNode.a );
 
 
@@ -541,7 +541,12 @@ export default NodeMaterial;
 export function addNodeMaterial( type, nodeMaterial ) {
 export function addNodeMaterial( type, nodeMaterial ) {
 
 
 	if ( typeof nodeMaterial !== 'function' || ! type ) throw new Error( `Node material ${ type } is not a class` );
 	if ( typeof nodeMaterial !== 'function' || ! type ) throw new Error( `Node material ${ type } is not a class` );
-	if ( NodeMaterials.has( type ) ) throw new Error( `Redefinition of node material ${ type }` );
+	if ( NodeMaterials.has( type ) ) {
+
+		console.warn( `Redefinition of node material ${ type }` );
+		return;
+
+	}
 
 
 	NodeMaterials.set( type, nodeMaterial );
 	NodeMaterials.set( type, nodeMaterial );
 	nodeMaterial.type = type;
 	nodeMaterial.type = type;

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

@@ -1,5 +1,5 @@
 import {
 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_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_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
 	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 { mx_srgb_texture_to_lin_rec709 } from './lib/mx_transform_color.js';
 import { mix, smoothstep } from '../math/MathNode.js';
 import { mix, smoothstep } from '../math/MathNode.js';
 import { uv } from '../accessors/UVNode.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 ) => {
 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_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_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_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 ) => {
 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_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 };
 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
 // 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
 // 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.RECIPROCAL = 'reciprocal';
 MathNode.TRUNC = 'trunc';
 MathNode.TRUNC = 'trunc';
 MathNode.FWIDTH = 'fwidth';
 MathNode.FWIDTH = 'fwidth';
+MathNode.BITCAST = 'bitcast';
 
 
 // 2 inputs
 // 2 inputs
 
 
@@ -278,6 +279,7 @@ export const round = nodeProxy( MathNode, MathNode.ROUND );
 export const reciprocal = nodeProxy( MathNode, MathNode.RECIPROCAL );
 export const reciprocal = nodeProxy( MathNode, MathNode.RECIPROCAL );
 export const trunc = nodeProxy( MathNode, MathNode.TRUNC );
 export const trunc = nodeProxy( MathNode, MathNode.TRUNC );
 export const fwidth = nodeProxy( MathNode, MathNode.FWIDTH );
 export const fwidth = nodeProxy( MathNode, MathNode.FWIDTH );
+export const bitcast = nodeProxy( MathNode, MathNode.BITCAST );
 
 
 export const atan2 = nodeProxy( MathNode, MathNode.ATAN2 );
 export const atan2 = nodeProxy( MathNode, MathNode.ATAN2 );
 export const min = nodeProxy( MathNode, MathNode.MIN );
 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 b = bNode.build( builder, typeB );
 
 
 		const outputLength = builder.getTypeLength( output );
 		const outputLength = builder.getTypeLength( output );
+		const fnOpSnippet = builder.getFunctionOperator( op );
 
 
 		if ( output !== 'void' ) {
 		if ( output !== 'void' ) {
 
 
 			if ( op === '<' && outputLength > 1 ) {
 			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 ) {
 			} 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 ) {
 			} 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 ) {
 			} 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 {
 			} else {
 
 
-				return builder.format( `( ${a} ${this.op} ${b} )`, type, output );
+				return builder.format( `( ${ a } ${ op } ${ b } )`, type, output );
 
 
 			}
 			}
 
 
 		} else if ( typeA !== 'void' ) {
 		} 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 );
+
+			}
 
 
 		}
 		}
 
 

+ 32 - 3
examples/jsm/nodes/shadernode/ShaderNode.js

@@ -15,7 +15,13 @@ const NodeElements = new Map(); // @TODO: Currently only a few nodes are added,
 
 
 export function addNodeElement( name, nodeElement ) {
 export function addNodeElement( name, nodeElement ) {
 
 
-	if ( NodeElements.has( name ) ) throw new Error( `Redefinition of node element ${ name }` );
+	if ( NodeElements.has( name ) ) {
+
+		console.warn( `Redefinition of node element ${ name }` );
+		return;
+
+	}
+
 	if ( typeof nodeElement !== 'function' ) throw new Error( `Node element ${ name } is not a function` );
 	if ( typeof nodeElement !== 'function' ) throw new Error( `Node element ${ name } is not a function` );
 
 
 	NodeElements.set( name, nodeElement );
 	NodeElements.set( name, nodeElement );
@@ -40,7 +46,13 @@ const shaderNodeHandler = {
 
 
 			if ( node.isStackNode !== true && prop === 'assign' ) {
 			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 ) ) {
 			} else if ( NodeElements.has( prop ) ) {
 
 
@@ -271,6 +283,12 @@ class ShaderCallNodeInternal extends Node {
 
 
 			}
 			}
 
 
+			if ( builder.currentFunctionNode !== null ) {
+
+				builder.currentFunctionNode.includes.push( functionNode );
+
+			}
+
 			return nodeObject( functionNode.call( inputNodes ) );
 			return nodeObject( functionNode.call( inputNodes ) );
 
 
 		}
 		}
@@ -505,7 +523,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 getCurrentStack = () => currentStack;
 
 
 export const If = ( ...params ) => currentStack.if( ...params );
 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 );

+ 2 - 2
examples/jsm/nodes/utils/RemapNode.js

@@ -1,9 +1,9 @@
 import Node, { addNodeClass } from '../core/Node.js';
 import Node, { addNodeClass } from '../core/Node.js';
-import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
+import { float, addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 
 
 class RemapNode extends Node {
 class RemapNode extends Node {
 
 
-	constructor( node, inLowNode, inHighNode, outLowNode, outHighNode ) {
+	constructor( node, inLowNode, inHighNode, outLowNode = float( 0 ), outHighNode = float( 1 ) ) {
 
 
 		super();
 		super();
 
 

+ 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 ) {
 	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
 				// needed expand the input node
 
 
-				type = builder.getTypeFromLength( this.getVectorLength() );
+				type = builder.getTypeFromLength( this.getVectorLength(), this.getPrimitiveType( builder ) );
 
 
 			}
 			}
 
 

+ 3 - 3
examples/jsm/nodes/utils/TriplanarTexturesNode.js

@@ -1,13 +1,13 @@
 import Node, { addNodeClass } from '../core/Node.js';
 import Node, { addNodeClass } from '../core/Node.js';
 import { add } from '../math/OperatorNode.js';
 import { add } from '../math/OperatorNode.js';
-import { normalWorld } from '../accessors/NormalNode.js';
-import { positionWorld } from '../accessors/PositionNode.js';
+import { normalLocal } from '../accessors/NormalNode.js';
+import { positionLocal } from '../accessors/PositionNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { addNodeElement, nodeProxy, float, vec3 } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy, float, vec3 } from '../shadernode/ShaderNode.js';
 
 
 class TriplanarTexturesNode extends Node {
 class TriplanarTexturesNode extends Node {
 
 
-	constructor( textureXNode, textureYNode = null, textureZNode = null, scaleNode = float( 1 ), positionNode = positionWorld, normalNode = normalWorld ) {
+	constructor( textureXNode, textureYNode = null, textureZNode = null, scaleNode = float( 1 ), positionNode = positionLocal, normalNode = normalLocal ) {
 
 
 		super( 'vec4' );
 		super( 'vec4' );
 
 

+ 60 - 0
examples/jsm/objects/QuadMesh.js

@@ -0,0 +1,60 @@
+import { BufferGeometry, Float32BufferAttribute, Mesh, OrthographicCamera } from 'three';
+
+// Helper for passes that need to fill the viewport with a single quad.
+
+const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
+
+// https://github.com/mrdoob/three.js/pull/21358
+
+class QuadGeometry extends BufferGeometry {
+
+	constructor( flipY = false ) {
+
+		super();
+
+		const uv = flipY === false ? [ 0, - 1, 0, 1, 2, 1 ] : [ 0, 2, 0, 0, 2, 0 ];
+
+		this.setAttribute( 'position', new Float32BufferAttribute( [ - 1, 3, 0, - 1, - 1, 0, 3, - 1, 0 ], 3 ) );
+		this.setAttribute( 'uv', new Float32BufferAttribute( uv, 2 ) );
+
+	}
+
+}
+
+const _geometry = new QuadGeometry();
+
+class QuadMesh {
+
+	constructor( material = null ) {
+
+		this._mesh = new Mesh( _geometry, material );
+
+	}
+
+	dispose() {
+
+		this._mesh.geometry.dispose();
+
+	}
+
+	render( renderer ) {
+
+		renderer.render( this._mesh, _camera );
+
+	}
+
+	get material() {
+
+		return this._mesh.material;
+
+	}
+
+	set material( value ) {
+
+		this._mesh.material = value;
+
+	}
+
+}
+
+export default QuadMesh;

+ 180 - 109
examples/jsm/postprocessing/HBAOPass.js → examples/jsm/postprocessing/GTAOPass.js

@@ -3,8 +3,8 @@ import {
 	Color,
 	Color,
 	CustomBlending,
 	CustomBlending,
 	DataTexture,
 	DataTexture,
-	DepthStencilFormat,
 	DepthTexture,
 	DepthTexture,
+	DepthStencilFormat,
 	DstAlphaFactor,
 	DstAlphaFactor,
 	DstColorFactor,
 	DstColorFactor,
 	HalfFloatType,
 	HalfFloatType,
@@ -21,14 +21,14 @@ import {
 	ZeroFactor
 	ZeroFactor
 } from 'three';
 } from 'three';
 import { Pass, FullScreenQuad } from './Pass.js';
 import { Pass, FullScreenQuad } from './Pass.js';
-import { generateHaboSampleKernelInitializer, HBAOShader, HBAODepthShader } from '../shaders/HBAOShader.js';
+import { generateMagicSquareNoise, GTAOShader, GTAODepthShader, GTAOBlendShader } from '../shaders/GTAOShader.js';
 import { generatePdSamplePointInitializer, PoissonDenoiseShader } from '../shaders/PoissonDenoiseShader.js';
 import { generatePdSamplePointInitializer, PoissonDenoiseShader } from '../shaders/PoissonDenoiseShader.js';
 import { CopyShader } from '../shaders/CopyShader.js';
 import { CopyShader } from '../shaders/CopyShader.js';
 import { SimplexNoise } from '../math/SimplexNoise.js';
 import { SimplexNoise } from '../math/SimplexNoise.js';
 
 
-class HBAOPass extends Pass {
+class GTAOPass extends Pass {
 
 
-	constructor( scene, camera, width, height, parameters ) {
+	constructor( scene, camera, width, height, parameters, aoParameters, pdParameters ) {
 
 
 		super();
 		super();
 
 
@@ -40,32 +40,32 @@ class HBAOPass extends Pass {
 		this.output = 0;
 		this.output = 0;
 		this._renderGBuffer = true;
 		this._renderGBuffer = true;
 		this._visibilityCache = new Map();
 		this._visibilityCache = new Map();
+		this.blendIntensity = 1.;
 
 
-		this.rings = 4;
-		this.samples = 16;
+		this.pdRings = 2.;
+		this.pdRadiusExponent = 2.;
+		this.pdSamples = 16;
 
 
-		this.noiseTexture = this.generateNoise();
+		this.gtaoNoiseTexture = generateMagicSquareNoise();
+		this.pdNoiseTexture = this.generateNoise();
 
 
-		this.hbaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { type: HalfFloatType } );
-		this.pdRenderTarget = this.hbaoRenderTarget.clone();
+		this.gtaoRenderTarget = new WebGLRenderTarget( this.width, this.height, { type: HalfFloatType } );
+		this.pdRenderTarget = this.gtaoRenderTarget.clone();
 
 
-		this.hbaoMaterial = new ShaderMaterial( {
-			defines: Object.assign( {}, HBAOShader.defines ),
-			uniforms: UniformsUtils.clone( HBAOShader.uniforms ),
-			vertexShader: HBAOShader.vertexShader,
-			fragmentShader: HBAOShader.fragmentShader,
+		this.gtaoMaterial = new ShaderMaterial( {
+			defines: Object.assign( {}, GTAOShader.defines ),
+			uniforms: UniformsUtils.clone( GTAOShader.uniforms ),
+			vertexShader: GTAOShader.vertexShader,
+			fragmentShader: GTAOShader.fragmentShader,
 			blending: NoBlending,
 			blending: NoBlending,
 			depthTest: false,
 			depthTest: false,
 			depthWrite: false,
 			depthWrite: false,
 		} );
 		} );
-		this.hbaoMaterial.defines[ 'PERSPECTIVE_CAMERA' ] = this.camera.isPerspectiveCamera ? 1 : 0;
-		this.hbaoMaterial.uniforms[ 'tNoise' ].value = this.noiseTexture;
-		this.hbaoMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
-		this.hbaoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
-		this.hbaoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
-		this.hbaoMaterial.uniforms[ 'radius' ].value = 2;
-		this.hbaoMaterial.uniforms[ 'distanceExponent' ].value = 2;
-		this.hbaoMaterial.uniforms[ 'bias' ].value = 0.01;
+		this.gtaoMaterial.definesPERSPECTIVE_CAMERA = this.camera.isPerspectiveCamera ? 1 : 0;
+		this.gtaoMaterial.uniforms.tNoise.value = this.gtaoNoiseTexture;
+		this.gtaoMaterial.uniforms.resolution.value.set( this.width, this.height );
+		this.gtaoMaterial.uniforms.cameraNear.value = this.camera.near;
+		this.gtaoMaterial.uniforms.cameraFar.value = this.camera.far;
 
 
 		this.normalMaterial = new MeshNormalMaterial();
 		this.normalMaterial = new MeshNormalMaterial();
 		this.normalMaterial.blending = NoBlending;
 		this.normalMaterial.blending = NoBlending;
@@ -78,22 +78,23 @@ class HBAOPass extends Pass {
 			depthTest: false,
 			depthTest: false,
 			depthWrite: false,
 			depthWrite: false,
 		} );
 		} );
-		this.pdMaterial.uniforms[ 'tDiffuse' ].value = this.hbaoRenderTarget.texture;
-		this.pdMaterial.uniforms[ 'tNoise' ].value = this.noiseTexture;
-		this.pdMaterial.uniforms[ 'resolution' ].value.set( this.width, this.height );
-		this.pdMaterial.uniforms[ 'lumaPhi' ].value = 10;
-		this.pdMaterial.uniforms[ 'depthPhi' ].value = 2;
-		this.pdMaterial.uniforms[ 'normalPhi' ].value = 3;
+		this.pdMaterial.uniforms.tDiffuse.value = this.gtaoRenderTarget.texture;
+		this.pdMaterial.uniforms.tNoise.value = this.pdNoiseTexture;
+		this.pdMaterial.uniforms.resolution.value.set( this.width, this.height );
+		this.pdMaterial.uniforms.lumaPhi.value = 10;
+		this.pdMaterial.uniforms.depthPhi.value = 2;
+		this.pdMaterial.uniforms.normalPhi.value = 3;
+		this.pdMaterial.uniforms.radius.value = 8;
 
 
 		this.depthRenderMaterial = new ShaderMaterial( {
 		this.depthRenderMaterial = new ShaderMaterial( {
-			defines: Object.assign( {}, HBAODepthShader.defines ),
-			uniforms: UniformsUtils.clone( HBAODepthShader.uniforms ),
-			vertexShader: HBAODepthShader.vertexShader,
-			fragmentShader: HBAODepthShader.fragmentShader,
+			defines: Object.assign( {}, GTAODepthShader.defines ),
+			uniforms: UniformsUtils.clone( GTAODepthShader.uniforms ),
+			vertexShader: GTAODepthShader.vertexShader,
+			fragmentShader: GTAODepthShader.fragmentShader,
 			blending: NoBlending
 			blending: NoBlending
 		} );
 		} );
-		this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
-		this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+		this.depthRenderMaterial.uniforms.cameraNear.value = this.camera.near;
+		this.depthRenderMaterial.uniforms.cameraFar.value = this.camera.far;
 
 
 		this.copyMaterial = new ShaderMaterial( {
 		this.copyMaterial = new ShaderMaterial( {
 			uniforms: UniformsUtils.clone( CopyShader.uniforms ),
 			uniforms: UniformsUtils.clone( CopyShader.uniforms ),
@@ -110,19 +111,48 @@ class HBAOPass extends Pass {
 			blendEquationAlpha: AddEquation
 			blendEquationAlpha: AddEquation
 		} );
 		} );
 
 
+		this.blendMaterial = new ShaderMaterial( {
+			uniforms: UniformsUtils.clone( GTAOBlendShader.uniforms ),
+			vertexShader: GTAOBlendShader.vertexShader,
+			fragmentShader: GTAOBlendShader.fragmentShader,
+			transparent: true,
+			depthTest: false,
+			depthWrite: false,
+			blending: CustomBlending,
+			blendSrc: DstColorFactor,
+			blendDst: ZeroFactor,
+			blendEquation: AddEquation,
+			blendSrcAlpha: DstAlphaFactor,
+			blendDstAlpha: ZeroFactor,
+			blendEquationAlpha: AddEquation
+		} );
+
 		this.fsQuad = new FullScreenQuad( null );
 		this.fsQuad = new FullScreenQuad( null );
 
 
 		this.originalClearColor = new Color();
 		this.originalClearColor = new Color();
 
 
-		this.setTextures( parameters ? parameters.depthTexture : undefined, parameters ? parameters.normalTexture : undefined );
+		this.setGBuffer( parameters ? parameters.depthTexture : undefined, parameters ? parameters.normalTexture : undefined );
+
+		if ( aoParameters !== undefined ) {
+
+			this.updateGtaoMaterial( aoParameters );
+
+		}
+
+		if ( pdParameters !== undefined ) {
+
+			this.updatePdMaterial( pdParameters );
+
+		}
 
 
 	}
 	}
 
 
 	dispose() {
 	dispose() {
 
 
-		this.noiseTexture.dispose();
+		this.gtaoNoiseTexture.dispose();
+		this.pdNoiseTexture.dispose();
 		this.normalRenderTarget.dispose();
 		this.normalRenderTarget.dispose();
-		this.hbaoRenderTarget.dispose();
+		this.gtaoRenderTarget.dispose();
 		this.pdRenderTarget.dispose();
 		this.pdRenderTarget.dispose();
 		this.normalMaterial.dispose();
 		this.normalMaterial.dispose();
 		this.pdMaterial.dispose();
 		this.pdMaterial.dispose();
@@ -132,7 +162,7 @@ class HBAOPass extends Pass {
 
 
 	}
 	}
 
 
-	setTextures( depthTexture, normalTexture ) {
+	setGBuffer( depthTexture, normalTexture ) {
 
 
 		if ( depthTexture !== undefined ) {
 		if ( depthTexture !== undefined ) {
 
 
@@ -145,7 +175,6 @@ class HBAOPass extends Pass {
 			this.depthTexture = new DepthTexture();
 			this.depthTexture = new DepthTexture();
 			this.depthTexture.format = DepthStencilFormat;
 			this.depthTexture.format = DepthStencilFormat;
 			this.depthTexture.type = UnsignedInt248Type;
 			this.depthTexture.type = UnsignedInt248Type;
-
 			this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
 			this.normalRenderTarget = new WebGLRenderTarget( this.width, this.height, {
 				minFilter: NearestFilter,
 				minFilter: NearestFilter,
 				magFilter: NearestFilter,
 				magFilter: NearestFilter,
@@ -158,47 +187,84 @@ class HBAOPass extends Pass {
 		}
 		}
 
 
 		const normalVectorType = ( this.normalTexture ) ? 1 : 0;
 		const normalVectorType = ( this.normalTexture ) ? 1 : 0;
-		const depthValueSource = ( this.depthTexture === this.normalTexture ) ? 1 : 0;
+		const depthValueSource = ( this.depthTexture === this.normalTexture ) ? 'w' : 'x';
 
 
-		this.hbaoMaterial.defines[ 'NORMAL_VECTOR_TYPE' ] = normalVectorType;
-		this.hbaoMaterial.defines[ 'DEPTH_VALUE_SOURCE' ] = depthValueSource;
-		this.hbaoMaterial.uniforms[ 'tNormal' ].value = this.normalTexture;
-		this.hbaoMaterial.uniforms[ 'tDepth' ].value = this.depthTexture;
+		this.gtaoMaterial.defines.NORMAL_VECTOR_TYPE = normalVectorType;
+		this.gtaoMaterial.defines.DEPTH_SWIZZLING = depthValueSource;
+		this.gtaoMaterial.uniforms.tNormal.value = this.normalTexture;
+		this.gtaoMaterial.uniforms.tDepth.value = this.depthTexture;
 
 
-		this.pdMaterial.defines[ 'NORMAL_VECTOR_TYPE' ] = normalVectorType;
-		this.pdMaterial.defines[ 'DEPTH_VALUE_SOURCE' ] = depthValueSource;
-		this.pdMaterial.uniforms[ 'tNormal' ].value = this.normalTexture;
-		this.pdMaterial.uniforms[ 'tDepth' ].value = this.depthTexture;
+		this.pdMaterial.defines.NORMAL_VECTOR_TYPE = normalVectorType;
+		this.pdMaterial.defines.DEPTH_SWIZZLING = depthValueSource;
+		this.pdMaterial.uniforms.tNormal.value = this.normalTexture;
+		this.pdMaterial.uniforms.tDepth.value = this.depthTexture;
 
 
-		this.depthRenderMaterial.uniforms[ 'tDepth' ].value = this.normalRenderTarget.depthTexture;
+		this.depthRenderMaterial.uniforms.tDepth.value = this.normalRenderTarget.depthTexture;
 
 
 	}
 	}
 
 
-	updateHbaoMaterial( parameters ) {
+	setSceneClipBox( box ) {
+
+		if ( box ) {
+
+			this.gtaoMaterial.needsUpdate = this.gtaoMaterial.defines.SCENE_CLIP_BOX !== 1;
+			this.gtaoMaterial.defines.SCENE_CLIP_BOX = 1;
+			this.gtaoMaterial.uniforms.sceneBoxMin.value.copy( box.min );
+			this.gtaoMaterial.uniforms.sceneBoxMax.value.copy( box.max );
+
+		} else {
+
+			this.gtaoMaterial.needsUpdate = this.gtaoMaterial.defines.SCENE_CLIP_BOX === 0;
+			this.gtaoMaterial.defines.SCENE_CLIP_BOX = 0;
+
+		}
+
+	}
+
+	updateGtaoMaterial( parameters ) {
 
 
 		if ( parameters.radius !== undefined ) {
 		if ( parameters.radius !== undefined ) {
 
 
-			this.hbaoMaterial.uniforms[ 'radius' ].value = parameters.radius;
+			this.gtaoMaterial.uniforms.radius.value = parameters.radius;
 
 
 		}
 		}
 
 
 		if ( parameters.distanceExponent !== undefined ) {
 		if ( parameters.distanceExponent !== undefined ) {
 
 
-			this.hbaoMaterial.uniforms[ 'distanceExponent' ].value = parameters.distanceExponent;
+			this.gtaoMaterial.uniforms.distanceExponent.value = parameters.distanceExponent;
+
+		}
+
+		if ( parameters.thickness !== undefined ) {
+
+			this.gtaoMaterial.uniforms.thickness.value = parameters.thickness;
+
+		}
+
+		if ( parameters.distanceFallOff !== undefined ) {
+
+			this.gtaoMaterial.uniforms.distanceFallOff.value = parameters.distanceFallOff;
+			this.gtaoMaterial.needsUpdate = true;
 
 
 		}
 		}
 
 
-		if ( parameters.bias !== undefined ) {
+		if ( parameters.scale !== undefined ) {
 
 
-			this.hbaoMaterial.uniforms[ 'bias' ].value = parameters.bias;
+			this.gtaoMaterial.uniforms.scale.value = parameters.scale;
 
 
 		}
 		}
 
 
-		if ( parameters.samples !== undefined && parameters.samples !== this.hbaoMaterial.defines[ 'SAMPLES' ] ) {
+		if ( parameters.samples !== undefined && parameters.samples !== this.gtaoMaterial.defines.SAMPLES ) {
 
 
-			this.hbaoMaterial.defines[ 'SAMPLES' ] = parameters.samples;
-			this.hbaoMaterial.defines[ 'SAMPLE_VECTORS' ] = generateHaboSampleKernelInitializer( parameters.samples );
-			this.hbaoMaterial.needsUpdate = true;
+			this.gtaoMaterial.defines.SAMPLES = parameters.samples;
+			this.gtaoMaterial.needsUpdate = true;
+
+		}
+
+		if ( parameters.screenSpaceRadius !== undefined && ( parameters.screenSpaceRadius ? 1 : 0 ) !== this.gtaoMaterial.defines.SCREEN_SPACE_RADIUS ) {
+
+			this.gtaoMaterial.defines.SCREEN_SPACE_RADIUS = parameters.screenSpaceRadius ? 1 : 0;
+			this.gtaoMaterial.needsUpdate = true;
 
 
 		}
 		}
 
 
@@ -210,47 +276,53 @@ class HBAOPass extends Pass {
 
 
 		if ( parameters.lumaPhi !== undefined ) {
 		if ( parameters.lumaPhi !== undefined ) {
 
 
-			this.pdMaterial.uniforms[ 'lumaPhi' ].value = parameters.lumaPhi;
+			this.pdMaterial.uniforms.lumaPhi.value = parameters.lumaPhi;
 
 
 		}
 		}
 
 
 		if ( parameters.depthPhi !== undefined ) {
 		if ( parameters.depthPhi !== undefined ) {
 
 
-			this.pdMaterial.uniforms[ 'depthPhi' ].value = parameters.depthPhi;
+			this.pdMaterial.uniforms.depthPhi.value = parameters.depthPhi;
 
 
 		}
 		}
 
 
 		if ( parameters.normalPhi !== undefined ) {
 		if ( parameters.normalPhi !== undefined ) {
 
 
-			this.pdMaterial.uniforms[ 'normalPhi' ].value = parameters.normalPhi;
+			this.pdMaterial.uniforms.normalPhi.value = parameters.normalPhi;
 
 
 		}
 		}
 
 
 		if ( parameters.radius !== undefined && parameters.radius !== this.radius ) {
 		if ( parameters.radius !== undefined && parameters.radius !== this.radius ) {
 
 
-			this.pdMaterial.uniforms[ 'radius' ].value = parameters.radius;
+			this.pdMaterial.uniforms.radius.value = parameters.radius;
 
 
 		}
 		}
 
 
-		if ( parameters.rings !== undefined && parameters.rings !== this.rings ) {
+		if ( parameters.radiusExponent !== undefined && parameters.radiusExponent !== this.pdRadiusExponent ) {
 
 
-			this.rings = parameters.rings;
+			this.pdRadiusExponent = parameters.radiusExponent;
 			updateShader = true;
 			updateShader = true;
 
 
 		}
 		}
 
 
-		if ( parameters.samples !== undefined && parameters.samples !== this.samples ) {
+		if ( parameters.rings !== undefined && parameters.rings !== this.pdRings ) {
 
 
-			this.samples = parameters.samples;
+			this.pdRings = parameters.rings;
 			updateShader = true;
 			updateShader = true;
 
 
 		}
 		}
 
 
-		if ( updateShader ) {
+		if ( parameters.samples !== undefined && parameters.samples !== this.pdSamples ) {
 
 
+			this.pdSamples = parameters.samples;
+			updateShader = true;
 
 
-			this.pdMaterial.defines[ 'SAMPLES' ] = parameters.samples;
-			this.pdMaterial.defines[ 'SAMPLE_VECTORS' ] = generatePdSamplePointInitializer( parameters.samples, this.rings );
+		}
+
+		if ( updateShader ) {
+
+			this.pdMaterial.defines.SAMPLES = this.pdSamples;
+			this.pdMaterial.defines.SAMPLE_VECTORS = generatePdSamplePointInitializer( this.pdSamples, this.pdRings, this.pdRadiusExponent );
 			this.pdMaterial.needsUpdate = true;
 			this.pdMaterial.needsUpdate = true;
 
 
 		}
 		}
@@ -259,7 +331,7 @@ class HBAOPass extends Pass {
 
 
 	render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
 	render( renderer, writeBuffer, readBuffer /*, deltaTime, maskActive */ ) {
 
 
-		// render normals and depth (honor only meshes, points and lines do not contribute to HBAO)
+		// render normals and depth (honor only meshes, points and lines do not contribute to AO)
 
 
 		if ( this._renderGBuffer ) {
 		if ( this._renderGBuffer ) {
 
 
@@ -269,77 +341,78 @@ class HBAOPass extends Pass {
 
 
 		}
 		}
 
 
-		// render HBAO
+		// render AO
 
 
-		this.hbaoMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
-		this.hbaoMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
-		this.hbaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
-		this.hbaoMaterial.uniforms[ 'cameraProjectionMatrixInverse' ].value.copy( this.camera.projectionMatrixInverse );
-		this.renderPass( renderer, this.hbaoMaterial, this.hbaoRenderTarget, 0xffffff, 1.0 );
+		this.gtaoMaterial.uniforms.cameraNear.value = this.camera.near;
+		this.gtaoMaterial.uniforms.cameraFar.value = this.camera.far;
+		this.gtaoMaterial.uniforms.cameraProjectionMatrix.value.copy( this.camera.projectionMatrix );
+		this.gtaoMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
+		this.gtaoMaterial.uniforms.cameraWorldMatrix.value.copy( this.camera.matrixWorld );
+		this.renderPass( renderer, this.gtaoMaterial, this.gtaoRenderTarget, 0xffffff, 1.0 );
 
 
 		// render poisson denoise
 		// render poisson denoise
 
 
-		this.pdMaterial.uniforms[ 'cameraProjectionMatrixInverse' ].value.copy( this.camera.projectionMatrixInverse );
+		this.pdMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
 		this.renderPass( renderer, this.pdMaterial, this.pdRenderTarget, 0xffffff, 1.0 );
 		this.renderPass( renderer, this.pdMaterial, this.pdRenderTarget, 0xffffff, 1.0 );
 
 
 		// output result to screen
 		// output result to screen
 
 
 		switch ( this.output ) {
 		switch ( this.output ) {
 
 
-			case HBAOPass.OUTPUT.Diffuse:
+			case GTAOPass.OUTPUT.Diffuse:
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
+				this.copyMaterial.uniforms.tDiffuse.value = readBuffer.texture;
 				this.copyMaterial.blending = NoBlending;
 				this.copyMaterial.blending = NoBlending;
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
-			case HBAOPass.OUTPUT.HBAO:
+			case GTAOPass.OUTPUT.AO:
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.hbaoRenderTarget.texture;
+				this.copyMaterial.uniforms.tDiffuse.value = this.gtaoRenderTarget.texture;
 				this.copyMaterial.blending = NoBlending;
 				this.copyMaterial.blending = NoBlending;
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
-			case HBAOPass.OUTPUT.Denoise:
+			case GTAOPass.OUTPUT.Denoise:
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.pdRenderTarget.texture;
+				this.copyMaterial.uniforms.tDiffuse.value = this.pdRenderTarget.texture;
 				this.copyMaterial.blending = NoBlending;
 				this.copyMaterial.blending = NoBlending;
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
-			case HBAOPass.OUTPUT.Depth:
+			case GTAOPass.OUTPUT.Depth:
 
 
-				this.depthRenderMaterial.uniforms[ 'cameraNear' ].value = this.camera.near;
-				this.depthRenderMaterial.uniforms[ 'cameraFar' ].value = this.camera.far;
+				this.depthRenderMaterial.uniforms.cameraNear.value = this.camera.near;
+				this.depthRenderMaterial.uniforms.cameraFar.value = this.camera.far;
 				this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.depthRenderMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
-			case HBAOPass.OUTPUT.Normal:
+			case GTAOPass.OUTPUT.Normal:
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.normalRenderTarget.texture;
+				this.copyMaterial.uniforms.tDiffuse.value = this.normalRenderTarget.texture;
 				this.copyMaterial.blending = NoBlending;
 				this.copyMaterial.blending = NoBlending;
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
-			case HBAOPass.OUTPUT.Default:
+			case GTAOPass.OUTPUT.Default:
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = readBuffer.texture;
+				this.copyMaterial.uniforms.tDiffuse.value = readBuffer.texture;
 				this.copyMaterial.blending = NoBlending;
 				this.copyMaterial.blending = NoBlending;
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
 
 
-				this.copyMaterial.uniforms[ 'tDiffuse' ].value = this.pdRenderTarget.texture;
-				this.copyMaterial.blending = CustomBlending;
-				this.renderPass( renderer, this.copyMaterial, this.renderToScreen ? null : writeBuffer );
+				this.blendMaterial.uniforms.intensity.value = this.blendIntensity;
+				this.blendMaterial.uniforms.tDiffuse.value = this.pdRenderTarget.texture;
+				this.renderPass( renderer, this.blendMaterial, this.renderToScreen ? null : writeBuffer );
 
 
 				break;
 				break;
 
 
 			default:
 			default:
-				console.warn( 'THREE.HBAOPass: Unknown output type.' );
+				console.warn( 'THREE.GTAOPass: Unknown output type.' );
 
 
 		}
 		}
 
 
@@ -398,8 +471,6 @@ class HBAOPass extends Pass {
 		renderer.render( this.scene, this.camera );
 		renderer.render( this.scene, this.camera );
 		this.scene.overrideMaterial = null;
 		this.scene.overrideMaterial = null;
 
 
-		// restore original state
-
 		renderer.autoClear = originalAutoClear;
 		renderer.autoClear = originalAutoClear;
 		renderer.setClearColor( this.originalClearColor );
 		renderer.setClearColor( this.originalClearColor );
 		renderer.setClearAlpha( originalClearAlpha );
 		renderer.setClearAlpha( originalClearAlpha );
@@ -411,16 +482,16 @@ class HBAOPass extends Pass {
 		this.width = width;
 		this.width = width;
 		this.height = height;
 		this.height = height;
 
 
-		this.hbaoRenderTarget.setSize( width, height );
+		this.gtaoRenderTarget.setSize( width, height );
 		this.normalRenderTarget.setSize( width, height );
 		this.normalRenderTarget.setSize( width, height );
 		this.pdRenderTarget.setSize( width, height );
 		this.pdRenderTarget.setSize( width, height );
 
 
-		this.hbaoMaterial.uniforms[ 'resolution' ].value.set( width, height );
-		this.hbaoMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
-		this.hbaoMaterial.uniforms[ 'cameraProjectionMatrixInverse' ].value.copy( this.camera.projectionMatrixInverse );
+		this.gtaoMaterial.uniforms.resolution.value.set( width, height );
+		this.gtaoMaterial.uniforms.cameraProjectionMatrix.value.copy( this.camera.projectionMatrix );
+		this.gtaoMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
 
 
-		this.pdMaterial.uniforms[ 'resolution' ].value.set( width, height );
-		this.pdMaterial.uniforms[ 'cameraProjectionMatrixInverse' ].value.copy( this.camera.projectionMatrixInverse );
+		this.pdMaterial.uniforms.resolution.value.set( width, height );
+		this.pdMaterial.uniforms.cameraProjectionMatrixInverse.value.copy( this.camera.projectionMatrixInverse );
 
 
 	}
 	}
 
 
@@ -469,10 +540,10 @@ class HBAOPass extends Pass {
 				const x = i;
 				const x = i;
 				const y = j;
 				const y = j;
 
 
-				data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) + 1.0 ) * 255.0;
-				data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) + 1.0 ) * 255.0;
-				data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) + 1.0 ) * 255.0;
-				data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) + 1.0 ) * 255.0;
+				data[ ( i * size + j ) * 4 ] = ( simplex.noise( x, y ) * 0.5 + 0.5 ) * 255;
+				data[ ( i * size + j ) * 4 + 1 ] = ( simplex.noise( x + size, y ) * 0.5 + 0.5 ) * 255;
+				data[ ( i * size + j ) * 4 + 2 ] = ( simplex.noise( x, y + size ) * 0.5 + 0.5 ) * 255;
+				data[ ( i * size + j ) * 4 + 3 ] = ( simplex.noise( x + size, y + size ) * 0.5 + 0.5 ) * 255;
 
 
 			}
 			}
 
 
@@ -489,13 +560,13 @@ class HBAOPass extends Pass {
 
 
 }
 }
 
 
-HBAOPass.OUTPUT = {
+GTAOPass.OUTPUT = {
 	'Default': 0,
 	'Default': 0,
 	'Diffuse': 1,
 	'Diffuse': 1,
 	'Depth': 2,
 	'Depth': 2,
 	'Normal': 3,
 	'Normal': 3,
-	'HBAO': 4,
+	'AO': 4,
 	'Denoise': 5,
 	'Denoise': 5,
 };
 };
 
 
-export { HBAOPass };
+export { GTAOPass };

+ 2 - 0
examples/jsm/postprocessing/OutputPass.js

@@ -5,6 +5,7 @@ import {
 	LinearToneMapping,
 	LinearToneMapping,
 	ReinhardToneMapping,
 	ReinhardToneMapping,
 	CineonToneMapping,
 	CineonToneMapping,
+	AgXToneMapping,
 	ACESFilmicToneMapping,
 	ACESFilmicToneMapping,
 	SRGBTransfer
 	SRGBTransfer
 } from 'three';
 } from 'three';
@@ -59,6 +60,7 @@ class OutputPass extends Pass {
 			else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
 			else if ( this._toneMapping === ReinhardToneMapping ) this.material.defines.REINHARD_TONE_MAPPING = '';
 			else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
 			else if ( this._toneMapping === CineonToneMapping ) this.material.defines.CINEON_TONE_MAPPING = '';
 			else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
 			else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
+			else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = '';
 
 
 			this.material.needsUpdate = true;
 			this.material.needsUpdate = true;
 
 

+ 8 - 10
examples/jsm/renderers/common/Background.js

@@ -1,7 +1,7 @@
 import DataMap from './DataMap.js';
 import DataMap from './DataMap.js';
 import Color4 from './Color4.js';
 import Color4 from './Color4.js';
 import { Mesh, SphereGeometry, BackSide } from 'three';
 import { Mesh, SphereGeometry, BackSide } from 'three';
-import { context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
+import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js';
 
 
 const _clearColor = new Color4();
 const _clearColor = new Color4();
 
 
@@ -14,9 +14,6 @@ class Background extends DataMap {
 		this.renderer = renderer;
 		this.renderer = renderer;
 		this.nodes = nodes;
 		this.nodes = nodes;
 
 
-		this.backgroundMesh = null;
-		this.backgroundMeshNode = null;
-
 	}
 	}
 
 
 	update( scene, renderList, renderContext ) {
 	update( scene, renderList, renderContext ) {
@@ -49,11 +46,11 @@ class Background extends DataMap {
 
 
 			_clearColor.copy( renderer._clearColor );
 			_clearColor.copy( renderer._clearColor );
 
 
-			let backgroundMesh = this.backgroundMesh;
+			let backgroundMesh = sceneData.backgroundMesh;
 
 
-			if ( backgroundMesh === null ) {
+			if ( backgroundMesh === undefined ) {
 
 
-				this.backgroundMeshNode = context( backgroundNode, {
+				const backgroundMeshNode = context( vec4( backgroundNode ), {
 					// @TODO: Add Texture2D support using node context
 					// @TODO: Add Texture2D support using node context
 					getUV: () => normalWorld,
 					getUV: () => normalWorld,
 					getTextureLevel: () => backgroundBlurriness
 					getTextureLevel: () => backgroundBlurriness
@@ -68,9 +65,10 @@ class Background extends DataMap {
 				nodeMaterial.depthWrite = false;
 				nodeMaterial.depthWrite = false;
 				nodeMaterial.fog = false;
 				nodeMaterial.fog = false;
 				nodeMaterial.vertexNode = viewProj;
 				nodeMaterial.vertexNode = viewProj;
-				nodeMaterial.fragmentNode = this.backgroundMeshNode;
+				nodeMaterial.fragmentNode = backgroundMeshNode;
 
 
-				this.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
+				sceneData.backgroundMeshNode = backgroundMeshNode;
+				sceneData.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial );
 				backgroundMesh.frustumCulled = false;
 				backgroundMesh.frustumCulled = false;
 
 
 				backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) {
 				backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) {
@@ -85,7 +83,7 @@ class Background extends DataMap {
 
 
 			if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
 			if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
 
 
-				this.backgroundMeshNode.node = backgroundNode;
+				sceneData.backgroundMeshNode.node = vec4( backgroundNode );
 
 
 				backgroundMesh.material.needsUpdate = true;
 				backgroundMesh.material.needsUpdate = true;
 
 

Some files were not shown because too many files changed in this diff