Sfoglia il codice sorgente

Merge remote-tracking branch 'upstream/dev' into dev5

Mugen87 7 anni fa
parent
commit
ce2395137f
42 ha cambiato i file con 945 aggiunte e 726 eliminazioni
  1. 74 18
      build/three.js
  2. 256 255
      build/three.min.js
  3. 74 18
      build/three.module.js
  4. 1 1
      docs/api/extras/curves/CatmullRomCurve3.html
  5. 19 0
      docs/api/loaders/managers/LoadingManager.html
  6. 0 6
      docs/api/objects/SkinnedMesh.html
  7. 8 0
      docs/examples/loaders/LoaderSupport.html
  8. 10 4
      editor/index.html
  9. 1 1
      examples/js/ParametricGeometries.js
  10. 291 320
      examples/js/loaders/FBXLoader.js
  11. 17 9
      examples/js/loaders/GLTFLoader.js
  12. 25 12
      examples/js/loaders/LoaderSupport.js
  13. 33 0
      examples/js/loaders/OBJLoader.js
  14. 36 54
      examples/js/loaders/OBJLoader2.js
  15. 1 1
      examples/webgl_geometry_extrude_shapes.html
  16. 1 1
      examples/webgl_geometry_extrude_splines.html
  17. 3 3
      examples/webgl_geometry_spline_editor.html
  18. 2 0
      src/extras/core/Curve.js
  19. 2 1
      src/extras/core/CurvePath.js
  20. 2 0
      src/extras/core/Font.js
  21. 3 0
      src/extras/core/Path.js
  22. 2 0
      src/extras/core/Shape.js
  23. 2 0
      src/extras/core/ShapePath.js
  24. 2 0
      src/extras/curves/ArcCurve.js
  25. 6 3
      src/extras/curves/CatmullRomCurve3.js
  26. 2 0
      src/extras/curves/CubicBezierCurve.js
  27. 2 0
      src/extras/curves/CubicBezierCurve3.js
  28. 2 0
      src/extras/curves/EllipseCurve.js
  29. 2 0
      src/extras/curves/LineCurve.js
  30. 2 0
      src/extras/curves/LineCurve3.js
  31. 2 0
      src/extras/curves/QuadraticBezierCurve.js
  32. 2 0
      src/extras/curves/QuadraticBezierCurve3.js
  33. 2 0
      src/extras/curves/SplineCurve.js
  34. 2 0
      src/loaders/FileLoader.js
  35. 2 0
      src/loaders/ImageLoader.js
  36. 22 1
      src/loaders/LoadingManager.js
  37. 7 5
      src/math/Math.js
  38. 1 7
      src/math/Vector3.js
  39. 3 1
      src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl
  40. 1 1
      test/Three.Unit.js
  41. 1 1
      test/unit/src/helpers/AxesHelper.js
  42. 19 3
      test/unit/src/loaders/LoadingManager.js

+ 74 - 18
build/three.js

@@ -326,35 +326,37 @@
 		generateUUID: function () {
 
 			// http://www.broofa.com/Tools/Math.uuid.htm
+			// Replaced .join with string concatenation (@takahirox)
 
 			var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
-			var uuid = new Array( 36 );
 			var rnd = 0, r;
 
 			return function generateUUID() {
 
+				var uuid = '';
+
 				for ( var i = 0; i < 36; i ++ ) {
 
 					if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
 
-						uuid[ i ] = '-';
+						uuid += '-';
 
 					} else if ( i === 14 ) {
 
-						uuid[ i ] = '4';
+						uuid += '4';
 
 					} else {
 
 						if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
 						r = rnd & 0xf;
 						rnd = rnd >> 4;
-						uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+						uuid += chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
 
 					}
 
 				}
 
-				return uuid.join( '' );
+				return uuid;
 
 			};
 
@@ -3027,13 +3029,7 @@
 
 			}
 
-			var x = this.x, y = this.y, z = this.z;
-
-			this.x = y * v.z - z * v.y;
-			this.y = z * v.x - x * v.z;
-			this.z = x * v.y - y * v.x;
-
-			return this;
+			return this.crossVectors( this, v );
 
 		},
 
@@ -6235,7 +6231,7 @@
 
 	var tonemapping_fragment = "#if defined( TONE_MAPPING )\n  gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
 
-	var tonemapping_pars_fragment = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
+	var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
 
 	var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif";
 
@@ -25046,6 +25042,8 @@
 
 		function update() {
 
+			var video = scope.image;
+
 			if ( video.readyState >= video.HAVE_CURRENT_DATA ) {
 
 				scope.needsUpdate = true;
@@ -30191,7 +30189,10 @@
 
 		var scope = this;
 
-		var isLoading = false, itemsLoaded = 0, itemsTotal = 0;
+		var isLoading = false;
+		var itemsLoaded = 0;
+		var itemsTotal = 0;
+		var urlModifier = undefined;
 
 		this.onStart = undefined;
 		this.onLoad = onLoad;
@@ -30250,6 +30251,24 @@
 
 		};
 
+		this.resolveURL = function ( url ) {
+
+			if ( urlModifier ) {
+
+				return urlModifier( url );
+
+			}
+
+			return url;
+
+		};
+
+		this.setURLModifier = function ( transform ) {
+
+			urlModifier = transform;
+
+		};
+
 	}
 
 	var DefaultLoadingManager = new LoadingManager();
@@ -30274,6 +30293,8 @@
 
 			if ( this.path !== undefined ) url = this.path + url;
 
+			url = this.manager.resolveURL( url );
+
 			var scope = this;
 
 			var cached = Cache.get( url );
@@ -30799,6 +30820,8 @@
 
 			if ( this.path !== undefined ) url = this.path + url;
 
+			url = this.manager.resolveURL( url );
+
 			var scope = this;
 
 			var cached = Cache.get( url );
@@ -35241,6 +35264,8 @@
 
 	function Curve() {
 
+		this.type = 'Curve';
+
 		this.arcLengthDivisions = 200;
 
 	}
@@ -35586,6 +35611,8 @@
 
 		Curve.call( this );
 
+		this.type = 'LineCurve';
+
 		this.v1 = v1;
 		this.v2 = v2;
 
@@ -35645,8 +35672,9 @@
 
 		Curve.call( this );
 
-		this.curves = [];
+		this.type = 'CurvePath';
 
+		this.curves = [];
 		this.autoClose = false; // Automatically closes the path
 
 	}
@@ -35834,6 +35862,8 @@
 
 		Curve.call( this );
 
+		this.type = 'EllipseCurve';
+
 		this.aX = aX;
 		this.aY = aY;
 
@@ -35920,6 +35950,8 @@
 
 		Curve.call( this );
 
+		this.type = 'SplineCurve';
+
 		this.points = ( points === undefined ) ? [] : points;
 
 	}
@@ -35957,6 +35989,8 @@
 
 		Curve.call( this );
 
+		this.type = 'CubicBezierCurve';
+
 		this.v0 = v0;
 		this.v1 = v1;
 		this.v2 = v2;
@@ -35988,6 +36022,8 @@
 
 		Curve.call( this );
 
+		this.type = 'QuadraticBezierCurve';
+
 		this.v0 = v0;
 		this.v1 = v1;
 		this.v2 = v2;
@@ -36142,6 +36178,9 @@
 	function Path( points ) {
 
 		CurvePath.call( this );
+
+		this.type = 'Path';
+
 		this.currentPoint = new Vector2();
 
 		if ( points ) {
@@ -36170,6 +36209,8 @@
 
 		Path.apply( this, arguments );
 
+		this.type = 'Shape';
+
 		this.holes = [];
 
 	}
@@ -36220,6 +36261,8 @@
 
 	function ShapePath() {
 
+		this.type = 'ShapePath';
+
 		this.subPaths = [];
 		this.currentPath = null;
 
@@ -36495,6 +36538,8 @@
 
 	function Font( data ) {
 
+		this.type = 'Font';
+
 		this.data = data;
 
 	}
@@ -42533,10 +42578,13 @@
 
 		Curve.call( this );
 
+		this.type = 'CatmullRomCurve3';
+
 		if ( points.length < 2 ) console.warn( 'THREE.CatmullRomCurve3: Points array needs at least two entries.' );
 
 		this.points = points || [];
 		this.closed = false;
+		this.curveType = 'centripetal';
 
 	}
 
@@ -42596,10 +42644,10 @@
 
 		}
 
-		if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
+		if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
 
 			// init Centripetal / Chordal Catmull-Rom
-			var pow = this.type === 'chordal' ? 0.5 : 0.25;
+			var pow = this.curveType === 'chordal' ? 0.5 : 0.25;
 			var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
 			var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
 			var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
@@ -42613,7 +42661,7 @@
 			py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
 			pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
 
-		} else if ( this.type === 'catmullrom' ) {
+		} else if ( this.curveType === 'catmullrom' ) {
 
 			var tension = this.tension !== undefined ? this.tension : 0.5;
 			px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
@@ -42636,6 +42684,8 @@
 
 		Curve.call( this );
 
+		this.type = 'CubicBezierCurve3';
+
 		this.v0 = v0;
 		this.v1 = v1;
 		this.v2 = v2;
@@ -42668,6 +42718,8 @@
 
 		Curve.call( this );
 
+		this.type = 'QuadraticBezierCurve3';
+
 		this.v0 = v0;
 		this.v1 = v1;
 		this.v2 = v2;
@@ -42699,6 +42751,8 @@
 
 		Curve.call( this );
 
+		this.type = 'LineCurve3';
+
 		this.v1 = v1;
 		this.v2 = v2;
 
@@ -42740,6 +42794,8 @@
 
 		EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
 
+		this.type = 'ArcCurve';
+
 	}
 
 	ArcCurve.prototype = Object.create( EllipseCurve.prototype );

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


+ 74 - 18
build/three.module.js

