Browse Source

Merge remote-tracking branch 'mrdoob-origin/dev' into pmrem-fix

# Conflicts:
#	src/renderers/WebGLRenderer.js
Garrett Johnson 4 years ago
parent
commit
4ed35ebdb1
46 changed files with 1074 additions and 220 deletions
  1. 36 33
      build/three.js
  2. 0 0
      build/three.min.js
  3. 52 49
      build/three.module.js
  4. 2 2
      docs/api/en/helpers/AxesHelper.html
  5. 1 1
      docs/api/en/textures/DataTexture.html
  6. 2 2
      docs/api/zh/helpers/AxesHelper.html
  7. 174 0
      docs/examples/en/math/OBB.html
  8. 174 0
      docs/examples/zh/math/OBB.html
  9. 4 2
      docs/list.json
  10. 10 10
      examples/index.html
  11. 1 1
      examples/js/exporters/GLTFExporter.js
  12. 98 0
      examples/js/utils/GeometryUtils.js
  13. 1 1
      examples/jsm/exporters/GLTFExporter.js
  14. 21 5
      examples/jsm/loaders/3DMLoader.js
  15. 0 1
      examples/jsm/loaders/ColladaLoader.d.ts
  16. 18 0
      examples/jsm/loaders/GLTFLoader.d.ts
  17. 6 13
      examples/jsm/loaders/LottieLoader.js
  18. 14 16
      examples/jsm/modifiers/CurveModifier.js
  19. 11 9
      examples/jsm/postprocessing/SSAOPass.d.ts
  20. 1 0
      examples/jsm/utils/GeometryUtils.d.ts
  21. 98 0
      examples/jsm/utils/GeometryUtils.js
  22. BIN
      examples/screenshots/webgl_framebuffer_texture.jpg
  23. 2 2
      examples/tags.json
  24. 43 14
      examples/webgl_framebuffer_texture.html
  25. 5 0
      package.json
  26. 4 4
      src/Three.Legacy.js
  27. 2 2
      src/animation/KeyframeTrack.js
  28. 2 2
      src/animation/PropertyBinding.js
  29. 2 2
      src/constants.js
  30. 2 2
      src/core/Object3D.js
  31. 1 1
      src/geometries/ExtrudeBufferGeometry.js
  32. 4 3
      src/loaders/CompressedTextureLoader.js
  33. 2 2
      src/materials/Material.js
  34. 2 0
      src/math/Matrix3.js
  35. 2 2
      src/renderers/WebGLRenderer.js
  36. 3 3
      src/renderers/webgl/WebGLBufferRenderer.d.ts
  37. 8 8
      src/renderers/webgl/WebGLProgram.js
  38. 14 14
      src/renderers/webgl/WebGLPrograms.js
  39. 2 2
      src/renderers/webgl/WebGLShadowMap.d.ts
  40. 3 3
      src/renderers/webgl/WebGLShadowMap.js
  41. 3 3
      src/renderers/webgl/WebGLTextures.js
  42. 2 2
      src/renderers/webxr/WebXRController.js
  43. 1 1
      src/textures/Texture.js
  44. 1 1
      test/unit/README.md
  45. 81 2
      test/unit/example/exporters/GLTFExporter.tests.js
  46. 159 0
      test/unit/src/core/Object3D.tests.js

+ 36 - 33
build/three.js

@@ -267,8 +267,8 @@
 	var StaticCopyUsage = 35046;
 	var DynamicCopyUsage = 35050;
 	var StreamCopyUsage = 35042;
