Mr.doob 10 月之前
父節點
當前提交
fe670fdcbf
共有 100 個文件被更改,包括 2838 次插入2123 次删除
  1. 115 23
      build/three.cjs
  2. 111 19
      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. 29 4
      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/loaders/DataTextureLoader.html
  13. 1 1
      docs/api/en/loaders/DataTextureLoader.html
  14. 1 1
      docs/api/en/materials/MeshDistanceMaterial.html
  15. 53 0
      docs/api/en/renderers/webxr/WebXRManager.html
  16. 1 1
      docs/api/it/loaders/DataTextureLoader.html
  17. 1 1
      docs/api/zh/loaders/DataTextureLoader.html
  18. 65 0
      docs/examples/zh/lines/Line2.html
  19. 90 0
      docs/examples/zh/lines/LineGeometry.html
  20. 1 1
      docs/examples/zh/loaders/FontLoader.html
  21. 5 0
      docs/list.json
  22. 2 2
      editor/js/libs/ui.three.js
  23. 1 1
      editor/sw.js
  24. 201 0
      examples/css3d_mixed.html
  25. 11 2
      examples/files.json
  26. 1 1
      examples/jsm/Addons.js
  27. 7 7
      examples/jsm/controls/ArcballControls.js
  28. 6 56
      examples/jsm/controls/DragControls.js
  29. 2 2
      examples/jsm/controls/FirstPersonControls.js
  30. 0 8
      examples/jsm/controls/PointerLockControls.js
  31. 30 22
      examples/jsm/exporters/GLTFExporter.js
  32. 4 2
      examples/jsm/exporters/KTX2Exporter.js
  33. 1 1
      examples/jsm/exporters/PLYExporter.js
  34. 0 0
      examples/jsm/libs/ktx-parse.module.js
  35. 3 3
      examples/jsm/lines/Line2.js
  36. 1 1
      examples/jsm/lines/LineGeometry.js
  37. 2 2
      examples/jsm/lines/LineSegments2.js
  38. 2 2
      examples/jsm/lines/Wireframe.js
  39. 1 1
      examples/jsm/lines/WireframeGeometry2.js
  40. 1 1
      examples/jsm/lines/webgpu/LineSegments2.js
  41. 1 1
      examples/jsm/lines/webgpu/Wireframe.js
  42. 1 1
      examples/jsm/loaders/ColladaLoader.js
  43. 5 5
      examples/jsm/loaders/EXRLoader.js
  44. 9 5
      examples/jsm/loaders/GLTFLoader.js
  45. 5 5
      examples/jsm/loaders/HDRCubeTextureLoader.js
  46. 486 0
      examples/jsm/loaders/HDRLoader.js
  47. 112 32
      examples/jsm/loaders/KTX2Loader.js
  48. 7 473
      examples/jsm/loaders/RGBELoader.js
  49. 0 1148
      examples/jsm/loaders/RGBMLoader.js
  50. 4 4
      examples/jsm/loaders/TTFLoader.js
  51. 1 1
      examples/jsm/loaders/UltraHDRLoader.js
  52. 1 1
      examples/jsm/loaders/lwo/IFFParser.js
  53. 533 0
      examples/jsm/materials/WoodNodeMaterial.js
  54. 19 1
      examples/jsm/math/ColorSpaces.js
  55. 2 2
      examples/jsm/math/ConvexHull.js
  56. 2 2
      examples/jsm/math/Lut.js
  57. 1 1
      examples/jsm/misc/MD2CharacterComplex.js
  58. 1 1
      examples/jsm/misc/ProgressiveLightMap.js
  59. 1 1
      examples/jsm/misc/Volume.js
  60. 1 1
      examples/jsm/postprocessing/OutlinePass.js
  61. 37 8
      examples/jsm/postprocessing/SSRPass.js
  62. 1 1
      examples/jsm/shaders/UnpackDepthRGBAShader.js
  63. 22 19
      examples/jsm/transpiler/GLSLDecoder.js
  64. 2 10
      examples/jsm/transpiler/TSLEncoder.js
  65. 24 0
      examples/jsm/transpiler/WGSLEncoder.js
  66. 27 4
      examples/jsm/tsl/display/AnamorphicNode.js
  67. 3 3
      examples/jsm/tsl/display/BloomNode.js
  68. 2 1
      examples/jsm/tsl/display/ChromaticAberrationNode.js
  69. 439 90
      examples/jsm/tsl/display/DepthOfFieldNode.js
  70. 8 0
      examples/jsm/tsl/display/GTAONode.js
  71. 47 35
      examples/jsm/tsl/display/GaussianBlurNode.js
  72. 2 2
      examples/jsm/tsl/display/OutlineNode.js
  73. 180 65
      examples/jsm/tsl/display/SSRNode.js
  74. 1 1
      examples/jsm/tsl/display/TRAANode.js
  75. 64 0
      examples/jsm/tsl/display/boxBlur.js
  76. 15 18
      examples/jsm/tsl/display/hashBlur.js
  77. 1 1
      examples/jsm/utils/BufferGeometryUtils.js
  78. 12 5
      examples/jsm/utils/ShadowMapViewerGPU.js
  79. 1 1
      examples/jsm/webxr/OculusHandModel.js
  80. 2 2
      examples/misc_controls_arcball.html
  81. 3 3
      examples/misc_exporter_exr.html
  82. 3 3
      examples/misc_exporter_ktx2.html
  83. 二進制
      examples/screenshots/css3d_mixed.jpg
  84. 二進制
      examples/screenshots/webgl_loader_gltf_animation_pointer.jpg
  85. 二進制
      examples/screenshots/webgl_loader_gltf_progressive_lod.jpg
  86. 二進制
      examples/screenshots/webgl_loader_texture_rgbm.jpg
  87. 二進制
      examples/screenshots/webgl_materials_envmaps_fasthdr.jpg
  88. 0 0
      examples/screenshots/webgl_reversed_depth_buffer.jpg
  89. 二進制
      examples/screenshots/webgpu_camera.jpg
  90. 二進制
      examples/screenshots/webgpu_compute_reduce.jpg
  91. 二進制
      examples/screenshots/webgpu_custom_fog_background.jpg
  92. 二進制
      examples/screenshots/webgpu_lights_custom.jpg
  93. 二進制
      examples/screenshots/webgpu_loader_texture_ktx2.jpg
  94. 二進制
      examples/screenshots/webgpu_materials_cubemap_mipmaps.jpg
  95. 二進制
      examples/screenshots/webgpu_materials_texture_manualmipmap.jpg
  96. 二進制
      examples/screenshots/webgpu_multiple_elements.jpg
  97. 二進制
      examples/screenshots/webgpu_particles.jpg
  98. 二進制
      examples/screenshots/webgpu_pmrem_cubemap.jpg
  99. 二進制
      examples/screenshots/webgpu_postprocessing_dof.jpg
  100. 二進制
      examples/screenshots/webgpu_postprocessing_dof_basic.jpg

File diff suppressed because it is too large
+ 115 - 23
build/three.cjs


File diff suppressed because it is too large
+ 111 - 19
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
+ 29 - 4
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/loaders/DataTextureLoader.html

@@ -20,7 +20,7 @@
 		<h2>أمثلة (Examples)</h2>
 		<h2>أمثلة (Examples)</h2>
 		<p>
 		<p>
 		انظر
 		انظر
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/RGBELoader.js RGBELoader]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/HDRLoader.js HDRLoader]
 		لمثال على فئة مشتقة.
 		لمثال على فئة مشتقة.
 		</p>
 		</p>
 		 
 		 

+ 1 - 1
docs/api/en/loaders/DataTextureLoader.html

@@ -20,7 +20,7 @@
 		<h2>Examples</h2>
 		<h2>Examples</h2>
 		<p>
 		<p>
 			See the
 			See the
-			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/RGBELoader.js RGBELoader] 
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/HDRLoader.js HDRLoader] 
 			for an example of a derived class.
 			for an example of a derived class.
 		</p>
 		</p>
 
 

+ 1 - 1
docs/api/en/materials/MeshDistanceMaterial.html

@@ -18,7 +18,7 @@
 			Can also be used to customize the shadow casting of an object by assigning
 			Can also be used to customize the shadow casting of an object by assigning
 			an instance of [name] to [page:Object3D.customDistanceMaterial]. The
 			an instance of [name] to [page:Object3D.customDistanceMaterial]. The
 			following examples demonstrates this approach in order to ensure
 			following examples demonstrates this approach in order to ensure
-			transparent parts of objects do no cast shadows.
+			transparent parts of objects do not cast shadows.
 		</p>
 		</p>
 
 
 		<h2>Examples</h2>
 		<h2>Examples</h2>

+ 53 - 0
docs/api/en/renderers/webxr/WebXRManager.html

@@ -37,6 +37,24 @@
 		</p>
 		</p>
 
 
 		<h2>Methods</h2>
 		<h2>Methods</h2>
+		
+		<h3>[method:XRWebGLLayer getBaseLayer]()</h3>
+		<p>
+			Returns the current base layer. This is an 
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/XRProjectionLayer XRProjectionLayer]
+			when the targeted XR device supports the
+			[link:https://www.w3.org/TR/webxrlayers-1/ WebXR Layers API], or an
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/XRWebGLLayer XRWebGLLayer]
+			if it doesn't.
+		</p>
+
+		<h3>[method:XRWebGLBinding getBinding]()</h3>
+		<p>
+			Returns the current 
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/XRWebGLBinding XRWebGLBinding].<br /><br />
+			
+			This method will create a new `XRWebGLBinding` if the browser is able to and one hasn't been created yet.
+		</p>
 
 
 		<h3>[method:ArrayCamera getCamera]()</h3>
 		<h3>[method:ArrayCamera getCamera]()</h3>
 		<p>
 		<p>
@@ -50,6 +68,18 @@
 			manually from the XR camera's projection matrices.
 			manually from the XR camera's projection matrices.
 		</p>
 		</p>
 
 
+		<h3>[method:Texture getCameraTexture]( [param:XRCamera xrCamera] )</h3>
+		<p>
+			[page:XRCamera xrCamera] — The 
+			[link:https://immersive-web.github.io/raw-camera-access/#xr-camera-section XRCamera]
+			to query.<br /><br />
+
+			Returns an opaque texture from the queried `XRCamera`. 
+			Only available during the current animation loop.<br /><br />
+
+			See the [example:webxr_ar_camera_access webxr / ar / camera_access] example.
+		</p>
+
 		<h3>[method:Group getController]( [param:Integer index] )</h3>
 		<h3>[method:Group getController]( [param:Integer index] )</h3>
 		<p>
 		<p>
 			[page:Integer index] — The index of the controller. <br /><br />
 			[page:Integer index] — The index of the controller. <br /><br />
@@ -76,6 +106,18 @@
 			different groups in two different coordinate spaces for the same WebXR
 			different groups in two different coordinate spaces for the same WebXR
 			controller.
 			controller.
 		</p>
 		</p>
+		
+		<h3>[method:Mesh getDepthSensingMesh]()</h3>
+		<p>
+			Returns a plane mesh that visualizes the depth sensing texture
+			if one is available, and `null` otherwise.
+		</p>
+
+		<h3>[method:ExternalTexture getDepthTexture]()</h3>
+		<p>
+			Returns a texture representing the computed depth of the
+			user's environment if available, and `null` otherwise.
+		</p>
 
 
 		<h3>[method:Float getFoveation]()</h3>
 		<h3>[method:Float getFoveation]()</h3>
 		<p>
 		<p>
@@ -83,6 +125,12 @@
 			projection layer.
 			projection layer.
 		</p>
 		</p>
 
 
+		<h3>[method:XRFrame getFrame]()</h3>
+		<p>
+			Returns the current [link:https://developer.mozilla.org/en-US/docs/Web/API/XRFrame XRFrame].
+			Will return `null` if used without an ongoing XR session.
+		</p>
+
 		<h3>[method:Group getHand]( [param:Integer index] )</h3>
 		<h3>[method:Group getHand]( [param:Integer index] )</h3>
 		<p>
 		<p>
 			[page:Integer index] — The index of the controller. <br /><br />
 			[page:Integer index] — The index of the controller. <br /><br />
@@ -101,6 +149,11 @@
 			of active WebXR sessions on application level.
 			of active WebXR sessions on application level.
 		</p>
 		</p>
 
 
+		<h3>[method:Boolean hasDepthSensing]()</h3>
+		<p>
+			Returns `true` if depth sensing data has been provided by the XR device.
+		</p>
+
 		<h3>[method:undefined setFoveation]( [param:Float foveation] )</h3>
 		<h3>[method:undefined setFoveation]( [param:Float foveation] )</h3>
 		<p>
 		<p>
 			[page:Float foveation] — The foveation to set.<br /><br />
 			[page:Float foveation] — The foveation to set.<br /><br />

+ 1 - 1
docs/api/it/loaders/DataTextureLoader.html

@@ -19,7 +19,7 @@
 		<h2>Esempi</h2>
 		<h2>Esempi</h2>
 
 
 		<p>
 		<p>
-			Vedi l'[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/RGBELoader.js RGBELoader]
+			Vedi l'[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/HDRLoader.js HDRLoader]
       per un esempio di una classe derivata.
       per un esempio di una classe derivata.
 		</p>
 		</p>
 
 

+ 1 - 1
docs/api/zh/loaders/DataTextureLoader.html

@@ -20,7 +20,7 @@
 		<h2>例子</h2>
 		<h2>例子</h2>
 
 
 		<p>
 		<p>
-			请参考[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/RGBELoader.js RGBELoader]
+			请参考[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/HDRLoader.js HDRLoader]
 			这个子类的例子。
 			这个子类的例子。
 		</p>
 		</p>
 
 

+ 65 - 0
docs/examples/zh/lines/Line2.html

@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Object3D] &rarr; [page:Mesh] &rarr; [page:LineSegments2] &rarr;
+
+		<h1>线段([name])</h1>
+
+		<p class="desc">
+			在顶点之间绘制的多段线。
+		</p>
+
+		<p class="desc">
+			相比 [page:Line],该类提供了更多功能,如任意线宽设置以及支持使用世界单位定义线宽。
+			它继承自 [page:LineSegments2],简化了从点链构建线段的过程。
+		</p>
+
+		<h2>导入</h2>
+
+		<p>
+			[name] 是一个附加组件,必须显式导入。
+			查看[link:#manual/introduction/Installation Installation / Addons]。
+		</p>
+
+		<code>
+			import { Line2 } from 'three/addons/lines/Line2.js';
+		</code>
+
+		<h2>示例</h2>
+
+		<p>
+			[example:webgl_lines_fat WebGL / lines / fat ]<br />
+			[example:webgl_lines_fat_raycasting WebGL / lines / fat / raycasting ]<br />
+			[example:webgpu_lines_fat WebGPU / lines / fat / raycasting ]
+		</p>
+
+		<h2>构造函数</h2>
+
+		<h3>[name]( [param:LineGeometry geometry], [param:LineMaterial material] )</h3>
+		<p>
+			[page:LineGeometry geometry] — (可选)表示每个线段的顶点对。<br />
+			[page:Material material] — (可选)线段的材质。默认为具有随机颜色的 [page:LineMaterial]。
+		</p>
+
+		<h2>属性</h2>
+		<p>共有属性,请参见基类 [page:LineSegments2]。</p>
+
+		<h3>[property:Boolean isLine2]</h3>
+		<p>只读标志,用于检查给定对象是否为 [name] 类型。</p>
+
+		<h2>方法</h2>
+		<p>共有方法,请参见基类 [page:LineSegments2]。</p>
+
+		<h2>源代码</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/lines/Line2.js examples/jsm/lines/Line2.js]
+		</p>
+	</body>
+</html>

+ 90 - 0
docs/examples/zh/lines/LineGeometry.html

@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:BufferGeometry] &rarr; [page:InstancedBufferGeometry] &rarr; [page:LineSegmentsGeometry] &rarr;
+
+		<h1>线段几何体([name])</h1>
+
+		<p class="desc">
+			由顶点链组成的多段线几何体。
+		</p>
+
+		<p class="desc">
+			在 [page:Line2] 中用于描述形状。
+		</p>
+
+		<h2>导入</h2>
+
+		<p>
+			[name] 是一个附加组件,必须显式导入。
+			查看[link:#manual/introduction/Installation Installation / Addons]。
+		</p>
+
+		<code>
+			import { LineGeometry } from 'three/addons/lines/LineGeometry.js';
+		</code>
+
+		<h2>示例</h2>
+
+		<p>
+			[example:webgl_lines_fat WebGL / lines / fat ]<br />
+			[example:webgl_lines_fat_raycasting WebGL / lines / fat / raycasting ]<br />
+			[example:webgpu_lines_fat WebGPU / lines / fat / raycasting ]
+		</p>
+
+		<h2>构造函数</h2>
+
+		<h3>[name]()</h3>
+		<p>
+			创建一个新的几何体。
+			调用 [page:LineGeometry.setPositions setPositions] 来添加线段。
+		</p>
+
+		<h2>属性</h2>
+		<p>共有属性,请参见基类 [page:LineSegmentsGeometry]。</p>
+
+		<h3>[property:Boolean isLineGeometry]</h3>
+		<p>只读标志,用于检查给定对象是否为 [name] 类型。</p>
+
+		<h2>方法</h2>
+		<p>共有方法,请参见基类 [page:LineSegmentsGeometry]。</p>
+
+		<h3>[method:this fromLine]( [param:Line line] )</h3>
+		<p>
+			将 [page:Line] 对象的顶点位置复制到该几何体中。
+			假定源几何体未使用索引。
+		</p>
+
+		<h3>[method:this setColors]( [param:Array array] )</h3>
+		<p>
+			替换每个顶点的颜色。
+			每个三元组描述一个线段顶点:`[r1, g1, b1]`。
+			数组可以是 `Array` 或 `Float32Array` 类型。
+		</p>
+
+		<h3>[method:this setPositions]( [param:Array array] )</h3>
+		<p>
+			用新的顶点位置集替换现有顶点位置。
+			数组可以是 `Array` 或 `Float32Array` 类型。
+			数组长度必须是 3 的倍数。
+		</p>
+
+		<h3>[method:this setFromPoints]( [param:Array points] )</h3>
+		<p>
+			用点数组替换顶点位置。
+			可以是 `Vector3` 或 `Vector2` 数组。
+		</p>
+
+		<h2>源代码</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/lines/LineGeometry.js examples/jsm/lines/LineGeometry.js]
+		</p>
+	</body>
+</html>

+ 1 - 1
docs/examples/zh/loaders/FontLoader.html

@@ -34,7 +34,7 @@
 		<code>
 		<code>
 		const loader = new FontLoader();
 		const loader = new FontLoader();
 		const font = loader.load(
 		const font = loader.load(
-			// 资源URL
+			// 资源URL,需在本地添加静态资源(根目录/public/fonts/helvetiker_bold.typeface.json)
 			'fonts/helvetiker_bold.typeface.json',
 			'fonts/helvetiker_bold.typeface.json',
 
 
 			// onLoad回调
 			// onLoad回调

+ 5 - 0
docs/list.json

@@ -1043,6 +1043,11 @@
 				"LightProbeGenerator": "examples/zh/lights/LightProbeGenerator"
 				"LightProbeGenerator": "examples/zh/lights/LightProbeGenerator"
 			},
 			},
 
 
