Mr.doob 1 year ago
parent
commit
eee30eacf4
100 changed files with 5632 additions and 14300 deletions
  1. 49 45
      build/three.cjs
  2. 0 13864
      build/three.js
  3. 0 6
      build/three.min.js
  4. 49 45
      build/three.module.js
  5. 0 0
      build/three.module.min.js
  6. 2 1
      docs/api/ar/materials/ShaderMaterial.html
  7. 1 1
      docs/api/ar/objects/LOD.html
  8. 13 1
      docs/api/en/cameras/PerspectiveCamera.html
  9. 6 0
      docs/api/en/core/InterleavedBufferAttribute.html
  10. 1 1
      docs/api/en/geometries/RingGeometry.html
  11. 1 1
      docs/api/en/lights/shadows/DirectionalLightShadow.html
  12. 3 2
      docs/api/en/materials/Material.html
  13. 2 1
      docs/api/en/materials/ShaderMaterial.html
  14. 1 1
      docs/api/en/objects/LOD.html
  15. 1 1
      docs/api/en/renderers/WebGLRenderer.html
  16. 7 2
      docs/api/en/textures/Source.html
  17. 2 1
      docs/api/fr/materials/ShaderMaterial.html
  18. 2 1
      docs/api/it/materials/ShaderMaterial.html
  19. 1 1
      docs/api/it/objects/LOD.html
  20. 1 1
      docs/api/it/renderers/WebGLRenderer.html
  21. 8 2
      docs/api/it/textures/Source.html
  22. 31 27
      docs/api/zh/constants/BufferAttributeUsage.html
  23. 1 1
      docs/api/zh/geometries/RingGeometry.html
  24. 1 1
      docs/api/zh/lights/RectAreaLight.html
  25. 4 2
      docs/api/zh/materials/Material.html
  26. 2 1
      docs/api/zh/materials/ShaderMaterial.html
  27. 208 0
      docs/api/zh/objects/BatchedMesh.html
  28. 1 1
      docs/api/zh/objects/LOD.html
  29. 7 1
      docs/api/zh/textures/Source.html
  30. 84 0
      docs/examples/en/loaders/LUT3dlLoader.html
  31. 83 0
      docs/examples/en/loaders/LUTCubeLoader.html
  32. 1 0
      docs/examples/en/postprocessing/EffectComposer.html
  33. 188 0
      docs/examples/zh/animations/CCDIKSolver.html
  34. 186 0
      docs/examples/zh/animations/MMDAnimationHelper.html
  35. 130 0
      docs/examples/zh/animations/MMDPhysics.html
  36. 287 0
      docs/examples/zh/controls/ArcballControls.html
  37. 119 0
      docs/examples/zh/controls/MapControls.html
  38. 86 0
      docs/examples/zh/exporters/DRACOExporter.html
  39. 96 0
      docs/examples/zh/exporters/EXRExporter.html
  40. 157 0
      docs/examples/zh/exporters/GLTFExporter.html
  41. 68 0
      docs/examples/zh/exporters/OBJExporter.html
  42. 76 0
      docs/examples/zh/exporters/PLYExporter.html
  43. 76 0
      docs/examples/zh/exporters/STLExporter.html
  44. 73 0
      docs/examples/zh/geometries/SDFGeometryGenerator.html
  45. 95 0
      docs/examples/zh/helpers/VertexTangentsHelper.html
  46. 279 0
      docs/examples/zh/loaders/3DMLoader.html
  47. 142 0
      docs/examples/zh/loaders/KTX2Loader.html
  48. 219 0
      docs/examples/zh/loaders/LDrawLoader.html
  49. 117 0
      docs/examples/zh/loaders/PDBLoader.html
  50. 141 0
      docs/examples/zh/math/Lut.html
  51. 98 0
      docs/examples/zh/math/MeshSurfaceSampler.html
  52. 193 0
      docs/examples/zh/math/OBB.html
  53. 238 0
      docs/examples/zh/math/convexhull/ConvexHull.html
  54. 111 0
      docs/examples/zh/math/convexhull/Face.html
  55. 98 0
      docs/examples/zh/math/convexhull/HalfEdge.html
  56. 117 0
      docs/examples/zh/math/convexhull/VertexList.html
  57. 69 0
      docs/examples/zh/math/convexhull/VertexNode.html
  58. 110 0
      docs/examples/zh/misc/Timer.html
  59. 1 0
      docs/examples/zh/postprocessing/EffectComposer.html
  60. 46 0
      docs/examples/zh/utils/CameraUtils.html
  61. 117 0
      docs/examples/zh/webxr/XREstimatedLight.html
  62. 59 8
      docs/list.json
  63. 2 2
      docs/manual/en/introduction/Installation.html
  64. 1 1
      docs/manual/en/introduction/Libraries-and-Plugins.html
  65. 272 0
      docs/manual/zh/introduction/Color-management.html
  66. 18 2
      editor/js/Menubar.File.js
  67. 23 0
      editor/js/Sidebar.Geometry.Modifiers.js
  68. 1 1
      editor/js/Sidebar.Material.MapProperty.js
  69. 3 0
      editor/js/Strings.js
  70. 4 0
      editor/sw.js
  71. 10 1
      examples/files.json
  72. 2 1
      examples/jsm/Addons.js
  73. 13 9
      examples/jsm/capabilities/WebGPU.js
  74. 46 19
      examples/jsm/controls/OrbitControls.js
  75. 59 4
      examples/jsm/curves/NURBSUtils.js
  76. 62 0
      examples/jsm/curves/NURBSVolume.js
  77. 8 7
      examples/jsm/exporters/USDZExporter.js
  78. 3 3
      examples/jsm/helpers/TextureHelper.js
  79. 44 43
      examples/jsm/loaders/3DMLoader.js
  80. 1 0
      examples/jsm/loaders/DDSLoader.js
  81. 20 5
      examples/jsm/loaders/KTX2Loader.js
  82. 78 46
      examples/jsm/loaders/LUT3dlLoader.js
  83. 81 67
      examples/jsm/loaders/LUTCubeLoader.js
  84. 3 2
      examples/jsm/loaders/LUTImageLoader.js
  85. 115 16
      examples/jsm/loaders/MaterialXLoader.js
  86. 4 3
      examples/jsm/loaders/SVGLoader.js
  87. 1 1
      examples/jsm/loaders/VRMLLoader.js
  88. 144 0
      examples/jsm/materials/MeshPostProcessingMaterial.js
  89. 13 4
      examples/jsm/misc/Timer.js
  90. 16 5
      examples/jsm/nodes/Nodes.js
  91. 5 3
      examples/jsm/nodes/accessors/BufferAttributeNode.js
  92. 9 4
      examples/jsm/nodes/accessors/CameraNode.js
  93. 12 1
      examples/jsm/nodes/accessors/CubeTextureNode.js
  94. 27 0
      examples/jsm/nodes/accessors/StorageBufferNode.js
  95. 56 3
      examples/jsm/nodes/accessors/TextureStoreNode.js
  96. 70 0
      examples/jsm/nodes/accessors/VertexColorNode.js
  97. 1 1
      examples/jsm/nodes/core/AttributeNode.js
  98. 23 18
      examples/jsm/nodes/core/NodeBuilder.js
  99. 16 8
      examples/jsm/nodes/core/NodeFrame.js
  100. 1 0
      examples/jsm/nodes/core/constants.js

File diff suppressed because it is too large
+ 49 - 45
build/three.cjs


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


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


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


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


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

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

+ 1 - 1
docs/api/ar/objects/LOD.html

@@ -27,7 +27,7 @@
 			
 			
 		// إنشاء كرات مع 3 مستويات من التفاصيل وإنشاء مستويات LOD جديدة لهم
 		// إنشاء كرات مع 3 مستويات من التفاصيل وإنشاء مستويات LOD جديدة لهم
 		for( let i = 0; i < 3; i++ ) {
 		for( let i = 0; i < 3; i++ ) {
-			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i )
+			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
 			const mesh = new THREE.Mesh( geometry, material );
 			const mesh = new THREE.Mesh( geometry, material );
 			lod.addLevel( mesh, i * 75 );
 			lod.addLevel( mesh, i * 75 );
 		}
 		}

+ 13 - 1
docs/api/en/cameras/PerspectiveCamera.html

@@ -145,6 +145,19 @@
 			By default, the focal length is specified for a 35mm (full frame) camera.
 			By default, the focal length is specified for a 35mm (full frame) camera.
 		</p>
 		</p>
 
 
+		<h3>[method:undefined getViewBounds]( [param:Float distance], [param:Vector2 minTarget], [param:Vector2 maxTarget] )
+		</h3>
+		<p>
+			Computes the 2D bounds of the camera's viewable rectangle at a given distance along the viewing direction.
+			Sets minTarget and maxTarget to the coordinates of the lower-left and upper-right corners of the view rectangle.
+		</p>
+
+		<h3>[method:Vector2 getViewSize]( [param:Float distance], [param:Vector2 target] )</h3>
+		<p>
+			Computes the width and height of the camera's viewable rectangle at a given distance along the viewing direction.
+			Copies the result into the target Vector2, where x is width and y is height.
+		</p>
+
 		<h3>[method:undefined setViewOffset]( [param:Float fullWidth], [param:Float fullHeight], [param:Float x], [param:Float y], [param:Float width], [param:Float height] )</h3>
 		<h3>[method:undefined setViewOffset]( [param:Float fullWidth], [param:Float fullHeight], [param:Float x], [param:Float y], [param:Float width], [param:Float height] )</h3>
 		<p>
 		<p>
 			fullWidth — full width of multiview setup<br />
 			fullWidth — full width of multiview setup<br />
@@ -214,6 +227,5 @@ camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );
 		<p>
 		<p>
 			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
 		</p>
 		</p>
-
 	</body>
 	</body>
 </html>
 </html>

+ 6 - 0
docs/api/en/core/InterleavedBufferAttribute.html

@@ -77,6 +77,9 @@
 			vectors.
 			vectors.
 		</p>
 		</p>
 
 
+		<h3>[method:Number getComponent]( [param:Integer index], [param:Integer component] ) </h3>
+		<p>Returns the given component of the vector at the given index.</p>
+
 		<h3>[method:Number getX]( [param:Integer index] )</h3>
 		<h3>[method:Number getX]( [param:Integer index] )</h3>
 		<p>Returns the x component of the item at the given index.</p>
 		<p>Returns the x component of the item at the given index.</p>
 
 
@@ -89,6 +92,9 @@
 		<h3>[method:Number getW]( [param:Integer index] )</h3>
 		<h3>[method:Number getW]( [param:Integer index] )</h3>
 		<p>Returns the w component of the item at the given index.</p>
 		<p>Returns the w component of the item at the given index.</p>
 
 
+		<h3>[method:Number setComponent]( [param:Integer index], [param:Integer component], [param:Float value] ) </h3>
+		<p>Sets the given component of the vector at the given index.</p>
+		
 		<h3>[method:this setX]( [param:Integer index], [param:Float x] )</h3>
 		<h3>[method:this setX]( [param:Integer index], [param:Float x] )</h3>
 		<p>Sets the x component of the item at the given index.</p>
 		<p>Sets the x component of the item at the given index.</p>
 
 

+ 1 - 1
docs/api/en/geometries/RingGeometry.html

@@ -49,7 +49,7 @@ const mesh = new THREE.Mesh( geometry, material ); scene.add( mesh );
 			outerRadius — Default is `1`. <br />
 			outerRadius — Default is `1`. <br />
 			thetaSegments — Number of segments. A higher number means the ring will be
 			thetaSegments — Number of segments. A higher number means the ring will be
 			more round. Minimum is `3`. Default is `32`. <br />
 			more round. Minimum is `3`. Default is `32`. <br />
-			phiSegments — Minimum is `1`. Default is `1`.<br />
+			phiSegments — Number of segments per ring segment. Minimum is `1`. Default is `1`.<br />
 			thetaStart — Starting angle. Default is `0`. <br />
 			thetaStart — Starting angle. Default is `0`. <br />
 			thetaLength — Central angle. Default is Math.PI * 2.
 			thetaLength — Central angle. Default is Math.PI * 2.
 		</p>
 		</p>

+ 1 - 1
docs/api/en/lights/shadows/DirectionalLightShadow.html

@@ -80,7 +80,7 @@
 
 
 			The default is an [page:OrthographicCamera] with
 			The default is an [page:OrthographicCamera] with
 			[page:OrthographicCamera.left left] and [page:OrthographicCamera.bottom bottom]
 			[page:OrthographicCamera.left left] and [page:OrthographicCamera.bottom bottom]
-			set to -5, [page:OrthographicCamera.right right] and
+			set to `-5`, [page:OrthographicCamera.right right] and
 			[page:OrthographicCamera.top top] set to `5`, the
 			[page:OrthographicCamera.top top] set to `5`, the
 			[page:OrthographicCamera.near near] clipping plane at `0.5` and the
 			[page:OrthographicCamera.near near] clipping plane at `0.5` and the
 			[page:OrthographicCamera.far far] clipping plane at `500`.
 			[page:OrthographicCamera.far far] clipping plane at `500`.

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

@@ -45,8 +45,9 @@
 		<h3>[property:Boolean alphaToCoverage]</h3>
 		<h3>[property:Boolean alphaToCoverage]</h3>
 		<p>
 		<p>
 			Enables alpha to coverage. Can only be used with MSAA-enabled contexts
 			Enables alpha to coverage. Can only be used with MSAA-enabled contexts
-			(meaning when the renderer was created with `antialias` parameter set to
-			`true`). Default is `false`.
+			(meaning when the renderer was created with *antialias* parameter set to
+			`true`). Enabling this will smooth aliasing on clip plane edges and alphaTest-clipped edges.
+			Default is `false`.
 		</p>
 		</p>
 
 
 		<h3>[property:Float blendAlpha]</h3>
 		<h3>[property:Float blendAlpha]</h3>

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

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

+ 1 - 1
docs/api/en/objects/LOD.html

@@ -28,7 +28,7 @@
 
 
 		//Create spheres with 3 levels of detail and create new LOD levels for them
 		//Create spheres with 3 levels of detail and create new LOD levels for them
 		for( let i = 0; i < 3; i++ ) {
 		for( let i = 0; i < 3; i++ ) {
-			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i )
+			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
 			const mesh = new THREE.Mesh( geometry, material );
 			const mesh = new THREE.Mesh( geometry, material );
 			lod.addLevel( mesh, i * 75 );
 			lod.addLevel( mesh, i * 75 );
 		}
 		}

+ 1 - 1
docs/api/en/renderers/WebGLRenderer.html

@@ -82,7 +82,7 @@
 		<h3>[property:Boolean autoClear]</h3>
 		<h3>[property:Boolean autoClear]</h3>
 		<p>
 		<p>
 			Defines whether the renderer should automatically clear its output before
 			Defines whether the renderer should automatically clear its output before
-			rendering a frame.
+			rendering a frame. Default is `true`.
 		</p>
 		</p>
 
 
 		<h3>[property:Boolean autoClearColor]</h3>
 		<h3>[property:Boolean autoClearColor]</h3>

+ 7 - 2
docs/api/en/textures/Source.html

@@ -31,8 +31,13 @@
 
 
 		<h3>[property:Boolean needsUpdate]</h3>
 		<h3>[property:Boolean needsUpdate]</h3>
 		<p>
 		<p>
-			Set this to `true` to trigger a data upload to the GPU next time the
-			source is used.
+			When the property is set to `true`, the engine allocates the memory for the texture (if necessary) and triggers the actual texture upload to the GPU next time the source is used.
+		</p>
+
+		<h3>[property:Boolean dataReady]</h3>
+		<p>
+			This property is only relevant when [page:.needUpdate] is set to `true` and provides more control on how texture data should be processed.
+			When `dataReady` is set to `false`, the engine performs the memory allocation (if necessary) but does not transfer the data into the GPU memory. Default is `true`.
 		</p>
 		</p>
 
 
 		<h3>[property:String uuid]</h3>
 		<h3>[property:String uuid]</h3>

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

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

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

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

+ 1 - 1
docs/api/it/objects/LOD.html

@@ -27,7 +27,7 @@
 		// Crea sfere con 3 livelli di dettaglio e crea nuovi livelli LOD per loro
 		// Crea sfere con 3 livelli di dettaglio e crea nuovi livelli LOD per loro
 		for( let i = 0; i < 3; i++ ) {
 		for( let i = 0; i < 3; i++ ) {
 
 
-			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i )
+			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
 
 
 			const mesh = new THREE.Mesh( geometry, material );
 			const mesh = new THREE.Mesh( geometry, material );
 
 

+ 1 - 1
docs/api/it/renderers/WebGLRenderer.html

@@ -72,7 +72,7 @@
 		<h2>Proprietà</h2>
 		<h2>Proprietà</h2>
 
 
 		<h3>[property:Boolean autoClear]</h3>
 		<h3>[property:Boolean autoClear]</h3>
-		<p>Definisce se il renderer deve cancellare automaticamente il suo output prima di eseguire il rendering di un frame.</p>
+		<p>Definisce se il renderer deve cancellare automaticamente il suo output prima di eseguire il rendering di un frame. Il valore predefinito è `true`.</p>
 
 
 
 
 		<h3>[property:Boolean autoClearColor]</h3>
 		<h3>[property:Boolean autoClearColor]</h3>

+ 8 - 2
docs/api/it/textures/Source.html

@@ -27,9 +27,15 @@
 			I dati effettivi di una texture. Il tipo di questa proprietà dipende dalla texture che utilizza questa istanza.
 			I dati effettivi di una texture. Il tipo di questa proprietà dipende dalla texture che utilizza questa istanza.
 		</p>
 		</p>
 
 
-		<h3>[property:Boolean isSource]</h3>
+		<h3>[property:Boolean needsUpdate]</h3>
+		<p>
+			When the property is set to `true`, the engine allocates the memory for the texture (if necessary) and triggers the actual texture upload to the GPU next time the source is used.
+		</p>
+
+		<h3>[property:Boolean dataReady]</h3>
 		<p>
 		<p>
-			Flag di sola lettura per verificare se l'oggetto dato è di tipo [name].
+			This property is only relevant when [page:.needUpdate] is set to `true` and provides more control on how texture data should be processed.
+			When `dataReady` is set to `false`, the engine performs the memory allocation (if necessary) but does not transfer the data into the GPU memory. Default is `true`.
 		</p>
 		</p>
 
 
 		<h3>[property:Boolean needsUpdate]</h3>
 		<h3>[property:Boolean needsUpdate]</h3>

+ 31 - 27
docs/api/zh/constants/BufferAttributeUsage.html

@@ -1,32 +1,34 @@
 <!DOCTYPE html>
 <!DOCTYPE html>
 <html lang="zh">
 <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>
-		<h1>Buffer Attribute Usage Constants</h1>
-
-		<p>
-			The usage constants can be used to provide a hint to the API regarding how the geometry buffer attribute will be used in order to optimize performance.
-		</p>
-
-		<h2>Code Example</h2>
-
-		<code>
+
+<head>
+	<meta charset="utf-8" />
+	<base href="../../../" />
+	<script src="page.js"></script>
+	<link type="text/css" rel="stylesheet" href="page.css" />
+</head>
+
+<body>
+	<h1>缓冲区属性使用常量([name])</h1>
+
+	<p>
+		使用常量可用于向 API 提供有关如何使用几何缓冲区属性以优化性能的提示。
+	</p>
+
+	<h2>代码示例</h2>
+
+	<code>
 		const geometry = new THREE.BufferGeometry();
 		const geometry = new THREE.BufferGeometry();
 		const positionAttribute = new THREE.BufferAttribute( array, 3 , false );
 		const positionAttribute = new THREE.BufferAttribute( array, 3 , false );
 		positionAttribute.setUsage( THREE.DynamicDrawUsage );
 		positionAttribute.setUsage( THREE.DynamicDrawUsage );
 		geometry.setAttribute( 'position', positionAttribute );
 		geometry.setAttribute( 'position', positionAttribute );
 		</code>
 		</code>
 
 
-		<h2>Examples</h2>
-		<p>[example:webgl_buffergeometry_drawrange materials / buffergeometry / drawrange ]</p>
+	<h2>例子</h2>
+	<p>[example:webgl_buffergeometry_drawrange materials / buffergeometry / drawrange ]</p>
 
 
-		<h2>Geometry Usage</h2>
-		<code>
+	<h2>几何体相关(Geometry Usage)</h2>
+	<code>
 		THREE.StaticDrawUsage
 		THREE.StaticDrawUsage
 		THREE.DynamicDrawUsage
 		THREE.DynamicDrawUsage
 		THREE.StreamDrawUsage
 		THREE.StreamDrawUsage
@@ -40,12 +42,14 @@
 		THREE.StreamCopyUsage
 		THREE.StreamCopyUsage
 		</code>
 		</code>
 
 
-		For more detailed information on each of these constants see [link:https://www.khronos.org/opengl/wiki/Buffer_Object#Buffer_Object_Usage this OpenGL documentation].
+	有关每个常量的更多详细信息,请参阅
+	[link:https://www.khronos.org/opengl/wiki/Buffer_Object#Buffer_Object_Usage OpenGL 文档]。
+
+	<h2>源代码</h2>
 
 
-		<h2>Source</h2>
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/src/constants.js src/constants.js]
+	</p>
+</body>
 
 
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/src/constants.js src/constants.js]
-		</p>
-	</body>
-</html>
+</html>

+ 1 - 1
docs/api/zh/geometries/RingGeometry.html

@@ -46,7 +46,7 @@
 			innerRadius — 内部半径,默认值为0.5。 <br />
 			innerRadius — 内部半径,默认值为0.5。 <br />
 			outerRadius — 外部半径,默认值为1。<br />
 			outerRadius — 外部半径,默认值为1。<br />
 			thetaSegments — 圆环的分段数。这个值越大,圆环就越圆。最小值为3,默认值为32。<br />
 			thetaSegments — 圆环的分段数。这个值越大,圆环就越圆。最小值为3,默认值为32。<br />
-			phiSegments — 最小值为1,默认值为8。<br />
+			phiSegments — 圆环半径的分段数字。最小值为1,默认值为1。<br />
 			thetaStart — 起始角度,默认值为0。<br />
 			thetaStart — 起始角度,默认值为0。<br />
 			thetaLength — 圆心角,默认值为Math.PI * 2。
 			thetaLength — 圆心角,默认值为Math.PI * 2。
 		</p>
 		</p>

+ 1 - 1
docs/api/zh/lights/RectAreaLight.html

@@ -33,7 +33,7 @@
 		rectLight.lookAt( 0, 0, 0 );
 		rectLight.lookAt( 0, 0, 0 );
 		scene.add( rectLight )
 		scene.add( rectLight )
 
 
-		rectLightHelper = new RectAreaLightHelper( rectLight );
+		const rectLightHelper = new RectAreaLightHelper( rectLight );
 		scene.add( rectLightHelper );
 		scene.add( rectLightHelper );
 		</code>
 		</code>
 
 

+ 4 - 2
docs/api/zh/materials/Material.html

@@ -35,8 +35,10 @@
 
 
 <h3>[property:Boolean alphaToCoverage]</h3>
 <h3>[property:Boolean alphaToCoverage]</h3>
 <p>
 <p>
-启用alpha to coverage. 只能在开启了MSAA的渲染环境中使用 (当渲染器创建的时候*antialias* 属性要*true*才能使用).
-默认为 *false*.
+	Enables alpha to coverage. Can only be used with MSAA-enabled contexts
+	(meaning when the renderer was created with *antialias* parameter set to
+	`true`). Enabling this will smooth aliasing on clip plane edges and alphaTest-clipped edges.
+	Default is `false`.
 </p>
 </p>
 
 
 <h3>[property:Integer blendDst]</h3>
 <h3>[property:Integer blendDst]</h3>

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

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

+ 208 - 0
docs/api/zh/objects/BatchedMesh.html

@@ -0,0 +1,208 @@
+<!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:Mesh] &rarr;
+
+	<h1>批处理网格([name])</h1>
+
+	<p class="desc">
+		[page:Mesh] 的特殊版本,支持多绘制批量渲染。如果您必须渲染大量具有相同材质但具有不同世界变换和几何形状的对象,请使用 [name]。使用 [name] 将帮助您减少绘制调用的数量,从而提高应用程序的整体渲染性能。
+		<br />
+		<br />
+
+		如果不支持 [link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_multi_draw WEBGL_multi_draw extension]
+		,则使用性能较低的回调。
+	</p>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const box = new THREE.BoxGeometry( 1, 1, 1 );
+		const sphere = new THREE.BoxGeometry( 1, 1, 1 );
+		const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+
+		// initialize and add geometries into the batched mesh
+		const batchedMesh = new BatchedMesh( 10, 5000, 10000, material );
+		const boxId = batchedMesh.addGeometry( box );
+		const sphereId = batchedMesh.addGeometry( sphere );
+
+		// position the geometries
+		batchedMesh.setMatrixAt( boxId, boxMatrix );
+		batchedMesh.setMatrixAt( sphereId, sphereMatrix );
+
+		scene.add( batchedMesh );
+		</code>
+
+	<h2>例子</h2>
+	<p>
+		[example:webgl_mesh_batch WebGL / mesh / batch]<br />
+	</p>
+
+	<h2>构造函数</h2>
+	<h3>
+		[name](
+		[param:Integer maxGeometryCount], [param:Integer maxVertexCount],
+		[param:Integer maxIndexCount], [param:Material material],
+		)
+	</h3>
+	<p>
+		[page:Integer maxGeometryCount] - 计划添加的单个几何体的最大数量。<br />
+		[page:Integer maxVertexCount] - 所有几何体使用的最大顶点数。<br />
+		[page:Integer maxIndexCount] - 所有几何图形使用的最大索引数。<br />
+		[page:Material material] - [page:Material] 的一个实例。默认是新的 [page:MeshBasicMaterial]。<br />
+	</p>
+
+	<h2>属性</h2>
+	<p>有关常见属性,请参阅 [page:Mesh] 基类</p>
+
+	<h3>[property:Box3 boundingBox]</h3>
+	<p>
+		该边界框包围了 [name] 的所有实例。可以用 [page:.computeBoundingBox]() 进行计算。默认为 `null`。
+	</p>
+
+	<h3>[property:Sphere boundingSphere]</h3>
+	<p>
+		该边界球包围了 [name] 的所有实例。可以用 [page:.computeBoundingSphere]() 进行计算。默认为 `null`。
+	</p>
+
+	<h3>[property:Boolean perObjectFrustumCulled]</h3>
+	<p>
+		如果为 true,则 [name] 内的各个对象将被视锥体剔除。默认为 `true`。
+	</p>
+
+	<h3>[property:Boolean sortObjects]</h3>
+	<p>
+		如果为 true,则对 [name] 中的各个对象进行排序以改善与过度绘制相关的工件。如果材质被标记为“透明”,则对象将从后到前渲染,如果没有,则它们从前到后渲染。默认为 `true`。
+	</p>
+
+	<h3>[property:Integer maxGeometryCount]</h3>
+	<p>
+		只读,[name] 中可以存储的单个几何体的最大数量。
+	</p>
+
+	<h3>[property:Boolean isBatchedMesh]</h3>
+	<p>用于检查给定对象是否属于 [name] 类型的只读标志。</p>
+
+	<h2>Methods</h2>
+	<p>有关常用方法,请参阅 [page:Mesh] 基类。</p>
+
+	<h3>[method:undefined computeBoundingBox]()</h3>
+	<p>
+		计算边界框,更新 [page:.boundingBox] 属性。<br />
+		默认情况下不计算边界框。它们需要显式计算,否则就是 `null`。
+	</p>
+
+	<h3>[method:undefined computeBoundingSphere]()</h3>
+	<p>
+		计算边界球,更新 [page:.boundingSphere] 属性。<br />
+		默认情况下不计算边界球。它们需要显式计算,否则就是 `null`。
+	</p>
+
+	<h3>[method:undefined dispose]()</h3>
+	<p>
+		释放该实例分配的GPU相关资源。每当您的应用程序中不再使用此实例时,请调用此方法。
+	</p>
+
+	<h3>[method:this setCustomSort]( [param:Function sortFunction] )</h3>
+	<p>
+		对渲染之前运行的函数进行排序。该函数需要一个要排序的项目列表和一个相机。列表中的对象包含一个“z”字段,用于执行深度排序。
+	</p>
+
+	<h3>
+		[method:Matrix4 getMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )
+	</h3>
+	<p>
+		[page:Integer index]: 实例的索引。值必须在 [0, count] 范围内。
+	</p>
+	<p>
+		[page:Matrix4 matrix]: 这个 4x4 矩阵将被设置为定义实例的局部变换矩阵。
+	</p>
+	<p>获取定义实例的局部变换矩阵。</p>
+
+	<h3>
+		[method:Boolean getVisibleAt]( [param:Integer index] )
+	</h3>
+	<p>
+		[page:Integer index]: 实例的索引。值必须在 [0, count] 范围内。
+	</p>
+	<p>获取给定实例是否标记为“可见”。</p>
+
+	<h3>
+		[method:this setMatrixAt]( [param:Integer index], [param:Matrix4 matrix] )
+	</h3>
+	<p>
+		[page:Integer index]: 实例的索引。值必须在 [0, count] 范围内。
+	</p>
+	<p>
+		[page:Matrix4 matrix]: 表示单个实例的局部变换的 4x4 矩阵。
+	</p>
+	<p>
+		将给定的局部变换矩阵设置为定义的实例。
+	</p>
+
+	<h3>
+		[method:this setVisibleAt]( [param:Integer index], [param:Boolean visible] )
+	</h3>
+	<p>
+		[page:Integer index]: 实例的索引。值必须在 [0, count] 范围内。
+	</p>
+	<p>
+		[page:Boolean visible]: 指示可见性状态的布尔值。
+	</p>
+	<p>
+		设置给定索引处对象的可见性。
+	</p>
+
+	<h3>
+		[method:Integer addGeometry]( [param:BufferGeometry geometry], [param:Integer reservedVertexRange], [param:Integer
+		reservedIndexRange] )
+	</h3>
+	<p>
+		[page:BufferGeometry geometry]: 要添加到 [name] 中的几何体。
+	</p>
+	<p>
+		[page:Integer reservedVertexRange]: 可选参数,指定为添加的几何体保留的顶点缓冲区空间量。如果计划稍后在此索引处设置大于原始几何图形的新几何图形,则这是必要的。默认为给定几何顶点缓冲区的长度。
+	</p>
+	<p>
+		[page:Integer reservedIndexRange]: 可选参数,指定为添加的几何体保留的索引缓冲区空间量。如果计划稍后在此索引处设置大于原始几何图形的新几何图形,则这是必要的。默认为给定几何索引缓冲区的长度。
+	</p>
+	<p>
+		将给定几何体添加到 [name] 并返回引用它的关联索引。
+	</p>
+
+	<h3>
+		[method:Integer setGeometryAt]( [param:Integer index], [param:BufferGeometry geometry] )
+	</h3>
+	<p>
+		[page:Integer index]: 用该几何图形替换哪个几何图形索引。
+	</p>
+	<p>
+		[page:BufferGeometry geometry]: 在给定几何索引处替换的几何。
+	</p>
+	<p>
+		用提供的几何图形替换 `index` 的几何图形。如果索引处没有为几何体保留足够的空间,则会引发错误。
+	</p>
+
+	<h3>
+		[method:this deleteGeometry]( [param:Integer index] )
+	</h3>
+	<p>
+		将给定索引处的几何体标记为已删除并且不再渲染。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</p>
+</body>
+
+</html>

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

