Browse Source

Merge branch 'dev' into dev-rotate_tex

Mr.doob 8 years ago
parent
commit
4053115423
100 changed files with 7073 additions and 6259 deletions
  1. 1 1
      .github/ISSUE_TEMPLATE.md
  2. 72 64
      build/three.js
  3. 360 360
      build/three.min.js
  4. 72 64
      build/three.module.js
  5. 1 1
      docs/api/core/Face3.html
  6. 0 63
      docs/api/extras/CurveUtils.html
  7. 47 0
      docs/api/extras/core/Interpolations.html
  8. 0 1
      docs/api/extras/core/Shape.html
  9. 1 1
      docs/api/extras/curves/SplineCurve.html
  10. 1 1
      docs/api/geometries/RingBufferGeometry.html
  11. 1 1
      docs/api/geometries/RingGeometry.html
  12. 1 1
      docs/api/geometries/TextBufferGeometry.html
  13. 0 2
      docs/api/lights/PointLight.html
  14. 0 1
      docs/api/lights/RectAreaLight.html
  15. 1 1
      docs/api/lights/SpotLight.html
  16. 3 3
      docs/api/loaders/AnimationLoader.html
  17. 17 7
      docs/api/loaders/TextureLoader.html
  18. 6 1
      docs/api/materials/Material.html
  19. 1 1
      docs/api/math/Matrix4.html
  20. 2 0
      docs/examples/geometries/ConvexBufferGeometry.html
  21. 2 0
      docs/examples/geometries/ConvexGeometry.html
  22. 57 0
      docs/examples/geometries/DecalGeometry.html
  23. 0 1
      docs/examples/loaders/ColladaLoader.html
  24. 12 11
      docs/examples/loaders/GLTFLoader.html
  25. 7 12
      docs/examples/loaders/PDBLoader.html
  26. 4 3
      docs/list.js
  27. 1 1
      docs/manual/introduction/Animation-system.html
  28. 1 1
      docs/manual/introduction/Import-via-modules.html
  29. 1 1
      docs/manual/introduction/Useful-links.html
  30. 3 1
      editor/css/dark.css
  31. 3 1
      editor/css/light.css
  32. 2 48
      editor/index.html
  33. 0 7
      editor/js/Editor.js
  34. 1 1
      editor/js/Loader.js
  35. 30 15
      editor/js/Menubar.File.js
  36. 3 3
      editor/js/Sidebar.Material.js
  37. 3 62
      editor/js/Viewport.js
  38. 26 40
      editor/js/libs/app.js
  39. 4 1
      examples/canvas_geometry_panorama.html
  40. 4 1
      examples/canvas_geometry_panorama_fisheye.html
  41. 18 29
      examples/css3d_molecules.html
  42. 4 1
      examples/css3d_panorama.html
  43. 23 16
      examples/files.js
  44. 1 1
      examples/js/GPUParticleSystem.js
  45. 23 2
      examples/js/Mirror.js
  46. 92 5
      examples/js/controls/DragControls.js
  47. 0 2
      examples/js/controls/EditorControls.js
  48. 0 10
      examples/js/controls/TransformControls.js
  49. 1015 0
      examples/js/exporters/GLTFExporter.js
  50. 22 16
      examples/js/loaders/3MFLoader.js
  51. 124 178
      examples/js/loaders/AssimpJSONLoader.js
  52. 836 789
      examples/js/loaders/AssimpLoader.js
  53. 29 1
      examples/js/loaders/ColladaLoader2.js
  54. 44 18
      examples/js/loaders/FBXLoader.js
  55. 0 2952
      examples/js/loaders/GLTF2Loader.js
  56. 621 255
      examples/js/loaders/GLTFLoader.js
  57. 58 135
      examples/js/loaders/OBJLoader.js
  58. 91 156
      examples/js/loaders/OBJLoader2.js
  59. 2 2
      examples/js/loaders/PCDLoader.js
  60. 310 70
      examples/js/loaders/TDSLoader.js
  61. 1 1
      examples/js/loaders/WWOBJLoader2.js
  62. 1 1
      examples/js/renderers/CSS3DRenderer.js
  63. 0 73
      examples/js/shaders/EdgeShader2.js
  64. 1083 56
      examples/js/shaders/FXAAShader.js
  65. 1 1
      examples/js/shaders/FreiChenShader.js
  66. 90 0
      examples/js/shaders/SobelOperatorShader.js
  67. 3 2
      examples/js/vr/DaydreamController.js
  68. 2 2
      examples/js/vr/ViveController.js
  69. 518 0
      examples/misc_exporter_gltf.html
  70. 0 0
      examples/misc_exporter_obj.html
  71. 17 17
      examples/misc_sound.html
  72. 185 0
      examples/misc_sound_visualizer.html
  73. BIN
      examples/models/3ds/portalgun/portalgun.3ds
  74. 9 9
      examples/models/assimp/interior/interior.assimp.json
  75. BIN
      examples/textures/fxaa_scene.png
  76. 18 42
      examples/webgl_buffergeometry.html
  77. 18 25
      examples/webgl_buffergeometry_custom_attributes_particles.html
  78. 53 52
      examples/webgl_buffergeometry_instancing.html
  79. 66 145
      examples/webgl_buffergeometry_instancing_dynamic.html
  80. 9 12
      examples/webgl_buffergeometry_lines.html
  81. 67 54
      examples/webgl_buffergeometry_lines_indexed.html
  82. 7 11
      examples/webgl_buffergeometry_points.html
  83. 16 17
      examples/webgl_buffergeometry_rawshader.html
  84. 46 50
      examples/webgl_buffergeometry_selective_draw.html
  85. 24 50
      examples/webgl_buffergeometry_uint.html
  86. 2 3
      examples/webgl_loader_3ds.html
  87. 1 1
      examples/webgl_loader_assimp.html
  88. 35 45
      examples/webgl_loader_assimp2json.html
  89. 6 8
      examples/webgl_loader_gltf.html
  90. 4 1
      examples/webgl_materials_cubemap_dynamic2.html
  91. 3 1
      examples/webgl_panorama_dualfisheye.html
  92. 4 1
      examples/webgl_panorama_equirectangular.html
  93. 1 1
      examples/webgl_physics_terrain.html
  94. 98 0
      examples/webgl_postprocessing_fxaa.html
  95. 147 147
      examples/webgl_postprocessing_outline.html
  96. 169 0
      examples/webgl_postprocessing_sobel.html
  97. 299 0
      examples/webgl_shadowmap_pcss.html
  98. 3 6
      examples/webgl_video_panorama_equirectangular.html
  99. 1 1
      package.json
  100. 2 2
      src/Three.Legacy.js

+ 1 - 1
.github/ISSUE_TEMPLATE.md

@@ -14,7 +14,7 @@ http://jsfiddle.net/hw9rcLL8/ (dev)
 ##### Three.js version
 
 - [ ] Dev
-- [ ] r85
+- [ ] r87
 - [ ] ...
 
 ##### Browser

+ 72 - 64
build/three.js

@@ -187,7 +187,7 @@
 
 	} );
 
-	var REVISION = '87dev';
+	var REVISION = '88dev';
 	var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
 	var CullFaceNone = 0;
 	var CullFaceBack = 1;
@@ -1079,7 +1079,7 @@
 
 				var canvas;
 
-				if ( image.toDataURL !== undefined ) {
+				if ( image instanceof HTMLCanvasElement ) {
 
 					canvas = image;
 
@@ -4879,7 +4879,7 @@
 		'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
 		'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
 		'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
-		'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+		'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
 		'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
 		'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
 		'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
@@ -6869,6 +6869,7 @@
 				fogNear:			gl.getUniformLocation( program, 'fogNear' ),
 				fogFar:				gl.getUniformLocation( program, 'fogFar' ),
 				fogColor:			gl.getUniformLocation( program, 'fogColor' ),
+				fogDepth:			gl.getUniformLocation( program, 'fogDepth' ),
 
 				alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
 			};
@@ -7065,6 +7066,7 @@
 				'attribute vec2 uv;',
 
 				'varying vec2 vUV;',
+				'varying float fogDepth;',
 
 				'void main() {',
 
@@ -7076,13 +7078,14 @@
 					'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
 					'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
 
-					'vec4 finalPosition;',
+					'vec4 mvPosition;',
 
-					'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
-					'finalPosition.xy += rotatedPosition;',
-					'finalPosition = projectionMatrix * finalPosition;',
+					'mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+					'mvPosition.xy += rotatedPosition;',
 
-					'gl_Position = finalPosition;',
+					'gl_Position = projectionMatrix * mvPosition;',
+
+					'fogDepth = - mvPosition.z;',
 
 				'}'
 
@@ -7106,33 +7109,33 @@
 				'uniform float alphaTest;',
 
 				'varying vec2 vUV;',
+				'varying float fogDepth;',
 
 				'void main() {',
 
 					'vec4 texture = texture2D( map, vUV );',
 
-					'if ( texture.a < alphaTest ) discard;',
-
 					'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
 
+					'if ( gl_FragColor.a < alphaTest ) discard;',
+
 					'if ( fogType > 0 ) {',
 
-						'float depth = gl_FragCoord.z / gl_FragCoord.w;',
 						'float fogFactor = 0.0;',
 
 						'if ( fogType == 1 ) {',
 
-							'fogFactor = smoothstep( fogNear, fogFar, depth );',
+							'fogFactor = smoothstep( fogNear, fogFar, fogDepth );',
 
 						'} else {',
 
 							'const float LOG2 = 1.442695;',
-							'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+							'fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );',
 							'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
 
 						'}',
 
-						'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
+						'gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );',
 
 					'}',
 
@@ -7231,6 +7234,8 @@
 
 		this.visible = true;
 
+		this.userData = {};
+
 		this.needsUpdate = true;
 
 	}
@@ -7391,17 +7396,21 @@
 			data.depthTest = this.depthTest;
 			data.depthWrite = this.depthWrite;
 
+			if ( this.dithering === true ) data.dithering = true;
+
 			if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
 			if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+
 			if ( this.wireframe === true ) data.wireframe = this.wireframe;
 			if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
 			if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
 			if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
 
-			data.skinning = this.skinning;
-			data.morphTargets = this.morphTargets;
+			if ( this.morphTargets === true ) data.morphTargets = true;
+			if ( this.skinning === true ) data.skinning = true;
 
-			data.dithering = this.dithering;
+			if ( this.visible === false ) data.visible = false;
+			if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
 
 			// TODO: Copied from Object3D.toJSON
 
@@ -7478,12 +7487,13 @@
 			this.dithering = source.dithering;
 
 			this.alphaTest = source.alphaTest;
-
 			this.premultipliedAlpha = source.premultipliedAlpha;
 
 			this.overdraw = source.overdraw;
 
 			this.visible = source.visible;
+			this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
 			this.clipShadows = source.clipShadows;
 			this.clipIntersection = source.clipIntersection;
 
@@ -10530,7 +10540,7 @@
 			}
 
 			return this;
-			
+
 		},
 
 		getObjectById: function ( id ) {
@@ -10770,10 +10780,10 @@
 			object.type = this.type;
 
 			if ( this.name !== '' ) object.name = this.name;
-			if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 			if ( this.castShadow === true ) object.castShadow = true;
 			if ( this.receiveShadow === true ) object.receiveShadow = true;
 			if ( this.visible === false ) object.visible = false;
+			if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 
 			object.matrix = this.matrix.toArray();
 
@@ -19590,8 +19600,6 @@
 		var currentPolygonOffsetFactor = null;
 		var currentPolygonOffsetUnits = null;
 
-		var currentScissorTest = null;
-
 		var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );
 
 		var version = parseFloat( /^WebGL\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] );
@@ -20010,16 +20018,8 @@
 
 		}
 
-		function getScissorTest() {
-
-			return currentScissorTest;
-
-		}
-
 		function setScissorTest( scissorTest ) {
 
-			currentScissorTest = scissorTest;
-
 			if ( scissorTest ) {
 
 				enable( gl.SCISSOR_TEST );
@@ -20189,7 +20189,6 @@
 			setLineWidth: setLineWidth,
 			setPolygonOffset: setPolygonOffset,
 
-			getScissorTest: getScissorTest,
 			setScissorTest: setScissorTest,
 
 			activeTexture: activeTexture,
@@ -20274,7 +20273,7 @@
 
 		}
 
-		var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true && !! extensions.get( 'EXT_frag_depth' );
+		var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
 
 		var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
 		var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
@@ -20376,7 +20375,7 @@
 
 		function onVRDisplayPresentChange() {
 
-			if ( device.isPresenting ) {
+			if ( device !== null && device.isPresenting ) {
 
 				var eyeParameters = device.getEyeParameters( 'left' );
 				var renderWidth = eyeParameters.renderWidth;
@@ -21881,22 +21880,35 @@
 
 		};
 
-		// Rendering
+		// Animation Loop
 
-		this.animate = function ( callback ) {
+		var isAnimating = false;
+		var onAnimationFrame = null;
 
-			function onFrame() {
+		function start() {
 
-				callback();
+			if ( isAnimating ) return;
+			( vr.getDevice() || window ).requestAnimationFrame( loop );
+			isAnimating = true;
 
-				( vr.getDevice() || window ).requestAnimationFrame( onFrame );
+		}
 
-			}
+		function loop( time ) {
+
+			if ( onAnimationFrame !== null ) onAnimationFrame( time );
+			( vr.getDevice() || window ).requestAnimationFrame( loop );
 
-			( vr.getDevice() || window ).requestAnimationFrame( onFrame );
+		}
+
+		this.animate = function ( callback ) {
+
+			onAnimationFrame = callback;
+			start();
 
 		};
 
+		// Rendering
+
 		this.render = function ( scene, camera, renderTarget, forceClear ) {
 
 			if ( ! ( camera && camera.isCamera ) ) {
@@ -22222,8 +22234,6 @@
 							var height = bounds.w * _height;
 
 							state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-							state.scissor( _currentScissor.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-							state.setScissorTest( true );
 
 							renderObject( object, scene, camera2, geometry, material, group );
 
@@ -22676,10 +22686,6 @@
 
 					}
 
-				} else if ( material.isMeshNormalMaterial ) {
-
-					refreshUniformsCommon( m_uniforms, material );
-
 				} else if ( material.isMeshDepthMaterial ) {
 
 					refreshUniformsCommon( m_uniforms, material );
@@ -22692,6 +22698,7 @@
 
 				} else if ( material.isMeshNormalMaterial ) {
 
+					refreshUniformsCommon( m_uniforms, material );
 					refreshUniformsNormal( m_uniforms, material );
 
 				} else if ( material.isLineBasicMaterial ) {
@@ -24740,17 +24747,17 @@
 
 		function update() {
 
-			requestAnimationFrame( update );
-
 			if ( video.readyState >= video.HAVE_CURRENT_DATA ) {
 
 				scope.needsUpdate = true;
 
 			}
 
+			requestAnimationFrame( update );
+
 		}
 
-		update();
+		requestAnimationFrame( update );
 
 	}
 
@@ -32888,12 +32895,17 @@
 			if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
 			if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
 			if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
+
 			if ( json.skinning !== undefined ) material.skinning = json.skinning;
 			if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
+			if ( json.dithering !== undefined ) material.dithering = json.dithering;
+
+			if ( json.visible !== undefined ) material.visible = json.visible;
+			if ( json.userData !== undefined ) material.userData = json.userData;
 
 			// Deprecated
 
-			if ( json.shading !== undefined ) material.shading = json.shading;
+			if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading
 
 			// for PointsMaterial
 
@@ -40881,21 +40893,19 @@
 		this.matrix = object.matrixWorld;
 		this.matrixAutoUpdate = false;
 
-		this.onBeforeRender();
-
 	}
 
 	SkeletonHelper.prototype = Object.create( LineSegments.prototype );
 	SkeletonHelper.prototype.constructor = SkeletonHelper;
 
-	SkeletonHelper.prototype.onBeforeRender = function () {
+	SkeletonHelper.prototype.updateMatrixWorld = function () {
 
 		var vector = new Vector3();
 
 		var boneMatrix = new Matrix4();
 		var matrixWorldInv = new Matrix4();
 
-		return function onBeforeRender() {
+		return function updateMatrixWorld( force ) {
 
 			var bones = this.bones;
 
@@ -40926,6 +40936,8 @@
 
 			geometry.getAttribute( 'position' ).needsUpdate = true;
 
+			Object3D.prototype.updateMatrixWorld.call( this, force );
+
 		};
 
 	}();
@@ -41826,14 +41838,12 @@
 
 		this.geometry.computeBoundingSphere();
 
-		this.onBeforeRender();
-
 	}
 
 	Box3Helper.prototype = Object.create( LineSegments.prototype );
 	Box3Helper.prototype.constructor = Box3Helper;
 
-	Box3Helper.prototype.onBeforeRender = function () {
+	Box3Helper.prototype.updateMatrixWorld = function ( force ) {
 
 		var box = this.box;
 
@@ -41845,6 +41855,8 @@
 
 		this.scale.multiplyScalar( 0.5 );
 
+		Object3D.prototype.updateMatrixWorld.call( this, force );
+
 	};
 
 	/**
@@ -41879,16 +41891,12 @@
 
 		this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) );
 
-		//
-
-		this.onBeforeRender();
-
 	}
 
 	PlaneHelper.prototype = Object.create( Line.prototype );
 	PlaneHelper.prototype.constructor = PlaneHelper;
 
-	PlaneHelper.prototype.onBeforeRender = function () {
+	PlaneHelper.prototype.updateMatrixWorld = function ( force ) {
 
 		var scale = - this.plane.constant;
 
@@ -41898,7 +41906,7 @@
 
 		this.lookAt( this.plane.normal );
 
-		this.updateMatrixWorld();
+		Object3D.prototype.updateMatrixWorld.call( this, force );
 
 	};
 
@@ -43333,7 +43341,7 @@
 			set: function ( value ) {
 
 				console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
-				this.flatShading = ( value === THREE.FlatShading ) ? true : false;
+				this.flatShading = ( value === FlatShading );
 
 			}
 		}

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


+ 72 - 64
build/three.module.js

@@ -181,7 +181,7 @@ Object.assign( EventDispatcher.prototype, {
 
 } );
 
-var REVISION = '87dev';
+var REVISION = '88dev';
 var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };
 var CullFaceNone = 0;
 var CullFaceBack = 1;
@@ -1073,7 +1073,7 @@ Object.assign( Texture.prototype, EventDispatcher.prototype, {
 
 			var canvas;
 
-			if ( image.toDataURL !== undefined ) {
+			if ( image instanceof HTMLCanvasElement ) {
 
 				canvas = image;
 
@@ -4873,7 +4873,7 @@ var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0
 	'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,
 	'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,
 	'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,
-	'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
+	'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,
 	'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,
 	'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,
 	'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,
@@ -6863,6 +6863,7 @@ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {
 			fogNear:			gl.getUniformLocation( program, 'fogNear' ),
 			fogFar:				gl.getUniformLocation( program, 'fogFar' ),
 			fogColor:			gl.getUniformLocation( program, 'fogColor' ),
+			fogDepth:			gl.getUniformLocation( program, 'fogDepth' ),
 
 			alphaTest:			gl.getUniformLocation( program, 'alphaTest' )
 		};
@@ -7059,6 +7060,7 @@ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {
 			'attribute vec2 uv;',
 
 			'varying vec2 vUV;',
+			'varying float fogDepth;',
 
 			'void main() {',
 
@@ -7070,13 +7072,14 @@ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {
 				'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',
 				'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',
 
-				'vec4 finalPosition;',
+				'vec4 mvPosition;',
 
-				'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
-				'finalPosition.xy += rotatedPosition;',
-				'finalPosition = projectionMatrix * finalPosition;',
+				'mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',
+				'mvPosition.xy += rotatedPosition;',
 
-				'gl_Position = finalPosition;',
+				'gl_Position = projectionMatrix * mvPosition;',
+
+				'fogDepth = - mvPosition.z;',
 
 			'}'
 
@@ -7100,33 +7103,33 @@ function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {
 			'uniform float alphaTest;',
 
 			'varying vec2 vUV;',
+			'varying float fogDepth;',
 
 			'void main() {',
 
 				'vec4 texture = texture2D( map, vUV );',
 
-				'if ( texture.a < alphaTest ) discard;',
-
 				'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',
 
+				'if ( gl_FragColor.a < alphaTest ) discard;',
+
 				'if ( fogType > 0 ) {',
 
-					'float depth = gl_FragCoord.z / gl_FragCoord.w;',
 					'float fogFactor = 0.0;',
 
 					'if ( fogType == 1 ) {',
 
-						'fogFactor = smoothstep( fogNear, fogFar, depth );',
+						'fogFactor = smoothstep( fogNear, fogFar, fogDepth );',
 
 					'} else {',
 
 						'const float LOG2 = 1.442695;',
-						'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );',
+						'fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );',
 						'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',
 
 					'}',
 
-					'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );',
+					'gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );',
 
 				'}',
 
@@ -7225,6 +7228,8 @@ function Material() {
 
 	this.visible = true;
 
+	this.userData = {};
+
 	this.needsUpdate = true;
 
 }
@@ -7385,17 +7390,21 @@ Object.assign( Material.prototype, EventDispatcher.prototype, {
 		data.depthTest = this.depthTest;
 		data.depthWrite = this.depthWrite;
 
+		if ( this.dithering === true ) data.dithering = true;
+
 		if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
 		if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+
 		if ( this.wireframe === true ) data.wireframe = this.wireframe;
 		if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
 		if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
 		if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
 
-		data.skinning = this.skinning;
-		data.morphTargets = this.morphTargets;
+		if ( this.morphTargets === true ) data.morphTargets = true;
+		if ( this.skinning === true ) data.skinning = true;
 
-		data.dithering = this.dithering;
+		if ( this.visible === false ) data.visible = false;
+		if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
 
 		// TODO: Copied from Object3D.toJSON
 
@@ -7472,12 +7481,13 @@ Object.assign( Material.prototype, EventDispatcher.prototype, {
 		this.dithering = source.dithering;
 
 		this.alphaTest = source.alphaTest;
-
 		this.premultipliedAlpha = source.premultipliedAlpha;
 
 		this.overdraw = source.overdraw;
 
 		this.visible = source.visible;
+		this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
 		this.clipShadows = source.clipShadows;
 		this.clipIntersection = source.clipIntersection;
 
@@ -10524,7 +10534,7 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, {
 		}
 
 		return this;
-		
+
 	},
 
 	getObjectById: function ( id ) {
@@ -10764,10 +10774,10 @@ Object.assign( Object3D.prototype, EventDispatcher.prototype, {
 		object.type = this.type;
 
 		if ( this.name !== '' ) object.name = this.name;
-		if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 		if ( this.castShadow === true ) object.castShadow = true;
 		if ( this.receiveShadow === true ) object.receiveShadow = true;
 		if ( this.visible === false ) object.visible = false;
+		if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;
 
 		object.matrix = this.matrix.toArray();
 
@@ -19584,8 +19594,6 @@ function WebGLState( gl, extensions, utils ) {
 	var currentPolygonOffsetFactor = null;
 	var currentPolygonOffsetUnits = null;
 
-	var currentScissorTest = null;
-
 	var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );
 
 	var version = parseFloat( /^WebGL\ ([0-9])/.exec( gl.getParameter( gl.VERSION ) )[ 1 ] );
@@ -20004,16 +20012,8 @@ function WebGLState( gl, extensions, utils ) {
 
 	}
 
-	function getScissorTest() {
-
-		return currentScissorTest;
-
-	}
-
 	function setScissorTest( scissorTest ) {
 
-		currentScissorTest = scissorTest;
-
 		if ( scissorTest ) {
 
 			enable( gl.SCISSOR_TEST );
@@ -20183,7 +20183,6 @@ function WebGLState( gl, extensions, utils ) {
 		setLineWidth: setLineWidth,
 		setPolygonOffset: setPolygonOffset,
 
-		getScissorTest: getScissorTest,
 		setScissorTest: setScissorTest,
 
 		activeTexture: activeTexture,
@@ -20268,7 +20267,7 @@ function WebGLCapabilities( gl, extensions, parameters ) {
 
 	}
 
-	var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true && !! extensions.get( 'EXT_frag_depth' );
+	var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;
 
 	var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );
 	var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
@@ -20370,7 +20369,7 @@ function WebVRManager( renderer ) {
 
 	function onVRDisplayPresentChange() {
 
-		if ( device.isPresenting ) {
+		if ( device !== null && device.isPresenting ) {
 
 			var eyeParameters = device.getEyeParameters( 'left' );
 			var renderWidth = eyeParameters.renderWidth;
@@ -21875,22 +21874,35 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	// Rendering
+	// Animation Loop
 
-	this.animate = function ( callback ) {
+	var isAnimating = false;
+	var onAnimationFrame = null;
 
-		function onFrame() {
+	function start() {
 
-			callback();
+		if ( isAnimating ) return;
+		( vr.getDevice() || window ).requestAnimationFrame( loop );
+		isAnimating = true;
 
-			( vr.getDevice() || window ).requestAnimationFrame( onFrame );
+	}
 
-		}
+	function loop( time ) {
+
+		if ( onAnimationFrame !== null ) onAnimationFrame( time );
+		( vr.getDevice() || window ).requestAnimationFrame( loop );
 
-		( vr.getDevice() || window ).requestAnimationFrame( onFrame );
+	}
+
+	this.animate = function ( callback ) {
+
+		onAnimationFrame = callback;
+		start();
 
 	};
 
+	// Rendering
+
 	this.render = function ( scene, camera, renderTarget, forceClear ) {
 
 		if ( ! ( camera && camera.isCamera ) ) {
@@ -22216,8 +22228,6 @@ function WebGLRenderer( parameters ) {
 						var height = bounds.w * _height;
 
 						state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-						state.scissor( _currentScissor.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
-						state.setScissorTest( true );
 
 						renderObject( object, scene, camera2, geometry, material, group );
 
@@ -22670,10 +22680,6 @@ function WebGLRenderer( parameters ) {
 
 				}
 
-			} else if ( material.isMeshNormalMaterial ) {
-
-				refreshUniformsCommon( m_uniforms, material );
-
 			} else if ( material.isMeshDepthMaterial ) {
 
 				refreshUniformsCommon( m_uniforms, material );
@@ -22686,6 +22692,7 @@ function WebGLRenderer( parameters ) {
 
 			} else if ( material.isMeshNormalMaterial ) {
 
+				refreshUniformsCommon( m_uniforms, material );
 				refreshUniformsNormal( m_uniforms, material );
 
 			} else if ( material.isLineBasicMaterial ) {
@@ -24734,17 +24741,17 @@ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, forma
 
 	function update() {
 
-		requestAnimationFrame( update );
-
 		if ( video.readyState >= video.HAVE_CURRENT_DATA ) {
 
 			scope.needsUpdate = true;
 
 		}
 
+		requestAnimationFrame( update );
+
 	}
 
-	update();
+	requestAnimationFrame( update );
 
 }
 
@@ -32882,12 +32889,17 @@ Object.assign( MaterialLoader.prototype, {
 		if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
 		if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
 		if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
+
 		if ( json.skinning !== undefined ) material.skinning = json.skinning;
 		if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
+		if ( json.dithering !== undefined ) material.dithering = json.dithering;
+
+		if ( json.visible !== undefined ) material.visible = json.visible;
+		if ( json.userData !== undefined ) material.userData = json.userData;
 
 		// Deprecated
 
-		if ( json.shading !== undefined ) material.shading = json.shading;
+		if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading
 
 		// for PointsMaterial
 
@@ -40875,21 +40887,19 @@ function SkeletonHelper( object ) {
 	this.matrix = object.matrixWorld;
 	this.matrixAutoUpdate = false;
 
-	this.onBeforeRender();
-
 }
 
 SkeletonHelper.prototype = Object.create( LineSegments.prototype );
 SkeletonHelper.prototype.constructor = SkeletonHelper;
 
-SkeletonHelper.prototype.onBeforeRender = function () {
+SkeletonHelper.prototype.updateMatrixWorld = function () {
 
 	var vector = new Vector3();
 
 	var boneMatrix = new Matrix4();
 	var matrixWorldInv = new Matrix4();
 
-	return function onBeforeRender() {
+	return function updateMatrixWorld( force ) {
 
 		var bones = this.bones;
 
@@ -40920,6 +40930,8 @@ SkeletonHelper.prototype.onBeforeRender = function () {
 
 		geometry.getAttribute( 'position' ).needsUpdate = true;
 
+		Object3D.prototype.updateMatrixWorld.call( this, force );
+
 	};
 
 }();
@@ -41820,14 +41832,12 @@ function Box3Helper( box, hex ) {
 
 	this.geometry.computeBoundingSphere();
 
-	this.onBeforeRender();
-
 }
 
 Box3Helper.prototype = Object.create( LineSegments.prototype );
 Box3Helper.prototype.constructor = Box3Helper;
 
-Box3Helper.prototype.onBeforeRender = function () {
+Box3Helper.prototype.updateMatrixWorld = function ( force ) {
 
 	var box = this.box;
 
@@ -41839,6 +41849,8 @@ Box3Helper.prototype.onBeforeRender = function () {
 
 	this.scale.multiplyScalar( 0.5 );
 
+	Object3D.prototype.updateMatrixWorld.call( this, force );
+
 };
 
 /**
@@ -41873,16 +41885,12 @@ function PlaneHelper( plane, size, hex ) {
 
 	this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) );
 
-	//
-
-	this.onBeforeRender();
-
 }
 
 PlaneHelper.prototype = Object.create( Line.prototype );
 PlaneHelper.prototype.constructor = PlaneHelper;
 
-PlaneHelper.prototype.onBeforeRender = function () {
+PlaneHelper.prototype.updateMatrixWorld = function ( force ) {
 
 	var scale = - this.plane.constant;
 
@@ -41892,7 +41900,7 @@ PlaneHelper.prototype.onBeforeRender = function () {
 
 	this.lookAt( this.plane.normal );
 
-	this.updateMatrixWorld();
+	Object3D.prototype.updateMatrixWorld.call( this, force );
 
 };
 
@@ -43327,7 +43335,7 @@ Object.defineProperties( Material.prototype, {
 		set: function ( value ) {
 
 			console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
-			this.flatShading = ( value === THREE.FlatShading ) ? true : false;
+			this.flatShading = ( value === FlatShading );
 
 		}
 	}

+ 1 - 1
docs/api/core/Face3.html

@@ -21,7 +21,7 @@
 
 		<div>[example:misc_ubiquity_test ubiquity / test ]</div>
 		<div>[example:svg_sandbox svg / sandbox ]</div>
-		<div>[example:webgl_exporter_obj WebGL / exporter / obj ]</div>
+		<div>[example:misc_exporter_obj exporter / obj ]</div>
 		<div>[example:webgl_shaders_vector WebGL / shaders / vector ]</div>
 
 

+ 0 - 63
docs/api/extras/CurveUtils.html

@@ -1,63 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../" />
-		<script src="list.js"></script>
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		<h1>[name]</h1>
-
-		<div class="desc">
-		A class containing utility functions for curves.<br /><br />
-
-		Note that these are all linear functions so it is neccessary to calculate separately for
-		x, y (and z, w if present) components of a curve.
-		</div>
-
-
-		<h2>Methods</h2>
-
-		<h3>[method:Number interpolate]( p0, p1, p2, p3, t )</h3>
-		<div>
-		t -- interpolation weight. <br />
-		p0, p1, p2, p4 -- the points defining the spline curve.<br /><br />
-
-		Used internally by [page:SplineCurve SplineCurve].
-		</div>
-
-		<h3>[method:Number tangentQuadraticBezier]( t, p0, p1, p2 )</h3>
-		<div>
-		t -- the point at which to calculate the tangent. <br />
-		p0, p1, p2 -- the three points defining the quadratic Bézier curve.<br /><br />
-
-		Calculate the tangent at the point t on a quadratic Bézier curve given by the three points.<br /><br />
-
-		Used internally by [page:QuadraticBezierCurve QuadraticBezierCurve].
-		</div>
-
-		<h3>[method:Number tangentCubicBezier]( t, p0, p1, p2, p3 )</h3>
-		<div>
-		t -- the point at which to calculate the tangent. <br />
-		p0, p1, p2, p3 -- the points defining the cubic Bézier curve.<br /><br />
-
-		Calculate the tangent at the point t on a cubic Bézier curve given by the four points.<br /><br />
-
-		Used internally by [page:CubicBezierCurve CubicBezierCurve].
-		</div>
-
-		<h3>[method:Number tangentSpline]( t, p0, p1, p2, p3 )</h3>
-		<div>
-		t -- the point at which to calculate the tangent. <br />
-		p0, p1, p2, p3 -- the points defining the spline curve.<br /><br />
-
-		Calculate the tangent at the point t on a spline curve given by the four points.
-		</div>
-
-		<h2>Source</h2>
-
-		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-	</body>
-</html>

+ 47 - 0
docs/api/extras/core/Interpolations.html

@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<div class="desc">
+		TODO
+		</div>
+
+		<h2>Methods</h2>
+
+		<h3>[method:Float CatmullRom]( [page:Float t], [page:Float p0], [page:Float p1], [page:Float p2], [page:Float p3] )</h3>
+		<div>
+		t -- interpolation weight.<br />
+		p0, p1, p2, p3 -- the points defining the spline curve.<br /><br />
+
+		Used internally by [page:SplineCurve SplineCurve].
+		</div>
+
+		<h3>[method:Float QuadraticBezier]( [page:Float t], [page:Float p0], [page:Float p1], [page:Float p2] )</h3>
+		<div>
+		t -- interpolation weight.<br />
+		p0, p1, p2 -- the starting, control and end points defining the curve.<br /><br />
+
+		Used internally by [page:QuadraticBezierCurve3 QuadraticBezierCurve3], [page:QuadraticBezierCurve QuadraticBezierCurve] and [page:Font Font].
+		</div>
+
+		<h3>[method:Float CubicBezier]( [page:Float t], [page:Float p0], [page:Float p1], [page:Float p2], [page:Float p3] )</h3>
+		<div>
+		t -- interpolation weight.<br />
+		p0, p1, p2, p3 -- the starting, control(twice) and end points defining the curve.<br /><br />
+
+		Used internally by [page:CubicBezierCurve3 CubicBezierCurve3], [page:CubicBezierCurve CubicBezierCurve] and [page:Font Font].
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 0 - 1
docs/api/extras/core/Shape.html

@@ -41,7 +41,6 @@
 		[example:webgl_geometry_shapes geometry / shapes ]<br/>
 		[example:webgl_geometry_extrude_shapes geometry / extrude / shapes ]<br/>
 		[example:webgl_geometry_extrude_shapes2 geometry / extrude / shapes2 ]<br/>
-		[example:webgl_particles_shapes particles / shapes ]
 		</div>
 
 

+ 1 - 1
docs/api/extras/curves/SplineCurve.html

@@ -14,7 +14,7 @@
 
 		<div class="desc">
 		Create a smooth 2d spline curve from a series of points. Internally this uses
-		[page:CurveUtils.interpolate] to create the curve.
+		[page:Interpolations.CatmullRom] to create the curve.
 		</div>
 
 		<h2>Example</h2>

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

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

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

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

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

@@ -40,7 +40,7 @@
 
 		<div>
 		[example:webgl_geometry_text geometry / text ]<br/>
-		[example:webgl_geometry_text2 geometry / text2 ]
+		[example:webgl_geometry_text_pnltri geometry / text2 ]
 		</div>
 
 		<code>

+ 0 - 2
docs/api/lights/PointLight.html

@@ -27,9 +27,7 @@
 			[example:webgl_lights_pointlights lights / pointlights ]<br />
 			[example:webgl_lights_pointlights2 lights / pointlights2 ]<br />
 			[example:webgldeferred_animation animation ]<br />
-			[example:webgldeferred_pointlights pointlights ]<br />
 			[example:webgl_effects_anaglyph effects / anaglyph ]<br />
-			[example:webgl_geometry_large_mesh geometry / large / mesh ]<br />
 			[example:webgl_geometry_text geometry / text ]<br />
 			[example:webgl_lensflares lensflares ]
 		</div>

+ 0 - 1
docs/api/lights/RectAreaLight.html

@@ -28,7 +28,6 @@
 		<h2>Examples</h2>
 
 		<div>
-			[example:webgl_lights_arealight WebGL / arealight ]<br />
 			[example:webgl_lights_rectarealight WebGL / rectarealight ]
 
 			<code>

+ 1 - 1
docs/api/lights/SpotLight.html

@@ -36,7 +36,7 @@
 			[example:webgl_interactive_draggablecubes interactive / draggablecubes ]<br />
 			[example:webgl_materials_bumpmap_skin materials / bumpmap / skin ]<br />
 			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]<br />
-			[example:webgl_morphtargets_md2 morphtargets / md2 ]<br />
+			[example:webgl_loader_md2 loader / md2 ]<br />
 			[example:webgl_shading_physical shading / physical ]<br />
 			[example:webgl_materials_bumpmap materials / bumpmap]<br/>
 			[example:webgl_shading_physical shading / physical]<br/>

+ 3 - 3
docs/api/loaders/AnimationLoader.html

@@ -11,7 +11,7 @@
 		<h1>[name]</h1>
 
 		<div class="desc">
-			Class for loading an animation in JSON format.
+			Class for loading [page:AnimationClip AnimationClips] in JSON format.
 			This uses the [page:FileLoader] internally for loading files.
 		</div>
 
@@ -26,8 +26,8 @@
 			// resource URL
 			'animations/animation.js',
 			// Function when resource is loaded
-			function ( animation ) {
-				// do something with the animation
+			function ( animations ) {
+				// animations is an array of AnimationClips
 			},
 			// Function called when download progresses
 			function ( xhr ) {

+ 17 - 7
docs/api/loaders/TextureLoader.html

@@ -17,6 +17,17 @@
 
 		<h2>Example</h2>
 
+		<code>
+		var texture = new THREE.TextureLoader().load( 'textures/land_ocean_ice_cloud_2048.jpg' );
+
+		// immediately use the texture for material creation
+		var material = new THREE.MeshBasicMaterial( { map: texture } );
+		</code>
+
+		[example:webgl_geometry_cube geometry / cube]
+
+		<h2>Example with Callbacks</h2>
+
 		<code>
 		// instantiate a loader
 		var loader = new THREE.TextureLoader();
@@ -27,24 +38,22 @@
 			'textures/land_ocean_ice_cloud_2048.jpg',
 			// Function when resource is loaded
 			function ( texture ) {
-				// do something with the texture
+				// in this example we create the material when the texture is loaded
 				var material = new THREE.MeshBasicMaterial( {
 					map: texture
 				 } );
 			},
 			// Function called when download progresses
 			function ( xhr ) {
-				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
+				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
 			},
 			// Function called when download errors
 			function ( xhr ) {
-				console.log( 'An error happened' );
+				console.error( 'An error happened' );
 			}
 		);
 		</code>
 
-		[example:canvas_geometry_earth]
-
 		<h2>Constructor</h2>
 
 		<h3>[name]( [page:LoadingManager manager] )</h3>
@@ -81,7 +90,7 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:null load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
+		<h3>[method:Texture load]( [page:String url], [page:Function onLoad], [page:Function onProgress], [page:Function onError] )</h3>
 		<div>
 		[page:String url] — the path or URL to the file. This can also be a
 			[link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs Data URI].<br />
@@ -89,7 +98,8 @@
 		[page:Function onProgress] — Will be called while load progresses. The argument will be the XMLHttpRequest instance, which contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
 		[page:Function onError] — Will be called when load errors.<br /><br />
 
-		Begin loading from url and pass the loaded [page:Texture texture] to onLoad.
+		Begin loading from the given URL and pass the fully loaded [page:Texture texture] to onLoad. The method also returns a new texture object which can directly be used for material creation.
+		If you do it this way, the texture may pop up in your scene once the respective loading process is finished.
 		</div>
 
 		<h3>[method:null setCrossOrigin]( [page:String value] )</h3>

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

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

+ 1 - 1
docs/api/math/Matrix4.html

@@ -260,7 +260,7 @@ xAxis.z, yAxis.z, zAxis.z, 0,
 		[link:https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion here].
 		The rest of the matrix is set to the identity. So, given [page:Quaternion q] = w + xi + yj + zk, the resulting matrix will be:
 		<code>
-1-2y²-2z²    2xy-2zw    2xz-2yw    0
+1-2y²-2z²    2xy-2zw    2xz+2yw    0
 2xy+2zw      1-2x²-2z²  2yz-2xw    0
 2xz-2yw      2yz+2xw    1-2x²-2y²  0
 0            0          0          1

+ 2 - 0
docs/examples/geometries/ConvexBufferGeometry.html

@@ -34,6 +34,8 @@
 
 		<h2>Example</h2>
 
+		<div>[example:webgl_geometry_convex geometry / convex ]</div>
+
 		<code>var geometry = new THREE.ConvexBufferGeometry( points );
 		var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
 		var mesh = new THREE.Mesh( geometry, material );

+ 2 - 0
docs/examples/geometries/ConvexGeometry.html

@@ -33,6 +33,8 @@
 
 		<h2>Example</h2>
 
+		<div>[example:webgl_geometry_convex geometry / convex ]</div>
+
 		<code>var geometry = new THREE.ConvexGeometry( points );
 		var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
 		var mesh = new THREE.Mesh( geometry, material );

+ 57 - 0
docs/examples/geometries/DecalGeometry.html

@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:BufferGeometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc">[name] can be used to create a decal mesh that serves different kinds of purposes e.g. adding unique details to models, performing dynamic visual environmental changes or covering seams.</div>
+
+		<script>
+
+		// iOS iframe auto-resize workaround
+
+		if ( /(iPad|iPhone|iPod)/g.test( navigator.userAgent ) ) {
+
+			var scene = document.getElementById( 'scene' );
+
+			scene.style.width = getComputedStyle( scene ).width;
+			scene.style.height = getComputedStyle( scene ).height;
+			scene.setAttribute( 'scrolling', 'no' );
+
+		}
+
+		</script>
+
+		<h2>Example</h2>
+
+		<div>[example:webgl_decals decals ]</div>
+
+		<code>var geometry =  new THREE.DecalGeometry( mesh, position, orientation, size );
+		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+		var mesh = new THREE.Mesh( geometry, material );
+		scene.add( mesh );
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [page:Mesh mesh], [page:Vector3 position], [page:Euler orientation], [page:Vector3 size] )</h3>
+		<div>
+		mesh — Any mesh object.<br />
+		position — Position of the decal projector.<br />
+		orientation — Orientation of the decal projector.<br />
+		size — Size of the decal projector.
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/geometries/DecalGeometry.js examples/js/geometries/DecalGeometry.js]
+	</body>
+</html>

+ 0 - 1
docs/examples/loaders/ColladaLoader.html

@@ -34,7 +34,6 @@
 		</code>
 
 		[example:webgl_loader_collada]<br />
-		[example:webgl_loader_collada_keyframe]<br />
 		[example:webgl_loader_collada_skinning]<br />
 		[example:webgl_loader_collada_kinematics]
 

+ 12 - 11
docs/examples/loaders/GLTF2Loader.html → docs/examples/loaders/GLTFLoader.html

@@ -19,28 +19,29 @@
 		for efficient delivery and loading of 3D content. Assets may be provided either in JSON (.gltf)
 		or binary (.glb) format. External files store textures (.jpg, .png, ...) and additional binary
 		data (.bin). A glTF asset may deliver one or more scenes, including meshes, materials,
-		textures, shaders, skins, skeletons, morph targets, animations, lights, and/or cameras.
+		textures, skins, skeletons, morph targets, animations, lights, and/or cameras.
 		</div>
 
 		<h2>Extensions</h2>
 
 		<div>
-		GLTF2Loader supports the following glTF extensions:
+		GLTFLoader supports the following glTF extensions:
 		</div>
 
 		<ul>
 			<li>
-				KHR_lights
+				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness">
+					KHR_materials_pbrSpecularGlossiness
+				</a>
 			</li>
 			<li>
 				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common">
 					KHR_materials_common
 				</a>
+				(experimental)
 			</li>
 			<li>
-				<a target="_blank" href="https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness">
-					KHR_materials_pbrSpecularGlossiness
-				</a>
+				KHR_lights (experimental)
 			</li>
 		</ul>
 
@@ -48,7 +49,7 @@
 
 		<code>
 		// Instantiate a loader
-		var loader = new THREE.GLTF2Loader();
+		var loader = new THREE.GLTFLoader();
 
 		// Load a glTF resource
 		loader.load( 'models/gltf/duck/duck.gltf', function ( gltf ) {
@@ -61,7 +62,7 @@
 		} );
 		</code>
 
-		[example:webgl_loader_gltf2]
+		[example:webgl_loader_gltf]
 
 		<h2>Constructor</h2>
 
@@ -91,7 +92,7 @@
 
 		<h3>[method:null setPath]( [page:String path] )</h3>
 		<div>
-		[page:String path] — Base path for loading additional resources e.g. textures, GLSL shaders, .bin data.
+		[page:String path] — Base path for loading additional resources e.g. textures and .bin data.
 		</div>
 		<div>
 		Set the base path for additional resources.
@@ -106,7 +107,7 @@
 		<div>
 		[page:Object json] — <em>JSON</em> object to parse.<br />
 		[page:Function callBack] — Will be called when parse completes.<br />
-		[page:String path] — The base path from which to find subsequent glTF resources such as textures, GLSL shaders and .bin data files.<br />
+		[page:String path] — The base path from which to find subsequent glTF resources such as textures and .bin data files.<br />
 		</div>
 		<div>
 		Parse a glTF-based <em>JSON</em> structure and fire [page:Function callback] when complete. The argument to [page:Function callback] will be an [page:object] that contains loaded parts: .[page:Scene scene], .[page:Array scenes], .[page:Array cameras], and .[page:Array animations].
@@ -114,6 +115,6 @@
 
 		<h2>Source</h2>
 
-		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTF2Loader.js examples/js/loaders/GLTF2Loader.js]
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/GLTFLoader.js examples/js/loaders/GLTFLoader.js]
 	</body>
 </html>

+ 7 - 12
docs/examples/loaders/PDBLoader.html

@@ -28,7 +28,12 @@
 			// resource URL
 			'models/molecules/caffeine.pdb',
 			// Function when resource is loaded
-			function ( geometryAtoms, geometryBonds, json ) {
+			function ( pdb ) {
+
+				var geometryAtoms = pdb.geometryAtoms;
+				var geometryBonds = pdb.geometryBonds;
+				var json = pdb.json;
+
 				console.log( 'This molecule has ' + json.atoms.length + ' atoms' );
 			},
 			// Function called when download progresses
@@ -71,7 +76,7 @@
 		Begin loading from url and call onLoad with the parsed response content.
 		</div>
 
-		<h3>[method:Object parsePDB]( [page:String text] )</h3>
+		<h3>[method:Object parse]( [page:String text] )</h3>
 		<div>
 		[page:String text] — The textual <em>pdb</em> structure to parse.
 		</div>
@@ -79,16 +84,6 @@
 		Parse a <em>pdb</em> text and return a <em>JSON</em> structure.<br />
 		</div>
 
-		<h3>[method:null createModel]( [page:Object json], [page:Function callback] )</h3>
-		<div>
-		[page:Object json] — The <em>(JSON) pdb</em> structure to parse.<br />
-		[page:Function callback] — Will be called when parse completes, with three arguments: [page:BufferGeometry geometryAtoms], [page:BufferGeometry geometryBonds] and the original [page:Object json].<br />
-		</div>
-		<div>
-		Parse a <em>(JSON) pdb</em> structure and return two [page:BufferGeometry]: one for atoms, one for bonds.<br />
-		</div>
-
-
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/PDBLoader.js examples/js/loaders/PDBLoader.js]

+ 4 - 3
docs/list.js

@@ -103,7 +103,6 @@ var list = {
 		},
 
 		"Extras": {
-			"CurveUtils": "api/extras/CurveUtils",
 			"SceneUtils": "api/extras/SceneUtils",
 			"ShapeUtils": "api/extras/ShapeUtils"
 		},
@@ -112,6 +111,7 @@ var list = {
 			"Curve": "api/extras/core/Curve",
 			"CurvePath": "api/extras/core/CurvePath",
 			"Font": "api/extras/core/Font",
+			"Interpolations": "api/extras/core/Interpolations",
 			"Path": "api/extras/core/Path",
 			"Shape": "api/extras/core/Shape",
 			"ShapePath": "api/extras/core/ShapePath"
@@ -335,13 +335,14 @@ var list = {
 
 		"Geometries": {
 			"ConvexBufferGeometry": "examples/geometries/ConvexBufferGeometry",
-			"ConvexGeometry": "examples/geometries/ConvexGeometry"
+			"ConvexGeometry": "examples/geometries/ConvexGeometry",
+			"DecalGeometry": "examples/geometries/DecalGeometry"
 		},
 
 		"Loaders": {
 			"BabylonLoader": "examples/loaders/BabylonLoader",
 			"ColladaLoader": "examples/loaders/ColladaLoader",
-			"GLTF2Loader": "examples/loaders/GLTF2Loader",
+			"GLTFLoader": "examples/loaders/GLTFLoader",
 			"MTLLoader": "examples/loaders/MTLLoader",
 			"OBJLoader": "examples/loaders/OBJLoader",
 			"OBJLoader2": "examples/loaders/OBJLoader2",

+ 1 - 1
docs/manual/introduction/Animation-system.html

@@ -111,7 +111,7 @@
 				<li>[page:ObjectLoader THREE.ObjectLoader]</li>
 				<li>THREE.BVHLoader</li>
 				<li>THREE.FBXLoader</li>
-				<li>[page:GLTF2Loader THREE.GLTF2Loader]</li>
+				<li>[page:GLTFLoader THREE.GLTFLoader]</li>
 				<li>THREE.MMDLoader</li>
 				<li>THREE.SEA3DLoader</li>
 			</ul>

+ 1 - 1
docs/manual/introduction/Import-via-modules.html

@@ -65,7 +65,7 @@
 		<h2>Caveats</h2>
 
 		<div>
-			Currenlty it's not possible to import the files within the "examples/js" directroy in this way.
+			Currently it's not possible to import the files within the "examples/js" directroy in this way.
 			This is due to some of the files relying on global namespace pollution of THREE. For more information see <a href="https://github.com/mrdoob/three.js/issues/9562" target="_blank">Transform `examples/js` to support modules #9562</a>.
 		</div>
 	</body>

+ 1 - 1
docs/manual/introduction/Useful-links.html

@@ -47,7 +47,7 @@
 				[link:http://blog.cjgammon.com/ Collection of tutorials] by [link:http://www.cjgammon.com/ CJ Gammon].
 			</li>
 			<li>
-				<a href="https://medium.com/@soffritti.pierfrancesco/glossy-spheres-in-three-js-bfd2785d4857">Glossy spheres in three.js</a>.
+				[link:https://medium.com/@soffritti.pierfrancesco/glossy-spheres-in-three-js-bfd2785d4857] Glossy spheres in three.js.
 			</li>
 		 <li>
 			 [link:https://www.udacity.com/course/cs291 Interactive 3D Graphics] - a free course on Udacity that teaches the fundamentals of 3D Graphics,

+ 3 - 1
editor/css/dark.css

@@ -110,11 +110,13 @@ select {
 		}
 
 		#menubar .menu .options {
-			position: absolute;
+			position: fixed;
 			display: none;
 			padding: 5px 0;
 			background: #111;
 			width: 150px;
+			max-height: calc(100% - 80px);
+			overflow: auto;
 		}
 
 		#menubar .menu:hover .options {

+ 3 - 1
editor/css/light.css

@@ -106,11 +106,13 @@ select {
 		}
 
 		#menubar .menu .options {
-			position: absolute;
+			position: fixed;
 			display: none;
 			padding: 5px 0;
 			background: #eee;
 			width: 150px;
+			max-height: calc(100% - 80px);
+			overflow: auto;
 		}
 
 		#menubar .menu:hover .options {

+ 2 - 48
editor/index.html

@@ -23,7 +23,7 @@
 		<script src="../examples/js/loaders/BabylonLoader.js"></script>
 		<script src="../examples/js/loaders/ColladaLoader2.js"></script>
 		<script src="../examples/js/loaders/FBXLoader.js"></script>
-		<script src="../examples/js/loaders/GLTF2Loader.js"></script>
+		<script src="../examples/js/loaders/GLTFLoader.js"></script>
 		<script src="../examples/js/loaders/KMZLoader.js"></script>
 		<script src="../examples/js/loaders/MD2Loader.js"></script>
 		<script src="../examples/js/loaders/OBJLoader.js"></script>
@@ -40,6 +40,7 @@
 		<script src="../examples/js/loaders/ctm/ctm.js"></script>
 		<script src="../examples/js/loaders/ctm/CTMLoader.js"></script>
 		<script src="../examples/js/exporters/OBJExporter.js"></script>
+		<script src="../examples/js/exporters/GLTFExporter.js"></script>
 		<script src="../examples/js/exporters/STLExporter.js"></script>
 
 		<script src="../examples/js/renderers/Projector.js"></script>
@@ -84,8 +85,6 @@
 		<script src="js/Player.js"></script>
 		<script src="js/Script.js"></script>
 
-		<script src="../examples/js/effects/VREffect.js"></script>
-		<script src="../examples/js/controls/VRControls.js"></script>
 		<script src="../examples/js/vr/WebVR.js"></script>
 
 		<script src="js/Storage.js"></script>
@@ -391,51 +390,6 @@
 			}, false );
 			*/
 
-			// VR
-
-			var groupVR;
-
-			// TODO: Use editor.signals.enteredVR (WebVR 1.0)
-
-			editor.signals.enterVR.add( function () {
-
-				if ( groupVR === undefined ) {
-
-					groupVR = new THREE.HTMLGroup( viewport.dom );
-					editor.sceneHelpers.add( groupVR );
-
-					var mesh = new THREE.HTMLMesh( sidebar.dom );
-					mesh.position.set( 15, 0, 15 );
-					mesh.rotation.y = - 0.5;
-					groupVR.add( mesh );
-
-					var signals = editor.signals;
-
-					function updateTexture() {
-
-						mesh.material.map.update();
-
-					}
-
-					signals.objectSelected.add( updateTexture );
-					signals.objectAdded.add( updateTexture );
-					signals.objectChanged.add( updateTexture );
-					signals.objectRemoved.add( updateTexture );
-					signals.sceneGraphChanged.add( updateTexture );
-					signals.historyChanged.add( updateTexture );
-
-				}
-
-				groupVR.visible = true;
-
-			} );
-
-			editor.signals.exitedVR.add( function () {
-
-				if ( groupVR !== undefined ) groupVR.visible = false;
-
-			} );
-
 		</script>
 	</body>
 </html>

+ 0 - 7
editor/js/Editor.js

@@ -22,13 +22,6 @@ var Editor = function () {
 		startPlayer: new Signal(),
 		stopPlayer: new Signal(),
 
-		// vr
-
-		enterVR: new Signal(),
-
-		enteredVR: new Signal(),
-		exitedVR: new Signal(),
-
 		// actions
 
 		showModal: new Signal(),

+ 1 - 1
editor/js/Loader.js

@@ -176,7 +176,7 @@ var Loader = function ( editor ) {
 
 					var contents = event.target.result;
 
-					var loader = new THREE.GLTF2Loader();
+					var loader = new THREE.GLTFLoader();
 					loader.parse( contents, '', function ( result ) {
 
 						result.scene.name = filename;

+ 30 - 15
editor/js/Menubar.File.js

@@ -48,13 +48,19 @@ Menubar.File = function ( editor ) {
 
 	// Import
 
+	var form = document.createElement( 'form' );
+	form.style.display = 'none';
+	document.body.appendChild( form );
+
 	var fileInput = document.createElement( 'input' );
 	fileInput.type = 'file';
 	fileInput.addEventListener( 'change', function ( event ) {
 
 		editor.loader.loadFile( fileInput.files[ 0 ] );
+		form.reset();
 
 	} );
+	form.appendChild( fileInput );
 
 	var option = new UI.Row();
 	option.setClass( 'option' );
@@ -172,6 +178,29 @@ Menubar.File = function ( editor ) {
 	} );
 	options.add( option );
 
+	//
+
+	options.add( new UI.HorizontalRule() );
+
+	// Export GLTF
+
+	var option = new UI.Row();
+	option.setClass( 'option' );
+	option.setTextContent( 'Export GLTF' );
+	option.onClick( function () {
+
+		var exporter = new THREE.GLTFExporter();
+
+		exporter.parse( editor.scene, function ( result ) {
+
+			saveString( JSON.stringify( result, null, 2 ), 'scene.gltf' );
+
+		} );
+
+
+	} );
+	options.add( option );
+
 	// Export OBJ
 
 	var option = new UI.Row();
@@ -250,8 +279,6 @@ Menubar.File = function ( editor ) {
 
 			if ( vr ) {
 
-				includes.push( '<script src="js/VRControls.js"></script>' );
-				includes.push( '<script src="js/VREffect.js"></script>' );
 				includes.push( '<script src="js/WebVR.js"></script>' );
 
 			}
@@ -274,19 +301,7 @@ Menubar.File = function ( editor ) {
 
 		if ( vr ) {
 
-			loader.load( '../examples/js/controls/VRControls.js', function ( content ) {
-
-				zip.file( 'js/VRControls.js', content );
-
-			} );
-
-			loader.load( '../examples/js/effects/VREffect.js', function ( content ) {
-
-				zip.file( 'js/VREffect.js', content );
-
-			} );
-
-			loader.load( '../examples/js/WebVR.js', function ( content ) {
+			loader.load( '../examples/js/vr/WebVR.js', function ( content ) {
 
 				zip.file( 'js/WebVR.js', content );
 

+ 3 - 3
editor/js/Sidebar.Material.js

@@ -26,8 +26,8 @@ Sidebar.Material = function ( editor ) {
 
 	materialSlotRow.add( new UI.Text( 'Slot' ).setWidth( '90px' ) );
 
-	var materialSlotSelect =  new UI.Select().setWidth( '170px' ).setFontSize( '12px' ).onChange( update );
-
+	var materialSlotSelect = new UI.Select().setWidth( '170px' ).setFontSize( '12px' ).onChange( update );
+	materialSlotSelect.setOptions( { 0: '' } ).setValue( 0 );
 	materialSlotRow.add( materialSlotSelect );
 
 	container.add( materialSlotRow );
@@ -518,7 +518,7 @@ Sidebar.Material = function ( editor ) {
 
 		if ( currentMaterialSlot !== previousSelectedSlot ) refreshUI( true );
 
-		material  = editor.getObjectMaterial( currentObject, currentMaterialSlot )
+		material = editor.getObjectMaterial( currentObject, currentMaterialSlot )
 
 		var textureWarning = false;
 		var objectHasUvs = false;

+ 3 - 62
editor/js/Viewport.js

@@ -22,18 +22,6 @@ var Viewport = function ( editor ) {
 
 	var objects = [];
 
-	//
-
-	var vrEffect, vrControls;
-
-	if ( WEBVR.isAvailable() === true ) {
-
-		var vrCamera = new THREE.PerspectiveCamera();
-		vrCamera.projectionMatrix = camera.projectionMatrix;
-		camera.add( vrCamera );
-
-	}
-
 	// helpers
 
 	var grid = new THREE.GridHelper( 60, 60 );
@@ -283,12 +271,6 @@ var Viewport = function ( editor ) {
 
 	} );
 
-	signals.enterVR.add( function () {
-
-		vrEffect.isPresenting ? vrEffect.exitPresent() : vrEffect.requestPresent();
-
-	} );
-
 	signals.themeChanged.add( function ( value ) {
 
 		switch ( value ) {
@@ -345,19 +327,6 @@ var Viewport = function ( editor ) {
 
 		container.dom.appendChild( renderer.domElement );
 
-		if ( WEBVR.isAvailable() === true ) {
-
-			vrControls = new THREE.VRControls( vrCamera );
-			vrEffect = new THREE.VREffect( renderer );
-
-			window.addEventListener( 'vrdisplaypresentchange', function ( event ) {
-
-				effect.isPresenting ? signals.enteredVR.dispatch() : signals.exitedVR.dispatch();
-
-			}, false );
-
-		}
-
 		render();
 
 	} );
@@ -557,49 +526,21 @@ var Viewport = function ( editor ) {
 
 	//
 
-	function animate() {
-
-		requestAnimationFrame( animate );
-
-		if ( vrEffect && vrEffect.isPresenting ) {
-
-			render();
-
-		}
-
-	}
-
 	function render() {
 
 		sceneHelpers.updateMatrixWorld();
 		scene.updateMatrixWorld();
 
-		if ( vrEffect && vrEffect.isPresenting ) {
+		renderer.render( scene, camera );
 
-			vrControls.update();
+		if ( renderer instanceof THREE.RaytracingRenderer === false ) {
 
-			camera.updateMatrixWorld();
-
-			vrEffect.render( scene, vrCamera );
-			vrEffect.render( sceneHelpers, vrCamera );
-
-		} else {
-
-			renderer.render( scene, camera );
-
-			if ( renderer instanceof THREE.RaytracingRenderer === false ) {
-
-				renderer.render( sceneHelpers, camera );
-
-			}
+			renderer.render( sceneHelpers, camera );
 
 		}
 
-
 	}
 
-	requestAnimationFrame( animate );
-
 	return container;
 
 };

+ 26 - 40
editor/js/libs/app.js

@@ -9,19 +9,17 @@ var APP = {
 		var loader = new THREE.ObjectLoader();
 		var camera, scene, renderer;
 
-		var controls, effect, cameraVR, isVR;
-
 		var events = {};
 
-		this.dom = document.createElement( 'div' );
+		var dom = document.createElement( 'div' );
+
+		this.dom = dom;
 
 		this.width = 500;
 		this.height = 500;
 
 		this.load = function ( json ) {
 
-			isVR = json.project.vr;
-
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setClearColor( 0x000000 );
 			renderer.setPixelRatio( window.devicePixelRatio );
@@ -36,7 +34,13 @@ var APP = {
 
 			}
 
-			this.dom.appendChild( renderer.domElement );
+			if ( json.project.vr ) {
+
+				renderer.vr.enabled = true;
+
+			}
+
+			dom.appendChild( renderer.domElement );
 
 			this.setScene( loader.parse( json.scene ) );
 			this.setCamera( loader.parse( json.camera ) );
@@ -116,26 +120,20 @@ var APP = {
 			camera.aspect = this.width / this.height;
 			camera.updateProjectionMatrix();
 
-			if ( isVR === true ) {
-
-				cameraVR = new THREE.PerspectiveCamera();
-				cameraVR.projectionMatrix = camera.projectionMatrix;
-				camera.add( cameraVR );
+			if ( renderer.vr.enabled ) {
 
-				controls = new THREE.VRControls( cameraVR );
-				effect = new THREE.VREffect( renderer );
+				WEBVR.checkAvailability().catch( function( message ) {
 
-				if ( WEBVR.isAvailable() === true ) {
+					dom.appendChild( WEBVR.getMessageContainer( message ) );
 
-					this.dom.appendChild( WEBVR.getButton( effect ) );
+				} );
 
-				}
+				WEBVR.getVRDisplay( function ( device ) {
 
-				if ( WEBVR.isLatestAvailable() === false ) {
+					renderer.vr.setDevice( device );
+					dom.appendChild( WEBVR.getButton( device, renderer.domElement ) );
 
-					this.dom.appendChild( WEBVR.getMessage() );
-
-				}
+				} );
 
 			}
 
@@ -177,12 +175,10 @@ var APP = {
 
 		}
 
-		var prevTime, request;
+		var prevTime;
 
 		function animate( time ) {
 
-			request = requestAnimationFrame( animate );
-
 			try {
 
 				dispatch( events.update, { time: time, delta: time - prevTime } );
@@ -193,18 +189,7 @@ var APP = {
 
 			}
 
-			if ( isVR === true ) {
-
-				camera.updateMatrixWorld();
-
-				controls.update();
-				effect.render( scene, cameraVR );
-
-			} else {
-
-				renderer.render( scene, camera );
-
-			}
+			renderer.render( scene, camera );
 
 			prevTime = time;
 
@@ -212,6 +197,8 @@ var APP = {
 
 		this.play = function () {
 
+			prevTime = performance.now();
+
 			document.addEventListener( 'keydown', onDocumentKeyDown );
 			document.addEventListener( 'keyup', onDocumentKeyUp );
 			document.addEventListener( 'mousedown', onDocumentMouseDown );
@@ -223,8 +210,7 @@ var APP = {
 
 			dispatch( events.start, arguments );
 
-			request = requestAnimationFrame( animate );
-			prevTime = performance.now();
+			renderer.animate( animate );
 
 		};
 
@@ -241,15 +227,15 @@ var APP = {
 
 			dispatch( events.stop, arguments );
 
-			cancelAnimationFrame( request );
+			renderer.animate( null );
 
 		};
 
 		this.dispose = function () {
 
-			while ( this.dom.children.length ) {
+			while ( dom.children.length ) {
 
-				this.dom.removeChild( this.dom.firstChild );
+				dom.removeChild( dom.firstChild );
 
 			}
 

+ 4 - 1
examples/canvas_geometry_panorama.html

@@ -164,7 +164,10 @@
 
 			function onDocumentMouseWheel( event ) {
 
-				camera.fov += event.deltaY * 0.05;
+				var fov = camera.fov + event.deltaY * 0.05;
+
+				camera.fov = THREE.Math.clamp( fov, 10, 75 );
+
 				camera.updateProjectionMatrix();
 
 			}

+ 4 - 1
examples/canvas_geometry_panorama_fisheye.html

@@ -172,7 +172,10 @@
 
 			function onDocumentMouseWheel( event ) {
 
-				camera.fov += event.deltaY * 0.05;
+				var fov = camera.fov + event.deltaY * 0.05;
+
+				camera.fov = THREE.Math.clamp( fov, 10, 75 );
+
 				camera.updateProjectionMatrix();
 
 			}

+ 18 - 29
examples/css3d_molecules.html

@@ -11,14 +11,7 @@
 
 			body {
 				background-color: #050505;
-
-				background: rgb(43,45,48); /* Old browsers */
-				background: -moz-radial-gradient(center, ellipse cover,  rgba(43,45,48,1) 0%, rgba(0,0,0,1) 100%); /* FF3.6+ */
-				background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,rgba(43,45,48,1)), color-stop(100%,rgba(0,0,0,1))); /* Chrome,Safari4+ */
-				background: -webkit-radial-gradient(center, ellipse cover,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%); /* Chrome10+,Safari5.1+ */
-				background: -o-radial-gradient(center, ellipse cover,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%); /* Opera 12+ */
-				background: -ms-radial-gradient(center, ellipse cover,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%); /* IE10+ */
-				background: radial-gradient(ellipse at center,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%); /* W3C */
+				background: radial-gradient(ellipse at center,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%);
 
 				margin: 0;
 				font-family: Arial;
@@ -286,27 +279,18 @@
 
 			//
 
-			function colorify( ctx, width, height, color, a ) {
+			function colorify( ctx, width, height, color ) {
 
-				var r = color.r;
-				var g = color.g;
-				var b = color.b;
+				var r = color.r, g = color.g, b = color.b;
 
 				var imageData = ctx.getImageData( 0, 0, width, height );
 				var data = imageData.data;
 
-				for ( var y = 0; y < height; y ++ ) {
-
-					for ( var x = 0; x < width; x ++ ) {
-
-						var index = ( y * width + x ) * 4;
+				for ( var i = 0, l = data.length; i < l; i += 4 ) {
 
-						data[ index ]     *= r;
-						data[ index + 1 ] *= g;
-						data[ index + 2 ] *= b;
-						data[ index + 3 ] *= a;
-
-					}
+					data[ i + 0 ] *= r;
+					data[ i + 1 ] *= g;
+					data[ i + 2 ] *= b;
 
 				}
 
@@ -344,13 +328,17 @@
 
 				objects = [];
 
-				loader.load( url, function ( geometry, geometryBonds ) {
+				loader.load( url, function ( pdb ) {
+
+					var geometryAtoms = pdb.geometryAtoms;
+					var geometryBonds = pdb.geometryBonds;
+					var json = pdb.json;
 
-					var offset = geometry.center();
+					var offset = geometryAtoms.center();
 					geometryBonds.translate( offset.x, offset.y, offset.z );
 
-					var positions = geometry.getAttribute( 'position' );
-					var colors = geometry.getAttribute( 'color' );
+					var positions = geometryAtoms.getAttribute( 'position' );
+					var colors = geometryAtoms.getAttribute( 'color' );
 
 					var position = new THREE.Vector3();
 					var color = new THREE.Color();
@@ -365,14 +353,15 @@
 						color.g = colors.getY( i );
 						color.b = colors.getZ( i );
 
-						var element = geometry.elements[ i ];
+						var atom = json.atoms[ i ];
+						var element = atom[ 4 ];
 
 						if ( ! colorSpriteMap[ element ] ) {
 
 							var canvas = imageToCanvas( baseSprite );
 							var context = canvas.getContext( '2d' );
 
-							colorify( context, canvas.width, canvas.height, color, 1 );
+							colorify( context, canvas.width, canvas.height, color );
 
 							var dataUrl = canvas.toDataURL();
 

+ 4 - 1
examples/css3d_panorama.html

@@ -156,7 +156,10 @@
 
 			function onDocumentMouseWheel( event ) {
 
-				camera.fov += event.deltaY * 0.05;
+				var fov = camera.fov + event.deltaY * 0.05;
+
+				camera.fov = THREE.Math.clamp( fov, 10, 75 );
+
 				camera.updateProjectionMatrix();
 
 			}

+ 23 - 16
examples/files.js

@@ -18,7 +18,6 @@ var files = {
 		"webgl_effects_parallaxbarrier",
 		"webgl_effects_peppersghost",
 		"webgl_effects_stereo",
-		"webgl_exporter_obj",
 		"webgl_geometries",
 		"webgl_geometries2",
 		"webgl_geometry_colors",
@@ -91,7 +90,7 @@ var files = {
 		"webgl_loader_ctm_materials",
 		"webgl_loader_draco",
 		"webgl_loader_fbx",
-		"webgl_loader_gltf2",
+		"webgl_loader_gltf",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_json_blender",
 		"webgl_loader_json_claraio",
@@ -147,7 +146,6 @@ var files = {
 		"webgl_materials_envmaps_hdr",
 		"webgl_materials_grass",
 		"webgl_materials_lightmap",
-		"webgl_materials_modified",
 		"webgl_materials_nodes",
 		"webgl_materials_normalmap",
 		"webgl_materials_parallaxmap",
@@ -214,6 +212,7 @@ var files = {
 		"webgl_postprocessing_crossfade",
 		"webgl_postprocessing_dof",
 		"webgl_postprocessing_dof2",
+		"webgl_postprocessing_fxaa",
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_godrays",
 		"webgl_postprocessing_masking",
@@ -222,9 +221,10 @@ var files = {
 		"webgl_postprocessing_nodes",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_procedural",
+		"webgl_postprocessing_sao",
 		"webgl_postprocessing_smaa",
+		"webgl_postprocessing_sobel",
 		"webgl_postprocessing_ssao",
-		"webgl_postprocessing_sao",
 		"webgl_postprocessing_taa",
 		"webgl_postprocessing_unreal_bloom",
 		"webgl_raycast_texture",
@@ -245,7 +245,6 @@ var files = {
 		"webgl_shadowmap_pointlight",
 		"webgl_shadowmap_viewer",
 		"webgl_shadowmesh",
-		"webgl_simple_gi",
 		"webgl_skinning_simple",
 		"webgl_sprites",
 		"webgl_sprites_nodes",
@@ -278,7 +277,10 @@ var files = {
 		"webgl_custom_attributes_points",
 		"webgl_custom_attributes_points2",
 		"webgl_custom_attributes_points3",
-		"webgl_raymarching_reflect"
+		"webgl_materials_modified",
+		"webgl_raymarching_reflect",
+		"webgl_shadowmap_pcss",
+		"webgl_simple_gi"
 	],
 	"webgl deferred": [
 		"webgldeferred_animation"
@@ -288,6 +290,10 @@ var files = {
 		"webgl2_sandbox"
 	],
 	*/
+	"webaudio": [
+		"misc_sound",
+		"misc_sound_visualizer"
+	],
 	"webvr": [
 		"webvr_cubes",
 		"webvr_daydream",
@@ -301,15 +307,6 @@ var files = {
 		"webvr_vive_paint",
 		"webvr_vive_sculpt"
 	],
-	"css3d": [
-		"css3d_molecules",
-		"css3d_panorama",
-		"css3d_panorama_deviceorientation",
-		"css3d_periodictable",
-		"css3d_sandbox",
-		"css3d_sprites",
-		"css3d_youtube"
-	],
 	"misc": [
 		"misc_animation_authoring",
 		"misc_animation_keys",
@@ -319,14 +316,24 @@ var files = {
 		"misc_controls_pointerlock",
 		"misc_controls_trackball",
 		"misc_controls_transform",
+		"misc_exporter_gltf",
+		"misc_exporter_obj",
 		"misc_fps",
 		"misc_lights_test",
 		"misc_lookat",
-		"misc_sound",
 		"misc_ubiquity_test",
 		"misc_ubiquity_test2",
 		"misc_uv_tests"
 	],
+	"css3d": [
+		"css3d_molecules",
+		"css3d_panorama",
+		"css3d_panorama_deviceorientation",
+		"css3d_periodictable",
+		"css3d_sandbox",
+		"css3d_sprites",
+		"css3d_youtube"
+	],
 	"canvas": [
 		"canvas_ascii_effect",
 		"canvas_camera_orthographic",

+ 1 - 1
examples/js/GPUParticleSystem.js

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

+ 23 - 2
examples/js/Mirror.js

@@ -13,6 +13,8 @@ THREE.Mirror = function ( width, height, options ) {
 
 	options = options || {};
 
+	var viewport = new THREE.Vector4();
+
 	var textureWidth = options.textureWidth !== undefined ? options.textureWidth : 512;
 	var textureHeight = options.textureHeight !== undefined ? options.textureHeight : 512;
 
@@ -134,11 +136,12 @@ THREE.Mirror = function ( width, height, options ) {
 		target.add( mirrorWorldPosition );
 
 		mirrorCamera.position.copy( view );
-		mirrorCamera.up.set( 0, - 1, 0 );
+		mirrorCamera.up.set( 0, 1, 0 );
 		mirrorCamera.up.applyMatrix4( rotationMatrix );
-		mirrorCamera.up.reflect( normal ).negate();
+		mirrorCamera.up.reflect( normal );
 		mirrorCamera.lookAt( target );
 
+		mirrorCamera.aspect = camera.aspect;
 		mirrorCamera.near = camera.near;
 		mirrorCamera.far = camera.far;
 
@@ -201,6 +204,24 @@ THREE.Mirror = function ( width, height, options ) {
 
 		renderer.setRenderTarget( currentRenderTarget );
 
+		// Restore viewport
+
+		var bounds = camera.bounds;
+
+		if ( bounds !== undefined ) {
+
+			var size = renderer.getSize();
+			var pixelRatio = renderer.getPixelRatio();
+
+			viewport.x = bounds.x * size.width * pixelRatio;
+			viewport.y = bounds.y * size.height * pixelRatio;
+			viewport.z = bounds.z * size.width * pixelRatio;
+			viewport.w = bounds.w * size.height * pixelRatio;
+
+			renderer.state.viewport( viewport );
+
+		}
+
 		scope.visible = true;
 
 	};

+ 92 - 5
examples/js/controls/DragControls.js

@@ -30,7 +30,11 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 		_domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
 		_domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
+		_domElement.addEventListener( 'mouseup', onDocumentMouseCancel, false );
+		_domElement.addEventListener( 'mouseleave', onDocumentMouseCancel, false );
+		_domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
+		_domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
+		_domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
 
 	}
 
@@ -38,7 +42,11 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 		_domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
 		_domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.removeEventListener( 'mouseup', onDocumentMouseUp, false );
+		_domElement.removeEventListener( 'mouseup', onDocumentMouseCancel, false );
+		_domElement.removeEventListener( 'mouseleave', onDocumentMouseCancel, false );
+		_domElement.removeEventListener( 'touchmove', onDocumentTouchMove, false );
+		_domElement.removeEventListener( 'touchstart', onDocumentTouchStart, false );
+		_domElement.removeEventListener( 'touchend', onDocumentTouchEnd, false );
 
 	}
 
@@ -54,8 +62,8 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 		var rect = _domElement.getBoundingClientRect();
 
-		_mouse.x = ( (event.clientX - rect.left) / rect.width ) * 2 - 1;
-		_mouse.y = - ( (event.clientY - rect.top) / rect.height ) * 2 + 1;
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
 
 		_raycaster.setFromCamera( _mouse, _camera );
 
@@ -134,7 +142,86 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 	}
 
-	function onDocumentMouseUp( event ) {
+	function onDocumentMouseCancel( event ) {
+
+		event.preventDefault();
+
+		if ( _selected ) {
+
+			scope.dispatchEvent( { type: 'dragend', object: _selected } );
+
+			_selected = null;
+
+		}
+
+		_domElement.style.cursor = 'auto';
+
+	}
+
+	function onDocumentTouchMove( event ) {
+
+		event.preventDefault();
+		event = event.changedTouches[ 0 ];
+
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		if ( _selected && scope.enabled ) {
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_selected.position.copy( _intersection.sub( _offset ) );
+
+			}
+
+			scope.dispatchEvent( { type: 'drag', object: _selected } );
+
+			return;
+
+		}
+
+	}
+
+	function onDocumentTouchStart( event ) {
+
+		event.preventDefault();
+		event = event.changedTouches[ 0 ];
+
+		var rect = _domElement.getBoundingClientRect();
+
+		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
+		_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
+
+		_raycaster.setFromCamera( _mouse, _camera );
+
+		var intersects = _raycaster.intersectObjects( _objects );
+
+		if ( intersects.length > 0 ) {
+
+			_selected = intersects[ 0 ].object;
+
+			_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _selected.position );
+
+			if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
+
+				_offset.copy( _intersection ).sub( _selected.position );
+
+			}
+
+			_domElement.style.cursor = 'move';
+
+			scope.dispatchEvent( { type: 'dragstart', object: _selected } );
+
+		}
+
+
+	}
+
+	function onDocumentTouchEnd( event ) {
 
 		event.preventDefault();
 

+ 0 - 2
examples/js/controls/EditorControls.js

@@ -199,8 +199,6 @@ THREE.EditorControls = function ( object, domElement ) {
 
 	// touch
 
-	var touch = new THREE.Vector3();
-
 	var touches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
 	var prevTouches = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
 

+ 0 - 10
examples/js/controls/TransformControls.js

@@ -83,8 +83,6 @@
 
 	THREE.TransformGizmo = function () {
 
-		var scope = this;
-
 		this.init = function () {
 
 			THREE.Object3D.call( this );
@@ -441,13 +439,6 @@
 
 			THREE.TransformGizmo.prototype.update.apply( this, arguments );
 
-			var group = {
-
-				handles: this[ "handles" ],
-				pickers: this[ "pickers" ]
-
-			};
-
 			var tempMatrix = new THREE.Matrix4();
 			var worldRotation = new THREE.Euler( 0, 0, 1 );
 			var tempQuaternion = new THREE.Quaternion();
@@ -625,7 +616,6 @@
 
 		var _mode = "translate";
 		var _dragging = false;
-		var _plane = "XY";
 		var _gizmo = {
 
 			"translate": new THREE.TransformGizmoTranslate(),

+ 1015 - 0
examples/js/exporters/GLTFExporter.js

@@ -0,0 +1,1015 @@
+/**
+ * @author fernandojsg / http://fernandojsg.com
+ */
+
+ //------------------------------------------------------------------------------
+ // Constants
+ //------------------------------------------------------------------------------
+var WEBGL_CONSTANTS = {
+	POINTS: 0x0000,
+	LINES: 0x0001,
+	LINE_LOOP: 0x0002,
+	LINE_STRIP: 0x0003,
+	TRIANGLES: 0x0004,
+	TRIANGLE_STRIP: 0x0005,
+	TRIANGLE_FAN: 0x0006,
+
+	UNSIGNED_BYTE: 0x1401,
+	UNSIGNED_SHORT: 0x1403,
+	FLOAT: 0x1406,
+	UNSIGNED_INT: 0x1405,
+	ARRAY_BUFFER: 0x8892,
+	ELEMENT_ARRAY_BUFFER: 0x8893,
+
+	NEAREST: 0x2600,
+	LINEAR: 0x2601,
+	NEAREST_MIPMAP_NEAREST: 0x2700,
+	LINEAR_MIPMAP_NEAREST: 0x2701,
+	NEAREST_MIPMAP_LINEAR: 0x2702,
+	LINEAR_MIPMAP_LINEAR: 0x2703
+};
+
+var THREE_TO_WEBGL = {
+	// @TODO Replace with computed property name [THREE.*] when available on es6
+	1003: WEBGL_CONSTANTS.NEAREST,
+	1004: WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST,
+	1005: WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR,
+	1006: WEBGL_CONSTANTS.LINEAR,
+	1007: WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST,
+	1008: WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR
+ };
+
+//------------------------------------------------------------------------------
+// GLTF Exporter
+//------------------------------------------------------------------------------
+THREE.GLTFExporter = function () {};
+
+THREE.GLTFExporter.prototype = {
+
+	constructor: THREE.GLTFExporter,
+
+	/**
+	 * Parse scenes and generate GLTF output
+	 * @param  {THREE.Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
+	 * @param  {Function} onDone  Callback on completed
+	 * @param  {Object} options options
+	 *                          trs: Exports position, rotation and scale instead of matrix
+	 */
+	parse: function ( input, onDone, options ) {
+
+		var DEFAULT_OPTIONS = {
+			trs: false,
+			onlyVisible: true,
+			truncateDrawRange: true
+		};
+
+		options = Object.assign( {}, DEFAULT_OPTIONS, options );
+
+		var outputJSON = {
+
+			asset: {
+
+				version: "2.0",
+				generator: "THREE.JS GLTFExporter" // @QUESTION Does it support spaces?
+
+		 	}
+
+    };
+
+		var byteOffset = 0;
+		var dataViews = [];
+		var cachedData = {
+
+			images: {},
+			materials: {}
+
+		};
+
+		/**
+		 * Compare two arrays
+		 */
+		/**
+		 * Compare two arrays
+		 * @param  {Array} array1 Array 1 to compare
+		 * @param  {Array} array2 Array 2 to compare
+		 * @return {Boolean}        Returns true if both arrays are equal
+		 */
+		function equalArray ( array1, array2 ) {
+
+			return ( array1.length === array2.length ) && array1.every( function( element, index ) {
+
+    		return element === array2[ index ];
+
+			});
+
+		}
+
+		/**
+		 * Get the min and he max vectors from the given attribute
+		 * @param  {THREE.WebGLAttribute} attribute Attribute to find the min/max
+		 * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
+		 */
+		function getMinMax ( attribute ) {
+
+			var output = {
+
+				min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
+				max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
+
+			};
+
+			for ( var i = 0; i < attribute.count; i++ ) {
+
+				for ( var a = 0; a < attribute.itemSize; a++ ) {
+
+					var value = attribute.array[ i * attribute.itemSize + a ];
+					output.min[ a ] = Math.min( output.min[ a ], value );
+					output.max[ a ] = Math.max( output.max[ a ], value );
+
+				}
+
+			}
+
+			return output;
+		}
+
+		/**
+		 * Process a buffer to append to the default one.
+		 * @param  {THREE.BufferAttribute} attribute     Attribute to store
+		 * @param  {Integer} componentType Component type (Unsigned short, unsigned int or float)
+		 * @return {Integer}               Index of the buffer created (Currently always 0)
+		 */
+		function processBuffer ( attribute, componentType, start, count ) {
+
+			if ( !outputJSON.buffers ) {
+
+				outputJSON.buffers = [
+
+					{
+
+						byteLength: 0,
+						uri: ''
+
+					}
+
+				];
+
+			}
+
+			var offset = 0;
+			var componentSize = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;
+
+			// Create a new dataview and dump the attribute's array into it
+			var byteLength = count * attribute.itemSize * componentSize;
+
+			var dataView = new DataView( new ArrayBuffer( byteLength ) );
+
+			for ( var i = start; i < start + count; i++ ) {
+
+				for (var a = 0; a < attribute.itemSize; a++ ) {
+
+					var value = attribute.array[ i * attribute.itemSize + a ];
+
+					if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
+
+						dataView.setFloat32( offset, value, true );
+
+					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_INT ) {
+
+						dataView.setUint8( offset, value, true );
+
+					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) {
+
+						dataView.setUint16( offset, value, true );
+
+					}
+
+					offset += componentSize;
+
+				}
+
+			}
+
+			// We just use one buffer
+			dataViews.push( dataView );
+
+			// Always using just one buffer
+			return 0;
+		}
+
+		/**
+		 * Process and generate a BufferView
+		 * @param  {[type]} data [description]
+		 * @return {[type]}      [description]
+		 */
+		function processBufferView ( data, componentType, start, count ) {
+
+			var isVertexAttributes = componentType === WEBGL_CONSTANTS.FLOAT;
+
+			if ( !outputJSON.bufferViews ) {
+
+				outputJSON.bufferViews = [];
+
+			}
+
+			var componentSize = componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ? 2 : 4;
+
+			// Create a new dataview and dump the attribute's array into it
+			var byteLength = count * data.itemSize * componentSize;
+
+			var gltfBufferView = {
+
+				buffer: processBuffer( data, componentType, start, count ),
+				byteOffset: byteOffset,
+				byteLength: byteLength,
+				byteStride: data.itemSize * componentSize,
+				target: isVertexAttributes ? WEBGL_CONSTANTS.ARRAY_BUFFER : WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER
+
+			};
+
+			byteOffset += byteLength;
+
+			outputJSON.bufferViews.push( gltfBufferView );
+
+			// @TODO Ideally we'll have just two bufferviews: 0 is for vertex attributes, 1 for indices
+			var output = {
+
+				id: outputJSON.bufferViews.length - 1,
+				byteLength: 0
+
+			};
+
+			return output;
+
+		}
+
+		/**
+		 * Process attribute to generate an accessor
+		 * @param  {THREE.WebGLAttribute} attribute Attribute to process
+		 * @return {Integer}           Index of the processed accessor on the "accessors" array
+		 */
+		function processAccessor ( attribute, geometry ) {
+
+			if ( !outputJSON.accessors ) {
+
+				outputJSON.accessors = [];
+
+			}
+
+			var types = [
+
+				'SCALAR',
+				'VEC2',
+				'VEC3',
+				'VEC4'
+
+			];
+
+			var componentType;
+
+			// Detect the component type of the attribute array (float, uint or ushort)
+			if ( attribute.array.constructor === Float32Array ) {
+
+				componentType = WEBGL_CONSTANTS.FLOAT;
+
+			} else if ( attribute.array.constructor === Uint32Array ) {
+
+				componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
+
+			} else if ( attribute.array.constructor === Uint16Array ) {
+
+				componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
+
+			} else {
+
+				throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type.' );
+
+			}
+
+			var minMax = getMinMax( attribute );
+
+			var start = 0;
+			var count = attribute.count;
+
+			// @TODO Indexed buffer geometry with drawRange not supported yet
+			if ( options.truncateDrawRange && geometry.index === null ) {
+				start = geometry.drawRange.start;
+				count = geometry.drawRange.count !== Infinity ? geometry.drawRange.count : attribute.count;
+			}
+
+			var bufferView = processBufferView( attribute, componentType, start, count );
+
+			var gltfAccessor = {
+
+				bufferView: bufferView.id,
+				byteOffset: bufferView.byteOffset,
+				componentType: componentType,
+				count: count,
+				max: minMax.max,
+				min: minMax.min,
+				type: types[ attribute.itemSize - 1 ]
+
+			};
+
+			outputJSON.accessors.push( gltfAccessor );
+
+			return outputJSON.accessors.length - 1;
+
+		}
+
+		/**
+		 * Process image
+		 * @param  {Texture} map Texture to process
+		 * @return {Integer}     Index of the processed texture in the "images" array
+		 */
+		function processImage ( map ) {
+
+			if ( cachedData.images[ map.uuid ] ) {
+
+				return cachedData.images[ map.uuid ];
+
+			}
+
+			if ( !outputJSON.images ) {
+
+				outputJSON.images = [];
+
+			}
+
+			var gltfImage = {};
+
+			if ( options.embedImages ) {
+
+				// @TODO { bufferView, mimeType }
+
+			} else {
+
+				// @TODO base64 based on options
+				gltfImage.uri = map.image.src;
+
+			}
+
+			outputJSON.images.push( gltfImage );
+
+			var index = outputJSON.images.length - 1;
+			cachedData.images[ map.uuid ] = index;
+
+			return index;
+
+		}
+
+		/**
+		 * Process sampler
+		 * @param  {Texture} map Texture to process
+		 * @return {Integer}     Index of the processed texture in the "samplers" array
+		 */
+		function processSampler ( map ) {
+
+			if ( !outputJSON.samplers ) {
+
+				outputJSON.samplers = [];
+
+			}
+
+			var gltfSampler = {
+
+				magFilter: THREE_TO_WEBGL[ map.magFilter ],
+				minFilter: THREE_TO_WEBGL[ map.minFilter ],
+				wrapS: THREE_TO_WEBGL[ map.wrapS ],
+				wrapT: THREE_TO_WEBGL[ map.wrapT ]
+
+			};
+
+			outputJSON.samplers.push( gltfSampler );
+
+			return outputJSON.samplers.length - 1;
+
+		}
+
+		/**
+		 * Process texture
+		 * @param  {Texture} map Map to process
+		 * @return {Integer}     Index of the processed texture in the "textures" array
+		 */
+		function processTexture ( map ) {
+
+			if (!outputJSON.textures) {
+
+				outputJSON.textures = [];
+
+			}
+
+			var gltfTexture = {
+
+				sampler: processSampler( map ),
+				source: processImage( map )
+
+			};
+
+			outputJSON.textures.push( gltfTexture );
+
+			return outputJSON.textures.length - 1;
+
+		}
+
+		/**
+		 * Process material
+		 * @param  {THREE.Material} material Material to process
+		 * @return {Integer}      Index of the processed material in the "materials" array
+		 */
+		function processMaterial ( material ) {
+
+			if ( cachedData.materials[ material.uuid ] ) {
+
+				return cachedData.materials[ material.uuid ];
+
+			}
+
+			if ( !outputJSON.materials ) {
+
+				outputJSON.materials = [];
+
+			}
+
+			if ( material instanceof THREE.ShaderMaterial ) {
+
+				console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
+				return null;
+
+			}
+
+
+			if ( !( material instanceof THREE.MeshStandardMaterial ) ) {
+
+				console.warn( 'GLTFExporter: Currently just THREE.StandardMaterial is supported. Material conversion may lose information.' );
+
+			}
+
+			// @QUESTION Should we avoid including any attribute that has the default value?
+			var gltfMaterial = {
+
+				pbrMetallicRoughness: {}
+
+			};
+
+			// pbrMetallicRoughness.baseColorFactor
+			var color = material.color.toArray().concat( [ material.opacity ] );
+
+			if ( !equalArray( color, [ 1, 1, 1, 1 ] ) ) {
+
+				gltfMaterial.pbrMetallicRoughness.baseColorFactor = color;
+
+			}
+
+			if ( material instanceof THREE.MeshStandardMaterial ) {
+
+				gltfMaterial.pbrMetallicRoughness.metallicFactor = material.metalness;
+				gltfMaterial.pbrMetallicRoughness.roughnessFactor = material.roughness;
+
+ 			} else {
+
+					gltfMaterial.pbrMetallicRoughness.metallicFactor = 0.5;
+					gltfMaterial.pbrMetallicRoughness.roughnessFactor = 0.5;
+
+			}
+
+			// pbrMetallicRoughness.baseColorTexture
+			if ( material.map ) {
+
+				gltfMaterial.pbrMetallicRoughness.baseColorTexture = {
+
+					index: processTexture( material.map )
+
+				};
+
+			}
+
+			if ( material instanceof THREE.MeshBasicMaterial ||
+				material instanceof THREE.LineBasicMaterial ||
+				material instanceof THREE.PointsMaterial ) {
+
+			} else {
+
+				// emissiveFactor
+				var emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity ).toArray();
+
+				if ( !equalArray( emissive, [ 0, 0, 0 ] ) ) {
+
+					gltfMaterial.emissiveFactor = emissive;
+
+				}
+
+				// emissiveTexture
+				if ( material.emissiveMap ) {
+
+					gltfMaterial.emissiveTexture = {
+
+						index: processTexture( material.emissiveMap )
+
+					};
+
+				}
+
+			}
+
+			// normalTexture
+			if ( material.normalMap ) {
+
+				gltfMaterial.normalTexture = {
+
+					index: processTexture( material.normalMap )
+
+				};
+
+				if ( material.normalScale.x !== -1 ) {
+
+					if ( material.normalScale.x !== material.normalScale.y ) {
+
+						console.warn('GLTFExporter: Normal scale components are different, ignoring Y and exporting X');
+
+					}
+
+					gltfMaterial.normalTexture.scale = material.normalScale.x;
+
+				}
+
+			}
+
+			// occlusionTexture
+			if ( material.aoMap ) {
+
+				gltfMaterial.occlusionTexture = {
+
+					index: processTexture( material.aoMap )
+
+				};
+
+				if ( material.aoMapIntensity !== 1.0 ) {
+
+					gltfMaterial.occlusionTexture.strength = material.aoMapIntensity;
+
+				}
+
+			}
+
+			// alphaMode
+			if ( material.transparent ) {
+
+				gltfMaterial.alphaMode = 'MASK'; // @FIXME We should detect MASK or BLEND
+
+				if ( material.alphaTest !== 0.5 ) {
+
+					gltfMaterial.alphaCutoff = material.alphaTest;
+
+				}
+
+			}
+
+			// doubleSided
+			if ( material.side === THREE.DoubleSide ) {
+
+				gltfMaterial.doubleSided = true;
+
+			}
+
+			if ( material.name ) {
+
+				gltfMaterial.name = material.name;
+
+			}
+
+			outputJSON.materials.push( gltfMaterial );
+
+			var index = outputJSON.materials.length - 1;
+			cachedData.materials[ material.uuid ] = index;
+
+			return index;
+
+		}
+
+		/**
+		 * Process mesh
+		 * @param  {THREE.Mesh} mesh Mesh to process
+		 * @return {Integer}      Index of the processed mesh in the "meshes" array
+		 */
+		function processMesh( mesh ) {
+
+			if ( !outputJSON.meshes ) {
+
+				outputJSON.meshes = [];
+
+			}
+
+			var geometry = mesh.geometry;
+
+			// Use the correct mode
+			if ( mesh instanceof THREE.LineSegments ) {
+
+				mode = WEBGL_CONSTANTS.LINES;
+
+			} else if ( mesh instanceof THREE.LineLoop ) {
+
+				mode = WEBGL_CONSTANTS.LINE_LOOP;
+
+			} else if ( mesh instanceof THREE.Line ) {
+
+				mode = WEBGL_CONSTANTS.LINE_STRIP;
+
+			} else if ( mesh instanceof THREE.Points ) {
+
+				mode = WEBGL_CONSTANTS.POINTS;
+
+			} else {
+
+				if ( !geometry.isBufferGeometry ) {
+
+					var geometryTemp = new THREE.BufferGeometry();
+					geometryTemp.fromGeometry( geometry );
+					geometry = geometryTemp;
+
+				}
+
+				if ( mesh.drawMode === THREE.TriangleFanDrawMode ) {
+
+					console.warn( 'GLTFExporter: TriangleFanDrawMode and wireframe incompatible.' );
+					mode = WEBGL_CONSTANTS.TRIANGLE_FAN;
+
+				} else if ( mesh.drawMode === THREE.TriangleStripDrawMode ) {
+
+					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINE_STRIP : WEBGL_CONSTANTS.TRIANGLE_STRIP;
+
+				} else {
+
+					mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
+
+				}
+
+			}
+
+			var gltfMesh = {
+				primitives: [
+					{
+						mode: mode,
+						attributes: {},
+					}
+				]
+			};
+
+			var material = processMaterial( mesh.material );
+			if ( material !== null ) {
+
+				gltfMesh.primitives[ 0 ].material = material;
+
+			}
+
+
+			if ( geometry.index ) {
+
+				gltfMesh.primitives[ 0 ].indices = processAccessor( geometry.index, geometry );
+
+			}
+
+			// We've just one primitive per mesh
+			var gltfAttributes = gltfMesh.primitives[ 0 ].attributes;
+			var attributes = geometry.attributes;
+
+			// Conversion between attributes names in threejs and gltf spec
+			var nameConversion = {
+
+				uv: 'TEXCOORD_0',
+				uv2: 'TEXCOORD_1',
+				color: 'COLOR_0',
+				skinWeight: 'WEIGHTS_0',
+				skinIndex: 'JOINTS_0'
+
+			};
+
+			// @QUESTION Detect if .vertexColors = THREE.VertexColors?
+			// For every attribute create an accessor
+			for ( var attributeName in geometry.attributes ) {
+
+				var attribute = geometry.attributes[ attributeName ];
+				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
+				gltfAttributes[ attributeName ] = processAccessor( attribute, geometry );
+
+			}
+
+			outputJSON.meshes.push( gltfMesh );
+
+			return outputJSON.meshes.length - 1;
+		}
+
+		/**
+		 * Process camera
+		 * @param  {THREE.Camera} camera Camera to process
+		 * @return {Integer}      Index of the processed mesh in the "camera" array
+		 */
+		function processCamera( camera ) {
+
+			if ( !outputJSON.cameras ) {
+
+				outputJSON.cameras = [];
+
+			}
+
+			var isOrtho = camera instanceof THREE.OrthographicCamera;
+
+			var gltfCamera = {
+
+				type: isOrtho ? 'orthographic' : 'perspective'
+
+			};
+
+			if ( isOrtho ) {
+
+				gltfCamera.orthographic = {
+
+					xmag: camera.right * 2,
+					ymag: camera.top * 2,
+					zfar: camera.far,
+					znear: camera.near
+
+				};
+
+			} else {
+
+				gltfCamera.perspective = {
+
+					aspectRatio: camera.aspect,
+					yfov: THREE.Math.degToRad( camera.fov ) / camera.aspect,
+					zfar: camera.far,
+					znear: camera.near
+
+				};
+
+			}
+
+			if ( camera.name ) {
+
+				gltfCamera.name = camera.type;
+
+			}
+
+			outputJSON.cameras.push( gltfCamera );
+
+			return outputJSON.cameras.length - 1;
+		}
+
+		/**
+		 * Process Object3D node
+		 * @param  {THREE.Object3D} node Object3D to processNode
+		 * @return {Integer}      Index of the node in the nodes list
+		 */
+		function processNode ( object ) {
+
+			if ( object instanceof THREE.Light ) {
+
+				console.warn( 'GLTFExporter: Unsupported node type:', object.constructor.name );
+				return null;
+
+			}
+
+			if ( !outputJSON.nodes ) {
+
+				outputJSON.nodes = [];
+
+			}
+
+			var gltfNode = {};
+
+			if ( options.trs ) {
+
+				var rotation = object.quaternion.toArray();
+				var position = object.position.toArray();
+				var scale = object.scale.toArray();
+
+				if ( !equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
+
+					gltfNode.rotation = rotation;
+
+				}
+
+				if ( !equalArray( position, [ 0, 0, 0 ] ) ) {
+
+					gltfNode.position = position;
+
+				}
+
+				if ( !equalArray( scale, [ 1, 1, 1 ] ) ) {
+
+					gltfNode.scale = scale;
+
+				}
+
+			} else {
+
+				object.updateMatrix();
+				if (! equalArray( object.matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] ) ) {
+
+					gltfNode.matrix = object.matrix.elements;
+
+				}
+
+			}
+
+			if ( object.name ) {
+
+				gltfNode.name = object.name;
+
+			}
+
+			if ( object.userData && Object.keys( object.userData ).length > 0 ) {
+
+				try {
+
+					gltfNode.extras = JSON.parse( JSON.stringify( object.userData ) );
+
+				} catch (e) {
+
+					throw new Error( 'GLTFExporter: userData can\'t be serialized' );
+
+				}
+
+			}
+
+			if ( object instanceof THREE.Mesh ||
+				object instanceof THREE.Line ||
+				object instanceof THREE.Points ) {
+
+				gltfNode.mesh = processMesh( object );
+
+			} else if ( object instanceof THREE.Camera ) {
+
+				gltfNode.camera = processCamera( object );
+
+			}
+
+			if ( object.children.length > 0 ) {
+
+				var children = [];
+
+				for ( var i = 0, l = object.children.length; i < l; i ++ ) {
+
+					var child = object.children[ i ];
+
+					if ( child.visible || options.onlyVisible === false ) {
+
+						var node = processNode( child );
+
+						if ( node !== null ) {
+
+							children.push( node );
+
+						}
+
+					}
+
+				}
+
+				if ( children.length > 0 ) {
+
+					gltfNode.children = children;
+
+				}
+
+
+			}
+
+			outputJSON.nodes.push( gltfNode );
+
+			return outputJSON.nodes.length - 1;
+
+		}
+
+		/**
+		 * Process Scene
+		 * @param  {THREE.Scene} node Scene to process
+		 */
+		function processScene( scene ) {
+
+			if ( !outputJSON.scenes ) {
+
+				outputJSON.scenes = [];
+				outputJSON.scene = 0;
+
+			}
+
+			var gltfScene = {
+
+				nodes: []
+
+			};
+
+			if ( scene.name ) {
+
+				gltfScene.name = scene.name;
+
+			}
+
+			outputJSON.scenes.push( gltfScene );
+
+			var nodes = [];
+
+			for ( var i = 0, l = scene.children.length; i < l; i ++ ) {
+
+				var child = scene.children[ i ];
+
+				if ( child.visible || options.onlyVisible === false ) {
+
+					var node = processNode( child );
+
+					if ( node !== null ) {
+
+						nodes.push( node );
+
+					}
+
+				}
+
+			}
+
+			if ( nodes.length > 0 ) {
+
+				gltfScene.nodes = nodes;
+
+			}
+
+		}
+
+		/**
+		 * Creates a THREE.Scene to hold a list of objects and parse it
+		 * @param  {Array} objects List of objects to process
+		 */
+		function processObjects ( objects ) {
+
+			var scene = new THREE.Scene();
+			scene.name = 'AuxScene';
+
+			for ( var i = 0; i < objects.length; i++ ) {
+
+				// We push directly to children instead of calling `add` to prevent
+				// modify the .parent and break its original scene and hierarchy
+				scene.children.push( objects[ i ] );
+
+			}
+
+			processScene( scene );
+
+		}
+
+		function processInput( input ) {
+
+			input = input instanceof Array ? input : [ input ];
+
+			var objectsWithoutScene = [];
+			for ( i = 0; i < input.length; i++ ) {
+
+				if ( input[ i ] instanceof THREE.Scene ) {
+
+					processScene( input[ i ] );
+
+				} else {
+
+					objectsWithoutScene.push( input[ i ] );
+
+				}
+
+			}
+
+			if ( objectsWithoutScene.length > 0 ) {
+
+				processObjects( objectsWithoutScene );
+
+			}
+
+		}
+
+		processInput( input );
+
+		// Generate buffer
+		// Create a new blob with all the dataviews from the buffers
+		var blob = new Blob( dataViews, { type: 'application/octet-stream' } );
+
+		// Update the bytlength of the only main buffer and update the uri with the base64 representation of it
+		if ( outputJSON.buffers && outputJSON.buffers.length > 0 ) {
+
+			outputJSON.buffers[ 0 ].byteLength = blob.size;
+			objectURL = URL.createObjectURL( blob );
+
+			var reader = new window.FileReader();
+			 reader.readAsDataURL( blob );
+			 reader.onloadend = function() {
+
+				 base64data = reader.result;
+				 outputJSON.buffers[ 0 ].uri = base64data;
+				 onDone( outputJSON );
+
+			 };
+
+		} else {
+
+			onDone ( outputJSON );
+
+		}
+	}
+};

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

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

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

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

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


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

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

+ 44 - 18
examples/js/loaders/FBXLoader.js

@@ -185,12 +185,12 @@
 	/**
 	 * Parses map of images referenced in FBXTree.
 	 * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
-	 * @returns {Map<number, string(image blob URL)>}
+	 * @returns {Map<number, string(image blob/data URL)>}
 	 */
 	function parseImages( FBXTree ) {
 
 		/**
-		 * @type {Map<number, string(image blob URL)>}
+		 * @type {Map<number, string(image blob/data URL)>}
 		 */
 		var imageMap = new Map();
 
@@ -220,12 +220,11 @@
 
 	/**
 	 * @param {videoNode} videoNode - Node to get texture image information from.
-	 * @returns {string} - image blob URL
+	 * @returns {string} - image blob/data URL
 	 */
 	function parseImage( videoNode ) {
 
-		var buffer = videoNode.properties.Content;
-		var array = new Uint8Array( buffer );
+		var content = videoNode.properties.Content;
 		var fileName = videoNode.properties.RelativeFilename || videoNode.properties.Filename;
 		var extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase();
 
@@ -260,7 +259,16 @@
 
 		}
 
-		return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) );
+		if ( typeof content === 'string' ) {
+
+			return 'data:' + type + ';base64,' + content;
+
+		} else {
+
+			var array = new Uint8Array( content );
+			return window.URL.createObjectURL( new Blob( [ array ], { type: type } ) );
+
+		}
 
 	}
 
@@ -268,7 +276,7 @@
 	 * Parses map of textures referenced in FBXTree.
 	 * @param {{Objects: {subNodes: {Texture: Object.<string, FBXTextureNode>}}}} FBXTree
 	 * @param {THREE.TextureLoader} loader
-	 * @param {Map<number, string(image blob URL)>} imageMap
+	 * @param {Map<number, string(image blob/data URL)>} imageMap
 	 * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
 	 * @returns {Map<number, THREE.Texture>}
 	 */
@@ -298,7 +306,7 @@
 	/**
 	 * @param {textureNode} textureNode - Node to get texture information from.
 	 * @param {THREE.TextureLoader} loader
-	 * @param {Map<number, string(image blob URL)>} imageMap
+	 * @param {Map<number, string(image blob/data URL)>} imageMap
 	 * @param {Map<number, {parents: {ID: number, relationship: string}[], children: {ID: number, relationship: string}[]}>} connections
 	 * @returns {THREE.Texture}
 	 */
@@ -345,7 +353,7 @@
 
 		var currentPath = loader.path;
 
-		if ( fileName.indexOf( 'blob:' ) === 0 ) {
+		if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) {
 
 			loader.setPath( undefined );
 
@@ -393,7 +401,7 @@
 			for ( var nodeID in materialNodes ) {
 
 				var material = parseMaterial( materialNodes[ nodeID ], textureMap, connections );
-				materialMap.set( parseInt( nodeID ), material );
+				if ( material !== null ) materialMap.set( parseInt( nodeID ), material );
 
 			}
 
@@ -423,6 +431,10 @@
 
 		}
 
+		// Seems like FBX can include unused materials which don't have any connections.
+		// Ignores them so far.
+		if ( ! connections.has( FBX_ID ) ) return null;
+
 		var children = connections.get( FBX_ID ).children;
 
 		var parameters = parseParameters( materialNode.properties, textureMap, children );
@@ -438,8 +450,8 @@
 				material = new THREE.MeshLambertMaterial();
 				break;
 			default:
-				console.warn( 'THREE.FBXLoader: No implementation given for material type %s in FBXLoader.js. Defaulting to basic material.', type );
-				material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
+				console.warn( 'THREE.FBXLoader: No implementation given for material type %s in FBXLoader.js. Defaulting to standard material.', type );
+				material = new THREE.MeshStandardMaterial( { color: 0x3300ff } );
 				break;
 
 		}
@@ -1408,7 +1420,7 @@
 
 						} else {
 
-							material = new THREE.MeshBasicMaterial( { color: 0x3300ff } );
+							material = new THREE.MeshStandardMaterial( { color: 0x3300ff } );
 							materials.push( material );
 
 						}
@@ -3619,25 +3631,28 @@
 
 			var split = text.split( '\n' );
 
-			for ( var line in split ) {
+			for ( var lineNum = 0, lineLength = split.length; lineNum < lineLength; lineNum ++ ) {
 
-				var l = split[ line ];
+				var l = split[ lineNum ];
 
-				// short cut
+				// skip comment line
 				if ( l.match( /^[\s\t]*;/ ) ) {
 
 					continue;
 
-				} // skip comment line
+				}
+
+				// skip empty line
 				if ( l.match( /^[\s\t]*$/ ) ) {
 
 					continue;
 
-				} // skip empty line
+				}
 
 				// beginning of node
 				var beginningOfNodeExp = new RegExp( '^\\t{' + this.currentIndent + '}(\\w+):(.*){', '' );
 				var match = l.match( beginningOfNodeExp );
+
 				if ( match ) {
 
 					var nodeName = match[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' );
@@ -3655,11 +3670,21 @@
 				// node's property
 				var propExp = new RegExp( '^\\t{' + ( this.currentIndent ) + '}(\\w+):[\\s\\t\\r\\n](.*)' );
 				var match = l.match( propExp );
+
 				if ( match ) {
 
 					var propName = match[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
 					var propValue = match[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
 
+					// for special case: base64 image data follows "Content: ," line
+					//	Content: ,
+					//	 "iVB..."
+					if ( propName === 'Content' && propValue === ',' ) {
+
+						propValue = split[ ++ lineNum ].replace( /"/g, '' ).trim();
+
+					}
+
 					this.parseNodeProperty( l, propName, propValue );
 					continue;
 
@@ -3667,6 +3692,7 @@
 
 				// end of node
 				var endOfNodeExp = new RegExp( '^\\t{' + ( this.currentIndent - 1 ) + '}}' );
+
 				if ( l.match( endOfNodeExp ) ) {
 
 					this.nodeEnd();

+ 0 - 2952
examples/js/loaders/GLTF2Loader.js

@@ -1,2952 +0,0 @@
-/**
- * @author Rich Tibbett / https://github.com/richtr
- * @author mrdoob / http://mrdoob.com/
- * @author Tony Parisi / http://www.tonyparisi.com/
- * @author Takahiro / https://github.com/takahirox
- * @author Don McCurdy / https://www.donmccurdy.com
- */
-
-THREE.GLTF2Loader = ( function () {
-
-	function GLTF2Loader( manager ) {
-
-		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-
-	}
-
-	GLTF2Loader.prototype = {
-
-		constructor: GLTF2Loader,
-
-		crossOrigin: 'Anonymous',
-
-		load: function ( url, onLoad, onProgress, onError ) {
-
-			var scope = this;
-
-			var path = this.path && ( typeof this.path === 'string' ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
-
-			var loader = new THREE.FileLoader( scope.manager );
-
-			loader.setResponseType( 'arraybuffer' );
-
-			loader.load( url, function ( data ) {
-
-				try {
-
-					scope.parse( data, path, onLoad, onError );
-
-				} catch ( e ) {
-
-					// For SyntaxError or TypeError, return a generic failure message.
-					onError( e.constructor === Error ? e : new Error( 'THREE.GLTF2Loader: Unable to parse model.' ) );
-
-				}
-
-			}, onProgress, onError );
-
-		},
-
-		setCrossOrigin: function ( value ) {
-
-			this.crossOrigin = value;
-
-		},
-
-		setPath: function ( value ) {
-
-			this.path = value;
-
-		},
-
-		parse: function ( data, path, onLoad, onError ) {
-
-			var content;
-			var extensions = {};
-
-			var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) );
-
-			if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
-
-				extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
-				content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
-
-			} else {
-
-				content = convertUint8ArrayToString( new Uint8Array( data ) );
-
-			}
-
-			var json = JSON.parse( content );
-
-			if ( json.asset.version[0] < 2 ) {
-
-				onError( new Error( 'THREE.GLTF2Loader: Legacy glTF detected. Use THREE.GLTFLoader instead.' ) );
-				return;
-
-			}
-
-			if ( json.extensionsUsed ) {
-
-				if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) {
-
-					extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json );
-
-				}
-
-				if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_COMMON ) >= 0 ) {
-
-					extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] = new GLTFMaterialsCommonExtension( json );
-
-				}
-
-				if( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ) >= 0 ) {
-
-					extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
-
-				}
-
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_TECHNIQUE_WEBGL ) >= 0 ) {
-
-					extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] = new GLTFTechniqueWebglExtension( json );
-
-				}
-
-			}
-
-			console.time( 'GLTF2Loader' );
-
-			var parser = new GLTFParser( json, extensions, {
-
-				path: path || this.path,
-				crossOrigin: this.crossOrigin
-
-			} );
-
-			parser.parse( function ( scene, scenes, cameras, animations ) {
-
-				console.timeEnd( 'GLTF2Loader' );
-
-				var glTF = {
-					scene: scene,
-					scenes: scenes,
-					cameras: cameras,
-					animations: animations
-				};
-
-				onLoad( glTF );
-
-			}, onError );
-
-		}
-
-	};
-
-	/* GLTFREGISTRY */
-
-	function GLTFRegistry() {
-
-		var objects = {};
-
-		return	{
-
-			get: function ( key ) {
-
-				return objects[ key ];
-
-			},
-
-			add: function ( key, object ) {
-
-				objects[ key ] = object;
-
-			},
-
-			remove: function ( key ) {
-
-				delete objects[ key ];
-
-			},
-
-			removeAll: function () {
-
-				objects = {};
-
-			},
-
-			update: function ( scene, camera ) {
-
-				for ( var name in objects ) {
-
-					var object = objects[ name ];
-
-					if ( object.update ) {
-
-						object.update( scene, camera );
-
-					}
-
-				}
-
-			}
-
-		};
-
-	}
-
-	/* GLTFSHADER */
-
-	function GLTFShader( targetNode, allNodes ) {
-
-		var boundUniforms = {};
-
-		// bind each uniform to its source node
-
-		var uniforms = targetNode.material.uniforms;
-
-		for ( var uniformId in uniforms ) {
-
-			var uniform = uniforms[ uniformId ];
-
-			if ( uniform.semantic ) {
-
-				var sourceNodeRef = uniform.node;
-
-				var sourceNode = targetNode;
-
-				if ( sourceNodeRef ) {
-
-					sourceNode = allNodes[ sourceNodeRef ];
-
-				}
-
-				boundUniforms[ uniformId ] = {
-					semantic: uniform.semantic,
-					sourceNode: sourceNode,
-					targetNode: targetNode,
-					uniform: uniform
-				};
-
-			}
-
-		}
-
-		this.boundUniforms = boundUniforms;
-		this._m4 = new THREE.Matrix4();
-
-	}
-
-	// Update - update all the uniform values
-	GLTFShader.prototype.update = function ( scene, camera ) {
-
-		var boundUniforms = this.boundUniforms;
-
-		for ( var name in boundUniforms ) {
-
-			var boundUniform = boundUniforms[ name ];
-
-			switch ( boundUniform.semantic ) {
-
-				case 'MODELVIEW':
-
-					var m4 = boundUniform.uniform.value;
-					m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
-					break;
-
-				case 'MODELVIEWINVERSETRANSPOSE':
-
-					var m3 = boundUniform.uniform.value;
-					this._m4.multiplyMatrices( camera.matrixWorldInverse, boundUniform.sourceNode.matrixWorld );
-					m3.getNormalMatrix( this._m4 );
-					break;
-
-				case 'PROJECTION':
-
-					var m4 = boundUniform.uniform.value;
-					m4.copy( camera.projectionMatrix );
-					break;
-
-				case 'JOINTMATRIX':
-
-					var m4v = boundUniform.uniform.value;
-
-					for ( var mi = 0; mi < m4v.length; mi ++ ) {
-
-						// So it goes like this:
-						// SkinnedMesh world matrix is already baked into MODELVIEW;
-						// transform joints to local space,
-						// then transform using joint's inverse
-						m4v[ mi ]
-							.getInverse( boundUniform.sourceNode.matrixWorld )
-							.multiply( boundUniform.targetNode.skeleton.bones[ mi ].matrixWorld )
-							.multiply( boundUniform.targetNode.skeleton.boneInverses[ mi ] )
-							.multiply( boundUniform.targetNode.bindMatrix );
-
-					}
-
-					break;
-
-				default :
-
-					console.warn( 'THREE.GLTF2Loader: Unhandled shader semantic: ' + boundUniform.semantic );
-					break;
-
-			}
-
-		}
-
-	};
-
-	/*********************************/
-	/********** EXTENSIONS ***********/
-	/*********************************/
-
-	var EXTENSIONS = {
-		KHR_BINARY_GLTF: 'KHR_binary_glTF',
-		KHR_LIGHTS: 'KHR_lights',
-		KHR_MATERIALS_COMMON: 'KHR_materials_common',
-		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
-		KHR_TECHNIQUE_WEBGL: 'KHR_technique_webgl',
-	};
-
-	/**
-	 * Lights Extension
-	 *
-	 * Specification: PENDING
-	 */
-	function GLTFLightsExtension( json ) {
-
-		this.name = EXTENSIONS.KHR_LIGHTS;
-
-		this.lights = {};
-
-		var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS ] ) || {};
-		var lights = extension.lights || {};
-
-		for ( var lightId in lights ) {
-
-			var light = lights[ lightId ];
-			var lightNode;
-
-			var color = new THREE.Color().fromArray( light.color );
-
-			switch ( light.type ) {
-
-				case 'directional':
-					lightNode = new THREE.DirectionalLight( color );
-					lightNode.position.set( 0, 0, 1 );
-					break;
-
-				case 'point':
-					lightNode = new THREE.PointLight( color );
-					break;
-
-				case 'spot':
-					lightNode = new THREE.SpotLight( color );
-					lightNode.position.set( 0, 0, 1 );
-					break;
-
-				case 'ambient':
-					lightNode = new THREE.AmbientLight( color );
-					break;
-
-			}
-
-			if ( lightNode ) {
-
-				if ( light.constantAttenuation !== undefined ) {
-
-					lightNode.intensity = light.constantAttenuation;
-
-				}
-
-				if ( light.linearAttenuation !== undefined ) {
-
-					lightNode.distance = 1 / light.linearAttenuation;
-
-				}
-
-				if ( light.quadraticAttenuation !== undefined ) {
-
-					lightNode.decay = light.quadraticAttenuation;
-
-				}
-
-				if ( light.fallOffAngle !== undefined ) {
-
-					lightNode.angle = light.fallOffAngle;
-
-				}
-
-				if ( light.fallOffExponent !== undefined ) {
-
-					console.warn( 'THREE.GLTF2Loader:: light.fallOffExponent not currently supported.' );
-
-				}
-
-				lightNode.name = light.name || ( 'light_' + lightId );
-				this.lights[ lightId ] = lightNode;
-
-			}
-
-		}
-
-	}
-
-	/**
-	 * Common Materials Extension
-	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common
-	 */
-	function GLTFMaterialsCommonExtension( json ) {
-
-		this.name = EXTENSIONS.KHR_MATERIALS_COMMON;
-
-	}
-
-	GLTFMaterialsCommonExtension.prototype.getMaterialType = function ( material ) {
-
-		var khrMaterial = material.extensions[ this.name ];
-
-		switch ( khrMaterial.type ) {
-
-			case 'commonBlinn' :
-			case 'commonPhong' :
-				return THREE.MeshPhongMaterial;
-
-			case 'commonLambert' :
-				return THREE.MeshLambertMaterial;
-
-			case 'commonConstant' :
-			default :
-				return THREE.MeshBasicMaterial;
-
-		}
-
-	};
-
-	GLTFMaterialsCommonExtension.prototype.extendParams = function ( materialParams, material, dependencies ) {
-
-		var khrMaterial = material.extensions[ this.name ];
-
-		var keys = [];
-
-		// TODO: Currently ignored: 'ambientFactor', 'ambientTexture'
-		switch ( khrMaterial.type ) {
-
-			case 'commonBlinn' :
-			case 'commonPhong' :
-				keys.push( 'diffuseFactor', 'diffuseTexture', 'specularFactor', 'specularTexture', 'shininessFactor' );
-				break;
-
-			case 'commonLambert' :
-				keys.push( 'diffuseFactor', 'diffuseTexture' );
-				break;
-
-			case 'commonConstant' :
-			default :
-				break;
-
-		}
-
-		var materialValues = {};
-
-		keys.forEach( function( v ) {
-
-			if ( khrMaterial[ v ] !== undefined ) materialValues[ v ] = khrMaterial[ v ];
-
-		} );
-
-		if ( materialValues.diffuseFactor !== undefined ) {
-
-			materialParams.color = new THREE.Color().fromArray( materialValues.diffuseFactor );
-			materialParams.opacity = materialValues.diffuseFactor[ 3 ];
-
-		}
-
-		if ( materialValues.diffuseTexture !== undefined ) {
-
-			materialParams.map = dependencies.textures[ materialValues.diffuseTexture.index ];
-
-		}
-
-		if ( materialValues.specularFactor !== undefined ) {
-
-			materialParams.specular = new THREE.Color().fromArray( materialValues.specularFactor );
-
-		}
-
-		if ( materialValues.specularTexture !== undefined ) {
-
-			materialParams.specularMap = dependencies.textures[ materialValues.specularTexture.index ];
-
-		}
-
-		if ( materialValues.shininessFactor !== undefined ) {
-
-			materialParams.shininess = materialValues.shininessFactor;
-
-		}
-
-	};
-
-	/* BINARY EXTENSION */
-
-	var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
-	var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
-	var BINARY_EXTENSION_HEADER_LENGTH = 12;
-	var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
-
-	function GLTFBinaryExtension( data ) {
-
-		this.name = EXTENSIONS.KHR_BINARY_GLTF;
-		this.content = null;
-		this.body = null;
-
-		var headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
-
-		this.header = {
-			magic: convertUint8ArrayToString( new Uint8Array( data.slice( 0, 4 ) ) ),
-			version: headerView.getUint32( 4, true ),
-			length: headerView.getUint32( 8, true )
-		};
-
-		if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Unsupported glTF-Binary header.' );
-
-		} else if ( this.header.version < 2.0 ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Legacy binary file detected. Use GLTFLoader instead.' );
-
-		}
-
-		var chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH );
-		var chunkIndex = 0;
-
-		while ( chunkIndex < chunkView.byteLength ) {
-
-			var chunkLength = chunkView.getUint32( chunkIndex, true );
-			chunkIndex += 4;
-
-			var chunkType = chunkView.getUint32( chunkIndex, true );
-			chunkIndex += 4;
-
-			if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) {
-
-				var contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength );
-				this.content = convertUint8ArrayToString( contentArray );
-
-			} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) {
-
-				var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
-				this.body = data.slice( byteOffset, byteOffset + chunkLength );
-
-			}
-
-			// Clients must ignore chunks with unknown types.
-
-			chunkIndex += chunkLength;
-
-		}
-
-		if ( this.content === null ) {
-
-			throw new Error( 'THREE.GLTF2Loader: JSON content not found.' );
-
-		}
-
-	}
-
-	/**
-	 * WebGL Technique Extension
-	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_technique_webgl
-	 */
-	function GLTFTechniqueWebglExtension( json ) {
-
-		this.name = EXTENSIONS.KHR_TECHNIQUE_WEBGL;
-
-		var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] ) || {};
-
-		this.techniques = extension.techniques || {};
-		this.programs = extension.programs || {};
-		this.shaders = extension.shaders || {};
-
-	}
-
-	GLTFTechniqueWebglExtension.prototype.getMaterialType = function () {
-
-		return DeferredShaderMaterial;
-
-	};
-
-	GLTFTechniqueWebglExtension.prototype.extendParams = function ( materialParams, material, dependencies ) {
-
-		var extension = material[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ];
-		var technique = dependencies.techniques[ extension.technique ];
-
-		materialParams.uniforms = {};
-
-		var program = dependencies.programs[ technique.program ];
-
-		if ( program === undefined ) {
-
-			return;
-
-		}
-
-		materialParams.fragmentShader = dependencies.shaders[ program.fragmentShader ];
-
-		if ( ! materialParams.fragmentShader ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Missing fragment shader definition: ' + program.fragmentShader );
-
-		}
-
-		var vertexShader = dependencies.shaders[ program.vertexShader ];
-
-		if ( ! vertexShader ) {
-
-			throw new Error( 'THREE.GLTF2Loader: Missing vertex shader definition: ' + program.vertexShader );
-
-		}
-
-		// IMPORTANT: FIX VERTEX SHADER ATTRIBUTE DEFINITIONS
-		materialParams.vertexShader = replaceTHREEShaderAttributes( vertexShader, technique );
-
-		var uniforms = technique.uniforms;
-
-		for ( var uniformId in uniforms ) {
-
-			var pname = uniforms[ uniformId ];
-			var shaderParam = technique.parameters[ pname ];
-
-			var ptype = shaderParam.type;
-
-			if ( WEBGL_TYPE[ ptype ] ) {
-
-				var pcount = shaderParam.count;
-				var value;
-
-				if ( material.values !== undefined ) value = material.values[ pname ];
-
-				var uvalue = new WEBGL_TYPE[ ptype ]();
-				var usemantic = shaderParam.semantic;
-				var unode = shaderParam.node;
-
-				switch ( ptype ) {
-
-					case WEBGL_CONSTANTS.FLOAT:
-
-						uvalue = shaderParam.value;
-
-						if ( pname === 'transparency' ) {
-
-							materialParams.transparent = true;
-
-						}
-
-						if ( value !== undefined ) {
-
-							uvalue = value;
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_VEC2:
-					case WEBGL_CONSTANTS.FLOAT_VEC3:
-					case WEBGL_CONSTANTS.FLOAT_VEC4:
-					case WEBGL_CONSTANTS.FLOAT_MAT3:
-
-						if ( shaderParam && shaderParam.value ) {
-
-							uvalue.fromArray( shaderParam.value );
-
-						}
-
-						if ( value ) {
-
-							uvalue.fromArray( value );
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_MAT2:
-
-						// what to do?
-						console.warn( 'THREE.GLTF2Loader: FLOAT_MAT2 is not a supported uniform type.' );
-						break;
-
-					case WEBGL_CONSTANTS.FLOAT_MAT4:
-
-						if ( pcount ) {
-
-							uvalue = new Array( pcount );
-
-							for ( var mi = 0; mi < pcount; mi ++ ) {
-
-								uvalue[ mi ] = new WEBGL_TYPE[ ptype ]();
-
-							}
-
-							if ( shaderParam && shaderParam.value ) {
-
-								var m4v = shaderParam.value;
-								uvalue.fromArray( m4v );
-
-							}
-
-							if ( value ) {
-
-								uvalue.fromArray( value );
-
-							}
-
-						} else {
-
-							if ( shaderParam && shaderParam.value ) {
-
-								var m4 = shaderParam.value;
-								uvalue.fromArray( m4 );
-
-							}
-
-							if ( value ) {
-
-								uvalue.fromArray( value );
-
-							}
-
-						}
-
-						break;
-
-					case WEBGL_CONSTANTS.SAMPLER_2D:
-
-						if ( value !== undefined ) {
-
-							uvalue = dependencies.textures[ value ];
-
-						} else if ( shaderParam.value !== undefined ) {
-
-							uvalue = dependencies.textures[ shaderParam.value ];
-
-						} else {
-
-							uvalue = null;
-
-						}
-
-						break;
-
-				}
-
-				materialParams.uniforms[ uniformId ] = {
-					value: uvalue,
-					semantic: usemantic,
-					node: unode
-				};
-
-			} else {
-
-				throw new Error( 'THREE.GLTF2Loader: Unknown shader uniform param type: ' + ptype );
-
-			}
-
-		}
-
-		var states = technique.states || {};
-		var enables = states.enable || [];
-		var functions = states.functions || {};
-
-		var enableCullFace = false;
-		var enableDepthTest = false;
-		var enableBlend = false;
-
-		for ( var i = 0, il = enables.length; i < il; i ++ ) {
-
-			var enable = enables[ i ];
-
-			switch ( STATES_ENABLES[ enable ] ) {
-
-				case 'CULL_FACE':
-
-					enableCullFace = true;
-
-					break;
-
-				case 'DEPTH_TEST':
-
-					enableDepthTest = true;
-
-					break;
-
-				case 'BLEND':
-
-					enableBlend = true;
-
-					break;
-
-				// TODO: implement
-				case 'SCISSOR_TEST':
-				case 'POLYGON_OFFSET_FILL':
-				case 'SAMPLE_ALPHA_TO_COVERAGE':
-
-					break;
-
-				default:
-
-					throw new Error( 'THREE.GLTF2Loader: Unknown technique.states.enable: ' + enable );
-
-			}
-
-		}
-
-		if ( enableCullFace ) {
-
-			materialParams.side = functions.cullFace !== undefined ? WEBGL_SIDES[ functions.cullFace ] : THREE.FrontSide;
-
-		} else {
-
-			materialParams.side = THREE.DoubleSide;
-
-		}
-
-		materialParams.depthTest = enableDepthTest;
-		materialParams.depthFunc = functions.depthFunc !== undefined ? WEBGL_DEPTH_FUNCS[ functions.depthFunc ] : THREE.LessDepth;
-		materialParams.depthWrite = functions.depthMask !== undefined ? functions.depthMask[ 0 ] : true;
-
-		materialParams.blending = enableBlend ? THREE.CustomBlending : THREE.NoBlending;
-		materialParams.transparent = enableBlend;
-
-		var blendEquationSeparate = functions.blendEquationSeparate;
-
-		if ( blendEquationSeparate !== undefined ) {
-
-			materialParams.blendEquation = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 0 ] ];
-			materialParams.blendEquationAlpha = WEBGL_BLEND_EQUATIONS[ blendEquationSeparate[ 1 ] ];
-
-		} else {
-
-			materialParams.blendEquation = THREE.AddEquation;
-			materialParams.blendEquationAlpha = THREE.AddEquation;
-
-		}
-
-		var blendFuncSeparate = functions.blendFuncSeparate;
-
-		if ( blendFuncSeparate !== undefined ) {
-
-			materialParams.blendSrc = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 0 ] ];
-			materialParams.blendDst = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 1 ] ];
-			materialParams.blendSrcAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 2 ] ];
-			materialParams.blendDstAlpha = WEBGL_BLEND_FUNCS[ blendFuncSeparate[ 3 ] ];
-
-		} else {
-
-			materialParams.blendSrc = THREE.OneFactor;
-			materialParams.blendDst = THREE.ZeroFactor;
-			materialParams.blendSrcAlpha = THREE.OneFactor;
-			materialParams.blendDstAlpha = THREE.ZeroFactor;
-
-		}
-
-	};
-
-	/**
-	 * Specular-Glossiness Extension
-	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness
-	 */
-	function GLTFMaterialsPbrSpecularGlossinessExtension() {
-
-		return {
-
-			name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
-
-			getMaterialType: function () {
-
-				return THREE.ShaderMaterial;
-
-			},
-
-			extendParams: function ( params, material, dependencies ) {
-
-				// specification
-				// https://github.com/sbtron/glTF/tree/KHRpbrSpecGloss/extensions/Khronos/KHR_materials_pbrSpecularGlossiness
-
-				var pbrSpecularGlossiness = material.extensions[ this.name ];
-
-				var shader = THREE.ShaderLib[ 'standard' ];
-
-				var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
-
-				var specularMapParsFragmentChunk = [
-					'#ifdef USE_SPECULARMAP',
-					'	uniform sampler2D specularMap;',
-					'#endif'
-				].join( '\n' );
-
-				var glossinessMapParsFragmentChunk = [
-					'#ifdef USE_GLOSSINESSMAP',
-					'	uniform sampler2D glossinessMap;',
-					'#endif'
-				].join( '\n' );
-
-				var specularMapFragmentChunk = [
-					'vec3 specularFactor = specular;',
-					'#ifdef USE_SPECULARMAP',
-					'	vec4 texelSpecular = texture2D( specularMap, vUv );',
-					'	// reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
-					'	specularFactor *= texelSpecular.rgb;',
-					'#endif'
-				].join( '\n' );
-
-				var glossinessMapFragmentChunk = [
-					'float glossinessFactor = glossiness;',
-					'#ifdef USE_GLOSSINESSMAP',
-					'	vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
-					'	// reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
-					'	glossinessFactor *= texelGlossiness.a;',
-					'#endif'
-				].join( '\n' );
-
-				var lightPhysicalFragmentChunk = [
-					'PhysicalMaterial material;',
-					'material.diffuseColor = diffuseColor.rgb;',
-					'material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );',
-					'material.specularColor = specularFactor.rgb;',
-				].join( '\n' );
-
-				var fragmentShader = shader.fragmentShader
-							.replace( '#include <specularmap_fragment>', '' )
-							.replace( 'uniform float roughness;', 'uniform vec3 specular;' )
-							.replace( 'uniform float metalness;', 'uniform float glossiness;' )
-							.replace( '#include <roughnessmap_pars_fragment>', specularMapParsFragmentChunk )
-							.replace( '#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk )
-							.replace( '#include <roughnessmap_fragment>', specularMapFragmentChunk )
-							.replace( '#include <metalnessmap_fragment>', glossinessMapFragmentChunk )
-							.replace( '#include <lights_physical_fragment>', lightPhysicalFragmentChunk );
-
-				delete uniforms.roughness;
-				delete uniforms.metalness;
-				delete uniforms.roughnessMap;
-				delete uniforms.metalnessMap;
-
-				uniforms.specular = { value: new THREE.Color().setHex( 0x111111 ) };
-				uniforms.glossiness = { value: 0.5 };
-				uniforms.specularMap = { value: null };
-				uniforms.glossinessMap = { value: null };
-
-				params.vertexShader = shader.vertexShader;
-				params.fragmentShader = fragmentShader;
-				params.uniforms = uniforms;
-				params.defines = { 'STANDARD': '' };
-
-				params.color = new THREE.Color( 1.0, 1.0, 1.0 );
-				params.opacity = 1.0;
-
-				if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) {
-
-					var array = pbrSpecularGlossiness.diffuseFactor;
-
-					params.color.fromArray( array );
-					params.opacity = array[ 3 ];
-
-				}
-
-				if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {
-
-					params.map = dependencies.textures[ pbrSpecularGlossiness.diffuseTexture.index ];
-
-				}
-
-				params.emissive = new THREE.Color( 0.0, 0.0, 0.0 );
-				params.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
-				params.specular = new THREE.Color( 1.0, 1.0, 1.0 );
-
-				if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) {
-
-					params.specular.fromArray( pbrSpecularGlossiness.specularFactor );
-
-				}
-
-				if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {
-
-					params.glossinessMap = dependencies.textures[ pbrSpecularGlossiness.specularGlossinessTexture.index ];
-					params.specularMap = dependencies.textures[ pbrSpecularGlossiness.specularGlossinessTexture.index ];
-
-				}
-
-			},
-
-			createMaterial: function ( params ) {
-
-				// setup material properties based on MeshStandardMaterial for Specular-Glossiness
-
-				var material = new THREE.ShaderMaterial( {
-					defines: params.defines,
-					vertexShader: params.vertexShader,
-					fragmentShader: params.fragmentShader,
-					uniforms: params.uniforms,
-					fog: true,
-					lights: true,
-					opacity: params.opacity,
-					transparent: params.transparent
-				} );
-
-				material.isGLTFSpecularGlossinessMaterial = true;
-
-				material.color = params.color;
-
-				material.map = params.map === undefined ? null : params.map;
-
-				material.lightMap = null;
-				material.lightMapIntensity = 1.0;
-
-				material.aoMap = params.aoMap === undefined ? null : params.aoMap;
-				material.aoMapIntensity = 1.0;
-
-				material.emissive = params.emissive;
-				material.emissiveIntensity = 1.0;
-				material.emissiveMap = params.emissiveMap === undefined ? null : params.emissiveMap;
-
-				material.bumpMap = params.bumpMap === undefined ? null : params.bumpMap;
-				material.bumpScale = 1;
-
-				material.normalMap = params.normalMap === undefined ? null : params.normalMap;
-				material.normalScale = new THREE.Vector2( 1, 1 );
-
-				material.displacementMap = null;
-				material.displacementScale = 1;
-				material.displacementBias = 0;
-
-				material.specularMap = params.specularMap === undefined ? null : params.specularMap;
-				material.specular = params.specular;
-
-				material.glossinessMap = params.glossinessMap === undefined ? null : params.glossinessMap;
-				material.glossiness = params.glossiness;
-
-				material.alphaMap = null;
-
-				material.envMap = params.envMap === undefined ? null : params.envMap;
-				material.envMapIntensity = 1.0;
-
-				material.refractionRatio = 0.98;
-
-				material.extensions.derivatives = true;
-
-				return material;
-
-			},
-
-			// Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer.
-			refreshUniforms: function ( renderer, scene, camera, geometry, material, group ) {
-
-				var uniforms = material.uniforms;
-				var defines = material.defines;
-
-				uniforms.opacity.value = material.opacity;
-
-				uniforms.diffuse.value.copy( material.color );
-				uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
-
-				uniforms.map.value = material.map;
-				uniforms.specularMap.value = material.specularMap;
-				uniforms.alphaMap.value = material.alphaMap;
-
-				uniforms.lightMap.value = material.lightMap;
-				uniforms.lightMapIntensity.value = material.lightMapIntensity;
-
-				uniforms.aoMap.value = material.aoMap;
-				uniforms.aoMapIntensity.value = material.aoMapIntensity;
-
-				// uv repeat and offset setting priorities
-				// 1. color map
-				// 2. specular map
-				// 3. normal map
-				// 4. bump map
-				// 5. alpha map
-				// 6. emissive map
-
-				var uvScaleMap;
-
-				if ( material.map ) {
-
-					uvScaleMap = material.map;
-
-				} else if ( material.specularMap ) {
-
-					uvScaleMap = material.specularMap;
-
-				} else if ( material.displacementMap ) {
-
-					uvScaleMap = material.displacementMap;
-
-				} else if ( material.normalMap ) {
-
-					uvScaleMap = material.normalMap;
-
-				} else if ( material.bumpMap ) {
-
-					uvScaleMap = material.bumpMap;
-
-				} else if ( material.glossinessMap ) {
-
-					uvScaleMap = material.glossinessMap;
-
-				} else if ( material.alphaMap ) {
-
-					uvScaleMap = material.alphaMap;
-
-				} else if ( material.emissiveMap ) {
-
-					uvScaleMap = material.emissiveMap;
-
-				}
-
-				if ( uvScaleMap !== undefined ) {
-
-					// backwards compatibility
-					if ( uvScaleMap.isWebGLRenderTarget ) {
-
-						uvScaleMap = uvScaleMap.texture;
-
-					}
-
-					var offset = uvScaleMap.offset;
-					var repeat = uvScaleMap.repeat;
-
-					uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
-
-				}
-
-				uniforms.envMap.value = material.envMap;
-				uniforms.envMapIntensity.value = material.envMapIntensity;
-				uniforms.flipEnvMap.value = ( material.envMap && material.envMap.isCubeTexture ) ? -1 : 1;
-
-				uniforms.refractionRatio.value = material.refractionRatio;
-
-				uniforms.specular.value.copy( material.specular );
-				uniforms.glossiness.value = material.glossiness;
-
-				uniforms.glossinessMap.value = material.glossinessMap;
-
-				uniforms.emissiveMap.value = material.emissiveMap;
-				uniforms.bumpMap.value = material.bumpMap;
-				uniforms.normalMap.value = material.normalMap;
-
-				uniforms.displacementMap.value = material.displacementMap;
-				uniforms.displacementScale.value = material.displacementScale;
-				uniforms.displacementBias.value = material.displacementBias;
-
-				if ( uniforms.glossinessMap.value !== null && defines.USE_GLOSSINESSMAP === undefined ) {
-
-					defines.USE_GLOSSINESSMAP = '';
-					// set USE_ROUGHNESSMAP to enable vUv
-					defines.USE_ROUGHNESSMAP = ''
-
-				}
-
-				if ( uniforms.glossinessMap.value === null && defines.USE_GLOSSINESSMAP !== undefined ) {
-
-					delete defines.USE_GLOSSINESSMAP;
-					delete defines.USE_ROUGHNESSMAP;
-
-				}
-
-			}
-
-		};
-
-	}
-
-	/*********************************/
-	/********** INTERNALS ************/
-	/*********************************/
-
-	/* CONSTANTS */
-
-	var WEBGL_CONSTANTS = {
-		FLOAT: 5126,
-		//FLOAT_MAT2: 35674,
-		FLOAT_MAT3: 35675,
-		FLOAT_MAT4: 35676,
-		FLOAT_VEC2: 35664,
-		FLOAT_VEC3: 35665,
-		FLOAT_VEC4: 35666,
-		LINEAR: 9729,
-		REPEAT: 10497,
-		SAMPLER_2D: 35678,
-		TRIANGLES: 4,
-		LINES: 1,
-		UNSIGNED_BYTE: 5121,
-		UNSIGNED_SHORT: 5123,
-
-		VERTEX_SHADER: 35633,
-		FRAGMENT_SHADER: 35632
-	};
-
-	var WEBGL_TYPE = {
-		5126: Number,
-		//35674: THREE.Matrix2,
-		35675: THREE.Matrix3,
-		35676: THREE.Matrix4,
-		35664: THREE.Vector2,
-		35665: THREE.Vector3,
-		35666: THREE.Vector4,
-		35678: THREE.Texture
-	};
-
-	var WEBGL_COMPONENT_TYPES = {
-		5120: Int8Array,
-		5121: Uint8Array,
-		5122: Int16Array,
-		5123: Uint16Array,
-		5125: Uint32Array,
-		5126: Float32Array
-	};
-
-	var WEBGL_FILTERS = {
-		9728: THREE.NearestFilter,
-		9729: THREE.LinearFilter,
-		9984: THREE.NearestMipMapNearestFilter,
-		9985: THREE.LinearMipMapNearestFilter,
-		9986: THREE.NearestMipMapLinearFilter,
-		9987: THREE.LinearMipMapLinearFilter
-	};
-
-	var WEBGL_WRAPPINGS = {
-		33071: THREE.ClampToEdgeWrapping,
-		33648: THREE.MirroredRepeatWrapping,
-		10497: THREE.RepeatWrapping
-	};
-
-	var WEBGL_TEXTURE_FORMATS = {
-		6406: THREE.AlphaFormat,
-		6407: THREE.RGBFormat,
-		6408: THREE.RGBAFormat,
-		6409: THREE.LuminanceFormat,
-		6410: THREE.LuminanceAlphaFormat
-	};
-
-	var WEBGL_TEXTURE_DATATYPES = {
-		5121: THREE.UnsignedByteType,
-		32819: THREE.UnsignedShort4444Type,
-		32820: THREE.UnsignedShort5551Type,
-		33635: THREE.UnsignedShort565Type
-	};
-
-	var WEBGL_SIDES = {
-		1028: THREE.BackSide,  // Culling front
-		1029: THREE.FrontSide  // Culling back
-		//1032: THREE.NoSide   // Culling front and back, what to do?
-	};
-
-	var WEBGL_DEPTH_FUNCS = {
-		512: THREE.NeverDepth,
-		513: THREE.LessDepth,
-		514: THREE.EqualDepth,
-		515: THREE.LessEqualDepth,
-		516: THREE.GreaterEqualDepth,
-		517: THREE.NotEqualDepth,
-		518: THREE.GreaterEqualDepth,
-		519: THREE.AlwaysDepth
-	};
-
-	var WEBGL_BLEND_EQUATIONS = {
-		32774: THREE.AddEquation,
-		32778: THREE.SubtractEquation,
-		32779: THREE.ReverseSubtractEquation
-	};
-
-	var WEBGL_BLEND_FUNCS = {
-		0: THREE.ZeroFactor,
-		1: THREE.OneFactor,
-		768: THREE.SrcColorFactor,
-		769: THREE.OneMinusSrcColorFactor,
-		770: THREE.SrcAlphaFactor,
-		771: THREE.OneMinusSrcAlphaFactor,
-		772: THREE.DstAlphaFactor,
-		773: THREE.OneMinusDstAlphaFactor,
-		774: THREE.DstColorFactor,
-		775: THREE.OneMinusDstColorFactor,
-		776: THREE.SrcAlphaSaturateFactor
-		// The followings are not supported by Three.js yet
-		//32769: CONSTANT_COLOR,
-		//32770: ONE_MINUS_CONSTANT_COLOR,
-		//32771: CONSTANT_ALPHA,
-		//32772: ONE_MINUS_CONSTANT_COLOR
-	};
-
-	var WEBGL_TYPE_SIZES = {
-		'SCALAR': 1,
-		'VEC2': 2,
-		'VEC3': 3,
-		'VEC4': 4,
-		'MAT2': 4,
-		'MAT3': 9,
-		'MAT4': 16
-	};
-
-	var PATH_PROPERTIES = {
-		scale: 'scale',
-		translation: 'position',
-		rotation: 'quaternion',
-		weights: 'morphTargetInfluences'
-	};
-
-	var INTERPOLATION = {
-		CATMULLROMSPLINE: THREE.InterpolateSmooth,
-		CUBICSPLINE: THREE.InterpolateSmooth,
-		LINEAR: THREE.InterpolateLinear,
-		STEP: THREE.InterpolateDiscrete
-	};
-
-	var STATES_ENABLES = {
-		2884: 'CULL_FACE',
-		2929: 'DEPTH_TEST',
-		3042: 'BLEND',
-		3089: 'SCISSOR_TEST',
-		32823: 'POLYGON_OFFSET_FILL',
-		32926: 'SAMPLE_ALPHA_TO_COVERAGE'
-	};
-
-	var ALPHA_MODES = {
-		OPAQUE: 'OPAQUE',
-		MASK: 'MASK',
-		BLEND: 'BLEND'
-	};
-
-	/* UTILITY FUNCTIONS */
-
-	function _each( object, callback, thisObj ) {
-
-		if ( !object ) {
-			return Promise.resolve();
-		}
-
-		var results;
-		var fns = [];
-
-		if ( Object.prototype.toString.call( object ) === '[object Array]' ) {
-
-			results = [];
-
-			var length = object.length;
-
-			for ( var idx = 0; idx < length; idx ++ ) {
-
-				var value = callback.call( thisObj || this, object[ idx ], idx );
-
-				if ( value ) {
-
-					fns.push( value );
-
-					if ( value instanceof Promise ) {
-
-						value.then( function( key, value ) {
-
-							results[ key ] = value;
-
-						}.bind( this, idx ));
-
-					} else {
-
-						results[ idx ] = value;
-
-					}
-
-				}
-
-			}
-
-		} else {
-
-			results = {};
-
-			for ( var key in object ) {
-
-				if ( object.hasOwnProperty( key ) ) {
-
-					var value = callback.call( thisObj || this, object[ key ], key );
-
-					if ( value ) {
-
-						fns.push( value );
-
-						if ( value instanceof Promise ) {
-
-							value.then( function( key, value ) {
-
-								results[ key ] = value;
-
-							}.bind( this, key ));
-
-						} else {
-
-							results[ key ] = value;
-
-						}
-
-					}
-
-				}
-
-			}
-
-		}
-
-		return Promise.all( fns ).then( function() {
-
-			return results;
-
-		});
-
-	}
-
-	function resolveURL( url, path ) {
-
-		// Invalid URL
-		if ( typeof url !== 'string' || url === '' )
-			return '';
-
-		// Absolute URL http://,https://,//
-		if ( /^(https?:)?\/\//i.test( url ) ) {
-
-			return url;
-
-		}
-
-		// Data URI
-		if ( /^data:.*,.*$/i.test( url ) ) {
-
-			return url;
-
-		}
-
-		// Blob URL
-		if ( /^blob:.*$/i.test( url ) ) {
-
-			return url;
-
-		}
-
-		// Relative URL
-		return ( path || '' ) + url;
-
-	}
-
-	function convertUint8ArrayToString( array ) {
-
-		if ( window.TextDecoder !== undefined ) {
-
-			return new TextDecoder().decode( array );
-
-		}
-
-		// Avoid the String.fromCharCode.apply(null, array) shortcut, which
-		// throws a "maximum call stack size exceeded" error for large arrays.
-
-		var s = '';
-
-		for ( var i = 0, il = array.length; i < il; i ++ ) {
-
-			s += String.fromCharCode( array[ i ] );
-
-		}
-
-		return s;
-
-	}
-
-	// Three.js seems too dependent on attribute names so globally
-	// replace those in the shader code
-	function replaceTHREEShaderAttributes( shaderText, technique ) {
-
-		// Expected technique attributes
-		var attributes = {};
-
-		for ( var attributeId in technique.attributes ) {
-
-			var pname = technique.attributes[ attributeId ];
-
-			var param = technique.parameters[ pname ];
-			var atype = param.type;
-			var semantic = param.semantic;
-
-			attributes[ attributeId ] = {
-				type: atype,
-				semantic: semantic
-			};
-
-		}
-
-		// Figure out which attributes to change in technique
-
-		var shaderParams = technique.parameters;
-		var shaderAttributes = technique.attributes;
-		var params = {};
-
-		for ( var attributeId in attributes ) {
-
-			var pname = shaderAttributes[ attributeId ];
-			var shaderParam = shaderParams[ pname ];
-			var semantic = shaderParam.semantic;
-			if ( semantic ) {
-
-				params[ attributeId ] = shaderParam;
-
-			}
-
-		}
-
-		for ( var pname in params ) {
-
-			var param = params[ pname ];
-			var semantic = param.semantic;
-
-			var regEx = new RegExp( '\\b' + pname + '\\b', 'g' );
-
-			switch ( semantic ) {
-
-				case 'POSITION':
-
-					shaderText = shaderText.replace( regEx, 'position' );
-					break;
-
-				case 'NORMAL':
-
-					shaderText = shaderText.replace( regEx, 'normal' );
-					break;
-
-				case 'TEXCOORD_0':
-				case 'TEXCOORD0':
-				case 'TEXCOORD':
-
-					shaderText = shaderText.replace( regEx, 'uv' );
-					break;
-
-				case 'TEXCOORD_1':
-
-					shaderText = shaderText.replace( regEx, 'uv2' );
-					break;
-
-				case 'COLOR_0':
-				case 'COLOR0':
-				case 'COLOR':
-
-					shaderText = shaderText.replace( regEx, 'color' );
-					break;
-
-				case 'WEIGHTS_0':
-				case 'WEIGHT': // WEIGHT semantic deprecated.
-
-					shaderText = shaderText.replace( regEx, 'skinWeight' );
-					break;
-
-				case 'JOINTS_0':
-				case 'JOINT': // JOINT semantic deprecated.
-
-					shaderText = shaderText.replace( regEx, 'skinIndex' );
-					break;
-
-			}
-
-		}
-
-		return shaderText;
-
-	}
-
-	/**
-	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
-	 */
-	function createDefaultMaterial() {
-
-		return new THREE.MeshStandardMaterial( {
-			color: 0xFFFFFF,
-			emissive: 0x000000,
-			metalness: 1,
-			roughness: 1,
-			transparent: false,
-			depthTest: true,
-			side: THREE.FrontSide
-		} );
-
-	}
-
-	// Deferred constructor for RawShaderMaterial types
-	function DeferredShaderMaterial( params ) {
-
-		this.isDeferredShaderMaterial = true;
-
-		this.params = params;
-
-	}
-
-	DeferredShaderMaterial.prototype.create = function () {
-
-		var uniforms = THREE.UniformsUtils.clone( this.params.uniforms );
-
-		for ( var uniformId in this.params.uniforms ) {
-
-			var originalUniform = this.params.uniforms[ uniformId ];
-
-			if ( originalUniform.value instanceof THREE.Texture ) {
-
-				uniforms[ uniformId ].value = originalUniform.value;
-				uniforms[ uniformId ].value.needsUpdate = true;
-
-			}
-
-			uniforms[ uniformId ].semantic = originalUniform.semantic;
-			uniforms[ uniformId ].node = originalUniform.node;
-
-		}
-
-		this.params.uniforms = uniforms;
-
-		return new THREE.RawShaderMaterial( this.params );
-
-	};
-
-	/* GLTF PARSER */
-
-	function GLTFParser( json, extensions, options ) {
-
-		this.json = json || {};
-		this.extensions = extensions || {};
-		this.options = options || {};
-
-		// loader object cache
-		this.cache = new GLTFRegistry();
-
-	}
-
-	GLTFParser.prototype._withDependencies = function ( dependencies ) {
-
-		var _dependencies = {};
-
-		for ( var i = 0; i < dependencies.length; i ++ ) {
-
-			var dependency = dependencies[ i ];
-			var fnName = 'load' + dependency.charAt( 0 ).toUpperCase() + dependency.slice( 1 );
-
-			var cached = this.cache.get( dependency );
-
-			if ( cached !== undefined ) {
-
-				_dependencies[ dependency ] = cached;
-
-			} else if ( this[ fnName ] ) {
-
-				var fn = this[ fnName ]();
-				this.cache.add( dependency, fn );
-
-				_dependencies[ dependency ] = fn;
-
-			}
-
-		}
-
-		return _each( _dependencies, function ( dependency ) {
-
-			return dependency;
-
-		} );
-
-	};
-
-	GLTFParser.prototype.parse = function ( onLoad, onError ) {
-
-		var json = this.json;
-
-		// Clear the loader cache
-		this.cache.removeAll();
-
-		// Fire the callback on complete
-		this._withDependencies( [
-
-			'scenes',
-			'cameras',
-			'animations'
-
-		] ).then( function ( dependencies ) {
-
-			var scenes = [];
-
-			for ( var name in dependencies.scenes ) {
-
-				scenes.push( dependencies.scenes[ name ] );
-
-			}
-
-			var scene = json.scene !== undefined ? dependencies.scenes[ json.scene ] : scenes[ 0 ];
-
-			var cameras = [];
-
-			for ( var name in dependencies.cameras ) {
-
-				var camera = dependencies.cameras[ name ];
-				cameras.push( camera );
-
-			}
-
-			var animations = [];
-
-			for ( var name in dependencies.animations ) {
-
-				animations.push( dependencies.animations[ name ] );
-
-			}
-
-			onLoad( scene, scenes, cameras, animations );
-
-		} ).catch( onError );
-
-	};
-
-	GLTFParser.prototype.loadShaders = function () {
-
-		var json = this.json;
-		var options = this.options;
-		var extensions = this.extensions;
-
-		return this._withDependencies( [
-
-			'bufferViews'
-
-		] ).then( function ( dependencies ) {
-
-			var shaders = extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] !== undefined ? extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].shaders : json.shaders;
-
-			if ( shaders === undefined ) shaders = {};
-
-			return _each( shaders, function ( shader ) {
-
-				if ( shader.bufferView !== undefined ) {
-
-					var bufferView = dependencies.bufferViews[ shader.bufferView ];
-					var array = new Uint8Array( bufferView );
-					return convertUint8ArrayToString( array );
-
-				}
-
-				return new Promise( function ( resolve ) {
-
-					var loader = new THREE.FileLoader();
-					loader.setResponseType( 'text' );
-					loader.load( resolveURL( shader.uri, options.path ), function ( shaderText ) {
-
-						resolve( shaderText );
-
-					} );
-
-				} );
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadBuffers = function () {
-
-		var json = this.json;
-		var extensions = this.extensions;
-		var options = this.options;
-
-		return _each( json.buffers, function ( buffer, name ) {
-
-			if ( buffer.type === 'arraybuffer' || buffer.type === undefined ) {
-
-				// If present, GLB container is required to be the first buffer.
-				if ( buffer.uri === undefined && name === 0 ) {
-
-					return extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body;
-
-				}
-
-				return new Promise( function ( resolve ) {
-
-					var loader = new THREE.FileLoader();
-					loader.setResponseType( 'arraybuffer' );
-					loader.load( resolveURL( buffer.uri, options.path ), function ( buffer ) {
-
-						resolve( buffer );
-
-					} );
-
-				} );
-
-			} else {
-
-				console.warn( 'THREE.GLTF2Loader: %s buffer type is not supported.', buffer.type );
-
-			}
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadBufferViews = function () {
-
-		var json = this.json;
-
-		return this._withDependencies( [
-
-			'buffers'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.bufferViews, function ( bufferView ) {
-
-				var arraybuffer = dependencies.buffers[ bufferView.buffer ];
-
-				var byteLength = bufferView.byteLength || 0;
-				var byteOffset = bufferView.byteOffset || 0;
-
-				return arraybuffer.slice( byteOffset, byteOffset + byteLength );
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadAccessors = function () {
-
-		var json = this.json;
-
-		return this._withDependencies( [
-
-			'bufferViews'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.accessors, function ( accessor ) {
-
-				var arraybuffer = dependencies.bufferViews[ accessor.bufferView ];
-				var itemSize = WEBGL_TYPE_SIZES[ accessor.type ];
-				var TypedArray = WEBGL_COMPONENT_TYPES[ accessor.componentType ];
-
-				// For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
-				var elementBytes = TypedArray.BYTES_PER_ELEMENT;
-				var itemBytes = elementBytes * itemSize;
-				var byteStride = json.bufferViews[ accessor.bufferView ].byteStride;
-				var array;
-
-				// The buffer is not interleaved if the stride is the item size in bytes.
-				if ( byteStride && byteStride !== itemBytes ) {
-
-					// Use the full buffer if it's interleaved.
-					array = new TypedArray( arraybuffer );
-
-					// Integer parameters to IB/IBA are in array elements, not bytes.
-					var ib = new THREE.InterleavedBuffer( array, byteStride / elementBytes );
-
-					return new THREE.InterleavedBufferAttribute( ib, itemSize, accessor.byteOffset / elementBytes );
-
-				} else {
-
-					array = new TypedArray( arraybuffer, accessor.byteOffset, accessor.count * itemSize );
-
-					return new THREE.BufferAttribute( array, itemSize );
-
-				}
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadTextures = function () {
-
-		var json = this.json;
-		var options = this.options;
-
-		return this._withDependencies( [
-
-			'bufferViews'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.textures, function ( texture ) {
-
-				if ( texture.source !== undefined ) {
-
-					return new Promise( function ( resolve ) {
-
-						var source = json.images[ texture.source ];
-						var sourceUri = source.uri;
-
-						var urlCreator;
-
-						if ( source.bufferView !== undefined ) {
-
-							var bufferView = dependencies.bufferViews[ source.bufferView ];
-							var blob = new Blob( [ bufferView ], { type: source.mimeType } );
-							urlCreator = window.URL || window.webkitURL;
-							sourceUri = urlCreator.createObjectURL( blob );
-
-						}
-
-						var textureLoader = THREE.Loader.Handlers.get( sourceUri );
-
-						if ( textureLoader === null ) {
-
-							textureLoader = new THREE.TextureLoader();
-
-						}
-
-						textureLoader.setCrossOrigin( options.crossOrigin );
-
-						textureLoader.load( resolveURL( sourceUri, options.path ), function ( _texture ) {
-
-							if ( urlCreator !== undefined ) {
-
-								urlCreator.revokeObjectURL( sourceUri );
-
-							}
-
-							_texture.flipY = false;
-
-							if ( texture.name !== undefined ) _texture.name = texture.name;
-
-							_texture.format = texture.format !== undefined ? WEBGL_TEXTURE_FORMATS[ texture.format ] : THREE.RGBAFormat;
-
-							if ( texture.internalFormat !== undefined && _texture.format !== WEBGL_TEXTURE_FORMATS[ texture.internalFormat ] ) {
-
-								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.' );
-
-							}
-
-							_texture.type = texture.type !== undefined ? WEBGL_TEXTURE_DATATYPES[ texture.type ] : THREE.UnsignedByteType;
-
-							var samplers = json.samplers || {};
-							var sampler = samplers[ texture.sampler ] || {};
-
-							_texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
-							_texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipMapLinearFilter;
-							_texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
-							_texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
-
-							resolve( _texture );
-
-						}, undefined, function () {
-
-							resolve();
-
-						} );
-
-					} );
-
-				}
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadMaterials = function () {
-
-		var json = this.json;
-		var extensions = this.extensions;
-
-		return this._withDependencies( [
-
-			'shaders',
-			'textures'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.materials, function ( material ) {
-
-				var materialType;
-				var materialParams = {};
-				var materialExtensions = material.extensions || {};
-
-				if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_COMMON ] ) {
-
-					materialType = extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].getMaterialType( material );
-					extensions[ EXTENSIONS.KHR_MATERIALS_COMMON ].extendParams( materialParams, material, dependencies );
-
-				} else if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] ) {
-
-					materialType = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].getMaterialType( material );
-					extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].extendParams( materialParams, material, dependencies );
-
-				} else if ( materialExtensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ] ) {
-
-					materialType = extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].getMaterialType( material );
-					extensions[ EXTENSIONS.KHR_TECHNIQUE_WEBGL ].extendParams( materialParams, material, dependencies );
-
-				} else if ( material.pbrMetallicRoughness !== undefined ) {
-
-					// Specification:
-					// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
-
-					materialType = THREE.MeshStandardMaterial;
-
-					var metallicRoughness = material.pbrMetallicRoughness;
-
-					materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
-					materialParams.opacity = 1.0;
-
-					if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
-
-						var array = metallicRoughness.baseColorFactor;
-
-						materialParams.color.fromArray( array );
-						materialParams.opacity = array[ 3 ];
-
-					}
-
-					if ( metallicRoughness.baseColorTexture !== undefined ) {
-
-						materialParams.map = dependencies.textures[ metallicRoughness.baseColorTexture.index ];
-
-					}
-
-					materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
-					materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;
-
-					if ( metallicRoughness.metallicRoughnessTexture !== undefined ) {
-
-						var textureIndex = metallicRoughness.metallicRoughnessTexture.index;
-						materialParams.metalnessMap = dependencies.textures[ textureIndex ];
-						materialParams.roughnessMap = dependencies.textures[ textureIndex ];
-
-					}
-
-				} else {
-
-					materialType = THREE.MeshPhongMaterial;
-
-				}
-
-				if ( material.doubleSided === true ) {
-
-					materialParams.side = THREE.DoubleSide;
-
-				}
-
-				var alphaMode = material.alphaMode || ALPHA_MODES.OPAQUE;
-
-				if ( alphaMode !== ALPHA_MODES.OPAQUE ) {
-
-					materialParams.transparent = true;
-
-				} else {
-
-					materialParams.transparent = false;
-
-				}
-
-				if ( material.normalTexture !== undefined ) {
-
-					materialParams.normalMap = dependencies.textures[ material.normalTexture.index ];
-
-				}
-
-				if ( material.occlusionTexture !== undefined ) {
-
-					materialParams.aoMap = dependencies.textures[ material.occlusionTexture.index ];
-
-				}
-
-				if ( material.emissiveFactor !== undefined ) {
-
-					if ( materialType === THREE.MeshBasicMaterial ) {
-
-						materialParams.color = new THREE.Color().fromArray( material.emissiveFactor );
-
-					} else {
-
-						materialParams.emissive = new THREE.Color().fromArray( material.emissiveFactor );
-
-					}
-
-				}
-
-				if ( material.emissiveTexture !== undefined ) {
-
-					if ( materialType === THREE.MeshBasicMaterial ) {
-
-						materialParams.map = dependencies.textures[ material.emissiveTexture.index ];
-
-					} else {
-
-						materialParams.emissiveMap = dependencies.textures[ material.emissiveTexture.index ];
-
-					}
-
-				}
-
-				var _material;
-
-				if ( materialType === THREE.ShaderMaterial ) {
-
-					_material = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].createMaterial( materialParams );
-
-				} else {
-
-					_material = new materialType( materialParams );
-
-				}
-
-				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;
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadMeshes = function () {
-
-		var json = this.json;
-
-		return this._withDependencies( [
-
-			'accessors',
-			'materials'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.meshes, function ( mesh ) {
-
-				var group = new THREE.Group();
-				if ( mesh.name !== undefined ) group.name = mesh.name;
-
-				if ( mesh.extras ) group.userData = mesh.extras;
-
-				var primitives = mesh.primitives || [];
-
-				for ( var name in primitives ) {
-
-					var primitive = primitives[ name ];
-
-					var material = primitive.material !== undefined ? dependencies.materials[ primitive.material ] : createDefaultMaterial();
-
-					var geometry;
-
-					var meshNode;
-
-					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === undefined ) {
-
-						geometry = new THREE.BufferGeometry();
-
-						var attributes = primitive.attributes;
-
-						for ( var attributeId in attributes ) {
-
-							var attributeEntry = attributes[ attributeId ];
-
-							if ( attributeEntry === undefined ) return;
-
-							var bufferAttribute = dependencies.accessors[ attributeEntry ];
-
-							switch ( attributeId ) {
-
-								case 'POSITION':
-
-									geometry.addAttribute( 'position', bufferAttribute );
-									break;
-
-								case 'NORMAL':
-
-									geometry.addAttribute( 'normal', bufferAttribute );
-									break;
-
-								case 'TEXCOORD_0':
-								case 'TEXCOORD0':
-								case 'TEXCOORD':
-
-									geometry.addAttribute( 'uv', bufferAttribute );
-									break;
-
-								case 'TEXCOORD_1':
-
-									geometry.addAttribute( 'uv2', bufferAttribute );
-									break;
-
-								case 'COLOR_0':
-								case 'COLOR0':
-								case 'COLOR':
-
-									geometry.addAttribute( 'color', bufferAttribute );
-									break;
-
-								case 'WEIGHTS_0':
-								case 'WEIGHT': // WEIGHT semantic deprecated.
-
-									geometry.addAttribute( 'skinWeight', bufferAttribute );
-									break;
-
-								case 'JOINTS_0':
-								case 'JOINT': // JOINT semantic deprecated.
-
-									geometry.addAttribute( 'skinIndex', bufferAttribute );
-									break;
-
-							}
-
-						}
-
-						if ( primitive.indices !== undefined ) {
-
-							geometry.setIndex( dependencies.accessors[ primitive.indices ] );
-
-						}
-
-						if ( material.aoMap
-								&& geometry.attributes.uv2 === undefined
-								&& geometry.attributes.uv !== undefined ) {
-
-							console.log( 'THREE.GLTF2Loader: Duplicating UVs to support aoMap.' );
-							geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
-
-						}
-
-						if ( geometry.attributes.normal === undefined ) {
-
-							if ( material.flatShading !== undefined ) {
-
-								material.flatShading = true;
-
-							} else {
-
-								// TODO: Remove this backwards-compatibility fix after r87 release.
-								material.shading = THREE.FlatShading;
-
-							}
-
-						}
-
-						meshNode = new THREE.Mesh( geometry, material );
-						meshNode.castShadow = true;
-
-						if ( primitive.targets !== undefined ) {
-
-							var targets = primitive.targets;
-							var morphAttributes = geometry.morphAttributes;
-
-							morphAttributes.position = [];
-							morphAttributes.normal = [];
-
-							material.morphTargets = true;
-
-							for ( var i = 0, il = targets.length; i < il; i ++ ) {
-
-								var target = targets[ i ];
-								var attributeName = 'morphTarget' + i;
-
-								var positionAttribute, normalAttribute;
-
-								if ( target.POSITION !== undefined ) {
-
-									// Three.js morph formula is
-									//   position
-									//     + weight0 * ( morphTarget0 - position )
-									//     + weight1 * ( morphTarget1 - position )
-									//     ...
-									// while the glTF one is
-									//   position
-									//     + weight0 * morphTarget0
-									//     + weight1 * morphTarget1
-									//     ...
-									// then adding position to morphTarget.
-									// So morphTarget value will depend on mesh's position, then cloning attribute
-									// for the case if attribute is shared among two or more meshes.
-
-									positionAttribute = dependencies.accessors[ target.POSITION ].clone();
-									var position = geometry.attributes.position;
-
-									for ( var j = 0, jl = positionAttribute.count; j < jl; j ++ ) {
-
-										positionAttribute.setXYZ(
-											j,
-											positionAttribute.getX( j ) + position.getX( j ),
-											positionAttribute.getY( j ) + position.getY( j ),
-											positionAttribute.getZ( j ) + position.getZ( j )
-										);
-
-									}
-
-								} else if ( geometry.attributes.position ) {
-
-									// Copying the original position not to affect the final position.
-									// See the formula above.
-									positionAttribute = geometry.attributes.position.clone();
-
-								}
-
-								if ( target.NORMAL !== undefined ) {
-
-									material.morphNormals = true;
-
-									// see target.POSITION's comment
-
-									normalAttribute = dependencies.accessors[ target.NORMAL ].clone();
-									var normal = geometry.attributes.normal;
-
-									for ( var j = 0, jl = normalAttribute.count; j < jl; j ++ ) {
-
-										normalAttribute.setXYZ(
-											j,
-											normalAttribute.getX( j ) + normal.getX( j ),
-											normalAttribute.getY( j ) + normal.getY( j ),
-											normalAttribute.getZ( j ) + normal.getZ( j )
-										);
-
-									}
-
-								} else if ( geometry.attributes.normal ) {
-
-									normalAttribute = geometry.attributes.normal.clone();
-
-								}
-
-								// TODO: implement
-								if ( target.TANGENT !== undefined ) {
-
-								}
-
-								if ( positionAttribute ) {
-
-									positionAttribute.name = attributeName;
-									morphAttributes.position.push( positionAttribute );
-
-								}
-
-								if ( normalAttribute ) {
-
-									normalAttribute.name = attributeName;
-									morphAttributes.normal.push( normalAttribute );
-
-								}
-
-							}
-
-							meshNode.updateMorphTargets();
-
-							if ( mesh.weights !== undefined ) {
-
-								for ( var i = 0, il = mesh.weights.length; i < il; i ++ ) {
-
-									meshNode.morphTargetInfluences[ i ] = mesh.weights[ i ];
-
-								}
-
-							}
-
-						}
-
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
-
-						geometry = new THREE.BufferGeometry();
-
-						var attributes = primitive.attributes;
-
-						for ( var attributeId in attributes ) {
-
-							var attributeEntry = attributes[ attributeId ];
-
-							if ( ! attributeEntry ) return;
-
-							var bufferAttribute = dependencies.accessors[ attributeEntry ];
-
-							switch ( attributeId ) {
-
-								case 'POSITION':
-									geometry.addAttribute( 'position', bufferAttribute );
-									break;
-
-								case 'COLOR_0':
-								case 'COLOR0':
-								case 'COLOR':
-									geometry.addAttribute( 'color', bufferAttribute );
-									break;
-
-							}
-
-						}
-
-						if ( primitive.indices !== undefined ) {
-
-							geometry.setIndex( dependencies.accessors[ primitive.indices ] );
-
-						}
-
-						meshNode = new THREE.LineSegments( geometry, material );
-
-					} else {
-
-						throw new Error( 'THREE.GLTF2Loader: Only triangular and line primitives are supported.' );
-
-					}
-
-					if ( geometry.attributes.color !== undefined ) {
-
-						material.vertexColors = THREE.VertexColors;
-						material.needsUpdate = true;
-
-					}
-
-					meshNode.name = group.name + '_' + name;
-
-					if ( primitive.extras ) meshNode.userData = primitive.extras;
-
-					group.add( meshNode );
-
-				}
-
-				return group;
-
-			} );
-
-		} );
-
-	};
-
-	/**
-	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
-	 */
-	GLTFParser.prototype.loadCameras = function () {
-
-		var json = this.json;
-
-		return _each( json.cameras, function ( camera ) {
-
-			var _camera;
-
-			var params = camera[ camera.type ];
-
-			if ( !params ) {
-
-				console.warn( 'THREE.GLTF2Loader: Missing camera parameters.' );
-				return;
-
-			}
-
-			if ( camera.type === 'perspective' ) {
-
-				var aspectRatio = params.aspectRatio || 1;
-				var xfov = params.yfov * aspectRatio;
-
-				_camera = new THREE.PerspectiveCamera( THREE.Math.radToDeg( xfov ), aspectRatio, params.znear || 1, params.zfar || 2e6 );
-
-			} else if ( camera.type === 'orthographic' ) {
-
-				_camera = new THREE.OrthographicCamera( params.xmag / -2, params.xmag / 2, params.ymag / 2, params.ymag / -2, params.znear, params.zfar );
-
-			}
-
-			if ( camera.name !== undefined ) _camera.name = camera.name;
-			if ( camera.extras ) _camera.userData = camera.extras;
-
-			return _camera;
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadSkins = function () {
-
-		var json = this.json;
-
-		return this._withDependencies( [
-
-			'accessors'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.skins, function ( skin ) {
-
-				var _skin = {
-					joints: skin.joints,
-					inverseBindMatrices: dependencies.accessors[ skin.inverseBindMatrices ]
-				};
-
-				return _skin;
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadAnimations = function () {
-
-		var json = this.json;
-
-		return this._withDependencies( [
-
-			'accessors',
-			'nodes'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.animations, function ( animation, animationId ) {
-
-				var tracks = [];
-
-				for ( var channelId in animation.channels ) {
-
-					var channel = animation.channels[ channelId ];
-					var sampler = animation.samplers[ channel.sampler ];
-
-					if ( sampler ) {
-
-						var target = channel.target;
-						var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.
-						var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input;
-						var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output;
-
-						var inputAccessor = dependencies.accessors[ input ];
-						var outputAccessor = dependencies.accessors[ output ];
-
-						var node = dependencies.nodes[ name ];
-
-						if ( node ) {
-
-							node.updateMatrix();
-							node.matrixAutoUpdate = true;
-
-							var TypedKeyframeTrack;
-
-							switch ( PATH_PROPERTIES[ target.path ] ) {
-
-								case PATH_PROPERTIES.weights:
-
-									TypedKeyframeTrack = THREE.NumberKeyframeTrack;
-									break;
-
-								case PATH_PROPERTIES.rotation:
-
-									TypedKeyframeTrack = THREE.QuaternionKeyframeTrack;
-									break;
-
-								case PATH_PROPERTIES.position:
-								case PATH_PROPERTIES.scale:
-								default:
-
-									TypedKeyframeTrack = THREE.VectorKeyframeTrack;
-									break;
-
-							}
-
-							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 = [];
-
-							if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
-
-								// node should be THREE.Group here but
-								// PATH_PROPERTIES.weights(morphTargetInfluences) should be
-								// the property of a mesh object under node.
-								// So finding targets here.
-
-								node.traverse( function ( object ) {
-
-									if ( object.isMesh === true && object.material.morphTargets === true ) {
-
-										targetNames.push( object.name ? object.name : object.uuid );
-
-									}
-
-								} );
-
-							} else {
-
-								targetNames.push( targetName );
-
-							}
-
-							// KeyframeTrack.optimize() will modify given 'times' and 'values'
-							// buffers before creating a truncated copy to keep. Because buffers may
-							// be reused by other tracks, make copies here.
-							for ( var i = 0, il = targetNames.length; i < il; i ++ ) {
-
-								tracks.push( new TypedKeyframeTrack(
-									targetNames[ i ] + '.' + PATH_PROPERTIES[ target.path ],
-									THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ),
-									THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ),
-									interpolation
-								) );
-
-							}
-
-						}
-
-					}
-
-				}
-
-				var name = animation.name !== undefined ? animation.name : 'animation_' + animationId;
-
-				return new THREE.AnimationClip( name, undefined, tracks );
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadNodes = function () {
-
-		var json = this.json;
-		var extensions = this.extensions;
-		var scope = this;
-
-		var nodes = json.nodes || [];
-		var skins = json.skins || [];
-
-		// Nothing in the node definition indicates whether it is a Bone or an
-		// Object3D. Use the skins' joint references to mark bones.
-		skins.forEach( function ( skin ) {
-
-			skin.joints.forEach( function ( id ) {
-
-				nodes[ id ].isBone = true;
-
-			} );
-
-		} );
-
-		return _each( json.nodes, function ( node ) {
-
-			var matrix = new THREE.Matrix4();
-
-			var _node = node.isBone === true ? new THREE.Bone() : new THREE.Object3D();
-
-			if ( node.name !== undefined ) {
-
-				_node.name = THREE.PropertyBinding.sanitizeNodeName( node.name );
-
-			}
-
-			if ( node.extras ) _node.userData = node.extras;
-
-			if ( node.matrix !== undefined ) {
-
-				matrix.fromArray( node.matrix );
-				_node.applyMatrix( matrix );
-
-			} else {
-
-				if ( node.translation !== undefined ) {
-
-					_node.position.fromArray( node.translation );
-
-				}
-
-				if ( node.rotation !== undefined ) {
-
-					_node.quaternion.fromArray( node.rotation );
-
-				}
-
-				if ( node.scale !== undefined ) {
-
-					_node.scale.fromArray( node.scale );
-
-				}
-
-			}
-
-			return _node;
-
-		} ).then( function ( __nodes ) {
-
-			return scope._withDependencies( [
-
-				'meshes',
-				'skins',
-				'cameras'
-
-			] ).then( function ( dependencies ) {
-
-				return _each( __nodes, function ( _node, nodeId ) {
-
-					var node = json.nodes[ nodeId ];
-
-					var meshes;
-
-					if ( node.mesh !== undefined) {
-
-						meshes = [ node.mesh ];
-
-					} else if ( node.meshes !== undefined ) {
-
-						console.warn( 'THREE.GLTF2Loader: Legacy glTF file detected. Nodes may have no more than one mesh.' );
-
-						meshes = node.meshes;
-
-					}
-
-					if ( meshes !== undefined ) {
-
-						for ( var meshId in meshes ) {
-
-							var mesh = meshes[ meshId ];
-							var group = dependencies.meshes[ mesh ];
-
-							if ( group === undefined ) {
-
-								console.warn( 'THREE.GLTF2Loader: Could not find node "' + mesh + '".' );
-								continue;
-
-							}
-
-							// do not clone children as they will be replaced anyway
-							var clonedgroup = group.clone( false );
-
-							for ( var childrenId in group.children ) {
-
-								var child = group.children[ childrenId ];
-
-								// clone Mesh to add to _node
-
-								var originalMaterial = child.material;
-								var originalGeometry = child.geometry;
-								var originalInfluences = child.morphTargetInfluences;
-								var originalUserData = child.userData;
-								var originalName = child.name;
-
-								var material;
-
-								if ( originalMaterial.isDeferredShaderMaterial ) {
-
-									originalMaterial = material = originalMaterial.create();
-
-								} else {
-
-									material = originalMaterial;
-
-								}
-
-								switch ( child.type ) {
-
-									case 'LineSegments':
-										child = new THREE.LineSegments( originalGeometry, material );
-										break;
-
-									case 'LineLoop':
-										child = new THREE.LineLoop( originalGeometry, material );
-										break;
-
-									case 'Line':
-										child = new THREE.Line( originalGeometry, material );
-										break;
-
-									default:
-										child = new THREE.Mesh( originalGeometry, material );
-
-								}
-
-								child.castShadow = true;
-								child.morphTargetInfluences = originalInfluences;
-								child.userData = originalUserData;
-								child.name = originalName;
-
-								var skinEntry;
-
-								if ( node.skin !== undefined ) {
-
-									skinEntry = dependencies.skins[ node.skin ];
-
-								}
-
-								// Replace Mesh with SkinnedMesh in library
-								if ( skinEntry ) {
-
-									var geometry = originalGeometry;
-									material = originalMaterial;
-									material.skinning = true;
-
-									child = new THREE.SkinnedMesh( geometry, material );
-									child.castShadow = true;
-									child.userData = originalUserData;
-									child.name = originalName;
-
-									var bones = [];
-									var boneInverses = [];
-
-									for ( var i = 0, l = skinEntry.joints.length; i < l; i ++ ) {
-
-										var jointId = skinEntry.joints[ i ];
-										var jointNode = __nodes[ jointId ];
-
-										if ( jointNode ) {
-
-											bones.push( jointNode );
-
-											var m = skinEntry.inverseBindMatrices.array;
-											var mat = new THREE.Matrix4().fromArray( m, i * 16 );
-											boneInverses.push( mat );
-
-										} else {
-
-											console.warn( 'THREE.GLTF2Loader: Joint "%s" could not be found.', jointId );
-
-										}
-
-									}
-
-									child.bind( new THREE.Skeleton( bones, boneInverses ), child.matrixWorld );
-
-								}
-
-								clonedgroup.add( child );
-
-							}
-
-							_node.add( clonedgroup );
-
-						}
-
-					}
-
-					if ( node.camera !== undefined ) {
-
-						var camera = dependencies.cameras[ node.camera ];
-
-						_node.add( camera );
-
-					}
-
-					if ( node.extensions
-							 && node.extensions[ EXTENSIONS.KHR_LIGHTS ]
-							 && node.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) {
-
-						var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights;
-						_node.add( lights[ node.extensions[ EXTENSIONS.KHR_LIGHTS ].light ] );
-
-					}
-
-					return _node;
-
-				} );
-
-			} );
-
-		} );
-
-	};
-
-	GLTFParser.prototype.loadScenes = function () {
-
-		var json = this.json;
-		var extensions = this.extensions;
-
-		// scene node hierachy builder
-
-		function buildNodeHierachy( nodeId, parentObject, allNodes ) {
-
-			var _node = allNodes[ nodeId ];
-			parentObject.add( _node );
-
-			var node = json.nodes[ nodeId ];
-
-			if ( node.children ) {
-
-				var children = node.children;
-
-				for ( var i = 0, l = children.length; i < l; i ++ ) {
-
-					var child = children[ i ];
-					buildNodeHierachy( child, _node, allNodes );
-
-				}
-
-			}
-
-		}
-
-		return this._withDependencies( [
-
-			'nodes'
-
-		] ).then( function ( dependencies ) {
-
-			return _each( json.scenes, function ( scene ) {
-
-				var _scene = new THREE.Scene();
-				if ( scene.name !== undefined ) _scene.name = scene.name;
-
-				if ( scene.extras ) _scene.userData = scene.extras;
-
-				var nodes = scene.nodes || [];
-
-				for ( var i = 0, l = nodes.length; i < l; i ++ ) {
-
-					var nodeId = nodes[ i ];
-					buildNodeHierachy( nodeId, _scene, dependencies.nodes );
-
-				}
-
-				_scene.traverse( function ( child ) {
-
-					// Register raw material meshes with GLTF2Loader.Shaders
-					if ( child.material && child.material.isRawShaderMaterial ) {
-
-						child.gltfShader = new GLTFShader( child, dependencies.nodes );
-						child.onBeforeRender = function(renderer, scene, camera){
-							this.gltfShader.update(scene, camera);
-						};
-
-					}
-
-					// for Specular-Glossiness.
-					if ( child.material && child.material.isGLTFSpecularGlossinessMaterial ) {
-
-						child.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
-
-					}
-
-				} );
-
-				// Ambient lighting, if present, is always attached to the scene root.
-				if ( scene.extensions
-							 && scene.extensions[ EXTENSIONS.KHR_LIGHTS ]
-							 && scene.extensions[ EXTENSIONS.KHR_LIGHTS ].light !== undefined ) {
-
-					var lights = extensions[ EXTENSIONS.KHR_LIGHTS ].lights;
-					_scene.add( lights[ scene.extensions[ EXTENSIONS.KHR_LIGHTS ].light ] );
-
-				}
-
-				return _scene;
-
-			} );
-
-		} );
-
-	};
-
-	return GLTF2Loader;
-
-} )();

File diff suppressed because it is too large
+ 621 - 255
examples/js/loaders/GLTFLoader.js


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

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

+ 91 - 156
examples/js/loaders/OBJLoader2.js

@@ -15,7 +15,7 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  */
 THREE.OBJLoader2 = (function () {
 
-	var OBJLOADER2_VERSION = '1.3.1';
+	var OBJLOADER2_VERSION = '1.4.1';
 
 	function OBJLoader2( manager ) {
 		console.log( "Using THREE.OBJLoader2 version: " + OBJLOADER2_VERSION );
@@ -189,23 +189,7 @@ THREE.OBJLoader2 = (function () {
 		LINE_VT: 'vt',
 		LINE_VN: 'vn',
 		LINE_MTLLIB: 'mtllib',
-		LINE_USEMTL: 'usemtl',
-		/*
-		 * Build Face/Quad: first element in indexArray is the line identification, therefore offset of one needs to be taken into account
-		 * N-Gons are not supported
-		 * Quad Faces: FaceA: 0, 1, 2  FaceB: 2, 3, 0
-		 *
-		 * 0: "f vertex/uv/normal	vertex/uv/normal	vertex/uv/normal	(vertex/uv/normal)"
-		 * 1: "f vertex/uv		  	vertex/uv		   	vertex/uv		   	(vertex/uv		 )"
-		 * 2: "f vertex//normal	 	vertex//normal	  	vertex//normal	  	(vertex//normal  )"
-		 * 3: "f vertex			 	vertex			  	vertex			  	(vertex		  	 )"
-		 *
-		 * @param indexArray
-		 * @param faceType
-		 */
-		QUAD_INDICES_1: [ 1, 2, 3, 3, 4, 1 ],
-		QUAD_INDICES_2: [ 1, 3, 5, 5, 7, 1 ],
-		QUAD_INDICES_3: [ 1, 4, 7, 7, 10, 1 ]
+		LINE_USEMTL: 'usemtl'
 	};
 
 	var Validator = {
@@ -265,10 +249,9 @@ THREE.OBJLoader2 = (function () {
 		Parser.prototype.parseArrayBuffer = function ( arrayBuffer ) {
 			var arrayBufferView = new Uint8Array( arrayBuffer );
 			var length = arrayBufferView.byteLength;
-			var buffer = new Array( 32 );
+			var buffer = new Array( 128 );
 			var bufferPointer = 0;
-			var slashes = new Array( 32 );
-			var slashesPointer = 0;
+			var slashesCount = 0;
 			var reachedFaces = false;
 			var code;
 			var word = '';
@@ -282,7 +265,7 @@ THREE.OBJLoader2 = (function () {
 						break;
 
 					case Consts.CODE_SLASH:
-						slashes[ slashesPointer++ ] = i;
+						slashesCount++;
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
 						break;
@@ -290,9 +273,9 @@ THREE.OBJLoader2 = (function () {
 					case Consts.CODE_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
-						slashesPointer = 0;
+						reachedFaces = this.processLine( buffer, bufferPointer, slashesCount, reachedFaces );
 						bufferPointer = 0;
+						slashesCount = 0;
 						break;
 
 					case Consts.CODE_CR:
@@ -313,10 +296,9 @@ THREE.OBJLoader2 = (function () {
 		 */
 		Parser.prototype.parseText = function ( text ) {
 			var length = text.length;
-			var buffer = new Array( 32 );
+			var buffer = new Array( 128 );
 			var bufferPointer = 0;
-			var slashes = new Array( 32 );
-			var slashesPointer = 0;
+			var slashesCount = 0;
 			var reachedFaces = false;
 			var char;
 			var word = '';
@@ -330,7 +312,7 @@ THREE.OBJLoader2 = (function () {
 						break;
 
 					case Consts.STRING_SLASH:
-						slashes[ slashesPointer++ ] = i;
+						slashesCount++;
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
 						break;
@@ -338,9 +320,9 @@ THREE.OBJLoader2 = (function () {
 					case Consts.STRING_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashes, slashesPointer, reachedFaces );
-						slashesPointer = 0;
+						reachedFaces = this.processLine( buffer, bufferPointer, slashesCount, reachedFaces );
 						bufferPointer = 0;
+						slashesCount = 0;
 						break;
 
 					case Consts.STRING_CR:
@@ -352,7 +334,7 @@ THREE.OBJLoader2 = (function () {
 			}
 		};
 
-		Parser.prototype.processLine = function ( buffer, bufferPointer, slashes, slashesPointer, reachedFaces ) {
+		Parser.prototype.processLine = function ( buffer, bufferPointer, slashesCount, reachedFaces ) {
 			if ( bufferPointer < 1 ) return reachedFaces;
 
 			var bufferLength = bufferPointer - 1;
@@ -393,50 +375,11 @@ THREE.OBJLoader2 = (function () {
 
 				case Consts.LINE_F:
 					reachedFaces = true;
-					/*
-					 * 0: "f vertex/uv/normal ..."
-					 * 1: "f vertex/uv ..."
-					 * 2: "f vertex//normal ..."
-					 * 3: "f vertex ..."
-					 */
-					var haveQuad = bufferLength % 4 === 0;
-					if ( slashesPointer > 1 && ( slashes[ 1 ] - slashes[ 0 ] ) === 1 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVn( buffer );
-						} else {
-							this.rawObject.buildFaceVVn( buffer );
-						}
-
-					} else if ( bufferLength === slashesPointer * 2 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVt( buffer );
-						} else {
-							this.rawObject.buildFaceVVt( buffer );
-						}
-
-					} else if ( bufferLength * 2 === slashesPointer * 3 ) {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadVVtVn( buffer );
-						} else {
-							this.rawObject.buildFaceVVtVn( buffer );
-						}
-
-					} else {
-
-						if ( haveQuad ) {
-							this.rawObject.buildQuadV( buffer );
-						} else {
-							this.rawObject.buildFaceV( buffer );
-						}
-
-					}
+					this.rawObject.processFaces( buffer, bufferPointer, slashesCount );
 					break;
 
 				case Consts.LINE_L:
-					if ( bufferLength === slashesPointer * 2 ) {
+					if ( bufferLength === slashesCount * 2 ) {
 
 						this.rawObject.buildLineVvt( buffer );
 
@@ -533,7 +476,7 @@ THREE.OBJLoader2 = (function () {
 	 */
 	var RawObject = (function () {
 
-		function RawObject( objectName, groupName, mtllibName ) {
+		function RawObject( objectName, groupName, activeMtlName ) {
 			this.globalVertexOffset = 1;
 			this.globalUvOffset = 1;
 			this.globalNormalOffset = 1;
@@ -544,10 +487,9 @@ THREE.OBJLoader2 = (function () {
 			this.uvs = [];
 
 			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
-			this.mtllibName = Validator.verifyInput( mtllibName, '' );
+			this.activeMtlName = Validator.verifyInput( activeMtlName, '' );
 			this.objectName = Validator.verifyInput( objectName, '' );
 			this.groupName = Validator.verifyInput( groupName, '' );
-			this.activeMtlName = '';
 			this.activeSmoothingGroup = 1;
 
 			this.mtlCount = 0;
@@ -565,7 +507,7 @@ THREE.OBJLoader2 = (function () {
 		};
 
 		RawObject.prototype.newInstanceFromObject = function ( objectName, groupName ) {
-			var newRawObject = new RawObject( objectName, groupName, this.mtllibName );
+			var newRawObject = new RawObject( objectName, groupName, this.activeMtlName );
 
 			// move indices forward
 			newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3;
@@ -576,7 +518,7 @@ THREE.OBJLoader2 = (function () {
 		};
 
 		RawObject.prototype.newInstanceFromGroup = function ( groupName ) {
-			var newRawObject = new RawObject( this.objectName, groupName, this.mtllibName );
+			var newRawObject = new RawObject( this.objectName, groupName, this.activeMtlName );
 
 			// keep current buffers and indices forward
 			newRawObject.vertices = this.vertices;
@@ -660,98 +602,92 @@ THREE.OBJLoader2 = (function () {
 			}
 		};
 
-		RawObject.prototype.buildQuadVVtVn = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_3[ i ] ] );
-				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_3[ i ] + 1 ] );
-				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_3[ i ] + 2 ] );
-			}
-		};
+		RawObject.prototype.processFaces = function ( buffer, bufferPointer, slashesCount ) {
+			var bufferLength = bufferPointer - 1;
+			var i;
 
-		RawObject.prototype.buildQuadVVt = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
-				this.attachFaceVt( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
-			}
-		};
+			// "f vertex ..."
+			if ( slashesCount === 0 ) {
 
-		RawObject.prototype.buildQuadVVn = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_2[ i ] ] );
-				this.attachFaceVn( indexArray[ Consts.QUAD_INDICES_2[ i ] + 1 ] );
-			}
-		};
+				for ( i = 2; i < bufferLength - 1; i ++ ) {
 
-		RawObject.prototype.buildQuadV = function ( indexArray ) {
-			for ( var i = 0; i < 6; i ++ ) {
-				this.attachFaceV_( indexArray[ Consts.QUAD_INDICES_1[ i ] ] );
-			}
-		};
+					this.attachFace( buffer[ 1     ] );
+					this.attachFace( buffer[ i     ] );
+					this.attachFace( buffer[ i + 1 ] );
 
-		RawObject.prototype.buildFaceVVtVn = function ( indexArray ) {
-			for ( var i = 1; i < 10; i += 3 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVt( indexArray[ i + 1 ] );
-				this.attachFaceVn( indexArray[ i + 2 ] );
-			}
-		};
+				}
 
-		RawObject.prototype.buildFaceVVt = function ( indexArray ) {
-			for ( var i = 1; i < 7; i += 2 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVt( indexArray[ i + 1 ] );
-			}
-		};
+			// "f vertex/uv ..."
+			} else if  ( bufferLength === slashesCount * 2 ) {
 
-		RawObject.prototype.buildFaceVVn = function ( indexArray ) {
-			for ( var i = 1; i < 7; i += 2 ) {
-				this.attachFaceV_( indexArray[ i ] );
-				this.attachFaceVn( indexArray[ i + 1 ] );
-			}
-		};
+				for ( i = 3; i < bufferLength - 2; i += 2 ) {
+
+					this.attachFace( buffer[ 1     ], buffer[ 2     ] );
+					this.attachFace( buffer[ i     ], buffer[ i + 1 ] );
+					this.attachFace( buffer[ i + 2 ], buffer[ i + 3 ] );
+
+				}
+
+			// "f vertex/uv/normal ..."
+			} else if  ( bufferLength * 2 === slashesCount * 3 ) {
+
+				for ( i = 4; i < bufferLength - 3; i += 3 ) {
+
+					this.attachFace( buffer[ 1     ], buffer[ 2     ], buffer[ 3     ] );
+					this.attachFace( buffer[ i     ], buffer[ i + 1 ], buffer[ i + 2 ] );
+					this.attachFace( buffer[ i + 3 ], buffer[ i + 4 ], buffer[ i + 5 ] );
+
+				}
+
+			// "f vertex//normal ..."
+			} else {
+
+				for ( i = 3; i < bufferLength - 2; i += 2 ) {
+
+					this.attachFace( buffer[ 1     ], undefined, buffer[ 2     ] );
+					this.attachFace( buffer[ i     ], undefined, buffer[ i + 1 ] );
+					this.attachFace( buffer[ i + 2 ], undefined, buffer[ i + 3 ] );
+
+				}
 
-		RawObject.prototype.buildFaceV = function ( indexArray ) {
-			for ( var i = 1; i < 4; i ++ ) {
-				this.attachFaceV_( indexArray[ i ] );
 			}
 		};
 
-		RawObject.prototype.attachFaceV_ = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalVertexOffset ) * 3;
-
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.vertices.push( this.vertices[ index++ ] );
-			rodiu.vertices.push( this.vertices[ index++ ] );
-			rodiu.vertices.push( this.vertices[ index ] );
+		RawObject.prototype.attachFace = function ( faceIndexV, faceIndexU, faceIndexN ) {
+			var indexV = ( parseInt( faceIndexV ) - this.globalVertexOffset ) * 3;
+			var vertices = this.rawObjectDescriptionInUse.vertices;
+			vertices.push( this.vertices[ indexV ++ ] );
+			vertices.push( this.vertices[ indexV ++ ] );
+			vertices.push( this.vertices[ indexV ] );
 
 			if ( this.colors.length > 0 ) {
 
-				index -= 2;
-				rodiu.colors.push( this.colors[ index++ ] );
-				rodiu.colors.push( this.colors[ index++ ] );
-				rodiu.colors.push( this.colors[ index ] );
+				indexV -= 2;
+				var colors = this.rawObjectDescriptionInUse.colors;
+				colors.push( this.colors[ indexV ++ ] );
+				colors.push( this.colors[ indexV ++ ] );
+				colors.push( this.colors[ indexV ] );
 
 			}
-		};
 
-		RawObject.prototype.attachFaceVt = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalUvOffset ) * 2;
+			if ( faceIndexU ) {
 
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.uvs.push( this.uvs[ index++ ] );
-			rodiu.uvs.push( this.uvs[ index ] );
-		};
+				var indexU = ( parseInt( faceIndexU ) - this.globalUvOffset ) * 2;
+				var uvs = this.rawObjectDescriptionInUse.uvs;
+				uvs.push( this.uvs[ indexU ++ ] );
+				uvs.push( this.uvs[ indexU ] );
+
+			}
 
-		RawObject.prototype.attachFaceVn = function ( faceIndex ) {
-			var faceIndexInt = parseInt( faceIndex );
-			var index = ( faceIndexInt - this.globalNormalOffset ) * 3;
+			if ( faceIndexN ) {
 
-			var rodiu = this.rawObjectDescriptionInUse;
-			rodiu.normals.push( this.normals[ index++ ] );
-			rodiu.normals.push( this.normals[ index++ ] );
-			rodiu.normals.push( this.normals[ index ] );
+				var indexN = ( parseInt( faceIndexN ) - this.globalNormalOffset ) * 3;
+				var normals = this.rawObjectDescriptionInUse.normals;
+				normals.push( this.normals[ indexN ++ ] );
+				normals.push( this.normals[ indexN ++ ] );
+				normals.push( this.normals[ indexN ] );
+
+			}
 		};
 
 		/*
@@ -778,8 +714,7 @@ THREE.OBJLoader2 = (function () {
 		 * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts
 		 */
 		RawObject.prototype.finalize = function ( meshCreator, inputObjectCount, debug ) {
-			var temp = this.rawObjectDescriptions;
-			this.rawObjectDescriptions = [];
+			var temp = [];
 			var rawObjectDescription;
 			var index = 0;
 			var absoluteVertexCount = 0;
@@ -787,12 +722,12 @@ THREE.OBJLoader2 = (function () {
 			var absoluteNormalCount = 0;
 			var absoluteUvCount = 0;
 
-			for ( var name in temp ) {
+			for ( var name in this.rawObjectDescriptions ) {
 
-				rawObjectDescription = temp[ name ];
+				rawObjectDescription = this.rawObjectDescriptions[ name ];
 				if ( rawObjectDescription.vertices.length > 0 ) {
 
-					this.rawObjectDescriptions[ index++ ] = rawObjectDescription;
+					temp[ index++ ] = rawObjectDescription;
 					absoluteVertexCount += rawObjectDescription.vertices.length;
 					absoluteColorCount += rawObjectDescription.colors.length;
 					absoluteUvCount += rawObjectDescription.uvs.length;
@@ -807,7 +742,7 @@ THREE.OBJLoader2 = (function () {
 
 				if ( debug ) this.createReport( inputObjectCount, true );
 				meshCreator.buildMesh(
-					this.rawObjectDescriptions,
+					temp,
 					inputObjectCount,
 					absoluteVertexCount,
 					absoluteColorCount,

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

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

+ 310 - 70
examples/js/loaders/TDSLoader.js

@@ -1,10 +1,12 @@
 /*
  * Autodesk 3DS threee.js file loader, based on lib3ds.
  *
- * Loads geometry with uv and materials basic properties.
+ * Loads geometry with uv and materials basic properties with texture support.
  *
  * @author @tentone
  * @author @timknip
+ * @class TDSLoader
+ * @constructor
  */
 
 'use strict';
@@ -20,10 +22,23 @@ THREE.TDSLoader = function ( manager ) {
 	this.materials = [];
 	this.meshes = [];
 
+	this.path = "";
+
 };
 
 THREE.TDSLoader.prototype = {
 
+	constructor: THREE.TDSLoader,
+	
+	/**
+	 * Load 3ds file from url.
+	 *
+	 * @method load
+	 * @param {[type]} url URL for the file.
+	 * @param {Function} onLoad onLoad callback, receives group Object3D as argument.
+	 * @param {Function} onProgress onProgress callback.
+	 * @param {Function} onError onError callback.
+	 */
 	load : function ( url, onLoad, onProgress, onError ) {
 
 		var scope = this;
@@ -40,6 +55,14 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Parse arraybuffer data and load 3ds file.
+	 *
+	 * @method parse
+	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
+	 * @param {String} path Path for external resources.
+	 * @return {Object3D} Group loaded from 3ds file.
+	 */
 	parse : function ( arraybuffer ) {
 
 		this.group = new THREE.Group();
@@ -59,6 +82,12 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Decode file content to read 3ds data.
+	 *
+	 * @method readFile
+	 * @param {ArrayBuffer} arraybuffer Arraybuffer data to be loaded.
+	 */
 	readFile : function ( arraybuffer ) {
 
 		var data = new DataView( arraybuffer );
@@ -96,6 +125,12 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read mesh data chunk.
+	 *
+	 * @method readMeshData
+	 * @param {Dataview} data Dataview in use.
+	 */
 	readMeshData : function ( data ) {
 
 		var chunk = this.readChunk( data );
@@ -138,6 +173,48 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read named object chunk.
+	 *
+	 * @method readNamedObject
+	 * @param {Dataview} data Dataview in use.
+	 */
+	readNamedObject : function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var name = this.readString( data, 64 );
+		chunk.cur = this.position;
+
+		var next = this.nextChunk( data, chunk );
+		while ( next !== 0 ) {
+
+			if ( next === N_TRI_OBJECT ) {
+
+				this.resetPosition( data );
+				var mesh = this.readMesh( data );
+				mesh.name = name;
+				this.meshes.push( mesh );
+
+			} else {
+
+				this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
+
+			}
+
+			next = this.nextChunk( data, chunk );
+
+		}
+
+		this.endChunk( chunk );
+
+	},
+
+	/**
+	 * Read material data chunk and add it to the material list.
+	 *
+	 * @method readMaterialEntry
+	 * @param {Dataview} data Dataview in use.
+	 */
 	readMaterialEntry : function ( data ) {
 
 		var chunk = this.readChunk( data );
@@ -193,58 +270,52 @@ THREE.TDSLoader.prototype = {
 				material.shininess = shininess;
 				this.debugMessage( '   Shininess : ' + shininess );
 
-			} else {
+			} else if( next === MAT_TEXMAP ) {
 
-				this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
-
-			}
-
-			next = this.nextChunk( data, chunk );
-
-		}
-
-		this.endChunk( chunk );
-
-		this.materials[ material.name ] = material;
-
-	},
-
-	readColor : function ( data ) {
-
-		var chunk = this.readChunk( data );
-		var color = new THREE.Color();
+				this.debugMessage( '   ColorMap' );
+				this.resetPosition( data );
+				material.map = this.readMap( data );
 
-		if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
+			} else if( next === MAT_BUMPMAP ) {
 
-			var r = this.readByte( data );
-			var g = this.readByte( data );
-			var b = this.readByte( data );
+				this.debugMessage( '   BumpMap' );
+				this.resetPosition( data );
+				material.bumpMap = this.readMap( data );
 
-			color.setRGB( r / 255, g / 255, b / 255 );
+			} else if( next == MAT_OPACMAP ) {
 
-			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
+				this.debugMessage( '   OpacityMap' );
+				this.resetPosition( data );
+				material.alphaMap = this.readMap( data );
 
-		}	else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
+			} else if( next == MAT_SPECMAP ) {
 
-			var r = this.readFloat( data );
-			var g = this.readFloat( data );
-			var b = this.readFloat( data );
+				this.debugMessage( '   SpecularMap' );
+				this.resetPosition( data );
+				material.specularMap = this.readMap( data );
 
-			color.setRGB( r, g, b );
+			} else {
 
-			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
+				this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
 
-		}	else {
+			}
 
-			this.debugMessage( '      Unknown color chunk: ' + c.toString( 16 ) );
+			next = this.nextChunk( data, chunk );
 
 		}
 
 		this.endChunk( chunk );
-		return color;
+
+		this.materials[ material.name ] = material;
 
 	},
 
+	/**
+	 * Read mesh data chunk.
+	 *
+	 * @method readMesh
+	 * @param {Dataview} data Dataview in use.
+	 */
 	readMesh : function ( data ) {
 
 		var chunk = this.readChunk( data );
@@ -350,28 +421,28 @@ THREE.TDSLoader.prototype = {
 				var matrix = new THREE.Matrix4();
 
 				//X Line
-				matrix.elements[ 0 ] = values[ 0 ];
-				matrix.elements[ 1 ] = values[ 6 ];
-				matrix.elements[ 2 ] = values[ 3 ];
-				matrix.elements[ 3 ] = values[ 9 ];
+				matrix.elements[0] = values[0];
+				matrix.elements[1] = values[6];
+				matrix.elements[2] = values[3];
+				matrix.elements[3] = values[9];
 
 				//Y Line
-				matrix.elements[ 4 ] = -values[ 2 ];
-				matrix.elements[ 5 ] = -values[ 8 ];
-				matrix.elements[ 6 ] = -values[ 5 ];
-				matrix.elements[ 7 ] = -values[ 11 ];
+				matrix.elements[4] = values[2];
+				matrix.elements[5] = values[8];
+				matrix.elements[6] = values[5];
+				matrix.elements[7] = values[11];
 
 				//Z Line
-				matrix.elements[ 8 ] = values[ 1 ];
-				matrix.elements[ 9 ] = values[ 7 ];
-				matrix.elements[ 10 ] = values[ 4 ];
-				matrix.elements[ 11 ] = -values[ 10 ];
+				matrix.elements[8] = values[1];
+				matrix.elements[9] = values[7];
+				matrix.elements[10] = values[4];
+				matrix.elements[11] = values[10];
 
 				//W Line
-				matrix.elements[ 12 ] = 0;
-				matrix.elements[ 13 ] = 0;
-				matrix.elements[ 14 ] = 0;
-				matrix.elements[ 15 ] = 1;
+				matrix.elements[12] = 0;
+				matrix.elements[13] = 0;
+				matrix.elements[14] = 0;
+				matrix.elements[15] = 1;
 
 				matrix.transpose();
 
@@ -419,6 +490,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read face array data chunk.
+	 *
+	 * @method readFaceArray
+	 * @param {Dataview} data Dataview in use.
+	 * @param {Mesh} mesh Mesh to be filled with the data read.
+	 */
 	readFaceArray : function ( data, mesh ) {
 
 		var chunk = this.readChunk( data );
@@ -476,21 +554,59 @@ THREE.TDSLoader.prototype = {
 
 	},
 
-	readMap : function ( data ) {
+	/**
+	 * Read texture map data chunk.
+	 *
+	 * @method readMap
+	 * @param {Dataview} data Dataview in use.
+	 * @return {Texture} Texture read from this data chunk.
+	 */
+	readMap : function( data ) {
 
 		var chunk = this.readChunk( data );
 		var next = this.nextChunk( data, chunk );
+		var texture = {};
 
-		while ( next !== 0 ) {
+		var loader = new THREE.TextureLoader();
+		loader.setPath( this.path );
+
+		while( next !== 0 ) {
 
 			if ( next === MAT_MAPNAME ) {
 
 				var name = this.readString( data, 128 );
-				this.debugMessage( '      MapName: ' + name );
+				texture = loader.load( name );
 
-			} else {
+				this.debugMessage( '      File: ' + this.path + name );
+
+			}
+			else if ( next === MAT_MAP_UOFFSET) {
+
+				texture.offset.x = this.readFloat( data );
+				this.debugMessage( '      OffsetX: ' + texture.offset.x );
+
+			}
+			else if ( next === MAT_MAP_VOFFSET) {
 
-				this.debugMessage( '      Unknown named object chunk: ' + next.toString( 16 ) );
+				texture.offset.y = this.readFloat( data );
+				this.debugMessage( '      OffsetY: ' + texture.offset.y );
+
+			}
+			else if ( next === MAT_MAP_USCALE) {
+
+				texture.repeat.x = this.readFloat( data );
+				this.debugMessage( '      RepeatX: ' + texture.repeat.x );
+
+			}
+			else if ( next === MAT_MAP_VSCALE) {
+
+				texture.repeat.y = this.readFloat( data );
+				this.debugMessage( '      RepeatY: ' + texture.repeat.y );
+
+			}
+			else {
+
+				this.debugMessage( '      Unknown map chunk: ' + next.toString( 16 ) );
 
 			}
 
@@ -500,8 +616,17 @@ THREE.TDSLoader.prototype = {
 
 		this.endChunk( chunk );
 
+		return texture;
+
 	},
 
+	/**
+	 * Read material group data chunk.
+	 *
+	 * @method readMaterialGroup
+	 * @param {Dataview} data Dataview in use.
+	 * @return {Object} Object with name and index of the object.
+	 */
 	readMaterialGroup : function ( data ) {
 
 		var chunk = this.readChunk( data );
@@ -522,36 +647,56 @@ THREE.TDSLoader.prototype = {
 
 	},
 
-	readNamedObject : function ( data ) {
+	/**
+	 * Read a color value.
+	 *
+	 * @method readColor
+	 * @param {DataView} data Dataview.
+	 * @return {Color} Color value read..
+	 */
+	readColor : function ( data ) {
 
 		var chunk = this.readChunk( data );
-		var name = this.readString( data, 64 );
-		chunk.cur = this.position;
+		var color = new THREE.Color();
 
-		var next = this.nextChunk( data, chunk );
-		while ( next !== 0 ) {
+		if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
 
-			if ( next === N_TRI_OBJECT ) {
+			var r = this.readByte( data );
+			var g = this.readByte( data );
+			var b = this.readByte( data );
 
-				this.resetPosition( data );
-				var mesh = this.readMesh( data );
-				mesh.name = name;
-				this.meshes.push( mesh );
+			color.setRGB( r / 255, g / 255, b / 255 );
 
-			} else {
+			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 
-				this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
+		}	else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
 
-			}
+			var r = this.readFloat( data );
+			var g = this.readFloat( data );
+			var b = this.readFloat( data );
 
-			next = this.nextChunk( data, chunk );
+			color.setRGB( r, g, b );
+
+			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
+
+		}	else {
+
+			this.debugMessage( '      Unknown color chunk: ' + c.toString( 16 ) );
 
 		}
 
 		this.endChunk( chunk );
+		return color;
 
 	},
 
+	/**
+	 * Read next chunk of data.
+	 *
+	 * @method readChunk
+	 * @param {DataView} data Dataview.
+	 * @return {Object} Chunk of data read.
+	 */
 	readChunk : function ( data ) {
 
 		var chunk = {};
@@ -566,12 +711,25 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Set position to the end of the current chunk of data.
+	 *
+	 * @method endChunk
+	 * @param {Object} chunk Data chunk.
+	 */
 	endChunk : function ( chunk ) {
 
 		this.position = chunk.end;
 
 	},
 
+	/**
+	 * Move to the next data chunk.
+	 *
+	 * @method nextChunk
+	 * @param {DataView} data Dataview.
+	 * @param {Object} chunk Data chunk.
+	 */
 	nextChunk : function ( data, chunk ) {
 
 		if ( chunk.cur >= chunk.end ) {
@@ -597,12 +755,25 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Reset dataview position.
+	 *
+	 * @method resetPosition
+	 * @param {DataView} data Dataview.
+	 */
 	resetPosition : function ( data, chunk ) {
 
 		this.position -= 6;
 
 	},
 
+	/**
+	 * Read byte value.
+	 *
+	 * @method readByte
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readByte : function ( data ) {
 
 		var v = data.getUint8( this.position, true );
@@ -611,6 +782,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read 32 bit float value.
+	 *
+	 * @method readFloat
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readFloat : function ( data ) {
 
 		try {
@@ -627,6 +805,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read 32 bit signed integer value.
+	 *
+	 * @method readInt
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readInt : function ( data ) {
 
 		var v = data.getInt32( this.position, true );
@@ -635,6 +820,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read 16 bit signed integer value.
+	 *
+	 * @method readShort
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readShort : function ( data ) {
 
 		var v = data.getInt16( this.position, true );
@@ -643,6 +835,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read 64 bit unsigned integer value.
+	 *
+	 * @method readDWord
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readDWord : function ( data ) {
 
 		var v = data.getUint32( this.position, true );
@@ -651,6 +850,13 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read 32 bit unsigned integer value.
+	 *
+	 * @method readWord
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
 	readWord : function ( data ) {
 
 		var v = data.getUint16( this.position, true );
@@ -659,6 +865,14 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Read string value.
+	 *
+	 * @method readString
+	 * @param {DataView} data Dataview to read data from.
+	 * @param {Number} maxLength Max size of the string to be read.
+	 * @return {String} Data read from the dataview.
+	 */
 	readString : function ( data, maxLength ) {
 
 		var s = '';
@@ -680,6 +894,32 @@ THREE.TDSLoader.prototype = {
 
 	},
 
+	/**
+	 * Set resource path used to determine the file path to attached resources.
+	 *
+	 * @method setPath
+	 * @param {String} path Path to resources.
+	 * @return Self for chaining.
+	 */
+	setPath : function ( path ) {
+
+		if(path !== undefined)
+		{
+			this.path = path;
+		}
+
+		return this;
+
+	},
+
+	/**
+	 * Print debug message to the console.
+	 *
+	 * Is controlled by a flag to show or hide debug messages.
+	 *
+	 * @method debugMessage
+	 * @param {Object} message Debug message to print to the console.
+	 */
 	debugMessage : function ( message ) {
 
 		if ( this.debug ) {

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

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

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

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

+ 0 - 73
examples/js/shaders/EdgeShader2.js

@@ -1,73 +0,0 @@
-/**
- * @author zz85 / https://github.com/zz85 | https://www.lab4games.net/zz85/blog
- *
- * Edge Detection Shader using Sobel filter
- * Based on http://rastergrid.com/blog/2011/01/frei-chen-edge-detector
- *
- * aspect: vec2 of (1/width, 1/height)
- */
-
-THREE.EdgeShader2 = {
-
-	uniforms: {
-
-		"tDiffuse": { value: null },
-		"aspect":    { value: new THREE.Vector2( 512, 512 ) }
-	},
-
-	vertexShader: [
-
-		"varying vec2 vUv;",
-
-		"void main() {",
-
-			"vUv = uv;",
-			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
-
-		"}"
-
-	].join( "\n" ),
-
-	fragmentShader: [
-
-		"uniform sampler2D tDiffuse;",
-		"varying vec2 vUv;",
-		"uniform vec2 aspect;",
-
-
-		"vec2 texel = vec2(1.0 / aspect.x, 1.0 / aspect.y);",
-
-		"mat3 G[2];",
-
-		"const mat3 g0 = mat3( 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0 );",
-		"const mat3 g1 = mat3( 1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0 );",
-
-
-		"void main(void)",
-		"{",
-			"mat3 I;",
-			"float cnv[2];",
-			"vec3 sample;",
-
-			"G[0] = g0;",
-			"G[1] = g1;",
-
-			/* fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value */
-			"for (float i=0.0; i<3.0; i++)",
-			"for (float j=0.0; j<3.0; j++) {",
-				"sample = texture2D( tDiffuse, vUv + texel * vec2(i-1.0,j-1.0) ).rgb;",
-				"I[int(i)][int(j)] = length(sample);",
-			"}",
-
-			/* calculate the convolution values for all the masks */
-			"for (int i=0; i<2; i++) {",
-				"float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]);",
-				"cnv[i] = dp3 * dp3; ",
-			"}",
-
-			"gl_FragColor = vec4(0.5 * sqrt(cnv[0]*cnv[0]+cnv[1]*cnv[1]));",
-		"} "
-
-	].join( "\n" )
-
-};

+ 1083 - 56
examples/js/shaders/FXAAShader.js

@@ -19,8 +19,11 @@ THREE.FXAAShader = {
 
 	vertexShader: [
 
+		"varying vec2 vUv;",
+
 		"void main() {",
 
+			"vUv = uv;",
 			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
 
 		"}"
@@ -28,61 +31,1085 @@ THREE.FXAAShader = {
 	].join( "\n" ),
 
 	fragmentShader: [
-
-		"uniform sampler2D tDiffuse;",
-		"uniform vec2 resolution;",
-
-		"#define FXAA_REDUCE_MIN   (1.0/128.0)",
-		"#define FXAA_REDUCE_MUL   (1.0/8.0)",
-		"#define FXAA_SPAN_MAX     8.0",
-
-		"void main() {",
-
-			"vec3 rgbNW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, -1.0 ) ) * resolution ).xyz;",
-			"vec3 rgbNE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, -1.0 ) ) * resolution ).xyz;",
-			"vec3 rgbSW = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( -1.0, 1.0 ) ) * resolution ).xyz;",
-			"vec3 rgbSE = texture2D( tDiffuse, ( gl_FragCoord.xy + vec2( 1.0, 1.0 ) ) * resolution ).xyz;",
-			"vec4 rgbaM  = texture2D( tDiffuse,  gl_FragCoord.xy  * resolution );",
-			"vec3 rgbM  = rgbaM.xyz;",
-			"vec3 luma = vec3( 0.299, 0.587, 0.114 );",
-
-			"float lumaNW = dot( rgbNW, luma );",
-			"float lumaNE = dot( rgbNE, luma );",
-			"float lumaSW = dot( rgbSW, luma );",
-			"float lumaSE = dot( rgbSE, luma );",
-			"float lumaM  = dot( rgbM,  luma );",
-			"float lumaMin = min( lumaM, min( min( lumaNW, lumaNE ), min( lumaSW, lumaSE ) ) );",
-			"float lumaMax = max( lumaM, max( max( lumaNW, lumaNE) , max( lumaSW, lumaSE ) ) );",
-
-			"vec2 dir;",
-			"dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));",
-			"dir.y =  ((lumaNW + lumaSW) - (lumaNE + lumaSE));",
-
-			"float dirReduce = max( ( lumaNW + lumaNE + lumaSW + lumaSE ) * ( 0.25 * FXAA_REDUCE_MUL ), FXAA_REDUCE_MIN );",
-
-			"float rcpDirMin = 1.0 / ( min( abs( dir.x ), abs( dir.y ) ) + dirReduce );",
-			"dir = min( vec2( FXAA_SPAN_MAX,  FXAA_SPAN_MAX),",
-				  "max( vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX),",
-						"dir * rcpDirMin)) * resolution;",
-			"vec4 rgbA = (1.0/2.0) * (",
-        	"texture2D(tDiffuse,  gl_FragCoord.xy  * resolution + dir * (1.0/3.0 - 0.5)) +",
-			"texture2D(tDiffuse,  gl_FragCoord.xy  * resolution + dir * (2.0/3.0 - 0.5)));",
-    		"vec4 rgbB = rgbA * (1.0/2.0) + (1.0/4.0) * (",
-			"texture2D(tDiffuse,  gl_FragCoord.xy  * resolution + dir * (0.0/3.0 - 0.5)) +",
-      		"texture2D(tDiffuse,  gl_FragCoord.xy  * resolution + dir * (3.0/3.0 - 0.5)));",
-    		"float lumaB = dot(rgbB, vec4(luma, 0.0));",
-
-			"if ( ( lumaB < lumaMin ) || ( lumaB > lumaMax ) ) {",
-
-				"gl_FragColor = rgbA;",
-
-			"} else {",
-				"gl_FragColor = rgbB;",
-
-			"}",
-
-		"}"
-
-	].join( "\n" )
+        "precision highp float;",
+        "",
+        "uniform sampler2D tDiffuse;",
+        "",
+        "uniform vec2 resolution;",
+        "",
+        "varying vec2 vUv;",
+        "",
+        "// FXAA 3.11 implementation by NVIDIA, ported to WebGL by Agost Biro ([email protected])",
+        "",
+        "//----------------------------------------------------------------------------------",
+        "// File:        es3-kepler\FXAA\assets\shaders/FXAA_DefaultES.frag",
+        "// SDK Version: v3.00",
+        "// Email:       [email protected]",
+        "// Site:        http://developer.nvidia.com/",
+        "//",
+        "// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.",
+        "//",
+        "// Redistribution and use in source and binary forms, with or without",
+        "// modification, are permitted provided that the following conditions",
+        "// are met:",
+        "//  * Redistributions of source code must retain the above copyright",
+        "//    notice, this list of conditions and the following disclaimer.",
+        "//  * Redistributions in binary form must reproduce the above copyright",
+        "//    notice, this list of conditions and the following disclaimer in the",
+        "//    documentation and/or other materials provided with the distribution.",
+        "//  * Neither the name of NVIDIA CORPORATION nor the names of its",
+        "//    contributors may be used to endorse or promote products derived",
+        "//    from this software without specific prior written permission.",
+        "//",
+        "// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY",
+        "// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE",
+        "// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR",
+        "// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR",
+        "// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,",
+        "// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,",
+        "// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR",
+        "// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY",
+        "// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT",
+        "// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE",
+        "// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
+        "//",
+        "//----------------------------------------------------------------------------------",
+        "",
+        "#define FXAA_PC 1",
+        "#define FXAA_GLSL_100 1",
+        "#define FXAA_QUALITY_PRESET 12",
+        "",
+        "#define FXAA_GREEN_AS_LUMA 1",
+        "",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_PC_CONSOLE",
+        "    //",
+        "    // The console algorithm for PC is included",
+        "    // for developers targeting really low spec machines.",
+        "    // Likely better to just run FXAA_PC, and use a really low preset.",
+        "    //",
+        "    #define FXAA_PC_CONSOLE 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_GLSL_120",
+        "    #define FXAA_GLSL_120 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_GLSL_130",
+        "    #define FXAA_GLSL_130 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_HLSL_3",
+        "    #define FXAA_HLSL_3 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_HLSL_4",
+        "    #define FXAA_HLSL_4 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_HLSL_5",
+        "    #define FXAA_HLSL_5 0",
+        "#endif",
+        "/*==========================================================================*/",
+        "#ifndef FXAA_GREEN_AS_LUMA",
+        "    //",
+        "    // For those using non-linear color,",
+        "    // and either not able to get luma in alpha, or not wanting to,",
+        "    // this enables FXAA to run using green as a proxy for luma.",
+        "    // So with this enabled, no need to pack luma in alpha.",
+        "    //",
+        "    // This will turn off AA on anything which lacks some amount of green.",
+        "    // Pure red and blue or combination of only R and B, will get no AA.",
+        "    //",
+        "    // Might want to lower the settings for both,",
+        "    //    fxaaConsoleEdgeThresholdMin",
+        "    //    fxaaQualityEdgeThresholdMin",
+        "    // In order to insure AA does not get turned off on colors",
+        "    // which contain a minor amount of green.",
+        "    //",
+        "    // 1 = On.",
+        "    // 0 = Off.",
+        "    //",
+        "    #define FXAA_GREEN_AS_LUMA 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_EARLY_EXIT",
+        "    //",
+        "    // Controls algorithm's early exit path.",
+        "    // On PS3 turning this ON adds 2 cycles to the shader.",
+        "    // On 360 turning this OFF adds 10ths of a millisecond to the shader.",
+        "    // Turning this off on console will result in a more blurry image.",
+        "    // So this defaults to on.",
+        "    //",
+        "    // 1 = On.",
+        "    // 0 = Off.",
+        "    //",
+        "    #define FXAA_EARLY_EXIT 1",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_DISCARD",
+        "    //",
+        "    // Only valid for PC OpenGL currently.",
+        "    // Probably will not work when FXAA_GREEN_AS_LUMA = 1.",
+        "    //",
+        "    // 1 = Use discard on pixels which don't need AA.",
+        "    //     For APIs which enable concurrent TEX+ROP from same surface.",
+        "    // 0 = Return unchanged color on pixels which don't need AA.",
+        "    //",
+        "    #define FXAA_DISCARD 0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_FAST_PIXEL_OFFSET",
+        "    //",
+        "    // Used for GLSL 120 only.",
+        "    //",
+        "    // 1 = GL API supports fast pixel offsets",
+        "    // 0 = do not use fast pixel offsets",
+        "    //",
+        "    #ifdef GL_EXT_gpu_shader4",
+        "        #define FXAA_FAST_PIXEL_OFFSET 1",
+        "    #endif",
+        "    #ifdef GL_NV_gpu_shader5",
+        "        #define FXAA_FAST_PIXEL_OFFSET 1",
+        "    #endif",
+        "    #ifdef GL_ARB_gpu_shader5",
+        "        #define FXAA_FAST_PIXEL_OFFSET 1",
+        "    #endif",
+        "    #ifndef FXAA_FAST_PIXEL_OFFSET",
+        "        #define FXAA_FAST_PIXEL_OFFSET 0",
+        "    #endif",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#ifndef FXAA_GATHER4_ALPHA",
+        "    //",
+        "    // 1 = API supports gather4 on alpha channel.",
+        "    // 0 = API does not support gather4 on alpha channel.",
+        "    //",
+        "    #if (FXAA_HLSL_5 == 1)",
+        "        #define FXAA_GATHER4_ALPHA 1",
+        "    #endif",
+        "    #ifdef GL_ARB_gpu_shader5",
+        "        #define FXAA_GATHER4_ALPHA 1",
+        "    #endif",
+        "    #ifdef GL_NV_gpu_shader5",
+        "        #define FXAA_GATHER4_ALPHA 1",
+        "    #endif",
+        "    #ifndef FXAA_GATHER4_ALPHA",
+        "        #define FXAA_GATHER4_ALPHA 0",
+        "    #endif",
+        "#endif",
+        "",
+        "",
+        "/*============================================================================",
+        "                        FXAA QUALITY - TUNING KNOBS",
+        "------------------------------------------------------------------------------",
+        "NOTE the other tuning knobs are now in the shader function inputs!",
+        "============================================================================*/",
+        "#ifndef FXAA_QUALITY_PRESET",
+        "    //",
+        "    // Choose the quality preset.",
+        "    // This needs to be compiled into the shader as it effects code.",
+        "    // Best option to include multiple presets is to",
+        "    // in each shader define the preset, then include this file.",
+        "    //",
+        "    // OPTIONS",
+        "    // -----------------------------------------------------------------------",
+        "    // 10 to 15 - default medium dither (10=fastest, 15=highest quality)",
+        "    // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality)",
+        "    // 39       - no dither, very expensive",
+        "    //",
+        "    // NOTES",
+        "    // -----------------------------------------------------------------------",
+        "    // 12 = slightly faster then FXAA 3.9 and higher edge quality (default)",
+        "    // 13 = about same speed as FXAA 3.9 and better than 12",
+        "    // 23 = closest to FXAA 3.9 visually and performance wise",
+        "    //  _ = the lowest digit is directly related to performance",
+        "    // _  = the highest digit is directly related to style",
+        "    //",
+        "    #define FXAA_QUALITY_PRESET 12",
+        "#endif",
+        "",
+        "",
+        "/*============================================================================",
+        "",
+        "                           FXAA QUALITY - PRESETS",
+        "",
+        "============================================================================*/",
+        "",
+        "/*============================================================================",
+        "                     FXAA QUALITY - MEDIUM DITHER PRESETS",
+        "============================================================================*/",
+        "#if (FXAA_QUALITY_PRESET == 10)",
+        "    #define FXAA_QUALITY_PS 3",
+        "    #define FXAA_QUALITY_P0 1.5",
+        "    #define FXAA_QUALITY_P1 3.0",
+        "    #define FXAA_QUALITY_P2 12.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 11)",
+        "    #define FXAA_QUALITY_PS 4",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 3.0",
+        "    #define FXAA_QUALITY_P3 12.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 12)",
+        "    #define FXAA_QUALITY_PS 5",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 4.0",
+        "    #define FXAA_QUALITY_P4 12.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 13)",
+        "    #define FXAA_QUALITY_PS 6",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 4.0",
+        "    #define FXAA_QUALITY_P5 12.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 14)",
+        "    #define FXAA_QUALITY_PS 7",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 4.0",
+        "    #define FXAA_QUALITY_P6 12.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 15)",
+        "    #define FXAA_QUALITY_PS 8",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 4.0",
+        "    #define FXAA_QUALITY_P7 12.0",
+        "#endif",
+        "",
+        "/*============================================================================",
+        "                     FXAA QUALITY - LOW DITHER PRESETS",
+        "============================================================================*/",
+        "#if (FXAA_QUALITY_PRESET == 20)",
+        "    #define FXAA_QUALITY_PS 3",
+        "    #define FXAA_QUALITY_P0 1.5",
+        "    #define FXAA_QUALITY_P1 2.0",
+        "    #define FXAA_QUALITY_P2 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 21)",
+        "    #define FXAA_QUALITY_PS 4",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 22)",
+        "    #define FXAA_QUALITY_PS 5",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 23)",
+        "    #define FXAA_QUALITY_PS 6",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 24)",
+        "    #define FXAA_QUALITY_PS 7",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 3.0",
+        "    #define FXAA_QUALITY_P6 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 25)",
+        "    #define FXAA_QUALITY_PS 8",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 4.0",
+        "    #define FXAA_QUALITY_P7 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 26)",
+        "    #define FXAA_QUALITY_PS 9",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 2.0",
+        "    #define FXAA_QUALITY_P7 4.0",
+        "    #define FXAA_QUALITY_P8 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 27)",
+        "    #define FXAA_QUALITY_PS 10",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 2.0",
+        "    #define FXAA_QUALITY_P7 2.0",
+        "    #define FXAA_QUALITY_P8 4.0",
+        "    #define FXAA_QUALITY_P9 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 28)",
+        "    #define FXAA_QUALITY_PS 11",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 2.0",
+        "    #define FXAA_QUALITY_P7 2.0",
+        "    #define FXAA_QUALITY_P8 2.0",
+        "    #define FXAA_QUALITY_P9 4.0",
+        "    #define FXAA_QUALITY_P10 8.0",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_QUALITY_PRESET == 29)",
+        "    #define FXAA_QUALITY_PS 12",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.5",
+        "    #define FXAA_QUALITY_P2 2.0",
+        "    #define FXAA_QUALITY_P3 2.0",
+        "    #define FXAA_QUALITY_P4 2.0",
+        "    #define FXAA_QUALITY_P5 2.0",
+        "    #define FXAA_QUALITY_P6 2.0",
+        "    #define FXAA_QUALITY_P7 2.0",
+        "    #define FXAA_QUALITY_P8 2.0",
+        "    #define FXAA_QUALITY_P9 2.0",
+        "    #define FXAA_QUALITY_P10 4.0",
+        "    #define FXAA_QUALITY_P11 8.0",
+        "#endif",
+        "",
+        "/*============================================================================",
+        "                     FXAA QUALITY - EXTREME QUALITY",
+        "============================================================================*/",
+        "#if (FXAA_QUALITY_PRESET == 39)",
+        "    #define FXAA_QUALITY_PS 12",
+        "    #define FXAA_QUALITY_P0 1.0",
+        "    #define FXAA_QUALITY_P1 1.0",
+        "    #define FXAA_QUALITY_P2 1.0",
+        "    #define FXAA_QUALITY_P3 1.0",
+        "    #define FXAA_QUALITY_P4 1.0",
+        "    #define FXAA_QUALITY_P5 1.5",
+        "    #define FXAA_QUALITY_P6 2.0",
+        "    #define FXAA_QUALITY_P7 2.0",
+        "    #define FXAA_QUALITY_P8 2.0",
+        "    #define FXAA_QUALITY_P9 2.0",
+        "    #define FXAA_QUALITY_P10 4.0",
+        "    #define FXAA_QUALITY_P11 8.0",
+        "#endif",
+        "",
+        "",
+        "",
+        "/*============================================================================",
+        "",
+        "                                API PORTING",
+        "",
+        "============================================================================*/",
+        "#if (FXAA_GLSL_100 == 1) || (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1)",
+        "    #define FxaaBool bool",
+        "    #define FxaaDiscard discard",
+        "    #define FxaaFloat float",
+        "    #define FxaaFloat2 vec2",
+        "    #define FxaaFloat3 vec3",
+        "    #define FxaaFloat4 vec4",
+        "    #define FxaaHalf float",
+        "    #define FxaaHalf2 vec2",
+        "    #define FxaaHalf3 vec3",
+        "    #define FxaaHalf4 vec4",
+        "    #define FxaaInt2 ivec2",
+        "    #define FxaaSat(x) clamp(x, 0.0, 1.0)",
+        "    #define FxaaTex sampler2D",
+        "#else",
+        "    #define FxaaBool bool",
+        "    #define FxaaDiscard clip(-1)",
+        "    #define FxaaFloat float",
+        "    #define FxaaFloat2 float2",
+        "    #define FxaaFloat3 float3",
+        "    #define FxaaFloat4 float4",
+        "    #define FxaaHalf half",
+        "    #define FxaaHalf2 half2",
+        "    #define FxaaHalf3 half3",
+        "    #define FxaaHalf4 half4",
+        "    #define FxaaSat(x) saturate(x)",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_GLSL_100 == 1)",
+        "  #define FxaaTexTop(t, p) texture2D(t, p, 0.0)",
+        "  #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), 0.0)",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_GLSL_120 == 1)",
+        "    // Requires,",
+        "    //  #version 120",
+        "    // And at least,",
+        "    //  #extension GL_EXT_gpu_shader4 : enable",
+        "    //  (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9)",
+        "    #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0)",
+        "    #if (FXAA_FAST_PIXEL_OFFSET == 1)",
+        "        #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o)",
+        "    #else",
+        "        #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0)",
+        "    #endif",
+        "    #if (FXAA_GATHER4_ALPHA == 1)",
+        "        // use #extension GL_ARB_gpu_shader5 : enable",
+        "        #define FxaaTexAlpha4(t, p) textureGather(t, p, 3)",
+        "        #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)",
+        "        #define FxaaTexGreen4(t, p) textureGather(t, p, 1)",
+        "        #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)",
+        "    #endif",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_GLSL_130 == 1)",
+        "    // Requires \"#version 130\" or better",
+        "    #define FxaaTexTop(t, p) textureLod(t, p, 0.0)",
+        "    #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o)",
+        "    #if (FXAA_GATHER4_ALPHA == 1)",
+        "        // use #extension GL_ARB_gpu_shader5 : enable",
+        "        #define FxaaTexAlpha4(t, p) textureGather(t, p, 3)",
+        "        #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3)",
+        "        #define FxaaTexGreen4(t, p) textureGather(t, p, 1)",
+        "        #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1)",
+        "    #endif",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_HLSL_3 == 1)",
+        "    #define FxaaInt2 float2",
+        "    #define FxaaTex sampler2D",
+        "    #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0))",
+        "    #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0))",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_HLSL_4 == 1)",
+        "    #define FxaaInt2 int2",
+        "    struct FxaaTex { SamplerState smpl; Texture2D tex; };",
+        "    #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)",
+        "    #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)",
+        "#endif",
+        "/*--------------------------------------------------------------------------*/",
+        "#if (FXAA_HLSL_5 == 1)",
+        "    #define FxaaInt2 int2",
+        "    struct FxaaTex { SamplerState smpl; Texture2D tex; };",
+        "    #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0)",
+        "    #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o)",
+        "    #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p)",
+        "    #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o)",
+        "    #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p)",
+        "    #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o)",
+        "#endif",
+        "",
+        "",
+        "/*============================================================================",
+        "                   GREEN AS LUMA OPTION SUPPORT FUNCTION",
+        "============================================================================*/",
+        "#if (FXAA_GREEN_AS_LUMA == 0)",
+        "    FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; }",
+        "#else",
+        "    FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; }",
+        "#endif",
+        "",
+        "",
+        "",
+        "",
+        "/*============================================================================",
+        "",
+        "                             FXAA3 QUALITY - PC",
+        "",
+        "============================================================================*/",
+        "#if (FXAA_PC == 1)",
+        "/*--------------------------------------------------------------------------*/",
+        "FxaaFloat4 FxaaPixelShader(",
+        "    //",
+        "    // Use noperspective interpolation here (turn off perspective interpolation).",
+        "    // {xy} = center of pixel",
+        "    FxaaFloat2 pos,",
+        "    //",
+        "    // Used only for FXAA Console, and not used on the 360 version.",
+        "    // Use noperspective interpolation here (turn off perspective interpolation).",
+        "    // {xy_} = upper left of pixel",
+        "    // {_zw} = lower right of pixel",
+        "    FxaaFloat4 fxaaConsolePosPos,",
+        "    //",
+        "    // Input color texture.",
+        "    // {rgb_} = color in linear or perceptual color space",
+        "    // if (FXAA_GREEN_AS_LUMA == 0)",
+        "    //     {__a} = luma in perceptual color space (not linear)",
+        "    FxaaTex tex,",
+        "    //",
+        "    // Only used on the optimized 360 version of FXAA Console.",
+        "    // For everything but 360, just use the same input here as for \"tex\".",
+        "    // For 360, same texture, just alias with a 2nd sampler.",
+        "    // This sampler needs to have an exponent bias of -1.",
+        "    FxaaTex fxaaConsole360TexExpBiasNegOne,",
+        "    //",
+        "    // Only used on the optimized 360 version of FXAA Console.",
+        "    // For everything but 360, just use the same input here as for \"tex\".",
+        "    // For 360, same texture, just alias with a 3nd sampler.",
+        "    // This sampler needs to have an exponent bias of -2.",
+        "    FxaaTex fxaaConsole360TexExpBiasNegTwo,",
+        "    //",
+        "    // Only used on FXAA Quality.",
+        "    // This must be from a constant/uniform.",
+        "    // {x_} = 1.0/screenWidthInPixels",
+        "    // {_y} = 1.0/screenHeightInPixels",
+        "    FxaaFloat2 fxaaQualityRcpFrame,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // This must be from a constant/uniform.",
+        "    // This effects sub-pixel AA quality and inversely sharpness.",
+        "    //   Where N ranges between,",
+        "    //     N = 0.50 (default)",
+        "    //     N = 0.33 (sharper)",
+        "    // {x__} = -N/screenWidthInPixels",
+        "    // {_y_} = -N/screenHeightInPixels",
+        "    // {_z_} =  N/screenWidthInPixels",
+        "    // {__w} =  N/screenHeightInPixels",
+        "    FxaaFloat4 fxaaConsoleRcpFrameOpt,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // Not used on 360, but used on PS3 and PC.",
+        "    // This must be from a constant/uniform.",
+        "    // {x__} = -2.0/screenWidthInPixels",
+        "    // {_y_} = -2.0/screenHeightInPixels",
+        "    // {_z_} =  2.0/screenWidthInPixels",
+        "    // {__w} =  2.0/screenHeightInPixels",
+        "    FxaaFloat4 fxaaConsoleRcpFrameOpt2,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // Only used on 360 in place of fxaaConsoleRcpFrameOpt2.",
+        "    // This must be from a constant/uniform.",
+        "    // {x__} =  8.0/screenWidthInPixels",
+        "    // {_y_} =  8.0/screenHeightInPixels",
+        "    // {_z_} = -4.0/screenWidthInPixels",
+        "    // {__w} = -4.0/screenHeightInPixels",
+        "    FxaaFloat4 fxaaConsole360RcpFrameOpt2,",
+        "    //",
+        "    // Only used on FXAA Quality.",
+        "    // This used to be the FXAA_QUALITY_SUBPIX define.",
+        "    // It is here now to allow easier tuning.",
+        "    // Choose the amount of sub-pixel aliasing removal.",
+        "    // This can effect sharpness.",
+        "    //   1.00 - upper limit (softer)",
+        "    //   0.75 - default amount of filtering",
+        "    //   0.50 - lower limit (sharper, less sub-pixel aliasing removal)",
+        "    //   0.25 - almost off",
+        "    //   0.00 - completely off",
+        "    FxaaFloat fxaaQualitySubpix,",
+        "    //",
+        "    // Only used on FXAA Quality.",
+        "    // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define.",
+        "    // It is here now to allow easier tuning.",
+        "    // The minimum amount of local contrast required to apply algorithm.",
+        "    //   0.333 - too little (faster)",
+        "    //   0.250 - low quality",
+        "    //   0.166 - default",
+        "    //   0.125 - high quality",
+        "    //   0.063 - overkill (slower)",
+        "    FxaaFloat fxaaQualityEdgeThreshold,",
+        "    //",
+        "    // Only used on FXAA Quality.",
+        "    // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define.",
+        "    // It is here now to allow easier tuning.",
+        "    // Trims the algorithm from processing darks.",
+        "    //   0.0833 - upper limit (default, the start of visible unfiltered edges)",
+        "    //   0.0625 - high quality (faster)",
+        "    //   0.0312 - visible limit (slower)",
+        "    // Special notes when using FXAA_GREEN_AS_LUMA,",
+        "    //   Likely want to set this to zero.",
+        "    //   As colors that are mostly not-green",
+        "    //   will appear very dark in the green channel!",
+        "    //   Tune by looking at mostly non-green content,",
+        "    //   then start at zero and increase until aliasing is a problem.",
+        "    FxaaFloat fxaaQualityEdgeThresholdMin,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // This used to be the FXAA_CONSOLE_EDGE_SHARPNESS define.",
+        "    // It is here now to allow easier tuning.",
+        "    // This does not effect PS3, as this needs to be compiled in.",
+        "    //   Use FXAA_CONSOLE_PS3_EDGE_SHARPNESS for PS3.",
+        "    //   Due to the PS3 being ALU bound,",
+        "    //   there are only three safe values here: 2 and 4 and 8.",
+        "    //   These options use the shaders ability to a free *|/ by 2|4|8.",
+        "    // For all other platforms can be a non-power of two.",
+        "    //   8.0 is sharper (default!!!)",
+        "    //   4.0 is softer",
+        "    //   2.0 is really soft (good only for vector graphics inputs)",
+        "    FxaaFloat fxaaConsoleEdgeSharpness,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD define.",
+        "    // It is here now to allow easier tuning.",
+        "    // This does not effect PS3, as this needs to be compiled in.",
+        "    //   Use FXAA_CONSOLE_PS3_EDGE_THRESHOLD for PS3.",
+        "    //   Due to the PS3 being ALU bound,",
+        "    //   there are only two safe values here: 1/4 and 1/8.",
+        "    //   These options use the shaders ability to a free *|/ by 2|4|8.",
+        "    // The console setting has a different mapping than the quality setting.",
+        "    // Other platforms can use other values.",
+        "    //   0.125 leaves less aliasing, but is softer (default!!!)",
+        "    //   0.25 leaves more aliasing, and is sharper",
+        "    FxaaFloat fxaaConsoleEdgeThreshold,",
+        "    //",
+        "    // Only used on FXAA Console.",
+        "    // This used to be the FXAA_CONSOLE_EDGE_THRESHOLD_MIN define.",
+        "    // It is here now to allow easier tuning.",
+        "    // Trims the algorithm from processing darks.",
+        "    // The console setting has a different mapping than the quality setting.",
+        "    // This only applies when FXAA_EARLY_EXIT is 1.",
+        "    // This does not apply to PS3,",
+        "    // PS3 was simplified to avoid more shader instructions.",
+        "    //   0.06 - faster but more aliasing in darks",
+        "    //   0.05 - default",
+        "    //   0.04 - slower and less aliasing in darks",
+        "    // Special notes when using FXAA_GREEN_AS_LUMA,",
+        "    //   Likely want to set this to zero.",
+        "    //   As colors that are mostly not-green",
+        "    //   will appear very dark in the green channel!",
+        "    //   Tune by looking at mostly non-green content,",
+        "    //   then start at zero and increase until aliasing is a problem.",
+        "    FxaaFloat fxaaConsoleEdgeThresholdMin,",
+        "    //",
+        "    // Extra constants for 360 FXAA Console only.",
+        "    // Use zeros or anything else for other platforms.",
+        "    // These must be in physical constant registers and NOT immedates.",
+        "    // Immedates will result in compiler un-optimizing.",
+        "    // {xyzw} = float4(1.0, -1.0, 0.25, -0.25)",
+        "    FxaaFloat4 fxaaConsole360ConstDir",
+        ") {",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat2 posM;",
+        "    posM.x = pos.x;",
+        "    posM.y = pos.y;",
+        "    #if (FXAA_GATHER4_ALPHA == 1)",
+        "        #if (FXAA_DISCARD == 0)",
+        "            FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);",
+        "            #if (FXAA_GREEN_AS_LUMA == 0)",
+        "                #define lumaM rgbyM.w",
+        "            #else",
+        "                #define lumaM rgbyM.y",
+        "            #endif",
+        "        #endif",
+        "        #if (FXAA_GREEN_AS_LUMA == 0)",
+        "            FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM);",
+        "            FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1));",
+        "        #else",
+        "            FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM);",
+        "            FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1));",
+        "        #endif",
+        "        #if (FXAA_DISCARD == 1)",
+        "            #define lumaM luma4A.w",
+        "        #endif",
+        "        #define lumaE luma4A.z",
+        "        #define lumaS luma4A.x",
+        "        #define lumaSE luma4A.y",
+        "        #define lumaNW luma4B.w",
+        "        #define lumaN luma4B.z",
+        "        #define lumaW luma4B.x",
+        "    #else",
+        "        FxaaFloat4 rgbyM = FxaaTexTop(tex, posM);",
+        "        #if (FXAA_GREEN_AS_LUMA == 0)",
+        "            #define lumaM rgbyM.w",
+        "        #else",
+        "            #define lumaM rgbyM.y",
+        "        #endif",
+        "        #if (FXAA_GLSL_100 == 1)",
+        "          FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0, 1.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 0.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 0.0,-1.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 0.0), fxaaQualityRcpFrame.xy));",
+        "        #else",
+        "          FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy));",
+        "        #endif",
+        "    #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat maxSM = max(lumaS, lumaM);",
+        "    FxaaFloat minSM = min(lumaS, lumaM);",
+        "    FxaaFloat maxESM = max(lumaE, maxSM);",
+        "    FxaaFloat minESM = min(lumaE, minSM);",
+        "    FxaaFloat maxWN = max(lumaN, lumaW);",
+        "    FxaaFloat minWN = min(lumaN, lumaW);",
+        "    FxaaFloat rangeMax = max(maxWN, maxESM);",
+        "    FxaaFloat rangeMin = min(minWN, minESM);",
+        "    FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold;",
+        "    FxaaFloat range = rangeMax - rangeMin;",
+        "    FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled);",
+        "    FxaaBool earlyExit = range < rangeMaxClamped;",
+        "/*--------------------------------------------------------------------------*/",
+        "    if(earlyExit)",
+        "        #if (FXAA_DISCARD == 1)",
+        "            FxaaDiscard;",
+        "        #else",
+        "            return rgbyM;",
+        "        #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "    #if (FXAA_GATHER4_ALPHA == 0)",
+        "        #if (FXAA_GLSL_100 == 1)",
+        "          FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0,-1.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0, 1.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2( 1.0,-1.0), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaFloat2(-1.0, 1.0), fxaaQualityRcpFrame.xy));",
+        "        #else",
+        "          FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy));",
+        "          FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));",
+        "        #endif",
+        "    #else",
+        "        FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy));",
+        "        FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy));",
+        "    #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat lumaNS = lumaN + lumaS;",
+        "    FxaaFloat lumaWE = lumaW + lumaE;",
+        "    FxaaFloat subpixRcpRange = 1.0/range;",
+        "    FxaaFloat subpixNSWE = lumaNS + lumaWE;",
+        "    FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS;",
+        "    FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat lumaNESE = lumaNE + lumaSE;",
+        "    FxaaFloat lumaNWNE = lumaNW + lumaNE;",
+        "    FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE;",
+        "    FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat lumaNWSW = lumaNW + lumaSW;",
+        "    FxaaFloat lumaSWSE = lumaSW + lumaSE;",
+        "    FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2);",
+        "    FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2);",
+        "    FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW;",
+        "    FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE;",
+        "    FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4;",
+        "    FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE;",
+        "    FxaaFloat lengthSign = fxaaQualityRcpFrame.x;",
+        "    FxaaBool horzSpan = edgeHorz >= edgeVert;",
+        "    FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE;",
+        "/*--------------------------------------------------------------------------*/",
+        "    if(!horzSpan) lumaN = lumaW;",
+        "    if(!horzSpan) lumaS = lumaE;",
+        "    if(horzSpan) lengthSign = fxaaQualityRcpFrame.y;",
+        "    FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat gradientN = lumaN - lumaM;",
+        "    FxaaFloat gradientS = lumaS - lumaM;",
+        "    FxaaFloat lumaNN = lumaN + lumaM;",
+        "    FxaaFloat lumaSS = lumaS + lumaM;",
+        "    FxaaBool pairN = abs(gradientN) >= abs(gradientS);",
+        "    FxaaFloat gradient = max(abs(gradientN), abs(gradientS));",
+        "    if(pairN) lengthSign = -lengthSign;",
+        "    FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange);",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat2 posB;",
+        "    posB.x = posM.x;",
+        "    posB.y = posM.y;",
+        "    FxaaFloat2 offNP;",
+        "    offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;",
+        "    offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;",
+        "    if(!horzSpan) posB.x += lengthSign * 0.5;",
+        "    if( horzSpan) posB.y += lengthSign * 0.5;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat2 posN;",
+        "    posN.x = posB.x - offNP.x * FXAA_QUALITY_P0;",
+        "    posN.y = posB.y - offNP.y * FXAA_QUALITY_P0;",
+        "    FxaaFloat2 posP;",
+        "    posP.x = posB.x + offNP.x * FXAA_QUALITY_P0;",
+        "    posP.y = posB.y + offNP.y * FXAA_QUALITY_P0;",
+        "    FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0;",
+        "    FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN));",
+        "    FxaaFloat subpixE = subpixC * subpixC;",
+        "    FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP));",
+        "/*--------------------------------------------------------------------------*/",
+        "    if(!pairN) lumaNN = lumaSS;",
+        "    FxaaFloat gradientScaled = gradient * 1.0/4.0;",
+        "    FxaaFloat lumaMM = lumaM - lumaNN * 0.5;",
+        "    FxaaFloat subpixF = subpixD * subpixE;",
+        "    FxaaBool lumaMLTZero = lumaMM < 0.0;",
+        "/*--------------------------------------------------------------------------*/",
+        "    lumaEndN -= lumaNN * 0.5;",
+        "    lumaEndP -= lumaNN * 0.5;",
+        "    FxaaBool doneN = abs(lumaEndN) >= gradientScaled;",
+        "    FxaaBool doneP = abs(lumaEndP) >= gradientScaled;",
+        "    if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1;",
+        "    if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1;",
+        "    FxaaBool doneNP = (!doneN) || (!doneP);",
+        "    if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1;",
+        "    if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1;",
+        "/*--------------------------------------------------------------------------*/",
+        "    if(doneNP) {",
+        "        if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "        if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "        if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "        if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "        doneN = abs(lumaEndN) >= gradientScaled;",
+        "        doneP = abs(lumaEndP) >= gradientScaled;",
+        "        if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2;",
+        "        if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2;",
+        "        doneNP = (!doneN) || (!doneP);",
+        "        if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2;",
+        "        if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2;",
+        "/*--------------------------------------------------------------------------*/",
+        "        #if (FXAA_QUALITY_PS > 3)",
+        "        if(doneNP) {",
+        "            if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "            if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "            if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "            if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "            doneN = abs(lumaEndN) >= gradientScaled;",
+        "            doneP = abs(lumaEndP) >= gradientScaled;",
+        "            if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3;",
+        "            if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3;",
+        "            doneNP = (!doneN) || (!doneP);",
+        "            if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3;",
+        "            if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3;",
+        "/*--------------------------------------------------------------------------*/",
+        "            #if (FXAA_QUALITY_PS > 4)",
+        "            if(doneNP) {",
+        "                if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                doneN = abs(lumaEndN) >= gradientScaled;",
+        "                doneP = abs(lumaEndP) >= gradientScaled;",
+        "                if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4;",
+        "                if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4;",
+        "                doneNP = (!doneN) || (!doneP);",
+        "                if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4;",
+        "                if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4;",
+        "/*--------------------------------------------------------------------------*/",
+        "                #if (FXAA_QUALITY_PS > 5)",
+        "                if(doneNP) {",
+        "                    if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                    if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                    if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                    if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                    doneN = abs(lumaEndN) >= gradientScaled;",
+        "                    doneP = abs(lumaEndP) >= gradientScaled;",
+        "                    if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5;",
+        "                    if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5;",
+        "                    doneNP = (!doneN) || (!doneP);",
+        "                    if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5;",
+        "                    if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5;",
+        "/*--------------------------------------------------------------------------*/",
+        "                    #if (FXAA_QUALITY_PS > 6)",
+        "                    if(doneNP) {",
+        "                        if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                        if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                        if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                        if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                        doneN = abs(lumaEndN) >= gradientScaled;",
+        "                        doneP = abs(lumaEndP) >= gradientScaled;",
+        "                        if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6;",
+        "                        if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6;",
+        "                        doneNP = (!doneN) || (!doneP);",
+        "                        if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6;",
+        "                        if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6;",
+        "/*--------------------------------------------------------------------------*/",
+        "                        #if (FXAA_QUALITY_PS > 7)",
+        "                        if(doneNP) {",
+        "                            if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                            if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                            if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                            if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                            doneN = abs(lumaEndN) >= gradientScaled;",
+        "                            doneP = abs(lumaEndP) >= gradientScaled;",
+        "                            if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7;",
+        "                            if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7;",
+        "                            doneNP = (!doneN) || (!doneP);",
+        "                            if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7;",
+        "                            if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7;",
+        "/*--------------------------------------------------------------------------*/",
+        "    #if (FXAA_QUALITY_PS > 8)",
+        "    if(doneNP) {",
+        "        if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "        if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "        if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "        if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "        doneN = abs(lumaEndN) >= gradientScaled;",
+        "        doneP = abs(lumaEndP) >= gradientScaled;",
+        "        if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8;",
+        "        if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8;",
+        "        doneNP = (!doneN) || (!doneP);",
+        "        if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8;",
+        "        if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8;",
+        "/*--------------------------------------------------------------------------*/",
+        "        #if (FXAA_QUALITY_PS > 9)",
+        "        if(doneNP) {",
+        "            if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "            if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "            if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "            if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "            doneN = abs(lumaEndN) >= gradientScaled;",
+        "            doneP = abs(lumaEndP) >= gradientScaled;",
+        "            if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9;",
+        "            if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9;",
+        "            doneNP = (!doneN) || (!doneP);",
+        "            if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9;",
+        "            if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9;",
+        "/*--------------------------------------------------------------------------*/",
+        "            #if (FXAA_QUALITY_PS > 10)",
+        "            if(doneNP) {",
+        "                if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                doneN = abs(lumaEndN) >= gradientScaled;",
+        "                doneP = abs(lumaEndP) >= gradientScaled;",
+        "                if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10;",
+        "                if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10;",
+        "                doneNP = (!doneN) || (!doneP);",
+        "                if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10;",
+        "                if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10;",
+        "/*--------------------------------------------------------------------------*/",
+        "                #if (FXAA_QUALITY_PS > 11)",
+        "                if(doneNP) {",
+        "                    if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                    if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                    if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                    if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                    doneN = abs(lumaEndN) >= gradientScaled;",
+        "                    doneP = abs(lumaEndP) >= gradientScaled;",
+        "                    if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11;",
+        "                    if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11;",
+        "                    doneNP = (!doneN) || (!doneP);",
+        "                    if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11;",
+        "                    if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11;",
+        "/*--------------------------------------------------------------------------*/",
+        "                    #if (FXAA_QUALITY_PS > 12)",
+        "                    if(doneNP) {",
+        "                        if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy));",
+        "                        if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy));",
+        "                        if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5;",
+        "                        if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5;",
+        "                        doneN = abs(lumaEndN) >= gradientScaled;",
+        "                        doneP = abs(lumaEndP) >= gradientScaled;",
+        "                        if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12;",
+        "                        if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12;",
+        "                        doneNP = (!doneN) || (!doneP);",
+        "                        if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12;",
+        "                        if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12;",
+        "/*--------------------------------------------------------------------------*/",
+        "                    }",
+        "                    #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "                }",
+        "                #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "            }",
+        "            #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "        }",
+        "        #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "    }",
+        "    #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "                        }",
+        "                        #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "                    }",
+        "                    #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "                }",
+        "                #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "            }",
+        "            #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "        }",
+        "        #endif",
+        "/*--------------------------------------------------------------------------*/",
+        "    }",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat dstN = posM.x - posN.x;",
+        "    FxaaFloat dstP = posP.x - posM.x;",
+        "    if(!horzSpan) dstN = posM.y - posN.y;",
+        "    if(!horzSpan) dstP = posP.y - posM.y;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero;",
+        "    FxaaFloat spanLength = (dstP + dstN);",
+        "    FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero;",
+        "    FxaaFloat spanLengthRcp = 1.0/spanLength;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaBool directionN = dstN < dstP;",
+        "    FxaaFloat dst = min(dstN, dstP);",
+        "    FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP;",
+        "    FxaaFloat subpixG = subpixF * subpixF;",
+        "    FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5;",
+        "    FxaaFloat subpixH = subpixG * fxaaQualitySubpix;",
+        "/*--------------------------------------------------------------------------*/",
+        "    FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0;",
+        "    FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH);",
+        "    if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign;",
+        "    if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign;",
+        "    #if (FXAA_DISCARD == 1)",
+        "        return FxaaTexTop(tex, posM);",
+        "    #else",
+        "        return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM);",
+        "    #endif",
+        "}",
+        "/*==========================================================================*/",
+        "#endif",
+        "",
+        "void main() {",
+        "  gl_FragColor = FxaaPixelShader(",
+        "    vUv,",
+        "    vec4(0.0),",
+        "    tDiffuse,",
+        "    tDiffuse,",
+        "    tDiffuse,",
+        "    resolution,",
+        "    vec4(0.0),",
+        "    vec4(0.0),",
+        "    vec4(0.0),",
+        "    0.75,",
+        "    0.166,",
+        "    0.0833,",
+        "    0.0,",
+        "    0.0,",
+        "    0.0,",
+        "    vec4(0.0)",
+        "  );",
+        "",
+        "  // TODO avoid querying texture twice for same texel",
+        "  gl_FragColor.a = texture2D(tDiffuse, vUv).a;",
+        "}"
+	].join("\n")
 
 };

+ 1 - 1
examples/js/shaders/EdgeShader.js → examples/js/shaders/FreiChenShader.js

@@ -7,7 +7,7 @@
  * aspect: vec2 of (1/width, 1/height)
  */
 
-THREE.EdgeShader = {
+THREE.FreiChenShader = {
 
 	uniforms: {
 

+ 90 - 0
examples/js/shaders/SobelOperatorShader.js

@@ -0,0 +1,90 @@
+/**
+ * @author Mugen87 / https://github.com/Mugen87
+ *
+ * Sobel Edge Detection (see https://youtu.be/uihBwtPIBxM)
+ *
+ * As mentioned in the video the Sobel operator expects a grayscale image as input.
+ *
+ */
+
+THREE.SobelOperatorShader = {
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"resolution": { value: new THREE.Vector2() }
+
+	},
+
+	vertexShader: [
+
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vUv = uv;",
+
+			"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform vec2 resolution;",
+		"varying vec2 vUv;",
+
+		"void main() {",
+
+			"vec2 texel = vec2( 1.0 / resolution.x, 1.0 / resolution.y );",
+
+			// kernel definition (in glsl matrices are filled in column-major order)
+
+			"const mat3 Gx = mat3( -1, -2, -1, 0, 0, 0, 1, 2, 1 );", // x direction kernel
+			"const mat3 Gy = mat3( -1, 0, 1, -2, 0, 2, -1, 0, 1 );", // y direction kernel
+
+			// fetch the 3x3 neighbourhood of a fragment
+
+			// first column
+
+			"float tx0y0 = texture2D( tDiffuse, vUv + texel * vec2( -1, -1 ) ).r;",
+			"float tx0y1 = texture2D( tDiffuse, vUv + texel * vec2( -1,  0 ) ).r;",
+			"float tx0y2 = texture2D( tDiffuse, vUv + texel * vec2( -1,  1 ) ).r;",
+
+			// second column
+
+			"float tx1y0 = texture2D( tDiffuse, vUv + texel * vec2(  0, -1 ) ).r;",
+			"float tx1y1 = texture2D( tDiffuse, vUv + texel * vec2(  0,  0 ) ).r;",
+			"float tx1y2 = texture2D( tDiffuse, vUv + texel * vec2(  0,  1 ) ).r;",
+
+			// third column
+
+			"float tx2y0 = texture2D( tDiffuse, vUv + texel * vec2(  1, -1 ) ).r;",
+			"float tx2y1 = texture2D( tDiffuse, vUv + texel * vec2(  1,  0 ) ).r;",
+			"float tx2y2 = texture2D( tDiffuse, vUv + texel * vec2(  1,  1 ) ).r;",
+
+			// gradient value in x direction
+
+			"float valueGx = Gx[0][0] * tx0y0 + Gx[1][0] * tx1y0 + Gx[2][0] * tx2y0 + ",
+				"Gx[0][1] * tx0y1 + Gx[1][1] * tx1y1 + Gx[2][1] * tx2y1 + ",
+				"Gx[0][2] * tx0y2 + Gx[1][2] * tx1y2 + Gx[2][2] * tx2y2; ",
+
+			// gradient value in y direction
+
+			"float valueGy = Gy[0][0] * tx0y0 + Gy[1][0] * tx1y0 + Gy[2][0] * tx2y0 + ",
+				"Gy[0][1] * tx0y1 + Gy[1][1] * tx1y1 + Gy[2][1] * tx2y1 + ",
+				"Gy[0][2] * tx0y2 + Gy[1][2] * tx1y2 + Gy[2][2] * tx2y2; ",
+
+			// magnitute of the total gradient
+
+			"float G = sqrt( ( valueGx * valueGx ) + ( valueGy * valueGy ) );",
+
+			"gl_FragColor = vec4( vec3( G ), 1 );",
+
+		"}"
+
+	].join( "\n" )
+
+};

+ 3 - 2
examples/js/vr/DaydreamController.js

@@ -17,9 +17,10 @@ THREE.DaydreamController = function () {
 
 	function findGamepad() {
 
-		// iterate across gamepads as the Daydream Controller may not be in position 0
+		// iterate across gamepads as the Daydream Controller may not be
+		// in position 0
 
-		var gamepads = navigator.getGamepads();
+		var gamepads = navigator.getGamepads && navigator.getGamepads();
 
 		for ( var i = 0; i < 4; i ++ ) {
 

+ 2 - 2
examples/js/vr/ViveController.js

@@ -21,7 +21,7 @@ THREE.ViveController = function ( id ) {
 		// Iterate across gamepads as Vive Controllers may not be
 		// in position 0 and 1.
 
-		var gamepads = navigator.getGamepads();
+		var gamepads = navigator.getGamepads && navigator.getGamepads();
 
 		for ( var i = 0, j = 0; i < 4; i ++ ) {
 
@@ -89,7 +89,7 @@ THREE.ViveController = function ( id ) {
 			if ( thumbpadIsPressed !== gamepad.buttons[ 0 ].pressed ) {
 
 				thumbpadIsPressed = gamepad.buttons[ 0 ].pressed;
-				scope.dispatchEvent( { type: thumbpadIsPressed ? 'thumbpaddown' : 'thumbpadup' } );
+				scope.dispatchEvent( { type: thumbpadIsPressed ? 'thumbpaddown' : 'thumbpadup', axes: axes } );
 
 			}
 

+ 518 - 0
examples/misc_exporter_gltf.html

@@ -0,0 +1,518 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - gltf exporter</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				color: #ccc;
+				text-align: center;
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			GLTF Exporter<br/>
+			<button id="export_scene">Export Scene1</button>
+			<button id="export_scenes">Export Scene1 and Scene 2</button>
+			<button id="export_object">Export Sphere</button>
+			<button id="export_objects">Export Sphere and Grid</button>
+			<button id="export_scene_object">Export Scene1 and Sphere</button>
+			<br/>
+			<label><input id="option_trs" name="trs" type="checkbox"/>TRS</label>
+			<label><input id="option_visible" name="visible" type="checkbox" checked="checked"/>Only Visible</label>
+			<label><input id="option_drawrange" name="visible" type="checkbox" checked="checked"/>Truncate drawRange</label>
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/exporters/GLTFExporter.js"></script>
+
+		<script>
+
+			function exportGLTF( input ) {
+
+				var gltfExporter = new THREE.GLTFExporter();
+
+				var options = {
+					trs: document.getElementById('option_trs').checked,
+					onlyVisible: document.getElementById('option_visible').checked,
+					truncateDrawRange: document.getElementById('option_drawrange').checked
+				}
+				gltfExporter.parse( input, function( result ) {
+
+					var output = JSON.stringify( result, null, 2 );
+					console.log( output );
+					saveString( output, 'scene.gltf' );
+
+				}, options );
+
+			}
+
+			document.getElementById( 'export_scene' ).addEventListener( 'click', function () {
+
+				exportGLTF( scene1 );
+
+			} );
+
+			document.getElementById( 'export_scenes' ).addEventListener( 'click', function () {
+
+				exportGLTF( [ scene1, scene2 ] );
+
+			} );
+
+			document.getElementById( 'export_object' ).addEventListener( 'click', function () {
+
+				exportGLTF( sphere );
+
+			} );
+
+			document.getElementById( 'export_objects' ).addEventListener( 'click', function () {
+
+				exportGLTF( [ sphere, gridHelper ] );
+
+			} );
+
+			document.getElementById( 'export_scene_object' ).addEventListener( 'click', function () {
+
+				exportGLTF( [ scene1, gridHelper ] );
+
+			} );
+
+
+			var link = document.createElement( 'a' );
+			link.style.display = 'none';
+			document.body.appendChild( link ); // Firefox workaround, see #6594
+
+			function save( blob, filename ) {
+
+				link.href = URL.createObjectURL( blob );
+				link.download = filename || 'data.json';
+				link.click();
+
+				// URL.revokeObjectURL( url ); breaks Firefox...
+
+			}
+
+			function saveString( text, filename ) {
+
+				save( new Blob( [ text ], { type: 'text/plain' } ), filename );
+
+			}
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container;
+
+			var camera, object, scene1, scene2, renderer;
+			var gridHelper, sphere;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				scene1 = new THREE.Scene();
+				scene1.name = 'Scene1';
+
+				// ---------------------------------------------------------------------
+				// Perspective Camera
+				// ---------------------------------------------------------------------
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set(600, 400, 0);
+
+				camera.name = "PerspectiveCamera";
+				scene1.add( camera );
+
+				// ---------------------------------------------------------------------
+				// Ambient light
+				// ---------------------------------------------------------------------
+				var light = new THREE.AmbientLight( 0xffffff, 0.2 );
+				light.name = 'AmbientLight';
+				scene1.add( light );
+
+				// ---------------------------------------------------------------------
+				// DirectLight
+				// ---------------------------------------------------------------------
+				light = new THREE.DirectionalLight( 0xffffff, 1 );
+				light.position.set( 1, 1, 0 );
+				light.name = 'DirectionalLight';
+				scene1.add( light );
+
+				// ---------------------------------------------------------------------
+				// Grid
+				// ---------------------------------------------------------------------
+				gridHelper = new THREE.GridHelper( 2000, 20 );
+				gridHelper.position.y = -50;
+				gridHelper.name = "Grid";
+				scene1.add( gridHelper );
+
+				// ---------------------------------------------------------------------
+				// Axis
+				// ---------------------------------------------------------------------
+				var axis = new THREE.AxisHelper(500);
+				axis.name = "AxisHelper";
+				scene1.add( axis );
+
+				// ---------------------------------------------------------------------
+				// Simple geometry with basic material
+				// ---------------------------------------------------------------------
+				// Icosahedron
+				var mapGrid = new THREE.TextureLoader().load( 'textures/UV_Grid_Sm.jpg' );
+				mapGrid.wrapS = mapGrid.wrapT = THREE.RepeatWrapping;
+				var material = new THREE.MeshBasicMaterial( {
+					color: 0xffffff,
+					map: mapGrid
+				} );
+
+				object = new THREE.Mesh( new THREE.IcosahedronGeometry( 75, 0 ), material );
+				object.position.set( -200, 0, 200 );
+				object.name = 'Icosahedron';
+				scene1.add( object );
+
+				// Octahedron
+				material = new THREE.MeshBasicMaterial( {
+					color: 0x0000ff,
+					wireframe: true
+				} );
+				object = new THREE.Mesh( new THREE.OctahedronGeometry( 75, 1 ), material );
+				object.position.set( 0, 0, 200 );
+				object.name = 'Octahedron';
+				scene1.add( object );
+
+				// Tetrahedron
+				material = new THREE.MeshBasicMaterial( {
+					color: 0xff0000,
+					transparent: true,
+					opacity: 0.5
+				} );
+
+				object = new THREE.Mesh( new THREE.TetrahedronGeometry( 75, 0 ), material );
+				object.position.set( 200, 0, 200 );
+				object.name = 'Tetrahedron';
+				scene1.add( object );
+
+				// ---------------------------------------------------------------------
+				// Buffered geometry primitives
+				// ---------------------------------------------------------------------
+				// Sphere
+				material = new THREE.MeshStandardMaterial( {
+					color: 0xffff00,
+					metalness: 0.5,
+					roughness: 1.0,
+					flatShading: true
+				} );
+				sphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 70, 10, 10 ), material );
+				sphere.position.set( 0, 0, 0 );
+				sphere.name = "Sphere";
+				scene1.add( sphere );
+
+				// Cylinder
+				material = new THREE.MeshStandardMaterial( {
+					color: 0xff00ff,
+					flatShading: true
+				} );
+				object = new THREE.Mesh( new THREE.CylinderBufferGeometry( 10, 80, 100 ), material );
+				object.position.set( 200, 0, 0 );
+				object.name = "Cylinder";
+				scene1.add( object );
+
+				// TorusKnot
+				material = new THREE.MeshStandardMaterial( {
+					color: 0xff0000,
+					roughness: 1
+				} );
+				object = new THREE.Mesh( new THREE.TorusKnotGeometry( 50, 15, 40, 10 ), material );
+				object.position.set( -200, 0, 0 );
+				object.name = "Cylinder";
+				scene1.add( object );
+
+
+				// ---------------------------------------------------------------------
+				// Hierarchy
+				// ---------------------------------------------------------------------
+				var mapWood = new THREE.TextureLoader().load( 'textures/hardwood2_diffuse.jpg' );
+				material = new THREE.MeshStandardMaterial( { map: mapWood, side: THREE.DoubleSide } );
+
+				object = new THREE.Mesh( new THREE.BoxBufferGeometry( 40, 100, 100 ), material );
+				object.position.set( -200, 0, 400 );
+				object.name = "Cube";
+				scene1.add( object );
+
+				object2 = new THREE.Mesh( new THREE.BoxBufferGeometry( 40, 40, 40, 2, 2, 2 ), material );
+				object2.position.set( 0, 0, 50 );
+				object2.rotation.set( 0, 45, 0 );
+				object2.name = "SubCube";
+				object.add( object2 );
+
+
+				// ---------------------------------------------------------------------
+				// Groups
+				// ---------------------------------------------------------------------
+				group1 = new THREE.Group();
+				group1.name = "Group";
+				scene1.add( group1 );
+
+				group2 = new THREE.Group();
+				group2.name = "subGroup";
+				group2.position.set( 0, 50, 0);
+				group1.add( group2 );
+
+				object2 = new THREE.Mesh( new THREE.BoxBufferGeometry( 30, 30, 30 ), material );
+				object2.name = "Cube in group";
+				object2.position.set( 0, 0, 400 );
+				group2.add( object2 );
+
+				// ---------------------------------------------------------------------
+				// Triangle Strip
+				// ---------------------------------------------------------------------
+				var geometry = new THREE.BufferGeometry();
+				var positions = new Float32Array([
+					0, 0, 0,
+					0, 80, 0,
+					80, 0, 0,
+					80, 80, 0,
+					80, 0, 80,
+					80, 80, 80,
+				]);
+
+				var colors = new Float32Array([
+					1, 0, 0,
+					1, 0, 0,
+					1, 1, 0,
+					1, 1, 0,
+					0, 0, 1,
+					0, 0, 1,
+				]);
+
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+				object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { side: THREE.DoubleSide, vertexColors: THREE.VertexColors } ) );
+				object.position.set( 140, -40, -250);
+				object.setDrawMode( THREE.TriangleStripDrawMode );
+				object.name = 'Custom buffered';
+				object.userData = { data: 'customdata', list: [ 1,2,3,4 ] };
+
+				scene1.add( object );
+
+
+				// ---------------------------------------------------------------------
+				// Line Strip
+				// ---------------------------------------------------------------------
+				var geometry = new THREE.BufferGeometry();
+				var numPoints = 100;
+				var positions = new Float32Array( numPoints * 3 );
+
+				for (var i = 0; i < numPoints; i++ ) {
+					positions[ i * 3 ] = i;
+					positions[ i * 3 + 1 ] = Math.sin( i / 2 ) * 20;
+					positions[ i * 3 + 2 ] = 0;
+				}
+
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				object = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) );
+				object.position.set(-50, 0, -200);
+				scene1.add( object );
+
+
+				// ---------------------------------------------------------------------
+				// Line Loop
+				// ---------------------------------------------------------------------
+				var geometry = new THREE.BufferGeometry();
+				var numPoints = 5;
+				var radius = 70;
+				var positions = new Float32Array( numPoints * 3 );
+
+				for (var i = 0; i < numPoints; i++ ) {
+					var s = i * Math.PI * 2 / numPoints;
+					positions[ i * 3 ] = radius * Math.sin ( s );
+					positions[ i * 3 + 1 ] = radius * Math.cos ( s );
+					positions[ i * 3 + 2 ] = 0;
+				}
+
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				object = new THREE.LineLoop( geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ) );
+				object.position.set(0, 0, -200);
+
+				scene1.add( object );
+
+				// ---------------------------------------------------------------------
+				// Buffer geometry truncated (DrawRange)
+				// ---------------------------------------------------------------------
+				var geometry = new THREE.BufferGeometry();
+				var numElements = 6;
+				var outOfRange = 3;
+
+				var positions = new Float32Array( ( numElements + outOfRange ) * 3 );
+				var colors = new Float32Array( ( numElements + outOfRange ) * 3 );
+
+				positions.set([
+					0, 0, 0,
+					0, 80, 0,
+					80, 0, 0,
+					80, 0, 0,
+					0, 80, 0,
+					80, 80, 0
+				]);
+
+				colors.set([
+					1, 0, 0,
+					1, 0, 0,
+					1, 1, 0,
+					1, 1, 0,
+					0, 0, 1,
+					0, 0, 1,
+				]);
+
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
+				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+				geometry.setDrawRange( 0, numElements );
+
+				object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { side: THREE.DoubleSide, vertexColors: THREE.VertexColors } ) );
+				object.name = 'Custom buffered truncated';
+				object.position.set( 340, -40, -200 );
+
+				scene1.add( object );
+
+				// ---------------------------------------------------------------------
+				// Points
+				// ---------------------------------------------------------------------
+				var numPoints = 100;
+				var pointsArray = new Float32Array( numPoints * 3 );
+				for ( var i = 0; i < numPoints; i++ ) {
+					pointsArray[ 3 * i ] = -50 + Math.random() * 100;
+					pointsArray[ 3 * i + 1 ] = Math.random() * 100;
+					pointsArray[ 3 * i + 2 ] = -50 + Math.random() * 100;
+				}
+
+				pointsGeo = new THREE.BufferGeometry();
+				pointsGeo.addAttribute( 'position', new THREE.BufferAttribute( pointsArray, 3 ) );
+
+				var pointsMaterial = new THREE.PointsMaterial( { color: 0xffff00, size: 5 } );
+				var points = new THREE.Points( pointsGeo, pointsMaterial );
+				points.name = "Points";
+				points.position.set( -200, 0, -200);
+				scene1.add( points );
+
+				// ---------------------------------------------------------------------
+				// Ortho camera
+				// ---------------------------------------------------------------------
+				var cameraOrtho = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 10, 10 );
+				scene1.add( cameraOrtho );
+				cameraOrtho.name = 'OrthographicCamera';
+
+				material = new THREE.MeshLambertMaterial( {
+					color: 0xffff00,
+					side: THREE.DoubleSide
+				} );
+
+				object = new THREE.Mesh( new THREE.CircleGeometry( 50, 20, 0, Math.PI * 2 ), material );
+				object.position.set( 200, 0, -400 );
+				scene1.add( object );
+
+				object = new THREE.Mesh( new THREE.RingGeometry( 10, 50, 20, 5, 0, Math.PI * 2 ), material );
+				object.position.set( 0, 0, -400 );
+				scene1.add( object );
+
+				object = new THREE.Mesh( new THREE.CylinderGeometry( 25, 75, 100, 40, 5 ), material );
+				object.position.set( -200, 0, -400 );
+				scene1.add( object );
+
+				//
+				var points = [];
+
+				for ( var i = 0; i < 50; i ++ ) {
+
+					points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * Math.sin( i * 0.1 ) * 15 + 50, ( i - 5 ) * 2 ) );
+
+				}
+
+				object = new THREE.Mesh( new THREE.LatheGeometry( points, 20 ), material );
+				object.position.set( 200, 0, 400 );
+				scene1.add( object );
+
+				// ---------------------------------------------------------------------
+				// Big red box hidden just for testing `onlyVisible` option
+				// ---------------------------------------------------------------------
+				material = new THREE.MeshBasicMaterial( {
+					color: 0xff0000
+				} );
+				object = new THREE.Mesh( new THREE.BoxBufferGeometry( 200, 200, 200 ), material );
+				object.position.set( 0, 0, 0 );
+				object.name = "CubeHidden";
+				object.visible = false;
+				scene1.add( object );
+
+				// ---------------------------------------------------------------------
+				// 2nd Scene
+				// ---------------------------------------------------------------------
+				scene2 = new THREE.Scene();
+				object = new THREE.Mesh( new THREE.BoxBufferGeometry( 100, 100, 100 ), material );
+				object.position.set( 0, 0, 0 );
+				object.name = "Cube2ndScene";
+				scene2.name = 'Scene2';
+				scene2.add(object);
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.appendChild( renderer.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+
+			}
+
+			function render() {
+
+				var timer = Date.now() * 0.0001;
+
+				camera.position.x = Math.cos( timer ) * 800;
+				camera.position.z = Math.sin( timer ) * 800;
+
+				camera.lookAt( scene1.position );
+				renderer.render( scene1, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 0 - 0
examples/webgl_exporter_obj.html → examples/misc_exporter_obj.html


+ 17 - 17
examples/misc_sound.html

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

+ 185 - 0
examples/misc_sound_visualizer.html

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

BIN
examples/models/3ds/portalgun/portalgun.3ds


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

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

BIN
examples/textures/fxaa_scene.png


+ 18 - 42
examples/webgl_buffergeometry.html

@@ -82,14 +82,14 @@
 
 				var geometry = new THREE.BufferGeometry();
 
-				var positions = new Float32Array( triangles * 3 * 3 );
-				var normals = new Float32Array( triangles * 3 * 3 );
-				var colors = new Float32Array( triangles * 3 * 3 );
+				var positions = [];
+				var normals = [];
+				var colors = [];
 
 				var color = new THREE.Color();
 
-				var n = 800, n2 = n/2;	// triangles spread in the cube
-				var d = 12, d2 = d/2;	// individual triangle size
+				var n = 800, n2 = n / 2;	// triangles spread in the cube
+				var d = 12, d2 = d / 2;	// individual triangle size
 
 				var pA = new THREE.Vector3();
 				var pB = new THREE.Vector3();
@@ -98,7 +98,7 @@
 				var cb = new THREE.Vector3();
 				var ab = new THREE.Vector3();
 
-				for ( var i = 0; i < positions.length; i += 9 ) {
+				for ( var i = 0; i < triangles; i ++ ) {
 
 					// positions
 
@@ -118,17 +118,9 @@
 					var cy = y + Math.random() * d - d2;
 					var cz = z + Math.random() * d - d2;
 
-					positions[ i ]     = ax;
-					positions[ i + 1 ] = ay;
-					positions[ i + 2 ] = az;
-
-					positions[ i + 3 ] = bx;
-					positions[ i + 4 ] = by;
-					positions[ i + 5 ] = bz;
-
-					positions[ i + 6 ] = cx;
-					positions[ i + 7 ] = cy;
-					positions[ i + 8 ] = cz;
+					positions.push( ax, ay, az );
+					positions.push( bx, by, bz );
+					positions.push( cx, cy, cz );
 
 					// flat face normals
 
@@ -146,17 +138,9 @@
 					var ny = cb.y;
 					var nz = cb.z;
 
-					normals[ i ]     = nx;
-					normals[ i + 1 ] = ny;
-					normals[ i + 2 ] = nz;
-
-					normals[ i + 3 ] = nx;
-					normals[ i + 4 ] = ny;
-					normals[ i + 5 ] = nz;
-
-					normals[ i + 6 ] = nx;
-					normals[ i + 7 ] = ny;
-					normals[ i + 8 ] = nz;
+					normals.push( nx, ny, nz );
+					normals.push( nx, ny, nz );
+					normals.push( nx, ny, nz );
 
 					// colors
 
@@ -166,25 +150,17 @@
 
 					color.setRGB( vx, vy, vz );
 
-					colors[ i ]     = color.r;
-					colors[ i + 1 ] = color.g;
-					colors[ i + 2 ] = color.b;
-
-					colors[ i + 3 ] = color.r;
-					colors[ i + 4 ] = color.g;
-					colors[ i + 5 ] = color.b;
-
-					colors[ i + 6 ] = color.r;
-					colors[ i + 7 ] = color.g;
-					colors[ i + 8 ] = color.b;
+					colors.push( color.r, color.g, color.b );
+					colors.push( color.r, color.g, color.b );
+					colors.push( color.r, color.g, color.b );
 
 				}
 
 				function disposeArray() { this.array = null; }
 
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).onUpload( disposeArray ) );
-				geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).onUpload( disposeArray ) );
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).onUpload( disposeArray ) );
+				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ).onUpload( disposeArray ) );
+				geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ).onUpload( disposeArray ) );
+				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ).onUpload( disposeArray ) );
 
 				geometry.computeBoundingSphere();
 

+ 18 - 25
examples/webgl_buffergeometry_custom_attributes_particles.html

@@ -39,13 +39,12 @@
 		<script type="x-shader/x-vertex" id="vertexshader">
 
 			attribute float size;
-			attribute vec3 customColor;
 
 			varying vec3 vColor;
 
 			void main() {
 
-				vColor = customColor;
+				vColor = color;
 
 				vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
 
@@ -59,14 +58,13 @@
 
 		<script type="x-shader/x-fragment" id="fragmentshader">
 
-			uniform vec3 color;
 			uniform sampler2D texture;
 
 			varying vec3 vColor;
 
 			void main() {
 
-				gl_FragColor = vec4( color * vColor, 1.0 );
+				gl_FragColor = vec4( vColor, 1.0 );
 
 				gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );
 
@@ -85,22 +83,18 @@
 
 		var particles = 100000;
 
-		var WIDTH = window.innerWidth;
-		var HEIGHT = window.innerHeight;
-
 		init();
 		animate();
 
 		function init() {
 
-			camera = new THREE.PerspectiveCamera( 40, WIDTH / HEIGHT, 1, 10000 );
+			camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
 			camera.position.z = 300;
 
 			scene = new THREE.Scene();
 
 			uniforms = {
 
-				color:     { value: new THREE.Color( 0xffffff ) },
 				texture:   { value: new THREE.TextureLoader().load( "textures/sprites/spark1.png" ) }
 
 			};
@@ -113,7 +107,8 @@
 
 				blending:       THREE.AdditiveBlending,
 				depthTest:      false,
-				transparent:    true
+				transparent:    true,
+				vertexColors:   true
 
 			});
 
@@ -122,31 +117,29 @@
 
 			geometry = new THREE.BufferGeometry();
 
-			var positions = new Float32Array( particles * 3 );
-			var colors = new Float32Array( particles * 3 );
-			var sizes = new Float32Array( particles );
+			var positions = [];
+			var colors = [];
+			var sizes = [];
 
 			var color = new THREE.Color();
 
-			for ( var i = 0, i3 = 0; i < particles; i ++, i3 += 3 ) {
+			for ( var i = 0; i < particles; i ++ ) {
 
-				positions[ i3 + 0 ] = ( Math.random() * 2 - 1 ) * radius;
-				positions[ i3 + 1 ] = ( Math.random() * 2 - 1 ) * radius;
-				positions[ i3 + 2 ] = ( Math.random() * 2 - 1 ) * radius;
+				positions.push( ( Math.random() * 2 - 1 ) * radius );
+				positions.push( ( Math.random() * 2 - 1 ) * radius );
+				positions.push( ( Math.random() * 2 - 1 ) * radius );
 
 				color.setHSL( i / particles, 1.0, 0.5 );
 
-				colors[ i3 + 0 ] = color.r;
-				colors[ i3 + 1 ] = color.g;
-				colors[ i3 + 2 ] = color.b;
+				colors.push( color.r, color.g, color.b );
 
-				sizes[ i ] = 20;
+				sizes.push( 20 );;
 
 			}
 
-			geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-			geometry.addAttribute( 'customColor', new THREE.BufferAttribute( colors, 3 ) );
-			geometry.addAttribute( 'size', new THREE.BufferAttribute( sizes, 1 ) );
+			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+			geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
+			geometry.addAttribute( 'size', new THREE.Float32BufferAttribute( sizes, 1 ).setDynamic( true ) );
 
 			particleSystem = new THREE.Points( geometry, shaderMaterial );
 
@@ -154,7 +147,7 @@
 
 			renderer = new THREE.WebGLRenderer();
 			renderer.setPixelRatio( window.devicePixelRatio );
-			renderer.setSize( WIDTH, HEIGHT );
+			renderer.setSize( window.innerWidth, window.innerHeight );
 
 			var container = document.getElementById( 'container' );
 			container.appendChild( renderer.domElement );

+ 53 - 52
examples/webgl_buffergeometry_instancing.html

@@ -72,10 +72,10 @@
 
 		void main(){
 
-			vPosition = offset * max(abs(sineTime * 2.0 + 1.0), 0.5) + position;
-			vec4 orientation = normalize(mix(orientationStart, orientationEnd, sineTime));
-			vec3 vcV = cross(orientation.xyz, vPosition);
-			vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
+			vPosition = offset * max( abs( sineTime * 2.0 + 1.0 ), 0.5 ) + position;
+			vec4 orientation = normalize( mix( orientationStart, orientationEnd, sineTime ) );
+			vec3 vcV = cross( orientation.xyz, vPosition );
+			vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
 
 			vColor = color;
 
@@ -124,74 +124,61 @@
 			camera.position.z = 2;
 
 			scene = new THREE.Scene();
-			scene.background = new THREE.Color( 0x101010 );
 
 			// geometry
 
-			var triangles = 1;
-			var instances = 65000;
-
-			var geometry = new THREE.InstancedBufferGeometry();
-
-			geometry.maxInstancedCount = instances; // set so its initalized for dat.GUI, will be set in first draw otherwise
-			var gui = new dat.GUI();
-			gui.add( geometry, "maxInstancedCount", 0, instances );
-
-			var vertices = new THREE.BufferAttribute( new Float32Array( triangles * 3 * 3 ), 3 );
-
-			vertices.setXYZ( 0, 0.025, -0.025, 0 );
-			vertices.setXYZ( 1, -0.025, 0.025, 0 );
-			vertices.setXYZ( 2, 0, 0, 0.025 );
-
-			geometry.addAttribute( 'position', vertices );
-
-			var offsets = new THREE.InstancedBufferAttribute( new Float32Array( instances * 3 ), 3, 1 );
-
-			for ( var i = 0, ul = offsets.count; i < ul; i++ ) {
+			var vector = new THREE.Vector4();
 
-				offsets.setXYZ( i, Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
+			var triangles = 1;
+			var instances = 50000;
 
-			}
+			var positions = [];
+			var offsets = [];
+			var colors = [];
+			var orientationsStart = [];
+			var orientationsEnd = [];
 
-			geometry.addAttribute( 'offset', offsets );
+			positions.push( 0.025, -0.025, 0 );
+			positions.push( -0.025, 0.025, 0 );
+			positions.push( 0, 0, 0.025 );
 
-			var colors = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1 );
+			// instanced attributes
 
-			for ( var i = 0, ul = colors.count; i < ul; i++ ) {
+			for ( var i = 0; i < instances; i ++ ) {
 
-				colors.setXYZW( i, Math.random(), Math.random(), Math.random(), Math.random() );
+				// offsets
 
-			}
+				offsets.push( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
 
-			geometry.addAttribute( 'color', colors );
+				// colors
 
-			var vector = new THREE.Vector4();
+				colors.push( Math.random(), Math.random(), Math.random(), Math.random() );
 
-			var orientationsStart = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1 );
-
-			for ( var i = 0, ul = orientationsStart.count; i < ul; i++ ) {
+				// orientation start
 
 				vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
 				vector.normalize();
 
-				orientationsStart.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
-
-			}
-
-			geometry.addAttribute( 'orientationStart', orientationsStart );
+				orientationsStart.push( vector.x, vector.y, vector.z, vector.w );
 
-			var orientationsEnd = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1 );
-
-			for ( var i = 0, ul = orientationsEnd.count; i < ul; i++ ) {
+				// orientation end
 
 				vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
 				vector.normalize();
 
-				orientationsEnd.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+				orientationsEnd.push( vector.x, vector.y, vector.z, vector.w );
 
 			}
 
-			geometry.addAttribute( 'orientationEnd', orientationsEnd );
+			var geometry = new THREE.InstancedBufferGeometry();
+			geometry.maxInstancedCount = instances; // set so its initalized for dat.GUI, will be set in first draw otherwise
+
+			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+
+			geometry.addAttribute( 'offset', new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 ) );
+			geometry.addAttribute( 'color', new THREE.InstancedBufferAttribute( new Float32Array( colors ), 4 ) );
+			geometry.addAttribute( 'orientationStart', new THREE.InstancedBufferAttribute( new Float32Array( orientationsStart ), 4 ) );
+			geometry.addAttribute( 'orientationEnd', new THREE.InstancedBufferAttribute( new Float32Array( orientationsEnd ), 4 ) );
 
 			// material
 
@@ -208,23 +195,37 @@
 
 			} );
 
+			//
+
 			var mesh = new THREE.Mesh( geometry, material );
 			scene.add( mesh );
 
+			//
+
 			renderer = new THREE.WebGLRenderer();
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
 
 			if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === false ) {
-				document.getElementById( "notSupported" ).style.display = "";
+
+				document.getElementById( 'notSupported' ).style.display = '';
 				return;
+
 			}
 
-			renderer.setPixelRatio( window.devicePixelRatio );
-			renderer.setSize( window.innerWidth, window.innerHeight );
-			container.appendChild( renderer.domElement );
+			//
+
+			var gui = new dat.GUI( { width: 350 } );
+			gui.add( geometry, 'maxInstancedCount', 0, instances );
+
+			//
 
 			stats = new Stats();
 			container.appendChild( stats.dom );
 
+			//
+
 			window.addEventListener( 'resize', onWindowResize, false );
 
 		}
@@ -253,7 +254,7 @@
 
 			var time = performance.now();
 
-			var object = scene.children[0];
+			var object = scene.children[ 0 ];
 
 			object.rotation.y = time * 0.0005;
 			object.material.uniforms.time.value = time * 0.005;

+ 66 - 145
examples/webgl_buffergeometry_instancing_dynamic.html

@@ -69,15 +69,14 @@
 		void main() {
 
 			vec3 vPosition = position;
-			vec3 vcV = cross(orientation.xyz, vPosition);
-			vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
+			vec3 vcV = cross( orientation.xyz, vPosition );
+			vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
 
 			vUv = uv;
 
 			gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
 
 		}
-
 	</script>
 
 	<script id="fragmentShader" type="x-shader/x-fragment">
@@ -89,187 +88,116 @@
 
 		void main() {
 
-			gl_FragColor = texture2D(map, vUv);
+			gl_FragColor = texture2D( map, vUv );
 
 		}
-
 	</script>
 
 	<script>
 
-		if ( !Detector.webgl ) Detector.addGetWebGLMessage();
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
 		var container, stats;
+		var camera, scene, renderer, mesh;
+		var offsetAttribute, orientationAttribute;
 
-		var camera, scene, renderer;
-		var orientations;
+		var lastTime = 0;
 
+		var moveQ = new THREE.Quaternion( 0.5, 0.5, 0.5, 0.0 ).normalize();
+		var tmpQ = new THREE.Quaternion();
+		var currentQ = new THREE.Quaternion();
+
+		init();
+		animate();
 
 		function init() {
 
 			container = document.getElementById( 'container' );
 
 			camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
-			//camera.position.z = 20;
 
 			scene = new THREE.Scene();
 			scene.background = new THREE.Color( 0x101010 );
 
-			renderer = new THREE.WebGLRenderer();
-
 			// geometry
 
 			var instances = 5000;
 
-			var geometry = new THREE.InstancedBufferGeometry();
+			var bufferGeometry = new THREE.BoxBufferGeometry( 2, 2, 2 );
 
-			// per mesh data
-			var vertices = new THREE.BufferAttribute( new Float32Array( [
-				// Front
-				-1, 1, 1,
-				1, 1, 1,
-				-1, -1, 1,
-				1, -1, 1,
-				// Back
-				1, 1, -1,
-				-1, 1, -1,
-				1, -1, -1,
-				-1, -1, -1,
-				// Left
-				-1, 1, -1,
-				-1, 1, 1,
-				-1, -1, -1,
-				-1, -1, 1,
-				// Right
-				1, 1, 1,
-				1, 1, -1,
-				1, -1, 1,
-				1, -1, -1,
-				// Top
-				-1, 1, 1,
-				1, 1, 1,
-				-1, 1, -1,
-				1, 1, -1,
-				// Bottom
-				1, -1, 1,
-				-1, -1, 1,
-				1, -1, -1,
-				-1, -1, -1
-			] ), 3 );
-
-			geometry.addAttribute( 'position', vertices );
-
-			var uvs = new THREE.BufferAttribute( new Float32Array( [
-						//x	y	z
-						// Front
-						0, 0,
-						1, 0,
-						0, 1,
-						1, 1,
-						// Back
-						1, 0,
-						0, 0,
-						1, 1,
-						0, 1,
-						// Left
-						1, 1,
-						1, 0,
-						0, 1,
-						0, 0,
-						// Right
-						1, 0,
-						1, 1,
-						0, 0,
-						0, 1,
-						// Top
-						0, 0,
-						1, 0,
-						0, 1,
-						1, 1,
-						// Bottom
-						1, 0,
-						0, 0,
-						1, 1,
-						0, 1
-			] ), 2 );
-
-			geometry.addAttribute( 'uv', uvs );
-
-			var indices = new Uint16Array( [
-				0, 1, 2,
-				2, 1, 3,
-				4, 5, 6,
-				6, 5, 7,
-				8, 9, 10,
-				10, 9, 11,
-				12, 13, 14,
-				14, 13, 15,
-				16, 17, 18,
-				18, 17, 19,
-				20, 21, 22,
-				22, 21, 23
-			] );
-
-			geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
+			// copying data from a simple box geometry, but you can specify a custom geometry if you want
+
+			var geometry = new THREE.InstancedBufferGeometry();
+			geometry.index = bufferGeometry.index;
+			geometry.attributes.position = bufferGeometry.attributes.position;
+			geometry.attributes.uv = bufferGeometry.attributes.uv;
 
 			// per instance data
-			var offsets = new THREE.InstancedBufferAttribute( new Float32Array( instances * 3 ), 3, 1 );
+
+			var offsets = [];
+			var orientations = [];
 
 			var vector = new THREE.Vector4();
-			for ( var i = 0, ul = offsets.count; i < ul; i++ ) {
-				var x = Math.random() * 100 - 50;
-				var y = Math.random() * 100 - 50;
-				var z = Math.random() * 100 - 50;
-				vector.set( x, y, z, 0 ).normalize();
-				// move out at least 5 units from center in current direction
-				offsets.setXYZ( i, x + vector.x * 5, y + vector.y * 5, z + vector.z * 5 );
+			var x, y, z, w;
 
-			}
+			for ( var i = 0; i < instances; i ++ ) {
 
-			geometry.addAttribute( 'offset', offsets ); // per mesh translation
+				// offsets
 
+				x = Math.random() * 100 - 50;
+				y = Math.random() * 100 - 50;
+				z = Math.random() * 100 - 50;
 
-			orientations = new THREE.InstancedBufferAttribute( new Float32Array( instances * 4 ), 4, 1 ).setDynamic( true );
+				vector.set( x, y, z, 0 ).normalize();
+				vector.multiplyScalar( 5 ); // move out at least 5 units from center in current direction
 
-			for ( var i = 0, ul = orientations.count; i < ul; i++ ) {
+				offsets.push( x + vector.x, y + vector.y, z + vector.z );
 
-				vector.set( Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1, Math.random() * 2 - 1 );
-				vector.normalize();
+				// orientations
 
-				orientations.setXYZW( i, vector.x, vector.y, vector.z, vector.w );
+				x = Math.random() * 2 - 1;
+				y = Math.random() * 2 - 1;
+				z = Math.random() * 2 - 1;
+				w = Math.random() * 2 - 1;
+
+				vector.set( x, y, z, w ).normalize();
+
+				orientations.push( vector.x, vector.y, vector.z, vector.w );
 
 			}
 
-			geometry.addAttribute( 'orientation', orientations ); // per mesh orientation
+			offsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 );
+			orientationAttribute = new THREE.InstancedBufferAttribute( new Float32Array( orientations ), 4 ).setDynamic( true );
+
+			geometry.addAttribute( 'offset', offsetAttribute );
+			geometry.addAttribute( 'orientation', orientationAttribute );
 
 			// material
-			var texture = new THREE.TextureLoader().load( 'textures/crate.gif' );
-			texture.anisotropy = renderer.getMaxAnisotropy();
 
 			var material = new THREE.RawShaderMaterial( {
 
 				uniforms: {
-					map: { value: texture }
+					map: { value: new THREE.TextureLoader().load( 'textures/crate.gif' ) }
 				},
 				vertexShader: document.getElementById( 'vertexShader' ).textContent,
-				fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
-				side: THREE.DoubleSide,
-				transparent: false
+				fragmentShader: document.getElementById( 'fragmentShader' ).textContent
 
 			} );
 
-			var mesh = new THREE.Mesh( geometry, material );
+			mesh = new THREE.Mesh( geometry, material );
 			scene.add( mesh );
 
+			renderer = new THREE.WebGLRenderer();
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
 
 			if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === false ) {
-				document.getElementById( "notSupported" ).style.display = "";
+
+				document.getElementById( 'notSupported' ).style.display = '';
 				return;
-			}
 
-			renderer.setPixelRatio( window.devicePixelRatio );
-			renderer.setSize( window.innerWidth, window.innerHeight );
-			container.appendChild( renderer.domElement );
+			}
 
 			stats = new Stats();
 			container.appendChild( stats.dom );
@@ -298,38 +226,31 @@
 
 		}
 
-		var lastTime = 0;
-
-		var moveQ = ( new THREE.Quaternion( .5, .5, .5, 0.0 ) ).normalize();
-		var tmpQ = new THREE.Quaternion();
-		var currentQ = new THREE.Quaternion();
 		function render() {
 
 			var time = performance.now();
 
-			var object = scene.children[0];
-
-			object.rotation.y = time * 0.00005;
-
-			renderer.render( scene, camera );
+			mesh.rotation.y = time * 0.00005;
 
 			var delta = ( time - lastTime ) / 5000;
 			tmpQ.set( moveQ.x * delta, moveQ.y * delta, moveQ.z * delta, 1 ).normalize();
 
-			for ( var i = 0, ul = orientations.count; i < ul; i++ ) {
-				var index = i * 4;
-				currentQ.set( orientations.array[index], orientations.array[index + 1], orientations.array[index + 2], orientations.array[index + 3] );
+			for ( var i = 0, il = orientationAttribute.count; i < il; i ++ ) {
+
+				currentQ.fromArray( orientationAttribute.array, ( i * 4 ) );
 				currentQ.multiply( tmpQ );
 
-				orientations.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
+				orientationAttribute.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
 
 			}
-			orientations.needsUpdate = true;
+
+			orientationAttribute.needsUpdate = true;
 			lastTime = time;
+
+			renderer.render( scene, camera );
+
 		}
 
-		init();
-		animate();
 	</script>
 
 </body>

+ 9 - 12
examples/webgl_buffergeometry_lines.html

@@ -62,14 +62,13 @@
 
 				scene = new THREE.Scene();
 
-
 				var segments = 10000;
 
 				var geometry = new THREE.BufferGeometry();
-				var material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
+				var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
-				var positions = new Float32Array( segments * 3 );
-				var colors = new Float32Array( segments * 3 );
+				var positions = [];
+				var colors = [];
 
 				var r = 800;
 
@@ -81,20 +80,18 @@
 
 					// positions
 
-					positions[ i * 3 ] = x;
-					positions[ i * 3 + 1 ] = y;
-					positions[ i * 3 + 2 ] = z;
+					positions.push( x, y, z );
 
 					// colors
 
-					colors[ i * 3 ] = ( x / r ) + 0.5;
-					colors[ i * 3 + 1 ] = ( y / r ) + 0.5;
-					colors[ i * 3 + 2 ] = ( z / r ) + 0.5;
+					colors.push( ( x / r ) + 0.5 );
+					colors.push( ( y / r ) + 0.5 );
+					colors.push( ( z / r ) + 0.5 );
 
 				}
 
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
 				geometry.computeBoundingSphere();
 

+ 67 - 54
examples/webgl_buffergeometry_lines_indexed.html

@@ -61,81 +61,95 @@
 				scene = new THREE.Scene();
 
 				var geometry = new THREE.BufferGeometry();
-				var material = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });
+				var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } );
 
+				var indices = [];
 				var positions = [];
-				var next_positions_index = 0;
 				var colors = [];
-				var indices_array = [];
+
+				var next_positions_index = 0;
 
 				//
 
 				var iteration_count = 4;
 				var rangle = 60 * Math.PI / 180.0;
 
-				function add_vertex(v) {
+				function add_vertex( v ) {
+
+					if ( next_positions_index == 0xffff ) console.error( 'Too many points.' );
+
+					positions.push( v.x, v.y, v.z );
+					colors.push( Math.random() * 0.5 + 0.5, Math.random() * 0.5 + 0.5, 1 );
 
-					if (next_positions_index == 0xffff) throw new Error("Too many points");
+					return next_positions_index ++;
 
-					positions.push(v.x, v.y, v.z);
-					colors.push(Math.random()*0.5+0.5, Math.random()*0.5+0.5, 1);
-					return next_positions_index++;
 				}
 
 				// simple Koch curve
-				function snowflake_iteration(p0, p4, depth) {
+				
+				function snowflake_iteration( p0, p4, depth ) {
 
-					if (--depth < 0) {
+					if ( -- depth < 0 ) {
+
+						var i = next_positions_index - 1; // p0 already there
+						add_vertex( p4 );
+						indices.push( i, i + 1 );
 
-						var i = next_positions_index-1; // p0 already there
-						add_vertex(p4);
-						indices_array.push(i, i+1);
 						return;
+
 					}
 
-					var v = p4.clone().sub(p0);
-					var v_tier = v.clone().multiplyScalar(1.0/3.0);
-					var p1 = p0.clone().add(v_tier);
+					var v = p4.clone().sub( p0 );
+					var v_tier = v.clone().multiplyScalar( 1 / 3 );
+					var p1 = p0.clone().add( v_tier );
 
-					var angle = Math.atan2(v.y, v.x) + rangle;
+					var angle = Math.atan2( v.y, v.x ) + rangle;
 					var length = v_tier.length();
 					var p2 = p1.clone();
-					p2.x += Math.cos(angle) * length;
-					p2.y += Math.sin(angle) * length;
+					p2.x += Math.cos( angle ) * length;
+					p2.y += Math.sin( angle ) * length;
 
-					var p3 = p0.clone().add(v_tier).add(v_tier);
+					var p3 = p0.clone().add( v_tier ).add( v_tier );
+
+					snowflake_iteration( p0, p1, depth );
+					snowflake_iteration( p1, p2, depth );
+					snowflake_iteration( p2, p3, depth );
+					snowflake_iteration( p3, p4, depth );
 
-					snowflake_iteration(p0, p1, depth);
-					snowflake_iteration(p1, p2, depth);
-					snowflake_iteration(p2, p3, depth);
-					snowflake_iteration(p3, p4, depth);
 				}
 
-				function snowflake(points, loop, x_offset) {
+				function snowflake( points, loop, x_offset ) {
+
+					for ( var iteration = 0; iteration != iteration_count; iteration ++ ) {
 
-					for (var iteration = 0; iteration != iteration_count; ++iteration) {
+						add_vertex( points[ 0 ] );
+
+						for ( var p_index=0, p_count = points.length - 1; p_index != p_count; p_index ++ ) {
+
+							snowflake_iteration( points[ p_index ], points[ p_index + 1 ], iteration );
 
-						add_vertex(points[0]);
-						for (var p_index=0, p_count=points.length-1; p_index != p_count; ++p_index) {
-							snowflake_iteration(points[p_index], points[p_index+1], iteration);
 						}
 
-						if (loop) snowflake_iteration(points[points.length-1], points[0], iteration);
+						if ( loop ) snowflake_iteration( points[ points.length - 1 ], points[ 0 ], iteration );
 
 						// translate input curve for next iteration
-						for (var p_index=0, p_count=points.length; p_index != p_count; ++p_index) {
+
+						for ( var p_index = 0, p_count = points.length; p_index != p_count; p_index ++ ) {
+
 							points[p_index].x += x_offset;
+
 						}
 
 					}
+
 				}
 
 				var y = 0;
 				snowflake
 				(
 					[
-						new THREE.Vector3(0, y+0, 0),
-						new THREE.Vector3(500, y+0, 0)
+						new THREE.Vector3( 0, y, 0 ),
+						new THREE.Vector3( 500, y, 0 )
 					],
 					false, 600
 				);
@@ -144,9 +158,9 @@
 				snowflake
 				(
 					[
-						new THREE.Vector3(0, y+0, 0),
-						new THREE.Vector3(250, y+400, 0),
-						new THREE.Vector3(500, y+0, 0)
+						new THREE.Vector3( 0, y, 0) ,
+						new THREE.Vector3( 250, y + 400, 0 ),
+						new THREE.Vector3( 500, y, 0 )
 					],
 					true, 600
 				);
@@ -155,10 +169,10 @@
 				snowflake
 				(
 					[
-						new THREE.Vector3(0, y+0, 0),
-						new THREE.Vector3(500, y, 0),
-						new THREE.Vector3(500, y+500, 0),
-						new THREE.Vector3(0, y+500, 0)
+						new THREE.Vector3( 0, y, 0 ),
+						new THREE.Vector3( 500, y, 0 ),
+						new THREE.Vector3( 500, y + 500, 0 ),
+						new THREE.Vector3( 0, y + 500, 0 )
 					],
 					true, 600
 				);
@@ -167,21 +181,22 @@
 				snowflake
 				(
 					[
-						new THREE.Vector3(250, y+0, 0),
-						new THREE.Vector3(500, y+0, 0),
-						new THREE.Vector3(250, y+0, 0),
-						new THREE.Vector3(250, y+250, 0),
-						new THREE.Vector3(250, y+0, 0),
-						new THREE.Vector3(0, y, 0),
-						new THREE.Vector3(250, y+0, 0),
-						new THREE.Vector3(250, y-250, 0),
-						new THREE.Vector3(250, y+0, 0)
+						new THREE.Vector3( 250, y, 0 ),
+						new THREE.Vector3( 500, y, 0 ),
+						new THREE.Vector3( 250, y, 0 ),
+						new THREE.Vector3( 250, y + 250, 0 ),
+						new THREE.Vector3( 250, y, 0 ),
+						new THREE.Vector3( 0, y, 0 ),
+						new THREE.Vector3( 250, y, 0 ),
+						new THREE.Vector3( 250, y - 250, 0 ),
+						new THREE.Vector3( 250, y, 0 )
 					],
 					false, 600
 				);
-				// --------------------------------
 
-				geometry.setIndex( indices_array );
+				//
+
+				geometry.setIndex( indices );
 				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
 				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 				geometry.computeBoundingSphere();
@@ -191,7 +206,7 @@
 				mesh.position.y -= 1200;
 
 				parent_node = new THREE.Object3D();
-				parent_node.add(mesh);
+				parent_node.add( mesh );
 
 				scene.add( parent_node );
 
@@ -239,8 +254,6 @@
 
 				var time = Date.now() * 0.001;
 
-				//mesh.rotation.x = time * 0.25;
-				//mesh.rotation.y = time * 0.5;
 				parent_node.rotation.z = time * 0.5;
 
 				renderer.render( scene, camera );

+ 7 - 11
examples/webgl_buffergeometry_points.html

@@ -69,14 +69,14 @@
 
 				var geometry = new THREE.BufferGeometry();
 
-				var positions = new Float32Array( particles * 3 );
-				var colors = new Float32Array( particles * 3 );
+				var positions = [];
+				var colors = [];
 
 				var color = new THREE.Color();
 
 				var n = 1000, n2 = n / 2; // particles spread in the cube
 
-				for ( var i = 0; i < positions.length; i += 3 ) {
+				for ( var i = 0; i < particles; i ++ ) {
 
 					// positions
 
@@ -84,9 +84,7 @@
 					var y = Math.random() * n - n2;
 					var z = Math.random() * n - n2;
 
-					positions[ i ]     = x;
-					positions[ i + 1 ] = y;
-					positions[ i + 2 ] = z;
+					positions.push( x, y, z );
 
 					// colors
 
@@ -96,14 +94,12 @@
 
 					color.setRGB( vx, vy, vz );
 
-					colors[ i ]     = color.r;
-					colors[ i + 1 ] = color.g;
-					colors[ i + 2 ] = color.b;
+					colors.push( color.r, color.g, color.b );
 
 				}
 
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
+				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+				geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
 				geometry.computeBoundingSphere();
 

+ 16 - 17
examples/webgl_buffergeometry_rawshader.html

@@ -113,30 +113,29 @@
 
 				var geometry = new THREE.BufferGeometry();
 
-				var vertices = new Float32Array( triangles * 3 * 3 );
+				var positions = [];
+				var colors = [];
 
-				for ( var i = 0, l = triangles * 3 * 3; i < l; i += 3 ) {
+				for ( var i = 0; i < triangles; i ++ ) {
 
-					vertices[ i     ] = Math.random() - 0.5;
-					vertices[ i + 1 ] = Math.random() - 0.5;
-					vertices[ i + 2 ] = Math.random() - 0.5;
+					positions.push( Math.random() - 0.5 );
+					positions.push( Math.random() - 0.5 );
+					positions.push( Math.random() - 0.5 );
 
-				}
-
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
-
-				var colors = new Uint8Array( triangles * 3 * 4 );
+					colors.push( Math.random() * 255 );
+					colors.push( Math.random() * 255 );
+					colors.push( Math.random() * 255 );
+					colors.push( Math.random() * 255 );
 
-				for ( var i = 0, l = triangles * 3 * 4; i < l; i += 4 ) {
+				}
 
-					colors[ i     ] = Math.random() * 255;
-					colors[ i + 1 ] = Math.random() * 255;
-					colors[ i + 2 ] = Math.random() * 255;
-					colors[ i + 3 ] = Math.random() * 255;
+				var positionAttribute = new THREE.Float32BufferAttribute( positions, 3 );
+				var colorAttribute = new THREE.Uint8BufferAttribute( colors, 4 );
 
-				}
+				colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader
 
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 4, true ) );
+				geometry.addAttribute( 'position', positionAttribute );
+				geometry.addAttribute( 'color', colorAttribute );
 
 				// material
 

+ 46 - 50
examples/webgl_buffergeometry_selective_draw.html

@@ -1,42 +1,30 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-	<title>three.js - BufferGeometry selectively drawn using attributes and a shader</title>
+	<title>three.js webgl - buffergeometry - selective - draw</title>
 	<meta charset="utf-8">
 	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 	<style>
-		body {
-			background-color: #000;
-			font-family: Monospace;
-			margin: 0;
-			color: #000;
-			overflow: hidden;
-		}
-		a {
-			color:#ff0;
-		}
-		#title {
-			position: absolute;
-			width: 100%;
-			top: 0;
-			height: 20px;
-			padding: 0;
-			text-align: center;
-			font-size: 1.1em;
-			background-color: rgba(64,96,64,0.7);
-			color: #fff;
-		}
-        #ui {
-        	position:absolute;
-        	left:0;
-        	top: 20px;
-        	padding: 0;
-        	text-align: center;
-        	width:100%;
-        	height:20px;
-        	color: #fff;
-        	background-color: rgba(64,96,64,0.6);
-		}
+	body {
+		color: #cccccc;
+		font-family:Monospace;
+		font-size:13px;
+		text-align:center;
+
+		background-color: #050505;
+		margin: 0px;
+		overflow: hidden;
+	}
+
+	#info {
+		position: absolute;
+		top: 0px; width: 100%;
+		padding: 5px;
+	}
+
+	a {
+		color: #0080ff;
+	}
 	</style>
 	<script type="text/javascript" src="../build/three.js"></script>
 	<script type="text/javascript" src="js/Detector.js"></script>
@@ -49,9 +37,11 @@
 		varying vec3 vColor;
 
 		void main() {
+
 			vColor = vertColor;
 			vVisible = visible;
-			gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
+			gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+
 		}
 	</script>
 	<script type="x-shader/x-fragment" id="fragmentshader">
@@ -59,36 +49,39 @@
 		varying vec3 vColor;
 
 		void main() {
+
 			if ( vVisible > 0.0 ) {
+
 				gl_FragColor = vec4( vColor, 1.0 );
+
 			} else {
+
 				discard;
+
 			}
+			
 		}
 	</script>
 	</head>
-	<body onload="app()">
-		<div id="title"></div>
-		<div id="ui"><a href="#" onclick="hideLines();">CULL SOME LINES</a> - <a href="#" onclick="showAllLines();">SHOW ALL LINES</a></div>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> buffergeometry - selective - draw
+			<div id="title"></div>
+			<div id="ui"><a href="#" onclick="hideLines();">CULL SOME LINES</a> - <a href="#" onclick="showAllLines();">SHOW ALL LINES</a></div>
+		</div>
+
 		<script>
+
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
 		var camera, scene, renderer, stats;
 		var geometry, mesh;
 		var numLat = 100;
 		var numLng = 200;
 		var numLinesCulled = 0;
 
-		function app() {
-
-			if ( ! Detector.webgl ) {
-
-				Detector.addGetWebGLMessage();
-
-			}
-
-			init();
-			animate();
-
-		}
+		init();
+		animate();
 
 		function init() {
 
@@ -177,8 +170,9 @@
 
 		function updateCount() {
 
-			var str = 'BufferGeometry selective drawing: 1 draw call, ' + numLat * numLng + ' lines, ' + numLinesCulled + ' culled (<a target="_blank" href="http://callum.com">author</a>)';
+			var str = '1 draw call, ' + numLat * numLng + ' lines, ' + numLinesCulled + ' culled (<a target="_blank" href="http://callum.com">author</a>)';
 			document.getElementById( 'title' ).innerHTML = str.replace( /\B(?=(\d{3})+(?!\d))/g, "," );
+
 		}
 
 		function hideLines() {
@@ -199,6 +193,7 @@
 				}
 
 			}
+
 			geometry.attributes.visible.needsUpdate = true;
 
 			updateCount();
@@ -226,6 +221,7 @@
 
 			camera.aspect = window.innerWidth / window.innerHeight;
 			camera.updateProjectionMatrix();
+
 			renderer.setSize( window.innerWidth, window.innerHeight );
 
 		}

+ 24 - 50
examples/webgl_buffergeometry_uint.html

@@ -82,22 +82,14 @@
 
 				var geometry = new THREE.BufferGeometry();
 
-				var indices = new Uint32Array( triangles * 3 );
-
-				for ( var i = 0; i < indices.length; i ++ ) {
-
-					indices[ i ] = i;
-
-				}
-
-				var positions = new Float32Array( triangles * 3 * 3 );
-				var normals = new Int16Array( triangles * 3 * 3 );
-				var colors = new Uint8Array( triangles * 3 * 3 );
+				var positions = [];
+				var normals = [];
+				var colors = [];
 
 				var color = new THREE.Color();
 
-				var n = 800, n2 = n/2;	// triangles spread in the cube
-				var d = 12, d2 = d/2;	// individual triangle size
+				var n = 800, n2 = n / 2;	// triangles spread in the cube
+				var d = 12, d2 = d / 2;	// individual triangle size
 
 				var pA = new THREE.Vector3();
 				var pB = new THREE.Vector3();
@@ -106,7 +98,7 @@
 				var cb = new THREE.Vector3();
 				var ab = new THREE.Vector3();
 
-				for ( var i = 0; i < positions.length; i += 9 ) {
+				for ( var i = 0; i < triangles; i ++ ) {
 
 					// positions
 
@@ -126,17 +118,9 @@
 					var cy = y + Math.random() * d - d2;
 					var cz = z + Math.random() * d - d2;
 
-					positions[ i ]     = ax;
-					positions[ i + 1 ] = ay;
-					positions[ i + 2 ] = az;
-
-					positions[ i + 3 ] = bx;
-					positions[ i + 4 ] = by;
-					positions[ i + 5 ] = bz;
-
-					positions[ i + 6 ] = cx;
-					positions[ i + 7 ] = cy;
-					positions[ i + 8 ] = cz;
+					positions.push( ax, ay, az );
+					positions.push( bx, by, bz );
+					positions.push( cx, cy, cz );
 
 					// flat face normals
 
@@ -154,17 +138,9 @@
 					var ny = cb.y;
 					var nz = cb.z;
 
-					normals[ i ]     = nx * 32767;
-					normals[ i + 1 ] = ny * 32767;
-					normals[ i + 2 ] = nz * 32767;
-
-					normals[ i + 3 ] = nx * 32767;
-					normals[ i + 4 ] = ny * 32767;
-					normals[ i + 5 ] = nz * 32767;
-
-					normals[ i + 6 ] = nx * 32767;
-					normals[ i + 7 ] = ny * 32767;
-					normals[ i + 8 ] = nz * 32767;
+					normals.push( nx * 32767, ny * 32767, nz * 32767 );
+					normals.push( nx * 32767, ny * 32767, nz * 32767 );
+					normals.push( nx * 32767, ny * 32767, nz * 32767 );
 
 					// colors
 
@@ -174,24 +150,22 @@
 
 					color.setRGB( vx, vy, vz );
 
-					colors[ i ]     = color.r * 255;
-					colors[ i + 1 ] = color.g * 255;
-					colors[ i + 2 ] = color.b * 255;
+					colors.push( color.r * 255, color.g * 255, color.b * 255 );
+					colors.push( color.r * 255, color.g * 255, color.b * 255 );
+					colors.push( color.r * 255, color.g * 255, color.b * 255 );
 
-					colors[ i + 3 ] = color.r * 255;
-					colors[ i + 4 ] = color.g * 255;
-					colors[ i + 5 ] = color.b * 255;
+				}
 
-					colors[ i + 6 ] = color.r * 255;
-					colors[ i + 7 ] = color.g * 255;
-					colors[ i + 8 ] = color.b * 255;
+				var positionAttribute = new THREE.Float32BufferAttribute( positions, 3 );
+				var normalAttribute = new THREE.Int16BufferAttribute( normals, 3 );
+				var colorAttribute = new THREE.Uint8BufferAttribute( colors, 3 );
 
-				}
+				normalAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader
+				colorAttribute.normalized = true;
 
-				geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
-				geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-				geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3, true ) );
-				geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3, true ) );
+				geometry.addAttribute( 'position', positionAttribute );
+				geometry.addAttribute( 'normal', normalAttribute );
+				geometry.addAttribute( 'color', colorAttribute );
 
 				geometry.computeBoundingSphere();
 

+ 2 - 3
examples/webgl_loader_3ds.html

@@ -59,19 +59,18 @@
 				directionalLight.position.set( 0, 0, 2 );
 				scene.add( directionalLight );
 
+				//3ds files dont store normal maps
 				var loader = new THREE.TextureLoader();
-
-				var texture = loader.load( 'models/3ds/portalgun/textures/color.jpg' );
 				var normal = loader.load( 'models/3ds/portalgun/textures/normal.jpg' );
 
 				var loader = new THREE.TDSLoader( );
+				loader.setPath( 'models/3ds/portalgun/textures/' );
 				loader.load( 'models/3ds/portalgun/portalgun.3ds', function ( object ) {
 
 					object.traverse( function ( child ) {
 
 						if ( child instanceof THREE.Mesh ) {
 
-							child.material.map = texture;
 							child.material.normalMap = normal;
 						}
 

+ 1 - 1
examples/webgl_loader_assimp.html

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

+ 35 - 45
examples/webgl_loader_assimp2json.html

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

+ 6 - 8
examples/webgl_loader_gltf2.html → examples/webgl_loader_gltf.html

@@ -91,13 +91,12 @@
 				<option value="glTF-Binary">None (Binary)</option>
 				<option value="glTF-MaterialsCommon">Common Materials</option>
 				<option value="glTF-pbrSpecularGlossiness">Specular-Glossiness (PBR)</option>
-				<option value="glTF-techniqueWebGL">GLSL</option>
 			</select>
 		</div>
 	</div>
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
-		<script src="js/loaders/GLTF2Loader.js"></script>
+		<script src="js/loaders/GLTFLoader.js"></script>
 
 		<script>
 			var orbitControls = null;
@@ -203,7 +202,7 @@
 					scene.add(ground);
 				}
 
-				loader = new THREE.GLTF2Loader();
+				loader = new THREE.GLTFLoader();
 
 				for (var i = 0; i < extensionSelect.children.length; i++) {
 					var child = extensionSelect.children[i];
@@ -436,7 +435,6 @@
 					addLights:true,
 					addGround:true,
 					shadows:true,
-					// TODO: 'glTF-techniqueWebGL'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-MaterialsCommon', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -449,7 +447,7 @@
 					addLights:true,
 					shadows:true,
 					addGround:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -459,7 +457,7 @@
 					 addLights:true,
 					 addGround:true,
 					 shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -469,7 +467,7 @@
 					addLights:true,
 					addGround:true,
 					shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{
@@ -479,7 +477,7 @@
 					objectRotation: new THREE.Euler(0, 90, 0),
 					addLights:true,
 					shadows:true,
-					// TODO: 'glTF-MaterialsCommon', 'glTF-techniqueWebGL'
+					// TODO: 'glTF-MaterialsCommon'
 					extensions: ['glTF', 'glTF-Embedded', 'glTF-pbrSpecularGlossiness', 'glTF-Binary']
 				},
 				{

+ 4 - 1
examples/webgl_materials_cubemap_dynamic2.html

@@ -142,7 +142,10 @@
 
 			function onDocumentMouseWheel( event ) {
 
-				camera.fov += ( event.deltaY * 0.05 );
+				var fov = camera.fov + event.deltaY * 0.05;
+
+				camera.fov = THREE.Math.clamp( fov, 10, 75 );
+
 				camera.updateProjectionMatrix();
 
 			}

+ 3 - 1
examples/webgl_panorama_dualfisheye.html

@@ -98,7 +98,7 @@
 				var texture = new THREE.TextureLoader().load( 'textures/ricoh_theta_s.jpg' );
 				texture.format = THREE.RGBFormat;
 
-				var material   = new THREE.MeshBasicMaterial( { map: texture } );
+				var material = new THREE.MeshBasicMaterial( { map: texture } );
 
 				mesh = new THREE.Mesh( geometry, material );
 				scene.add( mesh );
@@ -163,6 +163,8 @@
 
 				distance += event.deltaY * 0.05;
 
+				distance = THREE.Math.clamp( distance, 400, 1000 );
+
 			}
 
 			function animate() {

+ 4 - 1
examples/webgl_panorama_equirectangular.html

@@ -169,7 +169,10 @@
 
 			function onDocumentMouseWheel( event ) {
 
-				camera.fov += event.deltaY * 0.05;
+				var fov = camera.fov + event.deltaY * 0.05;
+
+				camera.fov = THREE.Math.clamp( fov, 10, 75 );
+
 				camera.updateProjectionMatrix();
 
 			}

+ 1 - 1
examples/webgl_physics_terrain.html

@@ -128,7 +128,7 @@
 
 						controls = new THREE.OrbitControls( camera );
 
-						var geometry = new THREE.PlaneBufferGeometry( 100, 100, terrainWidth - 1, terrainDepth - 1 );
+						var geometry = new THREE.PlaneBufferGeometry( terrainWidthExtents, terrainDepthExtents, terrainWidth - 1, terrainDepth - 1 );
 						geometry.rotateX( -Math.PI / 2 );
 
 						var vertices = geometry.attributes.position.array;

+ 98 - 0
examples/webgl_postprocessing_fxaa.html

@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing - FXAA 3.11</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #61443e;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				/* background-color: #bfd1e5; */
+				background-color: #ffffff;
+				margin: 0px;
+			}
+
+			a {	color: #a06851;	}
+
+			canvas {
+				margin: 20px;
+				margin-top: 40px;
+			}
+
+		</style>
+	</head>
+	<body>
+
+        <div id="info">
+            <p><a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - FXAA example </p>
+            <p>original image (left) - FXAA 3.11 post-processing antialiasing (right)</p>
+			<p>Image source: <a href="https://github.com/mattdesl/glsl-fxaa" target="_blank" rel="noopener">glsl-fxaa by mattdesl</a></p>
+        </div>
+
+		<div id="container"></div>
+
+
+		<script src="../build/three.js"></script>
+		<script src="js/shaders/FXAAShader.js"></script>
+
+		<script src="js/Detector.js"></script>
+
+		<script>
+
+			( function closure() {
+
+				'use strict';
+
+				if ( ! Detector.webgl ) {
+					Detector.addGetWebGLMessage();
+				}
+
+				var loader = new THREE.TextureLoader();
+				loader.load( "textures/fxaa_scene.png", function onLoad( texture ) {
+					var image = texture.image;
+					texture.minFilter = THREE.LinearFilter;
+					texture.magFilter = THREE.LinearFilter;
+
+					var fxaaMaterial = new THREE.ShaderMaterial( THREE.FXAAShader );
+					fxaaMaterial.uniforms.tDiffuse.value = texture;
+					fxaaMaterial.uniforms.resolution.value.x = 1 / image.naturalWidth;
+					fxaaMaterial.uniforms.resolution.value.y = 1 / image.naturalHeight;
+
+					var basicMaterial = new THREE.MeshBasicMaterial( { map: texture } );
+
+					var plane = new THREE.PlaneBufferGeometry( 1, 1 );
+
+					var quad1 = new THREE.Mesh( plane, basicMaterial );
+					var quad2 = new THREE.Mesh( plane, fxaaMaterial );
+					var scene1 = new THREE.Scene();
+					var scene2 = new THREE.Scene();
+					scene1.add( quad1 );
+					scene2.add( quad2 );
+
+					var camera = new THREE.OrthographicCamera( -0.5, 0.5, 0.5, -0.5, -0.5, 0.5 );
+
+					var container = document.getElementById( 'container' );
+
+
+					var renderer1 = new THREE.WebGLRenderer();
+					renderer1.setPixelRatio( window.devicePixelRatio );
+					renderer1.setSize( image.naturalWidth, image.naturalHeight );
+					container.appendChild( renderer1.domElement );
+
+					var renderer2 = new THREE.WebGLRenderer();
+					renderer2.setPixelRatio( window.devicePixelRatio );
+					renderer2.setSize( image.naturalWidth, image.naturalHeight );
+					container.appendChild( renderer2.domElement );
+
+					renderer1.render( scene1, camera );
+					renderer2.render( scene2, camera );
+				});
+			})();
+
+		</script>
+	</body>
+</html>

+ 147 - 147
examples/webgl_postprocessing_outline.html

@@ -43,7 +43,7 @@
 		<script src="js/shaders/FXAAShader.js"></script>
 		<script src="js/postprocessing/EffectComposer.js"></script>
 		<script src="js/postprocessing/RenderPass.js"></script>
-    <script src="js/postprocessing/ShaderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
 		<script src="js/postprocessing/OutlinePass.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 		<script src='js/libs/dat.gui.min.js'></script>
@@ -58,7 +58,6 @@
 
 			var container, stats;
 			var camera, scene, renderer, controls;
-			var mesh, decal;
 			var raycaster = new THREE.Raycaster();
 
 			var mouse = new THREE.Vector2();
@@ -67,7 +66,7 @@
 			var composer, effectFXAA, outlinePass;
 			var obj3d = new THREE.Object3D();
 
-			var group = new THREE.Object3D();
+			var group = new THREE.Group();
 
 			var params = {
 				edgeStrength: 3.0,
@@ -79,68 +78,61 @@
 			};
 
 			// Init gui
-			var gui = new dat.GUI();
-			gui.add(params, "edgeStrength", 0.01, 10).onChange(function( value ) {
-				outlinePass.edgeStrength = Number(value);
-			});
-			gui.add(params, "edgeGlow", 0.0, 1).onChange(function( value ) {
-				outlinePass.edgeGlow = Number(value);
-			});
-			gui.add(params, "edgeThickness", 1, 4).onChange(function( value ) {
-				outlinePass.edgeThickness = Number(value);
-			});
-			gui.add(params, "pulsePeriod", 0.0, 5).onChange(function( value ) {
-				outlinePass.pulsePeriod = Number(value);
-			});
-			gui.add(params, "rotate");
-			gui.add(params, "usePatternTexture").onChange(function( value ) {
+
+			var gui = new dat.GUI( { width: 300 } );
+
+			gui.add( params, 'edgeStrength', 0.01, 10 ).onChange( function ( value ) {
+
+				outlinePass.edgeStrength = Number( value );
+
+			} );
+
+			gui.add( params, 'edgeGlow', 0.0, 1 ).onChange( function ( value ) {
+
+				outlinePass.edgeGlow = Number( value );
+
+			} );
+
+			gui.add( params, 'edgeThickness', 1, 4 ).onChange( function ( value ) {
+
+				outlinePass.edgeThickness = Number( value );
+
+			} );
+
+			gui.add( params, 'pulsePeriod', 0.0, 5 ).onChange( function ( value ) {
+
+				outlinePass.pulsePeriod = Number( value );
+
+			} );
+
+			gui.add( params, 'rotate' );
+
+			gui.add( params, 'usePatternTexture' ).onChange( function ( value ) {
+
 				outlinePass.usePatternTexture = value;
-			});
-			var Configuration = function() {
-				this.visibleEdgeColor = "#ffffff";
-				this.hiddenEdgeColor  = "#190a05";
+
+			} );
+
+			var Configuration = function () {
+
+				this.visibleEdgeColor = '#ffffff';
+				this.hiddenEdgeColor = '#190a05';
+
 			};
+
 			var conf = new Configuration();
 
-			var controladorVisible = gui.addColor( conf, 'visibleEdgeColor');
-				var controladorHidden = gui.addColor( conf, 'hiddenEdgeColor');
-			controladorVisible.onChange(function( colorValue ) {
-				//the return value by the chooser is like as: #ffff
-				colorValue = colorValue.replace('#', '');
-				function hexToRgb( hex ) {
-					var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
-					return result ? {
-						r: parseInt(result[ 1 ], 16),
-						g: parseInt(result[ 2 ], 16),
-						b: parseInt(result[ 3 ], 16)
-					} : null;
-				}
+			var controllerVisible = gui.addColor( conf, 'visibleEdgeColor' ).onChange( function ( value ) {
 
-				var rgba  = hexToRgb(colorValue);
-				var color = outlinePass.visibleEdgeColor;
-				color.r   = rgba.r / 255;
-				color.g   = rgba.g / 255;
-				color.b   = rgba.b / 255;
-			});
-
-			controladorHidden.onChange(function( colorValue ) {
-				//the return value by the chooser is like as: #ffff
-				colorValue = colorValue.replace('#', '');
-				function hexToRgb( hex ) {
-					var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
-					return result ? {
-						r: parseInt(result[ 1 ], 16),
-						g: parseInt(result[ 2 ], 16),
-						b: parseInt(result[ 3 ], 16)
-					} : null;
-				}
+				outlinePass.visibleEdgeColor.set( value );
+
+			} );
+
+			var controllerHidden = gui.addColor( conf, 'hiddenEdgeColor' ).onChange( function ( value ) {
 
-				var rgba  = hexToRgb(colorValue);
-				var color = outlinePass.hiddenEdgeColor;
-				color.r   = rgba.r / 255;
-				color.g   = rgba.g / 255;
-				color.b   = rgba.b / 255;
-			});
+				outlinePass.hiddenEdgeColor.set( value );
+
+			} );
 
 			init();
 			animate();
@@ -150,116 +142,113 @@
 				container = document.createElement( 'div' );
 				document.body.appendChild( container );
 
-				var width = window.innerWidth || 1;
-				var height = window.innerHeight || 1;
-				var devicePixelRatio = window.devicePixelRatio || 1;
+				var width = window.innerWidth;
+				var height = window.innerHeight;
 
 				renderer = new THREE.WebGLRenderer( { antialias: false } );
 				renderer.shadowMap.enabled = true;
-				renderer.setClearColor( 0xa0a0a0 );
-				renderer.setPixelRatio( 1 );
+				// todo - support pixelRatio in this demo
 				renderer.setSize( width, height );
 				document.body.appendChild( renderer.domElement );
 
+				scene = new THREE.Scene();
+
 				camera = new THREE.PerspectiveCamera( 45, width / height, 0.1, 100 );
-				camera.position.z = 8;
-				camera.position.x = 0;
+				camera.position.set( 0, 0, 8 );
 
-				scene = new THREE.Scene();
+				controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.minDistance = 5;
+				controls.maxDistance = 20;
+				controls.enablePan = false;
+				controls.enableDamping = true;
+				controls.dampingFactor = 0.25;
+
+				//
+
+				scene.add( new THREE.AmbientLight( 0xaaaaaa, 0.2 ) );
+
+				var light = new THREE.DirectionalLight( 0xddffdd, 0.6 );
+				light.position.set( 1, 1, 1 );
+
+				light.castShadow = true;
+
+				light.shadow.mapSize.width = 1024;
+				light.shadow.mapSize.height = 1024;
+
+				var d = 10;
+
+				light.shadow.camera.left = - d;
+				light.shadow.camera.right = d;
+				light.shadow.camera.top = d;
+				light.shadow.camera.bottom = - d;
+
+				light.shadow.camera.far = 1000;
+
+				scene.add( light );
+
+				// model
 
 				var manager = new THREE.LoadingManager();
+
 				manager.onProgress = function ( item, loaded, total ) {
+
 					console.log( item, loaded, total );
+
 				};
-				// model
+
 				var loader = new THREE.OBJLoader( manager );
 				loader.load( 'models/obj/tree.obj', function ( object ) {
+
 					var scale = 1.0;
+
 					object.traverse( function ( child ) {
+
 						if ( child instanceof THREE.Mesh ) {
+
 							child.geometry.center();
 							child.geometry.computeBoundingSphere();
-							scale = 0.2*child.geometry.boundingSphere.radius;
-							var phongMaterial = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 5 } );
+							scale = 0.2 * child.geometry.boundingSphere.radius;
+
+							var phongMaterial = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x111111, shininess: 5 } );
 							child.material = phongMaterial;
-							child.material.side = THREE.DoubleSide;
 							child.receiveShadow = true;
 							child.castShadow = true;
+
 						}
+
 					} );
+
 					object.position.y = 1;
-					object.scale.x /= scale;
-					object.scale.y /= scale;
-					object.scale.z /= scale;
+					object.scale.divideScalar( scale );
 					obj3d.add( object );
-				} );
-				group.add(obj3d);
 
-				controls = new THREE.OrbitControls( camera, renderer.domElement );
-				controls.enableDamping = true;
-				controls.dampingFactor = 0.25;
+				} );
 
 				scene.add( group );
 
-				var light = new THREE.DirectionalLight( 0xddffdd, 0.4);
-				light.position.z = 1;
-				light.position.y = 1;
-				light.position.x = 1;
-				scene.add( light );
-				light.castShadow = true;
-
-				light.shadow.mapSize.width = 1024;
-				light.shadow.mapSize.height = 1024;
-
-				var d = 20;
-
-				light.shadow.camera.left = - d;
-				light.shadow.camera.right = d;
-				light.shadow.camera.top = d;
-				light.shadow.camera.bottom = - d;
-
-				light.shadow.camera.far = 1000;
-
-				var light2 = new THREE.DirectionalLight( 0xaadddd, 0.15 );
-				light2.position.z = 1;
-				light2.position.x = -1;
-				light2.position.y = -1;
-				scene.add( light2 );
+				group.add( obj3d );
 
-				var light3 = new THREE.DirectionalLight( 0xddddaa, 0.1 );
-				light3.position.z = 1;
-				light3.position.x = -1;
-				light3.position.y = 1;
-				scene.add( light3 );
-
-				var light3 = new THREE.AmbientLight( 0xaaaaaa, 0.2 );
-				scene.add( light3 );
+				//
 
 				var geometry = new THREE.SphereBufferGeometry( 3, 48, 24 );
+
 				for ( var i = 0; i < 20; i ++ ) {
 
 					var material = new THREE.MeshLambertMaterial();
-					material.roughness = 1;//0.5 * Math.random() + 0.25;
-					material.metalness = 0;
 					material.color.setHSL( Math.random(), 1.0, 0.3 );
 
 					var mesh = new THREE.Mesh( geometry, material );
 					mesh.position.x = Math.random() * 4 - 2;
 					mesh.position.y = Math.random() * 4 - 2;
 					mesh.position.z = Math.random() * 4 - 2;
-					mesh.rotation.x = Math.random();
-					mesh.rotation.y = Math.random();
-					mesh.rotation.z = Math.random();
 					mesh.receiveShadow = true;
 					mesh.castShadow = true;
-					mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 0.3 + 0.1;
+					mesh.scale.multiplyScalar( Math.random() * 0.3 + 0.1 );
 					group.add( mesh );
+
 				}
 
-				var floorMaterial = new THREE.MeshLambertMaterial();
-				floorMaterial.side = THREE.DoubleSide;
-				material.roughness = 0.5 * Math.random() + 0.25;
-				material.metalness = 0;
+				var floorMaterial = new THREE.MeshLambertMaterial( { side: THREE.DoubleSide } );
 
 				var floorGeometry = new THREE.PlaneBufferGeometry( 12, 12 );
 				var floorMesh = new THREE.Mesh( floorGeometry, floorMaterial );
@@ -268,43 +257,43 @@
 				group.add( floorMesh );
 				floorMesh.receiveShadow = true;
 
-				var geometry = new THREE.TorusGeometry( 1, 0.3, 16, 100 );
+				var geometry = new THREE.TorusBufferGeometry( 1, 0.3, 16, 100 );
 				var material = new THREE.MeshPhongMaterial( { color: 0xffaaff } );
 				var torus = new THREE.Mesh( geometry, material );
-				torus.position.z = -4;
+				torus.position.z = - 4;
 				group.add( torus );
 				torus.receiveShadow = true;
 				torus.castShadow = true;
 
+				//
+
 				stats = new Stats();
 				container.appendChild( stats.dom );
 
 				// postprocessing
+
 				composer = new THREE.EffectComposer( renderer );
 
 				var renderPass = new THREE.RenderPass( scene, camera );
 				composer.addPass( renderPass );
 
-				outlinePass = new THREE.OutlinePass( new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
+				outlinePass = new THREE.OutlinePass( new THREE.Vector2( window.innerWidth, window.innerHeight ), scene, camera );
 				composer.addPass( outlinePass );
+
 				var onLoad = function ( texture ) {
+
 					outlinePass.patternTexture = texture;
 					texture.wrapS = THREE.RepeatWrapping;
 					texture.wrapT = THREE.RepeatWrapping;
+
 				};
 
 				var loader = new THREE.TextureLoader();
 
-				// load a resource
-				loader.load(
-					// resource URL
-					'textures/tri_pattern.jpg',
-					// Function when resource is loaded
-					onLoad
-				);
+				loader.load( 'textures/tri_pattern.jpg', onLoad );
 
-				effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
-				effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight );
+				effectFXAA = new THREE.ShaderPass( THREE.FXAAShader );
+				effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
 				effectFXAA.renderToScreen = true;
 				composer.addPass( effectFXAA );
 
@@ -336,9 +325,11 @@
 
 				}
 
-				function addSelectedObject(object) {
+				function addSelectedObject( object ) {
+
 					selectedObjects = [];
-					selectedObjects.push(object);
+					selectedObjects.push( object );
+
 				}
 
 				function checkIntersection() {
@@ -348,29 +339,34 @@
 					var intersects = raycaster.intersectObjects( [ scene ], true );
 
 					if ( intersects.length > 0 ) {
+
 						var selectedObject = intersects[ 0 ].object;
-						addSelectedObject(selectedObject);
+						addSelectedObject( selectedObject );
 						outlinePass.selectedObjects = selectedObjects;
-					}
-					else {
+
+					} else {
+
 						// outlinePass.selectedObjects = [];
+
 					}
+
 				}
 
 			}
 
 			function onWindowResize() {
 
-				var width = window.innerWidth || 1;
-				var height = window.innerHeight || 1;
-				var devicePixelRatio = window.devicePixelRatio || 1;
+				var width = window.innerWidth;
+				var height = window.innerHeight;
 
 				camera.aspect = width / height;
 				camera.updateProjectionMatrix();
 
 				renderer.setSize( width, height );
 				composer.setSize( width, height );
-				effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight );
+
+				effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
+
 			}
 
 			function animate() {
@@ -380,16 +376,20 @@
 				stats.begin();
 
 				var timer = performance.now();
-				if(params.rotate)
+
+				if ( params.rotate ) {
+
 					group.rotation.y = timer * 0.0001;
-				renderer.autoClear = true;
-				renderer.setClearColor( 0xfff0f0 );
-				renderer.setClearAlpha( 0.0 );
+
+				}
+
+				controls.update();
 
 				composer.render();
+
 				stats.end();
-			}
 
+			}
 
 		</script>
 	</body>

+ 169 - 0
examples/webgl_postprocessing_sobel.html

@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing - sobel (edge detection)</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
+				padding: 5px;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+			}
+
+			a {
+				color: #ffffff;
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/LuminosityShader.js"></script>
+		<script src="js/shaders/SobelOperatorShader.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank">three.js</a> - webgl - postprocessing - sobel (edge detection)
+		</div>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var camera, scene, renderer, composer;
+
+			var effectSobel;
+
+			var params = {
+				enable: true
+			};
+
+			init();
+			animate();
+
+			function init() {
+
+				//
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 0, 10, 25 );
+				camera.lookAt( scene.position );
+
+				//
+
+				var geometry = new THREE.TorusKnotBufferGeometry( 8, 3, 256, 32, 2, 3 );
+				var material = new THREE.MeshPhongMaterial( { color: 0xffff00 } );
+
+				var mesh = new THREE.Mesh( geometry, material );
+				scene.add( mesh );
+
+				//
+
+				var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
+				scene.add( ambientLight );
+
+				var pointLight = new THREE.PointLight( 0xffffff, 0.8 );
+				camera.add( pointLight );
+				scene.add( camera );
+
+				//
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				// postprocessing
+
+				composer = new THREE.EffectComposer( renderer );
+				var renderPass = new THREE.RenderPass( scene, camera );
+				composer.addPass( renderPass );
+
+				// color to grayscale conversion
+
+				var effectGrayScale = new THREE.ShaderPass( THREE.LuminosityShader );
+				composer.addPass( effectGrayScale );
+
+				// you might want to use a gaussian blur filter before
+				// the next pass to improve the result of the Sobel operator
+
+				// Sobel operator
+
+				effectSobel = new THREE.ShaderPass( THREE.SobelOperatorShader );
+				effectSobel.renderToScreen = true;
+				effectSobel.uniforms.resolution.value.x = window.innerWidth;
+				effectSobel.uniforms.resolution.value.y = window.innerHeight;
+				composer.addPass( effectSobel );
+
+				var controls = new THREE.OrbitControls( camera );
+
+				//
+
+				var gui = new dat.GUI();
+
+				gui.add( params, 'enable' );
+				gui.open();
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect =  window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				composer.setSize( window.innerWidth, window.innerHeight );
+
+				effectSobel.uniforms.resolution.value.x = window.innerWidth;
+				effectSobel.uniforms.resolution.value.y = window.innerHeight;
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				if ( params.enable === true ) {
+
+					composer.render();
+
+				} else {
+
+					renderer.render( scene, camera );
+
+				}
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 299 - 0
examples/webgl_shadowmap_pcss.html

@@ -0,0 +1,299 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - percent closer soft-shadows</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000;
+				color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				padding: 10px;
+				width: 100%;
+				text-align: center;
+			}
+
+			a {
+				text-decoration: underline;
+				cursor: pointer;
+			}
+
+		</style>
+	</head>
+
+	<body>
+		<div id="info">Percent Closer Soft-Shadows (PCSS) by <a href="http://eduperiment.com">spidersharma03</a></div>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script type="x-shader/x-fragment" id="PCSS">
+
+				#define LIGHT_WORLD_SIZE 0.005
+				#define LIGHT_FRUSTUM_WIDTH 3.75
+				#define LIGHT_SIZE_UV (LIGHT_WORLD_SIZE / LIGHT_FRUSTUM_WIDTH)
+				#define NEAR_PLANE 9.5
+
+				#define NUM_SAMPLES 17
+				#define NUM_RINGS 11
+				#define BLOCKER_SEARCH_NUM_SAMPLES NUM_SAMPLES
+				#define PCF_NUM_SAMPLES NUM_SAMPLES
+
+				vec2 poissonDisk[NUM_SAMPLES];
+
+				void initPoissonSamples( const in vec2 randomSeed ) {
+					float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES );
+					float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES );
+
+					// jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/
+					float angle = rand( randomSeed ) * PI2;
+					float radius = INV_NUM_SAMPLES;
+					float radiusStep = radius;
+
+					for( int i = 0; i < NUM_SAMPLES; i ++ ) {
+						poissonDisk[i] = vec2( cos( angle ), sin( angle ) ) * pow( radius, 0.75 );
+						radius += radiusStep;
+						angle += ANGLE_STEP;
+					}
+				}
+
+				float penumbraSize( const in float zReceiver, const in float zBlocker ) { // Parallel plane estimation
+					return (zReceiver - zBlocker) / zBlocker;
+				}
+
+				float findBlocker( sampler2D shadowMap, const in vec2 uv, const in float zReceiver ) {
+					// This uses similar triangles to compute what
+					// area of the shadow map we should search
+					float searchRadius = LIGHT_SIZE_UV * ( zReceiver - NEAR_PLANE ) / zReceiver;
+					float blockerDepthSum = 0.0;
+					int numBlockers = 0;
+
+					for( int i = 0; i < BLOCKER_SEARCH_NUM_SAMPLES; i++ ) {
+						float shadowMapDepth = unpackRGBAToDepth(texture2D(shadowMap, uv + poissonDisk[i] * searchRadius));
+						if ( shadowMapDepth < zReceiver ) {
+							blockerDepthSum += shadowMapDepth;
+							numBlockers ++;
+						}
+					}
+
+					if( numBlockers == 0 ) return -1.0;
+
+					return blockerDepthSum / float( numBlockers );
+				}
+
+				float PCF_Filter(sampler2D shadowMap, vec2 uv, float zReceiver, float filterRadius ) {
+					float sum = 0.0;
+					for( int i = 0; i < PCF_NUM_SAMPLES; i ++ ) {
+						float depth = unpackRGBAToDepth( texture2D( shadowMap, uv + poissonDisk[ i ] * filterRadius ) );
+						if( zReceiver <= depth ) sum += 1.0;
+					}
+					for( int i = 0; i < PCF_NUM_SAMPLES; i ++ ) {
+						float depth = unpackRGBAToDepth( texture2D( shadowMap, uv + -poissonDisk[ i ].yx * filterRadius ) );
+						if( zReceiver <= depth ) sum += 1.0;
+					}
+					return sum / ( 2.0 * float( PCF_NUM_SAMPLES ) );
+				}
+
+				float PCSS ( sampler2D shadowMap, vec4 coords ) {
+					vec2 uv = coords.xy;
+					float zReceiver = coords.z; // Assumed to be eye-space z in this code
+
+					initPoissonSamples( uv );
+					// STEP 1: blocker search
+					float avgBlockerDepth = findBlocker( shadowMap, uv, zReceiver );
+
+					//There are no occluders so early out (this saves filtering)
+					if( avgBlockerDepth == -1.0 ) return 1.0;
+
+					// STEP 2: penumbra size
+					float penumbraRatio = penumbraSize( zReceiver, avgBlockerDepth );
+					float filterRadius = penumbraRatio * LIGHT_SIZE_UV * NEAR_PLANE / zReceiver;
+
+					// STEP 3: filtering
+					//return avgBlockerDepth;
+					return PCF_Filter( shadowMap, uv, zReceiver, filterRadius );
+				}
+
+		</script>
+
+		<script type="x-shader/x-fragment" id="PCSSGetShadow">
+
+			return PCSS( shadowMap, shadowCoord );
+
+		</script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+			var camera, scene, renderer;
+
+			var group;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				// scene
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.Fog( 0xcce0ff, 5, 100 );
+
+				// camera
+
+				camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.y = 5;
+				camera.position.z = 15;
+				scene.add( camera );
+
+				// lights
+
+				scene.add( new THREE.AmbientLight( 0x666666 ) );
+
+				var light = new THREE.DirectionalLight( 0xdfebff, 1.75 );
+				light.position.set( 2, 8, 4 );
+
+				light.castShadow = true;
+				light.shadow.mapSize.width = 1024;
+				light.shadow.mapSize.height = 1024;
+				light.shadow.camera.far = 20;
+
+				scene.add( light );
+
+				// scene.add( new THREE.DirectionalLightHelper( light ) );
+				scene.add( new THREE.CameraHelper( light.shadow.camera ) );
+
+				// group
+
+				group = new THREE.Group();
+				scene.add( group );
+
+				var geometry = new THREE.SphereGeometry( 0.3, 20, 20 );
+
+				for ( var i = 0; i < 20; i ++ ) {
+
+					var material = new THREE.MeshPhongMaterial( { color: Math.random() * 0xffffff } );
+
+					var sphere = new THREE.Mesh( geometry, material );
+					sphere.position.x = Math.random() * 6 - 3;
+					sphere.position.z = Math.random() * 6 - 3;
+					sphere.castShadow = true;
+					sphere.receiveShadow = true;
+					sphere.userData.phase = Math.random() * Math.PI;
+					group.add( sphere );
+
+				}
+
+				// ground
+
+				var groundMaterial = new THREE.MeshPhongMaterial( { color: 0x404040, specular: 0x111111 } );
+
+				var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 20000, 20000 ), groundMaterial );
+				mesh.rotation.x = - Math.PI / 2;
+				mesh.receiveShadow = true;
+				scene.add( mesh );
+
+				// overwrite shadowmap code
+
+				var shader = THREE.ShaderChunk.shadowmap_pars_fragment;
+
+				shader = shader.replace(
+					'#ifdef USE_SHADOWMAP',
+					'#ifdef USE_SHADOWMAP' +
+					document.getElementById( 'PCSS' ).textContent
+				);
+
+				shader = shader.replace(
+					'#if defined( SHADOWMAP_TYPE_PCF )',
+					document.getElementById( 'PCSSGetShadow' ).textContent +
+					'#if defined( SHADOWMAP_TYPE_PCF )'
+				);
+
+				THREE.ShaderChunk.shadowmap_pars_fragment = shader;
+
+				// renderer
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setClearColor( scene.fog.color );
+
+				container.appendChild( renderer.domElement );
+
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+
+				renderer.shadowMap.enabled = true;
+
+				// controls
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.maxPolarAngle = Math.PI * 0.5;
+				controls.minDistance = 10;
+				controls.maxDistance = 75;
+				controls.target.set( 0, 2.5, 0 );
+				controls.update();
+
+				// performance monitor
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			//
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				var time = performance.now() / 1000;
+
+				group.traverse( function ( child ) {
+
+					if ( 'phase' in child.userData ) {
+
+						child.position.y = Math.abs( Math.sin( time + child.userData.phase ) ) * 4 + 0.3;
+
+					}
+
+				} );
+
+				renderer.render( scene, camera );
+
+				stats.update();
+
+				requestAnimationFrame( animate );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 3 - 6
examples/webgl_video_panorama_equirectangular.html

@@ -43,7 +43,7 @@
 			lon = 0, onMouseDownLon = 0,
 			lat = 0, onMouseDownLat = 0,
 			phi = 0, theta = 0,
-			distance = 500,
+			distance = 50,
 			onPointerDownPointerX = 0,
 			onPointerDownPointerY = 0,
 			onPointerDownLon = 0,
@@ -146,6 +146,8 @@
 
 				distance += event.deltaY * 0.05;
 
+				distance = THREE.Math.clamp( distance, 1, 50 );
+
 			}
 
 			function animate() {
@@ -167,11 +169,6 @@
 
 				camera.lookAt( camera.target );
 
-				/*
-				// distortion
-				camera.position.copy( camera.target ).negate();
-				*/
-
 				renderer.render( scene, camera );
 
 			}

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "three",
-  "version": "0.86.0",
+  "version": "0.87.1",
   "description": "JavaScript 3D library",
   "main": "build/three.js",
   "repository": "mrdoob/three.js",

+ 2 - 2
src/Three.Legacy.js

@@ -5,7 +5,7 @@
 import { Audio } from './audio/Audio.js';
 import { AudioAnalyser } from './audio/AudioAnalyser.js';
 import { PerspectiveCamera } from './cameras/PerspectiveCamera.js';
-import { CullFaceFront, CullFaceBack } from './constants.js';
+import { CullFaceFront, CullFaceBack, FlatShading } from './constants.js';
 import {
 	Float64BufferAttribute,
 	Float32BufferAttribute,
@@ -1047,7 +1047,7 @@ Object.defineProperties( Material.prototype, {
 		set: function ( value ) {
 
 			console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );
-			this.flatShading = ( value === THREE.FlatShading ) ? true : false;
+			this.flatShading = ( value === FlatShading );
 
 		}
 	}

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