فهرست منبع

Merge branch 'dev' into ChainableEffect

Takahiro 8 سال پیش
والد
کامیت
fe38d25be8
32فایلهای تغییر یافته به همراه705 افزوده شده و 279 حذف شده
  1. 20 13
      build/three.js
  2. 17 17
      build/three.min.js
  3. 20 13
      build/three.modules.js
  4. 1 0
      examples/files.js
  5. 10 4
      examples/js/animation/MMDPhysics.js
  6. 2 1
      examples/js/effects/OutlineEffect.js
  7. 142 131
      examples/js/loaders/MMDLoader.js
  8. 71 71
      examples/js/renderers/WebGLDeferredRenderer.js
  9. 30 2
      examples/webgl_loader_mmd.html
  10. 239 0
      examples/webgl_materials_variations_toon.html
  11. 19 2
      src/audio/AudioListener.js
  12. 13 13
      src/loaders/FileLoader.js
  13. 7 0
      src/loaders/ImageLoader.js
  14. 2 0
      src/loaders/MaterialLoader.js
  15. 6 0
      src/materials/Material.js
  16. 1 0
      src/materials/Materials.js
  17. 20 0
      src/renderers/WebGLRenderer.js
  18. 6 0
      src/renderers/shaders/ShaderChunk.js
  19. 1 1
      src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl
  20. 1 1
      src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl
  21. 1 1
      src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl
  22. 1 1
      src/renderers/shaders/ShaderChunk/envmap_fragment.glsl
  23. 2 2
      src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl
  24. 1 1
      src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl
  25. 1 1
      src/renderers/shaders/ShaderChunk/envmap_vertex.glsl
  26. 24 0
      src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl
  27. 9 2
      src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl
  28. 1 1
      src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl
  29. 25 0
      src/renderers/shaders/ShaderLib.js
  30. 6 0
      src/renderers/shaders/UniformsLib.js
  31. 2 0
      src/renderers/webgl/WebGLProgram.js
  32. 4 1
      src/renderers/webgl/WebGLPrograms.js

+ 20 - 13
build/three.js

@@ -28934,15 +28934,13 @@
 			// Safari can not handle Data URIs through XMLHttpRequest so process manually
 			if ( dataUriRegexResult ) {
 
-				var mimeType = dataUriRegexResult[1];
-				var isBase64 = !!dataUriRegexResult[2];
-				var data = dataUriRegexResult[3];
+				var mimeType = dataUriRegexResult[ 1 ];
+				var isBase64 = !! dataUriRegexResult[ 2 ];
+				var data = dataUriRegexResult[ 3 ];
 
-				data = window.decodeURIComponent(data);
+				data = window.decodeURIComponent( data );
 
-				if( isBase64 ) {
-					data = window.atob(data);
-				}
+				if ( isBase64 ) data = window.atob( data );
 
 				try {
 
@@ -28955,16 +28953,18 @@
 						case 'blob':
 
 						 	response = new ArrayBuffer( data.length );
+
 							var view = new Uint8Array( response );
+
 							for ( var i = 0; i < data.length; i ++ ) {
 
-									view[ i ] = data.charCodeAt( i );
+								view[ i ] = data.charCodeAt( i );
 
 							}
 
 							if ( responseType === 'blob' ) {
 
-								response = new Blob( [ response ], { "type" : mimeType } );
+								response = new Blob( [ response ], { type: mimeType } );
 
 							}
 
@@ -28992,24 +28992,24 @@
 					}
 
 					// Wait for next browser tick
-					window.setTimeout( function() {
+					window.setTimeout( function () {
 
 						if ( onLoad ) onLoad( response );
 
 						scope.manager.itemEnd( url );
 
-					}, 0);
+					}, 0 );
 
 				} catch ( error ) {
 
 					// Wait for next browser tick
-					window.setTimeout( function() {
+					window.setTimeout( function () {
 
 						if ( onError ) onError( error );
 
 						scope.manager.itemError( url );
 
-					}, 0);
+					}, 0 );
 
 				}
 
@@ -29371,6 +29371,13 @@
 
 				image.src = url;
 
