2
0
Эх сурвалжийг харах

Merge branch 'dev' into FixGLTFExporterSkinAnimation

Takahiro 7 жил өмнө
parent
commit
4369d6ae3d
35 өөрчлөгдсөн 1228 нэмэгдсэн , 697 устгасан
  1. 14 11
      .github/ISSUE_TEMPLATE.md
  2. 123 110
      build/three.js
  3. 265 265
      build/three.min.js
  4. 123 110
      build/three.module.js
  5. 2 2
      docs/api/loaders/ObjectLoader.html
  6. 1 1
      docs/examples/loaders/MTLLoader.html
  7. 1 0
      examples/files.js
  8. 2 2
      examples/js/exporters/GLTFExporter.js
  9. 1 3
      examples/js/loaders/FBXLoader.js
  10. 1 3
      examples/js/nodes/NodeMaterial.js
  11. 47 0
      examples/js/shaders/PixelShader.js
  12. BIN
      examples/textures/compressed/disturb_ASTC4x4.ktx
  13. BIN
      examples/textures/compressed/disturb_BC1.ktx
  14. BIN
      examples/textures/compressed/disturb_ETC1.ktx
  15. BIN
      examples/textures/compressed/disturb_PVR2bpp.ktx
  16. BIN
      examples/textures/compressed/disturb_cube.ktx
  17. BIN
      examples/textures/compressed/disturb_rgb.ktx
  18. BIN
      examples/textures/compressed/disturb_rgba.ktx
  19. BIN
      examples/textures/compressed/lensflare_ASTC8x8.ktx
  20. BIN
      examples/textures/compressed/lensflare_BC3.ktx
  21. BIN
      examples/textures/compressed/lensflare_PVR4bpp 2.ktx
  22. BIN
      examples/textures/compressed/lensflare_PVR4bpp.ktx
  23. 115 63
      examples/webgl_loader_texture_ktx.html
  24. 147 0
      examples/webgl_materials_video_webcam.html
  25. 194 0
      examples/webgl_postprocessing_pixel.html
  26. 1 1
      examples/webgl_shaders_ocean.html
  27. 31 16
      src/animation/PropertyBinding.js
  28. 9 9
      src/constants.js
  29. 2 1
      src/extras/ShapeUtils.js
  30. 89 94
      src/extras/core/Font.js
  31. 1 1
      src/loaders/JSONLoader.js
  32. 1 1
      src/math/Math.js
  33. 18 0
      src/objects/Skeleton.js
  34. 2 1
      src/renderers/webgl/WebGLState.js
  35. 38 3
      test/unit/src/animation/PropertyBinding.tests.js

+ 14 - 11
.github/ISSUE_TEMPLATE.md

@@ -1,15 +1,20 @@
-##### Description of the problem 
+##### Description of the problem
 