@@ -25,7 +25,7 @@
 		//Create spheres with 3 levels of detail and create new LOD levels for them
 		//Create spheres with 3 levels of detail and create new LOD levels for them
 		for( let i = 0; i < 3; i++ ) {
 		for( let i = 0; i < 3; i++ ) {
 
 
-			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i )
+			const geometry = new THREE.IcosahedronGeometry( 10, 3 - i );
 
 
 			const mesh = new THREE.Mesh( geometry, material );
 			const mesh = new THREE.Mesh( geometry, material );
 
 

+ 7 - 1
docs/api/zh/textures/Source.html

@@ -34,7 +34,13 @@
 
 
 		<h3>[property:Boolean needsUpdate]</h3>
 		<h3>[property:Boolean needsUpdate]</h3>
 		<p>
 		<p>
-		Set this to *true* to trigger a data upload to the GPU next time the source is used.
+			When the property is set to `true`, the engine allocates the memory for the texture (if necessary) and triggers the actual texture upload to the GPU next time the source is used.
+		</p>
+
+		<h3>[property:Boolean dataReady]</h3>
+		<p>
+			This property is only relevant when [page:.needUpdate] is set to `true` and provides more control on how texture data should be processed.
+			When `dataReady` is set to `false`, the engine performs the memory allocation (if necessary) but does not transfer the data into the GPU memory. Default is `true`.
 		</p>
 		</p>
 
 
 		<h3>[property:String uuid]</h3>
 		<h3>[property:String uuid]</h3>

+ 84 - 0
docs/examples/en/loaders/LUT3dlLoader.html

@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Loader] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			A 3D LUT loader that supports the .3dl file format.<br />
+			Based on the following references:
+		</p>
+
+		<ul>
+			<li>[link:http://download.autodesk.com/us/systemdocs/help/2011/lustre/index.html?url=./files/WSc4e151a45a3b785a24c3d9a411df9298473-7ffd.htm,topicNumber=d0e9492]</li>
+			<li>[link:https://community.foundry.com/discuss/topic/103636/format-spec-for-3dl?mode=Post&postID=895258]</li>
+		</ul>
+
+		<h2>Import</h2>
+
+		<p>
+			[name] is an add-on, and must be imported explicitly.
+			See [link:#manual/introduction/Installation Installation / Addons].
+		</p>
+
+		<code>
+			import { LUT3dlLoader } from 'three/addons/loaders/LUT3dlLoader.js';
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+			[page:LoadingManager manager] — The LoadingManager to use. Defaults to [page:DefaultLoadingManager DefaultLoadingManager]<br />
+		</p>
+		<p>
+			Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+		<p>See the base [page:Loader] class for common properties.</p>
+
+		<h2>Methods</h2>
+		<p>See the base [page:Loader] class for common methods.</p>
+
+		<h3>[method:undefined load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+			[page:String url] — A string containing the path/URL of the `.3dl` file.<br />
+			[page:Function onLoad] — (optional) A function to be called after the loading is successfully completed. The function receives the result of the [page:Function parse] method.<br />
+			[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes. If the server does not set the Content-Length header; .[page:Integer total] will be 0.<br />
+			[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br />
+		</p>
+		<p>
+			Begin loading from url and return the loaded LUT.
+		</p>
+
+		<h3>[method:Object parse]( [param:String input] )</h3>
+		<p>
+			[page:String input] — The 3dl data string.<br />
+		</p>
+		<p>
+			Parse a 3dl data string and fire [page:Function onLoad] callback when complete. The argument to [page:Function onLoad] will be an [page:Object object] containing the following LUT data: [page:Number .size], [page:DataTexture .texture] and [page:Data3DTexture .texture3D].
+		</p>
+
+		<h3>[method:this setType]( [param:Number type] )</h3>
+		<p>
+			[page:Number type] - The texture type. See the [page:Textures texture constants] page for details.<br />
+		</p>
+		<p>
+			Sets the desired texture type. Only [page:Textures THREE.UnsignedByteType] and [page:Textures THREE.FloatType] are supported. The default is [page:Textures THREE.UnsignedByteType].
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/[name].js examples/jsm/loaders/[name].js]
+		</p>
+	</body>
+</html>

+ 83 - 0
docs/examples/en/loaders/LUTCubeLoader.html

@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Loader] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			A 3D LUT loader that supports the .cube file format.<br />
+			Based on the following reference:
+		</p>
+
+		<ul>
+			<li>[link:https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf]</li>
+		</ul>
+
+		<h2>Import</h2>
+
+		<p>
+			[name] is an add-on, and must be imported explicitly.
+			See [link:#manual/introduction/Installation Installation / Addons].
+		</p>
+
+		<code>
+			import { LUTCubeLoader } from 'three/addons/loaders/LUTCubeLoader.js';
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+			[page:LoadingManager manager] — The LoadingManager to use. Defaults to [page:DefaultLoadingManager DefaultLoadingManager]<br />
+		</p>
+		<p>
+			Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+		<p>See the base [page:Loader] class for common properties.</p>
+
+		<h2>Methods</h2>
+		<p>See the base [page:Loader] class for common methods.</p>
+
+		<h3>[method:undefined load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+			[page:String url] — A string containing the path/URL of the `.cube` file.<br />
+			[page:Function onLoad] — (optional) A function to be called after the loading is successfully completed. The function receives the result of the [page:Function parse] method.<br />
+			[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains [page:Integer total] and [page:Integer loaded] bytes. If the server does not set the Content-Length header; .[page:Integer total] will be 0.<br />
+			[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives the error as an argument.<br />
+		</p>
+		<p>
+			Begin loading from url and return the loaded LUT.
+		</p>
+
+		<h3>[method:Object parse]( [param:String input] )</h3>
+		<p>
+			[page:String input] — The cube data string.<br />
+		</p>
+		<p>
+			Parse a cube data string and fire [page:Function onLoad] callback when complete. The argument to [page:Function onLoad] will be an [page:Object object] containing the following LUT data: [page:String .title], [page:Number .size], [page:Vector3 .domainMin], [page:Vector3 .domainMax], [page:DataTexture .texture] and [page:Data3DTexture .texture3D].
+		</p>
+
+		<h3>[method:this setType]( [param:Number type] )</h3>
+		<p>
+			[page:Number type] - The texture type. See the [page:Textures texture constants] page for details.<br />
+		</p>
+		<p>
+			Sets the desired texture type. Only [page:Textures THREE.UnsignedByteType] and [page:Textures THREE.FloatType] are supported. The default is [page:Textures THREE.UnsignedByteType].
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/[name].js examples/jsm/loaders/[name].js]
+		</p>
+	</body>
+</html>

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

@@ -41,6 +41,7 @@
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
+			[example:webgl_postprocessing_material_ao postprocessing material ao]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_procedural postprocessing procedural]<br />
 			[example:webgl_postprocessing_procedural postprocessing procedural]<br />

+ 188 - 0
docs/examples/zh/animations/CCDIKSolver.html

@@ -0,0 +1,188 @@
+<!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>
+	<h1>CCDIK解算器([name])</h1>
+
+	<p class="desc"> 一种基于 <a href="https://sites.google.com/site/auraliusproject/ccd-algorithm">`CCD Algorithm`</a> 的 IK
+		解算器。<br /><br />
+		[name] 用 CCD 算法解决逆运动学问题。
+		[name] 设计用于与 [page:SkinnedMesh] 配合使用,但也可与 [page:MMDLoader] 或 [page:GLTFLoader] 配合使用。
+	</p>
+
+	<iframe id="scene" src="scenes/ccdiksolver-browser.html"></iframe>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { CCDIKSolver } from 'three/addons/animation/CCDIKSolver.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		let ikSolver;
+
+		//
+		// Bones hierarchy:
+		//
+		//   root
+		//     ├── bone0
+		//     │    └── bone1
+		//     │          └── bone2
+		//     │                └── bone3
+		//     └── target
+		//
+		// Positioned as follow on the cylinder:
+		//
+		//        o      <- target      (y =  20)
+		//        
+		//   +----o----+ <- bone3       (y =  12)
+		//   |         |
+		//   |    o    | <- bone2       (y =   4)
+		//   |         |
+		//   |    o    | <- bone1       (y =  -4)
+		//   |         |
+		//   +----oo---+ <- root, bone0 (y = -12)
+		//
+
+		let bones = []
+
+		// "root"
+		let rootBone = new Bone();
+		rootBone.position.y = -12;
+		bones.push( rootBone );
+
+		// "bone0"
+		let prevBone = new Bone();
+		prevBone.position.y = 0;
+		rootBone.add( prevBone );
+		bones.push( prevBone );
+
+		// "bone1", "bone2", "bone3"
+		for ( let i = 1; i <= 3; i ++ ) {
+			const bone = new Bone();
+			bone.position.y = 8;
+			bones.push( bone );
+			
+			prevBone.add( bone );
+			prevBone = bone;
+		}
+
+		// "target"
+		const targetBone = new Bone();
+		targetBone.position.y = 24 + 8
+		rootBone.add( targetBone );
+		bones.push( targetBone );
+
+		//
+		// skinned mesh
+		//
+
+		const mesh = new SkinnedMesh( geometry,	material );
+		const skeleton = new Skeleton( bones );
+
+		mesh.add( bones[ 0 ] ); // "root" bone
+		mesh.bind( skeleton );
+
+		//
+		// ikSolver
+		//
+
+		const iks = [
+			{
+				target: 5, // "target"
+				effector: 4, // "bone3"
+				links: [ { index: 3 }, { index: 2 }, { index: 1 } ] // "bone2", "bone1", "bone0"
+			}
+		];
+		ikSolver = new CCDIKSolver( mesh, iks );
+
+		function render() {
+			ikSolver?.update();
+			renderer.render( scene, camera );
+		}
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_animation_skinning_ik]<br />
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_pose]<br />
+		[example:webgl_loader_mmd_audio]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:SkinnedMesh mesh], [param:Array iks] )</h3>
+	<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh] 用于 [name] 解决 IK 问题<br />
+		[page:Array iks] — 指定 IK 参数的对象 [page:Object] 数组。target、effector 和 link-index 是 .sculptor.bones
+		中的索引整数。骨骼关系从父级到子级的顺序应为“links[ n ]、 links[ n - 1 ]、...、 links[ 0 ]、effector”。<br />
+	</p>
+	<ul>
+		<li>[page:Integer target] — 目标骨骼</li>
+		<li>[page:Integer effector] — 效应器骨</li>
+		<li>[page:Array links] — 指定链接骨骼的对象[page:Object] 数组
+			<ul>
+				<li>[page:Integer index] — 链接骨骼</li>
+				<li>[page:Vector3 limitation] — (可选)旋转轴。默认值 undefined</li>
+				<li>[page:Vector3 rotationMin] — (可选)旋转最小限制。默认值 undefined</li>
+				<li>[page:Vector3 rotationMax] — (可选)旋转最大限制。默认值 undefined</li>
+				<li>[page:Boolean enabled] — (可选)默认值为 true。</li>
+			</ul>
+		</li>
+		<li>[page:Integer iteration] — (可选)计算的迭代次数。越小速度越快,但精度较差。默认值为 1。</li>
+		<li>[page:Number minAngle] — (可选)一步中的最小旋转角度。默认值 undefined</li>
+		<li>[page:Number maxAngle] — (可选)一步中的最大旋转角度。默认值 undefined</li>
+	</ul>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Array iks]</h3>
+	<p>传递给构造函数的 IK 参数数组。</p>
+
+	<h3>[property:SkinnedMesh mesh]</h3>
+	<p>[page:SkinnedMesh] 传递给构造函数。</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:CCDIKHelper createHelper]()</h3>
+	<p>
+		返回 [page:CCDIKHelper]. 。您可以通过将辅助对象添加到场景来可视化 IK 骨骼。
+	</p>
+
+	<h3>[method:this update]()</h3>
+	<p>
+		通过求解 CCD 算法更新 IK 骨骼四元数。
+	</p>
+
+	<h3>[method:this updateOne]( [param:Object ikParam] )</h3>
+	<p>
+		通过求解 CCD 算法来更新一个 IK 骨骼四元数。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/animation/CCDIKSolver.js
+		examples/jsm/animation/CCDIKSolver.js]
+	</p>
+</body>
+
+</html>

+ 186 - 0
docs/examples/zh/animations/MMDAnimationHelper.html

@@ -0,0 +1,186 @@
+<!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>
+	<h1>MMD动画辅助对象([name])</h1>
+
+	<p class="desc"> <a href="https://sites.google.com/view/evpvp/">`MMD`</a> 资源的动画辅助对象。<br /><br />
+		[name] 处理由 [page:MMDLoader] 加载的 MMD 资源的动画,具有 IK、Grant 和Physics 等 MMD 特殊功能。它内部使用 [page:CCDIKSolver] 和
+		[page:MMDPhysics]。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { MMDAnimationHelper } from 'three/addons/animation/MMDAnimationHelper.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate a helper
+		const helper = new MMDAnimationHelper();
+
+		// Load MMD resources and add to helper
+		new MMDLoader().loadWithAnimation(
+			'models/mmd/miku.pmd',
+			'models/mmd/dance.vmd',
+			function ( mmd ) {
+
+				helper.add( mmd.mesh, {
+					animation: mmd.animation,
+					physics: true
+				} );
+
+				scene.add( mmd.mesh );
+
+				new THREE.AudioLoader().load(
+					'audios/mmd/song.mp3',
+					function ( buffer ) {
+
+						const listener = new THREE.AudioListener();
+						const audio = new THREE.Audio( listener ).setBuffer( buffer );
+
+						listener.position.z = 1;
+
+						scene.add( audio );
+						scene.add( listener );
+
+					}
+
+				);
+
+			}
+		);
+
+		function render() {
+
+			helper.update( clock.getDelta() );
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_pose]<br />
+		[example:webgl_loader_mmd_audio]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:Object params] )</h3>
+	<p>
+		[page:Object params] — (可选)<br />
+	</p>
+	<ul>
+		<li> [page:Boolean sync] - 添加对象的动画持续时间是否同步。默认为 true</li>
+		<li> [page:Number afterglow] - 默认值为 0.0。</li>
+		<li> [page:Boolean resetPhysicsOnLoop] - 默认值为 true</li>
+		<li> [page:Boolean pmxAnimation] - 如果设置为 true,则帮助程序遵循复杂且昂贵的 PMX 动画系统。仅当您的 PMX 模型动画效果不佳时才尝试此选项。默认为 false。</li>
+	</ul>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Audio audio]</h3>
+	<p>添加的 [page:Audio] 音频</p>
+
+	<h3>[property:Camera camera]</h3>
+	<p>添加的 [page:Camera] 相机</p>
+
+	<h3>[property:Array meshes]</h3>
+	<p>添加的 [page:SkinnedMesh] 数组</p>
+
+	<h3>[property:WeakMap objects]</h3>
+	<p>一个 [page:WeakMap] ,它保存添加到 helper 的对象的 helper 中使用的动画内容。例如,您可以使用“helper.objects.get(mesh).mixer”访问添加的
+		[page:AnimationMixer] 的 [page:SkinnedMesh]。</p>
+
+	<h3>[property:Function onBeforePhysics]</h3>
+	<p> 在 [page:SkinnedMesh] 的物理计算之前立即执行的可选回调。该函数通过 [page:SkinnedMesh] 调用。</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:MMDAnimationHelper add]( [param:Object3D object], [param:Object params] )</h3>
+	<p>
+		[page:Object3D object] — [page:SkinnedMesh], [page:Camera] 或 [page:Audio]<br />
+		[page:Object params] — (可选)<br />
+	</p>
+	<ul>
+		<li>[page:AnimationClip animation] - 一个 [page:AnimationClip] 或设置为对象的 [page:AnimationClip] 数组。仅适用于 [page:SkinnedMesh]
+			和 [page:Camera]。默认值 undefined。</li>
+		<li>[page:Boolean physics] - 仅适用于 [page:SkinnedMesh]。是否开启物理标志。默认为 true。</li>
+		<li>[page:Integer warmup] - 仅适用于 [page:SkinnedMesh] 并且 physics 为 true。物理参数。默认值为 60。</li>
+		<li>[page:Number unitStep] - 仅适用于 [page:SkinnedMesh] 并且 physics 为 true。物理参数。默认值为 1 / 65。</li>
+		<li>[page:Integer maxStepNum] - 仅适用于 [page:SkinnedMesh] 并且 physics 为 true。物理参数。默认值为 3。</li>
+		<li>[page:Vector3 gravity] - 仅适用于 [page:SkinnedMesh] 并且 physics 为 true。物理参数。默认值为 ( 0, - 9.8 * 10, 0 )。</li>
+		<li>[page:Number delayTime] - 仅适用于 [page:Audio]。默认值为 0.0。</li>
+	</ul>
+	<p>
+		添加 [page:SkinnedMesh]、[page:Camera] 或 [page:Audio] 到辅助对象并设置动画。 添加的对象的动画持续时间是同步的。如果已经添加了摄像头/音频,它将被替换为新的。
+	</p>
+
+	<h3>[method:MMDAnimationHelper enable]( [param:String key], [param:Boolean enabled] )</h3>
+	<p>
+		[page:String key] — 允许的字符串为 'animation'、'ik'、'grant'、'physicals' 和 'cameraAnimation'。<br />
+		[page:Boolean enabled] — true 表示启用,false 表示禁用。<br />
+	</p>
+	<p>
+		启用/禁用动画功能
+	</p>
+
+	<h3>[method:MMDAnimationHelper pose]( [param:SkinnedMesh mesh], [param:Object vpd], [param:Object params] )</h3>
+	<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh] 改变姿势。不需要将其添加到 helper 中。<br />
+		[page:Object vpd] — 获取由 [page:MMDLoader].loadVPD 加载的 VPD 内容<br />
+		[page:Object params] — (可选)<br />
+	</p>
+	<ul>
+		<li>[page:Boolean resetPose] - 默认为 true</li>
+		<li>[page:Boolean ik] - 默认为 true</li>
+		<li>[page:Boolean grant] - 默认为 true</li>
+	</ul>
+	<p>
+		根据 VPD 内容指定 更改 [page:SkinnedMesh] 的姿势。
+	</p>
+
+	<h3>[method:MMDAnimationHelper remove]( [param:Object3D object] )</h3>
+	<p>
+		[page:Object3D object] — [page:SkinnedMesh]、[page:Camera] 或 [page:Audio]<br />
+	</p>
+	<p>
+		从助手中删除 [page:SkinnedMesh]、[page:Camera] 或 [page:Audio]。
+	</p>
+
+	<h3>[method:MMDAnimationHelper update]( [param:Number delta] )</h3>
+	<p>
+		[page:Number delta] — 秒数<br />
+	</p>
+	<p>
+		提前混合器时间并更新添加到助手的对象的动画。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/animation/MMDAnimationHelper.js
+		examples/jsm/animation/MMDAnimationHelper.js]
+	</p>
+</body>
+
+</html>

+ 130 - 0
docs/examples/zh/animations/MMDPhysics.html

@@ -0,0 +1,130 @@
+<!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>
+	<h1>MMD物理([name])</h1>
+
+	<p class="desc"> 资源的物理处理程序 <a href="https://sites.google.com/view/evpvp/">`MMD`</a>。 <br /><br />
+		[name] 使用 [link:https://github.com/kripken/ammo.js/ ammo.js] (基于 Bullet 的 JavaScript 物理引擎)为 [page:MMDLoader]
+		计算加载模型的物理量。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { MMDPhysics } from 'three/addons/animation/MMDPhysics.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		let physics;
+
+		// Load MMD resources and instantiate MMDPhysics
+		new MMDLoader().load(
+			'models/mmd/miku.pmd',
+			function ( mesh ) {
+
+				physics = new MMDPhysics( mesh )
+				scene.add( mesh );
+
+			}
+		);
+
+		function render() {
+
+			const delta = clock.getDelta();
+			animate( delta );  // update bones
+			if ( physics !== undefined ) physics.update( delta );
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_audio]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:SkinnedMesh mesh], [param:Array rigidBodyParams], [param:Array constraintParams], [param:Object
+		params] )</h3>
+	<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh], [name] 为其计算物理。<br />
+		[page:Array rigidBodyParams] — 指定刚体参数的 [page:Object] 数组。<br />
+		[page:Array constraintParams] — 可选)指定约束参数的 [page:Object] 数组。<br />
+		[page:Object params] — (可选)<br />
+	</p>
+	<ul>
+		<li>[page:Number unitStep] - 默认为 1 / 65</li>
+		<li>[page:Integer maxStepNum] - 默认为 3</li>
+		<li>[page:Vector3 gravity] - 默认为 ( 0, - 9.8 * 10, 0 )</li>
+	</ul>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Array mesh]</h3>
+	<p>[page:SkinnedMesh] 传递给构造函数。</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:MMDPhysicsHelper createHelper]()</h3>
+	<p>
+		返回 [page:MMDPhysicsHelper]。您可以通过将辅助对象添加到场景来可视化刚体。
+	</p>
+
+	<h3>[method:this reset]()</h3>
+	<p>
+		重置刚体变换为当前骨骼的刚体。
+	</p>
+
+	<h3>[method:this setGravity]( [param:Vector3 gravity] )</h3>
+	<p>
+		[page:Vector3 gravity] — 重力的方向和体积。
+	</p>
+	<p>
+		设置重力。
+	</p>
+
+	<h3>[method:this update]( [param:Number delta] )</h3>
+	<p>
+		[page:Number delta] — 时间(以秒为单位)
+	</p>
+	<p>
+		高级物理计算和更新骨骼。
+	</p>
+
+	<h3>[method:this warmup]( [param:Integer cycles] )</h3>
+	<p>
+		[page:Number delta] — 时间(以秒为单位)
+	</p>
+	<p>
+		热身刚体。计算循环步数。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/animation/MMDPhysics.js
+		examples/jsm/animation/MMDPhysics.js]
+	</p>
+</body>
+
+</html>

+ 287 - 0
docs/examples/zh/controls/ArcballControls.html

@@ -0,0 +1,287 @@
+<!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:EventDispatcher] &rarr;
+
+	<h1>弧球控制器([name])</h1>
+
+	<p class="desc">
+		ArcballControls 允许通过具有完全触摸支持和高级导航功能的虚拟轨迹球控制相机。<br>
+		光标/手指位置和移动被映射到由小控件表示的虚拟轨迹球表面上,并映射为直观且一致的相机移动。拖动光标/手指将使相机以保守的方式围绕轨迹球中心旋转(返回起始点将使相机返回到其起始方向)。<br><br>
+
+		除了支持平移、缩放和捏合手势之外, ArcballControls 还通过双击/点击提供 <i>聚焦</i> 功能,以便直观地将对象的兴趣点移动到虚拟轨迹球的中心。焦点可以在复杂的环境中更好地检查和导航。此外,
+		ArcballControls 允许 FOV 操作(以眩晕方式)和 z 轴旋转。还支持通过剪贴板保存和恢复相机状态(使用 ctrl+c 和 ctrl+v 快捷键复制和粘贴状态)。<br><br>
+
+		不同于 [page:OrbitControls] 和 [page:TrackballControls],[name] 不需要在动画打开时在动画循环中从外部调用 [page:.update]。<br><br>
+
+
+		要使用此功能,与 /examples 目录中的所有文件一样,您必须将该文件单独包含在 HTML 中。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { ArcballControls } from 'three/addons/controls/ArcballControls.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const renderer = new THREE.WebGLRenderer();
+		renderer.setSize( window.innerWidth, window.innerHeight );
+		document.body.appendChild( renderer.domElement );
+
+		const scene = new THREE.Scene();
+
+		const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
+
+		const controls = new ArcballControls( camera, renderer.domElement, scene );
+
+		controls.addEventListener( 'change', function () {
+
+			renderer.render( scene, camera );
+
+		} );
+
+		//controls.update() must be called after any manual changes to the camera's transform
+		camera.position.set( 0, 20, 100 );
+		controls.update();
+		</code>
+
+	<h2>例子</h2>
+
+	<p>[example:misc_controls_arcball misc / controls / arcball ]</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:Camera camera], [param:HTMLDOMElement domElement], [param:Scene scene] )</h3>
+	<p>
+		[page:Camera camera]:(必填)要控制的相机。相机不能是另一个对象的子对象,除非该对象是场景本身。<br><br>
+
+		[page:HTMLDOMElement domElement]: 用于事件侦听器的 HTML 元素。<br><br>
+
+		[page:Scene scene]: 相机渲染的场景。如果未给出,则小控件无法显示。
+	</p>
+
+	<h2>事件</h2>
+
+	<h3>change</h3>
+	<p>
+		当相机被小控件改变时触发。
+	</p>
+
+	<h3>start</h3>
+	<p>
+		当交互开始时触发。
+	</p>
+
+	<h3>end</h3>
+	<p>
+		当交互完成时触发。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Boolean adjustNearFar]</h3>
+	<p>
+		如果为 true,则每次执行缩放时都会调整相机的近端和远端值,尝试保持初始近端和远端值给出的相同可见部分(仅限 [page:PerspectiveCamera] )。默认为 false。
+
+	</p>
+
+	<h3>[property:Camera camera]</h3>
+	<p>
+		被控制的摄像机。
+	</p>
+
+	<h3>[property:Boolean cursorZoom]</h3>
+	<p>
+		设置为 true 以使缩放变为光标居中。
+	</p>
+
+	<h3>
+		[property:Float dampingFactor]</h3>
+	<p>
+		设置为 [page:.enableAnimations] 为true 时使用的阻尼惯性。
+	</p>
+
+	<h3>[property:HTMLDOMElement domElement]</h3>
+	<p>
+		HTMLDOMElement 用于监听鼠标/触摸事件。这必须在构造函数中传递;此处更改它不会设置新的事件侦听器。
+	</p>
+
+	<h3>[property:Boolean enabled]</h3>
+	<p>
+		当设置为 时 `false`,小控件将不再响应用户交互。默认为 `true`。
+	</p>
+
+	<h3>[property:Boolean enableAnimations]</h3>
+	<p>
+		设置为 true 以启用旋转(阻尼)和聚焦操作的动画。默认为 true。
+	</p>
+
+	<h3>[property:Boolean enableGrid]</h3>
+	<p>
+		设置为 true 时,执行平移操作时将出现网格(仅限桌面交互)。默认为 false。
+	</p>
+
+	<h3>[property:Boolean enablePan]</h3>
+	<p>
+		启用或禁用相机平移。默认为 true。
+	</p>
+
+	<h3>[property:Boolean enableRotate]</h3>
+	<p>
+		启用或禁用相机旋转。默认为 true。
+	</p>
+
+	<h3>[property:Boolean enableZoom]</h3>
+	<p>
+		启用或禁用相机变焦。
+	</p>
+
+	<h3>[property:Float focusAnimationTime]</h3>
+	<p>
+		焦点动画的持续时间。
+	</p>
+
+	<h3>[property:Float maxDistance]</h3>
+	<p>
+		最大移动距离(仅限 [page:PerspectiveCamera])。默认为无穷大。
+	</p>
+
+	<h3>[property:Float maxZoom]</h3>
+	<p>
+		最大缩放值(仅限 [page:OrthographicCamera] )。默认为无穷大。
+	</p>
+
+	<h3>[property:Float minDistance]</h3>
+	<p>
+		最小移动距离(仅限 [page:PerspectiveCamera] )。默认值为 0。
+	</p>
+
+	<h3>[property:Float minZoom]</h3>
+	<p>
+		最小缩放值(仅限 [page:OrthographicCamera] )。默认值为 0。
+	</p>
+
+	<h3>[property:Float radiusFactor]</h3>
+	<p>
+		小控件相对于屏幕宽度和高度的大小。默认值为 0.67。
+	</p>
+
+	<h3>[property:Float rotateSpeed]</h3>
+	<p>
+		旋转速度。默认值为 1。
+	</p>
+
+	<h3>[property:Float scaleFactor]</h3>
+	<p>
+		执行缩放操作时使用的缩放因子。
+	</p>
+
+	<h3>[property:Scene scene]</h3>
+	<p>
+		相机渲染的场景。
+	</p>
+
+	<h3>[property:Float wMax]</h3>
+	<p>
+		旋转动画开始时允许的最大角速度。
+	</p>
+
+
+	<h2>方法</h2>
+
+	<h3>[method:undefined activateGizmos] ( [param:Boolean isActive] )</h3>
+	<p>
+		使小控件或多或少可见。
+	</p>
+
+	<h3>[method:undefined copyState] ()</h3>
+	<p>
+		将当前状态复制到剪贴板(作为可读的 JSON 文本)。
+	</p>
+
+	<h3>[method:undefined dispose] ()</h3>
+	<p>
+		删除所有事件侦听器,取消任何待处理的动画并清除场景中的小控件和网格。
+	</p>
+
+	<h3>[method:undefined pasteState] ()</h3>
+	<p>
+		从剪贴板设置控件状态,假设剪贴板存储从 [page:.copyState] 保存的 JSON 文本。
+	</p>
+
+	<h3>[method:undefined reset] ()</h3>
+	<p>
+		将控件重置为上次调用 [page:.saveState] 时的状态或初始状态。
+	</p>
+
+	<h3>[method:undefined saveState] ()</h3>
+	<p>
+		保存控件的当前状态。稍后可以使用 [page:.reset] 恢复。
+	</p>
+
+	<h3>[method:undefined setCamera] ( [param:Camera camera] )</h3>
+	<p>
+		设置要控制的摄像机。必须调用才能设置要控制的新摄像机。
+	</p>
+
+	<h3>[method:undefined setGizmosVisible] ( [param:Boolean value] )</h3>
+	<p>
+		设置小控件的可见属性。
+	</p>
+
+	<h3>[method:undefined setTbRadius] ( [param:Float value] )</h3>
+	<p>
+		更新 `radiusFactor` 值,重新绘制小控件并发送 `changeEvent` 让可视化更新。
+	</p>
+
+	<h3>[method:Boolean setMouseAction] ( [param:String operation], mouse, key )</h3>
+	<p>
+		通过指定要执行的操作和鼠标/按键组合来设置新的鼠标操作。如果发生冲突,则替换现有的。<br><br>
+		操作可以指定为 'ROTATE'、'PAN'、'FOV' 或 'ZOOM'。<br>
+		鼠标输入可以指定为鼠标按钮 0、1、2 或 'WHEEL'。<br>
+		键盘修饰符可以指定为 'CTRL'、'SHIFT' 或 null(如果不再需要) 。
+	</p>
+
+	<h3>[method:Boolean unsetMouseAction] ( mouse, key )</h3>
+	<p>
+		通过指定 鼠标/按键 组合来删除鼠标操作。<br><br>
+		鼠标输入可以指定为鼠标按钮 0、1、2 或 'WHEEL'。<br>
+		键盘修饰符可以指定为 'CTRL'、'SHIFT' 或 null(如果不再需要) 。
+	</p>
+
+	<h3>[method:undefined update] ()</h3>
+	<p>
+		更新控件。必须在对相机变换进行任何手动更改后调用。
+	</p>
+
+	<h3>[method:Raycaster getRaycaster] ()</h3>
+	<p>
+		返回用于用户交互的 [page:Raycaster] 对象。如果设置了 [name] 的 [page:Object3D.layers .layers] 属性,您还需要使用匹配的值设置 [page:Raycaster.layers
+		.layers] 的 [page:Raycaster] 属性,否则 [name] 将无法按预期工作。
+		won't work as expected.
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/ArcballControls.js
+		examples/jsm/controls/ArcballControls.js]
+	</p>
+</body>
+
+</html>

+ 119 - 0
docs/examples/zh/controls/MapControls.html

@@ -0,0 +1,119 @@
+<!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:OrbitControls] &rarr;
+
+	<h1>地图控制器([name])</h1>
+
+	<p class="desc">
+		[name] 旨在从鸟瞰角度在地图上转换相机。该类与 [page:OrbitControls] 共享其实现,但使用特定的预设进行鼠标/触摸交互,并默认禁用屏幕空间平移。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { MapControls } from 'three/addons/controls/MapControls.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const renderer = new THREE.WebGLRenderer();
+		renderer.setSize( window.innerWidth, window.innerHeight );
+		document.body.appendChild( renderer.domElement );
+
+		const scene = new THREE.Scene();
+
+		const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
+		camera.position.set( 0, 20, 100 );
+
+		const controls = new MapControls( camera, renderer.domElement );
+		controls.enableDamping = true;
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			// required if controls.enableDamping or controls.autoRotate are set to true
+			controls.update();
+
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+	<h2>例子</h2>
+
+	<p>[example:misc_controls_map misc / controls / map ]</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:Camera object], [param:HTMLDOMElement domElement] )</h3>
+	<p>
+		[page:Camera object]:(必填)要控制的相机。相机不能是另一个对象的子对象,除非该对象是场景本身。<br><br>
+
+		[page:HTMLDOMElement domElement]: 用于事件侦听器的 HTML 元素。
+	</p>
+
+	<h2>事件</h2>
+
+	<p>有关常见事件,请参阅 [page:OrbitControls] 基类。</p>
+
+	<h2>属性</h2>
+
+	<p>有关常见属性,请参阅 [page:OrbitControls] 基类。</p>
+
+	<h3>
+		[property:Object mouseButtons]</h3>
+	<p>
+		该对象包含对控件使用的鼠标操作的引用。
+		<code>
+controls.mouseButtons = {
+	LEFT: THREE.MOUSE.PAN,
+	MIDDLE: THREE.MOUSE.DOLLY,
+	RIGHT: THREE.MOUSE.ROTATE
+}
+			</code>
+	</p>
+
+	<h3>[property:Boolean screenSpacePanning]</h3>
+	<p>
+		定义平移时摄像机位置的平移方式。如果为 true,相机将在屏幕空间中平移。否则,摄像机将在与摄像机向上方向正交的平面中平移。默认为 `false`。
+	</p>
+
+	<h3>[property:Object touches]</h3>
+	<p>
+		该对象包含对控件使用的触摸操作的引用。
+		<code>
+controls.touches = {
+	ONE: THREE.TOUCH.PAN,
+	TWO: THREE.TOUCH.DOLLY_ROTATE
+}
+			</code>
+	</p>
+
+	<h2>方法</h2>
+
+	<p>有关常用方法,请参阅 [page:OrbitControls]。</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/MapControls.js
+		examples/jsm/controls/MapControls.js]
+	</p>
+</body>
+
+</html>

+ 86 - 0
docs/examples/zh/exporters/DRACOExporter.html

@@ -0,0 +1,86 @@
+<!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>
+	<h1>DRACO导出器([name])</h1>
+
+	<p class="desc">
+		一个用于使用 Draco 库压缩几何体的导出器。<br /><br />
+		[link:https://google.github.io/draco/ Draco] 是一个用于压缩和解压缩 3D 网格和点云的开源库。压缩后的几何体可以显著减小文件大小,但在客户设备上需要额外的解码时间。
+	</p>
+
+	<p>
+		独立的 Draco 文件具有 `.drc` 扩展名,包含顶点位置、法线、颜色和其他属性。Draco 文件不包含材质、纹理、动画或节点层次结构 - 要使用这些功能,请将 Draco 几何体嵌入到 glTF
+		文件中。可以使用 [link:https://github.com/AnalyticalGraphicsInc/gltf-pipeline glTF-Pipeline] 将普通的 glTF 文件转换为经过 Draco 压缩的
+		glTF 文件。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { DRACOExporter } from 'three/addons/exporters/DRACOExporter.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate a exporter
+		const exporter = new DRACOExporter();
+
+		// Parse the input and generate the DRACO encoded output
+		const binaryData = exporter.parse( mesh, options );
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:misc_exporter_draco]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:Int8Array parse]( [param:Mesh object] | [param:Points object], [param:Object options] )</h3>
+
+	<p>
+		[page:Mesh object] | [page:Points object] — 要编码的网格或点。<br />
+		[page:Options options] — 可选的导出选项<br />
+	<ul>
+		<li>decodeSpeed - int. 指示如何根据解码速度调整编码器(0 提供更好的速度,但质量最差)。默认值为 5。</li>
+		<li>encodeSpeed - int. 指示如何调整编码器参数(0 提供更好的速度,但质量最差)。默认值为 5。</li>
+		<li>encoderMethod - int. 顺序(很少压缩)或破边。Edgebreaker 以确定性、螺旋状的方式遍历网格的三角形,这提供了这种数据格式的大部分优点。默认值为
+			DRACOExporter.MESH_EDGEBREAKER_ENCODING。</li>
+		<li>quantization - Array of int. 指示按照顺序(POSITION、NORMAL、COLOR、TEX_COORD、GENERIC)存储在 draco 文件中的每种数据类型的精度。默认为 [ 16, 8,
+			8, 8, 8 ]</li>
+		<li>exportUvs - bool. 布尔值。默认为 true。</li>
+		<li>exportNormals - bool. 布尔值。默认为 true。</li>
+		<li>exportColor - bool. 布尔值。默认为 false。</li>
+	</ul>
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/DRACOExporter.js
+		examples/jsm/exporters/DRACOExporter.js]
+	</p>
+</body>
+
+</html>

+ 96 - 0
docs/examples/zh/exporters/EXRExporter.html

@@ -0,0 +1,96 @@
+<!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>
+	<h1>EXR导出器([name])</h1>
+
+	<p class="desc">
+		一个用于 EXR 的导出器。
+		<br /><br />
+		[link:https://www.openexr.com/ EXR] ( Extended Dynamic Range) 是用于电影行业专业级图像存储格式的
+		[link:https://github.com/AcademySoftwareFoundation/openexr 开放格式规范]。 该格式的目的是准确有效地表示高动态范围的线性场景图像数据和相关的元数据。
+		这个库在需要准确性的主机应用软件中广泛使用,如逼真渲染、纹理访问、图像合成、深度合成和数字中间处理。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { EXRExporter } from 'three/addons/exporters/EXRExporter.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate a exporter
+		const exporter = new EXRExporter();
+
+		// Parse the input render target data and generate the EXR output
+		const EXR = exporter.parse( renderer, renderTarget, options );
+		downloadFile( EXR );
+		</code>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+	</p>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:null parse]( [param:WebGLRenderer renderer], [param:WebGLRenderTarget renderTarget], [param:Object
+		options] )</h3>
+	<p>
+		[page:Function renderTarget] — 包含用于导出 EXR 图像的数据的 WebGLRenderTarget。<br />
+		[page:Options options] — 导出选项<br />
+	<ul>
+		<li>type - 内部 EXR 数据的输出数据类型。可用选项:<br />
+			<code>
+THREE.HalfFloatType // default option
+THREE.FloatType
+					</code>
+		</li>
+		<li>compression - 内部压缩算法。可用选项:<br />
+			<code>
+NO_COMPRESSION
+ZIP_COMPRESSION // default option
+ZIPS_COMPRESSION
+					</code>
+		</li>
+	</ul>
+	</p>
+	<p>
+		从输入的渲染目标生成一个 .exr 输出。
+	</p>
+
+	<h3>[method:null parse]( [param:DataTexture dataTexture], [param:Object options] )</h3>
+	<p>
+		[page:Function dataTexture] — 包含用于导出 EXR 图像的数据的 DataTexture。<br />
+		[page:Options options] — 导出选项(详情见上文)。<br />
+	</p>
+	<p>
+		从输入的数据纹理生成一个 .exr 输出。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/EXRExporter.js
+		examples/jsm/exporters/EXRExporter.js]
+	</p>
+</body>
+
+</html>

