Mr.doob 1 год назад
Родитель
Сommit
84e969fe96
100 измененных файлов с 2271 добавлено и 997 удалено
  1. 426 380
      build/three.cjs
  2. 412 40
      build/three.core.js
  3. 1 1
      build/three.core.min.js
  4. 1 1
      build/three.module.js
  5. 1 1
      build/three.module.min.js
  6. 10 2
      build/three.tsl.js
  7. 1 1
      build/three.tsl.min.js
  8. 1 1
      build/three.webgpu.js
  9. 1 1
      build/three.webgpu.min.js
  10. 1 1
      build/three.webgpu.nodes.js
  11. 1 1
      build/three.webgpu.nodes.min.js
  12. 0 1
      docs/api/ar/materials/RawShaderMaterial.html
  13. 3 0
      docs/api/en/core/BufferAttribute.html
  14. 0 1
      docs/api/en/materials/RawShaderMaterial.html
  15. 2 2
      docs/api/en/math/Matrix4.html
  16. 3 0
      docs/api/en/objects/BatchedMesh.html
  17. 3 0
      docs/api/en/objects/InstancedMesh.html
  18. 1 1
      docs/api/en/scenes/Scene.html
  19. 1 4
      docs/api/en/textures/Texture.html
  20. 101 0
      docs/api/en/textures/VideoFrameTexture.html
  21. 0 1
      docs/api/fr/materials/RawShaderMaterial.html
  22. 0 1
      docs/api/it/materials/RawShaderMaterial.html
  23. 0 1
      docs/api/zh/materials/RawShaderMaterial.html
  24. 1 1
      docs/examples/en/animations/CCDIKSolver.html
  25. 5 4
      docs/examples/en/geometries/ConvexGeometry.html
  26. 1 1
      docs/examples/en/lines/LineMaterial.html
  27. 1 1
      docs/examples/zh/animations/CCDIKSolver.html
  28. 1 0
      docs/list.json
  29. 2 6
      docs/manual/en/introduction/Color-management.html
  30. 5 6
      editor/js/Command.js
  31. 4 4
      editor/js/Strings.js
  32. 7 3
      examples/files.json
  33. 6 5
      examples/jsm/animation/CCDIKSolver.js
  34. 1 1
      examples/jsm/capabilities/WebGPU.js
  35. 23 21
      examples/jsm/controls/ArcballControls.js
  36. 26 25
      examples/jsm/exporters/GLTFExporter.js
  37. 0 176
      examples/jsm/geometries/InstancedPointsGeometry.js
  38. 1 12
      examples/jsm/geometries/TextGeometry.js
  39. 108 51
      examples/jsm/interactive/InteractiveGroup.js
  40. 109 0
      examples/jsm/libs/demuxer_mp4.js
  41. 3 1
      examples/jsm/loaders/GCodeLoader.js
  42. 0 2
      examples/jsm/loaders/GLTFLoader.js
  43. 0 1
      examples/jsm/loaders/TDSLoader.js
  44. 2 0
      examples/jsm/materials/MeshGouraudMaterial.js
  45. 6 7
      examples/jsm/misc/GPUComputationRenderer.js
  46. 4 3
      examples/jsm/misc/ProgressiveLightMap.js
  47. 4 3
      examples/jsm/misc/ProgressiveLightMapGPU.js
  48. 7 5
      examples/jsm/misc/Volume.js
  49. 5 3
      examples/jsm/misc/VolumeSlice.js
  50. 0 19
      examples/jsm/objects/InstancedPoints.js
  51. 25 20
      examples/jsm/objects/WaterMesh.js
  52. 7 7
      examples/jsm/physics/JoltPhysics.js
  53. 4 4
      examples/jsm/physics/RapierPhysics.js
  54. 2 0
      examples/jsm/postprocessing/OutputPass.js
  55. 5 1
      examples/jsm/shaders/OutputShader.js
  56. 2 2
      examples/jsm/transpiler/GLSLDecoder.js
  57. 2 0
      examples/jsm/transpiler/TSLEncoder.js
  58. 112 0
      examples/misc_raycaster_helper.html
  59. BIN
      examples/screenshots/misc_raycaster_helper.jpg
  60. BIN
      examples/screenshots/webgl_geometry_teapot.jpg
  61. BIN
      examples/screenshots/webgpu_camera.jpg
  62. BIN
      examples/screenshots/webgpu_camera_array.jpg
  63. BIN
      examples/screenshots/webgpu_clearcoat.jpg
  64. BIN
      examples/screenshots/webgpu_clipping.jpg
  65. BIN
      examples/screenshots/webgpu_instance_points.jpg
  66. BIN
      examples/screenshots/webgpu_lights_custom.jpg
  67. BIN
      examples/screenshots/webgpu_lights_ies_spotlight.jpg
  68. BIN
      examples/screenshots/webgpu_postprocessing_3dlut.jpg
  69. BIN
      examples/screenshots/webgpu_sprites.jpg
  70. BIN
      examples/screenshots/webgpu_struct_drawindirect.jpg
  71. BIN
      examples/screenshots/webgpu_tsl_coffee_smoke.jpg
  72. BIN
      examples/screenshots/webgpu_tsl_compute_attractors_particles.jpg
  73. BIN
      examples/screenshots/webgpu_tsl_vfx_tornado.jpg
  74. BIN
      examples/screenshots/webgpu_video_frame.jpg
  75. BIN
      examples/screenshots/webgpu_xr_cubes.jpg
  76. BIN
      examples/screenshots/webxr_vr_layers.jpg
  77. 4 1
      examples/tags.json
  78. 3 3
      examples/webgl_geometry_teapot.html
  79. 2 2
      examples/webgpu_animation_retargeting.html
  80. 1 1
      examples/webgpu_animation_retargeting_readyplayer.html
  81. 154 0
      examples/webgpu_camera_array.html
  82. 41 24
      examples/webgpu_compute_birds.html
  83. 3 0
      examples/webgpu_compute_particles.html
  84. 10 6
      examples/webgpu_compute_particles_snow.html
  85. 29 2
      examples/webgpu_compute_points.html
  86. 14 0
      examples/webgpu_compute_sort_bitonic.html
  87. 18 13
      examples/webgpu_compute_water.html
  88. 39 35
      examples/webgpu_instance_points.html
  89. 64 7
      examples/webgpu_lights_ies_spotlight.html
  90. 9 0
      examples/webgpu_materials.html
  91. 1 1
      examples/webgpu_multisampled_renderbuffers.html
  92. 1 6
      examples/webgpu_occlusion.html
  93. 9 0
      examples/webgpu_particles.html
  94. 15 6
      examples/webgpu_performance.html
  95. 7 4
      examples/webgpu_performance_renderbundle.html
  96. 111 45
      examples/webgpu_postprocessing_3dlut.html
  97. 1 1
      examples/webgpu_postprocessing_traa.html
  98. 5 2
      examples/webgpu_storage_buffer.html
  99. 268 0
      examples/webgpu_struct_drawindirect.html
  100. 1 1
      examples/webgpu_texturegrad.html

+ 426 - 380
build/three.cjs

@@ -1,11 +1,11 @@
 /**
  * @license
- * Copyright 2010-2024 Three.js Authors
+ * Copyright 2010-2025 Three.js Authors
  * SPDX-License-Identifier: MIT
  */
 'use strict';
 
-const REVISION = '172';
+const REVISION = '173';
 
 const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
 const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
@@ -217,6 +217,11 @@ const GLSL3 = '300 es';
 const WebGLCoordinateSystem = 2000;
 const WebGPUCoordinateSystem = 2001;
 
+const TimestampQuery = {
+	COMPUTE: 'compute',
+	RENDER: 'render'
+};
+
 /**
  * https://github.com/mrdoob/eventdispatcher.js/
  */