-	var GLSL1 = "100";
-	var GLSL3 = "300 es";
+	var GLSL1 = '100';
+	var GLSL3 = '300 es';
 
 	/**
 	 * https://github.com/mrdoob/eventdispatcher.js/
@@ -1043,6 +1043,7 @@
 			var c = Math.cos(rotation);
 			var s = Math.sin(rotation);
 			this.set(sx * c, sx * s, -sx * (c * cx + s * cy) + cx + tx, -sy * s, sy * c, -sy * (-s * cx + c * cy) + cy + ty, 0, 0, 1);
+			return this;
 		};
 
 		_proto.scale = function scale(sx, sy) {
@@ -1423,7 +1424,7 @@
 			return uv;
 		}
 	});
-	Object.defineProperty(Texture.prototype, "needsUpdate", {
+	Object.defineProperty(Texture.prototype, 'needsUpdate', {
 		set: function set(value) {
 			if (value === true) this.version++;
 		}
@@ -5332,7 +5333,7 @@
 			}
 
 			if (object === this) {
-				console.error("THREE.Object3D.add: object can't be added as a child of itself.", object);
+				console.error('THREE.Object3D.add: object can\'t be added as a child of itself.', object);
 				return this;
 			}
 
@@ -5345,7 +5346,7 @@
 				this.children.push(object);
 				object.dispatchEvent(_addedEvent);
 			} else {
-				console.error("THREE.Object3D.add: object not an instance of THREE.Object3D.", object);
+				console.error('THREE.Object3D.add: object not an instance of THREE.Object3D.', object);
 			}
 
 			return this;
@@ -6865,7 +6866,7 @@
 				var newValue = values[key];
 
 				if (newValue === undefined) {
-					console.warn("THREE.Material: '" + key + "' parameter is undefined.");
+					console.warn('THREE.Material: \'' + key + '\' parameter is undefined.');
 					continue;
 				} // for backward compatability if shading is set in the constructor
 
@@ -6879,7 +6880,7 @@
 				var currentValue = this[key];
 
 				if (currentValue === undefined) {
-					console.warn("THREE." + this.type + ": '" + key + "' is not a property of this material.");
+					console.warn('THREE.' + this.type + ': \'' + key + '\' is not a property of this material.');
 					continue;
 				}
 
@@ -13228,14 +13229,14 @@
 
 
 	function generatePrecision(parameters) {
-		var precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;";
+		var precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;';
 
-		if (parameters.precision === "highp") {
-			precisionstring += "\n#define HIGH_PRECISION";
-		} else if (parameters.precision === "mediump") {
-			precisionstring += "\n#define MEDIUM_PRECISION";
-		} else if (parameters.precision === "lowp") {
-			precisionstring += "\n#define LOW_PRECISION";
+		if (parameters.precision === 'highp') {
+			precisionstring += '\n#define HIGH_PRECISION';
+		} else if (parameters.precision === 'mediump') {
+			precisionstring += '\n#define MEDIUM_PRECISION';
+		} else if (parameters.precision === 'lowp') {
+			precisionstring += '\n#define LOW_PRECISION';
 		}
 
 		return precisionstring;
@@ -13326,7 +13327,7 @@
 		var customDefines = generateDefines(defines);
 		var program = gl.createProgram();
 		var prefixVertex, prefixFragment;
-		var versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + "\n" : '';
+		var versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
 
 		if (parameters.isRawShaderMaterial) {
 			prefixVertex = [customDefines].filter(filterEmptyLine).join('\n');
@@ -13487,7 +13488,7 @@
 			ShadowMaterial: 'shadow',
 			SpriteMaterial: 'sprite'
 		};
-		var parameterNames = ["precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", "instancingColor", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV", "lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap", "roughnessMap", "metalnessMap", "gradientMap", "alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2", "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", "maxBones", "useVertexTexture", "morphTargets", "morphNormals", "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", "numDirLightShadows", "numPointLightShadows", "numSpotLightShadows", "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering", "sheen", "transmissionMap"];
+		var parameterNames = ['precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor', 'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV', 'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap', 'roughnessMap', 'metalnessMap', 'gradientMap', 'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2', 'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning', 'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals', 'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha', 'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights', 'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows', 'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights', 'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering', 'sheen', 'transmissionMap'];
 
 		function getMaxBones(object) {
 			var skeleton = object.skeleton;
@@ -13523,7 +13524,7 @@
 			} else if (map.isTexture) {
 				encoding = map.encoding;
 			} else if (map.isWebGLRenderTarget) {
-				console.warn("THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead.");
+				console.warn('THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.');
 				encoding = map.texture.encoding;
 			}
 
@@ -14536,7 +14537,7 @@
 		var shadowMaterialHorizonal = shadowMaterialVertical.clone();
 		shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1;
 		var fullScreenTri = new BufferGeometry();
-		fullScreenTri.setAttribute("position", new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3));
+		fullScreenTri.setAttribute('position', new BufferAttribute(new Float32Array([-1, -1, 0.5, 3, -1, 0.5, -1, 3, 0.5]), 3));
 		var fullScreenMesh = new Mesh(fullScreenTri, shadowMaterialVertical);
 		var scope = this;
 		this.enabled = false;
@@ -14606,7 +14607,7 @@
 						format: RGBAFormat
 					};
 					shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars);
-					shadow.map.texture.name = light.name + ".shadowMap";
+					shadow.map.texture.name = light.name + '.shadowMap';
 					shadow.mapPass = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, pars);
 					shadow.camera.updateProjectionMatrix();
 				}
@@ -14618,7 +14619,7 @@
 						format: RGBAFormat
 					};
 					shadow.map = new WebGLRenderTarget(_shadowMapSize.x, _shadowMapSize.y, _pars);
-					shadow.map.texture.name = light.name + ".shadowMap";
+					shadow.map.texture.name = light.name + '.shadowMap';
 					shadow.camera.updateProjectionMatrix();
 				}
 
@@ -15467,7 +15468,7 @@
 		var useOffscreenCanvas = false;
 
 		try {
-			useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && new OffscreenCanvas(1, 1).getContext("2d") !== null;
+			useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' && new OffscreenCanvas(1, 1).getContext('2d') !== null;
 		} catch (err) {// Ignore any errors
 		}
 
@@ -16274,7 +16275,7 @@
 		function safeSetTexture2D(texture, slot) {
 			if (texture && texture.isWebGLRenderTarget) {
 				if (warnedTexture2D === false) {
-					console.warn("THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead.");
+					console.warn('THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.');
 					warnedTexture2D = true;
 				}
 
@@ -16287,7 +16288,7 @@
 		function safeSetTextureCube(texture, slot) {
 			if (texture && texture.isWebGLCubeRenderTarget) {
 				if (warnedTextureCube === false) {
-					console.warn("THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead.");
+					console.warn('THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.');
 					warnedTextureCube = true;
 				}
 
@@ -16585,14 +16586,14 @@
 							if (hand.inputState.pinching && distance > distanceToPinch + threshold) {
 								hand.inputState.pinching = false;
 								this.dispatchEvent({
-									type: "pinchend",
+									type: 'pinchend',
 									handedness: inputSource.handedness,
 									target: this
 								});
 							} else if (!hand.inputState.pinching && distance <= distanceToPinch - threshold) {
 								hand.inputState.pinching = true;
 								this.dispatchEvent({
-									type: "pinchstart",
+									type: 'pinchstart',
 									handedness: inputSource.handedness,
 									target: this
 								});
@@ -22923,7 +22924,7 @@
 				}
 
 				function scalePt2(pt, vec, size) {
-					if (!vec) console.error("THREE.ExtrudeGeometry: vec does not exist");
+					if (!vec) console.error('THREE.ExtrudeGeometry: vec does not exist');
 					return vec.clone().multiplyScalar(size).add(pt);
 				}
 
@@ -26413,7 +26414,7 @@
 			}
 
 			if (factoryMethod === undefined) {
-				var message = "unsupported interpolation for " + this.ValueTypeName + " keyframe track named " + this.name;
+				var message = 'unsupported interpolation for ' + this.ValueTypeName + ' keyframe track named ' + this.name;
 
 				if (this.createInterpolant === undefined) {
 					// fall back to default, unless the default itself is messed up
@@ -27474,7 +27475,6 @@
 			var scope = this;
 			var images = [];
 			var texture = new CompressedTexture();
-			texture.image = images;
 			var loader = new FileLoader(this.manager);
 			loader.setPath(this.path);
 			loader.setResponseType('arraybuffer');
@@ -27495,6 +27495,7 @@
 
 					if (loaded === 6) {
 						if (texDatas.mipmapCount === 1) texture.minFilter = LinearFilter;
+						texture.image = images;
 						texture.format = texDatas.format;
 						texture.needsUpdate = true;
 						if (onLoad) onLoad(texture);
@@ -27526,6 +27527,8 @@
 								images[f].height = texDatas.height;
 							}
 						}
+
+						texture.image = images;
 					} else {
 						texture.image.width = texDatas.width;
 						texture.image.height = texDatas.height;
@@ -32470,7 +32473,7 @@
 			return results;
 		},
 		findNode: function findNode(root, nodeName) {
-			if (!nodeName || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) {
+			if (!nodeName || nodeName === '' || nodeName === '.' || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) {
 				return root;
 			} // search into skeleton bones.
 
@@ -32706,7 +32709,7 @@
 
 			if (propertyIndex !== undefined) {
 				// access a sub element of the property array (only primitives are supported right now)
-				if (propertyName === "morphTargetInfluences") {
+				if (propertyName === 'morphTargetInfluences') {
 					// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
 					// support resolving morphTarget names into indices.
 					if (!targetObject.geometry) {
@@ -36608,7 +36611,7 @@
 	});
 	Object.assign(Matrix3.prototype, {
 		flattenToArrayOffset: function flattenToArrayOffset(array, offset) {
-			console.warn("THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.");
+			console.warn('THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.');
 			return this.toArray(array, offset);
 		},
 		multiplyVector3: function multiplyVector3(vector) {
@@ -36640,7 +36643,7 @@
 			return this.copyPosition(m);
 		},
 		flattenToArrayOffset: function flattenToArrayOffset(array, offset) {
-			console.warn("THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.");
+			console.warn('THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.');
 			return this.toArray(array, offset);
 		},
 		getPosition: function getPosition() {
@@ -36946,7 +36949,7 @@
 	}); //
 
 	PerspectiveCamera.prototype.setLens = function (focalLength, filmGauge) {
-		console.warn("THREE.PerspectiveCamera.setLens is deprecated. " + "Use .setFocalLength and .filmGauge for a photographic setup.");
+		console.warn('THREE.PerspectiveCamera.setLens is deprecated. ' + 'Use .setFocalLength and .filmGauge for a photographic setup.');
 		if (filmGauge !== undefined) this.filmGauge = filmGauge;
 		this.setFocalLength(focalLength);
 	}; //

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


+ 52 - 49
build/three.module.js

@@ -197,8 +197,8 @@ const StaticCopyUsage = 35046;
 const DynamicCopyUsage = 35050;
 const StreamCopyUsage = 35042;
 
-const GLSL1 = "100";
-const GLSL3 = "300 es";
+const GLSL1 = '100';
+const GLSL3 = '300 es';
 
 /**
  * https://github.com/mrdoob/eventdispatcher.js/
@@ -1216,6 +1216,8 @@ class Matrix3 {
 			0, 0, 1
 		);
 
+		return this;
+
 	}
 
 	scale( sx, sy ) {
@@ -1683,7 +1685,7 @@ Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
 
 } );
 
-Object.defineProperty( Texture.prototype, "needsUpdate", {
+Object.defineProperty( Texture.prototype, 'needsUpdate', {
 
 	set: function ( value ) {
 
@@ -6622,7 +6624,7 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		if ( object === this ) {
 
-			console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+			console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object );
 			return this;
 
 		}
@@ -6642,7 +6644,7 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		} else {
 
-			console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
+			console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object );
 
 		}
 
@@ -8504,7 +8506,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 			if ( newValue === undefined ) {
 
-				console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+				console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' );
 				continue;
 
 			}
@@ -8522,7 +8524,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 			if ( currentValue === undefined ) {
 
-				console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
+				console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' );
 				continue;
 
 			}
@@ -16945,19 +16947,19 @@ function loopReplacer( match, start, end, snippet ) {
 
 function generatePrecision( parameters ) {
 
-	let precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;";
+	let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;';
 
-	if ( parameters.precision === "highp" ) {
+	if ( parameters.precision === 'highp' ) {
 
-		precisionstring += "\n#define HIGH_PRECISION";
+		precisionstring += '\n#define HIGH_PRECISION';
 
-	} else if ( parameters.precision === "mediump" ) {
+	} else if ( parameters.precision === 'mediump' ) {
 
-		precisionstring += "\n#define MEDIUM_PRECISION";
+		precisionstring += '\n#define MEDIUM_PRECISION';
 
-	} else if ( parameters.precision === "lowp" ) {
+	} else if ( parameters.precision === 'lowp' ) {
 
-		precisionstring += "\n#define LOW_PRECISION";
+		precisionstring += '\n#define LOW_PRECISION';
 
 	}
 
@@ -17087,7 +17089,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 	const program = gl.createProgram();
 
 	let prefixVertex, prefixFragment;
-	let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + "\n" : '';
+	let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
 
 	if ( parameters.isRawShaderMaterial ) {
 
@@ -17566,19 +17568,19 @@ function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingSta
 	};
 
 	const parameterNames = [
-		"precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", "instancingColor",
-		"map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV",
-		"lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap",
-		"roughnessMap", "metalnessMap", "gradientMap",
-		"alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2",
-		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
-		"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
-		"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
-		"numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights",
-		"numDirLightShadows", "numPointLightShadows", "numSpotLightShadows",
-		"shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights',
-		"alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering",
-		"sheen", "transmissionMap"
+		'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
+		'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
+		'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap',
+		'roughnessMap', 'metalnessMap', 'gradientMap',
+		'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
+		'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
+		'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals',
+		'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha',
+		'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights',
+		'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows',
+		'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights',
+		'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering',
+		'sheen', 'transmissionMap'
 	];
 
 	function getMaxBones( object ) {
@@ -17631,7 +17633,7 @@ function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingSta
 
 		} else if ( map.isWebGLRenderTarget ) {
 
-			console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." );
+			console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' );
 			encoding = map.texture.encoding;
 
 		}
@@ -18975,7 +18977,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	const fullScreenTri = new BufferGeometry();
 	fullScreenTri.setAttribute(
-		"position",
+		'position',
 		new BufferAttribute(
 			new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ),
 			3
@@ -19061,7 +19063,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 				const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
 
 				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + ".shadowMap";
+				shadow.map.texture.name = light.name + '.shadowMap';
 
 				shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
 
@@ -19074,7 +19076,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 				const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
 
 				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + ".shadowMap";
+				shadow.map.texture.name = light.name + '.shadowMap';
 
 				shadow.camera.updateProjectionMatrix();
 
@@ -20334,7 +20336,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 	try {
 
 		useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'
-			&& ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null;
+			&& ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null;
 
 	} catch ( err ) {
 
@@ -21530,7 +21532,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			if ( warnedTexture2D === false ) {
 
-				console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." );
+				console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' );
 				warnedTexture2D = true;
 
 			}
@@ -21549,7 +21551,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			if ( warnedTextureCube === false ) {
 
-				console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." );
+				console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' );
 				warnedTextureCube = true;
 
 			}
@@ -21977,7 +21979,7 @@ Object.assign( WebXRController.prototype, {
 
 							hand.inputState.pinching = false;
 							this.dispatchEvent( {
-								type: "pinchend",
+								type: 'pinchend',
 								handedness: inputSource.handedness,
 								target: this
 							} );
@@ -21986,7 +21988,7 @@ Object.assign( WebXRController.prototype, {
 
 							hand.inputState.pinching = true;
 							this.dispatchEvent( {
-								type: "pinchstart",
+								type: 'pinchstart',
 								handedness: inputSource.handedness,
 								target: this
 							} );
@@ -30835,7 +30837,7 @@ class ExtrudeBufferGeometry extends BufferGeometry {
 
 			function scalePt2( pt, vec, size ) {
 
-				if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );
+				if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' );
 
 				return vec.clone().multiplyScalar( size ).add( pt );
 
@@ -35233,8 +35235,8 @@ Object.assign( KeyframeTrack.prototype, {
 
 		if ( factoryMethod === undefined ) {
 
-			const message = "unsupported interpolation for " +
-				this.ValueTypeName + " keyframe track named " + this.name;
+			const message = 'unsupported interpolation for ' +
+				this.ValueTypeName + ' keyframe track named ' + this.name;
 
 			if ( this.createInterpolant === undefined ) {
 
@@ -36852,7 +36854,6 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 		const images = [];
 
 		const texture = new CompressedTexture();
-		texture.image = images;
 
 		const loader = new FileLoader( this.manager );
 		loader.setPath( this.path );
@@ -36879,9 +36880,9 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 
 				if ( loaded === 6 ) {
 
-					if ( texDatas.mipmapCount === 1 )
-						texture.minFilter = LinearFilter;
+					if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;
 
+					texture.image = images;
 					texture.format = texDatas.format;
 					texture.needsUpdate = true;
 
@@ -36928,6 +36929,8 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 
 					}
 
+					texture.image = images;
+
 				} else {
 
 					texture.image.width = texDatas.width;
@@ -44019,7 +44022,7 @@ Object.assign( PropertyBinding, {
 
 	findNode: function ( root, nodeName ) {
 
-		if ( ! nodeName || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
+		if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
 
 			return root;
 
@@ -44422,7 +44425,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 			// access a sub element of the property array (only primitives are supported right now)
 
-			if ( propertyName === "morphTargetInfluences" ) {
+			if ( propertyName === 'morphTargetInfluences' ) {
 
 				// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
 
@@ -49686,7 +49689,7 @@ Object.assign( Matrix3.prototype, {
 
 	flattenToArrayOffset: function ( array, offset ) {
 
-		console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
+		console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
 		return this.toArray( array, offset );
 
 	},
@@ -49731,7 +49734,7 @@ Object.assign( Matrix4.prototype, {
 	},
 	flattenToArrayOffset: function ( array, offset ) {
 
-		console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
+		console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
 		return this.toArray( array, offset );
 
 	},
@@ -50221,8 +50224,8 @@ Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {
 
 PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {
 
-	console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " +
-			"Use .setFocalLength and .filmGauge for a photographic setup." );
+	console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' +
+			'Use .setFocalLength and .filmGauge for a photographic setup.' );
 
 	if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
 	this.setFocalLength( focalLength );

+ 2 - 2
docs/api/en/helpers/AxesHelper.html

@@ -25,9 +25,9 @@ scene.add( axesHelper );
 		<h2>Examples</h2>
 
 		<p>
-			[example:webgl_geometries WebGL / geometries]<br/>
+			[example:webgl_buffergeometry_compression WebGL / buffergeometry / compression]<br/>
 			[example:webgl_geometry_convex WebGL / geometry / convex]<br/>
-			[example:webgl_geometry_spline_editor WebGL / geometry / spline / editor]
+			[example:webgl_loader_nrrd WebGL / loader / nrrd]
 		</p>
 
 		<h2>Constructor</h2>

+ 1 - 1
docs/api/en/textures/DataTexture.html

@@ -16,7 +16,7 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy )</h3>
+		<h3>[name]( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding )</h3>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
 			Further parameters correspond to the properties inherited from [page:Texture], where both magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.

+ 2 - 2
docs/api/zh/helpers/AxesHelper.html

@@ -24,9 +24,9 @@
 		<h2>例子</h2>
 
 		<p>
-			[example:webgl_geometries WebGL / geometries]<br/>
+			[example:webgl_buffergeometry_compression WebGL / buffergeometry / compression]<br/>
 			[example:webgl_geometry_convex WebGL / geometry / convex]<br/>
-			[example:webgl_geometry_spline_editor WebGL / geometry / spline / editor]
+			[example:webgl_loader_nrrd WebGL / loader / nrrd]
 		</p>
 
 		<h2>构造函数</h2>

+ 174 - 0
docs/examples/en/math/OBB.html

@@ -0,0 +1,174 @@
+<!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>
+		<h1>[name]</h1>
+
+		<p class="desc">
+			Represents an oriented bounding box (OBB) in 3D space.
+		</p>
+
+		<h2>Examples</h2>
+		<p>
+			[example:webgl_math_obb]
+		</p>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+		<p>
+			[page:Vector3 center] — The center of the [name]. (optional)<br />
+			[page:Vector3 halfSize] — Positive halfwidth extents of the [name] along each axis. (optional)<br />
+			[page:Matrix3 rotation] — The rotation of the [name]. (optional)
+		</p>
+		<p>
+			Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:Vector3 center]</h3>
+		<p>
+			The center of the [name]. Default is *( 0, 0, 0 )*.
+		</p>
+
+		<h3>[property:Vector3 halfSize]</h3>
+		<p>
+			Positive halfwidth extents of the [name] along each axis. Default is *( 0, 0, 0 )*.
+		</p>
+
+		<h3>[property:Matrix3 rotation]</h3>
+		<p>
+			The rotation of the [name]. Default is the identity matrix.
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:this applyMatrix4]( [param:Matrix4 matrix] )</h3>
+		<p>
+			[page:Matrix4 matrix] — A 4x4 transformation matrix.
+		</p>
+		<p>
+			Applies the given transformation matrix to this [name]. This method can be used to transform the
+			bounding volume with the world matrix of a 3D object in order to keep both entities in sync.
+		</p>
+
+		<h3>[method:Vector3 clampPoint]( [param:Vector3 point], [param:Vector3 clampedPoint] )</h3>
+		<p>
+			[page:Vector3 point] — The point that should be clamped within the bounds of this [name].<br />
+			[page:Matrix3 clampedPoint] — The result will be copied into this vector.
+		</p>
+		<p>
+			Clamps the given point within the bounds of this [name].
+		</p>
+
+		<h3>[method:OBB clone]()</h3>
+		<p>
+			Creates a cloned [name] for this instance.
+		</p>
+
+		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
+		<p>
+			[page:Vector3 point] — The point to test.
+		</p>
+		<p>
+			Whether the given point lies within this [name] or not.
+		</p>
+
+		<h3>[method:this copy]( [param:OBB obb] )</h3>
+		<p>
+			[page:OBB obb] — The [name] to copy.
+		</p>
+		<p>
+			Copies the properties of the given [name] to this [name].
+		</p>
+
+		<h3>[method:Boolean equals]( [param:OBB obb] )</h3>
+		<p>
+			[page:OBB obb] — The [name] to test.
+		</p>
+		<p>
+			Whether the given [name] is equal to this [name] or not.
+		</p>
+
+		<h3>[method:this fromBox3]( [param:Box3 box3] )</h3>
+		<p>
+			[page:Box3 box3] — An AABB.
+		</p>
+		<p>
+			Defines an [name] based on the given AABB.
+		</p>
+
+		<h3>[method:Vector3 getSize]( [param:Vector3 size] )</h3>
+		<p>
+			[page:Vector3 size] — The result will be copied into this vector.
+		</p>
+		<p>
+			Returns the size of this [name] into the given vector.
+		</p>
+
+		<h3>[method:Boolean intersectsBox3]( [param:Box3 box3] )</h3>
+		<p>
+			[page:Box3 box3] — The AABB to test.
+		</p>
+		<p>
+			Whether the given AABB intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsSphere]( [param:Sphere sphere] )</h3>
+		<p>
+			[page:Sphere sphere] — The bounding sphere to test.
+		</p>
+		<p>
+			Whether the given bounding sphere intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsOBB]( [param:OBB obb], [param:Number epsilon] )</h3>
+		<p>
+			[page:OBB obb] — The OBB to test.<br />
+			[page:Number epsilon] — An optional numeric value to counteract arithmetic errors. Default is *Number.EPSILON*.
+		</p>
+		<p>
+			Whether the given [name] intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsRay]( [param:Ray ray] )</h3>
+		<p>
+			[page:Ray ray] — The ray to test.
+		</p>
+		<p>
+			Whether the given ray intersects this [name] or not.
+		</p>
+
+		<h3>[method:Vector3 intersectRay]( [param:Ray ray], [param:Vector3 intersectionPoint] )</h3>
+		<p>
+			[page:Ray ray] — The ray to test.<br />
+			[page:Vector3 intersectionPoint] — The result will be copied into this vector.
+		</p>
+		<p>
+			Performs a Ray/OBB intersection test and stores the intersection point to the given 3D vector.
+			If no intersection is detected, *null* is returned.
+		</p>
+
+		<h3>[method:this set]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+		<p>
+			[page:Vector3 center] — The center of the [name].<br />
+			[page:Vector3 halfSize] — Positive halfwidth extents of the [name] along each axis.<br />
+			[page:Matrix3 rotation] — The rotation of the [name].
+		</p>
+		<p>
+			Defines the [name] for the given values.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/OBB.js examples/jsm/math/OBB.js]
+		</p>
+	</body>
+</html>

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

@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<html lang="zh">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc">
+			Represents an oriented bounding box (OBB) in 3D space.
+		</p>
+
+		<h2>Examples</h2>
+		<p>
+			[example:webgl_math_obb]
+		</p>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+		<p>
+			[page:Vector3 center] — The center of the [name]. (optional)<br />
+			[page:Vector3 halfSize] — Positive halfwidth extents of the [name] along each axis. (optional)<br />
+			[page:Matrix3 rotation] — The rotation of the [name]. (optional)
+		</p>
+		<p>
+			Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:Vector3 center]</h3>
+		<p>
+			The center of the [name]. Default is *( 0, 0, 0 )*.
+		</p>
+
+		<h3>[property:Vector3 halfSize]</h3>
+		<p>
+			Positive halfwidth extents of the [name] along each axis. Default is *( 0, 0, 0 )*.
+		</p>
+
+		<h3>[property:Matrix3 rotation]</h3>
+		<p>
+			The rotation of the [name]. Default is the identity matrix.
+		</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:this applyMatrix4]( [param:Matrix4 matrix] )</h3>
+		<p>
+			[page:Matrix4 matrix] — A 4x4 transformation matrix.
+		</p>
+		<p>
+			Applies the given transformation matrix to this [name]. This method can be used to transform the
+			bounding volume with the world matrix of a 3D object in order to keep both entities in sync.
+		</p>
+
+		<h3>[method:Vector3 clampPoint]( [param:Vector3 point], [param:Vector3 clampedPoint] )</h3>
+		<p>
+			[page:Vector3 point] — The point that should be clamped within the bounds of this [name].<br />
+			[page:Matrix3 clampedPoint] — The result will be copied into this vector.
+		</p>
+		<p>
+			Clamps the given point within the bounds of this [name].
+		</p>
+
+		<h3>[method:OBB clone]()</h3>
+		<p>
+			Creates a cloned [name] for this instance.
+		</p>
+
+		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
+		<p>
+			[page:Vector3 point] — The point to test.
+		</p>
+		<p>
+			Whether the given point lies within this [name] or not.
+		</p>
+
+		<h3>[method:this copy]( [param:OBB obb] )</h3>
+		<p>
+			[page:OBB obb] — The [name] to copy.
+		</p>
+		<p>
+			Copies the properties of the given [name] to this [name].
+		</p>
+
+		<h3>[method:Boolean equals]( [param:OBB obb] )</h3>
+		<p>
+			[page:OBB obb] — The [name] to test.
+		</p>
+		<p>
+			Whether the given [name] is equal to this [name] or not.
+		</p>
+
+		<h3>[method:this fromBox3]( [param:Box3 box3] )</h3>
+		<p>
+			[page:Box3 box3] — An AABB.
+		</p>
+		<p>
+			Defines an [name] based on the given AABB.
+		</p>
+
+		<h3>[method:Vector3 getSize]( [param:Vector3 size] )</h3>
+		<p>
+			[page:Vector3 size] — The result will be copied into this vector.
+		</p>
+		<p>
+			Returns the size of this [name] into the given vector.
+		</p>
+
+		<h3>[method:Boolean intersectsBox3]( [param:Box3 box3] )</h3>
+		<p>
+			[page:Box3 box3] — The AABB to test.
+		</p>
+		<p>
+			Whether the given AABB intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsSphere]( [param:Sphere sphere] )</h3>
+		<p>
+			[page:Sphere sphere] — The bounding sphere to test.
+		</p>
+		<p>
+			Whether the given bounding sphere intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsOBB]( [param:OBB obb], [param:Number epsilon] )</h3>
+		<p>
+			[page:OBB obb] — The OBB to test.<br />
+			[page:Number epsilon] — An optional numeric value to counteract arithmetic errors. Default is *Number.EPSILON*.
+		</p>
+		<p>
+			Whether the given [name] intersects this [name] or not.
+		</p>
+
+		<h3>[method:Boolean intersectsRay]( [param:Ray ray] )</h3>
+		<p>
+			[page:Ray ray] — The ray to test.
+		</p>
+		<p>
+			Whether the given ray intersects this [name] or not.
+		</p>
+
+		<h3>[method:Vector3 intersectRay]( [param:Ray ray], [param:Vector3 intersectionPoint] )</h3>
+		<p>
+			[page:Ray ray] — The ray to test.<br />
+			[page:Vector3 intersectionPoint] — The result will be copied into this vector.
+		</p>
+		<p>
+			Performs a Ray/OBB intersection test and stores the intersection point to the given 3D vector.
+			If no intersection is detected, *null* is returned.
+		</p>
+
+		<h3>[method:this set]( [param:Vector3 center], [param:Vector3 halfSize], [param:Matrix3 rotation] )</h3>
+		<p>
+			[page:Vector3 center] — The center of the [name].<br />
+			[page:Vector3 halfSize] — Positive halfwidth extents of the [name] along each axis.<br />
+			[page:Matrix3 rotation] — The rotation of the [name].
+		</p>
+		<p>
+			Defines the [name] for the given values.
+		</p>
+
+		<h2>Source</h2>
+
+		<p>
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/math/OBB.js examples/jsm/math/OBB.js]
+		</p>
+	</body>
+</html>

+ 4 - 2
docs/list.json

@@ -422,7 +422,8 @@
 
 			"Math": {
 				"LookupTable": "examples/en/math/Lut",
-				"MeshSurfaceSampler": "examples/en/math/MeshSurfaceSampler"
+				"MeshSurfaceSampler": "examples/en/math/MeshSurfaceSampler",
+				"OBB": "examples/en/math/OBB"
 			},
 
 			"ConvexHull": {
@@ -957,7 +958,8 @@
 
 			"数学库": {
 				"LookupTable": "examples/zh/math/Lut",
-				"MeshSurfaceSampler": "examples/zh/math/MeshSurfaceSampler"
+				"MeshSurfaceSampler": "examples/zh/math/MeshSurfaceSampler",
+				"OBB": "examples/en/math/OBB"
 			},
 
 			"QuickHull": {

+ 10 - 10
examples/index.html

@@ -188,16 +188,16 @@
 
 		function createLink( file ) {
 
-			const template = [
-				'<div class="card">',
-				'	<a href="' + file + '.html" target="viewer">',
-				'		<div class="cover">',
-				'			<img src="screenshots/' + file + '.jpg" loading="lazy" width="400" />',
-				'		</div>',
-				'		<div class="title">' + getName( file ) + '</div>',
-				'	</a>',
-				'</div>'
-			].join( "\n" );
+			const template = `
+				<div class="card">
+					<a href="${file}.html" target="viewer">
+						<div class="cover">
+							<img src="screenshots/${ file }.jpg" loading="lazy" width="400" />
+						</div>
+						<div class="title">${getName( file )}</div>
+					</a>
+				</div>
+			`;
 
 			const link = createElementFromHTML( template );
 

+ 1 - 1
examples/js/exporters/GLTFExporter.js

@@ -2282,7 +2282,7 @@ THREE.GLTFExporter.Utils = {
 
 				// We need to take into consideration the intended target node
 				// of our original un-merged morphTarget animation.
-				mergedTrack.name = sourceTrackBinding.nodeName + '.morphTargetInfluences';
+				mergedTrack.name = ( sourceTrackBinding.nodeName || '' ) + '.morphTargetInfluences';
 				mergedTrack.values = values;
 
 				mergedTracks[ sourceTrackNode.uuid ] = mergedTrack;

+ 98 - 0
examples/js/utils/GeometryUtils.js

@@ -140,6 +140,104 @@ THREE.GeometryUtils = {
 		// Return complete Hilbert Curve.
 		return vec;
 
+	},
+
+	/**
+	 * Generates a Gosper curve (lying in the XY plane)
+	 *
+	 * https://gist.github.com/nitaku/6521802
+	 *
+	 * @param size The size of a single gosper island.
+	 */
+	gosper: function ( size ) {
+
+		size = ( size !== undefined ) ? size : 1;
+
+		function fractalize( config ) {
+
+			var output;
+			var input = config.axiom;
+
+			for ( var i = 0, il = config.steps; 0 <= il ? i < il : i > il; 0 <= il ? i ++ : i -- ) {
+
+				output = '';
+
+				for ( var j = 0, jl = input.length; j < jl; j ++ ) {
+
+					var char = input[ j ];
+
+					if ( char in config.rules ) {
+
+						output += config.rules[ char ];
+
+					} else {
+
+						output += char;
+
+					}
+
+				}
+
+				input = output;
+
+			}
+
+			return output;
+
+		}
+
+		function toPoints( config ) {
+
+			var currX = 0, currY = 0;
+			var angle = 0;
+			var path = [ 0, 0, 0 ];
+			var fractal = config.fractal;
+
+			for ( var i = 0, l = fractal.length; i < l; i ++ ) {
+
+				var char = fractal[ i ];
+
+				if ( char === '+' ) {
+
+					angle += config.angle;
+
+				} else if ( char === '-' ) {
+
+					angle -= config.angle;
+
+				} else if ( char === 'F' ) {
+
+					currX += config.size * Math.cos( angle );
+					currY += - config.size * Math.sin( angle );
+					path.push( currX, currY, 0 );
+
+				}
+
+			}
+
+			return path;
+
+		}
+
+		//
+
+		var gosper = fractalize( {
+			axiom: 'A',
+			steps: 4,
+			rules: {
+				A: 'A+BF++BF-FA--FAFA-BF+',
+				B: '-FA+BFBF++BF+FA--FA-B'
+			}
+		} );
+
+		var points = toPoints( {
+			fractal: gosper,
+			size: size,
+			angle: Math.PI / 3 // 60 degrees
+		} );
+
+		return points;
+
 	}
 
 };

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