+ 157 - 0
docs/examples/zh/exporters/GLTFExporter.html

@@ -0,0 +1,157 @@
+<!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>
+	<h1>GLTF导出器([name])</h1>
+
+	<p class="desc">
+		一个用于 glTF 2.0 的导出器。
+		<br /><br />
+		[link:https://www.khronos.org/gltf glTF] (GL Transmission Format) 是一个用于高效传输和加载3D内容的
+		[link:https://github.com/KhronosGroup/glTF/tree/master/specification/2.0 开放格式规范]。
+		资源可以以 JSON (.gltf) 或二进制 (.glb) 格式提供。外部文件存储纹理 (.jpg, .png) 和额外的二进制数据 (.bin)。一个 glTF
+		资产可以包含一个或多个场景,包括网格、材质、纹理、蒙皮、骨骼、变形目标、动画、灯光和/或相机。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
+		</code>
+
+	<h2>扩展</h2>
+
+	<p>
+		[name] 支持以下 [link:https://github.com/KhronosGroup/glTF/tree/master/extensions/ glTF 2.0 扩展]:
+	</p>
+
+	<ul>
+		<li>KHR_lights_punctual</li>
+		<li>KHR_materials_clearcoat</li>
+		<li>KHR_materials_emissive_strength</li>
+		<li>KHR_materials_ior</li>
+		<li>KHR_materials_iridescence</li>
+		<li>KHR_materials_specular</li>
+		<li>KHR_materials_sheen</li>
+		<li>KHR_materials_transmission</li>
+		<li>KHR_materials_unlit</li>
+		<li>KHR_materials_volume</li>
+		<li>KHR_mesh_quantization</li>
+		<li>KHR_texture_transform</li>
+	</ul>
+
+	<p>
+		以下 glTF 2.0 扩展由外部用户插件支持
+	</p>
+
+	<ul>
+		<li>[link:https://github.com/takahirox/three-gltf-extensions KHR_materials_variants]</li>
+	</ul>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate a exporter
+		const exporter = new GLTFExporter();
+
+		// Parse the input and generate the glTF output
+		exporter.parse(
+			scene,
+			// called when the gltf has been generated
+			function ( gltf ) {
+
+				console.log( gltf );
+				downloadJSON( gltf );
+
+			},
+			// called when there is an error in the generation
+			function ( error ) {
+
+				console.log( 'An error happened' );
+
+			},
+			options
+		);
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:misc_exporter_gltf]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+	</p>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:undefined parse]( [param:Object3D input], [param:Function onCompleted], [param:Function onError],
+		[param:Object options] )</h3>
+
+	<p>
+		[page:Object input] — 要导出的场景或对象。有效选项:<br />
+	<ul>
+		<li>
+			导出场景
+			<code>
+				exporter.parse( scene1, ... )
+		exporter.parse( [ scene1, scene2 ], ... )
+				</code>
+		</li>
+		<li>
+			导出对象(它将创建一个新场景来保存所有对象)
+			<code>
+				exporter.parse( object1, ... )
+		exporter.parse( [ object1, object2 ], ... )
+				</code>
+		</li>
+		<li>
+			混合场景和对象(它将像平常一样导出场景,但会创建一个新场景来保存所有单个对象)。
+			<code>
+				exporter.parse( [ scene1, object1, object2, scene2 ], ... )
+				</code>
+		</li>
+	</ul>
+
+	[page:Function onCompleted] — 导出完成时将被调用。参数将是生成的 glTF JSON 或二进制 ArrayBuffer。<br />
+	[page:Function onError] — 如果在 gltf 生成过程中出现任何错误,将被调用。<br />
+	[page:Options options] — 导出选项<br />
+	<ul>
+		<li>trs - bool. 导出每个节点的位置、旋转和缩放而不是矩阵。默认为 false。</li>
+		<li>onlyVisible - bool. 仅导出可见对象。默认为 true。</li>
+		<li>binary - bool. 以二进制 (.glb) 格式导出,返回 ArrayBuffer。默认为 false。</li>
+		<li>maxTextureSize - int. 将图像最大尺寸(宽度和高度)限制为给定值。默认为无穷大。</li>
+		<li>animations - Array<[page:AnimationClip AnimationClip]>. 要包含在导出中的动画列表。</li>
+		<li>includeCustomExtensions - bool. 导出在对象属性上定义的自定义 glTF 扩展(`userData.gltfExtensions`)。默认为 false。</li>
+	</ul>
+	</p>
+	<p>
+		从输入(场景或对象)生成一个 .gltf(JSON)或 .glb(二进制)输出。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/GLTFExporter.js
+		examples/jsm/exporters/GLTFExporter.js]
+	</p>
+</body>
+
+</html>

+ 68 - 0
docs/examples/zh/exporters/OBJExporter.html

@@ -0,0 +1,68 @@
+<!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>
+	<h1>OBJ导出器([name])</h1>
+
+	<p class="desc">
+		一个用于 [link:https://en.wikipedia.org/wiki/Wavefront_.obj_file OBJ] 文件格式的导出器。
+		<br /><br />
+		[name] 不能将材质数据导出到 MTL 文件中,因此仅支持几何数据。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { OBJExporter } from 'three/addons/exporters/OBJExporter.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate an exporter
+		const exporter = new OBJExporter();
+
+		// Parse the input and generate the OBJ output
+		const data = exporter.parse( scene );
+		downloadFile( data );
+		</code>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+	</p>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:String parse]( [param:Object3D object] )</h3>
+	<p>
+		[page:Object object] — 要导出的 Object3D。
+	</p>
+	<p>
+		生成包含 OBJ 数据的字符串。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/OBJExporter.js
+		examples/jsm/exporters/OBJExporter.js]
+	</p>
+</body>
+
+</html>

+ 76 - 0
docs/examples/zh/exporters/PLYExporter.html

@@ -0,0 +1,76 @@
+<!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>
+	<h1>PLY导出器([name])</h1>
+
+	<p class="desc">
+		一个用于 `PLY` 文件格式的导出器。
+		<br /><br />
+		[link:https://en.wikipedia.org/wiki/PLY_(file_format) PLY] (Polygon or Stanford Triangle Format)
+		是一种用于高效传输和加载简单、静态的3D内容的文件格式,采用紧凑的格式。支持二进制和 ASCII 两种格式。PLY 可以存储顶点位置、颜色、法线和 UV 坐标。不保存纹理或纹理引用。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { PLYExporter } from 'three/addons/exporters/PLYExporter.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate an exporter
+		const exporter = new PLYExporter();
+
+		// Parse the input and generate the ply output
+		const data = exporter.parse( scene, options );
+		downloadFile( data );
+		</code>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+	</p>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:Object parse]( [param:Object3D input], [param:Function onDone], [param:Object options] )</h3>
+	<p>
+		[page:Object input] — Object3D<br />
+		[page:Function onCompleted] — 将在导出完成时调用。参数将是生成的 ply ascii 或二进制 ArrayBuffer。<br />
+		[page:Options options] — 导出选项<br />
+	<ul>
+		<li>excludeAttributes - array. 要从导出的 PLY 文件中显式排除哪些属性。有效值为 'color'、'normal'、'uv' 和 'index'。如果排除三角形索引,则导出点云。默认是一个空数组。
+		</li>
+		<li>binary - bool. 以二进制格式导出,返回 ArrayBuffer。默认为 false。</li>
+	</ul>
+	</p>
+	<p>
+		从输入对象生成 PLY 文件数据作为字符串或 ArrayBuffer(ASCII 或二进制)输出。返回的数据与传递给 "onCompleted" 函数的数据相同。如果对象由多个子元素和几何体组成,它们将在文件中合并为一个单独的网格。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/PLYExporter.js
+		examples/jsm/exporters/PLYExporter.js]
+	</p>
+</body>
+
+</html>

+ 76 - 0
docs/examples/zh/exporters/STLExporter.html

@@ -0,0 +1,76 @@
+<!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>
+	<h1>STL导出器([name])</h1>
+
+	<p class="desc">
+		一个用于 STL 文件格式的导出器。<br /><br />
+		[link:https://en.wikipedia.org/wiki/STL_(file_format) STL] 文件仅描述三维对象的表面几何,不包含颜色、纹理或其他常见的模型属性。STL 格式指定了 ASCII
+		和二进制两种表示方式,其中二进制表示更加紧凑。STL 文件不包含比例信息或索引,单位是任意的。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { STLExporter } from 'three/addons/exporters/STLExporter.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate an exporter
+		const exporter = new STLExporter();
+
+		// Configure export options
+		const options = { binary: true }
+
+		// Parse the input and generate the STL encoded output
+		const result = exporter.parse( mesh, options );
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:misc_exporter_stl]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]()</h3>
+	<p>
+		创建一个新的 [name] 实例。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:Object parse]( [param:Object3D scene], [param:Object options] )</h3>
+
+	<p>
+		[page:Object3D scene] — 场景、网格或其他包含要编码的网格的基于 Object3D 的类。<br />
+		[page:Options options] — 可选的导出选项<br />
+	<ul>
+		<li>binary - bool. 返回 ASCII 编码字符串或二进制数据缓冲区。默认为 `false`。</li>
+	</ul>
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/exporters/STLExporter.js
+		examples/jsm/exporters/STLExporter.js]
+	</p>
+</body>
+
+</html>

+ 73 - 0
docs/examples/zh/geometries/SDFGeometryGenerator.html

@@ -0,0 +1,73 @@
+<!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>
+
+	<h1>SDF几何体生成器([name])</h1>
+
+	<p class="desc">
+		[name] 从有符号距离函数 生成 [page:BufferGeometry] 实例。</br>
+		使用 <a href="https://www.npmjs.com/package/isosurface" target="_blank"> Mikola Lysenko 的等值面。</a>
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { SDFGeometryGenerator } from 'three/addons/geometries/SDFGeometryGenerator.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const generator = new SDFGeometryGenerator( renderer );
+		const sdf = 'float dist( vec3 p ){ return length(p) - 0.5; }' // glsl
+		const geometry = generator.generate( 64, sdf, 1 ); // ~> THREE.BufferGeometry
+		</code>
+
+	<h2>例子</h2>
+
+	<p>[example:webgl_geometry_sdf geometry / sdf ]</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:WebGLRenderer renderer] )</h3>
+
+	<p>
+		[page:WebGLRenderer renderer] -- 用于渲染场景的渲染器。<br />
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:BufferGeometry generate]( [param:Int resolution], [param:String distanceField], [param:Int bounds] )</h3>
+
+	<p>
+		<b>resolution</b> - Int [必填项] 用于三角测量的“体素”数量。必须是 2 的幂。256 之后会变得很重,大多数机器将无法处理超过 512 的数据。默认为 64。
+	</p>
+	<p>
+		<b>distanceField</b> - String [必填项] 具有 glsl 距离函数的字符串。函数名称必须是“dist”,带有 vec3 参数。(参见上面的代码)。默认为球体距离。
+	</p>
+	<p>
+		<b>bounds</b> - Int [可选] 将评估有符号距离字段的边界。默认为 1。
+	</p>
+
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/geometries/SDFGeometry.js
+		examples/jsm/geometries/SDFGeometryGenerator.js]
+	</p>
+</body>
+
+</html>

+ 95 - 0
docs/examples/zh/helpers/VertexTangentsHelper.html

@@ -0,0 +1,95 @@
+<!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:Line] &rarr; [page:LineSegments] &rarr;
+
+	<h1>顶点切线辅助对象([name])</h1>
+
+	<p class="desc">
+		可视化对象的顶点切线。要求已在 [page:BufferAttribute custom attribute] 中指定切线或已使用 [page:BufferGeometry.computeTangents
+		computeTangents] 计算切线。<br /><br />
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { VertexTangentsHelper } from 'three/addons/helpers/VertexTangentsHelper.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const geometry = new THREE.BoxGeometry( 10, 10, 10, 2, 2, 2 );
+		const material = new THREE.MeshStandardMaterial();
+		const mesh = new THREE.Mesh( geometry, material );
+
+		const helper = new VertexTangentsHelper( mesh, 1, 0x00ffff );
+
+		scene.add( mesh );
+		scene.add( helper );
+		</code>
+
+	<h2>例子</h2>
+	<p>
+		[example:webgl_helpers WebGL / helpers]
+	</p>
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]( [param:Object3D object], [param:Number size], [param:Hex color] )</h3>
+	<p>
+		[page:Object3D object] -- 要为其渲染顶点切线的对象。<br />
+		[page:Number size] -- (可选)箭头的长度。默认值为 *1*。<br />
+		[page:Hex color] --(可选)箭头的十六进制颜色。默认值为 *0x00ffff*。
+	</p>
+
+
+	<h2>属性</h2>
+	<p>有关常见属性,请参阅 [page:LineSegments] 基类。</p>
+
+	<h3>[property:Object matrixAutoUpdate]</h3>
+	<p>
+		请参阅 [page:Object3D.matrixAutoUpdate]。默认设置为 `false`,因为正在使用对象的 [page:Object3D.matrixWorld matrixWorld]。
+	</p>
+
+	<h3>[property:Object3D object]</h3>
+	<p>对其顶点切线进行可视化的对象。</p>
+
+	<h3>[property:Number size]</h3>
+	<p>箭头的长度。默认值为 *1*。</p>
+
+
+	<h2>方法</h2>
+	<p>有关常用方法,请参阅 [page:LineSegments] 基类。</p>
+
+
+	<h3>[method:undefined update]()</h3>
+	<p>根据对象的世界变换更新顶点切线预览。</p>
+
+	<h3>[method:undefined dispose]()</h3>
+	<p>
+		释放该实例分配的GPU相关资源。每当您的应用程序中不再使用此实例时,请调用此方法。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/helpers/VertexTangentsHelper.js
+		examples/jsm/helpers/VertexTangentsHelper.js]
+	</p>
+</body>
+
+</html>

+ 279 - 0
docs/examples/zh/loaders/3DMLoader.html

@@ -0,0 +1,279 @@
+<!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:Loader] &rarr;
+	<h1>3DM加载器([name])</h1>
+
+	<p class="desc">
+		用于加载 Rhinoceros 3d 模型文件。<br /><br />
+		Rhinoceros 是一个 3D 建模器,用于创建、编辑、分析、记录、渲染、动画和转换 NURBS 曲线、曲面、breps、挤压、点云以及多边形网格和 SubD 对象。
+		[link:https://github.com/mcneel/rhino3dm rhino3dm.js] 从开源几何库 [link:https://github.com/mcneel/opennurbs openNURBS]
+		编译为 WebAssembly 。加载器当前使用 [link:https://www.npmjs.com/package/rhino3dm/v/8.0.1 rhino3dm.js 8.0.1.]。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { Rhino3dmLoader } from 'three/addons/loaders/3DMLoader.js';
+		</code>
+
+	<h2>支持的转换</h2>
+
+	<p>
+		[name] 将 Rhino 对象转换为以下 three.js 类型:
+	</p>
+
+	<table>
+		<tr>
+			<th>3dm type</th>
+			<th>three.js type</th>
+		</tr>
+		<tr>
+			<td>Point</td>
+			<td>[page:Points Points]</td>
+		</tr>
+		<tr>
+			<td>PointSet / PointCloud</td>
+			<td>[page:Points Points]</td>
+		</tr>
+		<tr>
+			<td>TextDot</td>
+			<td>[page:Sprite Sprite]</td>
+		</tr>
+		<tr>
+			<td>Curve</td>
+			<td>[page:Line Line] <sup> 1</sup></td>
+		</tr>
+		<tr>
+			<td>Mesh</td>
+			<td>[page:Mesh Mesh]</td>
+		</tr>
+		<tr>
+			<td>Extrusion</td>
+			<td>[page:Mesh Mesh]<sup> 2</sup></td>
+		</tr>
+		<tr>
+			<td>BREP</td>
+			<td>[page:Mesh Mesh]<sup> 2</sup></td>
+		</tr>
+		<tr>
+			<td>SubD</td>
+			<td>[page:Mesh Mesh]<sup> 3</sup></td>
+		</tr>
+		<tr>
+			<td>InstanceReferences</td>
+			<td>[page:Object3D Object3D]</td>
+		</tr>
+		<tr>
+			<td>DirectionalLight</td>
+			<td>[page:DirectionalLight DirectionalLight]</td>
+		</tr>
+		<tr>
+			<td>PointLight</td>
+			<td>[page:PointLight PointLight]</td>
+		</tr>
+		<tr>
+			<td>RectangularLight</td>
+			<td>[page:RectAreaLight RectAreaLight]</td>
+		</tr>
+		<tr>
+			<td>SpotLight</td>
+			<td>[page:SpotLight SpotLight]</td>
+		</tr>
+		<tr>
+			<td>File3dm</td>
+			<td>[page:Object3D Object3D]<sup> 4</sup></td>
+		</tr>
+		<tr>
+			<td>Material / Physically Based Material</td>
+			<td>[page:MeshPhysicalMaterial MeshPhysicalMaterial]</td>
+		</tr>
+	</table>
+
+	<h3>注意:</h3>
+
+	<p><i>
+			<sup>1</sup> NURBS 曲线被离散化为硬编码分辨率。
+		</i></p>
+	<p><i>
+			<sup>2</sup> 基于 BREP 和 NURBS 曲面的类型用其“渲染网格”表示。如果渲染网格未在 Rhino 中以适当的显示模式显示(即“着色”、“渲染”等),或者以编程方式创建(例如通过 Grasshopper
+			或直接使用 rhino3dm),则渲染网格可能不会与这些对象关联图书馆。从 [email protected] 开始,BrepFace 和 Extrusions 可以分配网格表示,但这些必须由用户生成。
+		</i></p>
+	<p><i>
+			<sup>3</sup> SubD 对象通过细分其控制网来表示。
+		</i></p>
+	<p><i>
+			<sup>4</sup> 无论 Rhino 文件(File3dm)被加载还是解析,返回的对象都是一个 [page:Object3D
+			Object3D] ,所有 Rhino 对象(File3dmObject)都是子对象。File3dm 图层和其他文件级属性将添加到生成对象的 userData 中。
+		</i></p>
+	<p><i>
+			<sup>5</sup> 所有生成的 Three.js 对象都具有来自填充在其 userData 对象中的 Rhino 对象的有用属性(即图层索引、名称等)。
+		</i></p>
+	<p><i>
+			<sup>6</sup> Rhino 和 Three.js 有不同的坐标系。导入后,您应该将生成的 [page:Object3D Object3D] 在 x 方向上旋转 -90° 或在应用程序开始时设置
+			THREE.Object3D.DEFAULT_UP:
+			<code>THREE.Object3D.DEFAULT_UP.set( 0, 0, 1 );</code>
+			请记住,这将影响应用程序中所有 Object3D 的方向。
+		</i></p>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_loader_3dm]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>Rhino3dmLoader( [param:LoadingManager manager] )</h3>
+	<p>
+		[page:LoadingManager manager] — 加载器使用的 [page:LoadingManager loadingManager]。默认值为 [page:LoadingManager
+		THREE.DefaultLoadingManager]。
+	</p>
+	<p>
+		创建一个新的 Rhino3dmLoader。
+	</p>
+
+	<h2>属性</h2>
+	<p>有关常用属性,请参阅 [page:Loader] 基类。</p>
+
+	<h2>方法</h2>
+	<p>有关常用方法,请参阅 [page:Loader] 基类。</p>
+
+	<h3>[method:Object3D load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function
+		onError] )</h3>
+	<p>
+		[page:String url] — 包含 `.3dm` 文件路径/URL的字符串<br />
+		[page:Function onLoad] — 加载成功完成后调用的函数<br />
+		[page:Function onProgress] — (可选)加载过程中调用的函数。参数将是 XMLHttpRequest 实例,其中包含 .[page:Integer total] 和 .[page:Integer
+		loaded] 字节。如果服务器没有设置 Content-Length,.[page:Integer total] 将为 0。<br />
+		[page:Function onError] — (可选)加载期间发生错误时调用的函数。该函数接收错误作为参数。<br />
+	</p>
+	<p>
+		开始从 url 加载并在解析得到 Object3d 后调用 `onLoad` 。
+	</p>
+
+	<code>
+			// Instantiate a loader
+			const loader = new Rhino3dmLoader();
+	
+			// Specify path to a folder containing WASM/JS libraries or a CDN.
+			// For example, /jsm/libs/rhino3dm/ is the location of the library inside the three.js repository
+			// loader.setLibraryPath( '/path_to_library/rhino3dm/' );
+			loader.setLibraryPath( 'https://unpkg.com/[email protected]/' );
+	
+			// Load a 3DM file
+			loader.load(
+				// resource URL
+				'model.3dm',
+				// called when the resource is loaded
+				function ( object ) {
+	
+					scene.add( object );
+	
+				},
+				// called as loading progresses
+				function ( xhr ) {
+	
+					console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
+	
+				},
+				// called when loading has errors
+				function ( error ) {
+	
+					console.log( 'An error happened' );
+	
+				}
+			);
+			</code>
+
+	<h3>[method:Object3D parse]( [param:ArrayBuffer buffer], [param:Function onLoad], [param:Function onProgress],
+		[param:Function onError] )</h3>
+	<p>
+		[page:ArrayBuffer buffer] — 代表 Rhino `File3dm` 文档的 ArrayBuffer<br />
+		[page:Function onLoad] — 加载成功完成后调用的函数。<br />
+		[page:Function onError] — (可选)加载期间发生错误时调用的函数。该函数接收错误作为参数。<br />
+	</p>
+	<p>
+		解析 File3dm ArrayBuffer 并在得到 Object3d 后调用 `onLoad`。
+		请参阅 [link:https://github.com/mcneel/rhino-developer-samples/tree/7/rhino3dm/js/SampleParse3dmObjects this example]
+		以获取更多参考。
+	</p>
+
+	<code>
+		import rhino3dm from 'https://unpkg.com/[email protected]'
+
+		// Instantiate a loader
+		const loader = new Rhino3dmLoader();
+
+		// Specify path to a folder containing WASM/JS libraries or a CDN.
+		loader.setLibraryPath( 'https://unpkg.com/[email protected]' );
+
+		const rhino = await rhino3dm();
+		console.log('Loaded rhino3dm.');
+
+		// create Rhino Document and add a point to it
+		const doc = new rhino.File3dm();
+		const ptA = [0, 0, 0];
+		const point = new rhino.Point( ptA );
+		doc.objects().add( point, null );
+
+		// create a copy of the doc.toByteArray data to get an ArrayBuffer
+		const buffer = new Uint8Array( doc.toByteArray() ).buffer;
+
+		loader.parse( buffer, function ( object ) {
+
+			scene.add( object );
+
+		} );
+
+		</code>
+
+	<h3>[method:this setLibraryPath]( [param:String value] )</h3>
+	<p>
+		[page:String value] — 包含 JS 和 WASM 库的文件夹的路径。
+	</p>
+	<code>
+		// Instantiate a loader
+		const loader = new Rhino3dmLoader();
+
+		// Specify path to a folder containing the WASM/JS library:
+		loader.setLibraryPath( '/path_to_library/rhino3dm/' );
+		// or from a CDN:
+		loader.setLibraryPath( 'https://unpkg.com/[email protected]' );
+		</code>
+
+	<h3>[method:this setWorkerLimit]( [param:Number workerLimit] )</h3>
+	<p>
+		[page:Number workerLimit] - 要分配的最大 worker 数量。默认值为 4。<br />
+	</p>
+	<p>
+		设置解码期间要使用的 [link:https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
+		Web Workers] 的最大数量。如果 worker 还负责应用程序中的其他任务,则较低的限制可能更好。
+	</p>
+
+	<h3>[method:this dispose]()</h3>
+	<p>
+		处理加载程序资源并释放内存。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/3DMLoader.js
+		examples/jsm/loaders/3DMLoader.js]
+	</p>
+</body>
+
+</html>

+ 142 - 0
docs/examples/zh/loaders/KTX2Loader.html

@@ -0,0 +1,142 @@
+<!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:Loader] &rarr;
+
+	<h1>KTX2加载器([name])</h1>
+
+	<p class="desc">
+		KTX 2.0 GPU 纹理容器的加载程序。<br><br>
+
+		[link:http://github.khronos.org/KTX-Specification/ KTX 2.0] 是各种 GPU 纹理格式的容器格式。该加载器支持 Basis Universal GPU
+		纹理,可以快速转码为多种 GPU 纹理压缩格式。虽然 KTX 2.0 还允许其他特定于硬件的格式,但此加载程序尚未解析它们。
+	</p>
+
+	<p>
+		该加载程序解析 KTX 2.0 容器并将其转码为受支持的 GPU 压缩纹理格式。所需的 WASM 转码器和 JS 包装器可从
+		[link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/basis examples/jsm/libs/basis] 目录中获取。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		var ktx2Loader = new THREE.KTX2Loader();
+		ktx2Loader.setTranscoderPath( 'examples/jsm/libs/basis/' );
+		ktx2Loader.detectSupport( renderer );
+		ktx2Loader.load( 'diffuse.ktx2', function ( texture ) {
+
+			var material = new THREE.MeshStandardMaterial( { map: texture } );
+
+		}, function () {
+
+			console.log( 'onProgress' );
+
+		}, function ( e ) {
+
+			console.error( e );
+
+		} );
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_loader_texture_ktx2]
+	</p>
+
+	<h2>浏览器兼容性</h2>
+
+	<p>
+		该加载器依赖于旧版浏览器不支持的 Web Assembly。
+	</p>
+
+	<br>
+	<hr>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:LoadingManager manager] )</h3>
+	<p>
+		[page:LoadingManager manager] — 供加载器使用的 [page:LoadingManager] 。默认值为 [page:LoadingManager
+		THREE.DefaultLoadingManager]。
+	</p>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+	<p>有关常用属性,请参阅 [page:Loader] 基类</p>
+
+	<h2>Methods</h2>
+	<p>有关常用方法,请参阅 [page:Loader] 基类</p>
+
+	<h3>[method:CompressedTexture load]( [param:String url], [param:Function onLoad], [param:Function onProgress],
+		[param:Function onError] )</h3>
+	<p>
+		[page:String url] — 包含 `.ktx2` 文件路径/URL 的字符串。<br />
+		[page:Function onLoad] — 加载成功完成后调用的函数。<br />
+		[page:Function onProgress] — (可选)加载过程中调用的函数。参数将是 XMLHttpRequest 实例,其中包含 .[page:Integer total] 和 .[page:Integer
+		loaded] 字节。如果服务器没有设置 Content-Length,.[page:Integer total] 将为 0。<br />
+		[page:Function onError] — (可选)加载期间发生错误时调用的函数。该函数接收错误作为参数。<br />
+	</p>
+	<p>
+		从 url 加载并在转码后 [page:CompressedTexture] 调用 `onLoad` 函数。
+	</p>
+
+	<h3>[method:this detectSupport]( [param:WebGLRenderer renderer] )</h3>
+	<p>
+		[page:WebGLRenderer renderer] — 渲染器实例。
+	</p>
+	<p>
+		检测可用压缩纹理格式的硬件支持,以确定转码器的输出格式。必须在加载纹理之前调用。
+	</p>
+
+	<h3>[method:this setTranscoderPath]( [param:String path] )</h3>
+	<p>
+		[page:String path] — 包含 WASM 转码器和 JS 包装器的文件夹路径。
+	</p>
+	<p>
+		WASM 转码器和 JS 包装器可从 [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/basis
+		examples/jsm/libs/basis] 目录中获取。
+	</p>
+
+	<h3>[method:this setWorkerLimit]( [param:Number limit] )</h3>
+	<p>
+		[page:Number limit] — 最大 worker 数量。默认值为 '4'。
+	</p>
+	<p>
+		设置此实例要分配的最大 Web Worker 数量。
+	</p>
+
+	<h3>[method:this dispose]()</h3>
+	<p>
+		处置加载器对象,取消分配创建的所有 Web Worker。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/KTX2Loader.js
+		examples/jsm/loaders/KTX2Loader.js]
+	</p>
+</body>
+
+</html>

+ 219 - 0
docs/examples/zh/loaders/LDrawLoader.html

@@ -0,0 +1,219 @@
+<!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:Loader] &rarr;
+
+	<h1>LDraw加载器([name])</h1>
+
+	<p class="desc"> 用于加载 `LDraw` 资源。<br /><br />
+		[link:https://ldraw.org LDraw] (LEGO Draw) 是一种
+		[link:https://ldraw.org/article/218.html 开放格式规范] ,用于描述 LEGO 和其他建筑套装 3D 模型。</p>
+
+	<p>LDraw 资源(通常扩展名为 .ldr、.dat 或 .txt 的文本文件)可以仅描述单个构造件或整个模型。对于模型,LDraw 文件可以引用其他 LDraw 文件,这些文件是从使用 [page:Function
+		setPartsLibraryPath] 设置的库路径加载的。您通常下载 LDraw 官方零件库,解压到一个文件夹并将 setPartsLibraryPath 指向它。
+	</p>
+
+	<p>库部件将通过子文件夹“parts”、“p” 和 “models” 中的反复试验来加载。这些文件访问对于 Web 环境来说并不是最佳的,因此我们制作了一个脚本工具来将 LDraw
+		文件及其所有依赖项打包到一个文件中,这样加载速度会更快。请参阅“打包 LDraw 模型”部分。LDrawLoader 示例加载多个打包文件。由于官方零件库较大,因此不包含在内。</p>
+
+	<h2>Import</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { LDrawLoader } from 'three/addons/loaders/LDrawLoader.js';
+		</code>
+
+	<h2>扩展</h2>
+
+	<p>
+		[name] 支持以下扩展:
+	</p>
+
+	<ul>
+		<li>!COLOUR: 颜色和表面光洁度声明。</li>
+		<li>BFC: 背面剔除规范。</li>
+		<li>!CATEGORY: 模型/零件类别声明。</li>
+		<li>!KEYWORDS: 模型/零件关键字声明。</li>
+	</ul>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Instantiate a loader
+		const loader = new LDrawLoader();
+
+		// Optionally set library parts path
+		// loader.setPartsLibraryPath( path to library );
+
+		// Load a LDraw resource
+		loader.load(
+			// resource URL
+			'models/car.ldr_Packed.mpd',
+			// called when the resource is loaded
+			function ( group ) {
+
+				// Optionally, use LDrawUtils.mergeObject() from
+				// 'examples/jsm/utils/LDrawUtils.js' to merge all
+				// geometries by material (it gives better runtime
+				// performance, but building steps are lost)
+				// group = LDrawUtils.mergeObject( group );
+
+				scene.add( group );
+
+			},
+			// called while loading is progressing
+			function ( xhr ) {
+
+				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
+
+			},
+			// called when loading has errors
+			function ( error ) {
+
+				console.log( 'An error happened' );
+
+			}
+		);
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_loader_ldraw]
+	</p>
+
+	<h2>包装 LDraw 模型</h2>
+
+	<p>要将模型及其所有引用文件打包,请下载 [link:https://www.ldraw.org/parts/latest-parts.html 官方 LDraw 零件库]
+		并使用以下节点脚本:[link:https://github.com/mrdoob/three.js/blob/master/utils/packLDrawModel.js utils/packLDrawModel.js]
+		它包含有关如何设置文件和执行文件的说明。</p>
+
+	<h2>.userData 中的元数据</h2>
+
+	<p>[name] 返回一个包含对象层次结构的 [page:Group] 。根据每个子对象类型,其 .userData 成员将包含以下成员:<br />
+		在 [page:Group]中, userData 成员将包含:<br />
+	<ul>
+		<li>.numBuildingSteps: 仅在 [page:Group] 根部,表示模型中构建步骤的总数。这些可用于设置对象的可见性以显示不同的构建步骤,这在示例中完成。</li>
+		<li>.buildingStep: 表示该步骤的构建索引。</li>
+		<li>.category: 如果不为空,则包含该作品或模型的 [page:String] 类别。</li>
+		<li>.keywords: 如果不为空,则包含该作品或模型的 [page:String] 关键字数组。</li>
+	</ul>
+	</p>
+	<p>在 [page:Material] 中,userData 成员将包含:
+	<ul>
+		<li>.code: 表示该材料的 LDraw 代码。</li>
+		<li>.edgeMaterial: 仅在 [page:Mesh] 材质中,表示属于相同颜色代码的边的 [page:LineBasicMaterial] (在LDraw格式中,每个表面材质也与一个边缘材质相关)</li>
+		<li>.conditionalEdgeMaterial: 仅在 [page:LineSegments] 材质中,表示属于相同颜色代码的条件边的 [page:Material]。</li>
+	</ul>
+	</p>
+
+	<br>
+	<hr>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:LoadingManager manager] )</h3>
+	<p>
+		[page:LoadingManager manager] — 加载器使用的 [page:LoadingManager loadingManager] 。默认值为 [page:LoadingManager
+		THREE.DefaultLoadingManager]。
+	</p>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>Properties</h2>
+	<p>有关公共属性,请参阅 [page:Loader] 基类。</p>
+
+	<h2>Methods</h2>
+	<p>有关常用方法,请参阅 [page:Loader] 基类。</p>
+
+	<h3>[method:undefined load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function
+		onError] )</h3>
+	<p>
+		[page:String url] — 包含 LDraw 文件的路径/URL 的字符串。<br />
+		[page:Function onLoad] — 加载成功完成后调用的函数。该函数接收从 [page:Function parse] 返回的加载的 JSON 响应。<br />
+		[page:Function onProgress] — (可选)加载过程中调用的函数。参数将是 XMLHttpRequest 实例,其中包含 .[page:Integer total] 和 .[page:Integer
+		loaded] 字节。如果服务器没有设置 Content-Length,.[page:Integer total] 将为 0。<br />
+		[page:Function onError] — (可选)加载期间发生错误时调用的函数。该函数接收错误作为参数。<br />
+	</p>
+	<p>
+		开始从 url 加载并使用解析的响应内容调用回调函数。
+	</p>
+
+	<h3>[method:this setPartsLibraryPath]( [param:String path] )</h3>
+	<p>
+		[page:String path] — 用于加载引用零件的库零件文件的路径。这与 [page:Loader.setPath] 不同,后者指示加载主资源的路径。<br />
+	</p>
+	<p>
+		必须在 [page:.load] 之前调用此方法,除非要加载的模型不引用库部件(通常它将是一个所有部件都打包在单个文件中的模型)。
+	</p>
+
+	<h3>[method:this setFileMap]( [param:Map fileMap] )</h3>
+	<p>
+		[page:Map map] — 设置从 [page:String] 到 [page:String] 的映射,将引用的库文件名映射到新文件名。如果未指定
+		fileMap(默认值),则将通过子文件夹“parts”、“p”和“models”中的反复试验来访问库部件。
+	</p>
+
+	<h3>[method:undefined parse]( [param:String text], [param:String path], [param:Function onLoad], [param:Function
+		onError] )</h3>
+	<p>
+		[page:String text] — 要解析的 LDraw 资源,作为字符串。<br />
+		[page:String path] — 从中查找其他引用的 LDraw 资源文件的基本路径。<br />
+		[page:Function onLoad] — 解析完成时调用的函数。<br />
+	</p>
+	<p>
+		将 LDraw 文件内容解析为字符串,并在完成时触发 [page:Function onLoad] 回调。[page:Function onLoad] 的参数将是一个 [page:Group],其中包含
+		[page:Group]、[page:Mesh] 和
+		[page:LineSegments] 的层次结构(以及 .userData 字段中的其他零件数据)。
+	</p>
+
+	<h3>[method:Material getMaterial]( [param:String colourCode] )</h3>
+	<p>
+		[page:String colourCode] — 用于获取关联 [page:Material] 的颜色代码。
+	</p>
+
+	<h3>[method:String getMainMaterial]()</h3>
+	<p>
+		返回主 LDraw 颜色的 [page:Material]。
+	</p>
+
+	<p>对于已加载的 LDraw 资源,返回与主颜色代码关联的 [page:Material]。此方法可用于修改模型或暴露模型的零件的主要材质。
+	</p>
+
+	<p>
+		主要颜色代码是为 LDraw
+		零件着色的标准方法。三角形为“16”,边为“24”。通常,完整的模型不会暴露主要颜色(也就是说,没有零件在顶层使用代码“16”,因为它们被分配了其他特定颜色)另一方面,LDraw零件文件将暴露代码“16”可以着色,并且可以有附加的固定颜色。
+	</p>
+
+	<h3>[method:String getMainEdgeMaterial]()</h3>
+	<p>
+		返回边缘主 LDraw 颜色的 [page:Material]。
+	</p>
+
+	<h3>[method:void preloadMaterials]( [param:String path] )</h3>
+	<p>
+		[page:String path] — LDraw 材料资源的路径。
+	</p>
+
+	<p>此异步方法从单个 LDraw 文件预加载材质。在官方零件库中,有一个特殊文件,始终首先加载(LDConfig.ldr)并包含所有标准颜色代码。此方法旨在与未打包的文件一起使用,例如在预加载材料并按需加载部件的编辑器中。</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/LDrawLoader.js
+		examples/jsm/loaders/LDrawLoader.js]
+	</p>
+</body>
+
+</html>

