Mr.doob 8 months ago
parent
commit
6881384a15
100 changed files with 4268 additions and 1964 deletions
  1. 565 100
      build/three.cjs
  2. 565 100
      build/three.core.js
  3. 0 0
      build/three.core.min.js
  4. 0 0
      build/three.module.js
  5. 0 0
      build/three.module.min.js
  6. 54 20
      build/three.tsl.js
  7. 0 0
      build/three.tsl.min.js
  8. 0 0
      build/three.webgpu.js
  9. 0 0
      build/three.webgpu.min.js
  10. 0 0
      build/three.webgpu.nodes.js
  11. 0 0
      build/three.webgpu.nodes.min.js
  12. 1 1
      docs/api/ar/animation/AnimationClip.html
  13. 1 1
      docs/api/en/animation/AnimationClip.html
  14. 7 4
      docs/api/en/animation/KeyframeTrack.html
  15. 1 12
      docs/api/en/core/Timer.html
  16. 4 0
      docs/api/en/helpers/SkeletonHelper.html
  17. 16 0
      docs/api/en/textures/Texture.html
  18. 1 1
      docs/api/fr/animation/AnimationClip.html
  19. 1 1
      docs/api/ko/animation/AnimationClip.html
  20. 1 1
      docs/api/ko/cameras/ArrayCamera.html
  21. 1 1
      docs/api/pt-br/animation/AnimationClip.html
  22. 1 1
      docs/api/zh/animation/AnimationClip.html
  23. 1 11
      docs/api/zh/core/Timer.html
  24. 86 0
      docs/examples/en/exporters/KTX2Exporter.html
  25. 130 0
      docs/examples/en/exporters/USDZExporter.html
  26. 5 9
      docs/list.json
  27. 25 2
      editor/js/Loader.js
  28. 10 3
      editor/js/Viewport.Info.js
  29. 37 7
      editor/js/libs/ui.three.js
  30. 8 4
      editor/sw.js
  31. 3 0
      examples/files.json
  32. 1 2
      examples/jsm/Addons.js
  33. 1 1
      examples/jsm/capabilities/WebGPU.js
  34. 4 4
      examples/jsm/csm/CSMShadowNode.js
  35. 8 3
      examples/jsm/environments/RoomEnvironment.js
  36. 573 246
      examples/jsm/exporters/USDZExporter.js
  37. 47 8
      examples/jsm/geometries/RoundedBoxGeometry.js
  38. 5 3
      examples/jsm/interactive/HTMLMesh.js
  39. 0 6
      examples/jsm/libs/meshopt_decoder.module.js
  40. 14 3
      examples/jsm/lights/LightProbeGenerator.js
  41. 210 22
      examples/jsm/loaders/EXRLoader.js
  42. 1 1
      examples/jsm/loaders/FBXLoader.js
  43. 212 30
      examples/jsm/loaders/MaterialXLoader.js
  44. 13 1
      examples/jsm/loaders/TTFLoader.js
  45. 219 0
      examples/jsm/loaders/USDLoader.js
  46. 4 892
      examples/jsm/loaders/USDZLoader.js
  47. 741 0
      examples/jsm/loaders/usd/USDAParser.js
  48. 17 0
      examples/jsm/loaders/usd/USDCParser.js
  49. 0 222
      examples/jsm/misc/Timer.js
  50. 3 3
      examples/jsm/objects/LensflareMesh.js
  51. 2 2
      examples/jsm/objects/SkyMesh.js
  52. 14 5
      examples/jsm/physics/RapierPhysics.js
  53. 10 9
      examples/jsm/postprocessing/GTAOPass.js
  54. 17 17
      examples/jsm/postprocessing/OutlinePass.js
  55. 10 9
      examples/jsm/postprocessing/SSAOPass.js
  56. 11 2
      examples/jsm/shaders/UnpackDepthRGBAShader.js
  57. 2 2
      examples/jsm/transpiler/GLSLDecoder.js
  58. 8 7
      examples/jsm/tsl/display/BloomNode.js
  59. 6 8
      examples/jsm/tsl/display/GaussianBlurNode.js
  60. 181 172
      examples/jsm/tsl/display/TRAANode.js
  61. 1 1
      examples/jsm/tsl/lighting/TiledLightsNode.js
  62. 38 0
      examples/materialx/color3_vec3_cm_test.mtlx
  63. 15 0
      examples/materialx/combined_test.mtlx
  64. 35 0
      examples/materialx/conditional_if_float.mtlx
  65. 23 0
      examples/materialx/heightnormal.mtlx
  66. 21 0
      examples/materialx/heighttonormal_normal_input.mtlx
  67. 26 0
      examples/materialx/image_transform.mtlx
  68. 12 0
      examples/materialx/ior_test.mtlx
  69. 14 0
      examples/materialx/opacity_only_test.mtlx
  70. 12 0
      examples/materialx/opacity_test.mtlx
  71. BIN
      examples/materialx/resources/Images/grid.png
  72. 23 0
      examples/materialx/rotate2d_test.mtlx
  73. 44 0
      examples/materialx/rotate3d_test.mtlx
  74. 17 0
      examples/materialx/roughness_test.mtlx
  75. 14 0
      examples/materialx/sheen_test.mtlx
  76. 13 0
      examples/materialx/specular_test.mtlx
  77. 19 0
      examples/materialx/texture_opacity_test.mtlx
  78. 15 0
      examples/materialx/thin_film_ior_clamp_test.mtlx
  79. 15 0
      examples/materialx/thin_film_rainbow_test.mtlx
  80. 14 0
      examples/materialx/transmission_only_test.mtlx
  81. 14 0
      examples/materialx/transmission_rough.mtlx
  82. 14 0
      examples/materialx/transmission_test.mtlx
  83. 2 2
      examples/misc_raycaster_helper.html
  84. BIN
      examples/models/fbx/vCube.fbx
  85. BIN
      examples/models/gltf/bath_day.glb
  86. 1 1
      examples/physics_jolt_instancing.html
  87. 8 1
      examples/physics_rapier_basic.html
  88. BIN
      examples/screenshots/webgl_animation_walk.jpg
  89. BIN
      examples/screenshots/webgl_shadowmap.jpg
  90. BIN
      examples/screenshots/webgpu_compute_birds.jpg
  91. BIN
      examples/screenshots/webgpu_compute_texture_3d.jpg
  92. BIN
      examples/screenshots/webgpu_loader_materialx.jpg
  93. BIN
      examples/screenshots/webgpu_postprocessing.jpg
  94. BIN
      examples/screenshots/webgpu_postprocessing_dof_basic.jpg
  95. BIN
      examples/screenshots/webgpu_postprocessing_ssr.jpg
  96. BIN
      examples/screenshots/webgpu_procedural_texture.jpg
  97. BIN
      examples/screenshots/webgpu_reflection.jpg
  98. BIN
      examples/screenshots/webgpu_sprites.jpg
  99. BIN
      examples/screenshots/webgpu_volume_lighting.jpg
  100. BIN
      examples/screenshots/webxr_ar_camera_access.jpg

File diff suppressed because it is too large
+ 565 - 100
build/three.cjs


File diff suppressed because it is too large
+ 565 - 100
build/three.core.js


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


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


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


File diff suppressed because it is too large
+ 54 - 20
build/three.tsl.js


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


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


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


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


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


+ 1 - 1
docs/api/ar/animation/AnimationClip.html

@@ -91,7 +91,7 @@
 		<h2>الوظائف الساكنة (Static Methods)</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			تُرجع مصفوفة من AnimationClips الجديدة التي تم إنشاؤها من morph target sequences من الشكل الهندسي ، في محاولة لفرز أسماء الأهداف التحويلية إلى أنماط قائمة على مجموعة الرسوم المتحركة مثل "Walk_001 ، Walk_002 ، Run_001 ، Run_002 ...".
 		</p>

+ 1 - 1
docs/api/en/animation/AnimationClip.html

@@ -105,7 +105,7 @@
 		<h2>Static Methods</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			Returns an array of new AnimationClips created from the morph target
 			sequences of a geometry, trying to sort morph target names into

+ 7 - 4
docs/api/en/animation/KeyframeTrack.html

@@ -126,10 +126,13 @@
 		</p>
 
 		<p>
-			Note: The track's name does not necessarily have to be unique. Multiple
-			tracks can drive the same property. The result should be based on a
-			weighted blend between the multiple tracks according to the weights of
-			their respective actions.
+			Note: The track’s name does not necessarily have to be unique. Multiple tracks
+			can drive the same property, resulting in a weighted blend between the tracks 
+			according to the weights of their respective actions. However, if object names
+			used for targeting are not unique within the subtree, tracks referencing
+			those objects by name will only animate the first object encountered, even if
+			the path is unique. To reliably target distinct objects use UUIDs, or ensure
+			object names remain unique.
 		</p>
 
 		<h3>[property:Float32Array times]</h3>

+ 1 - 12
docs/examples/en/misc/Timer.html → docs/api/en/core/Timer.html

@@ -20,17 +20,6 @@
 			</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>
@@ -114,7 +103,7 @@
 		<h2>Source</h2>
 
 		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/misc/Timer.js examples/jsm/misc/Timer.js]
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 		</p>
 	</body>
 </html>

+ 4 - 0
docs/api/en/helpers/SkeletonHelper.html

@@ -58,6 +58,10 @@
 			Frees the GPU-related resources allocated by this instance. Call this
 			method whenever this instance is no longer used in your app.
 		</p>
+
+		<h3>[method:this setColors]( [param:Color color1], [param:Color color2] )</h3>
+		<p>Defines the colors of the helper.</p>
+
 		<h2>Source</h2>
 
 		<p>

+ 16 - 0
docs/api/en/textures/Texture.html

@@ -292,6 +292,12 @@
 			transformations.
 		</p>
 
+		<h3>[property:Array updateRanges]</h3>
+		<p>
+			This can be used to only update a subregion or specific rows of the texture (for example, just 
+			the first 3 rows). Use the [page:Texture.addUpdateRange .addUpdateRange] function to add ranges to this array.
+		</p>
+
 		<h2>Methods</h2>
 
 		<p>
@@ -332,6 +338,16 @@
 			[page:Texture.wrapT .wrapT] and [page:Texture.flipY .flipY] properties.
 		</p>
 
+		<h3>[method:undefined addUpdateRange]( [param:number start], [param:number count] )</h3>
+		<p>
+			Adds a range of data in the data texture to be updated on the GPU.
+		</p>
+
+		<h3>[method:undefined clearUpdateRanges]()</h3>
+		<p>
+			Clears the update ranges.
+		</p>
+
 		<h2>Source</h2>
 
 		<p>

+ 1 - 1
docs/api/fr/animation/AnimationClip.html

@@ -101,7 +101,7 @@
 		<h2>Méthodes Statiques</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			Renvoie un tableau de nouveaux AnimationClips créés depuis les séquences de morph
 			target d'une forme, essayant de trier les noms des morph targets en un pattern basé sur le groupe d'animation

+ 1 - 1
docs/api/ko/animation/AnimationClip.html

@@ -94,7 +94,7 @@
 		<h2>정적 메서드</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			geometry의 morphtarget sequences 를 통해 생성된 새 AnimationClips 배열을 리턴하고 ,
 			모프 타겟 이름을 애니메이션-그룹-기반의 "Walk_001, Walk_002, Run_001, Run_002 ..."와 같은 패턴으로 정리합니다.

+ 1 - 1
docs/api/ko/cameras/ArrayCamera.html

@@ -12,7 +12,7 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-			[name]은 미리 설정해놓은 카메라로 장면을 효율적으로 렌더할 때 사용될 수 있습니다. VR 장면을 렌더링할 때 중요한 퍼포먼스적 측면이기도 합니다.<br />
+			[name]은 미리 설정해놓은 카메라로 장면을 효율적으로 렌더할 때 사용될 수 있습니다. VR 장면을 렌더링할 때 중요한 퍼포먼스적 측면이기도 합니다.<br />
 			[name] 인스턴스는 항상 하위 카메라들의 배열을 가지고 있습니다.
 			각 하위 카메라에는 이 카메라로 렌더링되는 뷰포트 부분을 결정하는 *뷰포트* 속성을 반드시 정의해야 합니다.
 		</p>

+ 1 - 1
docs/api/pt-br/animation/AnimationClip.html

@@ -100,7 +100,7 @@
 		<h2>Métodos estáticos</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			Retorna um array de novos AnimationClips criados a partir
 			de sequências morph target de uma geometria, tentando classificar nomes de morph targets em grupos de animação

+ 1 - 1
docs/api/zh/animation/AnimationClip.html

@@ -93,7 +93,7 @@
 		<h2>静态方法</h2>
 
 
-		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:String name], [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
+		<h3>[method:Array CreateClipsFromMorphTargetSequences]( [param:Array morphTargetSequence], [param:Number fps], [param:Boolean noLoop] )</h3>
 		<p>
 			返回从几何体的变形目标序列(morph
 			target sequences)创建的新动画剪辑(AnimationClip)数组,并尝试将变形目标名称分类为基于动画组的模式,如“Walk_001、Walk_002、Run_001、Run_002……”。

+ 1 - 11
docs/examples/zh/misc/Timer.html → docs/api/zh/core/Timer.html

@@ -22,16 +22,6 @@
 	</ul>
 	</p>
 
-	<h2>导入</h2>
-
-	<p>
-		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
-	</p>
-
-	<code>
-			import { Timer } from 'three/addons/misc/Timer.js';
-		</code>
-
 	<h2>代码示例</h2>
 
 	<code>
@@ -103,7 +93,7 @@
 	<h2>源代码</h2>
 
 	<p>
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/misc/Timer.js examples/jsm/misc/Timer.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 	</p>
 </body>
 

+ 86 - 0
docs/examples/en/exporters/KTX2Exporter.html

@@ -0,0 +1,86 @@
+<!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">
+			An exporter for `KTX2`.
+			<br /><br />
+			[link:https://www.khronos.org/ktx/ KTX2] is an
+			[link:https://github.khronos.org/KTX-Specification/ktxspec.v2.html open
+			format specification] container format that provides efficient storage and
+			transmission of texture data with support for GPU-optimized formats.
+		</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 { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js';
+		</code>
+
+		<h2>Code Example</h2>
+
+		<code>
+			// Instantiate an exporter
+			const exporter = new KTX2Exporter();
+			
+			// Export a data texture
+			const result = await exporter.parse( dataTexture );
+			
+			// Export a render target
+			const result = await exporter.parse( renderer, renderTarget );
+		</code>
+
+		<h2>Examples</h2>
+
+		<p>[example:misc_exporter_ktx2]</p>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]()</h3>
+		<p>Creates a new [name].</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:Promise parse]( [param:DataTexture texture] )</h3>
+
+		<p>[page:Object texture] — DataTexture or Data3DTexture to export.<br /></p>
+		<p>
+			Generates a KTX2 file from the input texture. Returns a promise that
+			resolves with the result.
+		</p>
+
+		<h3>
+			[method:Promise parse]( [param:WebGLRenderer renderer],
+			[param:WebGLRenderTarget renderTarget] )
+		</h3>
+
+		<p>
+			[page:Object renderer] — Renderer to use to read pixel data from the render target. Should be a
+			[page:WebGLRenderer] or [page:WebGPURenderer].<br />
+
+			[page:Object renderTarget] — The render target to export. Should be a [page:RenderTarget].<br />
+		</p>
+		<p>
+			Generates a KTX2 file from the render target. Returns a promise that
+			resolves with the result.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/KTX2Exporter.js examples/jsm/exporters/KTX2Exporter.js]
+		</p>
+	</body>
+</html>

+ 130 - 0
docs/examples/en/exporters/USDZExporter.html

@@ -0,0 +1,130 @@
+<!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">
+		An exporter for `USDZ`.
+		<br /><br />
+		[link:https://graphics.pixar.com/usd/docs/index.html USDZ] (Universal Scene Description ZIP archive) is a
+		[link:https://openusd.org/release/index.html USD-based format] for 3D content optimized for sharing and viewing.
+		USDZ files are ZIP archives containing USD scene data and associated textures. The format supports meshes,
+		materials, textures, and cameras.
+		</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 { USDZExporter } from 'three/addons/exporters/USDZExporter.js';
+		</code>
+
+		<h2>Features</h2>
+
+		<p>
+			USDZExporter supports the following features:
+		</p>
+
+		<ul>
+			<li>Meshes with MeshStandardMaterial</li>
+			<li>Textures (diffuse, normal, emissive, roughness, metalness, alpha, ambient occlusion)</li>
+			<li>Clearcoat and clearcoat roughness (from MeshPhysicalMaterial)</li>
+			<li>Cameras (perspective and orthographic)</li>
+			<li>AR anchoring properties for iOS Quick Look</li>
+			<li>Texture compression support with texture utils</li>
+		</ul>
+
+		<h2>Code Example</h2>
+
+		<code>
+		// Instantiate a exporter
+		const exporter = new USDZExporter();
+
+		// Configure export options
+		const options = { maxTextureSize: 2048 };
+
+		try {
+
+			// Parse the input and generate the USDZ output
+			const arraybuffer = await exporter.parseAsync( scene, options );
+			downloadUSDZ( arraybuffer );
+
+		} catch ( error ) {
+
+			console.error( 'Export failed:', error );
+
+		}
+		</code>
+
+		<h2>Examples</h2>
+
+		<p>
+			[example:misc_exporter_usdz]
+		</p>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]()</h3>
+		<p>
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:undefined parse]( [param:Object3D scene], [param:Function onCompleted], [param:Function onError], [param:Object options] )</h3>
+
+		<p>
+		[page:Object3D scene] — Scene or object to export.<br />
+		[page:Function onCompleted] — Will be called when the export completes. The argument will be the generated USDZ ArrayBuffer.<br />
+		[page:Function onError] — Will be called if there are any errors during the USDZ generation.<br />
+		[page:Options options] — Export options<br />
+		<ul>
+			<li>`maxTextureSize` - int. Restricts the image maximum size (both width and height) to the given value. Default is 1024.</li>
+			<li>`includeAnchoringProperties` - bool. Whether to include anchoring properties for AR. Default is true.</li>
+			<li>`onlyVisible` - bool. Export only visible 3D objects. Default is true.</li>
+			<li>`quickLookCompatible` - bool. Whether to make the exported USDZ compatible with Apple's QuickLook. Default is false.</li>
+			<li>`ar` - Object. AR-specific options including anchoring type and plane anchoring alignment.</li>
+		</ul>
+		</p>
+		<p>
+		Generates a USDZ ArrayBuffer output from the input Object3D. Note that only MeshStandardMaterial is supported.
+		</p>
+
+		<h3>[method:Promise parseAsync]( [param:Object3D scene], [param:Object options] )</h3>
+
+		<p>
+			Generates a USDZ ArrayBuffer output from the input Object3D.
+		</p>
+		<p>
+			This is just like the [page:.parse]() method, but instead of
+			accepting callbacks it returns a promise that resolves with the
+			ArrayBuffer result, and otherwise accepts the same options.
+		</p>
+
+		<h3>[method:undefined setTextureUtils]( [param:Object utils] )</h3>
+
+		<p>
+			Sets the texture utils for this exporter. Only relevant when compressed textures have to be exported.
+			Depending on whether you use WebGLRenderer or WebGPURenderer, you must inject the corresponding
+			texture utils WebGLTextureUtils or WebGPUTextureUtils.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/USDZExporter.js examples/jsm/exporters/USDZExporter.js]
+		</p>
+	</body>
+</html>

+ 5 - 9
docs/list.json

@@ -65,6 +65,7 @@
 				"Layers": "api/en/core/Layers",
 				"Object3D": "api/en/core/Object3D",
 				"Raycaster": "api/en/core/Raycaster",
+				"Timer": "api/en/core/Timer",
 				"Uniform": "api/en/core/Uniform"
 			},
 
@@ -374,9 +375,11 @@
 				"DRACOExporter": "examples/en/exporters/DRACOExporter",
 				"EXRExporter": "examples/en/exporters/EXRExporter",
 				"GLTFExporter": "examples/en/exporters/GLTFExporter",
+				"KTX2Exporter": "examples/en/exporters/KTX2Exporter",
 				"OBJExporter": "examples/en/exporters/OBJExporter",
 				"PLYExporter": "examples/en/exporters/PLYExporter",
-				"STLExporter": "examples/en/exporters/STLExporter"
+				"STLExporter": "examples/en/exporters/STLExporter",
+				"USDZExporter": "examples/en/exporters/USDZExporter"
 			},
 
 			"Math": {
@@ -385,10 +388,6 @@
 				"OBB": "examples/en/math/OBB"
 			},
 
-			"Misc": {
-				"Timer": "examples/en/misc/Timer"
-			},
-
 			"Modifiers": {
 				"EdgeSplit": "examples/en/modifiers/EdgeSplitModifier"
 			},
@@ -775,6 +774,7 @@
 				"Layers": "api/zh/core/Layers",
 				"Object3D": "api/zh/core/Object3D",
 				"Raycaster": "api/zh/core/Raycaster",
+				"Timer": "api/zh/core/Timer",
 				"Uniform": "api/zh/core/Uniform"
 			},
 
