Browse Source

Merge branch 'dev' into dev

Mr.doob 8 years ago
parent
commit
96ca9bba3a

+ 1 - 1
docs/api/geometries/RingBufferGeometry.html

@@ -44,7 +44,7 @@
 
 		<h3>[name]([page:Float innerRadius], [page:Float outerRadius], [page:Integer thetaSegments], [page:Integer phiSegments], [page:Float thetaStart], [page:Float thetaLength])</h3>
 		<div>
-		innerRadius — Default is 0, but it doesn't work right when innerRadius is set to 0.<br />
+		innerRadius — Default is 20. <br />
 		outerRadius — Default is 50. <br />
 		thetaSegments — Number of segments.  A higher number means the ring will be more round.  Minimum is 3.  Default is 8. <br />
 		phiSegments — Minimum is 1.  Default is 8.<br />

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

@@ -44,7 +44,7 @@
 
 		<h3>[name]([page:Float innerRadius], [page:Float outerRadius], [page:Integer thetaSegments], [page:Integer phiSegments], [page:Float thetaStart], [page:Float thetaLength])</h3>
 		<div>
-		innerRadius — Default is 0, but it doesn't work right when innerRadius is set to 0.<br />
+		innerRadius — Default is 20. <br />
 		outerRadius — Default is 50. <br />
 		thetaSegments — Number of segments.  A higher number means the ring will be more round.  Minimum is 3.  Default is 8. <br />
 		phiSegments — Minimum is 1.  Default is 8.<br />

+ 6 - 1
docs/api/materials/Material.html

@@ -252,7 +252,12 @@
 		<div>
 		Defines whether this material is visible. Default is *true*.
 		</div>
-
+		
+		<h3>[property:object userData]</h3>
+		<div>
+		An object that can be used to store custom data about the Material. It should not hold
+		references to functions as these will not be cloned.
+		</div>
 
 		<h2>Methods</h2>
 

+ 8 - 7
docs/examples/loaders/GLTF2Loader.html

@@ -19,7 +19,7 @@
 		for efficient delivery and loading of 3D content. Assets may be provided either in JSON (.gltf)
 		or binary (.glb) format. External files store textures (.jpg, .png, ...) and additional binary
 		data (.bin). A glTF asset may deliver one or more scenes, including meshes, materials,
-		textures, shaders, skins, skeletons, morph targets, animations, lights, and/or cameras.
+		textures, skins, skeletons, morph targets, animations, lights, and/or cameras.
 		</div>
 
 		<h2>Extensions</h2>
@@ -30,17 +30,18 @@
 
 		<ul>
 			<li>
-				KHR_lights
+				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness">
+					KHR_materials_pbrSpecularGlossiness
+				</a>
 			</li>
 			<li>
 				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common">
 					KHR_materials_common
 				</a>
+				(experimental)
 			</li>
 			<li>
-				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness">
-					KHR_materials_pbrSpecularGlossiness
-				</a>
+				KHR_lights (experimental)
 			</li>
 		</ul>
 
@@ -91,7 +92,7 @@
 
 		<h3>[method:null setPath]( [page:String path] )</h3>
 		<div>
-		[page:String path] — Base path for loading additional resources e.g. textures, GLSL shaders, .bin data.
+		[page:String path] — Base path for loading additional resources e.g. textures and .bin data.
 		</div>
 		<div>
 		Set the base path for additional resources.
@@ -106,7 +107,7 @@
 		<div>
 		[page:Object json] — <em>JSON</em> object to parse.<br />
 		[page:Function callBack] — Will be called when parse completes.<br />
-		[page:String path] — The base path from which to find subsequent glTF resources such as textures, GLSL shaders and .bin data files.<br />
+		[page:String path] — The base path from which to find subsequent glTF resources such as textures and .bin data files.<br />
 		</div>
 		<div>
 		Parse a glTF-based <em>JSON</em> structure and fire [page:Function callback] when complete. The argument to [page:Function callback] will be an [page:object] that contains loaded parts: .[page:Scene scene], .[page:Array scenes], .[page:Array cameras], and .[page:Array animations].

+ 10 - 5
examples/css3d_molecules.html

@@ -344,13 +344,17 @@
 
 				objects = [];
 