+ 117 - 0
docs/examples/zh/loaders/PDBLoader.html

@@ -0,0 +1,117 @@
+<!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:Loader] &rarr;
+
+	<h1>PDB加载器([name])</h1>
+
+	<p class="desc">用于加载 `.pdb` 资源的加载器。<br>
+		[link:http://en.wikipedia.org/wiki/Protein_Data_Bank_(file_format) 蛋白质数据库] 文件格式是描述分子三​​维结构的文本文件。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { PDBLoader } from 'three/addons/loaders/PDBLoader.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// instantiate a loader
+		const loader = new PDBLoader();
+
+		// load a PDB resource
+		loader.load(
+			// resource URL
+			'models/pdb/caffeine.pdb',
+			// called when the resource is loaded
+			function ( pdb ) {
+
+				const geometryAtoms = pdb.geometryAtoms;
+				const geometryBonds = pdb.geometryBonds;
+				const json = pdb.json;
+
+				console.log( 'This molecule has ' + json.atoms.length + ' atoms' );
+
+			},
+			// called when loading is in progresses
+			function ( xhr ) {
+
+				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
+
+			},
+			// called when loading has errors
+			function ( error ) {
+
+				console.log( 'An error happened' );
+
+			}
+		);
+		</code>
+
+	<h2>例子</h2>
+	<p>
+		[example:webgl_loader_pdb]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:LoadingManager manager] )</h3>
+	<p>
+		[page:LoadingManager manager] — 加载器使用的 [page:LoadingManager loadingManager] 。默认值为 [page:LoadingManager
+		THREE.DefaultLoadingManager]。
+	</p>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+	<p>有关公共属性,请参阅 [page:Loader] 基类。</p>
+
+	<h2>Methods</h2>
+	<p>有关常用方法,请参阅 [page:Loader] 基类。</p>
+
+	<h3>[method:undefined load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function
+		onError] )</h3>
+	<p>
+		[page:String url] — 包含 `.pdb` 文件路径/URL 的字符串。<br />
+		[page:Function onLoad] — (可选)加载成功完成后调用的函数。该函数接收具有以下属性的对象。[page:BufferGeometry geometryAtoms]、[page:BufferGeometry
+		geometryBonds] 和 [page:Object JSON] 结构。<br />
+		[page:Function onProgress] — (可选)加载过程中调用的函数。参数将是 XMLHttpRequest 实例,其中包含 .[page:Integer total] 和 .[page:Integer
+		loaded] 字节。如果服务器没有设置 Content-Length,.[page:Integer total] 将为 0。<br />
+		[page:Function onError] —(可选)加载期间发生错误时调用的函数。该函数接收错误作为参数。<br />
+	</p>
+	<p>
+		开始从 url 加载并使用解析后的响应内容调用 onLoad。
+	</p>
+
+	<h3>[method:Object parse]( [param:String text] )</h3>
+	<p>
+		[page:String text] — 要解析的文本 `pdb` 结构。
+	</p>
+	<p>
+		解析 `pdb` 文本并返回JSON结构。<br />
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/loaders/PDBLoader.js
+		examples/jsm/loaders/PDBLoader.js]
+	</p>
+</body>
+
+</html>

+ 141 - 0
docs/examples/zh/math/Lut.html

@@ -0,0 +1,141 @@
+<!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>
+	<h1>查找表([name])</h1>
+
+	<p class="desc">
+		表示色彩映射的查找表。它用于从一系列数据值中确定颜色值。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { Lut } from 'three/addons/math/Lut.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const lut = new Lut( 'rainbow', 512 );
+		const color = lut.getColor( 0.5 );
+		</code>
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]( [param:String colormap], [param:Number count] )</h3>
+	<p>
+		colormap - 设置预定义色彩映射中的一个。可用的色彩映射有:`rainbow`, `cooltowarm`, `blackbody`,
+		`grayscale`。 默认为 `rainbow`。<br />
+		count - 设置用于表示数据数组的颜色数量。默认为 `32`。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Array lut]</h3>
+	<p>
+		所选颜色映射的查找表,表示为 [page:Color] 数组。
+	</p>
+
+	<h3>[property:Array map]</h3>
+	<p>
+		当前选择的颜色映射。默认是 `rainbow`。
+	</p>
+
+	<h3>[property:Number minV]</h3>
+	<p>
+		用查找表表示的最小值。默认值为 *0*。
+	</p>
+
+	<h3>[property:Number maxV]</h3>
+	<p>
+		用查找表表示的最大值。默认值为 *1*。
+	</p>
+
+	<h3>[property:Number n]</h3>
+	<p>
+		当前所选颜色映射的颜色数。默认为 `32`。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:this copy]( [param:Lut lut] ) [param:Lut this]</h3>
+	<p>
+		color — Lut 复制。
+	</p>
+	<p>
+		复制给定的 lut。
+	</p>
+
+	<h3>[method:this addColorMap]( [param:String name], [param:Array arrayOfColors] )</h3>
+	<p>
+		name — 颜色映射的名称。<br />
+		arrayOfColors — 颜色值数组。每个值都是一个数组,其中包含阈值和十六进制数形式的实际颜色值。
+	</p>
+	<p>
+		将一个颜色映射添加到此 [name] 实例。
+	</p>
+
+	<h3>[method:HTMLCanvasElement createCanvas]()</h3>
+	<p>
+		创建一个画布以将查找表可视化为纹理。
+	</p>
+
+	<h3>[method:Color getColor]( [param:Number alpha] )</h3>
+	<p>
+		value -- 要显示为颜色的数据值。
+	</p>
+	<p>
+		返回给定数据值的 [page:Color] 实例。
+	</p>
+
+	<h3>[method:this setColorMap]( [param:String colormap], [param:Number count] )</h3>
+	<p>
+		colormap — 颜色映射的名称。<br />
+		count — 颜色的数量。默认为 `32`。
+	</p>
+	<p>
+		为给定的颜色映射和颜色数量配置查找表。
+	</p>
+
+	<h3>[method:this setMin]( [param:Number minV] )</h3>
+	<p>
+		minV — 用查找表表示的最小值
+	</p>
+	<p>
+		设置此 [name] 的表示最小值。
+	</p>
+
+	<h3>[method:this setMax]( [param:Number maxV] )</h3>
+	<p>
+		maxV — 用查找表表示的最大值。
+	</p>
+	<p>
+		设置此 [name] 的表示最大值。
+	</p>
+
+	<h3>[method:HTMLCanvasElement updateCanvas]( [param:HTMLCanvasElement canvas] )</h3>
+	<p>
+		使用 [name] 的数据更新画布。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/Lut.js examples/jsm/math/Lut.js]
+	</p>
+</body>
+
+</html>

+ 98 - 0
docs/examples/zh/math/MeshSurfaceSampler.html

@@ -0,0 +1,98 @@
+<!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>
+	<h1>网格表面采样器([name])</h1>
+
+	<p class="desc">用于在网格表面上对加权随机点进行采样的实用类。</p>
+
+	<p>加权采样对于诸如地形的特定区域内更浓密的植被生长或来自网格特定部分的浓缩粒子排放等效果非常有用。顶点权重可以通过编程方式编写,也可以在 3D 工具(如 Blender)中作为顶点颜色手工绘制。</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { MeshSurfaceSampler } from 'three/addons/math/MeshSurfaceSampler.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		// Create a sampler for a Mesh surface.
+		const sampler = new MeshSurfaceSampler( surfaceMesh )
+			.setWeightAttribute( 'color' )
+			.build();
+
+		const mesh = new THREE.InstancedMesh( sampleGeometry, sampleMaterial, 100 );
+
+		const position = new THREE.Vector3();
+		const matrix = new THREE.Matrix4();
+
+		// Sample randomly from the surface, creating an instance of the sample
+		// geometry at each sample point.
+		for ( let i = 0; i < 100; i ++ ) {
+
+			sampler.sample( position );
+
+			matrix.makeTranslation( position.x, position.y, position.z );
+
+			mesh.setMatrixAt( i, matrix );
+
+		}
+
+		scene.add( mesh );
+		</code>
+
+	<h2>例子</h2>
+	<p>
+		[example:webgl_instancing_scatter]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:Mesh mesh] )</h3>
+	<p>
+		[page:Mesh mesh] — ​​进行采样的表面网格。
+	</p>
+	<p>
+		创建一个新的 [name]。如果输入的几何体是索引的,将创建一个非索引的副本。构造后,采样器无法返回样本,直到调用 [page:MeshSurfaceSampler.build build] 方法。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:this setWeightAttribute]( [param:String name] )</h3>
+	<p>
+		指定从表面采样时用作权重的顶点属性。权重较高的面更有可能被采样,而权重为零的面则根本不会被采样。对于向量属性,仅使用 <i>.x</i> 进行采样。
+	</p>
+	<p>如果没有选择权重属性,则按区域随机分布采样。</p>
+
+	<h3>[method:this build]()</h3>
+	<p>
+		处理输入几何体并准备返回样本。几何体或采样器的任何配置都必须在调用此方法之前进行。对于具有 <i>n</i> 个面的曲面,时间复杂度为<i>O(n)</i>。
+	</p>
+
+	<h3>[method:this sample]( [param:Vector3 targetPosition], [param:Vector3 targetNormal], [param:Color targetColor],
+		[param:Vector2 targetUV] )</h3>
+	<p>
+		选择输入几何体表面上的随机点,返回该点的位置以及可选的法线向量、颜色和 UV 坐标。对于具有 <i>n</i> 个面的曲面,时间复杂度为<i>O(n)</i>。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/MeshSurfaceSampler.js
+		examples/jsm/math/MeshSurfaceSampler.js]
+	</p>
+</body>
+
+</html>

+ 193 - 0
docs/examples/zh/math/OBB.html

@@ -0,0 +1,193 @@
+<!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>
+	<h1>定向包围盒([name])</h1>
+
+	<p class="desc">
+		表示三维空间中的定向包围盒(OBB)。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { OBB } from 'three/addons/math/OBB.js';
+		</code>
+
+	<h2>例子</h2>
+	<p>
+		[example:webgl_math_obb]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+	<p>
+		[page:Vector3 center] — [name] 的中心。(可选)<br />
+		[page:Vector3 halfSize] — [name] 沿每个轴的正半宽范围。(可选)<br />
+		[page:Matrix3 rotation] — [name] 的旋转。(可选)
+	</p>
+	<p>
+		创建一个新的 [name]。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Vector3 center]</h3>
+	<p>
+		[name] 的中心。默认值为 *( 0, 0, 0 )*。
+	</p>
+
+	<h3>[property:Vector3 halfSize]</h3>
+	<p>
+		[name] 沿每个轴的正半宽范围。默认值为 *( 0, 0, 0 )*。
+	</p>
+
+	<h3>[property:Matrix3 rotation]</h3>
+	<p>
+		[name] 的旋转。默认为单位矩阵。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:this applyMatrix4]( [param:Matrix4 matrix] )</h3>
+	<p>
+		[page:Matrix4 matrix] — 一个 4x4 变换矩阵。
+	</p>
+	<p>
+		将给定的变换矩阵应用于此 [name]。此方法可用于将包围体与 3D 对象的世界矩阵进行转换,以保持两个实体同步。
+	</p>
+
+	<h3>[method:Vector3 clampPoint]( [param:Vector3 point], [param:Vector3 clampedPoint] )</h3>
+	<p>
+		[page:Vector3 point] — 应该被限制在该 [name] 范围内的点。<br />
+		[page:Matrix3 clampedPoint] — 结果将被复制到此向量中。
+	</p>
+	<p>
+		将给定点限制在该 [name] 范围内。
+	</p>
+
+	<h3>[method:OBB clone]()</h3>
+	<p>
+		创建此实例的克隆。
+	</p>
+
+	<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
+	<p>
+		[page:Vector3 point] — 要测试的点。
+	</p>
+	<p>
+		给定点是否位于此 [name] 内。
+	</p>
+
+	<h3>[method:this copy]( [param:OBB obb] )</h3>
+	<p>
+		[page:OBB obb] — 要复制的 [name]。
+	</p>
+	<p>
+		将给定 [name] 的属性复制到此 [name]。
+	</p>
+
+	<h3>[method:Boolean equals]( [param:OBB obb] )</h3>
+	<p>
+		[page:OBB obb] — 要测试的 [name]。
+	</p>
+	<p>
+		给定的 [name] 是否等于此 [name]。
+	</p>
+
+	<h3>[method:this fromBox3]( [param:Box3 box3] )</h3>
+	<p>
+		[page:Box3 box3] — AABB
+	</p>
+	<p>
+		根据给定的 AABB 定义 [name]。
+	</p>
+
+	<h3>[method:Vector3 getSize]( [param:Vector3 size] )</h3>
+	<p>
+		[page:Vector3 size] — 结果将被复制到此向量中。
+	</p>
+	<p>
+		将此 [name] 的大小返回到给定向量中。
+	</p>
+
+	<h3>[method:Boolean intersectsBox3]( [param:Box3 box3] )</h3>
+	<p>
+		[page:Box3 box3] — 要测试的 AABB。
+	</p>
+	<p>
+		给定的 AABB 是否与此 [name] 相交。
+	</p>
+
+	<h3>[method:Boolean intersectsSphere]( [param:Sphere sphere] )</h3>
+	<p>
+		[page:Sphere sphere] — 要测试的边界球体。
+	</p>
+	<p>
+		给定的边界球体是否与此 [name] 相交。
+	</p>
+
+	<h3>[method:Boolean intersectsOBB]( [param:OBB obb], [param:Number epsilon] )</h3>
+	<p>
+		[page:OBB obb] — 要测试的 [name]<br />
+		[page:Number epsilon] — 一个可选的数值,用于抵消算术错误。默认为 `Number.EPSILON`。
+	</p>
+	<p>
+		给定的 [name] 是否与此 [name] 相交。
+	</p>
+
+	<h3>[method:Boolean intersectsPlane]( [param:Plane plane] )</h3>
+	<p>
+		[page:Plane plane] — 要测试的平面。
+	</p>
+	<p>
+		给定平面是否与此 [name] 相交。
+	</p>
+
+	<h3>[method:Boolean intersectsRay]( [param:Ray ray] )</h3>
+	<p>
+		[page:Ray ray] — 要测试的射线。
+	</p>
+	<p>
+		给定射线是否与此 [name] 相交。
+	</p>
+
+	<h3>[method:Vector3 intersectRay]( [param:Ray ray], [param:Vector3 intersectionPoint] )</h3>
+	<p>
+		[page:Ray ray] — 要测试的射线。<br />
+		[page:Vector3 intersectionPoint] — 结果将被复制到此向量中。
+	</p>
+	<p>
+		执行 射线/OBB 相交测试并将相交点存储到给定的 3D 向量。如果没有检测到交叉点则返回 `null`。
+	</p>
+
+	<h3>[method:this set]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+	<p>
+		[page:Vector3 center] — [name] 的中心。<br />
+		[page:Vector3 halfSize] — [name] 沿每个轴的正半宽范围。<br />
+		[page:Matrix3 rotation] — [name] 的旋转。
+	</p>
+	<p>
+		定义给定值的 [name]。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/OBB.js examples/jsm/math/OBB.js]
+	</p>
+</body>
+
+</html>

+ 238 - 0
docs/examples/zh/math/convexhull/ConvexHull.html

@@ -0,0 +1,238 @@
+<!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>
+	<h1>凸包([name])</h1>
+
+	<p class="desc">
+		凸包类。Quickhull 算法的实现者: Dirk Gregorius。2014 年 3 月,游戏开发者大会:
+		[link:http://media.steampowered.com/apps/valve/2014/DirkGregorius_ImplementingQuickHull.pdf Implementing QuickHull]
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { ConvexHull } from 'three/addons/math/ConvexHull.js';
+		</code>
+
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]()</h3>
+	<p>
+		创建一个 [name] 实例。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:VertexList assigned]</h3>
+	<p>
+		该 [page:VertexList vertex list] 包含分配给面的所有顶点。默认是一个空的顶点列表。
+	</p>
+
+	<h3>[property:Array faces]</h3>
+	<p>
+		生成的凸包面。默认是一个空数组。
+	</p>
+
+	<h3>[property:Array newFaces]</h3>
+	<p>
+		该数组保存在单次迭代中生成的面。默认是一个空数组。
+	</p>
+
+	<h3>[property:Float tolerance]</h3>
+	<p>
+		用于内部比较运算的 epsilon 值。该值的计算取决于几何形状的大小。默认值为 -1。
+	</p>
+
+	<h3>[property:VertexList unassigned]</h3>
+	<p>
+		该 [page:VertexList vertex list] 包含未分配给面的所有顶点。默认是一个空的顶点列表。
+	</p>
+
+	<h3>[property:Array vertices]</h3>
+	<p>
+		给定几何数据的内部表示( [page:VertexNode vertices] 数组)。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:HalfEdge addAdjoiningFace]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
+	<p>
+		[page:VertexNode eyeVertex] - 添加到凸包的顶点。<br />
+		[page:HalfEdge horizonEdge] - 地平线的单个边缘。<br /><br />
+
+
+		创建一个面,顶点顺序为 'eyeVertex.point'、'horizonEdge.tail' 和 'horizonEdge.head',顺序为逆时针(CCW)。所有半边按照逆时针顺序创建,因此该面始终指向凸包的外部。
+	</p>
+
+	<h3>[method:this addNewFaces]( [param:VertexNode eyeVertex], [param:HalfEdge horizonEdge] )</h3>
+	<p>
+		[page:VertexNode eyeVertex] - 添加到凸包的顶点。<br />
+		[page:HalfEdge horizon] - 形成地平线的半边数组。<br /><br />
+
+		将 'horizon.length' 个面添加到凸包,每个面将与地平线上的相对面以及左/右相邻的面连接。
+	</p>
+
+	<h3>[method:this addVertexToFace]( [param:VertexNode vertex], [param:Face face] )</h3>
+	<p>
+		[page:VertexNodeNode vertex] - 要添加的顶点。<br />
+		[page:Face face] - 目标面。<br /><br />
+
+		将一个顶点添加到 “分配(assigned)” 顶点列表,并将其分配给给定的面。
+	</p>
+
+	<h3>[method:this addVertexToHull]( [param:VertexNode eyeVertex] )</h3>
+	<p>
+		[page:VertexNode eyeVertex] - 添加到凸包的顶点。<br /><br />
+
+		使用以下算法向凸包添加顶点:
+	<ul>
+		<li>计算“地平线”,这是一系列半边。要使一条边属于这个组,它必须是连接一个能够看到 'eyeVertex' 的面和一个不能看到 'eyeVertex' 的面的边。</li>
+		<li>所有能够看到 'eyeVertex' 的面都会从已分配顶点列表中移除其可见的顶点。</li>
+		<li>使用“地平线”和 'eyeVertex' 的每条边创建一组新的面。每个面都与相对的地平线面以及左/右相邻的面连接。</li>
+		<li>从所有可见面中移除的顶点将被分配给新的面,如果可能的话。</li>
+	</ul>
+	</p>
+
+	<h3>[method:this cleanup]()</h3>
+
+	<p>计算凸包后清理内部属性。</p>
+
+	<h3>[method:this compute]()</h3>
+
+	<p>开始执行快速凸包算法。</p>
+
+	<h3>[method:Object computeExtremes]()</h3>
+
+	<p>计算将用于计算初始凸包的极值(最小/最大向量)。</p>
+
+	<h3>[method:this computeHorizon]( [param:Vector3 eyePoint], [param:HalfEdge crossEdge], [param:Face face],
+		[param:Array horizon] )</h3>
+	<p>
+		[page:Vector3 eyePoint] - 点的 3D 坐标。<br />
+		[page:HalfEdge crossEdge] - 用于跳转到当前面的边。<br />
+		[page:Face face] - 当前正在测试的面。<br />
+		[page:Array horizon] - 按 CCW 顺序构成地平线一部分的边。<br /><br />
+
+
+		计算一个逆时针(CCW)顺序的半边链,称为“地平线”('horizon')。要使一条边成为地平线的一部分,它必须连接一个能够看到 'eyePoint' 的面和一个不能看到 'eyePoint' 的面。
+	</p>
+
+	<h3>[method:this computeInitialHull]()</h3>
+
+	<p>计算初始的单纯形,将所有可能成为凸包一部分的点分配给其面。
+	</p>
+
+	<h3>[method:this containsPoint]( [param:Vector3 point] )</h3>
+	<p>
+		[page:Vector3 point] - 3D 空间中的一个点。<br /><br />
+
+		返回 `true` 如果定点在此凸包内。
+	</p>
+
+	<h3>[method:this deleteFaceVertices]( [param:Face face], [param:Face absorbingFace] )</h3>
+	<p>
+		[page:Face face] - 给定的面。<br />
+		[page:Face absorbingFace] - 尝试吸收第一个面的顶点的可选面。<br /><br />
+
+		删除 “面(face)” 能够看到的所有可见顶点。
+	<ul>
+		<li>如果 'absorbingFace' 不存在,则所有已移除的顶点将被添加到 'unassigned' 顶点列表。
+		</li>
+		<li>如果 'absorbingFace' 存在,则该方法将分配所有 'face' 中可以看到 'absorbingFace' 的顶点。
+		</li>
+		<li>如果一个顶点不能看到 'absorbingFace',则将其添加到 'unassigned' 顶点列表。</li>
+	</ul>
+	</p>
+
+	<h3>[method:Vector3 intersectRay]( [param:Ray ray], [param:Vector3 target] )</h3>
+	<p>
+		[page:Ray ray] - 给定的射线。<br />
+		[page:Vector3 target] - 表示交点的目标向量。<br /><br />
+
+		与该凸包执行光线相交测试。如果没有找到交集则返回 `null`。
+	</p>
+
+	<h3>[method:Boolean intersectsRay]( [param:Ray ray] )</h3>
+	<p>
+		[page:Ray ray] - 给定的射线。<br /><br />
+
+		返回给的射线是否与此凸包相交,如果相交则返回 `true`。
+	</p>
+
+	<h3>[method:this makeEmpty]()</h3>
+
+	<p>使这个凸包为空。</p>
+
+	<h3>[method:VertexNode nextVertexToAdd]()</h3>
+
+	<p>查找下一个顶点以使用当前凸包创建面。
+	<ul>
+		<li>让初始面成为存在于“已分配(assigned)”顶点列表中的第一个面。</li>
+		<li>如果面不存在则返回,因为没有顶点剩下。</li>
+		<li>否则,对于面能看到的每个顶点,找到距离它最远的那个。</li>
+	</ul>
+	</p>
+
+	<h3>[method:this reindexFaces]()</h3>
+
+	<p>从内部面列表中移除不活跃的(例如已删除的)面。</p>
+
+	<h3>[method:VertexNode removeAllVerticesFromFace]( [param:Face face] )</h3>
+	<p>
+		[page:Face face] - 给定的面。<br /><br />
+
+		移除给定面能够看到的所有可见顶点,这些顶点存储在“已分配(assigned)”顶点列表中。
+	</p>
+
+	<h3>[method:this removeVertexFromFace]( [param:VertexNode vertex], [param:Face face] )</h3>
+	<p>
+		[page:VertexNode vertex] - 要删除的顶点。<br />
+		[page:Face face] - 目标面。<br /><br />
+
+		从“已分配”顶点列表和给定面中移除一个顶点。在移除后,它还确保从“面”到它在“已分配”中看到的第一个顶点的链接被正确连接。
+	</p>
+
+	<h3>[method:this resolveUnassignedPoints]( [param:Array newFaces] )</h3>
+	<p>
+		[page:Face newFaces] - 一组新的面。<br /><br />
+
+		尽可能从未分配的顶点列表中重新分配顶点给新的面。
+	</p>
+
+	<h3>[method:this setFromObject]( [param:Object3D object] )</h3>
+	<p>
+		[page:Object3D object] - 用于计算凸包的 [page:Object3D]<br /><br />
+
+		计算 [page:Object3D] 的凸包(包括其子元素),考虑到对象及其子元素的世界变换。
+	</p>
+
+	<h3>[method:this setFromPoints]( [param:Array points] )</h3>
+	<p>
+		[page:Array points] - 生成的凸包将包含的 [page:Vector3 Vector3s] 数组<br /><br />
+
+		计算给定点数组的凸包。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/ConvexHull.js
+		examples/jsm/math/ConvexHull.js]
+	</p>
+</body>
+
+</html>

+ 111 - 0
docs/examples/zh/math/convexhull/Face.html

@@ -0,0 +1,111 @@
+<!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>
+	<h1>面([name])</h1>
+
+	<p class="desc">
+		表示由特定数量的半边界定的部分。当前的实现假设一个面始终由三个边组成。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { Face } from 'three/addons/math/ConvexHull.js';
+		</code>
+
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]()</h3>
+	<p>
+		创建一个 [name] 实例。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Vector3 normal]</h3>
+	<p>
+		面的法线向量。默认值为(0, 0, 0) 处的 [page:Vector3]。
+	</p>
+
+	<h3>[property:Vector3 midpoint]</h3>
+	<p>
+		面的中点或质心。默认值为(0, 0, 0) 处的 [page:Vector3]。
+	</p>
+
+	<h3>[property:Float area]</h3>
+	<p>
+		面的面积。默认值为 0。
+	</p>
+
+	<h3>[property:Float constant]</h3>
+	<p>
+		从面到原点的有符号距离。默认值为 0。
+	</p>
+
+	<h3>[property:VertexNode outside]</h3>
+	<p>
+		引用该面可以看到的顶点列表中的顶点。默认为 null。
+	</p>
+
+	<h3>[property:Integer mark]</h3>
+	<p>
+		标记面部是否可见或已删除。默认为 'Visible'。
+	</p>
+
+	<h3>[property:HalfEdge edge]</h3>
+	<p>
+		对面的基边的引用。要检索所有边,您可以使用当前边的 “next” 引用。默认为空。 null.
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:Face create]( [param:VertexNode a], [param:VertexNode b], [param:VertexNode c] )</h3>
+	<p>
+		[page:VertexNode a] - 面的第一个顶点。<br />
+		[page:VertexNode b] - 面的第二个顶点。<br />
+		[page:VertexNode c] - 面的第三个顶点。<br /><br />
+
+		创建一个 [name]。
+	</p>
+
+	<h3>[method:HalfEdge getEdge]( [param:Integer i] )</h3>
+	<p>
+		[page:Integer i] - 边的索引<br /><br />
+
+		返回给定索引的边。
+	</p>
+
+	<h3>[method:this compute] ()</h3>
+
+	<p>计算面的所有属性。</p>
+
+	<h3>[method:Float distanceToPoint]( [param:Vector3 point] )</h3>
+	<p>
+		[page:Vector3 point] - 3D 空间中的任何点。<br /><br />
+
+		返回从给定点到该面的平面表示的带符号距离。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/ConvexHull.js
+		examples/jsm/math/ConvexHull.js]
+	</p>
+</body>
+
+</html>

+ 98 - 0
docs/examples/zh/math/convexhull/HalfEdge.html

@@ -0,0 +1,98 @@
+<!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>
+	<h1>半边([name])</h1>
+
+	<p class="desc">
+		半边数据结构的基础,也被称为双连通边列表 (DCEL)。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { HalfEdge } from 'three/addons/math/ConvexHull.js';
+		</code>
+
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]( [param:VertexNode vertex], [param:Face face] )</h3>
+	<p>
+		[page:VertexNode vertex] - [page:VertexNode] 对其目标顶点的引用<br />
+		[page:Face face] - [page:Face] 对其面的引用<br /><br />
+
+		创建一个 [name] 实例。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:VertexNode vertex]</h3>
+	<p>
+		对目标顶点的引用。可以通过查询其孪生顶点或前一个半边的目的地来获得原点。默认值 undefined。
+	</p>
+
+	<h3>[property:HalfEdge prev]</h3>
+	<p>
+		对同一面的前一半边的引用。默认值为 null。
+	</p>
+
+	<h3>[property:HalfEdge next]</h3>
+	<p>
+		对同一面的下一半边的引用。默认值为 null。
+	</p>
+
+	<h3>[property:HalfEdge twin]</h3>
+	<p>
+		对应到达相对面的孪生半边的引用。默认值为 null。
+	</p>
+
+	<h3>[property:Face face]</h3>
+	<p>
+		每个半边限定一个面,因此具有对该面的引用。默认值 undefined。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:VertexNode head]()</h3>
+	<p>返回目标顶点。</p>
+
+	<h3>[method:VertexNode tail]()</h3>
+	<p>返回原点顶点</p>
+
+	<h3>[method:Float length]()</h3>
+	<p>返回边的 [link:https://en.wikipedia.org/wiki/Euclidean_distance 欧几里得长度]
+		(直线长度)。</p>
+
+	<h3>[method:Float lengthSquared]()</h3>
+	<p>返回边的 [link:https://en.wikipedia.org/wiki/Euclidean_distance 欧几里得长度]
+		(直线长度)的平方。</p>
+
+	<h3>[method:this setTwin]( [param:HalfEdge edge] )</h3>
+	<p>
+		[page:HalfEdge edge] - 任何半边缘。<br /><br />
+
+		设置这个半边的孪生边。还确保给定半边的孪生引用被正确设置。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/ConvexHull.js
+		examples/jsm/math/ConvexHull.js]
+	</p>
+</body>
+
+</html>

+ 117 - 0
docs/examples/zh/math/convexhull/VertexList.html

@@ -0,0 +1,117 @@
+<!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>
+	<h1>顶点列表([name])</h1>
+
+	<p class="desc">
+		顶点的双向链表。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { VertexList } from 'three/addons/math/ConvexHull.js';
+		</code>
+
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]()</h3>
+	<p>
+		创建一个 [name] 实例。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:VertexNode head]</h3>
+	<p>
+		引用链表的第一个顶点。默认为 null。
+	</p>
+
+	<h3>[property:VertexNode tail]</h3>
+	<p>
+		引用链表的最后一个顶点。默认为 null。
+	</p>
+
+	<h2>方法</h2>
+
+	<h3>[method:VertexNode first]()</h3>
+	<p>返回头引用。</p>
+
+	<h3>[method:VertexNode last]()</h3>
+	<p>返回尾部引用。</p>
+
+	<h3>[method:this clear]()</h3>
+	<p>清除链接列表。</p>
+
+	<h3>[method:this insertBefore]( [param:Vertex target], [param:Vertex vertex] )</h3>
+	<p>
+		[page:Vertex target] - 目标顶点。假设该顶点属于链表。<br />
+		[page:Vertex vertex] - 要插入的顶点。<br /><br />
+
+		在目标顶点 <strong>之前</strong> 插入一个顶点。
+	</p>
+
+	<h3>[method:this insertAfter]( [param:Vertex target], [param:Vertex vertex] )</h3>
+	<p>
+		[page:Vertex target] - 目标顶点。假设该顶点属于链表。<br />
+		[page:Vertex vertex] - 要插入的顶点。<br /><br />
+
+		在目标顶点 <strong>之后</strong> 插入一个顶点。
+	</p>
+
+	<h3>[method:this append]( [param:Vertex vertex] )</h3>
+	<p>
+		[page:Vertex vertex] - 要追加的顶点。<br /><br />
+
+		将一个顶点追加到链表的末尾。
+	</p>
+
+	<h3>[method:this appendChain]( [param:Vertex vertex] )</h3>
+	<p>
+		[page:Vertex vertex] - 顶点链的头顶点。<br /><br />
+
+		添加一个顶点链,其中给定顶点是头。
+	</p>
+
+	<h3>[method:this remove]( [param:Vertex vertex] )</h3>
+	<p>
+		[page:Vertex vertex] - 要删除的顶点。<br /><br />
+
+		从链表中删除一个顶点。
+	</p>
+
+	<h3>[method:this removeSubList]( [param:Vertex a], [param:Vertex b] )</h3>
+	<p>
+		[page:Vertex a] - 子列表的头部。<br />
+		[page:Vertex b] - 子列表的尾部。<br /><br />
+
+		从链接列表中删除顶点的子列表。
+	</p>
+
+	<h3>[method:Boolean isEmpty]()</h3>
+
+	<p>如果链表为空则返回 true。</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/ConvexHull.js
+		examples/jsm/math/ConvexHull.js]
+	<p>
+</body>
+
+</html>

+ 69 - 0
docs/examples/zh/math/convexhull/VertexNode.html

@@ -0,0 +1,69 @@
+<!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>
+	<h1>顶点节点([name])</h1>
+
+	<p class="desc">
+		一个顶点作为双链表节点。
+	</p>
+
+	<h2>Import</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+			import { VertexNode } from 'three/addons/math/ConvexHull.js';
+		</code>
+
+
+	<h2>构造函数</h2>
+
+
+	<h3>[name]( [param:Vector3 point] )</h3>
+	<p>
+		[page:Vector3 point] - [page:Vector3] 3D 空间中的点 (x, y, z)。<br /><br />
+
+		创建一个 [name] 实例。
+	</p>
+
+	<h2>属性</h2>
+
+	<h3>[property:Vector3 point]</h3>
+	<p>
+		3D 空间中的点 (x, y, z)。默认值 undefined。
+	</p>
+
+	<h3>[property:VertexNode prev]</h3>
+	<p>
+		引用双链表中的前一个顶点。默认为 null。
+	</p>
+
+	<h3>[property:VertexNode next]</h3>
+	<p>
+		引用双链表中的下一个顶点。默认为 null。
+	</p>
+
+	<h3>[property:Face face]</h3>
+	<p>
+		对能够看到该顶点的面的引用。默认值为 undefined。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/ConvexHull.js
+		examples/jsm/math/ConvexHull.js]
+	<p>
+</body>
+
+</html>

+ 110 - 0
docs/examples/zh/misc/Timer.html

@@ -0,0 +1,110 @@
+<!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>
+
+	<h1>定时器([name])</h1>
+
+	<p class="desc">
+		此类是 [page:Clock] 的替代品,具有不同的 API 设计和行为。目标是避免随着时间的推移 [page:Clock] 中变得明显的概念缺陷。
+
+	<ul>
+		<li>[name] 具有 [page:.update]() 方法,用于更新其内部状态。这使得可以在模拟步骤中多次调用 [page:.getDelta]() 和 [page:.getElapsed]() 而不会得到不同的值。
+		</li>
+		<li>该类使用页面可见性 API(Page Visibility API),以避免在应用程序处于非活动状态(例如切换标签或浏览器隐藏)时出现大的时间差值。</li>
+	</ul>
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import { Timer } from 'three/addons/misc/Timer.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		const timer = new Timer();
+
+		function animate( timestamp ) {
+
+			requestAnimationFrame( animate );
+
+			// timestamp is optional
+			timer.update( timestamp );
+
+			const delta = timer.getDelta();
+
+			// do something with delta
+
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+	<h2>例子</h2>
+
+	<p>
+		[example:webgl_morphtargets_sphere WebGL / morphtargets / sphere]
+	</p>
+
+	<h2>构造函数</h2>
+
+	<h3>Timer()</h3>
+
+	<h2>方法</h2>
+
+	<h3>[method:Number getDelta]()</h3>
+	<p>
+		返回以秒为单位的时间增量。
+	</p>
+
+	<h3>[method:Number getElapsed]()</h3>
+	<p>
+		返回经过的时间(以秒为单位)。
+	</p>
+
+	<h3>[method:this setTimescale]( [param:Number timescale] )</h3>
+	<p>
+		设置一个时间刻度,缩放 [page:.update]() 中的时间增量。
+	</p>
+
+	<h3>[method:this reset]()</h3>
+	<p>
+		重置当前模拟步骤的时间计算。
+	</p>
+
+	<h3>[method:this dispose]()</h3>
+	<p>
+		可用于释放所有内部资源。通常在不再需要计时器实例时调用。
+	</p>
+
+	<h3>[method:this update]( [param:Number timestamp] )</h3>
+	<p>
+		timestamp -- (可选)当前时间(以毫秒为单位)。可以从
+		[link:https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame requestAnimationFrame]
+		回调参数中获取 。如果未提供,当前时间将由 [link:https://developer.mozilla.org/en-US/docs/Web/API/Performance/now performance.now]
+		确定。<br /><br />
+
+		更新定时器的内部状态。该方法应该在每个模拟步骤以及对计时器执行查询之前调用一次(例如通过 [page:.getDelta]())。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/misc/Timer.js examples/jsm/misc/Timer.js]
+	</p>
+</body>
+
+</html>

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

@@ -40,6 +40,7 @@
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_godrays postprocessing godrays]<br />
 			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_gtao postprocessing gtao]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
 			[example:webgl_postprocessing_masking postprocessing masking]<br />
+			[example:webgl_postprocessing_material_ao postprocessing material ao]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_outline postprocessing outline]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_pixel postprocessing pixelate]<br />
 			[example:webgl_postprocessing_procedural postprocessing procedural]<br />
 			[example:webgl_postprocessing_procedural postprocessing procedural]<br />

+ 46 - 0
docs/examples/zh/utils/CameraUtils.html

@@ -0,0 +1,46 @@
+<!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>
+	<h1>相机工具([name])</h1>
+
+	<p class="desc">
+		包含用于相机操作的有用实用函数的类。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons].
+	</p>
+
+	<code>
+			import * as CameraUtils from 'three/addons/utils/CameraUtils.js';
+		</code>
+
+	<h2>方法</h2>
+
+	<h3>[method:undefined frameCorners]( [param:PerspectiveCamera camera], [param:Vector3 bottomLeftCorner],
+		[param:Vector3 bottomRightCorner], [param:Vector3 topLeftCorner], [param:boolean estimateViewFrustum] )</h3>
+	<p>
+		使用 [link:https://web.archive.org/web/20191110002841/http://csc.lsu.edu/~kooima/articles/genperspective/index.html
+		Kooima 广义透视投影公式].
+		注意:此功能忽略标准参数;在此之后不要调用 updateProjectionMatrix() ! toJSON也不会捕获此函数生成的离轴矩阵。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/utils/CameraUtils.js
+		examples/jsm/utils/CameraUtils.js]
+	</p>
+</body>
+
+</html>

+ 117 - 0
docs/examples/zh/webxr/XREstimatedLight.html

@@ -0,0 +1,117 @@
+<!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:Group] &rarr;
+
+	<h1>XR估计光照([name])</h1>
+
+	<p class="desc">
+		[name] 使用 WebXR 的光照估计来创建光探针、定向光和(可选)模拟用户真实世界环境和照明的环境图。<br>
+		当 WebXR 更新光照和环境估计时,[name] 会自动更新光照探针、定向光和环境贴图。<br><br>
+
+		在创建 WebXR 会话时,将光照估计指定为可选或必需的功能非常重要,否则光照估计将无法工作。<br><br>
+
+		有关浏览器兼容性信息,请参阅 <a
+			href="https://developer.mozilla.org/en-US/docs/Web/API/XRLightProbe#browser_compatibility">此处</a>,因为这仍然是 WebXR
+		中的一个实验功能。<br><br>
+
+		要使用它,就像 /examples 目录中的所有文件一样,您必须在HTML中单独包含该文件。
+	</p>
+
+	<h2>引入</h2>
+
+	<p>
+		[name] 是一个附加组件,必须显式导入。请参阅 [link:#manual/introduction/Installation Installation / Addons]。
+	</p>
+
+	<code>
+		import { XREstimatedLight } from 'three/addons/webxr/XREstimatedLight.js';
+		</code>
+
+	<h2>代码示例</h2>
+
+	<code>
+		renderer.xr.enabled = true;
+
+		// Don't add the XREstimatedLight to the scene initially.
+		// It doesn't have any estimated lighting values until an AR session starts.
+		const xrLight = new XREstimatedLight( renderer );
+
+		xrLight.addEventListener( 'estimationstart' , () => {
+
+			scene.add( xrLight );
+
+			if ( xrLight.environment ) {
+
+				scene.environment = xrLight.environment;
+
+			}
+
+		} );
+
+		xrLight.addEventListener( 'estimationend', () => {
+
+			scene.remove( xrLight );
+
+			scene.environment = null;
+
+		} );
+
+		// In order for lighting estimation to work, 'light-estimation' must be included as either
+		// an optional or required feature.
+		document.body.appendChild( XRButton.createButton( renderer, {
+			optionalFeatures: [ 'light-estimation' ]
+		} ) );
+		</code>
+
+	<h2>例子</h2>
+
+	<p>[example:webxr_ar_lighting webxr / light estimation]</p>
+
+	<h2>构造函数</h2>
+
+	<h3>[name]( [param:WebGLRenderer renderer], [param:Boolean environmentEstimation] )</h3>
+	<p>
+		[page:WebGLRenderer renderer]: (必需)用于渲染场景的渲染器。主要用于与 WebXRManager 交互。<br><br>
+
+		environmentEstimation: 如果 `true`,则使用 WebXR 来估计环境地图。
+	</p>
+
+	<h2>事件</h2>
+
+	<h3>estimationstart</h3>
+	<p>
+		当估计的照明值开始更新时触发。
+	</p>
+
+	<h3>estimationend</h3>
+	<p>
+		当估计的照明值停止更新时触发。
+	</p>
+
+	<h2>Properties</h2>
+
+	<h3>[property:Texture environment]</h3>
+	<p>
+		WebXR 估计的环境地图。仅当 environmentEstimation 为 时,此选项才可用 `true`。<br><br>
+
+		它可以用作 [page:Scene.environment] 的 [page:MeshStandardMaterial.envMap] 或 [page:Scene.background]。
+	</p>
+
+	<h2>源代码</h2>
+
+	<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/webxr/XREstimatedLight.js
+		examples/jsm/webxr/XREstimatedLight.js]
+	</p>
+</body>
+
+</html>

+ 59 - 8
docs/list.json

@@ -368,6 +368,8 @@
 				"GLTFLoader": "examples/en/loaders/GLTFLoader",
 				"GLTFLoader": "examples/en/loaders/GLTFLoader",
 				"KTX2Loader": "examples/en/loaders/KTX2Loader",
 				"KTX2Loader": "examples/en/loaders/KTX2Loader",
 				"LDrawLoader": "examples/en/loaders/LDrawLoader",
 				"LDrawLoader": "examples/en/loaders/LDrawLoader",
+				"LUT3dlLoader": "examples/en/loaders/LUT3dlLoader",
+				"LUTCubeLoader": "examples/en/loaders/LUTCubeLoader",
 				"MMDLoader": "examples/en/loaders/MMDLoader",
 				"MMDLoader": "examples/en/loaders/MMDLoader",
 				"MTLLoader": "examples/en/loaders/MTLLoader",
 				"MTLLoader": "examples/en/loaders/MTLLoader",
 				"OBJLoader": "examples/en/loaders/OBJLoader",
 				"OBJLoader": "examples/en/loaders/OBJLoader",
@@ -770,7 +772,8 @@
 				"如何创建VR内容": "manual/zh/introduction/How-to-create-VR-content",
 				"如何创建VR内容": "manual/zh/introduction/How-to-create-VR-content",
 				"如何使用后期处理": "manual/zh/introduction/How-to-use-post-processing",
 				"如何使用后期处理": "manual/zh/introduction/How-to-use-post-processing",
 				"矩阵变换": "manual/zh/introduction/Matrix-transformations",
 				"矩阵变换": "manual/zh/introduction/Matrix-transformations",
-				"动画系统": "manual/zh/introduction/Animation-system"
+				"动画系统": "manual/zh/introduction/Animation-system",
+				"色彩管理": "manual/zh/introduction/Color-management"
 			}
 			}
 
 
 		},
 		},
@@ -818,6 +821,7 @@
 				"Animation": "api/zh/constants/Animation",
 				"Animation": "api/zh/constants/Animation",
 				"Core": "api/zh/constants/Core",
 				"Core": "api/zh/constants/Core",
 				"CustomBlendingEquation": "api/zh/constants/CustomBlendingEquations",
 				"CustomBlendingEquation": "api/zh/constants/CustomBlendingEquations",
+				"BufferAttributeUsage": "api/zh/constants/BufferAttributeUsage",
 				"Materials": "api/zh/constants/Materials",
 				"Materials": "api/zh/constants/Materials",
 				"Renderer": "api/zh/constants/Renderer",
 				"Renderer": "api/zh/constants/Renderer",
 				"Textures": "api/zh/constants/Textures"
 				"Textures": "api/zh/constants/Textures"
@@ -1008,6 +1012,7 @@
 			},
 			},
 
 
 			"物体": {
 			"物体": {
+				"BatchedMesh": "api/zh/objects/BatchedMesh",
 				"Bone": "api/zh/objects/Bone",
 				"Bone": "api/zh/objects/Bone",
 				"Group": "api/zh/objects/Group",
 				"Group": "api/zh/objects/Group",
 				"InstancedMesh": "api/zh/objects/InstancedMesh",
 				"InstancedMesh": "api/zh/objects/InstancedMesh",
@@ -1023,12 +1028,12 @@
 			},
 			},
 
 
 			"渲染器": {
 			"渲染器": {
-				"WebGL3DRenderTarget": "api/zh/renderers/WebGL3DRenderTarget",
-				"WebGLArrayRenderTarget": "api/zh/renderers/WebGLArrayRenderTarget",
 				"WebGLMultipleRenderTargets": "api/zh/renderers/WebGLMultipleRenderTargets",
 				"WebGLMultipleRenderTargets": "api/zh/renderers/WebGLMultipleRenderTargets",
 				"WebGLRenderer": "api/zh/renderers/WebGLRenderer",
 				"WebGLRenderer": "api/zh/renderers/WebGLRenderer",
 				"WebGL1Renderer": "api/zh/renderers/WebGL1Renderer",
 				"WebGL1Renderer": "api/zh/renderers/WebGL1Renderer",
 				"WebGLRenderTarget": "api/zh/renderers/WebGLRenderTarget",
 				"WebGLRenderTarget": "api/zh/renderers/WebGLRenderTarget",
+				"WebGL3DRenderTarget": "api/zh/renderers/WebGL3DRenderTarget",
+				"WebGLArrayRenderTarget": "api/zh/renderers/WebGLArrayRenderTarget",
 				"WebGLCubeRenderTarget": "api/zh/renderers/WebGLCubeRenderTarget"
 				"WebGLCubeRenderTarget": "api/zh/renderers/WebGLCubeRenderTarget"
 			},
 			},
 
 
@@ -1054,8 +1059,8 @@
 				"CompressedTexture": "api/zh/textures/CompressedTexture",
 				"CompressedTexture": "api/zh/textures/CompressedTexture",
 				"CompressedArrayTexture": "api/zh/textures/CompressedArrayTexture",
 				"CompressedArrayTexture": "api/zh/textures/CompressedArrayTexture",
 				"CubeTexture": "api/zh/textures/CubeTexture",
 				"CubeTexture": "api/zh/textures/CubeTexture",
-				"DataArrayTexture": "api/zh/textures/DataArrayTexture",
 				"Data3DTexture": "api/zh/textures/Data3DTexture",
 				"Data3DTexture": "api/zh/textures/Data3DTexture",
+				"DataArrayTexture": "api/zh/textures/DataArrayTexture",
 				"DataTexture": "api/zh/textures/DataTexture",
 				"DataTexture": "api/zh/textures/DataTexture",
 				"DepthTexture": "api/zh/textures/DepthTexture",
 				"DepthTexture": "api/zh/textures/DepthTexture",
 				"FramebufferTexture": "api/zh/textures/FramebufferTexture",
 				"FramebufferTexture": "api/zh/textures/FramebufferTexture",
@@ -1066,12 +1071,20 @@
 
 
 		},
 		},
 
 
-		"Addons": {
+		"附加": {
+
+			"动画": {
+				"CCDIKSolver": "examples/zh/animations/CCDIKSolver",
+				"MMDAnimationHelper": "examples/zh/animations/MMDAnimationHelper",
+				"MMDPhysics": "examples/zh/animations/MMDPhysics"
+			},
 
 
 			"控制": {
 			"控制": {
+				"ArcballControls": "examples/zh/controls/ArcballControls",
 				"DragControls": "examples/zh/controls/DragControls",
 				"DragControls": "examples/zh/controls/DragControls",
 				"FirstPersonControls": "examples/zh/controls/FirstPersonControls",
 				"FirstPersonControls": "examples/zh/controls/FirstPersonControls",
 				"FlyControls": "examples/zh/controls/FlyControls",
 				"FlyControls": "examples/zh/controls/FlyControls",
+				"MapControls": "examples/zh/controls/MapControls",
 				"OrbitControls": "examples/zh/controls/OrbitControls",
 				"OrbitControls": "examples/zh/controls/OrbitControls",
 				"PointerLockControls": "examples/zh/controls/PointerLockControls",
 				"PointerLockControls": "examples/zh/controls/PointerLockControls",
 				"TrackballControls": "examples/zh/controls/TrackballControls",
 				"TrackballControls": "examples/zh/controls/TrackballControls",
@@ -1082,14 +1095,16 @@
 				"ConvexGeometry": "examples/zh/geometries/ConvexGeometry",
 				"ConvexGeometry": "examples/zh/geometries/ConvexGeometry",
 				"DecalGeometry": "examples/zh/geometries/DecalGeometry",
 				"DecalGeometry": "examples/zh/geometries/DecalGeometry",
 				"ParametricGeometry": "examples/zh/geometries/ParametricGeometry",
 				"ParametricGeometry": "examples/zh/geometries/ParametricGeometry",
-				"TextGeometry": "examples/zh/geometries/TextGeometry"
+				"TextGeometry": "examples/zh/geometries/TextGeometry",
+				"SDFGeometryGenerator": "examples/zh/geometries/SDFGeometryGenerator"
 			},
 			},
 
 
 			"辅助对象": {
 			"辅助对象": {
 				"LightProbeHelper": "examples/zh/helpers/LightProbeHelper",
 				"LightProbeHelper": "examples/zh/helpers/LightProbeHelper",
 				"PositionalAudioHelper": "examples/zh/helpers/PositionalAudioHelper",
 				"PositionalAudioHelper": "examples/zh/helpers/PositionalAudioHelper",
 				"RectAreaLightHelper": "examples/zh/helpers/RectAreaLightHelper",
 				"RectAreaLightHelper": "examples/zh/helpers/RectAreaLightHelper",
-				"VertexNormalsHelper": "examples/zh/helpers/VertexNormalsHelper"
+				"VertexNormalsHelper": "examples/zh/helpers/VertexNormalsHelper",
+				"VertexTangentsHelper": "examples/zh/helpers/VertexTangentsHelper"
 			},
 			},
 
 
 			"灯光": {
 			"灯光": {
@@ -1097,13 +1112,17 @@
 			},
 			},
 
 
 			"加载器": {
 			"加载器": {
+				"3DMLoader": "examples/zh/loaders/3DMLoader",
+				"DRACOLoader": "examples/zh/loaders/DRACOLoader",
 				"FontLoader": "examples/zh/loaders/FontLoader",
 				"FontLoader": "examples/zh/loaders/FontLoader",
-				"DracoLoader": "examples/zh/loaders/DracoLoader",
 				"GLTFLoader": "examples/zh/loaders/GLTFLoader",
 				"GLTFLoader": "examples/zh/loaders/GLTFLoader",
+				"KTX2Loader": "examples/zh/loaders/KTX2Loader",
+				"LDrawLoader": "examples/zh/loaders/LDrawLoader",
 				"MMDLoader": "examples/zh/loaders/MMDLoader",
 				"MMDLoader": "examples/zh/loaders/MMDLoader",
 				"MTLLoader": "examples/zh/loaders/MTLLoader",
 				"MTLLoader": "examples/zh/loaders/MTLLoader",
 				"OBJLoader": "examples/zh/loaders/OBJLoader",
 				"OBJLoader": "examples/zh/loaders/OBJLoader",
 				"PCDLoader": "examples/zh/loaders/PCDLoader",
 				"PCDLoader": "examples/zh/loaders/PCDLoader",
+				"PDBLoader": "examples/en/loaders/PDBLoader",
 				"SVGLoader": "examples/zh/loaders/SVGLoader",
 				"SVGLoader": "examples/zh/loaders/SVGLoader",
 				"TGALoader": "examples/zh/loaders/TGALoader"
 				"TGALoader": "examples/zh/loaders/TGALoader"
 			},
 			},
@@ -1116,6 +1135,33 @@
 				"EffectComposer": "examples/zh/postprocessing/EffectComposer"
 				"EffectComposer": "examples/zh/postprocessing/EffectComposer"
 			},
 			},
 
 
+			"导出器": {
+				"DRACOExporter": "examples/zh/exporters/DRACOExporter",
+				"EXRExporter": "examples/zh/exporters/EXRExporter",
+				"GLTFExporter": "examples/zh/exporters/GLTFExporter",
+				"OBJExporter": "examples/zh/exporters/OBJExporter",
+				"PLYExporter": "examples/zh/exporters/PLYExporter",
+				"STLExporter": "examples/zh/exporters/STLExporter"
+			},
+
+			"数学库": {
+				"LookupTable": "examples/zh/math/Lut",
+				"MeshSurfaceSampler": "examples/zh/math/MeshSurfaceSampler",
+				"OBB": "examples/zh/math/OBB"
+			},
+
+			"杂项": {
+				"Timer": "examples/zh/misc/Timer"
+			},
+
+			"凸包": {
+				"Face": "examples/zh/math/convexhull/Face",
+				"HalfEdge": "examples/zh/math/convexhull/HalfEdge",
+				"ConvexHull": "examples/zh/math/convexhull/ConvexHull",
+				"VertexNode": "examples/zh/math/convexhull/VertexNode",
+				"VertexList": "examples/zh/math/convexhull/VertexList"
+			},
+
 			"渲染器": {
 			"渲染器": {
 				"CSS2DRenderer": "examples/zh/renderers/CSS2DRenderer",
 				"CSS2DRenderer": "examples/zh/renderers/CSS2DRenderer",
 				"CSS3DRenderer": "examples/zh/renderers/CSS3DRenderer",
 				"CSS3DRenderer": "examples/zh/renderers/CSS3DRenderer",
@@ -1124,8 +1170,13 @@
 
 
 			"实用工具": {
 			"实用工具": {
 				"BufferGeometryUtils": "examples/zh/utils/BufferGeometryUtils",
 				"BufferGeometryUtils": "examples/zh/utils/BufferGeometryUtils",
+				"CameraUtils": "examples/zh/utils/CameraUtils",
 				"SceneUtils": "examples/zh/utils/SceneUtils",
 				"SceneUtils": "examples/zh/utils/SceneUtils",
 				"SkeletonUtils": "examples/zh/utils/SkeletonUtils"
 				"SkeletonUtils": "examples/zh/utils/SkeletonUtils"
+			},
+
+			"WebXR": {
+				"XREstimatedLight": "examples/zh/webxr/XREstimatedLight"
 			}
 			}
 
 
 		},
 		},

+ 2 - 2
docs/manual/en/introduction/Installation.html

@@ -87,7 +87,7 @@ npm install --save-dev vite
 							npm uses <i>package.json</i> to describe which versions of each dependency you've installed. If you have other people working on the project with you, they can install the original versions of each dependency simply by running <i>npm install</i>. If you're using version history, commit <i>package.json</i>.
 							npm uses <i>package.json</i> to describe which versions of each dependency you've installed. If you have other people working on the project with you, they can install the original versions of each dependency simply by running <i>npm install</i>. If you're using version history, commit <i>package.json</i>.
 						</p>
 						</p>
 						<p>
 						<p>
-							npm the code for each dependency in a new <i>node_modules/</i> folder. When Vite builds your application, it sees imports for 'three' and pulls three.js files automatically from this folder. The <i>node_modules/</i> folder is used only during development, and shouldn't be uploaded to your web hosting provider or committed to version history.
+							npm installs the code for each dependency in a new <i>node_modules/</i> folder. When Vite builds your application, it sees imports for 'three' and pulls three.js files automatically from this folder. The <i>node_modules/</i> folder is used only during development, and shouldn't be uploaded to your web hosting provider or committed to version history.
 						</p>
 						</p>
 					</details>
 					</details>
 				</aside>
 				</aside>
@@ -164,7 +164,7 @@ npm install --save-dev vite
 			</li>
 			</li>
 			<li>
 			<li>
 				<p>
 				<p>
-					We'll also need to run a <i>local server</i> to host these files at URL where the web browser can access them. While it's technically possible to double-click an HTML file and open it in your browser, important features that we'll later do not work when the page is opened this way, for security reasons.
+					We'll also need to run a <i>local server</i> to host these files at URL where the web browser can access them. While it's technically possible to double-click an HTML file and open it in your browser, important features that we'll later implement, do not work when the page is opened this way, for security reasons.
 				</p>
 				</p>
 				<p>
 				<p>
 					Install [link:https://nodejs.org/ Node.js], then run [link:https://www.npmjs.com/package/serve serve] to start a local server in the project's directory:
 					Install [link:https://nodejs.org/ Node.js], then run [link:https://www.npmjs.com/package/serve serve] to start a local server in the project's directory:

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

@@ -12,7 +12,7 @@
 		<p class="desc">
 		<p class="desc">
 			Listed here are externally developed compatible libraries and plugins for three.js. This
 			Listed here are externally developed compatible libraries and plugins for three.js. This
 			list and the associated packages are maintained by the community and not guaranteed
 			list and the associated packages are maintained by the community and not guaranteed
-			to be up to date. If you'd like to update this list make PR!
+			to be up to date. If you'd like to update this list make a PR!
 		</p>
 		</p>
 
 
 		<h3>Physics</h3>
 		<h3>Physics</h3>

+ 272 - 0
docs/manual/zh/introduction/Color-management.html

@@ -0,0 +1,272 @@
+<!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" />
+	<style>
+		blockquote {
+			font-size: 0.8em;
+			line-height: 1.5em;
+			margin-left: 0;
+			border-left: 4px solid #cccccc;
+			padding: 1em 2em 1em 2em;
+		}
+
+		blockquote p:first-child {
+			margin-top: 0;
+		}
+
+		blockquote p:last-child {
+			margin-bottom: 0;
+		}
+
+		figure {
+			width: 100%;
+			margin: 1em 0;
+			font-style: italic;
+		}
+
+		figure img {
+			width: 100%;
+		}
+
+		figure.float {
+			float: right;
+			max-width: 30%;
+			margin: 1em;
+		}
+
+		@media all and (max-width: 640px) {
+
+			figure.float {
+				float: none;
+				max-width: 100%;
+			}
+
+		}
+	</style>
+</head>
+
+<body>
+	<h1>色彩管理([name])</h1>
+
+	<h2>什么是色彩空间?</h2>
+
+	<p>
+		每个色彩空间都是多个设计决策的集合,这些设计决策一起选择以支持多种颜色,同时满足与精度和显示技术相关的技术限制。创建 3D 资源或将 3D
+		资源组装到场景中时,了解这些属性是什么以及一种颜色空间的属性如何与场景中的其他颜色空间相关非常重要。
+	</p>
+
+	<figure class="float">
+		<img src="resources/srgb_gamut.png" alt="">
+		<figcaption>
+			参考 CIE 1931 色度图中显示的 sRGB 颜色和白点 (D65)。彩色区域表示 sRGB 色域的 2D 投影,即 3D 体积。资料来源:<a
+				href="https://en.wikipedia.org/wiki/SRGB" target="_blank" rel="noopener">维基百科</a>
+		</figcaption>
+	</figure>
+
+	<ul>
+		<li>
+			<b>原色:</b> 原色(例如红、绿、蓝)不是绝对的;它们是根据可用显示设备的有限精度和功能的限制从可见光谱中选择的。颜色以原色的比率表示。
+		</li>
+		<li>
+			<b>白点:</b> 大多数色彩空间经过精心设计,使得原色 <i>R = G = B</i>
+			的同等加权总和看起来没有颜色,或“消色差”。消色差值(如白色或灰色)的外观取决于人类的感知,而人类的感知又很大程度上取决于观察者的背景。色彩空间指定其“白点”来平衡这些需求。sRGB
+			色彩空间定义的白点是 [link:https://en.wikipedia.org/wiki/Illuminant_D65 D65]。
+		</li>
+		<li>
+			<b>传递函数:</b> 选择色域和颜色模型后,我们仍然需要定义数值与颜色空间之间的映射(“传递函数”)。 <i>r = 0.5</i>
+			是否表示物理照明比 <i>r = 1.0</i>少 50% ?或者像普通人眼所感知的那样亮度降低 50%?这些是不同的东西,这种差异可以用数学函数来表示。 传递函数可以是 <i>线性</i> 或
+			<i>非线性</i>的,具体取决于色彩空间的目标。sRGB 定义非线性传递函数。这些函数有时近似为<i>伽玛函数</i>,但术语“伽玛(gamma)”是不明确的,在这种情况下应避免使用。
+		</li>
+	</ul>
+
+	这三个参数——原色、白点和传递函数——定义了一个色彩空间,每个参数都是为了特定的目标而选择的。定义参数后,一些附加术语会有所帮助:
+
+	<ul>
+		<li>
+			<b>颜色模型:</b> 用于在所选色域(颜色的坐标系)内以数字方式识别颜色的语法。在 Three.js 中,我们主要关注 RGB 颜色模型,具有三个坐标 <i>r, g, b ∈ [0,1]</i> ("封闭域") 或
+			<i>r, g, b ∈ [0,∞]</i> ("开放域") 每个代表原色的一部分。其他颜色模型(HSL、Lab、LCH)通常用于艺术控制。
+		</li>
+		<li>
+			<b>色域:</b> 一旦选择了原色和白点,它们就代表可见光谱内的一个体积(“色域”)。不在该体积内的颜色(“色域外”)不能用闭域 [0,1] RGB 值表示。在开放域 [0,∞] 中,色域在技术上是无限的。
+		</li>
+	</ul>
+
+	<p>
+		考虑两种非常常见的颜色空间: [page:SRGBColorSpace] ("sRGB") 和
+		[page:LinearSRGBColorSpace] ("Linear-sRGB")。两者都使用相同的原色和白点,因此具有相同的色域。两者都使用 RGB 颜色模型。它们仅在传递函数上有所不同 - Linear-sRGB
+		相对于物理光强度是线性的。sRGB 使用非线性 sRGB 传输函数,更接近于人眼感知光的方式以及常见显示设备的响应能力。
+	</p>
+
+	<p>
+		这种差异很重要。照明计算和其他渲染操作通常必须在线性色彩空间中进行。然而,线性颜色在图像或帧缓冲区中存储的效率较低,并且在人类观察者观看时看起来不正确。因此,输入纹理和最终渲染图像通常将使用非线性 sRGB 颜色空间。
+	</p>
+
+	<blockquote>
+		<p>
+			ℹ️ <i><b>注意:</b> 虽然一些现代显示器支持更宽的色域(例如 Display-P3),但 Web 平台的图形 API 在很大程度上依赖于 sRGB。如今使用 Three.js 的应用程序通常仅使用 sRGB 和
+				Linear-sRGB 颜色空间。</i>
+		</p>
+	</blockquote>
+
+	<h2>色彩空间的作用</h2>
+
+	<p>
+		现代渲染方法所需的线性工作流程通常涉及多个颜色空间,每个颜色空间分配给一个特定的角色。线性和非线性颜色空间适合不同的角色,如下所述。
+	</p>
+
+	<h3>输入色彩空间</h3>
+
+	<p>
+		提供给 Three.js 的颜色(来自颜色选择器、纹理、3D 模型和其他来源)每种颜色都有一个关联的颜色空间。那些尚未在 Linear-sRGB 工作色彩空间中的纹理必须进行转换,并为纹理指定正确的
+		<i>texture.colorSpace</i> 分配。如果在初始化颜色之前启用了 THREE.ColorManagement API,则可以自动进行某些转换(对于 sRGB 中的十六进制和 CSS 颜色):
+	</p>
+
+	<code>
+THREE.ColorManagement.enabled = true;
+	</code>
+
+	<ul>
+		<li>
+			<b>材质、灯光和着色器:</b> 材质、灯光和着色器中的颜色将 RGB 分量存储在 Linear-sRGB 工作颜色空间中。
+		</li>
+		<li>
+			<b>顶点颜色:</b> [page:BufferAttribute BufferAttributes] 将 RGB 分量存储在 Linear-sRGB 工作颜色空间中。
+		</li>
+		<li>
+			<b>颜色纹理:</b> 包含颜色信息的PNG 或 JPEG [page:Texture 纹理] (如 .map 或 .emissiveMap)使用闭域 sRGB 颜色空间,并且必须使用
+			<i>texture.colorSpace = SRGBColorSpace</i>进行注释。像 OpenEXR 之类的格式(有时用于 .envMap 或 .lightMap)使用由 <i>texture.colorSpace
+				= LinearSRGBColorSpace</i> 指示的 Linear-sRGB 颜色空间,并且可能包含开放域 [0,∞] 中的值。
+		</li>
+		<li>
+			<b>非颜色纹理:</b> 不存储颜色信息的纹理(如 .normalMap 或 .roughnessMap)没有关联的颜色空间,并且通常使用(默认)纹理注释 <i>texture.colorSpace =
+				NoColorSpace</i>。在极少数情况下,出于技术原因,非颜色数据可以用其他非线性编码来表示。
+		</li>
+	</ul>
+
+	<blockquote>
+		<p>
+			⚠️ <i><b>警告:</b> 许多 3D 模型格式无法正确或一致地定义色彩空间信息。虽然 Three.js 尝试处理大多数情况,但较旧的文件格式很常见问题。为了获得最佳结果,请使用 glTF 2.0
+				([page:GLTFLoader]) 并尽早在在线查看器中测试 3D 模型,以确认资产本身是正确的。</i>
+		</p>
+	</blockquote>
+
+	<h3>工作色彩空间</h3>
+
+	<p>
+		渲染、插值和许多其他操作必须在开放域线性工作色彩空间中执行,其中 RGB 分量与物理照明成正比。在 Three.js 中,工作色彩空间是 Linear-sRGB。
+	</p>
+
+	<h3>输出色彩空间</h3>
+
+	<p>
+		输出到显示设备、图像或视频可能涉及从开放域 Linear-sRGB 工作色彩空间到另一个色彩空间的转换。此转换可以在主渲染通道 ([page:WebGLRenderer.outputColorSpace]) 中或在后处理期间执行。
+	</p>
+
+	<code>
+renderer.outputColorSpace = THREE.SRGBColorSpace; // optional with post-processing
+	</code>
+
+	<ul>
+		<li>
+			<b>显示:</b> 写入 WebGL 画布用于显示的颜色应位于 sRGB 颜色空间中。
+		</li>
+		<li>
+			<b>图像:</b> 写入图像的颜色应使用适合格式和用途的颜色空间。写入 PNG 或 JPEG 纹理的完全渲染图像通常使用 sRGB 颜色空间。包含发射、光照贴图或其他不限于 [0,1] 范围的数据的图像通常会使用开放域
+			Linear-sRGB 颜色空间以及兼容的图像格式(如 OpenEXR)。
+		</li>
+	</ul>
+
+	<blockquote>
+		<p>
+			⚠️ <i><b>警告:</b> 渲染目标可以使用 sRGB 或 Linear-sRGB。sRGB 更好地利用了有限的精度。在封闭域中,8 位通常足以满足 sRGB,而 Linear-sRGB 可能需要 ≥12
+				位(半浮点)。如果后面的管道阶段需要 Linear-sRGB 输入,则额外的转换可能会产生较小的性能成本。</i>
+		</p>
+	</blockquote>
+
+	<p>
+		基于 [page:ShaderMaterial] 和 [page:RawShaderMaterial] 的自定义材质必须实现自己的输出颜色空间转换。对于 的实例 `ShaderMaterial`,将
+		`colorspace_fragment` 着色器块添加到片段着色器的函数 `main()` 应该就足够了。
+	</p>
+
+	<h2>使用 THREE.Color 实例</h2>
+
+	<p>
+		读取或修改 [page:Color] 实例的方法假定数据已位于 Three.js 工作色彩空间 Linear-sRGB 中。RGB 和 HSL 分量是 Color 实例存储的数据的直接表示,并且永远不会隐式转换。可以使用
+		<i>.convertLinearToSRGB()</i>
+		或 <i>.convertSRGBToLinear()</i> 显式转换颜色数据。
+	</p>
+
+	<code>
+		// RGB components (no change).
+		color.r = color.g = color.b = 0.5;
+		console.log( color.r ); // → 0.5
+
+		// Manual conversion.
+		color.r = 0.5;
+		color.convertSRGBToLinear();
+		console.log( color.r ); // → 0.214041140
+	</code>
+
+	<p>
+		设置 <i>ColorManagement.enabled = true</i> (推荐) 时,会自动进行某些转换。由于十六进制和 CSS 颜色通常是 sRGB,因此 [page:Color] 方法会在 setter
+		中自动将这些输入从 sRGB 转换为 Linear-sRGB,或者在从 getter 返回十六进制或 CSS 输出时从 Linear-sRGB 转换为 sRGB。
+	</p>
+
+	<code>
+		// Hexadecimal conversion.
+		color.setHex( 0x808080 );
+		console.log( color.r ); // → 0.214041140
+		console.log( color.getHex() ); // → 0x808080
+
+		// CSS conversion.
+		color.setStyle( 'rgb( 0.5, 0.5, 0.5 )' );
+		console.log( color.r ); // → 0.214041140
+
+		// Override conversion with 'colorSpace' argument.
+		color.setHex( 0x808080, LinearSRGBColorSpace );
+		console.log( color.r ); // → 0.5
+		console.log( color.getHex( LinearSRGBColorSpace ) ); // → 0x808080
+		console.log( color.getHex( SRGBColorSpace ) ); // → 0xBCBCBC
+	</code>
+
+	<h2>常见错误</h2>
+
+	<p>
+		当单个颜色或纹理配置错误时,它会显得比预期更暗或更亮。当渲染器的输出色彩空间配置错误时,整个场景可能会显得更暗(例如,缺少到 sRGB 的转换)或更亮(例如,通过后处理双重转换到
+		sRGB)。在每种情况下,问题可能并不统一,并且简单地增加/减少照明并不能解决问题。
+	</p>
+
+	<p>
+		当 <i>输入色彩空间</i> 和 <i>输出色彩空间</i> 都不正确时, 会出现一个更微妙的问题-
+		整体亮度水平可能很好,但在不同的照明下颜色可能会发生意外变化,或者阴影可能会比预期的更加过度且不那么柔和。这两个错误并不能构成正确,重要的是工作颜色空间是线性的(“场景参考”)和输出颜色空间是非线性的(“显示参考”)。
+	</p>
+
+	<h2>进一步阅读</h2>
+
+	<ul>
+		<li>
+			<a href="https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-24-importance-being-linear"
+				target="_blank" rel="noopener">GPU Gems 3:线性的重要性</a>,作者: Larry Gritz 和 Eugene d'Eon
+		</li>
+		<li>
+			<a href="https://blog.johnnovak.net/2016/09/21/what-every-coder-should-know-about-gamma/" target="_blank"
+				rel="noopener">每个程序员都应该了解 gamma 的知识</a>,作者: John Novak
+		</li>
+		<li>
+			<a href="https://hg2dc.com/" target="_blank" rel="noopener">《数字色彩漫游指南》</a>,作者: Troy
+			Sobotka
+		</li>
+		<li>
+			<a href="https://docs.blender.org/manual/en/latest/render/color_management.html" target="_blank"
+				rel="noopener">色彩管理</a>,来源:Blender
+		</li>
+	</ul>
+
+</body>
+
+</html>

+ 18 - 2
editor/js/Menubar.File.js

@@ -225,6 +225,14 @@ function MenubarFile( editor ) {
 		const scene = editor.scene;
 		const scene = editor.scene;
 		const animations = getAnimations( scene );
 		const animations = getAnimations( scene );
 
 
+		const optimizedAnimations = [];
+
+		for ( const animation of animations ) {
+
+			optimizedAnimations.push( animation.clone().optimize() );
+
+		}
+
 		const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
 		const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
 
 
 		const exporter = new GLTFExporter();
 		const exporter = new GLTFExporter();
@@ -233,7 +241,7 @@ function MenubarFile( editor ) {
 
 
 			saveArrayBuffer( result, 'scene.glb' );
 			saveArrayBuffer( result, 'scene.glb' );
 
 
-		}, undefined, { binary: true, animations: animations } );
+		}, undefined, { binary: true, animations: optimizedAnimations } );
 
 
 	} );
 	} );
 	options.add( option );
 	options.add( option );
