瀏覽代碼

Merge commit 'da9f5a68e48bd855b1acf4a4ea1076e69a8a477a' into feature/FBX-Normal-Index-Fix

Kyle Larson 8 年之前
父節點
當前提交
a9a8398abb

+ 26 - 0
bower.json

@@ -0,0 +1,26 @@
+{
+	"name": "three.js",
+	"homepage": "http://threejs.org/",
+	"description": "JavaScript 3D library",
+	"main": "build/three.js",
+	"keywords": [
+		"three",
+		"threejs",
+		"three.js",
+		"3D",
+		"webgl"
+	],
+	"license": "MIT",
+	"ignore": [
+		"**/.*",
+		"*.md",
+		"/docs",
+		"/editor",
+		"/examples/*",
+		"!/examples/js",
+		"/src",
+		"/test",
+		"/utils",
+		"/LICENSE"
+	]
+}

+ 112 - 94
build/three.js

@@ -1001,7 +1001,6 @@
 		this.flipY = true;
 		this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
 
-
 		// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
 		//
 		// Also changing the encoding after already used by a Material will not automatically make the Material
@@ -1018,13 +1017,13 @@
 
 	Object.defineProperty( Texture.prototype, "needsUpdate", {
 
-		set: function(value) { 
-			
-			if ( value === true ) this.version ++; 
-		
+		set: function ( value ) {
+
+			if ( value === true ) this.version ++;
+
 		}
 
-	});
+	} );
 
 	Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
@@ -4986,7 +4985,7 @@
 
 	var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n";
 
-	var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.r;\n#endif\n";
+	var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
 
 	var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
 
@@ -5008,7 +5007,7 @@
 
 	var project_vertex = "#ifdef USE_SKINNING\n\tvec4 mvPosition = modelViewMatrix * skinned;\n#else\n\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n";
 
-	var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.r;\n#endif\n";
+	var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
 
 	var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
 
@@ -7171,22 +7170,22 @@
 
 	}
 
-	Object.defineProperty( Material.prototype, "needsUpdate", {
+	Object.defineProperty( Material.prototype, 'needsUpdate', {
 
-		get: function() {
+		get: function () {
 
 			return this._needsUpdate;
 
 		},
-		
-		set: function(value) {
+
+		set: function ( value ) {
 
 			if ( value === true ) this.update();
 			this._needsUpdate = value;
 
 		}
 
-	});
+	} );
 
 	Object.assign( Material.prototype, EventDispatcher.prototype, {
 
@@ -11691,15 +11690,15 @@
 
 	}
 
-	Object.defineProperty( BufferAttribute.prototype, "needsUpdate", {
+	Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
+
+		set: function ( value ) {
 
-		set: function(value) { 
-			
 			if ( value === true ) this.version ++;
-		
+
 		}
 
-	});
+	} );
 
 	Object.assign( BufferAttribute.prototype, {
 
@@ -21115,17 +21114,31 @@
 								var groups = geometry.groups;
 								var materials = material.materials;
 
-								for ( var i = 0, l = groups.length; i < l; i ++ ) {
+								if ( groups.length > 0 ) {
+
+									// push a render item for each group of the geometry
 
-									var group = groups[ i ];
-									var groupMaterial = materials[ group.materialIndex ];
+									for ( var i = 0, l = groups.length; i < l; i ++ ) {
 
-									if ( groupMaterial.visible === true ) {
+										var group = groups[ i ];
+										var groupMaterial = materials[ group.materialIndex ];
 
-										pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+										if ( groupMaterial === undefined ) {
+
+											console.warn( 'THREE.WebGLRenderer: MultiMaterial has insufficient amount of materials for geometry. %i material(s) expected but only %i provided.', groups.length, materials.length );
+
+										} else if ( groupMaterial.visible === true ) {
+
+											pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+
+										}
 
 									}
 
+								} else {
+
+									console.warn( 'THREE.WebGLRenderer: MultiMaterial can not be used without groups.' );
+
 								}
 
 							} else {
@@ -24117,7 +24130,7 @@
 
 				if ( groups.length === 0 ) {
 
-					geometry.addGroup( 0, indices.count );
+					groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];
 
 				}
 
@@ -31515,7 +31528,7 @@
 
 		this.name = name;
 		this.tracks = tracks;
-		this.duration = ( duration !== undefined ) ? duration : -1;
+		this.duration = ( duration !== undefined ) ? duration : - 1;
 
 		this.uuid = _Math.generateUUID();
 
@@ -31532,7 +31545,7 @@
 
 	Object.assign( AnimationClip, {
 
-		parse: function( json ) {
+		parse: function ( json ) {
 
 			var tracks = [],
 				jsonTracks = json.tracks,
@@ -31547,8 +31560,8 @@
 			return new AnimationClip( json.name, json.duration, tracks );
 
 		},
-		
-		toJSON: function( clip ) {
+
+		toJSON: function ( clip ) {
 
 			var tracks = [],
 				clipTracks = clip.tracks;
@@ -31570,8 +31583,8 @@
 			return json;
 
 		},
-		
-		CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {
+
+		CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
 
 			var numMorphTargets = morphTargetSequence.length;
 			var tracks = [];
@@ -31606,13 +31619,14 @@
 							'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
 							times, values
 						).scale( 1.0 / fps ) );
+
 			}
 
-			return new AnimationClip( name, -1, tracks );
+			return new AnimationClip( name, - 1, tracks );
 
 		},
 
-		findByName: function( objectOrClipArray, name ) {
+		findByName: function ( objectOrClipArray, name ) {
 
 			var clipArray = objectOrClipArray;
 
@@ -31630,13 +31644,14 @@
 					return clipArray[ i ];
 
 				}
+
 			}
 
 			return null;
 
 		},
 
-		CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) {
+		CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
 
 			var animationToMorphTargets = {};
 
@@ -31681,7 +31696,7 @@
 		},
 
 		// parse the animation.hierarchy format
-		parseAnimation: function( animation, bones ) {
+		parseAnimation: function ( animation, bones ) {
 
 			if ( ! animation ) {
 
@@ -31690,8 +31705,7 @@
 
 			}
 
-			var addNonemptyTrack = function(
-					trackType, trackName, animationKeys, propertyName, destTracks ) {
+			var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
 
 				// only return track if there are actually keys.
 				if ( animationKeys.length !== 0 ) {
@@ -31699,8 +31713,7 @@
 					var times = [];
 					var values = [];
 
-					AnimationUtils.flattenJSON(
-							animationKeys, times, values, propertyName );
+					AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
 
 					// empty keys are filtered out, so check again
 					if ( times.length !== 0 ) {
@@ -31717,7 +31730,7 @@
 
 			var clipName = animation.name || 'default';
 			// automatic length determination in AnimationClip.
-			var duration = animation.length || -1;
+			var duration = animation.length || - 1;
 			var fps = animation.fps || 30;
 
 			var hierarchyTracks = animation.hierarchy || [];
@@ -31731,17 +31744,19 @@
 
 				// process morph targets in a way exactly compatible
 				// with AnimationHandler.init( animation )
-				if ( animationKeys[0].morphTargets ) {
+				if ( animationKeys[ 0 ].morphTargets ) {
 
 					// figure out all morph targets used in this track
 					var morphTargetNames = {};
+
 					for ( var k = 0; k < animationKeys.length; k ++ ) {
 
-						if ( animationKeys[k].morphTargets ) {
+						if ( animationKeys[ k ].morphTargets ) {
+
+							for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
 
-							for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+								morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
 
-								morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
 							}
 
 						}
@@ -31756,22 +31771,23 @@
 						var times = [];
 						var values = [];
 
-						for ( var m = 0; m !== animationKeys[k].morphTargets.length; ++ m ) {
+						for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
 
-							var animationKey = animationKeys[k];
+							var animationKey = animationKeys[ k ];
 
 							times.push( animationKey.time );
 							values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
 
 						}
 
-						tracks.push( new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
+						tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
 
 					}
 
 					duration = morphTargetNames.length * ( fps || 1.0 );
 
 				} else {
+
 					// ...assume skeletal animation
 
 					var boneName = '.bones[' + bones[ h ].name + ']';
@@ -31808,10 +31824,9 @@
 
 	Object.assign( AnimationClip.prototype, {
 
-		resetDuration: function() {
+		resetDuration: function () {
 
-			var tracks = this.tracks,
-				duration = 0;
+			var tracks = this.tracks, duration = 0;
 
 			for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
 
@@ -31825,7 +31840,7 @@
 
 		},
 
-		trim: function() {
+		trim: function () {
 
 			for ( var i = 0; i < this.tracks.length; i ++ ) {
 
@@ -31837,7 +31852,7 @@
 
 		},
 
-		optimize: function() {
+		optimize: function () {
 
 			for ( var i = 0; i < this.tracks.length; i ++ ) {
 
@@ -36192,7 +36207,7 @@
 	Object.assign( PropertyMixer.prototype, {
 
 		// accumulate data in the 'incoming' region into 'accu<i>'
-		accumulate: function( accuIndex, weight ) {
+		accumulate: function ( accuIndex, weight ) {
 
 			// note: happily accumulating nothing when weight = 0, the caller knows
 			// the weight and shouldn't have made the call in the first place
@@ -36230,7 +36245,7 @@
 		},
 
 		// apply the state of 'accu<i>' to the binding when accus differ
-		apply: function( accuIndex ) {
+		apply: function ( accuIndex ) {
 
 			var stride = this.valueSize,
 				buffer = this.buffer,
@@ -36269,7 +36284,7 @@
 		},
 
 		// remember the state of the bound property and copy it to both accus
-		saveOriginalState: function() {
+		saveOriginalState: function () {
 
 			var binding = this.binding;
 
@@ -36292,7 +36307,7 @@
 		},
 
 		// apply the state previously taken via 'saveOriginalState' to the binding
-		restoreOriginalState: function() {
+		restoreOriginalState: function () {
 
 			var originalValueOffset = this.valueSize * 3;
 			this.binding.setValue( this.buffer, originalValueOffset );
@@ -36302,7 +36317,7 @@
 
 		// mix functions
 
-		_select: function( buffer, dstOffset, srcOffset, t, stride ) {
+		_select: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 			if ( t >= 0.5 ) {
 
@@ -36316,14 +36331,13 @@
 
 		},
 
-		_slerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+		_slerp: function ( buffer, dstOffset, srcOffset, t ) {
 
-			Quaternion.slerpFlat( buffer, dstOffset,
-				buffer, dstOffset, buffer, srcOffset, t );
+			Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
 
 		},
 
-		_lerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+		_lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 			var s = 1 - t;
 
@@ -36349,7 +36363,7 @@
 	 * @author tschw
 	 */
 
-	function Composite ( targetGroup, path, optionalParsedPath ) {
+	function Composite( targetGroup, path, optionalParsedPath ) {
 
 		var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
 
@@ -36360,7 +36374,7 @@
 
 	Object.assign( Composite.prototype, {
 
-		getValue: function( array, offset ) {
+		getValue: function ( array, offset ) {
 
 			this.bind(); // bind all binding
 
@@ -36372,7 +36386,7 @@
 
 		},
 
-		setValue: function( array, offset ) {
+		setValue: function ( array, offset ) {
 
 			var bindings = this._bindings;
 
@@ -36385,7 +36399,7 @@
 
 		},
 
-		bind: function() {
+		bind: function () {
 
 			var bindings = this._bindings;
 
@@ -36398,7 +36412,7 @@
 
 		},
 
-		unbind: function() {
+		unbind: function () {
 
 			var bindings = this._bindings;
 
@@ -36429,7 +36443,7 @@
 
 		Composite: Composite,
 
-		create: function( root, path, parsedPath ) {
+		create: function ( root, path, parsedPath ) {
 
 			if ( ! ( root && root.isAnimationObjectGroup ) ) {
 
@@ -36443,7 +36457,7 @@
 
 		},
 
-		parseTrackName: function( trackName ) {
+		parseTrackName: function ( trackName ) {
 
 			// matches strings in the form of:
 			//    nodeName.property
@@ -36485,9 +36499,9 @@
 
 		},
 
-		findNode: function( root, nodeName ) {
+		findNode: function ( root, nodeName ) {
 
-			if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+			if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
 
 				return root;
 
@@ -36496,9 +36510,9 @@
 			// search into skeleton bones.
 			if ( root.skeleton ) {
 
-				var searchSkeleton = function( skeleton ) {
+				var searchSkeleton = function ( skeleton ) {
 
-					for( var i = 0; i < skeleton.bones.length; i ++ ) {
+					for ( var i = 0; i < skeleton.bones.length; i ++ ) {
 
 						var bone = skeleton.bones[ i ];
 
@@ -36507,6 +36521,7 @@
 							return bone;
 
 						}
+
 					}
 
 					return null;
@@ -36520,14 +36535,15 @@
 					return bone;
 
 				}
+
 			}
 
 			// search into node subtree.
 			if ( root.children ) {
 
-				var searchNodeSubtree = function( children ) {
+				var searchNodeSubtree = function ( children ) {
 
-					for( var i = 0; i < children.length; i ++ ) {
+					for ( var i = 0; i < children.length; i ++ ) {
 
 						var childNode = children[ i ];
 
@@ -36566,8 +36582,8 @@
 	Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 		// these are used to "bind" a nonexistent property
-		_getValue_unavailable: function() {},
-		_setValue_unavailable: function() {},
+		_getValue_unavailable: function () {},
+		_setValue_unavailable: function () {},
 
 		BindingType: {
 			Direct: 0,
@@ -36758,7 +36774,7 @@
 		},
 
 		// create getter / setter pair for a property in the scene graph
-		bind: function() {
+		bind: function () {
 
 			var targetObject = this.node,
 				parsedPath = this.parsedPath,
@@ -36904,9 +36920,11 @@
 			var bindingType = this.BindingType.Direct;
 
 			if ( propertyIndex !== undefined ) {
+
 				// access a sub element of the property array (only primitives are supported right now)
 
 				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.
@@ -36943,6 +36961,7 @@
 				this.propertyIndex = propertyIndex;
 
 			} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
+
 				// must use copy for Object3D.Euler/Quaternion
 
 				bindingType = this.BindingType.HasFromToArray;
@@ -36950,7 +36969,7 @@
 				this.resolvedProperty = nodeProperty;
 
 			} else if ( Array.isArray( nodeProperty ) ) {
-	      
+
 				bindingType = this.BindingType.EntireArray;
 
 				this.resolvedProperty = nodeProperty;
@@ -36967,7 +36986,7 @@
 
 		},
 
-		unbind: function() {
+		unbind: function () {
 
 			this.node = null;
 
@@ -37050,7 +37069,7 @@
 
 			objects: {
 				get total() { return scope._objects.length; },
-				get inUse() { return this.total - scope.nCachedObjects_;  }
+				get inUse() { return this.total - scope.nCachedObjects_; }
 			},
 
 			get bindingsPerObject() { return scope._bindings.length; }
@@ -37139,7 +37158,7 @@
 
 					}
 
-				} else if ( objects[ index ] !== knownObject) {
+				} else if ( objects[ index ] !== knownObject ) {
 
 					console.error( "Different objects with the same UUID " +
 							"detected. Clean the caches or recreate your " +
@@ -37287,7 +37306,8 @@
 
 		// Internal interface used by befriended PropertyBinding.Composite:
 
-		subscribe_: function( path, parsedPath ) {
+		subscribe_: function ( path, parsedPath ) {
+
 			// returns an array of bindings for the given path that is changed
 			// according to the contained objects in the group
 
@@ -37312,13 +37332,10 @@
 			parsedPaths.push( parsedPath );
 			bindings.push( bindingsForPath );
 
-			for ( var i = nCachedObjects,
-					n = objects.length; i !== n; ++ i ) {
+			for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {
 
 				var object = objects[ i ];
-
-				bindingsForPath[ i ] =
-						new PropertyBinding( object, path, parsedPath );
+				bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );
 
 			}
 
@@ -37326,7 +37343,8 @@
 
 		},
 
-		unsubscribe_: function( path ) {
+		unsubscribe_: function ( path ) {
+
 			// tells the group to forget about a property path and no longer
 			// update the array previously obtained with 'subscribe_'
 
@@ -38289,7 +38307,7 @@
 
 
 			var actionByRoot = actionsForClip.actionByRoot,
-				rootUuid = ( actions._localRoot || this._root ).uuid;
+				rootUuid = ( action._localRoot || this._root ).uuid;
 
 			delete actionByRoot[ rootUuid ];
 
@@ -38984,15 +39002,15 @@
 
 	}
 
-	Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", {
+	Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
+
+		set: function ( value ) {
+
+			if ( value === true ) this.version ++;
 
-		set: function(value) { 
-			
-			if ( value === true ) this.version ++; 
-		
 		}
 
-	});
+	} );
 
 	Object.assign( InterleavedBuffer.prototype, {
 

文件差異過大導致無法顯示
+ 248 - 247
build/three.min.js


+ 112 - 94
build/three.module.js

@@ -995,7 +995,6 @@ function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, ty
 	this.flipY = true;
 	this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
 
-
 	// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
 	//
 	// Also changing the encoding after already used by a Material will not automatically make the Material
@@ -1012,13 +1011,13 @@ Texture.DEFAULT_MAPPING = UVMapping;
 
 Object.defineProperty( Texture.prototype, "needsUpdate", {
 
-	set: function(value) { 
-		
-		if ( value === true ) this.version ++; 
-	
+	set: function ( value ) {
+
+		if ( value === true ) this.version ++;
+
 	}
 
-});
+} );
 
 Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
@@ -4980,7 +4979,7 @@ var map_particle_fragment = "#ifdef USE_MAP\n\tvec4 mapTexel = texture2D( map, v
 
 var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform vec4 offsetRepeat;\n\tuniform sampler2D map;\n#endif\n";
 
-var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.r;\n#endif\n";
+var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif\n";
 
 var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif";
 
@@ -5002,7 +5001,7 @@ var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.r
 
 var project_vertex = "#ifdef USE_SKINNING\n\tvec4 mvPosition = modelViewMatrix * skinned;\n#else\n\tvec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n";
 
-var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.r;\n#endif\n";
+var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif\n";
 
 var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif";
 
@@ -7165,22 +7164,22 @@ function Material() {
 
 }
 
-Object.defineProperty( Material.prototype, "needsUpdate", {
+Object.defineProperty( Material.prototype, 'needsUpdate', {
 
-	get: function() {
+	get: function () {
 
 		return this._needsUpdate;
 
 	},
-	
-	set: function(value) {
+
+	set: function ( value ) {
 
 		if ( value === true ) this.update();
 		this._needsUpdate = value;
 
 	}
 
-});
+} );
 
 Object.assign( Material.prototype, EventDispatcher.prototype, {
 
@@ -11685,15 +11684,15 @@ function BufferAttribute( array, itemSize, normalized ) {
 
 }
 
-Object.defineProperty( BufferAttribute.prototype, "needsUpdate", {
+Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
+
+	set: function ( value ) {
 
-	set: function(value) { 
-		
 		if ( value === true ) this.version ++;
-	
+
 	}
 
-});
+} );
 
 Object.assign( BufferAttribute.prototype, {
 
@@ -21109,17 +21108,31 @@ function WebGLRenderer( parameters ) {
 							var groups = geometry.groups;
 							var materials = material.materials;
 
-							for ( var i = 0, l = groups.length; i < l; i ++ ) {
+							if ( groups.length > 0 ) {
+
+								// push a render item for each group of the geometry
 
-								var group = groups[ i ];
-								var groupMaterial = materials[ group.materialIndex ];
+								for ( var i = 0, l = groups.length; i < l; i ++ ) {
 
-								if ( groupMaterial.visible === true ) {
+									var group = groups[ i ];
+									var groupMaterial = materials[ group.materialIndex ];
 
-									pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+									if ( groupMaterial === undefined ) {
+
+										console.warn( 'THREE.WebGLRenderer: MultiMaterial has insufficient amount of materials for geometry. %i material(s) expected but only %i provided.', groups.length, materials.length );
+
+									} else if ( groupMaterial.visible === true ) {
+
+										pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+
+									}
 
 								}
 
+							} else {
+
+								console.warn( 'THREE.WebGLRenderer: MultiMaterial can not be used without groups.' );
+
 							}
 
 						} else {
@@ -24111,7 +24124,7 @@ function WireframeGeometry( geometry ) {
 
 			if ( groups.length === 0 ) {
 
-				geometry.addGroup( 0, indices.count );
+				groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];
 
 			}
 
@@ -31509,7 +31522,7 @@ function AnimationClip( name, duration, tracks ) {
 
 	this.name = name;
 	this.tracks = tracks;
-	this.duration = ( duration !== undefined ) ? duration : -1;
+	this.duration = ( duration !== undefined ) ? duration : - 1;
 
 	this.uuid = _Math.generateUUID();
 
@@ -31526,7 +31539,7 @@ function AnimationClip( name, duration, tracks ) {
 
 Object.assign( AnimationClip, {
 
-	parse: function( json ) {
+	parse: function ( json ) {
 
 		var tracks = [],
 			jsonTracks = json.tracks,
@@ -31541,8 +31554,8 @@ Object.assign( AnimationClip, {
 		return new AnimationClip( json.name, json.duration, tracks );
 
 	},
-	
-	toJSON: function( clip ) {
+
+	toJSON: function ( clip ) {
 
 		var tracks = [],
 			clipTracks = clip.tracks;
@@ -31564,8 +31577,8 @@ Object.assign( AnimationClip, {
 		return json;
 
 	},
-	
-	CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {
+
+	CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
 
 		var numMorphTargets = morphTargetSequence.length;
 		var tracks = [];
@@ -31600,13 +31613,14 @@ Object.assign( AnimationClip, {
 						'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
 						times, values
 					).scale( 1.0 / fps ) );
+
 		}
 
-		return new AnimationClip( name, -1, tracks );
+		return new AnimationClip( name, - 1, tracks );
 
 	},
 
-	findByName: function( objectOrClipArray, name ) {
+	findByName: function ( objectOrClipArray, name ) {
 
 		var clipArray = objectOrClipArray;
 
@@ -31624,13 +31638,14 @@ Object.assign( AnimationClip, {
 				return clipArray[ i ];
 
 			}
+
 		}
 
 		return null;
 
 	},
 
-	CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) {
+	CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
 
 		var animationToMorphTargets = {};
 
@@ -31675,7 +31690,7 @@ Object.assign( AnimationClip, {
 	},
 
 	// parse the animation.hierarchy format
-	parseAnimation: function( animation, bones ) {
+	parseAnimation: function ( animation, bones ) {
 
 		if ( ! animation ) {
 
@@ -31684,8 +31699,7 @@ Object.assign( AnimationClip, {
 
 		}
 
-		var addNonemptyTrack = function(
-				trackType, trackName, animationKeys, propertyName, destTracks ) {
+		var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
 
 			// only return track if there are actually keys.
 			if ( animationKeys.length !== 0 ) {
@@ -31693,8 +31707,7 @@ Object.assign( AnimationClip, {
 				var times = [];
 				var values = [];
 
-				AnimationUtils.flattenJSON(
-						animationKeys, times, values, propertyName );
+				AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
 
 				// empty keys are filtered out, so check again
 				if ( times.length !== 0 ) {
@@ -31711,7 +31724,7 @@ Object.assign( AnimationClip, {
 
 		var clipName = animation.name || 'default';
 		// automatic length determination in AnimationClip.
-		var duration = animation.length || -1;
+		var duration = animation.length || - 1;
 		var fps = animation.fps || 30;
 
 		var hierarchyTracks = animation.hierarchy || [];
@@ -31725,17 +31738,19 @@ Object.assign( AnimationClip, {
 
 			// process morph targets in a way exactly compatible
 			// with AnimationHandler.init( animation )
-			if ( animationKeys[0].morphTargets ) {
+			if ( animationKeys[ 0 ].morphTargets ) {
 
 				// figure out all morph targets used in this track
 				var morphTargetNames = {};
+
 				for ( var k = 0; k < animationKeys.length; k ++ ) {
 
-					if ( animationKeys[k].morphTargets ) {
+					if ( animationKeys[ k ].morphTargets ) {
+
+						for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
 
-						for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+							morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
 
-							morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
 						}
 
 					}
@@ -31750,22 +31765,23 @@ Object.assign( AnimationClip, {
 					var times = [];
 					var values = [];
 
-					for ( var m = 0; m !== animationKeys[k].morphTargets.length; ++ m ) {
+					for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
 
-						var animationKey = animationKeys[k];
+						var animationKey = animationKeys[ k ];
 
 						times.push( animationKey.time );
 						values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
 
 					}
 
-					tracks.push( new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
+					tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
 
 				}
 
 				duration = morphTargetNames.length * ( fps || 1.0 );
 
 			} else {
+
 				// ...assume skeletal animation
 
 				var boneName = '.bones[' + bones[ h ].name + ']';
@@ -31802,10 +31818,9 @@ Object.assign( AnimationClip, {
 
 Object.assign( AnimationClip.prototype, {
 
-	resetDuration: function() {
+	resetDuration: function () {
 
-		var tracks = this.tracks,
-			duration = 0;
+		var tracks = this.tracks, duration = 0;
 
 		for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
 
@@ -31819,7 +31834,7 @@ Object.assign( AnimationClip.prototype, {
 
 	},
 
-	trim: function() {
+	trim: function () {
 
 		for ( var i = 0; i < this.tracks.length; i ++ ) {
 
@@ -31831,7 +31846,7 @@ Object.assign( AnimationClip.prototype, {
 
 	},
 
-	optimize: function() {
+	optimize: function () {
 
 		for ( var i = 0; i < this.tracks.length; i ++ ) {
 
@@ -36186,7 +36201,7 @@ function PropertyMixer( binding, typeName, valueSize ) {
 Object.assign( PropertyMixer.prototype, {
 
 	// accumulate data in the 'incoming' region into 'accu<i>'
-	accumulate: function( accuIndex, weight ) {
+	accumulate: function ( accuIndex, weight ) {
 
 		// note: happily accumulating nothing when weight = 0, the caller knows
 		// the weight and shouldn't have made the call in the first place
@@ -36224,7 +36239,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// apply the state of 'accu<i>' to the binding when accus differ
-	apply: function( accuIndex ) {
+	apply: function ( accuIndex ) {
 
 		var stride = this.valueSize,
 			buffer = this.buffer,
@@ -36263,7 +36278,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// remember the state of the bound property and copy it to both accus
-	saveOriginalState: function() {
+	saveOriginalState: function () {
 
 		var binding = this.binding;
 
@@ -36286,7 +36301,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// apply the state previously taken via 'saveOriginalState' to the binding
-	restoreOriginalState: function() {
+	restoreOriginalState: function () {
 
 		var originalValueOffset = this.valueSize * 3;
 		this.binding.setValue( this.buffer, originalValueOffset );
@@ -36296,7 +36311,7 @@ Object.assign( PropertyMixer.prototype, {
 
 	// mix functions
 
-	_select: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_select: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 		if ( t >= 0.5 ) {
 
@@ -36310,14 +36325,13 @@ Object.assign( PropertyMixer.prototype, {
 
 	},
 
-	_slerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_slerp: function ( buffer, dstOffset, srcOffset, t ) {
 
-		Quaternion.slerpFlat( buffer, dstOffset,
-			buffer, dstOffset, buffer, srcOffset, t );
+		Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
 
 	},
 
-	_lerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 		var s = 1 - t;
 
@@ -36343,7 +36357,7 @@ Object.assign( PropertyMixer.prototype, {
  * @author tschw
  */
 
-function Composite ( targetGroup, path, optionalParsedPath ) {
+function Composite( targetGroup, path, optionalParsedPath ) {
 
 	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
 
@@ -36354,7 +36368,7 @@ function Composite ( targetGroup, path, optionalParsedPath ) {
 
 Object.assign( Composite.prototype, {
 
-	getValue: function( array, offset ) {
+	getValue: function ( array, offset ) {
 
 		this.bind(); // bind all binding
 
@@ -36366,7 +36380,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	setValue: function( array, offset ) {
+	setValue: function ( array, offset ) {
 
 		var bindings = this._bindings;
 
@@ -36379,7 +36393,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	bind: function() {
+	bind: function () {
 
 		var bindings = this._bindings;
 
@@ -36392,7 +36406,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	unbind: function() {
+	unbind: function () {
 
 		var bindings = this._bindings;
 
@@ -36423,7 +36437,7 @@ Object.assign( PropertyBinding, {
 
 	Composite: Composite,
 
-	create: function( root, path, parsedPath ) {
+	create: function ( root, path, parsedPath ) {
 
 		if ( ! ( root && root.isAnimationObjectGroup ) ) {
 
@@ -36437,7 +36451,7 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	parseTrackName: function( trackName ) {
+	parseTrackName: function ( trackName ) {
 
 		// matches strings in the form of:
 		//    nodeName.property
@@ -36479,9 +36493,9 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	findNode: function( root, nodeName ) {
+	findNode: function ( root, nodeName ) {
 
-		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
 
 			return root;
 
@@ -36490,9 +36504,9 @@ Object.assign( PropertyBinding, {
 		// search into skeleton bones.
 		if ( root.skeleton ) {
 
-			var searchSkeleton = function( skeleton ) {
+			var searchSkeleton = function ( skeleton ) {
 
-				for( var i = 0; i < skeleton.bones.length; i ++ ) {
+				for ( var i = 0; i < skeleton.bones.length; i ++ ) {
 
 					var bone = skeleton.bones[ i ];
 
@@ -36501,6 +36515,7 @@ Object.assign( PropertyBinding, {
 						return bone;
 
 					}
+
 				}
 
 				return null;
@@ -36514,14 +36529,15 @@ Object.assign( PropertyBinding, {
 				return bone;
 
 			}
+
 		}
 
 		// search into node subtree.
 		if ( root.children ) {
 
-			var searchNodeSubtree = function( children ) {
+			var searchNodeSubtree = function ( children ) {
 
-				for( var i = 0; i < children.length; i ++ ) {
+				for ( var i = 0; i < children.length; i ++ ) {
 
 					var childNode = children[ i ];
 
@@ -36560,8 +36576,8 @@ Object.assign( PropertyBinding, {
 Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 	// these are used to "bind" a nonexistent property
-	_getValue_unavailable: function() {},
-	_setValue_unavailable: function() {},
+	_getValue_unavailable: function () {},
+	_setValue_unavailable: function () {},
 
 	BindingType: {
 		Direct: 0,
@@ -36752,7 +36768,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 	},
 
 	// create getter / setter pair for a property in the scene graph
-	bind: function() {
+	bind: function () {
 
 		var targetObject = this.node,
 			parsedPath = this.parsedPath,
@@ -36898,9 +36914,11 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 		var bindingType = this.BindingType.Direct;
 
 		if ( propertyIndex !== undefined ) {
+
 			// access a sub element of the property array (only primitives are supported right now)
 
 			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.
@@ -36937,6 +36955,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 			this.propertyIndex = propertyIndex;
 
 		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
+
 			// must use copy for Object3D.Euler/Quaternion
 
 			bindingType = this.BindingType.HasFromToArray;
@@ -36944,7 +36963,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 			this.resolvedProperty = nodeProperty;
 
 		} else if ( Array.isArray( nodeProperty ) ) {
-      
+
 			bindingType = this.BindingType.EntireArray;
 
 			this.resolvedProperty = nodeProperty;
@@ -36961,7 +36980,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 	},
 
-	unbind: function() {
+	unbind: function () {
 
 		this.node = null;
 
@@ -37044,7 +37063,7 @@ function AnimationObjectGroup( var_args ) {
 
 		objects: {
 			get total() { return scope._objects.length; },
-			get inUse() { return this.total - scope.nCachedObjects_;  }
+			get inUse() { return this.total - scope.nCachedObjects_; }
 		},
 
 		get bindingsPerObject() { return scope._bindings.length; }
@@ -37133,7 +37152,7 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 				}
 
-			} else if ( objects[ index ] !== knownObject) {
+			} else if ( objects[ index ] !== knownObject ) {
 
 				console.error( "Different objects with the same UUID " +
 						"detected. Clean the caches or recreate your " +
@@ -37281,7 +37300,8 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 	// Internal interface used by befriended PropertyBinding.Composite:
 
-	subscribe_: function( path, parsedPath ) {
+	subscribe_: function ( path, parsedPath ) {
+
 		// returns an array of bindings for the given path that is changed
 		// according to the contained objects in the group
 
@@ -37306,13 +37326,10 @@ Object.assign( AnimationObjectGroup.prototype, {
 		parsedPaths.push( parsedPath );
 		bindings.push( bindingsForPath );
 
-		for ( var i = nCachedObjects,
-				n = objects.length; i !== n; ++ i ) {
+		for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {
 
 			var object = objects[ i ];
-
-			bindingsForPath[ i ] =
-					new PropertyBinding( object, path, parsedPath );
+			bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );
 
 		}
 
@@ -37320,7 +37337,8 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 	},
 
-	unsubscribe_: function( path ) {
+	unsubscribe_: function ( path ) {
+
 		// tells the group to forget about a property path and no longer
 		// update the array previously obtained with 'subscribe_'
 
@@ -38283,7 +38301,7 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 
 
 		var actionByRoot = actionsForClip.actionByRoot,
-			rootUuid = ( actions._localRoot || this._root ).uuid;
+			rootUuid = ( action._localRoot || this._root ).uuid;
 
 		delete actionByRoot[ rootUuid ];
 
@@ -38978,15 +38996,15 @@ function InterleavedBuffer( array, stride ) {
 
 }
 
-Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", {
+Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
+
+	set: function ( value ) {
+
+		if ( value === true ) this.version ++;
 
-	set: function(value) { 
-		
-		if ( value === true ) this.version ++; 
-	
 	}
 
-});
+} );
 
 Object.assign( InterleavedBuffer.prototype, {
 

+ 145 - 145
docs/api/geometries/ExtrudeGeometry.html

@@ -1,145 +1,145 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Geometry] &rarr;
-
-		<h1>[name]</h1>
-
-		<div class="desc">Creates extruded geometry from a path shape.</div>
-
-		<iframe id="scene" src="scenes/geometry-browser.html#ExtrudeGeometry"></iframe>
-
-		<script>
-
-		// iOS iframe auto-resize workaround
-
-		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
-
-			var scene = document.getElementById( 'scene' );
-
-			scene.style.width = getComputedStyle( scene ).width;
-			scene.style.height = getComputedStyle( scene ).height;
-			scene.setAttribute( 'scrolling', 'no' );
-
-		}
-
-		</script>
-
-		<h2>Example</h2>
-
-
-		<code>
-		var length = 12, width = 8;
-
-		var shape = new THREE.Shape();
-		shape.moveTo( 0,0 );
-		shape.lineTo( 0, width );
-		shape.lineTo( length, width );
-		shape.lineTo( length, 0 );
-		shape.lineTo( 0, 0 );
-
-		var extrudeSettings = {
-			steps: 2,
-			amount: 16,
-			bevelEnabled: true,
-			bevelThickness: 1,
-			bevelSize: 1,
-			bevelSegments: 1
-		};
-
-		var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
-		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
-		var mesh = new THREE.Mesh( geometry, material ) ;
-		scene.add( mesh );
-		</code>
-
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]([page:Array shapes], [page:Object options])</h3>
-		<div>
-		shapes — Shape or an array of shapes. <br />
-		options — Object that can contain the following parameters.
-
-			<ul>
-				<li>curveSegments —  int. number of points on the curves</li>
-				<li>steps —  int. number of points used for subdividing segements of extrude spline</li>
-				<li>amount —  int. Depth to extrude the shape</li>
-				<li>bevelEnabled —  bool. turn on bevel</li>
-				<li>bevelThickness —  float. how deep into the original shape bevel goes</li>
-				<li>bevelSize —  float. how far from shape outline is bevel</li>
-				<li>bevelSegments —  int. number of bevel layers</li>
-				<li>extrudePath —  THREE.CurvePath. 3d spline path to extrude shape along. (creates Frames if frames aren't defined)</li>
-				<li>frames —  THREE.TubeGeometry.FrenetFrames.  containing arrays of tangents, normals, binormals</li>
-				<li>material —  int. material index for front and back faces</li>
-				<li>extrudeMaterial —  int. material index for extrusion and beveled faces</li>
-				<li>UVGenerator —  Object. object that provides UV generator functions</li>
-			</ul>
-
-		</div>
-		<div>
-		This object extrudes an 2D shape to an 3D geometry.
-		</div>
-
-
-		<h2>Properties</h2>
-
-
-		<h2>Methods</h2>
-
-		<h3>[method:null addShapeList]([page:Array shapes], [page:Object options])</h3>
-		<div>
-			shapes — An Array of shapes to add. <br />
-			options — Object that can contain the following parameters.
-			<ul>
-				<li>curveSegments —  int. number of points on the curves</li>
-				<li>steps —  int. number of points used for subdividing segements of extrude spline</li>
-				<li>amount —  int. Depth to extrude the shape</li>
-				<li>bevelEnabled —  bool. turn on bevel</li>
-				<li>bevelThickness —  float. how deep into the original shape bevel goes</li>
-				<li>bevelSize —  float. how far from shape outline is bevel</li>
-				<li>bevelSegments —  int. number of bevel layers</li>
-				<li>extrudePath —  THREE.Curve. curve to extrude shape along</li>
-				<li>frames —  Object.  containing arrays of tangents, normals, binormals</li>
-				<li>material —  int. material index for front and back faces</li>
-				<li>extrudeMaterial —  int. material index for extrusion and beveled faces</li>
-				<li>UVGenerator —  Object. object that provides UV generator functions</li>
-			</ul>
-		</div>
-		<div>Adds the shapes to the list to extrude.</div>
-
-		<h3>[method:null addShape]([page:Shape shape], [page:Object options])</h3>
-		<div>
-			shape — A shape to add. <br />
-			options — Object that can contain the following parameters.
-			<ul>
-				<li>curveSegments —  int. number of points on the curves</li>
-				<li>steps —  int. number of points used for subdividing segements of extrude spline</li>
-				<li>amount —  int. Depth to extrude the shape</li>
-				<li>bevelEnabled —  bool. turn on bevel</li>
-				<li>bevelThickness —  float. how deep into the original shape bevel goes</li>
-				<li>bevelSize —  float. how far from shape outline is bevel</li>
-				<li>bevelSegments —  int. number of bevel layers</li>
-				<li>extrudePath —  THREE.Curve. curve to extrude shape along</li>
-				<li>frames —  Object.  containing arrays of tangents, normals, binormals</li>
-				<li>material —  int. material index for front and back faces</li>
-				<li>extrudeMaterial —  int. material index for extrusion and beveled faces</li>
-				<li>UVGenerator —  Object. object that provides UV generator functions</li>
-			</ul>
-		</div>
-		<div>Add the shape to the list to extrude.</div>
-
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Geometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">Creates extruded geometry from a path shape.</div>
+
+		<iframe id="scene" src="scenes/geometry-browser.html#ExtrudeGeometry"></iframe>
+
+		<script>
+
+		// iOS iframe auto-resize workaround
+
+		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
+
+			var scene = document.getElementById( 'scene' );
+
+			scene.style.width = getComputedStyle( scene ).width;
+			scene.style.height = getComputedStyle( scene ).height;
+			scene.setAttribute( 'scrolling', 'no' );
+
+		}
+
+		</script>
+
+		<h2>Example</h2>
+
+
+		<code>
+		var length = 12, width = 8;
+
+		var shape = new THREE.Shape();
+		shape.moveTo( 0,0 );
+		shape.lineTo( 0, width );
+		shape.lineTo( length, width );
+		shape.lineTo( length, 0 );
+		shape.lineTo( 0, 0 );
+
+		var extrudeSettings = {
+			steps: 2,
+			amount: 16,
+			bevelEnabled: true,
+			bevelThickness: 1,
+			bevelSize: 1,
+			bevelSegments: 1
+		};
+
+		var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+		var mesh = new THREE.Mesh( geometry, material ) ;
+		scene.add( mesh );
+		</code>
+
+
+		<h2>Constructor</h2>
+
+
+		<h3>[name]([page:Array shapes], [page:Object options])</h3>
+		<div>
+		shapes — Shape or an array of shapes. <br />
+		options — Object that can contain the following parameters.
+
+			<ul>
+				<li>curveSegments — int. Number of points on the curves. Default is 12.</li>
+				<li>steps — int. Number of points used for subdividing segments along the depth of the extruded spline. Default is 1.</li>
+				<li>amount — int. Depth to extrude the shape. Default is 100.</li>
+				<li>bevelEnabled — bool. Apply beveling to the shape. Default is true.</li>
+				<li>bevelThickness — float. How deep into the original shape the bevel goes. Default is 6.</li>
+				<li>bevelSize — float. Distance from the shape outline that the bevel extends. Default is bevelThickness - 2.</li>
+				<li>bevelSegments — int. Number of bevel layers. Default is 3.</li>
+				<li>extrudePath — THREE.CurvePath. A 3D spline path along which the shape should be extruded (creates Frames if frames aren't defined).</li>
+				<li>frames — An instance of THREE.TubeGeometry.FrenetFrames containing arrays of tangents, normals, binormals for each step along the extrudePath. </li>
+				<li>UVGenerator —  Object. object that provides UV generator functions</li>
+			</ul>
+
+		</div>
+		<div>
+			This object extrudes a 2D shape to a 3D geometry.
+		</div>
+
+		<div>
+			When creating a Mesh with this geometry, if you'd like to have a separate material used for its face 
+			and its extruded sides, you can use an instance of THREE.MultiMaterial. The first material will be 
+			applied to the face; the second material will be applied to the sides.
+		</div>
+
+
+		<h2>Properties</h2>
+
+
+		<h2>Methods</h2>
+
+		<h3>[method:null addShapeList]([page:Array shapes], [page:Object options])</h3>
+		<div>
+			shapes — An Array of shapes to add. <br />
+			options — Object that can contain the following parameters.
+			<ul>
+				<li>curveSegments — int. Number of points on the curves. Default is 12.</li>
+				<li>steps — int. Number of points used for subdividing segments along the depth of the extruded spline. Default is 1.</li>
+				<li>amount — int. Depth to extrude the shape. Default is 100.</li>
+				<li>bevelEnabled — bool. Apply beveling to the shape. Default is true.</li>
+				<li>bevelThickness — float. How deep into the original shape the bevel goes. Default is 6.</li>
+				<li>bevelSize — float. Distance from the shape outline that the bevel extends. Default is bevelThickness - 2.</li>
+				<li>bevelSegments — int. Number of bevel layers. Default is 3.</li>
+				<li>extrudePath — THREE.CurvePath. A 3D spline path along which the shape should be extruded (creates Frames if frames aren't defined).</li>
+				<li>frames — An instance of THREE.TubeGeometry.FrenetFrames containing arrays of tangents, normals, binormals for each step along the extrudePath. </li>
+				<li>UVGenerator —  Object. object that provides UV generator functions</li>
+			</ul>
+		</div>
+		<div>Adds the shapes to the list to extrude.</div>
+
+		<h3>[method:null addShape]([page:Shape shape], [page:Object options])</h3>
+		<div>
+			shape — A shape to add. <br />
+			options — Object that can contain the following parameters.
+			<ul>
+				<li>curveSegments — int. Number of points on the curves. Default is 12.</li>
+				<li>steps — int. Number of points used for subdividing segments along the depth of the extruded spline. Default is 1.</li>
+				<li>amount — int. Depth to extrude the shape. Default is 100.</li>
+				<li>bevelEnabled — bool. Apply beveling to the shape. Default is true.</li>
+				<li>bevelThickness — float. How deep into the original shape the bevel goes. Default is 6.</li>
+				<li>bevelSize — float. Distance from the shape outline that the bevel extends. Default is bevelThickness - 2.</li>
+				<li>bevelSegments — int. Number of bevel layers. Default is 3.</li>
+				<li>extrudePath — THREE.CurvePath. A 3D spline path along which the shape should be extruded (creates Frames if frames aren't defined).</li>
+				<li>frames — An instance of THREE.TubeGeometry.FrenetFrames containing arrays of tangents, normals, binormals for each step along the extrudePath. </li>
+				<li>UVGenerator —  Object. object that provides UV generator functions</li>
+			</ul>
+		</div>
+		<div>Add the shape to the list to extrude.</div>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 2 - 5
docs/index.html

@@ -249,7 +249,6 @@
 			var MEMBER_DELIMITER = '.';
 			var nameCategoryMap = {};
 			var sections = [];
-			var selected = null;
 
 			var content = document.getElementById( 'content' );
 
@@ -339,11 +338,10 @@
 
 				var v = filterInput.value;
 				if( v !== '' ) {
-					window.history.replaceState( {} , '', '?q=' + v );
+					window.history.replaceState( {} , '', '?q=' + v + window.location.hash );
 				} else {
-					window.history.replaceState( {} , '', window.location.pathname );
+					window.history.replaceState( {} , '', window.location.pathname + window.location.hash );
 				}
-				if( selected ) window.location.hash = selected;
 
 				var exp = new RegExp( filterInput.value, 'gi' );
 				for( var j in nameCategoryMap ) {
@@ -400,7 +398,6 @@
 
 				var title = 'three.js - documentation - ' + section + ' - ' + name;
 				var url = encodeUrl(section) + DELIMITER + encodeUrl( category ) + DELIMITER + encodeUrl(name) + (!!member ? MEMBER_DELIMITER + encodeUrl(member) : '');
-				selected = url;
 
 				window.location.hash = url;
 				window.document.title = title;

+ 2 - 3
examples/index.html

@@ -379,11 +379,10 @@
 
 			var v = filterInput.value;
 			if( v !== '' ) {
-				window.history.replaceState( {} , '', '?q=' + v );
+				window.history.replaceState( {} , '', '?q=' + v + window.location.hash );
 			} else {
-				window.history.replaceState( {} , '', window.location.pathname );
+				window.history.replaceState( {} , '', window.location.pathname + window.location.hash );
 			}
-			if( selected ) window.location.hash = selected;
 
 			var exp = new RegExp( v, 'gi' );
 

+ 108 - 122
examples/js/Mirror.js

@@ -28,25 +28,6 @@ THREE.Mirror = function ( renderer, camera, options ) {
 	this.lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
 	this.clipPlane = new THREE.Vector4();
 
-	// For debug only, show the normal and plane of the mirror
-	var debugMode = options.debugMode !== undefined ? options.debugMode : false;
-
-	if ( debugMode ) {
-
-		var arrow = new THREE.ArrowHelper( new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, 0 ), 10, 0xffff80 );
-		var planeGeometry = new THREE.Geometry();
-		planeGeometry.vertices.push( new THREE.Vector3( - 10, - 10, 0 ) );
-		planeGeometry.vertices.push( new THREE.Vector3( 10, - 10, 0 ) );
-		planeGeometry.vertices.push( new THREE.Vector3( 10, 10, 0 ) );
-		planeGeometry.vertices.push( new THREE.Vector3( - 10, 10, 0 ) );
-		planeGeometry.vertices.push( planeGeometry.vertices[ 0 ] );
-		var plane = new THREE.Line( planeGeometry, new THREE.LineBasicMaterial( { color: 0xffff80 } ) );
-
-		this.add( arrow );
-		this.add( plane );
-
-	}
-
 	if ( camera instanceof THREE.PerspectiveCamera ) {
 
 		this.camera = camera;
@@ -136,159 +117,164 @@ THREE.Mirror = function ( renderer, camera, options ) {
 };
 
 THREE.Mirror.prototype = Object.create( THREE.Object3D.prototype );
-THREE.Mirror.prototype.constructor = THREE.Mirror;
 
-THREE.Mirror.prototype.renderWithMirror = function ( otherMirror ) {
+Object.assign( THREE.Mirror.prototype, {
 
-	// update the mirror matrix to mirror the current view
-	this.updateTextureMatrix();
-	this.matrixNeedsUpdate = false;
+	constructor: THREE.Mirror,
 
-	// set the camera of the other mirror so the mirrored view is the reference view
-	var tempCamera = otherMirror.camera;
-	otherMirror.camera = this.mirrorCamera;
+	renderWithMirror: function ( otherMirror ) {
 
-	// render the other mirror in temp texture
-	otherMirror.renderTemp();
-	otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget2.texture;
+		// update the mirror matrix to mirror the current view
+		this.updateTextureMatrix();
+		this.matrixNeedsUpdate = false;
 
-	// render the current mirror
-	this.render();
-	this.matrixNeedsUpdate = true;
+		// set the camera of the other mirror so the mirrored view is the reference view
+		var tempCamera = otherMirror.camera;
+		otherMirror.camera = this.mirrorCamera;
 
-	// restore material and camera of other mirror
-	otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget.texture;
-	otherMirror.camera = tempCamera;
+		// render the other mirror in temp texture
+		otherMirror.renderTemp();
+		otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget2.texture;
 
-	// restore texture matrix of other mirror
-	otherMirror.updateTextureMatrix();
+		// render the current mirror
+		this.render();
+		this.matrixNeedsUpdate = true;
 
-};
+		// restore material and camera of other mirror
+		otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget.texture;
+		otherMirror.camera = tempCamera;
 
-THREE.Mirror.prototype.updateTextureMatrix = function () {
+		// restore texture matrix of other mirror
+		otherMirror.updateTextureMatrix();
 
-	this.updateMatrixWorld();
-	this.camera.updateMatrixWorld();
+	},
 
-	this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld );
-	this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld );
+	updateTextureMatrix: function () {
 
-	this.rotationMatrix.extractRotation( this.matrixWorld );
+		this.updateMatrixWorld();
+		this.camera.updateMatrixWorld();
 
-	this.normal.set( 0, 0, 1 );
-	this.normal.applyMatrix4( this.rotationMatrix );
+		this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld );
+		this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld );
 
-	var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition );
-	view.reflect( this.normal ).negate();
-	view.add( this.mirrorWorldPosition );
+		this.rotationMatrix.extractRotation( this.matrixWorld );
 
-	this.rotationMatrix.extractRotation( this.camera.matrixWorld );
+		this.normal.set( 0, 0, 1 );
+		this.normal.applyMatrix4( this.rotationMatrix );
 
-	this.lookAtPosition.set( 0, 0, - 1 );
-	this.lookAtPosition.applyMatrix4( this.rotationMatrix );
-	this.lookAtPosition.add( this.cameraWorldPosition );
+		var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition );
+		view.reflect( this.normal ).negate();
+		view.add( this.mirrorWorldPosition );
 
-	var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition );
-	target.reflect( this.normal ).negate();
-	target.add( this.mirrorWorldPosition );
+		this.rotationMatrix.extractRotation( this.camera.matrixWorld );
 
-	this.up.set( 0, - 1, 0 );
-	this.up.applyMatrix4( this.rotationMatrix );
-	this.up.reflect( this.normal ).negate();
+		this.lookAtPosition.set( 0, 0, - 1 );
+		this.lookAtPosition.applyMatrix4( this.rotationMatrix );
+		this.lookAtPosition.add( this.cameraWorldPosition );
 
-	this.mirrorCamera.position.copy( view );
-	this.mirrorCamera.up = this.up;
-	this.mirrorCamera.lookAt( target );
+		var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition );
+		target.reflect( this.normal ).negate();
+		target.add( this.mirrorWorldPosition );
 
-	this.mirrorCamera.updateProjectionMatrix();
-	this.mirrorCamera.updateMatrixWorld();
-	this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld );
+		this.up.set( 0, - 1, 0 );
+		this.up.applyMatrix4( this.rotationMatrix );
+		this.up.reflect( this.normal ).negate();
 
-	// Update the texture matrix
-	this.textureMatrix.set(
-		0.5, 0.0, 0.0, 0.5,
-		0.0, 0.5, 0.0, 0.5,
-		0.0, 0.0, 0.5, 0.5,
-		0.0, 0.0, 0.0, 1.0
-	);
-	this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix );
-	this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse );
+		this.mirrorCamera.position.copy( view );
+		this.mirrorCamera.up = this.up;
+		this.mirrorCamera.lookAt( target );
 
-	// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
-	// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
-	this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition );
-	this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse );
+		this.mirrorCamera.updateProjectionMatrix();
+		this.mirrorCamera.updateMatrixWorld();
+		this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld );
 
-	this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant );
+		// Update the texture matrix
+		this.textureMatrix.set(
+			0.5, 0.0, 0.0, 0.5,
+			0.0, 0.5, 0.0, 0.5,
+			0.0, 0.0, 0.5, 0.5,
+			0.0, 0.0, 0.0, 1.0
+		);
+		this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix );
+		this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse );
 
-	var q = new THREE.Vector4();
-	var projectionMatrix = this.mirrorCamera.projectionMatrix;
+		// Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
+		// Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
+		this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition );
+		this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse );
 
-	q.x = ( Math.sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
-	q.y = ( Math.sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
-	q.z = - 1.0;
-	q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
+		this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant );
 
-	// Calculate the scaled plane vector
-	var c = new THREE.Vector4();
-	c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) );
+		var q = new THREE.Vector4();
+		var projectionMatrix = this.mirrorCamera.projectionMatrix;
 
-	// Replacing the third row of the projection matrix
-	projectionMatrix.elements[ 2 ] = c.x;
-	projectionMatrix.elements[ 6 ] = c.y;
-	projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias;
-	projectionMatrix.elements[ 14 ] = c.w;
+		q.x = ( Math.sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
+		q.y = ( Math.sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
+		q.z = - 1.0;
+		q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
 
-};
+		// Calculate the scaled plane vector
+		var c = new THREE.Vector4();
+		c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) );
 
-THREE.Mirror.prototype.render = function () {
+		// Replacing the third row of the projection matrix
+		projectionMatrix.elements[ 2 ] = c.x;
+		projectionMatrix.elements[ 6 ] = c.y;
+		projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias;
+		projectionMatrix.elements[ 14 ] = c.w;
 
-	if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
+	},
 
-	this.matrixNeedsUpdate = true;
+	render: function () {
 
-	// Render the mirrored view of the current scene into the target texture
-	var scene = this;
+		if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
 
-	while ( scene.parent !== null ) {
+		this.matrixNeedsUpdate = true;
 
-		scene = scene.parent;
+		// Render the mirrored view of the current scene into the target texture
+		var scene = this;
 
-	}
+		while ( scene.parent !== null ) {
 
-	if ( scene !== undefined && scene instanceof THREE.Scene ) {
+			scene = scene.parent;
 
-		// We can't render ourself to ourself
-		var visible = this.material.visible;
-		this.material.visible = false;
+		}
 
-		this.renderer.render( scene, this.mirrorCamera, this.renderTarget, true );
+		if ( scene !== undefined && scene instanceof THREE.Scene ) {
 
-		this.material.visible = visible;
+			// We can't render ourself to ourself
+			var visible = this.material.visible;
+			this.material.visible = false;
 
-	}
+			this.renderer.render( scene, this.mirrorCamera, this.renderTarget, true );
 
-};
+			this.material.visible = visible;
 
-THREE.Mirror.prototype.renderTemp = function () {
+		}
 
-	if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
+	},
 
-	this.matrixNeedsUpdate = true;
+	renderTemp: function () {
 
-	// Render the mirrored view of the current scene into the target texture
-	var scene = this;
+		if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
 
-	while ( scene.parent !== null ) {
+		this.matrixNeedsUpdate = true;
 
-		scene = scene.parent;
+		// Render the mirrored view of the current scene into the target texture
+		var scene = this;
 
-	}
+		while ( scene.parent !== null ) {
+
+			scene = scene.parent;
+
+		}
 
-	if ( scene !== undefined && scene instanceof THREE.Scene ) {
+		if ( scene !== undefined && scene instanceof THREE.Scene ) {
 
-		this.renderer.render( scene, this.mirrorCamera, this.renderTarget2, true );
+			this.renderer.render( scene, this.mirrorCamera, this.renderTarget2, true );
+
+		}
 
 	}
 
-};
+} );

二進制
examples/models/obj/cerberus/Cerberus_RM.jpg


+ 2 - 2
examples/webgl_materials_standard.html

@@ -126,8 +126,8 @@
 					material.metalness = 1;
 
 					material.map = loader.load( path + 'Cerberus_A.jpg' );
-					material.roughnessMap = loader.load( path + 'Cerberus_R.jpg' );
-					material.metalnessMap = loader.load( path + 'Cerberus_M.jpg' );
+					// roughness is in G channel, metalness is in B channel 
+					material.metalnessMap = material.roughnessMap = loader.load( path + 'Cerberus_RM.jpg' );
 					material.normalMap = loader.load( path + 'Cerberus_N.jpg' );
 
 					material.map.wrapS = THREE.RepeatWrapping;

+ 1 - 0
package.json

@@ -8,6 +8,7 @@
   "module": "build/three.module.js",
   "files": [
     "package.json",
+    "bower.json",
     "LICENSE",
     "README.md",
     "build/three.js",

+ 29 - 27
src/animation/AnimationClip.js

@@ -17,7 +17,7 @@ function AnimationClip( name, duration, tracks ) {
 
 	this.name = name;
 	this.tracks = tracks;
-	this.duration = ( duration !== undefined ) ? duration : -1;
+	this.duration = ( duration !== undefined ) ? duration : - 1;
 
 	this.uuid = _Math.generateUUID();
 
@@ -34,7 +34,7 @@ function AnimationClip( name, duration, tracks ) {
 
 Object.assign( AnimationClip, {
 
-	parse: function( json ) {
+	parse: function ( json ) {
 
 		var tracks = [],
 			jsonTracks = json.tracks,
@@ -49,8 +49,8 @@ Object.assign( AnimationClip, {
 		return new AnimationClip( json.name, json.duration, tracks );
 
 	},
-	
-	toJSON: function( clip ) {
+
+	toJSON: function ( clip ) {
 
 		var tracks = [],
 			clipTracks = clip.tracks;
@@ -72,8 +72,8 @@ Object.assign( AnimationClip, {
 		return json;
 
 	},
-	
-	CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) {
+
+	CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {
 
 		var numMorphTargets = morphTargetSequence.length;
 		var tracks = [];
@@ -108,13 +108,14 @@ Object.assign( AnimationClip, {
 						'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',
 						times, values
 					).scale( 1.0 / fps ) );
+
 		}
 
-		return new AnimationClip( name, -1, tracks );
+		return new AnimationClip( name, - 1, tracks );
 
 	},
 
-	findByName: function( objectOrClipArray, name ) {
+	findByName: function ( objectOrClipArray, name ) {
 
 		var clipArray = objectOrClipArray;
 
@@ -132,13 +133,14 @@ Object.assign( AnimationClip, {
 				return clipArray[ i ];
 
 			}
+
 		}
 
 		return null;
 
 	},
 
-	CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) {
+	CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {
 
 		var animationToMorphTargets = {};
 
@@ -183,7 +185,7 @@ Object.assign( AnimationClip, {
 	},
 
 	// parse the animation.hierarchy format
-	parseAnimation: function( animation, bones ) {
+	parseAnimation: function ( animation, bones ) {
 
 		if ( ! animation ) {
 
@@ -192,8 +194,7 @@ Object.assign( AnimationClip, {
 
 		}
 
-		var addNonemptyTrack = function(
-				trackType, trackName, animationKeys, propertyName, destTracks ) {
+		var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {
 
 			// only return track if there are actually keys.
 			if ( animationKeys.length !== 0 ) {
@@ -201,8 +202,7 @@ Object.assign( AnimationClip, {
 				var times = [];
 				var values = [];
 
-				AnimationUtils.flattenJSON(
-						animationKeys, times, values, propertyName );
+				AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );
 
 				// empty keys are filtered out, so check again
 				if ( times.length !== 0 ) {
@@ -219,7 +219,7 @@ Object.assign( AnimationClip, {
 
 		var clipName = animation.name || 'default';
 		// automatic length determination in AnimationClip.
-		var duration = animation.length || -1;
+		var duration = animation.length || - 1;
 		var fps = animation.fps || 30;
 
 		var hierarchyTracks = animation.hierarchy || [];
@@ -233,17 +233,19 @@ Object.assign( AnimationClip, {
 
 			// process morph targets in a way exactly compatible
 			// with AnimationHandler.init( animation )
-			if ( animationKeys[0].morphTargets ) {
+			if ( animationKeys[ 0 ].morphTargets ) {
 
 				// figure out all morph targets used in this track
 				var morphTargetNames = {};
+
 				for ( var k = 0; k < animationKeys.length; k ++ ) {
 
-					if ( animationKeys[k].morphTargets ) {
+					if ( animationKeys[ k ].morphTargets ) {
+
+						for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {
 
-						for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) {
+							morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;
 
-							morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1;
 						}
 
 					}
@@ -258,22 +260,23 @@ Object.assign( AnimationClip, {
 					var times = [];
 					var values = [];
 
-					for ( var m = 0; m !== animationKeys[k].morphTargets.length; ++ m ) {
+					for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {
 
-						var animationKey = animationKeys[k];
+						var animationKey = animationKeys[ k ];
 
 						times.push( animationKey.time );
 						values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );
 
 					}
 
-					tracks.push( new NumberKeyframeTrack('.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
+					tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );
 
 				}
 
 				duration = morphTargetNames.length * ( fps || 1.0 );
 
 			} else {
+
 				// ...assume skeletal animation
 
 				var boneName = '.bones[' + bones[ h ].name + ']';
@@ -310,10 +313,9 @@ Object.assign( AnimationClip, {
 
 Object.assign( AnimationClip.prototype, {
 
-	resetDuration: function() {
+	resetDuration: function () {
 
-		var tracks = this.tracks,
-			duration = 0;
+		var tracks = this.tracks, duration = 0;
 
 		for ( var i = 0, n = tracks.length; i !== n; ++ i ) {
 
@@ -327,7 +329,7 @@ Object.assign( AnimationClip.prototype, {
 
 	},
 
-	trim: function() {
+	trim: function () {
 
 		for ( var i = 0; i < this.tracks.length; i ++ ) {
 
@@ -339,7 +341,7 @@ Object.assign( AnimationClip.prototype, {
 
 	},
 
-	optimize: function() {
+	optimize: function () {
 
 		for ( var i = 0; i < this.tracks.length; i ++ ) {
 

+ 1 - 1
src/animation/AnimationMixer.js

@@ -284,7 +284,7 @@ Object.assign( AnimationMixer.prototype, EventDispatcher.prototype, {
 
 
 		var actionByRoot = actionsForClip.actionByRoot,
-			rootUuid = ( actions._localRoot || this._root ).uuid;
+			rootUuid = ( action._localRoot || this._root ).uuid;
 
 		delete actionByRoot[ rootUuid ];
 

+ 8 - 9
src/animation/AnimationObjectGroup.js

@@ -62,7 +62,7 @@ function AnimationObjectGroup( var_args ) {
 
 		objects: {
 			get total() { return scope._objects.length; },
-			get inUse() { return this.total - scope.nCachedObjects_;  }
+			get inUse() { return this.total - scope.nCachedObjects_; }
 		},
 
 		get bindingsPerObject() { return scope._bindings.length; }
@@ -151,7 +151,7 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 				}
 
-			} else if ( objects[ index ] !== knownObject) {
+			} else if ( objects[ index ] !== knownObject ) {
 
 				console.error( "Different objects with the same UUID " +
 						"detected. Clean the caches or recreate your " +
@@ -299,7 +299,8 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 	// Internal interface used by befriended PropertyBinding.Composite:
 
-	subscribe_: function( path, parsedPath ) {
+	subscribe_: function ( path, parsedPath ) {
+
 		// returns an array of bindings for the given path that is changed
 		// according to the contained objects in the group
 
@@ -324,13 +325,10 @@ Object.assign( AnimationObjectGroup.prototype, {
 		parsedPaths.push( parsedPath );
 		bindings.push( bindingsForPath );
 
-		for ( var i = nCachedObjects,
-				n = objects.length; i !== n; ++ i ) {
+		for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {
 
 			var object = objects[ i ];
-
-			bindingsForPath[ i ] =
-					new PropertyBinding( object, path, parsedPath );
+			bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );
 
 		}
 
@@ -338,7 +336,8 @@ Object.assign( AnimationObjectGroup.prototype, {
 
 	},
 
-	unsubscribe_: function( path ) {
+	unsubscribe_: function ( path ) {
+
 		// tells the group to forget about a property path and no longer
 		// update the array previously obtained with 'subscribe_'
 

+ 23 - 18
src/animation/PropertyBinding.js

@@ -8,7 +8,7 @@
  * @author tschw
  */
 
-function Composite ( targetGroup, path, optionalParsedPath ) {
+function Composite( targetGroup, path, optionalParsedPath ) {
 
 	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
 
@@ -19,7 +19,7 @@ function Composite ( targetGroup, path, optionalParsedPath ) {
 
 Object.assign( Composite.prototype, {
 
-	getValue: function( array, offset ) {
+	getValue: function ( array, offset ) {
 
 		this.bind(); // bind all binding
 
@@ -31,7 +31,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	setValue: function( array, offset ) {
+	setValue: function ( array, offset ) {
 
 		var bindings = this._bindings;
 
@@ -44,7 +44,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	bind: function() {
+	bind: function () {
 
 		var bindings = this._bindings;
 
@@ -57,7 +57,7 @@ Object.assign( Composite.prototype, {
 
 	},
 
-	unbind: function() {
+	unbind: function () {
 
 		var bindings = this._bindings;
 
@@ -88,7 +88,7 @@ Object.assign( PropertyBinding, {
 
 	Composite: Composite,
 
-	create: function( root, path, parsedPath ) {
+	create: function ( root, path, parsedPath ) {
 
 		if ( ! ( root && root.isAnimationObjectGroup ) ) {
 
@@ -102,7 +102,7 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	parseTrackName: function( trackName ) {
+	parseTrackName: function ( trackName ) {
 
 		// matches strings in the form of:
 		//    nodeName.property
@@ -144,9 +144,9 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	findNode: function( root, nodeName ) {
+	findNode: function ( root, nodeName ) {
 
-		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) {
+		if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {
 
 			return root;
 
@@ -155,9 +155,9 @@ Object.assign( PropertyBinding, {
 		// search into skeleton bones.
 		if ( root.skeleton ) {
 
-			var searchSkeleton = function( skeleton ) {
+			var searchSkeleton = function ( skeleton ) {
 
-				for( var i = 0; i < skeleton.bones.length; i ++ ) {
+				for ( var i = 0; i < skeleton.bones.length; i ++ ) {
 
 					var bone = skeleton.bones[ i ];
 
@@ -166,6 +166,7 @@ Object.assign( PropertyBinding, {
 						return bone;
 
 					}
+
 				}
 
 				return null;
@@ -179,14 +180,15 @@ Object.assign( PropertyBinding, {
 				return bone;
 
 			}
+
 		}
 
 		// search into node subtree.
 		if ( root.children ) {
 
-			var searchNodeSubtree = function( children ) {
+			var searchNodeSubtree = function ( children ) {
 
-				for( var i = 0; i < children.length; i ++ ) {
+				for ( var i = 0; i < children.length; i ++ ) {
 
 					var childNode = children[ i ];
 
@@ -225,8 +227,8 @@ Object.assign( PropertyBinding, {
 Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 	// these are used to "bind" a nonexistent property
-	_getValue_unavailable: function() {},
-	_setValue_unavailable: function() {},
+	_getValue_unavailable: function () {},
+	_setValue_unavailable: function () {},
 
 	BindingType: {
 		Direct: 0,
@@ -417,7 +419,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 	},
 
 	// create getter / setter pair for a property in the scene graph
-	bind: function() {
+	bind: function () {
 
 		var targetObject = this.node,
 			parsedPath = this.parsedPath,
@@ -563,9 +565,11 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 		var bindingType = this.BindingType.Direct;
 
 		if ( propertyIndex !== undefined ) {
+
 			// access a sub element of the property array (only primitives are supported right now)
 
 			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.
@@ -602,6 +606,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 			this.propertyIndex = propertyIndex;
 
 		} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {
+
 			// must use copy for Object3D.Euler/Quaternion
 
 			bindingType = this.BindingType.HasFromToArray;
@@ -609,7 +614,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 			this.resolvedProperty = nodeProperty;
 
 		} else if ( Array.isArray( nodeProperty ) ) {
-      
+
 			bindingType = this.BindingType.EntireArray;
 
 			this.resolvedProperty = nodeProperty;
@@ -626,7 +631,7 @@ Object.assign( PropertyBinding.prototype, { // prototype, continued
 
 	},
 
-	unbind: function() {
+	unbind: function () {
 
 		this.node = null;
 

+ 8 - 9
src/animation/PropertyMixer.js

@@ -59,7 +59,7 @@ function PropertyMixer( binding, typeName, valueSize ) {
 Object.assign( PropertyMixer.prototype, {
 
 	// accumulate data in the 'incoming' region into 'accu<i>'
-	accumulate: function( accuIndex, weight ) {
+	accumulate: function ( accuIndex, weight ) {
 
 		// note: happily accumulating nothing when weight = 0, the caller knows
 		// the weight and shouldn't have made the call in the first place
@@ -97,7 +97,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// apply the state of 'accu<i>' to the binding when accus differ
-	apply: function( accuIndex ) {
+	apply: function ( accuIndex ) {
 
 		var stride = this.valueSize,
 			buffer = this.buffer,
@@ -136,7 +136,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// remember the state of the bound property and copy it to both accus
-	saveOriginalState: function() {
+	saveOriginalState: function () {
 
 		var binding = this.binding;
 
@@ -159,7 +159,7 @@ Object.assign( PropertyMixer.prototype, {
 	},
 
 	// apply the state previously taken via 'saveOriginalState' to the binding
-	restoreOriginalState: function() {
+	restoreOriginalState: function () {
 
 		var originalValueOffset = this.valueSize * 3;
 		this.binding.setValue( this.buffer, originalValueOffset );
@@ -169,7 +169,7 @@ Object.assign( PropertyMixer.prototype, {
 
 	// mix functions
 
-	_select: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_select: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 		if ( t >= 0.5 ) {
 
@@ -183,14 +183,13 @@ Object.assign( PropertyMixer.prototype, {
 
 	},
 
-	_slerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_slerp: function ( buffer, dstOffset, srcOffset, t ) {
 
-		Quaternion.slerpFlat( buffer, dstOffset,
-			buffer, dstOffset, buffer, srcOffset, t );
+		Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );
 
 	},
 
-	_lerp: function( buffer, dstOffset, srcOffset, t, stride ) {
+	_lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {
 
 		var s = 1 - t;
 

+ 5 - 5
src/core/BufferAttribute.js

@@ -32,15 +32,15 @@ function BufferAttribute( array, itemSize, normalized ) {
 
 }
 
-Object.defineProperty( BufferAttribute.prototype, "needsUpdate", {
+Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {
+
+	set: function ( value ) {
 
-	set: function(value) { 
-		
 		if ( value === true ) this.version ++;
-	
+
 	}
 
-});
+} );
 
 Object.assign( BufferAttribute.prototype, {
 

+ 6 - 6
src/core/InterleavedBuffer.js

@@ -21,15 +21,15 @@ function InterleavedBuffer( array, stride ) {
 
 }
 
-Object.defineProperty( InterleavedBuffer.prototype, "needsUpdate", {
+Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {
+
+	set: function ( value ) {
+
+		if ( value === true ) this.version ++;
 
-	set: function(value) { 
-		
-		if ( value === true ) this.version ++; 
-	
 	}
 
-});
+} );
 
 Object.assign( InterleavedBuffer.prototype, {
 

+ 3 - 3
src/extras/core/Curve.js

@@ -65,7 +65,7 @@ Object.assign( Curve.prototype, {
 
 	getPoints: function ( divisions ) {
 
-		if ( isNaN( divisions ) ) divisions = 5;
+		if ( divisions === undefined ) divisions = 5;
 
 		var points = [];
 
@@ -83,7 +83,7 @@ Object.assign( Curve.prototype, {
 
 	getSpacedPoints: function ( divisions ) {
 
-		if ( isNaN( divisions ) ) divisions = 5;
+		if ( divisions === undefined ) divisions = 5;
 
 		var points = [];
 
@@ -110,7 +110,7 @@ Object.assign( Curve.prototype, {
 
 	getLengths: function ( divisions ) {
 
-		if ( isNaN( divisions ) ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;
+		if ( divisions === undefined ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200;
 
 		if ( this.cacheArcLengths
 			&& ( this.cacheArcLengths.length === divisions + 1 )

+ 1 - 1
src/extras/core/CurvePath.js

@@ -139,7 +139,7 @@ CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {
 
 	getSpacedPoints: function ( divisions ) {
 
-		if ( isNaN( divisions ) ) divisions = 40;
+		if ( divisions === undefined ) divisions = 40;
 
 		var points = [];
 

+ 1 - 1
src/geometries/WireframeGeometry.js

@@ -86,7 +86,7 @@ function WireframeGeometry( geometry ) {
 
 			if ( groups.length === 0 ) {
 
-				geometry.addGroup( 0, indices.count );
+				groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];
 
 			}
 

+ 5 - 5
src/materials/Material.js

@@ -63,22 +63,22 @@ function Material() {
 
 }
 
-Object.defineProperty( Material.prototype, "needsUpdate", {
+Object.defineProperty( Material.prototype, 'needsUpdate', {
 
-	get: function() {
+	get: function () {
 
 		return this._needsUpdate;
 
 	},
-	
-	set: function(value) {
+
+	set: function ( value ) {
 
 		if ( value === true ) this.update();
 		this._needsUpdate = value;
 
 	}
 
-});
+} );
 
 Object.assign( Material.prototype, EventDispatcher.prototype, {
 

+ 19 - 5
src/renderers/WebGLRenderer.js

@@ -1434,17 +1434,31 @@ function WebGLRenderer( parameters ) {
 							var groups = geometry.groups;
 							var materials = material.materials;
 
-							for ( var i = 0, l = groups.length; i < l; i ++ ) {
+							if ( groups.length > 0 ) {
 
-								var group = groups[ i ];
-								var groupMaterial = materials[ group.materialIndex ];
+								// push a render item for each group of the geometry
 
-								if ( groupMaterial.visible === true ) {
+								for ( var i = 0, l = groups.length; i < l; i ++ ) {
 
-									pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+									var group = groups[ i ];
+									var groupMaterial = materials[ group.materialIndex ];
+
+									if ( groupMaterial === undefined ) {
+
+										console.warn( 'THREE.WebGLRenderer: MultiMaterial has insufficient amount of materials for geometry. %i material(s) expected but only %i provided.', groups.length, materials.length );
+
+									} else if ( groupMaterial.visible === true ) {
+
+										pushRenderItem( object, geometry, groupMaterial, _vector3.z, group );
+
+									}
 
 								}
 
+							} else {
+
+								console.warn( 'THREE.WebGLRenderer: MultiMaterial can not be used without groups.' );
+
 							}
 
 						} else {

+ 1 - 0
src/renderers/shaders/ShaderChunk/aomap_fragment.glsl

@@ -1,5 +1,6 @@
 #ifdef USE_AOMAP
 
+	// reads channel R, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
 	float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;
 
 	reflectedLight.indirectDiffuse *= ambientOcclusion;

+ 3 - 1
src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl

@@ -3,6 +3,8 @@ float metalnessFactor = metalness;
 #ifdef USE_METALNESSMAP
 
 	vec4 texelMetalness = texture2D( metalnessMap, vUv );
-	metalnessFactor *= texelMetalness.r;
+
+	// reads channel B, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
+	metalnessFactor *= texelMetalness.b;
 
 #endif

+ 3 - 1
src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl

@@ -3,6 +3,8 @@ float roughnessFactor = roughness;
 #ifdef USE_ROUGHNESSMAP
 
 	vec4 texelRoughness = texture2D( roughnessMap, vUv );
-	roughnessFactor *= texelRoughness.r;
+
+	// reads channel G, compatible with a combined OcclusionRoughnessMetallic (RGB) texture
+	roughnessFactor *= texelRoughness.g;
 
 #endif

+ 5 - 6
src/textures/Texture.js

@@ -44,7 +44,6 @@ function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, ty
 	this.flipY = true;
 	this.unpackAlignment = 4;	// valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)
 
-
 	// Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.
 	//
 	// Also changing the encoding after already used by a Material will not automatically make the Material
@@ -61,13 +60,13 @@ Texture.DEFAULT_MAPPING = UVMapping;
 
 Object.defineProperty( Texture.prototype, "needsUpdate", {
 
-	set: function(value) { 
-		
-		if ( value === true ) this.version ++; 
-	
+	set: function ( value ) {
+
+		if ( value === true ) this.version ++;
+
 	}
 
-});
+} );
 
 Object.assign( Texture.prototype, EventDispatcher.prototype, {
 

部分文件因文件數量過多而無法顯示