-This form is for three.js bug reports and feature requests only.  
-This is NOT a help site. Do not ask help questions here.  
-If you need help, please use [stackoverflow](http://stackoverflow.com/questions/tagged/three.js).
+This form is for three.js bug reports and feature requests only.
 
-Describe the bug or feature request in detail.  
-A code snippet, screenshot, and small-test help us understand.
+This is NOT a help site. Do not ask help questions here.
+If you need help, please use the [forum](https://discourse.threejs.org/) or [stackoverflow](http://stackoverflow.com/questions/tagged/three.js).
 
-You can edit for small-test.  
-http://jsfiddle.net/akmcv7Lh/ (current revision)  
-http://jsfiddle.net/hw9rcLL8/ (dev)
+Describe the bug or feature request in detail.
+
+Always include a code snippet, screenshots, and any relevant models or textures to help us understand your issue.
+
+Please also include a live example if possible. You can start from these templates:
+
+* [jsfiddle](https://jsfiddle.net/s3rjfcc3/) (latest release branch)
+* [jsfiddle](https://jsfiddle.net/ptgwhemb/) (dev branch)
+* [codepen](https://codepen.io/anon/pen/aEBKxR) (latest release branch)
+* [codepen](https://codepen.io/anon/pen/BJWzaN) (dev branch)
 
 ##### Three.js version
 
@@ -34,5 +39,3 @@ http://jsfiddle.net/hw9rcLL8/ (dev)
 - [ ] iOS
 
 ##### Hardware Requirements (graphics card, VR Device, ...)
-
-

+ 123 - 110
build/three.js

@@ -279,15 +279,15 @@
 	var RGBEFormat = RGBAFormat;
 	var DepthFormat = 1026;
 	var DepthStencilFormat = 1027;
-	var RGB_S3TC_DXT1_Format = 2001;
-	var RGBA_S3TC_DXT1_Format = 2002;
-	var RGBA_S3TC_DXT3_Format = 2003;
-	var RGBA_S3TC_DXT5_Format = 2004;
-	var RGB_PVRTC_4BPPV1_Format = 2100;
-	var RGB_PVRTC_2BPPV1_Format = 2101;
-	var RGBA_PVRTC_4BPPV1_Format = 2102;
-	var RGBA_PVRTC_2BPPV1_Format = 2103;
-	var RGB_ETC1_Format = 2151;
+	var RGB_S3TC_DXT1_Format = 33776;
+	var RGBA_S3TC_DXT1_Format = 33777;
+	var RGBA_S3TC_DXT3_Format = 33778;
+	var RGBA_S3TC_DXT5_Format = 33779;
+	var RGB_PVRTC_4BPPV1_Format = 35840;
+	var RGB_PVRTC_2BPPV1_Format = 35841;
+	var RGBA_PVRTC_4BPPV1_Format = 35842;
+	var RGBA_PVRTC_2BPPV1_Format = 35843;
+	var RGB_ETC1_Format = 36196;
 	var RGBA_ASTC_4x4_Format = 37808;
 	var RGBA_ASTC_5x4_Format = 37809;
 	var RGBA_ASTC_5x5_Format = 37810;
@@ -19837,7 +19837,8 @@
 
 				if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
 				     extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
-				     extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {
+				     extensions.get( 'WEBGL_compressed_texture_etc1' ) ||
+				     extensions.get( 'WEBGL_compressed_texture_astc' ) ) {
 
 					var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
 
@@ -27524,9 +27525,10 @@
 			//
 
 			var holeIndex = contour.length;
+
 			holes.forEach( removeDupEndPts );
 
-			for ( i = 0; i < holes.length; i ++ ) {
+			for ( var i = 0; i < holes.length; i ++ ) {
 
 				holeIndices.push( holeIndex );
 				holeIndex += holes[ i ].length;
@@ -37996,155 +37998,151 @@
 
 		generateShapes: function ( text, size, divisions ) {
 
-			function createPaths( text ) {
-
-				var chars = String( text ).split( '' );
-				var scale = size / data.resolution;
-				var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
-
-				var offsetX = 0, offsetY = 0;
-
-				var paths = [];
+			if ( size === undefined ) size = 100;
+			if ( divisions === undefined ) divisions = 4;
 
-				for ( var i = 0; i < chars.length; i ++ ) {
+			var shapes = [];
+			var paths = createPaths( text, size, divisions, this.data );
 
-					var char = chars[ i ];
+			for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
 
-					if ( char === '\n' ) {
+				Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
 
-						offsetX = 0;
-						offsetY -= line_height;
+			}
 
-					} else {
+			return shapes;
 
-						var ret = createPath( char, scale, offsetX, offsetY );
-						offsetX += ret.offsetX;
-						paths.push( ret.path );
+		}
 
-					}
+	} );
 
-				}
+	function createPaths( text, size, divisions, data ) {
 
-				return paths;
+		var chars = String( text ).split( '' );
+		var scale = size / data.resolution;
+		var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 
-			}
+		var paths = [];
 
-			function createPath( c, scale, offsetX, offsetY ) {
+		var offsetX = 0, offsetY = 0;
 
-				var glyph = data.glyphs[ c ] || data.glyphs[ '?' ];
+		for ( var i = 0; i < chars.length; i ++ ) {
 
-				if ( ! glyph ) return;
+			var char = chars[ i ];
 
-				var path = new ShapePath();
+			if ( char === '\n' ) {
 
-				var pts = [];
-				var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
+				offsetX = 0;
+				offsetY -= line_height;
 
-				if ( glyph.o ) {
+			} else {
 
-					var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+				var ret = createPath( char, divisions, scale, offsetX, offsetY, data );
+				offsetX += ret.offsetX;
+				paths.push( ret.path );
 
-					for ( var i = 0, l = outline.length; i < l; ) {
+			}
 
-						var action = outline[ i ++ ];
+		}
 
-						switch ( action ) {
+		return paths;
 
-							case 'm': // moveTo
+	}
 
-								x = outline[ i ++ ] * scale + offsetX;
-								y = outline[ i ++ ] * scale + offsetY;
+	function createPath( char, divisions, scale, offsetX, offsetY, data ) {
 
-								path.moveTo( x, y );
+		var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
 
-								break;
+		if ( ! glyph ) return;
 
-							case 'l': // lineTo
+		var path = new ShapePath();
 
-								x = outline[ i ++ ] * scale + offsetX;
-								y = outline[ i ++ ] * scale + offsetY;
+		var pts = [];
+		var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
 
-								path.lineTo( x, y );
+		if ( glyph.o ) {
 
-								break;
+			var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
 
-							case 'q': // quadraticCurveTo
+			for ( var i = 0, l = outline.length; i < l; ) {
 
-								cpx = outline[ i ++ ] * scale + offsetX;
-								cpy = outline[ i ++ ] * scale + offsetY;
-								cpx1 = outline[ i ++ ] * scale + offsetX;
-								cpy1 = outline[ i ++ ] * scale + offsetY;
+				var action = outline[ i ++ ];
 
-								path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+				switch ( action ) {
 
-								laste = pts[ pts.length - 1 ];
+					case 'm': // moveTo
 
-								if ( laste ) {
+						x = outline[ i ++ ] * scale + offsetX;
+						y = outline[ i ++ ] * scale + offsetY;
 
-									cpx0 = laste.x;
-									cpy0 = laste.y;
+						path.moveTo( x, y );
 
-									
+						break;
 
-								}
+					case 'l': // lineTo
 
-								break;
+						x = outline[ i ++ ] * scale + offsetX;
+						y = outline[ i ++ ] * scale + offsetY;
 
-							case 'b': // bezierCurveTo
+						path.lineTo( x, y );
 
-								cpx = outline[ i ++ ] * scale + offsetX;
-								cpy = outline[ i ++ ] * scale + offsetY;
-								cpx1 = outline[ i ++ ] * scale + offsetX;
-								cpy1 = outline[ i ++ ] * scale + offsetY;
-								cpx2 = outline[ i ++ ] * scale + offsetX;
-								cpy2 = outline[ i ++ ] * scale + offsetY;
+						break;
 
-								path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+					case 'q': // quadraticCurveTo
 
-								laste = pts[ pts.length - 1 ];
+						cpx = outline[ i ++ ] * scale + offsetX;
+						cpy = outline[ i ++ ] * scale + offsetY;
+						cpx1 = outline[ i ++ ] * scale + offsetX;
+						cpy1 = outline[ i ++ ] * scale + offsetY;
 
-								if ( laste ) {
+						path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
 
-									cpx0 = laste.x;
-									cpy0 = laste.y;
+						laste = pts[ pts.length - 1 ];
 
-									
+						if ( laste ) {
 
-								}
+							cpx0 = laste.x;
+							cpy0 = laste.y;
 
-								break;
+							
 
 						}
 
-					}
+						break;
 
-				}
+					case 'b': // bezierCurveTo
 
-				return { offsetX: glyph.ha * scale, path: path };
+						cpx = outline[ i ++ ] * scale + offsetX;
+						cpy = outline[ i ++ ] * scale + offsetY;
+						cpx1 = outline[ i ++ ] * scale + offsetX;
+						cpy1 = outline[ i ++ ] * scale + offsetY;
+						cpx2 = outline[ i ++ ] * scale + offsetX;
+						cpy2 = outline[ i ++ ] * scale + offsetY;
 
-			}
+						path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
 
-			//
+						laste = pts[ pts.length - 1 ];
 
-			if ( size === undefined ) size = 100;
-			if ( divisions === undefined ) divisions = 4;
+						if ( laste ) {
 
-			var data = this.data;
+							cpx0 = laste.x;
+							cpy0 = laste.y;
 
-			var paths = createPaths( text );
-			var shapes = [];
+							
 
-			for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+						}
 
-				Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+						break;
 
-			}
+				}
 
-			return shapes;
+			}
 
 		}
 
-	} );
+		return { offsetX: glyph.ha * scale, path: path };
+
+	}
 
 	/**
 	 * @author mrdoob / http://mrdoob.com/
@@ -39231,6 +39229,9 @@
 	 * @author tschw
 	 */
 
+	// Characters [].:/ are reserved for track binding syntax.
+	var RESERVED_CHARS_RE = '\\[\\]\\.:\\/';
+
 	function Composite( targetGroup, path, optionalParsedPath ) {
 
 		var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
@@ -39332,35 +39333,47 @@
 		 * @param  {string} name Node name to be sanitized.
 		 * @return {string}
 		 */
-		sanitizeNodeName: function ( name ) {
+		sanitizeNodeName: ( function () {
 
-			return name.replace( /\s/g, '_' ).replace( /[^\w-]/g, '' );
+			var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );
 
-		},
+			return function sanitizeNodeName ( name ) {
+
+				return name.replace( /\s/g, '_' ).replace( reservedRe, '' );
+
+			};
+
+		}() ),
 
 		parseTrackName: function () {
 
+			// Attempts to allow node names from any language. ES5's `\w` regexp matches
+			// only latin characters, and the unicode \p{L} is not yet supported. So
+			// instead, we exclude reserved characters and match everything else.
+			var wordChar = '[^' + RESERVED_CHARS_RE + ']';
+			var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\.', '' ) + ']';
+
 			// Parent directories, delimited by '/' or ':'. Currently unused, but must
 			// be matched to parse the rest of the track name.
-			var directoryRe = /((?:[\w-]+[\/:])*)/;
+			var directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', wordChar );
 
 			// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
-			var nodeRe = /([\w-\.]+)?/;
+			var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );
 
-			// Object on target node, and accessor. Name may contain only word
+			// Object on target node, and accessor. May not contain reserved
 			// characters. Accessor may contain any character except closing bracket.
-			var objectRe = /(?:\.([\w-]+)(?:\[(.+)\])?)?/;
+			var objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', wordChar );
 
-			// Property and accessor. May contain only word characters. Accessor may
+			// Property and accessor. May not contain reserved characters. Accessor may
 			// contain any non-bracket characters.
-			var propertyRe = /\.([\w-]+)(?:\[(.+)\])?/;
+			var propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', wordChar );
 
 			var trackRe = new RegExp( ''
 				+ '^'
-				+ directoryRe.source
-				+ nodeRe.source
-				+ objectRe.source
-				+ propertyRe.source
+				+ directoryRe
+				+ nodeRe
+				+ objectRe
+				+ propertyRe
 				+ '$'
 			);
 

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 265 - 265
build/three.min.js


+ 123 - 110
build/three.module.js

@@ -273,15 +273,15 @@ var LuminanceAlphaFormat = 1025;
 var RGBEFormat = RGBAFormat;
 var DepthFormat = 1026;
 var DepthStencilFormat = 1027;
-var RGB_S3TC_DXT1_Format = 2001;
-var RGBA_S3TC_DXT1_Format = 2002;
-var RGBA_S3TC_DXT3_Format = 2003;
-var RGBA_S3TC_DXT5_Format = 2004;
-var RGB_PVRTC_4BPPV1_Format = 2100;
-var RGB_PVRTC_2BPPV1_Format = 2101;
-var RGBA_PVRTC_4BPPV1_Format = 2102;
-var RGBA_PVRTC_2BPPV1_Format = 2103;
-var RGB_ETC1_Format = 2151;
+var RGB_S3TC_DXT1_Format = 33776;
+var RGBA_S3TC_DXT1_Format = 33777;
+var RGBA_S3TC_DXT3_Format = 33778;
+var RGBA_S3TC_DXT5_Format = 33779;
+var RGB_PVRTC_4BPPV1_Format = 35840;
+var RGB_PVRTC_2BPPV1_Format = 35841;
+var RGBA_PVRTC_4BPPV1_Format = 35842;
+var RGBA_PVRTC_2BPPV1_Format = 35843;
+var RGB_ETC1_Format = 36196;
 var RGBA_ASTC_4x4_Format = 37808;
 var RGBA_ASTC_5x4_Format = 37809;
 var RGBA_ASTC_5x5_Format = 37810;
@@ -19831,7 +19831,8 @@ function WebGLState( gl, extensions, utils ) {
 
 			if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
 			     extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
-			     extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {
+			     extensions.get( 'WEBGL_compressed_texture_etc1' ) ||
+			     extensions.get( 'WEBGL_compressed_texture_astc' ) ) {
 
 				var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
 
@@ -27518,9 +27519,10 @@ var ShapeUtils = {
 		//
 
 		var holeIndex = contour.length;
+
 		holes.forEach( removeDupEndPts );
 
-		for ( i = 0; i < holes.length; i ++ ) {
+		for ( var i = 0; i < holes.length; i ++ ) {
 
 			holeIndices.push( holeIndex );
 			holeIndex += holes[ i ].length;
@@ -37990,155 +37992,151 @@ Object.assign( Font.prototype, {
 
 	generateShapes: function ( text, size, divisions ) {
 
-		function createPaths( text ) {
-
-			var chars = String( text ).split( '' );
-			var scale = size / data.resolution;
-			var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
-
-			var offsetX = 0, offsetY = 0;
-
-			var paths = [];
+		if ( size === undefined ) size = 100;
+		if ( divisions === undefined ) divisions = 4;
 
-			for ( var i = 0; i < chars.length; i ++ ) {
+		var shapes = [];
+		var paths = createPaths( text, size, divisions, this.data );
 
-				var char = chars[ i ];
+		for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
 
-				if ( char === '\n' ) {
+			Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
 
-					offsetX = 0;
-					offsetY -= line_height;
+		}
 
-				} else {
+		return shapes;
 
-					var ret = createPath( char, scale, offsetX, offsetY );
-					offsetX += ret.offsetX;
-					paths.push( ret.path );
+	}
 
-				}
+} );
 
-			}
+function createPaths( text, size, divisions, data ) {
 
-			return paths;
+	var chars = String( text ).split( '' );
+	var scale = size / data.resolution;
+	var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 
-		}
+	var paths = [];
 
-		function createPath( c, scale, offsetX, offsetY ) {
+	var offsetX = 0, offsetY = 0;
 
-			var glyph = data.glyphs[ c ] || data.glyphs[ '?' ];
+	for ( var i = 0; i < chars.length; i ++ ) {
 
-			if ( ! glyph ) return;
+		var char = chars[ i ];
 
-			var path = new ShapePath();
+		if ( char === '\n' ) {
 
-			var pts = [];
-			var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
+			offsetX = 0;
+			offsetY -= line_height;
 
-			if ( glyph.o ) {
+		} else {
 
-				var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+			var ret = createPath( char, divisions, scale, offsetX, offsetY, data );
+			offsetX += ret.offsetX;
+			paths.push( ret.path );
 
-				for ( var i = 0, l = outline.length; i < l; ) {
+		}
 
-					var action = outline[ i ++ ];
+	}
 
-					switch ( action ) {
+	return paths;
 
-						case 'm': // moveTo
+}
 
-							x = outline[ i ++ ] * scale + offsetX;
-							y = outline[ i ++ ] * scale + offsetY;
+function createPath( char, divisions, scale, offsetX, offsetY, data ) {
 
-							path.moveTo( x, y );
+	var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
 
-							break;
+	if ( ! glyph ) return;
 
-						case 'l': // lineTo
+	var path = new ShapePath();
 
-							x = outline[ i ++ ] * scale + offsetX;
-							y = outline[ i ++ ] * scale + offsetY;
+	var pts = [];
+	var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
 
-							path.lineTo( x, y );
+	if ( glyph.o ) {
 
-							break;
+		var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
 
-						case 'q': // quadraticCurveTo
+		for ( var i = 0, l = outline.length; i < l; ) {
 
-							cpx = outline[ i ++ ] * scale + offsetX;
-							cpy = outline[ i ++ ] * scale + offsetY;
-							cpx1 = outline[ i ++ ] * scale + offsetX;
-							cpy1 = outline[ i ++ ] * scale + offsetY;
+			var action = outline[ i ++ ];
 
-							path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+			switch ( action ) {
 
-							laste = pts[ pts.length - 1 ];
+				case 'm': // moveTo
 
-							if ( laste ) {
+					x = outline[ i ++ ] * scale + offsetX;
+					y = outline[ i ++ ] * scale + offsetY;
 
-								cpx0 = laste.x;
-								cpy0 = laste.y;
+					path.moveTo( x, y );
 
-								
+					break;
 
-							}
+				case 'l': // lineTo
 
-							break;
+					x = outline[ i ++ ] * scale + offsetX;
+					y = outline[ i ++ ] * scale + offsetY;
 
-						case 'b': // bezierCurveTo
+					path.lineTo( x, y );
 
-							cpx = outline[ i ++ ] * scale + offsetX;
-							cpy = outline[ i ++ ] * scale + offsetY;
-							cpx1 = outline[ i ++ ] * scale + offsetX;
-							cpy1 = outline[ i ++ ] * scale + offsetY;
-							cpx2 = outline[ i ++ ] * scale + offsetX;
-							cpy2 = outline[ i ++ ] * scale + offsetY;
+					break;
 
-							path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+				case 'q': // quadraticCurveTo
 
-							laste = pts[ pts.length - 1 ];
+					cpx = outline[ i ++ ] * scale + offsetX;
+					cpy = outline[ i ++ ] * scale + offsetY;
+					cpx1 = outline[ i ++ ] * scale + offsetX;
+					cpy1 = outline[ i ++ ] * scale + offsetY;
 
-							if ( laste ) {
+					path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
 
-								cpx0 = laste.x;
-								cpy0 = laste.y;
+					laste = pts[ pts.length - 1 ];
 
-								
+					if ( laste ) {
 
-							}
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-							break;
+						
 
 					}
 
-				}
+					break;
 
-			}
+				case 'b': // bezierCurveTo
 
-			return { offsetX: glyph.ha * scale, path: path };
+					cpx = outline[ i ++ ] * scale + offsetX;
+					cpy = outline[ i ++ ] * scale + offsetY;
+					cpx1 = outline[ i ++ ] * scale + offsetX;
+					cpy1 = outline[ i ++ ] * scale + offsetY;
+					cpx2 = outline[ i ++ ] * scale + offsetX;
+					cpy2 = outline[ i ++ ] * scale + offsetY;
 
-		}
+					path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
 
-		//
+					laste = pts[ pts.length - 1 ];
 
-		if ( size === undefined ) size = 100;
-		if ( divisions === undefined ) divisions = 4;
+					if ( laste ) {
 
-		var data = this.data;
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-		var paths = createPaths( text );
-		var shapes = [];
+						
 
-		for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+					}
 
-			Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+					break;
 
-		}
+			}
 
-		return shapes;
+		}
 
 	}
 
-} );
+	return { offsetX: glyph.ha * scale, path: path };
+
+}
 
 /**
  * @author mrdoob / http://mrdoob.com/
@@ -39225,6 +39223,9 @@ Object.assign( PropertyMixer.prototype, {
  * @author tschw
  */
 
+// Characters [].:/ are reserved for track binding syntax.
+var RESERVED_CHARS_RE = '\\[\\]\\.:\\/';
+
 function Composite( targetGroup, path, optionalParsedPath ) {
 
 	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
@@ -39326,35 +39327,47 @@ Object.assign( PropertyBinding, {
 	 * @param  {string} name Node name to be sanitized.
 	 * @return {string}
 	 */
-	sanitizeNodeName: function ( name ) {
+	sanitizeNodeName: ( function () {
 
-		return name.replace( /\s/g, '_' ).replace( /[^\w-]/g, '' );
+		var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );
 
-	},
+		return function sanitizeNodeName ( name ) {
+
+			return name.replace( /\s/g, '_' ).replace( reservedRe, '' );
+
+		};
+
+	}() ),
 
 	parseTrackName: function () {
 
+		// Attempts to allow node names from any language. ES5's `\w` regexp matches
+		// only latin characters, and the unicode \p{L} is not yet supported. So
+		// instead, we exclude reserved characters and match everything else.
+		var wordChar = '[^' + RESERVED_CHARS_RE + ']';
+		var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\.', '' ) + ']';
+
 		// Parent directories, delimited by '/' or ':'. Currently unused, but must
 		// be matched to parse the rest of the track name.
-		var directoryRe = /((?:[\w-]+[\/:])*)/;
+		var directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', wordChar );
 
 		// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
-		var nodeRe = /([\w-\.]+)?/;
+		var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );
 
-		// Object on target node, and accessor. Name may contain only word
+		// Object on target node, and accessor. May not contain reserved
 		// characters. Accessor may contain any character except closing bracket.
-		var objectRe = /(?:\.([\w-]+)(?:\[(.+)\])?)?/;
+		var objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', wordChar );
 
-		// Property and accessor. May contain only word characters. Accessor may
+		// Property and accessor. May not contain reserved characters. Accessor may
 		// contain any non-bracket characters.
-		var propertyRe = /\.([\w-]+)(?:\[(.+)\])?/;
+		var propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', wordChar );
 
 		var trackRe = new RegExp( ''
 			+ '^'
-			+ directoryRe.source
-			+ nodeRe.source
-			+ objectRe.source
-			+ propertyRe.source
+			+ directoryRe
+			+ nodeRe
+			+ objectRe
+			+ propertyRe
 			+ '$'
 		);
 

+ 2 - 2
docs/api/loaders/ObjectLoader.html

@@ -43,12 +43,12 @@
 			},
 
 			// onProgress callback
-			function ( err ) {
+			function ( xhr ) {
 				console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
 			},
 
 			// onError callback
-			function ( xhr ) {
+			function ( err ) {
 				console.error( 'An error happened' );
 			}
 		);

+ 1 - 1
docs/examples/loaders/MTLLoader.html

@@ -11,7 +11,7 @@
 
 		<h1>[name]</h1>
 
-		<div class="desc">A loader for loading an <em>.mtl</em> resource, used internaly by [page:OBJMTLLoader] and [page:UTF8Loader].<br />
+		<div class="desc">A loader for loading an <em>.mtl</em> resource, used internaly by [page:OBJLoader] and [page:UTF8Loader].<br />
 		The Material Template Library format (MTL) or .MTL File Format is a companion file format to .OBJ that describes surface shading
 		(material) properties of objects within one or more .OBJ files. 		
 		</div>

+ 1 - 0
examples/files.js

@@ -173,6 +173,7 @@ var files = {
 		"webgl_materials_variations_physical",
 		"webgl_materials_variations_toon",
 		"webgl_materials_video",
+		"webgl_materials_video_webcam",
 		"webgl_materials_wireframe",
 		"webgl_mirror",
 		"webgl_mirror_nodes",

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

@@ -396,7 +396,7 @@ THREE.GLTFExporter.prototype = {
 		 */
 		function processImage( map ) {
 
-			if ( cachedData.images[ map.uuid ] ) {
+			if ( cachedData.images[ map.uuid ] !== undefined ) {
 
 				return cachedData.images[ map.uuid ];
 
@@ -507,7 +507,7 @@ THREE.GLTFExporter.prototype = {
 		 */
 		function processMaterial( material ) {
 
-			if ( cachedData.materials[ material.uuid ] ) {
+			if ( cachedData.materials[ material.uuid ] !== undefined ) {
 
 				return cachedData.materials[ material.uuid ];
 

+ 1 - 3
examples/js/loaders/FBXLoader.js

@@ -1098,8 +1098,6 @@
 				}
 
 				polygonIndex ++;
-
-				endOfFace = false;
 				faceLength = 0;
 
 				// reset arrays for the next face
@@ -1524,7 +1522,7 @@
 
 		bindSkeleton( FBXTree, skeletons, geometryMap, modelMap, connections, sceneGraph );
 
-		addAnimations( FBXTree, connections, sceneGraph, modelMap );
+		addAnimations( FBXTree, connections, sceneGraph );
 
 		createAmbientLight( FBXTree, sceneGraph );
 

+ 1 - 3
examples/js/nodes/NodeMaterial.js

@@ -189,12 +189,10 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	if ( this.requestAttribs.worldPosition ) {
 
-		// for future update replace from the native "varying vec3 vWorldPosition" for optimization
-
 		this.addVertexPars( 'varying vec3 vWPosition;' );
 		this.addFragmentPars( 'varying vec3 vWPosition;' );
 
-		this.addVertexCode( 'vWPosition = worldPosition.xyz;' );
+		this.addVertexCode( 'vWPosition = ( modelMatrix * vec4( position, 1.0 ) ).xyz;' );
 
 	}
 

+ 47 - 0
examples/js/shaders/PixelShader.js

@@ -0,0 +1,47 @@
+/**
+ * @author wongbryan / http://wongbryan.github.io
+ *
+ * Pixelation shader
+ */
+
+THREE.PixelShader = {
+
+	uniforms: {
+
+		"tDiffuse": { value: null },
+		"resolution": { value: null },
+		"pixelSize": { value: 1. },
+
+	},
+
+	vertexShader: [
+
+		"varying highp vec2 vUv;",
+
+		"void main() {",
+
+		"vUv = uv;",
+		"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+		"}"
+
+	].join( "\n" ),
+
+	fragmentShader: [
+
+		"uniform sampler2D tDiffuse;",
+		"uniform float pixelSize;",
+		"uniform vec2 resolution;",
+
+		"varying highp vec2 vUv;",
+
+		"void main(){",
+
+		"vec2 dxy = pixelSize / resolution;",
+		"vec2 coord = dxy * floor( vUv / dxy );",
+		"gl_FragColor = texture2D(tDiffuse, coord);",
+
+		"}"
+
+	].join( "\n" )
+};

BIN
examples/textures/compressed/disturb_ASTC4x4.ktx


BIN
examples/textures/compressed/disturb_BC1.ktx


BIN
examples/textures/compressed/disturb_ETC1.ktx


BIN
examples/textures/compressed/disturb_PVR2bpp.ktx


BIN
examples/textures/compressed/disturb_cube.ktx


BIN
examples/textures/compressed/disturb_rgb.ktx


BIN
examples/textures/compressed/disturb_rgba.ktx


BIN
examples/textures/compressed/lensflare_ASTC8x8.ktx


BIN
examples/textures/compressed/lensflare_BC3.ktx


BIN
examples/textures/compressed/lensflare_PVR4bpp 2.ktx


BIN
examples/textures/compressed/lensflare_PVR4bpp.ktx


+ 115 - 63
examples/webgl_loader_texture_ktx.html

@@ -1,36 +1,36 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-    <title>three.js webgl - materials - compressed textures</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 {
-            margin: 0px;
-            background-color: #050505;
-            color: #fff;
-            overflow: hidden;
-        }
-
-        a { color: #e00 }
-
-        #info {
-            position: absolute;
-            top: 0px; width: 100%;
-            color: #ffffff;
-            padding: 5px;
-            font-family:Monospace;
-            font-size:13px;
-            text-align:center;
-            z-index:1000;
-        }
-    </style>
+	<title>three.js webgl - materials - compressed textures</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 {
+			margin: 0px;
+			background-color: #050505;
+			color: #fff;
+			overflow: hidden;
+		}
+
+		a { color: #e00 }
+
+		#info {
+			position: absolute;
+			top: 0px; width: 100%;
+			color: #ffffff;
+			padding: 5px;
+			font-family:Monospace;
+			font-size:13px;
+			text-align:center;
+			z-index:1000;
+		}
+	</style>
 </head>
 <body>
 
 <div id="info">
-    <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl - compressed KTX textures -
-    <a href="http://opengameart.org/node/10505">Khronos Texture</a> is a lightweight file format for OpenGL
+	<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl - compressed KTX textures<br />
+	<a href="https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/">Khronos Texture</a> is a lightweight file format for OpenGL
 </div>
 
 <script src="../build/three.js"></script>
@@ -40,6 +40,21 @@
 
 <script>
 
+	/*
+	This is how compressed textures are supposed to be used:
+
+	best for desktop:
+	BC1(DXT1) - opaque textures
+	BC3(DXT5) - transparent textures with full alpha range
+
+	best for iOS:
+	PVR2, PVR4 - opaque textures or alpha
+
+	best for Android:
+	ETC1 - opaque textures
+	ASTC_4x4, ASTC8x8 - transparent textures with full alpha range
+	*/
+
 	if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
 
 	var camera, scene, renderer;
@@ -50,62 +65,99 @@
 
 	function init() {
 
+		renderer = new THREE.WebGLRenderer( { antialias: true } );
+		renderer.setPixelRatio( window.devicePixelRatio );
+		renderer.setSize( window.innerWidth, window.innerHeight );
+		document.body.appendChild( renderer.domElement );
+
+		var formats = {
+			astc: renderer.extensions.get( 'WEBGL_compressed_texture_astc' ),
+			etc1: renderer.extensions.get( 'WEBGL_compressed_texture_etc1' ),
+			s3tc: renderer.extensions.get( 'WEBGL_compressed_texture_s3tc' ),
+			pvrtc: renderer.extensions.get( 'WEBGL_compressed_texture_pvrtc' )
+		};
+
 		camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 2000 );
 		camera.position.z = 1000;
 
 		scene = new THREE.Scene();
 
 		var geometry = new THREE.BoxGeometry( 200, 200, 200 );
+		var material1, material2;
+
+		// TODO: add cubemap support
+		var loader = new THREE.KTXLoader();
 
-		/*
-        This is how compressed textures are supposed to be used:
+		if ( formats.pvrtc ) {
 
-        DXT1 - RGB - opaque textures
-        DXT3 - RGBA - transparent textures with sharp alpha transitions
-        DXT5 - RGBA - transparent textures with full alpha range
-        */
+			material1 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/disturb_PVR2bpp.ktx' )
+			} );
+			material2 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/lensflare_PVR4bpp.ktx' ),
+				depthTest: false,
+				transparent: true,
+				side: THREE.DoubleSide
+			} );
 
-		var loader = new THREE.KTXLoader();
+			meshes.push( new THREE.Mesh( geometry, material1 ) );
+			meshes.push( new THREE.Mesh( geometry, material2 ) );
 
-		var map1 = loader.load( 'textures/compressed/disturb_rgb.ktx' );
+		}
 
-		var map2 = loader.load( 'textures/compressed/disturb_rgba.ktx' );
+		if ( formats.s3tc ) {
 
-		// TODO: fix cubemap support in disturb_cube.ktx
-		var cubemap = loader.load( 'textures/compressed/disturb_rgb.ktx', function ( texture ) {
-			texture.magFilter = THREE.LinearFilter;
-			texture.minFilter = THREE.LinearFilter;
-			texture.mapping = THREE.CubeReflectionMapping;
-		} );
+			material1 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/disturb_BC1.ktx' )
+			} );
+			material2 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/lensflare_BC3.ktx' ),
+				depthTest: false,
+				transparent: true,
+				side: THREE.DoubleSide
+			} );
 
-		var material1 = new THREE.MeshBasicMaterial( { map: map1 } );
-		var material2 = new THREE.MeshBasicMaterial( { map: map1 } );
-		var material3 = new THREE.MeshBasicMaterial( { map: map2, alphaTest: 0.5, side: THREE.DoubleSide } );
+			meshes.push( new THREE.Mesh( geometry, material1 ) );
+			meshes.push( new THREE.Mesh( geometry, material2 ) );
 
+		}
 
-		var mesh = new THREE.Mesh( new THREE.TorusGeometry( 100, 50, 32, 16 ), material1 );
-		mesh.position.x = -200;
-		mesh.position.y = -200;
-		scene.add( mesh );
-		meshes.push( mesh );
+		if ( formats.etc1 ) {
 
-		mesh = new THREE.Mesh( geometry, material2 );
-		mesh.position.x = -200;
-		mesh.position.y = 200;
-		scene.add( mesh );
-		meshes.push( mesh );
+			material1 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/disturb_ETC1.ktx' )
+			} );
 
-		mesh = new THREE.Mesh( geometry, material3 );
-		mesh.position.x = 200;
-		mesh.position.y = 200;
-		scene.add( mesh );
-		meshes.push( mesh );
+			meshes.push( new THREE.Mesh( geometry, material1 ) );
 
+		}
 
-		renderer = new THREE.WebGLRenderer( { antialias: true } );
-		renderer.setPixelRatio( window.devicePixelRatio );
-		renderer.setSize( window.innerWidth, window.innerHeight );
-		document.body.appendChild( renderer.domElement );
+		if ( formats.astc ) {
+
+			material1 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/disturb_ASTC4x4.ktx' )
+			} );
+			material2 = new THREE.MeshBasicMaterial( {
+				map: loader.load( 'textures/compressed/lensflare_ASTC8x8.ktx' ),
+				depthTest: false,
+				transparent: true,
+				side: THREE.DoubleSide
+			} );
+
+			meshes.push( new THREE.Mesh( geometry, material1 ) );
+			meshes.push( new THREE.Mesh( geometry, material2 ) );
+
+		}
+
+		var x = - meshes.length / 2 * 150;
+		for ( var i = 0; i < meshes.length; ++ i, x += 300 ) {
+
+			var mesh = meshes[ i ];
+			mesh.position.x = x;
+			mesh.position.y = 0;
+			scene.add( mesh );
+
+		}
 
 		window.addEventListener( 'resize', onWindowResize, false );
 

