Mr.doob 10 luni în urmă
părinte
comite
fe670fdcbf
100 a modificat fișierele cu 2838 adăugiri și 2123 ștergeri
  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. BIN
      examples/screenshots/css3d_mixed.jpg
  84. BIN
      examples/screenshots/webgl_loader_gltf_animation_pointer.jpg
  85. BIN
      examples/screenshots/webgl_loader_gltf_progressive_lod.jpg
  86. BIN
      examples/screenshots/webgl_loader_texture_rgbm.jpg
  87. BIN
      examples/screenshots/webgl_materials_envmaps_fasthdr.jpg
  88. 0 0
      examples/screenshots/webgl_reversed_depth_buffer.jpg
  89. BIN
      examples/screenshots/webgpu_camera.jpg
  90. BIN
      examples/screenshots/webgpu_compute_reduce.jpg
  91. BIN
      examples/screenshots/webgpu_custom_fog_background.jpg
  92. BIN
      examples/screenshots/webgpu_lights_custom.jpg
  93. BIN
      examples/screenshots/webgpu_loader_texture_ktx2.jpg
  94. BIN
      examples/screenshots/webgpu_materials_cubemap_mipmaps.jpg
  95. BIN
      examples/screenshots/webgpu_materials_texture_manualmipmap.jpg
  96. BIN
      examples/screenshots/webgpu_multiple_elements.jpg
  97. BIN
      examples/screenshots/webgpu_particles.jpg
  98. BIN
      examples/screenshots/webgpu_pmrem_cubemap.jpg
  99. BIN
      examples/screenshots/webgpu_postprocessing_dof.jpg
  100. BIN
      examples/screenshots/webgpu_postprocessing_dof_basic.jpg

Fișier diff suprimat deoarece este prea mare
+ 115 - 23
build/three.cjs


Fișier diff suprimat deoarece este prea mare
+ 111 - 19
build/three.core.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.core.min.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.module.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.module.min.js


Fișier diff suprimat deoarece este prea mare
+ 29 - 4
build/three.tsl.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.tsl.min.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.webgpu.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.webgpu.min.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.webgpu.nodes.js


Fișier diff suprimat deoarece este prea mare
+ 0 - 0
build/three.webgpu.nodes.min.js


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

@@ -20,7 +20,7 @@
 		<h2>أمثلة (Examples)</h2>
 		<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>
 		 

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

@@ -20,7 +20,7 @@
 		<h2>Examples</h2>
 		<p>
 			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.
 		</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
 			an instance of [name] to [page:Object3D.customDistanceMaterial]. The
 			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>
 
 		<h2>Examples</h2>

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

@@ -37,6 +37,24 @@
 		</p>
 
 		<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>
 		<p>
@@ -50,6 +68,18 @@
 			manually from the XR camera's projection matrices.
 		</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>
 		<p>
 			[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
 			controller.
 		</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>
 		<p>
@@ -83,6 +125,12 @@
 			projection layer.
 		</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>
 		<p>
 			[page:Integer index] — The index of the controller. <br /><br />
@@ -101,6 +149,11 @@
 			of active WebXR sessions on application level.
 		</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>
 		<p>
 			[page:Float foveation] — The foveation to set.<br /><br />

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

@@ -19,7 +19,7 @@
 		<h2>Esempi</h2>
 
 		<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.
 		</p>
 

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

@@ -20,7 +20,7 @@
 		<h2>例子</h2>
 
 		<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>
 

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

+ 5 - 0
docs/list.json

@@ -1043,6 +1043,11 @@
 				"LightProbeGenerator": "examples/zh/lights/LightProbeGenerator"
 			},
 