@@ -320,35 +320,37 @@ var _Math = {
 	generateUUID: function () {
 
 		// http://www.broofa.com/Tools/Math.uuid.htm
+		// Replaced .join with string concatenation (@takahirox)
 
 		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
-		var uuid = new Array( 36 );
 		var rnd = 0, r;
 
 		return function generateUUID() {
 
+			var uuid = '';
+
 			for ( var i = 0; i < 36; i ++ ) {
 
 				if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
 
-					uuid[ i ] = '-';
+					uuid += '-';
 
 				} else if ( i === 14 ) {
 
-					uuid[ i ] = '4';
+					uuid += '4';
 
 				} else {
 
 					if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
 					r = rnd & 0xf;
 					rnd = rnd >> 4;
-					uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+					uuid += chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
 
 				}
 
 			}
 
-			return uuid.join( '' );
+			return uuid;
 
 		};
 
@@ -3021,13 +3023,7 @@ Object.assign( Vector3.prototype, {
 
 		}
 
-		var x = this.x, y = this.y, z = this.z;
-
-		this.x = y * v.z - z * v.y;
-		this.y = z * v.x - x * v.z;
-		this.z = x * v.y - y * v.x;
-
-		return this;
+		return this.crossVectors( this, v );
 
 	},
 
@@ -6229,7 +6225,7 @@ var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D spe
 
 var tonemapping_fragment = "#if defined( TONE_MAPPING )\n  gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n";
 
-var tonemapping_pars_fragment = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
+var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n";
 
 var uv_pars_fragment = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n\tvarying vec2 vUv;\n#endif";
 
@@ -25040,6 +25036,8 @@ function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, forma
 
 	function update() {
 
+		var video = scope.image;
+
 		if ( video.readyState >= video.HAVE_CURRENT_DATA ) {
 
 			scope.needsUpdate = true;
@@ -30185,7 +30183,10 @@ function LoadingManager( onLoad, onProgress, onError ) {
 
 	var scope = this;
 
-	var isLoading = false, itemsLoaded = 0, itemsTotal = 0;
+	var isLoading = false;
+	var itemsLoaded = 0;
+	var itemsTotal = 0;
+	var urlModifier = undefined;
 
 	this.onStart = undefined;
 	this.onLoad = onLoad;
@@ -30244,6 +30245,24 @@ function LoadingManager( onLoad, onProgress, onError ) {
 
 	};
 
+	this.resolveURL = function ( url ) {
+
+		if ( urlModifier ) {
+
+			return urlModifier( url );
+
+		}
+
+		return url;
+
+	};
+
+	this.setURLModifier = function ( transform ) {
+
+		urlModifier = transform;
+
+	};
+
 }
 
 var DefaultLoadingManager = new LoadingManager();
@@ -30268,6 +30287,8 @@ Object.assign( FileLoader.prototype, {
 
 		if ( this.path !== undefined ) url = this.path + url;
 
+		url = this.manager.resolveURL( url );
+
 		var scope = this;
 
 		var cached = Cache.get( url );
@@ -30793,6 +30814,8 @@ Object.assign( ImageLoader.prototype, {
 
 		if ( this.path !== undefined ) url = this.path + url;
 
+		url = this.manager.resolveURL( url );
+
 		var scope = this;
 
 		var cached = Cache.get( url );
@@ -35235,6 +35258,8 @@ function CubicBezier( t, p0, p1, p2, p3 ) {
 
 function Curve() {
 
+	this.type = 'Curve';
+
 	this.arcLengthDivisions = 200;
 
 }
@@ -35580,6 +35605,8 @@ function LineCurve( v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'LineCurve';
+
 	this.v1 = v1;
 	this.v2 = v2;
 
@@ -35639,8 +35666,9 @@ function CurvePath() {
 
 	Curve.call( this );
 
-	this.curves = [];
+	this.type = 'CurvePath';
 
+	this.curves = [];
 	this.autoClose = false; // Automatically closes the path
 
 }
@@ -35828,6 +35856,8 @@ function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockw
 
 	Curve.call( this );
 
+	this.type = 'EllipseCurve';
+
 	this.aX = aX;
 	this.aY = aY;
 
@@ -35914,6 +35944,8 @@ function SplineCurve( points /* array of Vector2 */ ) {
 
 	Curve.call( this );
 
+	this.type = 'SplineCurve';
+
 	this.points = ( points === undefined ) ? [] : points;
 
 }
@@ -35951,6 +35983,8 @@ function CubicBezierCurve( v0, v1, v2, v3 ) {
 
 	Curve.call( this );
 
+	this.type = 'CubicBezierCurve';
+
 	this.v0 = v0;
 	this.v1 = v1;
 	this.v2 = v2;
@@ -35982,6 +36016,8 @@ function QuadraticBezierCurve( v0, v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'QuadraticBezierCurve';
+
 	this.v0 = v0;
 	this.v1 = v1;
 	this.v2 = v2;
@@ -36136,6 +36172,9 @@ var PathPrototype = Object.assign( Object.create( CurvePath.prototype ), {
 function Path( points ) {
 
 	CurvePath.call( this );
+
+	this.type = 'Path';
+
 	this.currentPoint = new Vector2();
 
 	if ( points ) {
@@ -36164,6 +36203,8 @@ function Shape() {
 
 	Path.apply( this, arguments );
 
+	this.type = 'Shape';
+
 	this.holes = [];
 
 }
@@ -36214,6 +36255,8 @@ Shape.prototype = Object.assign( Object.create( PathPrototype ), {
 
 function ShapePath() {
 
+	this.type = 'ShapePath';
+
 	this.subPaths = [];
 	this.currentPath = null;
 
@@ -36489,6 +36532,8 @@ Object.assign( ShapePath.prototype, {
 
 function Font( data ) {
 
+	this.type = 'Font';
+
 	this.data = data;
 
 }
@@ -42527,10 +42572,13 @@ function CatmullRomCurve3( points ) {
 
 	Curve.call( this );
 
+	this.type = 'CatmullRomCurve3';
+
 	if ( points.length < 2 ) console.warn( 'THREE.CatmullRomCurve3: Points array needs at least two entries.' );
 
 	this.points = points || [];
 	this.closed = false;
+	this.curveType = 'centripetal';
 
 }
 
@@ -42590,10 +42638,10 @@ CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {
 
 	}
 
-	if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
+	if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
 
 		// init Centripetal / Chordal Catmull-Rom
-		var pow = this.type === 'chordal' ? 0.5 : 0.25;
+		var pow = this.curveType === 'chordal' ? 0.5 : 0.25;
 		var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
 		var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
 		var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
@@ -42607,7 +42655,7 @@ CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {
 		py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
 		pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
 
-	} else if ( this.type === 'catmullrom' ) {
+	} else if ( this.curveType === 'catmullrom' ) {
 
 		var tension = this.tension !== undefined ? this.tension : 0.5;
 		px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
@@ -42630,6 +42678,8 @@ function CubicBezierCurve3( v0, v1, v2, v3 ) {
 
 	Curve.call( this );
 
+	this.type = 'CubicBezierCurve3';
+
 	this.v0 = v0;
 	this.v1 = v1;
 	this.v2 = v2;
@@ -42662,6 +42712,8 @@ function QuadraticBezierCurve3( v0, v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'QuadraticBezierCurve3';
+
 	this.v0 = v0;
 	this.v1 = v1;
 	this.v2 = v2;
@@ -42693,6 +42745,8 @@ function LineCurve3( v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'LineCurve3';
+
 	this.v1 = v1;
 	this.v2 = v2;
 
@@ -42734,6 +42788,8 @@ function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
 
 	EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
 
+	this.type = 'ArcCurve';
+
 }
 
 ArcCurve.prototype = Object.create( EllipseCurve.prototype );

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

@@ -65,7 +65,7 @@ var curveObject = new THREE.Line( geometry, material );
 		<h3>[property:Boolean closed]</h3>
 		<div>The curve will loop back onto itself when this is true.</div>
 
-		<h3>[property:String type]</h3>
+		<h3>[property:String curveType]</h3>
 		<div>Possible values are `centripetal` (default), `chordal` and `catmullrom`.</div>
 
 		<h3>[property:float tension]</h3>

+ 19 - 0
docs/api/loaders/managers/LoadingManager.html

@@ -121,6 +121,25 @@
 
 		<h2>Methods</h2>
 
+		<h3>[method:null setURLModifier]( [page:Function callback] )</h3>
+		<div>
+		[page:Function callback] — URL modifier callback. Called with [page:String url] argument, and
+		must return [page:String resolvedURL].<br /><br />
+
+		If provided, the callback will be passed each resource URL before a request is sent. The
+		callback may return the original URL, or a new URL to override loading behavior. This
+		behavior can be used to load assets from .ZIP files, drag-and-drop APIs, and Data URIs.
+		</div>
+
+		<h3>[method:String resolveURL]( [page:String url] )</h3>
+		<div>
+		[page:String url] — the url to load<br /><br />
+
+		Given a URL, uses the URL modifier callback (if any) and returns a resolved URL. If no
+		URL modifier is set, returns the original URL.
+		</div>
+
+		<br /><br />
 		<div>
 			<em>Note: The following methods are designed to be called internally by loaders. You shouldn't call
 			them directly.</em>

+ 0 - 6
docs/api/objects/SkinnedMesh.html

@@ -97,12 +97,6 @@
 		The base matrix that is used for resetting the bound bone transforms.
 		</div>
 
-		<h3>[property:array bones]</h3>
-		<div>
-		This contains the array of [page:Bone bones] for this mesh. These should be set in
-		the constructor.
-		</div>
-
 		<h3>[property:Boolean isSkinnedMesh]</h3>
 		<div>
 			Used to check whether this or derived classes are skinned meshes. Default is *true*.<br /><br />

+ 8 - 0
docs/examples/loaders/LoaderSupport.html

@@ -520,6 +520,14 @@
 		<div>
 			Register callback function that is called once loading of the complete OBJ file is completed.
 		</div>
+
+		<h3>[method:null setCallbackOnLoadMaterials]( [page:Function callbackOnLoadMaterials] )</h3>
+		<div>
+			[page:Function callbackOnLoadMaterials] - Callback function for described functionality
+		</div>
+		<div>
+			Register callback function that is called when materials have been loaded.
+		</div>
 		<br>
 		<br>
 

+ 10 - 4
editor/index.html

@@ -163,6 +163,8 @@
 			window.URL = window.URL || window.webkitURL;
 			window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
 
+			const IS_MAC = navigator.platform.toUpperCase().indexOf( 'MAC' ) >= 0;
+
 			Number.prototype.format = function (){
 				return this.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,");
 			};
@@ -311,13 +313,17 @@
 
 					case 90: // Register Ctrl-Z for Undo, Ctrl-Shift-Z for Redo
 
-						if ( event.ctrlKey && event.shiftKey ) {
+						if ( IS_MAC ? event.metaKey : event.ctrlKey ) {
+
+							if ( event.shiftKey ) {
+
+								editor.redo();
 
-							editor.redo();
+							} else {
 
-						} else if ( event.ctrlKey ) {
+								editor.undo();
 
-							editor.undo();
+							}
 
 						}
 

+ 1 - 1
examples/js/ParametricGeometries.js

@@ -241,7 +241,7 @@ THREE.ParametricGeometries.SphereGeometry = function ( size, u, v ) {
 
 	}
 
-	THREE.ParametricGeometry.call( this, sphere, u, v, ! true );
+	THREE.ParametricGeometry.call( this, sphere, u, v );
 
 };
 

File diff suppressed because it is too large
+ 291 - 320
examples/js/loaders/FBXLoader.js


+ 17 - 9
examples/js/loaders/GLTFLoader.js

@@ -24,7 +24,7 @@ THREE.GLTFLoader = ( function () {
 
 			var scope = this;
 
-			var path = this.path && ( typeof this.path === 'string' ) ? this.path : THREE.Loader.prototype.extractUrlBase( url );
+			var path = this.path !== undefined ? this.path : THREE.Loader.prototype.extractUrlBase( url );
 
 			var loader = new THREE.FileLoader( scope.manager );
 
@@ -68,16 +68,24 @@ THREE.GLTFLoader = ( function () {
 			var content;
 			var extensions = {};
 
-			var magic = convertUint8ArrayToString( new Uint8Array( data, 0, 4 ) );
+			if ( typeof data === 'string' ) {
 
-			if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
-
-				extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
-				content = extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content;
+				content = data;
 
 			} else {
 
-				content = convertUint8ArrayToString( new Uint8Array( data ) );
+				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 ) );
+
+				}
 
 			}
 
@@ -116,7 +124,7 @@ THREE.GLTFLoader = ( function () {
 
 			var parser = new GLTFParser( json, extensions, {
 
-				path: path || this.path,
+				path: path || this.path || '',
 				crossOrigin: this.crossOrigin,
 				manager: this.manager
 
@@ -1109,7 +1117,7 @@ THREE.GLTFLoader = ( function () {
 		}
 
 		// Relative URL
-		return ( path || '' ) + url;
+		return path + url;
 
 	}
 

+ 25 - 12
examples/js/loaders/LoaderSupport.js

@@ -163,6 +163,7 @@ THREE.LoaderSupport.Callbacks = (function () {
 		this.onProgress = null;
 		this.onMeshAlter = null;
 		this.onLoad = null;
+		this.onLoadMaterials = null;
 	}
 
 	/**
@@ -196,6 +197,16 @@ THREE.LoaderSupport.Callbacks = (function () {
 		this.onLoad = Validator.verifyInput( callbackOnLoad, this.onLoad );
 	};
 
+	/**
+	 * Register callback function that is called when materials have been loaded.
+	 * @memberOf THREE.LoaderSupport.Callbacks
+	 *
+	 * @param {callback} callbackOnLoadMaterials Callback function for described functionality
+	 */
+	Callbacks.prototype.setCallbackOnLoadMaterials = function ( callbackOnLoadMaterials ) {
+		this.onLoadMaterials = Validator.verifyInput( callbackOnLoadMaterials, this.onLoadMaterials );
+	};
+
 	return Callbacks;
 })();
 
@@ -424,7 +435,7 @@ THREE.LoaderSupport.PrepData = (function () {
  */
 THREE.LoaderSupport.Builder = (function () {
 
-	var LOADER_BUILDER_VERSION = '1.1.0';
+	var LOADER_BUILDER_VERSION = '1.1.1';
 
 	var Validator = THREE.LoaderSupport.Validator;
 	var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
@@ -448,16 +459,17 @@ THREE.LoaderSupport.Builder = (function () {
 			materials: {
 				materialCloneInstructions: null,
 				serializedMaterials: null,
-				runtimeMaterials: materials
+				runtimeMaterials: Validator.isValid( this.callbacks.onLoadMaterials ) ? this.callbacks.onLoadMaterials( materials ) : materials
 			}
 		};
 		this.updateMaterials( payload );
 	};
 
-	Builder.prototype._setCallbacks = function ( callbackOnProgress, callbackOnMeshAlter, callbackOnLoad ) {
-		this.callbacks.setCallbackOnProgress( callbackOnProgress );
-		this.callbacks.setCallbackOnMeshAlter( callbackOnMeshAlter );
-		this.callbacks.setCallbackOnLoad( callbackOnLoad );
+	Builder.prototype._setCallbacks = function ( callbacks ) {
+		if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
+		if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
+		if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
+		if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
 	};
 
 	/**
@@ -771,16 +783,17 @@ THREE.LoaderSupport.LoaderBase = (function () {
 			this.setUseIndices( prepData.useIndices );
 			this.setDisregardNormals( prepData.disregardNormals );
 
-			this._setCallbacks( prepData.getCallbacks().onProgress, prepData.getCallbacks().onMeshAlter, prepData.getCallbacks().onLoad );
+			this._setCallbacks( prepData.getCallbacks() );
 		}
 	};
 
-	LoaderBase.prototype._setCallbacks = function ( callbackOnProgress, callbackOnMeshAlter, callbackOnLoad ) {
-		this.callbacks.setCallbackOnProgress( callbackOnProgress );
-		this.callbacks.setCallbackOnMeshAlter( callbackOnMeshAlter );
-		this.callbacks.setCallbackOnLoad( callbackOnLoad );
+	LoaderBase.prototype._setCallbacks = function ( callbacks ) {
+		if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress );
+		if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter );
+		if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad );
+		if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials );
 
-		this.builder._setCallbacks( callbackOnProgress, callbackOnMeshAlter, callbackOnLoad );
+		this.builder._setCallbacks( this.callbacks );
 	};
 
 	/**

+ 33 - 0
examples/js/loaders/OBJLoader.js

@@ -19,6 +19,7 @@ THREE.OBJLoader = ( function () {
 
 			vertices : [],
 			normals  : [],
+			colors   : [],
 			uvs      : [],
 
 			materialLibraries : [],
@@ -50,6 +51,7 @@ THREE.OBJLoader = ( function () {
 					geometry : {
 						vertices : [],
 						normals  : [],
+						colors   : [],
 						uvs      : []
 					},
 					materials : [],
@@ -226,6 +228,17 @@ THREE.OBJLoader = ( function () {
 
 			},
 
+			addColor: function ( a, b, c ) {
+
+				var src = this.colors;
+				var dst = this.object.geometry.colors;
+
+				dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
+				dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
+				dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
+
+			},
+
 			addUV: function ( a, b, c ) {
 
 				var src = this.uvs;
@@ -281,6 +294,12 @@ THREE.OBJLoader = ( function () {
 
 				}
 
+				if ( this.colors.length > 0 ) {
+
+					this.addColor( ia, ib, ic );
+
+				}
+
 			},
 
 			addLineGeometry: function ( vertices, uvs ) {
@@ -409,6 +428,14 @@ THREE.OBJLoader = ( function () {
 								parseFloat( data[ 2 ] ),
 								parseFloat( data[ 3 ] )
 							);
+							if ( data.length === 8 ) {
+								state.colors.push(
+									parseFloat( data[ 4 ] ),
+									parseFloat( data[ 5 ] ),
+									parseFloat( data[ 6 ] )
+
+								);
+							}
 							break;
 						case 'vn':
 							state.normals.push(
@@ -588,6 +615,12 @@ THREE.OBJLoader = ( function () {
 
 				}
 
+				if ( geometry.colors.length > 0 ) {
+
+					buffergeometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( geometry.colors ), 3 ) );
+
+				}
+
 				if ( geometry.uvs.length > 0 ) {
 
 					buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );

+ 36 - 54
examples/js/loaders/OBJLoader2.js

@@ -16,10 +16,9 @@ if ( THREE.OBJLoader2 === undefined ) { THREE.OBJLoader2 = {} }
  */
 THREE.OBJLoader2 = (function () {
 
-	var OBJLOADER2_VERSION = '2.1.1';
+	var OBJLOADER2_VERSION = '2.1.2';
 	var LoaderBase = THREE.LoaderSupport.LoaderBase;
 	var Validator = THREE.LoaderSupport.Validator;
-	var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger;
 
 	OBJLoader2.prototype = Object.create( THREE.LoaderSupport.LoaderBase.prototype );
 	OBJLoader2.prototype.constructor = OBJLoader2;
@@ -92,7 +91,9 @@ THREE.OBJLoader2 = (function () {
 
 			} else {
 
-				scope._setCallbacks( null, onMeshAlter, null );
+				var callbacks = new THREE.LoaderSupport.Callbacks();
+				callbacks.setCallbackOnMeshAlter( onMeshAlter );
+				scope._setCallbacks( callbacks );
 				onLoad(
 					{
 						detail: {
@@ -349,6 +350,7 @@ THREE.OBJLoader2 = (function () {
 			};
 			this.logger = logger;
 			this.totalBytes = 0;
+			this.reachedFaces = false;
 		};
 
 		Parser.prototype.setUseAsync = function ( useAsync ) {
@@ -417,7 +419,6 @@ THREE.OBJLoader2 = (function () {
 			var bufferPointer = 0;
 			var slashSpacePattern = new Array( 16 );
 			var slashSpacePatternPointer = 0;
-			var reachedFaces = false;
 			var code;
 			var word = '';
 			var i = 0;
@@ -440,7 +441,7 @@ THREE.OBJLoader2 = (function () {
 					case Consts.CODE_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, reachedFaces, i );
+						this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i );
 						bufferPointer = 0;
 						slashSpacePatternPointer = 0;
 						break;
@@ -473,7 +474,6 @@ THREE.OBJLoader2 = (function () {
 			var bufferPointer = 0;
 			var slashSpacePattern = new Array( 16 );
 			var slashSpacePatternPointer = 0;
-			var reachedFaces = false;
 			var char;
 			var word = '';
 			var i = 0;
@@ -496,7 +496,7 @@ THREE.OBJLoader2 = (function () {
 					case Consts.STRING_LF:
 						if ( word.length > 0 ) buffer[ bufferPointer++ ] = word;
 						word = '';
-						reachedFaces = this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, reachedFaces, i );
+						this.processLine( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i );
 						bufferPointer = 0;
 						slashSpacePatternPointer = 0;
 						break;
@@ -512,8 +512,8 @@ THREE.OBJLoader2 = (function () {
 			this.logger.logTimeEnd( 'OBJLoader2.Parser.parseText' );
 		};
 
-		Parser.prototype.processLine = function ( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, reachedFaces, currentByte ) {
-			if ( bufferPointer < 1 ) return reachedFaces;
+		Parser.prototype.processLine = function ( buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, currentByte ) {
+			if ( bufferPointer < 1 ) return;
 
 			var countSlashes = function ( slashSpacePattern, slashSpacePatternPointer ) {
 				var slashesCount = 0;
@@ -552,15 +552,16 @@ THREE.OBJLoader2 = (function () {
 			switch ( buffer[ 0 ] ) {
 				case Consts.LINE_V:
 					// object complete instance required if reached faces already (= reached next block of v)
-					if ( reachedFaces ) {
+					if ( this.reachedFaces ) {
 
 						if ( this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length ) {
 
 							throw 'Vertex Colors were detected, but vertex count and color count do not match!';
 
 						}
-						this.processCompletedObject( this.rawMesh.objectName, this.rawMesh.groupName, currentByte );
-						reachedFaces = false;
+						// only when new vertices after faces are detected complete new mesh is prepared (reset offsets, etc)
+						this.processCompletedMesh( this.rawMesh.objectName, this.rawMesh.groupName, currentByte, true );
+						this.reachedFaces = false;
 
 					}
 					if ( bufferPointer === 4 ) {
@@ -583,7 +584,7 @@ THREE.OBJLoader2 = (function () {
 					break;
 
 				case Consts.LINE_F:
-					reachedFaces = true;
+					this.reachedFaces = true;
 					this.rawMesh.processFaces( buffer, bufferPointer, countSlashes( slashSpacePattern, slashSpacePatternPointer ) );
 					break;
 
@@ -597,14 +598,12 @@ THREE.OBJLoader2 = (function () {
 					break;
 
 				case Consts.LINE_G:
-					this.processCompletedGroup( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), currentByte );
-					reachedFaces = false;
+					this.processCompletedMesh( this.rawMesh.objectName, concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), currentByte, false );
 					flushStringBuffer( buffer, bufferPointer );
 					break;
 
 				case Consts.LINE_O:
-					this.processCompletedObject( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), this.rawMesh.groupName, currentByte );
-						reachedFaces = false;
+					this.processCompletedMesh( concatStringBuffer( buffer, bufferPointer, slashSpacePattern ), this.rawMesh.groupName, currentByte, false );
 					flushStringBuffer( buffer, bufferPointer );
 					break;
 
@@ -621,7 +620,6 @@ THREE.OBJLoader2 = (function () {
 				default:
 					break;
 			}
-			return reachedFaces;
 		};
 
 		Parser.prototype.createRawMeshReport = function ( rawMesh , inputObjectCount ) {
@@ -638,7 +636,7 @@ THREE.OBJLoader2 = (function () {
 				'\n\tReal RawMeshSubGroup count: ' + report.subGroups;
 		};
 
-		Parser.prototype.processCompletedObject = function ( objectName, groupName, currentByte ) {
+		Parser.prototype.processCompletedMesh = function ( objectName, groupName, currentByte, beginNewObject ) {
 			var result = this.rawMesh.finalize();
 			if ( Validator.isValid( result ) ) {
 
@@ -646,35 +644,13 @@ THREE.OBJLoader2 = (function () {
 				if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
 				this.buildMesh( result, currentByte );
 				var progressBytesPercent = currentByte / this.totalBytes;
-				this.callbackProgress( 'Completed object: ' + objectName + ' Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
-			this.rawMesh = this.rawMesh.newInstanceFromObject( objectName, groupName );
-
-			} else {
-
-				// if a object was set that did not lead to object creation in finalize, then the object name has to be updated
-				this.rawMesh.pushObject( objectName );
-
-			}
-
-		};
-
-		Parser.prototype.processCompletedGroup = function ( groupName, currentByte ) {
-			var result = this.rawMesh.finalize();
-			if ( Validator.isValid( result ) ) {
-
-				this.inputObjectCount++;
-				if ( this.logger.isDebug() ) this.logger.logDebug( this.createRawMeshReport( this.rawMesh, this.inputObjectCount ) );
-				this.buildMesh( result, currentByte );
-				var progressBytesPercent = currentByte / this.totalBytes;
-				this.callbackProgress( 'Completed group: ' + groupName + ' Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
-				this.rawMesh = this.rawMesh.newInstanceFromGroup( groupName );
-
-			} else {
-
-				// if a group was set that did not lead to object creation in finalize, then the group name has to be updated
-				this.rawMesh.pushGroup( groupName );
+				this.callbackProgress( 'Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + ( progressBytesPercent * 100 ).toFixed( 2 ) + '%', progressBytesPercent );
+				this.rawMesh = beginNewObject ? this.rawMesh.newInstanceResetOffsets() : this.rawMesh.newInstanceKeepOffsets();
 
 			}
+			// Always update group an object name in case they have changed and are valid
+			if ( this.rawMesh.objectName !== objectName && Validator.isValid( objectName ) ) this.rawMesh.pushObject( objectName );
+			if ( this.rawMesh.groupName !== groupName && Validator.isValid( groupName ) ) this.rawMesh.pushGroup( groupName );
 		};
 
 		Parser.prototype.finalize = function ( currentByte ) {
@@ -907,7 +883,7 @@ THREE.OBJLoader2 = (function () {
 	 */
 	var RawMesh = (function () {
 
-		function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals, objectName, groupName, activeMtlName ) {
+		function RawMesh( materialPerSmoothingGroup, useIndices, disregardNormals, activeMtlName ) {
 			this.globalVertexOffset = 1;
 			this.globalUvOffset = 1;
 			this.globalNormalOffset = 1;
@@ -919,8 +895,8 @@ THREE.OBJLoader2 = (function () {
 
 			// faces are stored according combined index of group, material and smoothingGroup (0 or not)
 			this.activeMtlName = Validator.verifyInput( activeMtlName, '' );
-			this.objectName = Validator.verifyInput( objectName, '' );
-			this.groupName = Validator.verifyInput( groupName, '' );
+			this.objectName = '';
+			this.groupName = '';
 			this.mtllibName = '';
 			this.smoothingGroup = {
 				splitMaterials: materialPerSmoothingGroup === true,
@@ -942,8 +918,8 @@ THREE.OBJLoader2 = (function () {
 			this.faceCount = 0;
 		}
 
-		RawMesh.prototype.newInstanceFromObject = function ( objectName, groupName ) {
-			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, objectName, groupName, this.activeMtlName );
+		RawMesh.prototype.newInstanceResetOffsets = function () {
+			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );
 
 			// move indices forward
 			newRawObject.globalVertexOffset = this.globalVertexOffset + this.vertices.length / 3;
@@ -953,8 +929,10 @@ THREE.OBJLoader2 = (function () {
 			return newRawObject;
 		};
 
-		RawMesh.prototype.newInstanceFromGroup = function ( groupName ) {
-			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.objectName, groupName, this.activeMtlName );
+		RawMesh.prototype.newInstanceKeepOffsets = function () {
+			var newRawObject = new RawMesh( this.smoothingGroup.splitMaterials, this.useIndices, this.disregardNormals, this.activeMtlName );
+			// keep objectName
+			newRawObject.pushObject( this.objectName );
 
 			// keep current buffers and indices forward
 			newRawObject.vertices = this.vertices;
@@ -1282,6 +1260,10 @@ THREE.OBJLoader2 = (function () {
 			this.groupName = groupName;
 			this.materialName = materialName;
 			this.smoothingGroup = smoothingGroup;
+			this._init();
+		}
+
+		RawMeshSubGroup.prototype._init = function () {
 			this.vertices = [];
 			this.indexMappingsCount = 0;
 			this.indexMappings = [];
@@ -1289,7 +1271,7 @@ THREE.OBJLoader2 = (function () {
 			this.colors = [];
 			this.uvs = [];
 			this.normals = [];
-		}
+		};
 
 		return RawMeshSubGroup;
 	})();

+ 1 - 1
examples/webgl_geometry_extrude_shapes.html

@@ -74,7 +74,7 @@
 					new THREE.Vector3(  60, -100, -60 )
 				] );
 
-				closedSpline.type = 'catmullrom';
+				closedSpline.curveType = 'catmullrom';
 				closedSpline.closed = true;
 
 				var extrudeSettings = {

+ 1 - 1
examples/webgl_geometry_extrude_splines.html

@@ -72,7 +72,7 @@
 			new THREE.Vector3( 0, -40, 40 )
 		] );
 
-		sampleClosedSpline.type = 'catmullrom';
+		sampleClosedSpline.curveType = 'catmullrom';
 		sampleClosedSpline.closed = true;
 
 		// Keep a dictionary of Curve instances

+ 3 - 3
examples/webgl_geometry_spline_editor.html

@@ -258,7 +258,7 @@
 				}
 
 				var curve = new THREE.CatmullRomCurve3( positions );
-				curve.type = 'catmullrom';
+				curve.curveType = 'catmullrom';
 				curve.mesh = new THREE.Line( geometry.clone(), new THREE.LineBasicMaterial( {
 					color: 0xff0000,
 					opacity: 0.35,
@@ -268,7 +268,7 @@
 				splines.uniform = curve;
 
 				curve = new THREE.CatmullRomCurve3( positions );
-				curve.type = 'centripetal';
+				curve.curveType = 'centripetal';
 				curve.mesh = new THREE.Line( geometry.clone(), new THREE.LineBasicMaterial( {
 					color: 0x00ff00,
 					opacity: 0.35,
@@ -278,7 +278,7 @@
 				splines.centripetal = curve;
 
 				curve = new THREE.CatmullRomCurve3( positions );
-				curve.type = 'chordal';
+				curve.curveType = 'chordal';
 				curve.mesh = new THREE.Line( geometry.clone(), new THREE.LineBasicMaterial( {
 					color: 0x0000ff,
 					opacity: 0.35,

+ 2 - 0
src/extras/core/Curve.js

@@ -39,6 +39,8 @@ import { Matrix4 } from '../../math/Matrix4.js';
 
 function Curve() {
 
+	this.type = 'Curve';
+
 	this.arcLengthDivisions = 200;
 
 }

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

@@ -15,8 +15,9 @@ function CurvePath() {
 
 	Curve.call( this );
 
-	this.curves = [];
+	this.type = 'CurvePath';
 
+	this.curves = [];
 	this.autoClose = false; // Automatically closes the path
 
 }

+ 2 - 0
src/extras/core/Font.js

@@ -9,6 +9,8 @@ import { ShapePath } from './ShapePath.js';
 
 function Font( data ) {
 
+	this.type = 'Font';
+
 	this.data = data;
 
 }

+ 3 - 0
src/extras/core/Path.js

@@ -11,6 +11,9 @@ import { CurvePath } from './CurvePath.js';
 function Path( points ) {
 
 	CurvePath.call( this );
+
+	this.type = 'Path';
+
 	this.currentPoint = new Vector2();
 
 	if ( points ) {

+ 2 - 0
src/extras/core/Shape.js

@@ -16,6 +16,8 @@ function Shape() {
 
 	Path.apply( this, arguments );
 
+	this.type = 'Shape';
+
 	this.holes = [];
 
 }

+ 2 - 0
src/extras/core/ShapePath.js

@@ -9,6 +9,8 @@ import { ShapeUtils } from '../ShapeUtils.js';
 
 function ShapePath() {
 
+	this.type = 'ShapePath';
+
 	this.subPaths = [];
 	this.currentPath = null;
 

+ 2 - 0
src/extras/curves/ArcCurve.js

@@ -5,6 +5,8 @@ function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {
 
 	EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );
 
+	this.type = 'ArcCurve';
+
 }
 
 ArcCurve.prototype = Object.create( EllipseCurve.prototype );

+ 6 - 3
src/extras/curves/CatmullRomCurve3.js

@@ -87,9 +87,12 @@ function CatmullRomCurve3( points, closed, tension ) {
 
 	Curve.call( this );
 
+	this.type = 'CatmullRomCurve3';
+
 	this.points = points || [];
 	this.closed = closed || false;
 	this.tension = tension || 0.5;
+	this.curveType = 'centripetal';
 
 }
 
@@ -149,10 +152,10 @@ CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {
 
 	}
 
-	if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) {
+	if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
 
 		// init Centripetal / Chordal Catmull-Rom
-		var pow = this.type === 'chordal' ? 0.5 : 0.25;
+		var pow = this.curveType === 'chordal' ? 0.5 : 0.25;
 		var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
 		var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
 		var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
@@ -166,7 +169,7 @@ CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {
 		py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
 		pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
 
-	} else if ( this.type === 'catmullrom' ) {
+	} else if ( this.curveType === 'catmullrom' ) {
 
 		px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );
 		py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );

+ 2 - 0
src/extras/curves/CubicBezierCurve.js

@@ -7,6 +7,8 @@ function CubicBezierCurve( v0, v1, v2, v3 ) {
 
 	Curve.call( this );
 
+	this.type = 'CubicBezierCurve';
+
 	this.v0 = v0 || new Vector2();
 	this.v1 = v1 || new Vector2();
 	this.v2 = v2 || new Vector2();

+ 2 - 0
src/extras/curves/CubicBezierCurve3.js

@@ -7,6 +7,8 @@ function CubicBezierCurve3( v0, v1, v2, v3 ) {
 
 	Curve.call( this );
 
+	this.type = 'CubicBezierCurve3';
+
 	this.v0 = v0 || new Vector3();
 	this.v1 = v1 || new Vector3();
 	this.v2 = v2 || new Vector3();

+ 2 - 0
src/extras/curves/EllipseCurve.js

@@ -6,6 +6,8 @@ function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockw
 
 	Curve.call( this );
 
+	this.type = 'EllipseCurve';
+
 	this.aX = aX;
 	this.aY = aY;
 

+ 2 - 0
src/extras/curves/LineCurve.js

@@ -6,6 +6,8 @@ function LineCurve( v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'LineCurve';
+
 	this.v1 = v1 || new Vector2();
 	this.v2 = v2 || new Vector2();
 

+ 2 - 0
src/extras/curves/LineCurve3.js

@@ -6,6 +6,8 @@ function LineCurve3( v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'LineCurve3';
+
 	this.v1 = v1 || new Vector3();
 	this.v2 = v2 || new Vector3();
 

+ 2 - 0
src/extras/curves/QuadraticBezierCurve.js

@@ -7,6 +7,8 @@ function QuadraticBezierCurve( v0, v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'QuadraticBezierCurve';
+
 	this.v0 = v0 || new Vector2();
 	this.v1 = v1 || new Vector2();
 	this.v2 = v2 || new Vector2();

+ 2 - 0
src/extras/curves/QuadraticBezierCurve3.js

@@ -7,6 +7,8 @@ function QuadraticBezierCurve3( v0, v1, v2 ) {
 
 	Curve.call( this );
 
+	this.type = 'QuadraticBezierCurve3';
+
 	this.v0 = v0 || new Vector3();
 	this.v1 = v1 || new Vector3();
 	this.v2 = v2 || new Vector3();

+ 2 - 0
src/extras/curves/SplineCurve.js

@@ -7,6 +7,8 @@ function SplineCurve( points /* array of Vector2 */ ) {
 
 	Curve.call( this );
 
+	this.type = 'SplineCurve';
+
 	this.points = points || [];
 
 }

+ 2 - 0
src/loaders/FileLoader.js

@@ -21,6 +21,8 @@ Object.assign( FileLoader.prototype, {
 
 		if ( this.path !== undefined ) url = this.path + url;
 
+		url = this.manager.resolveURL( url );
+
 		var scope = this;
 
 		var cached = Cache.get( url );

+ 2 - 0
src/loaders/ImageLoader.js

@@ -22,6 +22,8 @@ Object.assign( ImageLoader.prototype, {
 
 		if ( this.path !== undefined ) url = this.path + url;
 
+		url = this.manager.resolveURL( url );
+
 		var scope = this;
 
 		var cached = Cache.get( url );

+ 22 - 1
src/loaders/LoadingManager.js

@@ -6,7 +6,10 @@ function LoadingManager( onLoad, onProgress, onError ) {
 
 	var scope = this;
 
-	var isLoading = false, itemsLoaded = 0, itemsTotal = 0;
+	var isLoading = false;
+	var itemsLoaded = 0;
+	var itemsTotal = 0;
+	var urlModifier = undefined;
 
 	this.onStart = undefined;
 	this.onLoad = onLoad;
@@ -65,6 +68,24 @@ function LoadingManager( onLoad, onProgress, onError ) {
 
 	};
 
+	this.resolveURL = function ( url ) {
+
+		if ( urlModifier ) {
+
+			return urlModifier( url );
+
+		}
+
+		return url;
+
+	};
+
+	this.setURLModifier = function ( transform ) {
+
+		urlModifier = transform;
+
+	};
+
 }
 
 var DefaultLoadingManager = new LoadingManager();

+ 7 - 5
src/math/Math.js

@@ -11,35 +11,37 @@ var _Math = {
 	generateUUID: function () {
 
 		// http://www.broofa.com/Tools/Math.uuid.htm
+		// Replaced .join with string concatenation (@takahirox)
 
 		var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
-		var uuid = new Array( 36 );
 		var rnd = 0, r;
 
 		return function generateUUID() {
 
+			var uuid = '';
+
 			for ( var i = 0; i < 36; i ++ ) {
 
 				if ( i === 8 || i === 13 || i === 18 || i === 23 ) {
 
-					uuid[ i ] = '-';
+					uuid += '-';
 
 				} else if ( i === 14 ) {
 
-					uuid[ i ] = '4';
+					uuid += '4';
 
 				} else {
 
 					if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
 					r = rnd & 0xf;
 					rnd = rnd >> 4;
-					uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
+					uuid += chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ];
 
 				}
 
 			}
 
-			return uuid.join( '' );
+			return uuid;
 
 		};
 

+ 1 - 7
src/math/Vector3.js

@@ -539,13 +539,7 @@ Object.assign( Vector3.prototype, {
 
 		}
 
-		var x = this.x, y = this.y, z = this.z;
-
-		this.x = y * v.z - z * v.y;
-		this.y = z * v.x - x * v.z;
-		this.z = x * v.y - y * v.x;
-
-		return this;
+		return this.crossVectors( this, v );
 
 	},
 

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

@@ -1,4 +1,6 @@
-#define saturate(a) clamp( a, 0.0, 1.0 )
+#ifndef saturate
+	#define saturate(a) clamp( a, 0.0, 1.0 )
+#endif
 
 uniform float toneMappingExposure;
 uniform float toneMappingWhitePoint;

+ 1 - 1
test/Three.Unit.js

@@ -98,7 +98,7 @@ export * from '../src/geometries/Geometries.js';
 
 //src/helpers
 export { ArrowHelper } from '../src/helpers/ArrowHelper.js';
-export { AxisHelper } from '../src/helpers/AxisHelper.js';
+export { AxesHelper } from '../src/helpers/AxesHelper.js';
 export { BoxHelper } from '../src/helpers/BoxHelper.js';
 export { Box3Helper } from '../src/helpers/Box3Helper.js';
 export { CameraHelper } from '../src/helpers/CameraHelper.js';

+ 1 - 1
test/unit/src/helpers/AxisHelper.js → test/unit/src/helpers/AxesHelper.js

@@ -3,4 +3,4 @@
  */
 
 //Todo
-console.warn("Todo: Unit tests of AxisHelper")
+console.warn("Todo: Unit tests of AxesHelper")

+ 19 - 3
test/unit/src/loaders/LoadingManager.js

@@ -1,6 +1,22 @@
 /**
- * @author TristanVALCKE / https://github.com/TristanVALCKE
+ * @author Don McCurdy / https://github.com/donmccurdy
  */
 
-//Todo
-console.warn("Todo: Unit tests of LoadingManager")
+QUnit.module( 'LoadingManager' );
+
+QUnit.test( 'setURLModifier', function( assert ) {
+
+	var manager = new THREE.LoadingManager();
+	var suffix = '?transformed=true';
+
+	manager.setURLModifier( function ( url ) {
+
+		return url + suffix;
+
+	} );
+
+	var url = 'https://foo.bar/baz';
+	var resolvedURL = manager.resolveURL( url );
+	assert.equal( resolvedURL, url + suffix, 'URL transform is applied' );
+
+});

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