+ 147 - 0
examples/webgl_materials_video_webcam.html

@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - materials - video - webcam</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;
+				color: #fff;
+				margin: 0px;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+				font-weight: bold;
+				text-align:center;
+			}
+
+			a {
+				color:#0078ff;
+			}
+
+			#info {
+				color:#fff;
+				position: absolute;
+				top: 5px; width: 100%;
+				z-index:100;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl video webcam input
+		</div>
+
+		<script src="../build/three.js"></script>
+		<script src="../examples/js/controls/OrbitControls.js"></script>
+		<script src="js/Detector.js"></script>
+
+		<video id="video" autoplay style="display:none"></video>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var camera, scene, renderer, video;
+
+			init();
+			animate();
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
+				camera.position.z = 0.01;
+
+				scene = new THREE.Scene();
+
+				video = document.getElementById( 'video' );
+				var texture = new THREE.VideoTexture( video );
+				texture.minFilter = THREE.LinearFilter;
+				texture.magFilter = THREE.LinearFilter;
+				texture.format = THREE.RGBFormat;
+
+				var geometry = new THREE.PlaneBufferGeometry( 16, 9 );
+				geometry.scale( 0.5, 0.5, 0.5 );
+				var material = new THREE.MeshBasicMaterial( { map: texture } );
+
+				var count = 128;
+				var radius = 32;
+
+				var spherical = new THREE.Spherical();
+
+				for ( var i = 1, l = count; i <= l; i ++ ) {
+
+					var phi = Math.acos( - 1 + ( 2 * i ) / l );
+					var theta = Math.sqrt( l * Math.PI ) * phi;
+
+					spherical.set( radius, phi, theta );
+
+					var mesh = new THREE.Mesh( geometry, material );
+					mesh.position.setFromSpherical( spherical );
+					mesh.lookAt( camera.position );
+					scene.add( mesh );
+
+				}
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+				controls.enableZoom = false;
+				controls.enablePan = false;
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				//
+
+				if ( navigator.mediaDevices && navigator.mediaDevices.getUserMedia ) {
+
+					var constraints = { video: { width: 1280, height: 720, facingMode: 'user' } };
+
+					navigator.mediaDevices.getUserMedia( constraints ).then( function( stream ) {
+
+							// apply the stream to the video element used in the texture
+
+							video.src = window.URL.createObjectURL( stream );
+							video.play();
+
+					} ).catch( function( error ) {
+
+						console.error( 'Unable to access the camera/webcam.', error );
+
+					} );
+
+				} else {
+
+					console.error( 'MediaDevices interface not available.' );
+
+				}
+
+			 }
+
+			 function onWindowResize() {
+
+				 camera.aspect = window.innerWidth / window.innerHeight;
+				 camera.updateProjectionMatrix();
+
+				 renderer.setSize( window.innerWidth, window.innerHeight );
+
+			 }
+
+			 function animate() {
+
+				 requestAnimationFrame( animate );
+				 renderer.render( scene, camera );
+
+			 }
+
+		</script>
+
+	</body>
+</html>

