Przeglądaj źródła

Docs: More JSDoc. (#30692)

Michael Herzog 1 rok temu
rodzic
commit
54f8b109ee

+ 88 - 29
examples/jsm/utils/BufferGeometryUtils.js

@@ -11,6 +11,27 @@ import {
 	Vector3,
 } from 'three';
 
+/** @module BufferGeometryUtils */
+
+/**
+ * Computes vertex tangents using the MikkTSpace algorithm. MikkTSpace generates the same tangents consistently,
+ * and is used in most modelling tools and normal map bakers. Use MikkTSpace for materials with normal maps,
+ * because inconsistent tangents may lead to subtle visual issues in the normal map, particularly around mirrored
+ * UV seams.
+ *
+ * In comparison to this method, {@link BufferGeometry#computeTangents} (a custom algorithm) generates tangents that
+ * probably will not match the tangents in other software. The custom algorithm is sufficient for general use with a
+ * custom material, and may be faster than MikkTSpace.
+ *
+ * Returns the original BufferGeometry. Indexed geometries will be de-indexed. Requires position, normal, and uv attributes.
+ *
+ * @param {BufferGeometry} geometry - The geometry to compute tangents for.
+ * @param {Object} MikkTSpace - Instance of `examples/jsm/libs/mikktspace.module.js`, or `mikktspace` npm package.
+ * Aawait `MikkTSpace.ready` before use.
+ * @param {boolean} [negateSign=true] - Whether to negate the sign component (.w) of each tangent.
+ * Required for normal map conventions in some formats, including glTF.
+ * @return {BufferGeometry} The updated geometry.
+ */
 function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
 
 	if ( ! MikkTSpace || ! MikkTSpace.isReady ) {
@@ -100,9 +121,11 @@ function computeMikkTSpaceTangents( geometry, MikkTSpace, negateSign = true ) {
 }
 
 /**
- * @param  {Array<BufferGeometry>} geometries
- * @param  {boolean} useGroups
- * @return {?BufferGeometry}
+ * Merges a set of geometries into a single instance. All geometries must have compatible attributes.
+ *
+ * @param {Array<BufferGeometry>} geometries - The geometries to merge.
+ * @param {boolean} [useGroups=false] - Whether to use groups or not.
+ * @return {?BufferGeometry} The merged geometry. Returns `null` if the merge does not succeed.
  */
 function mergeGeometries( geometries, useGroups = false ) {
 
@@ -296,8 +319,11 @@ function mergeGeometries( geometries, useGroups = false ) {
 }
 
 /**
- * @param {Array<BufferAttribute>} attributes
- * @return {?BufferAttribute}
+ * Merges a set of attributes into a single instance. All attributes must have compatible properties and types.
+ * Instances of {@link InterleavedBufferAttribute} are not supported.
+ *
+ * @param {Array<BufferAttribute>} attributes - The attributes to merge.
+ * @return {?BufferAttribute} The merged attribute. Returns `null` if the merge does not succeed.
  */
 function mergeAttributes( attributes ) {
 
@@ -389,10 +415,12 @@ function mergeAttributes( attributes ) {
 }
 
 /**
- * @param {BufferAttribute} attribute
- * @return {BufferAttribute}
+ * Performs a deep clone of the given buffer attribute.
+ *
+ * @param {BufferAttribute} attribute - The attribute to clone.
+ * @return {BufferAttribute} The cloned attribute.
  */
-export function deepCloneAttribute( attribute ) {
+function deepCloneAttribute( attribute ) {
 
 	if ( attribute.isInstancedInterleavedBufferAttribute || attribute.isInterleavedBufferAttribute ) {
 
@@ -411,8 +439,11 @@ export function deepCloneAttribute( attribute ) {
 }
 
 /**
- * @param {Array<BufferAttribute>} attributes
- * @return {Array<InterleavedBufferAttribute>}
+ * Interleaves a set of attributes and returns a new array of corresponding attributes that share a
+ * single {@link InterleavedBuffer} instance. All attributes must have compatible types.
+ *
+ * @param {Array<BufferAttribute>} attributes - The attributes to interleave.
+ * @return {Array<InterleavedBufferAttribute>} An array of interleaved attributes. If interleave does not succeed, the method returns `null`.
  */
 function interleaveAttributes( attributes ) {
 
@@ -475,8 +506,13 @@ function interleaveAttributes( attributes ) {
 
 }
 
-// returns a new, non-interleaved version of the provided attribute
-export function deinterleaveAttribute( attribute ) {
+/**
+ * Returns a new, non-interleaved version of the given attribute.
+ *
+ * @param {InterleavedBufferAttribute} attribute - The interleaved attribute.
+ * @return {BufferAttribute} The non-interleaved attribute.
+ */
+function deinterleaveAttribute( attribute ) {
 
 	const cons = attribute.data.array.constructor;
 	const count = attribute.count;
@@ -523,8 +559,12 @@ export function deinterleaveAttribute( attribute ) {
 
 }
 
-// deinterleaves all attributes on the geometry
-export function deinterleaveGeometry( geometry ) {
+/**
+ * Deinterleaves all attributes on the given geometry.
+ *
+ * @param {BufferGeometry} geometry - The geometry to deinterleave.
+ */
+function deinterleaveGeometry( geometry ) {
 
 	const attributes = geometry.attributes;
 	const morphTargets = geometry.morphTargets;
@@ -567,8 +607,10 @@ export function deinterleaveGeometry( geometry ) {
 }
 
 /**
- * @param {BufferGeometry} geometry
- * @return {number}
+ * Returns the amount of bytes used by all attributes to represent the geometry.
+ *
+ * @param {BufferGeometry} geometry - The geometry.
+ * @return {number} The estimate bytes used.
  */
 function estimateBytesUsed( geometry ) {
 
@@ -590,9 +632,11 @@ function estimateBytesUsed( geometry ) {
 }
 
 /**
- * @param {BufferGeometry} geometry
- * @param {number} tolerance
- * @return {BufferGeometry}
+ * Returns a new geometry with vertices for which all similar vertex attributes (within tolerance) are merged.
+ *
+ * @param {BufferGeometry} geometry - The geometry to merge vertices for.
+ * @param {number} [tolerance=1e-4] - The tolerance value.
+ * @return {BufferGeometry} - The new geometry with merged vertices.
  */
 function mergeVertices( geometry, tolerance = 1e-4 ) {
 
@@ -753,9 +797,12 @@ function mergeVertices( geometry, tolerance = 1e-4 ) {
 }
 
 /**
- * @param {BufferGeometry} geometry
- * @param {number} drawMode
- * @return {BufferGeometry}
+ * Returns a new indexed geometry based on `TrianglesDrawMode` draw mode.
+ * This mode corresponds to the `gl.TRIANGLES` primitive in WebGL.
+ *
+ * @param {BufferGeometry} geometry - The geometry to convert.
+ * @param {number} drawMode - The current draw mode.
+ * @return {BufferGeometry} The new geometry using `TrianglesDrawMode`.
  */
 function toTrianglesDrawMode( geometry, drawMode ) {
 
@@ -864,9 +911,13 @@ function toTrianglesDrawMode( geometry, drawMode ) {
 
 /**
  * Calculates the morphed attributes of a morphed/skinned BufferGeometry.
- * Helpful for Raytracing or Decals.
- * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points.
- * @return {Object} An Object with original position/normal attributes and morphed ones.
+ *
+ * Helpful for Raytracing or Decals (i.e. a `DecalGeometry` applied to a morphed Object with a `BufferGeometry`
+ * will use the original `BufferGeometry`, not the morphed/skinned one, generating an incorrect result.
+ * Using this function to create a shadow `Object3`D the `DecalGeometry` can be correctly generated).
+ *
+ * @param {Mesh|Line|Points} object - The 3D object tocompute morph attributes for.
+ * @return {Object} An object with original position/normal attributes and morphed ones.
  */
 function computeMorphedAttributes( object ) {
 
@@ -1142,6 +1193,12 @@ function computeMorphedAttributes( object ) {
 
 }
 
+/**
+ * Merges the {@link BufferGeometry#groups} for the given geometry.
+ *
+ * @param {BufferGeometry} geometry - The geometry to modify.
+ * @return {BufferGeometry} - The updated geometry
+ */
 function mergeGroups( geometry ) {
 
 	if ( geometry.groups.length === 0 ) {
@@ -1244,15 +1301,14 @@ function mergeGroups( geometry ) {
 
 }
 
-
 /**
  * Modifies the supplied geometry if it is non-indexed, otherwise creates a new,
  * non-indexed geometry. Returns the geometry with smooth normals everywhere except
  * faces that meet at an angle greater than the crease angle.
  *
- * @param {BufferGeometry} geometry
- * @param {number} [creaseAngle]
- * @return {BufferGeometry}
+ * @param {BufferGeometry} geometry - The geometry to modify.
+ * @param {number} [creaseAngle=Math.PI/3] - The crease angle in radians.
+ * @return {BufferGeometry} - The updated geometry
  */
 function toCreasedNormals( geometry, creaseAngle = Math.PI / 3 /* 60 degrees */ ) {
 
@@ -1363,6 +1419,9 @@ export {
 	computeMikkTSpaceTangents,
 	mergeGeometries,
 	mergeAttributes,
+	deepCloneAttribute,
+	deinterleaveAttribute,
+	deinterleaveGeometry,
 	interleaveAttributes,
 	estimateBytesUsed,
 	mergeVertices,

+ 10 - 7
examples/jsm/utils/CameraUtils.js

@@ -4,6 +4,8 @@ import {
 	Vector3
 } from 'three';
 
+/** @module CameraUtils */
+
 const _va = /*@__PURE__*/ new Vector3(), // from pe to pa
 	_vb = /*@__PURE__*/ new Vector3(), // from pe to pb
 	_vc = /*@__PURE__*/ new Vector3(), // from pe to pc
@@ -14,16 +16,17 @@ const _va = /*@__PURE__*/ new Vector3(), // from pe to pa
 	_quat = /*@__PURE__*/ new Quaternion(); // temporary quaternion
 
 
-/** Set a PerspectiveCamera's projectionMatrix and quaternion
+/**
+ * Set projection matrix and the orientation of a perspective camera
  * to exactly frame the corners of an arbitrary rectangle.
  * NOTE: This function ignores the standard parameters;
- * do not call updateProjectionMatrix() after this!
+ * do not call `updateProjectionMatrix()` after this.
  *
- * @param {PerspectiveCamera} camera
- * @param {Vector3} bottomLeftCorner
- * @param {Vector3} bottomRightCorner
- * @param {Vector3} topLeftCorner
- * @param {boolean} [estimateViewFrustum=false]
+ * @param {PerspectiveCamera} camera - The camera.
+ * @param {Vector3} bottomLeftCorner - The bottom-left corner point.
+ * @param {Vector3} bottomRightCorner - The bottom-right corner point.
+ * @param {Vector3} topLeftCorner - The top-left corner point.
+ * @param {boolean} [estimateViewFrustum=false] - If set to `true`, the function tries to estimate the camera's FOV.
  */
 function frameCorners( camera, bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {
 

+ 20 - 30
examples/jsm/utils/GeometryCompressionUtils.js

@@ -1,10 +1,3 @@
-/**
- * Octahedron and Quantization encodings based on work by:
- *
- * @link https://github.com/tsherif/mesh-quantization-example
- *
- */
-
 import {
 	BufferAttribute,
 	Matrix3,
@@ -12,14 +5,15 @@ import {
 	Vector3
 } from 'three';
 
+/** @module GeometryCompressionUtils */
 
+// Octahedron and Quantization encodings based on work by: https://github.com/tsherif/mesh-quantization-example
 
 /**
- * Make the input geometry's normal attribute encoded and compressed by 3 different methods.
- *
- * @param {THREE.BufferGeometry} geometry
- * @param {string} encodeMethod		"DEFAULT" || "OCT1Byte" || "OCT2Byte" || "ANGLES"
+ * Compressed the given geometry's `normal` attribute by the selected encode method.
  *
+ * @param {BufferGeometry} geometry - The geometry whose normals should be compressed.
+ * @param {('DEFAULT'|'OCT1Byte'|'OCT2Byte'|'ANGLES')} encodeMethod - The compression method.
  */
 function compressNormals( geometry, encodeMethod ) {
 
@@ -27,7 +21,7 @@ function compressNormals( geometry, encodeMethod ) {
 
 	if ( ! normal ) {
 
-		console.error( 'Geometry must contain normal attribute. ' );
+		console.error( 'THREE.GeometryCompressionUtils.compressNormals(): Geometry must contain normal attribute.' );
 
 	}
 
@@ -35,7 +29,7 @@ function compressNormals( geometry, encodeMethod ) {
 
 	if ( normal.itemSize != 3 ) {
 
-		console.error( 'normal.itemSize is not 3, which cannot be encoded. ' );
+		console.error( 'THREE.GeometryCompressionUtils.compressNormals(): normal.itemSize is not 3, which cannot be encoded.' );
 
 	}
 
@@ -63,11 +57,10 @@ function compressNormals( geometry, encodeMethod ) {
 
 	} else if ( encodeMethod == 'OCT1Byte' ) {
 
-		/**
-		* It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
-		* As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
-		* Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
-		*/
+
+		// It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
+		// As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
+		// Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
 
 		result = new Int8Array( count * 2 );
 
@@ -127,20 +120,18 @@ function compressNormals( geometry, encodeMethod ) {
 
 }
 
-
 /**
-	 * Make the input geometry's position attribute encoded and compressed.
-	 *
-	 * @param {THREE.BufferGeometry} geometry
-	 *
-	 */
+ * Compressed the given geometry's `position` attribute.
+ *
+ * @param {BufferGeometry} geometry - The geometry whose position values should be compressed.
+ */
 function compressPositions( geometry ) {
 
 	const position = geometry.attributes.position;
 
 	if ( ! position ) {
 
-		console.error( 'Geometry must contain position attribute. ' );
+		console.error( 'THREE.GeometryCompressionUtils.compressPositions(): Geometry must contain position attribute.' );
 
 	}
 
@@ -148,7 +139,7 @@ function compressPositions( geometry ) {
 
 	if ( position.itemSize != 3 ) {
 
-		console.error( 'position.itemSize is not 3, which cannot be packed. ' );
+		console.error( 'THREE.GeometryCompressionUtils.compressPositions(): position.itemSize is not 3, which cannot be packed.' );
 
 	}
 
@@ -171,10 +162,9 @@ function compressPositions( geometry ) {
 }
 
 /**
- * Make the input geometry's uv attribute encoded and compressed.
- *
- * @param {THREE.BufferGeometry} geometry
+ * Compressed the given geometry's `uv` attribute.
  *
+ * @param {BufferGeometry} geometry - The geometry whose texture coordinates should be compressed.
  */
 function compressUvs( geometry ) {
 
@@ -182,7 +172,7 @@ function compressUvs( geometry ) {
 
 	if ( ! uvs ) {
 
-		console.error( 'Geometry must contain uv attribute. ' );
+		console.error( 'THREE.GeometryCompressionUtils.compressUvs(): Geometry must contain uv attribute.' );
 
 	}
 

+ 10 - 11
examples/jsm/utils/GeometryUtils.js

@@ -1,13 +1,13 @@
 import { Vector3 } from 'three';
 
+/** @module GeometryUtils */
 
 /**
- * Generates 2D-Coordinates in a very fast way.
+ * Generates 2D-Coordinates along a Hilbert curve.
  *
- * Based on work by:
- * @link http://www.openprocessing.org/sketch/15493
+ * Based on work by: {@link http://www.openprocessing.org/sketch/15493}
  *
- * @param {Vector3} center - Center of Hilbert curve.
+ * @param {Vector3} [center] - Center of Hilbert curve.
  * @param {number} [size=10] - Total width of Hilbert curve.
  * @param {number} [iterations=10] - Number of subdivisions.
  * @param {number} [v0=0] - Corner index -X, -Z.
@@ -52,12 +52,11 @@ function hilbert2D( center = new Vector3( 0, 0, 0 ), size = 10, iterations = 1,
 }
 
 /**
- * Generates 3D-Coordinates in a very fast way.
+ * Generates 3D-Coordinates along a Hilbert curve.
  *
- * Based on work by:
- * @link https://openprocessing.org/user/5654
+ * Based on work by: {@link https://openprocessing.org/user/5654}
  *
- * @param {Vector3} [center=new Vector3( 0, 0, 0 )] - Center of Hilbert curve.
+ * @param {Vector3} [center] - Center of Hilbert curve.
  * @param {number} [size=10] - Total width of Hilbert curve.
  * @param {number} [iterations=1] - Number of subdivisions.
  * @param {number} [v0=0] - Corner index -X, +Y, -Z.
@@ -119,12 +118,12 @@ function hilbert3D( center = new Vector3( 0, 0, 0 ), size = 10, iterations = 1,
 }
 
 /**
- * Generates a Gosper curve (lying in the XY plane)
+ * Generates a Gosper curve (lying in the XY plane).
  *
- * https://gist.github.com/nitaku/6521802
+ * Reference: {@link https://gist.github.com/nitaku/6521802}
  *
  * @param {number} [size=1] - The size of a single gosper island.
- * @return {Array<[number, number, number]>} The gosper island points.
+ * @return {Array<number>} The gosper island points.
  */
 function gosper( size = 1 ) {
 

+ 11 - 4
examples/jsm/utils/LDrawUtils.js

@@ -9,14 +9,21 @@ import {
 
 import { mergeGeometries } from './BufferGeometryUtils.js';
 
+/**
+ * Utility class for LDraw models.
+ */
 class LDrawUtils {
 
+	/**
+	 * Merges geometries in the given object by materials and returns a new group object.
+	 * Use on not indexed geometries. The object buffers reference the old object ones.
+	 * Special treatment is done to the conditional lines generated by LDrawLoader.
+	 *
+	 * @param {Object3D} object - The object to merge.
+	 * @returns {Group} The merged object.
+	 */
 	static mergeObject( object ) {
 
-		// Merges geometries in object by materials and returns new object. Use on not indexed geometries.
-		// The object buffers reference the old object ones.
-		// Special treatment is done to the conditional lines generated by LDrawLoader.
-
 		function extractGroup( geometry, group, elementSize, isConditionalLine ) {
 
 			// Extracts a group from a geometry as a new geometry (with attribute buffers referencing original buffers)

+ 65 - 19
examples/jsm/utils/SceneOptimizer.js

@@ -1,7 +1,18 @@
 import * as THREE from 'three';
 
+/**
+ * This class can be used to optimized scenes by converting
+ * individual meshes into {@link BatchedMesh}. This component
+ * is an experimental attempt to implment auto-batching in three.js.
+ */
 class SceneOptimizer {
 
+	/**
+	 * Constructs a new scene optimizer.
+	 *
+	 * @param {Scene} scene - The scene to optimize.
+	 * @param {SceneOptimizer~Options} options - The configuration options.
+	 */
 	constructor( scene, options = {} ) {
 
 		this.scene = scene;
@@ -9,7 +20,7 @@ class SceneOptimizer {
 
 	}
 
-	bufferToHash( buffer ) {
+	_bufferToHash( buffer ) {
 
 		let hash = 0;
 		if ( buffer.byteLength !== 0 ) {
@@ -43,7 +54,7 @@ class SceneOptimizer {
 
 	}
 
-	getMaterialPropertiesHash( material ) {
+	_getMaterialPropertiesHash( material ) {
 
 		const mapProps = [
 			'map',
@@ -128,7 +139,7 @@ class SceneOptimizer {
 
 	}
 
-	getAttributesSignature( geometry ) {
+	_getAttributesSignature( geometry ) {
 
 		return Object.keys( geometry.attributes )
 			.sort()
@@ -142,24 +153,24 @@ class SceneOptimizer {
 
 	}
 
-	getGeometryHash( geometry ) {
+	_getGeometryHash( geometry ) {
 
 		const indexHash = geometry.index
-			? this.bufferToHash( geometry.index.array )
+			? this._bufferToHash( geometry.index.array )
 			: 'noIndex';
-		const positionHash = this.bufferToHash( geometry.attributes.position.array );
-		const attributesSignature = this.getAttributesSignature( geometry );
+		const positionHash = this._bufferToHash( geometry.attributes.position.array );
+		const attributesSignature = this._getAttributesSignature( geometry );
 		return `${indexHash}_${positionHash}_${attributesSignature}`;
 
 	}
 
-	getBatchKey( materialProps, attributesSignature ) {
+	_getBatchKey( materialProps, attributesSignature ) {
 
 		return `${materialProps}_${attributesSignature}`;
 
 	}
 
-	analyzeModel() {
+	_analyzeModel() {
 
 		const batchGroups = new Map();
 		const singleGroups = new Map();
@@ -170,10 +181,10 @@ class SceneOptimizer {
 
 			if ( ! node.isMesh ) return;
 
-			const materialProps = this.getMaterialPropertiesHash( node.material );
-			const attributesSignature = this.getAttributesSignature( node.geometry );
-			const batchKey = this.getBatchKey( materialProps, attributesSignature );
-			const geometryHash = this.getGeometryHash( node.geometry );
+			const materialProps = this._getMaterialPropertiesHash( node.material );
+			const attributesSignature = this._getAttributesSignature( node.geometry );
+			const batchKey = this._getBatchKey( materialProps, attributesSignature );
+			const geometryHash = this._getGeometryHash( node.geometry );
 			uniqueGeometries.add( geometryHash );
 
 			if ( ! batchGroups.has( batchKey ) ) {
@@ -222,7 +233,7 @@ class SceneOptimizer {
 
 	}
 
-	createBatchedMeshes( batchGroups ) {
+	_createBatchedMeshes( batchGroups ) {
 
 		const meshesToRemove = new Set();
 
@@ -269,7 +280,7 @@ class SceneOptimizer {
 
 			for ( const mesh of group.meshes ) {
 
-				const geometryHash = this.getGeometryHash( mesh.geometry );
+				const geometryHash = this._getGeometryHash( mesh.geometry );
 
 				if ( ! geometryIds.has( geometryHash ) ) {
 
@@ -308,6 +319,11 @@ class SceneOptimizer {
 
 	}
 
+	/**
+	 * Removes empty nodes from all descendants of the given 3D object.
+	 *
+	 * @param {Object3D} object - The 3D object to process.
+	 */
 	removeEmptyNodes( object ) {
 
 		const children = [ ...object.children ];
@@ -327,6 +343,11 @@ class SceneOptimizer {
 
 	}
 
+	/**
+	 * Removes the given array of meshes from the scene.
+	 *
+	 * @param {Array<Mesh>} meshesToRemove - The meshes to remove.
+	 */
 	disposeMeshes( meshesToRemove ) {
 
 		meshesToRemove.forEach( ( mesh ) => {
@@ -356,7 +377,7 @@ class SceneOptimizer {
 
 	}
 
-	logDebugInfo( stats ) {
+	_logDebugInfo( stats ) {
 
 		console.group( 'Scene Optimization Results' );
 		console.log( `Original meshes: ${stats.originalMeshes}` );
@@ -368,10 +389,18 @@ class SceneOptimizer {
 
 	}
 
+	/**
+	 * Performs the auto-baching by identifying groups of meshes in the scene
+	 * that can be represented as a single {@link BatchedMesh}. The method modifies
+	 * the scene by adding instances of `BatchedMesh` and removing the now redundant
+	 * individual meshes.
+	 *
+	 * @return {Scene} The optimized scene.
+	 */
 	toBatchedMesh() {
 
-		const { batchGroups, singleGroups, uniqueGeometries } = this.analyzeModel();
-		const meshesToRemove = this.createBatchedMeshes( batchGroups );
+		const { batchGroups, singleGroups, uniqueGeometries } = this._analyzeModel();
+		const meshesToRemove = this._createBatchedMeshes( batchGroups );
 
 		this.disposeMeshes( meshesToRemove );
 		this.removeEmptyNodes( this.scene );
@@ -398,7 +427,17 @@ class SceneOptimizer {
 
 	}
 
-	// Placeholder for future implementation
+	/**
+	 * Performs the auto-instancing by identifying groups of meshes in the scene
+	 * that can be represented as a single {@link InstancedMesh}. The method modifies
+	 * the scene by adding instances of `InstancedMesh` and removing the now redundant
+	 * individual meshes.
+	 *
+	 * This method is not yet implemented.
+	 *
+	 * @abstract
+	 * @return {Scene} The optimized scene.
+	 */
 	toInstancingMesh() {
 
 		throw new Error( 'InstancedMesh optimization not implemented yet' );
@@ -407,4 +446,11 @@ class SceneOptimizer {
 
 }
 
+/**
+ * Constructor options of `SceneOptimizer`.
+ *
+ * @typedef {Object} SceneOptimizer~Options
+ * @property {boolean} [debug=false] - Whether to enable debug mode or not.
+ **/
+
 export { SceneOptimizer };

+ 50 - 3
examples/jsm/utils/SceneUtils.js

@@ -10,9 +10,19 @@ import {
 
 import { mergeGroups, deepCloneAttribute } from './BufferGeometryUtils.js';
 
+/** @module SceneUtils */
+
 const _color = /*@__PURE__*/new Color();
 const _matrix = /*@__PURE__*/new Matrix4();
 
+/**
+ * This function creates a mesh for each instance of the given instanced mesh and
+ * adds it to a group. Each mesh will honor the current 3D transformation of its
+ * corresponding instance.
+ *
+ * @param {InstancedMesh} instancedMesh - The instanced mesh.
+ * @return {Group} A group of meshes.
+ */
 function createMeshesFromInstancedMesh( instancedMesh ) {
 
 	const group = new Group();
@@ -39,6 +49,13 @@ function createMeshesFromInstancedMesh( instancedMesh ) {
 
 }
 
+/**
+ * This function creates a mesh for each geoemtry-group of the given multi-material mesh and
+ * adds it to a group.
+ *
+ * @param {Mesh} mesh - The multi-material mesh.
+ * @return {Group} A group of meshes.
+ */
 function createMeshesFromMultiMaterialMesh( mesh ) {
 
 	if ( Array.isArray( mesh.material ) === false ) {
@@ -110,6 +127,16 @@ function createMeshesFromMultiMaterialMesh( mesh ) {
 
 }
 
+/**
+ * This function represents an alternative way to create 3D objects with multiple materials.
+ * Normally, {@link BufferGeometry#groups} are used which might introdues issues e.g. when
+ * exporting the object to a 3D format. This function accepts a geometry and an array of
+ * materials and creates for each material a mesh that is added to a group.
+ *
+ * @param {BufferGeometry} geometry - The geometry.
+ * @param {Array<Material>} materials - An array of materials.
+ * @return {Group} A group representing a multi-material object.
+ */
 function createMultiMaterialObject( geometry, materials ) {
 
 	const group = new Group();
@@ -124,6 +151,18 @@ function createMultiMaterialObject( geometry, materials ) {
 
 }
 
+
+/**
+ * Executes a reducer function for each vertex of the given 3D object.
+ * `reduceVertices()` returns a single value: the function's accumulated result.
+ *
+ * @param {Object3D} object - The 3D object that should be processed. It must have a
+ * geometry with a `position` attribute.
+ * @param {function(number,Vector3):number} func - The reducer function. First argument
+ * is the current value, second argument the current vertex.
+ * @param {any} initialValue - The initial value.
+ * @return {any} The result.
+ */
 function reduceVertices( object, func, initialValue ) {
 
 	let value = initialValue;
@@ -174,8 +213,10 @@ function reduceVertices( object, func, initialValue ) {
 }
 
 /**
- * @param {InstancedMesh} mesh
- * @param {function(int, int):int} compareFn
+ * Sorts the instances of the given instanced mesh.
+ *
+ * @param {InstancedMesh} mesh - The instanced mesh to sort.
+ * @param {function(number, number):number} compareFn - A custom compare function for the sort.
  */
 function sortInstancedMesh( mesh, compareFn ) {
 
@@ -246,7 +287,9 @@ function sortInstancedMesh( mesh, compareFn ) {
 }
 
 /**
- * @param {Object3D} object Object to traverse.
+ * Generator based alternative to {@link Object3D#traverse}.
+ *
+ * @param {Object3D} object - Object to traverse.
  * @yields {Object3D} Objects that passed the filter condition.
  */
 function* traverseGenerator( object ) {
@@ -264,6 +307,8 @@ function* traverseGenerator( object ) {
 }
 
 /**
+ * Generator based alternative to {@link Object3D#traverseVisible}.
+ *
  * @param {Object3D} object Object to traverse.
  * @yields {Object3D} Objects that passed the filter condition.
  */
@@ -284,6 +329,8 @@ function* traverseVisibleGenerator( object ) {
 }
 
 /**
+ * Generator based alternative to {@link Object3D#traverseAncestors}.
+ *
  * @param {Object3D} object Object to traverse.
  * @yields {Object3D} Objects that passed the filter condition.
  */

+ 47 - 24
examples/jsm/utils/ShadowMapViewer.js

@@ -16,30 +16,25 @@ import { UnpackDepthRGBAShader } from '../shaders/UnpackDepthRGBAShader.js';
  * It works for shadow casting lights: DirectionalLight and SpotLight.
  * It renders out the shadow map and displays it on a HUD.
  *
- * Example usage:
- *	1) Import ShadowMapViewer into your app.
+ * This module can only be used with {@link WebGLRenderer}. When using {@link WebGPURenderer},
+ * import the class from `ShadowMapViewerGPU.js`.
  *
- *	2) Create a shadow casting light and name it optionally:
- *		let light = new DirectionalLight( 0xffffff, 1 );
- *		light.castShadow = true;
- *		light.name = 'Sun';
- *
- *	3) Create a shadow map viewer for that light and set its size and position optionally:
- *		let shadowMapViewer = new ShadowMapViewer( light );
- *		shadowMapViewer.size.set( 128, 128 );	//width, height  default: 256, 256
- *		shadowMapViewer.position.set( 10, 10 );	//x, y in pixel	 default: 0, 0 (top left corner)
- *
- *	4) Render the shadow map viewer in your render loop:
- *		shadowMapViewer.render( renderer );
- *
- *	5) Optionally: Update the shadow map viewer on window resize:
- *		shadowMapViewer.updateForWindowResize();
- *
- *	6) If you set the position or size members directly, you need to call shadowMapViewer.update();
+ * ```js
+ * const lightShadowMapViewer = new ShadowMapViewer( light );
+ * lightShadowMapViewer.position.x = 10;
+ * lightShadowMapViewer.position.y = SCREEN_HEIGHT - ( SHADOW_MAP_HEIGHT / 4 ) - 10;
+ * lightShadowMapViewer.size.width = SHADOW_MAP_WIDTH / 4;
+ * lightShadowMapViewer.size.height = SHADOW_MAP_HEIGHT / 4;
+ * lightShadowMapViewer.update();
+ * ```
  */
-
 class ShadowMapViewer {
 
+	/**
+	 * Constructs a new shadow map viewer.
+	 *
+	 * @param {Light} light - The shadow casting light.
+	 */
 	constructor( light ) {
 
 		//- Internals
@@ -110,11 +105,21 @@ class ShadowMapViewer {
 
 		}
 
-		//- API
-		// Set to false to disable displaying this shadow map
+		/**
+		 * Whether to display the shadow map viewer or not.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
 		this.enabled = true;
 
-		// Set the size of the displayed shadow map on the HUD
+		/**
+		 * The size of the viewer. When changing this property, make sure
+		 * to call {@link ShadowMapViewer#update}.
+		 *
+		 * @type {{width:number,height:number}}
+		 * @default true
+		 */
 		this.size = {
 			width: frame.width,
 			height: frame.height,
@@ -131,7 +136,13 @@ class ShadowMapViewer {
 			}
 		};
 
-		// Set the position of the displayed shadow map on the HUD
+		/**
+		 * The position of the viewer. When changing this property, make sure
+		 * to call {@link ShadowMapViewer#update}.
+		 *
+		 * @type {{width:number,height:number}}
+		 * @default true
+		 */
 		this.position = {
 			x: frame.x,
 			y: frame.y,
@@ -150,6 +161,11 @@ class ShadowMapViewer {
 			}
 		};
 
+		/**
+		 * Renders the viewer. This method must be called in the app's animation loop.
+		 *
+		 * @param {WebGLRenderer} renderer - The renderer.
+		 */
 		this.render = function ( renderer ) {
 
 			if ( this.enabled ) {
@@ -171,6 +187,10 @@ class ShadowMapViewer {
 
 		};
 
+		/**
+		 * Resizes the viewer. This method should be called whenever the app's
+		 * window is resized.
+		 */
 		this.updateForWindowResize = function () {
 
 			if ( this.enabled ) {
@@ -187,6 +207,9 @@ class ShadowMapViewer {
 
 		};
 
+		/**
+		 * Updates the viewer.
+		 */
 		this.update = function () {
 
 			this.position.set( this.position.x, this.position.y );

+ 47 - 24
examples/jsm/utils/ShadowMapViewerGPU.js

@@ -16,30 +16,25 @@ import { texture } from 'three/tsl';
  * It works for shadow casting lights: DirectionalLight and SpotLight.
  * It renders out the shadow map and displays it on a HUD.
  *
- * Example usage:
- *	1) Import ShadowMapViewer into your app.
+ * This module can only be used with {@link WebGPURenderer}. When using {@link WebGLRenderer},
+ * import the class from `ShadowMapViewer.js`.
  *
- *	2) Create a shadow casting light and name it optionally:
- *		let light = new DirectionalLight( 0xffffff, 1 );
- *		light.castShadow = true;
- *		light.name = 'Sun';
- *
- *	3) Create a shadow map viewer for that light and set its size and position optionally:
- *		let shadowMapViewer = new ShadowMapViewer( light );
- *		shadowMapViewer.size.set( 128, 128 );	//width, height  default: 256, 256
- *		shadowMapViewer.position.set( 10, 10 );	//x, y in pixel	 default: 0, 0 (top left corner)
- *
- *	4) Render the shadow map viewer in your render loop:
- *		shadowMapViewer.render( renderer );
- *
- *	5) Optionally: Update the shadow map viewer on window resize:
- *		shadowMapViewer.updateForWindowResize();
- *
- *	6) If you set the position or size members directly, you need to call shadowMapViewer.update();
+ * ```js
+ * const lightShadowMapViewer = new ShadowMapViewer( light );
+ * lightShadowMapViewer.position.x = 10;
+ * lightShadowMapViewer.position.y = SCREEN_HEIGHT - ( SHADOW_MAP_HEIGHT / 4 ) - 10;
+ * lightShadowMapViewer.size.width = SHADOW_MAP_WIDTH / 4;
+ * lightShadowMapViewer.size.height = SHADOW_MAP_HEIGHT / 4;
+ * lightShadowMapViewer.update();
+ * ```
  */
-
 class ShadowMapViewer {
 
+	/**
+	 * Constructs a new shadow map viewer.
+	 *
+	 * @param {Light} light - The shadow casting light.
+	 */
 	constructor( light ) {
 
 		//- Internals
@@ -106,11 +101,21 @@ class ShadowMapViewer {
 
 		}
 
-		//- API
-		// Set to false to disable displaying this shadow map
+		/**
+		 * Whether to display the shadow map viewer or not.
+		 *
+		 * @type {boolean}
+		 * @default true
+		 */
 		this.enabled = true;
 
-		// Set the size of the displayed shadow map on the HUD
+		/**
+		 * The size of the viewer. When changing this property, make sure
+		 * to call {@link ShadowMapViewer#update}.
+		 *
+		 * @type {{width:number,height:number}}
+		 * @default true
+		 */
 		this.size = {
 			width: frame.width,
 			height: frame.height,
@@ -127,7 +132,13 @@ class ShadowMapViewer {
 			}
 		};
 
-		// Set the position of the displayed shadow map on the HUD
+		/**
+		 * The position of the viewer. When changing this property, make sure
+		 * to call {@link ShadowMapViewer#update}.
+		 *
+		 * @type {{width:number,height:number}}
+		 * @default true
+		 */
 		this.position = {
 			x: frame.x,
 			y: frame.y,
@@ -146,6 +157,11 @@ class ShadowMapViewer {
 			}
 		};
 
+		/**
+		 * Renders the viewer. This method must be called in the app's animation loop.
+		 *
+		 * @param {WebGPURenderer} renderer - The renderer.
+		 */
 		this.render = function ( renderer ) {
 
 			if ( this.enabled ) {
@@ -167,6 +183,10 @@ class ShadowMapViewer {
 
 		};
 
+		/**
+		 * Resizes the viewer. This method should be called whenever the app's
+		 * window is resized.
+		 */
 		this.updateForWindowResize = function () {
 
 			if ( this.enabled ) {
@@ -183,6 +203,9 @@ class ShadowMapViewer {
 
 		};
 
+		/**
+		 * Updates the viewer.
+		 */
 		this.update = function () {
 
 			this.position.set( this.position.x, this.position.y );

+ 45 - 0
examples/jsm/utils/SkeletonUtils.js

@@ -9,6 +9,8 @@ import {
 	VectorKeyframeTrack
 } from 'three';
 
+/** @module SkeletonUtils */
+
 function getBoneName( bone, options ) {
 
 	if ( options.getBoneName !== undefined ) {
@@ -21,6 +23,14 @@ function getBoneName( bone, options ) {
 
 }
 
+/**
+ * Retargets the skeleton from the given source 3D object to the
+ * target 3D object.
+ *
+ * @param {Object3D} target - The target 3D object.
+ * @param {Object3D} source - The source 3D object.
+ * @param {module:SkeletonUtils~RetargetOptions} options - The options.
+ */
 function retarget( target, source, options = {} ) {
 
 	const quat = new Quaternion(),
@@ -196,6 +206,16 @@ function retarget( target, source, options = {} ) {
 
 }
 
+/**
+ * Retargets the animation clip of the source object to the
+ * target 3D object.
+ *
+ * @param {Object3D} target - The target 3D object.
+ * @param {Object3D} source - The source 3D object.
+ * @param {AnimationClip} clip - The animation clip.
+ * @param {module:SkeletonUtils~RetargetOptions} options - The options.
+ * @return {AnimationClip} The retargeted animation clip.
+ */
 function retargetClip( target, source, clip, options = {} ) {
 
 	options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
@@ -354,6 +374,14 @@ function retargetClip( target, source, clip, options = {} ) {
 
 }
 
+/**
+ * Clones the given 3D object and its descendants, ensuring that any `SkinnedMesh` instances are
+ * correctly associated with their bones. Bones are also cloned, and must be descendants of the
+ * object passed to this method. Other data, like geometries and materials, are reused by reference.
+ *
+ * @param {Object3D} source - The 3D object to clone.
+ * @return {Object3D} The cloned 3D object.
+ */
 function clone( source ) {
 
 	const sourceLookup = new Map();
@@ -435,6 +463,23 @@ function parallelTraverse( a, b, callback ) {
 
 }
 
+/**
+ * Retarget options of `SkeletonUtils`.
+ *
+ * @typedef {Object} module:SkeletonUtils~RetargetOptions
+ * @property {boolean} [useFirstFramePosition=false] - Whether to use the position of the first frame or not.
+ * @property {number} [fps] - The FPS of the clip.
+ * @property {Object<string,string>} [names] - A dicionary for mapping target to source bone names.
+ * @property {function(string):string} [getBoneName] - A function for mapping bone names. Alternative to `names`.
+ * @property {Array<number>} [trim] - Whether to trim the clip or not. If set the array should hold two values for the start and end.
+ * @property {boolean} [preserveBoneMatrix=true] - Whether to preserve bone matrices or not.
+ * @property {boolean} [preserveBonePositions=true] - Whether to preserve bone positions or not.
+ * @property {boolean} [useTargetMatrix=false] - Whether to use the target matrix or not.
+ * @property {string} [hip='hip'] - The name of the source's hip bone.
+ * @property {Vector3} [hipInfluence=(1,1,1)] - The hip influence.
+ * @property {number} [scale=1] - The scale.
+ **/
+
 export {
 	retarget,
 	retargetClip,

+ 14 - 5
examples/jsm/utils/SortUtils.js

@@ -1,8 +1,5 @@
-// Hybrid radix sort from
-// - https://gist.github.com/sciecode/93ed864dd77c5c8803c6a86698d68dab
-// - https://github.com/mrdoob/three.js/pull/27202#issuecomment-1817640271
-//
-// expects unsigned 32b integer values
+
+/** @module SortUtils */
 
 const POWER = 3;
 const BIT_MAX = 32;
@@ -24,6 +21,18 @@ for ( let i = 0; i < ( ITERATIONS + 1 ); i ++ ) {
 
 const defaultGet = ( el ) => el;
 
+/**
+ * Hybrid radix sort from.
+ *
+ * - {@link https://gist.github.com/sciecode/93ed864dd77c5c8803c6a86698d68dab}
+ * - {@link https://github.com/mrdoob/three.js/pull/27202#issuecomment-1817640271}
+ *
+ * Expects unsigned 32b integer values.
+ *
+ * @function
+ * @param {Array<Object>} arr - The array to sort.
+ * @param {Object} opt - The options
+ */
 export const radixSort = ( arr, opt ) => {
 
 	const len = arr.length;

+ 9 - 4
examples/jsm/utils/UVsDebug.js

@@ -2,14 +2,19 @@ import {
 	Vector2
 } from 'three';
 
+/** @module UVsDebug */
+
 /**
- * tool for "unwrapping" and debugging three.js geometries UV mapping
+ * Function for "unwrapping" and debugging three.js geometries UV mapping.
  *
- * Sample usage:
- *	document.body.appendChild( UVsDebug( new THREE.SphereGeometry( 10, 10, 10, 10 ) ) );
+ * ```js
+ * document.body.appendChild( UVsDebug( new THREE.SphereGeometry() ) );
+ * ```
  *
+ * @param {BufferGeometry} geometry - The geometry whose uv coordinates should be inspected.
+ * @param {number} [size=1024] - The size of the debug canvas.
+ * @return {HTMLCanvasElement} A canvas element with visualized uv coordinates.
  */
-
 function UVsDebug( geometry, size = 1024 ) {
 
 	// handles wrapping of uv.x > 1 only

+ 13 - 0
examples/jsm/utils/WebGLTextureUtils.js

@@ -10,11 +10,24 @@ import {
 	SRGBColorSpace
 } from 'three';
 
+/** @module WebGLTextureUtils */
+
 let _renderer;
 let fullscreenQuadGeometry;
 let fullscreenQuadMaterial;
 let fullscreenQuad;
 
+/**
+ * Returns an uncompressed version of the given compressed texture.
+ *
+ * This module can only be used with {@link WebGLRenderer}. When using {@link WebGPURenderer},
+ * import the function from {@link WebGPUTextureUtils}.
+ *
+ * @param {CompressedTexture} texture - The compressed texture.
+ * @param {number} [maxTextureSize=Infinity] - The maximum size of the uncompressed texture.
+ * @param {?WebGLRenderer} [renderer=null] - A reference to a renderer.
+ * @return {CanvasTexture} The uncompressed texture.
+ */
 export function decompress( texture, maxTextureSize = Infinity, renderer = null ) {
 
 	if ( ! fullscreenQuadGeometry ) fullscreenQuadGeometry = new PlaneGeometry( 2, 2, 1, 1 );

+ 14 - 0
examples/jsm/utils/WebGPUTextureUtils.js

@@ -6,9 +6,23 @@ import {
 } from 'three';
 import { texture, uv } from 'three/tsl';
 
+/** @module WebGPUTextureUtils */
+
 let _renderer;
 const _quadMesh = /*@__PURE__*/ new QuadMesh();
 
+/**
+ * Returns an uncompressed version of the given compressed texture.
+ *
+ * This module can only be used with {@link WebGPURenderer}. When using {@link WebGLRenderer},
+ * import the function from {@link WebGLTextureUtils}.
+ *
+ * @async
+ * @param {CompressedTexture} blitTexture - The compressed texture.
+ * @param {number} [maxTextureSize=Infinity] - The maximum size of the uncompressed texture.
+ * @param {?WebGPURenderer} [renderer=null] - A reference to a renderer.
+ * @return {Promise<CanvasTexture>} A Promise that resolved with the uncompressed texture.
+ */
 export async function decompress( blitTexture, maxTextureSize = Infinity, renderer = null ) {
 
 	if ( renderer === null ) {

+ 65 - 2
examples/jsm/utils/WorkerPool.js

@@ -1,17 +1,58 @@
 /**
- * @author Deepkolos / https://github.com/deepkolos
+ * A simple pool for managing Web Workers.
  */
-
 export class WorkerPool {
 
+	/**
+	 * Constructs a new Worker pool.
+	 *
+	 * @param {number} [pool=4] - The size of the pool.
+	 */
 	constructor( pool = 4 ) {
 
+		/**
+		 * The size of the pool.
+		 *
+		 * @type {number}
+		 * @default 4
+		 */
 		this.pool = pool;
+
+		/**
+		 * A message queue.
+		 *
+		 * @type {Array<Object>}
+		 */
 		this.queue = [];
+
+		/**
+		 * An array of Workers.
+		 *
+		 * @type {Array<Worker>}
+		 */
 		this.workers = [];
+
+		/**
+		 * An array with resolve functions for messages.
+		 *
+		 * @type {Array<Function>}
+		 */
 		this.workersResolve = [];
+
+		/**
+		 * The current worker status.
+		 *
+		 * @type {number}
+		 */
 		this.workerStatus = 0;
 
+		/**
+		 * A factory function for creating workers.
+		 *
+		 * @type {?Function}
+		 */
+		this.workerCreator = null;
+
 	}
 
 	_initWorker( workerId ) {
@@ -54,18 +95,36 @@ export class WorkerPool {
 
 	}
 
+	/**
+	 * Sets a function that is responsible for creating Workers.
+	 *
+	 * @param {Function} workerCreator - The worker creator function.
+	 */
 	setWorkerCreator( workerCreator ) {
 
 		this.workerCreator = workerCreator;
 
 	}
 
+	/**
+	 * Sets the Worker limit
+	 *
+	 * @param {number} pool - The size of the pool.
+	 */
 	setWorkerLimit( pool ) {
 
 		this.pool = pool;
 
 	}
 
+	/**
+	 * Post a message to an idle Worker. If no Worker is available,
+	 * the message is pushed into a message queue for later processing.
+	 *
+	 * @param {Object} msg - The message.
+	 * @param {Array<ArrayBuffer>} transfer - An array with array buffers for data transfer.
+	 * @return {Promise} A Promise that resolves when the message has been processed.
+	 */
 	postMessage( msg, transfer ) {
 
 		return new Promise( ( resolve ) => {
@@ -89,6 +148,10 @@ export class WorkerPool {
 
 	}
 
+	/**
+	 * Terminates all Workers of this pool. Call this  method whenever this
+	 * Worker pool is no longer used in your app.
+	 */
 	dispose() {
 
 		this.workers.forEach( ( worker ) => worker.terminate() );

+ 3 - 2
utils/docs/jsdoc.config.json

@@ -33,7 +33,7 @@
             "examples/jsm/renderers",
             "examples/jsm/textures",
             "examples/jsm/tsl",
-            "examples/jsm/webxr",
+            "examples/jsm/utils",
             "src"
         ],
         "exclude": [
@@ -44,7 +44,8 @@
             "examples/jsm/materials/LDrawConditionalLineNodeMaterial.js",
             "examples/jsm/modifiers/CurveModifierGPU.js",
             "examples/jsm/objects/Water2.js",
-            "examples/jsm/objects/Water2Mesh.js"
+            "examples/jsm/objects/Water2Mesh.js",
+            "examples/jsm/utils/ShadowMapViewerGPU.js"
         ]
     }
 }

+ 2 - 0
utils/docs/template/publish.js

@@ -399,6 +399,8 @@ function buildMainNav( items, itemsSeen, linktoFn ) {
 
 				let navItems = '';
 
+				links.sort();
+
 				for ( const link of links ) {
 
 					navItems += link;

粤ICP备19079148号