@@ -1088,10 +1088,6 @@
 				"EdgeSplitModifier": "examples/zh/modifiers/EdgeSplitModifier"
 			},
 
-			"杂项": {
-				"Timer": "examples/zh/misc/Timer"
-			},
-
 			"凸包": {
 				"Face": "examples/zh/math/convexhull/Face",
 				"HalfEdge": "examples/zh/math/convexhull/HalfEdge",

+ 25 - 2
editor/js/Loader.js

@@ -622,6 +622,29 @@ function Loader( editor ) {
 
 			}
 
+			case 'usda':
+
+			{
+
+				reader.addEventListener( 'load', async function ( event ) {
+
+					const contents = event.target.result;
+
+					const { USDLoader } = await import( 'three/addons/loaders/USDLoader.js' );
+
+					const group = new USDLoader().parse( contents );
+					group.name = filename;
+
+					editor.execute( new AddObjectCommand( editor, group ) );
+
+				}, false );
+				reader.readAsText( file );
+
+				break;
+
+			}
+
+			case 'usdc':
 			case 'usdz':
 
 			{
@@ -630,9 +653,9 @@ function Loader( editor ) {
 
 					const contents = event.target.result;
 
-					const { USDZLoader } = await import( 'three/addons/loaders/USDZLoader.js' );
+					const { USDLoader } = await import( 'three/addons/loaders/USDLoader.js' );
 
-					const group = new USDZLoader().parse( contents );
+					const group = new USDLoader().parse( contents );
 					group.name = filename;
 
 					editor.execute( new AddObjectCommand( editor, group ) );

+ 10 - 3
editor/js/Viewport.Info.js

@@ -60,8 +60,15 @@ function ViewportInfo( editor ) {
 				if ( object.isMesh || object.isPoints ) {
 
 					const geometry = object.geometry;
+					const positionAttribute = geometry.attributes.position;
 
-					vertices += geometry.attributes.position.count;
+					// update counts only if vertex data are defined
+
+					if ( positionAttribute !== undefined && positionAttribute !== null ) {
+
+						vertices += positionAttribute.count;
+
+					}
 
 					if ( object.isMesh ) {
 
@@ -69,9 +76,9 @@ function ViewportInfo( editor ) {
 
 							triangles += geometry.index.count / 3;
 
-						} else {
+						} else if ( positionAttribute !== undefined && positionAttribute !== null ) {
 
-							triangles += geometry.attributes.position.count / 3;
+							triangles += positionAttribute.count / 3;
 
 						}
 

+ 37 - 7
editor/js/libs/ui.three.js

@@ -1,8 +1,5 @@
 import * as THREE from 'three';
 
-import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
-import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
-import { TGALoader } from 'three/addons/loaders/TGALoader.js';
 import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
 
 import { UISpan, UIDiv, UIRow, UIButton, UICheckbox, UIText, UINumber } from './ui.js';
@@ -49,7 +46,7 @@ class UITexture extends UISpan {
 		} );
 		this.dom.appendChild( canvas );
 
-		function loadFile( file ) {
+		async function loadFile( file ) {
 
 			const extension = file.name.split( '.' ).pop().toLowerCase();
 			const reader = new FileReader();
@@ -66,10 +63,12 @@ class UITexture extends UISpan {
 
 			} else if ( extension === 'hdr' || extension === 'pic' ) {
 
-				reader.addEventListener( 'load', function ( event ) {
+				reader.addEventListener( 'load', async function ( event ) {
 
 					// assuming RGBE/Radiance HDR image format
 
+					const { RGBELoader } = await import( 'three/addons/loaders/RGBELoader.js' );
+
 					const loader = new RGBELoader();
 					loader.load( event.target.result, function ( hdrTexture ) {
 
@@ -89,7 +88,9 @@ class UITexture extends UISpan {
 
 			} else if ( extension === 'tga' ) {
 
-				reader.addEventListener( 'load', function ( event ) {
+				reader.addEventListener( 'load', async function ( event ) {
+
+					const { TGALoader } = await import( 'three/addons/loaders/TGALoader.js' );
 
 					const loader = new TGALoader();
 					loader.load( event.target.result, function ( texture ) {
@@ -112,7 +113,9 @@ class UITexture extends UISpan {
 
 			} else if ( extension === 'ktx2' ) {
 
-				reader.addEventListener( 'load', function ( event ) {
+				reader.addEventListener( 'load', async function ( event ) {
+
+					const { KTX2Loader } = await import( 'three/addons/loaders/KTX2Loader.js' );
 
 					const arrayBuffer = event.target.result;
 					const blobURL = URL.createObjectURL( new Blob( [ arrayBuffer ] ) );
@@ -139,6 +142,33 @@ class UITexture extends UISpan {
 
 				reader.readAsArrayBuffer( file );
 
+			} else if ( extension === 'exr' ) {
+
+				reader.addEventListener( 'load', async function ( event ) {
+
+					const { EXRLoader } = await import( 'three/addons/loaders/EXRLoader.js' );
+
+					const arrayBuffer = event.target.result;
+					const blobURL = URL.createObjectURL( new Blob( [ arrayBuffer ] ) );
+					const exrLoader = new EXRLoader();
+
+					exrLoader.load( blobURL, function ( texture ) {
+
+						texture.sourceFile = file.name;
+						texture.needsUpdate = true;
+
+						cache.set( hash, texture );
+
+						scope.setValue( texture );
+
+						if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
+
+					} );
+
+				} );
+
+				reader.readAsArrayBuffer( file );
+
 			} else if ( file.type.match( 'image.*' ) ) {
 
 				reader.addEventListener( 'load', function ( event ) {

+ 8 - 4
editor/sw.js

@@ -41,23 +41,27 @@ const assets = [
 	'../examples/jsm/loaders/FBXLoader.js',
 	'../examples/jsm/loaders/GLTFLoader.js',
 	'../examples/jsm/loaders/KMZLoader.js',
-	'../examples/jsm/loaders/KTX2Loader.js',
 	'../examples/jsm/loaders/MD2Loader.js',
 	'../examples/jsm/loaders/OBJLoader.js',
 	'../examples/jsm/loaders/MTLLoader.js',
 	'../examples/jsm/loaders/PCDLoader.js',
 	'../examples/jsm/loaders/PLYLoader.js',
-	'../examples/jsm/loaders/RGBELoader.js',
 	'../examples/jsm/loaders/STLLoader.js',
 	'../examples/jsm/loaders/SVGLoader.js',
-	'../examples/jsm/loaders/TGALoader.js',
 	'../examples/jsm/loaders/TDSLoader.js',
-	'../examples/jsm/loaders/USDZLoader.js',
+	'../examples/jsm/loaders/USDLoader.js',
+	'../examples/jsm/loaders/usd/USDAParser.js',
+	'../examples/jsm/loaders/usd/USDCParser.js',
 	'../examples/jsm/loaders/VOXLoader.js',
 	'../examples/jsm/loaders/VRMLLoader.js',
 	'../examples/jsm/loaders/VTKLoader.js',
 	'../examples/jsm/loaders/XYZLoader.js',
 
+	'../examples/jsm/loaders/EXRLoader.js',
+	'../examples/jsm/loaders/KTX2Loader.js',
+	'../examples/jsm/loaders/RGBELoader.js',
+	'../examples/jsm/loaders/TGALoader.js',
+
 	'../examples/jsm/curves/NURBSCurve.js',
 	'../examples/jsm/curves/NURBSUtils.js',
 

+ 3 - 0
examples/files.json

@@ -315,6 +315,7 @@
 		"webgpu_compute_points",
 		"webgpu_compute_sort_bitonic",
 		"webgpu_compute_texture",
+		"webgpu_compute_texture_3d",
 		"webgpu_compute_texture_pingpong",
 		"webgpu_compute_water",
 		"webgpu_cubemap_adjustments",
@@ -399,6 +400,7 @@
 		"webgpu_postprocessing_bloom_selective",
 		"webgpu_postprocessing_difference",
 		"webgpu_postprocessing_dof",
+		"webgpu_postprocessing_dof_basic",
 		"webgpu_postprocessing_pixel",
 		"webgpu_postprocessing_fxaa",
 		"webgpu_postprocessing_lensflare",
@@ -473,6 +475,7 @@
 		"webaudio_visualizer"
 	],
 	"webxr": [
+		"webxr_ar_camera_access",
 		"webxr_ar_cones",
 		"webxr_ar_hittest",
 		"webxr_ar_lighting",

+ 1 - 2
examples/jsm/Addons.js

@@ -117,7 +117,7 @@ export * from './loaders/TDSLoader.js';
 export * from './loaders/TGALoader.js';
 export * from './loaders/TIFFLoader.js';
 export * from './loaders/TTFLoader.js';
-export * from './loaders/USDZLoader.js';
+export * from './loaders/USDLoader.js';
 export * from './loaders/VOXLoader.js';
 export * from './loaders/VRMLLoader.js';
 export * from './loaders/VTKLoader.js';
@@ -146,7 +146,6 @@ export * from './misc/MorphAnimMesh.js';
 export * from './misc/MorphBlendMesh.js';
 export * from './misc/ProgressiveLightMap.js';
 export * from './misc/RollerCoaster.js';
-export * from './misc/Timer.js';
 export * from './misc/TubePainter.js';
 export * from './misc/Volume.js';
 export * from './misc/VolumeSlice.js';

+ 1 - 1
examples/jsm/capabilities/WebGPU.js

@@ -2,7 +2,7 @@ let isAvailable = ( typeof navigator !== 'undefined' && navigator.gpu !== undefi
 
 if ( typeof window !== 'undefined' && isAvailable ) {
 
-	isAvailable = await navigator.gpu.requestAdapter();
+	isAvailable = Boolean( await navigator.gpu.requestAdapter() );
 
 }
 

+ 4 - 4
examples/jsm/csm/CSMShadowNode.js

@@ -374,9 +374,9 @@ class CSMShadowNode extends ShadowBaseNode {
 	_setupFade() {
 
 		const cameraNear = reference( 'camera.near', 'float', this ).setGroup( renderGroup );
-		const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).label( 'cascades' );
+		const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).setName( 'cascades' );
 
-		const shadowFar = uniform( 'float' ).setGroup( renderGroup ).label( 'shadowFar' )
+		const shadowFar = uniform( 'float' ).setGroup( renderGroup ).setName( 'shadowFar' )
 			.onRenderUpdate( () => Math.min( this.maxFar, this.camera.far ) );
 
 		const linearDepth = viewZToOrthographicDepth( positionView.z, cameraNear, shadowFar ).toVar( 'linearDepth' );
@@ -456,9 +456,9 @@ class CSMShadowNode extends ShadowBaseNode {
 	_setupStandard() {
 
 		const cameraNear = reference( 'camera.near', 'float', this ).setGroup( renderGroup );
-		const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).label( 'cascades' );
+		const cascades = reference( '_cascades', 'vec2', this ).setGroup( renderGroup ).setName( 'cascades' );
 
-		const shadowFar = uniform( 'float' ).setGroup( renderGroup ).label( 'shadowFar' )
+		const shadowFar = uniform( 'float' ).setGroup( renderGroup ).setName( 'shadowFar' )
 			.onRenderUpdate( () => Math.min( this.maxFar, this.camera.far ) );
 
 		const linearDepth = viewZToOrthographicDepth( positionView.z, cameraNear, shadowFar ).toVar( 'linearDepth' );

+ 8 - 3
examples/jsm/environments/RoomEnvironment.js

@@ -3,7 +3,7 @@ import {
  	BoxGeometry,
  	InstancedMesh,
  	Mesh,
-	MeshBasicMaterial,
+	MeshLambertMaterial,
  	MeshStandardMaterial,
  	PointLight,
  	Scene,
@@ -168,8 +168,13 @@ class RoomEnvironment extends Scene {
 
 function createAreaLightMaterial( intensity ) {
 
-	const material = new MeshBasicMaterial();
-	material.color.setScalar( intensity );
+	// create an emissive-only material. see #31348
+	const material = new MeshLambertMaterial( {
+		color: 0x000000,
+		emissive: 0xffffff,
+		emissiveIntensity: intensity
+	} );
+
 	return material;
 
 }

File diff suppressed because it is too large
+ 573 - 246
examples/jsm/exporters/USDZExporter.js


+ 47 - 8
examples/jsm/geometries/RoundedBoxGeometry.js

@@ -59,21 +59,40 @@ class RoundedBoxGeometry extends BoxGeometry {
 	 * @param {number} [width=1] - The width. That is, the length of the edges parallel to the X axis.
 	 * @param {number} [height=1] - The height. That is, the length of the edges parallel to the Y axis.
 	 * @param {number} [depth=1] - The depth. That is, the length of the edges parallel to the Z axis.
-	 * @param {number} [segments=2] - Number of segmented that form the rounded corners.
+	 * @param {number} [segments=2] - Number of segments that form the rounded corners.
 	 * @param {number} [radius=0.1] - The radius of the rounded corners.
 	 */
 	constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
 
-		// ensure segments is odd so we have a plane connecting the rounded corners
-		segments = segments * 2 + 1;
+		// calculate total segments needed &
+		// ensure it's odd so that we have a plane connecting the rounded corners
+		const totalSegments = segments * 2 + 1;
 
 		// ensure radius isn't bigger than shortest side
 		radius = Math.min( width / 2, height / 2, depth / 2, radius );
 
-		super( width, height, depth, segments, segments, segments );
-
-		// if we just have one segment we're the same as a regular box
-		if ( segments === 1 ) return;
+		// start with a unit box geometry, its vertices will be modified to form the rounded box
+		super( 1, 1, 1, totalSegments, totalSegments, totalSegments );
+
+		this.type = 'RoundedBoxGeometry';
+
+		/**
+		 * Holds the constructor parameters that have been
+		 * used to generate the geometry. Any modification
+		 * after instantiation does not change the geometry.
+		 *
+		 * @type {Object}
+		 */
+		this.parameters = {
+			width: width,
+			height: height,
+			depth: depth,
+			segments: segments,
+			radius: radius,
+		};
+
+		// if totalSegments is 1, no rounding is needed - return regular box
+		if ( totalSegments === 1 ) return;
 
 		const geometry2 = this.toNonIndexed();
 
@@ -95,7 +114,7 @@ class RoundedBoxGeometry extends BoxGeometry {
 
 		const faceTris = positions.length / 6;
 		const faceDirVector = new Vector3();
-		const halfSegmentSize = 0.5 / segments;
+		const halfSegmentSize = 0.5 / totalSegments;
 
 		for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
 
@@ -172,6 +191,26 @@ class RoundedBoxGeometry extends BoxGeometry {
 
 	}
 
+	/**
+	 * Factory method for creating an instance of this class from the given
+	 * JSON object.
+	 *
+	 * @param {Object} data - A JSON object representing the serialized geometry.
+	 * @returns {RoundedBoxGeometry} A new instance.
+	 */
+	static fromJSON( data ) {
+
+		return new RoundedBoxGeometry(
+			data.width,
+			data.height,
+			data.depth,
+			data.segments,
+			data.radius
+		);
+
+	}
+
+
 }
 
 export { RoundedBoxGeometry };

+ 5 - 3
examples/jsm/interactive/HTMLMesh.js

@@ -478,11 +478,13 @@ function html2canvas( element ) {
 
 				}
 
-				if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
+				if ( element.type === 'color' || element.type === 'text' || element.type === 'number' || element.type === 'email' || element.type === 'password' ) {
 
 					clipper.add( { x: x, y: y, width: width, height: height } );
 
-					drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
+					const displayValue = element.type === 'password' ? '*'.repeat( element.value.length ) : element.value;
+
+					drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), displayValue );
 
 					clipper.remove();
 
@@ -578,7 +580,7 @@ function htmlevent( element, event, x, y ) {
 
 				}
 
-				if ( element instanceof HTMLInputElement && ( element.type === 'text' || element.type === 'number' ) && ( event === 'mousedown' || event === 'click' ) ) {
+				if ( element instanceof HTMLInputElement && ( element.type === 'text' || element.type === 'number' || element.type === 'email' || element.type === 'password' ) && ( event === 'mousedown' || event === 'click' ) ) {
 
 					element.focus();
 

File diff suppressed because it is too large
+ 0 - 6
examples/jsm/libs/meshopt_decoder.module.js


+ 14 - 3
examples/jsm/lights/LightProbeGenerator.js

@@ -8,7 +8,8 @@ import {
 	NoColorSpace,
 	HalfFloatType,
 	DataUtils,
-	WebGLCoordinateSystem
+	WebGLCoordinateSystem,
+	FloatType
 } from 'three';
 
 /**
@@ -178,7 +179,11 @@ class LightProbeGenerator {
 
 		if ( renderer.isWebGLRenderer ) {
 
-			if ( dataType === HalfFloatType ) {
+			if ( dataType === FloatType ) {
+
+				data = new Float32Array( imageWidth * imageWidth * 4 );
+
+			} else if ( dataType === HalfFloatType ) {
 
 				data = new Uint16Array( imageWidth * imageWidth * 4 );
 
@@ -210,7 +215,13 @@ class LightProbeGenerator {
 
 				let r, g, b;
 
-				if ( dataType === HalfFloatType ) {
+				if ( dataType === FloatType ) {
+
+					r = data[ i ];
+					g = data[ i + 1 ];
+					b = data[ i + 2 ];
+
+				} else if ( dataType === HalfFloatType ) {
 
 					r = DataUtils.fromHalfFloat( data[ i ] );
 					g = DataUtils.fromHalfFloat( data[ i + 1 ] );

+ 210 - 22
examples/jsm/loaders/EXRLoader.js

@@ -3,10 +3,10 @@ import {
 	DataUtils,
 	FloatType,
 	HalfFloatType,
-	NoColorSpace,
 	LinearFilter,
 	LinearSRGBColorSpace,
 	RedFormat,
+	RGFormat,
 	RGBAFormat
 } from 'three';
 import * as fflate from '../libs/fflate.module.js';
@@ -113,6 +113,14 @@ class EXRLoader extends DataTextureLoader {
 		 */
 		this.type = HalfFloatType;
 
+		/**
+		 * Texture output format.
+		 *
+		 * @type {(RGBAFormat|RGFormat|RedFormat)}
+		 * @default RGBAFormat
+		 */
+		this.outputFormat = RGBAFormat;
+
 	}
 
 	/**
@@ -1022,6 +1030,81 @@ class EXRLoader extends DataTextureLoader {
 
 		}
 
+		function lossyDctChannelDecode( channelIndex, rowPtrs, channelData, acBuffer, dcBuffer, outBuffer ) {
+
+			const dataView = new DataView( outBuffer.buffer );
+			const cd = channelData[ channelIndex ];
+			const width = cd.width;
+			const height = cd.height;
+
+			const numBlocksX = Math.ceil( width / 8.0 );
+			const numBlocksY = Math.ceil( height / 8.0 );
+			const numFullBlocksX = Math.floor( width / 8.0 );
+			const leftoverX = width - ( numBlocksX - 1 ) * 8;
+			const leftoverY = height - ( numBlocksY - 1 ) * 8;
+
+			const currAcComp = { value: 0 };
+			let currDcComp = 0;
+			const dctData = new Float32Array( 64 );
+			const halfZigBlock = new Uint16Array( 64 );
+			const rowBlock = new Uint16Array( numBlocksX * 64 );
+
+			for ( let blocky = 0; blocky < numBlocksY; ++ blocky ) {
+
+				let maxY = 8;
+
+				if ( blocky == numBlocksY - 1 ) maxY = leftoverY;
+
+				for ( let blockx = 0; blockx < numBlocksX; ++ blockx ) {
+
+					halfZigBlock.fill( 0 );
+					halfZigBlock[ 0 ] = dcBuffer[ currDcComp ++ ];
+					unRleAC( currAcComp, acBuffer, halfZigBlock );
+					unZigZag( halfZigBlock, dctData );
+					dctInverse( dctData );
+					convertToHalf( dctData, rowBlock, blockx * 64 );
+
+				}
+
+				// Write decoded data to output buffer
+				for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) {
+
+					let offset = rowPtrs[ channelIndex ][ y ];
+
+					for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) {
+
+						const src = blockx * 64 + ( ( y & 0x7 ) * 8 );
+
+						for ( let x = 0; x < 8; ++ x ) {
+
+							dataView.setUint16( offset + x * INT16_SIZE * cd.type, rowBlock[ src + x ], true );
+
+						}
+
+						offset += 8 * INT16_SIZE * cd.type;
+
+					}
+
+					if ( numBlocksX != numFullBlocksX ) {
+
+						const src = numFullBlocksX * 64 + ( ( y & 0x7 ) * 8 );
+
+						for ( let x = 0; x < leftoverX; ++ x ) {
+
+							dataView.setUint16( offset + x * INT16_SIZE * cd.type, rowBlock[ src + x ], true );
+
+						}
+
+					}
+
+				}
+
+			}
+
+			cd.decoded = true;
+
+		}
+
 		function unRleAC( currAcComp, acBuffer, halfZigBlock ) {
 
 			let acValue;
@@ -1634,8 +1717,12 @@ class EXRLoader extends DataTextureLoader {
 
 			}
 
-			// Lossy DCT decode RGB channels
-			lossyDctDecode( cscSet, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer );
+			// Decode lossy DCT data if we have a valid color space conversion set with the first RGB channel present
+			if ( cscSet.idx[ 0 ] !== undefined && channelData[ cscSet.idx[ 0 ] ] ) {
+
+				lossyDctDecode( cscSet, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer );
+
+			}
 
 			// Decode other channels
 			for ( let i = 0; i < channelData.length; ++ i ) {
@@ -1673,7 +1760,11 @@ class EXRLoader extends DataTextureLoader {
 
 						break;
 
-					case LOSSY_DCT: // skip
+					case LOSSY_DCT:
+
+						lossyDctChannelDecode( i, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer );
+
+						break;
 
 					default:
 						throw new Error( 'EXRLoader.parse: unsupported channel compression' );
@@ -2291,7 +2382,7 @@ class EXRLoader extends DataTextureLoader {
 
 		}
 
-		function setupDecoder( EXRHeader, dataView, uInt8Array, offset, outputType ) {
+		function setupDecoder( EXRHeader, dataView, uInt8Array, offset, outputType, outputFormat ) {
 
 			const EXRDecoder = {
 				size: 0,
@@ -2302,6 +2393,7 @@ class EXRLoader extends DataTextureLoader {
 				height: EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1,
 				inputChannels: EXRHeader.channels,
 				channelByteOffsets: {},
+				shouldExpand: false,
 				scanOrder: null,
 				totalBytes: null,
 				columns: null,
@@ -2379,17 +2471,16 @@ class EXRLoader extends DataTextureLoader {
 
 			// RGB images will be converted to RGBA format, preventing software emulation in select devices.
 			let fillAlpha = false;
+			let invalidOutput = false;
 
+			// Validate if input texture contain supported channels
 			if ( channels.R && channels.G && channels.B ) {
 
-				fillAlpha = ! channels.A;
 				EXRDecoder.outputChannels = 4;
-				EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
 
 			} else if ( channels.Y ) {
 
 				EXRDecoder.outputChannels = 1;
-				EXRDecoder.decodeChannels = { Y: 0 };
 
 			} else {
 
@@ -2397,6 +2488,83 @@ class EXRLoader extends DataTextureLoader {
 
 			}
 
+			// Setup output texture configuration
+			switch ( EXRDecoder.outputChannels ) {
+
+				case 4:
+
+					if ( outputFormat == RGBAFormat ) {
+
+						fillAlpha = ! channels.A;
+						EXRDecoder.format = RGBAFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 4;
+						EXRDecoder.decodeChannels = { R: 0, G: 1, B: 2, A: 3 };
+
+					} else if ( outputFormat == RGFormat ) {
+
+						EXRDecoder.format = RGFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 2;
+						EXRDecoder.decodeChannels = { R: 0, G: 1 };
+
+					} else if ( outputFormat == RedFormat ) {
+
+						EXRDecoder.format = RedFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 1;
+						EXRDecoder.decodeChannels = { R: 0 };
+
+					} else  {
+
+						invalidOutput = true;
+
+					}
+
+					break;
+
+				case 1:
+
+					if ( outputFormat == RGBAFormat ) {
+
+						fillAlpha = true;
+						EXRDecoder.format = RGBAFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 4;
+						EXRDecoder.shouldExpand = true;
+						EXRDecoder.decodeChannels = { Y: 0 };
+
+					} else if ( outputFormat == RGFormat ) {
+
+						EXRDecoder.format = RGFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 2;
+						EXRDecoder.shouldExpand = true;
+						EXRDecoder.decodeChannels = { Y: 0 };
+
+					} else if ( outputFormat == RedFormat ) {
+
+						EXRDecoder.format = RedFormat;
+						EXRDecoder.colorSpace = LinearSRGBColorSpace;
+						EXRDecoder.outputChannels = 1;
+						EXRDecoder.decodeChannels = { Y: 0 };
+
+					} else  {
+
+						invalidOutput = true;
+
+					}
+
+					break;
+
+				default:
+
+					invalidOutput = true;
+
+			}
+
+			if (invalidOutput) throw new Error( 'EXRLoader.parse: invalid output format for specified file.' );
+
 			if ( EXRDecoder.type == 1 ) {
 
 				// half
@@ -2486,18 +2654,6 @@ class EXRLoader extends DataTextureLoader {
 
 			}
 
-			if ( EXRDecoder.outputChannels == 4 ) {
-
-				EXRDecoder.format = RGBAFormat;
-				EXRDecoder.colorSpace = LinearSRGBColorSpace;
-
-			} else {
-
-				EXRDecoder.format = RedFormat;
-				EXRDecoder.colorSpace = NoColorSpace;
-
-			}
-
 			if ( EXRHeader.spec.singleTile ) {
 
 				EXRDecoder.blockHeight = EXRHeader.tiles.ySize;
@@ -2543,11 +2699,30 @@ class EXRLoader extends DataTextureLoader {
 		const EXRHeader = parseHeader( bufferDataView, buffer, offset );
 
 		// get input compression information and prepare decoding.
-		const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type );
+		const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type, this.outputFormat );
 
 		// parse input data
 		EXRDecoder.decode();
 
+		// output texture post-processing
+		if ( EXRDecoder.shouldExpand ) {
+
+			const byteArray = EXRDecoder.byteArray;
+
+			if ( this.outputFormat == RGBAFormat ) {
+
+				for ( let i = 0; i < byteArray.length; i += 4 )
+					byteArray [i + 2 ] = ( byteArray [ i + 1 ] = byteArray[ i ] );
+
+			} else if ( this.outputFormat == RGFormat ) {
+
+				for ( let i = 0; i < byteArray.length; i += 2 )
+					byteArray [ i + 1 ] = byteArray[ i ] ;
+
+			}
+
+		}
+
 		return {
 			header: EXRHeader,
 			width: EXRDecoder.width,
@@ -2564,7 +2739,7 @@ class EXRLoader extends DataTextureLoader {
 	 * Sets the texture type.
 	 *
 	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
-	 * @return {RGBMLoader} A reference to this loader.
+	 * @return {EXRLoader} A reference to this loader.
 	 */
 	setDataType( value ) {
 
@@ -2573,6 +2748,19 @@ class EXRLoader extends DataTextureLoader {
 
 	}
 
+	/**
+	 * Sets texture output format. Defaults to `RGBAFormat`.
+	 *
+	 * @param {(RGBAFormat|RGFormat|RedFormat)} value - Texture output format.
+	 * @return {EXRLoader} A reference to this loader.
+	 */
+	setOutputFormat( value ) {
+
+		this.outputFormat = value;
+		return this;
+
+	}
+
 	load( url, onLoad, onProgress, onError ) {
 
 		function onLoadCallback( texture, texData ) {

+ 1 - 1
examples/jsm/loaders/FBXLoader.js

@@ -1784,7 +1784,7 @@ class GeometryParser {
 		geoInfo.vertexPositions = ( geoNode.Vertices !== undefined ) ? geoNode.Vertices.a : [];
 		geoInfo.vertexIndices = ( geoNode.PolygonVertexIndex !== undefined ) ? geoNode.PolygonVertexIndex.a : [];
 
-		if ( geoNode.LayerElementColor && geoNode.LayerElementColor.Color ) {
+		if ( geoNode.LayerElementColor && geoNode.LayerElementColor[ 0 ].Colors ) {
 
 			geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] );
 

+ 212 - 30
examples/jsm/loaders/MaterialXLoader.js

@@ -1,20 +1,26 @@
-import { FileLoader, Loader, TextureLoader, RepeatWrapping, MeshBasicNodeMaterial, MeshPhysicalNodeMaterial } from 'three/webgpu';
+import {
+	FileLoader, Loader, TextureLoader, RepeatWrapping, MeshBasicNodeMaterial,
+	MeshPhysicalNodeMaterial, DoubleSide,
+} from 'three/webgpu';
 
 import {
 	float, bool, int, vec2, vec3, vec4, color, texture,
 	positionLocal, positionWorld, uv, vertexColor,
 	normalLocal, normalWorld, tangentLocal, tangentWorld,
-	add, sub, mul, div, mod, abs, sign, floor, ceil, round, pow, sin, cos, tan,
-	asin, acos, atan2, sqrt, exp, clamp, min, max, normalize, length, dot, cross, normalMap,
+	mul, abs, sign, floor, ceil, round, sin, cos, tan,
+	asin, acos, sqrt, exp, clamp, min, max, normalize, length, dot, cross, normalMap,
 	remap, smoothstep, luminance, mx_rgbtohsv, mx_hsvtorgb,
-	mix, split,
+	mix, saturation, transpose, determinant, inverse, log, reflect, refract, element,
 	mx_ramplr, mx_ramptb, mx_splitlr, mx_splittb,
 	mx_fractal_noise_float, mx_noise_float, mx_cell_noise_float, mx_worley_noise_float,
 	mx_transform_uv,
 	mx_safepower, mx_contrast,
 	mx_srgb_texture_to_lin_rec709,
-	saturation,
-	timerLocal, frameId
+	mx_add, mx_atan2, mx_divide, mx_modulo, mx_multiply, mx_power, mx_subtract,
+	mx_timer, mx_frame, mat3, mx_ramp4,
+	mx_invert, mx_ifgreater, mx_ifgreatereq, mx_ifequal, distance,
+	mx_separate, mx_place2d, mx_rotate2d, mx_rotate3d, mx_heighttonormal,
+	mx_unifiednoise2d, mx_unifiednoise3d
 } from 'three/tsl';
 
 const colorSpaceLib = {
@@ -35,19 +41,9 @@ class MXElement {
 
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 
-const mx_add = ( in1, in2 = float( 0 ) ) => add( in1, in2 );
-const mx_subtract = ( in1, in2 = float( 0 ) ) => sub( in1, in2 );
-const mx_multiply = ( in1, in2 = float( 1 ) ) => mul( in1, in2 );
-const mx_divide = ( in1, in2 = float( 1 ) ) => div( in1, in2 );
-const mx_modulo = ( in1, in2 = float( 1 ) ) => mod( in1, in2 );
-const mx_power = ( in1, in2 = float( 1 ) ) => pow( in1, in2 );
-const mx_atan2 = ( in1 = float( 0 ), in2 = float( 1 ) ) => atan2( in1, in2 );
-const mx_timer = () => timerLocal();
-const mx_frame = () => frameId;
-const mx_invert = ( in1, amount = float( 1 ) ) => sub( amount, in1 );
+// Enhanced separate node to support multi-output referencing (outx, outy, outz, outw)
 
-const separate = ( in1, channel ) => split( in1, channel.at( - 1 ) );
-const extract = ( in1, index ) => in1.element( index );
+// Type/arity-aware MaterialX node wrappers
 
 const MXElements = [
 
@@ -70,7 +66,7 @@ const MXElements = [
 	new MXElement( 'acos', acos, [ 'in' ] ),
 	new MXElement( 'atan2', mx_atan2, [ 'in1', 'in2' ] ),
 	new MXElement( 'sqrt', sqrt, [ 'in' ] ),
-	//new MtlXElement( 'ln', ... ),
+	new MXElement( 'ln', log, [ 'in' ] ),
 	new MXElement( 'exp', exp, [ 'in' ] ),
 	new MXElement( 'clamp', clamp, [ 'in', 'low', 'high' ] ),
 	new MXElement( 'min', min, [ 'in1', 'in2' ] ),
@@ -79,20 +75,27 @@ const MXElements = [
 	new MXElement( 'magnitude', length, [ 'in1', 'in2' ] ),
 	new MXElement( 'dotproduct', dot, [ 'in1', 'in2' ] ),
 	new MXElement( 'crossproduct', cross, [ 'in' ] ),
+	new MXElement( 'distance', distance, [ 'in1', 'in2' ] ),
 	new MXElement( 'invert', mx_invert, [ 'in', 'amount' ] ),
 	//new MtlXElement( 'transformpoint', ... ),
 	//new MtlXElement( 'transformvector', ... ),
 	//new MtlXElement( 'transformnormal', ... ),
-	//new MtlXElement( 'transformmatrix', ... ),
+	new MXElement( 'transformmatrix', mul, [ 'in1', 'in2' ] ),
 	new MXElement( 'normalmap', normalMap, [ 'in', 'scale' ] ),
-	//new MtlXElement( 'transpose', ... ),
-	//new MtlXElement( 'determinant', ... ),
-	//new MtlXElement( 'invertmatrix', ... ),
+	new MXElement( 'transpose', transpose, [ 'in' ] ),
+	new MXElement( 'determinant', determinant, [ 'in' ] ),
+	new MXElement( 'invertmatrix', inverse, [ 'in' ] ),
+	new MXElement( 'creatematrix', mat3, [ 'in1', 'in2', 'in3' ] ),
 	//new MtlXElement( 'rotate2d', rotateUV, [ 'in', radians( 'amount' )** ] ),
 	//new MtlXElement( 'rotate3d', ... ),
 	//new MtlXElement( 'arrayappend', ... ),
 	//new MtlXElement( 'dot', ... ),
 
+	new MXElement( 'length', length, [ 'in' ] ),
+	new MXElement( 'crossproduct', cross, [ 'in1', 'in2' ] ),
+	new MXElement( 'floor', floor, [ 'in' ] ),
+	new MXElement( 'ceil', ceil, [ 'in' ] ),
+
 	// << Adjustment >>
 	new MXElement( 'remap', remap, [ 'in', 'inlow', 'inhigh', 'outlow', 'outhigh' ] ),
 	new MXElement( 'smoothstep', smoothstep, [ 'in', 'low', 'high' ] ),
@@ -113,6 +116,7 @@ const MXElements = [
 	// << Procedural >>
 	new MXElement( 'ramplr', mx_ramplr, [ 'valuel', 'valuer', 'texcoord' ] ),
 	new MXElement( 'ramptb', mx_ramptb, [ 'valuet', 'valueb', 'texcoord' ] ),
+	new MXElement( 'ramp4', mx_ramp4, [ 'valuetl', 'valuetr', 'valuebl', 'valuebr', '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' ] ),
@@ -122,23 +126,34 @@ const MXElements = [
 	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' ] ),
-
+	new MXElement( 'unifiednoise2d', mx_unifiednoise2d, [ 'type', 'texcoord', 'freq', 'offset', 'jitter', 'outmin', 'outmax', 'clampoutput', 'octaves', 'lacunarity', 'diminish' ] ),
+	new MXElement( 'unifiednoise3d', mx_unifiednoise3d, [ 'type', 'texcoord', 'freq', 'offset', 'jitter', 'outmin', 'outmax', 'clampoutput', 'octaves', 'lacunarity', 'diminish' ] ),
 	// << Supplemental >>
 	//new MtlXElement( 'tiledimage', ... ),
 	//new MtlXElement( 'triplanarprojection', triplanarTextures, [ 'filex', 'filey', 'filez' ] ),
 	//new MtlXElement( 'ramp4', ... ),
-	//new MtlXElement( 'place2d', mx_place2d, [ 'texcoord', 'pivot', 'scale', 'rotate', 'offset' ] ),
+	new MXElement( 'place2d', mx_place2d, [ 'texcoord', 'pivot', 'scale', 'rotate', 'offset', 'operationorder' ] ),
 	new MXElement( 'safepower', mx_safepower, [ 'in1', 'in2' ] ),
 	new MXElement( 'contrast', mx_contrast, [ 'in', 'amount', 'pivot' ] ),
 	//new MtlXElement( 'hsvadjust', ... ),
 	new MXElement( 'saturate', saturation, [ 'in', 'amount' ] ),
-	new MXElement( 'extract', extract, [ 'in', 'index' ] ),
-	new MXElement( 'separate2', separate, [ 'in' ] ),
-	new MXElement( 'separate3', separate, [ 'in' ] ),
-	new MXElement( 'separate4', separate, [ 'in' ] ),
+	new MXElement( 'extract', element, [ 'in', 'index' ] ),
+	new MXElement( 'separate2', mx_separate, [ 'in' ] ),
+	new MXElement( 'separate3', mx_separate, [ 'in' ] ),
+	new MXElement( 'separate4', mx_separate, [ 'in' ] ),
+	new MXElement( 'reflect', reflect, [ 'in', 'normal' ] ),
+	new MXElement( 'refract', refract, [ 'in', 'normal', 'ior' ] ),
 
 	new MXElement( 'time', mx_timer ),
-	new MXElement( 'frame', mx_frame )
+	new MXElement( 'frame', mx_frame ),
+	new MXElement( 'ifgreater', mx_ifgreater, [ 'value1', 'value2', 'in1', 'in2' ] ),
+	new MXElement( 'ifgreatereq', mx_ifgreatereq, [ 'value1', 'value2', 'in1', 'in2' ] ),
+	new MXElement( 'ifequal', mx_ifequal, [ 'value1', 'value2', 'in1', 'in2' ] ),
+
+	// Placeholder implementations for unsupported nodes
+	new MXElement( 'rotate2d', mx_rotate2d, [ 'in', 'amount' ] ),
+	new MXElement( 'rotate3d', mx_rotate3d, [ 'in', 'amount', 'axis' ] ),
+	new MXElement( 'heighttonormal', mx_heighttonormal, [ 'in', 'scale', 'texcoord' ] ),
 
 ];
 
@@ -220,6 +235,22 @@ class MaterialXLoader extends Loader {
 	/**
 	 * Parses the given MaterialX data and returns the resulting materials.
 	 *
+	 * Supported standard_surface inputs:
+	 * - base, base_color: Base color/albedo
+	 * - opacity: Alpha/transparency
+	 * - specular_roughness: Surface roughness
+	 * - metalness: Metallic property
+	 * - specular: Specular reflection intensity
+	 * - specular_color: Specular reflection color
+	 * - ior: Index of refraction
+	 * - specular_anisotropy, specular_rotation: Anisotropic reflection
+	 * - transmission, transmission_color: Transmission properties
+	 * - thin_film_thickness, thin_film_ior: Thin film interference
+	 * - sheen, sheen_color, sheen_roughness: Sheen properties
+	 * - normal: Normal map
+	 * - coat, coat_roughness, coat_color: Clearcoat properties
+	 * - emission, emissionColor: Emission properties
+	 *
 	 * @param {string} text - The raw MaterialX data as a string.
 	 * @return {Object<string,NodeMaterial>} A dictionary holding the parse node materials.
 	 */
@@ -235,6 +266,12 @@ class MaterialXNode {
 
 	constructor( materialX, nodeXML, nodePath = '' ) {
 
+		if ( ! materialX || typeof materialX !== 'object' ) {
+
+			console.warn( 'MaterialXNode: materialX argument is not an object!', { materialX, nodeXML, nodePath } );
+
+		}
+
 		this.materialX = materialX;
 		this.nodeXML = nodeXML;
 		this.nodePath = nodePath ? nodePath + '/' + this.name : this.name;
@@ -418,6 +455,37 @@ class MaterialXNode {
 
 		}
 
+		// Handle <input name="texcoord" type="vector2" ... />
+		if (
+			this.element === 'input' &&
+			this.name === 'texcoord' &&
+			this.type === 'vector2'
+		) {
+
+			// Try to get index from defaultgeomprop (e.g., "UV0" => 0)
+			let index = 0;
+			const defaultGeomProp = this.getAttribute( 'defaultgeomprop' );
+			if ( defaultGeomProp && /^UV(\d+)$/.test( defaultGeomProp ) ) {
+
+				index = parseInt( defaultGeomProp.match( /^UV(\d+)$/ )[ 1 ], 10 );
+
+			}
+
+			node = uv( index );
+
+		}
+
+		// Multi-output support for separate/separate3
+		if (
+			( this.element === 'separate3' || this.element === 'separate2' || this.element === 'separate4' ) &&
+			out && typeof out === 'string' && out.startsWith( 'out' )
+		) {
+
+			const inNode = this.getNodeByName( 'in' );
+			return mx_separate( inNode, out );
+
+		}
+
 		//
 
 		const type = this.type;
@@ -519,6 +587,18 @@ class MaterialXNode {
 
 				const nodeElement = MtlXLibrary[ element ];
 
+				if ( ! nodeElement ) {
+
+					throw new Error( `THREE.MaterialXLoader: Unexpected node ${ new XMLSerializer().serializeToString( this.nodeXML ) }.` );
+
+				}
+
+				if ( ! nodeElement.nodeFunc ) {
+
+					throw new Error( `THREE.MaterialXLoader: Unexpected node 2 ${ new XMLSerializer().serializeToString( this.nodeXML ) }.` );
+
+				}
+
 				if ( out !== null ) {
 
 					node = nodeElement.nodeFunc( ...this.getNodesByNames( ...nodeElement.params ), out );
@@ -551,6 +631,11 @@ class MaterialXNode {
 
 			node = nodeToTypeClass( node );
 
+		} else {
+
+			console.warn( `THREE.MaterialXLoader: Unexpected node ${ new XMLSerializer().serializeToString( this.nodeXML ) }.` );
+			node = float( 0 );
+
 		}
 
 		node.name = this.name;
@@ -673,6 +758,12 @@ class MaterialXNode {
 
 		//
 
+		let opacityNode = null;
+
+		if ( inputs.opacity ) opacityNode = inputs.opacity;
+
+		//
+
 		let roughnessNode = null;
 
 		if ( inputs.specular_roughness ) roughnessNode = inputs.specular_roughness;
@@ -685,6 +776,64 @@ class MaterialXNode {
 
 		//
 
+		let specularIntensityNode = null;
+
+		if ( inputs.specular ) specularIntensityNode = inputs.specular;
+
+		//
+
+		let specularColorNode = null;
+
+		if ( inputs.specular_color ) specularColorNode = inputs.specular_color;
+
+		//
+
+		let iorNode = null;
+
+		if ( inputs.ior ) iorNode = inputs.ior;
+
+		//
+
+		let anisotropyNode = null;
+		let anisotropyRotationNode = null;
+
+		if ( inputs.specular_anisotropy ) anisotropyNode = inputs.specular_anisotropy;
+		if ( inputs.specular_rotation ) anisotropyRotationNode = inputs.specular_rotation;
+
+		//
+
+		let transmissionNode = null;
+		let transmissionColorNode = null;
+
+		if ( inputs.transmission ) transmissionNode = inputs.transmission;
+		if ( inputs.transmission_color ) transmissionColorNode = inputs.transmission_color;
+
+		//
+
+		let thinFilmThicknessNode = null;
+		let thinFilmIorNode = null;
+
+		if ( inputs.thin_film_thickness ) thinFilmThicknessNode = inputs.thin_film_thickness;
+
+		if ( inputs.thin_film_ior ) {
+
+			// Clamp IOR to valid range for Three.js (1.0 to 2.333)
+			thinFilmIorNode = clamp( inputs.thin_film_ior, float( 1.0 ), float( 2.333 ) );
+
+		}
+
+		//
+
+		let sheenNode = null;
+		let sheenColorNode = null;
+		let sheenRoughnessNode = null;
+
+		if ( inputs.sheen ) sheenNode = inputs.sheen;
+		if ( inputs.sheen_color ) sheenColorNode = inputs.sheen_color;
+		if ( inputs.sheen_roughness ) sheenRoughnessNode = inputs.sheen_roughness;
+
+		//
+
 		let clearcoatNode = null;
 		let clearcoatRoughnessNode = null;
 
@@ -717,13 +866,46 @@ class MaterialXNode {
 		//
 
 		material.colorNode = colorNode || color( 0.8, 0.8, 0.8 );
+		material.opacityNode = opacityNode || float( 1.0 );
 		material.roughnessNode = roughnessNode || float( 0.2 );
 		material.metalnessNode = metalnessNode || float( 0 );
+		material.specularIntensityNode = specularIntensityNode || float( 0.5 );
+		material.specularColorNode = specularColorNode || color( 1.0, 1.0, 1.0 );
+		material.iorNode = iorNode || float( 1.5 );
+		material.anisotropyNode = anisotropyNode || float( 0 );
+		material.anisotropyRotationNode = anisotropyRotationNode || float( 0 );
+		material.transmissionNode = transmissionNode || float( 0 );
+		material.transmissionColorNode = transmissionColorNode || color( 1.0, 1.0, 1.0 );
+		material.thinFilmThicknessNode = thinFilmThicknessNode || float( 0 );
+		material.thinFilmIorNode = thinFilmIorNode || float( 1.5 );
+		material.sheenNode = sheenNode || float( 0 );
+		material.sheenColorNode = sheenColorNode || color( 1.0, 1.0, 1.0 );
+		material.sheenRoughnessNode = sheenRoughnessNode || float( 0.5 );
 		material.clearcoatNode = clearcoatNode || float( 0 );
 		material.clearcoatRoughnessNode = clearcoatRoughnessNode || float( 0 );
 		if ( normalNode ) material.normalNode = normalNode;
 		if ( emissiveNode ) material.emissiveNode = emissiveNode;
 
+		// Auto-enable iridescence when thin film parameters are present
+		if ( thinFilmThicknessNode && thinFilmThicknessNode.value !== undefined && thinFilmThicknessNode.value > 0 ) {
+
+			material.iridescence = 1.0;
+
+		}
+
+		if ( opacityNode !== null ) {
+
+			material.transparent = true;
+
+		}
+
+		if ( transmissionNode !== null ) {
+
+			material.side = DoubleSide;
+			material.transparent = true;
+
+		}
+
 	}
 
 	/*setGltfPBR( material ) {

+ 13 - 1
examples/jsm/loaders/TTFLoader.js

@@ -152,7 +152,19 @@ class TTFLoader extends Loader {
 
 					} );
 
-					glyphs[ String.fromCodePoint( glyph.unicode ) ] = token;
+					if ( Array.isArray( glyph.unicodes ) && glyph.unicodes.length > 0 ) {
+						
+						glyph.unicodes.forEach( function ( unicode ) {
+							
+							glyphs[ String.fromCodePoint( unicode ) ] = token;
+							
+						} );
+						
+					} else {
+
+						glyphs[ String.fromCodePoint( glyph.unicode ) ] = token;
+
+					}
 
 				}
 

+ 219 - 0
examples/jsm/loaders/USDLoader.js

@@ -0,0 +1,219 @@
+import {
+	FileLoader,
+	Loader
+} from 'three';
+
+import * as fflate from '../libs/fflate.module.js';
+import { USDAParser } from './usd/USDAParser.js';
+import { USDCParser } from './usd/USDCParser.js';
+
+/**
+ * A loader for the USDZ format.
+ *
+ * USDZ files that use USDC internally are not yet supported, only USDA.
+ *
+ * ```js
+ * const loader = new USDZLoader();
+ * const model = await loader.loadAsync( 'saeukkang.usdz' );
+ * scene.add( model );
+ * ```
+ *
+ * @augments Loader
+ * @three_import import { USDLoader } from 'three/addons/loaders/USDLoader.js';
+ */
+class USDLoader extends Loader {
+
+	/**
+	 * Constructs a new USDZ loader.
+	 *
+	 * @param {LoadingManager} [manager] - The loading manager.
+	 */
+	constructor( manager ) {
+
+		super( manager );
+
+	}
+
+	/**
+	 * Starts loading from the given URL and passes the loaded USDZ asset
+	 * to the `onLoad()` callback.
+	 *
+	 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
+	 * @param {function(Group)} onLoad - Executed when the loading process has been finished.
+	 * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
+	 * @param {onErrorCallback} onError - Executed when errors occur.
+	 */
+	load( url, onLoad, onProgress, onError ) {
+
+		const scope = this;
+
+		const loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.setRequestHeader( scope.requestHeader );
+		loader.setWithCredentials( scope.withCredentials );
+		loader.load( url, function ( text ) {
+
+			try {
+
+				onLoad( scope.parse( text ) );
+
+			} catch ( e ) {
+
+				if ( onError ) {
+
+					onError( e );
+
+				} else {
+
+					console.error( e );
+
+				}
+
+				scope.manager.itemError( url );
+
+			}
+
+		}, onProgress, onError );
+
+	}
+
+	/**
+	 * Parses the given USDZ data and returns the resulting group.
+	 *
+	 * @param {ArrayBuffer|string} buffer - The raw USDZ data as an array buffer.
+	 * @return {Group} The parsed asset as a group.
+	 */
+	parse( buffer ) {
+
+		const usda = new USDAParser();
+		const usdc = new USDCParser();
+
+		function parseAssets( zip ) {
+
+			const data = {};
+			const loader = new FileLoader();
+			loader.setResponseType( 'arraybuffer' );
+
+			for ( const filename in zip ) {
+
+				if ( filename.endsWith( 'png' ) ) {
+
+					const blob = new Blob( [ zip[ filename ] ], { type: 'image/png' } );
+					data[ filename ] = URL.createObjectURL( blob );
+
+				}
+
+				if ( filename.endsWith( 'usd' ) || filename.endsWith( 'usda' ) || filename.endsWith( 'usdc' ) ) {
+
+					if ( isCrateFile( zip[ filename ] ) ) {
+
+						data[ filename ] = usdc.parse( zip[ filename ].buffer, data );
+
+					} else {
+
+						const text = fflate.strFromU8( zip[ filename ] );
+						data[ filename ] = usda.parseText( text );
+
+					}
+
+				}
+
+			}
+
+			return data;
+
+		}
+
+		function isCrateFile( buffer ) {
+
+			const crateHeader = new Uint8Array( [ 0x50, 0x58, 0x52, 0x2D, 0x55, 0x53, 0x44, 0x43 ] ); // PXR-USDC
+
+			if ( buffer.byteLength < crateHeader.length ) return false;
+
+			const view = new Uint8Array( buffer, 0, crateHeader.length );
+
+			for ( let i = 0; i < crateHeader.length; i ++ ) {
+
+				if ( view[ i ] !== crateHeader[ i ] ) return false;
+
+			}
+
+			return true;
+
+		}
+
+		function findUSD( zip ) {
+
+			if ( zip.length < 1 ) return undefined;
+
+			const firstFileName = Object.keys( zip )[ 0 ];
+			let isCrate = false;
+
+			// As per the USD specification, the first entry in the zip archive is used as the main file ("UsdStage").
+			// ASCII files can end in either .usda or .usd.
+			// See https://openusd.org/release/spec_usdz.html#layout
+			if ( firstFileName.endsWith( 'usda' ) ) return zip[ firstFileName ];
+
+			if ( firstFileName.endsWith( 'usdc' ) ) {
+
+				isCrate = true;
+
+			} else if ( firstFileName.endsWith( 'usd' ) ) {
+
+				// If this is not a crate file, we assume it is a plain USDA file.
+				if ( ! isCrateFile( zip[ firstFileName ] ) ) {
+
+					return zip[ firstFileName ];
+
+				} else {
+
+					isCrate = true;
+
+				}
+
+			}
+
+			if ( isCrate ) {
+
+				return zip[ firstFileName ];
+
+			}
+
+		}
+
+		// USDA
+
+		if ( typeof buffer === 'string' ) {
+
+			return usda.parse( buffer, {} );
+
+		}
+
+		// USDC
+
+		if ( isCrateFile( buffer ) ) {
+
+			return usdc.parse( buffer );
+
+		}
+
+		// USDZ
+
+		const zip = fflate.unzipSync( new Uint8Array( buffer ) );
+
+		const assets = parseAssets( zip );
+
+		// console.log( assets );
+
+		const file = findUSD( zip );
+
+		const text = fflate.strFromU8( file );
+
+		return usda.parse( text, assets );
+
+	}
+
+}
+
+export { USDLoader };

+ 4 - 892
examples/jsm/loaders/USDZLoader.js

@@ -1,904 +1,16 @@
-import {
-	BufferAttribute,
-	BufferGeometry,
-	ClampToEdgeWrapping,
-	FileLoader,
-	Group,
-	NoColorSpace,
-	Loader,
-	Mesh,
-	MeshPhysicalMaterial,
-	MirroredRepeatWrapping,
-	RepeatWrapping,
-	SRGBColorSpace,
-	TextureLoader,
-	Object3D,
-	Vector2
-} from 'three';
+import { USDLoader } from './USDLoader.js';
 
-import * as fflate from '../libs/fflate.module.js';
+// @deprecated, r179
 
-class USDAParser {
+class USDZLoader extends USDLoader {
 
-	parse( text ) {
-
-		const data = {};
-
-		const lines = text.split( '\n' );
-
-		let string = null;
-		let target = data;
-
-		const stack = [ data ];
-
-		// debugger;
-
-		for ( const line of lines ) {
-
-			// console.log( line );
-
-			if ( line.includes( '=' ) ) {
-
-				const assignment = line.split( '=' );
-
-				const lhs = assignment[ 0 ].trim();
-				const rhs = assignment[ 1 ].trim();
-
-				if ( rhs.endsWith( '{' ) ) {
-
-					const group = {};
-					stack.push( group );
-
-					target[ lhs ] = group;
-					target = group;
-
-				} else if ( rhs.endsWith( '(' ) ) {
-
-					// see #28631
-
-					const values = rhs.slice( 0, - 1 );
-					target[ lhs ] = values;
-
-					const meta = {};
-					stack.push( meta );
-
-					target = meta;
-
-				} else {
-
-					target[ lhs ] = rhs;
-
-				}
-
-			} else if ( line.endsWith( '{' ) ) {
-
-				const group = target[ string ] || {};
-				stack.push( group );
-
-				target[ string ] = group;
-				target = group;
-
-			} else if ( line.endsWith( '}' ) ) {
-
-				stack.pop();
-
-				if ( stack.length === 0 ) continue;
-
-				target = stack[ stack.length - 1 ];
-
-			} else if ( line.endsWith( '(' ) ) {
-
-				const meta = {};
-				stack.push( meta );
-
-				string = line.split( '(' )[ 0 ].trim() || string;
-
-				target[ string ] = meta;
-				target = meta;
-
-			} else if ( line.endsWith( ')' ) ) {
-
-				stack.pop();
-
-				target = stack[ stack.length - 1 ];
-
-			} else {
-
-				string = line.trim();
-
-			}
-
-		}
-
-		return data;
-
-	}
-
-}
-
-/**
- * A loader for the USDZ format.
- *
- * USDZ files that use USDC internally are not yet supported, only USDA.
- *
- * ```js
- * const loader = new USDZLoader();
- * const model = await loader.loadAsync( 'saeukkang.usdz' );
- * scene.add( model );
- * ```
- *
- * @augments Loader
- * @three_import import { USDZLoader } from 'three/addons/loaders/USDZLoader.js';
- */
-class USDZLoader extends Loader {
-
-	/**
-	 * Constructs a new USDZ loader.
-	 *
-	 * @param {LoadingManager} [manager] - The loading manager.
-	 */
 	constructor( manager ) {
 
+		console.warn( 'USDZLoader has been deprecated. Please use USDLoader instead.' );
 		super( manager );
 
 	}
 
-	/**
-	 * Starts loading from the given URL and passes the loaded USDZ asset
-	 * to the `onLoad()` callback.
-	 *
-	 * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI.
-	 * @param {function(Group)} onLoad - Executed when the loading process has been finished.
-	 * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
-	 * @param {onErrorCallback} onError - Executed when errors occur.
-	 */
-	load( url, onLoad, onProgress, onError ) {
-
-		const scope = this;
-
-		const loader = new FileLoader( scope.manager );
-		loader.setPath( scope.path );
-		loader.setResponseType( 'arraybuffer' );
-		loader.setRequestHeader( scope.requestHeader );
-		loader.setWithCredentials( scope.withCredentials );
-		loader.load( url, function ( text ) {
-
-			try {
-
-				onLoad( scope.parse( text ) );
-
-			} catch ( e ) {
-
-				if ( onError ) {
-
-					onError( e );
-
-				} else {
-
-					console.error( e );
-
-				}
-
-				scope.manager.itemError( url );
-
-			}
-
-		}, onProgress, onError );
-
-	}
-
-	/**
-	 * Parses the given USDZ data and returns the resulting group.
-	 *
-	 * @param {ArrayBuffer} buffer - The raw USDZ data as an array buffer.
-	 * @return {Group} The parsed asset as a group.
-	 */
-	parse( buffer ) {
-
-		const parser = new USDAParser();
-
-		function parseAssets( zip ) {
-
-			const data = {};
-			const loader = new FileLoader();
-			loader.setResponseType( 'arraybuffer' );
-
-			for ( const filename in zip ) {
-
-				if ( filename.endsWith( 'png' ) ) {
-
-					const blob = new Blob( [ zip[ filename ] ], { type: 'image/png' } );
-					data[ filename ] = URL.createObjectURL( blob );
-
-				}
-
-				if ( filename.endsWith( 'usd' ) || filename.endsWith( 'usda' ) ) {
-
-					if ( isCrateFile( zip[ filename ] ) ) {
-
-						throw Error( 'THREE.USDZLoader: Crate files (.usdc or binary .usd) are not supported.' );
-
-					}
-
-					const text = fflate.strFromU8( zip[ filename ] );
-					data[ filename ] = parser.parse( text );
-
-				}
-
-			}
-
-			return data;
-
-		}
-
-		function isCrateFile( buffer ) {
-
-			// Check if this a crate file. First 7 bytes of a crate file are "PXR-USDC".
-			const fileHeader = buffer.slice( 0, 7 );
-			const crateHeader = new Uint8Array( [ 0x50, 0x58, 0x52, 0x2D, 0x55, 0x53, 0x44, 0x43 ] );
-
-			// If this is not a crate file, we assume it is a plain USDA file.
-			return fileHeader.every( ( value, index ) => value === crateHeader[ index ] );
-
-		}
-
-		function findUSD( zip ) {
-
-			if ( zip.length < 1 ) return undefined;
-
-			const firstFileName = Object.keys( zip )[ 0 ];
-			let isCrate = false;
-
-			// As per the USD specification, the first entry in the zip archive is used as the main file ("UsdStage").
-			// ASCII files can end in either .usda or .usd.
-			// See https://openusd.org/release/spec_usdz.html#layout
-			if ( firstFileName.endsWith( 'usda' ) ) return zip[ firstFileName ];
-
-			if ( firstFileName.endsWith( 'usdc' ) ) {
-
-				isCrate = true;
-
-			} else if ( firstFileName.endsWith( 'usd' ) ) {
-
-				// If this is not a crate file, we assume it is a plain USDA file.
-				if ( ! isCrateFile( zip[ firstFileName ] ) ) {
-
-					return zip[ firstFileName ];
-
-				} else {
-
-					isCrate = true;
-
-				}
-
-			}
-
-			if ( isCrate ) {
-
-				throw Error( 'THREE.USDZLoader: Crate files (.usdc or binary .usd) are not supported.' );
-
-			}
-
-		}
-
-		const zip = fflate.unzipSync( new Uint8Array( buffer ) );
-
-		// console.log( zip );
-
-		const assets = parseAssets( zip );
-
-		// console.log( assets )
-
-		const file = findUSD( zip );
-
-		// Parse file
-
-		const text = fflate.strFromU8( file );
-		const root = parser.parse( text );
-
-		// Build scene
-
-		function findMeshGeometry( data ) {
-
-			if ( ! data ) return undefined;
-
-			if ( 'prepend references' in data ) {
-
-				const reference = data[ 'prepend references' ];
-				const parts = reference.split( '@' );
-				const path = parts[ 1 ].replace( /^.\//, '' );
-				const id = parts[ 2 ].replace( /^<\//, '' ).replace( />$/, '' );
-
-				return findGeometry( assets[ path ], id );
-
-			}
-
-			return findGeometry( data );
-
-		}
-
-		function findGeometry( data, id ) {
-
-			if ( ! data ) return undefined;
-
-			if ( id !== undefined ) {
-
-				const def = `def Mesh "${id}"`;
-
-				if ( def in data ) {
-
-					return data[ def ];
-
-				}
-
-			}
-
-			for ( const name in data ) {
-
-				const object = data[ name ];
-
-				if ( name.startsWith( 'def Mesh' ) ) {
-
-					return object;
-
-				}
-
-
-				if ( typeof object === 'object' ) {
-
-					const geometry = findGeometry( object );
-
-					if ( geometry ) return geometry;
-
-				}
-
-			}
-
-		}
-
-		function buildGeometry( data ) {
-
-			if ( ! data ) return undefined;
-
-			const geometry = new BufferGeometry();
-			let indices = null;
-			let counts = null;
-			let uvs = null;
-
-			let positionsLength = - 1;
-
-			// index
-
-			if ( 'int[] faceVertexIndices' in data ) {
-
-				indices = JSON.parse( data[ 'int[] faceVertexIndices' ] );
-
-			}
-
-			// face count
-
-			if ( 'int[] faceVertexCounts' in data ) {
-
-				counts = JSON.parse( data[ 'int[] faceVertexCounts' ] );
-				indices = toTriangleIndices( indices, counts );
-
-			}
-
-			// position
-
-			if ( 'point3f[] points' in data ) {
-
-				const positions = JSON.parse( data[ 'point3f[] points' ].replace( /[()]*/g, '' ) );
-				positionsLength = positions.length;
-				let attribute = new BufferAttribute( new Float32Array( positions ), 3 );
-
-				if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
-
-				geometry.setAttribute( 'position', attribute );
-
-			}
-
-			// uv
-
-			if ( 'float2[] primvars:st' in data ) {
-
-				data[ 'texCoord2f[] primvars:st' ] = data[ 'float2[] primvars:st' ];
-
-			}
-
-			if ( 'texCoord2f[] primvars:st' in data ) {
-
-				uvs = JSON.parse( data[ 'texCoord2f[] primvars:st' ].replace( /[()]*/g, '' ) );
-				let attribute = new BufferAttribute( new Float32Array( uvs ), 2 );
-
-				if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
-
-				geometry.setAttribute( 'uv', attribute );
-
-			}
-
-			if ( 'int[] primvars:st:indices' in data && uvs !== null ) {
-
-				// custom uv index, overwrite uvs with new data
-
-				const attribute = new BufferAttribute( new Float32Array( uvs ), 2 );
-				let indices = JSON.parse( data[ 'int[] primvars:st:indices' ] );
-				indices = toTriangleIndices( indices, counts );
-				geometry.setAttribute( 'uv', toFlatBufferAttribute( attribute, indices ) );
-
-			}
-
-			// normal
-
-			if ( 'normal3f[] normals' in data ) {
-
-				const normals = JSON.parse( data[ 'normal3f[] normals' ].replace( /[()]*/g, '' ) );
-				let attribute = new BufferAttribute( new Float32Array( normals ), 3 );
-
-				// normals require a special treatment in USD
-
-				if ( normals.length === positionsLength ) {
-
-					// raw normal and position data have equal length (like produced by USDZExporter)
-
-					if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
-
-				} else {
-
-					// unequal length, normals are independent of faceVertexIndices
-
-					let indices = Array.from( Array( normals.length / 3 ).keys() ); // [ 0, 1, 2, 3 ... ]
-					indices = toTriangleIndices( indices, counts );
-					attribute = toFlatBufferAttribute( attribute, indices );
-
-				}
-
-				geometry.setAttribute( 'normal', attribute );
-
-			} else {
-
-				// compute flat vertex normals
-
-				geometry.computeVertexNormals();
-
-			}
-
-			return geometry;
-
-		}
-
-		function toTriangleIndices( rawIndices, counts ) {
-
-			const indices = [];
-
-			for ( let i = 0; i < counts.length; i ++ ) {
-
-				const count = counts[ i ];
-
-				const stride = i * count;
-
-				if ( count === 3 ) {
-
-					const a = rawIndices[ stride + 0 ];
-					const b = rawIndices[ stride + 1 ];
-					const c = rawIndices[ stride + 2 ];
-
-					indices.push( a, b, c );
-
-				} else if ( count === 4 ) {
-
-					const a = rawIndices[ stride + 0 ];
-					const b = rawIndices[ stride + 1 ];
-					const c = rawIndices[ stride + 2 ];
-					const d = rawIndices[ stride + 3 ];
-
-					indices.push( a, b, c );
-					indices.push( a, c, d );
-
-				} else {
-
-					console.warn( 'THREE.USDZLoader: Face vertex count of %s unsupported.', count );
-
-				}
-
-			}
-
-			return indices;
-
-		}
-
-		function toFlatBufferAttribute( attribute, indices ) {
-
-			const array = attribute.array;
-			const itemSize = attribute.itemSize;
-
-			const array2 = new array.constructor( indices.length * itemSize );
-
-			let index = 0, index2 = 0;
-
-			for ( let i = 0, l = indices.length; i < l; i ++ ) {
-
-				index = indices[ i ] * itemSize;
-
-				for ( let j = 0; j < itemSize; j ++ ) {
-
-					array2[ index2 ++ ] = array[ index ++ ];
-
-				}
-
-			}
-
-			return new BufferAttribute( array2, itemSize );
-
-		}
-
-		function findMeshMaterial( data ) {
-
-			if ( ! data ) return undefined;
-
-			if ( 'rel material:binding' in data ) {
-
-				const reference = data[ 'rel material:binding' ];
-				const id = reference.replace( /^<\//, '' ).replace( />$/, '' );
-				const parts = id.split( '/' );
-
-				return findMaterial( root, ` "${ parts[ 1 ] }"` );
-
-			}
-
-			return findMaterial( data );
-
-		}
-
-		function findMaterial( data, id = '' ) {
-
-			for ( const name in data ) {
-
-				const object = data[ name ];
-
-				if ( name.startsWith( 'def Material' + id ) ) {
-
-					return object;
-
-				}
-
-				if ( typeof object === 'object' ) {
-
-					const material = findMaterial( object, id );
-
-					if ( material ) return material;
-
-				}
-
-			}
-
-		}
-
-		function setTextureParams( map, data_value ) {
-
-			// rotation, scale and translation
-
-			if ( data_value[ 'float inputs:rotation' ] ) {
-
-				map.rotation = parseFloat( data_value[ 'float inputs:rotation' ] );
-
-			}
-
-			if ( data_value[ 'float2 inputs:scale' ] ) {
-
-				map.repeat = new Vector2().fromArray( JSON.parse( '[' + data_value[ 'float2 inputs:scale' ].replace( /[()]*/g, '' ) + ']' ) );
-
-			}
-
-			if ( data_value[ 'float2 inputs:translation' ] ) {
-
-				map.offset = new Vector2().fromArray( JSON.parse( '[' + data_value[ 'float2 inputs:translation' ].replace( /[()]*/g, '' ) + ']' ) );
-
-			}
-
-		}
-
-		function buildMaterial( data ) {
-
-			const material = new MeshPhysicalMaterial();
-
-			if ( data !== undefined ) {
-
-				const surfaceConnection = data[ 'token outputs:surface.connect' ];
-				const surfaceName = /(\w+).output/.exec( surfaceConnection )[ 1 ];
-				const surface = data[ `def Shader "${surfaceName}"` ];
-
-				if ( surface !== undefined ) {
-
-					if ( 'color3f inputs:diffuseColor.connect' in surface ) {
-
-						const path = surface[ 'color3f inputs:diffuseColor.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.map = buildTexture( sampler );
-						material.map.colorSpace = SRGBColorSpace;
-
-						if ( 'def Shader "Transform2d_diffuse"' in data ) {
-
-							setTextureParams( material.map, data[ 'def Shader "Transform2d_diffuse"' ] );
-
-						}
-
-					} else if ( 'color3f inputs:diffuseColor' in surface ) {
-
-						const color = surface[ 'color3f inputs:diffuseColor' ].replace( /[()]*/g, '' );
-						material.color.fromArray( JSON.parse( '[' + color + ']' ) );
-
-					}
-
-					if ( 'color3f inputs:emissiveColor.connect' in surface ) {
-
-						const path = surface[ 'color3f inputs:emissiveColor.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.emissiveMap = buildTexture( sampler );
-						material.emissiveMap.colorSpace = SRGBColorSpace;
-						material.emissive.set( 0xffffff );
-
-						if ( 'def Shader "Transform2d_emissive"' in data ) {
-
-							setTextureParams( material.emissiveMap, data[ 'def Shader "Transform2d_emissive"' ] );
-
-						}
-
-					} else if ( 'color3f inputs:emissiveColor' in surface ) {
-
-						const color = surface[ 'color3f inputs:emissiveColor' ].replace( /[()]*/g, '' );
-						material.emissive.fromArray( JSON.parse( '[' + color + ']' ) );
-
-					}
-
-					if ( 'normal3f inputs:normal.connect' in surface ) {
-
-						const path = surface[ 'normal3f inputs:normal.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.normalMap = buildTexture( sampler );
-						material.normalMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_normal"' in data ) {
-
-							setTextureParams( material.normalMap, data[ 'def Shader "Transform2d_normal"' ] );
-
-						}
-
-					}
-
-					if ( 'float inputs:roughness.connect' in surface ) {
-
-						const path = surface[ 'float inputs:roughness.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.roughness = 1.0;
-						material.roughnessMap = buildTexture( sampler );
-						material.roughnessMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_roughness"' in data ) {
-
-							setTextureParams( material.roughnessMap, data[ 'def Shader "Transform2d_roughness"' ] );
-
-						}
-
-					} else if ( 'float inputs:roughness' in surface ) {
-
-						material.roughness = parseFloat( surface[ 'float inputs:roughness' ] );
-
-					}
-
-					if ( 'float inputs:metallic.connect' in surface ) {
-
-						const path = surface[ 'float inputs:metallic.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.metalness = 1.0;
-						material.metalnessMap = buildTexture( sampler );
-						material.metalnessMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_metallic"' in data ) {
-
-							setTextureParams( material.metalnessMap, data[ 'def Shader "Transform2d_metallic"' ] );
-
-						}
-
-					} else if ( 'float inputs:metallic' in surface ) {
-
-						material.metalness = parseFloat( surface[ 'float inputs:metallic' ] );
-
-					}
-
-					if ( 'float inputs:clearcoat.connect' in surface ) {
-
-						const path = surface[ 'float inputs:clearcoat.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.clearcoat = 1.0;
-						material.clearcoatMap = buildTexture( sampler );
-						material.clearcoatMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_clearcoat"' in data ) {
-
-							setTextureParams( material.clearcoatMap, data[ 'def Shader "Transform2d_clearcoat"' ] );
-
-						}
-
-					} else if ( 'float inputs:clearcoat' in surface ) {
-
-						material.clearcoat = parseFloat( surface[ 'float inputs:clearcoat' ] );
-
-					}
-
-					if ( 'float inputs:clearcoatRoughness.connect' in surface ) {
-
-						const path = surface[ 'float inputs:clearcoatRoughness.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.clearcoatRoughness = 1.0;
-						material.clearcoatRoughnessMap = buildTexture( sampler );
-						material.clearcoatRoughnessMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_clearcoatRoughness"' in data ) {
-
-							setTextureParams( material.clearcoatRoughnessMap, data[ 'def Shader "Transform2d_clearcoatRoughness"' ] );
-
-						}
-
-					} else if ( 'float inputs:clearcoatRoughness' in surface ) {
-
-						material.clearcoatRoughness = parseFloat( surface[ 'float inputs:clearcoatRoughness' ] );
-
-					}
-
-					if ( 'float inputs:ior' in surface ) {
-
-						material.ior = parseFloat( surface[ 'float inputs:ior' ] );
-
-					}
-
-					if ( 'float inputs:occlusion.connect' in surface ) {
-
-						const path = surface[ 'float inputs:occlusion.connect' ];
-						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
-
-						material.aoMap = buildTexture( sampler );
-						material.aoMap.colorSpace = NoColorSpace;
-
-						if ( 'def Shader "Transform2d_occlusion"' in data ) {
-
-							setTextureParams( material.aoMap, data[ 'def Shader "Transform2d_occlusion"' ] );
-
-						}
-
-					}
-
-				}
-
-			}
-
-			return material;
-
-		}
-
-		function findTexture( data, id ) {
-
-			for ( const name in data ) {
-
-				const object = data[ name ];
-
-				if ( name.startsWith( `def Shader "${ id }"` ) ) {
-
-					return object;
-
-				}
-
-				if ( typeof object === 'object' ) {
-
-					const texture = findTexture( object, id );
-
-					if ( texture ) return texture;
-
-				}
-
-			}
-
-		}
-
-		function buildTexture( data ) {
-
-			if ( 'asset inputs:file' in data ) {
-
-				const path = data[ 'asset inputs:file' ].replace( /@*/g, '' ).trim();
-
-				const loader = new TextureLoader();
-
-				const texture = loader.load( assets[ path ] );
-
-				const map = {
-					'"clamp"': ClampToEdgeWrapping,
-					'"mirror"': MirroredRepeatWrapping,
-					'"repeat"': RepeatWrapping
-				};
-
-				if ( 'token inputs:wrapS' in data ) {
-
-					texture.wrapS = map[ data[ 'token inputs:wrapS' ] ];
-
-				}
-
-				if ( 'token inputs:wrapT' in data ) {
-
-					texture.wrapT = map[ data[ 'token inputs:wrapT' ] ];
-
-				}
-
-				return texture;
-
-			}
-
-			return null;
-
-		}
-
-		function buildObject( data ) {
-
-			const geometry = buildGeometry( findMeshGeometry( data ) );
-			const material = buildMaterial( findMeshMaterial( data ) );
-
-			const mesh = geometry ? new Mesh( geometry, material ) : new Object3D();
-
-			if ( 'matrix4d xformOp:transform' in data ) {
-
-				const array = JSON.parse( '[' + data[ 'matrix4d xformOp:transform' ].replace( /[()]*/g, '' ) + ']' );
-
-				mesh.matrix.fromArray( array );
-				mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
-
-			}
-
-			return mesh;
-
-		}
-
-		function buildHierarchy( data, group ) {
-
-			for ( const name in data ) {
-
-				if ( name.startsWith( 'def Scope' ) ) {
-
-					buildHierarchy( data[ name ], group );
-
-				} else if ( name.startsWith( 'def Xform' ) ) {
-
-					const mesh = buildObject( data[ name ] );
-
-					if ( /def Xform "(\w+)"/.test( name ) ) {
-
-						mesh.name = /def Xform "(\w+)"/.exec( name )[ 1 ];
-
-					}
-
-					group.add( mesh );
-
-					buildHierarchy( data[ name ], mesh );
-
-				}
-
-			}
-
-		}
-
-		const group = new Group();
-
-		buildHierarchy( root, group );
-
-		return group;
-
-	}
-
 }
 
 export { USDZLoader };

+ 741 - 0
examples/jsm/loaders/usd/USDAParser.js

@@ -0,0 +1,741 @@
+import {
+	BufferAttribute,
+	BufferGeometry,
+	ClampToEdgeWrapping,
+	Group,
+	NoColorSpace,
+	Mesh,
+	MeshPhysicalMaterial,
+	MirroredRepeatWrapping,
+	RepeatWrapping,
+	SRGBColorSpace,
+	TextureLoader,
+	Object3D,
+	Vector2
+} from 'three';
+
+class USDAParser {
+
+	parseText( text ) {
+
+		const root = {};
+
+		const lines = text.split( '\n' );
+
+		let string = null;
+		let target = root;
+
+		const stack = [ root ];
+
+		// Parse USDA file
+
+		for ( const line of lines ) {
+
+			// console.log( line );
+
+			if ( line.includes( '=' ) ) {
+
+				const assignment = line.split( '=' );
+
+				const lhs = assignment[ 0 ].trim();
+				const rhs = assignment[ 1 ].trim();
+
+				if ( rhs.endsWith( '{' ) ) {
+
+					const group = {};
+					stack.push( group );
+
+					target[ lhs ] = group;
+					target = group;
+
+				} else if ( rhs.endsWith( '(' ) ) {
+
+					// see #28631
+
+					const values = rhs.slice( 0, - 1 );
+					target[ lhs ] = values;
+
+					const meta = {};
+					stack.push( meta );
+
+					target = meta;
+
+				} else {
+
+					target[ lhs ] = rhs;
+
+				}
+
+			} else if ( line.endsWith( '{' ) ) {
+
+				const group = target[ string ] || {};
+				stack.push( group );
+
+				target[ string ] = group;
+				target = group;
+
+			} else if ( line.endsWith( '}' ) ) {
+
+				stack.pop();
+
+				if ( stack.length === 0 ) continue;
+
+				target = stack[ stack.length - 1 ];
+
+			} else if ( line.endsWith( '(' ) ) {
+
+				const meta = {};
+				stack.push( meta );
+
+				string = line.split( '(' )[ 0 ].trim() || string;
+
+				target[ string ] = meta;
+				target = meta;
+
+			} else if ( line.endsWith( ')' ) ) {
+
+				stack.pop();
+
+				target = stack[ stack.length - 1 ];
+
+			} else {
+
+				string = line.trim();
+
+			}
+
+		}
+
+		return root;
+
+	}
+
+	parse( text, assets ) {
+
+		const root = this.parseText( text );
+
+		// Build scene graph
+
+		function findMeshGeometry( data ) {
+
+			if ( ! data ) return undefined;
+
+			if ( 'prepend references' in data ) {
+
+				const reference = data[ 'prepend references' ];
+				const parts = reference.split( '@' );
+				const path = parts[ 1 ].replace( /^.\//, '' );
+				const id = parts[ 2 ].replace( /^<\//, '' ).replace( />$/, '' );
+
+				return findGeometry( assets[ path ], id );
+
+			}
+
+			return findGeometry( data );
+
+		}
+
+		function findGeometry( data, id ) {
+
+			if ( ! data ) return undefined;
+
+			if ( id !== undefined ) {
+
+				const def = `def Mesh "${id}"`;
+
+				if ( def in data ) {
+
+					return data[ def ];
+
+				}
+
+			}
+
+			for ( const name in data ) {
+
+				const object = data[ name ];
+
+				if ( name.startsWith( 'def Mesh' ) ) {
+
+					return object;
+
+				}
+
+
+				if ( typeof object === 'object' ) {
+
+					const geometry = findGeometry( object );
+
+					if ( geometry ) return geometry;
+
+				}
+
+			}
+
+		}
+
+		function buildGeometry( data ) {
+
+			if ( ! data ) return undefined;
+
+			const geometry = new BufferGeometry();
+			let indices = null;
+			let counts = null;
+			let uvs = null;
+
+			let positionsLength = - 1;
+
+			// index
+
+			if ( 'int[] faceVertexIndices' in data ) {
+
+				indices = JSON.parse( data[ 'int[] faceVertexIndices' ] );
+
+			}
+
+			// face count
+
+			if ( 'int[] faceVertexCounts' in data ) {
+
+				counts = JSON.parse( data[ 'int[] faceVertexCounts' ] );
+				indices = toTriangleIndices( indices, counts );
+
+			}
+
+			// position
+
+			if ( 'point3f[] points' in data ) {
+
+				const positions = JSON.parse( data[ 'point3f[] points' ].replace( /[()]*/g, '' ) );
+				positionsLength = positions.length;
+				let attribute = new BufferAttribute( new Float32Array( positions ), 3 );
+
+				if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
+
+				geometry.setAttribute( 'position', attribute );
+
+			}
+
+			// uv
+
+			if ( 'float2[] primvars:st' in data ) {
+
+				data[ 'texCoord2f[] primvars:st' ] = data[ 'float2[] primvars:st' ];
+
+			}
+
+			if ( 'texCoord2f[] primvars:st' in data ) {
+
+				uvs = JSON.parse( data[ 'texCoord2f[] primvars:st' ].replace( /[()]*/g, '' ) );
+				let attribute = new BufferAttribute( new Float32Array( uvs ), 2 );
+
+				if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
+
+				geometry.setAttribute( 'uv', attribute );
+
+			}
+
+			if ( 'int[] primvars:st:indices' in data && uvs !== null ) {
+
+				// custom uv index, overwrite uvs with new data
+
+				const attribute = new BufferAttribute( new Float32Array( uvs ), 2 );
+				let indices = JSON.parse( data[ 'int[] primvars:st:indices' ] );
+				indices = toTriangleIndices( indices, counts );
+				geometry.setAttribute( 'uv', toFlatBufferAttribute( attribute, indices ) );
+
+			}
+
+			// normal
+
+			if ( 'normal3f[] normals' in data ) {
+
+				const normals = JSON.parse( data[ 'normal3f[] normals' ].replace( /[()]*/g, '' ) );
+				let attribute = new BufferAttribute( new Float32Array( normals ), 3 );
+
+				// normals require a special treatment in USD
+
+				if ( normals.length === positionsLength ) {
+
+					// raw normal and position data have equal length (like produced by USDZExporter)
+
+					if ( indices !== null ) attribute = toFlatBufferAttribute( attribute, indices );
+
+				} else {
+
+					// unequal length, normals are independent of faceVertexIndices
+
+					let indices = Array.from( Array( normals.length / 3 ).keys() ); // [ 0, 1, 2, 3 ... ]
+					indices = toTriangleIndices( indices, counts );
+					attribute = toFlatBufferAttribute( attribute, indices );
+
+				}
+
+				geometry.setAttribute( 'normal', attribute );
+
+			} else {
+
+				// compute flat vertex normals
+
+				geometry.computeVertexNormals();
+
+			}
+
+			return geometry;
+
+		}
+
+		function toTriangleIndices( rawIndices, counts ) {
+
+			const indices = [];
+
+			for ( let i = 0; i < counts.length; i ++ ) {
+
+				const count = counts[ i ];
+
+				const stride = i * count;
+
+				if ( count === 3 ) {
+
+					const a = rawIndices[ stride + 0 ];
+					const b = rawIndices[ stride + 1 ];
+					const c = rawIndices[ stride + 2 ];
+
+					indices.push( a, b, c );
+
+				} else if ( count === 4 ) {
+
+					const a = rawIndices[ stride + 0 ];
+					const b = rawIndices[ stride + 1 ];
+					const c = rawIndices[ stride + 2 ];
+					const d = rawIndices[ stride + 3 ];
+
+					indices.push( a, b, c );
+					indices.push( a, c, d );
+
+				} else {
+
+					console.warn( 'THREE.USDZLoader: Face vertex count of %s unsupported.', count );
+
+				}
+
+			}
+
+			return indices;
+
+		}
+
+		function toFlatBufferAttribute( attribute, indices ) {
+
+			const array = attribute.array;
+			const itemSize = attribute.itemSize;
+
+			const array2 = new array.constructor( indices.length * itemSize );
+
+			let index = 0, index2 = 0;
+
+			for ( let i = 0, l = indices.length; i < l; i ++ ) {
+
+				index = indices[ i ] * itemSize;
+
+				for ( let j = 0; j < itemSize; j ++ ) {
+
+					array2[ index2 ++ ] = array[ index ++ ];
+
+				}
+
+			}
+
+			return new BufferAttribute( array2, itemSize );
+
+		}
+
+		function findMeshMaterial( data ) {
+
+			if ( ! data ) return undefined;
+
+			if ( 'rel material:binding' in data ) {
+
+				const reference = data[ 'rel material:binding' ];
+				const id = reference.replace( /^<\//, '' ).replace( />$/, '' );
+				const parts = id.split( '/' );
+
+				return findMaterial( root, ` "${ parts[ 1 ] }"` );
+
+			}
+
+			return findMaterial( data );
+
+		}
+
+		function findMaterial( data, id = '' ) {
+
+			for ( const name in data ) {
+
+				const object = data[ name ];
+
+				if ( name.startsWith( 'def Material' + id ) ) {
+
+					return object;
+
+				}
+
+				if ( typeof object === 'object' ) {
+
+					const material = findMaterial( object, id );
+
+					if ( material ) return material;
+
+				}
+
+			}
+
+		}
+
+		function setTextureParams( map, data_value ) {
+
+			// rotation, scale and translation
+
+			if ( data_value[ 'float inputs:rotation' ] ) {
+
+				map.rotation = parseFloat( data_value[ 'float inputs:rotation' ] );
+
+			}
+
+			if ( data_value[ 'float2 inputs:scale' ] ) {
+
+				map.repeat = new Vector2().fromArray( JSON.parse( '[' + data_value[ 'float2 inputs:scale' ].replace( /[()]*/g, '' ) + ']' ) );
+
+			}
+
+			if ( data_value[ 'float2 inputs:translation' ] ) {
+
+				map.offset = new Vector2().fromArray( JSON.parse( '[' + data_value[ 'float2 inputs:translation' ].replace( /[()]*/g, '' ) + ']' ) );
+
+			}
+
+		}
+
+		function buildMaterial( data ) {
+
+			const material = new MeshPhysicalMaterial();
+
+			if ( data !== undefined ) {
+
+				let surface = undefined;
+
+				const surfaceConnection = data[ 'token outputs:surface.connect' ];
+
+				if ( surfaceConnection ) {
+
+					const match = /(\w+)\.output/.exec( surfaceConnection );
+
+					if ( match ) {
+
+						const surfaceName = match[ 1 ];
+						surface = data[ `def Shader "${surfaceName}"` ];
+
+					}
+
+				}
+
+				if ( surface !== undefined ) {
+
+					if ( 'color3f inputs:diffuseColor.connect' in surface ) {
+
+						const path = surface[ 'color3f inputs:diffuseColor.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.map = buildTexture( sampler );
+						material.map.colorSpace = SRGBColorSpace;
+
+						if ( 'def Shader "Transform2d_diffuse"' in data ) {
+
+							setTextureParams( material.map, data[ 'def Shader "Transform2d_diffuse"' ] );
+
+						}
+
+					} else if ( 'color3f inputs:diffuseColor' in surface ) {
+
+						const color = surface[ 'color3f inputs:diffuseColor' ].replace( /[()]*/g, '' );
+						material.color.fromArray( JSON.parse( '[' + color + ']' ) );
+
+					}
+
+					if ( 'color3f inputs:emissiveColor.connect' in surface ) {
+
+						const path = surface[ 'color3f inputs:emissiveColor.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.emissiveMap = buildTexture( sampler );
+						material.emissiveMap.colorSpace = SRGBColorSpace;
+						material.emissive.set( 0xffffff );
+
+						if ( 'def Shader "Transform2d_emissive"' in data ) {
+
+							setTextureParams( material.emissiveMap, data[ 'def Shader "Transform2d_emissive"' ] );
+
+						}
+
+					} else if ( 'color3f inputs:emissiveColor' in surface ) {
+
+						const color = surface[ 'color3f inputs:emissiveColor' ].replace( /[()]*/g, '' );
+						material.emissive.fromArray( JSON.parse( '[' + color + ']' ) );
+
+					}
+
+					if ( 'normal3f inputs:normal.connect' in surface ) {
+
+						const path = surface[ 'normal3f inputs:normal.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.normalMap = buildTexture( sampler );
+						material.normalMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_normal"' in data ) {
+
+							setTextureParams( material.normalMap, data[ 'def Shader "Transform2d_normal"' ] );
+
+						}
+
+					}
+
+					if ( 'float inputs:roughness.connect' in surface ) {
+
+						const path = surface[ 'float inputs:roughness.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.roughness = 1.0;
+						material.roughnessMap = buildTexture( sampler );
+						material.roughnessMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_roughness"' in data ) {
+
+							setTextureParams( material.roughnessMap, data[ 'def Shader "Transform2d_roughness"' ] );
+
+						}
+
+					} else if ( 'float inputs:roughness' in surface ) {
+
+						material.roughness = parseFloat( surface[ 'float inputs:roughness' ] );
+
+					}
+
+					if ( 'float inputs:metallic.connect' in surface ) {
+
+						const path = surface[ 'float inputs:metallic.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.metalness = 1.0;
+						material.metalnessMap = buildTexture( sampler );
+						material.metalnessMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_metallic"' in data ) {
+
+							setTextureParams( material.metalnessMap, data[ 'def Shader "Transform2d_metallic"' ] );
+
+						}
+
+					} else if ( 'float inputs:metallic' in surface ) {
+
+						material.metalness = parseFloat( surface[ 'float inputs:metallic' ] );
+
+					}
+
+					if ( 'float inputs:clearcoat.connect' in surface ) {
+
+						const path = surface[ 'float inputs:clearcoat.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.clearcoat = 1.0;
+						material.clearcoatMap = buildTexture( sampler );
+						material.clearcoatMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_clearcoat"' in data ) {
+
+							setTextureParams( material.clearcoatMap, data[ 'def Shader "Transform2d_clearcoat"' ] );
+
+						}
+
+					} else if ( 'float inputs:clearcoat' in surface ) {
+
+						material.clearcoat = parseFloat( surface[ 'float inputs:clearcoat' ] );
+
+					}
+
+					if ( 'float inputs:clearcoatRoughness.connect' in surface ) {
+
+						const path = surface[ 'float inputs:clearcoatRoughness.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.clearcoatRoughness = 1.0;
+						material.clearcoatRoughnessMap = buildTexture( sampler );
+						material.clearcoatRoughnessMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_clearcoatRoughness"' in data ) {
+
+							setTextureParams( material.clearcoatRoughnessMap, data[ 'def Shader "Transform2d_clearcoatRoughness"' ] );
+
+						}
+
+					} else if ( 'float inputs:clearcoatRoughness' in surface ) {
+
+						material.clearcoatRoughness = parseFloat( surface[ 'float inputs:clearcoatRoughness' ] );
+
+					}
+
+					if ( 'float inputs:ior' in surface ) {
+
+						material.ior = parseFloat( surface[ 'float inputs:ior' ] );
+
+					}
+
+					if ( 'float inputs:occlusion.connect' in surface ) {
+
+						const path = surface[ 'float inputs:occlusion.connect' ];
+						const sampler = findTexture( root, /(\w+).output/.exec( path )[ 1 ] );
+
+						material.aoMap = buildTexture( sampler );
+						material.aoMap.colorSpace = NoColorSpace;
+
+						if ( 'def Shader "Transform2d_occlusion"' in data ) {
+
+							setTextureParams( material.aoMap, data[ 'def Shader "Transform2d_occlusion"' ] );
+
+						}
+
+					}
+
+				}
+
+			}
+
+			return material;
+
+		}
+
+		function findTexture( data, id ) {
+
+			for ( const name in data ) {
+
+				const object = data[ name ];
+
+				if ( name.startsWith( `def Shader "${ id }"` ) ) {
+
+					return object;
+
+				}
+
+				if ( typeof object === 'object' ) {
+
+					const texture = findTexture( object, id );
+
+					if ( texture ) return texture;
+
+				}
+
+			}
+
+		}
+
+		function buildTexture( data ) {
+
+			if ( 'asset inputs:file' in data ) {
+
+				const path = data[ 'asset inputs:file' ].replace( /@*/g, '' ).trim();
+
+				const loader = new TextureLoader();
+
+				const texture = loader.load( assets[ path ] );
+
+				const map = {
+					'"clamp"': ClampToEdgeWrapping,
+					'"mirror"': MirroredRepeatWrapping,
+					'"repeat"': RepeatWrapping
+				};
+
+				if ( 'token inputs:wrapS' in data ) {
+
+					texture.wrapS = map[ data[ 'token inputs:wrapS' ] ];
+
+				}
+
+				if ( 'token inputs:wrapT' in data ) {
+
+					texture.wrapT = map[ data[ 'token inputs:wrapT' ] ];
+
+				}
+
+				return texture;
+
+			}
+
+			return null;
+
+		}
+
+		function buildObject( data ) {
+
+			const geometry = buildGeometry( findMeshGeometry( data ) );
+			const material = buildMaterial( findMeshMaterial( data ) );
+
+			const mesh = geometry ? new Mesh( geometry, material ) : new Object3D();
+
+			if ( 'matrix4d xformOp:transform' in data ) {
+
+				const array = JSON.parse( '[' + data[ 'matrix4d xformOp:transform' ].replace( /[()]*/g, '' ) + ']' );
+
+				mesh.matrix.fromArray( array );
+				mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
+
+			}
+
+			return mesh;
+
+		}
+
+		function buildHierarchy( data, group ) {
+
+			for ( const name in data ) {
+
+				if ( name.startsWith( 'def Scope' ) ) {
+
+					buildHierarchy( data[ name ], group );
+
+				} else if ( name.startsWith( 'def Xform' ) ) {
+
+					const mesh = buildObject( data[ name ] );
+
+					if ( /def Xform "(\w+)"/.test( name ) ) {
+
+						mesh.name = /def Xform "(\w+)"/.exec( name )[ 1 ];
+
+					}
+
+					group.add( mesh );
+
+					buildHierarchy( data[ name ], mesh );
+
+				}
+
+			}
+
+		}
+
+		function buildGroup( data ) {
+
+			const group = new Group();
+
+			buildHierarchy( data, group );
+
+			return group;
+
+		}
+
+		return buildGroup( root );
+
+	}
+
+}
+
+export { USDAParser };

+ 17 - 0
examples/jsm/loaders/usd/USDCParser.js

@@ -0,0 +1,17 @@
+import {
+	Group
+} from 'three';
+
+class USDCParser {
+
+	parse( buffer ) {
+
+		// TODO
+
+		return new Group();
+
+	}
+
+}
+
+export { USDCParser };

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

@@ -1,222 +0,0 @@
-/**
- * This class is an alternative to {@link Clock} with a different API design and behavior.
- * The goal is to avoid the conceptual flaws that became apparent in `Clock` over time.
- *
- * - `Timer` has an `update()` method that updates its internal state. That makes it possible to
- * call `getDelta()` and `getElapsed()` multiple times per simulation step without getting different values.
- * - The class can make use of the Page Visibility API to avoid large time delta values when the app
- * is inactive (e.g. tab switched or browser hidden).
- *
- * ```js
- * const timer = new Timer();
- * timer.connect( document ); // use Page Visibility API
- * ```
- *
- * @three_import import { Timer } from 'three/addons/misc/Timer.js';
- */
-class Timer {
-
-	/**
-	 * Constructs a new timer.
-	 */
-	constructor() {
-
-		this._previousTime = 0;
-		this._currentTime = 0;
-		this._startTime = now();
-
-		this._delta = 0;
-		this._elapsed = 0;
-
-		this._timescale = 1;
-
-		this._document = null;
-		this._pageVisibilityHandler = null;
-
-	}
-
-	/**
-	 * Connect the timer to the given document.Calling this method is not mandatory to
-	 * use the timer but enables the usage of the Page Visibility API to avoid large time
-	 * delta values.
-	 *
-	 * @param {Document} document - The document.
-	 */
-	connect( document ) {
-
-		this._document = document;
-
-		// use Page Visibility API to avoid large time delta values
-
-		if ( document.hidden !== undefined ) {
-
-			this._pageVisibilityHandler = handleVisibilityChange.bind( this );
-
-			document.addEventListener( 'visibilitychange', this._pageVisibilityHandler, false );
-
-		}
-
-	}
-
-	/**
-	 * Disconnects the timer from the DOM and also disables the usage of the Page Visibility API.
-	 */
-	disconnect() {
-
-		if ( this._pageVisibilityHandler !== null ) {
-
-			this._document.removeEventListener( 'visibilitychange', this._pageVisibilityHandler );
-			this._pageVisibilityHandler = null;
-
-		}
-
-		this._document = null;
-
-	}
-
-	/**
-	 * Returns the time delta in seconds.
-	 *
-	 * @return {number} The time delta in second.
-	 */
-	getDelta() {
-
-		return this._delta / 1000;
-
-	}
-
-	/**
-	 * Returns the elapsed time in seconds.
-	 *
-	 * @return {number} The elapsed time in second.
-	 */
-	getElapsed() {
-
-		return this._elapsed / 1000;
-
-	}
-
-	/**
-	 * Returns the timescale.
-	 *
-	 * @return {number} The timescale.
-	 */
-	getTimescale() {
-
-		return this._timescale;
-
-	}
-
-	/**
-	 * Sets the given timescale which scale the time delta computation
-	 * in `update()`.
-	 *
-	 * @param {number} timescale - The timescale to set.
-	 * @return {Timer} A reference to this timer.
-	 */
-	setTimescale( timescale ) {
-
-		this._timescale = timescale;
-
-		return this;
-
-	}
-
-	/**
-	 * Resets the time computation for the current simulation step.
-	 *
-	 * @return {Timer} A reference to this timer.
-	 */
-	reset() {
-
-		this._currentTime = now() - this._startTime;
-
-		return this;
-
-	}
-
-	/**
-	 * Can be used to free all internal resources. Usually called when
-	 * the timer instance isn't required anymore.
-	 */
-	dispose() {
-
-		this.disconnect();
-
-	}
-
-	/**
-	 * 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 `getDelta()`).
-	 *
-	 * @param {number} timestamp - The current time in milliseconds. Can be obtained
-	 * from the `requestAnimationFrame` callback argument. If not provided, the current
-	 * time will be determined with `performance.now`.
-	 * @return {Timer} A reference to this timer.
-	 */
-	update( timestamp ) {
-
-		if ( this._pageVisibilityHandler !== null && this._document.hidden === true ) {
-
-			this._delta = 0;
-
-		} else {
-
-			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;
-
-	}
-
-}
-
-/**
- * A special version of a timer with a fixed time delta value.
- * Can be useful for testing and debugging purposes.
- *
- * @augments Timer
- */
-class FixedTimer extends Timer {
-
-	/**
-	 * Constructs a new timer.
-	 *
-	 * @param {number} [fps=60] - The fixed FPS of this 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 performance.now();
-
-}
-
-function handleVisibilityChange() {
-
-	if ( this._document.hidden === false ) this.reset();
-
-}
-
-export { Timer, FixedTimer };

+ 3 - 3
examples/jsm/objects/LensflareMesh.js

@@ -110,7 +110,7 @@ class LensflareMesh extends Mesh {
 		material1a.type = 'Lensflare-1a';
 
 		material1a.vertexNode = vertexNode;
-		material1a.fragmentNode = vec4( 1.0, 0.0, 1.0, 1.0 );
+		material1a.colorNode = vec4( 1.0, 0.0, 1.0, 1.0 );
 
 		const material1b = new NodeMaterial();
 
@@ -121,7 +121,7 @@ class LensflareMesh extends Mesh {
 		material1b.type = 'Lensflare-1b';
 
 		material1b.vertexNode = vertexNode;
-		material1b.fragmentNode = texture( tempMap, vec2( uv().flipY() ) );
+		material1b.colorNode = texture( tempMap, vec2( uv().flipY() ) );
 
 		// the following object is used for occlusionMap generation
 
@@ -174,7 +174,7 @@ class LensflareMesh extends Mesh {
 
 		} )();
 
-		material2.fragmentNode = Fn( () => {
+		material2.colorNode = Fn( () => {
 
 			const color = reference( 'color', 'color' );
 			const map = reference( 'map', 'texture' );

+ 2 - 2
examples/jsm/objects/SkyMesh.js

@@ -165,7 +165,7 @@ class SkyMesh extends Mesh {
 
 		} )();
 
-		const fragmentNode = /*@__PURE__*/ Fn( () => {
+		const colorNode = /*@__PURE__*/ Fn( () => {
 
 			// constants for atmospheric scattering
 			const pi = float( 3.141592653589793238462643383279502884197169 );
@@ -234,7 +234,7 @@ class SkyMesh extends Mesh {
 		material.depthWrite = false;
 
 		material.vertexNode = vertexNode;
-		material.fragmentNode = fragmentNode;
+		material.colorNode = colorNode;
 
 	}
 

+ 14 - 5
examples/jsm/physics/RapierPhysics.js

@@ -15,7 +15,16 @@ function getShape( geometry ) {
 
 	// TODO change type to is*
 
-	if ( geometry.type === 'BoxGeometry' ) {
+	if ( geometry.type === 'RoundedBoxGeometry' ) {
+
+		const sx = parameters.width !== undefined ? parameters.width / 2 : 0.5;
+		const sy = parameters.height !== undefined ? parameters.height / 2 : 0.5;
+		const sz = parameters.depth !== undefined ? parameters.depth / 2 : 0.5;
+		const radius = parameters.radius !== undefined ? parameters.radius : 0.1;
+
+		return RAPIER.ColliderDesc.roundCuboid( sx - radius, sy - radius, sz - radius, radius );
+
+	} else if ( geometry.type === 'BoxGeometry' ) {
 
 		const sx = parameters.width !== undefined ? parameters.width / 2 : 0.5;
 		const sy = parameters.height !== undefined ? parameters.height / 2 : 0.5;
@@ -273,17 +282,17 @@ async function RapierPhysics() {
 	function addHeightfield( mesh, width, depth, heights, scale ) {
 
 		const shape = RAPIER.ColliderDesc.heightfield( width, depth, heights, scale );
-		
+
 		const bodyDesc = RAPIER.RigidBodyDesc.fixed();
 		bodyDesc.setTranslation( mesh.position.x, mesh.position.y, mesh.position.z );
 		bodyDesc.setRotation( mesh.quaternion );
-		
+
 		const body = world.createRigidBody( bodyDesc );
 		world.createCollider( shape, body );
-		
+
 		if ( ! mesh.userData.physics ) mesh.userData.physics = {};
 		mesh.userData.physics.body = body;
-		
+
 		return body;
 
 	}

+ 10 - 9
examples/jsm/postprocessing/GTAOPass.js

@@ -103,7 +103,7 @@ class GTAOPass extends Pass {
 		 */
 		this.output = 0;
 		this._renderGBuffer = true;
-		this._visibilityCache = new Map();
+		this._visibilityCache = [];
 
 		/**
 		 * The AO blend intensity.
@@ -655,9 +655,12 @@ class GTAOPass extends Pass {
 
 		scene.traverse( function ( object ) {
 
-			cache.set( object, object.visible );
+			if ( ( object.isPoints || object.isLine || object.isLine2 ) && object.visible ) {
 
-			if ( object.isPoints || object.isLine ) object.visible = false;
+				object.visible = false;
+				cache.push( object );
+
+			}
 
 		} );
 
@@ -665,17 +668,15 @@ class GTAOPass extends Pass {
 
 	_restoreVisibility() {
 
-		const scene = this.scene;
 		const cache = this._visibilityCache;
 
-		scene.traverse( function ( object ) {
+		for ( let i = 0; i < cache.length; i ++ ) {
 
-			const visible = cache.get( object );
-			object.visible = visible;
+			cache[ i ].visible = true;
 
-		} );
+		}
 
-		cache.clear();
+		cache.length = 0;
 
 	}
 

+ 17 - 17
examples/jsm/postprocessing/OutlinePass.js

@@ -494,37 +494,37 @@ class OutlinePass extends Pass {
 
 		function VisibilityChangeCallBack( object ) {
 
-			if ( object.isMesh || object.isSprite ) {
+			if ( object.isPoints || object.isLine || object.isLine2 ) {
 
-				// only meshes and sprites are supported by OutlinePass
+				// the visibility of points and lines is always set to false in order to
+				// not affect the outline computation
 
-				if ( ! selectionCache.has( object ) ) {
+				if ( bVisible === true ) {
 
-					const visibility = object.visible;
+					object.visible = visibilityCache.get( object ); // restore
 
-					if ( bVisible === false || visibilityCache.get( object ) === true ) {
+				} else {
 
-						object.visible = bVisible;
+					visibilityCache.set( object, object.visible );
+					object.visible = bVisible;
 
-					}
+				}
 
-					visibilityCache.set( object, visibility );
+			} else if ( object.isMesh || object.isSprite) {
 
-				}
+				// only meshes and sprites are supported by OutlinePass
 
-			} else if ( object.isPoints || object.isLine ) {
+				if ( ! selectionCache.has( object ) ) {
 
-				// the visibility of points and lines is always set to false in order to
-				// not affect the outline computation
+					const visibility = object.visible;
 
-				if ( bVisible === true ) {
+					if ( bVisible === false || visibilityCache.get( object ) === true ) {
 
-					object.visible = visibilityCache.get( object ); // restore
+						object.visible = bVisible;
 
-				} else {
+					}
 
-					visibilityCache.set( object, object.visible );
-					object.visible = bVisible;
+					visibilityCache.set( object, visibility );
 
 				}
 

+ 10 - 9
examples/jsm/postprocessing/SSAOPass.js

@@ -139,7 +139,7 @@ class SSAOPass extends Pass {
 		 */
 		this.maxDistance = 0.1;
 
-		this._visibilityCache = new Map();
+		this._visibilityCache = [];
 
 		//
 
@@ -489,9 +489,12 @@ class SSAOPass extends Pass {
 
 		scene.traverse( function ( object ) {
 
-			cache.set( object, object.visible );
+			if ( ( object.isPoints || object.isLine || object.isLine2 ) && object.visible ) {
 
-			if ( object.isPoints || object.isLine ) object.visible = false;
+				object.visible = false;
+				cache.push( object );
+
+			}
 
 		} );
 
@@ -499,17 +502,15 @@ class SSAOPass extends Pass {
 
 	_restoreVisibility() {
 
-		const scene = this.scene;
 		const cache = this._visibilityCache;
 
-		scene.traverse( function ( object ) {
+		for ( let i = 0; i < cache.length; i ++ ) {
 
-			const visible = cache.get( object );
-			object.visible = visible;
+			cache[ i ].visible = true;
 
-		} );
+		}
 
-		cache.clear();
+		cache.length = 0;
 
 	}
 

+ 11 - 2
examples/jsm/shaders/UnpackDepthRGBAShader.js

@@ -43,8 +43,17 @@ const UnpackDepthRGBAShader = {
 
 		void main() {
 
-			float depth = 1.0 - unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
-			gl_FragColor = vec4( vec3( depth ), opacity );
+			float depth = unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
+
+			#ifdef USE_REVERSEDEPTHBUF
+
+				gl_FragColor = vec4( vec3( depth ), opacity );
+
+			#else
+
+				gl_FragColor = vec4( vec3( 1.0 - depth ), opacity );
+
+			#endif
 
 		}`
 

+ 2 - 2
examples/jsm/transpiler/GLSLDecoder.js

@@ -11,7 +11,7 @@ const arithmeticOperators = [
 ];
 
 const precedenceOperators = [
-	'*', '/', '%',
+	'/', '*', '%',
 	'-', '+',
 	'<<', '>>',
 	'<', '>', '<=', '>=',
@@ -861,7 +861,7 @@ class GLSLDecoder {
 
 		const switchDeterminantTokens = this.readTokensUntil( ')' );
 
-		// Parse expresison between parentheses. Index 1: char after '('. Index -1: char before ')'
+		// Parse expression between parentheses. Index 1: char after '('. Index -1: char before ')'
 		const discriminant = this.parseExpressionFromTokens( switchDeterminantTokens.slice( 1, - 1 ) );
 
 		// Validate curly braces

+ 8 - 7
examples/jsm/tsl/display/BloomNode.js

@@ -364,7 +364,9 @@ class BloomNode extends TempNode {
 
 		// gaussian blur materials
 
-		const kernelSizeArray = [ 3, 5, 7, 9, 11 ];
+		// These sizes have been changed to account for the altered coefficents-calculation to avoid blockiness,
+		// while retaining the same blur-strength. For details see https://github.com/mrdoob/three.js/pull/31528
+		const kernelSizeArray = [ 6, 10, 14, 18, 22 ]; 
 
 		for ( let i = 0; i < this._nMips; i ++ ) {
 
@@ -449,10 +451,11 @@ class BloomNode extends TempNode {
 	_getSeparableBlurMaterial( builder, kernelRadius ) {
 
 		const coefficients = [];
+		const sigma = kernelRadius / 3;
 
 		for ( let i = 0; i < kernelRadius; i ++ ) {
 
-			coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
+			coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( sigma * sigma ) ) / sigma );
 
 		}
 
@@ -468,8 +471,7 @@ class BloomNode extends TempNode {
 
 		const separableBlurPass = Fn( () => {
 
-			const weightSum = gaussianCoefficients.element( 0 ).toVar();
-			const diffuseSum = sampleTexel( uvNode ).rgb.mul( weightSum ).toVar();
+			const diffuseSum = sampleTexel( uvNode ).rgb.mul( gaussianCoefficients.element( 0 ) ).toVar();
 
 			Loop( { start: int( 1 ), end: int( kernelRadius ), type: 'int', condition: '<' }, ( { i } ) => {
 
@@ -478,12 +480,11 @@ class BloomNode extends TempNode {
 				const uvOffset = direction.mul( invSize ).mul( x );
 				const sample1 = sampleTexel( uvNode.add( uvOffset ) ).rgb;
 				const sample2 = sampleTexel( uvNode.sub( uvOffset ) ).rgb;
-				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );
-				weightSum.addAssign( float( 2.0 ).mul( w ) );
+				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );		
 
 			} );
 
-			return vec4( diffuseSum.div( weightSum ), 1.0 );
+			return vec4( diffuseSum, 1.0 );
 
 		} );
 

+ 6 - 8
examples/jsm/tsl/display/GaussianBlurNode.js

@@ -1,5 +1,5 @@
 import { RenderTarget, Vector2, NodeMaterial, RendererUtils, QuadMesh, TempNode, NodeUpdateType } from 'three/webgpu';
-import { nodeObject, Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, mul, premultiplyAlpha, unpremultiplyAlpha } from 'three/tsl';
+import { nodeObject, Fn, float, uv, uniform, convertToTexture, vec2, vec4, passTexture, premultiplyAlpha, unpremultiplyAlpha } from 'three/tsl';
 
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 
@@ -26,7 +26,7 @@ class GaussianBlurNode extends TempNode {
 	 * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
 	 * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
 	 */
-	constructor( textureNode, directionNode = null, sigma = 2 ) {
+	constructor( textureNode, directionNode = null, sigma = 4 ) {
 
 		super( 'vec4' );
 
@@ -267,8 +267,7 @@ class GaussianBlurNode extends TempNode {
 			const invSize = this._invSize;
 			const direction = directionNode.mul( this._passDirection );
 
-			const weightSum = float( gaussianCoefficients[ 0 ] ).toVar();
-			const diffuseSum = vec4( sampleTexture( uvNode ).mul( weightSum ) ).toVar();
+			const diffuseSum = vec4( sampleTexture( uvNode ).mul( gaussianCoefficients[ 0 ] ) ).toVar();
 
 			for ( let i = 1; i < kernelSize; i ++ ) {
 
@@ -281,11 +280,9 @@ class GaussianBlurNode extends TempNode {
 				const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
 
 				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
-				weightSum.addAssign( mul( 2.0, w ) );
-
 			}
 
-			return output( diffuseSum.div( weightSum ) );
+			return output( diffuseSum );
 
 		} );
 
@@ -328,10 +325,11 @@ class GaussianBlurNode extends TempNode {
 	_getCoefficients( kernelRadius ) {
 
 		const coefficients = [];
+		const sigma = kernelRadius / 3;
 
 		for ( let i = 0; i < kernelRadius; i ++ ) {
 
-			coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( kernelRadius * kernelRadius ) ) / kernelRadius );
+			coefficients.push( 0.39894 * Math.exp( - 0.5 * i * i / ( sigma * sigma ) ) / sigma );
 
 		}
 

+ 181 - 172
examples/jsm/tsl/display/TRAAPassNode.js → examples/jsm/tsl/display/TRAANode.js

@@ -1,5 +1,5 @@
-import { Color, Vector2, NearestFilter, Matrix4, RendererUtils, PassNode, QuadMesh, NodeMaterial } from 'three/webgpu';
-import { add, float, If, Loop, int, Fn, min, max, clamp, nodeObject, texture, uniform, uv, vec2, vec4, luminance } from 'three/tsl';
+import { HalfFloatType, Vector2, RenderTarget, RendererUtils, QuadMesh, NodeMaterial, TempNode, NodeUpdateType, Matrix4 } from 'three/webgpu';
+import { add, float, If, Loop, int, Fn, min, max, clamp, nodeObject, texture, uniform, uv, vec2, vec4, luminance, convertToTexture, passTexture, velocity } from 'three/tsl';
 
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 const _size = /*@__PURE__*/ new Vector2();
@@ -8,34 +8,34 @@ let _rendererState;
 
 
 /**
- * A special render pass node that renders the scene with TRAA (Temporal Reprojection Anti-Aliasing).
- *
- * Note: The current implementation does not yet support MRT setups.
+ * A special node that applies TRAA (Temporal Reprojection Anti-Aliasing).
  *
  * References:
  * - {@link https://alextardif.com/TAA.html}
  * - {@link https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail/}
  *
- * @augments PassNode
- * @three_import import { traaPass } from 'three/addons/tsl/display/TRAAPassNode.js';
+ * @augments TempNode
+ * @three_import import { traa } from 'three/addons/tsl/display/TRAANode.js';
  */
-class TRAAPassNode extends PassNode {
+class TRAANode extends TempNode {
 
 	static get type() {
 
-		return 'TRAAPassNode';
+		return 'TRAANode';
 
 	}
 
 	/**
-	 * Constructs a new TRAA pass node.
+	 * Constructs a new TRAA node.
 	 *
-	 * @param {Scene} scene - The scene to render.
-	 * @param {Camera} camera - The camera to render the scene with.
+	 * @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
+	 * @param {TextureNode} depthNode - A node that represents the scene's depth.
+	 * @param {TextureNode} velocityNode - A node that represents the scene's velocity.
+	 * @param {Camera} camera - The camera the scene is rendered with.
 	 */
-	constructor( scene, camera ) {
+	constructor( beautyNode, depthNode, velocityNode, camera ) {
 
-		super( PassNode.COLOR, scene, camera );
+		super( 'vec4' );
 
 		/**
 		 * This flag can be used for type testing.
@@ -44,40 +44,53 @@ class TRAAPassNode extends PassNode {
 		 * @readonly
 		 * @default true
 		 */
-		this.isTRAAPassNode = true;
+		this.isTRAANode = true;
 
 		/**
-		 * The clear color of the pass.
+		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
+		 * its effect once per frame in `updateBefore()`.
 		 *
-		 * @type {Color}
-		 * @default 0x000000
+		 * @type {string}
+		 * @default 'frame'
 		 */
-		this.clearColor = new Color( 0x000000 );
+		this.updateBeforeType = NodeUpdateType.FRAME;
 
 		/**
-		 * The clear alpha of the pass.
+		 * The texture node that represents the input of the effect.
 		 *
-		 * @type {number}
-		 * @default 0
+		 * @type {TextureNode}
 		 */
-		this.clearAlpha = 0;
+		this.beautyNode = beautyNode;
 
 		/**
-		 * The jitter index selects the current camera offset value.
+		 * A node that represents the scene's velocity.
 		 *
-		 * @private
-		 * @type {number}
-		 * @default 0
+		 * @type {TextureNode}
 		 */
-		this._jitterIndex = 0;
+		this.depthNode = depthNode;
 
 		/**
-		 * Used to save the original/unjittered projection matrix.
+		 * A node that represents the scene's velocity.
+		 *
+		 * @type {TextureNode}
+		 */
+		this.velocityNode = velocityNode;
+
+		/**
+		 *  The camera the scene is rendered with.
+		 *
+		 * @type {TextureNode}
+		 */
+		this.camera = camera;
+
+		/**
+		 * The jitter index selects the current camera offset value.
 		 *
 		 * @private
-		 * @type {Matrix4}
+		 * @type {number}
+		 * @default 0
 		 */
-		this._originalProjectionMatrix = new Matrix4();
+		this._jitterIndex = 0;
 
 		/**
 		 * A uniform node holding the inverse resolution value.
@@ -88,22 +101,22 @@ class TRAAPassNode extends PassNode {
 		this._invSize = uniform( new Vector2() );
 
 		/**
-		 * The render target that holds the current sample.
+		 * The render target that represents the history of frame data.
 		 *
 		 * @private
 		 * @type {?RenderTarget}
-		 * @default null
 		 */
-		this._sampleRenderTarget = null;
+		this._historyRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._historyRenderTarget.texture.name = 'TRAANode.history';
 
 		/**
-		 * The render target that represents the history of frame data.
+		 * The render target for the resolve.
 		 *
 		 * @private
 		 * @type {?RenderTarget}
-		 * @default null
 		 */
-		this._historyRenderTarget = null;
+		this._resolveRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._resolveRenderTarget.texture.name = 'TRAANode.resolve';
 
 		/**
 		 * Material used for the resolve step.
@@ -112,7 +125,41 @@ class TRAAPassNode extends PassNode {
 		 * @type {NodeMaterial}
 		 */
 		this._resolveMaterial = new NodeMaterial();
-		this._resolveMaterial.name = 'TRAA.Resolve';
+		this._resolveMaterial.name = 'TRAA.resolve';
+
+		/**
+		 * The result of the effect is represented as a separate texture node.
+		 *
+		 * @private
+		 * @type {PassTextureNode}
+		 */
+		this._textureNode = passTexture( this, this._resolveRenderTarget.texture );
+
+		/**
+		 * Used to save the original/unjittered projection matrix.
+		 *
+		 * @private
+		 * @type {Matrix4}
+		 */
+		this._originalProjectionMatrix = new Matrix4();
+
+		/**
+		 * Sync the post processing stack with the TRAA node.
+		 * @private
+		 * @type {boolean}
+		 */
+		this._needsPostProcessingSync = false;
+
+	}
+
+	/**
+	 * Returns the result of the effect as a texture node.
+	 *
+	 * @return {PassTextureNode} A texture node that represents the result of the effect.
+	 */
+	getTextureNode() {
+
+		return this._textureNode;
 
 	}
 
@@ -121,78 +168,48 @@ class TRAAPassNode extends PassNode {
 	 *
 	 * @param {number} width - The width of the effect.
 	 * @param {number} height - The height of the effect.
-	 * @return {boolean} Whether the TRAA needs a restart or not. That is required after a resize since buffer data with different sizes can't be resolved.
 	 */
 	setSize( width, height ) {
 
-		super.setSize( width, height );
-
-		let needsRestart = false;
-
-		if ( this.renderTarget.width !== this._sampleRenderTarget.width || this.renderTarget.height !== this._sampleRenderTarget.height ) {
-
-			this._sampleRenderTarget.setSize( this.renderTarget.width, this.renderTarget.height );
-			this._historyRenderTarget.setSize( this.renderTarget.width, this.renderTarget.height );
-
-			this._invSize.value.set( 1 / this.renderTarget.width, 1 / this.renderTarget.height );
-
-			needsRestart = true;
-
-		}
+		this._historyRenderTarget.setSize( width, height );
+		this._resolveRenderTarget.setSize( width, height );
 
-		return needsRestart;
+		this._invSize.value.set( 1 / width, 1 / height );
 
 	}
 
 	/**
-	 * This method is used to render the effect once per frame.
+	 * Defines the TRAA's current jitter as a view offset
+	 * to the scene's camera.
 	 *
-	 * @param {NodeFrame} frame - The current node frame.
+	 * @param {number} width - The width of the effect.
+	 * @param {number} height - The height of the effect.
 	 */
-	updateBefore( frame ) {
-
-		const { renderer } = frame;
-		const { scene, camera } = this;
-
-		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
-
-		//
-
-		this._pixelRatio = renderer.getPixelRatio();
-		const size = renderer.getSize( _size );
-
-		const needsRestart = this.setSize( size.width, size.height );
+	setViewOffset( width, height ) {
 
 		// save original/unjittered projection matrix for velocity pass
 
-		camera.updateProjectionMatrix();
-		this._originalProjectionMatrix.copy( camera.projectionMatrix );
+		this.camera.updateProjectionMatrix();
+		this._originalProjectionMatrix.copy( this.camera.projectionMatrix );
 
-		// camera configuration
+		velocity.setProjectionMatrix( this._originalProjectionMatrix );
 
-		this._cameraNear.value = camera.near;
-		this._cameraFar.value = camera.far;
-
-		// configure jitter as view offset
+		//
 
 		const viewOffset = {
 
-			fullWidth: this.renderTarget.width,
-			fullHeight: this.renderTarget.height,
+			fullWidth: width,
+			fullHeight: height,
 			offsetX: 0,
 			offsetY: 0,
-			width: this.renderTarget.width,
-			height: this.renderTarget.height
+			width: width,
+			height: height
 
 		};
 
-		const originalViewOffset = Object.assign( {}, camera.view );
-
-		if ( originalViewOffset.enabled ) Object.assign( viewOffset, originalViewOffset );
-
 		const jitterOffset = _JitterVectors[ this._jitterIndex ];
 
-		camera.setViewOffset(
+		this.camera.setViewOffset(
 
 			viewOffset.fullWidth, viewOffset.fullHeight,
 
@@ -202,95 +219,88 @@ class TRAAPassNode extends PassNode {
 
 		);
 
-		// configure velocity
-
-		const mrt = this.getMRT();
-		const velocityOutput = mrt.get( 'velocity' );
-
-		if ( velocityOutput !== undefined ) {
-
-			velocityOutput.setProjectionMatrix( this._originalProjectionMatrix );
-
-		} else {
-
-			throw new Error( 'THREE:TRAAPassNode: Missing velocity output in MRT configuration.' );
-
-		}
-
-		// render sample
+	}
 
-		renderer.setMRT( mrt );
+	/**
+	 * Clears the view offset from the scene's camera.
+	 */
+	clearViewOffset() {
 
-		renderer.setClearColor( this.clearColor, this.clearAlpha );
-		renderer.setRenderTarget( this._sampleRenderTarget );
-		renderer.render( scene, camera );
+		this.camera.clearViewOffset();
 
-		renderer.setRenderTarget( null );
-		renderer.setMRT( null );
+		velocity.setProjectionMatrix( null );
 
-		// every time when the dimensions change we need fresh history data. Copy the sample
-		// into the history and final render target (no AA happens at that point).
+		// update jitter index
 
-		if ( needsRestart === true ) {
+		this._jitterIndex ++;
+		this._jitterIndex = this._jitterIndex % ( _JitterVectors.length - 1 );
 
-			// bind and clear render target to make sure they are initialized after the resize which triggers a dispose()
+	}
 
-			renderer.setRenderTarget( this._historyRenderTarget );
-			renderer.clear();
+	/**
+	 * This method is used to render the effect once per frame.
+	 *
+	 * @param {NodeFrame} frame - The current node frame.
+	 */
+	updateBefore( frame ) {
 
-			renderer.setRenderTarget( this.renderTarget );
-			renderer.clear();
+		const { renderer } = frame;
 
-			renderer.setRenderTarget( null );
+		// keep the TRAA in sync with the dimensions of the beauty node
 
-			renderer.copyTextureToTexture( this._sampleRenderTarget.texture, this._historyRenderTarget.texture );
-			renderer.copyTextureToTexture( this._sampleRenderTarget.texture, this.renderTarget.texture );
+		const beautyRenderTarget = ( this.beautyNode.isRTTNode ) ? this.beautyNode.renderTarget : this.beautyNode.passNode.renderTarget;
 
-		} else {
+		const width = beautyRenderTarget.texture.width;
+		const height = beautyRenderTarget.texture.height;
 
-			// resolve
+		//
 
-			renderer.setRenderTarget( this.renderTarget );
-			_quadMesh.material = this._resolveMaterial;
-			_quadMesh.render( renderer );
-			renderer.setRenderTarget( null );
+		if ( this._needsPostProcessingSync === true ) {
 
-			// update history
+			this.setViewOffset( width, height );
 
-			renderer.copyTextureToTexture( this.renderTarget.texture, this._historyRenderTarget.texture );
+			this._needsPostProcessingSync = false;
 
 		}
 
-		// copy depth
+		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
+
+		//
 
-		renderer.copyTextureToTexture( this._sampleRenderTarget.depthTexture, this.renderTarget.depthTexture );
+		const needsRestart = this._historyRenderTarget.width !== width || this._historyRenderTarget.height !== height;
+		this.setSize( width, height );
 
-		// update jitter index
+		// every time when the dimensions change we need fresh history data
 
-		this._jitterIndex ++;
-		this._jitterIndex = this._jitterIndex % ( _JitterVectors.length - 1 );
+		if ( needsRestart === true ) {
 
-		// restore
+			// bind and clear render target to make sure they are initialized after the resize which triggers a dispose()
 
-		if ( originalViewOffset.enabled ) {
+			renderer.setRenderTarget( this._historyRenderTarget );
+			renderer.clear();
 
-			camera.setViewOffset(
+			renderer.setRenderTarget( this._resolveRenderTarget );
+			renderer.clear();
 
-				originalViewOffset.fullWidth, originalViewOffset.fullHeight,
+			// make sure to reset the history with the contents of the beauty buffer otherwise subsequent frames after the
+			// resize will fade from a darker color to the correct one because the history was cleared with black.
 
-				originalViewOffset.offsetX, originalViewOffset.offsetY,
+			renderer.copyTextureToTexture( beautyRenderTarget.texture, this._historyRenderTarget.texture );
 
-				originalViewOffset.width, originalViewOffset.height
+		}
 
-			);
+		// resolve
 
-		} else {
+		renderer.setRenderTarget( this._resolveRenderTarget );
+		_quadMesh.material = this._resolveMaterial;
+		_quadMesh.render( renderer );
+		renderer.setRenderTarget( null );
 
-			camera.clearViewOffset();
+		// update history
 
-		}
+		renderer.copyTextureToTexture( this._resolveRenderTarget.texture, this._historyRenderTarget.texture );
 
-		velocityOutput.setProjectionMatrix( null );
+		// restore
 
 		RendererUtils.restoreRendererState( renderer, _rendererState );
 
@@ -304,28 +314,31 @@ class TRAAPassNode extends PassNode {
 	 */
 	setup( builder ) {
 
-		if ( this._sampleRenderTarget === null ) {
+		const postProcessing = builder.context.postProcessing;
 
-			this._sampleRenderTarget = this.renderTarget.clone();
-			this._historyRenderTarget = this.renderTarget.clone();
+		if ( postProcessing ) {
 
-			this._sampleRenderTarget.texture.minFiler = NearestFilter;
-			this._sampleRenderTarget.texture.magFilter = NearestFilter;
+			this._needsPostProcessingSync = true;
 
-			const velocityTarget = this._sampleRenderTarget.texture.clone();
-			velocityTarget.isRenderTargetTexture = true;
-			velocityTarget.name = 'velocity';
+			postProcessing.context.onBeforePostProcessing = () => {
 
-			this._sampleRenderTarget.textures.push( velocityTarget ); // for MRT
+				const size = builder.renderer.getDrawingBufferSize( _size );
+				this.setViewOffset( size.width, size.height );
 
-		}
+			};
 
-		// textures
+			postProcessing.context.onAfterPostProcessing = () => {
+
+				this.clearViewOffset();
+
+			};
+
+		}
 
 		const historyTexture = texture( this._historyRenderTarget.texture );
-		const sampleTexture = texture( this._sampleRenderTarget.textures[ 0 ] );
-		const velocityTexture = texture( this._sampleRenderTarget.textures[ 1 ] );
-		const depthTexture = texture( this._sampleRenderTarget.depthTexture );
+		const sampleTexture = this.beautyNode;
+		const depthTexture = this.depthNode;
+		const velocityTexture = this.velocityNode;
 
 		const resolve = Fn( () => {
 
@@ -395,9 +408,9 @@ class TRAAPassNode extends PassNode {
 
 		// materials
 
-		this._resolveMaterial.fragmentNode = resolve();
+		this._resolveMaterial.colorNode = resolve();
 
-		return super.setup( builder );
+		return this._textureNode;
 
 	}
 
@@ -407,14 +420,8 @@ class TRAAPassNode extends PassNode {
 	 */
 	dispose() {
 
-		super.dispose();
-
-		if ( this._sampleRenderTarget !== null ) {
-
-			this._sampleRenderTarget.dispose();
-			this._historyRenderTarget.dispose();
-
-		}
+		this._historyRenderTarget.dispose();
+		this._resolveRenderTarget.dispose();
 
 		this._resolveMaterial.dispose();
 
@@ -422,7 +429,7 @@ class TRAAPassNode extends PassNode {
 
 }
 
-export default TRAAPassNode;
+export default TRAANode;
 
 // These jitter vectors are specified in integers because it is easier.
 // I am assuming a [-8,8) integer grid, but it needs to be mapped onto [-0.5,0.5)
@@ -441,12 +448,14 @@ const _JitterVectors = [
 ];
 
 /**
- * TSL function for creating a TRAA pass node for Temporal Reprojection Anti-Aliasing.
+ * TSL function for creating a TRAA node for Temporal Reprojection Anti-Aliasing.
  *
  * @tsl
  * @function
- * @param {Scene} scene - The scene to render.
- * @param {Camera} camera - The camera to render the scene with.
- * @returns {TRAAPassNode}
+ * @param {TextureNode} beautyNode - The texture node that represents the input of the effect.
+ * @param {TextureNode} depthNode - A node that represents the scene's depth.
+ * @param {TextureNode} velocityNode - A node that represents the scene's velocity.
+ * @param {Camera} camera - The camera the scene is rendered with.
+ * @returns {TRAANode}
  */
-export const traaPass = ( scene, camera ) => nodeObject( new TRAAPassNode( scene, camera ) );
+export const traa = ( beautyNode, depthNode, velocityNode, camera ) => nodeObject( new TRAANode( convertToTexture( beautyNode ), depthNode, velocityNode, camera ) );

+ 1 - 1
examples/jsm/tsl/lighting/TiledLightsNode.js

@@ -322,7 +322,7 @@ class TiledLightsNode extends LightsNode {
 		const lightsTexture = new DataTexture( lightsData, lightsData.length / 8, 2, RGBAFormat, FloatType );
 
 		const lightIndexesArray = new Int32Array( count * 4 * 2 );
-		const lightIndexes = attributeArray( lightIndexesArray, 'ivec4' ).label( 'lightIndexes' );
+		const lightIndexes = attributeArray( lightIndexesArray, 'ivec4' ).setName( 'lightIndexes' );
 
 		// compute
 

+ 38 - 0
examples/materialx/color3_vec3_cm_test.mtlx

@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<materialx version="1.39" colorspace="lin_rec709">
+  <surfacematerial name="mat_color3_vec3_cm_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="normalmap_cm" />
+  </standard_surface>
+  <!-- create nodegraph to test cm conversion on image node before feeding to heighttonormal  -->
+  <nodegraph name="height_to_normal_cm">
+    <image name="b_image" type="color3">
+      <input name="file" type="filename" value="resources/Images/grid.png" colorspace="srgb_texture" />
+    </image>
+    <extract name="extract1" type="float">
+      <input name="in" type="color3" nodename="b_image" />
+      <input name="index" type="integer" value="1" />
+    </extract>
+    <heighttonormal name="impl_heighttonormalmap" type="vector3">
+      <input name="in" type="float" nodename="extract1" />
+      <input name="scale" type="float" value="1.0" />
+    </heighttonormal>
+    <output name="out" type="vector3" nodename="impl_heighttonormalmap" />
+  </nodegraph>
+  <!-- create nodegraph to test cm conversion on image node before feeding to normalmap  -->
+  <nodegraph name="normalmap_cm">
+    <image name="b_image" type="color3">
+      <input name="file" type="filename" value="resources/Images/grid.png" colorspace="srgb_texture" />
+    </image>
+    <convert name="c3tov3" type="vector3">
+      <input name="in" type="color3" nodename="b_image" />
+    </convert>
+    <normalmap name="impl_normalmap" type="vector3">
+      <input name="in" type="vector3" nodename="c3tov3" />
+      <input name="scale" type="float" value="1.5" />
+    </normalmap>
+    <output name="out" type="vector3" nodename="impl_normalmap" />
+  </nodegraph>
+</materialx>

+ 15 - 0
examples/materialx/combined_test.mtlx

@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_combined_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.6, 0.8, 0.4" />
+    <input name="opacity" type="float" value="0.7" />
+    <input name="specular" type="float" value="0.9" />
+    <input name="specular_color" type="color3" value="0.8, 1.0, 0.8" />
+    <input name="ior" type="float" value="1.8" />
+    <input name="specular_roughness" type="float" value="0.1" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 35 - 0
examples/materialx/conditional_if_float.mtlx

@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_ifgreater_blue" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="ifgreater_blue" />
+  </standard_surface>
+  <nodegraph name="ifgreater_blue">
+    <image name="input_image" type="float">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="float" value="0.0" />
+    </image>
+    <!-- Output a gradient of blue based on the image value for debugging -->
+    <combine3 name="blue_gradient" type="color3">
+      <input name="in1" type="float" value="0.0" />
+      <input name="in2" type="float" value="0.0" />
+      <input name="in3" type="float" nodename="input_image" />
+    </combine3>
+    <!-- Use ifgreater to output bright blue above threshold, dark blue below -->
+    <ifgreater name="ifgreater1" type="color3">
+      <input name="value1" type="float" nodename="input_image" />
+      <input name="value2" type="float" value="0.5" />
+      <input name="in1" type="color3" value="0.2, 0.2, 1.0" /> <!-- bright blue -->
+      <input name="in2" type="color3" value="0.0, 0.0, 0.2" /> <!-- dark blue -->
+    </ifgreater>
+    <!-- Mix between the blue_gradient and the thresholded blue for debug visualization -->
+    <mix name="debug_mix" type="color3">
+      <input name="bg" type="color3" nodename="blue_gradient" />
+      <input name="fg" type="color3" nodename="ifgreater1" />
+      <input name="mix" type="float" value="0.5" />
+    </mix>
+    <output name="out" type="color3" nodename="debug_mix" />
+  </nodegraph>
+</materialx>

+ 23 - 0
examples/materialx/heightnormal.mtlx

@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <nodegraph name="height_to_normal_example">
+    <image name="height_image" type="float">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="float" value="0.5" />
+    </image>
+    <heighttonormal name="height_to_normal" type="vector3">
+      <input name="in" type="float" nodename="height_image" />
+      <input name="scale" type="float" value="1.0" />
+    </heighttonormal>
+    <convert name="normal_to_color" type="color3">
+      <input name="in" type="vector3" nodename="height_to_normal" />
+    </convert>
+    <output name="out" type="color3" nodename="normal_to_color" />
+  </nodegraph>
+  <surfacematerial name="mat_heightnormal" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="height_to_normal_example" />
+  </standard_surface>
+</materialx> 

+ 21 - 0
examples/materialx/heighttonormal_normal_input.mtlx

@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_heighttonormal_normal" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.8, 0.8, 0.8" />
+    <input name="normal" type="vector3" output="out" nodegraph="height_to_normal_normal" />
+  </standard_surface>
+  <nodegraph name="height_to_normal_normal">
+    <image name="height_image" type="float">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="float" value="0.5" />
+    </image>
+    <heighttonormal name="height_to_normal" type="vector3">
+      <input name="in" type="float" nodename="height_image" />
+      <input name="scale" type="float" value="2.0" />
+    </heighttonormal>
+    <output name="out" type="vector3" nodename="height_to_normal" />
+  </nodegraph>
+</materialx> 

+ 26 - 0
examples/materialx/image_transform.mtlx

@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_image_transform" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="test_place2d_SRT" />
+  </standard_surface>
+  <nodegraph name="test_place2d_SRT">
+    <texcoord name="texcoord1" type="vector2" />
+    <place2d name="a_place2d" type="vector2">
+      <input name="texcoord" type="vector2" nodename="texcoord1" />
+      <input name="offset" type="vector2" value="0.0, 0.0" />
+      <input name="rotate" type="float" value="30.0" unittype="angle" unit="degree" />
+      <input name="scale" type="vector2" value="2.0, 1.0" />
+      <input name="pivot" type="vector2" value="0.5, 0.5" />
+      <input name="operationorder" type="integer" value="0" />
+    </place2d>
+    <image name="image_number_1" type="color3">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="color3" value="1.0, 0.0, 0.0" />
+      <input name="texcoord" type="vector2" nodename="a_place2d" />
+    </image>
+    <output name="out" type="color3" nodename="image_number_1" />
+  </nodegraph>
+</materialx>

+ 12 - 0
examples/materialx/ior_test.mtlx

@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_ior_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.9, 0.9, 0.9" />
+    <input name="ior" type="float" value="2.4" />
+    <input name="specular_roughness" type="float" value="0.0" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 14 - 0
examples/materialx/opacity_only_test.mtlx

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_opacity_only_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.2, 0.8, 0.2" />
+    <input name="opacity" type="float" value="0.5" />
+    <input name="specular_roughness" type="float" value="0.2" />
+    <input name="metalness" type="float" value="0.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular" type="float" value="1.0" />
+  </standard_surface>
+</materialx> 

+ 12 - 0
examples/materialx/opacity_test.mtlx

@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_opacity_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.8, 0.2, 0.2" />
+    <input name="opacity" type="float" value="0.5" />
+    <input name="specular_roughness" type="float" value="0.1" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

BIN
examples/materialx/resources/Images/grid.png


+ 23 - 0
examples/materialx/rotate2d_test.mtlx

@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_rotate2d_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="rotate2d_test" />
+  </standard_surface>
+  <nodegraph name="rotate2d_test">
+    <texcoord name="texcoord1" type="vector2" />
+    <rotate2d name="rotate2d_1" type="vector2">
+      <input name="in" type="vector2" nodename="texcoord1" />
+      <input name="amount" type="float" value="45.0" unittype="angle" unit="degree" />
+      <input name="pivot" type="vector2" value="0.5, 0.5" />
+    </rotate2d>
+    <image name="rotated_image" type="color3">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="color3" value="0.5, 0.5, 0.5" />
+      <input name="texcoord" type="vector2" nodename="rotate2d_1" />
+    </image>
+    <output name="out" type="color3" nodename="rotated_image" />
+  </nodegraph>
+</materialx> 

+ 44 - 0
examples/materialx/rotate3d_test.mtlx

@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_rotate2d_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" output="out" nodegraph="rotate2d_test" />
+  </standard_surface>
+  <nodegraph name="rotate2d_test">
+    <texcoord name="texcoord1" type="vector2" />
+    <separate2 name="separate_texcoord" type="vector2">
+      <input name="in" type="vector2" nodename="texcoord1" />
+    </separate2>
+    <combine3 name="texcoord_3d" type="vector3">
+      <input name="in1" type="float" nodename="separate_texcoord" output="x" />
+      <input name="in2" type="float" nodename="separate_texcoord" output="y" />
+      <input name="in3" type="float" value="0.0" />
+    </combine3>
+    <time name="time1" type="float" />
+    <multiply name="multiply1" type="float">
+      <input name="in1" type="float" nodename="time1" />
+      <input name="in2" type="float" value="10.0" />
+    </multiply>
+    <rotate3d name="rotate3d_1" type="vector3">
+      <input name="in" type="vector3" nodename="texcoord_3d" />
+      <input name="amount" type="float" nodename="multiply1" />
+      <input name="axis" type="vector3" value="0.0, 0.0, 1.0" />
+    </rotate3d>
+    <separate3 name="separate_rotated" type="vector3">
+      <input name="in" type="vector3" nodename="rotate3d_1" />
+    </separate3>
+    <combine3 name="rotated_texcoord" type="vector3">
+      <input name="in1" type="float" nodename="separate_rotated" output="x" />
+      <input name="in2" type="float" nodename="separate_rotated" output="y" />
+      <input name="in3" type="float" nodename="separate_rotated" output="z" />
+    </combine3>
+    <image name="rotated_image" type="color3">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="color3" value="0.5, 0.5, 0.5" />
+      <input name="texcoord" type="vector2" nodename="rotated_texcoord" />
+    </image>
+    <output name="out" type="color3" nodename="rotated_image" />
+  </nodegraph>
+</materialx> 

+ 17 - 0
examples/materialx/roughness_test.mtlx

@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_roughness_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.8, 0.8, 0.8" />
+    <input name="roughness" type="float" output="out" nodegraph="roughness_map" />
+  </standard_surface>
+  <nodegraph name="roughness_map">
+    <image name="roughness_image" type="float">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="float" value="0.5" />
+    </image>
+    <output name="out" type="float" nodename="roughness_image" />
+  </nodegraph>
+</materialx> 

+ 14 - 0
examples/materialx/sheen_test.mtlx

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_sheen_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.2, 0.4, 0.8" />
+    <input name="sheen" type="float" value="0.8" />
+    <input name="sheen_color" type="color3" value="1.0, 0.9, 0.7" />
+    <input name="sheen_roughness" type="float" value="0.3" />
+    <input name="specular_roughness" type="float" value="0.8" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 13 - 0
examples/materialx/specular_test.mtlx

@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_specular_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.2, 0.2, 0.8" />
+    <input name="specular" type="float" value="0.8" />
+    <input name="specular_color" type="color3" value="1.0, 0.8, 0.6" />
+    <input name="specular_roughness" type="float" value="0.05" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 19 - 0
examples/materialx/texture_opacity_test.mtlx

@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_texture_opacity_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.2, 0.6, 0.8" />
+    <input name="opacity" type="float" output="out" nodegraph="opacity_texture_graph" />
+    <input name="specular_roughness" type="float" value="0.2" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+  <nodegraph name="opacity_texture_graph">
+    <image name="opacity_texture" type="float">
+      <input name="file" type="filename" value="resources/Images/grid.png" />
+      <input name="default" type="float" value="1.0" />
+    </image>
+    <output name="out" type="float" nodename="opacity_texture" />
+  </nodegraph>
+</materialx> 

+ 15 - 0
examples/materialx/thin_film_ior_clamp_test.mtlx

@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_thin_film_ior_clamp_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.1, 0.1, 0.1" />
+    <input name="thin_film_thickness" type="float" value="200.0" />
+    <input name="thin_film_ior" type="float" value="3.5" />
+    <input name="specular_roughness" type="float" value="0.1" />
+    <input name="metalness" type="float" value="0.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular" type="float" value="1.0" />
+  </standard_surface>
+</materialx> 

+ 15 - 0
examples/materialx/thin_film_rainbow_test.mtlx

@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_thin_film_rainbow_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.05, 0.05, 0.05" />
+    <input name="thin_film_thickness" type="float" value="100.0" />
+    <input name="thin_film_ior" type="float" value="2.0" />
+    <input name="specular_roughness" type="float" value="0.0" />
+    <input name="metalness" type="float" value="0.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular" type="float" value="1.0" />
+  </standard_surface>
+</materialx> 

+ 14 - 0
examples/materialx/transmission_only_test.mtlx

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_transmission_only_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="1.0, 1.0, 1.0" />
+    <input name="transmission" type="float" value="1.0" />
+    <input name="transmission_color" type="color3" value="1.0, 1.0, 1.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular_roughness" type="float" value="0.05" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 14 - 0
examples/materialx/transmission_rough.mtlx

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_transmission_rough" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.9, 0.95, 1.0" />
+    <input name="transmission" type="float" value="0.95" />
+    <input name="transmission_color" type="color3" value="0.95, 0.98, 1.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular_roughness" type="float" value="0.3" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 14 - 0
examples/materialx/transmission_test.mtlx

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<materialx version="1.39">
+  <surfacematerial name="mat_transmission_test" type="material" nodedef="ND_surfacematerial">
+    <input name="surfaceshader" type="surfaceshader" nodename="surface_shader1" />
+  </surfacematerial>
+  <standard_surface name="surface_shader1" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
+    <input name="base_color" type="color3" value="0.9, 0.95, 1.0" />
+    <input name="transmission" type="float" value="0.9" />
+    <input name="transmission_color" type="color3" value="0.95, 0.98, 1.0" />
+    <input name="ior" type="float" value="1.5" />
+    <input name="specular_roughness" type="float" value="0.1" />
+    <input name="metalness" type="float" value="0.0" />
+  </standard_surface>
+</materialx> 

+ 2 - 2
examples/misc_raycaster_helper.html

@@ -25,7 +25,7 @@
 
 		<script type="module">
 
-			import * as THREE from 'three';
+			import * as THREE from 'three/webgpu';
 			import { RaycasterHelper } from 'https://cdn.jsdelivr.net/npm/@gsimone/three-raycaster-helper@0.1.0/dist/gsimone-three-raycaster-helper.esm.js';
 
 			let scene, renderer;
@@ -110,4 +110,4 @@
 		</script>
 
 	</body>
-</html>
+</html>

BIN
examples/models/fbx/vCube.fbx


BIN
examples/models/gltf/bath_day.glb


+ 1 - 1
examples/physics_jolt_instancing.html

@@ -25,7 +25,7 @@
 
 		<script type="module">
 
-			import * as THREE from 'three';
+			import * as THREE from 'three/webgpu';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { JoltPhysics } from 'three/addons/physics/JoltPhysics.js';
 			import Stats from 'three/addons/libs/stats.module.js';

+ 8 - 1
examples/physics_rapier_basic.html

@@ -32,6 +32,7 @@
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js';
 			import { RapierHelper } from 'three/addons/helpers/RapierHelper.js';
+			import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
 			import Stats from 'three/addons/libs/stats.module.js';
 
 			let camera, scene, renderer, stats, controls;
@@ -131,9 +132,15 @@
 
 			}
 
+			const geometries = [
+				new THREE.BoxGeometry( 1, 1, 1 ),
+				new THREE.SphereGeometry( 0.5 ),
+				new RoundedBoxGeometry( 1, 1, 1, 2, 0.25 )
+			];
+
 			function addBody( ) {
 
-				const geometry = ( Math.random() > 0.5 ) ? new THREE.SphereGeometry( 0.5 ) : new THREE.BoxGeometry( 1, 1, 1 );
+				const geometry = geometries[ Math.floor( Math.random() * geometries.length ) ];
 				const material = new THREE.MeshStandardMaterial( { color: Math.floor( Math.random() * 0xFFFFFF ) } );
 
 				const mesh = new THREE.Mesh( geometry, material );

BIN
examples/screenshots/webgl_animation_walk.jpg


BIN
examples/screenshots/webgl_shadowmap.jpg


BIN
examples/screenshots/webgpu_compute_birds.jpg


BIN
examples/screenshots/webgpu_compute_texture_3d.jpg


BIN
examples/screenshots/webgpu_loader_materialx.jpg


BIN
examples/screenshots/webgpu_postprocessing.jpg


BIN
examples/screenshots/webgpu_postprocessing_dof_basic.jpg


BIN
examples/screenshots/webgpu_postprocessing_ssr.jpg


BIN
examples/screenshots/webgpu_procedural_texture.jpg


BIN
examples/screenshots/webgpu_reflection.jpg


BIN
examples/screenshots/webgpu_sprites.jpg


BIN
examples/screenshots/webgpu_volume_lighting.jpg


BIN
examples/screenshots/webxr_ar_camera_access.jpg


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

粤ICP备19079148号