Browse Source

Merge branch 'dev' into blender-exporter-multimaterial

Michael Schlachter 8 years ago
parent
commit
9fb2237298

+ 203 - 155
build/three.js

@@ -5389,9 +5389,18 @@
 			map: { value: null },
 			offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) },
 
-			specularMap: { value: null },
 			alphaMap: { value: null },
 
+		},
+
+		specularmap: {
+
+			specularMap: { value: null },
+
+		},
+
+		envmap: {
+
 			envMap: { value: null },
 			flipEnvMap: { value: - 1 },
 			reflectivity: { value: 1.0 },
@@ -5784,9 +5793,9 @@
 
 	var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
 
-	var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
+	var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
 
-	var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n";
+	var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\tgl_Position = projectionMatrix * vec4( normalMatrix * position, 1.0 );\n}\n";
 
 	var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <logdepthbuf_fragment>\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n";
 
@@ -5796,7 +5805,7 @@
 
 	var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}\n";
 
-	var equirect_frag = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
+	var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
 
 	var equirect_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n";
 
@@ -5957,6 +5966,8 @@
 
 			uniforms: UniformsUtils.merge( [
 				UniformsLib.common,
+				UniformsLib.specularmap,
+				UniformsLib.envmap,
 				UniformsLib.aomap,
 				UniformsLib.lightmap,
 				UniformsLib.fog
@@ -5971,6 +5982,8 @@
 
 			uniforms: UniformsUtils.merge( [
 				UniformsLib.common,
+				UniformsLib.specularmap,
+				UniformsLib.envmap,
 				UniformsLib.aomap,
 				UniformsLib.lightmap,
 				UniformsLib.emissivemap,
@@ -5990,6 +6003,8 @@
 
 			uniforms: UniformsUtils.merge( [
 				UniformsLib.common,
+				UniformsLib.specularmap,
+				UniformsLib.envmap,
 				UniformsLib.aomap,
 				UniformsLib.lightmap,
 				UniformsLib.emissivemap,
@@ -6015,6 +6030,7 @@
 
 			uniforms: UniformsUtils.merge( [
 				UniformsLib.common,
+				UniformsLib.envmap,
 				UniformsLib.aomap,
 				UniformsLib.lightmap,
 				UniformsLib.emissivemap,
@@ -6113,15 +6129,10 @@
 
 		},
 
-		/* -------------------------------------------------------------------------
-		//	Cube map shader
-		 ------------------------------------------------------------------------- */
-
 		equirect: {
 
 			uniforms: {
 				tEquirect: { value: null },
-				tFlip: { value: - 1 }
 			},
 
 			vertexShader: ShaderChunk.equirect_vert,
@@ -8356,15 +8367,7 @@
 
 		intersectsPlane: function ( plane ) {
 
-			// We use the following equation to compute the signed distance from
-			// the center of the sphere to the plane.
-			//
-			// distance = q * n - d
-			//
-			// If this distance is greater than the radius of the sphere,
-			// then there is no intersection.
-
-			return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius;
+			return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
 
 		},
 
@@ -8756,6 +8759,8 @@
 
 	function Plane( normal, constant ) {
 
+		// normal is assumed to be normalized
+
 		this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
 		this.constant = ( constant !== undefined ) ? constant : 0;
 
@@ -16301,7 +16306,7 @@
 		var clearAlpha = 0;
 
 		var planeCamera, planeMesh;
-		var boxCamera, boxMesh;
+		var boxMesh;
 
 		function render( scene, camera, forceClear ) {
 
@@ -16326,9 +16331,7 @@
 
 			if ( background && background.isCubeTexture ) {
 
-				if ( boxCamera === undefined ) {
-
-					boxCamera = new PerspectiveCamera();
+				if ( boxMesh === undefined ) {
 
 					boxMesh = new Mesh(
 						new BoxBufferGeometry( 5, 5, 5 ),
@@ -16345,17 +16348,13 @@
 
 				}
 
-				boxCamera.projectionMatrix.copy( camera.projectionMatrix );
-
-				boxCamera.matrixWorld.extractRotation( camera.matrixWorld );
-				boxCamera.matrixWorldInverse.getInverse( boxCamera.matrixWorld );
-
 				boxMesh.material.uniforms[ "tCube" ].value = background;
-				boxMesh.modelViewMatrix.multiplyMatrices( boxCamera.matrixWorldInverse, boxMesh.matrixWorld );
+				boxMesh.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, boxMesh.matrixWorld );
+				boxMesh.normalMatrix.getNormalMatrix( boxMesh.modelViewMatrix );
 
 				geometries.update( boxMesh.geometry );
 
-				renderer.renderBufferDirect( boxCamera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
+				renderer.renderBufferDirect( camera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
 
 			} else if ( background && background.isTexture ) {
 
@@ -16467,43 +16466,42 @@
 
 	function WebGLRenderList() {
 
-		var opaque = [];
-		var opaqueLastIndex = - 1;
+		var renderItems = [];
+		var renderItemsIndex = 0;
 
+		var opaque = [];
 		var transparent = [];
-		var transparentLastIndex = - 1;
 
 		function init() {
 
-			opaqueLastIndex = - 1;
-			transparentLastIndex = - 1;
+			renderItemsIndex = 0;
+
+			opaque.length = 0;
+			transparent.length = 0;
 
 		}
 
 		function push( object, geometry, material, z, group ) {
 
-			var array, index;
+			var renderItem = renderItems[ renderItemsIndex ];
 
-			// allocate the next position in the appropriate array
+			if ( renderItem === undefined ) {
 
-			if ( material.transparent ) {
+				renderItem = {
+					id: object.id,
+					object: object,
+					geometry: geometry,
+					material: material,
+					program: material.program,
+					renderOrder: object.renderOrder,
+					z: z,
+					group: group
+				};
 
-				array = transparent;
-				index = ++ transparentLastIndex;
+				renderItems[ renderItemsIndex ] = renderItem;
 
 			} else {
 
-				array = opaque;
-				index = ++ opaqueLastIndex;
-
-			}
-
-			// recycle existing render item or grow the array
-
-			var renderItem = array[ index ];
-
-			if ( renderItem ) {
-
 				renderItem.id = object.id;
 				renderItem.object = object;
 				renderItem.geometry = geometry;
@@ -16513,37 +16511,18 @@
 				renderItem.z = z;
 				renderItem.group = group;
 
-			} else {
-
-				renderItem = {
-					id: object.id,
-					object: object,
-					geometry: geometry,
-					material: material,
-					program: material.program,
-					renderOrder: object.renderOrder,
-					z: z,
-					group: group
-				};
-
-				// assert( index === array.length );
-				array.push( renderItem );
-
 			}
 
-		}
-
-		function finish() {
+			( material.transparent === true ? transparent : opaque ).push( renderItem );
 
-			opaque.length = opaqueLastIndex + 1;
-			transparent.length = transparentLastIndex + 1;
+			renderItemsIndex ++;
 
 		}
 
 		function sort() {
 
-			opaque.sort( painterSortStable );
-			transparent.sort( reversePainterSortStable );
+			if ( opaque.length > 1 ) opaque.sort( painterSortStable );
+			if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );
 
 		}
 
@@ -16553,7 +16532,6 @@
 
 			init: init,
 			push: push,
-			finish: finish,
 
 			sort: sort
 		};
@@ -16595,6 +16573,115 @@
 
 	}
 
+	/**
+	 * @author mrdoob / http://mrdoob.com/
+	 */
+
+	function absNumericalSort( a, b ) {
+
+		return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
+
+	}
+
+	function WebGLMorphtargets( gl ) {
+
+		var influencesList = {};
+		var morphInfluences = new Float32Array( 8 );
+
+		function update( object, geometry, material, program ) {
+
+			var objectInfluences = object.morphTargetInfluences;
+
+			var length = objectInfluences.length;
+
+			var influences = influencesList[ geometry.id ];
+
+			if ( influences === undefined ) {
+
+				// initialise list
+
+				influences = [];
+
+				for ( var i = 0; i < length; i ++ ) {
+
+					influences[ i ] = [ i, 0 ];
+
+				}
+
+				influencesList[ geometry.id ] = influences;
+
+			}
+
+			var morphTargets = material.morphTargets && geometry.morphAttributes.position;
+			var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
+
+			// Remove current morphAttributes
+
+			for ( var i = 0; i < length; i ++ ) {
+
+				var influence = influences[ i ];
+
+				if ( influence[ 1 ] !== 0 ) {
+
+					if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );
+					if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );
+
+				}
+
+			}
+
+			// Collect influences
+
+			for ( var i = 0; i < length; i ++ ) {
+
+				var influence = influences[ i ];
+
+				influence[ 0 ] = i;
+				influence[ 1 ] = objectInfluences[ i ];
+
+			}
+
+			influences.sort( absNumericalSort );
+
+			// Add morphAttributes
+
+			for ( var i = 0; i < 8; i ++ ) {
+
+				var influence = influences[ i ];
+
+				if ( influence ) {
+
+					var index = influence[ 0 ];
+					var value = influence[ 1 ];
+
+					if ( value ) {
+
+						if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
+						if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
+
+						morphInfluences[ i ] = value;
+						continue;
+
+					}
+
+				}
+
+				morphInfluences[ i ] = 0;
+
+			}
+
+			program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
+
+		}
+
+		return {
+
+			update: update
+
+		}
+
+	}
+
 	/**
 	 * @author mrdoob / http://mrdoob.com/
 	 */
@@ -20679,8 +20766,6 @@
 
 		var currentRenderList = null;
 
-		var morphInfluences = new Float32Array( 8 );
-
 		var spritesArray = [];
 		var flaresArray = [];
 
@@ -20864,7 +20949,7 @@
 		var properties, textures, attributes, geometries, objects, lights;
 		var programCache, renderLists;
 
-		var background, bufferRenderer, indexedBufferRenderer;
+		var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 		var flareRenderer, spriteRenderer;
 
 		function initGLContext() {
@@ -20895,6 +20980,7 @@
 			attributes = new WebGLAttributes( _gl );
 			geometries = new WebGLGeometries( _gl, attributes, _infoMemory );
 			objects = new WebGLObjects( geometries, _infoRender );
+			morphtargets = new WebGLMorphtargets( _gl );
 			programCache = new WebGLPrograms( _this, extensions, capabilities );
 			lights = new WebGLLights();
 			renderLists = new WebGLRenderLists();
@@ -21276,12 +21362,6 @@
 
 		};
 
-		function absNumericalSort( a, b ) {
-
-			return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] );
-
-		}
-
 		this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {
 
 			state.setMaterial( material );
@@ -21298,61 +21378,9 @@
 
 			}
 
-			// morph targets
-
-			var morphTargetInfluences = object.morphTargetInfluences;
-
-			if ( morphTargetInfluences !== undefined ) {
-
-				// TODO Remove allocations
-
-				var activeInfluences = [];
-
-				for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {
-
-					var influence = morphTargetInfluences[ i ];
-					activeInfluences.push( [ influence, i ] );
-
-				}
-
-				activeInfluences.sort( absNumericalSort );
-
-				if ( activeInfluences.length > 8 ) {
-
-					activeInfluences.length = 8;
+			if ( object.morphTargetInfluences ) {
 
-				}
-
-				var morphAttributes = geometry.morphAttributes;
-
-				for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {
-
-					var influence = activeInfluences[ i ];
-					morphInfluences[ i ] = influence[ 0 ];
-
-					if ( influence[ 0 ] !== 0 ) {
-
-						var index = influence[ 1 ];
-
-						if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
-						if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );
-
-					} else {
-
-						if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
-						if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );
-
-					}
-
-				}
-
-				for ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) {
-
-					morphInfluences[ i ] = 0.0;
-
-				}
-
-				program.getUniforms().setValue( _gl, 'morphTargetInfluences', morphInfluences );
+				morphtargets.update( object, geometry, material, program );
 
 				updateBuffers = true;
 
@@ -21740,8 +21768,6 @@
 
 			projectObject( scene, camera, _this.sortObjects );
 
-			currentRenderList.finish();
-
 			if ( _this.sortObjects === true ) {
 
 				currentRenderList.sort();
@@ -22523,7 +22549,11 @@
 
 			uniforms.opacity.value = material.opacity;
 
-			uniforms.diffuse.value = material.color;
+			if ( material.color ) {
+
+				uniforms.diffuse.value = material.color;
+
+			}
 
 			if ( material.emissive ) {
 
@@ -22531,9 +22561,38 @@
 
 			}
 
-			uniforms.map.value = material.map;
-			uniforms.specularMap.value = material.specularMap;
-			uniforms.alphaMap.value = material.alphaMap;
+			if ( material.map ) {
+
+				uniforms.map.value = material.map;
+
+			}
+
+			if ( material.alphaMap ) {
+
+				uniforms.alphaMap.value = material.alphaMap;
+
+			}
+
+			if ( material.specularMap ) {
+
+				uniforms.specularMap.value = material.specularMap;
+
+			}
+
+			if ( material.envMap ) {
+
+				uniforms.envMap.value = material.envMap;
+
+				// don't flip CubeTexture envMaps, flip everything else:
+				//  WebGLRenderTargetCube will be flipped for backwards compatibility
+				//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
+				// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
+				uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
+
+				uniforms.reflectivity.value = material.reflectivity;
+				uniforms.refractionRatio.value = material.refractionRatio;
+
+			}
 
 			if ( material.lightMap ) {
 
@@ -22613,17 +22672,6 @@
 
 			}
 
-			uniforms.envMap.value = material.envMap;
-
-			// don't flip CubeTexture envMaps, flip everything else:
-			//  WebGLRenderTargetCube will be flipped for backwards compatibility
-			//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
-			// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
-			uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
-
-			uniforms.reflectivity.value = material.reflectivity;
-			uniforms.refractionRatio.value = material.refractionRatio;
-
 		}
 
 		function refreshUniformsLine( uniforms, material ) {
@@ -32276,7 +32324,7 @@
 
 			} else {
 
-				// by default, we asssume a constructor compatible with the base
+				// by default, we assume a constructor compatible with the base
 				return new trackType(
 						json.name, json.times, json.values, json.interpolation );
 

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


+ 203 - 155
build/three.module.js

@@ -5383,9 +5383,18 @@ var UniformsLib = {
 		map: { value: null },
 		offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) },
 
-		specularMap: { value: null },
 		alphaMap: { value: null },
 
+	},
+
+	specularmap: {
+
+		specularMap: { value: null },
+
+	},
+
+	envmap: {
+
 		envMap: { value: null },
 		flipEnvMap: { value: - 1 },
 		reflectivity: { value: 1.0 },
@@ -5778,9 +5787,9 @@ var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 =
 
 var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif\n";
 
-var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
+var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\nvoid main() {\n\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n\tgl_FragColor.a *= opacity;\n}\n";
 
-var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n";
+var cube_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\tgl_Position = projectionMatrix * vec4( normalMatrix * position, 1.0 );\n}\n";
 
 var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include <common>\n#include <packing>\n#include <uv_pars_fragment>\n#include <map_pars_fragment>\n#include <alphamap_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\nvoid main() {\n\t#include <clipping_planes_fragment>\n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include <map_fragment>\n\t#include <alphamap_fragment>\n\t#include <alphatest_fragment>\n\t#include <logdepthbuf_fragment>\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}\n";
 
@@ -5790,7 +5799,7 @@ var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nunif
 
 var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include <common>\n#include <uv_pars_vertex>\n#include <displacementmap_pars_vertex>\n#include <morphtarget_pars_vertex>\n#include <skinning_pars_vertex>\n#include <clipping_planes_pars_vertex>\nvoid main() {\n\t#include <uv_vertex>\n\t#include <skinbase_vertex>\n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include <beginnormal_vertex>\n\t\t#include <morphnormal_vertex>\n\t\t#include <skinnormal_vertex>\n\t#endif\n\t#include <begin_vertex>\n\t#include <morphtarget_vertex>\n\t#include <skinning_vertex>\n\t#include <displacementmap_vertex>\n\t#include <project_vertex>\n\t#include <worldpos_vertex>\n\t#include <clipping_planes_vertex>\n\tvWorldPosition = worldPosition.xyz;\n}\n";
 
-var equirect_frag = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
+var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvec3 direction = normalize( vWorldPosition );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n}\n";
 
 var equirect_vert = "varying vec3 vWorldPosition;\n#include <common>\nvoid main() {\n\tvWorldPosition = transformDirection( position, modelMatrix );\n\t#include <begin_vertex>\n\t#include <project_vertex>\n}\n";
 
@@ -5951,6 +5960,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.fog
@@ -5965,6 +5976,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -5984,6 +5997,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -6009,6 +6024,7 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -6107,15 +6123,10 @@ var ShaderLib = {
 
 	},
 
-	/* -------------------------------------------------------------------------
-	//	Cube map shader
-	 ------------------------------------------------------------------------- */
-
 	equirect: {
 
 		uniforms: {
 			tEquirect: { value: null },
-			tFlip: { value: - 1 }
 		},
 
 		vertexShader: ShaderChunk.equirect_vert,
@@ -8350,15 +8361,7 @@ Object.assign( Sphere.prototype, {
 
 	intersectsPlane: function ( plane ) {
 
-		// We use the following equation to compute the signed distance from
-		// the center of the sphere to the plane.
-		//
-		// distance = q * n - d
-		//
-		// If this distance is greater than the radius of the sphere,
-		// then there is no intersection.
-
-		return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius;
+		return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
 
 	},
 
@@ -8750,6 +8753,8 @@ Object.assign( Matrix3.prototype, {
 
 function Plane( normal, constant ) {
 
+	// normal is assumed to be normalized
+
 	this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
 	this.constant = ( constant !== undefined ) ? constant : 0;
 
@@ -16295,7 +16300,7 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 	var clearAlpha = 0;
 
 	var planeCamera, planeMesh;
-	var boxCamera, boxMesh;
+	var boxMesh;
 
 	function render( scene, camera, forceClear ) {
 
@@ -16320,9 +16325,7 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 
 		if ( background && background.isCubeTexture ) {
 
-			if ( boxCamera === undefined ) {
-
-				boxCamera = new PerspectiveCamera();
+			if ( boxMesh === undefined ) {
 
 				boxMesh = new Mesh(
 					new BoxBufferGeometry( 5, 5, 5 ),
@@ -16339,17 +16342,13 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 
 			}
 
-			boxCamera.projectionMatrix.copy( camera.projectionMatrix );
-
-			boxCamera.matrixWorld.extractRotation( camera.matrixWorld );
-			boxCamera.matrixWorldInverse.getInverse( boxCamera.matrixWorld );
-
 			boxMesh.material.uniforms[ "tCube" ].value = background;
-			boxMesh.modelViewMatrix.multiplyMatrices( boxCamera.matrixWorldInverse, boxMesh.matrixWorld );
+			boxMesh.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, boxMesh.matrixWorld );
+			boxMesh.normalMatrix.getNormalMatrix( boxMesh.modelViewMatrix );
 
 			geometries.update( boxMesh.geometry );
 
-			renderer.renderBufferDirect( boxCamera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
+			renderer.renderBufferDirect( camera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
 
 		} else if ( background && background.isTexture ) {
 
@@ -16461,43 +16460,42 @@ function reversePainterSortStable( a, b ) {
 
 function WebGLRenderList() {
 
-	var opaque = [];
-	var opaqueLastIndex = - 1;
+	var renderItems = [];
+	var renderItemsIndex = 0;
 
+	var opaque = [];
 	var transparent = [];
-	var transparentLastIndex = - 1;
 
 	function init() {
 
-		opaqueLastIndex = - 1;
-		transparentLastIndex = - 1;
+		renderItemsIndex = 0;
+
+		opaque.length = 0;
+		transparent.length = 0;
 
 	}
 
 	function push( object, geometry, material, z, group ) {
 
-		var array, index;
+		var renderItem = renderItems[ renderItemsIndex ];
 
-		// allocate the next position in the appropriate array
+		if ( renderItem === undefined ) {
 
-		if ( material.transparent ) {
+			renderItem = {
+				id: object.id,
+				object: object,
+				geometry: geometry,
+				material: material,
+				program: material.program,
+				renderOrder: object.renderOrder,
+				z: z,
+				group: group
+			};
 
-			array = transparent;
-			index = ++ transparentLastIndex;
+			renderItems[ renderItemsIndex ] = renderItem;
 
 		} else {
 
-			array = opaque;
-			index = ++ opaqueLastIndex;
-
-		}
-
-		// recycle existing render item or grow the array
-
-		var renderItem = array[ index ];
-
-		if ( renderItem ) {
-
 			renderItem.id = object.id;
 			renderItem.object = object;
 			renderItem.geometry = geometry;
@@ -16507,37 +16505,18 @@ function WebGLRenderList() {
 			renderItem.z = z;
 			renderItem.group = group;
 
-		} else {
-
-			renderItem = {
-				id: object.id,
-				object: object,
-				geometry: geometry,
-				material: material,
-				program: material.program,
-				renderOrder: object.renderOrder,
-				z: z,
-				group: group
-			};
-
-			// assert( index === array.length );
-			array.push( renderItem );
-
 		}
 
-	}
-
-	function finish() {
+		( material.transparent === true ? transparent : opaque ).push( renderItem );
 
-		opaque.length = opaqueLastIndex + 1;
-		transparent.length = transparentLastIndex + 1;
+		renderItemsIndex ++;
 
 	}
 
 	function sort() {
 
-		opaque.sort( painterSortStable );
-		transparent.sort( reversePainterSortStable );
+		if ( opaque.length > 1 ) opaque.sort( painterSortStable );
+		if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );
 
 	}
 
@@ -16547,7 +16526,6 @@ function WebGLRenderList() {
 
 		init: init,
 		push: push,
-		finish: finish,
 
 		sort: sort
 	};
@@ -16589,6 +16567,115 @@ function WebGLRenderLists() {
 
 }
 
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+function absNumericalSort( a, b ) {
+
+	return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
+
+}
+
+function WebGLMorphtargets( gl ) {
+
+	var influencesList = {};
+	var morphInfluences = new Float32Array( 8 );
+
+	function update( object, geometry, material, program ) {
+
+		var objectInfluences = object.morphTargetInfluences;
+
+		var length = objectInfluences.length;
+
+		var influences = influencesList[ geometry.id ];
+
+		if ( influences === undefined ) {
+
+			// initialise list
+
+			influences = [];
+
+			for ( var i = 0; i < length; i ++ ) {
+
+				influences[ i ] = [ i, 0 ];
+
+			}
+
+			influencesList[ geometry.id ] = influences;
+
+		}
+
+		var morphTargets = material.morphTargets && geometry.morphAttributes.position;
+		var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
+
+		// Remove current morphAttributes
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			var influence = influences[ i ];
+
+			if ( influence[ 1 ] !== 0 ) {
+
+				if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );
+				if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );
+
+			}
+
+		}
+
+		// Collect influences
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			var influence = influences[ i ];
+
+			influence[ 0 ] = i;
+			influence[ 1 ] = objectInfluences[ i ];
+
+		}
+
+		influences.sort( absNumericalSort );
+
+		// Add morphAttributes
+
+		for ( var i = 0; i < 8; i ++ ) {
+
+			var influence = influences[ i ];
+
+			if ( influence ) {
+
+				var index = influence[ 0 ];
+				var value = influence[ 1 ];
+
+				if ( value ) {
+
+					if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
+					if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
+
+					morphInfluences[ i ] = value;
+					continue;
+
+				}
+
+			}
+
+			morphInfluences[ i ] = 0;
+
+		}
+
+		program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
+
+	}
+
+	return {
+
+		update: update
+
+	}
+
+}
+
 /**
  * @author mrdoob / http://mrdoob.com/
  */
@@ -20673,8 +20760,6 @@ function WebGLRenderer( parameters ) {
 
 	var currentRenderList = null;
 
-	var morphInfluences = new Float32Array( 8 );
-
 	var spritesArray = [];
 	var flaresArray = [];
 
@@ -20858,7 +20943,7 @@ function WebGLRenderer( parameters ) {
 	var properties, textures, attributes, geometries, objects, lights;
 	var programCache, renderLists;
 
-	var background, bufferRenderer, indexedBufferRenderer;
+	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 	var flareRenderer, spriteRenderer;
 
 	function initGLContext() {
@@ -20889,6 +20974,7 @@ function WebGLRenderer( parameters ) {
 		attributes = new WebGLAttributes( _gl );
 		geometries = new WebGLGeometries( _gl, attributes, _infoMemory );
 		objects = new WebGLObjects( geometries, _infoRender );
+		morphtargets = new WebGLMorphtargets( _gl );
 		programCache = new WebGLPrograms( _this, extensions, capabilities );
 		lights = new WebGLLights();
 		renderLists = new WebGLRenderLists();
@@ -21270,12 +21356,6 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	function absNumericalSort( a, b ) {
-
-		return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] );
-
-	}
-
 	this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {
 
 		state.setMaterial( material );
@@ -21292,61 +21372,9 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		// morph targets
-
-		var morphTargetInfluences = object.morphTargetInfluences;
-
-		if ( morphTargetInfluences !== undefined ) {
-
-			// TODO Remove allocations
-
-			var activeInfluences = [];
-
-			for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {
-
-				var influence = morphTargetInfluences[ i ];
-				activeInfluences.push( [ influence, i ] );
-
-			}
-
-			activeInfluences.sort( absNumericalSort );
-
-			if ( activeInfluences.length > 8 ) {
-
-				activeInfluences.length = 8;
+		if ( object.morphTargetInfluences ) {
 
-			}
-
-			var morphAttributes = geometry.morphAttributes;
-
-			for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {
-
-				var influence = activeInfluences[ i ];
-				morphInfluences[ i ] = influence[ 0 ];
-
-				if ( influence[ 0 ] !== 0 ) {
-
-					var index = influence[ 1 ];
-
-					if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
-					if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );
-
-				} else {
-
-					if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
-					if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );
-
-				}
-
-			}
-
-			for ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) {
-
-				morphInfluences[ i ] = 0.0;
-
-			}
-
-			program.getUniforms().setValue( _gl, 'morphTargetInfluences', morphInfluences );
+			morphtargets.update( object, geometry, material, program );
 
 			updateBuffers = true;
 
@@ -21734,8 +21762,6 @@ function WebGLRenderer( parameters ) {
 
 		projectObject( scene, camera, _this.sortObjects );
 
-		currentRenderList.finish();
-
 		if ( _this.sortObjects === true ) {
 
 			currentRenderList.sort();
@@ -22517,7 +22543,11 @@ function WebGLRenderer( parameters ) {
 
 		uniforms.opacity.value = material.opacity;
 
-		uniforms.diffuse.value = material.color;
+		if ( material.color ) {
+
+			uniforms.diffuse.value = material.color;
+
+		}
 
 		if ( material.emissive ) {
 
@@ -22525,9 +22555,38 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		uniforms.map.value = material.map;
-		uniforms.specularMap.value = material.specularMap;
-		uniforms.alphaMap.value = material.alphaMap;
+		if ( material.map ) {
+
+			uniforms.map.value = material.map;
+
+		}
+
+		if ( material.alphaMap ) {
+
+			uniforms.alphaMap.value = material.alphaMap;
+
+		}
+
+		if ( material.specularMap ) {
+
+			uniforms.specularMap.value = material.specularMap;
+
+		}
+
+		if ( material.envMap ) {
+
+			uniforms.envMap.value = material.envMap;
+
+			// don't flip CubeTexture envMaps, flip everything else:
+			//  WebGLRenderTargetCube will be flipped for backwards compatibility
+			//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
+			// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
+			uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
+
+			uniforms.reflectivity.value = material.reflectivity;
+			uniforms.refractionRatio.value = material.refractionRatio;
+
+		}
 
 		if ( material.lightMap ) {
 
@@ -22607,17 +22666,6 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		uniforms.envMap.value = material.envMap;
-
-		// don't flip CubeTexture envMaps, flip everything else:
-		//  WebGLRenderTargetCube will be flipped for backwards compatibility
-		//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
-		// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
-		uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
-
-		uniforms.reflectivity.value = material.reflectivity;
-		uniforms.refractionRatio.value = material.refractionRatio;
-
 	}
 
 	function refreshUniformsLine( uniforms, material ) {
@@ -32270,7 +32318,7 @@ Object.assign( KeyframeTrack, {
 
 		} else {
 
-			// by default, we asssume a constructor compatible with the base
+			// by default, we assume a constructor compatible with the base
 			return new trackType(
 					json.name, json.times, json.values, json.interpolation );
 

+ 13 - 10
docs/api/core/InterleavedBufferAttribute.html

@@ -27,6 +27,19 @@
 			The [page:InterleavedBuffer InterleavedBuffer] instance passed in the constructor.
 		</div>
 
+		<h3>[property:TypedArray array]</h3>
+		<div>
+			The value of [page:InterleavedBufferAttribute.data data].array.
+		</div>
+
+		<h3>[property:Integer count]</h3>
+		<div>
+			The value of [page:InterleavedBufferAttribute.data data].count.
+
+			If the buffer is storing a 3-component vector (such as a position, normal, or color),
+			then this will count the number of such vectors stored.
+		</div>
+
 		<h3>[property:Integer itemSize]</h3>
 		<div>
 		</div>
@@ -47,16 +60,6 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:Integer count]()</h3>
-		<div>
-			The value of [page:InterleavedBufferAttribute.data data].count.
-		</div>
-
-		<h3>[method:Array array]()</h3>
-		<div>
-			The value of [page:InterleavedBufferAttribute.data data].array.
-		</div>
-
 		<h3>[method:null getX]( index ) </h3>
 		<div>
 

+ 2 - 2
docs/api/materials/PointsMaterial.html

@@ -44,11 +44,11 @@ for ( var i = 0; i < 10000; i ++ ) {
 	star.y = THREE.Math.randFloatSpread( 2000 );
 	star.z = THREE.Math.randFloatSpread( 2000 );
 
-	starsGeometry.vertices.push( star )
+	starsGeometry.vertices.push( star );
 
 }
 
-var starsMaterial = new THREE.PointsMaterial( { color: 0x888888 } )
+var starsMaterial = new THREE.PointsMaterial( { color: 0x888888 } );
 
 var starField = new THREE.Points( starsGeometry, starsMaterial );
 

+ 7 - 7
docs/api/math/Plane.html

@@ -12,7 +12,7 @@
 
 		<div class="desc">
 			A two dimensional surface that extends infinitely in 3d space, represented in [link:http://mathworld.wolfram.com/HessianNormalForm.html Hessian normal form]
-			by a normal vector and a constant.
+			by a unit length normal vector and a constant.
 		</div>
 
 
@@ -21,7 +21,7 @@
 
 		<h3>[name]( [page:Vector3 normal], [page:Float constant] )</h3>
 		<div>
-		[page:Vector3 normal] - (optional) a [page:Vector3] defining the normal of the plane. Default is *(1, 0, 0)*.<br />
+		[page:Vector3 normal] - (optional) a unit length [page:Vector3] defining the normal of the plane. Default is *(1, 0, 0)*.<br />
 		[page:Float constant] - (optional) the signed distance from the origin to the plane. Default is *0*.
 		</div>
 
@@ -130,7 +130,7 @@
 
 		<h3>[method:Plane set]( [page:Vector3 normal], [page:Float constant] )</h3>
 		<div>
-			[page:Vector3 normal] - a [page:Vector3] defining the normal of the plane.<br />
+			[page:Vector3 normal] - a unit length [page:Vector3] defining the normal of the plane.<br />
 			[page:Float constant] - the signed distance from the origin to the plane. Default is *0*.<br /><br />
 
 			 Sets the plane's [page:.normal normal] and [page:.constant constant] properties.
@@ -138,9 +138,9 @@
 
 		<h3>[method:Plane setComponents]( [page:Float x], [page:Float y], [page:Float z], [page:Float w] )</h3>
 		<div>
-		[page:Float x] - x value of the normal vector.<br />
-		[page:Float y] - y value of the normal vector.<br />
-		[page:Float z] - z value of the normal vector.<br />
+		[page:Float x] - x value of the unit length normal vector.<br />
+		[page:Float y] - y value of the unit length normal vector.<br />
+		[page:Float z] - z value of the unit length normal vector.<br />
 		[page:Float w] - the value of the plane's [page:.constant constant] property.<br /><br />
 
 		Set the individual components that define the plane.
@@ -158,7 +158,7 @@
 
 		<h3>[method:Plane setFromNormalAndCoplanarPoint]( [page:Vector3 normal], [page:Vector3 point] ) [page:Vector3 this]</h3>
 		<div>
-		[page:Vector3 normal] - a [page:Vector3] defining the normal of the plane.<br />
+		[page:Vector3 normal] - a unit length [page:Vector3] defining the normal of the plane.<br />
 		[page:Vector3 point] - [page:Vector3]<br /><br />
 
 		Sets the plane's properties as defined by a [page:Vector3 normal] and an arbitrary coplanar [page:Vector3 point].

+ 1 - 1
docs/api/renderers/WebGLRenderer.html

@@ -152,7 +152,7 @@
 		various WebGL extensions are supported.
 		</div>
 
-		<h3>[property:Boolean gammaFactor]</h3>
+		<h3>[property:Float gammaFactor]</h3>
 		<div>Default is *2*. </div>
 
 

+ 164 - 46
examples/js/loaders/ColladaLoader2.js

@@ -1885,6 +1885,24 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
+		function groupPrimitives( primitives ) {
+
+			var build = {};
+
+			for ( var i = 0; i < primitives.length; i ++ ) {
+
+				var primitive = primitives[ i ];
+
+				if ( build[ primitive.type ] === undefined ) build[ primitive.type ] = [];
+
+				build[ primitive.type ].push( primitive );
+
+			}
+
+			return build;
+
+		}
+
 		function buildGeometry( data ) {
 
 			var build = {};
@@ -1895,6 +1913,27 @@ THREE.ColladaLoader.prototype = {
 
 			if ( primitives.length === 0 ) return {};
 
+			// our goal is to create one buffer geoemtry for a single type of primitives
+			// first, we group all primitives by their type
+
+			var groupedPrimitives = groupPrimitives( primitives );
+
+			for ( var type in groupedPrimitives ) {
+
+				// second, we create for each type of primitives (polylist,triangles or lines) a buffer geometry
+
+				build[ type ] = buildGeometryType( groupedPrimitives[ type ], sources, vertices );
+
+			}
+
+			return build;
+
+		}
+
+		function buildGeometryType( primitives, sources, vertices ) {
+
+			var build = {};
+
 			var position = { array: [], stride: 0 };
 			var normal = { array: [], stride: 0 };
 			var uv = { array: [], stride: 0 };
@@ -1904,7 +1943,6 @@ THREE.ColladaLoader.prototype = {
 			var skinWeight = { array: [], stride: 4 };
 
 			var geometry = new THREE.BufferGeometry();
-			geometry.name = data.name || '';
 
 			var materialKeys = [];
 
@@ -1925,7 +1963,16 @@ THREE.ColladaLoader.prototype = {
 
 				// groups
 
-				count = primitive.count * 3 * triangleCount;
+				if ( primitive.type === 'lines' || primitive.type === 'linestrips' ) {
+
+					count = primitive.count * 2;
+
+				} else {
+
+					count = primitive.count * 3 * triangleCount;
+
+				}
+
 				geometry.addGroup( start, count, p );
 				start += count;
 
@@ -1944,17 +1991,39 @@ THREE.ColladaLoader.prototype = {
 						case 'VERTEX':
 							for ( var key in vertices ) {
 
-								if ( key === 'POSITION' ) {
+								var id =  vertices[ key ];
 
-									buildGeometryData( primitive, sources[ vertices[ key ] ], input.offset, position.array );
-									position.stride = sources[ vertices[ key ] ].stride;
+								switch ( key ) {
 
-									if ( sources.skinWeights && sources.skinIndices ) {
+									case 'POSITION':
+										buildGeometryData( primitive, sources[ id ], input.offset, position.array );
+										position.stride = sources[ id ].stride;
 
-										buildGeometryData( primitive, sources.skinIndices, input.offset, skinIndex.array );
-										buildGeometryData( primitive, sources.skinWeights, input.offset, skinWeight.array );
+										if ( sources.skinWeights && sources.skinIndices ) {
 
-									}
+											buildGeometryData( primitive, sources.skinIndices, input.offset, skinIndex.array );
+											buildGeometryData( primitive, sources.skinWeights, input.offset, skinWeight.array );
+
+										}
+										break;
+
+									case 'NORMAL':
+										buildGeometryData( primitive, sources[ id ], input.offset, normal.array );
+										normal.stride = sources[ id ].stride;
+										break;
+
+									case 'COLOR':
+										buildGeometryData( primitive, sources[ id ], input.offset, color.array );
+										color.stride = sources[ id ].stride;
+										break;
+
+									case 'TEXCOORD':
+										buildGeometryData( primitive, sources[ id ], input.offset, uv.array );
+										uv.stride = sources[ id ].stride;
+										break;
+
+									default:
+										console.warn( 'THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key );
 
 								}
 
@@ -2785,7 +2854,7 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
-		function getSkeleton( skeletons, joints ) {
+		function buildSkeleton( skeletons, joints ) {
 
 			var boneData = [];
 			var sortedBoneData = [];
@@ -2917,54 +2986,83 @@ THREE.ColladaLoader.prototype = {
 			var instanceGeometries = data.instanceGeometries;
 			var instanceNodes = data.instanceNodes;
 
+			// nodes
+
 			for ( var i = 0, l = nodes.length; i < l; i ++ ) {
 
 				objects.push( getNode( nodes[ i ] ) );
 
 			}
 
+			// instance cameras
+
 			for ( var i = 0, l = instanceCameras.length; i < l; i ++ ) {
 
 				objects.push( getCamera( instanceCameras[ i ] ).clone() );
 
 			}
 
+			// instance controllers
+
 			for ( var i = 0, l = instanceControllers.length; i < l; i ++ ) {
 
 				var instance = instanceControllers[ i ];
 				var controller = getController( instance.id );
-				var geometry = getGeometry( controller.id );
-				var materials = resolveMaterialBinding( geometry.materialKeys, instance.materials );
-				var object = getObject( geometry, materials );
+				var geometries = getGeometry( controller.id );
+				var newObjects = buildObjects( geometries, instance.materials );
 
 				var skeletons = instance.skeletons;
 				var joints = controller.skin.joints;
 
-				var skeleton = getSkeleton( skeletons, joints );
+				var skeleton = buildSkeleton( skeletons, joints );
 
-				object.bind( skeleton, controller.skin.bindMatrix );
-				object.normalizeSkinWeights();
+				for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) {
 
-				objects.push( object );
+					var object = newObjects[ j ];
+
+					if ( object.isSkinnedMesh ) {
+
+						object.bind( skeleton, controller.skin.bindMatrix );
+						object.normalizeSkinWeights();
+
+					}
+
+					objects.push( object );
+
+				}
 
 			}
 
+			// instance lights
+
 			for ( var i = 0, l = instanceLights.length; i < l; i ++ ) {
 
 				objects.push( getLight( instanceLights[ i ] ).clone() );
 
 			}
 
+			// instance geometries
+
 			for ( var i = 0, l = instanceGeometries.length; i < l; i ++ ) {
 
 				var instance = instanceGeometries[ i ];
-				var geometry = getGeometry( instance.id );
-				var materials = resolveMaterialBinding( geometry.materialKeys, instance.materials );
-				var object = getObject( geometry, materials );
-				objects.push( object );
+
+				// a single geometry instance in collada can lead to multiple object3Ds.
+				// this is the case when primitives are combined like triangles and lines
+
+				var geometries = getGeometry( instance.id );
+				var newObjects = buildObjects( geometries, instance.materials );
+
+				for ( var j = 0, jl = newObjects.length; j < jl; j ++ ) {
+
+					objects.push( newObjects[ j ] );
+
+				}
 
 			}
 
+			// instance nodes
+
 			for ( var i = 0, l = instanceNodes.length; i < l; i ++ ) {
 
 				objects.push( getNode( instanceNodes[ i ] ).clone() );
@@ -3012,50 +3110,60 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
-		function getObject( geometry, materials ) {
+		function buildObjects( geometries, instanceMaterials ) {
 
-			var object;
+			var objects = [];
+
+			for ( var type in geometries ) {
+
+				var geometry = geometries[ type ];
 
-			var skinning = ( geometry.data.attributes.skinIndex !== undefined );
+				var materials = resolveMaterialBinding( geometry.materialKeys, instanceMaterials );
 
-			if ( skinning ) {
+				var skinning = ( geometry.data.attributes.skinIndex !== undefined );
 
-				for ( var i = 0, l = materials.length; i < l; i ++ ) {
+				if ( skinning ) {
 
-					materials[ i ].skinning = true;
+					for ( var i = 0, l = materials.length; i < l; i ++ ) {
+
+						materials[ i ].skinning = true;
+
+					}
 
 				}
 
-			}
+				var material = ( materials.length === 1 ) ? materials[ 0 ] : materials;
 
-			var material = ( materials.length === 1 ) ? materials[ 0 ] : materials;
+				switch ( type ) {
 
-			switch ( geometry.type ) {
+					case 'lines':
+						object = new THREE.LineSegments( geometry.data, material );
+						break;
 
-				case 'lines':
-					object = new THREE.LineSegments( geometry.data, material );
-					break;
+					case 'linestrips':
+						object = new THREE.Line( geometry.data, material );
+						break;
 
-				case 'linestrips':
-					object = new THREE.Line( geometry.data, material );
-					break;
+					case 'triangles':
+					case 'polylist':
+						if ( skinning ) {
 
-				case 'triangles':
-				case 'polylist':
-					if ( skinning ) {
+							object = new THREE.SkinnedMesh( geometry.data, material );
 
-						object = new THREE.SkinnedMesh( geometry.data, material );
+						} else {
 
-					} else {
+							object = new THREE.Mesh( geometry.data, material );
 
-						object = new THREE.Mesh( geometry.data, material );
+						}
+						break;
 
-					}
-					break;
+				}
+
+				objects.push( object );
 
 			}
 
-			return object;
+			return objects;
 
 		}
 
@@ -3097,7 +3205,17 @@ THREE.ColladaLoader.prototype = {
 
 				var child = children[ i ];
 
-				group.add( getNode( child.id ) );
+				if ( child.id === null ) {
+
+					group.add( buildNode( child ) );
+
+				} else {
+
+					// if there is an ID, let's try to get the finished build (e.g. joints are already build)
+
+					group.add( getNode( child.id ) );
+
+				}
 
 			}
 

+ 91 - 58
examples/js/loaders/GLTF2Loader.js

@@ -22,7 +22,7 @@ THREE.GLTF2Loader = ( function () {
 
 			var scope = this;
 
-			var path = this.path && ( typeof this.path === "string" ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
+			var path = this.path && ( typeof this.path === 'string' ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
 
 			var loader = new THREE.FileLoader( scope.manager );
 
@@ -110,10 +110,10 @@ THREE.GLTF2Loader = ( function () {
 				console.timeEnd( 'GLTF2Loader' );
 
 				var glTF = {
-					"scene": scene,
-					"scenes": scenes,
-					"cameras": cameras,
-					"animations": animations
+					scene: scene,
+					scenes: scenes,
+					cameras: cameras,
+					animations: animations
 				};
 
 				callback( glTF );
@@ -229,26 +229,26 @@ THREE.GLTF2Loader = ( function () {
 
 			switch ( boundUniform.semantic ) {
 
-				case "MODELVIEW":
+				case 'MODELVIEW':
 
 					var m4 = boundUniform.uniform.value;
 					m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
 					break;
 
-				case "MODELVIEWINVERSETRANSPOSE":
+				case 'MODELVIEWINVERSETRANSPOSE':
 
 					var m3 = boundUniform.uniform.value;
 					this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
 					m3.getNormalMatrix( this._m4 );
 					break;
 
-				case "PROJECTION":
+				case 'PROJECTION':
 
 					var m4 = boundUniform.uniform.value;
 					m4.copy( camera.projectionMatrix );
 					break;
 
-				case "JOINTMATRIX":
+				case 'JOINTMATRIX':
 
 					var m4v = boundUniform.uniform.value;
 
@@ -270,7 +270,7 @@ THREE.GLTF2Loader = ( function () {
 
 				default :
 
-					console.warn( "Unhandled shader semantic: " + boundUniform.semantic );
+					console.warn( 'THREE.GLTF2Loader: Unhandled shader semantic: ' + boundUniform.semantic );
 					break;
 
 			}
@@ -362,7 +362,7 @@ THREE.GLTF2Loader = ( function () {
 
 				if ( light.fallOffExponent !== undefined ) {
 
-					console.warn( 'GLTF2Loader: light.fallOffExponent not currently supported.' );
+					console.warn( 'THREE.GLTF2Loader:: light.fallOffExponent not currently supported.' );
 
 				}
 
@@ -495,11 +495,11 @@ THREE.GLTF2Loader = ( function () {
 
 		if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
 
-			throw new Error( 'GLTF2Loader: Unsupported glTF-Binary header.' );
+			throw new Error( 'THREE.GLTF2Loader: Unsupported glTF-Binary header.' );
 
 		} else if ( this.header.version < 2.0 ) {
 
-			throw new Error( 'GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' );
+			throw new Error( 'THREE.GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' );
 
 		}
 
@@ -534,7 +534,7 @@ THREE.GLTF2Loader = ( function () {
 
 		if ( this.content === null ) {
 
-			throw new Error( 'GLTF2Loader: JSON content not found.' );
+			throw new Error( 'THREE.GLTF2Loader: JSON content not found.' );
 
 		}
 
@@ -582,7 +582,7 @@ THREE.GLTF2Loader = ( function () {
 
 		if ( ! materialParams.fragmentShader ) {
 
-			throw new Error( 'ERROR: Missing fragment shader definition:', program.fragmentShader );
+			throw new Error( 'THREE.GLTF2Loader: Missing fragment shader definition: ', program.fragmentShader );
 
 		}
 
@@ -590,7 +590,7 @@ THREE.GLTF2Loader = ( function () {
 
 		if ( ! vertexShader ) {
 
-			throw new Error( 'ERROR: Missing vertex shader definition:', program.vertexShader );
+			throw new Error( 'THREE.GLTF2Loader: Missing vertex shader definition: ', program.vertexShader );
 
 		}
 
@@ -659,7 +659,7 @@ THREE.GLTF2Loader = ( function () {
 					case WEBGL_CONSTANTS.FLOAT_MAT2:
 
 						// what to do?
-						console.warn( 'FLOAT_MAT2 is not a supported uniform type' );
+						console.warn( 'THREE.GLTF2Loader: FLOAT_MAT2 is not a supported uniform type.' );
 						break;
 
 					case WEBGL_CONSTANTS.FLOAT_MAT4:
@@ -734,7 +734,7 @@ THREE.GLTF2Loader = ( function () {
 
 			} else {
 
-				throw new Error( 'Unknown shader uniform param type: ' + ptype );
+				throw new Error( 'THREE.GLTF2Loader: Unknown shader uniform param type: ' + ptype );
 
 			}
 
@@ -781,7 +781,7 @@ THREE.GLTF2Loader = ( function () {
 
 				default:
 
-					throw new Error( "Unknown technique.states.enable: " + enable );
+					throw new Error( 'THREE.GLTF2Loader: Unknown technique.states.enable: ' + enable );
 
 			}
 
@@ -980,6 +980,8 @@ THREE.GLTF2Loader = ( function () {
 					transparent: params.transparent
 				} );
 
+				material.isGLTFSpecularGlossinessMaterial = true;
+
 				material.color = params.color;
 
 				material.map = params.map === undefined ? null : params.map;
@@ -1280,6 +1282,8 @@ THREE.GLTF2Loader = ( function () {
 	};
 
 	var INTERPOLATION = {
+		CATMULLROMSPLINE: THREE.InterpolateSmooth,
+		CUBICSPLINE: THREE.InterpolateSmooth,
 		LINEAR: THREE.InterpolateLinear,
 		STEP: THREE.InterpolateDiscrete
 	};
@@ -1487,7 +1491,7 @@ THREE.GLTF2Loader = ( function () {
 			var param = params[ pname ];
 			var semantic = param.semantic;
 
-			var regEx = new RegExp( "\\b" + pname + "\\b", "g" );
+			var regEx = new RegExp( '\\b' + pname + '\\b', 'g' );
 
 			switch ( semantic ) {
 
@@ -1612,7 +1616,7 @@ THREE.GLTF2Loader = ( function () {
 		for ( var i = 0; i < dependencies.length; i ++ ) {
 
 			var dependency = dependencies[ i ];
-			var fnName = "load" + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 );
+			var fnName = 'load' + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 );
 
 			var cached = this.cache.get( dependency );
 
@@ -1649,9 +1653,9 @@ THREE.GLTF2Loader = ( function () {
 		// Fire the callback on complete
 		this._withDependencies( [
 
-			"scenes",
-			"cameras",
-			"animations"
+			'scenes',
+			'cameras',
+			'animations'
 
 		] ).then( function ( dependencies ) {
 
@@ -1696,7 +1700,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"bufferViews"
+			'bufferViews'
 
 		] ).then( function ( dependencies ) {
 
@@ -1763,7 +1767,7 @@ THREE.GLTF2Loader = ( function () {
 
 			} else {
 
-				console.warn( 'THREE.GLTF2Loader: ' + buffer.type + ' buffer type is not supported' );
+				console.warn( 'THREE.GLTF2Loader: %s buffer type is not supported.', buffer.type );
 
 			}
 
@@ -1777,7 +1781,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"buffers"
+			'buffers'
 
 		] ).then( function ( dependencies ) {
 
@@ -1802,7 +1806,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"bufferViews"
+			'bufferViews'
 
 		] ).then( function ( dependencies ) {
 
@@ -1850,7 +1854,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"bufferViews"
+			'bufferViews'
 
 		] ).then( function ( dependencies ) {
 
@@ -1900,7 +1904,7 @@ THREE.GLTF2Loader = ( function () {
 
 							if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) {
 
-								console.warn( 'THREE.GLTF2Loader: Three.js doesn\'t support texture internalFormat which is different from texture format. ' +
+								console.warn( 'THREE.GLTF2Loader: Three.js does not support texture internalFormat which is different from texture format. ' +
 															'internalFormat will be forced to be the same value as format.' );
 
 							}
@@ -1911,7 +1915,7 @@ THREE.GLTF2Loader = ( function () {
 							var sampler = samplers[ texture.sampler ] || {};
 
 							_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
-							_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.NearestMipMapLinearFilter;
+							_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipMapLinearFilter;
 							_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
 							_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
 
@@ -2082,6 +2086,10 @@ THREE.GLTF2Loader = ( function () {
 
 				if ( material.name !== undefined ) _material.name = material.name;
 
+				// Normal map textures use OpenGL conventions:
+				// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture
+				_material.normalScale.x = -1;
+
 				return _material;
 
 			} );
@@ -2096,8 +2104,8 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"accessors",
-			"materials"
+			'accessors',
+			'materials'
 
 		] ).then( function ( dependencies ) {
 
@@ -2191,7 +2199,7 @@ THREE.GLTF2Loader = ( function () {
 								&& geometry.attributes.uv2 === undefined
 								&& geometry.attributes.uv !== undefined ) {
 
-							console.log( 'GLTF2Loader: Duplicating UVs to support aoMap.' );
+							console.log( 'THREE.GLTF2Loader: Duplicating UVs to support aoMap.' );
 							geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
 
 						}
@@ -2235,13 +2243,18 @@ THREE.GLTF2Loader = ( function () {
 									positionAttribute = dependencies.accessors[ target.POSITION ].clone();
 									var position = geometry.attributes.position;
 
-									for ( var j = 0, jl = positionAttribute.array.length; j < jl; j ++ ) {
+									for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) {
 
-										positionAttribute.array[ j ] += position.array[ j ];
+										positionAttribute.setXYZ(
+											j,
+											positionAttribute.getX( j ) + position.getX( j ),
+											positionAttribute.getY( j ) + position.getY( j ),
+											positionAttribute.getZ( j ) + position.getZ( j )
+										);
 
 									}
 
-								} else {
+								} else if ( geometry.attributes.position ) {
 
 									// Copying the original position not to affect the final position.
 									// See the formula above.
@@ -2258,13 +2271,18 @@ THREE.GLTF2Loader = ( function () {
 									normalAttribute = dependencies.accessors[ target.NORMAL ].clone();
 									var normal = geometry.attributes.normal;
 
-									for ( var j = 0, jl = normalAttribute.array.length; j < jl; j ++ ) {
+									for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) {
 
-										normalAttribute.array[ j ] += normal.array[ j ];
+										normalAttribute.setXYZ(
+											j,
+											normalAttribute.getX( j ) + normal.getX( j ),
+											normalAttribute.getY( j ) + normal.getY( j ),
+											normalAttribute.getZ( j ) + normal.getZ( j )
+										);
 
 									}
 
-								} else {
+								} else if ( geometry.attributes.normal ) {
 
 									normalAttribute = geometry.attributes.normal.clone();
 
@@ -2275,11 +2293,19 @@ THREE.GLTF2Loader = ( function () {
 
 								}
 
-								positionAttribute.name = attributeName;
-								normalAttribute.name = attributeName;
+								if ( positionAttribute ) {
+
+									positionAttribute.name = attributeName;
+									morphAttributes.position.push( positionAttribute );
+
+								}
+
+								if ( normalAttribute ) {
 
-								morphAttributes.position.push( positionAttribute );
-								morphAttributes.normal.push( normalAttribute );
+									normalAttribute.name = attributeName;
+									morphAttributes.normal.push( normalAttribute );
+
+								}
 
 							}
 
@@ -2337,7 +2363,7 @@ THREE.GLTF2Loader = ( function () {
 
 					} else {
 
-						throw new Error( 'Only triangular and line primitives are supported' );
+						throw new Error( 'THREE.GLTF2Loader: Only triangular and line primitives are supported.' );
 
 					}
 
@@ -2379,7 +2405,7 @@ THREE.GLTF2Loader = ( function () {
 
 			if ( !params ) {
 
-				console.warn( 'GLTF2Loader: Missing camera parameters.' );
+				console.warn( 'THREE.GLTF2Loader: Missing camera parameters.' );
 				return;
 
 			}
@@ -2412,7 +2438,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"accessors"
+			'accessors'
 
 		] ).then( function ( dependencies ) {
 
@@ -2437,8 +2463,8 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"accessors",
-			"nodes"
+			'accessors',
+			'nodes'
 
 		] ).then( function ( dependencies ) {
 
@@ -2492,6 +2518,13 @@ THREE.GLTF2Loader = ( function () {
 							}
 
 							var targetName = node.name ? node.name : node.uuid;
+
+							if ( sampler.interpolation === 'CATMULLROMSPLINE' ) {
+
+								console.warn( 'THREE.GLTF2Loader: CATMULLROMSPLINE interpolation is not supported. Using CUBICSPLINE instead.' );
+
+							}
+
 							var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear;
 
 							var targetNames = [];
@@ -2539,7 +2572,7 @@ THREE.GLTF2Loader = ( function () {
 
 				}
 
-				var name = animation.name !== undefined ? animation.name : "animation_" + animationId;
+				var name = animation.name !== undefined ? animation.name : 'animation_' + animationId;
 
 				return new THREE.AnimationClip( name, undefined, tracks );
 
@@ -2617,9 +2650,9 @@ THREE.GLTF2Loader = ( function () {
 
 			return scope._withDependencies( [
 
-				"meshes",
-				"skins",
-				"cameras"
+				'meshes',
+				'skins',
+				'cameras'
 
 			] ).then( function ( dependencies ) {
 
@@ -2635,7 +2668,7 @@ THREE.GLTF2Loader = ( function () {
 
 					} else if ( node.meshes !== undefined ) {
 
-						console.warn( 'GLTF2Loader: Legacy glTF file detected. Nodes may have no more than 1 mesh.' );
+						console.warn( 'THREE.GLTF2Loader: Legacy glTF file detected. Nodes may have no more than one mesh.' );
 
 						meshes = node.meshes;
 
@@ -2650,7 +2683,7 @@ THREE.GLTF2Loader = ( function () {
 
 							if ( group === undefined ) {
 
-								console.warn( 'GLTF2Loader: Couldn\'t find node "' + mesh + '".' );
+								console.warn( 'THREE.GLTF2Loader: Could not find node "' + mesh + '".' );
 								continue;
 
 							}
@@ -2743,7 +2776,7 @@ THREE.GLTF2Loader = ( function () {
 
 										} else {
 
-											console.warn( "WARNING: joint: '" + jointId + "' could not be found" );
+											console.warn( 'THREE.GLTF2Loader: Joint "%s" could not be found.', jointId );
 
 										}
 
@@ -2819,7 +2852,7 @@ THREE.GLTF2Loader = ( function () {
 
 		return this._withDependencies( [
 
-			"nodes"
+			'nodes'
 
 		] ).then( function ( dependencies ) {
 
@@ -2852,7 +2885,7 @@ THREE.GLTF2Loader = ( function () {
 					}
 
 					// for Specular-Glossiness.
-					if ( child.material && child.material.type === 'ShaderMaterial' ) {
+					if ( child.material && child.material.isGLTFSpecularGlossinessMaterial ) {
 
 						child.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
 

+ 6 - 0
examples/js/loaders/MTLLoader.js

@@ -421,6 +421,12 @@ THREE.MTLLoader.MaterialCreator.prototype = {
 
 					break;
 
+				case 'norm':
+
+					setMapForType( "normalMap", value );
+
+					break;
+
 				case 'map_bump':
 				case 'bump':
 

+ 13 - 13
examples/js/shaders/SSAOShader.js

@@ -20,9 +20,10 @@ THREE.SSAOShader = {
 		"size":         { value: new THREE.Vector2( 512, 512 ) },
 		"cameraNear":   { value: 1 },
 		"cameraFar":    { value: 100 },
+		"radius":       { value: 32 },
 		"onlyAO":       { value: 0 },
-		"aoClamp":      { value: 0.5 },
-		"lumInfluence": { value: 0.5 }
+		"aoClamp":      { value: 0.25 },
+		"lumInfluence": { value: 0.7 }
 
 	},
 
@@ -48,6 +49,7 @@ THREE.SSAOShader = {
 			"uniform float logDepthBufFC;",
 		"#endif",
 
+		"uniform float radius;",     // ao radius
 		"uniform bool onlyAO;",      // use only ambient occlusion pass?
 
 		"uniform vec2 size;",        // texture width, height
@@ -66,11 +68,10 @@ THREE.SSAOShader = {
 
 		// user variables
 
-		"const int samples = 8;",     // ao sample count
-		"const float radius = 5.0;",  // ao radius
+		"const int samples = 64;",     // ao sample count
 
-		"const bool useNoise = false;",      // use noise instead of pattern for sample dithering
-		"const float noiseAmount = 0.0003;", // dithering amount
+		"const bool useNoise = true;",      // use noise instead of pattern for sample dithering
+		"const float noiseAmount = 0.0004;", // dithering amount
 
 		"const float diffArea = 0.4;",   // self-shadowing reduction
 		"const float gDisplace = 0.4;",  // gauss bell center
@@ -131,7 +132,7 @@ THREE.SSAOShader = {
 
 		"float compareDepths( const in float depth1, const in float depth2, inout int far ) {",
 
-			"float garea = 2.0;",                         // gauss bell width
+			"float garea = 8.0;",                         // gauss bell width
 			"float diff = ( depth1 - depth2 ) * 100.0;",  // depth difference (0-100)
 
 			// reduce left bell width to avoid self-shadowing
@@ -147,18 +148,17 @@ THREE.SSAOShader = {
 			"}",
 
 			"float dd = diff - gDisplace;",
-			"float gauss = pow( EULER, -2.0 * dd * dd / ( garea * garea ) );",
+			"float gauss = pow( EULER, -2.0 * ( dd * dd ) / ( garea * garea ) );",
 			"return gauss;",
 
 		"}",
 
 		"float calcAO( float depth, float dw, float dh ) {",
 
-			"float dd = radius - depth * radius;",
 			"vec2 vv = vec2( dw, dh );",
 
-			"vec2 coord1 = vUv + dd * vv;",
-			"vec2 coord2 = vUv - dd * vv;",
+			"vec2 coord1 = vUv + radius * vv;",
+			"vec2 coord2 = vUv - radius * vv;",
 
 			"float temp1 = 0.0;",
 			"float temp2 = 0.0;",
@@ -186,14 +186,14 @@ THREE.SSAOShader = {
 
 			"float tt = clamp( depth, aoClamp, 1.0 );",
 
-			"float w = ( 1.0 / size.x )  / tt + ( noise.x * ( 1.0 - noise.x ) );",
+			"float w = ( 1.0 / size.x ) / tt + ( noise.x * ( 1.0 - noise.x ) );",
 			"float h = ( 1.0 / size.y ) / tt + ( noise.y * ( 1.0 - noise.y ) );",
 
 			"float ao = 0.0;",
 
 			"float dz = 1.0 / float( samples );",
-			"float z = 1.0 - dz / 2.0;",
 			"float l = 0.0;",
+			"float z = 1.0 - dz / 2.0;",
 
 			"for ( int i = 0; i <= samples; i ++ ) {",
 

+ 10 - 22
examples/webgl_postprocessing_ssao.html

@@ -67,8 +67,7 @@ Spiral sampling http://web.archive.org/web/20120421191837/http://www.cgafaq.info
 			var depthMaterial, effectComposer, depthRenderTarget;
 			var ssaoPass;
 			var group;
-			var depthScale = 1.0;
-			var postprocessing = { enabled : true, renderMode: 0 }; // renderMode: 0('framebuffer'), 1('onlyAO')
+			var postprocessing = { enabled: true, ao_only: false, radius: 32 };
 
 			init();
 			animate();
@@ -79,7 +78,6 @@ Spiral sampling http://web.archive.org/web/20120421191837/http://www.cgafaq.info
 				document.body.appendChild( container );
 
 				renderer = new THREE.WebGLRenderer( { antialias: false } );
-				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );
 
@@ -92,7 +90,7 @@ Spiral sampling http://web.archive.org/web/20120421191837/http://www.cgafaq.info
 				group = new THREE.Object3D();
 				scene.add( group );
 
-				var geometry = new THREE.IcosahedronGeometry( 5, 1 );
+				var geometry = new THREE.BoxGeometry( 10, 10, 10 );
 				for ( var i = 0; i < 200; i ++ ) {
 
 					var material = new THREE.MeshBasicMaterial();
@@ -121,30 +119,23 @@ Spiral sampling http://web.archive.org/web/20120421191837/http://www.cgafaq.info
 
 				// Init gui
 				var gui = new dat.GUI();
-				gui.add( postprocessing, "enabled" ).onChange();
-				gui.add( postprocessing, "renderMode", { framebuffer: 0, onlyAO: 1 } ).onChange( renderModeChange ).listen();
+				gui.add( postprocessing, "enabled" );
+				gui.add( postprocessing, "ao_only", false ).onChange( renderModeChange );
+				gui.add( postprocessing, "radius" ).min( 0 ).max( 64 ).onChange( radiusChange );
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
 			}
 
-			function renderModeChange( value ) {
-
-				if ( value == 0 ) {
-
-					// framebuffer
-					ssaoPass.uniforms[ 'onlyAO' ].value = false;
-
-				} else if ( value == 1 ) {
+			function radiusChange( value ) {
 
-					// onlyAO
-					ssaoPass.uniforms[ 'onlyAO' ].value = true;
+				ssaoPass.uniforms[ 'radius' ].value = value;
 
-				} else {
+			}
 
-					console.error( "Not define renderModeChange type: " + value );
+			function renderModeChange( value ) {
 
-				}
+				ssaoPass.uniforms[ 'onlyAO' ].value = value;
 
 			}
 
@@ -190,9 +181,6 @@ Spiral sampling http://web.archive.org/web/20120421191837/http://www.cgafaq.info
 				ssaoPass.uniforms[ 'size' ].value.set( window.innerWidth, window.innerHeight );
 				ssaoPass.uniforms[ 'cameraNear' ].value = camera.near;
 				ssaoPass.uniforms[ 'cameraFar' ].value = camera.far;
-				ssaoPass.uniforms[ 'onlyAO' ].value = ( postprocessing.renderMode == 1 );
-				ssaoPass.uniforms[ 'aoClamp' ].value = 0.3;
-				ssaoPass.uniforms[ 'lumInfluence' ].value = 0.5;
 
 				// Add pass to effect composer
 				effectComposer = new THREE.EffectComposer( renderer );

+ 2 - 2
examples/webvr_cubes.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - cubes</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			body {
 				font-family: Monospace;

+ 2 - 2
examples/webvr_daydream.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - daydream</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			body {
 				font-family: Monospace;

+ 2 - 2
examples/webvr_panorama.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - panorama</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			html, body {
 				background-color: #000;

+ 2 - 2
examples/webvr_rollercoaster.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - roller coaster</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			body {
 				margin: 0px;

+ 2 - 2
examples/webvr_sandbox.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - sandbox</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			body {
 				margin: 0px;

+ 2 - 2
examples/webvr_video.html

@@ -4,8 +4,8 @@
 		<title>three.js webvr - video</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-07-28 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-07-28" content="Ave6CPNUgSwHb3vCbyd55P/R7pfkwNniUJsYfSoUqI+l1X1BIOt6HfriVP0g2hmaG7Pp3qaUXuXdZeqGBmoMKg8AAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwMTI2NzQwNX0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M59+), origin = https://threejs.org, expires = 2017-09-03 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M59+)" data-expires="2017-09-03" content="Aqj+6XPnxHoSxQj/CDjGXhzrVFeP12/Pgvj7rfqFqVzodYX0ZXJNHTJIvCVExQfvip+Zq9GES+SOQ/zJlpceZgcAAABNeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMSIsImV4cGlyeSI6MTUwNDQ2NDIwNn0=">
 		<style>
 			body {
 				font-family: Monospace;

+ 1 - 1
src/animation/KeyframeTrack.js

@@ -62,7 +62,7 @@ Object.assign( KeyframeTrack, {
 
 		} else {
 
-			// by default, we asssume a constructor compatible with the base
+			// by default, we assume a constructor compatible with the base
 			return new trackType(
 					json.name, json.times, json.values, json.interpolation );
 

+ 2 - 0
src/math/Plane.js

@@ -7,6 +7,8 @@ import { Vector3 } from './Vector3';
 
 function Plane( normal, constant ) {
 
+	// normal is assumed to be normalized
+
 	this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );
 	this.constant = ( constant !== undefined ) ? constant : 0;
 

+ 1 - 9
src/math/Sphere.js

@@ -107,15 +107,7 @@ Object.assign( Sphere.prototype, {
 
 	intersectsPlane: function ( plane ) {
 
-		// We use the following equation to compute the signed distance from
-		// the center of the sphere to the plane.
-		//
-		// distance = q * n - d
-		//
-		// If this distance is greater than the radius of the sphere,
-		// then there is no intersection.
-
-		return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius;
+		return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;
 
 	},
 

+ 42 - 80
src/renderers/WebGLRenderer.js

@@ -12,6 +12,7 @@ import { WebGLShadowMap } from './webgl/WebGLShadowMap';
 import { WebGLAttributes } from './webgl/WebGLAttributes';
 import { WebGLBackground } from './webgl/WebGLBackground';
 import { WebGLRenderLists } from './webgl/WebGLRenderLists';
+import { WebGLMorphtargets } from './webgl/WebGLMorphtargets';
 import { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer';
 import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer';
 import { WebGLGeometries } from './webgl/WebGLGeometries';
@@ -60,8 +61,6 @@ function WebGLRenderer( parameters ) {
 
 	var currentRenderList = null;
 
-	var morphInfluences = new Float32Array( 8 );
-
 	var spritesArray = [];
 	var flaresArray = [];
 
@@ -245,7 +244,7 @@ function WebGLRenderer( parameters ) {
 	var properties, textures, attributes, geometries, objects, lights;
 	var programCache, renderLists;
 
-	var background, bufferRenderer, indexedBufferRenderer;
+	var background, morphtargets, bufferRenderer, indexedBufferRenderer;
 	var flareRenderer, spriteRenderer;
 
 	function initGLContext() {
@@ -276,6 +275,7 @@ function WebGLRenderer( parameters ) {
 		attributes = new WebGLAttributes( _gl );
 		geometries = new WebGLGeometries( _gl, attributes, _infoMemory );
 		objects = new WebGLObjects( geometries, _infoRender );
+		morphtargets = new WebGLMorphtargets( _gl );
 		programCache = new WebGLPrograms( _this, extensions, capabilities );
 		lights = new WebGLLights();
 		renderLists = new WebGLRenderLists();
@@ -657,12 +657,6 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	function absNumericalSort( a, b ) {
-
-		return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] );
-
-	}
-
 	this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {
 
 		state.setMaterial( material );
@@ -679,61 +673,9 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		// morph targets
-
-		var morphTargetInfluences = object.morphTargetInfluences;
-
-		if ( morphTargetInfluences !== undefined ) {
-
-			// TODO Remove allocations
-
-			var activeInfluences = [];
-
-			for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) {
-
-				var influence = morphTargetInfluences[ i ];
-				activeInfluences.push( [ influence, i ] );
-
-			}
-
-			activeInfluences.sort( absNumericalSort );
-
-			if ( activeInfluences.length > 8 ) {
-
-				activeInfluences.length = 8;
-
-			}
-
-			var morphAttributes = geometry.morphAttributes;
-
-			for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) {
-
-				var influence = activeInfluences[ i ];
-				morphInfluences[ i ] = influence[ 0 ];
-
-				if ( influence[ 0 ] !== 0 ) {
-
-					var index = influence[ 1 ];
-
-					if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] );
-					if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] );
-
-				} else {
-
-					if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i );
-					if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i );
-
-				}
-
-			}
-
-			for ( var i = activeInfluences.length, il = morphInfluences.length; i < il; i ++ ) {
+		if ( object.morphTargetInfluences ) {
 
-				morphInfluences[ i ] = 0.0;
-
-			}
-
-			program.getUniforms().setValue( _gl, 'morphTargetInfluences', morphInfluences );
+			morphtargets.update( object, geometry, material, program );
 
 			updateBuffers = true;
 
@@ -1121,8 +1063,6 @@ function WebGLRenderer( parameters ) {
 
 		projectObject( scene, camera, _this.sortObjects );
 
-		currentRenderList.finish();
-
 		if ( _this.sortObjects === true ) {
 
 			currentRenderList.sort();
@@ -1904,7 +1844,11 @@ function WebGLRenderer( parameters ) {
 
 		uniforms.opacity.value = material.opacity;
 
-		uniforms.diffuse.value = material.color;
+		if ( material.color ) {
+
+			uniforms.diffuse.value = material.color;
+
+		}
 
 		if ( material.emissive ) {
 
@@ -1912,9 +1856,38 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		uniforms.map.value = material.map;
-		uniforms.specularMap.value = material.specularMap;
-		uniforms.alphaMap.value = material.alphaMap;
+		if ( material.map ) {
+
+			uniforms.map.value = material.map;
+
+		}
+
+		if ( material.alphaMap ) {
+
+			uniforms.alphaMap.value = material.alphaMap;
+
+		}
+
+		if ( material.specularMap ) {
+
+			uniforms.specularMap.value = material.specularMap;
+
+		}
+
+		if ( material.envMap ) {
+
+			uniforms.envMap.value = material.envMap;
+
+			// don't flip CubeTexture envMaps, flip everything else:
+			//  WebGLRenderTargetCube will be flipped for backwards compatibility
+			//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
+			// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
+			uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
+
+			uniforms.reflectivity.value = material.reflectivity;
+			uniforms.refractionRatio.value = material.refractionRatio;
+
+		}
 
 		if ( material.lightMap ) {
 
@@ -1994,17 +1967,6 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		uniforms.envMap.value = material.envMap;
-
-		// don't flip CubeTexture envMaps, flip everything else:
-		//  WebGLRenderTargetCube will be flipped for backwards compatibility
-		//  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
-		// this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
-		uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;
-
-		uniforms.reflectivity.value = material.reflectivity;
-		uniforms.refractionRatio.value = material.refractionRatio;
-
 	}
 
 	function refreshUniformsLine( uniforms, material ) {

+ 7 - 5
src/renderers/shaders/ShaderLib.js

@@ -16,6 +16,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.fog
@@ -30,6 +32,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -49,6 +53,8 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.specularmap,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -74,6 +80,7 @@ var ShaderLib = {
 
 		uniforms: UniformsUtils.merge( [
 			UniformsLib.common,
+			UniformsLib.envmap,
 			UniformsLib.aomap,
 			UniformsLib.lightmap,
 			UniformsLib.emissivemap,
@@ -172,15 +179,10 @@ var ShaderLib = {
 
 	},
 
-	/* -------------------------------------------------------------------------
-	//	Cube map shader
-	 ------------------------------------------------------------------------- */
-
 	equirect: {
 
 		uniforms: {
 			tEquirect: { value: null },
-			tFlip: { value: - 1 }
 		},
 
 		vertexShader: ShaderChunk.equirect_vert,

+ 0 - 2
src/renderers/shaders/ShaderLib/cube_frag.glsl

@@ -4,8 +4,6 @@ uniform float opacity;
 
 varying vec3 vWorldPosition;
 
-#include <common>
-
 void main() {
 
 	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );

+ 1 - 3
src/renderers/shaders/ShaderLib/cube_vert.glsl

@@ -5,8 +5,6 @@ varying vec3 vWorldPosition;
 void main() {
 
 	vWorldPosition = transformDirection( position, modelMatrix );
-
-	#include <begin_vertex>
-	#include <project_vertex>
+	gl_Position = projectionMatrix * vec4( normalMatrix * position, 1.0 );
 
 }

+ 5 - 3
src/renderers/shaders/ShaderLib/equirect_frag.glsl

@@ -1,5 +1,4 @@
 uniform sampler2D tEquirect;
-uniform float tFlip;
 
 varying vec3 vWorldPosition;
 
@@ -7,11 +6,14 @@ varying vec3 vWorldPosition;
 
 void main() {
 
-	// 	gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );
 	vec3 direction = normalize( vWorldPosition );
+
 	vec2 sampleUV;
-	sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );
+
+	sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;
+
 	sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;
+
 	gl_FragColor = texture2D( tEquirect, sampleUV );
 
 }

+ 10 - 1
src/renderers/shaders/UniformsLib.js

@@ -17,9 +17,18 @@ var UniformsLib = {
 		map: { value: null },
 		offsetRepeat: { value: new Vector4( 0, 0, 1, 1 ) },
 
-		specularMap: { value: null },
 		alphaMap: { value: null },
 
+	},
+
+	specularmap: {
+
+		specularMap: { value: null },
+
+	},
+
+	envmap: {
+
 		envMap: { value: null },
 		flipEnvMap: { value: - 1 },
 		reflectivity: { value: 1.0 },

+ 5 - 11
src/renderers/webgl/WebGLBackground.js

@@ -19,7 +19,7 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 	var clearAlpha = 0;
 
 	var planeCamera, planeMesh;
-	var boxCamera, boxMesh;
+	var boxMesh;
 
 	function render( scene, camera, forceClear ) {
 
@@ -44,9 +44,7 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 
 		if ( background && background.isCubeTexture ) {
 
-			if ( boxCamera === undefined ) {
-
-				boxCamera = new PerspectiveCamera();
+			if ( boxMesh === undefined ) {
 
 				boxMesh = new Mesh(
 					new BoxBufferGeometry( 5, 5, 5 ),
@@ -63,17 +61,13 @@ function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {
 
 			}
 
-			boxCamera.projectionMatrix.copy( camera.projectionMatrix );
-
-			boxCamera.matrixWorld.extractRotation( camera.matrixWorld );
-			boxCamera.matrixWorldInverse.getInverse( boxCamera.matrixWorld );
-
 			boxMesh.material.uniforms[ "tCube" ].value = background;
-			boxMesh.modelViewMatrix.multiplyMatrices( boxCamera.matrixWorldInverse, boxMesh.matrixWorld );
+			boxMesh.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, boxMesh.matrixWorld );
+			boxMesh.normalMatrix.getNormalMatrix( boxMesh.modelViewMatrix );
 
 			geometries.update( boxMesh.geometry );
 
-			renderer.renderBufferDirect( boxCamera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
+			renderer.renderBufferDirect( camera, null, boxMesh.geometry, boxMesh.material, boxMesh, null );
 
 		} else if ( background && background.isTexture ) {
 

+ 111 - 0
src/renderers/webgl/WebGLMorphtargets.js

@@ -0,0 +1,111 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+function absNumericalSort( a, b ) {
+
+	return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );
+
+}
+
+function WebGLMorphtargets( gl ) {
+
+	var influencesList = {};
+	var morphInfluences = new Float32Array( 8 );
+
+	function update( object, geometry, material, program ) {
+
+		var objectInfluences = object.morphTargetInfluences;
+
+		var length = objectInfluences.length;
+
+		var influences = influencesList[ geometry.id ];
+
+		if ( influences === undefined ) {
+
+			// initialise list
+
+			influences = [];
+
+			for ( var i = 0; i < length; i ++ ) {
+
+				influences[ i ] = [ i, 0 ];
+
+			}
+
+			influencesList[ geometry.id ] = influences;
+
+		}
+
+		var morphTargets = material.morphTargets && geometry.morphAttributes.position;
+		var morphNormals = material.morphNormals && geometry.morphAttributes.normal;
+
+		// Remove current morphAttributes
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			var influence = influences[ i ];
+
+			if ( influence[ 1 ] !== 0 ) {
+
+				if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );
+				if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );
+
+			}
+
+		}
+
+		// Collect influences
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			var influence = influences[ i ];
+
+			influence[ 0 ] = i;
+			influence[ 1 ] = objectInfluences[ i ];
+
+		}
+
+		influences.sort( absNumericalSort );
+
+		// Add morphAttributes
+
+		for ( var i = 0; i < 8; i ++ ) {
+
+			var influence = influences[ i ];
+
+			if ( influence ) {
+
+				var index = influence[ 0 ];
+				var value = influence[ 1 ];
+
+				if ( value ) {
+
+					if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );
+					if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );
+
+					morphInfluences[ i ] = value;
+					continue;
+
+				}
+
+			}
+
+			morphInfluences[ i ] = 0;
+
+		}
+
+		program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );
+
+	}
+
+	return {
+
+		update: update
+
+	}
+
+}
+
+
+export { WebGLMorphtargets };

+ 24 - 45
src/renderers/webgl/WebGLRenderLists.js

@@ -48,43 +48,42 @@ function reversePainterSortStable( a, b ) {
 
 function WebGLRenderList() {
 
-	var opaque = [];
-	var opaqueLastIndex = - 1;
+	var renderItems = [];
+	var renderItemsIndex = 0;
 
+	var opaque = [];
 	var transparent = [];
-	var transparentLastIndex = - 1;
 
 	function init() {
 
-		opaqueLastIndex = - 1;
-		transparentLastIndex = - 1;
+		renderItemsIndex = 0;
+
+		opaque.length = 0;
+		transparent.length = 0;
 
 	}
 
 	function push( object, geometry, material, z, group ) {
 
-		var array, index;
+		var renderItem = renderItems[ renderItemsIndex ];
 
-		// allocate the next position in the appropriate array
+		if ( renderItem === undefined ) {
 
-		if ( material.transparent ) {
+			renderItem = {
+				id: object.id,
+				object: object,
+				geometry: geometry,
+				material: material,
+				program: material.program,
+				renderOrder: object.renderOrder,
+				z: z,
+				group: group
+			};
 
-			array = transparent;
-			index = ++ transparentLastIndex;
+			renderItems[ renderItemsIndex ] = renderItem;
 
 		} else {
 
-			array = opaque;
-			index = ++ opaqueLastIndex;
-
-		}
-
-		// recycle existing render item or grow the array
-
-		var renderItem = array[ index ];
-
-		if ( renderItem ) {
-
 			renderItem.id = object.id;
 			renderItem.object = object;
 			renderItem.geometry = geometry;
@@ -94,37 +93,18 @@ function WebGLRenderList() {
 			renderItem.z = z;
 			renderItem.group = group;
 
-		} else {
-
-			renderItem = {
-				id: object.id,
-				object: object,
-				geometry: geometry,
-				material: material,
-				program: material.program,
-				renderOrder: object.renderOrder,
-				z: z,
-				group: group
-			};
-
-			// assert( index === array.length );
-			array.push( renderItem );
-
 		}
 
-	}
-
-	function finish() {
+		( material.transparent === true ? transparent : opaque ).push( renderItem );
 
-		opaque.length = opaqueLastIndex + 1;
-		transparent.length = transparentLastIndex + 1;
+		renderItemsIndex ++;
 
 	}
 
 	function sort() {
 
-		opaque.sort( painterSortStable );
-		transparent.sort( reversePainterSortStable );
+		if ( opaque.length > 1 ) opaque.sort( painterSortStable );
+		if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );
 
 	}
 
@@ -134,7 +114,6 @@ function WebGLRenderList() {
 
 		init: init,
 		push: push,
-		finish: finish,
 
 		sort: sort
 	};

+ 6 - 6
utils/exporters/blender/addons/io_three/exporter/api/object.py

@@ -116,7 +116,7 @@ def children(obj, valid_types):
     """
     logger.debug('object.children(%s, %s)', obj, valid_types)
     for child in obj.children:
-        if child.type in valid_types:
+        if child.type in valid_types and child.THREE_export:
             yield child.name
 
 
@@ -455,6 +455,10 @@ def extract_mesh(obj, options, recalculate=False):
 
     """
     logger.debug('object.extract_mesh(%s, %s)', obj, options)
+    bpy.context.scene.objects.active = obj
+    hidden_state = obj.hide
+    obj.hide = False
+
     apply_modifiers = options.get(constants.APPLY_MODIFIERS, True)
     if apply_modifiers:
         bpy.ops.object.mode_set(mode='OBJECT')
@@ -470,8 +474,6 @@ def extract_mesh(obj, options, recalculate=False):
     opt_buffer = opt_buffer == constants.BUFFER_GEOMETRY
     prop_buffer = mesh_node.THREE_geometry_type == constants.BUFFER_GEOMETRY
 
-    bpy.context.scene.objects.active = obj
-
     # if doing buffer geometry it is imperative to triangulate the mesh
     if opt_buffer or prop_buffer:
         original_mesh = obj.data
@@ -480,8 +482,6 @@ def extract_mesh(obj, options, recalculate=False):
                      original_mesh.name,
                      mesh_node.name)
 
-        hidden_state = obj.hide
-        obj.hide = False
         bpy.ops.object.mode_set(mode='OBJECT')
         obj.select = True
         bpy.context.scene.objects.active = obj
@@ -491,7 +491,6 @@ def extract_mesh(obj, options, recalculate=False):
                                       modifier='Triangulate')
         obj.data = original_mesh
         obj.select = False
-        obj.hide = hidden_state
 
     # split sharp edges
     original_mesh = obj.data
@@ -504,6 +503,7 @@ def extract_mesh(obj, options, recalculate=False):
     bpy.context.object.modifiers['EdgeSplit'].use_edge_sharp = True
     bpy.ops.object.modifier_apply(apply_as='DATA', modifier='EdgeSplit')
 
+    obj.hide = hidden_state
     obj.select = False
     obj.data = original_mesh
 

+ 3 - 0
utils/exporters/blender/addons/io_three/exporter/object.py

@@ -94,7 +94,9 @@ class Object(base_classes.BaseNode):
 
             material_names = api.object.material(self.node) #manthrax: changes for multimaterial start here
             if material_names:
+
                 logger.info("Got material names for this object:%s",str(material_names));
+
                 materialArray = [self.scene.material(objname)[constants.UUID] for objname in material_names]
                 if len(materialArray) == 0:  # If no materials.. dont export a material entry
                     materialArray = None
@@ -102,6 +104,7 @@ class Object(base_classes.BaseNode):
                     materialArray = materialArray[0]
                 # else export array of material uuids
                 self[constants.MATERIAL] = materialArray
+
                 logger.info("Materials:%s",str(self[constants.MATERIAL]));
             else:
                 logger.info("%s has no materials", self.node) #manthrax: end multimaterial

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