-				loader.load( url, function ( geometry, geometryBonds ) {
+				loader.load( url, function ( pdb ) {
 
-					var offset = geometry.center();
+					var geometryAtoms = pdb.geometryAtoms;
+					var geometryBonds = pdb.geometryBonds;
+					var json = pdb.json;
+
+					var offset = geometryAtoms.center();
 					geometryBonds.translate( offset.x, offset.y, offset.z );
 
-					var positions = geometry.getAttribute( 'position' );
-					var colors = geometry.getAttribute( 'color' );
+					var positions = geometryAtoms.getAttribute( 'position' );
+					var colors = geometryAtoms.getAttribute( 'color' );
 
 					var position = new THREE.Vector3();
 					var color = new THREE.Color();
@@ -365,7 +369,8 @@
 						color.g = colors.getY( i );
 						color.b = colors.getZ( i );
 
-						var element = geometry.elements[ i ];
+						var atom = json.atoms[ i ];
+						var element = atom[ 4 ];
 
 						if ( ! colorSpriteMap[ element ] ) {
 

+ 1 - 0
examples/files.js

@@ -323,6 +323,7 @@ var files = {
 		"misc_lights_test",
 		"misc_lookat",
 		"misc_sound",
+		"misc_sound_visualizer",
 		"misc_ubiquity_test",
 		"misc_ubiquity_test2",
 		"misc_uv_tests"

+ 1 - 1
examples/js/GPUParticleSystem.js

@@ -77,7 +77,7 @@ THREE.GPUParticleSystem = function( options ) {
 			'	v.y = ( velocity.y - 0.5 ) * 3.0;',
 			'	v.z = ( velocity.z - 0.5 ) * 3.0;',
 
-			'	newPosition = positionStart + ( v * 10.0 ) * ( uTime - startTime );',
+			'	newPosition = positionStart + ( v * 10.0 ) * timeElapsed;',
 
 			'	vec3 noise = texture2D( tNoise, vec2( newPosition.x * 0.015 + ( uTime * 0.05 ), newPosition.y * 0.02 + ( uTime * 0.015 ) ) ).rgb;',
 			'	vec3 noiseVel = ( noise.rgb - 0.5 ) * 30.0;',

+ 16 - 10
examples/js/loaders/3MFLoader.js

@@ -1,3 +1,7 @@
+/**
+ * @author technohippy / https://github.com/technohippy
+ */
+
 THREE.ThreeMFLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
@@ -14,9 +18,9 @@ THREE.ThreeMFLoader.prototype = {
 		var scope = this;
 		var loader = new THREE.FileLoader( scope.manager );
 		loader.setResponseType( 'arraybuffer' );
-		loader.load( url, function( text ) {
+		loader.load( url, function( buffer ) {
 
-			onLoad( scope.parse( text ) );
+			onLoad( scope.parse( buffer ) );
 
 		}, onProgress, onError );
 
@@ -51,7 +55,7 @@ THREE.ThreeMFLoader.prototype = {
 
 				if ( e instanceof ReferenceError ) {
 
-					console.log( '	jszip missing and file is compressed.' );
+					console.error( 'THREE.ThreeMFLoader: jszip missing and file is compressed.' );
 					return null;
 
 				}
@@ -95,7 +99,7 @@ THREE.ThreeMFLoader.prototype = {
 
 				if ( TextDecoder === undefined ) {
 
-					console.log( '	TextDecoder not present.	Please use TextDecoder polyfill.' );
+					console.error( 'THREE.ThreeMFLoader: TextDecoder not present. Please use a TextDecoder polyfill.' );
 					return null;
 
 				}
@@ -105,7 +109,7 @@ THREE.ThreeMFLoader.prototype = {
 
 				if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
 
-					console.log( '	Error loading 3MF - no 3MF document found: ' + modelPart );
+					console.error( 'THREE.ThreeMFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
 
 				}
 
@@ -372,7 +376,6 @@ THREE.ThreeMFLoader.prototype = {
 		function parseResourcesNode( resourcesNode ) {
 
 			var resourcesData = {};
-			var geometry, material;
 			var basematerialsNode = resourcesNode.querySelector( 'basematerials' );
 
 			if ( basematerialsNode ) {
@@ -499,8 +502,10 @@ THREE.ThreeMFLoader.prototype = {
 
 		function applyExtensions( extensions, meshData, modelXml, data3mf ) {
 
-			if ( !extensions ) {
+			if ( ! extensions ) {
+
 				return;
+
 			}
 
 			var availableExtensions = [];
@@ -530,6 +535,7 @@ THREE.ThreeMFLoader.prototype = {
 				extension.apply( modelXml, extensions[ extension[ 'ns' ] ], meshData );
 
 			}
+
 		}
 
 		function buildMeshes( data3mf ) {
@@ -590,14 +596,14 @@ THREE.ThreeMFLoader.prototype = {
 		var data3mf = loadDocument( data );
 		var meshes = buildMeshes( data3mf );
 
-		return build( meshes, data3mf[ 'rels' ], data3mf )
+		return build( meshes, data3mf[ 'rels' ], data3mf );
 
 	},
 
-    addExtension: function( extension ) {
+	addExtension: function( extension ) {
 
 		this.availableExtensions.push( extension );
 
-    }
+	}
 
 };

+ 124 - 178
examples/js/loaders/AssimpJSONLoader.js

@@ -27,38 +27,38 @@ THREE.AssimpJSONLoader.prototype = {
 
 		var scope = this;
 
-		this.texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : this.extractUrlBase( url );
+		var path = THREE.Loader.prototype.extractUrlBase( url );
 
 		var loader = new THREE.FileLoader( this.manager );
 		loader.load( url, function ( text ) {
 
-			var json = JSON.parse( text ), scene, metadata;
+			var json = JSON.parse( text );
+			var metadata = json.__metadata__;
+
+			// check if __metadata__ meta header is present
+			// this header is used to disambiguate between different JSON-based file formats
 
-			// Check __metadata__ meta header if present
-			// This header is used to disambiguate between
-			// different JSON-based file formats.
-			metadata = json.__metadata__;
 			if ( typeof metadata !== 'undefined' ) {
 
-				// Check if assimp2json at all
+				// check if assimp2json at all
+
 				if ( metadata.format !== 'assimp2json' ) {
 
-					onError( 'Not an assimp2json scene' );
+					onError( 'THREE.AssimpJSONLoader: Not an assimp2json scene.' );
 					return;
 
-				}
-				// Check major format version
-				else if ( metadata.version < 100 && metadata.version >= 200 ) {
+				// check major format version
+
+				} else if ( metadata.version < 100 && metadata.version >= 200 ) {
 
-					onError( 'Unsupported assimp2json file format version' );
+					onError( 'THREE.AssimpJSONLoader: Unsupported assimp2json file format version.' );
 					return;
 
 				}
 
 			}
 
-			scene = scope.parse( json );
-			onLoad( scene );
+			onLoad( scope.parse( json, path ) );
 
 		}, onProgress, onError );
 
@@ -70,249 +70,195 @@ THREE.AssimpJSONLoader.prototype = {
 
 	},
 
-	setTexturePath: function ( value ) {
+	parse: function ( json, path ) {
 
-		this.texturePath = value;
+		function parseList( json, handler ) {
 
-	},
-
-	extractUrlBase: function ( url ) {
+			var meshes = new Array( json.length );
 
-		// from three/src/loaders/Loader.js
-		var parts = url.split( '/' );
-		parts.pop();
-		return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
-
-	},
+			for ( var i = 0; i < json.length; ++ i ) {
 
-	parse: function ( json ) {
+				meshes[ i ] = handler.call( this, json[ i ] );
 
-		var meshes = this.parseList ( json.meshes, this.parseMesh );
-		var materials = this.parseList ( json.materials, this.parseMaterial );
-		return this.parseObject( json, json.rootnode, meshes, materials );
-
-	},
-
-	parseList : function( json, handler ) {
-
-		var meshes = new Array( json.length );
-		for ( var i = 0; i < json.length; ++ i ) {
+			}
 
-			meshes[ i ] = handler.call( this, json[ i ] );
+			return meshes;
 
 		}
-		return meshes;
-
-	},
 
-	parseMesh : function( json ) {
+		function parseMesh( json ) {
 
-		var geometry = new THREE.BufferGeometry();
+			var geometry = new THREE.BufferGeometry();
 
-		var i, l, face;
+			var i, l, face;
 
-		var indices = [];
+			var indices = [];
 
-		var vertices = json.vertices || [];
-		var normals = json.normals || [];
-		var uvs = json.texturecoords || [];
-		var colors = json.colors || [];
+			var vertices = json.vertices || [];
+			var normals = json.normals || [];
+			var uvs = json.texturecoords || [];
+			var colors = json.colors || [];
 
-		uvs = uvs[ 0 ] || []; // only support for a single set of uvs
+			uvs = uvs[ 0 ] || []; // only support for a single set of uvs
 
-		for ( i = 0, l = json.faces.length; i < l; i ++ ) {
+			for ( i = 0, l = json.faces.length; i < l; i ++ ) {
 
-			face = json.faces[ i ];
-			indices.push( face[ 0 ], face[ 1 ], face[ 2 ] );
+				face = json.faces[ i ];
+				indices.push( face[ 0 ], face[ 1 ], face[ 2 ] );
 
-		}
-
-		geometry.setIndex( indices );
-		geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-
-		if ( normals.length > 0 ) {
-
-			geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
-
-		}
-
-		if ( uvs.length > 0 ) {
-
-			geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+			}
 
-		}
+			geometry.setIndex( indices );
+			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
 
-		if ( colors.length > 0 ) {
+			if ( normals.length > 0 ) {
 
-			geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
+				geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
 
-		}
+			}
 
-		geometry.computeBoundingSphere();
+			if ( uvs.length > 0 ) {
 
-		return geometry;
+				geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
 
-	},
+			}
 
-	parseMaterial : function( json ) {
+			if ( colors.length > 0 ) {
 
-		var mat = null;
-		var scope = this;
-		var i, prop, has_textures = [],
+				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
-		init_props = {};
+			}
 
-		function toColor( value_arr ) {
+			geometry.computeBoundingSphere();
 
-			var col = new THREE.Color();
-			col.setRGB( value_arr[ 0 ], value_arr[ 1 ], value_arr[ 2 ] );
-			return col;
+			return geometry;
 
 		}
 
-		function defaultTexture() {
+		function parseMaterial( json ) {
 
-			var im = new Image();
-			im.width = 1;
-			im.height = 1;
-			return new THREE.Texture( im );
+			var material = new THREE.MeshPhongMaterial();
 
-		}
+			for ( var i in json.properties ) {
 
-		for ( i in json.properties ) {
+				var property = json.properties[ i ];
+				var key = property.key;
+				var value = property.value;
 
-			prop = json.properties[ i ];
+				switch ( key ) {
 
-			if ( prop.key === '$tex.file' ) {
+					case '$tex.file': {
 
-				// prop.semantic gives the type of the texture
-				// 1: diffuse
-				// 2: specular mao
-				// 5: height map (bumps)
-				// 6: normal map
-				// more values (i.e. emissive, environment) are known by assimp and may be relevant
-				if ( prop.semantic === 1 || prop.semantic === 5 || prop.semantic === 6 || prop.semantic === 2 ) {
+						var semantic =  property.semantic;
 
-					( function( semantic ) {
+						// prop.semantic gives the type of the texture
+						// 1: diffuse
+						// 2: specular mao
+						// 5: height map (bumps)
+						// 6: normal map
+						// more values (i.e. emissive, environment) are known by assimp and may be relevant
 
-						var loader = new THREE.TextureLoader( scope.manager ),
-						keyname;
+						if ( semantic === 1 || semantic === 2 || semantic === 5 || semantic === 6 ) {
 
-						if ( semantic === 1 ) {
+							var keyname;
 
-							keyname = 'map';
+							switch ( semantic ) {
 
-						} else if ( semantic === 5 ) {
+								case 1:
+									keyname = 'map';
+									break;
+								case 2:
+									keyname = 'specularMap';
+									break;
+								case 5:
+									keyname = 'bumpMap';
+									break;
+								case 6:
+									keyname = 'normalMap';
+									break;
 
-							keyname = 'bumpMap';
+							}
 
-						} else if ( semantic === 6 ) {
+							var texture = textureLoader.load( value );
 
-							keyname = 'normalMap';
+							// TODO: read texture settings from assimp.
+							// Wrapping is the default, though.
 
-						} else if ( semantic === 2 ) {
+							texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
 
-							keyname = 'specularMap';
+							material[ keyname ] = texture;
 
 						}
 
-						has_textures.push( keyname );
+						break;
 
-						loader.setCrossOrigin( this.crossOrigin );
-						var material_url = scope.texturePath + '/' + prop.value;
-						material_url = material_url.replace( /\\/g, '/' );
-						loader.load( material_url, function( tex ) {
+					}
 
-							if ( tex ) {
+					case '?mat.name':
+						material.name = value;
+						break;
 
-								// TODO: read texture settings from assimp.
-								// Wrapping is the default, though.
-								tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
+					case '$clr.diffuse':
+						material.color.fromArray( value );
+						break;
 
-								mat[ keyname ] = tex;
-								mat.needsUpdate = true;
+					case '$clr.specular':
+						material.specular.fromArray( value );
+						break;
 
-							}
+					case '$clr.emissive':
+						material.emissive.fromArray( value );
+						break;
 
-						} );
+					case '$mat.shininess':
+						material.shininess = value;
+						break;
 
-					} )( prop.semantic );
+					case '$mat.shadingm':
+						// aiShadingMode_Flat
+						material.flatShading = ( value === 1 ) ? true : false;
+						break;
 
 				}
 
-			} else if ( prop.key === '?mat.name' ) {
-
-				init_props.name = prop.value;
-
-			} else if ( prop.key === '$clr.diffuse' ) {
-
-				init_props.color = toColor( prop.value );
-
-			} else if ( prop.key === '$clr.specular' ) {
-
-				init_props.specular = toColor( prop.value );
+			}
 
-			} else if ( prop.key === '$clr.emissive' ) {
+			return material;
 
-				init_props.emissive = toColor( prop.value );
+		}
 
-			} else if ( prop.key === '$mat.shadingm' ) {
+		function parseObject( json, node, meshes, materials ) {
 
-				// aiShadingMode_Flat
-				if ( prop.value === 1 ) {
+			var obj = new THREE.Object3D(),	i, idx;
 
-					init_props.flatShading = true;
+			obj.name = node.name || '';
+			obj.matrix = new THREE.Matrix4().fromArray( node.transformation ).transpose();
+			obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
 
-				}
+			for ( i = 0; node.meshes && i < node.meshes.length; i ++ ) {
 
-			} else if ( prop.key === '$mat.shininess' ) {
-
-				init_props.shininess = prop.value;
+				idx = node.meshes[ i ];
+				obj.add( new THREE.Mesh( meshes[ idx ], materials[ json.meshes[ idx ].materialindex ] ) );
 
 			}
 
-		}
-
-		// note: three.js does not like it when a texture is added after the geometry
-		// has been rendered once, see http://stackoverflow.com/questions/16531759/.
-		// for this reason we fill all slots upfront with default textures
-		if ( has_textures.length ) {
+			for ( i = 0; node.children && i < node.children.length; i ++ ) {
 
-			for ( i = has_textures.length - 1; i >= 0; -- i ) {
-
-				init_props[ has_textures[ i ]] = defaultTexture();
+				obj.add( parseObject( json, node.children[ i ], meshes, materials ) );
 
 			}
 
-		}
-
-		mat = new THREE.MeshPhongMaterial( init_props );
-		return mat;
-
-	},
-
-	parseObject : function( json, node, meshes, materials ) {
-
-		var obj = new THREE.Object3D(),	i, idx;
-
-		obj.name = node.name || "";
-		obj.matrix = new THREE.Matrix4().fromArray( node.transformation ).transpose();
-		obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
-
-		for ( i = 0; node.meshes && i < node.meshes.length; ++ i ) {
-
-			idx = node.meshes[ i ];
-			obj.add( new THREE.Mesh( meshes[ idx ], materials[ json.meshes[ idx ].materialindex ] ) );
+			return obj;
 
 		}
 
-		for ( i = 0; node.children && i < node.children.length; ++ i ) {
+		var textureLoader = new THREE.TextureLoader( this.manager );
+		textureLoader.setPath( path ).setCrossOrigin( this.crossOrigin );
 
-			obj.add( this.parseObject( json, node.children[ i ], meshes, materials ) );
-
-		}
-
-		return obj;
+		var meshes = parseList ( json.meshes, parseMesh );
+		var materials = parseList ( json.materials, parseMaterial );
+		return parseObject( json, json.rootnode, meshes, materials );
 
 	}
+
 };

File diff suppressed because it is too large
+ 836 - 789
examples/js/loaders/AssimpLoader.js


+ 27 - 1
examples/js/loaders/ColladaLoader2.js

@@ -1980,7 +1980,11 @@ THREE.ColladaLoader.prototype = {
 
 				// material
 
-				materialKeys.push( primitive.material );
+				if ( primitive.material ) {
+
+					materialKeys.push( primitive.material );
+
+				}
 
 				// geometry data
 
@@ -3122,6 +3126,24 @@ THREE.ColladaLoader.prototype = {
 
 				var materials = resolveMaterialBinding( geometry.materialKeys, instanceMaterials );
 
+				// handle case if no materials are defined
+
+				if ( materials.length === 0 ) {
+
+					if ( type === 'lines' || type === 'linestrips' ) {
+
+						materials.push( new THREE.LineBasicMaterial() );
+
+					} else {
+
+						materials.push( new THREE.MeshPhongMaterial() );
+
+					}
+
+				}
+
+				// regard skinning
+
 				var skinning = ( geometry.data.attributes.skinIndex !== undefined );
 
 				if ( skinning ) {
@@ -3134,8 +3156,12 @@ THREE.ColladaLoader.prototype = {
 
 				}
 
+				// choose between a single or multi materials (material array)
+
 				var material = ( materials.length === 1 ) ? materials[ 0 ] : materials;
 
+				// now create a specific 3D object
+
 				var object;
 
 				switch ( type ) {

+ 36 - 14
examples/js/loaders/FBXLoader.js

@@ -185,12 +185,12 @@
 	/**
 	 * Parses map of images referenced in FBXTree.
 	 * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
-	 * @returns {Map<number, string(image blob URL)>}
+	 * @returns {Map<number, string(image blob/data URL)>}
 	 */
 	function parseImages( FBXTree ) {
 
 		/**
-		 * @type {Map<number, string(image blob URL)>}
+		 * @type {Map<number, string(image blob/data URL)>}
 		 */
 		var imageMap = new Map();
 
@@ -220,12 +220,11 @@
 
 	/**
 	 * @param {videoNode} videoNode - Node to get texture image information from.
-	 * @returns {string} - image blob URL
+	 * @returns {string} - image blob/data URL
 	 */
 	function parseImage( videoNode ) {
 
-		var buffer = videoNode.properties.Content;
-		var array = new Uint8Array( buffer );
+		var content = videoNode.properties.Content;
 		var fileName = videoNode.properties.RelativeFilename || videoNode.properties.Filename;
 		var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase();
 
@@ -260,7 +259,16 @@
 
 		}
 
-		return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) );
+		if ( typeof content === 'string' ) {
+
+			return 'data:' + type + ';base64,' + content;
+
+		} else {
+
+			var array = new Uint8Array( content );
+			return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) );
+
+		}
 
 	}
 
@@ -268,7 +276,7 @@
 	 * Parses map of textures referenced in FBXTree.
 	 * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
 	 * @param {THREE.TextureLoader} loader
-	 * @param {Map<number, string(image blob URL)>} imageMap
+	 * @param {Map<number, string(image blob/data URL)>} imageMap
 	 * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
 	 * @returns {Map<number, THREE.Texture>}
 	 */
@@ -298,7 +306,7 @@
 	/**
 	 * @param {textureNode} textureNode - Node to get texture information from.
 	 * @param {THREE.TextureLoader} loader
-	 * @param {Map<number, string(image blob URL)>} imageMap
+	 * @param {Map<number, string(image blob/data URL)>} imageMap
 	 * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
 	 * @returns {THREE.Texture}
 	 */
@@ -345,7 +353,7 @@
 
 		var currentPath = loader.path;
 
-		if ( fileName.indexOf( 'blob:' ) === 0 ) {
+		if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) {
 
 			loader.setPath( undefined );
 
@@ -3619,25 +3627,28 @@
 
 			var split = text.split( '\n' );
 
-			for ( var line in split ) {
+			for ( var lineNum = 0, lineLength = split.length; lineNum < lineLength; lineNum ++ ) {
 
-				var l = split[ line ];
+				var l = split[ lineNum ];
 
-				// short cut
+				// skip comment line
 				if ( l.match( /^[\s\t]*;/ ) ) {
 
 					continue;
 
-				} // skip comment line
+				}
+
+				// skip empty line
 				if ( l.match( /^[\s\t]*$/ ) ) {
 
 					continue;
 
-				} // skip empty line
+				}
 
 				// beginning of node
 				var beginningOfNodeExp = new RegExp( '^\\t{' + this.currentIndent + '}(\\w+):(.*){', '' );
 				var match = l.match( beginningOfNodeExp );
+
 				if ( match ) {
 
 					var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' );
@@ -3655,11 +3666,21 @@
 				// node's property
 				var propExp = new RegExp( '^\\t{' + ( this.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' );
 				var match = l.match( propExp );
+
 				if ( match ) {
 
 					var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
 					var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
 
+					// for special case: base64 image data follows "Content: ," line
+					//	Content: ,
+					//	 "iVB..."
+					if ( propName === 'Content' && propValue === ',' ) {
+
+						propValue = split[ ++ lineNum ].replace( /"/g, '' ).trim();
+
+					}
+
 					this.parseNodeProperty( l, propName, propValue );
 					continue;
 
@@ -3667,6 +3688,7 @@
 
 				// end of node
 				var endOfNodeExp = new RegExp( '^\\t{' + ( this.currentIndent - 1 ) + '}}' );
+
 				if ( l.match( endOfNodeExp ) ) {
 
 					this.nodeEnd();

+ 39 - 615
examples/js/loaders/GLTF2Loader.js

@@ -106,12 +106,6 @@ THREE.GLTF2Loader = ( function () {
 
 				}
 
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_TECHNIQUE_WEBGL ) >= 0 ) {
-
-					extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] = new GLTFTechniqueWebglExtension( json );
-
-				}
-
 			}
 
 			console.time( 'GLTF2Loader' );
@@ -194,109 +188,6 @@ THREE.GLTF2Loader = ( function () {
 
 	}
 
-	/* GLTFSHADER */
-
-	function GLTFShader( targetNode, allNodes ) {
-
-		var boundUniforms = {};
-
-		// bind each uniform to its source node
-
-		var uniforms = targetNode.material.uniforms;
-
-		for ( var uniformId in uniforms ) {
-
-			var uniform = uniforms[ uniformId ];
-
-			if ( uniform.semantic ) {
-
-				var sourceNodeRef = uniform.node;
-
-				var sourceNode = targetNode;
-
-				if ( sourceNodeRef ) {
-
-					sourceNode = allNodes[ sourceNodeRef ];
-
-				}
-
-				boundUniforms[ uniformId ] = {
-					semantic: uniform.semantic,
-					sourceNode: sourceNode,
-					targetNode: targetNode,
-					uniform: uniform
-				};
-
-			}
-
-		}
-
-		this.boundUniforms = boundUniforms;
-		this._m4 = new THREE.Matrix4();
-
-	}
-
-	// Update - update all the uniform values
-	GLTFShader.prototype.update = function ( scene, camera ) {
-
-		var boundUniforms = this.boundUniforms;
-
-		for ( var name in boundUniforms ) {
-
-			var boundUniform = boundUniforms[ name ];
-
-			switch ( boundUniform.semantic ) {
-
-				case 'MODELVIEW':
-
-					var m4 = boundUniform.uniform.value;
-					m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
-					break;
-
-				case 'MODELVIEWINVERSETRANSPOSE':
-
-					var m3 = boundUniform.uniform.value;
-					this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
-					m3.getNormalMatrix( this._m4 );
-					break;
-
-				case 'PROJECTION':
-
-					var m4 = boundUniform.uniform.value;
-					m4.copy( camera.projectionMatrix );
-					break;
-
-				case 'JOINTMATRIX':
-
-					var m4v = boundUniform.uniform.value;
-
-					for ( var mi = 0; mi < m4v.length; mi ++ ) {
-
-						// So it goes like this:
-						// SkinnedMesh world matrix is already baked into MODELVIEW;
-						// transform joints to local space,
-						// then transform using joint's inverse
-						m4v[ mi ]
-							.getInverse( boundUniform.sourceNode.matrixWorld )
-							.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld )
-							.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] )
-							.multiply( boundUniform.targetNode.bindMatrix );
-
-					}
-
-					break;
-
-				default :
-
-					console.warn( 'THREE.GLTF2Loader: Unhandled shader semantic: ' + boundUniform.semantic );
-					break;
-
-			}
-
-		}
-
-	};
-
 	/*********************************/
 	/********** EXTENSIONS ***********/
 	/*********************************/
@@ -558,304 +449,6 @@ THREE.GLTF2Loader = ( function () {
 
 	}
 
-	/**
-	 * WebGL Technique Extension
-	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_technique_webgl
-	 */
-	function GLTFTechniqueWebglExtension( json ) {
-
-		this.name = EXTENSIONS.KHR_TECHNIQUE_WEBGL;
-
-		var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] ) || {};
-
-		this.techniques = extension.techniques || {};
-		this.programs = extension.programs || {};
-		this.shaders = extension.shaders || {};
-
-	}
-
-	GLTFTechniqueWebglExtension.prototype.getMaterialType = function () {
-
-		return DeferredShaderMaterial;
-
-	};
-
-	GLTFTechniqueWebglExtension.prototype.extendParams = function ( materialParams, material, dependencies ) {
-
-		var extension = material[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ];
-		var technique = dependencies.techniques[ extension.technique ];
-
-		materialParams.uniforms = {};
-
-		var program = dependencies.programs[ technique.program ];
-
-		if ( program === undefined ) {
-
-			return;
-
-		}
-
-		materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ];
-
-		if ( ! materialParams.fragmentShader ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Missing fragment shader definition: ' + program.fragmentShader );
-
-		}
-
-		var vertexShader = dependencies.shaders[ program.vertexShader ];
-
-		if ( ! vertexShader ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Missing vertex shader definition: ' + program.vertexShader );
-
-		}
-
-		// IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS
-		materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique );
-
-		var uniforms = technique.uniforms;
-
-		for ( var uniformId in uniforms ) {
-
-			var pname = uniforms[ uniformId ];
-			var shaderParam = technique.parameters[ pname ];
-
-			var ptype = shaderParam.type;
-
-			if ( WEBGL_TYPE[ ptype ] ) {
-
-				var pcount = shaderParam.count;
-				var value;
-
-				if ( material.values !== undefined ) value = material.values[ pname ];
-
-				var uvalue = new WEBGL_TYPE[ ptype ]();
-				var usemantic = shaderParam.semantic;
-				var unode = shaderParam.node;
-
-				switch ( ptype ) {
-
-					case WEBGL_CONSTANTS.FLOAT:
-
-						uvalue = shaderParam.value;
-
-						if ( pname === 'transparency' ) {
-
-							materialParams.transparent = true;
-
-						}
-
-						if ( value !== undefined ) {
-
-							uvalue = value;
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_VEC2:
-					case WEBGL_CONSTANTS.FLOAT_VEC3:
-					case WEBGL_CONSTANTS.FLOAT_VEC4:
-					case WEBGL_CONSTANTS.FLOAT_MAT3:
-
-						if ( shaderParam && shaderParam.value ) {
-
-							uvalue.fromArray( shaderParam.value );
-
-						}
-
-						if ( value ) {
-
-							uvalue.fromArray( value );
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_MAT2:
-
-						// what to do?
-						console.warn( 'THREE.GLTF2Loader: FLOAT_MAT2 is not a supported uniform type.' );
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_MAT4:
-
-						if ( pcount ) {
-
-							uvalue = new Array( pcount );
-
-							for ( var mi = 0; mi < pcount; mi ++ ) {
-
-								uvalue[ mi ] = new WEBGL_TYPE[ ptype ]();
-
-							}
-
-							if ( shaderParam && shaderParam.value ) {
-
-								var m4v = shaderParam.value;
-								uvalue.fromArray( m4v );
-
-							}
-
-							if ( value ) {
-
-								uvalue.fromArray( value );
-
-							}
-
-						} else {
-
-							if ( shaderParam && shaderParam.value ) {
-
-								var m4 = shaderParam.value;
-								uvalue.fromArray( m4 );
-
-							}
-
-							if ( value ) {
-
-								uvalue.fromArray( value );
-
-							}
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.SAMPLER_2D:
-
-						if ( value !== undefined ) {
-
-							uvalue = dependencies.textures[ value ];
-
-						} else if ( shaderParam.value !== undefined ) {
-
-							uvalue = dependencies.textures[ shaderParam.value ];
-
-						} else {
-
-							uvalue = null;
-
-						}
-
-						break;
-
-				}
-
-				materialParams.uniforms[ uniformId ] = {
-					value: uvalue,
-					semantic: usemantic,
-					node: unode
-				};
-
-			} else {
-
-				throw new Error( 'THREE.GLTF2Loader: Unknown shader uniform param type: ' + ptype );
-
-			}
-
-		}
-
-		var states = technique.states || {};
-		var enables = states.enable || [];
-		var functions = states.functions || {};
-
-		var enableCullFace = false;
-		var enableDepthTest = false;
-		var enableBlend = false;
-
-		for ( var i = 0, il = enables.length; i < il; i ++ ) {
-
-			var enable = enables[ i ];
-
-			switch ( STATES_ENABLES[ enable ] ) {
-
-				case 'CULL_FACE':
-
-					enableCullFace = true;
-
-					break;
-
-				case 'DEPTH_TEST':
-
-					enableDepthTest = true;
-
-					break;
-
-				case 'BLEND':
-
-					enableBlend = true;
-
-					break;
-
-				// TODO: implement
-				case 'SCISSOR_TEST':
-				case 'POLYGON_OFFSET_FILL':
-				case 'SAMPLE_ALPHA_TO_COVERAGE':
-
-					break;
-
-				default:
-
-					throw new Error( 'THREE.GLTF2Loader: Unknown technique.states.enable: ' + enable );
-
-			}
-
-		}
-
-		if ( enableCullFace ) {
-
-			materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide;
-
-		} else {
-
-			materialParams.side = THREE.DoubleSide;
-
-		}
-
-		materialParams.depthTest = enableDepthTest;
-		materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth;
-		materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true;
-
-		materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending;
-		materialParams.transparent = enableBlend;
-
-		var blendEquationSeparate = functions.blendEquationSeparate;
-
-		if ( blendEquationSeparate !== undefined ) {
-
-			materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ];
-			materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ];
-
-		} else {
-
-			materialParams.blendEquation = THREE.AddEquation;
-			materialParams.blendEquationAlpha = THREE.AddEquation;
-
-		}
-
-		var blendFuncSeparate = functions.blendFuncSeparate;
-
-		if ( blendFuncSeparate !== undefined ) {
-
-			materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ];
-			materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ];
-			materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ];
-			materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ];
-
-		} else {
-
-			materialParams.blendSrc = THREE.OneFactor;
-			materialParams.blendDst = THREE.ZeroFactor;
-			materialParams.blendSrcAlpha = THREE.OneFactor;
-			materialParams.blendDstAlpha = THREE.ZeroFactor;
-
-		}
-
-	};
-
 	/**
 	 * Specular-Glossiness Extension
 	 *
@@ -1181,13 +774,15 @@ THREE.GLTF2Loader = ( function () {
 		LINEAR: 9729,
 		REPEAT: 10497,
 		SAMPLER_2D: 35678,
-		TRIANGLES: 4,
+		POINTS: 0,
 		LINES: 1,
+		LINE_LOOP: 2,
+		LINE_STRIP: 3,
+		TRIANGLES: 4,
+		TRIANGLE_STRIP: 5,
+		TRIANGLE_FAN: 6,
 		UNSIGNED_BYTE: 5121,
-		UNSIGNED_SHORT: 5123,
-
-		VERTEX_SHADER: 35633,
-		FRAGMENT_SHADER: 35632
+		UNSIGNED_SHORT: 5123
 	};
 
 	var WEBGL_TYPE = {
@@ -1463,105 +1058,6 @@ THREE.GLTF2Loader = ( function () {
 
 	}
 
-	// Three.js seems too dependent on attribute names so globally
-	// replace those in the shader code
-	function replaceTHREEShaderAttributes( shaderText, technique ) {
-
-		// Expected technique attributes
-		var attributes = {};
-
-		for ( var attributeId in technique.attributes ) {
-
-			var pname = technique.attributes[ attributeId ];
-
-			var param = technique.parameters[ pname ];
-			var atype = param.type;
-			var semantic = param.semantic;
-
-			attributes[ attributeId ] = {
-				type: atype,
-				semantic: semantic
-			};
-
-		}
-
-		// Figure out which attributes to change in technique
-
-		var shaderParams = technique.parameters;
-		var shaderAttributes = technique.attributes;
-		var params = {};
-
-		for ( var attributeId in attributes ) {
-
-			var pname = shaderAttributes[ attributeId ];
-			var shaderParam = shaderParams[ pname ];
-			var semantic = shaderParam.semantic;
-			if ( semantic ) {
-
-				params[ attributeId ] = shaderParam;
-
-			}
-
-		}
-
-		for ( var pname in params ) {
-
-			var param = params[ pname ];
-			var semantic = param.semantic;
-
-			var regEx = new RegExp( '\\b' + pname + '\\b', 'g' );
-
-			switch ( semantic ) {
-
-				case 'POSITION':
-
-					shaderText = shaderText.replace( regEx, 'position' );
-					break;
-
-				case 'NORMAL':
-
-					shaderText = shaderText.replace( regEx, 'normal' );
-					break;
-
-				case 'TEXCOORD_0':
-				case 'TEXCOORD0':
-				case 'TEXCOORD':
-
-					shaderText = shaderText.replace( regEx, 'uv' );
-					break;
-
-				case 'TEXCOORD_1':
-
-					shaderText = shaderText.replace( regEx, 'uv2' );
-					break;
-
-				case 'COLOR_0':
-				case 'COLOR0':
-				case 'COLOR':
-
-					shaderText = shaderText.replace( regEx, 'color' );
-					break;
-
-				case 'WEIGHTS_0':
-				case 'WEIGHT': // WEIGHT semantic deprecated.
-
-					shaderText = shaderText.replace( regEx, 'skinWeight' );
-					break;
-
-				case 'JOINTS_0':
-				case 'JOINT': // JOINT semantic deprecated.
-
-					shaderText = shaderText.replace( regEx, 'skinIndex' );
-					break;
-
-			}
-
-		}
-
-		return shaderText;
-
-	}
-
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
 	 */
@@ -1698,41 +1194,6 @@ THREE.GLTF2Loader = ( function () {
 
 	}
 
-	// Deferred constructor for RawShaderMaterial types
-	function DeferredShaderMaterial( params ) {
-
-		this.isDeferredShaderMaterial = true;
-
-		this.params = params;
-
-	}
-
-	DeferredShaderMaterial.prototype.create = function () {
-
-		var uniforms = THREE.UniformsUtils.clone( this.params.uniforms );
-
-		for ( var uniformId in this.params.uniforms ) {
-
-			var originalUniform = this.params.uniforms[ uniformId ];
-
-			if ( originalUniform.value instanceof THREE.Texture ) {
-
-				uniforms[ uniformId ].value = originalUniform.value;
-				uniforms[ uniformId ].value.needsUpdate = true;
-
-			}
-
-			uniforms[ uniformId ].semantic = originalUniform.semantic;
-			uniforms[ uniformId ].node = originalUniform.node;
-
-		}
-
-		this.params.uniforms = uniforms;
-
-		return new THREE.RawShaderMaterial( this.params );
-
-	};
-
 	/* GLTF PARSER */
 
 	function GLTFParser( json, extensions, options ) {
@@ -1829,50 +1290,6 @@ THREE.GLTF2Loader = ( function () {
 
 	};
 
-	GLTFParser.prototype.loadShaders = function () {
-
-		var json = this.json;
-		var options = this.options;
-		var extensions = this.extensions;
-
-		return this._withDependencies( [
-
-			'bufferViews'
-
-		] ).then( function ( dependencies ) {
-
-			var shaders = extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] !== undefined ? extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].shaders : json.shaders;
-
-			if ( shaders === undefined ) shaders = {};
-
-			return _each( shaders, function ( shader ) {
-
-				if ( shader.bufferView !== undefined ) {
-
-					var bufferView = dependencies.bufferViews[ shader.bufferView ];
-					var array = new Uint8Array( bufferView );
-					return convertUint8ArrayToString( array );
-
-				}
-
-				return new Promise( function ( resolve ) {
-
-					var loader = new THREE.FileLoader();
-					loader.setResponseType( 'text' );
-					loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) {
-
-						resolve( shaderText );
-
-					} );
-
-				} );
-
-			} );
-
-		} );
-
-	};
-
 	GLTFParser.prototype.loadBuffers = function () {
 
 		var json = this.json;
@@ -2081,7 +1498,6 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			'shaders',
 			'textures'
 
 		] ).then( function ( dependencies ) {
@@ -2227,6 +1643,8 @@ THREE.GLTF2Loader = ( function () {
 				// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture
 				_material.normalScale.x = -1;
 
+				_material.userData = material.extras;
+
 				return _material;
 
 			} );
@@ -2237,8 +1655,6 @@ THREE.GLTF2Loader = ( function () {
 
 	GLTFParser.prototype.loadGeometries = function ( primitives ) {
 
-		var extensions = this.extensions;
-
 		return this._withDependencies( [
 
 			'accessors',
@@ -2393,13 +1809,35 @@ THREE.GLTF2Loader = ( function () {
 
 							mesh = new THREE.Mesh( geometry, material );
 
+						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.drawMode = THREE.TriangleStripDrawMode;
+
+						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
+
+							mesh = new THREE.Mesh( geometry, material );
+							mesh.drawMode = THREE.TriangleFanDrawMode;
+
 						} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
 
 							mesh = new THREE.LineSegments( geometry, material );
 
+						} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
+
+							mesh = new THREE.Line( geometry, material );
+
+						} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+
+							mesh = new THREE.LineLoop( geometry, material );
+
+						} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+
+							mesh = new THREE.Points( geometry, material );
+
 						} else {
 
-							throw new Error( 'THREE.GLTF2Loader: Only TRIANGLE and LINE primitives are supported.' );
+							throw new Error( 'THREE.GLTF2Loader: Primitive mode unsupported: ', primitive.mode );
 
 						}
 
@@ -2731,6 +2169,7 @@ THREE.GLTF2Loader = ( function () {
 							for ( var childrenId in group.children ) {
 
 								var child = group.children[ childrenId ];
+								var originalChild = child;
 
 								// clone Mesh to add to _node
 
@@ -2740,17 +2179,7 @@ THREE.GLTF2Loader = ( function () {
 								var originalUserData = child.userData;
 								var originalName = child.name;
 
-								var material;
-
-								if ( originalMaterial.isDeferredShaderMaterial ) {
-
-									originalMaterial = material = originalMaterial.create();
-
-								} else {
-
-									material = originalMaterial;
-
-								}
+								var material = originalMaterial;
 
 								switch ( child.type ) {
 
@@ -2766,8 +2195,13 @@ THREE.GLTF2Loader = ( function () {
 										child = new THREE.Line( originalGeometry, material );
 										break;
 
+									case 'Points':
+										child = new THREE.Points( originalGeometry, material );
+										break;
+
 									default:
 										child = new THREE.Mesh( originalGeometry, material );
+										child.drawMode = originalChild.drawMode;
 
 								}
 
@@ -2914,16 +2348,6 @@ THREE.GLTF2Loader = ( function () {
 
 				_scene.traverse( function ( child ) {
 
-					// Register raw material meshes with GLTF2Loader.Shaders
-					if ( child.material && child.material.isRawShaderMaterial ) {
-
-						child.gltfShader = new GLTFShader( child, dependencies.nodes );
-						child.onBeforeRender = function(renderer, scene, camera){
-							this.gltfShader.update(scene, camera);
-						};
-
-					}
-
 					// for Specular-Glossiness.
 					if ( child.material && child.material.isGLTFSpecularGlossinessMaterial ) {
 

+ 57 - 135
examples/js/loaders/OBJLoader.js

@@ -4,20 +4,6 @@
 
 THREE.OBJLoader = ( function () {
 
-	// v float float float
-	var vertex_pattern           = /^v\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
-	// vn float float float
-	var normal_pattern           = /^vn\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
-	// vt float float
-	var uv_pattern               = /^vt\s+([\d\.\+\-eE]+)\s+([\d\.\+\-eE]+)/;
-	// f vertex vertex vertex
-	var face_vertex              = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/;
-	// f vertex/uv vertex/uv vertex/uv
-	var face_vertex_uv           = /^f\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+))?/;
-	// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
-	var face_vertex_uv_normal    = /^f\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)\s+(-?\d+)\/(-?\d+)\/(-?\d+)(?:\s+(-?\d+)\/(-?\d+)\/(-?\d+))?/;
-	// f vertex//normal vertex//normal vertex//normal
-	var face_vertex_normal       = /^f\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)\s+(-?\d+)\/\/(-?\d+)(?:\s+(-?\d+)\/\/(-?\d+))?/;
 	// o object_name | g group_name
 	var object_pattern           = /^[og]\s*(.+)?/;
 	// mtllib file_reference
@@ -69,7 +55,7 @@ THREE.OBJLoader = ( function () {
 					materials : [],
 					smooth : true,
 
-					startMaterial : function( name, libraries ) {
+					startMaterial: function ( name, libraries ) {
 
 						var previous = this._finalize( false );
 
@@ -91,7 +77,7 @@ THREE.OBJLoader = ( function () {
 							groupCount : -1,
 							inherited  : false,
 
-							clone : function( index ) {
+							clone: function ( index ) {
 								var cloned = {
 									index      : ( typeof index === 'number' ? index : this.index ),
 									name       : this.name,
@@ -113,7 +99,7 @@ THREE.OBJLoader = ( function () {
 
 					},
 
-					currentMaterial : function() {
+					currentMaterial: function () {
 
 						if ( this.materials.length > 0 ) {
 							return this.materials[ this.materials.length - 1 ];
@@ -123,7 +109,7 @@ THREE.OBJLoader = ( function () {
 
 					},
 
-					_finalize : function( end ) {
+					_finalize: function ( end ) {
 
 						var lastMultiMaterial = this.currentMaterial();
 						if ( lastMultiMaterial && lastMultiMaterial.groupEnd === -1 ) {
@@ -166,7 +152,7 @@ THREE.OBJLoader = ( function () {
 				// overwrite the inherited material. Exception being that there was already face declarations
 				// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
 
-				if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === "function" ) {
+				if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
 
 					var declared = previousMaterial.clone( 0 );
 					declared.inherited = true;
@@ -178,7 +164,7 @@ THREE.OBJLoader = ( function () {
 
 			},
 
-			finalize : function() {
+			finalize: function () {
 
 				if ( this.object && typeof this.object._finalize === 'function' ) {
 
@@ -229,7 +215,7 @@ THREE.OBJLoader = ( function () {
 
 			},
 
-			addNormal : function ( a, b, c ) {
+			addNormal: function ( a, b, c ) {
 
 				var src = this.normals;
 				var dst = this.object.geometry.normals;
@@ -260,27 +246,15 @@ THREE.OBJLoader = ( function () {
 
 			},
 
-			addFace: function ( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) {
+			addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
 
 				var vLen = this.vertices.length;
 
 				var ia = this.parseVertexIndex( a, vLen );
 				var ib = this.parseVertexIndex( b, vLen );
 				var ic = this.parseVertexIndex( c, vLen );
-				var id;
 
-				if ( d === undefined ) {
-
-					this.addVertex( ia, ib, ic );
-
-				} else {
-
-					id = this.parseVertexIndex( d, vLen );
-
-					this.addVertex( ia, ib, id );
-					this.addVertex( ib, ic, id );
-
-				}
+				this.addVertex( ia, ib, ic );
 
 				if ( ua !== undefined ) {
 
@@ -290,18 +264,7 @@ THREE.OBJLoader = ( function () {
 					ib = this.parseUVIndex( ub, uvLen );
 					ic = this.parseUVIndex( uc, uvLen );
 
-					if ( d === undefined ) {
-
-						this.addUV( ia, ib, ic );
-
-					} else {
-
-						id = this.parseUVIndex( ud, uvLen );
-
-						this.addUV( ia, ib, id );
-						this.addUV( ib, ic, id );
-
-					}
+					this.addUV( ia, ib, ic );
 
 				}
 
@@ -314,18 +277,7 @@ THREE.OBJLoader = ( function () {
 					ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
 					ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
 
-					if ( d === undefined ) {
-
-						this.addNormal( ia, ib, ic );
-
-					} else {
-
-						id = this.parseNormalIndex( nd, nLen );
-
-						this.addNormal( ia, ib, id );
-						this.addNormal( ib, ic, id );
-
-					}
+					this.addNormal( ia, ib, ic );
 
 				}
 
@@ -423,7 +375,7 @@ THREE.OBJLoader = ( function () {
 			}
 
 			var lines = text.split( '\n' );
-			var line = '', lineFirstChar = '', lineSecondChar = '';
+			var line = '', lineFirstChar = '';
 			var lineLength = 0;
 			var result = [];
 
@@ -447,100 +399,70 @@ THREE.OBJLoader = ( function () {
 
 				if ( lineFirstChar === 'v' ) {
 
-					lineSecondChar = line.charAt( 1 );
-
-					if ( lineSecondChar === ' ' && ( result = vertex_pattern.exec( line ) ) !== null ) {
-
-						// 0                  1      2      3
-						// ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
-
-						state.vertices.push(
-							parseFloat( result[ 1 ] ),
-							parseFloat( result[ 2 ] ),
-							parseFloat( result[ 3 ] )
-						);
+					var data = line.split( /\s+/ );
+
+					switch ( data[ 0 ] ) {
+
+						case 'v':
+							state.vertices.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] ),
+								parseFloat( data[ 3 ] )
+							);
+							break;
+						case 'vn':
+							state.normals.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] ),
+								parseFloat( data[ 3 ] )
+							);
+						case 'vt':
+							state.uvs.push(
+								parseFloat( data[ 1 ] ),
+								parseFloat( data[ 2 ] )
+							);
+							break;
+					}
 
-					} else if ( lineSecondChar === 'n' && ( result = normal_pattern.exec( line ) ) !== null ) {
+				} else if ( lineFirstChar === 'f' ) {
 
-						// 0                   1      2      3
-						// ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
+					var lineData = line.substr( 1 ).trim();
+					var vertexData = lineData.split( /\s+/ );
+					var faceVertices = [];
 
-						state.normals.push(
-							parseFloat( result[ 1 ] ),
-							parseFloat( result[ 2 ] ),
-							parseFloat( result[ 3 ] )
-						);
+					// Parse the face vertex data into an easy to work with format
 
-					} else if ( lineSecondChar === 't' && ( result = uv_pattern.exec( line ) ) !== null ) {
+					for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
 
-						// 0               1      2
-						// ["vt 0.1 0.2", "0.1", "0.2"]
+						var vertex = vertexData[ j ];
 
-						state.uvs.push(
-							parseFloat( result[ 1 ] ),
-							parseFloat( result[ 2 ] )
-						);
+						if ( vertex.length > 0 ) {
 
-					} else {
+							var vertexParts = vertex.split( '/' );
+							faceVertices.push( vertexParts );
 
-						throw new Error( "Unexpected vertex/normal/uv line: '" + line  + "'" );
+						}
 
 					}
 
-				} else if ( lineFirstChar === "f" ) {
+					// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
 
-					if ( ( result = face_vertex_uv_normal.exec( line ) ) !== null ) {
+					var v1 = faceVertices[ 0 ];
 
-						// f vertex/uv/normal vertex/uv/normal vertex/uv/normal
-						// 0                        1    2    3    4    5    6    7    8    9   10         11         12
-						// ["f 1/1/1 2/2/2 3/3/3", "1", "1", "1", "2", "2", "2", "3", "3", "3", undefined, undefined, undefined]
+					for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
 
-						state.addFace(
-							result[ 1 ], result[ 4 ], result[ 7 ], result[ 10 ],
-							result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
-							result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
-						);
-
-					} else if ( ( result = face_vertex_uv.exec( line ) ) !== null ) {
-
-						// f vertex/uv vertex/uv vertex/uv
-						// 0                  1    2    3    4    5    6   7          8
-						// ["f 1/1 2/2 3/3", "1", "1", "2", "2", "3", "3", undefined, undefined]
+						var v2 = faceVertices[ j ];
+						var v3 = faceVertices[ j + 1 ];
 
 						state.addFace(
-							result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
-							result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
+							v1[ 0 ], v2[ 0 ], v3[ 0 ],
+							v1[ 1 ], v2[ 1 ], v3[ 1 ],
+							v1[ 2 ], v2[ 2 ], v3[ 2 ]
 						);
 
-					} else if ( ( result = face_vertex_normal.exec( line ) ) !== null ) {
-
-						// f vertex//normal vertex//normal vertex//normal
-						// 0                     1    2    3    4    5    6   7          8
-						// ["f 1//1 2//2 3//3", "1", "1", "2", "2", "3", "3", undefined, undefined]
-
-						state.addFace(
-							result[ 1 ], result[ 3 ], result[ 5 ], result[ 7 ],
-							undefined, undefined, undefined, undefined,
-							result[ 2 ], result[ 4 ], result[ 6 ], result[ 8 ]
-						);
-
-					} else if ( ( result = face_vertex.exec( line ) ) !== null ) {
-
-						// f vertex vertex vertex
-						// 0            1    2    3   4
-						// ["f 1 2 3", "1", "2", "3", undefined]
-
-						state.addFace(
-							result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
-						);
-
-					} else {
-
-						throw new Error( "Unexpected face line: '" + line  + "'" );
-
 					}
 
-				} else if ( lineFirstChar === "l" ) {
+				} else if ( lineFirstChar === 'l' ) {
 
 					var lineParts = line.substring( 1 ).trim().split( " " );
 					var lineVertices = [], lineUVs = [];
@@ -587,7 +509,7 @@ THREE.OBJLoader = ( function () {
 
 					state.materialLibraries.push( line.substring( 7 ).trim() );
 
-				} else if ( lineFirstChar === "s" ) {
+				} else if ( lineFirstChar === 's' ) {
 
 					result = line.split( ' ' );
 

+ 82 - 145
examples/js/loaders/OBJLoader2.js

@@ -15,7 +15,7 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  */
 THREE.OBJLoader2 = (function () {
 
-	var OBJLOADER2_VERSION = '1.3.1';
+	var OBJLOADER2_VERSION = '1.4.0';
 
 	function OBJLoader2( manager ) {
 		console.log( "Using THREE.OBJLoader2 version: " + OBJLOADER2_VERSION );
@@ -189,23 +189,7 @@ THREE.OBJLoader2 = (function () {
 		LINE_VT: 'vt',
 		LINE_VN: 'vn',
 		LINE_MTLLIB: 'mtllib',
-		LINE_USEMTL: 'usemtl',
-		/*
-		 * Build Face/Quad: first element in indexArray is the line identification, therefore offset of one needs to be taken into account
-		 * N-Gons are not supported
-		 * Quad Faces: FaceA: 0, 1, 2  FaceB: 2, 3, 0
-		 *
-		 * 0: "f vertex/uv/normal	vertex/uv/normal	vertex/uv/normal	(vertex/uv/normal)"
-		 * 1: "f vertex/uv		  	vertex/uv		   	vertex/uv		   	(vertex/uv		 )"
-		 * 2: "f vertex//normal	 	vertex//normal	  	vertex//normal	  	(vertex//normal  )"
-		 * 3: "f vertex			 	vertex			  	vertex			  	(vertex		  	 )"
-		 *
-		 * @param indexArray
-		 * @param faceType
-		 */
-		QUAD_INDICES_1: [ 1, 2, 3, 3, 4, 1 ],
-		QUAD_INDICES_2: [ 1, 3, 5, 5, 7, 1 ],
-		QUAD_INDICES_3: [ 1, 4, 7, 7, 10, 1 ]
+		LINE_USEMTL: 'usemtl'
 	};
 
 	var Validator = {
@@ -265,10 +249,9 @@ THREE.OBJLoader2 = (function () {
 		Parser.prototype.parseArrayBuffer = function ( arrayBuffer ) {
 			var arrayBufferView = new Uint8Array( arrayBuffer );
 			var length = arrayBufferView.byteLength;
-			var buffer = new Array( 32 );
+			var buffer = new Array( 128 );
 			var bufferPointer = 0;
-			var slashes = new Array( 32 );
-			var slashesPointer = 0;
+			var slashesCount = 0;
 			var reachedFaces = false;
 			var code;
 			var word = '';
@@ -282,7 +265,7 @@ THREE.OBJLoader2 = (function () {
 						break;
 
 					case Consts.CODE_SLASH:
-						slashes[ slashesPointer++ ] = i;
+						slashesCount++;
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
 						break;
@@ -290,9 +273,9 @@ THREE.OBJLoader2 = (function () {
 					case Consts.CODE_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
-						slashesPointer = 0;
+						reachedFaces = this.processLine( buffer, bufferPointer, slashesCount, reachedFaces );
 						bufferPointer = 0;
+						slashesCount = 0;
 						break;
 
 					case Consts.CODE_CR:
@@ -313,10 +296,9 @@ THREE.OBJLoader2 = (function () {
 		 */
 		Parser.prototype.parseText = function ( text ) {
 			var length = text.length;
-			var buffer = new Array( 32 );
+			var buffer = new Array( 128 );
 			var bufferPointer = 0;
-			var slashes = new Array( 32 );
-			var slashesPointer = 0;
+			var slashesCount = 0;
 			var reachedFaces = false;
 			var char;
 			var word = '';
@@ -330,7 +312,7 @@ THREE.OBJLoader2 = (function () {
 						break;
 
 					case Consts.STRING_SLASH:
-						slashes[ slashesPointer++ ] = i;
+						slashesCount++;
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
 						break;
@@ -338,9 +320,9 @@ THREE.OBJLoader2 = (function () {
 					case Consts.STRING_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
-						slashesPointer = 0;
+						reachedFaces = this.processLine( buffer, bufferPointer, slashesCount, reachedFaces );
 						bufferPointer = 0;
+						slashesCount = 0;
 						break;
 
 					case Consts.STRING_CR:
@@ -352,7 +334,7 @@ THREE.OBJLoader2 = (function () {
 			}
 		};
 
-		Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) {
+		Parser.prototype.processLine = function ( buffer, bufferPointer, slashesCount, reachedFaces ) {
 			if ( bufferPointer < 1 ) return reachedFaces;
 
 			var bufferLength = bufferPointer - 1;
@@ -393,50 +375,11 @@ THREE.OBJLoader2 = (function () {
 
 				case Consts.LINE_F:
 					reachedFaces = true;
-					/*
-					 * 0: "f vertex/uv/normal ..."
-					 * 1: "f vertex/uv ..."
-					 * 2: "f vertex//normal ..."
-					 * 3: "f vertex ..."
-					 */
-					var haveQuad = bufferLength % 4 === 0;
-					if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVn( buffer );
-						} else {
-							this.rawObject.buildFaceVVn( buffer );
-						}
-
-					} else if ( bufferLength === slashesPointer * 2 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVt( buffer );
-						} else {
-							this.rawObject.buildFaceVVt( buffer );
-						}
-
-					} else if ( bufferLength * 2 === slashesPointer * 3 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVtVn( buffer );
-						} else {
-							this.rawObject.buildFaceVVtVn( buffer );
-						}
-
-					} else {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadV( buffer );
-						} else {
-							this.rawObject.buildFaceV( buffer );
-						}
-
-					}
+					this.rawObject.processFaces( buffer, bufferPointer, slashesCount );
 					break;
 
 				case Consts.LINE_L:
-					if ( bufferLength === slashesPointer * 2 ) {
+					if ( bufferLength === slashesCount * 2 ) {
 
 						this.rawObject.buildLineVvt( buffer );
 
@@ -660,98 +603,92 @@ THREE.OBJLoader2 = (function () {
 			}
 		};
 
-		RawObject.prototype.buildQuadVVtVn = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] );
-				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] );
-				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] );
-			}
-		};
+		RawObject.prototype.processFaces = function ( buffer, bufferPointer, slashesCount ) {
+			var bufferLength = bufferPointer - 1;
+			var i;
 
-		RawObject.prototype.buildQuadVVt = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
-				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
-			}
-		};
+			// "f vertex ..."
+			if ( slashesCount === 0 ) {
 
-		RawObject.prototype.buildQuadVVn = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
-				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
-			}
-		};
+				for ( i = 2; i < bufferLength - 1; i ++ ) {
 
-		RawObject.prototype.buildQuadV = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] );
-			}
-		};
+					this.attachFace( buffer[ 1     ] );
+					this.attachFace( buffer[ i     ] );
+					this.attachFace( buffer[ i + 1 ] );
 
-		RawObject.prototype.buildFaceVVtVn = function ( indexArray ) {
-			for ( var i = 1; i < 10; i += 3 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVt( indexArray[ i + 1 ] );
-				this.attachFaceVn( indexArray[ i + 2 ] );
-			}
-		};
+				}
 
-		RawObject.prototype.buildFaceVVt = function ( indexArray ) {
-			for ( var i = 1; i < 7; i += 2 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVt( indexArray[ i + 1 ] );
-			}
-		};
+				// "f vertex/uv ..."
+			} else if  ( bufferLength === slashesCount * 2 ) {
 
-		RawObject.prototype.buildFaceVVn = function ( indexArray ) {
-			for ( var i = 1; i < 7; i += 2 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVn( indexArray[ i + 1 ] );
-			}
-		};
+				for ( i = 3; i < bufferLength - 2; i += 2 ) {
+
+					this.attachFace( buffer[ 1     ], buffer[ 2     ] );
+					this.attachFace( buffer[ i     ], buffer[ i + 1 ] );
+					this.attachFace( buffer[ i + 2 ], buffer[ i + 3 ] );
+
+				}
+
+				// "f vertex/uv/normal ..."
+			} else if  ( bufferLength * 2 === slashesCount * 3 ) {
+
+				for ( i = 4; i < bufferLength - 3; i += 3 ) {
+
+					this.attachFace( buffer[ 1     ], buffer[ 2     ], buffer[ 3     ] );
+					this.attachFace( buffer[ i     ], buffer[ i + 1 ], buffer[ i + 2 ] );
+					this.attachFace( buffer[ i + 3 ], buffer[ i + 4 ], buffer[ i + 5 ] );
+
+				}
+
+				// "f vertex//normal ..."
+			} else {
+
+				for ( i = 3; i < bufferLength - 2; i += 2 ) {
+
+					this.attachFace( buffer[ 1     ], undefined, buffer[ 2     ] );
+					this.attachFace( buffer[ i     ], undefined, buffer[ i + 1 ] );
+					this.attachFace( buffer[ i + 2 ], undefined, buffer[ i + 3 ] );
+
+				}
 
-		RawObject.prototype.buildFaceV = function ( indexArray ) {
-			for ( var i = 1; i < 4; i ++ ) {
-				this.attachFaceV_( indexArray[ i ] );
 			}
 		};
 
-		RawObject.prototype.attachFaceV_ = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalVertexOffset ) * 3;
-
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.vertices.push( this.vertices[ index++ ] );
-			rodiu.vertices.push( this.vertices[ index++ ] );
-			rodiu.vertices.push( this.vertices[ index ] );
+		RawObject.prototype.attachFace = function ( faceIndexV, faceIndexU, faceIndexN ) {
+			var indexV = ( parseInt( faceIndexV ) - this.globalVertexOffset ) * 3;
+			var vertices = this.rawObjectDescriptionInUse.vertices;
+			vertices.push( this.vertices[ indexV ++ ] );
+			vertices.push( this.vertices[ indexV ++ ] );
+			vertices.push( this.vertices[ indexV ] );
 
 			if ( this.colors.length > 0 ) {
 
-				index -= 2;
-				rodiu.colors.push( this.colors[ index++ ] );
-				rodiu.colors.push( this.colors[ index++ ] );
-				rodiu.colors.push( this.colors[ index ] );
+				indexV -= 2;
+				var colors = this.rawObjectDescriptionInUse.colors;
+				colors.push( this.colors[ indexV ++ ] );
+				colors.push( this.colors[ indexV ++ ] );
+				colors.push( this.colors[ indexV ] );
 
 			}
-		};
 
-		RawObject.prototype.attachFaceVt = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalUvOffset ) * 2;
+			if ( faceIndexU ) {
 
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.uvs.push( this.uvs[ index++ ] );
-			rodiu.uvs.push( this.uvs[ index ] );
-		};
+				var indexU = ( parseInt( faceIndexU ) - this.globalUvOffset ) * 2;
+				var uvs = this.rawObjectDescriptionInUse.uvs;
+				uvs.push( this.uvs[ indexU ++ ] );
+				uvs.push( this.uvs[ indexU ] );
+
+			}
+
+			if ( faceIndexN ) {
 
-		RawObject.prototype.attachFaceVn = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalNormalOffset ) * 3;
+				var indexN = ( parseInt( faceIndexN ) - this.globalNormalOffset ) * 3;
+				var normals = this.rawObjectDescriptionInUse.normals;
+				normals.push( this.normals[ indexN ++ ] );
+				normals.push( this.normals[ indexN ++ ] );
+				normals.push( this.normals[ indexN ] );
 
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.normals.push( this.normals[ index++ ] );
-			rodiu.normals.push( this.normals[ index++ ] );
-			rodiu.normals.push( this.normals[ index ] );
+			}
 		};
 
 		/*

+ 2 - 2
examples/js/loaders/PCDLoader.js

@@ -36,7 +36,7 @@ THREE.PCDLoader.prototype = {
 
 	parse: function ( data, url ) {
 
-		function binarryToStr( data ) {
+		function binaryToStr( data ) {
 
 			var charArray = new Uint8Array( data );
 
@@ -167,7 +167,7 @@ THREE.PCDLoader.prototype = {
 
 		}
 
-		var textData = binarryToStr( data );
+		var textData = binaryToStr( data );
 
 		// parse header (always ascii format)
 

+ 1 - 1
examples/js/loaders/WWOBJLoader2.js

@@ -15,7 +15,7 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  */
 THREE.OBJLoader2.WWOBJLoader2 = (function () {
 
-	var WWOBJLOADER2_VERSION = '1.3.1';
+	var WWOBJLOADER2_VERSION = '1.4.0';
 
 	var Validator = THREE.OBJLoader2.prototype._getValidator();
 

+ 1 - 1
examples/js/renderers/CSS3DRenderer.js

@@ -261,7 +261,7 @@ THREE.CSS3DRenderer = function () {
 
 	this.render = function ( scene, camera ) {
 
-		var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.getEffectiveFOV() * 0.5 ) ) * _height;
+		var fov = camera.projectionMatrix.elements[ 5 ] * _heightHalf;
 
 		if ( cache.camera.fov !== fov ) {
 

+ 17 - 17
examples/misc_sound.html

@@ -6,35 +6,35 @@
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
 			body {
-				background-color: #000000;
-				margin: 0px;
-				overflow: hidden;
-
-				font-family:Monospace;
-				font-size:13px;
+				background:#777;
+				padding:0;
+				margin:0;
 				font-weight: bold;
-				text-align: center;
-			}
-
-			a {
-				color: #0078ff;
+				overflow:hidden;
 			}
 
 			#info {
-				color:#fff;
 				position: absolute;
-				top: 0px; left: 0px; width: 50%;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
 				padding: 5px;
-				z-index:100;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+			}
+
+			a {
+				color: #ffffff;
 			}
 		</style>
 	</head>
 	<body>
 
 		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl 3d sounds example -
-			music by <a href="http://www.newgrounds.com/audio/listen/358232" target="_blank" rel="noopener">larrylarrybb</a> and
-			<a href="http://www.newgrounds.com/audio/listen/376737" target="_blank" rel="noopener">skullbeatz</a>  and
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl 3d sounds -
+			music by <a href="http://www.newgrounds.com/audio/listen/358232" target="_blank" rel="noopener">larrylarrybb</a>,
+			<a href="http://www.newgrounds.com/audio/listen/376737" target="_blank" rel="noopener">skullbeatz</a> and
 			<a href="http://opengameart.org/content/project-utopia-seamless-loop" target="_blank" rel="noopener">congusbongus</a><br/><br/>
 			navigate with WASD / arrows / mouse
 		</div>

+ 185 - 0
examples/misc_sound_visualizer.html

@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js misc - sound visualizer</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+			}
+
+			a {
+				color: #ffffff;
+			}
+		</style>
+
+		<script src="../build/three.js"></script>
+		<script src="js/Detector.js"></script>
+
+		<script id="vertexShader" type="x-shader/x-vertex">
+
+			varying vec2 vUv;
+
+			void main() {
+
+				vUv = uv;
+				gl_Position = vec4( position, 1.0 );
+
+			}
+
+		</script>
+
+		<script id="fragmentShader" type="x-shader/x-fragment">
+
+			uniform sampler2D tAudioData;
+
+			varying vec2 vUv;
+
+			void main() {
+
+				vec3 backgroundColor = vec3( 0.0 );
+				vec3 color = vec3( 1.0, 0.0, 0.0 );
+
+				float f = texture2D( tAudioData, vec2( vUv.x, 0.0 ) ).r; // sample data texture (only the red channel is relevant)
+				float i = step( vUv.y, f );
+
+				gl_FragColor = vec4( mix( backgroundColor, color, i ), 1.0 );
+
+			}
+
+		</script>
+
+	</head>
+<body>
+
+	<div id="container"></div>
+	<div id="info">
+		<a href="https://threejs.org" target="_blank">three.js</a> - misc - sound visualizer -
+		music by <a href="http://www.newgrounds.com/audio/listen/358232" target="_blank">larrylarrybb</a>
+	</div>
+
+	<script>
+
+	if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+	var scene, camera, renderer, analyser, uniforms;
+
+	init();
+	animate();
+
+	function init() {
+
+		var fftSize = 2048;
+
+		//
+
+		var container = document.getElementById( 'container' );
+
+		//
+
+		scene = new THREE.Scene();
+
+		//
+
+		renderer = new THREE.WebGLRenderer( { antialias: true } );
+		renderer.setSize( window.innerWidth, window.innerHeight );
+		renderer.setClearColor( 0x000000 );
+		renderer.setPixelRatio( window.devicePixelRatio );
+		container.appendChild( renderer.domElement );
+
+		//
+
+		camera = new THREE.Camera();
+		camera.position.z = 1;
+
+		//
+
+		var audioLoader = new THREE.AudioLoader();
+
+		var listener = new THREE.AudioListener();
+		camera.add( listener );
+
+		var audio = new THREE.Audio( listener );
+		audioLoader.load( 'sounds/358232_j_s_song.mp3', function( buffer ) {
+			audio.setBuffer( buffer );
+			audio.setLoop( true );
+			audio.play();
+		});
+
+		analyser = new THREE.AudioAnalyser( audio, fftSize );
+
+		//
+
+		var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
+
+		//
+
+		var size = fftSize / 2;
+
+		uniforms = {
+			tAudioData: { value: new THREE.DataTexture( analyser.data, size, 1, THREE.LuminanceFormat ) }
+		};
+
+		var material = new THREE.ShaderMaterial( {
+
+			uniforms: uniforms,
+			vertexShader: document.getElementById( 'vertexShader' ).textContent,
+			fragmentShader: document.getElementById( 'fragmentShader' ).textContent
+
+		} );
+
+		//
+
+		var mesh = new THREE.Mesh( geometry, material );
+		scene.add( mesh );
+
+		//
+
+		window.addEventListener( 'resize', onResize, false );
+
+	}
+
+	function onResize() {
+
+		renderer.setSize( window.innerWidth, window.innerHeight );
+
+	}
+
+	function animate() {
+
+		requestAnimationFrame( animate );
+
+		render();
+
+	}
+
+	function render() {
+
+		var data = analyser.getFrequencyData();
+
+		uniforms.tAudioData.value.needsUpdate = true;
+
+		renderer.render( scene, camera );
+
+	}
+
+	</script>
+
+</body>
+</html>

+ 9 - 9
examples/models/assimp/interior/interior.assimp.json

@@ -2373,9 +2373,9 @@
 					,"index": 0
 					,"type": 1
 					,"value": [
-						 0.898039
-						,0.898039
-						,0.898039
+						 0.1
+						,0.1
+						,0.1
 					]
 				}
 				,{
@@ -2490,9 +2490,9 @@
 					,"index": 0
 					,"type": 1
 					,"value": [
-						 0.898039
-						,0.898039
-						,0.898039
+						 0.1
+						,0.1
+						,0.1
 					]
 				}
 				,{
@@ -2607,9 +2607,9 @@
 					,"index": 0
 					,"type": 1
 					,"value": [
-						 0.898039
-						,0.898039
-						,0.898039
+						 0.1
+						,0.1
+						,0.1
 					]
 				}
 				,{

+ 1 - 1
examples/webgl_loader_assimp.html

@@ -93,7 +93,7 @@
 			container.appendChild( stats.dom );
 
 			var loader = new THREE.AssimpLoader();
-			loader.load( "./models/assimp/octaminator/Octaminator.assimp", function ( err, result ) {
+			loader.load( './models/assimp/octaminator/Octaminator.assimp', function ( result ) {
 
 				var object = result.object;
 

+ 35 - 45
examples/webgl_loader_assimp2json.html

@@ -68,84 +68,75 @@
 
 			*/
 
-			var container, stats;
-			var camera, scene, renderer, objects;
-			var clock = new THREE.Clock();
+			var container, stats, clock;
+			var camera, scene, renderer;
 
-			// init scene
 			init();
+			animate();
 
-			var onProgress = function ( xhr ) {
-				if ( xhr.lengthComputable ) {
-					var percentComplete = xhr.loaded / xhr.total * 100;
-					console.log( Math.round(percentComplete, 2) + '% downloaded' );
-				}
-			};
+			//
 
-			var onError = function ( xhr ) {
-			};
+			function init() {
 
-			// Load jeep model using the AssimpJSONLoader
-			var loader1 = new THREE.AssimpJSONLoader();
-			loader1.load( 'models/assimp/jeep/jeep.assimp.json', function ( object ) {
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
 
-				object.scale.multiplyScalar( 0.2 );
-				scene.add( object );
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
 
-			}, onProgress, onError );
+				scene = new THREE.Scene();
 
-			// load interior model
-			var loader2 = new THREE.AssimpJSONLoader();
-			loader2.load( 'models/assimp/interior/interior.assimp.json', function ( object ) {
+				clock = new THREE.Clock();
 
-				scene.add( object );
+				// load jeep model
 
-			}, onProgress, onError );
+				var loader1 = new THREE.AssimpJSONLoader();
+				loader1.load( 'models/assimp/jeep/jeep.assimp.json', function ( object ) {
 
-			animate();
+					object.scale.multiplyScalar( 0.2 );
+					scene.add( object );
 
+				} );
 
-			//
+				// load interior model
 
-			function init() {
+				var loader2 = new THREE.AssimpJSONLoader();
+				loader2.load( 'models/assimp/interior/interior.assimp.json', function ( object ) {
 
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
+					scene.add( object );
 
-				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
-				camera.position.set( 2, 4, 5 );
+				} );
 
-				scene = new THREE.Scene();
-				scene.fog = new THREE.FogExp2( 0x000000, 0.035 );
+				//
 
-				// Lights
-				scene.add( new THREE.AmbientLight( 0xcccccc ) );
+				var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
+				scene.add( ambientLight );
 
 				var directionalLight = new THREE.DirectionalLight( 0xeeeeee );
-				directionalLight.position.x = Math.random() - 0.5;
-				directionalLight.position.y = Math.random();
-				directionalLight.position.z = Math.random() - 0.5;
+				directionalLight.position.set( 1, 1, - 1 );
 				directionalLight.position.normalize();
 				scene.add( directionalLight );
 
-				// Renderer
+				//
+
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
 
-				// Stats
+				//
+
 				stats = new Stats();
 				container.appendChild( stats.dom );
 
-				// Events
+				//
+
 				window.addEventListener( 'resize', onWindowResize, false );
 
 			}
 
 			//
 
-			function onWindowResize( event ) {
+			function onWindowResize() {
 
 				renderer.setSize( window.innerWidth, window.innerHeight );
 
@@ -156,7 +147,6 @@
 
 			//
 
-			var t = 0;
 			function animate() {
 
 				requestAnimationFrame( animate );
@@ -169,11 +159,11 @@
 
 			function render() {
 
-				var timer = Date.now() * 0.0005;
+				var elapsedTime = clock.getElapsedTime();
 
-				camera.position.x = Math.cos( timer ) * 10;
+				camera.position.x = Math.cos( elapsedTime * 0.5 ) * 10;
 				camera.position.y = 4;
-				camera.position.z = Math.sin( timer ) * 10;
+				camera.position.z = Math.sin( elapsedTime * 0.5 ) * 10;
 
 				camera.lookAt( scene.position );
 

+ 4 - 6
examples/webgl_loader_gltf2.html

@@ -91,7 +91,6 @@
 				<option value="glTF-Binary">None (Binary)</option>
 				<option value="glTF-MaterialsCommon">Common Materials</option>
 				<option value="glTF-pbrSpecularGlossiness">Specular-Glossiness (PBR)</option>
-				<option value="glTF-techniqueWebGL">GLSL</option>
 			</select>
 		</div>
 	</div>
@@ -436,7 +435,6 @@
 					addLights:true,
 					addGround:true,
 					shadows:true,
-					// TODO: 'glTF-techniqueWebGL'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-MaterialsCommon', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -449,7 +447,7 @@
 					addLights:true,
 					shadows:true,
 					addGround:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -459,7 +457,7 @@
 					 addLights:true,
 					 addGround:true,
 					 shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -469,7 +467,7 @@
 					addLights:true,
 					addGround:true,
 					shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -479,7 +477,7 @@
 					objectRotation: new THREE.Euler(0, 90, 0),
 					addLights:true,
 					shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{

+ 2 - 2
src/core/Object3D.js

@@ -373,7 +373,7 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, {
 		}
 
 		return this;
-		
+
 	},
 
 	getObjectById: function ( id ) {
@@ -613,10 +613,10 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, {
 		object.type = this.type;
 
 		if ( this.name !== '' ) object.name = this.name;
-		if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 		if ( this.castShadow === true ) object.castShadow = true;
 		if ( this.receiveShadow === true ) object.receiveShadow = true;
 		if ( this.visible === false ) object.visible = false;
+		if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 
 		object.matrix = this.matrix.toArray();
 

+ 5 - 0
src/loaders/MaterialLoader.js

@@ -81,8 +81,13 @@ Object.assign( MaterialLoader.prototype, {
 		if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
 		if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
 		if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
+
 		if ( json.skinning !== undefined ) material.skinning = json.skinning;
 		if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
+		if ( json.dithering !== undefined ) material.dithering = json.dithering;
+
+		if ( json.visible !== undefined ) material.visible = json.visible;
+		if ( json.userData !== undefined ) material.userData = json.userData;
 
 		// Deprecated
 

+ 11 - 4
src/materials/Material.js

@@ -61,6 +61,8 @@ function Material() {
 
 	this.visible = true;
 
+	this.userData = {};
+
 	this.needsUpdate = true;
 
 }
@@ -221,17 +223,21 @@ Object.assign( Material.prototype, EventDispatcher.prototype, {
 		data.depthTest = this.depthTest;
 		data.depthWrite = this.depthWrite;
 
+		if ( this.dithering === true ) data.dithering = true;
+
 		if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
 		if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+
 		if ( this.wireframe === true ) data.wireframe = this.wireframe;
 		if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
 		if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
 		if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
 
-		data.skinning = this.skinning;
-		data.morphTargets = this.morphTargets;
+		if ( this.morphTargets === true ) data.morphTargets = true;
+		if ( this.skinning === true ) data.skinning = true;
 
-		data.dithering = this.dithering;
+		if ( this.visible === false ) data.visible = false;
+		if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
 
 		// TODO: Copied from Object3D.toJSON
 
@@ -308,12 +314,13 @@ Object.assign( Material.prototype, EventDispatcher.prototype, {
 		this.dithering = source.dithering;
 
 		this.alphaTest = source.alphaTest;
-
 		this.premultipliedAlpha = source.premultipliedAlpha;
 
 		this.overdraw = source.overdraw;
 
 		this.visible = source.visible;
+		this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
 		this.clipShadows = source.clipShadows;
 		this.clipIntersection = source.clipIntersection;
 

+ 1 - 1
src/textures/Texture.js

@@ -125,7 +125,7 @@ Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
 			var canvas;
 
-			if ( image.toDataURL !== undefined ) {
+			if ( image instanceof HTMLCanvasElement ) {
 
 				canvas = image;
 

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