+			} else if ( this.crossOrigin !== undefined ) {
+
+				// crossOrigin doesn't work with URL.createObjectURL()?
+
+				image.crossOrigin = this.crossOrigin;
+				image.src = url;
+
 			} else {
 
 				var loader = new FileLoader();

+ 17 - 17
build/three.min.js

@@ -612,23 +612,23 @@ e.manager.itemError(a)},0)}}else{var n=new XMLHttpRequest;n.open("GET",a,!0);n.a
 b,c,d){function e(e){k.load(a[e],function(a){a=f._parser(a,!0);g[e]={width:a.width,height:a.height,format:a.format,mipmaps:a.mipmaps};m+=1;6===m&&(1===a.mipmapCount&&(h.minFilter=1006),h.format=a.format,h.needsUpdate=!0,b&&b(h))},c,d)}var f=this,g=[],h=new Lb;h.image=g;var k=new Na(this.manager);k.setPath(this.path);k.setResponseType("arraybuffer");if(Array.isArray(a))for(var m=0,l=0,p=a.length;l<p;++l)e(l);else k.load(a,function(a){a=f._parser(a,!0);if(a.isCubemap)for(var c=a.mipmaps.length/a.mipmapCount,
 d=0;d<c;d++){g[d]={mipmaps:[]};for(var e=0;e<a.mipmapCount;e++)g[d].mipmaps.push(a.mipmaps[d*a.mipmapCount+e]),g[d].format=a.format,g[d].width=a.width,g[d].height=a.height}else h.image.width=a.width,h.image.height=a.height,h.mipmaps=a.mipmaps;1===a.mipmapCount&&(h.minFilter=1006);h.format=a.format;h.needsUpdate=!0;b&&b(h)},c,d);return h},setPath:function(a){this.path=a;return this}});Object.assign(Nd.prototype,{load:function(a,b,c,d){var e=this,f=new fb,g=new Na(this.manager);g.setResponseType("arraybuffer");
 g.load(a,function(a){if(a=e._parser(a))void 0!==a.image?f.image=a.image:void 0!==a.data&&(f.image.width=a.width,f.image.height=a.height,f.image.data=a.data),f.wrapS=void 0!==a.wrapS?a.wrapS:1001,f.wrapT=void 0!==a.wrapT?a.wrapT:1001,f.magFilter=void 0!==a.magFilter?a.magFilter:1006,f.minFilter=void 0!==a.minFilter?a.minFilter:1008,f.anisotropy=void 0!==a.anisotropy?a.anisotropy:1,void 0!==a.format&&(f.format=a.format),void 0!==a.type&&(f.type=a.type),void 0!==a.mipmaps&&(f.mipmaps=a.mipmaps),1===
-a.mipmapCount&&(f.minFilter=1006),f.needsUpdate=!0,b&&b(f,a)},c,d);return f}});Object.assign(Tc.prototype,{load:function(a,b,c,d){var e=this,f=document.createElementNS("http://www.w3.org/1999/xhtml","img");f.onload=function(){f.onload=null;URL.revokeObjectURL(f.src);b&&b(f);e.manager.itemEnd(a)};f.onerror=d;if(0===a.indexOf("data:"))f.src=a;else{var g=new Na;g.setPath(this.path);g.setResponseType("blob");g.setWithCredentials(this.withCredentials);g.load(a,function(a){f.src=URL.createObjectURL(a)},
-c,d)}e.manager.itemStart(a);return f},setCrossOrigin:function(a){this.crossOrigin=a;return this},setWithCredentials:function(a){this.withCredentials=a;return this},setPath:function(a){this.path=a;return this}});Object.assign(Od.prototype,{load:function(a,b,c,d){function e(c){g.load(a[c],function(a){f.images[c]=a;h++;6===h&&(f.needsUpdate=!0,b&&b(f))},void 0,d)}var f=new Za,g=new Tc(this.manager);g.setCrossOrigin(this.crossOrigin);g.setPath(this.path);var h=0;for(c=0;c<a.length;++c)e(c);return f},
-setCrossOrigin:function(a){this.crossOrigin=a;return this},setPath:function(a){this.path=a;return this}});Object.assign(nd.prototype,{load:function(a,b,c,d){var e=new ea,f=new Tc(this.manager);f.setCrossOrigin(this.crossOrigin);f.setWithCredentials(this.withCredentials);f.setPath(this.path);f.load(a,function(c){var d=0<a.search(/\.(jpg|jpeg)$/)||0===a.search(/^data\:image\/jpeg/);e.format=d?1022:1023;e.image=c;e.needsUpdate=!0;void 0!==b&&b(e)},c,d);return e},setCrossOrigin:function(a){this.crossOrigin=
-a;return this},setWithCredentials:function(a){this.withCredentials=a;return this},setPath:function(a){this.path=a;return this}});oa.prototype=Object.assign(Object.create(E.prototype),{constructor:oa,isLight:!0,copy:function(a){E.prototype.copy.call(this,a);this.color.copy(a.color);this.intensity=a.intensity;return this},toJSON:function(a){a=E.prototype.toJSON.call(this,a);a.object.color=this.color.getHex();a.object.intensity=this.intensity;void 0!==this.groundColor&&(a.object.groundColor=this.groundColor.getHex());
-void 0!==this.distance&&(a.object.distance=this.distance);void 0!==this.angle&&(a.object.angle=this.angle);void 0!==this.decay&&(a.object.decay=this.decay);void 0!==this.penumbra&&(a.object.penumbra=this.penumbra);void 0!==this.shadow&&(a.object.shadow=this.shadow.toJSON());return a}});od.prototype=Object.assign(Object.create(oa.prototype),{constructor:od,isHemisphereLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.groundColor.copy(a.groundColor);return this}});Object.assign(ub.prototype,
-{copy:function(a){this.camera=a.camera.clone();this.bias=a.bias;this.radius=a.radius;this.mapSize.copy(a.mapSize);return this},clone:function(){return(new this.constructor).copy(this)},toJSON:function(){var a={};0!==this.bias&&(a.bias=this.bias);1!==this.radius&&(a.radius=this.radius);if(512!==this.mapSize.x||512!==this.mapSize.y)a.mapSize=this.mapSize.toArray();a.camera=this.camera.toJSON(!1).object;delete a.camera.matrix;return a}});pd.prototype=Object.assign(Object.create(ub.prototype),{constructor:pd,
-isSpotLightShadow:!0,update:function(a){var b=2*S.RAD2DEG*a.angle,c=this.mapSize.width/this.mapSize.height;a=a.distance||500;var d=this.camera;if(b!==d.fov||c!==d.aspect||a!==d.far)d.fov=b,d.aspect=c,d.far=a,d.updateProjectionMatrix()}});qd.prototype=Object.assign(Object.create(oa.prototype),{constructor:qd,isSpotLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.distance=a.distance;this.angle=a.angle;this.penumbra=a.penumbra;this.decay=a.decay;this.target=a.target.clone();this.shadow=
-a.shadow.clone();return this}});rd.prototype=Object.assign(Object.create(oa.prototype),{constructor:rd,isPointLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.distance=a.distance;this.decay=a.decay;this.shadow=a.shadow.clone();return this}});sd.prototype=Object.assign(Object.create(ub.prototype),{constructor:sd});td.prototype=Object.assign(Object.create(oa.prototype),{constructor:td,isDirectionalLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.target=a.target.clone();this.shadow=
-a.shadow.clone();return this}});ud.prototype=Object.assign(Object.create(oa.prototype),{constructor:ud,isAmbientLight:!0});var xa={arraySlice:function(a,b,c){return xa.isTypedArray(a)?new a.constructor(a.subarray(b,c)):a.slice(b,c)},convertArray:function(a,b,c){return!a||!c&&a.constructor===b?a:"number"===typeof b.BYTES_PER_ELEMENT?new b(a):Array.prototype.slice.call(a)},isTypedArray:function(a){return ArrayBuffer.isView(a)&&!(a instanceof DataView)},getKeyframeOrder:function(a){for(var b=a.length,
-c=Array(b),d=0;d!==b;++d)c[d]=d;c.sort(function(b,c){return a[b]-a[c]});return c},sortedArray:function(a,b,c){for(var d=a.length,e=new a.constructor(d),f=0,g=0;g!==d;++f)for(var h=c[f]*b,k=0;k!==b;++k)e[g++]=a[h+k];return e},flattenJSON:function(a,b,c,d){for(var e=1,f=a[0];void 0!==f&&void 0===f[d];)f=a[e++];if(void 0!==f){var g=f[d];if(void 0!==g)if(Array.isArray(g)){do g=f[d],void 0!==g&&(b.push(f.time),c.push.apply(c,g)),f=a[e++];while(void 0!==f)}else if(void 0!==g.toArray){do g=f[d],void 0!==
-g&&(b.push(f.time),g.toArray(c,c.length)),f=a[e++];while(void 0!==f)}else{do g=f[d],void 0!==g&&(b.push(f.time),c.push(g)),f=a[e++];while(void 0!==f)}}}};ka.prototype={constructor:ka,evaluate:function(a){var b=this.parameterPositions,c=this._cachedIndex,d=b[c],e=b[c-1];a:{b:{c:{d:if(!(a<d)){for(var f=c+2;;){if(void 0===d){if(a<e)break d;this._cachedIndex=c=b.length;return this.afterEnd_(c-1,a,e)}if(c===f)break;e=d;d=b[++c];if(a<d)break b}d=b.length;break c}if(a>=e)break a;else{f=b[1];a<f&&(c=2,e=
-f);for(f=c-2;;){if(void 0===e)return this._cachedIndex=0,this.beforeStart_(0,a,d);if(c===f)break;d=e;e=b[--c-1];if(a>=e)break b}d=c;c=0}}for(;c<d;)e=c+d>>>1,a<b[e]?d=e:c=e+1;d=b[c];e=b[c-1];if(void 0===e)return this._cachedIndex=0,this.beforeStart_(0,a,d);if(void 0===d)return this._cachedIndex=c=b.length,this.afterEnd_(c-1,e,a)}this._cachedIndex=c;this.intervalChanged_(c,e,d)}return this.interpolate_(c,e,a,d)},settings:null,DefaultSettings_:{},getSettings_:function(){return this.settings||this.DefaultSettings_},
-copySampleValue_:function(a){var b=this.resultBuffer,c=this.sampleValues,d=this.valueSize;a*=d;for(var e=0;e!==d;++e)b[e]=c[a+e];return b},interpolate_:function(a,b,c,d){throw Error("call to abstract method");},intervalChanged_:function(a,b,c){}};Object.assign(ka.prototype,{beforeStart_:ka.prototype.copySampleValue_,afterEnd_:ka.prototype.copySampleValue_});vd.prototype=Object.assign(Object.create(ka.prototype),{constructor:vd,DefaultSettings_:{endingStart:2400,endingEnd:2400},intervalChanged_:function(a,
-b,c){var d=this.parameterPositions,e=a-2,f=a+1,g=d[e],h=d[f];if(void 0===g)switch(this.getSettings_().endingStart){case 2401:e=a;g=2*b-c;break;case 2402:e=d.length-2;g=b+d[e]-d[e+1];break;default:e=a,g=c}if(void 0===h)switch(this.getSettings_().endingEnd){case 2401:f=a;h=2*c-b;break;case 2402:f=1;h=c+d[1]-d[0];break;default:f=a-1,h=b}a=.5*(c-b);d=this.valueSize;this._weightPrev=a/(b-g);this._weightNext=a/(h-c);this._offsetPrev=e*d;this._offsetNext=f*d},interpolate_:function(a,b,c,d){var e=this.resultBuffer,
-f=this.sampleValues,g=this.valueSize;a*=g;var h=a-g,k=this._offsetPrev,m=this._offsetNext,l=this._weightPrev,p=this._weightNext,n=(c-b)/(d-b);c=n*n;d=c*n;b=-l*d+2*l*c-l*n;l=(1+l)*d+(-1.5-2*l)*c+(-.5+l)*n+1;n=(-1-p)*d+(1.5+p)*c+.5*n;p=p*d-p*c;for(c=0;c!==g;++c)e[c]=b*f[k+c]+l*f[h+c]+n*f[a+c]+p*f[m+c];return e}});Uc.prototype=Object.assign(Object.create(ka.prototype),{constructor:Uc,interpolate_:function(a,b,c,d){var e=this.resultBuffer,f=this.sampleValues,g=this.valueSize;a*=g;var h=a-g;b=(c-b)/(d-
-b);c=1-b;for(d=0;d!==g;++d)e[d]=f[h+d]*c+f[a+d]*b;return e}});wd.prototype=Object.assign(Object.create(ka.prototype),{constructor:wd,interpolate_:function(a,b,c,d){return this.copySampleValue_(a-1)}});var Ya;Ya={TimeBufferType:Float32Array,ValueBufferType:Float32Array,DefaultInterpolation:2301,InterpolantFactoryMethodDiscrete:function(a){return new wd(this.times,this.values,this.getValueSize(),a)},InterpolantFactoryMethodLinear:function(a){return new Uc(this.times,this.values,this.getValueSize(),
-a)},InterpolantFactoryMethodSmooth:function(a){return new vd(this.times,this.values,this.getValueSize(),a)},setInterpolation:function(a){var b;switch(a){case 2300:b=this.InterpolantFactoryMethodDiscrete;break;case 2301:b=this.InterpolantFactoryMethodLinear;break;case 2302:b=this.InterpolantFactoryMethodSmooth}if(void 0===b){b="unsupported interpolation for "+this.ValueTypeName+" keyframe track named "+this.name;if(void 0===this.createInterpolant)if(a!==this.DefaultInterpolation)this.setInterpolation(this.DefaultInterpolation);
+a.mipmapCount&&(f.minFilter=1006),f.needsUpdate=!0,b&&b(f,a)},c,d);return f}});Object.assign(Tc.prototype,{load:function(a,b,c,d){var e=this,f=document.createElementNS("http://www.w3.org/1999/xhtml","img");f.onload=function(){f.onload=null;URL.revokeObjectURL(f.src);b&&b(f);e.manager.itemEnd(a)};f.onerror=d;if(0===a.indexOf("data:"))f.src=a;else if(void 0!==this.crossOrigin)f.crossOrigin=this.crossOrigin,f.src=a;else{var g=new Na;g.setPath(this.path);g.setResponseType("blob");g.setWithCredentials(this.withCredentials);
+g.load(a,function(a){f.src=URL.createObjectURL(a)},c,d)}e.manager.itemStart(a);return f},setCrossOrigin:function(a){this.crossOrigin=a;return this},setWithCredentials:function(a){this.withCredentials=a;return this},setPath:function(a){this.path=a;return this}});Object.assign(Od.prototype,{load:function(a,b,c,d){function e(c){g.load(a[c],function(a){f.images[c]=a;h++;6===h&&(f.needsUpdate=!0,b&&b(f))},void 0,d)}var f=new Za,g=new Tc(this.manager);g.setCrossOrigin(this.crossOrigin);g.setPath(this.path);
+var h=0;for(c=0;c<a.length;++c)e(c);return f},setCrossOrigin:function(a){this.crossOrigin=a;return this},setPath:function(a){this.path=a;return this}});Object.assign(nd.prototype,{load:function(a,b,c,d){var e=new ea,f=new Tc(this.manager);f.setCrossOrigin(this.crossOrigin);f.setWithCredentials(this.withCredentials);f.setPath(this.path);f.load(a,function(c){var d=0<a.search(/\.(jpg|jpeg)$/)||0===a.search(/^data\:image\/jpeg/);e.format=d?1022:1023;e.image=c;e.needsUpdate=!0;void 0!==b&&b(e)},c,d);return e},
+setCrossOrigin:function(a){this.crossOrigin=a;return this},setWithCredentials:function(a){this.withCredentials=a;return this},setPath:function(a){this.path=a;return this}});oa.prototype=Object.assign(Object.create(E.prototype),{constructor:oa,isLight:!0,copy:function(a){E.prototype.copy.call(this,a);this.color.copy(a.color);this.intensity=a.intensity;return this},toJSON:function(a){a=E.prototype.toJSON.call(this,a);a.object.color=this.color.getHex();a.object.intensity=this.intensity;void 0!==this.groundColor&&
+(a.object.groundColor=this.groundColor.getHex());void 0!==this.distance&&(a.object.distance=this.distance);void 0!==this.angle&&(a.object.angle=this.angle);void 0!==this.decay&&(a.object.decay=this.decay);void 0!==this.penumbra&&(a.object.penumbra=this.penumbra);void 0!==this.shadow&&(a.object.shadow=this.shadow.toJSON());return a}});od.prototype=Object.assign(Object.create(oa.prototype),{constructor:od,isHemisphereLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.groundColor.copy(a.groundColor);
+return this}});Object.assign(ub.prototype,{copy:function(a){this.camera=a.camera.clone();this.bias=a.bias;this.radius=a.radius;this.mapSize.copy(a.mapSize);return this},clone:function(){return(new this.constructor).copy(this)},toJSON:function(){var a={};0!==this.bias&&(a.bias=this.bias);1!==this.radius&&(a.radius=this.radius);if(512!==this.mapSize.x||512!==this.mapSize.y)a.mapSize=this.mapSize.toArray();a.camera=this.camera.toJSON(!1).object;delete a.camera.matrix;return a}});pd.prototype=Object.assign(Object.create(ub.prototype),
+{constructor:pd,isSpotLightShadow:!0,update:function(a){var b=2*S.RAD2DEG*a.angle,c=this.mapSize.width/this.mapSize.height;a=a.distance||500;var d=this.camera;if(b!==d.fov||c!==d.aspect||a!==d.far)d.fov=b,d.aspect=c,d.far=a,d.updateProjectionMatrix()}});qd.prototype=Object.assign(Object.create(oa.prototype),{constructor:qd,isSpotLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.distance=a.distance;this.angle=a.angle;this.penumbra=a.penumbra;this.decay=a.decay;this.target=a.target.clone();
+this.shadow=a.shadow.clone();return this}});rd.prototype=Object.assign(Object.create(oa.prototype),{constructor:rd,isPointLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.distance=a.distance;this.decay=a.decay;this.shadow=a.shadow.clone();return this}});sd.prototype=Object.assign(Object.create(ub.prototype),{constructor:sd});td.prototype=Object.assign(Object.create(oa.prototype),{constructor:td,isDirectionalLight:!0,copy:function(a){oa.prototype.copy.call(this,a);this.target=a.target.clone();
+this.shadow=a.shadow.clone();return this}});ud.prototype=Object.assign(Object.create(oa.prototype),{constructor:ud,isAmbientLight:!0});var xa={arraySlice:function(a,b,c){return xa.isTypedArray(a)?new a.constructor(a.subarray(b,c)):a.slice(b,c)},convertArray:function(a,b,c){return!a||!c&&a.constructor===b?a:"number"===typeof b.BYTES_PER_ELEMENT?new b(a):Array.prototype.slice.call(a)},isTypedArray:function(a){return ArrayBuffer.isView(a)&&!(a instanceof DataView)},getKeyframeOrder:function(a){for(var b=
+a.length,c=Array(b),d=0;d!==b;++d)c[d]=d;c.sort(function(b,c){return a[b]-a[c]});return c},sortedArray:function(a,b,c){for(var d=a.length,e=new a.constructor(d),f=0,g=0;g!==d;++f)for(var h=c[f]*b,k=0;k!==b;++k)e[g++]=a[h+k];return e},flattenJSON:function(a,b,c,d){for(var e=1,f=a[0];void 0!==f&&void 0===f[d];)f=a[e++];if(void 0!==f){var g=f[d];if(void 0!==g)if(Array.isArray(g)){do g=f[d],void 0!==g&&(b.push(f.time),c.push.apply(c,g)),f=a[e++];while(void 0!==f)}else if(void 0!==g.toArray){do g=f[d],
+void 0!==g&&(b.push(f.time),g.toArray(c,c.length)),f=a[e++];while(void 0!==f)}else{do g=f[d],void 0!==g&&(b.push(f.time),c.push(g)),f=a[e++];while(void 0!==f)}}}};ka.prototype={constructor:ka,evaluate:function(a){var b=this.parameterPositions,c=this._cachedIndex,d=b[c],e=b[c-1];a:{b:{c:{d:if(!(a<d)){for(var f=c+2;;){if(void 0===d){if(a<e)break d;this._cachedIndex=c=b.length;return this.afterEnd_(c-1,a,e)}if(c===f)break;e=d;d=b[++c];if(a<d)break b}d=b.length;break c}if(a>=e)break a;else{f=b[1];a<f&&
+(c=2,e=f);for(f=c-2;;){if(void 0===e)return this._cachedIndex=0,this.beforeStart_(0,a,d);if(c===f)break;d=e;e=b[--c-1];if(a>=e)break b}d=c;c=0}}for(;c<d;)e=c+d>>>1,a<b[e]?d=e:c=e+1;d=b[c];e=b[c-1];if(void 0===e)return this._cachedIndex=0,this.beforeStart_(0,a,d);if(void 0===d)return this._cachedIndex=c=b.length,this.afterEnd_(c-1,e,a)}this._cachedIndex=c;this.intervalChanged_(c,e,d)}return this.interpolate_(c,e,a,d)},settings:null,DefaultSettings_:{},getSettings_:function(){return this.settings||
+this.DefaultSettings_},copySampleValue_:function(a){var b=this.resultBuffer,c=this.sampleValues,d=this.valueSize;a*=d;for(var e=0;e!==d;++e)b[e]=c[a+e];return b},interpolate_:function(a,b,c,d){throw Error("call to abstract method");},intervalChanged_:function(a,b,c){}};Object.assign(ka.prototype,{beforeStart_:ka.prototype.copySampleValue_,afterEnd_:ka.prototype.copySampleValue_});vd.prototype=Object.assign(Object.create(ka.prototype),{constructor:vd,DefaultSettings_:{endingStart:2400,endingEnd:2400},
+intervalChanged_:function(a,b,c){var d=this.parameterPositions,e=a-2,f=a+1,g=d[e],h=d[f];if(void 0===g)switch(this.getSettings_().endingStart){case 2401:e=a;g=2*b-c;break;case 2402:e=d.length-2;g=b+d[e]-d[e+1];break;default:e=a,g=c}if(void 0===h)switch(this.getSettings_().endingEnd){case 2401:f=a;h=2*c-b;break;case 2402:f=1;h=c+d[1]-d[0];break;default:f=a-1,h=b}a=.5*(c-b);d=this.valueSize;this._weightPrev=a/(b-g);this._weightNext=a/(h-c);this._offsetPrev=e*d;this._offsetNext=f*d},interpolate_:function(a,
+b,c,d){var e=this.resultBuffer,f=this.sampleValues,g=this.valueSize;a*=g;var h=a-g,k=this._offsetPrev,m=this._offsetNext,l=this._weightPrev,p=this._weightNext,n=(c-b)/(d-b);c=n*n;d=c*n;b=-l*d+2*l*c-l*n;l=(1+l)*d+(-1.5-2*l)*c+(-.5+l)*n+1;n=(-1-p)*d+(1.5+p)*c+.5*n;p=p*d-p*c;for(c=0;c!==g;++c)e[c]=b*f[k+c]+l*f[h+c]+n*f[a+c]+p*f[m+c];return e}});Uc.prototype=Object.assign(Object.create(ka.prototype),{constructor:Uc,interpolate_:function(a,b,c,d){var e=this.resultBuffer,f=this.sampleValues,g=this.valueSize;
+a*=g;var h=a-g;b=(c-b)/(d-b);c=1-b;for(d=0;d!==g;++d)e[d]=f[h+d]*c+f[a+d]*b;return e}});wd.prototype=Object.assign(Object.create(ka.prototype),{constructor:wd,interpolate_:function(a,b,c,d){return this.copySampleValue_(a-1)}});var Ya;Ya={TimeBufferType:Float32Array,ValueBufferType:Float32Array,DefaultInterpolation:2301,InterpolantFactoryMethodDiscrete:function(a){return new wd(this.times,this.values,this.getValueSize(),a)},InterpolantFactoryMethodLinear:function(a){return new Uc(this.times,this.values,
+this.getValueSize(),a)},InterpolantFactoryMethodSmooth:function(a){return new vd(this.times,this.values,this.getValueSize(),a)},setInterpolation:function(a){var b;switch(a){case 2300:b=this.InterpolantFactoryMethodDiscrete;break;case 2301:b=this.InterpolantFactoryMethodLinear;break;case 2302:b=this.InterpolantFactoryMethodSmooth}if(void 0===b){b="unsupported interpolation for "+this.ValueTypeName+" keyframe track named "+this.name;if(void 0===this.createInterpolant)if(a!==this.DefaultInterpolation)this.setInterpolation(this.DefaultInterpolation);
 else throw Error(b);console.warn(b)}else this.createInterpolant=b},getInterpolation:function(){switch(this.createInterpolant){case this.InterpolantFactoryMethodDiscrete:return 2300;case this.InterpolantFactoryMethodLinear:return 2301;case this.InterpolantFactoryMethodSmooth:return 2302}},getValueSize:function(){return this.values.length/this.times.length},shift:function(a){if(0!==a)for(var b=this.times,c=0,d=b.length;c!==d;++c)b[c]+=a;return this},scale:function(a){if(1!==a)for(var b=this.times,c=
 0,d=b.length;c!==d;++c)b[c]*=a;return this},trim:function(a,b){for(var c=this.times,d=c.length,e=0,f=d-1;e!==d&&c[e]<a;)++e;for(;-1!==f&&c[f]>b;)--f;++f;if(0!==e||f!==d)e>=f&&(f=Math.max(f,1),e=f-1),d=this.getValueSize(),this.times=xa.arraySlice(c,e,f),this.values=xa.arraySlice(this.values,e*d,f*d);return this},validate:function(){var a=!0,b=this.getValueSize();0!==b-Math.floor(b)&&(console.error("invalid value size in track",this),a=!1);var c=this.times,b=this.values,d=c.length;0===d&&(console.error("track is empty",
 this),a=!1);for(var e=null,f=0;f!==d;f++){var g=c[f];if("number"===typeof g&&isNaN(g)){console.error("time is not a valid number",this,f,g);a=!1;break}if(null!==e&&e>g){console.error("out of order keys",this,f,g,e);a=!1;break}e=g}if(void 0!==b&&xa.isTypedArray(b))for(f=0,c=b.length;f!==c;++f)if(d=b[f],isNaN(d)){console.error("value is not a valid number",this,f,d);a=!1;break}return a},optimize:function(){for(var a=this.times,b=this.values,c=this.getValueSize(),d=2302===this.getInterpolation(),e=1,

+ 20 - 13
build/three.modules.js

@@ -28928,15 +28928,13 @@ Object.assign( FileLoader.prototype, {
 		// Safari can not handle Data URIs through XMLHttpRequest so process manually
 		if ( dataUriRegexResult ) {
 
-			var mimeType = dataUriRegexResult[1];
-			var isBase64 = !!dataUriRegexResult[2];
-			var data = dataUriRegexResult[3];
+			var mimeType = dataUriRegexResult[ 1 ];
+			var isBase64 = !! dataUriRegexResult[ 2 ];
+			var data = dataUriRegexResult[ 3 ];
 
-			data = window.decodeURIComponent(data);
+			data = window.decodeURIComponent( data );
 
-			if( isBase64 ) {
-				data = window.atob(data);
-			}
+			if ( isBase64 ) data = window.atob( data );
 
 			try {
 
@@ -28949,16 +28947,18 @@ Object.assign( FileLoader.prototype, {
 					case 'blob':
 
 					 	response = new ArrayBuffer( data.length );
+
 						var view = new Uint8Array( response );
+
 						for ( var i = 0; i < data.length; i ++ ) {
 
-								view[ i ] = data.charCodeAt( i );
+							view[ i ] = data.charCodeAt( i );
 
 						}
 
 						if ( responseType === 'blob' ) {
 
-							response = new Blob( [ response ], { "type" : mimeType } );
+							response = new Blob( [ response ], { type: mimeType } );
 
 						}
 
@@ -28986,24 +28986,24 @@ Object.assign( FileLoader.prototype, {
 				}
 
 				// Wait for next browser tick
-				window.setTimeout( function() {
+				window.setTimeout( function () {
 
 					if ( onLoad ) onLoad( response );
 
 					scope.manager.itemEnd( url );
 
-				}, 0);
+				}, 0 );
 
 			} catch ( error ) {
 
 				// Wait for next browser tick
-				window.setTimeout( function() {
+				window.setTimeout( function () {
 
 					if ( onError ) onError( error );
 
 					scope.manager.itemError( url );
 
-				}, 0);
+				}, 0 );
 
 			}
 
@@ -29365,6 +29365,13 @@ Object.assign( ImageLoader.prototype, {
 
 			image.src = url;
 
+		} else if ( this.crossOrigin !== undefined ) {
+
+			// crossOrigin doesn't work with URL.createObjectURL()?
+
+			image.crossOrigin = this.crossOrigin;
+			image.src = url;
+
 		} else {
 
 			var loader = new FileLoader();

+ 1 - 0
examples/files.js

@@ -155,6 +155,7 @@ var files = {
 		"webgl_materials_variations_phong",
 		"webgl_materials_variations_standard",
 		"webgl_materials_variations_physical",
+		"webgl_materials_variations_toon",
 		"webgl_materials_video",
 		"webgl_materials_wireframe",
 		"webgl_math_spherical_distribution",

+ 10 - 4
examples/js/animation/MMDPhysics.js

@@ -35,7 +35,7 @@ THREE.MMDPhysics = function ( mesh, params ) {
 	this.unitStep = ( params.unitStep !== undefined ) ? params.unitStep : 1 / 65;
 	this.maxStepNum = ( params.maxStepNum !== undefined ) ? params.maxStepNum : 3;
 
-	this.world = null;
+	this.world = params.world !== undefined ? params.world : null;
 	this.bodies = [];
 	this.constraints = [];
 
@@ -67,7 +67,7 @@ THREE.MMDPhysics.prototype = {
 
 		mesh.updateMatrixWorld( true );
 
-		this.initWorld();
+		if ( this.world === null ) this.initWorld();
 		this.initRigidBodies();
 		this.initConstraints();
 
@@ -131,6 +131,14 @@ THREE.MMDPhysics.prototype = {
 
 	update: function ( delta ) {
 
+		this.updateRigidBodies();
+		this.stepSimulation( delta );
+		this.updateBones();
+
+	},
+
+	stepSimulation: function ( delta ) {
+
 		var unitStep = this.unitStep;
 		var stepTime = delta;
 		var maxStepNum = ( ( delta / unitStep ) | 0 ) + 1;
@@ -148,9 +156,7 @@ THREE.MMDPhysics.prototype = {
 
 		}
 
-		this.updateRigidBodies();
 		this.world.stepSimulation( stepTime, maxStepNum, unitStep );
-		this.updateBones();
 
 	},
 

+ 2 - 1
examples/js/effects/OutlineEffect.js

@@ -62,6 +62,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 		MeshBasicMaterial: 'basic',
 		MeshLambertMaterial: 'lambert',
 		MeshPhongMaterial: 'phong',
+		MeshToonMaterial: 'toon',
 		MeshStandardMaterial: 'physical',
 		MeshPhysicalMaterial: 'physical'
 	};
@@ -91,7 +92,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	var vertexShaderChunk2 = [
 
-		"#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( PHYSICAL )",
+		"#if ! defined( LAMBERT ) && ! defined( PHONG ) && ! defined( TOON ) && ! defined( PHYSICAL )",
 
 		"	#ifndef USE_ENVMAP",
 		"		vec3 objectNormal = normalize( normal );",

+ 142 - 131
examples/js/loaders/MMDLoader.js

@@ -42,6 +42,7 @@ THREE.MMDLoader = function ( manager ) {
 	THREE.Loader.call( this );
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 	this.parser = new MMDParser.Parser();
+	this.textureCrossOrigin = null;
 
 };
 
@@ -68,6 +69,17 @@ THREE.MMDLoader.prototype.defaultToonTextures = [
 	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII='
 ];
 
+/*
+ * Set 'anonymous' for the the texture image file in other domain
+ * even if server responds with "Access-Control-Allow-Origin: *"
+ * because some image operation fails in MMDLoader.
+ */
+THREE.MMDLoader.prototype.setTextureCrossOrigin = function ( value ) {
+
+	this.textureCrossOrigin = value;
+
+};
+
 THREE.MMDLoader.prototype.load = function ( modelUrl, vmdUrls, callback, onProgress, onError ) {
 
 	var scope = this;
@@ -94,15 +106,15 @@ THREE.MMDLoader.prototype.loadModel = function ( url, callback, onProgress, onEr
 
 	this.loadFileAsBuffer( url, function ( buffer ) {
 
-		callback( scope.createModel( buffer, modelExtension, texturePath ) );
+		callback( scope.createModel( buffer, modelExtension, texturePath, onProgress, onError ) );
 
 	}, onProgress, onError );
 
 };
 
-THREE.MMDLoader.prototype.createModel = function ( buffer, modelExtension, texturePath ) {
+THREE.MMDLoader.prototype.createModel = function ( buffer, modelExtension, texturePath, onProgress, onError ) {
 
-	return this.createMesh( this.parseModel( buffer, modelExtension ), texturePath );
+	return this.createMesh( this.parseModel( buffer, modelExtension ), texturePath, onProgress, onError );
 
 };
 
@@ -123,6 +135,7 @@ THREE.MMDLoader.prototype.loadVmds = function ( urls, callback, onProgress, onEr
 	var scope = this;
 
 	var vmds = [];
+	urls = urls.slice();
 
 	function run () {
 
@@ -857,12 +870,16 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 	var initMaterials = function () {
 
-		var textures = [];
+		var textures = {};
 		var textureLoader = new THREE.TextureLoader( scope.manager );
 		var tgaLoader = new THREE.TGALoader( scope.manager );
+		var canvas = document.createElement( 'canvas' );
+		var context = canvas.getContext( '2d' );
 		var offset = 0;
 		var materialParams = [];
 
+		if ( scope.textureCrossOrigin !== null ) textureLoader.setCrossOrigin( scope.textureCrossOrigin );
+
 		function loadTexture ( filePath, params ) {
 
 			if ( params === undefined ) {
@@ -892,6 +909,8 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 			}
 
+			if ( textures[ fullPath ] !== undefined ) return fullPath;
+
 			var loader = THREE.Loader.Handlers.get( fullPath );
 
 			if ( loader === null ) {
@@ -902,6 +921,28 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 			var texture = loader.load( fullPath, function ( t ) {
 
+				// MMD toon texture is Axis-Y oriented
+				// but Three.js gradient map is Axis-X oriented.
+				// So here replaces the toon texture image with the rotated one.
+				if ( params.isToonTexture === true ) {
+
+					var image = t.image;
+					var width = image.width;
+					var height = image.height;
+
+					canvas.width = width;
+					canvas.height = height;
+
+					context.clearRect( 0, 0, width, height );
+					context.translate( width / 2.0, height / 2.0 );
+					context.rotate( 0.5 * Math.PI );  // 90.0 * Math.PI / 180.0
+					context.translate( -width / 2.0, -height / 2.0 );
+					context.drawImage( image, 0, 0 );
+
+					t.image = context.getImageData( 0, 0, width, height );
+
+				}
+
 				t.flipY = false;
 				t.wrapS = THREE.RepeatWrapping;
 				t.wrapT = THREE.RepeatWrapping;
@@ -920,15 +961,13 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 				delete texture.readyCallbacks;
 
-			} );
+			}, onProgress, onError );
 
 			texture.readyCallbacks = [];
 
-			var uuid = THREE.Math.generateUUID();
-
-			textures[ uuid ] = texture;
+			textures[ fullPath ] = texture;
 
-			return uuid;
+			return fullPath;
 
 		}
 
@@ -959,13 +998,13 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 			/*
 			 * Color
 			 *
-			 * MMD         MeshPhongMaterial
+			 * MMD         MeshToonMaterial
 			 * diffuse  -  color
 			 * specular -  specular
 			 * ambient  -  emissive * a
 			 *               (a = 1.0 without map texture or 0.2 with map texture)
 			 *
-			 * MeshPhongMaterial doesn't have ambient. Set it to emissive instead.
+			 * MeshToonMaterial doesn't have ambient. Set it to emissive instead.
 			 * It'll be too bright if material has map texture so using coef 0.2.
 			 */
 			params.color = new THREE.Color( m.diffuse[ 0 ], m.diffuse[ 1 ], m.diffuse[ 2 ] );
@@ -1069,17 +1108,11 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 		}
 
-		var shader = THREE.ShaderLib[ 'mmd' ];
-
 		for ( var i = 0; i < materialParams.length; i++ ) {
 
 			var p = materialParams[ i ];
 			var p2 = model.materials[ i ];
-			var m = new THREE.ShaderMaterial( {
-				uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
-				vertexShader: shader.vertexShader,
-				fragmentShader: shader.fragmentShader
-			} );
+			var m = new THREE.MeshToonMaterial();
 
 			geometry.addGroup( p.faceOffset * 3, p.faceNum * 3, i );
 
@@ -1216,7 +1249,6 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 				}
 
 				m.map = getTexture( p.map, textures );
-				m.uniforms.map.value = m.map;
 				checkTextureTransparency( m );
 
 			}
@@ -1224,7 +1256,6 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 			if ( p.envMap !== undefined ) {
 
 				m.envMap = getTexture( p.envMap, textures );
-				m.uniforms.envMap.value = m.envMap;
 				m.combine = p.envMapType;
 
 				// TODO: WebGLRenderer should automatically update?
@@ -1236,17 +1267,17 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 			}
 
-			m.uniforms.opacity.value = p.opacity;
-			m.uniforms.diffuse.value.copy( p.color );
+			m.opacity = p.opacity;
+			m.color = p.color;
 
 			if ( p.emissive !== undefined ) {
 
-				m.uniforms.emissive.value.copy( p.emissive );
+				m.emissive = p.emissive;
 
 			}
 
-			m.uniforms.specular.value.copy( p.specular );
-			m.uniforms.shininess.value = Math.max( p.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+			m.specular = p.specular;
+			m.shininess = Math.max( p.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
 
 			if ( model.metadata.format === 'pmd' ) {
 
@@ -1271,21 +1302,9 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 				if ( m.outlineParameters.thickness === 0.0 ) m.outlineParameters.visible = false;
 
-				m.uniforms.toonMap.value = textures[ p2.toonIndex ];
-				m.uniforms.celShading.value = 1;
-
-				if ( p2.toonIndex === -1 ) {
-
-					m.uniforms.hasToonTexture.value = 0;
-
-				} else {
-
-					var n = model.toonTextures[ p2.toonIndex ].fileName;
-					var uuid = loadTexture( n, { defaultTexturePath: isDefaultToonTexture( n ) } );
-					m.uniforms.toonMap.value = textures[ uuid ];
-					m.uniforms.hasToonTexture.value = 1;
-
-				}
+				var toonFileName = ( p2.toonIndex === -1 ) ? 'toon00.bmp' : model.toonTextures[ p2.toonIndex ].fileName;
+				var uuid = loadTexture( toonFileName, { isToonTexture: true, defaultTexturePath: isDefaultToonTexture( toonFileName ) } );
+				m.gradientMap = getTexture( uuid, textures );
 
 			} else {
 
@@ -1298,33 +1317,24 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 				if ( ( p2.flag & 0x10 ) === 0 || m.outlineParameters.thickness === 0.0 ) m.outlineParameters.visible = false;
 
-				m.uniforms.celShading.value = 1;
+				var toonFileName, isDefaultToon;
 
-				if ( p2.toonIndex === -1 ) {
+				if ( p2.toonIndex === -1 || p2.toonFlag !== 0 ) {
 
-					m.uniforms.hasToonTexture.value = 0;
+					var num = p2.toonIndex + 1;
+					toonFileName = 'toon' + ( num < 10 ? '0' + num : num ) + '.bmp';
+					isDefaultToon = true;
 
 				} else {
 
-					if ( p2.toonFlag === 0 ) {
-
-						var n = model.textures[ p2.toonIndex ];
-						var uuid = loadTexture( n );
-						m.uniforms.toonMap.value = textures[ uuid ];
-
-					} else {
-
-						var num = p2.toonIndex + 1;
-						var fileName = 'toon' + ( num < 10 ? '0' + num : num ) + '.bmp';
-						var uuid = loadTexture( fileName, { defaultTexturePath: true } );
-						m.uniforms.toonMap.value = textures[ uuid ];
-
-					}
-
-					m.uniforms.hasToonTexture.value = 1;
+					toonFileName = model.textures[ p2.toonIndex ];
+					isDefaultToon = false;
 
 				}
 
+				var uuid = loadTexture( toonFileName, { isToonTexture: true, defaultTexturePath: isDefaultToon } );
+				m.gradientMap = getTexture( uuid, textures );
+
 			}
 
 			material.materials.push( m );
@@ -1353,7 +1363,7 @@ THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress
 
 					var m = material.materials[ e.index ];
 
-					if ( m.uniforms.opacity.value !== e.diffuse[ 3 ] ) {
+					if ( m.opacity !== e.diffuse[ 3 ] ) {
 
 						m.transparent = true;
 
@@ -1979,74 +1989,6 @@ THREE.MMDLoader.CubicBezierInterpolation.prototype._calculate = function( x1, x2
 
 };
 
-/*
- * Shaders are copied from MeshPhongMaterial and then MMD spcific codes are inserted.
- * Keep shaders updated on MeshPhongMaterial.
- */
-THREE.ShaderLib[ 'mmd' ] = {
-
-	uniforms: THREE.UniformsUtils.merge( [
-
-		THREE.ShaderLib[ 'phong' ].uniforms,
-
-		// MMD specific for toon mapping
-		{
-			"celShading"      : { type: "i", value: 0 },
-			"toonMap"         : { type: "t", value: null },
-			"hasToonTexture"  : { type: "i", value: 0 }
-		}
-
-	] ),
-
-	vertexShader: THREE.ShaderLib[ 'phong' ].vertexShader,
-
-	// put toon mapping logic right before "void main() {...}"
-	fragmentShader: THREE.ShaderLib[ 'phong' ].fragmentShader.replace( /void\s+main\s*\(\s*\)/, [
-
-		"	uniform bool celShading;",
-		"	uniform sampler2D toonMap;",
-		"	uniform bool hasToonTexture;",
-
-		"	vec3 toon ( vec3 lightDirection, vec3 norm ) {",
-		"		if ( ! hasToonTexture ) {",
-		"			return vec3( 1.0 );",
-		"		}",
-		"		vec2 coord = vec2( 0.0, 0.5 * ( 1.0 - dot( lightDirection, norm ) ) );",
-		"		return texture2D( toonMap, coord ).rgb;",
-		"	}",
-
-		// redefine for MMD
-		"#undef RE_Direct",
-		"void RE_Direct_BlinnMMD( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {",
-		"	float dotNL = saturate( dot( geometry.normal, directLight.direction ) );",
-		"	vec3 irradiance = dotNL * directLight.color;",
-
-		"	#ifndef PHYSICALLY_CORRECT_LIGHTS",
-
-		"		irradiance *= PI; // punctual light",
-
-		"	#endif",
-
-		// ---- MMD specific for toon mapping
-		"	if ( celShading ) {",
-		"		reflectedLight.directDiffuse += material.diffuseColor * directLight.color * toon( directLight.direction, geometry.normal );",
-		"	} else {",
-		"		reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );",
-		"	}",
-		// ---- MMD specific for toon mapping
-
-		"	reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;",
-		"}",
-		// ---- MMD specific for toon mapping
-		"#define RE_Direct	RE_Direct_BlinnMMD",
-		// ---- MMD specific for toon mapping
-
-		"void main()"
-
-	].join( "\n" ) )
-
-};
-
 THREE.MMDAudioManager = function ( audio, listener, p ) {
 
 	var params = ( p === null || p === undefined ) ? {} : p;
@@ -2201,6 +2143,9 @@ THREE.MMDHelper = function () {
 	this.doPhysics = true;
 	this.doCameraAnimation = true;
 
+	this.sharedPhysics = false;
+	this.masterPhysics = null;
+
 	this.audioManager = null;
 	this.camera = null;
 
@@ -2256,13 +2201,21 @@ THREE.MMDHelper.prototype = {
 
 	setPhysics: function ( mesh, params ) {
 
-		if ( params === undefined ) params = {};
+		params = ( params === undefined ) ? {} : Object.assign( {}, params );
+
+		if ( params.world === undefined && this.sharedPhysics ) {
+
+			var masterPhysics = this.getMasterPhysics();
+
+			if ( masterPhysics !== null ) params.world = masterPhysics.world;
+
+		}
 
 		var warmup = params.warmup !== undefined ? params.warmup : 60;
 
 		var physics = new THREE.MMDPhysics( mesh, params );
 
-		if ( mesh.mixer !== null && mesh.mixer !== undefined && this.doAnimation === true && params.preventAnimationWarmup !== false ) {
+		if ( mesh.mixer !== null && mesh.mixer !== undefined && params.preventAnimationWarmup !== false ) {
 
 			this.animateOneMesh( 0, mesh );
 			physics.reset();
@@ -2277,6 +2230,26 @@ THREE.MMDHelper.prototype = {
 
 	},
 
+	getMasterPhysics: function () {
+
+		if ( this.masterPhysics !== null ) return this.masterPhysics;
+
+		for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+			var physics = this.meshes[ i ].physics;
+
+			if ( physics !== undefined && physics !== null ) {
+
+				this.masterPhysics = physics;
+				return this.masterPhysics;
+
+			}
+		}
+
+		return null;
+
+	},
+
 	enablePhysics: function ( enabled ) {
 
 		if ( enabled === true ) {
@@ -2539,6 +2512,8 @@ THREE.MMDHelper.prototype = {
 
 		}
 
+		if ( this.sharedPhysics ) this.updateSharedPhysics( delta );
+
 		this.animateCamera( delta );
 
 	},
@@ -2582,7 +2557,7 @@ THREE.MMDHelper.prototype = {
 
 		}
 
-		if ( physics !== null && this.doPhysics === true ) {
+		if ( physics !== null && this.doPhysics && ! this.sharedPhysics) {
 
 			physics.update( delta );
 
@@ -2590,6 +2565,42 @@ THREE.MMDHelper.prototype = {
 
 	},
 
+	updateSharedPhysics: function ( delta ) {
+
+		if ( this.meshes.length === 0 || ! this.doPhysics || ! this.sharedPhysics ) return;
+
+		var physics = this.getMasterPhysics();
+
+		if ( physics === null ) return;
+
+		for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+			var p = this.meshes[ i ].physics;
+
+			if ( p !== null && p !== undefined ) {
+
+				p.updateRigidBodies();
+
+			}
+
+		}
+
+		physics.stepSimulation( delta );
+
+		for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+			var p = this.meshes[ i ].physics;
+
+			if ( p !== null && p !== undefined ) {
+
+				p.updateBones();
+
+			}
+
+		}
+
+	},
+
 	animateCamera: function ( delta ) {
 
 		if ( this.camera === null ) {

+ 71 - 71
examples/js/renderers/WebGLDeferredRenderer.js

@@ -1623,13 +1623,13 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			map: { type: "t", value: null },
-			offsetRepeat: { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+			map: { value: null },
+			offsetRepeat: { value: new THREE.Vector4( 0, 0, 1, 1 ) },
 
-			diffuse: { type: "c", value: new THREE.Color( 0x000000 ) },
-			emissive: { type: "c", value: new THREE.Color( 0x000000 ) },
-			specular: { type: "c", value: new THREE.Color( 0x000000 ) },
-			shininess: { type: "f", value: 30.0 }
+			diffuse: { value: new THREE.Color( 0x000000 ) },
+			emissive: { value: new THREE.Color( 0x000000 ) },
+			specular: { value: new THREE.Color( 0x000000 ) },
+			shininess: { value: 30.0 }
 
 		},
 
@@ -1687,9 +1687,9 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerColor: { type: "t", value: null },
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 }
+			samplerColor: { value: null },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 }
 
 		},
 
@@ -1729,18 +1729,18 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepth: { type: "t", value: null },
-			samplerColor: { type: "t", value: null },
+			samplerNormalDepth: { value: null },
+			samplerColor: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightPositionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightIntensity: { type: "f", value: 1.0 },
-			lightRadius: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightPositionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightIntensity: { value: 1.0 },
+			lightRadius: { value: 1.0 }
 
 		},
 
@@ -1811,19 +1811,19 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepth: { type: "t", value: null },
-			samplerColor: { type: "t", value: null },
+			samplerNormalDepth: { value: null },
+			samplerColor: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightDirectionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightPositionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightAngle: { type: "f", value: 1.0 },
-			lightIntensity: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightDirectionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightPositionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightAngle: { value: 1.0 },
+			lightIntensity: { value: 1.0 }
 
 		},
 
@@ -1907,17 +1907,17 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepth: { type: "t", value: null },
-			samplerColor: { type: "t", value: null },
+			samplerNormalDepth: { value: null },
+			samplerColor: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightDirectionVS : { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightIntensity: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightDirectionVS : { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightIntensity: { value: 1.0 }
 
 		},
 
@@ -1972,7 +1972,7 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			shininess: { type: "f", value: 30.0 }
+			shininess: { value: 30.0 }
 
 		},
 
@@ -2030,17 +2030,17 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepthShininess: { type: "t", value: null },
+			samplerNormalDepthShininess: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightPositionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightIntensity: { type: "f", value: 1.0 },
-			lightRadius: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightPositionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightIntensity: { value: 1.0 },
+			lightRadius: { value: 1.0 }
 
 		},
 
@@ -2101,18 +2101,18 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepthShininess: { type: "t", value: null },
+			samplerNormalDepthShininess: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightDirectionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightPositionVS: { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightAngle: { type: "f", value: 1.0 },
-			lightIntensity: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightDirectionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightPositionVS: { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightAngle: { value: 1.0 },
+			lightIntensity: { value: 1.0 }
 
 		},
 
@@ -2194,16 +2194,16 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerNormalDepthShininess: { type: "t", value: null },
+			samplerNormalDepthShininess: { value: null },
 
-			matProjInverse: { type: "m4", value: new THREE.Matrix4() },
+			matProjInverse: { value: new THREE.Matrix4() },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			lightColor: { type: "c", value: new THREE.Color( 0x000000 ) },
-			lightDirectionVS : { type: "v3", value: new THREE.Vector3( 0, 1, 0 ) },
-			lightIntensity: { type: "f", value: 1.0 }
+			lightColor: { value: new THREE.Color( 0x000000 ) },
+			lightDirectionVS : { value: new THREE.Vector3( 0, 1, 0 ) },
+			lightIntensity: { value: 1.0 }
 
 		},
 
@@ -2258,18 +2258,18 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerLight: { type: "t", value: null },
+			samplerLight: { value: null },
 
-			map: { type: "t", value: null },
-			offsetRepeat: { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) },
+			map: { value: null },
+			offsetRepeat: { value: new THREE.Vector4( 0, 0, 1, 1 ) },
 
-			viewWidth: { type: "f", value: 800 },
-			viewHeight: { type: "f", value: 600 },
+			viewWidth: { value: 800 },
+			viewHeight: { value: 600 },
 
-			diffuse: { type: "c", value: new THREE.Color( 0x000000 ) },
-			emissive: { type: "c", value: new THREE.Color( 0x000000 ) },
-			specular: { type: "c", value: new THREE.Color( 0x000000 ) },
-			shininess: { type: "f", value: 30.0 }
+			diffuse: { value: new THREE.Color( 0x000000 ) },
+			emissive: { value: new THREE.Color( 0x000000 ) },
+			specular: { value: new THREE.Color( 0x000000 ) },
+			shininess: { value: 30.0 }
 
 		},
 
@@ -2341,7 +2341,7 @@ THREE.ShaderDeferred = {
 
 		uniforms: {
 
-			samplerResult: { type: "t", value: null }
+			samplerResult: { value: null }
 
 		},
 

+ 30 - 2
examples/webgl_loader_mmd.html

@@ -160,6 +160,27 @@
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
+				var phongMaterials;
+				var originalMaterials;
+
+				function makePhongMaterials ( materials ) {
+
+					var array = [];
+
+					for ( var i = 0, il = materials.length; i < il; i ++ ) {
+
+						var m = new THREE.MeshPhongMaterial();
+						m.copy( materials[ i ] );
+						m.needsUpdate = true;
+
+						array.push( m );
+
+					}
+
+					phongMaterials = new THREE.MultiMaterial( array );
+
+				}
+
 				function initGui () {
 
 					var api = {
@@ -180,9 +201,16 @@
 
 					gui.add( api, 'gradient mapping' ).onChange( function () {
 
-						for ( var i = 0, il = mesh.material.materials.length; i < il; i ++ ) {
+						if ( originalMaterials === undefined ) originalMaterials = mesh.material;
+						if ( phongMaterials === undefined ) makePhongMaterials( mesh.material.materials );
+
+						if ( api[ 'gradient mapping' ] ) {
+
+							mesh.material = originalMaterials;
+
+						} else {
 
-							var material = mesh.material.materials[ i ].uniforms.celShading.value = api[ 'gradient mapping' ] ? 1 : 0;
+							mesh.material = phongMaterials;
 
 						}
 

+ 239 - 0
examples/webgl_materials_variations_toon.html

@@ -0,0 +1,239 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - materials</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #fff;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				background-color: #000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - Toon Material Variantions w/ OutlineEffect by <a href="http://clara.io/" target="_blank">Ben Houston</a>.</div>
+
+		<script src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/effects/OutlineEffect.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+
+			var camera, scene, renderer, controls, effect;
+			var particleLight;
+
+			var loader = new THREE.FontLoader();
+			loader.load( 'fonts/gentilis_regular.typeface.json', function ( font ) {
+
+				init( font );
+				animate();
+
+			} );
+
+			function init( font ) {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 0.0, 400, 400 * 3.5 );
+
+				//
+
+				var reflectionCube = new THREE.CubeTextureLoader()
+					.setPath( 'textures/cube/SwedishRoyalCastle/' )
+					.load( [ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ] );
+				reflectionCube.format = THREE.RGBFormat;
+
+				scene = new THREE.Scene();
+				scene.background = reflectionCube;
+
+				// Materials
+
+				var imgTexture = new THREE.TextureLoader().load( "textures/planets/moon_1024.jpg" );
+				imgTexture.wrapS = imgTexture.wrapT = THREE.RepeatWrapping;
+				imgTexture.anisotropy = 16;
+				imgTexture = null;
+
+				var shininess = 50, specular = 0x333333, bumpScale = 1, shading = THREE.SmoothShading;
+
+				var materials = [];
+
+				var cubeWidth = 400;
+				var numberOfSphersPerSide = 5;
+				var sphereRadius = ( cubeWidth / numberOfSphersPerSide ) * 0.8 * 0.5;
+				var stepSize = 1.0 / numberOfSphersPerSide;
+
+				var geometry = new THREE.SphereBufferGeometry( sphereRadius, 32, 16 );
+
+				for ( var alpha = 0, alphaIndex = 0; alpha <= 1.0; alpha += stepSize, alphaIndex ++ ) {
+
+					var specularShininess = Math.pow( 2, alpha * 10 );
+
+					for ( var beta = 0; beta <= 1.0; beta += stepSize ) {
+
+						var specularColor = new THREE.Color( beta * 0.2, beta * 0.2, beta * 0.2 );
+
+						for ( var gamma = 0; gamma <= 1.0; gamma += stepSize ) {
+
+							// basic monochromatic energy preservation
+							var diffuseColor = new THREE.Color().setHSL( alpha, 0.5, gamma * 0.5 ).multiplyScalar( 1 - beta * 0.2 );
+
+							var material = new THREE.MeshToonMaterial( {
+								map: imgTexture,
+								bumpMap: imgTexture,
+								bumpScale: bumpScale,
+								color: diffuseColor,
+								specular: specularColor,
+								reflectivity: beta,
+								shininess: specularShininess,
+								shading: THREE.SmoothShading,
+								envMap: alphaIndex % 2 === 0 ? null : reflectionCube
+							} );
+
+							var mesh = new THREE.Mesh( geometry, material );
+
+							mesh.position.x = alpha * 400 - 200;
+							mesh.position.y = beta * 400 - 200;
+							mesh.position.z = gamma * 400 - 200;
+
+							scene.add( mesh );
+
+						}
+
+					}
+
+				}
+
+				function addLabel( name, location ) {
+
+					var textGeo = new THREE.TextGeometry( name, {
+
+						font: font,
+
+						size: 20,
+						height: 1,
+						curveSegments: 1
+
+					});
+
+					var textMaterial = new THREE.MeshBasicMaterial( { color: 0xffffff } );
+					var textMesh = new THREE.Mesh( textGeo, textMaterial );
+					textMesh.position.copy( location );
+					scene.add( textMesh );
+
+				}
+
+				addLabel( "-shininess", new THREE.Vector3( -350, 0, 0 ) );
+				addLabel( "+shininess", new THREE.Vector3( 350, 0, 0 ) );
+
+				addLabel( "-specular, -reflectivity", new THREE.Vector3( 0, -300, 0 ) );
+				addLabel( "+specular, +reflectivity", new THREE.Vector3( 0, 300, 0 ) );
+
+				addLabel( "-diffuse", new THREE.Vector3( 0, 0, -300 ) );
+				addLabel( "+diffuse", new THREE.Vector3( 0, 0, 300 ) );
+
+				particleLight = new THREE.Mesh( new THREE.SphereBufferGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
+				scene.add( particleLight );
+
+				// Lights
+
+				scene.add( new THREE.AmbientLight( 0x222222 ) );
+
+				var directionalLight = new THREE.DirectionalLight( 0xffffff, 1 );
+				directionalLight.position.set( 1, 1, 1 ).normalize();
+				scene.add( directionalLight );
+
+				var pointLight = new THREE.PointLight( 0xffffff, 2, 800 );
+				particleLight.add( pointLight );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+
+				effect = new THREE.OutlineEffect( renderer );
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				controls = new THREE.OrbitControls( camera );
+				controls.target.set( 0, 0, 0 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				stats.begin();
+				render();
+				stats.end();
+
+			}
+
+			function render() {
+
+				var timer = Date.now() * 0.00025;
+
+				//camera.position.x = Math.cos( timer ) * 800;
+				//camera.position.z = Math.sin( timer ) * 800;
+
+				camera.lookAt( scene.position );
+
+				particleLight.position.x = Math.sin( timer * 7 ) * 300;
+				particleLight.position.y = Math.cos( timer * 5 ) * 400;
+				particleLight.position.z = Math.cos( timer * 3 ) * 300;
+
+				effect.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 19 - 2
src/audio/AudioListener.js

@@ -101,8 +101,25 @@ AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), {
 
 			orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );
 
-			listener.setPosition( position.x, position.y, position.z );
-			listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
+			if ( listener.positionX ) {
+
+				listener.positionX.setValueAtTime( position.x, this.context.currentTime );
+				listener.positionY.setValueAtTime( position.y, this.context.currentTime );
+				listener.positionZ.setValueAtTime( position.z, this.context.currentTime );
+				listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime );
+				listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime );
+				listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime );
+				listener.upX.setValueAtTime( up.x, this.context.currentTime );
+				listener.upY.setValueAtTime( up.y, this.context.currentTime );
+				listener.upZ.setValueAtTime( up.z, this.context.currentTime );
+
+			}
+			else {
+
+				listener.setPosition( position.x, position.y, position.z );
+				listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );
+
+			}
 
 		};
 

+ 13 - 13
src/loaders/FileLoader.js

@@ -46,15 +46,13 @@ Object.assign( FileLoader.prototype, {
 		// Safari can not handle Data URIs through XMLHttpRequest so process manually
 		if ( dataUriRegexResult ) {
 
-			var mimeType = dataUriRegexResult[1];
-			var isBase64 = !!dataUriRegexResult[2];
-			var data = dataUriRegexResult[3];
+			var mimeType = dataUriRegexResult[ 1 ];
+			var isBase64 = !! dataUriRegexResult[ 2 ];
+			var data = dataUriRegexResult[ 3 ];
 
-			data = window.decodeURIComponent(data);
+			data = window.decodeURIComponent( data );
 
-			if( isBase64 ) {
-				data = window.atob(data);
-			}
+			if ( isBase64 ) data = window.atob( data );
 
 			try {
 
@@ -67,16 +65,18 @@ Object.assign( FileLoader.prototype, {
 					case 'blob':
 
 					 	response = new ArrayBuffer( data.length );
+
 						var view = new Uint8Array( response );
+
 						for ( var i = 0; i < data.length; i ++ ) {
 
-								view[ i ] = data.charCodeAt( i );
+							view[ i ] = data.charCodeAt( i );
 
 						}
 
 						if ( responseType === 'blob' ) {
 
-							response = new Blob( [ response ], { "type" : mimeType } );
+							response = new Blob( [ response ], { type: mimeType } );
 
 						}
 
@@ -104,24 +104,24 @@ Object.assign( FileLoader.prototype, {
 				}
 
 				// Wait for next browser tick
-				window.setTimeout( function() {
+				window.setTimeout( function () {
 
 					if ( onLoad ) onLoad( response );
 
 					scope.manager.itemEnd( url );
 
-				}, 0);
+				}, 0 );
 
 			} catch ( error ) {
 
 				// Wait for next browser tick
-				window.setTimeout( function() {
+				window.setTimeout( function () {
 
 					if ( onError ) onError( error );
 
 					scope.manager.itemError( url );
 
-				}, 0);
+				}, 0 );
 
 			}
 

+ 7 - 0
src/loaders/ImageLoader.js

@@ -35,6 +35,13 @@ Object.assign( ImageLoader.prototype, {
 
 			image.src = url;
 
+		} else if ( this.crossOrigin !== undefined ) {
+
+			// crossOrigin doesn't work with URL.createObjectURL()?
+
+			image.crossOrigin = this.crossOrigin;
+			image.src = url;
+
 		} else {
 
 			var loader = new FileLoader();

+ 2 - 0
src/loaders/MaterialLoader.js

@@ -142,6 +142,8 @@ Object.assign( MaterialLoader.prototype, {
 		if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );
 		if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
 
+		if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
+
 		// MultiMaterial
 
 		if ( json.materials !== undefined ) {

+ 6 - 0
src/materials/Material.js

@@ -202,6 +202,12 @@ Material.prototype = {
 
 		}
 
+		if ( this.gradientMap && this.gradientMap.isTexture ) {
+
+			data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
+
+		}
+
 		if ( this.size !== undefined ) data.size = this.size;
 		if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
 

+ 1 - 0
src/materials/Materials.js

@@ -7,6 +7,7 @@ export { MultiMaterial } from './MultiMaterial.js';
 export { MeshPhysicalMaterial } from './MeshPhysicalMaterial.js';
 export { MeshStandardMaterial } from './MeshStandardMaterial.js';
 export { MeshPhongMaterial } from './MeshPhongMaterial.js';
+export { MeshToonMaterial } from './MeshToonMaterial.js';
 export { MeshNormalMaterial } from './MeshNormalMaterial.js';
 export { MeshLambertMaterial } from './MeshLambertMaterial.js';
 export { MeshDepthMaterial } from './MeshDepthMaterial.js';

+ 20 - 0
src/renderers/WebGLRenderer.js

@@ -623,6 +623,7 @@ function WebGLRenderer( parameters ) {
 			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );
 
 			if ( ! material.isMeshPhongMaterial &&
+				! material.isMeshToonMaterial &&
 				! material.isMeshStandardMaterial &&
 				material.shading === FlatShading ) {
 
@@ -1775,6 +1776,7 @@ function WebGLRenderer( parameters ) {
 
 			if ( material.isShaderMaterial ||
 				material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
 				material.isMeshStandardMaterial ||
 				material.envMap ) {
 
@@ -1790,6 +1792,7 @@ function WebGLRenderer( parameters ) {
 			}
 
 			if ( material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
 				material.isMeshLambertMaterial ||
 				material.isMeshBasicMaterial ||
 				material.isMeshStandardMaterial ||
@@ -1862,6 +1865,7 @@ function WebGLRenderer( parameters ) {
 			if ( material.isMeshBasicMaterial ||
 				material.isMeshLambertMaterial ||
 				material.isMeshPhongMaterial ||
+				material.isMeshToonMaterial ||
 				material.isMeshStandardMaterial ||
 				material.isMeshDepthMaterial ) {
 
@@ -1892,6 +1896,10 @@ function WebGLRenderer( parameters ) {
 
 				refreshUniformsPhong( m_uniforms, material );
 
+			} else if ( material.isMeshToonMaterial ) {
+
+				refreshUniformsToon( m_uniforms, material );
+
 			} else if ( material.isMeshPhysicalMaterial ) {
 
 				refreshUniformsPhysical( m_uniforms, material );
@@ -2138,6 +2146,18 @@ function WebGLRenderer( parameters ) {
 
 	}
 
+	function refreshUniformsToon( uniforms, material ) {
+
+		refreshUniformsPhong( uniforms, material );
+
+		if ( material.gradientMap ) {
+
+			uniforms.gradientMap.value = material.gradientMap;
+
+		}
+
+	}
+
 	function refreshUniformsStandard( uniforms, material ) {
 
 		uniforms.roughness.value = material.roughness;

+ 6 - 0
src/renderers/shaders/ShaderChunk.js

@@ -30,6 +30,7 @@ import envmap_pars_vertex from './ShaderChunk/envmap_pars_vertex.glsl';
 import envmap_vertex from './ShaderChunk/envmap_vertex.glsl';
 import fog_fragment from './ShaderChunk/fog_fragment.glsl';
 import fog_pars_fragment from './ShaderChunk/fog_pars_fragment.glsl';
+import gradientmap_pars_fragment from './ShaderChunk/gradientmap_pars_fragment.glsl';
 import lightmap_fragment from './ShaderChunk/lightmap_fragment.glsl';
 import lightmap_pars_fragment from './ShaderChunk/lightmap_pars_fragment.glsl';
 import lights_lambert_vertex from './ShaderChunk/lights_lambert_vertex.glsl';
@@ -98,6 +99,8 @@ import meshphong_frag from './ShaderLib/meshphong_frag.glsl';
 import meshphong_vert from './ShaderLib/meshphong_vert.glsl';
 import meshphysical_frag from './ShaderLib/meshphysical_frag.glsl';
 import meshphysical_vert from './ShaderLib/meshphysical_vert.glsl';
+import meshtoon_frag from './ShaderLib/meshtoon_frag.glsl';
+import meshtoon_vert from './ShaderLib/meshtoon_vert.glsl';
 import normal_frag from './ShaderLib/normal_frag.glsl';
 import normal_vert from './ShaderLib/normal_vert.glsl';
 import points_frag from './ShaderLib/points_frag.glsl';
@@ -138,6 +141,7 @@ export var ShaderChunk = {
 	envmap_vertex: envmap_vertex,
 	fog_fragment: fog_fragment,
 	fog_pars_fragment: fog_pars_fragment,
+	gradientmap_pars_fragment: gradientmap_pars_fragment,
 	lightmap_fragment: lightmap_fragment,
 	lightmap_pars_fragment: lightmap_pars_fragment,
 	lights_lambert_vertex: lights_lambert_vertex,
@@ -206,6 +210,8 @@ export var ShaderChunk = {
 	meshphong_vert: meshphong_vert,
 	meshphysical_frag: meshphysical_frag,
 	meshphysical_vert: meshphysical_vert,
+	meshtoon_frag: meshtoon_frag,
+	meshtoon_vert: meshtoon_vert,
 	normal_frag: normal_frag,
 	normal_vert: normal_vert,
 	points_frag: points_frag,

+ 1 - 1
src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl

@@ -1,6 +1,6 @@
 #if NUM_CLIPPING_PLANES > 0
 
-	#if ! defined( PHYSICAL ) && ! defined( PHONG )
+	#if ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( TOON )
 		varying vec3 vViewPosition;
 	#endif
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl

@@ -1,3 +1,3 @@
-#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )
+#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( TOON )
 	varying vec3 vViewPosition;
 #endif

+ 1 - 1
src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl

@@ -1,4 +1,4 @@
-#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )
+#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG ) && ! defined( TOON )
 	vViewPosition = - mvPosition.xyz;
 #endif
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_fragment.glsl

@@ -1,6 +1,6 @@
 #ifdef USE_ENVMAP
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( TOON )
 
 		vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );
 

+ 2 - 2
src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl

@@ -5,7 +5,7 @@
 
 #ifdef USE_ENVMAP
 
-	#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )
+	#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( TOON ) )
 		varying vec3 vWorldPosition;
 	#endif
 
@@ -16,7 +16,7 @@
 	#endif
 	uniform float flipEnvMap;
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( TOON ) || defined( PHYSICAL )
 		uniform float refractionRatio;
 	#else
 		varying vec3 vReflect;

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl

@@ -1,6 +1,6 @@
 #ifdef USE_ENVMAP
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( TOON )
 		varying vec3 vWorldPosition;
 
 	#else

+ 1 - 1
src/renderers/shaders/ShaderChunk/envmap_vertex.glsl

@@ -1,6 +1,6 @@
 #ifdef USE_ENVMAP
 
-	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )
+	#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( TOON )
 
 		vWorldPosition = worldPosition.xyz;
 

+ 24 - 0
src/renderers/shaders/ShaderChunk/gradientmap_pars_fragment.glsl

@@ -0,0 +1,24 @@
+#ifdef TOON
+
+	uniform sampler2D gradientMap;
+
+	vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {
+
+		// dotNL will be from -1.0 to 1.0
+		float dotNL = dot( normal, lightDirection );
+		vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );
+
+		#ifdef USE_GRADIENTMAP
+
+			return texture2D( gradientMap, coord ).rgb;
+
+		#else
+
+			return ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );
+
+		#endif
+
+
+	}
+
+#endif

+ 9 - 2
src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl

@@ -44,9 +44,16 @@ struct BlinnPhongMaterial {
 
 void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
 
-	float dotNL = saturate( dot( geometry.normal, directLight.direction ) );
+	#ifdef TOON
 
-	vec3 irradiance = dotNL * directLight.color;
+		vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;
+
+	#else
+
+		float dotNL = saturate( dot( geometry.normal, directLight.direction ) );
+		vec3 irradiance = dotNL * directLight.color;
+
+	#endif
 
 	#ifndef PHYSICALLY_CORRECT_LIGHTS
 

+ 1 - 1
src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl

@@ -1,4 +1,4 @@
-#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )
+#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( TOON ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )
 
 	#ifdef USE_SKINNING
 

+ 25 - 0
src/renderers/shaders/ShaderLib.js

@@ -68,6 +68,31 @@ var ShaderLib = {
 
 	},
 
+	toon: {
+
+		uniforms: Object.assign( {},
+			UniformsLib.common,
+			UniformsLib.aomap,
+			UniformsLib.lightmap,
+			UniformsLib.emissivemap,
+			UniformsLib.bumpmap,
+			UniformsLib.normalmap,
+			UniformsLib.displacementmap,
+			UniformsLib.gradientmap,
+			UniformsLib.fog,
+			UniformsLib.lights,
+			{
+				emissive : { value: new Color( 0x000000 ) },
+				specular : { value: new Color( 0x111111 ) },
+				shininess: { value: 30 }
+			}
+		),
+
+		vertexShader: ShaderChunk.meshtoon_vert,
+		fragmentShader: ShaderChunk.meshtoon_frag
+
+	},
+
 	standard: {
 
 		uniforms: Object.assign( {},

+ 6 - 0
src/renderers/shaders/UniformsLib.js

@@ -81,6 +81,12 @@ var UniformsLib = {
 
 	},
 
+	gradientmap: {
+
+		gradientMap: { value: null }
+
+	},
+
 	fog: {
 
 		fogDensity: { value: 0.00025 },

+ 2 - 0
src/renderers/webgl/WebGLProgram.js

@@ -446,6 +446,8 @@ function WebGLProgram( renderer, code, material, parameters ) {
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 			parameters.vertexColors ? '#define USE_COLOR' : '',
 
+			parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',
+
 			parameters.flatShading ? '#define FLAT_SHADED' : '',
 
 			parameters.doubleSided ? '#define DOUBLE_SIDED' : '',

+ 4 - 1
src/renderers/webgl/WebGLPrograms.js

@@ -15,6 +15,7 @@ function WebGLPrograms( renderer, capabilities ) {
 		MeshBasicMaterial: 'basic',
 		MeshLambertMaterial: 'lambert',
 		MeshPhongMaterial: 'phong',
+		MeshToonMaterial: 'toon',
 		MeshStandardMaterial: 'physical',
 		MeshPhysicalMaterial: 'physical',
 		LineBasicMaterial: 'basic',
@@ -25,7 +26,7 @@ function WebGLPrograms( renderer, capabilities ) {
 	var parameterNames = [
 		"precision", "supportsVertexTextures", "map", "mapEncoding", "envMap", "envMapMode", "envMapEncoding",
 		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "displacementMap", "specularMap",
-		"roughnessMap", "metalnessMap",
+		"roughnessMap", "metalnessMap", "gradientMap",
 		"alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
 		"maxBones", "useVertexTexture", "morphTargets", "morphNormals",
@@ -153,6 +154,8 @@ function WebGLPrograms( renderer, capabilities ) {
 			specularMap: !! material.specularMap,
 			alphaMap: !! material.alphaMap,
 
+			gradientMap: !! material.gradientMap,
+
 			combine: material.combine,
 
 			vertexColors: material.vertexColors,