+ 194 - 0
examples/webgl_postprocessing_pixel.html

@@ -0,0 +1,194 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing - pixel shader</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: #000000;
+				margin: 0px;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				/*text-align:center;*/
+				font-weight: lighter;
+			}
+
+			a {
+				color: gray;
+				font-weight: bold;
+			}
+
+			#info {
+				color: gray;
+				position: absolute;
+				bottom: 0px;
+				width: 100%;
+				padding: 15px;
+			}
+
+			.dg.ac {
+				z-index: 1 !important;
+			}
+		</style>
+	</head>
+	<body>
+
+		<script src="../build/three.js"></script>
+
+		<script src="js/shaders/CopyShader.js"></script>
+		<script src="js/shaders/PixelShader.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/controls/TrackballControls.js"></script>
+		<script src='js/libs/dat.gui.min.js'></script>
+
+		<div id="container"></div>
+
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - pixel shader example<br/>
+			shader by <a href="http://wongbryan.github.io">wongbryan</a>
+		</div>
+
+		<script>
+
+			var camera, scene, renderer, gui, composer;
+			var pixelPass, params;
+
+			var group;
+
+			init();
+			animate();
+
+			function updateGUI() {
+
+				pixelPass.uniforms.pixelSize.value = params.pixelSize;
+
+			}
+
+			function resize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				pixelPass.uniforms.resolution.value.set( window.innerWidth, window.innerHeight ).multiplyScalar( window.devicePixelRatio );
+
+			}
+
+			function init() {
+
+				var container = document.getElementById( 'container' );
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.setClearColor( 0xbfe7ff );
+				container.appendChild( renderer.domElement );
+
+				camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.set( 0, 0, 30 );
+				controls = new THREE.TrackballControls( camera, renderer.domElement );
+				controls.rotateSpeed = 2.0;
+				controls.panSpeed = 0.8;
+				controls.zoomSpeed = 1.5;
+
+				scene = new THREE.Scene();
+
+				var hemisphereLight = new THREE.HemisphereLight( 0xfceafc, 0x000000, .8 );
+				scene.add( hemisphereLight );
+
+				var dirLight = new THREE.DirectionalLight( 0xffffff, .5 );
+				dirLight.position.set( 150, 75, 150 );
+				scene.add( dirLight );
+
+				var dirLight2 = new THREE.DirectionalLight( 0xffffff, .2 );
+				dirLight2.position.set( - 150, 75, - 150 );
+				scene.add( dirLight2 );
+
+				var dirLight3 = new THREE.DirectionalLight( 0xffffff, .1 );
+				dirLight3.position.set( 0, 125, 0 );
+				scene.add( dirLight3 );
+
+				var geometries = [
+					new THREE.SphereGeometry( 1, 64, 64 ),
+					new THREE.BoxGeometry( 1, 1, 1 ),
+					new THREE.ConeGeometry( 1, 1, 32 ),
+					new THREE.TetrahedronGeometry( 1 ),
+					new THREE.TorusKnotGeometry( 1, .4 )
+				];
+
+				group = new THREE.Group();
+
+				for ( var i = 0; i < 25; i ++ ) {
+
+					var geom = geometries[ Math.floor( Math.random() * geometries.length ) ];
+					var color = new THREE.Color();
+					color.setHSL( Math.random(), .7 + .2 * Math.random(), .5 + .1 * Math.random() );
+					var mat = new THREE.MeshPhongMaterial( { color: color, shininess: 200 } );
+					var mesh = new THREE.Mesh( geom, mat );
+					var s = 4 + Math.random() * 10;
+					mesh.scale.set( s, s, s );
+
+					mesh.position.set( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 ).normalize();
+					mesh.position.multiplyScalar( Math.random() * 200 );
+					mesh.rotation.set( Math.random() * 2, Math.random() * 2, Math.random() * 2 );
+					group.add( mesh );
+
+				}
+
+				scene.add( group );
+
+				composer = new THREE.EffectComposer( renderer );
+				composer.addPass( new THREE.RenderPass( scene, camera ) );
+
+				pixelPass = new THREE.ShaderPass( THREE.PixelShader );
+				pixelPass.uniforms.resolution.value = new THREE.Vector2( window.innerWidth, window.innerHeight );
+				pixelPass.uniforms.resolution.value.multiplyScalar( window.devicePixelRatio );
+				pixelPass.renderToScreen = true;
+				composer.addPass( pixelPass );
+
+				window.addEventListener( 'resize', resize );
+
+				params = {
+					pixelSize: 128,
+					postprocessing: true
+				};
+				gui = new dat.GUI();
+				gui.add( params, 'pixelSize' ).min( 2 ).max( 256 ).step( 2 );
+				gui.add( params, 'postprocessing' );
+
+			}
+
+			function update() {
+
+				controls.update();
+				updateGUI();
+
+				group.rotation.y += .0015;
+				group.rotation.z += .001;
+
+			}
+
+			function animate() {
+
+				update();
+
+				if ( params.postprocessing ) {
+
+					composer.render();
+
+				} else {
+
+					renderer.render( scene, camera );
+
+				}
+
+				window.requestAnimationFrame( animate );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 1 - 1
examples/webgl_shaders_ocean.html

@@ -140,7 +140,7 @@
 
 				//
 
-				gui = new dat.GUI();
+				var gui = new dat.GUI();
 
 				gui.add( parameters, 'distortionScale', 0, 8, 0.1 );
 				gui.add( parameters, 'size', 0.1, 10, 0.1 );

+ 31 - 16
src/animation/PropertyBinding.js

@@ -8,6 +8,9 @@
  * @author tschw
  */
 
+// Characters [].:/ are reserved for track binding syntax.
+var RESERVED_CHARS_RE = '\\[\\]\\.:\\/';
+
 function Composite( targetGroup, path, optionalParsedPath ) {
 
 	var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );
@@ -109,41 +112,53 @@ Object.assign( PropertyBinding, {
 	 * @param  {string} name Node name to be sanitized.
 	 * @return {string}
 	 */
-	sanitizeNodeName: function ( name ) {
+	sanitizeNodeName: ( function () {
 
-		return name.replace( /\s/g, '_' ).replace( /[^\w-]/g, '' );
+		var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );
 
-	},
+		return function sanitizeNodeName ( name ) {
+
+			return name.replace( /\s/g, '_' ).replace( reservedRe, '' );
+
+		};
+
+	}() ),
 
 	parseTrackName: function () {
 
+		// Attempts to allow node names from any language. ES5's `\w` regexp matches
+		// only latin characters, and the unicode \p{L} is not yet supported. So
+		// instead, we exclude reserved characters and match everything else.
+		var wordChar = '[^' + RESERVED_CHARS_RE + ']';
+		var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\.', '' ) + ']';
+
 		// Parent directories, delimited by '/' or ':'. Currently unused, but must
 		// be matched to parse the rest of the track name.
-		var directoryRe = /((?:[\w-]+[\/:])*)/;
+		var directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', wordChar );
 
 		// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
-		var nodeRe = /([\w-\.]+)?/;
+		var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );
 
-		// Object on target node, and accessor. Name may contain only word
+		// Object on target node, and accessor. May not contain reserved
 		// characters. Accessor may contain any character except closing bracket.
-		var objectRe = /(?:\.([\w-]+)(?:\[(.+)\])?)?/;
+		var objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', wordChar );
 
-		// Property and accessor. May contain only word characters. Accessor may
+		// Property and accessor. May not contain reserved characters. Accessor may
 		// contain any non-bracket characters.
-		var propertyRe = /\.([\w-]+)(?:\[(.+)\])?/;
+		var propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', wordChar );
 
 		var trackRe = new RegExp( ''
 			+ '^'
-			+ directoryRe.source
-			+ nodeRe.source
-			+ objectRe.source
-			+ propertyRe.source
+			+ directoryRe
+			+ nodeRe
+			+ objectRe
+			+ propertyRe
 			+ '$'
 		);
 
 		var supportedObjectNames = [ 'material', 'materials', 'bones' ];
 
-		return function ( trackName ) {
+		return function parseTrackName( trackName ) {
 
 			var matches = trackRe.exec( trackName );
 
@@ -204,9 +219,9 @@ Object.assign( PropertyBinding, {
 		// search into skeleton bones.
 		if ( root.skeleton ) {
 
-			var bone = PropertyBinding.findBone( root, nodeName );
+			var bone = root.skeleton.getBoneByName( nodeName );
 
-			if ( bone ) {
+			if ( bone !== undefined ) {
 
 				return bone;
 

+ 9 - 9
src/constants.js

@@ -92,15 +92,15 @@ export var LuminanceAlphaFormat = 1025;
 export var RGBEFormat = RGBAFormat;
 export var DepthFormat = 1026;
 export var DepthStencilFormat = 1027;
-export var RGB_S3TC_DXT1_Format = 2001;
-export var RGBA_S3TC_DXT1_Format = 2002;
-export var RGBA_S3TC_DXT3_Format = 2003;
-export var RGBA_S3TC_DXT5_Format = 2004;
-export var RGB_PVRTC_4BPPV1_Format = 2100;
-export var RGB_PVRTC_2BPPV1_Format = 2101;
-export var RGBA_PVRTC_4BPPV1_Format = 2102;
-export var RGBA_PVRTC_2BPPV1_Format = 2103;
-export var RGB_ETC1_Format = 2151;
+export var RGB_S3TC_DXT1_Format = 33776;
+export var RGBA_S3TC_DXT1_Format = 33777;
+export var RGBA_S3TC_DXT3_Format = 33778;
+export var RGBA_S3TC_DXT5_Format = 33779;
+export var RGB_PVRTC_4BPPV1_Format = 35840;
+export var RGB_PVRTC_2BPPV1_Format = 35841;
+export var RGBA_PVRTC_4BPPV1_Format = 35842;
+export var RGBA_PVRTC_2BPPV1_Format = 35843;
+export var RGB_ETC1_Format = 36196;
 export var RGBA_ASTC_4x4_Format = 37808;
 export var RGBA_ASTC_5x4_Format = 37809;
 export var RGBA_ASTC_5x5_Format = 37810;

+ 2 - 1
src/extras/ShapeUtils.js

@@ -41,9 +41,10 @@ var ShapeUtils = {
 		//
 
 		var holeIndex = contour.length;
+
 		holes.forEach( removeDupEndPts );
 
-		for ( i = 0; i < holes.length; i ++ ) {
+		for ( var i = 0; i < holes.length; i ++ ) {
 
 			holeIndices.push( holeIndex );
 			holeIndex += holes[ i ].length;

+ 89 - 94
src/extras/core/Font.js

@@ -21,167 +21,162 @@ Object.assign( Font.prototype, {
 
 	generateShapes: function ( text, size, divisions ) {
 
-		function createPaths( text ) {
-
-			var chars = String( text ).split( '' );
-			var scale = size / data.resolution;
-			var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
-
-			var offsetX = 0, offsetY = 0;
+		if ( size === undefined ) size = 100;
+		if ( divisions === undefined ) divisions = 4;
 
-			var paths = [];
+		var shapes = [];
+		var paths = createPaths( text, size, divisions, this.data );
 
-			for ( var i = 0; i < chars.length; i ++ ) {
+		for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
 
-				var char = chars[ i ];
+			Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
 
-				if ( char === '\n' ) {
+		}
 
-					offsetX = 0;
-					offsetY -= line_height;
+		return shapes;
 
-				} else {
+	}
 
-					var ret = createPath( char, scale, offsetX, offsetY );
-					offsetX += ret.offsetX;
-					paths.push( ret.path );
+} );
 
-				}
+function createPaths( text, size, divisions, data ) {
 
-			}
+	var chars = String( text ).split( '' );
+	var scale = size / data.resolution;
+	var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 
-			return paths;
+	var paths = [];
 
-		}
+	var offsetX = 0, offsetY = 0;
 
-		function createPath( c, scale, offsetX, offsetY ) {
+	for ( var i = 0; i < chars.length; i ++ ) {
 
-			var glyph = data.glyphs[ c ] || data.glyphs[ '?' ];
+		var char = chars[ i ];
 
-			if ( ! glyph ) return;
+		if ( char === '\n' ) {
 
-			var path = new ShapePath();
+			offsetX = 0;
+			offsetY -= line_height;
 
-			var pts = [];
-			var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
+		} else {
 
-			if ( glyph.o ) {
+			var ret = createPath( char, divisions, scale, offsetX, offsetY, data );
+			offsetX += ret.offsetX;
+			paths.push( ret.path );
 
-				var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+		}
 
-				for ( var i = 0, l = outline.length; i < l; ) {
+	}
 
-					var action = outline[ i ++ ];
+	return paths;
 
-					switch ( action ) {
+}
 
-						case 'm': // moveTo
+function createPath( char, divisions, scale, offsetX, offsetY, data ) {
 
-							x = outline[ i ++ ] * scale + offsetX;
-							y = outline[ i ++ ] * scale + offsetY;
+	var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
 
-							path.moveTo( x, y );
+	if ( ! glyph ) return;
 
-							break;
+	var path = new ShapePath();
 
-						case 'l': // lineTo
+	var pts = [];
+	var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste;
 
-							x = outline[ i ++ ] * scale + offsetX;
-							y = outline[ i ++ ] * scale + offsetY;
+	if ( glyph.o ) {
 
-							path.lineTo( x, y );
+		var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
 
-							break;
+		for ( var i = 0, l = outline.length; i < l; ) {
 
-						case 'q': // quadraticCurveTo
+			var action = outline[ i ++ ];
 
-							cpx = outline[ i ++ ] * scale + offsetX;
-							cpy = outline[ i ++ ] * scale + offsetY;
-							cpx1 = outline[ i ++ ] * scale + offsetX;
-							cpy1 = outline[ i ++ ] * scale + offsetY;
+			switch ( action ) {
 
-							path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+				case 'm': // moveTo
 
-							laste = pts[ pts.length - 1 ];
+					x = outline[ i ++ ] * scale + offsetX;
+					y = outline[ i ++ ] * scale + offsetY;
 
-							if ( laste ) {
+					path.moveTo( x, y );
 
-								cpx0 = laste.x;
-								cpy0 = laste.y;
+					break;
 
-								for ( var i2 = 1; i2 <= divisions; i2 ++ ) {
+				case 'l': // lineTo
 
-									var t = i2 / divisions;
-									QuadraticBezier( t, cpx0, cpx1, cpx );
-									QuadraticBezier( t, cpy0, cpy1, cpy );
+					x = outline[ i ++ ] * scale + offsetX;
+					y = outline[ i ++ ] * scale + offsetY;
 
-								}
+					path.lineTo( x, y );
 
-							}
+					break;
 
-							break;
+				case 'q': // quadraticCurveTo
 
-						case 'b': // bezierCurveTo
+					cpx = outline[ i ++ ] * scale + offsetX;
+					cpy = outline[ i ++ ] * scale + offsetY;
+					cpx1 = outline[ i ++ ] * scale + offsetX;
+					cpy1 = outline[ i ++ ] * scale + offsetY;
 
-							cpx = outline[ i ++ ] * scale + offsetX;
-							cpy = outline[ i ++ ] * scale + offsetY;
-							cpx1 = outline[ i ++ ] * scale + offsetX;
-							cpy1 = outline[ i ++ ] * scale + offsetY;
-							cpx2 = outline[ i ++ ] * scale + offsetX;
-							cpy2 = outline[ i ++ ] * scale + offsetY;
+					path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
 
-							path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+					laste = pts[ pts.length - 1 ];
 
-							laste = pts[ pts.length - 1 ];
+					if ( laste ) {
 
-							if ( laste ) {
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-								cpx0 = laste.x;
-								cpy0 = laste.y;
+						for ( var i2 = 1; i2 <= divisions; i2 ++ ) {
 
-								for ( var i2 = 1; i2 <= divisions; i2 ++ ) {
+							var t = i2 / divisions;
+							QuadraticBezier( t, cpx0, cpx1, cpx );
+							QuadraticBezier( t, cpy0, cpy1, cpy );
 
-									var t = i2 / divisions;
-									CubicBezier( t, cpx0, cpx1, cpx2, cpx );
-									CubicBezier( t, cpy0, cpy1, cpy2, cpy );
+						}
 
-								}
+					}
 
-							}
+					break;
 
-							break;
+				case 'b': // bezierCurveTo
 
-					}
+					cpx = outline[ i ++ ] * scale + offsetX;
+					cpy = outline[ i ++ ] * scale + offsetY;
+					cpx1 = outline[ i ++ ] * scale + offsetX;
+					cpy1 = outline[ i ++ ] * scale + offsetY;
+					cpx2 = outline[ i ++ ] * scale + offsetX;
+					cpy2 = outline[ i ++ ] * scale + offsetY;
 
-				}
+					path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
 
-			}
+					laste = pts[ pts.length - 1 ];
 
-			return { offsetX: glyph.ha * scale, path: path };
+					if ( laste ) {
 
-		}
+						cpx0 = laste.x;
+						cpy0 = laste.y;
 
-		//
+						for ( var i2 = 1; i2 <= divisions; i2 ++ ) {
 
-		if ( size === undefined ) size = 100;
-		if ( divisions === undefined ) divisions = 4;
+							var t = i2 / divisions;
+							CubicBezier( t, cpx0, cpx1, cpx2, cpx );
+							CubicBezier( t, cpy0, cpy1, cpy2, cpy );
 
-		var data = this.data;
+						}
 
-		var paths = createPaths( text );
-		var shapes = [];
+					}
 
-		for ( var p = 0, pl = paths.length; p < pl; p ++ ) {
+					break;
 
-			Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+			}
 
 		}
 
-		return shapes;
-
 	}
 
-} );
+	return { offsetX: glyph.ha * scale, path: path };
 
+}
 
 export { Font };

+ 1 - 1
src/loaders/JSONLoader.js

@@ -525,7 +525,7 @@ Object.assign( JSONLoader.prototype, {
 
 		}
 
-		return function ( json, texturePath ) {
+		return function parse( json, texturePath ) {
 
 			if ( json.data !== undefined ) {
 

+ 1 - 1
src/math/Math.js

@@ -20,7 +20,7 @@ var _Math = {
 
 		}
 
-		return function () {
+		return function generateUUID() {
 
 			var d0 = Math.random() * 0xffffffff | 0;
 			var d1 = Math.random() * 0xffffffff | 0;

+ 18 - 0
src/objects/Skeleton.js

@@ -152,6 +152,24 @@ Object.assign( Skeleton.prototype, {
 
 		return new Skeleton( this.bones, this.boneInverses );
 
+	},
+
+	getBoneByName: function ( name ) {
+
+		for ( var i = 0, il = this.bones.length; i < il; i ++ ) {
+
+			var bone = this.bones[ i ];
+
+			if ( bone.name === name ) {
+
+				return bone;
+
+			}
+
+		}
+
+		return undefined;
+
 	}
 
 } );

+ 2 - 1
src/renderers/webgl/WebGLState.js

@@ -491,7 +491,8 @@ function WebGLState( gl, extensions, utils ) {
 
 			if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||
 			     extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||
-			     extensions.get( 'WEBGL_compressed_texture_etc1' ) ) {
+			     extensions.get( 'WEBGL_compressed_texture_etc1' ) ||
+			     extensions.get( 'WEBGL_compressed_texture_astc' ) ) {
 
 				var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );
 

+ 38 - 3
test/unit/src/animation/PropertyBinding.tests.js

@@ -41,6 +41,12 @@ export default QUnit.module( 'Animation', () => {
 				'Leaves valid name intact.'
 			);
 
+			assert.equal(
+				PropertyBinding.sanitizeNodeName( '急須' ),
+				'急須',
+				'Leaves non-latin unicode characters intact.'
+			);
+
 			assert.equal(
 				PropertyBinding.sanitizeNodeName( 'space separated name 123_ -' ),
 				'space_separated_name_123__-',
@@ -48,9 +54,15 @@ export default QUnit.module( 'Animation', () => {
 			);
 
 			assert.equal(
-				PropertyBinding.sanitizeNodeName( '"invalid" name %123%_' ),
-				'invalid_name_123_',
-				'Strips invalid characters.'
+				PropertyBinding.sanitizeNodeName( '"Mátyás" %_* 😇' ),
+				'"Mátyás"_%_*_😇',
+				'Allows various punctuation and symbols.'
+			);
+
+			assert.equal(
+				PropertyBinding.sanitizeNodeName( '/invalid: name ^123.[_]' ),
+				'invalid_name_^123_',
+				'Strips reserved characters.'
 			);
 
 		} );
@@ -236,7 +248,30 @@ export default QUnit.module( 'Animation', () => {
 						propertyName: 'position',
 						propertyIndex: undefined
 					}
+				],
+
+				[
+					'急須.材料[零]',
+					{
+						nodeName: '急須',
+						objectName: undefined,
+						objectIndex: undefined,
+						propertyName: '材料',
+						propertyIndex: '零'
+					}
+				],
+
+				[
+					'📦.🎨[🔴]',
+					{
+						nodeName: '📦',
+						objectName: undefined,
+						objectIndex: undefined,
+						propertyName: '🎨',
+						propertyIndex: '🔴'
+					}
 				]
+
 			];
 
 			paths.forEach( function ( path ) {

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно