Browse Source

Merge branch 'dev' into WebGLRendererWebGL2.0

Takahiro 7 years ago
parent
commit
a18d1a217d
77 changed files with 7792 additions and 4698 deletions
  1. 375 61
      build/three.js
  2. 334 329
      build/three.min.js
  3. 375 61
      build/three.module.js
  4. 102 0
      docs/examples/animations/CCDIKSolver.html
  5. 169 0
      docs/examples/animations/MMDAnimationHelper.html
  6. 112 0
      docs/examples/animations/MMDPhysics.html
  7. 22 0
      docs/examples/loaders/GLTFLoader.html
  8. 118 0
      docs/examples/loaders/MMDLoader.html
  9. 7 0
      docs/list.js
  10. 6 0
      editor/index.html
  11. 1 0
      examples/files.js
  12. 27 1
      examples/js/BufferGeometryUtils.js
  13. 327 327
      examples/js/MarchingCubes.js
  14. 299 299
      examples/js/Octree.js
  15. 41 4
      examples/js/ShaderGodRays.js
  16. 278 239
      examples/js/animation/CCDIKSolver.js
  17. 1035 0
      examples/js/animation/MMDAnimationHelper.js
  18. 965 774
      examples/js/animation/MMDPhysics.js
  19. 4 12
      examples/js/controls/TransformControls.js
  20. 32 33
      examples/js/crossfade/scenes.js
  21. 8 8
      examples/js/effects/OutlineEffect.js
  22. 0 1
      examples/js/libs/draco/gltf/draco_decoder.js
  23. BIN
      examples/js/libs/draco/gltf/draco_decoder.wasm
  24. 3 0
      examples/js/libs/draco/gltf/draco_encoder.js
  25. 119 115
      examples/js/libs/draco/gltf/draco_wasm_wrapper.js
  26. 2 2
      examples/js/lines/LineMaterial.js
  27. 82 15
      examples/js/loaders/DRACOLoader.js
  28. 428 149
      examples/js/loaders/GLTFLoader.js
  29. 1169 1930
      examples/js/loaders/MMDLoader.js
  30. 1 1
      examples/js/loaders/sea3d/SEA3DLoader.js
  31. 46 4
      examples/js/vr/WebVR.js
  32. 26 24
      examples/misc_exporter_obj.html
  33. 52 6
      examples/webaudio_sandbox.html
  34. 52 4
      examples/webaudio_timing.html
  35. 59 10
      examples/webaudio_visualizer.html
  36. 9 3
      examples/webgl_buffergeometry_instancing_dynamic.html
  37. 9 3
      examples/webgl_buffergeometry_instancing_interleaved_dynamic.html
  38. 422 0
      examples/webgl_buffergeometry_instancing_lambert.html
  39. 9 15
      examples/webgl_geometry_minecraft.html
  40. 33 28
      examples/webgl_interactive_cubes_gpu.html
  41. 2 3
      examples/webgl_interactive_instances_gpu.html
  42. 16 23
      examples/webgl_loader_mmd.html
  43. 17 19
      examples/webgl_loader_mmd_audio.html
  44. 5 4
      examples/webgl_loader_mmd_pose.html
  45. 1 1
      examples/webgl_materials.html
  46. 2 2
      examples/webgl_morphnormals.html
  47. 46 36
      examples/webgl_multiple_elements_text.html
  48. 1 1
      examples/webgl_physics_convex_break.html
  49. 3 3
      examples/webgl_physics_volume.html
  50. 1 0
      examples/webgl_postprocessing_crossfade.html
  51. 20 2
      examples/webgl_postprocessing_godrays.html
  52. 5 10
      examples/webgl_raycast_texture.html
  53. 1 15
      examples/webgl_shaders_tonemapping.html
  54. 2 2
      examples/webgl_shadowmap.html
  55. 2 2
      examples/webgl_shadowmap_performance.html
  56. 6 2
      examples/webvr_cubes.html
  57. 6 2
      examples/webvr_daydream.html
  58. 6 2
      examples/webvr_gearvr.html
  59. 6 2
      examples/webvr_panorama.html
  60. 6 2
      examples/webvr_rollercoaster.html
  61. 6 2
      examples/webvr_sandbox.html
  62. 6 2
      examples/webvr_video.html
  63. 9 5
      examples/webvr_vive.html
  64. 6 2
      examples/webvr_vive_dragging.html
  65. 15 9
      examples/webvr_vive_paint.html
  66. 11 7
      examples/webvr_vive_sculpt.html
  67. 21 32
      src/math/Box3.js
  68. 2 0
      src/renderers/WebGLRenderTarget.js
  69. 19 14
      src/renderers/WebGLRenderer.js
  70. 1 17
      src/renderers/webgl/WebGLState.js
  71. 175 16
      src/renderers/webgl/WebGLUniforms.js
  72. 9 1
      src/renderers/webvr/WebVRManager.js
  73. 146 0
      src/renderers/webvr/WebXRManager.js
  74. 1 0
      test/benchmark/benchmarks.html
  75. 48 0
      test/benchmark/core/UpdateMatrixWorld.js
  76. 1 0
      utils/build/externs.js
  77. 4 0
      utils/exporters/blender/README.md

+ 375 - 61
build/three.js

@@ -4582,6 +4582,8 @@
 
 		this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
 
+		this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true;
+
 		this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
 		this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
 		this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
@@ -5241,41 +5243,30 @@
 
 		},
 
-		applyMatrix4: function () {
-
-			var points = [
-				new Vector3(),
-				new Vector3(),
-				new Vector3(),
-				new Vector3(),
-				new Vector3(),
-				new Vector3(),
-				new Vector3(),
-				new Vector3()
-			];
-
-			return function applyMatrix4( matrix ) {
+		applyMatrix4: function ( matrix ) {
 
-				// transform of empty box is an empty box.
-				if ( this.isEmpty() ) return this;
+			// transform of empty box is an empty box.
+			if ( this.isEmpty( ) ) return this;
 
-				// NOTE: I am using a binary pattern to specify all 2^3 combinations below
-				points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
-				points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
-				points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
-				points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
-				points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
-				points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
-				points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
-				points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );	// 111
+			var m = matrix.elements;
 
-				this.setFromPoints( points );
+			var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x;
+			var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x;
+			var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y;
+			var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y;
+			var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z;
+			var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z;
 
-				return this;
+			this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ];
+			this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ];
+			this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ];
+			this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ];
+			this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ];
+			this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ];
 
-			};
+			return this;
 
-		}(),
+		},
 
 		translate: function ( offset ) {
 
@@ -15524,6 +15515,7 @@
 
 	var mat4array = new Float32Array( 16 );
 	var mat3array = new Float32Array( 9 );
+	var mat2array = new Float32Array( 4 );
 
 	// Flattening for arrays of vectors and matrices
 
@@ -15562,6 +15554,30 @@
 
 	}
 
+	function arraysEqual( a, b ) {
+
+		if ( a.length !== b.length ) return false;
+
+		for ( var i = 0, l = a.length; i < l; i ++ ) {
+
+			if ( a[ i ] !== b[ i ] ) return false;
+
+		}
+
+		return true;
+
+	}
+
+	function copyArray( a, b ) {
+
+		for ( var i = 0, l = b.length; i < l; i ++ ) {
+
+			a[ i ] = b[ i ];
+
+		}
+
+	}
+
 	// Texture unit allocation
 
 	function allocTexUnits( renderer, n ) {
@@ -15591,27 +15607,52 @@
 
 	function setValue1f( gl, v ) {
 
+		var cache = this.cache;
+
+		if ( cache[ 0 ] === v ) return;
+
 		gl.uniform1f( this.addr, v );
 
+		cache[ 0 ] = v;
+
 	}
 
 	function setValue1i( gl, v ) {
 
+		var cache = this.cache;
+
+		if ( cache[ 0 ] === v ) return;
+
 		gl.uniform1i( this.addr, v );
 
+		cache[ 0 ] = v;
+
 	}
 
 	// Single float vector (from flat array or THREE.VectorN)
 
 	function setValue2fv( gl, v ) {
 
-		if ( v.x === undefined ) {
+		var cache = this.cache;
 
-			gl.uniform2fv( this.addr, v );
+		if ( v.x !== undefined ) {
+
+			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
+
+				gl.uniform2f( this.addr, v.x, v.y );
+
+				cache[ 0 ] = v.x;
+				cache[ 1 ] = v.y;
+
+			}
 
 		} else {
 
-			gl.uniform2f( this.addr, v.x, v.y );
+			if ( arraysEqual( cache, v ) ) return;
+
+			gl.uniform2fv( this.addr, v );
+
+			copyArray( cache, v );
 
 		}
 
@@ -15619,31 +15660,68 @@
 
 	function setValue3fv( gl, v ) {
 
+		var cache = this.cache;
+
 		if ( v.x !== undefined ) {
 
-			gl.uniform3f( this.addr, v.x, v.y, v.z );
+			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
+
+				gl.uniform3f( this.addr, v.x, v.y, v.z );
+
+				cache[ 0 ] = v.x;
+				cache[ 1 ] = v.y;
+				cache[ 2 ] = v.z;
+
+			}
 
 		} else if ( v.r !== undefined ) {
 
-			gl.uniform3f( this.addr, v.r, v.g, v.b );
+			if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
+
+				gl.uniform3f( this.addr, v.r, v.g, v.b );
+
+				cache[ 0 ] = v.r;
+				cache[ 1 ] = v.g;
+				cache[ 2 ] = v.b;
+
+			}
 
 		} else {
 
+			if ( arraysEqual( cache, v ) ) return;
+
 			gl.uniform3fv( this.addr, v );
 
+			copyArray( cache, v );
+
 		}
 
 	}
 
 	function setValue4fv( gl, v ) {
 
-		if ( v.x === undefined ) {
+		var cache = this.cache;
 
-			gl.uniform4fv( this.addr, v );
+		if ( v.x !== undefined ) {
+
+			if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
+
+				gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+
+				cache[ 0 ] = v.x;
+				cache[ 1 ] = v.y;
+				cache[ 2 ] = v.z;
+				cache[ 3 ] = v.w;
+
+			}
 
 		} else {
 
-			 gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+			if ( arraysEqual( cache, v ) ) return;
+
+			gl.uniform4fv( this.addr, v );
+
+			copyArray( cache, v );
 
 		}
 
@@ -15653,36 +15731,81 @@
 
 	function setValue2fm( gl, v ) {
 
-		gl.uniformMatrix2fv( this.addr, false, v.elements || v );
+		var cache = this.cache;
+		var elements = v.elements;
+
+		if ( elements === undefined ) {
+
+			if ( arraysEqual( cache, v ) ) return;
+
+			gl.uniformMatrix2fv( this.addr, false, v );
+
+			copyArray( cache, v );
+
+		} else {
+
+			if ( arraysEqual( cache, elements ) ) return;
+
+			mat2array.set( elements );
+
+			gl.uniformMatrix2fv( this.addr, false, mat2array );
+
+			copyArray( cache, elements );
+
+		}
 
 	}
 
 	function setValue3fm( gl, v ) {
 
-		if ( v.elements === undefined ) {
+		var cache = this.cache;
+		var elements = v.elements;
+
+		if ( elements === undefined ) {
+
+			if ( arraysEqual( cache, v ) ) return;
 
 			gl.uniformMatrix3fv( this.addr, false, v );
 
+			copyArray( cache, v );
+
 		} else {
 
-			mat3array.set( v.elements );
+			if ( arraysEqual( cache, elements ) ) return;
+
+			mat3array.set( elements );
+
 			gl.uniformMatrix3fv( this.addr, false, mat3array );
 
+			copyArray( cache, elements );
+
 		}
 
 	}
 
 	function setValue4fm( gl, v ) {
 
-		if ( v.elements === undefined ) {
+		var cache = this.cache;
+		var elements = v.elements;
+
+		if ( elements === undefined ) {
+
+			if ( arraysEqual( cache, v ) ) return;
 
 			gl.uniformMatrix4fv( this.addr, false, v );
 
+			copyArray( cache, v );
+
 		} else {
 
-			mat4array.set( v.elements );
+			if ( arraysEqual( cache, elements ) ) return;
+
+			mat4array.set( elements );
+
 			gl.uniformMatrix4fv( this.addr, false, mat4array );
 
+			copyArray( cache, elements );
+
 		}
 
 	}
@@ -15692,7 +15815,14 @@
 	function setValueT1( gl, v, renderer ) {
 
 		var unit = renderer.allocTextureUnit();
-		gl.uniform1i( this.addr, unit );
+
+		if ( this.cache[ 0 ] !== unit ) {
+
+			gl.uniform1i( this.addr, unit );
+			this.cache[ 0 ] = unit;
+
+		}
+
 		renderer.setTexture2D( v || emptyTexture, unit );
 
 	}
@@ -15700,7 +15830,14 @@
 	function setValueT6( gl, v, renderer ) {
 
 		var unit = renderer.allocTextureUnit();
-		gl.uniform1i( this.addr, unit );
+
+		if ( this.cache[ 0 ] !== unit ) {
+
+			gl.uniform1i( this.addr, unit );
+			this.cache[ 0 ] = unit;
+
+		}
+
 		renderer.setTextureCube( v || emptyCubeTexture, unit );
 
 	}
@@ -15709,20 +15846,32 @@
 
 	function setValue2iv( gl, v ) {
 
+		if ( arraysEqual( this.cache, v ) ) return;
+
 		gl.uniform2iv( this.addr, v );
 
+		copyArray( this.cache, v );
+
 	}
 
 	function setValue3iv( gl, v ) {
 
+		if ( arraysEqual( this.cache, v ) ) return;
+
 		gl.uniform3iv( this.addr, v );
 
+		copyArray( this.cache, v );
+
 	}
 
 	function setValue4iv( gl, v ) {
 
+		if ( arraysEqual( this.cache, v ) ) return;
+
 		gl.uniform4iv( this.addr, v );
 
+		copyArray( this.cache, v );
+
 	}
 
 	// Helper to pick the right setter for the singular case
@@ -15870,6 +16019,7 @@
 
 		this.id = id;
 		this.addr = addr;
+		this.cache = [];
 		this.setValue = getSingularSetter( activeInfo.type );
 
 		// this.path = activeInfo.name; // DEBUG
@@ -15941,7 +16091,7 @@
 		// reset RegExp object, because of the early exit of a previous run
 		RePathPart.lastIndex = 0;
 
-		for ( ; ; ) {
+		while ( true ) {
 
 			var match = RePathPart.exec( path ),
 				matchEnd = RePathPart.lastIndex,
@@ -21139,6 +21289,14 @@
 
 		};
 
+		this.isPresenting = isPresenting;
+
+		this.requestAnimationFrame = function ( callback ) {
+
+			device.requestAnimationFrame( callback );
+
+		};
+
 		this.submitFrame = function () {
 
 			if ( isPresenting() ) device.submitFrame();
@@ -21157,6 +21315,142 @@
 
 	}
 
+	/**
+	 * @author mrdoob / http://mrdoob.com/
+	 */
+
+	function WebXRManager( gl ) {
+
+		var device = null;
+		var session = null;
+
+		var frameOfRef = null;
+		var isExclusive = false;
+
+		var pose = null;
+
+		function isPresenting() {
+
+			return session !== null && frameOfRef !== null;
+
+		}
+
+		//
+
+		var cameraL = new PerspectiveCamera();
+		cameraL.layers.enable( 1 );
+		cameraL.viewport = new Vector4();
+
+		var cameraR = new PerspectiveCamera();
+		cameraR.layers.enable( 2 );
+		cameraR.viewport = new Vector4();
+
+		var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
+		cameraVR.layers.enable( 1 );
+		cameraVR.layers.enable( 2 );
+
+		//
+
+		this.enabled = false;
+
+		this.getDevice = function () {
+
+			return device;
+
+		};
+
+		this.setDevice = function ( value ) {
+
+			if ( value !== undefined ) device = value;
+
+			gl.setCompatibleXRDevice( value );
+
+		};
+
+		this.setSession = function ( value ) {
+
+			session = value;
+
+			if ( session !== null ) {
+
+				session.baseLayer = new XRWebGLLayer( session, gl );
+				session.requestFrameOfReference( 'stage' ).then( function ( value ) {
+
+					frameOfRef = value;
+					isExclusive = session.exclusive;
+
+				} );
+
+			}
+
+		};
+
+		this.getCamera = function ( camera ) {
+
+			return isPresenting() ? cameraVR : camera;
+
+		};
+
+		this.isPresenting = isPresenting;
+
+		this.requestAnimationFrame = function ( callback ) {
+
+			function onFrame( time, frame ) {
+
+				pose = frame.getDevicePose( frameOfRef );
+
+				var layer = session.baseLayer;
+				var views = frame.views;
+
+				for ( var i = 0; i < views.length; i ++ ) {
+
+					var view = views[ i ];
+					var viewport = layer.getViewport( view );
+					var viewMatrix = pose.getViewMatrix( view );
+
+					var camera = cameraVR.cameras[ i ];
+					camera.projectionMatrix.fromArray( view.projectionMatrix );
+					camera.matrixWorldInverse.fromArray( viewMatrix );
+					camera.matrixWorld.getInverse( camera.matrixWorldInverse );
+					camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
+
+					if ( i === 0 ) {
+
+						cameraVR.matrixWorld.copy( camera.matrixWorld );
+						cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );
+
+						// HACK (mrdoob)
+						// https://github.com/w3c/webvr/issues/203
+
+						cameraVR.projectionMatrix.copy( camera.projectionMatrix );
+
+					}
+
+				}
+
+				gl.bindFramebuffer( gl.FRAMEBUFFER, session.baseLayer.framebuffer );
+
+				callback();
+
+			}
+
+			session.requestAnimationFrame( onFrame );
+
+		};
+
+		// DEPRECATED
+
+		this.getStandingMatrix = function () {
+
+			console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
+			return new THREE.Matrix4();
+
+		};
+
+		this.submitFrame = function () {};
+
+	}
+
 	/**
 	 * @author supereggbert / http://www.paulbrunt.co.uk/
 	 * @author mrdoob / http://mrdoob.com/
@@ -21402,7 +21696,7 @@
 
 		// vr
 
-		var vr = new WebVRManager( _this );
+		var vr = ( 'xr' in navigator ) ? new WebXRManager( _gl ) : new WebVRManager( _this );
 
 		this.vr = vr;
 
@@ -21467,9 +21761,7 @@
 
 		this.setSize = function ( width, height, updateStyle ) {
 
-			var device = vr.getDevice();
-
-			if ( device && device.isPresenting ) {
+			if ( vr.isPresenting() ) {
 
 				console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
 				return;
@@ -22151,11 +22443,9 @@
 
 		function requestAnimationLoopFrame() {
 
-			var device = vr.getDevice();
-
-			if ( device && device.isPresenting ) {
+			if ( vr.isPresenting() ) {
 
-				device.requestAnimationFrame( animationLoop );
+				vr.requestAnimationFrame( animationLoop );
 
 			} else {
 
@@ -22502,14 +22792,22 @@
 
 						if ( object.layers.test( camera2.layers ) ) {
 
-							var bounds = camera2.bounds;
+							if ( 'viewport' in camera2 ) { // XR
+
+								state.viewport( _currentViewport.copy( camera2.viewport ) );
+
+							} else {
 
-							var x = bounds.x * _width;
-							var y = bounds.y * _height;
-							var width = bounds.z * _width;
-							var height = bounds.w * _height;
+								var bounds = camera2.bounds;
 
-							state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+								var x = bounds.x * _width;
+								var y = bounds.y * _height;
+								var width = bounds.z * _width;
+								var height = bounds.w * _height;
+
+								state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+
+							}
 
 							renderObject( object, scene, camera2, geometry, material, group );
 
@@ -35222,7 +35520,8 @@
 
 				'name': clip.name,
 				'duration': clip.duration,
-				'tracks': tracks
+				'tracks': tracks,
+				'uuid': clip.uuid
 
 			};
 
@@ -37105,7 +37404,11 @@
 
 			for ( var i = 0; i < json.length; i ++ ) {
 
-				var clip = AnimationClip.parse( json[ i ] );
+				var data = json[ i ];
+
+				var clip = AnimationClip.parse( data );
+
+				if ( data.uuid !== undefined ) clip.uuid = data.uuid;
 
 				animations.push( clip );
 
@@ -38553,6 +38856,17 @@
 
 		},
 
+		setMediaElementSource: function ( mediaElement ) {
+
+			this.hasPlaybackControl = false;
+			this.sourceType = 'mediaNode';
+			this.source = this.context.createMediaElementSource( mediaElement );
+			this.connect();
+
+			return this;
+
+		},
+
 		setBuffer: function ( audioBuffer ) {
 
 			this.buffer = audioBuffer;

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


+ 375 - 61
build/three.module.js

@@ -4576,6 +4576,8 @@ function WebGLRenderTarget( width, height, options ) {
 
 	this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
 
+	this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true;
+
 	this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
 	this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
 	this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;
@@ -5235,41 +5237,30 @@ Object.assign( Box3.prototype, {
 
 	},
 
-	applyMatrix4: function () {
-
-		var points = [
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3()
-		];
-
-		return function applyMatrix4( matrix ) {
+	applyMatrix4: function ( matrix ) {
 
-			// transform of empty box is an empty box.
-			if ( this.isEmpty() ) return this;
+		// transform of empty box is an empty box.
+		if ( this.isEmpty( ) ) return this;
 
-			// NOTE: I am using a binary pattern to specify all 2^3 combinations below
-			points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
-			points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
-			points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
-			points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
-			points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
-			points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
-			points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
-			points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );	// 111
+		var m = matrix.elements;
 
-			this.setFromPoints( points );
+		var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x;
+		var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x;
+		var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y;
+		var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y;
+		var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z;
+		var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z;
 
-			return this;
+		this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ];
+		this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ];
+		this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ];
+		this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ];
+		this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ];
+		this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ];
 
-		};
+		return this;
 
-	}(),
+	},
 
 	translate: function ( offset ) {
 
@@ -15518,6 +15509,7 @@ var arrayCacheI32 = [];
 
 var mat4array = new Float32Array( 16 );
 var mat3array = new Float32Array( 9 );
+var mat2array = new Float32Array( 4 );
 
 // Flattening for arrays of vectors and matrices
 
@@ -15556,6 +15548,30 @@ function flatten( array, nBlocks, blockSize ) {
 
 }
 
+function arraysEqual( a, b ) {
+
+	if ( a.length !== b.length ) return false;
+
+	for ( var i = 0, l = a.length; i < l; i ++ ) {
+
+		if ( a[ i ] !== b[ i ] ) return false;
+
+	}
+
+	return true;
+
+}
+
+function copyArray( a, b ) {
+
+	for ( var i = 0, l = b.length; i < l; i ++ ) {
+
+		a[ i ] = b[ i ];
+
+	}
+
+}
+
 // Texture unit allocation
 
 function allocTexUnits( renderer, n ) {
@@ -15585,27 +15601,52 @@ function allocTexUnits( renderer, n ) {
 
 function setValue1f( gl, v ) {
 
+	var cache = this.cache;
+
+	if ( cache[ 0 ] === v ) return;
+
 	gl.uniform1f( this.addr, v );
 
+	cache[ 0 ] = v;
+
 }
 
 function setValue1i( gl, v ) {
 
+	var cache = this.cache;
+
+	if ( cache[ 0 ] === v ) return;
+
 	gl.uniform1i( this.addr, v );
 
+	cache[ 0 ] = v;
+
 }
 
 // Single float vector (from flat array or THREE.VectorN)
 
 function setValue2fv( gl, v ) {
 
-	if ( v.x === undefined ) {
+	var cache = this.cache;
 
-		gl.uniform2fv( this.addr, v );
+	if ( v.x !== undefined ) {
+
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
+
+			gl.uniform2f( this.addr, v.x, v.y );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+
+		}
 
 	} else {
 
-		gl.uniform2f( this.addr, v.x, v.y );
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniform2fv( this.addr, v );
+
+		copyArray( cache, v );
 
 	}
 
@@ -15613,31 +15654,68 @@ function setValue2fv( gl, v ) {
 
 function setValue3fv( gl, v ) {
 
+	var cache = this.cache;
+
 	if ( v.x !== undefined ) {
 
-		gl.uniform3f( this.addr, v.x, v.y, v.z );
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
+
+			gl.uniform3f( this.addr, v.x, v.y, v.z );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+			cache[ 2 ] = v.z;
+
+		}
 
 	} else if ( v.r !== undefined ) {
 
-		gl.uniform3f( this.addr, v.r, v.g, v.b );
+		if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
+
+			gl.uniform3f( this.addr, v.r, v.g, v.b );
+
+			cache[ 0 ] = v.r;
+			cache[ 1 ] = v.g;
+			cache[ 2 ] = v.b;
+
+		}
 
 	} else {
 
+		if ( arraysEqual( cache, v ) ) return;
+
 		gl.uniform3fv( this.addr, v );
 
+		copyArray( cache, v );
+
 	}
 
 }
 
 function setValue4fv( gl, v ) {
 
-	if ( v.x === undefined ) {
+	var cache = this.cache;
 
-		gl.uniform4fv( this.addr, v );
+	if ( v.x !== undefined ) {
+
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
+
+			gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+			cache[ 2 ] = v.z;
+			cache[ 3 ] = v.w;
+
+		}
 
 	} else {
 
-		 gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniform4fv( this.addr, v );
+
+		copyArray( cache, v );
 
 	}
 
@@ -15647,36 +15725,81 @@ function setValue4fv( gl, v ) {
 
 function setValue2fm( gl, v ) {
 
-	gl.uniformMatrix2fv( this.addr, false, v.elements || v );
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniformMatrix2fv( this.addr, false, v );
+
+		copyArray( cache, v );
+
+	} else {
+
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat2array.set( elements );
+
+		gl.uniformMatrix2fv( this.addr, false, mat2array );
+
+		copyArray( cache, elements );
+
+	}
 
 }
 
 function setValue3fm( gl, v ) {
 
-	if ( v.elements === undefined ) {
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
 
 		gl.uniformMatrix3fv( this.addr, false, v );
 
+		copyArray( cache, v );
+
 	} else {
 
-		mat3array.set( v.elements );
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat3array.set( elements );
+
 		gl.uniformMatrix3fv( this.addr, false, mat3array );
 
+		copyArray( cache, elements );
+
 	}
 
 }
 
 function setValue4fm( gl, v ) {
 
-	if ( v.elements === undefined ) {
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
 
 		gl.uniformMatrix4fv( this.addr, false, v );
 
+		copyArray( cache, v );
+
 	} else {
 
-		mat4array.set( v.elements );
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat4array.set( elements );
+
 		gl.uniformMatrix4fv( this.addr, false, mat4array );
 
+		copyArray( cache, elements );
+
 	}
 
 }
@@ -15686,7 +15809,14 @@ function setValue4fm( gl, v ) {
 function setValueT1( gl, v, renderer ) {
 
 	var unit = renderer.allocTextureUnit();
-	gl.uniform1i( this.addr, unit );
+
+	if ( this.cache[ 0 ] !== unit ) {
+
+		gl.uniform1i( this.addr, unit );
+		this.cache[ 0 ] = unit;
+
+	}
+
 	renderer.setTexture2D( v || emptyTexture, unit );
 
 }
@@ -15694,7 +15824,14 @@ function setValueT1( gl, v, renderer ) {
 function setValueT6( gl, v, renderer ) {
 
 	var unit = renderer.allocTextureUnit();
-	gl.uniform1i( this.addr, unit );
+
+	if ( this.cache[ 0 ] !== unit ) {
+
+		gl.uniform1i( this.addr, unit );
+		this.cache[ 0 ] = unit;
+
+	}
+
 	renderer.setTextureCube( v || emptyCubeTexture, unit );
 
 }
@@ -15703,20 +15840,32 @@ function setValueT6( gl, v, renderer ) {
 
 function setValue2iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform2iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 function setValue3iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform3iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 function setValue4iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform4iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 // Helper to pick the right setter for the singular case
@@ -15864,6 +16013,7 @@ function SingleUniform( id, activeInfo, addr ) {
 
 	this.id = id;
 	this.addr = addr;
+	this.cache = [];
 	this.setValue = getSingularSetter( activeInfo.type );
 
 	// this.path = activeInfo.name; // DEBUG
@@ -15935,7 +16085,7 @@ function parseUniform( activeInfo, addr, container ) {
 	// reset RegExp object, because of the early exit of a previous run
 	RePathPart.lastIndex = 0;
 
-	for ( ; ; ) {
+	while ( true ) {
 
 		var match = RePathPart.exec( path ),
 			matchEnd = RePathPart.lastIndex,
@@ -21133,6 +21283,14 @@ function WebVRManager( renderer ) {
 
 	};
 
+	this.isPresenting = isPresenting;
+
+	this.requestAnimationFrame = function ( callback ) {
+
+		device.requestAnimationFrame( callback );
+
+	};
+
 	this.submitFrame = function () {
 
 		if ( isPresenting() ) device.submitFrame();
@@ -21151,6 +21309,142 @@ function WebVRManager( renderer ) {
 
 }
 
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+function WebXRManager( gl ) {
+
+	var device = null;
+	var session = null;
+
+	var frameOfRef = null;
+	var isExclusive = false;
+
+	var pose = null;
+
+	function isPresenting() {
+
+		return session !== null && frameOfRef !== null;
+
+	}
+
+	//
+
+	var cameraL = new PerspectiveCamera();
+	cameraL.layers.enable( 1 );
+	cameraL.viewport = new Vector4();
+
+	var cameraR = new PerspectiveCamera();
+	cameraR.layers.enable( 2 );
+	cameraR.viewport = new Vector4();
+
+	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
+	cameraVR.layers.enable( 1 );
+	cameraVR.layers.enable( 2 );
+
+	//
+
+	this.enabled = false;
+
+	this.getDevice = function () {
+
+		return device;
+
+	};
+
+	this.setDevice = function ( value ) {
+
+		if ( value !== undefined ) device = value;
+
+		gl.setCompatibleXRDevice( value );
+
+	};
+
+	this.setSession = function ( value ) {
+
+		session = value;
+
+		if ( session !== null ) {
+
+			session.baseLayer = new XRWebGLLayer( session, gl );
+			session.requestFrameOfReference( 'stage' ).then( function ( value ) {
+
+				frameOfRef = value;
+				isExclusive = session.exclusive;
+
+			} );
+
+		}
+
+	};
+
+	this.getCamera = function ( camera ) {
+
+		return isPresenting() ? cameraVR : camera;
+
+	};
+
+	this.isPresenting = isPresenting;
+
+	this.requestAnimationFrame = function ( callback ) {
+
+		function onFrame( time, frame ) {
+
+			pose = frame.getDevicePose( frameOfRef );
+
+			var layer = session.baseLayer;
+			var views = frame.views;
+
+			for ( var i = 0; i < views.length; i ++ ) {
+
+				var view = views[ i ];
+				var viewport = layer.getViewport( view );
+				var viewMatrix = pose.getViewMatrix( view );
+
+				var camera = cameraVR.cameras[ i ];
+				camera.projectionMatrix.fromArray( view.projectionMatrix );
+				camera.matrixWorldInverse.fromArray( viewMatrix );
+				camera.matrixWorld.getInverse( camera.matrixWorldInverse );
+				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
+
+				if ( i === 0 ) {
+
+					cameraVR.matrixWorld.copy( camera.matrixWorld );
+					cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );
+
+					// HACK (mrdoob)
+					// https://github.com/w3c/webvr/issues/203
+
+					cameraVR.projectionMatrix.copy( camera.projectionMatrix );
+
+				}
+
+			}
+
+			gl.bindFramebuffer( gl.FRAMEBUFFER, session.baseLayer.framebuffer );
+
+			callback();
+
+		}
+
+		session.requestAnimationFrame( onFrame );
+
+	};
+
+	// DEPRECATED
+
+	this.getStandingMatrix = function () {
+
+		console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
+		return new THREE.Matrix4();
+
+	};
+
+	this.submitFrame = function () {};
+
+}
+
 /**
  * @author supereggbert / http://www.paulbrunt.co.uk/
  * @author mrdoob / http://mrdoob.com/
@@ -21396,7 +21690,7 @@ function WebGLRenderer( parameters ) {
 
 	// vr
 
-	var vr = new WebVRManager( _this );
+	var vr = ( 'xr' in navigator ) ? new WebXRManager( _gl ) : new WebVRManager( _this );
 
 	this.vr = vr;
 
@@ -21461,9 +21755,7 @@ function WebGLRenderer( parameters ) {
 
 	this.setSize = function ( width, height, updateStyle ) {
 
-		var device = vr.getDevice();
-
-		if ( device && device.isPresenting ) {
+		if ( vr.isPresenting() ) {
 
 			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
 			return;
@@ -22145,11 +22437,9 @@ function WebGLRenderer( parameters ) {
 
 	function requestAnimationLoopFrame() {
 
-		var device = vr.getDevice();
-
-		if ( device && device.isPresenting ) {
+		if ( vr.isPresenting() ) {
 
-			device.requestAnimationFrame( animationLoop );
+			vr.requestAnimationFrame( animationLoop );
 
 		} else {
 
@@ -22496,14 +22786,22 @@ function WebGLRenderer( parameters ) {
 
 					if ( object.layers.test( camera2.layers ) ) {
 
-						var bounds = camera2.bounds;
+						if ( 'viewport' in camera2 ) { // XR
+
+							state.viewport( _currentViewport.copy( camera2.viewport ) );
+
+						} else {
 
-						var x = bounds.x * _width;
-						var y = bounds.y * _height;
-						var width = bounds.z * _width;
-						var height = bounds.w * _height;
+							var bounds = camera2.bounds;
 
-						state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+							var x = bounds.x * _width;
+							var y = bounds.y * _height;
+							var width = bounds.z * _width;
+							var height = bounds.w * _height;
+
+							state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+
+						}
 
 						renderObject( object, scene, camera2, geometry, material, group );
 
@@ -35216,7 +35514,8 @@ Object.assign( AnimationClip, {
 
 			'name': clip.name,
 			'duration': clip.duration,
-			'tracks': tracks
+			'tracks': tracks,
+			'uuid': clip.uuid
 
 		};
 
@@ -37099,7 +37398,11 @@ Object.assign( ObjectLoader.prototype, {
 
 		for ( var i = 0; i < json.length; i ++ ) {
 
-			var clip = AnimationClip.parse( json[ i ] );
+			var data = json[ i ];
+
+			var clip = AnimationClip.parse( data );
+
+			if ( data.uuid !== undefined ) clip.uuid = data.uuid;
 
 			animations.push( clip );
 
@@ -38547,6 +38850,17 @@ Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {
 
 	},
 
+	setMediaElementSource: function ( mediaElement ) {
+
+		this.hasPlaybackControl = false;
+		this.sourceType = 'mediaNode';
+		this.source = this.context.createMediaElementSource( mediaElement );
+		this.connect();
+
+		return this;
+
+	},
+
 	setBuffer: function ( audioBuffer ) {
 
 		this.buffer = audioBuffer;

+ 102 - 0
docs/examples/animations/CCDIKSolver.html

@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc"> A solver for IK with <a href="https://sites.google.com/site/auraliusproject/ccd-algorithm"><em>CCD Algorithm</em></a>. <br /><br />
+		[name] solves Inverse Kinematics Problem with CCD Algorithm.
+		[name] is designed to work with [page:SkinnedMesh] loaded by [page:MMDLoader] but also can be used for generic [page:SkinnedMesh].
+		</p>
+
+		<h2>Example</h2>
+
+		<code>
+		var ikSolver;
+
+		// Load MMD resources and instantiate CCDIKSolver
+		new THREE.MMDLoader().load(
+			'models/mmd/miku.pmd',
+			function ( mesh ) {
+
+				ikSolver = new CCDIKSolver( mesh, mesh.geometry.iks );
+				scene.add( mesh );
+
+			}
+		);
+
+		function render() {
+
+			animate(); // update bones
+			if ( ikSolver !== undefined ) ikSolver.update();
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_pose]<br />
+		[example:webgl_loader_mmd_audio]<br />
+
+		<br />
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:SkinnedMesh mesh], [param:Array iks] )</h3>
+		<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh] for which [name] solves IK problem.<br />
+		[page:Array iks] — An array of [page:Object] specifying IK parameter. target, effector, and link-index are index integers in .skeleton.bones.
+		The bones relation should be "links[ n ], links[ n - 1 ], ..., links[ 0 ], effector" in order from parent to child.<br />
+		<ul>
+			<li>[page:Integer target] — Target bone.</li>
+			<li>[page:Integer effector] — Effector bone.</li>
+			<li>[page:Array links] — An array of [page: Object] specifying link bones.
+			<ul>
+				<li>[page:Integer index] — Link bone.</li>
+				<li>[page:Vector3 limitation] — (optional) Rotation axis. Default is undefined.</li>
+				<li>[page:Vector3 rotationMin] — (optional) Rotation minimum limit. Default is undefined.</li>
+				<li>[page:Vector3 rotationMax] — (optional) Rotation maximum limit. Default is undefined.</li>
+				<li>[page:Boolean enabled] — (optional) Default is true.</li>
+			</ul>
+			</li>
+			<li>[page:Integer iteration] — (optional) Iteration number of calculation. Smaller is faster but less precise. Default is 1.</li>
+			<li>[page:Number minAngle] — (optional) Minimum rotation angle in a step. Default is undefined.</li>
+			<li>[page:Number maxAngle] — (optional) Maximum rotation angle in a step. Default is undefined.</li>
+		</ul>
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:Array iks]</h3>
+		<p>An array of IK parameter passed to the constructor.</p>
+
+		<h3>[property:SkinnedMesh mesh]</h3>
+		<p>[page:SkinnedMesh] passed to the constructor.</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:CCDIKHelper createHelper]()</h3>
+		<p>
+		Return [page:CCDIKHelper]. You can visualize IK bones by adding the helper to scene.
+		</p>
+
+		<h3>[method:CCDIKSolver update]()</h3>
+		<p>
+		Update bones quaternion by solving CCD algorithm.
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/animation/CCDIKSolver.js examples/js/animation/CCDIKSolver.js]
+	</body>
+</html>

+ 169 - 0
docs/examples/animations/MMDAnimationHelper.html

@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc"> A animation helper for <a href="http://www.geocities.jp/higuchuu4/index_e.htm"><em>MMD</em></a> resources. <br /><br />
+		[name] handles animation of MMD assets loaded by [page:MMDLoader] with MMD special features as IK, Grant, and Physics.
+		It uses [page:CCDIKSolver] and [page:MMDPhysics] inside.
+		</p>
+
+		<h2>Example</h2>
+
+		<code>
+		// Instantiate a helper
+		var helper = new THREE.MMDAnimationHelper();
+
+		// Load MMD resources and add to helper
+		new THREE.MMDLoader().loadWithAnimation(
+			'models/mmd/miku.pmd',
+			'models/mmd/dance.vmd',
+			function ( mmd ) {
+
+				helper.add( mmd.mesh, {
+					animation: mmd.animation,
+					physics: true
+				} );
+
+				scene.add( mmd.mesh );
+
+				new THREE.AudioLoader().load(
+					'audios/mmd/song.mp3',
+					function ( buffer ) {
+
+						var listener = new THREE.AudioListener();
+						var audio = new THREE.Audio( listener )
+							.setBuffer( buffer );
+
+						listener.position.z = 1;
+
+						scene.add( audio );
+						scene.add( listener );
+
+					}
+
+				);
+
+			}
+		);
+
+		function render() {
+
+			helper.update( clock.getDelta() );
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_pose]<br />
+		[example:webgl_loader_mmd_audio]<br />
+
+		<br />
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:Object params] )</h3>
+		<p>
+		[page:Object params] — (optional)<br />
+		<ul>
+			<li> [page:Boolean sync] - Whether animation durations of added objects are synched. Default is true.</li>
+			<li> [page:Number afterglow] - Default is 0.0.</li>
+			<li> [page:Boolean resetPhysicsOnLoop] - Default is true.</li>
+		</ul>
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:Audio audio]</h3>
+		<p>An [page:Audio] added to helper.</p>
+
+		<h3>[property:Camera camera]</h3>
+		<p>An [page:Camera] added to helper.</p>
+
+		<h3>[property:Array meshes]</h3>
+		<p>An array of [page:SkinnedMesh] added to helper.</p>
+
+		<h3>[property:WeakMap objects]</h3>
+		<p>A [page:WeakMap] which holds animation stuffs used in helper for objects added to helper. For example, you can access [page:AnimationMixer] for an added [page:SkinnedMesh] with "helper.objects.get( mesh ).mixer"</p>
+
+		<h3>[property:function onBeforePhysics]</h3>
+		<p>An optional callback that is executed immediately before the physicis calculation for an [page:SkinnedMesh]. This function is called with the [page:SkinnedMesh].</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:MMDAnimationHelper add]( [param:Object3D object], [param:Object params] )</h3>
+		<p>
+		[page:Object3D object] — [page:SkinnedMesh], [page:Camera], or [page:Audio]<br />
+		[page:Object params] — (optional)<br />
+		<ul>
+			<li>[page:AnimationClip animation] - an [page:AnimationClip] or an array of [page:AnimationClip] set to object. Only for [page:SkinnedMesh] and [page:Camera]. Default is undefined.</li>
+			<li>[page:Boolean physics] - Only for [page:SkinnedMesh]. A flag whether turn on physics. Default is true.</li>
+			<li>[page:Integer warmup] - Only for [page:SkinnedMesh] and physics is true. Physics parameter. Default is 60.</li>
+			<li>[page:Number unitStep] - Only for [page:SkinnedMesh] and physics is true. Physics parameter. Default is 1 / 65.</li>
+			<li>[page:Integer maxStepNum] - Only for [page:SkinnedMesh] and physics is true. Physics parameter. Default is 3.</li>
+			<li>[page:Vector3 gravity] - Only for [page:SkinnedMesh] and physics is true. Physics parameter. Default is ( 0, - 9.8 * 10, 0 ).</li>
+			<li>[page:Number delayTime] - Only for [page:Audio]. Default is 0.0.</li>
+		</ul>
+		</p>
+		<p>
+		Add an [page:SkinnedMesh], [page:Camera], or [page:Audio] to helper and setup animation. The anmation durations of added objects are synched.
+		If camera/audio has already been added, it'll be replaced with a new one.
+		</p>
+
+		<h3>[method:MMDAnimationHelper enable]( [param:string key], [param:Boolean enabled] )</h3>
+		<p>
+		[page:string key] — Allowed strings are 'animation', 'ik', 'grant', 'physics', and 'cameraAnimation'.<br />
+		[page:Boolean enabled] — true is enable, false is disable<br />
+		</p>
+		<p>
+		Enable/Disable an animation feature
+		</p>
+
+		<h3>[method:MMDAnimationHelper pose]( [param:SkinnedMesh mesh], [param:Object vpd], [param:Object params] )</h3>
+		<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh] which changes the posing. It doesn't need to be added to helper.<br />
+		[page:Object vpd] — VPD content obtained by [page:MMDLoader].loadVPD<br />
+		[page:Object params] — (optional)<br />
+		<ul>
+			<li>[page:Boolean resetPose] - Default is true.</li>
+			<li>[page:Boolean ik] - Default is true.</li>
+			<li>[page:Boolean grant] - Default is true.</li>
+		</ul>
+		</p>
+		<p>
+		Changes the posing of [page:SkinnedMesh] as VPD content specifies.
+		</p>
+
+		<h3>[method:MMDAnimationHelper remove]( [param:Object3D object] )</h3>
+		<p>
+		[page:Object3D object] — [page:SkinnedMesh], [page:Camera], or [page:Audio]<br />
+		</p>
+		<p>
+		Remove an [page:SkinnedMesh], [page:Camera], or [page:Audio] from helper.
+		</p>
+
+		<h3>[method:MMDAnimationHelper update]( [param:Nummber delta] )</h3>
+		<p>
+		[page:Number delta] — number in second<br />
+		</p>
+		<p>
+		Advance mixer time and update the animations of objects added to helper
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/animation/MMDAnimationHelper.js examples/js/animation/MMDAnimationHelper.js]
+	</body>
+</html>

+ 112 - 0
docs/examples/animations/MMDPhysics.html

@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		<h1>[name]</h1>
+
+		<p class="desc"> A Physics handler for <a href="http://www.geocities.jp/higuchuu4/index_e.htm"><em>MMD</em></a> resources. <br /><br />
+		[name] calculates Physics for model loaded by [page:MMDLoader] with <a href="https://github.com/kripken/ammo.js/">ammo.js</a> (Bullet-based JavaScript Physics engine).
+		</p>
+
+		<h2>Example</h2>
+
+		<code>
+		var physics;
+
+		// Load MMD resources and instantiate MMDPhysics
+		new THREE.MMDLoader().load(
+			'models/mmd/miku.pmd',
+			function ( mesh ) {
+
+				physics = new THREE.MMDPhysics( mesh )
+				scene.add( mesh );
+
+			}
+		);
+
+		function render() {
+
+			var delta = clock.getDelta();
+			animate( delta );  // update bones
+			if ( physics !== undefined ) physics.update( delta );
+			renderer.render( scene, camera );
+
+		}
+		</code>
+
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_audio]<br />
+
+		<br />
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:SkinnedMesh mesh], [param:Array rigidBodyParams], [param:Array constraintParams], [param:Object params] )</h3>
+		<p>
+		[page:SkinnedMesh mesh] — [page:SkinnedMesh] for which [name] calculates Physics.<br />
+		[page:Array rigidBodyParams] — An array of [page:Object] specifying Rigid Body parameters.<br />
+		[page:Array constraintParams] — (optional) An array of [page:Object] specifying Constraint parameters.<br />
+		[page:Object params] — (optional)<br />
+		<ul>
+			<li>[page:Number unitStep] - Default is 1 / 65.</li>
+			<li>[page:Integer maxStepNum] - Default is 3.</li>
+			<li>[page:Vector3 gravity] - Default is ( 0, - 9.8 * 10, 0 )</li>
+		</ul>
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:Array mesh]</h3>
+		<p>[page:SkinnedMesh] passed to the constructor.</p>
+
+		<h2>Methods</h2>
+
+		<h3>[method:MMDPhysicsHelper createHelper]()</h3>
+		<p>
+		Return [page:MMDPhysicsHelper]. You can visualize Rigid bodies by adding the helper to scene.
+		</p>
+
+		<h3>[method:CCDIKSolver reset]()</h3>
+		<p>
+		Resets Rigid bodies transorm to current bone's.
+		</p>
+
+		<h3>[method:CCDIKSolver setGravity]( [param:Vector3 gravity] )</h3>
+		<p>
+		[page:Vector3 gravity] — Direction and volume of gravity.
+		</p>
+		<p>
+		Set gravity.
+		</p>
+
+		<h3>[method:CCDIKSolver update]( [param:Number delta] )</h3>
+		<p>
+		[page:Number delta] — Time in second.
+		</p>
+		<p>
+		Advance Physics calculation and updates bones.
+		</p>
+
+		<h3>[method:CCDIKSolver warmup]( [param:Integer cycles] )</h3>
+		<p>
+		[page:Number delta] — Time in second.
+		</p>
+		<p>
+		Warm up Rigid bodies. Calculates cycles steps.
+		</p>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/animation/MMDPhysics.js examples/js/animation/MMDPhysics.js]
+	</body>
+</html>

+ 22 - 0
docs/examples/loaders/GLTFLoader.html

@@ -84,6 +84,28 @@
 		<a href="https://github.com/stefanpenner/es6-promise">include a polyfill</a>
 		providing a Promise replacement.</p>
 
+		<h2>Custom extensions</h2>
+
+		<p>
+			Metadata from unknown extensions is preserved as “.userData.gltfExtensions” on Object3D, Scene, and Material instances,
+			or attached to the response “gltf” object. Example:
+		</p>
+
+		<code>
+		loader.load('foo.gltf', function ( gltf ) {
+
+			var scene = gltf.scene;
+
+			var mesh = scene.children[ 3 ];
+
+			var fooExtension = mesh.userData.gltfExtensions.EXT_foo;
+
+			gltf.parser.getDependency( 'bufferView', fooExtension.bufferView )
+				.then( function ( fooBuffer ) { ... } );
+
+		} );
+		</code>
+
 		<br>
 		<hr>
 

+ 118 - 0
docs/examples/loaders/MMDLoader.html

@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:Loader] &rarr;
+		<h1>[name]</h1>
+
+		<p class="desc"> A loader for <a href="http://www.geocities.jp/higuchuu4/index_e.htm"><em>MMD</em></a> resources. <br /><br />
+		[name] creates Three.js Objects from MMD resources as PMD, PMX, VMD, and VPD files.
+		You can easily handle MMD special features, as IK, Grant, and Physics, with [page:MMDAnimationHelper].<br /><br />
+
+		If you want raw content of MMD resources, use .loadPMD/PMX/VMD/VPD methods.
+
+		<h2>Example</h2>
+
+		<code>
+		// Instantiate a loader
+		var loader = new THREE.MMDLoader();
+
+		// Load a MMD model
+		loader.load(
+			// path to PMD/PMX file
+			'models/mmd/miku.pmd',
+			// called when the resource is loaded
+			function ( mesh ) {
+
+				scene.add( mesh );
+
+			},
+			// called when loading is in progresses
+			function ( xhr ) {
+
+				console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
+
+			},
+			// called when loading has errors
+			function ( error ) {
+
+				console.log( 'An error happened' );
+
+			}
+		);
+		</code>
+
+		[example:webgl_loader_mmd]<br />
+		[example:webgl_loader_mmd_pose]<br />
+		[example:webgl_loader_mmd_audio]<br />
+
+		<br />
+		<hr>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [param:LoadingManager manager] )</h3>
+		<p>
+		[page:LoadingManager manager] — The [page:LoadingManager loadingManager] for the loader to use. Default is [page:LoadingManager THREE.DefaultLoadingManager].
+		</p>
+		<p>
+		Creates a new [name].
+		</p>
+
+		<h2>Properties</h2>
+
+
+		<h2>Methods</h2>
+
+		<h3>[method:null load]( [param:String url], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String url] — A string containing the path/URL of the <em>.pmd</em> or <em>.pmx</em> file.<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
+		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
+		</p>
+		<p>
+		Begin loading PMD/PMX model file from url and fire the callback function with the parsed [page:SkinnedMesh] containing [page:BufferGeometry] and an array of [page:MeshToonMaterial].
+		</p>
+
+		<h3>[method:null loadAnimation]( [param:String url], [param:Object3D object], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String url] — A string or an array of string containing the path/URL of the <em>.vmd</em> file(s).If two or more files are specified, they'll be merged.<br />
+		[page:Object3D object] — [page:SkinnedMesh] or [page:Camera]. Clip and its tacks will be fitting to this object.<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
+		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
+		</p>
+		<p>
+		Begin loading VMD motion file(s) from url(s) and fire the callback function with the parsed [page:AnimatioinClip].
+		</p>
+
+		<h3>[method:null loadWithAnimation]( [param:String modelUrl], [param:String vmdUrl], [param:Function onLoad], [param:Function onProgress], [param:Function onError] )</h3>
+		<p>
+		[page:String modelUrl] — A string containing the path/URL of the <em>.pmd</em> or <em>.pmx</em> file.<br />
+		[page:String vmdUrl] — A string or an array of string containing the path/URL of the <em>.vmd</em> file(s).<br />
+		[page:Function onLoad] — A function to be called after the loading is successfully completed.<br />
+		[page:Function onProgress] — (optional) A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, that contains .[page:Integer total] and .[page:Integer loaded] bytes.<br />
+		[page:Function onError] — (optional) A function to be called if an error occurs during loading. The function receives error as an argument.<br />
+		</p>
+		<p>
+		Begin loading PMD/PMX model file and VMD motion file(s) from urls and fire the callback function with an [page:Object] containing parsed [page:SkinnedMesh] and [page:AnimationClip] fitting to the [page:SkinnedMesh].
+		</p>
+
+		<h3>[method:MMDLoader setCrossOrigin]( [param:String crossOrigin] )</h3>
+		<p>
+		[page:String crossOrigin] — The crossOrigin string to implement CORS for loading the url from a different domain that allows CORS.
+		</p>
+
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/MMDLoader.js examples/js/loaders/MMDLoader.js]
+	</body>
+</html>

+ 7 - 0
docs/list.js

@@ -337,6 +337,12 @@ var list = {
 
 	"Examples": {
 
+		"Animations": {
+			"CCDIKSolver": "examples/animations/CCDIKSolver",
+			"MMDAnimationHelper": "examples/animations/MMDAnimationHelper",
+			"MMDPhysics": "examples/animations/MMDPhysics"
+		},
+
 		"Controls": {
 			"OrbitControls": "examples/controls/OrbitControls"
 		},
@@ -350,6 +356,7 @@ var list = {
 		"Loaders": {
 			"BabylonLoader": "examples/loaders/BabylonLoader",
 			"GLTFLoader": "examples/loaders/GLTFLoader",
+			"MMDLoader": "examples/loaders/MMDLoader",
 			"MTLLoader": "examples/loaders/MTLLoader",
 			"OBJLoader": "examples/loaders/OBJLoader",
 			"OBJLoader2": "examples/loaders/OBJLoader2",

+ 6 - 0
editor/index.html

@@ -4,6 +4,12 @@
 		<title>three.js / editor</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 	</head>
 	<body ontouchstart="">
 		<link href="css/main.css" rel="stylesheet" />

+ 1 - 0
examples/files.js

@@ -284,6 +284,7 @@ var files = {
 		"webgl_buffergeometry_instancing_billboards",
 		"webgl_buffergeometry_instancing_dynamic",
 		"webgl_buffergeometry_instancing_interleaved_dynamic",
+		"webgl_buffergeometry_instancing_lambert",
 		"webgl_buffergeometry_lines",
 		"webgl_buffergeometry_lines_indexed",
 		"webgl_buffergeometry_points",

+ 27 - 1
examples/js/BufferGeometryUtils.js

@@ -188,7 +188,7 @@ THREE.BufferGeometryUtils = {
 	 * @param  {Array<THREE.BufferGeometry>} geometries
 	 * @return {THREE.BufferGeometry}
 	 */
-	mergeBufferGeometries: function ( geometries ) {
+	mergeBufferGeometries: function ( geometries, useGroups ) {
 
 		var isIndexed = geometries[ 0 ].index !== null;
 
@@ -200,6 +200,8 @@ THREE.BufferGeometryUtils = {
 
 		var mergedGeometry = new THREE.BufferGeometry();
 
+		var offset = 0;
+
 		for ( var i = 0; i < geometries.length; ++ i ) {
 
 			var geometry = geometries[ i ];
@@ -242,6 +244,30 @@ THREE.BufferGeometryUtils = {
 
 			}
 
+			if ( useGroups ) {
+
+				var count;
+
+				if ( isIndexed ) {
+
+					count = geometry.index.count;
+
+				} else if ( geometry.attributes.position !== undefined ) {
+
+					count = geometry.attributes.position.count;
+
+				} else {
+
+					return null;
+
+				}
+
+				mergedGeometry.addGroup( offset, count, i );
+
+				offset += count;
+
+			}
+
 		}
 
 		// merge indices

+ 327 - 327
examples/js/MarchingCubes.js

@@ -57,7 +57,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 		this.hasUvs = false;
 
 		this.positionArray = new Float32Array( this.maxCount * 3 );
-		this.normalArray   = new Float32Array( this.maxCount * 3 );
+		this.normalArray = new Float32Array( this.maxCount * 3 );
 
 		if ( this.enableUvs ) {
 
@@ -67,7 +67,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 		if ( this.enableColors ) {
 
-			this.colorArray   = new Float32Array( this.maxCount * 3 );
+			this.colorArray = new Float32Array( this.maxCount * 3 );
 
 		}
 
@@ -86,7 +86,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 	function VIntX( q, offset, isol, x, y, z, valp1, valp2 ) {
 
 		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
+			nc = scope.normal_cache;
 
 		vlist[ offset + 0 ] = x + mu * scope.delta;
 		vlist[ offset + 1 ] = y;
@@ -101,7 +101,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 	function VIntY( q, offset, isol, x, y, z, valp1, valp2 ) {
 
 		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
+			nc = scope.normal_cache;
 
 		vlist[ offset + 0 ] = x;
 		vlist[ offset + 1 ] = y + mu * scope.delta;
@@ -118,7 +118,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 	function VIntZ( q, offset, isol, x, y, z, valp1, valp2 ) {
 
 		var mu = ( isol - valp1 ) / ( valp2 - valp1 ),
-		nc = scope.normal_cache;
+			nc = scope.normal_cache;
 
 		vlist[ offset + 0 ] = x;
 		vlist[ offset + 1 ] = y;
@@ -138,7 +138,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 		if ( scope.normal_cache[ q3 ] === 0.0 ) {
 
-			scope.normal_cache[ q3 + 0 ] = scope.field[ q - 1 ] 	    - scope.field[ q + 1 ];
+			scope.normal_cache[ q3 + 0 ] = scope.field[ q - 1 ] - scope.field[ q + 1 ];
 			scope.normal_cache[ q3 + 1 ] = scope.field[ q - scope.yd ] - scope.field[ q + scope.yd ];
 			scope.normal_cache[ q3 + 2 ] = scope.field[ q - scope.zd ] - scope.field[ q + scope.zd ];
 
@@ -271,7 +271,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 			compNorm( q1 );
 			compNorm( q1z );
-			VIntZ( q1 * 3, 27, isol, fx2, fy,  fz, field1, field5 );
+			VIntZ( q1 * 3, 27, isol, fx2, fy, fz, field1, field5 );
 
 		}
 
@@ -287,11 +287,11 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 			compNorm( qy );
 			compNorm( qyz );
-			VIntZ( qy * 3, 33, isol, fx,  fy2, fz, field2, field6 );
+			VIntZ( qy * 3, 33, isol, fx, fy2, fz, field2, field6 );
 
 		}
 
-		cubeindex <<= 4;  // re-purpose cubeindex into an offset into triTable
+		cubeindex <<= 4; // re-purpose cubeindex into an offset into triTable
 
 		var o1, o2, o3, numtris = 0, i = 0;
 
@@ -482,7 +482,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 		var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1;
 		var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1;
 		var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1;
-		var min_x = Math.floor( xs - radius ); if ( min_x < 1  ) min_x = 1;
+		var min_x = Math.floor( xs - radius ); if ( min_x < 1 ) min_x = 1;
 		var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1;
 
 
@@ -517,7 +517,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 	};
 
-	this.addPlaneX = function( strength, subtract ) {
+	this.addPlaneX = function ( strength, subtract ) {
 
 		var x, y, z, xx, val, xdiv, cxy,
 
@@ -557,7 +557,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 	};
 
-	this.addPlaneY = function( strength, subtract ) {
+	this.addPlaneY = function ( strength, subtract ) {
 
 		var x, y, z, yy, val, ydiv, cy, cxy,
 
@@ -596,7 +596,7 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 	};
 
-	this.addPlaneZ = function( strength, subtract ) {
+	this.addPlaneZ = function ( strength, subtract ) {
 
 		var x, y, z, zz, val, zdiv, cz, cyz,
 
@@ -688,48 +688,48 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 	};
 
-	this.generateGeometry = function() {
+	this.generateGeometry = function () {
 
-		var start = 0, geo = new THREE.Geometry();
-		var normals = [];
+		console.warn( 'THREE.MarchingCubes: generateGeometry() now returns THREE.BufferGeometry' );
+		return this.generateBufferGeometry();
 
-		var geo_callback = function( object ) {
-
-			for ( var i = 0; i < object.count; i ++ ) {
-
-				var vertex = new THREE.Vector3().fromArray( object.positionArray, i * 3 );
-				var normal = new THREE.Vector3().fromArray( object.normalArray, i * 3 );
+	};
 
-				geo.vertices.push( vertex );
-				normals.push( normal );
+	function concatenate( a, b, length ) {
 
-			}
+		var result = new Float32Array( a.length + length );
+		result.set( a, 0 );
+		result.set( b.slice( 0, length ), a.length );
+		return result;
 
-			var nfaces = object.count / 3;
-
-			for ( i = 0; i < nfaces; i ++ ) {
+	}
 
-				var a = ( start + i ) * 3;
-				var b = a + 1;
-				var c = a + 2;
+	this.generateBufferGeometry = function () {
 
-				var na = normals[ a ];
-				var nb = normals[ b ];
-				var nc = normals[ c ];
+		var geo = new THREE.BufferGeometry();
+		var posArray = new Float32Array();
+		var normArray = new Float32Array();
+		var colorArray = new Float32Array();
+		var uvArray = new Float32Array();
+		var scope = this;
 
-				var face = new THREE.Face3( a, b, c, [ na, nb, nc ] );
-				geo.faces.push( face );
+		var geo_callback = function ( object ) {
 
-			}
+			if ( scope.hasPositions ) posArray = concatenate( posArray, object.positionArray, object.count * 3 );
+			if ( scope.hasNormals ) normArray = concatenate( normArray, object.normalArray, object.count * 3 );
+			if ( scope.hasColors ) colorArray = concatenate( colorArray, object.colorArray, object.count * 3 );
+			if ( scope.hasUvs ) uvArray = concatenate( uvArray, object.uvArray, object.count * 2 );
 
-			start += nfaces;
 			object.count = 0;
 
 		};
 
 		this.render( geo_callback );
 
-		// console.log( "generated " + geo.faces.length + " triangles" );
+		if ( this.hasPositions ) geo.addAttribute( 'position', new THREE.BufferAttribute( posArray, 3 ) );
+		if ( this.hasNormals ) geo.addAttribute( 'normal', new THREE.BufferAttribute( normArray, 3 ) );
+		if ( this.hasColors ) geo.addAttribute( 'color', new THREE.BufferAttribute( colorArray, 3 ) );
+		if ( this.hasUvs ) geo.addAttribute( 'uv', new THREE.BufferAttribute( uvArray, 2 ) );
 
 		return geo;
 
@@ -752,293 +752,293 @@ THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
 // who in turn got them from Cory Gene Bloyd.
 
 THREE.edgeTable = new Int32Array( [
-0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
-0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
-0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
-0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
-0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
-0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
-0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
-0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
-0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
-0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
-0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
-0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
-0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
-0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
-0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
-0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
-0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
-0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
-0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
-0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
-0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
-0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
-0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
-0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
-0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
-0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
-0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
-0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
-0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
-0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
-0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
-0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
+	0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
+	0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
+	0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
+	0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
+	0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c,
+	0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
+	0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac,
+	0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
+	0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c,
+	0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
+	0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc,
+	0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
+	0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c,
+	0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
+	0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc,
+	0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
+	0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
+	0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
+	0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
+	0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
+	0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
+	0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
+	0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
+	0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460,
+	0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
+	0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0,
+	0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
+	0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230,
+	0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
+	0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190,
+	0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
+	0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );
 
 THREE.triTable = new Int32Array( [
-- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
-3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
-4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
-9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
-10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
-5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
-5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
-8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
-2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
-11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
-5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
-11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
-11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
-2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
-6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
-6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
-6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
-8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
-7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
-3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
-0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
-9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
-8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
-5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
-0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
-6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
-10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
-0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
-3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
-6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
-9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
-8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
-3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
-10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
-10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
-7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
-2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
-1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
-11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
-8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
-0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
-7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
-7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
-10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
-0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
-7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
-6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
-4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
-10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
-8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
-1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
-10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
-10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
-9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
-7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
-3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
-7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
-3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
-6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
-9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
-1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
-4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
-7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
-6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
-0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
-6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
-0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
-11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
-6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
-5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
-9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
-1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
-10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
-0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
-11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
-9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
-7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
-2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
-9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
-9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
-1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
-0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
-10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
-2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
-0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
-0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
-9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
-5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
-5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
-8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
-9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
-1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
-3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
-4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
-9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
-11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
-2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
-9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
-3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
-1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
-4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
-0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
-1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
-- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );
+	- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 8, 3, 9, 8, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 2, 10, 0, 2, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 8, 3, 2, 10, 8, 10, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 11, 2, 8, 11, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 9, 0, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 11, 2, 1, 9, 11, 9, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 10, 1, 11, 10, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 10, 1, 0, 8, 10, 8, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 9, 0, 3, 11, 9, 11, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 3, 0, 7, 3, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 1, 9, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 1, 9, 4, 7, 1, 7, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 4, 7, 3, 0, 4, 1, 2, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 2, 10, 9, 0, 2, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+	8, 4, 7, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 4, 7, 11, 2, 4, 2, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 0, 1, 8, 4, 7, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, - 1, - 1, - 1, - 1,
+	3, 10, 1, 3, 11, 10, 7, 8, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, - 1, - 1, - 1, - 1,
+	4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+	4, 7, 11, 4, 11, 9, 9, 11, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 5, 4, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 5, 4, 1, 5, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 5, 4, 8, 3, 5, 3, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 0, 8, 1, 2, 10, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 2, 10, 5, 4, 2, 4, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, - 1, - 1, - 1, - 1,
+	9, 5, 4, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 11, 2, 0, 8, 11, 4, 9, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 5, 4, 0, 1, 5, 2, 3, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, - 1, - 1, - 1, - 1,
+	10, 3, 11, 10, 1, 3, 9, 5, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, - 1, - 1, - 1, - 1,
+	5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, - 1, - 1, - 1, - 1,
+	5, 4, 8, 5, 8, 10, 10, 8, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 7, 8, 5, 7, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 3, 0, 9, 5, 3, 5, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 7, 8, 0, 1, 7, 1, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 7, 8, 9, 5, 7, 10, 1, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, - 1, - 1, - 1, - 1,
+	8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, - 1, - 1, - 1, - 1,
+	2, 10, 5, 2, 5, 3, 3, 5, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 9, 5, 7, 8, 9, 3, 11, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+	2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, - 1, - 1, - 1, - 1,
+	11, 2, 1, 11, 1, 7, 7, 1, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, - 1, - 1, - 1, - 1,
+	5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, - 1,
+	11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, - 1,
+	11, 10, 5, 7, 11, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 0, 1, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 8, 3, 1, 9, 8, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 6, 5, 2, 6, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 6, 5, 1, 2, 6, 3, 0, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 6, 5, 9, 0, 6, 0, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, - 1, - 1, - 1, - 1,
+	2, 3, 11, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 0, 8, 11, 2, 0, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 1, 9, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, - 1, - 1, - 1, - 1,
+	6, 3, 11, 6, 5, 3, 5, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+	3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, - 1, - 1, - 1, - 1,
+	6, 5, 9, 6, 9, 11, 11, 9, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 10, 6, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 3, 0, 4, 7, 3, 6, 5, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 9, 0, 5, 10, 6, 8, 4, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, - 1, - 1, - 1, - 1,
+	6, 1, 2, 6, 5, 1, 4, 7, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, - 1, - 1, - 1, - 1,
+	8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, - 1, - 1, - 1, - 1,
+	7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, - 1,
+	3, 11, 2, 7, 8, 4, 10, 6, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, - 1, - 1, - 1, - 1,
+	0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, - 1, - 1, - 1, - 1,
+	9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, - 1,
+	8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, - 1, - 1, - 1, - 1,
+	5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, - 1,
+	0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, - 1,
+	6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, - 1, - 1, - 1, - 1,
+	10, 4, 9, 6, 4, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 10, 6, 4, 9, 10, 0, 8, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 0, 1, 10, 6, 0, 6, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+	1, 4, 9, 1, 2, 4, 2, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, - 1, - 1, - 1, - 1,
+	0, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 3, 2, 8, 2, 4, 4, 2, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 4, 9, 10, 6, 4, 11, 2, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, - 1, - 1, - 1, - 1,
+	3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, - 1, - 1, - 1, - 1,
+	6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, - 1,
+	9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, - 1, - 1, - 1, - 1,
+	8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, - 1,
+	3, 11, 6, 3, 6, 0, 0, 6, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	6, 4, 8, 11, 6, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 10, 6, 7, 8, 10, 8, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, - 1, - 1, - 1, - 1,
+	10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, - 1, - 1, - 1, - 1,
+	10, 6, 7, 10, 7, 1, 1, 7, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+	2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, - 1,
+	7, 8, 0, 7, 0, 6, 6, 0, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 3, 2, 6, 7, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, - 1, - 1, - 1, - 1,
+	2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, - 1,
+	1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, - 1,
+	11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, - 1, - 1, - 1, - 1,
+	8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, - 1,
+	0, 9, 1, 11, 6, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, - 1, - 1, - 1, - 1,
+	7, 11, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 0, 8, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 1, 9, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 1, 9, 8, 3, 1, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 1, 2, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, 3, 0, 8, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 9, 0, 2, 10, 9, 6, 11, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, - 1, - 1, - 1, - 1,
+	7, 2, 3, 6, 2, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	7, 0, 8, 7, 6, 0, 6, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 7, 6, 2, 3, 7, 0, 1, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, - 1, - 1, - 1, - 1,
+	10, 7, 6, 10, 1, 7, 1, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, - 1, - 1, - 1, - 1,
+	0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, - 1, - 1, - 1, - 1,
+	7, 6, 10, 7, 10, 8, 8, 10, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	6, 8, 4, 11, 8, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 6, 11, 3, 0, 6, 0, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 6, 11, 8, 4, 6, 9, 0, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, - 1, - 1, - 1, - 1,
+	6, 8, 4, 6, 11, 8, 2, 10, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, - 1, - 1, - 1, - 1,
+	4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, - 1, - 1, - 1, - 1,
+	10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, - 1,
+	8, 2, 3, 8, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 4, 2, 4, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, - 1, - 1, - 1, - 1,
+	1, 9, 4, 1, 4, 2, 2, 4, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, - 1, - 1, - 1, - 1,
+	10, 1, 0, 10, 0, 6, 6, 0, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, - 1,
+	10, 9, 4, 6, 10, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 9, 5, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, 4, 9, 5, 11, 7, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 0, 1, 5, 4, 0, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, - 1, - 1, - 1, - 1,
+	9, 5, 4, 10, 1, 2, 7, 6, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, - 1, - 1, - 1, - 1,
+	7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, - 1, - 1, - 1, - 1,
+	3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, - 1,
+	7, 2, 3, 7, 6, 2, 5, 4, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, - 1, - 1, - 1, - 1,
+	3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, - 1, - 1, - 1, - 1,
+	6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, - 1,
+	9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, - 1, - 1, - 1, - 1,
+	1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, - 1,
+	4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, - 1,
+	7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, - 1, - 1, - 1, - 1,
+	6, 9, 5, 6, 11, 9, 11, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, - 1, - 1, - 1, - 1,
+	0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, - 1, - 1, - 1, - 1,
+	6, 11, 3, 6, 3, 5, 5, 3, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, - 1, - 1, - 1, - 1,
+	0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, - 1,
+	11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, - 1,
+	6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, - 1, - 1, - 1, - 1,
+	5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, - 1, - 1, - 1, - 1,
+	9, 5, 6, 9, 6, 0, 0, 6, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, - 1,
+	1, 5, 6, 2, 1, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, - 1,
+	10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, - 1, - 1, - 1, - 1,
+	0, 3, 8, 5, 6, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 5, 6, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 5, 10, 7, 5, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 5, 10, 11, 7, 5, 8, 3, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 11, 7, 5, 10, 11, 1, 9, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, - 1, - 1, - 1, - 1,
+	11, 1, 2, 11, 7, 1, 7, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, - 1, - 1, - 1, - 1,
+	9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, - 1, - 1, - 1, - 1,
+	7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, - 1,
+	2, 5, 10, 2, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, - 1, - 1, - 1, - 1,
+	9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, - 1, - 1, - 1, - 1,
+	9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, - 1,
+	1, 3, 5, 3, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 7, 0, 7, 1, 1, 7, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 0, 3, 9, 3, 5, 5, 3, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 8, 7, 5, 9, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 8, 4, 5, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, - 1, - 1, - 1, - 1,
+	0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, - 1, - 1, - 1, - 1,
+	10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, - 1,
+	2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, - 1, - 1, - 1, - 1,
+	0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, - 1,
+	0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, - 1,
+	9, 4, 5, 2, 11, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, - 1, - 1, - 1, - 1,
+	5, 10, 2, 5, 2, 4, 4, 2, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, - 1,
+	5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, - 1, - 1, - 1, - 1,
+	8, 4, 5, 8, 5, 3, 3, 5, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 4, 5, 1, 0, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, - 1, - 1, - 1, - 1,
+	9, 4, 5, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 11, 7, 4, 9, 11, 9, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, - 1, - 1, - 1, - 1,
+	1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, - 1, - 1, - 1, - 1,
+	3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, - 1,
+	4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, - 1, - 1, - 1, - 1,
+	9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, - 1,
+	11, 7, 4, 11, 4, 2, 2, 4, 0, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, - 1, - 1, - 1, - 1,
+	2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, - 1, - 1, - 1, - 1,
+	9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, - 1,
+	3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, - 1,
+	1, 10, 2, 8, 7, 4, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 9, 1, 4, 1, 7, 7, 1, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, - 1, - 1, - 1, - 1,
+	4, 0, 3, 7, 4, 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	4, 8, 7, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 10, 8, 10, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 0, 9, 3, 9, 11, 11, 9, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 1, 10, 0, 10, 8, 8, 10, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 1, 10, 11, 3, 10, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 2, 11, 1, 11, 9, 9, 11, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, - 1, - 1, - 1, - 1,
+	0, 2, 11, 8, 0, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	3, 2, 11, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 3, 8, 2, 8, 10, 10, 8, 9, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	9, 10, 2, 0, 9, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, - 1, - 1, - 1, - 1,
+	1, 10, 2, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	1, 3, 8, 9, 1, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 9, 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	0, 3, 8, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1,
+	- 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 ] );

File diff suppressed because it is too large
+ 299 - 299
examples/js/Octree.js


+ 41 - 4
examples/js/ShaderGodRays.js

@@ -20,15 +20,52 @@
 
 THREE.ShaderGodRays = {
 
+	'godrays_depthMask': {
+
+		uniforms: {
+
+			tInput: {
+				value: null
+			}
+
+		},
+
+		vertexShader: [
+
+			"varying vec2 vUv;",
+
+			"void main() {",
+
+			" vUv = uv;",
+			" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
+
+			"}"
+
+		].join( "\n" ),
+
+		fragmentShader: [
+
+			"varying vec2 vUv;",
+
+			"uniform sampler2D tInput;",
+
+			"void main() {",
+
+			"	gl_FragColor = vec4( 1.0 ) - texture2D( tInput, vUv );",
+
+
+			"}"
+
+		].join( "\n" )
+
+	},
+
+
 	/**
 	 * The god-ray generation shader.
 	 *
 	 * First pass:
 	 *
-	 * The input is the depth map. I found that the output from the
-	 * THREE.MeshDepthMaterial material was directly suitable without
-	 * requiring any treatment whatsoever.
-	 *
 	 * The depth map is blurred along radial lines towards the "sun". The
 	 * output is written to a temporary render target (I used a 1/4 sized
 	 * target).

+ 278 - 239
examples/js/animation/CCDIKSolver.js

@@ -2,416 +2,455 @@
  * @author takahiro / https://github.com/takahirox
  *
  * CCD Algorithm
- *  https://sites.google.com/site/auraliusproject/ccd-algorithm
- *
- * mesh.geometry needs to have iks array.
+ *  - https://sites.google.com/site/auraliusproject/ccd-algorithm
  *
  * // ik parameter example
  * //
- * // target, effector, index in links are bone index in skeleton.
+ * // target, effector, index in links are bone index in skeleton.bones.
  * // the bones relation should be
  * // <-- parent                                  child -->
  * // links[ n ], links[ n - 1 ], ..., links[ 0 ], effector
- * ik = {
+ * iks = [ {
  *	target: 1,
  *	effector: 2,
  *	links: [ { index: 5, limitation: new THREE.Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
  *	iteration: 10,
  *	minAngle: 0.0,
  *	maxAngle: 1.0,
- * };
+ * } ];
  */
 
-THREE.CCDIKSolver = function ( mesh ) {
+THREE.CCDIKSolver = ( function () {
 
-	this.mesh = mesh;
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKSolver( mesh, iks ) {
 
-	this._valid();
+		this.mesh = mesh;
+		this.iks = iks || [];
 
-};
+		this._valid();
 
-THREE.CCDIKSolver.prototype = {
+	}
 
-	constructor: THREE.CCDIKSolver,
+	CCDIKSolver.prototype = {
 
-	_valid: function () {
+		constructor: CCDIKSolver,
 
-		var iks = this.mesh.geometry.iks;
-		var bones = this.mesh.skeleton.bones;
+		/**
+		 * Update IK bones.
+		 *
+		 * @return {THREE.CCDIKSolver}
+		 */
+		update: function () {
 
-		for ( var i = 0, il = iks.length; i < il; i ++ ) {
+			var q = new THREE.Quaternion();
+			var targetPos = new THREE.Vector3();
+			var targetVec = new THREE.Vector3();
+			var effectorPos = new THREE.Vector3();
+			var effectorVec = new THREE.Vector3();
+			var linkPos = new THREE.Vector3();
+			var invLinkQ = new THREE.Quaternion();
+			var linkScale = new THREE.Vector3();
+			var axis = new THREE.Vector3();
+			var vector = new THREE.Vector3();
 
-			var ik = iks[ i ];
+			return function update() {
 
-			var effector = bones[ ik.effector ];
+				var bones = this.mesh.skeleton.bones;
+				var iks = this.iks;
 
-			var links = ik.links;
+				// for reference overhead reduction in loop
+				var math = Math;
 
-			var link0, link1;
+				for ( var i = 0, il = iks.length; i < il; i++ ) {
 
-			link0 = effector;
+					var ik = iks[ i ];
+					var effector = bones[ ik.effector ];
+					var target = bones[ ik.target ];
 
-			for ( var j = 0, jl = links.length; j < jl; j ++ ) {
+					// don't use getWorldPosition() here for the performance
+					// because it calls updateMatrixWorld( true ) inside.
+					targetPos.setFromMatrixPosition( target.matrixWorld );
 
-				link1 = bones[ links[ j ].index ];
+					var links = ik.links;
+					var iteration = ik.iteration !== undefined ? ik.iteration : 1;
 
-				if ( link0.parent !== link1 ) {
+					for ( var j = 0; j < iteration; j++ ) {
 
-					console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
+						var rotated = false;
 
-				}
+						for ( var k = 0, kl = links.length; k < kl; k++ ) {
 
-				link0 = link1;
+							var link = bones[ links[ k ].index ];
 
-			}
-
-		}
+							// skip this link and following links.
+							// this skip is used for MMD performance optimization.
+							if ( links[ k ].enabled === false ) break;
 
-	},
+							var limitation = links[ k ].limitation;
+							var rotationMin = links[ k ].rotationMin;
+							var rotationMax = links[ k ].rotationMax;
 
-	/*
-	 * save the bone matrices before solving IK.
-	 * they're used for generating VMD and VPD.
-	 */
-	_saveOriginalBonesInfo: function () {
+							// don't use getWorldPosition/Quaternion() here for the performance
+							// because they call updateMatrixWorld( true ) inside.
+							link.matrixWorld.decompose( linkPos, invLinkQ, linkScale );
+							invLinkQ.inverse();
+							effectorPos.setFromMatrixPosition( effector.matrixWorld );
 
-		var bones = this.mesh.skeleton.bones;
+							// work in link world
+							effectorVec.subVectors( effectorPos, linkPos );
+							effectorVec.applyQuaternion( invLinkQ );
+							effectorVec.normalize();
 
-		for ( var i = 0, il = bones.length; i < il; i ++ ) {
+							targetVec.subVectors( targetPos, linkPos );
+							targetVec.applyQuaternion( invLinkQ );
+							targetVec.normalize();
 
-			var bone = bones[ i ];
+							var angle = targetVec.dot( effectorVec );
 
-			if ( bone.userData.ik === undefined ) bone.userData.ik = {};
+							if ( angle > 1.0 ) {
 
-			bone.userData.ik.originalMatrix = bone.matrix.toArray();
+								angle = 1.0;
 
-		}
+							} else if ( angle < -1.0 ) {
 
-	},
+								angle = -1.0;
 
-	update: function ( saveOriginalBones ) {
+							}
 
-		var q = new THREE.Quaternion();
+							angle = math.acos( angle );
 
-		var targetPos = new THREE.Vector3();
-		var targetVec = new THREE.Vector3();
-		var effectorPos = new THREE.Vector3();
-		var effectorVec = new THREE.Vector3();
-		var linkPos = new THREE.Vector3();
-		var invLinkQ = new THREE.Quaternion();
-		var linkScale = new THREE.Vector3();
-		var axis = new THREE.Vector3();
+							// skip if changing angle is too small to prevent vibration of bone
+							// Refer to http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
+							if ( angle < 1e-5 ) continue;
 
-		var bones = this.mesh.skeleton.bones;
-		var iks = this.mesh.geometry.iks;
+							if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
 
-		var boneParams = this.mesh.geometry.bones;
+								angle = ik.minAngle;
 
-		// for reference overhead reduction in loop
-		var math = Math;
+							}
 
-		this.mesh.updateMatrixWorld( true );
+							if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
 
-		if ( saveOriginalBones === true ) this._saveOriginalBonesInfo();
+								angle = ik.maxAngle;
 
-		for ( var i = 0, il = iks.length; i < il; i++ ) {
+							}
 
-			var ik = iks[ i ];
-			var effector = bones[ ik.effector ];
-			var target = bones[ ik.target ];
+							axis.crossVectors( effectorVec, targetVec );
+							axis.normalize();
 
-			// don't use getWorldPosition() here for the performance
-			// because it calls updateMatrixWorld( true ) inside.
-			targetPos.setFromMatrixPosition( target.matrixWorld );
+							q.setFromAxisAngle( axis, angle );
+							link.quaternion.multiply( q );
 
-			var links = ik.links;
-			var iteration = ik.iteration !== undefined ? ik.iteration : 1;
+							// TODO: re-consider the limitation specification
+							if ( limitation !== undefined ) {
 
-			for ( var j = 0; j < iteration; j++ ) {
+								var c = link.quaternion.w;
 
-				var rotated = false;
+								if ( c > 1.0 ) c = 1.0;
 
-				for ( var k = 0, kl = links.length; k < kl; k++ ) {
+								var c2 = math.sqrt( 1 - c * c );
+								link.quaternion.set( limitation.x * c2,
+								                     limitation.y * c2,
+								                     limitation.z * c2,
+								                     c );
 
-					var link = bones[ links[ k ].index ];
+							}
 
-					// skip this link and following links.
-					// this skip is used for MMD performance optimization.
-					if ( links[ k ].enabled === false ) break;
+							if ( rotationMin !== undefined ) {
 
-					var limitation = links[ k ].limitation;
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.max( rotationMin ) );
 
-					// don't use getWorldPosition/Quaternion() here for the performance
-					// because they call updateMatrixWorld( true ) inside.
-					link.matrixWorld.decompose( linkPos, invLinkQ, linkScale );
-					invLinkQ.inverse();
-					effectorPos.setFromMatrixPosition( effector.matrixWorld );
+							}
 
-					// work in link world
-					effectorVec.subVectors( effectorPos, linkPos );
-					effectorVec.applyQuaternion( invLinkQ );
-					effectorVec.normalize();
+							if ( rotationMax !== undefined ) {
 
-					targetVec.subVectors( targetPos, linkPos );
-					targetVec.applyQuaternion( invLinkQ );
-					targetVec.normalize();
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.min( rotationMax ) );
 
-					var angle = targetVec.dot( effectorVec );
+							}
 
-					if ( angle > 1.0 ) {
+							link.updateMatrixWorld( true );
 
-						angle = 1.0;
+							rotated = true;
 
-					} else if ( angle < -1.0 ) {
+						}
 
-						angle = -1.0;
+						if ( ! rotated ) break;
 
 					}
 
-					angle = math.acos( angle );
+				}
 
-					// skip if changing angle is too small to prevent vibration of bone
-					// Refer to http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
-					if ( angle < 1e-5 ) continue;
+				return this;
 
-					if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
+			};
 
-						angle = ik.minAngle;
+		}(),
 
-					}
+		/**
+		 * Creates Helper
+		 *
+		 * @return {CCDIKHelper}
+		 */
+		createHelper: function () {
 
-					if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
+			return new CCDIKHelper( this.mesh, this.mesh.geometry.iks );
 
-						angle = ik.maxAngle;
+		},
 
-					}
+		// private methods
 
-					axis.crossVectors( effectorVec, targetVec );
-					axis.normalize();
+		_valid: function () {
 
-					q.setFromAxisAngle( axis, angle );
-					link.quaternion.multiply( q );
+			var iks = this.iks;
+			var bones = this.mesh.skeleton.bones;
 
-					// TODO: re-consider the limitation specification
-					if ( limitation !== undefined ) {
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
 
-						var c = link.quaternion.w;
+				var ik = iks[ i ];
+				var effector = bones[ ik.effector ];
+				var links = ik.links;
+				var link0, link1;
 
-						if ( c > 1.0 ) {
+				link0 = effector;
 
-							c = 1.0;
+				for ( var j = 0, jl = links.length; j < jl; j ++ ) {
 
-						}
+					link1 = bones[ links[ j ].index ];
 
-						var c2 = math.sqrt( 1 - c * c );
-						link.quaternion.set( limitation.x * c2,
-						                     limitation.y * c2,
-						                     limitation.z * c2,
-						                     c );
+					if ( link0.parent !== link1 ) {
+
+						console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
 
 					}
 
-					link.updateMatrixWorld( true );
-					rotated = true;
+					link0 = link1;
 
 				}
 
-				if ( ! rotated ) break;
-
 			}
 
 		}
 
-		// just in case
-		this.mesh.updateMatrixWorld( true );
+	};
 
-	}
+	/**
+	 * Visualize IK bones
+	 *
+	 * @param {SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKHelper( mesh, iks ) {
+
+		THREE.Object3D.call( this );
+
+		this.root = mesh;
+		this.iks = iks || [];
+
+		this.matrix.copy( mesh.matrixWorld );
+		this.matrixAutoUpdate = false;
 
-};
+		this.sphereGeometry = new THREE.SphereBufferGeometry( 0.25, 16, 8 );
 
+		this.targetSphereMaterial = new THREE.MeshBasicMaterial( {
+			color: new THREE.Color( 0xff8888 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
 
-THREE.CCDIKHelper = function ( mesh ) {
+		this.effectorSphereMaterial = new THREE.MeshBasicMaterial( {
+			color: new THREE.Color( 0x88ff88 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
 
-	if ( mesh.geometry.iks === undefined || mesh.skeleton === undefined ) {
+		this.linkSphereMaterial = new THREE.MeshBasicMaterial( {
+			color: new THREE.Color( 0x8888ff ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
 
-		throw 'THREE.CCDIKHelper requires iks in mesh.geometry and skeleton in mesh.';
+		this.lineMaterial = new THREE.LineBasicMaterial( {
+			color: new THREE.Color( 0xff0000 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this._init();
 
 	}
 
-	THREE.Object3D.call( this );
+	CCDIKHelper.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
 
-	this.root = mesh;
+		constructor: CCDIKHelper,
 
-	this.matrix = mesh.matrixWorld;
-	this.matrixAutoUpdate = false;
+		/**
+		 * Updates IK bones visualization.
+		 */
+		updateMatrixWorld: function () {
 
-	this.sphereGeometry = new THREE.SphereBufferGeometry( 0.25, 16, 8 );
+			var matrix = new THREE.Matrix4();
+			var vector = new THREE.Vector3();
 
-	this.targetSphereMaterial = new THREE.MeshBasicMaterial( {
-		color: new THREE.Color( 0xff8888 ),
-		depthTest: false,
-		depthWrite: false,
-		transparent: true
-	} );
+			function getPosition( bone, matrixWorldInv ) {
 
-	this.effectorSphereMaterial = new THREE.MeshBasicMaterial( {
-		color: new THREE.Color( 0x88ff88 ),
-		depthTest: false,
-		depthWrite: false,
-		transparent: true
-	} );
+				return vector
+					.setFromMatrixPosition( bone.matrixWorld )
+					.applyMatrix4( matrixWorldInv );
 
-	this.linkSphereMaterial = new THREE.MeshBasicMaterial( {
-		color: new THREE.Color( 0x8888ff ),
-		depthTest: false,
-		depthWrite: false,
-		transparent: true
-	} );
+			}
 
-	this.lineMaterial = new THREE.LineBasicMaterial( {
-		color: new THREE.Color( 0xff0000 ),
-		depthTest: false,
-		depthWrite: false,
-		transparent: true
-	} );
+			function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
 
-	this._init();
-	this.update();
+				var v = getPosition( bone, matrixWorldInv );
 
-};
+				array[ index * 3 + 0 ] = v.x;
+				array[ index * 3 + 1 ] = v.y;
+				array[ index * 3 + 2 ] = v.z;
 
-THREE.CCDIKHelper.prototype = Object.create( THREE.Object3D.prototype );
-THREE.CCDIKHelper.prototype.constructor = THREE.CCDIKHelper;
+			}
 
-THREE.CCDIKHelper.prototype._init = function () {
+			return function updateMatrixWorld( force ) {
 
-	var self = this;
-	var mesh = this.root;
-	var iks = mesh.geometry.iks;
+				var mesh = this.root;
 
-	function createLineGeometry( ik ) {
+				if ( this.visible ) {
 
-		var geometry = new THREE.BufferGeometry();
-		var vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
-		geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+					var offset = 0;
 
-		return geometry;
+					var iks = this.iks;
+					var bones = mesh.skeleton.bones;
 
-	}
+					matrix.getInverse( mesh.matrixWorld );
 
-	function createTargetMesh() {
+					for ( var i = 0, il = iks.length; i < il; i ++ ) {
 
-		return new THREE.Mesh( self.sphereGeometry, self.targetSphereMaterial );
+						var ik = iks[ i ];
 
-	}
+						var targetBone = bones[ ik.target ];
+						var effectorBone = bones[ ik.effector ];
 
-	function createEffectorMesh() {
+						var targetMesh = this.children[ offset ++ ];
+						var effectorMesh = this.children[ offset ++ ];
 
-		return new THREE.Mesh( self.sphereGeometry, self.effectorSphereMaterial );
+						targetMesh.position.copy( getPosition( targetBone, matrix ) );
+						effectorMesh.position.copy( getPosition( effectorBone, matrix ) );
 
-	}
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
 
-	function createLinkMesh() {
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
 
-		return new THREE.Mesh( self.sphereGeometry, self.linkSphereMaterial );
+							var linkMesh = this.children[ offset ++ ];
 
-	}
+							linkMesh.position.copy( getPosition( linkBone, matrix ) );
 
-	function createLine( ik ) {
+						}
 
-		return new THREE.Line( createLineGeometry( ik ), self.lineMaterial );
+						var line = this.children[ offset ++ ];
+						var array = line.geometry.attributes.position.array;
 
-	}
+						setPositionOfBoneToAttributeArray( array, 0, targetBone, matrix );
+						setPositionOfBoneToAttributeArray( array, 1, effectorBone, matrix );
 
-	for ( var i = 0, il = iks.length; i < il; i ++ ) {
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
 
-		var ik = iks[ i ];
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
+							setPositionOfBoneToAttributeArray( array, j + 2, linkBone, matrix );
 
-		this.add( createTargetMesh() );
-		this.add( createEffectorMesh() );
+						}
 
-		for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+						line.geometry.attributes.position.needsUpdate = true;
 
-			this.add( createLinkMesh() );
+					}
 
-		}
+				}
 
-		this.add( createLine( ik ) );
+				this.matrix.copy( mesh.matrixWorld );
 
-	}
+				THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-};
+			};
 
-THREE.CCDIKHelper.prototype.update = function () {
+		}(),
 
-	var offset = 0;
+		// private method
 
-	var mesh = this.root;
-	var iks = mesh.geometry.iks;
-	var bones = mesh.skeleton.bones;
+		_init: function () {
 
-	var matrixWorldInv = new THREE.Matrix4().getInverse( mesh.matrixWorld );
-	var vector = new THREE.Vector3();
+			var self = this;
+			var mesh = this.root;
+			var iks = this.iks;
 
-	function getPosition( bone ) {
+			function createLineGeometry( ik ) {
 
-		vector.setFromMatrixPosition( bone.matrixWorld );
-		vector.applyMatrix4( matrixWorldInv );
+				var geometry = new THREE.BufferGeometry();
+				var vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
+				geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
 
-		return vector;
+				return geometry;
 
-	}
+			}
 
-	function setPositionOfBoneToAttributeArray( array, index, bone ) {
+			function createTargetMesh() {
 
-		var v = getPosition( bone );
+				return new THREE.Mesh( self.sphereGeometry, self.targetSphereMaterial );
 
-		array[ index * 3 + 0 ] = v.x;
-		array[ index * 3 + 1 ] = v.y;
-		array[ index * 3 + 2 ] = v.z;
+			}
 
-	}
+			function createEffectorMesh() {
 
-	for ( var i = 0, il = iks.length; i < il; i ++ ) {
+				return new THREE.Mesh( self.sphereGeometry, self.effectorSphereMaterial );
 
-		var ik = iks[ i ];
+			}
 
-		var targetBone = bones[ ik.target ];
-		var effectorBone = bones[ ik.effector ];
+			function createLinkMesh() {
 
-		var targetMesh = this.children[ offset ++ ];
-		var effectorMesh = this.children[ offset ++ ];
+				return new THREE.Mesh( self.sphereGeometry, self.linkSphereMaterial );
 
-		targetMesh.position.copy( getPosition( targetBone ) );
-		effectorMesh.position.copy( getPosition( effectorBone ) );
+			}
 
-		for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+			function createLine( ik ) {
 
-			var link = ik.links[ j ];
-			var linkBone = bones[ link.index ];
+				return new THREE.Line( createLineGeometry( ik ), self.lineMaterial );
 
-			var linkMesh = this.children[ offset ++ ];
+			}
 
-			linkMesh.position.copy( getPosition( linkBone ) );
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
 
-		}
+				var ik = iks[ i ];
 
-		var line = this.children[ offset ++ ];
-		var array = line.geometry.attributes.position.array;
+				this.add( createTargetMesh() );
+				this.add( createEffectorMesh() );
 
-		setPositionOfBoneToAttributeArray( array, 0, targetBone );
-		setPositionOfBoneToAttributeArray( array, 1, effectorBone );
+				for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
 
-		for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+					this.add( createLinkMesh() );
 
-			var link = ik.links[ j ];
-			var linkBone = bones[ link.index ];
-			setPositionOfBoneToAttributeArray( array, j + 2, linkBone );
+				}
+
+				this.add( createLine( ik ) );
+
+			}
 
 		}
 
-		line.geometry.attributes.position.needsUpdate = true;
+	} );
 
-	}
+	return CCDIKSolver;
 
-};
+} )();

+ 1035 - 0
examples/js/animation/MMDAnimationHelper.js

@@ -0,0 +1,1035 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * MMDAnimationHelper handles animation of MMD assets loaded by MMDLoader
+ * with MMD special features as IK, Grant, and Physics.
+ *
+ * Dependencies
+ *  - ammo.js https://github.com/kripken/ammo.js
+ *  - THREE.MMDPhysics
+ *  - THREE.CCDIKSolver
+ *
+ * TODO
+ *  - more precise grant skinning support.
+ */
+
+THREE.MMDAnimationHelper = ( function () {
+
+	/**
+	 * @param {Object} params - (optional)
+	 * @param {boolean} params.sync - Whether animation durations of added objects are synched. Default is true.
+	 * @param {Number} params.afterglow - Default is 0.0.
+	 * @param {boolean} params resetPhysicsOnLoop - Default is true.
+	 */
+	function MMDAnimationHelper( params ) {
+
+		params = params || {};
+
+		this.meshes = [];
+
+		this.camera = null;
+		this.cameraTarget = new THREE.Object3D();
+		this.cameraTarget.name = 'target';
+
+		this.audio = null;
+		this.audioManager = null;
+
+		this.objects = new WeakMap();
+
+		this.configuration = {
+			sync: params.sync !== undefined
+				? params.sync : true,
+			afterglow: params.afterglow !== undefined
+				? params.afterglow : 0.0,
+			resetPhysicsOnLoop: params.resetPhysicsOnLoop !== undefined
+				? params.resetPhysicsOnLoop : true
+		};
+
+		this.enabled = {
+			animation: true,
+			ik: true,
+			grant: true,
+			physics: true,
+			cameraAnimation: true
+		};
+
+		this.onBeforePhysics = function ( mesh ) {};
+
+		// experimental
+		this.sharedPhysics = false;
+		this.masterPhysics = null;
+
+	}
+
+	MMDAnimationHelper.prototype = {
+
+		constructor: MMDAnimationHelper,
+
+		/**
+		 * Adds an Three.js Object to helper and setups animation.
+		 * The anmation durations of added objects are synched
+		 * if this.configuration.sync is true.
+		 *
+		 * @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
+		 * @param {Object} params - (optional)
+		 * @param {THREE.AnimationClip|Array<THREE.AnimationClip>} params.animation - Only for THREE.SkinnedMesh and THREE.Camera. Default is undefined.
+		 * @param {boolean} params.physics - Only for THREE.SkinnedMesh. Default is true.
+		 * @param {Integer} params.warmup - Only for THREE.SkinnedMesh and physics is true. Default is 60.
+		 * @param {Number} params.unitStep - Only for THREE.SkinnedMesh and physics is true. Default is 1 / 65.
+		 * @param {Integer} params.maxStepNum - Only for THREE.SkinnedMesh and physics is true. Default is 3.
+		 * @param {THREE.Vector3} params.gravity - Only for THREE.SkinnedMesh and physics is true. Default ( 0, - 9.8 * 10, 0 ).
+		 * @param {Number} params.delayTime - Only for THREE.Audio. Default is 0.0.
+		 * @return {THREE.MMDAnimationHelper}
+		 */
+		add: function ( object, params ) {
+
+			params = params || {};
+
+			if ( object.isSkinnedMesh ) {
+
+				this._addMesh( object, params );
+
+			} else if ( object.isCamera ) {
+
+				this._setupCamera( object, params );
+
+			} else if ( object.type === 'Audio' ) {
+
+				this._setupAudio( object, params );
+
+			} else {
+
+				throw new Error( 'THREE.MMDAnimationHelper.add: '
+					+ 'accepts only '
+					+ 'THREE.SkinnedMesh or '
+					+ 'THREE.Camera or '
+					+ 'THREE.Audio instance.' );
+
+			}
+
+			if ( this.configuration.sync ) this._syncDuration();
+
+			return this;
+
+		},
+
+		/**
+		 * Removes an Three.js Object from helper.
+		 *
+		 * @param {THREE.SkinnedMesh|THREE.Camera|THREE.Audio} object
+		 * @return {THREE.MMDAnimationHelper}
+		 */
+		remove: function ( object ) {
+
+			if ( object.isSkinnedMesh ) {
+
+				this._removeMesh( object );
+
+			} else if ( object.isCamera ) {
+
+				this._clearCamera( object );
+
+			} else if ( object.type === 'Audio' ) {
+
+				this._clearAudio( object );
+
+			} else {
+
+				throw new Error( 'THREE.MMDAnimationHelper.remove: '
+					+ 'accepts only '
+					+ 'THREE.SkinnedMesh or '
+					+ 'THREE.Camera or '
+					+ 'THREE.Audio instance.' );
+
+			}
+
+			if ( this.configuration.sync ) this._syncDuration();
+
+			return this;
+
+		},
+
+		/**
+		 * Updates the animation.
+		 *
+		 * @param {Number} delta
+		 * @return {THREE.MMDAnimationHelper}
+		 */
+		update: function ( delta ) {
+
+			if ( this.audioManager !== null ) this.audioManager.control( delta );
+
+			for ( var i = 0; i < this.meshes.length; i ++ ) {
+
+				this._animateMesh( this.meshes[ i ], delta );
+
+			}
+
+			if ( this.sharedPhysics ) this._updateSharedPhysics( delta );
+
+			if ( this.camera !== null ) this._animateCamera( this.camera, delta );
+
+			return this;
+
+		},
+
+		/**
+		 * Changes the pose of SkinnedMesh as VPD specifies.
+		 *
+		 * @param {THREE.SkinnedMesh} mesh
+		 * @param {Object} vpd - VPD content parsed MMDParser
+		 * @param {Object} params - (optional)
+		 * @param {boolean} params.resetPose - Default is true.
+		 * @param {boolean} params.ik - Default is true.
+		 * @param {boolean} params.grant - Default is true.
+		 * @return {THREE.MMDAnimationHelper}
+		 */
+		pose: function ( mesh, vpd, params ) {
+
+			params = params || {};
+
+			if ( params.resetPose !== false ) mesh.pose();
+
+			var bones = mesh.skeleton.bones;
+			var boneParams = vpd.bones;
+
+			var boneNameDictionary = {};
+
+			for ( var i = 0, il = bones.length; i < il; i ++ ) {
+
+				boneNameDictionary[ bones[ i ].name ] = i;
+
+			}
+
+			var vector = new THREE.Vector3();
+			var quaternion = new THREE.Quaternion();
+
+			for ( var i = 0, il = boneParams.length; i < il; i ++ ) {
+
+				var boneParam = boneParams[ i ];
+				var boneIndex = boneNameDictionary[ boneParam.name ];
+
+				if ( boneIndex === undefined ) continue;
+
+				var bone = bones[ boneIndex ];
+				bone.position.add( vector.fromArray( boneParam.translation ) );
+				bone.quaternion.multiply( quaternion.fromArray( boneParam.quaternion ) );
+
+			}
+
+			mesh.updateMatrixWorld( true );
+
+			if ( params.ik !== false ) {
+
+				this._createCCDIKSolver( mesh ).update( params.saveOriginalBonesBeforeIK );  // this param is experimental
+
+			}
+
+			if ( params.grant !== false ) {
+
+				this.createGrantSolver( mesh ).update();
+
+			}
+
+			return this;
+
+		},
+
+		/**
+		 * Enabes/Disables an animation feature.
+		 *
+		 * @param {string} key
+		 * @param {boolean} enebled
+		 * @return {THREE.MMDAnimationHelper}
+		 */
+		enable: function ( key, enabled ) {
+
+			if ( this.enabled[ key ] === undefined ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper.enable: '
+					+ 'unknown key ' + key );
+
+			}
+
+			this.enabled[ key ] = enabled;
+
+			if ( key === 'physics' ) {
+
+				for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+					this._optimizeIK( this.meshes[ i ], enabled );
+
+				}
+
+			}
+
+			return this;
+
+		},
+
+		/**
+		 * Creates an GrantSolver instance.
+		 *
+		 * @param {THREE.SkinnedMesh} mesh
+		 * @return {GrantSolver}
+		 */
+		createGrantSolver: function ( mesh ) {
+
+			return new GrantSolver( mesh, mesh.geometry.grants );
+
+		},
+
+		// private methods
+
+		_addMesh: function ( mesh, params ) {
+
+			if ( this.meshes.indexOf( mesh ) >= 0 ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._addMesh: '
+					+ 'SkinnedMesh \'' + mesh.name + '\' has already been added.' );
+
+			}
+
+			this.meshes.push( mesh );
+			this.objects.set( mesh, { looped: false } );
+
+			this._setupMeshAnimation( mesh, params.animation );
+
+			if ( params.physics !== false ) {
+
+				this._setupMeshPhysics( mesh, params );
+
+			}
+
+			return this;
+
+		},
+
+		_setupCamera: function ( camera, params ) {
+
+			if ( this.camera === camera ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._setupCamera: '
+					+ 'Camera \'' + camera.name + '\' has already been set.' );
+
+			}
+
+			if ( this.camera ) this.clearCamera( this.camera );
+
+			this.camera = camera;
+
+			camera.add( this.cameraTarget );
+
+			this.objects.set( camera, {} );
+
+			if ( params.animation !== undefined ) {
+
+				this._setupCameraAnimation( camera, params.animation )
+
+			}
+
+			return this;
+
+		},
+
+		_setupAudio: function ( audio, params ) {
+
+			if ( this.audio === audio ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._setupAudio: '
+					+ 'Audio \'' + audio.name + '\' has already been set.' );
+
+			}
+
+			if ( this.audio ) this.clearAudio( this.audio );
+
+			this.audio = audio;
+			this.audioManager = new AudioManager( audio, params );
+
+			this.objects.set( this.audioManager, {
+				duration: this.audioManager.duration
+			} );
+
+			return this;
+
+		},
+
+		_removeMesh: function ( mesh ) {
+
+			var found = false;
+			var writeIndex = 0;
+
+			for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+				if ( this.meshes[ i ] === mesh ) {
+
+					this.objects.delete( mesh );
+					found = true;
+
+					continue;
+
+				}
+
+				this.meshes[ writeIndex ++ ] = this.meshes[ i ];
+
+			}
+
+			if ( ! found ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._removeMesh: '
+					+ 'SkinnedMesh \'' + mesh.name + '\' has not been added yet.' );
+
+			}
+
+			this.meshes.length = writeIndex;
+
+			return this;
+
+		},
+
+		_clearCamera: function ( camera ) {
+
+			if ( camera !== this.camera ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._clearCamera: '
+					+ 'Camera \'' + camera.name + '\' has not been set yet.' );
+
+			}
+
+			this.camera.remove( this.cameraTarget );
+
+			this.objects.delete( this.camera );
+			this.camera = null;
+
+			return this;
+
+		},
+
+		_clearAudio: function ( audio ) {
+
+			if ( audio !== this.audio ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper._clearAudio: '
+					+ 'Audio \'' + audio.name + '\' has not been set yet.' );
+
+			}
+
+			this.objects.delete( this.audioManager );
+
+			this.audio = null;
+			this.audioManager = null;
+
+			return this;
+
+		},
+
+		_setupMeshAnimation: function ( mesh, animation ) {
+
+			var objects = this.objects.get( mesh );
+
+			if ( animation !== undefined ) {
+
+				var animations = Array.isArray( animation )
+					? animation : [ animation ];
+
+				objects.mixer = new THREE.AnimationMixer( mesh );
+
+				for ( var i = 0, il = animations.length; i < il; i ++ ) {
+
+					objects.mixer.clipAction( animations[ i ] ).play();
+
+				}
+
+				// TODO: find a workaround not to access ._clip looking like a private property
+				objects.mixer.addEventListener( 'loop', function ( event ) {
+
+					var tracks = event.action._clip.tracks;
+
+					if ( tracks.length > 0 &&
+					     tracks[ 0 ].name.slice( 0, 6 ) !== '.bones' ) return;
+
+					objects.looped = true;
+
+				} );
+
+			}
+
+			objects.ikSolver = this._createCCDIKSolver( mesh );
+			objects.grantSolver = this.createGrantSolver( mesh );
+
+			return this;
+
+		},
+
+		_setupCameraAnimation: function ( camera, animation ) {
+
+			var animations = Array.isArray( animation )
+				? animation : [ animation ];
+
+			var objects = this.objects.get( camera );
+
+			objects.mixer = new THREE.AnimationMixer( camera );
+
+			for ( var i = 0, il = animations.length; i < il; i ++ ) {
+
+				objects.mixer.clipAction( animations[ i ] ).play();
+
+			}
+
+		},
+
+		_setupMeshPhysics: function ( mesh, params ) {
+
+			var objects = this.objects.get( mesh );
+
+			// shared physics is experimental
+
+			if ( params.world === undefined && this.sharedPhysics ) {
+
+				var masterPhysics = this._getMasterPhysics();
+
+				if ( masterPhysics !== null ) world = masterPhysics.world;
+
+			}
+
+			objects.physics = this._createMMDPhysics( mesh, params );
+
+			if ( objects.mixer && params.animationWarmup !== false ) {
+
+				this._animateMesh( mesh, 0 );
+				objects.physics.reset();
+
+			}
+
+			objects.physics.warmup( params.warmup !== undefined ? params.warmup : 60 );
+
+			this._optimizeIK( mesh, true );
+
+		},
+
+		_animateMesh: function ( mesh, delta ) {
+
+			var objects = this.objects.get( mesh );
+
+			var mixer = objects.mixer;
+			var ikSolver = objects.ikSolver;
+			var grantSolver = objects.grantSolver;
+			var physics = objects.physics;
+			var looped = objects.looped;
+
+			// alternate solution to save/restore bones but less performant?
+			//mesh.pose();
+			//this._updatePropertyMixersBuffer( mesh );
+
+			if ( mixer && this.enabled.animation ) {
+
+				this._restoreBones( mesh );
+
+				mixer.update( delta );
+
+				this._saveBones( mesh );
+
+				if ( ikSolver && this.enabled.ik ) {
+
+					mesh.updateMatrixWorld( true );
+					ikSolver.update();
+
+				}
+
+				if ( grantSolver && this.enabled.grant ) {
+
+					grantSolver.update();
+
+				}
+
+			}
+
+			if ( looped === true && this.enabled.physics ) {
+
+				if ( physics && this.configuration.resetPhysicsOnLoop ) physics.reset();
+
+				objects.looped = false;
+
+			}
+
+			if ( physics && this.enabled.physics && ! this.sharedPhysics ) {
+
+				this.onBeforePhysics( mesh );
+				physics.update( delta );
+
+			}
+
+		},
+
+		_animateCamera: function ( camera, delta ) {
+
+			var mixer = this.objects.get( camera ).mixer;
+
+			if ( mixer && this.enabled.cameraAnimation ) {
+
+				mixer.update( delta );
+
+				camera.updateProjectionMatrix();
+
+				camera.up.set( 0, 1, 0 );
+				camera.up.applyQuaternion( camera.quaternion );
+				camera.lookAt( this.cameraTarget.position );
+
+			}
+
+		},
+
+		_optimizeIK: function ( mesh, physicsEnabled ) {
+
+			var iks = mesh.geometry.iks;
+			var bones = mesh.geometry.bones;
+
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+				var ik = iks[ i ];
+				var links = ik.links;
+
+				for ( var j = 0, jl = links.length; j < jl; j ++ ) {
+
+					var link = links[ j ];
+
+					if ( physicsEnabled === true ) {
+
+						// disable IK of the bone the corresponding rigidBody type of which is 1 or 2
+						// because its rotation will be overriden by physics
+						link.enabled = bones[ link.index ].rigidBodyType > 0 ? false : true;
+
+					} else {
+
+						link.enabled = true;
+
+					}
+
+				}
+
+			}
+
+		},
+
+		_createCCDIKSolver: function ( mesh ) {
+
+			if ( THREE.CCDIKSolver === undefined ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper: Import THREE.CCDIKSolver.' );
+
+			}
+
+			return new THREE.CCDIKSolver( mesh, mesh.geometry.iks );
+
+		},
+
+		_createMMDPhysics: function ( mesh, params ) {
+
+			if ( THREE.MMDPhysics === undefined ) {
+
+				throw new Error( 'THREE.MMDPhysics: Import THREE.MMDPhysics.' );
+
+			}
+
+			return new THREE.MMDPhysics(
+				mesh, mesh.geometry.rigidBodies, mesh.geometry.constraints, params );
+
+		},
+
+		/*
+		 * Detects the longest duration and then sets it to them to sync.
+		 * TODO: Not to access private properties ( ._actions and ._clip )
+		 */
+		_syncDuration: function () {
+
+			var max = 0.0;
+
+			var objects = this.objects;
+			var meshes = this.meshes;
+			var camera = this.camera;
+			var audioManager = this.audioManager;
+
+			// get the longest duration
+
+			for ( var i = 0, il = meshes.length; i < il; i ++ ) {
+
+				var mixer = this.objects.get( meshes[ i ] ).mixer;
+
+				if ( mixer === undefined ) continue;
+
+				for ( var j = 0; j < mixer._actions.length; j ++ ) {
+
+					var clip = mixer._actions[ j ]._clip;
+
+					if ( ! objects.has( clip ) ) {
+
+						objects.set( clip, {
+							duration: clip.duration
+						} )
+
+					}
+
+					max = Math.max( max, objects.get( clip ).duration );
+
+				}
+
+			}
+
+			if ( camera !== null ) {
+
+				var mixer = this.objects.get( camera ).mixer;
+
+				if ( mixer !== undefined ) {
+
+					for ( var i = 0, il = mixer._actions.length; i < il; i ++ ) {
+
+						var clip = mixer._actions[ i ]._clip;
+
+						if ( ! objects.has( clip ) ) {
+
+							objects.set( clip, {
+								duration: clip.duration
+							} )
+
+						}
+
+						max = Math.max( max, objects.get( clip ).duration );
+
+					}
+
+				}
+
+			}
+
+			if ( audioManager !== null ) {
+
+				max = Math.max( max, objects.get( audioManager ).duration );
+
+			}
+
+			max += this.configuration.afterglow;
+
+			// update the duration
+
+			for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
+
+				var mixer = this.objects.get( this.meshes[ i ] ).mixer;
+
+				if ( mixer === undefined ) continue;
+
+				for ( var j = 0, jl = mixer._actions.length; j < jl; j ++ ) {
+
+					mixer._actions[ j ]._clip.duration = max;
+
+				}
+
+			}
+
+			if ( camera !== null ) {
+
+				var mixer = this.objects.get( camera ).mixer;
+
+				if ( mixer !== undefined ) {
+
+					for ( var i = 0, il = mixer._actions.length; i < il; i ++ ) {
+
+						mixer._actions[ i ]._clip.duration = max;
+
+					}
+
+				}
+
+			}
+
+			if ( audioManager !== null ) {
+
+				audioManager.duration = max;
+
+			}
+
+		},
+
+		// workaround
+
+		_updatePropertyMixersBuffer: function ( mesh ) {
+
+			var mixer = this.objects.get( mesh ).mixer;
+
+			var propertyMixers = mixer._bindings;
+			var accuIndex = mixer._accuIndex;
+
+			for ( var i = 0, il = propertyMixers.length; i < il; i ++ ) {
+
+				var propertyMixer = propertyMixers[ i ];
+				var buffer = propertyMixer.buffer;
+				var stride = propertyMixer.valueSize;
+				var offset = ( accuIndex + 1 ) * stride;
+
+				propertyMixer.binding.getValue( buffer, offset );
+
+			}
+
+		},
+
+		/*
+		 * Avoiding these two issues by restore/save bones before/after mixer animation.
+		 *
+		 * 1. PropertyMixer used by AnimationMixer holds cache value in .buffer.
+		 *    Calculating IK, Grant, and Physics after mixer animation can break
+		 *    the cache coherency.
+		 *
+		 * 2. Applying Grant two or more times without reset the posing breaks model.
+		 */
+		_saveBones: function ( mesh ) {
+
+			var objects = this.objects.get( mesh );
+
+			var bones = mesh.skeleton.bones;
+
+			var backupBones = objects.backupBones;
+
+			if ( backupBones === undefined ) {
+
+				backupBones = new Float32Array( bones.length * 7 );
+				objects.backupBones = backupBones;
+
+			}
+
+			for ( var i = 0, il = bones.length; i < il; i ++ ) {
+
+				var bone = bones[ i ];
+				bone.position.toArray( backupBones, i * 7 );
+				bone.quaternion.toArray( backupBones, i * 7 + 3 );
+
+			}
+
+		},
+
+		_restoreBones: function ( mesh ) {
+
+			var objects = this.objects.get( mesh );
+
+			var backupBones = objects.backupBones;
+
+			if ( backupBones === undefined ) return;
+
+			var bones = mesh.skeleton.bones;
+
+			for ( var i = 0, il = bones.length; i < il; i ++ ) {
+
+				var bone = bones[ i ];
+				bone.position.fromArray( backupBones, i * 7 );
+				bone.quaternion.fromArray( backupBones, i * 7 + 3 );
+
+			}
+
+		},
+
+		// experimental
+
+		_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;
+
+		},
+
+		_updateSharedPhysics: function ( delta ) {
+
+			if ( this.meshes.length === 0 || ! this.enabled.physics || ! 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();
+
+				}
+
+			}
+
+		}
+
+	};
+
+	//
+
+	/**
+	 * @param {THREE.Audio} audio
+	 * @param {Object} params - (optional)
+	 * @param {Nuumber} params.delayTime
+	 */
+	function AudioManager( audio, params ) {
+
+		params = params || {};
+
+		this.audio = audio;
+
+		this.elapsedTime = 0.0;
+		this.currentTime = 0.0;
+		this.delayTime = params.delayTime !== undefined
+			? params.delayTime : 0.0;
+
+		this.audioDuration = this.audio.buffer.duration;
+		this.duration = this.audioDuration + this.delayTime;
+
+	}
+
+	AudioManager.prototype = {
+
+		constructor: AudioManager,
+
+		/**
+		 * @param {Number} delta
+		 * @return {AudioManager}
+		 */
+		control: function ( delta ) {
+
+			this.elapsed += delta;
+			this.currentTime += delta;
+
+			if ( this._shouldStopAudio() ) this.audio.stop();
+			if ( this._shouldStartAudio() ) this.audio.play();
+
+			return this;
+
+		},
+
+		// private methods
+
+		_shouldStartAudio: function () {
+
+			if ( this.audio.isPlaying ) return false;
+
+			while ( this.currentTime >= this.duration ) {
+
+				this.currentTime -= this.duration;
+
+			}
+
+			if ( this.currentTime < this.delayTime ) return false;
+
+			this.audio.startTime = this.currentTime - this.delayTime;
+
+			return true;
+
+		},
+
+		_shouldStopAudio: function () {
+
+			return this.audio.isPlaying &&
+				this.currentTime >= this.duration;
+
+		}
+
+	};
+
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Array<Object>} grants
+	 */
+	function GrantSolver( mesh, grants ) {
+
+		this.mesh = mesh;
+		this.grants = grants || [];
+
+	}
+
+	GrantSolver.prototype = {
+
+		constructor: GrantSolver,
+
+		/**
+		 * @return {GrantSolver}
+		 */
+		update: function () {
+
+			var quaternion = new THREE.Quaternion();
+
+			return function () {
+
+				var bones = this.mesh.skeleton.bones;
+				var grants = this.grants;
+
+				for ( var i = 0, il = grants.length; i < il; i ++ ) {
+
+					var grant = grants[ i ];
+					var bone = bones[ grant.index ];
+					var parentBone = bones[ grant.parentIndex ];
+
+					if ( grant.isLocal ) {
+
+						// TODO: implement
+						if ( grant.affectPosition ) {
+
+						}
+
+						// TODO: implement
+						if ( grant.affectRotation ) {
+
+						}
+
+					} else {
+
+						// TODO: implement
+						if ( grant.affectPosition ) {
+
+						}
+
+						if ( grant.affectRotation ) {
+
+							quaternion.set( 0, 0, 0, 1 );
+							quaternion.slerp( parentBone.quaternion, grant.ratio );
+							bone.quaternion.multiply( quaternion );
+
+						}
+
+					}
+
+				}
+
+				return this;
+
+			};
+
+		}()
+
+	};
+
+	return MMDAnimationHelper;
+
+} )();

+ 965 - 774
examples/js/animation/MMDPhysics.js

@@ -4,1213 +4,1404 @@
  * Dependencies
  *  - Ammo.js https://github.com/kripken/ammo.js
  *
- * MMD specific Physics class.
- *
- * See THREE.MMDLoader for the passed parameter list of RigidBody/Constraint.
- *
- * Requirement:
- *  - don't change object's scale from (1,1,1) after setting physics to object
+ * MMDPhysics calculates physics with Ammo(Bullet based JavaScript Physics engine)
+ * for MMD model loaded by THREE.MMDLoader.
  *
  * TODO
- *  - optimize for the performance
- *  - use Physijs http://chandlerprall.github.io/Physijs/
- *    and improve the performance by making use of Web worker.
- *  - if possible, make this class being non-MMD specific.
- *  - object scale change support
+ *  - Physics in Worker
  */
 
-THREE.MMDPhysics = function ( mesh, params ) {
+THREE.MMDPhysics = ( function () {
 
-	if ( params === undefined ) params = {};
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Array<Object>} rigidBodyParams
+	 * @param {Array<Object>} (optional) constraintParams
+	 * @param {Object} params - (optional)
+	 * @param {Number} params.unitStep - Default is 1 / 65.
+	 * @param {Integer} params.maxStepNum - Default is 3.
+	 * @param {THREE.Vector3} params.gravity - Default is ( 0, - 9.8 * 10, 0 )
+	 */
+	function MMDPhysics( mesh, rigidBodyParams, constraintParams, params ) {
 
-	this.mesh = mesh;
-	this.helper = new THREE.MMDPhysics.ResourceHelper();
+		if ( typeof Ammo === 'undefined' ) {
 
-	/*
-	 * I don't know why but 1/60 unitStep easily breaks models
-	 * so I set it 1/65 so far.
-	 * Don't set too small unitStep because
-	 * the smaller unitStep can make the performance worse.
-	 */
-	this.unitStep = ( params.unitStep !== undefined ) ? params.unitStep : 1 / 65;
-	this.maxStepNum = ( params.maxStepNum !== undefined ) ? params.maxStepNum : 3;
+			throw new Error( 'THREE.MMDPhysics: Import ammo.js https://github.com/kripken/ammo.js' );
 
-	this.world = params.world !== undefined ? params.world : null;
-	this.bodies = [];
-	this.constraints = [];
+		}
 
-	this.init( mesh );
+		constraintParams = constraintParams || [];
+		params = params || {};
 
-};
+		this.manager = new ResourceManager();
 
-THREE.MMDPhysics.prototype = {
+		this.mesh = mesh;
 
-	constructor: THREE.MMDPhysics,
+		/*
+		 * I don't know why but 1/60 unitStep easily breaks models
+		 * so I set it 1/65 so far.
+		 * Don't set too small unitStep because
+		 * the smaller unitStep can make the performance worse.
+		 */
+		this.unitStep = ( params.unitStep !== undefined ) ? params.unitStep : 1 / 65;
+		this.maxStepNum = ( params.maxStepNum !== undefined ) ? params.maxStepNum : 3;
+		this.gravity = new THREE.Vector3( 0, - 9.8 * 10, 0 );
 
-	init: function ( mesh ) {
+		if ( params.gravity !== undefined ) this.gravity.copy( gravity );
 
-		var parent = mesh.parent;
+		this.world = params.world !== undefined ? params.world : null; // experimental
 
-		if ( parent !== null ) {
+		this.bodies = [];
+		this.constraints = [];
 
-			parent.remove( mesh );
+		this._init( mesh, rigidBodyParams, constraintParams );
 
-		}
+	}
 
-		var helper = this.helper;
-		var currentPosition = helper.allocThreeVector3();
-		var currentRotation = helper.allocThreeEuler();
-		var currentScale = helper.allocThreeVector3();
+	MMDPhysics.prototype = {
 
-		currentPosition.copy( mesh.position );
-		currentRotation.copy( mesh.rotation );
-		currentScale.copy( mesh.scale );
+		constructor: MMDPhysics,
 
-		mesh.position.set( 0, 0, 0 );
-		mesh.rotation.set( 0, 0, 0 );
-		mesh.scale.set( 1, 1, 1 );
+		/**
+		 * Advances Physics calculation and updates bones.
+		 *
+		 * @param {Number} delta - time in second
+		 * @return {THREE.MMDPhysics}
+		 */
+		update: function ( delta ) {
 
-		mesh.updateMatrixWorld( true );
+			var manager = this.manager;
+			var mesh = this.mesh;
 
-		if ( this.world === null ) this.initWorld();
-		this.initRigidBodies();
-		this.initConstraints();
+			// rigid bodies and constrains are for
+			// mesh's world scale (1, 1, 1).
+			// Convert to (1, 1, 1) if it isn't.
 
-		if ( parent !== null ) {
+			var isNonDefaultScale = false;
 
-			parent.add( mesh );
+			var position = manager.allocThreeVector3();
+			var quaternion = manager.allocThreeQuaternion();
+			var scale = manager.allocThreeVector3();
 
-		}
+			mesh.matrixWorld.decompose( position, quaternion, scale );
 
-		mesh.position.copy( currentPosition );
-		mesh.rotation.copy( currentRotation );
-		mesh.scale.copy( currentScale );
+			if ( scale.x !== 1 || scale.y !== 1 || scale.z !== 1 ) {
 
-		mesh.updateMatrixWorld( true );
+				isNonDefaultScale = true;
 
-		this.reset();
+			}
 
-		helper.freeThreeVector3( currentPosition );
-		helper.freeThreeEuler( currentRotation );
-		helper.freeThreeVector3( currentScale );
+			var parent;
 
-	},
+			if ( isNonDefaultScale ) {
 
-	initWorld: function () {
+				parent = mesh.parent;
 
-		var config = new Ammo.btDefaultCollisionConfiguration();
-		var dispatcher = new Ammo.btCollisionDispatcher( config );
-		var cache = new Ammo.btDbvtBroadphase();
-		var solver = new Ammo.btSequentialImpulseConstraintSolver();
-		var world = new Ammo.btDiscreteDynamicsWorld( dispatcher, cache, solver, config );
-		world.setGravity( new Ammo.btVector3( 0, -9.8 * 10, 0 ) );
-		this.world = world;
+				if ( parent !== null ) mesh.parent = null;
 
-	},
+				scale.copy( this.mesh.scale );
 
-	initRigidBodies: function () {
+				mesh.scale.set( 1, 1, 1 );
+				mesh.updateMatrixWorld( true );
 
-		var bodies = this.mesh.geometry.rigidBodies;
+			}
 
-		for ( var i = 0; i < bodies.length; i++ ) {
+			// calculate physics and update bones
 
-			var b = new THREE.MMDPhysics.RigidBody( this.mesh, this.world, bodies[ i ], this.helper );
-			this.bodies.push( b );
+			this._updateRigidBodies();
+			this._stepSimulation( delta );
+			this._updateBones();
 
-		}
+			// restore mesh if converted above
 
-	},
+			if ( isNonDefaultScale ) {
 
-	initConstraints: function () {
+				if ( parent !== null ) parent.parent = parent;
 
-		var constraints = this.mesh.geometry.constraints;
+				mesh.scale.copy( scale );
 
-		for ( var i = 0; i < constraints.length; i++ ) {
+			}
 
-			var params = constraints[ i ];
-			var bodyA = this.bodies[ params.rigidBodyIndex1 ];
-			var bodyB = this.bodies[ params.rigidBodyIndex2 ];
-			var c = new THREE.MMDPhysics.Constraint( this.mesh, this.world, bodyA, bodyB, params, this.helper );
-			this.constraints.push( c );
+			manager.freeThreeVector3( scale );
+			manager.freeThreeQuaternion( quaternion );
+			manager.freeThreeVector3( position );
 
-		}
+			return this;
 
+		},
 
-	},
+		/**
+		 * Resets rigid bodies transorm to current bone's.
+		 *
+		 * @return {THREE.MMDPhysics}
+		 */
+		reset: function () {
 
-	update: function ( delta ) {
+			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
 
-		this.updateRigidBodies();
-		this.stepSimulation( delta );
-		this.updateBones();
+				this.bodies[ i ].reset();
 
-	},
+			}
 
-	stepSimulation: function ( delta ) {
+			return this;
 
-		var unitStep = this.unitStep;
-		var stepTime = delta;
-		var maxStepNum = ( ( delta / unitStep ) | 0 ) + 1;
+		},
 
-		if ( stepTime < unitStep ) {
+		/**
+		 * Warm ups Rigid bodies. Calculates cycles steps.
+		 *
+		 * @param {Integer} cycles
+		 * @return {THREE.MMDPhysics}
+		 */
+		warmup: function ( cycles ) {
 
-			stepTime = unitStep;
-			maxStepNum = 1;
+			for ( var i = 0; i < cycles; i++ ) {
 
-		}
+				this.update( 1 / 60 );
 
-		if ( maxStepNum > this.maxStepNum ) {
+			}
 
-			maxStepNum = this.maxStepNum;
+			return this;
 
-		}
+		},
 
-		this.world.stepSimulation( stepTime, maxStepNum, unitStep );
+		/**
+		 * Sets gravity.
+		 *
+		 * @param {THREE.Vector3} gravity
+		 * @return {MMDPhysicsHelper}
+		 */
+		setGravity: function ( gravity ) {
 
-	},
+			this.world.setGravity( new Ammo.btVector3( gravity.x, gravity.y, gravity.z ) );
+			this.gravity.copy( gravity );
 
-	updateRigidBodies: function () {
+			return this;
 
-		for ( var i = 0; i < this.bodies.length; i++ ) {
+		},
 
-			this.bodies[ i ].updateFromBone();
+		/**
+		 * Creates MMDPhysicsHelper
+		 *
+		 * @return {MMDPhysicsHelper}
+		 */
+		createHelper: function () {
 
-		}
+			return new MMDPhysicsHelper( this.mesh, this );
 
-	},
+		},
 
-	updateBones: function () {
+		// private methods
 
-		for ( var i = 0; i < this.bodies.length; i++ ) {
+		_init: function ( mesh, rigidBodyParams, constraintParams ) {
 
-			this.bodies[ i ].updateBone();
+			var manager = this.manager;
 
-		}
+			// rigid body/constraint parameters are for
+			// mesh's default world transform as position(0, 0, 0),
+			// quaternion(0, 0, 0, 1) and scale(0, 0, 0)
 
-	},
+			var parent = mesh.parent;
 
-	reset: function () {
+			if ( parent !== null ) parent = null;
 
-		for ( var i = 0; i < this.bodies.length; i++ ) {
+			var currentPosition = manager.allocThreeVector3();
+			var currentQuaternion = manager.allocThreeQuaternion();
+			var currentScale = manager.allocThreeVector3();
 
-			this.bodies[ i ].reset();
+			currentPosition.copy( mesh.position );
+			currentQuaternion.copy( mesh.quaternion );
+			currentScale.copy( mesh.scale );
 
-		}
+			mesh.position.set( 0, 0, 0 );
+			mesh.quaternion.set( 0, 0, 0, 1 );
+			mesh.scale.set( 1, 1, 1 );
 
-	},
+			mesh.updateMatrixWorld( true );
 
-	warmup: function ( cycles ) {
+			if ( this.world === null ) {
 
-		for ( var i = 0; i < cycles; i++ ) {
+				this.world = this._createWorld();
+				this.setGravity( this.gravity );
 
-			this.update( 1 / 60 );
+			}
 
-		}
+			this._initRigidBodies( rigidBodyParams );
+			this._initConstraints( constraintParams );
 
-	}
+			if ( parent !== null ) mesh.parent = parent;
 
-};
+			mesh.position.copy( currentPosition );
+			mesh.quaternion.copy( currentQuaternion );
+			mesh.scale.copy( currentScale );
 
-/**
- * This helper class responsibilies are
- *
- * 1. manage Ammo.js and Three.js object resources and
- *    improve the performance and the memory consumption by
- *    reusing objects.
- *
- * 2. provide simple Ammo object operations.
- */
-THREE.MMDPhysics.ResourceHelper = function () {
+			mesh.updateMatrixWorld( true );
 
-	// for Three.js
-	this.threeVector3s = [];
-	this.threeMatrix4s = [];
-	this.threeQuaternions = [];
-	this.threeEulers = [];
+			this.reset();
 
-	// for Ammo.js
-	this.transforms = [];
-	this.quaternions = [];
-	this.vector3s = [];
+			manager.freeThreeVector3( currentPosition );
+			manager.freeThreeQuaternion( currentQuaternion );
+			manager.freeThreeVector3( currentScale );
 
-};
+		},
 
-THREE.MMDPhysics.ResourceHelper.prototype = {
+		_createWorld: function () {
 
-	allocThreeVector3: function () {
+			var config = new Ammo.btDefaultCollisionConfiguration();
+			var dispatcher = new Ammo.btCollisionDispatcher( config );
+			var cache = new Ammo.btDbvtBroadphase();
+			var solver = new Ammo.btSequentialImpulseConstraintSolver();
+			var world = new Ammo.btDiscreteDynamicsWorld( dispatcher, cache, solver, config );
+			return world;
 
-		return ( this.threeVector3s.length > 0 ) ? this.threeVector3s.pop() : new THREE.Vector3();
+		},
 
-	},
+		_initRigidBodies: function ( rigidBodies ) {
 
-	freeThreeVector3: function ( v ) {
+			for ( var i = 0, il = rigidBodies.length; i < il; i++ ) {
 
-		this.threeVector3s.push( v );
+				this.bodies.push( new RigidBody(
+					this.mesh, this.world, rigidBodies[ i ], this.manager ) );
 
-	},
+			}
 
-	allocThreeMatrix4: function () {
+		},
 
-		return ( this.threeMatrix4s.length > 0 ) ? this.threeMatrix4s.pop() : new THREE.Matrix4();
+		_initConstraints: function ( constraints ) {
 
-	},
+			for ( var i = 0, il = constraints.length; i < il; i++ ) {
 
-	freeThreeMatrix4: function ( m ) {
+				var params = constraints[ i ];
+				var bodyA = this.bodies[ params.rigidBodyIndex1 ];
+				var bodyB = this.bodies[ params.rigidBodyIndex2 ];
+				this.constraints.push( new Constraint(
+					this.mesh, this.world, bodyA, bodyB, params, this.manager ) );
 
-		this.threeMatrix4s.push( m );
+			}
 
-	},
 
-	allocThreeQuaternion: function () {
+		},
 
-		return ( this.threeQuaternions.length > 0 ) ? this.threeQuaternions.pop() : new THREE.Quaternion();
+		_stepSimulation: function ( delta ) {
 
-	},
+			var unitStep = this.unitStep;
+			var stepTime = delta;
+			var maxStepNum = ( ( delta / unitStep ) | 0 ) + 1;
 
-	freeThreeQuaternion: function ( q ) {
+			if ( stepTime < unitStep ) {
 
-		this.threeQuaternions.push( q );
+				stepTime = unitStep;
+				maxStepNum = 1;
 
-	},
+			}
 
-	allocThreeEuler: function () {
+			if ( maxStepNum > this.maxStepNum ) {
 
-		return ( this.threeEulers.length > 0 ) ? this.threeEulers.pop() : new THREE.Euler();
+				maxStepNum = this.maxStepNum;
 
-	},
+			}
 
-	freeThreeEuler: function ( e ) {
+			this.world.stepSimulation( stepTime, maxStepNum, unitStep );
 
-		this.threeEulers.push( e );
+		},
 
-	},
+		_updateRigidBodies: function () {
 
-	allocTransform: function () {
+			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
 
-		return ( this.transforms.length > 0 ) ? this.transforms.pop() : new Ammo.btTransform();
+				this.bodies[ i ].updateFromBone();
 
-	},
+			}
 
-	freeTransform: function ( t ) {
+		},
 
-		this.transforms.push( t );
+		_updateBones: function () {
 
-	},
+			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
 
-	allocQuaternion: function () {
+				this.bodies[ i ].updateBone();
 
-		return ( this.quaternions.length > 0 ) ? this.quaternions.pop() : new Ammo.btQuaternion();
+			}
 
-	},
+		}
 
-	freeQuaternion: function ( q ) {
+	};
 
-		this.quaternions.push( q );
+	/**
+	 * This manager's responsibilies are
+	 *
+	 * 1. manage Ammo.js and Three.js object resources and
+	 *    improve the performance and the memory consumption by
+	 *    reusing objects.
+	 *
+	 * 2. provide simple Ammo object operations.
+	 */
+	function ResourceManager() {
 
-	},
+		// for Three.js
+		this.threeVector3s = [];
+		this.threeMatrix4s = [];
+		this.threeQuaternions = [];
+		this.threeEulers = [];
 
-	allocVector3: function () {
+		// for Ammo.js
+		this.transforms = [];
+		this.quaternions = [];
+		this.vector3s = [];
 
-		return ( this.vector3s.length > 0 ) ? this.vector3s.pop() : new Ammo.btVector3();
+	}
 
-	},
+	ResourceManager.prototype = {
 
-	freeVector3: function ( v ) {
+		constructor: ResourceManager,
 
-		this.vector3s.push( v );
+		allocThreeVector3: function () {
 
-	},
+			return ( this.threeVector3s.length > 0 )
+				? this.threeVector3s.pop()
+				: new THREE.Vector3();
 
-	setIdentity: function ( t ) {
+		},
 
-		t.setIdentity();
+		freeThreeVector3: function ( v ) {
 
-	},
+			this.threeVector3s.push( v );
 
-	getBasis: function ( t ) {
+		},
 
-		var q = this.allocQuaternion();
-		t.getBasis().getRotation( q );
-		return q;
+		allocThreeMatrix4: function () {
 
-	},
+			return ( this.threeMatrix4s.length > 0 )
+				? this.threeMatrix4s.pop()
+				: new THREE.Matrix4();
 
-	getBasisAsMatrix3: function ( t ) {
+		},
 
-		var q = this.getBasis( t );
-		var m = this.quaternionToMatrix3( q );
-		this.freeQuaternion( q );
-		return m;
+		freeThreeMatrix4: function ( m ) {
 
-	},
+			this.threeMatrix4s.push( m );
 
-	getOrigin: function( t ) {
+		},
 
-		return t.getOrigin();
+		allocThreeQuaternion: function () {
 
-	},
+			return ( this.threeQuaternions.length > 0 )
+				? this.threeQuaternions.pop()
+				: new THREE.Quaternion();
 
-	setOrigin: function( t, v ) {
+		},
 
-		t.getOrigin().setValue( v.x(), v.y(), v.z() );
+		freeThreeQuaternion: function ( q ) {
 
-	},
+			this.threeQuaternions.push( q );
 
-	copyOrigin: function( t1, t2 ) {
+		},
 
-		var o = t2.getOrigin();
-		this.setOrigin( t1, o );
+		allocThreeEuler: function () {
 
-	},
+			return ( this.threeEulers.length > 0 )
+				? this.threeEulers.pop()
+				: new THREE.Euler();
 
-	setBasis: function( t, q ) {
+		},
 
-		t.setRotation( q );
+		freeThreeEuler: function ( e ) {
 
-	},
+			this.threeEulers.push( e );
 
-	setBasisFromMatrix3: function( t, m ) {
+		},
 
-		var q = this.matrix3ToQuaternion( m );
-		this.setBasis( t, q );
-		this.freeQuaternion( q );
+		allocTransform: function () {
 
-	},
+			return ( this.transforms.length > 0 )
+				? this.transforms.pop()
+				: new Ammo.btTransform();
 
-	setOriginFromArray3: function ( t, a ) {
+		},
 
-		t.getOrigin().setValue( a[ 0 ], a[ 1 ], a[ 2 ] );
+		freeTransform: function ( t ) {
 
-	},
+			this.transforms.push( t );
 
-	setOriginFromThreeVector3: function ( t, v ) {
+		},
 
-		t.getOrigin().setValue( v.x, v.y, v.z );
+		allocQuaternion: function () {
 
-	},
+			return ( this.quaternions.length > 0 )
+				? this.quaternions.pop()
+				: new Ammo.btQuaternion();
 
-	setBasisFromArray3: function ( t, a ) {
+		},
 
-		var thQ = this.allocThreeQuaternion();
-		var thE = this.allocThreeEuler();
-		thE.set( a[ 0 ], a[ 1 ], a[ 2 ] );
-		this.setBasisFromThreeQuaternion( t, thQ.setFromEuler( thE ) );
+		freeQuaternion: function ( q ) {
 
-		this.freeThreeEuler( thE );
-		this.freeThreeQuaternion( thQ );
+			this.quaternions.push( q );
 
-	},
+		},
 
-	setBasisFromThreeQuaternion: function ( t, a ) {
+		allocVector3: function () {
 
-		var q = this.allocQuaternion();
+			return ( this.vector3s.length > 0 )
+				? this.vector3s.pop()
+				: new Ammo.btVector3();
 
-		q.setX( a.x );
-		q.setY( a.y );
-		q.setZ( a.z );
-		q.setW( a.w );
-		this.setBasis( t, q );
+		},
 
-		this.freeQuaternion( q );
+		freeVector3: function ( v ) {
 
-	},
+			this.vector3s.push( v );
 
-	multiplyTransforms: function ( t1, t2 ) {
+		},
 
-		var t = this.allocTransform();
-		this.setIdentity( t );
+		setIdentity: function ( t ) {
 
-		var m1 = this.getBasisAsMatrix3( t1 );
-		var m2 = this.getBasisAsMatrix3( t2 );
+			t.setIdentity();
 
-		var o1 = this.getOrigin( t1 );
-		var o2 = this.getOrigin( t2 );
+		},
 
-		var v1 = this.multiplyMatrix3ByVector3( m1, o2 );
-		var v2 = this.addVector3( v1, o1 );
-		this.setOrigin( t, v2 );
+		getBasis: function ( t ) {
 
-		var m3 = this.multiplyMatrices3( m1, m2 );
-		this.setBasisFromMatrix3( t, m3 );
+			var q = this.allocQuaternion();
+			t.getBasis().getRotation( q );
+			return q;
 
-		this.freeVector3( v1 );
-		this.freeVector3( v2 );
+		},
 
-		return t;
+		getBasisAsMatrix3: function ( t ) {
 
-	},
+			var q = this.getBasis( t );
+			var m = this.quaternionToMatrix3( q );
+			this.freeQuaternion( q );
+			return m;
 
-	inverseTransform: function ( t ) {
+		},
 
-		var t2 = this.allocTransform();
+		getOrigin: function( t ) {
 
-		var m1 = this.getBasisAsMatrix3( t );
-		var o = this.getOrigin( t );
+			return t.getOrigin();
 
-		var m2 = this.transposeMatrix3( m1 );
-		var v1 = this.negativeVector3( o );
-		var v2 = this.multiplyMatrix3ByVector3( m2, v1 );
+		},
 
-		this.setOrigin( t2, v2 );
-		this.setBasisFromMatrix3( t2, m2 );
+		setOrigin: function( t, v ) {
 
-		this.freeVector3( v1 );
-		this.freeVector3( v2 );
+			t.getOrigin().setValue( v.x(), v.y(), v.z() );
 
-		return t2;
+		},
 
-	},
+		copyOrigin: function( t1, t2 ) {
 
-	multiplyMatrices3: function ( m1, m2 ) {
+			var o = t2.getOrigin();
+			this.setOrigin( t1, o );
 
-		var m3 = [];
+		},
 
-		var v10 = this.rowOfMatrix3( m1, 0 );
-		var v11 = this.rowOfMatrix3( m1, 1 );
-		var v12 = this.rowOfMatrix3( m1, 2 );
+		setBasis: function( t, q ) {
 
-		var v20 = this.columnOfMatrix3( m2, 0 );
-		var v21 = this.columnOfMatrix3( m2, 1 );
-		var v22 = this.columnOfMatrix3( m2, 2 );
+			t.setRotation( q );
 
-		m3[ 0 ] = this.dotVectors3( v10, v20 );
-		m3[ 1 ] = this.dotVectors3( v10, v21 );
-		m3[ 2 ] = this.dotVectors3( v10, v22 );
-		m3[ 3 ] = this.dotVectors3( v11, v20 );
-		m3[ 4 ] = this.dotVectors3( v11, v21 );
-		m3[ 5 ] = this.dotVectors3( v11, v22 );
-		m3[ 6 ] = this.dotVectors3( v12, v20 );
-		m3[ 7 ] = this.dotVectors3( v12, v21 );
-		m3[ 8 ] = this.dotVectors3( v12, v22 );
+		},
 
-		this.freeVector3( v10 );
-		this.freeVector3( v11 );
-		this.freeVector3( v12 );
-		this.freeVector3( v20 );
-		this.freeVector3( v21 );
-		this.freeVector3( v22 );
+		setBasisFromMatrix3: function( t, m ) {
 
-		return m3;
+			var q = this.matrix3ToQuaternion( m );
+			this.setBasis( t, q );
+			this.freeQuaternion( q );
 
-	},
+		},
 
-	addVector3: function( v1, v2 ) {
+		setOriginFromArray3: function ( t, a ) {
 
-		var v = this.allocVector3();
-		v.setValue( v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z() );
-		return v;
+			t.getOrigin().setValue( a[ 0 ], a[ 1 ], a[ 2 ] );
 
-	},
+		},
 
-	dotVectors3: function( v1, v2 ) {
+		setOriginFromThreeVector3: function ( t, v ) {
 
-		return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
+			t.getOrigin().setValue( v.x, v.y, v.z );
 
-	},
+		},
 
-	rowOfMatrix3: function( m, i ) {
+		setBasisFromArray3: function ( t, a ) {
 
-		var v = this.allocVector3();
-		v.setValue( m[ i * 3 + 0 ], m[ i * 3 + 1 ], m[ i * 3 + 2 ] );
-		return v;
+			var thQ = this.allocThreeQuaternion();
+			var thE = this.allocThreeEuler();
+			thE.set( a[ 0 ], a[ 1 ], a[ 2 ] );
+			this.setBasisFromThreeQuaternion( t, thQ.setFromEuler( thE ) );
 
-	},
+			this.freeThreeEuler( thE );
+			this.freeThreeQuaternion( thQ );
 
-	columnOfMatrix3: function( m, i ) {
+		},
 
-		var v = this.allocVector3();
-		v.setValue( m[ i + 0 ], m[ i + 3 ], m[ i + 6 ] );
-		return v;
+		setBasisFromThreeQuaternion: function ( t, a ) {
 
-	},
+			var q = this.allocQuaternion();
 
-	negativeVector3: function( v ) {
+			q.setX( a.x );
+			q.setY( a.y );
+			q.setZ( a.z );
+			q.setW( a.w );
+			this.setBasis( t, q );
 
-		var v2 = this.allocVector3();
-		v2.setValue( -v.x(), -v.y(), -v.z() );
-		return v2;
+			this.freeQuaternion( q );
 
-	},
+		},
 
-	multiplyMatrix3ByVector3: function ( m, v ) {
+		multiplyTransforms: function ( t1, t2 ) {
 
-		var v4 = this.allocVector3();
+			var t = this.allocTransform();
+			this.setIdentity( t );
 
-		var v0 = this.rowOfMatrix3( m, 0 );
-		var v1 = this.rowOfMatrix3( m, 1 );
-		var v2 = this.rowOfMatrix3( m, 2 );
-		var x = this.dotVectors3( v0, v );
-		var y = this.dotVectors3( v1, v );
-		var z = this.dotVectors3( v2, v );
+			var m1 = this.getBasisAsMatrix3( t1 );
+			var m2 = this.getBasisAsMatrix3( t2 );
 
-		v4.setValue( x, y, z );
+			var o1 = this.getOrigin( t1 );
+			var o2 = this.getOrigin( t2 );
 
-		this.freeVector3( v0 );
-		this.freeVector3( v1 );
-		this.freeVector3( v2 );
+			var v1 = this.multiplyMatrix3ByVector3( m1, o2 );
+			var v2 = this.addVector3( v1, o1 );
+			this.setOrigin( t, v2 );
 
-		return v4;
+			var m3 = this.multiplyMatrices3( m1, m2 );
+			this.setBasisFromMatrix3( t, m3 );
 
-	},
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
 
-	transposeMatrix3: function( m ) {
+			return t;
 
-		var m2 = [];
-		m2[ 0 ] = m[ 0 ];
-		m2[ 1 ] = m[ 3 ];
-		m2[ 2 ] = m[ 6 ];
-		m2[ 3 ] = m[ 1 ];
-		m2[ 4 ] = m[ 4 ];
-		m2[ 5 ] = m[ 7 ];
-		m2[ 6 ] = m[ 2 ];
-		m2[ 7 ] = m[ 5 ];
-		m2[ 8 ] = m[ 8 ];
-		return m2;
+		},
 
-	},
+		inverseTransform: function ( t ) {
 
-	quaternionToMatrix3: function ( q ) {
+			var t2 = this.allocTransform();
 
-		var m = [];
+			var m1 = this.getBasisAsMatrix3( t );
+			var o = this.getOrigin( t );
 
-		var x = q.x();
-		var y = q.y();
-		var z = q.z();
-		var w = q.w();
+			var m2 = this.transposeMatrix3( m1 );
+			var v1 = this.negativeVector3( o );
+			var v2 = this.multiplyMatrix3ByVector3( m2, v1 );
 
-		var xx = x * x;
-		var yy = y * y;
-		var zz = z * z;
+			this.setOrigin( t2, v2 );
+			this.setBasisFromMatrix3( t2, m2 );
 
-		var xy = x * y;
-		var yz = y * z;
-		var zx = z * x;
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
 
-		var xw = x * w;
-		var yw = y * w;
-		var zw = z * w;
+			return t2;
 
-		m[ 0 ] = 1 - 2 * ( yy + zz );
-		m[ 1 ] = 2 * ( xy - zw );
-		m[ 2 ] = 2 * ( zx + yw );
-		m[ 3 ] = 2 * ( xy + zw );
-		m[ 4 ] = 1 - 2 * ( zz + xx );
-		m[ 5 ] = 2 * ( yz - xw );
-		m[ 6 ] = 2 * ( zx - yw );
-		m[ 7 ] = 2 * ( yz + xw );
-		m[ 8 ] = 1 - 2 * ( xx + yy );
+		},
 
-		return m;
+		multiplyMatrices3: function ( m1, m2 ) {
 
-	},
+			var m3 = [];
 
-	matrix3ToQuaternion: function( m ) {
+			var v10 = this.rowOfMatrix3( m1, 0 );
+			var v11 = this.rowOfMatrix3( m1, 1 );
+			var v12 = this.rowOfMatrix3( m1, 2 );
 
-		var t = m[ 0 ] + m[ 4 ] + m[ 8 ];
-		var s, x, y, z, w;
+			var v20 = this.columnOfMatrix3( m2, 0 );
+			var v21 = this.columnOfMatrix3( m2, 1 );
+			var v22 = this.columnOfMatrix3( m2, 2 );
 
-		if( t > 0 ) {
+			m3[ 0 ] = this.dotVectors3( v10, v20 );
+			m3[ 1 ] = this.dotVectors3( v10, v21 );
+			m3[ 2 ] = this.dotVectors3( v10, v22 );
+			m3[ 3 ] = this.dotVectors3( v11, v20 );
+			m3[ 4 ] = this.dotVectors3( v11, v21 );
+			m3[ 5 ] = this.dotVectors3( v11, v22 );
+			m3[ 6 ] = this.dotVectors3( v12, v20 );
+			m3[ 7 ] = this.dotVectors3( v12, v21 );
+			m3[ 8 ] = this.dotVectors3( v12, v22 );
 
-			s = Math.sqrt( t + 1.0 ) * 2;
-			w = 0.25 * s;
-			x = ( m[ 7 ] - m[ 5 ] ) / s;
-			y = ( m[ 2 ] - m[ 6 ] ) / s; 
-			z = ( m[ 3 ] - m[ 1 ] ) / s; 
+			this.freeVector3( v10 );
+			this.freeVector3( v11 );
+			this.freeVector3( v12 );
+			this.freeVector3( v20 );
+			this.freeVector3( v21 );
+			this.freeVector3( v22 );
 
-		} else if( ( m[ 0 ] > m[ 4 ] ) && ( m[ 0 ] > m[ 8 ] ) ) {
+			return m3;
 
-			s = Math.sqrt( 1.0 + m[ 0 ] - m[ 4 ] - m[ 8 ] ) * 2;
-			w = ( m[ 7 ] - m[ 5 ] ) / s;
-			x = 0.25 * s;
-			y = ( m[ 1 ] + m[ 3 ] ) / s;
-			z = ( m[ 2 ] + m[ 6 ] ) / s;
+		},
 
-		} else if( m[ 4 ] > m[ 8 ] ) {
+		addVector3: function( v1, v2 ) {
 
-			s = Math.sqrt( 1.0 + m[ 4 ] - m[ 0 ] - m[ 8 ] ) * 2;
-			w = ( m[ 2 ] - m[ 6 ] ) / s;
-			x = ( m[ 1 ] + m[ 3 ] ) / s;
-			y = 0.25 * s;
-			z = ( m[ 5 ] + m[ 7 ] ) / s;
+			var v = this.allocVector3();
+			v.setValue( v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z() );
+			return v;
 
-		} else {
+		},
 
-			s = Math.sqrt( 1.0 + m[ 8 ] - m[ 0 ] - m[ 4 ] ) * 2;
-			w = ( m[ 3 ] - m[ 1 ] ) / s;
-			x = ( m[ 2 ] + m[ 6 ] ) / s;
-			y = ( m[ 5 ] + m[ 7 ] ) / s;
-			z = 0.25 * s;
+		dotVectors3: function( v1, v2 ) {
 
-		}
+			return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
 
-		var q = this.allocQuaternion();
-		q.setX( x );
-		q.setY( y );
-		q.setZ( z );
-		q.setW( w );
-		return q;
+		},
 
-	}
+		rowOfMatrix3: function( m, i ) {
 
-};
+			var v = this.allocVector3();
+			v.setValue( m[ i * 3 + 0 ], m[ i * 3 + 1 ], m[ i * 3 + 2 ] );
+			return v;
 
-THREE.MMDPhysics.RigidBody = function ( mesh, world, params, helper ) {
+		},
 
-	this.mesh  = mesh;
-	this.world = world;
-	this.params = params;
-	this.helper = helper;
+		columnOfMatrix3: function( m, i ) {
 
-	this.body = null;
-	this.bone = null;
-	this.boneOffsetForm = null;
-	this.boneOffsetFormInverse = null;
+			var v = this.allocVector3();
+			v.setValue( m[ i + 0 ], m[ i + 3 ], m[ i + 6 ] );
+			return v;
 
-	this.init();
+		},
 
-};
+		negativeVector3: function( v ) {
 
-THREE.MMDPhysics.RigidBody.prototype = {
+			var v2 = this.allocVector3();
+			v2.setValue( -v.x(), -v.y(), -v.z() );
+			return v2;
 
-	constructor: THREE.MMDPhysics.RigidBody,
+		},
 
-	init: function () {
+		multiplyMatrix3ByVector3: function ( m, v ) {
 
-		function generateShape( p ) {
+			var v4 = this.allocVector3();
 
-			switch( p.shapeType ) {
+			var v0 = this.rowOfMatrix3( m, 0 );
+			var v1 = this.rowOfMatrix3( m, 1 );
+			var v2 = this.rowOfMatrix3( m, 2 );
+			var x = this.dotVectors3( v0, v );
+			var y = this.dotVectors3( v1, v );
+			var z = this.dotVectors3( v2, v );
 
-				case 0:
-					return new Ammo.btSphereShape( p.width );
+			v4.setValue( x, y, z );
 
-				case 1:
-					return new Ammo.btBoxShape( new Ammo.btVector3( p.width, p.height, p.depth ) );
+			this.freeVector3( v0 );
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
 
-				case 2:
-					return new Ammo.btCapsuleShape( p.width, p.height );
+			return v4;
 
-				default:
-					throw 'unknown shape type ' + p.shapeType;
+		},
 
-			}
+		transposeMatrix3: function( m ) {
 
-		}
+			var m2 = [];
+			m2[ 0 ] = m[ 0 ];
+			m2[ 1 ] = m[ 3 ];
+			m2[ 2 ] = m[ 6 ];
+			m2[ 3 ] = m[ 1 ];
+			m2[ 4 ] = m[ 4 ];
+			m2[ 5 ] = m[ 7 ];
+			m2[ 6 ] = m[ 2 ];
+			m2[ 7 ] = m[ 5 ];
+			m2[ 8 ] = m[ 8 ];
+			return m2;
 
-		var helper = this.helper;
-		var params = this.params;
-		var bones = this.mesh.skeleton.bones;
-		var bone = ( params.boneIndex === -1 ) ? new THREE.Bone() : bones[ params.boneIndex ];
+		},
 
-		var shape = generateShape( params );
-		var weight = ( params.type === 0 ) ? 0 : params.weight;
-		var localInertia = helper.allocVector3();
-		localInertia.setValue( 0, 0, 0 );
+		quaternionToMatrix3: function ( q ) {
 
-		if( weight !== 0 ) {
+			var m = [];
 
-			shape.calculateLocalInertia( weight, localInertia );
+			var x = q.x();
+			var y = q.y();
+			var z = q.z();
+			var w = q.w();
 
-		}
+			var xx = x * x;
+			var yy = y * y;
+			var zz = z * z;
 
-		var boneOffsetForm = helper.allocTransform();
-		helper.setIdentity( boneOffsetForm );
-		helper.setOriginFromArray3( boneOffsetForm, params.position );
-		helper.setBasisFromArray3( boneOffsetForm, params.rotation );
+			var xy = x * y;
+			var yz = y * z;
+			var zx = z * x;
 
-		var vector = helper.allocThreeVector3();
-		var boneForm = helper.allocTransform();
-		helper.setIdentity( boneForm );
-		helper.setOriginFromThreeVector3( boneForm, bone.getWorldPosition( vector ) );
+			var xw = x * w;
+			var yw = y * w;
+			var zw = z * w;
 
-		var form = helper.multiplyTransforms( boneForm, boneOffsetForm );
-		var state = new Ammo.btDefaultMotionState( form );
+			m[ 0 ] = 1 - 2 * ( yy + zz );
+			m[ 1 ] = 2 * ( xy - zw );
+			m[ 2 ] = 2 * ( zx + yw );
+			m[ 3 ] = 2 * ( xy + zw );
+			m[ 4 ] = 1 - 2 * ( zz + xx );
+			m[ 5 ] = 2 * ( yz - xw );
+			m[ 6 ] = 2 * ( zx - yw );
+			m[ 7 ] = 2 * ( yz + xw );
+			m[ 8 ] = 1 - 2 * ( xx + yy );
 
-		var info = new Ammo.btRigidBodyConstructionInfo( weight, state, shape, localInertia );
-		info.set_m_friction( params.friction );
-		info.set_m_restitution( params.restitution );
+			return m;
 
-		var body = new Ammo.btRigidBody( info );
+		},
 
-		if ( params.type === 0 ) {
+		matrix3ToQuaternion: function( m ) {
 
-			body.setCollisionFlags( body.getCollisionFlags() | 2 );
+			var t = m[ 0 ] + m[ 4 ] + m[ 8 ];
+			var s, x, y, z, w;
 
-			/*
-			 * It'd be better to comment out this line though in general I should call this method
-			 * because I'm not sure why but physics will be more like MMD's
-			 * if I comment out.
-			 */
-			body.setActivationState( 4 );
+			if( t > 0 ) {
 
-		}
+				s = Math.sqrt( t + 1.0 ) * 2;
+				w = 0.25 * s;
+				x = ( m[ 7 ] - m[ 5 ] ) / s;
+				y = ( m[ 2 ] - m[ 6 ] ) / s; 
+				z = ( m[ 3 ] - m[ 1 ] ) / s; 
 
-		body.setDamping( params.positionDamping, params.rotationDamping );
-		body.setSleepingThresholds( 0, 0 );
+			} else if( ( m[ 0 ] > m[ 4 ] ) && ( m[ 0 ] > m[ 8 ] ) ) {
 
-		this.world.addRigidBody( body, 1 << params.groupIndex, params.groupTarget );
+				s = Math.sqrt( 1.0 + m[ 0 ] - m[ 4 ] - m[ 8 ] ) * 2;
+				w = ( m[ 7 ] - m[ 5 ] ) / s;
+				x = 0.25 * s;
+				y = ( m[ 1 ] + m[ 3 ] ) / s;
+				z = ( m[ 2 ] + m[ 6 ] ) / s;
 
-		this.body = body;
-		this.bone = bone;
-		this.boneOffsetForm = boneOffsetForm;
-		this.boneOffsetFormInverse = helper.inverseTransform( boneOffsetForm );
+			} else if( m[ 4 ] > m[ 8 ] ) {
 
-		helper.freeVector3( localInertia );
-		helper.freeTransform( form );
-		helper.freeTransform( boneForm );
-		helper.freeThreeVector3( vector );
+				s = Math.sqrt( 1.0 + m[ 4 ] - m[ 0 ] - m[ 8 ] ) * 2;
+				w = ( m[ 2 ] - m[ 6 ] ) / s;
+				x = ( m[ 1 ] + m[ 3 ] ) / s;
+				y = 0.25 * s;
+				z = ( m[ 5 ] + m[ 7 ] ) / s;
 
-	},
+			} else {
 
-	reset: function () {
+				s = Math.sqrt( 1.0 + m[ 8 ] - m[ 0 ] - m[ 4 ] ) * 2;
+				w = ( m[ 3 ] - m[ 1 ] ) / s;
+				x = ( m[ 2 ] + m[ 6 ] ) / s;
+				y = ( m[ 5 ] + m[ 7 ] ) / s;
+				z = 0.25 * s;
 
-		this.setTransformFromBone();
+			}
 
-	},
+			var q = this.allocQuaternion();
+			q.setX( x );
+			q.setY( y );
+			q.setZ( z );
+			q.setW( w );
+			return q;
 
-	updateFromBone: function () {
+		}
 
-		if ( this.params.boneIndex === -1 ) {
+	};
 
-			return;
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Ammo.btDiscreteDynamicsWorld} world
+	 * @param {Object} params
+	 * @param {ResourceManager} manager
+	 */
+	function RigidBody( mesh, world, params, manager ) {
 
-		}
+		this.mesh  = mesh;
+		this.world = world;
+		this.params = params;
+		this.manager = manager;
 
-		if ( this.params.type === 0 ) {
+		this.body = null;
+		this.bone = null;
+		this.boneOffsetForm = null;
+		this.boneOffsetFormInverse = null;
 
-			this.setTransformFromBone();
+		this._init();
 
-		}
+	}
 
-	},
+	RigidBody.prototype = {
 
-	updateBone: function () {
+		constructor: MMDPhysics.RigidBody,
 
-		if ( this.params.type === 0 || this.params.boneIndex === -1 ) {
+		/**
+		 * Resets rigid body transform to the current bone's.
+		 *
+		 * @return {RigidBody}
+		 */
+		reset: function () {
 
-			return;
+			this._setTransformFromBone();
+			return this;
 
-		}
+		},
 
-		this.updateBoneRotation();
+		/**
+		 * Updates rigid body's transform from the current bone.
+		 *
+		 * @return {RidigBody}
+		 */
+		updateFromBone: function () {
 
-		if ( this.params.type === 1 ) {
+			if ( this.params.boneIndex !== - 1 &&
+				this.params.type === 0 ) {
 
-			this.updateBonePosition();
+				this._setTransformFromBone();
 
-		}
+			}
+
+			return this;
 
-		this.bone.updateMatrixWorld( true );
+		},
 
-		if ( this.params.type === 2 ) {
+		/**
+		 * Updates bone from the current ridid body's transform.
+		 *
+		 * @return {RidigBody}
+		 */
+		updateBone: function () {
 
-			this.setPositionFromBone();
+			if ( this.params.type === 0 ||
+				this.params.boneIndex === - 1 ) {
 
-		}
+				return this;
 
-	},
+			}
 
-	getBoneTransform: function () {
+			this._updateBoneRotation();
 
-		var helper = this.helper;
-		var p = helper.allocThreeVector3();
-		var q = helper.allocThreeQuaternion();
-		var s = helper.allocThreeVector3();
+			if ( this.params.type === 1 ) {
 
-		this.bone.matrixWorld.decompose( p, q, s );
+				this._updateBonePosition();
 
-		var tr = helper.allocTransform();
-		helper.setOriginFromThreeVector3( tr, p );
-		helper.setBasisFromThreeQuaternion( tr, q );
+			}
 
-		var form = helper.multiplyTransforms( tr, this.boneOffsetForm );
+			this.bone.updateMatrixWorld( true );
 
-		helper.freeTransform( tr );
-		helper.freeThreeVector3( s );
-		helper.freeThreeQuaternion( q );
-		helper.freeThreeVector3( p );
+			if ( this.params.type === 2 ) {
 
-		return form;
+				this._setPositionFromBone();
 
-	},
+			}
 
-	getWorldTransformForBone: function () {
+			return this;
 
-		var helper = this.helper;
+		},
 
-		var tr = helper.allocTransform();
-		this.body.getMotionState().getWorldTransform( tr );
-		var tr2 = helper.multiplyTransforms( tr, this.boneOffsetFormInverse );
+		// private methods
 
-		helper.freeTransform( tr );
+		_init: function () {
 
-		return tr2;
+			function generateShape( p ) {
 
-	},
+				switch( p.shapeType ) {
 
-	setTransformFromBone: function () {
+					case 0:
+						return new Ammo.btSphereShape( p.width );
 
-		var helper = this.helper;
-		var form = this.getBoneTransform();
+					case 1:
+						return new Ammo.btBoxShape( new Ammo.btVector3( p.width, p.height, p.depth ) );
 
-		// TODO: check the most appropriate way to set
-		//this.body.setWorldTransform( form );
-		this.body.setCenterOfMassTransform( form );
-		this.body.getMotionState().setWorldTransform( form );
+					case 2:
+						return new Ammo.btCapsuleShape( p.width, p.height );
 
-		helper.freeTransform( form );
+					default:
+						throw 'unknown shape type ' + p.shapeType;
 
-	},
+				}
 
-	setPositionFromBone: function () {
+			}
 
-		var helper = this.helper;
-		var form = this.getBoneTransform();
+			var manager = this.manager;
+			var params = this.params;
+			var bones = this.mesh.skeleton.bones;
+			var bone = ( params.boneIndex === - 1 )
+				? new THREE.Bone()
+				: bones[ params.boneIndex ];
 
-		var tr = helper.allocTransform();
-		this.body.getMotionState().getWorldTransform( tr );
-		helper.copyOrigin( tr, form );
+			var shape = generateShape( params );
+			var weight = ( params.type === 0 ) ? 0 : params.weight;
+			var localInertia = manager.allocVector3();
+			localInertia.setValue( 0, 0, 0 );
 
-		// TODO: check the most appropriate way to set
-		//this.body.setWorldTransform( tr );
-		this.body.setCenterOfMassTransform( tr );
-		this.body.getMotionState().setWorldTransform( tr );
+			if( weight !== 0 ) {
 
-		helper.freeTransform( tr );
-		helper.freeTransform( form );
+				shape.calculateLocalInertia( weight, localInertia );
 
-	},
+			}
 
-	updateBoneRotation: function () {
+			var boneOffsetForm = manager.allocTransform();
+			manager.setIdentity( boneOffsetForm );
+			manager.setOriginFromArray3( boneOffsetForm, params.position );
+			manager.setBasisFromArray3( boneOffsetForm, params.rotation );
 
-		var helper = this.helper;
+			var vector = manager.allocThreeVector3();
+			var boneForm = manager.allocTransform();
+			manager.setIdentity( boneForm );
+			manager.setOriginFromThreeVector3( boneForm, bone.getWorldPosition( vector ) );
 
-		var tr = this.getWorldTransformForBone();
-		var q = helper.getBasis( tr );
+			var form = manager.multiplyTransforms( boneForm, boneOffsetForm );
+			var state = new Ammo.btDefaultMotionState( form );
 
-		var thQ = helper.allocThreeQuaternion();
-		var thQ2 = helper.allocThreeQuaternion();
-		var thQ3 = helper.allocThreeQuaternion();
+			var info = new Ammo.btRigidBodyConstructionInfo( weight, state, shape, localInertia );
+			info.set_m_friction( params.friction );
+			info.set_m_restitution( params.restitution );
 
-		thQ.set( q.x(), q.y(), q.z(), q.w() );
-		thQ2.setFromRotationMatrix( this.bone.matrixWorld );
-		thQ2.conjugate();
-		thQ2.multiply( thQ );
+			var body = new Ammo.btRigidBody( info );
 
-		//this.bone.quaternion.multiply( thQ2 );
+			if ( params.type === 0 ) {
 
-		thQ3.setFromRotationMatrix( this.bone.matrix );
-		this.bone.quaternion.copy( thQ2.multiply( thQ3 ) );
+				body.setCollisionFlags( body.getCollisionFlags() | 2 );
 
-		helper.freeThreeQuaternion( thQ );
-		helper.freeThreeQuaternion( thQ2 );
-		helper.freeThreeQuaternion( thQ3 );
+				/*
+				 * It'd be better to comment out this line though in general I should call this method
+				 * because I'm not sure why but physics will be more like MMD's
+				 * if I comment out.
+				 */
+				body.setActivationState( 4 );
 
-		helper.freeQuaternion( q );
-		helper.freeTransform( tr );
+			}
 
-	},
+			body.setDamping( params.positionDamping, params.rotationDamping );
+			body.setSleepingThresholds( 0, 0 );
 
-	updateBonePosition: function () {
+			this.world.addRigidBody( body, 1 << params.groupIndex, params.groupTarget );
 
-		var helper = this.helper;
+			this.body = body;
+			this.bone = bone;
+			this.boneOffsetForm = boneOffsetForm;
+			this.boneOffsetFormInverse = manager.inverseTransform( boneOffsetForm );
 
-		var tr = this.getWorldTransformForBone();
+			manager.freeVector3( localInertia );
+			manager.freeTransform( form );
+			manager.freeTransform( boneForm );
+			manager.freeThreeVector3( vector );
 
-		var thV = helper.allocThreeVector3();
+		},
 
-		var o = helper.getOrigin( tr );
-		thV.set( o.x(), o.y(), o.z() );
+		_getBoneTransform: function () {
 
-		var v = this.bone.worldToLocal( thV );
-		this.bone.position.add( v );
+			var manager = this.manager;
+			var p = manager.allocThreeVector3();
+			var q = manager.allocThreeQuaternion();
+			var s = manager.allocThreeVector3();
 
-		helper.freeThreeVector3( thV );
+			this.bone.matrixWorld.decompose( p, q, s );
 
-		helper.freeTransform( tr );
+			var tr = manager.allocTransform();
+			manager.setOriginFromThreeVector3( tr, p );
+			manager.setBasisFromThreeQuaternion( tr, q );
 
-	}
+			var form = manager.multiplyTransforms( tr, this.boneOffsetForm );
 
-};
+			manager.freeTransform( tr );
+			manager.freeThreeVector3( s );
+			manager.freeThreeQuaternion( q );
+			manager.freeThreeVector3( p );
 
-THREE.MMDPhysics.Constraint = function ( mesh, world, bodyA, bodyB, params, helper ) {
+			return form;
 
-	this.mesh  = mesh;
-	this.world = world;
-	this.bodyA = bodyA;
-	this.bodyB = bodyB;
-	this.params = params;
-	this.helper = helper;
+		},
 
-	this.constraint = null;
+		_getWorldTransformForBone: function () {
 
-	this.init();
+			var manager = this.manager;
 
-};
+			var tr = manager.allocTransform();
+			this.body.getMotionState().getWorldTransform( tr );
+			var tr2 = manager.multiplyTransforms( tr, this.boneOffsetFormInverse );
 
-THREE.MMDPhysics.Constraint.prototype = {
+			manager.freeTransform( tr );
 
-	constructor: THREE.MMDPhysics.Constraint,
+			return tr2;
 
-	init: function () {
+		},
 
-		var helper = this.helper;
-		var params = this.params;
-		var bodyA = this.bodyA;
-		var bodyB = this.bodyB;
+		_setTransformFromBone: function () {
 
-		var form = helper.allocTransform();
-		helper.setIdentity( form );
-		helper.setOriginFromArray3( form, params.position );
-		helper.setBasisFromArray3( form, params.rotation );
+			var manager = this.manager;
+			var form = this._getBoneTransform();
 
-		var formA = helper.allocTransform();
-		var formB = helper.allocTransform();
+			// TODO: check the most appropriate way to set
+			//this.body.setWorldTransform( form );
+			this.body.setCenterOfMassTransform( form );
+			this.body.getMotionState().setWorldTransform( form );
 
-		bodyA.body.getMotionState().getWorldTransform( formA );
-		bodyB.body.getMotionState().getWorldTransform( formB );
+			manager.freeTransform( form );
 
-		var formInverseA = helper.inverseTransform( formA );
-		var formInverseB = helper.inverseTransform( formB );
+		},
 
-		var formA2 = helper.multiplyTransforms( formInverseA, form );
-		var formB2 = helper.multiplyTransforms( formInverseB, form );
+		_setPositionFromBone: function () {
 
-		var constraint = new Ammo.btGeneric6DofSpringConstraint( bodyA.body, bodyB.body, formA2, formB2, true );
+			var manager = this.manager;
+			var form = this._getBoneTransform();
 
-		var lll = helper.allocVector3();
-		var lul = helper.allocVector3();
-		var all = helper.allocVector3();
-		var aul = helper.allocVector3();
+			var tr = manager.allocTransform();
+			this.body.getMotionState().getWorldTransform( tr );
+			manager.copyOrigin( tr, form );
 
-		lll.setValue( params.translationLimitation1[ 0 ],
-		              params.translationLimitation1[ 1 ],
-		              params.translationLimitation1[ 2 ] );
-		lul.setValue( params.translationLimitation2[ 0 ],
-		              params.translationLimitation2[ 1 ],
-		              params.translationLimitation2[ 2 ] );
-		all.setValue( params.rotationLimitation1[ 0 ],
-		              params.rotationLimitation1[ 1 ],
-		              params.rotationLimitation1[ 2 ] );
-		aul.setValue( params.rotationLimitation2[ 0 ],
-		              params.rotationLimitation2[ 1 ],
-		              params.rotationLimitation2[ 2 ] );
+			// TODO: check the most appropriate way to set
+			//this.body.setWorldTransform( tr );
+			this.body.setCenterOfMassTransform( tr );
+			this.body.getMotionState().setWorldTransform( tr );
 
-		constraint.setLinearLowerLimit( lll );
-		constraint.setLinearUpperLimit( lul );
-		constraint.setAngularLowerLimit( all );
-		constraint.setAngularUpperLimit( aul );
+			manager.freeTransform( tr );
+			manager.freeTransform( form );
 
-		for ( var i = 0; i < 3; i++ ) {
+		},
 
-			if( params.springPosition[ i ] !== 0 ) {
+		_updateBoneRotation: function () {
 
-				constraint.enableSpring( i, true );
-				constraint.setStiffness( i, params.springPosition[ i ] );
+			var manager = this.manager;
 
-			}
+			var tr = this._getWorldTransformForBone();
+			var q = manager.getBasis( tr );
 
-		}
+			var thQ = manager.allocThreeQuaternion();
+			var thQ2 = manager.allocThreeQuaternion();
+			var thQ3 = manager.allocThreeQuaternion();
 
-		for ( var i = 0; i < 3; i++ ) {
+			thQ.set( q.x(), q.y(), q.z(), q.w() );
+			thQ2.setFromRotationMatrix( this.bone.matrixWorld );
+			thQ2.conjugate();
+			thQ2.multiply( thQ );
 
-			if( params.springRotation[ i ] !== 0 ) {
+			//this.bone.quaternion.multiply( thQ2 );
 
-				constraint.enableSpring( i + 3, true );
-				constraint.setStiffness( i + 3, params.springRotation[ i ] );
+			thQ3.setFromRotationMatrix( this.bone.matrix );
+			this.bone.quaternion.copy( thQ2.multiply( thQ3 ) );
 
-			}
+			manager.freeThreeQuaternion( thQ );
+			manager.freeThreeQuaternion( thQ2 );
+			manager.freeThreeQuaternion( thQ3 );
 
-		}
+			manager.freeQuaternion( q );
+			manager.freeTransform( tr );
 
-		/*
-		 * Currently(10/31/2016) official ammo.js doesn't support
-		 * btGeneric6DofSpringConstraint.setParam method.
-		 * You need custom ammo.js (add the method into idl) if you wanna use.
-		 * By setting this parameter, physics will be more like MMD's
-		 */
-		if ( constraint.setParam !== undefined ) {
+		},
 
-			for ( var i = 0; i < 6; i ++ ) {
+		_updateBonePosition: function () {
 
-				// this parameter is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
-				constraint.setParam( 2, 0.475, i );
+			var manager = this.manager;
 
-			}
+			var tr = this._getWorldTransformForBone();
+
+			var thV = manager.allocThreeVector3();
+
+			var o = manager.getOrigin( tr );
+			thV.set( o.x(), o.y(), o.z() );
+
+			var v = this.bone.worldToLocal( thV );
+			this.bone.position.add( v );
+
+			manager.freeThreeVector3( thV );
+
+			manager.freeTransform( tr );
 
 		}
 
-		this.world.addConstraint( constraint, true );
-		this.constraint = constraint;
-
-		helper.freeTransform( form );
-		helper.freeTransform( formA );
-		helper.freeTransform( formB );
-		helper.freeTransform( formInverseA );
-		helper.freeTransform( formInverseB );
-		helper.freeTransform( formA2 );
-		helper.freeTransform( formB2 );
-		helper.freeVector3( lll );
-		helper.freeVector3( lul );
-		helper.freeVector3( all );
-		helper.freeVector3( aul );
+	};
+
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Ammo.btDiscreteDynamicsWorld} world
+	 * @param {RigidBody} bodyA
+	 * @param {RigidBody} bodyB
+	 * @param {Object} params
+	 * @param {ResourceManager} manager
+	 */
+	function Constraint( mesh, world, bodyA, bodyB, params, manager ) {
+
+		this.mesh  = mesh;
+		this.world = world;
+		this.bodyA = bodyA;
+		this.bodyB = bodyB;
+		this.params = params;
+		this.manager = manager;
+
+		this.constraint = null;
+
+		this._init();
 
 	}
 
-};
+	Constraint.prototype = {
 
+		constructor: Constraint,
 
-THREE.MMDPhysicsHelper = function ( mesh ) {
+		// private method
 
-	if ( mesh.physics === undefined || mesh.geometry.rigidBodies === undefined ) {
+		_init: function () {
 
-		throw 'THREE.MMDPhysicsHelper requires physics in mesh and rigidBodies in mesh.geometry.';
+			var manager = this.manager;
+			var params = this.params;
+			var bodyA = this.bodyA;
+			var bodyB = this.bodyB;
 
-	}
+			var form = manager.allocTransform();
+			manager.setIdentity( form );
+			manager.setOriginFromArray3( form, params.position );
+			manager.setBasisFromArray3( form, params.rotation );
 
-	THREE.Object3D.call( this );
+			var formA = manager.allocTransform();
+			var formB = manager.allocTransform();
 
-	this.root = mesh;
+			bodyA.body.getMotionState().getWorldTransform( formA );
+			bodyB.body.getMotionState().getWorldTransform( formB );
 
-	this.matrix = mesh.matrixWorld;
-	this.matrixAutoUpdate = false;
+			var formInverseA = manager.inverseTransform( formA );
+			var formInverseB = manager.inverseTransform( formB );
 
-	this.materials = [];
+			var formA2 = manager.multiplyTransforms( formInverseA, form );
+			var formB2 = manager.multiplyTransforms( formInverseB, form );
 
-	this.materials.push(
-		new THREE.MeshBasicMaterial( {
-			color: new THREE.Color( 0xff8888 ),
-			wireframe: true,
-			depthTest: false,
-			depthWrite: false,
-			opacity: 0.25,
-			transparent: true
-		} )
-	);
+			var constraint = new Ammo.btGeneric6DofSpringConstraint( bodyA.body, bodyB.body, formA2, formB2, true );
 
-	this.materials.push(
-		new THREE.MeshBasicMaterial( {
-			color: new THREE.Color( 0x88ff88 ),
-			wireframe: true,
-			depthTest: false,
-			depthWrite: false,
-			opacity: 0.25,
-			transparent: true
-		} )
-	);
+			var lll = manager.allocVector3();
+			var lul = manager.allocVector3();
+			var all = manager.allocVector3();
+			var aul = manager.allocVector3();
 
-	this.materials.push(
-		new THREE.MeshBasicMaterial( {
-			color: new THREE.Color( 0x8888ff ),
-			wireframe: true,
-			depthTest: false,
-			depthWrite: false,
-			opacity: 0.25,
-			transparent: true
-		} )
-	);
+			lll.setValue( params.translationLimitation1[ 0 ],
+			              params.translationLimitation1[ 1 ],
+			              params.translationLimitation1[ 2 ] );
+			lul.setValue( params.translationLimitation2[ 0 ],
+			              params.translationLimitation2[ 1 ],
+			              params.translationLimitation2[ 2 ] );
+			all.setValue( params.rotationLimitation1[ 0 ],
+			              params.rotationLimitation1[ 1 ],
+			              params.rotationLimitation1[ 2 ] );
+			aul.setValue( params.rotationLimitation2[ 0 ],
+			              params.rotationLimitation2[ 1 ],
+			              params.rotationLimitation2[ 2 ] );
 
-	this._init();
-	this.update();
+			constraint.setLinearLowerLimit( lll );
+			constraint.setLinearUpperLimit( lul );
+			constraint.setAngularLowerLimit( all );
+			constraint.setAngularUpperLimit( aul );
 
-};
+			for ( var i = 0; i < 3; i++ ) {
 
-THREE.MMDPhysicsHelper.prototype = Object.create( THREE.Object3D.prototype );
-THREE.MMDPhysicsHelper.prototype.constructor = THREE.MMDPhysicsHelper;
+				if( params.springPosition[ i ] !== 0 ) {
 
-THREE.MMDPhysicsHelper.prototype._init = function () {
+					constraint.enableSpring( i, true );
+					constraint.setStiffness( i, params.springPosition[ i ] );
 
-	var mesh = this.root;
-	var rigidBodies = mesh.geometry.rigidBodies;
+				}
 
-	function createGeometry( param ) {
+			}
+
+			for ( var i = 0; i < 3; i++ ) {
+
+				if( params.springRotation[ i ] !== 0 ) {
+
+					constraint.enableSpring( i + 3, true );
+					constraint.setStiffness( i + 3, params.springRotation[ i ] );
 
-		switch ( param.shapeType ) {
+				}
 
-			case 0:
-				return new THREE.SphereBufferGeometry( param.width, 16, 8 );
+			}
+
+			/*
+			 * Currently(10/31/2016) official ammo.js doesn't support
+			 * btGeneric6DofSpringConstraint.setParam method.
+			 * You need custom ammo.js (add the method into idl) if you wanna use.
+			 * By setting this parameter, physics will be more like MMD's
+			 */
+			if ( constraint.setParam !== undefined ) {
 
-			case 1:
-				return new THREE.BoxBufferGeometry( param.width * 2, param.height * 2, param.depth * 2, 8, 8, 8 );
+				for ( var i = 0; i < 6; i ++ ) {
 
-			case 2:
-				return new createCapsuleGeometry( param.width, param.height, 16, 8 );
+					// this parameter is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
+					constraint.setParam( 2, 0.475, i );
 
-			default:
-				return null;
+				}
+
+			}
+
+			this.world.addConstraint( constraint, true );
+			this.constraint = constraint;
+
+			manager.freeTransform( form );
+			manager.freeTransform( formA );
+			manager.freeTransform( formB );
+			manager.freeTransform( formInverseA );
+			manager.freeTransform( formInverseB );
+			manager.freeTransform( formA2 );
+			manager.freeTransform( formB2 );
+			manager.freeVector3( lll );
+			manager.freeVector3( lul );
+			manager.freeVector3( all );
+			manager.freeVector3( aul );
 
 		}
 
+	};
+
+	/**
+	 * Visualize Rigid bodies
+	 *
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {THREE.Physics} physics
+	 */
+	function MMDPhysicsHelper( mesh, physics ) {
+
+		THREE.Object3D.call( this );
+
+		this.root = mesh;
+		this.physics = physics;
+
+		this.matrix.copy( mesh.matrixWorld );
+		this.matrixAutoUpdate = false;
+
+		this.materials = [];
+
+		this.materials.push(
+			new THREE.MeshBasicMaterial( {
+				color: new THREE.Color( 0xff8888 ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this.materials.push(
+			new THREE.MeshBasicMaterial( {
+				color: new THREE.Color( 0x88ff88 ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this.materials.push(
+			new THREE.MeshBasicMaterial( {
+				color: new THREE.Color( 0x8888ff ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this._init();
+
 	}
 
-	// copy from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mytest37.js?ver=20160815
-	function createCapsuleGeometry( radius, cylinderHeight, segmentsRadius, segmentsHeight ) {
+	MMDPhysicsHelper.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), {
 
-		var geometry = new THREE.CylinderBufferGeometry( radius, radius, cylinderHeight, segmentsRadius, segmentsHeight, true );
-		var upperSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( radius, segmentsRadius, segmentsHeight, 0, Math.PI * 2, 0, Math.PI / 2 ) );
-		var lowerSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( radius, segmentsRadius, segmentsHeight, 0, Math.PI * 2, Math.PI / 2, Math.PI / 2 ) );
+		constructor: MMDPhysicsHelper,
 
-		upperSphere.position.set( 0, cylinderHeight / 2, 0 );
-		lowerSphere.position.set( 0, -cylinderHeight / 2, 0 );
+		/**
+		 * Updates Rigid Bodies visualization.
+		 */
+		updateMatrixWorld: function () {
 
-		upperSphere.updateMatrix();
-		lowerSphere.updateMatrix();
+			var position = new THREE.Vector3();
+			var quaternion = new THREE.Quaternion();
+			var scale = new THREE.Vector3();
+			var matrixWorldInv = new THREE.Matrix4();
 
-		geometry.merge( upperSphere.geometry, upperSphere.matrix );
-		geometry.merge( lowerSphere.geometry, lowerSphere.matrix );
+			return function updateMatrixWorld( force ) {
 
-		return geometry;
+				var mesh = this.root;
 
-	}
+				if ( this.visible ) {
 
-	for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
+					var bodies = this.physics.bodies;
 
-		var param = rigidBodies[ i ];
-		this.add( new THREE.Mesh( createGeometry( param ), this.materials[ param.type ] ) );
+					matrixWorldInv
+						.copy( mesh.matrixWorld )
+						.decompose( position, quaternion, scale )
+						.compose( position, quaternion, scale.set( 1, 1, 1 ) )
+						.getInverse( matrixWorldInv );
 
-	}
+					for ( var i = 0, il = bodies.length; i < il; i ++ ) {
 
-};
+						var body = bodies[ i ].body;
+						var child = this.children[ i ];
 
-THREE.MMDPhysicsHelper.prototype.update = function () {
+						var tr = body.getCenterOfMassTransform();
+						var origin = tr.getOrigin();
+						var rotation = tr.getRotation();
 
-	var mesh = this.root;
-	var rigidBodies = mesh.geometry.rigidBodies;
-	var bodies = mesh.physics.bodies;
+						child.position
+							.set( origin.x(), origin.y(), origin.z() )
+							.applyMatrix4( matrixWorldInv );
 
-	var matrixWorldInv = new THREE.Matrix4().getInverse( mesh.matrixWorld );
-	var vector = new THREE.Vector3();
-	var quaternion = new THREE.Quaternion();
-	var quaternion2 = new THREE.Quaternion();
+						child.quaternion
+							.setFromRotationMatrix( matrixWorldInv )
+							.multiply(
+								quaternion.set(
+									rotation.x(), rotation.y(), rotation.z(), rotation.w() )
+							);
 
-	function getPosition( origin ) {
+					}
 
-		vector.set( origin.x(), origin.y(), origin.z() );
-		vector.applyMatrix4( matrixWorldInv );
+				}
 
-		return vector;
+				this.matrix
+					.copy( mesh.matrixWorld )
+					.decompose( position, quaternion, scale )
+					.compose( position, quaternion, scale.set( 1, 1, 1 ) );
 
-	}
+				THREE.Object3D.prototype.updateMatrixWorld.call( this, force );
 
-	function getQuaternion( rotation ) {
+			};
 
-		quaternion.set( rotation.x(), rotation.y(), rotation.z(), rotation.w() );
-		quaternion2.setFromRotationMatrix( matrixWorldInv );
-		quaternion2.multiply( quaternion );
+		}(),
 
-		return quaternion2;
+		// private method
 
-	}
+		_init: function () {
 
-	for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
+			var mesh = this.root;
+			var bodies = this.physics.bodies;
 
-		var body = bodies[ i ].body;
-		var mesh = this.children[ i ];
+			function createGeometry( param ) {
 
-		var tr = body.getCenterOfMassTransform();
+				switch ( param.shapeType ) {
 
-		mesh.position.copy( getPosition( tr.getOrigin() ) );
-		mesh.quaternion.copy( getQuaternion( tr.getRotation() ) );
+					case 0:
+						return new THREE.SphereBufferGeometry( param.width, 16, 8 );
 
-	}
+					case 1:
+						return new THREE.BoxBufferGeometry( param.width * 2, param.height * 2, param.depth * 2, 8, 8, 8 );
+
+					case 2:
+						return new createCapsuleGeometry( param.width, param.height, 16, 8 );
+
+					default:
+						return null;
+
+				}
+
+			}
+
+			// copy from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mytest37.js?ver=20160815
+			function createCapsuleGeometry( radius, cylinderHeight, segmentsRadius, segmentsHeight ) {
+
+				var geometry = new THREE.CylinderBufferGeometry( radius, radius, cylinderHeight, segmentsRadius, segmentsHeight, true );
+				var upperSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( radius, segmentsRadius, segmentsHeight, 0, Math.PI * 2, 0, Math.PI / 2 ) );
+				var lowerSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( radius, segmentsRadius, segmentsHeight, 0, Math.PI * 2, Math.PI / 2, Math.PI / 2 ) );
+
+				upperSphere.position.set( 0, cylinderHeight / 2, 0 );
+				lowerSphere.position.set( 0, - cylinderHeight / 2, 0 );
+
+				upperSphere.updateMatrix();
+				lowerSphere.updateMatrix();
+
+				geometry.merge( upperSphere.geometry, upperSphere.matrix );
+				geometry.merge( lowerSphere.geometry, lowerSphere.matrix );
+
+				return geometry;
+
+			}
+
+			for ( var i = 0, il = bodies.length; i < il; i ++ ) {
+
+				var param = bodies[ i ].params;
+				this.add( new THREE.Mesh( createGeometry( param ), this.materials[ param.type ] ) );
+
+			}
+
+		}
+
+	} );
+
+	return MMDPhysics;
 
-};
+} )();

+ 4 - 12
examples/js/controls/TransformControls.js

@@ -227,12 +227,8 @@
 
 		THREE.TransformGizmo.call( this );
 
-		var arrowGeometry = new THREE.Geometry();
-		var mesh = new THREE.Mesh( new THREE.CylinderGeometry( 0, 0.05, 0.2, 12, 1, false ) );
-		mesh.position.y = 0.5;
-		mesh.updateMatrix();
-
-		arrowGeometry.merge( mesh.geometry, mesh.matrix );
+		var arrowGeometry = new THREE.ConeBufferGeometry( 0.05, 0.2, 12, 1, false );
+		arrowGeometry.translate( 0, 0.5, 0 );
 
 		var lineXGeometry = new THREE.BufferGeometry();
 		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
@@ -505,12 +501,8 @@
 
 		THREE.TransformGizmo.call( this );
 
-		var arrowGeometry = new THREE.Geometry();
-		var mesh = new THREE.Mesh( new THREE.BoxGeometry( 0.125, 0.125, 0.125 ) );
-		mesh.position.y = 0.5;
-		mesh.updateMatrix();
-
-		arrowGeometry.merge( mesh.geometry, mesh.matrix );
+		var arrowGeometry = new THREE.BoxBufferGeometry( 0.125, 0.125, 0.125 );
+		arrowGeometry.translate( 0, 0.5, 0 );
 
 		var lineXGeometry = new THREE.BufferGeometry();
 		lineXGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0,  1, 0, 0 ], 3 ) );

+ 32 - 33
examples/js/crossfade/scenes.js

@@ -1,76 +1,75 @@
 function generateGeometry( objectType, numObjects ) {
 
-	var geometry = new THREE.Geometry();
+	function applyVertexColors( geometry, color ) {
 
-	function applyVertexColors( g, c ) {
+		var position = geometry.attributes.position;
+		var colors = [];
 
-		g.faces.forEach( function( f ) {
+		for ( var i = 0; i < position.count; i ++ ) {
 
-			var n = ( f instanceof THREE.Face3 ) ? 3 : 4;
+			colors.push( color.r, color.g, color.b );
 
-			for ( var j = 0; j < n; j ++ ) {
+		}
 
-				f.vertexColors[ j ] = c;
+		geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
-			}
+	}
 
-		} );
+	var geometries = [];
 
-	}
+	var matrix = new THREE.Matrix4();
+	var position = new THREE.Vector3();
+	var rotation = new THREE.Euler();
+	var quaternion = new THREE.Quaternion();
+	var scale = new THREE.Vector3();
+	var color = new THREE.Color();
 
 	for ( var i = 0; i < numObjects; i ++ ) {
 
-		var position = new THREE.Vector3();
-
 		position.x = Math.random() * 10000 - 5000;
 		position.y = Math.random() * 6000 - 3000;
 		position.z = Math.random() * 8000 - 4000;
 
-		var rotation = new THREE.Euler();
-
 		rotation.x = Math.random() * 2 * Math.PI;
 		rotation.y = Math.random() * 2 * Math.PI;
 		rotation.z = Math.random() * 2 * Math.PI;
-
-		var scale = new THREE.Vector3();
-
-		var geom, color = new THREE.Color();
+		quaternion.setFromEuler( rotation, false );
 
 		scale.x = Math.random() * 200 + 100;
 
-		if ( objectType == "cube" ) {
+		var geometry;
+
+		if ( objectType === 'cube' ) {
 
-			geom = new THREE.BoxGeometry( 1, 1, 1 );
+			geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
+			geometry = geometry.toNonIndexed(); // merging needs consistent buffer geometries
 			scale.y = Math.random() * 200 + 100;
 			scale.z = Math.random() * 200 + 100;
-			color.setRGB( 0, 0, Math.random() + 0.1 );
+			color.setRGB( 0, 0, 0.1 + 0.9 * Math.random() );
 
-		} else if ( objectType == "sphere" ) {
+		} else if ( objectType === 'sphere' ) {
 
-			geom = new THREE.IcosahedronGeometry( 1, 1 );
+			geometry = new THREE.IcosahedronBufferGeometry( 1, 1 );
 			scale.y = scale.z = scale.x;
-			color.setRGB( Math.random() + 0.1, 0, 0 );
+			color.setRGB( 0.1 + 0.9 * Math.random(), 0, 0 );
 
 		}
 
 		// give the geom's vertices a random color, to be displayed
-		applyVertexColors( geom, color );
+		applyVertexColors( geometry, color );
 
-		var mesh = new THREE.Mesh( geom );
-		mesh.position.copy( position );
-		mesh.rotation.copy( rotation );
-		mesh.scale.copy( scale );
-		mesh.updateMatrix();
+		matrix.compose( position, quaternion, scale );
+		geometry.applyMatrix( matrix );
 
-		geometry.merge( mesh.geometry, mesh.matrix );
+		geometries.push( geometry );
 
 	}
 
-	return geometry;
+	return THREE.BufferGeometryUtils.mergeBufferGeometries( geometries );
 
 }
 
-function Scene ( type, numObjects, cameraZ, fov, rotationSpeed, clearColor ) {
+function Scene( type, numObjects, cameraZ, fov, rotationSpeed, clearColor ) {
 
 	this.clearColor = clearColor;
 
@@ -94,7 +93,7 @@ function Scene ( type, numObjects, cameraZ, fov, rotationSpeed, clearColor ) {
 	var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
 	this.fbo = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, renderTargetParameters );
 
-	this.render = function( delta, rtt ) {
+	this.render = function ( delta, rtt ) {
 
 		this.mesh.rotation.x += delta * this.rotationSpeed.x;
 		this.mesh.rotation.y += delta * this.rotationSpeed.y;

+ 8 - 8
examples/js/effects/OutlineEffect.js

@@ -6,15 +6,15 @@
  * // How to set default outline parameters
  * new THREE.OutlineEffect( renderer, {
  * 	defaultThickNess: 0.01,
- * 	defaultColor: new THREE.Color( 0x888888 ),
+ * 	defaultColor: [ 0, 0, 0 ],
  * 	defaultAlpha: 0.8,
  * 	defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
  * } );
  *
  * // How to set outline parameters for each material
- * material.outlineParameters = {
+ * material.userData.outlineParameters = {
  * 	thickNess: 0.01,
- * 	color: new THREE.Color( 0x888888 ),
+ * 	color: [ 0, 0, 0 ]
  * 	alpha: 0.8,
  * 	visible: true,
  * 	keepAlive: true
@@ -31,7 +31,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 	this.enabled = true;
 
 	var defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
-	var defaultColor = parameters.defaultColor !== undefined ? parameters.defaultColor : new THREE.Color( 0x000000 );
+	var defaultColor = new THREE.Color().fromArray( parameters.defaultColor !== undefined ? parameters.defaultColor : [ 0, 0, 0 ] );
 	var defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
 	var defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
 
@@ -140,7 +140,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 		var shaderID = shaderIDs[ originalMaterial.type ];
 		var originalUniforms, originalVertexShader;
-		var outlineParameters = originalMaterial.outlineParameters;
+		var outlineParameters = originalMaterial.userData.outlineParameters;
 
 		if ( shaderID !== undefined ) {
 
@@ -300,14 +300,14 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 	function updateUniforms( material, originalMaterial ) {
 
-		var outlineParameters = originalMaterial.outlineParameters;
+		var outlineParameters = originalMaterial.userData.outlineParameters;
 
 		material.uniforms.outlineAlpha.value = originalMaterial.opacity;
 
 		if ( outlineParameters !== undefined ) {
 
 			if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
-			if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.copy( outlineParameters.color );
+			if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.fromArray( outlineParameters.color );
 			if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
 
 		}
@@ -318,7 +318,7 @@ THREE.OutlineEffect = function ( renderer, parameters ) {
 
 		if ( material.name === 'invisible' ) return;
 
-		var outlineParameters = originalMaterial.outlineParameters;
+		var outlineParameters = originalMaterial.userData.outlineParameters;
 
 		material.skinning = originalMaterial.skinning;
 		material.morphTargets = originalMaterial.morphTargets;

File diff suppressed because it is too large
+ 0 - 1
examples/js/libs/draco/gltf/draco_decoder.js


BIN
examples/js/libs/draco/gltf/draco_decoder.wasm


File diff suppressed because it is too large
+ 3 - 0
examples/js/libs/draco/gltf/draco_encoder.js


+ 119 - 115
examples/js/libs/draco/gltf/draco_wasm_wrapper.js

@@ -1,115 +1,119 @@
-var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(c,f,n){c!=Array.prototype&&c!=Object.prototype&&(c[f]=n.value)};$jscomp.getGlobal=function(c){return"undefined"!=typeof window&&window===c?c:"undefined"!=typeof global&&null!=global?global:c};$jscomp.global=$jscomp.getGlobal(this);
-$jscomp.polyfill=function(c,f,n,D){if(f){n=$jscomp.global;c=c.split(".");for(D=0;D<c.length-1;D++){var g=c[D];g in n||(n[g]={});n=n[g]}c=c[c.length-1];D=n[c];f=f(D);f!=D&&null!=f&&$jscomp.defineProperty(n,c,{configurable:!0,writable:!0,value:f})}};$jscomp.polyfill("Math.imul",function(c){return c?c:function(f,c){f=Number(f);c=Number(c);var n=f&65535,g=c&65535;return n*g+((f>>>16&65535)*g+n*(c>>>16&65535)<<16>>>0)|0}},"es6","es3");
-$jscomp.polyfill("Math.clz32",function(c){return c?c:function(f){f=Number(f)>>>0;if(0===f)return 32;var c=0;0===(f&4294901760)&&(f<<=16,c+=16);0===(f&4278190080)&&(f<<=8,c+=8);0===(f&4026531840)&&(f<<=4,c+=4);0===(f&3221225472)&&(f<<=2,c+=2);0===(f&2147483648)&&c++;return c}},"es6","es3");$jscomp.polyfill("Math.trunc",function(c){return c?c:function(c){c=Number(c);if(isNaN(c)||Infinity===c||-Infinity===c||0===c)return c;var f=Math.floor(Math.abs(c));return 0>c?-f:f}},"es6","es3");
-$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(c){return $jscomp.SYMBOL_PREFIX+(c||"")+$jscomp.symbolCounter_++};
-$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var c=$jscomp.global.Symbol.iterator;c||(c=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[c]&&$jscomp.defineProperty(Array.prototype,c,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(c){var f=0;return $jscomp.iteratorPrototype(function(){return f<c.length?{done:!1,value:c[f++]}:{done:!0}})};
-$jscomp.iteratorPrototype=function(c){$jscomp.initSymbolIterator();c={next:c};c[$jscomp.global.Symbol.iterator]=function(){return this};return c};$jscomp.makeIterator=function(c){$jscomp.initSymbolIterator();var f=c[Symbol.iterator];return f?f.call(c):$jscomp.arrayIterator(c)};$jscomp.FORCE_POLYFILL_PROMISE=!1;
-$jscomp.polyfill("Promise",function(c){function f(){this.batch_=null}function n(c){return c instanceof g?c:new g(function(f,R){f(c)})}if(c&&!$jscomp.FORCE_POLYFILL_PROMISE)return c;f.prototype.asyncExecute=function(c){null==this.batch_&&(this.batch_=[],this.asyncExecuteBatch_());this.batch_.push(c);return this};f.prototype.asyncExecuteBatch_=function(){var c=this;this.asyncExecuteFunction(function(){c.executeBatch_()})};var D=$jscomp.global.setTimeout;f.prototype.asyncExecuteFunction=function(c){D(c,
-0)};f.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var c=this.batch_;this.batch_=[];for(var f=0;f<c.length;++f){var g=c[f];delete c[f];try{g()}catch(v){this.asyncThrow_(v)}}}this.batch_=null};f.prototype.asyncThrow_=function(c){this.asyncExecuteFunction(function(){throw c;})};var g=function(c){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];var f=this.createResolveAndReject_();try{c(f.resolve,f.reject)}catch(u){f.reject(u)}};g.prototype.createResolveAndReject_=
-function(){function c(c){return function(R){g||(g=!0,c.call(f,R))}}var f=this,g=!1;return{resolve:c(this.resolveTo_),reject:c(this.reject_)}};g.prototype.resolveTo_=function(c){if(c===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(c instanceof g)this.settleSameAsPromise_(c);else{a:switch(typeof c){case "object":var f=null!=c;break a;case "function":f=!0;break a;default:f=!1}f?this.resolveToNonPromiseObj_(c):this.fulfill_(c)}};g.prototype.resolveToNonPromiseObj_=function(c){var f=
-void 0;try{f=c.then}catch(u){this.reject_(u);return}"function"==typeof f?this.settleSameAsThenable_(f,c):this.fulfill_(c)};g.prototype.reject_=function(c){this.settle_(2,c)};g.prototype.fulfill_=function(c){this.settle_(1,c)};g.prototype.settle_=function(c,f){if(0!=this.state_)throw Error("Cannot settle("+c+", "+f|"): Promise already settled in state"+this.state_);this.state_=c;this.result_=f;this.executeOnSettledCallbacks_()};g.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var c=
-this.onSettledCallbacks_,f=0;f<c.length;++f)c[f].call(),c[f]=null;this.onSettledCallbacks_=null}};var ha=new f;g.prototype.settleSameAsPromise_=function(c){var f=this.createResolveAndReject_();c.callWhenSettled_(f.resolve,f.reject)};g.prototype.settleSameAsThenable_=function(c,f){var g=this.createResolveAndReject_();try{c.call(f,g.resolve,g.reject)}catch(v){g.reject(v)}};g.prototype.then=function(c,f){function n(c,f){return"function"==typeof c?function(f){try{v(c(f))}catch(aa){R(aa)}}:f}var v,R,D=
-new g(function(c,f){v=c;R=f});this.callWhenSettled_(n(c,v),n(f,R));return D};g.prototype.catch=function(c){return this.then(void 0,c)};g.prototype.callWhenSettled_=function(c,f){function g(){switch(n.state_){case 1:c(n.result_);break;case 2:f(n.result_);break;default:throw Error("Unexpected state: "+n.state_);}}var n=this;null==this.onSettledCallbacks_?ha.asyncExecute(g):this.onSettledCallbacks_.push(function(){ha.asyncExecute(g)})};g.resolve=n;g.reject=function(c){return new g(function(f,g){g(c)})};
-g.race=function(c){return new g(function(f,g){for(var v=$jscomp.makeIterator(c),u=v.next();!u.done;u=v.next())n(u.value).callWhenSettled_(f,g)})};g.all=function(c){var f=$jscomp.makeIterator(c),u=f.next();return u.done?n([]):new g(function(c,g){function D(f){return function(g){v[f]=g;L--;0==L&&c(v)}}var v=[],L=0;do v.push(void 0),L++,n(u.value).callWhenSettled_(D(v.length-1),g),u=f.next();while(!u.done)})};return g},"es6","es3");
-var DracoDecoderModule=function(c){function f(a,b){a||S("Assertion failed: "+b)}function n(e,b){if(0===b||!e)return"";for(var d=0,l,c=0;;){l=O[e+c>>0];d|=l;if(0==l&&!b)break;c++;if(b&&c==b)break}b||(b=c);l="";if(128>d){for(;0<b;)d=String.fromCharCode.apply(String,O.subarray(e,e+Math.min(b,1024))),l=l?l+d:d,e+=1024,b-=1024;return l}return a.UTF8ToString(e)}function D(a){return a.replace(/__Z[\w\d_]+/g,function(a){return a===a?a:a+" ["+a+"]"})}function g(){a:{var e=Error();if(!e.stack){try{throw Error(0);
-}catch(b){e=b}if(!e.stack){e="(no stack trace available)";break a}}e=e.stack.toString()}a.extraStackTrace&&(e+="\n"+a.extraStackTrace());return D(e)}function ha(a,b){0<a%b&&(a+=b-a%b);return a}function R(){a.HEAP8=ba=new Int8Array(E);a.HEAP16=ua=new Int16Array(E);a.HEAP32=w=new Int32Array(E);a.HEAPU8=O=new Uint8Array(E);a.HEAPU16=Ja=new Uint16Array(E);a.HEAPU32=Ka=new Uint32Array(E);a.HEAPF32=La=new Float32Array(E);a.HEAPF64=Ma=new Float64Array(E)}function Ha(){var e=a.usingWasm?va:Na,b=2147483648-
-e;if(w[X>>2]>b)return!1;var d=x;for(x=Math.max(x,db);x<w[X>>2];)x=536870912>=x?ha(2*x,e):Math.min(ha((3*x+2147483648)/4,e),b);e=a.reallocBuffer(x);if(!e||e.byteLength!=x)return x=d,!1;a.buffer=E=e;R();return!0}function u(e){for(;0<e.length;){var b=e.shift();if("function"==typeof b)b();else{var d=b.func;"number"===typeof d?void 0===b.arg?a.dynCall_v(d):a.dynCall_vi(d,b.arg):d(void 0===b.arg?null:b.arg)}}}function v(e){ca++;a.monitorRunDependencies&&a.monitorRunDependencies(ca)}function Ia(e){ca--;
-a.monitorRunDependencies&&a.monitorRunDependencies(ca);0==ca&&(null!==wa&&(clearInterval(wa),wa=null),oa&&(e=oa,oa=null,e()))}function ia(){return!!ia.uncaught_exception}function ma(){var e=z.last;if(!e)return(m.setTempRet0(0),0)|0;var b=z.infos[e],d=b.type;if(!d)return(m.setTempRet0(0),e)|0;var l=Array.prototype.slice.call(arguments);a.___cxa_is_pointer_type(d);ma.buffer||(ma.buffer=Oa(4));w[ma.buffer>>2]=e;e=ma.buffer;for(var c=0;c<l.length;c++)if(l[c]&&a.___cxa_can_catch(l[c],d,e))return e=w[e>>
-2],b.adjusted=e,(m.setTempRet0(l[c]),e)|0;e=w[e>>2];return(m.setTempRet0(d),e)|0}function L(e,b){t.varargs=b;try{var d=t.get(),l=t.get(),c=t.get();e=0;L.buffer||(L.buffers=[null,[],[]],L.printChar=function(b,e){var d=L.buffers[b];f(d);if(0===e||10===e){b=1===b?a.print:a.printErr;a:{for(var l=e=0;d[l];)++l;if(16<l-e&&d.subarray&&Pa)e=Pa.decode(d.subarray(e,l));else for(l="";;){var c=d[e++];if(!c){e=l;break a}if(c&128){var g=d[e++]&63;if(192==(c&224))l+=String.fromCharCode((c&31)<<6|g);else{var h=d[e++]&
-63;if(224==(c&240))c=(c&15)<<12|g<<6|h;else{var F=d[e++]&63;if(240==(c&248))c=(c&7)<<18|g<<12|h<<6|F;else{var k=d[e++]&63;if(248==(c&252))c=(c&3)<<24|g<<18|h<<12|F<<6|k;else{var pa=d[e++]&63;c=(c&1)<<30|g<<24|h<<18|F<<12|k<<6|pa}}}65536>c?l+=String.fromCharCode(c):(c-=65536,l+=String.fromCharCode(55296|c>>10,56320|c&1023))}}else l+=String.fromCharCode(c)}}b(e);d.length=0}else d.push(e)});for(b=0;b<c;b++){for(var g=w[l+8*b>>2],h=w[l+(8*b+4)>>2],k=0;k<h;k++)L.printChar(d,O[g+k]);e+=h}return e}catch(xa){return"undefined"!==
-typeof FS&&xa instanceof FS.ErrnoError||S(xa),-xa.errno}}function na(e,b){na.seen||(na.seen={});e in na.seen||(a.dynCall_v(b),na.seen[e]=1)}function aa(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}function ya(e){function b(){if(!a.calledRun&&(a.calledRun=!0,!ja)){Qa||(Qa=!0,u(Ra));u(Sa);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)Ta.unshift(a.postRun.shift());
-u(Ta)}}null===Ua&&(Ua=Date.now());if(!(0<ca)){if(a.preRun)for("function"==typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)Va.unshift(a.preRun.shift());u(Va);0<ca||a.calledRun||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);b()},1)):b())}}function S(e){if(a.onAbort)a.onAbort(e);void 0!==e?(a.print(e),a.printErr(e),e=JSON.stringify(e)):e="";ja=!0;var b="abort("+e+") at "+g()+"\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";
-Wa&&Wa.forEach(function(a){b=a(b,e)});throw b;}function r(){}function B(a){return(a||r).__cache__}function T(a,b){var e=B(b),c=e[a];if(c)return c;c=Object.create((b||r).prototype);c.ptr=a;return e[a]=c}function U(a){if("string"===typeof a){for(var b=0,e=0;e<a.length;++e){var c=a.charCodeAt(e);55296<=c&&57343>=c&&(c=65536+((c&1023)<<10)|a.charCodeAt(++e)&1023);127>=c?++b:b=2047>=c?b+2:65535>=c?b+3:2097151>=c?b+4:67108863>=c?b+5:b+6}b=Array(b+1);e=0;c=b.length;if(0<c){c=e+c-1;for(var f=0;f<a.length;++f){var g=
-a.charCodeAt(f);55296<=g&&57343>=g&&(g=65536+((g&1023)<<10)|a.charCodeAt(++f)&1023);if(127>=g){if(e>=c)break;b[e++]=g}else{if(2047>=g){if(e+1>=c)break;b[e++]=192|g>>6}else{if(65535>=g){if(e+2>=c)break;b[e++]=224|g>>12}else{if(2097151>=g){if(e+3>=c)break;b[e++]=240|g>>18}else{if(67108863>=g){if(e+4>=c)break;b[e++]=248|g>>24}else{if(e+5>=c)break;b[e++]=252|g>>30;b[e++]=128|g>>24&63}b[e++]=128|g>>18&63}b[e++]=128|g>>12&63}b[e++]=128|g>>6&63}b[e++]=128|g&63}}b[e]=0}a=k.alloc(b,ba);k.copy(b,ba,a)}return a}
-function A(){throw"cannot construct a Status, no constructor in IDL";}function G(){this.ptr=gb();B(G)[this.ptr]=this}function H(){this.ptr=hb();B(H)[this.ptr]=this}function p(){this.ptr=ib();B(p)[this.ptr]=this}function K(){this.ptr=jb();B(K)[this.ptr]=this}function y(){this.ptr=kb();B(y)[this.ptr]=this}function q(){this.ptr=lb();B(q)[this.ptr]=this}function I(){this.ptr=mb();B(I)[this.ptr]=this}function V(){this.ptr=nb();B(V)[this.ptr]=this}function M(){this.ptr=ob();B(M)[this.ptr]=this}function h(){this.ptr=
-pb();B(h)[this.ptr]=this}function C(){this.ptr=qb();B(C)[this.ptr]=this}function Y(){throw"cannot construct a VoidPtr, no constructor in IDL";}function J(){this.ptr=rb();B(J)[this.ptr]=this}function N(){this.ptr=sb();B(N)[this.ptr]=this}var a=c=c||{},Xa=!1,Ya=!1;a.onRuntimeInitialized=function(){Xa=!0;if(Ya&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Ya=!0;if(Xa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(a){if("string"!==
-typeof a)return!1;a=a.split(".");return 2>a.length||3<a.length?!1:1==a[0]&&0<=a[1]&&2>=a[1]?!0:0!=a[0]||10<a[1]?!1:!0};a||(a=("undefined"!==typeof c?c:null)||{});var qa={},Z;for(Z in a)a.hasOwnProperty(Z)&&(qa[Z]=a[Z]);var ka=!1,fa=!1,la=!1,ra=!1;if(a.ENVIRONMENT)if("WEB"===a.ENVIRONMENT)ka=!0;else if("WORKER"===a.ENVIRONMENT)fa=!0;else if("NODE"===a.ENVIRONMENT)la=!0;else if("SHELL"===a.ENVIRONMENT)ra=!0;else throw Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.");
-else ka="object"===typeof window,fa="function"===typeof importScripts,la="object"===typeof process&&"function"===typeof require&&!ka&&!fa,ra=!ka&&!la&&!fa;if(la){a.print||(a.print=console.log);a.printErr||(a.printErr=console.warn);var za,Aa;a.read=function(a,b){za||(za=require("fs"));Aa||(Aa=require("path"));a=Aa.normalize(a);a=za.readFileSync(a);return b?a:a.toString()};a.readBinary=function(e){e=a.read(e,!0);e.buffer||(e=new Uint8Array(e));f(e.buffer);return e};a.thisProgram||(a.thisProgram=1<process.argv.length?
-process.argv[1].replace(/\\/g,"/"):"unknown-program");a.arguments=process.argv.slice(2);process.on("uncaughtException",function(a){if(!(a instanceof aa))throw a;});a.inspect=function(){return"[Emscripten Module object]"}}else if(ra)a.print||(a.print=print),"undefined"!=typeof printErr&&(a.printErr=printErr),a.read="undefined"!=typeof read?function(a){return read(a)}:function(){throw"no read() available";},a.readBinary=function(a){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(a));
-a=read(a,"binary");f("object"===typeof a);return a},"undefined"!=typeof scriptArgs?a.arguments=scriptArgs:"undefined"!=typeof arguments&&(a.arguments=arguments),"function"===typeof quit&&(a.quit=function(a,b){quit(a)});else if(ka||fa)a.read=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},fa&&(a.readBinary=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),a.readAsync=function(a,
-b,d){var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=function(){200==e.status||0==e.status&&e.response?b(e.response):d()};e.onerror=d;e.send(null)},"undefined"!=typeof arguments&&(a.arguments=arguments),"undefined"!==typeof console?(a.print||(a.print=function(a){console.log(a)}),a.printErr||(a.printErr=function(a){console.warn(a)})):a.print||(a.print=function(a){}),"undefined"===typeof a.setWindowTitle&&(a.setWindowTitle=function(a){document.title=a});else throw Error("Unknown runtime environment. Where are we?");
-a.print||(a.print=function(){});a.printErr||(a.printErr=a.print);a.arguments||(a.arguments=[]);a.thisProgram||(a.thisProgram="./this.program");a.quit||(a.quit=function(a,b){throw b;});a.print=a.print;a.printErr=a.printErr;a.preRun=[];a.postRun=[];for(Z in qa)qa.hasOwnProperty(Z)&&(a[Z]=qa[Z]);qa=void 0;var m={setTempRet0:function(a){return tempRet0=a},getTempRet0:function(){return tempRet0},stackSave:function(){return P},stackRestore:function(a){P=a},getNativeTypeSize:function(a){switch(a){case "i1":case "i8":return 1;
-case "i16":return 2;case "i32":return 4;case "i64":return 8;case "float":return 4;case "double":return 8;default:return"*"===a[a.length-1]?m.QUANTUM_SIZE:"i"===a[0]?(a=parseInt(a.substr(1)),f(0===a%8),a/8):0}},getNativeFieldSize:function(a){return Math.max(m.getNativeTypeSize(a),m.QUANTUM_SIZE)},STACK_ALIGN:16,prepVararg:function(a,b){"double"===b||"i64"===b?a&7&&(f(4===(a&7)),a+=4):f(0===(a&3));return a},getAlignSize:function(a,b,d){return d||"i64"!=a&&"double"!=a?a?Math.min(b||(a?m.getNativeFieldSize(a):
-0),m.QUANTUM_SIZE):Math.min(b,8):8},dynCall:function(e,b,d){return d&&d.length?a["dynCall_"+e].apply(null,[b].concat(d)):a["dynCall_"+e].call(null,b)},functionPointers:[],addFunction:function(a){for(var b=0;b<m.functionPointers.length;b++)if(!m.functionPointers[b])return m.functionPointers[b]=a,2*(1+b);throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS.";},removeFunction:function(a){m.functionPointers[(a-2)/2]=null},warnOnce:function(e){m.warnOnce.shown||
-(m.warnOnce.shown={});m.warnOnce.shown[e]||(m.warnOnce.shown[e]=1,a.printErr(e))},funcWrappers:{},getFuncWrapper:function(a,b){if(a){f(b);m.funcWrappers[b]||(m.funcWrappers[b]={});var d=m.funcWrappers[b];d[a]||(d[a]=1===b.length?function(){return m.dynCall(b,a)}:2===b.length?function(d){return m.dynCall(b,a,[d])}:function(){return m.dynCall(b,a,Array.prototype.slice.call(arguments))});return d[a]}},getCompilerSetting:function(a){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work";
-},stackAlloc:function(a){var b=P;P=P+a|0;P=P+15&-16;return b},staticAlloc:function(a){var b=W;W=W+a|0;W=W+15&-16;return b},dynamicAlloc:function(a){var b=w[X>>2];a=(b+a+15|0)&-16;w[X>>2]=a;return a>=x&&!Ha()?(w[X>>2]=b,0):b},alignMemory:function(a,b){return Math.ceil(a/(b?b:16))*(b?b:16)},makeBigInt:function(a,b,d){return d?+(a>>>0)+4294967296*+(b>>>0):+(a>>>0)+4294967296*+(b|0)},GLOBAL_BASE:1024,QUANTUM_SIZE:4,__dummy__:0},ja=0,Pa="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;"undefined"!==
-typeof TextDecoder&&new TextDecoder("utf-16le");var va=65536,Na=16777216,db=16777216,ba,O,ua,Ja,w,Ka,La,Ma,W,Ba,P,sa,Ca,X;var Da=W=Ba=P=sa=Ca=X=0;a.reallocBuffer||(a.reallocBuffer=function(a){try{if(ArrayBuffer.transfer)var b=ArrayBuffer.transfer(E,a);else{var d=ba;b=new ArrayBuffer(a);(new Int8Array(b)).set(d)}}catch(l){return!1}return tb(b)?b:!1});try{var Ea=Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype,"byteLength").get);Ea(new ArrayBuffer(4))}catch(e){Ea=function(a){return a.byteLength}}var Fa=
-a.TOTAL_STACK||5242880,x=a.TOTAL_MEMORY||16777216;x<Fa&&a.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+x+"! (TOTAL_STACK="+Fa+")");if(a.buffer)var E=a.buffer;else"object"===typeof WebAssembly&&"function"===typeof WebAssembly.Memory?(a.wasmMemory=new WebAssembly.Memory({initial:x/va}),E=a.wasmMemory.buffer):E=new ArrayBuffer(x);R();w[0]=1668509029;ua[1]=25459;if(115!==O[2]||99!==O[3])throw"Runtime error: expected the system to be little-endian!";a.HEAP=void 0;a.buffer=E;a.HEAP8=
-ba;a.HEAP16=ua;a.HEAP32=w;a.HEAPU8=O;a.HEAPU16=Ja;a.HEAPU32=Ka;a.HEAPF32=La;a.HEAPF64=Ma;var Va=[],Ra=[],Sa=[],Za=[],Ta=[],Qa=!1;f(Math.imul&&Math.fround&&Math.clz32&&Math.trunc,"this is a legacy browser, build with LEGACY_VM_SUPPORT");var ca=0,wa=null,oa=null;a.preloadedImages={};a.preloadedAudios={};var Q=null;(function(){function e(){try{if(a.wasmBinary)return new Uint8Array(a.wasmBinary);if(a.readBinary)return a.readBinary(c);throw"on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)";
-}catch(eb){S(eb)}}function b(){return a.wasmBinary||!ka&&!fa||"function"!==typeof fetch?new Promise(function(a,b){a(e())}):fetch(c,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+c+"'";return a.arrayBuffer()}).catch(function(){return e()})}function d(d,e,l){function f(b,d){h=b.exports;if(h.memory){b=h.memory;d=a.buffer;b.byteLength<d.byteLength&&a.printErr("the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here");
-d=new Int8Array(d);var e=new Int8Array(b);Q||d.set(e.subarray(a.STATIC_BASE,a.STATIC_BASE+a.STATIC_BUMP),a.STATIC_BASE);e.set(d);a.buffer=E=b;R()}a.asm=h;a.usingWasm=!0;Ia("wasm-instantiate")}function k(a){f(a.instance,a.module)}function F(d){b().then(function(a){return WebAssembly.instantiate(a,g)}).then(d).catch(function(b){a.printErr("failed to asynchronously prepare wasm: "+b);S(b)})}if("object"!==typeof WebAssembly)return a.printErr("no native wasm support detected"),!1;if(!(a.wasmMemory instanceof
-WebAssembly.Memory))return a.printErr("no native wasm Memory in use"),!1;e.memory=a.wasmMemory;g.global={NaN:NaN,Infinity:Infinity};g["global.Math"]=d.Math;g.env=e;v("wasm-instantiate");if(a.instantiateWasm)try{return a.instantiateWasm(g,f)}catch(fb){return a.printErr("Module.instantiateWasm callback failed with error: "+fb),!1}a.wasmBinary||"function"!==typeof WebAssembly.instantiateStreaming||0===c.indexOf("data:")||"function"!==typeof fetch?F(k):WebAssembly.instantiateStreaming(fetch(c,{credentials:"same-origin"}),
-g).then(k).catch(function(b){a.printErr("wasm streaming compile failed: "+b);a.printErr("falling back to ArrayBuffer instantiation");F(k)});return{}}var c="draco_decoder.wasm",f="draco_decoder.temp.asm.js";"function"===typeof a.locateFile&&(a.locateFile("draco_decoder.wast"),c=a.locateFile(c),f=a.locateFile(f));var g={global:null,env:null,asm2wasm:{"f64-rem":function(a,b){return a%b},"debugger":function(){debugger}},parent:a},h=null;a.asmPreload=a.asm;var k=a.reallocBuffer;a.reallocBuffer=function(b){if("asmjs"===
-m)var d=k(b);else a:{b=ha(b,a.usingWasm?va:Na);var e=a.buffer.byteLength;if(a.usingWasm)try{d=-1!==a.wasmMemory.grow((b-e)/65536)?a.buffer=a.wasmMemory.buffer:null;break a}catch(fd){d=null;break a}d=void 0}return d};var m="";a.asm=function(b,e,c){if(!e.table){var l=a.wasmTableSize;void 0===l&&(l=1024);var f=a.wasmMaxTableSize;e.table="object"===typeof WebAssembly&&"function"===typeof WebAssembly.Table?void 0!==f?new WebAssembly.Table({initial:l,maximum:f,element:"anyfunc"}):new WebAssembly.Table({initial:l,
-element:"anyfunc"}):Array(l);a.wasmTable=e.table}e.memoryBase||(e.memoryBase=a.STATIC_BASE);e.tableBase||(e.tableBase=0);(b=d(b,e,c))||S("no binaryen method succeeded. consider enabling more options, like interpreting, if you want that: https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods");return b}})();Da=m.GLOBAL_BASE;W=Da+17952;Ra.push();Q=null;a.STATIC_BASE=Da;a.STATIC_BUMP=17952;var ub=W;W+=16;var z={last:0,caught:[],infos:{},deAdjust:function(a){if(!a||z.infos[a])return a;
-for(var b in z.infos)if(z.infos[b].adjusted===a)return b;return a},addRef:function(a){a&&z.infos[a].refcount++},decRef:function(e){if(e){var b=z.infos[e];f(0<b.refcount);b.refcount--;0!==b.refcount||b.rethrown||(b.destructor&&a.dynCall_vi(b.destructor,e),delete z.infos[e],___cxa_free_exception(e))}},clearRef:function(a){a&&(z.infos[a].refcount=0)}},t={varargs:0,get:function(a){t.varargs+=4;return w[t.varargs-4>>2]},getStr:function(){return n(t.get())},get64:function(){var a=t.get(),b=t.get();0<=a?
-f(0===b):f(-1===b);return a},getZero:function(){f(0===t.get())}},ta={},Ga=1;Za.push(function(){var e=a._fflush;e&&e(0);if(e=L.printChar){var b=L.buffers;b[1].length&&e(1,10);b[2].length&&e(2,10)}});X=m.staticAlloc(4);Ba=P=m.alignMemory(W);sa=Ba+Fa;Ca=m.alignMemory(sa);w[X>>2]=Ca;a.wasmTableSize=468;a.wasmMaxTableSize=468;a.asmGlobalArg={Math:Math,Int8Array:Int8Array,Int16Array:Int16Array,Int32Array:Int32Array,Uint8Array:Uint8Array,Uint16Array:Uint16Array,Uint32Array:Uint32Array,Float32Array:Float32Array,
-Float64Array:Float64Array,NaN:NaN,Infinity:Infinity,byteLength:Ea};a.asmLibraryArg={abort:S,assert:f,enlargeMemory:Ha,getTotalMemory:function(){return x},abortOnCannotGrowMemory:function(){S("Cannot enlarge memory arrays. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+x+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")},
-invoke_ii:function(e,b){try{return a.dynCall_ii(e,b)}catch(d){if("number"!==typeof d&&"longjmp"!==d)throw d;a.setThrew(1,0)}},invoke_iii:function(e,b,d){try{return a.dynCall_iii(e,b,d)}catch(l){if("number"!==typeof l&&"longjmp"!==l)throw l;a.setThrew(1,0)}},invoke_iiii:function(e,b,d,c){try{return a.dynCall_iiii(e,b,d,c)}catch(F){if("number"!==typeof F&&"longjmp"!==F)throw F;a.setThrew(1,0)}},invoke_iiiiiii:function(e,b,d,c,f,g,h){try{return a.dynCall_iiiiiii(e,b,d,c,f,g,h)}catch(ea){if("number"!==
-typeof ea&&"longjmp"!==ea)throw ea;a.setThrew(1,0)}},invoke_v:function(e){try{a.dynCall_v(e)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;a.setThrew(1,0)}},invoke_vi:function(e,b){try{a.dynCall_vi(e,b)}catch(d){if("number"!==typeof d&&"longjmp"!==d)throw d;a.setThrew(1,0)}},invoke_vii:function(e,b,d){try{a.dynCall_vii(e,b,d)}catch(l){if("number"!==typeof l&&"longjmp"!==l)throw l;a.setThrew(1,0)}},invoke_viii:function(e,b,d,c){try{a.dynCall_viii(e,b,d,c)}catch(F){if("number"!==typeof F&&
-"longjmp"!==F)throw F;a.setThrew(1,0)}},invoke_viiii:function(e,b,d,c,f){try{a.dynCall_viiii(e,b,d,c,f)}catch(pa){if("number"!==typeof pa&&"longjmp"!==pa)throw pa;a.setThrew(1,0)}},invoke_viiiii:function(e,b,d,c,f,g){try{a.dynCall_viiiii(e,b,d,c,f,g)}catch(da){if("number"!==typeof da&&"longjmp"!==da)throw da;a.setThrew(1,0)}},invoke_viiiiii:function(e,b,d,c,f,g,h){try{a.dynCall_viiiiii(e,b,d,c,f,g,h)}catch(ea){if("number"!==typeof ea&&"longjmp"!==ea)throw ea;a.setThrew(1,0)}},__ZSt18uncaught_exceptionv:ia,
-___assert_fail:function(a,b,d,c){ja=!0;throw"Assertion failed: "+n(a)+", at: "+[b?n(b):"unknown filename",d,c?n(c):"unknown function"]+" at "+g();},___cxa_allocate_exception:function(a){return Oa(a)},___cxa_begin_catch:function(a){var b=z.infos[a];b&&!b.caught&&(b.caught=!0,ia.uncaught_exception--);b&&(b.rethrown=!1);z.caught.push(a);z.addRef(z.deAdjust(a));return a},___cxa_find_matching_catch:ma,___cxa_pure_virtual:function(){ja=!0;throw"Pure virtual function called!";},___cxa_throw:function(a,b,
-d){z.infos[a]={ptr:a,adjusted:a,type:b,destructor:d,refcount:0,caught:!1,rethrown:!1};z.last=a;"uncaught_exception"in ia?ia.uncaught_exception++:ia.uncaught_exception=1;throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";},___gxx_personality_v0:function(){},___resumeException:function(a){z.last||(z.last=a);throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";
-},___setErrNo:function(c){a.___errno_location&&(w[a.___errno_location()>>2]=c);return c},___syscall140:function(a,b){t.varargs=b;try{var d=t.getStreamFromFD();t.get();var c=t.get(),e=t.get(),f=t.get();FS.llseek(d,c,f);w[e>>2]=d.position;d.getdents&&0===c&&0===f&&(d.getdents=null);return 0}catch(da){return"undefined"!==typeof FS&&da instanceof FS.ErrnoError||S(da),-da.errno}},___syscall146:L,___syscall6:function(a,b){t.varargs=b;try{var d=t.getStreamFromFD();FS.close(d);return 0}catch(l){return"undefined"!==
-typeof FS&&l instanceof FS.ErrnoError||S(l),-l.errno}},_abort:function(){a.abort()},_emscripten_memcpy_big:function(a,b,d){O.set(O.subarray(b,b+d),a);return a},_pthread_getspecific:function(a){return ta[a]||0},_pthread_key_create:function(a,b){if(0==a)return 22;w[a>>2]=Ga;ta[Ga]=0;Ga++;return 0},_pthread_once:na,_pthread_setspecific:function(a,b){if(!(a in ta))return 22;ta[a]=b;return 0},DYNAMICTOP_PTR:X,tempDoublePtr:ub,ABORT:ja,STACKTOP:P,STACK_MAX:sa};var $a=a.asm(a.asmGlobalArg,a.asmLibraryArg,
-E);a.asm=$a;a.___cxa_can_catch=function(){return a.asm.___cxa_can_catch.apply(null,arguments)};a.___cxa_is_pointer_type=function(){return a.asm.___cxa_is_pointer_type.apply(null,arguments)};var hb=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0.apply(null,arguments)},vb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1.apply(null,
-arguments)},wb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform___destroy___0.apply(null,arguments)},xb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0.apply(null,arguments)},kb=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0.apply(null,
-arguments)},yb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1.apply(null,arguments)},zb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform___destroy___0.apply(null,arguments)},Ab=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_min_value_1.apply(null,
-arguments)},Bb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0.apply(null,arguments)},Cb=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_range_0.apply(null,arguments)},jb=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return a.asm._emscripten_bind_AttributeTransformData_AttributeTransformData_0.apply(null,
-arguments)},Db=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return a.asm._emscripten_bind_AttributeTransformData___destroy___0.apply(null,arguments)},Eb=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return a.asm._emscripten_bind_AttributeTransformData_transform_type_0.apply(null,arguments)},ob=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return a.asm._emscripten_bind_DecoderBuffer_DecoderBuffer_0.apply(null,arguments)},Fb=a._emscripten_bind_DecoderBuffer_Init_2=
-function(){return a.asm._emscripten_bind_DecoderBuffer_Init_2.apply(null,arguments)},Gb=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return a.asm._emscripten_bind_DecoderBuffer___destroy___0.apply(null,arguments)},Hb=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToMesh_2.apply(null,arguments)},Ib=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToPointCloud_2.apply(null,
-arguments)},pb=a._emscripten_bind_Decoder_Decoder_0=function(){return a.asm._emscripten_bind_Decoder_Decoder_0.apply(null,arguments)},Jb=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeByUniqueId_2.apply(null,arguments)},Kb=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3.apply(null,arguments)},Lb=a._emscripten_bind_Decoder_GetAttributeFloat_3=
-function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloat_3.apply(null,arguments)},Mb=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3.apply(null,arguments)},Nb=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByName_2.apply(null,arguments)},Ob=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeId_2.apply(null,
-arguments)},Pb=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3.apply(null,arguments)},Qb=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3.apply(null,arguments)},Rb=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeMetadata_2.apply(null,arguments)},Sb=a._emscripten_bind_Decoder_GetAttribute_2=
-function(){return a.asm._emscripten_bind_Decoder_GetAttribute_2.apply(null,arguments)},Tb=a._emscripten_bind_Decoder_GetEncodedGeometryType_1=function(){return a.asm._emscripten_bind_Decoder_GetEncodedGeometryType_1.apply(null,arguments)},Ub=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return a.asm._emscripten_bind_Decoder_GetFaceFromMesh_3.apply(null,arguments)},Vb=a._emscripten_bind_Decoder_GetMetadata_1=function(){return a.asm._emscripten_bind_Decoder_GetMetadata_1.apply(null,arguments)},
-Wb=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return a.asm._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2.apply(null,arguments)},Xb=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return a.asm._emscripten_bind_Decoder_SkipAttributeTransform_1.apply(null,arguments)},Yb=a._emscripten_bind_Decoder___destroy___0=function(){return a.asm._emscripten_bind_Decoder___destroy___0.apply(null,arguments)},mb=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0.apply(null,
-arguments)},Zb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoFloat32Array_GetValue_1.apply(null,arguments)},$b=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoFloat32Array___destroy___0.apply(null,arguments)},ac=a._emscripten_bind_DracoFloat32Array_size_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_size_0.apply(null,arguments)},rb=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return a.asm._emscripten_bind_DracoInt32Array_DracoInt32Array_0.apply(null,
-arguments)},bc=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt32Array_GetValue_1.apply(null,arguments)},cc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt32Array___destroy___0.apply(null,arguments)},dc=a._emscripten_bind_DracoInt32Array_size_0=function(){return a.asm._emscripten_bind_DracoInt32Array_size_0.apply(null,arguments)},nb=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return a.asm._emscripten_bind_GeometryAttribute_GeometryAttribute_0.apply(null,
-arguments)},ec=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return a.asm._emscripten_bind_GeometryAttribute___destroy___0.apply(null,arguments)},qb=a._emscripten_bind_Mesh_Mesh_0=function(){return a.asm._emscripten_bind_Mesh_Mesh_0.apply(null,arguments)},fc=a._emscripten_bind_Mesh___destroy___0=function(){return a.asm._emscripten_bind_Mesh___destroy___0.apply(null,arguments)},gc=a._emscripten_bind_Mesh_num_attributes_0=function(){return a.asm._emscripten_bind_Mesh_num_attributes_0.apply(null,
-arguments)},hc=a._emscripten_bind_Mesh_num_faces_0=function(){return a.asm._emscripten_bind_Mesh_num_faces_0.apply(null,arguments)},ic=a._emscripten_bind_Mesh_num_points_0=function(){return a.asm._emscripten_bind_Mesh_num_points_0.apply(null,arguments)},jc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetDoubleEntry_2.apply(null,arguments)},kc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetEntryName_2.apply(null,
-arguments)},lc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetIntEntry_2.apply(null,arguments)},mc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetStringEntry_2.apply(null,arguments)},nc=a._emscripten_bind_MetadataQuerier_HasDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasDoubleEntry_2.apply(null,arguments)},oc=a._emscripten_bind_MetadataQuerier_HasEntry_2=
-function(){return a.asm._emscripten_bind_MetadataQuerier_HasEntry_2.apply(null,arguments)},pc=a._emscripten_bind_MetadataQuerier_HasIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasIntEntry_2.apply(null,arguments)},qc=a._emscripten_bind_MetadataQuerier_HasStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasStringEntry_2.apply(null,arguments)},lb=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return a.asm._emscripten_bind_MetadataQuerier_MetadataQuerier_0.apply(null,
-arguments)},rc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return a.asm._emscripten_bind_MetadataQuerier_NumEntries_1.apply(null,arguments)},sc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return a.asm._emscripten_bind_MetadataQuerier___destroy___0.apply(null,arguments)},sb=a._emscripten_bind_Metadata_Metadata_0=function(){return a.asm._emscripten_bind_Metadata_Metadata_0.apply(null,arguments)},tc=a._emscripten_bind_Metadata___destroy___0=function(){return a.asm._emscripten_bind_Metadata___destroy___0.apply(null,
-arguments)},uc=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return a.asm._emscripten_bind_PointAttribute_GetAttributeTransformData_0.apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return a.asm._emscripten_bind_PointAttribute_PointAttribute_0.apply(null,arguments)},vc=a._emscripten_bind_PointAttribute___destroy___0=function(){return a.asm._emscripten_bind_PointAttribute___destroy___0.apply(null,arguments)},wc=a._emscripten_bind_PointAttribute_attribute_type_0=
-function(){return a.asm._emscripten_bind_PointAttribute_attribute_type_0.apply(null,arguments)},xc=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_offset_0.apply(null,arguments)},yc=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_stride_0.apply(null,arguments)},zc=a._emscripten_bind_PointAttribute_data_type_0=function(){return a.asm._emscripten_bind_PointAttribute_data_type_0.apply(null,
-arguments)},Ac=a._emscripten_bind_PointAttribute_normalized_0=function(){return a.asm._emscripten_bind_PointAttribute_normalized_0.apply(null,arguments)},Bc=a._emscripten_bind_PointAttribute_num_components_0=function(){return a.asm._emscripten_bind_PointAttribute_num_components_0.apply(null,arguments)},Cc=a._emscripten_bind_PointAttribute_size_0=function(){return a.asm._emscripten_bind_PointAttribute_size_0.apply(null,arguments)},Dc=a._emscripten_bind_PointAttribute_unique_id_0=function(){return a.asm._emscripten_bind_PointAttribute_unique_id_0.apply(null,
-arguments)},gb=a._emscripten_bind_PointCloud_PointCloud_0=function(){return a.asm._emscripten_bind_PointCloud_PointCloud_0.apply(null,arguments)},Ec=a._emscripten_bind_PointCloud___destroy___0=function(){return a.asm._emscripten_bind_PointCloud___destroy___0.apply(null,arguments)},Fc=a._emscripten_bind_PointCloud_num_attributes_0=function(){return a.asm._emscripten_bind_PointCloud_num_attributes_0.apply(null,arguments)},Gc=a._emscripten_bind_PointCloud_num_points_0=function(){return a.asm._emscripten_bind_PointCloud_num_points_0.apply(null,
-arguments)},Hc=a._emscripten_bind_Status___destroy___0=function(){return a.asm._emscripten_bind_Status___destroy___0.apply(null,arguments)},Ic=a._emscripten_bind_Status_code_0=function(){return a.asm._emscripten_bind_Status_code_0.apply(null,arguments)},Jc=a._emscripten_bind_Status_error_msg_0=function(){return a.asm._emscripten_bind_Status_error_msg_0.apply(null,arguments)},Kc=a._emscripten_bind_Status_ok_0=function(){return a.asm._emscripten_bind_Status_ok_0.apply(null,arguments)},Lc=a._emscripten_bind_VoidPtr___destroy___0=
-function(){return a.asm._emscripten_bind_VoidPtr___destroy___0.apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM.apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM.apply(null,arguments)},Oc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=
-function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM.apply(null,arguments)},Pc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM.apply(null,arguments)},Qc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE.apply(null,
-arguments)},Rc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD.apply(null,arguments)},Sc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH.apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_COLOR.apply(null,arguments)},Uc=
-a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_GENERIC.apply(null,arguments)},Vc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_INVALID.apply(null,arguments)},Wc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_NORMAL.apply(null,arguments)},Xc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
-function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_POSITION.apply(null,arguments)},Yc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD.apply(null,arguments)},Zc=a._emscripten_enum_draco_StatusCode_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_ERROR.apply(null,arguments)},$c=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return a.asm._emscripten_enum_draco_StatusCode_INVALID_PARAMETER.apply(null,
-arguments)},ad=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_IO_ERROR.apply(null,arguments)},bd=a._emscripten_enum_draco_StatusCode_OK=function(){return a.asm._emscripten_enum_draco_StatusCode_OK.apply(null,arguments)},cd=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION.apply(null,arguments)},dd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION.apply(null,
-arguments)};a._emscripten_get_global_libc=function(){return a.asm._emscripten_get_global_libc.apply(null,arguments)};var tb=a._emscripten_replace_memory=function(){return a.asm._emscripten_replace_memory.apply(null,arguments)};a._free=function(){return a.asm._free.apply(null,arguments)};a._llvm_bswap_i32=function(){return a.asm._llvm_bswap_i32.apply(null,arguments)};var Oa=a._malloc=function(){return a.asm._malloc.apply(null,arguments)};a._memcpy=function(){return a.asm._memcpy.apply(null,arguments)};
-a._memmove=function(){return a.asm._memmove.apply(null,arguments)};a._memset=function(){return a.asm._memset.apply(null,arguments)};a._sbrk=function(){return a.asm._sbrk.apply(null,arguments)};a.establishStackSpace=function(){return a.asm.establishStackSpace.apply(null,arguments)};a.getTempRet0=function(){return a.asm.getTempRet0.apply(null,arguments)};a.runPostSets=function(){return a.asm.runPostSets.apply(null,arguments)};a.setTempRet0=function(){return a.asm.setTempRet0.apply(null,arguments)};
-a.setThrew=function(){return a.asm.setThrew.apply(null,arguments)};a.stackAlloc=function(){return a.asm.stackAlloc.apply(null,arguments)};a.stackRestore=function(){return a.asm.stackRestore.apply(null,arguments)};a.stackSave=function(){return a.asm.stackSave.apply(null,arguments)};a.dynCall_ii=function(){return a.asm.dynCall_ii.apply(null,arguments)};a.dynCall_iii=function(){return a.asm.dynCall_iii.apply(null,arguments)};a.dynCall_iiii=function(){return a.asm.dynCall_iiii.apply(null,arguments)};
-a.dynCall_iiiiiii=function(){return a.asm.dynCall_iiiiiii.apply(null,arguments)};a.dynCall_v=function(){return a.asm.dynCall_v.apply(null,arguments)};a.dynCall_vi=function(){return a.asm.dynCall_vi.apply(null,arguments)};a.dynCall_vii=function(){return a.asm.dynCall_vii.apply(null,arguments)};a.dynCall_viii=function(){return a.asm.dynCall_viii.apply(null,arguments)};a.dynCall_viiii=function(){return a.asm.dynCall_viiii.apply(null,arguments)};a.dynCall_viiiii=function(){return a.asm.dynCall_viiiii.apply(null,
-arguments)};a.dynCall_viiiiii=function(){return a.asm.dynCall_viiiiii.apply(null,arguments)};m.stackAlloc=a.stackAlloc;m.stackSave=a.stackSave;m.stackRestore=a.stackRestore;m.establishStackSpace=a.establishStackSpace;m.setTempRet0=a.setTempRet0;m.getTempRet0=a.getTempRet0;a.asm=$a;if(Q)if("function"===typeof a.locateFile?Q=a.locateFile(Q):a.memoryInitializerPrefixURL&&(Q=a.memoryInitializerPrefixURL+Q),la||ra){var ed=a.readBinary(Q);O.set(ed,m.GLOBAL_BASE)}else{var bb=function(){a.readAsync(Q,ab,
-function(){throw"could not load memory initializer "+Q;})};v("memory initializer");var ab=function(c){c.byteLength&&(c=new Uint8Array(c));O.set(c,m.GLOBAL_BASE);a.memoryInitializerRequest&&delete a.memoryInitializerRequest.response;Ia("memory initializer")};if(a.memoryInitializerRequest){var cb=function(){var c=a.memoryInitializerRequest,b=c.response;200!==c.status&&0!==c.status?(console.warn("a problem seems to have happened with Module.memoryInitializerRequest, status: "+c.status+", retrying "+
-Q),bb()):ab(b)};a.memoryInitializerRequest.response?setTimeout(cb,0):a.memoryInitializerRequest.addEventListener("load",cb)}else bb()}a.then=function(c){if(a.calledRun)c(a);else{var b=a.onRuntimeInitialized;a.onRuntimeInitialized=function(){b&&b();c(a)}}return a};aa.prototype=Error();aa.prototype.constructor=aa;var Ua=null;oa=function b(){a.calledRun||ya();a.calledRun||(oa=b)};a.run=ya;a.exit=function(b,d){if(!d||!a.noExitRuntime){if(!a.noExitRuntime&&(ja=!0,P=void 0,u(Za),a.onExit))a.onExit(b);la&&
-process.exit(b);a.quit(b,new aa(b))}};var Wa=[];a.abort=S;if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();ya();r.prototype=Object.create(r.prototype);r.prototype.constructor=r;r.prototype.__class__=r;r.__cache__={};a.WrapperObject=r;a.getCache=B;a.wrapPointer=T;a.castObject=function(a,d){return T(a.ptr,d)};a.NULL=T(0);a.destroy=function(a){if(!a.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";a.__destroy__();
-delete B(a.__class__)[a.ptr]};a.compare=function(a,d){return a.ptr===d.ptr};a.getPointer=function(a){return a.ptr};a.getClass=function(a){return a.__class__};var k={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(k.needed){for(var b=0;b<k.temps.length;b++)a._free(k.temps[b]);k.temps.length=0;a._free(k.buffer);k.buffer=0;k.size+=k.needed;k.needed=0}k.buffer||(k.size+=128,k.buffer=a._malloc(k.size),f(k.buffer));k.pos=0},alloc:function(b,d){f(k.buffer);b=b.length*d.BYTES_PER_ELEMENT;b=
-b+7&-8;k.pos+b>=k.size?(f(0<b),k.needed+=b,d=a._malloc(b),k.temps.push(d)):(d=k.buffer+k.pos,k.pos+=b);return d},copy:function(a,d,c){switch(d.BYTES_PER_ELEMENT){case 2:c>>=1;break;case 4:c>>=2;break;case 8:c>>=3}for(var b=0;b<a.length;b++)d[c+b]=a[b]}};A.prototype=Object.create(r.prototype);A.prototype.constructor=A;A.prototype.__class__=A;A.__cache__={};a.Status=A;A.prototype.code=A.prototype.code=function(){return Ic(this.ptr)};A.prototype.ok=A.prototype.ok=function(){return!!Kc(this.ptr)};A.prototype.error_msg=
-A.prototype.error_msg=function(){return n(Jc(this.ptr))};A.prototype.__destroy__=A.prototype.__destroy__=function(){Hc(this.ptr)};G.prototype=Object.create(r.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=function(){return Fc(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Gc(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Ec(this.ptr)};H.prototype=Object.create(r.prototype);
-H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.AttributeOctahedronTransform=H;H.prototype.InitFromAttribute=H.prototype.InitFromAttribute=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return!!vb(b,a)};H.prototype.quantization_bits=H.prototype.quantization_bits=function(){return xb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};p.prototype=Object.create(r.prototype);p.prototype.constructor=p;p.prototype.__class__=p;p.__cache__=
-{};a.PointAttribute=p;p.prototype.size=p.prototype.size=function(){return Cc(this.ptr)};p.prototype.GetAttributeTransformData=p.prototype.GetAttributeTransformData=function(){return T(uc(this.ptr),K)};p.prototype.attribute_type=p.prototype.attribute_type=function(){return wc(this.ptr)};p.prototype.data_type=p.prototype.data_type=function(){return zc(this.ptr)};p.prototype.num_components=p.prototype.num_components=function(){return Bc(this.ptr)};p.prototype.normalized=p.prototype.normalized=function(){return!!Ac(this.ptr)};
-p.prototype.byte_stride=p.prototype.byte_stride=function(){return yc(this.ptr)};p.prototype.byte_offset=p.prototype.byte_offset=function(){return xc(this.ptr)};p.prototype.unique_id=p.prototype.unique_id=function(){return Dc(this.ptr)};p.prototype.__destroy__=p.prototype.__destroy__=function(){vc(this.ptr)};K.prototype=Object.create(r.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.AttributeTransformData=K;K.prototype.transform_type=K.prototype.transform_type=function(){return Eb(this.ptr)};
-K.prototype.__destroy__=K.prototype.__destroy__=function(){Db(this.ptr)};y.prototype=Object.create(r.prototype);y.prototype.constructor=y;y.prototype.__class__=y;y.__cache__={};a.AttributeQuantizationTransform=y;y.prototype.InitFromAttribute=y.prototype.InitFromAttribute=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return!!yb(b,a)};y.prototype.quantization_bits=y.prototype.quantization_bits=function(){return Bb(this.ptr)};y.prototype.min_value=y.prototype.min_value=function(a){var b=
-this.ptr;a&&"object"===typeof a&&(a=a.ptr);return Ab(b,a)};y.prototype.range=y.prototype.range=function(){return Cb(this.ptr)};y.prototype.__destroy__=y.prototype.__destroy__=function(){zb(this.ptr)};q.prototype=Object.create(r.prototype);q.prototype.constructor=q;q.prototype.__class__=q;q.__cache__={};a.MetadataQuerier=q;q.prototype.HasEntry=q.prototype.HasEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return!!oc(b,a,d)};q.prototype.HasIntEntry=
-q.prototype.HasIntEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return!!pc(b,a,d)};q.prototype.GetIntEntry=q.prototype.GetIntEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return lc(b,a,d)};q.prototype.HasDoubleEntry=q.prototype.HasDoubleEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return!!nc(b,
-a,d)};q.prototype.GetDoubleEntry=q.prototype.GetDoubleEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return jc(b,a,d)};q.prototype.HasStringEntry=q.prototype.HasStringEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return!!qc(b,a,d)};q.prototype.GetStringEntry=q.prototype.GetStringEntry=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);
-d=d&&"object"===typeof d?d.ptr:U(d);return n(mc(b,a,d))};q.prototype.NumEntries=q.prototype.NumEntries=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return rc(b,a)};q.prototype.GetEntryName=q.prototype.GetEntryName=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return n(kc(b,a,d))};q.prototype.__destroy__=q.prototype.__destroy__=function(){sc(this.ptr)};I.prototype=Object.create(r.prototype);I.prototype.constructor=I;I.prototype.__class__=
-I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return Zb(b,a)};I.prototype.size=I.prototype.size=function(){return ac(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){$b(this.ptr)};V.prototype=Object.create(r.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){ec(this.ptr)};M.prototype=
-Object.create(r.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DecoderBuffer=M;M.prototype.Init=M.prototype.Init=function(a,d){var b=this.ptr;k.prepare();if("object"==typeof a&&"object"===typeof a){var c=k.alloc(a,ba);k.copy(a,ba,c);a=c}d&&"object"===typeof d&&(d=d.ptr);Fb(b,a,d)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Gb(this.ptr)};h.prototype=Object.create(r.prototype);h.prototype.constructor=h;h.prototype.__class__=h;h.__cache__={};a.Decoder=h;
-h.prototype.GetEncodedGeometryType=h.prototype.GetEncodedGeometryType=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return Tb(b,a)};h.prototype.DecodeBufferToPointCloud=h.prototype.DecodeBufferToPointCloud=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return T(Ib(b,a,d),A)};h.prototype.DecodeBufferToMesh=h.prototype.DecodeBufferToMesh=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return T(Hb(b,
-a,d),A)};h.prototype.GetAttributeId=h.prototype.GetAttributeId=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return Ob(b,a,d)};h.prototype.GetAttributeIdByName=h.prototype.GetAttributeIdByName=function(a,d){var b=this.ptr;k.prepare();a&&"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);return Nb(b,a,d)};h.prototype.GetAttributeIdByMetadataEntry=h.prototype.GetAttributeIdByMetadataEntry=function(a,d,c){var b=this.ptr;k.prepare();a&&
-"object"===typeof a&&(a=a.ptr);d=d&&"object"===typeof d?d.ptr:U(d);c=c&&"object"===typeof c?c.ptr:U(c);return Mb(b,a,d,c)};h.prototype.GetAttribute=h.prototype.GetAttribute=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return T(Sb(b,a,d),p)};h.prototype.GetAttributeByUniqueId=h.prototype.GetAttributeByUniqueId=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return T(Jb(b,a,d),p)};h.prototype.GetMetadata=
-h.prototype.GetMetadata=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return T(Vb(b,a),N)};h.prototype.GetAttributeMetadata=h.prototype.GetAttributeMetadata=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return T(Rb(b,a,d),N)};h.prototype.GetFaceFromMesh=h.prototype.GetFaceFromMesh=function(a,d,c){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Ub(b,a,d,c)};
-h.prototype.GetTriangleStripsFromMesh=h.prototype.GetTriangleStripsFromMesh=function(a,d){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);return Wb(b,a,d)};h.prototype.GetAttributeFloat=h.prototype.GetAttributeFloat=function(a,d,c){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);d&&"object"===typeof d&&(d=d.ptr);c&&"object"===typeof c&&(c=c.ptr);return!!Lb(b,a,d,c)};h.prototype.GetAttributeFloatForAllPoints=h.prototype.GetAttributeFloatForAllPoints=function(a,c,
-f){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);c&&"object"===typeof c&&(c=c.ptr);f&&"object"===typeof f&&(f=f.ptr);return!!Kb(b,a,c,f)};h.prototype.GetAttributeIntForAllPoints=h.prototype.GetAttributeIntForAllPoints=function(a,c,f){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);c&&"object"===typeof c&&(c=c.ptr);f&&"object"===typeof f&&(f=f.ptr);return!!Qb(b,a,c,f)};h.prototype.GetAttributeInt32ForAllPoints=h.prototype.GetAttributeInt32ForAllPoints=function(a,c,f){var b=this.ptr;a&&"object"===
-typeof a&&(a=a.ptr);c&&"object"===typeof c&&(c=c.ptr);f&&"object"===typeof f&&(f=f.ptr);return!!Pb(b,a,c,f)};h.prototype.SkipAttributeTransform=h.prototype.SkipAttributeTransform=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);Xb(b,a)};h.prototype.__destroy__=h.prototype.__destroy__=function(){Yb(this.ptr)};C.prototype=Object.create(r.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Mesh=C;C.prototype.num_faces=C.prototype.num_faces=function(){return hc(this.ptr)};
-C.prototype.num_attributes=C.prototype.num_attributes=function(){return gc(this.ptr)};C.prototype.num_points=C.prototype.num_points=function(){return ic(this.ptr)};C.prototype.__destroy__=C.prototype.__destroy__=function(){fc(this.ptr)};Y.prototype=Object.create(r.prototype);Y.prototype.constructor=Y;Y.prototype.__class__=Y;Y.__cache__={};a.VoidPtr=Y;Y.prototype.__destroy__=Y.prototype.__destroy__=function(){Lc(this.ptr)};J.prototype=Object.create(r.prototype);J.prototype.constructor=J;J.prototype.__class__=
-J;J.__cache__={};a.DracoInt32Array=J;J.prototype.GetValue=J.prototype.GetValue=function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return bc(b,a)};J.prototype.size=J.prototype.size=function(){return dc(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){cc(this.ptr)};N.prototype=Object.create(r.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.Metadata=N;N.prototype.__destroy__=N.prototype.__destroy__=function(){tc(this.ptr)};(function(){function b(){a.OK=
-bd();a.ERROR=Zc();a.IO_ERROR=ad();a.INVALID_PARAMETER=$c();a.UNSUPPORTED_VERSION=dd();a.UNKNOWN_VERSION=cd();a.INVALID_GEOMETRY_TYPE=Qc();a.POINT_CLOUD=Rc();a.TRIANGULAR_MESH=Sc();a.ATTRIBUTE_INVALID_TRANSFORM=Mc();a.ATTRIBUTE_NO_TRANSFORM=Nc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Pc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Oc();a.INVALID=Vc();a.POSITION=Xc();a.NORMAL=Wc();a.COLOR=Tc();a.TEX_COORD=Yc();a.GENERIC=Uc()}a.calledRun?b():Sa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();
-return c};"object"===typeof module&&module.exports&&(module.exports=DracoDecoderModule);
+var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(d,k,f){d!=Array.prototype&&d!=Object.prototype&&(d[k]=f.value)};$jscomp.getGlobal=function(d){return"undefined"!=typeof window&&window===d?d:"undefined"!=typeof global&&null!=global?global:d};$jscomp.global=$jscomp.getGlobal(this);
+$jscomp.polyfill=function(d,k,f,u){if(k){f=$jscomp.global;d=d.split(".");for(u=0;u<d.length-1;u++){var h=d[u];h in f||(f[h]={});f=f[h]}d=d[d.length-1];u=f[d];k=k(u);k!=u&&null!=k&&$jscomp.defineProperty(f,d,{configurable:!0,writable:!0,value:k})}};$jscomp.polyfill("Math.imul",function(d){return d?d:function(d,f){d=Number(d);f=Number(f);var k=d&65535,h=f&65535;return k*h+((d>>>16&65535)*h+k*(f>>>16&65535)<<16>>>0)|0}},"es6","es3");
+$jscomp.polyfill("Math.clz32",function(d){return d?d:function(d){d=Number(d)>>>0;if(0===d)return 32;var f=0;0===(d&4294901760)&&(d<<=16,f+=16);0===(d&4278190080)&&(d<<=8,f+=8);0===(d&4026531840)&&(d<<=4,f+=4);0===(d&3221225472)&&(d<<=2,f+=2);0===(d&2147483648)&&f++;return f}},"es6","es3");$jscomp.polyfill("Math.trunc",function(d){return d?d:function(d){d=Number(d);if(isNaN(d)||Infinity===d||-Infinity===d||0===d)return d;var f=Math.floor(Math.abs(d));return 0>d?-f:f}},"es6","es3");
+$jscomp.SYMBOL_PREFIX="jscomp_symbol_";$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var d=0;return function(k){return $jscomp.SYMBOL_PREFIX+(k||"")+d++}}();
+$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var d=$jscomp.global.Symbol.iterator;d||(d=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[d]&&$jscomp.defineProperty(Array.prototype,d,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(d){var k=0;return $jscomp.iteratorPrototype(function(){return k<d.length?{done:!1,value:d[k++]}:{done:!0}})};
+$jscomp.iteratorPrototype=function(d){$jscomp.initSymbolIterator();d={next:d};d[$jscomp.global.Symbol.iterator]=function(){return this};return d};$jscomp.makeIterator=function(d){$jscomp.initSymbolIterator();var k=d[Symbol.iterator];return k?k.call(d):$jscomp.arrayIterator(d)};$jscomp.FORCE_POLYFILL_PROMISE=!1;
+$jscomp.polyfill("Promise",function(d){function k(){this.batch_=null}function f(d){return d instanceof h?d:new h(function(r,f){r(d)})}if(d&&!$jscomp.FORCE_POLYFILL_PROMISE)return d;k.prototype.asyncExecute=function(d){null==this.batch_&&(this.batch_=[],this.asyncExecuteBatch_());this.batch_.push(d);return this};k.prototype.asyncExecuteBatch_=function(){var d=this;this.asyncExecuteFunction(function(){d.executeBatch_()})};var u=$jscomp.global.setTimeout;k.prototype.asyncExecuteFunction=function(d){u(d,
+0)};k.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var d=this.batch_;this.batch_=[];for(var B=0;B<d.length;++B){var f=d[B];delete d[B];try{f()}catch(v){this.asyncThrow_(v)}}}this.batch_=null};k.prototype.asyncThrow_=function(d){this.asyncExecuteFunction(function(){throw d;})};var h=function(d){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];var r=this.createResolveAndReject_();try{d(r.resolve,r.reject)}catch(Y){r.reject(Y)}};h.prototype.createResolveAndReject_=
+function(){function d(d){return function(r){h||(h=!0,d.call(f,r))}}var f=this,h=!1;return{resolve:d(this.resolveTo_),reject:d(this.reject_)}};h.prototype.resolveTo_=function(d){if(d===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(d instanceof h)this.settleSameAsPromise_(d);else{a:switch(typeof d){case "object":var f=null!=d;break a;case "function":f=!0;break a;default:f=!1}f?this.resolveToNonPromiseObj_(d):this.fulfill_(d)}};h.prototype.resolveToNonPromiseObj_=function(d){var f=
+void 0;try{f=d.then}catch(Y){this.reject_(Y);return}"function"==typeof f?this.settleSameAsThenable_(f,d):this.fulfill_(d)};h.prototype.reject_=function(d){this.settle_(2,d)};h.prototype.fulfill_=function(d){this.settle_(1,d)};h.prototype.settle_=function(d,f){if(0!=this.state_)throw Error("Cannot settle("+d+", "+f|"): Promise already settled in state"+this.state_);this.state_=d;this.result_=f;this.executeOnSettledCallbacks_()};h.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var d=
+this.onSettledCallbacks_,f=0;f<d.length;++f)d[f].call(),d[f]=null;this.onSettledCallbacks_=null}};var ha=new k;h.prototype.settleSameAsPromise_=function(d){var f=this.createResolveAndReject_();d.callWhenSettled_(f.resolve,f.reject)};h.prototype.settleSameAsThenable_=function(d,f){var h=this.createResolveAndReject_();try{d.call(f,h.resolve,h.reject)}catch(v){h.reject(v)}};h.prototype.then=function(d,f){function k(d,f){return"function"==typeof d?function(f){try{v(d(f))}catch(O){r(O)}}:f}var v,r,B=new h(function(d,
+f){v=d;r=f});this.callWhenSettled_(k(d,v),k(f,r));return B};h.prototype.catch=function(d){return this.then(void 0,d)};h.prototype.callWhenSettled_=function(d,f){function h(){switch(k.state_){case 1:d(k.result_);break;case 2:f(k.result_);break;default:throw Error("Unexpected state: "+k.state_);}}var k=this;null==this.onSettledCallbacks_?ha.asyncExecute(h):this.onSettledCallbacks_.push(function(){ha.asyncExecute(h)})};h.resolve=f;h.reject=function(d){return new h(function(f,h){h(d)})};h.race=function(d){return new h(function(h,
+k){for(var v=$jscomp.makeIterator(d),r=v.next();!r.done;r=v.next())f(r.value).callWhenSettled_(h,k)})};h.all=function(d){var k=$jscomp.makeIterator(d),r=k.next();return r.done?f([]):new h(function(d,h){function v(f){return function(h){u[f]=h;B--;0==B&&d(u)}}var u=[],B=0;do u.push(void 0),B++,f(r.value).callWhenSettled_(v(u.length-1),h),r=k.next();while(!r.done)})};return h},"es6","es3");
+var DracoDecoderModule=function(d){function k(a,c){c||(c=16);return Math.ceil(a/c)*c}function f(a,c){a||O("Assertion failed: "+c)}function u(a,c){if(0===c||!a)return"";for(var b=0,e,d=0;;){e=W[a+d>>0];b|=e;if(0==e&&!c)break;d++;if(c&&d==c)break}c||(c=d);e="";if(128>b){for(;0<c;)b=String.fromCharCode.apply(String,W.subarray(a,a+Math.min(c,1024))),e=e?e+b:b,a+=1024,c-=1024;return e}return h(W,a)}function h(a,c){for(var b=c;a[b];)++b;if(16<b-c&&a.subarray&&Ia)return Ia.decode(a.subarray(c,b));for(b=
+"";;){var e=a[c++];if(!e)return b;if(e&128){var d=a[c++]&63;if(192==(e&224))b+=String.fromCharCode((e&31)<<6|d);else{var f=a[c++]&63;if(224==(e&240))e=(e&15)<<12|d<<6|f;else{var g=a[c++]&63;if(240==(e&248))e=(e&7)<<18|d<<12|f<<6|g;else{var h=a[c++]&63;if(248==(e&252))e=(e&3)<<24|d<<18|f<<12|g<<6|h;else{var k=a[c++]&63;e=(e&1)<<30|d<<24|f<<18|g<<12|h<<6|k}}}65536>e?b+=String.fromCharCode(e):(e-=65536,b+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else b+=String.fromCharCode(e)}}function ha(a,c){0<
+a%c&&(a+=c-a%c);return a}function r(){a.HEAP8=ia=new Int8Array(D);a.HEAP16=Ja=new Int16Array(D);a.HEAP32=E=new Int32Array(D);a.HEAPU8=W=new Uint8Array(D);a.HEAPU16=new Uint16Array(D);a.HEAPU32=new Uint32Array(D);a.HEAPF32=new Float32Array(D);a.HEAPF64=new Float64Array(D)}function B(e){for(;0<e.length;){var c=e.shift();if("function"==typeof c)c();else{var b=c.func;"number"===typeof b?void 0===c.arg?a.dynCall_v(b):a.dynCall_vi(b,c.arg):b(void 0===c.arg?null:c.arg)}}}function Y(a){return String.prototype.startsWith?
+a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}function v(){return!!v.uncaught_exception}function la(){var e=y.last;if(!e)return(sa(0),0)|0;var c=y.infos[e],b=c.type;if(!b)return(sa(0),e)|0;var p=Array.prototype.slice.call(arguments);a.___cxa_is_pointer_type(b);la.buffer||(la.buffer=Ka(4));E[la.buffer>>2]=e;e=la.buffer;for(var d=0;d<p.length;d++)if(p[d]&&a.___cxa_can_catch(p[d],b,e))return e=E[e>>2],c.adjusted=e,(sa(p[d]),e)|0;e=E[e>>2];
+return(sa(b),e)|0}function Z(e,c){w.varargs=c;try{var b=w.get(),p=w.get(),d=w.get();e=0;Z.buffers||(Z.buffers=[null,[],[]],Z.printChar=function(c,b){var e=Z.buffers[c];f(e);0===b||10===b?((1===c?a.print:a.printErr)(h(e,0)),e.length=0):e.push(b)});for(c=0;c<d;c++){for(var g=E[p+8*c>>2],k=E[p+(8*c+4)>>2],l=0;l<k;l++)Z.printChar(b,W[g+l]);e+=k}return e}catch(ya){return"undefined"!==typeof FS&&ya instanceof FS.ErrnoError||O(ya),-ya.errno}}function ma(e,c){ma.seen||(ma.seen={});e in ma.seen||(a.dynCall_v(c),
+ma.seen[e]=1)}function na(a){this.name="ExitStatus";this.message="Program terminated with exit("+a+")";this.status=a}function wa(e){function c(){if(!a.calledRun&&(a.calledRun=!0,!oa)){La||(La=!0,B(Ma));B(Na);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)Oa.unshift(a.postRun.shift());B(Oa)}}if(!(0<ea)){if(a.preRun)for("function"==typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)Pa.unshift(a.preRun.shift());
+B(Pa);0<ea||a.calledRun||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);c()},1)):c())}}function O(e){if(a.onAbort)a.onAbort(e);void 0!==e?(a.print(e),a.printErr(e),e=JSON.stringify(e)):e="";oa=!0;throw"abort("+e+"). Build with -s ASSERTIONS=1 for more info.";}function m(){}function t(a){return(a||m).__cache__}function T(a,c){var b=t(c),e=b[a];if(e)return e;e=Object.create((c||m).prototype);e.ptr=a;return b[a]=e}function U(a){if("string"===typeof a){for(var c=
+0,b=0;b<a.length;++b){var e=a.charCodeAt(b);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++b)&1023);127>=e?++c:c=2047>=e?c+2:65535>=e?c+3:2097151>=e?c+4:67108863>=e?c+5:c+6}c=Array(c+1);b=0;e=c.length;if(0<e){e=b+e-1;for(var d=0;d<a.length;++d){var f=a.charCodeAt(d);55296<=f&&57343>=f&&(f=65536+((f&1023)<<10)|a.charCodeAt(++d)&1023);if(127>=f){if(b>=e)break;c[b++]=f}else{if(2047>=f){if(b+1>=e)break;c[b++]=192|f>>6}else{if(65535>=f){if(b+2>=e)break;c[b++]=224|f>>12}else{if(2097151>=f){if(b+
+3>=e)break;c[b++]=240|f>>18}else{if(67108863>=f){if(b+4>=e)break;c[b++]=248|f>>24}else{if(b+5>=e)break;c[b++]=252|f>>30;c[b++]=128|f>>24&63}c[b++]=128|f>>18&63}c[b++]=128|f>>12&63}c[b++]=128|f>>6&63}c[b++]=128|f&63}}c[b]=0}a=l.alloc(c,ia);l.copy(c,ia,a)}return a}function z(){throw"cannot construct a Status, no constructor in IDL";}function F(){this.ptr=Wa();t(F)[this.ptr]=this}function G(){this.ptr=Xa();t(G)[this.ptr]=this}function H(){this.ptr=Ya();t(H)[this.ptr]=this}function I(){this.ptr=Za();
+t(I)[this.ptr]=this}function J(){this.ptr=$a();t(J)[this.ptr]=this}function n(){this.ptr=ab();t(n)[this.ptr]=this}function P(){this.ptr=bb();t(P)[this.ptr]=this}function x(){this.ptr=cb();t(x)[this.ptr]=this}function K(){this.ptr=db();t(K)[this.ptr]=this}function q(){this.ptr=eb();t(q)[this.ptr]=this}function L(){this.ptr=fb();t(L)[this.ptr]=this}function M(){this.ptr=gb();t(M)[this.ptr]=this}function V(){this.ptr=hb();t(V)[this.ptr]=this}function Q(){this.ptr=ib();t(Q)[this.ptr]=this}function g(){this.ptr=
+jb();t(g)[this.ptr]=this}function C(){this.ptr=kb();t(C)[this.ptr]=this}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function N(){this.ptr=lb();t(N)[this.ptr]=this}function R(){this.ptr=mb();t(R)[this.ptr]=this}d=d||{};var a="undefined"!==typeof d?d:{},Qa=!1,Ra=!1;a.onRuntimeInitialized=function(){Qa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Ra=!0;if(Qa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=
+function(a){if("string"!==typeof a)return!1;a=a.split(".");return 2>a.length||3<a.length?!1:1==a[0]&&0<=a[1]&&3>=a[1]?!0:0!=a[0]||10<a[1]?!1:!0};var pa={},aa;for(aa in a)a.hasOwnProperty(aa)&&(pa[aa]=a[aa]);a.arguments=[];a.thisProgram="./this.program";a.quit=function(a,c){throw c;};a.preRun=[];a.postRun=[];var ja=!1,fa=!1,qa=!1,za=!1;if(a.ENVIRONMENT)if("WEB"===a.ENVIRONMENT)ja=!0;else if("WORKER"===a.ENVIRONMENT)fa=!0;else if("NODE"===a.ENVIRONMENT)qa=!0;else if("SHELL"===a.ENVIRONMENT)za=!0;else throw Error("Module['ENVIRONMENT'] value is not valid. must be one of: WEB|WORKER|NODE|SHELL.");
+else ja="object"===typeof window,fa="function"===typeof importScripts,qa="object"===typeof process&&"function"===typeof require&&!ja&&!fa,za=!ja&&!qa&&!fa;if(qa){var Aa,Ba;a.read=function(a,c){Aa||(Aa=require("fs"));Ba||(Ba=require("path"));a=Ba.normalize(a);a=Aa.readFileSync(a);return c?a:a.toString()};a.readBinary=function(e){e=a.read(e,!0);e.buffer||(e=new Uint8Array(e));f(e.buffer);return e};1<process.argv.length&&(a.thisProgram=process.argv[1].replace(/\\/g,"/"));a.arguments=process.argv.slice(2);
+process.on("uncaughtException",function(a){if(!(a instanceof na))throw a;});process.on("unhandledRejection",function(a,c){process.exit(1)});a.inspect=function(){return"[Emscripten Module object]"}}else if(za)"undefined"!=typeof read&&(a.read=function(a){return read(a)}),a.readBinary=function(a){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(a));a=read(a,"binary");f("object"===typeof a);return a},"undefined"!=typeof scriptArgs?a.arguments=scriptArgs:"undefined"!=typeof arguments&&
+(a.arguments=arguments),"function"===typeof quit&&(a.quit=function(a,c){quit(a)});else if(ja||fa)a.read=function(a){var c=new XMLHttpRequest;c.open("GET",a,!1);c.send(null);return c.responseText},fa&&(a.readBinary=function(a){var c=new XMLHttpRequest;c.open("GET",a,!1);c.responseType="arraybuffer";c.send(null);return new Uint8Array(c.response)}),a.readAsync=function(a,c,b){var e=new XMLHttpRequest;e.open("GET",a,!0);e.responseType="arraybuffer";e.onload=function(){200==e.status||0==e.status&&e.response?
+c(e.response):b()};e.onerror=b;e.send(null)},a.setWindowTitle=function(a){document.title=a};a.print="undefined"!==typeof console?console.log.bind(console):"undefined"!==typeof print?print:null;a.printErr="undefined"!==typeof printErr?printErr:"undefined"!==typeof console&&console.warn.bind(console)||a.print;a.print=a.print;a.printErr=a.printErr;for(aa in pa)pa.hasOwnProperty(aa)&&(a[aa]=pa[aa]);pa=void 0;var oa=0,Ia="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;"undefined"!==typeof TextDecoder&&
+new TextDecoder("utf-16le");var ia,W,Ja,E,ba,Ca,ta,ua,Da,ka;var Ea=ba=Ca=ta=ua=Da=ka=0;var Sa=!1;a.reallocBuffer||(a.reallocBuffer=function(a){try{if(ArrayBuffer.transfer)var c=ArrayBuffer.transfer(D,a);else{var b=ia;c=new ArrayBuffer(a);(new Int8Array(c)).set(b)}}catch(p){return!1}return nb(c)?c:!1});try{var Ta=Function.prototype.call.bind(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype,"byteLength").get);Ta(new ArrayBuffer(4))}catch(e){Ta=function(a){return a.byteLength}}var Fa=a.TOTAL_STACK||
+5242880,A=a.TOTAL_MEMORY||16777216;A<Fa&&a.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+A+"! (TOTAL_STACK="+Fa+")");if(a.buffer)var D=a.buffer;else"object"===typeof WebAssembly&&"function"===typeof WebAssembly.Memory?(a.wasmMemory=new WebAssembly.Memory({initial:A/65536}),D=a.wasmMemory.buffer):D=new ArrayBuffer(A),a.buffer=D;r();E[0]=1668509029;Ja[1]=25459;if(115!==W[2]||99!==W[3])throw"Runtime error: expected the system to be little-endian!";var Pa=[],Ma=[],Na=[],ob=[],Oa=[],
+La=!1,ea=0,Ga=null,ra=null;a.preloadedImages={};a.preloadedAudios={};(function(){function e(){try{if(a.wasmBinary)return new Uint8Array(a.wasmBinary);if(a.readBinary)return a.readBinary(f);throw"on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)";}catch(Va){O(Va)}}function c(){return a.wasmBinary||!ja&&!fa||"function"!==typeof fetch?new Promise(function(a,c){a(e())}):fetch(f,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+
+f+"'";return a.arrayBuffer()}).catch(function(){return e()})}function b(b,e,d){function p(c,b){k=c.exports;k.memory&&(c=k.memory,b=a.buffer,c.byteLength<b.byteLength&&a.printErr("the new buffer in mergeMemory is smaller than the previous one. in native wasm, we should grow memory here"),b=new Int8Array(b),(new Int8Array(c)).set(b),a.buffer=D=c,r());a.asm=k;a.usingWasm=!0;ea--;a.monitorRunDependencies&&a.monitorRunDependencies(ea);0==ea&&(null!==Ga&&(clearInterval(Ga),Ga=null),ra&&(c=ra,ra=null,c()))}
+function g(a){p(a.instance,a.module)}function S(b){c().then(function(a){return WebAssembly.instantiate(a,h)}).then(b).catch(function(c){a.printErr("failed to asynchronously prepare wasm: "+c);O(c)})}if("object"!==typeof WebAssembly)return a.printErr("no native wasm support detected"),!1;if(!(a.wasmMemory instanceof WebAssembly.Memory))return a.printErr("no native wasm Memory in use"),!1;e.memory=a.wasmMemory;h.global={NaN:NaN,Infinity:Infinity};h["global.Math"]=Math;h.env=e;ea++;a.monitorRunDependencies&&
+a.monitorRunDependencies(ea);if(a.instantiateWasm)try{return a.instantiateWasm(h,p)}catch(pb){return a.printErr("Module.instantiateWasm callback failed with error: "+pb),!1}a.wasmBinary||"function"!==typeof WebAssembly.instantiateStreaming||Y(f)||"function"!==typeof fetch?S(g):WebAssembly.instantiateStreaming(fetch(f,{credentials:"same-origin"}),h).then(g).catch(function(c){a.printErr("wasm streaming compile failed: "+c);a.printErr("falling back to ArrayBuffer instantiation");S(g)});return{}}var d=
+"draco_decoder.wast",f="draco_decoder.wasm",g="draco_decoder.temp.asm.js";"function"===typeof a.locateFile&&(Y(d)||(d=a.locateFile(d)),Y(f)||(f=a.locateFile(f)),Y(g)||(g=a.locateFile(g)));var h={global:null,env:null,asm2wasm:{"f64-rem":function(a,c){return a%c},"debugger":function(){debugger}},parent:a},k=null;a.asmPreload=a.asm;var l=a.reallocBuffer;a.reallocBuffer=function(c){if("asmjs"===m)var b=l(c);else a:{c=ha(c,a.usingWasm?65536:16777216);var e=a.buffer.byteLength;if(a.usingWasm)try{b=-1!==
+a.wasmMemory.grow((c-e)/65536)?a.buffer=a.wasmMemory.buffer:null;break a}catch(ud){b=null;break a}b=void 0}return b};var m="";a.asm=function(c,e,d){if(!e.table){var p=a.wasmTableSize;void 0===p&&(p=1024);var f=a.wasmMaxTableSize;e.table="object"===typeof WebAssembly&&"function"===typeof WebAssembly.Table?void 0!==f?new WebAssembly.Table({initial:p,maximum:f,element:"anyfunc"}):new WebAssembly.Table({initial:p,element:"anyfunc"}):Array(p);a.wasmTable=e.table}e.memoryBase||(e.memoryBase=a.STATIC_BASE);
+e.tableBase||(e.tableBase=0);(c=b(c,e,d))||O("no binaryen method succeeded. consider enabling more options, like interpreting, if you want that: https://github.com/kripken/emscripten/wiki/WebAssembly#binaryen-methods");return c}})();Ea=1024;ba=Ea+14480;Ma.push();a.STATIC_BASE=Ea;a.STATIC_BUMP=14480;var qb=ba;ba+=16;var y={last:0,caught:[],infos:{},deAdjust:function(a){if(!a||y.infos[a])return a;for(var c in y.infos)if(y.infos[c].adjusted===a)return c;return a},addRef:function(a){a&&y.infos[a].refcount++},
+decRef:function(e){if(e){var c=y.infos[e];f(0<c.refcount);c.refcount--;0!==c.refcount||c.rethrown||(c.destructor&&a.dynCall_vi(c.destructor,e),delete y.infos[e],___cxa_free_exception(e))}},clearRef:function(a){a&&(y.infos[a].refcount=0)}},w={varargs:0,get:function(a){w.varargs+=4;return E[w.varargs-4>>2]},getStr:function(){return u(w.get())},get64:function(){var a=w.get(),c=w.get();0<=a?f(0===c):f(-1===c);return a},getZero:function(){f(0===w.get())}},va={},Ha=1;ka=function(a){f(!Sa);var c=ba;ba=ba+
+a+15&-16;return c}(4);Ca=ta=k(ba);ua=Ca+Fa;Da=k(ua);E[ka>>2]=Da;Sa=!0;a.wasmTableSize=468;a.wasmMaxTableSize=468;a.asmGlobalArg={};a.asmLibraryArg={abort:O,assert:f,enlargeMemory:function(){var e=a.usingWasm?65536:16777216,c=2147483648-e;if(E[ka>>2]>c)return!1;var b=A;for(A=Math.max(A,16777216);A<E[ka>>2];)A=536870912>=A?ha(2*A,e):Math.min(ha((3*A+2147483648)/4,e),c);e=a.reallocBuffer(A);if(!e||e.byteLength!=A)return A=b,!1;a.buffer=D=e;r();return!0},getTotalMemory:function(){return A},abortOnCannotGrowMemory:function(){O("Cannot enlarge memory arrays. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+
+A+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")},invoke_ii:function(e,c){try{return a.dynCall_ii(e,c)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;a.setThrew(1,0)}},invoke_iii:function(e,c,b){try{return a.dynCall_iii(e,c,b)}catch(p){if("number"!==typeof p&&"longjmp"!==p)throw p;a.setThrew(1,0)}},invoke_iiii:function(e,c,b,d){try{return a.dynCall_iiii(e,
+c,b,d)}catch(S){if("number"!==typeof S&&"longjmp"!==S)throw S;a.setThrew(1,0)}},invoke_iiiiiii:function(e,c,b,d,f,g,h){try{return a.dynCall_iiiiiii(e,c,b,d,f,g,h)}catch(da){if("number"!==typeof da&&"longjmp"!==da)throw da;a.setThrew(1,0)}},invoke_v:function(e){try{a.dynCall_v(e)}catch(c){if("number"!==typeof c&&"longjmp"!==c)throw c;a.setThrew(1,0)}},invoke_vi:function(e,c){try{a.dynCall_vi(e,c)}catch(b){if("number"!==typeof b&&"longjmp"!==b)throw b;a.setThrew(1,0)}},invoke_vii:function(e,c,b){try{a.dynCall_vii(e,
+c,b)}catch(p){if("number"!==typeof p&&"longjmp"!==p)throw p;a.setThrew(1,0)}},invoke_viii:function(e,c,b,d){try{a.dynCall_viii(e,c,b,d)}catch(S){if("number"!==typeof S&&"longjmp"!==S)throw S;a.setThrew(1,0)}},invoke_viiii:function(e,c,b,d,f){try{a.dynCall_viiii(e,c,b,d,f)}catch(xa){if("number"!==typeof xa&&"longjmp"!==xa)throw xa;a.setThrew(1,0)}},invoke_viiiii:function(e,c,b,d,f,g){try{a.dynCall_viiiii(e,c,b,d,f,g)}catch(ca){if("number"!==typeof ca&&"longjmp"!==ca)throw ca;a.setThrew(1,0)}},invoke_viiiiii:function(e,
+c,b,d,f,g,h){try{a.dynCall_viiiiii(e,c,b,d,f,g,h)}catch(da){if("number"!==typeof da&&"longjmp"!==da)throw da;a.setThrew(1,0)}},__ZSt18uncaught_exceptionv:v,___cxa_allocate_exception:function(a){return Ka(a)},___cxa_begin_catch:function(a){var c=y.infos[a];c&&!c.caught&&(c.caught=!0,v.uncaught_exception--);c&&(c.rethrown=!1);y.caught.push(a);y.addRef(y.deAdjust(a));return a},___cxa_find_matching_catch:la,___cxa_pure_virtual:function(){oa=!0;throw"Pure virtual function called!";},___cxa_throw:function(a,
+c,b){y.infos[a]={ptr:a,adjusted:a,type:c,destructor:b,refcount:0,caught:!1,rethrown:!1};y.last=a;"uncaught_exception"in v?v.uncaught_exception++:v.uncaught_exception=1;throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";},___gxx_personality_v0:function(){},___resumeException:function(a){y.last||(y.last=a);throw a+" - Exception catching is disabled, this exception cannot be caught. Compile with -s DISABLE_EXCEPTION_CATCHING=0 or DISABLE_EXCEPTION_CATCHING=2 to catch.";
+},___setErrNo:function(d){a.___errno_location&&(E[a.___errno_location()>>2]=d);return d},___syscall140:function(a,c){w.varargs=c;try{var b=w.getStreamFromFD();w.get();var d=w.get(),e=w.get(),f=w.get();FS.llseek(b,d,f);E[e>>2]=b.position;b.getdents&&0===d&&0===f&&(b.getdents=null);return 0}catch(ca){return"undefined"!==typeof FS&&ca instanceof FS.ErrnoError||O(ca),-ca.errno}},___syscall146:Z,___syscall6:function(a,c){w.varargs=c;try{var b=w.getStreamFromFD();FS.close(b);return 0}catch(p){return"undefined"!==
+typeof FS&&p instanceof FS.ErrnoError||O(p),-p.errno}},_abort:function(){a.abort()},_emscripten_memcpy_big:function(a,c,b){W.set(W.subarray(c,c+b),a);return a},_llvm_trap:function(){O("trap!")},_pthread_getspecific:function(a){return va[a]||0},_pthread_key_create:function(a,c){if(0==a)return 22;E[a>>2]=Ha;va[Ha]=0;Ha++;return 0},_pthread_once:ma,_pthread_setspecific:function(a,c){if(!(a in va))return 22;va[a]=c;return 0},flush_NO_FILESYSTEM:function(){var d=a._fflush;d&&d(0);if(d=Z.printChar){var c=
+Z.buffers;c[1].length&&d(1,10);c[2].length&&d(2,10)}},DYNAMICTOP_PTR:ka,tempDoublePtr:qb,ABORT:oa,STACKTOP:ta,STACK_MAX:ua};var Ua=a.asm(a.asmGlobalArg,a.asmLibraryArg,D);a.asm=Ua;a.___cxa_can_catch=function(){return a.asm.___cxa_can_catch.apply(null,arguments)};a.___cxa_is_pointer_type=function(){return a.asm.___cxa_is_pointer_type.apply(null,arguments)};var $a=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0.apply(null,
+arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1.apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform___destroy___0.apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0.apply(null,
+arguments)},cb=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0.apply(null,arguments)},ub=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1.apply(null,arguments)},vb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform___destroy___0.apply(null,
+arguments)},wb=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_min_value_1.apply(null,arguments)},xb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0.apply(null,arguments)},yb=a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return a.asm._emscripten_bind_AttributeQuantizationTransform_range_0.apply(null,
+arguments)},bb=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return a.asm._emscripten_bind_AttributeTransformData_AttributeTransformData_0.apply(null,arguments)},zb=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return a.asm._emscripten_bind_AttributeTransformData___destroy___0.apply(null,arguments)},Ab=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return a.asm._emscripten_bind_AttributeTransformData_transform_type_0.apply(null,
+arguments)},ib=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=function(){return a.asm._emscripten_bind_DecoderBuffer_DecoderBuffer_0.apply(null,arguments)},Bb=a._emscripten_bind_DecoderBuffer_Init_2=function(){return a.asm._emscripten_bind_DecoderBuffer_Init_2.apply(null,arguments)},Cb=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return a.asm._emscripten_bind_DecoderBuffer___destroy___0.apply(null,arguments)},Db=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToMesh_2.apply(null,
+arguments)},Eb=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return a.asm._emscripten_bind_Decoder_DecodeBufferToPointCloud_2.apply(null,arguments)},jb=a._emscripten_bind_Decoder_Decoder_0=function(){return a.asm._emscripten_bind_Decoder_Decoder_0.apply(null,arguments)},Fb=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeByUniqueId_2.apply(null,arguments)},Gb=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=
+function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3.apply(null,arguments)},Hb=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeFloat_3.apply(null,arguments)},Ib=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3.apply(null,arguments)},Jb=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIdByName_2.apply(null,
+arguments)},Kb=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeId_2.apply(null,arguments)},Lb=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3.apply(null,arguments)},Mb=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3.apply(null,arguments)},Nb=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=
+function(){return a.asm._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3.apply(null,arguments)},Ob=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3.apply(null,arguments)},Pb=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return a.asm._emscripten_bind_Decoder_GetAttributeMetadata_2.apply(null,arguments)},Qb=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3.apply(null,
+arguments)},Rb=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3.apply(null,arguments)},Sb=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return a.asm._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3.apply(null,arguments)},Tb=a._emscripten_bind_Decoder_GetAttribute_2=function(){return a.asm._emscripten_bind_Decoder_GetAttribute_2.apply(null,arguments)},Ub=a._emscripten_bind_Decoder_GetEncodedGeometryType_1=
+function(){return a.asm._emscripten_bind_Decoder_GetEncodedGeometryType_1.apply(null,arguments)},Vb=a._emscripten_bind_Decoder_GetFaceFromMesh_3=function(){return a.asm._emscripten_bind_Decoder_GetFaceFromMesh_3.apply(null,arguments)},Wb=a._emscripten_bind_Decoder_GetMetadata_1=function(){return a.asm._emscripten_bind_Decoder_GetMetadata_1.apply(null,arguments)},Xb=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return a.asm._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2.apply(null,
+arguments)},Yb=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return a.asm._emscripten_bind_Decoder_SkipAttributeTransform_1.apply(null,arguments)},Zb=a._emscripten_bind_Decoder___destroy___0=function(){return a.asm._emscripten_bind_Decoder___destroy___0.apply(null,arguments)},gb=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0.apply(null,arguments)},$b=a._emscripten_bind_DracoFloat32Array_GetValue_1=
+function(){return a.asm._emscripten_bind_DracoFloat32Array_GetValue_1.apply(null,arguments)},ac=a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoFloat32Array___destroy___0.apply(null,arguments)},bc=a._emscripten_bind_DracoFloat32Array_size_0=function(){return a.asm._emscripten_bind_DracoFloat32Array_size_0.apply(null,arguments)},fb=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return a.asm._emscripten_bind_DracoInt16Array_DracoInt16Array_0.apply(null,
+arguments)},cc=a._emscripten_bind_DracoInt16Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt16Array_GetValue_1.apply(null,arguments)},dc=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt16Array___destroy___0.apply(null,arguments)},ec=a._emscripten_bind_DracoInt16Array_size_0=function(){return a.asm._emscripten_bind_DracoInt16Array_size_0.apply(null,arguments)},lb=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=function(){return a.asm._emscripten_bind_DracoInt32Array_DracoInt32Array_0.apply(null,
+arguments)},fc=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt32Array_GetValue_1.apply(null,arguments)},gc=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt32Array___destroy___0.apply(null,arguments)},hc=a._emscripten_bind_DracoInt32Array_size_0=function(){return a.asm._emscripten_bind_DracoInt32Array_size_0.apply(null,arguments)},db=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return a.asm._emscripten_bind_DracoInt8Array_DracoInt8Array_0.apply(null,
+arguments)},ic=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoInt8Array_GetValue_1.apply(null,arguments)},jc=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return a.asm._emscripten_bind_DracoInt8Array___destroy___0.apply(null,arguments)},kc=a._emscripten_bind_DracoInt8Array_size_0=function(){return a.asm._emscripten_bind_DracoInt8Array_size_0.apply(null,arguments)},Wa=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return a.asm._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0.apply(null,
+arguments)},lc=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt16Array_GetValue_1.apply(null,arguments)},mc=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt16Array___destroy___0.apply(null,arguments)},nc=a._emscripten_bind_DracoUInt16Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt16Array_size_0.apply(null,arguments)},Za=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return a.asm._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0.apply(null,
+arguments)},oc=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt32Array_GetValue_1.apply(null,arguments)},pc=a._emscripten_bind_DracoUInt32Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt32Array___destroy___0.apply(null,arguments)},qc=a._emscripten_bind_DracoUInt32Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt32Array_size_0.apply(null,arguments)},Ya=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return a.asm._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0.apply(null,
+arguments)},rc=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return a.asm._emscripten_bind_DracoUInt8Array_GetValue_1.apply(null,arguments)},sc=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return a.asm._emscripten_bind_DracoUInt8Array___destroy___0.apply(null,arguments)},tc=a._emscripten_bind_DracoUInt8Array_size_0=function(){return a.asm._emscripten_bind_DracoUInt8Array_size_0.apply(null,arguments)},hb=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return a.asm._emscripten_bind_GeometryAttribute_GeometryAttribute_0.apply(null,
+arguments)},uc=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return a.asm._emscripten_bind_GeometryAttribute___destroy___0.apply(null,arguments)},kb=a._emscripten_bind_Mesh_Mesh_0=function(){return a.asm._emscripten_bind_Mesh_Mesh_0.apply(null,arguments)},vc=a._emscripten_bind_Mesh___destroy___0=function(){return a.asm._emscripten_bind_Mesh___destroy___0.apply(null,arguments)},wc=a._emscripten_bind_Mesh_num_attributes_0=function(){return a.asm._emscripten_bind_Mesh_num_attributes_0.apply(null,
+arguments)},xc=a._emscripten_bind_Mesh_num_faces_0=function(){return a.asm._emscripten_bind_Mesh_num_faces_0.apply(null,arguments)},yc=a._emscripten_bind_Mesh_num_points_0=function(){return a.asm._emscripten_bind_Mesh_num_points_0.apply(null,arguments)},zc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetDoubleEntry_2.apply(null,arguments)},Ac=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetEntryName_2.apply(null,
+arguments)},Bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetIntEntry_2.apply(null,arguments)},Cc=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_GetStringEntry_2.apply(null,arguments)},Dc=a._emscripten_bind_MetadataQuerier_HasDoubleEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasDoubleEntry_2.apply(null,arguments)},Ec=a._emscripten_bind_MetadataQuerier_HasEntry_2=
+function(){return a.asm._emscripten_bind_MetadataQuerier_HasEntry_2.apply(null,arguments)},Fc=a._emscripten_bind_MetadataQuerier_HasIntEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasIntEntry_2.apply(null,arguments)},Gc=a._emscripten_bind_MetadataQuerier_HasStringEntry_2=function(){return a.asm._emscripten_bind_MetadataQuerier_HasStringEntry_2.apply(null,arguments)},eb=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return a.asm._emscripten_bind_MetadataQuerier_MetadataQuerier_0.apply(null,
+arguments)},Hc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return a.asm._emscripten_bind_MetadataQuerier_NumEntries_1.apply(null,arguments)},Ic=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return a.asm._emscripten_bind_MetadataQuerier___destroy___0.apply(null,arguments)},mb=a._emscripten_bind_Metadata_Metadata_0=function(){return a.asm._emscripten_bind_Metadata_Metadata_0.apply(null,arguments)},Jc=a._emscripten_bind_Metadata___destroy___0=function(){return a.asm._emscripten_bind_Metadata___destroy___0.apply(null,
+arguments)},Kc=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=function(){return a.asm._emscripten_bind_PointAttribute_GetAttributeTransformData_0.apply(null,arguments)},ab=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return a.asm._emscripten_bind_PointAttribute_PointAttribute_0.apply(null,arguments)},Lc=a._emscripten_bind_PointAttribute___destroy___0=function(){return a.asm._emscripten_bind_PointAttribute___destroy___0.apply(null,arguments)},Mc=a._emscripten_bind_PointAttribute_attribute_type_0=
+function(){return a.asm._emscripten_bind_PointAttribute_attribute_type_0.apply(null,arguments)},Nc=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_offset_0.apply(null,arguments)},Oc=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return a.asm._emscripten_bind_PointAttribute_byte_stride_0.apply(null,arguments)},Pc=a._emscripten_bind_PointAttribute_data_type_0=function(){return a.asm._emscripten_bind_PointAttribute_data_type_0.apply(null,
+arguments)},Qc=a._emscripten_bind_PointAttribute_normalized_0=function(){return a.asm._emscripten_bind_PointAttribute_normalized_0.apply(null,arguments)},Rc=a._emscripten_bind_PointAttribute_num_components_0=function(){return a.asm._emscripten_bind_PointAttribute_num_components_0.apply(null,arguments)},Sc=a._emscripten_bind_PointAttribute_size_0=function(){return a.asm._emscripten_bind_PointAttribute_size_0.apply(null,arguments)},Tc=a._emscripten_bind_PointAttribute_unique_id_0=function(){return a.asm._emscripten_bind_PointAttribute_unique_id_0.apply(null,
+arguments)},Xa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return a.asm._emscripten_bind_PointCloud_PointCloud_0.apply(null,arguments)},Uc=a._emscripten_bind_PointCloud___destroy___0=function(){return a.asm._emscripten_bind_PointCloud___destroy___0.apply(null,arguments)},Vc=a._emscripten_bind_PointCloud_num_attributes_0=function(){return a.asm._emscripten_bind_PointCloud_num_attributes_0.apply(null,arguments)},Wc=a._emscripten_bind_PointCloud_num_points_0=function(){return a.asm._emscripten_bind_PointCloud_num_points_0.apply(null,
+arguments)},Xc=a._emscripten_bind_Status___destroy___0=function(){return a.asm._emscripten_bind_Status___destroy___0.apply(null,arguments)},Yc=a._emscripten_bind_Status_code_0=function(){return a.asm._emscripten_bind_Status_code_0.apply(null,arguments)},Zc=a._emscripten_bind_Status_error_msg_0=function(){return a.asm._emscripten_bind_Status_error_msg_0.apply(null,arguments)},$c=a._emscripten_bind_Status_ok_0=function(){return a.asm._emscripten_bind_Status_ok_0.apply(null,arguments)},ad=a._emscripten_bind_VoidPtr___destroy___0=
+function(){return a.asm._emscripten_bind_VoidPtr___destroy___0.apply(null,arguments)},bd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM.apply(null,arguments)},cd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM.apply(null,arguments)},dd=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=
+function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM.apply(null,arguments)},ed=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return a.asm._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM.apply(null,arguments)},fd=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE.apply(null,
+arguments)},gd=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD.apply(null,arguments)},hd=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return a.asm._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH.apply(null,arguments)},id=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_COLOR.apply(null,arguments)},jd=
+a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_GENERIC.apply(null,arguments)},kd=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_INVALID.apply(null,arguments)},ld=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_NORMAL.apply(null,arguments)},md=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
+function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_POSITION.apply(null,arguments)},nd=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return a.asm._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD.apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_ERROR.apply(null,arguments)},pd=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=function(){return a.asm._emscripten_enum_draco_StatusCode_INVALID_PARAMETER.apply(null,
+arguments)},qd=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return a.asm._emscripten_enum_draco_StatusCode_IO_ERROR.apply(null,arguments)},rd=a._emscripten_enum_draco_StatusCode_OK=function(){return a.asm._emscripten_enum_draco_StatusCode_OK.apply(null,arguments)},sd=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION.apply(null,arguments)},td=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return a.asm._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION.apply(null,
+arguments)},nb=a._emscripten_replace_memory=function(){return a.asm._emscripten_replace_memory.apply(null,arguments)};a._free=function(){return a.asm._free.apply(null,arguments)};a._llvm_bswap_i32=function(){return a.asm._llvm_bswap_i32.apply(null,arguments)};var Ka=a._malloc=function(){return a.asm._malloc.apply(null,arguments)};a._memcpy=function(){return a.asm._memcpy.apply(null,arguments)};a._memmove=function(){return a.asm._memmove.apply(null,arguments)};a._memset=function(){return a.asm._memset.apply(null,
+arguments)};a._sbrk=function(){return a.asm._sbrk.apply(null,arguments)};a.establishStackSpace=function(){return a.asm.establishStackSpace.apply(null,arguments)};a.getTempRet0=function(){return a.asm.getTempRet0.apply(null,arguments)};a.runPostSets=function(){return a.asm.runPostSets.apply(null,arguments)};var sa=a.setTempRet0=function(){return a.asm.setTempRet0.apply(null,arguments)};a.setThrew=function(){return a.asm.setThrew.apply(null,arguments)};a.stackAlloc=function(){return a.asm.stackAlloc.apply(null,
+arguments)};a.stackRestore=function(){return a.asm.stackRestore.apply(null,arguments)};a.stackSave=function(){return a.asm.stackSave.apply(null,arguments)};a.dynCall_ii=function(){return a.asm.dynCall_ii.apply(null,arguments)};a.dynCall_iii=function(){return a.asm.dynCall_iii.apply(null,arguments)};a.dynCall_iiii=function(){return a.asm.dynCall_iiii.apply(null,arguments)};a.dynCall_iiiiiii=function(){return a.asm.dynCall_iiiiiii.apply(null,arguments)};a.dynCall_v=function(){return a.asm.dynCall_v.apply(null,
+arguments)};a.dynCall_vi=function(){return a.asm.dynCall_vi.apply(null,arguments)};a.dynCall_vii=function(){return a.asm.dynCall_vii.apply(null,arguments)};a.dynCall_viii=function(){return a.asm.dynCall_viii.apply(null,arguments)};a.dynCall_viiii=function(){return a.asm.dynCall_viiii.apply(null,arguments)};a.dynCall_viiiii=function(){return a.asm.dynCall_viiiii.apply(null,arguments)};a.dynCall_viiiiii=function(){return a.asm.dynCall_viiiiii.apply(null,arguments)};a.asm=Ua;a.then=function(d){if(a.calledRun)d(a);
+else{var c=a.onRuntimeInitialized;a.onRuntimeInitialized=function(){c&&c();d(a)}}return a};na.prototype=Error();na.prototype.constructor=na;ra=function c(){a.calledRun||wa();a.calledRun||(ra=c)};a.run=wa;a.exit=function(c,b){if(!b||!a.noExitRuntime||0!==c){if(!a.noExitRuntime&&(oa=!0,ta=void 0,B(ob),a.onExit))a.onExit(c);qa&&process.exit(c);a.quit(c,new na(c))}};a.abort=O;if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();a.noExitRuntime=!0;
+wa();m.prototype=Object.create(m.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.WrapperObject=m;a.getCache=t;a.wrapPointer=T;a.castObject=function(a,b){return T(a.ptr,b)};a.NULL=T(0);a.destroy=function(a){if(!a.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";a.__destroy__();delete t(a.__class__)[a.ptr]};a.compare=function(a,b){return a.ptr===b.ptr};a.getPointer=function(a){return a.ptr};a.getClass=function(a){return a.__class__};var l=
+{buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(l.needed){for(var c=0;c<l.temps.length;c++)a._free(l.temps[c]);l.temps.length=0;a._free(l.buffer);l.buffer=0;l.size+=l.needed;l.needed=0}l.buffer||(l.size+=128,l.buffer=a._malloc(l.size),f(l.buffer));l.pos=0},alloc:function(c,b){f(l.buffer);c=c.length*b.BYTES_PER_ELEMENT;c=c+7&-8;l.pos+c>=l.size?(f(0<c),l.needed+=c,b=a._malloc(c),l.temps.push(b)):(b=l.buffer+l.pos,l.pos+=c);return b},copy:function(a,b,d){switch(b.BYTES_PER_ELEMENT){case 2:d>>=
+1;break;case 4:d>>=2;break;case 8:d>>=3}for(var c=0;c<a.length;c++)b[d+c]=a[c]}};z.prototype=Object.create(m.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.Status=z;z.prototype.code=z.prototype.code=function(){return Yc(this.ptr)};z.prototype.ok=z.prototype.ok=function(){return!!$c(this.ptr)};z.prototype.error_msg=z.prototype.error_msg=function(){return u(Zc(this.ptr))};z.prototype.__destroy__=z.prototype.__destroy__=function(){Xc(this.ptr)};F.prototype=Object.create(m.prototype);
+F.prototype.constructor=F;F.prototype.__class__=F;F.__cache__={};a.DracoUInt16Array=F;F.prototype.GetValue=F.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return lc(c,a)};F.prototype.size=F.prototype.size=function(){return nc(this.ptr)};F.prototype.__destroy__=F.prototype.__destroy__=function(){mc(this.ptr)};G.prototype=Object.create(m.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.PointCloud=G;G.prototype.num_attributes=G.prototype.num_attributes=
+function(){return Vc(this.ptr)};G.prototype.num_points=G.prototype.num_points=function(){return Wc(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){Uc(this.ptr)};H.prototype=Object.create(m.prototype);H.prototype.constructor=H;H.prototype.__class__=H;H.__cache__={};a.DracoUInt8Array=H;H.prototype.GetValue=H.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return rc(c,a)};H.prototype.size=H.prototype.size=function(){return tc(this.ptr)};H.prototype.__destroy__=
+H.prototype.__destroy__=function(){sc(this.ptr)};I.prototype=Object.create(m.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoUInt32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return oc(c,a)};I.prototype.size=I.prototype.size=function(){return qc(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){pc(this.ptr)};J.prototype=Object.create(m.prototype);J.prototype.constructor=J;J.prototype.__class__=
+J;J.__cache__={};a.AttributeOctahedronTransform=J;J.prototype.InitFromAttribute=J.prototype.InitFromAttribute=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return!!rb(c,a)};J.prototype.quantization_bits=J.prototype.quantization_bits=function(){return tb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){sb(this.ptr)};n.prototype=Object.create(m.prototype);n.prototype.constructor=n;n.prototype.__class__=n;n.__cache__={};a.PointAttribute=n;n.prototype.size=n.prototype.size=
+function(){return Sc(this.ptr)};n.prototype.GetAttributeTransformData=n.prototype.GetAttributeTransformData=function(){return T(Kc(this.ptr),P)};n.prototype.attribute_type=n.prototype.attribute_type=function(){return Mc(this.ptr)};n.prototype.data_type=n.prototype.data_type=function(){return Pc(this.ptr)};n.prototype.num_components=n.prototype.num_components=function(){return Rc(this.ptr)};n.prototype.normalized=n.prototype.normalized=function(){return!!Qc(this.ptr)};n.prototype.byte_stride=n.prototype.byte_stride=
+function(){return Oc(this.ptr)};n.prototype.byte_offset=n.prototype.byte_offset=function(){return Nc(this.ptr)};n.prototype.unique_id=n.prototype.unique_id=function(){return Tc(this.ptr)};n.prototype.__destroy__=n.prototype.__destroy__=function(){Lc(this.ptr)};P.prototype=Object.create(m.prototype);P.prototype.constructor=P;P.prototype.__class__=P;P.__cache__={};a.AttributeTransformData=P;P.prototype.transform_type=P.prototype.transform_type=function(){return Ab(this.ptr)};P.prototype.__destroy__=
+P.prototype.__destroy__=function(){zb(this.ptr)};x.prototype=Object.create(m.prototype);x.prototype.constructor=x;x.prototype.__class__=x;x.__cache__={};a.AttributeQuantizationTransform=x;x.prototype.InitFromAttribute=x.prototype.InitFromAttribute=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return!!ub(c,a)};x.prototype.quantization_bits=x.prototype.quantization_bits=function(){return xb(this.ptr)};x.prototype.min_value=x.prototype.min_value=function(a){var c=this.ptr;a&&"object"===
+typeof a&&(a=a.ptr);return wb(c,a)};x.prototype.range=x.prototype.range=function(){return yb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){vb(this.ptr)};K.prototype=Object.create(m.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return ic(c,a)};K.prototype.size=K.prototype.size=function(){return kc(this.ptr)};K.prototype.__destroy__=
+K.prototype.__destroy__=function(){jc(this.ptr)};q.prototype=Object.create(m.prototype);q.prototype.constructor=q;q.prototype.__class__=q;q.__cache__={};a.MetadataQuerier=q;q.prototype.HasEntry=q.prototype.HasEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return!!Ec(c,a,b)};q.prototype.HasIntEntry=q.prototype.HasIntEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);
+return!!Fc(c,a,b)};q.prototype.GetIntEntry=q.prototype.GetIntEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return Bc(c,a,b)};q.prototype.HasDoubleEntry=q.prototype.HasDoubleEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return!!Dc(c,a,b)};q.prototype.GetDoubleEntry=q.prototype.GetDoubleEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=
+a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return zc(c,a,b)};q.prototype.HasStringEntry=q.prototype.HasStringEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return!!Gc(c,a,b)};q.prototype.GetStringEntry=q.prototype.GetStringEntry=function(a,b){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return u(Cc(c,a,b))};q.prototype.NumEntries=q.prototype.NumEntries=function(a){var c=this.ptr;
+a&&"object"===typeof a&&(a=a.ptr);return Hc(c,a)};q.prototype.GetEntryName=q.prototype.GetEntryName=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return u(Ac(c,a,b))};q.prototype.__destroy__=q.prototype.__destroy__=function(){Ic(this.ptr)};L.prototype=Object.create(m.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=L;L.prototype.GetValue=L.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&
+(a=a.ptr);return cc(c,a)};L.prototype.size=L.prototype.size=function(){return ec(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){dc(this.ptr)};M.prototype=Object.create(m.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoFloat32Array=M;M.prototype.GetValue=M.prototype.GetValue=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return $b(c,a)};M.prototype.size=M.prototype.size=function(){return bc(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=
+function(){ac(this.ptr)};V.prototype=Object.create(m.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){uc(this.ptr)};Q.prototype=Object.create(m.prototype);Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.DecoderBuffer=Q;Q.prototype.Init=Q.prototype.Init=function(a,b){var c=this.ptr;l.prepare();if("object"==typeof a&&"object"===typeof a){var d=l.alloc(a,ia);l.copy(a,ia,d);a=
+d}b&&"object"===typeof b&&(b=b.ptr);Bb(c,a,b)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){Cb(this.ptr)};g.prototype=Object.create(m.prototype);g.prototype.constructor=g;g.prototype.__class__=g;g.__cache__={};a.Decoder=g;g.prototype.GetEncodedGeometryType=g.prototype.GetEncodedGeometryType=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return Ub(c,a)};g.prototype.DecodeBufferToPointCloud=g.prototype.DecodeBufferToPointCloud=function(a,b){var c=this.ptr;a&&"object"===typeof a&&
+(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return T(Eb(c,a,b),z)};g.prototype.DecodeBufferToMesh=g.prototype.DecodeBufferToMesh=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return T(Db(c,a,b),z)};g.prototype.GetAttributeId=g.prototype.GetAttributeId=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return Kb(c,a,b)};g.prototype.GetAttributeIdByName=g.prototype.GetAttributeIdByName=function(a,b){var c=
+this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);return Jb(c,a,b)};g.prototype.GetAttributeIdByMetadataEntry=g.prototype.GetAttributeIdByMetadataEntry=function(a,b,d){var c=this.ptr;l.prepare();a&&"object"===typeof a&&(a=a.ptr);b=b&&"object"===typeof b?b.ptr:U(b);d=d&&"object"===typeof d?d.ptr:U(d);return Ib(c,a,b,d)};g.prototype.GetAttribute=g.prototype.GetAttribute=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=
+b.ptr);return T(Tb(c,a,b),n)};g.prototype.GetAttributeByUniqueId=g.prototype.GetAttributeByUniqueId=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return T(Fb(c,a,b),n)};g.prototype.GetMetadata=g.prototype.GetMetadata=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return T(Wb(c,a),R)};g.prototype.GetAttributeMetadata=g.prototype.GetAttributeMetadata=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&
+(b=b.ptr);return T(Pb(c,a,b),R)};g.prototype.GetFaceFromMesh=g.prototype.GetFaceFromMesh=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Vb(c,a,b,d)};g.prototype.GetTriangleStripsFromMesh=g.prototype.GetTriangleStripsFromMesh=function(a,b){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);return Xb(c,a,b)};g.prototype.GetAttributeFloat=g.prototype.GetAttributeFloat=function(a,
+b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Hb(c,a,b,d)};g.prototype.GetAttributeFloatForAllPoints=g.prototype.GetAttributeFloatForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Gb(c,a,b,d)};g.prototype.GetAttributeIntForAllPoints=g.prototype.GetAttributeIntForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===
+typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ob(c,a,b,d)};g.prototype.GetAttributeInt8ForAllPoints=g.prototype.GetAttributeInt8ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Nb(c,a,b,d)};g.prototype.GetAttributeUInt8ForAllPoints=g.prototype.GetAttributeUInt8ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===
+typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Sb(c,a,b,d)};g.prototype.GetAttributeInt16ForAllPoints=g.prototype.GetAttributeInt16ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Lb(c,a,b,d)};g.prototype.GetAttributeUInt16ForAllPoints=g.prototype.GetAttributeUInt16ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&
+"object"===typeof d&&(d=d.ptr);return!!Qb(c,a,b,d)};g.prototype.GetAttributeInt32ForAllPoints=g.prototype.GetAttributeInt32ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Mb(c,a,b,d)};g.prototype.GetAttributeUInt32ForAllPoints=g.prototype.GetAttributeUInt32ForAllPoints=function(a,b,d){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);b&&"object"===typeof b&&(b=b.ptr);d&&"object"===typeof d&&(d=
+d.ptr);return!!Rb(c,a,b,d)};g.prototype.SkipAttributeTransform=g.prototype.SkipAttributeTransform=function(a){var c=this.ptr;a&&"object"===typeof a&&(a=a.ptr);Yb(c,a)};g.prototype.__destroy__=g.prototype.__destroy__=function(){Zb(this.ptr)};C.prototype=Object.create(m.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Mesh=C;C.prototype.num_faces=C.prototype.num_faces=function(){return xc(this.ptr)};C.prototype.num_attributes=C.prototype.num_attributes=function(){return wc(this.ptr)};
+C.prototype.num_points=C.prototype.num_points=function(){return yc(this.ptr)};C.prototype.__destroy__=C.prototype.__destroy__=function(){vc(this.ptr)};X.prototype=Object.create(m.prototype);X.prototype.constructor=X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){ad(this.ptr)};N.prototype=Object.create(m.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=
+function(a){var b=this.ptr;a&&"object"===typeof a&&(a=a.ptr);return fc(b,a)};N.prototype.size=N.prototype.size=function(){return hc(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){gc(this.ptr)};R.prototype=Object.create(m.prototype);R.prototype.constructor=R;R.prototype.__class__=R;R.__cache__={};a.Metadata=R;R.prototype.__destroy__=R.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function c(){a.OK=rd();a.ERROR=od();a.IO_ERROR=qd();a.INVALID_PARAMETER=pd();a.UNSUPPORTED_VERSION=
+td();a.UNKNOWN_VERSION=sd();a.INVALID_GEOMETRY_TYPE=fd();a.POINT_CLOUD=gd();a.TRIANGULAR_MESH=hd();a.ATTRIBUTE_INVALID_TRANSFORM=bd();a.ATTRIBUTE_NO_TRANSFORM=cd();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=ed();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=dd();a.INVALID=kd();a.POSITION=md();a.NORMAL=ld();a.COLOR=id();a.TEX_COORD=nd();a.GENERIC=jd()}a.calledRun?c():Na.unshift(c)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();return d};
+"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);

+ 2 - 2
examples/js/lines/LineMaterial.js

@@ -168,9 +168,9 @@ THREE.ShaderLib[ 'line' ] = {
 
 			gl_Position = clip;
 
-			#include <logdepthbuf_vertex>
+			vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
 
-			#include <worldpos_vertex>
+			#include <logdepthbuf_vertex>
 			#include <clipping_planes_vertex>
 			#include <fog_vertex>
 

+ 82 - 15
examples/js/loaders/DRACOLoader.js

@@ -100,17 +100,18 @@ THREE.DRACOLoader.prototype = {
      * The format is:
      *     attributeUniqueIdMap[attributeName] = attributeId
      */
-    decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap) {
+    decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap,
+                              attributeTypeMap) {
       var scope = this;
       THREE.DRACOLoader.getDecoderModule()
           .then( function ( module ) {
             scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback,
-              attributeUniqueIdMap || {});
+              attributeUniqueIdMap || {}, attributeTypeMap || {});
           });
     },
 
     decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback,
-                                      attributeUniqueIdMap) {
+                                      attributeUniqueIdMap, attributeTypeMap) {
       /*
        * Here is how to use Draco Javascript decoder and get the geometry.
        */
@@ -136,38 +137,103 @@ THREE.DRACOLoader.prototype = {
         throw new Error(errorMsg);
       }
       callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder,
-          geometryType, buffer, attributeUniqueIdMap));
+          geometryType, buffer, attributeUniqueIdMap, attributeTypeMap));
     },
 
     addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry,
-                                     attributeName, attribute, geometry,
-                                     geometryBuffer) {
+                                     attributeName, attributeType, attribute, 
+                                     geometry, geometryBuffer) {
       if (attribute.ptr === 0) {
         var errorMsg = 'THREE.DRACOLoader: No attribute ' + attributeName;
         console.error(errorMsg);
         throw new Error(errorMsg);
       }
+
       var numComponents = attribute.num_components();
-      var attributeData = new dracoDecoder.DracoFloat32Array();
-      decoder.GetAttributeFloatForAllPoints(
-          dracoGeometry, attribute, attributeData);
       var numPoints = dracoGeometry.num_points();
       var numValues = numPoints * numComponents;
-      // Allocate space for attribute.
-      geometryBuffer[attributeName] = new Float32Array(numValues);
+      var attributeData;
+      var TypedBufferAttribute;
+
+      switch ( attributeType ) {
+
+        case Float32Array:
+          attributeData = new dracoDecoder.DracoFloat32Array();
+          decoder.GetAttributeFloatForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Float32Array( numValues );
+          TypedBufferAttribute = THREE.Float32BufferAttribute;
+          break;
+
+        case Int8Array:
+          attributeData = new dracoDecoder.DracoInt8Array();
+          decoder.GetAttributeInt8ForAllPoints(
+            dracoGeometry, attribute, attributeData );
+          geometryBuffer[ attributeName ] = new Int8Array( numValues );
+          TypedBufferAttribute = THREE.Int8BufferAttribute;
+          break;
+
+        case Int16Array:
+          attributeData = new dracoDecoder.DracoInt16Array();
+          decoder.GetAttributeInt16ForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Int16Array( numValues );
+          TypedBufferAttribute = THREE.Int16BufferAttribute;
+          break;
+
+        case Int32Array:
+          attributeData = new dracoDecoder.DracoInt32Array();
+          decoder.GetAttributeInt32ForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Int32Array( numValues );
+          TypedBufferAttribute = THREE.Int32BufferAttribute;
+          break;
+
+        case Uint8Array:
+          attributeData = new dracoDecoder.DracoUInt8Array();
+          decoder.GetAttributeUInt8ForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Uint8Array( numValues );
+          TypedBufferAttribute = THREE.Uint8BufferAttribute;
+          break;
+
+        case Uint16Array:
+          attributeData = new dracoDecoder.DracoUInt16Array();
+          decoder.GetAttributeUInt16ForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Uint16Array( numValues );
+          TypedBufferAttribute = THREE.Uint16BufferAttribute;
+          break;
+
+        case Uint32Array:
+          attributeData = new dracoDecoder.DracoUInt32Array();
+          decoder.GetAttributeUInt32ForAllPoints(
+            dracoGeometry, attribute, attributeData);
+          geometryBuffer[ attributeName ] = new Uint32Array( numValues );
+          TypedBufferAttribute = THREE.Uint32BufferAttribute;
+          break;
+
+        default:
+          var errorMsg = 'THREE.DRACOLoader: Unexpected attribute type.';
+          console.error( errorMsg );
+          throw new Error( errorMsg );
+
+      }
+      
       // Copy data from decoder.
       for (var i = 0; i < numValues; i++) {
         geometryBuffer[attributeName][i] = attributeData.GetValue(i);
       }
       // Add attribute to THREEJS geometry for rendering.
       geometry.addAttribute(attributeName,
-          new THREE.Float32BufferAttribute(geometryBuffer[attributeName],
+          new TypedBufferAttribute(geometryBuffer[attributeName],
             numComponents));
       dracoDecoder.destroy(attributeData);
     },
 
     convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
-                                        buffer, attributeUniqueIdMap) {
+                                        buffer, attributeUniqueIdMap,
+                                        attributeTypeMap) {
         if (this.getAttributeOptions('position').skipDequantization === true) {
           decoder.SkipAttributeTransform(dracoDecoder.POSITION);
         }
@@ -244,18 +310,19 @@ THREE.DRACOLoader.prototype = {
               }
               var attribute = decoder.GetAttribute(dracoGeometry, attId);
               this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
-                  attributeName, attribute, geometry, geometryBuffer);
+                  attributeName, Float32Array, attribute, geometry, geometryBuffer);
             }
           }
         }
 
         // Add attributes of user specified unique id. E.g. GLTF models.
         for (var attributeName in attributeUniqueIdMap) {
+          var attributeType = attributeTypeMap[attributeName] || Float32Array;
           var attributeId = attributeUniqueIdMap[attributeName];
           var attribute = decoder.GetAttributeByUniqueId(dracoGeometry,
                                                          attributeId);
           this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
-              attributeName, attribute, geometry, geometryBuffer);
+              attributeName, attributeType, attribute, geometry, geometryBuffer);
         }
 
         // For mesh, we need to generate the faces.

+ 428 - 149
examples/js/loaders/GLTFLoader.js

@@ -123,33 +123,42 @@ THREE.GLTFLoader = ( function () {
 
 			if ( json.extensionsUsed ) {
 
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_LIGHTS ) >= 0 ) {
+				for ( var i = 0; i < json.extensionsUsed.length; ++ i ) {
 
-					extensions[ EXTENSIONS.KHR_LIGHTS ] = new GLTFLightsExtension( json );
+					var extensionName = json.extensionsUsed[ i ];
+					var extensionsRequired = json.extensionsRequired || [];
 
-				}
-
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_UNLIT ) >= 0 ) {
+					switch ( extensionName ) {
 
-					extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] = new GLTFMaterialsUnlitExtension( json );
+						case EXTENSIONS.KHR_LIGHTS:
+							extensions[ extensionName ] = new GLTFLightsExtension( json );
+							break;
 
-				}
+						case EXTENSIONS.KHR_MATERIALS_UNLIT:
+							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension( json );
+							break;
 
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ) >= 0 ) {
+						case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
+							extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
+							break;
 
-					extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
+						case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
+							extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
+							break;
 
-				}
+						case EXTENSIONS.MSFT_TEXTURE_DDS:
+							extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension();
+							break;
 
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ) >= 0 ) {
+						default:
 
-					extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] = new GLTFDracoMeshCompressionExtension( this.dracoLoader );
+							if ( extensionsRequired.indexOf( extensionName ) >= 0 ) {
 
-				}
+								console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' );
 
-				if ( json.extensionsUsed.indexOf( EXTENSIONS.MSFT_TEXTURE_DDS ) >= 0 ) {
+							}
 
-					extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension();
+					}
 
 				}
 
@@ -163,16 +172,20 @@ THREE.GLTFLoader = ( function () {
 
 			} );
 
-			parser.parse( function ( scene, scenes, cameras, animations, asset ) {
+			parser.parse( function ( scene, scenes, cameras, animations, json ) {
 
 				var glTF = {
 					scene: scene,
 					scenes: scenes,
 					cameras: cameras,
 					animations: animations,
-					asset: asset
+					asset: json.asset,
+					parser: parser,
+					userData: {}
 				};
 
+				addUnknownExtensionsToUserData( extensions, glTF, json );
+
 				onLoad( glTF );
 
 			}, onError );
@@ -442,7 +455,7 @@ THREE.GLTFLoader = ( function () {
 	 *
 	 * Specification: https://github.com/KhronosGroup/glTF/pull/874
 	 */
-	function GLTFDracoMeshCompressionExtension ( dracoLoader ) {
+	function GLTFDracoMeshCompressionExtension ( json, dracoLoader ) {
 
 		if ( ! dracoLoader ) {
 
@@ -451,16 +464,20 @@ THREE.GLTFLoader = ( function () {
 		}
 
 		this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
+		this.json = json;
 		this.dracoLoader = dracoLoader;
 
 	}
 
 	GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {
 
+		var json = this.json;
 		var dracoLoader = this.dracoLoader;
 		var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
 		var gltfAttributeMap = primitive.extensions[ this.name ].attributes;
 		var threeAttributeMap = {};
+		var attributeNormalizedMap = {};
+		var attributeTypeMap = {};
 
 		for ( var attributeName in gltfAttributeMap ) {
 
@@ -470,11 +487,38 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
+		for ( attributeName in primitive.attributes ) {
+
+			if ( ATTRIBUTES[ attributeName ] !== undefined && gltfAttributeMap[ attributeName ] !== undefined ) {
+
+				var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
+				var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
+
+				attributeTypeMap[ ATTRIBUTES[ attributeName ] ]  = componentType;
+				attributeNormalizedMap[ ATTRIBUTES[ attributeName ] ] = accessorDef.normalized === true;
+
+			}
+
+		}
+
 		return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
 
 			return new Promise( function ( resolve ) {
 
-				dracoLoader.decodeDracoFile( bufferView, resolve, threeAttributeMap );
+				dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
+
+					for ( var attributeName in geometry.attributes ) {
+
+						var attribute = geometry.attributes[ attributeName ];
+						var normalized = attributeNormalizedMap[ attributeName ];
+
+						if ( normalized !== undefined ) attribute.normalized = normalized;
+
+					}
+
+					resolve( geometry );
+
+				}, threeAttributeMap, attributeTypeMap );
 
 			} );
 
@@ -1150,18 +1194,31 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
+	function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
+
+		// Add unknown glTF extensions to an object's userData.
+
+		for ( var name in objectDef.extensions ) {
+
+			if ( knownExtensions[ name ] === undefined ) {
+
+				object.userData.gltfExtensions = object.userData.gltfExtensions || {};
+				object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ];
+
+			}
+
+		}
+
+	}
+
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
 	 *
-	 * @param {THREE.Mesh} mesh
-	 * @param {GLTF.Mesh} meshDef
-	 * @param {GLTF.Primitive} primitiveDef
+	 * @param {THREE.Geometry} geometry
+	 * @param {Array<GLTF.Target>} targets
 	 * @param {Array<THREE.BufferAttribute>} accessors
 	 */
-	function addMorphTargets( mesh, meshDef, primitiveDef, accessors ) {
-
-		var geometry = mesh.geometry;
-		var targets = primitiveDef.targets;
+	function addMorphTargets( geometry, targets, accessors ) {
 
 		var hasMorphPosition = false;
 		var hasMorphNormal = false;
@@ -1269,6 +1326,14 @@ THREE.GLTFLoader = ( function () {
 		if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
 		if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
 
+	}
+
+	/**
+	 * @param {THREE.Mesh} mesh
+	 * @param {GLTF.Mesh} meshDef
+	 */
+	function updateMorphTargets( mesh, meshDef ) {
+
 		mesh.updateMorphTargets();
 
 		if ( meshDef.weights !== undefined ) {
@@ -1314,26 +1379,31 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
-		var attribA = a.attributes || {};
-		var attribB = b.attributes || {};
-		var keysA = Object.keys( attribA );
-		var keysB = Object.keys( attribB );
+		return isObjectEqual( a.attributes, b.attributes );
+
+	}
 
-		if ( keysA.length !== keysB.length ) {
+	function isObjectEqual( a, b ) {
 
-			return false;
+		if ( Object.keys( a ).length !== Object.keys( b ).length ) return false;
+
+		for ( var key in a ) {
+
+			if ( a[ key ] !== b[ key ] ) return false;
 
 		}
 
-		for ( var i = 0, il = keysA.length; i < il; i ++ ) {
+		return true;
+
+	}
 
-			var key = keysA[ i ];
+	function isArrayEqual( a, b ) {
 
-			if ( attribA[ key ] !== attribB[ key ] ) {
+		if ( a.length !== b.length ) return false;
 
-				return false;
+		for ( var i = 0, il = a.length; i < il; i ++ ) {
 
-			}
+			if ( a[ i ] !== b[ i ] ) return false;
 
 		}
 
@@ -1347,11 +1417,35 @@ THREE.GLTFLoader = ( function () {
 
 			var cached = cache[ i ];
 
-			if ( isPrimitiveEqual( cached.primitive, newPrimitive ) ) {
+			if ( isPrimitiveEqual( cached.primitive, newPrimitive ) ) return cached.promise;
 
-				return cached.promise;
+		}
 
-			}
+		return null;
+
+	}
+
+	function getCachedCombinedGeometry( cache, geometries ) {
+
+		for ( var i = 0, il = cache.length; i < il; i ++ ) {
+
+			var cached = cache[ i ];
+
+			if ( isArrayEqual( geometries, cached.baseGeometries ) ) return cached.geometry;
+
+		}
+
+		return null;
+
+	}
+
+	function getCachedMultiPassGeometry( cache, geometry, primitives ) {
+
+		for ( var i = 0, il = cache.length; i < il; i ++ ) {
+
+			var cached = cache[ i ];
+
+			if ( geometry === cached.baseGeometry && isArrayEqual( primitives, cached.primitives ) ) return cached.geometry;
 
 		}
 
@@ -1384,6 +1478,47 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
+	/**
+	 * Checks if we can build a single Mesh with MultiMaterial from multiple primitives.
+	 * Returns true if all primitives use the same attributes/morphAttributes/mode
+	 * and also have index. Otherwise returns false.
+	 *
+	 * @param {Array<GLTF.Primitive>} primitives
+	 * @return {Boolean}
+	 */
+	function isMultiPassGeometry( primitives ) {
+
+		if ( primitives.length < 2 ) return false;
+
+		var primitive0 = primitives[ 0 ];
+		var targets0 = primitive0.targets || [];
+
+		if ( primitive0.indices === undefined ) return false;
+
+		for ( var i = 1, il = primitives.length; i < il; i ++ ) {
+
+			var primitive = primitives[ i ];
+
+			if ( primitive0.mode !== primitive.mode ) return false;
+			if ( primitive.indices === undefined ) return false;
+			if ( ! isObjectEqual( primitive0.attributes, primitive.attributes ) ) return false;
+
+			var targets = primitive.targets || [];
+
+			if ( targets0.length !== targets.length ) return false;
+
+			for ( var j = 0, jl = targets0.length; j < jl; j ++ ) {
+
+				if ( ! isObjectEqual( targets0[ j ], targets[ j ] ) ) return false;
+
+			}
+
+		}
+
+		return true;
+
+	}
+
 	/* GLTF PARSER */
 
 	function GLTFParser( json, extensions, options ) {
@@ -1397,6 +1532,8 @@ THREE.GLTFLoader = ( function () {
 
 		// BufferGeometry caching
 		this.primitiveCache = [];
+		this.multiplePrimitivesCache = [];
+		this.multiPassGeometryCache = []
 
 		this.textureLoader = new THREE.TextureLoader( this.options.manager );
 		this.textureLoader.setCrossOrigin( this.options.crossOrigin );
@@ -1428,10 +1565,9 @@ THREE.GLTFLoader = ( function () {
 			var scenes = dependencies.scenes || [];
 			var scene = scenes[ json.scene || 0 ];
 			var animations = dependencies.animations || [];
-			var asset = json.asset;
 			var cameras = dependencies.cameras || [];
 
-			onLoad( scene, scenes, cameras, animations, asset );
+			onLoad( scene, scenes, cameras, animations, json );
 
 		} ).catch( onError );
 
@@ -2110,6 +2246,8 @@ THREE.GLTFLoader = ( function () {
 
 			if ( materialDef.extras ) material.userData = materialDef.extras;
 
+			if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
+
 			return material;
 
 		} );
@@ -2121,7 +2259,7 @@ THREE.GLTFLoader = ( function () {
 	 * @param  {GLTF.Primitive} primitiveDef
 	 * @param  {Array<THREE.BufferAttribute>} accessors
 	 */
-	function addPrimitiveAttributes ( geometry, primitiveDef, accessors ) {
+	function addPrimitiveAttributes( geometry, primitiveDef, accessors ) {
 
 		var attributes = primitiveDef.attributes;
 
@@ -2138,16 +2276,33 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
-		if ( primitiveDef.indices !== undefined && !geometry.index ) {
+		if ( primitiveDef.indices !== undefined && ! geometry.index ) {
 
 			geometry.setIndex( accessors[ primitiveDef.indices ] );
 
 		}
 
+		if ( primitiveDef.targets !== undefined ) {
+
+			addMorphTargets( geometry, primitiveDef.targets, accessors );
+
+		}
+
+		if ( primitiveDef.extras !== undefined ) {
+
+			geometry.userData = primitiveDef.extras;
+
+		}
+
 	}
 
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
+	 *
+	 * Creates BufferGeometries from primitives.
+	 * If we can build a single BufferGeometry with .groups from multiple primitives, returns one BufferGeometry.
+	 * Otherwise, returns BufferGeometries without .groups as many as primitives.
+	 *
 	 * @param {Array<Object>} primitives
 	 * @return {Promise<Array<THREE.BufferGeometry>>}
 	 */
@@ -2157,6 +2312,22 @@ THREE.GLTFLoader = ( function () {
 		var extensions = this.extensions;
 		var cache = this.primitiveCache;
 
+		var isMultiPass = isMultiPassGeometry( primitives );
+		var originalPrimitives;
+
+		if ( isMultiPass ) {
+
+			originalPrimitives = primitives; // save original primitives and use later
+
+			// We build a single BufferGeometry with .groups from multiple primitives
+			// because all primitives share the same attributes/morph/mode and have indices.
+
+			primitives = [ primitives[ 0 ] ];
+
+			// Sets .groups and combined indices to a geometry later in this method.
+
+		}
+
 		return this.getDependencies( 'accessor' ).then( function ( accessors ) {
 
 			var pending = [];
@@ -2200,12 +2371,7 @@ THREE.GLTFLoader = ( function () {
 					var geometryPromise = Promise.resolve( geometry );
 
 					// Cache this geometry
-					cache.push( {
-
-						primitive: primitive,
-						promise: geometryPromise
-
-					} );
+					cache.push( { primitive: primitive, promise: geometryPromise } );
 
 					pending.push( geometryPromise );
 
@@ -2213,7 +2379,83 @@ THREE.GLTFLoader = ( function () {
 
 			}
 
-			return Promise.all( pending );
+			return Promise.all( pending ).then( function ( geometries ) {
+
+				if ( isMultiPass ) {
+
+					var baseGeometry = geometries[ 0 ];
+
+					// See if we've already created this combined geometry
+					var cache = parser.multiPassGeometryCache;
+					var cached = getCachedMultiPassGeometry( cache, baseGeometry, originalPrimitives );
+
+					if ( cached !== null ) return [ cached.geometry ];
+
+					// Cloning geometry because of index override.
+					// Attributes can be reused so cloning by myself here.
+					var geometry = new THREE.BufferGeometry();
+
+					geometry.name = baseGeometry.name;
+					geometry.userData = baseGeometry.userData;
+
+					for ( var key in baseGeometry.attributes ) geometry.addAttribute( key, baseGeometry.attributes[ key ] );
+					for ( var key in baseGeometry.morphAttributes ) geometry.morphAttributes[ key ] = baseGeometry.morphAttributes[ key ];
+
+					var indices = [];
+					var offset = 0;
+
+					for ( var i = 0, il = originalPrimitives.length; i < il; i ++ ) {
+
+						var accessor = accessors[ originalPrimitives[ i ].indices ];
+
+						for ( var j = 0, jl = accessor.count; j < jl; j ++ ) indices.push( accessor.array[ j ] );
+
+						geometry.addGroup( offset, accessor.count, i );
+
+						offset += accessor.count;
+
+					}
+
+					geometry.setIndex( indices );
+
+					cache.push( { geometry: geometry, baseGeometry: baseGeometry, primitives: originalPrimitives } );
+
+					return [ geometry ];
+
+				} else if ( geometries.length > 1 && THREE.BufferGeometryUtils !== undefined ) {
+
+					// Tries to merge geometries with BufferGeometryUtils if possible
+
+					for ( var i = 1, il = primitives.length; i < il; i ++ ) {
+
+						// can't merge if draw mode is different
+						if ( primitives[ 0 ].mode !== primitives[ i ].mode ) return geometries;
+
+					}
+
+					// See if we've already created this combined geometry
+					var cache = parser.multiplePrimitivesCache;
+					var cached = getCachedCombinedGeometry( cache, geometries );
+
+					if ( cached ) {
+
+						if ( cached.geometry !== null ) return [ cached.geometry ];
+
+					} else {
+
+						var geometry = THREE.BufferGeometryUtils.mergeBufferGeometries( geometries, true );
+
+						cache.push( { geometry: geometry, baseGeometries: geometries } );
+
+						if ( geometry !== null ) return [ geometry ];
+
+					}
+
+				}
+
+				return geometries;
+
+			} );
 
 		} );
 
@@ -2239,188 +2481,220 @@ THREE.GLTFLoader = ( function () {
 
 		] ).then( function ( dependencies ) {
 
-			var group = new THREE.Group();
-
 			var primitives = meshDef.primitives;
+			var originalMaterials = [];
+
+			for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+
+				originalMaterials[ i ] = primitives[ i ].material === undefined
+					? createDefaultMaterial()
+					: dependencies.materials[ primitives[ i ].material ];
+
+			}
 
 			return scope.loadGeometries( primitives ).then( function ( geometries ) {
 
-				for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+				var isMultiMaterial = geometries.length === 1 && geometries[ 0 ].groups.length > 0;
 
-					var primitive = primitives[ i ];
-					var geometry = geometries[ i ];
+				var meshes = [];
 
-					var material = primitive.material === undefined
-						? createDefaultMaterial()
-						: dependencies.materials[ primitive.material ];
+				for ( var i = 0, il = geometries.length; i < il; i ++ ) {
 
-					if ( material.aoMap
-							&& geometry.attributes.uv2 === undefined
-							&& geometry.attributes.uv !== undefined ) {
+					var geometry = geometries[ i ];
+					var primitive = primitives[ i ];
 
-						console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
-						geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
+					// 1. create Mesh
 
-					}
+					var mesh;
 
-					// If the material will be modified later on, clone it now.
-					var useVertexColors = geometry.attributes.color !== undefined;
-					var useFlatShading = geometry.attributes.normal === undefined;
-					var useSkinning = meshDef.isSkinnedMesh === true;
-					var useMorphTargets = primitive.targets !== undefined;
+					var material = isMultiMaterial ? originalMaterials : originalMaterials[ i ]
 
-					if ( useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
+					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
+						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
+						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
+						primitive.mode === undefined ) {
 
-						if ( material.isGLTFSpecularGlossinessMaterial ) {
+						// .isSkinnedMesh isn't in glTF spec. See .markDefs()
+						mesh = meshDef.isSkinnedMesh === true
+							? new THREE.SkinnedMesh( geometry, material )
+							: new THREE.Mesh( geometry, material );
 
-							var specGlossExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ];
-							material = specGlossExtension.cloneMaterial( material );
+						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
 
-						} else {
+							mesh.drawMode = THREE.TriangleStripDrawMode;
+
+						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
 
-							material = material.clone();
+							mesh.drawMode = THREE.TriangleFanDrawMode;
 
 						}
 
-					}
+					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
+
+						mesh = new THREE.LineSegments( geometry, material );
+
+					} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
 
-					if ( useVertexColors ) {
+						mesh = new THREE.Line( geometry, material );
 
-						material.vertexColors = THREE.VertexColors;
-						material.needsUpdate = true;
+					} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+
+						mesh = new THREE.LineLoop( geometry, material );
+
+					} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+
+						mesh = new THREE.Points( geometry, material );
+
+					} else {
+
+						throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
 
 					}
 
-					if ( useFlatShading ) {
+					if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
 
-						material.flatShading = true;
+						updateMorphTargets( mesh, meshDef );
 
 					}
 
-					var mesh;
+					mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
 
-					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
-						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
-						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
-						primitive.mode === undefined ) {
+					if ( geometries.length > 1 ) mesh.name += '_' + i;
 
-						if ( useSkinning ) {
+					if ( meshDef.extras !== undefined ) mesh.userData = meshDef.extras;
 
-							mesh = new THREE.SkinnedMesh( geometry, material );
-							material.skinning = true;
+					meshes.push( mesh );
 
-						} else {
+					// 2. update Material depending on Mesh and BufferGeometry
 
-							mesh = new THREE.Mesh( geometry, material );
+					var materials = isMultiMaterial ? mesh.material : [ mesh.material ];
 
-						}
+					var useVertexColors = geometry.attributes.color !== undefined;
+					var useFlatShading = geometry.attributes.normal === undefined;
+					var useSkinning = mesh.isSkinnedMesh === true;
+					var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
+					var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
 
-						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
+					for ( var j = 0, jl = materials.length; j < jl; j ++ ) {
 
-							mesh.drawMode = THREE.TriangleStripDrawMode;
+						var material = materials[ j ];
 
-						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
+						if ( mesh.isPoints ) {
 
-							mesh.drawMode = THREE.TriangleFanDrawMode;
+							var cacheKey = 'PointsMaterial:' + material.uuid;
 
-						}
+							var pointsMaterial = scope.cache.get( cacheKey );
 
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ||
-						primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ||
-						primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+							if ( ! pointsMaterial ) {
 
-						var cacheKey = 'LineBasicMaterial:' + material.uuid;
+								pointsMaterial = new THREE.PointsMaterial();
+								THREE.Material.prototype.copy.call( pointsMaterial, material );
+								pointsMaterial.color.copy( material.color );
+								pointsMaterial.map = material.map;
+								pointsMaterial.lights = false;  // PointsMaterial doesn't support lights yet
 
-						var lineMaterial = scope.cache.get( cacheKey );
+								scope.cache.add( cacheKey, pointsMaterial );
 
-						if ( ! lineMaterial ) {
+							}
 
-							lineMaterial = new THREE.LineBasicMaterial();
-							THREE.Material.prototype.copy.call( lineMaterial, material );
-							lineMaterial.color.copy( material.color );
-							lineMaterial.lights = false;  // LineBasicMaterial doesn't support lights yet
+							material = pointsMaterial;
 
-							scope.cache.add( cacheKey, lineMaterial );
+						} else if ( mesh.isLine ) {
 
-						}
+							var cacheKey = 'LineBasicMaterial:' + material.uuid;
 
-						material = lineMaterial;
+							var lineMaterial = scope.cache.get( cacheKey );
 
-						if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
+							if ( ! lineMaterial ) {
 
-							mesh = new THREE.LineSegments( geometry, material );
+								lineMaterial = new THREE.LineBasicMaterial();
+								THREE.Material.prototype.copy.call( lineMaterial, material );
+								lineMaterial.color.copy( material.color );
+								lineMaterial.lights = false;  // LineBasicMaterial doesn't support lights yet
 
-						} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
+								scope.cache.add( cacheKey, lineMaterial );
 
-							mesh = new THREE.Line( geometry, material );
+							}
 
-						} else {
+							material = lineMaterial;
 
-							mesh = new THREE.LineLoop( geometry, material );
+						}
+
+						// If the material will be modified later on, clone it now.
+						if ( useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
+
+							material = material.isGLTFSpecularGlossinessMaterial
+									? extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].cloneMaterial( material )
+									: material.clone();
 
 						}
 
-					} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+						if ( useSkinning ) {
+
+							material.skinning = true;
+
+						}
 
-						var cacheKey = 'PointsMaterial:' + material.uuid;
+						if ( useVertexColors ) {
 
-						var pointsMaterial = scope.cache.get( cacheKey );
+							material.vertexColors = THREE.VertexColors;
+							material.needsUpdate = true;
 
-						if ( ! pointsMaterial ) {
+						}
 
-							pointsMaterial = new THREE.PointsMaterial();
-							THREE.Material.prototype.copy.call( pointsMaterial, material );
-							pointsMaterial.color.copy( material.color );
-							pointsMaterial.map = material.map;
-							pointsMaterial.lights = false;  // PointsMaterial doesn't support lights yet
+						if ( useFlatShading ) {
 
-							scope.cache.add( cacheKey, pointsMaterial );
+							material.flatShading = true;
 
 						}
 
-						material = pointsMaterial;
+						if ( useMorphTargets ) {
 
-						mesh = new THREE.Points( geometry, material );
+							material.morphTargets = true;
 
-					} else {
+						}
 
-						throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
+						if ( useMorphNormals ) {
 
-					}
+							material.morphNormals = true;
 
-					mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
+						}
 
-					if ( useMorphTargets ) {
+						materials[ j ] = material;
 
-						addMorphTargets( mesh, meshDef, primitive, dependencies.accessors );
+						// workarounds for mesh and geometry
 
-						material.morphTargets = true;
+						if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
 
-						if ( mesh.geometry.morphAttributes.normal !== undefined ) material.morphNormals = true;
+							console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
+							geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
 
-					}
+						}
 
-					if ( meshDef.extras !== undefined ) mesh.userData = meshDef.extras;
-					if ( primitive.extras !== undefined ) mesh.geometry.userData = primitive.extras;
+						if ( material.isGLTFSpecularGlossinessMaterial ) {
 
-					// for Specular-Glossiness.
-					if ( material.isGLTFSpecularGlossinessMaterial === true ) {
+							// for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
+							mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
 
-						mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
+						}
 
 					}
 
-					if ( primitives.length > 1 ) {
+					mesh.material = isMultiMaterial ? materials : materials[ 0 ];
 
-						mesh.name += '_' + i;
+				}
 
-						group.add( mesh );
+				if ( meshes.length === 1 ) {
 
-					} else {
+					return meshes[ 0 ];
 
-						return mesh;
+				}
 
-					}
+				var group = new THREE.Group();
+
+				for ( var i = 0, il = meshes.length; i < il; i ++ ) {
+
+					group.add( meshes[ i ] );
 
 				}
 
@@ -2664,6 +2938,7 @@ THREE.GLTFLoader = ( function () {
 
 			var node;
 
+			// .isBone isn't in glTF spec. See .markDefs
 			if ( nodeDef.isBone === true ) {
 
 				node = new THREE.Bone();
@@ -2730,6 +3005,8 @@ THREE.GLTFLoader = ( function () {
 
 			if ( nodeDef.extras ) node.userData = nodeDef.extras;
 
+			if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef );
+
 			if ( nodeDef.matrix !== undefined ) {
 
 				var matrix = new THREE.Matrix4();
@@ -2862,6 +3139,8 @@ THREE.GLTFLoader = ( function () {
 
 				if ( sceneDef.extras ) scene.userData = sceneDef.extras;
 
+				if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
+
 				var nodeIds = sceneDef.nodes || [];
 
 				for ( var i = 0, il = nodeIds.length; i < il; i ++ ) {

+ 1169 - 1930
examples/js/loaders/MMDLoader.js

@@ -3,31 +3,23 @@
  *
  * Dependencies
  *  - mmd-parser https://github.com/takahirox/mmd-parser
- *  - ammo.js https://github.com/kripken/ammo.js
  *  - THREE.TGALoader
- *  - THREE.MMDPhysics
- *  - THREE.CCDIKSolver
  *  - THREE.OutlineEffect
  *
+ * MMDLoader creates Three.js Objects from MMD resources as
+ * PMD, PMX, VMD, and VPD files.
  *
- * This loader loads and parses PMD/PMX and VMD binary files
- * then creates mesh for Three.js.
- *
- * PMD/PMX is a model data format and VMD is a motion data format
- * used in MMD(Miku Miku Dance).
- *
- * MMD is a 3D CG animation tool which is popular in Japan.
- *
+ * PMD/PMX is a model data format, VMD is a motion data format
+ * VPD is a posing data format used in MMD(Miku Miku Dance).
  *
  * MMD official site
- *  http://www.geocities.jp/higuchuu4/index_e.htm
+ *  - http://www.geocities.jp/higuchuu4/index_e.htm
  *
- * PMD, VMD format
- *  http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
+ * PMD, VMD format (in Japanese)
+ *  - http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
  *
  * PMX format
- *  http://gulshan-i-raz.geo.jp/labs/2012/10/17/pmx-format1/
- *
+ *  - https://gist.github.com/felixjones/f8a06bd48f9da9a4539f
  *
  * TODO
  *  - light motion in vmd support.
@@ -37,2627 +29,1874 @@
  *  - shadow support.
  */
 
-THREE.MMDLoader = function ( manager ) {
+THREE.MMDLoader = ( function () {
 
-	THREE.Loader.call( this );
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-	this.parser = new MMDParser.Parser();
-	this.textureCrossOrigin = null;
-
-};
+	/**
+	 * @param {THREE.LoadingManager} manager
+	 */
+	function MMDLoader( manager ) {
 
-THREE.MMDLoader.prototype = Object.create( THREE.Loader.prototype );
-THREE.MMDLoader.prototype.constructor = THREE.MMDLoader;
+		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-/*
- * base64 encoded defalut toon textures toon00.bmp - toon10.bmp
- * Users don't need to prepare default texture files.
- *
- * This idea is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
- */
-THREE.MMDLoader.prototype.defaultToonTextures = [
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/bWiiMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh8aBHZBl14e8wAAAABJRU5ErkJggg==',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOUlEQVRYR+3WMREAMAwDsYY/yoDI7MLwIiP40+RJklfcCCBAgAABAgTqArfb/QMCCBAgQIAAgbbAB3z/e0F3js2cAAAAAElFTkSuQmCC',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/B5ilMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh81dWyx0gFwKAAAAABJRU5ErkJggg==',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOklEQVRYR+3WoREAMAwDsWb/UQtCy9wxTOQJ/oQ8SXKKGwEECBAgQIBAXeDt7f4BAQQIECBAgEBb4AOz8Hzx7WLY4wAAAABJRU5ErkJggg==',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABPUlEQVRYR+1XwW7CMAy1+f9fZOMysSEOEweEOPRNdm3HbdOyIhAcklPrOs/PLy9RygBALxzcCDQFmgJNgaZAU6Ap0BR4PwX8gsRMVLssMRH5HcpzJEaWL7EVg9F1IHRlyqQohgVr4FGUlUcMJSjcUlDw0zvjeun70cLWmneoyf7NgBTQSniBTQQSuJAZsOnnaczjIMb5hCiuHKxokCrJfVnrctyZL0PkJAJe1HMil4nxeyi3Ypfn1kX51jpPvo/JeCNC4PhVdHdJw2XjBR8brF8PEIhNVn12AgP7uHsTBguBn53MUZCqv7Lp07Pn5k1Ro+uWmUNn7D+M57rtk7aG0Vo73xyF/fbFf0bPJjDXngnGocDTdFhygZjwUQrMNrDcmZlQT50VJ/g/UwNyHpu778+yW+/ksOz/BFo54P4AsUXMfRq7XWsAAAAASUVORK5CYII=',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACMElEQVRYR+2Xv4pTQRTGf2dubhLdICiii2KnYKHVolhauKWPoGAnNr6BD6CvIVaihYuI2i1ia0BY0MZGRHQXjZj/mSPnnskfNWiWZUlzJ5k7M2cm833nO5Mziej2DWWJRUoCpQKlAntSQCqgw39/iUWAGmh37jrRnVsKlgpiqmkoGVABA7E57fvY+pJDdgKqF6HzFCSADkDq+F6AHABtQ+UMVE5D7zXod7fFNhTEckTbj5XQgHzNN+5tQvc5NG7C6BNkp6D3EmpXHDR+dQAjFLchW3VS9rlw3JBh+B7ys5Cf9z0GW1C/7P32AyBAOAz1q4jGliIH3YPuBnSfQX4OGreTIgEYQb/pBDtPnEQ4CivXYPAWBk13oHrB54yA9QuSn2H4AcKRpEILDt0BUzj+RLR1V5EqjD66NPRBVpLcQwjHoHYJOhsQv6U4mnzmrIXJCFr4LDwm/xBUoboG9XX4cc9VKdYoSA2yk5NQLJaKDUjTBoveG3Z2TElTxwjNK4M3LEZgUdDdruvcXzKBpStgp2NPiWi3ks9ZXxIoFVi+AvHLdc9TqtjL3/aYjpPlrzOcEnK62Szhimdd7xX232zFDTgtxezOu3WNMRLjiKgjtOhHVMd1loynVHvOgjuIIJMaELEqhJAV/RCSLbWTcfPFakFgFlALTRRvx+ok6Hlp/Q+v3fmx90bMyUzaEAhmM3KvHlXTL5DxnbGf/1M8RNNACLL5MNtPxP/mypJAqcDSFfgFhpYqWUzhTEAAAAAASUVORK5CYII=',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
-	'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
-	'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.loader = new THREE.FileLoader( this.manager );
 
-	this.textureCrossOrigin = value;
+		this.parser = null; // lazy generation
+		this.meshBuilder = new MeshBuilder( this.manager );
+		this.animationBuilder = new AnimationBuilder();
 
-};
+	}
 
-THREE.MMDLoader.prototype.load = function ( modelUrl, vmdUrls, callback, onProgress, onError ) {
+	MMDLoader.prototype = {
 
-	var scope = this;
+		constructor: MMDLoader,
 
-	this.loadModel( modelUrl, function ( mesh ) {
+		crossOrigin: undefined,
 
-		scope.loadVmds( vmdUrls, function ( vmd ) {
+		/**
+		 * @param {string} value
+		 * @return {THREE.MMDLoader}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
 
-			scope.pourVmdIntoModel( mesh, vmd );
-			callback( mesh );
+			this.crossOrigin = crossOrigin;
+			return this;
 
-		}, onProgress, onError );
+		},
 
-	}, onProgress, onError );
+		// Load MMD assets as Three.js Object
 
-};
+		/**
+		 * Loads Model file (.pmd or .pmx) as a THREE.SkinnedMesh.
+		 *
+		 * @param {string} url - url to Model(.pmd or .pmx) file
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		load: function ( url, onLoad, onProgress, onError ) {
 
-THREE.MMDLoader.prototype.loadModel = function ( url, callback, onProgress, onError ) {
+			var parser = this._getParser();
+			var builder = this.meshBuilder.setCrossOrigin( this.crossOrigin );
 
-	var scope = this;
+			var texturePath = THREE.LoaderUtils.extractUrlBase( url );
+			var modelExtension = this._extractExtension( url ).toLowerCase();
 
-	var texturePath = THREE.LoaderUtils.extractUrlBase( url );
-	var modelExtension = this.extractExtension( url );
+			// Should I detect by seeing header?
+			if ( modelExtension !== 'pmd' && modelExtension !== 'pmx' ) {
 
-	this.loadFileAsBuffer( url, function ( buffer ) {
+				if ( onError ) onError( new Error( 'THREE.MMDLoader: Unknown model file extension .' + modelExtension + '.' ) );
 
-		callback( scope.createModel( buffer, modelExtension, texturePath, onProgress, onError ) );
+				return;
 
-	}, onProgress, onError );
+			}
 
-};
+			this[ modelExtension === 'pmd' ? 'loadPMD' : 'loadPMX' ]( url, function ( data ) {
 
-THREE.MMDLoader.prototype.createModel = function ( buffer, modelExtension, texturePath, onProgress, onError ) {
+				onLoad(	builder.build( data, texturePath, onProgress, onError )	);
 
-	return this.createMesh( this.parseModel( buffer, modelExtension ), texturePath, onProgress, onError );
+			}, onProgress, onError );
 
-};
+		},
 
-THREE.MMDLoader.prototype.loadVmd = function ( url, callback, onProgress, onError ) {
+		/**
+		 * Loads Motion file(s) (.vmd) as a THREE.AnimationClip.
+		 * If two or more files are specified, they'll be merged.
+		 *
+		 * @param {string|Array<string>} url - url(s) to animation(.vmd) file(s)
+		 * @param {THREE.SkinnedMesh|THREE.Camera} object - tracks will be fitting to this object
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadAnimation: function ( url, object, onLoad, onProgress, onError ) {
 
-	var scope = this;
+			var builder = this.animationBuilder;
 
-	this.loadFileAsBuffer( url, function ( buffer ) {
+			this.loadVMD( url, function ( vmd ) {
 
-		callback( scope.parseVmd( buffer ) );
+				onLoad( object.isCamera
+					? builder.buildCameraAnimation( vmd )
+					: builder.build( vmd, object ) );
 
-	}, onProgress, onError );
+			}, onProgress, onError );
 
-};
+		},
 
-THREE.MMDLoader.prototype.loadVmds = function ( urls, callback, onProgress, onError ) {
+		/**
+		 * Loads mode file and motion file(s) as an object containing
+		 * a THREE.SkinnedMesh and a THREE.AnimationClip.
+		 * Tracks of THREE.AnimationClip are fitting to the model.
+		 *
+		 * @param {string} modelUrl - url to Model(.pmd or .pmx) file
+		 * @param {string|Array{string}} vmdUrl - url(s) to animation(.vmd) file
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadWithAnimation: function ( modelUrl, vmdUrl, onLoad, onProgress, onError ) {
 
-	var scope = this;
+			var scope = this;
 
-	var vmds = [];
-	urls = urls.slice();
+			this.load( modelUrl, function ( mesh ) {
 
-	function run() {
+				scope.loadAnimation( vmdUrl, mesh, function ( animation ) {
 
-		var url = urls.shift();
+					onLoad( {
+						mesh: mesh,
+						animation: animation
+					} );
 
-		scope.loadVmd( url, function ( vmd ) {
+				}, onProgress, onError );
 
-			vmds.push( vmd );
+			}, onProgress, onError );
 
-			if ( urls.length > 0 ) {
+		},
 
-				run();
+		// Load MMD assets as Object data parsed by MMDParser
 
-			} else {
+		/**
+		 * Loads .pmd file as an Object.
+		 *
+		 * @param {string} url - url to .pmd file
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadPMD: function ( url, onLoad, onProgress, onError ) {
 
-				callback( scope.mergeVmds( vmds ) );
+			var parser = this._getParser();
 
-			}
+			this.loader
+				.setMimeType( undefined )
+				.setResponseType( 'arraybuffer' )
+				.load( url, function ( buffer ) {
 
-		}, onProgress, onError );
+					onLoad( parser.parsePmd( buffer, true ) );
 
-	}
+				}, onProgress, onError );
 
-	run();
+		},
 
-};
+		/**
+		 * Loads .pmx file as an Object.
+		 *
+		 * @param {string} url - url to .pmx file
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadPMX: function ( url, onLoad, onProgress, onError ) {
 
-THREE.MMDLoader.prototype.loadAudio = function ( url, callback, onProgress, onError ) {
+			var parser = this._getParser();
 
-	var listener = new THREE.AudioListener();
-	var audio = new THREE.Audio( listener );
-	var loader = new THREE.AudioLoader( this.manager );
+			this.loader
+				.setMimeType( undefined )
+				.setResponseType( 'arraybuffer' )
+				.load( url, function ( buffer ) {
 
-	loader.load( url, function ( buffer ) {
+					onLoad( parser.parsePmx( buffer, true ) );
 
-		audio.setBuffer( buffer );
-		callback( audio, listener );
+				}, onProgress, onError );
 
-	}, onProgress, onError );
+		},
 
-};
+		/**
+		 * Loads .vmd file as an Object. If two or more files are specified
+		 * they'll be merged.
+		 *
+		 * @param {string|Array<string>} url - url(s) to .vmd file(s)
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadVMD: function ( url, onLoad, onProgress, onError ) {
 
-THREE.MMDLoader.prototype.loadVpd = function ( url, callback, onProgress, onError, params ) {
+			var urls = Array.isArray( url ) ? url : [ url ];
 
-	var scope = this;
+			var vmds = [];
+			var vmdNum = urls.length;
 
-	var func = ( ( params && params.charcode === 'unicode' ) ? this.loadFileAsText : this.loadFileAsShiftJISText ).bind( this );
+			var scope = this;
+			var parser = this._getParser();
 
-	func( url, function ( text ) {
+			this.loader
+				.setMimeType( undefined )
+				.setResponseType( 'arraybuffer' );
 
-		callback( scope.parseVpd( text ) );
+			for ( var i = 0, il = urls.length; i < il; i ++ ) {
 
-	}, onProgress, onError );
+				this.loader.load( urls[ i ], function ( buffer ) {
 
-};
+					vmds.push( parser.parseVmd( buffer, true ) );
 
-THREE.MMDLoader.prototype.parseModel = function ( buffer, modelExtension ) {
+					if ( vmds.length === vmdNum ) onLoad( parser.mergeVmds( vmds ) );
 
-	// Should I judge from model data header?
-	switch ( modelExtension.toLowerCase() ) {
+				}, onProgress, onError );
 
-		case 'pmd':
-			return this.parsePmd( buffer );
+			}
 
-		case 'pmx':
-			return this.parsePmx( buffer );
+		},
 
-		default:
-			throw 'extension ' + modelExtension + ' is not supported.';
+		/**
+		 * Loads .vpd file as an Object.
+		 *
+		 * @param {string} url - url to .vpd file
+		 * @param {boolean} isUnicode
+		 * @param {function} onLoad
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 */
+		loadVPD: function ( url, isUnicode, onLoad, onProgress, onError, params ) {
 
-	}
+			params = params || {};
 
-};
+			var parser = this._getParser();
 
-THREE.MMDLoader.prototype.parsePmd = function ( buffer ) {
+			this.loader
+				.setMimeType( isUnicode ? undefined : 'text/plain; charset=shift_jis' )
+				.setResponseType( 'text' )
+				.load( url, function ( text ) {
 
-	return this.parser.parsePmd( buffer, true );
+					onLoad( parser.parseVpd( text, true ) );
 
-};
+				}, onProgress, onError );
 
-THREE.MMDLoader.prototype.parsePmx = function ( buffer ) {
+		},
 
-	return this.parser.parsePmx( buffer, true );
+		// private methods
 
-};
+		_extractExtension: function ( url ) {
 
-THREE.MMDLoader.prototype.parseVmd = function ( buffer ) {
+			var index = url.lastIndexOf( '.' );
+			return index < 0 ? '' : url.slice( index + 1 );
 
-	return this.parser.parseVmd( buffer, true );
+		},
 
-};
+		_getParser: function () {
 
-THREE.MMDLoader.prototype.parseVpd = function ( text ) {
+			if ( this.parser === null ) {
 
-	return this.parser.parseVpd( text, true );
+				if ( typeof MMDParser === 'undefined' ) {
 
-};
+					throw new Error( 'THREE.MMDLoader: Import MMDParser https://github.com/takahirox/mmd-parser' );
 
-THREE.MMDLoader.prototype.mergeVmds = function ( vmds ) {
+				}
 
-	return this.parser.mergeVmds( vmds );
+				this.parser = new MMDParser.Parser();
 
-};
+			}
 
-THREE.MMDLoader.prototype.pourVmdIntoModel = function ( mesh, vmd, name ) {
+			return this.parser;
 
-	this.createAnimation( mesh, vmd, name );
+		}
 
-};
+	};
 
-THREE.MMDLoader.prototype.pourVmdIntoCamera = function ( camera, vmd, name ) {
+	// Utilities
 
-	var helper = new THREE.MMDLoader.DataCreationHelper();
+	/*
+	 * base64 encoded defalut toon textures toon00.bmp - toon10.bmp.
+	 * We don't need to request external toon image files.
+	 * This idea is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
+	 */
+	var DEFAULT_TOON_TEXTURES = [
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/bWiiMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh8aBHZBl14e8wAAAABJRU5ErkJggg==',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOUlEQVRYR+3WMREAMAwDsYY/yoDI7MLwIiP40+RJklfcCCBAgAABAgTqArfb/QMCCBAgQIAAgbbAB3z/e0F3js2cAAAAAElFTkSuQmCC',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAN0lEQVRYR+3WQREAMBACsZ5/B5ilMvgEBTt5cW37hjsBBAgQIECAwFwgyfYPCCBAgAABAgTWAh81dWyx0gFwKAAAAABJRU5ErkJggg==',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAOklEQVRYR+3WoREAMAwDsWb/UQtCy9wxTOQJ/oQ8SXKKGwEECBAgQIBAXeDt7f4BAQQIECBAgEBb4AOz8Hzx7WLY4wAAAABJRU5ErkJggg==',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABPUlEQVRYR+1XwW7CMAy1+f9fZOMysSEOEweEOPRNdm3HbdOyIhAcklPrOs/PLy9RygBALxzcCDQFmgJNgaZAU6Ap0BR4PwX8gsRMVLssMRH5HcpzJEaWL7EVg9F1IHRlyqQohgVr4FGUlUcMJSjcUlDw0zvjeun70cLWmneoyf7NgBTQSniBTQQSuJAZsOnnaczjIMb5hCiuHKxokCrJfVnrctyZL0PkJAJe1HMil4nxeyi3Ypfn1kX51jpPvo/JeCNC4PhVdHdJw2XjBR8brF8PEIhNVn12AgP7uHsTBguBn53MUZCqv7Lp07Pn5k1Ro+uWmUNn7D+M57rtk7aG0Vo73xyF/fbFf0bPJjDXngnGocDTdFhygZjwUQrMNrDcmZlQT50VJ/g/UwNyHpu778+yW+/ksOz/BFo54P4AsUXMfRq7XWsAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACMElEQVRYR+2Xv4pTQRTGf2dubhLdICiii2KnYKHVolhauKWPoGAnNr6BD6CvIVaihYuI2i1ia0BY0MZGRHQXjZj/mSPnnskfNWiWZUlzJ5k7M2cm833nO5Mziej2DWWJRUoCpQKlAntSQCqgw39/iUWAGmh37jrRnVsKlgpiqmkoGVABA7E57fvY+pJDdgKqF6HzFCSADkDq+F6AHABtQ+UMVE5D7zXod7fFNhTEckTbj5XQgHzNN+5tQvc5NG7C6BNkp6D3EmpXHDR+dQAjFLchW3VS9rlw3JBh+B7ys5Cf9z0GW1C/7P32AyBAOAz1q4jGliIH3YPuBnSfQX4OGreTIgEYQb/pBDtPnEQ4CivXYPAWBk13oHrB54yA9QuSn2H4AcKRpEILDt0BUzj+RLR1V5EqjD66NPRBVpLcQwjHoHYJOhsQv6U4mnzmrIXJCFr4LDwm/xBUoboG9XX4cc9VKdYoSA2yk5NQLJaKDUjTBoveG3Z2TElTxwjNK4M3LEZgUdDdruvcXzKBpStgp2NPiWi3ks9ZXxIoFVi+AvHLdc9TqtjL3/aYjpPlrzOcEnK62Szhimdd7xX232zFDTgtxezOu3WNMRLjiKgjtOhHVMd1loynVHvOgjuIIJMaELEqhJAV/RCSLbWTcfPFakFgFlALTRRvx+ok6Hlp/Q+v3fmx90bMyUzaEAhmM3KvHlXTL5DxnbGf/1M8RNNACLL5MNtPxP/mypJAqcDSFfgFhpYqWUzhTEAAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII=',
+		'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAL0lEQVRYR+3QQREAAAzCsOFfNJPBJ1XQS9r2hsUAAQIECBAgQIAAAQIECBAgsBZ4MUx/ofm2I/kAAAAASUVORK5CYII='
+	];
+
+	// Builders. They build Three.js object from Object data parsed by MMDParser.
+
+	/**
+	 * @param {THREE.LoadingManager} manager
+	 */
+	function MeshBuilder( manager ) {
 
-	var initAnimation = function () {
+		this.geometryBuilder = new GeometryBuilder();
+		this.materialBuilder = new MaterialBuilder( manager );
 
-		var orderedMotions = helper.createOrderedMotionArray( vmd.cameras );
+	}
 
-		var times = [];
-		var centers = [];
-		var quaternions = [];
-		var positions = [];
-		var fovs = [];
+	MeshBuilder.prototype = {
 
-		var cInterpolations = [];
-		var qInterpolations = [];
-		var pInterpolations = [];
-		var fInterpolations = [];
+		constructor: MeshBuilder,
 
-		var quaternion = new THREE.Quaternion();
-		var euler = new THREE.Euler();
-		var position = new THREE.Vector3();
-		var center = new THREE.Vector3();
+		crossOrigin: undefined,
 
-		var pushVector3 = function ( array, vec ) {
+		/**
+		 * @param {string} crossOrigin
+		 * @return {MeshBuilder}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
 
-			array.push( vec.x );
-			array.push( vec.y );
-			array.push( vec.z );
+			this.crossOrigin = crossOrigin;
+			return this;
 
-		};
+		},
 
-		var pushQuaternion = function ( array, q ) {
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @param {string} texturePath
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 * @return {THREE.SkinnedMesh}
+		 */
+		build: function ( data, texturePath, onProgress, onError ) {
 
-			array.push( q.x );
-			array.push( q.y );
-			array.push( q.z );
-			array.push( q.w );
+			var geometry = this.geometryBuilder.build( data );
+			var material = this.materialBuilder
+					.setCrossOrigin( this.crossOrigin )
+					.setTexturePath( texturePath )
+					.build( data, geometry, onProgress, onError );
 
-		};
+			var mesh = new THREE.SkinnedMesh( geometry, material );
 
-		var pushInterpolation = function ( array, interpolation, index ) {
+			// console.log( mesh ); // for console debug
 
-			array.push( interpolation[ index * 4 + 0 ] / 127 ); // x1
-			array.push( interpolation[ index * 4 + 1 ] / 127 ); // x2
-			array.push( interpolation[ index * 4 + 2 ] / 127 ); // y1
-			array.push( interpolation[ index * 4 + 3 ] / 127 ); // y2
+			return mesh;
 
-		};
+		}
 
-		var createTrack = function ( node, type, times, values, interpolations ) {
+	};
 
-			/*
-			 * optimizes here not to let KeyframeTrackPrototype optimize
-			 * because KeyframeTrackPrototype optimizes times and values but
-			 * doesn't optimize interpolations.
-			 */
-			if ( times.length > 2 ) {
+	//
 
-				times = times.slice();
-				values = values.slice();
-				interpolations = interpolations.slice();
+	function GeometryBuilder() {
 
-				var stride = values.length / times.length;
-				var interpolateStride = ( stride === 3 ) ? 12 : 4; // 3: Vector3, others: Quaternion or Number
+	}
 
-				var index = 1;
+	GeometryBuilder.prototype = {
 
-				for ( var aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex ++ ) {
+		constructor: GeometryBuilder,
 
-					for ( var i = 0; i < stride; i ++ ) {
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @return {THREE.BufferGeometry}
+		 */
+		build: function ( data ) {
 
-						if ( values[ index * stride + i ] !== values[ ( index - 1 ) * stride + i ] ||
-							values[ index * stride + i ] !== values[ aheadIndex * stride + i ] ) {
+			// for geometry
+			var positions = [];
+			var uvs = [];
+			var normals = [];
 
-							index ++;
-							break;
+			var indices = [];
 
-						}
+			var groups = [];
 
-					}
+			var bones = [];
+			var skinIndices = [];
+			var skinWeights = [];
 
-					if ( aheadIndex > index ) {
+			var morphTargets = [];
+			var morphPositions = [];
 
-						times[ index ] = times[ aheadIndex ];
+			var iks = [];
+			var grants = [];
 
-						for ( var i = 0; i < stride; i ++ ) {
+			var rigidBodies = [];
+			var constraints = [];
 
-							values[ index * stride + i ] = values[ aheadIndex * stride + i ];
+			// for work
+			var offset = 0;
+			var boneTypeTable = {};
 
-						}
+			// positions, normals, uvs, skinIndices, skinWeights
 
-						for ( var i = 0; i < interpolateStride; i ++ ) {
+			for ( var i = 0; i < data.metadata.vertexCount; i ++ ) {
 
-							interpolations[ index * interpolateStride + i ] = interpolations[ aheadIndex * interpolateStride + i ];
+				var v = data.vertices[ i ];
 
-						}
+				for ( var j = 0, jl = v.position.length; j < jl; j ++ ) {
 
-					}
+					positions.push( v.position[ j ] );
 
 				}
 
-				times.length = index + 1;
-				values.length = ( index + 1 ) * stride;
-				interpolations.length = ( index + 1 ) * interpolateStride;
+				for ( var j = 0, jl = v.normal.length; j < jl; j ++ ) {
 
-			}
+					normals.push( v.normal[ j ] );
 
-			var track = new THREE[ type ]( node, times, values );
+				}
 
-			track.createInterpolant = function InterpolantFactoryMethodCubicBezier( result ) {
+				for ( var j = 0, jl = v.uv.length; j < jl; j ++ ) {
 
-				return new THREE.MMDLoader.CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, new Float32Array( interpolations ) );
+					uvs.push( v.uv[ j ] );
 
-			};
+				}
 
-			return track;
+				for ( var j = 0; j < 4; j ++ ) {
 
-		};
+					skinIndices.push( v.skinIndices.length - 1 >= j ? v.skinIndices[ j ] : 0.0 );
 
-		for ( var i = 0; i < orderedMotions.length; i ++ ) {
+				}
 
-			var m = orderedMotions[ i ];
+				for ( var j = 0; j < 4; j ++ ) {
 
-			var time = m.frameNum / 30;
-			var pos = m.position;
-			var rot = m.rotation;
-			var distance = m.distance;
-			var fov = m.fov;
-			var interpolation = m.interpolation;
+					skinWeights.push( v.skinWeights.length - 1 >= j ? v.skinWeights[ j ] : 0.0 );
 
-			position.set( 0, 0, - distance );
-			center.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
+				}
 
-			euler.set( - rot[ 0 ], - rot[ 1 ], - rot[ 2 ] );
-			quaternion.setFromEuler( euler );
+			}
 
-			position.add( center );
-			position.applyQuaternion( quaternion );
+			// indices
 
-			times.push( time );
+			for ( var i = 0; i < data.metadata.faceCount; i ++ ) {
 
-			pushVector3( centers, center );
-			pushQuaternion( quaternions, quaternion );
-			pushVector3( positions, position );
+				var face = data.faces[ i ];
 
-			fovs.push( fov );
+				for ( var j = 0, jl = face.indices.length; j < jl; j ++ ) {
 
-			for ( var j = 0; j < 3; j ++ ) {
+					indices.push( face.indices[ j ] );
 
-				pushInterpolation( cInterpolations, interpolation, j );
+				}
 
 			}
 
-			pushInterpolation( qInterpolations, interpolation, 3 );
-
-			// use same one parameter for x, y, z axis.
-			for ( var j = 0; j < 3; j ++ ) {
+			// groups
 
-				pushInterpolation( pInterpolations, interpolation, 4 );
-
-			}
+			for ( var i = 0; i < data.metadata.materialCount; i ++ ) {
 
-			pushInterpolation( fInterpolations, interpolation, 5 );
+				var material = data.materials[ i ];
 
-		}
+				groups.push( {
+					offset: offset * 3,
+					count: material.faceCount * 3
+				} );
 
-		if ( times.length === 0 ) return;
+				offset += material.faceCount;
 
-		var tracks = [];
+			}
 
-		tracks.push( createTrack( '.center', 'VectorKeyframeTrack', times, centers, cInterpolations ) );
-		tracks.push( createTrack( '.quaternion', 'QuaternionKeyframeTrack', times, quaternions, qInterpolations ) );
-		tracks.push( createTrack( '.position', 'VectorKeyframeTrack', times, positions, pInterpolations ) );
-		tracks.push( createTrack( '.fov', 'NumberKeyframeTrack', times, fovs, fInterpolations ) );
+			// bones
 
-		var clip = new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name, - 1, tracks );
+			for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) {
 
-		if ( camera.center === undefined ) camera.center = new THREE.Vector3( 0, 0, 0 );
-		if ( camera.animations === undefined ) camera.animations = [];
-		camera.animations.push( clip );
+				var body = data.rigidBodies[ i ];
+				var value = boneTypeTable[ body.boneIndex ];
 
-	};
+				// keeps greater number if already value is set without any special reasons
+				value = value === undefined ? body.type : Math.max( body.type, value );
 
-	initAnimation();
+				boneTypeTable[ body.boneIndex ] = value;
 
-};
+			}
 
-THREE.MMDLoader.prototype.extractExtension = function ( url ) {
+			for ( var i = 0; i < data.metadata.boneCount; i ++ ) {
 
-	var index = url.lastIndexOf( '.' );
+				var boneData = data.bones[ i ];
 
-	if ( index < 0 ) {
+				var bone = {
+					parent: boneData.parentIndex,
+					name: boneData.name,
+					pos: boneData.position.slice( 0, 3 ),
+					rotq: [ 0, 0, 0, 1 ],
+					scl: [ 1, 1, 1 ],
+					rigidBodyType: boneTypeTable[ i ] !== undefined ? boneTypeTable[ i ] : - 1
+				};
 
-		return null;
+				if ( bone.parent !== - 1 ) {
 
-	}
+					bone.pos[ 0 ] -= data.bones[ bone.parent ].position[ 0 ];
+					bone.pos[ 1 ] -= data.bones[ bone.parent ].position[ 1 ];
+					bone.pos[ 2 ] -= data.bones[ bone.parent ].position[ 2 ];
 
-	return url.slice( index + 1 );
+				}
 
-};
+				bones.push( bone );
 
-THREE.MMDLoader.prototype.loadFile = function ( url, onLoad, onProgress, onError, responseType, mimeType ) {
+			}
 
-	var loader = new THREE.FileLoader( this.manager );
+			// iks
 
-	if ( mimeType !== undefined ) loader.setMimeType( mimeType );
+			// TODO: remove duplicated codes between PMD and PMX
+			if ( data.metadata.format === 'pmd' ) {
 
-	loader.setResponseType( responseType );
+				for ( var i = 0; i < data.metadata.ikCount; i ++ ) {
 
-	var request = loader.load( url, function ( result ) {
+					var ik = data.iks[ i ];
 
-		onLoad( result );
+					var param = {
+						target: ik.target,
+						effector: ik.effector,
+						iteration: ik.iteration,
+						maxAngle: ik.maxAngle * 4,
+						links: []
+					};
 
-	}, onProgress, onError );
+					for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
 
-	return request;
+						var link = {};
+						link.index = ik.links[ j ].index;
+						link.enabled = true;
 
-};
+						if ( data.bones[ link.index ].name.indexOf( 'ひざ' ) >= 0 ) {
 
-THREE.MMDLoader.prototype.loadFileAsBuffer = function ( url, onLoad, onProgress, onError ) {
+							link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
 
-	this.loadFile( url, onLoad, onProgress, onError, 'arraybuffer' );
+						}
 
-};
+						param.links.push( link );
 
-THREE.MMDLoader.prototype.loadFileAsText = function ( url, onLoad, onProgress, onError ) {
+					}
 
-	this.loadFile( url, onLoad, onProgress, onError, 'text' );
+					iks.push( param );
 
-};
+				}
 
-THREE.MMDLoader.prototype.loadFileAsShiftJISText = function ( url, onLoad, onProgress, onError ) {
+			} else {
 
-	this.loadFile( url, onLoad, onProgress, onError, 'text', 'text/plain; charset=shift_jis' );
+				for ( var i = 0; i < data.metadata.boneCount; i ++ ) {
 
-};
+					var ik = data.bones[ i ].ik;
 
-THREE.MMDLoader.prototype.createMesh = function ( model, texturePath, onProgress, onError ) {
+					if ( ik === undefined ) continue;
 
-	var scope = this;
-	var geometry = new THREE.BufferGeometry();
-	var materials = [];
+					var param = {
+						target: i,
+						effector: ik.effector,
+						iteration: ik.iteration,
+						maxAngle: ik.maxAngle,
+						links: []
+					};
 
-	var buffer = {};
+					for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
 
-	buffer.vertices = [];
-	buffer.uvs = [];
-	buffer.normals = [];
-	buffer.skinIndices = [];
-	buffer.skinWeights = [];
-	buffer.indices = [];
+						var link = {};
+						link.index = ik.links[ j ].index;
+						link.enabled = true;
 
-	var initVartices = function () {
+						if ( ik.links[ j ].angleLimitation === 1 ) {
 
-		for ( var i = 0; i < model.metadata.vertexCount; i ++ ) {
+							// Revert if rotationMin/Max doesn't work well
+							// link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
 
-			var v = model.vertices[ i ];
+							var rotationMin = ik.links[ j ].lowerLimitationAngle;
+							var rotationMax = ik.links[ j ].upperLimitationAngle;
 
-			for ( var j = 0, jl = v.position.length; j < jl; j ++ ) {
+							// Convert Left to Right coordinate by myself because
+							// MMDParser doesn't convert. It's a MMDParser's bug
 
-				buffer.vertices.push( v.position[ j ] );
+							var tmp1 = - rotationMax[ 0 ];
+							var tmp2 = - rotationMax[ 1 ];
+							rotationMax[ 0 ] = - rotationMin[ 0 ];
+							rotationMax[ 1 ] = - rotationMin[ 1 ];
+							rotationMin[ 0 ] = tmp1;
+							rotationMin[ 1 ] = tmp2;
 
-			}
+							link.rotationMin = new THREE.Vector3().fromArray( rotationMin );
+							link.rotationMax = new THREE.Vector3().fromArray( rotationMax );
 
-			for ( var j = 0, jl = v.normal.length; j < jl; j ++ ) {
+						}
 
-				buffer.normals.push( v.normal[ j ] );
+						param.links.push( link );
 
-			}
+					}
 
-			for ( var j = 0, jl = v.uv.length; j < jl; j ++ ) {
+					iks.push( param );
 
-				buffer.uvs.push( v.uv[ j ] );
+				}
 
 			}
 
-			for ( var j = 0; j < 4; j ++ ) {
-
-				buffer.skinIndices.push( v.skinIndices.length - 1 >= j ? v.skinIndices[ j ] : 0.0 );
-
-			}
+			// grants
 
-			for ( var j = 0; j < 4; j ++ ) {
+			if ( data.metadata.format === 'pmx' ) {
 
-				buffer.skinWeights.push( v.skinWeights.length - 1 >= j ? v.skinWeights[ j ] : 0.0 );
+				for ( var i = 0; i < data.metadata.boneCount; i ++ ) {
 
-			}
+					var boneData = data.bones[ i ];
+					var grant = boneData.grant;
 
-		}
+					if ( grant === undefined ) continue;
 
-	};
+					var param = {
+						index: i,
+						parentIndex: grant.parentIndex,
+						ratio: grant.ratio,
+						isLocal: grant.isLocal,
+						affectRotation: grant.affectRotation,
+						affectPosition: grant.affectPosition,
+						transformationClass: boneData.transformationClass
+					};
 
-	var initFaces = function () {
+					grants.push( param );
 
-		for ( var i = 0; i < model.metadata.faceCount; i ++ ) {
+				}
 
-			var f = model.faces[ i ];
+				grants.sort( function ( a, b ) {
 
-			for ( var j = 0, jl = f.indices.length; j < jl; j ++ ) {
+					return a.transformationClass - b.transformationClass;
 
-				buffer.indices.push( f.indices[ j ] );
+				} );
 
 			}
 
-		}
+			// morph
 
-	};
+			function updateAttributes( attribute, morph, ratio ) {
 
-	var initBones = function () {
+				for ( var i = 0; i < morph.elementCount; i ++ ) {
 
-		var bones = [];
+					var element = morph.elements[ i ];
 
-		var rigidBodies = model.rigidBodies;
-		var dictionary = {};
+					var index;
 
-		for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
+					if ( data.metadata.format === 'pmd' ) {
 
-			var body = rigidBodies[ i ];
-			var value = dictionary[ body.boneIndex ];
+						index = data.morphs[ 0 ].elements[ element.index ].index;
 
-			// keeps greater number if already value is set without any special reasons
-			value = value === undefined ? body.type : Math.max( body.type, value );
+					} else {
 
-			dictionary[ body.boneIndex ] = value;
+						index = element.index;
 
-		}
+					}
 
-		for ( var i = 0; i < model.metadata.boneCount; i ++ ) {
+					attribute.array[ index * 3 + 0 ] += element.position[ 0 ] * ratio;
+					attribute.array[ index * 3 + 1 ] += element.position[ 1 ] * ratio;
+					attribute.array[ index * 3 + 2 ] += element.position[ 2 ] * ratio;
 
-			var bone = {};
-			var b = model.bones[ i ];
+				}
 
-			bone.parent = b.parentIndex;
-			bone.name = b.name;
-			bone.pos = [ b.position[ 0 ], b.position[ 1 ], b.position[ 2 ] ];
-			bone.rotq = [ 0, 0, 0, 1 ];
-			bone.scl = [ 1, 1, 1 ];
+			}
 
-			if ( bone.parent !== - 1 ) {
+			for ( var i = 0; i < data.metadata.morphCount; i ++ ) {
 
-				bone.pos[ 0 ] -= model.bones[ bone.parent ].position[ 0 ];
-				bone.pos[ 1 ] -= model.bones[ bone.parent ].position[ 1 ];
-				bone.pos[ 2 ] -= model.bones[ bone.parent ].position[ 2 ];
+				var morph = data.morphs[ i ];
+				var params = { name: morph.name };
 
-			}
+				var attribute = new THREE.Float32BufferAttribute( data.metadata.vertexCount * 3, 3 );
+				attribute.name = morph.name;
 
-			bone.rigidBodyType = dictionary[ i ] !== undefined ? dictionary[ i ] : - 1;
+				for ( var j = 0; j < data.metadata.vertexCount * 3; j ++ ) {
 
-			bones.push( bone );
+					attribute.array[ j ] = positions[ j ];
 
-		}
+				}
 
-		geometry.bones = bones;
+				if ( data.metadata.format === 'pmd' ) {
 
-	};
+					if ( i !== 0 ) {
+
+						updateAttributes( attribute, morph, 1.0 );
 
-	var initIKs = function () {
+					}
 
-		var iks = [];
+				} else {
 
-		// TODO: remove duplicated codes between PMD and PMX
-		if ( model.metadata.format === 'pmd' ) {
+					if ( morph.type === 0 ) { // group
 
-			for ( var i = 0; i < model.metadata.ikCount; i ++ ) {
+						for ( var j = 0; j < morph.elementCount; j ++ ) {
 
-				var ik = model.iks[ i ];
-				var param = {};
+							var morph2 = data.morphs[ morph.elements[ j ].index ];
+							var ratio = morph.elements[ j ].ratio;
 
-				param.target = ik.target;
-				param.effector = ik.effector;
-				param.iteration = ik.iteration;
-				param.maxAngle = ik.maxAngle * 4;
-				param.links = [];
+							if ( morph2.type === 1 ) {
 
-				for ( var j = 0; j < ik.links.length; j ++ ) {
+								updateAttributes( attribute, morph2, ratio );
 
-					var link = {};
-					link.index = ik.links[ j ].index;
+							} else {
 
-					if ( model.bones[ link.index ].name.indexOf( 'ひざ' ) >= 0 ) {
+								// TODO: implement
 
-						link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
+							}
 
-					}
+						}
 
-					param.links.push( link );
+					} else if ( morph.type === 1 ) { // vertex
 
-				}
+						updateAttributes( attribute, morph, 1.0 );
 
-				iks.push( param );
+					} else if ( morph.type === 2 ) { // bone
 
-			}
+						// TODO: implement
 
-		} else {
+					} else if ( morph.type === 3 ) { // uv
 
-			for ( var i = 0; i < model.metadata.boneCount; i ++ ) {
+						// TODO: implement
 
-				var b = model.bones[ i ];
-				var ik = b.ik;
+					} else if ( morph.type === 4 ) { // additional uv1
 
-				if ( ik === undefined ) {
+						// TODO: implement
 
-					continue;
+					} else if ( morph.type === 5 ) { // additional uv2
 
-				}
+						// TODO: implement
 
-				var param = {};
+					} else if ( morph.type === 6 ) { // additional uv3
 
-				param.target = i;
-				param.effector = ik.effector;
-				param.iteration = ik.iteration;
-				param.maxAngle = ik.maxAngle;
-				param.links = [];
+						// TODO: implement
 
-				for ( var j = 0; j < ik.links.length; j ++ ) {
+					} else if ( morph.type === 7 ) { // additional uv4
 
-					var link = {};
-					link.index = ik.links[ j ].index;
-					link.enabled = true;
+						// TODO: implement
 
-					if ( ik.links[ j ].angleLimitation === 1 ) {
+					} else if ( morph.type === 8 ) { // material
 
-						link.limitation = new THREE.Vector3( 1.0, 0.0, 0.0 );
-						// TODO: use limitation angles
-						// link.lowerLimitationAngle;
-						// link.upperLimitationAngle;
+						// TODO: implement
 
 					}
 
-					param.links.push( link );
-
 				}
 
-				iks.push( param );
+				morphTargets.push( params );
+				morphPositions.push( attribute );
 
 			}
 
-		}
+			// rigid bodies from rigidBodies field.
 
-		geometry.iks = iks;
+			for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) {
 
-	};
+				var rigidBody = data.rigidBodies[ i ];
+				var params = {};
 
-	var initGrants = function () {
+				for ( var key in rigidBody ) {
 
-		if ( model.metadata.format === 'pmd' ) {
+					params[ key ] = rigidBody[ key ];
 
-			return;
+				}
 
-		}
+				/*
+				 * RigidBody position parameter in PMX seems global position
+				 * while the one in PMD seems offset from corresponding bone.
+				 * So unify being offset.
+				 */
+				if ( data.metadata.format === 'pmx' ) {
 
-		var grants = [];
+					if ( params.boneIndex !== - 1 ) {
 
-		for ( var i = 0; i < model.metadata.boneCount; i ++ ) {
+						var bone = data.bones[ params.boneIndex ];
+						params.position[ 0 ] -= bone.position[ 0 ];
+						params.position[ 1 ] -= bone.position[ 1 ];
+						params.position[ 2 ] -= bone.position[ 2 ];
 
-			var b = model.bones[ i ];
-			var grant = b.grant;
+					}
 
-			if ( grant === undefined ) {
+				}
 
-				continue;
+				rigidBodies.push( params );
 
 			}
 
-			var param = {};
+			// constraints from constraints field.
 
-			param.index = i;
-			param.parentIndex = grant.parentIndex;
-			param.ratio = grant.ratio;
-			param.isLocal = grant.isLocal;
-			param.affectRotation = grant.affectRotation;
-			param.affectPosition = grant.affectPosition;
-			param.transformationClass = b.transformationClass;
+			for ( var i = 0; i < data.metadata.constraintCount; i ++ ) {
 
-			grants.push( param );
+				var constraint = data.constraints[ i ];
+				var params = {};
 
-		}
+				for ( var key in constraint ) {
 
-		grants.sort( function ( a, b ) {
+					params[ key ] = constraint[ key ];
 
-			return a.transformationClass - b.transformationClass;
+				}
 
-		} );
+				var bodyA = rigidBodies[ params.rigidBodyIndex1 ];
+				var bodyB = rigidBodies[ params.rigidBodyIndex2 ];
 
-		geometry.grants = grants;
+				// Refer to http://www20.atpages.jp/katwat/wp/?p=4135
+				if ( bodyA.type !== 0 && bodyB.type === 2 ) {
 
-	};
+					if ( bodyA.boneIndex !== - 1 && bodyB.boneIndex !== - 1 &&
+					     data.bones[ bodyB.boneIndex ].parentIndex === bodyA.boneIndex ) {
 
-	var initMorphs = function () {
+						bodyB.type = 1;
 
-		function updateVertex( attribute, index, v, ratio ) {
+					}
 
-			attribute.array[ index * 3 + 0 ] += v.position[ 0 ] * ratio;
-			attribute.array[ index * 3 + 1 ] += v.position[ 1 ] * ratio;
-			attribute.array[ index * 3 + 2 ] += v.position[ 2 ] * ratio;
+				}
 
-		}
+				constraints.push( params );
 
-		function updateVertices( attribute, m, ratio ) {
+			}
 
-			for ( var i = 0; i < m.elementCount; i ++ ) {
+			// build BufferGeometry.
 
-				var v = m.elements[ i ];
+			var geometry = new THREE.BufferGeometry();
 
-				var index;
+			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+			geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+			geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+			geometry.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( skinIndices, 4 ) );
+			geometry.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( skinWeights, 4 ) );
+			geometry.setIndex( indices );
 
-				if ( model.metadata.format === 'pmd' ) {
+			for ( var i = 0, il = groups.length; i < il; i ++ ) {
 
-					index = model.morphs[ 0 ].elements[ v.index ].index;
+				geometry.addGroup( groups[ i ].offset, groups[ i ].count, i );
 
-				} else {
+			}
 
-					index = v.index;
+			geometry.bones = bones;
 
-				}
+			geometry.morphTargets = morphTargets;
+			geometry.morphAttributes.position = morphPositions;
 
-				updateVertex( attribute, index, v, ratio );
+			geometry.iks = iks;
+			geometry.grants = grants;
 
-			}
+			geometry.rigidBodies = rigidBodies;
+			geometry.constraints = constraints;
 
-		}
+			geometry.mmdFormat = data.metadata.format;
+
+			geometry.computeBoundingSphere();
 
-		var morphTargets = [];
-		var attributes = [];
+			return geometry;
 
-		for ( var i = 0; i < model.metadata.morphCount; i ++ ) {
+		}
 
-			var m = model.morphs[ i ];
-			var params = { name: m.name };
+	};
 
-			var attribute = new THREE.Float32BufferAttribute( model.metadata.vertexCount * 3, 3 );
-			attribute.name = m.name;
+	//
 
-			for ( var j = 0; j < model.metadata.vertexCount * 3; j ++ ) {
+	/**
+	 * @param {THREE.LoadingManager} manager
+	 */
+	function MaterialBuilder( manager ) {
 
-				attribute.array[ j ] = buffer.vertices[ j ];
+		this.manager = manager;
 
-			}
+		this.textureLoader = new THREE.TextureLoader( this.manager );
+		this.tgaLoader = null; // lazy generation
 
-			if ( model.metadata.format === 'pmd' ) {
+	}
 
-				if ( i !== 0 ) {
+	MaterialBuilder.prototype = {
 
-					updateVertices( attribute, m, 1.0 );
+		constructor: MaterialBuilder,
 
-				}
+		crossOrigin: undefined,
 
-			} else {
+		texturePath: undefined,
 
-				if ( m.type === 0 ) { // group
+		/**
+		 * @param {string} crossOrigin
+		 * @return {MaterialBuilder}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
 
-					for ( var j = 0; j < m.elementCount; j ++ ) {
+			this.crossOrigin = crossOrigin;
+			return this;
 
-						var m2 = model.morphs[ m.elements[ j ].index ];
-						var ratio = m.elements[ j ].ratio;
+		},
 
-						if ( m2.type === 1 ) {
+		/**
+		 * @param {string} texturePath
+		 * @return {MaterialBuilder}
+		 */
+		setTexturePath: function ( texturePath ) {
 
-							updateVertices( attribute, m2, ratio );
+			this.texturePath = texturePath;
+			return this;
 
-						} else {
+		},
 
-							// TODO: implement
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @param {THREE.BufferGeometry} geometry - some properties are dependend on geometry
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 * @return {Array<THREE.MeshToonMaterial>}
+		 */
+		build: function ( data, geometry, onProgress, onError ) {
 
-						}
+			var materials = [];
 
-					}
+			var textures = {};
 
-				} else if ( m.type === 1 ) { // vertex
+			this.textureLoader.setCrossOrigin( this.crossOrigin );
 
-					updateVertices( attribute, m, 1.0 );
+			// materials
 
-				} else if ( m.type === 2 ) { // bone
+			for ( var i = 0; i < data.metadata.materialCount; i ++ ) {
 
-					// TODO: implement
+				var material = data.materials[ i ];
 
-				} else if ( m.type === 3 ) { // uv
+				var params = { userData: {} };
 
-					// TODO: implement
+				if ( material.name !== undefined ) params.name = material.name;
 
-				} else if ( m.type === 4 ) { // additional uv1
+				/*
+				 * Color
+				 *
+				 * MMD         MeshToonMaterial
+				 * diffuse  -  color
+				 * specular -  specular
+				 * ambient  -  emissive * a
+				 *               (a = 1.0 without map texture or 0.2 with map texture)
+				 *
+				 * 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().fromArray( material.diffuse );
+				params.opacity = material.diffuse[ 3 ];
+				params.specular = new THREE.Color().fromArray( material.specular );
+				params.emissive = new THREE.Color().fromArray( material.ambient );
+				params.shininess = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+				params.transparent = params.opacity !== 1.0;
 
-					// TODO: implement
+				// 
 
-				} else if ( m.type === 5 ) { // additional uv2
+				params.skinning = geometry.bones.length > 0 ? true : false;
+				params.morphTargets = geometry.morphTargets.length > 0 ? true : false;
+				params.lights = true;
+				params.fog = true;
 
-					// TODO: implement
+				// blend
 
-				} else if ( m.type === 6 ) { // additional uv3
+				params.blending = THREE.CustomBlending;
+				params.blendSrc = THREE.SrcAlphaFactor;
+				params.blendDst = THREE.OneMinusSrcAlphaFactor;
+				params.blendSrcAlpha = THREE.SrcAlphaFactor;
+				params.blendDstAlpha = THREE.DstAlphaFactor;
 
-					// TODO: implement
+				// side
 
-				} else if ( m.type === 7 ) { // additional uv4
+				if ( data.metadata.format === 'pmx' && ( material.flag & 0x1 ) === 1 ) {
 
-					// TODO: implement
+					params.side = THREE.DoubleSide;
 
-				} else if ( m.type === 8 ) { // material
+				} else {
 
-					// TODO: implement
+					params.side = params.opacity === 1.0 ? THREE.FrontSide : THREE.DoubleSide;
 
 				}
 
-			}
-
-			morphTargets.push( params );
-			attributes.push( attribute );
+				if ( data.metadata.format === 'pmd' ) {
 
-		}
+					// map, envMap
 
-		geometry.morphTargets = morphTargets;
-		geometry.morphAttributes.position = attributes;
+					if ( material.fileName ) {
 
-	};
+						var fileName = material.fileName;
+						var fileNames = fileName.split( '*' );
 
-	var initMaterials = function () {
+						// fileNames[ 0 ]: mapFileName
+						// fileNames[ 1 ]: envMapFileName( optional )
 
-		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 = [];
+						params.map = this._loadTexture( fileNames[ 0 ], textures );
 
-		if ( scope.textureCrossOrigin !== null ) textureLoader.setCrossOrigin( scope.textureCrossOrigin );
+						if ( fileNames.length > 1 ) {
 
-		function loadTexture( filePath, params ) {
+							var extension = fileNames[ 1 ].slice( - 4 ).toLowerCase();
 
-			if ( params === undefined ) {
+							params.envMap = this._loadTexture(
+								fileNames[ 1 ],
+								textures,
+								{ sphericalReflectionMapping: true }
+							);
 
-				params = {};
+							params.combine = extension === '.sph'
+								? THREE.MultiplyOperation
+								: THREE.AddOperation;
 
-			}
+						}
 
-			var fullPath;
+					}
 
-			if ( params.defaultTexturePath === true ) {
+					// gradientMap
 
-				try {
+					var toonFileName = ( material.toonIndex === - 1 )
+						? 'toon00.bmp'
+						: data.toonTextures[ material.toonIndex ].fileName;
 
-					fullPath = scope.defaultToonTextures[ parseInt( filePath.match( 'toon([0-9]{2})\.bmp$' )[ 1 ] ) ];
+					params.gradientMap = this._loadTexture(
+						toonFileName,
+						textures,
+						{
+							isToonTexture: true,
+							isDefaultToonTexture: this._isDefaultToonTexture( toonFileName )
+						}
+					);
 
-				} catch ( e ) {
+					// parameters for OutlineEffect
 
-					console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like not right default texture path. Using toon00.bmp instead.' );
-					fullPath = scope.defaultToonTextures[ 0 ];
+					params.userData.outlineParameters = {
+						thickness: material.edgeFlag === 1 ? 0.003 : 0.0,
+						color: [ 0, 0, 0 ],
+						alpha: 1.0,
+						visible: material.edgeFlag === 1
+					};
 
-				}
+				} else {
 
-			} else {
+					// map
 
-				fullPath = texturePath + filePath;
+					if ( material.textureIndex !== - 1 ) {
 
-			}
+						params.map = this._loadTexture( data.textures[ material.textureIndex ], textures );
 
-			if ( textures[ fullPath ] !== undefined ) return fullPath;
+					}
 
-			var loader = THREE.Loader.Handlers.get( fullPath );
+					// envMap TODO: support m.envFlag === 3
 
-			if ( loader === null ) {
+					if ( material.envTextureIndex !== - 1 && ( material.envFlag === 1 || material.envFlag == 2 ) ) {
 
-				loader = ( filePath.indexOf( '.tga' ) >= 0 ) ? tgaLoader : textureLoader;
+						params.envMap = this._loadTexture(
+							data.textures[ material.envTextureIndex ],
+							textures, { sphericalReflectionMapping: true }
+						);
 
-			}
+						params.combine = material.envFlag === 1
+							? THREE.MultiplyOperation
+							: THREE.AddOperation;
 
-			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 ) {
+					// gradientMap
 
-					var image = t.image;
-					var width = image.width;
-					var height = image.height;
+					var toonFileName, isDefaultToon;
 
-					canvas.width = width;
-					canvas.height = height;
+					if ( material.toonIndex === - 1 || material.toonFlag !== 0 ) {
 
-					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 );
+						toonFileName = 'toon' + ( '0' + ( material.toonIndex + 1 ) ).slice( - 2 ) + '.bmp';
+						isDefaultToon = true;
 
-					t.image = context.getImageData( 0, 0, width, height );
+					} else {
 
-				}
+						toonFileName = data.textures[ material.toonIndex ];
+						isDefaultToon = false;
 
-				t.flipY = false;
-				t.wrapS = THREE.RepeatWrapping;
-				t.wrapT = THREE.RepeatWrapping;
+					}
 
-				for ( var i = 0; i < texture.readyCallbacks.length; i ++ ) {
+					params.gradientMap = this._loadTexture(
+						toonFileName,
+						textures,
+						{
+							isToonTexture: true,
+							isDefaultToonTexture: isDefaultToon
+						}
+					);
 
-					texture.readyCallbacks[ i ]( texture );
+					// parameters for OutlineEffect
+					params.userData.outlineParameters = {
+						thickness: material.edgeSize / 300,  // TODO: better calculation?
+						color: material.edgeColor.slice( 0, 3 ),
+						alpha: material.edgeColor[ 3 ],
+						visible: ( material.flag & 0x10 ) !== 0 && material.edgeSize > 0.0
+					};
 
 				}
 
-				delete texture.readyCallbacks;
+				if ( params.map !== undefined ) {
 
-			}, onProgress, onError );
+					if ( ! params.transparent ) {
 
-			if ( params.sphericalReflectionMapping === true ) {
+						this._checkImageTransparency( params.map, geometry, i );
 
-				texture.mapping = THREE.SphericalReflectionMapping;
+					}
 
-			}
+					params.emissive.multiplyScalar( 0.2 );
 
-			texture.readyCallbacks = [];
+				}
 
-			textures[ fullPath ] = texture;
+				materials.push( new THREE.MeshToonMaterial( params ) );
 
-			return fullPath;
+			}
 
-		}
+			if ( data.metadata.format === 'pmx' ) {
 
-		function getTexture( name, textures ) {
+				// set transparent true if alpha morph is defined.
 
-			if ( textures[ name ] === undefined ) {
+				function checkAlphaMorph( elements, materials ) {
 
-				console.warn( 'THREE.MMDLoader: Undefined texture', name );
+					for ( var i = 0, il = elements.length; i < il; i ++ ) {
 
-			}
+						var element = elements[ i ];
 
-			return textures[ name ];
+						if ( element.index === - 1 ) continue;
 
-		}
+						var material = materials[ element.index ];
 
-		for ( var i = 0; i < model.metadata.materialCount; i ++ ) {
+						if ( material.opacity !== element.diffuse[ 3 ] ) {
 
-			var m = model.materials[ i ];
-			var params = {};
+							material.transparent = true;
 
-			params.faceOffset = offset;
-			params.faceNum = m.faceCount;
+						}
 
-			offset += m.faceCount;
+					}
 
-			params.name = m.name;
+				}
 
-			/*
-			 * Color
-			 *
-			 * MMD         MeshToonMaterial
-			 * diffuse  -  color
-			 * specular -  specular
-			 * ambient  -  emissive * a
-			 *               (a = 1.0 without map texture or 0.2 with map texture)
-			 *
-			 * 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 ] );
-			params.opacity = m.diffuse[ 3 ];
-			params.specular = new THREE.Color( m.specular[ 0 ], m.specular[ 1 ], m.specular[ 2 ] );
-			params.shininess = m.shininess;
+				for ( var i = 0, il = data.morphs.length; i < il; i ++ ) {
 
-			if ( params.opacity === 1.0 ) {
+					var morph = data.morphs[ i ];
+					var elements = morph.elements;
 
-				params.side = THREE.FrontSide;
-				params.transparent = false;
+					if ( morph.type === 0 ) {
 
-			} else {
+						for ( var j = 0, jl = elements.length; j < jl; j ++ ) {
 
-				params.side = THREE.DoubleSide;
-				params.transparent = true;
+							var morph2 = data.morphs[ elements[ j ].index ];
 
-			}
+							if ( morph2.type !== 8 ) continue;
 
-			if ( model.metadata.format === 'pmd' ) {
+							checkAlphaMorph( morph2.elements, materials );
 
-				if ( m.fileName ) {
+						}
 
-					var fileName = m.fileName;
-					var fileNames = [];
+					} else if ( morph.type === 8 ) {
 
-					var index = fileName.lastIndexOf( '*' );
+						checkAlphaMorph( elements, materials );
 
-					if ( index >= 0 ) {
+					}
 
-						fileNames.push( fileName.slice( 0, index ) );
-						fileNames.push( fileName.slice( index + 1 ) );
+				}
 
-					} else {
+			}
 
-						fileNames.push( fileName );
+			return materials;
 
-					}
+		},
 
-					for ( var j = 0; j < fileNames.length; j ++ ) {
+		// private methods
 
-						var n = fileNames[ j ];
+		_getTGALoader: function () {
 
-						if ( n.indexOf( '.sph' ) >= 0 || n.indexOf( '.spa' ) >= 0 ) {
+			if ( this.tgaLoader === null ) {
 
-							params.envMap = loadTexture( n, { sphericalReflectionMapping: true } );
+				if ( THREE.TGALoader === undefined ) {
 
-							if ( n.indexOf( '.sph' ) >= 0 ) {
+					throw new Error( 'THREE.MMDLoader: Import THREE.TGALoader' );
 
-								params.envMapType = THREE.MultiplyOperation;
+				}
 
-							} else {
+				this.tgaLoader = new THREE.TGALoader( this.manager );
 
-								params.envMapType = THREE.AddOperation;
+			}
 
-							}
+			return this.tgaLoader;
 
-						} else {
+		},
 
-							params.map = loadTexture( n );
+		_isDefaultToonTexture: function ( name ) {
 
-						}
+			if ( name.length !== 10 ) return false;
 
-					}
+			return /toon(10|0[0-9])\.bmp/.test( name );
 
-				}
+		},
 
-			} else {
+		_loadTexture: function ( filePath, textures, params, onProgress, onError ) {
 
-				if ( m.textureIndex !== - 1 ) {
+			params = params || {};
 
-					var n = model.textures[ m.textureIndex ];
-					params.map = loadTexture( n );
+			var scope = this;
 
-				}
+			var fullPath;
 
-				// TODO: support m.envFlag === 3
-				if ( m.envTextureIndex !== - 1 && ( m.envFlag === 1 || m.envFlag == 2 ) ) {
+			if ( params.isDefaultToonTexture === true ) {
 
-					var n = model.textures[ m.envTextureIndex ];
-					params.envMap = loadTexture( n, { sphericalReflectionMapping: true } );
+				var index;
 
-					if ( m.envFlag === 1 ) {
+				try {
 
-						params.envMapType = THREE.MultiplyOperation;
+					index = parseInt( filePath.match( 'toon([0-9]{2})\.bmp$' )[ 1 ] );
 
-					} else {
+				} catch ( e ) {
 
-						params.envMapType = THREE.AddOperation;
+					console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like a '
+						+ 'not right default texture path. Using toon00.bmp instead.' );
 
-					}
+					index = 0;
 
 				}
 
-			}
+				fullPath = DEFAULT_TOON_TEXTURES[ index ];
 
-			var coef = ( params.map === undefined ) ? 1.0 : 0.2;
-			params.emissive = new THREE.Color( m.ambient[ 0 ] * coef, m.ambient[ 1 ] * coef, m.ambient[ 2 ] * coef );
+			} else {
 
-			materialParams.push( params );
+				fullPath = this.texturePath + filePath;
 
-		}
+			}
 
-		for ( var i = 0; i < materialParams.length; i ++ ) {
+			if ( textures[ fullPath ] !== undefined ) return textures[ fullPath ];
 
-			var p = materialParams[ i ];
-			var p2 = model.materials[ i ];
-			var m = new THREE.MeshToonMaterial();
+			var loader = THREE.Loader.Handlers.get( fullPath );
 
-			geometry.addGroup( p.faceOffset * 3, p.faceNum * 3, i );
+			if ( loader === null ) {
 
-			if ( p.name !== undefined ) m.name = p.name;
+				loader = ( filePath.slice( - 4 ).toLowerCase() === '.tga' )
+					? this._getTGALoader()
+					: this.textureLoader;
 
-			m.skinning = geometry.bones.length > 0 ? true : false;
-			m.morphTargets = geometry.morphTargets.length > 0 ? true : false;
-			m.lights = true;
-			m.side = ( model.metadata.format === 'pmx' && ( p2.flag & 0x1 ) === 1 ) ? THREE.DoubleSide : p.side;
-			m.transparent = p.transparent;
-			m.fog = true;
+			}
 
-			m.blending = THREE.CustomBlending;
-			m.blendSrc = THREE.SrcAlphaFactor;
-			m.blendDst = THREE.OneMinusSrcAlphaFactor;
-			m.blendSrcAlpha = THREE.SrcAlphaFactor;
-			m.blendDstAlpha = THREE.DstAlphaFactor;
+			var texture = loader.load( fullPath, function ( t ) {
 
-			if ( p.map !== undefined ) {
+				// 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 ) {
 
-				m.faceOffset = p.faceOffset;
-				m.faceNum = p.faceNum;
+					t.image = scope._getRotatedImage( t.image );
 
-				// Check if this part of the texture image the material uses requires transparency
-				function checkTextureTransparency( m ) {
+				}
 
-					m.map.readyCallbacks.push( function ( t ) {
+				t.flipY = false;
+				t.wrapS = THREE.RepeatWrapping;
+				t.wrapT = THREE.RepeatWrapping;
 
-						// Is there any efficient ways?
-						function createImageData( image ) {
+				for ( var i = 0; i < texture.readyCallbacks.length; i ++ ) {
 
-							var c = document.createElement( 'canvas' );
-							c.width = image.width;
-							c.height = image.height;
+					texture.readyCallbacks[ i ]( texture );
 
-							var ctx = c.getContext( '2d' );
-							ctx.drawImage( image, 0, 0 );
+				}
 
-							return ctx.getImageData( 0, 0, c.width, c.height );
+				delete texture.readyCallbacks;
 
-						}
+			}, onProgress, onError );
 
-						function detectTextureTransparency( image, uvs, indices ) {
+			if ( params.sphericalReflectionMapping === true ) {
 
-							var width = image.width;
-							var height = image.height;
-							var data = image.data;
-							var threshold = 253;
+				texture.mapping = THREE.SphericalReflectionMapping;
 
-							if ( data.length / ( width * height ) !== 4 ) {
-
-								return false;
-
-							}
-
-							for ( var i = 0; i < indices.length; i += 3 ) {
-
-								var centerUV = { x: 0.0, y: 0.0 };
-
-								for ( var j = 0; j < 3; j ++ ) {
-
-									var index = indices[ i * 3 + j ];
-									var uv = { x: uvs[ index * 2 + 0 ], y: uvs[ index * 2 + 1 ] };
-
-									if ( getAlphaByUv( image, uv ) < threshold ) {
-
-										return true;
-
-									}
-
-									centerUV.x += uv.x;
-									centerUV.y += uv.y;
-
-								}
-
-								centerUV.x /= 3;
-								centerUV.y /= 3;
-
-								if ( getAlphaByUv( image, centerUV ) < threshold ) {
-
-									return true;
-
-								}
-
-							}
-
-							return false;
+			}
 
-						}
+			texture.readyCallbacks = [];
 
-						/*
-						 * This method expects
-						 *   t.flipY = false
-						 *   t.wrapS = THREE.RepeatWrapping
-						 *   t.wrapT = THREE.RepeatWrapping
-						 * TODO: more precise
-						 */
-						function getAlphaByUv( image, uv ) {
+			textures[ fullPath ] = texture;
 
-							var width = image.width;
-							var height = image.height;
+			return texture;
 
-							var x = Math.round( uv.x * width ) % width;
-							var y = Math.round( uv.y * height ) % height;
+		},
 
-							if ( x < 0 ) {
+		_getRotatedImage: function ( image ) {
 
-								x += width;
+			var canvas = document.createElement( 'canvas' );
+			var context = canvas.getContext( '2d' );
 
-							}
+			var width = image.width;
+			var height = image.height;
 
-							if ( y < 0 ) {
+			canvas.width = width;
+			canvas.height = height;
 
-								y += 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 );
 
-							}
+			return context.getImageData( 0, 0, width, height );
 
-							var index = y * width + x;
+		},
 
-							return image.data[ index * 4 + 3 ];
+		// Check if the partial image area used by the texture is transparent.
+		_checkImageTransparency: function ( map, geometry, groupIndex ) {
 
-						}
+			map.readyCallbacks.push( function ( texture ) {
 
-						var imageData = t.image.data !== undefined ? t.image : createImageData( t.image );
-						var indices = geometry.index.array.slice( m.faceOffset * 3, m.faceOffset * 3 + m.faceNum * 3 );
+				// Is there any efficient ways?
+				function createImageData( image ) {
 
-						if ( detectTextureTransparency( imageData, geometry.attributes.uv.array, indices ) ) m.transparent = true;
+					var canvas = document.createElement( 'canvas' );
+					canvas.width = image.width;
+					canvas.height = image.height;
 
-						delete m.faceOffset;
-						delete m.faceNum;
+					var context = canvas.getContext( '2d' );
+					context.drawImage( image, 0, 0 );
 
-					} );
+					return context.getImageData( 0, 0, canvas.width, canvas.height );
 
 				}
 
-				m.map = getTexture( p.map, textures );
-				checkTextureTransparency( m );
+				function detectImageTransparency( image, uvs, indices ) {
 
-			}
-
-			if ( p.envMap !== undefined ) {
-
-				m.envMap = getTexture( p.envMap, textures );
-				m.combine = p.envMapType;
+					var width = image.width;
+					var height = image.height;
+					var data = image.data;
+					var threshold = 253;
 
-			}
+					if ( data.length / ( width * height ) !== 4 ) return false;
 
-			m.opacity = p.opacity;
-			m.color = p.color;
+					for ( var i = 0; i < indices.length; i += 3 ) {
 
-			if ( p.emissive !== undefined ) {
+						var centerUV = { x: 0.0, y: 0.0 };
 
-				m.emissive = p.emissive;
+						for ( var j = 0; j < 3; j ++ ) {
 
-			}
+							var index = indices[ i * 3 + j ];
+							var uv = { x: uvs[ index * 2 + 0 ], y: uvs[ index * 2 + 1 ] };
 
-			m.specular = p.specular;
-			m.shininess = Math.max( p.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )
+							if ( getAlphaByUv( image, uv ) < threshold ) return true;
 
-			if ( model.metadata.format === 'pmd' ) {
+							centerUV.x += uv.x;
+							centerUV.y += uv.y;
 
-				function isDefaultToonTexture( n ) {
+						}
 
-					if ( n.length !== 10 ) {
+						centerUV.x /= 3;
+						centerUV.y /= 3;
 
-						return false;
+						if ( getAlphaByUv( image, centerUV ) < threshold ) return true;
 
 					}
 
-					return n.match( /toon(10|0[0-9]).bmp/ ) === null ? false : true;
-
-				}
-
-				// parameters for OutlineEffect
-				m.outlineParameters = {
-					thickness: p2.edgeFlag === 1 ? 0.003 : 0.0,
-					color: new THREE.Color( 0.0, 0.0, 0.0 ),
-					alpha: 1.0
-				};
-
-				if ( m.outlineParameters.thickness === 0.0 ) m.outlineParameters.visible = false;
-
-				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 {
-
-				// parameters for OutlineEffect
-				m.outlineParameters = {
-					thickness: p2.edgeSize / 300,
-					color: new THREE.Color( p2.edgeColor[ 0 ], p2.edgeColor[ 1 ], p2.edgeColor[ 2 ] ),
-					alpha: p2.edgeColor[ 3 ]
-				};
-
-				if ( ( p2.flag & 0x10 ) === 0 || m.outlineParameters.thickness === 0.0 ) m.outlineParameters.visible = false;
-
-				var toonFileName, isDefaultToon;
-
-				if ( p2.toonIndex === - 1 || p2.toonFlag !== 0 ) {
-
-					var num = p2.toonIndex + 1;
-					toonFileName = 'toon' + ( num < 10 ? '0' + num : num ) + '.bmp';
-					isDefaultToon = true;
-
-				} else {
-
-					toonFileName = model.textures[ p2.toonIndex ];
-					isDefaultToon = false;
-
-				}
-
-				var uuid = loadTexture( toonFileName, { isToonTexture: true, defaultTexturePath: isDefaultToon } );
-				m.gradientMap = getTexture( uuid, textures );
-
-			}
-
-			materials.push( m );
-
-		}
-
-		if ( model.metadata.format === 'pmx' ) {
-
-			function checkAlphaMorph( morph, elements ) {
-
-				if ( morph.type !== 8 ) {
-
-					return;
+					return false;
 
 				}
 
-				for ( var i = 0; i < elements.length; i ++ ) {
-
-					var e = elements[ i ];
-
-					if ( e.index === - 1 ) {
-
-						continue;
+				/*
+				 * This method expects
+				 *   texture.flipY = false
+				 *   texture.wrapS = THREE.RepeatWrapping
+				 *   texture.wrapT = THREE.RepeatWrapping
+				 * TODO: more precise
+				 */
+				function getAlphaByUv( image, uv ) {
 
-					}
+					var width = image.width;
+					var height = image.height;
 
-					var m = materials[ e.index ];
+					var x = Math.round( uv.x * width ) % width;
+					var y = Math.round( uv.y * height ) % height;
 
-					if ( m.opacity !== e.diffuse[ 3 ] ) {
+					if ( x < 0 ) x += width;
+					if ( y < 0 ) y += height;
 
-						m.transparent = true;
+					var index = y * width + x;
 
-					}
+					return image.data[ index * 4 + 3 ];
 
 				}
 
-			}
-
-			for ( var i = 0; i < model.morphs.length; i ++ ) {
-
-				var morph = model.morphs[ i ];
-				var elements = morph.elements;
-
-				if ( morph.type === 0 ) {
+				var imageData = texture.image.data !== undefined
+					? texture.image
+					: createImageData( texture.image );
 
-					for ( var j = 0; j < elements.length; j ++ ) {
-
-						var morph2 = model.morphs[ elements[ j ].index ];
-						var elements2 = morph2.elements;
-
-						checkAlphaMorph( morph2, elements2 );
-
-					}
+				var group = geometry.groups[ groupIndex ];
 
-				} else {
+				if ( detectImageTransparency(
+					imageData,
+					geometry.attributes.uv.array,
+					geometry.index.array.slice( group.start, group.start + group.count ) ) ) {
 
-					checkAlphaMorph( morph, elements );
+					map.transparent = true;
 
 				}
 
-			}
+			} );
 
 		}
 
 	};
 
-	var initPhysics = function () {
-
-		var rigidBodies = [];
-		var constraints = [];
+	//
 
-		for ( var i = 0; i < model.metadata.rigidBodyCount; i ++ ) {
+	function AnimationBuilder() {
 
-			var b = model.rigidBodies[ i ];
-			var keys = Object.keys( b );
-
-			var p = {};
+	}
 
-			for ( var j = 0; j < keys.length; j ++ ) {
+	AnimationBuilder.prototype = {
 
-				var key = keys[ j ];
-				p[ key ] = b[ key ];
+		constructor: AnimationBuilder,
 
-			}
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {THREE.SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {THREE.AnimationClip}
+		 */
+		build: function ( vmd, mesh ) {
 
-			/*
-			 * RigidBody position parameter in PMX seems global position
-			 * while the one in PMD seems offset from corresponding bone.
-			 * So unify being offset.
-			 */
-			if ( model.metadata.format === 'pmx' ) {
+			// combine skeletal and morph animations
 
-				if ( p.boneIndex !== - 1 ) {
+			var tracks = this.buildSkeletalAnimation( vmd, mesh ).tracks;
+			var tracks2 = this.buildMorphAnimation( vmd, mesh ).tracks;
 
-					var bone = model.bones[ p.boneIndex ];
-					p.position[ 0 ] -= bone.position[ 0 ];
-					p.position[ 1 ] -= bone.position[ 1 ];
-					p.position[ 2 ] -= bone.position[ 2 ];
+			for ( var i = 0, il = tracks2.length; i < il; i ++ ) {
 
-				}
+				tracks.push( tracks2[ i ] );
 
 			}
 
-			rigidBodies.push( p );
-
-		}
-
-		for ( var i = 0; i < model.metadata.constraintCount; i ++ ) {
-
-			var c = model.constraints[ i ];
-			var keys = Object.keys( c );
+			return new THREE.AnimationClip( '', - 1, tracks );
 
-			var p = {};
+		},
 
-			for ( var j = 0; j < keys.length; j ++ ) {
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {THREE.SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {THREE.AnimationClip}
+		 */
+		buildSkeletalAnimation: function ( vmd, mesh ) {
 
-				var key = keys[ j ];
-				p[ key ] = c[ key ];
+			function pushInterpolation( array, interpolation, index ) {
 
-			}
+				array.push( interpolation[ index + 0 ] / 127 ); // x1
+				array.push( interpolation[ index + 8 ] / 127 ); // x2
+				array.push( interpolation[ index + 4 ] / 127 ); // y1
+				array.push( interpolation[ index + 12 ] / 127 ); // y2
 
-			var bodyA = rigidBodies[ p.rigidBodyIndex1 ];
-			var bodyB = rigidBodies[ p.rigidBodyIndex2 ];
+			};
 
-			/*
-			 * Refer to http://www20.atpages.jp/katwat/wp/?p=4135
-			 */
-			if ( bodyA.type !== 0 && bodyB.type === 2 ) {
+			var tracks = [];
 
-				if ( bodyA.boneIndex !== - 1 && bodyB.boneIndex !== - 1 &&
-				     model.bones[ bodyB.boneIndex ].parentIndex === bodyA.boneIndex ) {
+			var motions = {};
+			var bones = mesh.skeleton.bones;
+			var boneNameDictionary = {};
 
-					bodyB.type = 1;
+			for ( var i = 0, il = bones.length; i < il; i ++ ) {
 
-				}
+				boneNameDictionary[ bones[ i ].name ] = true;
 
 			}
 
-			constraints.push( p );
+			for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) {
 
-		}
+				var motion = vmd.motions[ i ];
+				var boneName = motion.boneName;
 
-		geometry.rigidBodies = rigidBodies;
-		geometry.constraints = constraints;
+				if ( boneNameDictionary[ boneName ] === undefined ) continue;
 
-	};
+				motions[ boneName ] = motions[ boneName ] || [];
+				motions[ boneName ].push( motion );
 
-	var initGeometry = function () {
+			}
 
-		geometry.setIndex( buffer.indices );
-		geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( buffer.vertices, 3 ) );
-		geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( buffer.normals, 3 ) );
-		geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( buffer.uvs, 2 ) );
-		geometry.addAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffer.skinIndices, 4 ) );
-		geometry.addAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffer.skinWeights, 4 ) );
+			for ( var key in motions ) {
 
-		geometry.computeBoundingSphere();
-		geometry.mmdFormat = model.metadata.format;
+				var array = motions[ key ];
 
-	};
+				array.sort( function ( a, b ) {
 
-	initVartices();
-	initFaces();
-	initBones();
-	initIKs();
-	initGrants();
-	initMorphs();
-	initMaterials();
-	initPhysics();
-	initGeometry();
+					return a.frameNum - b.frameNum;
 
-	var mesh = new THREE.SkinnedMesh( geometry, materials );
+				} );
 
-	// console.log( mesh ); // for console debug
+				var times = [];
+				var positions = [];
+				var rotations = [];
+				var pInterpolations = [];
+				var rInterpolations = [];
 
-	return mesh;
+				var basePosition = mesh.skeleton.getBoneByName( key ).position.toArray();
 
-};
+				for ( var i = 0, il = array.length; i < il; i ++ ) {
 
-THREE.MMDLoader.prototype.createAnimation = function ( mesh, vmd, name ) {
+					var time = array[ i ].frameNum / 30;
+					var position = array[ i ].position;
+					var rotation = array[ i ].rotation;
+					var interpolation = array[ i ].interpolation;
 
-	var helper = new THREE.MMDLoader.DataCreationHelper();
+					times.push( time );
 
-	var initMotionAnimations = function () {
+					for ( var j = 0; j < 3; j ++ ) positions.push( basePosition[ j ] + position[ j ] );
+					for ( var j = 0; j < 4; j ++ ) rotations.push( rotation[ j ] );
+					for ( var j = 0; j < 3; j ++ ) pushInterpolation( pInterpolations, interpolation, j );
 
-		if ( vmd.metadata.motionCount === 0 ) {
+					pushInterpolation( rInterpolations, interpolation, 3 );
 
-			return;
+				}
 
-		}
+				var targetName = '.bones[' + key + ']';
 
-		var bones = mesh.geometry.bones;
-		var orderedMotions = helper.createOrderedMotionArrays( bones, vmd.motions, 'boneName' );
+				tracks.push( this._createTrack( targetName + '.position', THREE.VectorKeyframeTrack, times, positions, pInterpolations ) );
+				tracks.push( this._createTrack( targetName + '.quaternion', THREE.QuaternionKeyframeTrack, times, rotations, rInterpolations ) );
 
-		var tracks = [];
+			}
 
-		var pushInterpolation = function ( array, interpolation, index ) {
+			return new THREE.AnimationClip( '', - 1, tracks );
 
-			array.push( interpolation[ index + 0 ] / 127 ); // x1
-			array.push( interpolation[ index + 8 ] / 127 ); // x2
-			array.push( interpolation[ index + 4 ] / 127 ); // y1
-			array.push( interpolation[ index + 12 ] / 127 ); // y2
+		},
 
-		};
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {THREE.SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {THREE.AnimationClip}
+		 */
+		buildMorphAnimation: function ( vmd, mesh ) {
 
-		var createTrack = function ( node, type, times, values, interpolations ) {
+			var tracks = [];
 
-			var track = new THREE[ type ]( node, times, values );
+			var morphs = {};
+			var morphTargetDictionary = mesh.morphTargetDictionary;
 
-			track.createInterpolant = function InterpolantFactoryMethodCubicBezier( result ) {
+			for ( var i = 0; i < vmd.metadata.morphCount; i ++ ) {
 
-				return new THREE.MMDLoader.CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, new Float32Array( interpolations ) );
+				var morph = vmd.morphs[ i ];
+				var morphName = morph.morphName;
 
-			};
+				if ( morphTargetDictionary[ morphName ] === undefined ) continue;
 
-			return track;
+				morphs[ morphName ] = morphs[ morphName ] || [];
+				morphs[ morphName ].push( morph );
 
-		};
+			}
 
-		for ( var i = 0; i < orderedMotions.length; i ++ ) {
+			for ( var key in morphs ) {
 
-			var times = [];
-			var positions = [];
-			var rotations = [];
-			var pInterpolations = [];
-			var rInterpolations = [];
+				var array = morphs[ key ];
 
-			var bone = bones[ i ];
-			var array = orderedMotions[ i ];
+				array.sort( function ( a, b ) {
 
-			for ( var j = 0; j < array.length; j ++ ) {
+					return a.frameNum - b.frameNum;
 
-				var time = array[ j ].frameNum / 30;
-				var pos = array[ j ].position;
-				var rot = array[ j ].rotation;
-				var interpolation = array[ j ].interpolation;
+				} );
 
-				times.push( time );
+				var times = [];
+				var values = [];
 
-				for ( var k = 0; k < 3; k ++ ) {
+				for ( var i = 0, il = array.length; i < il; i ++ ) {
 
-					positions.push( bone.pos[ k ] + pos[ k ] );
+					times.push( array[ i ].frameNum / 30 );
+					values.push( array[ i ].weight );
 
 				}
 
-				for ( var k = 0; k < 4; k ++ ) {
+				tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetDictionary[ key ] + ']', times, values ) );
 
-					rotations.push( rot[ k ] );
+			}
 
-				}
+			return new THREE.AnimationClip( '', - 1, tracks );
 
-				for ( var k = 0; k < 3; k ++ ) {
+		},
 
-					pushInterpolation( pInterpolations, interpolation, k );
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @return {THREE.AnimationClip}
+		 */
+		buildCameraAnimation: function ( vmd ) {
 
-				}
+			function pushVector3( array, vec ) {
 
-				pushInterpolation( rInterpolations, interpolation, 3 );
+				array.push( vec.x );
+				array.push( vec.y );
+				array.push( vec.z );
 
 			}
 
-			if ( times.length === 0 ) continue;
-
-			var boneName = '.bones[' + bone.name + ']';
-
-			tracks.push( createTrack( boneName + '.position', 'VectorKeyframeTrack', times, positions, pInterpolations ) );
-			tracks.push( createTrack( boneName + '.quaternion', 'QuaternionKeyframeTrack', times, rotations, rInterpolations ) );
-
-		}
+			function pushQuaternion( array, q ) {
 
-		var clip = new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name, - 1, tracks );
+				array.push( q.x );
+				array.push( q.y );
+				array.push( q.z );
+				array.push( q.w );
 
-		if ( mesh.geometry.animations === undefined ) mesh.geometry.animations = [];
-		mesh.geometry.animations.push( clip );
+			}
 
-	};
+			function pushInterpolation( array, interpolation, index ) {
 
-	var initMorphAnimations = function () {
+				array.push( interpolation[ index * 4 + 0 ] / 127 ); // x1
+				array.push( interpolation[ index * 4 + 1 ] / 127 ); // x2
+				array.push( interpolation[ index * 4 + 2 ] / 127 ); // y1
+				array.push( interpolation[ index * 4 + 3 ] / 127 ); // y2
 
-		if ( vmd.metadata.morphCount === 0 ) {
+			};
 
-			return;
+			var tracks = [];
 
-		}
+			var cameras = vmd.cameras === undefined ? [] : vmd.cameras.slice();
 
-		var orderedMorphs = helper.createOrderedMotionArrays( mesh.geometry.morphTargets, vmd.morphs, 'morphName' );
+			cameras.sort( function ( a, b ) {
 
-		var tracks = [];
+				return a.frameNum - b.frameNum;
 
-		for ( var i = 0; i < orderedMorphs.length; i ++ ) {
+			} );
 
 			var times = [];
-			var values = [];
-			var array = orderedMorphs[ i ];
-
-			for ( var j = 0; j < array.length; j ++ ) {
-
-				times.push( array[ j ].frameNum / 30 );
-				values.push( array[ j ].weight );
-
-			}
-
-			if ( times.length === 0 ) continue;
-
-			tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + i + ']', times, values ) );
-
-		}
-
-		var clip = new THREE.AnimationClip( name === undefined ? THREE.Math.generateUUID() : name + 'Morph', - 1, tracks );
-
-		if ( mesh.geometry.animations === undefined ) mesh.geometry.animations = [];
-		mesh.geometry.animations.push( clip );
-
-	};
-
-	initMotionAnimations();
-	initMorphAnimations();
-
-};
+			var centers = [];
+			var quaternions = [];
+			var positions = [];
+			var fovs = [];
 
-THREE.MMDLoader.DataCreationHelper = function () {
+			var cInterpolations = [];
+			var qInterpolations = [];
+			var pInterpolations = [];
+			var fInterpolations = [];
 
-};
+			var quaternion = new THREE.Quaternion();
+			var euler = new THREE.Euler();
+			var position = new THREE.Vector3();
+			var center = new THREE.Vector3();
 
-THREE.MMDLoader.DataCreationHelper.prototype = {
+			for ( var i = 0, il = cameras.length; i < il; i ++ ) {
 
-	constructor: THREE.MMDLoader.DataCreationHelper,
+				var motion = cameras[ i ];
 
-	/*
-	 * Note: Sometimes to use Japanese Unicode characters runs into problems in Three.js.
-	 *       In such a case, use this method to convert it to Unicode hex charcode strings,
-	 *       like 'あいう' -> '0x30420x30440x3046'
-	 */
+				var time = motion.frameNum / 30;
+				var pos = motion.position;
+				var rot = motion.rotation;
+				var distance = motion.distance;
+				var fov = motion.fov;
+				var interpolation = motion.interpolation;
 
-	toCharcodeStrings: function ( s ) {
+				times.push( time );
 
-		var str = '';
+				position.set( 0, 0, - distance );
+				center.set( pos[ 0 ], pos[ 1 ], pos[ 2 ] );
 
-		for ( var i = 0; i < s.length; i ++ ) {
+				euler.set( - rot[ 0 ], - rot[ 1 ], - rot[ 2 ] );
+				quaternion.setFromEuler( euler );
 
-			str += '0x' + ( '0000' + s[ i ].charCodeAt().toString( 16 ) ).substr( - 4 );
+				position.add( center );
+				position.applyQuaternion( quaternion );
 
-		}
+				pushVector3( centers, center );
+				pushQuaternion( quaternions, quaternion );
+				pushVector3( positions, position );
 
-		return str;
+				fovs.push( fov );
 
-	},
+				for ( var j = 0; j < 3; j ++ ) {
 
-	createDictionary: function ( array ) {
+					pushInterpolation( cInterpolations, interpolation, j );
 
-		var dict = {};
+				}
 
-		for ( var i = 0; i < array.length; i ++ ) {
+				pushInterpolation( qInterpolations, interpolation, 3 );
 
-			dict[ array[ i ].name ] = i;
+				// use the same parameter for x, y, z axis.
+				for ( var j = 0; j < 3; j ++ ) {
 
-		}
+					pushInterpolation( pInterpolations, interpolation, 4 );
 
-		return dict;
+				}
 
-	},
+				pushInterpolation( fInterpolations, interpolation, 5 );
 
-	initializeMotionArrays: function ( array ) {
+			}
 
-		var result = [];
+			var tracks = [];
 
-		for ( var i = 0; i < array.length; i ++ ) {
+			// I expect an object whose name 'target' exists under THREE.Camera
+			tracks.push( this._createTrack( 'target.position', THREE.VectorKeyframeTrack, times, centers, cInterpolations ) );
 
-			result[ i ] = [];
+			tracks.push( this._createTrack( '.quaternion', THREE.QuaternionKeyframeTrack, times, quaternions, qInterpolations ) );
+			tracks.push( this._createTrack( '.position', THREE.VectorKeyframeTrack, times, positions, pInterpolations ) );
+			tracks.push( this._createTrack( '.fov', THREE.NumberKeyframeTrack, times, fovs, fInterpolations ) );
 
-		}
+			return new THREE.AnimationClip( '', - 1, tracks );
 
-		return result;
+		},
 
-	},
+		// private method
 
-	sortMotionArray: function ( array ) {
+		_createTrack: function ( node, typedKeyframeTrack, times, values, interpolations ) {
 
-		array.sort( function ( a, b ) {
+			/*
+			 * optimizes here not to let KeyframeTrackPrototype optimize
+			 * because KeyframeTrackPrototype optimizes times and values but
+			 * doesn't optimize interpolations.
+			 */
+			if ( times.length > 2 ) {
 
-			return a.frameNum - b.frameNum;
+				times = times.slice();
+				values = values.slice();
+				interpolations = interpolations.slice();
 
-		} );
+				var stride = values.length / times.length;
+				var interpolateStride = interpolations.length / times.length;
 
-	},
+				var index = 1;
 
-	sortMotionArrays: function ( arrays ) {
+				for ( var aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex ++ ) {
 
-		for ( var i = 0; i < arrays.length; i ++ ) {
+					for ( var i = 0; i < stride; i ++ ) {
 
-			this.sortMotionArray( arrays[ i ] );
+						if ( values[ index * stride + i ] !== values[ ( index - 1 ) * stride + i ] ||
+							values[ index * stride + i ] !== values[ aheadIndex * stride + i ] ) {
 
-		}
+							index ++;
+							break;
 
-	},
+						}
 
-	createMotionArray: function ( array ) {
+					}
 
-		var result = [];
+					if ( aheadIndex > index ) {
 
-		for ( var i = 0; i < array.length; i ++ ) {
+						times[ index ] = times[ aheadIndex ];
 
-			result.push( array[ i ] );
+						for ( var i = 0; i < stride; i ++ ) {
 
-		}
+							values[ index * stride + i ] = values[ aheadIndex * stride + i ];
 
-		return result;
+						}
 
-	},
+						for ( var i = 0; i < interpolateStride; i ++ ) {
 
-	createMotionArrays: function ( array, result, dict, key ) {
+							interpolations[ index * interpolateStride + i ] = interpolations[ aheadIndex * interpolateStride + i ];
 
-		for ( var i = 0; i < array.length; i ++ ) {
+						}
 
-			var a = array[ i ];
-			var num = dict[ a[ key ] ];
+					}
 
-			if ( num === undefined ) {
+				}
 
-				continue;
+				times.length = index + 1;
+				values.length = ( index + 1 ) * stride;
+				interpolations.length = ( index + 1 ) * interpolateStride;
 
 			}
 
-			result[ num ].push( a );
-
-		}
-
-	},
-
-	createOrderedMotionArray: function ( array ) {
-
-		var result = this.createMotionArray( array );
-		this.sortMotionArray( result );
-		return result;
-
-	},
-
-	createOrderedMotionArrays: function ( targetArray, motionArray, key ) {
+			var track = new typedKeyframeTrack( node, times, values );
 
-		var dict = this.createDictionary( targetArray );
-		var result = this.initializeMotionArrays( targetArray );
-		this.createMotionArrays( motionArray, result, dict, key );
-		this.sortMotionArrays( result );
-
-		return result;
-
-	}
-
-};
-
-THREE.MMDLoader.CubicBezierInterpolation = function ( parameterPositions, sampleValues, sampleSize, resultBuffer, params ) {
-
-	THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
-
-	this.params = params;
-
-};
-
-THREE.MMDLoader.CubicBezierInterpolation.prototype = Object.create( THREE.LinearInterpolant.prototype );
-THREE.MMDLoader.CubicBezierInterpolation.prototype.constructor = THREE.MMDLoader.CubicBezierInterpolation;
-
-THREE.MMDLoader.CubicBezierInterpolation.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
-
-	var result = this.resultBuffer;
-	var values = this.sampleValues;
-	var stride = this.valueSize;
-
-	var offset1 = i1 * stride;
-	var offset0 = offset1 - stride;
-
-	// No interpolation if next key frame is in one frame in 30fps. This is from MMD animation spec.
-	var weight1 = ( ( t1 - t0 ) < 1 / 30 * 1.5 ) ? 0.0 : ( t - t0 ) / ( t1 - t0 );
-
-	if ( stride === 4 ) { // Quaternion
-
-		var x1 = this.params[ i1 * 4 + 0 ];
-		var x2 = this.params[ i1 * 4 + 1 ];
-		var y1 = this.params[ i1 * 4 + 2 ];
-		var y2 = this.params[ i1 * 4 + 3 ];
-
-		var ratio = this._calculate( x1, x2, y1, y2, weight1 );
-
-		THREE.Quaternion.slerpFlat( result, 0, values, offset0, values, offset1, ratio );
-
-	} else if ( stride === 3 ) { // Vector3
-
-		for ( var i = 0; i !== stride; ++ i ) {
+			track.createInterpolant = function InterpolantFactoryMethodCubicBezier( result ) {
 
-			var x1 = this.params[ i1 * 12 + i * 4 + 0 ];
-			var x2 = this.params[ i1 * 12 + i * 4 + 1 ];
-			var y1 = this.params[ i1 * 12 + i * 4 + 2 ];
-			var y2 = this.params[ i1 * 12 + i * 4 + 3 ];
+				return new CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, new Float32Array( interpolations ) );
 
-			var ratio = this._calculate( x1, x2, y1, y2, weight1 );
+			};
 
-			result[ i ] = values[ offset0 + i ] * ( 1 - ratio ) + values[ offset1 + i ] * ratio;
+			return track;
 
 		}
 
-	} else { // Number
-
-		var x1 = this.params[ i1 * 4 + 0 ];
-		var x2 = this.params[ i1 * 4 + 1 ];
-		var y1 = this.params[ i1 * 4 + 2 ];
-		var y2 = this.params[ i1 * 4 + 3 ];
-
-		var ratio = this._calculate( x1, x2, y1, y2, weight1 );
-
-		result[ 0 ] = values[ offset0 ] * ( 1 - ratio ) + values[ offset1 ] * ratio;
-
-	}
-
-	return result;
-
-};
-
-THREE.MMDLoader.CubicBezierInterpolation.prototype._calculate = function ( x1, x2, y1, y2, x ) {
-
-	/*
-	 * Cubic Bezier curves
-	 *   https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
-	 *
-	 * B(t) = ( 1 - t ) ^ 3 * P0
-	 *      + 3 * ( 1 - t ) ^ 2 * t * P1
-	 *      + 3 * ( 1 - t ) * t^2 * P2
-	 *      + t ^ 3 * P3
-	 *      ( 0 <= t <= 1 )
-	 *
-	 * MMD uses Cubic Bezier curves for bone and camera animation interpolation.
-	 *   http://d.hatena.ne.jp/edvakf/20111016/1318716097
-	 *
-	 *    x = ( 1 - t ) ^ 3 * x0
-	 *      + 3 * ( 1 - t ) ^ 2 * t * x1
-	 *      + 3 * ( 1 - t ) * t^2 * x2
-	 *      + t ^ 3 * x3
-	 *    y = ( 1 - t ) ^ 3 * y0
-	 *      + 3 * ( 1 - t ) ^ 2 * t * y1
-	 *      + 3 * ( 1 - t ) * t^2 * y2
-	 *      + t ^ 3 * y3
-	 *      ( x0 = 0, y0 = 0 )
-	 *      ( x3 = 1, y3 = 1 )
-	 *      ( 0 <= t, x1, x2, y1, y2 <= 1 )
-	 *
-	 * Here solves this equation with Bisection method,
-	 *   https://en.wikipedia.org/wiki/Bisection_method
-	 * gets t, and then calculate y.
-	 *
-	 * f(t) = 3 * ( 1 - t ) ^ 2 * t * x1
-	 *      + 3 * ( 1 - t ) * t^2 * x2
-	 *      + t ^ 3 - x = 0
-	 *
-	 * (Another option: Newton's method
-	 *    https://en.wikipedia.org/wiki/Newton%27s_method)
-	 */
-
-	var c = 0.5;
-	var t = c;
-	var s = 1.0 - t;
-	var loop = 15;
-	var eps = 1e-5;
-	var math = Math;
-
-	var sst3, stt3, ttt;
-
-	for ( var i = 0; i < loop; i ++ ) {
-
-		sst3 = 3.0 * s * s * t;
-		stt3 = 3.0 * s * t * t;
-		ttt = t * t * t;
+	};
 
-		var ft = ( sst3 * x1 ) + ( stt3 * x2 ) + ( ttt ) - x;
+	// interpolation
 
-		if ( math.abs( ft ) < eps ) break;
+	function CubicBezierInterpolation( parameterPositions, sampleValues, sampleSize, resultBuffer, params ) {
 
-		c /= 2.0;
+		THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
 
-		t += ( ft < 0 ) ? c : - c;
-		s = 1.0 - t;
+		this.interpolationParams = params;
 
 	}
 
-	return ( sst3 * y1 ) + ( stt3 * y2 ) + ttt;
-
-};
-
-THREE.MMDAudioManager = function ( audio, listener, p ) {
+	CubicBezierInterpolation.prototype = Object.assign( Object.create( THREE.Interpolant.prototype ), {
 
-	var params = ( p === null || p === undefined ) ? {} : p;
+		constructor: CubicBezierInterpolation,
 
-	this.audio = audio;
-	this.listener = listener;
+		interpolate_: function ( i1, t0, t, t1 ) {
 
-	this.elapsedTime = 0.0;
-	this.currentTime = 0.0;
-	this.delayTime = params.delayTime !== undefined ? params.delayTime : 0.0;
+			var result = this.resultBuffer;
+			var values = this.sampleValues;
+			var stride = this.valueSize;
+			var params = this.interpolationParams;
 
-	this.audioDuration = this.audio.buffer.duration;
-	this.duration = this.audioDuration + this.delayTime;
+			var offset1 = i1 * stride;
+			var offset0 = offset1 - stride;
 
-};
+			// No interpolation if next key frame is in one frame in 30fps.
+			// This is from MMD animation spec.
+			// '1.5' is for precision loss. times are Float32 in Three.js Animation system.
+			var weight1 = ( ( t1 - t0 ) < 1 / 30 * 1.5 ) ? 0.0 : ( t - t0 ) / ( t1 - t0 );
 
-THREE.MMDAudioManager.prototype = {
+			if ( stride === 4 ) { // Quaternion
 
-	constructor: THREE.MMDAudioManager,
+				var x1 = params[ i1 * 4 + 0 ];
+				var x2 = params[ i1 * 4 + 1 ];
+				var y1 = params[ i1 * 4 + 2 ];
+				var y2 = params[ i1 * 4 + 3 ];
 
-	control: function ( delta ) {
+				var ratio = this._calculate( x1, x2, y1, y2, weight1 );
 
-		this.elapsed += delta;
-		this.currentTime += delta;
+				THREE.Quaternion.slerpFlat( result, 0, values, offset0, values, offset1, ratio );
 
-		if ( this.checkIfStopAudio() ) {
+			} else if ( stride === 3 ) { // Vector3
 
-			this.audio.stop();
+				for ( var i = 0; i !== stride; ++ i ) {
 
-		}
-
-		if ( this.checkIfStartAudio() ) {
+					var x1 = params[ i1 * 12 + i * 4 + 0 ];
+					var x2 = params[ i1 * 12 + i * 4 + 1 ];
+					var y1 = params[ i1 * 12 + i * 4 + 2 ];
+					var y2 = params[ i1 * 12 + i * 4 + 3 ];
 
-			this.audio.play();
+					var ratio = this._calculate( x1, x2, y1, y2, weight1 );
 
-		}
+					result[ i ] = values[ offset0 + i ] * ( 1 - ratio ) + values[ offset1 + i ] * ratio;
 
-	},
+				}
 
-	checkIfStartAudio: function () {
+			} else { // Number
 
-		if ( this.audio.isPlaying ) {
+				var x1 = params[ i1 * 4 + 0 ];
+				var x2 = params[ i1 * 4 + 1 ];
+				var y1 = params[ i1 * 4 + 2 ];
+				var y2 = params[ i1 * 4 + 3 ];
 
-			return false;
+				var ratio = this._calculate( x1, x2, y1, y2, weight1 );
 
-		}
+				result[ 0 ] = values[ offset0 ] * ( 1 - ratio ) + values[ offset1 ] * ratio;
 
-		while ( this.currentTime >= this.duration ) {
+			}
 
-			this.currentTime -= this.duration;
+			return result;
 
-		}
+		},
 
-		if ( this.currentTime < this.delayTime ) {
+		_calculate: function ( x1, x2, y1, y2, x ) {
 
-			return false;
+			/*
+			 * Cubic Bezier curves
+			 *   https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
+			 *
+			 * B(t) = ( 1 - t ) ^ 3 * P0
+			 *      + 3 * ( 1 - t ) ^ 2 * t * P1
+			 *      + 3 * ( 1 - t ) * t^2 * P2
+			 *      + t ^ 3 * P3
+			 *      ( 0 <= t <= 1 )
+			 *
+			 * MMD uses Cubic Bezier curves for bone and camera animation interpolation.
+			 *   http://d.hatena.ne.jp/edvakf/20111016/1318716097
+			 *
+			 *    x = ( 1 - t ) ^ 3 * x0
+			 *      + 3 * ( 1 - t ) ^ 2 * t * x1
+			 *      + 3 * ( 1 - t ) * t^2 * x2
+			 *      + t ^ 3 * x3
+			 *    y = ( 1 - t ) ^ 3 * y0
+			 *      + 3 * ( 1 - t ) ^ 2 * t * y1
+			 *      + 3 * ( 1 - t ) * t^2 * y2
+			 *      + t ^ 3 * y3
+			 *      ( x0 = 0, y0 = 0 )
+			 *      ( x3 = 1, y3 = 1 )
+			 *      ( 0 <= t, x1, x2, y1, y2 <= 1 )
+			 *
+			 * Here solves this equation with Bisection method,
+			 *   https://en.wikipedia.org/wiki/Bisection_method
+			 * gets t, and then calculate y.
+			 *
+			 * f(t) = 3 * ( 1 - t ) ^ 2 * t * x1
+			 *      + 3 * ( 1 - t ) * t^2 * x2
+			 *      + t ^ 3 - x = 0
+			 *
+			 * (Another option: Newton's method
+			 *    https://en.wikipedia.org/wiki/Newton%27s_method)
+			 */
 
-		}
+			var c = 0.5;
+			var t = c;
+			var s = 1.0 - t;
+			var loop = 15;
+			var eps = 1e-5;
+			var math = Math;
 
-		this.audio.startTime = this.currentTime - this.delayTime;
+			var sst3, stt3, ttt;
 
-		return true;
+			for ( var i = 0; i < loop; i ++ ) {
 
-	},
+				sst3 = 3.0 * s * s * t;
+				stt3 = 3.0 * s * t * t;
+				ttt = t * t * t;
 
-	checkIfStopAudio: function () {
+				var ft = ( sst3 * x1 ) + ( stt3 * x2 ) + ( ttt ) - x;
 
-		if ( ! this.audio.isPlaying ) {
+				if ( math.abs( ft ) < eps ) break;
 
-			return false;
+				c /= 2.0;
 
-		}
+				t += ( ft < 0 ) ? c : - c;
+				s = 1.0 - t;
 
-		if ( this.currentTime >= this.duration ) {
+			}
 
-			return true;
+			return ( sst3 * y1 ) + ( stt3 * y2 ) + ttt;
 
 		}
 
-		return false;
-
-	}
-
-};
-
-THREE.MMDGrantSolver = function ( mesh ) {
-
-	this.mesh = mesh;
-
-};
+	} );
 
-THREE.MMDGrantSolver.prototype = {
-
-	constructor: THREE.MMDGrantSolver,
-
-	update: function () {
-
-		var q = new THREE.Quaternion();
-
-		return function () {
-
-			for ( var i = 0; i < this.mesh.geometry.grants.length; i ++ ) {
-
-				var g = this.mesh.geometry.grants[ i ];
-				var b = this.mesh.skeleton.bones[ g.index ];
-				var pb = this.mesh.skeleton.bones[ g.parentIndex ];
-
-				if ( g.isLocal ) {
-
-					// TODO: implement
-					if ( g.affectPosition ) {
-
-					}
-
-					// TODO: implement
-					if ( g.affectRotation ) {
-
-					}
-
-				} else {
-
-					// TODO: implement
-					if ( g.affectPosition ) {
-
-					}
-
-					if ( g.affectRotation ) {
-
-						q.set( 0, 0, 0, 1 );
-						q.slerp( pb.quaternion, g.ratio );
-						b.quaternion.multiply( q );
-
-					}
-
-				}
-
-			}
-
-		};
-
-	}()
-
-};
-
-THREE.MMDHelper = function () {
-
-	this.meshes = [];
-
-	this.doAnimation = true;
-	this.doIk = true;
-	this.doGrant = true;
-	this.doPhysics = true;
-	this.doCameraAnimation = true;
-
-	this.sharedPhysics = false;
-	this.masterPhysics = null;
-
-	this.audioManager = null;
-	this.camera = null;
-
-};
-
-THREE.MMDHelper.prototype = {
-
-	constructor: THREE.MMDHelper,
-
-	add: function ( mesh ) {
-
-		if ( ! ( mesh instanceof THREE.SkinnedMesh ) ) {
-
-			throw new Error( 'THREE.MMDHelper.add() accepts only THREE.SkinnedMesh instance.' );
-
-		}
-
-		if ( mesh.mixer === undefined ) mesh.mixer = null;
-		if ( mesh.ikSolver === undefined ) mesh.ikSolver = null;
-		if ( mesh.grantSolver === undefined ) mesh.grantSolver = null;
-		if ( mesh.physics === undefined ) mesh.physics = null;
-		if ( mesh.looped === undefined ) mesh.looped = false;
-
-		this.meshes.push( mesh );
-
-		// workaround until I make IK and Physics Animation plugin
-		this.initBackupBones( mesh );
-
-	},
-
-	setAudio: function ( audio, listener, params ) {
-
-		this.audioManager = new THREE.MMDAudioManager( audio, listener, params );
-
-	},
-
-	setCamera: function ( camera ) {
-
-		camera.mixer = null;
-		this.camera = camera;
-
-	},
-
-	setPhysicses: function ( params ) {
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.setPhysics( this.meshes[ i ], params );
-
-		}
-
-	},
-
-	setPhysics: function ( mesh, 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 && params.preventAnimationWarmup !== true ) {
-
-			this.animateOneMesh( 0, mesh );
-			physics.reset();
-
-		}
-
-		physics.warmup( warmup );
-
-		this.updateIKParametersDependingOnPhysicsEnabled( mesh, true );
-
-		mesh.physics = physics;
-
-	},
-
-	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 ) {
-
-			this.doPhysics = true;
-
-		} else {
-
-			this.doPhysics = false;
-
-		}
-
-		for ( var i = 0, il = this.meshes.length; i < il; i ++ ) {
-
-			this.updateIKParametersDependingOnPhysicsEnabled( this.meshes[ i ], enabled );
-
-		}
-
-	},
-
-	updateIKParametersDependingOnPhysicsEnabled: function ( mesh, physicsEnabled ) {
-
-		var iks = mesh.geometry.iks;
-		var bones = mesh.geometry.bones;
-
-		for ( var j = 0, jl = iks.length; j < jl; j ++ ) {
-
-			var ik = iks[ j ];
-			var links = ik.links;
-
-			for ( var k = 0, kl = links.length; k < kl; k ++ ) {
-
-				var link = links[ k ];
-
-				if ( physicsEnabled === true ) {
-
-					// disable IK of the bone the corresponding rigidBody type of which is 1 or 2
-					// because its rotation will be overriden by physics
-					link.enabled = bones[ link.index ].rigidBodyType > 0 ? false : true;
-
-				} else {
-
-					link.enabled = true;
-
-				}
-
-			}
-
-		}
-
-	},
-
-	setAnimations: function () {
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.setAnimation( this.meshes[ i ] );
-
-		}
-
-	},
-
-	setAnimation: function ( mesh ) {
-
-		if ( mesh.geometry.animations !== undefined ) {
-
-			mesh.mixer = new THREE.AnimationMixer( mesh );
-
-			// TODO: find a workaround not to access (seems like) private properties
-			//       the name of them begins with "_".
-			mesh.mixer.addEventListener( 'loop', function ( e ) {
-
-				if ( e.action._clip.tracks.length > 0 &&
-				     e.action._clip.tracks[ 0 ].name.indexOf( '.bones' ) !== 0 ) return;
-
-				var mesh = e.target._root;
-				mesh.looped = true;
-
-			} );
-
-			var foundAnimation = false;
-			var foundMorphAnimation = false;
-
-			for ( var i = 0; i < mesh.geometry.animations.length; i ++ ) {
-
-				var clip = mesh.geometry.animations[ i ];
-
-				var action = mesh.mixer.clipAction( clip );
-
-				if ( clip.tracks.length > 0 && clip.tracks[ 0 ].name.indexOf( '.morphTargetInfluences' ) === 0 ) {
-
-					if ( ! foundMorphAnimation ) {
-
-						action.play();
-						foundMorphAnimation = true;
-
-					}
-
-				} else {
-
-					if ( ! foundAnimation ) {
-
-						action.play();
-						foundAnimation = true;
-
-					}
-
-				}
-
-			}
-
-			if ( foundAnimation ) {
-
-				mesh.ikSolver = new THREE.CCDIKSolver( mesh );
-
-				if ( mesh.geometry.grants !== undefined ) {
-
-					mesh.grantSolver = new THREE.MMDGrantSolver( mesh );
-
-				}
-
-			}
-
-		}
-
-	},
-
-	setCameraAnimation: function ( camera ) {
-
-		if ( camera.animations !== undefined ) {
-
-			camera.mixer = new THREE.AnimationMixer( camera );
-			camera.mixer.clipAction( camera.animations[ 0 ] ).play();
-
-		}
-
-	},
-
-	/*
-	 * detect the longest duration among model, camera, and audio animations and then
-	 * set it to them to sync.
-	 * TODO: touching private properties ( ._actions and ._clip ) so consider better way
-	 *       to access them for safe and modularity.
-	 */
-	unifyAnimationDuration: function ( params ) {
-
-		params = params === undefined ? {} : params;
-
-		var max = 0.0;
-
-		var camera = this.camera;
-		var audioManager = this.audioManager;
-
-		// check the longest duration
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			var mesh = this.meshes[ i ];
-			var mixer = mesh.mixer;
-
-			if ( mixer === null ) {
-
-				continue;
-
-			}
-
-			for ( var j = 0; j < mixer._actions.length; j ++ ) {
-
-				var action = mixer._actions[ j ];
-				max = Math.max( max, action._clip.duration );
-
-			}
-
-		}
-
-		if ( camera !== null && camera.mixer !== null ) {
-
-			var mixer = camera.mixer;
-
-			for ( var i = 0; i < mixer._actions.length; i ++ ) {
-
-				var action = mixer._actions[ i ];
-				max = Math.max( max, action._clip.duration );
-
-			}
-
-		}
-
-		if ( audioManager !== null ) {
-
-			max = Math.max( max, audioManager.duration );
-
-		}
-
-		if ( params.afterglow !== undefined ) {
-
-			max += params.afterglow;
-
-		}
-
-		// set the duration
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			var mesh = this.meshes[ i ];
-			var mixer = mesh.mixer;
-
-			if ( mixer === null ) {
-
-				continue;
-
-			}
-
-			for ( var j = 0; j < mixer._actions.length; j ++ ) {
-
-				var action = mixer._actions[ j ];
-				action._clip.duration = max;
-
-			}
-
-		}
-
-		if ( camera !== null && camera.mixer !== null ) {
-
-			var mixer = camera.mixer;
-
-			for ( var i = 0; i < mixer._actions.length; i ++ ) {
-
-				var action = mixer._actions[ i ];
-				action._clip.duration = max;
-
-			}
-
-		}
-
-		if ( audioManager !== null ) {
-
-			audioManager.duration = max;
-
-		}
-
-	},
-
-	controlAudio: function ( delta ) {
-
-		if ( this.audioManager === null ) {
-
-			return;
-
-		}
-
-		this.audioManager.control( delta );
-
-	},
-
-	animate: function ( delta ) {
-
-		this.controlAudio( delta );
-
-		for ( var i = 0; i < this.meshes.length; i ++ ) {
-
-			this.animateOneMesh( delta, this.meshes[ i ] );
-
-		}
-
-		if ( this.sharedPhysics ) this.updateSharedPhysics( delta );
-
-		this.animateCamera( delta );
-
-	},
-
-	animateOneMesh: function ( delta, mesh ) {
-
-		var mixer = mesh.mixer;
-		var ikSolver = mesh.ikSolver;
-		var grantSolver = mesh.grantSolver;
-		var physics = mesh.physics;
-
-		if ( mixer !== null && this.doAnimation === true ) {
-
-			// restore/backupBones are workaround
-			// until I make IK, Grant, and Physics Animation plugin
-			this.restoreBones( mesh );
-
-			mixer.update( delta );
-
-			this.backupBones( mesh );
-
-		}
-
-		if ( ikSolver !== null && this.doIk === true ) {
-
-			ikSolver.update();
-
-		}
-
-		if ( grantSolver !== null && this.doGrant === true ) {
-
-			grantSolver.update();
-
-		}
-
-		if ( mesh.looped === true ) {
-
-			if ( physics !== null ) physics.reset();
-
-			mesh.looped = false;
-
-		}
-
-		if ( physics !== null && this.doPhysics && ! this.sharedPhysics ) {
-
-			physics.update( delta );
-
-		}
-
-	},
-
-	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 ) {
-
-			return;
-
-		}
-
-		var mixer = this.camera.mixer;
-
-		if ( mixer !== null && this.camera.center !== undefined && this.doCameraAnimation === true ) {
-
-			mixer.update( delta );
-
-			// TODO: Let PerspectiveCamera automatically update?
-			this.camera.updateProjectionMatrix();
-
-			this.camera.up.set( 0, 1, 0 );
-			this.camera.up.applyQuaternion( this.camera.quaternion );
-			this.camera.lookAt( this.camera.center );
-
-		}
-
-	},
-
-	poseAsVpd: function ( mesh, vpd, params ) {
-
-		if ( params === undefined ) params = {};
-
-		if ( params.preventResetPose !== true ) mesh.pose();
-
-		var bones = mesh.skeleton.bones;
-		var bones2 = vpd.bones;
-
-		var table = {};
-
-		for ( var i = 0; i < bones.length; i ++ ) {
-
-			table[ bones[ i ].name ] = i;
-
-		}
-
-		var thV = new THREE.Vector3();
-		var thQ = new THREE.Quaternion();
-
-		for ( var i = 0; i < bones2.length; i ++ ) {
-
-			var b = bones2[ i ];
-			var index = table[ b.name ];
-
-			if ( index === undefined ) continue;
-
-			var b2 = bones[ index ];
-			var t = b.translation;
-			var q = b.quaternion;
-
-			thV.set( t[ 0 ], t[ 1 ], t[ 2 ] );
-			thQ.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] );
-
-			b2.position.add( thV );
-			b2.quaternion.multiply( thQ );
-
-		}
-
-		mesh.updateMatrixWorld( true );
-
-		if ( params.preventIk !== true ) {
-
-			var solver = new THREE.CCDIKSolver( mesh );
-			solver.update( params.saveOriginalBonesBeforeIK );
-
-		}
-
-		if ( params.preventGrant !== true && mesh.geometry.grants !== undefined ) {
-
-			var solver = new THREE.MMDGrantSolver( mesh );
-			solver.update();
-
-		}
-
-	},
-
-	/*
-	 * Note: These following three functions are workaround for r74dev.
-	 *       THREE.PropertyMixer.apply() seems to save values into buffer cache
-	 *       when mixer.update() is called.
-	 *       ikSolver.update() and physics.update() change bone position/quaternion
-	 *       without mixer.update() then buffer cache will be inconsistent.
-	 *       So trying to avoid buffer cache inconsistency by doing
-	 *       backup bones position/quaternion right after mixer.update() call
-	 *       and then restore them after rendering.
-	 */
-	initBackupBones: function ( mesh ) {
-
-		mesh.skeleton.backupBones = [];
-
-		for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
-
-			mesh.skeleton.backupBones.push( mesh.skeleton.bones[ i ].clone() );
-
-		}
-
-	},
-
-	backupBones: function ( mesh ) {
-
-		mesh.skeleton.backupBoneIsSaved = true;
-
-		for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
-
-			var b = mesh.skeleton.backupBones[ i ];
-			var b2 = mesh.skeleton.bones[ i ];
-			b.position.copy( b2.position );
-			b.quaternion.copy( b2.quaternion );
-
-		}
-
-	},
-
-	restoreBones: function ( mesh ) {
-
-		if ( mesh.skeleton.backupBoneIsSaved !== true ) {
-
-			return;
-
-		}
-
-		mesh.skeleton.backupBoneIsSaved = false;
-
-		for ( var i = 0; i < mesh.skeleton.bones.length; i ++ ) {
-
-			var b = mesh.skeleton.bones[ i ];
-			var b2 = mesh.skeleton.backupBones[ i ];
-			b.position.copy( b2.position );
-			b.quaternion.copy( b2.quaternion );
-
-		}
-
-	}
+	return MMDLoader;
 
-};
+} )();

+ 1 - 1
examples/js/loaders/sea3d/SEA3DLoader.js

@@ -1211,7 +1211,7 @@ THREE.SEA3D.Dummy = function ( width, height, depth ) {
 	this.height = height != undefined ? height : 100;
 	this.depth = depth != undefined ? depth : 100;
 
-	var geo = new THREE.BoxGeometry( this.width, this.height, this.depth, 1, 1, 1 );
+	var geo = new THREE.BoxBufferGeometry( this.width, this.height, this.depth, 1, 1, 1 );
 
 	geo.computeBoundingBox();
 	geo.computeBoundingSphere();

+ 46 - 4
examples/js/vr/WebVR.js

@@ -9,7 +9,7 @@ var WEBVR = {
 
 	createButton: function ( renderer ) {
 
-		function showEnterVR( display ) {
+		function showEnterVR( device ) {
 
 			button.style.display = '';
 
@@ -24,11 +24,29 @@ var WEBVR = {
 
 			button.onclick = function () {
 
-				display.isPresenting ? display.exitPresent() : display.requestPresent( [ { source: renderer.domElement } ] );
+				if ( 'xr' in navigator ) {
+
+					device.requestSession( { exclusive: true } ).then( function ( session ) {
+
+						renderer.vr.setSession( session );
+
+						session.addEventListener( 'end', function ( event ) {
+
+							renderer.vr.setSession( null );
+
+						} );
+
+					} );
+
+				} else {
+
+					device.isPresenting ? device.exitPresent() : device.requestPresent( [ { source: renderer.domElement } ] );
+
+				}
 
 			};
 
-			renderer.vr.setDevice( display );
+			renderer.vr.setDevice( device );
 
 		}
 
@@ -68,7 +86,31 @@ var WEBVR = {
 
 		}
 
-		if ( 'getVRDisplays' in navigator ) {
+		var isWebXR = false;
+
+		if ( 'xr' in navigator ) {
+
+			isWebXR = true;
+
+			var button = document.createElement( 'button' );
+			button.style.display = 'none';
+
+			stylizeElement( button );
+
+			navigator.xr.requestDevice().then( function ( device ) {
+
+				device.supportsSession( { exclusive: true } ).then( function () {
+
+					showEnterVR( device );
+					button.textContent = 'ENTER XR'; // TODO
+
+				} ).catch( showVRNotFound );
+
+			} ).catch( showVRNotFound );
+
+			return button;
+
+		} else if ( 'getVRDisplays' in navigator ) {
 
 			var button = document.createElement( 'button' );
 			button.style.display = 'none';

+ 26 - 24
examples/misc_exporter_obj.html

@@ -81,15 +81,15 @@
 
 			function addGeometry( type ) {
 
-				for( var i = 0; i < scene.children.length; i++ ) {
+				for( var i = 0; i < scene.children.length; i ++ ) {
 
-					var current = scene.children[ i ];
+					var child = scene.children[ i ];
 
-					if( current instanceof THREE.Mesh ) {
+					if( child.isMesh ) {
 
-						current.geometry.dispose();
-						scene.remove( current );
-						i--;
+						child.geometry.dispose();
+						scene.remove( child );
+						i --;
 
 					}
 
@@ -98,14 +98,7 @@
 				if ( type === 1 ) {
 
 					var material = new THREE.MeshLambertMaterial( { color : 0x00cc00 } );
-
-					var geometry = new THREE.Geometry();
-					geometry.vertices.push( new THREE.Vector3( -50, -50, 0 ) );
-					geometry.vertices.push( new THREE.Vector3(  50, -50, 0 ) );
-					geometry.vertices.push( new THREE.Vector3(  50,  50, 0 ) );
-					var face = new THREE.Face3( 0, 1, 2 );
-					geometry.faces.push( face );
-					geometry.computeFaceNormals();
+					var geometry = generateTriangleGeometry();
 
 					scene.add( new THREE.Mesh( geometry, material ) );
 
@@ -125,14 +118,7 @@
 				} else if ( type === 4 || type === 5 ) {
 
 					var material = new THREE.MeshLambertMaterial( { color : 0x00cc00 } );
-
-					var geometry = new THREE.Geometry();
-					geometry.vertices.push( new THREE.Vector3( -50, -50, 0 ) );
-					geometry.vertices.push( new THREE.Vector3(  50, -50, 0 ) );
-					geometry.vertices.push( new THREE.Vector3(  50,  50, 0 ) );
-					var face = new THREE.Face3( 0, 1, 2 );
-					geometry.faces.push( face );
-					geometry.computeFaceNormals();
+					var geometry = generateTriangleGeometry()
 
 					var mesh = new THREE.Mesh( geometry, material );
 					mesh.position.x = -200;
@@ -244,8 +230,8 @@
 
 				requestAnimationFrame( animate );
 
-				camera.position.x += ( mouseX - camera.position.x ) * .05;
-				camera.position.y += ( -mouseY - camera.position.y ) * .05;
+				camera.position.x += ( mouseX - camera.position.x ) * 0.05;
+				camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
 				camera.lookAt( scene.position );
 
 				light.position.set( camera.position.x, camera.position.y, camera.position.z ).normalize();
@@ -253,6 +239,22 @@
 
 			}
 
+			function generateTriangleGeometry() {
+
+				var geometry = new THREE.BufferGeometry();
+				var vertices = [];
+
+				vertices.push( - 50, - 50, 0 );
+				vertices.push( 50, - 50, 0 );
+				vertices.push( 50, 50, 0 );
+
+				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+				geometry.computeVertexNormals();
+
+				return geometry;
+
+			}
+
 			init();
 			animate();
 

+ 52 - 6
examples/webaudio_sandbox.html

@@ -9,30 +9,71 @@
 				background:#777;
 				padding:0;
 				margin:0;
-				font-weight: bold;
 				overflow:hidden;
+				font-family: Monospace;
 			}
 
 			#info {
 				position: absolute;
+				z-index: 2;
 				top: 0px;
 				width: 100%;
 				color: #ffffff;
 				padding: 5px;
-				font-family:Monospace;
+
 				font-size:13px;
 				text-align:center;
+				font-weight: bold;
 			}
 
 			a {
 				color: #ffffff;
 			}
+
+			#overlay {
+				position: absolute;
+				z-index: 1;
+				top: 0;
+				left: 0;
+				width: 100%;
+				height:100%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				opacity: 1;
+				background-color: #000000;
+				color: #ffffff;
+			}
+
+			#overlay > div {
+				text-align: center;
+			}
+
+			#overlay > div > button {
+				height: 20px;
+				width: 100px;
+				background: transparent;
+				color: #ffffff;
+				outline: 1px solid #ffffff;
+				border: 0px;
+				cursor: pointer;
+			}
+
+			#overlay > div > p {
+				color: #777777;
+				font-size: 12px;
+			}
 		</style>
 	</head>
 	<body>
-
+		<div id="overlay">
+			<div>
+				<button id="startButton">Click to Play</button>
+				<p>Automatic audio playback requires a user interaction.</p>
+			</div>
+		</div>
 		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webaudio - sandbox<br/>
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> webaudio - sandbox |
 			music by <a href="http://www.newgrounds.com/audio/listen/358232" target="_blank" rel="noopener">larrylarrybb</a>,
 			<a href="http://www.newgrounds.com/audio/listen/376737" target="_blank" rel="noopener">skullbeatz</a> and
 			<a href="http://opengameart.org/content/project-utopia-seamless-loop" target="_blank" rel="noopener">congusbongus</a><br/><br/>
@@ -58,11 +99,14 @@
 
 			var clock = new THREE.Clock();
 
-			init();
-			animate();
+			var startButton = document.getElementById( 'startButton' );
+			startButton.addEventListener( 'click', init );
 
 			function init() {
 
+				var overlay = document.getElementById( 'overlay' );
+				overlay.remove();
+
 				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
 				camera.position.set( 0, 25, 0 );
 
@@ -235,6 +279,8 @@
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
+				animate();
+
 			}
 
 			function onWindowResize() {

+ 52 - 4
examples/webaudio_timing.html

@@ -9,24 +9,61 @@
 				background:#777;
 				padding:0;
 				margin:0;
-				font-weight: bold;
 				overflow:hidden;
+				font-family: Monospace;
 			}
 
 			#info {
 				position: absolute;
+				z-index: 2;
 				top: 0px;
 				width: 100%;
 				color: #ffffff;
 				padding: 5px;
-				font-family:Monospace;
+
 				font-size:13px;
 				text-align:center;
+				font-weight: bold;
 			}
 
 			a {
 				color: #ffffff;
 			}
+
+			#overlay {
+				position: absolute;
+				z-index: 1;
+				top: 0;
+				left: 0;
+				width: 100%;
+				height:100%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				opacity: 1;
+				background-color: #000000;
+				color: #ffffff;
+			}
+
+			#overlay > div {
+				text-align: center;
+			}
+
+			#overlay > div > button {
+				height: 20px;
+				width: 100px;
+				background: transparent;
+				color: #ffffff;
+				outline: 1px solid #ffffff;
+				border: 0px;
+				cursor: pointer;
+			}
+
+			#overlay > div > p {
+				color: #777777;
+				font-size: 12px;
+			}
+
 		</style>
 
 		<script src="../build/three.js"></script>
@@ -36,9 +73,16 @@
 	</head>
 <body>
 
+	<div id="overlay">
+		<div>
+			<button id="startButton">Click to Play</button>
+			<p>Automatic audio playback requires a user interaction.</p>
+		</div>
+	</div>
 	<div id="container"></div>
 	<div id="info">
-		<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> webaudio timing | sound effect by <a href="https://freesound.org/people/michorvath/sounds/269718/" target="_blank" rel="noopener noreferrer">michorvath</a>
+		<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> webaudio timing |
+		sound effect by <a href="https://freesound.org/people/michorvath/sounds/269718/" target="_blank" rel="noopener noreferrer">michorvath</a>
 	</div>
 
 	<script>
@@ -49,10 +93,14 @@
 
 	var objects = [];
 
-	init();
+	var startButton = document.getElementById( 'startButton' );
+	startButton.addEventListener( 'click', init );
 
 	function init() {
 
+		var overlay = document.getElementById( 'overlay' );
+		overlay.remove();
+
 		var container = document.getElementById( 'container' );
 
 		scene = new THREE.Scene();

+ 59 - 10
examples/webaudio_visualizer.html

@@ -9,24 +9,61 @@
 				background:#777;
 				padding:0;
 				margin:0;
-				font-weight: bold;
 				overflow:hidden;
+				font-family: Monospace;
 			}
 
 			#info {
 				position: absolute;
+				z-index: 2;
 				top: 0px;
 				width: 100%;
 				color: #ffffff;
 				padding: 5px;
-				font-family:Monospace;
+
 				font-size:13px;
 				text-align:center;
+				font-weight: bold;
 			}
 
 			a {
 				color: #ffffff;
 			}
+
+			#overlay {
+				position: absolute;
+				z-index: 1;
+				top: 0;
+				left: 0;
+				width: 100%;
+				height:100%;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				opacity: 1;
+				background-color: #000000;
+				color: #ffffff;
+			}
+
+			#overlay > div {
+				text-align: center;
+			}
+
+			#overlay > div > button {
+				height: 20px;
+				width: 100px;
+				background: transparent;
+				color: #ffffff;
+				outline: 1px solid #ffffff;
+				border: 0px;
+				cursor: pointer;
+			}
+
+			#overlay > div > p {
+				color: #777777;
+				font-size: 12px;
+			}
+
 		</style>
 
 		<script src="../build/three.js"></script>
@@ -68,12 +105,17 @@
 
 	</head>
 	<body>
-
-	<div id="container"></div>
-	<div id="info">
-		<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> webaudio - visualizer<br/>
-		music by <a href="http://www.newgrounds.com/audio/listen/376737" target="_blank" rel="noopener">skullbeatz</a>
-	</div>
+		<div id="overlay">
+			<div>
+				<button id="startButton">Click to Play</button>
+				<p>Automatic audio playback requires a user interaction.</p>
+			</div>
+		</div>
+		<div id="container"></div>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> webaudio - visualizer |
+			music by <a href="http://www.newgrounds.com/audio/listen/376737" target="_blank" rel="noopener">skullbeatz</a>
+		</div>
 
 	<script>
 
@@ -81,8 +123,8 @@
 
 	var scene, camera, renderer, analyser, uniforms;
 
-	init();
-	animate();
+	var startButton = document.getElementById( 'startButton' );
+	startButton.addEventListener( 'click', init );
 
 	function init() {
 
@@ -90,6 +132,11 @@
 
 		//
 
+		var overlay = document.getElementById( 'overlay' );
+		overlay.remove();
+
+		//
+
 		var container = document.getElementById( 'container' );
 
 		renderer = new THREE.WebGLRenderer( { antialias: true } );
@@ -141,6 +188,8 @@
 
 		window.addEventListener( 'resize', onResize, false );
 
+		animate();
+
 	}
 
 	function onResize() {

+ 9 - 3
examples/webgl_buffergeometry_instancing_dynamic.html

@@ -66,11 +66,17 @@
 
 		varying vec2 vUv;
 
+		// http://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
+
+		vec3 applyQuaternionToVector( vec4 q, vec3 v ){
+
+			return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
+
+		}
+
 		void main() {
 
-			vec3 vPosition = position;
-			vec3 vcV = cross( orientation.xyz, vPosition );
-			vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
+			vec3 vPosition = applyQuaternionToVector( orientation, position );
 
 			vUv = uv;
 

+ 9 - 3
examples/webgl_buffergeometry_instancing_interleaved_dynamic.html

@@ -66,11 +66,17 @@
 
 		varying vec2 vUv;
 
+		// http://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
+
+		vec3 applyQuaternionToVector( vec4 q, vec3 v ){
+
+			return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
+
+		}
+
 		void main() {
 
-			vec3 vPosition = position;
-			vec3 vcV = cross(orientation.xyz, vPosition);
-			vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
+			vec3 vPosition = applyQuaternionToVector( orientation, position );
 
 			vUv = uv;
 

+ 422 - 0
examples/webgl_buffergeometry_instancing_lambert.html

@@ -0,0 +1,422 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+	<title>three.js webgl - instancing - lambert shader</title>
+	<meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+	<style>
+		body {
+			color: #ffffff;
+			font-family: Monospace;
+			font-size: 13px;
+			text-align: center;
+			font-weight: bold;
+			background-color: #000000;
+			margin: 0px;
+			overflow: hidden;
+		}
+
+		#info {
+			position: absolute;
+			top: 0px;
+			width: 100%;
+			padding: 5px;
+		}
+
+		a {
+			color: #ffffff;
+		}
+
+		#notSupported {
+			width: 50%;
+			margin: auto;
+			border: 2px red solid;
+			margin-top: 20px;
+			padding: 10px;
+		}
+	</style>
+</head>
+<body>
+
+	<div id="container"></div>
+	<div id="info">
+		<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - instancing - lambert shader
+		<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
+	</div>
+
+	<script src="../build/three.js"></script>
+	<script src="js/Detector.js"></script>
+	<script src="js/libs/stats.min.js"></script>
+
+	<script src="js/controls/OrbitControls.js"></script>
+	<script src="js/CurveExtras.js"></script>
+
+
+	<script>
+
+		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+		THREE.CustomShaderLib = {
+
+			customDepthRGBA: { // this is a cut-and-paste of the depth shader -- modified to accommodate instancing for this app
+
+				uniforms: THREE.ShaderLib.depth.uniforms,
+
+				vertexShader:
+					`
+					// instanced
+					#ifdef INSTANCED
+
+						attribute vec3 instanceOffset;
+						attribute float instanceScale;
+
+					#endif
+
+					#include <common>
+					#include <uv_pars_vertex>
+					#include <displacementmap_pars_vertex>
+					#include <morphtarget_pars_vertex>
+					#include <skinning_pars_vertex>
+					#include <logdepthbuf_pars_vertex>
+					#include <clipping_planes_pars_vertex>
+
+					void main() {
+
+						#include <uv_vertex>
+
+						#include <skinbase_vertex>
+
+						#ifdef USE_DISPLACEMENTMAP
+
+							#include <beginnormal_vertex>
+							#include <morphnormal_vertex>
+							#include <skinnormal_vertex>
+
+						#endif
+
+						#include <begin_vertex>
+
+						// instanced
+						#ifdef INSTANCED
+
+							transformed *= instanceScale;
+							transformed = transformed + instanceOffset;
+
+						#endif
+
+						#include <morphtarget_vertex>
+						#include <skinning_vertex>
+						#include <displacementmap_vertex>
+						#include <project_vertex>
+						#include <logdepthbuf_vertex>
+						#include <clipping_planes_vertex>
+
+					}
+				`,
+
+				fragmentShader: THREE.ShaderChunk.depth_frag
+
+			},
+
+			lambert: { // this is a cut-and-paste of the lambert shader -- modified to accommodate instancing for this app
+
+				uniforms: THREE.ShaderLib.lambert.uniforms,
+
+				vertexShader:
+					`
+					#define LAMBERT
+
+					#ifdef INSTANCED
+						attribute vec3 instanceOffset;
+						attribute vec3 instanceColor;
+						attribute float instanceScale;
+					#endif
+
+					varying vec3 vLightFront;
+
+					#ifdef DOUBLE_SIDED
+
+						varying vec3 vLightBack;
+
+					#endif
+
+					#include <common>
+					#include <uv_pars_vertex>
+					#include <uv2_pars_vertex>
+					#include <envmap_pars_vertex>
+					#include <bsdfs>
+					#include <lights_pars_begin>
+					#include <lights_pars_maps>
+					#include <color_pars_vertex>
+					#include <fog_pars_vertex>
+					#include <morphtarget_pars_vertex>
+					#include <skinning_pars_vertex>
+					#include <shadowmap_pars_vertex>
+					#include <logdepthbuf_pars_vertex>
+					#include <clipping_planes_pars_vertex>
+
+					void main() {
+
+						#include <uv_vertex>
+						#include <uv2_vertex>
+						#include <color_vertex>
+
+						// vertex colors instanced
+						#ifdef INSTANCED
+							#ifdef USE_COLOR
+								vColor.xyz = instanceColor.xyz;
+							#endif
+						#endif
+
+						#include <beginnormal_vertex>
+						#include <morphnormal_vertex>
+						#include <skinbase_vertex>
+						#include <skinnormal_vertex>
+						#include <defaultnormal_vertex>
+
+						#include <begin_vertex>
+
+						// position instanced
+						#ifdef INSTANCED
+							transformed *= instanceScale;
+							transformed = transformed + instanceOffset;
+						#endif
+
+						#include <morphtarget_vertex>
+						#include <skinning_vertex>
+						#include <project_vertex>
+						#include <logdepthbuf_vertex>
+						#include <clipping_planes_vertex>
+
+						#include <worldpos_vertex>
+						#include <envmap_vertex>
+						#include <lights_lambert_vertex>
+						#include <shadowmap_vertex>
+						#include <fog_vertex>
+
+					}
+					`,
+
+				fragmentShader: THREE.ShaderLib.lambert.fragmentShader
+
+			}
+
+		};
+
+		//
+
+		var mesh, renderer, scene, camera, controls;
+		var stats;
+
+		init();
+		animate();
+
+		function init() {
+
+			renderer = new THREE.WebGLRenderer( { antialias: true } );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			renderer.shadowMap.enabled = true;
+			document.body.appendChild( renderer.domElement );
+
+			renderer.gammaOutput = true;
+
+			scene = new THREE.Scene();
+
+			scene.fog = new THREE.FogExp2( 0x000000, 0.004 );
+			renderer.setClearColor( scene.fog.color, 1 );
+
+			camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 1000 );
+			camera.position.set( 80, 40, 80 );
+
+			scene.add( camera );
+
+			controls = new THREE.OrbitControls( camera, renderer.domElement );
+			controls.enableZoom = false;
+			controls.maxPolarAngle = Math.PI / 2;
+
+			scene.add( new THREE.AmbientLight( 0xffffff, 0.7 ) );
+
+			var light = new THREE.DirectionalLight( 0xffffff, 0.4 );
+			light.position.set( 50, 40, 0 );
+
+			light.castShadow = true;
+			light.shadow.camera.left = - 40;
+			light.shadow.camera.right = 40;
+			light.shadow.camera.top = 40;
+			light.shadow.camera.bottom = - 40;
+			light.shadow.camera.near = 10;
+			light.shadow.camera.far = 180;
+
+			light.shadow.bias = - 0.001;
+			light.shadow.mapSize.width = 512;
+			light.shadow.mapSize.height = 512;
+
+			scene.add( light );
+
+			// light shadow camera helper
+			//light.shadowCameraHelper = new THREE.CameraHelper( light.shadow.camera );
+			//scene.add( light.shadowCameraHelper );
+
+
+			// instanced buffer geometry
+
+			var geometry = new THREE.InstancedBufferGeometry();
+			geometry.copy( new THREE.TorusBufferGeometry( 2, 0.5, 8, 128 ) );
+
+			const INSTANCES = 256;
+
+			var knot = new THREE.Curves.TorusKnot( 10 );
+			var positions = knot.getSpacedPoints( INSTANCES );
+
+			var offsets = new Float32Array( INSTANCES * 3 ); // xyz
+			var colors = new Float32Array( INSTANCES * 3 ); // rgb
+			var scales = new Float32Array( INSTANCES * 1 ); // s
+
+			var color = new THREE.Color();
+
+			for ( var i = 0, l = INSTANCES; i < l; i ++ ) {
+
+				var index = 3 * i;
+
+				// per-instance position offset
+				offsets[ index ] = positions[ i ].x;
+				offsets[ index + 1 ] = positions[ i ].y;
+				offsets[ index + 2 ] = positions[ i ].z;
+
+				// per-instance color tint - optional
+				colors[ index ] = 1;
+				colors[ index + 1 ] = 1;
+				colors[ index + 2 ] = 1;
+
+				// per-instance scale variation
+				scales[ i ] = 1 + 0.5 * Math.sin( 32 * Math.PI * i / INSTANCES );
+
+			}
+
+			geometry.addAttribute( 'instanceOffset', new THREE.InstancedBufferAttribute( offsets, 3 ) );
+			geometry.addAttribute( 'instanceColor', new THREE.InstancedBufferAttribute( colors, 3 ) );
+			geometry.addAttribute( 'instanceScale', new THREE.InstancedBufferAttribute( scales, 1 ) );
+
+
+			// material
+
+			var envMap = new THREE.TextureLoader().load( `textures/metal.jpg`, function ( texture ) {
+
+				texture.mapping = THREE.SphericalReflectionMapping;
+
+			} );
+
+			var shader = THREE.CustomShaderLib[ 'lambert' ];
+
+			var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+			uniforms[ "diffuse" ].value.set( 0xffb54a );
+			uniforms[ "envMap" ].value = envMap;
+			uniforms[ "reflectivity" ].value = 1;
+
+			// defines - Since we are reusing the ShaderChunks, we must specify the required defines.
+			// The renderer does not set these defines for ShaderMaterial
+
+			var defines = {
+				'INSTANCED': "",
+				'USE_ENVMAP': "",
+				'ENVMAP_TYPE_SPHERE': "",
+				'ENVMAP_MODE_REFLECTION': "",
+				'ENVMAP_BLENDING_MULTIPLY': "",
+			};
+
+			var material = new THREE.ShaderMaterial( {
+
+				// Material and ShaderMaterial properties can be set here -- except opacity
+				// the Lambert-properties must be set in the uniforms or defines
+
+				defines: defines,
+				uniforms: uniforms,
+				vertexShader: shader.vertexShader,
+				fragmentShader: shader.fragmentShader,
+
+				vertexColors: THREE.VertexColors,
+				lights: true,
+				fog: true
+
+			} );
+
+
+			// custom depth material - required for instanced shadows
+
+			var shader = THREE.CustomShaderLib[ 'customDepthRGBA' ];
+
+			var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+			var customDepthMaterial = new THREE.ShaderMaterial( {
+
+				defines: {
+					'INSTANCED': "",
+					'DEPTH_PACKING': THREE.RGBADepthPacking
+				},
+				uniforms: uniforms,
+				vertexShader: shader.vertexShader,
+				fragmentShader: shader.fragmentShader
+
+			} );
+
+			//
+
+			mesh = new THREE.Mesh( geometry, material );
+			mesh.scale.set( 1, 1, 2 );
+			mesh.castShadow = true;
+			mesh.receiveShadow = true;
+			mesh.customDepthMaterial = customDepthMaterial;
+			mesh.frustumCulled = false;
+
+			scene.add( mesh );
+
+			//
+
+			var ground = new THREE.Mesh(
+				new THREE.PlaneBufferGeometry( 800, 800 ).rotateX( - Math.PI / 2 ),
+				new THREE.MeshPhongMaterial( { color: 0x888888 } )
+			);
+			ground.position.set( 0, - 40, 0 );
+			ground.receiveShadow = true;
+
+			scene.add( ground );
+
+			//
+
+			stats = new Stats();
+			document.body.appendChild( stats.dom );
+
+			//
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+		}
+
+		function onWindowResize( event ) {
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+		}
+
+		function animate() {
+
+			requestAnimationFrame( animate );
+
+			mesh.rotation.y += 0.005;
+
+			stats.update();
+
+			renderer.render( scene, camera );
+
+		}
+
+
+	</script>
+
+</body>
+
+</html>

+ 9 - 15
examples/webgl_geometry_minecraft.html

@@ -45,6 +45,7 @@
 
 		<script src="js/controls/FirstPersonControls.js"></script>
 
+		<script src="js/BufferGeometryUtils.js"></script>
 		<script src="js/ImprovedNoise.js"></script>
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -124,13 +125,7 @@
 
 				//
 
-				// BufferGeometry cannot be merged yet.
-				var tmpGeometry = new THREE.Geometry();
-				var pxTmpGeometry = new THREE.Geometry().fromBufferGeometry( pxGeometry );
-				var nxTmpGeometry = new THREE.Geometry().fromBufferGeometry( nxGeometry );
-				var pyTmpGeometry = new THREE.Geometry().fromBufferGeometry( pyGeometry );
-				var pzTmpGeometry = new THREE.Geometry().fromBufferGeometry( pzGeometry );
-				var nzTmpGeometry = new THREE.Geometry().fromBufferGeometry( nzGeometry );
+				var geometries = [];
 
 				for ( var z = 0; z < worldDepth; z ++ ) {
 
@@ -149,29 +144,29 @@
 						var pz = getY( x, z + 1 );
 						var nz = getY( x, z - 1 );
 
-						tmpGeometry.merge( pyTmpGeometry, matrix );
+						geometries.push( pyGeometry.clone().applyMatrix( matrix ) );
 
 						if ( ( px !== h && px !== h + 1 ) || x === 0 ) {
 
-							tmpGeometry.merge( pxTmpGeometry, matrix );
+							geometries.push( pxGeometry.clone().applyMatrix( matrix ) );
 
 						}
 
 						if ( ( nx !== h && nx !== h + 1 ) || x === worldWidth - 1 ) {
 
-							tmpGeometry.merge( nxTmpGeometry, matrix );
+							geometries.push( nxGeometry.clone().applyMatrix( matrix ) );
 
 						}
 
 						if ( ( pz !== h && pz !== h + 1 ) || z === worldDepth - 1 ) {
 
-							tmpGeometry.merge( pzTmpGeometry, matrix );
+							geometries.push( pzGeometry.clone().applyMatrix( matrix ) );
 
 						}
 
 						if ( ( nz !== h && nz !== h + 1 ) || z === 0 ) {
 
-							tmpGeometry.merge( nzTmpGeometry, matrix );
+							geometries.push( nzGeometry.clone().applyMatrix( matrix ) );
 
 						}
 
@@ -179,12 +174,11 @@
 
 				}
 
-				var geometry = new THREE.BufferGeometry().fromGeometry( tmpGeometry );
+				var geometry = THREE.BufferGeometryUtils.mergeBufferGeometries( geometries );
 				geometry.computeBoundingSphere();
 
 				var texture = new THREE.TextureLoader().load( 'textures/minecraft/atlas.png' );
 				texture.magFilter = THREE.NearestFilter;
-				texture.minFilter = THREE.LinearMipMapLinearFilter;
 
 				var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: texture } ) );
 				scene.add( mesh );
@@ -196,7 +190,7 @@
 				directionalLight.position.set( 1, 1, 0.5 ).normalize();
 				scene.add( directionalLight );
 
-				renderer = new THREE.WebGLRenderer();
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 

+ 33 - 28
examples/webgl_interactive_cubes_gpu.html

@@ -38,7 +38,7 @@
 		<script src="../build/three.js"></script>
 
 		<script src="js/controls/TrackballControls.js"></script>
-
+		<script src="js/BufferGeometryUtils.js"></script>
 		<script src="js/libs/stats.min.js"></script>
 
 		<script>
@@ -46,7 +46,6 @@
 			var container, stats;
 			var camera, controls, scene, renderer;
 			var pickingData = [], pickingTexture, pickingScene;
-			var objects = [];
 			var highlightBox;
 
 			var mouse = new THREE.Vector2();
@@ -84,35 +83,35 @@
 				light.position.set( 0, 500, 2000 );
 				scene.add( light );
 
-				var geometry = new THREE.Geometry(),
-				pickingGeometry = new THREE.Geometry(),
-				pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } ),
-				defaultMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true, vertexColors: THREE.VertexColors, shininess: 0	} );
-
-				function applyVertexColors( g, c ) {
+				var pickingMaterial = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } );
+				var defaultMaterial = new THREE.MeshPhongMaterial( { color: 0xffffff, flatShading: true, vertexColors: THREE.VertexColors, shininess: 0	} );
 
-					g.faces.forEach( function( f ) {
+				function applyVertexColors( geometry, color ) {
 
-						var n = ( f instanceof THREE.Face3 ) ? 3 : 4;
+					var position = geometry.attributes.position;
+					var colors = [];
 
-						for( var j = 0; j < n; j ++ ) {
+					for ( var i = 0; i < position.count; i ++ ) {
 
-							f.vertexColors[ j ] = c;
+						colors.push( color.r, color.g, color.b );
 
-						}
+					}
 
-					} );
+					geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
 
 				}
 
-				var geom = new THREE.BoxGeometry( 1, 1, 1 );
-				var color = new THREE.Color();
+				var geometriesDrawn = [];
+				var geometriesPicking = [];
 
 				var matrix = new THREE.Matrix4();
 				var quaternion = new THREE.Quaternion();
+				var color = new THREE.Color();
 
 				for ( var i = 0; i < 5000; i ++ ) {
 
+					var geometry = new THREE.BoxBufferGeometry();
+
 					var position = new THREE.Vector3();
 					position.x = Math.random() * 10000 - 5000;
 					position.y = Math.random() * 6000 - 3000;
@@ -131,17 +130,21 @@
 					quaternion.setFromEuler( rotation, false );
 					matrix.compose( position, quaternion, scale );
 
-					// give the geom's vertices a random color, to be displayed
+					geometry.applyMatrix( matrix );
+
+					// give the geometry's vertices a random color, to be displayed
+
+					applyVertexColors( geometry, color.setHex( Math.random() * 0xffffff ) );
 
-					applyVertexColors( geom, color.setHex( Math.random() * 0xffffff ) );
+					geometriesDrawn.push( geometry );
 
-					geometry.merge( geom, matrix );
+					geometry = geometry.clone();
 
-					// give the geom's vertices a color corresponding to the "id"
+					// give the geometry's vertices a color corresponding to the "id"
 
-					applyVertexColors( geom, color.setHex( i ) );
+					applyVertexColors( geometry, color.setHex( i ) );
 
-					pickingGeometry.merge( geom, matrix );
+					geometriesPicking.push( geometry );
 
 					pickingData[ i ] = {
 
@@ -153,13 +156,13 @@
 
 				}
 
-				var drawnObject = new THREE.Mesh( geometry, defaultMaterial );
-				scene.add( drawnObject );
+				var objects = new THREE.Mesh( THREE.BufferGeometryUtils.mergeBufferGeometries( geometriesDrawn ), defaultMaterial );
+				scene.add( objects );
 
-				pickingScene.add( new THREE.Mesh( pickingGeometry, pickingMaterial ) );
+				pickingScene.add( new THREE.Mesh( THREE.BufferGeometryUtils.mergeBufferGeometries( geometriesPicking ), pickingMaterial ) );
 
 				highlightBox = new THREE.Mesh(
-					new THREE.BoxGeometry( 1, 1, 1 ),
+					new THREE.BoxBufferGeometry(),
 					new THREE.MeshLambertMaterial( { color: 0xffff00 }
 				) );
 				scene.add( highlightBox );
@@ -201,14 +204,16 @@
 				renderer.render( pickingScene, camera, pickingTexture );
 
 				//create buffer for reading single pixel
+
 				var pixelBuffer = new Uint8Array( 4 );
 
 				//read the pixel under the mouse from the texture
-				renderer.readRenderTargetPixels(pickingTexture, mouse.x, pickingTexture.height - mouse.y, 1, 1, pixelBuffer);
+
+				renderer.readRenderTargetPixels( pickingTexture, mouse.x, pickingTexture.height - mouse.y, 1, 1, pixelBuffer );
 
 				//interpret the pixel as an ID
 
-				var id = ( pixelBuffer[0] << 16 ) | ( pixelBuffer[1] << 8 ) | ( pixelBuffer[2] );
+				var id = ( pixelBuffer[ 0 ] << 16 ) | ( pixelBuffer[ 1 ] << 8 ) | ( pixelBuffer[ 2 ] );
 				var data = pickingData[ id ];
 
 				if ( data) {

+ 2 - 3
examples/webgl_interactive_instances_gpu.html

@@ -949,12 +949,11 @@
 			// highlight box
 
 			highlightBox = new THREE.Mesh(
-				new THREE.BoxGeometry( 1, 1, 1 ),
+				new THREE.BoxBufferGeometry( 1, 1, 1 ),
 				new THREE.MeshLambertMaterial( {
 					emissive: 0xffff00,
 					transparent: true,
-					opacity: 0.5,
-					side: THREE.FrontSide
+					opacity: 0.5
 				} )
 			);
 

+ 16 - 23
examples/webgl_loader_mmd.html

@@ -43,6 +43,7 @@
 		<script src="js/effects/OutlineEffect.js"></script>
 		<script src="js/animation/CCDIKSolver.js"></script>
 		<script src="js/animation/MMDPhysics.js"></script>
+		<script src="js/animation/MMDAnimationHelper.js"></script>
 
 		<script src="js/controls/OrbitControls.js"></script>
 
@@ -120,37 +121,31 @@
 				var modelFile = 'models/mmd/miku/miku_v2.pmd';
 				var vmdFiles = [ 'models/mmd/vmds/wavefile_v2.vmd' ];
 
-				helper = new THREE.MMDHelper();
+				helper = new THREE.MMDAnimationHelper( {
+					afterglow: 2.0
+				} );
 
 				var loader = new THREE.MMDLoader();
 
-				loader.load( modelFile, vmdFiles, function ( object ) {
+				loader.loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
 
-					mesh = object;
+					mesh = mmd.mesh;
 					mesh.position.y = -10;
 					scene.add( mesh );
 
-					helper.add( mesh );
-					helper.setAnimation( mesh );
+					helper.add( mesh, {
+						animation: mmd.animation,
+						physics: true
+					} );
 
-					/*
-					 * Note: create CCDIKHelper after calling helper.setAnimation()
-					 */
-					ikHelper = new THREE.CCDIKHelper( mesh );
+					ikHelper = helper.objects.get( mesh ).ikSolver.createHelper();
 					ikHelper.visible = false;
 					scene.add( ikHelper );
 
-					/*
-					 * Note: You're recommended to call helper.setPhysics()
-					 *       after calling helper.setAnimation().
-			 		 */
-					helper.setPhysics( mesh );
-					physicsHelper = new THREE.MMDPhysicsHelper( mesh );
+					physicsHelper = helper.objects.get( mesh ).physics.createHelper();
 					physicsHelper.visible = false;
 					scene.add( physicsHelper );
 
-					helper.unifyAnimationDuration( { afterglow: 2.0 } );
-
 					initGui();
 
 				}, onProgress, onError );
@@ -195,7 +190,7 @@
 					var gui = new dat.GUI();
 
 					gui.add( api, 'animation' ).onChange( function () {
-						helper.doAnimation = api[ 'animation' ];
+						helper.enable( 'animation', api[ 'animation' ] );
 					} );
 
 					gui.add( api, 'gradient mapping' ).onChange( function () {
@@ -216,7 +211,7 @@
 					} );
 
 					gui.add( api, 'ik' ).onChange( function () {
-						helper.doIk = api[ 'ik' ];
+						helper.enable( 'ik', api[ 'ik' ] );
 					} );
 
 					gui.add( api, 'outline' ).onChange( function () {
@@ -224,7 +219,7 @@
 					} );
 
 					gui.add( api, 'physics' ).onChange( function () {
-						helper.enablePhysics( api[ 'physics' ] );
+						helper.enable( 'physics', api[ 'physics' ] );
 					} );
 
 					gui.add( api, 'show IK bones' ).onChange( function () {
@@ -265,9 +260,7 @@
 
 			function render() {
 
-				helper.animate( clock.getDelta() );
-				if ( physicsHelper !== undefined && physicsHelper.visible ) physicsHelper.update();
-				if ( ikHelper !== undefined && ikHelper.visible ) ikHelper.update();
+				helper.update( clock.getDelta() );
 				effect.render( scene, camera );
 
 			}

+ 17 - 19
examples/webgl_loader_mmd_audio.html

@@ -45,6 +45,7 @@
 		<script src="js/effects/OutlineEffect.js"></script>
 		<script src="js/animation/CCDIKSolver.js"></script>
 		<script src="js/animation/MMDPhysics.js"></script>
+		<script src="js/animation/MMDAnimationHelper.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -116,36 +117,33 @@
 				var audioFile = 'models/mmd/audios/wavefile_short.mp3';
 				var audioParams = { delayTime: 160 * 1 / 30 };
 
-				helper = new THREE.MMDHelper();
+				helper = new THREE.MMDAnimationHelper();
 
 				var loader = new THREE.MMDLoader();
 
-				loader.load( modelFile, vmdFiles, function ( object ) {
+				loader.loadWithAnimation( modelFile, vmdFiles, function ( mmd ) {
 
-					mesh = object;
+					mesh = mmd.mesh;
 
-					helper.add( mesh );
-					helper.setAnimation( mesh );
-					helper.setPhysics( mesh );
+					helper.add( mesh, {
+						animation: mmd.animation,
+						physics: true
+					} );
 
-					loader.loadVmds( cameraFiles, function ( vmd ) {
+					loader.loadAnimation( cameraFiles, camera, function ( cameraAnimation ) {
 
-						helper.setCamera( camera );
+						helper.add( camera, {
+							animation: cameraAnimation
+						} );
 
-						loader.pourVmdIntoCamera( camera, vmd );
-						helper.setCameraAnimation( camera );
+						new THREE.AudioLoader().load( audioFile, function ( buffer ) {
 
-						loader.loadAudio( audioFile, function ( audio, listener ) {
+							var listener = new THREE.AudioListener();
+							var audio = new THREE.Audio( listener ).setBuffer( buffer );
 
 							listener.position.z = 1;
 
-							helper.setAudio( audio, listener, audioParams );
-
-							/*
-							 * Note: call this method after you set all animations
-							 *       including camera and audio.
-							 */
-							helper.unifyAnimationDuration();
+							helper.add( audio, audioParams );
 
 							scene.add( audio );
 							scene.add( listener );
@@ -199,7 +197,7 @@
 
 				if ( ready ) {
 
-					helper.animate( clock.getDelta() );
+					helper.update( clock.getDelta() );
 
 				}
 

+ 5 - 4
examples/webgl_loader_mmd_pose.html

@@ -43,6 +43,7 @@
 		<script src="js/effects/OutlineEffect.js"></script>
 		<script src="js/animation/CCDIKSolver.js"></script>
 		<script src="js/animation/MMDPhysics.js"></script>
+		<script src="js/animation/MMDAnimationHelper.js"></script>
 
 		<script src="js/Detector.js"></script>
 		<script src="js/libs/stats.min.js"></script>
@@ -118,11 +119,11 @@
 					'models/mmd/vpds/11.vpd'
 				];
 
-				helper = new THREE.MMDHelper();
+				helper = new THREE.MMDAnimationHelper();
 
 				var loader = new THREE.MMDLoader();
 
-				loader.loadModel( modelFile, function ( object ) {
+				loader.load( modelFile, function ( object ) {
 
 					mesh = object;
 					mesh.position.y = -10;
@@ -135,7 +136,7 @@
 
 						var vpdFile = vpdFiles[ vpdIndex ];
 
-						loader.loadVpd( vpdFile, function ( vpd ) {
+						loader.loadVPD( vpdFile, false, function ( vpd ) {
 
 							vpds.push( vpd );
 
@@ -254,7 +255,7 @@
 
 						} else {
 
-							helper.poseAsVpd( mesh, vpds[ index ] );
+							helper.pose( mesh, vpds[ index ] );
 
 						}
 

+ 1 - 1
examples/webgl_materials.html

@@ -113,7 +113,7 @@
 				pointLight = new THREE.PointLight( 0xffffff, 1 );
 				scene.add( pointLight );
 
-				pointLight.add( new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ) );
+				pointLight.add( new THREE.Mesh( new THREE.SphereBufferGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ) );
 
 				//
 

+ 2 - 2
examples/webgl_morphnormals.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js webgl - morph normals - horse</title>
+		<title>three.js webgl - morph normals</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
@@ -148,7 +148,7 @@
 
 				renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
 
-				camera.aspect = 0.5 * SCREEN_WIDTH / SCREEN_HEIGHT;
+				camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
 				camera.updateProjectionMatrix();
 
 			}

+ 46 - 36
examples/webgl_multiple_elements_text.html

@@ -132,8 +132,10 @@
 					var scene = new THREE.Scene();
 					scene.background = new THREE.Color( 0xffffff );
 
-					var geometry = new THREE.Geometry();
-					var geometry0 = new THREE.Geometry();
+					var geometry0 = new THREE.BufferGeometry();
+					var geometry1 = new THREE.BufferGeometry();
+
+					var vertices = [];
 
 					if ( views[n].lattice ) {
 
@@ -144,8 +146,7 @@
 
 								for ( var k = -range ; k <= range ; k++ ) {
 
-									geometry.vertices.push( new THREE.Vector3( i, j, k ) );
-									geometry0.vertices.push( new THREE.Vector3( i, j, k ) );
+									vertices.push( i, j, k );
 
 								}
 
@@ -161,13 +162,15 @@
 							var j = balls * Math.random() - balls / 2;
 							var k = balls * Math.random() - balls / 2;
 
-							geometry.vertices.push( new THREE.Vector3( i, j, k ) );
-							geometry0.vertices.push( new THREE.Vector3( i, j, k ) );
+							vertices.push( i, j, k );
 
 						}
 
 					}
 
+					geometry0.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+					geometry1.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices.slice(), 3 ) );
+
 					var index = Math.floor( colors.length * Math.random() );
 
 					var canvas2 = document.createElement( 'canvas' );
@@ -177,21 +180,20 @@
 					context.arc( 64, 64, 64, 0, 2 * Math.PI );
 					context.fillStyle = colors[ index ];
 					context.fill();
-					var texture = new THREE.Texture( canvas2 );
-					texture.needsUpdate = true;
+					var texture = new THREE.CanvasTexture( canvas2 );
 
-					var material = new THREE.PointsMaterial( { size: size, map: texture, transparent: true, alphaTest: .1 } );
+					var material = new THREE.PointsMaterial( { size: size, map: texture, transparent: true, alphaTest: 0.1 } );
 
-					scene.add( new THREE.Points( geometry, material ) );
+					scene.add( new THREE.Points( geometry0, material ) );
 
-					scene.userData.view = views[n];
-					scene.userData.geometry0 = geometry0;
+					scene.userData.view = views[ n ];
+					scene.userData.geometry1 = geometry1;
 
-					var camera = new THREE.PerspectiveCamera( 75, 1, .1, 100 );
-					camera.position.set( 0, 0, 1.2*balls );
+					var camera = new THREE.PerspectiveCamera( 75, 1, 0.1, 100 );
+					camera.position.set( 0, 0, 1.2 * balls );
 					scene.userData.camera = camera;
 
-					var controls = new THREE.OrbitControls( camera, views[n] );
+					var controls = new THREE.OrbitControls( camera, views[ n ] );
 					scene.userData.controls = controls;
 
 					scenes.push( scene );
@@ -253,15 +255,23 @@
 
 					renderer.render( scene, scene.userData.camera );
 
-					for ( var i = 0 ; i < scene.children[0].geometry.vertices.length ; i++ ) {
+					var points = scene.children[ 0 ];
+					var position = points.geometry.attributes.position;
+
+					var point = new THREE.Vector3();
+					var offset = new THREE.Vector3();
+
+					for ( var i = 0 ; i < position.count; i ++ ) {
+
+						point.fromBufferAttribute( scene.userData.geometry1.attributes.position, i );
+
+						scene.userData.view.displacement( point.x, point.y, point.z, t / 5, offset );
 
-						var v0 = scene.userData.geometry0.vertices[i];
-						var v = scene.userData.view.displacement( v0.x, v0.y, v0.z, t/5 );
-						scene.children[0].geometry.vertices[i].set( v.x + v0.x, v.y + v0.y, v.z + v0.z );
+						position.setXYZ( i, point.x + offset.x, point.y + offset.y, point.z + offset.z );
 
 					}
 
-					scene.children[0].geometry.verticesNeedUpdate = true;
+					position.needsUpdate = true;
 
 				} );
 
@@ -328,9 +338,9 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
-				return new THREE.Vector3( Math.sin( x - t ), 0, 0);
+				return target.set( Math.sin( x - t ), 0, 0 );
 
 			};
 
@@ -348,9 +358,9 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
-				return new THREE.Vector3( Math.sin( x - t ), 0, 0);
+				return target.set( Math.sin( x - t ), 0, 0 );
 
 			};
 
@@ -442,18 +452,18 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
 				if ( x * x + y * y < 0.01 ) {
 
-					return new THREE.Vector3( 0, 0, 0);
+					return target.set( 0, 0, 0 );
 
 				} else {
 
 					var rho = Math.sqrt( x * x + y * y );
 					var phi = Math.atan2( y, x );
 
-					return new THREE.Vector3( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0);
+					return target.set( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0 );
 
 				}
 
@@ -473,18 +483,18 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
 				if ( x * x + y * y < 0.01 ) {
 
-					return new THREE.Vector3( 0, 0, 0);
+					return target.set( 0, 0, 0 );
 
 				} else {
 
 					var rho = Math.sqrt( x * x + y * y );
 					var phi = Math.atan2( y, x );
 
-					return new THREE.Vector3( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0);
+					return target.set( 1.5 * Math.cos( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 1.5 * Math.sin( phi ) * Math.sin( rho - t ) / Math.sqrt( rho ), 0 );
 
 				}
 
@@ -578,11 +588,11 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
 				if ( x * x + y * y + z * z < 0.01 ) {
 
-					return new THREE.Vector3( 0, 0, 0);
+					return target.set( 0, 0, 0 );
 
 				} else {
 
@@ -590,7 +600,7 @@
 					var theta = Math.acos( z / r );
 					var phi = Math.atan2( y, x );
 
-					return new THREE.Vector3( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
+					return target.set( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
 
 				}
 
@@ -610,11 +620,11 @@
 
 			var parent = document.scripts[ document.scripts.length - 1 ].parentNode;
 
-			parent.displacement = function( x, y, z, t ) {
+			parent.displacement = function( x, y, z, t, target ) {
 
 				if ( x * x + y * y + z * z < 0.01 ) {
 
-					return new THREE.Vector3( 0, 0, 0);
+					return target.set( 0, 0, 0 );
 
 				} else {
 
@@ -622,7 +632,7 @@
 					var theta = Math.acos( z / r );
 					var phi = Math.atan2( y, x );
 
-					return new THREE.Vector3( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
+					return target.set( 3 * Math.cos( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.sin( phi ) * Math.sin( theta ) * Math.sin( r - t ) / r , 3 * Math.cos( theta ) * Math.sin( r - t ) / r );
 
 				}
 			};

+ 1 - 1
examples/webgl_physics_convex_break.html

@@ -387,7 +387,7 @@
 				var ballMass = 35;
 				var ballRadius = 0.4;
 
-				var ball = new THREE.Mesh( new THREE.SphereGeometry( ballRadius, 14, 10 ), ballMaterial );
+				var ball = new THREE.Mesh( new THREE.SphereBufferGeometry( ballRadius, 14, 10 ), ballMaterial );
 				ball.castShadow = true;
 				ball.receiveShadow = true;
 				var ballShape = new Ammo.btSphereShape( ballRadius );

+ 3 - 3
examples/webgl_physics_volume.html

@@ -185,8 +185,8 @@
 				sphereGeometry.translate( 5, 5, 0 );
 				createSoftVolume( sphereGeometry, volumeMass, 250 );
 
-				var boxGeometry = new THREE.BufferGeometry().fromGeometry( new THREE.BoxGeometry( 1, 1, 5, 4, 4, 20 ) );
-				boxGeometry.translate( -2, 5, 0 );
+				var boxGeometry = new THREE.BoxBufferGeometry( 1, 1, 5, 4, 4, 20 );
+				boxGeometry.translate( - 2, 5, 0 );
 				createSoftVolume( boxGeometry, volumeMass, 120 );
 
 				// Ramp
@@ -426,7 +426,7 @@
 					var ballMass = 3;
 					var ballRadius = 0.4;
 
-					var ball = new THREE.Mesh( new THREE.SphereGeometry( ballRadius, 18, 16 ), ballMaterial );
+					var ball = new THREE.Mesh( new THREE.SphereBufferGeometry( ballRadius, 18, 16 ), ballMaterial );
 					ball.castShadow = true;
 					ball.receiveShadow = true;
 					var ballShape = new Ammo.btSphereShape( ballRadius );

+ 1 - 0
examples/webgl_postprocessing_crossfade.html

@@ -41,6 +41,7 @@
 		<script src="js/crossfade/scenes.js"></script>
 		<script src="js/crossfade/gui.js"></script>
 		<script src="js/crossfade/transition.js"></script>
+		<script src="js/BufferGeometryUtils.js"></script>
 
 		<script>
 

+ 20 - 2
examples/webgl_postprocessing_godrays.html

@@ -81,7 +81,6 @@
 				camera.position.z = 200;
 
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( bgColor );
 
 				//
 
@@ -111,6 +110,7 @@
 				//
 
 				renderer = new THREE.WebGLRenderer();
+				renderer.setClearColor( 0xffffff );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				container.appendChild( renderer.domElement );
@@ -191,6 +191,7 @@
 				// targets but the aliasing causes some temporal flickering
 
 				postprocessing.rtTextureDepth = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );
+				postprocessing.rtTextureDepthMask = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, pars );
 
 				// Aggressive downsize god-ray ping-pong render targets to minimize cost
 
@@ -201,6 +202,16 @@
 
 				// god-ray shaders
 
+				var godraysMaskShader = THREE.ShaderGodRays[ "godrays_depthMask" ];
+				postprocessing.godrayMaskUniforms = THREE.UniformsUtils.clone( godraysMaskShader.uniforms );
+				postprocessing.materialGodraysDepthMask = new THREE.ShaderMaterial( {
+
+					uniforms: postprocessing.godrayMaskUniforms,
+					vertexShader: godraysMaskShader.vertexShader,
+					fragmentShader: godraysMaskShader.fragmentShader
+
+				} );
+
 				var godraysGenShader = THREE.ShaderGodRays[ "godrays_generate" ];
 				postprocessing.godrayGenUniforms = THREE.UniformsUtils.clone( godraysGenShader.uniforms );
 				postprocessing.materialGodraysGenerate = new THREE.ShaderMaterial( {
@@ -323,6 +334,13 @@
 					scene.overrideMaterial = materialDepth;
 					renderer.render( scene, camera, postprocessing.rtTextureDepth, true );
 
+					//
+
+					postprocessing.godrayMaskUniforms[ "tInput" ].value = postprocessing.rtTextureDepth.texture;
+
+					postprocessing.scene.overrideMaterial = postprocessing.materialGodraysDepthMask;
+					renderer.render( postprocessing.scene, postprocessing.camera, postprocessing.rtTextureDepthMask  );
+
 					// -- Render god-rays --
 
 					// Maximum length of god-rays (in texture space [0,1]X[0,1])
@@ -344,7 +362,7 @@
 					var stepLen = filterLen * Math.pow( TAPS_PER_PASS, -pass );
 
 					postprocessing.godrayGenUniforms[ "fStepSize" ].value = stepLen;
-					postprocessing.godrayGenUniforms[ "tInput" ].value = postprocessing.rtTextureDepth.texture;
+					postprocessing.godrayGenUniforms[ "tInput" ].value = postprocessing.rtTextureDepthMask.texture;
 
 					postprocessing.scene.overrideMaterial = postprocessing.materialGodraysGenerate;
 

+ 5 - 10
examples/webgl_raycast_texture.html

@@ -225,20 +225,15 @@
 				cubeTexture = new THREE.Texture( undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping );
 				canvas = new CanvasTexture( cubeTexture );
 				var cubeMaterial = new THREE.MeshBasicMaterial( { map: cubeTexture } );
-				var cubeGeometry = new THREE.BoxGeometry( 20, 20, 20 );
+				var cubeGeometry = new THREE.BoxBufferGeometry( 20, 20, 20 );
+				var uvs = cubeGeometry.attributes.uv.array;
 				// Set a specific texture mapping.
-				var uvs;
-				for ( var i = 0; i < cubeGeometry.faceVertexUvs[ 0 ].length; i ++ ) {
-
-					uvs = cubeGeometry.faceVertexUvs[ 0 ][ i ];
-					for ( var j = 0; j < 3; j ++ ) {
-
-						if ( uvs[ j ].x < 0.1 ) uvs[ j ].x = - 1;
-						if ( uvs[ j ].y < 0.1 ) uvs[ j ].y = - 1;
+				for ( var i = 0; i < uvs.length; i ++ ) {
 
-					}
+					uvs[ i ] *= 2;
 
 				}
+
 				var cube = new THREE.Mesh( cubeGeometry, cubeMaterial );
 				cube.position.x = 4;
 				cube.position.y = - 5;

+ 1 - 15
examples/webgl_shaders_tonemapping.html

@@ -323,22 +323,8 @@
 
 				var textureCube = new THREE.CubeTextureLoader().load( urls );
 				textureCube.format = THREE.RGBFormat;
-				var skyboxShader = THREE.ShaderLib[ "cube" ];
-				skyboxShader.uniforms[ "tCube" ].value = textureCube;
-
-				var skyboxMaterial = new THREE.ShaderMaterial( {
-
-					fragmentShader: skyboxShader.fragmentShader,
-					vertexShader: skyboxShader.vertexShader,
-					uniforms: skyboxShader.uniforms,
-					depthWrite: false,
-					side: THREE.BackSide
-
-				} ),
-
-				mesh = new THREE.Mesh( new THREE.BoxGeometry( 100, 100, 100 ), skyboxMaterial );
-				sceneCube.add( mesh );
 
+				sceneCube.background = textureCube;
 
 				renderer = new THREE.WebGLRenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );

+ 2 - 2
examples/webgl_shadowmap.html

@@ -241,7 +241,7 @@
 
 				// CUBES
 
-				var mesh = new THREE.Mesh( new THREE.BoxGeometry( 1500, 220, 150 ), planeMaterial );
+				var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1500, 220, 150 ), planeMaterial );
 
 				mesh.position.y = FLOOR - 50;
 				mesh.position.z = 20;
@@ -251,7 +251,7 @@
 
 				scene.add( mesh );
 
-				var mesh = new THREE.Mesh( new THREE.BoxGeometry( 1600, 170, 250 ), planeMaterial );
+				var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1600, 170, 250 ), planeMaterial );
 
 				mesh.position.y = FLOOR - 50;
 				mesh.position.z = 20;

+ 2 - 2
examples/webgl_shadowmap_performance.html

@@ -212,7 +212,7 @@
 
 				// CUBES
 
-				var mesh = new THREE.Mesh( new THREE.BoxGeometry( 1500, 220, 150 ), planeMaterial );
+				var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1500, 220, 150 ), planeMaterial );
 
 				mesh.position.y = FLOOR - 50;
 				mesh.position.z = 20;
@@ -222,7 +222,7 @@
 
 				scene.add( mesh );
 
-				var mesh = new THREE.Mesh( new THREE.BoxGeometry( 1600, 170, 250 ), planeMaterial );
+				var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1600, 170, 250 ), planeMaterial );
 
 				mesh.position.y = FLOOR - 50;
 				mesh.position.z = 20;

+ 6 - 2
examples/webvr_cubes.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - cubes</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0="> 
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;

+ 6 - 2
examples/webvr_daydream.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - daydream</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;

+ 6 - 2
examples/webvr_gearvr.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - gear vr</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;

+ 6 - 2
examples/webvr_panorama.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - panorama</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			html, body {
 				background-color: #000;

+ 6 - 2
examples/webvr_rollercoaster.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - roller coaster</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				margin: 0px;

+ 6 - 2
examples/webvr_sandbox.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - sandbox</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				margin: 0px;

+ 6 - 2
examples/webvr_video.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - video</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;

+ 9 - 5
examples/webvr_vive.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - htc vive</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;
@@ -60,7 +64,7 @@
 				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 10 );
 
 				room = new THREE.Mesh(
-					new THREE.BoxGeometry( 6, 6, 6, 8, 8, 8 ),
+					new THREE.BoxBufferGeometry( 6, 6, 6, 8, 8, 8 ),
 					new THREE.MeshBasicMaterial( { color: 0x404040, wireframe: true } )
 				);
 				room.position.y = 3;
@@ -72,7 +76,7 @@
 				light.position.set( 1, 1, 1 ).normalize();
 				scene.add( light );
 
-				var geometry = new THREE.BoxGeometry( 0.2, 0.2, 0.2 );
+				var geometry = new THREE.BoxBufferGeometry( 0.2, 0.2, 0.2 );
 
 				for ( var i = 0; i < 200; i ++ ) {
 
@@ -217,7 +221,7 @@
 
 					var cube = room.children[ i ];
 
-					if ( cube.geometry instanceof THREE.BoxGeometry === false ) continue;
+					if ( cube.geometry instanceof THREE.BoxBufferGeometry === false ) continue;
 
 					// cube.position.add( cube.userData.velocity );
 

+ 6 - 2
examples/webvr_vive_dragging.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - htc vive - dragging</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;

+ 15 - 9
examples/webvr_vive_paint.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - htc vive - paint</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;
@@ -71,7 +75,7 @@
 
 				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 50 );
 
-				var geometry = new THREE.BoxGeometry( 0.5, 0.8, 0.5 );
+				var geometry = new THREE.BoxBufferGeometry( 0.5, 0.8, 0.5 );
 				var material = new THREE.MeshStandardMaterial( {
 					color: 0x444444,
 					roughness: 1.0,
@@ -93,7 +97,7 @@
 				scene.add( table );
 				*/
 
-				var geometry = new THREE.PlaneGeometry( 4, 4 );
+				var geometry = new THREE.PlaneBufferGeometry( 4, 4 );
 				var material = new THREE.MeshStandardMaterial( {
 					color: 0x222222,
 					roughness: 1.0,
@@ -162,8 +166,8 @@
 					controller.receiveShadow = true;
 
 					// var pivot = new THREE.Group();
-					// var pivot = new THREE.Mesh( new THREE.BoxGeometry( 0.01, 0.01, 0.01 ) );
-					var pivot = new THREE.Mesh( new THREE.IcosahedronGeometry( 0.01, 2 ) );
+					// var pivot = new THREE.Mesh( new THREE.BoxBufferGeometry( 0.01, 0.01, 0.01 ) );
+					var pivot = new THREE.Mesh( new THREE.IcosahedronBufferGeometry( 0.01, 2 ) );
 					pivot.name = 'pivot';
 					pivot.position.y = -0.016;
 					pivot.position.z = -0.043;
@@ -230,20 +234,22 @@
 				scene.add( line );
 
 				// Shapes
-				shapes[ 'tube' ] = getTubeShapes(1.0);
+				shapes[ 'tube' ] = getTubeShapes( 1.0 );
 			}
 
-			function getTubeShapes(size) {
+			function getTubeShapes( size ) {
 
 				var PI2 = Math.PI * 2;
 
 				var sides = 10;
 				var array = [];
 				var radius = 0.01 * size;
-				for( var i = 0; i < sides; i ++ ){
+
+				for( var i = 0; i < sides; i ++ ) {
 
 					var angle = ( i / sides ) * PI2;
 					array.push( new THREE.Vector3( Math.sin( angle ) * radius, Math.cos( angle ) * radius, 0 ) );
+
 				}
 
 				return array;

+ 11 - 7
examples/webvr_vive_sculpt.html

@@ -4,8 +4,12 @@
 		<title>three.js webvr - htc vive - sculpt</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-05-12 -->
-		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-05-12" content="AkM+gi6Fb7IUBwFLs18hn/x6dusbsVRYG9KBTWUUjvmT0m0wGkxRPy63Aj/i+Ti4d6qMEMnRcsR7MVV5Wuvp/AIAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyNjExODIwMH0=">
+		<!-- Origin Trial Token, feature = WebVR (For Chrome M62+), origin = https://threejs.org, expires = 2018-06-19 -->
+		<meta http-equiv="origin-trial" data-feature="WebVR (For Chrome M62+)" data-expires="2018-06-19" content="Alxt96tYGgIr9l6EXU0eeI360zcmzOY6Kuo3kcTfBGIRDOQbgFIZKRQ1joExQ74WZr1einsE+cUMHgSclNHCQQ4AAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJWUjEuMU02MiIsImV4cGlyeSI6MTUyOTM5NzgyOH0=">
+		<!-- Origin Trial Token, feature = WebXR Device API, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Device API" data-expires="2018-06-15" content="AtJH9g6nn0B87bnjJt+9m1joZXEYDmLSlRvtMr5qJD52hMcm3S86S7jg5I7y2I5cgQglE0rzsXzti5DECQLb8QkAAABQeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkRldmljZSIsImV4cGlyeSI6MTUyOTA4NDY2OH0=">
+		<!-- Origin Trial Token, feature = WebXR Gamepad Support, origin = https://threejs.org, expires = 2018-06-15 -->
+		<meta http-equiv="origin-trial" data-feature="WebXR Gamepad Support" data-expires="2018-06-15" content="Aihhr0yXkVlCKF0DIpTbH8WX7ZmEexUhI/95+t8aoLfvBkePMiZ/iOoDPU3xefyfuczkDahH1L6eiPvRsuzITAAAAABYeyJvcmlnaW4iOiJodHRwczovL3RocmVlanMub3JnOjQ0MyIsImZlYXR1cmUiOiJXZWJYUkdhbWVwYWRTdXBwb3J0IiwiZXhwaXJ5IjoxNTI5MDg0NjY4fQ==">
 		<style>
 			body {
 				font-family: Monospace;
@@ -61,7 +65,7 @@
 
 				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 50 );
 
-				var geometry = new THREE.BoxGeometry( 0.5, 0.8, 0.5 );
+				var geometry = new THREE.BoxBufferGeometry( 0.5, 0.8, 0.5 );
 				var material = new THREE.MeshStandardMaterial( {
 					color: 0x444444,
 					roughness: 1.0,
@@ -74,7 +78,7 @@
 				table.receiveShadow = true;
 				scene.add( table );
 
-				var geometry = new THREE.PlaneGeometry( 4, 4 );
+				var geometry = new THREE.PlaneBufferGeometry( 4, 4 );
 				var material = new THREE.MeshStandardMaterial( {
 					color: 0x222222,
 					roughness: 1.0,
@@ -139,8 +143,8 @@
 					controller.castShadow = true;
 					controller.receiveShadow = true;
 
-					// var pivot = new THREE.Mesh( new THREE.BoxGeometry( 0.01, 0.01, 0.01 ) );
-					var pivot = new THREE.Mesh( new THREE.IcosahedronGeometry( 0.002, 2 ), blob.material );
+					// var pivot = new THREE.Mesh( new THREE.BoxBufferGeometry( 0.01, 0.01, 0.01 ) );
+					var pivot = new THREE.Mesh( new THREE.IcosahedronBufferGeometry( 0.002, 2 ), blob.material );
 					pivot.name = 'pivot';
 					pivot.position.y = -0.016;
 					pivot.position.z = -0.043;
@@ -264,7 +268,7 @@
 
 							updateBlob();
 
-							var geometry = blob.generateGeometry();
+							var geometry = blob.generateBufferGeometry();
 							var mesh = new THREE.Mesh( geometry, blob.material.clone() );
 							mesh.position.y = 1;
 							mesh.castShadow = true;

+ 21 - 32
src/math/Box3.js

@@ -559,41 +559,30 @@ Object.assign( Box3.prototype, {
 
 	},
 
-	applyMatrix4: function () {
-
-		var points = [
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3(),
-			new Vector3()
-		];
-
-		return function applyMatrix4( matrix ) {
-
-			// transform of empty box is an empty box.
-			if ( this.isEmpty() ) return this;
-
-			// NOTE: I am using a binary pattern to specify all 2^3 combinations below
-			points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000
-			points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001
-			points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010
-			points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011
-			points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100
-			points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101
-			points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110
-			points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix );	// 111
-
-			this.setFromPoints( points );
+	applyMatrix4: function ( matrix ) {
 
-			return this;
+		// transform of empty box is an empty box.
+		if ( this.isEmpty( ) ) return this;
 
-		};
+		var m = matrix.elements;
 
-	}(),
+		var xax = m[ 0 ] * this.min.x, xay = m[ 1 ] * this.min.x, xaz = m[ 2 ] * this.min.x;
+		var xbx = m[ 0 ] * this.max.x, xby = m[ 1 ] * this.max.x, xbz = m[ 2 ] * this.max.x;
+		var yax = m[ 4 ] * this.min.y, yay = m[ 5 ] * this.min.y, yaz = m[ 6 ] * this.min.y;
+		var ybx = m[ 4 ] * this.max.y, yby = m[ 5 ] * this.max.y, ybz = m[ 6 ] * this.max.y;
+		var zax = m[ 8 ] * this.min.z, zay = m[ 9 ] * this.min.z, zaz = m[ 10 ] * this.min.z;
+		var zbx = m[ 8 ] * this.max.z, zby = m[ 9 ] * this.max.z, zbz = m[ 10 ] * this.max.z;
+
+		this.min.x = Math.min( xax, xbx ) + Math.min( yax, ybx ) + Math.min( zax, zbx ) + m[ 12 ];
+		this.min.y = Math.min( xay, xby ) + Math.min( yay, yby ) + Math.min( zay, zby ) + m[ 13 ];
+		this.min.z = Math.min( xaz, xbz ) + Math.min( yaz, ybz ) + Math.min( zaz, zbz ) + m[ 14 ];
+		this.max.x = Math.max( xax, xbx ) + Math.max( yax, ybx ) + Math.max( zax, zbx ) + m[ 12 ];
+		this.max.y = Math.max( xay, xby ) + Math.max( yay, yby ) + Math.max( zay, zby ) + m[ 13 ];
+		this.max.z = Math.max( xaz, xbz ) + Math.max( yaz, ybz ) + Math.max( zaz, zbz ) + m[ 14 ];
+
+		return this;
+
+	},
 
 	translate: function ( offset ) {
 

+ 2 - 0
src/renderers/WebGLRenderTarget.js

@@ -30,6 +30,8 @@ function WebGLRenderTarget( width, height, options ) {
 
 	this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );
 
+	this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : true;
+
 	this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;
 	this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;
 	this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;

+ 19 - 14
src/renderers/WebGLRenderer.js

@@ -42,6 +42,7 @@ import { WebGLTextures } from './webgl/WebGLTextures.js';
 import { WebGLUniforms } from './webgl/WebGLUniforms.js';
 import { WebGLUtils } from './webgl/WebGLUtils.js';
 import { WebVRManager } from './webvr/WebVRManager.js';
+import { WebXRManager } from './webvr/WebXRManager.js';
 
 /**
  * @author supereggbert / http://www.paulbrunt.co.uk/
@@ -296,7 +297,7 @@ function WebGLRenderer( parameters ) {
 
 	// vr
 
-	var vr = new WebVRManager( _this );
+	var vr = ( 'xr' in navigator ) ? new WebXRManager( _gl ) : new WebVRManager( _this );
 
 	this.vr = vr;
 
@@ -361,9 +362,7 @@ function WebGLRenderer( parameters ) {
 
 	this.setSize = function ( width, height, updateStyle ) {
 
-		var device = vr.getDevice();
-
-		if ( device && device.isPresenting ) {
+		if ( vr.isPresenting() ) {
 
 			console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
 			return;
@@ -1045,11 +1044,9 @@ function WebGLRenderer( parameters ) {
 
 	function requestAnimationLoopFrame() {
 
-		var device = vr.getDevice();
-
-		if ( device && device.isPresenting ) {
+		if ( vr.isPresenting() ) {
 
-			device.requestAnimationFrame( animationLoop );
+			vr.requestAnimationFrame( animationLoop );
 
 		} else {
 
@@ -1396,14 +1393,22 @@ function WebGLRenderer( parameters ) {
 
 					if ( object.layers.test( camera2.layers ) ) {
 
-						var bounds = camera2.bounds;
+						if ( 'viewport' in camera2 ) { // XR
+
+							state.viewport( _currentViewport.copy( camera2.viewport ) );
+
+						} else {
+
+							var bounds = camera2.bounds;
 
-						var x = bounds.x * _width;
-						var y = bounds.y * _height;
-						var width = bounds.z * _width;
-						var height = bounds.w * _height;
+							var x = bounds.x * _width;
+							var y = bounds.y * _height;
+							var width = bounds.z * _width;
+							var height = bounds.w * _height;
 
-						state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+							state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );
+
+						}
 
 						renderObject( object, scene, camera2, geometry, material, group );
 

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

@@ -417,23 +417,7 @@ function WebGLState( gl, extensions, utils ) {
 
 	function enableAttribute( attribute ) {
 
-		newAttributes[ attribute ] = 1;
-
-		if ( enabledAttributes[ attribute ] === 0 ) {
-
-			gl.enableVertexAttribArray( attribute );
-			enabledAttributes[ attribute ] = 1;
-
-		}
-
-		if ( attributeDivisors[ attribute ] !== 0 ) {
-
-			var extension = gl.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' );
-
-			extension[ gl.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, 0 );
-			attributeDivisors[ attribute ] = 0;
-
-		}
+		enableAttributeAndDivisor( attribute, 0 );
 
 	}
 

+ 175 - 16
src/renderers/webgl/WebGLUniforms.js

@@ -73,6 +73,7 @@ var arrayCacheI32 = [];
 
 var mat4array = new Float32Array( 16 );
 var mat3array = new Float32Array( 9 );
+var mat2array = new Float32Array( 4 );
 
 // Flattening for arrays of vectors and matrices
 
@@ -111,6 +112,30 @@ function flatten( array, nBlocks, blockSize ) {
 
 }
 
+function arraysEqual( a, b ) {
+
+	if ( a.length !== b.length ) return false;
+
+	for ( var i = 0, l = a.length; i < l; i ++ ) {
+
+		if ( a[ i ] !== b[ i ] ) return false;
+
+	}
+
+	return true;
+
+}
+
+function copyArray( a, b ) {
+
+	for ( var i = 0, l = b.length; i < l; i ++ ) {
+
+		a[ i ] = b[ i ];
+
+	}
+
+}
+
 // Texture unit allocation
 
 function allocTexUnits( renderer, n ) {
@@ -140,27 +165,52 @@ function allocTexUnits( renderer, n ) {
 
 function setValue1f( gl, v ) {
 
+	var cache = this.cache;
+
+	if ( cache[ 0 ] === v ) return;
+
 	gl.uniform1f( this.addr, v );
 
+	cache[ 0 ] = v;
+
 }
 
 function setValue1i( gl, v ) {
 
+	var cache = this.cache;
+
+	if ( cache[ 0 ] === v ) return;
+
 	gl.uniform1i( this.addr, v );
 
+	cache[ 0 ] = v;
+
 }
 
 // Single float vector (from flat array or THREE.VectorN)
 
 function setValue2fv( gl, v ) {
 
-	if ( v.x === undefined ) {
+	var cache = this.cache;
 
-		gl.uniform2fv( this.addr, v );
+	if ( v.x !== undefined ) {
+
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) {
+
+			gl.uniform2f( this.addr, v.x, v.y );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+
+		}
 
 	} else {
 
-		gl.uniform2f( this.addr, v.x, v.y );
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniform2fv( this.addr, v );
+
+		copyArray( cache, v );
 
 	}
 
@@ -168,31 +218,68 @@ function setValue2fv( gl, v ) {
 
 function setValue3fv( gl, v ) {
 
+	var cache = this.cache;
+
 	if ( v.x !== undefined ) {
 
-		gl.uniform3f( this.addr, v.x, v.y, v.z );
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) {
+
+			gl.uniform3f( this.addr, v.x, v.y, v.z );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+			cache[ 2 ] = v.z;
+
+		}
 
 	} else if ( v.r !== undefined ) {
 
-		gl.uniform3f( this.addr, v.r, v.g, v.b );
+		if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) {
+
+			gl.uniform3f( this.addr, v.r, v.g, v.b );
+
+			cache[ 0 ] = v.r;
+			cache[ 1 ] = v.g;
+			cache[ 2 ] = v.b;
+
+		}
 
 	} else {
 
+		if ( arraysEqual( cache, v ) ) return;
+
 		gl.uniform3fv( this.addr, v );
 
+		copyArray( cache, v );
+
 	}
 
 }
 
 function setValue4fv( gl, v ) {
 
-	if ( v.x === undefined ) {
+	var cache = this.cache;
 
-		gl.uniform4fv( this.addr, v );
+	if ( v.x !== undefined ) {
+
+		if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) {
+
+			gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+
+			cache[ 0 ] = v.x;
+			cache[ 1 ] = v.y;
+			cache[ 2 ] = v.z;
+			cache[ 3 ] = v.w;
+
+		}
 
 	} else {
 
-		 gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniform4fv( this.addr, v );
+
+		copyArray( cache, v );
 
 	}
 
@@ -202,36 +289,81 @@ function setValue4fv( gl, v ) {
 
 function setValue2fm( gl, v ) {
 
-	gl.uniformMatrix2fv( this.addr, false, v.elements || v );
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
+
+		gl.uniformMatrix2fv( this.addr, false, v );
+
+		copyArray( cache, v );
+
+	} else {
+
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat2array.set( elements );
+
+		gl.uniformMatrix2fv( this.addr, false, mat2array );
+
+		copyArray( cache, elements );
+
+	}
 
 }
 
 function setValue3fm( gl, v ) {
 
-	if ( v.elements === undefined ) {
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
 
 		gl.uniformMatrix3fv( this.addr, false, v );
 
+		copyArray( cache, v );
+
 	} else {
 
-		mat3array.set( v.elements );
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat3array.set( elements );
+
 		gl.uniformMatrix3fv( this.addr, false, mat3array );
 
+		copyArray( cache, elements );
+
 	}
 
 }
 
 function setValue4fm( gl, v ) {
 
-	if ( v.elements === undefined ) {
+	var cache = this.cache;
+	var elements = v.elements;
+
+	if ( elements === undefined ) {
+
+		if ( arraysEqual( cache, v ) ) return;
 
 		gl.uniformMatrix4fv( this.addr, false, v );
 
+		copyArray( cache, v );
+
 	} else {
 
-		mat4array.set( v.elements );
+		if ( arraysEqual( cache, elements ) ) return;
+
+		mat4array.set( elements );
+
 		gl.uniformMatrix4fv( this.addr, false, mat4array );
 
+		copyArray( cache, elements );
+
 	}
 
 }
@@ -241,7 +373,14 @@ function setValue4fm( gl, v ) {
 function setValueT1( gl, v, renderer ) {
 
 	var unit = renderer.allocTextureUnit();
-	gl.uniform1i( this.addr, unit );
+
+	if ( this.cache[ 0 ] !== unit ) {
+
+		gl.uniform1i( this.addr, unit );
+		this.cache[ 0 ] = unit;
+
+	}
+
 	renderer.setTexture2D( v || emptyTexture, unit );
 
 }
@@ -249,7 +388,14 @@ function setValueT1( gl, v, renderer ) {
 function setValueT6( gl, v, renderer ) {
 
 	var unit = renderer.allocTextureUnit();
-	gl.uniform1i( this.addr, unit );
+
+	if ( this.cache[ 0 ] !== unit ) {
+
+		gl.uniform1i( this.addr, unit );
+		this.cache[ 0 ] = unit;
+
+	}
+
 	renderer.setTextureCube( v || emptyCubeTexture, unit );
 
 }
@@ -258,20 +404,32 @@ function setValueT6( gl, v, renderer ) {
 
 function setValue2iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform2iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 function setValue3iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform3iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 function setValue4iv( gl, v ) {
 
+	if ( arraysEqual( this.cache, v ) ) return;
+
 	gl.uniform4iv( this.addr, v );
 
+	copyArray( this.cache, v );
+
 }
 
 // Helper to pick the right setter for the singular case
@@ -419,6 +577,7 @@ function SingleUniform( id, activeInfo, addr ) {
 
 	this.id = id;
 	this.addr = addr;
+	this.cache = [];
 	this.setValue = getSingularSetter( activeInfo.type );
 
 	// this.path = activeInfo.name; // DEBUG
@@ -490,7 +649,7 @@ function parseUniform( activeInfo, addr, container ) {
 	// reset RegExp object, because of the early exit of a previous run
 	RePathPart.lastIndex = 0;
 
-	for ( ; ; ) {
+	while ( true ) {
 
 		var match = RePathPart.exec( path ),
 			matchEnd = RePathPart.lastIndex,

+ 9 - 1
src/renderers/webvr/WebVRManager.js

@@ -3,8 +3,8 @@
  */
 
 import { Matrix4 } from '../../math/Matrix4.js';
-import { Vector4 } from '../../math/Vector4.js';
 import { Vector3 } from '../../math/Vector3.js';
+import { Vector4 } from '../../math/Vector4.js';
 import { Quaternion } from '../../math/Quaternion.js';
 import { ArrayCamera } from '../../cameras/ArrayCamera.js';
 import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
@@ -226,6 +226,14 @@ function WebVRManager( renderer ) {
 
 	};
 
+	this.isPresenting = isPresenting;
+
+	this.requestAnimationFrame = function ( callback ) {
+
+		device.requestAnimationFrame( callback );
+
+	};
+
 	this.submitFrame = function () {
 
 		if ( isPresenting() ) device.submitFrame();

+ 146 - 0
src/renderers/webvr/WebXRManager.js

@@ -0,0 +1,146 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+import { Matrix4 } from '../../math/Matrix4.js';
+import { Vector4 } from '../../math/Vector4.js';
+import { Vector3 } from '../../math/Vector3.js';
+import { Quaternion } from '../../math/Quaternion.js';
+import { ArrayCamera } from '../../cameras/ArrayCamera.js';
+import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
+
+function WebXRManager( gl ) {
+
+	var scope = this;
+
+	var device = null;
+	var session = null;
+
+	var frameOfRef = null;
+	var isExclusive = false;
+
+	var pose = null;
+
+	function isPresenting() {
+
+		return session !== null && frameOfRef !== null;
+
+	}
+
+	//
+
+	var cameraL = new PerspectiveCamera();
+	cameraL.layers.enable( 1 );
+	cameraL.viewport = new Vector4();
+
+	var cameraR = new PerspectiveCamera();
+	cameraR.layers.enable( 2 );
+	cameraR.viewport = new Vector4();
+
+	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
+	cameraVR.layers.enable( 1 );
+	cameraVR.layers.enable( 2 );
+
+	//
+
+	this.enabled = false;
+
+	this.getDevice = function () {
+
+		return device;
+
+	};
+
+	this.setDevice = function ( value ) {
+
+		if ( value !== undefined ) device = value;
+
+		gl.setCompatibleXRDevice( value );
+
+	};
+
+	this.setSession = function ( value ) {
+
+		session = value;
+
+		if ( session !== null ) {
+
+			session.baseLayer = new XRWebGLLayer( session, gl );
+			session.requestFrameOfReference( 'stage' ).then( function ( value ) {
+
+				frameOfRef = value;
+				isExclusive = session.exclusive;
+
+			} );
+
+		}
+
+	};
+
+	this.getCamera = function ( camera ) {
+
+		return isPresenting() ? cameraVR : camera;
+
+	};
+
+	this.isPresenting = isPresenting;
+
+	this.requestAnimationFrame = function ( callback ) {
+
+		function onFrame( time, frame ) {
+
+			pose = frame.getDevicePose( frameOfRef );
+
+			var layer = session.baseLayer;
+			var views = frame.views;
+
+			for ( var i = 0; i < views.length; i ++ ) {
+
+				var view = views[ i ];
+				var viewport = layer.getViewport( view );
+				var viewMatrix = pose.getViewMatrix( view );
+
+				var camera = cameraVR.cameras[ i ];
+				camera.projectionMatrix.fromArray( view.projectionMatrix );
+				camera.matrixWorldInverse.fromArray( viewMatrix );
+				camera.matrixWorld.getInverse( camera.matrixWorldInverse );
+				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
+
+				if ( i === 0 ) {
+
+					cameraVR.matrixWorld.copy( camera.matrixWorld );
+					cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );
+
+					// HACK (mrdoob)
+					// https://github.com/w3c/webvr/issues/203
+
+					cameraVR.projectionMatrix.copy( camera.projectionMatrix );
+
+				}
+
+			}
+
+			gl.bindFramebuffer( gl.FRAMEBUFFER, session.baseLayer.framebuffer );
+
+			callback();
+
+		}
+
+		session.requestAnimationFrame( onFrame );
+
+	};
+
+	// DEPRECATED
+
+	this.getStandingMatrix = function () {
+
+		console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
+		return new THREE.Matrix4();
+
+	};
+
+	this.submitFrame = function () {};
+
+}
+
+export { WebXRManager };

+ 1 - 0
test/benchmark/benchmarks.html

@@ -15,6 +15,7 @@
   <script src="core/Vector3Storage.js"></script>
   <script src="core/Vector3Length.js"></script>
   <script src="core/Float32Array.js"></script>
+  <script src="core/UpdateMatrixWorld.js"></script>
 </head>
 <body>
   <header>

+ 48 - 0
test/benchmark/core/UpdateMatrixWorld.js

@@ -0,0 +1,48 @@
+(function() {
+
+  THREE = Bench.THREE;
+
+  var position = new THREE.Vector3(1, 1, 1);
+  var scale = new THREE.Vector3(2, 1, 0.5);
+  var rotation = new THREE.Quaternion();
+  rotation.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 8);
+  var createLocallyOffsetChild = function() {
+    var child = new THREE.Object3D();
+    child.position = position;
+    child.scale = scale;
+    child.rotation = rotation;
+    return child;
+  };
+
+  var generateSceneGraph = function(root, depth, breadth, initObject) {
+    if (depth > 0) {
+      for (var i = 0; i < breadth; i++) {
+        var child = initObject();
+        root.add(child);
+        generateSceneGraph(child, depth - 1, breadth, initObject);
+      }
+    }
+    return root;
+  };
+
+  var nodeCount = function(root) {
+    return root.children.reduce(function(acc, x) { return acc + nodeCount(x); }, 1);
+  };
+
+  var rootA = generateSceneGraph(new THREE.Object3D(), 100, 1, createLocallyOffsetChild);
+  var rootB = generateSceneGraph(new THREE.Object3D(), 3, 10, createLocallyOffsetChild);
+  var rootC = generateSceneGraph(new THREE.Object3D(), 9, 3, createLocallyOffsetChild);
+
+  var s = Bench.newSuite("Update world transforms");
+
+  s.add('Update graph depth=100, breadth=1 (' + nodeCount(rootA) + ' nodes)', function() {
+    rootA.updateMatrixWorld(true);
+  });
+  s.add('Update graph depth=3, breadth=10 (' + nodeCount(rootB) + ' nodes)', function() {
+    rootB.updateMatrixWorld(true);
+  });
+  s.add('Update graph depth=9, breadth=3 (' + nodeCount(rootC) + ' nodes)', function() {
+    rootC.updateMatrixWorld(true);
+  });
+
+})();

+ 1 - 0
utils/build/externs.js

@@ -5,3 +5,4 @@ var exports;
 var performance;
 var createImageBitmap;
 var WebGL2RenderingContext;
+var XRWebGLLayer;

+ 4 - 0
utils/exporters/blender/README.md

@@ -33,6 +33,10 @@ OR (for 2.6)
 In your user's library for user installed Blender addons:
 
     /Users/(myuser)/Library/Application Support/Blender/2.7X/scripts/addons
+    
+OR (for 2.79)
+    
+    /Applications/Blender/blender.app/Contents/Resources/2.79/scripts/addons
 
 ### Linux
 

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