@@ -248,6 +256,14 @@ function MenubarFile( editor ) {
 		const scene = editor.scene;
 		const scene = editor.scene;
 		const animations = getAnimations( scene );
 		const animations = getAnimations( scene );
 
 
+		const optimizedAnimations = [];
+
+		for ( const animation of animations ) {
+
+			optimizedAnimations.push( animation.clone().optimize() );
+
+		}
+
 		const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
 		const { GLTFExporter } = await import( 'three/addons/exporters/GLTFExporter.js' );
 
 
 		const exporter = new GLTFExporter();
 		const exporter = new GLTFExporter();
@@ -256,7 +272,7 @@ function MenubarFile( editor ) {
 
 
 			saveString( JSON.stringify( result, null, 2 ), 'scene.gltf' );
 			saveString( JSON.stringify( result, null, 2 ), 'scene.gltf' );
 
 
-		}, undefined, { animations: animations } );
+		}, undefined, { animations: optimizedAnimations } );
 
 
 
 
 	} );
 	} );

+ 23 - 0
editor/js/Sidebar.Geometry.Modifiers.js

@@ -1,5 +1,8 @@
 import { UIDiv, UIButton, UIRow } from './libs/ui.js';
 import { UIDiv, UIButton, UIRow } from './libs/ui.js';
 
 