@@ -2305,7 +2305,7 @@ GLTFExporter.Utils = {
 
 				// We need to take into consideration the intended target node
 				// of our original un-merged morphTarget animation.
-				mergedTrack.name = sourceTrackBinding.nodeName + '.morphTargetInfluences';
+				mergedTrack.name = ( sourceTrackBinding.nodeName || '' ) + '.morphTargetInfluences';
 				mergedTrack.values = values;
 
 				mergedTracks[ sourceTrackNode.uuid ] = mergedTrack;

+ 21 - 5
examples/jsm/loaders/3DMLoader.js

@@ -442,9 +442,13 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				var points = new Points( geometry, material );
 				points.userData[ 'attributes' ] = attributes;
 				points.userData[ 'objectType' ] = obj.objectType;
-				if( attributes.name ) {
+
+				if ( attributes.name ) {
+
 					points.name = attributes.name;
+
 				}
+
 				return points;
 
 			case 'Mesh':
@@ -471,8 +475,11 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				mesh.receiveShadow = attributes.receivesShadows;
 				mesh.userData[ 'attributes' ] = attributes;
 				mesh.userData[ 'objectType' ] = obj.objectType;
-				if( attributes.name ) {
+
+				if ( attributes.name ) {
+
 					mesh.name = attributes.name;
+
 				}
 
 				return mesh;
@@ -494,8 +501,11 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 				brepObject.userData[ 'attributes' ] = attributes;
 				brepObject.userData[ 'objectType' ] = obj.objectType;
-				if( attributes.name ) {
+
+				if ( attributes.name ) {
+
 					brepObject.name = attributes.name;
+
 				}
 
 				return brepObject;
@@ -513,8 +523,11 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				var lines = new Line( geometry, material );
 				lines.userData[ 'attributes' ] = attributes;
 				lines.userData[ 'objectType' ] = obj.objectType;
-				if( attributes.name ) {
+
+				if ( attributes.name ) {
+
 					lines.name = attributes.name;
+
 				}
 
 				return lines;
@@ -558,8 +571,11 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 				sprite.userData[ 'attributes' ] = attributes;
 				sprite.userData[ 'objectType' ] = obj.objectType;
-				if( attributes.name ) {
+
+				if ( attributes.name ) {
+
 					sprite.name = attributes.name;
+
 				}
 
 				return sprite;

+ 0 - 1
examples/jsm/loaders/ColladaLoader.d.ts

@@ -1,5 +1,4 @@
 import {
-	AnimationClip,
 	Loader,
 	LoadingManager,
 	Scene

+ 18 - 0
examples/jsm/loaders/GLTFLoader.d.ts

@@ -4,8 +4,10 @@ import {
 	Group,
 	Loader,
 	LoadingManager,
+	Mesh,
 	Object3D,
 	Material,
+	SkinnedMesh,
 	Texture
 } from '../../../src/Three';
 
@@ -39,6 +41,10 @@ export class GLTFLoader extends Loader {
 	load( url: string, onLoad: ( gltf: GLTF ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 	setDRACOLoader( dracoLoader: DRACOLoader ): GLTFLoader;
 	setDDSLoader( ddsLoader: DDSLoader ): GLTFLoader;
+
+	register( callback: ( parser: GLTFParser ) => GLTFLoaderPlugin ): GLTFLoader;
+	unregister( callback: ( parser: GLTFParser ) => GLTFLoaderPlugin ): GLTFLoader;
+
 	setKTX2Loader( ktx2Loader: KTX2Loader ): GLTFLoader;
 	setMeshoptDecoder( meshoptDecoder: /* MeshoptDecoder */ any ): GLTFLoader;
 
@@ -61,3 +67,15 @@ export class GLTFParser {
 	getDependencies: ( type: string ) => Promise<any[]>;
 
 }
+
+export interface GLTFLoaderPlugin {
+
+	loadMesh?: ( meshIndex: number ) => Promise<Group | Mesh | SkinnedMesh> | null;
+	loadBufferView?: ( bufferViewIndex: number ) => Promise<ArrayBuffer> | null;
+	loadMaterial?: ( materialIndex: number ) => Promise<Material> | null;
+	loadTexture?: ( textureIndex: number ) => Promise<Texture> | null;
+	getMaterialType?: ( materialIndex: number ) => typeof Material | null;
+	extendMaterialParams?: ( materialIndex: number, materialParams: { [ key: string ]: any } ) => Promise<any> | null;
+	createNodeAttachment?: ( nodeIndex: number ) => Promise<Object3D> | null;
+
+}

+ 6 - 13
examples/jsm/loaders/LottieLoader.js

@@ -5,23 +5,15 @@ import {
 	NearestFilter
 } from "../../../build/three.module.js";
 
-var LottieLoader = function ( manager ) {
+class LottieLoader extends Loader {
 
-	Loader.call( this, manager );
-
-};
-
-LottieLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
-
-	constructor: LottieLoader,
-
-	setQuality: function ( value ) {
+	setQuality( value ) {
 
 		this._quality = value;
 
-	},
+	}
 
-	load: function ( url, onLoad, onProgress, onError ) {
+	load( url, onLoad, onProgress, onError ) {
 
 		const quality = this._quality || 1;
 
@@ -44,6 +36,7 @@ LottieLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 			container.style.height = data.h + 'px';
 			document.body.appendChild( container );
 
+			// eslint-disable-next-line no-undef
 			const animation = bodymovin.loadAnimation( {
 				container: container,
 				animType: 'canvas',
@@ -76,6 +69,6 @@ LottieLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 	}
 
-} );
+}
 
 export { LottieLoader };

+ 14 - 16
examples/jsm/modifiers/CurveModifier.js

@@ -132,18 +132,18 @@ export function modifyShader( material, uniforms, numberOfCurves = 1 ) {
 		${shader.vertexShader}
 		`
 		// chunk import moved in front of modified shader below
-		.replace('#include <beginnormal_vertex>', ``)
+			.replace( '#include <beginnormal_vertex>', `` )
 
-		// vec3 transformedNormal declaration overriden below
-		.replace('#include <defaultnormal_vertex>', ``)
+			// vec3 transformedNormal declaration overriden below
+			.replace( '#include <defaultnormal_vertex>', `` )
 
-		// vec3 transformed declaration overriden below
-		.replace('#include <begin_vertex>', ``)
+			// vec3 transformed declaration overriden below
+			.replace( '#include <begin_vertex>', `` )
 
-		// shader override
-		.replace(
-			/void\s*main\s*\(\)\s*\{/,
-`
+			// shader override
+			.replace(
+				/void\s*main\s*\(\)\s*\{/,
+				`
 void main() {
 #include <beginnormal_vertex>
 
@@ -180,13 +180,11 @@ vec3 transformed = basis
 	+ spinePos;
 
 vec3 transformedNormal = normalMatrix * (basis * objectNormal);
-`).replace(
-	'#include <project_vertex>',
-`
-vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
-gl_Position = projectionMatrix * mvPosition;
-`
-);
+			` ).replace(
+				'#include <project_vertex>',
+				`vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
+				gl_Position = projectionMatrix * mvPosition;`
+			);
 
 		shader.vertexShader = vertexShader;
 

+ 11 - 9
examples/jsm/postprocessing/SSAOPass.d.ts

@@ -13,6 +13,15 @@ import {
 
 import { Pass } from './Pass';
 
+export enum OUTPUT {
+	Default,
+	SSAO,
+	Blur,
+	Beauty,
+	Depth,
+	Normal
+}
+
 export class SSAOPass extends Pass {
 
 	constructor( scene: Scene, camera: Camera, width?: number, height?: number );
@@ -25,7 +34,7 @@ export class SSAOPass extends Pass {
 	kernelSize: number;
 	kernel: Vector3[];
 	noiseTexture: DataTexture;
-	output: number;
+	output: OUTPUT;
 	minDistance: number;
 	maxDistance: number;
 	beautyRenderTarget: WebGLRenderTarget;
@@ -40,14 +49,7 @@ export class SSAOPass extends Pass {
 	fsQuad: object;
 	originalClearColor: Color;
 
-	static OUTPUT: {
-		Default: number;
-		SSAO: number;
-		Blur: number;
-		Beauty: number;
-		Depth: number;
-		Normal: number;
-	};
+	static OUTPUT: OUTPUT;
 
 	dipose(): void;
 	generateSampleKernel(): Vector3[];

+ 1 - 0
examples/jsm/utils/GeometryUtils.d.ts

@@ -5,4 +5,5 @@ import {
 export namespace GeometryUtils {
 	export function hilbert2D( center?: Vector3, size?: number, iterations?: number, v0?: number, v1?: number, v2?: number, v3?: number ): Vector3[];
 	export function hilbert3D( center?: Vector3, size?: number, iterations?: number, v0?: number, v1?: number, v2?: number, v3?: number, v4?: number, v5?: number, v6?: number, v7?: number ): Vector3[];
+	export function gosper( size?: number ): number[];
 }

+ 98 - 0
examples/jsm/utils/GeometryUtils.js

@@ -144,6 +144,104 @@ var GeometryUtils = {
 		// Return complete Hilbert Curve.
 		return vec;
 
+	},
+
+	/**
+	 * Generates a Gosper curve (lying in the XY plane)
+	 *
+	 * https://gist.github.com/nitaku/6521802
+	 *
+	 * @param size The size of a single gosper island.
+	 */
+	gosper: function ( size ) {
+
+		size = ( size !== undefined ) ? size : 1;
+
+		function fractalize( config ) {
+
+			var output;
+			var input = config.axiom;
+
+			for ( var i = 0, il = config.steps; 0 <= il ? i < il : i > il; 0 <= il ? i ++ : i -- ) {
+
+				output = '';
+
+				for ( var j = 0, jl = input.length; j < jl; j ++ ) {
+
+					var char = input[ j ];
+
+					if ( char in config.rules ) {
+
+						output += config.rules[ char ];
+
+					} else {
+
+						output += char;
+
+					}
+
+				}
+
+				input = output;
+
+			}
+
+			return output;
+
+		}
+
+		function toPoints( config ) {
+
+			var currX = 0, currY = 0;
+			var angle = 0;
+			var path = [ 0, 0, 0 ];
+			var fractal = config.fractal;
+
+			for ( var i = 0, l = fractal.length; i < l; i ++ ) {
+
+				var char = fractal[ i ];
+
+				if ( char === '+' ) {
+
+					angle += config.angle;
+
+				} else if ( char === '-' ) {
+
+					angle -= config.angle;
+
+				} else if ( char === 'F' ) {
+
+					currX += config.size * Math.cos( angle );
+					currY += - config.size * Math.sin( angle );
+					path.push( currX, currY, 0 );
+
+				}
+
+			}
+
+			return path;
+
+		}
+
+		//
+
+		var gosper = fractalize( {
+			axiom: 'A',
+			steps: 4,
+			rules: {
+				A: 'A+BF++BF-FA--FAFA-BF+',
+				B: '-FA+BFBF++BF+FA--FA-B'
+			}
+		} );
+
+		var points = toPoints( {
+			fractal: gosper,
+			size: size,
+			angle: Math.PI / 3 // 60 degrees
+		} );
+
+		return points;
+
 	}
 
 };

BIN
examples/screenshots/webgl_framebuffer_texture.jpg


+ 2 - 2
examples/tags.json

@@ -1,5 +1,5 @@
 {
-	"webgl_animation_cloth": [ "physics", "integration" ],
+	"webgl_animation_cloth": [ "physics", "integration", "shadow" ],
 	"webgl_clipping": [ "solid" ],
 	"webgl_clipping_advanced": [ "solid" ],
 	"webgl_clipping_intersection": [ "solid" ],
@@ -37,7 +37,7 @@
 	"webgl_materials_channels": [ "normal", "depth", "rgba packing" ],
 	"webgl_materials_cubemap_mipmaps": [ "envmap" ],
 	"webgl_materials_envmaps_parallax": [ "onBeforeCompile" ],
-	"webgl_materials_lightmap": [ "shadows" ],
+	"webgl_materials_lightmap": [ "shadow" ],
 	"webgl_materials_physical_clearcoat": [ "anisotropy" ],
 	"webgl_materials_physical_transmission": [ "alpha" ],
 	"webgl_materials_shaders_fresnel": [ "refraction" ],

+ 43 - 14
examples/webgl_framebuffer_texture.html

@@ -40,16 +40,20 @@
 			import * as THREE from '../build/three.module.js';
 
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { GeometryUtils } from './jsm/utils/GeometryUtils.js';
 
 			let camera, scene, renderer;
-			let mesh, sprite, texture;
+			let line, sprite, texture;
 
 			let cameraOrtho, sceneOrtho;
 
+			let offset = 0;
+
 			const dpr = window.devicePixelRatio;
 
 			const textureSize = 128 * dpr;
 			const vector = new THREE.Vector2();
+			const color = new THREE.Color();
 
 			init();
 			animate();
@@ -68,24 +72,26 @@
 				cameraOrtho.position.z = 10;
 
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0x20252f );
 				sceneOrtho = new THREE.Scene();
 
 				//
 
-				const geometry = new THREE.TorusKnotBufferGeometry( 3, 1, 256, 32 );
-				const material = new THREE.MeshStandardMaterial( { color: 0x6083c2 } );
+				const points = GeometryUtils.gosper( 8 );
 
-				mesh = new THREE.Mesh( geometry, material );
-				scene.add( mesh );
+				const geometry = new THREE.BufferGeometry();
+				const positionAttribute = new THREE.Float32BufferAttribute( points, 3 );
+				geometry.setAttribute( 'position', positionAttribute );
+				geometry.center();
 
-				//
-				const ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
-				scene.add( ambientLight );
+				const colorAttribute = new THREE.BufferAttribute( new Float32Array( positionAttribute.array.length ), 3 );
+				colorAttribute.setUsage( THREE.DynamicDrawUsage );
+				geometry.setAttribute( 'color', colorAttribute );
 
-				const pointLight = new THREE.PointLight( 0xffffff, 0.8 );
-				camera.add( pointLight );
-				scene.add( camera );
+				const material = new THREE.LineBasicMaterial( { vertexColors: true } );
+
+				line = new THREE.Line( geometry, material );
+				line.scale.setScalar( 0.05 );
+				scene.add( line );
 
 				//
 
@@ -160,8 +166,10 @@
 
 				requestAnimationFrame( animate );
 
-				mesh.rotation.x += 0.005;
-				mesh.rotation.y += 0.01;
+				const colorAttribute = line.geometry.getAttribute( 'color' );
+				updateColors( colorAttribute );
+
+				// scene rendering
 
 				renderer.clear();
 				renderer.render( scene, camera );
@@ -178,6 +186,27 @@
 
 			}
 
+			function updateColors( colorAttribute ) {
+
+				const l = colorAttribute.count;
+
+				for ( let i = 0; i < l; i ++ ) {
+
+					const h = ( ( offset + i ) % l ) / l;
+
+					color.setHSL( h, 1, 0.5 );
+					colorAttribute.setX( i, color.r );
+					colorAttribute.setY( i, color.g );
+					colorAttribute.setZ( i, color.b );
+
+				}
+
+				colorAttribute.needsUpdate = true;
+
+				offset -= 25;
+
+			}
+
 		</script>
 
 	</body>

+ 5 - 0
package.json

@@ -27,6 +27,7 @@
     "test": "test"
   },
   "eslintConfig": {
+    "root": true,
     "parser": "@typescript-eslint/parser",
     "extends": "mdcs",
     "plugins": [
@@ -44,6 +45,10 @@
           "SwitchCase": 1
         }
       ],
+      "quotes": [
+        "error",
+        "single"
+      ],
       "prefer-const": [
         "error",
         {

+ 4 - 4
src/Three.Legacy.js

@@ -583,7 +583,7 @@ Object.assign( Matrix3.prototype, {
 
 	flattenToArrayOffset: function ( array, offset ) {
 
-		console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
+		console.warn( 'THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
 		return this.toArray( array, offset );
 
 	},
@@ -628,7 +628,7 @@ Object.assign( Matrix4.prototype, {
 	},
 	flattenToArrayOffset: function ( array, offset ) {
 
-		console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." );
+		console.warn( 'THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.' );
 		return this.toArray( array, offset );
 
 	},
@@ -1118,8 +1118,8 @@ Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {
 
 PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {
 
-	console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " +
-			"Use .setFocalLength and .filmGauge for a photographic setup." );
+	console.warn( 'THREE.PerspectiveCamera.setLens is deprecated. ' +
+			'Use .setFocalLength and .filmGauge for a photographic setup.' );
 
 	if ( filmGauge !== undefined ) this.filmGauge = filmGauge;
 	this.setFocalLength( focalLength );

+ 2 - 2
src/animation/KeyframeTrack.js

@@ -125,8 +125,8 @@ Object.assign( KeyframeTrack.prototype, {
 
 		if ( factoryMethod === undefined ) {
 
-			const message = "unsupported interpolation for " +
-				this.ValueTypeName + " keyframe track named " + this.name;
+			const message = 'unsupported interpolation for ' +
+				this.ValueTypeName + ' keyframe track named ' + this.name;
 
 			if ( this.createInterpolant === undefined ) {
 

+ 2 - 2
src/animation/PropertyBinding.js

@@ -188,7 +188,7 @@ Object.assign( PropertyBinding, {
 
 	findNode: function ( root, nodeName ) {
 
-		if ( ! nodeName || nodeName === "" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
+		if ( ! nodeName || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
 
 			return root;
 
@@ -591,7 +591,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 			// access a sub element of the property array (only primitives are supported right now)
 
-			if ( propertyName === "morphTargetInfluences" ) {
+			if ( propertyName === 'morphTargetInfluences' ) {
 
 				// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.
 

+ 2 - 2
src/constants.js

@@ -196,5 +196,5 @@ export const StaticCopyUsage = 35046;
 export const DynamicCopyUsage = 35050;
 export const StreamCopyUsage = 35042;
 
-export const GLSL1 = "100";
-export const GLSL3 = "300 es";
+export const GLSL1 = '100';
+export const GLSL3 = '300 es';

+ 2 - 2
src/core/Object3D.js

@@ -315,7 +315,7 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		if ( object === this ) {
 
-			console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object );
+			console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object );
 			return this;
 
 		}
@@ -335,7 +335,7 @@ Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		} else {
 
-			console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object );
+			console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object );
 
 		}
 

+ 1 - 1
src/geometries/ExtrudeBufferGeometry.js

@@ -175,7 +175,7 @@ class ExtrudeBufferGeometry extends BufferGeometry {
 
 			function scalePt2( pt, vec, size ) {
 
-				if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" );
+				if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' );
 
 				return vec.clone().multiplyScalar( size ).add( pt );
 

+ 4 - 3
src/loaders/CompressedTextureLoader.js

@@ -26,7 +26,6 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 		const images = [];
 
 		const texture = new CompressedTexture();
-		texture.image = images;
 
 		const loader = new FileLoader( this.manager );
 		loader.setPath( this.path );
@@ -53,9 +52,9 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 
 				if ( loaded === 6 ) {
 
-					if ( texDatas.mipmapCount === 1 )
-						texture.minFilter = LinearFilter;
+					if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter;
 
+					texture.image = images;
 					texture.format = texDatas.format;
 					texture.needsUpdate = true;
 
@@ -102,6 +101,8 @@ CompressedTextureLoader.prototype = Object.assign( Object.create( Loader.prototy
 
 					}
 
+					texture.image = images;
+
 				} else {
 
 					texture.image.width = texDatas.width;

+ 2 - 2
src/materials/Material.js

@@ -96,7 +96,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 			if ( newValue === undefined ) {
 
-				console.warn( "THREE.Material: '" + key + "' parameter is undefined." );
+				console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' );
 				continue;
 
 			}
@@ -114,7 +114,7 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 			if ( currentValue === undefined ) {
 
-				console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." );
+				console.warn( 'THREE.' + this.type + ': \'' + key + '\' is not a property of this material.' );
 				continue;
 
 			}

+ 2 - 0
src/math/Matrix3.js

@@ -239,6 +239,8 @@ class Matrix3 {
 			0, 0, 1
 		);
 
+		return this;
+
 	}
 
 	scale( sx, sy ) {

+ 2 - 2
src/renderers/WebGLRenderer.js

@@ -13,6 +13,7 @@ import { Matrix4 } from '../math/Matrix4.js';
 import { Vector2 } from '../math/Vector2.js';
 import { Vector3 } from '../math/Vector3.js';
 import { Vector4 } from '../math/Vector4.js';
+import { Color } from '../math/Color.js';
 import { WebGLAnimation } from './webgl/WebGLAnimation.js';
 import { WebGLAttributes } from './webgl/WebGLAttributes.js';
 import { WebGLBackground } from './webgl/WebGLBackground.js';
@@ -37,8 +38,7 @@ import { WebGLTextures } from './webgl/WebGLTextures.js';
 import { WebGLUniforms } from './webgl/WebGLUniforms.js';
 import { WebGLUtils } from './webgl/WebGLUtils.js';
 import { WebXRManager } from './webxr/WebXRManager.js';
-import { WebGLMaterials } from "./webgl/WebGLMaterials.js";
-import { Color } from '../math/Color.js';
+import { WebGLMaterials } from './webgl/WebGLMaterials.js';
 
 function createCanvasElement() {
 

+ 3 - 3
src/renderers/webgl/WebGLBufferRenderer.d.ts

@@ -1,7 +1,7 @@
 // Renderers / WebGL /////////////////////////////////////////////////////////////////////
-import { WebGLExtensions } from "./WebGLExtensions";
-import { WebGLInfo } from "./WebGLInfo";
-import { WebGLCapabilities } from "./WebGLCapabilities";
+import { WebGLExtensions } from './WebGLExtensions';
+import { WebGLInfo } from './WebGLInfo';
+import { WebGLCapabilities } from './WebGLCapabilities';
 
 export class WebGLBufferRenderer {
 

+ 8 - 8
src/renderers/webgl/WebGLProgram.js

@@ -257,19 +257,19 @@ function loopReplacer( match, start, end, snippet ) {
 
 function generatePrecision( parameters ) {
 
-	let precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;";
+	let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;';
 
-	if ( parameters.precision === "highp" ) {
+	if ( parameters.precision === 'highp' ) {
 
-		precisionstring += "\n#define HIGH_PRECISION";
+		precisionstring += '\n#define HIGH_PRECISION';
 
-	} else if ( parameters.precision === "mediump" ) {
+	} else if ( parameters.precision === 'mediump' ) {
 
-		precisionstring += "\n#define MEDIUM_PRECISION";
+		precisionstring += '\n#define MEDIUM_PRECISION';
 
-	} else if ( parameters.precision === "lowp" ) {
+	} else if ( parameters.precision === 'lowp' ) {
 
-		precisionstring += "\n#define LOW_PRECISION";
+		precisionstring += '\n#define LOW_PRECISION';
 
 	}
 
@@ -399,7 +399,7 @@ function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) {
 	const program = gl.createProgram();
 
 	let prefixVertex, prefixFragment;
-	let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + "\n" : '';
+	let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : '';
 
 	if ( parameters.isRawShaderMaterial ) {
 

+ 14 - 14
src/renderers/webgl/WebGLPrograms.js

@@ -34,19 +34,19 @@ function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingSta
 	};
 
 	const parameterNames = [
-		"precision", "isWebGL2", "supportsVertexTextures", "outputEncoding", "instancing", "instancingColor",
-		"map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", "envMapCubeUV",
-		"lightMap", "lightMapEncoding", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatMap", "clearcoatRoughnessMap", "clearcoatNormalMap", "displacementMap", "specularMap",
-		"roughnessMap", "metalnessMap", "gradientMap",
-		"alphaMap", "combine", "vertexColors", "vertexTangents", "vertexUvs", "uvsVertexOnly", "fog", "useFog", "fogExp2",
-		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
-		"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
-		"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
-		"numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights",
-		"numDirLightShadows", "numPointLightShadows", "numSpotLightShadows",
-		"shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights',
-		"alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering",
-		"sheen", "transmissionMap"
+		'precision', 'isWebGL2', 'supportsVertexTextures', 'outputEncoding', 'instancing', 'instancingColor',
+		'map', 'mapEncoding', 'matcap', 'matcapEncoding', 'envMap', 'envMapMode', 'envMapEncoding', 'envMapCubeUV',
+		'lightMap', 'lightMapEncoding', 'aoMap', 'emissiveMap', 'emissiveMapEncoding', 'bumpMap', 'normalMap', 'objectSpaceNormalMap', 'tangentSpaceNormalMap', 'clearcoatMap', 'clearcoatRoughnessMap', 'clearcoatNormalMap', 'displacementMap', 'specularMap',
+		'roughnessMap', 'metalnessMap', 'gradientMap',
+		'alphaMap', 'combine', 'vertexColors', 'vertexTangents', 'vertexUvs', 'uvsVertexOnly', 'fog', 'useFog', 'fogExp2',
+		'flatShading', 'sizeAttenuation', 'logarithmicDepthBuffer', 'skinning',
+		'maxBones', 'useVertexTexture', 'morphTargets', 'morphNormals',
+		'maxMorphTargets', 'maxMorphNormals', 'premultipliedAlpha',
+		'numDirLights', 'numPointLights', 'numSpotLights', 'numHemiLights', 'numRectAreaLights',
+		'numDirLightShadows', 'numPointLightShadows', 'numSpotLightShadows',
+		'shadowMapEnabled', 'shadowMapType', 'toneMapping', 'physicallyCorrectLights',
+		'alphaTest', 'doubleSided', 'flipSided', 'numClippingPlanes', 'numClipIntersection', 'depthPacking', 'dithering',
+		'sheen', 'transmissionMap'
 	];
 
 	function getMaxBones( object ) {
@@ -99,7 +99,7 @@ function WebGLPrograms( renderer, cubemaps, extensions, capabilities, bindingSta
 
 		} else if ( map.isWebGLRenderTarget ) {
 
-			console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." );
+			console.warn( 'THREE.WebGLPrograms.getTextureEncodingFromMap: don\'t use render targets as textures. Use their .texture property instead.' );
 			encoding = map.texture.encoding;
 
 		}

+ 2 - 2
src/renderers/webgl/WebGLShadowMap.d.ts

@@ -2,8 +2,8 @@ import { Scene } from './../../scenes/Scene';
 import { Camera } from './../../cameras/Camera';
 import { WebGLRenderer } from '../WebGLRenderer';
 import { ShadowMapType } from '../../constants';
-import { WebGLObjects } from "./WebGLObjects";
-import { Light } from "../../lights/Light";
+import { WebGLObjects } from './WebGLObjects';
+import { Light } from '../../lights/Light';
 
 export class WebGLShadowMap {
 

+ 3 - 3
src/renderers/webgl/WebGLShadowMap.js

@@ -53,7 +53,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 
 	const fullScreenTri = new BufferGeometry();
 	fullScreenTri.setAttribute(
-		"position",
+		'position',
 		new BufferAttribute(
 			new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ),
 			3
@@ -139,7 +139,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 				const pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat };
 
 				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + ".shadowMap";
+				shadow.map.texture.name = light.name + '.shadowMap';
 
 				shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
 
@@ -152,7 +152,7 @@ function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {
 				const pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };
 
 				shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );
-				shadow.map.texture.name = light.name + ".shadowMap";
+				shadow.map.texture.name = light.name + '.shadowMap';
 
 				shadow.camera.updateProjectionMatrix();
 

+ 3 - 3
src/renderers/webgl/WebGLTextures.js

@@ -21,7 +21,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 	try {
 
 		useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'
-			&& ( new OffscreenCanvas( 1, 1 ).getContext( "2d" ) ) !== null;
+			&& ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null;
 
 	} catch ( err ) {
 
@@ -1217,7 +1217,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			if ( warnedTexture2D === false ) {
 
-				console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." );
+				console.warn( 'THREE.WebGLTextures.safeSetTexture2D: don\'t use render targets as textures. Use their .texture property instead.' );
 				warnedTexture2D = true;
 
 			}
@@ -1236,7 +1236,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			if ( warnedTextureCube === false ) {
 
-				console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." );
+				console.warn( 'THREE.WebGLTextures.safeSetTextureCube: don\'t use cube render targets as textures. Use their .texture property instead.' );
 				warnedTextureCube = true;
 
 			}

+ 2 - 2
src/renderers/webxr/WebXRController.js

@@ -171,7 +171,7 @@ Object.assign( WebXRController.prototype, {
 
 							hand.inputState.pinching = false;
 							this.dispatchEvent( {
-								type: "pinchend",
+								type: 'pinchend',
 								handedness: inputSource.handedness,
 								target: this
 							} );
@@ -180,7 +180,7 @@ Object.assign( WebXRController.prototype, {
 
 							hand.inputState.pinching = true;
 							this.dispatchEvent( {
-								type: "pinchstart",
+								type: 'pinchstart',
 								handedness: inputSource.handedness,
 								target: this
 							} );

+ 1 - 1
src/textures/Texture.js

@@ -326,7 +326,7 @@ Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {
 
 } );
 
-Object.defineProperty( Texture.prototype, "needsUpdate", {
+Object.defineProperty( Texture.prototype, 'needsUpdate', {
 
 	set: function ( value ) {
 

+ 1 - 1
test/unit/README.md

@@ -7,4 +7,4 @@
 You can run the unit tests in two environments:
 
 - Node.js: Execute `npm run test-unit` from root folder
-- Browser: Execute `npm run dev-test` and call `http://localhost:8080/test/unit/UnitTests.html` (see [How to run things locally](https://threejs.org/docs/#manual/introduction/How-to-run-things-locally))
+- Browser: Execute `npm start` (or run any other local web sever) from root folder and access `http://localhost:8080/test/unit/UnitTests.html` on web browser. (See [How to run things locally](https://threejs.org/docs/#manual/introduction/How-to-run-things-locally))

+ 81 - 2
test/unit/example/exporters/GLTFExporter.tests.js

@@ -6,6 +6,7 @@ import { AnimationClip } from '../../../../src/animation/AnimationClip';
 import { BoxBufferGeometry } from '../../../../src/geometries/BoxBufferGeometry';
 import { BufferAttribute } from '../../../../src/core/BufferAttribute';
 import { BufferGeometry } from '../../../../src/core/BufferGeometry';
+import { DirectionalLight } from '../../../../src/lights/DirectionalLight';
 import { Mesh } from '../../../../src/objects/Mesh';
 import { MeshBasicMaterial } from '../../../../src/materials/MeshBasicMaterial';
 import { MeshStandardMaterial } from '../../../../src/materials/MeshStandardMaterial';
@@ -39,8 +40,6 @@ export default QUnit.module( 'Exporters', () => {
 
 			exporter.parse( object, function ( gltf ) {
 
-				console.log( gltf );
-
 				assert.equal( '2.0', gltf.asset.version, 'asset.version' );
 				assert.equal( 'GLTFExporter', gltf.asset.generator, 'asset.generator' );
 
@@ -340,6 +339,86 @@ export default QUnit.module( 'Exporters', () => {
 
 		} );
 
+		QUnit.test( 'parse - KHR_lights_punctual extension', ( assert ) => {
+
+			const done = assert.async();
+
+			const scene = new Scene();
+			const light = new DirectionalLight( 0xffffff );
+			light.position.set( 1, 2, 3 );
+			scene.add( light );
+			scene.updateMatrixWorld();
+
+			const exporter = new GLTFExporter();
+
+			exporter.parse( scene, gltf => {
+
+				const extensionName = 'KHR_lights_punctual';
+				const extensionsUsed = gltf.extensionsUsed || [];
+				const extensions = gltf.extensions || {};
+				const lightsDef = extensions[ extensionName ] || {};
+				const lights = lightsDef.lights || [];
+				const lightDef = lights[ 0 ] || {};
+
+				const nodes = gltf.nodes || [];
+				const lightNodeDefsNum = nodes.filter( nodeDef => nodeDef.extensions && nodeDef.extensions[ extensionName ] ).length;
+				const lightNodeDef = nodes.find( nodeDef => nodeDef.extensions && nodeDef.extensions[ extensionName ] ) || {};
+				const lightNodeExtensions = lightNodeDef.extensions || {};
+				const lightNodeExtensionDef = lightNodeExtensions[ extensionName ] || {};
+
+				assert.ok( extensionsUsed.indexOf( extensionName ) >= 0, `${extensionName} exists in extensionsUsed` );
+				assert.equal( 1, lights.length, 'one light' );
+				assert.smartEqual( [ 1, 1, 1 ], lightDef.color, 'correct color' );
+				assert.equal( light.intensity, lightDef.intensity, 'correct intensity' );
+				assert.equal( 'directional', lightDef.type, 'correct type' );
+
+				assert.equal( 1, lightNodeDefsNum, `one node having ${extensionName} extension` );
+				assert.equal( 0, lightNodeExtensionDef.light, 'correct light node index' );
+				assert.smartEqual( light.matrix.elements,
+					lightNodeDef.matrix, 'correct light node transform' );
+
+				// @TODO: Add PointLight and SpotLight tests
+
+				done();
+
+			} );
+
+		} );
+
+		QUnit.test( 'parse - KHR_materials_unlit extension', ( assert ) => {
+
+			const done = assert.async();
+
+			const scene = new Scene();
+			const mesh = new Mesh(
+				new BoxBufferGeometry( 1, 1, 1 ),
+				new MeshBasicMaterial()
+			);
+			scene.add( mesh );
+
+			const exporter = new GLTFExporter();
+
+			exporter.parse( scene, gltf => {
+
+				const extensionName = 'KHR_materials_unlit';
+				const extensionsUsed = gltf.extensionsUsed || [];
+				const materials = gltf.materials || [];
+				const materialDef = materials[ 0 ] || {};
+				const pbrMetallicRoughness = materialDef.pbrMetallicRoughness || {};
+				const extensions = materialDef.extensions || {};
+
+				assert.ok( extensionsUsed.indexOf( extensionName ) >= 0, `${extensionName} exists in extensionsUsed` );
+				assert.equal( 1, materials.length, 'one material' );
+				assert.ok( extensions[ extensionName ], `material has ${extensionName} extension` );
+				assert.equal( 0.0, pbrMetallicRoughness.metallicFactor, 'correct metallicFactor' );
+				assert.equal( 0.9, pbrMetallicRoughness.roughnessFactor, 'correct roughnessFactor' );
+
+				done();
+
+			} );
+
+		} );
+
 	} );
 
 } );

+ 159 - 0
test/unit/src/core/Object3D.tests.js

@@ -732,6 +732,165 @@ export default QUnit.module( 'Core', () => {
 
 		} );
 
+		QUnit.test( "updateWorldMatrix", ( assert ) => {
+
+			const object = new Object3D();
+			const parent = new Object3D();
+			const child = new Object3D();
+
+			const m = new Matrix4();
+			const v = new Vector3();
+
+			parent.add( object );
+			object.add( child );
+
+			parent.position.set( 1, 2, 3 );
+			object.position.set( 4, 5, 6 );
+			child.position.set( 7, 8, 9 );
+
+			// Update the world matrix of an object
+
+			object.updateWorldMatrix();
+
+			assert.deepEqual( parent.matrix.elements,
+				m.elements,
+				"No effect to parents' local matrices" );
+
+			assert.deepEqual( parent.matrixWorld.elements,
+				m.elements,
+				"No effect to parents' world matrices" );
+
+			assert.deepEqual( object.matrix.elements,
+				m.setPosition( object.position ).elements,
+				"Object's local matrix is updated" );
+
+			assert.deepEqual( object.matrixWorld.elements,
+				m.setPosition( object.position ).elements,
+				"Object's world matrix is updated" );
+
+			assert.deepEqual( child.matrix.elements,
+				m.identity().elements,
+				"No effect to children's local matrices" );
+
+			assert.deepEqual( child.matrixWorld.elements,
+				m.elements,
+				"No effect to children's world matrices" );
+
+			// Update the world matrices of an object and its parents
+
+			object.matrix.identity();
+			object.matrixWorld.identity();
+
+			object.updateWorldMatrix( true, false );
+
+			assert.deepEqual( parent.matrix.elements,
+				m.setPosition( parent.position ).elements,
+				"Parents' local matrices are updated" );
+
+			assert.deepEqual( parent.matrixWorld.elements,
+				m.setPosition( parent.position ).elements,
+				"Parents' world matrices are updated" );
+
+			assert.deepEqual( object.matrix.elements,
+				m.setPosition( object.position ).elements,
+				"Object's local matrix is updated" );
+
+			assert.deepEqual( object.matrixWorld.elements,
+				m.setPosition( v.copy( parent.position ).add( object.position ) ).elements,
+				"Object's world matrix is updated" );
+
+			assert.deepEqual( child.matrix.elements,
+				m.identity().elements,
+				"No effect to children's local matrices" );
+
+			assert.deepEqual( child.matrixWorld.elements,
+				m.identity().elements,
+				"No effect to children's world matrices" );
+
+			// Update the world matrices of an object and its children
+
+			parent.matrix.identity();
+			parent.matrixWorld.identity();
+			object.matrix.identity();
+			object.matrixWorld.identity();
+
+			object.updateWorldMatrix( false, true );
+
+			assert.deepEqual( parent.matrix.elements,
+				m.elements,
+				"No effect to parents' local matrices" );
+
+			assert.deepEqual( parent.matrixWorld.elements,
+				m.elements,
+				"No effect to parents' world matrices" );
+
+			assert.deepEqual( object.matrix.elements,
+				m.setPosition( object.position ).elements,
+				"Object's local matrix is updated" );
+
+			assert.deepEqual( object.matrixWorld.elements,
+				m.setPosition( object.position ).elements,
+				"Object's world matrix is updated" );
+
+			assert.deepEqual( child.matrix.elements,
+				m.setPosition( child.position ).elements,
+				"Children's local matrices are updated" );
+
+			assert.deepEqual( child.matrixWorld.elements,
+				m.setPosition( v.copy( object.position ).add( child.position ) ).elements,
+				"Children's world matrices are updated" );
+
+			// Update the world matrices of an object and its parents and children
+
+			object.matrix.identity();
+			object.matrixWorld.identity();
+			child.matrix.identity();
+			child.matrixWorld.identity();
+
+			object.updateWorldMatrix( true, true );
+
+			assert.deepEqual( parent.matrix.elements,
+				m.setPosition( parent.position ).elements,
+				"Parents' local matrices are updated" );
+
+			assert.deepEqual( parent.matrixWorld.elements,
+				m.setPosition( parent.position ).elements,
+				"Parents' world matrices are updated" );
+
+			assert.deepEqual( object.matrix.elements,
+				m.setPosition( object.position ).elements,
+				"Object's local matrix is updated" );
+
+			assert.deepEqual( object.matrixWorld.elements,
+				m.setPosition( v.copy( parent.position ).add( object.position ) ).elements,
+				"Object's world matrix is updated" );
+
+			assert.deepEqual( child.matrix.elements,
+				m.setPosition( child.position ).elements,
+				"Children's local matrices are updated" );
+
+			assert.deepEqual( child.matrixWorld.elements,
+				m.setPosition( v.copy( parent.position ).add( object.position ).add( child.position ) ).elements,
+				"Children's world matrices are updated" );
+
+			// object.matrixAutoUpdate = false test
+
+			object.matrix.identity();
+			object.matrixWorld.identity();
+
+			object.matrixAutoUpdate = false;
+			object.updateWorldMatrix( true, false );
+
+			assert.deepEqual( object.matrix.elements,
+				m.identity().elements,
+				"No effect to object's local matrix if matrixAutoUpdate is false" );
+
+			assert.deepEqual( object.matrixWorld.elements,
+				m.setPosition( parent.position ).elements,
+				"object's world matrix is updated even if matrixAutoUpdate is false" );
+
+		} );
+
 		QUnit.test( "toJSON", ( assert ) => {
 
 			var a = new Object3D();

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