@@ -245,19 +250,20 @@ class EventDispatcher {
 
 	hasEventListener( type, listener ) {
 
-		if ( this._listeners === undefined ) return false;
-
 		const listeners = this._listeners;
 
+		if ( listeners === undefined ) return false;
+
 		return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;
 
 	}
 
 	removeEventListener( type, listener ) {
 
-		if ( this._listeners === undefined ) return;
-
 		const listeners = this._listeners;
+
+		if ( listeners === undefined ) return;
+
 		const listenerArray = listeners[ type ];
 
 		if ( listenerArray !== undefined ) {
@@ -276,9 +282,10 @@ class EventDispatcher {
 
 	dispatchEvent( event ) {
 
-		if ( this._listeners === undefined ) return;
-
 		const listeners = this._listeners;
+
+		if ( listeners === undefined ) return;
+
 		const listenerArray = listeners[ event.type ];
 
 		if ( listenerArray !== undefined ) {
@@ -1856,17 +1863,7 @@ class ImageUtils {
 
 		}
 
-		if ( canvas.width > 2048 || canvas.height > 2048 ) {
-
-			console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image );
-
-			return canvas.toDataURL( 'image/jpeg', 0.6 );
-
-		} else {
-
-			return canvas.toDataURL( 'image/png' );
-
-		}
+		return canvas.toDataURL( 'image/png' );
 
 	}
 
@@ -10002,6 +9999,8 @@ const DataUtils = {
 const _vector$9 = /*@__PURE__*/ new Vector3();
 const _vector2$1 = /*@__PURE__*/ new Vector2();
 
+let _id$3 = 0;
+
 class BufferAttribute {
 
 	constructor( array, itemSize, normalized = false ) {
@@ -10014,6 +10013,8 @@ class BufferAttribute {
 
 		this.isBufferAttribute = true;
 
+		Object.defineProperty( this, 'id', { value: _id$3 ++ } );
+
 		this.name = '';
 
 		this.array = array;
@@ -13251,6 +13252,358 @@ class WebGLCubeRenderTarget extends WebGLRenderTarget {
 
 }
 
+class Group extends Object3D {
+
+	constructor() {
+
+		super();
+
+		this.isGroup = true;
+
+		this.type = 'Group';
+
+	}
+
+}
+
+const _moveEvent = { type: 'move' };
+
+class WebXRController {
+
+	constructor() {
+
+		this._targetRay = null;
+		this._grip = null;
+		this._hand = null;
+
+	}
+
+	getHandSpace() {
+
+		if ( this._hand === null ) {
+
+			this._hand = new Group();
+			this._hand.matrixAutoUpdate = false;
+			this._hand.visible = false;
+
+			this._hand.joints = {};
+			this._hand.inputState = { pinching: false };
+
+		}
+
+		return this._hand;
+
+	}
+
+	getTargetRaySpace() {
+
+		if ( this._targetRay === null ) {
+
+			this._targetRay = new Group();
+			this._targetRay.matrixAutoUpdate = false;
+			this._targetRay.visible = false;
+			this._targetRay.hasLinearVelocity = false;
+			this._targetRay.linearVelocity = new Vector3();
+			this._targetRay.hasAngularVelocity = false;
+			this._targetRay.angularVelocity = new Vector3();
+
+		}
+
+		return this._targetRay;
+
+	}
+
+	getGripSpace() {
+
+		if ( this._grip === null ) {
+
+			this._grip = new Group();
+			this._grip.matrixAutoUpdate = false;
+			this._grip.visible = false;
+			this._grip.hasLinearVelocity = false;
+			this._grip.linearVelocity = new Vector3();
+			this._grip.hasAngularVelocity = false;
+			this._grip.angularVelocity = new Vector3();
+
+		}
+
+		return this._grip;
+
+	}
+
+	dispatchEvent( event ) {
+
+		if ( this._targetRay !== null ) {
+
+			this._targetRay.dispatchEvent( event );
+
+		}
+
+		if ( this._grip !== null ) {
+
+			this._grip.dispatchEvent( event );
+
+		}
+
+		if ( this._hand !== null ) {
+
+			this._hand.dispatchEvent( event );
+
+		}
+
+		return this;
+
+	}
+
+	connect( inputSource ) {
+
+		if ( inputSource && inputSource.hand ) {
+
+			const hand = this._hand;
+
+			if ( hand ) {
+
+				for ( const inputjoint of inputSource.hand.values() ) {
+
+					// Initialize hand with joints when connected
+					this._getHandJoint( hand, inputjoint );
+
+				}
+
+			}
+
+		}
+
+		this.dispatchEvent( { type: 'connected', data: inputSource } );
+
+		return this;
+
+	}
+
+	disconnect( inputSource ) {
+
+		this.dispatchEvent( { type: 'disconnected', data: inputSource } );
+
+		if ( this._targetRay !== null ) {
+
+			this._targetRay.visible = false;
+
+		}
+
+		if ( this._grip !== null ) {
+
+			this._grip.visible = false;
+
+		}
+
+		if ( this._hand !== null ) {
+
+			this._hand.visible = false;
+
+		}
+
+		return this;
+
+	}
+
+	update( inputSource, frame, referenceSpace ) {
+
+		let inputPose = null;
+		let gripPose = null;
+		let handPose = null;
+
+		const targetRay = this._targetRay;
+		const grip = this._grip;
+		const hand = this._hand;
+
+		if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) {
+
+			if ( hand && inputSource.hand ) {
+
+				handPose = true;
+
+				for ( const inputjoint of inputSource.hand.values() ) {
+
+					// Update the joints groups with the XRJoint poses
+					const jointPose = frame.getJointPose( inputjoint, referenceSpace );
+
+					// The transform of this joint will be updated with the joint pose on each frame
+					const joint = this._getHandJoint( hand, inputjoint );
+
+					if ( jointPose !== null ) {
+
+						joint.matrix.fromArray( jointPose.transform.matrix );
+						joint.matrix.decompose( joint.position, joint.rotation, joint.scale );
+						joint.matrixWorldNeedsUpdate = true;
+						joint.jointRadius = jointPose.radius;
+
+					}
+
+					joint.visible = jointPose !== null;
+
+				}
+
+				// Custom events
+
+				// Check pinchz
+				const indexTip = hand.joints[ 'index-finger-tip' ];
+				const thumbTip = hand.joints[ 'thumb-tip' ];
+				const distance = indexTip.position.distanceTo( thumbTip.position );
+
+				const distanceToPinch = 0.02;
+				const threshold = 0.005;
+
+				if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {
+
+					hand.inputState.pinching = false;
+					this.dispatchEvent( {
+						type: 'pinchend',
+						handedness: inputSource.handedness,
+						target: this
+					} );
+
+				} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {
+
+					hand.inputState.pinching = true;
+					this.dispatchEvent( {
+						type: 'pinchstart',
+						handedness: inputSource.handedness,
+						target: this
+					} );
+
+				}
+
+			} else {
+
+				if ( grip !== null && inputSource.gripSpace ) {
+
+					gripPose = frame.getPose( inputSource.gripSpace, referenceSpace );
+
+					if ( gripPose !== null ) {
+
+						grip.matrix.fromArray( gripPose.transform.matrix );
+						grip.matrix.decompose( grip.position, grip.rotation, grip.scale );
+						grip.matrixWorldNeedsUpdate = true;
+
+						if ( gripPose.linearVelocity ) {
+
+							grip.hasLinearVelocity = true;
+							grip.linearVelocity.copy( gripPose.linearVelocity );
+
+						} else {
+
+							grip.hasLinearVelocity = false;
+
+						}
+
+						if ( gripPose.angularVelocity ) {
+
+							grip.hasAngularVelocity = true;
+							grip.angularVelocity.copy( gripPose.angularVelocity );
+
+						} else {
+
+							grip.hasAngularVelocity = false;
+
+						}
+
+					}
+
+				}
+
+			}
+
+			if ( targetRay !== null ) {
+
+				inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );
+
+				// Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it
+				if ( inputPose === null && gripPose !== null ) {
+
+					inputPose = gripPose;
+
+				}
+
+				if ( inputPose !== null ) {
+
+					targetRay.matrix.fromArray( inputPose.transform.matrix );
+					targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale );
+					targetRay.matrixWorldNeedsUpdate = true;
+
+					if ( inputPose.linearVelocity ) {
+
+						targetRay.hasLinearVelocity = true;
+						targetRay.linearVelocity.copy( inputPose.linearVelocity );
+
+					} else {
+
+						targetRay.hasLinearVelocity = false;
+
+					}
+
+					if ( inputPose.angularVelocity ) {
+
+						targetRay.hasAngularVelocity = true;
+						targetRay.angularVelocity.copy( inputPose.angularVelocity );
+
+					} else {
+
+						targetRay.hasAngularVelocity = false;
+
+					}
+
+					this.dispatchEvent( _moveEvent );
+
+				}
+
+			}
+
+
+		}
+
+		if ( targetRay !== null ) {
+
+			targetRay.visible = ( inputPose !== null );
+
+		}
+
+		if ( grip !== null ) {
+
+			grip.visible = ( gripPose !== null );
+
+		}
+
+		if ( hand !== null ) {
+
+			hand.visible = ( handPose !== null );
+
+		}
+
+		return this;
+
+	}
+
+	// private method
+
+	_getHandJoint( hand, inputjoint ) {
+
+		if ( hand.joints[ inputjoint.jointName ] === undefined ) {
+
+			const joint = new Group();
+			joint.matrixAutoUpdate = false;
+			joint.visible = false;
+			hand.joints[ inputjoint.jointName ] = joint;
+
+			hand.add( joint );
+
+		}
+
+		return hand.joints[ inputjoint.jointName ];
+
+	}
+
+}
+
 class FogExp2 {
 
 	constructor( color, density = 0.00025 ) {
@@ -16214,7 +16567,7 @@ class BatchedMesh extends Mesh {
 		const instanceInfo = this._instanceInfo;
 		for ( let i = 0, l = instanceInfo.length; i < l; i ++ ) {
 
-			if ( instanceInfo[ i ].geometryIndex === geometryId ) {
+			if ( instanceInfo[ i ].active && instanceInfo[ i ].geometryIndex === geometryId ) {
 
 				this.deleteInstance( i );
 
@@ -17065,7 +17418,7 @@ class Line extends Object3D {
 				const a = index.getX( i );
 				const b = index.getX( i + 1 );
 
-				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b );
+				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, i );
 
 				if ( intersect ) {
 
@@ -17080,7 +17433,7 @@ class Line extends Object3D {
 				const a = index.getX( end - 1 );
 				const b = index.getX( start );
 
-				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b );
+				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, a, b, end - 1 );
 
 				if ( intersect ) {
 
@@ -17097,7 +17450,7 @@ class Line extends Object3D {
 
 			for ( let i = start, l = end - 1; i < l; i += step ) {
 
-				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, i, i + 1 );
+				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, i, i + 1, i );
 
 				if ( intersect ) {
 
@@ -17109,7 +17462,7 @@ class Line extends Object3D {
 
 			if ( this.isLineLoop ) {
 
-				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, end - 1, start );
+				const intersect = checkIntersection( this, raycaster, _ray$1, localThresholdSq, end - 1, start, end - 1 );
 
 				if ( intersect ) {
 
@@ -17156,7 +17509,7 @@ class Line extends Object3D {
 
 }
 
-function checkIntersection( object, raycaster, ray, thresholdSq, a, b ) {
+function checkIntersection( object, raycaster, ray, thresholdSq, a, b, i ) {
 
 	const positionAttribute = object.geometry.attributes.position;
 
@@ -17179,7 +17532,7 @@ function checkIntersection( object, raycaster, ray, thresholdSq, a, b ) {
 		// What do we want? intersection point on the ray or on the segment??
 		// point: raycaster.ray.at( distance ),
 		point: _intersectPointOnSegment.clone().applyMatrix4( object.matrixWorld ),
-		index: a,
+		index: i,
 		face: null,
 		faceIndex: null,
 		barycoord: null,
@@ -17458,20 +17811,6 @@ function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, inte
 
 }
 
-class Group extends Object3D {
-
-	constructor() {
-
-		super();
-
-		this.isGroup = true;
-
-		this.type = 'Group';
-
-	}
-
-}
-
 class VideoTexture extends Texture {
 
 	constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
@@ -17523,6 +17862,38 @@ class VideoTexture extends Texture {
 
 }
 
+class VideoFrameTexture extends VideoTexture {
+
+	constructor( mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
+
+		super( {}, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
+
+		this.isVideoFrameTexture = true;
+
+	}
+
+	update() {
+
+		// overwrites `VideoTexture.update()` with an empty method since
+		// this type of texture is updated via `setFrame()`.
+
+	}
+
+	clone() {
+
+		return new this.constructor().copy( this ); // restoring Texture.clone()
+
+	}
+
+	setFrame( frame ) {
+
+		this.image = frame;
+		this.needsUpdate = true;
+
+	}
+
+}
+
 class FramebufferTexture extends Texture {
 
 	constructor( width, height ) {
@@ -30689,6 +31060,7 @@ class ArrayCamera extends PerspectiveCamera {
 		this.isArrayCamera = true;
 
 		this.cameras = array;
+		this.index = 0;
 
 	}
 
@@ -38577,6 +38949,8 @@ function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha,
 			boxMesh.geometry.dispose();
 			boxMesh.material.dispose();
 
+			boxMesh = undefined;
+
 		}
 
 		if ( planeMesh !== undefined ) {
@@ -38584,6 +38958,8 @@ function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha,
 			planeMesh.geometry.dispose();
 			planeMesh.material.dispose();
 
+			planeMesh = undefined;
+
 		}
 
 	}
@@ -49522,344 +49898,6 @@ function WebGLUtils( gl, extensions ) {
 
 }
 
-const _moveEvent = { type: 'move' };
-
-class WebXRController {
-
-	constructor() {
-
-		this._targetRay = null;
-		this._grip = null;
-		this._hand = null;
-
-	}
-
-	getHandSpace() {
-
-		if ( this._hand === null ) {
-
-			this._hand = new Group();
-			this._hand.matrixAutoUpdate = false;
-			this._hand.visible = false;
-
-			this._hand.joints = {};
-			this._hand.inputState = { pinching: false };
-
-		}
-
-		return this._hand;
-
-	}
-
-	getTargetRaySpace() {
-
-		if ( this._targetRay === null ) {
-
-			this._targetRay = new Group();
-			this._targetRay.matrixAutoUpdate = false;
-			this._targetRay.visible = false;
-			this._targetRay.hasLinearVelocity = false;
-			this._targetRay.linearVelocity = new Vector3();
-			this._targetRay.hasAngularVelocity = false;
-			this._targetRay.angularVelocity = new Vector3();
-
-		}
-
-		return this._targetRay;
-
-	}
-
-	getGripSpace() {
-
-		if ( this._grip === null ) {
-
-			this._grip = new Group();
-			this._grip.matrixAutoUpdate = false;
-			this._grip.visible = false;
-			this._grip.hasLinearVelocity = false;
-			this._grip.linearVelocity = new Vector3();
-			this._grip.hasAngularVelocity = false;
-			this._grip.angularVelocity = new Vector3();
-
-		}
-
-		return this._grip;
-
-	}
-
-	dispatchEvent( event ) {
-
-		if ( this._targetRay !== null ) {
-
-			this._targetRay.dispatchEvent( event );
-
-		}
-
-		if ( this._grip !== null ) {
-
-			this._grip.dispatchEvent( event );
-
-		}
-
-		if ( this._hand !== null ) {
-
-			this._hand.dispatchEvent( event );
-
-		}
-
-		return this;
-
-	}
-
-	connect( inputSource ) {
-
-		if ( inputSource && inputSource.hand ) {
-
-			const hand = this._hand;
-
-			if ( hand ) {
-
-				for ( const inputjoint of inputSource.hand.values() ) {
-
-					// Initialize hand with joints when connected
-					this._getHandJoint( hand, inputjoint );
-
-				}
-
-			}
-
-		}
-
-		this.dispatchEvent( { type: 'connected', data: inputSource } );
-
-		return this;
-
-	}
-
-	disconnect( inputSource ) {
-
-		this.dispatchEvent( { type: 'disconnected', data: inputSource } );
-
-		if ( this._targetRay !== null ) {
-
-			this._targetRay.visible = false;
-
-		}
-
-		if ( this._grip !== null ) {
-
-			this._grip.visible = false;
-
-		}
-
-		if ( this._hand !== null ) {
-
-			this._hand.visible = false;
-
-		}
-
-		return this;
-
-	}
-
-	update( inputSource, frame, referenceSpace ) {
-
-		let inputPose = null;
-		let gripPose = null;
-		let handPose = null;
-
-		const targetRay = this._targetRay;
-		const grip = this._grip;
-		const hand = this._hand;
-
-		if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) {
-
-			if ( hand && inputSource.hand ) {
-
-				handPose = true;
-
-				for ( const inputjoint of inputSource.hand.values() ) {
-
-					// Update the joints groups with the XRJoint poses
-					const jointPose = frame.getJointPose( inputjoint, referenceSpace );
-
-					// The transform of this joint will be updated with the joint pose on each frame
-					const joint = this._getHandJoint( hand, inputjoint );
-
-					if ( jointPose !== null ) {
-
-						joint.matrix.fromArray( jointPose.transform.matrix );
-						joint.matrix.decompose( joint.position, joint.rotation, joint.scale );
-						joint.matrixWorldNeedsUpdate = true;
-						joint.jointRadius = jointPose.radius;
-
-					}
-
-					joint.visible = jointPose !== null;
-
-				}
-
-				// Custom events
-
-				// Check pinchz
-				const indexTip = hand.joints[ 'index-finger-tip' ];
-				const thumbTip = hand.joints[ 'thumb-tip' ];
-				const distance = indexTip.position.distanceTo( thumbTip.position );
-
-				const distanceToPinch = 0.02;
-				const threshold = 0.005;
-
-				if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) {
-
-					hand.inputState.pinching = false;
-					this.dispatchEvent( {
-						type: 'pinchend',
-						handedness: inputSource.handedness,
-						target: this
-					} );
-
-				} else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) {
-
-					hand.inputState.pinching = true;
-					this.dispatchEvent( {
-						type: 'pinchstart',
-						handedness: inputSource.handedness,
-						target: this
-					} );
-
-				}
-
-			} else {
-
-				if ( grip !== null && inputSource.gripSpace ) {
-
-					gripPose = frame.getPose( inputSource.gripSpace, referenceSpace );
-
-					if ( gripPose !== null ) {
-
-						grip.matrix.fromArray( gripPose.transform.matrix );
-						grip.matrix.decompose( grip.position, grip.rotation, grip.scale );
-						grip.matrixWorldNeedsUpdate = true;
-
-						if ( gripPose.linearVelocity ) {
-
-							grip.hasLinearVelocity = true;
-							grip.linearVelocity.copy( gripPose.linearVelocity );
-
-						} else {
-
-							grip.hasLinearVelocity = false;
-
-						}
-
-						if ( gripPose.angularVelocity ) {
-
-							grip.hasAngularVelocity = true;
-							grip.angularVelocity.copy( gripPose.angularVelocity );
-
-						} else {
-
-							grip.hasAngularVelocity = false;
-
-						}
-
-					}
-
-				}
-
-			}
-
-			if ( targetRay !== null ) {
-
-				inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace );
-
-				// Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it
-				if ( inputPose === null && gripPose !== null ) {
-
-					inputPose = gripPose;
-
-				}
-
-				if ( inputPose !== null ) {
-
-					targetRay.matrix.fromArray( inputPose.transform.matrix );
-					targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale );
-					targetRay.matrixWorldNeedsUpdate = true;
-
-					if ( inputPose.linearVelocity ) {
-
-						targetRay.hasLinearVelocity = true;
-						targetRay.linearVelocity.copy( inputPose.linearVelocity );
-
-					} else {
-
-						targetRay.hasLinearVelocity = false;
-
-					}
-
-					if ( inputPose.angularVelocity ) {
-
-						targetRay.hasAngularVelocity = true;
-						targetRay.angularVelocity.copy( inputPose.angularVelocity );
-
-					} else {
-
-						targetRay.hasAngularVelocity = false;
-
-					}
-
-					this.dispatchEvent( _moveEvent );
-
-				}
-
-			}
-
-
-		}
-
-		if ( targetRay !== null ) {
-
-			targetRay.visible = ( inputPose !== null );
-
-		}
-
-		if ( grip !== null ) {
-
-			grip.visible = ( gripPose !== null );
-
-		}
-
-		if ( hand !== null ) {
-
-			hand.visible = ( handPose !== null );
-
-		}
-
-		return this;
-
-	}
-
-	// private method
-
-	_getHandJoint( hand, inputjoint ) {
-
-		if ( hand.joints[ inputjoint.jointName ] === undefined ) {
-
-			const joint = new Group();
-			joint.matrixAutoUpdate = false;
-			joint.visible = false;
-			hand.joints[ inputjoint.jointName ] = joint;
-
-			hand.add( joint );
-
-		}
-
-		return hand.joints[ inputjoint.jointName ];
-
-	}
-
-}
-
 const _occlusion_vertex = `
 void main() {
 
@@ -50227,7 +50265,9 @@ class WebXRManager extends EventDispatcher {
 				currentPixelRatio = renderer.getPixelRatio();
 				renderer.getSize( currentSize );
 
-				const useLayers = session.enabledFeatures !== undefined && session.enabledFeatures.includes( 'layers' );
+				// Check that the browser implements the necessary APIs to use an
+				// XRProjectionLayer rather than an XRWebGLLayer
+				const useLayers = typeof XRWebGLBinding !== 'undefined' && 'createProjectionLayer' in XRWebGLBinding.prototype;
 
 				if ( ! useLayers ) {
 
@@ -50736,8 +50776,11 @@ class WebXRManager extends EventDispatcher {
 				//
 
 				const enabledFeatures = session.enabledFeatures;
+				const gpuDepthSensingEnabled = enabledFeatures &&
+					enabledFeatures.includes( 'depth-sensing' ) &&
+					session.depthUsage == 'gpu-optimized';
 
-				if ( enabledFeatures && enabledFeatures.includes( 'depth-sensing' ) ) {
+				if ( gpuDepthSensingEnabled && glBinding ) {
 
 					const depthData = glBinding.getDepthInformation( views[ 0 ] );
 
@@ -55037,6 +55080,7 @@ exports.TetrahedronGeometry = TetrahedronGeometry;
 exports.Texture = Texture;
 exports.TextureLoader = TextureLoader;
 exports.TextureUtils = TextureUtils;
+exports.TimestampQuery = TimestampQuery;
 exports.TorusGeometry = TorusGeometry;
 exports.TorusKnotGeometry = TorusKnotGeometry;
 exports.Triangle = Triangle;
@@ -55065,6 +55109,7 @@ exports.Vector2 = Vector2;
 exports.Vector3 = Vector3;
 exports.Vector4 = Vector4;
 exports.VectorKeyframeTrack = VectorKeyframeTrack;
+exports.VideoFrameTexture = VideoFrameTexture;
 exports.VideoTexture = VideoTexture;
 exports.WebGL3DRenderTarget = WebGL3DRenderTarget;
 exports.WebGLArrayRenderTarget = WebGLArrayRenderTarget;
@@ -55074,6 +55119,7 @@ exports.WebGLRenderTarget = WebGLRenderTarget;
 exports.WebGLRenderer = WebGLRenderer;
 exports.WebGLUtils = WebGLUtils;
 exports.WebGPUCoordinateSystem = WebGPUCoordinateSystem;
+exports.WebXRController = WebXRController;
 exports.WireframeGeometry = WireframeGeometry;
 exports.WrapAroundEnding = WrapAroundEnding;
 exports.ZeroCurvatureEnding = ZeroCurvatureEnding;

Разница между файлами не показана из-за своего большого размера
+ 412 - 40
build/three.core.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.core.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.module.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.module.min.js


Разница между файлами не показана из-за своего большого размера
+ 10 - 2
build/three.tsl.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.tsl.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.webgpu.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.webgpu.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.webgpu.nodes.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
build/three.webgpu.nodes.min.js


+ 0 - 1
docs/api/ar/materials/RawShaderMaterial.html

@@ -35,7 +35,6 @@
 		[example:webgl_buffergeometry_rawshader WebGL / buffergeometry / rawshader]<br />
 		[example:webgl_buffergeometry_instancing_billboards WebGL / buffergeometry / instancing / billboards]<br />
 		[example:webgl_buffergeometry_instancing WebGL / buffergeometry / instancing]<br />
-		[example:webgl_raymarching_reflect WebGL / raymarching / reflect]<br />
 		[example:webgl_volume_cloud WebGL / volume / cloud]<br />
 		[example:webgl_volume_instancing WebGL / volume / instancing]<br />
 		[example:webgl_volume_perlin WebGL / volume / perlin]

+ 3 - 0
docs/api/en/core/BufferAttribute.html

@@ -70,6 +70,9 @@
 		<h3>[property:Boolean isBufferAttribute]</h3>
 		<p>Read-only flag to check if a given object is of type [name].</p>
 
+		<h3>[property:Integer id]</h3>
+		<p>Unique number for this attribute instance.</p>
+
 		<h3>[property:Integer itemSize]</h3>
 		<p>
 			The length of vectors that are being stored in the

+ 0 - 1
docs/api/en/materials/RawShaderMaterial.html

@@ -35,7 +35,6 @@
 			[example:webgl_buffergeometry_rawshader WebGL / buffergeometry / rawshader]<br />
 			[example:webgl_buffergeometry_instancing_billboards WebGL / buffergeometry / instancing / billboards]<br />
 			[example:webgl_buffergeometry_instancing WebGL / buffergeometry / instancing]<br />
-			[example:webgl_raymarching_reflect WebGL / raymarching / reflect]<br />
 			[example:webgl_volume_cloud WebGL / volume / cloud]<br />
 			[example:webgl_volume_instancing WebGL / volume / instancing]<br />
 			[example:webgl_volume_perlin WebGL / volume / perlin]

+ 2 - 2
docs/api/en/math/Matrix4.html

@@ -315,8 +315,8 @@ m.elements = [ 11, 21, 31, 41,
 			[method:this lookAt]( [param:Vector3 eye], [param:Vector3 target], [param:Vector3 up] )
 		</h3>
 		<p>
-			Constructs a rotation matrix, looking from [page:Vector3 eye] towards
-			[page:Vector3 target] oriented by the [page:Vector3 up] vector.
+			Sets the rotation component of the transformation matrix, looking from [page:Vector3 eye] towards
+			[page:Vector3 target], and oriented by the up-direction [page:Vector3 up].
 		</p>
 
 		<h3>

+ 3 - 0
docs/api/en/objects/BatchedMesh.html

@@ -214,6 +214,9 @@
 		<p>
 			Sets the given local transformation matrix to the defined instance.
 		</p>
+		<p>
+			Negatively scaled matrices are not supported.
+		</p>
 
 		<h3>
 			[method:this setVisibleAt]( [param:Integer instanceId], [param:Boolean visible] )

+ 3 - 0
docs/api/en/objects/InstancedMesh.html

@@ -180,6 +180,9 @@
 			sure you set [page:.instanceMatrix][page:BufferAttribute.needsUpdate .needsUpdate] 
 			to true after updating all the matrices.
 		</p>
+		<p>
+			Negatively scaled matrices are not supported.
+		</p>
 
 		<h3>
 			[method:undefined setMorphAt]( [param:Integer index], [param:Mesh mesh] )

+ 1 - 1
docs/api/en/scenes/Scene.html

@@ -11,7 +11,7 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-			Scenes allow you to set up what and where is to be rendered by three.js.
+			Scenes allow you to set up what is to be rendered and where by three.js.
 			This is where you place objects, lights and cameras.
 		</p>
 

+ 1 - 4
docs/api/en/textures/Texture.html

@@ -309,10 +309,7 @@
 		<h3>[method:Texture clone]()</h3>
 		<p>
 			Make copy of the texture. Note this is not a "deep copy", the image is
-			shared. Besides, cloning a texture does not automatically mark it for a
-			texture upload. You have to set [page:Texture.needsUpdate .needsUpdate] to
-			true as soon as its image property (the data source) is fully loaded or
-			ready.
+			shared. Cloning the texture automatically marks it for texture upload.
 		</p>
 
 		<h3>[method:Object toJSON]( [param:Object meta] )</h3>

+ 101 - 0
docs/api/en/textures/VideoFrameTexture.html

@@ -0,0 +1,101 @@
+<!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:VideoTexture] &rarr;
+
+		<h1>[name]</h1>
+
+		<p class="desc">
+			This class can be used as an alternative way to define video data. Instead of using
+			an instance of `HTMLVideoElement` like with `VideoTexture`, [name] expects each frame is 
+			defined manaully via [page:.setFrame setFrame](). A typical use case for this module is when
+			video frames are decoded with the WebCodecs API.
+		</p>
+
+		<h2>Code Example</h2>
+
+		<code>
+		const texture = new THREE.VideoFrameTexture();
+		texture.setFrame( frame );
+		</code>
+
+		<h2>Examples</h2>
+
+		<p>
+			[example:webgpu_video_frame video / frame]
+		</p>
+
+		<h2>Constructor</h2>
+		<h3>
+			[name]( [param:Constant mapping], [param:Constant wrapS], 
+			[param:Constant wrapT], [param:Constant magFilter], [param:Constant minFilter], 
+			[param:Constant format], [param:Constant type], [param:Number anisotropy] )
+		</h3>
+		<p>
+			[page:Constant mapping] -- How the image is applied to the object. An
+			object type of [page:Textures THREE.UVMapping]. 
+			See [page:Textures mapping constants] for other choices.<br />
+
+			[page:Constant wrapS] -- The default is [page:Textures THREE.ClampToEdgeWrapping]. 
+			See [page:Textures wrap mode constants] for
+			other choices.<br />
+
+			[page:Constant wrapT] -- The default is [page:Textures THREE.ClampToEdgeWrapping]. 
+			See [page:Textures wrap mode constants] for
+			other choices.<br />
+
+			[page:Constant magFilter] -- How the texture is sampled when a texel
+			covers more than one pixel. The default is [page:Textures THREE.LinearFilter]. 
+			See [page:Textures magnification filter constants]
+			for other choices.<br />
+
+			[page:Constant minFilter] -- How the texture is sampled when a texel
+			covers less than one pixel. The default is [page:Textures THREE.LinearFilter]. 
+			See [page:Textures minification filter constants] for
+			other choices.<br />
+
+			[page:Constant format] -- The default is [page:Textures THREE.RGBAFormat].
+			See [page:Textures format constants] for other choices.<br />
+
+			[page:Constant type] -- Default is [page:Textures THREE.UnsignedByteType].
+			See [page:Textures type constants] for other choices.<br />
+
+			[page:Number anisotropy] -- The number of samples taken along the axis
+			through the pixel that has the highest density of texels. By default, this
+			value is `1`. A higher value gives a less blurry result than a basic mipmap,
+			at the cost of more texture samples being used. Use
+			[page:WebGLrenderer.getMaxAnisotropy renderer.getMaxAnisotropy]() to find
+			the maximum valid anisotropy value for the GPU; this value is usually a
+			power of 2.<br /><br />
+		</p>
+
+		<h2>Properties</h2>
+
+		<p>See the base [page:VideoTexture VideoTexture] class for common properties.</p>
+
+		<h3>[property:Boolean isVideoFrameTexture]</h3>
+		<p>Read-only flag to check if a given object is of type [name].</p>
+
+		<h2>Methods</h2>
+
+		<p>See the base [page:VideoTexture VideoTexture] class for common methods.</p>
+
+		<h3>[method:undefined setFrame]( [param:VideoFrame frame] )</h3>
+		<p>
+			Sets the current frame of the video. This will automatically update the texture 
+			so the data can be used for rendering.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+		</p>
+	</body>
+</html>

+ 0 - 1
docs/api/fr/materials/RawShaderMaterial.html

@@ -34,7 +34,6 @@
 			[example:webgl_buffergeometry_rawshader WebGL / buffergeometry / rawshader]<br />
 			[example:webgl_buffergeometry_instancing_billboards WebGL / buffergeometry / instancing / billboards]<br />
 			[example:webgl_buffergeometry_instancing WebGL / buffergeometry / instancing]<br />
-			[example:webgl_raymarching_reflect WebGL / raymarching / reflect]<br />
 			[example:webgl_volume_cloud WebGL / volume / cloud]<br />
 			[example:webgl_volume_instancing WebGL / volume / instancing]<br />
 			[example:webgl_volume_perlin WebGL / volume / perlin]

+ 0 - 1
docs/api/it/materials/RawShaderMaterial.html

@@ -35,7 +35,6 @@
 			[example:webgl_buffergeometry_rawshader WebGL / buffergeometry / rawshader]<br />
 			[example:webgl_buffergeometry_instancing_billboards WebGL / buffergeometry / instancing / billboards]<br />
 			[example:webgl_buffergeometry_instancing WebGL / buffergeometry / instancing]<br />
-			[example:webgl_raymarching_reflect WebGL / raymarching / reflect]<br />
 			[example:webgl_volume_cloud WebGL / volume / cloud]<br />
 			[example:webgl_volume_instancing WebGL / volume / instancing]<br />
 			[example:webgl_volume_perlin WebGL / volume / perlin]

+ 0 - 1
docs/api/zh/materials/RawShaderMaterial.html

@@ -33,7 +33,6 @@
 			[example:webgl_buffergeometry_rawshader WebGL / buffergeometry / rawshader]<br />
 			[example:webgl_buffergeometry_instancing_billboards WebGL / buffergeometry / instancing / billboards]<br />
 			[example:webgl_buffergeometry_instancing WebGL / buffergeometry / instancing]<br />
-			[example:webgl_raymarching_reflect WebGL / raymarching / reflect]<br />
 			[example:webgl_volume_cloud WebGL / volume / cloud]<br />
 			[example:webgl_volume_instancing WebGL / volume / instancing]<br />
 			[example:webgl_volume_perlin WebGL / volume / perlin]

+ 1 - 1
docs/examples/en/animations/CCDIKSolver.html

@@ -9,7 +9,7 @@
 	<body>
 		<h1>[name]</h1>
 
-		<p class="desc"> A solver for IK with <a href="https://sites.google.com/site/auraliusproject/ccd-algorithm">`CCD Algorithm`</a>. <br /><br />
+		<p class="desc"> A solver for IK with <a href="https://web.archive.org/web/20221206080850/https://sites.google.com/site/auraliusproject/ccd-algorithm">`CCD Algorithm`</a>. <br /><br />
 		[name] solves Inverse Kinematics Problem with CCD Algorithm.
 		[name] is designed to work with [page:SkinnedMesh] but also can be used with [page:GLTFLoader] skeleton.
 		</p>

+ 5 - 4
docs/examples/en/geometries/ConvexGeometry.html

@@ -29,10 +29,11 @@
 
 		<h2>Code Example</h2>
 
-		<code>const geometry = new ConvexGeometry( points );
-		const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
-		const mesh = new THREE.Mesh( geometry, material );
-		scene.add( mesh );
+		<code>
+const geometry = new ConvexGeometry( points );
+const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+const mesh = new THREE.Mesh( geometry, material );
+scene.add( mesh );
 		</code>
 
 		<h2>Examples</h2>

+ 1 - 1
docs/examples/en/lines/LineMaterial.html

@@ -64,7 +64,7 @@
 		<p>The size of the gap. Default is `1`.</p>
 
 		<h3>[property:Float linewidth]</h3>
-		<p>Controls line thickness. Default is `1`.</p>
+		<p>Controls line thickness in CSS pixel units when [page:worldUnits] is `false` (default), or in world units when [page:worldUnits] is `true`. Default is `1`.</p>
 
 		<h3>[property:Vector2 resolution]</h3>
 		<p>

+ 1 - 1
docs/examples/zh/animations/CCDIKSolver.html

@@ -11,7 +11,7 @@
 <body>
 	<h1>CCDIK解算器([name])</h1>
 
-	<p class="desc"> 一种基于 <a href="https://sites.google.com/site/auraliusproject/ccd-algorithm">`CCD Algorithm`</a> 的 IK
+	<p class="desc"> 一种基于 <a href="https://web.archive.org/web/20221206080850/https://sites.google.com/site/auraliusproject/ccd-algorithm">`CCD Algorithm`</a> 的 IK
 		解算器。<br /><br />
 		[name] 用 CCD 算法解决逆运动学问题。
 		[name] 设计用于与 [page:SkinnedMesh] 配合使用,但也可与 [page:GLTFLoader] 配合使用。

+ 1 - 0
docs/list.json

@@ -318,6 +318,7 @@
 				"FramebufferTexture": "api/en/textures/FramebufferTexture",
 				"Source": "api/en/textures/Source",
 				"Texture": "api/en/textures/Texture",
+				"VideoFrameTexture": "api/en/textures/VideoFrameTexture",
 				"VideoTexture": "api/en/textures/VideoTexture"
 			}
 

+ 2 - 6
docs/manual/en/introduction/Color-management.html

@@ -214,14 +214,10 @@ THREE.ColorManagement.enabled = true;
 
 	<p>
 		Output to a display device, image, or video may involve conversion from the open domain
-		Linear-sRGB working color space to another color space. This conversion may be performed in
-		the main render pass ([page:WebGLRenderer.outputColorSpace]), or during post-processing.
+		Linear-sRGB working color space to another color space. The conversion is defined by
+		([page:WebGLRenderer.outputColorSpace]). When using post-processing, this requires OutputPass.
 	</p>
 
-	<code>
-renderer.outputColorSpace = THREE.SRGBColorSpace; // optional with post-processing
-	</code>
-
 	<ul>
 		<li>
 			<b>Display:</b> Colors written to a WebGL canvas for display should be in the sRGB

+ 5 - 6
editor/js/Command.js

@@ -1,11 +1,10 @@
-/**
- * @param editor pointer to main editor object used to initialize
- *        each command object with a reference to the editor
- * @constructor
- */
-
 class Command {
 
+	/**
+	 * @param {Editor} editor pointer to main editor object used to initialize
+	 *        each command object with a reference to the editor
+	 * @constructor
+	 */
 	constructor( editor ) {
 
 		this.id = - 1;

+ 4 - 4
editor/js/Strings.js

@@ -3,8 +3,8 @@ function Strings( config ) {
 	const language = config.getKey( 'language' );
 
 	const values = {
-fa: {
-'prompt/file/open': 'تمام داده های ذخیره نشده پاک خواهند شد آیا مطمئنید؟',
+		fa: {
+			'prompt/file/open': 'تمام داده های ذخیره نشده پاک خواهند شد آیا مطمئنید؟',
 			'prompt/file/failedToOpenProject': 'خطایی در باز کردن پروژه پیش آمده',
 			'prompt/file/export/noMeshSelected': 'هیچ Mesh ای انتخاب نکردید',
 			'prompt/file/export/noObjectSelected': 'هیچ آبجکتی انتخاب نکردید!',
@@ -176,7 +176,7 @@ fa: {
 			'sidebar/geometry/buffer_geometry/attributes': 'صفات',
 			'sidebar/geometry/buffer_geometry/index': 'شاخص',
 			'sidebar/geometry/buffer_geometry/morphAttributes': 'صفات شکل (مورف)',
-			'sidebar/geometry/buffer_geometry/morphRelative':  'صفات نسبی (رلتیو)',
+			'sidebar/geometry/buffer_geometry/morphRelative': 'صفات نسبی (رلتیو)',
 
 			'sidebar/geometry/capsule_geometry/radius': 'شعاع',
 			'sidebar/geometry/capsule_geometry/length': 'طول',
@@ -239,7 +239,7 @@ fa: {
 
 			'sidebar/geometry/sphere_geometry/radius': 'شعاع',
 			'sidebar/geometry/sphere_geometry/widthsegments': 'بخش عرض',
-			'sidebar/geometry/sphere_geometry/heightsegments':  'بخش ارتفاع',
+			'sidebar/geometry/sphere_geometry/heightsegments': 'بخش ارتفاع',
 			'sidebar/geometry/sphere_geometry/phistart': 'شروع فی',
 			'sidebar/geometry/sphere_geometry/philength': ' طول فی',
 			'sidebar/geometry/sphere_geometry/thetastart': 'شروع تتا',

+ 7 - 3
examples/files.json

@@ -300,6 +300,7 @@
 		"webgpu_backdrop_area",
 		"webgpu_backdrop_water",
 		"webgpu_camera",
+		"webgpu_camera_array",
 		"webgpu_camera_logarithmicdepthbuffer",
 		"webgpu_clearcoat",
 		"webgpu_clipping",
@@ -423,6 +424,7 @@
 		"webgpu_sky",
 		"webgpu_sprites",
 		"webgpu_storage_buffer",
+		"webgpu_struct_drawindirect",
 		"webgpu_texturegrad",
 		"webgpu_textures_2d-array",
 		"webgpu_textures_2d-array_compressed",
@@ -430,7 +432,6 @@
 		"webgpu_textures_partialupdate",
 		"webgpu_tonemapping",
 		"webgpu_tsl_angular_slicing",
-		"webgpu_tsl_coffee_smoke",
 		"webgpu_tsl_compute_attractors_particles",
 		"webgpu_tsl_earth",
 		"webgpu_tsl_editor",
@@ -443,10 +444,12 @@
 		"webgpu_tsl_vfx_flames",
 		"webgpu_tsl_vfx_linkedparticles",
 		"webgpu_tsl_vfx_tornado",
+		"webgpu_video_frame",
 		"webgpu_video_panorama",
 		"webgpu_volume_cloud",
 		"webgpu_volume_perlin",
-		"webgpu_water"
+		"webgpu_water",
+		"webgpu_xr_cubes"
 	],
 	"webaudio": [
 		"webaudio_orientation",
@@ -514,7 +517,8 @@
 		"misc_exporter_usdz",
 		"misc_exporter_exr",
 		"misc_exporter_ktx2",
-		"misc_lookat"
+		"misc_lookat",
+		"misc_raycaster_helper"
 	],
 	"css2d": [
 		"css2d_label"

+ 6 - 5
examples/jsm/animation/CCDIKSolver.js

@@ -28,7 +28,7 @@ const _matrix = new Matrix4();
 
 /**
  * CCD Algorithm
- *  - https://sites.google.com/site/auraliusproject/ccd-algorithm
+ *  - https://web.archive.org/web/20221206080850/https://sites.google.com/site/auraliusproject/ccd-algorithm
  *
  * // ik parameter example
  * //
@@ -277,13 +277,14 @@ function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv )
 
 /**
  * Visualize IK bones
- *
- * @param {SkinnedMesh} mesh
- * @param {Array<Object>} iks
- * @param {number} sphereSize
  */
 class CCDIKHelper extends Object3D {
 
+	/**
+	 * @param {SkinnedMesh} mesh
+ 	 * @param {Array<Object>} [iks=[]]
+ 	 * @param {number} [sphereSize=0.25]
+	 */
 	constructor( mesh, iks = [], sphereSize = 0.25 ) {
 
 		super();

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

@@ -1,4 +1,4 @@
-let isAvailable = ( typeof navigator !== 'undefined'  && navigator.gpu !== undefined );
+let isAvailable = ( typeof navigator !== 'undefined' && navigator.gpu !== undefined );
 
 if ( typeof window !== 'undefined' && isAvailable ) {
 

+ 23 - 21
examples/jsm/controls/ArcballControls.js

@@ -70,14 +70,18 @@ const _offset = new Vector3();
 const _gizmoMatrixStateTemp = new Matrix4();
 const _cameraMatrixStateTemp = new Matrix4();
 const _scalePointTemp = new Vector3();
-/**
- *
- * @param {Camera} camera Virtual camera used in the scene
- * @param {HTMLElement} domElement Renderer's dom element
- * @param {Scene} scene The scene to be rendered
- */
+
+const _EPS = 0.000001;
+
+
 class ArcballControls extends Controls {
 
+	/**
+	 *
+	 * @param {Camera} camera Virtual camera used in the scene
+	 * @param {HTMLElement?} [domElement=null] Renderer's dom element
+	 * @param {Scene?} [scene=null] The scene to be rendered
+	 */
 	constructor( camera, domElement = null, scene = null ) {
 
 		super( camera, domElement );
@@ -1100,9 +1104,9 @@ class ArcballControls extends Controls {
 
 	/**
 	 * Set a new mouse action by specifying the operation to be performed and a mouse/key combination. In case of conflict, replaces the existing one
-	 * @param {String} operation The operation to be performed ('PAN', 'ROTATE', 'ZOOM', 'FOV)
-	 * @param {*} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
-	 * @param {*} key The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
+	 * @param {'PAN'|'ROTATE'|'ZOOM'|'FOV'} operation The operation to be performed ('PAN', 'ROTATE', 'ZOOM', 'FOV')
+	 * @param {0|1|2|'WHEEL'} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
+	 * @param {'CTRL'|'SHIFT'|null} [key=null] The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
 	 * @returns {Boolean} True if the mouse action has been successfully added, false otherwise
 	 */
 	setMouseAction( operation, mouse, key = null ) {
@@ -1181,8 +1185,8 @@ class ArcballControls extends Controls {
 
 	/**
 	 * Remove a mouse action by specifying its mouse/key combination
-	 * @param {*} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
-	 * @param {*} key The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
+	 * @param {0|1|2|'WHEEL'} mouse A mouse button (0, 1, 2) or 'WHEEL' for wheel notches
+	 * @param {'CTRL'|'SHIFT'|null} key The keyboard modifier ('CTRL', 'SHIFT') or null if key is not needed
 	 * @returns {Boolean} True if the operation has been successfully removed, false otherwise
 	 */
 	unsetMouseAction( mouse, key = null ) {
@@ -1206,7 +1210,7 @@ class ArcballControls extends Controls {
 	 * Return the operation associated to a mouse/keyboard combination
 	 * @param {0|1|2|'WHEEL'} mouse Mouse button index (0, 1, 2) or 'WHEEL' for wheel notches
 	 * @param {'CTRL'|'SHIFT'|null} key Keyboard modifier
-	 * @returns {string|null} The operation if it has been found, null otherwise
+	 * @returns {'PAN'|'ROTATE'|'ZOOM'|'FOV'|null} The operation if it has been found, null otherwise
 	 */
 	getOpFromAction( mouse, key ) {
 
@@ -1246,7 +1250,7 @@ class ArcballControls extends Controls {
 	 * Get the operation associated to mouse and key combination and returns the corresponding FSA state
 	 * @param {0|1|2} mouse Mouse button index (0, 1, 2)
 	 * @param {'CTRL'|'SHIFT'|null} key Keyboard modifier
-	 * @returns {STATE|null} The FSA state obtained from the operation associated to mouse/keyboard combination
+	 * @returns {STATE?} The FSA state obtained from the operation associated to mouse/keyboard combination
 	 */
 	getOpStateFromAction( mouse, key ) {
 
@@ -1478,7 +1482,7 @@ class ArcballControls extends Controls {
 	 * Focus operation consist of positioning the point of interest in front of the camera and a slightly zoom in
 	 * @param {Vector3} point The point of interest
 	 * @param {Number} size Scale factor
-	 * @param {Number} amount Amount of operation to be completed (used for focus animations, default is complete full operation)
+	 * @param {Number} [amount=1] Amount of operation to be completed (used for focus animations, default is complete full operation)
 	 */
 	focus( point, size, amount = 1 ) {
 
@@ -1717,7 +1721,7 @@ class ArcballControls extends Controls {
 
 	/**
 	 * Set gizmos radius factor and redraws gizmos
-	 * @param {Float} value Value of radius factor
+	 * @param {Number} value Value of radius factor
 	 */
 	setTbRadius( value ) {
 
@@ -2324,7 +2328,7 @@ class ArcballControls extends Controls {
 	 *
 	 * @param {Vector2} cursor Cursor coordinates in NDC
 	 * @param {Camera} camera Virtual camera
-	 * @returns {Vector3|null} The point of intersection with the model, if exist, null otherwise
+	 * @returns {Vector3?} The point of intersection with the model, if exist, null otherwise
 	 */
 	unprojectOnObj( cursor, camera ) {
 
@@ -2485,7 +2489,7 @@ class ArcballControls extends Controls {
 	 * @param {Number} cursorX Cursor horizontal coordinate on screen
 	 * @param {Number} cursorY Cursor vertical coordinate on screen
 	 * @param {HTMLElement} canvas The canvas where the renderer draws its output
-	 * @param {Boolean} initialDistance If initial distance between camera and gizmos should be used for calculations instead of current (Perspective only)
+	 * @param {Boolean} [initialDistance=false] If initial distance between camera and gizmos should be used for calculations instead of current (Perspective only)
 	 * @returns {Vector3} The unprojected point on the trackball plane
 	 */
 	unprojectOnTbPlane( camera, cursorX, cursorY, canvas, initialDistance = false ) {
@@ -2600,8 +2604,6 @@ class ArcballControls extends Controls {
 
 	update() {
 
-		const EPS = 0.000001;
-
 		if ( this.target.equals( this._currentTarget ) === false ) {
 
 			this._gizmos.position.copy( this.target );	//for correct radius calculation
@@ -2627,7 +2629,7 @@ class ArcballControls extends Controls {
 			//check distance
 			const distance = this.object.position.distanceTo( this._gizmos.position );
 
-			if ( distance > this.maxDistance + EPS || distance < this.minDistance - EPS ) {
+			if ( distance > this.maxDistance + _EPS || distance < this.minDistance - _EPS ) {
 
 				const newDistance = MathUtils.clamp( distance, this.minDistance, this.maxDistance );
 				this.applyTransformMatrix( this.scale( newDistance / distance, this._gizmos.position ) );
@@ -2646,7 +2648,7 @@ class ArcballControls extends Controls {
 			const oldRadius = this._tbRadius;
 			this._tbRadius = this.calculateTbRadius( this.object );
 
-			if ( oldRadius < this._tbRadius - EPS || oldRadius > this._tbRadius + EPS ) {
+			if ( oldRadius < this._tbRadius - _EPS || oldRadius > this._tbRadius + _EPS ) {
 
 				const scale = ( this._gizmos.scale.x + this._gizmos.scale.y + this._gizmos.scale.z ) / 3;
 				const newRadius = this._tbRadius / scale;

+ 26 - 25
examples/jsm/exporters/GLTFExporter.js

@@ -191,7 +191,7 @@ class GLTFExporter {
 	/**
 	 * Parse scenes and generate GLTF output
 	 *
-	 * @param  {Scene|THREE.Scenes} input   Scene or Array of THREE.Scenes
+	 * @param  {Scene|Array<Scene>} input   Scene or Array of THREE.Scenes
 	 * @param  {Function} onDone  Callback on completed
 	 * @param  {Function} onError  Callback on errors
 	 * @param  {Object} options options
@@ -343,9 +343,10 @@ function isIdentityMatrix( matrix ) {
 
 /**
  * Get the min and max vectors from the given attribute
+ *
  * @param  {BufferAttribute} attribute Attribute to find the min/max in range from start to start + count
- * @param  {Integer} start
- * @param  {Integer} count
+ * @param  {Number} start Start index
+ * @param  {Number} count Range to cover
  * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
  */
 function getMinMax( attribute, start, count ) {
@@ -399,8 +400,8 @@ function getMinMax( attribute, start, count ) {
  * Get the required size + padding for a buffer, rounded to the next 4-byte boundary.
  * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
  *
- * @param {Integer} bufferSize The size the original buffer.
- * @returns {Integer} new buffer size with required padding.
+ * @param {Number} bufferSize The size the original buffer. Should be an integer.
+ * @returns {Number} new buffer size with required padding as an integer.
  *
  */
 function getPaddedBufferSize( bufferSize ) {
@@ -413,7 +414,7 @@ function getPaddedBufferSize( bufferSize ) {
  * Returns a buffer aligned to 4-byte boundary.
  *
  * @param {ArrayBuffer} arrayBuffer Buffer to pad
- * @param {Integer} paddingByte (Optional)
+ * @param {Number} [paddingByte=0] Should be an integer
  * @returns {ArrayBuffer} The same buffer if it's already aligned to 4-byte boundary or a new buffer
  */
 function getPaddedArrayBuffer( arrayBuffer, paddingByte = 0 ) {
@@ -545,8 +546,8 @@ class GLTFWriter {
 	/**
 	 * Parse scenes and generate GLTF output
 	 *
-	 * @param {Scene|THREE.Scenes} input   Scene or Array of THREE.Scenes
-	 * @param {Function} onDone  Callback on completed
+	 * @param {Scene|Array<Scene>} input Scene or Array of THREE.Scenes
+	 * @param {Function} onDone Callback on completed
 	 * @param {Object} options options
 	 */
 	async writeAsync( input, onDone, options = {} ) {
@@ -714,7 +715,7 @@ class GLTFWriter {
 	 *
 	 * @param  {Object} attribute
 	 * @param {boolean} [isRelativeCopy=false]
-	 * @return {Integer}
+	 * @return {Number} An integer
 	 */
 	getUID( attribute, isRelativeCopy = false ) {
 
@@ -968,7 +969,7 @@ class GLTFWriter {
 	/**
 	 * Process a buffer to append to the default one.
 	 * @param  {ArrayBuffer} buffer
-	 * @return {Integer}
+	 * @return {0}
 	 */
 	processBuffer( buffer ) {
 
@@ -990,7 +991,7 @@ class GLTFWriter {
 	 * @param  {number} componentType
 	 * @param  {number} start
 	 * @param  {number} count
-	 * @param  {number} target (Optional) Target usage of the BufferView
+	 * @param  {number?} target Target usage of the BufferView
 	 * @return {Object}
 	 */
 	processBufferView( attribute, componentType, start, count, target ) {
@@ -1144,7 +1145,7 @@ class GLTFWriter {
 	/**
 	 * Process and generate a BufferView from an image Blob.
 	 * @param {Blob} blob
-	 * @return {Promise<Integer>}
+	 * @return {Promise<Number>} An integer
 	 */
 	processBufferViewImage( blob ) {
 
@@ -1179,10 +1180,10 @@ class GLTFWriter {
 	/**
 	 * Process attribute to generate an accessor
 	 * @param  {BufferAttribute} attribute Attribute to process
-	 * @param  {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
-	 * @param  {Integer} start (Optional)
-	 * @param  {Integer} count (Optional)
-	 * @return {Integer|null} Index of the processed accessor on the "accessors" array
+	 * @param  {THREE.BufferGeometry?} geometry Geometry used for truncated draw range
+	 * @param  {Number} [start=0]
+	 * @param  {Number} [count=Infinity]
+	 * @return {Number?} Index of the processed accessor on the "accessors" array
 	 */
 	processAccessor( attribute, geometry, start, count ) {
 
@@ -1277,10 +1278,10 @@ class GLTFWriter {
 	/**
 	 * Process image
 	 * @param  {Image} image to process
-	 * @param  {Integer} format of the image (RGBAFormat)
+	 * @param  {Number} format Identifier of the format (RGBAFormat)
 	 * @param  {Boolean} flipY before writing out the image
 	 * @param  {String} mimeType export format
-	 * @return {Integer}     Index of the processed texture in the "images" array
+	 * @return {Number}     Index of the processed texture in the "images" array
 	 */
 	processImage( image, format, flipY, mimeType = 'image/png' ) {
 
@@ -1417,7 +1418,7 @@ class GLTFWriter {
 	/**
 	 * Process sampler
 	 * @param  {Texture} map Texture to process
-	 * @return {Integer}     Index of the processed texture in the "samplers" array
+	 * @return {Number}      Index of the processed texture in the "samplers" array
 	 */
 	processSampler( map ) {
 
@@ -1439,7 +1440,7 @@ class GLTFWriter {
 	/**
 	 * Process texture
 	 * @param  {Texture} map Map to process
-	 * @return {Integer} Index of the processed texture in the "textures" array
+	 * @return {Promise<Number>} Index of the processed texture in the "textures" array
 	 */
 	async processTextureAsync( map ) {
 
@@ -1485,7 +1486,7 @@ class GLTFWriter {
 	/**
 	 * Process material
 	 * @param  {THREE.Material} material Material to process
-	 * @return {Integer|null} Index of the processed material in the "materials" array
+	 * @return {Promise<Number|null>} Index of the processed material in the "materials" array
 	 */
 	async processMaterialAsync( material ) {
 
@@ -1661,7 +1662,7 @@ class GLTFWriter {
 	/**
 	 * Process mesh
 	 * @param  {THREE.Mesh} mesh Mesh to process
-	 * @return {Integer|null} Index of the processed mesh in the "meshes" array
+	 * @return {Promise<Number|null>} Index of the processed mesh in the "meshes" array
 	 */
 	async processMeshAsync( mesh ) {
 
@@ -2066,7 +2067,7 @@ class GLTFWriter {
 	/**
 	 * Process camera
 	 * @param  {THREE.Camera} camera Camera to process
-	 * @return {Integer}      Index of the processed mesh in the "camera" array
+	 * @return {Number} Index of the processed mesh in the "camera" array
 	 */
 	processCamera( camera ) {
 
@@ -2267,7 +2268,7 @@ class GLTFWriter {
 	/**
 	 * Process Object3D node
 	 * @param  {THREE.Object3D} object Object3D to processNodeAsync
-	 * @return {Integer} Index of the node in the nodes list
+	 * @return {Promise<Number>} Index of the node in the nodes list
 	 */
 	async processNodeAsync( object ) {
 
@@ -2418,7 +2419,7 @@ class GLTFWriter {
 
 	/**
 	 * Creates a Scene to hold a list of objects and parse it
-	 * @param  {Array} objects List of objects to process
+	 * @param  {Array<THREE.Object3D>} objects List of objects to process
 	 */
 	async processObjectsAsync( objects ) {
 

+ 0 - 176
examples/jsm/geometries/InstancedPointsGeometry.js

@@ -1,176 +0,0 @@
-import {
-	Box3,
-	Float32BufferAttribute,
-	InstancedBufferGeometry,
-	InstancedBufferAttribute,
-	Sphere,
-	Vector3
-} from 'three';
-
-const _vector = new Vector3();
-
-class InstancedPointsGeometry extends InstancedBufferGeometry {
-
-	constructor() {
-
-		super();
-
-		this.isInstancedPointsGeometry = true;
-
-		this.type = 'InstancedPointsGeometry';
-
-		const positions = [ - 1, 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];
-		const uvs = [ 0, 1, 1, 1, 0, 0, 1, 0 ];
-		const index = [ 0, 2, 1, 2, 3, 1 ];
-
-		this.setIndex( index );
-		this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
-		this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
-
-	}
-
-	applyMatrix4( matrix ) {
-
-		const pos = this.attributes.instancePosition;
-
-		if ( pos !== undefined ) {
-
-			pos.applyMatrix4( matrix );
-
-			pos.needsUpdate = true;
-
-		}
-
-		if ( this.boundingBox !== null ) {
-
-			this.computeBoundingBox();
-
-		}
-
-		if ( this.boundingSphere !== null ) {
-
-			this.computeBoundingSphere();
-
-		}
-
-		return this;
-
-	}
-
-	setPositions( array ) {
-
-		let points;
-
-		if ( array instanceof Float32Array ) {
-
-			points = array;
-
-		} else if ( Array.isArray( array ) ) {
-
-			points = new Float32Array( array );
-
-		}
-
-		this.setAttribute( 'instancePosition', new InstancedBufferAttribute( points, 3 ) ); // xyz
-
-		//
-
-		this.computeBoundingBox();
-		this.computeBoundingSphere();
-
-		this.instanceCount = points.length / 3;
-
-		return this;
-
-	}
-
-	setColors( array ) {
-
-		let colors;
-
-		if ( array instanceof Float32Array ) {
-
-			colors = array;
-
-		} else if ( Array.isArray( array ) ) {
-
-			colors = new Float32Array( array );
-
-		}
-
-		this.setAttribute( 'instanceColor', new InstancedBufferAttribute( colors, 3 ) ); // rgb
-
-		return this;
-
-	}
-
-	computeBoundingBox() {
-
-		if ( this.boundingBox === null ) {
-
-			this.boundingBox = new Box3();
-
-		}
-
-		const pos = this.attributes.instancePosition;
-
-		if ( pos !== undefined ) {
-
-			this.boundingBox.setFromBufferAttribute( pos );
-
-		}
-
-	}
-
-	computeBoundingSphere() {
-
-		if ( this.boundingSphere === null ) {
-
-			this.boundingSphere = new Sphere();
-
-		}
-
-		if ( this.boundingBox === null ) {
-
-			this.computeBoundingBox();
-
-		}
-
-		const pos = this.attributes.instancePosition;
-
-		if ( pos !== undefined ) {
-
-			const center = this.boundingSphere.center;
-
-			this.boundingBox.getCenter( center );
-
-			let maxRadiusSq = 0;
-
-			for ( let i = 0, il = pos.count; i < il; i ++ ) {
-
-				_vector.fromBufferAttribute( pos, i );
-				maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
-
-			}
-
-			this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
-
-			if ( isNaN( this.boundingSphere.radius ) ) {
-
-				console.error( 'THREE.InstancedPointsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
-
-			}
-
-		}
-
-	}
-
-	toJSON() {
-
-		// todo
-
-	}
-
-}
-
-export default InstancedPointsGeometry;

+ 1 - 12
examples/jsm/geometries/TextGeometry.js

@@ -33,20 +33,9 @@ class TextGeometry extends ExtrudeGeometry {
 
 			const shapes = font.generateShapes( text, parameters.size );
 
-			// translate parameters to ExtrudeGeometry API
-
-			if ( parameters.depth === undefined && parameters.height !== undefined ) {
-
-				console.warn( 'THREE.TextGeometry: .height is now depreciated. Please use .depth instead' ); // @deprecated, r163
-
-			}
-
-			parameters.depth = parameters.depth !== undefined ?
-				parameters.depth : parameters.height !== undefined ?
-					parameters.height : 50;
-
 			// defaults
 
+			if ( parameters.depth === undefined ) parameters.depth = 50;
 			if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
 			if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
 			if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;

+ 108 - 51
examples/jsm/interactive/InteractiveGroup.js

@@ -7,97 +7,154 @@ import {
 const _pointer = new Vector2();
 const _event = { type: '', data: _pointer };
 
+// TODO: Dispatch pointerevents too
+
+/**
+ * The XR events that are mapped to "standard" pointer events
+ */
+const _events = {
+	'move': 'mousemove',
+	'select': 'click',
+	'selectstart': 'mousedown',
+	'selectend': 'mouseup'
+};
+
 const _raycaster = new Raycaster();
 
 class InteractiveGroup extends Group {
 
-	listenToPointerEvents( renderer, camera ) {
+	constructor() {
+
+		super();
+
+		this.raycaster = new Raycaster();
 
-		const scope = this;
-		const raycaster = new Raycaster();
+		this.element = null;
+		this.camera = null;
 
-		const element = renderer.domElement;
+		this.controllers = [];
 
-		function onPointerEvent( event ) {
+		this._onPointerEvent = this.onPointerEvent.bind( this );
+		this._onXRControllerEvent = this.onXRControllerEvent.bind( this );
 
-			event.stopPropagation();
+	}
+
+	onPointerEvent( event ) {
 
-			const rect = renderer.domElement.getBoundingClientRect();
+		event.stopPropagation();
 
-			_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-			_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
+		const rect = this.element.getBoundingClientRect();
 
-			raycaster.setFromCamera( _pointer, camera );
+		_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
+		_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
 
-			const intersects = raycaster.intersectObjects( scope.children, false );
+		this.raycaster.setFromCamera( _pointer, this.camera );
 
-			if ( intersects.length > 0 ) {
+		const intersects = this.raycaster.intersectObjects( this.children, false );
 
-				const intersection = intersects[ 0 ];
+		if ( intersects.length > 0 ) {
 
-				const object = intersection.object;
-				const uv = intersection.uv;
+			const intersection = intersects[ 0 ];
 
-				_event.type = event.type;
-				_event.data.set( uv.x, 1 - uv.y );
+			const object = intersection.object;
+			const uv = intersection.uv;
 
-				object.dispatchEvent( _event );
+			_event.type = event.type;
+			_event.data.set( uv.x, 1 - uv.y );
 
-			}
+			object.dispatchEvent( _event );
 
 		}
 
-		element.addEventListener( 'pointerdown', onPointerEvent );
-		element.addEventListener( 'pointerup', onPointerEvent );
-		element.addEventListener( 'pointermove', onPointerEvent );
-		element.addEventListener( 'mousedown', onPointerEvent );
-		element.addEventListener( 'mouseup', onPointerEvent );
-		element.addEventListener( 'mousemove', onPointerEvent );
-		element.addEventListener( 'click', onPointerEvent );
+	}
+
+	onXRControllerEvent( event ) {
+
+		const controller = event.target;
+
+		_raycaster.setFromXRController( controller );
+
+		const intersections = _raycaster.intersectObjects( this.children, false );
+
+		if ( intersections.length > 0 ) {
+
+			const intersection = intersections[ 0 ];
+
+			const object = intersection.object;
+			const uv = intersection.uv;
+
+			_event.type = _events[ event.type ];
+			_event.data.set( uv.x, 1 - uv.y );
+
+			object.dispatchEvent( _event );
+
+		}
 
 	}
 
-	listenToXRControllerEvents( controller ) {
+	listenToPointerEvents( renderer, camera ) {
 
-		const scope = this;
+		this.camera = camera;
+		this.element = renderer.domElement;
 
-		// TODO: Dispatch pointerevents too
+		this.element.addEventListener( 'pointerdown', this._onPointerEvent );
+		this.element.addEventListener( 'pointerup', this._onPointerEvent );
+		this.element.addEventListener( 'pointermove', this._onPointerEvent );
+		this.element.addEventListener( 'mousedown', this._onPointerEvent );
+		this.element.addEventListener( 'mouseup', this._onPointerEvent );
+		this.element.addEventListener( 'mousemove', this._onPointerEvent );
+		this.element.addEventListener( 'click', this._onPointerEvent );
 
-		const events = {
-			'move': 'mousemove',
-			'select': 'click',
-			'selectstart': 'mousedown',
-			'selectend': 'mouseup'
-		};
+	}
 
-		function onXRControllerEvent( event ) {
+	disconnectionPointerEvents() {
 
-			const controller = event.target;
+		if ( this.element !== null ) {
 
-			_raycaster.setFromXRController( controller );
+			this.element.removeEventListener( 'pointerdown', this._onPointerEvent );
+			this.element.removeEventListener( 'pointerup', this._onPointerEvent );
+			this.element.removeEventListener( 'pointermove', this._onPointerEvent );
+			this.element.removeEventListener( 'mousedown', this._onPointerEvent );
+			this.element.removeEventListener( 'mouseup', this._onPointerEvent );
+			this.element.removeEventListener( 'mousemove', this._onPointerEvent );
+			this.element.removeEventListener( 'click', this._onPointerEvent );
 
-			const intersections = _raycaster.intersectObjects( scope.children, false );
+		}
 
-			if ( intersections.length > 0 ) {
+	}
 
-				const intersection = intersections[ 0 ];
+	listenToXRControllerEvents( controller ) {
 
-				const object = intersection.object;
-				const uv = intersection.uv;
+		this.controllers.push( controller );
+		controller.addEventListener( 'move', this._onXRControllerEvent );
+		controller.addEventListener( 'select', this._onXRControllerEvent );
+		controller.addEventListener( 'selectstart', this._onXRControllerEvent );
+		controller.addEventListener( 'selectend', this._onXRControllerEvent );
 
-				_event.type = events[ event.type ];
-				_event.data.set( uv.x, 1 - uv.y );
+	}
+
+	disconnectXrControllerEvents() {
 
-				object.dispatchEvent( _event );
+		for ( const controller of this.controllers ) {
 
-			}
+			controller.removeEventListener( 'move', this._onXRControllerEvent );
+			controller.removeEventListener( 'select', this._onXRControllerEvent );
+			controller.removeEventListener( 'selectstart', this._onXRControllerEvent );
+			controller.removeEventListener( 'selectend', this._onXRControllerEvent );
 
 		}
 
-		controller.addEventListener( 'move', onXRControllerEvent );
-		controller.addEventListener( 'select', onXRControllerEvent );
-		controller.addEventListener( 'selectstart', onXRControllerEvent );
-		controller.addEventListener( 'selectend', onXRControllerEvent );
+	}
+
+	disconnect() {
+
+		this.disconnectionPointerEvents();
+		this.disconnectXrControllerEvents();
+
+		this.camera = null;
+		this.element = null;
+
+		this.controllers = [];
 
 	}
 

+ 109 - 0
examples/jsm/libs/demuxer_mp4.js

@@ -0,0 +1,109 @@
+import MP4Box from 'https://cdn.jsdelivr.net/npm/mp4box@0.5.3/+esm';
+
+// From: https://w3c.github.io/webcodecs/samples/video-decode-display/
+
+// Wraps an MP4Box File as a WritableStream underlying sink.
+class MP4FileSink {
+  #setStatus = null;
+  #file = null;
+  #offset = 0;
+
+  constructor(file, setStatus) {
+    this.#file = file;
+    this.#setStatus = setStatus;
+  }
+
+  write(chunk) {
+    // MP4Box.js requires buffers to be ArrayBuffers, but we have a Uint8Array.
+    const buffer = new ArrayBuffer(chunk.byteLength);
+    new Uint8Array(buffer).set(chunk);
+
+    // Inform MP4Box where in the file this chunk is from.
+    buffer.fileStart = this.#offset;
+    this.#offset += buffer.byteLength;
+
+    // Append chunk.
+    this.#setStatus("fetch", (this.#offset / (1024 ** 2)).toFixed(1) + " MiB");
+    this.#file.appendBuffer(buffer);
+  }
+
+  close() {
+    this.#setStatus("fetch", "Done");
+    this.#file.flush();
+  }
+}
+
+// Demuxes the first video track of an MP4 file using MP4Box, calling
+// `onConfig()` and `onChunk()` with appropriate WebCodecs objects.
+export class MP4Demuxer {
+  #onConfig = null;
+  #onChunk = null;
+  #setStatus = null;
+  #file = null;
+
+  constructor(uri, {onConfig, onChunk, setStatus}) {
+    this.#onConfig = onConfig;
+    this.#onChunk = onChunk;
+    this.#setStatus = setStatus;
+
+    // Configure an MP4Box File for demuxing.
+    this.#file = MP4Box.createFile();
+    this.#file.onError = error => setStatus("demux", error);
+    this.#file.onReady = this.#onReady.bind(this);
+    this.#file.onSamples = this.#onSamples.bind(this);
+
+    // Fetch the file and pipe the data through.
+    const fileSink = new MP4FileSink(this.#file, setStatus);
+    fetch(uri).then(response => {
+      // highWaterMark should be large enough for smooth streaming, but lower is
+      // better for memory usage.
+      response.body.pipeTo(new WritableStream(fileSink, {highWaterMark: 2}));
+    });
+  }
+
+  // Get the appropriate `description` for a specific track. Assumes that the
+  // track is H.264, H.265, VP8, VP9, or AV1.
+  #description(track) {
+    const trak = this.#file.getTrackById(track.id);
+    for (const entry of trak.mdia.minf.stbl.stsd.entries) {
+      const box = entry.avcC || entry.hvcC || entry.vpcC || entry.av1C;
+      if (box) {
+        const stream = new MP4Box.DataStream(undefined, 0, MP4Box.DataStream.BIG_ENDIAN);
+        box.write(stream);
+        return new Uint8Array(stream.buffer, 8);  // Remove the box header.
+      }
+    }
+    throw new Error("avcC, hvcC, vpcC, or av1C box not found");
+  }
+
+  #onReady(info) {
+    this.#setStatus("demux", "Ready");
+    const track = info.videoTracks[0];
+
+    // Generate and emit an appropriate VideoDecoderConfig.
+    this.#onConfig({
+      // Browser doesn't support parsing full vp8 codec (eg: `vp08.00.41.08`),
+      // they only support `vp8`.
+      codec: track.codec.startsWith('vp08') ? 'vp8' : track.codec,
+      codedHeight: track.video.height,
+      codedWidth: track.video.width,
+      description: this.#description(track),
+    });
+
+    // Start demuxing.
+    this.#file.setExtractionOptions(track.id);
+    this.#file.start();
+  }
+
+  #onSamples(track_id, ref, samples) {
+    // Generate and emit an EncodedVideoChunk for each demuxed sample.
+    for (const sample of samples) {
+      this.#onChunk(new EncodedVideoChunk({
+        type: sample.is_sync ? "key" : "delta",
+        timestamp: 1e6 * sample.cts / sample.timescale,
+        duration: 1e6 * sample.duration / sample.timescale,
+        data: sample.data
+      }));
+    }
+  }
+}

+ 3 - 1
examples/jsm/loaders/GCodeLoader.js

@@ -14,11 +14,13 @@ import {
  * Gcode files are composed by commands used by machines to create objects.
  *
  * @class GCodeLoader
- * @param {Manager} manager Loading manager.
  */
 
 class GCodeLoader extends Loader {
 
+	/**
+	 * @param {Manager} manager Loading manager.
+	 */
 	constructor( manager ) {
 
 		super( manager );

+ 0 - 2
examples/jsm/loaders/GLTFLoader.js

@@ -600,8 +600,6 @@ class GLTFLightsExtension {
 		// here, because node-level parsing will only override position if explicitly specified.
 		lightNode.position.set( 0, 0, 0 );
 
-		lightNode.decay = 2;
-
 		assignExtrasToUserData( lightNode, lightDef );
 
 		if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;

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

@@ -20,7 +20,6 @@ import {
  * Loads geometry with uv and materials basic properties with texture support.
  *
  * @class TDSLoader
- * @constructor
  */
 
 class TDSLoader extends Loader {

+ 2 - 0
examples/jsm/materials/MeshGouraudMaterial.js

@@ -319,6 +319,8 @@ class MeshGouraudMaterial extends ShaderMaterial {
 
 		super();
 
+		console.warn( 'THREE.MeshGouraudMaterial: MeshGouraudMaterial has been deprecated and will be removed with r183. Use THREE.MeshLambertMaterial instead.' ); // @deprecated r173
+
 		this.isMeshGouraudMaterial = true;
 
 		this.type = 'MeshGouraudMaterial';

+ 6 - 7
examples/jsm/misc/GPUComputationRenderer.js

@@ -99,16 +99,15 @@ import { FullScreenQuad } from '../postprocessing/Pass.js';
  * // And compute each frame, before rendering to screen:
  * gpuCompute.doRenderTarget( myFilter1, myRenderTarget );
  * gpuCompute.doRenderTarget( myFilter2, outputRenderTarget );
- *
- *
- *
- * @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements.
- * @param {int} sizeY Computation problem size is always 2d: sizeX * sizeY elements.
- * @param {WebGLRenderer} renderer The renderer
-  */
+ */
 
 class GPUComputationRenderer {
 
+	/**
+	 * @param {Number} sizeX Computation problem size is always 2d: sizeX * sizeY elements.
+ 	 * @param {Number} sizeY Computation problem size is always 2d: sizeX * sizeY elements.
+ 	 * @param {WebGLRenderer} renderer The renderer
+	 */
 	constructor( sizeX, sizeY, renderer ) {
 
 		this.variables = [];

+ 4 - 3
examples/jsm/misc/ProgressiveLightMap.js

@@ -13,12 +13,13 @@ import { potpack } from '../libs/potpack.module.js';
  * This should begin accumulating lightmaps which apply to
  * your objects, so you can start jittering lighting to achieve
  * the texture-space effect you're looking for.
- *
- * @param {WebGLRenderer} renderer An instance of WebGLRenderer.
- * @param {number} res The side-long dimension of you total lightmap.
  */
 class ProgressiveLightMap {
 
+	/**
+	 * @param {WebGLRenderer} renderer An instance of WebGLRenderer.
+ 	 * @param {number} [res=1024] The side-long dimension of you total lightmap.
+	 */
 	constructor( renderer, res = 1024 ) {
 
 		this.renderer = renderer;

+ 4 - 3
examples/jsm/misc/ProgressiveLightMapGPU.js

@@ -15,12 +15,13 @@ import { potpack } from '../libs/potpack.module.js';
  * This should begin accumulating lightmaps which apply to
  * your objects, so you can start jittering lighting to achieve
  * the texture-space effect you're looking for.
- *
- * @param {WebGPURenderer} renderer An instance of WebGPURenderer.
- * @param {number} resolution The side-long dimension of you total lightmap.
  */
 class ProgressiveLightMap {
 
+	/**
+	 * @param {WebGPURenderer} renderer An instance of WebGPURenderer.
+	 * @param {number} [resolution=1024] The side-long dimension of you total lightmap.
+	 */
 	constructor( renderer, resolution = 1024 ) {
 
 		this.renderer = renderer;

+ 7 - 5
examples/jsm/misc/Volume.js

@@ -11,14 +11,16 @@ import { VolumeSlice } from '../misc/VolumeSlice.js';
  * For now it only handles 3 dimensional data.
  * See the webgl_loader_nrrd.html example and the loaderNRRD.js file to see how to use this class.
  * @class
- * @param   {number}        xLength         Width of the volume
- * @param   {number}        yLength         Length of the volume
- * @param   {number}        zLength         Depth of the volume
- * @param   {string}        type            The type of data (uint8, uint16, ...)
- * @param   {ArrayBuffer}   arrayBuffer     The buffer with volume data
  */
 class Volume {
 
+	/**
+	 * @param   {number}        xLength         Width of the volume
+	 * @param   {number}        yLength         Length of the volume
+	 * @param   {number}        zLength         Depth of the volume
+	 * @param   {string}        type            The type of data (uint8, uint16, ...)
+	 * @param   {ArrayBuffer}   arrayBuffer     The buffer with volume data
+	 */
 	constructor( xLength, yLength, zLength, type, arrayBuffer ) {
 
 		if ( xLength !== undefined ) {

+ 5 - 3
examples/jsm/misc/VolumeSlice.js

@@ -12,13 +12,15 @@ import {
 /**
  * This class has been made to hold a slice of a volume data
  * @class
- * @param   {Volume} volume    The associated volume
- * @param   {number}       [index=0] The index of the slice
- * @param   {string}       [axis='z']      For now only 'x', 'y' or 'z' but later it will change to a normal vector
  * @see Volume
  */
 class VolumeSlice {
 
+	/**
+ 	 * @param {Volume} volume     The associated volume
+ 	 * @param {number} [index=0]  The index of the slice
+ 	 * @param {string} [axis='z'] For now only 'x', 'y' or 'z' but later it will change to a normal vector
+	 */
 	constructor( volume, index, axis ) {
 
 		const slice = this;

+ 0 - 19
examples/jsm/objects/InstancedPoints.js

@@ -1,19 +0,0 @@
-import { Mesh, InstancedPointsNodeMaterial } from 'three/webgpu';
-
-import InstancedPointsGeometry from '../geometries/InstancedPointsGeometry.js';
-
-class InstancedPoints extends Mesh {
-
-	constructor( geometry = new InstancedPointsGeometry(), material = new InstancedPointsNodeMaterial() ) {
-
-		super( geometry, material );
-
-		this.isInstancedPoints = true;
-
-		this.type = 'InstancedPoints';
-
-	}
-
-}
-
-export default InstancedPoints;

+ 25 - 20
examples/jsm/objects/WaterMesh.js

@@ -2,10 +2,10 @@ import {
 	Color,
 	Mesh,
 	Vector3,
-	NodeMaterial
+	MeshLambertNodeMaterial
 } from 'three/webgpu';
 
-import { Fn, add, cameraPosition, div, normalize, positionWorld, sub, time, texture, vec2, vec3, vec4, max, dot, reflect, pow, length, float, uniform, reflector, mul, mix } from 'three/tsl';
+import { Fn, add, cameraPosition, div, normalize, positionWorld, sub, time, texture, vec2, vec3, max, dot, reflect, pow, length, float, uniform, reflector, mul, mix, diffuseColor } from 'three/tsl';
 
 /**
  * Work based on :
@@ -18,7 +18,7 @@ class WaterMesh extends Mesh {
 
 	constructor( geometry, options ) {
 
-		const material = new NodeMaterial();
+		const material = new MeshLambertNodeMaterial();
 
 		super( geometry, material );
 
@@ -26,7 +26,7 @@ class WaterMesh extends Mesh {
 
 		this.resolution = options.resolution !== undefined ? options.resolution : 0.5;
 
-		// uniforms
+		// Uniforms
 
 		this.waterNormals = texture( options.waterNormals );
 		this.alpha = uniform( options.alpha !== undefined ? options.alpha : 1.0 );
@@ -58,25 +58,32 @@ class WaterMesh extends Mesh {
 
 		} );
 
-		const fragmentNode = Fn( () => {
+		const noise = getNoise( positionWorld.xz.mul( this.size ) );
+		const surfaceNormal = normalize( noise.xzy.mul( 1.5, 1.0, 1.5 ) );
 
-			const noise = getNoise( positionWorld.xz.mul( this.size ) );
-			const surfaceNormal = normalize( noise.xzy.mul( 1.5, 1.0, 1.5 ) );
+		const worldToEye = cameraPosition.sub( positionWorld );
+		const eyeDirection = normalize( worldToEye );
 
-			const diffuseLight = vec3( 0 ).toVar();
-			const specularLight = vec3( 0 ).toVar();
+		const reflection = normalize( reflect( this.sunDirection.negate(), surfaceNormal ) );
+		const direction = max( 0.0, dot( eyeDirection, reflection ) );
+		const specularLight = pow( direction, 100 ).mul( this.sunColor ).mul( 2.0 );
+		const diffuseLight = max( dot( this.sunDirection, surfaceNormal ), 0.0 ).mul( this.sunColor ).mul( 0.5 );
 
-			const worldToEye = cameraPosition.sub( positionWorld );
-			const eyeDirection = normalize( worldToEye );
+		const distance = length( worldToEye );
 
-			const reflection = normalize( reflect( this.sunDirection.negate(), surfaceNormal ) );
-			const direction = max( 0.0, dot( eyeDirection, reflection ) );
-			specularLight.addAssign( pow( direction, 100 ).mul( this.sunColor ).mul( 2.0 ) );
-			diffuseLight.addAssign( max( dot( this.sunDirection, surfaceNormal ), 0.0 ).mul( this.sunColor ).mul( 0.5 ) );
+		const distortion = surfaceNormal.xz.mul( float( 0.001 ).add( float( 1.0 ).div( distance ) ) ).mul( this.distortionScale );
 
-			const distance = length( worldToEye );
+		// Material
 
-			const distortion = surfaceNormal.xz.mul( float( 0.001 ).add( float( 1.0 ).div( distance ) ) ).mul( this.distortionScale );
+		material.transparent = true;
+
+		material.opacityNode = this.alpha;
+
+		material.shadowPositionNode = positionWorld.add( distortion );
+
+		material.setupOutgoingLight = () => diffuseColor.rgb; // backwards compatibility
+
+		material.colorNode = Fn( () => {
 
 			const mirrorSampler = reflector();
 			mirrorSampler.uvNode = mirrorSampler.uvNode.add( distortion );
@@ -90,12 +97,10 @@ class WaterMesh extends Mesh {
 			const scatter = max( 0.0, dot( surfaceNormal, eyeDirection ) ).mul( this.waterColor );
 			const albedo = mix( this.sunColor.mul( diffuseLight ).mul( 0.3 ).add( scatter ), mirrorSampler.rgb.mul( specularLight ).add( mirrorSampler.rgb.mul( 0.9 ) ).add( vec3( 0.1 ) ), reflectance );
 
-			return vec4( albedo, this.alpha );
+			return albedo;
 
 		} )();
 
-		material.fragmentNode = fragmentNode;
-
 	}
 
 }

+ 7 - 7
examples/jsm/physics/JoltPhysics.js

@@ -39,7 +39,7 @@ const NUM_OBJECT_LAYERS = 2;
 
 function setupCollisionFiltering( settings ) {
 
-	let objectFilter = new Jolt.ObjectLayerPairFilterTable( NUM_OBJECT_LAYERS );
+	const objectFilter = new Jolt.ObjectLayerPairFilterTable( NUM_OBJECT_LAYERS );
 	objectFilter.EnableCollision( LAYER_NON_MOVING, LAYER_MOVING );
 	objectFilter.EnableCollision( LAYER_MOVING, LAYER_MOVING );
 
@@ -47,7 +47,7 @@ function setupCollisionFiltering( settings ) {
 	const BP_LAYER_MOVING = new Jolt.BroadPhaseLayer( 1 );
 	const NUM_BROAD_PHASE_LAYERS = 2;
 
-	let bpInterface = new Jolt.BroadPhaseLayerInterfaceTable( NUM_OBJECT_LAYERS, NUM_BROAD_PHASE_LAYERS );
+	const bpInterface = new Jolt.BroadPhaseLayerInterfaceTable( NUM_OBJECT_LAYERS, NUM_BROAD_PHASE_LAYERS );
 	bpInterface.MapObjectToBroadPhaseLayer( LAYER_NON_MOVING, BP_LAYER_NON_MOVING );
 	bpInterface.MapObjectToBroadPhaseLayer( LAYER_MOVING, BP_LAYER_MOVING );
 
@@ -55,7 +55,7 @@ function setupCollisionFiltering( settings ) {
 	settings.mBroadPhaseLayerInterface = bpInterface;
 	settings.mObjectVsBroadPhaseLayerFilter = new Jolt.ObjectVsBroadPhaseLayerFilterTable( settings.mBroadPhaseLayerInterface, NUM_BROAD_PHASE_LAYERS, settings.mObjectLayerPairFilter, NUM_OBJECT_LAYERS );
 
-};
+}
 
 async function JoltPhysics() {
 
@@ -111,8 +111,8 @@ async function JoltPhysics() {
 		if ( shape === null ) return;
 
 		const body = mesh.isInstancedMesh
-							? createInstancedBody( mesh, mass, restitution, shape )
-							: createBody( mesh.position, mesh.quaternion, mass, restitution, shape );
+			? createInstancedBody( mesh, mass, restitution, shape )
+			: createBody( mesh.position, mesh.quaternion, mass, restitution, shape );
 
 		if ( mass > 0 ) {
 
@@ -175,8 +175,8 @@ async function JoltPhysics() {
 
 			const physics = mesh.userData.physics;
 
-			let shape = body.GetShape();
-			let body2 = createBody( position, { x: 0, y: 0, z: 0, w: 1 }, physics.mass, physics.restitution, shape );
+			const shape = body.GetShape();
+			const body2 = createBody( position, { x: 0, y: 0, z: 0, w: 1 }, physics.mass, physics.restitution, shape );
 
 			bodies[ index ] = body2;
 

+ 4 - 4
examples/jsm/physics/RapierPhysics.js

@@ -43,8 +43,8 @@ function getShape( geometry ) {
 
 		// if the buffer is non-indexed, generate an index buffer
 		const indices = geometry.getIndex() === null
-							? Uint32Array.from( Array( parseInt( vertices.length / 3 ) ).keys() )
-							: geometry.getIndex().array;
+			? Uint32Array.from( Array( parseInt( vertices.length / 3 ) ).keys() )
+			: geometry.getIndex().array;
 
 		return RAPIER.ColliderDesc.trimesh( vertices, indices );
 
@@ -105,8 +105,8 @@ async function RapierPhysics() {
 		shape.setRestitution( restitution );
 
 		const body = mesh.isInstancedMesh
-							? createInstancedBody( mesh, mass, shape )
-							: createBody( mesh.position, mesh.quaternion, mass, shape );
+			? createInstancedBody( mesh, mass, shape )
+			: createBody( mesh.position, mesh.quaternion, mass, shape );
 
 		if ( mass > 0 ) {
 

+ 2 - 0
examples/jsm/postprocessing/OutputPass.js

@@ -8,6 +8,7 @@ import {
 	AgXToneMapping,
 	ACESFilmicToneMapping,
 	NeutralToneMapping,
+	CustomToneMapping,
 	SRGBTransfer
 } from 'three';
 import { Pass, FullScreenQuad } from './Pass.js';
@@ -63,6 +64,7 @@ class OutputPass extends Pass {
 			else if ( this._toneMapping === ACESFilmicToneMapping ) this.material.defines.ACES_FILMIC_TONE_MAPPING = '';
 			else if ( this._toneMapping === AgXToneMapping ) this.material.defines.AGX_TONE_MAPPING = '';
 			else if ( this._toneMapping === NeutralToneMapping ) this.material.defines.NEUTRAL_TONE_MAPPING = '';
+			else if ( this._toneMapping === CustomToneMapping ) this.material.defines.CUSTOM_TONE_MAPPING = '';
 
 			this.material.needsUpdate = true;
 

+ 5 - 1
examples/jsm/shaders/OutputShader.js

@@ -28,7 +28,7 @@ const OutputShader = {
 		}`,
 
 	fragmentShader: /* glsl */`
-	
+
 		precision highp float;
 
 		uniform sampler2D tDiffuse;
@@ -68,6 +68,10 @@ const OutputShader = {
 
 				gl_FragColor.rgb = NeutralToneMapping( gl_FragColor.rgb );
 
+			#elif defined( CUSTOM_TONE_MAPPING )
+
+				gl_FragColor.rgb = CustomToneMapping( gl_FragColor.rgb );
+
 			#endif
 
 			// color space

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

@@ -461,11 +461,11 @@ class GLSLDecoder {
 			const isHex = /^(0x)/.test( firstToken.str );
 
 			if ( isHex ) type = 'int';
-			else if ( /u$/.test( firstToken.str ) ) type = 'uint';
+			else if ( /u$|U$/.test( firstToken.str ) ) type = 'uint';
 			else if ( /f|e|\./.test( firstToken.str ) ) type = 'float';
 			else type = 'int';
 
-			let str = firstToken.str.replace( /u|i$/, '' );
+			let str = firstToken.str.replace( /u|U|i$/, '' );
 
 			if ( isHex === false ) {
 

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

@@ -255,6 +255,8 @@ class TSLEncoder {
 
 			code = node.expression.type + '( ' + node.type + ' ' + node.expression.value + ' )';
 
+			this.addImport( node.expression.type );
+
 		} else if ( node.isUnary ) {
 
 			let type = unaryLib[ node.type ];

+ 112 - 0
examples/misc_raycaster_helper.html

@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js misc - raycaster - helper</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - raycaster - helper<br/>
+			See external <a href="https://github.com/gsimone/things?tab=readme-ov-file#raycasterhelper" target="_blank" rel="noopener">RaycasterHelper</a> for more information.
+		</div>
+
+        <script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.webgpu.js",
+					"three/webgpu": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.tsl.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { RaycasterHelper } from 'https://cdn.jsdelivr.net/npm/@gsimone/three-raycaster-helper@0.1.0/dist/gsimone-three-raycaster-helper.esm.js';
+
+			let scene, renderer;
+			let camera, clock;
+
+			let capsule1, capsule2, capsule3;
+			let raycaster, raycasterHelper;
+
+			init();
+
+			function init() {
+
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 10;
+
+				scene = new THREE.Scene();
+				clock = new THREE.Clock();
+
+				//
+
+				const geometry = new THREE.CapsuleGeometry( 0.5, 0.5, 4, 32 );
+				const material = new THREE.MeshNormalMaterial();
+				material.side = THREE.DoubleSide;
+
+				capsule1 = new THREE.Mesh( geometry, material );
+				capsule1.position.x = - 2;
+				capsule2 = new THREE.Mesh( geometry, material );
+				capsule2.position.x = 0;
+				capsule3 = new THREE.Mesh( geometry, material );
+				capsule3.position.x = 2;
+
+				scene.add( capsule1 );
+				scene.add( capsule2 );
+				scene.add( capsule3 );
+
+				raycaster = new THREE.Raycaster( new THREE.Vector3( - 4, 0, 0 ), new THREE.Vector3( 1, 0, 0 ) );
+				raycaster.near = 1;
+				raycaster.far = 8;
+				raycasterHelper = new RaycasterHelper( raycaster );
+				scene.add( raycasterHelper );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				[ capsule1, capsule2, capsule3 ].forEach( capsule => {
+
+					capsule.position.y = Math.sin( clock.getElapsedTime() * 0.5 + capsule.position.x );
+					capsule.rotation.z = Math.sin( clock.getElapsedTime() * 0.5 ) * Math.PI * 1;
+
+				} );
+
+				raycasterHelper.hits = raycaster.intersectObjects( scene.children );
+				raycasterHelper.update();
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

BIN
examples/screenshots/misc_raycaster_helper.jpg


BIN
examples/screenshots/webgl_geometry_teapot.jpg


BIN
examples/screenshots/webgpu_camera.jpg


BIN
examples/screenshots/webgpu_camera_array.jpg


BIN
examples/screenshots/webgpu_clearcoat.jpg


BIN
examples/screenshots/webgpu_clipping.jpg


BIN
examples/screenshots/webgpu_instance_points.jpg


BIN
examples/screenshots/webgpu_lights_custom.jpg


BIN
examples/screenshots/webgpu_lights_ies_spotlight.jpg


BIN
examples/screenshots/webgpu_postprocessing_3dlut.jpg


BIN
examples/screenshots/webgpu_sprites.jpg


BIN
examples/screenshots/webgpu_struct_drawindirect.jpg


BIN
examples/screenshots/webgpu_tsl_coffee_smoke.jpg


BIN
examples/screenshots/webgpu_tsl_compute_attractors_particles.jpg


BIN
examples/screenshots/webgpu_tsl_vfx_tornado.jpg


BIN
examples/screenshots/webgpu_video_frame.jpg


BIN
examples/screenshots/webgpu_xr_cubes.jpg


BIN
examples/screenshots/webxr_vr_layers.jpg


+ 4 - 1
examples/tags.json

@@ -5,6 +5,7 @@
 	"misc_controls_orbit": [ "rotation" ],
 	"misc_controls_trackball": [ "rotation" ],
 	"misc_controls_transform": [ "scale", "rotate", "translate" ],
+	"misc_raycaster_helper": [ "external" ],
 	"physics_ammo_cloth": [ "integration" ],
 	"physics_jolt_instancing": [ "external" ],
 	"physics_rapier_instancing": [ "external" ],
@@ -122,6 +123,7 @@
 	"webgpu_compute_sort_bitonic": [ "gpgpu" ],
 	"webgpu_compute_texture": [ "gpgpu" ],
 	"webgpu_compute_texture_pingpong": [ "gpgpu" ],
+	"webgpu_compute_water": [ "gpgpu", "struct" ],
 	"webgpu_depth_texture": [ "renderTarget" ],
 	"webgpu_loader_gltf_dispersion": [ "transmission" ],
 	"webgpu_materials_lightmap": [ "shadow" ],
@@ -148,5 +150,6 @@
 	"webgpu_sky": [ "sun" ],
 	"webgpu_tonemapping": [ "gltf" ],
 	"webgpu_tsl_compute_attractors_particles": [ "gpgpu" ],
-	"webgpu_ocean": [ "water" ]
+	"webgpu_ocean": [ "water" ],
+	"webgpu_video_frame": [ "webcodecs" ]
 }

+ 3 - 3
examples/webgl_geometry_teapot.html

@@ -63,9 +63,9 @@
 				camera.position.set( - 600, 550, 1300 );
 
 				// LIGHTS
-				ambientLight = new THREE.AmbientLight( 0x7c7c7c, 3.0 );
+				ambientLight = new THREE.AmbientLight( 0x7c7c7c, 2.0 );
 
-				light = new THREE.DirectionalLight( 0xFFFFFF, 3.0 );
+				light = new THREE.DirectionalLight( 0xFFFFFF, 2.0 );
 				light.position.set( 0.32, 0.39, 0.7 );
 
 				// RENDERER
@@ -96,7 +96,7 @@
 				materials[ 'wireframe' ] = new THREE.MeshBasicMaterial( { wireframe: true } );
 				materials[ 'flat' ] = new THREE.MeshPhongMaterial( { specular: 0x000000, flatShading: true, side: THREE.DoubleSide } );
 				materials[ 'smooth' ] = new THREE.MeshLambertMaterial( { side: THREE.DoubleSide } );
-				materials[ 'glossy' ] = new THREE.MeshPhongMaterial( { side: THREE.DoubleSide } );
+				materials[ 'glossy' ] = new THREE.MeshPhongMaterial( { color: 0xc0c0c0, specular: 0x404040, shininess: 300, side: THREE.DoubleSide } );
 				materials[ 'textured' ] = new THREE.MeshPhongMaterial( { map: textureMap, side: THREE.DoubleSide } );
 				materials[ 'reflective' ] = new THREE.MeshPhongMaterial( { envMap: textureCube, side: THREE.DoubleSide } );
 

+ 2 - 2
examples/webgpu_animation_retargeting.html

@@ -85,7 +85,7 @@
 
 			const coloredVignette = screenUV.distance( .5 ).mix( hue( color( 0x0175ad ), time.mul( .1 ) ), hue( color( 0x02274f ), time.mul( .5 ) ) );
 			const lightSpeedEffect = lightSpeed( normalWorld ).clamp();
-			const lightSpeedSky = normalWorld.y.remapClamp( -.1, 1 ).mix( 0, lightSpeedEffect );
+			const lightSpeedSky = normalWorld.y.remapClamp( - .1, 1 ).mix( 0, lightSpeedEffect );
 			const composedBackground = blendDodge( coloredVignette, lightSpeedSky );
 
 			scene.backgroundNode = composedBackground;
@@ -164,7 +164,7 @@
 
 			function getSource( sourceModel ) {
 
-				const clip = sourceModel.animations[ 0 ]
+				const clip = sourceModel.animations[ 0 ];
 
 				const helper = new THREE.SkeletonHelper( sourceModel.scene );
 				helpers.add( helper );

+ 1 - 1
examples/webgpu_animation_retargeting_readyplayer.html

@@ -137,7 +137,7 @@
 
 			function getSource( sourceModel ) {
 
-				const clip = sourceModel.animations[ 0 ]
+				const clip = sourceModel.animations[ 0 ];
 
 				const helper = new THREE.SkeletonHelper( sourceModel );
 				const skeleton = new THREE.Skeleton( helper.bones );

+ 154 - 0
examples/webgpu_camera_array.html

@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - arraycamera</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../build/three.webgpu.js",
+					"three/webgpu": "../build/three.webgpu.js",
+					"three/tsl": "../build/three.tsl.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+
+			import Stats from 'three/addons/libs/stats.module.js';
+
+			let camera, scene, renderer;
+			let mesh;
+			let stats;
+
+			const AMOUNT = 6;
+
+			init();
+
+			function init() {
+
+				const subCameras = [];
+
+				for ( let i = 0; i < AMOUNT * AMOUNT; i ++ ) {
+
+					const subCamera = new THREE.PerspectiveCamera( 40, 1, 0.1, 10 );
+					subCamera.viewport = new THREE.Vector4();
+
+					subCameras.push( subCamera );
+
+				}
+
+				camera = new THREE.ArrayCamera( subCameras );
+				camera.position.z = 3;
+
+				updateCameras();
+
+				scene = new THREE.Scene();
+
+				scene.add( new THREE.AmbientLight( 0x999999 ) );
+
+				const light = new THREE.DirectionalLight( 0xffffff, 3 );
+				light.position.set( 0.5, 0.5, 1 );
+				light.castShadow = true;
+				light.shadow.bias = - 0.001;
+				light.shadow.camera.zoom = 4; // tighter shadow map
+				scene.add( light );
+
+				const geometryBackground = new THREE.PlaneGeometry( 100, 100 );
+				const materialBackground = new THREE.MeshPhongMaterial( { color: 0x000066 } );
+
+				const background = new THREE.Mesh( geometryBackground, materialBackground );
+				background.receiveShadow = true;
+				background.position.set( 0, 0, - 1 );
+				scene.add( background );
+
+				const geometryCylinder = new THREE.CylinderGeometry( 0.5, 0.5, 1, 32 );
+				const materialCylinder = new THREE.MeshPhongMaterial( { color: 0xff0000 } );
+
+				mesh = new THREE.Mesh( geometryCylinder, materialCylinder );
+				mesh.castShadow = true;
+				mesh.receiveShadow = true;
+				scene.add( mesh );
+
+				renderer = new THREE.WebGPURenderer( /*{ forceWebGL: true }*/ );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				renderer.shadowMap.enabled = true;
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				//
+
+				stats = new Stats();
+				document.body.appendChild( stats.dom );
+
+			}
+
+			function updateCameras() {
+
+				const ASPECT_RATIO = window.innerWidth / window.innerHeight;
+				const WIDTH = window.innerWidth / AMOUNT;
+				const HEIGHT = window.innerHeight / AMOUNT;
+
+				camera.aspect = ASPECT_RATIO;
+				camera.updateProjectionMatrix();
+
+				for ( let y = 0; y < AMOUNT; y ++ ) {
+
+					for ( let x = 0; x < AMOUNT; x ++ ) {
+
+						const subcamera = camera.cameras[ AMOUNT * y + x ];
+						subcamera.copy( camera ); // copy fov, aspect ratio, near, far from the root camera
+
+						subcamera.viewport.set( Math.floor( x * WIDTH ), Math.floor( y * HEIGHT ), Math.ceil( WIDTH ), Math.ceil( HEIGHT ) );
+						subcamera.updateProjectionMatrix();
+
+						subcamera.position.x = ( x / AMOUNT ) - 0.5;
+						subcamera.position.y = 0.5 - ( y / AMOUNT );
+						subcamera.position.z = 1.5 + ( ( x + y ) * .5 );
+						subcamera.position.multiplyScalar( 2 );
+
+						subcamera.lookAt( 0, 0, 0 );
+						subcamera.updateMatrixWorld();
+
+					}
+
+				}
+
+			}
+
+			function onWindowResize() {
+
+				updateCameras();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				mesh.rotation.x += 0.005;
+				mesh.rotation.z += 0.01;
+
+				renderer.render( scene, camera );
+
+				stats.update();
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 41 - 24
examples/webgpu_compute_birds.html

@@ -28,7 +28,8 @@
 					"three": "../build/three.webgpu.js",
 					"three/webgpu": "../build/three.webgpu.js",
 					"three/tsl": "../build/three.tsl.js",
-					"three/addons/": "./jsm/"
+					"three/addons/": "./jsm/",
+					"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@3.6.0/dist/main.js"
 				}
 			}
 		</script>
@@ -40,7 +41,7 @@
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
-			import Stats from 'three/addons/libs/stats.module.js';
+			import Stats from 'stats-gl';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 			let container, stats;
@@ -168,7 +169,7 @@
 
 				//
 
-				renderer = new THREE.WebGPURenderer( { antialias: true } );
+				renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: false } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
@@ -310,30 +311,29 @@
 					// Destructure uniforms
 					const { alignment, separation, cohesion, deltaTime, rayOrigin, rayDirection } = effectController;
 
-					const zoneRadius = separation.add( alignment ).add( cohesion );
-					const separationThresh = separation.div( zoneRadius );
-					const alignmentThresh = ( separation.add( alignment ) ).div( zoneRadius );
-					const zoneRadiusSq = zoneRadius.mul( zoneRadius );
+					const zoneRadius = separation.add( alignment ).add( cohesion ).toConst();
+					const separationThresh = separation.div( zoneRadius ).toConst();
+					const alignmentThresh = ( separation.add( alignment ) ).div( zoneRadius ).toConst();
+					const zoneRadiusSq = zoneRadius.mul( zoneRadius ).toConst();
 
-					const position = positionStorage.element( instanceIndex );
-					const velocity = velocityStorage.element( instanceIndex );
-
-					// Add influence of pointer position to velocity.
-					const directionToRay = rayOrigin.sub( position );
-					const projectionLength = dot( directionToRay, rayDirection );
-
-					const closestPoint = rayOrigin.sub( rayDirection.mul( projectionLength ) );
+					// Cache current bird's position and velocity outside the loop
+					const birdIndex = instanceIndex.toConst( 'birdIndex' );
+					const position = positionStorage.element( birdIndex ).toVar();
+					const velocity = velocityStorage.element( birdIndex ).toVar();
 
-					const directionToClosestPoint = closestPoint.sub( position );
-					const distanceToClosestPoint = length( directionToClosestPoint );
-					const distanceToClosestPointSq = distanceToClosestPoint.mul( distanceToClosestPoint );
+					// Add influence of pointer position to velocity using cached position
+					const directionToRay = rayOrigin.sub( position ).toConst();
+					const projectionLength = dot( directionToRay, rayDirection ).toConst();
+					const closestPoint = rayOrigin.sub( rayDirection.mul( projectionLength ) ).toConst();
+					const directionToClosestPoint = closestPoint.sub( position ).toConst();
+					const distanceToClosestPoint = length( directionToClosestPoint ).toConst();
+					const distanceToClosestPointSq = distanceToClosestPoint.mul( distanceToClosestPoint ).toConst();
 
-					const rayRadius = float( 150.0 );
-					const rayRadiusSq = rayRadius.mul( rayRadius );
+					const rayRadius = float( 150.0 ).toConst();
+					const rayRadiusSq = rayRadius.mul( rayRadius ).toConst();
 
 					If( distanceToClosestPointSq.lessThan( rayRadiusSq ), () => {
 
-						// Scale bird velocity inversely with distance from prey radius center.
 						const velocityAdjust = ( distanceToClosestPointSq.div( rayRadiusSq ).sub( 1.0 ) ).mul( deltaTime ).mul( 100.0 );
 						velocity.addAssign( normalize( directionToClosestPoint ).mul( velocityAdjust ) );
 						limit.addAssign( 5.0 );
@@ -347,11 +347,18 @@
 
 					Loop( { start: uint( 0 ), end: uint( BIRDS ), type: 'uint', condition: '<' }, ( { i } ) => {
 
+						If( i.equal( birdIndex ), () => {
+
+							Continue();
+			
+						} );
+
+						// Cache bird's position and velocity
+
 						const birdPosition = positionStorage.element( i );
 						const dirToBird = birdPosition.sub( position );
 						const distToBird = length( dirToBird );
 
-						// Don't apply any changes to velocity if the distance to this bird is negligible.
 						If( distToBird.lessThan( 0.0001 ), () => {
 
 							Continue();
@@ -413,8 +420,10 @@
 
 					} );
 
-				} )().compute( BIRDS );
+					// Write back the final velocity to storage
+					velocityStorage.element( birdIndex ).assign( velocity );
 
+				} )().compute( BIRDS );
 				computePosition = Fn( () => {
 
 					const { deltaTime } = effectController;
@@ -430,7 +439,13 @@
 
 				scene.add( birdMesh );
 
-				stats = new Stats();
+				stats = new Stats( {
+					precision: 3,
+					horizontal: false,
+					trackGPU: true,
+					trackCPT: true
+				} );
+				stats.init( renderer );
 				container.appendChild( stats.dom );
 
 				container.style.touchAction = 'none';
@@ -468,6 +483,7 @@
 			function animate() {
 
 				render();
+				renderer.resolveTimestampsAsync();
 				stats.update();
 
 			}
@@ -489,6 +505,7 @@
 
 				renderer.compute( computeVelocity );
 				renderer.compute( computePosition );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
 				renderer.render( scene, camera );
 
 				// Move pointer away so we only affect birds when moving the mouse

+ 3 - 0
examples/webgpu_compute_particles.html

@@ -227,6 +227,7 @@
 				// events
 
 				renderer.domElement.addEventListener( 'pointermove', onMove );
+
 				//
 
 				controls = new OrbitControls( camera, renderer.domElement );
@@ -266,8 +267,10 @@
 				stats.update();
 
 				await renderer.computeAsync( computeParticles );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
 
 				await renderer.renderAsync( scene, camera );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.RENDER );
 
 				// throttle the logging
 

+ 10 - 6
examples/webgpu_compute_particles_snow.html

@@ -14,11 +14,11 @@
 		<script type="importmap">
 			{
 				"imports": {
-					"three": "../build/three.webgpu.js",
-					"three/webgpu": "../build/three.webgpu.js",
+					"three": "../src/Three.WebGPU.js",
+					"three/webgpu": "../src/Three.WebGPU.js",
 					"three/tsl": "../build/three.tsl.js",
 					"three/addons/": "./jsm/",
-					"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@2.2.8/dist/main.js"
+					"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@3.6.0/dist/main.js"
 				}
 			}
 		</script>
@@ -273,7 +273,9 @@
 
 				stats = new Stats( {
 					precision: 3,
-					horizontal: false
+					horizontal: false,
+					trackGPU: true,
+					trackCPT: true
 				} );
 				stats.init( renderer );
 				document.body.appendChild( stats.dom );
@@ -343,11 +345,12 @@
 
 				scene.overrideMaterial = collisionPosMaterial;
 				renderer.setRenderTarget( collisionPosRT );
-				await renderer.renderAsync( scene, collisionCamera );
+				renderer.render( scene, collisionCamera );
 
 				// compute
 
-				await renderer.computeAsync( computeParticles );
+				renderer.compute( computeParticles );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
 
 				// result
 
@@ -356,6 +359,7 @@
 
 				await postProcessing.renderAsync();
 
+				renderer.resolveTimestampsAsync();
 				stats.update();
 
 			}

+ 29 - 2
examples/webgpu_compute_points.html

@@ -17,7 +17,8 @@
 					"three": "../build/three.webgpu.js",
 					"three/webgpu": "../build/three.webgpu.js",
 					"three/tsl": "../build/three.tsl.js",
-					"three/addons/": "./jsm/"
+					"three/addons/": "./jsm/",
+					"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@3.6.0/dist/main.js"
 				}
 			}
 		</script>
@@ -25,11 +26,15 @@
 		<script type="module">
 
 			import * as THREE from 'three';
+
+			import Stats from 'stats-gl';
+
+
 			import { Fn, uniform, instancedArray, float, vec2, vec3, color, instanceIndex } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
-			let camera, scene, renderer;
+			let camera, scene, renderer, stats;
 			let computeNode;
 
 			const pointerVector = new THREE.Vector2( - 10.0, - 10.0 ); // Out of bounds first
@@ -120,6 +125,19 @@
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 
+				stats = new Stats( {
+					precision: 4,
+					horizontal: false,
+					trackGPU: true,
+					trackCPT: true,
+					logsPerSecond: 10,
+					graphsPerSecond: 60,
+					samplesGraph: 30,
+				} );
+				stats.init( renderer );
+				document.body.appendChild( stats.dom );
+				stats.dom.style.position = 'absolute';
+
 				window.addEventListener( 'resize', onWindowResize );
 				window.addEventListener( 'mousemove', onMouseMove );
 
@@ -158,8 +176,17 @@
 			function animate() {
 
 				renderer.compute( computeNode );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
+
 				renderer.render( scene, camera );
 
+
+				renderer.resolveTimestampsAsync().then( () => {
+
+					stats.update();
+
+				} );
+
 			}
 
 		</script>

+ 14 - 0
examples/webgpu_compute_sort_bitonic.html

@@ -57,6 +57,8 @@
 			import * as THREE from 'three';
 			import { storage, If, vec3, not, uniform, uv, uint, float, Fn, vec2, abs, int, invocationLocalIndex, workgroupArray, uvec2, floor, instanceIndex, workgroupBarrier, atomicAdd, atomicStore, workgroupId } from 'three/tsl';
 
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
+
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 			const StepType = {
@@ -120,6 +122,14 @@
 			
 			} );
 
+			if ( WebGPU.isAvailable() === false ) {
+
+				document.body.appendChild( WebGPU.getErrorMessage() );
+
+				throw new Error( 'No WebGPU support' );
+
+			}
+
 			// Allow Workgroup Array Swaps
 			init();
 
@@ -515,11 +525,15 @@
 			
 					}
 
+					renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
+
+
 					const algo = new Uint32Array( await renderer.getArrayBufferAsync( nextAlgoBuffer ) );
 					algo > StepType.DISPERSE_LOCAL ? ( nextStepGlobal = true ) : ( nextStepGlobal = false );
 					const totalSwaps = new Uint32Array( await renderer.getArrayBufferAsync( counterBuffer ) );
 			
 					renderer.render( scene, camera );
+					renderer.resolveTimestampsAsync( THREE.TimestampQuery.RENDER );
 
 					timestamps[ forceGlobalSwap ? 'global_swap' : 'local_swap' ].innerHTML = `
 

+ 18 - 13
examples/webgpu_compute_water.html

@@ -28,7 +28,7 @@
 
 			import * as THREE from 'three';
 
-			import { color, instanceIndex, If, varyingProperty, uint, int, negate, floor, float, length, clamp, vec2, cos, vec3, vertexIndex, Fn, uniform, instancedArray, min, max, positionLocal, transformNormalToView } from 'three/tsl';
+			import { color, instanceIndex, struct, If, varyingProperty, uint, int, negate, floor, float, length, clamp, vec2, cos, vec3, vertexIndex, Fn, uniform, instancedArray, min, max, positionLocal, transformNormalToView } from 'three/tsl';
 			import { SimplexNoise } from 'three/addons/math/SimplexNoise.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import Stats from 'three/addons/libs/stats.module.js';
@@ -279,30 +279,35 @@
 				const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xFFFF00 } );
 			
 				// Initialize sphere mesh instance position and velocity.
-				const spherePositionArray = new Float32Array( NUM_SPHERES * 3 );
-			
+				// position<vec3> + velocity<vec2> + unused<vec3> = 8 floats per sphere.
+				// for structs arrays must be enclosed in multiple of 4
+
+				const sphereStride = 8;
+				const sphereArray = new Float32Array( NUM_SPHERES * sphereStride );
+
 				// Only hold velocity in x and z directions.
 				// The sphere is wedded to the surface of the water, and will only move vertically with the water.
-				const sphereVelocityArray = new Float32Array( NUM_SPHERES * 2 );
 
 				for ( let i = 0; i < NUM_SPHERES; i ++ ) {
 
-					spherePositionArray[ i * 3 + 0 ] = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
-					spherePositionArray[ i * 3 + 1 ] = 0;
-					spherePositionArray[ i * 3 + 2 ] = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
+					sphereArray[ i * sphereStride + 0 ] = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
+					sphereArray[ i * sphereStride + 1 ] = 0;
+					sphereArray[ i * sphereStride + 2 ] = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
 
 				}
 
-				sphereVelocityArray.fill( 0.0 );
+				const SphereStruct = struct( {
+					position: 'vec3',
+					velocity: 'vec2'
+				} );
 
 				// Sphere Instance Storage
-				const sphereInstancePositionStorage = instancedArray( spherePositionArray, 'vec3' ).label( 'SpherePosition' );
-				const sphereVelocityStorage = instancedArray( sphereVelocityArray, 'vec2' ).label( 'SphereVelocity' );
+				const sphereVelocityStorage = instancedArray( sphereArray, SphereStruct ).label( 'SphereData' );
 
 				computeSphere = Fn( () => {
 
-					const instancePosition = sphereInstancePositionStorage.element( instanceIndex );
-					const velocity = sphereVelocityStorage.element( instanceIndex );
+					const instancePosition = sphereVelocityStorage.element( instanceIndex ).get( 'position' );
+					const velocity = sphereVelocityStorage.element( instanceIndex ).get( 'velocity' );
 
 					// Bring position from range of [ -BOUNDS/2, BOUNDS/2 ] to [ 0, BOUNDS ]
 					const tempX = instancePosition.x.add( BOUNDS_HALF );
@@ -372,7 +377,7 @@
 
 				sphereMaterial.positionNode = Fn( () => {
 
-					const instancePosition = sphereInstancePositionStorage.element( instanceIndex );
+					const instancePosition = sphereVelocityStorage.element( instanceIndex ).get( 'position' );
 
 					const newPosition = positionLocal.add( instancePosition );
 

+ 39 - 35
examples/webgpu_instance_points.html

@@ -27,16 +27,13 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { color, storage, Fn, instanceIndex, sin, time, float, uniform, attribute, mix, vec3 } from 'three/tsl';
+			import { color, storage, Fn, instancedBufferAttribute, instanceIndex, sin, time, float, uniform, shapeCircle, mix, vec3 } from 'three/tsl';
 
 			import Stats from 'three/addons/libs/stats.module.js';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
-			import InstancedPoints from 'three/addons/objects/InstancedPoints.js';
-			import InstancedPointsGeometry from 'three/addons/geometries/InstancedPointsGeometry.js';
-
 			import * as GeometryUtils from 'three/addons/utils/GeometryUtils.js';
 
 			let renderer, scene, camera, camera2, controls, backgroundNode;
@@ -54,13 +51,7 @@
 
 			init();
 
-			function init() {
-
-				renderer = new THREE.WebGPURenderer( { antialias: true } );
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( window.innerWidth, window.innerHeight );
-				renderer.setAnimationLoop( animate );
-				document.body.appendChild( renderer.domElement );
+			async function init() {
 
 				scene = new THREE.Scene();
 
@@ -70,19 +61,14 @@
 				camera2 = new THREE.PerspectiveCamera( 40, 1, 1, 1000 );
 				camera2.position.copy( camera.position );
 
-				controls = new OrbitControls( camera, renderer.domElement );
-				controls.enableDamping = true;
-				controls.minDistance = 10;
-				controls.maxDistance = 500;
-
 				backgroundNode = color( 0x222222 );
 
 				effectController = {
 
 					pulseSpeed: uniform( 6 ),
 					minWidth: uniform( 6 ),
-					maxWidth: uniform( 12 ),
-					alphaToCoverage: true,
+					maxWidth: uniform( 20 ),
+					alphaToCoverage: true
 
 				};
 
@@ -115,12 +101,10 @@
 
 				// Instanced Points
 
-				const geometry = new InstancedPointsGeometry();
-				geometry.setPositions( positions );
-				geometry.setColors( colors );
+				const positionAttribute = new THREE.InstancedBufferAttribute( new Float32Array( positions ), 3 );
+				const colorsAttribute = new THREE.InstancedBufferAttribute( new Float32Array( colors ), 3 );
 
 				const instanceSizeBufferAttribute = new THREE.StorageInstancedBufferAttribute( sizes, 1 );
-				geometry.setAttribute( 'instanceSize', instanceSizeBufferAttribute );
 				const instanceSizeStorage = storage( instanceSizeBufferAttribute, 'float', instanceSizeBufferAttribute.count );
 
 				computeSize = Fn( () => {
@@ -135,29 +119,48 @@
 
 				} )().compute( divisions );
 			
-				geometry.instanceCount = positions.length / 3; // this should not be necessary
+				// Material / Sprites
+
+				const attributeRange = instancedBufferAttribute( instanceSizeBufferAttribute );
+				const pointColors = mix( vec3( 0.0 ), instancedBufferAttribute( colorsAttribute ), attributeRange.div( float( effectController.maxWidth ) ) );
 
-				material = new THREE.InstancedPointsNodeMaterial( {
+				material = new THREE.PointsNodeMaterial( {
 
-					color: 0xffffff,
-					pointWidth: 10, // in pixel units
+					colorNode: pointColors,
+					opacityNode: shapeCircle(),
+					positionNode: instancedBufferAttribute( positionAttribute ),
+					// rotationNode: time,
+					sizeNode: instancedBufferAttribute( instanceSizeBufferAttribute ),
+					// size: 40, // in pixels units
 					vertexColors: true,
-					alphaToCoverage: true,
+					sizeAttenuation: false,
+					alphaToCoverage: true
 
 				} );
 
-				const attributeRange = attribute( 'instanceSize' ).sub( 1 );
+				const instancedPoints = new THREE.Sprite( material );
+				instancedPoints.count = divisions;
+				scene.add( instancedPoints );
 
-				material.pointWidthNode = attribute( 'instanceSize' );
-				material.pointColorNode = mix( vec3( 0.0 ), attribute( 'instanceColor' ), attributeRange.div( float( effectController.maxWidth.sub( 1 ) ) ) );
+				// Renderer / Controls
 
-				const instancedPoints = new InstancedPoints( geometry, material );
-				instancedPoints.scale.set( 1, 1, 1 );
-				scene.add( instancedPoints );
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setAnimationLoop( animate );
+				//renderer.logarithmicDepthBuffer = true;
+				document.body.appendChild( renderer.domElement );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.enableDamping = true;
+				controls.minDistance = 10;
+				controls.maxDistance = 500;
 
 				window.addEventListener( 'resize', onWindowResize );
 				onWindowResize();
 
+				// GUI
+
 				stats = new Stats();
 				document.body.appendChild( stats.dom );
 
@@ -169,8 +172,8 @@
 
 				} );
 
-				gui.add( effectController.minWidth, 'value', 1, 20, 1 ).name( 'minWidth' );
-				gui.add( effectController.maxWidth, 'value', 2, 20, 1 ).name( 'maxWidth' );
+				gui.add( effectController.minWidth, 'value', 1, 30, 1 ).name( 'minWidth' );
+				gui.add( effectController.maxWidth, 'value', 2, 30, 1 ).name( 'maxWidth' );
 				gui.add( effectController.pulseSpeed, 'value', 1, 20, 0.1 ).name( 'pulseSpeed' );
 
 			}
@@ -195,6 +198,7 @@
 				stats.update();
 
 				// compute
+
 				renderer.compute( computeSize );
 
 				// main scene

+ 64 - 7
examples/webgpu_lights_ies_spotlight.html

@@ -31,6 +31,8 @@
 
 			import { IESLoader } from 'three/addons/loaders/IESLoader.js';
 
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+
 			let renderer, scene, camera;
 			let lights;
 
@@ -53,42 +55,58 @@
 				//
 
 				const spotLight = new THREE.IESSpotLight( 0xff0000, 500 );
-				spotLight.position.set( 6.5, 1.5, 6.5 );
+				spotLight.position.set( 6.5, 3, 6.5 );
 				spotLight.angle = Math.PI / 8;
 				spotLight.penumbra = 0.7;
 				spotLight.distance = 20;
+				spotLight.castShadow = true;
 				spotLight.iesMap = iesTexture1;
+				spotLight.userData.helper = new THREE.SpotLightHelper( spotLight );
 				scene.add( spotLight );
+				scene.add( spotLight.target );
+				scene.add( spotLight.userData.helper );
 
 				//
 
 				const spotLight2 = new THREE.IESSpotLight( 0x00ff00, 500 );
-				spotLight2.position.set( 6.5, 1.5, - 6.5 );
+				spotLight2.position.set( - 6.5, 3, 6.5 );
 				spotLight2.angle = Math.PI / 8;
 				spotLight2.penumbra = 0.7;
 				spotLight2.distance = 20;
+				spotLight2.castShadow = true;
 				spotLight2.iesMap = iesTexture2;
+				spotLight2.userData.helper = new THREE.SpotLightHelper( spotLight2 );
 				scene.add( spotLight2 );
+				scene.add( spotLight2.target );
+				scene.add( spotLight2.userData.helper );
 
 				//
 
 				const spotLight3 = new THREE.IESSpotLight( 0x0000ff, 500 );
-				spotLight3.position.set( - 6.5, 1.5, - 6.5 );
+				spotLight3.position.set( - 6.5, 3, - 6.5 );
 				spotLight3.angle = Math.PI / 8;
 				spotLight3.penumbra = 0.7;
 				spotLight3.distance = 20;
+				spotLight3.castShadow = true;
 				spotLight3.iesMap = iesTexture3;
+				spotLight3.userData.helper = new THREE.SpotLightHelper( spotLight3 );
 				scene.add( spotLight3 );
+				scene.add( spotLight3.target );
+				scene.add( spotLight3.userData.helper );
 
 				//
 
 				const spotLight4 = new THREE.IESSpotLight( 0xffffff, 500 );
-				spotLight4.position.set( - 6.5, 1.5, 6.5 );
+				spotLight4.position.set( 6.5, 3, - 6.5 );
 				spotLight4.angle = Math.PI / 8;
 				spotLight4.penumbra = 0.7;
 				spotLight4.distance = 20;
+				spotLight4.castShadow = true;
 				spotLight4.iesMap = iesTexture4;
+				spotLight4.userData.helper = new THREE.SpotLightHelper( spotLight4 );
 				scene.add( spotLight4 );
+				scene.add( spotLight4.target );
+				scene.add( spotLight4.userData.helper );
 
 				//
 
@@ -96,20 +114,30 @@
 
 				//
 
-				const material = new THREE.MeshPhongMaterial( { color: 0x808080/*, dithering: true*/ } );
+				const material = new THREE.MeshPhongMaterial( { color: 0x999999/*, dithering: true*/ } );
 
 				const geometry = new THREE.PlaneGeometry( 200, 200 );
 
 				const mesh = new THREE.Mesh( geometry, material );
 				mesh.rotation.x = - Math.PI * 0.5;
+				mesh.receiveShadow = true;
 				scene.add( mesh );
 
+				const geometry2 = new THREE.BoxGeometry( 2, 2, 2 );
+				//const geometry2 = new THREE.IcosahedronGeometry( 1, 5 );
+
+				const mesh2 = new THREE.Mesh( geometry2, material );
+				mesh2.position.y = 1;
+				mesh2.castShadow = true;
+				scene.add( mesh2 );
+
 				//
 
 				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( render );
+				renderer.shadowMap.enabled = true;
 				document.body.appendChild( renderer.domElement );
 
 				camera = new THREE.PerspectiveCamera( 35, window.innerWidth / window.innerHeight, .1, 100 );
@@ -122,6 +150,28 @@
 
 				//
 
+				function setHelperVisible( value ) {
+
+					for ( let i = 0; i < lights.length; i ++ ) {
+
+						lights[ i ].userData.helper.visible = value;
+
+					}
+
+				}
+
+				setHelperVisible( false );
+
+				//
+
+				const gui = new GUI();
+
+				gui.add( { helper: false }, 'helper' ).onChange( ( v ) => setHelperVisible( v ) );
+
+				gui.open();
+
+				//
+
 				window.addEventListener( 'resize', onWindowResize );
 
 			}
@@ -137,11 +187,18 @@
 
 			function render( time ) {
 
-				time = ( time / 1000 ) * 2.0;
+				time = time / 1000;
 
 				for ( let i = 0; i < lights.length; i ++ ) {
 
-					lights[ i ].position.y = Math.sin( time + i ) + 0.97;
+					const t = ( Math.sin( ( time + i ) * ( Math.PI / 2 ) ) + 1 ) / 2;
+
+					const x = THREE.MathUtils.lerp( lights[ i ].position.x, 0, t );
+					const z = THREE.MathUtils.lerp( lights[ i ].position.z, 0, t );
+
+					lights[ i ].target.position.x = x;
+					lights[ i ].target.position.z = z;
+					if ( lights[ i ].userData.helper ) lights[ i ].userData.helper.update();
 
 				}
 

+ 9 - 0
examples/webgpu_materials.html

@@ -31,6 +31,7 @@
 			import { Fn, wgslFn, positionLocal, scriptable, positionWorld, normalLocal, normalWorld, normalView, color, texture, uv, float, vec2, vec3, vec4, oscSine, triplanarTexture, screenUV, js, string, Loop, cameraProjectionMatrix, ScriptableNodeResources } from 'three/tsl';
 
 			import { TeapotGeometry } from 'three/addons/geometries/TeapotGeometry.js';
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 
 			import Stats from 'three/addons/libs/stats.module.js';
 
@@ -44,6 +45,14 @@
 
 			function init() {
 
+				if ( WebGPU.isAvailable() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw new Error( 'No WebGPU support' );
+
+				}
+
 				const container = document.createElement( 'div' );
 				document.body.appendChild( container );
 

+ 1 - 1
examples/webgpu_multisampled_renderbuffers.html

@@ -71,7 +71,7 @@
 
 				const gui = new GUI();
 				gui.add( params, 'samples', 0, 4 ).step( 1 );
-				gui.add( params, 'animated', true );
+				gui.add( params, 'animated' );
 
 			}
 

+ 1 - 6
examples/webgpu_occlusion.html

@@ -92,7 +92,7 @@
 				const plane = new THREE.Mesh( planeGeometry, new THREE.MeshPhongNodeMaterial( { color: 0x00ff00, side: THREE.DoubleSide } ) );
 				const sphere = new THREE.Mesh( sphereGeometry, new THREE.MeshPhongNodeMaterial( { color: 0xffff00 } ) );
 
-				const instanceUniform = nodeObject( new OcclusionNode( sphere, new THREE.Color( 0x00ff00 ), new THREE.Color( 0x0000ff ) ) );
+				const instanceUniform = nodeObject( new OcclusionNode( sphere, new THREE.Color( 0x0000ff ), new THREE.Color( 0x00ff00 ) ) );
 
 				plane.material.colorNode = instanceUniform;
 
@@ -107,11 +107,6 @@
 				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
-
-				// ensure shaders/pipelines are all complete before rendering
-
-				await renderer.compileAsync( scene, camera );
-
 				renderer.setAnimationLoop( render );
 				document.body.appendChild( renderer.domElement );
 

+ 9 - 0
examples/webgpu_particles.html

@@ -28,6 +28,7 @@
 			import { range, texture, mix, uv, color, rotateUV, positionLocal, time, uniform } from 'three/tsl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
@@ -38,6 +39,14 @@
 
 			function init() {
 
+				if ( WebGPU.isAvailable() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw new Error( 'No WebGPU support' );
+
+				}
+
 				const { innerWidth, innerHeight } = window;
 
 				camera = new THREE.PerspectiveCamera( 60, innerWidth / innerHeight, 1, 5000 );

+ 15 - 6
examples/webgpu_performance.html

@@ -20,7 +20,9 @@
 				"imports": {
 					"three": "../build/three.webgpu.js",
 					"three/webgpu": "../build/three.webgpu.js",
-					"three/addons/": "./jsm/"
+					"three/addons/": "./jsm/",
+					"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@3.6.0/dist/main.js"
+
 				}
 			}
 		</script>
@@ -29,7 +31,7 @@
 
 			import * as THREE from 'three';
 
-			import Stats from 'three/addons/libs/stats.module.js';
+			import Stats from 'stats-gl';
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
@@ -71,7 +73,7 @@
 				scene = new THREE.Scene();
 
 
-				renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: false } );
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.toneMapping = THREE.ACESFilmicToneMapping;
@@ -82,7 +84,13 @@
 
 				//
 
-				stats = new Stats();
+				stats = new Stats( {
+					precision: 3,
+					horizontal: false,
+					trackGPU: true,
+				} );
+				stats.init( renderer );
+
 				document.body.appendChild( stats.dom );
 
 				new RGBELoader()
@@ -149,9 +157,10 @@
 
 			//
 
-			function render() {
+			async function render() {
 
-				renderer.renderAsync( scene, camera );
+				await renderer.renderAsync( scene, camera );
+				renderer.resolveTimestampsAsync( THREE.TimestampQuery.RENDER );
 
 				stats.update();
 

+ 7 - 4
examples/webgpu_performance_renderbundle.html

@@ -29,10 +29,10 @@
 		{
 			"imports": {
 				"three": "../build/three.webgpu.js",
-					"three/webgpu": "../build/three.webgpu.js",
+				"three/webgpu": "../build/three.webgpu.js",
 				"three/tsl": "../build/three.tsl.js",
 				"three/addons/": "./jsm/",
-				"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@2.2.7/dist/main.js"
+				"stats-gl": "https://cdn.jsdelivr.net/npm/stats-gl@3.6.0/dist/main.js"
 			}
 		}
 	</script>
@@ -229,9 +229,10 @@
 
 			stats = new Stats( {
 				precision: 3,
-				horizontal: false
+				horizontal: false,
+				trackGPU: true,
 			} );
-			// stats.init( renderer );
+			stats.init( renderer );
 			document.body.appendChild( stats.dom );
 			stats.dom.style.position = 'absolute';
 
@@ -288,6 +289,8 @@
 				if ( renderTimeAverages.length > 60 ) renderTimeAverages.shift();
 		
 				const average = renderTimeAverages.reduce( ( a, b ) => a + b, 0 ) / renderTimeAverages.length;
+
+				renderer.resolveTimestampsAsync();
 				stats.update();
 
 				document.getElementById( 'backend' ).innerText = `Average Render Time ${api.renderBundle ? '(Bundle)' : ''}: ` + average.toFixed( 2 ) + 'ms';

+ 111 - 45
examples/webgpu_postprocessing_3dlut.html

@@ -6,13 +6,12 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<link type="text/css" rel="stylesheet" href="main.css">
 	</head>
-
 	<body>
+
 		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - 3D LUTs<br />
-			Battle Damaged Sci-fi Helmet by
-			<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
-			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> from <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a><br />
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js webgpu</a> - post processing - 3D LUTs<br />
+			Based on <a href="https://threejs-journey.com/lessons/coffee-smoke-shader" target="_blank" rel="noopener">Three.js Journey</a> lesson<br />
+			Perlin noise texture from <a href="http://kitfox.com/projects/perlinNoiseMaker/" target="_blank" rel="noopener">Perlin Noise Maker</a>, 
 			LUTs from <a href="https://www.rocketstock.com/free-after-effects-templates/35-free-luts-for-color-grading-videos/">RocketStock</a>, <a href="https://www.freepresets.com/product/free-luts-cinematic/">FreePresets.com</a>
 		</div>
 
@@ -30,12 +29,11 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { pass, texture3D, uniform, renderOutput } from 'three/tsl';
+			import { mix, mul, oneMinus, positionLocal, smoothstep, texture, time, rotateUV, Fn, uv, vec2, vec3, vec4, pass, texture3D, uniform, renderOutput } from 'three/tsl';
 			import { lut3D } from 'three/addons/tsl/display/Lut3DNode.js';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
-			import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
 			import { LUTCubeLoader } from 'three/addons/loaders/LUTCubeLoader.js';
 			import { LUT3dlLoader } from 'three/addons/loaders/LUT3dlLoader.js';
 			import { LUTImageLoader } from 'three/addons/loaders/LUTImageLoader.js';
@@ -58,41 +56,23 @@
 				'NightLUT': null
 			};
 
-			let gui;
-			let camera, scene, renderer;
-			let postProcessing, lutPass;
+			let camera, scene, renderer, postProcessing, controls, lutPass;
 
 			init();
 
 			async function init() {
 
-				const container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
-				camera.position.set( - 1.8, 0.6, 2.7 );
+				camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 0.1, 100 );
+				camera.position.set( 8, 10, 12 );
 
 				scene = new THREE.Scene();
 
-				new RGBELoader()
-					.setPath( 'textures/equirectangular/' )
-					.load( 'royal_esplanade_1k.hdr', function ( texture ) {
-
-						texture.mapping = THREE.EquirectangularReflectionMapping;
-
-						scene.background = texture;
-						scene.environment = texture;
-
-						// model
+				// Loaders
 
-						const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
-						loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
+				const gltfLoader = new GLTFLoader();
+				const textureLoader = new THREE.TextureLoader();
 
-							scene.add( gltf.scene );
-
-						} );
-
-					} );
+				// LUTs
 
 				const lutCubeLoader = new LUTCubeLoader();
 				const lutImageLoader = new LUTImageLoader();
@@ -125,12 +105,97 @@
 
 				}
 
-				renderer = new THREE.WebGPURenderer();
+				// baked model
+
+				gltfLoader.load(
+					'./models/gltf/coffeeMug.glb',
+					( gltf ) => {
+
+        				gltf.scene.getObjectByName( 'baked' ).material.map.anisotropy = 8;
+						scene.add( gltf.scene );
+
+					}
+				);
+
+				// geometry
+
+				const smokeGeometry = new THREE.PlaneGeometry( 1, 1, 16, 64 );
+				smokeGeometry.translate( 0, 0.5, 0 );
+				smokeGeometry.scale( 1.5, 6, 1.5 );
+
+				// texture
+
+				const noiseTexture = textureLoader.load( './textures/noises/perlin/128x128.png' );
+				noiseTexture.wrapS = THREE.RepeatWrapping;
+				noiseTexture.wrapT = THREE.RepeatWrapping;
+
+				// material
+
+				const smokeMaterial = new THREE.MeshBasicNodeMaterial( { transparent: true, side: THREE.DoubleSide, depthWrite: false } );
+
+				// position
+
+				smokeMaterial.positionNode = Fn( () => {
+
+					// twist
+
+					const twistNoiseUv = vec2( 0.5, uv().y.mul( 0.2 ).sub( time.mul( 0.005 ) ).mod( 1 ) );
+					const twist = texture( noiseTexture, twistNoiseUv ).r.mul( 10 );
+					positionLocal.xz.assign( rotateUV( positionLocal.xz, twist, vec2( 0 ) ) );
+
+					// wind
+
+					const windOffset = vec2(
+						texture( noiseTexture, vec2( 0.25, time.mul( 0.01 ) ).mod( 1 ) ).r.sub( 0.5 ),
+						texture( noiseTexture, vec2( 0.75, time.mul( 0.01 ) ).mod( 1 ) ).r.sub( 0.5 ),
+					).mul( uv().y.pow( 2 ).mul( 10 ) );
+					positionLocal.addAssign( windOffset );
+
+					return positionLocal;
+
+				} )();
+
+				// color
+
+				smokeMaterial.colorNode = Fn( () => {
+
+					// alpha
+
+					const alphaNoiseUv = uv().mul( vec2( 0.5, 0.3 ) ).add( vec2( 0, time.mul( 0.03 ).negate() ) );
+					const alpha = mul(
+
+						// pattern
+						texture( noiseTexture, alphaNoiseUv ).r.smoothstep( 0.4, 1 ),
+
+						// edges fade
+						smoothstep( 0, 0.1, uv().x ),
+						smoothstep( 0, 0.1, oneMinus( uv().x ) ),
+						smoothstep( 0, 0.1, uv().y ),
+						smoothstep( 0, 0.1, oneMinus( uv().y ) )
+
+					);
+
+					// color
+
+					const finalColor = mix( vec3( 0.6, 0.3, 0.2 ), vec3( 1, 1, 1 ), alpha.pow( 3 ) );
+
+					return vec4( finalColor, alpha );
+
+				} )();
+
+				// mesh
+
+				const smoke = new THREE.Mesh( smokeGeometry, smokeMaterial );
+				smoke.position.y = 1.83;
+				scene.add( smoke );
+
+				// renderer
+
+				renderer = new THREE.WebGPURenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );
-				renderer.toneMapping = THREE.ACESFilmicToneMapping;
-				container.appendChild( renderer.domElement );
+				document.body.appendChild( renderer.domElement );
 
 				// post processing
 
@@ -151,15 +216,17 @@
 
 				postProcessing.outputNode = lutPass;
 
-				//
+				// controls
 
-				const controls = new OrbitControls( camera, renderer.domElement );
-				controls.minDistance = 2;
-				controls.maxDistance = 10;
-				controls.target.set( 0, 0, - 0.2 );
-				controls.update();
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.enableDamping = true;
+				controls.minDistance = 0.1;
+				controls.maxDistance = 50;
+				controls.target.y = 3;
+
+				// gui
 
-				gui = new GUI();
+				const gui = new GUI();
 				gui.add( params, 'lut', Object.keys( lutMap ) );
 				gui.add( params, 'intensity' ).min( 0 ).max( 1 );
 
@@ -176,9 +243,9 @@
 
 			}
 
-			//
+			async function animate() {
 
-			function animate() {
+				controls.update();
 
 				lutPass.intensityNode.value = params.intensity;
 
@@ -195,6 +262,5 @@
 			}
 
 		</script>
-
 	</body>
 </html>

+ 1 - 1
examples/webgpu_postprocessing_traa.html

@@ -38,7 +38,7 @@
 
 			function init() {
 
-				renderer = new THREE.WebGPURenderer( { forceWebGL: true } );
+				renderer = new THREE.WebGPURenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( animate );

+ 5 - 2
examples/webgpu_storage_buffer.html

@@ -53,7 +53,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { storageObject, If, vec3, uv, uint, float, Fn, instanceIndex, workgroupBarrier } from 'three/tsl';
+			import { storage, If, vec3, uv, uint, float, Fn, instanceIndex, workgroupBarrier } from 'three/tsl';
 
 			const timestamps = {
 				webgpu: document.getElementById( 'timestamps' ),
@@ -90,7 +90,7 @@
 
 					const arrayBuffer = new THREE.StorageInstancedBufferAttribute( new Float32Array( array ), typeSize );
 
-					arrayBufferNodes.push( storageObject( arrayBuffer, type[ i ], size ) );
+					arrayBufferNodes.push( storage( arrayBuffer, type[ i ], size ).setPBO( true ) );
 
 				}
 
@@ -218,6 +218,9 @@
 					await renderer.computeAsync( compute );
 					await renderer.renderAsync( scene, camera );
 
+					renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
+					renderer.resolveTimestampsAsync( THREE.TimestampQuery.RENDER );
+
 					timestamps[ forceWebGL ? 'webgl' : 'webgpu' ].innerHTML = `
 
 							Compute ${renderer.info.compute.frameCalls} pass in ${renderer.info.compute.timestamp.toFixed( 6 )}ms<br>

+ 268 - 0
examples/webgpu_struct_drawindirect.html

@@ -0,0 +1,268 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - struct drawIndirect</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgpu - struct drawIndirect<br />
+		</div>
+
+		<script type="importmap">
+			{
+				"imports": {
+					"three": "../src/three.webgpu.js",
+					"three/webgpu": "../src/three.webgpu.js",
+					"three/tsl": "../src/three.tsl.js",
+					"three/addons/": "./jsm/"
+				}
+			}
+		</script>
+
+		<script type="module">
+
+			import * as THREE from 'three';
+			import { struct, storage, wgslFn, instanceIndex, time, varyingProperty, attribute } from 'three/tsl';
+
+			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+			import WebGPU from 'three/addons/capabilities/WebGPU.js';
+
+			if ( WebGPU.isAvailable() === false ) {
+
+				document.body.appendChild( WebGPU.getErrorMessage() );
+
+				throw new Error( 'No WebGPU support' );
+
+			}
+
+
+			const renderer = new THREE.WebGPURenderer( { antialias: true } );
+			renderer.outputColorSpace = THREE.SRGBColorSpace;
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			renderer.setClearColor( 0x000000 );
+			renderer.setClearAlpha( 0 );
+			document.body.appendChild( renderer.domElement );
+
+			const aspect = window.innerWidth / window.innerHeight;
+
+			const camera = new THREE.PerspectiveCamera( 50.0, aspect, 0.1, 10000 );
+			const scene = new THREE.Scene();
+
+			scene.background = new THREE.Color( 0x00001f );
+			camera.position.set( 1, 1, 1 );
+			const controls = new OrbitControls( camera, renderer.domElement );
+
+			let computeDrawBuffer, computeInitDrawBuffer;
+
+			init();
+
+			async function init() {
+
+				await renderer.init();
+
+				// geometry
+
+				const vector = new THREE.Vector4();
+
+				const instances = 100000;
+
+				const positions = [];
+				const offsets = [];
+				const colors = [];
+				const orientationsStart = [];
+				const orientationsEnd = [];
+
+				positions.push( 0.025, - 0.025, 0 );
+				positions.push( - 0.025, 0.025, 0 );
+				positions.push( 0, 0, 0.025 );
+
+				// instanced attributes
+
+				for ( let i = 0; i < instances; i ++ ) {
+
+					// offsets
+
+					offsets.push( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
+
+					// colors
+
+					colors.push( Math.random(), Math.random(), Math.random(), Math.random() );
+
+					// orientation start
+
+					vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+					vector.normalize();
+
+					orientationsStart.push( vector.x, vector.y, vector.z, vector.w );
+
+					// orientation end
+
+					vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
+					vector.normalize();
+
+					orientationsEnd.push( vector.x, vector.y, vector.z, vector.w );
+
+				}
+
+				const geometry = new THREE.InstancedBufferGeometry();
+				geometry.instanceCount = instances;
+
+				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+				geometry.setAttribute( 'offset', new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 ) );
+				geometry.setAttribute( 'color', new THREE.InstancedBufferAttribute( new Float32Array( colors ), 4 ) );
+				geometry.setAttribute( 'orientationStart', new THREE.InstancedBufferAttribute( new Float32Array( orientationsStart ), 4 ) );
+				geometry.setAttribute( 'orientationEnd', new THREE.InstancedBufferAttribute( new Float32Array( orientationsEnd ), 4 ) );
+
+				const drawBuffer = new THREE.IndirectStorageBufferAttribute( new Uint32Array( 5 ), 5 );
+				geometry.setIndirect( drawBuffer );
+
+				const drawBufferStruct = struct( {
+					vertexCount: 'uint',
+					instanceCount: { type: 'uint', atomic: true },
+					firstVertex: 'uint',
+					firstInstance: 'uint',
+					offset: 'uint'
+				}, 'DrawBuffer' );
+
+				const writeDrawBuffer = wgslFn( `
+					fn compute(            
+						index: u32,
+						drawBuffer: ptr<storage, DrawBuffer, read_write>,
+						instances: f32,
+						time: f32,
+					) -> void {
+
+						let instanceCount = max( instances * pow( sin( time * 0.5 ) + 1, 4.0 ), 100 );
+
+						atomicStore( &drawBuffer.instanceCount, u32( instanceCount ) );
+					}
+				` );
+
+				computeDrawBuffer = writeDrawBuffer( {
+					drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ),
+					instances: instances,
+					index: instanceIndex,
+					time: time
+				} ).compute( instances ); // not neccessary in this case but normally one wants to run through all instances
+
+				const initDrawBuffer = wgslFn( `
+					fn compute(            
+						drawBuffer: ptr< storage, DrawBuffer, read_write >,
+					) -> void {
+
+						drawBuffer.vertexCount = 3u;
+						atomicStore(&drawBuffer.instanceCount, 0u);
+						drawBuffer.firstVertex = 0u;
+						drawBuffer.firstInstance = 0u;
+						drawBuffer.offset = 0u;
+					}
+				` );
+
+				computeInitDrawBuffer = initDrawBuffer( {
+					drawBuffer: storage( drawBuffer, drawBufferStruct, drawBuffer.count ),
+  				} ).compute( 1 );
+
+  				const vPosition = varyingProperty( 'vec3', 'vPosition' );
+  				const vColor = varyingProperty( 'vec4', 'vColor' );
+
+  				const positionShaderParams = {
+					position: attribute( 'position' ),
+					offset: attribute( 'offset' ),
+					color: attribute( 'color' ),
+					orientationStart: attribute( 'orientationStart' ),
+					orientationEnd: attribute( 'orientationEnd' ),
+					time: time
+				};
+
+				const positionShader = wgslFn( `
+					fn main_vertex(
+						position: vec3<f32>,
+						offset: vec3<f32>,
+						color: vec4<f32>,
+						orientationStart: vec4<f32>,
+						orientationEnd: vec4<f32>,
+						time: f32
+					) -> vec4<f32> {
+
+						var vPosition = offset * max( abs( sin( time * 0.5 ) * 2.0 + 1.0 ), 0.5 ) + position;
+						var orientation = normalize( mix( orientationStart, orientationEnd, sin( time * 0.5 ) ) );
+						var vcV = cross( orientation.xyz, vPosition );
+						vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
+
+						var vColor = color;
+
+						var outPosition = vec4f(vPosition, 1);
+
+						varyings.vPosition = vPosition;
+						varyings.vColor = vColor;
+
+						return outPosition;
+					}
+				`, [ vPosition, vColor ] );
+
+				const fragmentShaderParams = {
+					time: time,
+					vPosition: vPosition,
+					vColor: vColor
+				};
+
+				const fragmentShader = wgslFn( `
+					fn main_fragment(
+						time: f32,
+						vPosition: vec3<f32>,
+						vColor: vec4<f32>
+					) -> vec4<f32> {
+
+						var color = vec4f( vColor );
+						color.r += sin( vPosition.x * 10.0 + time ) * 0.5;
+
+						return color;
+					}
+				` );
+
+				const material = new THREE.MeshBasicNodeMaterial( {
+					side: THREE.DoubleSide,
+					forceSinglePass: true,
+					transparent: true
+				} );
+
+				material.positionNode = positionShader( positionShaderParams );
+				material.fragmentNode = fragmentShader( fragmentShaderParams );
+
+				const mesh = new THREE.Mesh( geometry, material );
+				scene.add( mesh );
+
+				renderer.setAnimationLoop( render );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function render() {
+
+				controls.update();
+
+				renderer.render( scene, camera );
+
+				renderer.compute( computeInitDrawBuffer );
+				renderer.compute( computeDrawBuffer );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 1
examples/webgpu_texturegrad.html

@@ -90,7 +90,7 @@
 				const box = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material );
 				scene.add( box );
 
-				const renderer = new THREE.WebGPURenderer( { antialias: false, forceWebGL: forceWebGL, trackTimestamp: true } );
+				const renderer = new THREE.WebGPURenderer( { antialias: false, forceWebGL: forceWebGL } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth / 2, window.innerHeight );
 

Некоторые файлы не были показаны из-за большого количества измененных файлов

粤ICP备19079148号