+import { computeMikkTSpaceTangents } from 'three/addons/utils/BufferGeometryUtils.js';
+import * as MikkTSpace from 'three/addons/libs/mikktspace.module.js';
+
 function SidebarGeometryModifiers( editor, object ) {
 function SidebarGeometryModifiers( editor, object ) {
 
 
 	const strings = editor.strings;
 	const strings = editor.strings;
@@ -25,6 +28,26 @@ function SidebarGeometryModifiers( editor, object ) {
 	computeVertexNormalsRow.add( computeVertexNormalsButton );
 	computeVertexNormalsRow.add( computeVertexNormalsButton );
 	container.add( computeVertexNormalsRow );
 	container.add( computeVertexNormalsRow );
 
 
+	// Compute Vertex Tangents
+
+	if ( geometry.hasAttribute( 'position' ) && geometry.hasAttribute( 'normal' ) && geometry.hasAttribute( 'uv' ) ) {
+
+		const computeVertexTangentsButton = new UIButton( strings.getKey( 'sidebar/geometry/compute_vertex_tangents' ) );
+		computeVertexTangentsButton.onClick( async function () {
+
+			await MikkTSpace.ready;
+
+			computeMikkTSpaceTangents( geometry, MikkTSpace );
+
+			signals.geometryChanged.dispatch( object );
+
+		} );
+
+		const computeVertexTangentsRow = new UIRow();
+		computeVertexTangentsRow.add( computeVertexTangentsButton );
+		container.add( computeVertexTangentsRow );
+
+	}
 
 
 	// Center Geometry
 	// Center Geometry
 
 

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

@@ -114,7 +114,7 @@ function SidebarMaterialMapProperty( editor, property, name ) {
 
 
 		if ( texture !== null ) {
 		if ( texture !== null ) {
 
 
-			if ( colorMaps[ property ] !== undefined && texture.isDataTexture !== true && texture.colorSpace !== THREE.SRGBColorSpace ) {
+			if ( colorMaps.includes( property ) && texture.isDataTexture !== true && texture.colorSpace !== THREE.SRGBColorSpace ) {
 
 
 				texture.colorSpace = THREE.SRGBColorSpace;
 				texture.colorSpace = THREE.SRGBColorSpace;
 				material.needsUpdate = true;
 				material.needsUpdate = true;

+ 3 - 0
editor/js/Strings.js

@@ -133,6 +133,7 @@ function Strings( config ) {
 			'sidebar/geometry/bounds': 'Bounds',
 			'sidebar/geometry/bounds': 'Bounds',
 			'sidebar/geometry/show_vertex_normals': 'Show Vertex Normals',
 			'sidebar/geometry/show_vertex_normals': 'Show Vertex Normals',
 			'sidebar/geometry/compute_vertex_normals': 'Compute Vertex Normals',
 			'sidebar/geometry/compute_vertex_normals': 'Compute Vertex Normals',
+			'sidebar/geometry/compute_vertex_tangents': 'Compute Tangents',
 			'sidebar/geometry/center': 'Center',
 			'sidebar/geometry/center': 'Center',
 
 
 			'sidebar/geometry/box_geometry/width': 'Width',
 			'sidebar/geometry/box_geometry/width': 'Width',
@@ -483,6 +484,7 @@ function Strings( config ) {
 			'sidebar/geometry/bounds': 'Limites',
 			'sidebar/geometry/bounds': 'Limites',
 			'sidebar/geometry/show_vertex_normals': 'Afficher normales',
 			'sidebar/geometry/show_vertex_normals': 'Afficher normales',
 			'sidebar/geometry/compute_vertex_normals': 'Compute Vertex Normals',
 			'sidebar/geometry/compute_vertex_normals': 'Compute Vertex Normals',
+			'sidebar/geometry/compute_vertex_tangents': 'Compute Tangents',
 			'sidebar/geometry/center': 'Center',
 			'sidebar/geometry/center': 'Center',
 
 
 			'sidebar/geometry/box_geometry/width': 'Largeur',
 			'sidebar/geometry/box_geometry/width': 'Largeur',
@@ -831,6 +833,7 @@ function Strings( config ) {
 			'sidebar/geometry/bounds': '界限',
 			'sidebar/geometry/bounds': '界限',
 			'sidebar/geometry/show_vertex_normals': '显示顶点法线',
 			'sidebar/geometry/show_vertex_normals': '显示顶点法线',
 			'sidebar/geometry/compute_vertex_normals': '计算顶点法线',
 			'sidebar/geometry/compute_vertex_normals': '计算顶点法线',
+			'sidebar/geometry/compute_vertex_tangents': 'Compute Tangents',
 			'sidebar/geometry/center': '居中',
 			'sidebar/geometry/center': '居中',
 
 
 			'sidebar/geometry/box_geometry/width': '宽度',
 			'sidebar/geometry/box_geometry/width': '宽度',

+ 4 - 0
editor/sw.js

@@ -26,6 +26,8 @@ const assets = [
 
 
 	'../examples/jsm/libs/meshopt_decoder.module.js',
 	'../examples/jsm/libs/meshopt_decoder.module.js',
 
 
+	'../examples/jsm/libs/mikktspace.module.js',
+
 	'../examples/jsm/libs/motion-controllers.module.js',
 	'../examples/jsm/libs/motion-controllers.module.js',
 
 
 	'../examples/jsm/libs/rhino3dm/rhino3dm.wasm',
 	'../examples/jsm/libs/rhino3dm/rhino3dm.wasm',
@@ -73,6 +75,8 @@ const assets = [
 
 
 	'../examples/jsm/helpers/VertexNormalsHelper.js',
 	'../examples/jsm/helpers/VertexNormalsHelper.js',
 
 
+	'../examples/jsm/utils/BufferGeometryUtils.js',
+
 	'../examples/jsm/webxr/VRButton.js',
 	'../examples/jsm/webxr/VRButton.js',
 	'../examples/jsm/webxr/XRControllerModelFactory.js',
 	'../examples/jsm/webxr/XRControllerModelFactory.js',
 
 

+ 10 - 1
examples/files.json

@@ -249,6 +249,7 @@
 		"webgl_postprocessing_gtao",
 		"webgl_postprocessing_gtao",
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_masking",
+		"webgl_postprocessing_material_ao",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_pixel",
@@ -329,6 +330,7 @@
 		"webgpu_cubemap_adjustments",
 		"webgpu_cubemap_adjustments",
 		"webgpu_cubemap_dynamic",
 		"webgpu_cubemap_dynamic",
 		"webgpu_cubemap_mix",
 		"webgpu_cubemap_mix",
+		"webgpu_custom_fog",
 		"webgpu_depth_texture",
 		"webgpu_depth_texture",
 		"webgpu_equirectangular",
 		"webgpu_equirectangular",
 		"webgpu_instance_mesh",
 		"webgpu_instance_mesh",
@@ -345,6 +347,7 @@
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_gltf_sheen",
 		"webgpu_loader_materialx",
 		"webgpu_loader_materialx",
 		"webgpu_materials",
 		"webgpu_materials",
+		"webgpu_materials_sss",
 		"webgpu_materials_video",
 		"webgpu_materials_video",
 		"webgpu_materialx_noise",
 		"webgpu_materialx_noise",
 		"webgpu_multiple_rendertargets",
 		"webgpu_multiple_rendertargets",
@@ -353,6 +356,7 @@
 		"webgpu_occlusion",
 		"webgpu_occlusion",
 		"webgpu_particles",
 		"webgpu_particles",
 		"webgpu_portal",
 		"webgpu_portal",
+		"webgpu_reflection",
 		"webgpu_rtt",
 		"webgpu_rtt",
 		"webgpu_sandbox",
 		"webgpu_sandbox",
 		"webgpu_shadertoy",
 		"webgpu_shadertoy",
@@ -364,7 +368,12 @@
 		"webgpu_textures_2d-array",
 		"webgpu_textures_2d-array",
 		"webgpu_tsl_editor",
 		"webgpu_tsl_editor",
 		"webgpu_tsl_transpiler",
 		"webgpu_tsl_transpiler",
-		"webgpu_video_panorama"
+		"webgpu_video_panorama",
+		"webgpu_postprocessing_afterimage",
+		"webgpu_postprocessing_anamorphic",
+		"webgpu_mirror",
+		"webgpu_multisampled_renderbuffers",
+		"webgpu_materials_texture_anisotropy"
 	],
 	],
 	"webaudio": [
 	"webaudio": [
 		"webaudio_orientation",
 		"webaudio_orientation",

+ 2 - 1
examples/jsm/Addons.js

@@ -25,6 +25,7 @@ export * from './csm/CSMShader.js';
 export * as Curves from './curves/CurveExtras.js';
 export * as Curves from './curves/CurveExtras.js';
 export * from './curves/NURBSCurve.js';
 export * from './curves/NURBSCurve.js';
 export * from './curves/NURBSSurface.js';
 export * from './curves/NURBSSurface.js';
+export * from './curves/NURBSVolume.js';
 export * as NURBSUtils from './curves/NURBSUtils.js';
 export * as NURBSUtils from './curves/NURBSUtils.js';
 
 
 export * from './effects/AnaglyphEffect.js';
 export * from './effects/AnaglyphEffect.js';
@@ -161,7 +162,7 @@ export * from './modifiers/EdgeSplitModifier.js';
 export * from './modifiers/SimplifyModifier.js';
 export * from './modifiers/SimplifyModifier.js';
 export * from './modifiers/TessellateModifier.js';
 export * from './modifiers/TessellateModifier.js';
 
 
-export * from './objects/GroundProjectedSkybox.js';
+export * from './objects/GroundedSkybox.js';
 export * from './objects/Lensflare.js';
 export * from './objects/Lensflare.js';
 export * from './objects/MarchingCubes.js';
 export * from './objects/MarchingCubes.js';
 export * from './objects/Reflector.js';
 export * from './objects/Reflector.js';

+ 13 - 9
examples/jsm/capabilities/WebGPU.js

@@ -1,20 +1,17 @@
-if ( window.GPUShaderStage === undefined ) {
+if ( self.GPUShaderStage === undefined ) {
 
 
-	window.GPUShaderStage = { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 };
+	self.GPUShaderStage = { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 };
 
 
 }
 }
 
 
-let isAvailable = false;
+// statics
 
 
-if ( navigator.gpu !== undefined ) {
+let isAvailable = navigator.gpu !== undefined;
 
 
-	const adapter = await navigator.gpu.requestAdapter();
 
 
-	if ( adapter !== null ) {
+if ( typeof window !== 'undefined' && isAvailable ) {
 
 
-		isAvailable = true;
-
-	}
+	isAvailable = await navigator.gpu.requestAdapter();
 
 
 }
 }
 
 
@@ -22,6 +19,12 @@ class WebGPU {
 
 
 	static isAvailable() {
 	static isAvailable() {
 
 
+		return Boolean( isAvailable );
+
+	}
+
+	static getStaticAdapter() {
+
 		return isAvailable;
 		return isAvailable;
 
 
 	}
 	}
@@ -50,4 +53,5 @@ class WebGPU {
 
 
 }
 }
 
 
+
 export default WebGPU;
 export default WebGPU;

+ 46 - 19
examples/jsm/controls/OrbitControls.js

@@ -374,9 +374,14 @@ class OrbitControls extends EventDispatcher {
 
 
 				} else if ( scope.object.isOrthographicCamera ) {
 				} else if ( scope.object.isOrthographicCamera ) {
 
 
-					scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );
-					scope.object.updateProjectionMatrix();
-					zoomChanged = true;
+					zoomChanged = scale !== 1;
+
+					if ( zoomChanged ) {
+
+						scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) );
+						scope.object.updateProjectionMatrix();
+
+					}
 
 
 				}
 				}
 
 
@@ -1042,18 +1047,32 @@ class OrbitControls extends EventDispatcher {
 
 
 			removePointer( event );
 			removePointer( event );
 
 
-			if ( pointers.length === 0 ) {
+			switch ( pointers.length ) {
 
 
-				scope.domElement.releasePointerCapture( event.pointerId );
+				case 0:
 
 
-				scope.domElement.removeEventListener( 'pointermove', onPointerMove );
-				scope.domElement.removeEventListener( 'pointerup', onPointerUp );
+					scope.domElement.releasePointerCapture( event.pointerId );
 
 
-			}
+					scope.domElement.removeEventListener( 'pointermove', onPointerMove );
+					scope.domElement.removeEventListener( 'pointerup', onPointerUp );
 
 
-			scope.dispatchEvent( _endEvent );
+					scope.dispatchEvent( _endEvent );
 
 
-			state = STATE.NONE;
+					state = STATE.NONE;
+
+					break;
+
+				case 1:
+
+					const pointerId = pointers[ 0 ];
+					const position = pointerPositions[ pointerId ];
+
+					// minimal placeholder event - allows state correction on pointer-up
+					onTouchStart( { pointerId: pointerId, pageX: position.x, pageY: position.y } );
+
+					break;
+
+			}
 
 
 		}
 		}
 
 
@@ -1209,7 +1228,7 @@ class OrbitControls extends EventDispatcher {
 				clientX: event.clientX,
 				clientX: event.clientX,
 				clientY: event.clientY,
 				clientY: event.clientY,
 				deltaY: event.deltaY,
 				deltaY: event.deltaY,
-			}
+			};
 
 
 			switch ( mode ) {
 			switch ( mode ) {
 
 
@@ -1224,7 +1243,7 @@ class OrbitControls extends EventDispatcher {
 			}
 			}
 
 
 			// detect if event was triggered by pinching
 			// detect if event was triggered by pinching
-			if ( event.ctrlKey && !controlActive ) {
+			if ( event.ctrlKey && ! controlActive ) {
 
 
 				newEvent.deltaY *= 10;
 				newEvent.deltaY *= 10;
 
 
@@ -1236,11 +1255,14 @@ class OrbitControls extends EventDispatcher {
 
 
 		function interceptControlDown( event ) {
 		function interceptControlDown( event ) {
 
 
-			if ( event.key === "Control" ) {
+			if ( event.key === 'Control' ) {
 
 
 				controlActive = true;
 				controlActive = true;
-				
-				document.addEventListener('keyup', interceptControlUp, { passive: true, capture: true });
+
+
+				const document = scope.domElement.getRootNode(); // offscreen canvas compatibility
+
+				document.addEventListener( 'keyup', interceptControlUp, { passive: true, capture: true } );
 
 
 			}
 			}
 
 
@@ -1248,11 +1270,14 @@ class OrbitControls extends EventDispatcher {
 
 
 		function interceptControlUp( event ) {
 		function interceptControlUp( event ) {
 
 
-			if ( event.key === "Control" ) {
+			if ( event.key === 'Control' ) {
 
 
 				controlActive = false;
 				controlActive = false;
-				
-				document.removeEventListener('keyup', interceptControlUp, { passive: true, capture: true });
+
+
+				const document = scope.domElement.getRootNode(); // offscreen canvas compatibility
+
+				document.removeEventListener( 'keyup', interceptControlUp, { passive: true, capture: true } );
 
 
 			}
 			}
 
 
@@ -1466,6 +1491,8 @@ class OrbitControls extends EventDispatcher {
 		scope.domElement.addEventListener( 'pointercancel', onPointerUp );
 		scope.domElement.addEventListener( 'pointercancel', onPointerUp );
 		scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
 		scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
 
 
+		const document = scope.domElement.getRootNode(); // offscreen canvas compatibility
+
 		document.addEventListener( 'keydown', interceptControlDown, { passive: true, capture: true } );
 		document.addEventListener( 'keydown', interceptControlDown, { passive: true, capture: true } );
 
 
 		// force an update at start
 		// force an update at start
@@ -1476,4 +1503,4 @@ class OrbitControls extends EventDispatcher {
 
 
 }
 }
 
 
-export { OrbitControls };
+export { OrbitControls };

+ 59 - 4
examples/jsm/curves/NURBSUtils.js

@@ -429,10 +429,10 @@ function calcNURBSDerivatives( p, U, P, u, nd ) {
 /*
 /*
 Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
 Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
 
 
-p1, p2 : degrees of B-Spline surface
-U1, U2 : knot vectors
-P      : control points (x, y, z, w)
-u, v   : parametric values
+p, q : degrees of B-Spline surface
+U, V : knot vectors
+P    : control points (x, y, z, w)
+u, v : parametric values
 
 
 returns point for given (u, v)
 returns point for given (u, v)
 */
 */
@@ -472,6 +472,60 @@ function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
 
 
 }
 }
 
 
+/*
+Calculate rational B-Spline volume point. See The NURBS Book, page 134, algorithm A4.3.
+
+p, q, r   : degrees of B-Splinevolume
+U, V, W   : knot vectors
+P         : control points (x, y, z, w)
+u, v, w   : parametric values
+
+returns point for given (u, v, w)
+*/
+function calcVolumePoint( p, q, r, U, V, W, P, u, v, w, target ) {
+
+	const uspan = findSpan( p, u, U );
+	const vspan = findSpan( q, v, V );
+	const wspan = findSpan( r, w, W );
+	const Nu = calcBasisFunctions( uspan, u, p, U );
+	const Nv = calcBasisFunctions( vspan, v, q, V );
+	const Nw = calcBasisFunctions( wspan, w, r, W );
+	const temp = [];
+
+	for ( let m = 0; m <= r; ++ m ) {
+
+		temp[ m ] = [];
+
+		for ( let l = 0; l <= q; ++ l ) {
+
+			temp[ m ][ l ] = new Vector4( 0, 0, 0, 0 );
+			for ( let k = 0; k <= p; ++ k ) {
+
+				const point = P[ uspan - p + k ][ vspan - q + l ][ wspan - r + m ].clone();
+				const w = point.w;
+				point.x *= w;
+				point.y *= w;
+				point.z *= w;
+				temp[ m ][ l ].add( point.multiplyScalar( Nu[ k ] ) );
+
+			}
+
+		}
+
+	}
+	const Sw = new Vector4( 0, 0, 0, 0 );
+	for ( let m = 0; m <= r; ++ m ) {
+		for ( let l = 0; l <= q; ++ l ) {
+
+			Sw.add( temp[ m ][ l ].multiplyScalar( Nw[ m ] ).multiplyScalar( Nv[ l ] ) );
+
+		}
+	}
+
+	Sw.divideScalar( Sw.w );
+	target.set( Sw.x, Sw.y, Sw.z );
+
+}
 
 
 
 
 export {
 export {
@@ -484,4 +538,5 @@ export {
 	calcRationalCurveDerivatives,
 	calcRationalCurveDerivatives,
 	calcNURBSDerivatives,
 	calcNURBSDerivatives,
 	calcSurfacePoint,
 	calcSurfacePoint,
+	calcVolumePoint,
 };
 };

+ 62 - 0
examples/jsm/curves/NURBSVolume.js

@@ -0,0 +1,62 @@
+import {
+	Vector4
+} from 'three';
+import * as NURBSUtils from '../curves/NURBSUtils.js';
+
+/**
+ * NURBS volume object
+ *
+ * Implementation is based on (x, y, z [, w=1]]) control points with w=weight.
+ **/
+
+class NURBSVolume {
+
+	constructor( degree1, degree2, degree3, knots1, knots2, knots3 /* arrays of reals */, controlPoints /* array^3 of Vector(2|3|4) */ ) {
+
+		this.degree1 = degree1;
+		this.degree2 = degree2;
+		this.degree3 = degree3;
+		this.knots1 = knots1;
+		this.knots2 = knots2;
+		this.knots3 = knots3;
+		this.controlPoints = [];
+
+		const len1 = knots1.length - degree1 - 1;
+		const len2 = knots2.length - degree2 - 1;
+		const len3 = knots3.length - degree3 - 1;
+
+		// ensure Vector4 for control points
+		for ( let i = 0; i < len1; ++ i ) {
+
+			this.controlPoints[ i ] = [];
+
+			for ( let j = 0; j < len2; ++ j ) {
+
+				this.controlPoints[ i ][ j ] = [];
+
+				for ( let k = 0; k < len3; ++ k ) {
+
+					const point = controlPoints[ i ][ j ][ k ];
+					this.controlPoints[ i ][ j ][ k ] = new Vector4( point.x, point.y, point.z, point.w );
+
+				}
+
+			}
+
+		}
+
+	}
+
+	getPoint( t1, t2, t3, target ) {
+
+		const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
+		const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->v
+		const w = this.knots3[ 0 ] + t3 * ( this.knots3[ this.knots3.length - 1 ] - this.knots3[ 0 ] ); // linear mapping t3->w
+
+		NURBSUtils.calcVolumePoint( this.degree1, this.degree2, this.degree3, this.knots1, this.knots2, this.knots3, this.controlPoints, u, v, w, target );
+
+	}
+
+}
+
+export { NURBSVolume };

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

@@ -1,11 +1,11 @@
 import {
 import {
-  NoColorSpace,
-  DoubleSide,
+	NoColorSpace,
+	DoubleSide,
 } from 'three';
 } from 'three';
 
 
 import {
 import {
-  strToU8,
-  zipSync,
+	strToU8,
+	zipSync,
 } from '../libs/fflate.module.js';
 } from '../libs/fflate.module.js';
 
 
 import { decompress } from './../utils/TextureUtils.js';
 import { decompress } from './../utils/TextureUtils.js';
@@ -20,6 +20,7 @@ class USDZExporter {
 				planeAnchoring: { alignment: 'horizontal' }
 				planeAnchoring: { alignment: 'horizontal' }
 			},
 			},
 			quickLookCompatible: false,
 			quickLookCompatible: false,
+			maxTextureSize: 1024,
 		}, options );
 		}, options );
 
 
 		const files = {};
 		const files = {};
@@ -93,7 +94,7 @@ class USDZExporter {
 
 
 			}
 			}
 
 
-			const canvas = imageToCanvas( texture.image, texture.flipY );
+			const canvas = imageToCanvas( texture.image, texture.flipY, options.maxTextureSize );
 			const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
 			const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
 
 
 			files[ `textures/Texture_${ id }.png` ] = new Uint8Array( await blob.arrayBuffer() );
 			files[ `textures/Texture_${ id }.png` ] = new Uint8Array( await blob.arrayBuffer() );
@@ -133,14 +134,14 @@ class USDZExporter {
 
 
 }
 }
 
 
-function imageToCanvas( image, flipY ) {
+function imageToCanvas( image, flipY, maxTextureSize ) {
 
 
 	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
 	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
 		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
 		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
 		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
 		( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
 		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
 		( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
 
 
-		const scale = 1024 / Math.max( image.width, image.height );
+		const scale = maxTextureSize / Math.max( image.width, image.height );
 
 
 		const canvas = document.createElement( 'canvas' );
 		const canvas = document.createElement( 'canvas' );
 		canvas.width = image.width * Math.min( 1, scale );
 		canvas.width = image.width * Math.min( 1, scale );

+ 3 - 3
examples/jsm/helpers/TextureHelper.js

@@ -216,8 +216,8 @@ function createSliceGeometry( texture, width, height, depth ) {
 			const w = sliceCount === 1
 			const w = sliceCount === 1
 				? 1
 				? 1
 				: texture.isDataArrayTexture || texture.isCompressedArrayTexture
 				: texture.isDataArrayTexture || texture.isCompressedArrayTexture
-				? i
-				: i / ( sliceCount - 1 );
+					? i
+					: i / ( sliceCount - 1 );
 
 
 			uvw.setXYZ( j, u, v, w );
 			uvw.setXYZ( j, u, v, w );
 
 
@@ -234,4 +234,4 @@ function createSliceGeometry( texture, width, height, depth ) {
 
 
 }
 }
 
 
-export { TextureHelper };
+export { TextureHelper };

+ 44 - 43
examples/jsm/loaders/3DMLoader.js

@@ -1,29 +1,29 @@
 import {
 import {
 	BufferGeometryLoader,
 	BufferGeometryLoader,
-	FileLoader,
-	Loader,
-	Object3D,
-	MeshStandardMaterial,
-	MeshPhysicalMaterial,
-	Mesh,
+	CanvasTexture,
+	ClampToEdgeWrapping,
 	Color,
 	Color,
-	Points,
-	PointsMaterial,
+	DirectionalLight,
+	DoubleSide,
+	FileLoader,
+	LinearFilter,
 	Line,
 	Line,
 	LineBasicMaterial,
 	LineBasicMaterial,
+	Loader,
 	Matrix4,
 	Matrix4,
-	DirectionalLight,
+	Mesh,
+	MeshPhysicalMaterial,
+	MeshStandardMaterial,
+	Object3D,
 	PointLight,
 	PointLight,
-	SpotLight,
+	Points,
+	PointsMaterial,
 	RectAreaLight,
 	RectAreaLight,
+	RepeatWrapping,
+	SpotLight,
 	Sprite,
 	Sprite,
 	SpriteMaterial,
 	SpriteMaterial,
-	CanvasTexture,
-	LinearFilter,
-	ClampToEdgeWrapping,
-	RepeatWrapping,
-	TextureLoader,
-	DoubleSide
+	TextureLoader
 } from 'three';
 } from 'three';
 
 
 import { EXRLoader } from '../loaders/EXRLoader.js';
 import { EXRLoader } from '../loaders/EXRLoader.js';
@@ -235,7 +235,7 @@ class Rhino3dmLoader extends Loader {
 
 
 		//console.log(material)
 		//console.log(material)
 
 
-		let mat = new MeshPhysicalMaterial( {
+		const mat = new MeshPhysicalMaterial( {
 
 
 			color: new Color( material.diffuseColor.r / 255.0, material.diffuseColor.g / 255.0, material.diffuseColor.b / 255.0 ),
 			color: new Color( material.diffuseColor.r / 255.0, material.diffuseColor.g / 255.0, material.diffuseColor.b / 255.0 ),
 			emissive: new Color( material.emissionColor.r, material.emissionColor.g, material.emissionColor.b ),
 			emissive: new Color( material.emissionColor.r, material.emissionColor.g, material.emissionColor.b ),
@@ -256,11 +256,11 @@ class Rhino3dmLoader extends Loader {
 
 
 			const pbr = material.pbr;
 			const pbr = material.pbr;
 
 
-			mat.anisotropy = pbr.anisotropy;
+			mat.anisotropy = pbr.anisotropic;
 			mat.anisotropyRotation = pbr.anisotropicRotation;
 			mat.anisotropyRotation = pbr.anisotropicRotation;
 			mat.color = new Color( pbr.baseColor.r, pbr.baseColor.g, pbr.baseColor.b );
 			mat.color = new Color( pbr.baseColor.r, pbr.baseColor.g, pbr.baseColor.b );
-			mat.clearCoat = pbr.clearCoat;
-			mat.clearCoatRoughness = pbr.clearCoatRoughness;
+			mat.clearcoat = pbr.clearcoat;
+			mat.clearcoatRoughness = pbr.clearcoatRoughness;
 			mat.metalness = pbr.metallic;
 			mat.metalness = pbr.metallic;
 			mat.transmission = 1 - pbr.opacity;
 			mat.transmission = 1 - pbr.opacity;
 			mat.roughness = pbr.roughness;
 			mat.roughness = pbr.roughness;
@@ -310,7 +310,7 @@ class Rhino3dmLoader extends Loader {
 						mat.envMap = map;
 						mat.envMap = map;
 
 
 						break;
 						break;
-					
+
 					case 'Opacity':
 					case 'Opacity':
 
 
 						mat.transmissionMap = map;
 						mat.transmissionMap = map;
@@ -330,7 +330,7 @@ class Rhino3dmLoader extends Loader {
 						mat.transparent = true;
 						mat.transparent = true;
 
 
 						break;
 						break;
-					
+
 					case 'PBR_AmbientOcclusion':
 					case 'PBR_AmbientOcclusion':
 
 
 						mat.aoMap = map;
 						mat.aoMap = map;
@@ -461,7 +461,7 @@ class Rhino3dmLoader extends Loader {
 		object.userData.settings[ 'renderSettings' ] = data.renderSettings;
 		object.userData.settings[ 'renderSettings' ] = data.renderSettings;
 		object.userData[ 'objectType' ] = 'File3dm';
 		object.userData[ 'objectType' ] = 'File3dm';
 		object.userData[ 'materials' ] = null;
 		object.userData[ 'materials' ] = null;
-		
+
 		object.name = this.url;
 		object.name = this.url;
 
 
 		let objects = data.objects;
 		let objects = data.objects;
@@ -490,7 +490,8 @@ class Rhino3dmLoader extends Loader {
 
 
 					let matId;
 					let matId;
 
 
-					switch( attributes.materialSource.name ) {
+					switch ( attributes.materialSource.name ) {
+
 						case 'ObjectMaterialSource_MaterialFromLayer':
 						case 'ObjectMaterialSource_MaterialFromLayer':
 							//check layer index
 							//check layer index
 							if ( attributes.layerIndex >= 0 ) {
 							if ( attributes.layerIndex >= 0 ) {
@@ -1129,7 +1130,7 @@ function Rhino3dmWorker() {
 
 
 			const _material = doc.materials().get( i );
 			const _material = doc.materials().get( i );
 
 
-			let material = extractProperties( _material );
+			const material = extractProperties( _material );
 
 
 			const textures = [];
 			const textures = [];
 
 
@@ -1232,42 +1233,42 @@ function Rhino3dmWorker() {
 		// Handle Render Environments for Material Environment
 		// Handle Render Environments for Material Environment
 
 
 		// get the id of the active render environment skylight, which we'll use for environment texture
 		// get the id of the active render environment skylight, which we'll use for environment texture
-		const reflectionId = doc.settings().renderSettings().renderEnvironments.reflectionId
+		const reflectionId = doc.settings().renderSettings().renderEnvironments.reflectionId;
 
 
-		const rc = doc.renderContent()
+		const rc = doc.renderContent();
 
 
-		let renderEnvironment = null
+		let renderEnvironment = null;
 
 
-		for( let i = 0; i < rc.count; i++ ) {
+		for ( let i = 0; i < rc.count; i ++ ) {
 
 
-			const content = rc.get(i)
+			const content = rc.get( i );
 
 
-			switch( content.kind ) {
+			switch ( content.kind ) {
 
 
 				case 'environment':
 				case 'environment':
 
 
-					const id = content.id
+					const id = content.id;
 
 
 					// there could be multiple render environments in a 3dm file
 					// there could be multiple render environments in a 3dm file
 					if ( id !== reflectionId ) break;
 					if ( id !== reflectionId ) break;
 
 
-					const renderTexture = content.findChild( 'texture' )
-					const fileName = renderTexture.fileName
+					const renderTexture = content.findChild( 'texture' );
+					const fileName = renderTexture.fileName;
 
 
 					for ( let j = 0; j < doc.embeddedFiles().count; j ++ ) {
 					for ( let j = 0; j < doc.embeddedFiles().count; j ++ ) {
 
 
-						const _fileName = doc.embeddedFiles().get( j ).fileName
+						const _fileName = doc.embeddedFiles().get( j ).fileName;
 
 
 						if ( fileName === _fileName ) {
 						if ( fileName === _fileName ) {
 
 
-							const background = doc.getEmbeddedFileAsBase64( fileName )
-							const backgroundImage = 'data:image/png;base64,' + background
+							const background = doc.getEmbeddedFileAsBase64( fileName );
+							const backgroundImage = 'data:image/png;base64,' + background;
 							renderEnvironment = { type: 'renderEnvironment', image: backgroundImage, name: fileName };
 							renderEnvironment = { type: 'renderEnvironment', image: backgroundImage, name: fileName };
 
 
 						}
 						}
 
 
 					}
 					}
-					
+
 					break;
 					break;
 
 
 			}
 			}
@@ -1307,7 +1308,7 @@ function Rhino3dmWorker() {
 			renderEnvironments: extractProperties( doc.settings().renderSettings().renderEnvironments ),
 			renderEnvironments: extractProperties( doc.settings().renderSettings().renderEnvironments ),
 			postEffects: extractProperties( doc.settings().renderSettings().postEffects ),
 			postEffects: extractProperties( doc.settings().renderSettings().postEffects ),
 
 
-		}
+		};
 
 
 		doc.delete();
 		doc.delete();
 
 
@@ -1317,7 +1318,7 @@ function Rhino3dmWorker() {
 
 
 	function extractTextures( m, tTypes, d ) {
 	function extractTextures( m, tTypes, d ) {
 
 
-		const textures = []
+		const textures = [];
 
 
 		for ( let i = 0; i < tTypes.length; i ++ ) {
 		for ( let i = 0; i < tTypes.length; i ++ ) {
 
 
@@ -1584,13 +1585,13 @@ function Rhino3dmWorker() {
 			if ( _attributes.decals().count > 0 ) {
 			if ( _attributes.decals().count > 0 ) {
 
 
 				self.postMessage( { type: 'warning', id: taskID, data: {
 				self.postMessage( { type: 'warning', id: taskID, data: {
-					message: `THREE.3DMLoader: No conversion exists for the decals associated with this object.`,
+					message: 'THREE.3DMLoader: No conversion exists for the decals associated with this object.',
 					type: 'no conversion',
 					type: 'no conversion',
 					guid: _attributes.id
 					guid: _attributes.id
 				}
 				}
-	
+
 				} );
 				} );
-				
+
 			}
 			}
 
 
 			attributes.drawColor = _attributes.drawColor( doc );
 			attributes.drawColor = _attributes.drawColor( doc );

+ 1 - 0
examples/jsm/loaders/DDSLoader.js

@@ -221,6 +221,7 @@ class DDSLoader extends CompressedTextureLoader {
 					}
 					}
 
 
 				}
 				}
+
 				break;
 				break;
 
 
 			default:
 			default:

+ 20 - 5
examples/jsm/loaders/KTX2Loader.js

@@ -120,17 +120,32 @@ class KTX2Loader extends Loader {
 
 
 	}
 	}
 
 
+	async detectSupportAsync( renderer ) {
+
+		this.workerConfig = {
+			astcSupported: await renderer.hasFeatureAsync( 'texture-compression-astc' ),
+			etc1Supported: await renderer.hasFeatureAsync( 'texture-compression-etc1' ),
+			etc2Supported: await renderer.hasFeatureAsync( 'texture-compression-etc2' ),
+			dxtSupported: await renderer.hasFeatureAsync( 'texture-compression-bc' ),
+			bptcSupported: await renderer.hasFeatureAsync( 'texture-compression-bptc' ),
+			pvrtcSupported: await renderer.hasFeatureAsync( 'texture-compression-pvrtc' )
+		};
+
+		return this;
+
+	}
+
 	detectSupport( renderer ) {
 	detectSupport( renderer ) {
 
 
 		if ( renderer.isWebGPURenderer === true ) {
 		if ( renderer.isWebGPURenderer === true ) {
 
 
 			this.workerConfig = {
 			this.workerConfig = {
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
 				astcSupported: renderer.hasFeature( 'texture-compression-astc' ),
-				etc1Supported: false,
+				etc1Supported: renderer.hasFeature( 'texture-compression-etc1' ),
 				etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				etc2Supported: renderer.hasFeature( 'texture-compression-etc2' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
 				dxtSupported: renderer.hasFeature( 'texture-compression-bc' ),
-				bptcSupported: false,
-				pvrtcSupported: false
+				bptcSupported: renderer.hasFeature( 'texture-compression-bptc' ),
+				pvrtcSupported: renderer.hasFeature( 'texture-compression-pvrtc' )
 			};
 			};
 
 
 		} else {
 		} else {
@@ -865,8 +880,8 @@ async function createRawTexture( container ) {
 	if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {
 	if ( UNCOMPRESSED_FORMATS.has( FORMAT_MAP[ vkFormat ] ) ) {
 
 
 		texture = container.pixelDepth === 0
 		texture = container.pixelDepth === 0
-		? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
-		: new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );
+			? new DataTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight )
+			: new Data3DTexture( mipmaps[ 0 ].data, container.pixelWidth, container.pixelHeight, container.pixelDepth );
 
 
 	} else {
 	} else {
 
 

+ 78 - 46
examples/jsm/loaders/LUT3dlLoader.js

@@ -1,18 +1,42 @@
 // http://download.autodesk.com/us/systemdocs/help/2011/lustre/index.html?url=./files/WSc4e151a45a3b785a24c3d9a411df9298473-7ffd.htm,topicNumber=d0e9492
 // http://download.autodesk.com/us/systemdocs/help/2011/lustre/index.html?url=./files/WSc4e151a45a3b785a24c3d9a411df9298473-7ffd.htm,topicNumber=d0e9492
 // https://community.foundry.com/discuss/topic/103636/format-spec-for-3dl?mode=Post&postID=895258
 // https://community.foundry.com/discuss/topic/103636/format-spec-for-3dl?mode=Post&postID=895258
+
 import {
 import {
-	Loader,
-	FileLoader,
+	ClampToEdgeWrapping,
 	DataTexture,
 	DataTexture,
 	Data3DTexture,
 	Data3DTexture,
+	FileLoader,
+	FloatType,
+	LinearFilter,
+	Loader,
 	RGBAFormat,
 	RGBAFormat,
 	UnsignedByteType,
 	UnsignedByteType,
-	ClampToEdgeWrapping,
-	LinearFilter,
 } from 'three';
 } from 'three';
 
 
 export class LUT3dlLoader extends Loader {
 export class LUT3dlLoader extends Loader {
 
 
+	constructor( manager ) {
+
+		super( manager );
+
+		this.type = UnsignedByteType;
+
+	}
+
+	setType( type ) {
+
+		if ( type !== UnsignedByteType && type !== FloatType ) {
+
+			throw new Error( 'LUT3dlLoader: Unsupported type' );
+
+		}
+
+		this.type = type;
+
+		return this;
+
+	}
+
 	load( url, onLoad, onProgress, onError ) {
 	load( url, onLoad, onProgress, onError ) {
 
 
 		const loader = new FileLoader( this.manager );
 		const loader = new FileLoader( this.manager );
@@ -44,80 +68,88 @@ export class LUT3dlLoader extends Loader {
 
 
 	}
 	}
 
 
-	parse( str ) {
+	parse( input ) {
+
+		const regExpGridInfo = /^[\d ]+$/m;
+		const regExpDataPoints = /^([\d.e+-]+) +([\d.e+-]+) +([\d.e+-]+) *$/gm;
 
 
-		// remove empty lines and comment lints
-		str = str
-			.replace( /^#.*?(\n|\r)/gm, '' )
-			.replace( /^\s*?(\n|\r)/gm, '' )
-			.trim();
+		// The first line describes the positions of values on the LUT grid.
+		let result = regExpGridInfo.exec( input );
 
 
-		const lines = str.split( /[\n\r]+/g );
+		if ( result === null ) {
 
 
-		// first line is the positions on the grid that are provided by the LUT
-		const gridLines = lines[ 0 ].trim().split( /\s+/g ).map( e => parseFloat( e ) );
+			throw new Error( 'LUT3dlLoader: Missing grid information' );
+
+		}
+
+		const gridLines = result[ 0 ].trim().split( /\s+/g ).map( Number );
 		const gridStep = gridLines[ 1 ] - gridLines[ 0 ];
 		const gridStep = gridLines[ 1 ] - gridLines[ 0 ];
 		const size = gridLines.length;
 		const size = gridLines.length;
+		const sizeSq = size ** 2;
 
 
-		for ( let i = 1, l = gridLines.length; i < l; i ++ ) {
+		for ( let i = 1, l = gridLines.length; i < l; ++ i ) {
 
 
 			if ( gridStep !== ( gridLines[ i ] - gridLines[ i - 1 ] ) ) {
 			if ( gridStep !== ( gridLines[ i ] - gridLines[ i - 1 ] ) ) {
 
 
-				throw new Error( 'LUT3dlLoader: Inconsistent grid size not supported.' );
+				throw new Error( 'LUT3dlLoader: Inconsistent grid size' );
 
 
 			}
 			}
 
 
 		}
 		}
 
 
-		const dataArray = new Array( size * size * size * 4 );
+		const dataFloat = new Float32Array( size ** 3 * 4 );
+		let maxValue = 0.0;
 		let index = 0;
 		let index = 0;
-		let maxOutputValue = 0.0;
-		for ( let i = 1, l = lines.length; i < l; i ++ ) {
 
 
-			const line = lines[ i ].trim();
-			const split = line.split( /\s/g );
+		while ( ( result = regExpDataPoints.exec( input ) ) !== null ) {
+
+			const r = Number( result[ 1 ] );
+			const g = Number( result[ 2 ] );
+			const b = Number( result[ 3 ] );
 
 
-			const r = parseFloat( split[ 0 ] );
-			const g = parseFloat( split[ 1 ] );
-			const b = parseFloat( split[ 2 ] );
-			maxOutputValue = Math.max( maxOutputValue, r, g, b );
+			maxValue = Math.max( maxValue, r, g, b );
 
 
 			const bLayer = index % size;
 			const bLayer = index % size;
 			const gLayer = Math.floor( index / size ) % size;
 			const gLayer = Math.floor( index / size ) % size;
-			const rLayer = Math.floor( index / ( size * size ) ) % size;
+			const rLayer = Math.floor( index / ( sizeSq ) ) % size;
 
 
-			// b grows first, then g, then r
-			const pixelIndex = bLayer * size * size + gLayer * size + rLayer;
-			dataArray[ 4 * pixelIndex + 0 ] = r;
-			dataArray[ 4 * pixelIndex + 1 ] = g;
-			dataArray[ 4 * pixelIndex + 2 ] = b;
-			dataArray[ 4 * pixelIndex + 3 ] = 1.0;
-			index += 1;
+			// b grows first, then g, then r.
+			const d4 = ( bLayer * sizeSq + gLayer * size + rLayer ) * 4;
+			dataFloat[ d4 + 0 ] = r;
+			dataFloat[ d4 + 1 ] = g;
+			dataFloat[ d4 + 2 ] = b;
+
+			++ index;
 
 
 		}
 		}
 
 
-		// Find the apparent bit depth of the stored RGB values and map the
-		// values to [ 0, 255 ].
-		const bits = Math.ceil( Math.log2( maxOutputValue ) );
-		const maxBitValue = Math.pow( 2.0, bits );
-		for ( let i = 0, l = dataArray.length; i < l; i += 4 ) {
+		// Determine the bit depth to scale the values to [0.0, 1.0].
+		const bits = Math.ceil( Math.log2( maxValue ) );
+		const maxBitValue = Math.pow( 2, bits );
+
+		const data = this.type === UnsignedByteType ? new Uint8Array( dataFloat.length ) : dataFloat;
+		const scale = this.type === UnsignedByteType ? 255 : 1;
+
+		for ( let i = 0, l = data.length; i < l; i += 4 ) {
+
+			const i1 = i + 1;
+			const i2 = i + 2;
+			const i3 = i + 3;
 
 
-			const r = dataArray[ i + 0 ];
-			const g = dataArray[ i + 1 ];
-			const b = dataArray[ i + 2 ];
-			dataArray[ i + 0 ] = 255 * r / maxBitValue; // r
-			dataArray[ i + 1 ] = 255 * g / maxBitValue; // g
-			dataArray[ i + 2 ] = 255 * b / maxBitValue; // b
+			// Note: data is dataFloat when type is FloatType.
+			data[ i ] = dataFloat[ i ] / maxBitValue * scale;
+			data[ i1 ] = dataFloat[ i1 ] / maxBitValue * scale;
+			data[ i2 ] = dataFloat[ i2 ] / maxBitValue * scale;
+			data[ i3 ] = scale;
 
 
 		}
 		}
 
 
-		const data = new Uint8Array( dataArray );
 		const texture = new DataTexture();
 		const texture = new DataTexture();
 		texture.image.data = data;
 		texture.image.data = data;
 		texture.image.width = size;
 		texture.image.width = size;
 		texture.image.height = size * size;
 		texture.image.height = size * size;
 		texture.format = RGBAFormat;
 		texture.format = RGBAFormat;
-		texture.type = UnsignedByteType;
+		texture.type = this.type;
 		texture.magFilter = LinearFilter;
 		texture.magFilter = LinearFilter;
 		texture.minFilter = LinearFilter;
 		texture.minFilter = LinearFilter;
 		texture.wrapS = ClampToEdgeWrapping;
 		texture.wrapS = ClampToEdgeWrapping;
@@ -131,7 +163,7 @@ export class LUT3dlLoader extends Loader {
 		texture3D.image.height = size;
 		texture3D.image.height = size;
 		texture3D.image.depth = size;
 		texture3D.image.depth = size;
 		texture3D.format = RGBAFormat;
 		texture3D.format = RGBAFormat;
-		texture3D.type = UnsignedByteType;
+		texture3D.type = this.type;
 		texture3D.magFilter = LinearFilter;
 		texture3D.magFilter = LinearFilter;
 		texture3D.minFilter = LinearFilter;
 		texture3D.minFilter = LinearFilter;
 		texture3D.wrapS = ClampToEdgeWrapping;
 		texture3D.wrapS = ClampToEdgeWrapping;

+ 81 - 67
examples/jsm/loaders/LUTCubeLoader.js

@@ -1,18 +1,41 @@
 // https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
 // https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
 
 
 import {
 import {
-	Loader,
-	FileLoader,
-	Vector3,
+	ClampToEdgeWrapping,
 	DataTexture,
 	DataTexture,
 	Data3DTexture,
 	Data3DTexture,
-	UnsignedByteType,
-	ClampToEdgeWrapping,
+	FileLoader,
+	FloatType,
 	LinearFilter,
 	LinearFilter,
+	Loader,
+	UnsignedByteType,
+	Vector3,
 } from 'three';
 } from 'three';
 
 
 export class LUTCubeLoader extends Loader {
 export class LUTCubeLoader extends Loader {
 
 
+	constructor( manager ) {
+
+		super( manager );
+
+		this.type = UnsignedByteType;
+
+	}
+
+	setType( type ) {
+
+		if ( type !== UnsignedByteType && type !== FloatType ) {
+
+			throw new Error( 'LUTCubeLoader: Unsupported type' );
+
+		}
+
+		this.type = type;
+
+		return this;
+
+	}
+
 	load( url, onLoad, onProgress, onError ) {
 	load( url, onLoad, onProgress, onError ) {
 
 
 		const loader = new FileLoader( this.manager );
 		const loader = new FileLoader( this.manager );
@@ -44,72 +67,63 @@ export class LUTCubeLoader extends Loader {
 
 
 	}
 	}
 
 
-	parse( str ) {
+	parse( input ) {
 
 
-		// Remove empty lines and comments
-		str = str
-			.replace( /^#.*?(\n|\r)/gm, '' )
-			.replace( /^\s*?(\n|\r)/gm, '' )
-			.trim();
+		const regExpTitle = /TITLE +"([^"]*)"/;
+		const regExpSize = /LUT_3D_SIZE +(\d+)/;
+		const regExpDomainMin = /DOMAIN_MIN +([\d.]+) +([\d.]+) +([\d.]+)/;
+		const regExpDomainMax = /DOMAIN_MAX +([\d.]+) +([\d.]+) +([\d.]+)/;
+		const regExpDataPoints = /^([\d.e+-]+) +([\d.e+-]+) +([\d.e+-]+) *$/gm;
+
+		let result = regExpTitle.exec( input );
+		const title = ( result !== null ) ? result[ 1 ] : null;
+
+		result = regExpSize.exec( input );
+
+		if ( result === null ) {
+
+			throw new Error( 'LUTCubeLoader: Missing LUT_3D_SIZE information' );
+
+		}
+
+		const size = Number( result[ 1 ] );
+		const length = size ** 3 * 4;
+		const data = this.type === UnsignedByteType ? new Uint8Array( length ) : new Float32Array( length );
 
 
-		let title = null;
-		let size = null;
 		const domainMin = new Vector3( 0, 0, 0 );
 		const domainMin = new Vector3( 0, 0, 0 );
 		const domainMax = new Vector3( 1, 1, 1 );
 		const domainMax = new Vector3( 1, 1, 1 );
 
 
-		const lines = str.split( /[\n\r]+/g );
-		let data = null;
-
-		let currIndex = 0;
-		for ( let i = 0, l = lines.length; i < l; i ++ ) {
-
-			const line = lines[ i ].trim();
-			const split = line.split( /\s/g );
-
-			switch ( split[ 0 ] ) {
-
-				case 'TITLE':
-					title = line.substring( 7, line.length - 1 );
-					break;
-				case 'LUT_3D_SIZE':
-					// TODO: A .CUBE LUT file specifies floating point values and could be represented with
-					// more precision than can be captured with Uint8Array.
-					const sizeToken = split[ 1 ];
-					size = parseFloat( sizeToken );
-					data = new Uint8Array( size * size * size * 4 );
-					break;
-				case 'DOMAIN_MIN':
-					domainMin.x = parseFloat( split[ 1 ] );
-					domainMin.y = parseFloat( split[ 2 ] );
-					domainMin.z = parseFloat( split[ 3 ] );
-					break;
-				case 'DOMAIN_MAX':
-					domainMax.x = parseFloat( split[ 1 ] );
-					domainMax.y = parseFloat( split[ 2 ] );
-					domainMax.z = parseFloat( split[ 3 ] );
-					break;
-				default:
-					const r = parseFloat( split[ 0 ] );
-					const g = parseFloat( split[ 1 ] );
-					const b = parseFloat( split[ 2 ] );
-
-					if (
-						r > 1.0 || r < 0.0 ||
-						g > 1.0 || g < 0.0 ||
-						b > 1.0 || b < 0.0
-					) {
-
-						throw new Error( 'LUTCubeLoader : Non normalized values not supported.' );
-
-					}
-
-					data[ currIndex + 0 ] = r * 255;
-					data[ currIndex + 1 ] = g * 255;
-					data[ currIndex + 2 ] = b * 255;
-					data[ currIndex + 3 ] = 255;
-					currIndex += 4;
+		result = regExpDomainMin.exec( input );
 
 
-			}
+		if ( result !== null ) {
+
+			domainMin.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
+
+		}
+
+		result = regExpDomainMax.exec( input );
+
+		if ( result !== null ) {
+
+			domainMax.set( Number( result[ 1 ] ), Number( result[ 2 ] ), Number( result[ 3 ] ) );
+
+		}
+
+		if ( domainMin.x > domainMax.x || domainMin.y > domainMax.y || domainMin.z > domainMax.z ) {
+
+			throw new Error( 'LUTCubeLoader: Invalid input domain' );
+
+		}
+
+		const scale = this.type === UnsignedByteType ? 255 : 1;
+		let i = 0;
+
+		while ( ( result = regExpDataPoints.exec( input ) ) !== null ) {
+
+			data[ i ++ ] = Number( result[ 1 ] ) * scale;
+			data[ i ++ ] = Number( result[ 2 ] ) * scale;
+			data[ i ++ ] = Number( result[ 3 ] ) * scale;
+			data[ i ++ ] = scale;
 
 
 		}
 		}
 
 
@@ -117,7 +131,7 @@ export class LUTCubeLoader extends Loader {
 		texture.image.data = data;
 		texture.image.data = data;
 		texture.image.width = size;
 		texture.image.width = size;
 		texture.image.height = size * size;
 		texture.image.height = size * size;
-		texture.type = UnsignedByteType;
+		texture.type = this.type;
 		texture.magFilter = LinearFilter;
 		texture.magFilter = LinearFilter;
 		texture.minFilter = LinearFilter;
 		texture.minFilter = LinearFilter;
 		texture.wrapS = ClampToEdgeWrapping;
 		texture.wrapS = ClampToEdgeWrapping;
@@ -130,7 +144,7 @@ export class LUTCubeLoader extends Loader {
 		texture3D.image.width = size;
 		texture3D.image.width = size;
 		texture3D.image.height = size;
 		texture3D.image.height = size;
 		texture3D.image.depth = size;
 		texture3D.image.depth = size;
-		texture3D.type = UnsignedByteType;
+		texture3D.type = this.type;
 		texture3D.magFilter = LinearFilter;
 		texture3D.magFilter = LinearFilter;
 		texture3D.minFilter = LinearFilter;
 		texture3D.minFilter = LinearFilter;
 		texture3D.wrapS = ClampToEdgeWrapping;
 		texture3D.wrapS = ClampToEdgeWrapping;

+ 3 - 2
examples/jsm/loaders/LUTImageLoader.js

@@ -12,9 +12,10 @@ import {
 export class LUTImageLoader extends Loader {
 export class LUTImageLoader extends Loader {
 
 
 	constructor( flipVertical = false ) {
 	constructor( flipVertical = false ) {
-		//The NeutralLUT.png has green at the bottom for Unreal ang green at the top for Unity URP Color Lookup 
+
+		//The NeutralLUT.png has green at the bottom for Unreal ang green at the top for Unity URP Color Lookup
 		//post-processing. If you're using lut image strips from a Unity pipeline then pass true to the constructor
 		//post-processing. If you're using lut image strips from a Unity pipeline then pass true to the constructor
-		
+
 		super();
 		super();
 
 
 		this.flip = flipVertical;
 		this.flip = flipVertical;

+ 115 - 16
examples/jsm/loaders/MaterialXLoader.js

@@ -6,9 +6,10 @@ import {
 } from 'three';
 } from 'three';
 
 
 import {
 import {
-	MeshPhysicalNodeMaterial,
+	MeshBasicNodeMaterial, MeshPhysicalNodeMaterial,
 	float, bool, int, vec2, vec3, vec4, color, texture,
 	float, bool, int, vec2, vec3, vec4, color, texture,
-	positionLocal,
+	positionLocal, positionWorld, uv, vertexColor,
+	normalLocal, normalWorld, tangentLocal, tangentWorld,
 	add, sub, mul, div, mod, abs, sign, floor, ceil, round, pow, sin, cos, tan,
 	add, sub, mul, div, mod, abs, sign, floor, ceil, round, pow, sin, cos, tan,
 	asin, acos, atan2, sqrt, exp, clamp, min, max, normalize, length, dot, cross, normalMap,
 	asin, acos, atan2, sqrt, exp, clamp, min, max, normalize, length, dot, cross, normalMap,
 	remap, smoothstep, luminance, mx_rgbtohsv, mx_hsvtorgb,
 	remap, smoothstep, luminance, mx_rgbtohsv, mx_hsvtorgb,
@@ -39,26 +40,34 @@ class MXElement {
 
 
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 // Ref: https://github.com/mrdoob/three.js/issues/24674
 
 
+const mx_add = ( in1, in2 = float( 0 ) ) => add( in1, in2 );
+const mx_subtract = ( in1, in2 = float( 0 ) ) => sub( in1, in2 );
+const mx_multiply = ( in1, in2 = float( 1 ) ) => mul( in1, in2 );
+const mx_divide = ( in1, in2 = float( 1 ) ) => div( in1, in2 );
+const mx_modulo = ( in1, in2 = float( 1 ) ) => mod( in1, in2 );
+const mx_power = ( in1, in2 = float( 1 ) ) => pow( in1, in2 );
+const mx_atan2 = ( in1 = float( 0 ), in2 = float( 1 ) ) => atan2( in1, in2 );
+
 const MXElements = [
 const MXElements = [
 
 
 	// << Math >>
 	// << Math >>
-	new MXElement( 'add', add, [ 'in1', 'in2' ] ),
-	new MXElement( 'subtract', sub, [ 'in1', 'in2' ] ),
-	new MXElement( 'multiply', mul, [ 'in1', 'in2' ] ),
-	new MXElement( 'divide', div, [ 'in1', 'in2' ] ),
-	new MXElement( 'modulo', mod, [ 'in1', 'in2' ] ),
+	new MXElement( 'add', mx_add, [ 'in1', 'in2' ] ),
+	new MXElement( 'subtract', mx_subtract, [ 'in1', 'in2' ] ),
+	new MXElement( 'multiply', mx_multiply, [ 'in1', 'in2' ] ),
+	new MXElement( 'divide', mx_divide, [ 'in1', 'in2' ] ),
+	new MXElement( 'modulo', mx_modulo, [ 'in1', 'in2' ] ),
 	new MXElement( 'absval', abs, [ 'in1', 'in2' ] ),
 	new MXElement( 'absval', abs, [ 'in1', 'in2' ] ),
 	new MXElement( 'sign', sign, [ 'in1', 'in2' ] ),
 	new MXElement( 'sign', sign, [ 'in1', 'in2' ] ),
 	new MXElement( 'floor', floor, [ 'in1', 'in2' ] ),
 	new MXElement( 'floor', floor, [ 'in1', 'in2' ] ),
 	new MXElement( 'ceil', ceil, [ 'in1', 'in2' ] ),
 	new MXElement( 'ceil', ceil, [ 'in1', 'in2' ] ),
 	new MXElement( 'round', round, [ 'in1', 'in2' ] ),
 	new MXElement( 'round', round, [ 'in1', 'in2' ] ),
-	new MXElement( 'power', pow, [ 'in1', 'in2' ] ),
+	new MXElement( 'power', mx_power, [ 'in1', 'in2' ] ),
 	new MXElement( 'sin', sin, [ 'in' ] ),
 	new MXElement( 'sin', sin, [ 'in' ] ),
 	new MXElement( 'cos', cos, [ 'in' ] ),
 	new MXElement( 'cos', cos, [ 'in' ] ),
 	new MXElement( 'tan', tan, [ 'in' ] ),
 	new MXElement( 'tan', tan, [ 'in' ] ),
 	new MXElement( 'asin', asin, [ 'in' ] ),
 	new MXElement( 'asin', asin, [ 'in' ] ),
 	new MXElement( 'acos', acos, [ 'in' ] ),
 	new MXElement( 'acos', acos, [ 'in' ] ),
-	new MXElement( 'atan2', atan2, [ 'in1', 'in2' ] ),
+	new MXElement( 'atan2', mx_atan2, [ 'in1', 'in2' ] ),
 	new MXElement( 'sqrt', sqrt, [ 'in' ] ),
 	new MXElement( 'sqrt', sqrt, [ 'in' ] ),
 	//new MtlXElement( 'ln', ... ),
 	//new MtlXElement( 'ln', ... ),
 	new MXElement( 'exp', exp, [ 'in' ] ),
 	new MXElement( 'exp', exp, [ 'in' ] ),
@@ -141,6 +150,20 @@ class MaterialXLoader extends Loader {
 
 
 	load( url, onLoad, onProgress, onError ) {
 	load( url, onLoad, onProgress, onError ) {
 
 
+		const _onError = function ( e ) {
+
+			if ( onError ) {
+
+				onError( e );
+
+			} else {
+
+				console.error( e );
+
+			}
+
+		};
+
 		new FileLoader( this.manager )
 		new FileLoader( this.manager )
 			.setPath( this.path )
 			.setPath( this.path )
 			.load( url, async ( text ) => {
 			.load( url, async ( text ) => {
@@ -151,11 +174,11 @@ class MaterialXLoader extends Loader {
 
 
 				} catch ( e ) {
 				} catch ( e ) {
 
 
-					onError( e );
+					_onError( e );
 
 
 				}
 				}
 
 
-			}, onProgress, onError );
+			}, onProgress, _onError );
 
 
 		return this;
 		return this;
 
 
@@ -312,7 +335,17 @@ class MaterialXNode {
 
 
 		const filePrefix = this.getRecursiveAttribute( 'fileprefix' ) || '';
 		const filePrefix = this.getRecursiveAttribute( 'fileprefix' ) || '';
 
 
-		const texture = this.materialX.textureLoader.load( filePrefix + this.value );
+		let loader = this.materialX.textureLoader;
+		const uri = filePrefix + this.value;
+
+		if ( uri ) {
+
+			const handler = this.materialX.manager.getHandler( uri );
+			if ( handler !== null ) loader = handler;
+
+		}
+
+		const texture = loader.load( uri );
 		texture.wrapS = texture.wrapT = RepeatWrapping;
 		texture.wrapS = texture.wrapT = RepeatWrapping;
 		texture.flipY = false;
 		texture.flipY = false;
 
 
@@ -376,7 +409,32 @@ class MaterialXNode {
 
 
 			} else if ( element === 'position' ) {
 			} else if ( element === 'position' ) {
 
 
-				node = positionLocal;
+				const space = this.getAttribute( 'space' );
+				node = space === 'world' ? positionWorld : positionLocal;
+
+			} else if ( element === 'normal' ) {
+
+				const space = this.getAttribute( 'space' );
+				node = space === 'world' ? normalWorld : normalLocal;
+
+			} else if ( element === 'tangent' ) {
+
+				const space = this.getAttribute( 'space' );
+				node = space === 'world' ? tangentWorld : tangentLocal;
+
+			} else if ( element === 'texcoord' ) {
+
+				const indexNode = this.getChildByName( 'index' );
+				const index = indexNode ? parseInt( indexNode.value ) : 0;
+
+				node = uv( index );
+
+			} else if ( element === 'geomcolor' ) {
+
+				const indexNode = this.getChildByName( 'index' );
+				const index = indexNode ? parseInt( indexNode.value ) : 0;
+
+				node = vertexColor( index );
 
 
 			} else if ( element === 'tiledimage' ) {
 			} else if ( element === 'tiledimage' ) {
 
 
@@ -597,7 +655,7 @@ class MaterialXNode {
 		let emissiveNode = null;
 		let emissiveNode = null;
 
 
 		if ( inputs.emission ) emissiveNode = inputs.emission;
 		if ( inputs.emission ) emissiveNode = inputs.emission;
-		if ( inputs.emissionColor )  {
+		if ( inputs.emissionColor ) {
 
 
 			emissiveNode = emissiveNode ? mul( emissiveNode, inputs.emissionColor ) : emissiveNode;
 			emissiveNode = emissiveNode ? mul( emissiveNode, inputs.emissionColor ) : emissiveNode;
 
 
@@ -639,7 +697,28 @@ class MaterialXNode {
 
 
 	}
 	}
 
 
-	toMaterial() {
+	toBasicMaterial() {
+
+		const material = new MeshBasicNodeMaterial();
+		material.name = this.name;
+
+		for ( const nodeX of this.children.toReversed() ) {
+
+			if ( nodeX.name === 'out' ) {
+
+				material.colorNode = nodeX.getNode();
+
+				break;
+
+			}
+
+		}
+
+		return material;
+
+	}
+
+	toPhysicalMaterial() {
 
 
 		const material = new MeshPhysicalNodeMaterial();
 		const material = new MeshPhysicalNodeMaterial();
 		material.name = this.name;
 		material.name = this.name;
@@ -659,14 +738,34 @@ class MaterialXNode {
 
 
 		const materials = {};
 		const materials = {};
 
 
+		let isUnlit = true;
+
 		for ( const nodeX of this.children ) {
 		for ( const nodeX of this.children ) {
 
 
 			if ( nodeX.element === 'surfacematerial' ) {
 			if ( nodeX.element === 'surfacematerial' ) {
 
 
-				const material = nodeX.toMaterial();
+				const material = nodeX.toPhysicalMaterial();
 
 
 				materials[ material.name ] = material;
 				materials[ material.name ] = material;
 
 
+				isUnlit = false;
+
+			}
+
+		}
+
+		if ( isUnlit ) {
+
+			for ( const nodeX of this.children ) {
+
+				if ( nodeX.element === 'nodegraph' ) {
+
+					const material = nodeX.toBasicMaterial();
+
+					materials[ material.name ] = material;
+
+				}
+
 			}
 			}
 
 
 		}
 		}

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

@@ -2924,8 +2924,8 @@ class SVGLoader extends Loader {
 			addVertex( currentPointL, u1, 0 );
 			addVertex( currentPointL, u1, 0 );
 
 
 			addVertex( lastPointR, u0, 1 );
 			addVertex( lastPointR, u0, 1 );
-			addVertex( currentPointL, u1, 1 );
-			addVertex( currentPointR, u1, 0 );
+			addVertex( currentPointL, u1, 0 );
+			addVertex( currentPointR, u1, 1 );
 
 
 		}
 		}
 
 
@@ -3082,7 +3082,8 @@ class SVGLoader extends Loader {
 						} else {
 						} else {
 
 
 							tempV2_3.toArray( vertices, 1 * 3 );
 							tempV2_3.toArray( vertices, 1 * 3 );
-							tempV2_3.toArray( vertices, 3 * 3 );
+							// using tempV2_4 to update 3rd vertex if the uv.y of 3rd vertex is 1
+							uvs[ 3 * 2 + 1 ] === 1 ? tempV2_4.toArray( vertices, 3 * 3 ) : tempV2_3.toArray( vertices, 3 * 3 );
 							tempV2_4.toArray( vertices, 0 * 3 );
 							tempV2_4.toArray( vertices, 0 * 3 );
 
 
 						}
 						}

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

@@ -126,7 +126,7 @@ class VRMLLoader extends Loader {
 			// from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics
 			// from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics
 
 
 			const RouteIdentifier = createToken( { name: 'RouteIdentifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/ } );
 			const RouteIdentifier = createToken( { name: 'RouteIdentifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/ } );
-			const Identifier = createToken( { name: 'Identifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/, longer_alt: RouteIdentifier } );
+			const Identifier = createToken( { name: 'Identifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]([^\0-\x20\x22\x27\x23\x2b\x2c\x2e\x5b\x5d\x5c\x7b\x7d])*/, longer_alt: RouteIdentifier } );
 
 
 			// from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html
 			// from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html
 
 

+ 144 - 0
examples/jsm/materials/MeshPostProcessingMaterial.js

@@ -0,0 +1,144 @@
+import { MeshPhysicalMaterial } from 'three';
+
+/**
+ * The aim of this mesh material is to use information from a post processing pass in the diffuse color pass.
+ * This material is based on the MeshPhysicalMaterial.
+ *
+ * In the current state, only the information of a screen space AO pass can be used in the material.
+ * Actually, the output of any screen space AO (SSAO, GTAO) can be used,
+ * as it is only necessary to provide the AO in one color channel of a texture,
+ * however the AO pass must be rendered prior to the color pass,
+ * which makes the post-processing pass somewhat of a pre-processing pass.
+ * Fot this purpose a new map (`aoPassMap`) is added to the material.
+ * The value of the map is used the same way as the `aoMap` value.
+ *
+ * Motivation to use the outputs AO pass directly in the material:
+ * The incident light of a fragment is composed of ambient light, direct light and indirect light
+ * Ambient Occlusion only occludes ambient light and environment light, but not direct light.
+ * Direct light is only occluded by geometry that casts shadows.
+ * And of course the emitted light should not be darkened by ambient occlusion either.
+ * This cannot be achieved if the AO post processing pass is simply blended with the diffuse render pass.
+ *
+ * Further extension work might be to use the output of an SSR pass or an HBIL pass from a previous frame.
+ * This would then create the possibility of SSR and IR depending on material properties such as `roughness`, `metalness` and `reflectivity`.
+**/
+
+class MeshPostProcessingMaterial extends MeshPhysicalMaterial {
+
+	constructor( parameters ) {
+
+		const aoPassMap = parameters.aoPassMap;
+		const aoPassMapScale = parameters.aoPassMapScale || 1.0;
+		delete parameters.aoPassMap;
+		delete parameters.aoPassMapScale;
+
+		super( parameters );
+
+		this.onBeforeCompile = this._onBeforeCompile;
+		this.customProgramCacheKey = this._customProgramCacheKey;
+		this._aoPassMap = aoPassMap;
+		this.aoPassMapScale = aoPassMapScale;
+		this._shader = null;
+
+	}
+
+	get aoPassMap() {
+
+		return this._aoPassMap;
+
+	}
+
+	set aoPassMap( aoPassMap ) {
+
+		this._aoPassMap = aoPassMap;
+		this.needsUpdate = true;
+		this._setUniforms();
+
+	}
+
+	_customProgramCacheKey() {
+
+		return this._aoPassMap !== undefined && this._aoPassMap !== null ? 'aoPassMap' : '';
+
+	}
+
+	_onBeforeCompile( shader ) {
+
+		this._shader = shader;
+
+		if ( this._aoPassMap !== undefined && this._aoPassMap !== null ) {
+
+			shader.fragmentShader = shader.fragmentShader.replace(
+				'#include <aomap_pars_fragment>',
+				aomap_pars_fragment_replacement
+			);
+			shader.fragmentShader = shader.fragmentShader.replace(
+				'#include <aomap_fragment>',
+				aomap_fragment_replacement
+			);
+
+		}
+
+		this._setUniforms();
+
+	}
+
+	_setUniforms() {
+
+		if ( this._shader ) {
+
+			this._shader.uniforms.tAoPassMap = { value: this._aoPassMap };
+			this._shader.uniforms.aoPassMapScale = { value: this.aoPassMapScale };
+
+		}
+
+	}
+
+}
+
+const aomap_pars_fragment_replacement = /* glsl */`
+#ifdef USE_AOMAP
+
+	uniform sampler2D aoMap;
+	uniform float aoMapIntensity;
+
+#endif
+
+	uniform sampler2D tAoPassMap;
+	uniform float aoPassMapScale;
+`;
+
+const aomap_fragment_replacement = /* glsl */`
+#ifndef AOPASSMAP_SWIZZLE
+	#define AOPASSMAP_SWIZZLE r
+#endif
+	float ambientOcclusion = texelFetch( tAoPassMap, ivec2( gl_FragCoord.xy * aoPassMapScale ), 0 ).AOPASSMAP_SWIZZLE;
+
+#ifdef USE_AOMAP
+
+	// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
+	ambientOcclusion = min( ambientOcclusion, texture2D( aoMap, vAoMapUv ).r );
+	ambientOcclusion *= ( ambientOcclusion - 1.0 ) * aoMapIntensity + 1.0;
+
+#endif
+
+	reflectedLight.indirectDiffuse *= ambientOcclusion;
+
+	#if defined( USE_CLEARCOAT ) 
+		clearcoatSpecularIndirect *= ambientOcclusion;
+	#endif
+
+	#if defined( USE_SHEEN ) 
+		sheenSpecularIndirect *= ambientOcclusion;
+	#endif
+
+	#if defined( USE_ENVMAP ) && defined( STANDARD )
+
+		float dotNV = saturate( dot( geometryNormal, geometryViewDir ) );
+
+		reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );
+
+	#endif
+`;
+
+export { MeshPostProcessingMaterial };

+ 13 - 4
examples/jsm/misc/Timer.js

@@ -73,11 +73,20 @@ class Timer {
 
 
 	update( timestamp ) {
 	update( timestamp ) {
 
 
-		this._previousTime = this._currentTime;
-		this._currentTime = ( timestamp !== undefined ? timestamp : now() ) - this._startTime;
 
 
-		this._delta = ( this._currentTime - this._previousTime ) * this._timescale;
-		this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas
+		if ( this._usePageVisibilityAPI === true && document.hidden === true ) {
+
+			this._delta = 0;
+
+		} else {
+
+			this._previousTime = this._currentTime;
+			this._currentTime = ( timestamp !== undefined ? timestamp : now() ) - this._startTime;
+
+			this._delta = ( this._currentTime - this._previousTime ) * this._timescale;
+			this._elapsed += this._delta; // _elapsed is the accumulation of all previous deltas
+
+		}
 
 
 		return this;
 		return this;
 
 

+ 16 - 5
examples/jsm/nodes/Nodes.js

@@ -39,11 +39,16 @@ import * as NodeUtils from './core/NodeUtils.js';
 export { NodeUtils };
 export { NodeUtils };
 
 
 // math
 // math
-export { default as MathNode, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, bitcast, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward } from './math/MathNode.js';
-export { default as OperatorNode, add, sub, mul, div, remainder, equal, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, xor, bitAnd, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js';
+export { default as MathNode, PI, PI2, EPSILON, INFINITY, radians, degrees, exp, exp2, log, log2, sqrt, inverseSqrt, floor, ceil, normalize, fract, sin, cos, tan, asin, acos, atan, abs, sign, length, lengthSq, negate, oneMinus, dFdx, dFdy, round, reciprocal, trunc, fwidth, bitcast, atan2, min, max, mod, step, reflect, distance, difference, dot, cross, pow, pow2, pow3, pow4, transformDirection, mix, clamp, saturate, refract, smoothstep, faceForward, cbrt } from './math/MathNode.js';
+
+export { default as OperatorNode, add, sub, mul, div, remainder, equal, lessThan, greaterThan, lessThanEqual, greaterThanEqual, and, or, not, xor, bitAnd, bitNot, bitOr, bitXor, shiftLeft, shiftRight } from './math/OperatorNode.js';
 export { default as CondNode, cond } from './math/CondNode.js';
 export { default as CondNode, cond } from './math/CondNode.js';
 export { default as HashNode, hash } from './math/HashNode.js';
 export { default as HashNode, hash } from './math/HashNode.js';
 
 
+// math utils
+export { parabola, gain, pcurve, sinc } from './math/MathUtils.js';
+export { triNoise3D } from './math/TriNoise3D.js';
+
 // utils
 // utils
 export { default as ArrayElementNode } from './utils/ArrayElementNode.js';
 export { default as ArrayElementNode } from './utils/ArrayElementNode.js';
 export { default as ConvertNode } from './utils/ConvertNode.js';
 export { default as ConvertNode } from './utils/ConvertNode.js';
@@ -58,12 +63,14 @@ export { default as OscNode, oscSine, oscSquare, oscTriangle, oscSawtooth } from
 export { default as PackingNode, directionToColor, colorToDirection } from './utils/PackingNode.js';
 export { default as PackingNode, directionToColor, colorToDirection } from './utils/PackingNode.js';
 export { default as RemapNode, remap, remapClamp } from './utils/RemapNode.js';
 export { default as RemapNode, remap, remapClamp } from './utils/RemapNode.js';
 export { default as RotateUVNode, rotateUV } from './utils/RotateUVNode.js';
 export { default as RotateUVNode, rotateUV } from './utils/RotateUVNode.js';
+export { default as RotateNode, rotate } from './utils/RotateNode.js';
 export { default as SetNode } from './utils/SetNode.js';
 export { default as SetNode } from './utils/SetNode.js';
 export { default as SpecularMIPLevelNode, specularMIPLevel } from './utils/SpecularMIPLevelNode.js';
 export { default as SpecularMIPLevelNode, specularMIPLevel } from './utils/SpecularMIPLevelNode.js';
 export { default as SplitNode } from './utils/SplitNode.js';
 export { default as SplitNode } from './utils/SplitNode.js';
 export { default as SpriteSheetUVNode, spritesheetUV } from './utils/SpriteSheetUVNode.js';
 export { default as SpriteSheetUVNode, spritesheetUV } from './utils/SpriteSheetUVNode.js';
 export { default as TimerNode, timerLocal, timerGlobal, timerDelta, frameId } from './utils/TimerNode.js';
 export { default as TimerNode, timerLocal, timerGlobal, timerDelta, frameId } from './utils/TimerNode.js';
 export { default as TriplanarTexturesNode, triplanarTextures, triplanarTexture } from './utils/TriplanarTexturesNode.js';
 export { default as TriplanarTexturesNode, triplanarTextures, triplanarTexture } from './utils/TriplanarTexturesNode.js';
+export { default as ReflectorNode, reflector } from './utils/ReflectorNode.js';
 
 
 // shadernode
 // shadernode
 export * from './shadernode/ShaderNode.js';
 export * from './shadernode/ShaderNode.js';
@@ -72,7 +79,8 @@ export * from './shadernode/ShaderNode.js';
 export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
 export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
 export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute, instancedBufferAttribute, instancedDynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
 export { default as BufferAttributeNode, bufferAttribute, dynamicBufferAttribute, instancedBufferAttribute, instancedDynamicBufferAttribute } from './accessors/BufferAttributeNode.js';
 export { default as BufferNode, buffer } from './accessors/BufferNode.js';
 export { default as BufferNode, buffer } from './accessors/BufferNode.js';
-export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition, cameraNear, cameraFar, cameraLogDepth } from './accessors/CameraNode.js';
+export { default as CameraNode, cameraProjectionMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition, cameraNear, cameraFar, cameraLogDepth } from './accessors/CameraNode.js';
+export { default as VertexColorNode, vertexColor } from './accessors/VertexColorNode.js';
 export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
 export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';
 export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
 export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
 export { default as MaterialNode, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialSpecularStrength, materialReflectivity, materialRoughness, materialMetalness, materialNormal, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialRotation, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLineScale, materialLineDashSize, materialLineGapSize, materialLineWidth, materialLineDashOffset, materialPointWidth } from './accessors/MaterialNode.js';
 export { default as MaterialNode, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialSpecularStrength, materialReflectivity, materialRoughness, materialMetalness, materialNormal, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialRotation, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLineScale, materialLineDashSize, materialLineGapSize, materialLineWidth, materialLineDashOffset, materialPointWidth } from './accessors/MaterialNode.js';
@@ -99,7 +107,7 @@ export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
 // display
 // display
 export { default as BlendModeNode, burn, dodge, overlay, screen } from './display/BlendModeNode.js';
 export { default as BlendModeNode, burn, dodge, overlay, screen } from './display/BlendModeNode.js';
 export { default as BumpMapNode, bumpMap } from './display/BumpMapNode.js';
 export { default as BumpMapNode, bumpMap } from './display/BumpMapNode.js';
-export { default as ColorAdjustmentNode, saturation, vibrance, hue, lumaCoeffs, luminance } from './display/ColorAdjustmentNode.js';
+export { default as ColorAdjustmentNode, saturation, vibrance, hue, lumaCoeffs, luminance, threshold } from './display/ColorAdjustmentNode.js';
 export { default as ColorSpaceNode, linearToColorSpace, colorSpaceToLinear, linearTosRGB, sRGBToLinear } from './display/ColorSpaceNode.js';
 export { default as ColorSpaceNode, linearToColorSpace, colorSpaceToLinear, linearTosRGB, sRGBToLinear } from './display/ColorSpaceNode.js';
 export { default as FrontFacingNode, frontFacing, faceDirection } from './display/FrontFacingNode.js';
 export { default as FrontFacingNode, frontFacing, faceDirection } from './display/FrontFacingNode.js';
 export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/NormalMapNode.js';
 export { default as NormalMapNode, normalMap, TBNViewMatrix } from './display/NormalMapNode.js';
@@ -111,6 +119,9 @@ export { default as ViewportSharedTextureNode, viewportSharedTexture } from './d
 export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
 export { default as ViewportDepthTextureNode, viewportDepthTexture } from './display/ViewportDepthTextureNode.js';
 export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
 export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDepthToViewZ, viewZToPerspectiveDepth, perspectiveDepthToViewZ, depth, depthTexture, depthPixel } from './display/ViewportDepthNode.js';
 export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlurNode.js';
 export { default as GaussianBlurNode, gaussianBlur } from './display/GaussianBlurNode.js';
+export { default as AfterImageNode, afterImage } from './display/AfterImageNode.js';
+export { default as AnamorphicNode, anamorphic } from './display/AnamorphicNode.js';
+
 export { default as PassNode, pass, depthPass } from './display/PassNode.js';
 export { default as PassNode, pass, depthPass } from './display/PassNode.js';
 
 
 // code
 // code
@@ -139,7 +150,7 @@ export { default as DirectionalLightNode } from './lighting/DirectionalLightNode
 export { default as SpotLightNode } from './lighting/SpotLightNode.js';
 export { default as SpotLightNode } from './lighting/SpotLightNode.js';
 export { default as IESSpotLightNode } from './lighting/IESSpotLightNode.js';
 export { default as IESSpotLightNode } from './lighting/IESSpotLightNode.js';
 export { default as AmbientLightNode } from './lighting/AmbientLightNode.js';
 export { default as AmbientLightNode } from './lighting/AmbientLightNode.js';
-export { default as LightsNode, lights, lightNodes, addLightNode } from './lighting/LightsNode.js';
+export { default as LightsNode, lights, lightsNode, addLightNode } from './lighting/LightsNode.js';
 export { default as LightingNode /* @TODO: lighting (abstract), light */ } from './lighting/LightingNode.js';
 export { default as LightingNode /* @TODO: lighting (abstract), light */ } from './lighting/LightingNode.js';
 export { default as LightingContextNode, lightingContext } from './lighting/LightingContextNode.js';
 export { default as LightingContextNode, lightingContext } from './lighting/LightingContextNode.js';
 export { default as HemisphereLightNode } from './lighting/HemisphereLightNode.js';
 export { default as HemisphereLightNode } from './lighting/HemisphereLightNode.js';

+ 5 - 3
examples/jsm/nodes/accessors/BufferAttributeNode.js

@@ -67,12 +67,14 @@ class BufferAttributeNode extends InputNode {
 
 
 		const nodeType = this.getNodeType( builder );
 		const nodeType = this.getNodeType( builder );
 
 
-		const nodeUniform = builder.getBufferAttributeFromNode( this, nodeType );
-		const propertyName = builder.getPropertyName( nodeUniform );
+		const nodeAttribute = builder.getBufferAttributeFromNode( this, nodeType );
+		const propertyName = builder.getPropertyName( nodeAttribute );
 
 
 		let output = null;
 		let output = null;
 
 
-		if ( builder.shaderStage === 'vertex' ) {
+		if ( builder.shaderStage === 'vertex' || builder.shaderStage === 'compute' ) {
+
+			this.name = propertyName;
 
 
 			output = propertyName;
 			output = propertyName;
 
 

+ 9 - 4
examples/jsm/nodes/accessors/CameraNode.js

@@ -1,6 +1,5 @@
 import Object3DNode from './Object3DNode.js';
 import Object3DNode from './Object3DNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
-import { label } from '../core/ContextNode.js';
 import { NodeUpdateType } from '../core/constants.js';
 import { NodeUpdateType } from '../core/constants.js';
 //import { sharedUniformGroup } from '../core/UniformGroupNode.js';
 //import { sharedUniformGroup } from '../core/UniformGroupNode.js';
 import { nodeImmutable } from '../shadernode/ShaderNode.js';
 import { nodeImmutable } from '../shadernode/ShaderNode.js';
@@ -23,7 +22,7 @@ class CameraNode extends Object3DNode {
 
 
 		const scope = this.scope;
 		const scope = this.scope;
 
 
-		if ( scope === CameraNode.PROJECTION_MATRIX ) {
+		if ( scope === CameraNode.PROJECTION_MATRIX || scope === CameraNode.PROJECTION_MATRIX_INVERSE ) {
 
 
 			return 'mat4';
 			return 'mat4';
 
 
@@ -53,6 +52,10 @@ class CameraNode extends Object3DNode {
 
 
 			uniformNode.value = camera.projectionMatrix;
 			uniformNode.value = camera.projectionMatrix;
 
 
+		} else if ( scope === CameraNode.PROJECTION_MATRIX_INVERSE ) {
+
+			uniformNode.value = camera.projectionMatrixInverse;
+
 		} else if ( scope === CameraNode.NEAR ) {
 		} else if ( scope === CameraNode.NEAR ) {
 
 
 			uniformNode.value = camera.near;
 			uniformNode.value = camera.near;
@@ -79,7 +82,7 @@ class CameraNode extends Object3DNode {
 
 
 		const scope = this.scope;
 		const scope = this.scope;
 
 
-		if ( scope === CameraNode.PROJECTION_MATRIX ) {
+		if ( scope === CameraNode.PROJECTION_MATRIX || scope === CameraNode.PROJECTION_MATRIX_INVERSE ) {
 
 
 			this._uniformNode.nodeType = 'mat4';
 			this._uniformNode.nodeType = 'mat4';
 
 
@@ -96,13 +99,15 @@ class CameraNode extends Object3DNode {
 }
 }
 
 
 CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
 CameraNode.PROJECTION_MATRIX = 'projectionMatrix';
+CameraNode.PROJECTION_MATRIX_INVERSE = 'projectionMatrixInverse';
 CameraNode.NEAR = 'near';
 CameraNode.NEAR = 'near';
 CameraNode.FAR = 'far';
 CameraNode.FAR = 'far';
 CameraNode.LOG_DEPTH = 'logDepth';
 CameraNode.LOG_DEPTH = 'logDepth';
 
 
 export default CameraNode;
 export default CameraNode;
 
 
-export const cameraProjectionMatrix = label( nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX ), 'projectionMatrix' );
+export const cameraProjectionMatrix = nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX );
+export const cameraProjectionMatrixInverse = nodeImmutable( CameraNode, CameraNode.PROJECTION_MATRIX_INVERSE );
 export const cameraNear = nodeImmutable( CameraNode, CameraNode.NEAR );
 export const cameraNear = nodeImmutable( CameraNode, CameraNode.NEAR );
 export const cameraFar = nodeImmutable( CameraNode, CameraNode.FAR );
 export const cameraFar = nodeImmutable( CameraNode, CameraNode.FAR );
 export const cameraLogDepth = nodeImmutable( CameraNode, CameraNode.LOG_DEPTH );
 export const cameraLogDepth = nodeImmutable( CameraNode, CameraNode.LOG_DEPTH );

+ 12 - 1
examples/jsm/nodes/accessors/CubeTextureNode.js

@@ -2,6 +2,7 @@ import TextureNode from './TextureNode.js';
 import { reflectVector } from './ReflectVectorNode.js';
 import { reflectVector } from './ReflectVectorNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
+import { WebGPUCoordinateSystem } from 'three';
 
 
 class CubeTextureNode extends TextureNode {
 class CubeTextureNode extends TextureNode {
 
 
@@ -29,7 +30,17 @@ class CubeTextureNode extends TextureNode {
 
 
 	setupUV( builder, uvNode ) {
 	setupUV( builder, uvNode ) {
 
 
-		return vec3( uvNode.x.negate(), uvNode.yz );
+		const texture = this.value;
+
+		if ( builder.renderer.coordinateSystem === WebGPUCoordinateSystem || ! texture.isRenderTargetTexture ) {
+
+			return vec3( uvNode.x.negate(), uvNode.yz );
+
+		} else {
+
+			return uvNode;
+
+		}
 
 
 	}
 	}
 
 

+ 27 - 0
examples/jsm/nodes/accessors/StorageBufferNode.js

@@ -1,6 +1,8 @@
 import BufferNode from './BufferNode.js';
 import BufferNode from './BufferNode.js';
+import { bufferAttribute } from './BufferAttributeNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { nodeObject } from '../shadernode/ShaderNode.js';
 import { nodeObject } from '../shadernode/ShaderNode.js';
+import { varying } from '../core/VaryingNode.js';
 
 
 class StorageBufferNode extends BufferNode {
 class StorageBufferNode extends BufferNode {
 
 
@@ -10,6 +12,9 @@ class StorageBufferNode extends BufferNode {
 
 
 		this.isStorageBufferNode = true;
 		this.isStorageBufferNode = true;
 
 
+		this._attribute = null;
+		this._varying = null;
+
 	}
 	}
 
 
 	getInputType( /*builder*/ ) {
 	getInputType( /*builder*/ ) {
@@ -18,6 +23,28 @@ class StorageBufferNode extends BufferNode {
 
 
 	}
 	}
 
 
+	generate( builder ) {
+
+		if ( builder.isAvailable( 'storageBuffer' ) ) return super.generate( builder );
+
+		const nodeType = this.getNodeType( builder );
+
+		if ( this._attribute === null ) {
+
+			this._attribute = bufferAttribute( this.value );
+			this._varying = varying( this._attribute );
+
+		}
+
+
+		const output = this._varying.build( builder, nodeType );
+
+		builder.registerTransform( output, this._attribute );
+
+		return output;
+
+	}
+
 }
 }
 
 
 export default StorageBufferNode;
 export default StorageBufferNode;

+ 56 - 3
examples/jsm/nodes/accessors/TextureStoreNode.js

@@ -14,9 +14,52 @@ class TextureStoreNode extends TextureNode {
 
 
 	}
 	}
 
 
-	getNodeType( /*builder*/ ) {
+	getInputType( /*builder*/ ) {
 
 
-		return 'void';
+		return 'storageTexture';
+
+	}
+
+	setup( builder ) {
+
+		super.setup( builder );
+
+		const properties = builder.getNodeProperties( this );
+		properties.storeNode = this.storeNode;
+
+	}
+
+	generate( builder, output ) {
+
+		let snippet;
+
+		if ( this.storeNode !== null ) {
+
+			snippet = this.generateStore( builder );
+
+		} else {
+
+			snippet = super.generate( builder, output );
+
+		}
+
+		return snippet;
+
+	}
+
+	generateStore( builder ) {
+
+		const properties = builder.getNodeProperties( this );
+
+		const { uvNode, storeNode } = properties;
+
+		const textureProperty = super.generate( builder, 'property' );
+		const uvSnippet = uvNode.build( builder, 'uvec2' );
+		const storeSnippet = storeNode.build( builder, 'vec4' );
+
+		const snippet = builder.generateTextureStore( builder, textureProperty, uvSnippet, storeSnippet );
+
+		builder.addLineFlowCode( snippet );
 
 
 	}
 	}
 
 
@@ -24,6 +67,16 @@ class TextureStoreNode extends TextureNode {
 
 
 export default TextureStoreNode;
 export default TextureStoreNode;
 
 
-export const textureStore = nodeProxy( TextureStoreNode );
+const textureStoreBase = nodeProxy( TextureStoreNode );
+
+export const textureStore = ( value, uvNode, storeNode ) => {
+
+	const node = textureStoreBase( value, uvNode, storeNode );
+
+	if ( storeNode !== null ) node.append();
+
+	return node;
+
+};
 
 
 addNodeClass( 'TextureStoreNode', TextureStoreNode );
 addNodeClass( 'TextureStoreNode', TextureStoreNode );

+ 70 - 0
examples/jsm/nodes/accessors/VertexColorNode.js

@@ -0,0 +1,70 @@
+import { addNodeClass } from '../core/Node.js';
+import AttributeNode from '../core/AttributeNode.js';
+import { nodeObject } from '../shadernode/ShaderNode.js';
+import { Vector4 } from 'three';
+
+class VertexColorNode extends AttributeNode {
+
+	constructor( index = 0 ) {
+
+		super( null, 'vec4' );
+
+		this.isVertexColorNode = true;
+
+		this.index = index;
+
+	}
+
+	getAttributeName( /*builder*/ ) {
+
+		const index = this.index;
+
+		return 'color' + ( index > 0 ? index : '' );
+
+	}
+
+	generate( builder ) {
+
+		const attributeName = this.getAttributeName( builder );
+		const geometryAttribute = builder.hasGeometryAttribute( attributeName );
+
+		let result;
+
+		if ( geometryAttribute === true ) {
+
+			result = super.generate( builder );
+
+		} else {
+
+			// Vertex color fallback should be white
+			result = builder.generateConst( this.nodeType, new Vector4( 1, 1, 1, 1 ) );
+
+		}
+
+		return result;
+
+	}
+
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.index = this.index;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.index = data.index;
+
+	}
+
+}
+
+export default VertexColorNode;
+
+export const vertexColor = ( ...params ) => nodeObject( new VertexColorNode( ...params ) );
+
+addNodeClass( 'VertexColorNode', VertexColorNode );

+ 1 - 1
examples/jsm/nodes/core/AttributeNode.js

@@ -91,7 +91,7 @@ class AttributeNode extends Node {
 
 
 		} else {
 		} else {
 
 
-			console.warn( `AttributeNode: Attribute "${ attributeName }" not found.` );
+			console.warn( `AttributeNode: Vertex attribute "${ attributeName }" not found on geometry.` );
 
 
 			return builder.generateConst( nodeType );
 			return builder.generateConst( nodeType );
 
 

+ 23 - 18
examples/jsm/nodes/core/NodeBuilder.js

@@ -7,7 +7,7 @@ import NodeKeywords from './NodeKeywords.js';
 import NodeCache from './NodeCache.js';
 import NodeCache from './NodeCache.js';
 import ParameterNode from './ParameterNode.js';
 import ParameterNode from './ParameterNode.js';
 import FunctionNode from '../code/FunctionNode.js';
 import FunctionNode from '../code/FunctionNode.js';
-import { createNodeMaterialFromType } from '../materials/NodeMaterial.js';
+import { createNodeMaterialFromType, default as NodeMaterial } from '../materials/NodeMaterial.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 import { NodeUpdateType, defaultBuildStages, shaderStages } from './constants.js';
 
 
 import {
 import {
@@ -462,7 +462,7 @@ class NodeBuilder {
 
 
 	isReference( type ) {
 	isReference( type ) {
 
 
-		return type === 'void' || type === 'property' || type === 'sampler' || type === 'texture' || type === 'cubeTexture';
+		return type === 'void' || type === 'property' || type === 'sampler' || type === 'texture' || type === 'cubeTexture' || type === 'storageTexture';
 
 
 	}
 	}
 
 
@@ -523,7 +523,7 @@ class NodeBuilder {
 	getVectorType( type ) {
 	getVectorType( type ) {
 
 
 		if ( type === 'color' ) return 'vec3';
 		if ( type === 'color' ) return 'vec3';
-		if ( type === 'texture' ) return 'vec4';
+		if ( type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' ) return 'vec4';
 
 
 		return type;
 		return type;
 
 
@@ -575,6 +575,7 @@ class NodeBuilder {
 
 
 		if ( vecNum !== null ) return Number( vecNum[ 1 ] );
 		if ( vecNum !== null ) return Number( vecNum[ 1 ] );
 		if ( vecType === 'float' || vecType === 'bool' || vecType === 'int' || vecType === 'uint' ) return 1;
 		if ( vecType === 'float' || vecType === 'bool' || vecType === 'int' || vecType === 'uint' ) return 1;
+		if ( /mat2/.test( type ) === true ) return 4;
 		if ( /mat3/.test( type ) === true ) return 9;
 		if ( /mat3/.test( type ) === true ) return 9;
 		if ( /mat4/.test( type ) === true ) return 16;
 		if ( /mat4/.test( type ) === true ) return 16;
 
 
@@ -1089,7 +1090,23 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
-	build() {
+	build( convertMaterial = true ) {
+
+		const { object, material } = this;
+
+		if ( convertMaterial ) {
+
+			if ( material !== null ) {
+
+				NodeMaterial.fromMaterial( material ).build( this );
+
+			} else {
+
+				this.addFlow( 'compute', object );
+
+			}
+
+		}
 
 
 		// setup() -> stage 1: create possible new nodes and returns an output reference node
 		// setup() -> stage 1: create possible new nodes and returns an output reference node
 		// analyze()   -> stage 2: analyze nodes to possible optimization and validation
 		// analyze()   -> stage 2: analyze nodes to possible optimization and validation
@@ -1155,24 +1172,12 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
-	createNodeMaterial( type ) {
+	createNodeMaterial( type = 'NodeMaterial' ) {
 
 
 		return createNodeMaterialFromType( type );
 		return createNodeMaterialFromType( type );
 
 
 	}
 	}
 
 
-	getPrimitiveType( type ) {
-
-		let primitiveType;
-
-		if ( type[ 0 ] === 'i' ) primitiveType = 'int';
-		else if ( type[ 0 ] === 'u' ) primitiveType = 'uint';
-		else primitiveType = 'float';
-
-		return primitiveType;
-
-	}
-
 	format( snippet, fromType, toType ) {
 	format( snippet, fromType, toType ) {
 
 
 		fromType = this.getVectorType( fromType );
 		fromType = this.getVectorType( fromType );
@@ -1232,7 +1237,7 @@ class NodeBuilder {
 			// convert a number value to vector type, e.g:
 			// convert a number value to vector type, e.g:
 			// vec3( 1u ) -> vec3( float( 1u ) )
 			// vec3( 1u ) -> vec3( float( 1u ) )
 
 
-			snippet = `${ this.getType( this.getPrimitiveType( toType ) ) }( ${ snippet } )`;
+			snippet = `${ this.getType( this.getComponentType( toType ) ) }( ${ snippet } )`;
 
 
 		}
 		}
 
 

+ 16 - 8
examples/jsm/nodes/core/NodeFrame.js

@@ -53,9 +53,11 @@ class NodeFrame {
 
 
 			if ( frameMap.get( node ) !== this.frameId ) {
 			if ( frameMap.get( node ) !== this.frameId ) {
 
 
-				frameMap.set( node, this.frameId );
+				if ( node.updateBefore( this ) !== false ) {
 
 
-				node.updateBefore( this );
+					frameMap.set( node, this.frameId );
+
+				}
 
 
 			}
 			}
 
 
@@ -65,9 +67,11 @@ class NodeFrame {
 
 
 			if ( renderMap.get( node ) !== this.renderId ) {
 			if ( renderMap.get( node ) !== this.renderId ) {
 
 
-				renderMap.set( node, this.renderId );
+				if ( node.updateBefore( this ) !== false ) {
+
+					renderMap.set( node, this.renderId );
 
 
-				node.updateBefore( this );
+				}
 
 
 			}
 			}
 
 
@@ -90,9 +94,11 @@ class NodeFrame {
 
 
 			if ( frameMap.get( node ) !== this.frameId ) {
 			if ( frameMap.get( node ) !== this.frameId ) {
 
 
-				frameMap.set( node, this.frameId );
+				if ( node.update( this ) !== false ) {
 
 
-				node.update( this );
+					frameMap.set( node, this.frameId );
+
+				}
 
 
 			}
 			}
 
 
@@ -102,9 +108,11 @@ class NodeFrame {
 
 
 			if ( renderMap.get( node ) !== this.renderId ) {
 			if ( renderMap.get( node ) !== this.renderId ) {
 
 
-				renderMap.set( node, this.renderId );
+				if ( node.update( this ) !== false ) {
+
+					renderMap.set( node, this.renderId );
 
 
-				node.update( this );
+				}
 
 
 			}
 			}
 
 

+ 1 - 0
examples/jsm/nodes/core/constants.js

@@ -17,6 +17,7 @@ export const NodeType = {
 	VECTOR2: 'vec2',
 	VECTOR2: 'vec2',
 	VECTOR3: 'vec3',
 	VECTOR3: 'vec3',
 	VECTOR4: 'vec4',
 	VECTOR4: 'vec4',
+	MATRIX2: 'mat2',
 	MATRIX3: 'mat3',
 	MATRIX3: 'mat3',
 	MATRIX4: 'mat4'
 	MATRIX4: 'mat4'
 };
 };

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