+			"线段": {
+				"Line2": "examples/zh/lines/Line2",
+				"LineGeometry": "examples/en/lines/LineGeometry"
+			},
+
 			"加载器": {
 				"3DMLoader": "examples/zh/loaders/3DMLoader",
 				"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
 
-					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 ) {
 
 						hdrTexture.sourceFile = file.name;

+ 1 - 1
editor/sw.js

@@ -59,7 +59,7 @@ const assets = [
 
 	'../examples/jsm/loaders/EXRLoader.js',
 	'../examples/jsm/loaders/KTX2Loader.js',
-	'../examples/jsm/loaders/RGBELoader.js',
+	'../examples/jsm/loaders/HDRLoader.js',
 	'../examples/jsm/loaders/TGALoader.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_gcode",
 		"webgl_loader_gltf",
+		"webgl_loader_gltf_animation_pointer",
+		"webgl_loader_gltf_progressive_lod",
 		"webgl_loader_gltf_avif",
 		"webgl_loader_gltf_compressed",
 		"webgl_loader_gltf_dispersion",
@@ -114,7 +116,6 @@
 		"webgl_loader_texture_ktx2",
 		"webgl_loader_texture_lottie",
 		"webgl_loader_texture_pvrtc",
-		"webgl_loader_texture_rgbm",
 		"webgl_loader_texture_tga",
 		"webgl_loader_texture_tiff",
 		"webgl_loader_ttf",
@@ -141,6 +142,7 @@
 		"webgl_materials_envmaps_exr",
 		"webgl_materials_envmaps_groundprojected",
 		"webgl_materials_envmaps_hdr",
+		"webgl_materials_envmaps_fasthdr",
 		"webgl_materials_matcap",
 		"webgl_materials_normalmap",
 		"webgl_materials_normalmap_object_space",
@@ -273,7 +275,7 @@
 		"webgl_multiple_rendertargets",
 		"webgl_multisampled_renderbuffers",
 		"webgl_rendertarget_texture2darray",
-		"webgl_reverse_depth_buffer",
+		"webgl_reversed_depth_buffer",
 		"webgl_shadowmap_csm",
 		"webgl_shadowmap_pcss",
 		"webgl_shadowmap_progressive",
@@ -313,6 +315,7 @@
 		"webgpu_compute_particles_rain",
 		"webgpu_compute_particles_snow",
 		"webgpu_compute_points",
+		"webgpu_compute_reduce",
 		"webgpu_compute_sort_bitonic",
 		"webgpu_compute_texture",
 		"webgpu_compute_texture_3d",
@@ -357,16 +360,19 @@
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_transmission",
 		"webgpu_loader_materialx",
+		"webgpu_loader_texture_ktx2",
 		"webgpu_materials",
 		"webgpu_materials_alphahash",
 		"webgpu_materials_arrays",
 		"webgpu_materials_basic",
+		"webgpu_materials_cubemap_mipmaps",
 		"webgpu_materials_displacementmap",
 		"webgpu_materials_envmaps_bpcem",
 		"webgpu_materials_envmaps",
 		"webgpu_materials_lightmap",
 		"webgpu_materials_matcap",
 		"webgpu_materials_sss",
+		"webgpu_materials_texture_manualmipmap",
 		"webgpu_materials_transmission",
 		"webgpu_materials_toon",
 		"webgpu_materials_video",
@@ -377,6 +383,7 @@
 		"webgpu_morphtargets",
 		"webgpu_morphtargets_face",
 		"webgpu_mrt",
+		"webgpu_multiple_elements",
 		"webgpu_mrt_mask",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets_readback",
@@ -456,6 +463,7 @@
 		"webgpu_tsl_vfx_flames",
 		"webgpu_tsl_vfx_linkedparticles",
 		"webgpu_tsl_vfx_tornado",
+		"webgpu_tsl_wood",
 		"webgpu_video_frame",
 		"webgpu_video_panorama",
 		"webgpu_volume_caustics",
@@ -547,6 +555,7 @@
 		"css2d_label"
 	],
 	"css3d": [
+		"css3d_mixed",
 		"css3d_molecules",
 		"css3d_orthographic",
 		"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/GCodeLoader.js';
 export * from './loaders/GLTFLoader.js';
+export * from './loaders/HDRLoader.js';
 export * from './loaders/HDRCubeTextureLoader.js';
 export * from './loaders/IESLoader.js';
 export * from './loaders/KMZLoader.js';
@@ -110,7 +111,6 @@ export * from './loaders/PLYLoader.js';
 export * from './loaders/PVRLoader.js';
 export * from './loaders/RGBELoader.js';
 export * from './loaders/UltraHDRLoader.js';
-export * from './loaders/RGBMLoader.js';
 export * from './loaders/STLLoader.js';
 export * from './loaders/SVGLoader.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 {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.
 	 */
 	setMouseAction( operation, mouse, key = null ) {
@@ -1396,7 +1396,7 @@ class ArcballControls extends Controls {
 	 * 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 {'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.
 	 */
 	unsetMouseAction( mouse, key = null ) {
@@ -1421,8 +1421,8 @@ class ArcballControls extends Controls {
 	 *
 	 * @private
 	 * @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 ) {
 
@@ -1463,7 +1463,7 @@ class ArcballControls extends Controls {
 	 *
 	 * @private
 	 * @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.
 	 */
 	getOpStateFromAction( mouse, key ) {
@@ -2509,8 +2509,8 @@ class ArcballControls extends Controls {
 	 * Sets values in transformation object.
 	 *
 	 * @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 ) {
 

+ 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 ) {
@@ -305,6 +255,7 @@ function onPointerMove( event ) {
 			if ( raycaster.ray.intersectPlane( _plane, _intersection ) ) {
 
 				_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 );
 			_selected.rotateOnWorldAxis( _up, _diff.x );
 			_selected.rotateOnWorldAxis( _right.normalize(), - _diff.y );
+			this.dispatchEvent( { type: 'drag', object: _selected } );
 
 		}
 
-		this.dispatchEvent( { type: 'drag', object: _selected } );
-
 		_previousPointer.copy( _pointer );
 
 	} else {
@@ -414,21 +364,21 @@ function onPointerDown( event ) {
 
 				_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
 				_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
+				domElement.style.cursor = 'move';
+				this.dispatchEvent( { type: 'dragstart', object: _selected } );
 
 			} else if ( this.state === STATE.ROTATE ) {
 
 				// the controls only support Y+ up
 				_up.set( 0, 1, 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 );

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

@@ -194,8 +194,8 @@ class FirstPersonControls extends Controls {
 		window.removeEventListener( 'keydown', this._onKeyDown );
 		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( '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.
 	 *

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

@@ -543,32 +543,36 @@ function getCanvas() {
 
 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.
 	 *
-	 * @param {THREE.Object3D|THREE.Material} object
+	 * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry|THREE.AnimationClip} object
 	 * @param {Object} objectDef
 	 */
 	serializeUserData( object, objectDef ) {
@@ -1555,7 +1559,7 @@ class GLTFWriter {
 	/**
 	 * Process material
 	 * @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 ) {
 
@@ -1731,7 +1735,7 @@ class GLTFWriter {
 	/**
 	 * Process mesh
 	 * @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 ) {
 
@@ -2185,7 +2189,7 @@ class GLTFWriter {
 	 *
 	 * @param {THREE.AnimationClip} clip
 	 * @param {THREE.Object3D} root
-	 * @return {number|null}
+	 * @return {?number}
 	 */
 	processAnimation( clip, root ) {
 
@@ -2279,11 +2283,15 @@ class GLTFWriter {
 
 		}
 
-		json.animations.push( {
+		const animationDef = {
 			name: clip.name || 'clip_' + json.animations.length,
 			samplers: samplers,
 			channels: channels
-		} );
+		};
+
+		this.serializeUserData( clip, animationDef );
+
+		json.animations.push( animationDef );
 
 		return json.animations.length - 1;
 
@@ -2291,7 +2299,7 @@ class GLTFWriter {
 
 	/**
 	 * @param {THREE.Object3D} object
-	 * @return {number|null}
+	 * @return {?number}
 	 */
 	 processSkin( object ) {
 

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

@@ -17,8 +17,8 @@ import {
 } from 'three';
 
 import {
+	createDefaultContainer,
 	write,
-	KTX2Container,
 	KHR_DF_CHANNEL_RGBSDA_ALPHA,
 	KHR_DF_CHANNEL_RGBSDA_BLUE,
 	KHR_DF_CHANNEL_RGBSDA_GREEN,
@@ -191,7 +191,7 @@ export class KTX2Exporter {
 
 		const array = texture.image.data;
 		const channelCount = getChannelCount( texture );
-		const container = new KTX2Container();
+		const container = createDefaultContainer();
 
 		container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
 		container.typeSize = array.BYTES_PER_ELEMENT;
@@ -257,6 +257,8 @@ export class KTX2Exporter {
 
 		//
 
+		container.levelCount = 1;
+
 		container.levels = [ {
 
 			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 {PLYExporter~OnDone} onDone - A callback function that is executed when the export has finished.
 	 * @param {PLYExporter~Options} options - The export options.
-	 * @return {?string|ArrayBuffer} The exported PLY.
+	 * @return {?(string|ArrayBuffer)} The exported PLY.
 	 */
 	parse( object, onDone, options = {} ) {
 

Fișier diff suprimat deoarece este prea mare
+ 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.

+ 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.

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

@@ -10,8 +10,8 @@ import {
 	Vector3,
 	Vector4
 } 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();
 

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

@@ -5,8 +5,8 @@ import {
 	Vector3,
 	Vector4
 } 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 _end = new Vector3();

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

@@ -1,7 +1,7 @@
 import {
 	WireframeGeometry
 } from 'three';
-import { LineSegmentsGeometry } from '../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from './LineSegmentsGeometry.js';
 
 /**
  * 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,
 	Vector2
 } from 'three/webgpu';
-import { LineSegmentsGeometry } from '../../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from '../LineSegmentsGeometry.js';
 
 const _start = new Vector3();
 const _end = new Vector3();

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

@@ -6,7 +6,7 @@ import {
 	Vector3
 } from 'three/webgpu';
 
-import { LineSegmentsGeometry } from '../../lines/LineSegmentsGeometry.js';
+import { LineSegmentsGeometry } from '../LineSegmentsGeometry.js';
 
 const _start = 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} 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 ) {
 

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

@@ -2515,7 +2515,7 @@ class EXRLoader extends DataTextureLoader {
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.decodeChannels = { R: 0 };
 
-					} else  {
+					} else {
 
 						invalidOutput = true;
 
@@ -2549,7 +2549,7 @@ class EXRLoader extends DataTextureLoader {
 						EXRDecoder.outputChannels = 1;
 						EXRDecoder.decodeChannels = { Y: 0 };
 
-					} else  {
+					} else {
 
 						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 ) {
 
@@ -2712,12 +2712,12 @@ class EXRLoader extends DataTextureLoader {
 			if ( this.outputFormat == RGBAFormat ) {
 
 				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 ) {
 
 				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
- * @param {Object3D|Material|BufferGeometry|Object} object
+ * @param {Object3D|Material|BufferGeometry|Object|AnimationClip} object
  * @param {GLTF.definition} gltfDef
  */
 function assignExtrasToUserData( object, gltfDef ) {
@@ -2943,7 +2943,7 @@ class GLTFParser {
 	 * @private
 	 * @param {string} type
 	 * @param {number} index
-	 * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
+	 * @return {Promise<Object3D|Material|Texture|AnimationClip|ArrayBuffer|Object>}
 	 */
 	getDependency( type, index ) {
 
@@ -3283,7 +3283,7 @@ class GLTFParser {
 	 *
 	 * @private
 	 * @param {number} textureIndex
-	 * @return {Promise<THREE.Texture|null>}
+	 * @return {Promise<?Texture>}
 	 */
 	loadTexture( textureIndex ) {
 
@@ -4020,7 +4020,7 @@ class GLTFParser {
 	 *
 	 * @private
 	 * @param {number} cameraIndex
-	 * @return {Promise<THREE.Camera>}
+	 * @return {Promise<Camera>|undefined}
 	 */
 	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,
 	Loader
 } from 'three';
-import { RGBELoader } from '../loaders/RGBELoader.js';
+import { HDRLoader } from '../loaders/HDRLoader.js';
 
 /**
  * A loader for loading HDR cube textures.
@@ -40,9 +40,9 @@ class HDRCubeTextureLoader extends Loader {
 		 * The internal HDR loader that loads the
 		 * individual textures for each cube face.
 		 *
-		 * @type {RGBELoader}
+         * @type {HDRLoader}
 		 */
-		this.hdrLoader = new RGBELoader();
+		this.hdrLoader = new HDRLoader();
 
 		/**
 		 * The texture type.
@@ -147,8 +147,8 @@ class HDRCubeTextureLoader extends Loader {
 	/**
 	 * 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 ) {
 

+ 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,
 	LinearFilter,
 	LinearMipmapLinearFilter,
+	NearestFilter,
+	NearestMipmapNearestFilter,
 	LinearSRGBColorSpace,
 	Loader,
 	NoColorSpace,
@@ -19,6 +21,7 @@ import {
 	RGBA_S3TC_DXT3_Format,
 	RGBA_ETC2_EAC_Format,
 	RGBA_PVRTC_4BPPV1_Format,
+	RGBA_PVRTC_2BPPV1_Format,
 	RGBA_S3TC_DXT1_Format,
 	RGBA_S3TC_DXT5_Format,
 	RGB_BPTC_UNSIGNED_Format,
@@ -26,10 +29,17 @@ import {
 	RGB_ETC2_Format,
 	RGB_PVRTC_4BPPV1_Format,
 	RGB_S3TC_DXT1_Format,
+	SIGNED_RED_GREEN_RGTC2_Format,
+	SIGNED_RED_RGTC1_Format,
+	RED_GREEN_RGTC2_Format,
+	RED_RGTC1_Format,
+	RGBFormat,
 	RGFormat,
 	RedFormat,
 	SRGBColorSpace,
-	UnsignedByteType
+	UnsignedByteType,
+	UnsignedInt5999Type,
+	UnsignedInt101111Type
 } from 'three';
 import { WorkerPool } from '../utils/WorkerPool.js';
 import {
@@ -42,6 +52,7 @@ import {
 	KHR_SUPERCOMPRESSION_NONE,
 	KHR_SUPERCOMPRESSION_ZSTD,
 	VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT,
+	VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT,
 	VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
 	VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
 	VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
@@ -52,12 +63,18 @@ import {
 	VK_FORMAT_BC1_RGB_UNORM_BLOCK,
 	VK_FORMAT_BC3_SRGB_BLOCK,
 	VK_FORMAT_BC3_UNORM_BLOCK,
+	VK_FORMAT_BC4_SNORM_BLOCK,
+	VK_FORMAT_BC4_UNORM_BLOCK,
 	VK_FORMAT_BC5_SNORM_BLOCK,
 	VK_FORMAT_BC5_UNORM_BLOCK,
 	VK_FORMAT_BC7_SRGB_BLOCK,
 	VK_FORMAT_BC7_UNORM_BLOCK,
 	VK_FORMAT_ETC2_R8G8B8_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_R16G16_SFLOAT,
 	VK_FORMAT_R16_SFLOAT,
@@ -70,6 +87,8 @@ import {
 	VK_FORMAT_R8G8_UNORM,
 	VK_FORMAT_R8_SRGB,
 	VK_FORMAT_R8_UNORM,
+	VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
+	VK_FORMAT_B10G11R11_UFLOAT_PACK32,
 	VK_FORMAT_UNDEFINED
 } from '../libs/ktx-parse.module.js';
 import { ZSTDDecoder } from '../libs/zstddec.module.js';
@@ -183,10 +202,10 @@ class KTX2Loader extends Loader {
 		this.workerConfig = {
 			astcSupported: await renderer.hasFeatureAsync( 'texture-compression-astc' ),
 			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' ),
 			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' )
 		};
 
@@ -208,10 +227,10 @@ class KTX2Loader extends Loader {
 			this.workerConfig = {
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
 				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' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
-				bptcSupported: renderer.hasFeature( 'texture-compression-bptc' ),
+				bptcSupported: renderer.hasFeature( 'texture-compression-bc' ),
 				pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' )
 			};
 
@@ -918,74 +937,114 @@ KTX2Loader.BasisWorker = function () {
 // Parsing for non-Basis textures. These textures may have supercompression
 // 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 = {
 
 	[ 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_R16G16_SFLOAT ]: RGFormat,
-	[ VK_FORMAT_R8G8_UNORM ]: RGFormat,
-	[ VK_FORMAT_R8G8_SRGB ]: RGFormat,
-
 	[ VK_FORMAT_R32_SFLOAT ]: RedFormat,
+
+	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: RGBAFormat,
+	[ VK_FORMAT_R16G16_SFLOAT ]: RGFormat,
 	[ 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_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_R8G8B8_SRGB_BLOCK ]: RGB_ETC2_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_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_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_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_UNORM_BLOCK ]: RGB_S3TC_DXT1_Format,
 
 	[ VK_FORMAT_BC3_SRGB_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_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 = {
 
 	[ 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_R16G16_SFLOAT ]: HalfFloatType,
-	[ VK_FORMAT_R8G8_UNORM ]: UnsignedByteType,
-	[ VK_FORMAT_R8G8_SRGB ]: UnsignedByteType,
-
 	[ VK_FORMAT_R32_SFLOAT ]: FloatType,
+
+	[ VK_FORMAT_R16G16B16A16_SFLOAT ]: HalfFloatType,
+	[ VK_FORMAT_R16G16_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_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_R8G8B8_SRGB_BLOCK ]: UnsignedByteType,
 
 	[ 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_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 ) {
@@ -994,7 +1053,14 @@ async function createRawTexture( container ) {
 
 	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 = [];
 
-
 	for ( let levelIndex = 0; levelIndex < container.levels.length; 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 {
 
 			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;
 
 	if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {
@@ -1095,14 +1173,16 @@ async function createRawTexture( container ) {
 		texture = container.pixelDepth === 0
 			? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
 			: 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 {
 
 		if ( container.pixelDepth > 0 ) throw new Error( 'THREE.KTX2Loader: Unsupported pixelDepth.' );
 
 		texture = new CompressedTexture( mipmaps, container.pixelWidth, container.pixelHeight );
-
-		texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
+		texture.minFilter = useMipmaps ? LinearMipmapLinearFilter : 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 ) {
 
+		console.warn( 'RGBELoader has been deprecated. Please use HDRLoader instead.' );
 		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 };
+
+

+ 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 ) {
-						
+
 						glyph.unicodes.forEach( function ( unicode ) {
-							
+
 							glyphs[ String.fromCodePoint( unicode ) ] = token;
-							
+
 						} );
-						
+
 					} else {
 
 						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.
 	 *
 	 * @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 ) {
 

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

@@ -1013,7 +1013,7 @@ class DataViewReader {
 
 	getFloat64() {
 
-		const value = this.dv.getFloat64( this.offset, this.littleEndian );
+		const value = this.dv.getFloat64( this.offset );
 		this.offset += 8;
 		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 */
 
@@ -114,6 +114,24 @@ export const LinearRec2020ColorSpaceImpl = {
 	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.

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

@@ -152,7 +152,7 @@ class ConvexHull {
 	 *
 	 * @param {Ray} ray - The ray to test.
 	 * @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 ) {
 
@@ -1298,7 +1298,7 @@ class HalfEdge {
 	 * Returns the origin vertex.
 	 *
 	 * @private
-	 * @return {VertexNode} The destination vertex.
+	 * @return {?VertexNode} The destination vertex.
 	 */
 	tail() {
 

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

@@ -45,7 +45,7 @@ class Lut {
 		/**
 		 * The currently selected color map.
 		 *
-		 * @type {Array}
+		 * @type {Array<Array<number>>}
 		 */
 		this.map = [];
 
@@ -219,7 +219,7 @@ class Lut {
 	 * Adds a color map to this Lut instance.
 	 *
 	 * @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.
 	 * @return {Lut} A reference to this LUT.
 	 */

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

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

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

@@ -280,7 +280,7 @@ class ProgressiveLightMap {
 	 *
 	 * @private
 	 * @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 ) {
 

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

@@ -234,7 +234,7 @@ class Volume {
 		/**
 		 * The list of all the slices associated to this volume
 		 *
-		 * @type {Array}
+		 * @type {Array<VolumeSlice>}
 		 */
 		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
 

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

@@ -137,6 +137,8 @@ class SSRPass extends Pass {
 
 		this._selects = selects;
 
+		this._resolutionScale = 1;
+
 		/**
 		 * 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
 	 * method whenever the pass is no longer used in your app.
@@ -661,23 +686,27 @@ class SSRPass extends Pass {
 		this.width = width;
 		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.beautyRenderTarget.setSize( width, height );
-		this.prevRenderTarget.setSize( width, height );
-		this.ssrRenderTarget.setSize( width, height );
 		this.normalRenderTarget.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.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[ '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 ) );
 
-			#ifdef USE_REVERSEDEPTHBUF
+			#ifdef USE_REVERSED_DEPTH_BUFFER
 
 				gl_FragColor = vec4( vec3( depth ), opacity );
 

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

@@ -11,22 +11,21 @@ const arithmeticOperators = [
 ];
 
 const precedenceOperators = [
-	'/', '*', '%',
-	'-', '+',
-	'<<', '>>',
-	'<', '>', '<=', '>=',
-	'==', '!=',
-	'&',
-	'^',
-	'|',
-	'&&',
-	'^^',
-	'||',
-	'?',
-	'=',
-	'+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=',
-	','
-].reverse();
+	[ ',' ],
+	[ '=', '+=', '-=', '*=', '/=', '%=', '^=', '&=', '|=', '<<=', '>>=' ],
+	[ '?' ],
+	[ '||' ],
+	[ '^^' ],
+	[ '&&' ],
+	[ '|' ],
+	[ '^' ],
+	[ '&' ],
+	[ '==', '!=' ],
+	[ '<', '>', '<=', '>=' ],
+	[ '<<', '>>' ],
+	[ '+', '-' ],
+	[ '*', '/', '%' ]
+];
 
 const associativityRightToLeft = [
 	'=',
@@ -334,7 +333,7 @@ class GLSLDecoder {
 
 		let groupIndex = 0;
 
-		for ( const operator of precedenceOperators ) {
+		for ( const operators of precedenceOperators ) {
 
 			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 === '?' ) {
 
@@ -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 ++ ) {
 

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

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

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

@@ -80,6 +80,10 @@ const wgslLib = {
 	'textureLod': 'textureSampleLevel',
 	'texelFetch': 'textureLoad',
 	'textureGrad': 'textureSampleGrad',
+	'floatBitsToInt': 'bitcast<i32>',
+	'floatBitsToUint': 'bitcast<u32>',
+	'intBitsToFloat': 'bitcast<f32>',
+	'uintBitsToFloat': 'bitcast<f32>',
 };
 
 class WGSLEncoder {
@@ -170,6 +174,26 @@ class WGSLEncoder {
 
 				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' ) ) {
 
 				// 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.
 		 *
-		 * @type {Vector2}
+		 * @type {float}
 		 */
-		this.resolution = new Vector2( 1, 1 );
+		this.resolutionScale = 1;
 
 		/**
 		 * The internal render target of the effect.
@@ -130,8 +130,8 @@ class AnamorphicNode extends TempNode {
 
 		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 );
 
@@ -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
 
-		// 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
-		const kernelSizeArray = [ 6, 10, 14, 18, 22 ]; 
+		const kernelSizeArray = [ 6, 10, 14, 18, 22 ];
 
 		for ( let i = 0; i < this._nMips; i ++ ) {
 
@@ -480,7 +480,7 @@ class BloomNode extends TempNode {
 				const uvOffset = direction.mul( invSize ).mul( x );
 				const sample1 = sampleTexel( uvNode.add( uvOffset ) ).rgb;
 				const sample2 = sampleTexel( uvNode.sub( uvOffset ) ).rgb;
-				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );		
+				diffuseSum.addAssign( add( sample1, sample2 ).mul( w ) );
 
 			} );
 

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

@@ -189,7 +189,7 @@ export default ChromaticAberrationNode;
  * @function
  * @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|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.
  * @returns {ChromaticAberrationNode}
  */
@@ -203,4 +203,5 @@ export const chromaticAberration = ( node, strength = 1.0, center = null, 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.
  *
+ * 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
  * @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 {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' );
 
@@ -47,29 +55,164 @@ class DepthOfFieldNode extends TempNode {
 		 *
 		 * @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>}
 		 */
-		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>}
 		 */
-		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
-		 * @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
@@ -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.
 	 *
 	 * @param {NodeFrame} frame - The current node frame.
 	 */
-	updateBefore() {
+	updateBefore( frame ) {
+
+		const { renderer } = frame;
+
+		// resize
 
 		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.
 	 * @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
  * @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> | 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}
  */
-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();
 
+			// Each iteration analyzes one vertical "slice" of the 3D space around the fragment.
+
 			Loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
 
 				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 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 } ) => {
 
 					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
 
 					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 nx = dot( normalInSlice, sliceTangent );
 				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 {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 {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' );
 
@@ -105,12 +108,12 @@ class GaussianBlurNode extends TempNode {
 		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`
@@ -119,32 +122,7 @@ class GaussianBlurNode extends TempNode {
 		 * @type {boolean}
 		 * @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 ) {
 
-		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._horizontalRT.setSize( width, height );
@@ -280,6 +258,7 @@ class GaussianBlurNode extends TempNode {
 				const sample2 = sampleTexture( uvNode.sub( uvOffset ) );
 
 				diffuseSum.addAssign( sample1.add( sample2 ).mul( w ) );
+
 			}
 
 			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;
@@ -349,18 +351,28 @@ export default GaussianBlurNode;
  * @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 {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}
  */
-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
+ * @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<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.
  * @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 {Camera} camera - The camera the scene is rendered with.
 	 * @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.edgeGlow=float(0)] - Can be used for an animated glow/pulse effects.
 	 * @param {number} [params.downSampleRatio=2] - The downsample ratio.
@@ -742,7 +742,7 @@ export default OutlineNode;
  * @param {Scene} scene - A reference to the scene.
  * @param {Camera} camera - The camera the scene is rendered with.
  * @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.edgeGlow=float(0)] - Can be used for animated glow/pulse effects.
  * @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 _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<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 {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' );
 
@@ -63,22 +65,26 @@ class SSRNode extends TempNode {
 		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}
-		 * @default 0.5
+		 * @default 1
 		 */
-		this.resolutionScale = 0.5;
+		this.resolutionScale = 1;
 
 		/**
 		 * The `updateBeforeType` is set to `NodeUpdateType.FRAME` since the node renders
@@ -90,17 +96,8 @@ class SSRNode extends TempNode {
 		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>}
 		 */
@@ -114,12 +111,63 @@ class SSRNode extends TempNode {
 		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>}
 		 */
 		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.
 		 *
@@ -158,7 +206,7 @@ class SSRNode extends TempNode {
 		 * @private
 		 * @type {UniformNode<bool>}
 		 */
-		this._isPerspectiveCamera = uniform( camera.isPerspectiveCamera ? 1 : 0 );
+		this._isPerspectiveCamera = uniform( camera.isPerspectiveCamera );
 
 		/**
 		 * The resolution of the pass.
@@ -169,13 +217,23 @@ class SSRNode extends TempNode {
 		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
-		 * @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.
@@ -183,8 +241,26 @@ class SSRNode extends TempNode {
 		 * @private
 		 * @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.
@@ -194,6 +270,25 @@ class SSRNode extends TempNode {
 		 */
 		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() {
 
-		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 );
 
 		this._resolution.value.set( width, height );
-		this._maxStep.value = Math.round( Math.sqrt( width * width + height * height ) );
-
 		this._ssrRenderTarget.setSize( width, height );
+		this._blurRenderTarget.setSize( width, height );
 
 	}
 
@@ -236,9 +330,12 @@ class SSRNode extends TempNode {
 
 		_rendererState = RendererUtils.resetRendererState( renderer, _rendererState );
 
+		const ssrRenderTarget = this._ssrRenderTarget;
+		const blurRenderTarget = this._blurRenderTarget;
+
 		const size = renderer.getDrawingBufferSize( _size );
 
-		_quadMesh.material = this._material;
+		_quadMesh.material = this._ssrMaterial;
 
 		this.setSize( size.width, size.height );
 
@@ -249,9 +346,27 @@ class SSRNode extends TempNode {
 
 		// ssr
 
-		renderer.setRenderTarget( this._ssrRenderTarget );
+		renderer.setRenderTarget( ssrRenderTarget );
 		_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
 
 		RendererUtils.restoreRendererState( renderer, _rendererState );
@@ -326,7 +441,7 @@ class SSRNode extends TempNode {
 
 		const ssr = Fn( () => {
 
-			const metalness = this.metalnessNode.sample( uvNode ).r;
+			const metalness = float( this.metalnessNode );
 
 			// fragments with no metalness do not reflect their environment
 			metalness.equal( 0.0 ).discard();
@@ -349,7 +464,7 @@ class SSRNode extends TempNode {
 			const d1viewPosition = viewPosition.add( viewReflectDir.mul( maxReflectRayLen ) ).toVar();
 
 			// 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.
 				// this prevents artifacts during the ray marching process
@@ -374,7 +489,7 @@ class SSRNode extends TempNode {
 			// determine the larger delta
 			// 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
-			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
 			const xSpan = xLen.div( totalStep ).toVar();
@@ -385,23 +500,9 @@ class SSRNode extends TempNode {
 			// the actual ray marching loop
 			// 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)
-			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();
 
 				// 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 d = sampleDepth( uvNode ).toVar();
 				const vZ = getViewZ( d ).toVar();
-				const vP = getViewPosition( uvNode, d, this._cameraProjectionMatrixInverse ).toVar();
 
 				const viewReflectRayZ = float( 0 ).toVar();
 
@@ -423,7 +523,7 @@ class SSRNode extends TempNode {
 				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
-				If( this._isPerspectiveCamera.equal( float( 1 ) ), () => {
+				If( this._isPerspectiveCamera, () => {
 
 					const recipVPZ = float( 1 ).div( viewPosition.z ).toVar();
 					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
 					// 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();
 
 					// 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() {
 
 		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<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 {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}
  */
-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.
 		 *
-		 * @type {TextureNode}
+		 * @type {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.
  *
+ * 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}.
  *
  * @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 {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<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.
  * @return {Node<vec4>} The blurred texture node.
  */
 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;
 
 	};
 
 	const targetUV = textureNode.uvNode || uv();
-	const blurred_image = vec4( 0. ).toVar();
+	const blurred_image = vec4( 0. );
 
 	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 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.
  *
  * @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 ) {
 

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

@@ -7,9 +7,10 @@ import {
 	OrthographicCamera,
 	PlaneGeometry,
 	Scene,
-	Texture
+	DepthTexture,
+	Vector2
 } 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.
@@ -60,8 +61,10 @@ class ShadowMapViewer {
 
 		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 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
 				//in the shader
 				//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;
 				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.
 	 *
-	 * @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() {
 

+ 2 - 2
examples/misc_controls_arcball.html

@@ -31,7 +31,7 @@
 			import { ArcballControls } from 'three/addons/controls/ArcballControls.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 cameraType = { type: 'Perspective' };
@@ -149,7 +149,7 @@
 						scene.add( group );
 						render();
 
-						new RGBELoader()
+						new HDRLoader()
 							.setPath( 'textures/equirectangular/' )
 							.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 { 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';
 
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
@@ -56,14 +56,14 @@
 				scene = new THREE.Scene();
 
 				exporter = new EXRExporter();
-				const rgbeloader = new RGBELoader();
+				const hdrLoader = new HDRLoader();
 
 				//
 
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				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;
 

+ 3 - 3
examples/misc_exporter_ktx2.html

@@ -26,7 +26,7 @@
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.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';
 
 			let scene, camera, renderer, exporter, mesh, controls, renderTarget, dataTexture;
@@ -55,14 +55,14 @@
 				scene = new THREE.Scene();
 
 				exporter = new KTX2Exporter();
-				const rgbeloader = new RGBELoader();
+				const hdrLoader = new HDRLoader();
 
 				//
 
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 				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;
 

BIN
examples/screenshots/css3d_mixed.jpg


BIN
examples/screenshots/webgl_loader_gltf_animation_pointer.jpg


BIN
examples/screenshots/webgl_loader_gltf_progressive_lod.jpg


BIN
examples/screenshots/webgl_loader_texture_rgbm.jpg


BIN
examples/screenshots/webgl_materials_envmaps_fasthdr.jpg


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


BIN
examples/screenshots/webgpu_camera.jpg


BIN
examples/screenshots/webgpu_compute_reduce.jpg


BIN
examples/screenshots/webgpu_custom_fog_background.jpg


BIN
examples/screenshots/webgpu_lights_custom.jpg


BIN
examples/screenshots/webgpu_loader_texture_ktx2.jpg


BIN
examples/screenshots/webgpu_materials_cubemap_mipmaps.jpg


BIN
examples/screenshots/webgpu_materials_texture_manualmipmap.jpg


BIN
examples/screenshots/webgpu_multiple_elements.jpg


BIN
examples/screenshots/webgpu_particles.jpg


BIN
examples/screenshots/webgpu_pmrem_cubemap.jpg


BIN
examples/screenshots/webgpu_postprocessing_dof.jpg


BIN
examples/screenshots/webgpu_postprocessing_dof_basic.jpg


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff

粤ICP备19079148号