+			"线段": {
+				"Line2": "examples/zh/lines/Line2",
+				"LineGeometry": "examples/en/lines/LineGeometry"
+			},
+
 			"加载器": {
 			"加载器": {
 				"3DMLoader": "examples/zh/loaders/3DMLoader",
 				"3DMLoader": "examples/zh/loaders/3DMLoader",
 				"DRACOLoader": "examples/zh/loaders/DRACOLoader",
 				"DRACOLoader": "examples/zh/loaders/DRACOLoader",

+ 2 - 2
editor/js/libs/ui.three.js

@@ -67,9 +67,9 @@ class UITexture extends UISpan {
 
 
 					// assuming RGBE/Radiance HDR image format
 					// assuming RGBE/Radiance HDR image format
 
 
-					const { RGBELoader } = await import( 'three/addons/loaders/RGBELoader.js' );
+					const { HDRLoader } = await import( 'three/addons/loaders/HDRLoader.js' );
 
 
-					const loader = new RGBELoader();
+					const loader = new HDRLoader();
 					loader.load( event.target.result, function ( hdrTexture ) {
 					loader.load( event.target.result, function ( hdrTexture ) {
 
 
 						hdrTexture.sourceFile = file.name;
 						hdrTexture.sourceFile = file.name;

+ 1 - 1
editor/sw.js

@@ -59,7 +59,7 @@ const assets = [
 
 
 	'../examples/jsm/loaders/EXRLoader.js',
 	'../examples/jsm/loaders/EXRLoader.js',
 	'../examples/jsm/loaders/KTX2Loader.js',
 	'../examples/jsm/loaders/KTX2Loader.js',
-	'../examples/jsm/loaders/RGBELoader.js',
+	'../examples/jsm/loaders/HDRLoader.js',
 	'../examples/jsm/loaders/TGALoader.js',
 	'../examples/jsm/loaders/TGALoader.js',
 
 
 	'../examples/jsm/curves/NURBSCurve.js',
 	'../examples/jsm/curves/NURBSCurve.js',

+ 201 - 0
examples/css3d_mixed.html

@@ -0,0 +1,201 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>three.js css3d - mixed</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+		<style>
+			body {
+				background-color: #ffffff;
+			}
+		</style>
+	</head>
+	<body>
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.module.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';
+
+			let camera, scene, rendererCSS3D, rendererWebGL;
+			let controls;
+
+			init();
+
+			function init() {
+
+				rendererCSS3D = new CSS3DRenderer();
+				rendererCSS3D.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( rendererCSS3D.domElement );
+
+				rendererWebGL = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
+				rendererWebGL.domElement.style.position = 'absolute';
+				rendererWebGL.domElement.style.top = '0';
+				rendererWebGL.setPixelRatio( window.devicePixelRatio );
+				rendererWebGL.setSize( window.innerWidth, window.innerHeight );
+				rendererWebGL.toneMapping = THREE.NeutralToneMapping;
+				rendererWebGL.setAnimationLoop( animate );
+				document.body.appendChild( rendererWebGL.domElement );
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.set( - 1000, 500, 1500 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xf0f0f0 );
+
+				// Add room
+				const roomGeometry = new THREE.EdgesGeometry( new THREE.BoxGeometry( 4000, 2000, 4000, 10, 5, 10 ) );
+				const roomMaterial = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2, transparent: true } );
+				const room = new THREE.LineSegments( roomGeometry, roomMaterial );
+				scene.add( room );
+
+				// Add light
+				const hemisphereLight = new THREE.HemisphereLight( 0xffffff, 0x444444, 4 );
+				hemisphereLight.position.set( - 25, 100, 50 );
+				scene.add( hemisphereLight );
+
+				// Add cutout mesh
+				const geometry = new THREE.PlaneGeometry( 1024, 768 );
+				const material = new THREE.MeshBasicMaterial( {
+					color: 0xff0000,
+					blending: THREE.NoBlending,
+					opacity: 0,
+					premultipliedAlpha: true
+				} );
+				const mesh = new THREE.Mesh( geometry, material );
+				mesh.name = 'cutout';
+				scene.add( mesh );
+
+				// Add frame
+				const frame = buildFrame( 1024, 768, 50 );
+				scene.add( frame );
+
+				// Add CSS3D element
+				const iframe = document.createElement( 'iframe' );
+				iframe.style.width = '1028px';
+				iframe.style.height = '768px';
+				iframe.style.border = '0px';
+				iframe.src = 'https://threejs.org/examples/#webgl_animation_keyframes';
+				scene.add( new CSS3DObject( iframe ) );
+
+				// Add controls
+				controls = new OrbitControls( camera );
+				controls.connect( rendererWebGL.domElement );
+				controls.enableDamping = true;
+
+				// Track OrbitControls state
+				let isDragging = false;
+				controls.addEventListener( 'start', () => isDragging = true );
+				controls.addEventListener( 'end', () => isDragging = false );
+
+				// raycast to find CSS3DObject
+				const raycaster = new THREE.Raycaster();
+				const pointer = new THREE.Vector2();
+				document.addEventListener( 'pointermove', ( event ) => {
+
+					// Skip raycasting when dragging
+					if ( isDragging ) return;
+
+					pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
+					pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
+
+					raycaster.setFromCamera( pointer, camera );
+
+					const intersects = raycaster.intersectObjects( scene.children, true );
+
+					rendererWebGL.domElement.style.pointerEvents = '';
+
+					if ( intersects.length > 0 ) {
+
+						const object = intersects[ 0 ].object;
+
+						if ( object.name === 'cutout' ) {
+
+							rendererWebGL.domElement.style.pointerEvents = 'none';
+
+						}
+
+					}
+
+				} );
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function buildFrame( width, height, thickness ) {
+
+				const group = new THREE.Group();
+				const material = new THREE.MeshStandardMaterial( { color: 0x2200ff } );
+
+				// Create the frame border
+				const outerShape = new THREE.Shape();
+				outerShape.moveTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );
+				outerShape.lineTo( width / 2 + thickness, - ( height / 2 + thickness ) );
+				outerShape.lineTo( width / 2 + thickness, height / 2 + thickness );
+				outerShape.lineTo( - ( width / 2 + thickness ), height / 2 + thickness );
+				outerShape.lineTo( - ( width / 2 + thickness ), - ( height / 2 + thickness ) );
+
+				// Create inner rectangle (hole)
+				const innerHole = new THREE.Path();
+				innerHole.moveTo( - width / 2, - height / 2 );
+				innerHole.lineTo( width / 2, - height / 2 );
+				innerHole.lineTo( width / 2, height / 2 );
+				innerHole.lineTo( - width / 2, height / 2 );
+				innerHole.lineTo( - width / 2, - height / 2 );
+
+				outerShape.holes.push( innerHole );
+
+				const frameGeometry = new THREE.ExtrudeGeometry( outerShape, {
+					depth: thickness,
+					bevelEnabled: false
+				} );
+
+				const frameMesh = new THREE.Mesh( frameGeometry, material );
+				frameMesh.position.z = - thickness / 2;
+				group.add( frameMesh );
+
+				// Add back plane
+				const backGeometry = new THREE.PlaneGeometry( width + ( thickness * 2 ), height + ( thickness * 2 ) );
+				const backMesh = new THREE.Mesh( backGeometry, material );
+				backMesh.position.set( 0, 0, - thickness / 2 );
+				backMesh.rotation.y = Math.PI;
+				group.add( backMesh );
+
+				return group;
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				rendererWebGL.setSize( window.innerWidth, window.innerHeight );
+				rendererCSS3D.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				controls.update();
+
+				rendererWebGL.render( scene, camera );
+				rendererCSS3D.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 11 - 2
examples/files.json

@@ -82,6 +82,8 @@
 		"webgl_loader_fbx_nurbs",
 		"webgl_loader_fbx_nurbs",
 		"webgl_loader_gcode",
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf",
+		"webgl_loader_gltf_animation_pointer",
+		"webgl_loader_gltf_progressive_lod",
 		"webgl_loader_gltf_avif",
 		"webgl_loader_gltf_avif",
 		"webgl_loader_gltf_compressed",
 		"webgl_loader_gltf_compressed",
 		"webgl_loader_gltf_dispersion",
 		"webgl_loader_gltf_dispersion",
@@ -114,7 +116,6 @@
 		"webgl_loader_texture_ktx2",
 		"webgl_loader_texture_ktx2",
 		"webgl_loader_texture_lottie",
 		"webgl_loader_texture_lottie",
 		"webgl_loader_texture_pvrtc",
 		"webgl_loader_texture_pvrtc",
-		"webgl_loader_texture_rgbm",
 		"webgl_loader_texture_tga",
 		"webgl_loader_texture_tga",
 		"webgl_loader_texture_tiff",
 		"webgl_loader_texture_tiff",
 		"webgl_loader_ttf",
 		"webgl_loader_ttf",
@@ -141,6 +142,7 @@
 		"webgl_materials_envmaps_exr",
 		"webgl_materials_envmaps_exr",
 		"webgl_materials_envmaps_groundprojected",
 		"webgl_materials_envmaps_groundprojected",
 		"webgl_materials_envmaps_hdr",
 		"webgl_materials_envmaps_hdr",
+		"webgl_materials_envmaps_fasthdr",
 		"webgl_materials_matcap",
 		"webgl_materials_matcap",
 		"webgl_materials_normalmap",
 		"webgl_materials_normalmap",
 		"webgl_materials_normalmap_object_space",
 		"webgl_materials_normalmap_object_space",
@@ -273,7 +275,7 @@
 		"webgl_multiple_rendertargets",
 		"webgl_multiple_rendertargets",
 		"webgl_multisampled_renderbuffers",
 		"webgl_multisampled_renderbuffers",
 		"webgl_rendertarget_texture2darray",
 		"webgl_rendertarget_texture2darray",
-		"webgl_reverse_depth_buffer",
+		"webgl_reversed_depth_buffer",
 		"webgl_shadowmap_csm",
 		"webgl_shadowmap_csm",
 		"webgl_shadowmap_pcss",
 		"webgl_shadowmap_pcss",
 		"webgl_shadowmap_progressive",
 		"webgl_shadowmap_progressive",
@@ -313,6 +315,7 @@
 		"webgpu_compute_particles_rain",
 		"webgpu_compute_particles_rain",
 		"webgpu_compute_particles_snow",
 		"webgpu_compute_particles_snow",
 		"webgpu_compute_points",
 		"webgpu_compute_points",
+		"webgpu_compute_reduce",
 		"webgpu_compute_sort_bitonic",
 		"webgpu_compute_sort_bitonic",
 		"webgpu_compute_texture",
 		"webgpu_compute_texture",
 		"webgpu_compute_texture_3d",
 		"webgpu_compute_texture_3d",
@@ -357,16 +360,19 @@
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_materialx",
 		"webgpu_loader_materialx",
+		"webgpu_loader_texture_ktx2",
 		"webgpu_materials",
 		"webgpu_materials",
 		"webgpu_materials_alphahash",
 		"webgpu_materials_alphahash",
 		"webgpu_materials_arrays",
 		"webgpu_materials_arrays",
 		"webgpu_materials_basic",
 		"webgpu_materials_basic",
+		"webgpu_materials_cubemap_mipmaps",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_envmaps_bpcem",
 		"webgpu_materials_envmaps_bpcem",
 		"webgpu_materials_envmaps",
 		"webgpu_materials_envmaps",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_matcap",
 		"webgpu_materials_matcap",
 		"webgpu_materials_sss",
 		"webgpu_materials_sss",
+		"webgpu_materials_texture_manualmipmap",
 		"webgpu_materials_transmission",
 		"webgpu_materials_transmission",
 		"webgpu_materials_toon",
 		"webgpu_materials_toon",
 		"webgpu_materials_video",
 		"webgpu_materials_video",
@@ -377,6 +383,7 @@
 		"webgpu_morphtargets",
 		"webgpu_morphtargets",
 		"webgpu_morphtargets_face",
 		"webgpu_morphtargets_face",
 		"webgpu_mrt",
 		"webgpu_mrt",
+		"webgpu_multiple_elements",
 		"webgpu_mrt_mask",
 		"webgpu_mrt_mask",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets_readback",
 		"webgpu_multiple_rendertargets_readback",
@@ -456,6 +463,7 @@
 		"webgpu_tsl_vfx_flames",
 		"webgpu_tsl_vfx_flames",
 		"webgpu_tsl_vfx_linkedparticles",
 		"webgpu_tsl_vfx_linkedparticles",
 		"webgpu_tsl_vfx_tornado",
 		"webgpu_tsl_vfx_tornado",
+		"webgpu_tsl_wood",
 		"webgpu_video_frame",
 		"webgpu_video_frame",
 		"webgpu_video_panorama",
 		"webgpu_video_panorama",
 		"webgpu_volume_caustics",
 		"webgpu_volume_caustics",
@@ -547,6 +555,7 @@
 		"css2d_label"
 		"css2d_label"
 	],
 	],
 	"css3d": [
 	"css3d": [
+		"css3d_mixed",
 		"css3d_molecules",
 		"css3d_molecules",
 		"css3d_orthographic",
 		"css3d_orthographic",
 		"css3d_periodictable",
 		"css3d_periodictable",

+ 1 - 1
examples/jsm/Addons.js

@@ -89,6 +89,7 @@ export * from './loaders/FBXLoader.js';
 export * from './loaders/FontLoader.js';
 export * from './loaders/FontLoader.js';
 export * from './loaders/GCodeLoader.js';
 export * from './loaders/GCodeLoader.js';
 export * from './loaders/GLTFLoader.js';
 export * from './loaders/GLTFLoader.js';
+export * from './loaders/HDRLoader.js';
 export * from './loaders/HDRCubeTextureLoader.js';
 export * from './loaders/HDRCubeTextureLoader.js';
 export * from './loaders/IESLoader.js';
 export * from './loaders/IESLoader.js';
 export * from './loaders/KMZLoader.js';
 export * from './loaders/KMZLoader.js';
@@ -110,7 +111,6 @@ export * from './loaders/PLYLoader.js';
 export * from './loaders/PVRLoader.js';
 export * from './loaders/PVRLoader.js';
 export * from './loaders/RGBELoader.js';
 export * from './loaders/RGBELoader.js';
 export * from './loaders/UltraHDRLoader.js';
 export * from './loaders/UltraHDRLoader.js';
-export * from './loaders/RGBMLoader.js';
 export * from './loaders/STLLoader.js';
 export * from './loaders/STLLoader.js';
 export * from './loaders/SVGLoader.js';
 export * from './loaders/SVGLoader.js';
 export * from './loaders/TDSLoader.js';
 export * from './loaders/TDSLoader.js';

+ 7 - 7
examples/jsm/controls/ArcballControls.js

@@ -1315,7 +1315,7 @@ class ArcballControls extends Controls {
 	 *
 	 *
 	 * @param {'PAN'|'ROTATE'|'ZOOM'|'FOV'} operation - The operation to be performed ('PAN', 'ROTATE', 'ZOOM', 'FOV').
 	 * @param {'PAN'|'ROTATE'|'ZOOM'|'FOV'} operation - The operation to be performed ('PAN', 'ROTATE', 'ZOOM', 'FOV').
 	 * @param {0|1|2|'WHEEL'} mouse - A mouse button (0, 1, 2) or 'WHEEL' for wheel notches.
 	 * @param {0|1|2|'WHEEL'} mouse - A mouse button (0, 1, 2) or 'WHEEL' for wheel notches.
-	 * @param {'CTRL'|'SHIFT'|null} [key=null] - The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed.
+	 * @param {?('CTRL'|'SHIFT')} [key=null] - The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed.
 	 * @returns {boolean} `true` if the mouse action has been successfully added, `false` otherwise.
 	 * @returns {boolean} `true` if the mouse action has been successfully added, `false` otherwise.
 	 */
 	 */
 	setMouseAction( operation, mouse, key = null ) {
 	setMouseAction( operation, mouse, key = null ) {
@@ -1396,7 +1396,7 @@ class ArcballControls extends Controls {
 	 * Remove a mouse action by specifying its mouse/key combination.
 	 * Remove a mouse action by specifying its mouse/key combination.
 	 *
 	 *
 	 * @param {0|1|2|'WHEEL'} mouse - A mouse button (0, 1, 2) or 'WHEEL' for wheel notches.
 	 * @param {0|1|2|'WHEEL'} mouse - A mouse button (0, 1, 2) or 'WHEEL' for wheel notches.
-	 * @param {'CTRL'|'SHIFT'|null} key - The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed.
+	 * @param {?('CTRL'|'SHIFT')} key - The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed.
 	 * @returns {boolean} `true` if the operation has been successfully removed, `false` otherwise.
 	 * @returns {boolean} `true` if the operation has been successfully removed, `false` otherwise.
 	 */
 	 */
 	unsetMouseAction( mouse, key = null ) {
 	unsetMouseAction( mouse, key = null ) {
@@ -1421,8 +1421,8 @@ class ArcballControls extends Controls {
 	 *
 	 *
 	 * @private
 	 * @private
 	 * @param {0|1|2|'WHEEL'} mouse - Mouse button index (0, 1, 2) or 'WHEEL' for wheel notches.
 	 * @param {0|1|2|'WHEEL'} mouse - Mouse button index (0, 1, 2) or 'WHEEL' for wheel notches.
-	 * @param {'CTRL'|'SHIFT'|null} key - Keyboard modifier.
-	 * @returns {'PAN'|'ROTATE'|'ZOOM'|'FOV'|null} The operation if it has been found, `null` otherwise.
+	 * @param {?('CTRL'|'SHIFT')} key - Keyboard modifier.
+	 * @returns {?('PAN'|'ROTATE'|'ZOOM'|'FOV')} The operation if it has been found, `null` otherwise.
 	 */
 	 */
 	getOpFromAction( mouse, key ) {
 	getOpFromAction( mouse, key ) {
 
 
@@ -1463,7 +1463,7 @@ class ArcballControls extends Controls {
 	 *
 	 *
 	 * @private
 	 * @private
 	 * @param {0|1|2} mouse - Mouse button index (0, 1, 2)
 	 * @param {0|1|2} mouse - Mouse button index (0, 1, 2)
-	 * @param {'CTRL'|'SHIFT'|null} key - Keyboard modifier
+	 * @param {?('CTRL'|'SHIFT')} key - Keyboard modifier
 	 * @returns {?STATE} The FSA state obtained from the operation associated to mouse/keyboard combination.
 	 * @returns {?STATE} The FSA state obtained from the operation associated to mouse/keyboard combination.
 	 */
 	 */
 	getOpStateFromAction( mouse, key ) {
 	getOpStateFromAction( mouse, key ) {
@@ -2509,8 +2509,8 @@ class ArcballControls extends Controls {
 	 * Sets values in transformation object.
 	 * Sets values in transformation object.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @param {Matrix4} [camera=null] - Transformation to be applied to the camera.
-	 * @param {Matrix4} [gizmos=null] - Transformation to be applied to gizmos.
+	 * @param {?Matrix4} [camera=null] - Transformation to be applied to the camera.
+	 * @param {?Matrix4} [gizmos=null] - Transformation to be applied to gizmos.
 	 */
 	 */
 	setTransformationMatrices( camera = null, gizmos = null ) {
 	setTransformationMatrices( camera = null, gizmos = null ) {
 
 

+ 6 - 56
examples/jsm/controls/DragControls.js

@@ -234,56 +234,6 @@ class DragControls extends Controls {
 
 
 	}
 	}
 
 
-	getRaycaster() {
-
-		console.warn( 'THREE.DragControls: getRaycaster() has been deprecated. Use controls.raycaster instead.' ); // @deprecated r169
-
-		return this.raycaster;
-
-	}
-
-	setObjects( objects ) {
-
-		console.warn( 'THREE.DragControls: setObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169
-
-		this.objects = objects;
-
-	}
-
-	getObjects() {
-
-		console.warn( 'THREE.DragControls: getObjects() has been deprecated. Use controls.objects instead.' ); // @deprecated r169
-
-		return this.objects;
-
-	}
-
-	activate() {
-
-		console.warn( 'THREE.DragControls: activate() has been renamed to connect().' ); // @deprecated r169
-		this.connect();
-
-	}
-
-	deactivate() {
-
-		console.warn( 'THREE.DragControls: deactivate() has been renamed to disconnect().' ); // @deprecated r169
-		this.disconnect();
-
-	}
-
-	set mode( value ) {
-
-		console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169
-
-	}
-
-	get mode() {
-
-		console.warn( 'THREE.DragControls: The .mode property has been removed. Define the type of transformation via the .mouseButtons or .touches properties.' ); // @deprecated r169
-
-	}
-
 }
 }
 
 
 function onPointerMove( event ) {
 function onPointerMove( event ) {
@@ -305,6 +255,7 @@ function onPointerMove( event ) {
 			if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 			if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
 
 				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
 				_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
+				this.dispatchEvent( { type: 'drag', object: _selected } );
 
 
 			}
 			}
 
 
@@ -313,11 +264,10 @@ function onPointerMove( event ) {
 			_diff.subVectors( _pointer, _previousPointer ).multiplyScalar( this.rotateSpeed );
 			_diff.subVectors( _pointer, _previousPointer ).multiplyScalar( this.rotateSpeed );
 			_selected.rotateOnWorldAxis( _up, _diff.x );
 			_selected.rotateOnWorldAxis( _up, _diff.x );
 			_selected.rotateOnWorldAxis( _right.normalize(), - _diff.y );
 			_selected.rotateOnWorldAxis( _right.normalize(), - _diff.y );
+			this.dispatchEvent( { type: 'drag', object: _selected } );
 
 
 		}
 		}
 
 
-		this.dispatchEvent( { type: 'drag', object: _selected } );
-
 		_previousPointer.copy( _pointer );
 		_previousPointer.copy( _pointer );
 
 
 	} else {
 	} else {
@@ -414,21 +364,21 @@ function onPointerDown( event ) {
 
 
 				_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
 				_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
 				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
 				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+				domElement.style.cursor = 'move';
+				this.dispatchEvent( { type: 'dragstart', object: _selected } );
 
 
 			} else if ( this.state === STATE.ROTATE ) {
 			} else if ( this.state === STATE.ROTATE ) {
 
 
 				// the controls only support Y+ up
 				// the controls only support Y+ up
 				_up.set( 0, 1, 0 ).applyQuaternion( camera.quaternion ).normalize();
 				_up.set( 0, 1, 0 ).applyQuaternion( camera.quaternion ).normalize();
 				_right.set( 1, 0, 0 ).applyQuaternion( camera.quaternion ).normalize();
 				_right.set( 1, 0, 0 ).applyQuaternion( camera.quaternion ).normalize();
+				domElement.style.cursor = 'move';
+				this.dispatchEvent( { type: 'dragstart', object: _selected } );
 
 
 			}
 			}
 
 
 		}
 		}
 
 
-		domElement.style.cursor = 'move';
-
-		this.dispatchEvent( { type: 'dragstart', object: _selected } );
-
 	}
 	}
 
 
 	_previousPointer.copy( _pointer );
 	_previousPointer.copy( _pointer );

+ 2 - 2
examples/jsm/controls/FirstPersonControls.js

@@ -194,8 +194,8 @@ class FirstPersonControls extends Controls {
 		window.removeEventListener( 'keydown', this._onKeyDown );
 		window.removeEventListener( 'keydown', this._onKeyDown );
 		window.removeEventListener( 'keyup', this._onKeyUp );
 		window.removeEventListener( 'keyup', this._onKeyUp );
 
 
-		this.domElement.removeEventListener( 'pointerdown', this._onPointerMove );
-		this.domElement.removeEventListener( 'pointermove', this._onPointerDown );
+		this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
+		this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
 		this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
 		this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
 		this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
 		this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
 
 

+ 0 - 8
examples/jsm/controls/PointerLockControls.js

@@ -141,14 +141,6 @@ class PointerLockControls extends Controls {
 
 
 	}
 	}
 
 
-	getObject() {
-
-		console.warn( 'THREE.PointerLockControls: getObject() has been deprecated. Use controls.object instead.' ); // @deprecated r169
-
-		return this.object;
-
-	}
-
 	/**
 	/**
 	 * Returns the look direction of the camera.
 	 * Returns the look direction of the camera.
 	 *
 	 *

+ 30 - 22
examples/jsm/exporters/GLTFExporter.js

@@ -543,32 +543,36 @@ function getCanvas() {
 
 
 function getToBlobPromise( canvas, mimeType ) {
 function getToBlobPromise( canvas, mimeType ) {
 
 
-	if ( canvas.toBlob !== undefined ) {
+	if ( typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas ) {
 
 
-		return new Promise( ( resolve ) => canvas.toBlob( resolve, mimeType ) );
+		let quality;
 
 
-	}
+		// Blink's implementation of convertToBlob seems to default to a quality level of 100%
+		// Use the Blink default quality levels of toBlob instead so that file sizes are comparable.
+		if ( mimeType === 'image/jpeg' ) {
 
 
-	let quality;
+			quality = 0.92;
 
 
-	// Blink's implementation of convertToBlob seems to default to a quality level of 100%
-	// Use the Blink default quality levels of toBlob instead so that file sizes are comparable.
-	if ( mimeType === 'image/jpeg' ) {
+		} else if ( mimeType === 'image/webp' ) {
 
 
-		quality = 0.92;
+			quality = 0.8;
 
 
-	} else if ( mimeType === 'image/webp' ) {
+		}
 
 
-		quality = 0.8;
+		return canvas.convertToBlob( {
 
 
-	}
+			type: mimeType,
+			quality: quality
 
 
-	return canvas.convertToBlob( {
+		} );
 
 
-		type: mimeType,
-		quality: quality
+	} else {
 
 
-	} );
+		// HTMLCanvasElement code path
+
+		return new Promise( ( resolve ) => canvas.toBlob( resolve, mimeType ) );
+
+	}
 
 
 }
 }
 
 
@@ -757,7 +761,7 @@ class GLTFWriter {
 	/**
 	/**
 	 * Serializes a userData.
 	 * Serializes a userData.
 	 *
 	 *
-	 * @param {THREE.Object3D|THREE.Material} object
+	 * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
 	 * @param {Object} objectDef
 	 * @param {Object} objectDef
 	 */
 	 */
 	serializeUserData( object, objectDef ) {
 	serializeUserData( object, objectDef ) {
@@ -1555,7 +1559,7 @@ class GLTFWriter {
 	/**
 	/**
 	 * Process material
 	 * Process material
 	 * @param {THREE.Material} material Material to process
 	 * @param {THREE.Material} material Material to process
-	 * @return {Promise<number|null>} Index of the processed material in the "materials" array
+	 * @return {Promise<?number>} Index of the processed material in the "materials" array
 	 */
 	 */
 	async processMaterialAsync( material ) {
 	async processMaterialAsync( material ) {
 
 
@@ -1731,7 +1735,7 @@ class GLTFWriter {
 	/**
 	/**
 	 * Process mesh
 	 * Process mesh
 	 * @param {THREE.Mesh} mesh Mesh to process
 	 * @param {THREE.Mesh} mesh Mesh to process
-	 * @return {Promise<number|null>} Index of the processed mesh in the "meshes" array
+	 * @return {Promise<?number>} Index of the processed mesh in the "meshes" array
 	 */
 	 */
 	async processMeshAsync( mesh ) {
 	async processMeshAsync( mesh ) {
 
 
@@ -2185,7 +2189,7 @@ class GLTFWriter {
 	 *
 	 *
 	 * @param {THREE.AnimationClip} clip
 	 * @param {THREE.AnimationClip} clip
 	 * @param {THREE.Object3D} root
 	 * @param {THREE.Object3D} root
-	 * @return {number|null}
+	 * @return {?number}
 	 */
 	 */
 	processAnimation( clip, root ) {
 	processAnimation( clip, root ) {
 
 
@@ -2279,11 +2283,15 @@ class GLTFWriter {
 
 
 		}
 		}
 
 
-		json.animations.push( {
+		const animationDef = {
 			name: clip.name || 'clip_' + json.animations.length,
 			name: clip.name || 'clip_' + json.animations.length,
 			samplers: samplers,
 			samplers: samplers,
 			channels: channels
 			channels: channels
-		} );
+		};
+
+		this.serializeUserData( clip, animationDef );
+
+		json.animations.push( animationDef );
 
 
 		return json.animations.length - 1;
 		return json.animations.length - 1;
 
 
@@ -2291,7 +2299,7 @@ class GLTFWriter {
 
 
 	/**
 	/**
 	 * @param {THREE.Object3D} object
 	 * @param {THREE.Object3D} object
-	 * @return {number|null}
+	 * @return {?number}
 	 */
 	 */
 	 processSkin( object ) {
 	 processSkin( object ) {
 
 

+ 4 - 2
examples/jsm/exporters/KTX2Exporter.js

@@ -17,8 +17,8 @@ import {
 } from 'three';
 } from 'three';
 
 
 import {
 import {
+	createDefaultContainer,
 	write,
 	write,
-	KTX2Container,
 	KHR_DF_CHANNEL_RGBSDA_ALPHA,
 	KHR_DF_CHANNEL_RGBSDA_ALPHA,
 	KHR_DF_CHANNEL_RGBSDA_BLUE,
 	KHR_DF_CHANNEL_RGBSDA_BLUE,
 	KHR_DF_CHANNEL_RGBSDA_GREEN,
 	KHR_DF_CHANNEL_RGBSDA_GREEN,
@@ -191,7 +191,7 @@ export class KTX2Exporter {
 
 
 		const array = texture.image.data;
 		const array = texture.image.data;
 		const channelCount = getChannelCount( texture );
 		const channelCount = getChannelCount( texture );
-		const container = new KTX2Container();
+		const container = createDefaultContainer();
 
 
 		container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
 		container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
 		container.typeSize = array.BYTES_PER_ELEMENT;
 		container.typeSize = array.BYTES_PER_ELEMENT;
@@ -257,6 +257,8 @@ export class KTX2Exporter {
 
 
 		//
 		//
 
 
+		container.levelCount = 1;
+
 		container.levels = [ {
 		container.levels = [ {
 
 
 			levelData: new Uint8Array( array.buffer, array.byteOffset, array.byteLength ),
 			levelData: new Uint8Array( array.buffer, array.byteOffset, array.byteLength ),

+ 1 - 1
examples/jsm/exporters/PLYExporter.js

@@ -31,7 +31,7 @@ class PLYExporter {
 	 * @param {Object3D} object - The 3D object to export.
 	 * @param {Object3D} object - The 3D object to export.
 	 * @param {PLYExporter~OnDone} onDone - A callback function that is executed when the export has finished.
 	 * @param {PLYExporter~OnDone} onDone - A callback function that is executed when the export has finished.
 	 * @param {PLYExporter~Options} options - The export options.
 	 * @param {PLYExporter~Options} options - The export options.
-	 * @return {?string|ArrayBuffer} The exported PLY.
+	 * @return {?(string|ArrayBuffer)} The exported PLY.
 	 */
 	 */
 	parse( object, onDone, options = {} ) {
 	parse( object, onDone, options = {} ) {
 
 

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


+ 3 - 3
examples/jsm/lines/Line2.js

@@ -1,6 +1,6 @@
-import { LineSegments2 } from '../lines/LineSegments2.js';
-import { LineGeometry } from '../lines/LineGeometry.js';
-import { LineMaterial } from '../lines/LineMaterial.js';
+import { LineSegments2 } from './LineSegments2.js';
+import { LineGeometry } from './LineGeometry.js';
+import { LineMaterial } from './LineMaterial.js';
 
 
 /**
 /**
  * A polyline drawn between vertices.
  * A polyline drawn between vertices.

+ 1 - 1
examples/jsm/lines/LineGeometry.js

@@ -1,4 +1,4 @@
-import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
 
 
 /**
 /**
  * A chain of vertices, forming a polyline.
  * A chain of vertices, forming a polyline.

+ 2 - 2
examples/jsm/lines/LineSegments2.js

@@ -10,8 +10,8 @@ import {
 	Vector3,
 	Vector3,
 	Vector4
 	Vector4
 } from 'three';
 } from 'three';
-import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
-import { LineMaterial } from '../lines/LineMaterial.js';
+import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
+import { LineMaterial } from './LineMaterial.js';
 
 
 const _viewport = new Vector4();
 const _viewport = new Vector4();
 
 

+ 2 - 2
examples/jsm/lines/Wireframe.js

@@ -5,8 +5,8 @@ import {
 	Vector3,
 	Vector3,
 	Vector4
 	Vector4
 } from 'three';
 } from 'three';
-import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
-import { LineMaterial } from '../lines/LineMaterial.js';
+import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
+import { LineMaterial } from './LineMaterial.js';
 
 
 const _start = new Vector3();
 const _start = new Vector3();
 const _end = new Vector3();
 const _end = new Vector3();

+ 1 - 1
examples/jsm/lines/WireframeGeometry2.js

@@ -1,7 +1,7 @@
 import {
 import {
 	WireframeGeometry
 	WireframeGeometry
 } from 'three';
 } from 'three';
-import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
 
 
 /**
 /**
  * A special type of line segments geometry intended for wireframe rendering.
  * A special type of line segments geometry intended for wireframe rendering.

+ 1 - 1
examples/jsm/lines/webgpu/LineSegments2.js

@@ -12,7 +12,7 @@ import {
 	Line2NodeMaterial,
 	Line2NodeMaterial,
 	Vector2
 	Vector2
 } from 'three/webgpu';
 } from 'three/webgpu';
-import { LineSegmentsGeometry } from '../../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from '../LineSegmentsGeometry.js';
 
 
 const _start = new Vector3();
 const _start = new Vector3();
 const _end = new Vector3();
 const _end = new Vector3();

+ 1 - 1
examples/jsm/lines/webgpu/Wireframe.js

@@ -6,7 +6,7 @@ import {
 	Vector3
 	Vector3
 } from 'three/webgpu';
 } from 'three/webgpu';
 
 
-import { LineSegmentsGeometry } from '../../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from '../LineSegmentsGeometry.js';
 
 
 const _start = new Vector3();
 const _start = new Vector3();
 const _end = new Vector3();
 const _end = new Vector3();

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

@@ -113,7 +113,7 @@ class ColladaLoader extends Loader {
 	 *
 	 *
 	 * @param {string} text - The raw Collada data as a string.
 	 * @param {string} text - The raw Collada data as a string.
 	 * @param {string} path - The asset path.
 	 * @param {string} path - The asset path.
-	 * @return {{scene:Group,animations:Array<AnimationClip>,kinematics:Object}} An object representing the parsed asset.
+	 * @return {?{scene:Group,animations:Array<AnimationClip>,kinematics:Object}} An object representing the parsed asset.
 	 */
 	 */
 	parse( text, path ) {
 	parse( text, path ) {
 
 

+ 5 - 5
examples/jsm/loaders/EXRLoader.js

@@ -2515,7 +2515,7 @@ class EXRLoader extends DataTextureLoader {
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.decodeChannels = { R: 0 };
 						EXRDecoder.decodeChannels = { R: 0 };
 
 
-					} else  {
+					} else {
 
 
 						invalidOutput = true;
 						invalidOutput = true;
 
 
@@ -2549,7 +2549,7 @@ class EXRLoader extends DataTextureLoader {
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.decodeChannels = { Y: 0 };
 						EXRDecoder.decodeChannels = { Y: 0 };
 
 
-					} else  {
+					} else {
 
 
 						invalidOutput = true;
 						invalidOutput = true;
 
 
@@ -2563,7 +2563,7 @@ class EXRLoader extends DataTextureLoader {
 
 
 			}
 			}
 
 
-			if (invalidOutput) throw new Error( 'EXRLoader.parse: invalid output format for specified file.' );
+			if ( invalidOutput ) throw new Error( 'EXRLoader.parse: invalid output format for specified file.' );
 
 
 			if ( EXRDecoder.type == 1 ) {
 			if ( EXRDecoder.type == 1 ) {
 
 
@@ -2712,12 +2712,12 @@ class EXRLoader extends DataTextureLoader {
 			if ( this.outputFormat == RGBAFormat ) {
 			if ( this.outputFormat == RGBAFormat ) {
 
 
 				for ( let i = 0; i < byteArray.length; i += 4 )
 				for ( let i = 0; i < byteArray.length; i += 4 )
-					byteArray [i + 2 ] = ( byteArray [ i + 1 ] = byteArray[ i ] );
+					byteArray[ i + 2 ] = ( byteArray[ i + 1 ] = byteArray[ i ] );
 
 
 			} else if ( this.outputFormat == RGFormat ) {
 			} else if ( this.outputFormat == RGFormat ) {
 
 
 				for ( let i = 0; i < byteArray.length; i += 2 )
 				for ( let i = 0; i < byteArray.length; i += 2 )
-					byteArray [ i + 1 ] = byteArray[ i ] ;
+					byteArray[ i + 1 ] = byteArray[ i ];
 
 
 			}
 			}
 
 

+ 9 - 5
examples/jsm/loaders/GLTFLoader.js

@@ -2376,7 +2376,7 @@ function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
 /**
 /**
  *
  *
  * @private
  * @private
- * @param {Object3D|Material|BufferGeometry|Object} object
+ * @param {Object3D|Material|BufferGeometry|Object|AnimationClip} object
  * @param {GLTF.definition} gltfDef
  * @param {GLTF.definition} gltfDef
  */
  */
 function assignExtrasToUserData( object, gltfDef ) {
 function assignExtrasToUserData( object, gltfDef ) {
@@ -2943,7 +2943,7 @@ class GLTFParser {
 	 * @private
 	 * @private
 	 * @param {string} type
 	 * @param {string} type
 	 * @param {number} index
 	 * @param {number} index
-	 * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
+	 * @return {Promise<Object3D|Material|Texture|AnimationClip|ArrayBuffer|Object>}
 	 */
 	 */
 	getDependency( type, index ) {
 	getDependency( type, index ) {
 
 
@@ -3283,7 +3283,7 @@ class GLTFParser {
 	 *
 	 *
 	 * @private
 	 * @private
 	 * @param {number} textureIndex
 	 * @param {number} textureIndex
-	 * @return {Promise<THREE.Texture|null>}
+	 * @return {Promise<?Texture>}
 	 */
 	 */
 	loadTexture( textureIndex ) {
 	loadTexture( textureIndex ) {
 
 
@@ -4020,7 +4020,7 @@ class GLTFParser {
 	 *
 	 *
 	 * @private
 	 * @private
 	 * @param {number} cameraIndex
 	 * @param {number} cameraIndex
-	 * @return {Promise<THREE.Camera>}
+	 * @return {Promise<Camera>|undefined}
 	 */
 	 */
 	loadCamera( cameraIndex ) {
 	loadCamera( cameraIndex ) {
 
 
@@ -4213,7 +4213,11 @@ class GLTFParser {
 
 
 			}
 			}
 
 
-			return new AnimationClip( animationName, undefined, tracks );
+			const animation = new AnimationClip( animationName, undefined, tracks );
+
+			assignExtrasToUserData( animation, animationDef );
+
+			return animation;
 
 
 		} );
 		} );
 
 

+ 5 - 5
examples/jsm/loaders/HDRCubeTextureLoader.js

@@ -8,7 +8,7 @@ import {
 	LinearSRGBColorSpace,
 	LinearSRGBColorSpace,
 	Loader
 	Loader
 } from 'three';
 } from 'three';
-import { RGBELoader } from '../loaders/RGBELoader.js';
+import { HDRLoader } from '../loaders/HDRLoader.js';
 
 
 /**
 /**
  * A loader for loading HDR cube textures.
  * A loader for loading HDR cube textures.
@@ -40,9 +40,9 @@ class HDRCubeTextureLoader extends Loader {
 		 * The internal HDR loader that loads the
 		 * The internal HDR loader that loads the
 		 * individual textures for each cube face.
 		 * individual textures for each cube face.
 		 *
 		 *
-		 * @type {RGBELoader}
+         * @type {HDRLoader}
 		 */
 		 */
-		this.hdrLoader = new RGBELoader();
+		this.hdrLoader = new HDRLoader();
 
 
 		/**
 		/**
 		 * The texture type.
 		 * The texture type.
@@ -147,8 +147,8 @@ class HDRCubeTextureLoader extends Loader {
 	/**
 	/**
 	 * Sets the texture type.
 	 * Sets the texture type.
 	 *
 	 *
-	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
-	 * @return {RGBELoader} A reference to this loader.
+     * @param {(HalfFloatType|FloatType)} value - The texture type to set.
+     * @return {HDRCubeTextureLoader} A reference to this loader.
 	 */
 	 */
 	setDataType( value ) {
 	setDataType( value ) {
 
 

+ 486 - 0
examples/jsm/loaders/HDRLoader.js

@@ -0,0 +1,486 @@
+import {
+	DataTextureLoader,
+	DataUtils,
+	FloatType,
+	HalfFloatType,
+	LinearFilter,
+	LinearSRGBColorSpace
+} from 'three';
+
+/**
+ * A loader for the RGBE HDR texture format.
+ *
+ * ```js
+ * const loader = new HDRLoader();
+ * const envMap = await loader.loadAsync( 'textures/equirectangular/blouberg_sunrise_2_1k.hdr' );
+ * envMap.mapping = THREE.EquirectangularReflectionMapping;
+ *
+ * scene.environment = envMap;
+ * ```
+ *
+ * @augments DataTextureLoader
+ * @three_import import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
+ */
+class HDRLoader extends DataTextureLoader {
+
+	/**
+     * Constructs a new RGBE/HDR loader.
+     *
+     * @param {LoadingManager} [manager] - The loading manager.
+     */
+	constructor( manager ) {
+
+		super( manager );
+
+		/**
+         * The texture type.
+         *
+         * @type {(HalfFloatType|FloatType)}
+         * @default HalfFloatType
+         */
+		this.type = HalfFloatType;
+
+	}
+
+	/**
+     * Parses the given RGBE texture data.
+     *
+     * @param {ArrayBuffer} buffer - The raw texture data.
+     * @return {DataTextureLoader~TexData} An object representing the parsed texture data.
+     */
+	parse( buffer ) {
+
+		// adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
+
+		const
+			/* default error routine.  change this to change error handling */
+			rgbe_read_error = 1,
+			rgbe_write_error = 2,
+			rgbe_format_error = 3,
+			rgbe_memory_error = 4,
+			rgbe_error = function ( rgbe_error_code, msg ) {
+
+				switch ( rgbe_error_code ) {
+
+					case rgbe_read_error: throw new Error( 'THREE.HDRLoader: Read Error: ' + ( msg || '' ) );
+					case rgbe_write_error: throw new Error( 'THREE.HDRLoader: Write Error: ' + ( msg || '' ) );
+					case rgbe_format_error: throw new Error( 'THREE.HDRLoader: Bad File Format: ' + ( msg || '' ) );
+					default:
+					case rgbe_memory_error: throw new Error( 'THREE.HDRLoader: Memory Error: ' + ( msg || '' ) );
+
+				}
+
+			},
+
+			/* offsets to red, green, and blue components in a data (float) pixel */
+			//RGBE_DATA_RED = 0,
+			//RGBE_DATA_GREEN = 1,
+			//RGBE_DATA_BLUE = 2,
+
+			/* number of floats per pixel, use 4 since stored in rgba image format */
+			//RGBE_DATA_SIZE = 4,
+
+			/* flags indicating which fields in an rgbe_header_info are valid */
+			RGBE_VALID_PROGRAMTYPE = 1,
+			RGBE_VALID_FORMAT = 2,
+			RGBE_VALID_DIMENSIONS = 4,
+
+			NEWLINE = '\n',
+
+			fgets = function ( buffer, lineLimit, consume ) {
+
+				const chunkSize = 128;
+
+				lineLimit = ! lineLimit ? 1024 : lineLimit;
+				let p = buffer.pos,
+					i = - 1, len = 0, s = '',
+					chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
+
+				while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) {
+
+					s += chunk; len += chunk.length;
+					p += chunkSize;
+					chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
+
+				}
+
+				if ( - 1 < i ) {
+
+					/*for (i=l-1; i>=0; i--) {
+                        byteCode = m.charCodeAt(i);
+                        if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++;
+                        else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2;
+                        if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate
+                    }*/
+					if ( false !== consume ) buffer.pos += len + i + 1;
+					return s + chunk.slice( 0, i );
+
+				}
+
+				return false;
+
+			},
+
+			/* minimal header reading.  modify if you want to parse more information */
+			RGBE_ReadHeader = function ( buffer ) {
+
+
+				// regexes to parse header info fields
+				const magic_token_re = /^#\?(\S+)/,
+					gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,
+					exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,
+					format_re = /^\s*FORMAT=(\S+)\s*$/,
+					dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,
+
+					// RGBE format header struct
+					header = {
+
+						valid: 0, /* indicate which fields are valid */
+
+						string: '', /* the actual header string */
+
+						comments: '', /* comments found in header */
+
+						programtype: 'RGBE', /* listed at beginning of file to identify it after "#?". defaults to "RGBE" */
+
+						format: '', /* RGBE format, default 32-bit_rle_rgbe */
+
+						gamma: 1.0, /* image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) */
+
+						exposure: 1.0, /* a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0 */
+
+						width: 0, height: 0 /* image dimensions, width/height */
+
+					};
+
+				let line, match;
+
+				if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) {
+
+					rgbe_error( rgbe_read_error, 'no header found' );
+
+				}
+
+				/* if you want to require the magic token then uncomment the next line */
+				if ( ! ( match = line.match( magic_token_re ) ) ) {
+
+					rgbe_error( rgbe_format_error, 'bad initial token' );
+
+				}
+
+				header.valid |= RGBE_VALID_PROGRAMTYPE;
+				header.programtype = match[ 1 ];
+				header.string += line + '\n';
+
+				while ( true ) {
+
+					line = fgets( buffer );
+					if ( false === line ) break;
+					header.string += line + '\n';
+
+					if ( '#' === line.charAt( 0 ) ) {
+
+						header.comments += line + '\n';
+						continue; // comment line
+
+					}
+
+					if ( match = line.match( gamma_re ) ) {
+
+						header.gamma = parseFloat( match[ 1 ] );
+
+					}
+
+					if ( match = line.match( exposure_re ) ) {
+
+						header.exposure = parseFloat( match[ 1 ] );
+
+					}
+
+					if ( match = line.match( format_re ) ) {
+
+						header.valid |= RGBE_VALID_FORMAT;
+						header.format = match[ 1 ];//'32-bit_rle_rgbe';
+
+					}
+
+					if ( match = line.match( dimensions_re ) ) {
+
+						header.valid |= RGBE_VALID_DIMENSIONS;
+						header.height = parseInt( match[ 1 ], 10 );
+						header.width = parseInt( match[ 2 ], 10 );
+
+					}
+
+					if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break;
+
+				}
+
+				if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) {
+
+					rgbe_error( rgbe_format_error, 'missing format specifier' );
+
+				}
+
+				if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) {
+
+					rgbe_error( rgbe_format_error, 'missing image size specifier' );
+
+				}
+
+				return header;
+
+			},
+
+			RGBE_ReadPixels_RLE = function ( buffer, w, h ) {
+
+				const scanline_width = w;
+
+				if (
+				// run length encoding is not allowed so read flat
+					( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) ||
+                    // this file is not run length encoded
+                    ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) )
+				) {
+
+					// return the flat buffer
+					return new Uint8Array( buffer );
+
+				}
+
+				if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) {
+
+					rgbe_error( rgbe_format_error, 'wrong scanline width' );
+
+				}
+
+				const data_rgba = new Uint8Array( 4 * w * h );
+
+				if ( ! data_rgba.length ) {
+
+					rgbe_error( rgbe_memory_error, 'unable to allocate buffer space' );
+
+				}
+
+				let offset = 0, pos = 0;
+
+				const ptr_end = 4 * scanline_width;
+				const rgbeStart = new Uint8Array( 4 );
+				const scanline_buffer = new Uint8Array( ptr_end );
+				let num_scanlines = h;
+
+				// read in each successive scanline
+				while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) {
+
+					if ( pos + 4 > buffer.byteLength ) {
+
+						rgbe_error( rgbe_read_error );
+
+					}
+
+					rgbeStart[ 0 ] = buffer[ pos ++ ];
+					rgbeStart[ 1 ] = buffer[ pos ++ ];
+					rgbeStart[ 2 ] = buffer[ pos ++ ];
+					rgbeStart[ 3 ] = buffer[ pos ++ ];
+
+					if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) {
+
+						rgbe_error( rgbe_format_error, 'bad rgbe scanline format' );
+
+					}
+
+					// read each of the four channels for the scanline into the buffer
+					// first red, then green, then blue, then exponent
+					let ptr = 0, count;
+
+					while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) {
+
+						count = buffer[ pos ++ ];
+						const isEncodedRun = count > 128;
+						if ( isEncodedRun ) count -= 128;
+
+						if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) {
+
+							rgbe_error( rgbe_format_error, 'bad scanline data' );
+
+						}
+
+						if ( isEncodedRun ) {
+
+							// a (encoded) run of the same value
+							const byteValue = buffer[ pos ++ ];
+							for ( let i = 0; i < count; i ++ ) {
+
+								scanline_buffer[ ptr ++ ] = byteValue;
+
+							}
+							//ptr += count;
+
+						} else {
+
+							// a literal-run
+							scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr );
+							ptr += count; pos += count;
+
+						}
+
+					}
+
+
+					// now convert data from buffer into rgba
+					// first red, then green, then blue, then exponent (alpha)
+					const l = scanline_width; //scanline_buffer.byteLength;
+					for ( let i = 0; i < l; i ++ ) {
+
+						let off = 0;
+						data_rgba[ offset ] = scanline_buffer[ i + off ];
+						off += scanline_width; //1;
+						data_rgba[ offset + 1 ] = scanline_buffer[ i + off ];
+						off += scanline_width; //1;
+						data_rgba[ offset + 2 ] = scanline_buffer[ i + off ];
+						off += scanline_width; //1;
+						data_rgba[ offset + 3 ] = scanline_buffer[ i + off ];
+						offset += 4;
+
+					}
+
+					num_scanlines --;
+
+				}
+
+				return data_rgba;
+
+			};
+
+		const RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+
+			const e = sourceArray[ sourceOffset + 3 ];
+			const scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+
+			destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
+			destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
+			destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
+			destArray[ destOffset + 3 ] = 1;
+
+		};
+
+		const RGBEByteToRGBHalf = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+
+			const e = sourceArray[ sourceOffset + 3 ];
+			const scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+
+			// clamping to 65504, the maximum representable value in float16
+			destArray[ destOffset + 0 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 0 ] * scale, 65504 ) );
+			destArray[ destOffset + 1 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 1 ] * scale, 65504 ) );
+			destArray[ destOffset + 2 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 2 ] * scale, 65504 ) );
+			destArray[ destOffset + 3 ] = DataUtils.toHalfFloat( 1 );
+
+		};
+
+		const byteArray = new Uint8Array( buffer );
+		byteArray.pos = 0;
+		const rgbe_header_info = RGBE_ReadHeader( byteArray );
+
+		const w = rgbe_header_info.width,
+			h = rgbe_header_info.height,
+			image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h );
+
+
+		let data, type;
+		let numElements;
+
+		switch ( this.type ) {
+
+			case FloatType:
+
+				numElements = image_rgba_data.length / 4;
+				const floatArray = new Float32Array( numElements * 4 );
+
+				for ( let j = 0; j < numElements; j ++ ) {
+
+					RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 4 );
+
+				}
+
+				data = floatArray;
+				type = FloatType;
+				break;
+
+			case HalfFloatType:
+
+				numElements = image_rgba_data.length / 4;
+				const halfArray = new Uint16Array( numElements * 4 );
+
+				for ( let j = 0; j < numElements; j ++ ) {
+
+					RGBEByteToRGBHalf( image_rgba_data, j * 4, halfArray, j * 4 );
+
+				}
+
+				data = halfArray;
+				type = HalfFloatType;
+				break;
+
+			default:
+
+				throw new Error( 'THREE.HDRLoader: Unsupported type: ' + this.type );
+				break;
+
+		}
+
+		return {
+			width: w, height: h,
+			data: data,
+			header: rgbe_header_info.string,
+			gamma: rgbe_header_info.gamma,
+			exposure: rgbe_header_info.exposure,
+			type: type
+		};
+
+	}
+
+	/**
+     * Sets the texture type.
+     *
+     * @param {(HalfFloatType|FloatType)} value - The texture type to set.
+     * @return {HDRLoader} A reference to this loader.
+     */
+	setDataType( value ) {
+
+		this.type = value;
+		return this;
+
+	}
+
+	load( url, onLoad, onProgress, onError ) {
+
+		function onLoadCallback( texture, texData ) {
+
+			switch ( texture.type ) {
+
+				case FloatType:
+				case HalfFloatType:
+
+					texture.colorSpace = LinearSRGBColorSpace;
+					texture.minFilter = LinearFilter;
+					texture.magFilter = LinearFilter;
+					texture.generateMipmaps = false;
+					texture.flipY = true;
+
+					break;
+
+			}
+
+			if ( onLoad ) onLoad( texture, texData );
+
+		}
+
+		return super.load( url, onLoadCallback, onProgress, onError );
+
+	}
+
+}
+
+export { HDRLoader };
+
+

+ 112 - 32
examples/jsm/loaders/KTX2Loader.js

@@ -9,6 +9,8 @@ import {
 	HalfFloatType,
 	HalfFloatType,
 	LinearFilter,
 	LinearFilter,
 	LinearMipmapLinearFilter,
 	LinearMipmapLinearFilter,
+	NearestFilter,
+	NearestMipmapNearestFilter,
 	LinearSRGBColorSpace,
 	LinearSRGBColorSpace,
 	Loader,
 	Loader,
 	NoColorSpace,
 	NoColorSpace,
@@ -19,6 +21,7 @@ import {
 	RGBA_S3TC_DXT3_Format,
 	RGBA_S3TC_DXT3_Format,
 	RGBA_ETC2_EAC_Format,
 	RGBA_ETC2_EAC_Format,
 	RGBA_PVRTC_4BPPV1_Format,
 	RGBA_PVRTC_4BPPV1_Format,
+	RGBA_PVRTC_2BPPV1_Format,
 	RGBA_S3TC_DXT1_Format,
 	RGBA_S3TC_DXT1_Format,
 	RGBA_S3TC_DXT5_Format,
 	RGBA_S3TC_DXT5_Format,
 	RGB_BPTC_UNSIGNED_Format,
 	RGB_BPTC_UNSIGNED_Format,
@@ -26,10 +29,17 @@ import {
 	RGB_ETC2_Format,
 	RGB_ETC2_Format,
 	RGB_PVRTC_4BPPV1_Format,
 	RGB_PVRTC_4BPPV1_Format,
 	RGB_S3TC_DXT1_Format,
 	RGB_S3TC_DXT1_Format,
+	SIGNED_RED_GREEN_RGTC2_Format,
+	SIGNED_RED_RGTC1_Format,
+	RED_GREEN_RGTC2_Format,
+	RED_RGTC1_Format,
+	RGBFormat,
 	RGFormat,
 	RGFormat,
 	RedFormat,
 	RedFormat,
 	SRGBColorSpace,
 	SRGBColorSpace,
-	UnsignedByteType
+	UnsignedByteType,
+	UnsignedInt5999Type,
+	UnsignedInt101111Type
 } from 'three';
 } from 'three';
 import { WorkerPool } from '../utils/WorkerPool.js';
 import { WorkerPool } from '../utils/WorkerPool.js';
 import {
 import {
@@ -42,6 +52,7 @@ import {
 	KHR_SUPERCOMPRESSION_NONE,
 	KHR_SUPERCOMPRESSION_NONE,
 	KHR_SUPERCOMPRESSION_ZSTD,
 	KHR_SUPERCOMPRESSION_ZSTD,
 	VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,
 	VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,
+	VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT,
 	VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
 	VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
 	VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
 	VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
 	VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
 	VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
@@ -52,12 +63,18 @@ import {
 	VK_FORMAT_BC1_RGB_UNORM_BLOCK,
 	VK_FORMAT_BC1_RGB_UNORM_BLOCK,
 	VK_FORMAT_BC3_SRGB_BLOCK,
 	VK_FORMAT_BC3_SRGB_BLOCK,
 	VK_FORMAT_BC3_UNORM_BLOCK,
 	VK_FORMAT_BC3_UNORM_BLOCK,
+	VK_FORMAT_BC4_SNORM_BLOCK,
+	VK_FORMAT_BC4_UNORM_BLOCK,
 	VK_FORMAT_BC5_SNORM_BLOCK,
 	VK_FORMAT_BC5_SNORM_BLOCK,
 	VK_FORMAT_BC5_UNORM_BLOCK,
 	VK_FORMAT_BC5_UNORM_BLOCK,
 	VK_FORMAT_BC7_SRGB_BLOCK,
 	VK_FORMAT_BC7_SRGB_BLOCK,
 	VK_FORMAT_BC7_UNORM_BLOCK,
 	VK_FORMAT_BC7_UNORM_BLOCK,
 	VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
 	VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
 	VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
 	VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
+	VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG,
+	VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG,
+	VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG,
+	VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG,
 	VK_FORMAT_R16G16B16A16_SFLOAT,
 	VK_FORMAT_R16G16B16A16_SFLOAT,
 	VK_FORMAT_R16G16_SFLOAT,
 	VK_FORMAT_R16G16_SFLOAT,
 	VK_FORMAT_R16_SFLOAT,
 	VK_FORMAT_R16_SFLOAT,
@@ -70,6 +87,8 @@ import {
 	VK_FORMAT_R8G8_UNORM,
 	VK_FORMAT_R8G8_UNORM,
 	VK_FORMAT_R8_SRGB,
 	VK_FORMAT_R8_SRGB,
 	VK_FORMAT_R8_UNORM,
 	VK_FORMAT_R8_UNORM,
+	VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
+	VK_FORMAT_B10G11R11_UFLOAT_PACK32,
 	VK_FORMAT_UNDEFINED
 	VK_FORMAT_UNDEFINED
 } from '../libs/ktx-parse.module.js';
 } from '../libs/ktx-parse.module.js';
 import { ZSTDDecoder } from '../libs/zstddec.module.js';
 import { ZSTDDecoder } from '../libs/zstddec.module.js';
@@ -183,10 +202,10 @@ class KTX2Loader extends Loader {
 		this.workerConfig = {
 		this.workerConfig = {
 			astcSupported: await renderer.hasFeatureAsync( 'texture-compression-astc' ),
 			astcSupported: await renderer.hasFeatureAsync( 'texture-compression-astc' ),
 			astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856
 			astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856
-			etc1Supported: await renderer.hasFeatureAsync( 'texture-compression-etc1' ),
+			etc1Supported: await renderer.hasFeatureAsync( 'texture-compression-etc2' ),
 			etc2Supported: await renderer.hasFeatureAsync( 'texture-compression-etc2' ),
 			etc2Supported: await renderer.hasFeatureAsync( 'texture-compression-etc2' ),
 			dxtSupported: await renderer.hasFeatureAsync( 'texture-compression-bc' ),
 			dxtSupported: await renderer.hasFeatureAsync( 'texture-compression-bc' ),
-			bptcSupported: await renderer.hasFeatureAsync( 'texture-compression-bptc' ),
+			bptcSupported: await renderer.hasFeatureAsync( 'texture-compression-bc' ),
 			pvrtcSupported: await renderer.hasFeatureAsync( 'texture-compression-pvrtc' )
 			pvrtcSupported: await renderer.hasFeatureAsync( 'texture-compression-pvrtc' )
 		};
 		};
 
 
@@ -208,10 +227,10 @@ class KTX2Loader extends Loader {
 			this.workerConfig = {
 			this.workerConfig = {
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
 				astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856
 				astcHDRSupported: false, // https://github.com/gpuweb/gpuweb/issues/3856
-				etc1Supported: renderer.hasFeature( 'texture-compression-etc1' ),
+				etc1Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
-				bptcSupported: renderer.hasFeature( 'texture-compression-bptc' ),
+				bptcSupported: renderer.hasFeature( 'texture-compression-bc' ),
 				pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' )
 				pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' )
 			};
 			};
 
 
@@ -918,74 +937,114 @@ KTX2Loader.BasisWorker = function () {
 // Parsing for non-Basis textures. These textures may have supercompression
 // Parsing for non-Basis textures. These textures may have supercompression
 // like Zstd, but they do not require transcoding.
 // like Zstd, but they do not require transcoding.
 
 
-const UNCOMPRESSED_FORMATS = new Set( [ RGBAFormat, RGFormat, RedFormat ] );
+const UNCOMPRESSED_FORMATS = new Set( [ RGBAFormat, RGBFormat, RGFormat, RedFormat ] );
 
 
 const FORMAT_MAP = {
 const FORMAT_MAP = {
 
 
 	[ VK_FORMAT_R32G32B32A32_SFLOAT ]: RGBAFormat,
 	[ VK_FORMAT_R32G32B32A32_SFLOAT ]: RGBAFormat,
-	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: RGBAFormat,
-	[ VK_FORMAT_R8G8B8A8_UNORM ]: RGBAFormat,
-	[ VK_FORMAT_R8G8B8A8_SRGB ]: RGBAFormat,
-
 	[ VK_FORMAT_R32G32_SFLOAT ]: RGFormat,
 	[ VK_FORMAT_R32G32_SFLOAT ]: RGFormat,
-	[ VK_FORMAT_R16G16_SFLOAT ]: RGFormat,
-	[ VK_FORMAT_R8G8_UNORM ]: RGFormat,
-	[ VK_FORMAT_R8G8_SRGB ]: RGFormat,
-
 	[ VK_FORMAT_R32_SFLOAT ]: RedFormat,
 	[ VK_FORMAT_R32_SFLOAT ]: RedFormat,
+
+	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: RGBAFormat,
+	[ VK_FORMAT_R16G16_SFLOAT ]: RGFormat,
 	[ VK_FORMAT_R16_SFLOAT ]: RedFormat,
 	[ VK_FORMAT_R16_SFLOAT ]: RedFormat,
+
+	[ VK_FORMAT_R8G8B8A8_SRGB ]: RGBAFormat,
+	[ VK_FORMAT_R8G8B8A8_UNORM ]: RGBAFormat,
+	[ VK_FORMAT_R8G8_SRGB ]: RGFormat,
+	[ VK_FORMAT_R8G8_UNORM ]: RGFormat,
 	[ VK_FORMAT_R8_SRGB ]: RedFormat,
 	[ VK_FORMAT_R8_SRGB ]: RedFormat,
 	[ VK_FORMAT_R8_UNORM ]: RedFormat,
 	[ VK_FORMAT_R8_UNORM ]: RedFormat,
 
 
-	[ VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK ]: RGB_ETC2_Format,
+	[ VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 ]: RGBFormat,
+	[ VK_FORMAT_B10G11R11_UFLOAT_PACK32 ]: RGBFormat,
+
 	[ VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK ]: RGBA_ETC2_EAC_Format,
 	[ VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK ]: RGBA_ETC2_EAC_Format,
+	[ VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK ]: RGB_ETC2_Format,
 
 
 	[ VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT ]: RGBA_ASTC_4x4_Format,
 	[ VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT ]: RGBA_ASTC_4x4_Format,
 	[ VK_FORMAT_ASTC_4x4_SRGB_BLOCK ]: RGBA_ASTC_4x4_Format,
 	[ VK_FORMAT_ASTC_4x4_SRGB_BLOCK ]: RGBA_ASTC_4x4_Format,
 	[ VK_FORMAT_ASTC_4x4_UNORM_BLOCK ]: RGBA_ASTC_4x4_Format,
 	[ VK_FORMAT_ASTC_4x4_UNORM_BLOCK ]: RGBA_ASTC_4x4_Format,
+	[ VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT ]: RGBA_ASTC_6x6_Format,
 	[ VK_FORMAT_ASTC_6x6_SRGB_BLOCK ]: RGBA_ASTC_6x6_Format,
 	[ VK_FORMAT_ASTC_6x6_SRGB_BLOCK ]: RGBA_ASTC_6x6_Format,
 	[ VK_FORMAT_ASTC_6x6_UNORM_BLOCK ]: RGBA_ASTC_6x6_Format,
 	[ VK_FORMAT_ASTC_6x6_UNORM_BLOCK ]: RGBA_ASTC_6x6_Format,
 
 
-	[ VK_FORMAT_BC1_RGBA_UNORM_BLOCK ]: RGBA_S3TC_DXT1_Format,
 	[ VK_FORMAT_BC1_RGBA_SRGB_BLOCK ]: RGBA_S3TC_DXT1_Format,
 	[ VK_FORMAT_BC1_RGBA_SRGB_BLOCK ]: RGBA_S3TC_DXT1_Format,
-	[ VK_FORMAT_BC1_RGB_UNORM_BLOCK ]: RGB_S3TC_DXT1_Format,
+	[ VK_FORMAT_BC1_RGBA_UNORM_BLOCK ]: RGBA_S3TC_DXT1_Format,
 	[ VK_FORMAT_BC1_RGB_SRGB_BLOCK ]: RGB_S3TC_DXT1_Format,
 	[ VK_FORMAT_BC1_RGB_SRGB_BLOCK ]: RGB_S3TC_DXT1_Format,
+	[ VK_FORMAT_BC1_RGB_UNORM_BLOCK ]: RGB_S3TC_DXT1_Format,
 
 
 	[ VK_FORMAT_BC3_SRGB_BLOCK ]: RGBA_S3TC_DXT3_Format,
 	[ VK_FORMAT_BC3_SRGB_BLOCK ]: RGBA_S3TC_DXT3_Format,
 	[ VK_FORMAT_BC3_UNORM_BLOCK ]: RGBA_S3TC_DXT3_Format,
 	[ VK_FORMAT_BC3_UNORM_BLOCK ]: RGBA_S3TC_DXT3_Format,
 
 
-	[ VK_FORMAT_BC5_SNORM_BLOCK ]: RGBA_S3TC_DXT5_Format,
-	[ VK_FORMAT_BC5_UNORM_BLOCK ]: RGBA_S3TC_DXT5_Format,
+	[ VK_FORMAT_BC4_SNORM_BLOCK ]: SIGNED_RED_RGTC1_Format,
+	[ VK_FORMAT_BC4_UNORM_BLOCK ]: RED_RGTC1_Format,
+
+	[ VK_FORMAT_BC5_SNORM_BLOCK ]: SIGNED_RED_GREEN_RGTC2_Format,
+	[ VK_FORMAT_BC5_UNORM_BLOCK ]: RED_GREEN_RGTC2_Format,
 
 
 	[ VK_FORMAT_BC7_SRGB_BLOCK ]: RGBA_BPTC_Format,
 	[ VK_FORMAT_BC7_SRGB_BLOCK ]: RGBA_BPTC_Format,
 	[ VK_FORMAT_BC7_UNORM_BLOCK ]: RGBA_BPTC_Format,
 	[ VK_FORMAT_BC7_UNORM_BLOCK ]: RGBA_BPTC_Format,
 
 
+	[ VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG ]: RGBA_PVRTC_4BPPV1_Format,
+	[ VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG ]: RGBA_PVRTC_4BPPV1_Format,
+	[ VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG ]: RGBA_PVRTC_2BPPV1_Format,
+	[ VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG ]: RGBA_PVRTC_2BPPV1_Format,
+
 };
 };
 
 
 const TYPE_MAP = {
 const TYPE_MAP = {
 
 
 	[ VK_FORMAT_R32G32B32A32_SFLOAT ]: FloatType,
 	[ VK_FORMAT_R32G32B32A32_SFLOAT ]: FloatType,
-	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: HalfFloatType,
-	[ VK_FORMAT_R8G8B8A8_UNORM ]: UnsignedByteType,
-	[ VK_FORMAT_R8G8B8A8_SRGB ]: UnsignedByteType,
-
 	[ VK_FORMAT_R32G32_SFLOAT ]: FloatType,
 	[ VK_FORMAT_R32G32_SFLOAT ]: FloatType,
-	[ VK_FORMAT_R16G16_SFLOAT ]: HalfFloatType,
-	[ VK_FORMAT_R8G8_UNORM ]: UnsignedByteType,
-	[ VK_FORMAT_R8G8_SRGB ]: UnsignedByteType,
-
 	[ VK_FORMAT_R32_SFLOAT ]: FloatType,
 	[ VK_FORMAT_R32_SFLOAT ]: FloatType,
+
+	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: HalfFloatType,
+	[ VK_FORMAT_R16G16_SFLOAT ]: HalfFloatType,
 	[ VK_FORMAT_R16_SFLOAT ]: HalfFloatType,
 	[ VK_FORMAT_R16_SFLOAT ]: HalfFloatType,
+
+	[ VK_FORMAT_R8G8B8A8_SRGB ]: UnsignedByteType,
+	[ VK_FORMAT_R8G8B8A8_UNORM ]: UnsignedByteType,
+	[ VK_FORMAT_R8G8_SRGB ]: UnsignedByteType,
+	[ VK_FORMAT_R8G8_UNORM ]: UnsignedByteType,
 	[ VK_FORMAT_R8_SRGB ]: UnsignedByteType,
 	[ VK_FORMAT_R8_SRGB ]: UnsignedByteType,
 	[ VK_FORMAT_R8_UNORM ]: UnsignedByteType,
 	[ VK_FORMAT_R8_UNORM ]: UnsignedByteType,
 
 
-	[ VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 ]: UnsignedInt5999Type,
+	[ VK_FORMAT_B10G11R11_UFLOAT_PACK32 ]: UnsignedInt101111Type,
+
 	[ VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK ]: UnsignedByteType,
 	[ VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK ]: UnsignedByteType,
 
 
 	[ VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT ]: HalfFloatType,
 	[ VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT ]: HalfFloatType,
+	[ VK_FORMAT_ASTC_4x4_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_ASTC_4x4_UNORM_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT ]: HalfFloatType,
 	[ VK_FORMAT_ASTC_6x6_SRGB_BLOCK ]: UnsignedByteType,
 	[ VK_FORMAT_ASTC_6x6_SRGB_BLOCK ]: UnsignedByteType,
 	[ VK_FORMAT_ASTC_6x6_UNORM_BLOCK ]: UnsignedByteType,
 	[ VK_FORMAT_ASTC_6x6_UNORM_BLOCK ]: UnsignedByteType,
 
 
+	[ VK_FORMAT_BC1_RGBA_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC1_RGBA_UNORM_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC1_RGB_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC1_RGB_UNORM_BLOCK ]: UnsignedByteType,
+
+	[ VK_FORMAT_BC3_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC3_UNORM_BLOCK ]: UnsignedByteType,
+
+	[ VK_FORMAT_BC4_SNORM_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC4_UNORM_BLOCK ]: UnsignedByteType,
+
+	[ VK_FORMAT_BC5_SNORM_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC5_UNORM_BLOCK ]: UnsignedByteType,
+
+	[ VK_FORMAT_BC7_SRGB_BLOCK ]: UnsignedByteType,
+	[ VK_FORMAT_BC7_UNORM_BLOCK ]: UnsignedByteType,
+
+	[ VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG ]: UnsignedByteType,
+	[ VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG ]: UnsignedByteType,
+	[ VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG ]: UnsignedByteType,
+	[ VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG ]: UnsignedByteType,
+
 };
 };
 
 
 async function createRawTexture( container ) {
 async function createRawTexture( container ) {
@@ -994,7 +1053,14 @@ async function createRawTexture( container ) {
 
 
 	if ( FORMAT_MAP[ vkFormat ] === undefined ) {
 	if ( FORMAT_MAP[ vkFormat ] === undefined ) {
 
 
-		throw new Error( 'THREE.KTX2Loader: Unsupported vkFormat.' );
+		throw new Error( 'THREE.KTX2Loader: Unsupported vkFormat: ' + vkFormat );
+
+	}
+
+	// TODO: Merge the TYPE_MAP warning into the thrown error above, after r190.
+	if ( TYPE_MAP[ vkFormat ] === undefined ) {
+
+		console.warn( 'THREE.KTX2Loader: Missing ".type" for vkFormat: ' + vkFormat );
 
 
 	}
 	}
 
 
@@ -1024,7 +1090,6 @@ async function createRawTexture( container ) {
 
 
 	const mipmaps = [];
 	const mipmaps = [];
 
 
-
 	for ( let levelIndex = 0; levelIndex < container.levels.length; levelIndex ++ ) {
 	for ( let levelIndex = 0; levelIndex < container.levels.length; levelIndex ++ ) {
 
 
 		const levelWidth = Math.max( 1, container.pixelWidth >> levelIndex );
 		const levelWidth = Math.max( 1, container.pixelWidth >> levelIndex );
@@ -1071,6 +1136,16 @@ async function createRawTexture( container ) {
 
 
 			);
 			);
 
 
+		} else if ( TYPE_MAP[ vkFormat ] === UnsignedInt5999Type || TYPE_MAP[ vkFormat ] === UnsignedInt101111Type ) {
+
+			data = new Uint32Array(
+
+				levelData.buffer,
+				levelData.byteOffset,
+				levelData.byteLength / Uint32Array.BYTES_PER_ELEMENT
+
+			);
+
 		} else {
 		} else {
 
 
 			data = levelData;
 			data = levelData;
@@ -1088,6 +1163,9 @@ async function createRawTexture( container ) {
 
 
 	}
 	}
 
 
+	// levelCount = 0 implies runtime-generated mipmaps.
+	const useMipmaps = container.levelCount === 0 || mipmaps.length > 1;
+
 	let texture;
 	let texture;
 
 
 	if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {
 	if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {
@@ -1095,14 +1173,16 @@ async function createRawTexture( container ) {
 		texture = container.pixelDepth === 0
 		texture = container.pixelDepth === 0
 			? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
 			? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
 			: new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );
 			: new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );
+		texture.minFilter = useMipmaps ? NearestMipmapNearestFilter : NearestFilter;
+		texture.magFilter = NearestFilter;
+		texture.generateMipmaps = container.levelCount === 0;
 
 
 	} else {
 	} else {
 
 
 		if ( container.pixelDepth > 0 ) throw new Error( 'THREE.KTX2Loader: Unsupported pixelDepth.' );
 		if ( container.pixelDepth > 0 ) throw new Error( 'THREE.KTX2Loader: Unsupported pixelDepth.' );
 
 
 		texture = new CompressedTexture( mipmaps, container.pixelWidth, container.pixelHeight );
 		texture = new CompressedTexture( mipmaps, container.pixelWidth, container.pixelHeight );
-
-		texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
+		texture.minFilter = useMipmaps ? LinearMipmapLinearFilter : LinearFilter;
 		texture.magFilter = LinearFilter;
 		texture.magFilter = LinearFilter;
 
 
 	}
 	}

+ 7 - 473
examples/jsm/loaders/RGBELoader.js

@@ -1,484 +1,18 @@
-import {
-	DataTextureLoader,
-	DataUtils,
-	FloatType,
-	HalfFloatType,
-	LinearFilter,
-	LinearSRGBColorSpace
-} from 'three';
+import { HDRLoader } from './HDRLoader.js';
 
 
-/**
- * A loader for the RGBE HDR texture format.
- *
- * ```js
- * const loader = new RGBELoader();
- * const envMap = await loader.loadAsync( 'textures/equirectangular/blouberg_sunrise_2_1k.hdr' );
- * envMap.mapping = THREE.EquirectangularReflectionMapping;
- *
- * scene.environment = envMap;
- * ```
- *
- * @augments DataTextureLoader
- * @three_import import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
- */
-class RGBELoader extends DataTextureLoader {
+// @deprecated, r180
+
+class RGBELoader extends HDRLoader {
 
 
-	/**
-	 * Constructs a new RGBE loader.
-	 *
-	 * @param {LoadingManager} [manager] - The loading manager.
-	 */
 	constructor( manager ) {
 	constructor( manager ) {
 
 
+		console.warn( 'RGBELoader has been deprecated. Please use HDRLoader instead.' );
 		super( manager );
 		super( manager );
 
 
-		/**
-		 * The texture type.
-		 *
-		 * @type {(HalfFloatType|FloatType)}
-		 * @default HalfFloatType
-		 */
-		this.type = HalfFloatType;
-
-	}
-
-	/**
-	 * Parses the given RGBE texture data.
-	 *
-	 * @param {ArrayBuffer} buffer - The raw texture data.
-	 * @return {DataTextureLoader~TexData} An object representing the parsed texture data.
-	 */
-	parse( buffer ) {
-
-		// adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
-
-		const
-			/* default error routine.  change this to change error handling */
-			rgbe_read_error = 1,
-			rgbe_write_error = 2,
-			rgbe_format_error = 3,
-			rgbe_memory_error = 4,
-			rgbe_error = function ( rgbe_error_code, msg ) {
-
-				switch ( rgbe_error_code ) {
-
-					case rgbe_read_error: throw new Error( 'THREE.RGBELoader: Read Error: ' + ( msg || '' ) );
-					case rgbe_write_error: throw new Error( 'THREE.RGBELoader: Write Error: ' + ( msg || '' ) );
-					case rgbe_format_error: throw new Error( 'THREE.RGBELoader: Bad File Format: ' + ( msg || '' ) );
-					default:
-					case rgbe_memory_error: throw new Error( 'THREE.RGBELoader: Memory Error: ' + ( msg || '' ) );
-
-				}
-
-			},
-
-			/* offsets to red, green, and blue components in a data (float) pixel */
-			//RGBE_DATA_RED = 0,
-			//RGBE_DATA_GREEN = 1,
-			//RGBE_DATA_BLUE = 2,
-
-			/* number of floats per pixel, use 4 since stored in rgba image format */
-			//RGBE_DATA_SIZE = 4,
-
-			/* flags indicating which fields in an rgbe_header_info are valid */
-			RGBE_VALID_PROGRAMTYPE = 1,
-			RGBE_VALID_FORMAT = 2,
-			RGBE_VALID_DIMENSIONS = 4,
-
-			NEWLINE = '\n',
-
-			fgets = function ( buffer, lineLimit, consume ) {
-
-				const chunkSize = 128;
-
-				lineLimit = ! lineLimit ? 1024 : lineLimit;
-				let p = buffer.pos,
-					i = - 1, len = 0, s = '',
-					chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
-
-				while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) {
-
-					s += chunk; len += chunk.length;
-					p += chunkSize;
-					chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
-
-				}
-
-				if ( - 1 < i ) {
-
-					/*for (i=l-1; i>=0; i--) {
-						byteCode = m.charCodeAt(i);
-						if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++;
-						else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2;
-						if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate
-					}*/
-					if ( false !== consume ) buffer.pos += len + i + 1;
-					return s + chunk.slice( 0, i );
-
-				}
-
-				return false;
-
-			},
-
-			/* minimal header reading.  modify if you want to parse more information */
-			RGBE_ReadHeader = function ( buffer ) {
-
-
-				// regexes to parse header info fields
-				const magic_token_re = /^#\?(\S+)/,
-					gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,
-					exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,
-					format_re = /^\s*FORMAT=(\S+)\s*$/,
-					dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,
-
-					// RGBE format header struct
-					header = {
-
-						valid: 0, /* indicate which fields are valid */
-
-						string: '', /* the actual header string */
-
-						comments: '', /* comments found in header */
-
-						programtype: 'RGBE', /* listed at beginning of file to identify it after "#?". defaults to "RGBE" */
-
-						format: '', /* RGBE format, default 32-bit_rle_rgbe */
-
-						gamma: 1.0, /* image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) */
-
-						exposure: 1.0, /* a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0 */
-
-						width: 0, height: 0 /* image dimensions, width/height */
-
-					};
-
-				let line, match;
-
-				if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) {
-
-					rgbe_error( rgbe_read_error, 'no header found' );
-
-				}
-
-				/* if you want to require the magic token then uncomment the next line */
-				if ( ! ( match = line.match( magic_token_re ) ) ) {
-
-					rgbe_error( rgbe_format_error, 'bad initial token' );
-
-				}
-
-				header.valid |= RGBE_VALID_PROGRAMTYPE;
-				header.programtype = match[ 1 ];
-				header.string += line + '\n';
-
-				while ( true ) {
-
-					line = fgets( buffer );
-					if ( false === line ) break;
-					header.string += line + '\n';
-
-					if ( '#' === line.charAt( 0 ) ) {
-
-						header.comments += line + '\n';
-						continue; // comment line
-
-					}
-
-					if ( match = line.match( gamma_re ) ) {
-
-						header.gamma = parseFloat( match[ 1 ] );
-
-					}
-
-					if ( match = line.match( exposure_re ) ) {
-
-						header.exposure = parseFloat( match[ 1 ] );
-
-					}
-
-					if ( match = line.match( format_re ) ) {
-
-						header.valid |= RGBE_VALID_FORMAT;
-						header.format = match[ 1 ];//'32-bit_rle_rgbe';
-
-					}
-
-					if ( match = line.match( dimensions_re ) ) {
-
-						header.valid |= RGBE_VALID_DIMENSIONS;
-						header.height = parseInt( match[ 1 ], 10 );
-						header.width = parseInt( match[ 2 ], 10 );
-
-					}
-
-					if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break;
-
-				}
-
-				if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) {
-
-					rgbe_error( rgbe_format_error, 'missing format specifier' );
-
-				}
-
-				if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) {
-
-					rgbe_error( rgbe_format_error, 'missing image size specifier' );
-
-				}
-
-				return header;
-
-			},
-
-			RGBE_ReadPixels_RLE = function ( buffer, w, h ) {
-
-				const scanline_width = w;
-
-				if (
-					// run length encoding is not allowed so read flat
-					( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) ||
-					// this file is not run length encoded
-					( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) )
-				) {
-
-					// return the flat buffer
-					return new Uint8Array( buffer );
-
-				}
-
-				if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) {
-
-					rgbe_error( rgbe_format_error, 'wrong scanline width' );
-
-				}
-
-				const data_rgba = new Uint8Array( 4 * w * h );
-
-				if ( ! data_rgba.length ) {
-
-					rgbe_error( rgbe_memory_error, 'unable to allocate buffer space' );
-
-				}
-
-				let offset = 0, pos = 0;
-
-				const ptr_end = 4 * scanline_width;
-				const rgbeStart = new Uint8Array( 4 );
-				const scanline_buffer = new Uint8Array( ptr_end );
-				let num_scanlines = h;
-
-				// read in each successive scanline
-				while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) {
-
-					if ( pos + 4 > buffer.byteLength ) {
-
-						rgbe_error( rgbe_read_error );
-
-					}
-
-					rgbeStart[ 0 ] = buffer[ pos ++ ];
-					rgbeStart[ 1 ] = buffer[ pos ++ ];
-					rgbeStart[ 2 ] = buffer[ pos ++ ];
-					rgbeStart[ 3 ] = buffer[ pos ++ ];
-
-					if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) {
-
-						rgbe_error( rgbe_format_error, 'bad rgbe scanline format' );
-
-					}
-
-					// read each of the four channels for the scanline into the buffer
-					// first red, then green, then blue, then exponent
-					let ptr = 0, count;
-
-					while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) {
-
-						count = buffer[ pos ++ ];
-						const isEncodedRun = count > 128;
-						if ( isEncodedRun ) count -= 128;
-
-						if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) {
-
-							rgbe_error( rgbe_format_error, 'bad scanline data' );
-
-						}
-
-						if ( isEncodedRun ) {
-
-							// a (encoded) run of the same value
-							const byteValue = buffer[ pos ++ ];
-							for ( let i = 0; i < count; i ++ ) {
-
-								scanline_buffer[ ptr ++ ] = byteValue;
-
-							}
-							//ptr += count;
-
-						} else {
-
-							// a literal-run
-							scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr );
-							ptr += count; pos += count;
-
-						}
-
-					}
-
-
-					// now convert data from buffer into rgba
-					// first red, then green, then blue, then exponent (alpha)
-					const l = scanline_width; //scanline_buffer.byteLength;
-					for ( let i = 0; i < l; i ++ ) {
-
-						let off = 0;
-						data_rgba[ offset ] = scanline_buffer[ i + off ];
-						off += scanline_width; //1;
-						data_rgba[ offset + 1 ] = scanline_buffer[ i + off ];
-						off += scanline_width; //1;
-						data_rgba[ offset + 2 ] = scanline_buffer[ i + off ];
-						off += scanline_width; //1;
-						data_rgba[ offset + 3 ] = scanline_buffer[ i + off ];
-						offset += 4;
-
-					}
-
-					num_scanlines --;
-
-				}
-
-				return data_rgba;
-
-			};
-
-		const RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
-
-			const e = sourceArray[ sourceOffset + 3 ];
-			const scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
-
-			destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
-			destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
-			destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
-			destArray[ destOffset + 3 ] = 1;
-
-		};
-
-		const RGBEByteToRGBHalf = function ( sourceArray, sourceOffset, destArray, destOffset ) {
-
-			const e = sourceArray[ sourceOffset + 3 ];
-			const scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
-
-			// clamping to 65504, the maximum representable value in float16
-			destArray[ destOffset + 0 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 0 ] * scale, 65504 ) );
-			destArray[ destOffset + 1 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 1 ] * scale, 65504 ) );
-			destArray[ destOffset + 2 ] = DataUtils.toHalfFloat( Math.min( sourceArray[ sourceOffset + 2 ] * scale, 65504 ) );
-			destArray[ destOffset + 3 ] = DataUtils.toHalfFloat( 1 );
-
-		};
-
-		const byteArray = new Uint8Array( buffer );
-		byteArray.pos = 0;
-		const rgbe_header_info = RGBE_ReadHeader( byteArray );
-
-		const w = rgbe_header_info.width,
-			h = rgbe_header_info.height,
-			image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h );
-
-
-		let data, type;
-		let numElements;
-
-		switch ( this.type ) {
-
-			case FloatType:
-
-				numElements = image_rgba_data.length / 4;
-				const floatArray = new Float32Array( numElements * 4 );
-
-				for ( let j = 0; j < numElements; j ++ ) {
-
-					RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 4 );
-
-				}
-
-				data = floatArray;
-				type = FloatType;
-				break;
-
-			case HalfFloatType:
-
-				numElements = image_rgba_data.length / 4;
-				const halfArray = new Uint16Array( numElements * 4 );
-
-				for ( let j = 0; j < numElements; j ++ ) {
-
-					RGBEByteToRGBHalf( image_rgba_data, j * 4, halfArray, j * 4 );
-
-				}
-
-				data = halfArray;
-				type = HalfFloatType;
-				break;
-
-			default:
-
-				throw new Error( 'THREE.RGBELoader: Unsupported type: ' + this.type );
-				break;
-
-		}
-
-		return {
-			width: w, height: h,
-			data: data,
-			header: rgbe_header_info.string,
-			gamma: rgbe_header_info.gamma,
-			exposure: rgbe_header_info.exposure,
-			type: type
-		};
-
-	}
-
-	/**
-	 * Sets the texture type.
-	 *
-	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
-	 * @return {RGBELoader} A reference to this loader.
-	 */
-	setDataType( value ) {
-
-		this.type = value;
-		return this;
-
-	}
-
-	load( url, onLoad, onProgress, onError ) {
-
-		function onLoadCallback( texture, texData ) {
-
-			switch ( texture.type ) {
-
-				case FloatType:
-				case HalfFloatType:
-
-					texture.colorSpace = LinearSRGBColorSpace;
-					texture.minFilter = LinearFilter;
-					texture.magFilter = LinearFilter;
-					texture.generateMipmaps = false;
-					texture.flipY = true;
-
-					break;
-
-			}
-
-			if ( onLoad ) onLoad( texture, texData );
-
-		}
-
-		return super.load( url, onLoadCallback, onProgress, onError );
-
 	}
 	}
 
 
 }
 }
 
 
 export { RGBELoader };
 export { RGBELoader };
+
+

+ 0 - 1148
examples/jsm/loaders/RGBMLoader.js

@@ -1,1148 +0,0 @@
-import {
-	DataTextureLoader,
-	RGBAFormat,
-	LinearFilter,
-	CubeTexture,
-	HalfFloatType,
-	DataUtils
-} from 'three';
-
-/**
- * A loader for the RGBM HDR texture format.
- *
- * ```js
- * const loader = new RGBMLoader();
- * loader.setMaxRange( 16 );
- *
- * const texture = await loader.loadAsync( 'textures/memorial.png' );
- * ```
- *
- * @augments DataTextureLoader
- * @three_import import { RGBMLoader } from 'three/addons/loaders/RGBMLoader.js';
- */
-class RGBMLoader extends DataTextureLoader {
-
-	/**
-	 * Constructs a new RGBM loader.
-	 *
-	 * @param {LoadingManager} [manager] - The loading manager.
-	 */
-	constructor( manager ) {
-
-		super( manager );
-
-		/**
-		 * The texture type.
-		 *
-		 * @type {(HalfFloatType|FloatType)}
-		 * @default HalfFloatType
-		 */
-		this.type = HalfFloatType;
-
-		/**
-		 * More information about this property at [The difference between RGBM and RGBD]{@link https://iwasbeingirony.blogspot.com/2010/06/difference-between-rgbm-and-rgbd.html}
-		 *
-		 * @type {(7|16)}
-		 * @default 7
-		 */
-		this.maxRange = 7;
-
-	}
-
-	/**
-	 * Sets the texture type.
-	 *
-	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
-	 * @return {RGBMLoader} A reference to this loader.
-	 */
-	setDataType( value ) {
-
-		this.type = value;
-		return this;
-
-	}
-
-	/**
-	 * Sets the maximum range.
-	 *
-	 * @param {(7|16)} value - The maximum range to set.
-	 * @return {RGBMLoader} A reference to this loader.
-	 */
-	setMaxRange( value ) {
-
-		this.maxRange = value;
-		return this;
-
-	}
-
-	/**
-	 * Starts loading from the given URLs and passes the loaded RGBM cube map
-	 * to the `onLoad()` callback.
-	 *
-	 * @param {Array<string>} urls - The paths/URLs of the files to be loaded. This can also be a data URIs.
-	 * @param {function(CubeTexture)} 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.
-	 * @return {CubeTexture} The cube texture.
-	 */
-	loadCubemap( urls, onLoad, onProgress, onError ) {
-
-		const texture = new CubeTexture();
-
-		for ( let i = 0; i < 6; i ++ ) {
-
-			texture.images[ i ] = undefined;
-
-		}
-
-		let loaded = 0;
-
-		const scope = this;
-
-		function loadTexture( i ) {
-
-			scope.load( urls[ i ], function ( image ) {
-
-				texture.images[ i ] = image;
-
-				loaded ++;
-
-				if ( loaded === 6 ) {
-
-					texture.needsUpdate = true;
-
-					if ( onLoad ) onLoad( texture );
-
-				}
-
-			}, undefined, onError );
-
-		}
-
-		for ( let i = 0; i < urls.length; ++ i ) {
-
-			loadTexture( i );
-
-		}
-
-		texture.type = this.type;
-		texture.format = RGBAFormat;
-		texture.minFilter = LinearFilter;
-		texture.generateMipmaps = false;
-
-		return texture;
-
-	}
-
-	/**
-	 * Async version of {@link RGBMLoader#loadCubemap}.
-	 *
-	 * @async
-	 * @param {Array<string>} urls - The paths/URLs of the files to be loaded. This can also be a data URIs.
-	 * @param {onProgressCallback} onProgress - Executed while the loading is in progress.
-	 * @return {Promise<CubeTexture>} A Promise that resolves with the loaded cube map.
-	 */
-	loadCubemapAsync( urls, onProgress ) {
-
-		return new Promise( ( resolve, reject ) => {
-
-			this.loadCubemap( urls, resolve, onProgress, reject );
-
-		} );
-
-	}
-
-	/**
-	 * Parses the given RGBM texture data.
-	 *
-	 * @param {ArrayBuffer} buffer - The raw texture data.
-	 * @return {DataTextureLoader~TexData} An object representing the parsed texture data.
-	 */
-	parse( buffer ) {
-
-		const img = UPNG.decode( buffer );
-		const rgba = UPNG.toRGBA8( img )[ 0 ];
-
-		const data = new Uint8Array( rgba );
-		const size = img.width * img.height * 4;
-
-		const output = ( this.type === HalfFloatType ) ? new Uint16Array( size ) : new Float32Array( size );
-
-		// decode RGBM
-
-		for ( let i = 0; i < data.length; i += 4 ) {
-
-			const r = data[ i + 0 ] / 255;
-			const g = data[ i + 1 ] / 255;
-			const b = data[ i + 2 ] / 255;
-			const a = data[ i + 3 ] / 255;
-
-			if ( this.type === HalfFloatType ) {
-
-				output[ i + 0 ] = DataUtils.toHalfFloat( Math.min( r * a * this.maxRange, 65504 ) );
-				output[ i + 1 ] = DataUtils.toHalfFloat( Math.min( g * a * this.maxRange, 65504 ) );
-				output[ i + 2 ] = DataUtils.toHalfFloat( Math.min( b * a * this.maxRange, 65504 ) );
-				output[ i + 3 ] = DataUtils.toHalfFloat( 1 );
-
-			} else {
-
-				output[ i + 0 ] = r * a * this.maxRange;
-				output[ i + 1 ] = g * a * this.maxRange;
-				output[ i + 2 ] = b * a * this.maxRange;
-				output[ i + 3 ] = 1;
-
-			}
-
-		}
-
-		return {
-			width: img.width,
-			height: img.height,
-			data: output,
-			format: RGBAFormat,
-			type: this.type,
-			flipY: true
-		};
-
-	}
-
-}
-
-// from https://github.com/photopea/UPNG.js (MIT License)
-
-var UPNG = {};
-
-UPNG.toRGBA8 = function ( out ) {
-
-	var w = out.width, h = out.height;
-	if ( out.tabs.acTL == null ) return [ UPNG.toRGBA8.decodeImage( out.data, w, h, out ).buffer ];
-
-	var frms = [];
-	if ( out.frames[ 0 ].data == null ) out.frames[ 0 ].data = out.data;
-
-	var len = w * h * 4, img = new Uint8Array( len ), empty = new Uint8Array( len ), prev = new Uint8Array( len );
-	for ( var i = 0; i < out.frames.length; i ++ ) {
-
-		var frm = out.frames[ i ];
-		var fx = frm.rect.x, fy = frm.rect.y, fw = frm.rect.width, fh = frm.rect.height;
-		var fdata = UPNG.toRGBA8.decodeImage( frm.data, fw, fh, out );
-
-		if ( i != 0 ) for ( var j = 0; j < len; j ++ ) prev[ j ] = img[ j ];
-
-		if ( frm.blend == 0 ) UPNG._copyTile( fdata, fw, fh, img, w, h, fx, fy, 0 );
-		else if ( frm.blend == 1 ) UPNG._copyTile( fdata, fw, fh, img, w, h, fx, fy, 1 );
-
-		frms.push( img.buffer.slice( 0 ) );
-
-		if ( frm.dispose == 1 ) UPNG._copyTile( empty, fw, fh, img, w, h, fx, fy, 0 );
-		else if ( frm.dispose == 2 ) for ( var j = 0; j < len; j ++ ) img[ j ] = prev[ j ];
-
-	}
-
-	return frms;
-
-};
-
-UPNG.toRGBA8.decodeImage = function ( data, w, h, out ) {
-
-	var area = w * h, bpp = UPNG.decode._getBPP( out );
-	var bpl = Math.ceil( w * bpp / 8 );	// bytes per line
-
-	var bf = new Uint8Array( area * 4 ), bf32 = new Uint32Array( bf.buffer );
-	var ctype = out.ctype, depth = out.depth;
-	var rs = UPNG._bin.readUshort;
-
-	if ( ctype == 6 ) { // RGB + alpha
-
-		var qarea = area << 2;
-		if ( depth == 8 ) for ( var i = 0; i < qarea; i += 4 ) {
-
-			bf[ i ] = data[ i ]; bf[ i + 1 ] = data[ i + 1 ]; bf[ i + 2 ] = data[ i + 2 ]; bf[ i + 3 ] = data[ i + 3 ];
-
-		}
-
-		if ( depth == 16 ) for ( var i = 0; i < qarea; i ++ ) {
-
-			bf[ i ] = data[ i << 1 ];
-
-		}
-
-	} else if ( ctype == 2 ) {	// RGB
-
-		var ts = out.tabs[ 'tRNS' ];
-		if ( ts == null ) {
-
-			if ( depth == 8 ) for ( var i = 0; i < area; i ++ ) {
-
-				var ti = i * 3; bf32[ i ] = ( 255 << 24 ) | ( data[ ti + 2 ] << 16 ) | ( data[ ti + 1 ] << 8 ) | data[ ti ];
-
-			}
-
-			if ( depth == 16 ) for ( var i = 0; i < area; i ++ ) {
-
-				var ti = i * 6; bf32[ i ] = ( 255 << 24 ) | ( data[ ti + 4 ] << 16 ) | ( data[ ti + 2 ] << 8 ) | data[ ti ];
-
-			}
-
-		} else {
-
-			var tr = ts[ 0 ], tg = ts[ 1 ], tb = ts[ 2 ];
-			if ( depth == 8 ) for ( var i = 0; i < area; i ++ ) {
-
-				var qi = i << 2, ti = i * 3; bf32[ i ] = ( 255 << 24 ) | ( data[ ti + 2 ] << 16 ) | ( data[ ti + 1 ] << 8 ) | data[ ti ];
-				if ( data[ ti ] == tr && data[ ti + 1 ] == tg && data[ ti + 2 ] == tb ) bf[ qi + 3 ] = 0;
-
-			}
-
-			if ( depth == 16 ) for ( var i = 0; i < area; i ++ ) {
-
-				var qi = i << 2, ti = i * 6; bf32[ i ] = ( 255 << 24 ) | ( data[ ti + 4 ] << 16 ) | ( data[ ti + 2 ] << 8 ) | data[ ti ];
-				if ( rs( data, ti ) == tr && rs( data, ti + 2 ) == tg && rs( data, ti + 4 ) == tb ) bf[ qi + 3 ] = 0;
-
-			}
-
-		}
-
-	} else if ( ctype == 3 ) {	// palette
-
-		var p = out.tabs[ 'PLTE' ], ap = out.tabs[ 'tRNS' ], tl = ap ? ap.length : 0;
-		//console.log(p, ap);
-		if ( depth == 1 ) for ( var y = 0; y < h; y ++ ) {
-
-			var s0 = y * bpl, t0 = y * w;
-			for ( var i = 0; i < w; i ++ ) {
-
-				var qi = ( t0 + i ) << 2, j = ( ( data[ s0 + ( i >> 3 ) ] >> ( 7 - ( ( i & 7 ) << 0 ) ) ) & 1 ), cj = 3 * j; bf[ qi ] = p[ cj ]; bf[ qi + 1 ] = p[ cj + 1 ]; bf[ qi + 2 ] = p[ cj + 2 ]; bf[ qi + 3 ] = ( j < tl ) ? ap[ j ] : 255;
-
-			}
-
-		}
-
-		if ( depth == 2 ) for ( var y = 0; y < h; y ++ ) {
-
-			var s0 = y * bpl, t0 = y * w;
-			for ( var i = 0; i < w; i ++ ) {
-
-				var qi = ( t0 + i ) << 2, j = ( ( data[ s0 + ( i >> 2 ) ] >> ( 6 - ( ( i & 3 ) << 1 ) ) ) & 3 ), cj = 3 * j; bf[ qi ] = p[ cj ]; bf[ qi + 1 ] = p[ cj + 1 ]; bf[ qi + 2 ] = p[ cj + 2 ]; bf[ qi + 3 ] = ( j < tl ) ? ap[ j ] : 255;
-
-			}
-
-		}
-
-		if ( depth == 4 ) for ( var y = 0; y < h; y ++ ) {
-
-			var s0 = y * bpl, t0 = y * w;
-			for ( var i = 0; i < w; i ++ ) {
-
-				var qi = ( t0 + i ) << 2, j = ( ( data[ s0 + ( i >> 1 ) ] >> ( 4 - ( ( i & 1 ) << 2 ) ) ) & 15 ), cj = 3 * j; bf[ qi ] = p[ cj ]; bf[ qi + 1 ] = p[ cj + 1 ]; bf[ qi + 2 ] = p[ cj + 2 ]; bf[ qi + 3 ] = ( j < tl ) ? ap[ j ] : 255;
-
-			}
-
-		}
-
-		if ( depth == 8 ) for ( var i = 0; i < area; i ++ ) {
-
-			var qi = i << 2, j = data[ i ], cj = 3 * j; bf[ qi ] = p[ cj ]; bf[ qi + 1 ] = p[ cj + 1 ]; bf[ qi + 2 ] = p[ cj + 2 ]; bf[ qi + 3 ] = ( j < tl ) ? ap[ j ] : 255;
-
-		}
-
-	} else if ( ctype == 4 ) {	// gray + alpha
-
-		if ( depth == 8 ) for ( var i = 0; i < area; i ++ ) {
-
-			var qi = i << 2, di = i << 1, gr = data[ di ]; bf[ qi ] = gr; bf[ qi + 1 ] = gr; bf[ qi + 2 ] = gr; bf[ qi + 3 ] = data[ di + 1 ];
-
-		}
-
-		if ( depth == 16 ) for ( var i = 0; i < area; i ++ ) {
-
-			var qi = i << 2, di = i << 2, gr = data[ di ]; bf[ qi ] = gr; bf[ qi + 1 ] = gr; bf[ qi + 2 ] = gr; bf[ qi + 3 ] = data[ di + 2 ];
-
-		}
-
-	} else if ( ctype == 0 ) {	// gray
-
-		var tr = out.tabs[ 'tRNS' ] ? out.tabs[ 'tRNS' ] : - 1;
-		for ( var y = 0; y < h; y ++ ) {
-
-			var off = y * bpl, to = y * w;
-			if ( depth == 1 ) for ( var x = 0; x < w; x ++ ) {
-
-				var gr = 255 * ( ( data[ off + ( x >>> 3 ) ] >>> ( 7 - ( x & 7 ) ) ) & 1 ), al = ( gr == tr * 255 ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
-
-			}
-			else if ( depth == 2 ) for ( var x = 0; x < w; x ++ ) {
-
-				var gr = 85 * ( ( data[ off + ( x >>> 2 ) ] >>> ( 6 - ( ( x & 3 ) << 1 ) ) ) & 3 ), al = ( gr == tr * 85 ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
-
-			}
-			else if ( depth == 4 ) for ( var x = 0; x < w; x ++ ) {
-
-				var gr = 17 * ( ( data[ off + ( x >>> 1 ) ] >>> ( 4 - ( ( x & 1 ) << 2 ) ) ) & 15 ), al = ( gr == tr * 17 ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
-
-			}
-			else if ( depth == 8 ) for ( var x = 0; x < w; x ++ ) {
-
-				var gr = data[ off + x ], al = ( gr == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
-
-			}
-			else if ( depth == 16 ) for ( var x = 0; x < w; x ++ ) {
-
-				var gr = data[ off + ( x << 1 ) ], al = ( rs( data, off + ( x << 1 ) ) == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
-
-			}
-
-		}
-
-	}
-
-	//console.log(Date.now()-time);
-	return bf;
-
-};
-
-
-
-UPNG.decode = function ( buff ) {
-
-	var data = new Uint8Array( buff ), offset = 8, bin = UPNG._bin, rUs = bin.readUshort, rUi = bin.readUint;
-	var out = { tabs: {}, frames: [] };
-	var dd = new Uint8Array( data.length ), doff = 0;	 // put all IDAT data into it
-	var fd, foff = 0;	// frames
-	var text, keyw, bfr;
-
-	var mgck = [ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a ];
-	for ( var i = 0; i < 8; i ++ ) if ( data[ i ] != mgck[ i ] ) throw new Error( 'The input is not a PNG file!' );
-
-	while ( offset < data.length ) {
-
-		var len = bin.readUint( data, offset ); offset += 4;
-		var type = bin.readASCII( data, offset, 4 ); offset += 4;
-		//console.log(type,len);
-
-		if ( type == 'IHDR' ) {
-
-			UPNG.decode._IHDR( data, offset, out );
-
-		} else if ( type == 'CgBI' ) {
-
-			out.tabs[ type ] = data.slice( offset, offset + 4 );
-
-		} else if ( type == 'IDAT' ) {
-
-			for ( var i = 0; i < len; i ++ ) dd[ doff + i ] = data[ offset + i ];
-			doff += len;
-
-		} else if ( type == 'acTL' ) {
-
-			out.tabs[ type ] = { num_frames: rUi( data, offset ), num_plays: rUi( data, offset + 4 ) };
-			fd = new Uint8Array( data.length );
-
-		} else if ( type == 'fcTL' ) {
-
-			if ( foff != 0 ) {
-
-				var fr = out.frames[ out.frames.length - 1 ];
-				fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height ); foff = 0;
-
-			}
-
-			var rct = { x: rUi( data, offset + 12 ), y: rUi( data, offset + 16 ), width: rUi( data, offset + 4 ), height: rUi( data, offset + 8 ) };
-			var del = rUs( data, offset + 22 ); del = rUs( data, offset + 20 ) / ( del == 0 ? 100 : del );
-			var frm = { rect: rct, delay: Math.round( del * 1000 ), dispose: data[ offset + 24 ], blend: data[ offset + 25 ] };
-			//console.log(frm);
-			out.frames.push( frm );
-
-		} else if ( type == 'fdAT' ) {
-
-			for ( var i = 0; i < len - 4; i ++ ) fd[ foff + i ] = data[ offset + i + 4 ];
-			foff += len - 4;
-
-		} else if ( type == 'pHYs' ) {
-
-			out.tabs[ type ] = [ bin.readUint( data, offset ), bin.readUint( data, offset + 4 ), data[ offset + 8 ] ];
-
-		} else if ( type == 'cHRM' ) {
-
-			out.tabs[ type ] = [];
-			for ( var i = 0; i < 8; i ++ ) out.tabs[ type ].push( bin.readUint( data, offset + i * 4 ) );
-
-		} else if ( type == 'tEXt' || type == 'zTXt' ) {
-
-			if ( out.tabs[ type ] == null ) out.tabs[ type ] = {};
-			var nz = bin.nextZero( data, offset );
-			keyw = bin.readASCII( data, offset, nz - offset );
-			var tl = offset + len - nz - 1;
-			if ( type == 'tEXt' ) text = bin.readASCII( data, nz + 1, tl );
-			else {
-
-				bfr = UPNG.decode._inflate( data.slice( nz + 2, nz + 2 + tl ) );
-				text = bin.readUTF8( bfr, 0, bfr.length );
-
-			}
-
-			out.tabs[ type ][ keyw ] = text;
-
-		} else if ( type == 'iTXt' ) {
-
-			if ( out.tabs[ type ] == null ) out.tabs[ type ] = {};
-			var nz = 0, off = offset;
-			nz = bin.nextZero( data, off );
-			keyw = bin.readASCII( data, off, nz - off ); off = nz + 1;
-			var cflag = data[ off ]; off += 2;
-			nz = bin.nextZero( data, off );
-			bin.readASCII( data, off, nz - off ); off = nz + 1;
-			nz = bin.nextZero( data, off );
-			bin.readUTF8( data, off, nz - off ); off = nz + 1;
-			var tl = len - ( off - offset );
-			if ( cflag == 0 ) text = bin.readUTF8( data, off, tl );
-			else {
-
-				bfr = UPNG.decode._inflate( data.slice( off, off + tl ) );
-				text = bin.readUTF8( bfr, 0, bfr.length );
-
-			}
-
-			out.tabs[ type ][ keyw ] = text;
-
-		} else if ( type == 'PLTE' ) {
-
-			out.tabs[ type ] = bin.readBytes( data, offset, len );
-
-		} else if ( type == 'hIST' ) {
-
-			var pl = out.tabs[ 'PLTE' ].length / 3;
-			out.tabs[ type ] = []; for ( var i = 0; i < pl; i ++ ) out.tabs[ type ].push( rUs( data, offset + i * 2 ) );
-
-		} else if ( type == 'tRNS' ) {
-
-			if ( out.ctype == 3 ) out.tabs[ type ] = bin.readBytes( data, offset, len );
-			else if ( out.ctype == 0 ) out.tabs[ type ] = rUs( data, offset );
-			else if ( out.ctype == 2 ) out.tabs[ type ] = [ rUs( data, offset ), rUs( data, offset + 2 ), rUs( data, offset + 4 ) ];
-			//else console.log("tRNS for unsupported color type",out.ctype, len);
-
-		} else if ( type == 'gAMA' ) out.tabs[ type ] = bin.readUint( data, offset ) / 100000;
-		else if ( type == 'sRGB' ) out.tabs[ type ] = data[ offset ];
-		else if ( type == 'bKGD' ) {
-
-			if ( out.ctype == 0 || out.ctype == 4 ) out.tabs[ type ] = [ rUs( data, offset ) ];
-			else if ( out.ctype == 2 || out.ctype == 6 ) out.tabs[ type ] = [ rUs( data, offset ), rUs( data, offset + 2 ), rUs( data, offset + 4 ) ];
-			else if ( out.ctype == 3 ) out.tabs[ type ] = data[ offset ];
-
-		} else if ( type == 'IEND' ) {
-
-			break;
-
-		}
-
-		//else {  console.log("unknown chunk type", type, len);  out.tabs[type]=data.slice(offset,offset+len);  }
-		offset += len;
-		bin.readUint( data, offset ); offset += 4;
-
-	}
-
-	if ( foff != 0 ) {
-
-		var fr = out.frames[ out.frames.length - 1 ];
-		fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height );
-
-	}
-
-	out.data = UPNG.decode._decompress( out, dd, out.width, out.height );
-
-	delete out.compress; delete out.interlace; delete out.filter;
-	return out;
-
-};
-
-UPNG.decode._decompress = function ( out, dd, w, h ) {
-
-	var bpp = UPNG.decode._getBPP( out ), bpl = Math.ceil( w * bpp / 8 ), buff = new Uint8Array( ( bpl + 1 + out.interlace ) * h );
-	if ( out.tabs[ 'CgBI' ] ) dd = UPNG.inflateRaw( dd, buff );
-	else dd = UPNG.decode._inflate( dd, buff );
-
-	if ( out.interlace == 0 ) dd = UPNG.decode._filterZero( dd, out, 0, w, h );
-	else if ( out.interlace == 1 ) dd = UPNG.decode._readInterlace( dd, out );
-
-	return dd;
-
-};
-
-UPNG.decode._inflate = function ( data, buff ) {
-
-	var out = UPNG[ 'inflateRaw' ]( new Uint8Array( data.buffer, 2, data.length - 6 ), buff ); return out;
-
-};
-
-UPNG.inflateRaw = function () {
-
-	var H = {}; H.H = {}; H.H.N = function ( N, W ) {
-
-		var R = Uint8Array, i = 0, m = 0, J = 0, h = 0, Q = 0, X = 0, u = 0, w = 0, d = 0, v, C;
-		if ( N[ 0 ] == 3 && N[ 1 ] == 0 ) return W ? W : new R( 0 ); var V = H.H, n = V.b, A = V.e, l = V.R, M = V.n, I = V.A, e = V.Z, b = V.m, Z = W == null;
-		if ( Z )W = new R( N.length >>> 2 << 5 ); while ( i == 0 ) {
-
-			i = n( N, d, 1 ); m = n( N, d + 1, 2 ); d += 3; if ( m == 0 ) {
-
-				if ( ( d & 7 ) != 0 )d += 8 - ( d & 7 );
-				var D = ( d >>> 3 ) + 4, q = N[ D - 4 ] | N[ D - 3 ] << 8; if ( Z )W = H.H.W( W, w + q ); W.set( new R( N.buffer, N.byteOffset + D, q ), w ); d = D + q << 3;
-				w += q; continue
-				;
-
-			}
-
-			if ( Z )W = H.H.W( W, w + ( 1 << 17 ) ); if ( m == 1 ) {
-
-				v = b.J; C = b.h; X = ( 1 << 9 ) - 1; u = ( 1 << 5 ) - 1;
-
-			}
-
-			if ( m == 2 ) {
-
-				J = A( N, d, 5 ) + 257;
-				h = A( N, d + 5, 5 ) + 1; Q = A( N, d + 10, 4 ) + 4; d += 14; var j = 1; for ( var c = 0; c < 38; c += 2 ) {
-
-					b.Q[ c ] = 0; b.Q[ c + 1 ] = 0;
-
-				}
-
-				for ( var c = 0;
-					c < Q; c ++ ) {
-
-					var K = A( N, d + c * 3, 3 ); b.Q[ ( b.X[ c ] << 1 ) + 1 ] = K; if ( K > j )j = K
-					;
-
-				}
-
-				d += 3 * Q; M( b.Q, j ); I( b.Q, j, b.u ); v = b.w; C = b.d;
-				d = l( b.u, ( 1 << j ) - 1, J + h, N, d, b.v ); var r = V.V( b.v, 0, J, b.C ); X = ( 1 << r ) - 1; var S = V.V( b.v, J, h, b.D ); u = ( 1 << S ) - 1; M( b.C, r );
-				I( b.C, r, v ); M( b.D, S ); I( b.D, S, C )
-				;
-
-			}
-
-			while ( ! 0 ) {
-
-				var T = v[ e( N, d ) & X ]; d += T & 15; var p = T >>> 4; if ( p >>> 8 == 0 ) {
-
-					W[ w ++ ] = p;
-
-				} else if ( p == 256 ) {
-
-					break;
-
-				} else {
-
-					var z = w + p - 254;
-					if ( p > 264 ) {
-
-						var _ = b.q[ p - 257 ]; z = w + ( _ >>> 3 ) + A( N, d, _ & 7 ); d += _ & 7;
-
-					}
-
-					var $ = C[ e( N, d ) & u ]; d += $ & 15; var s = $ >>> 4, Y = b.c[ s ], a = ( Y >>> 4 ) + n( N, d, Y & 15 );
-					d += Y & 15; while ( w < z ) {
-
-						W[ w ] = W[ w ++ - a ]; W[ w ] = W[ w ++ - a ]; W[ w ] = W[ w ++ - a ]; W[ w ] = W[ w ++ - a ];
-
-					}
-
-					w = z
-					;
-
-				}
-
-			}
-
-		}
-
-		return W.length == w ? W : W.slice( 0, w )
-		;
-
-	};
-
-	H.H.W = function ( N, W ) {
-
-		var R = N.length; if ( W <= R ) return N; var V = new Uint8Array( R << 1 ); V.set( N, 0 ); return V;
-
-	};
-
-	H.H.R = function ( N, W, R, V, n, A ) {
-
-		var l = H.H.e, M = H.H.Z, I = 0; while ( I < R ) {
-
-			var e = N[ M( V, n ) & W ]; n += e & 15; var b = e >>> 4;
-			if ( b <= 15 ) {
-
-				A[ I ] = b; I ++;
-
-			} else {
-
-				var Z = 0, m = 0; if ( b == 16 ) {
-
-					m = 3 + l( V, n, 2 ); n += 2; Z = A[ I - 1 ];
-
-				} else if ( b == 17 ) {
-
-					m = 3 + l( V, n, 3 );
-					n += 3
-					;
-
-				} else if ( b == 18 ) {
-
-					m = 11 + l( V, n, 7 ); n += 7;
-
-				}
-
-				var J = I + m; while ( I < J ) {
-
-					A[ I ] = Z; I ++;
-
-				}
-
-			}
-
-		}
-
-		return n
-		;
-
-	};
-
-	H.H.V = function ( N, W, R, V ) {
-
-		var n = 0, A = 0, l = V.length >>> 1;
-		while ( A < R ) {
-
-			var M = N[ A + W ]; V[ A << 1 ] = 0; V[ ( A << 1 ) + 1 ] = M; if ( M > n )n = M; A ++;
-
-		}
-
-		while ( A < l ) {
-
-			V[ A << 1 ] = 0; V[ ( A << 1 ) + 1 ] = 0; A ++;
-
-		}
-
-		return n
-		;
-
-	};
-
-	H.H.n = function ( N, W ) {
-
-		var R = H.H.m, V = N.length, n, A, l, M, I, e = R.j; for ( var M = 0; M <= W; M ++ )e[ M ] = 0; for ( M = 1; M < V; M += 2 )e[ N[ M ] ] ++;
-		var b = R.K; n = 0; e[ 0 ] = 0; for ( A = 1; A <= W; A ++ ) {
-
-			n = n + e[ A - 1 ] << 1; b[ A ] = n;
-
-		}
-
-		for ( l = 0; l < V; l += 2 ) {
-
-			I = N[ l + 1 ]; if ( I != 0 ) {
-
-				N[ l ] = b[ I ];
-				b[ I ] ++
-				;
-
-			}
-
-		}
-
-	};
-
-	H.H.A = function ( N, W, R ) {
-
-		var V = N.length, n = H.H.m, A = n.r; for ( var l = 0; l < V; l += 2 ) if ( N[ l + 1 ] != 0 ) {
-
-			var M = l >> 1, I = N[ l + 1 ], e = M << 4 | I, b = W - I, Z = N[ l ] << b, m = Z + ( 1 << b );
-			while ( Z != m ) {
-
-				var J = A[ Z ] >>> 15 - W; R[ J ] = e; Z ++;
-
-			}
-
-		}
-
-	};
-
-	H.H.l = function ( N, W ) {
-
-		var R = H.H.m.r, V = 15 - W; for ( var n = 0; n < N.length;
-			n += 2 ) {
-
-			var A = N[ n ] << W - N[ n + 1 ]; N[ n ] = R[ A ] >>> V;
-
-		}
-
-	};
-
-	H.H.M = function ( N, W, R ) {
-
-		R = R << ( W & 7 ); var V = W >>> 3; N[ V ] |= R; N[ V + 1 ] |= R >>> 8;
-
-	};
-
-	H.H.I = function ( N, W, R ) {
-
-		R = R << ( W & 7 ); var V = W >>> 3; N[ V ] |= R; N[ V + 1 ] |= R >>> 8; N[ V + 2 ] |= R >>> 16;
-
-	};
-
-	H.H.e = function ( N, W, R ) {
-
-		return ( N[ W >>> 3 ] | N[ ( W >>> 3 ) + 1 ] << 8 ) >>> ( W & 7 ) & ( 1 << R ) - 1;
-
-	};
-
-	H.H.b = function ( N, W, R ) {
-
-		return ( N[ W >>> 3 ] | N[ ( W >>> 3 ) + 1 ] << 8 | N[ ( W >>> 3 ) + 2 ] << 16 ) >>> ( W & 7 ) & ( 1 << R ) - 1;
-
-	};
-
-	H.H.Z = function ( N, W ) {
-
-		return ( N[ W >>> 3 ] | N[ ( W >>> 3 ) + 1 ] << 8 | N[ ( W >>> 3 ) + 2 ] << 16 ) >>> ( W & 7 );
-
-	};
-
-	H.H.i = function ( N, W ) {
-
-		return ( N[ W >>> 3 ] | N[ ( W >>> 3 ) + 1 ] << 8 | N[ ( W >>> 3 ) + 2 ] << 16 | N[ ( W >>> 3 ) + 3 ] << 24 ) >>> ( W & 7 );
-
-	};
-
-	H.H.m = function () {
-
-		var N = Uint16Array, W = Uint32Array;
-		return { K: new N( 16 ), j: new N( 16 ), X: [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ], S: [ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 999, 999, 999 ], T: [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0, 0 ], q: new N( 32 ), p: [ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 65535, 65535 ], z: [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 ], c: new W( 32 ), J: new N( 512 ), _: [], h: new N( 32 ), $: [], w: new N( 32768 ), C: [], v: [], d: new N( 32768 ), D: [], u: new N( 512 ), Q: [], r: new N( 1 << 15 ), s: new W( 286 ), Y: new W( 30 ), a: new W( 19 ), t: new W( 15e3 ), k: new N( 1 << 16 ), g: new N( 1 << 15 ) }
-		;
-
-	}();
-	( function () {
-
-		var N = H.H.m, W = 1 << 15; for ( var R = 0; R < W; R ++ ) {
-
-			var V = R; V = ( V & 2863311530 ) >>> 1 | ( V & 1431655765 ) << 1;
-			V = ( V & 3435973836 ) >>> 2 | ( V & 858993459 ) << 2; V = ( V & 4042322160 ) >>> 4 | ( V & 252645135 ) << 4; V = ( V & 4278255360 ) >>> 8 | ( V & 16711935 ) << 8;
-			N.r[ R ] = ( V >>> 16 | V << 16 ) >>> 17
-			;
-
-		}
-
-		function n( A, l, M ) {
-
-			while ( l -- != 0 )A.push( 0, M )
-			;
-
-		}
-
-		for ( var R = 0; R < 32; R ++ ) {
-
-			N.q[ R ] = N.S[ R ] << 3 | N.T[ R ];
-			N.c[ R ] = N.p[ R ] << 4 | N.z[ R ]
-			;
-
-		}
-
-		n( N._, 144, 8 ); n( N._, 255 - 143, 9 ); n( N._, 279 - 255, 7 ); n( N._, 287 - 279, 8 ); H.H.n( N._, 9 );
-		H.H.A( N._, 9, N.J ); H.H.l( N._, 9 ); n( N.$, 32, 5 ); H.H.n( N.$, 5 ); H.H.A( N.$, 5, N.h ); H.H.l( N.$, 5 ); n( N.Q, 19, 0 ); n( N.C, 286, 0 );
-		n( N.D, 30, 0 ); n( N.v, 320, 0 )
-		;
-
-	}() );
-
-	return H.H.N
-	;
-
-}();
-
-
-UPNG.decode._readInterlace = function ( data, out ) {
-
-	var w = out.width, h = out.height;
-	var bpp = UPNG.decode._getBPP( out ), cbpp = bpp >> 3, bpl = Math.ceil( w * bpp / 8 );
-	var img = new Uint8Array( h * bpl );
-	var di = 0;
-
-	var starting_row = [ 0, 0, 4, 0, 2, 0, 1 ];
-	var starting_col = [ 0, 4, 0, 2, 0, 1, 0 ];
-	var row_increment = [ 8, 8, 8, 4, 4, 2, 2 ];
-	var col_increment = [ 8, 8, 4, 4, 2, 2, 1 ];
-
-	var pass = 0;
-	while ( pass < 7 ) {
-
-		var ri = row_increment[ pass ], ci = col_increment[ pass ];
-		var sw = 0, sh = 0;
-		var cr = starting_row[ pass ]; while ( cr < h ) {
-
-			cr += ri; sh ++;
-
-		}
-
-		var cc = starting_col[ pass ]; while ( cc < w ) {
-
-			cc += ci; sw ++;
-
-		}
-
-		var bpll = Math.ceil( sw * bpp / 8 );
-		UPNG.decode._filterZero( data, out, di, sw, sh );
-
-		var y = 0, row = starting_row[ pass ];
-		var val;
-
-		while ( row < h ) {
-
-			var col = starting_col[ pass ];
-			var cdi = ( di + y * bpll ) << 3;
-
-			while ( col < w ) {
-
-				if ( bpp == 1 ) {
-
-					val = data[ cdi >> 3 ]; val = ( val >> ( 7 - ( cdi & 7 ) ) ) & 1;
-					img[ row * bpl + ( col >> 3 ) ] |= ( val << ( 7 - ( ( col & 7 ) << 0 ) ) );
-
-				}
-
-				if ( bpp == 2 ) {
-
-					val = data[ cdi >> 3 ]; val = ( val >> ( 6 - ( cdi & 7 ) ) ) & 3;
-					img[ row * bpl + ( col >> 2 ) ] |= ( val << ( 6 - ( ( col & 3 ) << 1 ) ) );
-
-				}
-
-				if ( bpp == 4 ) {
-
-					val = data[ cdi >> 3 ]; val = ( val >> ( 4 - ( cdi & 7 ) ) ) & 15;
-					img[ row * bpl + ( col >> 1 ) ] |= ( val << ( 4 - ( ( col & 1 ) << 2 ) ) );
-
-				}
-
-				if ( bpp >= 8 ) {
-
-					var ii = row * bpl + col * cbpp;
-					for ( var j = 0; j < cbpp; j ++ ) img[ ii + j ] = data[ ( cdi >> 3 ) + j ];
-
-				}
-
-				cdi += bpp; col += ci;
-
-			}
-
-			y ++; row += ri;
-
-		}
-
-		if ( sw * sh != 0 ) di += sh * ( 1 + bpll );
-		pass = pass + 1;
-
-	}
-
-	return img;
-
-};
-
-UPNG.decode._getBPP = function ( out ) {
-
-	var noc = [ 1, null, 3, 1, 2, null, 4 ][ out.ctype ];
-	return noc * out.depth;
-
-};
-
-UPNG.decode._filterZero = function ( data, out, off, w, h ) {
-
-	var bpp = UPNG.decode._getBPP( out ), bpl = Math.ceil( w * bpp / 8 ), paeth = UPNG.decode._paeth;
-	bpp = Math.ceil( bpp / 8 );
-
-	var i, di, type = data[ off ], x = 0;
-
-	if ( type > 1 ) data[ off ] = [ 0, 0, 1 ][ type - 2 ];
-	if ( type == 3 ) for ( x = bpp; x < bpl; x ++ ) data[ x + 1 ] = ( data[ x + 1 ] + ( data[ x + 1 - bpp ] >>> 1 ) ) & 255;
-
-	for ( var y = 0; y < h; y ++ ) {
-
-		i = off + y * bpl; di = i + y + 1;
-		type = data[ di - 1 ]; x = 0;
-
-		if ( type == 0 ) for ( ; x < bpl; x ++ ) data[ i + x ] = data[ di + x ];
-		else if ( type == 1 ) {
-
-			for ( ; x < bpp; x ++ ) data[ i + x ] = data[ di + x ];
-						   for ( ; x < bpl; x ++ ) data[ i + x ] = ( data[ di + x ] + data[ i + x - bpp ] );
-
-		} else if ( type == 2 ) {
-
-			for ( ; x < bpl; x ++ ) data[ i + x ] = ( data[ di + x ] + data[ i + x - bpl ] );
-
-		} else if ( type == 3 ) {
-
-			for ( ; x < bpp; x ++ ) data[ i + x ] = ( data[ di + x ] + ( data[ i + x - bpl ] >>> 1 ) );
-			               for ( ; x < bpl; x ++ ) data[ i + x ] = ( data[ di + x ] + ( ( data[ i + x - bpl ] + data[ i + x - bpp ] ) >>> 1 ) );
-
-		} else {
-
-			for ( ; x < bpp; x ++ ) data[ i + x ] = ( data[ di + x ] + paeth( 0, data[ i + x - bpl ], 0 ) );
-						   for ( ; x < bpl; x ++ ) data[ i + x ] = ( data[ di + x ] + paeth( data[ i + x - bpp ], data[ i + x - bpl ], data[ i + x - bpp - bpl ] ) );
-
-		}
-
-	}
-
-	return data;
-
-};
-
-UPNG.decode._paeth = function ( a, b, c ) {
-
-	var p = a + b - c, pa = ( p - a ), pb = ( p - b ), pc = ( p - c );
-	if ( pa * pa <= pb * pb && pa * pa <= pc * pc ) return a;
-	else if ( pb * pb <= pc * pc ) return b;
-	return c;
-
-};
-
-UPNG.decode._IHDR = function ( data, offset, out ) {
-
-	var bin = UPNG._bin;
-	out.width = bin.readUint( data, offset ); offset += 4;
-	out.height = bin.readUint( data, offset ); offset += 4;
-	out.depth = data[ offset ]; offset ++;
-	out.ctype = data[ offset ]; offset ++;
-	out.compress = data[ offset ]; offset ++;
-	out.filter = data[ offset ]; offset ++;
-	out.interlace = data[ offset ]; offset ++;
-
-};
-
-UPNG._bin = {
-	nextZero: function ( data, p ) {
-
-		while ( data[ p ] != 0 ) p ++; return p;
-
-	},
-	readUshort: function ( buff, p ) {
-
-		return ( buff[ p ] << 8 ) | buff[ p + 1 ];
-
-	},
-	writeUshort: function ( buff, p, n ) {
-
-		buff[ p ] = ( n >> 8 ) & 255; buff[ p + 1 ] = n & 255;
-
-	},
-	readUint: function ( buff, p ) {
-
-		return ( buff[ p ] * ( 256 * 256 * 256 ) ) + ( ( buff[ p + 1 ] << 16 ) | ( buff[ p + 2 ] << 8 ) | buff[ p + 3 ] );
-
-	},
-	writeUint: function ( buff, p, n ) {
-
-		buff[ p ] = ( n >> 24 ) & 255; buff[ p + 1 ] = ( n >> 16 ) & 255; buff[ p + 2 ] = ( n >> 8 ) & 255; buff[ p + 3 ] = n & 255;
-
-	},
-	readASCII: function ( buff, p, l ) {
-
-		var s = ''; for ( var i = 0; i < l; i ++ ) s += String.fromCharCode( buff[ p + i ] ); return s;
-
-	},
-	writeASCII: function ( data, p, s ) {
-
-		for ( var i = 0; i < s.length; i ++ ) data[ p + i ] = s.charCodeAt( i );
-
-	},
-	readBytes: function ( buff, p, l ) {
-
-		var arr = []; for ( var i = 0; i < l; i ++ ) arr.push( buff[ p + i ] ); return arr;
-
-	},
-	pad: function ( n ) {
-
-		return n.length < 2 ? '0' + n : n;
-
-	},
-	readUTF8: function ( buff, p, l ) {
-
-		var s = '', ns;
-		for ( var i = 0; i < l; i ++ ) s += '%' + UPNG._bin.pad( buff[ p + i ].toString( 16 ) );
-		try {
-
-			ns = decodeURIComponent( s );
-
-		} catch ( e ) {
-
-			return UPNG._bin.readASCII( buff, p, l );
-
-		}
-
-		return ns;
-
-	}
-};
-UPNG._copyTile = function ( sb, sw, sh, tb, tw, th, xoff, yoff, mode ) {
-
-	var w = Math.min( sw, tw ), h = Math.min( sh, th );
-	var si = 0, ti = 0;
-	for ( var y = 0; y < h; y ++ )
-		for ( var x = 0; x < w; x ++ ) {
-
-			if ( xoff >= 0 && yoff >= 0 ) {
-
-				si = ( y * sw + x ) << 2; ti = ( ( yoff + y ) * tw + xoff + x ) << 2;
-
-			} else {
-
-				si = ( ( - yoff + y ) * sw - xoff + x ) << 2; ti = ( y * tw + x ) << 2;
-
-			}
-
-			if ( mode == 0 ) {
-
-				tb[ ti ] = sb[ si ]; tb[ ti + 1 ] = sb[ si + 1 ]; tb[ ti + 2 ] = sb[ si + 2 ]; tb[ ti + 3 ] = sb[ si + 3 ];
-
-			} else if ( mode == 1 ) {
-
-				var fa = sb[ si + 3 ] * ( 1 / 255 ), fr = sb[ si ] * fa, fg = sb[ si + 1 ] * fa, fb = sb[ si + 2 ] * fa;
-				var ba = tb[ ti + 3 ] * ( 1 / 255 ), br = tb[ ti ] * ba, bg = tb[ ti + 1 ] * ba, bb = tb[ ti + 2 ] * ba;
-
-				var ifa = 1 - fa, oa = fa + ba * ifa, ioa = ( oa == 0 ? 0 : 1 / oa );
-				tb[ ti + 3 ] = 255 * oa;
-				tb[ ti + 0 ] = ( fr + br * ifa ) * ioa;
-				tb[ ti + 1 ] = ( fg + bg * ifa ) * ioa;
-				tb[ ti + 2 ] = ( fb + bb * ifa ) * ioa;
-
-			} else if ( mode == 2 ) {	// copy only differences, otherwise zero
-
-				var fa = sb[ si + 3 ], fr = sb[ si ], fg = sb[ si + 1 ], fb = sb[ si + 2 ];
-				var ba = tb[ ti + 3 ], br = tb[ ti ], bg = tb[ ti + 1 ], bb = tb[ ti + 2 ];
-				if ( fa == ba && fr == br && fg == bg && fb == bb ) {
-
-					tb[ ti ] = 0; tb[ ti + 1 ] = 0; tb[ ti + 2 ] = 0; tb[ ti + 3 ] = 0;
-
-				} else {
-
-					tb[ ti ] = fr; tb[ ti + 1 ] = fg; tb[ ti + 2 ] = fb; tb[ ti + 3 ] = fa;
-
-				}
-
-			} else if ( mode == 3 ) {	// check if can be blended
-
-				var fa = sb[ si + 3 ], fr = sb[ si ], fg = sb[ si + 1 ], fb = sb[ si + 2 ];
-				var ba = tb[ ti + 3 ], br = tb[ ti ], bg = tb[ ti + 1 ], bb = tb[ ti + 2 ];
-				if ( fa == ba && fr == br && fg == bg && fb == bb ) continue;
-				//if(fa!=255 && ba!=0) return false;
-				if ( fa < 220 && ba > 20 ) return false;
-
-			}
-
-		}
-
-	return true;
-
-};
-
-export { RGBMLoader };

+ 4 - 4
examples/jsm/loaders/TTFLoader.js

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

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

@@ -89,7 +89,7 @@ class UltraHDRLoader extends Loader {
 	 * Sets the texture type.
 	 * Sets the texture type.
 	 *
 	 *
 	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
 	 * @param {(HalfFloatType|FloatType)} value - The texture type to set.
-	 * @return {RGBELoader} A reference to this loader.
+	 * @return {UltraHDRLoader} A reference to this loader.
 	 */
 	 */
 	setDataType( value ) {
 	setDataType( value ) {
 
 

+ 1 - 1
examples/jsm/loaders/lwo/IFFParser.js

@@ -1013,7 +1013,7 @@ class DataViewReader {
 
 
 	getFloat64() {
 	getFloat64() {
 
 
-		const value = this.dv.getFloat64( this.offset, this.littleEndian );
+		const value = this.dv.getFloat64( this.offset );
 		this.offset += 8;
 		this.offset += 8;
 		return value;
 		return value;
 
 

+ 533 - 0
examples/jsm/materials/WoodNodeMaterial.js

@@ -0,0 +1,533 @@
+import * as THREE from 'three';
+import * as TSL from 'three/tsl';
+
+// some helpers below are ported from Blender and converted to TSL
+
+const mapRange = TSL.Fn( ( [ x, fromMin, fromMax, toMin, toMax, clmp ] ) => {
+
+	const factor = x.sub( fromMin ).div( fromMax.sub( fromMin ) );
+	const result = toMin.add( factor.mul( toMax.sub( toMin ) ) );
+
+	return TSL.select( clmp, TSL.max( TSL.min( result, toMax ), toMin ), result );
+
+} );
+
+const voronoi3d = TSL.wgslFn( `
+    fn voronoi3d(x: vec3<f32>, smoothness: f32, randomness: f32) -> f32
+    {
+        let p = floor(x);
+        let f = fract(x);
+
+        var res = 0.0;
+        var totalWeight = 0.0;
+        
+        for (var k = -1; k <= 1; k++)
+        {
+            for (var j = -1; j <= 1; j++)
+            {
+                for (var i = -1; i <= 1; i++)
+                {
+                    let b = vec3<f32>(f32(i), f32(j), f32(k));
+                    let hashOffset = hash3d(p + b) * randomness;
+                    let r = b - f + hashOffset;
+                    let d = length(r);
+                    
+                    let weight = exp(-d * d / max(smoothness * smoothness, 0.001));
+                    res += d * weight;
+                    totalWeight += weight;
+                }
+            }
+        }
+        
+        if (totalWeight > 0.0)
+        {
+            res /= totalWeight;
+        }
+        
+        return smoothstep(0.0, 1.0, res);
+    }
+
+    fn hash3d(p: vec3<f32>) -> vec3<f32>
+    {
+        var p3 = fract(p * vec3<f32>(0.1031, 0.1030, 0.0973));
+        p3 += dot(p3, p3.yzx + 33.33);
+        return fract((p3.xxy + p3.yzz) * p3.zyx);
+    }
+` );
+
+// const hash3d = TSL.Fn( ( [ p ] ) => {
+
+// 	const p3 = p.mul( TSL.vec3( 0.1031, 0.1030, 0.0973 ) ).fract();
+// 	const dotProduct = p3.dot( p3.yzx.add( 33.33 ) );
+// 	p3.addAssign( dotProduct );
+
+// 	return p3.xxy.add( p3.yzz ).mul( p3.zyx ).fract();
+
+// } );
+
+// const voronoi3d = TSL.Fn( ( [ x, smoothness, randomness ] ) => {
+// 	let p = TSL.floor(x);
+// 	let f = TSL.fract(x);
+
+// 	var res = TSL.float(0.0);
+// 	var totalWeight = TSL.float(0.0);
+
+// 	TSL.Loop( 3, 3, 3, ( { k, j, i } ) => {
+// 		let b = TSL.vec3(TSL.float(i).sub(1), TSL.float(j).sub(1), TSL.float(k).sub(1));
+// 		let hashOffset = hash3d(p.add(b)).mul(randomness);
+// 		let r = b.sub(f).add(hashOffset);
+// 		let d = TSL.length(r);
+
+// 		let weight = TSL.exp(d.negate().mul(d).div(TSL.max(smoothness.mul(smoothness), 0.001)));
+// 		res.addAssign(d.mul(weight));
+// 		totalWeight.addAssign(weight);
+// 	} );
+
+// 	res.assign(TSL.select(totalWeight.greaterThan(0.0), res.div(totalWeight), res));
+
+// 	return TSL.smoothstep(0.0, 1.0, res);
+// } );
+
+const softLightMix = TSL.Fn( ( [ t, col1, col2 ] ) => {
+
+	const tm = TSL.float( 1.0 ).sub( t );
+
+	const one = TSL.vec3( 1.0 );
+	const scr = one.sub( one.sub( col2 ).mul( one.sub( col1 ) ) );
+
+	return tm.mul( col1 ).add( t.mul( one.sub( col1 ).mul( col2 ).mul( col1 ).add( col1.mul( scr ) ) ) );
+
+} );
+
+const noiseFbm = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => {
+
+	const fscale = TSL.float( 1.0 ).toVar();
+	const amp = TSL.float( 1.0 ).toVar();
+	const maxamp = TSL.float( 0.0 ).toVar();
+	const sum = TSL.float( 0.0 ).toVar();
+
+	const iterations = detail.floor();
+
+	TSL.Loop( iterations, () => {
+
+		const t = TSL.mx_noise_float( p.mul( fscale ) );
+		sum.addAssign( t.mul( amp ) );
+		maxamp.addAssign( amp );
+		amp.mulAssign( roughness );
+		fscale.mulAssign( lacunarity );
+
+	} );
+
+	const rmd = detail.sub( iterations );
+	const hasRemainder = rmd.greaterThan( 0.001 );
+
+	return TSL.select(
+		hasRemainder,
+		TSL.select(
+			useNormalize.equal( 1 ),
+			( () => {
+
+				const t = TSL.mx_noise_float( p.mul( fscale ) );
+				const sum2 = sum.add( t.mul( amp ) );
+				const maxamp2 = maxamp.add( amp );
+				const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 );
+				const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 );
+				return TSL.mix( normalizedSum, normalizedSum2, rmd );
+
+			} )(),
+			( () => {
+
+				const t = TSL.mx_noise_float( p.mul( fscale ) );
+				const sum2 = sum.add( t.mul( amp ) );
+				return TSL.mix( sum, sum2, rmd );
+
+			} )()
+		),
+		TSL.select(
+			useNormalize.equal( 1 ),
+			sum.div( maxamp ).mul( 0.5 ).add( 0.5 ),
+			sum
+		)
+	);
+
+} );
+
+const noiseFbm3d = TSL.Fn( ( [ p, detail, roughness, lacunarity, useNormalize ] ) => {
+
+	const fscale = TSL.float( 1.0 ).toVar();
+
+	const amp = TSL.float( 1.0 ).toVar();
+	const maxamp = TSL.float( 0.0 ).toVar();
+	const sum = TSL.vec3( 0.0 ).toVar();
+
+	const iterations = detail.floor();
+
+	TSL.Loop( iterations, () => {
+
+		const t = TSL.mx_noise_vec3( p.mul( fscale ) );
+		sum.addAssign( t.mul( amp ) );
+		maxamp.addAssign( amp );
+		amp.mulAssign( roughness );
+		fscale.mulAssign( lacunarity );
+
+	} );
+
+	const rmd = detail.sub( iterations );
+	const hasRemainder = rmd.greaterThan( 0.001 );
+
+	return TSL.select(
+		hasRemainder,
+		TSL.select(
+			useNormalize.equal( 1 ),
+			( () => {
+
+				const t = TSL.mx_noise_vec3( p.mul( fscale ) );
+				const sum2 = sum.add( t.mul( amp ) );
+				const maxamp2 = maxamp.add( amp );
+				const normalizedSum = sum.div( maxamp ).mul( 0.5 ).add( 0.5 );
+				const normalizedSum2 = sum2.div( maxamp2 ).mul( 0.5 ).add( 0.5 );
+				return TSL.mix( normalizedSum, normalizedSum2, rmd );
+
+			} )(),
+			( () => {
+
+				const t = TSL.mx_noise_vec3( p.mul( fscale ) );
+				const sum2 = sum.add( t.mul( amp ) );
+				return TSL.mix( sum, sum2, rmd );
+
+			} )()
+		),
+		TSL.select(
+			useNormalize.equal( 1 ),
+			sum.div( maxamp ).mul( 0.5 ).add( 0.5 ),
+			sum
+		)
+	);
+
+} );
+
+const woodCenter = TSL.Fn( ( [ p, centerSize ] ) => {
+
+	const pxyCenter = p.mul( TSL.vec3( 1, 1, 0 ) ).length();
+	const center = mapRange( pxyCenter, 0, 1, 0, centerSize, true );
+
+	return center;
+
+} );
+
+const spaceWarp = TSL.Fn( ( [ p, warpStrength, xyScale, zScale ] ) => {
+
+	const combinedXyz = TSL.vec3( xyScale, xyScale, zScale ).mul( p );
+	const noise = noiseFbm3d( combinedXyz.mul( 1.6 * 1.5 ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.int( 1 ) ).sub( 0.5 ).mul( warpStrength );
+	const pXy = p.mul( TSL.vec3( 1, 1, 0 ) );
+	const normalizedXy = pXy.normalize();
+	const warp = noise.mul( normalizedXy ).add( pXy );
+
+	return warp;
+
+} );
+
+const woodRings = TSL.Fn( ( [ w, ringThickness, ringBias, ringSizeVariance, ringVarianceScale, barkThickness ] ) => {
+
+	const rings = noiseFbm( w.mul( ringVarianceScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 1 ), TSL.int( 1 ) ).mul( ringSizeVariance ).add( w ).mul( ringThickness ).fract().mul( barkThickness );
+
+	const sharpRings = TSL.min( mapRange( rings, 0, ringBias, 0, 1, TSL.bool( true ) ), mapRange( rings, ringBias, 1, 1, 0, TSL.bool( true ) ) );
+
+	const blurAmount = TSL.max( TSL.positionView.length().div( 10 ), 1 );
+	const blurredRings = TSL.smoothstep( blurAmount.negate(), blurAmount, sharpRings.sub( 0.5 ) ).mul( 0.5 ).add( 0.5 );
+
+	return blurredRings;
+
+} );
+
+const woodDetail = TSL.Fn( ( [ warp, p, y, splotchScale ] ) => {
+
+	const radialCoords = TSL.clamp( TSL.atan( warp.y, warp.x ).div( TSL.PI2 ).add( 0.5 ), 0, 1 ).mul( TSL.PI2.mul( 3 ) );
+	const combinedXyz = TSL.vec3( radialCoords.sin(), y, radialCoords.cos().mul( p.z ) );
+	const scaled = TSL.vec3( 0.1, 1.19, 0.05 ).mul( combinedXyz );
+
+	return noiseFbm( scaled.mul( splotchScale ), TSL.float( 1 ), TSL.float( 0.5 ), TSL.float( 2 ), TSL.bool( true ) );
+
+} );
+
+const cellStructure = TSL.Fn( ( [ p, cellScale, cellSize ] ) => {
+
+	const warp = spaceWarp( p.mul( cellScale.div( 50 ) ), cellScale.div( 1000 ), 0.1, 1.77 );
+	const cells = voronoi3d( warp.xy.mul( 75 ), 0.5, 1 );
+
+	return mapRange( cells, cellSize, cellSize.add( 0.21 ), 0, 1, TSL.bool( true ) );
+
+} );
+
+const wood = TSL.Fn( ( [
+	p,
+	centerSize,
+	largeWarpScale,
+	largeGrainStretch,
+	smallWarpStrength,
+	smallWarpScale,
+	fineWarpStrength,
+	fineWarpScale,
+	ringThickness,
+	ringBias,
+	ringSizeVariance,
+	ringVarianceScale,
+	barkThickness,
+	splotchScale,
+	splotchIntensity,
+	cellScale,
+	cellSize,
+	darkGrainColor,
+	lightGrainColor
+] ) => {
+
+	const center = woodCenter( p, centerSize );
+	const mainWarp = spaceWarp( spaceWarp( p, center, largeWarpScale, largeGrainStretch ), smallWarpStrength, smallWarpScale, 0.17 );
+	const detailWarp = spaceWarp( mainWarp, fineWarpStrength, fineWarpScale, 0.17 );
+	const rings = woodRings( detailWarp.length(), TSL.float( 1 ).div( ringThickness ), ringBias, ringSizeVariance, ringVarianceScale, barkThickness );
+	const detail = woodDetail( detailWarp, p, detailWarp.length(), splotchScale );
+	const cells = cellStructure( mainWarp, cellScale, cellSize.div( TSL.max( TSL.positionView.length().mul( 10 ), 1 ) ) );
+	const baseColor = TSL.mix( darkGrainColor, lightGrainColor, rings );
+
+	return softLightMix( splotchIntensity, softLightMix( 0.407, baseColor, cells ), detail );
+
+} );
+
+const woodParams = {
+	teak: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.11, largeWarpScale: 0.32, largeGrainStretch: 0.24, smallWarpStrength: 0.059,
+		smallWarpScale: 2, fineWarpStrength: 0.006, fineWarpScale: 32.8, ringThickness: 1/34,
+		ringBias: 0.03, ringSizeVariance: 0.03, ringVarianceScale: 4.4, barkThickness: 0.3,
+		splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 910, cellSize: 0.1,
+		darkGrainColor: '#0c0504', lightGrainColor: '#926c50'
+	},
+	walnut: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.07, largeWarpScale: 0.42, largeGrainStretch: 0.34, smallWarpStrength: 0.016,
+		smallWarpScale: 10.3, fineWarpStrength: 0.028, fineWarpScale: 12.7, ringThickness: 1/32,
+		ringBias: 0.08, ringSizeVariance: 0.03, ringVarianceScale: 5.5, barkThickness: 0.98,
+		splotchScale: 1.84, splotchIntensity: 0.97, cellScale: 710, cellSize: 0.31,
+		darkGrainColor: '#311e13', lightGrainColor: '#523424'
+	},
+	white_oak: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.21, smallWarpStrength: 0.034,
+		smallWarpScale: 2.44, fineWarpStrength: 0.01, fineWarpScale: 14.3, ringThickness: 1/34,
+		ringBias: 0.82, ringSizeVariance: 0.16, ringVarianceScale: 1.4, barkThickness: 0.7,
+		splotchScale: 0.2, splotchIntensity: 0.541, cellScale: 800, cellSize: 0.28,
+		darkGrainColor: '#8b4c21', lightGrainColor: '#c57e43'
+	},
+	pine: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.23, largeWarpScale: 0.21, largeGrainStretch: 0.18, smallWarpStrength: 0.041,
+		smallWarpScale: 2.44, fineWarpStrength: 0.006, fineWarpScale: 23.2, ringThickness: 1/24,
+		ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 5, barkThickness: 0.35,
+		splotchScale: 0.51, splotchIntensity: 3.32, cellScale: 1480, cellSize: 0.07,
+		darkGrainColor: '#c58355', lightGrainColor: '#d19d61'
+	},
+	poplar: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.43, largeWarpScale: 0.33, largeGrainStretch: 0.18, smallWarpStrength: 0.04,
+		smallWarpScale: 4.3, fineWarpStrength: 0.004, fineWarpScale: 33.6, ringThickness: 1/37,
+		ringBias: 0.07, ringSizeVariance: 0.03, ringVarianceScale: 3.8, barkThickness: 0.3,
+		splotchScale: 1.92, splotchIntensity: 0.71, cellScale: 830, cellSize: 0.04,
+		darkGrainColor: '#716347', lightGrainColor: '#998966'
+	},
+	maple: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.4, largeWarpScale: 0.38, largeGrainStretch: 0.25, smallWarpStrength: 0.067,
+		smallWarpScale: 2.5, fineWarpStrength: 0.005, fineWarpScale: 33.6, ringThickness: 1/35,
+		ringBias: 0.1, ringSizeVariance: 0.07, ringVarianceScale: 4.6, barkThickness: 0.61,
+		splotchScale: 0.46, splotchIntensity: 1.49, cellScale: 800, cellSize: 0.03,
+		darkGrainColor: '#b08969', lightGrainColor: '#bc9d7d'
+	},
+	red_oak: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.21, largeWarpScale: 0.24, largeGrainStretch: 0.25, smallWarpStrength: 0.044,
+		smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 14.5, ringThickness: 1/34,
+		ringBias: 0.92, ringSizeVariance: 0.03, ringVarianceScale: 5.6, barkThickness: 1.01,
+		splotchScale: 0.28, splotchIntensity: 3.48, cellScale: 800, cellSize: 0.25,
+		darkGrainColor: '#af613b', lightGrainColor: '#e0a27a'
+	},
+	cherry: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.33, largeWarpScale: 0.11, largeGrainStretch: 0.33, smallWarpStrength: 0.024,
+		smallWarpScale: 2.48, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringThickness: 1/36,
+		ringBias: 0.02, ringSizeVariance: 0.04, ringVarianceScale: 6.5, barkThickness: 0.09,
+		splotchScale: 1.27, splotchIntensity: 1.24, cellScale: 1530, cellSize: 0.15,
+		darkGrainColor: '#913f27', lightGrainColor: '#b45837'
+	},
+	cedar: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.11, largeWarpScale: 0.39, largeGrainStretch: 0.12, smallWarpStrength: 0.061,
+		smallWarpScale: 1.9, fineWarpStrength: 0.006, fineWarpScale: 4.8, ringThickness: 1/25,
+		ringBias: 0.01, ringSizeVariance: 0.07, ringVarianceScale: 6.7, barkThickness: 0.1,
+		splotchScale: 0.61, splotchIntensity: 2.54, cellScale: 630, cellSize: 0.19,
+		darkGrainColor: '#9a5b49', lightGrainColor: '#ae745e'
+	},
+	mahogany: {
+		transformationMatrix: new THREE.Matrix4().identity(),
+		centerSize: 1.25, largeWarpScale: 0.26, largeGrainStretch: 0.29, smallWarpStrength: 0.044,
+		smallWarpScale: 2.54, fineWarpStrength: 0.01, fineWarpScale: 15.3, ringThickness: 1/38,
+		ringBias: 0.01, ringSizeVariance: 0.33, ringVarianceScale: 1.2, barkThickness: 0.07,
+		splotchScale: 0.77, splotchIntensity: 1.39, cellScale: 1400, cellSize: 0.23,
+		darkGrainColor: '#501d12', lightGrainColor: '#6d3722'
+	}
+};
+
+export const WoodGenuses = [ 'teak', 'walnut', 'white_oak', 'pine', 'poplar', 'maple', 'red_oak', 'cherry', 'cedar', 'mahogany' ];
+export const Finishes = [ 'raw', 'matte', 'semigloss', 'gloss' ];
+
+export function GetWoodPreset( genus, finish ) {
+
+	const params = woodParams[ genus ];
+
+	let clearcoat, clearcoatRoughness, clearcoatDarken;
+
+	switch ( finish ) {
+
+		case 'gloss':
+			clearcoatDarken = 0.2; clearcoatRoughness = 0.1; clearcoat = 1;
+			break;
+
+		case 'semigloss':
+			clearcoatDarken = 0.4; clearcoatRoughness = 0.4; clearcoat = 1;
+			break;
+
+		case 'matte':
+			clearcoatDarken = 0.6; clearcoatRoughness = 1; clearcoat = 1;
+			break;
+
+		case 'raw':
+		default:
+			clearcoatDarken = 1; clearcoatRoughness = 0; clearcoat = 0;
+
+	}
+
+	return { ...params, transformationMatrix: new THREE.Matrix4().copy( params.transformationMatrix ), genus, finish, clearcoat, clearcoatRoughness, clearcoatDarken };
+
+}
+
+const params = GetWoodPreset( WoodGenuses[ 0 ], Finishes[ 0 ] );
+const uniforms = {};
+
+uniforms.centerSize = TSL.uniform( params.centerSize ).onObjectUpdate( ( { material } ) => material.centerSize );
+uniforms.largeWarpScale = TSL.uniform( params.largeWarpScale ).onObjectUpdate( ( { material } ) => material.largeWarpScale );
+uniforms.largeGrainStretch = TSL.uniform( params.largeGrainStretch ).onObjectUpdate( ( { material } ) => material.largeGrainStretch );
+uniforms.smallWarpStrength = TSL.uniform( params.smallWarpStrength ).onObjectUpdate( ( { material } ) => material.smallWarpStrength );
+uniforms.smallWarpScale = TSL.uniform( params.smallWarpScale ).onObjectUpdate( ( { material } ) => material.smallWarpScale );
+uniforms.fineWarpStrength = TSL.uniform( params.fineWarpStrength ).onObjectUpdate( ( { material } ) => material.fineWarpStrength );
+uniforms.fineWarpScale = TSL.uniform( params.fineWarpScale ).onObjectUpdate( ( { material } ) => material.fineWarpScale );
+uniforms.ringThickness = TSL.uniform( params.ringThickness ).onObjectUpdate( ( { material } ) => material.ringThickness );
+uniforms.ringBias = TSL.uniform( params.ringBias ).onObjectUpdate( ( { material } ) => material.ringBias );
+uniforms.ringSizeVariance = TSL.uniform( params.ringSizeVariance ).onObjectUpdate( ( { material } ) => material.ringSizeVariance );
+uniforms.ringVarianceScale = TSL.uniform( params.ringVarianceScale ).onObjectUpdate( ( { material } ) => material.ringVarianceScale );
+uniforms.barkThickness = TSL.uniform( params.barkThickness ).onObjectUpdate( ( { material } ) => material.barkThickness );
+uniforms.splotchScale = TSL.uniform( params.splotchScale ).onObjectUpdate( ( { material } ) => material.splotchScale );
+uniforms.splotchIntensity = TSL.uniform( params.splotchIntensity ).onObjectUpdate( ( { material } ) => material.splotchIntensity );
+uniforms.cellScale = TSL.uniform( params.cellScale ).onObjectUpdate( ( { material } ) => material.cellScale );
+uniforms.cellSize = TSL.uniform( params.cellSize ).onObjectUpdate( ( { material } ) => material.cellSize );
+uniforms.darkGrainColor = TSL.uniform( new THREE.Color( params.darkGrainColor ) ).onObjectUpdate( ( { material }, self ) => self.value.set( material.darkGrainColor ) );
+uniforms.lightGrainColor = TSL.uniform( new THREE.Color( params.lightGrainColor ) ).onObjectUpdate( ( { material }, self ) => self.value.set( material.lightGrainColor ) );
+uniforms.transformationMatrix = TSL.uniform( new THREE.Matrix4().copy( params.transformationMatrix ) ).onObjectUpdate( ( { material } ) => material.transformationMatrix );
+
+const colorNode = wood(
+	uniforms.transformationMatrix.mul( TSL.vec4(TSL.positionLocal, 1) ).xyz,
+	uniforms.centerSize,
+	uniforms.largeWarpScale,
+	uniforms.largeGrainStretch,
+	uniforms.smallWarpStrength,
+	uniforms.smallWarpScale,
+	uniforms.fineWarpStrength,
+	uniforms.fineWarpScale,
+	uniforms.ringThickness,
+	uniforms.ringBias,
+	uniforms.ringSizeVariance,
+	uniforms.ringVarianceScale,
+	uniforms.barkThickness,
+	uniforms.splotchScale,
+	uniforms.splotchIntensity,
+	uniforms.cellScale,
+	uniforms.cellSize,
+	uniforms.darkGrainColor,
+	uniforms.lightGrainColor
+).mul( params.clearcoatDarken );
+
+/**
+ * Procedural wood material using TSL (Three.js Shading Language).
+ *
+ * Usage examples:
+ *
+ * // Using presets (recommended for common wood types)
+ * const material = WoodNodeMaterial.fromPreset('walnut', 'gloss');
+ *
+ * // Using custom parameters (for advanced customization)
+ * const material = new WoodNodeMaterial({
+ *   centerSize: 1.2,
+ *   ringThickness: 1/40,
+ *   darkGrainColor: new THREE.Color('#2a1a0a'),
+ *   lightGrainColor: new THREE.Color('#8b4513'),
+ *   clearcoat: 1,
+ *   clearcoatRoughness: 0.3
+ * });
+ *
+ * // Mixing presets with custom overrides
+ * const walnutParams = GetWoodPreset('walnut', 'raw');
+ * const material = new WoodNodeMaterial({
+ *   ...walnutParams,
+ *   ringThickness: 1/50,  // Override specific parameter
+ *   clearcoat: 1    // Add finish
+ * });
+ */
+export class WoodNodeMaterial extends THREE.MeshPhysicalMaterial {
+
+	static get type() {
+
+		return 'WoodNodeMaterial';
+
+	}
+
+	constructor( params = {} ) {
+
+		super();
+
+		this.isWoodNodeMaterial = true;
+
+		// Get default parameters from teak/raw preset
+		const defaultParams = GetWoodPreset( 'teak', 'raw' );
+
+		// Merge default params with provided params
+		const finalParams = { ...defaultParams, ...params };
+
+		for ( const key in finalParams ) {
+
+			if ( key === 'genus' || key === 'finish' ) continue;
+
+			if ( typeof finalParams[ key ] === 'string' ) {
+
+				this[ key ] = new THREE.Color( finalParams[ key ] );
+
+			} else {
+
+				this[ key ] = finalParams[ key ];
+
+			}
+
+		}
+
+		this.colorNode = colorNode;
+		this.clearcoatNode = finalParams.clearcoat;
+		this.clearcoatRoughness = finalParams.clearcoatRoughness;
+
+	}
+
+	// Static method to create material from preset
+	static fromPreset( genus = 'teak', finish = 'raw' ) {
+
+		const params = GetWoodPreset( genus, finish );
+		return new WoodNodeMaterial( params );
+
+	}
+
+}

+ 19 - 1
examples/jsm/math/ColorSpaces.js

@@ -1,4 +1,4 @@
-import { LinearTransfer, Matrix3, SRGBTransfer } from 'three';
+import { LinearTransfer, Matrix3, SRGBTransfer, SRGBColorSpace, ColorManagement } from 'three';
 
 
 /** @module ColorSpaces */
 /** @module ColorSpaces */
 
 
@@ -114,6 +114,24 @@ export const LinearRec2020ColorSpaceImpl = {
 	luminanceCoefficients: REC2020_LUMINANCE_COEFFICIENTS,
 	luminanceCoefficients: REC2020_LUMINANCE_COEFFICIENTS,
 };
 };
 
 
+/**
+ * Extended-sRGB color space.
+ *
+ * @type {string}
+ * @constant
+ */
+export const ExtendedSRGBColorSpace = 'extended-srgb';
+
+/**
+ * Implementation object for the Extended-sRGB color space.
+ *
+ * @type {module:ColorSpaces~ColorSpaceImpl}
+ * @constant
+ */
+export const ExtendedSRGBColorSpaceImpl = {
+	...ColorManagement.spaces[ SRGBColorSpace ],
+	outputColorSpaceConfig: { drawingBufferColorSpace: SRGBColorSpace, toneMappingMode: 'extended' }
+};
 
 
 /**
 /**
  * An object holding the color space implementation.
  * An object holding the color space implementation.

+ 2 - 2
examples/jsm/math/ConvexHull.js

@@ -152,7 +152,7 @@ class ConvexHull {
 	 *
 	 *
 	 * @param {Ray} ray - The ray to test.
 	 * @param {Ray} ray - The ray to test.
 	 * @param {Vector3} target - The target vector that is used to store the method's result.
 	 * @param {Vector3} target - The target vector that is used to store the method's result.
-	 * @return {Vector3|null} The intersection point. Returns `null` if not intersection was detected.
+	 * @return {?Vector3} The intersection point. Returns `null` if not intersection was detected.
 	 */
 	 */
 	intersectRay( ray, target ) {
 	intersectRay( ray, target ) {
 
 
@@ -1298,7 +1298,7 @@ class HalfEdge {
 	 * Returns the origin vertex.
 	 * Returns the origin vertex.
 	 *
 	 *
 	 * @private
 	 * @private
-	 * @return {VertexNode} The destination vertex.
+	 * @return {?VertexNode} The destination vertex.
 	 */
 	 */
 	tail() {
 	tail() {
 
 

+ 2 - 2
examples/jsm/math/Lut.js

@@ -45,7 +45,7 @@ class Lut {
 		/**
 		/**
 		 * The currently selected color map.
 		 * The currently selected color map.
 		 *
 		 *
-		 * @type {Array}
+		 * @type {Array<Array<number>>}
 		 */
 		 */
 		this.map = [];
 		this.map = [];
 
 
@@ -219,7 +219,7 @@ class Lut {
 	 * Adds a color map to this Lut instance.
 	 * Adds a color map to this Lut instance.
 	 *
 	 *
 	 * @param {string} name - The name of the color map.
 	 * @param {string} name - The name of the color map.
-	 * @param {Array} arrayOfColors - An array of color values. Each value is an array
+	 * @param {Array<Array<number>>} arrayOfColors - An array of color values. Each value is an array
 	 * holding a threshold and the actual color value as a hexadecimal number.
 	 * holding a threshold and the actual color value as a hexadecimal number.
 	 * @return {Lut} A reference to this LUT.
 	 * @return {Lut} A reference to this LUT.
 	 */
 	 */

+ 1 - 1
examples/jsm/misc/MD2CharacterComplex.js

@@ -121,7 +121,7 @@ class MD2CharacterComplex {
 		/**
 		/**
 		 * The movement controls.
 		 * The movement controls.
 		 *
 		 *
-		 * @type {Object}
+		 * @type {?Object}
 		 * @default null
 		 * @default null
 		 */
 		 */
 		this.controls = null;
 		this.controls = null;

+ 1 - 1
examples/jsm/misc/ProgressiveLightMap.js

@@ -280,7 +280,7 @@ class ProgressiveLightMap {
 	 *
 	 *
 	 * @private
 	 * @private
 	 * @param {number} res - The square resolution of this object's lightMap.
 	 * @param {number} res - The square resolution of this object's lightMap.
-	 * @param {WebGLRenderTarget} [lightMap] - The lightmap to initialize the plane with.
+	 * @param {?WebGLRenderTarget} [lightMap] - The lightmap to initialize the plane with.
 	 */
 	 */
 	_initializeBlurPlane( res, lightMap = null ) {
 	_initializeBlurPlane( res, lightMap = null ) {
 
 

+ 1 - 1
examples/jsm/misc/Volume.js

@@ -234,7 +234,7 @@ class Volume {
 		/**
 		/**
 		 * The list of all the slices associated to this volume
 		 * The list of all the slices associated to this volume
 		 *
 		 *
-		 * @type {Array}
+		 * @type {Array<VolumeSlice>}
 		 */
 		 */
 		this.sliceList = [];
 		this.sliceList = [];
 
 

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

@@ -510,7 +510,7 @@ class OutlinePass extends Pass {
 
 
 				}
 				}
 
 
-			} else if ( object.isMesh || object.isSprite) {
+			} else if ( object.isMesh || object.isSprite ) {
 
 
 				// only meshes and sprites are supported by OutlinePass
 				// only meshes and sprites are supported by OutlinePass
 
 

+ 37 - 8
examples/jsm/postprocessing/SSRPass.js

@@ -137,6 +137,8 @@ class SSRPass extends Pass {
 
 
 		this._selects = selects;
 		this._selects = selects;
 
 
+		this._resolutionScale = 1;
+
 		/**
 		/**
 		 * Whether the pass is selective or not.
 		 * Whether the pass is selective or not.
 		 *
 		 *
@@ -457,6 +459,29 @@ class SSRPass extends Pass {
 
 
 	}
 	}
 
 
+
+	/**
+	 * The resolution scale. Valid values are in the range
+	 * `[0,1]`. `1` means best quality but also results in
+	 * more computational overhead. Setting to `0.5` means
+	 * the effect is computed in half-resolution.
+	 *
+	 * @type {number}
+	 * @default 1
+	 */
+	get resolutionScale() {
+
+		return this._resolutionScale;
+
+	}
+
+	set resolutionScale( value ) {
+
+		this._resolutionScale = value;
+		this.setSize( this.width, this.height ); // force a resize when resolution scaling changes
+
+	}
+
 	/**
 	/**
 	 * Frees the GPU-related resources allocated by this instance. Call this
 	 * Frees the GPU-related resources allocated by this instance. Call this
 	 * method whenever the pass is no longer used in your app.
 	 * method whenever the pass is no longer used in your app.
@@ -661,23 +686,27 @@ class SSRPass extends Pass {
 		this.width = width;
 		this.width = width;
 		this.height = height;
 		this.height = height;
 
 
-		this.ssrMaterial.defines.MAX_STEP = Math.sqrt( width * width + height * height );
+		const effectiveWidth = Math.round( this.resolutionScale * width );
+		const effectiveHeight = Math.round( this.resolutionScale * height );
+
+		this.ssrMaterial.defines.MAX_STEP = Math.sqrt( effectiveWidth * effectiveWidth + effectiveHeight * effectiveHeight );
 		this.ssrMaterial.needsUpdate = true;
 		this.ssrMaterial.needsUpdate = true;
+
 		this.beautyRenderTarget.setSize( width, height );
 		this.beautyRenderTarget.setSize( width, height );
-		this.prevRenderTarget.setSize( width, height );
-		this.ssrRenderTarget.setSize( width, height );
 		this.normalRenderTarget.setSize( width, height );
 		this.normalRenderTarget.setSize( width, height );
 		this.metalnessRenderTarget.setSize( width, height );
 		this.metalnessRenderTarget.setSize( width, height );
-		this.blurRenderTarget.setSize( width, height );
-		this.blurRenderTarget2.setSize( width, height );
+		this.ssrRenderTarget.setSize( effectiveWidth, effectiveHeight );
+		this.prevRenderTarget.setSize( effectiveWidth, effectiveHeight );
+		this.blurRenderTarget.setSize( effectiveWidth, effectiveHeight );
+		this.blurRenderTarget2.setSize( effectiveWidth, effectiveHeight );
 		// this.blurRenderTarget3.setSize(width, height);
 		// this.blurRenderTarget3.setSize(width, height);
 
 
-		this.ssrMaterial.uniforms[ 'resolution' ].value.set( width, height );
+		this.ssrMaterial.uniforms[ 'resolution' ].value.set( effectiveWidth, effectiveHeight );
 		this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
 		this.ssrMaterial.uniforms[ 'cameraProjectionMatrix' ].value.copy( this.camera.projectionMatrix );
 		this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
 		this.ssrMaterial.uniforms[ 'cameraInverseProjectionMatrix' ].value.copy( this.camera.projectionMatrixInverse );
 
 
-		this.blurMaterial.uniforms[ 'resolution' ].value.set( width, height );
-		this.blurMaterial2.uniforms[ 'resolution' ].value.set( width, height );
+		this.blurMaterial.uniforms[ 'resolution' ].value.set( effectiveWidth, effectiveHeight );
+		this.blurMaterial2.uniforms[ 'resolution' ].value.set( effectiveWidth, effectiveHeight );
 
 
 	}
 	}
 
 

+ 1 - 1
examples/jsm/shaders/UnpackDepthRGBAShader.js

@@ -45,7 +45,7 @@ const UnpackDepthRGBAShader = {
 
 
 			float depth = unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
 			float depth = unpackRGBAToDepth( texture2D( tDiffuse, vUv ) );
 
 
-			#ifdef USE_REVERSEDEPTHBUF
+			#ifdef USE_REVERSED_DEPTH_BUFFER
 
 
 				gl_FragColor = vec4( vec3( depth ), opacity );
 				gl_FragColor = vec4( vec3( depth ), opacity );
 
 

+ 22 - 19
examples/jsm/transpiler/GLSLDecoder.js

@@ -11,22 +11,21 @@ const arithmeticOperators = [
 ];
 ];
 
 
 const precedenceOperators = [
 const precedenceOperators = [
-	'/', '*', '%',
-	'-', '+',
-	'<<', '>>',
-	'<', '>', '<=', '>=',
-	'==', '!=',
-	'&',
-	'^',
-	'|',
-	'&&',
-	'^^',
-	'||',
-	'?',
-	'=',
-	'+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=',
-	','
-].reverse();
+	[ ',' ],
+	[ '=', '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=' ],
+	[ '?' ],
+	[ '||' ],
+	[ '^^' ],
+	[ '&&' ],
+	[ '|' ],
+	[ '^' ],
+	[ '&' ],
+	[ '==', '!=' ],
+	[ '<', '>', '<=', '>=' ],
+	[ '<<', '>>' ],
+	[ '+', '-' ],
+	[ '*', '/', '%' ]
+];
 
 
 const associativityRightToLeft = [
 const associativityRightToLeft = [
 	'=',
 	'=',
@@ -334,7 +333,7 @@ class GLSLDecoder {
 
 
 		let groupIndex = 0;
 		let groupIndex = 0;
 
 
-		for ( const operator of precedenceOperators ) {
+		for ( const operators of precedenceOperators ) {
 
 
 			const parseToken = ( i, inverse = false ) => {
 			const parseToken = ( i, inverse = false ) => {
 
 
@@ -351,7 +350,9 @@ class GLSLDecoder {
 
 
 				}
 				}
 
 
-				if ( groupIndex === 0 && token.str === operator ) {
+				if ( groupIndex === 0 && operators.includes( token.str ) ) {
+
+					const operator = token.str;
 
 
 					if ( operator === '?' ) {
 					if ( operator === '?' ) {
 
 
@@ -396,7 +397,9 @@ class GLSLDecoder {
 
 
 			};
 			};
 
 
-			if ( associativityRightToLeft.includes( operator ) ) {
+			const isRightAssociative = operators.some( op => associativityRightToLeft.includes( op ) );
+
+			if ( isRightAssociative ) {
 
 
 				for ( let i = 0; i < tokens.length; i ++ ) {
 				for ( let i = 0; i < tokens.length; i ++ ) {
 
 

+ 2 - 10
examples/jsm/transpiler/TSLEncoder.js

@@ -340,7 +340,7 @@ class TSLEncoder {
 
 
 			if ( node.hasAssignment ) {
 			if ( node.hasAssignment ) {
 
 
-				if ( node.after === false ) {
+				if ( node.after === false && ( node.type === '++' || node.type === '--' ) ) {
 
 
 					type += 'Before';
 					type += 'Before';
 
 
@@ -665,15 +665,7 @@ ${ this.tab }} )`;
 
 
 			}
 			}
 
 
-			if ( node.linker.assignments.length > 0 ) {
-
-				varStr += ' = ' + valueStr + '.toVar()';
-
-			} else {
-
-				varStr += ' = ' + valueStr;
-
-			}
+			varStr += ' = ' + valueStr;
 
 
 		} else {
 		} else {
 
 

+ 24 - 0
examples/jsm/transpiler/WGSLEncoder.js

@@ -80,6 +80,10 @@ const wgslLib = {
 	'textureLod': 'textureSampleLevel',
 	'textureLod': 'textureSampleLevel',
 	'texelFetch': 'textureLoad',
 	'texelFetch': 'textureLoad',
 	'textureGrad': 'textureSampleGrad',
 	'textureGrad': 'textureSampleGrad',
+	'floatBitsToInt': 'bitcast<i32>',
+	'floatBitsToUint': 'bitcast<u32>',
+	'intBitsToFloat': 'bitcast<f32>',
+	'uintBitsToFloat': 'bitcast<f32>',
 };
 };
 
 
 class WGSLEncoder {
 class WGSLEncoder {
@@ -170,6 +174,26 @@ class WGSLEncoder {
 
 
 				code = `${ modFnName }( ${ snippets.join( ', ' ) } )`;
 				code = `${ modFnName }( ${ snippets.join( ', ' ) } )`;
 
 
+			} else if ( fnName.startsWith( 'bitcast' ) ) {
+
+				const params = node.params.map( p => this.emitExpression( p ) ).join( ',' );
+				const types = node.params.map( p => p.getType() );
+
+				if ( /.*vec[234]/.test( types[ 0 ] ) ) {
+
+					const conversionType = fnName.substring( 8, fnName.length - 1 );
+					const vectorType = types[ 0 ].substring( - 1 );
+
+					code = `bitcast<${ vectorType }<${ conversionType }>>`;
+
+				} else {
+
+					code = fnName;
+
+				}
+
+				code += `( ${ params } )`;
+
 			} else if ( fnName.startsWith( 'texture' ) ) {
 			} else if ( fnName.startsWith( 'texture' ) ) {
 
 
 				// Handle texture functions separately due to sampler handling
 				// Handle texture functions separately due to sampler handling

+ 27 - 4
examples/jsm/tsl/display/AnamorphicNode.js

@@ -69,9 +69,9 @@ class AnamorphicNode extends TempNode {
 		/**
 		/**
 		 * The resolution scale.
 		 * The resolution scale.
 		 *
 		 *
-		 * @type {Vector2}
+		 * @type {float}
 		 */
 		 */
-		this.resolution = new Vector2( 1, 1 );
+		this.resolutionScale = 1;
 
 
 		/**
 		/**
 		 * The internal render target of the effect.
 		 * The internal render target of the effect.
@@ -130,8 +130,8 @@ class AnamorphicNode extends TempNode {
 
 
 		this._invSize.value.set( 1 / width, 1 / height );
 		this._invSize.value.set( 1 / width, 1 / height );
 
 
-		width = Math.max( Math.round( width * this.resolution.x ), 1 );
-		height = Math.max( Math.round( height * this.resolution.y ), 1 );
+		width = Math.max( Math.round( width * this.resolutionScale ), 1 );
+		height = Math.max( Math.round( height * this.resolutionScale ), 1 );
 
 
 		this._renderTarget.setSize( width, height );
 		this._renderTarget.setSize( width, height );
 
 
@@ -240,6 +240,29 @@ class AnamorphicNode extends TempNode {
 
 
 	}
 	}
 
 
+	/**
+	 * The resolution scale.
+	 *
+	 * @deprecated
+	 * @type {Vector2}
+	 * @default {(1,1)}
+	 */
+	get resolution() {
+
+		console.warn( 'THREE.AnamorphicNode: The "resolution" property has been renamed to "resolutionScale" and is now of type `number`.' ); // @deprecated r180
+
+		return new Vector2( this.resolutionScale, this.resolutionScale );
+
+	}
+
+	set resolution( value ) {
+
+		console.warn( 'THREE.AnamorphicNode: The "resolution" property has been renamed to "resolutionScale" and is now of type `number`.' ); // @deprecated r180
+
+		this.resolutionScale = value.x;
+
+	}
+
 }
 }
 
 
 /**
 /**

+ 3 - 3
examples/jsm/tsl/display/BloomNode.js

@@ -364,9 +364,9 @@ class BloomNode extends TempNode {
 
 
 		// gaussian blur materials
 		// gaussian blur materials
 
 
-		// These sizes have been changed to account for the altered coefficents-calculation to avoid blockiness,
+		// These sizes have been changed to account for the altered coefficients-calculation to avoid blockiness,
 		// while retaining the same blur-strength. For details see https://github.com/mrdoob/three.js/pull/31528
 		// while retaining the same blur-strength. For details see https://github.com/mrdoob/three.js/pull/31528
-		const kernelSizeArray = [ 6, 10, 14, 18, 22 ]; 
+		const kernelSizeArray = [ 6, 10, 14, 18, 22 ];
 
 
 		for ( let i = 0; i < this._nMips; i ++ ) {
 		for ( let i = 0; i < this._nMips; i ++ ) {
 
 
@@ -480,7 +480,7 @@ class BloomNode extends TempNode {
 				const uvOffset = direction.mul( invSize ).mul( x );
 				const uvOffset = direction.mul( invSize ).mul( x );
 				const sample1 = sampleTexel( uvNode.add( uvOffset ) ).rgb;
 				const sample1 = sampleTexel( uvNode.add( uvOffset ) ).rgb;
 				const sample2 = sampleTexel( uvNode.sub( uvOffset ) ).rgb;
 				const sample2 = sampleTexel( uvNode.sub( uvOffset ) ).rgb;
-				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );		
+				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );
 
 
 			} );
 			} );
 
 

+ 2 - 1
examples/jsm/tsl/display/ChromaticAberrationNode.js

@@ -189,7 +189,7 @@ export default ChromaticAberrationNode;
  * @function
  * @function
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node|number} [strength=1.0] - The strength of the chromatic aberration effect as a node or value.
  * @param {Node|number} [strength=1.0] - The strength of the chromatic aberration effect as a node or value.
- * @param {Node|Vector2} [center=null] - The center point of the effect as a node or value. If null, uses screen center (0.5, 0.5).
+ * @param {?(Node|Vector2)} [center=null] - The center point of the effect as a node or value. If null, uses screen center (0.5, 0.5).
  * @param {Node|number} [scale=1.1] - The scale factor for stepped scaling from center as a node or value.
  * @param {Node|number} [scale=1.1] - The scale factor for stepped scaling from center as a node or value.
  * @returns {ChromaticAberrationNode}
  * @returns {ChromaticAberrationNode}
  */
  */
@@ -203,4 +203,5 @@ export const chromaticAberration = ( node, strength = 1.0, center = null, scale
 			nodeObject( scale )
 			nodeObject( scale )
 		)
 		)
 	);
 	);
+
 };
 };

+ 439 - 90
examples/jsm/tsl/display/DepthOfFieldNode.js

@@ -1,9 +1,17 @@
-import { TempNode, NodeUpdateType } from 'three/webgpu';
-import { convertToTexture, nodeObject, Fn, uv, uniform, vec2, vec4, clamp } from 'three/tsl';
+import { TempNode, NodeMaterial, NodeUpdateType, RenderTarget, Vector2, HalfFloatType, RedFormat, QuadMesh, RendererUtils } from 'three/webgpu';
+import { convertToTexture, nodeObject, Fn, uniform, smoothstep, step, texture, max, uniformArray, outputStruct, property, vec4, vec3, uv, Loop, min, mix } from 'three/tsl';
+import { gaussianBlur } from './GaussianBlurNode.js';
+
+const _quadMesh = /*@__PURE__*/ new QuadMesh();
+let _rendererState;
 
 
 /**
 /**
  * Post processing node for creating depth of field (DOF) effect.
  * Post processing node for creating depth of field (DOF) effect.
  *
  *
+ * References:
+ * - {@link https://pixelmischiefblog.wordpress.com/2016/11/25/bokeh-depth-of-field/}
+ * - {@link https://www.adriancourreges.com/blog/2016/09/09/doom-2016-graphics-study/}
+ *
  * @augments TempNode
  * @augments TempNode
  * @three_import import { dof } from 'three/addons/tsl/display/DepthOfFieldNode.js';
  * @three_import import { dof } from 'three/addons/tsl/display/DepthOfFieldNode.js';
  */
  */
@@ -20,11 +28,11 @@ class DepthOfFieldNode extends TempNode {
 	 *
 	 *
 	 * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
 	 * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
 	 * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
 	 * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
-	 * @param {Node<float>} focusNode - Defines the effect's focus which is the distance along the camera's look direction in world units.
-	 * @param {Node<float>} apertureNode - Defines the effect's aperture.
-	 * @param {Node<float>} maxblurNode - Defines the effect's maximum blur.
+	 * @param {Node<float>} focusDistanceNode - Defines the effect's focus which is the distance along the camera's look direction in world units.
+	 * @param {Node<float>} focalLengthNode - How far an object can be from the focal plane before it goes completely out-of-focus in world units.
+	 * @param {Node<float>} bokehScaleNode - A unitless value for artistic purposes to adjust the size of the bokeh.
 	 */
 	 */
-	constructor( textureNode, viewZNode, focusNode, apertureNode, maxblurNode ) {
+	constructor( textureNode, viewZNode, focusDistanceNode, focalLengthNode, bokehScaleNode ) {
 
 
 		super( 'vec4' );
 		super( 'vec4' );
 
 
@@ -47,29 +55,164 @@ class DepthOfFieldNode extends TempNode {
 		 *
 		 *
 		 * @type {Node<float>}
 		 * @type {Node<float>}
 		 */
 		 */
-		this.focusNode = focusNode;
+		this.focusDistanceNode = focusDistanceNode;
 
 
 		/**
 		/**
-		 * Defines the effect's aperture.
+		 * How far an object can be from the focal plane before it goes completely out-of-focus in world units.
 		 *
 		 *
 		 * @type {Node<float>}
 		 * @type {Node<float>}
 		 */
 		 */
-		this.apertureNode = apertureNode;
+		this.focalLengthNode = focalLengthNode;
 
 
 		/**
 		/**
-		 * Defines the effect's maximum blur.
+		 * A unitless value for artistic purposes to adjust the size of the bokeh.
 		 *
 		 *
 		 * @type {Node<float>}
 		 * @type {Node<float>}
 		 */
 		 */
-		this.maxblurNode = maxblurNode;
+		this.bokehScaleNode = bokehScaleNode;
+
+		/**
+		 * The inverse size of the resolution.
+		 *
+		 * @private
+		 * @type {UniformNode<vec2>}
+		 */
+		this._invSize = uniform( new Vector2() );
+
+		/**
+		 * The render target used for the near and far field.
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._CoCRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, format: RedFormat, count: 2 } );
+		this._CoCRT.textures[ 0 ].name = 'DepthOfField.NearField';
+		this._CoCRT.textures[ 1 ].name = 'DepthOfField.FarField';
+
+		/**
+		 * The render target used for blurring the near field.
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._CoCBlurredRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, format: RedFormat } );
+		this._CoCBlurredRT.texture.name = 'DepthOfField.NearFieldBlurred';
+
+		/**
+		 * The render target used for the first blur pass.
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._blur64RT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._blur64RT.texture.name = 'DepthOfField.Blur64';
+
+		/**
+		 * The render target used for the near field's second blur pass.
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._blur16NearRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._blur16NearRT.texture.name = 'DepthOfField.Blur16Near';
+
+		/**
+		 * The render target used for the far field's second blur pass.
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._blur16FarRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._blur16FarRT.texture.name = 'DepthOfField.Blur16Far';
+
+		/**
+		 * The render target used for the composite
+		 *
+		 * @private
+		 * @type {RenderTarget}
+		 */
+		this._compositeRT = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._compositeRT.texture.name = 'DepthOfField.Composite';
+
+		/**
+		 * The material used for the CoC/near and far fields.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._CoCMaterial = new NodeMaterial();
+
+		/**
+		 * The material used for blurring the near field.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._CoCBlurredMaterial = new NodeMaterial();
+
+		/**
+		 * The material used for the 64 tap blur.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._blur64Material = new NodeMaterial();
+
+		/**
+		 * The material used for the 16 tap blur.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._blur16Material = new NodeMaterial();
+
+		/**
+		 * The material used for the final composite.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._compositeMaterial = new NodeMaterial();
+
+		/**
+		 * The result of the effect is represented as a separate texture node.
+		 *
+		 * @private
+		 * @type {TextureNode}
+		 */
+		this._textureNode = texture( this._compositeRT.texture );
+
+		/**
+		 * The result of the CoC pass as a texture node.
+		 *
+		 * @private
+		 * @type {TextureNode}
+		 */
+		this._CoCTextureNode = texture( this._CoCRT.texture );
+
+		/**
+		 * The result of the blur64 pass as a texture node.
+		 *
+		 * @private
+		 * @type {TextureNode}
+		 */
+		this._blur64TextureNode = texture( this._blur64RT.texture );
 
 
 		/**
 		/**
-		 * Represents the input's aspect ratio.
+		 * The result of the near field's blur16 pass as a texture node.
 		 *
 		 *
 		 * @private
 		 * @private
-		 * @type {UniformNode<float>}
+		 * @type {TextureNode}
 		 */
 		 */
-		this._aspect = uniform( 0 );
+		this._blur16NearTextureNode = texture( this._blur16NearRT.texture );
+
+		/**
+		 * The result of the far field's blur16 pass as a texture node.
+		 *
+		 * @private
+		 * @type {TextureNode}
+		 */
+		this._blur16FarTextureNode = texture( this._blur16FarRT.texture );
 
 
 		/**
 		/**
 		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates
 		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node updates
@@ -82,16 +225,114 @@ class DepthOfFieldNode extends TempNode {
 
 
 	}
 	}
 
 
+	/**
+	 * Sets the size of the effect.
+	 *
+	 * @param {number} width - The width of the effect.
+	 * @param {number} height - The height of the effect.
+	 */
+	setSize( width, height ) {
+
+		this._invSize.value.set( 1 / width, 1 / height );
+
+		this._CoCRT.setSize( width, height );
+		this._compositeRT.setSize( width, height );
+
+		// blur runs in half resolution
+
+		const halfResX = Math.round( width / 2 );
+		const halfResY = Math.round( height / 2 );
+
+		this._CoCBlurredRT.setSize( halfResX, halfResY );
+		this._blur64RT.setSize( halfResX, halfResY );
+		this._blur16NearRT.setSize( halfResX, halfResY );
+		this._blur16FarRT.setSize( halfResX, halfResY );
+
+	}
+
+	/**
+	 * 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;
+
+	}
+
 	/**
 	/**
 	 * This method is used to update the effect's uniforms once per frame.
 	 * This method is used to update the effect's uniforms once per frame.
 	 *
 	 *
 	 * @param {NodeFrame} frame - The current node frame.
 	 * @param {NodeFrame} frame - The current node frame.
 	 */
 	 */
-	updateBefore() {
+	updateBefore( frame ) {
+
+		const { renderer } = frame;
+
+		// resize
 
 
 		const map = this.textureNode.value;
 		const map = this.textureNode.value;
+		this.setSize( map.image.width, map.image.height );
+
+		// save state
+
+		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
+
+		renderer.setClearColor( 0x000000, 0 );
+
+		// coc
+
+		_quadMesh.material = this._CoCMaterial;
+		renderer.setRenderTarget( this._CoCRT );
+		_quadMesh.render( renderer );
+
+		// blur near field to avoid visible aliased edges when the near field
+		// is blended with the background
+
+		this._CoCTextureNode.value = this._CoCRT.textures[ 0 ];
+
+		_quadMesh.material = this._CoCBlurredMaterial;
+		renderer.setRenderTarget( this._CoCBlurredRT );
+		_quadMesh.render( renderer );
+
+		// blur64 near
+
+		this._CoCTextureNode.value = this._CoCBlurredRT.texture;
+
+		_quadMesh.material = this._blur64Material;
+		renderer.setRenderTarget( this._blur64RT );
+		_quadMesh.render( renderer );
+
+		// blur16 near
+
+		_quadMesh.material = this._blur16Material;
+		renderer.setRenderTarget( this._blur16NearRT );
+		_quadMesh.render( renderer );
+
+		// blur64 far
+
+		this._CoCTextureNode.value = this._CoCRT.textures[ 1 ];
+
+		_quadMesh.material = this._blur64Material;
+		renderer.setRenderTarget( this._blur64RT );
+		_quadMesh.render( renderer );
+
+		// blur16 far
+
+		_quadMesh.material = this._blur16Material;
+		renderer.setRenderTarget( this._blur16FarRT );
+		_quadMesh.render( renderer );
 
 
-		this._aspect.value = map.image.width / map.image.height;
+		// composite
+
+		_quadMesh.material = this._compositeMaterial;
+		renderer.setRenderTarget( this._compositeRT );
+		_quadMesh.render( renderer );
+
+		// restore
+
+		RendererUtils.restoreRendererState( renderer, _rendererState );
 
 
 	}
 	}
 
 
@@ -101,81 +342,189 @@ class DepthOfFieldNode extends TempNode {
 	 * @param {NodeBuilder} builder - The current node builder.
 	 * @param {NodeBuilder} builder - The current node builder.
 	 * @return {ShaderCallNodeInternal}
 	 * @return {ShaderCallNodeInternal}
 	 */
 	 */
-	setup() {
-
-		const textureNode = this.textureNode;
-		const uvNode = textureNode.uvNode || uv();
-
-		const sampleTexture = ( uv ) => textureNode.sample( uv );
-
-		const dof = Fn( () => {
-
-			const aspectcorrect = vec2( 1.0, this._aspect );
-
-			const factor = this.focusNode.add( this.viewZNode );
-
-			const dofblur = vec2( clamp( factor.mul( this.apertureNode ), this.maxblurNode.negate(), this.maxblurNode ) );
-
-			const dofblur9 = dofblur.mul( 0.9 );
-			const dofblur7 = dofblur.mul( 0.7 );
-			const dofblur4 = dofblur.mul( 0.4 );
-
-			let col = vec4( 0.0 );
-
-			col = col.add( sampleTexture( uvNode ) );
-
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.15, 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.37, 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.37, - 0.15 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.15, - 0.37 ).mul( aspectcorrect ).mul( dofblur9 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.40, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur7 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, - 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.4, 0.0 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( - 0.29, - 0.29 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-			col = col.add( sampleTexture( uvNode.add( vec2( 0.0, 0.4 ).mul( aspectcorrect ).mul( dofblur4 ) ) ) );
-
-			col = col.div( 41 );
-			col.a = 1;
-
-			return vec4( col );
+	setup( builder ) {
+
+		const kernels = this._generateKernels();
+
+		// CoC, near and far fields
+
+		const nearField = property( 'float' );
+		const farField = property( 'float' );
+
+		const outputNode = outputStruct( nearField, farField );
 
 
+		const CoC = Fn( () => {
+
+			const signedDist = this.viewZNode.negate().sub( this.focusDistanceNode );
+			const CoC = smoothstep( 0, this.focalLengthNode, signedDist.abs() );
+
+			nearField.assign( step( signedDist, 0 ).mul( CoC ) );
+			farField.assign( step( 0, signedDist ).mul( CoC ) );
+
+			return vec4( 0 );
 
 
 		} );
 		} );
 
 
-		const outputNode = dof();
+		this._CoCMaterial.colorNode = CoC().context( builder.getSharedContext() );
+		this._CoCMaterial.outputNode = outputNode;
+		this._CoCMaterial.needsUpdate = true;
+
+		// blurred CoC for near field
+
+		this._CoCBlurredMaterial.colorNode = gaussianBlur( this._CoCTextureNode, 1, 2 );
+		this._CoCBlurredMaterial.needsUpdate = true;
+
+		// bokeh 64 blur pass
+
+		const bokeh64 = uniformArray( kernels.points64 );
+
+		const blur64 = Fn( () => {
+
+			const acc = vec3();
+			const uvNode = uv();
+
+			const CoC = this._CoCTextureNode.sample( uvNode ).r;
+			const sampleStep = this._invSize.mul( this.bokehScaleNode ).mul( CoC );
+
+			Loop( 64, ( { i } ) => {
+
+				const sUV = uvNode.add( sampleStep.mul( bokeh64.element( i ) ) );
+				const tap = this.textureNode.sample( sUV );
+
+				acc.addAssign( tap.rgb );
+
+			} );
+
+			acc.divAssign( 64 );
+
+			return vec4( acc, CoC );
+
+		} );
+
+		this._blur64Material.fragmentNode = blur64().context( builder.getSharedContext() );
+		this._blur64Material.needsUpdate = true;
+
+		// bokeh 16 blur pass
+
+		const bokeh16 = uniformArray( kernels.points16 );
+
+		const blur16 = Fn( () => {
+
+			const uvNode = uv();
+
+			const col = this._blur64TextureNode.sample( uvNode ).toVar();
+			const maxVal = col.rgb;
+			const CoC = col.a;
+			const sampleStep = this._invSize.mul( this.bokehScaleNode ).mul( CoC );
+
+			Loop( 16, ( { i } ) => {
+
+				const sUV = uvNode.add( sampleStep.mul( bokeh16.element( i ) ) );
+				const tap = this._blur64TextureNode.sample( sUV );
+
+				maxVal.assign( max( tap.rgb, maxVal ) );
+
+			} );
+
+			return vec4( maxVal, CoC );
+
+		} );
+
+		this._blur16Material.fragmentNode = blur16().context( builder.getSharedContext() );
+		this._blur16Material.needsUpdate = true;
+
+		// composite
+
+		const composite = Fn( () => {
+
+			const uvNode = uv();
+
+			const near = this._blur16NearTextureNode.sample( uvNode );
+			const far = this._blur16FarTextureNode.sample( uvNode );
+			const beauty = this.textureNode.sample( uvNode );
+
+			// TODO: applying the bokeh scale to the near field CoC value introduces blending
+			// issues around edges of blurred foreground objects when their are rendered above
+			// the background. for now, don't apply the bokeh scale to the blend factors. that
+			// will cause less blur for objects which are partly out-of-focus (CoC between 0 and 1).
+
+			const blendNear = min( near.a, 0.5 ).mul( 2 );
+			const blendFar = min( far.a, 0.5 ).mul( 2 );
+
+			const result = vec4( 0, 0, 0, 1 ).toVar();
+			result.rgb = mix( beauty.rgb, far.rgb, blendFar );
+			result.rgb = mix( result.rgb, near.rgb, blendNear );
+
+			return result;
+
+		} );
+
+		this._compositeMaterial.fragmentNode = composite().context( builder.getSharedContext() );
+		this._compositeMaterial.needsUpdate = true;
+
+		return this._textureNode;
+
+	}
+
+	_generateKernels() {
+
+		// Vogel's method, see https://www.shadertoy.com/view/4fBXRG
+		// this approach allows to generate uniformly distributed sample
+		// points in a disc-shaped pattern. Blurring with these samples
+		// produces a typical optical lens blur
+
+		const GOLDEN_ANGLE = 2.39996323;
+		const SAMPLES = 80;
+
+		const points64 = [];
+		const points16 = [];
+
+		let idx64 = 0;
+		let idx16 = 0;
+
+		for ( let i = 0; i < SAMPLES; i ++ ) {
+
+			const theta = i * GOLDEN_ANGLE;
+			const r = Math.sqrt( i ) / Math.sqrt( SAMPLES );
+
+			const p = new Vector2( r * Math.cos( theta ), r * Math.sin( theta ) );
+
+			if ( i % 5 === 0 ) {
+
+				points16[ idx16 ] = p;
+				idx16 ++;
+
+			} else {
+
+				points64[ idx64 ] = p;
+				idx64 ++;
+
+			}
+
+		}
+
+		return { points16, points64 };
+
+	}
+
+	/**
+	 * Frees internal resources. This method should be called
+	 * when the effect is no longer required.
+	 */
+	dispose() {
+
+		this._CoCRT.dispose();
+		this._CoCBlurredRT.dispose();
+		this._blur64RT.dispose();
+		this._blur16NearRT.dispose();
+		this._blur16FarRT.dispose();
+		this._compositeRT.dispose();
 
 
-		return outputNode;
+		this._CoCMaterial.dispose();
+		this._CoCBlurredMaterial.dispose();
+		this._blur64Material.dispose();
+		this._blur16Material.dispose();
+		this._compositeMaterial.dispose();
 
 
 	}
 	}
 
 
@@ -190,9 +539,9 @@ export default DepthOfFieldNode;
  * @function
  * @function
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
  * @param {Node<float>} viewZNode - Represents the viewZ depth values of the scene.
- * @param {Node<float> | number} focus - Defines the effect's focus which is the distance along the camera's look direction in world units.
- * @param {Node<float> | number} aperture - Defines the effect's aperture.
- * @param {Node<float> | number} maxblur - Defines the effect's maximum blur.
+ * @param {Node<float> | number} focusDistance - Defines the effect's focus which is the distance along the camera's look direction in world units.
+ * @param {Node<float> | number} focalLength - How far an object can be from the focal plane before it goes completely out-of-focus in world units.
+ * @param {Node<float> | number} bokehScale - A unitless value for artistic purposes to adjust the size of the bokeh.
  * @returns {DepthOfFieldNode}
  * @returns {DepthOfFieldNode}
  */
  */
-export const dof = ( node, viewZNode, focus = 1, aperture = 0.025, maxblur = 1 ) => nodeObject( new DepthOfFieldNode( convertToTexture( node ), nodeObject( viewZNode ), nodeObject( focus ), nodeObject( aperture ), nodeObject( maxblur ) ) );
+export const dof = ( node, viewZNode, focusDistance = 1, focalLength = 1, bokehScale = 1 ) => nodeObject( new DepthOfFieldNode( convertToTexture( node ), nodeObject( viewZNode ), nodeObject( focusDistance ), nodeObject( focalLength ), nodeObject( bokehScale ) ) );

+ 8 - 0
examples/jsm/tsl/display/GTAONode.js

@@ -323,6 +323,8 @@ class GTAONode extends TempNode {
 
 
 			const ao = float( 0 ).toVar();
 			const ao = float( 0 ).toVar();
 
 
+			// Each iteration analyzes one vertical "slice" of the 3D space around the fragment.
+
 			Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
 			Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
 
 
 				const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).toVar();
 				const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI ).toVar();
@@ -337,10 +339,14 @@ class GTAONode extends TempNode {
 				const tangentToNormalInSlice = cross( normalInSlice, sliceBitangent ).toVar();
 				const tangentToNormalInSlice = cross( normalInSlice, sliceBitangent ).toVar();
 				const cosHorizons = vec2( dot( viewDir, tangentToNormalInSlice ), dot( viewDir, tangentToNormalInSlice.negate() ) ).toVar();
 				const cosHorizons = vec2( dot( viewDir, tangentToNormalInSlice ), dot( viewDir, tangentToNormalInSlice.negate() ) ).toVar();
 
 
+				// For each slice, the inner loop performs ray marching to find the horizons.
+
 				Loop( { end: STEPS, type: 'int', name: 'j', condition: '<' }, ( { j } ) => {
 				Loop( { end: STEPS, type: 'int', name: 'j', condition: '<' }, ( { j } ) => {
 
 
 					const sampleViewOffset = sampleDir.xyz.mul( radiusToUse ).mul( sampleDir.w ).mul( pow( div( float( j ).add( 1.0 ), float( STEPS ) ), this.distanceExponent ) );
 					const sampleViewOffset = sampleDir.xyz.mul( radiusToUse ).mul( sampleDir.w ).mul( pow( div( float( j ).add( 1.0 ), float( STEPS ) ), this.distanceExponent ) );
 
 
+					// The loop marches in two opposite directions (x and y) along the slice's line to find the horizon on both sides.
+
 					// x
 					// x
 
 
 					const sampleScreenPositionX = getScreenPosition( viewPosition.add( sampleViewOffset ), this._cameraProjectionMatrix ).toVar();
 					const sampleScreenPositionX = getScreenPosition( viewPosition.add( sampleViewOffset ), this._cameraProjectionMatrix ).toVar();
@@ -371,6 +377,8 @@ class GTAONode extends TempNode {
 
 
 				} );
 				} );
 
 
+				// After the horizons are found for a given slice, their contribution to the total occlusion is calculated.
+
 				const sinHorizons = sqrt( sub( 1.0, cosHorizons.mul( cosHorizons ) ) ).toVar();
 				const sinHorizons = sqrt( sub( 1.0, cosHorizons.mul( cosHorizons ) ) ).toVar();
 				const nx = dot( normalInSlice, sliceTangent );
 				const nx = dot( normalInSlice, sliceTangent );
 				const ny = dot( normalInSlice, viewDir );
 				const ny = dot( normalInSlice, viewDir );

+ 47 - 35
examples/jsm/tsl/display/GaussianBlurNode.js

@@ -25,8 +25,11 @@ class GaussianBlurNode extends TempNode {
 	 * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
 	 * @param {TextureNode} textureNode - The texture node that represents the input of the effect.
 	 * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
 	 * @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.
 	 * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
+	 * @param {Object} [options={}] - Additional options for the gaussian blur effect.
+	 * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
+	 * @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
 	 */
 	 */
-	constructor( textureNode, directionNode = null, sigma = 4 ) {
+	constructor( textureNode, directionNode = null, sigma = 4, options = {} ) {
 
 
 		super( 'vec4' );
 		super( 'vec4' );
 
 
@@ -105,12 +108,12 @@ class GaussianBlurNode extends TempNode {
 		this.updateBeforeType = NodeUpdateType.FRAME;
 		this.updateBeforeType = NodeUpdateType.FRAME;
 
 
 		/**
 		/**
-		 * Controls the resolution of the effect.
+		 * The resolution scale.
 		 *
 		 *
-		 * @type {Vector2}
-		 * @default (1,1)
+		 * @type {float}
+		 * @default (1)
 		 */
 		 */
-		this.resolution = new Vector2( 1, 1 );
+		this.resolutionScale = options.resolutionScale || 1;
 
 
 		/**
 		/**
 		 * Whether the effect should use premultiplied alpha or not. Set this to `true`
 		 * Whether the effect should use premultiplied alpha or not. Set this to `true`
@@ -119,32 +122,7 @@ class GaussianBlurNode extends TempNode {
 		 * @type {boolean}
 		 * @type {boolean}
 		 * @default false
 		 * @default false
 		 */
 		 */
-		this.premultipliedAlpha = false;
-
-	}
-
-	/**
-	 * Sets the given premultiplied alpha value.
-	 *
-	 * @param {boolean} value - Whether the effect should use premultiplied alpha or not.
-	 * @return {GaussianBlurNode} height - A reference to this node.
-	 */
-	setPremultipliedAlpha( value ) {
-
-		this.premultipliedAlpha = value;
-
-		return this;
-
-	}
-
-	/**
-	 * Returns the premultiplied alpha value.
-	 *
-	 * @return {boolean} Whether the effect should use premultiplied alpha or not.
-	 */
-	getPremultipliedAlpha() {
-
-		return this.premultipliedAlpha;
+		this.premultipliedAlpha = options.premultipliedAlpha || false;
 
 
 	}
 	}
 
 
@@ -156,8 +134,8 @@ class GaussianBlurNode extends TempNode {
 	 */
 	 */
 	setSize( width, height ) {
 	setSize( width, height ) {
 
 
-		width = Math.max( Math.round( width * this.resolution.x ), 1 );
-		height = Math.max( Math.round( height * this.resolution.y ), 1 );
+		width = Math.max( Math.round( width * this.resolutionScale ), 1 );
+		height = Math.max( Math.round( height * this.resolutionScale ), 1 );
 
 
 		this._invSize.value.set( 1 / width, 1 / height );
 		this._invSize.value.set( 1 / width, 1 / height );
 		this._horizontalRT.setSize( width, height );
 		this._horizontalRT.setSize( width, height );
@@ -280,6 +258,7 @@ class GaussianBlurNode extends TempNode {
 				const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
 				const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
 
 
 				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
 				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
+
 			}
 			}
 
 
 			return output( diffuseSum );
 			return output( diffuseSum );
@@ -337,6 +316,29 @@ class GaussianBlurNode extends TempNode {
 
 
 	}
 	}
 
 
+	/**
+	 * The resolution scale.
+	 *
+	 * @deprecated
+	 * @type {Vector2}
+	 * @default {(1,1)}
+	 */
+	get resolution() {
+
+		console.warn( 'THREE.GaussianBlurNode: The "resolution" property has been renamed to "resolutionScale" and is now of type `number`.' ); // @deprecated r180
+
+		return new Vector2( this.resolutionScale, this.resolutionScale );
+
+	}
+
+	set resolution( value ) {
+
+		console.warn( 'THREE.GaussianBlurNode: The "resolution" property has been renamed to "resolutionScale" and is now of type `number`.' ); // @deprecated r180
+
+		this.resolutionScale = value.x;
+
+	}
+
 }
 }
 
 
 export default GaussianBlurNode;
 export default GaussianBlurNode;
@@ -349,18 +351,28 @@ export default GaussianBlurNode;
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
  * @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.
  * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
+ * @param {Object} [options={}] - Additional options for the gaussian blur effect.
+ * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
+ * @param {Vector2} [options.resolution=new Vector2(1, 1)] - The resolution of the effect. 0.5 means half the resolution of the texture node.
  * @returns {GaussianBlurNode}
  * @returns {GaussianBlurNode}
  */
  */
-export const gaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ) );
+export const gaussianBlur = ( node, directionNode, sigma, options = {} ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma, options ) );
 
 
 /**
 /**
  * TSL function for creating a gaussian blur node for post processing with enabled premultiplied alpha.
  * TSL function for creating a gaussian blur node for post processing with enabled premultiplied alpha.
  *
  *
  * @tsl
  * @tsl
  * @function
  * @function
+ * @deprecated  since r180. Use `gaussianBlur()` with `premultipliedAlpha: true` option instead.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec4>} node - The node that represents the input of the effect.
  * @param {Node<vec2|float>} directionNode - Defines the direction and radius of the blur.
  * @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.
  * @param {number} sigma - Controls the kernel of the blur filter. Higher values mean a wider blur radius.
  * @returns {GaussianBlurNode}
  * @returns {GaussianBlurNode}
  */
  */
-export const premultipliedGaussianBlur = ( node, directionNode, sigma ) => nodeObject( new GaussianBlurNode( convertToTexture( node ), directionNode, sigma ).setPremultipliedAlpha( true ) );
+export function premultipliedGaussianBlur( node, directionNode, sigma ) {
+
+	console.warn( 'THREE.TSL: "premultipliedGaussianBlur()" is deprecated. Use "gaussianBlur()" with "premultipliedAlpha: true" option instead.' ); // deprecated, r180
+
+	return gaussianBlur( node, directionNode, sigma, { premultipliedAlpha: true } );
+
+}

+ 2 - 2
examples/jsm/tsl/display/OutlineNode.js

@@ -56,7 +56,7 @@ class OutlineNode extends TempNode {
 	 * @param {Scene} scene - A reference to the scene.
 	 * @param {Scene} scene - A reference to the scene.
 	 * @param {Camera} camera - The camera the scene is rendered with.
 	 * @param {Camera} camera - The camera the scene is rendered with.
 	 * @param {Object} params - The configuration parameters.
 	 * @param {Object} params - The configuration parameters.
-	 * @param {Array<Object3D>} params.selectedObjects - An array of selected objects.
+	 * @param {Array<Object3D>} [params.selectedObjects] - An array of selected objects.
 	 * @param {Node<float>} [params.edgeThickness=float(1)] - The thickness of the edges.
 	 * @param {Node<float>} [params.edgeThickness=float(1)] - The thickness of the edges.
 	 * @param {Node<float>} [params.edgeGlow=float(0)] - Can be used for an animated glow/pulse effects.
 	 * @param {Node<float>} [params.edgeGlow=float(0)] - Can be used for an animated glow/pulse effects.
 	 * @param {number} [params.downSampleRatio=2] - The downsample ratio.
 	 * @param {number} [params.downSampleRatio=2] - The downsample ratio.
@@ -742,7 +742,7 @@ export default OutlineNode;
  * @param {Scene} scene - A reference to the scene.
  * @param {Scene} scene - A reference to the scene.
  * @param {Camera} camera - The camera the scene is rendered with.
  * @param {Camera} camera - The camera the scene is rendered with.
  * @param {Object} params - The configuration parameters.
  * @param {Object} params - The configuration parameters.
- * @param {Array<Object3D>} params.selectedObjects - An array of selected objects.
+ * @param {Array<Object3D>} [params.selectedObjects] - An array of selected objects.
  * @param {Node<float>} [params.edgeThickness=float(1)] - The thickness of the edges.
  * @param {Node<float>} [params.edgeThickness=float(1)] - The thickness of the edges.
  * @param {Node<float>} [params.edgeGlow=float(0)] - Can be used for animated glow/pulse effects.
  * @param {Node<float>} [params.edgeGlow=float(0)] - Can be used for animated glow/pulse effects.
  * @param {number} [params.downSampleRatio=2] - The downsample ratio.
  * @param {number} [params.downSampleRatio=2] - The downsample ratio.

+ 180 - 65
examples/jsm/tsl/display/SSRNode.js

@@ -1,5 +1,6 @@
-import { NearestFilter, RenderTarget, Vector2, RendererUtils, QuadMesh, TempNode, NodeMaterial, NodeUpdateType } from 'three/webgpu';
-import { reference, viewZToPerspectiveDepth, logarithmicDepthToViewZ, getScreenPosition, getViewPosition, sqrt, mul, div, cross, float, Continue, Break, Loop, int, max, abs, sub, If, dot, reflect, normalize, screenCoordinate, nodeObject, Fn, passTexture, uv, uniform, perspectiveDepthToViewZ, orthographicDepthToViewZ, vec2, vec3, vec4 } from 'three/tsl';
+import { HalfFloatType, RenderTarget, Vector2, RendererUtils, QuadMesh, TempNode, NodeMaterial, NodeUpdateType, LinearFilter, LinearMipmapLinearFilter } from 'three/webgpu';
+import { texture, reference, viewZToPerspectiveDepth, logarithmicDepthToViewZ, getScreenPosition, getViewPosition, sqrt, mul, div, cross, float, Continue, Break, Loop, int, max, abs, sub, If, dot, reflect, normalize, screenCoordinate, nodeObject, Fn, passTexture, uv, uniform, perspectiveDepthToViewZ, orthographicDepthToViewZ, vec2, vec3, vec4 } from 'three/tsl';
+import { boxBlur } from './boxBlur.js';
 
 
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 const _size = /*@__PURE__*/ new Vector2();
 const _size = /*@__PURE__*/ new Vector2();
@@ -28,9 +29,10 @@ class SSRNode extends TempNode {
 	 * @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
 	 * @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
 	 * @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
 	 * @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
 	 * @param {Node<float>} metalnessNode - A node that represents the beauty pass's metalness.
 	 * @param {Node<float>} metalnessNode - A node that represents the beauty pass's metalness.
-	 * @param {Camera} camera - The camera the scene is rendered with.
+	 * @param {?Node<float>} [roughnessNode=null] - A node that represents the beauty pass's roughness.
+	 * @param {?Camera} [camera=null] - The camera the scene is rendered with.
 	 */
 	 */
-	constructor( colorNode, depthNode, normalNode, metalnessNode, camera ) {
+	constructor( colorNode, depthNode, normalNode, metalnessNode, roughnessNode = null, camera = null ) {
 
 
 		super( 'vec4' );
 		super( 'vec4' );
 
 
@@ -63,22 +65,26 @@ class SSRNode extends TempNode {
 		this.metalnessNode = metalnessNode;
 		this.metalnessNode = metalnessNode;
 
 
 		/**
 		/**
-		 * The camera the scene is rendered with.
+		 * Whether the SSR reflections should be blurred or not. Blurring is a costly
+		 * operation so turn it off if you encounter performance issues on certain
+		 * devices.
 		 *
 		 *
-		 * @type {Camera}
+		 * @private
+		 * @type {Node<float>}
+		 * @default false
 		 */
 		 */
-		this.camera = camera;
+		this.roughnessNode = roughnessNode;
 
 
 		/**
 		/**
-		 * The resolution scale. By default SSR reflections
-		 * are computed in half resolutions. Setting the value
-		 * to `1` improves quality but also results in more
-		 * computational overhead.
+		 * The resolution scale. Valid values are in the range
+		 * `[0,1]`. `1` means best quality but also results in
+		 * more computational overhead. Setting to `0.5` means
+		 * the effect is computed in half-resolution.
 		 *
 		 *
 		 * @type {number}
 		 * @type {number}
-		 * @default 0.5
+		 * @default 1
 		 */
 		 */
-		this.resolutionScale = 0.5;
+		this.resolutionScale = 1;
 
 
 		/**
 		/**
 		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
 		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
@@ -90,17 +96,8 @@ class SSRNode extends TempNode {
 		this.updateBeforeType = NodeUpdateType.FRAME;
 		this.updateBeforeType = NodeUpdateType.FRAME;
 
 
 		/**
 		/**
-		 * The render target the SSR is rendered into.
-		 *
-		 * @private
-		 * @type {RenderTarget}
-		 */
-		this._ssrRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, minFilter: NearestFilter, magFilter: NearestFilter } );
-		this._ssrRenderTarget.texture.name = 'SSRNode.SSR';
-
-		/**
-		 * Controls how far a fragment can reflect.
-		 *
+		 * Controls how far a fragment can reflect. Increasing this value result in more
+		 * computational overhead but also increases the reflection distance.
 		 *
 		 *
 		 * @type {UniformNode<float>}
 		 * @type {UniformNode<float>}
 		 */
 		 */
@@ -114,12 +111,63 @@ class SSRNode extends TempNode {
 		this.thickness = uniform( 0.1 );
 		this.thickness = uniform( 0.1 );
 
 
 		/**
 		/**
-		 * Controls the transparency of the reflected colors.
+		 * Controls how the SSR reflections are blended with the beauty pass.
 		 *
 		 *
 		 * @type {UniformNode<float>}
 		 * @type {UniformNode<float>}
 		 */
 		 */
 		this.opacity = uniform( 1 );
 		this.opacity = uniform( 1 );
 
 
+		/**
+		 * This parameter controls how detailed the raymarching process works.
+		 * The value ranges is `[0,1]` where `1` means best quality (the maximum number
+		 * of raymarching iterations/samples) and `0` means no samples at all.
+		 *
+		 * A quality of `0.5` is usually sufficient for most use cases. Try to keep
+		 * this parameter as low as possible. Larger values result in noticeable more
+		 * overhead.
+		 *
+		 * @type {UniformNode<float>}
+		 */
+		this.quality = uniform( 0.5 );
+
+		/**
+		 * The quality of the blur. Must be an integer in the range `[1,3]`.
+		 *
+		 * @type {UniformNode<int>}
+		 */
+		this.blurQuality = uniform( 2 );
+
+		//
+
+		if ( camera === null ) {
+
+			if ( this.colorNode.passNode && this.colorNode.passNode.isPassNode === true ) {
+
+				camera = this.colorNode.passNode.camera;
+
+			} else {
+
+				throw new Error( 'THREE.TSL: No camera found. ssr() requires a camera.' );
+
+			}
+
+		}
+
+		/**
+		 * The camera the scene is rendered with.
+		 *
+		 * @type {Camera}
+		 */
+		this.camera = camera;
+
+		/**
+		 * The spread of the blur. Automatically set when generating mips.
+		 *
+		 * @private
+		 * @type {UniformNode<int>}
+		 */
+		this._blurSpread = uniform( 1 );
+
 		/**
 		/**
 		 * Represents the projection matrix of the scene's camera.
 		 * Represents the projection matrix of the scene's camera.
 		 *
 		 *
@@ -158,7 +206,7 @@ class SSRNode extends TempNode {
 		 * @private
 		 * @private
 		 * @type {UniformNode<bool>}
 		 * @type {UniformNode<bool>}
 		 */
 		 */
-		this._isPerspectiveCamera = uniform( camera.isPerspectiveCamera ? 1 : 0 );
+		this._isPerspectiveCamera = uniform( camera.isPerspectiveCamera );
 
 
 		/**
 		/**
 		 * The resolution of the pass.
 		 * The resolution of the pass.
@@ -169,13 +217,23 @@ class SSRNode extends TempNode {
 		this._resolution = uniform( new Vector2() );
 		this._resolution = uniform( new Vector2() );
 
 
 		/**
 		/**
-		 * This value is derived from the resolution and restricts
-		 * the maximum raymarching steps in the fragment shader.
+		 * The render target the SSR is rendered into.
 		 *
 		 *
 		 * @private
 		 * @private
-		 * @type {UniformNode<float>}
+		 * @type {RenderTarget}
+		 */
+		this._ssrRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType } );
+		this._ssrRenderTarget.texture.name = 'SSRNode.SSR';
+
+		/**
+		 * The render target for the blurred SSR reflections.
+		 *
+		 * @private
+		 * @type {RenderTarget}
 		 */
 		 */
-		this._maxStep = uniform( 0 );
+		this._blurRenderTarget = new RenderTarget( 1, 1, { depthBuffer: false, type: HalfFloatType, minFilter: LinearMipmapLinearFilter, magFilter: LinearFilter } );
+		this._blurRenderTarget.texture.name = 'SSRNode.Blur';
+		this._blurRenderTarget.texture.mipmaps.push( {}, {}, {}, {}, {} );
 
 
 		/**
 		/**
 		 * The material that is used to render the effect.
 		 * The material that is used to render the effect.
@@ -183,8 +241,26 @@ class SSRNode extends TempNode {
 		 * @private
 		 * @private
 		 * @type {NodeMaterial}
 		 * @type {NodeMaterial}
 		 */
 		 */
-		this._material = new NodeMaterial();
-		this._material.name = 'SSRNode.SSR';
+		this._ssrMaterial = new NodeMaterial();
+		this._ssrMaterial.name = 'SSRNode.SSR';
+
+		/**
+		 * The blur material.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._blurMaterial = new NodeMaterial();
+		this._blurMaterial.name = 'SSRNode.Blur';
+
+		/**
+		 * The copy material.
+		 *
+		 * @private
+		 * @type {NodeMaterial}
+		 */
+		this._copyMaterial = new NodeMaterial();
+		this._copyMaterial.name = 'SSRNode.Copy';
 
 
 		/**
 		/**
 		 * The result of the effect is represented as a separate texture node.
 		 * The result of the effect is represented as a separate texture node.
@@ -194,6 +270,25 @@ class SSRNode extends TempNode {
 		 */
 		 */
 		this._textureNode = passTexture( this, this._ssrRenderTarget.texture );
 		this._textureNode = passTexture( this, this._ssrRenderTarget.texture );
 
 
+		let blurredTextureNode = null;
+
+		if ( this.roughnessNode !== null ) {
+
+			const mips = this._blurRenderTarget.texture.mipmaps.length - 1;
+			const lod = float( this.roughnessNode ).mul( mips ).clamp( 0, mips );
+
+			blurredTextureNode = passTexture( this, this._blurRenderTarget.texture ).level( lod );
+
+		}
+
+		/**
+		 * Holds the blurred SSR reflections.
+		 *
+		 * @private
+		 * @type {?PassTextureNode}
+		 */
+		this._blurredTextureNode = blurredTextureNode;
+
 	}
 	}
 
 
 	/**
 	/**
@@ -203,7 +298,7 @@ class SSRNode extends TempNode {
 	 */
 	 */
 	getTextureNode() {
 	getTextureNode() {
 
 
-		return this._textureNode;
+		return this.roughnessNode !== null ? this._blurredTextureNode : this._textureNode;
 
 
 	}
 	}
 
 
@@ -219,9 +314,8 @@ class SSRNode extends TempNode {
 		height = Math.round( this.resolutionScale * height );
 		height = Math.round( this.resolutionScale * height );
 
 
 		this._resolution.value.set( width, height );
 		this._resolution.value.set( width, height );
-		this._maxStep.value = Math.round( Math.sqrt( width * width + height * height ) );
-
 		this._ssrRenderTarget.setSize( width, height );
 		this._ssrRenderTarget.setSize( width, height );
+		this._blurRenderTarget.setSize( width, height );
 
 
 	}
 	}
 
 
@@ -236,9 +330,12 @@ class SSRNode extends TempNode {
 
 
 		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
 		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
 
 
+		const ssrRenderTarget = this._ssrRenderTarget;
+		const blurRenderTarget = this._blurRenderTarget;
+
 		const size = renderer.getDrawingBufferSize( _size );
 		const size = renderer.getDrawingBufferSize( _size );
 
 
-		_quadMesh.material = this._material;
+		_quadMesh.material = this._ssrMaterial;
 
 
 		this.setSize( size.width, size.height );
 		this.setSize( size.width, size.height );
 
 
@@ -249,9 +346,27 @@ class SSRNode extends TempNode {
 
 
 		// ssr
 		// ssr
 
 
-		renderer.setRenderTarget( this._ssrRenderTarget );
+		renderer.setRenderTarget( ssrRenderTarget );
 		_quadMesh.render( renderer );
 		_quadMesh.render( renderer );
 
 
+		// blur (optional)
+
+		if ( this.roughnessNode !== null ) {
+
+			// blur mips but leave the base mip unblurred
+
+			for ( let i = 0; i < blurRenderTarget.texture.mipmaps.length; i ++ ) {
+
+				_quadMesh.material = ( i === 0 ) ? this._copyMaterial : this._blurMaterial;
+
+				this._blurSpread.value = i;
+				renderer.setRenderTarget( blurRenderTarget, 0, i );
+				_quadMesh.render( renderer );
+
+			}
+
+		}
+
 		// restore
 		// restore
 
 
 		RendererUtils.restoreRendererState( renderer, _rendererState );
 		RendererUtils.restoreRendererState( renderer, _rendererState );
@@ -326,7 +441,7 @@ class SSRNode extends TempNode {
 
 
 		const ssr = Fn( () => {
 		const ssr = Fn( () => {
 
 
-			const metalness = this.metalnessNode.sample( uvNode ).r;
+			const metalness = float( this.metalnessNode );
 
 
 			// fragments with no metalness do not reflect their environment
 			// fragments with no metalness do not reflect their environment
 			metalness.equal( 0.0 ).discard();
 			metalness.equal( 0.0 ).discard();
@@ -349,7 +464,7 @@ class SSRNode extends TempNode {
 			const d1viewPosition = viewPosition.add( viewReflectDir.mul( maxReflectRayLen ) ).toVar();
 			const d1viewPosition = viewPosition.add( viewReflectDir.mul( maxReflectRayLen ) ).toVar();
 
 
 			// check if d1viewPosition lies behind the camera near plane
 			// check if d1viewPosition lies behind the camera near plane
-			If( this._isPerspectiveCamera.equal( float( 1 ) ).and( d1viewPosition.z.greaterThan( this._cameraNear.negate() ) ), () => {
+			If( this._isPerspectiveCamera.and( d1viewPosition.z.greaterThan( this._cameraNear.negate() ) ), () => {
 
 
 				// if so, ensure d1viewPosition is clamped on the near plane.
 				// if so, ensure d1viewPosition is clamped on the near plane.
 				// this prevents artifacts during the ray marching process
 				// this prevents artifacts during the ray marching process
@@ -374,7 +489,7 @@ class SSRNode extends TempNode {
 			// determine the larger delta
 			// determine the larger delta
 			// The larger difference will help to determine how much to travel in the X and Y direction each iteration and
 			// The larger difference will help to determine how much to travel in the X and Y direction each iteration and
 			// how many iterations are needed to travel the entire ray
 			// how many iterations are needed to travel the entire ray
-			const totalStep = max( abs( xLen ), abs( yLen ) ).toVar();
+			const totalStep = int( max( abs( xLen ), abs( yLen ) ).mul( this.quality.clamp() ) ).toConst();
 
 
 			// step sizes in the x and y directions
 			// step sizes in the x and y directions
 			const xSpan = xLen.div( totalStep ).toVar();
 			const xSpan = xLen.div( totalStep ).toVar();
@@ -385,23 +500,9 @@ class SSRNode extends TempNode {
 			// the actual ray marching loop
 			// the actual ray marching loop
 			// starting from d0, the code gradually travels along the ray and looks for an intersection with the geometry.
 			// starting from d0, the code gradually travels along the ray and looks for an intersection with the geometry.
 			// it does not exceed d1 (the maximum ray extend)
 			// it does not exceed d1 (the maximum ray extend)
-			Loop( { start: int( 0 ), end: int( this._maxStep ), type: 'int', condition: '<' }, ( { i } ) => {
-
-				// TODO: Remove this when Chrome is fixed, see https://issues.chromium.org/issues/372714384#comment14
-				If( metalness.equal( 0 ), () => {
+			Loop( totalStep, ( { i } ) => {
 
 
-					Break();
-
-				} );
-
-				// stop if the maximum number of steps is reached for this specific ray
-				If( float( i ).greaterThanEqual( totalStep ), () => {
-
-					Break();
-
-				} );
-
-				// advance on the ray by computing a new position in screen space
+				// advance on the ray by computing a new position in screen coordinates
 				const xy = vec2( d0.x.add( xSpan.mul( float( i ) ) ), d0.y.add( ySpan.mul( float( i ) ) ) ).toVar();
 				const xy = vec2( d0.x.add( xSpan.mul( float( i ) ) ), d0.y.add( ySpan.mul( float( i ) ) ) ).toVar();
 
 
 				// stop processing if the new position lies outside of the screen
 				// stop processing if the new position lies outside of the screen
@@ -411,11 +512,10 @@ class SSRNode extends TempNode {
 
 
 				} );
 				} );
 
 
-				// compute new uv, depth, viewZ and viewPosition for the new location on the ray
+				// compute new uv, depth and viewZ for the next fragment
 				const uvNode = xy.div( this._resolution );
 				const uvNode = xy.div( this._resolution );
 				const d = sampleDepth( uvNode ).toVar();
 				const d = sampleDepth( uvNode ).toVar();
 				const vZ = getViewZ( d ).toVar();
 				const vZ = getViewZ( d ).toVar();
-				const vP = getViewPosition( uvNode, d, this._cameraProjectionMatrixInverse ).toVar();
 
 
 				const viewReflectRayZ = float( 0 ).toVar();
 				const viewReflectRayZ = float( 0 ).toVar();
 
 
@@ -423,7 +523,7 @@ class SSRNode extends TempNode {
 				const s = xy.sub( d0 ).length().div( totalLen );
 				const s = xy.sub( d0 ).length().div( totalLen );
 
 
 				// depending on the camera type, we now compute the z-coordinate of the reflected ray at the current step in view space
 				// depending on the camera type, we now compute the z-coordinate of the reflected ray at the current step in view space
-				If( this._isPerspectiveCamera.equal( float( 1 ) ), () => {
+				If( this._isPerspectiveCamera, () => {
 
 
 					const recipVPZ = float( 1 ).div( viewPosition.z ).toVar();
 					const recipVPZ = float( 1 ).div( viewPosition.z ).toVar();
 					viewReflectRayZ.assign( float( 1 ).div( recipVPZ.add( s.mul( float( 1 ).div( d1viewPosition.z ).sub( recipVPZ ) ) ) ) );
 					viewReflectRayZ.assign( float( 1 ).div( recipVPZ.add( s.mul( float( 1 ).div( d1viewPosition.z ).sub( recipVPZ ) ) ) ) );
@@ -439,6 +539,7 @@ class SSRNode extends TempNode {
 
 
 					// compute the distance of the new location to the ray in view space
 					// compute the distance of the new location to the ray in view space
 					// to clarify vP is the fragment's view position which is not an exact point on the ray
 					// to clarify vP is the fragment's view position which is not an exact point on the ray
+					const vP = getViewPosition( uvNode, d, this._cameraProjectionMatrixInverse ).toVar();
 					const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar();
 					const away = pointToLineDistance( vP, viewPosition, d1viewPosition ).toVar();
 
 
 					// compute the minimum thickness between the current fragment and its neighbor in the x-direction.
 					// compute the minimum thickness between the current fragment and its neighbor in the x-direction.
@@ -499,12 +600,22 @@ class SSRNode extends TempNode {
 
 
 		} );
 		} );
 
 
-		this._material.fragmentNode = ssr().context( builder.getSharedContext() );
-		this._material.needsUpdate = true;
+		this._ssrMaterial.fragmentNode = ssr().context( builder.getSharedContext() );
+		this._ssrMaterial.needsUpdate = true;
+
+		// below materials are used for blurring
+
+		const reflectionBuffer = texture( this._ssrRenderTarget.texture );
+
+		this._blurMaterial.fragmentNode = boxBlur( reflectionBuffer, { size: this.blurQuality, separation: this._blurSpread } );
+		this._blurMaterial.needsUpdate = true;
+
+		this._copyMaterial.fragmentNode = reflectionBuffer;
+		this._copyMaterial.needsUpdate = true;
 
 
 		//
 		//
 
 
-		return this._textureNode;
+		return this.getTextureNode();
 
 
 	}
 	}
 
 
@@ -515,8 +626,11 @@ class SSRNode extends TempNode {
 	dispose() {
 	dispose() {
 
 
 		this._ssrRenderTarget.dispose();
 		this._ssrRenderTarget.dispose();
+		this._blurRenderTarget.dispose();
 
 
-		this._material.dispose();
+		this._ssrMaterial.dispose();
+		this._blurMaterial.dispose();
+		this._copyMaterial.dispose();
 
 
 	}
 	}
 
 
@@ -533,7 +647,8 @@ export default SSRNode;
  * @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
  * @param {Node<float>} depthNode - A node that represents the beauty pass's depth.
  * @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
  * @param {Node<vec3>} normalNode - A node that represents the beauty pass's normals.
  * @param {Node<float>} metalnessNode - A node that represents the beauty pass's metalness.
  * @param {Node<float>} metalnessNode - A node that represents the beauty pass's metalness.
- * @param {Camera} camera - The camera the scene is rendered with.
+ * @param {?Node<float>} [roughnessNode=null] - A node that represents the beauty pass's roughness.
+ * @param {?Camera} [camera=null] - The camera the scene is rendered with.
  * @returns {SSRNode}
  * @returns {SSRNode}
  */
  */
-export const ssr = ( colorNode, depthNode, normalNode, metalnessNode, camera ) => nodeObject( new SSRNode( nodeObject( colorNode ), nodeObject( depthNode ), nodeObject( normalNode ), nodeObject( metalnessNode ), camera ) );
+export const ssr = ( colorNode, depthNode, normalNode, metalnessNode, roughnessNode = null, camera = null ) => nodeObject( new SSRNode( nodeObject( colorNode ), nodeObject( depthNode ), nodeObject( normalNode ), nodeObject( metalnessNode ), nodeObject( roughnessNode ), camera ) );

+ 1 - 1
examples/jsm/tsl/display/TRAANode.js

@@ -79,7 +79,7 @@ class TRAANode extends TempNode {
 		/**
 		/**
 		 *  The camera the scene is rendered with.
 		 *  The camera the scene is rendered with.
 		 *
 		 *
-		 * @type {TextureNode}
+		 * @type {Camera}
 		 */
 		 */
 		this.camera = camera;
 		this.camera = camera;
 
 

+ 64 - 0
examples/jsm/tsl/display/boxBlur.js

@@ -0,0 +1,64 @@
+import { Fn, vec2, uv, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, max, int, textureSize, nodeObject, convertToTexture } from 'three/tsl';
+
+/**
+ * Applies a box blur effect to the given texture node.
+ *
+ * Compared to Gaussian blur, box blur produces a more blocky result but with better performance when correctly
+ * configured. It is intended for mobile devices or performance restricted use cases where Gaussian is too heavy.
+ *
+ * The (kernel) `size` parameter should be small (1, 2 or 3) since it determines the number of samples based on (size * 2 + 1)^2.
+ * This implementation uses a single pass approach so the kernel is not applied as a separable filter. That means larger
+ * kernels won't perform well. Use Gaussian instead if you need a more high-quality blur.
+ *
+ * To produce wider blurs, increase the `separation` parameter instead which has no influence on the performance.
+ *
+ * Reference: {@link https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/box-blur.frag}.
+ *
+ * @function
+ * @param {Node<vec4>} textureNode - The texture node that should be blurred.
+ * @param {Object} [options={}] - Additional options for the hash blur effect.
+ * @param {Node<int>} [options.size=int(1)] - Controls the blur's kernel. For performant results, the range should within [1, 3].
+ * @param {Node<int>} [options.separation=int(1)] - Spreads out the blur without having to sample additional fragments. Ranges from [1, Infinity].
+ * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
+ * @return {Node<vec4>} The blurred texture node.
+ */
+export const boxBlur = /*#__PURE__*/ Fn( ( [ textureNode, options = {} ] ) => {
+
+	textureNode = convertToTexture( textureNode );
+
+	const size = nodeObject( options.size ) || int( 1 );
+	const separation = nodeObject( options.separation ) || int( 1 );
+	const premultipliedAlpha = options.premultipliedAlpha || false;
+
+	const tap = ( uv ) => {
+
+		const sample = textureNode.sample( uv );
+
+		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
+
+	};
+
+	const targetUV = textureNode.uvNode || uv();
+
+	const result = vec4( 0 );
+	const sep = max( separation, 1 );
+	const count = int( 0 );
+	const pixelStep = vec2( 1 ).div( textureSize( textureNode ) );
+
+	Loop( { start: size.negate(), end: size, name: 'i', condition: '<=' }, ( { i } ) => {
+
+		Loop( { start: size.negate(), end: size, name: 'j', condition: '<=' }, ( { j } ) => {
+
+			const uvs = targetUV.add( vec2( i, j ).mul( pixelStep ).mul( sep ) );
+			result.addAssign( tap( uvs ) );
+			count.addAssign( 1 );
+
+		} );
+
+	} );
+
+	result.divAssign( count );
+
+	return premultipliedAlpha ? unpremultiplyAlpha( result ) : result;
+
+} );

+ 15 - 18
examples/jsm/tsl/display/hashBlur.js

@@ -1,8 +1,15 @@
-import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha } from 'three/tsl';
+import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAlpha, unpremultiplyAlpha, convertToTexture, nodeObject } from 'three/tsl';
 
 
 /**
 /**
  * Applies a hash blur effect to the given texture node.
  * Applies a hash blur effect to the given texture node.
  *
  *
+ * The approach of this blur is different compared to Gaussian and box blur since
+ * it does not rely on a kernel to apply a convolution. Instead, it reads the base
+ * texture multiple times in a random pattern and then averages the samples. A
+ * typical artifact of this technique is a slightly noisy appearance of the blur which
+ * can be mitigated by increasing the number of iterations (see `repeats` parameter).
+ * Compared to Gaussian blur, hash blur requires just a single pass.
+ *
  * Reference: {@link https://www.shadertoy.com/view/4lXXWn}.
  * Reference: {@link https://www.shadertoy.com/view/4lXXWn}.
  *
  *
  * @function
  * @function
@@ -10,42 +17,32 @@ import { float, Fn, vec2, uv, sin, rand, degrees, cos, Loop, vec4, premultiplyAl
  * @param {Node<float>} [bluramount=float(0.1)] - This node determines the amount of blur.
  * @param {Node<float>} [bluramount=float(0.1)] - This node determines the amount of blur.
  * @param {Object} [options={}] - Additional options for the hash blur effect.
  * @param {Object} [options={}] - Additional options for the hash blur effect.
  * @param {Node<float>} [options.repeats=float(45)] - The number of iterations for the blur effect.
  * @param {Node<float>} [options.repeats=float(45)] - The number of iterations for the blur effect.
- * @param {Node<vec4>} [options.mask=null] - A mask node to control the alpha blending of the blur.
  * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
  * @param {boolean} [options.premultipliedAlpha=false] - Whether to use premultiplied alpha for the blur effect.
  * @return {Node<vec4>} The blurred texture node.
  * @return {Node<vec4>} The blurred texture node.
  */
  */
 export const hashBlur = /*#__PURE__*/ Fn( ( [ textureNode, bluramount = float( 0.1 ), options = {} ] ) => {
 export const hashBlur = /*#__PURE__*/ Fn( ( [ textureNode, bluramount = float( 0.1 ), options = {} ] ) => {
 
 
-	const {
-		repeats = float( 45 ),
-		mask = null,
-		premultipliedAlpha = false
-	} = options;
-
-	const draw = ( uv ) => {
-
-		let sample = textureNode.sample( uv );
-
-		if ( mask !== null ) {
+	textureNode = convertToTexture( textureNode );
 
 
-			const alpha = mask.sample( uv ).x;
+	const repeats = nodeObject( options.repeats ) || float( 45 );
+	const premultipliedAlpha = options.premultipliedAlpha || false;
 
 
-			sample = vec4( sample.rgb, sample.a.mul( alpha ) );
+	const tap = ( uv ) => {
 
 
-		}
+		const sample = textureNode.sample( uv );
 
 
 		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
 		return premultipliedAlpha ? premultiplyAlpha( sample ) : sample;
 
 
 	};
 	};
 
 
 	const targetUV = textureNode.uvNode || uv();
 	const targetUV = textureNode.uvNode || uv();
-	const blurred_image = vec4( 0. ).toVar();
+	const blurred_image = vec4( 0. );
 
 
 	Loop( { start: 0., end: repeats, type: 'float' }, ( { i } ) => {
 	Loop( { start: 0., end: repeats, type: 'float' }, ( { i } ) => {
 
 
 		const q = vec2( vec2( cos( degrees( i.div( repeats ).mul( 360. ) ) ), sin( degrees( i.div( repeats ).mul( 360. ) ) ) ).mul( rand( vec2( i, targetUV.x.add( targetUV.y ) ) ).add( bluramount ) ) );
 		const q = vec2( vec2( cos( degrees( i.div( repeats ).mul( 360. ) ) ), sin( degrees( i.div( repeats ).mul( 360. ) ) ) ).mul( rand( vec2( i, targetUV.x.add( targetUV.y ) ) ).add( bluramount ) ) );
 		const uv2 = vec2( targetUV.add( q.mul( bluramount ) ) );
 		const uv2 = vec2( targetUV.add( q.mul( bluramount ) ) );
-		blurred_image.addAssign( draw( uv2 ) );
+		blurred_image.addAssign( tap( uv2 ) );
 
 
 	} );
 	} );
 
 

+ 1 - 1
examples/jsm/utils/BufferGeometryUtils.js

@@ -446,7 +446,7 @@ function deepCloneAttribute( attribute ) {
  * single {@link InterleavedBuffer} instance. All attributes must have compatible types.
  * single {@link InterleavedBuffer} instance. All attributes must have compatible types.
  *
  *
  * @param {Array<BufferAttribute>} attributes - The attributes to interleave.
  * @param {Array<BufferAttribute>} attributes - The attributes to interleave.
- * @return {Array<InterleavedBufferAttribute>} An array of interleaved attributes. If interleave does not succeed, the method returns `null`.
+ * @return {?Array<InterleavedBufferAttribute>} An array of interleaved attributes. If interleave does not succeed, the method returns `null`.
  */
  */
 function interleaveAttributes( attributes ) {
 function interleaveAttributes( attributes ) {
 
 

+ 12 - 5
examples/jsm/utils/ShadowMapViewerGPU.js

@@ -7,9 +7,10 @@ import {
 	OrthographicCamera,
 	OrthographicCamera,
 	PlaneGeometry,
 	PlaneGeometry,
 	Scene,
 	Scene,
-	Texture
+	DepthTexture,
+	Vector2
 } from 'three';
 } from 'three';
-import { texture } from 'three/tsl';
+import { uv, uniform, textureLoad } from 'three/tsl';
 
 
 /**
 /**
  * This is a helper for visualising a given light's shadow map.
  * This is a helper for visualising a given light's shadow map.
@@ -60,8 +61,10 @@ class ShadowMapViewer {
 
 
 		const material = new NodeMaterial();
 		const material = new NodeMaterial();
 
 
-		const shadowMapUniform = texture( new Texture() );
-		material.fragmentNode = shadowMapUniform;
+		const textureDimension = uniform( new Vector2() );
+
+		const shadowMapUniform = textureLoad( new DepthTexture(), uv().flipY().mul( textureDimension ) );
+		material.fragmentNode = shadowMapUniform.x.oneMinus();
 
 
 		const plane = new PlaneGeometry( frame.width, frame.height );
 		const plane = new PlaneGeometry( frame.width, frame.height );
 		const mesh = new Mesh( plane, material );
 		const mesh = new Mesh( plane, material );
@@ -173,7 +176,11 @@ class ShadowMapViewer {
 				//always end up with the scene's first added shadow casting light's shadowMap
 				//always end up with the scene's first added shadow casting light's shadowMap
 				//in the shader
 				//in the shader
 				//See: https://github.com/mrdoob/three.js/issues/5932
 				//See: https://github.com/mrdoob/three.js/issues/5932
-				shadowMapUniform.value = light.shadow.map.texture;
+
+				const depthTexture = light.shadow.map.depthTexture;
+
+				shadowMapUniform.value = depthTexture;
+				textureDimension.value.set( depthTexture.width, depthTexture.height );
 
 
 				currentAutoClear = renderer.autoClear;
 				currentAutoClear = renderer.autoClear;
 				renderer.autoClear = false; // To allow render overlay
 				renderer.autoClear = false; // To allow render overlay

+ 1 - 1
examples/jsm/webxr/OculusHandModel.js

@@ -122,7 +122,7 @@ class OculusHandModel extends Object3D {
 	/**
 	/**
 	 * Returns the pointer position which is the position of the index finger tip.
 	 * Returns the pointer position which is the position of the index finger tip.
 	 *
 	 *
-	 * @return {Vector3|null} The pointer position. Returns `null` if not index finger tip joint was found.
+	 * @return {?Vector3} The pointer position. Returns `null` if not index finger tip joint was found.
 	 */
 	 */
 	getPointerPosition() {
 	getPointerPosition() {
 
 

+ 2 - 2
examples/misc_controls_arcball.html

@@ -31,7 +31,7 @@
 			import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
 			import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
 
 
 			import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
 			import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
-			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+			import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
 
 
 			const cameras = [ 'Orthographic', 'Perspective' ];
 			const cameras = [ 'Orthographic', 'Perspective' ];
 			const cameraType = { type: 'Perspective' };
 			const cameraType = { type: 'Perspective' };
@@ -149,7 +149,7 @@
 						scene.add( group );
 						scene.add( group );
 						render();
 						render();
 
 
-						new RGBELoader()
+						new HDRLoader()
 							.setPath( 'textures/equirectangular/' )
 							.setPath( 'textures/equirectangular/' )
 							.load( 'venice_sunset_1k.hdr', function ( hdrEquirect ) {
 							.load( 'venice_sunset_1k.hdr', function ( hdrEquirect ) {
 
 

+ 3 - 3
examples/misc_exporter_exr.html

@@ -26,7 +26,7 @@
 
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { EXRExporter, ZIP_COMPRESSION, ZIPS_COMPRESSION, NO_COMPRESSION } from 'three/addons/exporters/EXRExporter.js';
 			import { EXRExporter, ZIP_COMPRESSION, ZIPS_COMPRESSION, NO_COMPRESSION } from 'three/addons/exporters/EXRExporter.js';
-			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+			import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
@@ -56,14 +56,14 @@
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
 
 
 				exporter = new EXRExporter();
 				exporter = new EXRExporter();
-				const rgbeloader = new RGBELoader();
+				const hdrLoader = new HDRLoader();
 
 
 				//
 				//
 
 
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				pmremGenerator.compileEquirectangularShader();
 				pmremGenerator.compileEquirectangularShader();
 
 
-				rgbeloader.load( 'textures/equirectangular/san_giuseppe_bridge_2k.hdr', function ( texture ) {
+				hdrLoader.load( 'textures/equirectangular/san_giuseppe_bridge_2k.hdr', function ( texture ) {
 
 
 					texture.mapping = THREE.EquirectangularReflectionMapping;
 					texture.mapping = THREE.EquirectangularReflectionMapping;
 
 

+ 3 - 3
examples/misc_exporter_ktx2.html

@@ -26,7 +26,7 @@
 
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js';
 			import { KTX2Exporter } from 'three/addons/exporters/KTX2Exporter.js';
-			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
+			import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
@@ -55,14 +55,14 @@
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
 
 
 				exporter = new KTX2Exporter();
 				exporter = new KTX2Exporter();
-				const rgbeloader = new RGBELoader();
+				const hdrLoader = new HDRLoader();
 
 
 				//
 				//
 
 
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				pmremGenerator.compileEquirectangularShader();
 				pmremGenerator.compileEquirectangularShader();
 
 
-				rgbeloader.load( 'textures/equirectangular/venice_sunset_1k.hdr', function ( texture ) {
+				hdrLoader.load( 'textures/equirectangular/venice_sunset_1k.hdr', function ( texture ) {
 
 
 					texture.mapping = THREE.EquirectangularReflectionMapping;
 					texture.mapping = THREE.EquirectangularReflectionMapping;
 
 

二進制
examples/screenshots/css3d_mixed.jpg


二進制
examples/screenshots/webgl_loader_gltf_animation_pointer.jpg


二進制
examples/screenshots/webgl_loader_gltf_progressive_lod.jpg


二進制
examples/screenshots/webgl_loader_texture_rgbm.jpg


二進制
examples/screenshots/webgl_materials_envmaps_fasthdr.jpg


+ 0 - 0
examples/screenshots/webgl_reverse_depth_buffer.jpg → examples/screenshots/webgl_reversed_depth_buffer.jpg


二進制
examples/screenshots/webgpu_camera.jpg


二進制
examples/screenshots/webgpu_compute_reduce.jpg


二進制
examples/screenshots/webgpu_custom_fog_background.jpg


二進制
examples/screenshots/webgpu_lights_custom.jpg


二進制
examples/screenshots/webgpu_loader_texture_ktx2.jpg


二進制
examples/screenshots/webgpu_materials_cubemap_mipmaps.jpg


二進制
examples/screenshots/webgpu_materials_texture_manualmipmap.jpg


二進制
examples/screenshots/webgpu_multiple_elements.jpg


二進制
examples/screenshots/webgpu_particles.jpg


二進制
examples/screenshots/webgpu_pmrem_cubemap.jpg


二進制
examples/screenshots/webgpu_postprocessing_dof.jpg


二進制
examples/screenshots/webgpu_postprocessing_dof_basic.jpg


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

粤ICP备19079148号