浏览代码

Merge branch 'dev' into add-material-stencil

Garrett Johnson 6 年之前
父节点
当前提交
3d93b1bebd
共有 100 个文件被更改,包括 35342 次插入3492 次删除
  1. 130 50
      build/three.js
  2. 329 328
      build/three.min.js
  3. 130 50
      build/three.module.js
  4. 4 29
      docs/api/en/math/Euler.html
  5. 1 32
      docs/api/en/math/Quaternion.html
  6. 6 0
      docs/api/en/math/Vector4.html
  7. 3 0
      docs/examples/loaders/BasisTextureLoader.html
  8. 42 1
      docs/manual/en/introduction/Import-via-modules.html
  9. 36 18
      examples/index.html
  10. 5 5
      examples/js/animation/CCDIKSolver.js
  11. 5 5
      examples/js/animation/MMDAnimationHelper.js
  12. 32 30
      examples/js/animation/MMDPhysics.js
  13. 5 3
      examples/js/exporters/ColladaExporter.js
  14. 3 3
      examples/js/exporters/DracoExporter.js
  15. 1 0
      examples/js/libs/chevrotain.min.js
  16. 0 0
      examples/js/libs/ctm.js
  17. 1 1
      examples/js/libs/lzma.js
  18. 42 63
      examples/js/loaders/AWDLoader.js
  19. 11 7
      examples/js/loaders/AssimpLoader.js
  20. 7 1
      examples/js/loaders/BasisTextureLoader.js
  21. 576 404
      examples/js/loaders/DRACOLoader.js
  22. 57 8
      examples/js/loaders/EXRLoader.js
  23. 5 81
      examples/js/loaders/GLTFLoader.js
  24. 63 127
      examples/js/loaders/HDRCubeTextureLoader.js
  25. 2 3
      examples/js/loaders/LDrawLoader.js
  26. 0 1
      examples/js/loaders/LWOLoader.js
  27. 1 1
      examples/js/loaders/MMDLoader.js
  28. 4 0
      examples/js/loaders/NRRDLoader.js
  29. 1 1
      examples/js/loaders/TTFLoader.js
  30. 1996 762
      examples/js/loaders/VRMLLoader.js
  31. 18 35
      examples/js/loaders/XLoader.js
  32. 24 0
      examples/js/loaders/ctm/CTMLoader.js
  33. 1 1
      examples/js/loaders/ctm/CTMWorker.js
  34. 0 20
      examples/js/loaders/ctm/license/OpenCTM.txt
  35. 0 19
      examples/js/loaders/ctm/license/js-lzma.txt
  36. 0 19
      examples/js/loaders/ctm/license/js-openctm.txt
  37. 3 1
      examples/js/loaders/sea3d/SEA3DLoader.js
  38. 5 5
      examples/js/misc/CarControls.js
  39. 0 0
      examples/js/misc/ConvexObjectBreaker.js
  40. 5 5
      examples/js/misc/GPUComputationRenderer.js
  41. 0 0
      examples/js/misc/Gyroscope.js
  42. 1 1
      examples/js/misc/MD2Character.js
  43. 9 9
      examples/js/misc/MD2CharacterComplex.js
  44. 4 3
      examples/js/misc/MorphAnimMesh.js
  45. 0 0
      examples/js/misc/MorphBlendMesh.js
  46. 12 8
      examples/js/misc/Ocean.js
  47. 15 15
      examples/js/misc/RollerCoaster.js
  48. 0 0
      examples/js/misc/Volume.js
  49. 0 0
      examples/js/misc/VolumeSlice.js
  50. 0 0
      examples/js/objects/GPUParticleSystem.js
  51. 0 2
      examples/js/objects/MarchingCubes.js
  52. 10 9
      examples/js/shaders/OceanShaders.js
  53. 311 311
      examples/js/shaders/VolumeShader.js
  54. 16 0
      examples/jsm/animation/AnimationClipCreator.d.ts
  55. 125 0
      examples/jsm/animation/AnimationClipCreator.js
  56. 25 0
      examples/jsm/animation/CCDIKSolver.d.ts
  57. 472 0
      examples/jsm/animation/CCDIKSolver.js
  58. 85 0
      examples/jsm/animation/MMDAnimationHelper.d.ts
  59. 1052 0
      examples/jsm/animation/MMDAnimationHelper.js
  60. 109 0
      examples/jsm/animation/MMDPhysics.d.ts
  61. 1428 0
      examples/jsm/animation/MMDPhysics.js
  62. 20 0
      examples/jsm/animation/TimelinerController.d.ts
  63. 283 0
      examples/jsm/animation/TimelinerController.js
  64. 6 3
      examples/jsm/exporters/ColladaExporter.js
  65. 20 0
      examples/jsm/exporters/DRACOExporter.d.ts
  66. 212 0
      examples/jsm/exporters/DRACOExporter.js
  67. 105 0
      examples/jsm/geometries/LightningStrike.d.ts
  68. 1021 0
      examples/jsm/geometries/LightningStrike.js
  69. 2311 0
      examples/jsm/libs/dat.gui.module.js
  70. 28 0
      examples/jsm/libs/gunzip.min.js
  71. 17 0
      examples/jsm/libs/inflate.min.js
  72. 11535 0
      examples/jsm/libs/mmdparser.module.js
  73. 10 0
      examples/jsm/lights/LightProbeGenerator.d.ts
  74. 130 0
      examples/jsm/lights/LightProbeGenerator.js
  75. 50 0
      examples/jsm/loaders/AWDLoader.d.ts
  76. 1234 0
      examples/jsm/loaders/AWDLoader.js
  77. 11 7
      examples/jsm/loaders/AssimpLoader.js
  78. 21 0
      examples/jsm/loaders/DRACOLoader.d.ts
  79. 740 0
      examples/jsm/loaders/DRACOLoader.js
  80. 2 0
      examples/jsm/loaders/EXRLoader.d.ts
  81. 1 0
      examples/jsm/loaders/FBXLoader.js
  82. 47 111
      examples/jsm/loaders/GLTFLoader.js
  83. 3 1
      examples/jsm/loaders/HDRCubeTextureLoader.d.ts
  84. 63 127
      examples/jsm/loaders/HDRCubeTextureLoader.js
  85. 21 0
      examples/jsm/loaders/LDrawLoader.d.ts
  86. 1926 0
      examples/jsm/loaders/LDrawLoader.js
  87. 0 1
      examples/jsm/loaders/LWOLoader.js
  88. 14 0
      examples/jsm/loaders/MD2Loader.d.ts
  89. 398 0
      examples/jsm/loaders/MD2Loader.js
  90. 36 0
      examples/jsm/loaders/MMDLoader.d.ts
  91. 2062 0
      examples/jsm/loaders/MMDLoader.js
  92. 23 0
      examples/jsm/loaders/NRRDLoader.d.ts
  93. 614 0
      examples/jsm/loaders/NRRDLoader.js
  94. 16 0
      examples/jsm/loaders/TTFLoader.d.ts
  95. 207 0
      examples/jsm/loaders/TTFLoader.js
  96. 2006 765
      examples/jsm/loaders/VRMLLoader.js
  97. 15 0
      examples/jsm/loaders/VTKLoader.d.ts
  98. 1187 0
      examples/jsm/loaders/VTKLoader.js
  99. 23 0
      examples/jsm/loaders/XLoader.d.ts
  100. 1696 0
      examples/jsm/loaders/XLoader.js

+ 130 - 50
build/three.js

@@ -3826,6 +3826,7 @@
 
 	/**
 	 * @author alteredq / http://alteredqualia.com
+	 * @author WestLangley / http://github.com/WestLangley
 	 */
 
 	function WebGLRenderTargetCube( width, height, options ) {
@@ -3839,6 +3840,99 @@
 
 	WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
 
+	WebGLRenderTargetCube.prototype.fromEquirectangularTexture = function ( renderer, texture ) {
+
+		this.texture.type = texture.type;
+		this.texture.format = texture.format;
+		this.texture.encoding = texture.encoding;
+
+		var scene = new THREE.Scene();
+
+		var shader = {
+
+			uniforms: {
+				tEquirect: { value: null },
+			},
+
+			vertexShader: [
+
+				"varying vec3 vWorldDirection;",
+
+				"vec3 transformDirection( in vec3 dir, in mat4 matrix ) {",
+
+				"	return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );",
+
+				"}",
+
+				"void main() {",
+
+				"	vWorldDirection = transformDirection( position, modelMatrix );",
+
+				"	#include <begin_vertex>",
+				"	#include <project_vertex>",
+
+				"}"
+
+			].join( '\n' ),
+
+			fragmentShader: [
+
+				"uniform sampler2D tEquirect;",
+
+				"varying vec3 vWorldDirection;",
+
+				"#define RECIPROCAL_PI 0.31830988618",
+				"#define RECIPROCAL_PI2 0.15915494",
+
+				"void main() {",
+
+				"	vec3 direction = normalize( vWorldDirection );",
+
+				"	vec2 sampleUV;",
+
+				"	sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;",
+
+				"	sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
+
+				"	gl_FragColor = texture2D( tEquirect, sampleUV );",
+
+				"}"
+
+			].join( '\n' ),
+		};
+
+		var material = new THREE.ShaderMaterial( {
+
+			type: 'CubemapFromEquirect',
+
+			uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader,
+			side: THREE.BackSide,
+			blending: THREE.NoBlending
+
+		} );
+
+		material.uniforms.tEquirect.value = texture;
+
+		var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 5, 5, 5 ), material );
+
+		scene.add( mesh );
+
+		var camera = new THREE.CubeCamera( 1, 10, 1 );
+
+		camera.renderTarget = this;
+		camera.renderTarget.texture.name = 'CubeCameraTexture';
+
+		camera.update( renderer, scene );
+
+		mesh.geometry.dispose();
+		mesh.material.dispose();
+
+		return this;
+
+	};
+
 	/**
 	 * @author alteredq / http://alteredqualia.com/
 	 */
@@ -6171,7 +6265,7 @@
 
 	var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
 
-	var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
+	var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
 
 	var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif";
 
@@ -17126,7 +17220,7 @@
 
 	}
 
-	function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures ) {
+	function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities ) {
 
 		var gl = renderer.context;
 
@@ -17595,7 +17689,7 @@
 
 			if ( cachedUniforms === undefined ) {
 
-				cachedUniforms = new WebGLUniforms( gl, program, textures );
+				cachedUniforms = new WebGLUniforms( gl, program );
 
 			}
 
@@ -17628,31 +17722,6 @@
 
 		};
 
-		// DEPRECATED
-
-		Object.defineProperties( this, {
-
-			uniforms: {
-				get: function () {
-
-					console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
-					return this.getUniforms();
-
-				}
-			},
-
-			attributes: {
-				get: function () {
-
-					console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
-					return this.getAttributes();
-
-				}
-			}
-
-		} );
-
-
 		//
 
 		this.name = shader.name;
@@ -17671,7 +17740,7 @@
 	 * @author mrdoob / http://mrdoob.com/
 	 */
 
-	function WebGLPrograms( renderer, extensions, capabilities, textures ) {
+	function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		var programs = [];
 
@@ -17945,7 +18014,7 @@
 
 			if ( program === undefined ) {
 
-				program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures );
+				program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities );
 				programs.push( program );
 
 			}
@@ -22990,7 +23059,7 @@
 			geometries = new WebGLGeometries( _gl, attributes, info );
 			objects = new WebGLObjects( geometries, info );
 			morphtargets = new WebGLMorphtargets( _gl );
-			programCache = new WebGLPrograms( _this, extensions, capabilities, textures );
+			programCache = new WebGLPrograms( _this, extensions, capabilities );
 			renderLists = new WebGLRenderLists();
 			renderStates = new WebGLRenderStates();
 
@@ -25823,9 +25892,18 @@
 			return function raycast( raycaster, intersects ) {
 
 				worldScale.setFromMatrixScale( this.matrixWorld );
-				viewWorldMatrix.getInverse( this.modelViewMatrix ).premultiply( this.matrixWorld );
+
+				viewWorldMatrix.copy( raycaster._camera.matrixWorld );
+				this.modelViewMatrix.multiplyMatrices( raycaster._camera.matrixWorldInverse, this.matrixWorld );
+
 				mvPosition.setFromMatrixPosition( this.modelViewMatrix );
 
+				if ( raycaster._camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) {
+
+					worldScale.multiplyScalar( - mvPosition.z );
+
+				}
+
 				var rotation = this.material.rotation;
 				var sin, cos;
 				if ( rotation !== 0 ) {
@@ -40397,19 +40475,19 @@
 			var coeff = this.coefficients;
 
 			// band 0
-			target = coeff[ 0 ] * 0.282095;
+			target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 );
 
 			// band 1
-			target += coeff[ 1 ] * 0.488603 * y;
-			target += coeff[ 2 ] * 0.488603 * z;
-			target += coeff[ 3 ] * 0.488603 * x;
+			target.addScale( coeff[ 1 ], 0.488603 * y );
+			target.addScale( coeff[ 2 ], 0.488603 * z );
+			target.addScale( coeff[ 3 ], 0.488603 * x );
 
 			// band 2
-			target += coeff[ 4 ] * 1.092548 * ( x * y );
-			target += coeff[ 5 ] * 1.092548 * ( y * z );
-			target += coeff[ 6 ] * 0.315392 * ( 3.0 * z * z - 1.0 );
-			target += coeff[ 7 ] * 1.092548 * ( x * z );
-			target += coeff[ 8 ] * 0.546274 * ( x * x - y * y );
+			target.addScale( coeff[ 4 ], 1.092548 * ( x * y ) );
+			target.addScale( coeff[ 5 ], 1.092548 * ( y * z ) );
+			target.addScale( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) );
+			target.addScale( coeff[ 7 ], 1.092548 * ( x * z ) );
+			target.addScale( coeff[ 8 ], 0.546274 * ( x * x - y * y ) );
 
 			return target;
 
@@ -40427,19 +40505,19 @@
 			var coeff = this.coefficients;
 
 			// band 0
-			target = coeff[ 0 ] * 0.886227; // π * 0.282095
+			target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095
 
 			// band 1
-			target += coeff[ 1 ] * 2.0 * 0.511664 * y; // ( 2 * π / 3 ) * 0.488603
-			target += coeff[ 2 ] * 2.0 * 0.511664 * z;
-			target += coeff[ 3 ] * 2.0 * 0.511664 * x;
+			target.addScale( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603
+			target.addScale( coeff[ 2 ], 2.0 * 0.511664 * z );
+			target.addScale( coeff[ 3 ], 2.0 * 0.511664 * x );
 
 			// band 2
-			target += coeff[ 4 ] * 2.0 * 0.429043 * x * y; // ( π / 4 ) * 1.092548
-			target += coeff[ 5 ] * 2.0 * 0.429043 * y * z;
-			target += coeff[ 6 ] * ( 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3
-			target += coeff[ 7 ] * 2.0 * 0.429043 * x * z;
-			target += coeff[ 8 ] * 0.429043 * ( x * x - y * y ); // ( π / 4 ) * 0.546274
+			target.addScale( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548
+			target.addScale( coeff[ 5 ], 2.0 * 0.429043 * y * z );
+			target.addScale( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3
+			target.addScale( coeff[ 7 ], 2.0 * 0.429043 * x * z );
+			target.addScale( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274
 
 			return target;
 
@@ -44503,11 +44581,13 @@
 
 				this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
 				this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
+				this._camera = camera;
 
 			} else if ( ( camera && camera.isOrthographicCamera ) ) {
 
 				this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera
 				this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
+				this._camera = camera;
 
 			} else {
 

文件差异内容过多而无法显示
+ 329 - 328
build/three.min.js


+ 130 - 50
build/three.module.js

@@ -3820,6 +3820,7 @@ WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRend
 
 /**
  * @author alteredq / http://alteredqualia.com
+ * @author WestLangley / http://github.com/WestLangley
  */
 
 function WebGLRenderTargetCube( width, height, options ) {
@@ -3833,6 +3834,99 @@ WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;
 
 WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;
 
+WebGLRenderTargetCube.prototype.fromEquirectangularTexture = function ( renderer, texture ) {
+
+	this.texture.type = texture.type;
+	this.texture.format = texture.format;
+	this.texture.encoding = texture.encoding;
+
+	var scene = new THREE.Scene();
+
+	var shader = {
+
+		uniforms: {
+			tEquirect: { value: null },
+		},
+
+		vertexShader: [
+
+			"varying vec3 vWorldDirection;",
+
+			"vec3 transformDirection( in vec3 dir, in mat4 matrix ) {",
+
+			"	return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );",
+
+			"}",
+
+			"void main() {",
+
+			"	vWorldDirection = transformDirection( position, modelMatrix );",
+
+			"	#include <begin_vertex>",
+			"	#include <project_vertex>",
+
+			"}"
+
+		].join( '\n' ),
+
+		fragmentShader: [
+
+			"uniform sampler2D tEquirect;",
+
+			"varying vec3 vWorldDirection;",
+
+			"#define RECIPROCAL_PI 0.31830988618",
+			"#define RECIPROCAL_PI2 0.15915494",
+
+			"void main() {",
+
+			"	vec3 direction = normalize( vWorldDirection );",
+
+			"	vec2 sampleUV;",
+
+			"	sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;",
+
+			"	sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;",
+
+			"	gl_FragColor = texture2D( tEquirect, sampleUV );",
+
+			"}"
+
+		].join( '\n' ),
+	};
+
+	var material = new THREE.ShaderMaterial( {
+
+		type: 'CubemapFromEquirect',
+
+		uniforms: THREE.UniformsUtils.clone( shader.uniforms ),
+		vertexShader: shader.vertexShader,
+		fragmentShader: shader.fragmentShader,
+		side: THREE.BackSide,
+		blending: THREE.NoBlending
+
+	} );
+
+	material.uniforms.tEquirect.value = texture;
+
+	var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 5, 5, 5 ), material );
+
+	scene.add( mesh );
+
+	var camera = new THREE.CubeCamera( 1, 10, 1 );
+
+	camera.renderTarget = this;
+	camera.renderTarget.texture.name = 'CubeCameraTexture';
+
+	camera.update( renderer, scene );
+
+	mesh.geometry.dispose();
+	mesh.material.dispose();
+
+	return this;
+
+};
+
 /**
  * @author alteredq / http://alteredqualia.com/
  */
@@ -6165,7 +6259,7 @@ var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n
 
 var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif";
 
-var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
+var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif";
 
 var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif";
 
@@ -17120,7 +17214,7 @@ function unrollLoops( string ) {
 
 }
 
-function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures ) {
+function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities ) {
 
 	var gl = renderer.context;
 
@@ -17589,7 +17683,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 		if ( cachedUniforms === undefined ) {
 
-			cachedUniforms = new WebGLUniforms( gl, program, textures );
+			cachedUniforms = new WebGLUniforms( gl, program );
 
 		}
 
@@ -17622,31 +17716,6 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 
 	};
 
-	// DEPRECATED
-
-	Object.defineProperties( this, {
-
-		uniforms: {
-			get: function () {
-
-				console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
-				return this.getUniforms();
-
-			}
-		},
-
-		attributes: {
-			get: function () {
-
-				console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
-				return this.getAttributes();
-
-			}
-		}
-
-	} );
-
-
 	//
 
 	this.name = shader.name;
@@ -17665,7 +17734,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
  * @author mrdoob / http://mrdoob.com/
  */
 
-function WebGLPrograms( renderer, extensions, capabilities, textures ) {
+function WebGLPrograms( renderer, extensions, capabilities ) {
 
 	var programs = [];
 
@@ -17939,7 +18008,7 @@ function WebGLPrograms( renderer, extensions, capabilities, textures ) {
 
 		if ( program === undefined ) {
 
-			program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities, textures );
+			program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities );
 			programs.push( program );
 
 		}
@@ -22984,7 +23053,7 @@ function WebGLRenderer( parameters ) {
 		geometries = new WebGLGeometries( _gl, attributes, info );
 		objects = new WebGLObjects( geometries, info );
 		morphtargets = new WebGLMorphtargets( _gl );
-		programCache = new WebGLPrograms( _this, extensions, capabilities, textures );
+		programCache = new WebGLPrograms( _this, extensions, capabilities );
 		renderLists = new WebGLRenderLists();
 		renderStates = new WebGLRenderStates();
 
@@ -25817,9 +25886,18 @@ Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {
 		return function raycast( raycaster, intersects ) {
 
 			worldScale.setFromMatrixScale( this.matrixWorld );
-			viewWorldMatrix.getInverse( this.modelViewMatrix ).premultiply( this.matrixWorld );
+
+			viewWorldMatrix.copy( raycaster._camera.matrixWorld );
+			this.modelViewMatrix.multiplyMatrices( raycaster._camera.matrixWorldInverse, this.matrixWorld );
+
 			mvPosition.setFromMatrixPosition( this.modelViewMatrix );
 
+			if ( raycaster._camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) {
+
+				worldScale.multiplyScalar( - mvPosition.z );
+
+			}
+
 			var rotation = this.material.rotation;
 			var sin, cos;
 			if ( rotation !== 0 ) {
@@ -40391,19 +40469,19 @@ Object.assign( SphericalHarmonics3.prototype, {
 		var coeff = this.coefficients;
 
 		// band 0
-		target = coeff[ 0 ] * 0.282095;
+		target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 );
 
 		// band 1
-		target += coeff[ 1 ] * 0.488603 * y;
-		target += coeff[ 2 ] * 0.488603 * z;
-		target += coeff[ 3 ] * 0.488603 * x;
+		target.addScale( coeff[ 1 ], 0.488603 * y );
+		target.addScale( coeff[ 2 ], 0.488603 * z );
+		target.addScale( coeff[ 3 ], 0.488603 * x );
 
 		// band 2
-		target += coeff[ 4 ] * 1.092548 * ( x * y );
-		target += coeff[ 5 ] * 1.092548 * ( y * z );
-		target += coeff[ 6 ] * 0.315392 * ( 3.0 * z * z - 1.0 );
-		target += coeff[ 7 ] * 1.092548 * ( x * z );
-		target += coeff[ 8 ] * 0.546274 * ( x * x - y * y );
+		target.addScale( coeff[ 4 ], 1.092548 * ( x * y ) );
+		target.addScale( coeff[ 5 ], 1.092548 * ( y * z ) );
+		target.addScale( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) );
+		target.addScale( coeff[ 7 ], 1.092548 * ( x * z ) );
+		target.addScale( coeff[ 8 ], 0.546274 * ( x * x - y * y ) );
 
 		return target;
 
@@ -40421,19 +40499,19 @@ Object.assign( SphericalHarmonics3.prototype, {
 		var coeff = this.coefficients;
 
 		// band 0
-		target = coeff[ 0 ] * 0.886227; // π * 0.282095
+		target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095
 
 		// band 1
-		target += coeff[ 1 ] * 2.0 * 0.511664 * y; // ( 2 * π / 3 ) * 0.488603
-		target += coeff[ 2 ] * 2.0 * 0.511664 * z;
-		target += coeff[ 3 ] * 2.0 * 0.511664 * x;
+		target.addScale( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603
+		target.addScale( coeff[ 2 ], 2.0 * 0.511664 * z );
+		target.addScale( coeff[ 3 ], 2.0 * 0.511664 * x );
 
 		// band 2
-		target += coeff[ 4 ] * 2.0 * 0.429043 * x * y; // ( π / 4 ) * 1.092548
-		target += coeff[ 5 ] * 2.0 * 0.429043 * y * z;
-		target += coeff[ 6 ] * ( 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3
-		target += coeff[ 7 ] * 2.0 * 0.429043 * x * z;
-		target += coeff[ 8 ] * 0.429043 * ( x * x - y * y ); // ( π / 4 ) * 0.546274
+		target.addScale( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548
+		target.addScale( coeff[ 5 ], 2.0 * 0.429043 * y * z );
+		target.addScale( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3
+		target.addScale( coeff[ 7 ], 2.0 * 0.429043 * x * z );
+		target.addScale( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274
 
 		return target;
 
@@ -44497,11 +44575,13 @@ Object.assign( Raycaster.prototype, {
 
 			this.ray.origin.setFromMatrixPosition( camera.matrixWorld );
 			this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();
+			this._camera = camera;
 
 		} else if ( ( camera && camera.isOrthographicCamera ) ) {
 
 			this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera
 			this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );
+			this._camera = camera;
 
 		} else {
 

+ 4 - 29
docs/api/en/math/Euler.html

@@ -58,29 +58,21 @@
 			to the <em>local</em> coordinate system. That is, for order 'XYZ', the rotation is first around the local-X
 			axis (which is the same as the world-X axis), then around local-Y (which may now be different from the
 			world Y-axis), then local-Z (which may be different from the world Z-axis).<br /><br />
-
-			If the order is changed, [page:.onChangeCallback onChangeCallback] will be called.
 		</p>
 
 		<h3>[property:Float x]</h3>
 		<p>
 			The current value of the x component.<br /><br />
-
-			If this is changed, [page:.onChangeCallback onChangeCallback] will be called.
 		</p>
 
 		<h3>[property:Float y]</h3>
 		<p>
 			The current value of the y component.<br /><br />
-
-			If this is changed, [page:.onChangeCallback onChangeCallback] will be called.
 		</p>
 
 		<h3>[property:Float z]</h3>
 		<p>
 			The current value of the z component.<br /><br />
-
-			If this is changed, [page:.onChangeCallback onChangeCallback] will be called.
 		</p>
 
 		<h2>Methods</h2>
@@ -104,18 +96,6 @@
 		Optionally assigns this euler's [page:.order order] to array[3].
 		</p>
 
-		<h3>[method:Euler onChange]( [param:Function onChangeCallback] )</h3>
-		<p>
-			[page:Function onChangeCallback] - set the value of the onChangeCallback() function.
-		</p>
-
-		<h3>[method:Euler onChangeCallback](  )</h3>
-		<p>
-			By default this is an empty function, however it can be set via [page:.onChange onChange]().<br />
-			It gets called after changing the [page:.x x], [page:.y y], [page:.z z] or [page:.order order] properties,
-			and also after calling most setter functions (see those for details).
-		</p>
-
 		<h3>[method:Euler reorder]( [param:String newOrder] )</h3>
 		<p>
 		Resets the euler angle with a new order by creating a quaternion from this euler angle
@@ -131,27 +111,23 @@
 			[page:.z z] - the angle of the z axis in radians.<br />
 			[page:.order order] - (optional) a string representing the order that the rotations are applied.<br /><br />
 
-			Sets the angles of this euler transform and optionally the [page:.order order] and then call [page:.onChangeCallback onChangeCallback]().
+			Sets the angles of this euler transform and optionally the [page:.order order].
 		</p>
 
-		<h3>[method:Euler setFromRotationMatrix]( [param:Matrix4 m], [param:String order], [param:Boolean update] )</h3>
+		<h3>[method:Euler setFromRotationMatrix]( [param:Matrix4 m], [param:String order] )</h3>
 		<p>
 		[page:Matrix4 m] - a [page:Matrix4] of which the upper 3x3 of matrix is a pure
 		[link:https://en.wikipedia.org/wiki/Rotation_matrix rotation matrix] (i.e. unscaled).<br />
 		[page:.order order] - (optional) a string representing the order that the rotations are applied.<br />
-		[page:Boolean update] - (optional) whether to call [page:.onChangeCallback onChangeCallback]() after applying
-		the matrix.<br /><br />
 
 		Sets the angles of this euler transform from a pure rotation matrix based on the orientation
 		specified by order.
 		</p>
 
-		<h3>[method:Euler setFromQuaternion]( [param:Quaternion q], [param:String order], [param:Boolean update] )</h3>
+		<h3>[method:Euler setFromQuaternion]( [param:Quaternion q], [param:String order] )</h3>
 		<p>
 		[page:Quaternion q] - a normalized quaternion.<br />
 		[page:.order order] - (optional) a string representing the order that the rotations are applied.<br />
-		[page:Boolean update] - (optional) whether to call [page:.onChangeCallback onChangeCallback]() after applying
-		the matrix.<br /><br />
 
 		Sets the angles of this euler transform from a normalized quaternion based on the orientation
 		specified by [page:.order order].
@@ -163,8 +139,7 @@
 		[page:Vector3 vector] - [page:Vector3].<br />
 		[page:.order order] - (optional) a string representing the order that the rotations are applied.<br /><br />
 
-		Set the [page:.x x], [page:.y y] and [page:.z z], and optionally update the [page:.order order]. [page:.onChangeCallback onChangeCallback]()
-		is called after these changes are made.
+		Set the [page:.x x], [page:.y y] and [page:.z z], and optionally update the [page:.order order].
 		</p>
 
 

+ 1 - 32
docs/api/en/math/Quaternion.html

@@ -52,16 +52,12 @@
 		</p>
 
 		<h3>[property:Float x]</h3>
-		<p>Changing this property will result in [page:.onChangeCallback onChangeCallback] being called.</p>
 
 		<h3>[property:Float y]</h3>
-		<p>Changing this property will result in [page:.onChangeCallback onChangeCallback] being called.</p>
 
 		<h3>[property:Float z]</h3>
-		<p>Changing this property will result in [page:.onChangeCallback onChangeCallback] being called.</p>
 
 		<h3>[property:Float w]</h3>
-		<p>Changing this property will result in [page:.onChangeCallback onChangeCallback] being called.</p>
 
 
 		<h2>Methods</h2>
@@ -115,8 +111,7 @@
 
 		<h3>[method:Quaternion inverse]()</h3>
 		<p>
-			Inverts this quaternion - calculate the [page:.conjugate conjugate] and then
-			[page:.normalize normalizes] the result.
+			Inverts this quaternion - calculates the [page:.conjugate conjugate]. The quaternion is assumed to have unit length.
 		</p>
 
 		<h3>[method:Float length]()</h3>
@@ -147,32 +142,6 @@
 		Adapted from the method outlined [link:http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm here].
 		</p>
 
-		<h3>[method:Quaternion onChange]( [param:Function onChangeCallback] )</h3>
-		<p>Sets the [page:.onChangeCallback onChangeCallback]() method.</p>
-
-		<h3>[method:Quaternion onChangeCallback]( )</h3>
-		<p>
-			This function is called whenever any of the following occurs:
-			<ul>
-				<li>
-					The [page:.x x], [page:.y y], [page:.z z] or
-				  [page:.w w] properties are changed.
-				</li>
-				<li>
-					The [page:.set set](), [page:.copy copy](), [page:.clone clone](),
-					[page:.setFromAxisAngle setFromAxisAngle](),  [page:.setFromRotationMatrix setFromRotationMatrix](),
-					[page:.conjugate conjugate](), [page:.normalize normalize](),
-					[page:.multiplyQuaternions multiplyQuaternions](), [page:.slerp slerp]() or [page:.fromArray fromArray]()
-					functions are called.
-				</li>
-				<li>
-					[page:.setFromEuler setFromEuler]() function is called with its *update* argument set to true.
-				</li>
-			</ul>
-			By default it is the empty function, however you can change it if needed using [page:.onChange onChange]( [page:Function onChangeCallback] ).
-		</p>
-
-
 		<h3>[method:Quaternion premultiply]( [param:Quaternion q] )</h3>
 		<p>Pre-multiplies this quaternion by [page:Quaternion q].</p>
 

+ 6 - 0
docs/api/en/math/Vector4.html

@@ -78,6 +78,12 @@ var d = a.dot( b );
 
 		<h3>[property:Float w]</h3>
 
+		<h3>[property:Float width]</h3>
+		<p>Alias for [page:.z z].</p>
+
+		<h3>[property:Float height]</h3>
+		<p>Alias for [page:.w w].</p>
+
 
 		<h2>Methods</h2>
 

+ 3 - 0
docs/examples/loaders/BasisTextureLoader.html

@@ -60,6 +60,9 @@
 			desktop, Android, and iOS devices, and transcoded into DXT, ETC1, or
 			PVRTC1. Other output formats may be supported in the future.
 		</p>
+		<p>
+			Transcoding to PVRTC1 (for iOS) requires square power-of-two textures.
+		</p>
 		<p>
 			This loader relies on ES6 Promises and Web Assembly, which are not
 			supported in IE11.

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

@@ -66,7 +66,7 @@
 		<p>
 			The core of three.js is focused on the most important components of a 3D engine. Many other components like loaders or controls are part of the
 			examples directory. three.js ensures that these files are kept in sync with the core but users have to import them separately if they are required
-			for their project. However, most of these files are not modules which makes their usage in certain cases inconvenient. In order to address this issue,
+			for their project. However, not all files are modules which makes their usage in certain cases inconvenient. In order to address this issue,
 			we are working to provide all the examples as modules in the [link:https://github.com/mrdoob/three.js/tree/master/examples/jsm examples/jsm] directory.
 			If you install three.js via npm, you can import them like so:
 		</p>
@@ -76,6 +76,15 @@
 		<p>
 			The following examples files are already available as modules:
 			<ul>
+				<li>animation
+					<ul>
+						<li>AnimationClipCreator</li>
+						<li>CCDIKSolver</li>
+						<li>MMDAnimationHelper</li>
+						<li>MMDPhysics</li>
+						<li>TimelinerController</li>
+					</ul>
+				</li>
 				<li>cameras
 					<ul>
 						<li>CinematicCamera</li>
@@ -117,6 +126,7 @@
 				<li>exporters
 					<ul>
 						<li>ColladaExporter</li>
+						<li>DRACOExporter</li>
 						<li>GLTFExporter</li>
 						<li>MMDExporter</li>
 						<li>OBJExporter</li>
@@ -140,6 +150,11 @@
 						<li>SelectionHelper</li>
 					</ul>
 				</li>
+				<li>lights
+					<ul>
+						<li>LightProbeGenerator</li>
+					</ul>
+				</li>
 				<li>lines
 					<ul>
 						<li>Line2</li>
@@ -155,12 +170,14 @@
 					<ul>
 						<li>3MFLoader</li>
 						<li>AMFLoader</li>
+						<li>AWDLoader</li>
 						<li>AssimpJSONLoader</li>
 						<li>AssimpLoader</li>
 						<li>BabylonLoader</li>
 						<li>BVHLoader</li>
 						<li>ColladaLoader</li>
 						<li>DDSLoader</li>
+						<li>DRACOLoader</li>
 						<li>EXRLoader</li>
 						<li>FBXLoader</li>
 						<li>GCodeLoader</li>
@@ -169,7 +186,10 @@
 						<li>KMZLoader</li>
 						<li>KTXLoader</li>
 						<li>LWOLoader</li>
+						<li>MD2Loader</li>
+						<li>MMDLoader</li>
 						<li>MTLLoader</li>
+						<li>NRRDLoader</li>
 						<li>OBJLoader</li>
 						<li>PCDLoader</li>
 						<li>PDBLoader</li>
@@ -182,7 +202,10 @@
 						<li>SVGLoader</li>
 						<li>TDSLoader</li>
 						<li>TGALoader</li>
+						<li>TTFLoader</li>
 						<li>VRMLLoader</li>
+						<li>VTKLoader</li>
+						<li>XLoader</li>
 					</ul>
 				</li>
 				<li>math
@@ -194,6 +217,22 @@
 						<li>SimplexNoise</li>
 					</ul>
 				</li>
+				<li>misc
+					<ul>
+						<li>CarControls</li>
+						<li>ConvexObjectBreaker</li>
+						<li>GPUComputationRenderer</li>
+						<li>Gyroscope</li>
+						<li>MD2Character</li>
+						<li>MD2CharacterComplex</li>
+						<li>MorphAnimMesh</li>
+						<li>MorphBlendMesh</li>
+						<li>Ocean</li>
+						<li>RollerCoaster</li>
+						<li>Volume</li>
+						<li>VolumeSlice</li>
+					</ul>
+				</li>
 				<li>modifiers
 					<ul>
 						<li>ExplodeModifier</li>
@@ -292,6 +331,7 @@
 						<li>LuminosityShader</li>
 						<li>MirrorShader</li>
 						<li>NormalMapShader</li>
+						<li>OceanShaders</li>
 						<li>ParallaxShader</li>
 						<li>PixelShader</li>
 						<li>RGBShiftShader</li>
@@ -311,6 +351,7 @@
 						<li>VerticalBlurShader</li>
 						<li>VerticalTiltShiftShader</li>
 						<li>VignetteShader</li>
+						<li>VolumeShader</li>
 						<li>WaterRefractionShader</li>
 					</ul>
 				</li>

+ 36 - 18
examples/index.html

@@ -50,11 +50,17 @@
 		<script>
 
 		function extractQuery() {
+
 			var p = window.location.search.indexOf( '?q=' );
-			if( p !== -1 ) {
+
+			if ( p !== - 1 ) {
+
 				return window.location.search.substr( 3 );
+
 			}
-			return ''
+
+			return '';
+
 		}
 
 		var panel = document.getElementById( 'panel' );
@@ -66,8 +72,10 @@
 
 		var expandButton = document.getElementById( 'expandButton' );
 		expandButton.addEventListener( 'click', function ( event ) {
+
 			event.preventDefault();
 			panel.classList.toggle( 'open' );
+
 		} );
 
 		var panelScrim = document.getElementById( 'panelScrim' );
@@ -174,27 +182,29 @@
 		// filter
 		filterInput.onfocus = function ( event ) {
 
-			panel.classList.add('searchFocused');
+			panel.classList.add( 'searchFocused' );
 
-		}
+		};
 
 		filterInput.onblur = function ( event ) {
 
-			if(filterInput.value === '') {
-				panel.classList.remove('searchFocused');
+			if ( filterInput.value === '' ) {
+
+				panel.classList.remove( 'searchFocused' );
+
 			}
 
-		}
+		};
 
-		exitSearchButton.onclick = function( event ) {
+		exitSearchButton.onclick = function ( event ) {
 
 			filterInput.value = '';
 			updateFilter();
-			panel.classList.remove('searchFocused');
+			panel.classList.remove( 'searchFocused' );
 
-		}
+		};
 
-		filterInput.addEventListener( 'input', function( e ) {
+		filterInput.addEventListener( 'input', function () {
 
 			updateFilter();
 
@@ -203,10 +213,15 @@
 		function updateFilter() {
 
 			var v = filterInput.value;
-			if( v !== '' ) {
-				window.history.replaceState( {} , '', '?q=' + v + window.location.hash );
+
+			if ( v !== '' ) {
+
+				window.history.replaceState( {}, '', '?q=' + v + window.location.hash );
+
 			} else {
-				window.history.replaceState( {} , '', window.location.pathname + window.location.hash );
+
+				window.history.replaceState( {}, '', window.location.pathname + window.location.hash );
+
 			}
 
 			var exp = new RegExp( v, 'gi' );
@@ -227,7 +242,7 @@
 
 		}
 
-		function filterExample( file, exp ){
+		function filterExample( file, exp ) {
 
 			var link = links[ file ];
 			var name = getName( file );
@@ -238,8 +253,10 @@
 
 				link.classList.remove( 'hidden' );
 
-				for( var i = 0; i < res.length; i++ ) {
+				for ( var i = 0; i < res.length; i ++ ) {
+
 					text = name.replace( res[ i ], '<b>' + res[ i ] + '</b>' );
+
 				}
 
 				link.innerHTML = text;
@@ -250,13 +267,14 @@
 				link.innerHTML = name;
 
 			}
+
 		}
 
 		function getName( file ) {
 
 			var name = file.split( '_' );
 			name.shift();
-			return name.join( '<span class="spacer">/</span>' );
+			return name.join( ' / ' );
 
 		}
 
@@ -272,7 +290,7 @@
 
 					var file = section[ i ];
 
-					if ( links[ file ].classList.contains( 'hidden' ) === false ){
+					if ( links[ file ].classList.contains( 'hidden' ) === false ) {
 
 						collapsed = false;
 						break;

+ 5 - 5
examples/js/animation/CCDIKSolver.js

@@ -65,7 +65,7 @@ THREE.CCDIKSolver = ( function () {
 				// for reference overhead reduction in loop
 				var math = Math;
 
-				for ( var i = 0, il = iks.length; i < il; i++ ) {
+				for ( var i = 0, il = iks.length; i < il; i ++ ) {
 
 					var ik = iks[ i ];
 					var effector = bones[ ik.effector ];
@@ -78,11 +78,11 @@ THREE.CCDIKSolver = ( function () {
 					var links = ik.links;
 					var iteration = ik.iteration !== undefined ? ik.iteration : 1;
 
-					for ( var j = 0; j < iteration; j++ ) {
+					for ( var j = 0; j < iteration; j ++ ) {
 
 						var rotated = false;
 
-						for ( var k = 0, kl = links.length; k < kl; k++ ) {
+						for ( var k = 0, kl = links.length; k < kl; k ++ ) {
 
 							var link = bones[ links[ k ].index ];
 
@@ -115,9 +115,9 @@ THREE.CCDIKSolver = ( function () {
 
 								angle = 1.0;
 
-							} else if ( angle < -1.0 ) {
+							} else if ( angle < - 1.0 ) {
 
-								angle = -1.0;
+								angle = - 1.0;
 
 							}
 

+ 5 - 5
examples/js/animation/MMDAnimationHelper.js

@@ -19,7 +19,7 @@ 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.
+	 * @param {boolean} params.resetPhysicsOnLoop - Default is true.
 	 */
 	function MMDAnimationHelper( params ) {
 
@@ -53,7 +53,7 @@ THREE.MMDAnimationHelper = ( function () {
 			cameraAnimation: true
 		};
 
-		this.onBeforePhysics = function ( mesh ) {};
+		this.onBeforePhysics = function ( /* mesh */ ) {};
 
 		// experimental
 		this.sharedPhysics = false;
@@ -239,7 +239,7 @@ THREE.MMDAnimationHelper = ( function () {
 		 * Enabes/Disables an animation feature.
 		 *
 		 * @param {string} key
-		 * @param {boolean} enebled
+		 * @param {boolean} enabled
 		 * @return {THREE.MMDAnimationHelper}
 		 */
 		enable: function ( key, enabled ) {
@@ -488,7 +488,7 @@ THREE.MMDAnimationHelper = ( function () {
 
 				var masterPhysics = this._getMasterPhysics();
 
-				if ( masterPhysics !== null ) world = masterPhysics.world;
+				if ( masterPhysics !== null ) world = masterPhysics.world; // eslint-disable-line no-undef
 
 			}
 
@@ -945,7 +945,7 @@ THREE.MMDAnimationHelper = ( function () {
 			}
 
 			if ( this.currentTime < this.delayTime ) return false;
-			
+
 			// 'duration' can be bigger than 'audioDuration + delayTime' because of sync configuration
 			if ( ( this.currentTime - this.delayTime ) > this.audioDuration ) return false;
 

+ 32 - 30
examples/js/animation/MMDPhysics.js

@@ -11,6 +11,8 @@
  *  - Physics in Worker
  */
 
+/* global Ammo */
+
 THREE.MMDPhysics = ( function () {
 
 	/**
@@ -137,7 +139,7 @@ THREE.MMDPhysics = ( function () {
 		 */
 		reset: function () {
 
-			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
 
 				this.bodies[ i ].reset();
 
@@ -155,7 +157,7 @@ THREE.MMDPhysics = ( function () {
 		 */
 		warmup: function ( cycles ) {
 
-			for ( var i = 0; i < cycles; i++ ) {
+			for ( var i = 0; i < cycles; i ++ ) {
 
 				this.update( 1 / 60 );
 
@@ -258,7 +260,7 @@ THREE.MMDPhysics = ( function () {
 
 		_initRigidBodies: function ( rigidBodies ) {
 
-			for ( var i = 0, il = rigidBodies.length; i < il; i++ ) {
+			for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
 
 				this.bodies.push( new RigidBody(
 					this.mesh, this.world, rigidBodies[ i ], this.manager ) );
@@ -269,7 +271,7 @@ THREE.MMDPhysics = ( function () {
 
 		_initConstraints: function ( constraints ) {
 
-			for ( var i = 0, il = constraints.length; i < il; i++ ) {
+			for ( var i = 0, il = constraints.length; i < il; i ++ ) {
 
 				var params = constraints[ i ];
 				var bodyA = this.bodies[ params.rigidBodyIndex1 ];
@@ -307,7 +309,7 @@ THREE.MMDPhysics = ( function () {
 
 		_updateRigidBodies: function () {
 
-			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
 
 				this.bodies[ i ].updateFromBone();
 
@@ -317,7 +319,7 @@ THREE.MMDPhysics = ( function () {
 
 		_updateBones: function () {
 
-			for ( var i = 0, il = this.bodies.length; i < il; i++ ) {
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
 
 				this.bodies[ i ].updateBone();
 
@@ -476,32 +478,32 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		getOrigin: function( t ) {
+		getOrigin: function ( t ) {
 
 			return t.getOrigin();
 
 		},
 
-		setOrigin: function( t, v ) {
+		setOrigin: function ( t, v ) {
 
 			t.getOrigin().setValue( v.x(), v.y(), v.z() );
 
 		},
 
-		copyOrigin: function( t1, t2 ) {
+		copyOrigin: function ( t1, t2 ) {
 
 			var o = t2.getOrigin();
 			this.setOrigin( t1, o );
 
 		},
 
-		setBasis: function( t, q ) {
+		setBasis: function ( t, q ) {
 
 			t.setRotation( q );
 
 		},
 
-		setBasisFromMatrix3: function( t, m ) {
+		setBasisFromMatrix3: function ( t, m ) {
 
 			var q = this.matrix3ToQuaternion( m );
 			this.setBasis( t, q );
@@ -626,7 +628,7 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		addVector3: function( v1, v2 ) {
+		addVector3: function ( v1, v2 ) {
 
 			var v = this.allocVector3();
 			v.setValue( v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z() );
@@ -634,13 +636,13 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		dotVectors3: function( v1, v2 ) {
+		dotVectors3: function ( v1, v2 ) {
 
 			return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
 
 		},
 
-		rowOfMatrix3: function( m, i ) {
+		rowOfMatrix3: function ( m, i ) {
 
 			var v = this.allocVector3();
 			v.setValue( m[ i * 3 + 0 ], m[ i * 3 + 1 ], m[ i * 3 + 2 ] );
@@ -648,7 +650,7 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		columnOfMatrix3: function( m, i ) {
+		columnOfMatrix3: function ( m, i ) {
 
 			var v = this.allocVector3();
 			v.setValue( m[ i + 0 ], m[ i + 3 ], m[ i + 6 ] );
@@ -656,10 +658,10 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		negativeVector3: function( v ) {
+		negativeVector3: function ( v ) {
 
 			var v2 = this.allocVector3();
-			v2.setValue( -v.x(), -v.y(), -v.z() );
+			v2.setValue( - v.x(), - v.y(), - v.z() );
 			return v2;
 
 		},
@@ -685,7 +687,7 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		transposeMatrix3: function( m ) {
+		transposeMatrix3: function ( m ) {
 
 			var m2 = [];
 			m2[ 0 ] = m[ 0 ];
@@ -736,12 +738,12 @@ THREE.MMDPhysics = ( function () {
 
 		},
 
-		matrix3ToQuaternion: function( m ) {
+		matrix3ToQuaternion: function ( m ) {
 
 			var t = m[ 0 ] + m[ 4 ] + m[ 8 ];
 			var s, x, y, z, w;
 
-			if( t > 0 ) {
+			if ( t > 0 ) {
 
 				s = Math.sqrt( t + 1.0 ) * 2;
 				w = 0.25 * s;
@@ -749,7 +751,7 @@ THREE.MMDPhysics = ( function () {
 				y = ( m[ 2 ] - m[ 6 ] ) / s;
 				z = ( m[ 3 ] - m[ 1 ] ) / s;
 
-			} else if( ( m[ 0 ] > m[ 4 ] ) && ( m[ 0 ] > m[ 8 ] ) ) {
+			} else if ( ( m[ 0 ] > m[ 4 ] ) && ( m[ 0 ] > m[ 8 ] ) ) {
 
 				s = Math.sqrt( 1.0 + m[ 0 ] - m[ 4 ] - m[ 8 ] ) * 2;
 				w = ( m[ 7 ] - m[ 5 ] ) / s;
@@ -757,7 +759,7 @@ THREE.MMDPhysics = ( function () {
 				y = ( m[ 1 ] + m[ 3 ] ) / s;
 				z = ( m[ 2 ] + m[ 6 ] ) / s;
 
-			} else if( m[ 4 ] > m[ 8 ] ) {
+			} else if ( m[ 4 ] > m[ 8 ] ) {
 
 				s = Math.sqrt( 1.0 + m[ 4 ] - m[ 0 ] - m[ 8 ] ) * 2;
 				w = ( m[ 2 ] - m[ 6 ] ) / s;
@@ -794,7 +796,7 @@ THREE.MMDPhysics = ( function () {
 	 */
 	function RigidBody( mesh, world, params, manager ) {
 
-		this.mesh  = mesh;
+		this.mesh = mesh;
 		this.world = world;
 		this.params = params;
 		this.manager = manager;
@@ -882,7 +884,7 @@ THREE.MMDPhysics = ( function () {
 
 			function generateShape( p ) {
 
-				switch( p.shapeType ) {
+				switch ( p.shapeType ) {
 
 					case 0:
 						return new Ammo.btSphereShape( p.width );
@@ -912,7 +914,7 @@ THREE.MMDPhysics = ( function () {
 			var localInertia = manager.allocVector3();
 			localInertia.setValue( 0, 0, 0 );
 
-			if( weight !== 0 ) {
+			if ( weight !== 0 ) {
 
 				shape.calculateLocalInertia( weight, localInertia );
 
@@ -1103,7 +1105,7 @@ THREE.MMDPhysics = ( function () {
 	 */
 	function Constraint( mesh, world, bodyA, bodyB, params, manager ) {
 
-		this.mesh  = mesh;
+		this.mesh = mesh;
 		this.world = world;
 		this.bodyA = bodyA;
 		this.bodyB = bodyB;
@@ -1171,9 +1173,9 @@ THREE.MMDPhysics = ( function () {
 			constraint.setAngularLowerLimit( all );
 			constraint.setAngularUpperLimit( aul );
 
-			for ( var i = 0; i < 3; i++ ) {
+			for ( var i = 0; i < 3; i ++ ) {
 
-				if( params.springPosition[ i ] !== 0 ) {
+				if ( params.springPosition[ i ] !== 0 ) {
 
 					constraint.enableSpring( i, true );
 					constraint.setStiffness( i, params.springPosition[ i ] );
@@ -1182,9 +1184,9 @@ THREE.MMDPhysics = ( function () {
 
 			}
 
-			for ( var i = 0; i < 3; i++ ) {
+			for ( var i = 0; i < 3; i ++ ) {
 
-				if( params.springRotation[ i ] !== 0 ) {
+				if ( params.springRotation[ i ] !== 0 ) {
 
 					constraint.enableSpring( i + 3, true );
 					constraint.setStiffness( i + 3, params.springRotation[ i ] );

+ 5 - 3
examples/js/exporters/ColladaExporter.js

@@ -17,7 +17,9 @@ THREE.ColladaExporter.prototype = {
 
 	constructor: THREE.ColladaExporter,
 
-	parse: function ( object, onDone, options = {} ) {
+	parse: function ( object, onDone, options ) {
+
+		options = options || {};
 
 		options = Object.assign( {
 			version: '1.4.1',
@@ -377,7 +379,7 @@ THREE.ColladaExporter.prototype = {
 				var reflectivity = m.reflectivity || 0;
 
 				// Do not export and alpha map for the reasons mentioned in issue (#13792)
-				// in THREE.js alpha maps are black and white, but collada expects the alpha
+				// in three.js alpha maps are black and white, but collada expects the alpha
 				// channel to specify the transparency
 				var transparencyNode = '';
 				if ( m.transparent === true ) {
@@ -586,7 +588,7 @@ THREE.ColladaExporter.prototype = {
 			'<asset>' +
 			(
 				'<contributor>' +
-				'<authoring_tool>THREE.js Collada Exporter</authoring_tool>' +
+				'<authoring_tool>three.js Collada Exporter</authoring_tool>' +
 				( options.author !== null ? `<author>${ options.author }</author>` : '' ) +
 				'</contributor>' +
 				`<created>${ ( new Date() ).toISOString() }</created>` +

+ 3 - 3
examples/js/exporters/DracoExporter.js

@@ -1,5 +1,3 @@
-'use strict';
-
 /**
  * Export draco compressed files from threejs geometry objects.
  *
@@ -17,6 +15,8 @@
  * @author tentone
  */
 
+/* global DracoEncoderModule */
+
 THREE.DRACOExporter = function () {};
 
 THREE.DRACOExporter.prototype = {
@@ -78,7 +78,7 @@ THREE.DRACOExporter.prototype = {
 
 		} else {
 
-			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array ) ( vertices.count );
+			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
 
 			for ( var i = 0; i < faces.length; i ++ ) {
 

文件差异内容过多而无法显示
+ 1 - 0
examples/js/libs/chevrotain.min.js


+ 0 - 0
examples/js/loaders/ctm/ctm.js → examples/js/libs/ctm.js


+ 1 - 1
examples/js/loaders/ctm/lzma.js → examples/js/libs/lzma.js

@@ -98,7 +98,7 @@ LZMA.RangeDecoder.prototype.init = function() {
 
 	this._code = 0;
 	this._range = -1;
-  
+
 	while (i --) {
 		this._code = (this._code << 8) | this._stream.readByte();
 	}

+ 42 - 63
examples/js/loaders/AWDLoader.js

@@ -3,11 +3,11 @@
  * Date: 09/12/2013 17:21
  */
 
-( function () {
+THREE.AWDLoader = ( function () {
 
-	var UNCOMPRESSED = 0,
-		DEFLATE = 1,
-		LZMA = 2,
+	var //UNCOMPRESSED = 0,
+		//DEFLATE = 1,
+		//LZMA = 2,
 
 		AWD_FIELD_INT8 = 1,
 		AWD_FIELD_INT16 = 2,
@@ -18,10 +18,10 @@
 		AWD_FIELD_FLOAT32 = 7,
 		AWD_FIELD_FLOAT64 = 8,
 		AWD_FIELD_BOOL = 21,
-		AWD_FIELD_COLOR = 22,
+		//AWD_FIELD_COLOR = 22,
 		AWD_FIELD_BADDR = 23,
-		AWD_FIELD_STRING = 31,
-		AWD_FIELD_BYTEARRAY = 32,
+		//AWD_FIELD_STRING = 31,
+		//AWD_FIELD_BYTEARRAY = 32,
 		AWD_FIELD_VECTOR2x1 = 41,
 		AWD_FIELD_VECTOR3x1 = 42,
 		AWD_FIELD_VECTOR4x1 = 43,
@@ -31,15 +31,15 @@
 		AWD_FIELD_MTX4x4 = 47,
 
 		BOOL = 21,
-		COLOR = 22,
+		//COLOR = 22,
 		BADDR = 23,
 
-		INT8 = 1,
-		INT16 = 2,
-		INT32 = 3,
+		//INT8 = 1,
+		//INT16 = 2,
+		//INT32 = 3,
 		UINT8 = 4,
 		UINT16 = 5,
-		UINT32 = 6,
+		//UINT32 = 6,
 		FLOAT32 = 7,
 		FLOAT64 = 8;
 
@@ -76,7 +76,7 @@
 		}
 	};
 
-	THREE.AWDLoader = function ( manager ) {
+	var AWDLoader = function ( manager ) {
 
 		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
@@ -104,9 +104,9 @@
 
 	};
 
-	THREE.AWDLoader.prototype = {
+	AWDLoader.prototype = {
 
-		constructor: THREE.AWDLoader,
+		constructor: AWDLoader,
 
 		load: function ( url, onLoad, onProgress, onError ) {
 
@@ -178,57 +178,49 @@
 			switch ( type ) {
 
 				case 1:
-					assetData = this.parseMeshData( len );
+					assetData = this.parseMeshData();
 					break;
 
 				case 22:
-					assetData = this.parseContainer( len );
+					assetData = this.parseContainer();
 					break;
 
 				case 23:
-					assetData = this.parseMeshInstance( len );
+					assetData = this.parseMeshInstance();
 					break;
 
 				case 81:
-					assetData = this.parseMaterial( len );
+					assetData = this.parseMaterial();
 					break;
 
 				case 82:
-					assetData = this.parseTexture( len );
+					assetData = this.parseTexture();
 					break;
 
 				case 101:
-					assetData = this.parseSkeleton( len );
+					assetData = this.parseSkeleton();
 					break;
 
-	    //  case 111:
-	    //    assetData = this.parseMeshPoseAnimation(len, true);
-	    //    break;
-
 				case 112:
-					assetData = this.parseMeshPoseAnimation( len, false );
+					assetData = this.parseMeshPoseAnimation( false );
 					break;
 
 				case 113:
-					assetData = this.parseVertexAnimationSet( len );
+					assetData = this.parseVertexAnimationSet();
 					break;
 
 				case 102:
-					assetData = this.parseSkeletonPose( len );
+					assetData = this.parseSkeletonPose();
 					break;
 
 				case 103:
-					assetData = this.parseSkeletonAnimation( len );
+					assetData = this.parseSkeletonAnimation();
 					break;
 
 				case 122:
-					assetData = this.parseAnimatorSet( len );
+					assetData = this.parseAnimatorSet();
 					break;
 
-				// case 121:
-				//  assetData = parseUVAnimation(len);
-				//  break;
-
 				default:
 					//debug('Ignoring block!',type, len);
 					this._ptr += len;
@@ -279,7 +271,7 @@
 
 		},
 
-		parseContainer: function ( len ) {
+		parseContainer: function () {
 
 			var parent,
 				ctr = new THREE.Object3D(),
@@ -305,7 +297,7 @@
 
 		},
 
-		parseMeshInstance: function ( len ) {
+		parseMeshInstance: function () {
 
 			var name,
 				mesh, geometries, meshLen, meshes,
@@ -379,14 +371,13 @@
 
 		},
 
-		parseMaterial: function ( len ) {
+		parseMaterial: function () {
 
 			var name,
 				type,
 				props,
 				mat,
 				attributes,
-				finalize,
 				num_methods,
 				methods_parsed;
 
@@ -449,7 +440,7 @@
 
 		},
 
-		parseTexture: function ( len ) {
+		parseTexture: function () {
 
 			var name = this.readUTF(),
 				type = this.readU8(),
@@ -493,7 +484,7 @@
 
 		},
 
-		parseSkeleton: function ( len ) {
+		parseSkeleton: function () {
 
 			// Array<Bone>
 			var name = this.readUTF(),
@@ -534,7 +525,7 @@
 
 		},
 
-		parseSkeletonPose: function ( blockID ) {
+		parseSkeletonPose: function () {
 
 			var name = this.readUTF();
 
@@ -549,8 +540,6 @@
 
 			while ( joints_parsed < num_joints ) {
 
-				var joint_pose;
-
 				var has_transform; //:uint;
 				var mtx_data;
 
@@ -577,7 +566,7 @@
 
 		},
 
-		parseSkeletonAnimation: function ( blockID ) {
+		parseSkeletonAnimation: function () {
 
 			var frame_dur;
 			var pose_addr;
@@ -591,7 +580,6 @@
 			this.parseProperties( null );
 
 			var frames_parsed = 0;
-			var returnedArray;
 
 			// debug( 'parse Skeleton Animation. frames : ' + num_frames);
 
@@ -623,7 +611,7 @@
 
 		},
 
-		parseVertexAnimationSet: function ( len ) {
+		parseVertexAnimationSet: function () {
 
 			var poseBlockAdress,
 				name = this.readUTF(),
@@ -647,14 +635,11 @@
 
 		},
 
-		parseAnimatorSet: function ( len ) {
-
-			var targetMesh;
+		parseAnimatorSet: function () {
 
 			var animSetBlockAdress; //:int
 
 			var targetAnimationSet; //:AnimationSetBase;
-			var outputString = ""; //:String = "";
 			var name = this.readUTF();
 			var type = this.readU16();
 
@@ -673,13 +658,12 @@
 			this.parseUserAttributes();
 			this.parseUserAttributes();
 
-			var returnedArray;
 			var targetMeshes = []; //:Vector.<Mesh> = new Vector.<Mesh>;
 
 			for ( i = 0; i < meshAdresses.length; i ++ ) {
 
-				//      returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
-				//      if (returnedArray[0])
+				//			returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
+				//			if (returnedArray[0])
 				targetMeshes.push( this._blocks[ meshAdresses[ i ] ].data );
 
 			}
@@ -711,14 +695,13 @@
 
 		},
 
-		parseMeshData: function ( len ) {
+		parseMeshData: function () {
 
 			var name = this.readUTF(),
 				num_subs = this.readU16(),
 				geom,
 				subs_parsed = 0,
 				buffer,
-				skinW, skinI,
 				geometries = [];
 
 			// Ignore for now
@@ -848,29 +831,23 @@
 
 		},
 
-		parseMeshPoseAnimation: function ( len, poseOnly ) {
+		parseMeshPoseAnimation: function ( poseOnly ) {
 
 			var num_frames = 1,
 				num_submeshes,
 				frames_parsed,
 				subMeshParsed,
-				frame_dur,
-				x, y, z,
 
 				str_len,
 				str_end,
 				geom,
-				subGeom,
 				idx = 0,
 				clip = {},
-				indices,
-				verts,
 				num_Streams,
 				streamsParsed,
 				streamtypes = [],
 
 				props,
-				thisGeo,
 				name = this.readUTF(),
 				geoAdress = this.readU32();
 
@@ -912,7 +889,7 @@
 
 			while ( frames_parsed < num_frames ) {
 
-				frame_dur = this.readU16();
+				this.readU16();
 				subMeshParsed = 0;
 
 				while ( subMeshParsed < num_submeshes ) {
@@ -1236,4 +1213,6 @@
 
 	};
 
+	return AWDLoader;
+
 } )();

+ 11 - 7
examples/js/loaders/AssimpLoader.js

@@ -538,8 +538,7 @@ THREE.AssimpLoader.prototype = {
 			for ( var i in root.children ) {
 
 				var child = cloneTreeToBones( root.children[ i ], scene );
-				if ( child )
-					rootBone.add( child );
+				rootBone.add( child );
 
 			}
 
@@ -1314,6 +1313,10 @@ THREE.AssimpLoader.prototype = {
 
 		function aiScene() {
 
+			this.versionMajor = 0;
+			this.versionMinor = 0;
+			this.versionRevision = 0;
+			this.compileFlags = 0;
 			this.mFlags = 0;
 			this.mNumMeshes = 0;
 			this.mNumMaterials = 0;
@@ -2232,13 +2235,13 @@ THREE.AssimpLoader.prototype = {
 			extendStream( stream );
 			stream.Seek( 44, aiOrigin_CUR ); // signature
 			/*unsigned int versionMajor =*/
-			var versionMajor = Read_unsigned_int( stream );
+			pScene.versionMajor = Read_unsigned_int( stream );
 			/*unsigned int versionMinor =*/
-			var versionMinor = Read_unsigned_int( stream );
+			pScene.versionMinor = Read_unsigned_int( stream );
 			/*unsigned int versionRevision =*/
-			var versionRevision = Read_unsigned_int( stream );
+			pScene.versionRevision = Read_unsigned_int( stream );
 			/*unsigned int compileFlags =*/
-			var compileFlags = Read_unsigned_int( stream );
+			pScene.compileFlags = Read_unsigned_int( stream );
 			shortened = Read_uint16_t( stream ) > 0;
 			compressed = Read_uint16_t( stream ) > 0;
 			if ( shortened )
@@ -2260,10 +2263,11 @@ THREE.AssimpLoader.prototype = {
 			} else {
 
 				ReadBinaryScene( stream, pScene );
-				return pScene.toTHREE();
 
 			}
 
+			return pScene.toTHREE();
+
 		}
 
 		return InternReadFile( buffer );

+ 7 - 1
examples/js/loaders/BasisTextureLoader.js

@@ -167,7 +167,7 @@ THREE.BasisTextureLoader.prototype = {
 
 				}
 
-				texture.minFilter = THREE.LinearMipMapLinearFilter;
+				texture.minFilter = mipmaps.length === 1 ? THREE.LinearFilter : THREE.LinearMipMapLinearFilter;
 				texture.magFilter = THREE.LinearFilter;
 				texture.generateMipmaps = false;
 				texture.needsUpdate = true;
@@ -449,6 +449,12 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		}
 
+		if ( basisFile.getHasAlpha() ) {
+
+			console.warn( 'THREE.BasisTextureLoader: Alpha not yet implemented.' );
+
+		}
+
 		var mipmaps = [];
 
 		for ( var mip = 0; mip < levels; mip ++ ) {

文件差异内容过多而无法显示
+ 576 - 404
examples/js/loaders/DRACOLoader.js


+ 57 - 8
examples/js/loaders/EXRLoader.js

@@ -76,11 +76,19 @@
 THREE.EXRLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+	this.type = THREE.FloatType;
 
 };
 
 THREE.EXRLoader.prototype = Object.create( THREE.DataTextureLoader.prototype );
 
+THREE.EXRLoader.prototype.setType = function ( value ) {
+
+	this.type = value;
+	return this;
+
+};
+
 THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 	const USHORT_RANGE = ( 1 << 16 );
@@ -1079,7 +1087,24 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 	var height = EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1;
 	var numChannels = EXRHeader.channels.length;
 
-	var byteArray = new Float32Array( width * height * numChannels );
+	switch ( this.type ) {
+
+		case THREE.FloatType:
+
+			var byteArray = new Float32Array( width * height * numChannels );
+			break;
+
+		case THREE.HalfFloatType:
+
+			var byteArray = new Uint16Array( width * height * numChannels );
+			break;
+
+		default:
+
+			console.error( 'THREE.EXRLoader: unsupported type: ', this.type );
+			break;
+
+	}
 
 	var channelOffsets = {
 		R: 0,
@@ -1099,12 +1124,23 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 				var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
 
-				if ( EXRHeader.channels[ channelID ].pixelType === 1 ) {
+				if ( EXRHeader.channels[ channelID ].pixelType === 1 ) { // half
 
-					// HALF
 					for ( var x = 0; x < width; x ++ ) {
 
-						var val = parseFloat16( bufferDataView, offset );
+						switch ( this.type ) {
+
+							case THREE.FloatType:
+
+								var val = parseFloat16( bufferDataView, offset );
+								break;
+
+							case THREE.HalfFloatType:
+
+								var val = parseUint16( bufferDataView, offset );
+								break;
+
+						}
 
 						byteArray[ ( ( ( height - y_scanline ) * ( width * numChannels ) ) + ( x * numChannels ) ) + cOff ] = val;
 
@@ -1139,12 +1175,25 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 
 					var cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
 
-					if ( EXRHeader.channels[ channelID ].pixelType === 1 ) {
+					if ( EXRHeader.channels[ channelID ].pixelType === 1 ) { // half
 
-						// HALF
 						for ( var x = 0; x < width; x ++ ) {
 
-							var val = decodeFloat16( tmpBuffer[ ( channelID * ( scanlineBlockSize * width ) ) + ( line_y * width ) + x ] );
+							var idx = ( channelID * ( scanlineBlockSize * width ) ) + ( line_y * width ) + x;
+
+							switch ( this.type ) {
+
+								case THREE.FloatType:
+
+									var val = decodeFloat16( tmpBuffer[ idx ] );
+									break;
+
+								case THREE.HalfFloatType:
+
+									var val = tmpBuffer[ idx ];
+									break;
+
+							}
 
 							var true_y = line_y + ( scanlineBlockIdx * scanlineBlockSize );
 
@@ -1176,7 +1225,7 @@ THREE.EXRLoader.prototype._parser = function ( buffer ) {
 		height: height,
 		data: byteArray,
 		format: EXRHeader.channels.length == 4 ? THREE.RGBAFormat : THREE.RGBFormat,
-		type: THREE.FloatType
+		type: this.type
 	};
 
 };

+ 5 - 81
examples/js/loaders/GLTFLoader.js

@@ -183,11 +183,11 @@ THREE.GLTFLoader = ( function () {
 							break;
 
 						case EXTENSIONS.KHR_MATERIALS_UNLIT:
-							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension( json );
+							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
 							break;
 
 						case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
-							extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension( json );
+							extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
 							break;
 
 						case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
@@ -199,7 +199,7 @@ THREE.GLTFLoader = ( function () {
 							break;
 
 						case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
-							extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension( json );
+							extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension();
 							break;
 
 						default:
@@ -419,8 +419,6 @@ THREE.GLTFLoader = ( function () {
 	};
 
 	/* BINARY EXTENSION */
-
-	var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
 	var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
 	var BINARY_EXTENSION_HEADER_LENGTH = 12;
 	var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
@@ -1105,17 +1103,6 @@ THREE.GLTFLoader = ( function () {
 		UNSIGNED_SHORT: 5123
 	};
 
-	var WEBGL_TYPE = {
-		5126: Number,
-		//35674: THREE.Matrix2,
-		35675: THREE.Matrix3,
-		35676: THREE.Matrix4,
-		35664: THREE.Vector2,
-		35665: THREE.Vector3,
-		35666: THREE.Vector4,
-		35678: THREE.Texture
-	};
-
 	var WEBGL_COMPONENT_TYPES = {
 		5120: Int8Array,
 		5121: Uint8Array,
@@ -1140,48 +1127,6 @@ THREE.GLTFLoader = ( function () {
 		10497: THREE.RepeatWrapping
 	};
 
-	var WEBGL_SIDES = {
-		1028: THREE.BackSide, // Culling front
-		1029: THREE.FrontSide // Culling back
-		//1032: THREE.NoSide   // Culling front and back, what to do?
-	};
-
-	var WEBGL_DEPTH_FUNCS = {
-		512: THREE.NeverDepth,
-		513: THREE.LessDepth,
-		514: THREE.EqualDepth,
-		515: THREE.LessEqualDepth,
-		516: THREE.GreaterEqualDepth,
-		517: THREE.NotEqualDepth,
-		518: THREE.GreaterEqualDepth,
-		519: THREE.AlwaysDepth
-	};
-
-	var WEBGL_BLEND_EQUATIONS = {
-		32774: THREE.AddEquation,
-		32778: THREE.SubtractEquation,
-		32779: THREE.ReverseSubtractEquation
-	};
-
-	var WEBGL_BLEND_FUNCS = {
-		0: THREE.ZeroFactor,
-		1: THREE.OneFactor,
-		768: THREE.SrcColorFactor,
-		769: THREE.OneMinusSrcColorFactor,
-		770: THREE.SrcAlphaFactor,
-		771: THREE.OneMinusSrcAlphaFactor,
-		772: THREE.DstAlphaFactor,
-		773: THREE.OneMinusDstAlphaFactor,
-		774: THREE.DstColorFactor,
-		775: THREE.OneMinusDstColorFactor,
-		776: THREE.SrcAlphaSaturateFactor
-		// The followings are not supported by Three.js yet
-		//32769: CONSTANT_COLOR,
-		//32770: ONE_MINUS_CONSTANT_COLOR,
-		//32771: CONSTANT_ALPHA,
-		//32772: ONE_MINUS_CONSTANT_COLOR
-	};
-
 	var WEBGL_TYPE_SIZES = {
 		'SCALAR': 1,
 		'VEC2': 2,
@@ -1217,15 +1162,6 @@ THREE.GLTFLoader = ( function () {
 		STEP: THREE.InterpolateDiscrete
 	};
 
-	var STATES_ENABLES = {
-		2884: 'CULL_FACE',
-		2929: 'DEPTH_TEST',
-		3042: 'BLEND',
-		3089: 'SCISSOR_TEST',
-		32823: 'POLYGON_OFFSET_FILL',
-		32926: 'SAMPLE_ALPHA_TO_COVERAGE'
-	};
-
 	var ALPHA_MODES = {
 		OPAQUE: 'OPAQUE',
 		MASK: 'MASK',
@@ -1518,19 +1454,6 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
-	}
-	function isObjectEqual( a, b ) {
-
-		if ( Object.keys( a ).length !== Object.keys( b ).length ) return false;
-
-		for ( var key in a ) {
-
-			if ( a[ key ] !== b[ key ] ) return false;
-
-		}
-
-		return true;
-
 	}
 
 	function createPrimitiveKey( primitiveDef ) {
@@ -1655,6 +1578,8 @@ THREE.GLTFLoader = ( function () {
 
 			addUnknownExtensionsToUserData( extensions, result, json );
 
+			assignExtrasToUserData( result, json );
+
 			onLoad( result );
 
 		} ).catch( onError );
@@ -2581,7 +2506,6 @@ THREE.GLTFLoader = ( function () {
 
 		var parser = this;
 		var json = this.json;
-		var extensions = this.extensions;
 
 		var meshDef = json.meshes[ meshIndex ];
 		var primitives = meshDef.primitives;

+ 63 - 127
examples/js/loaders/HDRCubeTextureLoader.js

@@ -6,101 +6,60 @@
 THREE.HDRCubeTextureLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
-	// override in sub classes
 	this.hdrLoader = new THREE.RGBELoader();
+	this.type = THREE.UnsignedByteType;
 
 };
 
-THREE.HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onProgress, onError ) {
+THREE.HDRCubeTextureLoader.prototype.load = function ( urls, onLoad, onProgress, onError ) {
 
-	var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+	if ( ! Array.isArray( urls ) ) {
 
-		var e = sourceArray[ sourceOffset + 3 ];
-		var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+		console.warn( 'THREE.HDRCubeTextureLoader signature has changed. Use .setType() instead.' );
 
-		destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
-		destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
-		destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
+		this.setType( urls );
 
-	};
+		urls = onLoad;
+		onLoad = onProgress;
+		onProgress = onError;
+		onError = arguments[ 4 ];
 
-	var RGBEByteToRGBHalf = ( function () {
-
-		// Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
-
-		var floatView = new Float32Array( 1 );
-		var int32View = new Int32Array( floatView.buffer );
-
-		/* This method is faster than the OpenEXR implementation (very often
-		 * used, eg. in Ogre), with the additional benefit of rounding, inspired
-		 * by James Tursa?s half-precision code. */
-		function toHalf( val ) {
-
-			floatView[ 0 ] = val;
-			var x = int32View[ 0 ];
-
-			var bits = ( x >> 16 ) & 0x8000; /* Get the sign */
-			var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */
-			var e = ( x >> 23 ) & 0xff; /* Using int is faster here */
-
-			/* If zero, or denormal, or exponent underflows too much for a denormal
-			 * half, return signed zero. */
-			if ( e < 103 ) return bits;
-
-			/* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
-			if ( e > 142 ) {
-
-				bits |= 0x7c00;
-				/* If exponent was 0xff and one mantissa bit was set, it means NaN,
-						 * not Inf, so make sure we set one mantissa bit too. */
-				bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff );
-				return bits;
-
-			}
-
-			/* If exponent underflows but not too much, return a denormal */
-			if ( e < 113 ) {
-
-				m |= 0x0800;
-				/* Extra rounding may overflow and set mantissa to 0 and exponent
-				 * to 1, which is OK. */
-				bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 );
-				return bits;
+	}
 
-			}
+	var texture = new THREE.CubeTexture();
 
-			bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 );
-			/* Extra rounding. An overflow will set mantissa to 0 and increment
-			 * the exponent, which is OK. */
-			bits += m & 1;
-			return bits;
+	texture.type = this.type;
 
-		}
+	switch ( texture.type ) {
 
-		return function ( sourceArray, sourceOffset, destArray, destOffset ) {
+		case THREE.UnsignedByteType:
 
-			var e = sourceArray[ sourceOffset + 3 ];
-			var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+			texture.encoding = THREE.RGBEEncoding;
+			texture.format = THREE.RGBAFormat;
+			texture.minFilter = THREE.NearestFilter;
+			texture.magFilter = THREE.NearestFilter;
+			texture.generateMipmaps = false;
+			break;
 
-			destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale );
-			destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale );
-			destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale );
+		case THREE.FloatType:
 
-		};
+			texture.encoding = THREE.LinearEncoding;
+			texture.format = THREE.RGBFormat;
+			texture.minFilter = THREE.LinearFilter;
+			texture.magFilter = THREE.LinearFilter;
+			texture.generateMipmaps = false;
+			break;
 
-	} )();
+		case THREE.HalfFloatType:
 
-	//
+			texture.encoding = THREE.LinearEncoding;
+			texture.format = THREE.RGBFormat;
+			texture.minFilter = THREE.LinearFilter;
+			texture.magFilter = THREE.LinearFilter;
+			texture.generateMipmaps = false;
+			break;
 
-	var texture = new THREE.CubeTexture();
-
-	texture.type = type;
-	texture.encoding = ( type === THREE.UnsignedByteType ) ? THREE.RGBEEncoding : THREE.LinearEncoding;
-	texture.format = ( type === THREE.UnsignedByteType ) ? THREE.RGBAFormat : THREE.RGBFormat;
-	texture.minFilter = ( texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter;
-	texture.magFilter = ( texture.encoding === THREE.RGBEEncoding ) ? THREE.NearestFilter : THREE.LinearFilter;
-	texture.generateMipmaps = ( texture.encoding !== THREE.RGBEEncoding );
-	texture.anisotropy = 0;
+	}
 
 	var scope = this;
 
@@ -108,71 +67,40 @@ THREE.HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onPro
 
 	function loadHDRData( i, onLoad, onProgress, onError ) {
 
-		var loader = new THREE.FileLoader( scope.manager );
-		loader.setPath( scope.path );
-		loader.setResponseType( 'arraybuffer' );
-		loader.load( urls[ i ], function ( buffer ) {
+		new THREE.FileLoader( scope.manager )
+			.setPath( scope.path )
+			.setResponseType( 'arraybuffer' )
+			.load( urls[ i ], function ( buffer ) {
 
-			loaded ++;
+				loaded ++;
 
-			var texData = scope.hdrLoader._parser( buffer );
+				var texData = scope.hdrLoader._parser( buffer );
 
-			if ( ! texData ) return;
+				if ( ! texData ) return;
 
-			if ( type === THREE.FloatType ) {
+				if ( texData.data !== undefined ) {
 
-				var numElements = ( texData.data.length / 4 ) * 3;
-				var floatdata = new Float32Array( numElements );
+					var dataTexture = new THREE.DataTexture( texData.data, texData.width, texData.height );
 
-				for ( var j = 0; j < numElements; j ++ ) {
+					dataTexture.type = texture.type;
+					dataTexture.encoding = texture.encoding;
+					dataTexture.format = texture.format;
+					dataTexture.minFilter = texture.minFilter;
+					dataTexture.magFilter = texture.magFilter;
+					dataTexture.generateMipmaps = texture.generateMipmaps;
 
-					RGBEByteToRGBFloat( texData.data, j * 4, floatdata, j * 3 );
+					texture.images[ i ] = dataTexture;
 
 				}
 
-				texData.data = floatdata;
-
-			} else if ( type === THREE.HalfFloatType ) {
-
-				var numElements = ( texData.data.length / 4 ) * 3;
-				var halfdata = new Uint16Array( numElements );
+				if ( loaded === 6 ) {
 
-				for ( var j = 0; j < numElements; j ++ ) {
-
-					RGBEByteToRGBHalf( texData.data, j * 4, halfdata, j * 3 );
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
 
 				}
 
-				texData.data = halfdata;
-
-			}
-
-			if ( texData.image !== undefined ) {
-
-				texture[ i ].images = texData.image;
-
-			} else if ( texData.data !== undefined ) {
-
-				var dataTexture = new THREE.DataTexture( texData.data, texData.width, texData.height );
-				dataTexture.format = texture.format;
-				dataTexture.type = texture.type;
-				dataTexture.encoding = texture.encoding;
-				dataTexture.minFilter = texture.minFilter;
-				dataTexture.magFilter = texture.magFilter;
-				dataTexture.generateMipmaps = texture.generateMipmaps;
-
-				texture.images[ i ] = dataTexture;
-
-			}
-
-			if ( loaded === 6 ) {
-
-				texture.needsUpdate = true;
-				if ( onLoad ) onLoad( texture );
-
-			}
-
-		}, onProgress, onError );
+			}, onProgress, onError );
 
 	}
 
@@ -192,3 +120,11 @@ THREE.HDRCubeTextureLoader.prototype.setPath = function ( value ) {
 	return this;
 
 };
+
+THREE.HDRCubeTextureLoader.prototype.setType = function ( value ) {
+
+	this.type = value;
+	this.hdrLoader.setType( value );
+	return this;
+
+};

+ 2 - 3
examples/js/loaders/LDrawLoader.js

@@ -1,6 +1,7 @@
 /**
  * @author mrdoob / http://mrdoob.com/
  * @author yomboprime / https://github.com/yomboprime/
+ * @author gkjohnson / https://github.com/gkjohnson/
  *
  *
  */
@@ -1045,8 +1046,6 @@ THREE.LDrawLoader = ( function () {
 			var mainColourCode = parentParseScope.mainColourCode;
 			var mainEdgeColourCode = parentParseScope.mainEdgeColourCode;
 
-			var url = parentParseScope.url;
-
 			var currentParseScope = this.getCurrentParseScope();
 
 			// Parse result variables
@@ -1356,7 +1355,7 @@ THREE.LDrawLoader = ( function () {
 							0, 0, 0, 1
 						);
 
-						var fileName = lp.getRemainingString().trim().replace( "\\", "/" );
+						var fileName = lp.getRemainingString().trim().replace( /\\/g, "/" );
 
 						if ( scope.fileMap[ fileName ] ) {
 

+ 0 - 1
examples/js/loaders/LWOLoader.js

@@ -545,7 +545,6 @@ LWO3Parser.prototype = {
 			case 'FLAG':
 
 			case 'TRNL':
-			case 'GLOS':
 			case 'SHRP':
 			case 'RFOP':
 			case 'RSAN':

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

@@ -1026,7 +1026,7 @@ THREE.MMDLoader = ( function () {
 		 * @param {function} onError
 		 * @return {Array<THREE.MeshToonMaterial>}
 		 */
-		build: function ( data, geometry, onProgress, onError ) {
+		build: function ( data, geometry /*, onProgress, onError */ ) {
 
 			var materials = [];
 

+ 4 - 0
examples/js/loaders/NRRDLoader.js

@@ -1,3 +1,7 @@
+/*
+ *  three.js NRRD file loader
+ */
+
 THREE.NRRDLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

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

@@ -68,7 +68,7 @@ THREE.TTFLoader.prototype = {
 
 					}
 
-					glyph.path.commands.forEach( function ( command, i ) {
+					glyph.path.commands.forEach( function ( command ) {
 
 						if ( command.type.toLowerCase() === 'c' ) {
 

+ 1996 - 762
examples/js/loaders/VRMLLoader.js

@@ -1,1332 +1,2566 @@
 /**
- * @author mrdoob / http://mrdoob.com/
+ * @author Mugen87 / https://github.com/Mugen87
  */
 
-THREE.VRMLLoader = function ( manager ) {
+/* global chevrotain */
 
-	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+THREE.VRMLLoader = ( function () {
 
-};
+	// dependency check
 
-THREE.VRMLLoader.prototype = {
+	if ( typeof chevrotain === 'undefined' ) {
 
-	constructor: THREE.VRMLLoader,
+		throw Error( 'THREE.VRMLLoader: External library chevrotain.min.js required.' );
 
-	// for IndexedFaceSet support
-	isRecordingPoints: false,
-	isRecordingFaces: false,
-	points: [],
-	indexes: [],
+	}
 
-	// for Background support
-	isRecordingAngles: false,
-	isRecordingColors: false,
-	angles: [],
-	colors: [],
+	// class definitions
 
-	recordingFieldname: null,
+	function VRMLLoader( manager ) {
 
-	crossOrigin: 'anonymous',
+		this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
 
-	load: function ( url, onLoad, onProgress, onError ) {
+	}
 
-		var scope = this;
+	VRMLLoader.prototype = {
 
-		var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
+		constructor: VRMLLoader,
 
-		var loader = new THREE.FileLoader( this.manager );
-		loader.setPath( scope.path );
-		loader.load( url, function ( text ) {
+		crossOrigin: 'anonymous',
 
-			onLoad( scope.parse( text, path ) );
+		load: function ( url, onLoad, onProgress, onError ) {
 
-		}, onProgress, onError );
+			var scope = this;
 
-	},
+			var path = ( scope.path === undefined ) ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
 
-	setPath: function ( value ) {
+			var loader = new THREE.FileLoader( this.manager );
+			loader.setPath( scope.path );
+			loader.load( url, function ( text ) {
 
-		this.path = value;
-		return this;
+				onLoad( scope.parse( text, path ) );
 
-	},
+			}, onProgress, onError );
 
-	setResourcePath: function ( value ) {
+		},
 
-		this.resourcePath = value;
-		return this;
+		setPath: function ( value ) {
 
-	},
+			this.path = value;
+			return this;
 
-	setCrossOrigin: function ( value ) {
+		},
 
-		this.crossOrigin = value;
-		return this;
+		setResourcePath: function ( value ) {
 
-	},
+			this.resourcePath = value;
+			return this;
 
-	parse: function ( data, path ) {
+		},
 
-		var scope = this;
+		setCrossOrigin: function ( value ) {
 
-		var textureLoader = new THREE.TextureLoader( this.manager );
-		textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
+			this.crossOrigin = value;
+			return this;
 
-		function parseV2( lines, scene ) {
+		},
 
-			var defines = {};
-			var float_pattern = /(\b|\-|\+)([\d\.e]+)/;
-			var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
-			var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
+		parse: function ( data, path ) {
 
-			/**
-			 * Vertically paints the faces interpolating between the
-			 * specified colors at the specified angels. This is used for the Background
-			 * node, but could be applied to other nodes with multiple faces as well.
-			 *
-			 * When used with the Background node, default is directionIsDown is true if
-			 * interpolating the skyColor down from the Zenith. When interpolationg up from
-			 * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
-			 *
-			 * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
-			 * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
-			 * is linear along the Y axis in any case.
-			 *
-			 * You must specify one more color than you have angles at the beginning of the colors array.
-			 * This is the color of the Zenith (the top of the shape).
-			 *
-			 * @param geometry
-			 * @param radius
-			 * @param angles
-			 * @param colors
-			 * @param boolean topDown Whether to work top down or bottom up.
-			 */
-			function paintFaces( geometry, radius, angles, colors, topDown ) {
+			var nodeMap = {};
 
-				var direction = ( topDown === true ) ? 1 : - 1;
+			function generateVRMLTree( data ) {
 
-				var coord = [], A = {}, B = {}, applyColor = false;
+				// create lexer, parser and visitor
 
-				for ( var k = 0; k < angles.length; k ++ ) {
+				var tokenData = createTokens();
 
-					// push the vector at which the color changes
+				var lexer = new VRMLLexer( tokenData.tokens );
+				var parser = new VRMLParser( tokenData.tokenVocabulary );
+				var visitor = createVisitor( parser.getBaseCstVisitorConstructor() );
 
-					var vec = {
-						x: direction * ( Math.cos( angles[ k ] ) * radius ),
-						y: direction * ( Math.sin( angles[ k ] ) * radius )
-					};
+				// lexing
 
-					coord.push( vec );
+				var lexingResult = lexer.lex( data );
+				parser.input = lexingResult.tokens;
+
+				// parsing
+
+				var cstOutput = parser.vrml();
+
+				if ( parser.errors.length > 0 ) {
+
+					console.error( parser.errors );
+
+					throw Error( 'THREE.VRMLLoader: Parsing errors detected.' );
 
 				}
 
-				var index = geometry.index;
-				var positionAttribute = geometry.attributes.position;
-				var colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
+				// actions
 
-				var position = new THREE.Vector3();
-				var color = new THREE.Color();
+				var ast = visitor.visit( cstOutput );
 
-				for ( var i = 0; i < index.count; i ++ ) {
+				return ast;
 
-					var vertexIndex = index.getX( i );
+			}
 
-					position.fromBufferAttribute( positionAttribute, vertexIndex );
+			function createTokens() {
+
+				var createToken = chevrotain.createToken;
+
+				// from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics
+
+				var RouteIdentifier = createToken( { name: 'RouteIdentifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/ } );
+				var Identifier = createToken( { name: 'Identifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/, longer_alt: RouteIdentifier } );
+
+				// from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html
+
+				var nodeTypes = [
+					'Anchor', 'Billboard', 'Collision', 'Group', 'Transform', // grouping nodes
+					'Inline', 'LOD', 'Switch', // special groups
+					'AudioClip', 'DirectionalLight', 'PointLight', 'Script', 'Shape', 'Sound', 'SpotLight', 'WorldInfo', // common nodes
+					'CylinderSensor', 'PlaneSensor', 'ProximitySensor', 'SphereSensor', 'TimeSensor', 'TouchSensor', 'VisibilitySensor', // sensors
+					'Box', 'Cone', 'Cylinder', 'ElevationGrid', 'Extrusion', 'IndexedFaceSet', 'IndexedLineSet', 'PointSet', 'Sphere', // geometries
+					'Color', 'Coordinate', 'Normal', 'TextureCoordinate', // geometric properties
+					'Appearance', 'FontStyle', 'ImageTexture', 'Material', 'MovieTexture', 'PixelTexture', 'TextureTransform', // appearance
+					'ColorInterpolator', 'CoordinateInterpolator', 'NormalInterpolator', 'OrientationInterpolator', 'PositionInterpolator', 'ScalarInterpolator', // interpolators
+					'Background', 'Fog', 'NavigationInfo', 'Viewpoint', // bindable nodes
+					'Text' // Text must be placed at the end of the regex so there are no matches for TextureTransform and TextureCoordinate
+				];
+
+				//
+
+				var Version = createToken( {
+					name: 'Version',
+					pattern: /#VRML.*/,
+					longer_alt: Identifier
+				} );
+
+				var NodeName = createToken( {
+					name: 'NodeName',
+					pattern: new RegExp( nodeTypes.join( '|' ) ),
+					longer_alt: Identifier
+				} );
+
+				var DEF = createToken( {
+					name: 'DEF',
+					pattern: /DEF/,
+					longer_alt: Identifier
+				} );
+
+				var USE = createToken( {
+					name: 'USE',
+					pattern: /USE/,
+					longer_alt: Identifier
+				} );
+
+				var ROUTE = createToken( {
+					name: 'ROUTE',
+					pattern: /ROUTE/,
+					longer_alt: Identifier
+				} );
+
+				var TO = createToken( {
+					name: 'TO',
+					pattern: /TO/,
+					longer_alt: Identifier
+				} );
+
+				//
+
+				var StringLiteral = createToken( { name: "StringLiteral", pattern: /"(:?[^\\"\n\r]+|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/ } );
+				var NumberLiteral = createToken( { name: 'NumberLiteral', pattern: /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ } );
+				var BooleanLiteral = createToken( { name: 'BooleanLiteral', pattern: /TRUE|FALSE/ } );
+				var NullLiteral = createToken( { name: 'NullLiteral', pattern: /NULL/ } );
+				var LSquare = createToken( { name: 'LSquare', pattern: /\[/ } );
+				var RSquare = createToken( { name: 'RSquare', pattern: /]/ } );
+				var LCurly = createToken( { name: 'LCurly', pattern: /{/ } );
+				var RCurly = createToken( { name: 'RCurly', pattern: /}/ } );
+				var Comment = createToken( {
+					name: 'Comment',
+					pattern: /#.*/,
+					group: chevrotain.Lexer.SKIPPED
+				} );
+
+				// commas, blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields
+
+				var WhiteSpace = createToken( {
+					name: 'WhiteSpace',
+					pattern: /[ ,\s]/,
+					group: chevrotain.Lexer.SKIPPED
+				} );
+
+				var tokens = [
+					WhiteSpace,
+					// keywords appear before the Identifier
+					NodeName,
+					DEF,
+					USE,
+					ROUTE,
+					TO,
+					BooleanLiteral,
+					NullLiteral,
+					// the Identifier must appear after the keywords because all keywords are valid identifiers
+					Version,
+					Identifier,
+					RouteIdentifier,
+					StringLiteral,
+					NumberLiteral,
+					LSquare,
+					RSquare,
+					LCurly,
+					RCurly,
+					Comment
+				];
+
+				var tokenVocabulary = {};
+
+				for ( var i = 0, l = tokens.length; i < l; i ++ ) {
+
+					var token = tokens[ i ];
+
+					tokenVocabulary[ token.name ] = token;
 
-					for ( var j = 0; j < colors.length; j ++ ) {
+				}
 
-						// linear interpolation between aColor and bColor, calculate proportion
-						// A is previous point (angle)
+				return { tokens: tokens, tokenVocabulary: tokenVocabulary };
 
-						if ( j === 0 ) {
+			}
 
-							A.x = 0;
-							A.y = ( topDown === true ) ? radius : - 1 * radius;
 
-						} else {
+			function createVisitor( BaseVRMLVisitor ) {
 
-							A.x = coord[ j - 1 ].x;
-							A.y = coord[ j - 1 ].y;
+				// the visitor is created dynmaically based on the given base class
+
+				function VRMLToASTVisitor() {
+
+					BaseVRMLVisitor.call( this );
+
+					this.validateVisitor();
+
+				}
+
+				VRMLToASTVisitor.prototype = Object.assign( Object.create( BaseVRMLVisitor.prototype ), {
+
+					constructor: VRMLToASTVisitor,
+
+					vrml: function ( ctx ) {
+
+						var data = {
+							version: this.visit( ctx.version ),
+							nodes: [],
+							routes: []
+						};
+
+						for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
+
+							var node = ctx.node[ i ];
+
+							data.nodes.push( this.visit( node ) );
 
 						}
 
-						// B is current point (angle)
+						if ( ctx.route ) {
 
-						B = coord[ j ];
+							for ( var i = 0, l = ctx.route.length; i < l; i ++ ) {
 
-						if ( B !== undefined ) {
+								var route = ctx.route[ i ];
 
-							// p has to be between the points A and B which we interpolate
+								data.routes.push( this.visit( route ) );
 
-							applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
+							}
 
-							if ( applyColor === true ) {
+						}
 
-								var aColor = colors[ j ];
-								var bColor = colors[ j + 1 ];
+						return data;
 
-								// below is simple linear interpolation
+					},
 
-								var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
+					version: function ( ctx ) {
 
-								// to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
+						return ctx.Version[ 0 ].image;
 
-								color.copy( aColor ).lerp( bColor, t );
+					},
 
-								colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
+					node: function ( ctx ) {
 
-							} else {
+						var data = {
+							name: ctx.NodeName[ 0 ].image,
+							fields: []
+						};
 
-								var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
-								var c = colors[ colorIndex ];
-								colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
+						if ( ctx.field ) {
+
+							for ( var i = 0, l = ctx.field.length; i < l; i ++ ) {
+
+								var field = ctx.field[ i ];
+
+								data.fields.push( this.visit( field ) );
 
 							}
 
 						}
 
-					}
+						// DEF
 
-				}
+						if ( ctx.def ) {
 
-				geometry.addAttribute( 'color', colorAttribute );
+							data.DEF = this.visit( ctx.def[ 0 ] );
 
-			}
+						}
+
+						return data;
 
-			var index = [];
+					},
 
-			function parseProperty( node, line ) {
+					field: function ( ctx ) {
 
-				var parts = [], part, property = {}, fieldName;
+						var data = {
+							name: ctx.Identifier[ 0 ].image,
+							type: null,
+							values: null
+						};
 
-				/**
-				 * Expression for matching relevant information, such as a name or value, but not the separators
-				 * @type {RegExp}
-				 */
-				var regex = /[^\s,\[\]]+/g;
+						var result;
 
-				var point;
+						// SFValue
 
-				while ( null !== ( part = regex.exec( line ) ) ) {
+						if ( ctx.singleFieldValue ) {
 
-					parts.push( part[ 0 ] );
+							result = this.visit( ctx.singleFieldValue[ 0 ] );
 
-				}
+						}
 
-				fieldName = parts[ 0 ];
+						// MFValue
 
+						if ( ctx.multiFieldValue ) {
 
-				// trigger several recorders
-				switch ( fieldName ) {
+							result = this.visit( ctx.multiFieldValue[ 0 ] );
 
-					case 'skyAngle':
-					case 'groundAngle':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingAngles = true;
-						scope.angles = [];
-						break;
+						}
 
-					case 'color':
-					case 'skyColor':
-					case 'groundColor':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingColors = true;
-						scope.colors = [];
-						break;
+						data.type = result.type;
+						data.values = result.values;
 
-					case 'point':
-					case 'vector':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingPoints = true;
-						scope.points = [];
-						break;
+						return data;
 
-					case 'colorIndex':
-					case 'coordIndex':
-					case 'normalIndex':
-					case 'texCoordIndex':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingFaces = true;
-						scope.indexes = [];
-						break;
+					},
 
-				}
+					def: function ( ctx ) {
 
-				if ( scope.isRecordingFaces ) {
+						return ctx.Identifier[ 0 ].image;
 
-					// the parts hold the indexes as strings
-					if ( parts.length > 0 ) {
+					},
 
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
+					use: function ( ctx ) {
 
-							// the part should either be positive integer or -1
-							if ( ! /(-?\d+)/.test( parts[ ind ] ) ) {
+						return { USE: ctx.Identifier[ 0 ].image };
 
-								continue;
+					},
 
-							}
+					singleFieldValue: function ( ctx ) {
 
-							// end of current face
-							if ( parts[ ind ] === '-1' ) {
+						return processField( this, ctx );
 
-								if ( index.length > 0 ) {
+					},
 
-									scope.indexes.push( index );
+					multiFieldValue: function ( ctx ) {
 
-								}
+						return processField( this, ctx );
 
-								// start new one
-								index = [];
+					},
 
-							} else {
+					route: function ( ctx ) {
 
-								index.push( parseInt( parts[ ind ] ) );
+						var data = {
+							FROM: ctx.RouteIdentifier[ 0 ].image,
+							TO: ctx.RouteIdentifier[ 1 ].image
+						};
 
-							}
+						return data;
+
+					}
+
+				} );
+
+				function processField( scope, ctx ) {
+
+					var field = {
+						type: null,
+						values: []
+					};
+
+					if ( ctx.node ) {
+
+						field.type = 'node';
+
+						for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
+
+							var node = ctx.node[ i ];
+
+							field.values.push( scope.visit( node ) );
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
+					if ( ctx.use ) {
+
+						field.type = 'use';
 
-						if ( index.length > 0 ) {
+						for ( var i = 0, l = ctx.use.length; i < l; i ++ ) {
 
-							scope.indexes.push( index );
+							var use = ctx.use[ i ];
+
+							field.values.push( scope.visit( use ) );
 
 						}
 
-						// start new one
-						index = [];
+					}
+
+					if ( ctx.StringLiteral ) {
+
+						field.type = 'string';
+
+						for ( var i = 0, l = ctx.StringLiteral.length; i < l; i ++ ) {
+
+							var stringLiteral = ctx.StringLiteral[ i ];
 
-						scope.isRecordingFaces = false;
-						node[ scope.recordingFieldname ] = scope.indexes;
+							field.values.push( stringLiteral.image.replace( /'|"/g, '' ) );
+
+						}
 
 					}
 
-				} else if ( scope.isRecordingPoints ) {
+					if ( ctx.NumberLiteral ) {
 
-					if ( node.nodeType == 'Coordinate' ) {
+						field.type = 'number';
 
-						while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+						for ( var i = 0, l = ctx.NumberLiteral.length; i < l; i ++ ) {
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
+							var numberLiteral = ctx.NumberLiteral[ i ];
 
-							scope.points.push( point );
+							field.values.push( parseFloat( numberLiteral.image ) );
 
 						}
 
 					}
 
-					if ( node.nodeType == 'Normal' ) {
+					if ( ctx.BooleanLiteral ) {
 
-  						while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+						field.type = 'boolean';
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
+						for ( var i = 0, l = ctx.BooleanLiteral.length; i < l; i ++ ) {
 
-							scope.points.push( point );
+							var booleanLiteral = ctx.BooleanLiteral[ i ];
+
+							field.values.push( booleanLiteral.image === 'TRUE' );
 
 						}
 
 					}
 
-					if ( node.nodeType == 'TextureCoordinate' ) {
+					if ( ctx.NullLiteral ) {
+
+						field.type = 'null';
+
+						ctx.NullLiteral.forEach( function () {
+
+							field.values.push( null );
+
+						} );
+
+					}
+
+					return field;
+
+				}
+
+				return new VRMLToASTVisitor();
+
+			}
+
+			function parseTree( tree ) {
+
+				// console.log( JSON.stringify( tree, null, 2 ) );
+
+				var nodes = tree.nodes;
+				var scene = new THREE.Scene();
+
+				// first iteration: build nodemap based on DEF statements
+
+				for ( var i = 0, l = nodes.length; i < l; i ++ ) {
+
+					var node = nodes[ i ];
+
+					buildNodeMap( node );
+
+				}
+
+				// second iteration: build nodes
+
+				for ( var i = 0, l = nodes.length; i < l; i ++ ) {
+
+					var node = nodes[ i ];
+					var object = getNode( node );
+
+					if ( object instanceof THREE.Object3D ) scene.add( object );
+
+				}
+
+				return scene;
+
+			}
+
+			function buildNodeMap( node ) {
+
+				if ( node.DEF ) {
+
+					nodeMap[ node.DEF ] = node;
+
+				}
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-						while ( null !== ( parts = float2_pattern.exec( line ) ) ) {
+					var field = fields[ i ];
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] )
-							};
+					if ( field.type === 'node' ) {
 
-							scope.points.push( point );
+						var fieldValues = field.values;
+
+						for ( var j = 0, jl = fieldValues.length; j < jl; j ++ ) {
+
+							buildNodeMap( fieldValues[ j ] );
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
 
-						scope.isRecordingPoints = false;
-						node.points = scope.points;
+				}
+
+			}
+
+
+			function getNode( node ) {
+
+				// handle case where a node refers to a different one
+
+				if ( node.USE ) {
+
+					return resolveUSE( node.USE );
+
+				}
+
+				if ( node.build !== undefined ) return node.build;
+
+				node.build = buildNode( node );
+
+				return node.build;
+
+			}
+
+			// node builder
+
+			function buildNode( node ) {
+
+				var nodeName = node.name;
+				var build;
+
+				switch ( nodeName ) {
+
+					case 'Group':
+					case 'Transform':
+						build = buildGroupingNode( node );
+						break;
+
+					case 'Background':
+						build = buildBackgroundNode( node );
+						break;
+
+					case 'Shape':
+						build = buildShapeNode( node );
+						break;
+
+					case 'Appearance':
+						build = buildApperanceNode( node );
+						break;
+
+					case 'Material':
+						build = buildMaterialNode( node );
+						break;
+
+					case 'ImageTexture':
+						build = buildImageTextureNode( node );
+						break;
+
+					case 'TextureTransform':
+						build = buildTextureTransformNode( node );
+						break;
+
+					case 'IndexedFaceSet':
+						build = buildIndexedFaceSetNode( node );
+						break;
+
+					case 'IndexedLineSet':
+						build = buildIndexedLineSetNode( node );
+						break;
+
+					case 'PointSet':
+						build = buildPointSetNode( node );
+						break;
+
+					case 'Box':
+						build = buildBoxNode( node );
+						break;
+
+					case 'Cone':
+						build = buildConeNode( node );
+						break;
+
+					case 'Cylinder':
+						build = buildCylinderNode( node );
+						break;
+
+					case 'Sphere':
+						build = buildSphereNode( node );
+						break;
+
+					case 'Color':
+					case 'Coordinate':
+					case 'Normal':
+					case 'TextureCoordinate':
+						build = buildGeometricNode( node );
+						break;
+
+					case 'Anchor':
+					case 'Billboard':
+					case 'Collision':
+
+					case 'Inline':
+					case 'LOD':
+					case 'Switch':
+
+					case 'AudioClip':
+					case 'DirectionalLight':
+					case 'PointLight':
+					case 'Script':
+					case 'Sound':
+					case 'SpotLight':
+					case 'WorldInfo':
+
+					case 'CylinderSensor':
+					case 'PlaneSensor':
+					case 'ProximitySensor':
+					case 'SphereSensor':
+					case 'TimeSensor':
+					case 'TouchSensor':
+					case 'VisibilitySensor':
+
+					case 'ElevationGrid':
+					case 'Extrusion':
+					case 'Text':
+
+					case 'FontStyle':
+					case 'MovieTexture':
+					case 'PixelTexture':
+
+					case 'ColorInterpolator':
+					case 'CoordinateInterpolator':
+					case 'NormalInterpolator':
+					case 'OrientationInterpolator':
+					case 'PositionInterpolator':
+					case 'ScalarInterpolator':
+
+					case 'Fog':
+					case 'NavigationInfo':
+					case 'Viewpoint':
+						// node not supported yet
+						break;
+
+					default:
+						console.warn( 'THREE.VRMLLoader: Unknown node:', nodeName );
+						break;
+
+				}
+
+				return build;
+
+			}
+
+			function buildGroupingNode( node ) {
+
+				var object = new THREE.Group();
+
+				//
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'center':
+							// field not supported
+							break;
+
+						case 'children':
+							parseFieldChildren( fieldValues, object );
+							break;
+
+						case 'rotation':
+							var axis = new THREE.Vector3( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							var angle = fieldValues[ 3 ];
+							object.quaternion.setFromAxisAngle( axis, angle );
+							break;
+
+						case 'scale':
+							object.scale.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'scaleOrientation':
+							// field not supported
+							break;
+
+						case 'translation':
+							object.position.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'bboxCenter':
+							// field not supported
+							break;
+
+						case 'bboxSize':
+							// field not supported
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return object;
+
+			}
+
+			function buildBackgroundNode( node ) {
+
+				var group = new THREE.Group();
+
+				var groundAngle, groundColor;
+				var skyAngle, skyColor;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'groundAngle':
+							groundAngle = fieldValues;
+							break;
+
+						case 'groundColor':
+							groundColor = fieldValues;
+							break;
+
+						case 'backUrl':
+							// field not supported
+							break;
+
+						case 'bottomUrl':
+							// field not supported
+							break;
+
+						case 'frontUrl':
+							// field not supported
+							break;
+
+						case 'leftUrl':
+							// field not supported
+							break;
+
+						case 'rightUrl':
+							// field not supported
+							break;
+
+						case 'topUrl':
+							// field not supported
+							break;
+
+						case 'skyAngle':
+							skyAngle = fieldValues;
+							break;
+
+						case 'skyColor':
+							skyColor = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// sky
+
+				if ( skyColor ) {
+
+					var radius = 10000;
+
+					var skyGeometry = new THREE.SphereBufferGeometry( radius, 32, 16 );
+					var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, depthWrite: false, depthTest: false } );
+
+					if ( skyColor.length > 3 ) {
+
+						paintFaces( skyGeometry, radius, skyAngle, toColorArray( skyColor ), true );
+						skyMaterial.vertexColors = THREE.VertexColors;
+
+					} else {
+
+						skyMaterial.color.setRGB( skyColor[ 0 ], skyColor[ 1 ], skyColor[ 2 ] );
+
+					}
+
+					var sky = new THREE.Mesh( skyGeometry, skyMaterial );
+					group.add( sky );
+
+				}
+
+				// ground
+
+				if ( groundColor ) {
+
+					if ( groundColor.length > 0 ) {
+
+						var groundGeometry = new THREE.SphereBufferGeometry( radius, 32, 16, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
+						var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors, depthWrite: false, depthTest: false } );
+
+						paintFaces( groundGeometry, radius, groundAngle, toColorArray( groundColor ), false );
+
+						var ground = new THREE.Mesh( groundGeometry, groundMaterial );
+						group.add( ground );
+
+					}
+
+				}
+
+				// render background group first
+
+				group.renderOrder = - Infinity;
+
+				return group;
+
+			}
+
+			function buildShapeNode( node ) {
+
+				var fields = node.fields;
+
+				// if the appearance field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)
+
+				var material = new THREE.MeshBasicMaterial( { color: 0x000000 } );
+				var geometry;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'appearance':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								material = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						case 'geometry':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								geometry = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// build 3D object
+
+				var object;
+
+				if ( geometry ) {
+
+					var type = geometry._type;
+
+					if ( type === 'points' ) { // points
+
+						var pointsMaterial = new THREE.PointsMaterial( { color: 0xffffff } );
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							pointsMaterial.vertexColors = THREE.VertexColors;
+
+						} else {
+
+							// if the color field is NULL and there is a material defined for the appearance affecting this PointSet, then use the emissiveColor of the material to draw the points
+
+							if ( material.isMeshPhongMaterial ) {
+
+								pointsMaterial.color.copy( material.emissive );
+
+							}
+
+						}
+
+						object = new THREE.Points( geometry, pointsMaterial );
+
+					} else if ( type === 'line' ) { // lines
+
+						var lineMaterial = new THREE.LineBasicMaterial( { color: 0xffffff } );
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							lineMaterial.vertexColors = THREE.VertexColors;
+
+						} else {
+
+							// if the color field is NULL and there is a material defined for the appearance affecting this IndexedLineSet, then use the emissiveColor of the material to draw the lines
+
+							if ( material.isMeshPhongMaterial ) {
+
+								lineMaterial.color.copy( material.emissive );
+
+							}
+
+						}
+
+						object = new THREE.LineSegments( geometry, lineMaterial );
+
+					} else { // consider meshes
+
+						// check "solid" hint (it's placed in the geometry but affects the material)
+
+						if ( geometry._solid !== undefined ) {
+
+							material.side = ( geometry._solid ) ? THREE.FrontSide : THREE.DoubleSide;
+
+						}
+
+						// check for vertex colors
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							material.vertexColors = THREE.VertexColors;
+
+						}
+
+						object = new THREE.Mesh( geometry, material );
+
+					}
+
+				} else {
+
+					object = new THREE.Object3D();
+
+					// if the geometry field is NULL the object is not drawn
+
+					object.visible = false;
+
+				}
+
+				return object;
+
+			}
+
+			function buildApperanceNode( node ) {
+
+				var material = new THREE.MeshPhongMaterial();
+				var transformData;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'material':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								var materialData = getNode( fieldValues[ 0 ] );
+
+								if ( materialData.diffuseColor ) material.color.copy( materialData.diffuseColor );
+								if ( materialData.emissiveColor ) material.emissive.copy( materialData.emissiveColor );
+								if ( materialData.shininess ) material.shininess = materialData.shininess;
+								if ( materialData.specularColor ) material.specular.copy( materialData.specularColor );
+								if ( materialData.transparency ) material.opacity = 1 - materialData.transparency;
+								if ( materialData.transparency > 0 ) material.transparent = true;
+
+							} else {
+
+								// if the material field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)
+
+								material = new THREE.MeshBasicMaterial( { color: 0x000000 } );
+
+							}
+							break;
+
+						case 'texture':
+							var textureNode = fieldValues[ 0 ];
+							if ( textureNode !== null ) {
+
+								if ( textureNode.name === 'ImageTexture' ) {
+
+									material.map = getNode( textureNode );
+
+								} else {
+
+									// MovieTexture and PixelTexture not supported yet
+
+								}
+
+							}
+							break;
+
+						case 'textureTransform':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								transformData = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// only apply texture transform data if a texture was defined
+
+				if ( material.map && transformData ) {
+
+					material.map.center.copy( transformData.center );
+					material.map.rotation = transformData.rotation;
+					material.map.repeat.copy( transformData.scale );
+					material.map.offset.copy( transformData.translation );
+
+				}
+
+				return material;
+
+			}
+
+			function buildMaterialNode( node ) {
+
+				var materialData = {};
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'ambientIntensity':
+							// field not supported
+							break;
+
+						case 'diffuseColor':
+							materialData.diffuseColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'emissiveColor':
+							materialData.emissiveColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'shininess':
+							materialData.shininess = fieldValues[ 0 ];
+							break;
+
+						case 'specularColor':
+							materialData.emissiveColor = new THREE.Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'transparency':
+							materialData.transparency = fieldValues[ 0 ];
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return materialData;
+
+			}
+
+			function buildImageTextureNode( node ) {
+
+				var texture;
+				var wrapS = THREE.RepeatWrapping;
+				var wrapT = THREE.RepeatWrapping;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'url':
+							var url = fieldValues[ 0 ];
+							if ( url ) texture = textureLoader.load( url );
+							break;
+
+						case 'repeatS':
+							if ( fieldValues[ 0 ] === false ) wrapS = THREE.ClampToEdgeWrapping;
+							break;
+
+						case 'repeatT':
+							if ( fieldValues[ 0 ] === false ) wrapT = THREE.ClampToEdgeWrapping;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				if ( texture ) {
+
+					texture.wrapS = wrapS;
+					texture.wrapT = wrapT;
+
+				}
+
+				return texture;
+
+			}
+
+			function buildTextureTransformNode( node ) {
+
+				var transformData = {
+					center: new THREE.Vector2(),
+					rotation: new THREE.Vector2(),
+					scale: new THREE.Vector2(),
+					translation: new THREE.Vector2()
+				};
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'center':
+							transformData.center.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						case 'rotation':
+							transformData.rotation = fieldValues[ 0 ];
+							break;
+
+						case 'scale':
+							transformData.scale.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						case 'translation':
+							transformData.translation.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return transformData;
+
+			}
+
+			function buildGeometricNode( node ) {
+
+				return node.fields[ 0 ].values;
+
+			}
+
+			function buildIndexedFaceSetNode( node ) {
+
+				var color, coord, normal, texCoord;
+				var ccw = true, solid = true, creaseAngle;
+				var colorIndex, coordIndex, normalIndex, texCoordIndex;
+				var colorPerVertex = true, normalPerVertex = true;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'color':
+							var colorNode = fieldValues[ 0 ];
+
+							if ( colorNode !== null ) {
+
+								color = getNode( colorNode );
+
+							}
+							break;
+
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
+
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
+
+							}
+							break;
+
+						case 'normal':
+							var normalNode = fieldValues[ 0 ];
+
+							if ( normalNode !== null ) {
+
+								normal = getNode( normalNode );
+
+							}
+							break;
+
+						case 'texCoord':
+							var texCoordNode = fieldValues[ 0 ];
+
+							if ( texCoordNode !== null ) {
+
+								texCoord = getNode( texCoordNode );
+
+							}
+							break;
+
+						case 'ccw':
+							ccw = fieldValues[ 0 ];
+							break;
+
+						case 'colorIndex':
+							colorIndex = fieldValues;
+							break;
+
+						case 'colorPerVertex':
+							colorPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'convex':
+							// field not supported
+							break;
+
+						case 'coordIndex':
+							coordIndex = fieldValues;
+							break;
+
+						case 'creaseAngle':
+							creaseAngle = fieldValues[ 0 ];
+							break;
+
+						case 'normalIndex':
+							normalIndex = fieldValues;
+							break;
+
+						case 'normalPerVertex':
+							normalPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'solid':
+							solid = fieldValues[ 0 ];
+							break;
+
+						case 'texCoordIndex':
+							texCoordIndex = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				var triangulatedCoordIndex = triangulateFaceIndex( coordIndex, ccw );
+
+				var positionAttribute;
+				var colorAttribute;
+				var normalAttribute;
+				var uvAttribute;
+
+				if ( color ) {
+
+					if ( colorPerVertex === true ) {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then it is used to choose colors for each vertex of the IndexedFaceSet.
+
+							var triangulatedColorIndex = triangulateFaceIndex( colorIndex, ccw );
+							colorAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedColorIndex, color, 3 );
+
+						} else {
+
+							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node
+
+							colorAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( color, 3 ) );
+
+						}
+
+					} else {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then they are used to choose one color for each face of the IndexedFaceSet
+
+							var flattenFaceColors = flattenData( color, colorIndex );
+							var triangulatedFaceColors = triangulateFaceData( flattenFaceColors, coordIndex );
+							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );
+
+						} else {
+
+							// if the colorIndex field is empty, then the color are applied to each face of the IndexedFaceSet in order
+
+							var triangulatedFaceColors = triangulateFaceData( color, coordIndex );
+							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );
+
+
+						}
+
+					}
+
+				}
+
+				if ( normal ) {
+
+					if ( normalPerVertex === true ) {
+
+						// consider vertex normals
+
+						if ( normalIndex.length > 0 ) {
+
+							// if the normalIndex field is not empty, then it is used to choose normals for each vertex of the IndexedFaceSet.
+
+							var triangulatedNormalIndex = triangulateFaceIndex( normalIndex, ccw );
+							normalAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedNormalIndex, normal, 3 );
+
+						} else {
+
+							// if the normalIndex field is empty, then the coordIndex field is used to choose normals from the Normal node
+
+							normalAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( normal, 3 ) );
+
+						}
+
+					} else {
+
+						// consider face normals
+
+						if ( normalIndex.length > 0 ) {
+
+							// if the normalIndex field is not empty, then they are used to choose one normal for each face of the IndexedFaceSet
+
+							var flattenFaceNormals = flattenData( normal, normalIndex );
+							var triangulatedFaceNormals = triangulateFaceData( flattenFaceNormals, coordIndex );
+							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );
+
+						} else {
+
+							// if the normalIndex field is empty, then the normals are applied to each face of the IndexedFaceSet in order
+
+							var triangulatedFaceNormals = triangulateFaceData( normal, coordIndex );
+							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );
+
+						}
+
+					}
+
+				} else {
+
+					// if the normal field is NULL, then the loader should automatically generate normals, using creaseAngle to determine if and how normals are smoothed across shared vertices
+
+					normalAttribute = computeNormalAttribute( triangulatedCoordIndex, coord, creaseAngle );
+
+				}
+
+				if ( texCoord ) {
+
+					// texture coordinates are always defined on vertex level
+
+					if ( texCoordIndex.length > 0 ) {
+
+						// if the texCoordIndex field is not empty, then it is used to choose texture coordinates for each vertex of the IndexedFaceSet.
+
+						var triangulatedTexCoordIndex = triangulateFaceIndex( texCoordIndex, ccw );
+						uvAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedTexCoordIndex, texCoord, 2 );
+
+
+					} else {
+
+						// if the texCoordIndex field is empty, then the coordIndex array is used to choose texture coordinates from the TextureCoordinate node
+
+						uvAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( texCoord, 2 ) );
+
+					}
+
+				}
+
+				var geometry = new THREE.BufferGeometry();
+				positionAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new THREE.Float32BufferAttribute( coord, 3 ) );
+
+				geometry.addAttribute( 'position', positionAttribute );
+				geometry.addAttribute( 'normal', normalAttribute );
+
+				// optional attributes
+
+				if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
+				if ( uvAttribute ) geometry.addAttribute( 'uv', uvAttribute );
+
+				// "solid" influences the material so let's store it for later use
+
+				geometry._solid = solid;
+				geometry._type = 'mesh';
+
+				return geometry;
+
+			}
+
+			function buildIndexedLineSetNode( node ) {
+
+				var color, coord;
+				var colorIndex, coordIndex;
+				var colorPerVertex = true;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'color':
+							var colorNode = fieldValues[ 0 ];
+
+							if ( colorNode !== null ) {
+
+								color = getNode( colorNode );
+
+							}
+							break;
+
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
+
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
+
+							}
+							break;
+
+						case 'colorIndex':
+							colorIndex = fieldValues;
+							break;
+
+						case 'colorPerVertex':
+							colorPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'coordIndex':
+							coordIndex = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// build lines
+
+				var colorAttribute;
+
+				var expandedLineIndex = expandLineIndex( coordIndex ); // create an index for three.js's linesegment primitive
+
+				if ( color ) {
+
+					if ( colorPerVertex === true ) {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then one color is used for each polyline of the IndexedLineSet.
+
+							var expandedColorIndex = expandLineIndex( colorIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromIndexedData( expandedLineIndex, expandedColorIndex, color, 3 ); // compute data on vertex level
+
+						} else {
+
+							// if the colorIndex field is empty, then the colors are applied to each polyline of the IndexedLineSet in order.
+
+							colorAttribute = toNonIndexedAttribute( expandedLineIndex, new THREE.Float32BufferAttribute( color, 3 ) );
+
+						}
 
-					}
+					} else {
 
-				} else if ( scope.isRecordingAngles ) {
+						if ( colorIndex.length > 0 ) {
 
-					// the parts hold the angles as strings
-					if ( parts.length > 0 ) {
+							// if the colorIndex field is not empty, then colors are applied to each vertex of the IndexedLineSet
 
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
+							var flattenLineColors = flattenData( color, colorIndex ); // compute colors for each VRML primitve
+							var expandedLineColors = expandLineData( flattenLineColors, coordIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
 
-							// the part should be a float
-							if ( ! float_pattern.test( parts[ ind ] ) ) {
 
-								continue;
+						} else {
 
-							}
+							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node
 
-							scope.angles.push( parseFloat( parts[ ind ] ) );
+							var expandedLineColors = expandLineData( color, coordIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
+				}
 
-						scope.isRecordingAngles = false;
-						node[ scope.recordingFieldname ] = scope.angles;
+				//
 
-					}
+				var geometry = new THREE.BufferGeometry();
 
-				} else if ( scope.isRecordingColors ) {
+				var positionAttribute = toNonIndexedAttribute( expandedLineIndex, new THREE.Float32BufferAttribute( coord, 3 ) );
+				geometry.addAttribute( 'position', positionAttribute );
 
-					while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+				if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
 
-						var color = {
-							r: parseFloat( parts[ 1 ] ),
-							g: parseFloat( parts[ 2 ] ),
-							b: parseFloat( parts[ 3 ] )
-						};
+				geometry._type = 'line';
 
-						scope.colors.push( color );
+				return geometry;
 
-					}
+			}
 
-					// end
-					if ( /]/.exec( line ) ) {
+			function buildPointSetNode( node ) {
 
-						scope.isRecordingColors = false;
-						node[ scope.recordingFieldname ] = scope.colors;
+				var geometry;
+				var color, coord;
 
-					}
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-				} else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) {
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
 					switch ( fieldName ) {
 
-						case 'diffuseColor':
-						case 'emissiveColor':
-						case 'specularColor':
 						case 'color':
+							var colorNode = fieldValues[ 0 ];
 
-							if ( parts.length !== 4 ) {
+							if ( colorNode !== null ) {
 
-								console.warn( 'THREE.VRMLLoader: Invalid color format detected for %s.', fieldName );
-								break;
+								color = getNode( colorNode );
 
 							}
-
-							property = {
-								r: parseFloat( parts[ 1 ] ),
-								g: parseFloat( parts[ 2 ] ),
-								b: parseFloat( parts[ 3 ] )
-							};
-
 							break;
 
-						case 'location':
-						case 'direction':
-						case 'translation':
-						case 'scale':
-						case 'size':
-							if ( parts.length !== 4 ) {
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
 
-								console.warn( 'THREE.VRMLLoader: Invalid vector format detected for %s.', fieldName );
-								break;
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
 
 							}
+							break;
 
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
 
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
 							break;
 
-						case 'intensity':
-						case 'cutOffAngle':
-						case 'radius':
-						case 'topRadius':
-						case 'bottomRadius':
-						case 'height':
-						case 'transparency':
-						case 'shininess':
-						case 'ambientIntensity':
-						case 'creaseAngle':
-							if ( parts.length !== 2 ) {
+					}
 
-								console.warn( 'THREE.VRMLLoader: Invalid single float value specification detected for %s.', fieldName );
-								break;
+				}
 
-							}
+				var geometry = new THREE.BufferGeometry();
 
-							property = parseFloat( parts[ 1 ] );
+				geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( coord, 3 ) );
+				if ( color ) geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( color, 3 ) );
 
-							break;
+				geometry._type = 'points';
 
-						case 'rotation':
-							if ( parts.length !== 5 ) {
+				return geometry;
 
-								console.warn( 'THREE.VRMLLoader: Invalid quaternion format detected for %s.', fieldName );
-								break;
+			}
 
-							}
+			function buildBoxNode( node ) {
 
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] ),
-								w: parseFloat( parts[ 4 ] )
-							};
+				var size = new THREE.Vector3( 2, 2, 2 );
 
-							break;
+				var fields = node.fields;
 
-						case 'on':
-						case 'ccw':
-						case 'solid':
-						case 'colorPerVertex':
-						case 'convex':
-							if ( parts.length !== 2 ) {
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-								console.warn( 'THREE.VRMLLoader: Invalid format detected for %s.', fieldName );
-								break;
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-							}
+					switch ( fieldName ) {
 
-							property = parts[ 1 ] === 'TRUE' ? true : false;
+						case 'size':
+							size.x = fieldValues[ 0 ];
+							size.y = fieldValues[ 1 ];
+							size.z = fieldValues[ 2 ];
+							break;
 
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
 							break;
 
 					}
 
-					// VRMLLoader does not support text so it can't process the "string" property yet
-
-					if ( fieldName !== 'string' ) node[ fieldName ] = property;
-
 				}
 
-				return property;
+				var geometry = new THREE.BoxBufferGeometry( size.x, size.y, size.z );
+
+				return geometry;
 
 			}
 
-			function getTree( lines ) {
+			function buildConeNode( node ) {
 
-				var tree = { 'string': 'Scene', children: [] };
-				var current = tree;
-				var matches;
-				var specification;
+				var radius = 1, height = 2, openEnded = false;
 
-				for ( var i = 0; i < lines.length; i ++ ) {
+				var fields = node.fields;
 
-					var comment = '';
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-					var line = lines[ i ];
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-					// omit whitespace only lines
-					if ( null !== ( /^\s+?$/g.exec( line ) ) ) {
+					switch ( fieldName ) {
 
-						continue;
+						case 'bottom':
+							openEnded = ! fieldValues[ 0 ];
+							break;
 
-					}
+						case 'bottomRadius':
+							radius = fieldValues[ 0 ];
+							break;
 
-					line = line.trim();
+						case 'height':
+							height = fieldValues[ 0 ];
+							break;
 
-					// skip empty lines
-					if ( line === '' ) {
+						case 'side':
+							// field not supported
+							break;
 
-						continue;
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
 					}
 
-					if ( /#/.exec( line ) ) {
-
-						var parts = line.split( '#' );
-
-						// discard everything after the #, it is a comment
-						line = parts[ 0 ];
+				}
 
-						// well, let's also keep the comment
-						comment = parts[ 1 ];
+				var geometry = new THREE.ConeBufferGeometry( radius, height, 16, 1, openEnded );
 
-					}
+				return geometry;
 
-					if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) {
+			}
 
-						// first subpattern should match the Node name
+			function buildCylinderNode( node ) {
 
-						var block = { 'nodeType': matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment': comment };
-						current.children.push( block );
-						current = block;
+				var radius = 1, height = 2;
 
-						if ( /}/.exec( line ) ) {
+				var fields = node.fields;
 
-							// example: geometry Box { size 1 1 1 } # all on the same line
-							specification = /{(.*)}/.exec( line )[ 1 ];
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-							// todo: remove once new parsing is complete?
-							block.children.push( specification );
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-							parseProperty( current, specification );
+					switch ( fieldName ) {
 
-							current = current.parent;
+						case 'bottom':
+							// field not supported
+							break;
 
-						}
+						case 'radius':
+							radius = fieldValues[ 0 ];
+							break;
 
-					} else if ( /}/.exec( line ) ) {
+						case 'height':
+							height = fieldValues[ 0 ];
+							break;
 
-						current = current.parent;
+						case 'side':
+							// field not supported
+							break;
 
-					} else if ( line !== '' ) {
+						case 'top':
+							// field not supported
+							break;
 
-						parseProperty( current, line );
-						// todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way
-						current.children.push( line );
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
 					}
 
 				}
 
-				return tree;
+				var geometry = new THREE.CylinderBufferGeometry( radius, radius, height, 16, 1 );
 
-			}
-
-			function parseNode( data, parent ) {
+				return geometry;
 
-				var object;
+			}
 
-				if ( typeof data === 'string' ) {
+			function buildSphereNode( node ) {
 
-					if ( /USE/.exec( data ) ) {
+				var radius = 1;
 
-						var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ];
+				var fields = node.fields;
 
-						if ( undefined == defines[ defineKey ] ) {
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-							console.warn( 'THREE.VRMLLoader: %s is not defined.', defineKey );
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-						} else {
+					switch ( fieldName ) {
 
-							if ( /appearance/.exec( data ) && defineKey ) {
+						case 'radius':
+							radius = fieldValues[ 0 ];
+							break;
 
-								parent.material = defines[ defineKey ].clone();
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
-							} else if ( /geometry/.exec( data ) && defineKey ) {
+					}
 
-								parent.geometry = defines[ defineKey ].clone();
+				}
 
-								// the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it
-								if ( defines[ defineKey ].solid !== undefined && defines[ defineKey ].solid === false ) {
+				var geometry = new THREE.SphereBufferGeometry( radius, 16, 16 );
 
-									parent.geometry.solid = false;
-									parent.material.side = THREE.DoubleSide;
+				return geometry;
 
-								}
+			}
 
-							} else if ( defineKey ) {
+			// helper functions
 
-								object = defines[ defineKey ].clone();
-								parent.add( object );
+			function resolveUSE( identifier ) {
 
-							}
+				var node = nodeMap[ identifier ];
+				var build = getNode( node );
 
-						}
+				// because the same 3D objects can have different transformations, it's necessary to clone them.
+				// materials can be influenced by the geometry (e.g. vertex normals). cloning is necessary to avoid
+				// any side effects
 
-					}
+				return ( build.isObject3D || build.isMaterial ) ? build.clone() : build;
 
-					return;
+			}
 
-				}
+			function parseFieldChildren( children, owner ) {
 
-				object = parent;
+				for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-				if ( data.string.indexOf( 'AmbientLight' ) > - 1 && data.nodeType === 'PointLight' ) {
+					var object = getNode( children[ i ] );
 
-					data.nodeType = 'AmbientLight';
+					if ( object instanceof THREE.Object3D ) owner.add( object );
 
 				}
 
-				var l_visible = data.on !== undefined ? data.on : true;
-				var l_intensity = data.intensity !== undefined ? data.intensity : 1;
-				var l_color = new THREE.Color();
+			}
 
-				if ( data.color ) {
+			function triangulateFaceIndex( index, ccw ) {
 
-					l_color.copy( data.color );
+				var indices = [];
 
-				}
+				// since face defintions can have more than three vertices, it's necessary to
+				// perform a simple triangulation
 
-				if ( data.nodeType === 'AmbientLight' ) {
+				var start = 0;
 
-					object = new THREE.AmbientLight( l_color, l_intensity );
-					object.visible = l_visible;
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					parent.add( object );
+					var i1 = index[ start ];
+					var i2 = index[ i + ( ccw ? 1 : 2 ) ];
+					var i3 = index[ i + ( ccw ? 2 : 1 ) ];
 
-				} else if ( data.nodeType === 'PointLight' ) {
+					indices.push( i1, i2, i3 );
 
-					var l_distance = 0;
+					// an index of -1 indicates that the current face has ended and the next one begins
 
-					if ( data.radius !== undefined && data.radius < 1000 ) {
+					if ( index[ i + 3 ] === - 1 ) {
 
-						l_distance = data.radius;
+						i += 3;
+						start = i + 1;
 
 					}
 
-					object = new THREE.PointLight( l_color, l_intensity, l_distance );
-					object.visible = l_visible;
+				}
 
-					parent.add( object );
+				return indices;
 
-				} else if ( data.nodeType === 'SpotLight' ) {
+			}
 
-					var l_intensity = 1;
-					var l_distance = 0;
-					var l_angle = Math.PI / 3;
-					var l_penumbra = 0;
-					var l_visible = true;
+			function triangulateFaceData( data, index ) {
 
-					if ( data.radius !== undefined && data.radius < 1000 ) {
+				var triangulatedData = [];
 
-						l_distance = data.radius;
+				var start = 0;
 
-					}
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					if ( data.cutOffAngle !== undefined ) {
+					var stride = start * 3;
 
-						l_angle = data.cutOffAngle;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-					}
+					triangulatedData.push( x, y, z );
 
-					object = new THREE.SpotLight( l_color, l_intensity, l_distance, l_angle, l_penumbra );
-					object.visible = l_visible;
+					// an index of -1 indicates that the current face has ended and the next one begins
 
-					parent.add( object );
+					if ( index[ i + 3 ] === - 1 ) {
 
-				} else if ( data.nodeType === 'Transform' || data.nodeType === 'Group' ) {
+						i += 3;
+						start ++;
 
-					object = new THREE.Object3D();
+					}
 
-					if ( /DEF/.exec( data.string ) ) {
+				}
 
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
-						defines[ object.name ] = object;
+				return triangulatedData;
 
-					}
+			}
 
-					if ( data.translation !== undefined ) {
+			function flattenData( data, index ) {
 
-						var t = data.translation;
+				var flattenData = [];
 
-						object.position.set( t.x, t.y, t.z );
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					}
+					var i1 = index[ i ];
 
-					if ( data.rotation !== undefined ) {
+					var stride = i1 * 3;
 
-						var r = data.rotation;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-						object.quaternion.setFromAxisAngle( new THREE.Vector3( r.x, r.y, r.z ), r.w );
+					flattenData.push( x, y, z );
 
-					}
+				}
 
-					if ( data.scale !== undefined ) {
+				return flattenData;
 
-						var s = data.scale;
+			}
 
-						object.scale.set( s.x, s.y, s.z );
+			function expandLineIndex( index ) {
 
-					}
+				var indices = [];
 
-					parent.add( object );
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-				} else if ( data.nodeType === 'Shape' ) {
+					var i1 = index[ i ];
+					var i2 = index[ i + 1 ];
 
-					object = new THREE.Mesh();
+					indices.push( i1, i2 );
 
-					if ( /DEF/.exec( data.string ) ) {
+					// an index of -1 indicates that the current line has ended and the next one begins
 
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
+					if ( index[ i + 2 ] === - 1 ) {
 
-						defines[ object.name ] = object;
+						i += 2;
 
 					}
 
-					parent.add( object );
+				}
 
-				} else if ( data.nodeType === 'Background' ) {
+				return indices;
 
-					var segments = 20;
+			}
 
-					// sky (full sphere):
+			function expandLineData( data, index ) {
 
-					var radius = 2e4;
+				var triangulatedData = [];
 
-					var skyGeometry = new THREE.SphereBufferGeometry( radius, segments, segments );
-					var skyMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide } );
+				var start = 0;
 
-					if ( data.skyColor.length > 1 ) {
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-						paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true );
+					var stride = start * 3;
 
-						skyMaterial.vertexColors = THREE.VertexColors;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-					} else {
+					triangulatedData.push( x, y, z );
 
-						var color = data.skyColor[ 0 ];
-						skyMaterial.color.setRGB( color.r, color.b, color.g );
+					// an index of -1 indicates that the current line has ended and the next one begins
 
-					}
+					if ( index[ i + 2 ] === - 1 ) {
 
-					scene.add( new THREE.Mesh( skyGeometry, skyMaterial ) );
+						i += 2;
+						start ++;
 
-					// ground (half sphere):
+					}
 
-					if ( data.groundColor !== undefined ) {
+				}
 
-						radius = 1.2e4;
+				return triangulatedData;
 
-						var groundGeometry = new THREE.SphereBufferGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
-						var groundMaterial = new THREE.MeshBasicMaterial( { fog: false, side: THREE.BackSide, vertexColors: THREE.VertexColors } );
+			}
 
-						paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false );
+			var vA = new THREE.Vector3();
+			var vB = new THREE.Vector3();
+			var vC = new THREE.Vector3();
 
-						scene.add( new THREE.Mesh( groundGeometry, groundMaterial ) );
+			var uvA = new THREE.Vector2();
+			var uvB = new THREE.Vector2();
+			var uvC = new THREE.Vector2();
 
-					}
+			function computeAttributeFromIndexedData( coordIndex, index, data, itemSize ) {
 
-				} else if ( /geometry/.exec( data.string ) ) {
+				var array = [];
 
-					if ( data.nodeType === 'Box' ) {
+				// we use the coordIndex.length as delimiter since normalIndex must contain at least as many indices
 
-						var s = data.size;
+				for ( var i = 0, l = coordIndex.length; i < l; i += 3 ) {
 
-						parent.geometry = new THREE.BoxBufferGeometry( s.x, s.y, s.z );
+					var a = index[ i ];
+					var b = index[ i + 1 ];
+					var c = index[ i + 2 ];
 
-					} else if ( data.nodeType === 'Cylinder' ) {
+					if ( itemSize === 2 ) {
 
-						parent.geometry = new THREE.CylinderBufferGeometry( data.radius, data.radius, data.height );
+						uvA.fromArray( data, a * itemSize );
+						uvB.fromArray( data, b * itemSize );
+						uvC.fromArray( data, c * itemSize );
 
-					} else if ( data.nodeType === 'Cone' ) {
+						array.push( uvA.x, uvA.y );
+						array.push( uvB.x, uvB.y );
+						array.push( uvC.x, uvC.y );
 
-						parent.geometry = new THREE.CylinderBufferGeometry( data.topRadius, data.bottomRadius, data.height );
+					} else {
 
-					} else if ( data.nodeType === 'Sphere' ) {
+						vA.fromArray( data, a * itemSize );
+						vB.fromArray( data, b * itemSize );
+						vC.fromArray( data, c * itemSize );
 
-						parent.geometry = new THREE.SphereBufferGeometry( data.radius );
+						array.push( vA.x, vA.y, vA.z );
+						array.push( vB.x, vB.y, vB.z );
+						array.push( vC.x, vC.y, vC.z );
 
-					} else if ( data.nodeType === 'IndexedLineSet' ) {
+					}
 
-						console.warn( 'THREE.VRMLLoader: IndexedLineSet not supported yet.' );
-						parent.parent.remove( parent ); // since the loader is not able to parse the geometry, remove the respective 3D object
+				}
 
-					} else if ( data.nodeType === 'Text' ) {
+				return new THREE.Float32BufferAttribute( array, itemSize );
 
-						console.warn( 'THREE.VRMLLoader: Text not supported yet.' );
-						parent.parent.remove( parent );
+			}
 
-					} else if ( data.nodeType === 'IndexedFaceSet' ) {
+			function computeAttributeFromFaceData( index, faceData ) {
 
-						var geometry = new THREE.BufferGeometry();
+				var array = [];
 
-						var positions = [];
-						var colors = [];
-						var normals = [];
-						var uvs = [];
+				for ( var i = 0, j = 0, l = index.length; i < l; i += 3, j ++ ) {
 
-						var position, color, normal, uv;
+					vA.fromArray( faceData, j * 3 );
 
-						var i, il, j, jl;
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
 
-						for ( i = 0, il = data.children.length; i < il; i ++ ) {
+				}
 
-							var child = data.children[ i ];
+				return new THREE.Float32BufferAttribute( array, 3 );
 
-							// uvs
+			}
 
-							if ( child.nodeType === 'TextureCoordinate' ) {
+			function computeAttributeFromLineData( index, lineData ) {
 
-								if ( child.points ) {
+				var array = [];
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+				for ( var i = 0, j = 0, l = index.length; i < l; i += 2, j ++ ) {
 
-										uv = child.points[ j ];
-										uvs.push( uv.x, uv.y );
+					vA.fromArray( lineData, j * 3 );
 
-									}
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
 
-								}
+				}
 
-							}
+				return new THREE.Float32BufferAttribute( array, 3 );
 
-							// normals
+			}
 
-							if ( child.nodeType === 'Normal' ) {
+			function toNonIndexedAttribute( indices, attribute ) {
 
-								if ( child.points ) {
+				var array = attribute.array;
+				var itemSize = attribute.itemSize;
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+				var array2 = new array.constructor( indices.length * itemSize );
 
-										normal = child.points[ j ];
-										normals.push( normal.x, normal.y, normal.z );
+				var index = 0, index2 = 0;
 
-									}
+				for ( var i = 0, l = indices.length; i < l; i ++ ) {
 
-								}
+					index = indices[ i ] * itemSize;
 
-							}
+					for ( var j = 0; j < itemSize; j ++ ) {
 
-							// colors
+						array2[ index2 ++ ] = array[ index ++ ];
 
-							if ( child.nodeType === 'Color' ) {
+					}
 
-								if ( child.color ) {
+				}
 
-									for ( j = 0, jl = child.color.length; j < jl; j ++ ) {
+				return new THREE.Float32BufferAttribute( array2, itemSize );
 
-										color = child.color[ j ];
-										colors.push( color.r, color.g, color.b );
+			}
 
-									}
+			var ab = new THREE.Vector3();
+			var cb = new THREE.Vector3();
 
-								}
+			function computeNormalAttribute( index, coord, creaseAngle ) {
 
-							}
+				var faces = [];
+				var vertexNormals = {};
 
-							// positions
+				// prepare face and raw vertex normals
 
-							if ( child.nodeType === 'Coordinate' ) {
+				for ( var i = 0, l = index.length; i < l; i += 3 ) {
 
-								if ( child.points ) {
+					var a = index[ i ];
+					var b = index[ i + 1 ];
+					var c = index[ i + 2 ];
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+					var face = new Face( a, b, c );
 
-										position = child.points[ j ];
-										positions.push( position.x, position.y, position.z );
+					vA.fromArray( coord, a * 3 );
+					vB.fromArray( coord, b * 3 );
+					vC.fromArray( coord, c * 3 );
 
-									}
+					cb.subVectors( vC, vB );
+					ab.subVectors( vA, vB );
+					cb.cross( ab );
 
-								}
+					cb.normalize();
 
-								if ( child.string.indexOf( 'DEF' ) > - 1 ) {
+					face.normal.copy( cb );
 
-									var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ];
+					if ( vertexNormals[ a ] === undefined ) vertexNormals[ a ] = [];
+					if ( vertexNormals[ b ] === undefined ) vertexNormals[ b ] = [];
+					if ( vertexNormals[ c ] === undefined ) vertexNormals[ c ] = [];
 
-									defines[ name ] = positions.slice( 0 );
+					vertexNormals[ a ].push( face.normal );
+					vertexNormals[ b ].push( face.normal );
+					vertexNormals[ c ].push( face.normal );
 
-								}
+					faces.push( face );
 
-								if ( child.string.indexOf( 'USE' ) > - 1 ) {
+				}
 
-									var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ];
+				// compute vertex normals and build final geometry
 
-									positions = defines[ defineKey ];
+				var normals = [];
 
-								}
+				for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-							}
+					var face = faces[ i ];
 
-						}
+					var nA = weightedNormal( vertexNormals[ face.a ], face.normal, creaseAngle );
+					var nB = weightedNormal( vertexNormals[ face.b ], face.normal, creaseAngle );
+					var nC = weightedNormal( vertexNormals[ face.c ], face.normal, creaseAngle );
 
-						// some shapes only have vertices for use in other shapes
+					vA.fromArray( coord, face.a * 3 );
+					vB.fromArray( coord, face.b * 3 );
+					vC.fromArray( coord, face.c * 3 );
 
-						if ( data.coordIndex ) {
+					normals.push( nA.x, nA.y, nA.z );
+					normals.push( nB.x, nB.y, nB.z );
+					normals.push( nC.x, nC.y, nC.z );
 
-							function triangulateIndexArray( indexArray, ccw, colorPerVertex ) {
+				}
 
-								if ( ccw === undefined ) {
+				return new THREE.Float32BufferAttribute( normals, 3 );
 
-									// ccw is true by default
-									ccw = true;
+			}
 
-								}
+			function weightedNormal( normals, vector, creaseAngle ) {
 
-								var triangulatedIndexArray = [];
-								var skip = 0;
+				var normal = vector.clone();
 
-								for ( i = 0, il = indexArray.length; i < il; i ++ ) {
+				for ( var i = 0, l = normals.length; i < l; i ++ ) {
 
-									if ( colorPerVertex === false ) {
+					if ( normals[ i ].angleTo( vector ) < creaseAngle ) {
 
-										var colorIndices = indexArray[ i ];
+						normal.add( normals[ i ] );
 
-										for ( j = 0, jl = colorIndices.length; j < jl; j ++ ) {
+					}
 
-											var index = colorIndices[ j ];
+				}
 
-											triangulatedIndexArray.push( index, index, index );
+				return normal.normalize();
 
-										}
+			}
 
-									} else {
+			function toColorArray( colors ) {
 
-										var indexedFace = indexArray[ i ];
+				var array = [];
 
-										// VRML support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
+				for ( var i = 0, l = colors.length; i < l; i += 3 ) {
 
-										skip = 0;
+					array.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
 
-										while ( indexedFace.length >= 3 && skip < ( indexedFace.length - 2 ) ) {
+				}
 
-											var i1 = indexedFace[ 0 ];
-											var i2 = indexedFace[ skip + ( ccw ? 1 : 2 ) ];
-											var i3 = indexedFace[ skip + ( ccw ? 2 : 1 ) ];
+				return array;
 
-											triangulatedIndexArray.push( i1, i2, i3 );
+			}
 
-											skip ++;
+			/**
+			 * Vertically paints the faces interpolating between the
+			 * specified colors at the specified angels. This is used for the Background
+			 * node, but could be applied to other nodes with multiple faces as well.
+			 *
+			 * When used with the Background node, default is directionIsDown is true if
+			 * interpolating the skyColor down from the Zenith. When interpolationg up from
+			 * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
+			 *
+			 * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
+			 * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
+			 * is linear along the Y axis in any case.
+			 *
+			 * You must specify one more color than you have angles at the beginning of the colors array.
+			 * This is the color of the Zenith (the top of the shape).
+			 *
+			 * @param {BufferGeometry} geometry
+			 * @param {number} radius
+			 * @param {array} angles
+			 * @param {array} colors
+			 * @param {boolean} topDown - Whether to work top down or bottom up.
+			 */
+			function paintFaces( geometry, radius, angles, colors, topDown ) {
 
-										}
+				var direction = ( topDown === true ) ? 1 : - 1;
 
-									}
+				var coord = [], A = {}, B = {}, applyColor = false;
 
-								}
+				for ( var k = 0; k < angles.length; k ++ ) {
 
-								return triangulatedIndexArray;
+					// push the vector at which the color changes
 
-							}
+					var vec = {
+						x: direction * ( Math.cos( angles[ k ] ) * radius ),
+						y: direction * ( Math.sin( angles[ k ] ) * radius )
+					};
 
-							var positionIndexes = data.coordIndex ? triangulateIndexArray( data.coordIndex, data.ccw ) : [];
-							var normalIndexes = data.normalIndex ? triangulateIndexArray( data.normalIndex, data.ccw ) : positionIndexes;
-							var colorIndexes = data.colorIndex ? triangulateIndexArray( data.colorIndex, data.ccw, data.colorPerVertex ) : [];
-							var uvIndexes = data.texCoordIndex ? triangulateIndexArray( data.texCoordIndex, data.ccw ) : positionIndexes;
+					coord.push( vec );
 
-							var newIndexes = [];
-							var newPositions = [];
-							var newNormals = [];
-							var newColors = [];
-							var newUvs = [];
+				}
 
-							// if any other index array does not match the coordinate indexes, split any points that differ
+				var index = geometry.index;
+				var positionAttribute = geometry.attributes.position;
+				var colorAttribute = new THREE.BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
 
-							var pointMap = Object.create( null );
+				var position = new THREE.Vector3();
+				var color = new THREE.Color();
 
-							for ( i = 0; i < positionIndexes.length; i ++ ) {
+				for ( var i = 0; i < index.count; i ++ ) {
 
-								var pointAttributes = [];
+					var vertexIndex = index.getX( i );
 
-								var positionIndex = positionIndexes[ i ];
-								var normalIndex = normalIndexes[ i ];
-								var colorIndex = colorIndexes[ i ];
-								var uvIndex = uvIndexes[ i ];
+					position.fromBufferAttribute( positionAttribute, vertexIndex );
 
-								var base = 10; // which base to use to represent each value
+					for ( var j = 0; j < colors.length; j ++ ) {
 
-								pointAttributes.push( positionIndex.toString( base ) );
+						// linear interpolation between aColor and bColor, calculate proportion
+						// A is previous point (angle)
 
-								if ( normalIndex !== undefined ) {
+						if ( j === 0 ) {
 
-									pointAttributes.push( normalIndex.toString( base ) );
+							A.x = 0;
+							A.y = ( topDown === true ) ? radius : - 1 * radius;
 
-								}
+						} else {
 
-								if ( colorIndex !== undefined ) {
+							A.x = coord[ j - 1 ].x;
+							A.y = coord[ j - 1 ].y;
 
-									pointAttributes.push( colorIndex.toString( base ) );
+						}
 
-								}
+						// B is current point (angle)
 
-								if ( uvIndex !== undefined ) {
+						B = coord[ j ];
 
-									pointAttributes.push( uvIndex.toString( base ) );
+						if ( B !== undefined ) {
 
-								}
+							// p has to be between the points A and B which we interpolate
 
-								var pointId = pointAttributes.join( ',' );
-								var newIndex = pointMap[ pointId ];
+							applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
 
-								if ( newIndex === undefined ) {
+							if ( applyColor === true ) {
 
-									newIndex = newPositions.length / 3;
-									pointMap[ pointId ] = newIndex;
+								var aColor = colors[ j ];
+								var bColor = colors[ j + 1 ];
 
-									newPositions.push(
-										positions[ positionIndex * 3 ],
-										positions[ positionIndex * 3 + 1 ],
-										positions[ positionIndex * 3 + 2 ]
-									);
+								// below is simple linear interpolation
 
-									if ( normalIndex !== undefined && normals.length > 0 ) {
+								var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
 
-										newNormals.push(
-											normals[ normalIndex * 3 ],
-											normals[ normalIndex * 3 + 1 ],
-											normals[ normalIndex * 3 + 2 ]
-										);
+								// to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
 
-									}
+								color.copy( aColor ).lerp( bColor, t );
 
-									if ( colorIndex !== undefined && colors.length > 0 ) {
+								colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
 
-										newColors.push(
-											colors[ colorIndex * 3 ],
-											colors[ colorIndex * 3 + 1 ],
-											colors[ colorIndex * 3 + 2 ]
-										);
+							} else {
 
-									}
+								var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
+								var c = colors[ colorIndex ];
+								colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
 
-									if ( uvIndex !== undefined && uvs.length > 0 ) {
+							}
 
-										newUvs.push(
-											uvs[ uvIndex * 2 ],
-											uvs[ uvIndex * 2 + 1 ]
-										);
+						}
 
-									}
+					}
 
-								}
+				}
 
-								newIndexes.push( newIndex );
+				geometry.addAttribute( 'color', colorAttribute );
 
-							}
+			}
 
-							positions = newPositions;
-							normals = newNormals;
-							colors = newColors;
-							uvs = newUvs;
+			//
 
-							geometry.setIndex( newIndexes );
+			var textureLoader = new THREE.TextureLoader( this.manager );
+			textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 
-						} else {
+			// create JSON representing the tree structure of the VRML asset
 
-							// do not add dummy mesh to the scene
+			var tree = generateVRMLTree( data );
 
-							parent.parent.remove( parent );
+			// check version (only 2.0 is supported)
 
-						}
+			if ( tree.version.indexOf( 'V2.0' ) === - 1 ) {
 
-						if ( false === data.solid ) {
+				throw Error( 'THREE.VRMLLexer: Version of VRML asset not supported.' );
 
-							parent.material.side = THREE.DoubleSide;
+			}
 
-						}
+			// parse the tree structure to a three.js scene
 
-						// we need to store it on the geometry for use with defines
-						geometry.solid = data.solid;
+			var scene = parseTree( tree );
 
-						geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
+			return scene;
 
-						if ( colors.length > 0 ) {
+		}
 
-							geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
+	};
 
-							parent.material.vertexColors = THREE.VertexColors;
+	function VRMLLexer( tokens ) {
 
-						}
+		this.lexer = new chevrotain.Lexer( tokens );
 
-						if ( uvs.length > 0 ) {
+	}
 
-							geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+	VRMLLexer.prototype = {
 
-						}
+		constructor: VRMLLexer,
 
-						if ( normals.length > 0 ) {
+		lex: function ( inputText ) {
 
-							geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+			var lexingResult = this.lexer.tokenize( inputText );
 
-						} else {
+			if ( lexingResult.errors.length > 0 ) {
 
-							// convert geometry to non-indexed to get sharp normals
-							geometry = geometry.toNonIndexed();
-							geometry.computeVertexNormals();
+				console.error( lexingResult.errors );
 
-						}
+				throw Error( 'THREE.VRMLLexer: Lexing errors detected.' );
 
-						geometry.computeBoundingSphere();
+			}
 
-						// see if it's a define
-						if ( /DEF/.exec( data.string ) ) {
+			return lexingResult;
 
-							geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
-							defines[ geometry.name ] = geometry;
+		}
 
-						}
+	};
 
-						parent.geometry = geometry;
+	function VRMLParser( tokenVocabulary ) {
 
-					}
+		chevrotain.Parser.call( this, tokenVocabulary );
 
-					return;
+		var $ = this;
 
-				} else if ( /appearance/.exec( data.string ) ) {
+		var Version = tokenVocabulary[ 'Version' ];
+		var LCurly = tokenVocabulary[ 'LCurly' ];
+		var RCurly = tokenVocabulary[ 'RCurly' ];
+		var LSquare = tokenVocabulary[ 'LSquare' ];
+		var RSquare = tokenVocabulary[ 'RSquare' ];
+		var Identifier = tokenVocabulary[ 'Identifier' ];
+		var RouteIdentifier = tokenVocabulary[ 'RouteIdentifier' ];
+		var StringLiteral = tokenVocabulary[ 'StringLiteral' ];
+		var NumberLiteral = tokenVocabulary[ 'NumberLiteral' ];
+		var BooleanLiteral = tokenVocabulary[ 'BooleanLiteral' ];
+		var NullLiteral = tokenVocabulary[ 'NullLiteral' ];
+		var DEF = tokenVocabulary[ 'DEF' ];
+		var USE = tokenVocabulary[ 'USE' ];
+		var ROUTE = tokenVocabulary[ 'ROUTE' ];
+		var TO = tokenVocabulary[ 'TO' ];
+		var NodeName = tokenVocabulary[ 'NodeName' ];
 
-					for ( var i = 0; i < data.children.length; i ++ ) {
+		$.RULE( 'vrml', function () {
 
-						var child = data.children[ i ];
+			$.SUBRULE( $.version );
+			$.AT_LEAST_ONE( function () {
 
-						if ( child.nodeType === 'Material' ) {
+				$.SUBRULE( $.node );
 
-							var material = new THREE.MeshPhongMaterial();
+			} );
+			$.MANY( function () {
 
-							if ( child.diffuseColor !== undefined ) {
+				$.SUBRULE( $.route );
 
-								var d = child.diffuseColor;
+			} );
 
-								material.color.setRGB( d.r, d.g, d.b );
+		} );
 
-							}
+		$.RULE( 'version', function () {
 
-							if ( child.emissiveColor !== undefined ) {
+			$.CONSUME( Version );
 
-								var e = child.emissiveColor;
+		} );
 
-								material.emissive.setRGB( e.r, e.g, e.b );
+		$.RULE( 'node', function () {
 
-							}
+			$.OPTION( function () {
 
-							if ( child.specularColor !== undefined ) {
+				$.SUBRULE( $.def );
 
-								var s = child.specularColor;
+			} );
 
-								material.specular.setRGB( s.r, s.g, s.b );
+			$.CONSUME( NodeName );
+			$.CONSUME( LCurly );
+			$.MANY( function () {
 
-							}
+				$.SUBRULE( $.field );
 
-							if ( child.transparency !== undefined ) {
+			} );
+			$.CONSUME( RCurly );
 
-								var t = child.transparency;
+		} );
 
-								// transparency is opposite of opacity
-								material.opacity = Math.abs( 1 - t );
+		$.RULE( 'field', function () {
 
-								material.transparent = true;
+			$.CONSUME( Identifier );
 
-							}
+			$.OR2( [
+				{ ALT: function () {
 
-							if ( /DEF/.exec( data.string ) ) {
+					$.SUBRULE( $.singleFieldValue );
 
-								material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
+				} },
+				{ ALT: function () {
 
-								defines[ material.name ] = material;
+					$.SUBRULE( $.multiFieldValue );
 
-							}
+				} }
+			] );
 
-							parent.material = material;
+		} );
 
-						}
+		$.RULE( 'def', function () {
 
-						if ( child.nodeType === 'ImageTexture' ) {
+			$.CONSUME( DEF );
+			$.CONSUME( Identifier );
 
-							var textureName = /"([^"]+)"/.exec( child.children[ 0 ] );
+		} );
 
-							if ( textureName ) {
+		$.RULE( 'use', function () {
 
-								parent.material.name = textureName[ 1 ];
+			$.CONSUME( USE );
+			$.CONSUME( Identifier );
 
-								parent.material.map = textureLoader.load( textureName[ 1 ] );
+		} );
 
-							}
+		$.RULE( 'singleFieldValue', function () {
 
-						}
+			$.AT_LEAST_ONE( function () {
 
-					}
+				$.OR( [
+					{ ALT: function () {
 
-					return;
+						$.SUBRULE( $.node );
 
-				}
+					} },
+					{ ALT: function () {
 
-				for ( var i = 0, l = data.children.length; i < l; i ++ ) {
+						$.SUBRULE( $.use );
 
-					parseNode( data.children[ i ], object );
+					} },
+					{ ALT: function () {
 
-				}
+						$.CONSUME( StringLiteral );
 
-			}
+					} },
+					{ ALT: function () {
 
-			parseNode( getTree( lines ), scene );
+						$.CONSUME( NumberLiteral );
 
-		}
+					} },
+					{ ALT: function () {
 
-		var scene = new THREE.Scene();
+						$.CONSUME( BooleanLiteral );
 
-		var lines = data.split( '\n' );
+					} },
+					{ ALT: function () {
 
-		// some lines do not have breaks
+						$.CONSUME( NullLiteral );
 
-		for ( var i = lines.length - 1; i > 0; i -- ) {
+					} }
+				] );
 
-			// The # symbol indicates that all subsequent text, until the end of the line is a comment,
-			// and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html)
-			lines[ i ] = lines[ i ].replace( /(#.*)/, '' );
 
-			var line = lines[ i ];
+			} );
 
-			// split lines with {..{ or {..[ - some have both
-			if ( /{.*[{\[]/.test( line ) ) {
+		} );
 
-				var parts = line.split( '{' ).join( '{\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+		$.RULE( 'multiFieldValue', function () {
 
-			} else if ( /\].*}/.test( line ) ) {
+			$.CONSUME( LSquare );
+			$.MANY( function () {
 
-				// split lines with ]..}
-				var parts = line.split( ']' ).join( ']\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+				$.OR( [
+					{ ALT: function () {
 
-			}
+						$.SUBRULE( $.node );
 
-			line = lines[ i ];
+					} },
+					{ ALT: function () {
 
-			if ( /}.*}/.test( line ) ) {
+						$.SUBRULE( $.use );
 
-				// split lines with }..}
-				var parts = line.split( '}' ).join( '}\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+					} },
+					{ ALT: function () {
 
-			}
+						$.CONSUME( StringLiteral );
 
-			line = lines[ i ];
+					} },
+					{ ALT: function () {
 
-			if ( /^\b[^\s]+\b$/.test( line.trim() ) ) {
+						$.CONSUME( NumberLiteral );
 
-				// prevent lines with single words like "coord" or "geometry", see #12209
-				lines[ i + 1 ] = line + ' ' + lines[ i + 1 ].trim();
-				lines.splice( i, 1 );
+					} },
+					{ ALT: function () {
 
-			} else if ( ( line.indexOf( 'coord' ) > - 1 ) && ( line.indexOf( '[' ) < 0 ) && ( line.indexOf( '{' ) < 0 ) ) {
+						$.CONSUME( NullLiteral );
 
-				// force the parser to create Coordinate node for empty coords
-				// coord USE something -> coord USE something Coordinate {}
+					} }
+				] );
 
-				lines[ i ] += ' Coordinate {}';
+			} );
+			$.CONSUME( RSquare );
 
-			}
+		} );
 
-		}
+		$.RULE( 'route', function () {
 
-		var header = lines.shift();
+			$.CONSUME( ROUTE );
+			$.CONSUME( RouteIdentifier );
+			$.CONSUME( TO );
+			$.CONSUME2( RouteIdentifier );
 
-		if ( /V1.0/.exec( header ) ) {
+		} );
 
-			console.warn( 'THREE.VRMLLoader: V1.0 not supported yet.' );
+		this.performSelfAnalysis();
 
-		} else if ( /V2.0/.exec( header ) ) {
+	}
 
-			parseV2( lines, scene );
+	VRMLParser.prototype = Object.create( chevrotain.Parser.prototype );
+	VRMLParser.prototype.constructor = VRMLParser;
 
-		}
+	function Face( a, b, c ) {
 
-		return scene;
+		this.a = a;
+		this.b = b;
+		this.c = c;
+		this.normal = new THREE.Vector3();
 
 	}
 
-};
+	return VRMLLoader;
+
+} )();

+ 18 - 35
examples/js/loaders/XLoader.js

@@ -2,16 +2,7 @@
  * @author adrs2002 / https://github.com/adrs2002
  */
 
-
-( function ( global, factory ) {
-
-	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
-		typeof define === 'function' && define.amd ? define( factory ) :
-			( global.THREE = global.THREE || {}, global.THREE.XLoader = factory() );
-
-}( this, ( function () {
-
-	'use strict';
+THREE.XLoader = ( function () {
 
 	var classCallCheck = function ( instance, Constructor ) {
 
@@ -283,7 +274,7 @@
 				loader.setResponseType( 'arraybuffer' );
 				loader.load( this.url, function ( response ) {
 
-					_this._parse( response, onLoad );
+					_this.parse( response, onLoad );
 
 				}, onProgress, onError );
 
@@ -311,14 +302,6 @@
 				this.resourcePath = value;
 				return this;
 
-			}
-		}, {
-			key: 'fromResponsedData',
-			value: function fromResponsedData( _data, _arg, onLoad ) {
-
-				this._setArgOption( _arg );
-				this._parse( _data, onLoad );
-
 			}
 		}, {
 			key: '_readLine',
@@ -441,8 +424,8 @@
 
 			}
 		}, {
-			key: 'ensureBinary',
-			value: function ensureBinary( buf ) {
+			key: '_ensureBinary',
+			value: function _ensureBinary( buf ) {
 
 				if ( typeof buf === "string" ) {
 
@@ -462,8 +445,8 @@
 
 			}
 		}, {
-			key: 'ensureString',
-			value: function ensureString( buf ) {
+			key: '_ensureString',
+			value: function _ensureString( buf ) {
 
 				if ( typeof buf !== "string" ) {
 
@@ -477,11 +460,11 @@
 
 			}
 		}, {
-			key: '_parse',
+			key: 'parse',
 			value: function _parse( data, onLoad ) {
 
-				var binData = this.ensureBinary( data );
-				this._data = this.ensureString( data );
+				var binData = this._ensureBinary( data );
+				this._data = this._ensureString( data );
 				this.onLoad = onLoad;
 				return this._isBinary( binData ) ? this._parseBinary( binData ) : this._parseASCII();
 
@@ -520,7 +503,7 @@
 				this._hierarchieParse( this.Hierarchies, endRead );
 				this._changeRoot();
 				this._currentObject = this.Hierarchies.children.shift();
-				this.mainloop();
+				this._mainloop();
 
 			}
 		}, {
@@ -605,17 +588,17 @@
 
 			}
 		}, {
-			key: 'mainloop',
-			value: function mainloop() {
+			key: '_mainloop',
+			value: function _mainloop() {
 
 				var _this2 = this;
 
-				this.mainProc();
+				this._mainProc();
 				if ( this._currentObject.parent || this._currentObject.children.length > 0 || ! this._currentObject.worked ) {
 
 					setTimeout( function () {
 
-						_this2.mainloop();
+						_this2._mainloop();
 
 					}, 1 );
 
@@ -634,8 +617,8 @@
 
 			}
 		}, {
-			key: 'mainProc',
-			value: function mainProc() {
+			key: '_mainProc',
+			value: function _mainProc() {
 
 				var breakFlag = false;
 				while ( true ) {
@@ -1573,7 +1556,7 @@
 			}
 		}, {
 			key: 'assignAnimation',
-			value: function assignAnimation( _model, _animation, _isBind ) {
+			value: function assignAnimation( _model, _animation ) {
 
 				var model = _model;
 				var animation = _animation;
@@ -1686,4 +1669,4 @@
 
 	return XLoader;
 
-} ) ) );
+} )();

+ 24 - 0
examples/js/loaders/ctm/CTMLoader.js

@@ -6,6 +6,30 @@
  *	http://code.google.com/p/js-openctm/
  *
  * @author alteredq / http://alteredqualia.com/
+ *
+ * OpenCTM LICENSE:
+ *
+ * Copyright (c) 2009-2010 Marcus Geelnard
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ *     1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ *
+ *    2. Altered source versions must be plainly marked as such, and must not
+ *    be misrepresented as being the original software.
+ *
+ *    3. This notice may not be removed or altered from any source
+ *    distribution.
+ *
  */
 
 THREE.CTMLoader = function () {

+ 1 - 1
examples/js/loaders/ctm/CTMWorker.js

@@ -1,4 +1,4 @@
-importScripts( "lzma.js", "ctm.js" );
+importScripts( "../../libs/lzma.js", "../../libs/ctm.js" );
 
 self.onmessage = function ( event ) {
 

+ 0 - 20
examples/js/loaders/ctm/license/OpenCTM.txt

@@ -1,20 +0,0 @@
-Copyright (c) 2009-2010 Marcus Geelnard
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-    1. The origin of this software must not be misrepresented; you must not
-    claim that you wrote the original software. If you use this software
-    in a product, an acknowledgment in the product documentation would be
-    appreciated but is not required.
-
-    2. Altered source versions must be plainly marked as such, and must not
-    be misrepresented as being the original software.
-
-    3. This notice may not be removed or altered from any source
-    distribution.

+ 0 - 19
examples/js/loaders/ctm/license/js-lzma.txt

@@ -1,19 +0,0 @@
-Copyright (c) 2011 Juan Mellado
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

+ 0 - 19
examples/js/loaders/ctm/license/js-openctm.txt

@@ -1,19 +0,0 @@
-Copyright (c) 2011 Juan Mellado
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

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

@@ -2470,7 +2470,9 @@ THREE.SEA3D.prototype.readCubeMapURL = function ( sea ) {
 
 		this.file.resume = ! usePMREM;
 
-		texture = new THREE.HDRCubeTextureLoader().load( THREE.UnsignedByteType, faces, function ( texture ) {
+		texture = new THREE.HDRCubeTextureLoader()
+			.setType( THREE.UnsignedByteType )
+			.load( faces, function ( texture ) {
 
 			if ( usePMREM ) {
 

+ 5 - 5
examples/js/Car.js → examples/js/misc/CarControls.js

@@ -9,7 +9,7 @@
  *
  */
 
-THREE.Car = ( function ( ) {
+THREE.CarControls = ( function ( ) {
 
 	// private variables
 	var steeringWheelSpeed = 1.5;
@@ -51,7 +51,7 @@ THREE.Car = ( function ( ) {
 
 	};
 
-	function Car( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
+	function CarControls( maxSpeed, acceleration, brakePower, turningRadius, keys ) {
 
 		this.enabled = true;
 
@@ -96,9 +96,9 @@ THREE.Car = ( function ( ) {
 
 	}
 
-	Car.prototype = {
+	CarControls.prototype = {
 
-		constructor: Car,
+		constructor: CarControls,
 
 		onKeyDown: function ( event ) {
 
@@ -300,6 +300,6 @@ THREE.Car = ( function ( ) {
 
 	}
 
-	return Car;
+	return CarControls;
 
 } )();

+ 0 - 0
examples/js/ConvexObjectBreaker.js → examples/js/misc/ConvexObjectBreaker.js


+ 5 - 5
examples/js/GPUComputationRenderer.js → examples/js/misc/GPUComputationRenderer.js

@@ -28,7 +28,7 @@
  * // Initialization...
  *
  * // Create computation renderer
- * var gpuCompute = new GPUComputationRenderer( 1024, 1024, renderer );
+ * var gpuCompute = new THREE.GPUComputationRenderer( 1024, 1024, renderer );
  *
  * // Create initial state float textures
  * var pos0 = gpuCompute.createTexture();
@@ -89,7 +89,7 @@
  * // And compute each frame, before rendering to screen:
  * gpuCompute.doRenderTarget( myFilter1, myRenderTarget );
  * gpuCompute.doRenderTarget( myFilter2, outputRenderTarget );
- * 
+ *
  *
  *
  * @param {int} sizeX Computation problem size is always 2d: sizeX * sizeY elements.
@@ -97,7 +97,7 @@
  * @param {WebGLRenderer} renderer The renderer
   */
 
-function GPUComputationRenderer( sizeX, sizeY, renderer ) {
+THREE.GPUComputationRenderer = function( sizeX, sizeY, renderer ) {
 
 	this.variables = [];
 
@@ -137,7 +137,7 @@ function GPUComputationRenderer( sizeX, sizeY, renderer ) {
 		this.variables.push( variable );
 
 		return variable;
-		
+
 	};
 
 	this.setVariableDependencies = function( variable, dependencies ) {
@@ -371,4 +371,4 @@ function GPUComputationRenderer( sizeX, sizeY, renderer ) {
 
 	}
 
-}
+};

+ 0 - 0
examples/js/Gyroscope.js → examples/js/misc/Gyroscope.js


+ 1 - 1
examples/js/MD2Character.js → examples/js/misc/MD2Character.js

@@ -46,7 +46,7 @@ THREE.MD2Character = function () {
 
 			var boundingBox = new THREE.Box3();
 			boundingBox.setFromBufferAttribute( geo.attributes.position );
-			
+
 			scope.root.position.y = - scope.scale * boundingBox.min.y;
 
 			var mesh = createPart( geo, scope.skinsBody[ 0 ] );

+ 9 - 9
examples/js/MD2CharacterComplex.js → examples/js/misc/MD2CharacterComplex.js

@@ -154,7 +154,7 @@ THREE.MD2CharacterComplex = function () {
 
 		var loader = new THREE.MD2Loader();
 
-		loader.load( config.baseUrl + config.body, function( geo ) {
+		loader.load( config.baseUrl + config.body, function ( geo ) {
 
 			var boundingBox = new THREE.Box3();
 			boundingBox.setFromBufferAttribute( geo.attributes.position );
@@ -177,7 +177,7 @@ THREE.MD2CharacterComplex = function () {
 
 		var generateCallback = function ( index, name ) {
 
-			return function( geo ) {
+			return function ( geo ) {
 
 				var mesh = createPart( geo, scope.skinsWeapon[ index ] );
 				mesh.scale.set( scope.scale, scope.scale, scope.scale );
@@ -193,7 +193,7 @@ THREE.MD2CharacterComplex = function () {
 
 				checkLoadingComplete();
 
-			}
+			};
 
 		};
 
@@ -228,7 +228,7 @@ THREE.MD2CharacterComplex = function () {
 
 	};
 
-	this.setSkin = function( index ) {
+	this.setSkin = function ( index ) {
 
 		if ( this.meshBody && this.meshBody.material.wireframe === false ) {
 
@@ -293,7 +293,7 @@ THREE.MD2CharacterComplex = function () {
 
 		if ( this.animations ) {
 
-			this.updateBehaviors( delta );
+			this.updateBehaviors();
 			this.updateAnimations( delta );
 
 		}
@@ -316,7 +316,7 @@ THREE.MD2CharacterComplex = function () {
 			this.meshBody.update( delta );
 
 			this.meshBody.setAnimationWeight( this.activeAnimation, mix );
-			this.meshBody.setAnimationWeight( this.oldAnimation,  1 - mix );
+			this.meshBody.setAnimationWeight( this.oldAnimation, 1 - mix );
 
 		}
 
@@ -325,13 +325,13 @@ THREE.MD2CharacterComplex = function () {
 			this.meshWeapon.update( delta );
 
 			this.meshWeapon.setAnimationWeight( this.activeAnimation, mix );
-			this.meshWeapon.setAnimationWeight( this.oldAnimation,  1 - mix );
+			this.meshWeapon.setAnimationWeight( this.oldAnimation, 1 - mix );
 
 		}
 
 	};
 
-	this.updateBehaviors = function ( delta ) {
+	this.updateBehaviors = function () {
 
 		var controls = this.controls;
 		var animations = this.animations;
@@ -451,7 +451,7 @@ THREE.MD2CharacterComplex = function () {
 
 		this.maxReverseSpeed = - this.maxSpeed;
 
-		if ( controls.moveForward )  this.speed = THREE.Math.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
+		if ( controls.moveForward ) this.speed = THREE.Math.clamp( this.speed + delta * this.frontAcceleration, this.maxReverseSpeed, this.maxSpeed );
 		if ( controls.moveBackward ) this.speed = THREE.Math.clamp( this.speed - delta * this.backAcceleration, this.maxReverseSpeed, this.maxSpeed );
 
 		// orientation based on controls

+ 4 - 3
examples/js/MorphAnimMesh.js → examples/js/misc/MorphAnimMesh.js

@@ -10,6 +10,7 @@ THREE.MorphAnimMesh = function ( geometry, material ) {
 
 	this.mixer = new THREE.AnimationMixer( this );
 	this.activeAction = null;
+
 };
 
 THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype );
@@ -23,17 +24,17 @@ THREE.MorphAnimMesh.prototype.setDirectionForward = function () {
 
 THREE.MorphAnimMesh.prototype.setDirectionBackward = function () {
 
-	this.mixer.timeScale = -1.0;
+	this.mixer.timeScale = - 1.0;
 
 };
 
 THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) {
 
-	if( this.activeAction ) {
+	if ( this.activeAction ) {
 
 		this.activeAction.stop();
 		this.activeAction = null;
-		
+
 	}
 
 	var clip = THREE.AnimationClip.findByName( this, label );

+ 0 - 0
examples/js/MorphBlendMesh.js → examples/js/misc/MorphBlendMesh.js


+ 12 - 8
examples/js/Ocean.js → examples/js/misc/Ocean.js

@@ -1,3 +1,7 @@
+/*
+	three.js Ocean
+*/
+
 THREE.Ocean = function ( renderer, camera, scene, options ) {
 
 	// flag used to trigger parameter changes
@@ -87,10 +91,10 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	////////////////////////////////////////
 
 	// 0 - The vertex shader used in all of the simulation steps
-	var fullscreeenVertexShader = THREE.ShaderLib[ "ocean_sim_vertex" ];
+	var fullscreeenVertexShader = THREE.OceanShaders[ "ocean_sim_vertex" ];
 
 	// 1 - Horizontal wave vertices used for FFT
-	var oceanHorizontalShader = THREE.ShaderLib[ "ocean_subtransform" ];
+	var oceanHorizontalShader = THREE.OceanShaders[ "ocean_subtransform" ];
 	var oceanHorizontalUniforms = THREE.UniformsUtils.clone( oceanHorizontalShader.uniforms );
 	this.materialOceanHorizontal = new THREE.ShaderMaterial( {
 		uniforms: oceanHorizontalUniforms,
@@ -103,7 +107,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialOceanHorizontal.depthTest = false;
 
 	// 2 - Vertical wave vertices used for FFT
-	var oceanVerticalShader = THREE.ShaderLib[ "ocean_subtransform" ];
+	var oceanVerticalShader = THREE.OceanShaders[ "ocean_subtransform" ];
 	var oceanVerticalUniforms = THREE.UniformsUtils.clone( oceanVerticalShader.uniforms );
 	this.materialOceanVertical = new THREE.ShaderMaterial( {
 		uniforms: oceanVerticalUniforms,
@@ -116,7 +120,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialOceanVertical.depthTest = false;
 
 	// 3 - Initial spectrum used to generate height map
-	var initialSpectrumShader = THREE.ShaderLib[ "ocean_initial_spectrum" ];
+	var initialSpectrumShader = THREE.OceanShaders[ "ocean_initial_spectrum" ];
 	var initialSpectrumUniforms = THREE.UniformsUtils.clone( initialSpectrumShader.uniforms );
 	this.materialInitialSpectrum = new THREE.ShaderMaterial( {
 		uniforms: initialSpectrumUniforms,
@@ -128,7 +132,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialInitialSpectrum.depthTest = false;
 
 	// 4 - Phases used to animate heightmap
-	var phaseShader = THREE.ShaderLib[ "ocean_phase" ];
+	var phaseShader = THREE.OceanShaders[ "ocean_phase" ];
 	var phaseUniforms = THREE.UniformsUtils.clone( phaseShader.uniforms );
 	this.materialPhase = new THREE.ShaderMaterial( {
 		uniforms: phaseUniforms,
@@ -139,7 +143,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialPhase.depthTest = false;
 
 	// 5 - Shader used to update spectrum
-	var spectrumShader = THREE.ShaderLib[ "ocean_spectrum" ];
+	var spectrumShader = THREE.OceanShaders[ "ocean_spectrum" ];
 	var spectrumUniforms = THREE.UniformsUtils.clone( spectrumShader.uniforms );
 	this.materialSpectrum = new THREE.ShaderMaterial( {
 		uniforms: spectrumUniforms,
@@ -151,7 +155,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialSpectrum.depthTest = false;
 
 	// 6 - Shader used to update spectrum normals
-	var normalShader = THREE.ShaderLib[ "ocean_normals" ];
+	var normalShader = THREE.OceanShaders[ "ocean_normals" ];
 	var normalUniforms = THREE.UniformsUtils.clone( normalShader.uniforms );
 	this.materialNormal = new THREE.ShaderMaterial( {
 		uniforms: normalUniforms,
@@ -163,7 +167,7 @@ THREE.Ocean = function ( renderer, camera, scene, options ) {
 	this.materialNormal.depthTest = false;
 
 	// 7 - Shader used to update normals
-	var oceanShader = THREE.ShaderLib[ "ocean_main" ];
+	var oceanShader = THREE.OceanShaders[ "ocean_main" ];
 	var oceanUniforms = THREE.UniformsUtils.clone( oceanShader.uniforms );
 	this.materialOcean = new THREE.ShaderMaterial( {
 		uniforms: oceanUniforms,

+ 15 - 15
examples/js/RollerCoaster.js → examples/js/misc/RollerCoaster.js

@@ -2,7 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-function RollerCoasterGeometry( curve, divisions ) {
+THREE.RollerCoasterGeometry = function ( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -210,11 +210,11 @@ function RollerCoasterGeometry( curve, divisions ) {
 	this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
 	this.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );
 
-}
+};
 
-RollerCoasterGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.RollerCoasterGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-function RollerCoasterLiftersGeometry( curve, divisions ) {
+THREE.RollerCoasterLiftersGeometry = function ( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -384,11 +384,11 @@ function RollerCoasterLiftersGeometry( curve, divisions ) {
 	this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
 	this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
 
-}
+};
 
-RollerCoasterLiftersGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.RollerCoasterLiftersGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-function RollerCoasterShadowGeometry( curve, divisions ) {
+THREE.RollerCoasterShadowGeometry = function ( curve, divisions ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -454,11 +454,11 @@ function RollerCoasterShadowGeometry( curve, divisions ) {
 
 	this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
 
-}
+};
 
-RollerCoasterShadowGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.RollerCoasterShadowGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-function SkyGeometry() {
+THREE.SkyGeometry = function () {
 
 	THREE.BufferGeometry.call( this );
 
@@ -485,11 +485,11 @@ function SkyGeometry() {
 
 	this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
 
-}
+};
 
-SkyGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.SkyGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
 
-function TreesGeometry( landscape ) {
+THREE.TreesGeometry = function ( landscape ) {
 
 	THREE.BufferGeometry.call( this );
 
@@ -539,6 +539,6 @@ function TreesGeometry( landscape ) {
 	this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
 	this.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );
 
-}
+};
 
-TreesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
+THREE.TreesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );

+ 0 - 0
examples/js/Volume.js → examples/js/misc/Volume.js


+ 0 - 0
examples/js/VolumeSlice.js → examples/js/misc/VolumeSlice.js


+ 0 - 0
examples/js/GPUParticleSystem.js → examples/js/objects/GPUParticleSystem.js


+ 0 - 2
examples/js/MarchingCubes.js → examples/js/objects/MarchingCubes.js

@@ -563,7 +563,6 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 
 			} catch ( err ) {
 
-				userDefineColor = false;
 				ballColor = new THREE.Color( ballx, bally, ballz );
 
 			}
@@ -974,7 +973,6 @@ THREE.MarchingCubes = function ( resolution, material, enableUvs, enableColors )
 THREE.MarchingCubes.prototype = Object.create( THREE.ImmediateRenderObject.prototype );
 THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes;
 
-
 /////////////////////////////////////
 // Marching cubes lookup tables
 /////////////////////////////////////

+ 10 - 9
examples/js/shaders/OceanShaders.js

@@ -1,4 +1,4 @@
-// Author: Aleksandr Albert
+/* Author: Aleksandr Albert
 // Website: www.routter.co.tt
 
 // Description: A deep water ocean shader set
@@ -20,9 +20,10 @@
 
 // -- Rendering Shader
 // [7] ocean_main               -> Vertex and Fragment shader used to create the final render
+*/
 
-
-THREE.ShaderLib[ 'ocean_sim_vertex' ] = {
+THREE.OceanShaders = {}
+THREE.OceanShaders[ 'ocean_sim_vertex' ] = {
 	vertexShader: [
 		'varying vec2 vUV;',
 
@@ -32,7 +33,7 @@ THREE.ShaderLib[ 'ocean_sim_vertex' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_subtransform' ] = {
+THREE.OceanShaders[ 'ocean_subtransform' ] = {
 	uniforms: {
 		"u_input": { value: null },
 		"u_transformSize": { value: 512.0 },
@@ -82,7 +83,7 @@ THREE.ShaderLib[ 'ocean_subtransform' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
+THREE.OceanShaders[ 'ocean_initial_spectrum' ] = {
 	uniforms: {
 		"u_wind": { value: new THREE.Vector2( 10.0, 10.0 ) },
 		"u_resolution": { value: 512.0 },
@@ -163,7 +164,7 @@ THREE.ShaderLib[ 'ocean_initial_spectrum' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_phase' ] = {
+THREE.OceanShaders[ 'ocean_phase' ] = {
 	uniforms: {
 		"u_phases": { value: null },
 		"u_deltaTime": { value: null },
@@ -203,7 +204,7 @@ THREE.ShaderLib[ 'ocean_phase' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_spectrum' ] = {
+THREE.OceanShaders[ 'ocean_spectrum' ] = {
 	uniforms: {
 		"u_size": { value: null },
 		"u_resolution": { value: null },
@@ -267,7 +268,7 @@ THREE.ShaderLib[ 'ocean_spectrum' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_normals' ] = {
+THREE.OceanShaders[ 'ocean_normals' ] = {
 	uniforms: {
 		"u_displacementMap": { value: null },
 		"u_resolution": { value: null },
@@ -301,7 +302,7 @@ THREE.ShaderLib[ 'ocean_normals' ] = {
 		'}'
 	].join( '\n' )
 };
-THREE.ShaderLib[ 'ocean_main' ] = {
+THREE.OceanShaders[ 'ocean_main' ] = {
 	uniforms: {
 		"u_displacementMap": { value: null },
 		"u_normalMap": { value: null },

+ 311 - 311
examples/js/shaders/VolumeShader.js

@@ -8,317 +8,317 @@
 
 THREE.VolumeRenderShader1 = {
 	uniforms: {
-        "u_size": { value: new THREE.Vector3( 1, 1, 1 ) },
-        "u_renderstyle": { value: 0 },
-        "u_renderthreshold": { value: 0.5 },
-        "u_clim": { value: new THREE.Vector2( 1, 1 ) },
-        "u_data": { value: null },
-        "u_cmdata": { value: null }
-    },
-    vertexShader: [
-        'varying vec4 v_nearpos;',
-        'varying vec4 v_farpos;',
-        'varying vec3 v_position;',
-
-        'mat4 inversemat(mat4 m) {',
-            // Taken from https://github.com/stackgl/glsl-inverse/blob/master/index.glsl
-            // This function is licenced by the MIT license to Mikola Lysenko
-            'float',
-            'a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3],',
-            'a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3],',
-            'a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3],',
-            'a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3],',
-
-            'b00 = a00 * a11 - a01 * a10,',
-            'b01 = a00 * a12 - a02 * a10,',
-            'b02 = a00 * a13 - a03 * a10,',
-            'b03 = a01 * a12 - a02 * a11,',
-            'b04 = a01 * a13 - a03 * a11,',
-            'b05 = a02 * a13 - a03 * a12,',
-            'b06 = a20 * a31 - a21 * a30,',
-            'b07 = a20 * a32 - a22 * a30,',
-            'b08 = a20 * a33 - a23 * a30,',
-            'b09 = a21 * a32 - a22 * a31,',
-            'b10 = a21 * a33 - a23 * a31,',
-            'b11 = a22 * a33 - a23 * a32,',
-
-            'det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;',
-
-        'return mat4(',
-            'a11 * b11 - a12 * b10 + a13 * b09,',
-            'a02 * b10 - a01 * b11 - a03 * b09,',
-            'a31 * b05 - a32 * b04 + a33 * b03,',
-            'a22 * b04 - a21 * b05 - a23 * b03,',
-            'a12 * b08 - a10 * b11 - a13 * b07,',
-            'a00 * b11 - a02 * b08 + a03 * b07,',
-            'a32 * b02 - a30 * b05 - a33 * b01,',
-            'a20 * b05 - a22 * b02 + a23 * b01,',
-            'a10 * b10 - a11 * b08 + a13 * b06,',
-            'a01 * b08 - a00 * b10 - a03 * b06,',
-            'a30 * b04 - a31 * b02 + a33 * b00,',
-            'a21 * b02 - a20 * b04 - a23 * b00,',
-            'a11 * b07 - a10 * b09 - a12 * b06,',
-            'a00 * b09 - a01 * b07 + a02 * b06,',
-            'a31 * b01 - a30 * b03 - a32 * b00,',
-            'a20 * b03 - a21 * b01 + a22 * b00) / det;',
-        '}',
-
-
-        'void main() {',
-            // Prepare transforms to map to "camera view". See also:
-            // https://threejs.org/docs/#api/renderers/webgl/WebGLProgram
-            'mat4 viewtransformf = viewMatrix;',
-            'mat4 viewtransformi = inversemat(viewMatrix);',
-
-            // Project local vertex coordinate to camera position. Then do a step
-            // backward (in cam coords) to the near clipping plane, and project back. Do
-            // the same for the far clipping plane. This gives us all the information we
-            // need to calculate the ray and truncate it to the viewing cone.
-            'vec4 position4 = vec4(position, 1.0);',
-            'vec4 pos_in_cam = viewtransformf * position4;',
-
-            // Intersection of ray and near clipping plane (z = -1 in clip coords)
-            'pos_in_cam.z = -pos_in_cam.w;',
-            'v_nearpos = viewtransformi * pos_in_cam;',
-
-            // Intersection of ray and far clipping plane (z = +1 in clip coords)
-            'pos_in_cam.z = pos_in_cam.w;',
-            'v_farpos = viewtransformi * pos_in_cam;',
-
-            // Set varyings and output pos
-            'v_position = position;',
-            'gl_Position = projectionMatrix * viewMatrix * modelMatrix * position4;',
-        '}',
-    ].join( '\n' ),
+				"u_size": { value: new THREE.Vector3( 1, 1, 1 ) },
+				"u_renderstyle": { value: 0 },
+				"u_renderthreshold": { value: 0.5 },
+				"u_clim": { value: new THREE.Vector2( 1, 1 ) },
+				"u_data": { value: null },
+				"u_cmdata": { value: null }
+		},
+		vertexShader: [
+				'varying vec4 v_nearpos;',
+				'varying vec4 v_farpos;',
+				'varying vec3 v_position;',
+
+				'mat4 inversemat(mat4 m) {',
+						// Taken from https://github.com/stackgl/glsl-inverse/blob/master/index.glsl
+						// This function is licenced by the MIT license to Mikola Lysenko
+						'float',
+						'a00 = m[0][0], a01 = m[0][1], a02 = m[0][2], a03 = m[0][3],',
+						'a10 = m[1][0], a11 = m[1][1], a12 = m[1][2], a13 = m[1][3],',
+						'a20 = m[2][0], a21 = m[2][1], a22 = m[2][2], a23 = m[2][3],',
+						'a30 = m[3][0], a31 = m[3][1], a32 = m[3][2], a33 = m[3][3],',
+
+						'b00 = a00 * a11 - a01 * a10,',
+						'b01 = a00 * a12 - a02 * a10,',
+						'b02 = a00 * a13 - a03 * a10,',
+						'b03 = a01 * a12 - a02 * a11,',
+						'b04 = a01 * a13 - a03 * a11,',
+						'b05 = a02 * a13 - a03 * a12,',
+						'b06 = a20 * a31 - a21 * a30,',
+						'b07 = a20 * a32 - a22 * a30,',
+						'b08 = a20 * a33 - a23 * a30,',
+						'b09 = a21 * a32 - a22 * a31,',
+						'b10 = a21 * a33 - a23 * a31,',
+						'b11 = a22 * a33 - a23 * a32,',
+
+						'det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;',
+
+				'return mat4(',
+						'a11 * b11 - a12 * b10 + a13 * b09,',
+						'a02 * b10 - a01 * b11 - a03 * b09,',
+						'a31 * b05 - a32 * b04 + a33 * b03,',
+						'a22 * b04 - a21 * b05 - a23 * b03,',
+						'a12 * b08 - a10 * b11 - a13 * b07,',
+						'a00 * b11 - a02 * b08 + a03 * b07,',
+						'a32 * b02 - a30 * b05 - a33 * b01,',
+						'a20 * b05 - a22 * b02 + a23 * b01,',
+						'a10 * b10 - a11 * b08 + a13 * b06,',
+						'a01 * b08 - a00 * b10 - a03 * b06,',
+						'a30 * b04 - a31 * b02 + a33 * b00,',
+						'a21 * b02 - a20 * b04 - a23 * b00,',
+						'a11 * b07 - a10 * b09 - a12 * b06,',
+						'a00 * b09 - a01 * b07 + a02 * b06,',
+						'a31 * b01 - a30 * b03 - a32 * b00,',
+						'a20 * b03 - a21 * b01 + a22 * b00) / det;',
+				'}',
+
+
+				'void main() {',
+						// Prepare transforms to map to "camera view". See also:
+						// https://threejs.org/docs/#api/renderers/webgl/WebGLProgram
+						'mat4 viewtransformf = viewMatrix;',
+						'mat4 viewtransformi = inversemat(viewMatrix);',
+
+						// Project local vertex coordinate to camera position. Then do a step
+						// backward (in cam coords) to the near clipping plane, and project back. Do
+						// the same for the far clipping plane. This gives us all the information we
+						// need to calculate the ray and truncate it to the viewing cone.
+						'vec4 position4 = vec4(position, 1.0);',
+						'vec4 pos_in_cam = viewtransformf * position4;',
+
+						// Intersection of ray and near clipping plane (z = -1 in clip coords)
+						'pos_in_cam.z = -pos_in_cam.w;',
+						'v_nearpos = viewtransformi * pos_in_cam;',
+
+						// Intersection of ray and far clipping plane (z = +1 in clip coords)
+						'pos_in_cam.z = pos_in_cam.w;',
+						'v_farpos = viewtransformi * pos_in_cam;',
+
+						// Set varyings and output pos
+						'v_position = position;',
+						'gl_Position = projectionMatrix * viewMatrix * modelMatrix * position4;',
+				'}',
+		].join( '\n' ),
 	fragmentShader: [
-        'precision highp float;',
-        'precision mediump sampler3D;',
-
-        'uniform vec3 u_size;',
-        'uniform int u_renderstyle;',
-        'uniform float u_renderthreshold;',
-        'uniform vec2 u_clim;',
-
-        'uniform sampler3D u_data;',
-        'uniform sampler2D u_cmdata;',
-
-        'varying vec3 v_position;',
-        'varying vec4 v_nearpos;',
-        'varying vec4 v_farpos;',
-
-        // The maximum distance through our rendering volume is sqrt(3).
-        'const int MAX_STEPS = 887;  // 887 for 512^3, 1774 for 1024^3',
-        'const int REFINEMENT_STEPS = 4;',
-        'const float relative_step_size = 1.0;',
-        'const vec4 ambient_color = vec4(0.2, 0.4, 0.2, 1.0);',
-        'const vec4 diffuse_color = vec4(0.8, 0.2, 0.2, 1.0);',
-        'const vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);',
-        'const float shininess = 40.0;',
-
-        'void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);',
-        'void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);',
-
-        'float sample1(vec3 texcoords);',
-        'vec4 apply_colormap(float val);',
-        'vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray);',
-
-
-        'void main() {',
-            // Normalize clipping plane info
-            'vec3 farpos = v_farpos.xyz / v_farpos.w;',
-            'vec3 nearpos = v_nearpos.xyz / v_nearpos.w;',
-
-            // Calculate unit vector pointing in the view direction through this fragment.
-            'vec3 view_ray = normalize(nearpos.xyz - farpos.xyz);',
-
-            // Compute the (negative) distance to the front surface or near clipping plane.
-            // v_position is the back face of the cuboid, so the initial distance calculated in the dot
-            // product below is the distance from near clip plane to the back of the cuboid
-            'float distance = dot(nearpos - v_position, view_ray);',
-            'distance = max(distance, min((-0.5 - v_position.x) / view_ray.x,',
-                                        '(u_size.x - 0.5 - v_position.x) / view_ray.x));',
-            'distance = max(distance, min((-0.5 - v_position.y) / view_ray.y,',
-                                        '(u_size.y - 0.5 - v_position.y) / view_ray.y));',
-            'distance = max(distance, min((-0.5 - v_position.z) / view_ray.z,',
-                                        '(u_size.z - 0.5 - v_position.z) / view_ray.z));',
-
-                                        // Now we have the starting position on the front surface
-            'vec3 front = v_position + view_ray * distance;',
-
-            // Decide how many steps to take
-            'int nsteps = int(-distance / relative_step_size + 0.5);',
-            'if ( nsteps < 1 )',
-                'discard;',
-
-            // Get starting location and step vector in texture coordinates
-            'vec3 step = ((v_position - front) / u_size) / float(nsteps);',
-            'vec3 start_loc = front / u_size;',
-
-            // For testing: show the number of steps. This helps to establish
-            // whether the rays are correctly oriented
-            //'gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0);',
-            //'return;',
-
-            'if (u_renderstyle == 0)',
-                'cast_mip(start_loc, step, nsteps, view_ray);',
-            'else if (u_renderstyle == 1)',
-                'cast_iso(start_loc, step, nsteps, view_ray);',
-
-            'if (gl_FragColor.a < 0.05)',
-                'discard;',
-        '}',
-
-
-        'float sample1(vec3 texcoords) {',
-            '/* Sample float value from a 3D texture. Assumes intensity data. */',
-            'return texture(u_data, texcoords.xyz).r;',
-        '}',
-
-
-        'vec4 apply_colormap(float val) {',
-            'val = (val - u_clim[0]) / (u_clim[1] - u_clim[0]);',
-            'return texture2D(u_cmdata, vec2(val, 0.5));',
-        '}',
-
-
-        'void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {',
-
-            'float max_val = -1e6;',
-            'int max_i = 100;',
-            'vec3 loc = start_loc;',
-
-            // Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
-            // non-constant expression. So we use a hard-coded max, and an additional condition
-            // inside the loop.
-            'for (int iter=0; iter<MAX_STEPS; iter++) {',
-                'if (iter >= nsteps)',
-                    'break;',
-                // Sample from the 3D texture
-                'float val = sample1(loc);',
-                // Apply MIP operation
-                'if (val > max_val) {',
-                    'max_val = val;',
-                    'max_i = iter;',
-                '}',
-                // Advance location deeper into the volume
-                'loc += step;',
-            '}',
-
-            // Refine location, gives crispier images
-            'vec3 iloc = start_loc + step * (float(max_i) - 0.5);',
-            'vec3 istep = step / float(REFINEMENT_STEPS);',
-            'for (int i=0; i<REFINEMENT_STEPS; i++) {',
-                'max_val = max(max_val, sample1(iloc));',
-                'iloc += istep;',
-            '}',
-
-            // Resolve final color
-            'gl_FragColor = apply_colormap(max_val);',
-        '}',
-
-
-        'void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {',
-
-            'gl_FragColor = vec4(0.0);  // init transparent',
-            'vec4 color3 = vec4(0.0);  // final color',
-            'vec3 dstep = 1.5 / u_size;  // step to sample derivative',
-            'vec3 loc = start_loc;',
-
-            'float low_threshold = u_renderthreshold - 0.02 * (u_clim[1] - u_clim[0]);',
-
-            // Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
-            // non-constant expression. So we use a hard-coded max, and an additional condition
-            // inside the loop.
-            'for (int iter=0; iter<MAX_STEPS; iter++) {',
-                'if (iter >= nsteps)',
-                    'break;',
-
-                    // Sample from the 3D texture
-                'float val = sample1(loc);',
-
-                'if (val > low_threshold) {',
-                // Take the last interval in smaller steps
-                    'vec3 iloc = loc - 0.5 * step;',
-                    'vec3 istep = step / float(REFINEMENT_STEPS);',
-                    'for (int i=0; i<REFINEMENT_STEPS; i++) {',
-                        'val = sample1(iloc);',
-                        'if (val > u_renderthreshold) {',
-                            'gl_FragColor = add_lighting(val, iloc, dstep, view_ray);',
-                            'return;',
-                        '}',
-                        'iloc += istep;',
-                    '}',
-                '}',
-
-                // Advance location deeper into the volume
-                'loc += step;',
-            '}',
-        '}',
-
-
-        'vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray)',
-        '{',
-            // Calculate color by incorporating lighting
-
-            // View direction
-            'vec3 V = normalize(view_ray);',
-
-            // calculate normal vector from gradient
-            'vec3 N;',
-            'float val1, val2;',
-            'val1 = sample1(loc + vec3(-step[0], 0.0, 0.0));',
-            'val2 = sample1(loc + vec3(+step[0], 0.0, 0.0));',
-            'N[0] = val1 - val2;',
-            'val = max(max(val1, val2), val);',
-            'val1 = sample1(loc + vec3(0.0, -step[1], 0.0));',
-            'val2 = sample1(loc + vec3(0.0, +step[1], 0.0));',
-            'N[1] = val1 - val2;',
-            'val = max(max(val1, val2), val);',
-            'val1 = sample1(loc + vec3(0.0, 0.0, -step[2]));',
-            'val2 = sample1(loc + vec3(0.0, 0.0, +step[2]));',
-            'N[2] = val1 - val2;',
-            'val = max(max(val1, val2), val);',
-
-            'float gm = length(N); // gradient magnitude',
-            'N = normalize(N);',
-
-            // Flip normal so it points towards viewer
-            'float Nselect = float(dot(N, V) > 0.0);',
-            'N = (2.0 * Nselect - 1.0) * N;  // ==  Nselect * N - (1.0-Nselect)*N;',
-
-            // Init colors
-            'vec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0);',
-            'vec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0);',
-            'vec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0);',
-
-            // note: could allow multiple lights
-            'for (int i=0; i<1; i++)',
-            '{',
-                 // Get light direction (make sure to prevent zero devision)
-                'vec3 L = normalize(view_ray);  //lightDirs[i];',
-                'float lightEnabled = float( length(L) > 0.0 );',
-                'L = normalize(L + (1.0 - lightEnabled));',
-
-                // Calculate lighting properties
-                'float lambertTerm = clamp(dot(N, L), 0.0, 1.0);',
-                'vec3 H = normalize(L+V); // Halfway vector',
-                'float specularTerm = pow(max(dot(H, N), 0.0), shininess);',
-
-                // Calculate mask
-                'float mask1 = lightEnabled;',
-
-                // Calculate colors
-                'ambient_color +=  mask1 * ambient_color;  // * gl_LightSource[i].ambient;',
-                'diffuse_color +=  mask1 * lambertTerm;',
-                'specular_color += mask1 * specularTerm * specular_color;',
-            '}',
-
-            // Calculate final color by componing different components
-            'vec4 final_color;',
-            'vec4 color = apply_colormap(val);',
-            'final_color = color * (ambient_color + diffuse_color) + specular_color;',
-            'final_color.a = color.a;',
-            'return final_color;',
-        '}',
+				'precision highp float;',
+				'precision mediump sampler3D;',
+
+				'uniform vec3 u_size;',
+				'uniform int u_renderstyle;',
+				'uniform float u_renderthreshold;',
+				'uniform vec2 u_clim;',
+
+				'uniform sampler3D u_data;',
+				'uniform sampler2D u_cmdata;',
+
+				'varying vec3 v_position;',
+				'varying vec4 v_nearpos;',
+				'varying vec4 v_farpos;',
+
+				// The maximum distance through our rendering volume is sqrt(3).
+				'const int MAX_STEPS = 887;	// 887 for 512^3, 1774 for 1024^3',
+				'const int REFINEMENT_STEPS = 4;',
+				'const float relative_step_size = 1.0;',
+				'const vec4 ambient_color = vec4(0.2, 0.4, 0.2, 1.0);',
+				'const vec4 diffuse_color = vec4(0.8, 0.2, 0.2, 1.0);',
+				'const vec4 specular_color = vec4(1.0, 1.0, 1.0, 1.0);',
+				'const float shininess = 40.0;',
+
+				'void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);',
+				'void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray);',
+
+				'float sample1(vec3 texcoords);',
+				'vec4 apply_colormap(float val);',
+				'vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray);',
+
+
+				'void main() {',
+						// Normalize clipping plane info
+						'vec3 farpos = v_farpos.xyz / v_farpos.w;',
+						'vec3 nearpos = v_nearpos.xyz / v_nearpos.w;',
+
+						// Calculate unit vector pointing in the view direction through this fragment.
+						'vec3 view_ray = normalize(nearpos.xyz - farpos.xyz);',
+
+						// Compute the (negative) distance to the front surface or near clipping plane.
+						// v_position is the back face of the cuboid, so the initial distance calculated in the dot
+						// product below is the distance from near clip plane to the back of the cuboid
+						'float distance = dot(nearpos - v_position, view_ray);',
+						'distance = max(distance, min((-0.5 - v_position.x) / view_ray.x,',
+																				'(u_size.x - 0.5 - v_position.x) / view_ray.x));',
+						'distance = max(distance, min((-0.5 - v_position.y) / view_ray.y,',
+																				'(u_size.y - 0.5 - v_position.y) / view_ray.y));',
+						'distance = max(distance, min((-0.5 - v_position.z) / view_ray.z,',
+																				'(u_size.z - 0.5 - v_position.z) / view_ray.z));',
+
+																				// Now we have the starting position on the front surface
+						'vec3 front = v_position + view_ray * distance;',
+
+						// Decide how many steps to take
+						'int nsteps = int(-distance / relative_step_size + 0.5);',
+						'if ( nsteps < 1 )',
+								'discard;',
+
+						// Get starting location and step vector in texture coordinates
+						'vec3 step = ((v_position - front) / u_size) / float(nsteps);',
+						'vec3 start_loc = front / u_size;',
+
+						// For testing: show the number of steps. This helps to establish
+						// whether the rays are correctly oriented
+						//'gl_FragColor = vec4(0.0, float(nsteps) / 1.0 / u_size.x, 1.0, 1.0);',
+						//'return;',
+
+						'if (u_renderstyle == 0)',
+								'cast_mip(start_loc, step, nsteps, view_ray);',
+						'else if (u_renderstyle == 1)',
+								'cast_iso(start_loc, step, nsteps, view_ray);',
+
+						'if (gl_FragColor.a < 0.05)',
+								'discard;',
+				'}',
+
+
+				'float sample1(vec3 texcoords) {',
+						'/* Sample float value from a 3D texture. Assumes intensity data. */',
+						'return texture(u_data, texcoords.xyz).r;',
+				'}',
+
+
+				'vec4 apply_colormap(float val) {',
+						'val = (val - u_clim[0]) / (u_clim[1] - u_clim[0]);',
+						'return texture2D(u_cmdata, vec2(val, 0.5));',
+				'}',
+
+
+				'void cast_mip(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {',
+
+						'float max_val = -1e6;',
+						'int max_i = 100;',
+						'vec3 loc = start_loc;',
+
+						// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
+						// non-constant expression. So we use a hard-coded max, and an additional condition
+						// inside the loop.
+						'for (int iter=0; iter<MAX_STEPS; iter++) {',
+								'if (iter >= nsteps)',
+										'break;',
+								// Sample from the 3D texture
+								'float val = sample1(loc);',
+								// Apply MIP operation
+								'if (val > max_val) {',
+										'max_val = val;',
+										'max_i = iter;',
+								'}',
+								// Advance location deeper into the volume
+								'loc += step;',
+						'}',
+
+						// Refine location, gives crispier images
+						'vec3 iloc = start_loc + step * (float(max_i) - 0.5);',
+						'vec3 istep = step / float(REFINEMENT_STEPS);',
+						'for (int i=0; i<REFINEMENT_STEPS; i++) {',
+								'max_val = max(max_val, sample1(iloc));',
+								'iloc += istep;',
+						'}',
+
+						// Resolve final color
+						'gl_FragColor = apply_colormap(max_val);',
+				'}',
+
+
+				'void cast_iso(vec3 start_loc, vec3 step, int nsteps, vec3 view_ray) {',
+
+						'gl_FragColor = vec4(0.0);	// init transparent',
+						'vec4 color3 = vec4(0.0);	// final color',
+						'vec3 dstep = 1.5 / u_size;	// step to sample derivative',
+						'vec3 loc = start_loc;',
+
+						'float low_threshold = u_renderthreshold - 0.02 * (u_clim[1] - u_clim[0]);',
+
+						// Enter the raycasting loop. In WebGL 1 the loop index cannot be compared with
+						// non-constant expression. So we use a hard-coded max, and an additional condition
+						// inside the loop.
+						'for (int iter=0; iter<MAX_STEPS; iter++) {',
+								'if (iter >= nsteps)',
+										'break;',
+
+										// Sample from the 3D texture
+								'float val = sample1(loc);',
+
+								'if (val > low_threshold) {',
+								// Take the last interval in smaller steps
+										'vec3 iloc = loc - 0.5 * step;',
+										'vec3 istep = step / float(REFINEMENT_STEPS);',
+										'for (int i=0; i<REFINEMENT_STEPS; i++) {',
+												'val = sample1(iloc);',
+												'if (val > u_renderthreshold) {',
+														'gl_FragColor = add_lighting(val, iloc, dstep, view_ray);',
+														'return;',
+												'}',
+												'iloc += istep;',
+										'}',
+								'}',
+
+								// Advance location deeper into the volume
+								'loc += step;',
+						'}',
+				'}',
+
+
+				'vec4 add_lighting(float val, vec3 loc, vec3 step, vec3 view_ray)',
+				'{',
+						// Calculate color by incorporating lighting
+
+						// View direction
+						'vec3 V = normalize(view_ray);',
+
+						// calculate normal vector from gradient
+						'vec3 N;',
+						'float val1, val2;',
+						'val1 = sample1(loc + vec3(-step[0], 0.0, 0.0));',
+						'val2 = sample1(loc + vec3(+step[0], 0.0, 0.0));',
+						'N[0] = val1 - val2;',
+						'val = max(max(val1, val2), val);',
+						'val1 = sample1(loc + vec3(0.0, -step[1], 0.0));',
+						'val2 = sample1(loc + vec3(0.0, +step[1], 0.0));',
+						'N[1] = val1 - val2;',
+						'val = max(max(val1, val2), val);',
+						'val1 = sample1(loc + vec3(0.0, 0.0, -step[2]));',
+						'val2 = sample1(loc + vec3(0.0, 0.0, +step[2]));',
+						'N[2] = val1 - val2;',
+						'val = max(max(val1, val2), val);',
+
+						'float gm = length(N); // gradient magnitude',
+						'N = normalize(N);',
+
+						// Flip normal so it points towards viewer
+						'float Nselect = float(dot(N, V) > 0.0);',
+						'N = (2.0 * Nselect - 1.0) * N;	// ==	Nselect * N - (1.0-Nselect)*N;',
+
+						// Init colors
+						'vec4 ambient_color = vec4(0.0, 0.0, 0.0, 0.0);',
+						'vec4 diffuse_color = vec4(0.0, 0.0, 0.0, 0.0);',
+						'vec4 specular_color = vec4(0.0, 0.0, 0.0, 0.0);',
+
+						// note: could allow multiple lights
+						'for (int i=0; i<1; i++)',
+						'{',
+								 // Get light direction (make sure to prevent zero devision)
+								'vec3 L = normalize(view_ray);	//lightDirs[i];',
+								'float lightEnabled = float( length(L) > 0.0 );',
+								'L = normalize(L + (1.0 - lightEnabled));',
+
+								// Calculate lighting properties
+								'float lambertTerm = clamp(dot(N, L), 0.0, 1.0);',
+								'vec3 H = normalize(L+V); // Halfway vector',
+								'float specularTerm = pow(max(dot(H, N), 0.0), shininess);',
+
+								// Calculate mask
+								'float mask1 = lightEnabled;',
+
+								// Calculate colors
+								'ambient_color +=	mask1 * ambient_color;	// * gl_LightSource[i].ambient;',
+								'diffuse_color +=	mask1 * lambertTerm;',
+								'specular_color += mask1 * specularTerm * specular_color;',
+						'}',
+
+						// Calculate final color by componing different components
+						'vec4 final_color;',
+						'vec4 color = apply_colormap(val);',
+						'final_color = color * (ambient_color + diffuse_color) + specular_color;',
+						'final_color.a = color.a;',
+						'return final_color;',
+				'}',
 	].join( '\n' )
 };

+ 16 - 0
examples/jsm/animation/AnimationClipCreator.d.ts

@@ -0,0 +1,16 @@
+import {
+  AnimationClip,
+  Vector3
+} from '../../../src/Three';
+
+export class AnimationClipCreator  {
+  constructor();
+
+  static CreateRotationAnimation(period: number, axis: string): AnimationClip;
+  static CreateScaleAxisAnimation(period: number, axis: string): AnimationClip;
+  static CreateShakeAnimation(duration: number, shakeScale: Vector3): AnimationClip;
+  static CreatePulsationAnimation(duration: number, pulseScale: number): AnimationClip;
+  static CreateVisibilityAnimation(duration: number): AnimationClip;
+  static CreateMaterialColorAnimation(duration: number, colors: number[]): AnimationClip;
+
+}

+ 125 - 0
examples/jsm/animation/AnimationClipCreator.js

@@ -0,0 +1,125 @@
+/**
+ *
+ * Creator of typical test AnimationClips / KeyframeTracks
+ *
+ * @author Ben Houston / http://clara.io/
+ * @author David Sarno / http://lighthaus.us/
+ */
+
+import {
+	AnimationClip,
+	BooleanKeyframeTrack,
+	ColorKeyframeTrack,
+	NumberKeyframeTrack,
+	Vector3,
+	VectorKeyframeTrack
+} from "../../../build/three.module.js";
+
+var AnimationClipCreator = function () {};
+
+AnimationClipCreator.CreateRotationAnimation = function ( period, axis ) {
+
+	var times = [ 0, period ], values = [ 0, 360 ];
+
+	axis = axis || 'x';
+	var trackName = '.rotation[' + axis + ']';
+
+	var track = new NumberKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, period, [ track ] );
+
+};
+
+AnimationClipCreator.CreateScaleAxisAnimation = function ( period, axis ) {
+
+	var times = [ 0, period ], values = [ 0, 1 ];
+
+	axis = axis || 'x';
+	var trackName = '.scale[' + axis + ']';
+
+	var track = new NumberKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, period, [ track ] );
+
+};
+
+AnimationClipCreator.CreateShakeAnimation = function ( duration, shakeScale ) {
+
+	var times = [], values = [], tmp = new Vector3();
+
+	for ( var i = 0; i < duration * 10; i ++ ) {
+
+		times.push( i / 10 );
+
+		tmp.set( Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0 ).
+			multiply( shakeScale ).
+			toArray( values, values.length );
+
+	}
+
+	var trackName = '.position';
+
+	var track = new VectorKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, duration, [ track ] );
+
+};
+
+
+AnimationClipCreator.CreatePulsationAnimation = function ( duration, pulseScale ) {
+
+	var times = [], values = [], tmp = new Vector3();
+
+	for ( var i = 0; i < duration * 10; i ++ ) {
+
+		times.push( i / 10 );
+
+		var scaleFactor = Math.random() * pulseScale;
+		tmp.set( scaleFactor, scaleFactor, scaleFactor ).
+			toArray( values, values.length );
+
+	}
+
+	var trackName = '.scale';
+
+	var track = new VectorKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, duration, [ track ] );
+
+};
+
+
+AnimationClipCreator.CreateVisibilityAnimation = function ( duration ) {
+
+	var times = [ 0, duration / 2, duration ], values = [ true, false, true ];
+
+	var trackName = '.visible';
+
+	var track = new BooleanKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, duration, [ track ] );
+
+};
+
+
+AnimationClipCreator.CreateMaterialColorAnimation = function ( duration, colors ) {
+
+	var times = [], values = [],
+		timeStep = duration / colors.length;
+
+	for ( var i = 0; i <= colors.length; i ++ ) {
+
+		times.push( i * timeStep );
+		values.push( colors[ i % colors.length ] );
+
+	}
+
+	var trackName = '.material[0].color';
+
+	var track = new ColorKeyframeTrack( trackName, times, values );
+
+	return new AnimationClip( null, duration, [ track ] );
+
+};
+
+export { AnimationClipCreator };

+ 25 - 0
examples/jsm/animation/CCDIKSolver.d.ts

@@ -0,0 +1,25 @@
+import {
+  SkinnedMesh,
+} from '../../../src/Three';
+
+export interface IKS {
+  effector: number;
+  iteration: number;
+  links: {
+    enabled: boolean;
+    index: number;
+  }
+  maxAngle: number;
+  target: number;
+}
+
+export class CCDIKSolver {
+  constructor(mesh: SkinnedMesh, iks: IKS[]);
+
+  update(): this;
+  createHelper(): CCDIKHelper;
+}
+
+export class CCDIKHelper {
+  constructor(mesh: SkinnedMesh, iks: IKS[]);
+}

+ 472 - 0
examples/jsm/animation/CCDIKSolver.js

@@ -0,0 +1,472 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * CCD Algorithm
+ *  - https://sites.google.com/site/auraliusproject/ccd-algorithm
+ *
+ * // ik parameter example
+ * //
+ * // 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
+ * iks = [ {
+ *	target: 1,
+ *	effector: 2,
+ *	links: [ { index: 5, limitation: new Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
+ *	iteration: 10,
+ *	minAngle: 0.0,
+ *	maxAngle: 1.0,
+ * } ];
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	Color,
+	Line,
+	LineBasicMaterial,
+	Matrix4,
+	Mesh,
+	MeshBasicMaterial,
+	Object3D,
+	Quaternion,
+	SphereBufferGeometry,
+	Vector3
+} from "../../../build/three.module.js";
+
+var CCDIKSolver = ( function () {
+
+	/**
+	 * @param {THREE.SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKSolver( mesh, iks ) {
+
+		this.mesh = mesh;
+		this.iks = iks || [];
+
+		this._valid();
+
+	}
+
+	CCDIKSolver.prototype = {
+
+		constructor: CCDIKSolver,
+
+		/**
+		 * Update IK bones.
+		 *
+		 * @return {CCDIKSolver}
+		 */
+		update: function () {
+
+			var q = new Quaternion();
+			var targetPos = new Vector3();
+			var targetVec = new Vector3();
+			var effectorPos = new Vector3();
+			var effectorVec = new Vector3();
+			var linkPos = new Vector3();
+			var invLinkQ = new Quaternion();
+			var linkScale = new Vector3();
+			var axis = new Vector3();
+			var vector = new Vector3();
+
+			return function update() {
+
+				var bones = this.mesh.skeleton.bones;
+				var iks = this.iks;
+
+				// for reference overhead reduction in loop
+				var math = Math;
+
+				for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+					var ik = iks[ i ];
+					var effector = bones[ ik.effector ];
+					var target = bones[ ik.target ];
+
+					// don't use getWorldPosition() here for the performance
+					// because it calls updateMatrixWorld( true ) inside.
+					targetPos.setFromMatrixPosition( target.matrixWorld );
+
+					var links = ik.links;
+					var iteration = ik.iteration !== undefined ? ik.iteration : 1;
+
+					for ( var j = 0; j < iteration; j ++ ) {
+
+						var rotated = false;
+
+						for ( var k = 0, kl = links.length; k < kl; k ++ ) {
+
+							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;
+
+							// 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();
+
+							targetVec.subVectors( targetPos, linkPos );
+							targetVec.applyQuaternion( invLinkQ );
+							targetVec.normalize();
+
+							var angle = targetVec.dot( effectorVec );
+
+							if ( angle > 1.0 ) {
+
+								angle = 1.0;
+
+							} else if ( angle < - 1.0 ) {
+
+								angle = - 1.0;
+
+							}
+
+							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;
+
+							if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
+
+								angle = ik.minAngle;
+
+							}
+
+							if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
+
+								angle = ik.maxAngle;
+
+							}
+
+							axis.crossVectors( effectorVec, targetVec );
+							axis.normalize();
+
+							q.setFromAxisAngle( axis, angle );
+							link.quaternion.multiply( q );
+
+							// TODO: re-consider the limitation specification
+							if ( limitation !== undefined ) {
+
+								var c = link.quaternion.w;
+
+								if ( c > 1.0 ) c = 1.0;
+
+								var c2 = math.sqrt( 1 - c * c );
+								link.quaternion.set( limitation.x * c2,
+								                     limitation.y * c2,
+								                     limitation.z * c2,
+								                     c );
+
+							}
+
+							if ( rotationMin !== undefined ) {
+
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.max( rotationMin ) );
+
+							}
+
+							if ( rotationMax !== undefined ) {
+
+								link.rotation.setFromVector3(
+									link.rotation
+										.toVector3( vector )
+										.min( rotationMax ) );
+
+							}
+
+							link.updateMatrixWorld( true );
+
+							rotated = true;
+
+						}
+
+						if ( ! rotated ) break;
+
+					}
+
+				}
+
+				return this;
+
+			};
+
+		}(),
+
+		/**
+		 * Creates Helper
+		 *
+		 * @return {CCDIKHelper}
+		 */
+		createHelper: function () {
+
+			return new CCDIKHelper( this.mesh, this.mesh.geometry.userData.MMD.iks );
+
+		},
+
+		// private methods
+
+		_valid: function () {
+
+			var iks = this.iks;
+			var bones = this.mesh.skeleton.bones;
+
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+				var ik = iks[ i ];
+				var effector = bones[ ik.effector ];
+				var links = ik.links;
+				var link0, link1;
+
+				link0 = effector;
+
+				for ( var j = 0, jl = links.length; j < jl; j ++ ) {
+
+					link1 = bones[ links[ j ].index ];
+
+					if ( link0.parent !== link1 ) {
+
+						console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
+
+					}
+
+					link0 = link1;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	/**
+	 * Visualize IK bones
+	 *
+	 * @param {SkinnedMesh} mesh
+	 * @param {Array<Object>} iks
+	 */
+	function CCDIKHelper( mesh, iks ) {
+
+		Object3D.call( this );
+
+		this.root = mesh;
+		this.iks = iks || [];
+
+		this.matrix.copy( mesh.matrixWorld );
+		this.matrixAutoUpdate = false;
+
+		this.sphereGeometry = new SphereBufferGeometry( 0.25, 16, 8 );
+
+		this.targetSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0xff8888 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.effectorSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0x88ff88 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.linkSphereMaterial = new MeshBasicMaterial( {
+			color: new Color( 0x8888ff ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this.lineMaterial = new LineBasicMaterial( {
+			color: new Color( 0xff0000 ),
+			depthTest: false,
+			depthWrite: false,
+			transparent: true
+		} );
+
+		this._init();
+
+	}
+
+	CCDIKHelper.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+		constructor: CCDIKHelper,
+
+		/**
+		 * Updates IK bones visualization.
+		 */
+		updateMatrixWorld: function () {
+
+			var matrix = new Matrix4();
+			var vector = new Vector3();
+
+			function getPosition( bone, matrixWorldInv ) {
+
+				return vector
+					.setFromMatrixPosition( bone.matrixWorld )
+					.applyMatrix4( matrixWorldInv );
+
+			}
+
+			function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
+
+				var v = getPosition( bone, matrixWorldInv );
+
+				array[ index * 3 + 0 ] = v.x;
+				array[ index * 3 + 1 ] = v.y;
+				array[ index * 3 + 2 ] = v.z;
+
+			}
+
+			return function updateMatrixWorld( force ) {
+
+				var mesh = this.root;
+
+				if ( this.visible ) {
+
+					var offset = 0;
+
+					var iks = this.iks;
+					var bones = mesh.skeleton.bones;
+
+					matrix.getInverse( mesh.matrixWorld );
+
+					for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+						var ik = iks[ i ];
+
+						var targetBone = bones[ ik.target ];
+						var effectorBone = bones[ ik.effector ];
+
+						var targetMesh = this.children[ offset ++ ];
+						var effectorMesh = this.children[ offset ++ ];
+
+						targetMesh.position.copy( getPosition( targetBone, matrix ) );
+						effectorMesh.position.copy( getPosition( effectorBone, matrix ) );
+
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
+
+							var linkMesh = this.children[ offset ++ ];
+
+							linkMesh.position.copy( getPosition( linkBone, matrix ) );
+
+						}
+
+						var line = this.children[ offset ++ ];
+						var array = line.geometry.attributes.position.array;
+
+						setPositionOfBoneToAttributeArray( array, 0, targetBone, matrix );
+						setPositionOfBoneToAttributeArray( array, 1, effectorBone, matrix );
+
+						for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+							var link = ik.links[ j ];
+							var linkBone = bones[ link.index ];
+							setPositionOfBoneToAttributeArray( array, j + 2, linkBone, matrix );
+
+						}
+
+						line.geometry.attributes.position.needsUpdate = true;
+
+					}
+
+				}
+
+				this.matrix.copy( mesh.matrixWorld );
+
+				Object3D.prototype.updateMatrixWorld.call( this, force );
+
+			};
+
+		}(),
+
+		// private method
+
+		_init: function () {
+
+			var self = this;
+			var iks = this.iks;
+
+			function createLineGeometry( ik ) {
+
+				var geometry = new BufferGeometry();
+				var vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
+				geometry.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );
+
+				return geometry;
+
+			}
+
+			function createTargetMesh() {
+
+				return new Mesh( self.sphereGeometry, self.targetSphereMaterial );
+
+			}
+
+			function createEffectorMesh() {
+
+				return new Mesh( self.sphereGeometry, self.effectorSphereMaterial );
+
+			}
+
+			function createLinkMesh() {
+
+				return new Mesh( self.sphereGeometry, self.linkSphereMaterial );
+
+			}
+
+			function createLine( ik ) {
+
+				return new Line( createLineGeometry( ik ), self.lineMaterial );
+
+			}
+
+			for ( var i = 0, il = iks.length; i < il; i ++ ) {
+
+				var ik = iks[ i ];
+
+				this.add( createTargetMesh() );
+				this.add( createEffectorMesh() );
+
+				for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+					this.add( createLinkMesh() );
+
+				}
+
+				this.add( createLine( ik ) );
+
+			}
+
+		}
+
+	} );
+
+	return CCDIKSolver;
+
+} )();
+
+export { CCDIKSolver };

+ 85 - 0
examples/jsm/animation/MMDAnimationHelper.d.ts

@@ -0,0 +1,85 @@
+import {
+  AnimationClip,
+  Audio,
+  Camera,
+  Mesh,
+  Object3D,
+  SkinnedMesh
+} from '../../../src/Three';
+
+export interface MMDAnimationHelperParameter {
+  sync?: boolean;
+  afterglow?: number;
+  resetPhysicsOnLoop?: boolean;
+}
+
+export interface MMDAnimationHelperAddParameter {
+  animation?: AnimationClip | AnimationClip[];
+  physics?: boolean;
+  warmup?: number;
+  unitStep?: number;
+  maxStepNum?: number;
+  gravity?: number;
+  delayTime?: number;
+}
+
+export interface MMDAnimationHelperPoseParameter {
+  resetPose?: boolean;
+  ik?: boolean;
+  grant?: boolean;
+}
+
+export class MMDAnimationHelper {
+  constructor(params?: MMDAnimationHelperParameter);
+  meshes: Mesh[];
+  camera: Camera | null;
+  cameraTarget: Object3D;
+  audio: Audio;
+  audioManager: AudioManager;
+  configuration: {
+    sync: boolean;
+    afterglow: number;
+    resetPhysicsOnLoop: boolean;
+  };
+  enabled: {
+    animation: boolean;
+    ik: boolean;
+    grant: boolean;
+    physics: boolean;
+    cameraAnimation: boolean;
+  };
+  onBeforePhysics: (mesh: SkinnedMesh) => void;
+  sharedPhysics: boolean;
+  masterPhysics: null;
+
+  add(object: SkinnedMesh | Camera | Audio, params?: MMDAnimationHelperAddParameter): this;
+  remove(object: SkinnedMesh | Camera | Audio): this;
+  update(delta: number): this;
+  pose(mesh: SkinnedMesh, vpd: object, params?: MMDAnimationHelperPoseParameter): this;
+  enable(key: string, enabled: boolean): this;
+  createGrantSolver(mesh: SkinnedMesh): GrantSolver;
+}
+
+export interface AudioManagerParameter {
+  delayTime?: number;
+}
+
+export class AudioManager {
+  constructor(audio: Audio, params?: AudioManagerParameter);
+  audio: Audio;
+  elapsedTime: number;
+  currentTime: number;
+  delayTime: number;
+  audioDuration: number;
+  duration: number;
+
+  control(delta: number): this;
+}
+
+export class GrantSolver {
+  constructor(mesh: SkinnedMesh, grants: object[]);
+  mesh: SkinnedMesh;
+  grants: object[];
+
+  update(): this;
+}

+ 1052 - 0
examples/jsm/animation/MMDAnimationHelper.js

@@ -0,0 +1,1052 @@
+/**
+ * @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
+ *  - MMDPhysics
+ *  - CCDIKSolver
+ *
+ * TODO
+ *  - more precise grant skinning support.
+ */
+
+import {
+	AnimationMixer,
+	Object3D,
+	Quaternion,
+	Vector3
+} from "../../../build/three.module.js";
+import { CCDIKSolver } from "../animation/CCDIKSolver.js";
+import { MMDPhysics } from "../animation/MMDPhysics.js";
+
+var 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 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 {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 {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 {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 {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 {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 Vector3();
+			var quaternion = new 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} enabled
+		 * @return {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.userData.MMD.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 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 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; // eslint-disable-line no-undef
+
+			}
+
+			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.userData.MMD.iks;
+			var bones = mesh.geometry.userData.MMD.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 ( CCDIKSolver === undefined ) {
+
+				throw new Error( 'THREE.MMDAnimationHelper: Import CCDIKSolver.' );
+
+			}
+
+			return new CCDIKSolver( mesh, mesh.geometry.userData.MMD.iks );
+
+		},
+
+		_createMMDPhysics: function ( mesh, params ) {
+
+			if ( MMDPhysics === undefined ) {
+
+				throw new Error( 'THREE.MMDPhysics: Import MMDPhysics.' );
+
+			}
+
+			return new MMDPhysics(
+				mesh,
+				mesh.geometry.userData.MMD.rigidBodies,
+				mesh.geometry.userData.MMD.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;
+
+			// 'duration' can be bigger than 'audioDuration + delayTime' because of sync configuration
+			if ( ( this.currentTime - this.delayTime ) > this.audioDuration ) 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 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;
+
+} )();
+
+export { MMDAnimationHelper };

+ 109 - 0
examples/jsm/animation/MMDPhysics.d.ts

@@ -0,0 +1,109 @@
+import {
+  Bone,
+  Euler,
+  Matrix4,
+  Object3D,
+  Quaternion,
+  SkinnedMesh,
+  Vector3
+} from '../../../src/Three';
+
+export interface MMDPhysicsParameter {
+  unitStep?: number;
+  maxStepNum?: number;
+  gravity?: Vector3;
+}
+
+export class MMDPhysics {
+  constructor(mesh: SkinnedMesh, rigidBodyParams: object[], constraintParams?: object[], params?: MMDPhysicsParameter);
+  manager: ResourceManager;
+  mesh: SkinnedMesh;
+  unitStep: number;
+  maxStepNum: number;
+  gravity: Vector3;
+  world: null;
+  bodies: RigidBody[];
+  constraints: Constraint[];
+
+  update(delta: number): this;
+  reset(): this;
+  warmup(cycles: number): this;
+  setGravity(gravity: Vector3): this;
+  createHelper(): MMDPhysicsHelper;
+}
+
+export class ResourceManager{
+  constructor();
+  threeVector3s: Vector3[];
+  threeMatrix4s: Matrix4[];
+  threeQuaternions: Quaternion[];
+  threeEulers: Euler[];
+  transforms: object[];
+  quaternions: object[];
+  vector3s: object[];
+
+  allocThreeVector3(): void;
+  freeThreeVector3(v: Vector3): void;
+  allocThreeMatrix4(): void;
+  freeThreeMatrix4(m: Matrix4): void;
+  allocThreeQuaternion(): void;
+  freeThreeQuaternion(q: Quaternion): void;
+  allocThreeEuler(): void;
+  freeThreeEuler(e: Euler): void;
+  allocTransform(): void;
+  freeTransform(t: object): void;
+  allocQuaternion(): void;
+  freeQuaternion(q: object): void;
+  allocVector3(): void;
+  freeVector3(v: object): void;
+  setIdentity(): void;
+  getBasis(t: object): object;
+  getBasisAsMatrix3(t: object): object;
+  getOrigin(t: object): object;
+  setOrigin(t: object, v: object): void;
+  copyOrigin(t1: object, t2: object): void;
+  setBasis(t: object, q: object): void;
+  setBasisFromMatrix3(t: object, m: object): void;
+  setOriginFromArray3(t: object, a: number[]): void;
+  setOriginFromThreeVector3(t: object, v: Vector3): void;
+  setBasisFromArray3(t: object, a: number[]): void;
+  setBasisFromThreeQuaternion(t: object, a: Quaternion): void;
+  multiplyTransforms(t1: object, t2: object): object;
+  inverseTransform(t: object): object;
+  multiplyMatrices3(m1: object, m2: object): object;
+  addVector3(v1: object, v2: object): object;
+  dotVectors3(v1: object, v2: object): number;
+  rowOfMatrix3(m: object, i: number): object;
+  columnOfMatrix3(m: object, i: number): object;
+  negativeVector3(v: object): object;
+  multiplyMatrix3ByVector3(m: object, v: object): object;
+  transposeMatrix3(m: object): object;
+  quaternionToMatrix3(q: object): object;
+  matrix3ToQuaternion(m: object): object;
+}
+
+export class RigidBody{
+  constructor(mesh: SkinnedMesh, world: object, params: object, manager: ResourceManager);
+  mesh: SkinnedMesh;
+  world: object;
+  params: object;
+  manager: ResourceManager;
+
+  body: object;
+  bone: Bone;
+  boneOffsetForm: object;
+  boneOffsetFormInverse: object;
+
+  reset(): this;
+  updateFromBone(): this;
+  updateBone(): this;
+}
+
+export class Constraint{
+  constructor(mesh: SkinnedMesh, world: object, bodyA: RigidBody, bodyB: RigidBody, params: object, manager: ResourceManager);
+
+}
+
+export class MMDPhysicsHelper extends Object3D {
+  constructor();
+}

+ 1428 - 0
examples/jsm/animation/MMDPhysics.js

@@ -0,0 +1,1428 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * Dependencies
+ *  - Ammo.js https://github.com/kripken/ammo.js
+ *
+ * MMDPhysics calculates physics with Ammo(Bullet based JavaScript Physics engine)
+ * for MMD model loaded by MMDLoader.
+ *
+ * TODO
+ *  - Physics in Worker
+ */
+
+import {
+	Bone,
+	BoxBufferGeometry,
+	Color,
+	CylinderBufferGeometry,
+	Euler,
+	Matrix4,
+	Mesh,
+	MeshBasicMaterial,
+	Object3D,
+	Quaternion,
+	SphereBufferGeometry,
+	Vector3
+} from "../../../build/three.module.js";
+
+/* global Ammo */
+
+var MMDPhysics = ( function () {
+
+	/**
+	 * @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 {Vector3} params.gravity - Default is ( 0, - 9.8 * 10, 0 )
+	 */
+	function MMDPhysics( mesh, rigidBodyParams, constraintParams, params ) {
+
+		if ( typeof Ammo === 'undefined' ) {
+
+			throw new Error( 'THREE.MMDPhysics: Import ammo.js https://github.com/kripken/ammo.js' );
+
+		}
+
+		constraintParams = constraintParams || [];
+		params = params || {};
+
+		this.manager = new ResourceManager();
+
+		this.mesh = mesh;
+
+		/*
+		 * 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 Vector3( 0, - 9.8 * 10, 0 );
+
+		if ( params.gravity !== undefined ) this.gravity.copy( params.gravity );
+
+		this.world = params.world !== undefined ? params.world : null; // experimental
+
+		this.bodies = [];
+		this.constraints = [];
+
+		this._init( mesh, rigidBodyParams, constraintParams );
+
+	}
+
+	MMDPhysics.prototype = {
+
+		constructor: MMDPhysics,
+
+		/**
+		 * Advances Physics calculation and updates bones.
+		 *
+		 * @param {Number} delta - time in second
+		 * @return {MMDPhysics}
+		 */
+		update: function ( delta ) {
+
+			var manager = this.manager;
+			var mesh = this.mesh;
+
+			// rigid bodies and constrains are for
+			// mesh's world scale (1, 1, 1).
+			// Convert to (1, 1, 1) if it isn't.
+
+			var isNonDefaultScale = false;
+
+			var position = manager.allocThreeVector3();
+			var quaternion = manager.allocThreeQuaternion();
+			var scale = manager.allocThreeVector3();
+
+			mesh.matrixWorld.decompose( position, quaternion, scale );
+
+			if ( scale.x !== 1 || scale.y !== 1 || scale.z !== 1 ) {
+
+				isNonDefaultScale = true;
+
+			}
+
+			var parent;
+
+			if ( isNonDefaultScale ) {
+
+				parent = mesh.parent;
+
+				if ( parent !== null ) mesh.parent = null;
+
+				scale.copy( this.mesh.scale );
+
+				mesh.scale.set( 1, 1, 1 );
+				mesh.updateMatrixWorld( true );
+
+			}
+
+			// calculate physics and update bones
+
+			this._updateRigidBodies();
+			this._stepSimulation( delta );
+			this._updateBones();
+
+			// restore mesh if converted above
+
+			if ( isNonDefaultScale ) {
+
+				if ( parent !== null ) mesh.parent = parent;
+
+				mesh.scale.copy( scale );
+
+			}
+
+			manager.freeThreeVector3( scale );
+			manager.freeThreeQuaternion( quaternion );
+			manager.freeThreeVector3( position );
+
+			return this;
+
+		},
+
+		/**
+		 * Resets rigid bodies transorm to current bone's.
+		 *
+		 * @return {MMDPhysics}
+		 */
+		reset: function () {
+
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
+
+				this.bodies[ i ].reset();
+
+			}
+
+			return this;
+
+		},
+
+		/**
+		 * Warm ups Rigid bodies. Calculates cycles steps.
+		 *
+		 * @param {Integer} cycles
+		 * @return {MMDPhysics}
+		 */
+		warmup: function ( cycles ) {
+
+			for ( var i = 0; i < cycles; i ++ ) {
+
+				this.update( 1 / 60 );
+
+			}
+
+			return this;
+
+		},
+
+		/**
+		 * Sets gravity.
+		 *
+		 * @param {Vector3} gravity
+		 * @return {MMDPhysicsHelper}
+		 */
+		setGravity: function ( gravity ) {
+
+			this.world.setGravity( new Ammo.btVector3( gravity.x, gravity.y, gravity.z ) );
+			this.gravity.copy( gravity );
+
+			return this;
+
+		},
+
+		/**
+		 * Creates MMDPhysicsHelper
+		 *
+		 * @return {MMDPhysicsHelper}
+		 */
+		createHelper: function () {
+
+			return new MMDPhysicsHelper( this.mesh, this );
+
+		},
+
+		// private methods
+
+		_init: function ( mesh, rigidBodyParams, constraintParams ) {
+
+			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;
+
+			if ( parent !== null ) parent = null;
+
+			var currentPosition = manager.allocThreeVector3();
+			var currentQuaternion = manager.allocThreeQuaternion();
+			var currentScale = manager.allocThreeVector3();
+
+			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 );
+
+			if ( this.world === null ) {
+
+				this.world = this._createWorld();
+				this.setGravity( this.gravity );
+
+			}
+
+			this._initRigidBodies( rigidBodyParams );
+			this._initConstraints( constraintParams );
+
+			if ( parent !== null ) mesh.parent = parent;
+
+			mesh.position.copy( currentPosition );
+			mesh.quaternion.copy( currentQuaternion );
+			mesh.scale.copy( currentScale );
+
+			mesh.updateMatrixWorld( true );
+
+			this.reset();
+
+			manager.freeThreeVector3( currentPosition );
+			manager.freeThreeQuaternion( currentQuaternion );
+			manager.freeThreeVector3( currentScale );
+
+		},
+
+		_createWorld: 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;
+
+		},
+
+		_initRigidBodies: function ( rigidBodies ) {
+
+			for ( var i = 0, il = rigidBodies.length; i < il; i ++ ) {
+
+				this.bodies.push( new RigidBody(
+					this.mesh, this.world, rigidBodies[ i ], this.manager ) );
+
+			}
+
+		},
+
+		_initConstraints: function ( constraints ) {
+
+			for ( var i = 0, il = constraints.length; i < il; i ++ ) {
+
+				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 ) );
+
+			}
+
+
+		},
+
+		_stepSimulation: function ( delta ) {
+
+			var unitStep = this.unitStep;
+			var stepTime = delta;
+			var maxStepNum = ( ( delta / unitStep ) | 0 ) + 1;
+
+			if ( stepTime < unitStep ) {
+
+				stepTime = unitStep;
+				maxStepNum = 1;
+
+			}
+
+			if ( maxStepNum > this.maxStepNum ) {
+
+				maxStepNum = this.maxStepNum;
+
+			}
+
+			this.world.stepSimulation( stepTime, maxStepNum, unitStep );
+
+		},
+
+		_updateRigidBodies: function () {
+
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
+
+				this.bodies[ i ].updateFromBone();
+
+			}
+
+		},
+
+		_updateBones: function () {
+
+			for ( var i = 0, il = this.bodies.length; i < il; i ++ ) {
+
+				this.bodies[ i ].updateBone();
+
+			}
+
+		}
+
+	};
+
+	/**
+	 * 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 = [];
+
+		// for Ammo.js
+		this.transforms = [];
+		this.quaternions = [];
+		this.vector3s = [];
+
+	}
+
+	ResourceManager.prototype = {
+
+		constructor: ResourceManager,
+
+		allocThreeVector3: function () {
+
+			return ( this.threeVector3s.length > 0 )
+				? this.threeVector3s.pop()
+				: new Vector3();
+
+		},
+
+		freeThreeVector3: function ( v ) {
+
+			this.threeVector3s.push( v );
+
+		},
+
+		allocThreeMatrix4: function () {
+
+			return ( this.threeMatrix4s.length > 0 )
+				? this.threeMatrix4s.pop()
+				: new Matrix4();
+
+		},
+
+		freeThreeMatrix4: function ( m ) {
+
+			this.threeMatrix4s.push( m );
+
+		},
+
+		allocThreeQuaternion: function () {
+
+			return ( this.threeQuaternions.length > 0 )
+				? this.threeQuaternions.pop()
+				: new Quaternion();
+
+		},
+
+		freeThreeQuaternion: function ( q ) {
+
+			this.threeQuaternions.push( q );
+
+		},
+
+		allocThreeEuler: function () {
+
+			return ( this.threeEulers.length > 0 )
+				? this.threeEulers.pop()
+				: new Euler();
+
+		},
+
+		freeThreeEuler: function ( e ) {
+
+			this.threeEulers.push( e );
+
+		},
+
+		allocTransform: function () {
+
+			return ( this.transforms.length > 0 )
+				? this.transforms.pop()
+				: new Ammo.btTransform();
+
+		},
+
+		freeTransform: function ( t ) {
+
+			this.transforms.push( t );
+
+		},
+
+		allocQuaternion: function () {
+
+			return ( this.quaternions.length > 0 )
+				? this.quaternions.pop()
+				: new Ammo.btQuaternion();
+
+		},
+
+		freeQuaternion: function ( q ) {
+
+			this.quaternions.push( q );
+
+		},
+
+		allocVector3: function () {
+
+			return ( this.vector3s.length > 0 )
+				? this.vector3s.pop()
+				: new Ammo.btVector3();
+
+		},
+
+		freeVector3: function ( v ) {
+
+			this.vector3s.push( v );
+
+		},
+
+		setIdentity: function ( t ) {
+
+			t.setIdentity();
+
+		},
+
+		getBasis: function ( t ) {
+
+			var q = this.allocQuaternion();
+			t.getBasis().getRotation( q );
+			return q;
+
+		},
+
+		getBasisAsMatrix3: function ( t ) {
+
+			var q = this.getBasis( t );
+			var m = this.quaternionToMatrix3( q );
+			this.freeQuaternion( q );
+			return m;
+
+		},
+
+		getOrigin: function ( t ) {
+
+			return t.getOrigin();
+
+		},
+
+		setOrigin: function ( t, v ) {
+
+			t.getOrigin().setValue( v.x(), v.y(), v.z() );
+
+		},
+
+		copyOrigin: function ( t1, t2 ) {
+
+			var o = t2.getOrigin();
+			this.setOrigin( t1, o );
+
+		},
+
+		setBasis: function ( t, q ) {
+
+			t.setRotation( q );
+
+		},
+
+		setBasisFromMatrix3: function ( t, m ) {
+
+			var q = this.matrix3ToQuaternion( m );
+			this.setBasis( t, q );
+			this.freeQuaternion( q );
+
+		},
+
+		setOriginFromArray3: function ( t, a ) {
+
+			t.getOrigin().setValue( a[ 0 ], a[ 1 ], a[ 2 ] );
+
+		},
+
+		setOriginFromThreeVector3: function ( t, v ) {
+
+			t.getOrigin().setValue( v.x, v.y, v.z );
+
+		},
+
+		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 ) );
+
+			this.freeThreeEuler( thE );
+			this.freeThreeQuaternion( thQ );
+
+		},
+
+		setBasisFromThreeQuaternion: function ( t, a ) {
+
+			var q = this.allocQuaternion();
+
+			q.setX( a.x );
+			q.setY( a.y );
+			q.setZ( a.z );
+			q.setW( a.w );
+			this.setBasis( t, q );
+
+			this.freeQuaternion( q );
+
+		},
+
+		multiplyTransforms: function ( t1, t2 ) {
+
+			var t = this.allocTransform();
+			this.setIdentity( t );
+
+			var m1 = this.getBasisAsMatrix3( t1 );
+			var m2 = this.getBasisAsMatrix3( t2 );
+
+			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 );
+
+			var m3 = this.multiplyMatrices3( m1, m2 );
+			this.setBasisFromMatrix3( t, m3 );
+
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
+
+			return t;
+
+		},
+
+		inverseTransform: function ( t ) {
+
+			var t2 = this.allocTransform();
+
+			var m1 = this.getBasisAsMatrix3( t );
+			var o = this.getOrigin( t );
+
+			var m2 = this.transposeMatrix3( m1 );
+			var v1 = this.negativeVector3( o );
+			var v2 = this.multiplyMatrix3ByVector3( m2, v1 );
+
+			this.setOrigin( t2, v2 );
+			this.setBasisFromMatrix3( t2, m2 );
+
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
+
+			return t2;
+
+		},
+
+		multiplyMatrices3: function ( m1, m2 ) {
+
+			var m3 = [];
+
+			var v10 = this.rowOfMatrix3( m1, 0 );
+			var v11 = this.rowOfMatrix3( m1, 1 );
+			var v12 = this.rowOfMatrix3( m1, 2 );
+
+			var v20 = this.columnOfMatrix3( m2, 0 );
+			var v21 = this.columnOfMatrix3( m2, 1 );
+			var v22 = this.columnOfMatrix3( m2, 2 );
+
+			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 );
+
+			return m3;
+
+		},
+
+		addVector3: function ( v1, v2 ) {
+
+			var v = this.allocVector3();
+			v.setValue( v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z() );
+			return v;
+
+		},
+
+		dotVectors3: function ( v1, v2 ) {
+
+			return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
+
+		},
+
+		rowOfMatrix3: function ( m, i ) {
+
+			var v = this.allocVector3();
+			v.setValue( m[ i * 3 + 0 ], m[ i * 3 + 1 ], m[ i * 3 + 2 ] );
+			return v;
+
+		},
+
+		columnOfMatrix3: function ( m, i ) {
+
+			var v = this.allocVector3();
+			v.setValue( m[ i + 0 ], m[ i + 3 ], m[ i + 6 ] );
+			return v;
+
+		},
+
+		negativeVector3: function ( v ) {
+
+			var v2 = this.allocVector3();
+			v2.setValue( - v.x(), - v.y(), - v.z() );
+			return v2;
+
+		},
+
+		multiplyMatrix3ByVector3: function ( m, v ) {
+
+			var v4 = this.allocVector3();
+
+			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 );
+
+			v4.setValue( x, y, z );
+
+			this.freeVector3( v0 );
+			this.freeVector3( v1 );
+			this.freeVector3( v2 );
+
+			return v4;
+
+		},
+
+		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;
+
+		},
+
+		quaternionToMatrix3: function ( q ) {
+
+			var m = [];
+
+			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 xy = x * y;
+			var yz = y * z;
+			var zx = z * x;
+
+			var xw = x * w;
+			var yw = y * w;
+			var zw = z * w;
+
+			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;
+
+		},
+
+		matrix3ToQuaternion: function ( m ) {
+
+			var t = m[ 0 ] + m[ 4 ] + m[ 8 ];
+			var s, x, y, z, w;
+
+			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;
+
+			} else if ( ( m[ 0 ] > m[ 4 ] ) && ( m[ 0 ] > m[ 8 ] ) ) {
+
+				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 ] ) {
+
+				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 {
+
+				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;
+
+			}
+
+			var q = this.allocQuaternion();
+			q.setX( x );
+			q.setY( y );
+			q.setZ( z );
+			q.setW( w );
+			return q;
+
+		}
+
+	};
+
+	/**
+	 * @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;
+
+		this.body = null;
+		this.bone = null;
+		this.boneOffsetForm = null;
+		this.boneOffsetFormInverse = null;
+
+		this._init();
+
+	}
+
+	RigidBody.prototype = {
+
+		constructor: MMDPhysics.RigidBody,
+
+		/**
+		 * Resets rigid body transform to the current bone's.
+		 *
+		 * @return {RigidBody}
+		 */
+		reset: function () {
+
+			this._setTransformFromBone();
+			return this;
+
+		},
+
+		/**
+		 * Updates rigid body's transform from the current bone.
+		 *
+		 * @return {RidigBody}
+		 */
+		updateFromBone: function () {
+
+			if ( this.params.boneIndex !== - 1 &&
+				this.params.type === 0 ) {
+
+				this._setTransformFromBone();
+
+			}
+
+			return this;
+
+		},
+
+		/**
+		 * Updates bone from the current ridid body's transform.
+		 *
+		 * @return {RidigBody}
+		 */
+		updateBone: function () {
+
+			if ( this.params.type === 0 ||
+				this.params.boneIndex === - 1 ) {
+
+				return this;
+
+			}
+
+			this._updateBoneRotation();
+
+			if ( this.params.type === 1 ) {
+
+				this._updateBonePosition();
+
+			}
+
+			this.bone.updateMatrixWorld( true );
+
+			if ( this.params.type === 2 ) {
+
+				this._setPositionFromBone();
+
+			}
+
+			return this;
+
+		},
+
+		// private methods
+
+		_init: function () {
+
+			function generateShape( p ) {
+
+				switch ( p.shapeType ) {
+
+					case 0:
+						return new Ammo.btSphereShape( p.width );
+
+					case 1:
+						return new Ammo.btBoxShape( new Ammo.btVector3( p.width, p.height, p.depth ) );
+
+					case 2:
+						return new Ammo.btCapsuleShape( p.width, p.height );
+
+					default:
+						throw 'unknown shape type ' + p.shapeType;
+
+				}
+
+			}
+
+			var manager = this.manager;
+			var params = this.params;
+			var bones = this.mesh.skeleton.bones;
+			var bone = ( params.boneIndex === - 1 )
+				? new Bone()
+				: bones[ params.boneIndex ];
+
+			var shape = generateShape( params );
+			var weight = ( params.type === 0 ) ? 0 : params.weight;
+			var localInertia = manager.allocVector3();
+			localInertia.setValue( 0, 0, 0 );
+
+			if ( weight !== 0 ) {
+
+				shape.calculateLocalInertia( weight, localInertia );
+
+			}
+
+			var boneOffsetForm = manager.allocTransform();
+			manager.setIdentity( boneOffsetForm );
+			manager.setOriginFromArray3( boneOffsetForm, params.position );
+			manager.setBasisFromArray3( boneOffsetForm, params.rotation );
+
+			var vector = manager.allocThreeVector3();
+			var boneForm = manager.allocTransform();
+			manager.setIdentity( boneForm );
+			manager.setOriginFromThreeVector3( boneForm, bone.getWorldPosition( vector ) );
+
+			var form = manager.multiplyTransforms( boneForm, boneOffsetForm );
+			var state = new Ammo.btDefaultMotionState( form );
+
+			var info = new Ammo.btRigidBodyConstructionInfo( weight, state, shape, localInertia );
+			info.set_m_friction( params.friction );
+			info.set_m_restitution( params.restitution );
+
+			var body = new Ammo.btRigidBody( info );
+
+			if ( params.type === 0 ) {
+
+				body.setCollisionFlags( body.getCollisionFlags() | 2 );
+
+				/*
+				 * 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 );
+
+			}
+
+			body.setDamping( params.positionDamping, params.rotationDamping );
+			body.setSleepingThresholds( 0, 0 );
+
+			this.world.addRigidBody( body, 1 << params.groupIndex, params.groupTarget );
+
+			this.body = body;
+			this.bone = bone;
+			this.boneOffsetForm = boneOffsetForm;
+			this.boneOffsetFormInverse = manager.inverseTransform( boneOffsetForm );
+
+			manager.freeVector3( localInertia );
+			manager.freeTransform( form );
+			manager.freeTransform( boneForm );
+			manager.freeThreeVector3( vector );
+
+		},
+
+		_getBoneTransform: function () {
+
+			var manager = this.manager;
+			var p = manager.allocThreeVector3();
+			var q = manager.allocThreeQuaternion();
+			var s = manager.allocThreeVector3();
+
+			this.bone.matrixWorld.decompose( p, q, s );
+
+			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 );
+
+			return form;
+
+		},
+
+		_getWorldTransformForBone: function () {
+
+			var manager = this.manager;
+			var tr = this.body.getCenterOfMassTransform();
+			return manager.multiplyTransforms( tr, this.boneOffsetFormInverse );
+
+		},
+
+		_setTransformFromBone: function () {
+
+			var manager = this.manager;
+			var form = this._getBoneTransform();
+
+			// TODO: check the most appropriate way to set
+			//this.body.setWorldTransform( form );
+			this.body.setCenterOfMassTransform( form );
+			this.body.getMotionState().setWorldTransform( form );
+
+			manager.freeTransform( form );
+
+		},
+
+		_setPositionFromBone: function () {
+
+			var manager = this.manager;
+			var form = this._getBoneTransform();
+
+			var tr = manager.allocTransform();
+			this.body.getMotionState().getWorldTransform( tr );
+			manager.copyOrigin( tr, form );
+
+			// TODO: check the most appropriate way to set
+			//this.body.setWorldTransform( tr );
+			this.body.setCenterOfMassTransform( tr );
+			this.body.getMotionState().setWorldTransform( tr );
+
+			manager.freeTransform( tr );
+			manager.freeTransform( form );
+
+		},
+
+		_updateBoneRotation: function () {
+
+			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();
+
+			thQ.set( q.x(), q.y(), q.z(), q.w() );
+			thQ2.setFromRotationMatrix( this.bone.matrixWorld );
+			thQ2.conjugate();
+			thQ2.multiply( thQ );
+
+			//this.bone.quaternion.multiply( thQ2 );
+
+			thQ3.setFromRotationMatrix( this.bone.matrix );
+
+			// Renormalizing quaternion here because repeatedly transforming
+			// quaternion continuously accumulates floating point error and
+			// can end up being overflow. See #15335
+			this.bone.quaternion.copy( thQ2.multiply( thQ3 ).normalize() );
+
+			manager.freeThreeQuaternion( thQ );
+			manager.freeThreeQuaternion( thQ2 );
+			manager.freeThreeQuaternion( thQ3 );
+
+			manager.freeQuaternion( q );
+			manager.freeTransform( tr );
+
+		},
+
+		_updateBonePosition: function () {
+
+			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() );
+
+			if ( this.bone.parent ) {
+
+				this.bone.parent.worldToLocal( thV );
+
+			}
+
+			this.bone.position.copy( thV );
+
+			manager.freeThreeVector3( thV );
+
+			manager.freeTransform( tr );
+
+		}
+
+	};
+
+	/**
+	 * @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,
+
+		// private method
+
+		_init: function () {
+
+			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 );
+
+			var formA = manager.allocTransform();
+			var formB = manager.allocTransform();
+
+			bodyA.body.getMotionState().getWorldTransform( formA );
+			bodyB.body.getMotionState().getWorldTransform( formB );
+
+			var formInverseA = manager.inverseTransform( formA );
+			var formInverseB = manager.inverseTransform( formB );
+
+			var formA2 = manager.multiplyTransforms( formInverseA, form );
+			var formB2 = manager.multiplyTransforms( formInverseB, form );
+
+			var constraint = new Ammo.btGeneric6DofSpringConstraint( bodyA.body, bodyB.body, formA2, formB2, true );
+
+			var lll = manager.allocVector3();
+			var lul = manager.allocVector3();
+			var all = manager.allocVector3();
+			var aul = manager.allocVector3();
+
+			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 ] );
+
+			constraint.setLinearLowerLimit( lll );
+			constraint.setLinearUpperLimit( lul );
+			constraint.setAngularLowerLimit( all );
+			constraint.setAngularUpperLimit( aul );
+
+			for ( var i = 0; i < 3; i ++ ) {
+
+				if ( params.springPosition[ i ] !== 0 ) {
+
+					constraint.enableSpring( i, true );
+					constraint.setStiffness( i, params.springPosition[ i ] );
+
+				}
+
+			}
+
+			for ( var i = 0; i < 3; i ++ ) {
+
+				if ( params.springRotation[ i ] !== 0 ) {
+
+					constraint.enableSpring( i + 3, true );
+					constraint.setStiffness( i + 3, params.springRotation[ i ] );
+
+				}
+
+			}
+
+			/*
+			 * 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 ++ ) {
+
+					// this parameter is from http://www20.atpages.jp/katwat/three.js_r58/examples/mytest37/mmd.three.js
+					constraint.setParam( 2, 0.475, i );
+
+				}
+
+			}
+
+			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 {Physics} physics
+	 */
+	function MMDPhysicsHelper( mesh, physics ) {
+
+		Object3D.call( this );
+
+		this.root = mesh;
+		this.physics = physics;
+
+		this.matrix.copy( mesh.matrixWorld );
+		this.matrixAutoUpdate = false;
+
+		this.materials = [];
+
+		this.materials.push(
+			new MeshBasicMaterial( {
+				color: new Color( 0xff8888 ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this.materials.push(
+			new MeshBasicMaterial( {
+				color: new Color( 0x88ff88 ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this.materials.push(
+			new MeshBasicMaterial( {
+				color: new Color( 0x8888ff ),
+				wireframe: true,
+				depthTest: false,
+				depthWrite: false,
+				opacity: 0.25,
+				transparent: true
+			} )
+		);
+
+		this._init();
+
+	}
+
+	MMDPhysicsHelper.prototype = Object.assign( Object.create( Object3D.prototype ), {
+
+		constructor: MMDPhysicsHelper,
+
+		/**
+		 * Updates Rigid Bodies visualization.
+		 */
+		updateMatrixWorld: function () {
+
+			var position = new Vector3();
+			var quaternion = new Quaternion();
+			var scale = new Vector3();
+			var matrixWorldInv = new Matrix4();
+
+			return function updateMatrixWorld( force ) {
+
+				var mesh = this.root;
+
+				if ( this.visible ) {
+
+					var bodies = this.physics.bodies;
+
+					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 ];
+
+						var tr = body.getCenterOfMassTransform();
+						var origin = tr.getOrigin();
+						var rotation = tr.getRotation();
+
+						child.position
+							.set( origin.x(), origin.y(), origin.z() )
+							.applyMatrix4( matrixWorldInv );
+
+						child.quaternion
+							.setFromRotationMatrix( matrixWorldInv )
+							.multiply(
+								quaternion.set(
+									rotation.x(), rotation.y(), rotation.z(), rotation.w() )
+							);
+
+					}
+
+				}
+
+				this.matrix
+					.copy( mesh.matrixWorld )
+					.decompose( position, quaternion, scale )
+					.compose( position, quaternion, scale.set( 1, 1, 1 ) );
+
+				Object3D.prototype.updateMatrixWorld.call( this, force );
+
+			};
+
+		}(),
+
+		// private method
+
+		_init: function () {
+
+			var bodies = this.physics.bodies;
+
+			function createGeometry( param ) {
+
+				switch ( param.shapeType ) {
+
+					case 0:
+						return new SphereBufferGeometry( param.width, 16, 8 );
+
+					case 1:
+						return new 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 CylinderBufferGeometry( radius, radius, cylinderHeight, segmentsRadius, segmentsHeight, true );
+				var upperSphere = new Mesh( new SphereBufferGeometry( radius, segmentsRadius, segmentsHeight, 0, Math.PI * 2, 0, Math.PI / 2 ) );
+				var lowerSphere = new Mesh( new 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 Mesh( createGeometry( param ), this.materials[ param.type ] ) );
+
+			}
+
+		}
+
+	} );
+
+	return MMDPhysics;
+
+} )();
+
+export { MMDPhysics };

+ 20 - 0
examples/jsm/animation/TimelinerController.d.ts

@@ -0,0 +1,20 @@
+import {
+  AnimationClip,
+  Scene,
+  Vector3
+} from '../../../src/Three';
+
+export class TimelinerController  {
+  constructor(scene: Scene, trackInfo: object[], onUpdate: () => void);
+
+  delKeyframe(channelName: string, time: number): void;
+  deserialize(structs: object): void;
+  getChannelKeyTimes(): number[];
+  getChannelNames(): string[];
+  init(): void;
+  moveKeyframe(channelName: string, time: number, delta: number, moveRemaining: boolean): void;
+  serialize(): object;
+  setDisplayTime(time: number): void;
+  setDuration(duration: number): void;
+  setKeyframe(channelName: string, time: number): void;
+}

+ 283 - 0
examples/jsm/animation/TimelinerController.js

@@ -0,0 +1,283 @@
+/**
+ * Controller class for the Timeliner GUI.
+ *
+ * Timeliner GUI library (required to use this class):
+ *
+ * 		../libs/timeliner_gui.min.js
+ *
+ * Source code:
+ *
+ * 		https://github.com/tschw/timeliner_gui
+ * 		https://github.com/zz85/timeliner (fork's origin)
+ *
+ * @author tschw
+ *
+ */
+
+import {
+	AnimationClip,
+	AnimationMixer,
+	AnimationUtils,
+	PropertyBinding
+} from "../../../build/three.module.js";
+
+var TimelinerController = function TimelinerController( scene, trackInfo, onUpdate ) {
+
+	this._scene = scene;
+	this._trackInfo = trackInfo;
+
+	this._onUpdate = onUpdate;
+
+	this._mixer = new AnimationMixer( scene );
+	this._clip = null;
+	this._action = null;
+
+	this._tracks = {};
+	this._propRefs = {};
+	this._channelNames = [];
+
+};
+
+TimelinerController.prototype = {
+
+	constructor: TimelinerController,
+
+	init: function () {
+
+		var tracks = [],
+			trackInfo = this._trackInfo;
+
+		for ( var i = 0, n = trackInfo.length; i !== n; ++ i ) {
+
+			var spec = trackInfo[ i ];
+
+			tracks.push( this._addTrack( spec.type, spec.propertyPath, spec.initialValue, spec.interpolation ) );
+
+		}
+
+		this._clip = new AnimationClip( 'editclip', 0, tracks );
+		this._action = this._mixer.clipAction( this._clip ).play();
+
+	},
+
+	setDisplayTime: function ( time ) {
+
+		this._action.time = time;
+		this._mixer.update( 0 );
+
+		this._onUpdate();
+
+	},
+
+	setDuration: function ( duration ) {
+
+		this._clip.duration = duration;
+
+	},
+
+	getChannelNames: function () {
+
+		return this._channelNames;
+
+	},
+
+	getChannelKeyTimes: function ( channelName ) {
+
+		return this._tracks[ channelName ].times;
+
+	},
+
+	setKeyframe: function ( channelName, time ) {
+
+		var track = this._tracks[ channelName ],
+			times = track.times,
+			index = Timeliner.binarySearch( times, time ),
+			values = track.values,
+			stride = track.getValueSize(),
+			offset = index * stride;
+
+		if ( index < 0 ) {
+
+			// insert new keyframe
+
+			index = ~ index;
+			offset = index * stride;
+
+			var nTimes = times.length + 1,
+				nValues = values.length + stride;
+
+			for ( var i = nTimes - 1; i !== index; -- i ) {
+
+				times[ i ] = times[ i - 1 ];
+
+			}
+
+			for ( var i = nValues - 1, e = offset + stride - 1; i !== e; -- i ) {
+
+				values[ i ] = values[ i - stride ];
+
+			}
+
+		}
+
+		times[ index ] = time;
+		this._propRefs[ channelName ].getValue( values, offset );
+
+	},
+
+	delKeyframe: function ( channelName, time ) {
+
+		var track = this._tracks[ channelName ],
+			times = track.times,
+			index = Timeliner.binarySearch( times, time );
+
+		// we disallow to remove the keyframe when it is the last one we have,
+		// since the animation system is designed to always produce a defined
+		// state
+
+		if ( times.length > 1 && index >= 0 ) {
+
+			var nTimes = times.length - 1,
+				values = track.values,
+				stride = track.getValueSize(),
+				nValues = values.length - stride;
+
+			// note: no track.getValueSize when array sizes are out of sync
+
+			for ( var i = index; i !== nTimes; ++ i ) {
+
+				times[ i ] = times[ i + 1 ];
+
+			}
+
+			times.pop();
+
+			for ( var offset = index * stride; offset !== nValues; ++ offset ) {
+
+				values[ offset ] = values[ offset + stride ];
+
+			}
+
+			values.length = nValues;
+
+		}
+
+	},
+
+	moveKeyframe: function ( channelName, time, delta, moveRemaining ) {
+
+		var track = this._tracks[ channelName ],
+			times = track.times,
+			index = Timeliner.binarySearch( times, time );
+
+		if ( index >= 0 ) {
+
+			var endAt = moveRemaining ? times.length : index + 1,
+				needsSort = times[ index - 1 ] <= time ||
+					! moveRemaining && time >= times[ index + 1 ];
+
+			while ( index !== endAt ) times[ index ++ ] += delta;
+
+			if ( needsSort ) this._sort( track );
+
+		}
+
+	},
+
+	serialize: function () {
+
+		var result = {
+				duration: this._clip.duration,
+				channels: {}
+			},
+
+			names = this._channelNames,
+			tracks = this._tracks,
+
+			channels = result.channels;
+
+		for ( var i = 0, n = names.length; i !== n; ++ i ) {
+
+			var name = names[ i ],
+				track = tracks[ name ];
+
+			channels[ name ] = {
+
+				times: track.times,
+				values: track.values
+
+			};
+
+		}
+
+		return result;
+
+	},
+
+	deserialize: function ( structs ) {
+
+		var names = this._channelNames,
+			tracks = this._tracks,
+
+			channels = structs.channels;
+
+		this.setDuration( structs.duration );
+
+		for ( var i = 0, n = names.length; i !== n; ++ i ) {
+
+			var name = names[ i ],
+				track = tracks[ name ],
+				data = channels[ name ];
+
+			this._setArray( track.times, data.times );
+			this._setArray( track.values, data.values );
+
+		}
+
+		// update display
+		this.setDisplayTime( this._mixer.time );
+
+	},
+
+	_sort: function ( track ) {
+
+		var times = track.times, order = AnimationUtils.getKeyframeOrder( times );
+
+		this._setArray( times, AnimationUtils.sortedArray( times, 1, order ) );
+
+		var values = track.values,
+			stride = track.getValueSize();
+
+		this._setArray( values, AnimationUtils.sortedArray( values, stride, order ) );
+
+	},
+
+	_setArray: function ( dst, src ) {
+
+		dst.length = 0;
+		dst.push.apply( dst, src );
+
+	},
+
+	_addTrack: function ( type, prop, initialValue, interpolation ) {
+
+		var track = new type( prop, [ 0 ], initialValue, interpolation );
+
+		// data must be in JS arrays so it can be resized
+		track.times = Array.prototype.slice.call( track.times );
+		track.values = Array.prototype.slice.call( track.values );
+
+		this._channelNames.push( prop );
+		this._tracks[ prop ] = track;
+
+		// for recording the state:
+		this._propRefs[ prop ] =
+				new PropertyBinding( this._scene, prop );
+
+		return track;
+
+	}
+
+};
+
+export { TimelinerController };

+ 6 - 3
examples/jsm/exporters/ColladaExporter.js

@@ -28,7 +28,9 @@ ColladaExporter.prototype = {
 
 	constructor: ColladaExporter,
 
-	parse: function ( object, onDone, options = {} ) {
+	parse: function ( object, onDone, options ) {
+
+		options = options || {};
 
 		options = Object.assign( {
 			version: '1.4.1',
@@ -388,7 +390,7 @@ ColladaExporter.prototype = {
 				var reflectivity = m.reflectivity || 0;
 
 				// Do not export and alpha map for the reasons mentioned in issue (#13792)
-				// in THREE.js alpha maps are black and white, but collada expects the alpha
+				// in three.js alpha maps are black and white, but collada expects the alpha
 				// channel to specify the transparency
 				var transparencyNode = '';
 				if ( m.transparent === true ) {
@@ -536,6 +538,7 @@ ColladaExporter.prototype = {
 				// the materials.
 				var mat = o.material || new MeshBasicMaterial();
 				var materials = Array.isArray( mat ) ? mat : [ mat ];
+
 				if ( geometry.groups.length > materials.length ) {
 
 					matidsArray = new Array( geometry.groups.length );
@@ -596,7 +599,7 @@ ColladaExporter.prototype = {
 			'<asset>' +
 			(
 				'<contributor>' +
-				'<authoring_tool>THREE.js Collada Exporter</authoring_tool>' +
+				'<authoring_tool>three.js Collada Exporter</authoring_tool>' +
 				( options.author !== null ? `<author>${ options.author }</author>` : '' ) +
 				'</contributor>' +
 				`<created>${ ( new Date() ).toISOString() }</created>` +

+ 20 - 0
examples/jsm/exporters/DRACOExporter.d.ts

@@ -0,0 +1,20 @@
+import {
+  BufferGeometry,
+  Geometry
+} from '../../../src/Three';
+
+export interface DRACOExporterOptions {
+  decodeSpeed?: number;
+  encodeSpeed?: number;
+  encoderMethod?: number;
+  quantization?: number[];
+  exportUvs?: boolean;
+  exportNormals?: boolean;
+  exportColor?: boolean;
+}
+
+export class DRACOExporter {
+  constructor();
+
+  parse(geometry: BufferGeometry | Geometry, options: DRACOExporterOptions): Int8Array;
+}

+ 212 - 0
examples/jsm/exporters/DRACOExporter.js

@@ -0,0 +1,212 @@
+/**
+ * Export draco compressed files from threejs geometry objects.
+ *
+ * Draco files are compressed and usually are smaller than conventional 3D file formats.
+ *
+ * The exporter receives a options object containing
+ *  - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality)
+ *  - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality)
+ *  - encoderMethod
+ *  - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)
+ *  - exportUvs
+ *  - exportNormals
+ *
+ * @class DRACOExporter
+ * @author tentone
+ */
+
+import {
+	BufferGeometry
+} from "../../../build/three.module.js";
+
+/* global DracoEncoderModule */
+
+var DRACOExporter = function () {};
+
+DRACOExporter.prototype = {
+
+	constructor: DRACOExporter,
+
+	parse: function ( geometry, options ) {
+
+
+		if ( DracoEncoderModule === undefined ) {
+
+			throw new Error( 'THREE.DRACOExporter: required the draco_decoder to work.' );
+
+		}
+
+		if ( options === undefined ) {
+
+			options = {
+
+				decodeSpeed: 5,
+				encodeSpeed: 5,
+				encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
+				quantization: [ 16, 8, 8, 8, 8 ],
+				exportUvs: true,
+				exportNormals: true,
+				exportColor: false,
+
+			};
+
+		}
+
+		var dracoEncoder = DracoEncoderModule();
+		var encoder = new dracoEncoder.Encoder();
+		var builder = new dracoEncoder.MeshBuilder();
+		var mesh = new dracoEncoder.Mesh();
+
+		if ( geometry.isGeometry === true ) {
+
+			var bufferGeometry = new BufferGeometry();
+			bufferGeometry.fromGeometry( geometry );
+			geometry = bufferGeometry;
+
+		}
+
+		if ( geometry.isBufferGeometry !== true ) {
+
+			throw new Error( 'THREE.DRACOExporter.parse(geometry, options): geometry is not a THREE.Geometry or BufferGeometry instance.' );
+
+		}
+
+		var vertices = geometry.getAttribute( 'position' );
+		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+
+		var faces = geometry.getIndex();
+
+		if ( faces !== null ) {
+
+			builder.AddFacesToMesh( mesh, faces.count, faces.array );
+
+		} else {
+
+			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+
+			for ( var i = 0; i < faces.length; i ++ ) {
+
+				faces[ i ] = i;
+
+			}
+
+			builder.AddFacesToMesh( mesh, vertices.count, faces );
+
+		}
+
+		if ( options.exportNormals === true ) {
+
+			var normals = geometry.getAttribute( 'normal' );
+
+			if ( normals !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+
+			}
+
+		}
+
+		if ( options.exportUvs === true ) {
+
+			var uvs = geometry.getAttribute( 'uv' );
+
+			if ( uvs !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+			}
+
+		}
+
+		if ( options.exportColor === true ) {
+
+			var colors = geometry.getAttribute( 'color' );
+
+			if ( colors !== undefined ) {
+
+				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+			}
+
+		}
+
+		//Compress using draco encoder
+
+		var encodedData = new dracoEncoder.DracoInt8Array();
+
+		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
+
+		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+
+		// Sets the desired encoding method for a given geometry.
+
+		if ( options.encoderMethod !== undefined ) {
+
+			encoder.SetEncodingMethod( options.encoderMethod );
+
+		}
+
+		// Sets the quantization (number of bits used to represent) compression options for a named attribute.
+		// The attribute values will be quantized in a box defined by the maximum extent of the attribute values.
+		if ( options.quantization !== undefined ) {
+
+			for ( var i = 0; i < 5; i ++ ) {
+
+				if ( options.quantization[ i ] !== undefined ) {
+
+					encoder.SetAttributeQuantization( i, options.quantization[ i ] );
+
+				}
+
+			}
+
+		}
+
+		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
+		dracoEncoder.destroy( mesh );
+
+		if ( length === 0 ) {
+
+			throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' );
+
+		}
+
+		//Copy encoded data to buffer.
+		var outputData = new Int8Array( new ArrayBuffer( length ) );
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			outputData[ i ] = encodedData.GetValue( i );
+
+		}
+
+		dracoEncoder.destroy( encodedData );
+		dracoEncoder.destroy( encoder );
+		dracoEncoder.destroy( builder );
+
+		return outputData;
+
+	}
+
+};
+
+// Encoder methods
+
+DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
+DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0;
+
+// Geometry type
+
+DRACOExporter.POINT_CLOUD = 0;
+DRACOExporter.TRIANGULAR_MESH = 1;
+
+// Attribute type
+
+DRACOExporter.INVALID = - 1;
+DRACOExporter.POSITION = 0;
+DRACOExporter.NORMAL = 1;
+DRACOExporter.COLOR = 2;
+DRACOExporter.TEX_COORD = 3;
+DRACOExporter.GENERIC = 4;
+
+export { DRACOExporter };

+ 105 - 0
examples/jsm/geometries/LightningStrike.d.ts

@@ -0,0 +1,105 @@
+import {
+  Vector3
+} from '../../../src/Three';
+
+export interface RandomGenerator {
+  random(): number;
+  getSeed(): number;
+  setSeed(seed: number): void;
+}
+
+export interface LightningSegment {
+  iteration: number;
+  pos0: Vector3;
+  pos1: Vector3;
+  linPos0: Vector3;
+  linPos1: Vector3;
+  up0: Vector3;
+  up1: Vector3;
+  radius0: number;
+  radius1: number;
+  fraction0: number;
+  fraction1: number;
+  positionVariationFactor: number;
+}
+
+export interface LightningSubray {
+  seed: number;
+  maxIterations: number;
+  recursion: number;
+  pos0: Vector3;
+  pos1: Vector3;
+  linPos0: Vector3;
+  linPos1: Vector3;
+  up0: Vector3;
+  up1: Vector3;
+  radius0: number;
+  radius1: number;
+  birthTime: number;
+  deathTime: number;
+  timeScale: number;
+  roughness: number;
+  straightness: number;
+  propagationTimeFactor: number;
+  vanishingTimeFactor: number;
+  endPropagationTime: number;
+  beginVanishingTime: number;
+}
+
+export interface RayParameters {
+  sourceOffset?: Vector3;
+  destOffset?: Vector3;
+
+  timeScale?: number;
+  roughness?: number;
+  straightness?: number;
+
+  up0?: Vector3;
+  up1?: Vector3;
+  radius0?: number;
+  radius1?: number;
+  radius0Factor? : number;
+  radius1Factor? : number;
+  minRadius? : number;
+
+  isEternal?: boolean;
+  birthTime?: number;
+  deathTime?: number;
+  propagationTimeFactor?: number;
+  vanishingTimeFactor?: number;
+  subrayPeriod?: number;
+  subrayDutyCycle?: number;
+
+  maxIterations?: number;
+  isStatic?: boolean;
+  ramification?: number;
+  maxSubrayRecursion?: number;
+  recursionProbability?: number;
+  generateUVs?: boolean;
+
+  randomGenerator?: RandomGenerator;
+  noiseSeed?: number;
+
+  onDecideSubrayCreation?: (segment: LightningSegment, lightningStrike: LightningStrike) => void;
+  onSubrayCreation?: (segment: LightningSegment, parentSubray: LightningSubray, childSubray: LightningSubray, lightningStrike: LightningStrike) => void;
+}
+
+export class LightningStrike {
+  constructor(rayParameters?: RayParameters);
+  copyParameters( dest?: RayParameters, source?: RayParameters): RayParameters;
+
+  // Ray states
+  static readonly RAY_INITIALIZED: number;
+  static readonly RAY_UNBORN: number;
+  static readonly RAY_PROPAGATING: number;
+  static readonly RAY_STEADY: number;
+  static readonly RAY_VANISHING: number;
+  static readonly RAY_EXTINGUISHED: number;
+
+  state: number;
+
+  update(time: number): void;
+
+  copy(source: LightningStrike): LightningStrike;
+  clone(): LightningStrike;
+}

+ 1021 - 0
examples/jsm/geometries/LightningStrike.js

@@ -0,0 +1,1021 @@
+/**
+ * @author yomboprime https://github.com/yomboprime
+ *
+ * @fileoverview LightningStrike object for creating lightning strikes and voltaic arcs.
+ *
+ *
+ * Usage
+ *
+ * var myRay = new LightningStrike( paramsObject );
+ * var myRayMesh = new Mesh( myRay, myMaterial );
+ * scene.add( myRayMesh );
+ * ...
+ * myRay.update( currentTime );
+ *
+ * The "currentTime" can vary its rate, go forwards, backwards or even jump, but it cannot be negative.
+ *
+ * You should normally leave the ray position to (0, 0, 0). You should control it by changing the sourceOffset and destOffset parameters.
+ *
+ *
+ * LightningStrike parameters
+ *
+ * The paramsObject can contain any of the following parameters.
+ *
+ * Legend:
+ * 'LightningStrike' (also called 'ray'): An independent voltaic arc with its ramifications and defined with a set of parameters.
+ * 'Subray': A ramification of the ray. It is not a LightningStrike object.
+ * 'Segment': A linear segment piece of a subray.
+ * 'Leaf segment': A ray segment which cannot be smaller.
+ *
+ *
+ * The following parameters can be changed any time and if they vary smoothly, the ray form will also change smoothly:
+ *
+ * @param {Vector3} sourceOffset The point where the ray starts.
+ *
+ * @param {Vector3} destOffset The point where the ray ends.
+ *
+ * @param {double} timeScale The rate at wich the ray form changes in time. Default: 1
+ *
+ * @param {double} roughness From 0 to 1. The higher the value, the more wrinkled is the ray. Default: 0.9
+ *
+ * @param {double} straightness From 0 to 1. The higher the value, the more straight will be a subray path. Default: 0.7
+ *
+ * @param {Vector3} up0 Ray 'up' direction at the ray starting point. Must be normalized. It should be perpendicular to the ray forward direction but it doesn't matter much.
+ *
+ * @param {Vector3} up1 Like the up0 parameter but at the end of the ray. Must be normalized.
+ *
+ * @param {double} radius0 Radius of the main ray trunk at the start point. Default: 1
+ *
+ * @param {double} radius1 Radius of the main ray trunk at the end point. Default: 1
+ *
+ * @param {double} radius0Factor The radius0 of a subray is this factor times the radius0 of its parent subray. Default: 0.5
+ *
+ * @param {double} radius1Factor The radius1 of a subray is this factor times the radius1 of its parent subray. Default: 0.2
+ *
+ * @param {minRadius} Minimum value a subray radius0 or radius1 can get. Default: 0.1
+ *
+ *
+ * The following parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly:
+ *
+ * @param {boolean} isEternal If true the ray never extinguishes. Otherwise its life is controlled by the 'birthTime' and 'deathTime' parameters. Default: true if any of those two parameters is undefined.
+ *
+ * @param {double} birthTime The time at which the ray starts its life and begins propagating. Only if isEternal is false. Default: None.
+ *
+ * @param {double} deathTime The time at which the ray ends vanishing and its life. Only if isEternal is false. Default: None.
+ *
+ * @param {double} propagationTimeFactor From 0 to 1. Lifetime factor at which the ray ends propagating and enters the steady phase. For example, 0.1 means it is propagating 1/10 of its lifetime. Default: 0.1
+ *
+ * @param {double} vanishingTimeFactor From 0 to 1. Lifetime factor at which the ray ends the steady phase and begins vanishing. For example, 0.9 means it is vanishing 1/10 of its lifetime. Default: 0.9
+ *
+ * @param {double} subrayPeriod Subrays cycle periodically. This is their time period. Default: 4
+ *
+ * @param {double} subrayDutyCycle From 0 to 1. This is the fraction of time a subray is active. Default: 0.6
+ *
+ *
+ * These parameters cannot change after lightning creation:
+ *
+ * @param {integer} maxIterations: Greater than 0. The number of ray's leaf segments is 2**maxIterations. Default: 9
+ *
+ * @param {boolean} isStatic Set to true only for rays which won't change over time and are not attached to moving objects (Rare case). It is used to set the vertex buffers non-dynamic. You can omit calling update() for these rays.
+ *
+ * @param {integer} ramification Greater than 0. Maximum number of child subrays a subray can have. Default: 5
+ *
+ * @param {integer} maxSubrayRecursion Greater than 0. Maximum level of recursion (subray descendant generations). Default: 3
+ *
+ * @param {double} recursionProbability From 0 to 1. The lower the value, the less chance each new generation of subrays has to generate new subrays. Default: 0.6
+ *
+ * @param {boolean} generateUVs If true, the ray geometry will have uv coordinates generated. u runs along the ray, and v across its perimeter. Default: false.
+ *
+ * @param {Object} randomGenerator Set here your random number generator which will seed the SimplexNoise and other decisions during ray tree creation.
+ * It can be used to generate repeatable rays. For that, set also the noiseSeed parameter, and each ray created with that generator and seed pair will be identical in time.
+ * The randomGenerator parameter should be an object with a random() function similar to Math.random, but seedable.
+ * It must have also a getSeed() method, which returns the current seed, and a setSeed( seed ) method, which accepts as seed a fractional number from 0 to 1, as well as any other number.
+ * The default value is an internal generator for some uses and Math.random for others (It is non-repeatable even if noiseSeed is supplied)
+ *
+ * @param {double} noiseSeed Seed used to make repeatable rays (see the randomGenerator)
+ *
+ * @param {function} onDecideSubrayCreation Set this to change the callback which decides subray creation. You can look at the default callback in the code (createDefaultSubrayCreationCallbacks)for more info.
+ *
+ * @param {function} onSubrayCreation This is another callback, more simple than the previous one. It can be used to adapt the form of subrays or other parameters once a subray has been created and initialized. It is used in the examples to adapt subrays to a sphere or to a plane.
+ *
+ *
+*/
+
+import {
+	BufferGeometry,
+	Float32BufferAttribute,
+	Math as _Math,
+	Mesh,
+	Uint32BufferAttribute,
+	Vector3
+} from "../../../build/three.module.js";
+import { SimplexNoise } from "../math/SimplexNoise.js";
+
+var LightningStrike = function ( rayParameters ) {
+
+	BufferGeometry.call( this );
+
+	this.type = 'LightningStrike';
+
+	// Set parameters, and set undefined parameters to default values
+	rayParameters = rayParameters || {};
+	this.init( LightningStrike.copyParameters( rayParameters, rayParameters ) );
+
+	// Creates and populates the mesh
+	this.createMesh();
+
+};
+
+LightningStrike.prototype = Object.create( BufferGeometry.prototype );
+
+LightningStrike.prototype.constructor = LightningStrike;
+
+LightningStrike.prototype.isLightningStrike = true;
+
+// Ray states
+LightningStrike.RAY_INITIALIZED = 0;
+LightningStrike.RAY_UNBORN = 1;
+LightningStrike.RAY_PROPAGATING = 2;
+LightningStrike.RAY_STEADY = 3;
+LightningStrike.RAY_VANISHING = 4;
+LightningStrike.RAY_EXTINGUISHED = 5;
+
+LightningStrike.COS30DEG = Math.cos( 30 * Math.PI / 180 );
+LightningStrike.SIN30DEG = Math.sin( 30 * Math.PI / 180 );
+
+LightningStrike.createRandomGenerator = function () {
+
+	var numSeeds = 2053;
+	var seeds = [];
+
+	for ( var i = 0; i < numSeeds; i ++ ) {
+
+		seeds.push( Math.random() );
+
+	}
+
+	var generator = {
+
+		currentSeed: 0,
+
+		random: function () {
+
+			var value = seeds[ generator.currentSeed ];
+
+			generator.currentSeed = ( generator.currentSeed + 1 ) % numSeeds;
+
+			return value;
+
+		},
+
+		getSeed: function () {
+
+			return generator.currentSeed / numSeeds;
+
+		},
+
+		setSeed: function ( seed ) {
+
+			generator.currentSeed = Math.floor( seed * numSeeds ) % numSeeds;
+
+		}
+
+	};
+
+	return generator;
+
+};
+
+LightningStrike.copyParameters = function ( dest, source ) {
+
+	source = source || {};
+	dest = dest || {};
+
+	var vecCopy = function ( v ) {
+
+		if ( source === dest ) {
+
+			return v;
+
+		} else {
+
+			return v.clone();
+
+		}
+
+	};
+
+	dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy( source.sourceOffset ) : new Vector3( 0, 100, 0 ),
+	dest.destOffset = source.destOffset !== undefined ? vecCopy( source.destOffset ) : new Vector3( 0, 0, 0 ),
+
+	dest.timeScale = source.timeScale !== undefined ? source.timeScale : 1,
+	dest.roughness = source.roughness !== undefined ? source.roughness : 0.9,
+	dest.straightness = source.straightness !== undefined ? source.straightness : 0.7,
+
+	dest.up0 = source.up0 !== undefined ? vecCopy( source.up0 ) : new Vector3( 0, 0, 1 );
+	dest.up1 = source.up1 !== undefined ? vecCopy( source.up1 ) : new Vector3( 0, 0, 1 ),
+	dest.radius0 = source.radius0 !== undefined ? source.radius0 : 1,
+	dest.radius1 = source.radius1 !== undefined ? source.radius1 : 1,
+	dest.radius0Factor = source.radius0Factor !== undefined ? source.radius0Factor : 0.5,
+	dest.radius1Factor = source.radius1Factor !== undefined ? source.radius1Factor : 0.2,
+	dest.minRadius = source.minRadius !== undefined ? source.minRadius : 0.2,
+
+	// These parameters should not be changed after lightning creation. They can be changed but the ray will change its form abruptly:
+
+	dest.isEternal = source.isEternal !== undefined ? source.isEternal : ( source.birthTime === undefined || source.deathTime === undefined ),
+	dest.birthTime = source.birthTime,
+	dest.deathTime = source.deathTime,
+	dest.propagationTimeFactor = source.propagationTimeFactor !== undefined ? source.propagationTimeFactor : 0.1,
+	dest.vanishingTimeFactor = source.vanishingTimeFactor !== undefined ? source.vanishingTimeFactor : 0.9,
+	dest.subrayPeriod = source.subrayPeriod !== undefined ? source.subrayPeriod : 4,
+	dest.subrayDutyCycle = source.subrayDutyCycle !== undefined ? source.subrayDutyCycle : 0.6;
+
+	// These parameters cannot change after lightning creation:
+
+	dest.maxIterations = source.maxIterations !== undefined ? source.maxIterations : 9;
+	dest.isStatic = source.isStatic !== undefined ? source.isStatic : false;
+	dest.ramification = source.ramification !== undefined ? source.ramification : 5;
+	dest.maxSubrayRecursion = source.maxSubrayRecursion !== undefined ? source.maxSubrayRecursion : 3;
+	dest.recursionProbability = source.recursionProbability !== undefined ? source.recursionProbability : 0.6;
+	dest.generateUVs = source.generateUVs !== undefined ? source.generateUVs : false;
+	dest.randomGenerator = source.randomGenerator,
+	dest.noiseSeed = source.noiseSeed,
+	dest.onDecideSubrayCreation = source.onDecideSubrayCreation,
+	dest.onSubrayCreation = source.onSubrayCreation;
+
+	return dest;
+
+};
+
+LightningStrike.prototype.update = function ( time ) {
+
+	if ( this.isStatic ) return;
+
+	if ( this.rayParameters.isEternal || ( this.rayParameters.birthTime <= time && time <= this.rayParameters.deathTime ) ) {
+
+		this.updateMesh( time );
+
+		if ( time < this.subrays[ 0 ].endPropagationTime ) {
+
+			this.state = LightningStrike.RAY_PROPAGATING;
+
+		} else if ( time > this.subrays[ 0 ].beginVanishingTime ) {
+
+			this.state = LightningStrike.RAY_VANISHING;
+
+		} else {
+
+			this.state = LightningStrike.RAY_STEADY;
+
+		}
+
+		this.visible = true;
+
+	} else {
+
+		this.visible = false;
+
+		if ( time < this.rayParameters.birthTime ) {
+
+			this.state = LightningStrike.RAY_UNBORN;
+
+		} else {
+
+			this.state = LightningStrike.RAY_EXTINGUISHED;
+
+		}
+
+	}
+
+};
+
+LightningStrike.prototype.init = function ( rayParameters ) {
+
+	// Init all the state from the parameters
+
+	this.rayParameters = rayParameters;
+
+	// These parameters cannot change after lightning creation:
+
+	this.maxIterations = rayParameters.maxIterations !== undefined ? Math.floor( rayParameters.maxIterations ) : 9;
+	rayParameters.maxIterations = this.maxIterations;
+	this.isStatic = rayParameters.isStatic !== undefined ? rayParameters.isStatic : false;
+	rayParameters.isStatic = this.isStatic;
+	this.ramification = rayParameters.ramification !== undefined ? Math.floor( rayParameters.ramification ) : 5;
+	rayParameters.ramification = this.ramification;
+	this.maxSubrayRecursion = rayParameters.maxSubrayRecursion !== undefined ? Math.floor( rayParameters.maxSubrayRecursion ) : 3;
+	rayParameters.maxSubrayRecursion = this.maxSubrayRecursion;
+	this.recursionProbability = rayParameters.recursionProbability !== undefined ? rayParameters.recursionProbability : 0.6;
+	rayParameters.recursionProbability = this.recursionProbability;
+	this.generateUVs = rayParameters.generateUVs !== undefined ? rayParameters.generateUVs : false;
+	rayParameters.generateUVs = this.generateUVs;
+
+	// Random generator
+	if ( rayParameters.randomGenerator !== undefined ) {
+
+		this.randomGenerator = rayParameters.randomGenerator;
+		this.seedGenerator = rayParameters.randomGenerator;
+
+		if ( rayParameters.noiseSeed !== undefined ) {
+
+			this.seedGenerator.setSeed( rayParameters.noiseSeed );
+
+		}
+
+	} else {
+
+		this.randomGenerator = LightningStrike.createRandomGenerator();
+		this.seedGenerator = Math;
+
+	}
+
+	// Ray creation callbacks
+	if ( rayParameters.onDecideSubrayCreation !== undefined ) {
+
+		this.onDecideSubrayCreation = rayParameters.onDecideSubrayCreation;
+
+	} else {
+
+		this.createDefaultSubrayCreationCallbacks();
+
+		if ( rayParameters.onSubrayCreation !== undefined ) {
+
+			this.onSubrayCreation = rayParameters.onSubrayCreation;
+
+		}
+
+	}
+
+	// Internal state
+
+	this.state = LightningStrike.RAY_INITIALIZED;
+
+	this.maxSubrays = Math.ceil( 1 + Math.pow( this.ramification, Math.max( 0, this.maxSubrayRecursion - 1 ) ) );
+	rayParameters.maxSubrays = this.maxSubrays;
+
+	this.maxRaySegments = 2 * ( 1 << this.maxIterations );
+
+	this.subrays = [];
+
+	for ( var i = 0; i < this.maxSubrays; i ++ ) {
+
+		this.subrays.push( this.createSubray() );
+
+	}
+
+	this.raySegments = [];
+
+	for ( var i = 0; i < this.maxRaySegments; i ++ ) {
+
+		this.raySegments.push( this.createSegment() );
+
+	}
+
+	this.time = 0;
+	this.timeFraction = 0;
+	this.currentSegmentCallback = null;
+	this.currentCreateTriangleVertices = this.generateUVs ? this.createTriangleVerticesWithUVs : this.createTriangleVerticesWithoutUVs;
+	this.numSubrays = 0;
+	this.currentSubray = null;
+	this.currentSegmentIndex = 0;
+	this.isInitialSegment = false;
+	this.subrayProbability = 0;
+
+	this.currentVertex = 0;
+	this.currentIndex = 0;
+	this.currentCoordinate = 0;
+	this.currentUVCoordinate = 0;
+	this.vertices = null;
+	this.uvs = null;
+	this.indices = null;
+	this.positionAttribute = null;
+	this.uvsAttribute = null;
+
+	this.simplexX = new SimplexNoise( this.seedGenerator );
+	this.simplexY = new SimplexNoise( this.seedGenerator );
+	this.simplexZ = new SimplexNoise( this.seedGenerator );
+
+	// Temp vectors
+	this.forwards = new Vector3();
+	this.forwardsFill = new Vector3();
+	this.side = new Vector3();
+	this.down = new Vector3();
+	this.middlePos = new Vector3();
+	this.middleLinPos = new Vector3();
+	this.newPos = new Vector3();
+	this.vPos = new Vector3();
+	this.cross1 = new Vector3();
+
+};
+
+LightningStrike.prototype.createMesh = function () {
+
+	var maxDrawableSegmentsPerSubRay = 1 << this.maxIterations;
+
+	var maxVerts = 3 * ( maxDrawableSegmentsPerSubRay + 1 ) * this.maxSubrays;
+	var maxIndices = 18 * maxDrawableSegmentsPerSubRay * this.maxSubrays;
+
+	this.vertices = new Float32Array( maxVerts * 3 );
+	this.indices = new Uint32Array( maxIndices );
+	if ( this.generateUVs ) {
+
+		this.uvs = new Float32Array( maxVerts * 2 );
+
+	}
+
+	// Populate the mesh
+	this.fillMesh( 0 );
+
+	this.setIndex( new Uint32BufferAttribute( this.indices, 1 ) );
+
+	this.positionAttribute = new Float32BufferAttribute( this.vertices, 3 );
+	this.addAttribute( 'position', this.positionAttribute );
+
+	if ( this.generateUVs ) {
+
+		this.uvsAttribute = new Float32BufferAttribute( new Float32Array( this.uvs ), 2 );
+		this.addAttribute( 'uv', this.uvsAttribute );
+
+	}
+
+	if ( ! this.isStatic ) {
+
+		this.index.dynamic = true;
+		this.positionAttribute.dynamic = true;
+		if ( this.generateUVs ) {
+
+			this.uvsAttribute.dynamic = true;
+
+		}
+
+	}
+
+	// Store buffers for later modification
+	this.vertices = this.positionAttribute.array;
+	this.indices = this.index.array;
+	if ( this.generateUVs ) {
+
+		this.uvs = this.uvsAttribute.array;
+
+	}
+
+};
+
+LightningStrike.prototype.updateMesh = function ( time ) {
+
+	this.fillMesh( time );
+
+	this.drawRange.count = this.currentIndex;
+
+	this.index.needsUpdate = true;
+
+	this.positionAttribute.needsUpdate = true;
+
+	if ( this.generateUVs ) {
+
+		this.uvsAttribute.needsUpdate = true;
+
+	}
+
+};
+
+LightningStrike.prototype.fillMesh = function ( time ) {
+
+	var scope = this;
+
+	this.currentVertex = 0;
+	this.currentIndex = 0;
+	this.currentCoordinate = 0;
+	this.currentUVCoordinate = 0;
+
+	this.fractalRay( time, function fillVertices( segment ) {
+
+		var subray = scope.currentSubray;
+
+		if ( time < subray.birthTime ) { //&& ( ! this.rayParameters.isEternal || scope.currentSubray.recursion > 0 ) ) {
+
+			return;
+
+		} else if ( this.rayParameters.isEternal && scope.currentSubray.recursion == 0 ) {
+
+			// Eternal rays don't propagate nor vanish, but its subrays do
+
+			scope.createPrism( segment );
+
+			scope.onDecideSubrayCreation( segment, scope );
+
+		} else if ( time < subray.endPropagationTime ) {
+
+			if ( scope.timeFraction >= segment.fraction0 * subray.propagationTimeFactor ) {
+
+				// Ray propagation has arrived to this segment
+
+				scope.createPrism( segment );
+
+				scope.onDecideSubrayCreation( segment, scope );
+
+			}
+
+		} else if ( time < subray.beginVanishingTime ) {
+
+			// Ray is steady (nor propagating nor vanishing)
+
+			scope.createPrism( segment );
+
+			scope.onDecideSubrayCreation( segment, scope );
+
+		} else {
+
+			if ( scope.timeFraction <= subray.vanishingTimeFactor + segment.fraction1 * ( 1 - subray.vanishingTimeFactor ) ) {
+
+				// Segment has not yet vanished
+
+				scope.createPrism( segment );
+
+			}
+
+			scope.onDecideSubrayCreation( segment, scope );
+
+		}
+
+	} );
+
+};
+
+LightningStrike.prototype.addNewSubray = function ( rayParameters ) {
+
+	return this.subrays[ this.numSubrays ++ ];
+
+};
+
+LightningStrike.prototype.initSubray = function ( subray, rayParameters ) {
+
+	subray.pos0.copy( rayParameters.sourceOffset );
+	subray.pos1.copy( rayParameters.destOffset );
+	subray.up0.copy( rayParameters.up0 );
+	subray.up1.copy( rayParameters.up1 );
+	subray.radius0 = rayParameters.radius0;
+	subray.radius1 = rayParameters.radius1;
+	subray.birthTime = rayParameters.birthTime;
+	subray.deathTime = rayParameters.deathTime;
+	subray.timeScale = rayParameters.timeScale;
+	subray.roughness = rayParameters.roughness;
+	subray.straightness = rayParameters.straightness;
+	subray.propagationTimeFactor = rayParameters.propagationTimeFactor;
+	subray.vanishingTimeFactor = rayParameters.vanishingTimeFactor;
+
+	subray.maxIterations = this.maxIterations;
+	subray.seed = rayParameters.noiseSeed !== undefined ? rayParameters.noiseSeed : 0;
+	subray.recursion = 0;
+
+};
+
+LightningStrike.prototype.fractalRay = function ( time, segmentCallback ) {
+
+	this.time = time;
+	this.currentSegmentCallback = segmentCallback;
+	this.numSubrays = 0;
+
+	// Add the top level subray
+	this.initSubray( this.addNewSubray(), this.rayParameters );
+
+	// Process all subrays that are being generated until consuming all of them
+	for ( var subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex ++ ) {
+
+		var subray = this.subrays[ subrayIndex ];
+		this.currentSubray = subray;
+
+		this.randomGenerator.setSeed( subray.seed );
+
+		subray.endPropagationTime = _Math.lerp( subray.birthTime, subray.deathTime, subray.propagationTimeFactor );
+		subray.beginVanishingTime = _Math.lerp( subray.deathTime, subray.birthTime, 1 - subray.vanishingTimeFactor );
+
+		var random1 = this.randomGenerator.random;
+		subray.linPos0.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
+		subray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
+
+		this.timeFraction = ( time - subray.birthTime ) / ( subray.deathTime - subray.birthTime );
+
+		this.currentSegmentIndex = 0;
+		this.isInitialSegment = true;
+
+		var segment = this.getNewSegment();
+		segment.iteration = 0;
+		segment.pos0.copy( subray.pos0 );
+		segment.pos1.copy( subray.pos1 );
+		segment.linPos0.copy( subray.linPos0 );
+		segment.linPos1.copy( subray.linPos1 );
+		segment.up0.copy( subray.up0 );
+		segment.up1.copy( subray.up1 );
+		segment.radius0 = subray.radius0;
+		segment.radius1 = subray.radius1;
+		segment.fraction0 = 0;
+		segment.fraction1 = 1;
+		segment.positionVariationFactor = 1 - subray.straightness;
+
+		this.subrayProbability = this.ramification * Math.pow( this.recursionProbability, subray.recursion ) / ( 1 << subray.maxIterations );
+
+		this.fractalRayRecursive( segment );
+
+	}
+
+	this.currentSegmentCallback = null;
+	this.currentSubray = null;
+
+};
+
+LightningStrike.prototype.fractalRayRecursive = function ( segment ) {
+
+	// Leave recursion condition
+	if ( segment.iteration >= this.currentSubray.maxIterations ) {
+
+		this.currentSegmentCallback( segment );
+
+		return;
+
+	}
+
+	// Interpolation
+	this.forwards.subVectors( segment.pos1, segment.pos0 );
+	var lForwards = this.forwards.length();
+
+	if ( lForwards < 0.000001 ) {
+
+		this.forwards.set( 0, 0, 0.01 );
+		lForwards = this.forwards.length();
+
+	}
+
+	var middleRadius = ( segment.radius0 + segment.radius1 ) * 0.5;
+	var middleFraction = ( segment.fraction0 + segment.fraction1 ) * 0.5;
+
+	var timeDimension = this.time * this.currentSubray.timeScale * Math.pow( 2, segment.iteration );
+
+	this.middlePos.lerpVectors( segment.pos0, segment.pos1, 0.5 );
+	this.middleLinPos.lerpVectors( segment.linPos0, segment.linPos1, 0.5 );
+	var p = this.middleLinPos;
+
+	// Noise
+	this.newPos.set( this.simplexX.noise4d( p.x, p.y, p.z, timeDimension ),
+		this.simplexY.noise4d( p.x, p.y, p.z, timeDimension ),
+		this.simplexZ.noise4d( p.x, p.y, p.z, timeDimension ) );
+
+	this.newPos.multiplyScalar( segment.positionVariationFactor * lForwards );
+	this.newPos.add( this.middlePos );
+
+	// Recursion
+
+	var newSegment1 = this.getNewSegment();
+	newSegment1.pos0.copy( segment.pos0 );
+	newSegment1.pos1.copy( this.newPos );
+	newSegment1.linPos0.copy( segment.linPos0 );
+	newSegment1.linPos1.copy( this.middleLinPos );
+	newSegment1.up0.copy( segment.up0 );
+	newSegment1.up1.copy( segment.up1 );
+	newSegment1.radius0 = segment.radius0;
+	newSegment1.radius1 = middleRadius;
+	newSegment1.fraction0 = segment.fraction0;
+	newSegment1.fraction1 = middleFraction;
+	newSegment1.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness;
+	newSegment1.iteration = segment.iteration + 1;
+
+	var newSegment2 = this.getNewSegment();
+	newSegment2.pos0.copy( this.newPos );
+	newSegment2.pos1.copy( segment.pos1 );
+	newSegment2.linPos0.copy( this.middleLinPos );
+	newSegment2.linPos1.copy( segment.linPos1 );
+	this.cross1.crossVectors( segment.up0, this.forwards.normalize() );
+	newSegment2.up0.crossVectors( this.forwards, this.cross1 ).normalize();
+	newSegment2.up1.copy( segment.up1 );
+	newSegment2.radius0 = middleRadius;
+	newSegment2.radius1 = segment.radius1;
+	newSegment2.fraction0 = middleFraction;
+	newSegment2.fraction1 = segment.fraction1;
+	newSegment2.positionVariationFactor = segment.positionVariationFactor * this.currentSubray.roughness;
+	newSegment2.iteration = segment.iteration + 1;
+
+	this.fractalRayRecursive( newSegment1 );
+
+	this.fractalRayRecursive( newSegment2 );
+
+};
+
+LightningStrike.prototype.createPrism = function ( segment ) {
+
+	// Creates one triangular prism and its vertices at the segment
+
+	this.forwardsFill.subVectors( segment.pos1, segment.pos0 ).normalize();
+
+	if ( this.isInitialSegment ) {
+
+		this.currentCreateTriangleVertices( segment.pos0, segment.up0, this.forwardsFill, segment.radius0, 0 );
+
+		this.isInitialSegment = false;
+
+	}
+
+	this.currentCreateTriangleVertices( segment.pos1, segment.up0, this.forwardsFill, segment.radius1, segment.fraction1 );
+
+	this.createPrismFaces();
+
+};
+
+LightningStrike.prototype.createTriangleVerticesWithoutUVs = function ( pos, up, forwards, radius ) {
+
+	// Create an equilateral triangle (only vertices)
+
+	this.side.crossVectors( up, forwards ).multiplyScalar( radius * LightningStrike.COS30DEG );
+	this.down.copy( up ).multiplyScalar( - radius * LightningStrike.SIN30DEG );
+
+	var p = this.vPos;
+	var v = this.vertices;
+
+	p.copy( pos ).sub( this.side ).add( this.down );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	p.copy( pos ).add( this.side ).add( this.down );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	p.copy( up ).multiplyScalar( radius ).add( pos );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	this.currentVertex += 3;
+
+};
+
+LightningStrike.prototype.createTriangleVerticesWithUVs = function ( pos, up, forwards, radius, u ) {
+
+	// Create an equilateral triangle (only vertices)
+
+	this.side.crossVectors( up, forwards ).multiplyScalar( radius * LightningStrike.COS30DEG );
+	this.down.copy( up ).multiplyScalar( - radius * LightningStrike.SIN30DEG );
+
+	var p = this.vPos;
+	var v = this.vertices;
+	var uv = this.uvs;
+
+	p.copy( pos ).sub( this.side ).add( this.down );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 0;
+
+	p.copy( pos ).add( this.side ).add( this.down );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 0.5;
+
+	p.copy( up ).multiplyScalar( radius ).add( pos );
+
+	v[ this.currentCoordinate ++ ] = p.x;
+	v[ this.currentCoordinate ++ ] = p.y;
+	v[ this.currentCoordinate ++ ] = p.z;
+
+	uv[ this.currentUVCoordinate ++ ] = u;
+	uv[ this.currentUVCoordinate ++ ] = 1;
+
+	this.currentVertex += 3;
+
+};
+
+LightningStrike.prototype.createPrismFaces = function ( vertex, index ) {
+
+	var indices = this.indices;
+	var vertex = this.currentVertex - 6;
+
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 5;
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 5;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 1;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 4;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 0;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 2;
+	indices[ this.currentIndex ++ ] = vertex + 3;
+	indices[ this.currentIndex ++ ] = vertex + 5;
+
+};
+
+LightningStrike.prototype.createDefaultSubrayCreationCallbacks = function () {
+
+	var random1 = this.randomGenerator.random;
+
+	this.onDecideSubrayCreation = function ( segment, lightningStrike ) {
+
+		// Decide subrays creation at parent (sub)ray segment
+
+		var subray = lightningStrike.currentSubray;
+
+		var period = lightningStrike.rayParameters.subrayPeriod;
+		var dutyCycle = lightningStrike.rayParameters.subrayDutyCycle;
+
+		var phase0 = ( lightningStrike.rayParameters.isEternal && subray.recursion == 0 ) ? - random1() * period : _Math.lerp( subray.birthTime, subray.endPropagationTime, segment.fraction0 ) - random1() * period;
+
+		var phase = lightningStrike.time - phase0;
+		var currentCycle = Math.floor( phase / period );
+
+		var childSubraySeed = random1() * ( currentCycle + 1 );
+
+		var isActive = phase % period <= dutyCycle * period;
+
+		probability = lightningStrike.subrayProbability;
+		var probability = 0;
+
+		if ( isActive ) {
+
+			probability = lightningStrike.subrayProbability;
+			// Distribution test: probability *= segment.fraction0 > 0.5 && segment.fraction0 < 0.9 ? 1 / 0.4 : 0;
+
+		}
+
+		if ( subray.recursion < lightningStrike.maxSubrayRecursion && lightningStrike.numSubrays < lightningStrike.maxSubrays && random1() < probability ) {
+
+			var childSubray = lightningStrike.addNewSubray();
+
+			var parentSeed = lightningStrike.randomGenerator.getSeed();
+			childSubray.seed = childSubraySeed;
+			lightningStrike.randomGenerator.setSeed( childSubraySeed );
+
+			childSubray.recursion = subray.recursion + 1;
+			childSubray.maxIterations = Math.max( 1, subray.maxIterations - 1 );
+
+			childSubray.linPos0.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
+			childSubray.linPos1.set( random1(), random1(), random1() ).multiplyScalar( 1000 );
+			childSubray.up0.copy( subray.up0 );
+			childSubray.up1.copy( subray.up1 );
+			childSubray.radius0 = segment.radius0 * lightningStrike.rayParameters.radius0Factor;
+			childSubray.radius1 = Math.min( lightningStrike.rayParameters.minRadius, segment.radius1 * lightningStrike.rayParameters.radius1Factor );
+
+			childSubray.birthTime = phase0 + ( currentCycle ) * period;
+			childSubray.deathTime = childSubray.birthTime + period * dutyCycle;
+
+			if ( ! lightningStrike.rayParameters.isEternal && subray.recursion == 0 ) {
+
+				childSubray.birthTime = Math.max( childSubray.birthTime, subray.birthTime );
+				childSubray.deathTime = Math.min( childSubray.deathTime, subray.deathTime );
+
+			}
+
+			childSubray.timeScale = subray.timeScale * 2;
+			childSubray.roughness = subray.roughness;
+			childSubray.straightness = subray.straightness;
+			childSubray.propagationTimeFactor = subray.propagationTimeFactor;
+			childSubray.vanishingTimeFactor = subray.vanishingTimeFactor;
+
+			lightningStrike.onSubrayCreation( segment, subray, childSubray, lightningStrike );
+
+			lightningStrike.randomGenerator.setSeed( parentSeed );
+
+		}
+
+	};
+
+	var vec1Pos = new Vector3();
+	var vec2Forward = new Vector3();
+	var vec3Side = new Vector3();
+	var vec4Up = new Vector3();
+
+	this.onSubrayCreation = function ( segment, parentSubray, childSubray, lightningStrike ) {
+
+		// Decide childSubray origin and destination positions (pos0 and pos1) and possibly other properties of childSubray
+
+		// Just use the default cone position generator
+		lightningStrike.subrayCylinderPosition( segment, parentSubray, childSubray, 0.5, 0.6, 0.2 );
+
+	};
+
+	this.subrayConePosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
+
+		// Sets childSubray pos0 and pos1 in a cone
+
+		childSubray.pos0.copy( segment.pos0 );
+
+		vec1Pos.subVectors( parentSubray.pos1, parentSubray.pos0 );
+		vec2Forward.copy( vec1Pos ).normalize();
+		vec1Pos.multiplyScalar( segment.fraction0 + ( 1 - segment.fraction0 ) * ( random1() * heightFactor ) );
+		var length = vec1Pos.length();
+		vec3Side.crossVectors( parentSubray.up0, vec2Forward );
+		var angle = 2 * Math.PI * random1();
+		vec3Side.multiplyScalar( Math.cos( angle ) );
+		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
+
+		childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
+
+	};
+
+	this.subrayCylinderPosition = function ( segment, parentSubray, childSubray, heightFactor, sideWidthFactor, minSideWidthFactor ) {
+
+		// Sets childSubray pos0 and pos1 in a cylinder
+
+		childSubray.pos0.copy( segment.pos0 );
+
+		vec1Pos.subVectors( parentSubray.pos1, parentSubray.pos0 );
+		vec2Forward.copy( vec1Pos ).normalize();
+		vec1Pos.multiplyScalar( segment.fraction0 + ( 1 - segment.fraction0 ) * ( ( 2 * random1() - 1 ) * heightFactor ) );
+		var length = vec1Pos.length();
+		vec3Side.crossVectors( parentSubray.up0, vec2Forward );
+		var angle = 2 * Math.PI * random1();
+		vec3Side.multiplyScalar( Math.cos( angle ) );
+		vec4Up.copy( parentSubray.up0 ).multiplyScalar( Math.sin( angle ) );
+
+		childSubray.pos1.copy( vec3Side ).add( vec4Up ).multiplyScalar( length * sideWidthFactor * ( minSideWidthFactor + random1() * ( 1 - minSideWidthFactor ) ) ).add( vec1Pos ).add( parentSubray.pos0 );
+
+	};
+
+};
+
+LightningStrike.prototype.createSubray = function () {
+
+	return {
+
+		seed: 0,
+		maxIterations: 0,
+		recursion: 0,
+		pos0: new Vector3(),
+		pos1: new Vector3(),
+		linPos0: new Vector3(),
+		linPos1: new Vector3(),
+		up0: new Vector3(),
+		up1: new Vector3(),
+		radius0: 0,
+		radius1: 0,
+		birthTime: 0,
+		deathTime: 0,
+		timeScale: 0,
+		roughness: 0,
+		straightness: 0,
+		propagationTimeFactor: 0,
+		vanishingTimeFactor: 0,
+		endPropagationTime: 0,
+		beginVanishingTime: 0
+
+	};
+
+};
+
+LightningStrike.prototype.createSegment = function () {
+
+	return {
+		iteration: 0,
+		pos0: new Vector3(),
+		pos1: new Vector3(),
+		linPos0: new Vector3(),
+		linPos1: new Vector3(),
+		up0: new Vector3(),
+		up1: new Vector3(),
+		radius0: 0,
+		radius1: 0,
+		fraction0: 0,
+		fraction1: 0,
+		positionVariationFactor: 0
+	};
+
+};
+
+LightningStrike.prototype.getNewSegment = function () {
+
+	return this.raySegments[ this.currentSegmentIndex ++ ];
+
+};
+
+LightningStrike.prototype.copy = function ( source ) {
+
+	BufferGeometry.prototype.copy.call( this, source );
+
+	this.init( LightningStrike.copyParameters( {}, source.rayParameters ) );
+
+	return this;
+
+};
+
+LightningStrike.prototype.clone = function () {
+
+	return new this.constructor( LightningStrike.copyParameters( {}, this.rayParameters ) );
+
+};
+
+export { LightningStrike };

文件差异内容过多而无法显示
+ 2311 - 0
examples/jsm/libs/dat.gui.module.js


+ 28 - 0
examples/jsm/libs/gunzip.min.js

@@ -0,0 +1,28 @@
+/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */function n(e){throw e;}var q=void 0,aa={};function r(e,c){var d=e.split("."),b=aa;!(d[0]in b)&&b.execScript&&b.execScript("var "+d[0]);for(var a;d.length&&(a=d.shift());)!d.length&&c!==q?b[a]=c:b=b[a]?b[a]:b[a]={}};var u="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;new (u?Uint8Array:Array)(256);var v;for(v=0;256>v;++v)for(var w=v,ba=7,w=w>>>1;w;w>>>=1)--ba;function x(e,c,d){var b,a="number"===typeof c?c:c=0,f="number"===typeof d?d:e.length;b=-1;for(a=f&7;a--;++c)b=b>>>8^z[(b^e[c])&255];for(a=f>>3;a--;c+=8)b=b>>>8^z[(b^e[c])&255],b=b>>>8^z[(b^e[c+1])&255],b=b>>>8^z[(b^e[c+2])&255],b=b>>>8^z[(b^e[c+3])&255],b=b>>>8^z[(b^e[c+4])&255],b=b>>>8^z[(b^e[c+5])&255],b=b>>>8^z[(b^e[c+6])&255],b=b>>>8^z[(b^e[c+7])&255];return(b^4294967295)>>>0}
+var A=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,
+2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,
+2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,
+2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,
+3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,
+936918E3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117],z=u?new Uint32Array(A):A;function B(){}B.prototype.getName=function(){return this.name};B.prototype.getData=function(){return this.data};B.prototype.H=function(){return this.I};r("Zlib.GunzipMember",B);r("Zlib.GunzipMember.prototype.getName",B.prototype.getName);r("Zlib.GunzipMember.prototype.getData",B.prototype.getData);r("Zlib.GunzipMember.prototype.getMtime",B.prototype.H);function D(e){var c=e.length,d=0,b=Number.POSITIVE_INFINITY,a,f,g,k,m,p,t,h,l,y;for(h=0;h<c;++h)e[h]>d&&(d=e[h]),e[h]<b&&(b=e[h]);a=1<<d;f=new (u?Uint32Array:Array)(a);g=1;k=0;for(m=2;g<=d;){for(h=0;h<c;++h)if(e[h]===g){p=0;t=k;for(l=0;l<g;++l)p=p<<1|t&1,t>>=1;y=g<<16|h;for(l=p;l<a;l+=m)f[l]=y;++k}++g;k<<=1;m<<=1}return[f,d,b]};var E=[],F;for(F=0;288>F;F++)switch(!0){case 143>=F:E.push([F+48,8]);break;case 255>=F:E.push([F-144+400,9]);break;case 279>=F:E.push([F-256+0,7]);break;case 287>=F:E.push([F-280+192,8]);break;default:n("invalid literal: "+F)}
+var ca=function(){function e(a){switch(!0){case 3===a:return[257,a-3,0];case 4===a:return[258,a-4,0];case 5===a:return[259,a-5,0];case 6===a:return[260,a-6,0];case 7===a:return[261,a-7,0];case 8===a:return[262,a-8,0];case 9===a:return[263,a-9,0];case 10===a:return[264,a-10,0];case 12>=a:return[265,a-11,1];case 14>=a:return[266,a-13,1];case 16>=a:return[267,a-15,1];case 18>=a:return[268,a-17,1];case 22>=a:return[269,a-19,2];case 26>=a:return[270,a-23,2];case 30>=a:return[271,a-27,2];case 34>=a:return[272,
+a-31,2];case 42>=a:return[273,a-35,3];case 50>=a:return[274,a-43,3];case 58>=a:return[275,a-51,3];case 66>=a:return[276,a-59,3];case 82>=a:return[277,a-67,4];case 98>=a:return[278,a-83,4];case 114>=a:return[279,a-99,4];case 130>=a:return[280,a-115,4];case 162>=a:return[281,a-131,5];case 194>=a:return[282,a-163,5];case 226>=a:return[283,a-195,5];case 257>=a:return[284,a-227,5];case 258===a:return[285,a-258,0];default:n("invalid length: "+a)}}var c=[],d,b;for(d=3;258>=d;d++)b=e(d),c[d]=b[2]<<24|b[1]<<
+16|b[0];return c}();u&&new Uint32Array(ca);function G(e,c){this.i=[];this.j=32768;this.d=this.f=this.c=this.n=0;this.input=u?new Uint8Array(e):e;this.o=!1;this.k=H;this.z=!1;if(c||!(c={}))c.index&&(this.c=c.index),c.bufferSize&&(this.j=c.bufferSize),c.bufferType&&(this.k=c.bufferType),c.resize&&(this.z=c.resize);switch(this.k){case I:this.a=32768;this.b=new (u?Uint8Array:Array)(32768+this.j+258);break;case H:this.a=0;this.b=new (u?Uint8Array:Array)(this.j);this.e=this.F;this.q=this.B;this.l=this.D;break;default:n(Error("invalid inflate mode"))}}
+var I=0,H=1;
+G.prototype.g=function(){for(;!this.o;){var e=J(this,3);e&1&&(this.o=!0);e>>>=1;switch(e){case 0:var c=this.input,d=this.c,b=this.b,a=this.a,f=c.length,g=q,k=q,m=b.length,p=q;this.d=this.f=0;d+1>=f&&n(Error("invalid uncompressed block header: LEN"));g=c[d++]|c[d++]<<8;d+1>=f&&n(Error("invalid uncompressed block header: NLEN"));k=c[d++]|c[d++]<<8;g===~k&&n(Error("invalid uncompressed block header: length verify"));d+g>c.length&&n(Error("input buffer is broken"));switch(this.k){case I:for(;a+g>b.length;){p=
+m-a;g-=p;if(u)b.set(c.subarray(d,d+p),a),a+=p,d+=p;else for(;p--;)b[a++]=c[d++];this.a=a;b=this.e();a=this.a}break;case H:for(;a+g>b.length;)b=this.e({t:2});break;default:n(Error("invalid inflate mode"))}if(u)b.set(c.subarray(d,d+g),a),a+=g,d+=g;else for(;g--;)b[a++]=c[d++];this.c=d;this.a=a;this.b=b;break;case 1:this.l(da,ea);break;case 2:fa(this);break;default:n(Error("unknown BTYPE: "+e))}}return this.q()};
+var K=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],L=u?new Uint16Array(K):K,N=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],O=u?new Uint16Array(N):N,P=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],Q=u?new Uint8Array(P):P,R=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],ga=u?new Uint16Array(R):R,ha=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,
+13,13],U=u?new Uint8Array(ha):ha,V=new (u?Uint8Array:Array)(288),W,ia;W=0;for(ia=V.length;W<ia;++W)V[W]=143>=W?8:255>=W?9:279>=W?7:8;var da=D(V),X=new (u?Uint8Array:Array)(30),Y,ja;Y=0;for(ja=X.length;Y<ja;++Y)X[Y]=5;var ea=D(X);function J(e,c){for(var d=e.f,b=e.d,a=e.input,f=e.c,g=a.length,k;b<c;)f>=g&&n(Error("input buffer is broken")),d|=a[f++]<<b,b+=8;k=d&(1<<c)-1;e.f=d>>>c;e.d=b-c;e.c=f;return k}
+function Z(e,c){for(var d=e.f,b=e.d,a=e.input,f=e.c,g=a.length,k=c[0],m=c[1],p,t;b<m&&!(f>=g);)d|=a[f++]<<b,b+=8;p=k[d&(1<<m)-1];t=p>>>16;e.f=d>>t;e.d=b-t;e.c=f;return p&65535}
+function fa(e){function c(a,c,b){var d,e=this.w,f,g;for(g=0;g<a;)switch(d=Z(this,c),d){case 16:for(f=3+J(this,2);f--;)b[g++]=e;break;case 17:for(f=3+J(this,3);f--;)b[g++]=0;e=0;break;case 18:for(f=11+J(this,7);f--;)b[g++]=0;e=0;break;default:e=b[g++]=d}this.w=e;return b}var d=J(e,5)+257,b=J(e,5)+1,a=J(e,4)+4,f=new (u?Uint8Array:Array)(L.length),g,k,m,p;for(p=0;p<a;++p)f[L[p]]=J(e,3);if(!u){p=a;for(a=f.length;p<a;++p)f[L[p]]=0}g=D(f);k=new (u?Uint8Array:Array)(d);m=new (u?Uint8Array:Array)(b);e.w=
+0;e.l(D(c.call(e,d,g,k)),D(c.call(e,b,g,m)))}G.prototype.l=function(e,c){var d=this.b,b=this.a;this.r=e;for(var a=d.length-258,f,g,k,m;256!==(f=Z(this,e));)if(256>f)b>=a&&(this.a=b,d=this.e(),b=this.a),d[b++]=f;else{g=f-257;m=O[g];0<Q[g]&&(m+=J(this,Q[g]));f=Z(this,c);k=ga[f];0<U[f]&&(k+=J(this,U[f]));b>=a&&(this.a=b,d=this.e(),b=this.a);for(;m--;)d[b]=d[b++-k]}for(;8<=this.d;)this.d-=8,this.c--;this.a=b};
+G.prototype.D=function(e,c){var d=this.b,b=this.a;this.r=e;for(var a=d.length,f,g,k,m;256!==(f=Z(this,e));)if(256>f)b>=a&&(d=this.e(),a=d.length),d[b++]=f;else{g=f-257;m=O[g];0<Q[g]&&(m+=J(this,Q[g]));f=Z(this,c);k=ga[f];0<U[f]&&(k+=J(this,U[f]));b+m>a&&(d=this.e(),a=d.length);for(;m--;)d[b]=d[b++-k]}for(;8<=this.d;)this.d-=8,this.c--;this.a=b};
+G.prototype.e=function(){var e=new (u?Uint8Array:Array)(this.a-32768),c=this.a-32768,d,b,a=this.b;if(u)e.set(a.subarray(32768,e.length));else{d=0;for(b=e.length;d<b;++d)e[d]=a[d+32768]}this.i.push(e);this.n+=e.length;if(u)a.set(a.subarray(c,c+32768));else for(d=0;32768>d;++d)a[d]=a[c+d];this.a=32768;return a};
+G.prototype.F=function(e){var c,d=this.input.length/this.c+1|0,b,a,f,g=this.input,k=this.b;e&&("number"===typeof e.t&&(d=e.t),"number"===typeof e.A&&(d+=e.A));2>d?(b=(g.length-this.c)/this.r[2],f=258*(b/2)|0,a=f<k.length?k.length+f:k.length<<1):a=k.length*d;u?(c=new Uint8Array(a),c.set(k)):c=k;return this.b=c};
+G.prototype.q=function(){var e=0,c=this.b,d=this.i,b,a=new (u?Uint8Array:Array)(this.n+(this.a-32768)),f,g,k,m;if(0===d.length)return u?this.b.subarray(32768,this.a):this.b.slice(32768,this.a);f=0;for(g=d.length;f<g;++f){b=d[f];k=0;for(m=b.length;k<m;++k)a[e++]=b[k]}f=32768;for(g=this.a;f<g;++f)a[e++]=c[f];this.i=[];return this.buffer=a};
+G.prototype.B=function(){var e,c=this.a;u?this.z?(e=new Uint8Array(c),e.set(this.b.subarray(0,c))):e=this.b.subarray(0,c):(this.b.length>c&&(this.b.length=c),e=this.b);return this.buffer=e};function $(e){this.input=e;this.c=0;this.m=[];this.s=!1}$.prototype.G=function(){this.s||this.g();return this.m.slice()};
+$.prototype.g=function(){for(var e=this.input.length;this.c<e;){var c=new B,d=q,b=q,a=q,f=q,g=q,k=q,m=q,p=q,t=q,h=this.input,l=this.c;c.u=h[l++];c.v=h[l++];(31!==c.u||139!==c.v)&&n(Error("invalid file signature:"+c.u+","+c.v));c.p=h[l++];switch(c.p){case 8:break;default:n(Error("unknown compression method: "+c.p))}c.h=h[l++];p=h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24;c.I=new Date(1E3*p);c.O=h[l++];c.N=h[l++];0<(c.h&4)&&(c.J=h[l++]|h[l++]<<8,l+=c.J);if(0<(c.h&8)){m=[];for(k=0;0<(g=h[l++]);)m[k++]=String.fromCharCode(g);
+c.name=m.join("")}if(0<(c.h&16)){m=[];for(k=0;0<(g=h[l++]);)m[k++]=String.fromCharCode(g);c.K=m.join("")}0<(c.h&2)&&(c.C=x(h,0,l)&65535,c.C!==(h[l++]|h[l++]<<8)&&n(Error("invalid header crc16")));d=h[h.length-4]|h[h.length-3]<<8|h[h.length-2]<<16|h[h.length-1]<<24;h.length-l-4-4<512*d&&(f=d);b=new G(h,{index:l,bufferSize:f});c.data=a=b.g();l=b.c;c.L=t=(h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24)>>>0;x(a,q,q)!==t&&n(Error("invalid CRC-32 checksum: 0x"+x(a,q,q).toString(16)+" / 0x"+t.toString(16)));c.M=
+d=(h[l++]|h[l++]<<8|h[l++]<<16|h[l++]<<24)>>>0;(a.length&4294967295)!==d&&n(Error("invalid input size: "+(a.length&4294967295)+" / "+d));this.m.push(c);this.c=l}this.s=!0;var y=this.m,s,M,S=0,T=0,C;s=0;for(M=y.length;s<M;++s)T+=y[s].data.length;if(u){C=new Uint8Array(T);for(s=0;s<M;++s)C.set(y[s].data,S),S+=y[s].data.length}else{C=[];for(s=0;s<M;++s)C[s]=y[s].data;C=Array.prototype.concat.apply([],C)}return C};r("Zlib.Gunzip",$);r("Zlib.Gunzip.prototype.decompress",$.prototype.g);r("Zlib.Gunzip.prototype.getMembers",$.prototype.G);
+var Zlib=aa.Zlib;
+export { Zlib }

+ 17 - 0
examples/jsm/libs/inflate.min.js

@@ -0,0 +1,17 @@
+/** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */var mod={}, l=void 0,aa=mod;function r(c,d){var a=c.split("."),b=aa;!(a[0]in b)&&b.execScript&&b.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&d!==l?b[e]=d:b=b[e]?b[e]:b[e]={}};var t="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function v(c){var d=c.length,a=0,b=Number.POSITIVE_INFINITY,e,f,g,h,k,m,n,p,s,x;for(p=0;p<d;++p)c[p]>a&&(a=c[p]),c[p]<b&&(b=c[p]);e=1<<a;f=new (t?Uint32Array:Array)(e);g=1;h=0;for(k=2;g<=a;){for(p=0;p<d;++p)if(c[p]===g){m=0;n=h;for(s=0;s<g;++s)m=m<<1|n&1,n>>=1;x=g<<16|p;for(s=m;s<e;s+=k)f[s]=x;++h}++g;h<<=1;k<<=1}return[f,a,b]};function w(c,d){this.g=[];this.h=32768;this.d=this.f=this.a=this.l=0;this.input=t?new Uint8Array(c):c;this.m=!1;this.i=y;this.r=!1;if(d||!(d={}))d.index&&(this.a=d.index),d.bufferSize&&(this.h=d.bufferSize),d.bufferType&&(this.i=d.bufferType),d.resize&&(this.r=d.resize);switch(this.i){case A:this.b=32768;this.c=new (t?Uint8Array:Array)(32768+this.h+258);break;case y:this.b=0;this.c=new (t?Uint8Array:Array)(this.h);this.e=this.z;this.n=this.v;this.j=this.w;break;default:throw Error("invalid inflate mode");
+}}var A=0,y=1,B={t:A,s:y};
+w.prototype.k=function(){for(;!this.m;){var c=C(this,3);c&1&&(this.m=!0);c>>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=l,h=l,k=b.length,m=l;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case A:for(;e+
+g>b.length;){m=k-e;g-=m;if(t)b.set(d.subarray(a,a+m),e),e+=m,a+=m;else for(;m--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case y:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(t)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(ba,ca);break;case 2:for(var n=C(this,5)+257,p=C(this,5)+1,s=C(this,4)+4,x=new (t?Uint8Array:Array)(D.length),S=l,T=l,U=l,u=l,M=l,F=l,z=l,q=l,V=l,q=0;q<s;++q)x[D[q]]=
+C(this,3);if(!t){q=s;for(s=x.length;q<s;++q)x[D[q]]=0}S=v(x);u=new (t?Uint8Array:Array)(n+p);q=0;for(V=n+p;q<V;)switch(M=E(this,S),M){case 16:for(z=3+C(this,2);z--;)u[q++]=F;break;case 17:for(z=3+C(this,3);z--;)u[q++]=0;F=0;break;case 18:for(z=11+C(this,7);z--;)u[q++]=0;F=0;break;default:F=u[q++]=M}T=t?v(u.subarray(0,n)):v(u.slice(0,n));U=t?v(u.subarray(n)):v(u.slice(n));this.j(T,U);break;default:throw Error("unknown BTYPE: "+c);}}return this.n()};
+var G=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],D=t?new Uint16Array(G):G,H=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],I=t?new Uint16Array(H):H,J=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],K=t?new Uint8Array(J):J,L=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],da=t?new Uint16Array(L):L,ea=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,
+13,13],N=t?new Uint8Array(ea):ea,O=new (t?Uint8Array:Array)(288),P,fa;P=0;for(fa=O.length;P<fa;++P)O[P]=143>=P?8:255>=P?9:279>=P?7:8;var ba=v(O),Q=new (t?Uint8Array:Array)(30),R,ga;R=0;for(ga=Q.length;R<ga;++R)Q[R]=5;var ca=v(Q);function C(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h;b<d;){if(f>=g)throw Error("input buffer is broken");a|=e[f++]<<b;b+=8}h=a&(1<<d)-1;c.f=a>>>d;c.d=b-d;c.a=f;return h}
+function E(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],m,n;b<k&&!(f>=g);)a|=e[f++]<<b,b+=8;m=h[a&(1<<k)-1];n=m>>>16;if(n>b)throw Error("invalid code length: "+n);c.f=a>>n;c.d=b-n;c.a=f;return m&65535}
+w.prototype.j=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length-258,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=I[g];0<K[g]&&(k+=C(this,K[g]));f=E(this,d);h=da[f];0<N[f]&&(h+=C(this,N[f]));b>=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b};
+w.prototype.w=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=E(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=I[g];0<K[g]&&(k+=C(this,K[g]));f=E(this,d);h=da[f];0<N[f]&&(h+=C(this,N[f]));b+k>e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b};
+w.prototype.e=function(){var c=new (t?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(t)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;a<b;++a)c[a]=e[a+32768]}this.g.push(c);this.l+=c.length;if(t)e.set(e.subarray(d,d+32768));else for(a=0;32768>a;++a)e[a]=e[d+a];this.b=32768;return e};
+w.prototype.z=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.u&&(a+=c.u));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=f<h.length?h.length+f:h.length<<1):e=h.length*a;t?(d=new Uint8Array(e),d.set(h)):d=h;return this.c=d};
+w.prototype.n=function(){var c=0,d=this.c,a=this.g,b,e=new (t?Uint8Array:Array)(this.l+(this.b-32768)),f,g,h,k;if(0===a.length)return t?this.c.subarray(32768,this.b):this.c.slice(32768,this.b);f=0;for(g=a.length;f<g;++f){b=a[f];h=0;for(k=b.length;h<k;++h)e[c++]=b[h]}f=32768;for(g=this.b;f<g;++f)e[c++]=d[f];this.g=[];return this.buffer=e};
+w.prototype.v=function(){var c,d=this.b;t?this.r?(c=new Uint8Array(d),c.set(this.c.subarray(0,d))):c=this.c.subarray(0,d):(this.c.length>d&&(this.c.length=d),c=this.c);return this.buffer=c};function W(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.A=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case ha:this.method=ha;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.q=new w(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})}
+W.prototype.k=function(){var c=this.input,d,a;d=this.q.k();this.a=this.q.a;if(this.A){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f<g;f++)e[f]=(e[f].charCodeAt(0)&255)>>>0;b=e}for(var h=1,k=0,m=b.length,n,p=0;0<m;){n=1024<m?1024:m;m-=n;do h+=b[p++],k+=h;while(--n);h%=65521;k%=65521}if(a!==(k<<16|h)>>>0)throw Error("invalid adler-32 checksum");}return d};var ha=8;r("Zlib.Inflate",W);r("Zlib.Inflate.prototype.decompress",W.prototype.k);var X={ADAPTIVE:B.s,BLOCK:B.t},Y,Z,$,ia;if(Object.keys)Y=Object.keys(X);else for(Z in Y=[],$=0,X)Y[$++]=Z;$=0;for(ia=Y.length;$<ia;++$)Z=Y[$],r("Zlib.Inflate.BufferType."+Z,X[Z]);
+var Zlib=mod.Zlib;
+export { Zlib }

+ 11535 - 0
examples/jsm/libs/mmdparser.module.js

@@ -0,0 +1,11535 @@
+/**
+ * @author Takahiro / https://github.com/takahirox
+ *
+ * Simple CharsetEncoder.
+ */
+
+function CharsetEncoder() {
+}
+
+/*
+ * Converts from Shift_JIS Uint8Array data to Unicode strings.
+ */
+CharsetEncoder.prototype.s2u = function ( uint8Array ) {
+
+	var t = this.s2uTable;
+	var str = '';
+	var p = 0;
+
+	while ( p < uint8Array.length ) {
+
+		var key = uint8Array[ p ++ ];
+
+		if ( ! ( ( key >= 0x00 && key <= 0x7e ) ||
+          ( key >= 0xa1 && key <= 0xdf ) ) &&
+       p < uint8Array.length ) {
+
+			key = ( key << 8 ) | uint8Array[ p ++ ];
+
+		}
+
+		if ( t[ key ] === undefined ) {
+
+			throw 'unknown char code ' + key + '.';
+
+		}
+
+		str += String.fromCharCode( t[ key ] );
+
+	}
+
+	return str;
+
+};
+
+CharsetEncoder.prototype.s2uTable = {
+	0: 0,
+	1: 1,
+	2: 2,
+	3: 3,
+	4: 4,
+	5: 5,
+	6: 6,
+	7: 7,
+	8: 8,
+	9: 9,
+	10: 10,
+	11: 11,
+	12: 12,
+	13: 13,
+	14: 14,
+	15: 15,
+	16: 16,
+	17: 17,
+	18: 18,
+	19: 19,
+	20: 20,
+	21: 21,
+	22: 22,
+	23: 23,
+	24: 24,
+	25: 25,
+	26: 26,
+	27: 27,
+	28: 28,
+	29: 29,
+	30: 30,
+	31: 31,
+	32: 32,
+	33: 33,
+	34: 34,
+	35: 35,
+	36: 36,
+	37: 37,
+	38: 38,
+	39: 39,
+	40: 40,
+	41: 41,
+	42: 42,
+	43: 43,
+	44: 44,
+	45: 45,
+	46: 46,
+	47: 47,
+	48: 48,
+	49: 49,
+	50: 50,
+	51: 51,
+	52: 52,
+	53: 53,
+	54: 54,
+	55: 55,
+	56: 56,
+	57: 57,
+	58: 58,
+	59: 59,
+	60: 60,
+	61: 61,
+	62: 62,
+	63: 63,
+	64: 64,
+	65: 65,
+	66: 66,
+	67: 67,
+	68: 68,
+	69: 69,
+	70: 70,
+	71: 71,
+	72: 72,
+	73: 73,
+	74: 74,
+	75: 75,
+	76: 76,
+	77: 77,
+	78: 78,
+	79: 79,
+	80: 80,
+	81: 81,
+	82: 82,
+	83: 83,
+	84: 84,
+	85: 85,
+	86: 86,
+	87: 87,
+	88: 88,
+	89: 89,
+	90: 90,
+	91: 91,
+	92: 92,
+	93: 93,
+	94: 94,
+	95: 95,
+	96: 96,
+	97: 97,
+	98: 98,
+	99: 99,
+	100: 100,
+	101: 101,
+	102: 102,
+	103: 103,
+	104: 104,
+	105: 105,
+	106: 106,
+	107: 107,
+	108: 108,
+	109: 109,
+	110: 110,
+	111: 111,
+	112: 112,
+	113: 113,
+	114: 114,
+	115: 115,
+	116: 116,
+	117: 117,
+	118: 118,
+	119: 119,
+	120: 120,
+	121: 121,
+	122: 122,
+	123: 123,
+	124: 124,
+	125: 125,
+	126: 126,
+	161: 65377,
+	162: 65378,
+	163: 65379,
+	164: 65380,
+	165: 65381,
+	166: 65382,
+	167: 65383,
+	168: 65384,
+	169: 65385,
+	170: 65386,
+	171: 65387,
+	172: 65388,
+	173: 65389,
+	174: 65390,
+	175: 65391,
+	176: 65392,
+	177: 65393,
+	178: 65394,
+	179: 65395,
+	180: 65396,
+	181: 65397,
+	182: 65398,
+	183: 65399,
+	184: 65400,
+	185: 65401,
+	186: 65402,
+	187: 65403,
+	188: 65404,
+	189: 65405,
+	190: 65406,
+	191: 65407,
+	192: 65408,
+	193: 65409,
+	194: 65410,
+	195: 65411,
+	196: 65412,
+	197: 65413,
+	198: 65414,
+	199: 65415,
+	200: 65416,
+	201: 65417,
+	202: 65418,
+	203: 65419,
+	204: 65420,
+	205: 65421,
+	206: 65422,
+	207: 65423,
+	208: 65424,
+	209: 65425,
+	210: 65426,
+	211: 65427,
+	212: 65428,
+	213: 65429,
+	214: 65430,
+	215: 65431,
+	216: 65432,
+	217: 65433,
+	218: 65434,
+	219: 65435,
+	220: 65436,
+	221: 65437,
+	222: 65438,
+	223: 65439,
+	33088: 12288,
+	33089: 12289,
+	33090: 12290,
+	33091: 65292,
+	33092: 65294,
+	33093: 12539,
+	33094: 65306,
+	33095: 65307,
+	33096: 65311,
+	33097: 65281,
+	33098: 12443,
+	33099: 12444,
+	33100: 180,
+	33101: 65344,
+	33102: 168,
+	33103: 65342,
+	33104: 65507,
+	33105: 65343,
+	33106: 12541,
+	33107: 12542,
+	33108: 12445,
+	33109: 12446,
+	33110: 12291,
+	33111: 20189,
+	33112: 12293,
+	33113: 12294,
+	33114: 12295,
+	33115: 12540,
+	33116: 8213,
+	33117: 8208,
+	33118: 65295,
+	33119: 65340,
+	33120: 65374,
+	33121: 8741,
+	33122: 65372,
+	33123: 8230,
+	33124: 8229,
+	33125: 8216,
+	33126: 8217,
+	33127: 8220,
+	33128: 8221,
+	33129: 65288,
+	33130: 65289,
+	33131: 12308,
+	33132: 12309,
+	33133: 65339,
+	33134: 65341,
+	33135: 65371,
+	33136: 65373,
+	33137: 12296,
+	33138: 12297,
+	33139: 12298,
+	33140: 12299,
+	33141: 12300,
+	33142: 12301,
+	33143: 12302,
+	33144: 12303,
+	33145: 12304,
+	33146: 12305,
+	33147: 65291,
+	33148: 65293,
+	33149: 177,
+	33150: 215,
+	33152: 247,
+	33153: 65309,
+	33154: 8800,
+	33155: 65308,
+	33156: 65310,
+	33157: 8806,
+	33158: 8807,
+	33159: 8734,
+	33160: 8756,
+	33161: 9794,
+	33162: 9792,
+	33163: 176,
+	33164: 8242,
+	33165: 8243,
+	33166: 8451,
+	33167: 65509,
+	33168: 65284,
+	33169: 65504,
+	33170: 65505,
+	33171: 65285,
+	33172: 65283,
+	33173: 65286,
+	33174: 65290,
+	33175: 65312,
+	33176: 167,
+	33177: 9734,
+	33178: 9733,
+	33179: 9675,
+	33180: 9679,
+	33181: 9678,
+	33182: 9671,
+	33183: 9670,
+	33184: 9633,
+	33185: 9632,
+	33186: 9651,
+	33187: 9650,
+	33188: 9661,
+	33189: 9660,
+	33190: 8251,
+	33191: 12306,
+	33192: 8594,
+	33193: 8592,
+	33194: 8593,
+	33195: 8595,
+	33196: 12307,
+	33208: 8712,
+	33209: 8715,
+	33210: 8838,
+	33211: 8839,
+	33212: 8834,
+	33213: 8835,
+	33214: 8746,
+	33215: 8745,
+	33224: 8743,
+	33225: 8744,
+	33226: 65506,
+	33227: 8658,
+	33228: 8660,
+	33229: 8704,
+	33230: 8707,
+	33242: 8736,
+	33243: 8869,
+	33244: 8978,
+	33245: 8706,
+	33246: 8711,
+	33247: 8801,
+	33248: 8786,
+	33249: 8810,
+	33250: 8811,
+	33251: 8730,
+	33252: 8765,
+	33253: 8733,
+	33254: 8757,
+	33255: 8747,
+	33256: 8748,
+	33264: 8491,
+	33265: 8240,
+	33266: 9839,
+	33267: 9837,
+	33268: 9834,
+	33269: 8224,
+	33270: 8225,
+	33271: 182,
+	33276: 9711,
+	33359: 65296,
+	33360: 65297,
+	33361: 65298,
+	33362: 65299,
+	33363: 65300,
+	33364: 65301,
+	33365: 65302,
+	33366: 65303,
+	33367: 65304,
+	33368: 65305,
+	33376: 65313,
+	33377: 65314,
+	33378: 65315,
+	33379: 65316,
+	33380: 65317,
+	33381: 65318,
+	33382: 65319,
+	33383: 65320,
+	33384: 65321,
+	33385: 65322,
+	33386: 65323,
+	33387: 65324,
+	33388: 65325,
+	33389: 65326,
+	33390: 65327,
+	33391: 65328,
+	33392: 65329,
+	33393: 65330,
+	33394: 65331,
+	33395: 65332,
+	33396: 65333,
+	33397: 65334,
+	33398: 65335,
+	33399: 65336,
+	33400: 65337,
+	33401: 65338,
+	33409: 65345,
+	33410: 65346,
+	33411: 65347,
+	33412: 65348,
+	33413: 65349,
+	33414: 65350,
+	33415: 65351,
+	33416: 65352,
+	33417: 65353,
+	33418: 65354,
+	33419: 65355,
+	33420: 65356,
+	33421: 65357,
+	33422: 65358,
+	33423: 65359,
+	33424: 65360,
+	33425: 65361,
+	33426: 65362,
+	33427: 65363,
+	33428: 65364,
+	33429: 65365,
+	33430: 65366,
+	33431: 65367,
+	33432: 65368,
+	33433: 65369,
+	33434: 65370,
+	33439: 12353,
+	33440: 12354,
+	33441: 12355,
+	33442: 12356,
+	33443: 12357,
+	33444: 12358,
+	33445: 12359,
+	33446: 12360,
+	33447: 12361,
+	33448: 12362,
+	33449: 12363,
+	33450: 12364,
+	33451: 12365,
+	33452: 12366,
+	33453: 12367,
+	33454: 12368,
+	33455: 12369,
+	33456: 12370,
+	33457: 12371,
+	33458: 12372,
+	33459: 12373,
+	33460: 12374,
+	33461: 12375,
+	33462: 12376,
+	33463: 12377,
+	33464: 12378,
+	33465: 12379,
+	33466: 12380,
+	33467: 12381,
+	33468: 12382,
+	33469: 12383,
+	33470: 12384,
+	33471: 12385,
+	33472: 12386,
+	33473: 12387,
+	33474: 12388,
+	33475: 12389,
+	33476: 12390,
+	33477: 12391,
+	33478: 12392,
+	33479: 12393,
+	33480: 12394,
+	33481: 12395,
+	33482: 12396,
+	33483: 12397,
+	33484: 12398,
+	33485: 12399,
+	33486: 12400,
+	33487: 12401,
+	33488: 12402,
+	33489: 12403,
+	33490: 12404,
+	33491: 12405,
+	33492: 12406,
+	33493: 12407,
+	33494: 12408,
+	33495: 12409,
+	33496: 12410,
+	33497: 12411,
+	33498: 12412,
+	33499: 12413,
+	33500: 12414,
+	33501: 12415,
+	33502: 12416,
+	33503: 12417,
+	33504: 12418,
+	33505: 12419,
+	33506: 12420,
+	33507: 12421,
+	33508: 12422,
+	33509: 12423,
+	33510: 12424,
+	33511: 12425,
+	33512: 12426,
+	33513: 12427,
+	33514: 12428,
+	33515: 12429,
+	33516: 12430,
+	33517: 12431,
+	33518: 12432,
+	33519: 12433,
+	33520: 12434,
+	33521: 12435,
+	33600: 12449,
+	33601: 12450,
+	33602: 12451,
+	33603: 12452,
+	33604: 12453,
+	33605: 12454,
+	33606: 12455,
+	33607: 12456,
+	33608: 12457,
+	33609: 12458,
+	33610: 12459,
+	33611: 12460,
+	33612: 12461,
+	33613: 12462,
+	33614: 12463,
+	33615: 12464,
+	33616: 12465,
+	33617: 12466,
+	33618: 12467,
+	33619: 12468,
+	33620: 12469,
+	33621: 12470,
+	33622: 12471,
+	33623: 12472,
+	33624: 12473,
+	33625: 12474,
+	33626: 12475,
+	33627: 12476,
+	33628: 12477,
+	33629: 12478,
+	33630: 12479,
+	33631: 12480,
+	33632: 12481,
+	33633: 12482,
+	33634: 12483,
+	33635: 12484,
+	33636: 12485,
+	33637: 12486,
+	33638: 12487,
+	33639: 12488,
+	33640: 12489,
+	33641: 12490,
+	33642: 12491,
+	33643: 12492,
+	33644: 12493,
+	33645: 12494,
+	33646: 12495,
+	33647: 12496,
+	33648: 12497,
+	33649: 12498,
+	33650: 12499,
+	33651: 12500,
+	33652: 12501,
+	33653: 12502,
+	33654: 12503,
+	33655: 12504,
+	33656: 12505,
+	33657: 12506,
+	33658: 12507,
+	33659: 12508,
+	33660: 12509,
+	33661: 12510,
+	33662: 12511,
+	33664: 12512,
+	33665: 12513,
+	33666: 12514,
+	33667: 12515,
+	33668: 12516,
+	33669: 12517,
+	33670: 12518,
+	33671: 12519,
+	33672: 12520,
+	33673: 12521,
+	33674: 12522,
+	33675: 12523,
+	33676: 12524,
+	33677: 12525,
+	33678: 12526,
+	33679: 12527,
+	33680: 12528,
+	33681: 12529,
+	33682: 12530,
+	33683: 12531,
+	33684: 12532,
+	33685: 12533,
+	33686: 12534,
+	33695: 913,
+	33696: 914,
+	33697: 915,
+	33698: 916,
+	33699: 917,
+	33700: 918,
+	33701: 919,
+	33702: 920,
+	33703: 921,
+	33704: 922,
+	33705: 923,
+	33706: 924,
+	33707: 925,
+	33708: 926,
+	33709: 927,
+	33710: 928,
+	33711: 929,
+	33712: 931,
+	33713: 932,
+	33714: 933,
+	33715: 934,
+	33716: 935,
+	33717: 936,
+	33718: 937,
+	33727: 945,
+	33728: 946,
+	33729: 947,
+	33730: 948,
+	33731: 949,
+	33732: 950,
+	33733: 951,
+	33734: 952,
+	33735: 953,
+	33736: 954,
+	33737: 955,
+	33738: 956,
+	33739: 957,
+	33740: 958,
+	33741: 959,
+	33742: 960,
+	33743: 961,
+	33744: 963,
+	33745: 964,
+	33746: 965,
+	33747: 966,
+	33748: 967,
+	33749: 968,
+	33750: 969,
+	33856: 1040,
+	33857: 1041,
+	33858: 1042,
+	33859: 1043,
+	33860: 1044,
+	33861: 1045,
+	33862: 1025,
+	33863: 1046,
+	33864: 1047,
+	33865: 1048,
+	33866: 1049,
+	33867: 1050,
+	33868: 1051,
+	33869: 1052,
+	33870: 1053,
+	33871: 1054,
+	33872: 1055,
+	33873: 1056,
+	33874: 1057,
+	33875: 1058,
+	33876: 1059,
+	33877: 1060,
+	33878: 1061,
+	33879: 1062,
+	33880: 1063,
+	33881: 1064,
+	33882: 1065,
+	33883: 1066,
+	33884: 1067,
+	33885: 1068,
+	33886: 1069,
+	33887: 1070,
+	33888: 1071,
+	33904: 1072,
+	33905: 1073,
+	33906: 1074,
+	33907: 1075,
+	33908: 1076,
+	33909: 1077,
+	33910: 1105,
+	33911: 1078,
+	33912: 1079,
+	33913: 1080,
+	33914: 1081,
+	33915: 1082,
+	33916: 1083,
+	33917: 1084,
+	33918: 1085,
+	33920: 1086,
+	33921: 1087,
+	33922: 1088,
+	33923: 1089,
+	33924: 1090,
+	33925: 1091,
+	33926: 1092,
+	33927: 1093,
+	33928: 1094,
+	33929: 1095,
+	33930: 1096,
+	33931: 1097,
+	33932: 1098,
+	33933: 1099,
+	33934: 1100,
+	33935: 1101,
+	33936: 1102,
+	33937: 1103,
+	33951: 9472,
+	33952: 9474,
+	33953: 9484,
+	33954: 9488,
+	33955: 9496,
+	33956: 9492,
+	33957: 9500,
+	33958: 9516,
+	33959: 9508,
+	33960: 9524,
+	33961: 9532,
+	33962: 9473,
+	33963: 9475,
+	33964: 9487,
+	33965: 9491,
+	33966: 9499,
+	33967: 9495,
+	33968: 9507,
+	33969: 9523,
+	33970: 9515,
+	33971: 9531,
+	33972: 9547,
+	33973: 9504,
+	33974: 9519,
+	33975: 9512,
+	33976: 9527,
+	33977: 9535,
+	33978: 9501,
+	33979: 9520,
+	33980: 9509,
+	33981: 9528,
+	33982: 9538,
+	34624: 9312,
+	34625: 9313,
+	34626: 9314,
+	34627: 9315,
+	34628: 9316,
+	34629: 9317,
+	34630: 9318,
+	34631: 9319,
+	34632: 9320,
+	34633: 9321,
+	34634: 9322,
+	34635: 9323,
+	34636: 9324,
+	34637: 9325,
+	34638: 9326,
+	34639: 9327,
+	34640: 9328,
+	34641: 9329,
+	34642: 9330,
+	34643: 9331,
+	34644: 8544,
+	34645: 8545,
+	34646: 8546,
+	34647: 8547,
+	34648: 8548,
+	34649: 8549,
+	34650: 8550,
+	34651: 8551,
+	34652: 8552,
+	34653: 8553,
+	34655: 13129,
+	34656: 13076,
+	34657: 13090,
+	34658: 13133,
+	34659: 13080,
+	34660: 13095,
+	34661: 13059,
+	34662: 13110,
+	34663: 13137,
+	34664: 13143,
+	34665: 13069,
+	34666: 13094,
+	34667: 13091,
+	34668: 13099,
+	34669: 13130,
+	34670: 13115,
+	34671: 13212,
+	34672: 13213,
+	34673: 13214,
+	34674: 13198,
+	34675: 13199,
+	34676: 13252,
+	34677: 13217,
+	34686: 13179,
+	34688: 12317,
+	34689: 12319,
+	34690: 8470,
+	34691: 13261,
+	34692: 8481,
+	34693: 12964,
+	34694: 12965,
+	34695: 12966,
+	34696: 12967,
+	34697: 12968,
+	34698: 12849,
+	34699: 12850,
+	34700: 12857,
+	34701: 13182,
+	34702: 13181,
+	34703: 13180,
+	34704: 8786,
+	34705: 8801,
+	34706: 8747,
+	34707: 8750,
+	34708: 8721,
+	34709: 8730,
+	34710: 8869,
+	34711: 8736,
+	34712: 8735,
+	34713: 8895,
+	34714: 8757,
+	34715: 8745,
+	34716: 8746,
+	34975: 20124,
+	34976: 21782,
+	34977: 23043,
+	34978: 38463,
+	34979: 21696,
+	34980: 24859,
+	34981: 25384,
+	34982: 23030,
+	34983: 36898,
+	34984: 33909,
+	34985: 33564,
+	34986: 31312,
+	34987: 24746,
+	34988: 25569,
+	34989: 28197,
+	34990: 26093,
+	34991: 33894,
+	34992: 33446,
+	34993: 39925,
+	34994: 26771,
+	34995: 22311,
+	34996: 26017,
+	34997: 25201,
+	34998: 23451,
+	34999: 22992,
+	35000: 34427,
+	35001: 39156,
+	35002: 32098,
+	35003: 32190,
+	35004: 39822,
+	35005: 25110,
+	35006: 31903,
+	35007: 34999,
+	35008: 23433,
+	35009: 24245,
+	35010: 25353,
+	35011: 26263,
+	35012: 26696,
+	35013: 38343,
+	35014: 38797,
+	35015: 26447,
+	35016: 20197,
+	35017: 20234,
+	35018: 20301,
+	35019: 20381,
+	35020: 20553,
+	35021: 22258,
+	35022: 22839,
+	35023: 22996,
+	35024: 23041,
+	35025: 23561,
+	35026: 24799,
+	35027: 24847,
+	35028: 24944,
+	35029: 26131,
+	35030: 26885,
+	35031: 28858,
+	35032: 30031,
+	35033: 30064,
+	35034: 31227,
+	35035: 32173,
+	35036: 32239,
+	35037: 32963,
+	35038: 33806,
+	35039: 34915,
+	35040: 35586,
+	35041: 36949,
+	35042: 36986,
+	35043: 21307,
+	35044: 20117,
+	35045: 20133,
+	35046: 22495,
+	35047: 32946,
+	35048: 37057,
+	35049: 30959,
+	35050: 19968,
+	35051: 22769,
+	35052: 28322,
+	35053: 36920,
+	35054: 31282,
+	35055: 33576,
+	35056: 33419,
+	35057: 39983,
+	35058: 20801,
+	35059: 21360,
+	35060: 21693,
+	35061: 21729,
+	35062: 22240,
+	35063: 23035,
+	35064: 24341,
+	35065: 39154,
+	35066: 28139,
+	35067: 32996,
+	35068: 34093,
+	35136: 38498,
+	35137: 38512,
+	35138: 38560,
+	35139: 38907,
+	35140: 21515,
+	35141: 21491,
+	35142: 23431,
+	35143: 28879,
+	35144: 32701,
+	35145: 36802,
+	35146: 38632,
+	35147: 21359,
+	35148: 40284,
+	35149: 31418,
+	35150: 19985,
+	35151: 30867,
+	35152: 33276,
+	35153: 28198,
+	35154: 22040,
+	35155: 21764,
+	35156: 27421,
+	35157: 34074,
+	35158: 39995,
+	35159: 23013,
+	35160: 21417,
+	35161: 28006,
+	35162: 29916,
+	35163: 38287,
+	35164: 22082,
+	35165: 20113,
+	35166: 36939,
+	35167: 38642,
+	35168: 33615,
+	35169: 39180,
+	35170: 21473,
+	35171: 21942,
+	35172: 23344,
+	35173: 24433,
+	35174: 26144,
+	35175: 26355,
+	35176: 26628,
+	35177: 27704,
+	35178: 27891,
+	35179: 27945,
+	35180: 29787,
+	35181: 30408,
+	35182: 31310,
+	35183: 38964,
+	35184: 33521,
+	35185: 34907,
+	35186: 35424,
+	35187: 37613,
+	35188: 28082,
+	35189: 30123,
+	35190: 30410,
+	35191: 39365,
+	35192: 24742,
+	35193: 35585,
+	35194: 36234,
+	35195: 38322,
+	35196: 27022,
+	35197: 21421,
+	35198: 20870,
+	35200: 22290,
+	35201: 22576,
+	35202: 22852,
+	35203: 23476,
+	35204: 24310,
+	35205: 24616,
+	35206: 25513,
+	35207: 25588,
+	35208: 27839,
+	35209: 28436,
+	35210: 28814,
+	35211: 28948,
+	35212: 29017,
+	35213: 29141,
+	35214: 29503,
+	35215: 32257,
+	35216: 33398,
+	35217: 33489,
+	35218: 34199,
+	35219: 36960,
+	35220: 37467,
+	35221: 40219,
+	35222: 22633,
+	35223: 26044,
+	35224: 27738,
+	35225: 29989,
+	35226: 20985,
+	35227: 22830,
+	35228: 22885,
+	35229: 24448,
+	35230: 24540,
+	35231: 25276,
+	35232: 26106,
+	35233: 27178,
+	35234: 27431,
+	35235: 27572,
+	35236: 29579,
+	35237: 32705,
+	35238: 35158,
+	35239: 40236,
+	35240: 40206,
+	35241: 40644,
+	35242: 23713,
+	35243: 27798,
+	35244: 33659,
+	35245: 20740,
+	35246: 23627,
+	35247: 25014,
+	35248: 33222,
+	35249: 26742,
+	35250: 29281,
+	35251: 20057,
+	35252: 20474,
+	35253: 21368,
+	35254: 24681,
+	35255: 28201,
+	35256: 31311,
+	35257: 38899,
+	35258: 19979,
+	35259: 21270,
+	35260: 20206,
+	35261: 20309,
+	35262: 20285,
+	35263: 20385,
+	35264: 20339,
+	35265: 21152,
+	35266: 21487,
+	35267: 22025,
+	35268: 22799,
+	35269: 23233,
+	35270: 23478,
+	35271: 23521,
+	35272: 31185,
+	35273: 26247,
+	35274: 26524,
+	35275: 26550,
+	35276: 27468,
+	35277: 27827,
+	35278: 28779,
+	35279: 29634,
+	35280: 31117,
+	35281: 31166,
+	35282: 31292,
+	35283: 31623,
+	35284: 33457,
+	35285: 33499,
+	35286: 33540,
+	35287: 33655,
+	35288: 33775,
+	35289: 33747,
+	35290: 34662,
+	35291: 35506,
+	35292: 22057,
+	35293: 36008,
+	35294: 36838,
+	35295: 36942,
+	35296: 38686,
+	35297: 34442,
+	35298: 20420,
+	35299: 23784,
+	35300: 25105,
+	35301: 29273,
+	35302: 30011,
+	35303: 33253,
+	35304: 33469,
+	35305: 34558,
+	35306: 36032,
+	35307: 38597,
+	35308: 39187,
+	35309: 39381,
+	35310: 20171,
+	35311: 20250,
+	35312: 35299,
+	35313: 22238,
+	35314: 22602,
+	35315: 22730,
+	35316: 24315,
+	35317: 24555,
+	35318: 24618,
+	35319: 24724,
+	35320: 24674,
+	35321: 25040,
+	35322: 25106,
+	35323: 25296,
+	35324: 25913,
+	35392: 39745,
+	35393: 26214,
+	35394: 26800,
+	35395: 28023,
+	35396: 28784,
+	35397: 30028,
+	35398: 30342,
+	35399: 32117,
+	35400: 33445,
+	35401: 34809,
+	35402: 38283,
+	35403: 38542,
+	35404: 35997,
+	35405: 20977,
+	35406: 21182,
+	35407: 22806,
+	35408: 21683,
+	35409: 23475,
+	35410: 23830,
+	35411: 24936,
+	35412: 27010,
+	35413: 28079,
+	35414: 30861,
+	35415: 33995,
+	35416: 34903,
+	35417: 35442,
+	35418: 37799,
+	35419: 39608,
+	35420: 28012,
+	35421: 39336,
+	35422: 34521,
+	35423: 22435,
+	35424: 26623,
+	35425: 34510,
+	35426: 37390,
+	35427: 21123,
+	35428: 22151,
+	35429: 21508,
+	35430: 24275,
+	35431: 25313,
+	35432: 25785,
+	35433: 26684,
+	35434: 26680,
+	35435: 27579,
+	35436: 29554,
+	35437: 30906,
+	35438: 31339,
+	35439: 35226,
+	35440: 35282,
+	35441: 36203,
+	35442: 36611,
+	35443: 37101,
+	35444: 38307,
+	35445: 38548,
+	35446: 38761,
+	35447: 23398,
+	35448: 23731,
+	35449: 27005,
+	35450: 38989,
+	35451: 38990,
+	35452: 25499,
+	35453: 31520,
+	35454: 27179,
+	35456: 27263,
+	35457: 26806,
+	35458: 39949,
+	35459: 28511,
+	35460: 21106,
+	35461: 21917,
+	35462: 24688,
+	35463: 25324,
+	35464: 27963,
+	35465: 28167,
+	35466: 28369,
+	35467: 33883,
+	35468: 35088,
+	35469: 36676,
+	35470: 19988,
+	35471: 39993,
+	35472: 21494,
+	35473: 26907,
+	35474: 27194,
+	35475: 38788,
+	35476: 26666,
+	35477: 20828,
+	35478: 31427,
+	35479: 33970,
+	35480: 37340,
+	35481: 37772,
+	35482: 22107,
+	35483: 40232,
+	35484: 26658,
+	35485: 33541,
+	35486: 33841,
+	35487: 31909,
+	35488: 21000,
+	35489: 33477,
+	35490: 29926,
+	35491: 20094,
+	35492: 20355,
+	35493: 20896,
+	35494: 23506,
+	35495: 21002,
+	35496: 21208,
+	35497: 21223,
+	35498: 24059,
+	35499: 21914,
+	35500: 22570,
+	35501: 23014,
+	35502: 23436,
+	35503: 23448,
+	35504: 23515,
+	35505: 24178,
+	35506: 24185,
+	35507: 24739,
+	35508: 24863,
+	35509: 24931,
+	35510: 25022,
+	35511: 25563,
+	35512: 25954,
+	35513: 26577,
+	35514: 26707,
+	35515: 26874,
+	35516: 27454,
+	35517: 27475,
+	35518: 27735,
+	35519: 28450,
+	35520: 28567,
+	35521: 28485,
+	35522: 29872,
+	35523: 29976,
+	35524: 30435,
+	35525: 30475,
+	35526: 31487,
+	35527: 31649,
+	35528: 31777,
+	35529: 32233,
+	35530: 32566,
+	35531: 32752,
+	35532: 32925,
+	35533: 33382,
+	35534: 33694,
+	35535: 35251,
+	35536: 35532,
+	35537: 36011,
+	35538: 36996,
+	35539: 37969,
+	35540: 38291,
+	35541: 38289,
+	35542: 38306,
+	35543: 38501,
+	35544: 38867,
+	35545: 39208,
+	35546: 33304,
+	35547: 20024,
+	35548: 21547,
+	35549: 23736,
+	35550: 24012,
+	35551: 29609,
+	35552: 30284,
+	35553: 30524,
+	35554: 23721,
+	35555: 32747,
+	35556: 36107,
+	35557: 38593,
+	35558: 38929,
+	35559: 38996,
+	35560: 39000,
+	35561: 20225,
+	35562: 20238,
+	35563: 21361,
+	35564: 21916,
+	35565: 22120,
+	35566: 22522,
+	35567: 22855,
+	35568: 23305,
+	35569: 23492,
+	35570: 23696,
+	35571: 24076,
+	35572: 24190,
+	35573: 24524,
+	35574: 25582,
+	35575: 26426,
+	35576: 26071,
+	35577: 26082,
+	35578: 26399,
+	35579: 26827,
+	35580: 26820,
+	35648: 27231,
+	35649: 24112,
+	35650: 27589,
+	35651: 27671,
+	35652: 27773,
+	35653: 30079,
+	35654: 31048,
+	35655: 23395,
+	35656: 31232,
+	35657: 32000,
+	35658: 24509,
+	35659: 35215,
+	35660: 35352,
+	35661: 36020,
+	35662: 36215,
+	35663: 36556,
+	35664: 36637,
+	35665: 39138,
+	35666: 39438,
+	35667: 39740,
+	35668: 20096,
+	35669: 20605,
+	35670: 20736,
+	35671: 22931,
+	35672: 23452,
+	35673: 25135,
+	35674: 25216,
+	35675: 25836,
+	35676: 27450,
+	35677: 29344,
+	35678: 30097,
+	35679: 31047,
+	35680: 32681,
+	35681: 34811,
+	35682: 35516,
+	35683: 35696,
+	35684: 25516,
+	35685: 33738,
+	35686: 38816,
+	35687: 21513,
+	35688: 21507,
+	35689: 21931,
+	35690: 26708,
+	35691: 27224,
+	35692: 35440,
+	35693: 30759,
+	35694: 26485,
+	35695: 40653,
+	35696: 21364,
+	35697: 23458,
+	35698: 33050,
+	35699: 34384,
+	35700: 36870,
+	35701: 19992,
+	35702: 20037,
+	35703: 20167,
+	35704: 20241,
+	35705: 21450,
+	35706: 21560,
+	35707: 23470,
+	35708: 24339,
+	35709: 24613,
+	35710: 25937,
+	35712: 26429,
+	35713: 27714,
+	35714: 27762,
+	35715: 27875,
+	35716: 28792,
+	35717: 29699,
+	35718: 31350,
+	35719: 31406,
+	35720: 31496,
+	35721: 32026,
+	35722: 31998,
+	35723: 32102,
+	35724: 26087,
+	35725: 29275,
+	35726: 21435,
+	35727: 23621,
+	35728: 24040,
+	35729: 25298,
+	35730: 25312,
+	35731: 25369,
+	35732: 28192,
+	35733: 34394,
+	35734: 35377,
+	35735: 36317,
+	35736: 37624,
+	35737: 28417,
+	35738: 31142,
+	35739: 39770,
+	35740: 20136,
+	35741: 20139,
+	35742: 20140,
+	35743: 20379,
+	35744: 20384,
+	35745: 20689,
+	35746: 20807,
+	35747: 31478,
+	35748: 20849,
+	35749: 20982,
+	35750: 21332,
+	35751: 21281,
+	35752: 21375,
+	35753: 21483,
+	35754: 21932,
+	35755: 22659,
+	35756: 23777,
+	35757: 24375,
+	35758: 24394,
+	35759: 24623,
+	35760: 24656,
+	35761: 24685,
+	35762: 25375,
+	35763: 25945,
+	35764: 27211,
+	35765: 27841,
+	35766: 29378,
+	35767: 29421,
+	35768: 30703,
+	35769: 33016,
+	35770: 33029,
+	35771: 33288,
+	35772: 34126,
+	35773: 37111,
+	35774: 37857,
+	35775: 38911,
+	35776: 39255,
+	35777: 39514,
+	35778: 20208,
+	35779: 20957,
+	35780: 23597,
+	35781: 26241,
+	35782: 26989,
+	35783: 23616,
+	35784: 26354,
+	35785: 26997,
+	35786: 29577,
+	35787: 26704,
+	35788: 31873,
+	35789: 20677,
+	35790: 21220,
+	35791: 22343,
+	35792: 24062,
+	35793: 37670,
+	35794: 26020,
+	35795: 27427,
+	35796: 27453,
+	35797: 29748,
+	35798: 31105,
+	35799: 31165,
+	35800: 31563,
+	35801: 32202,
+	35802: 33465,
+	35803: 33740,
+	35804: 34943,
+	35805: 35167,
+	35806: 35641,
+	35807: 36817,
+	35808: 37329,
+	35809: 21535,
+	35810: 37504,
+	35811: 20061,
+	35812: 20534,
+	35813: 21477,
+	35814: 21306,
+	35815: 29399,
+	35816: 29590,
+	35817: 30697,
+	35818: 33510,
+	35819: 36527,
+	35820: 39366,
+	35821: 39368,
+	35822: 39378,
+	35823: 20855,
+	35824: 24858,
+	35825: 34398,
+	35826: 21936,
+	35827: 31354,
+	35828: 20598,
+	35829: 23507,
+	35830: 36935,
+	35831: 38533,
+	35832: 20018,
+	35833: 27355,
+	35834: 37351,
+	35835: 23633,
+	35836: 23624,
+	35904: 25496,
+	35905: 31391,
+	35906: 27795,
+	35907: 38772,
+	35908: 36705,
+	35909: 31402,
+	35910: 29066,
+	35911: 38536,
+	35912: 31874,
+	35913: 26647,
+	35914: 32368,
+	35915: 26705,
+	35916: 37740,
+	35917: 21234,
+	35918: 21531,
+	35919: 34219,
+	35920: 35347,
+	35921: 32676,
+	35922: 36557,
+	35923: 37089,
+	35924: 21350,
+	35925: 34952,
+	35926: 31041,
+	35927: 20418,
+	35928: 20670,
+	35929: 21009,
+	35930: 20804,
+	35931: 21843,
+	35932: 22317,
+	35933: 29674,
+	35934: 22411,
+	35935: 22865,
+	35936: 24418,
+	35937: 24452,
+	35938: 24693,
+	35939: 24950,
+	35940: 24935,
+	35941: 25001,
+	35942: 25522,
+	35943: 25658,
+	35944: 25964,
+	35945: 26223,
+	35946: 26690,
+	35947: 28179,
+	35948: 30054,
+	35949: 31293,
+	35950: 31995,
+	35951: 32076,
+	35952: 32153,
+	35953: 32331,
+	35954: 32619,
+	35955: 33550,
+	35956: 33610,
+	35957: 34509,
+	35958: 35336,
+	35959: 35427,
+	35960: 35686,
+	35961: 36605,
+	35962: 38938,
+	35963: 40335,
+	35964: 33464,
+	35965: 36814,
+	35966: 39912,
+	35968: 21127,
+	35969: 25119,
+	35970: 25731,
+	35971: 28608,
+	35972: 38553,
+	35973: 26689,
+	35974: 20625,
+	35975: 27424,
+	35976: 27770,
+	35977: 28500,
+	35978: 31348,
+	35979: 32080,
+	35980: 34880,
+	35981: 35363,
+	35982: 26376,
+	35983: 20214,
+	35984: 20537,
+	35985: 20518,
+	35986: 20581,
+	35987: 20860,
+	35988: 21048,
+	35989: 21091,
+	35990: 21927,
+	35991: 22287,
+	35992: 22533,
+	35993: 23244,
+	35994: 24314,
+	35995: 25010,
+	35996: 25080,
+	35997: 25331,
+	35998: 25458,
+	35999: 26908,
+	36000: 27177,
+	36001: 29309,
+	36002: 29356,
+	36003: 29486,
+	36004: 30740,
+	36005: 30831,
+	36006: 32121,
+	36007: 30476,
+	36008: 32937,
+	36009: 35211,
+	36010: 35609,
+	36011: 36066,
+	36012: 36562,
+	36013: 36963,
+	36014: 37749,
+	36015: 38522,
+	36016: 38997,
+	36017: 39443,
+	36018: 40568,
+	36019: 20803,
+	36020: 21407,
+	36021: 21427,
+	36022: 24187,
+	36023: 24358,
+	36024: 28187,
+	36025: 28304,
+	36026: 29572,
+	36027: 29694,
+	36028: 32067,
+	36029: 33335,
+	36030: 35328,
+	36031: 35578,
+	36032: 38480,
+	36033: 20046,
+	36034: 20491,
+	36035: 21476,
+	36036: 21628,
+	36037: 22266,
+	36038: 22993,
+	36039: 23396,
+	36040: 24049,
+	36041: 24235,
+	36042: 24359,
+	36043: 25144,
+	36044: 25925,
+	36045: 26543,
+	36046: 28246,
+	36047: 29392,
+	36048: 31946,
+	36049: 34996,
+	36050: 32929,
+	36051: 32993,
+	36052: 33776,
+	36053: 34382,
+	36054: 35463,
+	36055: 36328,
+	36056: 37431,
+	36057: 38599,
+	36058: 39015,
+	36059: 40723,
+	36060: 20116,
+	36061: 20114,
+	36062: 20237,
+	36063: 21320,
+	36064: 21577,
+	36065: 21566,
+	36066: 23087,
+	36067: 24460,
+	36068: 24481,
+	36069: 24735,
+	36070: 26791,
+	36071: 27278,
+	36072: 29786,
+	36073: 30849,
+	36074: 35486,
+	36075: 35492,
+	36076: 35703,
+	36077: 37264,
+	36078: 20062,
+	36079: 39881,
+	36080: 20132,
+	36081: 20348,
+	36082: 20399,
+	36083: 20505,
+	36084: 20502,
+	36085: 20809,
+	36086: 20844,
+	36087: 21151,
+	36088: 21177,
+	36089: 21246,
+	36090: 21402,
+	36091: 21475,
+	36092: 21521,
+	36160: 21518,
+	36161: 21897,
+	36162: 22353,
+	36163: 22434,
+	36164: 22909,
+	36165: 23380,
+	36166: 23389,
+	36167: 23439,
+	36168: 24037,
+	36169: 24039,
+	36170: 24055,
+	36171: 24184,
+	36172: 24195,
+	36173: 24218,
+	36174: 24247,
+	36175: 24344,
+	36176: 24658,
+	36177: 24908,
+	36178: 25239,
+	36179: 25304,
+	36180: 25511,
+	36181: 25915,
+	36182: 26114,
+	36183: 26179,
+	36184: 26356,
+	36185: 26477,
+	36186: 26657,
+	36187: 26775,
+	36188: 27083,
+	36189: 27743,
+	36190: 27946,
+	36191: 28009,
+	36192: 28207,
+	36193: 28317,
+	36194: 30002,
+	36195: 30343,
+	36196: 30828,
+	36197: 31295,
+	36198: 31968,
+	36199: 32005,
+	36200: 32024,
+	36201: 32094,
+	36202: 32177,
+	36203: 32789,
+	36204: 32771,
+	36205: 32943,
+	36206: 32945,
+	36207: 33108,
+	36208: 33167,
+	36209: 33322,
+	36210: 33618,
+	36211: 34892,
+	36212: 34913,
+	36213: 35611,
+	36214: 36002,
+	36215: 36092,
+	36216: 37066,
+	36217: 37237,
+	36218: 37489,
+	36219: 30783,
+	36220: 37628,
+	36221: 38308,
+	36222: 38477,
+	36224: 38917,
+	36225: 39321,
+	36226: 39640,
+	36227: 40251,
+	36228: 21083,
+	36229: 21163,
+	36230: 21495,
+	36231: 21512,
+	36232: 22741,
+	36233: 25335,
+	36234: 28640,
+	36235: 35946,
+	36236: 36703,
+	36237: 40633,
+	36238: 20811,
+	36239: 21051,
+	36240: 21578,
+	36241: 22269,
+	36242: 31296,
+	36243: 37239,
+	36244: 40288,
+	36245: 40658,
+	36246: 29508,
+	36247: 28425,
+	36248: 33136,
+	36249: 29969,
+	36250: 24573,
+	36251: 24794,
+	36252: 39592,
+	36253: 29403,
+	36254: 36796,
+	36255: 27492,
+	36256: 38915,
+	36257: 20170,
+	36258: 22256,
+	36259: 22372,
+	36260: 22718,
+	36261: 23130,
+	36262: 24680,
+	36263: 25031,
+	36264: 26127,
+	36265: 26118,
+	36266: 26681,
+	36267: 26801,
+	36268: 28151,
+	36269: 30165,
+	36270: 32058,
+	36271: 33390,
+	36272: 39746,
+	36273: 20123,
+	36274: 20304,
+	36275: 21449,
+	36276: 21766,
+	36277: 23919,
+	36278: 24038,
+	36279: 24046,
+	36280: 26619,
+	36281: 27801,
+	36282: 29811,
+	36283: 30722,
+	36284: 35408,
+	36285: 37782,
+	36286: 35039,
+	36287: 22352,
+	36288: 24231,
+	36289: 25387,
+	36290: 20661,
+	36291: 20652,
+	36292: 20877,
+	36293: 26368,
+	36294: 21705,
+	36295: 22622,
+	36296: 22971,
+	36297: 23472,
+	36298: 24425,
+	36299: 25165,
+	36300: 25505,
+	36301: 26685,
+	36302: 27507,
+	36303: 28168,
+	36304: 28797,
+	36305: 37319,
+	36306: 29312,
+	36307: 30741,
+	36308: 30758,
+	36309: 31085,
+	36310: 25998,
+	36311: 32048,
+	36312: 33756,
+	36313: 35009,
+	36314: 36617,
+	36315: 38555,
+	36316: 21092,
+	36317: 22312,
+	36318: 26448,
+	36319: 32618,
+	36320: 36001,
+	36321: 20916,
+	36322: 22338,
+	36323: 38442,
+	36324: 22586,
+	36325: 27018,
+	36326: 32948,
+	36327: 21682,
+	36328: 23822,
+	36329: 22524,
+	36330: 30869,
+	36331: 40442,
+	36332: 20316,
+	36333: 21066,
+	36334: 21643,
+	36335: 25662,
+	36336: 26152,
+	36337: 26388,
+	36338: 26613,
+	36339: 31364,
+	36340: 31574,
+	36341: 32034,
+	36342: 37679,
+	36343: 26716,
+	36344: 39853,
+	36345: 31545,
+	36346: 21273,
+	36347: 20874,
+	36348: 21047,
+	36416: 23519,
+	36417: 25334,
+	36418: 25774,
+	36419: 25830,
+	36420: 26413,
+	36421: 27578,
+	36422: 34217,
+	36423: 38609,
+	36424: 30352,
+	36425: 39894,
+	36426: 25420,
+	36427: 37638,
+	36428: 39851,
+	36429: 30399,
+	36430: 26194,
+	36431: 19977,
+	36432: 20632,
+	36433: 21442,
+	36434: 23665,
+	36435: 24808,
+	36436: 25746,
+	36437: 25955,
+	36438: 26719,
+	36439: 29158,
+	36440: 29642,
+	36441: 29987,
+	36442: 31639,
+	36443: 32386,
+	36444: 34453,
+	36445: 35715,
+	36446: 36059,
+	36447: 37240,
+	36448: 39184,
+	36449: 26028,
+	36450: 26283,
+	36451: 27531,
+	36452: 20181,
+	36453: 20180,
+	36454: 20282,
+	36455: 20351,
+	36456: 21050,
+	36457: 21496,
+	36458: 21490,
+	36459: 21987,
+	36460: 22235,
+	36461: 22763,
+	36462: 22987,
+	36463: 22985,
+	36464: 23039,
+	36465: 23376,
+	36466: 23629,
+	36467: 24066,
+	36468: 24107,
+	36469: 24535,
+	36470: 24605,
+	36471: 25351,
+	36472: 25903,
+	36473: 23388,
+	36474: 26031,
+	36475: 26045,
+	36476: 26088,
+	36477: 26525,
+	36478: 27490,
+	36480: 27515,
+	36481: 27663,
+	36482: 29509,
+	36483: 31049,
+	36484: 31169,
+	36485: 31992,
+	36486: 32025,
+	36487: 32043,
+	36488: 32930,
+	36489: 33026,
+	36490: 33267,
+	36491: 35222,
+	36492: 35422,
+	36493: 35433,
+	36494: 35430,
+	36495: 35468,
+	36496: 35566,
+	36497: 36039,
+	36498: 36060,
+	36499: 38604,
+	36500: 39164,
+	36501: 27503,
+	36502: 20107,
+	36503: 20284,
+	36504: 20365,
+	36505: 20816,
+	36506: 23383,
+	36507: 23546,
+	36508: 24904,
+	36509: 25345,
+	36510: 26178,
+	36511: 27425,
+	36512: 28363,
+	36513: 27835,
+	36514: 29246,
+	36515: 29885,
+	36516: 30164,
+	36517: 30913,
+	36518: 31034,
+	36519: 32780,
+	36520: 32819,
+	36521: 33258,
+	36522: 33940,
+	36523: 36766,
+	36524: 27728,
+	36525: 40575,
+	36526: 24335,
+	36527: 35672,
+	36528: 40235,
+	36529: 31482,
+	36530: 36600,
+	36531: 23437,
+	36532: 38635,
+	36533: 19971,
+	36534: 21489,
+	36535: 22519,
+	36536: 22833,
+	36537: 23241,
+	36538: 23460,
+	36539: 24713,
+	36540: 28287,
+	36541: 28422,
+	36542: 30142,
+	36543: 36074,
+	36544: 23455,
+	36545: 34048,
+	36546: 31712,
+	36547: 20594,
+	36548: 26612,
+	36549: 33437,
+	36550: 23649,
+	36551: 34122,
+	36552: 32286,
+	36553: 33294,
+	36554: 20889,
+	36555: 23556,
+	36556: 25448,
+	36557: 36198,
+	36558: 26012,
+	36559: 29038,
+	36560: 31038,
+	36561: 32023,
+	36562: 32773,
+	36563: 35613,
+	36564: 36554,
+	36565: 36974,
+	36566: 34503,
+	36567: 37034,
+	36568: 20511,
+	36569: 21242,
+	36570: 23610,
+	36571: 26451,
+	36572: 28796,
+	36573: 29237,
+	36574: 37196,
+	36575: 37320,
+	36576: 37675,
+	36577: 33509,
+	36578: 23490,
+	36579: 24369,
+	36580: 24825,
+	36581: 20027,
+	36582: 21462,
+	36583: 23432,
+	36584: 25163,
+	36585: 26417,
+	36586: 27530,
+	36587: 29417,
+	36588: 29664,
+	36589: 31278,
+	36590: 33131,
+	36591: 36259,
+	36592: 37202,
+	36593: 39318,
+	36594: 20754,
+	36595: 21463,
+	36596: 21610,
+	36597: 23551,
+	36598: 25480,
+	36599: 27193,
+	36600: 32172,
+	36601: 38656,
+	36602: 22234,
+	36603: 21454,
+	36604: 21608,
+	36672: 23447,
+	36673: 23601,
+	36674: 24030,
+	36675: 20462,
+	36676: 24833,
+	36677: 25342,
+	36678: 27954,
+	36679: 31168,
+	36680: 31179,
+	36681: 32066,
+	36682: 32333,
+	36683: 32722,
+	36684: 33261,
+	36685: 33311,
+	36686: 33936,
+	36687: 34886,
+	36688: 35186,
+	36689: 35728,
+	36690: 36468,
+	36691: 36655,
+	36692: 36913,
+	36693: 37195,
+	36694: 37228,
+	36695: 38598,
+	36696: 37276,
+	36697: 20160,
+	36698: 20303,
+	36699: 20805,
+	36700: 21313,
+	36701: 24467,
+	36702: 25102,
+	36703: 26580,
+	36704: 27713,
+	36705: 28171,
+	36706: 29539,
+	36707: 32294,
+	36708: 37325,
+	36709: 37507,
+	36710: 21460,
+	36711: 22809,
+	36712: 23487,
+	36713: 28113,
+	36714: 31069,
+	36715: 32302,
+	36716: 31899,
+	36717: 22654,
+	36718: 29087,
+	36719: 20986,
+	36720: 34899,
+	36721: 36848,
+	36722: 20426,
+	36723: 23803,
+	36724: 26149,
+	36725: 30636,
+	36726: 31459,
+	36727: 33308,
+	36728: 39423,
+	36729: 20934,
+	36730: 24490,
+	36731: 26092,
+	36732: 26991,
+	36733: 27529,
+	36734: 28147,
+	36736: 28310,
+	36737: 28516,
+	36738: 30462,
+	36739: 32020,
+	36740: 24033,
+	36741: 36981,
+	36742: 37255,
+	36743: 38918,
+	36744: 20966,
+	36745: 21021,
+	36746: 25152,
+	36747: 26257,
+	36748: 26329,
+	36749: 28186,
+	36750: 24246,
+	36751: 32210,
+	36752: 32626,
+	36753: 26360,
+	36754: 34223,
+	36755: 34295,
+	36756: 35576,
+	36757: 21161,
+	36758: 21465,
+	36759: 22899,
+	36760: 24207,
+	36761: 24464,
+	36762: 24661,
+	36763: 37604,
+	36764: 38500,
+	36765: 20663,
+	36766: 20767,
+	36767: 21213,
+	36768: 21280,
+	36769: 21319,
+	36770: 21484,
+	36771: 21736,
+	36772: 21830,
+	36773: 21809,
+	36774: 22039,
+	36775: 22888,
+	36776: 22974,
+	36777: 23100,
+	36778: 23477,
+	36779: 23558,
+	36780: 23567,
+	36781: 23569,
+	36782: 23578,
+	36783: 24196,
+	36784: 24202,
+	36785: 24288,
+	36786: 24432,
+	36787: 25215,
+	36788: 25220,
+	36789: 25307,
+	36790: 25484,
+	36791: 25463,
+	36792: 26119,
+	36793: 26124,
+	36794: 26157,
+	36795: 26230,
+	36796: 26494,
+	36797: 26786,
+	36798: 27167,
+	36799: 27189,
+	36800: 27836,
+	36801: 28040,
+	36802: 28169,
+	36803: 28248,
+	36804: 28988,
+	36805: 28966,
+	36806: 29031,
+	36807: 30151,
+	36808: 30465,
+	36809: 30813,
+	36810: 30977,
+	36811: 31077,
+	36812: 31216,
+	36813: 31456,
+	36814: 31505,
+	36815: 31911,
+	36816: 32057,
+	36817: 32918,
+	36818: 33750,
+	36819: 33931,
+	36820: 34121,
+	36821: 34909,
+	36822: 35059,
+	36823: 35359,
+	36824: 35388,
+	36825: 35412,
+	36826: 35443,
+	36827: 35937,
+	36828: 36062,
+	36829: 37284,
+	36830: 37478,
+	36831: 37758,
+	36832: 37912,
+	36833: 38556,
+	36834: 38808,
+	36835: 19978,
+	36836: 19976,
+	36837: 19998,
+	36838: 20055,
+	36839: 20887,
+	36840: 21104,
+	36841: 22478,
+	36842: 22580,
+	36843: 22732,
+	36844: 23330,
+	36845: 24120,
+	36846: 24773,
+	36847: 25854,
+	36848: 26465,
+	36849: 26454,
+	36850: 27972,
+	36851: 29366,
+	36852: 30067,
+	36853: 31331,
+	36854: 33976,
+	36855: 35698,
+	36856: 37304,
+	36857: 37664,
+	36858: 22065,
+	36859: 22516,
+	36860: 39166,
+	36928: 25325,
+	36929: 26893,
+	36930: 27542,
+	36931: 29165,
+	36932: 32340,
+	36933: 32887,
+	36934: 33394,
+	36935: 35302,
+	36936: 39135,
+	36937: 34645,
+	36938: 36785,
+	36939: 23611,
+	36940: 20280,
+	36941: 20449,
+	36942: 20405,
+	36943: 21767,
+	36944: 23072,
+	36945: 23517,
+	36946: 23529,
+	36947: 24515,
+	36948: 24910,
+	36949: 25391,
+	36950: 26032,
+	36951: 26187,
+	36952: 26862,
+	36953: 27035,
+	36954: 28024,
+	36955: 28145,
+	36956: 30003,
+	36957: 30137,
+	36958: 30495,
+	36959: 31070,
+	36960: 31206,
+	36961: 32051,
+	36962: 33251,
+	36963: 33455,
+	36964: 34218,
+	36965: 35242,
+	36966: 35386,
+	36967: 36523,
+	36968: 36763,
+	36969: 36914,
+	36970: 37341,
+	36971: 38663,
+	36972: 20154,
+	36973: 20161,
+	36974: 20995,
+	36975: 22645,
+	36976: 22764,
+	36977: 23563,
+	36978: 29978,
+	36979: 23613,
+	36980: 33102,
+	36981: 35338,
+	36982: 36805,
+	36983: 38499,
+	36984: 38765,
+	36985: 31525,
+	36986: 35535,
+	36987: 38920,
+	36988: 37218,
+	36989: 22259,
+	36990: 21416,
+	36992: 36887,
+	36993: 21561,
+	36994: 22402,
+	36995: 24101,
+	36996: 25512,
+	36997: 27700,
+	36998: 28810,
+	36999: 30561,
+	37000: 31883,
+	37001: 32736,
+	37002: 34928,
+	37003: 36930,
+	37004: 37204,
+	37005: 37648,
+	37006: 37656,
+	37007: 38543,
+	37008: 29790,
+	37009: 39620,
+	37010: 23815,
+	37011: 23913,
+	37012: 25968,
+	37013: 26530,
+	37014: 36264,
+	37015: 38619,
+	37016: 25454,
+	37017: 26441,
+	37018: 26905,
+	37019: 33733,
+	37020: 38935,
+	37021: 38592,
+	37022: 35070,
+	37023: 28548,
+	37024: 25722,
+	37025: 23544,
+	37026: 19990,
+	37027: 28716,
+	37028: 30045,
+	37029: 26159,
+	37030: 20932,
+	37031: 21046,
+	37032: 21218,
+	37033: 22995,
+	37034: 24449,
+	37035: 24615,
+	37036: 25104,
+	37037: 25919,
+	37038: 25972,
+	37039: 26143,
+	37040: 26228,
+	37041: 26866,
+	37042: 26646,
+	37043: 27491,
+	37044: 28165,
+	37045: 29298,
+	37046: 29983,
+	37047: 30427,
+	37048: 31934,
+	37049: 32854,
+	37050: 22768,
+	37051: 35069,
+	37052: 35199,
+	37053: 35488,
+	37054: 35475,
+	37055: 35531,
+	37056: 36893,
+	37057: 37266,
+	37058: 38738,
+	37059: 38745,
+	37060: 25993,
+	37061: 31246,
+	37062: 33030,
+	37063: 38587,
+	37064: 24109,
+	37065: 24796,
+	37066: 25114,
+	37067: 26021,
+	37068: 26132,
+	37069: 26512,
+	37070: 30707,
+	37071: 31309,
+	37072: 31821,
+	37073: 32318,
+	37074: 33034,
+	37075: 36012,
+	37076: 36196,
+	37077: 36321,
+	37078: 36447,
+	37079: 30889,
+	37080: 20999,
+	37081: 25305,
+	37082: 25509,
+	37083: 25666,
+	37084: 25240,
+	37085: 35373,
+	37086: 31363,
+	37087: 31680,
+	37088: 35500,
+	37089: 38634,
+	37090: 32118,
+	37091: 33292,
+	37092: 34633,
+	37093: 20185,
+	37094: 20808,
+	37095: 21315,
+	37096: 21344,
+	37097: 23459,
+	37098: 23554,
+	37099: 23574,
+	37100: 24029,
+	37101: 25126,
+	37102: 25159,
+	37103: 25776,
+	37104: 26643,
+	37105: 26676,
+	37106: 27849,
+	37107: 27973,
+	37108: 27927,
+	37109: 26579,
+	37110: 28508,
+	37111: 29006,
+	37112: 29053,
+	37113: 26059,
+	37114: 31359,
+	37115: 31661,
+	37116: 32218,
+	37184: 32330,
+	37185: 32680,
+	37186: 33146,
+	37187: 33307,
+	37188: 33337,
+	37189: 34214,
+	37190: 35438,
+	37191: 36046,
+	37192: 36341,
+	37193: 36984,
+	37194: 36983,
+	37195: 37549,
+	37196: 37521,
+	37197: 38275,
+	37198: 39854,
+	37199: 21069,
+	37200: 21892,
+	37201: 28472,
+	37202: 28982,
+	37203: 20840,
+	37204: 31109,
+	37205: 32341,
+	37206: 33203,
+	37207: 31950,
+	37208: 22092,
+	37209: 22609,
+	37210: 23720,
+	37211: 25514,
+	37212: 26366,
+	37213: 26365,
+	37214: 26970,
+	37215: 29401,
+	37216: 30095,
+	37217: 30094,
+	37218: 30990,
+	37219: 31062,
+	37220: 31199,
+	37221: 31895,
+	37222: 32032,
+	37223: 32068,
+	37224: 34311,
+	37225: 35380,
+	37226: 38459,
+	37227: 36961,
+	37228: 40736,
+	37229: 20711,
+	37230: 21109,
+	37231: 21452,
+	37232: 21474,
+	37233: 20489,
+	37234: 21930,
+	37235: 22766,
+	37236: 22863,
+	37237: 29245,
+	37238: 23435,
+	37239: 23652,
+	37240: 21277,
+	37241: 24803,
+	37242: 24819,
+	37243: 25436,
+	37244: 25475,
+	37245: 25407,
+	37246: 25531,
+	37248: 25805,
+	37249: 26089,
+	37250: 26361,
+	37251: 24035,
+	37252: 27085,
+	37253: 27133,
+	37254: 28437,
+	37255: 29157,
+	37256: 20105,
+	37257: 30185,
+	37258: 30456,
+	37259: 31379,
+	37260: 31967,
+	37261: 32207,
+	37262: 32156,
+	37263: 32865,
+	37264: 33609,
+	37265: 33624,
+	37266: 33900,
+	37267: 33980,
+	37268: 34299,
+	37269: 35013,
+	37270: 36208,
+	37271: 36865,
+	37272: 36973,
+	37273: 37783,
+	37274: 38684,
+	37275: 39442,
+	37276: 20687,
+	37277: 22679,
+	37278: 24974,
+	37279: 33235,
+	37280: 34101,
+	37281: 36104,
+	37282: 36896,
+	37283: 20419,
+	37284: 20596,
+	37285: 21063,
+	37286: 21363,
+	37287: 24687,
+	37288: 25417,
+	37289: 26463,
+	37290: 28204,
+	37291: 36275,
+	37292: 36895,
+	37293: 20439,
+	37294: 23646,
+	37295: 36042,
+	37296: 26063,
+	37297: 32154,
+	37298: 21330,
+	37299: 34966,
+	37300: 20854,
+	37301: 25539,
+	37302: 23384,
+	37303: 23403,
+	37304: 23562,
+	37305: 25613,
+	37306: 26449,
+	37307: 36956,
+	37308: 20182,
+	37309: 22810,
+	37310: 22826,
+	37311: 27760,
+	37312: 35409,
+	37313: 21822,
+	37314: 22549,
+	37315: 22949,
+	37316: 24816,
+	37317: 25171,
+	37318: 26561,
+	37319: 33333,
+	37320: 26965,
+	37321: 38464,
+	37322: 39364,
+	37323: 39464,
+	37324: 20307,
+	37325: 22534,
+	37326: 23550,
+	37327: 32784,
+	37328: 23729,
+	37329: 24111,
+	37330: 24453,
+	37331: 24608,
+	37332: 24907,
+	37333: 25140,
+	37334: 26367,
+	37335: 27888,
+	37336: 28382,
+	37337: 32974,
+	37338: 33151,
+	37339: 33492,
+	37340: 34955,
+	37341: 36024,
+	37342: 36864,
+	37343: 36910,
+	37344: 38538,
+	37345: 40667,
+	37346: 39899,
+	37347: 20195,
+	37348: 21488,
+	37349: 22823,
+	37350: 31532,
+	37351: 37261,
+	37352: 38988,
+	37353: 40441,
+	37354: 28381,
+	37355: 28711,
+	37356: 21331,
+	37357: 21828,
+	37358: 23429,
+	37359: 25176,
+	37360: 25246,
+	37361: 25299,
+	37362: 27810,
+	37363: 28655,
+	37364: 29730,
+	37365: 35351,
+	37366: 37944,
+	37367: 28609,
+	37368: 35582,
+	37369: 33592,
+	37370: 20967,
+	37371: 34552,
+	37372: 21482,
+	37440: 21481,
+	37441: 20294,
+	37442: 36948,
+	37443: 36784,
+	37444: 22890,
+	37445: 33073,
+	37446: 24061,
+	37447: 31466,
+	37448: 36799,
+	37449: 26842,
+	37450: 35895,
+	37451: 29432,
+	37452: 40008,
+	37453: 27197,
+	37454: 35504,
+	37455: 20025,
+	37456: 21336,
+	37457: 22022,
+	37458: 22374,
+	37459: 25285,
+	37460: 25506,
+	37461: 26086,
+	37462: 27470,
+	37463: 28129,
+	37464: 28251,
+	37465: 28845,
+	37466: 30701,
+	37467: 31471,
+	37468: 31658,
+	37469: 32187,
+	37470: 32829,
+	37471: 32966,
+	37472: 34507,
+	37473: 35477,
+	37474: 37723,
+	37475: 22243,
+	37476: 22727,
+	37477: 24382,
+	37478: 26029,
+	37479: 26262,
+	37480: 27264,
+	37481: 27573,
+	37482: 30007,
+	37483: 35527,
+	37484: 20516,
+	37485: 30693,
+	37486: 22320,
+	37487: 24347,
+	37488: 24677,
+	37489: 26234,
+	37490: 27744,
+	37491: 30196,
+	37492: 31258,
+	37493: 32622,
+	37494: 33268,
+	37495: 34584,
+	37496: 36933,
+	37497: 39347,
+	37498: 31689,
+	37499: 30044,
+	37500: 31481,
+	37501: 31569,
+	37502: 33988,
+	37504: 36880,
+	37505: 31209,
+	37506: 31378,
+	37507: 33590,
+	37508: 23265,
+	37509: 30528,
+	37510: 20013,
+	37511: 20210,
+	37512: 23449,
+	37513: 24544,
+	37514: 25277,
+	37515: 26172,
+	37516: 26609,
+	37517: 27880,
+	37518: 34411,
+	37519: 34935,
+	37520: 35387,
+	37521: 37198,
+	37522: 37619,
+	37523: 39376,
+	37524: 27159,
+	37525: 28710,
+	37526: 29482,
+	37527: 33511,
+	37528: 33879,
+	37529: 36015,
+	37530: 19969,
+	37531: 20806,
+	37532: 20939,
+	37533: 21899,
+	37534: 23541,
+	37535: 24086,
+	37536: 24115,
+	37537: 24193,
+	37538: 24340,
+	37539: 24373,
+	37540: 24427,
+	37541: 24500,
+	37542: 25074,
+	37543: 25361,
+	37544: 26274,
+	37545: 26397,
+	37546: 28526,
+	37547: 29266,
+	37548: 30010,
+	37549: 30522,
+	37550: 32884,
+	37551: 33081,
+	37552: 33144,
+	37553: 34678,
+	37554: 35519,
+	37555: 35548,
+	37556: 36229,
+	37557: 36339,
+	37558: 37530,
+	37559: 38263,
+	37560: 38914,
+	37561: 40165,
+	37562: 21189,
+	37563: 25431,
+	37564: 30452,
+	37565: 26389,
+	37566: 27784,
+	37567: 29645,
+	37568: 36035,
+	37569: 37806,
+	37570: 38515,
+	37571: 27941,
+	37572: 22684,
+	37573: 26894,
+	37574: 27084,
+	37575: 36861,
+	37576: 37786,
+	37577: 30171,
+	37578: 36890,
+	37579: 22618,
+	37580: 26626,
+	37581: 25524,
+	37582: 27131,
+	37583: 20291,
+	37584: 28460,
+	37585: 26584,
+	37586: 36795,
+	37587: 34086,
+	37588: 32180,
+	37589: 37716,
+	37590: 26943,
+	37591: 28528,
+	37592: 22378,
+	37593: 22775,
+	37594: 23340,
+	37595: 32044,
+	37596: 29226,
+	37597: 21514,
+	37598: 37347,
+	37599: 40372,
+	37600: 20141,
+	37601: 20302,
+	37602: 20572,
+	37603: 20597,
+	37604: 21059,
+	37605: 35998,
+	37606: 21576,
+	37607: 22564,
+	37608: 23450,
+	37609: 24093,
+	37610: 24213,
+	37611: 24237,
+	37612: 24311,
+	37613: 24351,
+	37614: 24716,
+	37615: 25269,
+	37616: 25402,
+	37617: 25552,
+	37618: 26799,
+	37619: 27712,
+	37620: 30855,
+	37621: 31118,
+	37622: 31243,
+	37623: 32224,
+	37624: 33351,
+	37625: 35330,
+	37626: 35558,
+	37627: 36420,
+	37628: 36883,
+	37696: 37048,
+	37697: 37165,
+	37698: 37336,
+	37699: 40718,
+	37700: 27877,
+	37701: 25688,
+	37702: 25826,
+	37703: 25973,
+	37704: 28404,
+	37705: 30340,
+	37706: 31515,
+	37707: 36969,
+	37708: 37841,
+	37709: 28346,
+	37710: 21746,
+	37711: 24505,
+	37712: 25764,
+	37713: 36685,
+	37714: 36845,
+	37715: 37444,
+	37716: 20856,
+	37717: 22635,
+	37718: 22825,
+	37719: 23637,
+	37720: 24215,
+	37721: 28155,
+	37722: 32399,
+	37723: 29980,
+	37724: 36028,
+	37725: 36578,
+	37726: 39003,
+	37727: 28857,
+	37728: 20253,
+	37729: 27583,
+	37730: 28593,
+	37731: 30000,
+	37732: 38651,
+	37733: 20814,
+	37734: 21520,
+	37735: 22581,
+	37736: 22615,
+	37737: 22956,
+	37738: 23648,
+	37739: 24466,
+	37740: 26007,
+	37741: 26460,
+	37742: 28193,
+	37743: 30331,
+	37744: 33759,
+	37745: 36077,
+	37746: 36884,
+	37747: 37117,
+	37748: 37709,
+	37749: 30757,
+	37750: 30778,
+	37751: 21162,
+	37752: 24230,
+	37753: 22303,
+	37754: 22900,
+	37755: 24594,
+	37756: 20498,
+	37757: 20826,
+	37758: 20908,
+	37760: 20941,
+	37761: 20992,
+	37762: 21776,
+	37763: 22612,
+	37764: 22616,
+	37765: 22871,
+	37766: 23445,
+	37767: 23798,
+	37768: 23947,
+	37769: 24764,
+	37770: 25237,
+	37771: 25645,
+	37772: 26481,
+	37773: 26691,
+	37774: 26812,
+	37775: 26847,
+	37776: 30423,
+	37777: 28120,
+	37778: 28271,
+	37779: 28059,
+	37780: 28783,
+	37781: 29128,
+	37782: 24403,
+	37783: 30168,
+	37784: 31095,
+	37785: 31561,
+	37786: 31572,
+	37787: 31570,
+	37788: 31958,
+	37789: 32113,
+	37790: 21040,
+	37791: 33891,
+	37792: 34153,
+	37793: 34276,
+	37794: 35342,
+	37795: 35588,
+	37796: 35910,
+	37797: 36367,
+	37798: 36867,
+	37799: 36879,
+	37800: 37913,
+	37801: 38518,
+	37802: 38957,
+	37803: 39472,
+	37804: 38360,
+	37805: 20685,
+	37806: 21205,
+	37807: 21516,
+	37808: 22530,
+	37809: 23566,
+	37810: 24999,
+	37811: 25758,
+	37812: 27934,
+	37813: 30643,
+	37814: 31461,
+	37815: 33012,
+	37816: 33796,
+	37817: 36947,
+	37818: 37509,
+	37819: 23776,
+	37820: 40199,
+	37821: 21311,
+	37822: 24471,
+	37823: 24499,
+	37824: 28060,
+	37825: 29305,
+	37826: 30563,
+	37827: 31167,
+	37828: 31716,
+	37829: 27602,
+	37830: 29420,
+	37831: 35501,
+	37832: 26627,
+	37833: 27233,
+	37834: 20984,
+	37835: 31361,
+	37836: 26932,
+	37837: 23626,
+	37838: 40182,
+	37839: 33515,
+	37840: 23493,
+	37841: 37193,
+	37842: 28702,
+	37843: 22136,
+	37844: 23663,
+	37845: 24775,
+	37846: 25958,
+	37847: 27788,
+	37848: 35930,
+	37849: 36929,
+	37850: 38931,
+	37851: 21585,
+	37852: 26311,
+	37853: 37389,
+	37854: 22856,
+	37855: 37027,
+	37856: 20869,
+	37857: 20045,
+	37858: 20970,
+	37859: 34201,
+	37860: 35598,
+	37861: 28760,
+	37862: 25466,
+	37863: 37707,
+	37864: 26978,
+	37865: 39348,
+	37866: 32260,
+	37867: 30071,
+	37868: 21335,
+	37869: 26976,
+	37870: 36575,
+	37871: 38627,
+	37872: 27741,
+	37873: 20108,
+	37874: 23612,
+	37875: 24336,
+	37876: 36841,
+	37877: 21250,
+	37878: 36049,
+	37879: 32905,
+	37880: 34425,
+	37881: 24319,
+	37882: 26085,
+	37883: 20083,
+	37884: 20837,
+	37952: 22914,
+	37953: 23615,
+	37954: 38894,
+	37955: 20219,
+	37956: 22922,
+	37957: 24525,
+	37958: 35469,
+	37959: 28641,
+	37960: 31152,
+	37961: 31074,
+	37962: 23527,
+	37963: 33905,
+	37964: 29483,
+	37965: 29105,
+	37966: 24180,
+	37967: 24565,
+	37968: 25467,
+	37969: 25754,
+	37970: 29123,
+	37971: 31896,
+	37972: 20035,
+	37973: 24316,
+	37974: 20043,
+	37975: 22492,
+	37976: 22178,
+	37977: 24745,
+	37978: 28611,
+	37979: 32013,
+	37980: 33021,
+	37981: 33075,
+	37982: 33215,
+	37983: 36786,
+	37984: 35223,
+	37985: 34468,
+	37986: 24052,
+	37987: 25226,
+	37988: 25773,
+	37989: 35207,
+	37990: 26487,
+	37991: 27874,
+	37992: 27966,
+	37993: 29750,
+	37994: 30772,
+	37995: 23110,
+	37996: 32629,
+	37997: 33453,
+	37998: 39340,
+	37999: 20467,
+	38000: 24259,
+	38001: 25309,
+	38002: 25490,
+	38003: 25943,
+	38004: 26479,
+	38005: 30403,
+	38006: 29260,
+	38007: 32972,
+	38008: 32954,
+	38009: 36649,
+	38010: 37197,
+	38011: 20493,
+	38012: 22521,
+	38013: 23186,
+	38014: 26757,
+	38016: 26995,
+	38017: 29028,
+	38018: 29437,
+	38019: 36023,
+	38020: 22770,
+	38021: 36064,
+	38022: 38506,
+	38023: 36889,
+	38024: 34687,
+	38025: 31204,
+	38026: 30695,
+	38027: 33833,
+	38028: 20271,
+	38029: 21093,
+	38030: 21338,
+	38031: 25293,
+	38032: 26575,
+	38033: 27850,
+	38034: 30333,
+	38035: 31636,
+	38036: 31893,
+	38037: 33334,
+	38038: 34180,
+	38039: 36843,
+	38040: 26333,
+	38041: 28448,
+	38042: 29190,
+	38043: 32283,
+	38044: 33707,
+	38045: 39361,
+	38046: 40614,
+	38047: 20989,
+	38048: 31665,
+	38049: 30834,
+	38050: 31672,
+	38051: 32903,
+	38052: 31560,
+	38053: 27368,
+	38054: 24161,
+	38055: 32908,
+	38056: 30033,
+	38057: 30048,
+	38058: 20843,
+	38059: 37474,
+	38060: 28300,
+	38061: 30330,
+	38062: 37271,
+	38063: 39658,
+	38064: 20240,
+	38065: 32624,
+	38066: 25244,
+	38067: 31567,
+	38068: 38309,
+	38069: 40169,
+	38070: 22138,
+	38071: 22617,
+	38072: 34532,
+	38073: 38588,
+	38074: 20276,
+	38075: 21028,
+	38076: 21322,
+	38077: 21453,
+	38078: 21467,
+	38079: 24070,
+	38080: 25644,
+	38081: 26001,
+	38082: 26495,
+	38083: 27710,
+	38084: 27726,
+	38085: 29256,
+	38086: 29359,
+	38087: 29677,
+	38088: 30036,
+	38089: 32321,
+	38090: 33324,
+	38091: 34281,
+	38092: 36009,
+	38093: 31684,
+	38094: 37318,
+	38095: 29033,
+	38096: 38930,
+	38097: 39151,
+	38098: 25405,
+	38099: 26217,
+	38100: 30058,
+	38101: 30436,
+	38102: 30928,
+	38103: 34115,
+	38104: 34542,
+	38105: 21290,
+	38106: 21329,
+	38107: 21542,
+	38108: 22915,
+	38109: 24199,
+	38110: 24444,
+	38111: 24754,
+	38112: 25161,
+	38113: 25209,
+	38114: 25259,
+	38115: 26000,
+	38116: 27604,
+	38117: 27852,
+	38118: 30130,
+	38119: 30382,
+	38120: 30865,
+	38121: 31192,
+	38122: 32203,
+	38123: 32631,
+	38124: 32933,
+	38125: 34987,
+	38126: 35513,
+	38127: 36027,
+	38128: 36991,
+	38129: 38750,
+	38130: 39131,
+	38131: 27147,
+	38132: 31800,
+	38133: 20633,
+	38134: 23614,
+	38135: 24494,
+	38136: 26503,
+	38137: 27608,
+	38138: 29749,
+	38139: 30473,
+	38140: 32654,
+	38208: 40763,
+	38209: 26570,
+	38210: 31255,
+	38211: 21305,
+	38212: 30091,
+	38213: 39661,
+	38214: 24422,
+	38215: 33181,
+	38216: 33777,
+	38217: 32920,
+	38218: 24380,
+	38219: 24517,
+	38220: 30050,
+	38221: 31558,
+	38222: 36924,
+	38223: 26727,
+	38224: 23019,
+	38225: 23195,
+	38226: 32016,
+	38227: 30334,
+	38228: 35628,
+	38229: 20469,
+	38230: 24426,
+	38231: 27161,
+	38232: 27703,
+	38233: 28418,
+	38234: 29922,
+	38235: 31080,
+	38236: 34920,
+	38237: 35413,
+	38238: 35961,
+	38239: 24287,
+	38240: 25551,
+	38241: 30149,
+	38242: 31186,
+	38243: 33495,
+	38244: 37672,
+	38245: 37618,
+	38246: 33948,
+	38247: 34541,
+	38248: 39981,
+	38249: 21697,
+	38250: 24428,
+	38251: 25996,
+	38252: 27996,
+	38253: 28693,
+	38254: 36007,
+	38255: 36051,
+	38256: 38971,
+	38257: 25935,
+	38258: 29942,
+	38259: 19981,
+	38260: 20184,
+	38261: 22496,
+	38262: 22827,
+	38263: 23142,
+	38264: 23500,
+	38265: 20904,
+	38266: 24067,
+	38267: 24220,
+	38268: 24598,
+	38269: 25206,
+	38270: 25975,
+	38272: 26023,
+	38273: 26222,
+	38274: 28014,
+	38275: 29238,
+	38276: 31526,
+	38277: 33104,
+	38278: 33178,
+	38279: 33433,
+	38280: 35676,
+	38281: 36000,
+	38282: 36070,
+	38283: 36212,
+	38284: 38428,
+	38285: 38468,
+	38286: 20398,
+	38287: 25771,
+	38288: 27494,
+	38289: 33310,
+	38290: 33889,
+	38291: 34154,
+	38292: 37096,
+	38293: 23553,
+	38294: 26963,
+	38295: 39080,
+	38296: 33914,
+	38297: 34135,
+	38298: 20239,
+	38299: 21103,
+	38300: 24489,
+	38301: 24133,
+	38302: 26381,
+	38303: 31119,
+	38304: 33145,
+	38305: 35079,
+	38306: 35206,
+	38307: 28149,
+	38308: 24343,
+	38309: 25173,
+	38310: 27832,
+	38311: 20175,
+	38312: 29289,
+	38313: 39826,
+	38314: 20998,
+	38315: 21563,
+	38316: 22132,
+	38317: 22707,
+	38318: 24996,
+	38319: 25198,
+	38320: 28954,
+	38321: 22894,
+	38322: 31881,
+	38323: 31966,
+	38324: 32027,
+	38325: 38640,
+	38326: 25991,
+	38327: 32862,
+	38328: 19993,
+	38329: 20341,
+	38330: 20853,
+	38331: 22592,
+	38332: 24163,
+	38333: 24179,
+	38334: 24330,
+	38335: 26564,
+	38336: 20006,
+	38337: 34109,
+	38338: 38281,
+	38339: 38491,
+	38340: 31859,
+	38341: 38913,
+	38342: 20731,
+	38343: 22721,
+	38344: 30294,
+	38345: 30887,
+	38346: 21029,
+	38347: 30629,
+	38348: 34065,
+	38349: 31622,
+	38350: 20559,
+	38351: 22793,
+	38352: 29255,
+	38353: 31687,
+	38354: 32232,
+	38355: 36794,
+	38356: 36820,
+	38357: 36941,
+	38358: 20415,
+	38359: 21193,
+	38360: 23081,
+	38361: 24321,
+	38362: 38829,
+	38363: 20445,
+	38364: 33303,
+	38365: 37610,
+	38366: 22275,
+	38367: 25429,
+	38368: 27497,
+	38369: 29995,
+	38370: 35036,
+	38371: 36628,
+	38372: 31298,
+	38373: 21215,
+	38374: 22675,
+	38375: 24917,
+	38376: 25098,
+	38377: 26286,
+	38378: 27597,
+	38379: 31807,
+	38380: 33769,
+	38381: 20515,
+	38382: 20472,
+	38383: 21253,
+	38384: 21574,
+	38385: 22577,
+	38386: 22857,
+	38387: 23453,
+	38388: 23792,
+	38389: 23791,
+	38390: 23849,
+	38391: 24214,
+	38392: 25265,
+	38393: 25447,
+	38394: 25918,
+	38395: 26041,
+	38396: 26379,
+	38464: 27861,
+	38465: 27873,
+	38466: 28921,
+	38467: 30770,
+	38468: 32299,
+	38469: 32990,
+	38470: 33459,
+	38471: 33804,
+	38472: 34028,
+	38473: 34562,
+	38474: 35090,
+	38475: 35370,
+	38476: 35914,
+	38477: 37030,
+	38478: 37586,
+	38479: 39165,
+	38480: 40179,
+	38481: 40300,
+	38482: 20047,
+	38483: 20129,
+	38484: 20621,
+	38485: 21078,
+	38486: 22346,
+	38487: 22952,
+	38488: 24125,
+	38489: 24536,
+	38490: 24537,
+	38491: 25151,
+	38492: 26292,
+	38493: 26395,
+	38494: 26576,
+	38495: 26834,
+	38496: 20882,
+	38497: 32033,
+	38498: 32938,
+	38499: 33192,
+	38500: 35584,
+	38501: 35980,
+	38502: 36031,
+	38503: 37502,
+	38504: 38450,
+	38505: 21536,
+	38506: 38956,
+	38507: 21271,
+	38508: 20693,
+	38509: 21340,
+	38510: 22696,
+	38511: 25778,
+	38512: 26420,
+	38513: 29287,
+	38514: 30566,
+	38515: 31302,
+	38516: 37350,
+	38517: 21187,
+	38518: 27809,
+	38519: 27526,
+	38520: 22528,
+	38521: 24140,
+	38522: 22868,
+	38523: 26412,
+	38524: 32763,
+	38525: 20961,
+	38526: 30406,
+	38528: 25705,
+	38529: 30952,
+	38530: 39764,
+	38531: 40635,
+	38532: 22475,
+	38533: 22969,
+	38534: 26151,
+	38535: 26522,
+	38536: 27598,
+	38537: 21737,
+	38538: 27097,
+	38539: 24149,
+	38540: 33180,
+	38541: 26517,
+	38542: 39850,
+	38543: 26622,
+	38544: 40018,
+	38545: 26717,
+	38546: 20134,
+	38547: 20451,
+	38548: 21448,
+	38549: 25273,
+	38550: 26411,
+	38551: 27819,
+	38552: 36804,
+	38553: 20397,
+	38554: 32365,
+	38555: 40639,
+	38556: 19975,
+	38557: 24930,
+	38558: 28288,
+	38559: 28459,
+	38560: 34067,
+	38561: 21619,
+	38562: 26410,
+	38563: 39749,
+	38564: 24051,
+	38565: 31637,
+	38566: 23724,
+	38567: 23494,
+	38568: 34588,
+	38569: 28234,
+	38570: 34001,
+	38571: 31252,
+	38572: 33032,
+	38573: 22937,
+	38574: 31885,
+	38575: 27665,
+	38576: 30496,
+	38577: 21209,
+	38578: 22818,
+	38579: 28961,
+	38580: 29279,
+	38581: 30683,
+	38582: 38695,
+	38583: 40289,
+	38584: 26891,
+	38585: 23167,
+	38586: 23064,
+	38587: 20901,
+	38588: 21517,
+	38589: 21629,
+	38590: 26126,
+	38591: 30431,
+	38592: 36855,
+	38593: 37528,
+	38594: 40180,
+	38595: 23018,
+	38596: 29277,
+	38597: 28357,
+	38598: 20813,
+	38599: 26825,
+	38600: 32191,
+	38601: 32236,
+	38602: 38754,
+	38603: 40634,
+	38604: 25720,
+	38605: 27169,
+	38606: 33538,
+	38607: 22916,
+	38608: 23391,
+	38609: 27611,
+	38610: 29467,
+	38611: 30450,
+	38612: 32178,
+	38613: 32791,
+	38614: 33945,
+	38615: 20786,
+	38616: 26408,
+	38617: 40665,
+	38618: 30446,
+	38619: 26466,
+	38620: 21247,
+	38621: 39173,
+	38622: 23588,
+	38623: 25147,
+	38624: 31870,
+	38625: 36016,
+	38626: 21839,
+	38627: 24758,
+	38628: 32011,
+	38629: 38272,
+	38630: 21249,
+	38631: 20063,
+	38632: 20918,
+	38633: 22812,
+	38634: 29242,
+	38635: 32822,
+	38636: 37326,
+	38637: 24357,
+	38638: 30690,
+	38639: 21380,
+	38640: 24441,
+	38641: 32004,
+	38642: 34220,
+	38643: 35379,
+	38644: 36493,
+	38645: 38742,
+	38646: 26611,
+	38647: 34222,
+	38648: 37971,
+	38649: 24841,
+	38650: 24840,
+	38651: 27833,
+	38652: 30290,
+	38720: 35565,
+	38721: 36664,
+	38722: 21807,
+	38723: 20305,
+	38724: 20778,
+	38725: 21191,
+	38726: 21451,
+	38727: 23461,
+	38728: 24189,
+	38729: 24736,
+	38730: 24962,
+	38731: 25558,
+	38732: 26377,
+	38733: 26586,
+	38734: 28263,
+	38735: 28044,
+	38736: 29494,
+	38737: 29495,
+	38738: 30001,
+	38739: 31056,
+	38740: 35029,
+	38741: 35480,
+	38742: 36938,
+	38743: 37009,
+	38744: 37109,
+	38745: 38596,
+	38746: 34701,
+	38747: 22805,
+	38748: 20104,
+	38749: 20313,
+	38750: 19982,
+	38751: 35465,
+	38752: 36671,
+	38753: 38928,
+	38754: 20653,
+	38755: 24188,
+	38756: 22934,
+	38757: 23481,
+	38758: 24248,
+	38759: 25562,
+	38760: 25594,
+	38761: 25793,
+	38762: 26332,
+	38763: 26954,
+	38764: 27096,
+	38765: 27915,
+	38766: 28342,
+	38767: 29076,
+	38768: 29992,
+	38769: 31407,
+	38770: 32650,
+	38771: 32768,
+	38772: 33865,
+	38773: 33993,
+	38774: 35201,
+	38775: 35617,
+	38776: 36362,
+	38777: 36965,
+	38778: 38525,
+	38779: 39178,
+	38780: 24958,
+	38781: 25233,
+	38782: 27442,
+	38784: 27779,
+	38785: 28020,
+	38786: 32716,
+	38787: 32764,
+	38788: 28096,
+	38789: 32645,
+	38790: 34746,
+	38791: 35064,
+	38792: 26469,
+	38793: 33713,
+	38794: 38972,
+	38795: 38647,
+	38796: 27931,
+	38797: 32097,
+	38798: 33853,
+	38799: 37226,
+	38800: 20081,
+	38801: 21365,
+	38802: 23888,
+	38803: 27396,
+	38804: 28651,
+	38805: 34253,
+	38806: 34349,
+	38807: 35239,
+	38808: 21033,
+	38809: 21519,
+	38810: 23653,
+	38811: 26446,
+	38812: 26792,
+	38813: 29702,
+	38814: 29827,
+	38815: 30178,
+	38816: 35023,
+	38817: 35041,
+	38818: 37324,
+	38819: 38626,
+	38820: 38520,
+	38821: 24459,
+	38822: 29575,
+	38823: 31435,
+	38824: 33870,
+	38825: 25504,
+	38826: 30053,
+	38827: 21129,
+	38828: 27969,
+	38829: 28316,
+	38830: 29705,
+	38831: 30041,
+	38832: 30827,
+	38833: 31890,
+	38834: 38534,
+	38835: 31452,
+	38836: 40845,
+	38837: 20406,
+	38838: 24942,
+	38839: 26053,
+	38840: 34396,
+	38841: 20102,
+	38842: 20142,
+	38843: 20698,
+	38844: 20001,
+	38845: 20940,
+	38846: 23534,
+	38847: 26009,
+	38848: 26753,
+	38849: 28092,
+	38850: 29471,
+	38851: 30274,
+	38852: 30637,
+	38853: 31260,
+	38854: 31975,
+	38855: 33391,
+	38856: 35538,
+	38857: 36988,
+	38858: 37327,
+	38859: 38517,
+	38860: 38936,
+	38861: 21147,
+	38862: 32209,
+	38863: 20523,
+	38864: 21400,
+	38865: 26519,
+	38866: 28107,
+	38867: 29136,
+	38868: 29747,
+	38869: 33256,
+	38870: 36650,
+	38871: 38563,
+	38872: 40023,
+	38873: 40607,
+	38874: 29792,
+	38875: 22593,
+	38876: 28057,
+	38877: 32047,
+	38878: 39006,
+	38879: 20196,
+	38880: 20278,
+	38881: 20363,
+	38882: 20919,
+	38883: 21169,
+	38884: 23994,
+	38885: 24604,
+	38886: 29618,
+	38887: 31036,
+	38888: 33491,
+	38889: 37428,
+	38890: 38583,
+	38891: 38646,
+	38892: 38666,
+	38893: 40599,
+	38894: 40802,
+	38895: 26278,
+	38896: 27508,
+	38897: 21015,
+	38898: 21155,
+	38899: 28872,
+	38900: 35010,
+	38901: 24265,
+	38902: 24651,
+	38903: 24976,
+	38904: 28451,
+	38905: 29001,
+	38906: 31806,
+	38907: 32244,
+	38908: 32879,
+	38976: 34030,
+	38977: 36899,
+	38978: 37676,
+	38979: 21570,
+	38980: 39791,
+	38981: 27347,
+	38982: 28809,
+	38983: 36034,
+	38984: 36335,
+	38985: 38706,
+	38986: 21172,
+	38987: 23105,
+	38988: 24266,
+	38989: 24324,
+	38990: 26391,
+	38991: 27004,
+	38992: 27028,
+	38993: 28010,
+	38994: 28431,
+	38995: 29282,
+	38996: 29436,
+	38997: 31725,
+	38998: 32769,
+	38999: 32894,
+	39000: 34635,
+	39001: 37070,
+	39002: 20845,
+	39003: 40595,
+	39004: 31108,
+	39005: 32907,
+	39006: 37682,
+	39007: 35542,
+	39008: 20525,
+	39009: 21644,
+	39010: 35441,
+	39011: 27498,
+	39012: 36036,
+	39013: 33031,
+	39014: 24785,
+	39015: 26528,
+	39016: 40434,
+	39017: 20121,
+	39018: 20120,
+	39019: 39952,
+	39020: 35435,
+	39021: 34241,
+	39022: 34152,
+	39023: 26880,
+	39024: 28286,
+	39025: 30871,
+	39026: 33109,
+	39071: 24332,
+	39072: 19984,
+	39073: 19989,
+	39074: 20010,
+	39075: 20017,
+	39076: 20022,
+	39077: 20028,
+	39078: 20031,
+	39079: 20034,
+	39080: 20054,
+	39081: 20056,
+	39082: 20098,
+	39083: 20101,
+	39084: 35947,
+	39085: 20106,
+	39086: 33298,
+	39087: 24333,
+	39088: 20110,
+	39089: 20126,
+	39090: 20127,
+	39091: 20128,
+	39092: 20130,
+	39093: 20144,
+	39094: 20147,
+	39095: 20150,
+	39096: 20174,
+	39097: 20173,
+	39098: 20164,
+	39099: 20166,
+	39100: 20162,
+	39101: 20183,
+	39102: 20190,
+	39103: 20205,
+	39104: 20191,
+	39105: 20215,
+	39106: 20233,
+	39107: 20314,
+	39108: 20272,
+	39109: 20315,
+	39110: 20317,
+	39111: 20311,
+	39112: 20295,
+	39113: 20342,
+	39114: 20360,
+	39115: 20367,
+	39116: 20376,
+	39117: 20347,
+	39118: 20329,
+	39119: 20336,
+	39120: 20369,
+	39121: 20335,
+	39122: 20358,
+	39123: 20374,
+	39124: 20760,
+	39125: 20436,
+	39126: 20447,
+	39127: 20430,
+	39128: 20440,
+	39129: 20443,
+	39130: 20433,
+	39131: 20442,
+	39132: 20432,
+	39133: 20452,
+	39134: 20453,
+	39135: 20506,
+	39136: 20520,
+	39137: 20500,
+	39138: 20522,
+	39139: 20517,
+	39140: 20485,
+	39141: 20252,
+	39142: 20470,
+	39143: 20513,
+	39144: 20521,
+	39145: 20524,
+	39146: 20478,
+	39147: 20463,
+	39148: 20497,
+	39149: 20486,
+	39150: 20547,
+	39151: 20551,
+	39152: 26371,
+	39153: 20565,
+	39154: 20560,
+	39155: 20552,
+	39156: 20570,
+	39157: 20566,
+	39158: 20588,
+	39159: 20600,
+	39160: 20608,
+	39161: 20634,
+	39162: 20613,
+	39163: 20660,
+	39164: 20658,
+	39232: 20681,
+	39233: 20682,
+	39234: 20659,
+	39235: 20674,
+	39236: 20694,
+	39237: 20702,
+	39238: 20709,
+	39239: 20717,
+	39240: 20707,
+	39241: 20718,
+	39242: 20729,
+	39243: 20725,
+	39244: 20745,
+	39245: 20737,
+	39246: 20738,
+	39247: 20758,
+	39248: 20757,
+	39249: 20756,
+	39250: 20762,
+	39251: 20769,
+	39252: 20794,
+	39253: 20791,
+	39254: 20796,
+	39255: 20795,
+	39256: 20799,
+	39257: 20800,
+	39258: 20818,
+	39259: 20812,
+	39260: 20820,
+	39261: 20834,
+	39262: 31480,
+	39263: 20841,
+	39264: 20842,
+	39265: 20846,
+	39266: 20864,
+	39267: 20866,
+	39268: 22232,
+	39269: 20876,
+	39270: 20873,
+	39271: 20879,
+	39272: 20881,
+	39273: 20883,
+	39274: 20885,
+	39275: 20886,
+	39276: 20900,
+	39277: 20902,
+	39278: 20898,
+	39279: 20905,
+	39280: 20906,
+	39281: 20907,
+	39282: 20915,
+	39283: 20913,
+	39284: 20914,
+	39285: 20912,
+	39286: 20917,
+	39287: 20925,
+	39288: 20933,
+	39289: 20937,
+	39290: 20955,
+	39291: 20960,
+	39292: 34389,
+	39293: 20969,
+	39294: 20973,
+	39296: 20976,
+	39297: 20981,
+	39298: 20990,
+	39299: 20996,
+	39300: 21003,
+	39301: 21012,
+	39302: 21006,
+	39303: 21031,
+	39304: 21034,
+	39305: 21038,
+	39306: 21043,
+	39307: 21049,
+	39308: 21071,
+	39309: 21060,
+	39310: 21067,
+	39311: 21068,
+	39312: 21086,
+	39313: 21076,
+	39314: 21098,
+	39315: 21108,
+	39316: 21097,
+	39317: 21107,
+	39318: 21119,
+	39319: 21117,
+	39320: 21133,
+	39321: 21140,
+	39322: 21138,
+	39323: 21105,
+	39324: 21128,
+	39325: 21137,
+	39326: 36776,
+	39327: 36775,
+	39328: 21164,
+	39329: 21165,
+	39330: 21180,
+	39331: 21173,
+	39332: 21185,
+	39333: 21197,
+	39334: 21207,
+	39335: 21214,
+	39336: 21219,
+	39337: 21222,
+	39338: 39149,
+	39339: 21216,
+	39340: 21235,
+	39341: 21237,
+	39342: 21240,
+	39343: 21241,
+	39344: 21254,
+	39345: 21256,
+	39346: 30008,
+	39347: 21261,
+	39348: 21264,
+	39349: 21263,
+	39350: 21269,
+	39351: 21274,
+	39352: 21283,
+	39353: 21295,
+	39354: 21297,
+	39355: 21299,
+	39356: 21304,
+	39357: 21312,
+	39358: 21318,
+	39359: 21317,
+	39360: 19991,
+	39361: 21321,
+	39362: 21325,
+	39363: 20950,
+	39364: 21342,
+	39365: 21353,
+	39366: 21358,
+	39367: 22808,
+	39368: 21371,
+	39369: 21367,
+	39370: 21378,
+	39371: 21398,
+	39372: 21408,
+	39373: 21414,
+	39374: 21413,
+	39375: 21422,
+	39376: 21424,
+	39377: 21430,
+	39378: 21443,
+	39379: 31762,
+	39380: 38617,
+	39381: 21471,
+	39382: 26364,
+	39383: 29166,
+	39384: 21486,
+	39385: 21480,
+	39386: 21485,
+	39387: 21498,
+	39388: 21505,
+	39389: 21565,
+	39390: 21568,
+	39391: 21548,
+	39392: 21549,
+	39393: 21564,
+	39394: 21550,
+	39395: 21558,
+	39396: 21545,
+	39397: 21533,
+	39398: 21582,
+	39399: 21647,
+	39400: 21621,
+	39401: 21646,
+	39402: 21599,
+	39403: 21617,
+	39404: 21623,
+	39405: 21616,
+	39406: 21650,
+	39407: 21627,
+	39408: 21632,
+	39409: 21622,
+	39410: 21636,
+	39411: 21648,
+	39412: 21638,
+	39413: 21703,
+	39414: 21666,
+	39415: 21688,
+	39416: 21669,
+	39417: 21676,
+	39418: 21700,
+	39419: 21704,
+	39420: 21672,
+	39488: 21675,
+	39489: 21698,
+	39490: 21668,
+	39491: 21694,
+	39492: 21692,
+	39493: 21720,
+	39494: 21733,
+	39495: 21734,
+	39496: 21775,
+	39497: 21780,
+	39498: 21757,
+	39499: 21742,
+	39500: 21741,
+	39501: 21754,
+	39502: 21730,
+	39503: 21817,
+	39504: 21824,
+	39505: 21859,
+	39506: 21836,
+	39507: 21806,
+	39508: 21852,
+	39509: 21829,
+	39510: 21846,
+	39511: 21847,
+	39512: 21816,
+	39513: 21811,
+	39514: 21853,
+	39515: 21913,
+	39516: 21888,
+	39517: 21679,
+	39518: 21898,
+	39519: 21919,
+	39520: 21883,
+	39521: 21886,
+	39522: 21912,
+	39523: 21918,
+	39524: 21934,
+	39525: 21884,
+	39526: 21891,
+	39527: 21929,
+	39528: 21895,
+	39529: 21928,
+	39530: 21978,
+	39531: 21957,
+	39532: 21983,
+	39533: 21956,
+	39534: 21980,
+	39535: 21988,
+	39536: 21972,
+	39537: 22036,
+	39538: 22007,
+	39539: 22038,
+	39540: 22014,
+	39541: 22013,
+	39542: 22043,
+	39543: 22009,
+	39544: 22094,
+	39545: 22096,
+	39546: 29151,
+	39547: 22068,
+	39548: 22070,
+	39549: 22066,
+	39550: 22072,
+	39552: 22123,
+	39553: 22116,
+	39554: 22063,
+	39555: 22124,
+	39556: 22122,
+	39557: 22150,
+	39558: 22144,
+	39559: 22154,
+	39560: 22176,
+	39561: 22164,
+	39562: 22159,
+	39563: 22181,
+	39564: 22190,
+	39565: 22198,
+	39566: 22196,
+	39567: 22210,
+	39568: 22204,
+	39569: 22209,
+	39570: 22211,
+	39571: 22208,
+	39572: 22216,
+	39573: 22222,
+	39574: 22225,
+	39575: 22227,
+	39576: 22231,
+	39577: 22254,
+	39578: 22265,
+	39579: 22272,
+	39580: 22271,
+	39581: 22276,
+	39582: 22281,
+	39583: 22280,
+	39584: 22283,
+	39585: 22285,
+	39586: 22291,
+	39587: 22296,
+	39588: 22294,
+	39589: 21959,
+	39590: 22300,
+	39591: 22310,
+	39592: 22327,
+	39593: 22328,
+	39594: 22350,
+	39595: 22331,
+	39596: 22336,
+	39597: 22351,
+	39598: 22377,
+	39599: 22464,
+	39600: 22408,
+	39601: 22369,
+	39602: 22399,
+	39603: 22409,
+	39604: 22419,
+	39605: 22432,
+	39606: 22451,
+	39607: 22436,
+	39608: 22442,
+	39609: 22448,
+	39610: 22467,
+	39611: 22470,
+	39612: 22484,
+	39613: 22482,
+	39614: 22483,
+	39615: 22538,
+	39616: 22486,
+	39617: 22499,
+	39618: 22539,
+	39619: 22553,
+	39620: 22557,
+	39621: 22642,
+	39622: 22561,
+	39623: 22626,
+	39624: 22603,
+	39625: 22640,
+	39626: 27584,
+	39627: 22610,
+	39628: 22589,
+	39629: 22649,
+	39630: 22661,
+	39631: 22713,
+	39632: 22687,
+	39633: 22699,
+	39634: 22714,
+	39635: 22750,
+	39636: 22715,
+	39637: 22712,
+	39638: 22702,
+	39639: 22725,
+	39640: 22739,
+	39641: 22737,
+	39642: 22743,
+	39643: 22745,
+	39644: 22744,
+	39645: 22757,
+	39646: 22748,
+	39647: 22756,
+	39648: 22751,
+	39649: 22767,
+	39650: 22778,
+	39651: 22777,
+	39652: 22779,
+	39653: 22780,
+	39654: 22781,
+	39655: 22786,
+	39656: 22794,
+	39657: 22800,
+	39658: 22811,
+	39659: 26790,
+	39660: 22821,
+	39661: 22828,
+	39662: 22829,
+	39663: 22834,
+	39664: 22840,
+	39665: 22846,
+	39666: 31442,
+	39667: 22869,
+	39668: 22864,
+	39669: 22862,
+	39670: 22874,
+	39671: 22872,
+	39672: 22882,
+	39673: 22880,
+	39674: 22887,
+	39675: 22892,
+	39676: 22889,
+	39744: 22904,
+	39745: 22913,
+	39746: 22941,
+	39747: 20318,
+	39748: 20395,
+	39749: 22947,
+	39750: 22962,
+	39751: 22982,
+	39752: 23016,
+	39753: 23004,
+	39754: 22925,
+	39755: 23001,
+	39756: 23002,
+	39757: 23077,
+	39758: 23071,
+	39759: 23057,
+	39760: 23068,
+	39761: 23049,
+	39762: 23066,
+	39763: 23104,
+	39764: 23148,
+	39765: 23113,
+	39766: 23093,
+	39767: 23094,
+	39768: 23138,
+	39769: 23146,
+	39770: 23194,
+	39771: 23228,
+	39772: 23230,
+	39773: 23243,
+	39774: 23234,
+	39775: 23229,
+	39776: 23267,
+	39777: 23255,
+	39778: 23270,
+	39779: 23273,
+	39780: 23254,
+	39781: 23290,
+	39782: 23291,
+	39783: 23308,
+	39784: 23307,
+	39785: 23318,
+	39786: 23346,
+	39787: 23248,
+	39788: 23338,
+	39789: 23350,
+	39790: 23358,
+	39791: 23363,
+	39792: 23365,
+	39793: 23360,
+	39794: 23377,
+	39795: 23381,
+	39796: 23386,
+	39797: 23387,
+	39798: 23397,
+	39799: 23401,
+	39800: 23408,
+	39801: 23411,
+	39802: 23413,
+	39803: 23416,
+	39804: 25992,
+	39805: 23418,
+	39806: 23424,
+	39808: 23427,
+	39809: 23462,
+	39810: 23480,
+	39811: 23491,
+	39812: 23495,
+	39813: 23497,
+	39814: 23508,
+	39815: 23504,
+	39816: 23524,
+	39817: 23526,
+	39818: 23522,
+	39819: 23518,
+	39820: 23525,
+	39821: 23531,
+	39822: 23536,
+	39823: 23542,
+	39824: 23539,
+	39825: 23557,
+	39826: 23559,
+	39827: 23560,
+	39828: 23565,
+	39829: 23571,
+	39830: 23584,
+	39831: 23586,
+	39832: 23592,
+	39833: 23608,
+	39834: 23609,
+	39835: 23617,
+	39836: 23622,
+	39837: 23630,
+	39838: 23635,
+	39839: 23632,
+	39840: 23631,
+	39841: 23409,
+	39842: 23660,
+	39843: 23662,
+	39844: 20066,
+	39845: 23670,
+	39846: 23673,
+	39847: 23692,
+	39848: 23697,
+	39849: 23700,
+	39850: 22939,
+	39851: 23723,
+	39852: 23739,
+	39853: 23734,
+	39854: 23740,
+	39855: 23735,
+	39856: 23749,
+	39857: 23742,
+	39858: 23751,
+	39859: 23769,
+	39860: 23785,
+	39861: 23805,
+	39862: 23802,
+	39863: 23789,
+	39864: 23948,
+	39865: 23786,
+	39866: 23819,
+	39867: 23829,
+	39868: 23831,
+	39869: 23900,
+	39870: 23839,
+	39871: 23835,
+	39872: 23825,
+	39873: 23828,
+	39874: 23842,
+	39875: 23834,
+	39876: 23833,
+	39877: 23832,
+	39878: 23884,
+	39879: 23890,
+	39880: 23886,
+	39881: 23883,
+	39882: 23916,
+	39883: 23923,
+	39884: 23926,
+	39885: 23943,
+	39886: 23940,
+	39887: 23938,
+	39888: 23970,
+	39889: 23965,
+	39890: 23980,
+	39891: 23982,
+	39892: 23997,
+	39893: 23952,
+	39894: 23991,
+	39895: 23996,
+	39896: 24009,
+	39897: 24013,
+	39898: 24019,
+	39899: 24018,
+	39900: 24022,
+	39901: 24027,
+	39902: 24043,
+	39903: 24050,
+	39904: 24053,
+	39905: 24075,
+	39906: 24090,
+	39907: 24089,
+	39908: 24081,
+	39909: 24091,
+	39910: 24118,
+	39911: 24119,
+	39912: 24132,
+	39913: 24131,
+	39914: 24128,
+	39915: 24142,
+	39916: 24151,
+	39917: 24148,
+	39918: 24159,
+	39919: 24162,
+	39920: 24164,
+	39921: 24135,
+	39922: 24181,
+	39923: 24182,
+	39924: 24186,
+	39925: 40636,
+	39926: 24191,
+	39927: 24224,
+	39928: 24257,
+	39929: 24258,
+	39930: 24264,
+	39931: 24272,
+	39932: 24271,
+	40000: 24278,
+	40001: 24291,
+	40002: 24285,
+	40003: 24282,
+	40004: 24283,
+	40005: 24290,
+	40006: 24289,
+	40007: 24296,
+	40008: 24297,
+	40009: 24300,
+	40010: 24305,
+	40011: 24307,
+	40012: 24304,
+	40013: 24308,
+	40014: 24312,
+	40015: 24318,
+	40016: 24323,
+	40017: 24329,
+	40018: 24413,
+	40019: 24412,
+	40020: 24331,
+	40021: 24337,
+	40022: 24342,
+	40023: 24361,
+	40024: 24365,
+	40025: 24376,
+	40026: 24385,
+	40027: 24392,
+	40028: 24396,
+	40029: 24398,
+	40030: 24367,
+	40031: 24401,
+	40032: 24406,
+	40033: 24407,
+	40034: 24409,
+	40035: 24417,
+	40036: 24429,
+	40037: 24435,
+	40038: 24439,
+	40039: 24451,
+	40040: 24450,
+	40041: 24447,
+	40042: 24458,
+	40043: 24456,
+	40044: 24465,
+	40045: 24455,
+	40046: 24478,
+	40047: 24473,
+	40048: 24472,
+	40049: 24480,
+	40050: 24488,
+	40051: 24493,
+	40052: 24508,
+	40053: 24534,
+	40054: 24571,
+	40055: 24548,
+	40056: 24568,
+	40057: 24561,
+	40058: 24541,
+	40059: 24755,
+	40060: 24575,
+	40061: 24609,
+	40062: 24672,
+	40064: 24601,
+	40065: 24592,
+	40066: 24617,
+	40067: 24590,
+	40068: 24625,
+	40069: 24603,
+	40070: 24597,
+	40071: 24619,
+	40072: 24614,
+	40073: 24591,
+	40074: 24634,
+	40075: 24666,
+	40076: 24641,
+	40077: 24682,
+	40078: 24695,
+	40079: 24671,
+	40080: 24650,
+	40081: 24646,
+	40082: 24653,
+	40083: 24675,
+	40084: 24643,
+	40085: 24676,
+	40086: 24642,
+	40087: 24684,
+	40088: 24683,
+	40089: 24665,
+	40090: 24705,
+	40091: 24717,
+	40092: 24807,
+	40093: 24707,
+	40094: 24730,
+	40095: 24708,
+	40096: 24731,
+	40097: 24726,
+	40098: 24727,
+	40099: 24722,
+	40100: 24743,
+	40101: 24715,
+	40102: 24801,
+	40103: 24760,
+	40104: 24800,
+	40105: 24787,
+	40106: 24756,
+	40107: 24560,
+	40108: 24765,
+	40109: 24774,
+	40110: 24757,
+	40111: 24792,
+	40112: 24909,
+	40113: 24853,
+	40114: 24838,
+	40115: 24822,
+	40116: 24823,
+	40117: 24832,
+	40118: 24820,
+	40119: 24826,
+	40120: 24835,
+	40121: 24865,
+	40122: 24827,
+	40123: 24817,
+	40124: 24845,
+	40125: 24846,
+	40126: 24903,
+	40127: 24894,
+	40128: 24872,
+	40129: 24871,
+	40130: 24906,
+	40131: 24895,
+	40132: 24892,
+	40133: 24876,
+	40134: 24884,
+	40135: 24893,
+	40136: 24898,
+	40137: 24900,
+	40138: 24947,
+	40139: 24951,
+	40140: 24920,
+	40141: 24921,
+	40142: 24922,
+	40143: 24939,
+	40144: 24948,
+	40145: 24943,
+	40146: 24933,
+	40147: 24945,
+	40148: 24927,
+	40149: 24925,
+	40150: 24915,
+	40151: 24949,
+	40152: 24985,
+	40153: 24982,
+	40154: 24967,
+	40155: 25004,
+	40156: 24980,
+	40157: 24986,
+	40158: 24970,
+	40159: 24977,
+	40160: 25003,
+	40161: 25006,
+	40162: 25036,
+	40163: 25034,
+	40164: 25033,
+	40165: 25079,
+	40166: 25032,
+	40167: 25027,
+	40168: 25030,
+	40169: 25018,
+	40170: 25035,
+	40171: 32633,
+	40172: 25037,
+	40173: 25062,
+	40174: 25059,
+	40175: 25078,
+	40176: 25082,
+	40177: 25076,
+	40178: 25087,
+	40179: 25085,
+	40180: 25084,
+	40181: 25086,
+	40182: 25088,
+	40183: 25096,
+	40184: 25097,
+	40185: 25101,
+	40186: 25100,
+	40187: 25108,
+	40188: 25115,
+	40256: 25118,
+	40257: 25121,
+	40258: 25130,
+	40259: 25134,
+	40260: 25136,
+	40261: 25138,
+	40262: 25139,
+	40263: 25153,
+	40264: 25166,
+	40265: 25182,
+	40266: 25187,
+	40267: 25179,
+	40268: 25184,
+	40269: 25192,
+	40270: 25212,
+	40271: 25218,
+	40272: 25225,
+	40273: 25214,
+	40274: 25234,
+	40275: 25235,
+	40276: 25238,
+	40277: 25300,
+	40278: 25219,
+	40279: 25236,
+	40280: 25303,
+	40281: 25297,
+	40282: 25275,
+	40283: 25295,
+	40284: 25343,
+	40285: 25286,
+	40286: 25812,
+	40287: 25288,
+	40288: 25308,
+	40289: 25292,
+	40290: 25290,
+	40291: 25282,
+	40292: 25287,
+	40293: 25243,
+	40294: 25289,
+	40295: 25356,
+	40296: 25326,
+	40297: 25329,
+	40298: 25383,
+	40299: 25346,
+	40300: 25352,
+	40301: 25327,
+	40302: 25333,
+	40303: 25424,
+	40304: 25406,
+	40305: 25421,
+	40306: 25628,
+	40307: 25423,
+	40308: 25494,
+	40309: 25486,
+	40310: 25472,
+	40311: 25515,
+	40312: 25462,
+	40313: 25507,
+	40314: 25487,
+	40315: 25481,
+	40316: 25503,
+	40317: 25525,
+	40318: 25451,
+	40320: 25449,
+	40321: 25534,
+	40322: 25577,
+	40323: 25536,
+	40324: 25542,
+	40325: 25571,
+	40326: 25545,
+	40327: 25554,
+	40328: 25590,
+	40329: 25540,
+	40330: 25622,
+	40331: 25652,
+	40332: 25606,
+	40333: 25619,
+	40334: 25638,
+	40335: 25654,
+	40336: 25885,
+	40337: 25623,
+	40338: 25640,
+	40339: 25615,
+	40340: 25703,
+	40341: 25711,
+	40342: 25718,
+	40343: 25678,
+	40344: 25898,
+	40345: 25749,
+	40346: 25747,
+	40347: 25765,
+	40348: 25769,
+	40349: 25736,
+	40350: 25788,
+	40351: 25818,
+	40352: 25810,
+	40353: 25797,
+	40354: 25799,
+	40355: 25787,
+	40356: 25816,
+	40357: 25794,
+	40358: 25841,
+	40359: 25831,
+	40360: 33289,
+	40361: 25824,
+	40362: 25825,
+	40363: 25260,
+	40364: 25827,
+	40365: 25839,
+	40366: 25900,
+	40367: 25846,
+	40368: 25844,
+	40369: 25842,
+	40370: 25850,
+	40371: 25856,
+	40372: 25853,
+	40373: 25880,
+	40374: 25884,
+	40375: 25861,
+	40376: 25892,
+	40377: 25891,
+	40378: 25899,
+	40379: 25908,
+	40380: 25909,
+	40381: 25911,
+	40382: 25910,
+	40383: 25912,
+	40384: 30027,
+	40385: 25928,
+	40386: 25942,
+	40387: 25941,
+	40388: 25933,
+	40389: 25944,
+	40390: 25950,
+	40391: 25949,
+	40392: 25970,
+	40393: 25976,
+	40394: 25986,
+	40395: 25987,
+	40396: 35722,
+	40397: 26011,
+	40398: 26015,
+	40399: 26027,
+	40400: 26039,
+	40401: 26051,
+	40402: 26054,
+	40403: 26049,
+	40404: 26052,
+	40405: 26060,
+	40406: 26066,
+	40407: 26075,
+	40408: 26073,
+	40409: 26080,
+	40410: 26081,
+	40411: 26097,
+	40412: 26482,
+	40413: 26122,
+	40414: 26115,
+	40415: 26107,
+	40416: 26483,
+	40417: 26165,
+	40418: 26166,
+	40419: 26164,
+	40420: 26140,
+	40421: 26191,
+	40422: 26180,
+	40423: 26185,
+	40424: 26177,
+	40425: 26206,
+	40426: 26205,
+	40427: 26212,
+	40428: 26215,
+	40429: 26216,
+	40430: 26207,
+	40431: 26210,
+	40432: 26224,
+	40433: 26243,
+	40434: 26248,
+	40435: 26254,
+	40436: 26249,
+	40437: 26244,
+	40438: 26264,
+	40439: 26269,
+	40440: 26305,
+	40441: 26297,
+	40442: 26313,
+	40443: 26302,
+	40444: 26300,
+	40512: 26308,
+	40513: 26296,
+	40514: 26326,
+	40515: 26330,
+	40516: 26336,
+	40517: 26175,
+	40518: 26342,
+	40519: 26345,
+	40520: 26352,
+	40521: 26357,
+	40522: 26359,
+	40523: 26383,
+	40524: 26390,
+	40525: 26398,
+	40526: 26406,
+	40527: 26407,
+	40528: 38712,
+	40529: 26414,
+	40530: 26431,
+	40531: 26422,
+	40532: 26433,
+	40533: 26424,
+	40534: 26423,
+	40535: 26438,
+	40536: 26462,
+	40537: 26464,
+	40538: 26457,
+	40539: 26467,
+	40540: 26468,
+	40541: 26505,
+	40542: 26480,
+	40543: 26537,
+	40544: 26492,
+	40545: 26474,
+	40546: 26508,
+	40547: 26507,
+	40548: 26534,
+	40549: 26529,
+	40550: 26501,
+	40551: 26551,
+	40552: 26607,
+	40553: 26548,
+	40554: 26604,
+	40555: 26547,
+	40556: 26601,
+	40557: 26552,
+	40558: 26596,
+	40559: 26590,
+	40560: 26589,
+	40561: 26594,
+	40562: 26606,
+	40563: 26553,
+	40564: 26574,
+	40565: 26566,
+	40566: 26599,
+	40567: 27292,
+	40568: 26654,
+	40569: 26694,
+	40570: 26665,
+	40571: 26688,
+	40572: 26701,
+	40573: 26674,
+	40574: 26702,
+	40576: 26803,
+	40577: 26667,
+	40578: 26713,
+	40579: 26723,
+	40580: 26743,
+	40581: 26751,
+	40582: 26783,
+	40583: 26767,
+	40584: 26797,
+	40585: 26772,
+	40586: 26781,
+	40587: 26779,
+	40588: 26755,
+	40589: 27310,
+	40590: 26809,
+	40591: 26740,
+	40592: 26805,
+	40593: 26784,
+	40594: 26810,
+	40595: 26895,
+	40596: 26765,
+	40597: 26750,
+	40598: 26881,
+	40599: 26826,
+	40600: 26888,
+	40601: 26840,
+	40602: 26914,
+	40603: 26918,
+	40604: 26849,
+	40605: 26892,
+	40606: 26829,
+	40607: 26836,
+	40608: 26855,
+	40609: 26837,
+	40610: 26934,
+	40611: 26898,
+	40612: 26884,
+	40613: 26839,
+	40614: 26851,
+	40615: 26917,
+	40616: 26873,
+	40617: 26848,
+	40618: 26863,
+	40619: 26920,
+	40620: 26922,
+	40621: 26906,
+	40622: 26915,
+	40623: 26913,
+	40624: 26822,
+	40625: 27001,
+	40626: 26999,
+	40627: 26972,
+	40628: 27000,
+	40629: 26987,
+	40630: 26964,
+	40631: 27006,
+	40632: 26990,
+	40633: 26937,
+	40634: 26996,
+	40635: 26941,
+	40636: 26969,
+	40637: 26928,
+	40638: 26977,
+	40639: 26974,
+	40640: 26973,
+	40641: 27009,
+	40642: 26986,
+	40643: 27058,
+	40644: 27054,
+	40645: 27088,
+	40646: 27071,
+	40647: 27073,
+	40648: 27091,
+	40649: 27070,
+	40650: 27086,
+	40651: 23528,
+	40652: 27082,
+	40653: 27101,
+	40654: 27067,
+	40655: 27075,
+	40656: 27047,
+	40657: 27182,
+	40658: 27025,
+	40659: 27040,
+	40660: 27036,
+	40661: 27029,
+	40662: 27060,
+	40663: 27102,
+	40664: 27112,
+	40665: 27138,
+	40666: 27163,
+	40667: 27135,
+	40668: 27402,
+	40669: 27129,
+	40670: 27122,
+	40671: 27111,
+	40672: 27141,
+	40673: 27057,
+	40674: 27166,
+	40675: 27117,
+	40676: 27156,
+	40677: 27115,
+	40678: 27146,
+	40679: 27154,
+	40680: 27329,
+	40681: 27171,
+	40682: 27155,
+	40683: 27204,
+	40684: 27148,
+	40685: 27250,
+	40686: 27190,
+	40687: 27256,
+	40688: 27207,
+	40689: 27234,
+	40690: 27225,
+	40691: 27238,
+	40692: 27208,
+	40693: 27192,
+	40694: 27170,
+	40695: 27280,
+	40696: 27277,
+	40697: 27296,
+	40698: 27268,
+	40699: 27298,
+	40700: 27299,
+	40768: 27287,
+	40769: 34327,
+	40770: 27323,
+	40771: 27331,
+	40772: 27330,
+	40773: 27320,
+	40774: 27315,
+	40775: 27308,
+	40776: 27358,
+	40777: 27345,
+	40778: 27359,
+	40779: 27306,
+	40780: 27354,
+	40781: 27370,
+	40782: 27387,
+	40783: 27397,
+	40784: 34326,
+	40785: 27386,
+	40786: 27410,
+	40787: 27414,
+	40788: 39729,
+	40789: 27423,
+	40790: 27448,
+	40791: 27447,
+	40792: 30428,
+	40793: 27449,
+	40794: 39150,
+	40795: 27463,
+	40796: 27459,
+	40797: 27465,
+	40798: 27472,
+	40799: 27481,
+	40800: 27476,
+	40801: 27483,
+	40802: 27487,
+	40803: 27489,
+	40804: 27512,
+	40805: 27513,
+	40806: 27519,
+	40807: 27520,
+	40808: 27524,
+	40809: 27523,
+	40810: 27533,
+	40811: 27544,
+	40812: 27541,
+	40813: 27550,
+	40814: 27556,
+	40815: 27562,
+	40816: 27563,
+	40817: 27567,
+	40818: 27570,
+	40819: 27569,
+	40820: 27571,
+	40821: 27575,
+	40822: 27580,
+	40823: 27590,
+	40824: 27595,
+	40825: 27603,
+	40826: 27615,
+	40827: 27628,
+	40828: 27627,
+	40829: 27635,
+	40830: 27631,
+	40832: 40638,
+	40833: 27656,
+	40834: 27667,
+	40835: 27668,
+	40836: 27675,
+	40837: 27684,
+	40838: 27683,
+	40839: 27742,
+	40840: 27733,
+	40841: 27746,
+	40842: 27754,
+	40843: 27778,
+	40844: 27789,
+	40845: 27802,
+	40846: 27777,
+	40847: 27803,
+	40848: 27774,
+	40849: 27752,
+	40850: 27763,
+	40851: 27794,
+	40852: 27792,
+	40853: 27844,
+	40854: 27889,
+	40855: 27859,
+	40856: 27837,
+	40857: 27863,
+	40858: 27845,
+	40859: 27869,
+	40860: 27822,
+	40861: 27825,
+	40862: 27838,
+	40863: 27834,
+	40864: 27867,
+	40865: 27887,
+	40866: 27865,
+	40867: 27882,
+	40868: 27935,
+	40869: 34893,
+	40870: 27958,
+	40871: 27947,
+	40872: 27965,
+	40873: 27960,
+	40874: 27929,
+	40875: 27957,
+	40876: 27955,
+	40877: 27922,
+	40878: 27916,
+	40879: 28003,
+	40880: 28051,
+	40881: 28004,
+	40882: 27994,
+	40883: 28025,
+	40884: 27993,
+	40885: 28046,
+	40886: 28053,
+	40887: 28644,
+	40888: 28037,
+	40889: 28153,
+	40890: 28181,
+	40891: 28170,
+	40892: 28085,
+	40893: 28103,
+	40894: 28134,
+	40895: 28088,
+	40896: 28102,
+	40897: 28140,
+	40898: 28126,
+	40899: 28108,
+	40900: 28136,
+	40901: 28114,
+	40902: 28101,
+	40903: 28154,
+	40904: 28121,
+	40905: 28132,
+	40906: 28117,
+	40907: 28138,
+	40908: 28142,
+	40909: 28205,
+	40910: 28270,
+	40911: 28206,
+	40912: 28185,
+	40913: 28274,
+	40914: 28255,
+	40915: 28222,
+	40916: 28195,
+	40917: 28267,
+	40918: 28203,
+	40919: 28278,
+	40920: 28237,
+	40921: 28191,
+	40922: 28227,
+	40923: 28218,
+	40924: 28238,
+	40925: 28196,
+	40926: 28415,
+	40927: 28189,
+	40928: 28216,
+	40929: 28290,
+	40930: 28330,
+	40931: 28312,
+	40932: 28361,
+	40933: 28343,
+	40934: 28371,
+	40935: 28349,
+	40936: 28335,
+	40937: 28356,
+	40938: 28338,
+	40939: 28372,
+	40940: 28373,
+	40941: 28303,
+	40942: 28325,
+	40943: 28354,
+	40944: 28319,
+	40945: 28481,
+	40946: 28433,
+	40947: 28748,
+	40948: 28396,
+	40949: 28408,
+	40950: 28414,
+	40951: 28479,
+	40952: 28402,
+	40953: 28465,
+	40954: 28399,
+	40955: 28466,
+	40956: 28364,
+	57408: 28478,
+	57409: 28435,
+	57410: 28407,
+	57411: 28550,
+	57412: 28538,
+	57413: 28536,
+	57414: 28545,
+	57415: 28544,
+	57416: 28527,
+	57417: 28507,
+	57418: 28659,
+	57419: 28525,
+	57420: 28546,
+	57421: 28540,
+	57422: 28504,
+	57423: 28558,
+	57424: 28561,
+	57425: 28610,
+	57426: 28518,
+	57427: 28595,
+	57428: 28579,
+	57429: 28577,
+	57430: 28580,
+	57431: 28601,
+	57432: 28614,
+	57433: 28586,
+	57434: 28639,
+	57435: 28629,
+	57436: 28652,
+	57437: 28628,
+	57438: 28632,
+	57439: 28657,
+	57440: 28654,
+	57441: 28635,
+	57442: 28681,
+	57443: 28683,
+	57444: 28666,
+	57445: 28689,
+	57446: 28673,
+	57447: 28687,
+	57448: 28670,
+	57449: 28699,
+	57450: 28698,
+	57451: 28532,
+	57452: 28701,
+	57453: 28696,
+	57454: 28703,
+	57455: 28720,
+	57456: 28734,
+	57457: 28722,
+	57458: 28753,
+	57459: 28771,
+	57460: 28825,
+	57461: 28818,
+	57462: 28847,
+	57463: 28913,
+	57464: 28844,
+	57465: 28856,
+	57466: 28851,
+	57467: 28846,
+	57468: 28895,
+	57469: 28875,
+	57470: 28893,
+	57472: 28889,
+	57473: 28937,
+	57474: 28925,
+	57475: 28956,
+	57476: 28953,
+	57477: 29029,
+	57478: 29013,
+	57479: 29064,
+	57480: 29030,
+	57481: 29026,
+	57482: 29004,
+	57483: 29014,
+	57484: 29036,
+	57485: 29071,
+	57486: 29179,
+	57487: 29060,
+	57488: 29077,
+	57489: 29096,
+	57490: 29100,
+	57491: 29143,
+	57492: 29113,
+	57493: 29118,
+	57494: 29138,
+	57495: 29129,
+	57496: 29140,
+	57497: 29134,
+	57498: 29152,
+	57499: 29164,
+	57500: 29159,
+	57501: 29173,
+	57502: 29180,
+	57503: 29177,
+	57504: 29183,
+	57505: 29197,
+	57506: 29200,
+	57507: 29211,
+	57508: 29224,
+	57509: 29229,
+	57510: 29228,
+	57511: 29232,
+	57512: 29234,
+	57513: 29243,
+	57514: 29244,
+	57515: 29247,
+	57516: 29248,
+	57517: 29254,
+	57518: 29259,
+	57519: 29272,
+	57520: 29300,
+	57521: 29310,
+	57522: 29314,
+	57523: 29313,
+	57524: 29319,
+	57525: 29330,
+	57526: 29334,
+	57527: 29346,
+	57528: 29351,
+	57529: 29369,
+	57530: 29362,
+	57531: 29379,
+	57532: 29382,
+	57533: 29380,
+	57534: 29390,
+	57535: 29394,
+	57536: 29410,
+	57537: 29408,
+	57538: 29409,
+	57539: 29433,
+	57540: 29431,
+	57541: 20495,
+	57542: 29463,
+	57543: 29450,
+	57544: 29468,
+	57545: 29462,
+	57546: 29469,
+	57547: 29492,
+	57548: 29487,
+	57549: 29481,
+	57550: 29477,
+	57551: 29502,
+	57552: 29518,
+	57553: 29519,
+	57554: 40664,
+	57555: 29527,
+	57556: 29546,
+	57557: 29544,
+	57558: 29552,
+	57559: 29560,
+	57560: 29557,
+	57561: 29563,
+	57562: 29562,
+	57563: 29640,
+	57564: 29619,
+	57565: 29646,
+	57566: 29627,
+	57567: 29632,
+	57568: 29669,
+	57569: 29678,
+	57570: 29662,
+	57571: 29858,
+	57572: 29701,
+	57573: 29807,
+	57574: 29733,
+	57575: 29688,
+	57576: 29746,
+	57577: 29754,
+	57578: 29781,
+	57579: 29759,
+	57580: 29791,
+	57581: 29785,
+	57582: 29761,
+	57583: 29788,
+	57584: 29801,
+	57585: 29808,
+	57586: 29795,
+	57587: 29802,
+	57588: 29814,
+	57589: 29822,
+	57590: 29835,
+	57591: 29854,
+	57592: 29863,
+	57593: 29898,
+	57594: 29903,
+	57595: 29908,
+	57596: 29681,
+	57664: 29920,
+	57665: 29923,
+	57666: 29927,
+	57667: 29929,
+	57668: 29934,
+	57669: 29938,
+	57670: 29936,
+	57671: 29937,
+	57672: 29944,
+	57673: 29943,
+	57674: 29956,
+	57675: 29955,
+	57676: 29957,
+	57677: 29964,
+	57678: 29966,
+	57679: 29965,
+	57680: 29973,
+	57681: 29971,
+	57682: 29982,
+	57683: 29990,
+	57684: 29996,
+	57685: 30012,
+	57686: 30020,
+	57687: 30029,
+	57688: 30026,
+	57689: 30025,
+	57690: 30043,
+	57691: 30022,
+	57692: 30042,
+	57693: 30057,
+	57694: 30052,
+	57695: 30055,
+	57696: 30059,
+	57697: 30061,
+	57698: 30072,
+	57699: 30070,
+	57700: 30086,
+	57701: 30087,
+	57702: 30068,
+	57703: 30090,
+	57704: 30089,
+	57705: 30082,
+	57706: 30100,
+	57707: 30106,
+	57708: 30109,
+	57709: 30117,
+	57710: 30115,
+	57711: 30146,
+	57712: 30131,
+	57713: 30147,
+	57714: 30133,
+	57715: 30141,
+	57716: 30136,
+	57717: 30140,
+	57718: 30129,
+	57719: 30157,
+	57720: 30154,
+	57721: 30162,
+	57722: 30169,
+	57723: 30179,
+	57724: 30174,
+	57725: 30206,
+	57726: 30207,
+	57728: 30204,
+	57729: 30209,
+	57730: 30192,
+	57731: 30202,
+	57732: 30194,
+	57733: 30195,
+	57734: 30219,
+	57735: 30221,
+	57736: 30217,
+	57737: 30239,
+	57738: 30247,
+	57739: 30240,
+	57740: 30241,
+	57741: 30242,
+	57742: 30244,
+	57743: 30260,
+	57744: 30256,
+	57745: 30267,
+	57746: 30279,
+	57747: 30280,
+	57748: 30278,
+	57749: 30300,
+	57750: 30296,
+	57751: 30305,
+	57752: 30306,
+	57753: 30312,
+	57754: 30313,
+	57755: 30314,
+	57756: 30311,
+	57757: 30316,
+	57758: 30320,
+	57759: 30322,
+	57760: 30326,
+	57761: 30328,
+	57762: 30332,
+	57763: 30336,
+	57764: 30339,
+	57765: 30344,
+	57766: 30347,
+	57767: 30350,
+	57768: 30358,
+	57769: 30355,
+	57770: 30361,
+	57771: 30362,
+	57772: 30384,
+	57773: 30388,
+	57774: 30392,
+	57775: 30393,
+	57776: 30394,
+	57777: 30402,
+	57778: 30413,
+	57779: 30422,
+	57780: 30418,
+	57781: 30430,
+	57782: 30433,
+	57783: 30437,
+	57784: 30439,
+	57785: 30442,
+	57786: 34351,
+	57787: 30459,
+	57788: 30472,
+	57789: 30471,
+	57790: 30468,
+	57791: 30505,
+	57792: 30500,
+	57793: 30494,
+	57794: 30501,
+	57795: 30502,
+	57796: 30491,
+	57797: 30519,
+	57798: 30520,
+	57799: 30535,
+	57800: 30554,
+	57801: 30568,
+	57802: 30571,
+	57803: 30555,
+	57804: 30565,
+	57805: 30591,
+	57806: 30590,
+	57807: 30585,
+	57808: 30606,
+	57809: 30603,
+	57810: 30609,
+	57811: 30624,
+	57812: 30622,
+	57813: 30640,
+	57814: 30646,
+	57815: 30649,
+	57816: 30655,
+	57817: 30652,
+	57818: 30653,
+	57819: 30651,
+	57820: 30663,
+	57821: 30669,
+	57822: 30679,
+	57823: 30682,
+	57824: 30684,
+	57825: 30691,
+	57826: 30702,
+	57827: 30716,
+	57828: 30732,
+	57829: 30738,
+	57830: 31014,
+	57831: 30752,
+	57832: 31018,
+	57833: 30789,
+	57834: 30862,
+	57835: 30836,
+	57836: 30854,
+	57837: 30844,
+	57838: 30874,
+	57839: 30860,
+	57840: 30883,
+	57841: 30901,
+	57842: 30890,
+	57843: 30895,
+	57844: 30929,
+	57845: 30918,
+	57846: 30923,
+	57847: 30932,
+	57848: 30910,
+	57849: 30908,
+	57850: 30917,
+	57851: 30922,
+	57852: 30956,
+	57920: 30951,
+	57921: 30938,
+	57922: 30973,
+	57923: 30964,
+	57924: 30983,
+	57925: 30994,
+	57926: 30993,
+	57927: 31001,
+	57928: 31020,
+	57929: 31019,
+	57930: 31040,
+	57931: 31072,
+	57932: 31063,
+	57933: 31071,
+	57934: 31066,
+	57935: 31061,
+	57936: 31059,
+	57937: 31098,
+	57938: 31103,
+	57939: 31114,
+	57940: 31133,
+	57941: 31143,
+	57942: 40779,
+	57943: 31146,
+	57944: 31150,
+	57945: 31155,
+	57946: 31161,
+	57947: 31162,
+	57948: 31177,
+	57949: 31189,
+	57950: 31207,
+	57951: 31212,
+	57952: 31201,
+	57953: 31203,
+	57954: 31240,
+	57955: 31245,
+	57956: 31256,
+	57957: 31257,
+	57958: 31264,
+	57959: 31263,
+	57960: 31104,
+	57961: 31281,
+	57962: 31291,
+	57963: 31294,
+	57964: 31287,
+	57965: 31299,
+	57966: 31319,
+	57967: 31305,
+	57968: 31329,
+	57969: 31330,
+	57970: 31337,
+	57971: 40861,
+	57972: 31344,
+	57973: 31353,
+	57974: 31357,
+	57975: 31368,
+	57976: 31383,
+	57977: 31381,
+	57978: 31384,
+	57979: 31382,
+	57980: 31401,
+	57981: 31432,
+	57982: 31408,
+	57984: 31414,
+	57985: 31429,
+	57986: 31428,
+	57987: 31423,
+	57988: 36995,
+	57989: 31431,
+	57990: 31434,
+	57991: 31437,
+	57992: 31439,
+	57993: 31445,
+	57994: 31443,
+	57995: 31449,
+	57996: 31450,
+	57997: 31453,
+	57998: 31457,
+	57999: 31458,
+	58000: 31462,
+	58001: 31469,
+	58002: 31472,
+	58003: 31490,
+	58004: 31503,
+	58005: 31498,
+	58006: 31494,
+	58007: 31539,
+	58008: 31512,
+	58009: 31513,
+	58010: 31518,
+	58011: 31541,
+	58012: 31528,
+	58013: 31542,
+	58014: 31568,
+	58015: 31610,
+	58016: 31492,
+	58017: 31565,
+	58018: 31499,
+	58019: 31564,
+	58020: 31557,
+	58021: 31605,
+	58022: 31589,
+	58023: 31604,
+	58024: 31591,
+	58025: 31600,
+	58026: 31601,
+	58027: 31596,
+	58028: 31598,
+	58029: 31645,
+	58030: 31640,
+	58031: 31647,
+	58032: 31629,
+	58033: 31644,
+	58034: 31642,
+	58035: 31627,
+	58036: 31634,
+	58037: 31631,
+	58038: 31581,
+	58039: 31641,
+	58040: 31691,
+	58041: 31681,
+	58042: 31692,
+	58043: 31695,
+	58044: 31668,
+	58045: 31686,
+	58046: 31709,
+	58047: 31721,
+	58048: 31761,
+	58049: 31764,
+	58050: 31718,
+	58051: 31717,
+	58052: 31840,
+	58053: 31744,
+	58054: 31751,
+	58055: 31763,
+	58056: 31731,
+	58057: 31735,
+	58058: 31767,
+	58059: 31757,
+	58060: 31734,
+	58061: 31779,
+	58062: 31783,
+	58063: 31786,
+	58064: 31775,
+	58065: 31799,
+	58066: 31787,
+	58067: 31805,
+	58068: 31820,
+	58069: 31811,
+	58070: 31828,
+	58071: 31823,
+	58072: 31808,
+	58073: 31824,
+	58074: 31832,
+	58075: 31839,
+	58076: 31844,
+	58077: 31830,
+	58078: 31845,
+	58079: 31852,
+	58080: 31861,
+	58081: 31875,
+	58082: 31888,
+	58083: 31908,
+	58084: 31917,
+	58085: 31906,
+	58086: 31915,
+	58087: 31905,
+	58088: 31912,
+	58089: 31923,
+	58090: 31922,
+	58091: 31921,
+	58092: 31918,
+	58093: 31929,
+	58094: 31933,
+	58095: 31936,
+	58096: 31941,
+	58097: 31938,
+	58098: 31960,
+	58099: 31954,
+	58100: 31964,
+	58101: 31970,
+	58102: 39739,
+	58103: 31983,
+	58104: 31986,
+	58105: 31988,
+	58106: 31990,
+	58107: 31994,
+	58108: 32006,
+	58176: 32002,
+	58177: 32028,
+	58178: 32021,
+	58179: 32010,
+	58180: 32069,
+	58181: 32075,
+	58182: 32046,
+	58183: 32050,
+	58184: 32063,
+	58185: 32053,
+	58186: 32070,
+	58187: 32115,
+	58188: 32086,
+	58189: 32078,
+	58190: 32114,
+	58191: 32104,
+	58192: 32110,
+	58193: 32079,
+	58194: 32099,
+	58195: 32147,
+	58196: 32137,
+	58197: 32091,
+	58198: 32143,
+	58199: 32125,
+	58200: 32155,
+	58201: 32186,
+	58202: 32174,
+	58203: 32163,
+	58204: 32181,
+	58205: 32199,
+	58206: 32189,
+	58207: 32171,
+	58208: 32317,
+	58209: 32162,
+	58210: 32175,
+	58211: 32220,
+	58212: 32184,
+	58213: 32159,
+	58214: 32176,
+	58215: 32216,
+	58216: 32221,
+	58217: 32228,
+	58218: 32222,
+	58219: 32251,
+	58220: 32242,
+	58221: 32225,
+	58222: 32261,
+	58223: 32266,
+	58224: 32291,
+	58225: 32289,
+	58226: 32274,
+	58227: 32305,
+	58228: 32287,
+	58229: 32265,
+	58230: 32267,
+	58231: 32290,
+	58232: 32326,
+	58233: 32358,
+	58234: 32315,
+	58235: 32309,
+	58236: 32313,
+	58237: 32323,
+	58238: 32311,
+	58240: 32306,
+	58241: 32314,
+	58242: 32359,
+	58243: 32349,
+	58244: 32342,
+	58245: 32350,
+	58246: 32345,
+	58247: 32346,
+	58248: 32377,
+	58249: 32362,
+	58250: 32361,
+	58251: 32380,
+	58252: 32379,
+	58253: 32387,
+	58254: 32213,
+	58255: 32381,
+	58256: 36782,
+	58257: 32383,
+	58258: 32392,
+	58259: 32393,
+	58260: 32396,
+	58261: 32402,
+	58262: 32400,
+	58263: 32403,
+	58264: 32404,
+	58265: 32406,
+	58266: 32398,
+	58267: 32411,
+	58268: 32412,
+	58269: 32568,
+	58270: 32570,
+	58271: 32581,
+	58272: 32588,
+	58273: 32589,
+	58274: 32590,
+	58275: 32592,
+	58276: 32593,
+	58277: 32597,
+	58278: 32596,
+	58279: 32600,
+	58280: 32607,
+	58281: 32608,
+	58282: 32616,
+	58283: 32617,
+	58284: 32615,
+	58285: 32632,
+	58286: 32642,
+	58287: 32646,
+	58288: 32643,
+	58289: 32648,
+	58290: 32647,
+	58291: 32652,
+	58292: 32660,
+	58293: 32670,
+	58294: 32669,
+	58295: 32666,
+	58296: 32675,
+	58297: 32687,
+	58298: 32690,
+	58299: 32697,
+	58300: 32686,
+	58301: 32694,
+	58302: 32696,
+	58303: 35697,
+	58304: 32709,
+	58305: 32710,
+	58306: 32714,
+	58307: 32725,
+	58308: 32724,
+	58309: 32737,
+	58310: 32742,
+	58311: 32745,
+	58312: 32755,
+	58313: 32761,
+	58314: 39132,
+	58315: 32774,
+	58316: 32772,
+	58317: 32779,
+	58318: 32786,
+	58319: 32792,
+	58320: 32793,
+	58321: 32796,
+	58322: 32801,
+	58323: 32808,
+	58324: 32831,
+	58325: 32827,
+	58326: 32842,
+	58327: 32838,
+	58328: 32850,
+	58329: 32856,
+	58330: 32858,
+	58331: 32863,
+	58332: 32866,
+	58333: 32872,
+	58334: 32883,
+	58335: 32882,
+	58336: 32880,
+	58337: 32886,
+	58338: 32889,
+	58339: 32893,
+	58340: 32895,
+	58341: 32900,
+	58342: 32902,
+	58343: 32901,
+	58344: 32923,
+	58345: 32915,
+	58346: 32922,
+	58347: 32941,
+	58348: 20880,
+	58349: 32940,
+	58350: 32987,
+	58351: 32997,
+	58352: 32985,
+	58353: 32989,
+	58354: 32964,
+	58355: 32986,
+	58356: 32982,
+	58357: 33033,
+	58358: 33007,
+	58359: 33009,
+	58360: 33051,
+	58361: 33065,
+	58362: 33059,
+	58363: 33071,
+	58364: 33099,
+	58432: 38539,
+	58433: 33094,
+	58434: 33086,
+	58435: 33107,
+	58436: 33105,
+	58437: 33020,
+	58438: 33137,
+	58439: 33134,
+	58440: 33125,
+	58441: 33126,
+	58442: 33140,
+	58443: 33155,
+	58444: 33160,
+	58445: 33162,
+	58446: 33152,
+	58447: 33154,
+	58448: 33184,
+	58449: 33173,
+	58450: 33188,
+	58451: 33187,
+	58452: 33119,
+	58453: 33171,
+	58454: 33193,
+	58455: 33200,
+	58456: 33205,
+	58457: 33214,
+	58458: 33208,
+	58459: 33213,
+	58460: 33216,
+	58461: 33218,
+	58462: 33210,
+	58463: 33225,
+	58464: 33229,
+	58465: 33233,
+	58466: 33241,
+	58467: 33240,
+	58468: 33224,
+	58469: 33242,
+	58470: 33247,
+	58471: 33248,
+	58472: 33255,
+	58473: 33274,
+	58474: 33275,
+	58475: 33278,
+	58476: 33281,
+	58477: 33282,
+	58478: 33285,
+	58479: 33287,
+	58480: 33290,
+	58481: 33293,
+	58482: 33296,
+	58483: 33302,
+	58484: 33321,
+	58485: 33323,
+	58486: 33336,
+	58487: 33331,
+	58488: 33344,
+	58489: 33369,
+	58490: 33368,
+	58491: 33373,
+	58492: 33370,
+	58493: 33375,
+	58494: 33380,
+	58496: 33378,
+	58497: 33384,
+	58498: 33386,
+	58499: 33387,
+	58500: 33326,
+	58501: 33393,
+	58502: 33399,
+	58503: 33400,
+	58504: 33406,
+	58505: 33421,
+	58506: 33426,
+	58507: 33451,
+	58508: 33439,
+	58509: 33467,
+	58510: 33452,
+	58511: 33505,
+	58512: 33507,
+	58513: 33503,
+	58514: 33490,
+	58515: 33524,
+	58516: 33523,
+	58517: 33530,
+	58518: 33683,
+	58519: 33539,
+	58520: 33531,
+	58521: 33529,
+	58522: 33502,
+	58523: 33542,
+	58524: 33500,
+	58525: 33545,
+	58526: 33497,
+	58527: 33589,
+	58528: 33588,
+	58529: 33558,
+	58530: 33586,
+	58531: 33585,
+	58532: 33600,
+	58533: 33593,
+	58534: 33616,
+	58535: 33605,
+	58536: 33583,
+	58537: 33579,
+	58538: 33559,
+	58539: 33560,
+	58540: 33669,
+	58541: 33690,
+	58542: 33706,
+	58543: 33695,
+	58544: 33698,
+	58545: 33686,
+	58546: 33571,
+	58547: 33678,
+	58548: 33671,
+	58549: 33674,
+	58550: 33660,
+	58551: 33717,
+	58552: 33651,
+	58553: 33653,
+	58554: 33696,
+	58555: 33673,
+	58556: 33704,
+	58557: 33780,
+	58558: 33811,
+	58559: 33771,
+	58560: 33742,
+	58561: 33789,
+	58562: 33795,
+	58563: 33752,
+	58564: 33803,
+	58565: 33729,
+	58566: 33783,
+	58567: 33799,
+	58568: 33760,
+	58569: 33778,
+	58570: 33805,
+	58571: 33826,
+	58572: 33824,
+	58573: 33725,
+	58574: 33848,
+	58575: 34054,
+	58576: 33787,
+	58577: 33901,
+	58578: 33834,
+	58579: 33852,
+	58580: 34138,
+	58581: 33924,
+	58582: 33911,
+	58583: 33899,
+	58584: 33965,
+	58585: 33902,
+	58586: 33922,
+	58587: 33897,
+	58588: 33862,
+	58589: 33836,
+	58590: 33903,
+	58591: 33913,
+	58592: 33845,
+	58593: 33994,
+	58594: 33890,
+	58595: 33977,
+	58596: 33983,
+	58597: 33951,
+	58598: 34009,
+	58599: 33997,
+	58600: 33979,
+	58601: 34010,
+	58602: 34000,
+	58603: 33985,
+	58604: 33990,
+	58605: 34006,
+	58606: 33953,
+	58607: 34081,
+	58608: 34047,
+	58609: 34036,
+	58610: 34071,
+	58611: 34072,
+	58612: 34092,
+	58613: 34079,
+	58614: 34069,
+	58615: 34068,
+	58616: 34044,
+	58617: 34112,
+	58618: 34147,
+	58619: 34136,
+	58620: 34120,
+	58688: 34113,
+	58689: 34306,
+	58690: 34123,
+	58691: 34133,
+	58692: 34176,
+	58693: 34212,
+	58694: 34184,
+	58695: 34193,
+	58696: 34186,
+	58697: 34216,
+	58698: 34157,
+	58699: 34196,
+	58700: 34203,
+	58701: 34282,
+	58702: 34183,
+	58703: 34204,
+	58704: 34167,
+	58705: 34174,
+	58706: 34192,
+	58707: 34249,
+	58708: 34234,
+	58709: 34255,
+	58710: 34233,
+	58711: 34256,
+	58712: 34261,
+	58713: 34269,
+	58714: 34277,
+	58715: 34268,
+	58716: 34297,
+	58717: 34314,
+	58718: 34323,
+	58719: 34315,
+	58720: 34302,
+	58721: 34298,
+	58722: 34310,
+	58723: 34338,
+	58724: 34330,
+	58725: 34352,
+	58726: 34367,
+	58727: 34381,
+	58728: 20053,
+	58729: 34388,
+	58730: 34399,
+	58731: 34407,
+	58732: 34417,
+	58733: 34451,
+	58734: 34467,
+	58735: 34473,
+	58736: 34474,
+	58737: 34443,
+	58738: 34444,
+	58739: 34486,
+	58740: 34479,
+	58741: 34500,
+	58742: 34502,
+	58743: 34480,
+	58744: 34505,
+	58745: 34851,
+	58746: 34475,
+	58747: 34516,
+	58748: 34526,
+	58749: 34537,
+	58750: 34540,
+	58752: 34527,
+	58753: 34523,
+	58754: 34543,
+	58755: 34578,
+	58756: 34566,
+	58757: 34568,
+	58758: 34560,
+	58759: 34563,
+	58760: 34555,
+	58761: 34577,
+	58762: 34569,
+	58763: 34573,
+	58764: 34553,
+	58765: 34570,
+	58766: 34612,
+	58767: 34623,
+	58768: 34615,
+	58769: 34619,
+	58770: 34597,
+	58771: 34601,
+	58772: 34586,
+	58773: 34656,
+	58774: 34655,
+	58775: 34680,
+	58776: 34636,
+	58777: 34638,
+	58778: 34676,
+	58779: 34647,
+	58780: 34664,
+	58781: 34670,
+	58782: 34649,
+	58783: 34643,
+	58784: 34659,
+	58785: 34666,
+	58786: 34821,
+	58787: 34722,
+	58788: 34719,
+	58789: 34690,
+	58790: 34735,
+	58791: 34763,
+	58792: 34749,
+	58793: 34752,
+	58794: 34768,
+	58795: 38614,
+	58796: 34731,
+	58797: 34756,
+	58798: 34739,
+	58799: 34759,
+	58800: 34758,
+	58801: 34747,
+	58802: 34799,
+	58803: 34802,
+	58804: 34784,
+	58805: 34831,
+	58806: 34829,
+	58807: 34814,
+	58808: 34806,
+	58809: 34807,
+	58810: 34830,
+	58811: 34770,
+	58812: 34833,
+	58813: 34838,
+	58814: 34837,
+	58815: 34850,
+	58816: 34849,
+	58817: 34865,
+	58818: 34870,
+	58819: 34873,
+	58820: 34855,
+	58821: 34875,
+	58822: 34884,
+	58823: 34882,
+	58824: 34898,
+	58825: 34905,
+	58826: 34910,
+	58827: 34914,
+	58828: 34923,
+	58829: 34945,
+	58830: 34942,
+	58831: 34974,
+	58832: 34933,
+	58833: 34941,
+	58834: 34997,
+	58835: 34930,
+	58836: 34946,
+	58837: 34967,
+	58838: 34962,
+	58839: 34990,
+	58840: 34969,
+	58841: 34978,
+	58842: 34957,
+	58843: 34980,
+	58844: 34992,
+	58845: 35007,
+	58846: 34993,
+	58847: 35011,
+	58848: 35012,
+	58849: 35028,
+	58850: 35032,
+	58851: 35033,
+	58852: 35037,
+	58853: 35065,
+	58854: 35074,
+	58855: 35068,
+	58856: 35060,
+	58857: 35048,
+	58858: 35058,
+	58859: 35076,
+	58860: 35084,
+	58861: 35082,
+	58862: 35091,
+	58863: 35139,
+	58864: 35102,
+	58865: 35109,
+	58866: 35114,
+	58867: 35115,
+	58868: 35137,
+	58869: 35140,
+	58870: 35131,
+	58871: 35126,
+	58872: 35128,
+	58873: 35148,
+	58874: 35101,
+	58875: 35168,
+	58876: 35166,
+	58944: 35174,
+	58945: 35172,
+	58946: 35181,
+	58947: 35178,
+	58948: 35183,
+	58949: 35188,
+	58950: 35191,
+	58951: 35198,
+	58952: 35203,
+	58953: 35208,
+	58954: 35210,
+	58955: 35219,
+	58956: 35224,
+	58957: 35233,
+	58958: 35241,
+	58959: 35238,
+	58960: 35244,
+	58961: 35247,
+	58962: 35250,
+	58963: 35258,
+	58964: 35261,
+	58965: 35263,
+	58966: 35264,
+	58967: 35290,
+	58968: 35292,
+	58969: 35293,
+	58970: 35303,
+	58971: 35316,
+	58972: 35320,
+	58973: 35331,
+	58974: 35350,
+	58975: 35344,
+	58976: 35340,
+	58977: 35355,
+	58978: 35357,
+	58979: 35365,
+	58980: 35382,
+	58981: 35393,
+	58982: 35419,
+	58983: 35410,
+	58984: 35398,
+	58985: 35400,
+	58986: 35452,
+	58987: 35437,
+	58988: 35436,
+	58989: 35426,
+	58990: 35461,
+	58991: 35458,
+	58992: 35460,
+	58993: 35496,
+	58994: 35489,
+	58995: 35473,
+	58996: 35493,
+	58997: 35494,
+	58998: 35482,
+	58999: 35491,
+	59000: 35524,
+	59001: 35533,
+	59002: 35522,
+	59003: 35546,
+	59004: 35563,
+	59005: 35571,
+	59006: 35559,
+	59008: 35556,
+	59009: 35569,
+	59010: 35604,
+	59011: 35552,
+	59012: 35554,
+	59013: 35575,
+	59014: 35550,
+	59015: 35547,
+	59016: 35596,
+	59017: 35591,
+	59018: 35610,
+	59019: 35553,
+	59020: 35606,
+	59021: 35600,
+	59022: 35607,
+	59023: 35616,
+	59024: 35635,
+	59025: 38827,
+	59026: 35622,
+	59027: 35627,
+	59028: 35646,
+	59029: 35624,
+	59030: 35649,
+	59031: 35660,
+	59032: 35663,
+	59033: 35662,
+	59034: 35657,
+	59035: 35670,
+	59036: 35675,
+	59037: 35674,
+	59038: 35691,
+	59039: 35679,
+	59040: 35692,
+	59041: 35695,
+	59042: 35700,
+	59043: 35709,
+	59044: 35712,
+	59045: 35724,
+	59046: 35726,
+	59047: 35730,
+	59048: 35731,
+	59049: 35734,
+	59050: 35737,
+	59051: 35738,
+	59052: 35898,
+	59053: 35905,
+	59054: 35903,
+	59055: 35912,
+	59056: 35916,
+	59057: 35918,
+	59058: 35920,
+	59059: 35925,
+	59060: 35938,
+	59061: 35948,
+	59062: 35960,
+	59063: 35962,
+	59064: 35970,
+	59065: 35977,
+	59066: 35973,
+	59067: 35978,
+	59068: 35981,
+	59069: 35982,
+	59070: 35988,
+	59071: 35964,
+	59072: 35992,
+	59073: 25117,
+	59074: 36013,
+	59075: 36010,
+	59076: 36029,
+	59077: 36018,
+	59078: 36019,
+	59079: 36014,
+	59080: 36022,
+	59081: 36040,
+	59082: 36033,
+	59083: 36068,
+	59084: 36067,
+	59085: 36058,
+	59086: 36093,
+	59087: 36090,
+	59088: 36091,
+	59089: 36100,
+	59090: 36101,
+	59091: 36106,
+	59092: 36103,
+	59093: 36111,
+	59094: 36109,
+	59095: 36112,
+	59096: 40782,
+	59097: 36115,
+	59098: 36045,
+	59099: 36116,
+	59100: 36118,
+	59101: 36199,
+	59102: 36205,
+	59103: 36209,
+	59104: 36211,
+	59105: 36225,
+	59106: 36249,
+	59107: 36290,
+	59108: 36286,
+	59109: 36282,
+	59110: 36303,
+	59111: 36314,
+	59112: 36310,
+	59113: 36300,
+	59114: 36315,
+	59115: 36299,
+	59116: 36330,
+	59117: 36331,
+	59118: 36319,
+	59119: 36323,
+	59120: 36348,
+	59121: 36360,
+	59122: 36361,
+	59123: 36351,
+	59124: 36381,
+	59125: 36382,
+	59126: 36368,
+	59127: 36383,
+	59128: 36418,
+	59129: 36405,
+	59130: 36400,
+	59131: 36404,
+	59132: 36426,
+	59200: 36423,
+	59201: 36425,
+	59202: 36428,
+	59203: 36432,
+	59204: 36424,
+	59205: 36441,
+	59206: 36452,
+	59207: 36448,
+	59208: 36394,
+	59209: 36451,
+	59210: 36437,
+	59211: 36470,
+	59212: 36466,
+	59213: 36476,
+	59214: 36481,
+	59215: 36487,
+	59216: 36485,
+	59217: 36484,
+	59218: 36491,
+	59219: 36490,
+	59220: 36499,
+	59221: 36497,
+	59222: 36500,
+	59223: 36505,
+	59224: 36522,
+	59225: 36513,
+	59226: 36524,
+	59227: 36528,
+	59228: 36550,
+	59229: 36529,
+	59230: 36542,
+	59231: 36549,
+	59232: 36552,
+	59233: 36555,
+	59234: 36571,
+	59235: 36579,
+	59236: 36604,
+	59237: 36603,
+	59238: 36587,
+	59239: 36606,
+	59240: 36618,
+	59241: 36613,
+	59242: 36629,
+	59243: 36626,
+	59244: 36633,
+	59245: 36627,
+	59246: 36636,
+	59247: 36639,
+	59248: 36635,
+	59249: 36620,
+	59250: 36646,
+	59251: 36659,
+	59252: 36667,
+	59253: 36665,
+	59254: 36677,
+	59255: 36674,
+	59256: 36670,
+	59257: 36684,
+	59258: 36681,
+	59259: 36678,
+	59260: 36686,
+	59261: 36695,
+	59262: 36700,
+	59264: 36706,
+	59265: 36707,
+	59266: 36708,
+	59267: 36764,
+	59268: 36767,
+	59269: 36771,
+	59270: 36781,
+	59271: 36783,
+	59272: 36791,
+	59273: 36826,
+	59274: 36837,
+	59275: 36834,
+	59276: 36842,
+	59277: 36847,
+	59278: 36999,
+	59279: 36852,
+	59280: 36869,
+	59281: 36857,
+	59282: 36858,
+	59283: 36881,
+	59284: 36885,
+	59285: 36897,
+	59286: 36877,
+	59287: 36894,
+	59288: 36886,
+	59289: 36875,
+	59290: 36903,
+	59291: 36918,
+	59292: 36917,
+	59293: 36921,
+	59294: 36856,
+	59295: 36943,
+	59296: 36944,
+	59297: 36945,
+	59298: 36946,
+	59299: 36878,
+	59300: 36937,
+	59301: 36926,
+	59302: 36950,
+	59303: 36952,
+	59304: 36958,
+	59305: 36968,
+	59306: 36975,
+	59307: 36982,
+	59308: 38568,
+	59309: 36978,
+	59310: 36994,
+	59311: 36989,
+	59312: 36993,
+	59313: 36992,
+	59314: 37002,
+	59315: 37001,
+	59316: 37007,
+	59317: 37032,
+	59318: 37039,
+	59319: 37041,
+	59320: 37045,
+	59321: 37090,
+	59322: 37092,
+	59323: 25160,
+	59324: 37083,
+	59325: 37122,
+	59326: 37138,
+	59327: 37145,
+	59328: 37170,
+	59329: 37168,
+	59330: 37194,
+	59331: 37206,
+	59332: 37208,
+	59333: 37219,
+	59334: 37221,
+	59335: 37225,
+	59336: 37235,
+	59337: 37234,
+	59338: 37259,
+	59339: 37257,
+	59340: 37250,
+	59341: 37282,
+	59342: 37291,
+	59343: 37295,
+	59344: 37290,
+	59345: 37301,
+	59346: 37300,
+	59347: 37306,
+	59348: 37312,
+	59349: 37313,
+	59350: 37321,
+	59351: 37323,
+	59352: 37328,
+	59353: 37334,
+	59354: 37343,
+	59355: 37345,
+	59356: 37339,
+	59357: 37372,
+	59358: 37365,
+	59359: 37366,
+	59360: 37406,
+	59361: 37375,
+	59362: 37396,
+	59363: 37420,
+	59364: 37397,
+	59365: 37393,
+	59366: 37470,
+	59367: 37463,
+	59368: 37445,
+	59369: 37449,
+	59370: 37476,
+	59371: 37448,
+	59372: 37525,
+	59373: 37439,
+	59374: 37451,
+	59375: 37456,
+	59376: 37532,
+	59377: 37526,
+	59378: 37523,
+	59379: 37531,
+	59380: 37466,
+	59381: 37583,
+	59382: 37561,
+	59383: 37559,
+	59384: 37609,
+	59385: 37647,
+	59386: 37626,
+	59387: 37700,
+	59388: 37678,
+	59456: 37657,
+	59457: 37666,
+	59458: 37658,
+	59459: 37667,
+	59460: 37690,
+	59461: 37685,
+	59462: 37691,
+	59463: 37724,
+	59464: 37728,
+	59465: 37756,
+	59466: 37742,
+	59467: 37718,
+	59468: 37808,
+	59469: 37804,
+	59470: 37805,
+	59471: 37780,
+	59472: 37817,
+	59473: 37846,
+	59474: 37847,
+	59475: 37864,
+	59476: 37861,
+	59477: 37848,
+	59478: 37827,
+	59479: 37853,
+	59480: 37840,
+	59481: 37832,
+	59482: 37860,
+	59483: 37914,
+	59484: 37908,
+	59485: 37907,
+	59486: 37891,
+	59487: 37895,
+	59488: 37904,
+	59489: 37942,
+	59490: 37931,
+	59491: 37941,
+	59492: 37921,
+	59493: 37946,
+	59494: 37953,
+	59495: 37970,
+	59496: 37956,
+	59497: 37979,
+	59498: 37984,
+	59499: 37986,
+	59500: 37982,
+	59501: 37994,
+	59502: 37417,
+	59503: 38000,
+	59504: 38005,
+	59505: 38007,
+	59506: 38013,
+	59507: 37978,
+	59508: 38012,
+	59509: 38014,
+	59510: 38017,
+	59511: 38015,
+	59512: 38274,
+	59513: 38279,
+	59514: 38282,
+	59515: 38292,
+	59516: 38294,
+	59517: 38296,
+	59518: 38297,
+	59520: 38304,
+	59521: 38312,
+	59522: 38311,
+	59523: 38317,
+	59524: 38332,
+	59525: 38331,
+	59526: 38329,
+	59527: 38334,
+	59528: 38346,
+	59529: 28662,
+	59530: 38339,
+	59531: 38349,
+	59532: 38348,
+	59533: 38357,
+	59534: 38356,
+	59535: 38358,
+	59536: 38364,
+	59537: 38369,
+	59538: 38373,
+	59539: 38370,
+	59540: 38433,
+	59541: 38440,
+	59542: 38446,
+	59543: 38447,
+	59544: 38466,
+	59545: 38476,
+	59546: 38479,
+	59547: 38475,
+	59548: 38519,
+	59549: 38492,
+	59550: 38494,
+	59551: 38493,
+	59552: 38495,
+	59553: 38502,
+	59554: 38514,
+	59555: 38508,
+	59556: 38541,
+	59557: 38552,
+	59558: 38549,
+	59559: 38551,
+	59560: 38570,
+	59561: 38567,
+	59562: 38577,
+	59563: 38578,
+	59564: 38576,
+	59565: 38580,
+	59566: 38582,
+	59567: 38584,
+	59568: 38585,
+	59569: 38606,
+	59570: 38603,
+	59571: 38601,
+	59572: 38605,
+	59573: 35149,
+	59574: 38620,
+	59575: 38669,
+	59576: 38613,
+	59577: 38649,
+	59578: 38660,
+	59579: 38662,
+	59580: 38664,
+	59581: 38675,
+	59582: 38670,
+	59583: 38673,
+	59584: 38671,
+	59585: 38678,
+	59586: 38681,
+	59587: 38692,
+	59588: 38698,
+	59589: 38704,
+	59590: 38713,
+	59591: 38717,
+	59592: 38718,
+	59593: 38724,
+	59594: 38726,
+	59595: 38728,
+	59596: 38722,
+	59597: 38729,
+	59598: 38748,
+	59599: 38752,
+	59600: 38756,
+	59601: 38758,
+	59602: 38760,
+	59603: 21202,
+	59604: 38763,
+	59605: 38769,
+	59606: 38777,
+	59607: 38789,
+	59608: 38780,
+	59609: 38785,
+	59610: 38778,
+	59611: 38790,
+	59612: 38795,
+	59613: 38799,
+	59614: 38800,
+	59615: 38812,
+	59616: 38824,
+	59617: 38822,
+	59618: 38819,
+	59619: 38835,
+	59620: 38836,
+	59621: 38851,
+	59622: 38854,
+	59623: 38856,
+	59624: 38859,
+	59625: 38876,
+	59626: 38893,
+	59627: 40783,
+	59628: 38898,
+	59629: 31455,
+	59630: 38902,
+	59631: 38901,
+	59632: 38927,
+	59633: 38924,
+	59634: 38968,
+	59635: 38948,
+	59636: 38945,
+	59637: 38967,
+	59638: 38973,
+	59639: 38982,
+	59640: 38991,
+	59641: 38987,
+	59642: 39019,
+	59643: 39023,
+	59644: 39024,
+	59712: 39025,
+	59713: 39028,
+	59714: 39027,
+	59715: 39082,
+	59716: 39087,
+	59717: 39089,
+	59718: 39094,
+	59719: 39108,
+	59720: 39107,
+	59721: 39110,
+	59722: 39145,
+	59723: 39147,
+	59724: 39171,
+	59725: 39177,
+	59726: 39186,
+	59727: 39188,
+	59728: 39192,
+	59729: 39201,
+	59730: 39197,
+	59731: 39198,
+	59732: 39204,
+	59733: 39200,
+	59734: 39212,
+	59735: 39214,
+	59736: 39229,
+	59737: 39230,
+	59738: 39234,
+	59739: 39241,
+	59740: 39237,
+	59741: 39248,
+	59742: 39243,
+	59743: 39249,
+	59744: 39250,
+	59745: 39244,
+	59746: 39253,
+	59747: 39319,
+	59748: 39320,
+	59749: 39333,
+	59750: 39341,
+	59751: 39342,
+	59752: 39356,
+	59753: 39391,
+	59754: 39387,
+	59755: 39389,
+	59756: 39384,
+	59757: 39377,
+	59758: 39405,
+	59759: 39406,
+	59760: 39409,
+	59761: 39410,
+	59762: 39419,
+	59763: 39416,
+	59764: 39425,
+	59765: 39439,
+	59766: 39429,
+	59767: 39394,
+	59768: 39449,
+	59769: 39467,
+	59770: 39479,
+	59771: 39493,
+	59772: 39490,
+	59773: 39488,
+	59774: 39491,
+	59776: 39486,
+	59777: 39509,
+	59778: 39501,
+	59779: 39515,
+	59780: 39511,
+	59781: 39519,
+	59782: 39522,
+	59783: 39525,
+	59784: 39524,
+	59785: 39529,
+	59786: 39531,
+	59787: 39530,
+	59788: 39597,
+	59789: 39600,
+	59790: 39612,
+	59791: 39616,
+	59792: 39631,
+	59793: 39633,
+	59794: 39635,
+	59795: 39636,
+	59796: 39646,
+	59797: 39647,
+	59798: 39650,
+	59799: 39651,
+	59800: 39654,
+	59801: 39663,
+	59802: 39659,
+	59803: 39662,
+	59804: 39668,
+	59805: 39665,
+	59806: 39671,
+	59807: 39675,
+	59808: 39686,
+	59809: 39704,
+	59810: 39706,
+	59811: 39711,
+	59812: 39714,
+	59813: 39715,
+	59814: 39717,
+	59815: 39719,
+	59816: 39720,
+	59817: 39721,
+	59818: 39722,
+	59819: 39726,
+	59820: 39727,
+	59821: 39730,
+	59822: 39748,
+	59823: 39747,
+	59824: 39759,
+	59825: 39757,
+	59826: 39758,
+	59827: 39761,
+	59828: 39768,
+	59829: 39796,
+	59830: 39827,
+	59831: 39811,
+	59832: 39825,
+	59833: 39830,
+	59834: 39831,
+	59835: 39839,
+	59836: 39840,
+	59837: 39848,
+	59838: 39860,
+	59839: 39872,
+	59840: 39882,
+	59841: 39865,
+	59842: 39878,
+	59843: 39887,
+	59844: 39889,
+	59845: 39890,
+	59846: 39907,
+	59847: 39906,
+	59848: 39908,
+	59849: 39892,
+	59850: 39905,
+	59851: 39994,
+	59852: 39922,
+	59853: 39921,
+	59854: 39920,
+	59855: 39957,
+	59856: 39956,
+	59857: 39945,
+	59858: 39955,
+	59859: 39948,
+	59860: 39942,
+	59861: 39944,
+	59862: 39954,
+	59863: 39946,
+	59864: 39940,
+	59865: 39982,
+	59866: 39963,
+	59867: 39973,
+	59868: 39972,
+	59869: 39969,
+	59870: 39984,
+	59871: 40007,
+	59872: 39986,
+	59873: 40006,
+	59874: 39998,
+	59875: 40026,
+	59876: 40032,
+	59877: 40039,
+	59878: 40054,
+	59879: 40056,
+	59880: 40167,
+	59881: 40172,
+	59882: 40176,
+	59883: 40201,
+	59884: 40200,
+	59885: 40171,
+	59886: 40195,
+	59887: 40198,
+	59888: 40234,
+	59889: 40230,
+	59890: 40367,
+	59891: 40227,
+	59892: 40223,
+	59893: 40260,
+	59894: 40213,
+	59895: 40210,
+	59896: 40257,
+	59897: 40255,
+	59898: 40254,
+	59899: 40262,
+	59900: 40264,
+	59968: 40285,
+	59969: 40286,
+	59970: 40292,
+	59971: 40273,
+	59972: 40272,
+	59973: 40281,
+	59974: 40306,
+	59975: 40329,
+	59976: 40327,
+	59977: 40363,
+	59978: 40303,
+	59979: 40314,
+	59980: 40346,
+	59981: 40356,
+	59982: 40361,
+	59983: 40370,
+	59984: 40388,
+	59985: 40385,
+	59986: 40379,
+	59987: 40376,
+	59988: 40378,
+	59989: 40390,
+	59990: 40399,
+	59991: 40386,
+	59992: 40409,
+	59993: 40403,
+	59994: 40440,
+	59995: 40422,
+	59996: 40429,
+	59997: 40431,
+	59998: 40445,
+	59999: 40474,
+	60000: 40475,
+	60001: 40478,
+	60002: 40565,
+	60003: 40569,
+	60004: 40573,
+	60005: 40577,
+	60006: 40584,
+	60007: 40587,
+	60008: 40588,
+	60009: 40594,
+	60010: 40597,
+	60011: 40593,
+	60012: 40605,
+	60013: 40613,
+	60014: 40617,
+	60015: 40632,
+	60016: 40618,
+	60017: 40621,
+	60018: 38753,
+	60019: 40652,
+	60020: 40654,
+	60021: 40655,
+	60022: 40656,
+	60023: 40660,
+	60024: 40668,
+	60025: 40670,
+	60026: 40669,
+	60027: 40672,
+	60028: 40677,
+	60029: 40680,
+	60030: 40687,
+	60032: 40692,
+	60033: 40694,
+	60034: 40695,
+	60035: 40697,
+	60036: 40699,
+	60037: 40700,
+	60038: 40701,
+	60039: 40711,
+	60040: 40712,
+	60041: 30391,
+	60042: 40725,
+	60043: 40737,
+	60044: 40748,
+	60045: 40766,
+	60046: 40778,
+	60047: 40786,
+	60048: 40788,
+	60049: 40803,
+	60050: 40799,
+	60051: 40800,
+	60052: 40801,
+	60053: 40806,
+	60054: 40807,
+	60055: 40812,
+	60056: 40810,
+	60057: 40823,
+	60058: 40818,
+	60059: 40822,
+	60060: 40853,
+	60061: 40860,
+	60062: 40864,
+	60063: 22575,
+	60064: 27079,
+	60065: 36953,
+	60066: 29796,
+	60067: 20956,
+	60068: 29081,
+	60736: 32394,
+	60737: 35100,
+	60738: 37704,
+	60739: 37512,
+	60740: 34012,
+	60741: 20425,
+	60742: 28859,
+	60743: 26161,
+	60744: 26824,
+	60745: 37625,
+	60746: 26363,
+	60747: 24389,
+	60748: 20008,
+	60749: 20193,
+	60750: 20220,
+	60751: 20224,
+	60752: 20227,
+	60753: 20281,
+	60754: 20310,
+	60755: 20370,
+	60756: 20362,
+	60757: 20378,
+	60758: 20372,
+	60759: 20429,
+	60760: 20544,
+	60761: 20514,
+	60762: 20479,
+	60763: 20510,
+	60764: 20550,
+	60765: 20592,
+	60766: 20546,
+	60767: 20628,
+	60768: 20724,
+	60769: 20696,
+	60770: 20810,
+	60771: 20836,
+	60772: 20893,
+	60773: 20926,
+	60774: 20972,
+	60775: 21013,
+	60776: 21148,
+	60777: 21158,
+	60778: 21184,
+	60779: 21211,
+	60780: 21248,
+	60781: 21255,
+	60782: 21284,
+	60783: 21362,
+	60784: 21395,
+	60785: 21426,
+	60786: 21469,
+	60787: 64014,
+	60788: 21660,
+	60789: 21642,
+	60790: 21673,
+	60791: 21759,
+	60792: 21894,
+	60793: 22361,
+	60794: 22373,
+	60795: 22444,
+	60796: 22472,
+	60797: 22471,
+	60798: 64015,
+	60800: 64016,
+	60801: 22686,
+	60802: 22706,
+	60803: 22795,
+	60804: 22867,
+	60805: 22875,
+	60806: 22877,
+	60807: 22883,
+	60808: 22948,
+	60809: 22970,
+	60810: 23382,
+	60811: 23488,
+	60812: 29999,
+	60813: 23512,
+	60814: 23532,
+	60815: 23582,
+	60816: 23718,
+	60817: 23738,
+	60818: 23797,
+	60819: 23847,
+	60820: 23891,
+	60821: 64017,
+	60822: 23874,
+	60823: 23917,
+	60824: 23992,
+	60825: 23993,
+	60826: 24016,
+	60827: 24353,
+	60828: 24372,
+	60829: 24423,
+	60830: 24503,
+	60831: 24542,
+	60832: 24669,
+	60833: 24709,
+	60834: 24714,
+	60835: 24798,
+	60836: 24789,
+	60837: 24864,
+	60838: 24818,
+	60839: 24849,
+	60840: 24887,
+	60841: 24880,
+	60842: 24984,
+	60843: 25107,
+	60844: 25254,
+	60845: 25589,
+	60846: 25696,
+	60847: 25757,
+	60848: 25806,
+	60849: 25934,
+	60850: 26112,
+	60851: 26133,
+	60852: 26171,
+	60853: 26121,
+	60854: 26158,
+	60855: 26142,
+	60856: 26148,
+	60857: 26213,
+	60858: 26199,
+	60859: 26201,
+	60860: 64018,
+	60861: 26227,
+	60862: 26265,
+	60863: 26272,
+	60864: 26290,
+	60865: 26303,
+	60866: 26362,
+	60867: 26382,
+	60868: 63785,
+	60869: 26470,
+	60870: 26555,
+	60871: 26706,
+	60872: 26560,
+	60873: 26625,
+	60874: 26692,
+	60875: 26831,
+	60876: 64019,
+	60877: 26984,
+	60878: 64020,
+	60879: 27032,
+	60880: 27106,
+	60881: 27184,
+	60882: 27243,
+	60883: 27206,
+	60884: 27251,
+	60885: 27262,
+	60886: 27362,
+	60887: 27364,
+	60888: 27606,
+	60889: 27711,
+	60890: 27740,
+	60891: 27782,
+	60892: 27759,
+	60893: 27866,
+	60894: 27908,
+	60895: 28039,
+	60896: 28015,
+	60897: 28054,
+	60898: 28076,
+	60899: 28111,
+	60900: 28152,
+	60901: 28146,
+	60902: 28156,
+	60903: 28217,
+	60904: 28252,
+	60905: 28199,
+	60906: 28220,
+	60907: 28351,
+	60908: 28552,
+	60909: 28597,
+	60910: 28661,
+	60911: 28677,
+	60912: 28679,
+	60913: 28712,
+	60914: 28805,
+	60915: 28843,
+	60916: 28943,
+	60917: 28932,
+	60918: 29020,
+	60919: 28998,
+	60920: 28999,
+	60921: 64021,
+	60922: 29121,
+	60923: 29182,
+	60924: 29361,
+	60992: 29374,
+	60993: 29476,
+	60994: 64022,
+	60995: 29559,
+	60996: 29629,
+	60997: 29641,
+	60998: 29654,
+	60999: 29667,
+	61000: 29650,
+	61001: 29703,
+	61002: 29685,
+	61003: 29734,
+	61004: 29738,
+	61005: 29737,
+	61006: 29742,
+	61007: 29794,
+	61008: 29833,
+	61009: 29855,
+	61010: 29953,
+	61011: 30063,
+	61012: 30338,
+	61013: 30364,
+	61014: 30366,
+	61015: 30363,
+	61016: 30374,
+	61017: 64023,
+	61018: 30534,
+	61019: 21167,
+	61020: 30753,
+	61021: 30798,
+	61022: 30820,
+	61023: 30842,
+	61024: 31024,
+	61025: 64024,
+	61026: 64025,
+	61027: 64026,
+	61028: 31124,
+	61029: 64027,
+	61030: 31131,
+	61031: 31441,
+	61032: 31463,
+	61033: 64028,
+	61034: 31467,
+	61035: 31646,
+	61036: 64029,
+	61037: 32072,
+	61038: 32092,
+	61039: 32183,
+	61040: 32160,
+	61041: 32214,
+	61042: 32338,
+	61043: 32583,
+	61044: 32673,
+	61045: 64030,
+	61046: 33537,
+	61047: 33634,
+	61048: 33663,
+	61049: 33735,
+	61050: 33782,
+	61051: 33864,
+	61052: 33972,
+	61053: 34131,
+	61054: 34137,
+	61056: 34155,
+	61057: 64031,
+	61058: 34224,
+	61059: 64032,
+	61060: 64033,
+	61061: 34823,
+	61062: 35061,
+	61063: 35346,
+	61064: 35383,
+	61065: 35449,
+	61066: 35495,
+	61067: 35518,
+	61068: 35551,
+	61069: 64034,
+	61070: 35574,
+	61071: 35667,
+	61072: 35711,
+	61073: 36080,
+	61074: 36084,
+	61075: 36114,
+	61076: 36214,
+	61077: 64035,
+	61078: 36559,
+	61079: 64036,
+	61080: 64037,
+	61081: 36967,
+	61082: 37086,
+	61083: 64038,
+	61084: 37141,
+	61085: 37159,
+	61086: 37338,
+	61087: 37335,
+	61088: 37342,
+	61089: 37357,
+	61090: 37358,
+	61091: 37348,
+	61092: 37349,
+	61093: 37382,
+	61094: 37392,
+	61095: 37386,
+	61096: 37434,
+	61097: 37440,
+	61098: 37436,
+	61099: 37454,
+	61100: 37465,
+	61101: 37457,
+	61102: 37433,
+	61103: 37479,
+	61104: 37543,
+	61105: 37495,
+	61106: 37496,
+	61107: 37607,
+	61108: 37591,
+	61109: 37593,
+	61110: 37584,
+	61111: 64039,
+	61112: 37589,
+	61113: 37600,
+	61114: 37587,
+	61115: 37669,
+	61116: 37665,
+	61117: 37627,
+	61118: 64040,
+	61119: 37662,
+	61120: 37631,
+	61121: 37661,
+	61122: 37634,
+	61123: 37744,
+	61124: 37719,
+	61125: 37796,
+	61126: 37830,
+	61127: 37854,
+	61128: 37880,
+	61129: 37937,
+	61130: 37957,
+	61131: 37960,
+	61132: 38290,
+	61133: 63964,
+	61134: 64041,
+	61135: 38557,
+	61136: 38575,
+	61137: 38707,
+	61138: 38715,
+	61139: 38723,
+	61140: 38733,
+	61141: 38735,
+	61142: 38737,
+	61143: 38741,
+	61144: 38999,
+	61145: 39013,
+	61146: 64042,
+	61147: 64043,
+	61148: 39207,
+	61149: 64044,
+	61150: 39326,
+	61151: 39502,
+	61152: 39641,
+	61153: 39644,
+	61154: 39797,
+	61155: 39794,
+	61156: 39823,
+	61157: 39857,
+	61158: 39867,
+	61159: 39936,
+	61160: 40304,
+	61161: 40299,
+	61162: 64045,
+	61163: 40473,
+	61164: 40657,
+	61167: 8560,
+	61168: 8561,
+	61169: 8562,
+	61170: 8563,
+	61171: 8564,
+	61172: 8565,
+	61173: 8566,
+	61174: 8567,
+	61175: 8568,
+	61176: 8569,
+	61177: 65506,
+	61178: 65508,
+	61179: 65287,
+	61180: 65282,
+	61504: 57344,
+	61505: 57345,
+	61506: 57346,
+	61507: 57347,
+	61508: 57348,
+	61509: 57349,
+	61510: 57350,
+	61511: 57351,
+	61512: 57352,
+	61513: 57353,
+	61514: 57354,
+	61515: 57355,
+	61516: 57356,
+	61517: 57357,
+	61518: 57358,
+	61519: 57359,
+	61520: 57360,
+	61521: 57361,
+	61522: 57362,
+	61523: 57363,
+	61524: 57364,
+	61525: 57365,
+	61526: 57366,
+	61527: 57367,
+	61528: 57368,
+	61529: 57369,
+	61530: 57370,
+	61531: 57371,
+	61532: 57372,
+	61533: 57373,
+	61534: 57374,
+	61535: 57375,
+	61536: 57376,
+	61537: 57377,
+	61538: 57378,
+	61539: 57379,
+	61540: 57380,
+	61541: 57381,
+	61542: 57382,
+	61543: 57383,
+	61544: 57384,
+	61545: 57385,
+	61546: 57386,
+	61547: 57387,
+	61548: 57388,
+	61549: 57389,
+	61550: 57390,
+	61551: 57391,
+	61552: 57392,
+	61553: 57393,
+	61554: 57394,
+	61555: 57395,
+	61556: 57396,
+	61557: 57397,
+	61558: 57398,
+	61559: 57399,
+	61560: 57400,
+	61561: 57401,
+	61562: 57402,
+	61563: 57403,
+	61564: 57404,
+	61565: 57405,
+	61566: 57406,
+	61568: 57407,
+	61569: 57408,
+	61570: 57409,
+	61571: 57410,
+	61572: 57411,
+	61573: 57412,
+	61574: 57413,
+	61575: 57414,
+	61576: 57415,
+	61577: 57416,
+	61578: 57417,
+	61579: 57418,
+	61580: 57419,
+	61581: 57420,
+	61582: 57421,
+	61583: 57422,
+	61584: 57423,
+	61585: 57424,
+	61586: 57425,
+	61587: 57426,
+	61588: 57427,
+	61589: 57428,
+	61590: 57429,
+	61591: 57430,
+	61592: 57431,
+	61593: 57432,
+	61594: 57433,
+	61595: 57434,
+	61596: 57435,
+	61597: 57436,
+	61598: 57437,
+	61599: 57438,
+	61600: 57439,
+	61601: 57440,
+	61602: 57441,
+	61603: 57442,
+	61604: 57443,
+	61605: 57444,
+	61606: 57445,
+	61607: 57446,
+	61608: 57447,
+	61609: 57448,
+	61610: 57449,
+	61611: 57450,
+	61612: 57451,
+	61613: 57452,
+	61614: 57453,
+	61615: 57454,
+	61616: 57455,
+	61617: 57456,
+	61618: 57457,
+	61619: 57458,
+	61620: 57459,
+	61621: 57460,
+	61622: 57461,
+	61623: 57462,
+	61624: 57463,
+	61625: 57464,
+	61626: 57465,
+	61627: 57466,
+	61628: 57467,
+	61629: 57468,
+	61630: 57469,
+	61631: 57470,
+	61632: 57471,
+	61633: 57472,
+	61634: 57473,
+	61635: 57474,
+	61636: 57475,
+	61637: 57476,
+	61638: 57477,
+	61639: 57478,
+	61640: 57479,
+	61641: 57480,
+	61642: 57481,
+	61643: 57482,
+	61644: 57483,
+	61645: 57484,
+	61646: 57485,
+	61647: 57486,
+	61648: 57487,
+	61649: 57488,
+	61650: 57489,
+	61651: 57490,
+	61652: 57491,
+	61653: 57492,
+	61654: 57493,
+	61655: 57494,
+	61656: 57495,
+	61657: 57496,
+	61658: 57497,
+	61659: 57498,
+	61660: 57499,
+	61661: 57500,
+	61662: 57501,
+	61663: 57502,
+	61664: 57503,
+	61665: 57504,
+	61666: 57505,
+	61667: 57506,
+	61668: 57507,
+	61669: 57508,
+	61670: 57509,
+	61671: 57510,
+	61672: 57511,
+	61673: 57512,
+	61674: 57513,
+	61675: 57514,
+	61676: 57515,
+	61677: 57516,
+	61678: 57517,
+	61679: 57518,
+	61680: 57519,
+	61681: 57520,
+	61682: 57521,
+	61683: 57522,
+	61684: 57523,
+	61685: 57524,
+	61686: 57525,
+	61687: 57526,
+	61688: 57527,
+	61689: 57528,
+	61690: 57529,
+	61691: 57530,
+	61692: 57531,
+	61760: 57532,
+	61761: 57533,
+	61762: 57534,
+	61763: 57535,
+	61764: 57536,
+	61765: 57537,
+	61766: 57538,
+	61767: 57539,
+	61768: 57540,
+	61769: 57541,
+	61770: 57542,
+	61771: 57543,
+	61772: 57544,
+	61773: 57545,
+	61774: 57546,
+	61775: 57547,
+	61776: 57548,
+	61777: 57549,
+	61778: 57550,
+	61779: 57551,
+	61780: 57552,
+	61781: 57553,
+	61782: 57554,
+	61783: 57555,
+	61784: 57556,
+	61785: 57557,
+	61786: 57558,
+	61787: 57559,
+	61788: 57560,
+	61789: 57561,
+	61790: 57562,
+	61791: 57563,
+	61792: 57564,
+	61793: 57565,
+	61794: 57566,
+	61795: 57567,
+	61796: 57568,
+	61797: 57569,
+	61798: 57570,
+	61799: 57571,
+	61800: 57572,
+	61801: 57573,
+	61802: 57574,
+	61803: 57575,
+	61804: 57576,
+	61805: 57577,
+	61806: 57578,
+	61807: 57579,
+	61808: 57580,
+	61809: 57581,
+	61810: 57582,
+	61811: 57583,
+	61812: 57584,
+	61813: 57585,
+	61814: 57586,
+	61815: 57587,
+	61816: 57588,
+	61817: 57589,
+	61818: 57590,
+	61819: 57591,
+	61820: 57592,
+	61821: 57593,
+	61822: 57594,
+	61824: 57595,
+	61825: 57596,
+	61826: 57597,
+	61827: 57598,
+	61828: 57599,
+	61829: 57600,
+	61830: 57601,
+	61831: 57602,
+	61832: 57603,
+	61833: 57604,
+	61834: 57605,
+	61835: 57606,
+	61836: 57607,
+	61837: 57608,
+	61838: 57609,
+	61839: 57610,
+	61840: 57611,
+	61841: 57612,
+	61842: 57613,
+	61843: 57614,
+	61844: 57615,
+	61845: 57616,
+	61846: 57617,
+	61847: 57618,
+	61848: 57619,
+	61849: 57620,
+	61850: 57621,
+	61851: 57622,
+	61852: 57623,
+	61853: 57624,
+	61854: 57625,
+	61855: 57626,
+	61856: 57627,
+	61857: 57628,
+	61858: 57629,
+	61859: 57630,
+	61860: 57631,
+	61861: 57632,
+	61862: 57633,
+	61863: 57634,
+	61864: 57635,
+	61865: 57636,
+	61866: 57637,
+	61867: 57638,
+	61868: 57639,
+	61869: 57640,
+	61870: 57641,
+	61871: 57642,
+	61872: 57643,
+	61873: 57644,
+	61874: 57645,
+	61875: 57646,
+	61876: 57647,
+	61877: 57648,
+	61878: 57649,
+	61879: 57650,
+	61880: 57651,
+	61881: 57652,
+	61882: 57653,
+	61883: 57654,
+	61884: 57655,
+	61885: 57656,
+	61886: 57657,
+	61887: 57658,
+	61888: 57659,
+	61889: 57660,
+	61890: 57661,
+	61891: 57662,
+	61892: 57663,
+	61893: 57664,
+	61894: 57665,
+	61895: 57666,
+	61896: 57667,
+	61897: 57668,
+	61898: 57669,
+	61899: 57670,
+	61900: 57671,
+	61901: 57672,
+	61902: 57673,
+	61903: 57674,
+	61904: 57675,
+	61905: 57676,
+	61906: 57677,
+	61907: 57678,
+	61908: 57679,
+	61909: 57680,
+	61910: 57681,
+	61911: 57682,
+	61912: 57683,
+	61913: 57684,
+	61914: 57685,
+	61915: 57686,
+	61916: 57687,
+	61917: 57688,
+	61918: 57689,
+	61919: 57690,
+	61920: 57691,
+	61921: 57692,
+	61922: 57693,
+	61923: 57694,
+	61924: 57695,
+	61925: 57696,
+	61926: 57697,
+	61927: 57698,
+	61928: 57699,
+	61929: 57700,
+	61930: 57701,
+	61931: 57702,
+	61932: 57703,
+	61933: 57704,
+	61934: 57705,
+	61935: 57706,
+	61936: 57707,
+	61937: 57708,
+	61938: 57709,
+	61939: 57710,
+	61940: 57711,
+	61941: 57712,
+	61942: 57713,
+	61943: 57714,
+	61944: 57715,
+	61945: 57716,
+	61946: 57717,
+	61947: 57718,
+	61948: 57719,
+	62016: 57720,
+	62017: 57721,
+	62018: 57722,
+	62019: 57723,
+	62020: 57724,
+	62021: 57725,
+	62022: 57726,
+	62023: 57727,
+	62024: 57728,
+	62025: 57729,
+	62026: 57730,
+	62027: 57731,
+	62028: 57732,
+	62029: 57733,
+	62030: 57734,
+	62031: 57735,
+	62032: 57736,
+	62033: 57737,
+	62034: 57738,
+	62035: 57739,
+	62036: 57740,
+	62037: 57741,
+	62038: 57742,
+	62039: 57743,
+	62040: 57744,
+	62041: 57745,
+	62042: 57746,
+	62043: 57747,
+	62044: 57748,
+	62045: 57749,
+	62046: 57750,
+	62047: 57751,
+	62048: 57752,
+	62049: 57753,
+	62050: 57754,
+	62051: 57755,
+	62052: 57756,
+	62053: 57757,
+	62054: 57758,
+	62055: 57759,
+	62056: 57760,
+	62057: 57761,
+	62058: 57762,
+	62059: 57763,
+	62060: 57764,
+	62061: 57765,
+	62062: 57766,
+	62063: 57767,
+	62064: 57768,
+	62065: 57769,
+	62066: 57770,
+	62067: 57771,
+	62068: 57772,
+	62069: 57773,
+	62070: 57774,
+	62071: 57775,
+	62072: 57776,
+	62073: 57777,
+	62074: 57778,
+	62075: 57779,
+	62076: 57780,
+	62077: 57781,
+	62078: 57782,
+	62080: 57783,
+	62081: 57784,
+	62082: 57785,
+	62083: 57786,
+	62084: 57787,
+	62085: 57788,
+	62086: 57789,
+	62087: 57790,
+	62088: 57791,
+	62089: 57792,
+	62090: 57793,
+	62091: 57794,
+	62092: 57795,
+	62093: 57796,
+	62094: 57797,
+	62095: 57798,
+	62096: 57799,
+	62097: 57800,
+	62098: 57801,
+	62099: 57802,
+	62100: 57803,
+	62101: 57804,
+	62102: 57805,
+	62103: 57806,
+	62104: 57807,
+	62105: 57808,
+	62106: 57809,
+	62107: 57810,
+	62108: 57811,
+	62109: 57812,
+	62110: 57813,
+	62111: 57814,
+	62112: 57815,
+	62113: 57816,
+	62114: 57817,
+	62115: 57818,
+	62116: 57819,
+	62117: 57820,
+	62118: 57821,
+	62119: 57822,
+	62120: 57823,
+	62121: 57824,
+	62122: 57825,
+	62123: 57826,
+	62124: 57827,
+	62125: 57828,
+	62126: 57829,
+	62127: 57830,
+	62128: 57831,
+	62129: 57832,
+	62130: 57833,
+	62131: 57834,
+	62132: 57835,
+	62133: 57836,
+	62134: 57837,
+	62135: 57838,
+	62136: 57839,
+	62137: 57840,
+	62138: 57841,
+	62139: 57842,
+	62140: 57843,
+	62141: 57844,
+	62142: 57845,
+	62143: 57846,
+	62144: 57847,
+	62145: 57848,
+	62146: 57849,
+	62147: 57850,
+	62148: 57851,
+	62149: 57852,
+	62150: 57853,
+	62151: 57854,
+	62152: 57855,
+	62153: 57856,
+	62154: 57857,
+	62155: 57858,
+	62156: 57859,
+	62157: 57860,
+	62158: 57861,
+	62159: 57862,
+	62160: 57863,
+	62161: 57864,
+	62162: 57865,
+	62163: 57866,
+	62164: 57867,
+	62165: 57868,
+	62166: 57869,
+	62167: 57870,
+	62168: 57871,
+	62169: 57872,
+	62170: 57873,
+	62171: 57874,
+	62172: 57875,
+	62173: 57876,
+	62174: 57877,
+	62175: 57878,
+	62176: 57879,
+	62177: 57880,
+	62178: 57881,
+	62179: 57882,
+	62180: 57883,
+	62181: 57884,
+	62182: 57885,
+	62183: 57886,
+	62184: 57887,
+	62185: 57888,
+	62186: 57889,
+	62187: 57890,
+	62188: 57891,
+	62189: 57892,
+	62190: 57893,
+	62191: 57894,
+	62192: 57895,
+	62193: 57896,
+	62194: 57897,
+	62195: 57898,
+	62196: 57899,
+	62197: 57900,
+	62198: 57901,
+	62199: 57902,
+	62200: 57903,
+	62201: 57904,
+	62202: 57905,
+	62203: 57906,
+	62204: 57907,
+	62272: 57908,
+	62273: 57909,
+	62274: 57910,
+	62275: 57911,
+	62276: 57912,
+	62277: 57913,
+	62278: 57914,
+	62279: 57915,
+	62280: 57916,
+	62281: 57917,
+	62282: 57918,
+	62283: 57919,
+	62284: 57920,
+	62285: 57921,
+	62286: 57922,
+	62287: 57923,
+	62288: 57924,
+	62289: 57925,
+	62290: 57926,
+	62291: 57927,
+	62292: 57928,
+	62293: 57929,
+	62294: 57930,
+	62295: 57931,
+	62296: 57932,
+	62297: 57933,
+	62298: 57934,
+	62299: 57935,
+	62300: 57936,
+	62301: 57937,
+	62302: 57938,
+	62303: 57939,
+	62304: 57940,
+	62305: 57941,
+	62306: 57942,
+	62307: 57943,
+	62308: 57944,
+	62309: 57945,
+	62310: 57946,
+	62311: 57947,
+	62312: 57948,
+	62313: 57949,
+	62314: 57950,
+	62315: 57951,
+	62316: 57952,
+	62317: 57953,
+	62318: 57954,
+	62319: 57955,
+	62320: 57956,
+	62321: 57957,
+	62322: 57958,
+	62323: 57959,
+	62324: 57960,
+	62325: 57961,
+	62326: 57962,
+	62327: 57963,
+	62328: 57964,
+	62329: 57965,
+	62330: 57966,
+	62331: 57967,
+	62332: 57968,
+	62333: 57969,
+	62334: 57970,
+	62336: 57971,
+	62337: 57972,
+	62338: 57973,
+	62339: 57974,
+	62340: 57975,
+	62341: 57976,
+	62342: 57977,
+	62343: 57978,
+	62344: 57979,
+	62345: 57980,
+	62346: 57981,
+	62347: 57982,
+	62348: 57983,
+	62349: 57984,
+	62350: 57985,
+	62351: 57986,
+	62352: 57987,
+	62353: 57988,
+	62354: 57989,
+	62355: 57990,
+	62356: 57991,
+	62357: 57992,
+	62358: 57993,
+	62359: 57994,
+	62360: 57995,
+	62361: 57996,
+	62362: 57997,
+	62363: 57998,
+	62364: 57999,
+	62365: 58000,
+	62366: 58001,
+	62367: 58002,
+	62368: 58003,
+	62369: 58004,
+	62370: 58005,
+	62371: 58006,
+	62372: 58007,
+	62373: 58008,
+	62374: 58009,
+	62375: 58010,
+	62376: 58011,
+	62377: 58012,
+	62378: 58013,
+	62379: 58014,
+	62380: 58015,
+	62381: 58016,
+	62382: 58017,
+	62383: 58018,
+	62384: 58019,
+	62385: 58020,
+	62386: 58021,
+	62387: 58022,
+	62388: 58023,
+	62389: 58024,
+	62390: 58025,
+	62391: 58026,
+	62392: 58027,
+	62393: 58028,
+	62394: 58029,
+	62395: 58030,
+	62396: 58031,
+	62397: 58032,
+	62398: 58033,
+	62399: 58034,
+	62400: 58035,
+	62401: 58036,
+	62402: 58037,
+	62403: 58038,
+	62404: 58039,
+	62405: 58040,
+	62406: 58041,
+	62407: 58042,
+	62408: 58043,
+	62409: 58044,
+	62410: 58045,
+	62411: 58046,
+	62412: 58047,
+	62413: 58048,
+	62414: 58049,
+	62415: 58050,
+	62416: 58051,
+	62417: 58052,
+	62418: 58053,
+	62419: 58054,
+	62420: 58055,
+	62421: 58056,
+	62422: 58057,
+	62423: 58058,
+	62424: 58059,
+	62425: 58060,
+	62426: 58061,
+	62427: 58062,
+	62428: 58063,
+	62429: 58064,
+	62430: 58065,
+	62431: 58066,
+	62432: 58067,
+	62433: 58068,
+	62434: 58069,
+	62435: 58070,
+	62436: 58071,
+	62437: 58072,
+	62438: 58073,
+	62439: 58074,
+	62440: 58075,
+	62441: 58076,
+	62442: 58077,
+	62443: 58078,
+	62444: 58079,
+	62445: 58080,
+	62446: 58081,
+	62447: 58082,
+	62448: 58083,
+	62449: 58084,
+	62450: 58085,
+	62451: 58086,
+	62452: 58087,
+	62453: 58088,
+	62454: 58089,
+	62455: 58090,
+	62456: 58091,
+	62457: 58092,
+	62458: 58093,
+	62459: 58094,
+	62460: 58095,
+	62528: 58096,
+	62529: 58097,
+	62530: 58098,
+	62531: 58099,
+	62532: 58100,
+	62533: 58101,
+	62534: 58102,
+	62535: 58103,
+	62536: 58104,
+	62537: 58105,
+	62538: 58106,
+	62539: 58107,
+	62540: 58108,
+	62541: 58109,
+	62542: 58110,
+	62543: 58111,
+	62544: 58112,
+	62545: 58113,
+	62546: 58114,
+	62547: 58115,
+	62548: 58116,
+	62549: 58117,
+	62550: 58118,
+	62551: 58119,
+	62552: 58120,
+	62553: 58121,
+	62554: 58122,
+	62555: 58123,
+	62556: 58124,
+	62557: 58125,
+	62558: 58126,
+	62559: 58127,
+	62560: 58128,
+	62561: 58129,
+	62562: 58130,
+	62563: 58131,
+	62564: 58132,
+	62565: 58133,
+	62566: 58134,
+	62567: 58135,
+	62568: 58136,
+	62569: 58137,
+	62570: 58138,
+	62571: 58139,
+	62572: 58140,
+	62573: 58141,
+	62574: 58142,
+	62575: 58143,
+	62576: 58144,
+	62577: 58145,
+	62578: 58146,
+	62579: 58147,
+	62580: 58148,
+	62581: 58149,
+	62582: 58150,
+	62583: 58151,
+	62584: 58152,
+	62585: 58153,
+	62586: 58154,
+	62587: 58155,
+	62588: 58156,
+	62589: 58157,
+	62590: 58158,
+	62592: 58159,
+	62593: 58160,
+	62594: 58161,
+	62595: 58162,
+	62596: 58163,
+	62597: 58164,
+	62598: 58165,
+	62599: 58166,
+	62600: 58167,
+	62601: 58168,
+	62602: 58169,
+	62603: 58170,
+	62604: 58171,
+	62605: 58172,
+	62606: 58173,
+	62607: 58174,
+	62608: 58175,
+	62609: 58176,
+	62610: 58177,
+	62611: 58178,
+	62612: 58179,
+	62613: 58180,
+	62614: 58181,
+	62615: 58182,
+	62616: 58183,
+	62617: 58184,
+	62618: 58185,
+	62619: 58186,
+	62620: 58187,
+	62621: 58188,
+	62622: 58189,
+	62623: 58190,
+	62624: 58191,
+	62625: 58192,
+	62626: 58193,
+	62627: 58194,
+	62628: 58195,
+	62629: 58196,
+	62630: 58197,
+	62631: 58198,
+	62632: 58199,
+	62633: 58200,
+	62634: 58201,
+	62635: 58202,
+	62636: 58203,
+	62637: 58204,
+	62638: 58205,
+	62639: 58206,
+	62640: 58207,
+	62641: 58208,
+	62642: 58209,
+	62643: 58210,
+	62644: 58211,
+	62645: 58212,
+	62646: 58213,
+	62647: 58214,
+	62648: 58215,
+	62649: 58216,
+	62650: 58217,
+	62651: 58218,
+	62652: 58219,
+	62653: 58220,
+	62654: 58221,
+	62655: 58222,
+	62656: 58223,
+	62657: 58224,
+	62658: 58225,
+	62659: 58226,
+	62660: 58227,
+	62661: 58228,
+	62662: 58229,
+	62663: 58230,
+	62664: 58231,
+	62665: 58232,
+	62666: 58233,
+	62667: 58234,
+	62668: 58235,
+	62669: 58236,
+	62670: 58237,
+	62671: 58238,
+	62672: 58239,
+	62673: 58240,
+	62674: 58241,
+	62675: 58242,
+	62676: 58243,
+	62677: 58244,
+	62678: 58245,
+	62679: 58246,
+	62680: 58247,
+	62681: 58248,
+	62682: 58249,
+	62683: 58250,
+	62684: 58251,
+	62685: 58252,
+	62686: 58253,
+	62687: 58254,
+	62688: 58255,
+	62689: 58256,
+	62690: 58257,
+	62691: 58258,
+	62692: 58259,
+	62693: 58260,
+	62694: 58261,
+	62695: 58262,
+	62696: 58263,
+	62697: 58264,
+	62698: 58265,
+	62699: 58266,
+	62700: 58267,
+	62701: 58268,
+	62702: 58269,
+	62703: 58270,
+	62704: 58271,
+	62705: 58272,
+	62706: 58273,
+	62707: 58274,
+	62708: 58275,
+	62709: 58276,
+	62710: 58277,
+	62711: 58278,
+	62712: 58279,
+	62713: 58280,
+	62714: 58281,
+	62715: 58282,
+	62716: 58283,
+	62784: 58284,
+	62785: 58285,
+	62786: 58286,
+	62787: 58287,
+	62788: 58288,
+	62789: 58289,
+	62790: 58290,
+	62791: 58291,
+	62792: 58292,
+	62793: 58293,
+	62794: 58294,
+	62795: 58295,
+	62796: 58296,
+	62797: 58297,
+	62798: 58298,
+	62799: 58299,
+	62800: 58300,
+	62801: 58301,
+	62802: 58302,
+	62803: 58303,
+	62804: 58304,
+	62805: 58305,
+	62806: 58306,
+	62807: 58307,
+	62808: 58308,
+	62809: 58309,
+	62810: 58310,
+	62811: 58311,
+	62812: 58312,
+	62813: 58313,
+	62814: 58314,
+	62815: 58315,
+	62816: 58316,
+	62817: 58317,
+	62818: 58318,
+	62819: 58319,
+	62820: 58320,
+	62821: 58321,
+	62822: 58322,
+	62823: 58323,
+	62824: 58324,
+	62825: 58325,
+	62826: 58326,
+	62827: 58327,
+	62828: 58328,
+	62829: 58329,
+	62830: 58330,
+	62831: 58331,
+	62832: 58332,
+	62833: 58333,
+	62834: 58334,
+	62835: 58335,
+	62836: 58336,
+	62837: 58337,
+	62838: 58338,
+	62839: 58339,
+	62840: 58340,
+	62841: 58341,
+	62842: 58342,
+	62843: 58343,
+	62844: 58344,
+	62845: 58345,
+	62846: 58346,
+	62848: 58347,
+	62849: 58348,
+	62850: 58349,
+	62851: 58350,
+	62852: 58351,
+	62853: 58352,
+	62854: 58353,
+	62855: 58354,
+	62856: 58355,
+	62857: 58356,
+	62858: 58357,
+	62859: 58358,
+	62860: 58359,
+	62861: 58360,
+	62862: 58361,
+	62863: 58362,
+	62864: 58363,
+	62865: 58364,
+	62866: 58365,
+	62867: 58366,
+	62868: 58367,
+	62869: 58368,
+	62870: 58369,
+	62871: 58370,
+	62872: 58371,
+	62873: 58372,
+	62874: 58373,
+	62875: 58374,
+	62876: 58375,
+	62877: 58376,
+	62878: 58377,
+	62879: 58378,
+	62880: 58379,
+	62881: 58380,
+	62882: 58381,
+	62883: 58382,
+	62884: 58383,
+	62885: 58384,
+	62886: 58385,
+	62887: 58386,
+	62888: 58387,
+	62889: 58388,
+	62890: 58389,
+	62891: 58390,
+	62892: 58391,
+	62893: 58392,
+	62894: 58393,
+	62895: 58394,
+	62896: 58395,
+	62897: 58396,
+	62898: 58397,
+	62899: 58398,
+	62900: 58399,
+	62901: 58400,
+	62902: 58401,
+	62903: 58402,
+	62904: 58403,
+	62905: 58404,
+	62906: 58405,
+	62907: 58406,
+	62908: 58407,
+	62909: 58408,
+	62910: 58409,
+	62911: 58410,
+	62912: 58411,
+	62913: 58412,
+	62914: 58413,
+	62915: 58414,
+	62916: 58415,
+	62917: 58416,
+	62918: 58417,
+	62919: 58418,
+	62920: 58419,
+	62921: 58420,
+	62922: 58421,
+	62923: 58422,
+	62924: 58423,
+	62925: 58424,
+	62926: 58425,
+	62927: 58426,
+	62928: 58427,
+	62929: 58428,
+	62930: 58429,
+	62931: 58430,
+	62932: 58431,
+	62933: 58432,
+	62934: 58433,
+	62935: 58434,
+	62936: 58435,
+	62937: 58436,
+	62938: 58437,
+	62939: 58438,
+	62940: 58439,
+	62941: 58440,
+	62942: 58441,
+	62943: 58442,
+	62944: 58443,
+	62945: 58444,
+	62946: 58445,
+	62947: 58446,
+	62948: 58447,
+	62949: 58448,
+	62950: 58449,
+	62951: 58450,
+	62952: 58451,
+	62953: 58452,
+	62954: 58453,
+	62955: 58454,
+	62956: 58455,
+	62957: 58456,
+	62958: 58457,
+	62959: 58458,
+	62960: 58459,
+	62961: 58460,
+	62962: 58461,
+	62963: 58462,
+	62964: 58463,
+	62965: 58464,
+	62966: 58465,
+	62967: 58466,
+	62968: 58467,
+	62969: 58468,
+	62970: 58469,
+	62971: 58470,
+	62972: 58471,
+	63040: 58472,
+	63041: 58473,
+	63042: 58474,
+	63043: 58475,
+	63044: 58476,
+	63045: 58477,
+	63046: 58478,
+	63047: 58479,
+	63048: 58480,
+	63049: 58481,
+	63050: 58482,
+	63051: 58483,
+	63052: 58484,
+	63053: 58485,
+	63054: 58486,
+	63055: 58487,
+	63056: 58488,
+	63057: 58489,
+	63058: 58490,
+	63059: 58491,
+	63060: 58492,
+	63061: 58493,
+	63062: 58494,
+	63063: 58495,
+	63064: 58496,
+	63065: 58497,
+	63066: 58498,
+	63067: 58499,
+	63068: 58500,
+	63069: 58501,
+	63070: 58502,
+	63071: 58503,
+	63072: 58504,
+	63073: 58505,
+	63074: 58506,
+	63075: 58507,
+	63076: 58508,
+	63077: 58509,
+	63078: 58510,
+	63079: 58511,
+	63080: 58512,
+	63081: 58513,
+	63082: 58514,
+	63083: 58515,
+	63084: 58516,
+	63085: 58517,
+	63086: 58518,
+	63087: 58519,
+	63088: 58520,
+	63089: 58521,
+	63090: 58522,
+	63091: 58523,
+	63092: 58524,
+	63093: 58525,
+	63094: 58526,
+	63095: 58527,
+	63096: 58528,
+	63097: 58529,
+	63098: 58530,
+	63099: 58531,
+	63100: 58532,
+	63101: 58533,
+	63102: 58534,
+	63104: 58535,
+	63105: 58536,
+	63106: 58537,
+	63107: 58538,
+	63108: 58539,
+	63109: 58540,
+	63110: 58541,
+	63111: 58542,
+	63112: 58543,
+	63113: 58544,
+	63114: 58545,
+	63115: 58546,
+	63116: 58547,
+	63117: 58548,
+	63118: 58549,
+	63119: 58550,
+	63120: 58551,
+	63121: 58552,
+	63122: 58553,
+	63123: 58554,
+	63124: 58555,
+	63125: 58556,
+	63126: 58557,
+	63127: 58558,
+	63128: 58559,
+	63129: 58560,
+	63130: 58561,
+	63131: 58562,
+	63132: 58563,
+	63133: 58564,
+	63134: 58565,
+	63135: 58566,
+	63136: 58567,
+	63137: 58568,
+	63138: 58569,
+	63139: 58570,
+	63140: 58571,
+	63141: 58572,
+	63142: 58573,
+	63143: 58574,
+	63144: 58575,
+	63145: 58576,
+	63146: 58577,
+	63147: 58578,
+	63148: 58579,
+	63149: 58580,
+	63150: 58581,
+	63151: 58582,
+	63152: 58583,
+	63153: 58584,
+	63154: 58585,
+	63155: 58586,
+	63156: 58587,
+	63157: 58588,
+	63158: 58589,
+	63159: 58590,
+	63160: 58591,
+	63161: 58592,
+	63162: 58593,
+	63163: 58594,
+	63164: 58595,
+	63165: 58596,
+	63166: 58597,
+	63167: 58598,
+	63168: 58599,
+	63169: 58600,
+	63170: 58601,
+	63171: 58602,
+	63172: 58603,
+	63173: 58604,
+	63174: 58605,
+	63175: 58606,
+	63176: 58607,
+	63177: 58608,
+	63178: 58609,
+	63179: 58610,
+	63180: 58611,
+	63181: 58612,
+	63182: 58613,
+	63183: 58614,
+	63184: 58615,
+	63185: 58616,
+	63186: 58617,
+	63187: 58618,
+	63188: 58619,
+	63189: 58620,
+	63190: 58621,
+	63191: 58622,
+	63192: 58623,
+	63193: 58624,
+	63194: 58625,
+	63195: 58626,
+	63196: 58627,
+	63197: 58628,
+	63198: 58629,
+	63199: 58630,
+	63200: 58631,
+	63201: 58632,
+	63202: 58633,
+	63203: 58634,
+	63204: 58635,
+	63205: 58636,
+	63206: 58637,
+	63207: 58638,
+	63208: 58639,
+	63209: 58640,
+	63210: 58641,
+	63211: 58642,
+	63212: 58643,
+	63213: 58644,
+	63214: 58645,
+	63215: 58646,
+	63216: 58647,
+	63217: 58648,
+	63218: 58649,
+	63219: 58650,
+	63220: 58651,
+	63221: 58652,
+	63222: 58653,
+	63223: 58654,
+	63224: 58655,
+	63225: 58656,
+	63226: 58657,
+	63227: 58658,
+	63228: 58659,
+	63296: 58660,
+	63297: 58661,
+	63298: 58662,
+	63299: 58663,
+	63300: 58664,
+	63301: 58665,
+	63302: 58666,
+	63303: 58667,
+	63304: 58668,
+	63305: 58669,
+	63306: 58670,
+	63307: 58671,
+	63308: 58672,
+	63309: 58673,
+	63310: 58674,
+	63311: 58675,
+	63312: 58676,
+	63313: 58677,
+	63314: 58678,
+	63315: 58679,
+	63316: 58680,
+	63317: 58681,
+	63318: 58682,
+	63319: 58683,
+	63320: 58684,
+	63321: 58685,
+	63322: 58686,
+	63323: 58687,
+	63324: 58688,
+	63325: 58689,
+	63326: 58690,
+	63327: 58691,
+	63328: 58692,
+	63329: 58693,
+	63330: 58694,
+	63331: 58695,
+	63332: 58696,
+	63333: 58697,
+	63334: 58698,
+	63335: 58699,
+	63336: 58700,
+	63337: 58701,
+	63338: 58702,
+	63339: 58703,
+	63340: 58704,
+	63341: 58705,
+	63342: 58706,
+	63343: 58707,
+	63344: 58708,
+	63345: 58709,
+	63346: 58710,
+	63347: 58711,
+	63348: 58712,
+	63349: 58713,
+	63350: 58714,
+	63351: 58715,
+	63352: 58716,
+	63353: 58717,
+	63354: 58718,
+	63355: 58719,
+	63356: 58720,
+	63357: 58721,
+	63358: 58722,
+	63360: 58723,
+	63361: 58724,
+	63362: 58725,
+	63363: 58726,
+	63364: 58727,
+	63365: 58728,
+	63366: 58729,
+	63367: 58730,
+	63368: 58731,
+	63369: 58732,
+	63370: 58733,
+	63371: 58734,
+	63372: 58735,
+	63373: 58736,
+	63374: 58737,
+	63375: 58738,
+	63376: 58739,
+	63377: 58740,
+	63378: 58741,
+	63379: 58742,
+	63380: 58743,
+	63381: 58744,
+	63382: 58745,
+	63383: 58746,
+	63384: 58747,
+	63385: 58748,
+	63386: 58749,
+	63387: 58750,
+	63388: 58751,
+	63389: 58752,
+	63390: 58753,
+	63391: 58754,
+	63392: 58755,
+	63393: 58756,
+	63394: 58757,
+	63395: 58758,
+	63396: 58759,
+	63397: 58760,
+	63398: 58761,
+	63399: 58762,
+	63400: 58763,
+	63401: 58764,
+	63402: 58765,
+	63403: 58766,
+	63404: 58767,
+	63405: 58768,
+	63406: 58769,
+	63407: 58770,
+	63408: 58771,
+	63409: 58772,
+	63410: 58773,
+	63411: 58774,
+	63412: 58775,
+	63413: 58776,
+	63414: 58777,
+	63415: 58778,
+	63416: 58779,
+	63417: 58780,
+	63418: 58781,
+	63419: 58782,
+	63420: 58783,
+	63421: 58784,
+	63422: 58785,
+	63423: 58786,
+	63424: 58787,
+	63425: 58788,
+	63426: 58789,
+	63427: 58790,
+	63428: 58791,
+	63429: 58792,
+	63430: 58793,
+	63431: 58794,
+	63432: 58795,
+	63433: 58796,
+	63434: 58797,
+	63435: 58798,
+	63436: 58799,
+	63437: 58800,
+	63438: 58801,
+	63439: 58802,
+	63440: 58803,
+	63441: 58804,
+	63442: 58805,
+	63443: 58806,
+	63444: 58807,
+	63445: 58808,
+	63446: 58809,
+	63447: 58810,
+	63448: 58811,
+	63449: 58812,
+	63450: 58813,
+	63451: 58814,
+	63452: 58815,
+	63453: 58816,
+	63454: 58817,
+	63455: 58818,
+	63456: 58819,
+	63457: 58820,
+	63458: 58821,
+	63459: 58822,
+	63460: 58823,
+	63461: 58824,
+	63462: 58825,
+	63463: 58826,
+	63464: 58827,
+	63465: 58828,
+	63466: 58829,
+	63467: 58830,
+	63468: 58831,
+	63469: 58832,
+	63470: 58833,
+	63471: 58834,
+	63472: 58835,
+	63473: 58836,
+	63474: 58837,
+	63475: 58838,
+	63476: 58839,
+	63477: 58840,
+	63478: 58841,
+	63479: 58842,
+	63480: 58843,
+	63481: 58844,
+	63482: 58845,
+	63483: 58846,
+	63484: 58847,
+	63552: 58848,
+	63553: 58849,
+	63554: 58850,
+	63555: 58851,
+	63556: 58852,
+	63557: 58853,
+	63558: 58854,
+	63559: 58855,
+	63560: 58856,
+	63561: 58857,
+	63562: 58858,
+	63563: 58859,
+	63564: 58860,
+	63565: 58861,
+	63566: 58862,
+	63567: 58863,
+	63568: 58864,
+	63569: 58865,
+	63570: 58866,
+	63571: 58867,
+	63572: 58868,
+	63573: 58869,
+	63574: 58870,
+	63575: 58871,
+	63576: 58872,
+	63577: 58873,
+	63578: 58874,
+	63579: 58875,
+	63580: 58876,
+	63581: 58877,
+	63582: 58878,
+	63583: 58879,
+	63584: 58880,
+	63585: 58881,
+	63586: 58882,
+	63587: 58883,
+	63588: 58884,
+	63589: 58885,
+	63590: 58886,
+	63591: 58887,
+	63592: 58888,
+	63593: 58889,
+	63594: 58890,
+	63595: 58891,
+	63596: 58892,
+	63597: 58893,
+	63598: 58894,
+	63599: 58895,
+	63600: 58896,
+	63601: 58897,
+	63602: 58898,
+	63603: 58899,
+	63604: 58900,
+	63605: 58901,
+	63606: 58902,
+	63607: 58903,
+	63608: 58904,
+	63609: 58905,
+	63610: 58906,
+	63611: 58907,
+	63612: 58908,
+	63613: 58909,
+	63614: 58910,
+	63616: 58911,
+	63617: 58912,
+	63618: 58913,
+	63619: 58914,
+	63620: 58915,
+	63621: 58916,
+	63622: 58917,
+	63623: 58918,
+	63624: 58919,
+	63625: 58920,
+	63626: 58921,
+	63627: 58922,
+	63628: 58923,
+	63629: 58924,
+	63630: 58925,
+	63631: 58926,
+	63632: 58927,
+	63633: 58928,
+	63634: 58929,
+	63635: 58930,
+	63636: 58931,
+	63637: 58932,
+	63638: 58933,
+	63639: 58934,
+	63640: 58935,
+	63641: 58936,
+	63642: 58937,
+	63643: 58938,
+	63644: 58939,
+	63645: 58940,
+	63646: 58941,
+	63647: 58942,
+	63648: 58943,
+	63649: 58944,
+	63650: 58945,
+	63651: 58946,
+	63652: 58947,
+	63653: 58948,
+	63654: 58949,
+	63655: 58950,
+	63656: 58951,
+	63657: 58952,
+	63658: 58953,
+	63659: 58954,
+	63660: 58955,
+	63661: 58956,
+	63662: 58957,
+	63663: 58958,
+	63664: 58959,
+	63665: 58960,
+	63666: 58961,
+	63667: 58962,
+	63668: 58963,
+	63669: 58964,
+	63670: 58965,
+	63671: 58966,
+	63672: 58967,
+	63673: 58968,
+	63674: 58969,
+	63675: 58970,
+	63676: 58971,
+	63677: 58972,
+	63678: 58973,
+	63679: 58974,
+	63680: 58975,
+	63681: 58976,
+	63682: 58977,
+	63683: 58978,
+	63684: 58979,
+	63685: 58980,
+	63686: 58981,
+	63687: 58982,
+	63688: 58983,
+	63689: 58984,
+	63690: 58985,
+	63691: 58986,
+	63692: 58987,
+	63693: 58988,
+	63694: 58989,
+	63695: 58990,
+	63696: 58991,
+	63697: 58992,
+	63698: 58993,
+	63699: 58994,
+	63700: 58995,
+	63701: 58996,
+	63702: 58997,
+	63703: 58998,
+	63704: 58999,
+	63705: 59000,
+	63706: 59001,
+	63707: 59002,
+	63708: 59003,
+	63709: 59004,
+	63710: 59005,
+	63711: 59006,
+	63712: 59007,
+	63713: 59008,
+	63714: 59009,
+	63715: 59010,
+	63716: 59011,
+	63717: 59012,
+	63718: 59013,
+	63719: 59014,
+	63720: 59015,
+	63721: 59016,
+	63722: 59017,
+	63723: 59018,
+	63724: 59019,
+	63725: 59020,
+	63726: 59021,
+	63727: 59022,
+	63728: 59023,
+	63729: 59024,
+	63730: 59025,
+	63731: 59026,
+	63732: 59027,
+	63733: 59028,
+	63734: 59029,
+	63735: 59030,
+	63736: 59031,
+	63737: 59032,
+	63738: 59033,
+	63739: 59034,
+	63740: 59035,
+	64064: 8560,
+	64065: 8561,
+	64066: 8562,
+	64067: 8563,
+	64068: 8564,
+	64069: 8565,
+	64070: 8566,
+	64071: 8567,
+	64072: 8568,
+	64073: 8569,
+	64074: 8544,
+	64075: 8545,
+	64076: 8546,
+	64077: 8547,
+	64078: 8548,
+	64079: 8549,
+	64080: 8550,
+	64081: 8551,
+	64082: 8552,
+	64083: 8553,
+	64084: 65506,
+	64085: 65508,
+	64086: 65287,
+	64087: 65282,
+	64088: 12849,
+	64089: 8470,
+	64090: 8481,
+	64091: 8757,
+	64092: 32394,
+	64093: 35100,
+	64094: 37704,
+	64095: 37512,
+	64096: 34012,
+	64097: 20425,
+	64098: 28859,
+	64099: 26161,
+	64100: 26824,
+	64101: 37625,
+	64102: 26363,
+	64103: 24389,
+	64104: 20008,
+	64105: 20193,
+	64106: 20220,
+	64107: 20224,
+	64108: 20227,
+	64109: 20281,
+	64110: 20310,
+	64111: 20370,
+	64112: 20362,
+	64113: 20378,
+	64114: 20372,
+	64115: 20429,
+	64116: 20544,
+	64117: 20514,
+	64118: 20479,
+	64119: 20510,
+	64120: 20550,
+	64121: 20592,
+	64122: 20546,
+	64123: 20628,
+	64124: 20724,
+	64125: 20696,
+	64126: 20810,
+	64128: 20836,
+	64129: 20893,
+	64130: 20926,
+	64131: 20972,
+	64132: 21013,
+	64133: 21148,
+	64134: 21158,
+	64135: 21184,
+	64136: 21211,
+	64137: 21248,
+	64138: 21255,
+	64139: 21284,
+	64140: 21362,
+	64141: 21395,
+	64142: 21426,
+	64143: 21469,
+	64144: 64014,
+	64145: 21660,
+	64146: 21642,
+	64147: 21673,
+	64148: 21759,
+	64149: 21894,
+	64150: 22361,
+	64151: 22373,
+	64152: 22444,
+	64153: 22472,
+	64154: 22471,
+	64155: 64015,
+	64156: 64016,
+	64157: 22686,
+	64158: 22706,
+	64159: 22795,
+	64160: 22867,
+	64161: 22875,
+	64162: 22877,
+	64163: 22883,
+	64164: 22948,
+	64165: 22970,
+	64166: 23382,
+	64167: 23488,
+	64168: 29999,
+	64169: 23512,
+	64170: 23532,
+	64171: 23582,
+	64172: 23718,
+	64173: 23738,
+	64174: 23797,
+	64175: 23847,
+	64176: 23891,
+	64177: 64017,
+	64178: 23874,
+	64179: 23917,
+	64180: 23992,
+	64181: 23993,
+	64182: 24016,
+	64183: 24353,
+	64184: 24372,
+	64185: 24423,
+	64186: 24503,
+	64187: 24542,
+	64188: 24669,
+	64189: 24709,
+	64190: 24714,
+	64191: 24798,
+	64192: 24789,
+	64193: 24864,
+	64194: 24818,
+	64195: 24849,
+	64196: 24887,
+	64197: 24880,
+	64198: 24984,
+	64199: 25107,
+	64200: 25254,
+	64201: 25589,
+	64202: 25696,
+	64203: 25757,
+	64204: 25806,
+	64205: 25934,
+	64206: 26112,
+	64207: 26133,
+	64208: 26171,
+	64209: 26121,
+	64210: 26158,
+	64211: 26142,
+	64212: 26148,
+	64213: 26213,
+	64214: 26199,
+	64215: 26201,
+	64216: 64018,
+	64217: 26227,
+	64218: 26265,
+	64219: 26272,
+	64220: 26290,
+	64221: 26303,
+	64222: 26362,
+	64223: 26382,
+	64224: 63785,
+	64225: 26470,
+	64226: 26555,
+	64227: 26706,
+	64228: 26560,
+	64229: 26625,
+	64230: 26692,
+	64231: 26831,
+	64232: 64019,
+	64233: 26984,
+	64234: 64020,
+	64235: 27032,
+	64236: 27106,
+	64237: 27184,
+	64238: 27243,
+	64239: 27206,
+	64240: 27251,
+	64241: 27262,
+	64242: 27362,
+	64243: 27364,
+	64244: 27606,
+	64245: 27711,
+	64246: 27740,
+	64247: 27782,
+	64248: 27759,
+	64249: 27866,
+	64250: 27908,
+	64251: 28039,
+	64252: 28015,
+	64320: 28054,
+	64321: 28076,
+	64322: 28111,
+	64323: 28152,
+	64324: 28146,
+	64325: 28156,
+	64326: 28217,
+	64327: 28252,
+	64328: 28199,
+	64329: 28220,
+	64330: 28351,
+	64331: 28552,
+	64332: 28597,
+	64333: 28661,
+	64334: 28677,
+	64335: 28679,
+	64336: 28712,
+	64337: 28805,
+	64338: 28843,
+	64339: 28943,
+	64340: 28932,
+	64341: 29020,
+	64342: 28998,
+	64343: 28999,
+	64344: 64021,
+	64345: 29121,
+	64346: 29182,
+	64347: 29361,
+	64348: 29374,
+	64349: 29476,
+	64350: 64022,
+	64351: 29559,
+	64352: 29629,
+	64353: 29641,
+	64354: 29654,
+	64355: 29667,
+	64356: 29650,
+	64357: 29703,
+	64358: 29685,
+	64359: 29734,
+	64360: 29738,
+	64361: 29737,
+	64362: 29742,
+	64363: 29794,
+	64364: 29833,
+	64365: 29855,
+	64366: 29953,
+	64367: 30063,
+	64368: 30338,
+	64369: 30364,
+	64370: 30366,
+	64371: 30363,
+	64372: 30374,
+	64373: 64023,
+	64374: 30534,
+	64375: 21167,
+	64376: 30753,
+	64377: 30798,
+	64378: 30820,
+	64379: 30842,
+	64380: 31024,
+	64381: 64024,
+	64382: 64025,
+	64384: 64026,
+	64385: 31124,
+	64386: 64027,
+	64387: 31131,
+	64388: 31441,
+	64389: 31463,
+	64390: 64028,
+	64391: 31467,
+	64392: 31646,
+	64393: 64029,
+	64394: 32072,
+	64395: 32092,
+	64396: 32183,
+	64397: 32160,
+	64398: 32214,
+	64399: 32338,
+	64400: 32583,
+	64401: 32673,
+	64402: 64030,
+	64403: 33537,
+	64404: 33634,
+	64405: 33663,
+	64406: 33735,
+	64407: 33782,
+	64408: 33864,
+	64409: 33972,
+	64410: 34131,
+	64411: 34137,
+	64412: 34155,
+	64413: 64031,
+	64414: 34224,
+	64415: 64032,
+	64416: 64033,
+	64417: 34823,
+	64418: 35061,
+	64419: 35346,
+	64420: 35383,
+	64421: 35449,
+	64422: 35495,
+	64423: 35518,
+	64424: 35551,
+	64425: 64034,
+	64426: 35574,
+	64427: 35667,
+	64428: 35711,
+	64429: 36080,
+	64430: 36084,
+	64431: 36114,
+	64432: 36214,
+	64433: 64035,
+	64434: 36559,
+	64435: 64036,
+	64436: 64037,
+	64437: 36967,
+	64438: 37086,
+	64439: 64038,
+	64440: 37141,
+	64441: 37159,
+	64442: 37338,
+	64443: 37335,
+	64444: 37342,
+	64445: 37357,
+	64446: 37358,
+	64447: 37348,
+	64448: 37349,
+	64449: 37382,
+	64450: 37392,
+	64451: 37386,
+	64452: 37434,
+	64453: 37440,
+	64454: 37436,
+	64455: 37454,
+	64456: 37465,
+	64457: 37457,
+	64458: 37433,
+	64459: 37479,
+	64460: 37543,
+	64461: 37495,
+	64462: 37496,
+	64463: 37607,
+	64464: 37591,
+	64465: 37593,
+	64466: 37584,
+	64467: 64039,
+	64468: 37589,
+	64469: 37600,
+	64470: 37587,
+	64471: 37669,
+	64472: 37665,
+	64473: 37627,
+	64474: 64040,
+	64475: 37662,
+	64476: 37631,
+	64477: 37661,
+	64478: 37634,
+	64479: 37744,
+	64480: 37719,
+	64481: 37796,
+	64482: 37830,
+	64483: 37854,
+	64484: 37880,
+	64485: 37937,
+	64486: 37957,
+	64487: 37960,
+	64488: 38290,
+	64489: 63964,
+	64490: 64041,
+	64491: 38557,
+	64492: 38575,
+	64493: 38707,
+	64494: 38715,
+	64495: 38723,
+	64496: 38733,
+	64497: 38735,
+	64498: 38737,
+	64499: 38741,
+	64500: 38999,
+	64501: 39013,
+	64502: 64042,
+	64503: 64043,
+	64504: 39207,
+	64505: 64044,
+	64506: 39326,
+	64507: 39502,
+	64508: 39641,
+	64576: 39644,
+	64577: 39797,
+	64578: 39794,
+	64579: 39823,
+	64580: 39857,
+	64581: 39867,
+	64582: 39936,
+	64583: 40304,
+	64584: 40299,
+	64585: 64045,
+	64586: 40473,
+	64587: 40657
+};
+
+/**
+ * @author takahiro / https://github.com/takahirox
+ */
+
+function DataViewEx( buffer, littleEndian ) {
+
+	this.dv = new DataView( buffer );
+	this.offset = 0;
+	this.littleEndian = ( littleEndian !== undefined ) ? littleEndian : true;
+	this.encoder = new CharsetEncoder();
+
+}
+
+DataViewEx.prototype = {
+
+	constructor: DataViewEx,
+
+	getInt8: function () {
+
+		var value = this.dv.getInt8( this.offset );
+		this.offset += 1;
+		return value;
+
+	},
+
+	getInt8Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getInt8() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint8: function () {
+
+		var value = this.dv.getUint8( this.offset );
+		this.offset += 1;
+		return value;
+
+	},
+
+	getUint8Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getUint8() );
+
+		}
+
+		return a;
+
+	},
+
+
+	getInt16: function () {
+
+		var value = this.dv.getInt16( this.offset, this.littleEndian );
+		this.offset += 2;
+		return value;
+
+	},
+
+	getInt16Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getInt16() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint16: function () {
+
+		var value = this.dv.getUint16( this.offset, this.littleEndian );
+		this.offset += 2;
+		return value;
+
+	},
+
+	getUint16Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getUint16() );
+
+		}
+
+		return a;
+
+	},
+
+	getInt32: function () {
+
+		var value = this.dv.getInt32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getInt32Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getInt32() );
+
+		}
+
+		return a;
+
+	},
+
+	getUint32: function () {
+
+		var value = this.dv.getUint32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getUint32Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getUint32() );
+
+		}
+
+		return a;
+
+	},
+
+	getFloat32: function () {
+
+		var value = this.dv.getFloat32( this.offset, this.littleEndian );
+		this.offset += 4;
+		return value;
+
+	},
+
+	getFloat32Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getFloat32() );
+
+		}
+
+		return a;
+
+	},
+
+	getFloat64: function () {
+
+		var value = this.dv.getFloat64( this.offset, this.littleEndian );
+		this.offset += 8;
+		return value;
+
+	},
+
+	getFloat64Array: function ( size ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getFloat64() );
+
+		}
+
+		return a;
+
+	},
+
+	getIndex: function ( type, isUnsigned ) {
+
+		switch ( type ) {
+
+			case 1:
+				return ( isUnsigned === true ) ? this.getUint8() : this.getInt8();
+
+			case 2:
+				return ( isUnsigned === true ) ? this.getUint16() : this.getInt16();
+
+			case 4:
+				return this.getInt32(); // No Uint32
+
+			default:
+				throw 'unknown number type ' + type + ' exception.';
+
+		}
+
+	},
+
+	getIndexArray: function ( type, size, isUnsigned ) {
+
+		var a = [];
+
+		for ( var i = 0; i < size; i ++ ) {
+
+			a.push( this.getIndex( type, isUnsigned ) );
+
+		}
+
+		return a;
+
+	},
+
+	getChars: function ( size ) {
+
+		var str = '';
+
+		while ( size > 0 ) {
+
+			var value = this.getUint8();
+			size --;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			str += String.fromCharCode( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size --;
+
+		}
+
+		return str;
+
+	},
+
+	getSjisStringsAsUnicode: function ( size ) {
+
+		var a = [];
+
+		while ( size > 0 ) {
+
+			var value = this.getUint8();
+			size --;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			a.push( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size --;
+
+		}
+
+		return this.encoder.s2u( new Uint8Array( a ) );
+
+	},
+
+	getUnicodeStrings: function ( size ) {
+
+		var str = '';
+
+		while ( size > 0 ) {
+
+			var value = this.getUint16();
+			size -= 2;
+
+			if ( value === 0 ) {
+
+				break;
+
+			}
+
+			str += String.fromCharCode( value );
+
+		}
+
+		while ( size > 0 ) {
+
+			this.getUint8();
+			size --;
+
+		}
+
+		return str;
+
+	},
+
+	getTextBuffer: function () {
+
+		var size = this.getUint32();
+		return this.getUnicodeStrings( size );
+
+	}
+
+};
+
+/**
+ * @author takahiro / https://github.com/takahirox
+ */
+
+function DataCreationHelper() {
+}
+
+DataCreationHelper.prototype = {
+
+	constructor: DataCreationHelper,
+
+	leftToRightVector3: function ( v ) {
+
+		v[ 2 ] = - v[ 2 ];
+
+	},
+
+	leftToRightQuaternion: function ( q ) {
+
+		q[ 0 ] = - q[ 0 ];
+		q[ 1 ] = - q[ 1 ];
+
+	},
+
+	leftToRightEuler: function ( r ) {
+
+		r[ 0 ] = - r[ 0 ];
+		r[ 1 ] = - r[ 1 ];
+
+	},
+
+	leftToRightIndexOrder: function ( p ) {
+
+		var tmp = p[ 2 ];
+		p[ 2 ] = p[ 0 ];
+		p[ 0 ] = tmp;
+
+	},
+
+	leftToRightVector3Range: function ( v1, v2 ) {
+
+		var tmp = - v2[ 2 ];
+		v2[ 2 ] = - v1[ 2 ];
+		v1[ 2 ] = tmp;
+
+	},
+
+	leftToRightEulerRange: function ( r1, r2 ) {
+
+		var tmp1 = - r2[ 0 ];
+		var tmp2 = - r2[ 1 ];
+		r2[ 0 ] = - r1[ 0 ];
+		r2[ 1 ] = - r1[ 1 ];
+		r1[ 0 ] = tmp1;
+		r1[ 1 ] = tmp2;
+
+	}
+
+};
+
+/**
+ * @author takahiro / https://github.com/takahirox
+ */
+
+function Parser() {
+}
+
+Parser.prototype.parsePmd = function ( buffer, leftToRight ) {
+
+	var pmd = {};
+	var dv = new DataViewEx( buffer );
+
+	pmd.metadata = {};
+	pmd.metadata.format = 'pmd';
+	pmd.metadata.coordinateSystem = 'left';
+
+	var parseHeader = function () {
+
+		var metadata = pmd.metadata;
+		metadata.magic = dv.getChars( 3 );
+
+		if ( metadata.magic !== 'Pmd' ) {
+
+			throw 'PMD file magic is not Pmd, but ' + metadata.magic;
+
+		}
+
+		metadata.version = dv.getFloat32();
+		metadata.modelName = dv.getSjisStringsAsUnicode( 20 );
+		metadata.comment = dv.getSjisStringsAsUnicode( 256 );
+
+	};
+
+	var parseVertices = function () {
+
+		var parseVertex = function () {
+
+			var p = {};
+			p.position = dv.getFloat32Array( 3 );
+			p.normal = dv.getFloat32Array( 3 );
+			p.uv = dv.getFloat32Array( 2 );
+			p.skinIndices = dv.getUint16Array( 2 );
+			p.skinWeights = [ dv.getUint8() / 100 ];
+			p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+			p.edgeFlag = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.vertexCount = dv.getUint32();
+
+		pmd.vertices = [];
+
+		for ( var i = 0; i < metadata.vertexCount; i ++ ) {
+
+			pmd.vertices.push( parseVertex() );
+
+		}
+
+	};
+
+	var parseFaces = function () {
+
+		var parseFace = function () {
+
+			var p = {};
+			p.indices = dv.getUint16Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.faceCount = dv.getUint32() / 3;
+
+		pmd.faces = [];
+
+		for ( var i = 0; i < metadata.faceCount; i ++ ) {
+
+			pmd.faces.push( parseFace() );
+
+		}
+
+	};
+
+	var parseMaterials = function () {
+
+		var parseMaterial = function () {
+
+			var p = {};
+			p.diffuse = dv.getFloat32Array( 4 );
+			p.shininess = dv.getFloat32();
+			p.specular = dv.getFloat32Array( 3 );
+			p.ambient = dv.getFloat32Array( 3 );
+			p.toonIndex = dv.getInt8();
+			p.edgeFlag = dv.getUint8();
+			p.faceCount = dv.getUint32() / 3;
+			p.fileName = dv.getSjisStringsAsUnicode( 20 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.materialCount = dv.getUint32();
+
+		pmd.materials = [];
+
+		for ( var i = 0; i < metadata.materialCount; i ++ ) {
+
+			pmd.materials.push( parseMaterial() );
+
+		}
+
+	};
+
+	var parseBones = function () {
+
+		var parseBone = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			p.parentIndex = dv.getInt16();
+			p.tailIndex = dv.getInt16();
+			p.type = dv.getUint8();
+			p.ikIndex = dv.getInt16();
+			p.position = dv.getFloat32Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.boneCount = dv.getUint16();
+
+		pmd.bones = [];
+
+		for ( var i = 0; i < metadata.boneCount; i ++ ) {
+
+			pmd.bones.push( parseBone() );
+
+		}
+
+	};
+
+	var parseIks = function () {
+
+		var parseIk = function () {
+
+			var p = {};
+			p.target = dv.getUint16();
+			p.effector = dv.getUint16();
+			p.linkCount = dv.getUint8();
+			p.iteration = dv.getUint16();
+			p.maxAngle = dv.getFloat32();
+
+			p.links = [];
+			for ( var i = 0; i < p.linkCount; i ++ ) {
+
+				var link = {};
+				link.index = dv.getUint16();
+				p.links.push( link );
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.ikCount = dv.getUint16();
+
+		pmd.iks = [];
+
+		for ( var i = 0; i < metadata.ikCount; i ++ ) {
+
+			pmd.iks.push( parseIk() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			p.elementCount = dv.getUint32();
+			p.type = dv.getUint8();
+
+			p.elements = [];
+			for ( var i = 0; i < p.elementCount; i ++ ) {
+
+				p.elements.push( {
+					index: dv.getUint32(),
+					position: dv.getFloat32Array( 3 )
+				} );
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.morphCount = dv.getUint16();
+
+		pmd.morphs = [];
+
+		for ( var i = 0; i < metadata.morphCount; i ++ ) {
+
+			pmd.morphs.push( parseMorph() );
+
+		}
+
+
+	};
+
+	var parseMorphFrames = function () {
+
+		var parseMorphFrame = function () {
+
+			var p = {};
+			p.index = dv.getUint16();
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.morphFrameCount = dv.getUint8();
+
+		pmd.morphFrames = [];
+
+		for ( var i = 0; i < metadata.morphFrameCount; i ++ ) {
+
+			pmd.morphFrames.push( parseMorphFrame() );
+
+		}
+
+	};
+
+	var parseBoneFrameNames = function () {
+
+		var parseBoneFrameName = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 50 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.boneFrameNameCount = dv.getUint8();
+
+		pmd.boneFrameNames = [];
+
+		for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) {
+
+			pmd.boneFrameNames.push( parseBoneFrameName() );
+
+		}
+
+	};
+
+	var parseBoneFrames = function () {
+
+		var parseBoneFrame = function () {
+
+			var p = {};
+			p.boneIndex = dv.getInt16();
+			p.frameIndex = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.boneFrameCount = dv.getUint32();
+
+		pmd.boneFrames = [];
+
+		for ( var i = 0; i < metadata.boneFrameCount; i ++ ) {
+
+			pmd.boneFrames.push( parseBoneFrame() );
+
+		}
+
+	};
+
+	var parseEnglishHeader = function () {
+
+		var metadata = pmd.metadata;
+		metadata.englishCompatibility = dv.getUint8();
+
+		if ( metadata.englishCompatibility > 0 ) {
+
+			metadata.englishModelName = dv.getSjisStringsAsUnicode( 20 );
+			metadata.englishComment = dv.getSjisStringsAsUnicode( 256 );
+
+		}
+
+	};
+
+	var parseEnglishBoneNames = function () {
+
+		var parseEnglishBoneName = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+
+		if ( metadata.englishCompatibility === 0 ) {
+
+			return;
+
+		}
+
+		pmd.englishBoneNames = [];
+
+		for ( var i = 0; i < metadata.boneCount; i ++ ) {
+
+			pmd.englishBoneNames.push( parseEnglishBoneName() );
+
+		}
+
+	};
+
+	var parseEnglishMorphNames = function () {
+
+		var parseEnglishMorphName = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+
+		if ( metadata.englishCompatibility === 0 ) {
+
+			return;
+
+		}
+
+		pmd.englishMorphNames = [];
+
+		for ( var i = 0; i < metadata.morphCount - 1; i ++ ) {
+
+			pmd.englishMorphNames.push( parseEnglishMorphName() );
+
+		}
+
+	};
+
+	var parseEnglishBoneFrameNames = function () {
+
+		var parseEnglishBoneFrameName = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 50 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+
+		if ( metadata.englishCompatibility === 0 ) {
+
+			return;
+
+		}
+
+		pmd.englishBoneFrameNames = [];
+
+		for ( var i = 0; i < metadata.boneFrameNameCount; i ++ ) {
+
+			pmd.englishBoneFrameNames.push( parseEnglishBoneFrameName() );
+
+		}
+
+	};
+
+	var parseToonTextures = function () {
+
+		var parseToonTexture = function () {
+
+			var p = {};
+			p.fileName = dv.getSjisStringsAsUnicode( 100 );
+			return p;
+
+		};
+
+		pmd.toonTextures = [];
+
+		for ( var i = 0; i < 10; i ++ ) {
+
+			pmd.toonTextures.push( parseToonTexture() );
+
+		}
+
+	};
+
+	var parseRigidBodies = function () {
+
+		var parseRigidBody = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			p.boneIndex = dv.getInt16();
+			p.groupIndex = dv.getUint8();
+			p.groupTarget = dv.getUint16();
+			p.shapeType = dv.getUint8();
+			p.width = dv.getFloat32();
+			p.height = dv.getFloat32();
+			p.depth = dv.getFloat32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 3 );
+			p.weight = dv.getFloat32();
+			p.positionDamping = dv.getFloat32();
+			p.rotationDamping = dv.getFloat32();
+			p.restitution = dv.getFloat32();
+			p.friction = dv.getFloat32();
+			p.type = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.rigidBodyCount = dv.getUint32();
+
+		pmd.rigidBodies = [];
+
+		for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) {
+
+			pmd.rigidBodies.push( parseRigidBody() );
+
+		}
+
+	};
+
+	var parseConstraints = function () {
+
+		var parseConstraint = function () {
+
+			var p = {};
+			p.name = dv.getSjisStringsAsUnicode( 20 );
+			p.rigidBodyIndex1 = dv.getUint32();
+			p.rigidBodyIndex2 = dv.getUint32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 3 );
+			p.translationLimitation1 = dv.getFloat32Array( 3 );
+			p.translationLimitation2 = dv.getFloat32Array( 3 );
+			p.rotationLimitation1 = dv.getFloat32Array( 3 );
+			p.rotationLimitation2 = dv.getFloat32Array( 3 );
+			p.springPosition = dv.getFloat32Array( 3 );
+			p.springRotation = dv.getFloat32Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmd.metadata;
+		metadata.constraintCount = dv.getUint32();
+
+		pmd.constraints = [];
+
+		for ( var i = 0; i < metadata.constraintCount; i ++ ) {
+
+			pmd.constraints.push( parseConstraint() );
+
+		}
+
+	};
+
+	parseHeader();
+	parseVertices();
+	parseFaces();
+	parseMaterials();
+	parseBones();
+	parseIks();
+	parseMorphs();
+	parseMorphFrames();
+	parseBoneFrameNames();
+	parseBoneFrames();
+	parseEnglishHeader();
+	parseEnglishBoneNames();
+	parseEnglishMorphNames();
+	parseEnglishBoneFrameNames();
+	parseToonTextures();
+	parseRigidBodies();
+	parseConstraints();
+
+	if ( leftToRight === true ) this.leftToRightModel( pmd );
+
+	// console.log( pmd ); // for console debug
+
+	return pmd;
+
+};
+
+Parser.prototype.parsePmx = function ( buffer, leftToRight ) {
+
+	var pmx = {};
+	var dv = new DataViewEx( buffer );
+
+	pmx.metadata = {};
+	pmx.metadata.format = 'pmx';
+	pmx.metadata.coordinateSystem = 'left';
+
+	var parseHeader = function () {
+
+		var metadata = pmx.metadata;
+		metadata.magic = dv.getChars( 4 );
+
+		// Note: don't remove the last blank space.
+		if ( metadata.magic !== 'PMX ' ) {
+
+			throw 'PMX file magic is not PMX , but ' + metadata.magic;
+
+		}
+
+		metadata.version = dv.getFloat32();
+
+		if ( metadata.version !== 2.0 && metadata.version !== 2.1 ) {
+
+			throw 'PMX version ' + metadata.version + ' is not supported.';
+
+		}
+
+		metadata.headerSize = dv.getUint8();
+		metadata.encoding = dv.getUint8();
+		metadata.additionalUvNum = dv.getUint8();
+		metadata.vertexIndexSize = dv.getUint8();
+		metadata.textureIndexSize = dv.getUint8();
+		metadata.materialIndexSize = dv.getUint8();
+		metadata.boneIndexSize = dv.getUint8();
+		metadata.morphIndexSize = dv.getUint8();
+		metadata.rigidBodyIndexSize = dv.getUint8();
+		metadata.modelName = dv.getTextBuffer();
+		metadata.englishModelName = dv.getTextBuffer();
+		metadata.comment = dv.getTextBuffer();
+		metadata.englishComment = dv.getTextBuffer();
+
+	};
+
+	var parseVertices = function () {
+
+		var parseVertex = function () {
+
+			var p = {};
+			p.position = dv.getFloat32Array( 3 );
+			p.normal = dv.getFloat32Array( 3 );
+			p.uv = dv.getFloat32Array( 2 );
+
+			p.auvs = [];
+
+			for ( var i = 0; i < pmx.metadata.additionalUvNum; i ++ ) {
+
+				p.auvs.push( dv.getFloat32Array( 4 ) );
+
+			}
+
+			p.type = dv.getUint8();
+
+			var indexSize = metadata.boneIndexSize;
+
+			if ( p.type === 0 ) { // BDEF1
+
+				p.skinIndices = dv.getIndexArray( indexSize, 1 );
+				p.skinWeights = [ 1.0 ];
+
+			} else if ( p.type === 1 ) { // BDEF2
+
+				p.skinIndices = dv.getIndexArray( indexSize, 2 );
+				p.skinWeights = dv.getFloat32Array( 1 );
+				p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+
+			} else if ( p.type === 2 ) { // BDEF4
+
+				p.skinIndices = dv.getIndexArray( indexSize, 4 );
+				p.skinWeights = dv.getFloat32Array( 4 );
+
+			} else if ( p.type === 3 ) { // SDEF
+
+				p.skinIndices = dv.getIndexArray( indexSize, 2 );
+				p.skinWeights = dv.getFloat32Array( 1 );
+				p.skinWeights.push( 1.0 - p.skinWeights[ 0 ] );
+
+				p.skinC = dv.getFloat32Array( 3 );
+				p.skinR0 = dv.getFloat32Array( 3 );
+				p.skinR1 = dv.getFloat32Array( 3 );
+
+				// SDEF is not supported yet and is handled as BDEF2 so far.
+				// TODO: SDEF support
+				p.type = 1;
+
+			} else {
+
+				throw 'unsupport bone type ' + p.type + ' exception.';
+
+			}
+
+			p.edgeRatio = dv.getFloat32();
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.vertexCount = dv.getUint32();
+
+		pmx.vertices = [];
+
+		for ( var i = 0; i < metadata.vertexCount; i ++ ) {
+
+			pmx.vertices.push( parseVertex() );
+
+		}
+
+	};
+
+	var parseFaces = function () {
+
+		var parseFace = function () {
+
+			var p = {};
+			p.indices = dv.getIndexArray( metadata.vertexIndexSize, 3, true );
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.faceCount = dv.getUint32() / 3;
+
+		pmx.faces = [];
+
+		for ( var i = 0; i < metadata.faceCount; i ++ ) {
+
+			pmx.faces.push( parseFace() );
+
+		}
+
+	};
+
+	var parseTextures = function () {
+
+		var parseTexture = function () {
+
+			return dv.getTextBuffer();
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.textureCount = dv.getUint32();
+
+		pmx.textures = [];
+
+		for ( var i = 0; i < metadata.textureCount; i ++ ) {
+
+			pmx.textures.push( parseTexture() );
+
+		}
+
+	};
+
+	var parseMaterials = function () {
+
+		var parseMaterial = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.diffuse = dv.getFloat32Array( 4 );
+			p.specular = dv.getFloat32Array( 3 );
+			p.shininess = dv.getFloat32();
+			p.ambient = dv.getFloat32Array( 3 );
+			p.flag = dv.getUint8();
+			p.edgeColor = dv.getFloat32Array( 4 );
+			p.edgeSize = dv.getFloat32();
+			p.textureIndex = dv.getIndex( pmx.metadata.textureIndexSize );
+			p.envTextureIndex = dv.getIndex( pmx.metadata.textureIndexSize );
+			p.envFlag = dv.getUint8();
+			p.toonFlag = dv.getUint8();
+
+			if ( p.toonFlag === 0 ) {
+
+				p.toonIndex = dv.getIndex( pmx.metadata.textureIndexSize );
+
+			} else if ( p.toonFlag === 1 ) {
+
+				p.toonIndex = dv.getInt8();
+
+			} else {
+
+				throw 'unknown toon flag ' + p.toonFlag + ' exception.';
+
+			}
+
+			p.comment = dv.getTextBuffer();
+			p.faceCount = dv.getUint32() / 3;
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.materialCount = dv.getUint32();
+
+		pmx.materials = [];
+
+		for ( var i = 0; i < metadata.materialCount; i ++ ) {
+
+			pmx.materials.push( parseMaterial() );
+
+		}
+
+	};
+
+	var parseBones = function () {
+
+		var parseBone = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.position = dv.getFloat32Array( 3 );
+			p.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize );
+			p.transformationClass = dv.getUint32();
+			p.flag = dv.getUint16();
+
+			if ( p.flag & 0x1 ) {
+
+				p.connectIndex = dv.getIndex( pmx.metadata.boneIndexSize );
+
+			} else {
+
+				p.offsetPosition = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x100 || p.flag & 0x200 ) {
+
+				// Note: I don't think Grant is an appropriate name
+				//       but I found that some English translated MMD tools use this term
+				//       so I've named it Grant so far.
+				//       I'd rename to more appropriate name from Grant later.
+				var grant = {};
+
+				grant.isLocal = ( p.flag & 0x80 ) !== 0 ? true : false;
+				grant.affectRotation = ( p.flag & 0x100 ) !== 0 ? true : false;
+				grant.affectPosition = ( p.flag & 0x200 ) !== 0 ? true : false;
+				grant.parentIndex = dv.getIndex( pmx.metadata.boneIndexSize );
+				grant.ratio = dv.getFloat32();
+
+				p.grant = grant;
+
+			}
+
+			if ( p.flag & 0x400 ) {
+
+				p.fixAxis = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x800 ) {
+
+				p.localXVector = dv.getFloat32Array( 3 );
+				p.localZVector = dv.getFloat32Array( 3 );
+
+			}
+
+			if ( p.flag & 0x2000 ) {
+
+				p.key = dv.getUint32();
+
+			}
+
+			if ( p.flag & 0x20 ) {
+
+				var ik = {};
+
+				ik.effector = dv.getIndex( pmx.metadata.boneIndexSize );
+				ik.target = null;
+				ik.iteration = dv.getUint32();
+				ik.maxAngle = dv.getFloat32();
+				ik.linkCount = dv.getUint32();
+				ik.links = [];
+
+				for ( var i = 0; i < ik.linkCount; i ++ ) {
+
+					var link = {};
+					link.index = dv.getIndex( pmx.metadata.boneIndexSize );
+					link.angleLimitation = dv.getUint8();
+
+					if ( link.angleLimitation === 1 ) {
+
+						link.lowerLimitationAngle = dv.getFloat32Array( 3 );
+						link.upperLimitationAngle = dv.getFloat32Array( 3 );
+
+					}
+
+					ik.links.push( link );
+
+				}
+
+				p.ik = ik;
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.boneCount = dv.getUint32();
+
+		pmx.bones = [];
+
+		for ( var i = 0; i < metadata.boneCount; i ++ ) {
+
+			pmx.bones.push( parseBone() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.panel = dv.getUint8();
+			p.type = dv.getUint8();
+			p.elementCount = dv.getUint32();
+			p.elements = [];
+
+			for ( var i = 0; i < p.elementCount; i ++ ) {
+
+				if ( p.type === 0 ) { // group morph
+
+					var m = {};
+					m.index = dv.getIndex( pmx.metadata.morphIndexSize );
+					m.ratio = dv.getFloat32();
+					p.elements.push( m );
+
+				} else if ( p.type === 1 ) { // vertex morph
+
+					var m = {};
+					m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true );
+					m.position = dv.getFloat32Array( 3 );
+					p.elements.push( m );
+
+				} else if ( p.type === 2 ) { // bone morph
+
+					var m = {};
+					m.index = dv.getIndex( pmx.metadata.boneIndexSize );
+					m.position = dv.getFloat32Array( 3 );
+					m.rotation = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				} else if ( p.type === 3 ) { // uv morph
+
+					var m = {};
+					m.index = dv.getIndex( pmx.metadata.vertexIndexSize, true );
+					m.uv = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				} else if ( p.type === 4 ) { // additional uv1
+
+					// TODO: implement
+
+				} else if ( p.type === 5 ) { // additional uv2
+
+					// TODO: implement
+
+				} else if ( p.type === 6 ) { // additional uv3
+
+					// TODO: implement
+
+				} else if ( p.type === 7 ) { // additional uv4
+
+					// TODO: implement
+
+				} else if ( p.type === 8 ) { // material morph
+
+					var m = {};
+					m.index = dv.getIndex( pmx.metadata.materialIndexSize );
+					m.type = dv.getUint8();
+					m.diffuse = dv.getFloat32Array( 4 );
+					m.specular = dv.getFloat32Array( 3 );
+					m.shininess = dv.getFloat32();
+					m.ambient = dv.getFloat32Array( 3 );
+					m.edgeColor = dv.getFloat32Array( 4 );
+					m.edgeSize = dv.getFloat32();
+					m.textureColor = dv.getFloat32Array( 4 );
+					m.sphereTextureColor = dv.getFloat32Array( 4 );
+					m.toonColor = dv.getFloat32Array( 4 );
+					p.elements.push( m );
+
+				}
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.morphCount = dv.getUint32();
+
+		pmx.morphs = [];
+
+		for ( var i = 0; i < metadata.morphCount; i ++ ) {
+
+			pmx.morphs.push( parseMorph() );
+
+		}
+
+	};
+
+	var parseFrames = function () {
+
+		var parseFrame = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.type = dv.getUint8();
+			p.elementCount = dv.getUint32();
+			p.elements = [];
+
+			for ( var i = 0; i < p.elementCount; i ++ ) {
+
+				var e = {};
+				e.target = dv.getUint8();
+				e.index = ( e.target === 0 ) ? dv.getIndex( pmx.metadata.boneIndexSize ) : dv.getIndex( pmx.metadata.morphIndexSize );
+				p.elements.push( e );
+
+			}
+
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.frameCount = dv.getUint32();
+
+		pmx.frames = [];
+
+		for ( var i = 0; i < metadata.frameCount; i ++ ) {
+
+			pmx.frames.push( parseFrame() );
+
+		}
+
+	};
+
+	var parseRigidBodies = function () {
+
+		var parseRigidBody = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.boneIndex = dv.getIndex( pmx.metadata.boneIndexSize );
+			p.groupIndex = dv.getUint8();
+			p.groupTarget = dv.getUint16();
+			p.shapeType = dv.getUint8();
+			p.width = dv.getFloat32();
+			p.height = dv.getFloat32();
+			p.depth = dv.getFloat32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 3 );
+			p.weight = dv.getFloat32();
+			p.positionDamping = dv.getFloat32();
+			p.rotationDamping = dv.getFloat32();
+			p.restitution = dv.getFloat32();
+			p.friction = dv.getFloat32();
+			p.type = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.rigidBodyCount = dv.getUint32();
+
+		pmx.rigidBodies = [];
+
+		for ( var i = 0; i < metadata.rigidBodyCount; i ++ ) {
+
+			pmx.rigidBodies.push( parseRigidBody() );
+
+		}
+
+	};
+
+	var parseConstraints = function () {
+
+		var parseConstraint = function () {
+
+			var p = {};
+			p.name = dv.getTextBuffer();
+			p.englishName = dv.getTextBuffer();
+			p.type = dv.getUint8();
+			p.rigidBodyIndex1 = dv.getIndex( pmx.metadata.rigidBodyIndexSize );
+			p.rigidBodyIndex2 = dv.getIndex( pmx.metadata.rigidBodyIndexSize );
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 3 );
+			p.translationLimitation1 = dv.getFloat32Array( 3 );
+			p.translationLimitation2 = dv.getFloat32Array( 3 );
+			p.rotationLimitation1 = dv.getFloat32Array( 3 );
+			p.rotationLimitation2 = dv.getFloat32Array( 3 );
+			p.springPosition = dv.getFloat32Array( 3 );
+			p.springRotation = dv.getFloat32Array( 3 );
+			return p;
+
+		};
+
+		var metadata = pmx.metadata;
+		metadata.constraintCount = dv.getUint32();
+
+		pmx.constraints = [];
+
+		for ( var i = 0; i < metadata.constraintCount; i ++ ) {
+
+			pmx.constraints.push( parseConstraint() );
+
+		}
+
+	};
+
+	parseHeader();
+	parseVertices();
+	parseFaces();
+	parseTextures();
+	parseMaterials();
+	parseBones();
+	parseMorphs();
+	parseFrames();
+	parseRigidBodies();
+	parseConstraints();
+
+	if ( leftToRight === true ) this.leftToRightModel( pmx );
+
+	// console.log( pmx ); // for console debug
+
+	return pmx;
+
+};
+
+Parser.prototype.parseVmd = function ( buffer, leftToRight ) {
+
+	var vmd = {};
+	var dv = new DataViewEx( buffer );
+
+	vmd.metadata = {};
+	vmd.metadata.coordinateSystem = 'left';
+
+	var parseHeader = function () {
+
+		var metadata = vmd.metadata;
+		metadata.magic = dv.getChars( 30 );
+
+		if ( metadata.magic !== 'Vocaloid Motion Data 0002' ) {
+
+			throw 'VMD file magic is not Vocaloid Motion Data 0002, but ' + metadata.magic;
+
+		}
+
+		metadata.name = dv.getSjisStringsAsUnicode( 20 );
+
+	};
+
+	var parseMotions = function () {
+
+		var parseMotion = function () {
+
+			var p = {};
+			p.boneName = dv.getSjisStringsAsUnicode( 15 );
+			p.frameNum = dv.getUint32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 4 );
+			p.interpolation = dv.getUint8Array( 64 );
+			return p;
+
+		};
+
+		var metadata = vmd.metadata;
+		metadata.motionCount = dv.getUint32();
+
+		vmd.motions = [];
+		for ( var i = 0; i < metadata.motionCount; i ++ ) {
+
+			vmd.motions.push( parseMotion() );
+
+		}
+
+	};
+
+	var parseMorphs = function () {
+
+		var parseMorph = function () {
+
+			var p = {};
+			p.morphName = dv.getSjisStringsAsUnicode( 15 );
+			p.frameNum = dv.getUint32();
+			p.weight = dv.getFloat32();
+			return p;
+
+		};
+
+		var metadata = vmd.metadata;
+		metadata.morphCount = dv.getUint32();
+
+		vmd.morphs = [];
+		for ( var i = 0; i < metadata.morphCount; i ++ ) {
+
+			vmd.morphs.push( parseMorph() );
+
+		}
+
+	};
+
+	var parseCameras = function () {
+
+		var parseCamera = function () {
+
+			var p = {};
+			p.frameNum = dv.getUint32();
+			p.distance = dv.getFloat32();
+			p.position = dv.getFloat32Array( 3 );
+			p.rotation = dv.getFloat32Array( 3 );
+			p.interpolation = dv.getUint8Array( 24 );
+			p.fov = dv.getUint32();
+			p.perspective = dv.getUint8();
+			return p;
+
+		};
+
+		var metadata = vmd.metadata;
+		metadata.cameraCount = dv.getUint32();
+
+		vmd.cameras = [];
+		for ( var i = 0; i < metadata.cameraCount; i ++ ) {
+
+			vmd.cameras.push( parseCamera() );
+
+		}
+
+	};
+
+	parseHeader();
+	parseMotions();
+	parseMorphs();
+	parseCameras();
+
+	if ( leftToRight === true ) this.leftToRightVmd( vmd );
+
+	// console.log( vmd ); // for console debug
+
+	return vmd;
+
+};
+
+Parser.prototype.parseVpd = function ( text, leftToRight ) {
+
+	var vpd = {};
+
+	vpd.metadata = {};
+	vpd.metadata.coordinateSystem = 'left';
+
+	vpd.bones = [];
+
+	var commentPatternG = /\/\/\w*(\r|\n|\r\n)/g;
+	var newlinePattern = /\r|\n|\r\n/;
+
+	var lines = text.replace( commentPatternG, '' ).split( newlinePattern );
+
+	function throwError() {
+
+		throw 'the file seems not vpd file.';
+
+	}
+
+	function checkMagic() {
+
+		if ( lines[ 0 ] !== 'Vocaloid Pose Data file' ) {
+
+			throwError();
+
+		}
+
+	}
+
+	function parseHeader() {
+
+		if ( lines.length < 4 ) {
+
+			throwError();
+
+		}
+
+		vpd.metadata.parentFile = lines[ 2 ];
+		vpd.metadata.boneCount = parseInt( lines[ 3 ] );
+
+	}
+
+	function parseBones() {
+
+		var boneHeaderPattern = /^\s*(Bone[0-9]+)\s*\{\s*(.*)$/;
+		var boneVectorPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/;
+		var boneQuaternionPattern = /^\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*,\s*(-?[0-9]+\.[0-9]+)\s*;/;
+		var boneFooterPattern = /^\s*}/;
+
+		var bones = vpd.bones;
+		var n = null;
+		var v = null;
+		var q = null;
+
+		for ( var i = 4; i < lines.length; i ++ ) {
+
+			var line = lines[ i ];
+
+			var result;
+
+			result = line.match( boneHeaderPattern );
+
+			if ( result !== null ) {
+
+				if ( n !== null ) {
+
+					throwError();
+
+				}
+
+				n = result[ 2 ];
+
+			}
+
+			result = line.match( boneVectorPattern );
+
+			if ( result !== null ) {
+
+				if ( v !== null ) {
+
+					throwError();
+
+				}
+
+				v = [
+
+					parseFloat( result[ 1 ] ),
+					parseFloat( result[ 2 ] ),
+					parseFloat( result[ 3 ] )
+
+				];
+
+			}
+
+			result = line.match( boneQuaternionPattern );
+
+			if ( result !== null ) {
+
+				if ( q !== null ) {
+
+					throwError();
+
+				}
+
+				q = [
+
+					parseFloat( result[ 1 ] ),
+					parseFloat( result[ 2 ] ),
+					parseFloat( result[ 3 ] ),
+					parseFloat( result[ 4 ] )
+
+				];
+
+
+			}
+
+			result = line.match( boneFooterPattern );
+
+			if ( result !== null ) {
+
+				if ( n === null || v === null || q === null ) {
+
+					throwError();
+
+				}
+
+				bones.push( {
+
+					name: n,
+					translation: v,
+					quaternion: q
+
+				} );
+
+				n = null;
+				v = null;
+				q = null;
+
+			}
+
+		}
+
+		if ( n !== null || v !== null || q !== null ) {
+
+			throwError();
+
+		}
+
+	}
+
+	checkMagic();
+	parseHeader();
+	parseBones();
+
+	if ( leftToRight === true ) this.leftToRightVpd( vpd );
+
+	// console.log( vpd );  // for console debug
+
+	return vpd;
+
+};
+
+Parser.prototype.mergeVmds = function ( vmds ) {
+
+	var v = {};
+	v.metadata = {};
+	v.metadata.name = vmds[ 0 ].metadata.name;
+	v.metadata.coordinateSystem = vmds[ 0 ].metadata.coordinateSystem;
+	v.metadata.motionCount = 0;
+	v.metadata.morphCount = 0;
+	v.metadata.cameraCount = 0;
+	v.motions = [];
+	v.morphs = [];
+	v.cameras = [];
+
+	for ( var i = 0; i < vmds.length; i ++ ) {
+
+		var v2 = vmds[ i ];
+
+		v.metadata.motionCount += v2.metadata.motionCount;
+		v.metadata.morphCount += v2.metadata.morphCount;
+		v.metadata.cameraCount += v2.metadata.cameraCount;
+
+		for ( var j = 0; j < v2.metadata.motionCount; j ++ ) {
+
+			v.motions.push( v2.motions[ j ] );
+
+		}
+
+		for ( var j = 0; j < v2.metadata.morphCount; j ++ ) {
+
+			v.morphs.push( v2.morphs[ j ] );
+
+		}
+
+		for ( var j = 0; j < v2.metadata.cameraCount; j ++ ) {
+
+			v.cameras.push( v2.cameras[ j ] );
+
+		}
+
+	}
+
+	return v;
+
+};
+
+Parser.prototype.leftToRightModel = function ( model ) {
+
+	if ( model.metadata.coordinateSystem === 'right' ) {
+
+		return;
+
+	}
+
+	model.metadata.coordinateSystem = 'right';
+
+	var helper = new DataCreationHelper();
+
+	for ( var i = 0; i < model.metadata.vertexCount; i ++ ) {
+
+		helper.leftToRightVector3( model.vertices[ i ].position );
+		helper.leftToRightVector3( model.vertices[ i ].normal );
+
+	}
+
+	for ( var i = 0; i < model.metadata.faceCount; i ++ ) {
+
+		helper.leftToRightIndexOrder( model.faces[ i ].indices );
+
+	}
+
+	for ( var i = 0; i < model.metadata.boneCount; i ++ ) {
+
+		helper.leftToRightVector3( model.bones[ i ].position );
+
+	}
+
+	// TODO: support other morph for PMX
+	for ( var i = 0; i < model.metadata.morphCount; i ++ ) {
+
+		var m = model.morphs[ i ];
+
+		if ( model.metadata.format === 'pmx' && m.type !== 1 ) {
+
+			// TODO: implement
+			continue;
+
+		}
+
+		for ( var j = 0; j < m.elements.length; j ++ ) {
+
+			helper.leftToRightVector3( m.elements[ j ].position );
+
+		}
+
+	}
+
+	for ( var i = 0; i < model.metadata.rigidBodyCount; i ++ ) {
+
+		helper.leftToRightVector3( model.rigidBodies[ i ].position );
+		helper.leftToRightEuler( model.rigidBodies[ i ].rotation );
+
+	}
+
+	for ( var i = 0; i < model.metadata.constraintCount; i ++ ) {
+
+		helper.leftToRightVector3( model.constraints[ i ].position );
+		helper.leftToRightEuler( model.constraints[ i ].rotation );
+		helper.leftToRightVector3Range( model.constraints[ i ].translationLimitation1, model.constraints[ i ].translationLimitation2 );
+		helper.leftToRightEulerRange( model.constraints[ i ].rotationLimitation1, model.constraints[ i ].rotationLimitation2 );
+
+	}
+
+};
+
+Parser.prototype.leftToRightVmd = function ( vmd ) {
+
+	if ( vmd.metadata.coordinateSystem === 'right' ) {
+
+		return;
+
+	}
+
+	vmd.metadata.coordinateSystem = 'right';
+
+	var helper = new DataCreationHelper();
+
+	for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) {
+
+		helper.leftToRightVector3( vmd.motions[ i ].position );
+		helper.leftToRightQuaternion( vmd.motions[ i ].rotation );
+
+	}
+
+	for ( var i = 0; i < vmd.metadata.cameraCount; i ++ ) {
+
+		helper.leftToRightVector3( vmd.cameras[ i ].position );
+		helper.leftToRightEuler( vmd.cameras[ i ].rotation );
+
+	}
+
+};
+
+Parser.prototype.leftToRightVpd = function ( vpd ) {
+
+	if ( vpd.metadata.coordinateSystem === 'right' ) {
+
+		return;
+
+	}
+
+	vpd.metadata.coordinateSystem = 'right';
+
+	var helper = new DataCreationHelper();
+
+	for ( var i = 0; i < vpd.bones.length; i ++ ) {
+
+		helper.leftToRightVector3( vpd.bones[ i ].translation );
+		helper.leftToRightQuaternion( vpd.bones[ i ].quaternion );
+
+	}
+
+};
+
+var MMDParser = { CharsetEncoder, Parser };
+
+export { MMDParser };

+ 10 - 0
examples/jsm/lights/LightProbeGenerator.d.ts

@@ -0,0 +1,10 @@
+import {
+  CubeTexture,
+  LightProbe
+} from '../../../src/Three';
+
+export namespace LightProbeGenerator {
+
+  export function fromCubeTexture(cubeTexture: CubeTexture): LightProbe;
+
+}

+ 130 - 0
examples/jsm/lights/LightProbeGenerator.js

@@ -0,0 +1,130 @@
+/**
+ * @author WestLangley / http://github.com/WestLangley
+ */
+
+import {
+	Color,
+	LightProbe,
+	SphericalHarmonics3,
+	Vector3
+} from "../../../build/three.module.js";
+
+var LightProbeGenerator = {
+
+	// https://www.ppsloan.org/publications/StupidSH36.pdf
+	fromCubeTexture: function ( cubeTexture ) {
+
+		var norm, lengthSq, weight, totalWeight = 0;
+
+		var coord = new Vector3();
+
+		var dir = new Vector3();
+
+		var color = new Color();
+
+		var shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+
+		var sh = new SphericalHarmonics3();
+		var shCoefficients = sh.coefficients;
+
+		for ( var faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
+
+			var image = cubeTexture.image[ faceIndex ];
+
+			var width = image.width;
+			var height = image.height;
+
+			var canvas = document.createElement( 'canvas' );
+
+			canvas.width = width;
+			canvas.height = height;
+
+			var context = canvas.getContext( '2d' );
+
+			context.drawImage( image, 0, 0, width, height );
+
+			var imageData = context.getImageData( 0, 0, width, height );
+
+			var data = imageData.data;
+
+			var imageWidth = imageData.width; // assumed to be square
+
+			var pixelSize = 2 / imageWidth;
+
+			for ( var i = 0, il = data.length; i < il; i += 4 ) { // RGBA assumed
+
+				// pixel color
+				color.setRGB( data[ i ] / 255, data[ i + 1 ] / 255, data[ i + 2 ] / 255 );
+
+				// convert to linear color space
+				color.copySRGBToLinear( color );
+
+				// pixel coordinate on unit cube
+
+				var pixelIndex = i / 4;
+
+				var col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
+
+				var row = 1 - ( Math.floor( pixelIndex / imageWidth ) + 0.5 ) * pixelSize;
+
+				switch ( faceIndex ) {
+
+					case 0: coord.set( - 1, row, - col ); break;
+
+					case 1: coord.set( 1, row, col ); break;
+
+					case 2: coord.set( - col, 1, - row ); break;
+
+					case 3: coord.set( - col, - 1, row ); break;
+
+					case 4: coord.set( - col, row, 1 ); break;
+
+					case 5: coord.set( col, row, - 1 ); break;
+
+				}
+
+				// weight assigned to this pixel
+
+				lengthSq = coord.lengthSq();
+
+				weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
+
+				totalWeight += weight;
+
+				// direction vector to this pixel
+				dir.copy( coord ).normalize();
+
+				// evaluate SH basis functions in direction dir
+				SphericalHarmonics3.getBasisAt( dir, shBasis );
+
+				// accummuulate
+				for ( var j = 0; j < 9; j ++ ) {
+
+					shCoefficients[ j ].x += shBasis[ j ] * color.r * weight;
+					shCoefficients[ j ].y += shBasis[ j ] * color.g * weight;
+					shCoefficients[ j ].z += shBasis[ j ] * color.b * weight;
+
+				}
+
+			}
+
+		}
+
+		// normalize
+		norm = ( 4 * Math.PI ) / totalWeight;
+
+		for ( var j = 0; j < 9; j ++ ) {
+
+			shCoefficients[ j ].x *= norm;
+			shCoefficients[ j ].y *= norm;
+			shCoefficients[ j ].z *= norm;
+
+		}
+
+		return new LightProbe( sh );
+
+	}
+
+};
+
+export { LightProbeGenerator };

+ 50 - 0
examples/jsm/loaders/AWDLoader.d.ts

@@ -0,0 +1,50 @@
+import {
+  Bone,
+  BufferGeometry,
+  LoadingManager,
+  Material,
+  Matrix4,
+  Mesh,
+  Object3D,
+  Texture
+} from '../../../src/Three';
+
+export class AWDLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  materialFactory: any;
+  path: string;
+  trunk: Object3D;
+
+  getBlock(id: number): any;
+  load(url: string, onLoad: (result: Object3D) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadTexture(url: string): Texture;
+  setPath(path: string): this;
+  parse(data: ArrayBuffer): Object3D;
+  parseAnimatorSet(): object;
+  parseAttrValue(type: number, value: number): any;
+  parseContainer(): Object3D;
+  parseMaterial(): Material;
+  parseMatrix4(): Matrix4;
+  parseMeshData(): BufferGeometry[];
+  parseMeshInstance(): Mesh;
+  parseMeshPoseAnimation(poseOnly: boolean): null;
+  parseNextBlock(): void;
+  parseProperties(expected: object): object;
+  parseSkeleton(): Bone[];
+  parseSkeletonAnimation(): object[];
+  parseSkeletonPose(): Matrix4[];
+  parseTexture(): Texture;
+  parseUserAttributes(): null;
+  parseVertexAnimationSet(): object[];
+  readU8(): number;
+  readI8(): number;
+  readU16(): number;
+  readI16(): number;
+  readU32(): number;
+  readI32(): number;
+  readF32(): number;
+  readF64(): number;
+  readUTF(): string;
+  readUTFBytes(len: number): string;
+}

+ 1234 - 0
examples/jsm/loaders/AWDLoader.js

@@ -0,0 +1,1234 @@
+/**
+ * Author: Pierre Lepers
+ * Date: 09/12/2013 17:21
+ */
+
+import {
+	Bone,
+	BufferAttribute,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	ImageLoader,
+	Matrix4,
+	Mesh,
+	MeshPhongMaterial,
+	Object3D,
+	Texture
+} from "../../../build/three.module.js";
+
+var AWDLoader = ( function () {
+
+	var //UNCOMPRESSED = 0,
+		//DEFLATE = 1,
+		//LZMA = 2,
+
+		AWD_FIELD_INT8 = 1,
+		AWD_FIELD_INT16 = 2,
+		AWD_FIELD_INT32 = 3,
+		AWD_FIELD_UINT8 = 4,
+		AWD_FIELD_UINT16 = 5,
+		AWD_FIELD_UINT32 = 6,
+		AWD_FIELD_FLOAT32 = 7,
+		AWD_FIELD_FLOAT64 = 8,
+		AWD_FIELD_BOOL = 21,
+		//AWD_FIELD_COLOR = 22,
+		AWD_FIELD_BADDR = 23,
+		//AWD_FIELD_STRING = 31,
+		//AWD_FIELD_BYTEARRAY = 32,
+		AWD_FIELD_VECTOR2x1 = 41,
+		AWD_FIELD_VECTOR3x1 = 42,
+		AWD_FIELD_VECTOR4x1 = 43,
+		AWD_FIELD_MTX3x2 = 44,
+		AWD_FIELD_MTX3x3 = 45,
+		AWD_FIELD_MTX4x3 = 46,
+		AWD_FIELD_MTX4x4 = 47,
+
+		BOOL = 21,
+		//COLOR = 22,
+		BADDR = 23,
+
+		//INT8 = 1,
+		//INT16 = 2,
+		//INT32 = 3,
+		UINT8 = 4,
+		UINT16 = 5,
+		//UINT32 = 6,
+		FLOAT32 = 7,
+		FLOAT64 = 8;
+
+	var littleEndian = true;
+
+	function Block() {
+
+		this.id = 0;
+		this.data = null;
+
+	}
+
+	function AWDProperties() {}
+
+	AWDProperties.prototype = {
+		set: function ( key, value ) {
+
+			this[ key ] = value;
+
+		},
+
+		get: function ( key, fallback ) {
+
+			if ( this.hasOwnProperty( key ) ) {
+
+				return this[ key ];
+
+			} else {
+
+				return fallback;
+
+			}
+
+		}
+	};
+
+	var AWDLoader = function ( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+		this.trunk = new Object3D();
+
+		this.materialFactory = undefined;
+
+		this._url = '';
+		this._baseDir = '';
+
+		this._data = undefined;
+		this._ptr = 0;
+
+		this._version = [];
+		this._streaming = false;
+		this._optimized_for_accuracy = false;
+		this._compression = 0;
+		this._bodylen = 0xFFFFFFFF;
+
+		this._blocks = [ new Block() ];
+
+		this._accuracyMatrix = false;
+		this._accuracyGeo = false;
+		this._accuracyProps = false;
+
+	};
+
+	AWDLoader.prototype = {
+
+		constructor: AWDLoader,
+
+		load: function ( url, onLoad, onProgress, onError ) {
+
+			var scope = this;
+
+			this._url = url;
+			this._baseDir = url.substr( 0, url.lastIndexOf( '/' ) + 1 );
+
+			var loader = new FileLoader( this.manager );
+			loader.setPath( this.path );
+			loader.setResponseType( 'arraybuffer' );
+			loader.load( url, function ( text ) {
+
+				onLoad( scope.parse( text ) );
+
+			}, onProgress, onError );
+
+		},
+
+		setPath: function ( value ) {
+
+			this.path = value;
+			return this;
+
+		},
+
+		parse: function ( data ) {
+
+			var blen = data.byteLength;
+
+			this._ptr = 0;
+			this._data = new DataView( data );
+
+			this._parseHeader( );
+
+			if ( this._compression != 0 ) {
+
+				console.error( 'compressed AWD not supported' );
+
+			}
+
+			if ( ! this._streaming && this._bodylen != data.byteLength - this._ptr ) {
+
+				console.error( 'AWDLoader: body len does not match file length', this._bodylen, blen - this._ptr );
+
+			}
+
+			while ( this._ptr < blen ) {
+
+				this.parseNextBlock();
+
+			}
+
+			return this.trunk;
+
+		},
+
+		parseNextBlock: function () {
+
+			var assetData,
+				ns, type, len, block,
+				blockId = this.readU32(),
+				ns = this.readU8(),
+				type = this.readU8(),
+				flags = this.readU8(),
+				len = this.readU32();
+
+
+			switch ( type ) {
+
+				case 1:
+					assetData = this.parseMeshData();
+					break;
+
+				case 22:
+					assetData = this.parseContainer();
+					break;
+
+				case 23:
+					assetData = this.parseMeshInstance();
+					break;
+
+				case 81:
+					assetData = this.parseMaterial();
+					break;
+
+				case 82:
+					assetData = this.parseTexture();
+					break;
+
+				case 101:
+					assetData = this.parseSkeleton();
+					break;
+
+				case 112:
+					assetData = this.parseMeshPoseAnimation( false );
+					break;
+
+				case 113:
+					assetData = this.parseVertexAnimationSet();
+					break;
+
+				case 102:
+					assetData = this.parseSkeletonPose();
+					break;
+
+				case 103:
+					assetData = this.parseSkeletonAnimation();
+					break;
+
+				case 122:
+					assetData = this.parseAnimatorSet();
+					break;
+
+				default:
+					//debug('Ignoring block!',type, len);
+					this._ptr += len;
+					break;
+
+			}
+
+
+			// Store block reference for later use
+			this._blocks[ blockId ] = block = new Block();
+			block.data = assetData;
+			block.id = blockId;
+
+
+		},
+
+		_parseHeader: function () {
+
+			var version = this._version,
+				awdmagic = ( this.readU8() << 16 ) | ( this.readU8() << 8 ) | this.readU8();
+
+			if ( awdmagic != 4282180 )
+				throw new Error( "AWDLoader - bad magic" );
+
+			version[ 0 ] = this.readU8();
+			version[ 1 ] = this.readU8();
+
+			var flags = this.readU16();
+
+			this._streaming = ( flags & 0x1 ) == 0x1;
+
+			if ( ( version[ 0 ] === 2 ) && ( version[ 1 ] === 1 ) ) {
+
+				this._accuracyMatrix = ( flags & 0x2 ) === 0x2;
+				this._accuracyGeo = ( flags & 0x4 ) === 0x4;
+				this._accuracyProps = ( flags & 0x8 ) === 0x8;
+
+			}
+
+			this._geoNrType = this._accuracyGeo ? FLOAT64 : FLOAT32;
+			this._matrixNrType = this._accuracyMatrix ? FLOAT64 : FLOAT32;
+			this._propsNrType = this._accuracyProps ? FLOAT64 : FLOAT32;
+
+			this._optimized_for_accuracy = ( flags & 0x2 ) === 0x2;
+
+			this._compression = this.readU8();
+			this._bodylen = this.readU32();
+
+		},
+
+		parseContainer: function () {
+
+			var parent,
+				ctr = new Object3D(),
+				par_id = this.readU32(),
+				mtx = this.parseMatrix4();
+
+			ctr.name = this.readUTF();
+			ctr.applyMatrix( mtx );
+
+			parent = this._blocks[ par_id ].data || this.trunk;
+			parent.add( ctr );
+
+			this.parseProperties( {
+				1: this._matrixNrType,
+				2: this._matrixNrType,
+				3: this._matrixNrType,
+				4: UINT8
+			} );
+
+			ctr.extra = this.parseUserAttributes();
+
+			return ctr;
+
+		},
+
+		parseMeshInstance: function () {
+
+			var name,
+				mesh, geometries, meshLen, meshes,
+				par_id, data_id,
+				mtx,
+				materials, mat, mat_id,
+				num_materials,
+				parent,
+				i;
+
+			par_id = this.readU32();
+			mtx = this.parseMatrix4();
+			name = this.readUTF();
+			data_id = this.readU32();
+			num_materials = this.readU16();
+
+			geometries = this.getBlock( data_id );
+
+			materials = [];
+
+			for ( i = 0; i < num_materials; i ++ ) {
+
+				mat_id = this.readU32();
+				mat = this.getBlock( mat_id );
+				materials.push( mat );
+
+			}
+
+			meshLen = geometries.length;
+			meshes = [];
+
+			// TODO : BufferGeometry don't support "geometryGroups" for now.
+			// so we create sub meshes for each groups
+			if ( meshLen > 1 ) {
+
+				mesh = new Object3D();
+				for ( i = 0; i < meshLen; i ++ ) {
+
+					var sm = new Mesh( geometries[ i ] );
+					meshes.push( sm );
+					mesh.add( sm );
+
+				}
+
+			} else {
+
+				mesh = new Mesh( geometries[ 0 ] );
+				meshes.push( mesh );
+
+			}
+
+			mesh.applyMatrix( mtx );
+			mesh.name = name;
+
+
+			parent = this.getBlock( par_id ) || this.trunk;
+			parent.add( mesh );
+
+
+			var matLen = materials.length;
+			var maxLen = Math.max( meshLen, matLen );
+			for ( i = 0; i < maxLen; i ++ )
+				meshes[ i % meshLen ].material = materials[ i % matLen ];
+
+
+			// Ignore for now
+			this.parseProperties( null );
+			mesh.extra = this.parseUserAttributes();
+
+			return mesh;
+
+		},
+
+		parseMaterial: function () {
+
+			var name,
+				type,
+				props,
+				mat,
+				attributes,
+				num_methods,
+				methods_parsed;
+
+			name = this.readUTF();
+			type = this.readU8();
+			num_methods = this.readU8();
+
+			//log( "AWDLoader parseMaterial ",name )
+
+			// Read material numerical properties
+			// (1=color, 2=bitmap url, 11=alpha_blending, 12=alpha_threshold, 13=repeat)
+			props = this.parseProperties( {
+				1: AWD_FIELD_INT32,
+				2: AWD_FIELD_BADDR,
+				11: AWD_FIELD_BOOL,
+				12: AWD_FIELD_FLOAT32,
+				13: AWD_FIELD_BOOL
+			} );
+
+			methods_parsed = 0;
+
+			while ( methods_parsed < num_methods ) {
+
+				var method_type = this.readU16();
+				this.parseProperties( null );
+				this.parseUserAttributes();
+
+			}
+
+			attributes = this.parseUserAttributes();
+
+			if ( this.materialFactory !== undefined ) {
+
+				mat = this.materialFactory( name );
+				if ( mat ) return mat;
+
+			}
+
+			mat = new MeshPhongMaterial();
+
+			if ( type === 1 ) {
+
+				// Color material
+				mat.color.setHex( props.get( 1, 0xcccccc ) );
+
+			} else if ( type === 2 ) {
+
+				// Bitmap material
+				var tex_addr = props.get( 2, 0 );
+				mat.map = this.getBlock( tex_addr );
+
+			}
+
+			mat.extra = attributes;
+			mat.alphaThreshold = props.get( 12, 0.0 );
+			mat.repeat = props.get( 13, false );
+
+
+			return mat;
+
+		},
+
+		parseTexture: function () {
+
+			var name = this.readUTF(),
+				type = this.readU8(),
+				asset,
+				data_len;
+
+			// External
+			if ( type === 0 ) {
+
+				data_len = this.readU32();
+				var url = this.readUTFBytes( data_len );
+				console.log( url );
+
+				asset = this.loadTexture( url );
+
+			} else {
+				// embed texture not supported
+			}
+			// Ignore for now
+			this.parseProperties( null );
+
+			this.parseUserAttributes();
+			return asset;
+
+		},
+
+		loadTexture: function ( url ) {
+
+			var tex = new Texture();
+
+			var loader = new ImageLoader( this.manager );
+
+			loader.load( this._baseDir + url, function ( image ) {
+
+				tex.image = image;
+				tex.needsUpdate = true;
+
+			} );
+
+			return tex;
+
+		},
+
+		parseSkeleton: function () {
+
+			// Array<Bone>
+			var name = this.readUTF(),
+				num_joints = this.readU16(),
+				skeleton = [],
+				joints_parsed = 0;
+
+			this.parseProperties( null );
+
+			while ( joints_parsed < num_joints ) {
+
+				var joint, ibp;
+
+				// Ignore joint id
+				this.readU16();
+
+				joint = new Bone();
+				joint.parent = this.readU16() - 1; // 0=null in AWD
+				joint.name = this.readUTF();
+
+				ibp = this.parseMatrix4();
+				joint.skinMatrix = ibp;
+
+				// Ignore joint props/attributes for now
+				this.parseProperties( null );
+				this.parseUserAttributes();
+
+				skeleton.push( joint );
+				joints_parsed ++;
+
+			}
+
+			// Discard attributes for now
+			this.parseUserAttributes();
+
+
+			return skeleton;
+
+		},
+
+		parseSkeletonPose: function () {
+
+			var name = this.readUTF();
+
+			var num_joints = this.readU16();
+			this.parseProperties( null );
+
+			// debug( 'parse Skeleton Pose. joints : ' + num_joints);
+
+			var pose = [];
+
+			var joints_parsed = 0;
+
+			while ( joints_parsed < num_joints ) {
+
+				var has_transform; //:uint;
+				var mtx_data;
+
+				has_transform = this.readU8();
+
+				if ( has_transform === 1 ) {
+
+					mtx_data = this.parseMatrix4();
+
+				} else {
+
+					mtx_data = new Matrix4();
+
+				}
+				pose[ joints_parsed ] = mtx_data;
+				joints_parsed ++;
+
+			}
+
+			// Skip attributes for now
+			this.parseUserAttributes();
+
+			return pose;
+
+		},
+
+		parseSkeletonAnimation: function () {
+
+			var frame_dur;
+			var pose_addr;
+			var pose;
+
+			var name = this.readUTF();
+
+			var clip = [];
+
+			var num_frames = this.readU16();
+			this.parseProperties( null );
+
+			var frames_parsed = 0;
+
+			// debug( 'parse Skeleton Animation. frames : ' + num_frames);
+
+			while ( frames_parsed < num_frames ) {
+
+				pose_addr = this.readU32();
+				frame_dur = this.readU16();
+
+				pose = this._blocks[ pose_addr ].data;
+				// debug( 'pose address ',pose[2].elements[12],pose[2].elements[13],pose[2].elements[14] );
+				clip.push( {
+					pose: pose,
+					duration: frame_dur
+				} );
+
+				frames_parsed ++;
+
+			}
+
+			if ( clip.length === 0 ) {
+
+				// debug("Could not this SkeletonClipNode, because no Frames where set.");
+				return;
+
+			}
+			// Ignore attributes for now
+			this.parseUserAttributes();
+			return clip;
+
+		},
+
+		parseVertexAnimationSet: function () {
+
+			var poseBlockAdress,
+				name = this.readUTF(),
+				num_frames = this.readU16(),
+				props = this.parseProperties( { 1: UINT16 } ),
+				frames_parsed = 0,
+				skeletonFrames = [];
+
+			while ( frames_parsed < num_frames ) {
+
+				poseBlockAdress = this.readU32();
+				skeletonFrames.push( this._blocks[ poseBlockAdress ].data );
+				frames_parsed ++;
+
+			}
+
+			this.parseUserAttributes();
+
+
+			return skeletonFrames;
+
+		},
+
+		parseAnimatorSet: function () {
+
+			var animSetBlockAdress; //:int
+
+			var targetAnimationSet; //:AnimationSetBase;
+			var name = this.readUTF();
+			var type = this.readU16();
+
+			var props = this.parseProperties( { 1: BADDR } );
+
+			animSetBlockAdress = this.readU32();
+			var targetMeshLength = this.readU16();
+
+			var meshAdresses = []; //:Vector.<uint> = new Vector.<uint>;
+
+			for ( var i = 0; i < targetMeshLength; i ++ )
+				meshAdresses.push( this.readU32() );
+
+			var activeState = this.readU16();
+			var autoplay = Boolean( this.readU8() );
+			this.parseUserAttributes();
+			this.parseUserAttributes();
+
+			var targetMeshes = []; //:Vector.<Mesh> = new Vector.<Mesh>;
+
+			for ( i = 0; i < meshAdresses.length; i ++ ) {
+
+				//			returnedArray = getAssetByID(meshAdresses[i], [AssetType.MESH]);
+				//			if (returnedArray[0])
+				targetMeshes.push( this._blocks[ meshAdresses[ i ] ].data );
+
+			}
+
+			targetAnimationSet = this._blocks[ animSetBlockAdress ].data;
+			var thisAnimator;
+
+			if ( type == 1 ) {
+
+
+				thisAnimator = {
+					animationSet: targetAnimationSet,
+					skeleton: this._blocks[ props.get( 1, 0 ) ].data
+				};
+
+			} else if ( type == 2 ) {
+				// debug( "vertex Anim???");
+			}
+
+
+			for ( i = 0; i < targetMeshes.length; i ++ ) {
+
+				targetMeshes[ i ].animator = thisAnimator;
+
+			}
+			// debug("Parsed a Animator: Name = " + name);
+
+			return thisAnimator;
+
+		},
+
+		parseMeshData: function () {
+
+			var name = this.readUTF(),
+				num_subs = this.readU16(),
+				geom,
+				subs_parsed = 0,
+				buffer,
+				geometries = [];
+
+			// Ignore for now
+			this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } );
+
+			// Loop through sub meshes
+			while ( subs_parsed < num_subs ) {
+
+				var sm_len, sm_end, attrib;
+
+				geom = new BufferGeometry();
+				geom.name = name;
+				geometries.push( geom );
+
+
+				sm_len = this.readU32();
+				sm_end = this._ptr + sm_len;
+
+
+				// Ignore for now
+				this.parseProperties( { 1: this._geoNrType, 2: this._geoNrType } );
+
+				// Loop through data streams
+				while ( this._ptr < sm_end ) {
+
+					var idx = 0,
+						str_type = this.readU8(),
+						str_ftype = this.readU8(),
+						str_len = this.readU32(),
+						str_end = str_len + this._ptr;
+
+					if ( str_type === 1 ) {
+
+						// VERTICES
+
+						buffer = new Float32Array( ( str_len / 12 ) * 3 );
+						attrib = new BufferAttribute( buffer, 3 );
+
+						geom.addAttribute( 'position', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = - this.readF32();
+							buffer[ idx + 1 ] = this.readF32();
+							buffer[ idx + 2 ] = this.readF32();
+							idx += 3;
+
+						}
+
+					} else if ( str_type === 2 ) {
+
+						// INDICES
+
+						buffer = new Uint16Array( str_len / 2 );
+						attrib = new BufferAttribute( buffer, 1 );
+						geom.setIndex( attrib );
+
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx + 1 ] = this.readU16();
+							buffer[ idx ] = this.readU16();
+							buffer[ idx + 2 ] = this.readU16();
+							idx += 3;
+
+						}
+
+					} else if ( str_type === 3 ) {
+
+						// UVS
+
+						buffer = new Float32Array( ( str_len / 8 ) * 2 );
+						attrib = new BufferAttribute( buffer, 2 );
+
+						geom.addAttribute( 'uv', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = this.readF32();
+							buffer[ idx + 1 ] = 1.0 - this.readF32();
+							idx += 2;
+
+						}
+
+					} else if ( str_type === 4 ) {
+
+						// NORMALS
+
+						buffer = new Float32Array( ( str_len / 12 ) * 3 );
+						attrib = new BufferAttribute( buffer, 3 );
+						geom.addAttribute( 'normal', attrib );
+						idx = 0;
+
+						while ( this._ptr < str_end ) {
+
+							buffer[ idx ] = - this.readF32();
+							buffer[ idx + 1 ] = this.readF32();
+							buffer[ idx + 2 ] = this.readF32();
+							idx += 3;
+
+						}
+
+					} else {
+
+						this._ptr = str_end;
+
+					}
+
+				}
+
+				this.parseUserAttributes();
+
+				geom.computeBoundingSphere();
+				subs_parsed ++;
+
+			}
+
+			//geom.computeFaceNormals();
+
+			this.parseUserAttributes();
+			//finalizeAsset(geom, name);
+
+			return geometries;
+
+		},
+
+		parseMeshPoseAnimation: function ( poseOnly ) {
+
+			var num_frames = 1,
+				num_submeshes,
+				frames_parsed,
+				subMeshParsed,
+
+				str_len,
+				str_end,
+				geom,
+				idx = 0,
+				clip = {},
+				num_Streams,
+				streamsParsed,
+				streamtypes = [],
+
+				props,
+				name = this.readUTF(),
+				geoAdress = this.readU32();
+
+			var mesh = this.getBlock( geoAdress );
+
+			if ( mesh === null ) {
+
+				console.log( "parseMeshPoseAnimation target mesh not found at:", geoAdress );
+				return;
+
+			}
+
+			geom = mesh.geometry;
+			geom.morphTargets = [];
+
+			if ( ! poseOnly )
+				num_frames = this.readU16();
+
+			num_submeshes = this.readU16();
+			num_Streams = this.readU16();
+
+			// debug("VA num_frames : ", num_frames );
+			// debug("VA num_submeshes : ", num_submeshes );
+			// debug("VA numstreams : ", num_Streams );
+
+			streamsParsed = 0;
+			while ( streamsParsed < num_Streams ) {
+
+				streamtypes.push( this.readU16() );
+				streamsParsed ++;
+
+			}
+			props = this.parseProperties( { 1: BOOL, 2: BOOL } );
+
+			clip.looping = props.get( 1, true );
+			clip.stitchFinalFrame = props.get( 2, false );
+
+			frames_parsed = 0;
+
+			while ( frames_parsed < num_frames ) {
+
+				this.readU16();
+				subMeshParsed = 0;
+
+				while ( subMeshParsed < num_submeshes ) {
+
+					streamsParsed = 0;
+					str_len = this.readU32();
+					str_end = this._ptr + str_len;
+
+					while ( streamsParsed < num_Streams ) {
+
+						if ( streamtypes[ streamsParsed ] === 1 ) {
+
+							//geom.addAttribute( 'morphTarget'+frames_parsed, Float32Array, str_len/12, 3 );
+							var buffer = new Float32Array( str_len / 4 );
+							geom.morphTargets.push( {
+								array: buffer
+							} );
+
+							//buffer = geom.attributes['morphTarget'+frames_parsed].array
+							idx = 0;
+
+							while ( this._ptr < str_end ) {
+
+								buffer[ idx ] = this.readF32();
+								buffer[ idx + 1 ] = this.readF32();
+								buffer[ idx + 2 ] = this.readF32();
+								idx += 3;
+
+							}
+
+
+							subMeshParsed ++;
+
+						} else
+							this._ptr = str_end;
+						streamsParsed ++;
+
+					}
+
+				}
+
+
+				frames_parsed ++;
+
+			}
+
+			this.parseUserAttributes();
+
+			return null;
+
+		},
+
+		getBlock: function ( id ) {
+
+			return this._blocks[ id ].data;
+
+		},
+
+		parseMatrix4: function () {
+
+			var mtx = new Matrix4();
+			var e = mtx.elements;
+
+			e[ 0 ] = this.readF32();
+			e[ 1 ] = this.readF32();
+			e[ 2 ] = this.readF32();
+			e[ 3 ] = 0.0;
+			//e[3] = 0.0;
+
+			e[ 4 ] = this.readF32();
+			e[ 5 ] = this.readF32();
+			e[ 6 ] = this.readF32();
+			//e[7] = this.readF32();
+			e[ 7 ] = 0.0;
+
+			e[ 8 ] = this.readF32();
+			e[ 9 ] = this.readF32();
+			e[ 10 ] = this.readF32();
+			//e[11] = this.readF32();
+			e[ 11 ] = 0.0;
+
+			e[ 12 ] = - this.readF32();
+			e[ 13 ] = this.readF32();
+			e[ 14 ] = this.readF32();
+			//e[15] = this.readF32();
+			e[ 15 ] = 1.0;
+			return mtx;
+
+		},
+
+		parseProperties: function ( expected ) {
+
+			var list_len = this.readU32();
+			var list_end = this._ptr + list_len;
+
+			var props = new AWDProperties();
+
+			if ( expected ) {
+
+				while ( this._ptr < list_end ) {
+
+					var key = this.readU16();
+					var len = this.readU32();
+					var type;
+
+					if ( expected.hasOwnProperty( key ) ) {
+
+						type = expected[ key ];
+						props.set( key, this.parseAttrValue( type, len ) );
+
+					} else {
+
+						this._ptr += len;
+
+					}
+
+				}
+
+			}
+
+			return props;
+
+		},
+
+		parseUserAttributes: function () {
+
+			// skip for now
+			this._ptr = this.readU32() + this._ptr;
+			return null;
+
+		},
+
+		parseAttrValue: function ( type, len ) {
+
+			var elem_len;
+			var read_func;
+
+			switch ( type ) {
+
+				case AWD_FIELD_INT8:
+					elem_len = 1;
+					read_func = this.readI8;
+					break;
+
+				case AWD_FIELD_INT16:
+					elem_len = 2;
+					read_func = this.readI16;
+					break;
+
+				case AWD_FIELD_INT32:
+					elem_len = 4;
+					read_func = this.readI32;
+					break;
+
+				case AWD_FIELD_BOOL:
+				case AWD_FIELD_UINT8:
+					elem_len = 1;
+					read_func = this.readU8;
+					break;
+
+				case AWD_FIELD_UINT16:
+					elem_len = 2;
+					read_func = this.readU16;
+					break;
+
+				case AWD_FIELD_UINT32:
+				case AWD_FIELD_BADDR:
+					elem_len = 4;
+					read_func = this.readU32;
+					break;
+
+				case AWD_FIELD_FLOAT32:
+					elem_len = 4;
+					read_func = this.readF32;
+					break;
+
+				case AWD_FIELD_FLOAT64:
+					elem_len = 8;
+					read_func = this.readF64;
+					break;
+
+				case AWD_FIELD_VECTOR2x1:
+				case AWD_FIELD_VECTOR3x1:
+				case AWD_FIELD_VECTOR4x1:
+				case AWD_FIELD_MTX3x2:
+				case AWD_FIELD_MTX3x3:
+				case AWD_FIELD_MTX4x3:
+				case AWD_FIELD_MTX4x4:
+					elem_len = 8;
+					read_func = this.readF64;
+					break;
+
+			}
+
+			if ( elem_len < len ) {
+
+				var list;
+				var num_read;
+				var num_elems;
+
+				list = [];
+				num_read = 0;
+				num_elems = len / elem_len;
+
+				while ( num_read < num_elems ) {
+
+					list.push( read_func.call( this ) );
+					num_read ++;
+
+				}
+
+				return list;
+
+			} else {
+
+				return read_func.call( this );
+
+			}
+
+		},
+
+		readU8: function () {
+
+			return this._data.getUint8( this._ptr ++ );
+
+		},
+		readI8: function () {
+
+			return this._data.getInt8( this._ptr ++ );
+
+		},
+		readU16: function () {
+
+			var a = this._data.getUint16( this._ptr, littleEndian );
+			this._ptr += 2;
+			return a;
+
+		},
+		readI16: function () {
+
+			var a = this._data.getInt16( this._ptr, littleEndian );
+			this._ptr += 2;
+			return a;
+
+		},
+		readU32: function () {
+
+			var a = this._data.getUint32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readI32: function () {
+
+			var a = this._data.getInt32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readF32: function () {
+
+			var a = this._data.getFloat32( this._ptr, littleEndian );
+			this._ptr += 4;
+			return a;
+
+		},
+		readF64: function () {
+
+			var a = this._data.getFloat64( this._ptr, littleEndian );
+			this._ptr += 8;
+			return a;
+
+		},
+
+		/**
+	 * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
+	 * @param {Array.<number>} bytes UTF-8 byte array.
+	 * @return {string} 16-bit Unicode string.
+	 */
+		readUTF: function () {
+
+			var len = this.readU16();
+			return this.readUTFBytes( len );
+
+		},
+
+		/**
+		 * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
+		 * @param {Array.<number>} bytes UTF-8 byte array.
+		 * @return {string} 16-bit Unicode string.
+		 */
+		readUTFBytes: function ( len ) {
+
+			// TODO(user): Use native implementations if/when available
+			var out = [], c = 0;
+
+			while ( out.length < len ) {
+
+				var c1 = this._data.getUint8( this._ptr ++, littleEndian );
+				if ( c1 < 128 ) {
+
+					out[ c ++ ] = String.fromCharCode( c1 );
+
+				} else if ( c1 > 191 && c1 < 224 ) {
+
+					var c2 = this._data.getUint8( this._ptr ++, littleEndian );
+					out[ c ++ ] = String.fromCharCode( ( c1 & 31 ) << 6 | c2 & 63 );
+
+				} else {
+
+					var c2 = this._data.getUint8( this._ptr ++, littleEndian );
+					var c3 = this._data.getUint8( this._ptr ++, littleEndian );
+					out[ c ++ ] = String.fromCharCode( ( c1 & 15 ) << 12 | ( c2 & 63 ) << 6 | c3 & 63 );
+
+				}
+
+			}
+			return out.join( '' );
+
+		}
+
+	};
+
+	return AWDLoader;
+
+} )();
+
+export { AWDLoader };

+ 11 - 7
examples/jsm/loaders/AssimpLoader.js

@@ -558,8 +558,7 @@ AssimpLoader.prototype = {
 			for ( var i in root.children ) {
 
 				var child = cloneTreeToBones( root.children[ i ], scene );
-				if ( child )
-					rootBone.add( child );
+				rootBone.add( child );
 
 			}
 
@@ -1334,6 +1333,10 @@ AssimpLoader.prototype = {
 
 		function aiScene() {
 
+			this.versionMajor = 0;
+			this.versionMinor = 0;
+			this.versionRevision = 0;
+			this.compileFlags = 0;
 			this.mFlags = 0;
 			this.mNumMeshes = 0;
 			this.mNumMaterials = 0;
@@ -2252,13 +2255,13 @@ AssimpLoader.prototype = {
 			extendStream( stream );
 			stream.Seek( 44, aiOrigin_CUR ); // signature
 			/*unsigned int versionMajor =*/
-			var versionMajor = Read_unsigned_int( stream );
+			pScene.versionMajor = Read_unsigned_int( stream );
 			/*unsigned int versionMinor =*/
-			var versionMinor = Read_unsigned_int( stream );
+			pScene.versionMinor = Read_unsigned_int( stream );
 			/*unsigned int versionRevision =*/
-			var versionRevision = Read_unsigned_int( stream );
+			pScene.versionRevision = Read_unsigned_int( stream );
 			/*unsigned int compileFlags =*/
-			var compileFlags = Read_unsigned_int( stream );
+			pScene.compileFlags = Read_unsigned_int( stream );
 			shortened = Read_uint16_t( stream ) > 0;
 			compressed = Read_uint16_t( stream ) > 0;
 			if ( shortened )
@@ -2280,10 +2283,11 @@ AssimpLoader.prototype = {
 			} else {
 
 				ReadBinaryScene( stream, pScene );
-				return pScene.toTHREE();
 
 			}
 
+			return pScene.toTHREE();
+
 		}
 
 		return InternReadFile( buffer );

+ 21 - 0
examples/jsm/loaders/DRACOLoader.d.ts

@@ -0,0 +1,21 @@
+import {
+  LoadingManager,
+  BufferGeometry,
+  TrianglesDrawModes
+} from '../../../src/Three';
+
+export class DRACOLoader {
+  constructor(manager?: LoadingManager);
+
+  static setDecoderPath(path: string): void;
+  static setDecoderConfig(config: object): void;
+  static getDecoderModule(): Promise<any>;
+  static releaseDecoderModule(): void;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): DRACOLoader;
+  setVerbosity(level: number): DRACOLoader;
+  setDrawMode(drawMode: TrianglesDrawModes): DRACOLoader;
+  setSkipDequantization(attributeName: 'position', skip?: boolean): DRACOLoader;
+  isVersionSupported(version: number, callback: (isVersionSupported: boolean) => any): void;
+}

+ 740 - 0
examples/jsm/loaders/DRACOLoader.js

@@ -0,0 +1,740 @@
+/** Copyright 2016 The Draco Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	Int16BufferAttribute,
+	Int32BufferAttribute,
+	Int8BufferAttribute,
+	TriangleStripDrawMode,
+	TrianglesDrawMode,
+	Uint16BufferAttribute,
+	Uint32BufferAttribute,
+	Uint8BufferAttribute
+} from "../../../build/three.module.js";
+
+/**
+ * @param {THREE.LoadingManager} manager
+ */
+var DRACOLoader = function ( manager ) {
+
+	this.timeLoaded = 0;
+	this.manager = manager || DefaultLoadingManager;
+	this.materials = null;
+	this.verbosity = 0;
+	this.attributeOptions = {};
+	this.drawMode = TrianglesDrawMode;
+	// Native Draco attribute type to Three.JS attribute type.
+	this.nativeAttributeMap = {
+		position: "POSITION",
+		normal: "NORMAL",
+		color: "COLOR",
+		uv: "TEX_COORD"
+	};
+
+};
+
+DRACOLoader.prototype = {
+	constructor: DRACOLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( this.path );
+		loader.setResponseType( "arraybuffer" );
+		loader.load(
+			url,
+			function ( blob ) {
+
+				scope.decodeDracoFile( blob, onLoad );
+
+			},
+			onProgress,
+			onError
+		);
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	setVerbosity: function ( level ) {
+
+		this.verbosity = level;
+		return this;
+
+	},
+
+	/**
+	 *  Sets desired mode for generated geometry indices.
+	 *  Can be either:
+	 *      TrianglesDrawMode
+	 *      TriangleStripDrawMode
+	 */
+	setDrawMode: function ( drawMode ) {
+
+		this.drawMode = drawMode;
+		return this;
+
+	},
+
+	/**
+	 * Skips dequantization for a specific attribute.
+	 * |attributeName| is the js name of the given attribute type.
+	 * The only currently supported |attributeName| is 'position', more may be
+	 * added in future.
+	 */
+	setSkipDequantization: function ( attributeName, skip ) {
+
+		var skipDequantization = true;
+		if ( typeof skip !== "undefined" ) skipDequantization = skip;
+		this.getAttributeOptions(
+			attributeName
+		).skipDequantization = skipDequantization;
+		return this;
+
+	},
+
+	/**
+	 * Decompresses a Draco buffer. Names of attributes (for ID and type maps)
+	 * must be one of the supported three.js types, including: position, color,
+	 * normal, uv, uv2, skinIndex, skinWeight.
+	 *
+	 * @param {ArrayBuffer} rawBuffer
+	 * @param {Function} callback
+	 * @param {Object|undefined} attributeUniqueIdMap Provides a pre-defined ID
+	 *     for each attribute in the geometry to be decoded. If given,
+	 *     `attributeTypeMap` is required and `nativeAttributeMap` will be
+	 *     ignored.
+	 * @param {Object|undefined} attributeTypeMap Provides a predefined data
+	 *     type (as a typed array constructor) for each attribute in the
+	 *     geometry to be decoded.
+	 */
+	decodeDracoFile: function (
+		rawBuffer,
+		callback,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		var scope = this;
+		DRACOLoader.getDecoderModule().then( function ( module ) {
+
+			scope.decodeDracoFileInternal(
+				rawBuffer,
+				module.decoder,
+				callback,
+				attributeUniqueIdMap,
+				attributeTypeMap
+			);
+
+		} );
+
+	},
+
+	decodeDracoFileInternal: function (
+		rawBuffer,
+		dracoDecoder,
+		callback,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		/*
+		 * Here is how to use Draco Javascript decoder and get the geometry.
+		 */
+		var buffer = new dracoDecoder.DecoderBuffer();
+		buffer.Init( new Int8Array( rawBuffer ), rawBuffer.byteLength );
+		var decoder = new dracoDecoder.Decoder();
+
+		/*
+		 * Determine what type is this file: mesh or point cloud.
+		 */
+		var geometryType = decoder.GetEncodedGeometryType( buffer );
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Loaded a mesh." );
+
+			}
+
+		} else if ( geometryType == dracoDecoder.POINT_CLOUD ) {
+
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Loaded a point cloud." );
+
+			}
+
+		} else {
+
+			var errorMsg = "DRACOLoader: Unknown geometry type.";
+			console.error( errorMsg );
+			throw new Error( errorMsg );
+
+		}
+		callback(
+			this.convertDracoGeometryTo3JS(
+				dracoDecoder,
+				decoder,
+				geometryType,
+				buffer,
+				attributeUniqueIdMap,
+				attributeTypeMap
+			)
+		);
+
+	},
+
+	addAttributeToGeometry: function (
+		dracoDecoder,
+		decoder,
+		dracoGeometry,
+		attributeName,
+		attributeType,
+		attribute,
+		geometry,
+		geometryBuffer
+	) {
+
+		if ( attribute.ptr === 0 ) {
+
+			var errorMsg = "DRACOLoader: No attribute " + attributeName;
+			console.error( errorMsg );
+			throw new Error( errorMsg );
+
+		}
+
+		var numComponents = attribute.num_components();
+		var numPoints = dracoGeometry.num_points();
+		var numValues = numPoints * numComponents;
+		var attributeData;
+		var TypedBufferAttribute;
+
+		switch ( attributeType ) {
+
+			case Float32Array:
+				attributeData = new dracoDecoder.DracoFloat32Array();
+				decoder.GetAttributeFloatForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Float32Array( numValues );
+				TypedBufferAttribute = Float32BufferAttribute;
+				break;
+
+			case Int8Array:
+				attributeData = new dracoDecoder.DracoInt8Array();
+				decoder.GetAttributeInt8ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int8Array( numValues );
+				TypedBufferAttribute = Int8BufferAttribute;
+				break;
+
+			case Int16Array:
+				attributeData = new dracoDecoder.DracoInt16Array();
+				decoder.GetAttributeInt16ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int16Array( numValues );
+				TypedBufferAttribute = Int16BufferAttribute;
+				break;
+
+			case Int32Array:
+				attributeData = new dracoDecoder.DracoInt32Array();
+				decoder.GetAttributeInt32ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Int32Array( numValues );
+				TypedBufferAttribute = Int32BufferAttribute;
+				break;
+
+			case Uint8Array:
+				attributeData = new dracoDecoder.DracoUInt8Array();
+				decoder.GetAttributeUInt8ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint8Array( numValues );
+				TypedBufferAttribute = Uint8BufferAttribute;
+				break;
+
+			case Uint16Array:
+				attributeData = new dracoDecoder.DracoUInt16Array();
+				decoder.GetAttributeUInt16ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint16Array( numValues );
+				TypedBufferAttribute = Uint16BufferAttribute;
+				break;
+
+			case Uint32Array:
+				attributeData = new dracoDecoder.DracoUInt32Array();
+				decoder.GetAttributeUInt32ForAllPoints(
+					dracoGeometry,
+					attribute,
+					attributeData
+				);
+				geometryBuffer[ attributeName ] = new Uint32Array( numValues );
+				TypedBufferAttribute = Uint32BufferAttribute;
+				break;
+
+			default:
+				var errorMsg = "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 TypedBufferAttribute( geometryBuffer[ attributeName ], numComponents )
+		);
+		dracoDecoder.destroy( attributeData );
+
+	},
+
+	convertDracoGeometryTo3JS: function (
+		dracoDecoder,
+		decoder,
+		geometryType,
+		buffer,
+		attributeUniqueIdMap,
+		attributeTypeMap
+	) {
+
+		// TODO: Should not assume native Draco attribute IDs apply.
+		if ( this.getAttributeOptions( "position" ).skipDequantization === true ) {
+
+			decoder.SkipAttributeTransform( dracoDecoder.POSITION );
+
+		}
+		var dracoGeometry;
+		var decodingStatus;
+		var start_time = performance.now();
+		if ( geometryType === dracoDecoder.TRIANGULAR_MESH ) {
+
+			dracoGeometry = new dracoDecoder.Mesh();
+			decodingStatus = decoder.DecodeBufferToMesh( buffer, dracoGeometry );
+
+		} else {
+
+			dracoGeometry = new dracoDecoder.PointCloud();
+			decodingStatus = decoder.DecodeBufferToPointCloud( buffer, dracoGeometry );
+
+		}
+		if ( ! decodingStatus.ok() || dracoGeometry.ptr == 0 ) {
+
+			var errorMsg = "DRACOLoader: Decoding failed: ";
+			errorMsg += decodingStatus.error_msg();
+			console.error( errorMsg );
+			dracoDecoder.destroy( decoder );
+			dracoDecoder.destroy( dracoGeometry );
+			throw new Error( errorMsg );
+
+		}
+
+		var decode_end = performance.now();
+		dracoDecoder.destroy( buffer );
+		/*
+		 * Example on how to retrieve mesh and attributes.
+		 */
+		var numFaces;
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			numFaces = dracoGeometry.num_faces();
+			if ( this.verbosity > 0 ) {
+
+				console.log( "Number of faces loaded: " + numFaces.toString() );
+
+			}
+
+		} else {
+
+			numFaces = 0;
+
+		}
+
+		var numPoints = dracoGeometry.num_points();
+		var numAttributes = dracoGeometry.num_attributes();
+		if ( this.verbosity > 0 ) {
+
+			console.log( "Number of points loaded: " + numPoints.toString() );
+			console.log( "Number of attributes loaded: " + numAttributes.toString() );
+
+		}
+
+		// Verify if there is position attribute.
+		// TODO: Should not assume native Draco attribute IDs apply.
+		var posAttId = decoder.GetAttributeId( dracoGeometry, dracoDecoder.POSITION );
+		if ( posAttId == - 1 ) {
+
+			var errorMsg = "DRACOLoader: No position attribute found.";
+			console.error( errorMsg );
+			dracoDecoder.destroy( decoder );
+			dracoDecoder.destroy( dracoGeometry );
+			throw new Error( errorMsg );
+
+		}
+		var posAttribute = decoder.GetAttribute( dracoGeometry, posAttId );
+
+		// Structure for converting to THREEJS geometry later.
+		var geometryBuffer = {};
+		// Import data to Three JS geometry.
+		var geometry = new BufferGeometry();
+
+		// Do not use both the native attribute map and a provided (e.g. glTF) map.
+		if ( attributeUniqueIdMap ) {
+
+			// Add attributes of user specified unique id. E.g. GLTF models.
+			for ( var attributeName in attributeUniqueIdMap ) {
+
+				var attributeType = attributeTypeMap[ attributeName ];
+				var attributeId = attributeUniqueIdMap[ attributeName ];
+				var attribute = decoder.GetAttributeByUniqueId(
+					dracoGeometry,
+					attributeId
+				);
+				this.addAttributeToGeometry(
+					dracoDecoder,
+					decoder,
+					dracoGeometry,
+					attributeName,
+					attributeType,
+					attribute,
+					geometry,
+					geometryBuffer
+				);
+
+			}
+
+		} else {
+
+			// Add native Draco attribute type to geometry.
+			for ( var attributeName in this.nativeAttributeMap ) {
+
+				var attId = decoder.GetAttributeId(
+					dracoGeometry,
+					dracoDecoder[ this.nativeAttributeMap[ attributeName ] ]
+				);
+				if ( attId !== - 1 ) {
+
+					if ( this.verbosity > 0 ) {
+
+						console.log( "Loaded " + attributeName + " attribute." );
+
+					}
+					var attribute = decoder.GetAttribute( dracoGeometry, attId );
+					this.addAttributeToGeometry(
+						dracoDecoder,
+						decoder,
+						dracoGeometry,
+						attributeName,
+						Float32Array,
+						attribute,
+						geometry,
+						geometryBuffer
+					);
+
+				}
+
+			}
+
+		}
+
+		// For mesh, we need to generate the faces.
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			if ( this.drawMode === TriangleStripDrawMode ) {
+
+				var stripsArray = new dracoDecoder.DracoInt32Array();
+				decoder.GetTriangleStripsFromMesh(
+					dracoGeometry,
+					stripsArray
+				);
+				geometryBuffer.indices = new Uint32Array( stripsArray.size() );
+				for ( var i = 0; i < stripsArray.size(); ++ i ) {
+
+					geometryBuffer.indices[ i ] = stripsArray.GetValue( i );
+
+				}
+				dracoDecoder.destroy( stripsArray );
+
+			} else {
+
+				var numIndices = numFaces * 3;
+				geometryBuffer.indices = new Uint32Array( numIndices );
+				var ia = new dracoDecoder.DracoInt32Array();
+				for ( var i = 0; i < numFaces; ++ i ) {
+
+					decoder.GetFaceFromMesh( dracoGeometry, i, ia );
+					var index = i * 3;
+					geometryBuffer.indices[ index ] = ia.GetValue( 0 );
+					geometryBuffer.indices[ index + 1 ] = ia.GetValue( 1 );
+					geometryBuffer.indices[ index + 2 ] = ia.GetValue( 2 );
+
+				}
+				dracoDecoder.destroy( ia );
+
+			}
+
+		}
+
+		geometry.drawMode = this.drawMode;
+		if ( geometryType == dracoDecoder.TRIANGULAR_MESH ) {
+
+			geometry.setIndex(
+				new ( geometryBuffer.indices.length > 65535
+					? Uint32BufferAttribute
+					: Uint16BufferAttribute )( geometryBuffer.indices, 1 )
+			);
+
+		}
+
+		// TODO: Should not assume native Draco attribute IDs apply.
+		// TODO: Can other attribute types be quantized?
+		var posTransform = new dracoDecoder.AttributeQuantizationTransform();
+		if ( posTransform.InitFromAttribute( posAttribute ) ) {
+
+			// Quantized attribute. Store the quantization parameters into the
+			// js attribute.
+			geometry.attributes[ "position" ].isQuantized = true;
+			geometry.attributes[ "position" ].maxRange = posTransform.range();
+			geometry.attributes[
+				"position"
+			].numQuantizationBits = posTransform.quantization_bits();
+			geometry.attributes[ "position" ].minValues = new Float32Array( 3 );
+			for ( var i = 0; i < 3; ++ i ) {
+
+				geometry.attributes[ "position" ].minValues[ i ] = posTransform.min_value(
+					i
+				);
+
+			}
+
+		}
+		dracoDecoder.destroy( posTransform );
+		dracoDecoder.destroy( decoder );
+		dracoDecoder.destroy( dracoGeometry );
+
+		this.decode_time = decode_end - start_time;
+		this.import_time = performance.now() - decode_end;
+
+		if ( this.verbosity > 0 ) {
+
+			console.log( "Decode time: " + this.decode_time );
+			console.log( "Import time: " + this.import_time );
+
+		}
+		return geometry;
+
+	},
+
+	isVersionSupported: function ( version, callback ) {
+
+		DRACOLoader.getDecoderModule().then( function ( module ) {
+
+			callback( module.decoder.isVersionSupported( version ) );
+
+		} );
+
+	},
+
+	getAttributeOptions: function ( attributeName ) {
+
+		if ( typeof this.attributeOptions[ attributeName ] === "undefined" )
+			this.attributeOptions[ attributeName ] = {};
+		return this.attributeOptions[ attributeName ];
+
+	}
+};
+
+DRACOLoader.decoderPath = "./";
+DRACOLoader.decoderConfig = {};
+DRACOLoader.decoderModulePromise = null;
+
+/**
+ * Sets the base path for decoder source files.
+ * @param {string} path
+ */
+DRACOLoader.setDecoderPath = function ( path ) {
+
+	DRACOLoader.decoderPath = path;
+
+};
+
+/**
+ * Sets decoder configuration and releases singleton decoder module. Module
+ * will be recreated with the next decoding call.
+ * @param {Object} config
+ */
+DRACOLoader.setDecoderConfig = function ( config ) {
+
+	var wasmBinary = DRACOLoader.decoderConfig.wasmBinary;
+	DRACOLoader.decoderConfig = config || {};
+	DRACOLoader.releaseDecoderModule();
+
+	// Reuse WASM binary.
+	if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary;
+
+};
+
+/**
+ * Releases the singleton DracoDecoderModule instance. Module will be recreated
+ * with the next decoding call.
+ */
+DRACOLoader.releaseDecoderModule = function () {
+
+	DRACOLoader.decoderModulePromise = null;
+
+};
+
+/**
+ * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule
+ * after testing for browser support. Returns Promise that resolves when
+ * module is available.
+ * @return {Promise<{decoder: DracoDecoderModule}>}
+ */
+DRACOLoader.getDecoderModule = function () {
+
+	var scope = this;
+	var path = DRACOLoader.decoderPath;
+	var config = DRACOLoader.decoderConfig;
+	var promise = DRACOLoader.decoderModulePromise;
+
+	if ( promise ) return promise;
+
+	// Load source files.
+	if ( typeof DracoDecoderModule !== "undefined" ) {
+
+		// Loaded externally.
+		promise = Promise.resolve();
+
+	} else if ( typeof WebAssembly !== "object" || config.type === "js" ) {
+
+		// Load with asm.js.
+		promise = DRACOLoader._loadScript( path + "draco_decoder.js" );
+
+	} else {
+
+		// Load with WebAssembly.
+		config.wasmBinaryFile = path + "draco_decoder.wasm";
+		promise = DRACOLoader._loadScript( path + "draco_wasm_wrapper.js" )
+			.then( function () {
+
+				return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile );
+
+			} )
+			.then( function ( wasmBinary ) {
+
+				config.wasmBinary = wasmBinary;
+
+			} );
+
+	}
+
+	// Wait for source files, then create and return a decoder.
+	promise = promise.then( function () {
+
+		return new Promise( function ( resolve ) {
+
+			config.onModuleLoaded = function ( decoder ) {
+
+				scope.timeLoaded = performance.now();
+				// Module is Promise-like. Wrap before resolving to avoid loop.
+				resolve( { decoder: decoder } );
+
+			};
+			DracoDecoderModule( config );
+
+		} );
+
+	} );
+
+	DRACOLoader.decoderModulePromise = promise;
+	return promise;
+
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadScript = function ( src ) {
+
+	var prevScript = document.getElementById( "decoder_script" );
+	if ( prevScript !== null ) {
+
+		prevScript.parentNode.removeChild( prevScript );
+
+	}
+	var head = document.getElementsByTagName( "head" )[ 0 ];
+	var script = document.createElement( "script" );
+	script.id = "decoder_script";
+	script.type = "text/javascript";
+	script.src = src;
+	return new Promise( function ( resolve ) {
+
+		script.onload = resolve;
+		head.appendChild( script );
+
+	} );
+
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadArrayBuffer = function ( src ) {
+
+	var loader = new FileLoader();
+	loader.setResponseType( "arraybuffer" );
+	return new Promise( function ( resolve, reject ) {
+
+		loader.load( src, resolve, undefined, reject );
+
+	} );
+
+};
+
+export { DRACOLoader };

+ 2 - 0
examples/jsm/loaders/EXRLoader.d.ts

@@ -16,6 +16,8 @@ export interface EXR {
 
 export class EXRLoader extends DataTextureLoader {
   constructor(manager?: LoadingManager);
+  type: TextureDataType;
 
   _parser(buffer: ArrayBuffer) : EXR;
+  setType(type: TextureDataType): this;
 }

+ 1 - 0
examples/jsm/loaders/FBXLoader.js

@@ -63,6 +63,7 @@ import {
 	VectorKeyframeTrack,
 	VertexColors
 } from "../../../build/three.module.js";
+import { Zlib } from "../libs/inflate.min.js";
 import { TGALoader } from "../loaders/TGALoader.js";
 import { NURBSCurve } from "../curves/NURBSCurve.js";
 

+ 47 - 111
examples/jsm/loaders/GLTFLoader.js

@@ -7,33 +7,23 @@
  */
 
 import {
-	AddEquation,
-	AlwaysDepth,
 	AnimationClip,
-	BackSide,
 	Bone,
 	BufferAttribute,
 	BufferGeometry,
-	Camera,
 	ClampToEdgeWrapping,
 	Color,
 	DefaultLoadingManager,
 	DirectionalLight,
 	DoubleSide,
-	DstAlphaFactor,
-	DstColorFactor,
-	EqualDepth,
 	FileLoader,
 	FrontSide,
-	GreaterEqualDepth,
 	Group,
 	InterleavedBuffer,
 	InterleavedBufferAttribute,
 	Interpolant,
 	InterpolateDiscrete,
 	InterpolateLinear,
-	LessDepth,
-	LessEqualDepth,
 	Line,
 	LineBasicMaterial,
 	LineLoop,
@@ -45,7 +35,6 @@ import {
 	LoaderUtils,
 	Material,
 	Math as _Math,
-	Matrix3,
 	Matrix4,
 	Mesh,
 	MeshBasicMaterial,
@@ -54,15 +43,8 @@ import {
 	NearestFilter,
 	NearestMipMapLinearFilter,
 	NearestMipMapNearestFilter,
-	NeverDepth,
-	NotEqualDepth,
 	NumberKeyframeTrack,
 	Object3D,
-	OneFactor,
-	OneMinusDstAlphaFactor,
-	OneMinusDstColorFactor,
-	OneMinusSrcAlphaFactor,
-	OneMinusSrcColorFactor,
 	OrthographicCamera,
 	PerspectiveCamera,
 	PointLight,
@@ -73,28 +55,20 @@ import {
 	RGBAFormat,
 	RGBFormat,
 	RepeatWrapping,
-	ReverseSubtractEquation,
 	Scene,
 	ShaderLib,
 	ShaderMaterial,
 	Skeleton,
 	SkinnedMesh,
 	SpotLight,
-	SrcAlphaFactor,
-	SrcAlphaSaturateFactor,
-	SrcColorFactor,
-	SubtractEquation,
 	Texture,
 	TextureLoader,
 	TriangleFanDrawMode,
 	TriangleStripDrawMode,
 	UniformsUtils,
 	Vector2,
-	Vector3,
-	Vector4,
 	VectorKeyframeTrack,
 	VertexColors,
-	ZeroFactor,
 	sRGBEncoding
 } from "../../../build/three.module.js";
 
@@ -160,9 +134,9 @@ var GLTFLoader = ( function () {
 			loader.setPath( this.path );
 			loader.setResponseType( 'arraybuffer' );
 
-			if ( this.options.crossOrigin === 'use-credentials' ) {
+			if ( scope.crossOrigin === 'use-credentials' ) {
 
-				this.fileLoader.setWithCredentials( true );
+				loader.setWithCredentials( true );
 
 			}
 
@@ -275,11 +249,11 @@ var GLTFLoader = ( function () {
 							break;
 
 						case EXTENSIONS.KHR_MATERIALS_UNLIT:
-							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension( json );
+							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
 							break;
 
 						case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
-							extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension( json );
+							extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension();
 							break;
 
 						case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
@@ -291,7 +265,7 @@ var GLTFLoader = ( function () {
 							break;
 
 						case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
-							extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension( json );
+							extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension();
 							break;
 
 						default:
@@ -511,8 +485,6 @@ var GLTFLoader = ( function () {
 	};
 
 	/* BINARY EXTENSION */
-
-	var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
 	var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
 	var BINARY_EXTENSION_HEADER_LENGTH = 12;
 	var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
@@ -1197,17 +1169,6 @@ var GLTFLoader = ( function () {
 		UNSIGNED_SHORT: 5123
 	};
 
-	var WEBGL_TYPE = {
-		5126: Number,
-		//35674: THREE.Matrix2,
-		35675: Matrix3,
-		35676: Matrix4,
-		35664: Vector2,
-		35665: Vector3,
-		35666: Vector4,
-		35678: Texture
-	};
-
 	var WEBGL_COMPONENT_TYPES = {
 		5120: Int8Array,
 		5121: Uint8Array,
@@ -1232,48 +1193,6 @@ var GLTFLoader = ( function () {
 		10497: RepeatWrapping
 	};
 
-	var WEBGL_SIDES = {
-		1028: BackSide, // Culling front
-		1029: FrontSide // Culling back
-		//1032: THREE.NoSide   // Culling front and back, what to do?
-	};
-
-	var WEBGL_DEPTH_FUNCS = {
-		512: NeverDepth,
-		513: LessDepth,
-		514: EqualDepth,
-		515: LessEqualDepth,
-		516: GreaterEqualDepth,
-		517: NotEqualDepth,
-		518: GreaterEqualDepth,
-		519: AlwaysDepth
-	};
-
-	var WEBGL_BLEND_EQUATIONS = {
-		32774: AddEquation,
-		32778: SubtractEquation,
-		32779: ReverseSubtractEquation
-	};
-
-	var WEBGL_BLEND_FUNCS = {
-		0: ZeroFactor,
-		1: OneFactor,
-		768: SrcColorFactor,
-		769: OneMinusSrcColorFactor,
-		770: SrcAlphaFactor,
-		771: OneMinusSrcAlphaFactor,
-		772: DstAlphaFactor,
-		773: OneMinusDstAlphaFactor,
-		774: DstColorFactor,
-		775: OneMinusDstColorFactor,
-		776: SrcAlphaSaturateFactor
-		// The followings are not supported by Three.js yet
-		//32769: CONSTANT_COLOR,
-		//32770: ONE_MINUS_CONSTANT_COLOR,
-		//32771: CONSTANT_ALPHA,
-		//32772: ONE_MINUS_CONSTANT_COLOR
-	};
-
 	var WEBGL_TYPE_SIZES = {
 		'SCALAR': 1,
 		'VEC2': 2,
@@ -1309,15 +1228,6 @@ var GLTFLoader = ( function () {
 		STEP: InterpolateDiscrete
 	};
 
-	var STATES_ENABLES = {
-		2884: 'CULL_FACE',
-		2929: 'DEPTH_TEST',
-		3042: 'BLEND',
-		3089: 'SCISSOR_TEST',
-		32823: 'POLYGON_OFFSET_FILL',
-		32926: 'SAMPLE_ALPHA_TO_COVERAGE'
-	};
-
 	var ALPHA_MODES = {
 		OPAQUE: 'OPAQUE',
 		MASK: 'MASK',
@@ -1610,19 +1520,6 @@ var GLTFLoader = ( function () {
 
 		}
 
-	}
-	function isObjectEqual( a, b ) {
-
-		if ( Object.keys( a ).length !== Object.keys( b ).length ) return false;
-
-		for ( var key in a ) {
-
-			if ( a[ key ] !== b[ key ] ) return false;
-
-		}
-
-		return true;
-
 	}
 
 	function createPrimitiveKey( primitiveDef ) {
@@ -2673,7 +2570,6 @@ var GLTFLoader = ( function () {
 
 		var parser = this;
 		var json = this.json;
-		var extensions = this.extensions;
 
 		var meshDef = json.meshes[ meshIndex ];
 		var primitives = meshDef.primitives;
@@ -2800,7 +2696,7 @@ var GLTFLoader = ( function () {
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
 	 * @param {number} cameraIndex
-	 * @return {Promise<Camera>}
+	 * @return {Promise<THREE.Camera>}
 	 */
 	GLTFParser.prototype.loadCamera = function ( cameraIndex ) {
 
@@ -2973,12 +2869,52 @@ var GLTFLoader = ( function () {
 
 				}
 
+				var outputArray = outputAccessor.array;
+
+				if ( outputAccessor.normalized ) {
+
+					var scale;
+
+					if ( outputArray.constructor === Int8Array ) {
+
+						scale = 1 / 127;
+
+					} else if ( outputArray.constructor === Uint8Array ) {
+
+						scale = 1 / 255;
+
+					} else if ( outputArray.constructor == Int16Array ) {
+
+						scale = 1 / 32767;
+
+					} else if ( outputArray.constructor === Uint16Array ) {
+
+						scale = 1 / 65535;
+
+					} else {
+
+						throw new Error( 'THREE.GLTFLoader: Unsupported output accessor component type.' );
+
+					}
+
+					var scaled = new Float32Array( outputArray.length );
+
+					for ( var j = 0, jl = outputArray.length; j < jl; j ++ ) {
+
+						scaled[j] = outputArray[j] * scale;
+
+					}
+
+					outputArray = scaled;
+
+				}
+
 				for ( var j = 0, jl = targetNames.length; j < jl; j ++ ) {
 
 					var track = new TypedKeyframeTrack(
 						targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ],
 						inputAccessor.array,
-						outputAccessor.array,
+						outputArray,
 						interpolation
 					);
 

+ 3 - 1
examples/jsm/loaders/HDRCubeTextureLoader.d.ts

@@ -11,7 +11,9 @@ export class HDRCubeTextureLoader {
   manager: LoadingManager;
   hdrLoader: RGBELoader;
   path: string;
+  type: TextureDataType;
 
-  load(type: TextureDataType, url: string, onLoad: (texture: CubeTexture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  load(url: string, onLoad: (texture: CubeTexture) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
   setPath(value: string): this;
+  setType(type: TextureDataType): this;
 }

+ 63 - 127
examples/jsm/loaders/HDRCubeTextureLoader.js

@@ -23,101 +23,60 @@ import { RGBELoader } from "../loaders/RGBELoader.js";
 var HDRCubeTextureLoader = function ( manager ) {
 
 	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
-	// override in sub classes
 	this.hdrLoader = new RGBELoader();
+	this.type = UnsignedByteType;
 
 };
 
-HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onProgress, onError ) {
+HDRCubeTextureLoader.prototype.load = function ( urls, onLoad, onProgress, onError ) {
 
-	var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
+	if ( ! Array.isArray( urls ) ) {
 
-		var e = sourceArray[ sourceOffset + 3 ];
-		var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+		console.warn( 'THREE.HDRCubeTextureLoader signature has changed. Use .setType() instead.' );
 
-		destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
-		destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
-		destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
+		this.setType( urls );
 
-	};
+		urls = onLoad;
+		onLoad = onProgress;
+		onProgress = onError;
+		onError = arguments[ 4 ];
 
-	var RGBEByteToRGBHalf = ( function () {
-
-		// Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
-
-		var floatView = new Float32Array( 1 );
-		var int32View = new Int32Array( floatView.buffer );
-
-		/* This method is faster than the OpenEXR implementation (very often
-		 * used, eg. in Ogre), with the additional benefit of rounding, inspired
-		 * by James Tursa?s half-precision code. */
-		function toHalf( val ) {
-
-			floatView[ 0 ] = val;
-			var x = int32View[ 0 ];
-
-			var bits = ( x >> 16 ) & 0x8000; /* Get the sign */
-			var m = ( x >> 12 ) & 0x07ff; /* Keep one extra bit for rounding */
-			var e = ( x >> 23 ) & 0xff; /* Using int is faster here */
-
-			/* If zero, or denormal, or exponent underflows too much for a denormal
-			 * half, return signed zero. */
-			if ( e < 103 ) return bits;
-
-			/* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
-			if ( e > 142 ) {
-
-				bits |= 0x7c00;
-				/* If exponent was 0xff and one mantissa bit was set, it means NaN,
-						 * not Inf, so make sure we set one mantissa bit too. */
-				bits |= ( ( e == 255 ) ? 0 : 1 ) && ( x & 0x007fffff );
-				return bits;
-
-			}
-
-			/* If exponent underflows but not too much, return a denormal */
-			if ( e < 113 ) {
-
-				m |= 0x0800;
-				/* Extra rounding may overflow and set mantissa to 0 and exponent
-				 * to 1, which is OK. */
-				bits |= ( m >> ( 114 - e ) ) + ( ( m >> ( 113 - e ) ) & 1 );
-				return bits;
+	}
 
-			}
+	var texture = new CubeTexture();
 
-			bits |= ( ( e - 112 ) << 10 ) | ( m >> 1 );
-			/* Extra rounding. An overflow will set mantissa to 0 and increment
-			 * the exponent, which is OK. */
-			bits += m & 1;
-			return bits;
+	texture.type = this.type;
 
-		}
+	switch ( texture.type ) {
 
-		return function ( sourceArray, sourceOffset, destArray, destOffset ) {
+		case UnsignedByteType:
 
-			var e = sourceArray[ sourceOffset + 3 ];
-			var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
+			texture.encoding = RGBEEncoding;
+			texture.format = RGBAFormat;
+			texture.minFilter = NearestFilter;
+			texture.magFilter = NearestFilter;
+			texture.generateMipmaps = false;
+			break;
 
-			destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale );
-			destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale );
-			destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale );
+		case FloatType:
 
-		};
+			texture.encoding = LinearEncoding;
+			texture.format = RGBFormat;
+			texture.minFilter = LinearFilter;
+			texture.magFilter = LinearFilter;
+			texture.generateMipmaps = false;
+			break;
 
-	} )();
+		case HalfFloatType:
 
-	//
+			texture.encoding = LinearEncoding;
+			texture.format = RGBFormat;
+			texture.minFilter = LinearFilter;
+			texture.magFilter = LinearFilter;
+			texture.generateMipmaps = false;
+			break;
 
-	var texture = new CubeTexture();
-
-	texture.type = type;
-	texture.encoding = ( type === UnsignedByteType ) ? RGBEEncoding : LinearEncoding;
-	texture.format = ( type === UnsignedByteType ) ? RGBAFormat : RGBFormat;
-	texture.minFilter = ( texture.encoding === RGBEEncoding ) ? NearestFilter : LinearFilter;
-	texture.magFilter = ( texture.encoding === RGBEEncoding ) ? NearestFilter : LinearFilter;
-	texture.generateMipmaps = ( texture.encoding !== RGBEEncoding );
-	texture.anisotropy = 0;
+	}
 
 	var scope = this;
 
@@ -125,71 +84,40 @@ HDRCubeTextureLoader.prototype.load = function ( type, urls, onLoad, onProgress,
 
 	function loadHDRData( i, onLoad, onProgress, onError ) {
 
-		var loader = new FileLoader( scope.manager );
-		loader.setPath( scope.path );
-		loader.setResponseType( 'arraybuffer' );
-		loader.load( urls[ i ], function ( buffer ) {
+		new FileLoader( scope.manager )
+			.setPath( scope.path )
+			.setResponseType( 'arraybuffer' )
+			.load( urls[ i ], function ( buffer ) {
 
-			loaded ++;
+				loaded ++;
 
-			var texData = scope.hdrLoader._parser( buffer );
+				var texData = scope.hdrLoader._parser( buffer );
 
-			if ( ! texData ) return;
+				if ( ! texData ) return;
 
-			if ( type === FloatType ) {
+				if ( texData.data !== undefined ) {
 
-				var numElements = ( texData.data.length / 4 ) * 3;
-				var floatdata = new Float32Array( numElements );
+					var dataTexture = new DataTexture( texData.data, texData.width, texData.height );
 
-				for ( var j = 0; j < numElements; j ++ ) {
+					dataTexture.type = texture.type;
+					dataTexture.encoding = texture.encoding;
+					dataTexture.format = texture.format;
+					dataTexture.minFilter = texture.minFilter;
+					dataTexture.magFilter = texture.magFilter;
+					dataTexture.generateMipmaps = texture.generateMipmaps;
 
-					RGBEByteToRGBFloat( texData.data, j * 4, floatdata, j * 3 );
+					texture.images[ i ] = dataTexture;
 
 				}
 
-				texData.data = floatdata;
-
-			} else if ( type === HalfFloatType ) {
-
-				var numElements = ( texData.data.length / 4 ) * 3;
-				var halfdata = new Uint16Array( numElements );
+				if ( loaded === 6 ) {
 
-				for ( var j = 0; j < numElements; j ++ ) {
-
-					RGBEByteToRGBHalf( texData.data, j * 4, halfdata, j * 3 );
+					texture.needsUpdate = true;
+					if ( onLoad ) onLoad( texture );
 
 				}
 
-				texData.data = halfdata;
-
-			}
-
-			if ( texData.image !== undefined ) {
-
-				texture[ i ].images = texData.image;
-
-			} else if ( texData.data !== undefined ) {
-
-				var dataTexture = new DataTexture( texData.data, texData.width, texData.height );
-				dataTexture.format = texture.format;
-				dataTexture.type = texture.type;
-				dataTexture.encoding = texture.encoding;
-				dataTexture.minFilter = texture.minFilter;
-				dataTexture.magFilter = texture.magFilter;
-				dataTexture.generateMipmaps = texture.generateMipmaps;
-
-				texture.images[ i ] = dataTexture;
-
-			}
-
-			if ( loaded === 6 ) {
-
-				texture.needsUpdate = true;
-				if ( onLoad ) onLoad( texture );
-
-			}
-
-		}, onProgress, onError );
+			}, onProgress, onError );
 
 	}
 
@@ -210,4 +138,12 @@ HDRCubeTextureLoader.prototype.setPath = function ( value ) {
 
 };
 
+HDRCubeTextureLoader.prototype.setType = function ( value ) {
+
+	this.type = value;
+	this.hdrLoader.setType( value );
+	return this;
+
+};
+
 export { HDRCubeTextureLoader };

+ 21 - 0
examples/jsm/loaders/LDrawLoader.d.ts

@@ -0,0 +1,21 @@
+import {
+	LoadingManager,
+	Group,
+	Material
+} from '../../../src/Three';
+
+export class LDrawLoader {
+	constructor(manager?: LoadingManager);
+	manager: LoadingManager;
+	path: string;
+
+	load(url: string, onLoad: (data: Group) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void) : void;
+	setPath(path: string) : this;
+	setFileMap(fileMap: Record<string, string>): void;
+	setMaterials(materials: Material[]): void;
+
+	parse(text: string, path: string, onLoad: (data: Group) => void): void;
+
+	addMaterial(material: Material ): void;
+	getMaterial(colourCode: string): Material | null;
+}

+ 1926 - 0
examples/jsm/loaders/LDrawLoader.js

@@ -0,0 +1,1926 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author yomboprime / https://github.com/yomboprime/
+ * @author gkjohnson / https://github.com/gkjohnson/
+ *
+ *
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	Color,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	Group,
+	LineBasicMaterial,
+	LineSegments,
+	Matrix4,
+	Mesh,
+	MeshPhongMaterial,
+	MeshStandardMaterial,
+	ShaderMaterial,
+	Vector3
+} from "../../../build/three.module.js";
+
+var LDrawLoader = ( function () {
+
+	var conditionalLineVertShader = /* glsl */`
+	attribute vec3 control0;
+	attribute vec3 control1;
+	attribute vec3 direction;
+	varying float discardFlag;
+
+	#include <common>
+	#include <color_pars_vertex>
+	#include <fog_pars_vertex>
+	#include <logdepthbuf_pars_vertex>
+	#include <clipping_planes_pars_vertex>
+	void main() {
+		#include <color_vertex>
+
+		vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
+		gl_Position = projectionMatrix * mvPosition;
+
+		// Transform the line segment ends and control points into camera clip space
+		vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 );
+		vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 );
+		vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+		vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + direction, 1.0 );
+
+		c0.xy /= c0.w;
+		c1.xy /= c1.w;
+		p0.xy /= p0.w;
+		p1.xy /= p1.w;
+
+		// Get the direction of the segment and an orthogonal vector
+		vec2 dir = p1.xy - p0.xy;
+		vec2 norm = vec2( -dir.y, dir.x );
+
+		// Get control point directions from the line
+		vec2 c0dir = c0.xy - p1.xy;
+		vec2 c1dir = c1.xy - p1.xy;
+
+		// If the vectors to the controls points are pointed in different directions away
+		// from the line segment then the line should not be drawn.
+		float d0 = dot( normalize( norm ), normalize( c0dir ) );
+		float d1 = dot( normalize( norm ), normalize( c1dir ) );
+		discardFlag = float( sign( d0 ) != sign( d1 ) );
+
+		#include <logdepthbuf_vertex>
+		#include <clipping_planes_vertex>
+		#include <fog_vertex>
+	}
+	`;
+
+	var conditionalLineFragShader = /* glsl */`
+	uniform vec3 diffuse;
+	uniform float opacity;
+	varying float discardFlag;
+
+	#include <common>
+	#include <color_pars_fragment>
+	#include <fog_pars_fragment>
+	#include <logdepthbuf_pars_fragment>
+	#include <clipping_planes_pars_fragment>
+	void main() {
+
+		if ( discardFlag > 0.5 ) discard;
+
+		#include <clipping_planes_fragment>
+		vec3 outgoingLight = vec3( 0.0 );
+		vec4 diffuseColor = vec4( diffuse, opacity );
+		#include <logdepthbuf_fragment>
+		#include <color_fragment>
+		outgoingLight = diffuseColor.rgb; // simple shader
+		gl_FragColor = vec4( outgoingLight, diffuseColor.a );
+		#include <premultiplied_alpha_fragment>
+		#include <tonemapping_fragment>
+		#include <encodings_fragment>
+		#include <fog_fragment>
+	}
+	`;
+
+
+
+	var tempVec0 = new Vector3();
+	var tempVec1 = new Vector3();
+	function smoothNormals( triangles, lineSegments ) {
+
+		function hashVertex( v ) {
+
+			// NOTE: 1e2 is pretty coarse but was chosen because it allows edges
+			// to be smoothed as expected (see minifig arms). The errors between edges
+			// could be due to matrix multiplication.
+			var x = ~ ~ ( v.x * 1e2 );
+			var y = ~ ~ ( v.y * 1e2 );
+			var z = ~ ~ ( v.z * 1e2 );
+			return `${ x },${ y },${ z }`;
+
+		}
+
+		function hashEdge( v0, v1 ) {
+
+			return `${ hashVertex( v0 ) }_${ hashVertex( v1 ) }`;
+
+		}
+
+		var hardEdges = new Set();
+		var halfEdgeList = {};
+		var fullHalfEdgeList = {};
+		var normals = [];
+
+		// Save the list of hard edges by hash
+		for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
+
+			var ls = lineSegments[ i ];
+			var v0 = ls.v0;
+			var v1 = ls.v1;
+			hardEdges.add( hashEdge( v0, v1 ) );
+			hardEdges.add( hashEdge( v1, v0 ) );
+
+		}
+
+		// track the half edges associated with each triangle
+		for ( var i = 0, l = triangles.length; i < l; i ++ ) {
+
+			var tri = triangles[ i ];
+			for ( var i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
+
+				var index = i2;
+				var next = ( i2 + 1 ) % 3;
+				var v0 = tri[ `v${ index }` ];
+				var v1 = tri[ `v${ next }` ];
+				var hash = hashEdge( v0, v1 );
+
+				// don't add the triangle if the edge is supposed to be hard
+				if ( hardEdges.has( hash ) ) continue;
+				halfEdgeList[ hash ] = tri;
+				fullHalfEdgeList[ hash ] = tri;
+
+			}
+
+		}
+
+		// NOTE: Some of the normals wind up being skewed in an unexpected way because
+		// quads provide more "influence" to some vertex normals than a triangle due to
+		// the fact that a quad is made up of two triangles and all triangles are weighted
+		// equally. To fix this quads could be tracked separately so their vertex normals
+		// are weighted appropriately or we could try only adding a normal direction
+		// once per normal.
+
+		// Iterate until we've tried to connect all triangles to share normals
+		while ( true ) {
+
+			// Stop if there are no more triangles left
+			var halfEdges = Object.keys( halfEdgeList );
+			if ( halfEdges.length === 0 ) break;
+
+			// Exhaustively find all connected triangles
+			var i = 0;
+			var queue = [ fullHalfEdgeList[ halfEdges[ 0 ] ] ];
+			while ( i < queue.length ) {
+
+				// initialize all vertex normals in this triangle
+				var tri = queue[ i ];
+				i ++;
+
+				var faceNormal = tri.faceNormal;
+				if ( tri.n0 === null ) {
+
+					tri.n0 = faceNormal.clone();
+					normals.push( tri.n0 );
+
+				}
+
+				if ( tri.n1 === null ) {
+
+					tri.n1 = faceNormal.clone();
+					normals.push( tri.n1 );
+
+				}
+
+				if ( tri.n2 === null ) {
+
+					tri.n2 = faceNormal.clone();
+					normals.push( tri.n2 );
+
+				}
+
+				// Check if any edge is connected to another triangle edge
+				for ( var i2 = 0, l2 = 3; i2 < l2; i2 ++ ) {
+
+					var index = i2;
+					var next = ( i2 + 1 ) % 3;
+					var v0 = tri[ `v${ index }` ];
+					var v1 = tri[ `v${ next }` ];
+
+					// delete this triangle from the list so it won't be found again
+					var hash = hashEdge( v0, v1 );
+					delete halfEdgeList[ hash ];
+
+					var reverseHash = hashEdge( v1, v0 );
+					var otherTri = fullHalfEdgeList[ reverseHash ];
+					if ( otherTri ) {
+
+						// NOTE: If the angle between triangles is > 67.5 degrees then assume it's
+						// hard edge. There are some cases where the line segments do not line up exactly
+						// with or span multiple triangle edges (see Lunar Vehicle wheels).
+						if ( Math.abs( otherTri.faceNormal.dot( tri.faceNormal ) ) < 0.25 ) {
+
+							continue;
+
+						}
+
+						// if this triangle has already been traversed then it won't be in
+						// the halfEdgeList. If it has not then add it to the queue and delete
+						// it so it won't be found again.
+						if ( reverseHash in halfEdgeList ) {
+
+							queue.push( otherTri );
+							delete halfEdgeList[ reverseHash ];
+
+						}
+
+						// Find the matching edge in this triangle and copy the normal vector over
+						for ( var i3 = 0, l3 = 3; i3 < l3; i3 ++ ) {
+
+							var otherIndex = i3;
+							var otherNext = ( i3 + 1 ) % 3;
+							var otherV0 = otherTri[ `v${ otherIndex }` ];
+							var otherV1 = otherTri[ `v${ otherNext }` ];
+
+							var otherHash = hashEdge( otherV0, otherV1 );
+							if ( otherHash === reverseHash ) {
+
+								if ( otherTri[ `n${ otherIndex }` ] === null ) {
+
+									var norm = tri[ `n${ next }` ];
+									otherTri[ `n${ otherIndex }` ] = norm;
+									norm.add( otherTri.faceNormal );
+
+								}
+
+								if ( otherTri[ `n${ otherNext }` ] === null ) {
+
+									var norm = tri[ `n${ index }` ];
+									otherTri[ `n${ otherNext }` ] = norm;
+									norm.add( otherTri.faceNormal );
+
+								}
+
+								break;
+
+							}
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		// The normals of each face have been added up so now we average them by normalizing the vector.
+		for ( var i = 0, l = normals.length; i < l; i ++ ) {
+
+			normals[ i ].normalize();
+
+		}
+
+	}
+
+	function isPrimitiveType( type ) {
+
+		return /primitive/i.test( type ) || type === 'Subpart';
+
+	}
+
+	function LineParser( line, lineNumber ) {
+
+		this.line = line;
+		this.lineLength = line.length;
+		this.currentCharIndex = 0;
+		this.currentChar = ' ';
+		this.lineNumber = lineNumber;
+
+	}
+
+	LineParser.prototype = {
+
+		constructor: LineParser,
+
+		seekNonSpace: function () {
+
+			while ( this.currentCharIndex < this.lineLength ) {
+
+				this.currentChar = this.line.charAt( this.currentCharIndex );
+
+				if ( this.currentChar !== ' ' && this.currentChar !== '\t' ) {
+
+					return;
+
+				}
+
+				this.currentCharIndex ++;
+
+			}
+
+		},
+
+		getToken: function () {
+
+			var pos0 = this.currentCharIndex ++;
+
+			// Seek space
+			while ( this.currentCharIndex < this.lineLength ) {
+
+				this.currentChar = this.line.charAt( this.currentCharIndex );
+
+				if ( this.currentChar === ' ' || this.currentChar === '\t' ) {
+
+					break;
+
+				}
+
+				this.currentCharIndex ++;
+
+			}
+
+			var pos1 = this.currentCharIndex;
+
+			this.seekNonSpace();
+
+			return this.line.substring( pos0, pos1 );
+
+		},
+
+		getRemainingString: function () {
+
+			return this.line.substring( this.currentCharIndex, this.lineLength );
+
+		},
+
+		isAtTheEnd: function () {
+
+			return this.currentCharIndex >= this.lineLength;
+
+		},
+
+		setToEnd: function () {
+
+			this.currentCharIndex = this.lineLength;
+
+		},
+
+		getLineNumberString: function () {
+
+			return this.lineNumber >= 0 ? " at line " + this.lineNumber : "";
+
+		}
+
+
+	};
+
+	function sortByMaterial( a, b ) {
+
+		if ( a.colourCode === b.colourCode ) {
+
+			return 0;
+
+		}
+
+		if ( a.colourCode < b.colourCode ) {
+
+			return - 1;
+
+		}
+
+		return 1;
+
+	}
+
+	function createObject( elements, elementSize, isConditionalSegments ) {
+
+		// Creates a LineSegments (elementSize = 2) or a Mesh (elementSize = 3 )
+		// With per face / segment material, implemented with mesh groups and materials array
+
+		// Sort the triangles or line segments by colour code to make later the mesh groups
+		elements.sort( sortByMaterial );
+
+		var positions = [];
+		var normals = [];
+		var materials = [];
+
+		var bufferGeometry = new BufferGeometry();
+		var prevMaterial = null;
+		var index0 = 0;
+		var numGroupVerts = 0;
+
+		for ( var iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
+
+			var elem = elements[ iElem ];
+			var v0 = elem.v0;
+			var v1 = elem.v1;
+			// Note that LDraw coordinate system is rotated 180 deg. in the X axis w.r.t. Three.js's one
+			positions.push( v0.x, v0.y, v0.z, v1.x, v1.y, v1.z );
+			if ( elementSize === 3 ) {
+
+				positions.push( elem.v2.x, elem.v2.y, elem.v2.z );
+
+				var n0 = elem.n0 || elem.faceNormal;
+				var n1 = elem.n1 || elem.faceNormal;
+				var n2 = elem.n2 || elem.faceNormal;
+				normals.push( n0.x, n0.y, n0.z );
+				normals.push( n1.x, n1.y, n1.z );
+				normals.push( n2.x, n2.y, n2.z );
+
+			}
+
+			if ( prevMaterial !== elem.material ) {
+
+				if ( prevMaterial !== null ) {
+
+					bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
+
+				}
+
+				materials.push( elem.material );
+
+				prevMaterial = elem.material;
+				index0 = iElem * elementSize;
+				numGroupVerts = elementSize;
+
+			} else {
+
+				numGroupVerts += elementSize;
+
+			}
+
+		}
+
+		if ( numGroupVerts > 0 ) {
+
+			bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
+
+		}
+
+		bufferGeometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+
+		if ( elementSize === 3 ) {
+
+			bufferGeometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+
+		}
+
+		var object3d = null;
+
+		if ( elementSize === 2 ) {
+
+			object3d = new LineSegments( bufferGeometry, materials );
+
+		} else if ( elementSize === 3 ) {
+
+			object3d = new Mesh( bufferGeometry, materials );
+
+		}
+
+		if ( isConditionalSegments ) {
+
+			object3d.isConditionalLine = true;
+
+			var controlArray0 = new Float32Array( elements.length * 3 * 2 );
+			var controlArray1 = new Float32Array( elements.length * 3 * 2 );
+			var directionArray = new Float32Array( elements.length * 3 * 2 );
+			for ( var i = 0, l = elements.length; i < l; i ++ ) {
+
+				var os = elements[ i ];
+				var c0 = os.c0;
+				var c1 = os.c1;
+				var v0 = os.v0;
+				var v1 = os.v1;
+				var index = i * 3 * 2;
+				controlArray0[ index + 0 ] = c0.x;
+				controlArray0[ index + 1 ] = c0.y;
+				controlArray0[ index + 2 ] = c0.z;
+				controlArray0[ index + 3 ] = c0.x;
+				controlArray0[ index + 4 ] = c0.y;
+				controlArray0[ index + 5 ] = c0.z;
+
+				controlArray1[ index + 0 ] = c1.x;
+				controlArray1[ index + 1 ] = c1.y;
+				controlArray1[ index + 2 ] = c1.z;
+				controlArray1[ index + 3 ] = c1.x;
+				controlArray1[ index + 4 ] = c1.y;
+				controlArray1[ index + 5 ] = c1.z;
+
+				directionArray[ index + 0 ] = v1.x - v0.x;
+				directionArray[ index + 1 ] = v1.y - v0.y;
+				directionArray[ index + 2 ] = v1.z - v0.z;
+				directionArray[ index + 3 ] = v1.x - v0.x;
+				directionArray[ index + 4 ] = v1.y - v0.y;
+				directionArray[ index + 5 ] = v1.z - v0.z;
+
+			}
+
+			bufferGeometry.addAttribute( 'control0', new BufferAttribute( controlArray0, 3, false ) );
+			bufferGeometry.addAttribute( 'control1', new BufferAttribute( controlArray1, 3, false ) );
+			bufferGeometry.addAttribute( 'direction', new BufferAttribute( directionArray, 3, false ) );
+
+		}
+
+		return object3d;
+
+	}
+
+	//
+
+	function LDrawLoader( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+		// This is a stack of 'parse scopes' with one level per subobject loaded file.
+		// Each level contains a material lib and also other runtime variables passed between parent and child subobjects
+		// When searching for a material code, the stack is read from top of the stack to bottom
+		// Each material library is an object map keyed by colour codes.
+		this.parseScopesStack = null;
+
+		this.path = '';
+
+		// Array of THREE.Material
+		this.materials = [];
+
+		// Not using THREE.Cache here because it returns the previous HTML error response instead of calling onError()
+		// This also allows to handle the embedded text files ("0 FILE" lines)
+		this.subobjectCache = {};
+
+		// This object is a map from file names to paths. It agilizes the paths search. If it is not set then files will be searched by trial and error.
+		this.fileMap = null;
+
+		// Add default main triangle and line edge materials (used in piecess that can be coloured with a main color)
+		this.setMaterials( [
+			this.parseColourMetaDirective( new LineParser( "Main_Colour CODE 16 VALUE #FF8080 EDGE #333333" ) ),
+			this.parseColourMetaDirective( new LineParser( "Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333" ) )
+		] );
+
+		// If this flag is set to true, each subobject will be a Object.
+		// If not (the default), only one object which contains all the merged primitives will be created.
+		this.separateObjects = false;
+
+		// If this flag is set to true the vertex normals will be smoothed.
+		this.smoothNormals = true;
+
+	}
+
+	// Special surface finish tag types.
+	// Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented
+	LDrawLoader.FINISH_TYPE_DEFAULT = 0;
+	LDrawLoader.FINISH_TYPE_CHROME = 1;
+	LDrawLoader.FINISH_TYPE_PEARLESCENT = 2;
+	LDrawLoader.FINISH_TYPE_RUBBER = 3;
+	LDrawLoader.FINISH_TYPE_MATTE_METALLIC = 4;
+	LDrawLoader.FINISH_TYPE_METAL = 5;
+
+	// State machine to search a subobject path.
+	// The LDraw standard establishes these various possible subfolders.
+	LDrawLoader.FILE_LOCATION_AS_IS = 0;
+	LDrawLoader.FILE_LOCATION_TRY_PARTS = 1;
+	LDrawLoader.FILE_LOCATION_TRY_P = 2;
+	LDrawLoader.FILE_LOCATION_TRY_MODELS = 3;
+	LDrawLoader.FILE_LOCATION_TRY_RELATIVE = 4;
+	LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE = 5;
+	LDrawLoader.FILE_LOCATION_NOT_FOUND = 6;
+
+	LDrawLoader.prototype = {
+
+		constructor: LDrawLoader,
+
+		load: function ( url, onLoad, onProgress, onError ) {
+
+			if ( ! this.fileMap ) {
+
+				this.fileMap = {};
+
+			}
+
+			var scope = this;
+
+			var fileLoader = new FileLoader( this.manager );
+			fileLoader.setPath( this.path );
+			fileLoader.load( url, function ( text ) {
+
+				scope.processObject( text, onLoad, null, url );
+
+			}, onProgress, onError );
+
+		},
+
+		parse: function ( text, path, onLoad ) {
+
+			// Async parse.  This function calls onParse with the parsed THREE.Object3D as parameter
+
+			this.processObject( text, onLoad, null, path );
+
+		},
+
+		setPath: function ( value ) {
+
+			this.path = value;
+
+			return this;
+
+		},
+
+		setMaterials: function ( materials ) {
+
+			// Clears parse scopes stack, adds new scope with material library
+
+			this.parseScopesStack = [];
+
+			this.newParseScopeLevel( materials );
+
+			this.getCurrentParseScope().isFromParse = false;
+
+			this.materials = materials;
+
+			return this;
+
+		},
+
+		setFileMap: function ( fileMap ) {
+
+			this.fileMap = fileMap;
+
+			return this;
+
+		},
+
+		newParseScopeLevel: function ( materials ) {
+
+			// Adds a new scope level, assign materials to it and returns it
+
+			var matLib = {};
+
+			if ( materials ) {
+
+				for ( var i = 0, n = materials.length; i < n; i ++ ) {
+
+					var material = materials[ i ];
+					matLib[ material.userData.code ] = material;
+
+				}
+
+			}
+
+			var topParseScope = this.getCurrentParseScope();
+			var newParseScope = {
+
+				lib: matLib,
+				url: null,
+
+				// Subobjects
+				subobjects: null,
+				numSubobjects: 0,
+				subobjectIndex: 0,
+				inverted: false,
+				category: null,
+				keywords: null,
+
+				// Current subobject
+				currentFileName: null,
+				mainColourCode: topParseScope ? topParseScope.mainColourCode : '16',
+				mainEdgeColourCode: topParseScope ? topParseScope.mainEdgeColourCode : '24',
+				currentMatrix: new Matrix4(),
+				matrix: new Matrix4(),
+
+				// If false, it is a root material scope previous to parse
+				isFromParse: true,
+
+				triangles: null,
+				lineSegments: null,
+				conditionalSegments: null,
+			};
+
+			this.parseScopesStack.push( newParseScope );
+
+			return newParseScope;
+
+		},
+
+		removeScopeLevel: function () {
+
+			this.parseScopesStack.pop();
+
+			return this;
+
+		},
+
+		addMaterial: function ( material ) {
+
+			// Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
+
+			var matLib = this.getCurrentParseScope().lib;
+
+			if ( ! matLib[ material.userData.code ] ) {
+
+				this.materials.push( material );
+
+			}
+
+			matLib[ material.userData.code ] = material;
+
+			return this;
+
+		},
+
+		getMaterial: function ( colourCode ) {
+
+			// Given a colour code search its material in the parse scopes stack
+
+			if ( colourCode.startsWith( "0x2" ) ) {
+
+				// Special 'direct' material value (RGB colour)
+
+				var colour = colourCode.substring( 3 );
+
+				return this.parseColourMetaDirective( new LineParser( "Direct_Color_" + colour + " CODE -1 VALUE #" + colour + " EDGE #" + colour + "" ) );
+
+			}
+
+			for ( var i = this.parseScopesStack.length - 1; i >= 0; i -- ) {
+
+				var material = this.parseScopesStack[ i ].lib[ colourCode ];
+
+				if ( material ) {
+
+					return material;
+
+				}
+
+			}
+
+			// Material was not found
+			return null;
+
+		},
+
+		getParentParseScope: function () {
+
+			if ( this.parseScopesStack.length > 1 ) {
+
+				return this.parseScopesStack[ this.parseScopesStack.length - 2 ];
+
+			}
+
+			return null;
+
+		},
+
+		getCurrentParseScope: function () {
+
+			if ( this.parseScopesStack.length > 0 ) {
+
+				return this.parseScopesStack[ this.parseScopesStack.length - 1 ];
+
+			}
+
+			return null;
+
+		},
+
+		parseColourMetaDirective: function ( lineParser ) {
+
+			// Parses a colour definition and returns a THREE.Material or null if error
+
+			var code = null;
+
+			// Triangle and line colours
+			var colour = 0xFF00FF;
+			var edgeColour = 0xFF00FF;
+
+			// Transparency
+			var alpha = 1;
+			var isTransparent = false;
+			// Self-illumination:
+			var luminance = 0;
+
+			var finishType = LDrawLoader.FINISH_TYPE_DEFAULT;
+			var canHaveEnvMap = true;
+
+			var edgeMaterial = null;
+
+			var name = lineParser.getToken();
+			if ( ! name ) {
+
+				throw 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + ".";
+
+			}
+
+			// Parse tag tokens and their parameters
+			var token = null;
+			while ( true ) {
+
+				token = lineParser.getToken();
+
+				if ( ! token ) {
+
+					break;
+
+				}
+
+				switch ( token.toUpperCase() ) {
+
+					case "CODE":
+
+						code = lineParser.getToken();
+						break;
+
+					case "VALUE":
+
+						colour = lineParser.getToken();
+						if ( colour.startsWith( '0x' ) ) {
+
+							colour = '#' + colour.substring( 2 );
+
+						} else if ( ! colour.startsWith( '#' ) ) {
+
+							throw 'LDrawLoader: Invalid colour while parsing material' + lineParser.getLineNumberString() + ".";
+
+						}
+						break;
+
+					case "EDGE":
+
+						edgeColour = lineParser.getToken();
+						if ( edgeColour.startsWith( '0x' ) ) {
+
+							edgeColour = '#' + edgeColour.substring( 2 );
+
+						} else if ( ! edgeColour.startsWith( '#' ) ) {
+
+							// Try to see if edge colour is a colour code
+							edgeMaterial = this.getMaterial( edgeColour );
+							if ( ! edgeMaterial ) {
+
+								throw 'LDrawLoader: Invalid edge colour while parsing material' + lineParser.getLineNumberString() + ".";
+
+							}
+
+							// Get the edge material for this triangle material
+							edgeMaterial = edgeMaterial.userData.edgeMaterial;
+
+						}
+						break;
+
+					case 'ALPHA':
+
+						alpha = parseInt( lineParser.getToken() );
+
+						if ( isNaN( alpha ) ) {
+
+							throw 'LDrawLoader: Invalid alpha value in material definition' + lineParser.getLineNumberString() + ".";
+
+						}
+
+						alpha = Math.max( 0, Math.min( 1, alpha / 255 ) );
+
+						if ( alpha < 1 ) {
+
+							isTransparent = true;
+
+						}
+
+						break;
+
+					case 'LUMINANCE':
+
+						luminance = parseInt( lineParser.getToken() );
+
+						if ( isNaN( luminance ) ) {
+
+							throw 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + ".";
+
+						}
+
+						luminance = Math.max( 0, Math.min( 1, luminance / 255 ) );
+
+						break;
+
+					case 'CHROME':
+						finishType = LDrawLoader.FINISH_TYPE_CHROME;
+						break;
+
+					case 'PEARLESCENT':
+						finishType = LDrawLoader.FINISH_TYPE_PEARLESCENT;
+						break;
+
+					case 'RUBBER':
+						finishType = LDrawLoader.FINISH_TYPE_RUBBER;
+						break;
+
+					case 'MATTE_METALLIC':
+						finishType = LDrawLoader.FINISH_TYPE_MATTE_METALLIC;
+						break;
+
+					case 'METAL':
+						finishType = LDrawLoader.FINISH_TYPE_METAL;
+						break;
+
+					case 'MATERIAL':
+						// Not implemented
+						lineParser.setToEnd();
+						break;
+
+					default:
+						throw 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + ".";
+						break;
+
+				}
+
+			}
+
+			var material = null;
+
+			switch ( finishType ) {
+
+				case LDrawLoader.FINISH_TYPE_DEFAULT:
+
+					material = new MeshStandardMaterial( { color: colour, roughness: 0.3, envMapIntensity: 0.3, metalness: 0 } );
+					break;
+
+				case LDrawLoader.FINISH_TYPE_PEARLESCENT:
+
+					// Try to imitate pearlescency by setting the specular to the complementary of the color, and low shininess
+					var specular = new Color( colour );
+					var hsl = specular.getHSL( { h: 0, s: 0, l: 0 } );
+					hsl.h = ( hsl.h + 0.5 ) % 1;
+					hsl.l = Math.min( 1, hsl.l + ( 1 - hsl.l ) * 0.7 );
+					specular.setHSL( hsl.h, hsl.s, hsl.l );
+
+					material = new MeshPhongMaterial( { color: colour, specular: specular, shininess: 10, reflectivity: 0.3 } );
+					break;
+
+				case LDrawLoader.FINISH_TYPE_CHROME:
+
+					// Mirror finish surface
+					material = new MeshStandardMaterial( { color: colour, roughness: 0, metalness: 1 } );
+					break;
+
+				case LDrawLoader.FINISH_TYPE_RUBBER:
+
+					// Rubber finish
+					material = new MeshStandardMaterial( { color: colour, roughness: 0.9, metalness: 0 } );
+					canHaveEnvMap = false;
+					break;
+
+				case LDrawLoader.FINISH_TYPE_MATTE_METALLIC:
+
+					// Brushed metal finish
+					material = new MeshStandardMaterial( { color: colour, roughness: 0.8, metalness: 0.4 } );
+					break;
+
+				case LDrawLoader.FINISH_TYPE_METAL:
+
+					// Average metal finish
+					material = new MeshStandardMaterial( { color: colour, roughness: 0.2, metalness: 0.85 } );
+					break;
+
+				default:
+					// Should not happen
+					break;
+
+			}
+
+			material.transparent = isTransparent;
+			material.premultipliedAlpha = true;
+			material.opacity = alpha;
+			material.depthWrite = ! isTransparent;
+
+			material.polygonOffset = true;
+			material.polygonOffsetFactor = 1;
+
+			material.userData.canHaveEnvMap = canHaveEnvMap;
+
+			if ( luminance !== 0 ) {
+
+				material.emissive.set( material.color ).multiplyScalar( luminance );
+
+			}
+
+			if ( ! edgeMaterial ) {
+
+				// This is the material used for edges
+				edgeMaterial = new LineBasicMaterial( {
+					color: edgeColour,
+					transparent: isTransparent,
+					opacity: alpha,
+					depthWrite: ! isTransparent
+				} );
+				edgeMaterial.userData.code = code;
+				edgeMaterial.name = name + " - Edge";
+				edgeMaterial.userData.canHaveEnvMap = false;
+
+				// This is the material used for conditional edges
+				edgeMaterial.userData.conditionalEdgeMaterial = new ShaderMaterial( {
+					vertexShader: conditionalLineVertShader,
+					fragmentShader: conditionalLineFragShader,
+					uniforms: {
+						diffuse: {
+							value: new Color( edgeColour )
+						},
+						opacity: {
+							value: alpha
+						}
+					},
+					transparent: isTransparent,
+					depthWrite: ! isTransparent
+				} );
+				edgeMaterial.userData.conditionalEdgeMaterial.userData.canHaveEnvMap = false;
+
+			}
+
+			material.userData.code = code;
+			material.name = name;
+
+			material.userData.edgeMaterial = edgeMaterial;
+
+			return material;
+
+		},
+
+		//
+
+		objectParse: function ( text ) {
+
+			//console.time( 'LDrawLoader' );
+
+			// Retrieve data from the parent parse scope
+			var parentParseScope = this.getParentParseScope();
+
+			// Main colour codes passed to this subobject (or default codes 16 and 24 if it is the root object)
+			var mainColourCode = parentParseScope.mainColourCode;
+			var mainEdgeColourCode = parentParseScope.mainEdgeColourCode;
+
+			var currentParseScope = this.getCurrentParseScope();
+
+			// Parse result variables
+			var triangles;
+			var lineSegments;
+			var conditionalSegments;
+
+			var subobjects = [];
+
+			var category = null;
+			var keywords = null;
+
+			if ( text.indexOf( '\r\n' ) !== - 1 ) {
+
+				// This is faster than String.split with regex that splits on both
+				text = text.replace( /\r\n/g, '\n' );
+
+			}
+
+			var lines = text.split( '\n' );
+			var numLines = lines.length;
+			var lineIndex = 0;
+
+			var parsingEmbeddedFiles = false;
+			var currentEmbeddedFileName = null;
+			var currentEmbeddedText = null;
+
+			var bfcCertified = false;
+			var bfcCCW = true;
+			var bfcInverted = false;
+			var bfcCull = true;
+			var type = '';
+
+			var scope = this;
+			function parseColourCode( lineParser, forEdge ) {
+
+				// Parses next colour code and returns a THREE.Material
+
+				var colourCode = lineParser.getToken();
+
+				if ( ! forEdge && colourCode === '16' ) {
+
+					colourCode = mainColourCode;
+
+				}
+				if ( forEdge && colourCode === '24' ) {
+
+					colourCode = mainEdgeColourCode;
+
+				}
+
+				var material = scope.getMaterial( colourCode );
+
+				if ( ! material ) {
+
+					throw 'LDrawLoader: Unknown colour code "' + colourCode + '" is used' + lineParser.getLineNumberString() + ' but it was not defined previously.';
+
+				}
+
+				return material;
+
+			}
+
+			function parseVector( lp ) {
+
+				var v = new Vector3( parseFloat( lp.getToken() ), parseFloat( lp.getToken() ), parseFloat( lp.getToken() ) );
+
+				if ( ! scope.separateObjects ) {
+
+					v.applyMatrix4( currentParseScope.currentMatrix );
+
+				}
+
+				return v;
+
+			}
+
+			// Parse all line commands
+			for ( lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
+
+				var line = lines[ lineIndex ];
+
+				if ( line.length === 0 ) continue;
+
+				if ( parsingEmbeddedFiles ) {
+
+					if ( line.startsWith( '0 FILE ' ) ) {
+
+						// Save previous embedded file in the cache
+						this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
+
+						// New embedded text file
+						currentEmbeddedFileName = line.substring( 7 );
+						currentEmbeddedText = '';
+
+					} else {
+
+						currentEmbeddedText += line + '\n';
+
+					}
+
+					continue;
+
+				}
+
+				var lp = new LineParser( line, lineIndex + 1 );
+
+				lp.seekNonSpace();
+
+				if ( lp.isAtTheEnd() ) {
+
+					// Empty line
+					continue;
+
+				}
+
+				// Parse the line type
+				var lineType = lp.getToken();
+
+				switch ( lineType ) {
+
+					// Line type 0: Comment or META
+					case '0':
+
+						// Parse meta directive
+						var meta = lp.getToken();
+
+						if ( meta ) {
+
+							switch ( meta ) {
+
+								case '!LDRAW_ORG':
+
+									type = lp.getToken();
+
+									if ( ! parsingEmbeddedFiles ) {
+
+										currentParseScope.triangles = [];
+										currentParseScope.lineSegments = [];
+										currentParseScope.conditionalSegments = [];
+										currentParseScope.type = type;
+
+										var isRoot = ! parentParseScope.isFromParse;
+										if ( isRoot || scope.separateObjects && ! isPrimitiveType( type ) ) {
+
+											currentParseScope.groupObject = new Group();
+
+										}
+
+										// If the scale of the object is negated then the triangle winding order
+										// needs to be flipped.
+										var matrix = currentParseScope.matrix;
+										if (
+											matrix.determinant() < 0 && (
+												scope.separateObjects && isPrimitiveType( type ) ||
+												! scope.separateObjects
+											) ) {
+
+											currentParseScope.inverted = ! currentParseScope.inverted;
+
+										}
+
+										triangles = currentParseScope.triangles;
+										lineSegments = currentParseScope.lineSegments;
+										conditionalSegments = currentParseScope.conditionalSegments;
+
+									}
+
+									break;
+
+								case '!COLOUR':
+
+									var material = this.parseColourMetaDirective( lp );
+									if ( material ) {
+
+										this.addMaterial( material );
+
+									}	else {
+
+										console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
+
+									}
+									break;
+
+								case '!CATEGORY':
+
+									category = lp.getToken();
+									break;
+
+								case '!KEYWORDS':
+
+									var newKeywords = lp.getRemainingString().split( ',' );
+									if ( newKeywords.length > 0 ) {
+
+										if ( ! keywords ) {
+
+											keywords = [];
+
+										}
+
+										newKeywords.forEach( function ( keyword ) {
+
+											keywords.push( keyword.trim() );
+
+										} );
+
+									}
+									break;
+
+								case 'FILE':
+
+									if ( lineIndex > 0 ) {
+
+										// Start embedded text files parsing
+										parsingEmbeddedFiles = true;
+										currentEmbeddedFileName = lp.getRemainingString();
+										currentEmbeddedText = '';
+
+										bfcCertified = false;
+										bfcCCW = true;
+
+									}
+
+									break;
+
+								case 'BFC':
+
+									// Changes to the backface culling state
+									while ( ! lp.isAtTheEnd() ) {
+
+										var token = lp.getToken();
+
+										switch ( token ) {
+
+											case 'CERTIFY':
+											case 'NOCERTIFY':
+
+												bfcCertified = token === 'CERTIFY';
+												bfcCCW = true;
+
+												break;
+
+											case 'CW':
+											case 'CCW':
+
+												bfcCCW = token === 'CCW';
+
+												break;
+
+											case 'INVERTNEXT':
+
+												bfcInverted = true;
+
+												break;
+
+											case 'CLIP':
+											case 'NOCLIP':
+
+											  bfcCull = token === 'CLIP';
+
+												break;
+
+											default:
+
+												console.warn( 'THREE.LDrawLoader: BFC directive "' + token + '" is unknown.' );
+
+												break;
+
+										}
+
+									}
+
+									break;
+
+								default:
+									// Other meta directives are not implemented
+									break;
+
+							}
+
+						}
+
+						break;
+
+					// Line type 1: Sub-object file
+					case '1':
+
+						var material = parseColourCode( lp );
+
+						var posX = parseFloat( lp.getToken() );
+						var posY = parseFloat( lp.getToken() );
+						var posZ = parseFloat( lp.getToken() );
+						var m0 = parseFloat( lp.getToken() );
+						var m1 = parseFloat( lp.getToken() );
+						var m2 = parseFloat( lp.getToken() );
+						var m3 = parseFloat( lp.getToken() );
+						var m4 = parseFloat( lp.getToken() );
+						var m5 = parseFloat( lp.getToken() );
+						var m6 = parseFloat( lp.getToken() );
+						var m7 = parseFloat( lp.getToken() );
+						var m8 = parseFloat( lp.getToken() );
+
+						var matrix = new Matrix4().set(
+							m0, m1, m2, posX,
+							m3, m4, m5, posY,
+							m6, m7, m8, posZ,
+							0, 0, 0, 1
+						);
+
+						var fileName = lp.getRemainingString().trim().replace( /\\/g, "/" );
+
+						if ( scope.fileMap[ fileName ] ) {
+
+							// Found the subobject path in the preloaded file path map
+							fileName = scope.fileMap[ fileName ];
+
+						}	else {
+
+							// Standardized subfolders
+							if ( fileName.startsWith( 's/' ) ) {
+
+								fileName = 'parts/' + fileName;
+
+							} else if ( fileName.startsWith( '48/' ) ) {
+
+								fileName = 'p/' + fileName;
+
+							}
+
+						}
+
+						subobjects.push( {
+							material: material,
+							matrix: matrix,
+							fileName: fileName,
+							originalFileName: fileName,
+							locationState: LDrawLoader.FILE_LOCATION_AS_IS,
+							url: null,
+							triedLowerCase: false,
+							inverted: bfcInverted !== currentParseScope.inverted
+						} );
+
+						bfcInverted = false;
+
+						break;
+
+					// Line type 2: Line segment
+					case '2':
+
+						var material = parseColourCode( lp, true );
+
+						var segment = {
+							material: material.userData.edgeMaterial,
+							colourCode: material.userData.code,
+							v0: parseVector( lp ),
+							v1: parseVector( lp )
+						};
+
+						lineSegments.push( segment );
+
+						break;
+
+					// Line type 5: Conditional Line segment
+					case '5':
+
+						var material = parseColourCode( lp, true );
+
+						var segment = {
+							material: material.userData.edgeMaterial.userData.conditionalEdgeMaterial,
+							colourCode: material.userData.code,
+							v0: parseVector( lp ),
+							v1: parseVector( lp ),
+							c0: parseVector( lp ),
+							c1: parseVector( lp )
+						};
+
+						conditionalSegments.push( segment );
+
+						break;
+
+					// Line type 3: Triangle
+					case '3':
+
+						var material = parseColourCode( lp );
+
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2, faceNormal;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+
+						} else {
+
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
+
+						tempVec0.subVectors( v1, v0 );
+						tempVec1.subVectors( v2, v1 );
+						faceNormal = new Vector3()
+							.crossVectors( tempVec0, tempVec1 )
+							.normalize();
+
+						triangles.push( {
+							material: material,
+							colourCode: material.userData.code,
+							v0: v0,
+							v1: v1,
+							v2: v2,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
+						} );
+
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
+							} );
+
+						}
+
+						break;
+
+					// Line type 4: Quadrilateral
+					case '4':
+
+						var material = parseColourCode( lp );
+
+						var inverted = currentParseScope.inverted;
+						var ccw = bfcCCW !== inverted;
+						var doubleSided = ! bfcCertified || ! bfcCull;
+						var v0, v1, v2, v3, faceNormal;
+
+						if ( ccw === true ) {
+
+							v0 = parseVector( lp );
+							v1 = parseVector( lp );
+							v2 = parseVector( lp );
+							v3 = parseVector( lp );
+
+						} else {
+
+							v3 = parseVector( lp );
+							v2 = parseVector( lp );
+							v1 = parseVector( lp );
+							v0 = parseVector( lp );
+
+						}
+
+						tempVec0.subVectors( v1, v0 );
+						tempVec1.subVectors( v2, v1 );
+						faceNormal = new Vector3()
+							.crossVectors( tempVec0, tempVec1 )
+							.normalize();
+
+						triangles.push( {
+							material: material,
+							colourCode: material.userData.code,
+							v0: v0,
+							v1: v1,
+							v2: v2,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
+						} );
+
+						triangles.push( {
+							material: material,
+							colourCode: material.userData.code,
+							v0: v0,
+							v1: v2,
+							v2: v3,
+							faceNormal: faceNormal,
+							n0: null,
+							n1: null,
+							n2: null
+						} );
+
+						if ( doubleSided === true ) {
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v2,
+								v2: v1,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
+							} );
+
+							triangles.push( {
+								material: material,
+								colourCode: material.userData.code,
+								v0: v0,
+								v1: v3,
+								v2: v2,
+								faceNormal: faceNormal,
+								n0: null,
+								n1: null,
+								n2: null
+							} );
+
+						}
+
+						break;
+
+					default:
+						throw 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.';
+						break;
+
+				}
+
+			}
+
+			if ( parsingEmbeddedFiles ) {
+
+				this.subobjectCache[ currentEmbeddedFileName.toLowerCase() ] = currentEmbeddedText;
+
+			}
+
+			currentParseScope.category = category;
+			currentParseScope.keywords = keywords;
+			currentParseScope.subobjects = subobjects;
+			currentParseScope.numSubobjects = subobjects.length;
+			currentParseScope.subobjectIndex = 0;
+
+		},
+
+		processObject: function ( text, onProcessed, subobject, url ) {
+
+			var scope = this;
+
+			var parseScope = scope.newParseScopeLevel();
+			parseScope.url = url;
+
+			var parentParseScope = scope.getParentParseScope();
+
+			// Set current matrix
+			if ( subobject ) {
+
+				parseScope.currentMatrix.multiplyMatrices( parentParseScope.currentMatrix, subobject.matrix );
+				parseScope.matrix.copy( subobject.matrix );
+				parseScope.inverted = subobject.inverted;
+
+			}
+
+			// Add to cache
+			var currentFileName = parentParseScope.currentFileName;
+			if ( currentFileName !== null ) {
+
+				currentFileName = parentParseScope.currentFileName.toLowerCase();
+
+			}
+
+			if ( scope.subobjectCache[ currentFileName ] === undefined ) {
+
+				scope.subobjectCache[ currentFileName ] = text;
+
+			}
+
+
+			// Parse the object (returns a Group)
+			scope.objectParse( text );
+			var finishedCount = 0;
+			onSubobjectFinish();
+
+			function onSubobjectFinish() {
+
+				finishedCount ++;
+
+				if ( finishedCount === parseScope.subobjects.length + 1 ) {
+
+					finalizeObject();
+
+				} else {
+
+					// Once the previous subobject has finished we can start processing the next one in the list.
+					// The subobject processing shares scope in processing so it's important that they be loaded serially
+					// to avoid race conditions.
+					// Promise.resolve is used as an approach to asynchronously schedule a task _before_ this frame ends to
+					// avoid stack overflow exceptions when loading many subobjects from the cache. RequestAnimationFrame
+					// will work but causes the load to happen after the next frame which causes the load to take significantly longer.
+					var subobject = parseScope.subobjects[ parseScope.subobjectIndex ];
+					Promise.resolve().then( function () {
+
+						loadSubobject( subobject );
+
+					} );
+					parseScope.subobjectIndex ++;
+
+				}
+
+			}
+
+			function finalizeObject() {
+
+				if ( scope.smoothNormals && parseScope.type === 'Part' ) {
+
+					smoothNormals( parseScope.triangles, parseScope.lineSegments );
+
+				}
+
+				var isRoot = ! parentParseScope.isFromParse;
+				if ( scope.separateObjects && ! isPrimitiveType( parseScope.type ) || isRoot ) {
+
+
+					const objGroup = parseScope.groupObject;
+					if ( parseScope.triangles.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.triangles, 3 ) );
+
+					}
+
+					if ( parseScope.lineSegments.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.lineSegments, 2 ) );
+
+					}
+
+					if ( parseScope.conditionalSegments.length > 0 ) {
+
+						objGroup.add( createObject( parseScope.conditionalSegments, 2, true ) );
+
+					}
+
+					if ( parentParseScope.groupObject ) {
+
+						objGroup.name = parseScope.fileName;
+						objGroup.userData.category = parseScope.category;
+						objGroup.userData.keywords = parseScope.keywords;
+						parseScope.matrix.decompose( objGroup.position, objGroup.quaternion, objGroup.scale );
+
+						parentParseScope.groupObject.add( objGroup );
+
+					}
+
+				} else {
+
+					var separateObjects = scope.separateObjects;
+					var parentLineSegments = parentParseScope.lineSegments;
+					var parentConditionalSegments = parentParseScope.conditionalSegments;
+					var parentTriangles = parentParseScope.triangles;
+
+					var lineSegments = parseScope.lineSegments;
+					var conditionalSegments = parseScope.conditionalSegments;
+					var triangles = parseScope.triangles;
+
+					for ( var i = 0, l = lineSegments.length; i < l; i ++ ) {
+
+						var ls = lineSegments[ i ];
+						if ( separateObjects ) {
+
+							ls.v0.applyMatrix4( parseScope.matrix );
+							ls.v1.applyMatrix4( parseScope.matrix );
+
+						}
+						parentLineSegments.push( ls );
+
+					}
+
+					for ( var i = 0, l = conditionalSegments.length; i < l; i ++ ) {
+
+						var os = conditionalSegments[ i ];
+						if ( separateObjects ) {
+
+							os.v0.applyMatrix4( parseScope.matrix );
+							os.v1.applyMatrix4( parseScope.matrix );
+							os.c0.applyMatrix4( parseScope.matrix );
+							os.c1.applyMatrix4( parseScope.matrix );
+
+						}
+						parentConditionalSegments.push( os );
+
+					}
+
+					for ( var i = 0, l = triangles.length; i < l; i ++ ) {
+
+						var tri = triangles[ i ];
+						if ( separateObjects ) {
+
+							tri.v0 = tri.v0.clone().applyMatrix4( parseScope.matrix );
+							tri.v1 = tri.v1.clone().applyMatrix4( parseScope.matrix );
+							tri.v2 = tri.v2.clone().applyMatrix4( parseScope.matrix );
+
+							tempVec0.subVectors( tri.v1, tri.v0 );
+							tempVec1.subVectors( tri.v2, tri.v1 );
+							tri.faceNormal.crossVectors( tempVec0, tempVec1 ).normalize();
+
+						}
+						parentTriangles.push( tri );
+
+					}
+
+				}
+
+				scope.removeScopeLevel();
+
+				if ( onProcessed ) {
+
+					onProcessed( parseScope.groupObject );
+
+				}
+
+			}
+
+			function loadSubobject( subobject ) {
+
+				parseScope.mainColourCode = subobject.material.userData.code;
+				parseScope.mainEdgeColourCode = subobject.material.userData.edgeMaterial.userData.code;
+				parseScope.currentFileName = subobject.originalFileName;
+
+
+				// If subobject was cached previously, use the cached one
+				var cached = scope.subobjectCache[ subobject.originalFileName.toLowerCase() ];
+				if ( cached ) {
+
+					scope.processObject( cached, function ( subobjectGroup ) {
+
+						onSubobjectLoaded( subobjectGroup, subobject );
+						onSubobjectFinish();
+
+					}, subobject, url );
+
+					return;
+
+				}
+
+				// Adjust file name to locate the subobject file path in standard locations (always under directory scope.path)
+				// Update also subobject.locationState for the next try if this load fails.
+				var subobjectURL = subobject.fileName;
+				var newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
+
+				switch ( subobject.locationState ) {
+
+					case LDrawLoader.FILE_LOCATION_AS_IS:
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_PARTS:
+						subobjectURL = 'parts/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_P:
+						subobjectURL = 'p/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_MODELS:
+						subobjectURL = 'models/' + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_RELATIVE:
+						subobjectURL = url.substring( 0, url.lastIndexOf( "/" ) + 1 ) + subobjectURL;
+						newLocationState = subobject.locationState + 1;
+						break;
+
+					case LDrawLoader.FILE_LOCATION_TRY_ABSOLUTE:
+
+						if ( subobject.triedLowerCase ) {
+
+							// Try absolute path
+							newLocationState = LDrawLoader.FILE_LOCATION_NOT_FOUND;
+
+						} else {
+
+							// Next attempt is lower case
+							subobject.fileName = subobject.fileName.toLowerCase();
+							subobjectURL = subobject.fileName;
+							subobject.triedLowerCase = true;
+							newLocationState = LDrawLoader.FILE_LOCATION_AS_IS;
+
+						}
+						break;
+
+					case LDrawLoader.FILE_LOCATION_NOT_FOUND:
+
+						// All location possibilities have been tried, give up loading this object
+						console.warn( 'LDrawLoader: Subobject "' + subobject.originalFileName + '" could not be found.' );
+
+						return;
+
+				}
+
+				subobject.locationState = newLocationState;
+				subobject.url = subobjectURL;
+
+				// Load the subobject
+				// Use another file loader here so we can keep track of the subobject information
+				// and use it when processing the next model.
+				var fileLoader = new FileLoader( scope.manager );
+				fileLoader.setPath( scope.path );
+				fileLoader.load( subobjectURL, function ( text ) {
+
+					scope.processObject( text, function ( subobjectGroup ) {
+
+						onSubobjectLoaded( subobjectGroup, subobject );
+						onSubobjectFinish();
+
+					}, subobject, url );
+
+				}, undefined, function ( err ) {
+
+					onSubobjectError( err, subobject );
+
+				}, subobject );
+
+			}
+
+			function onSubobjectLoaded( subobjectGroup, subobject ) {
+
+				if ( subobjectGroup === null ) {
+
+					// Try to reload
+					loadSubobject( subobject );
+					return;
+
+				}
+
+				scope.fileMap[ subobject.originalFileName ] = subobject.url;
+
+			}
+
+			function onSubobjectError( err, subobject ) {
+
+				// Retry download from a different default possible location
+				loadSubobject( subobject );
+
+			}
+
+		}
+
+	};
+
+	return LDrawLoader;
+
+} )();
+
+export { LDrawLoader };

+ 0 - 1
examples/jsm/loaders/LWOLoader.js

@@ -574,7 +574,6 @@ LWO3Parser.prototype = {
 			case 'FLAG':
 
 			case 'TRNL':
-			case 'GLOS':
 			case 'SHRP':
 			case 'RFOP':
 			case 'RSAN':

+ 14 - 0
examples/jsm/loaders/MD2Loader.d.ts

@@ -0,0 +1,14 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+export class MD2Loader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): this;
+  parse(data: ArrayBuffer): BufferGeometry;
+}

+ 398 - 0
examples/jsm/loaders/MD2Loader.js

@@ -0,0 +1,398 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+import {
+	AnimationClip,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	Vector3
+} from "../../../build/three.module.js";
+
+var MD2Loader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+};
+
+MD2Loader.prototype = {
+
+	constructor: MD2Loader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( buffer ) {
+
+			onLoad( scope.parse( buffer ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: ( function () {
+
+		var normalData = [
+			[ - 0.525731, 0.000000, 0.850651 ], [ - 0.442863, 0.238856, 0.864188 ],
+			[ - 0.295242, 0.000000, 0.955423 ], [ - 0.309017, 0.500000, 0.809017 ],
+			[ - 0.162460, 0.262866, 0.951056 ], [ 0.000000, 0.000000, 1.000000 ],
+			[ 0.000000, 0.850651, 0.525731 ], [ - 0.147621, 0.716567, 0.681718 ],
+			[ 0.147621, 0.716567, 0.681718 ], [ 0.000000, 0.525731, 0.850651 ],
+			[ 0.309017, 0.500000, 0.809017 ], [ 0.525731, 0.000000, 0.850651 ],
+			[ 0.295242, 0.000000, 0.955423 ], [ 0.442863, 0.238856, 0.864188 ],
+			[ 0.162460, 0.262866, 0.951056 ], [ - 0.681718, 0.147621, 0.716567 ],
+			[ - 0.809017, 0.309017, 0.500000 ], [ - 0.587785, 0.425325, 0.688191 ],
+			[ - 0.850651, 0.525731, 0.000000 ], [ - 0.864188, 0.442863, 0.238856 ],
+			[ - 0.716567, 0.681718, 0.147621 ], [ - 0.688191, 0.587785, 0.425325 ],
+			[ - 0.500000, 0.809017, 0.309017 ], [ - 0.238856, 0.864188, 0.442863 ],
+			[ - 0.425325, 0.688191, 0.587785 ], [ - 0.716567, 0.681718, - 0.147621 ],
+			[ - 0.500000, 0.809017, - 0.309017 ], [ - 0.525731, 0.850651, 0.000000 ],
+			[ 0.000000, 0.850651, - 0.525731 ], [ - 0.238856, 0.864188, - 0.442863 ],
+			[ 0.000000, 0.955423, - 0.295242 ], [ - 0.262866, 0.951056, - 0.162460 ],
+			[ 0.000000, 1.000000, 0.000000 ], [ 0.000000, 0.955423, 0.295242 ],
+			[ - 0.262866, 0.951056, 0.162460 ], [ 0.238856, 0.864188, 0.442863 ],
+			[ 0.262866, 0.951056, 0.162460 ], [ 0.500000, 0.809017, 0.309017 ],
+			[ 0.238856, 0.864188, - 0.442863 ], [ 0.262866, 0.951056, - 0.162460 ],
+			[ 0.500000, 0.809017, - 0.309017 ], [ 0.850651, 0.525731, 0.000000 ],
+			[ 0.716567, 0.681718, 0.147621 ], [ 0.716567, 0.681718, - 0.147621 ],
+			[ 0.525731, 0.850651, 0.000000 ], [ 0.425325, 0.688191, 0.587785 ],
+			[ 0.864188, 0.442863, 0.238856 ], [ 0.688191, 0.587785, 0.425325 ],
+			[ 0.809017, 0.309017, 0.500000 ], [ 0.681718, 0.147621, 0.716567 ],
+			[ 0.587785, 0.425325, 0.688191 ], [ 0.955423, 0.295242, 0.000000 ],
+			[ 1.000000, 0.000000, 0.000000 ], [ 0.951056, 0.162460, 0.262866 ],
+			[ 0.850651, - 0.525731, 0.000000 ], [ 0.955423, - 0.295242, 0.000000 ],
+			[ 0.864188, - 0.442863, 0.238856 ], [ 0.951056, - 0.162460, 0.262866 ],
+			[ 0.809017, - 0.309017, 0.500000 ], [ 0.681718, - 0.147621, 0.716567 ],
+			[ 0.850651, 0.000000, 0.525731 ], [ 0.864188, 0.442863, - 0.238856 ],
+			[ 0.809017, 0.309017, - 0.500000 ], [ 0.951056, 0.162460, - 0.262866 ],
+			[ 0.525731, 0.000000, - 0.850651 ], [ 0.681718, 0.147621, - 0.716567 ],
+			[ 0.681718, - 0.147621, - 0.716567 ], [ 0.850651, 0.000000, - 0.525731 ],
+			[ 0.809017, - 0.309017, - 0.500000 ], [ 0.864188, - 0.442863, - 0.238856 ],
+			[ 0.951056, - 0.162460, - 0.262866 ], [ 0.147621, 0.716567, - 0.681718 ],
+			[ 0.309017, 0.500000, - 0.809017 ], [ 0.425325, 0.688191, - 0.587785 ],
+			[ 0.442863, 0.238856, - 0.864188 ], [ 0.587785, 0.425325, - 0.688191 ],
+			[ 0.688191, 0.587785, - 0.425325 ], [ - 0.147621, 0.716567, - 0.681718 ],
+			[ - 0.309017, 0.500000, - 0.809017 ], [ 0.000000, 0.525731, - 0.850651 ],
+			[ - 0.525731, 0.000000, - 0.850651 ], [ - 0.442863, 0.238856, - 0.864188 ],
+			[ - 0.295242, 0.000000, - 0.955423 ], [ - 0.162460, 0.262866, - 0.951056 ],
+			[ 0.000000, 0.000000, - 1.000000 ], [ 0.295242, 0.000000, - 0.955423 ],
+			[ 0.162460, 0.262866, - 0.951056 ], [ - 0.442863, - 0.238856, - 0.864188 ],
+			[ - 0.309017, - 0.500000, - 0.809017 ], [ - 0.162460, - 0.262866, - 0.951056 ],
+			[ 0.000000, - 0.850651, - 0.525731 ], [ - 0.147621, - 0.716567, - 0.681718 ],
+			[ 0.147621, - 0.716567, - 0.681718 ], [ 0.000000, - 0.525731, - 0.850651 ],
+			[ 0.309017, - 0.500000, - 0.809017 ], [ 0.442863, - 0.238856, - 0.864188 ],
+			[ 0.162460, - 0.262866, - 0.951056 ], [ 0.238856, - 0.864188, - 0.442863 ],
+			[ 0.500000, - 0.809017, - 0.309017 ], [ 0.425325, - 0.688191, - 0.587785 ],
+			[ 0.716567, - 0.681718, - 0.147621 ], [ 0.688191, - 0.587785, - 0.425325 ],
+			[ 0.587785, - 0.425325, - 0.688191 ], [ 0.000000, - 0.955423, - 0.295242 ],
+			[ 0.000000, - 1.000000, 0.000000 ], [ 0.262866, - 0.951056, - 0.162460 ],
+			[ 0.000000, - 0.850651, 0.525731 ], [ 0.000000, - 0.955423, 0.295242 ],
+			[ 0.238856, - 0.864188, 0.442863 ], [ 0.262866, - 0.951056, 0.162460 ],
+			[ 0.500000, - 0.809017, 0.309017 ], [ 0.716567, - 0.681718, 0.147621 ],
+			[ 0.525731, - 0.850651, 0.000000 ], [ - 0.238856, - 0.864188, - 0.442863 ],
+			[ - 0.500000, - 0.809017, - 0.309017 ], [ - 0.262866, - 0.951056, - 0.162460 ],
+			[ - 0.850651, - 0.525731, 0.000000 ], [ - 0.716567, - 0.681718, - 0.147621 ],
+			[ - 0.716567, - 0.681718, 0.147621 ], [ - 0.525731, - 0.850651, 0.000000 ],
+			[ - 0.500000, - 0.809017, 0.309017 ], [ - 0.238856, - 0.864188, 0.442863 ],
+			[ - 0.262866, - 0.951056, 0.162460 ], [ - 0.864188, - 0.442863, 0.238856 ],
+			[ - 0.809017, - 0.309017, 0.500000 ], [ - 0.688191, - 0.587785, 0.425325 ],
+			[ - 0.681718, - 0.147621, 0.716567 ], [ - 0.442863, - 0.238856, 0.864188 ],
+			[ - 0.587785, - 0.425325, 0.688191 ], [ - 0.309017, - 0.500000, 0.809017 ],
+			[ - 0.147621, - 0.716567, 0.681718 ], [ - 0.425325, - 0.688191, 0.587785 ],
+			[ - 0.162460, - 0.262866, 0.951056 ], [ 0.442863, - 0.238856, 0.864188 ],
+			[ 0.162460, - 0.262866, 0.951056 ], [ 0.309017, - 0.500000, 0.809017 ],
+			[ 0.147621, - 0.716567, 0.681718 ], [ 0.000000, - 0.525731, 0.850651 ],
+			[ 0.425325, - 0.688191, 0.587785 ], [ 0.587785, - 0.425325, 0.688191 ],
+			[ 0.688191, - 0.587785, 0.425325 ], [ - 0.955423, 0.295242, 0.000000 ],
+			[ - 0.951056, 0.162460, 0.262866 ], [ - 1.000000, 0.000000, 0.000000 ],
+			[ - 0.850651, 0.000000, 0.525731 ], [ - 0.955423, - 0.295242, 0.000000 ],
+			[ - 0.951056, - 0.162460, 0.262866 ], [ - 0.864188, 0.442863, - 0.238856 ],
+			[ - 0.951056, 0.162460, - 0.262866 ], [ - 0.809017, 0.309017, - 0.500000 ],
+			[ - 0.864188, - 0.442863, - 0.238856 ], [ - 0.951056, - 0.162460, - 0.262866 ],
+			[ - 0.809017, - 0.309017, - 0.500000 ], [ - 0.681718, 0.147621, - 0.716567 ],
+			[ - 0.681718, - 0.147621, - 0.716567 ], [ - 0.850651, 0.000000, - 0.525731 ],
+			[ - 0.688191, 0.587785, - 0.425325 ], [ - 0.587785, 0.425325, - 0.688191 ],
+			[ - 0.425325, 0.688191, - 0.587785 ], [ - 0.425325, - 0.688191, - 0.587785 ],
+			[ - 0.587785, - 0.425325, - 0.688191 ], [ - 0.688191, - 0.587785, - 0.425325 ]
+		];
+
+		return function ( buffer ) {
+
+			console.time( 'MD2Loader' );
+
+			var data = new DataView( buffer );
+
+			// http://tfc.duke.free.fr/coding/md2-specs-en.html
+
+			var header = {};
+			var headerNames = [
+				'ident', 'version',
+				'skinwidth', 'skinheight',
+				'framesize',
+				'num_skins', 'num_vertices', 'num_st', 'num_tris', 'num_glcmds', 'num_frames',
+				'offset_skins', 'offset_st', 'offset_tris', 'offset_frames', 'offset_glcmds', 'offset_end'
+			];
+
+			for ( var i = 0; i < headerNames.length; i ++ ) {
+
+				header[ headerNames[ i ] ] = data.getInt32( i * 4, true );
+
+			}
+
+			if ( header.ident !== 844121161 || header.version !== 8 ) {
+
+				console.error( 'Not a valid MD2 file' );
+				return;
+
+			}
+
+			if ( header.offset_end !== data.byteLength ) {
+
+				console.error( 'Corrupted MD2 file' );
+				return;
+
+			}
+
+			//
+
+			var geometry = new BufferGeometry();
+
+			// uvs
+
+			var uvsTemp = [];
+			var offset = header.offset_st;
+
+			for ( var i = 0, l = header.num_st; i < l; i ++ ) {
+
+				var u = data.getInt16( offset + 0, true );
+				var v = data.getInt16( offset + 2, true );
+
+				uvsTemp.push( u / header.skinwidth, 1 - ( v / header.skinheight ) );
+
+				offset += 4;
+
+			}
+
+			// triangles
+
+			offset = header.offset_tris;
+
+			var vertexIndices = [];
+			var uvIndices = [];
+
+			for ( var i = 0, l = header.num_tris; i < l; i ++ ) {
+
+				vertexIndices.push(
+					data.getUint16( offset + 0, true ),
+					data.getUint16( offset + 2, true ),
+					data.getUint16( offset + 4, true )
+				);
+
+				uvIndices.push(
+					data.getUint16( offset + 6, true ),
+					data.getUint16( offset + 8, true ),
+					data.getUint16( offset + 10, true )
+				);
+
+				offset += 12;
+
+			}
+
+			// frames
+
+			var translation = new Vector3();
+			var scale = new Vector3();
+			var string = [];
+
+			var frames = [];
+
+			offset = header.offset_frames;
+
+			for ( var i = 0, l = header.num_frames; i < l; i ++ ) {
+
+				scale.set(
+					data.getFloat32( offset + 0, true ),
+					data.getFloat32( offset + 4, true ),
+					data.getFloat32( offset + 8, true )
+				);
+
+				translation.set(
+					data.getFloat32( offset + 12, true ),
+					data.getFloat32( offset + 16, true ),
+					data.getFloat32( offset + 20, true )
+				);
+
+				offset += 24;
+
+				for ( var j = 0; j < 16; j ++ ) {
+
+					var character = data.getUint8( offset + j, true );
+					if ( character === 0 ) break;
+
+					string[ j ] = character;
+
+				}
+
+				var frame = {
+					name: String.fromCharCode.apply( null, string ),
+					vertices: [],
+					normals: []
+				};
+
+				offset += 16;
+
+				for ( var j = 0; j < header.num_vertices; j ++ ) {
+
+					var x = data.getUint8( offset ++, true );
+					var y = data.getUint8( offset ++, true );
+					var z = data.getUint8( offset ++, true );
+					var n = normalData[ data.getUint8( offset ++, true ) ];
+
+					x = x * scale.x + translation.x;
+					y = y * scale.y + translation.y;
+					z = z * scale.z + translation.z;
+
+					frame.vertices.push( x, z, y ); // convert to Y-up
+					frame.normals.push( n[ 0 ], n[ 2 ], n[ 1 ] ); // convert to Y-up
+
+				}
+
+				frames.push( frame );
+
+			}
+
+			// static
+
+			var positions = [];
+			var normals = [];
+			var uvs = [];
+
+			var verticesTemp = frames[ 0 ].vertices;
+			var normalsTemp = frames[ 0 ].normals;
+
+			for ( var i = 0, l = vertexIndices.length; i < l; i ++ ) {
+
+				var vertexIndex = vertexIndices[ i ];
+				var stride = vertexIndex * 3;
+
+				//
+
+				var x = verticesTemp[ stride ];
+				var y = verticesTemp[ stride + 1 ];
+				var z = verticesTemp[ stride + 2 ];
+
+				positions.push( x, y, z );
+
+				//
+
+				var nx = normalsTemp[ stride ];
+				var ny = normalsTemp[ stride + 1 ];
+				var nz = normalsTemp[ stride + 2 ];
+
+				normals.push( nx, ny, nz );
+
+				//
+
+				var uvIndex = uvIndices[ i ];
+				stride = uvIndex * 2;
+
+				var u = uvsTemp[ stride ];
+				var v = uvsTemp[ stride + 1 ];
+
+				uvs.push( u, v );
+
+			}
+
+			geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+			geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+			geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+
+			// animation
+
+			var morphPositions = [];
+			var morphNormals = [];
+
+			for ( var i = 0, l = frames.length; i < l; i ++ ) {
+
+				var frame = frames[ i ];
+				var attributeName = frame.name;
+
+				if ( frame.vertices.length > 0 ) {
+
+					var positions = [];
+
+					for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) {
+
+						var vertexIndex = vertexIndices[ j ];
+						var stride = vertexIndex * 3;
+
+						var x = frame.vertices[ stride ];
+						var y = frame.vertices[ stride + 1 ];
+						var z = frame.vertices[ stride + 2 ];
+
+						positions.push( x, y, z );
+
+					}
+
+					var positionAttribute = new Float32BufferAttribute( positions, 3 );
+					positionAttribute.name = attributeName;
+
+					morphPositions.push( positionAttribute );
+
+				}
+
+				if ( frame.normals.length > 0 ) {
+
+					var normals = [];
+
+					for ( var j = 0, jl = vertexIndices.length; j < jl; j ++ ) {
+
+						var vertexIndex = vertexIndices[ j ];
+						var stride = vertexIndex * 3;
+
+						var nx = frame.normals[ stride ];
+						var ny = frame.normals[ stride + 1 ];
+						var nz = frame.normals[ stride + 2 ];
+
+						normals.push( nx, ny, nz );
+
+					}
+
+					var normalAttribute = new Float32BufferAttribute( normals, 3 );
+					normalAttribute.name = attributeName;
+
+					morphNormals.push( normalAttribute );
+
+				}
+
+			}
+
+			geometry.morphAttributes.position = morphPositions;
+			geometry.morphAttributes.normal = morphNormals;
+
+			geometry.animations = AnimationClip.CreateClipsFromMorphTargetSequences( frames, 10 );
+
+			console.timeEnd( 'MD2Loader' );
+
+			return geometry;
+
+		};
+
+	} )()
+
+};
+
+export { MD2Loader };

+ 36 - 0
examples/jsm/loaders/MMDLoader.d.ts

@@ -0,0 +1,36 @@
+import {
+  AnimationClip,
+  FileLoader,
+  LoadingManager,
+  SkinnedMesh
+} from '../../../src/Three';
+
+export interface MMDLoaderAnimationObject {
+  animation: AnimationClip;
+  mesh: SkinnedMesh;
+}
+
+export class MMDLoader {
+  constructor(manager?: LoadingManager);
+  animationBuilder: object;
+  animationPath: string;
+  crossOrigin: string;
+  loader: FileLoader;
+  manager: LoadingManager;
+  meshBuilder: object;
+  path: string;
+  parser: object | null;
+  resourcePath: string;
+
+  load(url: string, onLoad: (mesh: SkinnedMesh) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadAnimation(url: string, onLoad: (object: SkinnedMesh | AnimationClip) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadPMD(url: string, onLoad: (object: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadPMX(url: string, onLoad: (object: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadVMD(url: string, onLoad: (object: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadVPD(url: string, onLoad: (object: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  loadWithAnimation(url: string, onLoad: (object: MMDLoaderAnimationObject) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setAnimationPath(animationPath: string): this;
+  setCrossOrigin(crossOrigin: string): this;
+  setPath(path: string): this;
+  setResoucePath(resourcePath: string): this;
+}

+ 2062 - 0
examples/jsm/loaders/MMDLoader.js

@@ -0,0 +1,2062 @@
+/**
+ * @author takahiro / https://github.com/takahirox
+ *
+ * Dependencies
+ *  - mmd-parser https://github.com/takahirox/mmd-parser
+ *  - TGALoader
+ *  - OutlineEffect
+ *
+ * MMDLoader creates Three.js Objects from MMD resources as
+ * PMD, PMX, VMD, and VPD files.
+ *
+ * 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
+ *
+ * PMD, VMD format (in Japanese)
+ *  - http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
+ *
+ * PMX format
+ *  - https://gist.github.com/felixjones/f8a06bd48f9da9a4539f
+ *
+ * TODO
+ *  - light motion in vmd support.
+ *  - SDEF support.
+ *  - uv/material/bone morphing support.
+ *  - more precise grant skinning support.
+ *  - shadow support.
+ */
+
+import {
+	AddOperation,
+	AnimationClip,
+	Bone,
+	BufferGeometry,
+	Color,
+	CustomBlending,
+	DefaultLoadingManager,
+	DoubleSide,
+	DstAlphaFactor,
+	Euler,
+	FileLoader,
+	Float32BufferAttribute,
+	FrontSide,
+	Interpolant,
+	Loader,
+	LoaderUtils,
+	MeshToonMaterial,
+	MultiplyOperation,
+	NearestFilter,
+	NumberKeyframeTrack,
+	OneMinusSrcAlphaFactor,
+	Quaternion,
+	QuaternionKeyframeTrack,
+	RepeatWrapping,
+	Skeleton,
+	SkinnedMesh,
+	SphericalReflectionMapping,
+	SrcAlphaFactor,
+	TextureLoader,
+	Uint16BufferAttribute,
+	Vector3,
+	VectorKeyframeTrack
+} from "../../../build/three.module.js";
+import { TGALoader } from "../loaders/TGALoader.js";
+import { MMDParser } from "../libs/mmdparser.module.js";
+
+var MMDLoader = ( function () {
+
+	/**
+	 * @param {THREE.LoadingManager} manager
+	 */
+	function MMDLoader( manager ) {
+
+		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+		this.loader = new FileLoader( this.manager );
+
+		this.parser = null; // lazy generation
+		this.meshBuilder = new MeshBuilder( this.manager );
+		this.animationBuilder = new AnimationBuilder();
+
+	}
+
+	MMDLoader.prototype = {
+
+		constructor: MMDLoader,
+
+		crossOrigin: 'anonymous',
+
+		/**
+		 * @param {string} crossOrigin
+		 * @return {MMDLoader}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
+
+			this.crossOrigin = crossOrigin;
+			return this;
+
+		},
+
+		/**
+		 * @param {string} animationPath
+		 * @return {MMDLoader}
+		 */
+		setAnimationPath: function ( animationPath ) {
+
+			this.animationPath = animationPath;
+			return this;
+
+		},
+
+		/**
+		 * @param {string} path
+		 * @return {MMDLoader}
+		 */
+		setPath: function ( path ) {
+
+			this.path = path;
+			return this;
+
+		},
+
+		/**
+		 * @param {string} resourcePath
+		 * @return {MMDLoader}
+		 */
+		setResoucePath: function ( resourcePath ) {
+
+			this.resourcePath = resourcePath;
+			return this;
+
+		},
+
+		// Load MMD assets as Three.js Object
+
+		/**
+		 * Loads Model file (.pmd or .pmx) as a 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 ) {
+
+			var builder = this.meshBuilder.setCrossOrigin( this.crossOrigin );
+
+			// resource path
+
+			var resourcePath;
+
+			if ( this.resourcePath !== undefined ) {
+
+				resourcePath = this.resourcePath;
+
+			} else if ( this.path !== undefined ) {
+
+				resourcePath = this.path;
+
+			} else {
+
+				resourcePath = LoaderUtils.extractUrlBase( url );
+
+			}
+
+			var modelExtension = this._extractExtension( url ).toLowerCase();
+
+			// Should I detect by seeing header?
+			if ( modelExtension !== 'pmd' && modelExtension !== 'pmx' ) {
+
+				if ( onError ) onError( new Error( 'THREE.MMDLoader: Unknown model file extension .' + modelExtension + '.' ) );
+
+				return;
+
+			}
+
+			this[ modelExtension === 'pmd' ? 'loadPMD' : 'loadPMX' ]( url, function ( data ) {
+
+				onLoad(	builder.build( data, resourcePath, onProgress, onError )	);
+
+			}, onProgress, onError );
+
+		},
+
+		/**
+		 * Loads Motion file(s) (.vmd) as a 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 {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 builder = this.animationBuilder;
+
+			this.loadVMD( url, function ( vmd ) {
+
+				onLoad( object.isCamera
+					? builder.buildCameraAnimation( vmd )
+					: builder.build( vmd, object ) );
+
+			}, onProgress, onError );
+
+		},
+
+		/**
+		 * Loads mode file and motion file(s) as an object containing
+		 * a SkinnedMesh and a AnimationClip.
+		 * Tracks of 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;
+
+			this.load( modelUrl, function ( mesh ) {
+
+				scope.loadAnimation( vmdUrl, mesh, function ( animation ) {
+
+					onLoad( {
+						mesh: mesh,
+						animation: animation
+					} );
+
+				}, onProgress, onError );
+
+			}, onProgress, onError );
+
+		},
+
+		// Load MMD assets as Object data parsed by MMDParser
+
+		/**
+		 * 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 ) {
+
+			var parser = this._getParser();
+
+			this.loader
+				.setMimeType( undefined )
+				.setPath( this.path )
+				.setResponseType( 'arraybuffer' )
+				.load( url, function ( buffer ) {
+
+					onLoad( parser.parsePmd( buffer, true ) );
+
+				}, onProgress, onError );
+
+		},
+
+		/**
+		 * 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 ) {
+
+			var parser = this._getParser();
+
+			this.loader
+				.setMimeType( undefined )
+				.setPath( this.path )
+				.setResponseType( 'arraybuffer' )
+				.load( url, function ( buffer ) {
+
+					onLoad( parser.parsePmx( buffer, true ) );
+
+				}, 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 ) {
+
+			var urls = Array.isArray( url ) ? url : [ url ];
+
+			var vmds = [];
+			var vmdNum = urls.length;
+
+			var parser = this._getParser();
+
+			this.loader
+				.setMimeType( undefined )
+				.setPath( this.animationPath )
+				.setResponseType( 'arraybuffer' );
+
+			for ( var i = 0, il = urls.length; i < il; i ++ ) {
+
+				this.loader.load( urls[ i ], function ( buffer ) {
+
+					vmds.push( parser.parseVmd( buffer, true ) );
+
+					if ( vmds.length === vmdNum ) onLoad( parser.mergeVmds( vmds ) );
+
+				}, onProgress, onError );
+
+			}
+
+		},
+
+		/**
+		 * 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 ) {
+
+			var parser = this._getParser();
+
+			this.loader
+				.setMimeType( isUnicode ? undefined : 'text/plain; charset=shift_jis' )
+				.setPath( this.animationPath )
+				.setResponseType( 'text' )
+				.load( url, function ( text ) {
+
+					onLoad( parser.parseVpd( text, true ) );
+
+				}, onProgress, onError );
+
+		},
+
+		// private methods
+
+		_extractExtension: function ( url ) {
+
+			var index = url.lastIndexOf( '.' );
+			return index < 0 ? '' : url.slice( index + 1 );
+
+		},
+
+		_getParser: function () {
+
+			if ( this.parser === null ) {
+
+				if ( typeof MMDParser === 'undefined' ) {
+
+					throw new Error( 'THREE.MMDLoader: Import MMDParser https://github.com/takahirox/mmd-parser' );
+
+				}
+
+				this.parser = new MMDParser.Parser();
+
+			}
+
+			return this.parser;
+
+		}
+
+	};
+
+	// Utilities
+
+	/*
+	 * 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 ) {
+
+		this.geometryBuilder = new GeometryBuilder();
+		this.materialBuilder = new MaterialBuilder( manager );
+
+	}
+
+	MeshBuilder.prototype = {
+
+		constructor: MeshBuilder,
+
+		crossOrigin: 'anonymous',
+
+		/**
+		 * @param {string} crossOrigin
+		 * @return {MeshBuilder}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
+
+			this.crossOrigin = crossOrigin;
+			return this;
+
+		},
+
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @param {string} resourcePath
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 * @return {SkinnedMesh}
+		 */
+		build: function ( data, resourcePath, onProgress, onError ) {
+
+			var geometry = this.geometryBuilder.build( data );
+			var material = this.materialBuilder
+				.setCrossOrigin( this.crossOrigin )
+				.setResourcePath( resourcePath )
+				.build( data, geometry, onProgress, onError );
+
+			var mesh = new SkinnedMesh( geometry, material );
+
+			var skeleton = new Skeleton( initBones( mesh ) );
+			mesh.bind( skeleton );
+
+			// console.log( mesh ); // for console debug
+
+			return mesh;
+
+		}
+
+	};
+
+	// TODO: Try to remove this function
+
+	function initBones( mesh ) {
+
+		var geometry = mesh.geometry;
+
+		var bones = [], bone, gbone;
+		var i, il;
+
+		if ( geometry && geometry.bones !== undefined ) {
+
+			// first, create array of 'Bone' objects from geometry data
+
+			for ( i = 0, il = geometry.bones.length; i < il; i ++ ) {
+
+				gbone = geometry.bones[ i ];
+
+				// create new 'Bone' object
+
+				bone = new Bone();
+				bones.push( bone );
+
+				// apply values
+
+				bone.name = gbone.name;
+				bone.position.fromArray( gbone.pos );
+				bone.quaternion.fromArray( gbone.rotq );
+				if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
+
+			}
+
+			// second, create bone hierarchy
+
+			for ( i = 0, il = geometry.bones.length; i < il; i ++ ) {
+
+				gbone = geometry.bones[ i ];
+
+				if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
+
+					// subsequent bones in the hierarchy
+
+					bones[ gbone.parent ].add( bones[ i ] );
+
+				} else {
+
+					// topmost bone, immediate child of the skinned mesh
+
+					mesh.add( bones[ i ] );
+
+				}
+
+			}
+
+		}
+
+		// now the bones are part of the scene graph and children of the skinned mesh.
+		// let's update the corresponding matrices
+
+		mesh.updateMatrixWorld( true );
+
+		return bones;
+
+	}
+
+	//
+
+	function GeometryBuilder() {
+
+	}
+
+	GeometryBuilder.prototype = {
+
+		constructor: GeometryBuilder,
+
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @return {BufferGeometry}
+		 */
+		build: function ( data ) {
+
+			// for geometry
+			var positions = [];
+			var uvs = [];
+			var normals = [];
+
+			var indices = [];
+
+			var groups = [];
+
+			var bones = [];
+			var skinIndices = [];
+			var skinWeights = [];
+
+			var morphTargets = [];
+			var morphPositions = [];
+
+			var iks = [];
+			var grants = [];
+
+			var rigidBodies = [];
+			var constraints = [];
+
+			// for work
+			var offset = 0;
+			var boneTypeTable = {};
+
+			// positions, normals, uvs, skinIndices, skinWeights
+
+			for ( var i = 0; i < data.metadata.vertexCount; i ++ ) {
+
+				var v = data.vertices[ i ];
+
+				for ( var j = 0, jl = v.position.length; j < jl; j ++ ) {
+
+					positions.push( v.position[ j ] );
+
+				}
+
+				for ( var j = 0, jl = v.normal.length; j < jl; j ++ ) {
+
+					normals.push( v.normal[ j ] );
+
+				}
+
+				for ( var j = 0, jl = v.uv.length; j < jl; j ++ ) {
+
+					uvs.push( v.uv[ j ] );
+
+				}
+
+				for ( var j = 0; j < 4; j ++ ) {
+
+					skinIndices.push( v.skinIndices.length - 1 >= j ? v.skinIndices[ j ] : 0.0 );
+
+				}
+
+				for ( var j = 0; j < 4; j ++ ) {
+
+					skinWeights.push( v.skinWeights.length - 1 >= j ? v.skinWeights[ j ] : 0.0 );
+
+				}
+
+			}
+
+			// indices
+
+			for ( var i = 0; i < data.metadata.faceCount; i ++ ) {
+
+				var face = data.faces[ i ];
+
+				for ( var j = 0, jl = face.indices.length; j < jl; j ++ ) {
+
+					indices.push( face.indices[ j ] );
+
+				}
+
+			}
+
+			// groups
+
+			for ( var i = 0; i < data.metadata.materialCount; i ++ ) {
+
+				var material = data.materials[ i ];
+
+				groups.push( {
+					offset: offset * 3,
+					count: material.faceCount * 3
+				} );
+
+				offset += material.faceCount;
+
+			}
+
+			// bones
+
+			for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) {
+
+				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 );
+
+				boneTypeTable[ body.boneIndex ] = value;
+
+			}
+
+			for ( var i = 0; i < data.metadata.boneCount; i ++ ) {
+
+				var boneData = data.bones[ i ];
+
+				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
+				};
+
+				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 ];
+
+				}
+
+				bones.push( bone );
+
+			}
+
+			// iks
+
+			// TODO: remove duplicated codes between PMD and PMX
+			if ( data.metadata.format === 'pmd' ) {
+
+				for ( var i = 0; i < data.metadata.ikCount; i ++ ) {
+
+					var ik = data.iks[ i ];
+
+					var param = {
+						target: ik.target,
+						effector: ik.effector,
+						iteration: ik.iteration,
+						maxAngle: ik.maxAngle * 4,
+						links: []
+					};
+
+					for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+						var link = {};
+						link.index = ik.links[ j ].index;
+						link.enabled = true;
+
+						if ( data.bones[ link.index ].name.indexOf( 'ひざ' ) >= 0 ) {
+
+							link.limitation = new Vector3( 1.0, 0.0, 0.0 );
+
+						}
+
+						param.links.push( link );
+
+					}
+
+					iks.push( param );
+
+				}
+
+			} else {
+
+				for ( var i = 0; i < data.metadata.boneCount; i ++ ) {
+
+					var ik = data.bones[ i ].ik;
+
+					if ( ik === undefined ) continue;
+
+					var param = {
+						target: i,
+						effector: ik.effector,
+						iteration: ik.iteration,
+						maxAngle: ik.maxAngle,
+						links: []
+					};
+
+					for ( var j = 0, jl = ik.links.length; j < jl; j ++ ) {
+
+						var link = {};
+						link.index = ik.links[ j ].index;
+						link.enabled = true;
+
+						if ( ik.links[ j ].angleLimitation === 1 ) {
+
+							// Revert if rotationMin/Max doesn't work well
+							// link.limitation = new Vector3( 1.0, 0.0, 0.0 );
+
+							var rotationMin = ik.links[ j ].lowerLimitationAngle;
+							var rotationMax = ik.links[ j ].upperLimitationAngle;
+
+							// Convert Left to Right coordinate by myself because
+							// MMDParser doesn't convert. It's a MMDParser's bug
+
+							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 Vector3().fromArray( rotationMin );
+							link.rotationMax = new Vector3().fromArray( rotationMax );
+
+						}
+
+						param.links.push( link );
+
+					}
+
+					iks.push( param );
+
+				}
+
+			}
+
+			// grants
+
+			if ( data.metadata.format === 'pmx' ) {
+
+				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
+					};
+
+					grants.push( param );
+
+				}
+
+				grants.sort( function ( a, b ) {
+
+					return a.transformationClass - b.transformationClass;
+
+				} );
+
+			}
+
+			// morph
+
+			function updateAttributes( attribute, morph, ratio ) {
+
+				for ( var i = 0; i < morph.elementCount; i ++ ) {
+
+					var element = morph.elements[ i ];
+
+					var index;
+
+					if ( data.metadata.format === 'pmd' ) {
+
+						index = data.morphs[ 0 ].elements[ element.index ].index;
+
+					} else {
+
+						index = element.index;
+
+					}
+
+					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;
+
+				}
+
+			}
+
+			for ( var i = 0; i < data.metadata.morphCount; i ++ ) {
+
+				var morph = data.morphs[ i ];
+				var params = { name: morph.name };
+
+				var attribute = new Float32BufferAttribute( data.metadata.vertexCount * 3, 3 );
+				attribute.name = morph.name;
+
+				for ( var j = 0; j < data.metadata.vertexCount * 3; j ++ ) {
+
+					attribute.array[ j ] = positions[ j ];
+
+				}
+
+				if ( data.metadata.format === 'pmd' ) {
+
+					if ( i !== 0 ) {
+
+						updateAttributes( attribute, morph, 1.0 );
+
+					}
+
+				} else {
+
+					if ( morph.type === 0 ) { // group
+
+						for ( var j = 0; j < morph.elementCount; j ++ ) {
+
+							var morph2 = data.morphs[ morph.elements[ j ].index ];
+							var ratio = morph.elements[ j ].ratio;
+
+							if ( morph2.type === 1 ) {
+
+								updateAttributes( attribute, morph2, ratio );
+
+							} else {
+
+								// TODO: implement
+
+							}
+
+						}
+
+					} else if ( morph.type === 1 ) { // vertex
+
+						updateAttributes( attribute, morph, 1.0 );
+
+					} else if ( morph.type === 2 ) { // bone
+
+						// TODO: implement
+
+					} else if ( morph.type === 3 ) { // uv
+
+						// TODO: implement
+
+					} else if ( morph.type === 4 ) { // additional uv1
+
+						// TODO: implement
+
+					} else if ( morph.type === 5 ) { // additional uv2
+
+						// TODO: implement
+
+					} else if ( morph.type === 6 ) { // additional uv3
+
+						// TODO: implement
+
+					} else if ( morph.type === 7 ) { // additional uv4
+
+						// TODO: implement
+
+					} else if ( morph.type === 8 ) { // material
+
+						// TODO: implement
+
+					}
+
+				}
+
+				morphTargets.push( params );
+				morphPositions.push( attribute );
+
+			}
+
+			// rigid bodies from rigidBodies field.
+
+			for ( var i = 0; i < data.metadata.rigidBodyCount; i ++ ) {
+
+				var rigidBody = data.rigidBodies[ i ];
+				var params = {};
+
+				for ( var key in rigidBody ) {
+
+					params[ key ] = rigidBody[ key ];
+
+				}
+
+				/*
+				 * 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' ) {
+
+					if ( params.boneIndex !== - 1 ) {
+
+						var bone = data.bones[ params.boneIndex ];
+						params.position[ 0 ] -= bone.position[ 0 ];
+						params.position[ 1 ] -= bone.position[ 1 ];
+						params.position[ 2 ] -= bone.position[ 2 ];
+
+					}
+
+				}
+
+				rigidBodies.push( params );
+
+			}
+
+			// constraints from constraints field.
+
+			for ( var i = 0; i < data.metadata.constraintCount; i ++ ) {
+
+				var constraint = data.constraints[ i ];
+				var params = {};
+
+				for ( var key in constraint ) {
+
+					params[ key ] = constraint[ key ];
+
+				}
+
+				var bodyA = rigidBodies[ params.rigidBodyIndex1 ];
+				var bodyB = rigidBodies[ params.rigidBodyIndex2 ];
+
+				// 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 ) {
+
+						bodyB.type = 1;
+
+					}
+
+				}
+
+				constraints.push( params );
+
+			}
+
+			// build BufferGeometry.
+
+			var geometry = new BufferGeometry();
+
+			geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+			geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+			geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+			geometry.addAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) );
+			geometry.addAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) );
+			geometry.setIndex( indices );
+
+			for ( var i = 0, il = groups.length; i < il; i ++ ) {
+
+				geometry.addGroup( groups[ i ].offset, groups[ i ].count, i );
+
+			}
+
+			geometry.bones = bones;
+
+			geometry.morphTargets = morphTargets;
+			geometry.morphAttributes.position = morphPositions;
+
+			geometry.userData.MMD = {
+				bones: bones,
+				iks: iks,
+				grants: grants,
+				rigidBodies: rigidBodies,
+				constraints: constraints,
+				format: data.metadata.format
+			};
+
+			geometry.computeBoundingSphere();
+
+			return geometry;
+
+		}
+
+	};
+
+	//
+
+	/**
+	 * @param {THREE.LoadingManager} manager
+	 */
+	function MaterialBuilder( manager ) {
+
+		this.manager = manager;
+
+		this.textureLoader = new TextureLoader( this.manager );
+		this.tgaLoader = null; // lazy generation
+
+	}
+
+	MaterialBuilder.prototype = {
+
+		constructor: MaterialBuilder,
+
+		crossOrigin: 'anonymous',
+
+		resourcePath: undefined,
+
+		/**
+		 * @param {string} crossOrigin
+		 * @return {MaterialBuilder}
+		 */
+		setCrossOrigin: function ( crossOrigin ) {
+
+			this.crossOrigin = crossOrigin;
+			return this;
+
+		},
+
+		/**
+		 * @param {string} resourcePath
+		 * @return {MaterialBuilder}
+		 */
+		setResourcePath: function ( resourcePath ) {
+
+			this.resourcePath = resourcePath;
+			return this;
+
+		},
+
+		/**
+		 * @param {Object} data - parsed PMD/PMX data
+		 * @param {BufferGeometry} geometry - some properties are dependend on geometry
+		 * @param {function} onProgress
+		 * @param {function} onError
+		 * @return {Array<MeshToonMaterial>}
+		 */
+		build: function ( data, geometry /*, onProgress, onError */ ) {
+
+			var materials = [];
+
+			var textures = {};
+
+			this.textureLoader.setCrossOrigin( this.crossOrigin );
+
+			// materials
+
+			for ( var i = 0; i < data.metadata.materialCount; i ++ ) {
+
+				var material = data.materials[ i ];
+
+				var params = { userData: {} };
+
+				if ( material.name !== undefined ) params.name = material.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 Color().fromArray( material.diffuse );
+				params.opacity = material.diffuse[ 3 ];
+				params.specular = new Color().fromArray( material.specular );
+				params.emissive = new 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;
+
+				//
+
+				params.skinning = geometry.bones.length > 0 ? true : false;
+				params.morphTargets = geometry.morphTargets.length > 0 ? true : false;
+				params.lights = true;
+				params.fog = true;
+
+				// blend
+
+				params.blending = CustomBlending;
+				params.blendSrc = SrcAlphaFactor;
+				params.blendDst = OneMinusSrcAlphaFactor;
+				params.blendSrcAlpha = SrcAlphaFactor;
+				params.blendDstAlpha = DstAlphaFactor;
+
+				// side
+
+				if ( data.metadata.format === 'pmx' && ( material.flag & 0x1 ) === 1 ) {
+
+					params.side = DoubleSide;
+
+				} else {
+
+					params.side = params.opacity === 1.0 ? FrontSide : DoubleSide;
+
+				}
+
+				if ( data.metadata.format === 'pmd' ) {
+
+					// map, envMap
+
+					if ( material.fileName ) {
+
+						var fileName = material.fileName;
+						var fileNames = fileName.split( '*' );
+
+						// fileNames[ 0 ]: mapFileName
+						// fileNames[ 1 ]: envMapFileName( optional )
+
+						params.map = this._loadTexture( fileNames[ 0 ], textures );
+
+						if ( fileNames.length > 1 ) {
+
+							var extension = fileNames[ 1 ].slice( - 4 ).toLowerCase();
+
+							params.envMap = this._loadTexture(
+								fileNames[ 1 ],
+								textures,
+								{ sphericalReflectionMapping: true }
+							);
+
+							params.combine = extension === '.sph'
+								? MultiplyOperation
+								: AddOperation;
+
+						}
+
+					}
+
+					// gradientMap
+
+					var toonFileName = ( material.toonIndex === - 1 )
+						? 'toon00.bmp'
+						: data.toonTextures[ material.toonIndex ].fileName;
+
+					params.gradientMap = this._loadTexture(
+						toonFileName,
+						textures,
+						{
+							isToonTexture: true,
+							isDefaultToonTexture: this._isDefaultToonTexture( toonFileName )
+						}
+					);
+
+					// parameters for OutlineEffect
+
+					params.userData.outlineParameters = {
+						thickness: material.edgeFlag === 1 ? 0.003 : 0.0,
+						color: [ 0, 0, 0 ],
+						alpha: 1.0,
+						visible: material.edgeFlag === 1
+					};
+
+				} else {
+
+					// map
+
+					if ( material.textureIndex !== - 1 ) {
+
+						params.map = this._loadTexture( data.textures[ material.textureIndex ], textures );
+
+					}
+
+					// envMap TODO: support m.envFlag === 3
+
+					if ( material.envTextureIndex !== - 1 && ( material.envFlag === 1 || material.envFlag == 2 ) ) {
+
+						params.envMap = this._loadTexture(
+							data.textures[ material.envTextureIndex ],
+							textures, { sphericalReflectionMapping: true }
+						);
+
+						params.combine = material.envFlag === 1
+							? MultiplyOperation
+							: AddOperation;
+
+					}
+
+					// gradientMap
+
+					var toonFileName, isDefaultToon;
+
+					if ( material.toonIndex === - 1 || material.toonFlag !== 0 ) {
+
+						toonFileName = 'toon' + ( '0' + ( material.toonIndex + 1 ) ).slice( - 2 ) + '.bmp';
+						isDefaultToon = true;
+
+					} else {
+
+						toonFileName = data.textures[ material.toonIndex ];
+						isDefaultToon = false;
+
+					}
+
+					params.gradientMap = this._loadTexture(
+						toonFileName,
+						textures,
+						{
+							isToonTexture: true,
+							isDefaultToonTexture: isDefaultToon
+						}
+					);
+
+					// 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
+					};
+
+				}
+
+				if ( params.map !== undefined ) {
+
+					if ( ! params.transparent ) {
+
+						this._checkImageTransparency( params.map, geometry, i );
+
+					}
+
+					params.emissive.multiplyScalar( 0.2 );
+
+				}
+
+				materials.push( new MeshToonMaterial( params ) );
+
+			}
+
+			if ( data.metadata.format === 'pmx' ) {
+
+				// set transparent true if alpha morph is defined.
+
+				function checkAlphaMorph( elements, materials ) {
+
+					for ( var i = 0, il = elements.length; i < il; i ++ ) {
+
+						var element = elements[ i ];
+
+						if ( element.index === - 1 ) continue;
+
+						var material = materials[ element.index ];
+
+						if ( material.opacity !== element.diffuse[ 3 ] ) {
+
+							material.transparent = true;
+
+						}
+
+					}
+
+				}
+
+				for ( var i = 0, il = data.morphs.length; i < il; i ++ ) {
+
+					var morph = data.morphs[ i ];
+					var elements = morph.elements;
+
+					if ( morph.type === 0 ) {
+
+						for ( var j = 0, jl = elements.length; j < jl; j ++ ) {
+
+							var morph2 = data.morphs[ elements[ j ].index ];
+
+							if ( morph2.type !== 8 ) continue;
+
+							checkAlphaMorph( morph2.elements, materials );
+
+						}
+
+					} else if ( morph.type === 8 ) {
+
+						checkAlphaMorph( elements, materials );
+
+					}
+
+				}
+
+			}
+
+			return materials;
+
+		},
+
+		// private methods
+
+		_getTGALoader: function () {
+
+			if ( this.tgaLoader === null ) {
+
+				if ( TGALoader === undefined ) {
+
+					throw new Error( 'THREE.MMDLoader: Import TGALoader' );
+
+				}
+
+				this.tgaLoader = new TGALoader( this.manager );
+
+			}
+
+			return this.tgaLoader;
+
+		},
+
+		_isDefaultToonTexture: function ( name ) {
+
+			if ( name.length !== 10 ) return false;
+
+			return /toon(10|0[0-9])\.bmp/.test( name );
+
+		},
+
+		_loadTexture: function ( filePath, textures, params, onProgress, onError ) {
+
+			params = params || {};
+
+			var scope = this;
+
+			var fullPath;
+
+			if ( params.isDefaultToonTexture === true ) {
+
+				var index;
+
+				try {
+
+					index = parseInt( filePath.match( 'toon([0-9]{2})\.bmp$' )[ 1 ] );
+
+				} catch ( e ) {
+
+					console.warn( 'THREE.MMDLoader: ' + filePath + ' seems like a '
+						+ 'not right default texture path. Using toon00.bmp instead.' );
+
+					index = 0;
+
+				}
+
+				fullPath = DEFAULT_TOON_TEXTURES[ index ];
+
+			} else {
+
+				fullPath = this.resourcePath + filePath;
+
+			}
+
+			if ( textures[ fullPath ] !== undefined ) return textures[ fullPath ];
+
+			var loader = Loader.Handlers.get( fullPath );
+
+			if ( loader === null ) {
+
+				loader = ( filePath.slice( - 4 ).toLowerCase() === '.tga' )
+					? this._getTGALoader()
+					: this.textureLoader;
+
+			}
+
+			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 ) {
+
+					t.image = scope._getRotatedImage( t.image );
+
+					t.magFilter = NearestFilter;
+					t.minFilter = NearestFilter;
+
+				}
+
+				t.flipY = false;
+				t.wrapS = RepeatWrapping;
+				t.wrapT = RepeatWrapping;
+
+				for ( var i = 0; i < texture.readyCallbacks.length; i ++ ) {
+
+					texture.readyCallbacks[ i ]( texture );
+
+				}
+
+				delete texture.readyCallbacks;
+
+			}, onProgress, onError );
+
+			if ( params.sphericalReflectionMapping === true ) {
+
+				texture.mapping = SphericalReflectionMapping;
+
+			}
+
+			texture.readyCallbacks = [];
+
+			textures[ fullPath ] = texture;
+
+			return texture;
+
+		},
+
+		_getRotatedImage: function ( image ) {
+
+			var canvas = document.createElement( 'canvas' );
+			var context = canvas.getContext( '2d' );
+
+			var width = image.width;
+			var height = image.height;
+
+			canvas.width = width;
+			canvas.height = height;
+
+			context.clearRect( 0, 0, width, height );
+			context.translate( width / 2.0, height / 2.0 );
+			context.rotate( 0.5 * Math.PI ); // 90.0 * Math.PI / 180.0
+			context.translate( - width / 2.0, - height / 2.0 );
+			context.drawImage( image, 0, 0 );
+
+			return context.getImageData( 0, 0, width, height );
+
+		},
+
+		// Check if the partial image area used by the texture is transparent.
+		_checkImageTransparency: function ( map, geometry, groupIndex ) {
+
+			map.readyCallbacks.push( function ( texture ) {
+
+				// Is there any efficient ways?
+				function createImageData( image ) {
+
+					var canvas = document.createElement( 'canvas' );
+					canvas.width = image.width;
+					canvas.height = image.height;
+
+					var context = canvas.getContext( '2d' );
+					context.drawImage( image, 0, 0 );
+
+					return context.getImageData( 0, 0, canvas.width, canvas.height );
+
+				}
+
+				function detectImageTransparency( image, uvs, indices ) {
+
+					var width = image.width;
+					var height = image.height;
+					var data = image.data;
+					var threshold = 253;
+
+					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;
+
+				}
+
+				/*
+				 * This method expects
+				 *   texture.flipY = false
+				 *   texture.wrapS = RepeatWrapping
+				 *   texture.wrapT = RepeatWrapping
+				 * TODO: more precise
+				 */
+				function getAlphaByUv( image, uv ) {
+
+					var width = image.width;
+					var height = image.height;
+
+					var x = Math.round( uv.x * width ) % width;
+					var y = Math.round( uv.y * height ) % height;
+
+					if ( x < 0 ) x += width;
+					if ( y < 0 ) y += height;
+
+					var index = y * width + x;
+
+					return image.data[ index * 4 + 3 ];
+
+				}
+
+				var imageData = texture.image.data !== undefined
+					? texture.image
+					: createImageData( texture.image );
+
+				var group = geometry.groups[ groupIndex ];
+
+				if ( detectImageTransparency(
+					imageData,
+					geometry.attributes.uv.array,
+					geometry.index.array.slice( group.start, group.start + group.count ) ) ) {
+
+					map.transparent = true;
+
+				}
+
+			} );
+
+		}
+
+	};
+
+	//
+
+	function AnimationBuilder() {
+
+	}
+
+	AnimationBuilder.prototype = {
+
+		constructor: AnimationBuilder,
+
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {AnimationClip}
+		 */
+		build: function ( vmd, mesh ) {
+
+			// combine skeletal and morph animations
+
+			var tracks = this.buildSkeletalAnimation( vmd, mesh ).tracks;
+			var tracks2 = this.buildMorphAnimation( vmd, mesh ).tracks;
+
+			for ( var i = 0, il = tracks2.length; i < il; i ++ ) {
+
+				tracks.push( tracks2[ i ] );
+
+			}
+
+			return new AnimationClip( '', - 1, tracks );
+
+		},
+
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {AnimationClip}
+		 */
+		buildSkeletalAnimation: function ( vmd, mesh ) {
+
+			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 tracks = [];
+
+			var motions = {};
+			var bones = mesh.skeleton.bones;
+			var boneNameDictionary = {};
+
+			for ( var i = 0, il = bones.length; i < il; i ++ ) {
+
+				boneNameDictionary[ bones[ i ].name ] = true;
+
+			}
+
+			for ( var i = 0; i < vmd.metadata.motionCount; i ++ ) {
+
+				var motion = vmd.motions[ i ];
+				var boneName = motion.boneName;
+
+				if ( boneNameDictionary[ boneName ] === undefined ) continue;
+
+				motions[ boneName ] = motions[ boneName ] || [];
+				motions[ boneName ].push( motion );
+
+			}
+
+			for ( var key in motions ) {
+
+				var array = motions[ key ];
+
+				array.sort( function ( a, b ) {
+
+					return a.frameNum - b.frameNum;
+
+				} );
+
+				var times = [];
+				var positions = [];
+				var rotations = [];
+				var pInterpolations = [];
+				var rInterpolations = [];
+
+				var basePosition = mesh.skeleton.getBoneByName( key ).position.toArray();
+
+				for ( var i = 0, il = array.length; i < il; i ++ ) {
+
+					var time = array[ i ].frameNum / 30;
+					var position = array[ i ].position;
+					var rotation = array[ i ].rotation;
+					var interpolation = array[ i ].interpolation;
+
+					times.push( time );
+
+					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 );
+
+					pushInterpolation( rInterpolations, interpolation, 3 );
+
+				}
+
+				var targetName = '.bones[' + key + ']';
+
+				tracks.push( this._createTrack( targetName + '.position', VectorKeyframeTrack, times, positions, pInterpolations ) );
+				tracks.push( this._createTrack( targetName + '.quaternion', QuaternionKeyframeTrack, times, rotations, rInterpolations ) );
+
+			}
+
+			return new AnimationClip( '', - 1, tracks );
+
+		},
+
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @param {SkinnedMesh} mesh - tracks will be fitting to mesh
+		 * @return {AnimationClip}
+		 */
+		buildMorphAnimation: function ( vmd, mesh ) {
+
+			var tracks = [];
+
+			var morphs = {};
+			var morphTargetDictionary = mesh.morphTargetDictionary;
+
+			for ( var i = 0; i < vmd.metadata.morphCount; i ++ ) {
+
+				var morph = vmd.morphs[ i ];
+				var morphName = morph.morphName;
+
+				if ( morphTargetDictionary[ morphName ] === undefined ) continue;
+
+				morphs[ morphName ] = morphs[ morphName ] || [];
+				morphs[ morphName ].push( morph );
+
+			}
+
+			for ( var key in morphs ) {
+
+				var array = morphs[ key ];
+
+				array.sort( function ( a, b ) {
+
+					return a.frameNum - b.frameNum;
+
+				} );
+
+				var times = [];
+				var values = [];
+
+				for ( var i = 0, il = array.length; i < il; i ++ ) {
+
+					times.push( array[ i ].frameNum / 30 );
+					values.push( array[ i ].weight );
+
+				}
+
+				tracks.push( new NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetDictionary[ key ] + ']', times, values ) );
+
+			}
+
+			return new AnimationClip( '', - 1, tracks );
+
+		},
+
+		/**
+		 * @param {Object} vmd - parsed VMD data
+		 * @return {AnimationClip}
+		 */
+		buildCameraAnimation: function ( vmd ) {
+
+			function pushVector3( array, vec ) {
+
+				array.push( vec.x );
+				array.push( vec.y );
+				array.push( vec.z );
+
+			}
+
+			function pushQuaternion( array, q ) {
+
+				array.push( q.x );
+				array.push( q.y );
+				array.push( q.z );
+				array.push( q.w );
+
+			}
+
+			function pushInterpolation( array, interpolation, index ) {
+
+				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
+
+			}
+
+			var tracks = [];
+
+			var cameras = vmd.cameras === undefined ? [] : vmd.cameras.slice();
+
+			cameras.sort( function ( a, b ) {
+
+				return a.frameNum - b.frameNum;
+
+			} );
+
+			var times = [];
+			var centers = [];
+			var quaternions = [];
+			var positions = [];
+			var fovs = [];
+
+			var cInterpolations = [];
+			var qInterpolations = [];
+			var pInterpolations = [];
+			var fInterpolations = [];
+
+			var quaternion = new Quaternion();
+			var euler = new Euler();
+			var position = new Vector3();
+			var center = new Vector3();
+
+			for ( var i = 0, il = cameras.length; i < il; i ++ ) {
+
+				var motion = cameras[ i ];
+
+				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;
+
+				times.push( time );
+
+				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 );
+
+				pushVector3( centers, center );
+				pushQuaternion( quaternions, quaternion );
+				pushVector3( positions, position );
+
+				fovs.push( fov );
+
+				for ( var j = 0; j < 3; j ++ ) {
+
+					pushInterpolation( cInterpolations, interpolation, j );
+
+				}
+
+				pushInterpolation( qInterpolations, interpolation, 3 );
+
+				// use the same parameter for x, y, z axis.
+				for ( var j = 0; j < 3; j ++ ) {
+
+					pushInterpolation( pInterpolations, interpolation, 4 );
+
+				}
+
+				pushInterpolation( fInterpolations, interpolation, 5 );
+
+			}
+
+			var tracks = [];
+
+			// I expect an object whose name 'target' exists under THREE.Camera
+			tracks.push( this._createTrack( 'target.position', VectorKeyframeTrack, times, centers, cInterpolations ) );
+
+			tracks.push( this._createTrack( '.quaternion', QuaternionKeyframeTrack, times, quaternions, qInterpolations ) );
+			tracks.push( this._createTrack( '.position', VectorKeyframeTrack, times, positions, pInterpolations ) );
+			tracks.push( this._createTrack( '.fov', NumberKeyframeTrack, times, fovs, fInterpolations ) );
+
+			return new AnimationClip( '', - 1, tracks );
+
+		},
+
+		// private method
+
+		_createTrack: function ( node, typedKeyframeTrack, 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();
+
+				var stride = values.length / times.length;
+				var interpolateStride = interpolations.length / times.length;
+
+				var index = 1;
+
+				for ( var aheadIndex = 2, endIndex = times.length; aheadIndex < endIndex; aheadIndex ++ ) {
+
+					for ( var i = 0; i < stride; i ++ ) {
+
+						if ( values[ index * stride + i ] !== values[ ( index - 1 ) * stride + i ] ||
+							values[ index * stride + i ] !== values[ aheadIndex * stride + i ] ) {
+
+							index ++;
+							break;
+
+						}
+
+					}
+
+					if ( aheadIndex > index ) {
+
+						times[ index ] = times[ aheadIndex ];
+
+						for ( var i = 0; i < stride; i ++ ) {
+
+							values[ index * stride + i ] = values[ aheadIndex * stride + i ];
+
+						}
+
+						for ( var i = 0; i < interpolateStride; i ++ ) {
+
+							interpolations[ index * interpolateStride + i ] = interpolations[ aheadIndex * interpolateStride + i ];
+
+						}
+
+					}
+
+				}
+
+				times.length = index + 1;
+				values.length = ( index + 1 ) * stride;
+				interpolations.length = ( index + 1 ) * interpolateStride;
+
+			}
+
+			var track = new typedKeyframeTrack( node, times, values );
+
+			track.createInterpolant = function InterpolantFactoryMethodCubicBezier( result ) {
+
+				return new CubicBezierInterpolation( this.times, this.values, this.getValueSize(), result, new Float32Array( interpolations ) );
+
+			};
+
+			return track;
+
+		}
+
+	};
+
+	// interpolation
+
+	function CubicBezierInterpolation( parameterPositions, sampleValues, sampleSize, resultBuffer, params ) {
+
+		Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+
+		this.interpolationParams = params;
+
+	}
+
+	CubicBezierInterpolation.prototype = Object.assign( Object.create( Interpolant.prototype ), {
+
+		constructor: CubicBezierInterpolation,
+
+		interpolate_: function ( i1, t0, t, t1 ) {
+
+			var result = this.resultBuffer;
+			var values = this.sampleValues;
+			var stride = this.valueSize;
+			var params = this.interpolationParams;
+
+			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 );
+
+			if ( stride === 4 ) { // Quaternion
+
+				var x1 = params[ i1 * 4 + 0 ];
+				var x2 = params[ i1 * 4 + 1 ];
+				var y1 = params[ i1 * 4 + 2 ];
+				var y2 = params[ i1 * 4 + 3 ];
+
+				var ratio = this._calculate( x1, x2, y1, y2, weight1 );
+
+				Quaternion.slerpFlat( result, 0, values, offset0, values, offset1, ratio );
+
+			} else if ( stride === 3 ) { // Vector3
+
+				for ( var i = 0; i !== stride; ++ i ) {
+
+					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 ];
+
+					var ratio = this._calculate( x1, x2, y1, y2, weight1 );
+
+					result[ i ] = values[ offset0 + i ] * ( 1 - ratio ) + values[ offset1 + i ] * ratio;
+
+				}
+
+			} else { // Number
+
+				var x1 = params[ i1 * 4 + 0 ];
+				var x2 = params[ i1 * 4 + 1 ];
+				var y1 = params[ i1 * 4 + 2 ];
+				var y2 = params[ i1 * 4 + 3 ];
+
+				var ratio = this._calculate( x1, x2, y1, y2, weight1 );
+
+				result[ 0 ] = values[ offset0 ] * ( 1 - ratio ) + values[ offset1 ] * ratio;
+
+			}
+
+			return result;
+
+		},
+
+		_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;
+
+				if ( math.abs( ft ) < eps ) break;
+
+				c /= 2.0;
+
+				t += ( ft < 0 ) ? c : - c;
+				s = 1.0 - t;
+
+			}
+
+			return ( sst3 * y1 ) + ( stt3 * y2 ) + ttt;
+
+		}
+
+	} );
+
+	return MMDLoader;
+
+} )();
+
+export { MMDLoader };

+ 23 - 0
examples/jsm/loaders/NRRDLoader.d.ts

@@ -0,0 +1,23 @@
+import {
+  Material,
+  LoadingManager,
+  Group
+} from '../../../src/Three';
+
+import {
+  Volume
+} from '../misc/Volume';
+
+export class NRRDLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  fieldFunctions: object;
+
+  load(url: string, onLoad: (group: Volume) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  parse(data: string) : Volume;
+  parseChars(array: number[], start?: number, end?: number) : string;
+  setPath(value: string) : this;
+
+}

+ 614 - 0
examples/jsm/loaders/NRRDLoader.js

@@ -0,0 +1,614 @@
+/*
+ *  three.js NRRD file loader
+ */
+
+import {
+	DefaultLoadingManager,
+	FileLoader,
+	Matrix4,
+	Vector3
+} from "../../../build/three.module.js";
+import { Zlib } from "../libs/gunzip.min.js";
+import { Volume } from "../misc/Volume.js";
+
+var NRRDLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+
+};
+
+NRRDLoader.prototype = {
+
+	constructor: NRRDLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( data ) {
+
+			onLoad( scope.parse( data ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( data ) {
+
+		// this parser is largely inspired from the XTK NRRD parser : https://github.com/xtk/X
+
+		var _data = data;
+
+		var _dataPointer = 0;
+
+		var _nativeLittleEndian = new Int8Array( new Int16Array( [ 1 ] ).buffer )[ 0 ] > 0;
+
+		var _littleEndian = true;
+
+		var headerObject = {};
+
+		function scan( type, chunks ) {
+
+			if ( chunks === undefined || chunks === null ) {
+
+				chunks = 1;
+
+			}
+
+			var _chunkSize = 1;
+			var _array_type = Uint8Array;
+
+			switch ( type ) {
+
+			// 1 byte data types
+				case 'uchar':
+					break;
+				case 'schar':
+					_array_type = Int8Array;
+					break;
+					// 2 byte data types
+				case 'ushort':
+					_array_type = Uint16Array;
+					_chunkSize = 2;
+					break;
+				case 'sshort':
+					_array_type = Int16Array;
+					_chunkSize = 2;
+					break;
+					// 4 byte data types
+				case 'uint':
+					_array_type = Uint32Array;
+					_chunkSize = 4;
+					break;
+				case 'sint':
+					_array_type = Int32Array;
+					_chunkSize = 4;
+					break;
+				case 'float':
+					_array_type = Float32Array;
+					_chunkSize = 4;
+					break;
+				case 'complex':
+					_array_type = Float64Array;
+					_chunkSize = 8;
+					break;
+				case 'double':
+					_array_type = Float64Array;
+					_chunkSize = 8;
+					break;
+
+			}
+
+			// increase the data pointer in-place
+			var _bytes = new _array_type( _data.slice( _dataPointer,
+				_dataPointer += chunks * _chunkSize ) );
+
+			// if required, flip the endianness of the bytes
+			if ( _nativeLittleEndian != _littleEndian ) {
+
+				// we need to flip here since the format doesn't match the native endianness
+				_bytes = flipEndianness( _bytes, _chunkSize );
+
+			}
+
+			if ( chunks == 1 ) {
+
+				// if only one chunk was requested, just return one value
+				return _bytes[ 0 ];
+
+			}
+
+			// return the byte array
+			return _bytes;
+
+		}
+
+		//Flips typed array endianness in-place. Based on https://github.com/kig/DataStream.js/blob/master/DataStream.js.
+
+		function flipEndianness( array, chunkSize ) {
+
+			var u8 = new Uint8Array( array.buffer, array.byteOffset, array.byteLength );
+			for ( var i = 0; i < array.byteLength; i += chunkSize ) {
+
+				for ( var j = i + chunkSize - 1, k = i; j > k; j --, k ++ ) {
+
+					var tmp = u8[ k ];
+					u8[ k ] = u8[ j ];
+					u8[ j ] = tmp;
+
+				}
+
+			}
+
+			return array;
+
+		}
+
+		//parse the header
+		function parseHeader( header ) {
+
+			var data, field, fn, i, l, lines, m, _i, _len;
+			lines = header.split( /\r?\n/ );
+			for ( _i = 0, _len = lines.length; _i < _len; _i ++ ) {
+
+				l = lines[ _i ];
+				if ( l.match( /NRRD\d+/ ) ) {
+
+					headerObject.isNrrd = true;
+
+				} else if ( l.match( /^#/ ) ) {
+				} else if ( m = l.match( /(.*):(.*)/ ) ) {
+
+					field = m[ 1 ].trim();
+					data = m[ 2 ].trim();
+					fn = NRRDLoader.prototype.fieldFunctions[ field ];
+					if ( fn ) {
+
+						fn.call( headerObject, data );
+
+					} else {
+
+						headerObject[ field ] = data;
+
+					}
+
+				}
+
+			}
+			if ( ! headerObject.isNrrd ) {
+
+				throw new Error( 'Not an NRRD file' );
+
+			}
+			if ( headerObject.encoding === 'bz2' || headerObject.encoding === 'bzip2' ) {
+
+				throw new Error( 'Bzip is not supported' );
+
+			}
+			if ( ! headerObject.vectors ) {
+
+				//if no space direction is set, let's use the identity
+				headerObject.vectors = [ new Vector3( 1, 0, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ) ];
+				//apply spacing if defined
+				if ( headerObject.spacings ) {
+
+					for ( i = 0; i <= 2; i ++ ) {
+
+						if ( ! isNaN( headerObject.spacings[ i ] ) ) {
+
+							headerObject.vectors[ i ].multiplyScalar( headerObject.spacings[ i ] );
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+		//parse the data when registred as one of this type : 'text', 'ascii', 'txt'
+		function parseDataAsText( data, start, end ) {
+
+			var number = '';
+			start = start || 0;
+			end = end || data.length;
+			var value;
+			//length of the result is the product of the sizes
+			var lengthOfTheResult = headerObject.sizes.reduce( function ( previous, current ) {
+
+				return previous * current;
+
+			}, 1 );
+
+			var base = 10;
+			if ( headerObject.encoding === 'hex' ) {
+
+				base = 16;
+
+			}
+
+			var result = new headerObject.__array( lengthOfTheResult );
+			var resultIndex = 0;
+			var parsingFunction = parseInt;
+			if ( headerObject.__array === Float32Array || headerObject.__array === Float64Array ) {
+
+				parsingFunction = parseFloat;
+
+			}
+			for ( var i = start; i < end; i ++ ) {
+
+				value = data[ i ];
+				//if value is not a space
+				if ( ( value < 9 || value > 13 ) && value !== 32 ) {
+
+					number += String.fromCharCode( value );
+
+				} else {
+
+					if ( number !== '' ) {
+
+						result[ resultIndex ] = parsingFunction( number, base );
+						resultIndex ++;
+
+					}
+					number = '';
+
+				}
+
+			}
+			if ( number !== '' ) {
+
+				result[ resultIndex ] = parsingFunction( number, base );
+				resultIndex ++;
+
+			}
+			return result;
+
+		}
+
+		var _bytes = scan( 'uchar', data.byteLength );
+		var _length = _bytes.length;
+		var _header = null;
+		var _data_start = 0;
+		var i;
+		for ( i = 1; i < _length; i ++ ) {
+
+			if ( _bytes[ i - 1 ] == 10 && _bytes[ i ] == 10 ) {
+
+				// we found two line breaks in a row
+				// now we know what the header is
+				_header = this.parseChars( _bytes, 0, i - 2 );
+				// this is were the data starts
+				_data_start = i + 1;
+				break;
+
+			}
+
+		}
+		// parse the header
+		parseHeader( _header );
+
+		var _data = _bytes.subarray( _data_start ); // the data without header
+		if ( headerObject.encoding === 'gzip' || headerObject.encoding === 'gz' ) {
+
+			// we need to decompress the datastream
+			// here we start the unzipping and get a typed Uint8Array back
+			var inflate = new Zlib.Gunzip( new Uint8Array( _data ) ); // eslint-disable-line no-undef
+			_data = inflate.decompress();
+
+		} else if ( headerObject.encoding === 'ascii' || headerObject.encoding === 'text' || headerObject.encoding === 'txt' || headerObject.encoding === 'hex' ) {
+
+			_data = parseDataAsText( _data );
+
+		} else if ( headerObject.encoding === 'raw' ) {
+
+			//we need to copy the array to create a new array buffer, else we retrieve the original arraybuffer with the header
+			var _copy = new Uint8Array( _data.length );
+
+			for ( var i = 0; i < _data.length; i ++ ) {
+
+				_copy[ i ] = _data[ i ];
+
+			}
+
+			_data = _copy;
+
+		}
+		// .. let's use the underlying array buffer
+		_data = _data.buffer;
+
+		var volume = new Volume();
+		volume.header = headerObject;
+		//
+		// parse the (unzipped) data to a datastream of the correct type
+		//
+		volume.data = new headerObject.__array( _data );
+		// get the min and max intensities
+		var min_max = volume.computeMinMax();
+		var min = min_max[ 0 ];
+		var max = min_max[ 1 ];
+		// attach the scalar range to the volume
+		volume.windowLow = min;
+		volume.windowHigh = max;
+
+		// get the image dimensions
+		volume.dimensions = [ headerObject.sizes[ 0 ], headerObject.sizes[ 1 ], headerObject.sizes[ 2 ] ];
+		volume.xLength = volume.dimensions[ 0 ];
+		volume.yLength = volume.dimensions[ 1 ];
+		volume.zLength = volume.dimensions[ 2 ];
+		// spacing
+		var spacingX = ( new Vector3( headerObject.vectors[ 0 ][ 0 ], headerObject.vectors[ 0 ][ 1 ],
+			headerObject.vectors[ 0 ][ 2 ] ) ).length();
+		var spacingY = ( new Vector3( headerObject.vectors[ 1 ][ 0 ], headerObject.vectors[ 1 ][ 1 ],
+			headerObject.vectors[ 1 ][ 2 ] ) ).length();
+		var spacingZ = ( new Vector3( headerObject.vectors[ 2 ][ 0 ], headerObject.vectors[ 2 ][ 1 ],
+			headerObject.vectors[ 2 ][ 2 ] ) ).length();
+		volume.spacing = [ spacingX, spacingY, spacingZ ];
+
+
+		// Create IJKtoRAS matrix
+		volume.matrix = new Matrix4();
+
+		var _spaceX = 1;
+		var _spaceY = 1;
+		var _spaceZ = 1;
+
+		if ( headerObject.space == "left-posterior-superior" ) {
+
+			_spaceX = - 1;
+			_spaceY = - 1;
+
+		} else if ( headerObject.space === 'left-anterior-superior' ) {
+
+			_spaceX = - 1;
+
+		}
+
+
+		if ( ! headerObject.vectors ) {
+
+			volume.matrix.set(
+				_spaceX, 0, 0, 0,
+				0, _spaceY, 0, 0,
+				0, 0, _spaceZ, 0,
+				0, 0, 0, 1 );
+
+		} else {
+
+			var v = headerObject.vectors;
+
+			volume.matrix.set(
+				_spaceX * v[ 0 ][ 0 ], _spaceX * v[ 1 ][ 0 ], _spaceX * v[ 2 ][ 0 ], 0,
+				_spaceY * v[ 0 ][ 1 ], _spaceY * v[ 1 ][ 1 ], _spaceY * v[ 2 ][ 1 ], 0,
+				_spaceZ * v[ 0 ][ 2 ], _spaceZ * v[ 1 ][ 2 ], _spaceZ * v[ 2 ][ 2 ], 0,
+				0, 0, 0, 1 );
+
+		}
+
+		volume.inverseMatrix = new Matrix4();
+		volume.inverseMatrix.getInverse( volume.matrix );
+		volume.RASDimensions = ( new Vector3( volume.xLength, volume.yLength, volume.zLength ) ).applyMatrix4( volume.matrix ).round().toArray().map( Math.abs );
+
+		// .. and set the default threshold
+		// only if the threshold was not already set
+		if ( volume.lowerThreshold === - Infinity ) {
+
+			volume.lowerThreshold = min;
+
+		}
+		if ( volume.upperThreshold === Infinity ) {
+
+			volume.upperThreshold = max;
+
+		}
+
+		return volume;
+
+	},
+
+	parseChars: function ( array, start, end ) {
+
+		// without borders, use the whole array
+		if ( start === undefined ) {
+
+			start = 0;
+
+		}
+		if ( end === undefined ) {
+
+			end = array.length;
+
+		}
+
+		var output = '';
+		// create and append the chars
+		var i = 0;
+		for ( i = start; i < end; ++ i ) {
+
+			output += String.fromCharCode( array[ i ] );
+
+		}
+
+		return output;
+
+	},
+
+	fieldFunctions: {
+
+		type: function ( data ) {
+
+			switch ( data ) {
+
+				case 'uchar':
+				case 'unsigned char':
+				case 'uint8':
+				case 'uint8_t':
+					this.__array = Uint8Array;
+					break;
+				case 'signed char':
+				case 'int8':
+				case 'int8_t':
+					this.__array = Int8Array;
+					break;
+				case 'short':
+				case 'short int':
+				case 'signed short':
+				case 'signed short int':
+				case 'int16':
+				case 'int16_t':
+					this.__array = Int16Array;
+					break;
+				case 'ushort':
+				case 'unsigned short':
+				case 'unsigned short int':
+				case 'uint16':
+				case 'uint16_t':
+					this.__array = Uint16Array;
+					break;
+				case 'int':
+				case 'signed int':
+				case 'int32':
+				case 'int32_t':
+					this.__array = Int32Array;
+					break;
+				case 'uint':
+				case 'unsigned int':
+				case 'uint32':
+				case 'uint32_t':
+					this.__array = Uint32Array;
+					break;
+				case 'float':
+					this.__array = Float32Array;
+					break;
+				case 'double':
+					this.__array = Float64Array;
+					break;
+				default:
+					throw new Error( 'Unsupported NRRD data type: ' + data );
+
+			}
+
+			return this.type = data;
+
+		},
+
+		endian: function ( data ) {
+
+			return this.endian = data;
+
+		},
+
+		encoding: function ( data ) {
+
+			return this.encoding = data;
+
+		},
+
+		dimension: function ( data ) {
+
+			return this.dim = parseInt( data, 10 );
+
+		},
+
+		sizes: function ( data ) {
+
+			var i;
+			return this.sizes = ( function () {
+
+				var _i, _len, _ref, _results;
+				_ref = data.split( /\s+/ );
+				_results = [];
+				for ( _i = 0, _len = _ref.length; _i < _len; _i ++ ) {
+
+					i = _ref[ _i ];
+					_results.push( parseInt( i, 10 ) );
+
+				}
+				return _results;
+
+			} )();
+
+		},
+
+		space: function ( data ) {
+
+			return this.space = data;
+
+		},
+
+		'space origin': function ( data ) {
+
+			return this.space_origin = data.split( "(" )[ 1 ].split( ")" )[ 0 ].split( "," );
+
+		},
+
+		'space directions': function ( data ) {
+
+			var f, parts, v;
+			parts = data.match( /\(.*?\)/g );
+			return this.vectors = ( function () {
+
+				var _i, _len, _results;
+				_results = [];
+				for ( _i = 0, _len = parts.length; _i < _len; _i ++ ) {
+
+					v = parts[ _i ];
+					_results.push( ( function () {
+
+						var _j, _len2, _ref, _results2;
+						_ref = v.slice( 1, - 1 ).split( /,/ );
+						_results2 = [];
+						for ( _j = 0, _len2 = _ref.length; _j < _len2; _j ++ ) {
+
+							f = _ref[ _j ];
+							_results2.push( parseFloat( f ) );
+
+						}
+						return _results2;
+
+					} )() );
+
+				}
+				return _results;
+
+			} )();
+
+		},
+
+		spacings: function ( data ) {
+
+			var f, parts;
+			parts = data.split( /\s+/ );
+			return this.spacings = ( function () {
+
+				var _i, _len, _results = [];
+
+				for ( _i = 0, _len = parts.length; _i < _len; _i ++ ) {
+
+					f = parts[ _i ];
+					_results.push( parseFloat( f ) );
+
+				}
+				return _results;
+
+			} )();
+
+		}
+	}
+
+};
+
+export { NRRDLoader };

+ 16 - 0
examples/jsm/loaders/TTFLoader.d.ts

@@ -0,0 +1,16 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+export class TTFLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+  reversed: boolean;
+
+  load(url: string, onLoad: (json: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): this;
+
+  parse(arraybuffer: ArrayBuffer): object;
+}

+ 207 - 0
examples/jsm/loaders/TTFLoader.js

@@ -0,0 +1,207 @@
+/**
+ * @author gero3 / https://github.com/gero3
+ * @author tentone / https://github.com/tentone
+ *
+ * Requires opentype.js to be included in the project.
+ * Loads TTF files and converts them into typeface JSON that can be used directly
+ * to create THREE.Font objects.
+ */
+
+import {
+	DefaultLoadingManager,
+	FileLoader
+} from "../../../build/three.module.js";
+
+var TTFLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+	this.reversed = false;
+
+};
+
+TTFLoader.prototype = {
+
+	constructor: TTFLoader,
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( buffer ) {
+
+			onLoad( scope.parse( buffer ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( arraybuffer ) {
+
+		function convert( font, reversed ) {
+
+			var round = Math.round;
+
+			var glyphs = {};
+			var scale = ( 100000 ) / ( ( font.unitsPerEm || 2048 ) * 72 );
+
+			for ( var i = 0; i < font.glyphs.length; i ++ ) {
+
+				var glyph = font.glyphs.glyphs[ i ];
+
+				if ( glyph.unicode !== undefined ) {
+
+					var token = {
+						ha: round( glyph.advanceWidth * scale ),
+						x_min: round( glyph.xMin * scale ),
+						x_max: round( glyph.xMax * scale ),
+						o: ''
+					};
+
+					if ( reversed ) {
+
+						glyph.path.commands = reverseCommands( glyph.path.commands );
+
+					}
+
+					glyph.path.commands.forEach( function ( command ) {
+
+						if ( command.type.toLowerCase() === 'c' ) {
+
+							command.type = 'b';
+
+						}
+
+						token.o += command.type.toLowerCase() + ' ';
+
+						if ( command.x !== undefined && command.y !== undefined ) {
+
+							token.o += round( command.x * scale ) + ' ' + round( command.y * scale ) + ' ';
+
+						}
+
+						if ( command.x1 !== undefined && command.y1 !== undefined ) {
+
+							token.o += round( command.x1 * scale ) + ' ' + round( command.y1 * scale ) + ' ';
+
+						}
+
+						if ( command.x2 !== undefined && command.y2 !== undefined ) {
+
+							token.o += round( command.x2 * scale ) + ' ' + round( command.y2 * scale ) + ' ';
+
+						}
+
+					} );
+
+					glyphs[ String.fromCharCode( glyph.unicode ) ] = token;
+
+				}
+
+			}
+
+			return {
+				glyphs: glyphs,
+				familyName: font.familyName,
+				ascender: round( font.ascender * scale ),
+				descender: round( font.descender * scale ),
+				underlinePosition: font.tables.post.underlinePosition,
+				underlineThickness: font.tables.post.underlineThickness,
+				boundingBox: {
+					xMin: font.tables.head.xMin,
+					xMax: font.tables.head.xMax,
+					yMin: font.tables.head.yMin,
+					yMax: font.tables.head.yMax
+				},
+				resolution: 1000,
+				original_font_information: font.tables.name
+			};
+
+		}
+
+		function reverseCommands( commands ) {
+
+			var paths = [];
+			var path;
+
+			commands.forEach( function ( c ) {
+
+				if ( c.type.toLowerCase() === 'm' ) {
+
+					path = [ c ];
+					paths.push( path );
+
+				} else if ( c.type.toLowerCase() !== 'z' ) {
+
+					path.push( c );
+
+				}
+
+			} );
+
+			var reversed = [];
+
+			paths.forEach( function ( p ) {
+
+				var result = {
+					type: 'm',
+					x: p[ p.length - 1 ].x,
+					y: p[ p.length - 1 ].y
+				};
+
+				reversed.push( result );
+
+				for ( var i = p.length - 1; i > 0; i -- ) {
+
+					var command = p[ i ];
+					var result = { type: command.type };
+
+					if ( command.x2 !== undefined && command.y2 !== undefined ) {
+
+						result.x1 = command.x2;
+						result.y1 = command.y2;
+						result.x2 = command.x1;
+						result.y2 = command.y1;
+
+					} else if ( command.x1 !== undefined && command.y1 !== undefined ) {
+
+						result.x1 = command.x1;
+						result.y1 = command.y1;
+
+					}
+
+					result.x = p[ i - 1 ].x;
+					result.y = p[ i - 1 ].y;
+					reversed.push( result );
+
+				}
+
+			} );
+
+			return reversed;
+
+		}
+
+		if ( typeof opentype === 'undefined' ) {
+
+			console.warn( 'THREE.TTFLoader: The loader requires opentype.js. Make sure it\'s included before using the loader.' );
+			return null;
+
+		}
+
+		return convert( opentype.parse( arraybuffer ), this.reversed );
+
+	}
+
+};
+
+export { TTFLoader };

+ 2006 - 765
examples/jsm/loaders/VRMLLoader.js

@@ -1,1360 +1,2601 @@
 /**
- * @author mrdoob / http://mrdoob.com/
+ * @author Mugen87 / https://github.com/Mugen87
  */
 
 import {
-	AmbientLight,
 	BackSide,
 	BoxBufferGeometry,
 	BufferAttribute,
 	BufferGeometry,
+	ClampToEdgeWrapping,
 	Color,
+	ConeBufferGeometry,
 	CylinderBufferGeometry,
 	DefaultLoadingManager,
 	DoubleSide,
 	FileLoader,
 	Float32BufferAttribute,
+	FrontSide,
+	Group,
+	LineBasicMaterial,
+	LineSegments,
 	LoaderUtils,
 	Mesh,
 	MeshBasicMaterial,
 	MeshPhongMaterial,
 	Object3D,
-	PointLight,
+	Points,
+	PointsMaterial,
+	RepeatWrapping,
 	Scene,
 	SphereBufferGeometry,
-	SpotLight,
 	TextureLoader,
+	Vector2,
 	Vector3,
 	VertexColors
 } from "../../../build/three.module.js";
 
-var VRMLLoader = function ( manager ) {
+/* global chevrotain */
 
-	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+var VRMLLoader = ( function () {
 
-};
+	// dependency check
 
-VRMLLoader.prototype = {
+	if ( typeof chevrotain === 'undefined' ) {
 
-	constructor: VRMLLoader,
+		throw Error( 'THREE.VRMLLoader: External library chevrotain.min.js required.' );
 
-	// for IndexedFaceSet support
-	isRecordingPoints: false,
-	isRecordingFaces: false,
-	points: [],
-	indexes: [],
+	}
 
-	// for Background support
-	isRecordingAngles: false,
-	isRecordingColors: false,
-	angles: [],
-	colors: [],
+	// class definitions
 
-	recordingFieldname: null,
+	function VRMLLoader( manager ) {
 
-	crossOrigin: 'anonymous',
+		this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
 
-	load: function ( url, onLoad, onProgress, onError ) {
+	}
 
-		var scope = this;
+	VRMLLoader.prototype = {
 
-		var path = ( scope.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : scope.path;
+		constructor: VRMLLoader,
 
-		var loader = new FileLoader( this.manager );
-		loader.setPath( scope.path );
-		loader.load( url, function ( text ) {
+		crossOrigin: 'anonymous',
 
-			onLoad( scope.parse( text, path ) );
+		load: function ( url, onLoad, onProgress, onError ) {
 
-		}, onProgress, onError );
+			var scope = this;
 
-	},
+			var path = ( scope.path === undefined ) ? LoaderUtils.extractUrlBase( url ) : scope.path;
 
-	setPath: function ( value ) {
+			var loader = new FileLoader( this.manager );
+			loader.setPath( scope.path );
+			loader.load( url, function ( text ) {
 
-		this.path = value;
-		return this;
+				onLoad( scope.parse( text, path ) );
 
-	},
+			}, onProgress, onError );
 
-	setResourcePath: function ( value ) {
+		},
 
-		this.resourcePath = value;
-		return this;
+		setPath: function ( value ) {
 
-	},
+			this.path = value;
+			return this;
 
-	setCrossOrigin: function ( value ) {
+		},
 
-		this.crossOrigin = value;
-		return this;
+		setResourcePath: function ( value ) {
 
-	},
+			this.resourcePath = value;
+			return this;
 
-	parse: function ( data, path ) {
+		},
 
-		var scope = this;
+		setCrossOrigin: function ( value ) {
 
-		var textureLoader = new TextureLoader( this.manager );
-		textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
+			this.crossOrigin = value;
+			return this;
 
-		function parseV2( lines, scene ) {
+		},
 
-			var defines = {};
-			var float_pattern = /(\b|\-|\+)([\d\.e]+)/;
-			var float2_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
-			var float3_pattern = /([\d\.\+\-e]+)\s+([\d\.\+\-e]+)\s+([\d\.\+\-e]+)/g;
+		parse: function ( data, path ) {
 
-			/**
-			 * Vertically paints the faces interpolating between the
-			 * specified colors at the specified angels. This is used for the Background
-			 * node, but could be applied to other nodes with multiple faces as well.
-			 *
-			 * When used with the Background node, default is directionIsDown is true if
-			 * interpolating the skyColor down from the Zenith. When interpolationg up from
-			 * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
-			 *
-			 * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
-			 * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
-			 * is linear along the Y axis in any case.
-			 *
-			 * You must specify one more color than you have angles at the beginning of the colors array.
-			 * This is the color of the Zenith (the top of the shape).
-			 *
-			 * @param geometry
-			 * @param radius
-			 * @param angles
-			 * @param colors
-			 * @param boolean topDown Whether to work top down or bottom up.
-			 */
-			function paintFaces( geometry, radius, angles, colors, topDown ) {
+			var nodeMap = {};
 
-				var direction = ( topDown === true ) ? 1 : - 1;
+			function generateVRMLTree( data ) {
 
-				var coord = [], A = {}, B = {}, applyColor = false;
+				// create lexer, parser and visitor
 
-				for ( var k = 0; k < angles.length; k ++ ) {
+				var tokenData = createTokens();
 
-					// push the vector at which the color changes
+				var lexer = new VRMLLexer( tokenData.tokens );
+				var parser = new VRMLParser( tokenData.tokenVocabulary );
+				var visitor = createVisitor( parser.getBaseCstVisitorConstructor() );
 
-					var vec = {
-						x: direction * ( Math.cos( angles[ k ] ) * radius ),
-						y: direction * ( Math.sin( angles[ k ] ) * radius )
-					};
+				// lexing
 
-					coord.push( vec );
+				var lexingResult = lexer.lex( data );
+				parser.input = lexingResult.tokens;
+
+				// parsing
+
+				var cstOutput = parser.vrml();
+
+				if ( parser.errors.length > 0 ) {
+
+					console.error( parser.errors );
+
+					throw Error( 'THREE.VRMLLoader: Parsing errors detected.' );
 
 				}
 
-				var index = geometry.index;
-				var positionAttribute = geometry.attributes.position;
-				var colorAttribute = new BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
+				// actions
 
-				var position = new Vector3();
-				var color = new Color();
+				var ast = visitor.visit( cstOutput );
 
-				for ( var i = 0; i < index.count; i ++ ) {
+				return ast;
 
-					var vertexIndex = index.getX( i );
+			}
 
-					position.fromBufferAttribute( positionAttribute, vertexIndex );
+			function createTokens() {
+
+				var createToken = chevrotain.createToken;
+
+				// from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics
+
+				var RouteIdentifier = createToken( { name: 'RouteIdentifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/ } );
+				var Identifier = createToken( { name: 'Identifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/, longer_alt: RouteIdentifier } );
+
+				// from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html
+
+				var nodeTypes = [
+					'Anchor', 'Billboard', 'Collision', 'Group', 'Transform', // grouping nodes
+					'Inline', 'LOD', 'Switch', // special groups
+					'AudioClip', 'DirectionalLight', 'PointLight', 'Script', 'Shape', 'Sound', 'SpotLight', 'WorldInfo', // common nodes
+					'CylinderSensor', 'PlaneSensor', 'ProximitySensor', 'SphereSensor', 'TimeSensor', 'TouchSensor', 'VisibilitySensor', // sensors
+					'Box', 'Cone', 'Cylinder', 'ElevationGrid', 'Extrusion', 'IndexedFaceSet', 'IndexedLineSet', 'PointSet', 'Sphere', // geometries
+					'Color', 'Coordinate', 'Normal', 'TextureCoordinate', // geometric properties
+					'Appearance', 'FontStyle', 'ImageTexture', 'Material', 'MovieTexture', 'PixelTexture', 'TextureTransform', // appearance
+					'ColorInterpolator', 'CoordinateInterpolator', 'NormalInterpolator', 'OrientationInterpolator', 'PositionInterpolator', 'ScalarInterpolator', // interpolators
+					'Background', 'Fog', 'NavigationInfo', 'Viewpoint', // bindable nodes
+					'Text' // Text must be placed at the end of the regex so there are no matches for TextureTransform and TextureCoordinate
+				];
+
+				//
+
+				var Version = createToken( {
+					name: 'Version',
+					pattern: /#VRML.*/,
+					longer_alt: Identifier
+				} );
+
+				var NodeName = createToken( {
+					name: 'NodeName',
+					pattern: new RegExp( nodeTypes.join( '|' ) ),
+					longer_alt: Identifier
+				} );
+
+				var DEF = createToken( {
+					name: 'DEF',
+					pattern: /DEF/,
+					longer_alt: Identifier
+				} );
+
+				var USE = createToken( {
+					name: 'USE',
+					pattern: /USE/,
+					longer_alt: Identifier
+				} );
+
+				var ROUTE = createToken( {
+					name: 'ROUTE',
+					pattern: /ROUTE/,
+					longer_alt: Identifier
+				} );
+
+				var TO = createToken( {
+					name: 'TO',
+					pattern: /TO/,
+					longer_alt: Identifier
+				} );
+
+				//
+
+				var StringLiteral = createToken( { name: "StringLiteral", pattern: /"(:?[^\\"\n\r]+|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/ } );
+				var NumberLiteral = createToken( { name: 'NumberLiteral', pattern: /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ } );
+				var BooleanLiteral = createToken( { name: 'BooleanLiteral', pattern: /TRUE|FALSE/ } );
+				var NullLiteral = createToken( { name: 'NullLiteral', pattern: /NULL/ } );
+				var LSquare = createToken( { name: 'LSquare', pattern: /\[/ } );
+				var RSquare = createToken( { name: 'RSquare', pattern: /]/ } );
+				var LCurly = createToken( { name: 'LCurly', pattern: /{/ } );
+				var RCurly = createToken( { name: 'RCurly', pattern: /}/ } );
+				var Comment = createToken( {
+					name: 'Comment',
+					pattern: /#.*/,
+					group: chevrotain.Lexer.SKIPPED
+				} );
+
+				// commas, blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields
+
+				var WhiteSpace = createToken( {
+					name: 'WhiteSpace',
+					pattern: /[ ,\s]/,
+					group: chevrotain.Lexer.SKIPPED
+				} );
+
+				var tokens = [
+					WhiteSpace,
+					// keywords appear before the Identifier
+					NodeName,
+					DEF,
+					USE,
+					ROUTE,
+					TO,
+					BooleanLiteral,
+					NullLiteral,
+					// the Identifier must appear after the keywords because all keywords are valid identifiers
+					Version,
+					Identifier,
+					RouteIdentifier,
+					StringLiteral,
+					NumberLiteral,
+					LSquare,
+					RSquare,
+					LCurly,
+					RCurly,
+					Comment
+				];
+
+				var tokenVocabulary = {};
+
+				for ( var i = 0, l = tokens.length; i < l; i ++ ) {
+
+					var token = tokens[ i ];
+
+					tokenVocabulary[ token.name ] = token;
 
-					for ( var j = 0; j < colors.length; j ++ ) {
+				}
 
-						// linear interpolation between aColor and bColor, calculate proportion
-						// A is previous point (angle)
+				return { tokens: tokens, tokenVocabulary: tokenVocabulary };
 
-						if ( j === 0 ) {
+			}
 
-							A.x = 0;
-							A.y = ( topDown === true ) ? radius : - 1 * radius;
 
-						} else {
+			function createVisitor( BaseVRMLVisitor ) {
 
-							A.x = coord[ j - 1 ].x;
-							A.y = coord[ j - 1 ].y;
+				// the visitor is created dynmaically based on the given base class
+
+				function VRMLToASTVisitor() {
+
+					BaseVRMLVisitor.call( this );
+
+					this.validateVisitor();
+
+				}
+
+				VRMLToASTVisitor.prototype = Object.assign( Object.create( BaseVRMLVisitor.prototype ), {
+
+					constructor: VRMLToASTVisitor,
+
+					vrml: function ( ctx ) {
+
+						var data = {
+							version: this.visit( ctx.version ),
+							nodes: [],
+							routes: []
+						};
+
+						for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
+
+							var node = ctx.node[ i ];
+
+							data.nodes.push( this.visit( node ) );
 
 						}
 
-						// B is current point (angle)
+						if ( ctx.route ) {
 
-						B = coord[ j ];
+							for ( var i = 0, l = ctx.route.length; i < l; i ++ ) {
 
-						if ( B !== undefined ) {
+								var route = ctx.route[ i ];
 
-							// p has to be between the points A and B which we interpolate
+								data.routes.push( this.visit( route ) );
 
-							applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
+							}
 
-							if ( applyColor === true ) {
+						}
 
-								var aColor = colors[ j ];
-								var bColor = colors[ j + 1 ];
+						return data;
 
-								// below is simple linear interpolation
+					},
 
-								var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
+					version: function ( ctx ) {
 
-								// to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
+						return ctx.Version[ 0 ].image;
 
-								color.copy( aColor ).lerp( bColor, t );
+					},
 
-								colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
+					node: function ( ctx ) {
 
-							} else {
+						var data = {
+							name: ctx.NodeName[ 0 ].image,
+							fields: []
+						};
 
-								var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
-								var c = colors[ colorIndex ];
-								colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
+						if ( ctx.field ) {
+
+							for ( var i = 0, l = ctx.field.length; i < l; i ++ ) {
+
+								var field = ctx.field[ i ];
+
+								data.fields.push( this.visit( field ) );
 
 							}
 
 						}
 
-					}
+						// DEF
 
-				}
+						if ( ctx.def ) {
 
-				geometry.addAttribute( 'color', colorAttribute );
+							data.DEF = this.visit( ctx.def[ 0 ] );
 
-			}
+						}
+
+						return data;
 
-			var index = [];
+					},
 
-			function parseProperty( node, line ) {
+					field: function ( ctx ) {
 
-				var parts = [], part, property = {}, fieldName;
+						var data = {
+							name: ctx.Identifier[ 0 ].image,
+							type: null,
+							values: null
+						};
 
-				/**
-				 * Expression for matching relevant information, such as a name or value, but not the separators
-				 * @type {RegExp}
-				 */
-				var regex = /[^\s,\[\]]+/g;
+						var result;
 
-				var point;
+						// SFValue
 
-				while ( null !== ( part = regex.exec( line ) ) ) {
+						if ( ctx.singleFieldValue ) {
 
-					parts.push( part[ 0 ] );
+							result = this.visit( ctx.singleFieldValue[ 0 ] );
 
-				}
+						}
 
-				fieldName = parts[ 0 ];
+						// MFValue
 
+						if ( ctx.multiFieldValue ) {
 
-				// trigger several recorders
-				switch ( fieldName ) {
+							result = this.visit( ctx.multiFieldValue[ 0 ] );
 
-					case 'skyAngle':
-					case 'groundAngle':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingAngles = true;
-						scope.angles = [];
-						break;
+						}
 
-					case 'color':
-					case 'skyColor':
-					case 'groundColor':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingColors = true;
-						scope.colors = [];
-						break;
+						data.type = result.type;
+						data.values = result.values;
 
-					case 'point':
-					case 'vector':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingPoints = true;
-						scope.points = [];
-						break;
+						return data;
 
-					case 'colorIndex':
-					case 'coordIndex':
-					case 'normalIndex':
-					case 'texCoordIndex':
-						scope.recordingFieldname = fieldName;
-						scope.isRecordingFaces = true;
-						scope.indexes = [];
-						break;
+					},
 
-				}
+					def: function ( ctx ) {
 
-				if ( scope.isRecordingFaces ) {
+						return ctx.Identifier[ 0 ].image;
 
-					// the parts hold the indexes as strings
-					if ( parts.length > 0 ) {
+					},
 
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
+					use: function ( ctx ) {
 
-							// the part should either be positive integer or -1
-							if ( ! /(-?\d+)/.test( parts[ ind ] ) ) {
+						return { USE: ctx.Identifier[ 0 ].image };
 
-								continue;
+					},
 
-							}
+					singleFieldValue: function ( ctx ) {
 
-							// end of current face
-							if ( parts[ ind ] === '-1' ) {
+						return processField( this, ctx );
 
-								if ( index.length > 0 ) {
+					},
 
-									scope.indexes.push( index );
+					multiFieldValue: function ( ctx ) {
 
-								}
+						return processField( this, ctx );
 
-								// start new one
-								index = [];
+					},
 
-							} else {
+					route: function ( ctx ) {
 
-								index.push( parseInt( parts[ ind ] ) );
+						var data = {
+							FROM: ctx.RouteIdentifier[ 0 ].image,
+							TO: ctx.RouteIdentifier[ 1 ].image
+						};
 
-							}
+						return data;
+
+					}
+
+				} );
+
+				function processField( scope, ctx ) {
+
+					var field = {
+						type: null,
+						values: []
+					};
+
+					if ( ctx.node ) {
+
+						field.type = 'node';
+
+						for ( var i = 0, l = ctx.node.length; i < l; i ++ ) {
+
+							var node = ctx.node[ i ];
+
+							field.values.push( scope.visit( node ) );
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
+					if ( ctx.use ) {
+
+						field.type = 'use';
 
-						if ( index.length > 0 ) {
+						for ( var i = 0, l = ctx.use.length; i < l; i ++ ) {
 
-							scope.indexes.push( index );
+							var use = ctx.use[ i ];
+
+							field.values.push( scope.visit( use ) );
 
 						}
 
-						// start new one
-						index = [];
+					}
+
+					if ( ctx.StringLiteral ) {
+
+						field.type = 'string';
+
+						for ( var i = 0, l = ctx.StringLiteral.length; i < l; i ++ ) {
+
+							var stringLiteral = ctx.StringLiteral[ i ];
 
-						scope.isRecordingFaces = false;
-						node[ scope.recordingFieldname ] = scope.indexes;
+							field.values.push( stringLiteral.image.replace( /'|"/g, '' ) );
+
+						}
 
 					}
 
-				} else if ( scope.isRecordingPoints ) {
+					if ( ctx.NumberLiteral ) {
 
-					if ( node.nodeType == 'Coordinate' ) {
+						field.type = 'number';
 
-						while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+						for ( var i = 0, l = ctx.NumberLiteral.length; i < l; i ++ ) {
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
+							var numberLiteral = ctx.NumberLiteral[ i ];
 
-							scope.points.push( point );
+							field.values.push( parseFloat( numberLiteral.image ) );
 
 						}
 
 					}
 
-					if ( node.nodeType == 'Normal' ) {
+					if ( ctx.BooleanLiteral ) {
 
-  						while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+						field.type = 'boolean';
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
+						for ( var i = 0, l = ctx.BooleanLiteral.length; i < l; i ++ ) {
 
-							scope.points.push( point );
+							var booleanLiteral = ctx.BooleanLiteral[ i ];
+
+							field.values.push( booleanLiteral.image === 'TRUE' );
 
 						}
 
 					}
 
-					if ( node.nodeType == 'TextureCoordinate' ) {
+					if ( ctx.NullLiteral ) {
+
+						field.type = 'null';
+
+						ctx.NullLiteral.forEach( function () {
+
+							field.values.push( null );
+
+						} );
+
+					}
+
+					return field;
+
+				}
+
+				return new VRMLToASTVisitor();
+
+			}
+
+			function parseTree( tree ) {
+
+				// console.log( JSON.stringify( tree, null, 2 ) );
+
+				var nodes = tree.nodes;
+				var scene = new Scene();
+
+				// first iteration: build nodemap based on DEF statements
+
+				for ( var i = 0, l = nodes.length; i < l; i ++ ) {
+
+					var node = nodes[ i ];
+
+					buildNodeMap( node );
+
+				}
+
+				// second iteration: build nodes
+
+				for ( var i = 0, l = nodes.length; i < l; i ++ ) {
+
+					var node = nodes[ i ];
+					var object = getNode( node );
+
+					if ( object instanceof Object3D ) scene.add( object );
+
+				}
+
+				return scene;
+
+			}
+
+			function buildNodeMap( node ) {
+
+				if ( node.DEF ) {
+
+					nodeMap[ node.DEF ] = node;
+
+				}
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-						while ( null !== ( parts = float2_pattern.exec( line ) ) ) {
+					var field = fields[ i ];
 
-							point = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] )
-							};
+					if ( field.type === 'node' ) {
 
-							scope.points.push( point );
+						var fieldValues = field.values;
+
+						for ( var j = 0, jl = fieldValues.length; j < jl; j ++ ) {
+
+							buildNodeMap( fieldValues[ j ] );
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
 
-						scope.isRecordingPoints = false;
-						node.points = scope.points;
+				}
+
+			}
+
+
+			function getNode( node ) {
+
+				// handle case where a node refers to a different one
+
+				if ( node.USE ) {
+
+					return resolveUSE( node.USE );
+
+				}
+
+				if ( node.build !== undefined ) return node.build;
+
+				node.build = buildNode( node );
+
+				return node.build;
+
+			}
+
+			// node builder
+
+			function buildNode( node ) {
+
+				var nodeName = node.name;
+				var build;
+
+				switch ( nodeName ) {
+
+					case 'Group':
+					case 'Transform':
+						build = buildGroupingNode( node );
+						break;
+
+					case 'Background':
+						build = buildBackgroundNode( node );
+						break;
+
+					case 'Shape':
+						build = buildShapeNode( node );
+						break;
+
+					case 'Appearance':
+						build = buildApperanceNode( node );
+						break;
+
+					case 'Material':
+						build = buildMaterialNode( node );
+						break;
+
+					case 'ImageTexture':
+						build = buildImageTextureNode( node );
+						break;
+
+					case 'TextureTransform':
+						build = buildTextureTransformNode( node );
+						break;
+
+					case 'IndexedFaceSet':
+						build = buildIndexedFaceSetNode( node );
+						break;
+
+					case 'IndexedLineSet':
+						build = buildIndexedLineSetNode( node );
+						break;
+
+					case 'PointSet':
+						build = buildPointSetNode( node );
+						break;
+
+					case 'Box':
+						build = buildBoxNode( node );
+						break;
+
+					case 'Cone':
+						build = buildConeNode( node );
+						break;
+
+					case 'Cylinder':
+						build = buildCylinderNode( node );
+						break;
+
+					case 'Sphere':
+						build = buildSphereNode( node );
+						break;
+
+					case 'Color':
+					case 'Coordinate':
+					case 'Normal':
+					case 'TextureCoordinate':
+						build = buildGeometricNode( node );
+						break;
+
+					case 'Anchor':
+					case 'Billboard':
+					case 'Collision':
+
+					case 'Inline':
+					case 'LOD':
+					case 'Switch':
+
+					case 'AudioClip':
+					case 'DirectionalLight':
+					case 'PointLight':
+					case 'Script':
+					case 'Sound':
+					case 'SpotLight':
+					case 'WorldInfo':
+
+					case 'CylinderSensor':
+					case 'PlaneSensor':
+					case 'ProximitySensor':
+					case 'SphereSensor':
+					case 'TimeSensor':
+					case 'TouchSensor':
+					case 'VisibilitySensor':
+
+					case 'ElevationGrid':
+					case 'Extrusion':
+					case 'Text':
+
+					case 'FontStyle':
+					case 'MovieTexture':
+					case 'PixelTexture':
+
+					case 'ColorInterpolator':
+					case 'CoordinateInterpolator':
+					case 'NormalInterpolator':
+					case 'OrientationInterpolator':
+					case 'PositionInterpolator':
+					case 'ScalarInterpolator':
+
+					case 'Fog':
+					case 'NavigationInfo':
+					case 'Viewpoint':
+						// node not supported yet
+						break;
+
+					default:
+						console.warn( 'THREE.VRMLLoader: Unknown node:', nodeName );
+						break;
+
+				}
+
+				return build;
+
+			}
+
+			function buildGroupingNode( node ) {
+
+				var object = new Group();
+
+				//
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'center':
+							// field not supported
+							break;
+
+						case 'children':
+							parseFieldChildren( fieldValues, object );
+							break;
+
+						case 'rotation':
+							var axis = new Vector3( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							var angle = fieldValues[ 3 ];
+							object.quaternion.setFromAxisAngle( axis, angle );
+							break;
+
+						case 'scale':
+							object.scale.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'scaleOrientation':
+							// field not supported
+							break;
+
+						case 'translation':
+							object.position.set( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'bboxCenter':
+							// field not supported
+							break;
+
+						case 'bboxSize':
+							// field not supported
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return object;
+
+			}
+
+			function buildBackgroundNode( node ) {
+
+				var group = new Group();
+
+				var groundAngle, groundColor;
+				var skyAngle, skyColor;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'groundAngle':
+							groundAngle = fieldValues;
+							break;
+
+						case 'groundColor':
+							groundColor = fieldValues;
+							break;
+
+						case 'backUrl':
+							// field not supported
+							break;
+
+						case 'bottomUrl':
+							// field not supported
+							break;
+
+						case 'frontUrl':
+							// field not supported
+							break;
+
+						case 'leftUrl':
+							// field not supported
+							break;
+
+						case 'rightUrl':
+							// field not supported
+							break;
+
+						case 'topUrl':
+							// field not supported
+							break;
+
+						case 'skyAngle':
+							skyAngle = fieldValues;
+							break;
+
+						case 'skyColor':
+							skyColor = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// sky
+
+				if ( skyColor ) {
+
+					var radius = 10000;
+
+					var skyGeometry = new SphereBufferGeometry( radius, 32, 16 );
+					var skyMaterial = new MeshBasicMaterial( { fog: false, side: BackSide, depthWrite: false, depthTest: false } );
+
+					if ( skyColor.length > 3 ) {
+
+						paintFaces( skyGeometry, radius, skyAngle, toColorArray( skyColor ), true );
+						skyMaterial.vertexColors = VertexColors;
+
+					} else {
+
+						skyMaterial.color.setRGB( skyColor[ 0 ], skyColor[ 1 ], skyColor[ 2 ] );
+
+					}
+
+					var sky = new Mesh( skyGeometry, skyMaterial );
+					group.add( sky );
+
+				}
+
+				// ground
+
+				if ( groundColor ) {
+
+					if ( groundColor.length > 0 ) {
+
+						var groundGeometry = new SphereBufferGeometry( radius, 32, 16, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
+						var groundMaterial = new MeshBasicMaterial( { fog: false, side: BackSide, vertexColors: VertexColors, depthWrite: false, depthTest: false } );
+
+						paintFaces( groundGeometry, radius, groundAngle, toColorArray( groundColor ), false );
+
+						var ground = new Mesh( groundGeometry, groundMaterial );
+						group.add( ground );
+
+					}
+
+				}
+
+				// render background group first
+
+				group.renderOrder = - Infinity;
+
+				return group;
+
+			}
+
+			function buildShapeNode( node ) {
+
+				var fields = node.fields;
+
+				// if the appearance field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)
+
+				var material = new MeshBasicMaterial( { color: 0x000000 } );
+				var geometry;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'appearance':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								material = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						case 'geometry':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								geometry = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// build 3D object
+
+				var object;
+
+				if ( geometry ) {
+
+					var type = geometry._type;
+
+					if ( type === 'points' ) { // points
+
+						var pointsMaterial = new PointsMaterial( { color: 0xffffff } );
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							pointsMaterial.vertexColors = VertexColors;
+
+						} else {
+
+							// if the color field is NULL and there is a material defined for the appearance affecting this PointSet, then use the emissiveColor of the material to draw the points
+
+							if ( material.isMeshPhongMaterial ) {
+
+								pointsMaterial.color.copy( material.emissive );
+
+							}
+
+						}
+
+						object = new Points( geometry, pointsMaterial );
+
+					} else if ( type === 'line' ) { // lines
+
+						var lineMaterial = new LineBasicMaterial( { color: 0xffffff } );
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							lineMaterial.vertexColors = VertexColors;
+
+						} else {
+
+							// if the color field is NULL and there is a material defined for the appearance affecting this IndexedLineSet, then use the emissiveColor of the material to draw the lines
+
+							if ( material.isMeshPhongMaterial ) {
+
+								lineMaterial.color.copy( material.emissive );
+
+							}
+
+						}
+
+						object = new LineSegments( geometry, lineMaterial );
+
+					} else { // consider meshes
+
+						// check "solid" hint (it's placed in the geometry but affects the material)
+
+						if ( geometry._solid !== undefined ) {
+
+							material.side = ( geometry._solid ) ? FrontSide : DoubleSide;
+
+						}
+
+						// check for vertex colors
+
+						if ( geometry.attributes.color !== undefined ) {
+
+							material.vertexColors = VertexColors;
+
+						}
+
+						object = new Mesh( geometry, material );
+
+					}
+
+				} else {
+
+					object = new Object3D();
+
+					// if the geometry field is NULL the object is not drawn
+
+					object.visible = false;
+
+				}
+
+				return object;
+
+			}
+
+			function buildApperanceNode( node ) {
+
+				var material = new MeshPhongMaterial();
+				var transformData;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'material':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								var materialData = getNode( fieldValues[ 0 ] );
+
+								if ( materialData.diffuseColor ) material.color.copy( materialData.diffuseColor );
+								if ( materialData.emissiveColor ) material.emissive.copy( materialData.emissiveColor );
+								if ( materialData.shininess ) material.shininess = materialData.shininess;
+								if ( materialData.specularColor ) material.specular.copy( materialData.specularColor );
+								if ( materialData.transparency ) material.opacity = 1 - materialData.transparency;
+								if ( materialData.transparency > 0 ) material.transparent = true;
+
+							} else {
+
+								// if the material field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0)
+
+								material = new MeshBasicMaterial( { color: 0x000000 } );
+
+							}
+							break;
+
+						case 'texture':
+							var textureNode = fieldValues[ 0 ];
+							if ( textureNode !== null ) {
+
+								if ( textureNode.name === 'ImageTexture' ) {
+
+									material.map = getNode( textureNode );
+
+								} else {
+
+									// MovieTexture and PixelTexture not supported yet
+
+								}
+
+							}
+							break;
+
+						case 'textureTransform':
+							if ( fieldValues[ 0 ] !== null ) {
+
+								transformData = getNode( fieldValues[ 0 ] );
+
+							}
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// only apply texture transform data if a texture was defined
+
+				if ( material.map && transformData ) {
+
+					material.map.center.copy( transformData.center );
+					material.map.rotation = transformData.rotation;
+					material.map.repeat.copy( transformData.scale );
+					material.map.offset.copy( transformData.translation );
+
+				}
+
+				return material;
+
+			}
+
+			function buildMaterialNode( node ) {
+
+				var materialData = {};
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'ambientIntensity':
+							// field not supported
+							break;
+
+						case 'diffuseColor':
+							materialData.diffuseColor = new Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'emissiveColor':
+							materialData.emissiveColor = new Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'shininess':
+							materialData.shininess = fieldValues[ 0 ];
+							break;
+
+						case 'specularColor':
+							materialData.emissiveColor = new Color( fieldValues[ 0 ], fieldValues[ 1 ], fieldValues[ 2 ] );
+							break;
+
+						case 'transparency':
+							materialData.transparency = fieldValues[ 0 ];
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return materialData;
+
+			}
+
+			function buildImageTextureNode( node ) {
+
+				var texture;
+				var wrapS = RepeatWrapping;
+				var wrapT = RepeatWrapping;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'url':
+							var url = fieldValues[ 0 ];
+							if ( url ) texture = textureLoader.load( url );
+							break;
+
+						case 'repeatS':
+							if ( fieldValues[ 0 ] === false ) wrapS = ClampToEdgeWrapping;
+							break;
+
+						case 'repeatT':
+							if ( fieldValues[ 0 ] === false ) wrapT = ClampToEdgeWrapping;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				if ( texture ) {
+
+					texture.wrapS = wrapS;
+					texture.wrapT = wrapT;
+
+				}
+
+				return texture;
+
+			}
+
+			function buildTextureTransformNode( node ) {
+
+				var transformData = {
+					center: new Vector2(),
+					rotation: new Vector2(),
+					scale: new Vector2(),
+					translation: new Vector2()
+				};
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'center':
+							transformData.center.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						case 'rotation':
+							transformData.rotation = fieldValues[ 0 ];
+							break;
+
+						case 'scale':
+							transformData.scale.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						case 'translation':
+							transformData.translation.set( fieldValues[ 0 ], fieldValues[ 1 ] );
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				return transformData;
+
+			}
+
+			function buildGeometricNode( node ) {
+
+				return node.fields[ 0 ].values;
+
+			}
+
+			function buildIndexedFaceSetNode( node ) {
+
+				var color, coord, normal, texCoord;
+				var ccw = true, solid = true, creaseAngle;
+				var colorIndex, coordIndex, normalIndex, texCoordIndex;
+				var colorPerVertex = true, normalPerVertex = true;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'color':
+							var colorNode = fieldValues[ 0 ];
+
+							if ( colorNode !== null ) {
+
+								color = getNode( colorNode );
+
+							}
+							break;
+
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
+
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
+
+							}
+							break;
+
+						case 'normal':
+							var normalNode = fieldValues[ 0 ];
+
+							if ( normalNode !== null ) {
+
+								normal = getNode( normalNode );
+
+							}
+							break;
+
+						case 'texCoord':
+							var texCoordNode = fieldValues[ 0 ];
+
+							if ( texCoordNode !== null ) {
+
+								texCoord = getNode( texCoordNode );
+
+							}
+							break;
+
+						case 'ccw':
+							ccw = fieldValues[ 0 ];
+							break;
+
+						case 'colorIndex':
+							colorIndex = fieldValues;
+							break;
+
+						case 'colorPerVertex':
+							colorPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'convex':
+							// field not supported
+							break;
+
+						case 'coordIndex':
+							coordIndex = fieldValues;
+							break;
+
+						case 'creaseAngle':
+							creaseAngle = fieldValues[ 0 ];
+							break;
+
+						case 'normalIndex':
+							normalIndex = fieldValues;
+							break;
+
+						case 'normalPerVertex':
+							normalPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'solid':
+							solid = fieldValues[ 0 ];
+							break;
+
+						case 'texCoordIndex':
+							texCoordIndex = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				var triangulatedCoordIndex = triangulateFaceIndex( coordIndex, ccw );
+
+				var positionAttribute;
+				var colorAttribute;
+				var normalAttribute;
+				var uvAttribute;
+
+				if ( color ) {
+
+					if ( colorPerVertex === true ) {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then it is used to choose colors for each vertex of the IndexedFaceSet.
+
+							var triangulatedColorIndex = triangulateFaceIndex( colorIndex, ccw );
+							colorAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedColorIndex, color, 3 );
+
+						} else {
+
+							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node
+
+							colorAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new Float32BufferAttribute( color, 3 ) );
+
+						}
+
+					} else {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then they are used to choose one color for each face of the IndexedFaceSet
+
+							var flattenFaceColors = flattenData( color, colorIndex );
+							var triangulatedFaceColors = triangulateFaceData( flattenFaceColors, coordIndex );
+							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );
+
+						} else {
+
+							// if the colorIndex field is empty, then the color are applied to each face of the IndexedFaceSet in order
+
+							var triangulatedFaceColors = triangulateFaceData( color, coordIndex );
+							colorAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceColors );
+
+
+						}
+
+					}
+
+				}
+
+				if ( normal ) {
+
+					if ( normalPerVertex === true ) {
+
+						// consider vertex normals
+
+						if ( normalIndex.length > 0 ) {
+
+							// if the normalIndex field is not empty, then it is used to choose normals for each vertex of the IndexedFaceSet.
+
+							var triangulatedNormalIndex = triangulateFaceIndex( normalIndex, ccw );
+							normalAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedNormalIndex, normal, 3 );
+
+						} else {
+
+							// if the normalIndex field is empty, then the coordIndex field is used to choose normals from the Normal node
+
+							normalAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new Float32BufferAttribute( normal, 3 ) );
+
+						}
+
+					} else {
+
+						// consider face normals
+
+						if ( normalIndex.length > 0 ) {
+
+							// if the normalIndex field is not empty, then they are used to choose one normal for each face of the IndexedFaceSet
+
+							var flattenFaceNormals = flattenData( normal, normalIndex );
+							var triangulatedFaceNormals = triangulateFaceData( flattenFaceNormals, coordIndex );
+							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );
+
+						} else {
+
+							// if the normalIndex field is empty, then the normals are applied to each face of the IndexedFaceSet in order
+
+							var triangulatedFaceNormals = triangulateFaceData( normal, coordIndex );
+							normalAttribute = computeAttributeFromFaceData( triangulatedCoordIndex, triangulatedFaceNormals );
+
+						}
+
+					}
+
+				} else {
+
+					// if the normal field is NULL, then the loader should automatically generate normals, using creaseAngle to determine if and how normals are smoothed across shared vertices
+
+					normalAttribute = computeNormalAttribute( triangulatedCoordIndex, coord, creaseAngle );
+
+				}
+
+				if ( texCoord ) {
+
+					// texture coordinates are always defined on vertex level
+
+					if ( texCoordIndex.length > 0 ) {
+
+						// if the texCoordIndex field is not empty, then it is used to choose texture coordinates for each vertex of the IndexedFaceSet.
+
+						var triangulatedTexCoordIndex = triangulateFaceIndex( texCoordIndex, ccw );
+						uvAttribute = computeAttributeFromIndexedData( triangulatedCoordIndex, triangulatedTexCoordIndex, texCoord, 2 );
+
+
+					} else {
+
+						// if the texCoordIndex field is empty, then the coordIndex array is used to choose texture coordinates from the TextureCoordinate node
+
+						uvAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new Float32BufferAttribute( texCoord, 2 ) );
+
+					}
+
+				}
+
+				var geometry = new BufferGeometry();
+				positionAttribute = toNonIndexedAttribute( triangulatedCoordIndex, new Float32BufferAttribute( coord, 3 ) );
+
+				geometry.addAttribute( 'position', positionAttribute );
+				geometry.addAttribute( 'normal', normalAttribute );
+
+				// optional attributes
+
+				if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
+				if ( uvAttribute ) geometry.addAttribute( 'uv', uvAttribute );
+
+				// "solid" influences the material so let's store it for later use
+
+				geometry._solid = solid;
+				geometry._type = 'mesh';
+
+				return geometry;
+
+			}
+
+			function buildIndexedLineSetNode( node ) {
+
+				var color, coord;
+				var colorIndex, coordIndex;
+				var colorPerVertex = true;
+
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
+
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
+
+					switch ( fieldName ) {
+
+						case 'color':
+							var colorNode = fieldValues[ 0 ];
+
+							if ( colorNode !== null ) {
+
+								color = getNode( colorNode );
+
+							}
+							break;
+
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
+
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
+
+							}
+							break;
+
+						case 'colorIndex':
+							colorIndex = fieldValues;
+							break;
+
+						case 'colorPerVertex':
+							colorPerVertex = fieldValues[ 0 ];
+							break;
+
+						case 'coordIndex':
+							coordIndex = fieldValues;
+							break;
+
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
+
+					}
+
+				}
+
+				// build lines
+
+				var colorAttribute;
+
+				var expandedLineIndex = expandLineIndex( coordIndex ); // create an index for three.js's linesegment primitive
+
+				if ( color ) {
+
+					if ( colorPerVertex === true ) {
+
+						if ( colorIndex.length > 0 ) {
+
+							// if the colorIndex field is not empty, then one color is used for each polyline of the IndexedLineSet.
+
+							var expandedColorIndex = expandLineIndex( colorIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromIndexedData( expandedLineIndex, expandedColorIndex, color, 3 ); // compute data on vertex level
+
+						} else {
+
+							// if the colorIndex field is empty, then the colors are applied to each polyline of the IndexedLineSet in order.
+
+							colorAttribute = toNonIndexedAttribute( expandedLineIndex, new Float32BufferAttribute( color, 3 ) );
+
+						}
 
-					}
+					} else {
 
-				} else if ( scope.isRecordingAngles ) {
+						if ( colorIndex.length > 0 ) {
 
-					// the parts hold the angles as strings
-					if ( parts.length > 0 ) {
+							// if the colorIndex field is not empty, then colors are applied to each vertex of the IndexedLineSet
 
-						for ( var ind = 0; ind < parts.length; ind ++ ) {
+							var flattenLineColors = flattenData( color, colorIndex ); // compute colors for each VRML primitve
+							var expandedLineColors = expandLineData( flattenLineColors, coordIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
 
-							// the part should be a float
-							if ( ! float_pattern.test( parts[ ind ] ) ) {
 
-								continue;
+						} else {
 
-							}
+							// if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node
 
-							scope.angles.push( parseFloat( parts[ ind ] ) );
+							var expandedLineColors = expandLineData( color, coordIndex ); // compute colors for each line segment (rendering primitve)
+							colorAttribute = computeAttributeFromLineData( expandedLineIndex, expandedLineColors ); // compute data on vertex level
 
 						}
 
 					}
 
-					// end
-					if ( /]/.exec( line ) ) {
+				}
 
-						scope.isRecordingAngles = false;
-						node[ scope.recordingFieldname ] = scope.angles;
+				//
 
-					}
+				var geometry = new BufferGeometry();
 
-				} else if ( scope.isRecordingColors ) {
+				var positionAttribute = toNonIndexedAttribute( expandedLineIndex, new Float32BufferAttribute( coord, 3 ) );
+				geometry.addAttribute( 'position', positionAttribute );
 
-					while ( null !== ( parts = float3_pattern.exec( line ) ) ) {
+				if ( colorAttribute ) geometry.addAttribute( 'color', colorAttribute );
 
-						var color = {
-							r: parseFloat( parts[ 1 ] ),
-							g: parseFloat( parts[ 2 ] ),
-							b: parseFloat( parts[ 3 ] )
-						};
+				geometry._type = 'line';
 
-						scope.colors.push( color );
+				return geometry;
 
-					}
+			}
 
-					// end
-					if ( /]/.exec( line ) ) {
+			function buildPointSetNode( node ) {
 
-						scope.isRecordingColors = false;
-						node[ scope.recordingFieldname ] = scope.colors;
+				var geometry;
+				var color, coord;
 
-					}
+				var fields = node.fields;
+
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-				} else if ( parts[ parts.length - 1 ] !== 'NULL' && fieldName !== 'children' ) {
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
 					switch ( fieldName ) {
 
-						case 'diffuseColor':
-						case 'emissiveColor':
-						case 'specularColor':
 						case 'color':
+							var colorNode = fieldValues[ 0 ];
 
-							if ( parts.length !== 4 ) {
+							if ( colorNode !== null ) {
 
-								console.warn( 'THREE.VRMLLoader: Invalid color format detected for %s.', fieldName );
-								break;
+								color = getNode( colorNode );
 
 							}
-
-							property = {
-								r: parseFloat( parts[ 1 ] ),
-								g: parseFloat( parts[ 2 ] ),
-								b: parseFloat( parts[ 3 ] )
-							};
-
 							break;
 
-						case 'location':
-						case 'direction':
-						case 'translation':
-						case 'scale':
-						case 'size':
-							if ( parts.length !== 4 ) {
+						case 'coord':
+							var coordNode = fieldValues[ 0 ];
 
-								console.warn( 'THREE.VRMLLoader: Invalid vector format detected for %s.', fieldName );
-								break;
+							if ( coordNode !== null ) {
+
+								coord = getNode( coordNode );
 
 							}
+							break;
 
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] )
-							};
 
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
 							break;
 
-						case 'intensity':
-						case 'cutOffAngle':
-						case 'radius':
-						case 'topRadius':
-						case 'bottomRadius':
-						case 'height':
-						case 'transparency':
-						case 'shininess':
-						case 'ambientIntensity':
-						case 'creaseAngle':
-							if ( parts.length !== 2 ) {
+					}
 
-								console.warn( 'THREE.VRMLLoader: Invalid single float value specification detected for %s.', fieldName );
-								break;
+				}
 
-							}
+				var geometry = new BufferGeometry();
 
-							property = parseFloat( parts[ 1 ] );
+				geometry.addAttribute( 'position', new Float32BufferAttribute( coord, 3 ) );
+				if ( color ) geometry.addAttribute( 'color', new Float32BufferAttribute( color, 3 ) );
 
-							break;
+				geometry._type = 'points';
 
-						case 'rotation':
-							if ( parts.length !== 5 ) {
+				return geometry;
 
-								console.warn( 'THREE.VRMLLoader: Invalid quaternion format detected for %s.', fieldName );
-								break;
+			}
 
-							}
+			function buildBoxNode( node ) {
 
-							property = {
-								x: parseFloat( parts[ 1 ] ),
-								y: parseFloat( parts[ 2 ] ),
-								z: parseFloat( parts[ 3 ] ),
-								w: parseFloat( parts[ 4 ] )
-							};
+				var size = new Vector3( 2, 2, 2 );
 
-							break;
+				var fields = node.fields;
 
-						case 'on':
-						case 'ccw':
-						case 'solid':
-						case 'colorPerVertex':
-						case 'convex':
-							if ( parts.length !== 2 ) {
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-								console.warn( 'THREE.VRMLLoader: Invalid format detected for %s.', fieldName );
-								break;
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-							}
+					switch ( fieldName ) {
 
-							property = parts[ 1 ] === 'TRUE' ? true : false;
+						case 'size':
+							size.x = fieldValues[ 0 ];
+							size.y = fieldValues[ 1 ];
+							size.z = fieldValues[ 2 ];
+							break;
 
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
 							break;
 
 					}
 
-					// VRMLLoader does not support text so it can't process the "string" property yet
-
-					if ( fieldName !== 'string' ) node[ fieldName ] = property;
-
 				}
 
-				return property;
+				var geometry = new BoxBufferGeometry( size.x, size.y, size.z );
+
+				return geometry;
 
 			}
 
-			function getTree( lines ) {
+			function buildConeNode( node ) {
 
-				var tree = { 'string': 'Scene', children: [] };
-				var current = tree;
-				var matches;
-				var specification;
+				var radius = 1, height = 2, openEnded = false;
 
-				for ( var i = 0; i < lines.length; i ++ ) {
+				var fields = node.fields;
 
-					var comment = '';
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-					var line = lines[ i ];
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-					// omit whitespace only lines
-					if ( null !== ( /^\s+?$/g.exec( line ) ) ) {
+					switch ( fieldName ) {
 
-						continue;
+						case 'bottom':
+							openEnded = ! fieldValues[ 0 ];
+							break;
 
-					}
+						case 'bottomRadius':
+							radius = fieldValues[ 0 ];
+							break;
 
-					line = line.trim();
+						case 'height':
+							height = fieldValues[ 0 ];
+							break;
 
-					// skip empty lines
-					if ( line === '' ) {
+						case 'side':
+							// field not supported
+							break;
 
-						continue;
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
 					}
 
-					if ( /#/.exec( line ) ) {
-
-						var parts = line.split( '#' );
-
-						// discard everything after the #, it is a comment
-						line = parts[ 0 ];
+				}
 
-						// well, let's also keep the comment
-						comment = parts[ 1 ];
+				var geometry = new ConeBufferGeometry( radius, height, 16, 1, openEnded );
 
-					}
+				return geometry;
 
-					if ( matches = /([^\s]*){1}(?:\s+)?{/.exec( line ) ) {
+			}
 
-						// first subpattern should match the Node name
+			function buildCylinderNode( node ) {
 
-						var block = { 'nodeType': matches[ 1 ], 'string': line, 'parent': current, 'children': [], 'comment': comment };
-						current.children.push( block );
-						current = block;
+				var radius = 1, height = 2;
 
-						if ( /}/.exec( line ) ) {
+				var fields = node.fields;
 
-							// example: geometry Box { size 1 1 1 } # all on the same line
-							specification = /{(.*)}/.exec( line )[ 1 ];
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-							// todo: remove once new parsing is complete?
-							block.children.push( specification );
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-							parseProperty( current, specification );
+					switch ( fieldName ) {
 
-							current = current.parent;
+						case 'bottom':
+							// field not supported
+							break;
 
-						}
+						case 'radius':
+							radius = fieldValues[ 0 ];
+							break;
 
-					} else if ( /}/.exec( line ) ) {
+						case 'height':
+							height = fieldValues[ 0 ];
+							break;
 
-						current = current.parent;
+						case 'side':
+							// field not supported
+							break;
 
-					} else if ( line !== '' ) {
+						case 'top':
+							// field not supported
+							break;
 
-						parseProperty( current, line );
-						// todo: remove once new parsing is complete? we still do not parse geometry and appearance the new way
-						current.children.push( line );
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
 					}
 
 				}
 
-				return tree;
+				var geometry = new CylinderBufferGeometry( radius, radius, height, 16, 1 );
 
-			}
-
-			function parseNode( data, parent ) {
+				return geometry;
 
-				var object;
+			}
 
-				if ( typeof data === 'string' ) {
+			function buildSphereNode( node ) {
 
-					if ( /USE/.exec( data ) ) {
+				var radius = 1;
 
-						var defineKey = /USE\s+?([^\s]+)/.exec( data )[ 1 ];
+				var fields = node.fields;
 
-						if ( undefined == defines[ defineKey ] ) {
+				for ( var i = 0, l = fields.length; i < l; i ++ ) {
 
-							console.warn( 'THREE.VRMLLoader: %s is not defined.', defineKey );
+					var field = fields[ i ];
+					var fieldName = field.name;
+					var fieldValues = field.values;
 
-						} else {
+					switch ( fieldName ) {
 
-							if ( /appearance/.exec( data ) && defineKey ) {
+						case 'radius':
+							radius = fieldValues[ 0 ];
+							break;
 
-								parent.material = defines[ defineKey ].clone();
+						default:
+							console.warn( 'THREE.VRMLLoader: Unknown field:', fieldName );
+							break;
 
-							} else if ( /geometry/.exec( data ) && defineKey ) {
+					}
 
-								parent.geometry = defines[ defineKey ].clone();
+				}
 
-								// the solid property is not cloned with clone(), is only needed for VRML loading, so we need to transfer it
-								if ( defines[ defineKey ].solid !== undefined && defines[ defineKey ].solid === false ) {
+				var geometry = new SphereBufferGeometry( radius, 16, 16 );
 
-									parent.geometry.solid = false;
-									parent.material.side = DoubleSide;
+				return geometry;
 
-								}
+			}
 
-							} else if ( defineKey ) {
+			// helper functions
 
-								object = defines[ defineKey ].clone();
-								parent.add( object );
+			function resolveUSE( identifier ) {
 
-							}
+				var node = nodeMap[ identifier ];
+				var build = getNode( node );
 
-						}
+				// because the same 3D objects can have different transformations, it's necessary to clone them.
+				// materials can be influenced by the geometry (e.g. vertex normals). cloning is necessary to avoid
+				// any side effects
 
-					}
+				return ( build.isObject3D || build.isMaterial ) ? build.clone() : build;
 
-					return;
+			}
 
-				}
+			function parseFieldChildren( children, owner ) {
 
-				object = parent;
+				for ( var i = 0, l = children.length; i < l; i ++ ) {
 
-				if ( data.string.indexOf( 'AmbientLight' ) > - 1 && data.nodeType === 'PointLight' ) {
+					var object = getNode( children[ i ] );
 
-					data.nodeType = 'AmbientLight';
+					if ( object instanceof Object3D ) owner.add( object );
 
 				}
 
-				var l_visible = data.on !== undefined ? data.on : true;
-				var l_intensity = data.intensity !== undefined ? data.intensity : 1;
-				var l_color = new Color();
+			}
 
-				if ( data.color ) {
+			function triangulateFaceIndex( index, ccw ) {
 
-					l_color.copy( data.color );
+				var indices = [];
 
-				}
+				// since face defintions can have more than three vertices, it's necessary to
+				// perform a simple triangulation
 
-				if ( data.nodeType === 'AmbientLight' ) {
+				var start = 0;
 
-					object = new AmbientLight( l_color, l_intensity );
-					object.visible = l_visible;
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					parent.add( object );
+					var i1 = index[ start ];
+					var i2 = index[ i + ( ccw ? 1 : 2 ) ];
+					var i3 = index[ i + ( ccw ? 2 : 1 ) ];
 
-				} else if ( data.nodeType === 'PointLight' ) {
+					indices.push( i1, i2, i3 );
 
-					var l_distance = 0;
+					// an index of -1 indicates that the current face has ended and the next one begins
 
-					if ( data.radius !== undefined && data.radius < 1000 ) {
+					if ( index[ i + 3 ] === - 1 ) {
 
-						l_distance = data.radius;
+						i += 3;
+						start = i + 1;
 
 					}
 
-					object = new PointLight( l_color, l_intensity, l_distance );
-					object.visible = l_visible;
+				}
 
-					parent.add( object );
+				return indices;
 
-				} else if ( data.nodeType === 'SpotLight' ) {
+			}
 
-					var l_intensity = 1;
-					var l_distance = 0;
-					var l_angle = Math.PI / 3;
-					var l_penumbra = 0;
-					var l_visible = true;
+			function triangulateFaceData( data, index ) {
 
-					if ( data.radius !== undefined && data.radius < 1000 ) {
+				var triangulatedData = [];
 
-						l_distance = data.radius;
+				var start = 0;
 
-					}
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					if ( data.cutOffAngle !== undefined ) {
+					var stride = start * 3;
 
-						l_angle = data.cutOffAngle;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-					}
+					triangulatedData.push( x, y, z );
 
-					object = new SpotLight( l_color, l_intensity, l_distance, l_angle, l_penumbra );
-					object.visible = l_visible;
+					// an index of -1 indicates that the current face has ended and the next one begins
 
-					parent.add( object );
+					if ( index[ i + 3 ] === - 1 ) {
 
-				} else if ( data.nodeType === 'Transform' || data.nodeType === 'Group' ) {
+						i += 3;
+						start ++;
 
-					object = new Object3D();
+					}
 
-					if ( /DEF/.exec( data.string ) ) {
+				}
 
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
-						defines[ object.name ] = object;
+				return triangulatedData;
 
-					}
+			}
 
-					if ( data.translation !== undefined ) {
+			function flattenData( data, index ) {
 
-						var t = data.translation;
+				var flattenData = [];
 
-						object.position.set( t.x, t.y, t.z );
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-					}
+					var i1 = index[ i ];
 
-					if ( data.rotation !== undefined ) {
+					var stride = i1 * 3;
 
-						var r = data.rotation;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-						object.quaternion.setFromAxisAngle( new Vector3( r.x, r.y, r.z ), r.w );
+					flattenData.push( x, y, z );
 
-					}
+				}
 
-					if ( data.scale !== undefined ) {
+				return flattenData;
 
-						var s = data.scale;
+			}
 
-						object.scale.set( s.x, s.y, s.z );
+			function expandLineIndex( index ) {
 
-					}
+				var indices = [];
 
-					parent.add( object );
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-				} else if ( data.nodeType === 'Shape' ) {
+					var i1 = index[ i ];
+					var i2 = index[ i + 1 ];
 
-					object = new Mesh();
+					indices.push( i1, i2 );
 
-					if ( /DEF/.exec( data.string ) ) {
+					// an index of -1 indicates that the current line has ended and the next one begins
 
-						object.name = /DEF\s+([^\s]+)/.exec( data.string )[ 1 ];
+					if ( index[ i + 2 ] === - 1 ) {
 
-						defines[ object.name ] = object;
+						i += 2;
 
 					}
 
-					parent.add( object );
+				}
 
-				} else if ( data.nodeType === 'Background' ) {
+				return indices;
 
-					var segments = 20;
+			}
 
-					// sky (full sphere):
+			function expandLineData( data, index ) {
 
-					var radius = 2e4;
+				var triangulatedData = [];
 
-					var skyGeometry = new SphereBufferGeometry( radius, segments, segments );
-					var skyMaterial = new MeshBasicMaterial( { fog: false, side: BackSide } );
+				var start = 0;
 
-					if ( data.skyColor.length > 1 ) {
+				for ( var i = 0, l = index.length; i < l; i ++ ) {
 
-						paintFaces( skyGeometry, radius, data.skyAngle, data.skyColor, true );
+					var stride = start * 3;
 
-						skyMaterial.vertexColors = VertexColors;
+					var x = data[ stride ];
+					var y = data[ stride + 1 ];
+					var z = data[ stride + 2 ];
 
-					} else {
+					triangulatedData.push( x, y, z );
 
-						var color = data.skyColor[ 0 ];
-						skyMaterial.color.setRGB( color.r, color.b, color.g );
+					// an index of -1 indicates that the current line has ended and the next one begins
 
-					}
+					if ( index[ i + 2 ] === - 1 ) {
 
-					scene.add( new Mesh( skyGeometry, skyMaterial ) );
+						i += 2;
+						start ++;
 
-					// ground (half sphere):
+					}
 
-					if ( data.groundColor !== undefined ) {
+				}
 
-						radius = 1.2e4;
+				return triangulatedData;
 
-						var groundGeometry = new SphereBufferGeometry( radius, segments, segments, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI );
-						var groundMaterial = new MeshBasicMaterial( { fog: false, side: BackSide, vertexColors: VertexColors } );
+			}
 
-						paintFaces( groundGeometry, radius, data.groundAngle, data.groundColor, false );
+			var vA = new Vector3();
+			var vB = new Vector3();
+			var vC = new Vector3();
 
-						scene.add( new Mesh( groundGeometry, groundMaterial ) );
+			var uvA = new Vector2();
+			var uvB = new Vector2();
+			var uvC = new Vector2();
 
-					}
+			function computeAttributeFromIndexedData( coordIndex, index, data, itemSize ) {
 
-				} else if ( /geometry/.exec( data.string ) ) {
+				var array = [];
 
-					if ( data.nodeType === 'Box' ) {
+				// we use the coordIndex.length as delimiter since normalIndex must contain at least as many indices
 
-						var s = data.size;
+				for ( var i = 0, l = coordIndex.length; i < l; i += 3 ) {
 
-						parent.geometry = new BoxBufferGeometry( s.x, s.y, s.z );
+					var a = index[ i ];
+					var b = index[ i + 1 ];
+					var c = index[ i + 2 ];
 
-					} else if ( data.nodeType === 'Cylinder' ) {
+					if ( itemSize === 2 ) {
 
-						parent.geometry = new CylinderBufferGeometry( data.radius, data.radius, data.height );
+						uvA.fromArray( data, a * itemSize );
+						uvB.fromArray( data, b * itemSize );
+						uvC.fromArray( data, c * itemSize );
 
-					} else if ( data.nodeType === 'Cone' ) {
+						array.push( uvA.x, uvA.y );
+						array.push( uvB.x, uvB.y );
+						array.push( uvC.x, uvC.y );
 
-						parent.geometry = new CylinderBufferGeometry( data.topRadius, data.bottomRadius, data.height );
+					} else {
 
-					} else if ( data.nodeType === 'Sphere' ) {
+						vA.fromArray( data, a * itemSize );
+						vB.fromArray( data, b * itemSize );
+						vC.fromArray( data, c * itemSize );
 
-						parent.geometry = new SphereBufferGeometry( data.radius );
+						array.push( vA.x, vA.y, vA.z );
+						array.push( vB.x, vB.y, vB.z );
+						array.push( vC.x, vC.y, vC.z );
 
-					} else if ( data.nodeType === 'IndexedLineSet' ) {
+					}
 
-						console.warn( 'THREE.VRMLLoader: IndexedLineSet not supported yet.' );
-						parent.parent.remove( parent ); // since the loader is not able to parse the geometry, remove the respective 3D object
+				}
 
-					} else if ( data.nodeType === 'Text' ) {
+				return new Float32BufferAttribute( array, itemSize );
 
-						console.warn( 'THREE.VRMLLoader: Text not supported yet.' );
-						parent.parent.remove( parent );
+			}
 
-					} else if ( data.nodeType === 'IndexedFaceSet' ) {
+			function computeAttributeFromFaceData( index, faceData ) {
 
-						var geometry = new BufferGeometry();
+				var array = [];
 
-						var positions = [];
-						var colors = [];
-						var normals = [];
-						var uvs = [];
+				for ( var i = 0, j = 0, l = index.length; i < l; i += 3, j ++ ) {
 
-						var position, color, normal, uv;
+					vA.fromArray( faceData, j * 3 );
 
-						var i, il, j, jl;
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
 
-						for ( i = 0, il = data.children.length; i < il; i ++ ) {
+				}
 
-							var child = data.children[ i ];
+				return new Float32BufferAttribute( array, 3 );
 
-							// uvs
+			}
 
-							if ( child.nodeType === 'TextureCoordinate' ) {
+			function computeAttributeFromLineData( index, lineData ) {
 
-								if ( child.points ) {
+				var array = [];
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+				for ( var i = 0, j = 0, l = index.length; i < l; i += 2, j ++ ) {
 
-										uv = child.points[ j ];
-										uvs.push( uv.x, uv.y );
+					vA.fromArray( lineData, j * 3 );
 
-									}
+					array.push( vA.x, vA.y, vA.z );
+					array.push( vA.x, vA.y, vA.z );
 
-								}
+				}
 
-							}
+				return new Float32BufferAttribute( array, 3 );
 
-							// normals
+			}
 
-							if ( child.nodeType === 'Normal' ) {
+			function toNonIndexedAttribute( indices, attribute ) {
 
-								if ( child.points ) {
+				var array = attribute.array;
+				var itemSize = attribute.itemSize;
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+				var array2 = new array.constructor( indices.length * itemSize );
 
-										normal = child.points[ j ];
-										normals.push( normal.x, normal.y, normal.z );
+				var index = 0, index2 = 0;
 
-									}
+				for ( var i = 0, l = indices.length; i < l; i ++ ) {
 
-								}
+					index = indices[ i ] * itemSize;
 
-							}
+					for ( var j = 0; j < itemSize; j ++ ) {
 
-							// colors
+						array2[ index2 ++ ] = array[ index ++ ];
 
-							if ( child.nodeType === 'Color' ) {
+					}
 
-								if ( child.color ) {
+				}
 
-									for ( j = 0, jl = child.color.length; j < jl; j ++ ) {
+				return new Float32BufferAttribute( array2, itemSize );
 
-										color = child.color[ j ];
-										colors.push( color.r, color.g, color.b );
+			}
 
-									}
+			var ab = new Vector3();
+			var cb = new Vector3();
 
-								}
+			function computeNormalAttribute( index, coord, creaseAngle ) {
 
-							}
+				var faces = [];
+				var vertexNormals = {};
 
-							// positions
+				// prepare face and raw vertex normals
 
-							if ( child.nodeType === 'Coordinate' ) {
+				for ( var i = 0, l = index.length; i < l; i += 3 ) {
 
-								if ( child.points ) {
+					var a = index[ i ];
+					var b = index[ i + 1 ];
+					var c = index[ i + 2 ];
 
-									for ( j = 0, jl = child.points.length; j < jl; j ++ ) {
+					var face = new Face( a, b, c );
 
-										position = child.points[ j ];
-										positions.push( position.x, position.y, position.z );
+					vA.fromArray( coord, a * 3 );
+					vB.fromArray( coord, b * 3 );
+					vC.fromArray( coord, c * 3 );
 
-									}
+					cb.subVectors( vC, vB );
+					ab.subVectors( vA, vB );
+					cb.cross( ab );
 
-								}
+					cb.normalize();
 
-								if ( child.string.indexOf( 'DEF' ) > - 1 ) {
+					face.normal.copy( cb );
 
-									var name = /DEF\s+([^\s]+)/.exec( child.string )[ 1 ];
+					if ( vertexNormals[ a ] === undefined ) vertexNormals[ a ] = [];
+					if ( vertexNormals[ b ] === undefined ) vertexNormals[ b ] = [];
+					if ( vertexNormals[ c ] === undefined ) vertexNormals[ c ] = [];
 
-									defines[ name ] = positions.slice( 0 );
+					vertexNormals[ a ].push( face.normal );
+					vertexNormals[ b ].push( face.normal );
+					vertexNormals[ c ].push( face.normal );
 
-								}
+					faces.push( face );
 
-								if ( child.string.indexOf( 'USE' ) > - 1 ) {
+				}
 
-									var defineKey = /USE\s+([^\s]+)/.exec( child.string )[ 1 ];
+				// compute vertex normals and build final geometry
 
-									positions = defines[ defineKey ];
+				var normals = [];
 
-								}
+				for ( var i = 0, l = faces.length; i < l; i ++ ) {
 
-							}
+					var face = faces[ i ];
 
-						}
+					var nA = weightedNormal( vertexNormals[ face.a ], face.normal, creaseAngle );
+					var nB = weightedNormal( vertexNormals[ face.b ], face.normal, creaseAngle );
+					var nC = weightedNormal( vertexNormals[ face.c ], face.normal, creaseAngle );
 
-						// some shapes only have vertices for use in other shapes
+					vA.fromArray( coord, face.a * 3 );
+					vB.fromArray( coord, face.b * 3 );
+					vC.fromArray( coord, face.c * 3 );
 
-						if ( data.coordIndex ) {
+					normals.push( nA.x, nA.y, nA.z );
+					normals.push( nB.x, nB.y, nB.z );
+					normals.push( nC.x, nC.y, nC.z );
 
-							function triangulateIndexArray( indexArray, ccw, colorPerVertex ) {
+				}
 
-								if ( ccw === undefined ) {
+				return new Float32BufferAttribute( normals, 3 );
 
-									// ccw is true by default
-									ccw = true;
+			}
 
-								}
+			function weightedNormal( normals, vector, creaseAngle ) {
 
-								var triangulatedIndexArray = [];
-								var skip = 0;
+				var normal = vector.clone();
 
-								for ( i = 0, il = indexArray.length; i < il; i ++ ) {
+				for ( var i = 0, l = normals.length; i < l; i ++ ) {
 
-									if ( colorPerVertex === false ) {
+					if ( normals[ i ].angleTo( vector ) < creaseAngle ) {
 
-										var colorIndices = indexArray[ i ];
+						normal.add( normals[ i ] );
 
-										for ( j = 0, jl = colorIndices.length; j < jl; j ++ ) {
+					}
 
-											var index = colorIndices[ j ];
+				}
 
-											triangulatedIndexArray.push( index, index, index );
+				return normal.normalize();
 
-										}
+			}
 
-									} else {
+			function toColorArray( colors ) {
 
-										var indexedFace = indexArray[ i ];
+				var array = [];
 
-										// VRML support multipoint indexed face sets (more then 3 vertices). You must calculate the composing triangles here
+				for ( var i = 0, l = colors.length; i < l; i += 3 ) {
 
-										skip = 0;
+					array.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );
 
-										while ( indexedFace.length >= 3 && skip < ( indexedFace.length - 2 ) ) {
+				}
 
-											var i1 = indexedFace[ 0 ];
-											var i2 = indexedFace[ skip + ( ccw ? 1 : 2 ) ];
-											var i3 = indexedFace[ skip + ( ccw ? 2 : 1 ) ];
+				return array;
 
-											triangulatedIndexArray.push( i1, i2, i3 );
+			}
 
-											skip ++;
+			/**
+			 * Vertically paints the faces interpolating between the
+			 * specified colors at the specified angels. This is used for the Background
+			 * node, but could be applied to other nodes with multiple faces as well.
+			 *
+			 * When used with the Background node, default is directionIsDown is true if
+			 * interpolating the skyColor down from the Zenith. When interpolationg up from
+			 * the Nadir i.e. interpolating the groundColor, the directionIsDown is false.
+			 *
+			 * The first angle is never specified, it is the Zenith (0 rad). Angles are specified
+			 * in radians. The geometry is thought a sphere, but could be anything. The color interpolation
+			 * is linear along the Y axis in any case.
+			 *
+			 * You must specify one more color than you have angles at the beginning of the colors array.
+			 * This is the color of the Zenith (the top of the shape).
+			 *
+			 * @param {BufferGeometry} geometry
+			 * @param {number} radius
+			 * @param {array} angles
+			 * @param {array} colors
+			 * @param {boolean} topDown - Whether to work top down or bottom up.
+			 */
+			function paintFaces( geometry, radius, angles, colors, topDown ) {
 
-										}
+				var direction = ( topDown === true ) ? 1 : - 1;
 
-									}
+				var coord = [], A = {}, B = {}, applyColor = false;
 
-								}
+				for ( var k = 0; k < angles.length; k ++ ) {
 
-								return triangulatedIndexArray;
+					// push the vector at which the color changes
 
-							}
+					var vec = {
+						x: direction * ( Math.cos( angles[ k ] ) * radius ),
+						y: direction * ( Math.sin( angles[ k ] ) * radius )
+					};
 
-							var positionIndexes = data.coordIndex ? triangulateIndexArray( data.coordIndex, data.ccw ) : [];
-							var normalIndexes = data.normalIndex ? triangulateIndexArray( data.normalIndex, data.ccw ) : positionIndexes;
-							var colorIndexes = data.colorIndex ? triangulateIndexArray( data.colorIndex, data.ccw, data.colorPerVertex ) : [];
-							var uvIndexes = data.texCoordIndex ? triangulateIndexArray( data.texCoordIndex, data.ccw ) : positionIndexes;
+					coord.push( vec );
 
-							var newIndexes = [];
-							var newPositions = [];
-							var newNormals = [];
-							var newColors = [];
-							var newUvs = [];
+				}
 
-							// if any other index array does not match the coordinate indexes, split any points that differ
+				var index = geometry.index;
+				var positionAttribute = geometry.attributes.position;
+				var colorAttribute = new BufferAttribute( new Float32Array( geometry.attributes.position.count * 3 ), 3 );
 
-							var pointMap = Object.create( null );
+				var position = new Vector3();
+				var color = new Color();
 
-							for ( i = 0; i < positionIndexes.length; i ++ ) {
+				for ( var i = 0; i < index.count; i ++ ) {
 
-								var pointAttributes = [];
+					var vertexIndex = index.getX( i );
 
-								var positionIndex = positionIndexes[ i ];
-								var normalIndex = normalIndexes[ i ];
-								var colorIndex = colorIndexes[ i ];
-								var uvIndex = uvIndexes[ i ];
+					position.fromBufferAttribute( positionAttribute, vertexIndex );
 
-								var base = 10; // which base to use to represent each value
+					for ( var j = 0; j < colors.length; j ++ ) {
 
-								pointAttributes.push( positionIndex.toString( base ) );
+						// linear interpolation between aColor and bColor, calculate proportion
+						// A is previous point (angle)
 
-								if ( normalIndex !== undefined ) {
+						if ( j === 0 ) {
 
-									pointAttributes.push( normalIndex.toString( base ) );
+							A.x = 0;
+							A.y = ( topDown === true ) ? radius : - 1 * radius;
 
-								}
+						} else {
 
-								if ( colorIndex !== undefined ) {
+							A.x = coord[ j - 1 ].x;
+							A.y = coord[ j - 1 ].y;
 
-									pointAttributes.push( colorIndex.toString( base ) );
+						}
 
-								}
+						// B is current point (angle)
 
-								if ( uvIndex !== undefined ) {
+						B = coord[ j ];
 
-									pointAttributes.push( uvIndex.toString( base ) );
+						if ( B !== undefined ) {
 
-								}
+							// p has to be between the points A and B which we interpolate
 
-								var pointId = pointAttributes.join( ',' );
-								var newIndex = pointMap[ pointId ];
+							applyColor = ( topDown === true ) ? ( position.y <= A.y && position.y > B.y ) : ( position.y >= A.y && position.y < B.y );
 
-								if ( newIndex === undefined ) {
+							if ( applyColor === true ) {
 
-									newIndex = newPositions.length / 3;
-									pointMap[ pointId ] = newIndex;
+								var aColor = colors[ j ];
+								var bColor = colors[ j + 1 ];
 
-									newPositions.push(
-										positions[ positionIndex * 3 ],
-										positions[ positionIndex * 3 + 1 ],
-										positions[ positionIndex * 3 + 2 ]
-									);
+								// below is simple linear interpolation
 
-									if ( normalIndex !== undefined && normals.length > 0 ) {
+								var t = Math.abs( position.y - A.y ) / ( A.y - B.y );
 
-										newNormals.push(
-											normals[ normalIndex * 3 ],
-											normals[ normalIndex * 3 + 1 ],
-											normals[ normalIndex * 3 + 2 ]
-										);
+								// to make it faster, you can only calculate this if the y coord changes, the color is the same for points with the same y
 
-									}
+								color.copy( aColor ).lerp( bColor, t );
 
-									if ( colorIndex !== undefined && colors.length > 0 ) {
+								colorAttribute.setXYZ( vertexIndex, color.r, color.g, color.b );
 
-										newColors.push(
-											colors[ colorIndex * 3 ],
-											colors[ colorIndex * 3 + 1 ],
-											colors[ colorIndex * 3 + 2 ]
-										);
+							} else {
 
-									}
+								var colorIndex = ( topDown === true ) ? colors.length - 1 : 0;
+								var c = colors[ colorIndex ];
+								colorAttribute.setXYZ( vertexIndex, c.r, c.g, c.b );
 
-									if ( uvIndex !== undefined && uvs.length > 0 ) {
+							}
 
-										newUvs.push(
-											uvs[ uvIndex * 2 ],
-											uvs[ uvIndex * 2 + 1 ]
-										);
+						}
 
-									}
+					}
 
-								}
+				}
 
-								newIndexes.push( newIndex );
+				geometry.addAttribute( 'color', colorAttribute );
 
-							}
+			}
 
-							positions = newPositions;
-							normals = newNormals;
-							colors = newColors;
-							uvs = newUvs;
+			//
 
-							geometry.setIndex( newIndexes );
+			var textureLoader = new TextureLoader( this.manager );
+			textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 
-						} else {
+			// create JSON representing the tree structure of the VRML asset
 
-							// do not add dummy mesh to the scene
+			var tree = generateVRMLTree( data );
 
-							parent.parent.remove( parent );
+			// check version (only 2.0 is supported)
 
-						}
+			if ( tree.version.indexOf( 'V2.0' ) === - 1 ) {
 
-						if ( false === data.solid ) {
+				throw Error( 'THREE.VRMLLexer: Version of VRML asset not supported.' );
 
-							parent.material.side = DoubleSide;
+			}
 
-						}
+			// parse the tree structure to a three.js scene
 
-						// we need to store it on the geometry for use with defines
-						geometry.solid = data.solid;
+			var scene = parseTree( tree );
 
-						geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+			return scene;
 
-						if ( colors.length > 0 ) {
+		}
 
-							geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
+	};
 
-							parent.material.vertexColors = VertexColors;
+	function VRMLLexer( tokens ) {
 
-						}
+		this.lexer = new chevrotain.Lexer( tokens );
 
-						if ( uvs.length > 0 ) {
+	}
 
-							geometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+	VRMLLexer.prototype = {
 
-						}
+		constructor: VRMLLexer,
 
-						if ( normals.length > 0 ) {
+		lex: function ( inputText ) {
 
-							geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+			var lexingResult = this.lexer.tokenize( inputText );
 
-						} else {
+			if ( lexingResult.errors.length > 0 ) {
 
-							// convert geometry to non-indexed to get sharp normals
-							geometry = geometry.toNonIndexed();
-							geometry.computeVertexNormals();
+				console.error( lexingResult.errors );
 
-						}
+				throw Error( 'THREE.VRMLLexer: Lexing errors detected.' );
 
-						geometry.computeBoundingSphere();
+			}
 
-						// see if it's a define
-						if ( /DEF/.exec( data.string ) ) {
+			return lexingResult;
 
-							geometry.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
-							defines[ geometry.name ] = geometry;
+		}
 
-						}
+	};
 
-						parent.geometry = geometry;
+	function VRMLParser( tokenVocabulary ) {
 
-					}
+		chevrotain.Parser.call( this, tokenVocabulary );
 
-					return;
+		var $ = this;
 
-				} else if ( /appearance/.exec( data.string ) ) {
+		var Version = tokenVocabulary[ 'Version' ];
+		var LCurly = tokenVocabulary[ 'LCurly' ];
+		var RCurly = tokenVocabulary[ 'RCurly' ];
+		var LSquare = tokenVocabulary[ 'LSquare' ];
+		var RSquare = tokenVocabulary[ 'RSquare' ];
+		var Identifier = tokenVocabulary[ 'Identifier' ];
+		var RouteIdentifier = tokenVocabulary[ 'RouteIdentifier' ];
+		var StringLiteral = tokenVocabulary[ 'StringLiteral' ];
+		var NumberLiteral = tokenVocabulary[ 'NumberLiteral' ];
+		var BooleanLiteral = tokenVocabulary[ 'BooleanLiteral' ];
+		var NullLiteral = tokenVocabulary[ 'NullLiteral' ];
+		var DEF = tokenVocabulary[ 'DEF' ];
+		var USE = tokenVocabulary[ 'USE' ];
+		var ROUTE = tokenVocabulary[ 'ROUTE' ];
+		var TO = tokenVocabulary[ 'TO' ];
+		var NodeName = tokenVocabulary[ 'NodeName' ];
 
-					for ( var i = 0; i < data.children.length; i ++ ) {
+		$.RULE( 'vrml', function () {
 
-						var child = data.children[ i ];
+			$.SUBRULE( $.version );
+			$.AT_LEAST_ONE( function () {
 
-						if ( child.nodeType === 'Material' ) {
+				$.SUBRULE( $.node );
 
-							var material = new MeshPhongMaterial();
+			} );
+			$.MANY( function () {
 
-							if ( child.diffuseColor !== undefined ) {
+				$.SUBRULE( $.route );
 
-								var d = child.diffuseColor;
+			} );
 
-								material.color.setRGB( d.r, d.g, d.b );
+		} );
 
-							}
+		$.RULE( 'version', function () {
 
-							if ( child.emissiveColor !== undefined ) {
+			$.CONSUME( Version );
 
-								var e = child.emissiveColor;
+		} );
 
-								material.emissive.setRGB( e.r, e.g, e.b );
+		$.RULE( 'node', function () {
 
-							}
+			$.OPTION( function () {
 
-							if ( child.specularColor !== undefined ) {
+				$.SUBRULE( $.def );
 
-								var s = child.specularColor;
+			} );
 
-								material.specular.setRGB( s.r, s.g, s.b );
+			$.CONSUME( NodeName );
+			$.CONSUME( LCurly );
+			$.MANY( function () {
 
-							}
+				$.SUBRULE( $.field );
 
-							if ( child.transparency !== undefined ) {
+			} );
+			$.CONSUME( RCurly );
 
-								var t = child.transparency;
+		} );
 
-								// transparency is opposite of opacity
-								material.opacity = Math.abs( 1 - t );
+		$.RULE( 'field', function () {
 
-								material.transparent = true;
+			$.CONSUME( Identifier );
 
-							}
+			$.OR2( [
+				{ ALT: function () {
 
-							if ( /DEF/.exec( data.string ) ) {
+					$.SUBRULE( $.singleFieldValue );
 
-								material.name = /DEF ([^\s]+)/.exec( data.string )[ 1 ];
+				} },
+				{ ALT: function () {
 
-								defines[ material.name ] = material;
+					$.SUBRULE( $.multiFieldValue );
 
-							}
+				} }
+			] );
 
-							parent.material = material;
+		} );
 
-						}
+		$.RULE( 'def', function () {
 
-						if ( child.nodeType === 'ImageTexture' ) {
+			$.CONSUME( DEF );
+			$.CONSUME( Identifier );
 
-							var textureName = /"([^"]+)"/.exec( child.children[ 0 ] );
+		} );
 
-							if ( textureName ) {
+		$.RULE( 'use', function () {
 
-								parent.material.name = textureName[ 1 ];
+			$.CONSUME( USE );
+			$.CONSUME( Identifier );
 
-								parent.material.map = textureLoader.load( textureName[ 1 ] );
+		} );
 
-							}
+		$.RULE( 'singleFieldValue', function () {
 
-						}
+			$.AT_LEAST_ONE( function () {
 
-					}
+				$.OR( [
+					{ ALT: function () {
 
-					return;
+						$.SUBRULE( $.node );
 
-				}
+					} },
+					{ ALT: function () {
 
-				for ( var i = 0, l = data.children.length; i < l; i ++ ) {
+						$.SUBRULE( $.use );
 
-					parseNode( data.children[ i ], object );
+					} },
+					{ ALT: function () {
 
-				}
+						$.CONSUME( StringLiteral );
 
-			}
+					} },
+					{ ALT: function () {
 
-			parseNode( getTree( lines ), scene );
+						$.CONSUME( NumberLiteral );
 
-		}
+					} },
+					{ ALT: function () {
 
-		var scene = new Scene();
+						$.CONSUME( BooleanLiteral );
 
-		var lines = data.split( '\n' );
+					} },
+					{ ALT: function () {
 
-		// some lines do not have breaks
+						$.CONSUME( NullLiteral );
 
-		for ( var i = lines.length - 1; i > 0; i -- ) {
+					} }
+				] );
 
-			// The # symbol indicates that all subsequent text, until the end of the line is a comment,
-			// and should be ignored. (see http://gun.teipir.gr/VRML-amgem/spec/part1/grammar.html)
-			lines[ i ] = lines[ i ].replace( /(#.*)/, '' );
 
-			var line = lines[ i ];
+			} );
 
-			// split lines with {..{ or {..[ - some have both
-			if ( /{.*[{\[]/.test( line ) ) {
+		} );
 
-				var parts = line.split( '{' ).join( '{\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+		$.RULE( 'multiFieldValue', function () {
 
-			} else if ( /\].*}/.test( line ) ) {
+			$.CONSUME( LSquare );
+			$.MANY( function () {
 
-				// split lines with ]..}
-				var parts = line.split( ']' ).join( ']\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+				$.OR( [
+					{ ALT: function () {
 
-			}
+						$.SUBRULE( $.node );
 
-			line = lines[ i ];
+					} },
+					{ ALT: function () {
 
-			if ( /}.*}/.test( line ) ) {
+						$.SUBRULE( $.use );
 
-				// split lines with }..}
-				var parts = line.split( '}' ).join( '}\n' ).split( '\n' );
-				parts.unshift( 1 );
-				parts.unshift( i );
-				lines.splice.apply( lines, parts );
+					} },
+					{ ALT: function () {
 
-			}
+						$.CONSUME( StringLiteral );
 
-			line = lines[ i ];
+					} },
+					{ ALT: function () {
 
-			if ( /^\b[^\s]+\b$/.test( line.trim() ) ) {
+						$.CONSUME( NumberLiteral );
 
-				// prevent lines with single words like "coord" or "geometry", see #12209
-				lines[ i + 1 ] = line + ' ' + lines[ i + 1 ].trim();
-				lines.splice( i, 1 );
+					} },
+					{ ALT: function () {
 
-			} else if ( ( line.indexOf( 'coord' ) > - 1 ) && ( line.indexOf( '[' ) < 0 ) && ( line.indexOf( '{' ) < 0 ) ) {
+						$.CONSUME( NullLiteral );
 
-				// force the parser to create Coordinate node for empty coords
-				// coord USE something -> coord USE something Coordinate {}
+					} }
+				] );
 
-				lines[ i ] += ' Coordinate {}';
+			} );
+			$.CONSUME( RSquare );
 
-			}
+		} );
 
-		}
+		$.RULE( 'route', function () {
 
-		var header = lines.shift();
+			$.CONSUME( ROUTE );
+			$.CONSUME( RouteIdentifier );
+			$.CONSUME( TO );
+			$.CONSUME2( RouteIdentifier );
 
-		if ( /V1.0/.exec( header ) ) {
+		} );
 
-			console.warn( 'THREE.VRMLLoader: V1.0 not supported yet.' );
+		this.performSelfAnalysis();
 
-		} else if ( /V2.0/.exec( header ) ) {
+	}
 
-			parseV2( lines, scene );
+	VRMLParser.prototype = Object.create( chevrotain.Parser.prototype );
+	VRMLParser.prototype.constructor = VRMLParser;
 
-		}
+	function Face( a, b, c ) {
 
-		return scene;
+		this.a = a;
+		this.b = b;
+		this.c = c;
+		this.normal = new Vector3();
 
 	}
 
-};
+	return VRMLLoader;
+
+} )();
 
 export { VRMLLoader };

+ 15 - 0
examples/jsm/loaders/VTKLoader.d.ts

@@ -0,0 +1,15 @@
+import {
+  BufferGeometry,
+  LoadingManager
+} from '../../../src/Three';
+
+export class VRMLLoader {
+  constructor(manager?: LoadingManager);
+  manager: LoadingManager;
+  path: string;
+
+  load(url: string, onLoad: (geometry: BufferGeometry) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setPath(path: string): this;
+
+  parse(data: ArrayBuffer | string, path: string): BufferGeometry;
+}

+ 1187 - 0
examples/jsm/loaders/VTKLoader.js

@@ -0,0 +1,1187 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ * @author Alex Pletzer
+ *
+ * Updated on 22.03.2017
+ * VTK header is now parsed and used to extract all the compressed data
+ * @author Andrii Iudin https://github.com/andreyyudin
+ * @author Paul Kibet Korir https://github.com/polarise
+ * @author Sriram Somasundharam https://github.com/raamssundar
+ */
+
+import {
+	BufferAttribute,
+	BufferGeometry,
+	DefaultLoadingManager,
+	EventDispatcher,
+	FileLoader,
+	Float32BufferAttribute,
+	LoaderUtils
+} from "../../../build/three.module.js";
+import { Zlib } from "../libs/inflate.min.js";
+
+var VTKLoader = function ( manager ) {
+
+	this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;
+
+};
+
+Object.assign( VTKLoader.prototype, EventDispatcher.prototype, {
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( text ) );
+
+		}, onProgress, onError );
+
+	},
+
+	setPath: function ( value ) {
+
+		this.path = value;
+		return this;
+
+	},
+
+	parse: function ( data ) {
+
+		function parseASCII( data ) {
+
+			// connectivity of the triangles
+			var indices = [];
+
+			// triangles vertices
+			var positions = [];
+
+			// red, green, blue colors in the range 0 to 1
+			var colors = [];
+
+			// normal vector, one per vertex
+			var normals = [];
+
+			var result;
+
+			// pattern for reading vertices, 3 floats or integers
+			var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g;
+
+			// pattern for connectivity, an integer followed by any number of ints
+			// the first integer is the number of polygon nodes
+			var patConnectivity = /^(\d+)\s+([\s\d]*)/;
+
+			// indicates start of vertex data section
+			var patPOINTS = /^POINTS /;
+
+			// indicates start of polygon connectivity section
+			var patPOLYGONS = /^POLYGONS /;
+
+			// indicates start of triangle strips section
+			var patTRIANGLE_STRIPS = /^TRIANGLE_STRIPS /;
+
+			// POINT_DATA number_of_values
+			var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/;
+
+			// CELL_DATA number_of_polys
+			var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/;
+
+			// Start of color section
+			var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/;
+
+			// NORMALS Normals float
+			var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/;
+
+			var inPointsSection = false;
+			var inPolygonsSection = false;
+			var inTriangleStripSection = false;
+			var inPointDataSection = false;
+			var inCellDataSection = false;
+			var inColorSection = false;
+			var inNormalsSection = false;
+
+			var lines = data.split( '\n' );
+
+			for ( var i in lines ) {
+
+				var line = lines[ i ];
+
+				if ( line.indexOf( 'DATASET' ) === 0 ) {
+
+					var dataset = line.split( ' ' )[ 1 ];
+
+					if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
+
+				} else if ( inPointsSection ) {
+
+					// get the vertices
+					while ( ( result = pat3Floats.exec( line ) ) !== null ) {
+
+						var x = parseFloat( result[ 1 ] );
+						var y = parseFloat( result[ 2 ] );
+						var z = parseFloat( result[ 3 ] );
+						positions.push( x, y, z );
+
+					}
+
+				} else if ( inPolygonsSection ) {
+
+					if ( ( result = patConnectivity.exec( line ) ) !== null ) {
+
+						// numVertices i0 i1 i2 ...
+						var numVertices = parseInt( result[ 1 ] );
+						var inds = result[ 2 ].split( /\s+/ );
+
+						if ( numVertices >= 3 ) {
+
+							var i0 = parseInt( inds[ 0 ] );
+							var i1, i2;
+							var k = 1;
+							// split the polygon in numVertices - 2 triangles
+							for ( var j = 0; j < numVertices - 2; ++ j ) {
+
+								i1 = parseInt( inds[ k ] );
+								i2 = parseInt( inds[ k + 1 ] );
+								indices.push( i0, i1, i2 );
+								k ++;
+
+							}
+
+						}
+
+					}
+
+				} else if ( inTriangleStripSection ) {
+
+					if ( ( result = patConnectivity.exec( line ) ) !== null ) {
+
+						// numVertices i0 i1 i2 ...
+						var numVertices = parseInt( result[ 1 ] );
+						var inds = result[ 2 ].split( /\s+/ );
+
+						if ( numVertices >= 3 ) {
+
+							var i0, i1, i2;
+							// split the polygon in numVertices - 2 triangles
+							for ( var j = 0; j < numVertices - 2; j ++ ) {
+
+								if ( j % 2 === 1 ) {
+
+									i0 = parseInt( inds[ j ] );
+									i1 = parseInt( inds[ j + 2 ] );
+									i2 = parseInt( inds[ j + 1 ] );
+									indices.push( i0, i1, i2 );
+
+								} else {
+
+									i0 = parseInt( inds[ j ] );
+									i1 = parseInt( inds[ j + 1 ] );
+									i2 = parseInt( inds[ j + 2 ] );
+									indices.push( i0, i1, i2 );
+
+								}
+
+							}
+
+						}
+
+					}
+
+				} else if ( inPointDataSection || inCellDataSection ) {
+
+					if ( inColorSection ) {
+
+						// Get the colors
+
+						while ( ( result = pat3Floats.exec( line ) ) !== null ) {
+
+							var r = parseFloat( result[ 1 ] );
+							var g = parseFloat( result[ 2 ] );
+							var b = parseFloat( result[ 3 ] );
+							colors.push( r, g, b );
+
+						}
+
+					} else if ( inNormalsSection ) {
+
+						// Get the normal vectors
+
+						while ( ( result = pat3Floats.exec( line ) ) !== null ) {
+
+							var nx = parseFloat( result[ 1 ] );
+							var ny = parseFloat( result[ 2 ] );
+							var nz = parseFloat( result[ 3 ] );
+							normals.push( nx, ny, nz );
+
+						}
+
+					}
+
+				}
+
+				if ( patPOLYGONS.exec( line ) !== null ) {
+
+					inPolygonsSection = true;
+					inPointsSection = false;
+					inTriangleStripSection = false;
+
+				} else if ( patPOINTS.exec( line ) !== null ) {
+
+					inPolygonsSection = false;
+					inPointsSection = true;
+					inTriangleStripSection = false;
+
+				} else if ( patTRIANGLE_STRIPS.exec( line ) !== null ) {
+
+					inPolygonsSection = false;
+					inPointsSection = false;
+					inTriangleStripSection = true;
+
+				} else if ( patPOINT_DATA.exec( line ) !== null ) {
+
+					inPointDataSection = true;
+					inPointsSection = false;
+					inPolygonsSection = false;
+					inTriangleStripSection = false;
+
+				} else if ( patCELL_DATA.exec( line ) !== null ) {
+
+					inCellDataSection = true;
+					inPointsSection = false;
+					inPolygonsSection = false;
+					inTriangleStripSection = false;
+
+				} else if ( patCOLOR_SCALARS.exec( line ) !== null ) {
+
+					inColorSection = true;
+					inNormalsSection = false;
+					inPointsSection = false;
+					inPolygonsSection = false;
+					inTriangleStripSection = false;
+
+				} else if ( patNORMALS.exec( line ) !== null ) {
+
+					inNormalsSection = true;
+					inColorSection = false;
+					inPointsSection = false;
+					inPolygonsSection = false;
+					inTriangleStripSection = false;
+
+				}
+
+			}
+
+			var geometry = new BufferGeometry();
+			geometry.setIndex( indices );
+			geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
+
+			if ( normals.length === positions.length ) {
+
+				geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+
+			}
+
+			if ( colors.length !== indices.length ) {
+
+				// stagger
+
+				if ( colors.length === positions.length ) {
+
+					geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
+
+				}
+
+			} else {
+
+				// cell
+
+				geometry = geometry.toNonIndexed();
+				var numTriangles = geometry.attributes.position.count / 3;
+
+				if ( colors.length === ( numTriangles * 3 ) ) {
+
+					var newColors = [];
+
+					for ( var i = 0; i < numTriangles; i ++ ) {
+
+						var r = colors[ 3 * i + 0 ];
+						var g = colors[ 3 * i + 1 ];
+						var b = colors[ 3 * i + 2 ];
+
+						newColors.push( r, g, b );
+						newColors.push( r, g, b );
+						newColors.push( r, g, b );
+
+					}
+
+					geometry.addAttribute( 'color', new Float32BufferAttribute( newColors, 3 ) );
+
+				}
+
+			}
+
+			return geometry;
+
+		}
+
+		function parseBinary( data ) {
+
+			var count, pointIndex, i, numberOfPoints, s;
+			var buffer = new Uint8Array( data );
+			var dataView = new DataView( data );
+
+			// Points and normals, by default, are empty
+			var points = [];
+			var normals = [];
+			var indices = [];
+
+			// Going to make a big array of strings
+			var vtk = [];
+			var index = 0;
+
+			function findString( buffer, start ) {
+
+				var index = start;
+				var c = buffer[ index ];
+				var s = [];
+				while ( c !== 10 ) {
+
+					s.push( String.fromCharCode( c ) );
+					index ++;
+					c = buffer[ index ];
+
+				}
+
+				return { start: start,
+					end: index,
+					next: index + 1,
+					parsedString: s.join( '' ) };
+
+			}
+
+			var state, line;
+
+			while ( true ) {
+
+				// Get a string
+				state = findString( buffer, index );
+				line = state.parsedString;
+
+				if ( line.indexOf( 'DATASET' ) === 0 ) {
+
+					var dataset = line.split( ' ' )[ 1 ];
+
+					if ( dataset !== 'POLYDATA' ) throw new Error( 'Unsupported DATASET type: ' + dataset );
+
+				} else if ( line.indexOf( 'POINTS' ) === 0 ) {
+
+					vtk.push( line );
+					// Add the points
+					numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 );
+
+					// Each point is 3 4-byte floats
+					count = numberOfPoints * 4 * 3;
+
+					points = new Float32Array( numberOfPoints * 3 );
+
+					pointIndex = state.next;
+					for ( i = 0; i < numberOfPoints; i ++ ) {
+
+						points[ 3 * i ] = dataView.getFloat32( pointIndex, false );
+						points[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false );
+						points[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false );
+						pointIndex = pointIndex + 12;
+
+					}
+					// increment our next pointer
+					state.next = state.next + count + 1;
+
+				} else if ( line.indexOf( 'TRIANGLE_STRIPS' ) === 0 ) {
+
+					var numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 );
+					var size = parseInt( line.split( ' ' )[ 2 ], 10 );
+					// 4 byte integers
+					count = size * 4;
+
+					indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
+					var indicesIndex = 0;
+
+					pointIndex = state.next;
+					for ( i = 0; i < numberOfStrips; i ++ ) {
+
+						// For each strip, read the first value, then record that many more points
+						var indexCount = dataView.getInt32( pointIndex, false );
+						var strip = [];
+						pointIndex += 4;
+						for ( s = 0; s < indexCount; s ++ ) {
+
+							strip.push( dataView.getInt32( pointIndex, false ) );
+							pointIndex += 4;
+
+						}
+
+						// retrieves the n-2 triangles from the triangle strip
+						for ( var j = 0; j < indexCount - 2; j ++ ) {
+
+							if ( j % 2 ) {
+
+								indices[ indicesIndex ++ ] = strip[ j ];
+								indices[ indicesIndex ++ ] = strip[ j + 2 ];
+								indices[ indicesIndex ++ ] = strip[ j + 1 ];
+
+							} else {
+
+
+								indices[ indicesIndex ++ ] = strip[ j ];
+								indices[ indicesIndex ++ ] = strip[ j + 1 ];
+								indices[ indicesIndex ++ ] = strip[ j + 2 ];
+
+							}
+
+						}
+
+					}
+					// increment our next pointer
+					state.next = state.next + count + 1;
+
+				} else if ( line.indexOf( 'POLYGONS' ) === 0 ) {
+
+					var numberOfStrips = parseInt( line.split( ' ' )[ 1 ], 10 );
+					var size = parseInt( line.split( ' ' )[ 2 ], 10 );
+					// 4 byte integers
+					count = size * 4;
+
+					indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
+					var indicesIndex = 0;
+
+					pointIndex = state.next;
+					for ( i = 0; i < numberOfStrips; i ++ ) {
+
+						// For each strip, read the first value, then record that many more points
+						var indexCount = dataView.getInt32( pointIndex, false );
+						var strip = [];
+						pointIndex += 4;
+						for ( s = 0; s < indexCount; s ++ ) {
+
+							strip.push( dataView.getInt32( pointIndex, false ) );
+							pointIndex += 4;
+
+						}
+
+						// divide the polygon in n-2 triangle
+						for ( var j = 1; j < indexCount - 1; j ++ ) {
+
+							indices[ indicesIndex ++ ] = strip[ 0 ];
+							indices[ indicesIndex ++ ] = strip[ j ];
+							indices[ indicesIndex ++ ] = strip[ j + 1 ];
+
+						}
+
+					}
+					// increment our next pointer
+					state.next = state.next + count + 1;
+
+				} else if ( line.indexOf( 'POINT_DATA' ) === 0 ) {
+
+					numberOfPoints = parseInt( line.split( ' ' )[ 1 ], 10 );
+
+					// Grab the next line
+					state = findString( buffer, state.next );
+
+					// Now grab the binary data
+					count = numberOfPoints * 4 * 3;
+
+					normals = new Float32Array( numberOfPoints * 3 );
+					pointIndex = state.next;
+					for ( i = 0; i < numberOfPoints; i ++ ) {
+
+						normals[ 3 * i ] = dataView.getFloat32( pointIndex, false );
+						normals[ 3 * i + 1 ] = dataView.getFloat32( pointIndex + 4, false );
+						normals[ 3 * i + 2 ] = dataView.getFloat32( pointIndex + 8, false );
+						pointIndex += 12;
+
+					}
+
+					// Increment past our data
+					state.next = state.next + count;
+
+				}
+
+				// Increment index
+				index = state.next;
+
+				if ( index >= buffer.byteLength ) {
+
+					break;
+
+				}
+
+			}
+
+			var geometry = new BufferGeometry();
+			geometry.setIndex( new BufferAttribute( indices, 1 ) );
+			geometry.addAttribute( 'position', new BufferAttribute( points, 3 ) );
+
+			if ( normals.length === points.length ) {
+
+				geometry.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );
+
+			}
+
+			return geometry;
+
+		}
+
+		function Float32Concat( first, second ) {
+
+		    var firstLength = first.length, result = new Float32Array( firstLength + second.length );
+
+		    result.set( first );
+		    result.set( second, firstLength );
+
+		    return result;
+
+		}
+
+		function Int32Concat( first, second ) {
+
+		    var firstLength = first.length, result = new Int32Array( firstLength + second.length );
+
+		    result.set( first );
+		    result.set( second, firstLength );
+
+		    return result;
+
+		}
+
+		function parseXML( stringFile ) {
+
+			// Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json
+
+			function xmlToJson( xml ) {
+
+				// Create the return object
+				var obj = {};
+
+				if ( xml.nodeType === 1 ) { // element
+
+					// do attributes
+
+					if ( xml.attributes ) {
+
+						if ( xml.attributes.length > 0 ) {
+
+							obj[ 'attributes' ] = {};
+
+							for ( var j = 0; j < xml.attributes.length; j ++ ) {
+
+								var attribute = xml.attributes.item( j );
+								obj[ 'attributes' ][ attribute.nodeName ] = attribute.nodeValue.trim();
+
+							}
+
+						}
+
+					}
+
+				} else if ( xml.nodeType === 3 ) { // text
+
+					obj = xml.nodeValue.trim();
+
+				}
+
+				// do children
+				if ( xml.hasChildNodes() ) {
+
+					for ( var i = 0; i < xml.childNodes.length; i ++ ) {
+
+						var item = xml.childNodes.item( i );
+						var nodeName = item.nodeName;
+
+						if ( typeof obj[ nodeName ] === 'undefined' ) {
+
+							var tmp = xmlToJson( item );
+
+							if ( tmp !== '' ) obj[ nodeName ] = tmp;
+
+						} else {
+
+							if ( typeof obj[ nodeName ].push === 'undefined' ) {
+
+								var old = obj[ nodeName ];
+								obj[ nodeName ] = [ old ];
+
+							}
+
+							var tmp = xmlToJson( item );
+
+							if ( tmp !== '' ) obj[ nodeName ].push( tmp );
+
+						}
+
+					}
+
+				}
+
+				return obj;
+
+			}
+
+			// Taken from Base64-js
+			function Base64toByteArray( b64 ) {
+
+				var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array;
+				var i;
+				var lookup = [];
+				var revLookup = [];
+				var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+				var len = code.length;
+
+				for ( i = 0; i < len; i ++ ) {
+
+					lookup[ i ] = code[ i ];
+
+				}
+
+				for ( i = 0; i < len; ++ i ) {
+
+					revLookup[ code.charCodeAt( i ) ] = i;
+
+				}
+
+				revLookup[ '-'.charCodeAt( 0 ) ] = 62;
+				revLookup[ '_'.charCodeAt( 0 ) ] = 63;
+
+				var j, l, tmp, placeHolders, arr;
+				var len = b64.length;
+
+				if ( len % 4 > 0 ) {
+
+					throw new Error( 'Invalid string. Length must be a multiple of 4' );
+
+				}
+
+				placeHolders = b64[ len - 2 ] === '=' ? 2 : b64[ len - 1 ] === '=' ? 1 : 0;
+				arr = new Arr( len * 3 / 4 - placeHolders );
+				l = placeHolders > 0 ? len - 4 : len;
+
+				var L = 0;
+
+				for ( i = 0, j = 0; i < l; i += 4, j += 3 ) {
+
+					tmp = ( revLookup[ b64.charCodeAt( i ) ] << 18 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] << 12 ) | ( revLookup[ b64.charCodeAt( i + 2 ) ] << 6 ) | revLookup[ b64.charCodeAt( i + 3 ) ];
+					arr[ L ++ ] = ( tmp & 0xFF0000 ) >> 16;
+					arr[ L ++ ] = ( tmp & 0xFF00 ) >> 8;
+					arr[ L ++ ] = tmp & 0xFF;
+
+				}
+
+				if ( placeHolders === 2 ) {
+
+					tmp = ( revLookup[ b64.charCodeAt( i ) ] << 2 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] >> 4 );
+					arr[ L ++ ] = tmp & 0xFF;
+
+				} else if ( placeHolders === 1 ) {
+
+					tmp = ( revLookup[ b64.charCodeAt( i ) ] << 10 ) | ( revLookup[ b64.charCodeAt( i + 1 ) ] << 4 ) | ( revLookup[ b64.charCodeAt( i + 2 ) ] >> 2 );
+					arr[ L ++ ] = ( tmp >> 8 ) & 0xFF;
+					arr[ L ++ ] = tmp & 0xFF;
+
+				}
+
+				return arr;
+
+			}
+
+			function parseDataArray( ele, compressed ) {
+
+				var numBytes = 0;
+
+				if ( json.attributes.header_type === 'UInt64' ) {
+
+					numBytes = 8;
+
+				}	else if ( json.attributes.header_type === 'UInt32' ) {
+
+					numBytes = 4;
+
+				}
+
+
+				// Check the format
+				if ( ele.attributes.format === 'binary' && compressed ) {
+
+					var rawData, content, byteData, blocks, cSizeStart, headerSize, padding, dataOffsets, currentOffset;
+
+					if ( ele.attributes.type === 'Float32' ) {
+
+						var txt = new Float32Array( );
+
+					} else if ( ele.attributes.type === 'Int64' ) {
+
+						var txt = new Int32Array( );
+
+					}
+
+					// VTP data with the header has the following structure:
+					// [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA]
+					//
+					// Each token is an integer value whose type is specified by "header_type" at the top of the file (UInt32 if no type specified). The token meanings are:
+					// [#blocks] = Number of blocks
+					// [#u-size] = Block size before compression
+					// [#p-size] = Size of last partial block (zero if it not needed)
+					// [#c-size-i] = Size in bytes of block i after compression
+					//
+					// The [DATA] portion stores contiguously every block appended together. The offset from the beginning of the data section to the beginning of a block is
+					// computed by summing the compressed block sizes from preceding blocks according to the header.
+
+					rawData = ele[ '#text' ];
+
+					byteData = Base64toByteArray( rawData );
+
+					blocks = byteData[ 0 ];
+					for ( var i = 1; i < numBytes - 1; i ++ ) {
+
+						blocks = blocks | ( byteData[ i ] << ( i * numBytes ) );
+
+					}
+
+					headerSize = ( blocks + 3 ) * numBytes;
+					padding = ( ( headerSize % 3 ) > 0 ) ? 3 - ( headerSize % 3 ) : 0;
+					headerSize = headerSize + padding;
+
+					dataOffsets = [];
+					currentOffset = headerSize;
+					dataOffsets.push( currentOffset );
+
+					// Get the blocks sizes after the compression.
+					// There are three blocks before c-size-i, so we skip 3*numBytes
+					cSizeStart = 3 * numBytes;
+
+					for ( var i = 0; i < blocks; i ++ ) {
+
+						var currentBlockSize = byteData[ i * numBytes + cSizeStart ];
+
+						for ( var j = 1; j < numBytes - 1; j ++ ) {
+
+							// Each data point consists of 8 bytes regardless of the header type
+							currentBlockSize = currentBlockSize | ( byteData[ i * numBytes + cSizeStart + j ] << ( j * 8 ) );
+
+						}
+
+						currentOffset = currentOffset + currentBlockSize;
+						dataOffsets.push( currentOffset );
+
+					}
+
+					for ( var i = 0; i < dataOffsets.length - 1; i ++ ) {
+
+						var inflate = new Zlib.Inflate( byteData.slice( dataOffsets[ i ], dataOffsets[ i + 1 ] ), { resize: true, verify: true } ); // eslint-disable-line no-undef
+						content = inflate.decompress();
+						content = content.buffer;
+
+						if ( ele.attributes.type === 'Float32' ) {
+
+							content = new Float32Array( content );
+							txt = Float32Concat( txt, content );
+
+						} else if ( ele.attributes.type === 'Int64' ) {
+
+							content = new Int32Array( content );
+							txt = Int32Concat( txt, content );
+
+						}
+
+					}
+
+					delete ele[ '#text' ];
+
+					if ( ele.attributes.type === 'Int64' ) {
+
+						if ( ele.attributes.format === 'binary' ) {
+
+							txt = txt.filter( function ( el, idx ) {
+
+								if ( idx % 2 !== 1 ) return true;
+
+							} );
+
+						}
+
+					}
+
+				} else {
+
+					if ( ele.attributes.format === 'binary' && ! compressed ) {
+
+						var content = Base64toByteArray( ele[ '#text' ] );
+
+						//  VTP data for the uncompressed case has the following structure:
+						// [#bytes][DATA]
+						// where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it.
+						content = content.slice( numBytes ).buffer;
+
+					} else {
+
+						if ( ele[ '#text' ] ) {
+
+							var content = ele[ '#text' ].split( /\s+/ ).filter( function ( el ) {
+
+								if ( el !== '' ) return el;
+
+							} );
+
+						} else {
+
+							var content = new Int32Array( 0 ).buffer;
+
+						}
+
+					}
+
+					delete ele[ '#text' ];
+
+					// Get the content and optimize it
+					if ( ele.attributes.type === 'Float32' ) {
+
+						var txt = new Float32Array( content );
+
+					} else if ( ele.attributes.type === 'Int32' ) {
+
+						var txt = new Int32Array( content );
+
+					} else if ( ele.attributes.type === 'Int64' ) {
+
+						var txt = new Int32Array( content );
+
+						if ( ele.attributes.format === 'binary' ) {
+
+							txt = txt.filter( function ( el, idx ) {
+
+								if ( idx % 2 !== 1 ) return true;
+
+							} );
+
+						}
+
+					}
+
+				} // endif ( ele.attributes.format === 'binary' && compressed )
+
+				return txt;
+
+			}
+
+			// Main part
+			// Get Dom
+			var dom = null;
+
+			if ( window.DOMParser ) {
+
+				try {
+
+					dom = ( new DOMParser() ).parseFromString( stringFile, 'text/xml' );
+
+				} catch ( e ) {
+
+					dom = null;
+
+				}
+
+			} else if ( window.ActiveXObject ) {
+
+				try {
+
+					dom = new ActiveXObject( 'Microsoft.XMLDOM' ); // eslint-disable-line no-undef
+					dom.async = false;
+
+					if ( ! dom.loadXML( /* xml */ ) ) {
+
+						throw new Error( dom.parseError.reason + dom.parseError.srcText );
+
+					}
+
+				} catch ( e ) {
+
+					dom = null;
+
+				}
+
+			} else {
+
+				throw new Error( 'Cannot parse xml string!' );
+
+			}
+
+			// Get the doc
+			var doc = dom.documentElement;
+			// Convert to json
+			var json = xmlToJson( doc );
+			var points = [];
+			var normals = [];
+			var indices = [];
+
+			if ( json.PolyData ) {
+
+				var piece = json.PolyData.Piece;
+				var compressed = json.attributes.hasOwnProperty( 'compressor' );
+
+				// Can be optimized
+				// Loop through the sections
+				var sections = [ 'PointData', 'Points', 'Strips', 'Polys' ];// +['CellData', 'Verts', 'Lines'];
+				var sectionIndex = 0, numberOfSections = sections.length;
+
+				while ( sectionIndex < numberOfSections ) {
+
+					var section = piece[ sections[ sectionIndex ] ];
+
+					// If it has a DataArray in it
+
+					if ( section && section.DataArray ) {
+
+						// Depending on the number of DataArrays
+
+						if ( Object.prototype.toString.call( section.DataArray ) === '[object Array]' ) {
+
+							var arr = section.DataArray;
+
+						} else {
+
+							var arr = [ section.DataArray ];
+
+						}
+
+						var dataArrayIndex = 0, numberOfDataArrays = arr.length;
+
+						while ( dataArrayIndex < numberOfDataArrays ) {
+
+							// Parse the DataArray
+							if ( ( '#text' in arr[ dataArrayIndex ] ) && ( arr[ dataArrayIndex ][ '#text' ].length > 0 ) ) {
+
+								arr[ dataArrayIndex ].text = parseDataArray( arr[ dataArrayIndex ], compressed );
+
+							}
+
+							dataArrayIndex ++;
+
+						}
+
+						switch ( sections[ sectionIndex ] ) {
+
+							// if iti is point data
+							case 'PointData':
+
+								var numberOfPoints = parseInt( piece.attributes.NumberOfPoints );
+								var normalsName = section.attributes.Normals;
+
+								if ( numberOfPoints > 0 ) {
+
+									for ( var i = 0, len = arr.length; i < len; i ++ ) {
+
+										if ( normalsName === arr[ i ].attributes.Name ) {
+
+											var components = arr[ i ].attributes.NumberOfComponents;
+											normals = new Float32Array( numberOfPoints * components );
+											normals.set( arr[ i ].text, 0 );
+
+										}
+
+									}
+
+								}
+
+								break;
+
+							// if it is points
+							case 'Points':
+
+								var numberOfPoints = parseInt( piece.attributes.NumberOfPoints );
+
+								if ( numberOfPoints > 0 ) {
+
+									var components = section.DataArray.attributes.NumberOfComponents;
+									points = new Float32Array( numberOfPoints * components );
+									points.set( section.DataArray.text, 0 );
+
+								}
+
+								break;
+
+							// if it is strips
+							case 'Strips':
+
+								var numberOfStrips = parseInt( piece.attributes.NumberOfStrips );
+
+								if ( numberOfStrips > 0 ) {
+
+									var connectivity = new Int32Array( section.DataArray[ 0 ].text.length );
+									var offset = new Int32Array( section.DataArray[ 1 ].text.length );
+									connectivity.set( section.DataArray[ 0 ].text, 0 );
+									offset.set( section.DataArray[ 1 ].text, 0 );
+
+									var size = numberOfStrips + connectivity.length;
+									indices = new Uint32Array( 3 * size - 9 * numberOfStrips );
+
+									var indicesIndex = 0;
+
+									for ( var i = 0, len = numberOfStrips; i < len; i ++ ) {
+
+										var strip = [];
+
+										for ( var s = 0, len1 = offset[ i ], len0 = 0; s < len1 - len0; s ++ ) {
+
+											strip.push( connectivity[ s ] );
+
+											if ( i > 0 ) len0 = offset[ i - 1 ];
+
+										}
+
+										for ( var j = 0, len1 = offset[ i ], len0 = 0; j < len1 - len0 - 2; j ++ ) {
+
+											if ( j % 2 ) {
+
+												indices[ indicesIndex ++ ] = strip[ j ];
+												indices[ indicesIndex ++ ] = strip[ j + 2 ];
+												indices[ indicesIndex ++ ] = strip[ j + 1 ];
+
+											} else {
+
+												indices[ indicesIndex ++ ] = strip[ j ];
+												indices[ indicesIndex ++ ] = strip[ j + 1 ];
+												indices[ indicesIndex ++ ] = strip[ j + 2 ];
+
+											}
+
+											if ( i > 0 ) len0 = offset[ i - 1 ];
+
+										}
+
+									}
+
+								}
+
+								break;
+
+							// if it is polys
+							case 'Polys':
+
+								var numberOfPolys = parseInt( piece.attributes.NumberOfPolys );
+
+								if ( numberOfPolys > 0 ) {
+
+									var connectivity = new Int32Array( section.DataArray[ 0 ].text.length );
+									var offset = new Int32Array( section.DataArray[ 1 ].text.length );
+									connectivity.set( section.DataArray[ 0 ].text, 0 );
+									offset.set( section.DataArray[ 1 ].text, 0 );
+
+									var size = numberOfPolys + connectivity.length;
+									indices = new Uint32Array( 3 * size - 9 * numberOfPolys );
+									var indicesIndex = 0, connectivityIndex = 0;
+									var i = 0, len = numberOfPolys, len0 = 0;
+
+									while ( i < len ) {
+
+										var poly = [];
+										var s = 0, len1 = offset[ i ];
+
+										while ( s < len1 - len0 ) {
+
+											poly.push( connectivity[ connectivityIndex ++ ] );
+											s ++;
+
+										}
+
+										var j = 1;
+
+										while ( j < len1 - len0 - 1 ) {
+
+											indices[ indicesIndex ++ ] = poly[ 0 ];
+											indices[ indicesIndex ++ ] = poly[ j ];
+											indices[ indicesIndex ++ ] = poly[ j + 1 ];
+											j ++;
+
+										}
+
+										i ++;
+										len0 = offset[ i - 1 ];
+
+									}
+
+								}
+
+								break;
+
+							default:
+								break;
+
+						}
+
+					}
+
+					sectionIndex ++;
+
+				}
+
+				var geometry = new BufferGeometry();
+				geometry.setIndex( new BufferAttribute( indices, 1 ) );
+				geometry.addAttribute( 'position', new BufferAttribute( points, 3 ) );
+
+				if ( normals.length === points.length ) {
+
+					geometry.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );
+
+				}
+
+				return geometry;
+
+			} else {
+
+				throw new Error( 'Unsupported DATASET type' );
+
+			}
+
+		}
+
+		function getStringFile( data ) {
+
+			var stringFile = '';
+			var charArray = new Uint8Array( data );
+			var i = 0;
+			var len = charArray.length;
+
+			while ( len -- ) {
+
+				stringFile += String.fromCharCode( charArray[ i ++ ] );
+
+			}
+
+			return stringFile;
+
+		}
+
+		// get the 5 first lines of the files to check if there is the key word binary
+		var meta = LoaderUtils.decodeText( new Uint8Array( data, 0, 250 ) ).split( '\n' );
+
+		if ( meta[ 0 ].indexOf( 'xml' ) !== - 1 ) {
+
+			return parseXML( getStringFile( data ) );
+
+		} else if ( meta[ 2 ].includes( 'ASCII' ) ) {
+
+			return parseASCII( getStringFile( data ) );
+
+		} else {
+
+			return parseBinary( data );
+
+		}
+
+	}
+
+} );
+
+export { VTKLoader };

+ 23 - 0
examples/jsm/loaders/XLoader.d.ts

@@ -0,0 +1,23 @@
+import {
+  Mesh,
+  LoadingManager
+} from '../../../src/Three';
+
+export interface XResult {
+  animations: object[];
+  models: Mesh[];
+}
+
+export class VRMLLoader {
+  constructor(manager?: LoadingManager);
+  crossOrigin: string;
+  manager: LoadingManager;
+  path: string;
+  resourcePath: string;
+
+  load(url: string, onLoad: (object: object) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void;
+  setCrossOrigin(path: string): this;
+  parse(data: ArrayBuffer | string, onLoad: (object: object) => void): object;
+  setPath(path: string): this;
+  setResourcePath(path: string): this;
+}

+ 1696 - 0
examples/jsm/loaders/XLoader.js

@@ -0,0 +1,1696 @@
+/**
+ * @author adrs2002 / https://github.com/adrs2002
+ */
+
+import {
+	AnimationClip,
+	AnimationMixer,
+	Bone,
+	BufferGeometry,
+	DefaultLoadingManager,
+	FileLoader,
+	Float32BufferAttribute,
+	FrontSide,
+	LoaderUtils,
+	Matrix4,
+	Mesh,
+	MeshPhongMaterial,
+	Quaternion,
+	Skeleton,
+	SkinnedMesh,
+	TextureLoader,
+	Uint16BufferAttribute,
+	Vector2,
+	Vector3
+} from "../../../build/three.module.js";
+
+var XLoader = ( function () {
+
+	var classCallCheck = function ( instance, Constructor ) {
+
+		if ( ! ( instance instanceof Constructor ) ) {
+
+			throw new TypeError( "Cannot call a class as a function" );
+
+		}
+
+	};
+
+	var createClass = function () {
+
+		function defineProperties( target, props ) {
+
+			for ( var i = 0; i < props.length; i ++ ) {
+
+				var descriptor = props[ i ];
+				descriptor.enumerable = descriptor.enumerable || false;
+				descriptor.configurable = true;
+				if ( "value" in descriptor ) descriptor.writable = true;
+				Object.defineProperty( target, descriptor.key, descriptor );
+
+			}
+
+		}
+
+		return function ( Constructor, protoProps, staticProps ) {
+
+			if ( protoProps ) defineProperties( Constructor.prototype, protoProps );
+			if ( staticProps ) defineProperties( Constructor, staticProps );
+			return Constructor;
+
+		};
+
+	}();
+
+	var XboneInf = function XboneInf() {
+
+		classCallCheck( this, XboneInf );
+
+		this.boneName = "";
+		this.BoneIndex = 0;
+		this.Indeces = [];
+		this.Weights = [];
+		this.initMatrix = null;
+		this.OffsetMatrix = null;
+
+	};
+
+	var XAnimationInfo = function XAnimationInfo() {
+
+		classCallCheck( this, XAnimationInfo );
+
+		this.animeName = "";
+		this.boneName = "";
+		this.targetBone = null;
+		this.keyType = 4;
+		this.frameStartLv = 0;
+		this.keyFrames = [];
+		this.InverseMx = null;
+
+	};
+
+	var XAnimationObj = function () {
+
+		function XAnimationObj( _flags ) {
+
+			classCallCheck( this, XAnimationObj );
+
+			this.fps = 30;
+			this.name = 'xanimation';
+			this.length = 0;
+			this.hierarchy = [];
+			this.putFlags = _flags;
+			if ( this.putFlags.putPos === undefined ) {
+
+				this.putFlags.putPos = true;
+
+			}
+			if ( this.putFlags.putRot === undefined ) {
+
+				this.putFlags.putRot = true;
+
+			}
+			if ( this.putFlags.putScl === undefined ) {
+
+				this.putFlags.putScl = true;
+
+			}
+
+		}
+
+		createClass( XAnimationObj, [ {
+			key: "make",
+			value: function make( XAnimationInfoArray ) {
+
+				for ( var i = 0; i < XAnimationInfoArray.length; i ++ ) {
+
+					this.hierarchy.push( this.makeBonekeys( XAnimationInfoArray[ i ] ) );
+
+				}
+				this.length = this.hierarchy[ 0 ].keys[ this.hierarchy[ 0 ].keys.length - 1 ].time;
+
+			}
+		}, {
+			key: "clone",
+			value: function clone() {
+
+				return Object.assign( {}, this );
+
+			}
+		}, {
+			key: "makeBonekeys",
+			value: function makeBonekeys( XAnimationInfo ) {
+
+				var refObj = {};
+				refObj.name = XAnimationInfo.boneName;
+				refObj.parent = "";
+				refObj.keys = this.keyFrameRefactor( XAnimationInfo );
+				refObj.copy = function () {
+
+					return Object.assign( {}, this );
+
+				};
+				return refObj;
+
+			}
+		}, {
+			key: "keyFrameRefactor",
+			value: function keyFrameRefactor( XAnimationInfo ) {
+
+				var keys = [];
+				for ( var i = 0; i < XAnimationInfo.keyFrames.length; i ++ ) {
+
+					var keyframe = {};
+					keyframe.time = XAnimationInfo.keyFrames[ i ].time * this.fps;
+					if ( XAnimationInfo.keyFrames[ i ].pos && this.putFlags.putPos ) {
+
+						keyframe.pos = XAnimationInfo.keyFrames[ i ].pos;
+
+					}
+					if ( XAnimationInfo.keyFrames[ i ].rot && this.putFlags.putRot ) {
+
+						keyframe.rot = XAnimationInfo.keyFrames[ i ].rot;
+
+					}
+					if ( XAnimationInfo.keyFrames[ i ].scl && this.putFlags.putScl ) {
+
+						keyframe.scl = XAnimationInfo.keyFrames[ i ].scl;
+
+					}
+					if ( XAnimationInfo.keyFrames[ i ].matrix ) {
+
+						keyframe.matrix = XAnimationInfo.keyFrames[ i ].matrix;
+						if ( this.putFlags.putPos ) {
+
+							keyframe.pos = new Vector3().setFromMatrixPosition( keyframe.matrix );
+
+						}
+						if ( this.putFlags.putRot ) {
+
+							keyframe.rot = new Quaternion().setFromRotationMatrix( keyframe.matrix );
+
+						}
+						if ( this.putFlags.putScl ) {
+
+							keyframe.scl = new Vector3().setFromMatrixScale( keyframe.matrix );
+
+						}
+
+					}
+					keys.push( keyframe );
+
+				}
+				return keys;
+
+			}
+		} ] );
+		return XAnimationObj;
+
+	}();
+
+	var XKeyFrameInfo = function XKeyFrameInfo() {
+
+		classCallCheck( this, XKeyFrameInfo );
+
+		this.index = 0;
+		this.Frame = 0;
+		this.time = 0.0;
+		this.matrix = null;
+
+	};
+
+	var XLoader = function () {
+
+		function XLoader( manager ) {
+
+			classCallCheck( this, XLoader );
+
+			this.debug = false;
+			this.manager = manager !== undefined ? manager : new DefaultLoadingManager();
+			this.texloader = new TextureLoader( this.manager );
+			this.url = "";
+			this._putMatLength = 0;
+			this._nowMat = null;
+			this._nowFrameName = "";
+			this.frameHierarchie = [];
+			this.Hierarchies = {};
+			this.HieStack = [];
+			this._currentObject = {};
+			this._currentFrame = {};
+			this._data = null;
+			this.onLoad = null;
+			this.IsUvYReverse = true;
+			this.Meshes = [];
+			this.animations = [];
+			this.animTicksPerSecond = 30;
+			this._currentGeo = null;
+			this._currentAnime = null;
+			this._currentAnimeFrames = null;
+
+		}
+
+		createClass( XLoader, [ {
+			key: 'crossOrigin',
+			value: 'anonymous'
+		}, {
+			key: '_setArgOption',
+			value: function _setArgOption( _arg ) {
+
+				var _start = arguments.length > 1 && arguments[ 1 ] !== undefined ? arguments[ 1 ] : 0;
+
+				if ( ! _arg ) {
+
+					return;
+
+				}
+				for ( var i = _start; i < _arg.length; i ++ ) {
+
+					switch ( i ) {
+
+						case 0:
+							this.url = _arg[ i ];
+							break;
+						case 1:
+							this.options = _arg[ i ];
+							break;
+
+					}
+
+				}
+				if ( this.options === undefined ) {
+
+					this.options = {};
+
+				}
+
+			}
+		}, {
+			key: 'load',
+			value: function load( _arg, onLoad, onProgress, onError ) {
+
+				var _this = this;
+
+				this._setArgOption( _arg );
+				var loader = new FileLoader( this.manager );
+				loader.setPath( this.path );
+				loader.setResponseType( 'arraybuffer' );
+				loader.load( this.url, function ( response ) {
+
+					_this.parse( response, onLoad );
+
+				}, onProgress, onError );
+
+			}
+		}, {
+			key: 'setCrossOrigin',
+			value: function setCrossOrigin( value ) {
+
+				this.crossOrigin = value;
+				return this;
+
+			}
+		}, {
+			key: 'setPath',
+			value: function setPath( value ) {
+
+				this.path = value;
+				return this;
+
+			}
+		}, {
+			key: 'setResourcePath',
+			value: function setResourcePath( value ) {
+
+				this.resourcePath = value;
+				return this;
+
+			}
+		}, {
+			key: '_readLine',
+			value: function _readLine( line ) {
+
+				var readed = 0;
+				while ( true ) {
+
+					var find = - 1;
+					find = line.indexOf( '//', readed );
+					if ( find === - 1 ) {
+
+						find = line.indexOf( '#', readed );
+
+					}
+					if ( find > - 1 && find < 2 ) {
+
+						var foundNewLine = - 1;
+						foundNewLine = line.indexOf( "\r\n", readed );
+						if ( foundNewLine > 0 ) {
+
+							readed = foundNewLine + 2;
+
+						} else {
+
+							foundNewLine = line.indexOf( "\r", readed );
+							if ( foundNewLine > 0 ) {
+
+								readed = foundNewLine + 1;
+
+							} else {
+
+								readed = line.indexOf( "\n", readed ) + 1;
+
+							}
+
+						}
+
+					} else {
+
+						break;
+
+					}
+
+				}
+				return line.substr( readed );
+
+			}
+		}, {
+			key: '_readLine',
+			value: function _readLine( line ) {
+
+				var readed = 0;
+				while ( true ) {
+
+					var find = - 1;
+					find = line.indexOf( '//', readed );
+					if ( find === - 1 ) {
+
+						find = line.indexOf( '#', readed );
+
+					}
+					if ( find > - 1 && find < 2 ) {
+
+						var foundNewLine = - 1;
+						foundNewLine = line.indexOf( "\r\n", readed );
+						if ( foundNewLine > 0 ) {
+
+							readed = foundNewLine + 2;
+
+						} else {
+
+							foundNewLine = line.indexOf( "\r", readed );
+							if ( foundNewLine > 0 ) {
+
+								readed = foundNewLine + 1;
+
+							} else {
+
+								readed = line.indexOf( "\n", readed ) + 1;
+
+							}
+
+						}
+
+					} else {
+
+						break;
+
+					}
+
+				}
+				return line.substr( readed );
+
+			}
+		}, {
+			key: '_isBinary',
+			value: function _isBinary( binData ) {
+
+				var reader = new DataView( binData );
+				var face_size = 32 / 8 * 3 + 32 / 8 * 3 * 3 + 16 / 8;
+				var n_faces = reader.getUint32( 80, true );
+				var expect = 80 + 32 / 8 + n_faces * face_size;
+				if ( expect === reader.byteLength ) {
+
+					return true;
+
+				}
+				var fileLength = reader.byteLength;
+				for ( var index = 0; index < fileLength; index ++ ) {
+
+					if ( reader.getUint8( index, false ) > 127 ) {
+
+						return true;
+
+					}
+
+				}
+				return false;
+
+			}
+		}, {
+			key: '_ensureBinary',
+			value: function _ensureBinary( buf ) {
+
+				if ( typeof buf === "string" ) {
+
+					var array_buffer = new Uint8Array( buf.length );
+					for ( var i = 0; i < buf.length; i ++ ) {
+
+						array_buffer[ i ] = buf.charCodeAt( i ) & 0xff;
+
+					}
+					return array_buffer.buffer || array_buffer;
+
+				} else {
+
+					return buf;
+
+				}
+
+			}
+		}, {
+			key: '_ensureString',
+			value: function _ensureString( buf ) {
+
+				if ( typeof buf !== "string" ) {
+
+					return LoaderUtils.decodeText( new Uint8Array( buf ) );
+
+				} else {
+
+					return buf;
+
+				}
+
+			}
+		}, {
+			key: 'parse',
+			value: function _parse( data, onLoad ) {
+
+				var binData = this._ensureBinary( data );
+				this._data = this._ensureString( data );
+				this.onLoad = onLoad;
+				return this._isBinary( binData ) ? this._parseBinary( binData ) : this._parseASCII();
+
+			}
+		}, {
+			key: '_parseBinary',
+			value: function _parseBinary( data ) {
+
+				return this._parseASCII( LoaderUtils.decodeText( new Uint8Array( data ) ) );
+
+			}
+		}, {
+			key: '_parseASCII',
+			value: function _parseASCII() {
+
+				var path;
+
+				if ( this.resourcePath !== undefined ) {
+
+					path = this.resourcePath;
+
+				} else if ( this.path !== undefined ) {
+
+					path = this.path;
+
+				} else {
+
+					path = LoaderUtils.extractUrlBase( this.url );
+
+				}
+
+				this.texloader.setPath( path ).setCrossOrigin( this.crossOrigin );
+
+				var endRead = 16;
+				this.Hierarchies.children = [];
+				this._hierarchieParse( this.Hierarchies, endRead );
+				this._changeRoot();
+				this._currentObject = this.Hierarchies.children.shift();
+				this._mainloop();
+
+			}
+		}, {
+			key: '_hierarchieParse',
+			value: function _hierarchieParse( _parent, _end ) {
+
+				var endRead = _end;
+				while ( true ) {
+
+					var find1 = this._data.indexOf( '{', endRead ) + 1;
+					var findEnd = this._data.indexOf( '}', endRead );
+					var findNext = this._data.indexOf( '{', find1 ) + 1;
+					if ( find1 > 0 && findEnd > find1 ) {
+
+						var _currentObject = {};
+						_currentObject.children = [];
+						var nameData = this._readLine( this._data.substr( endRead, find1 - endRead - 1 ) ).trim();
+						var word = nameData.split( / /g );
+						if ( word.length > 0 ) {
+
+							_currentObject.type = word[ 0 ];
+							if ( word.length >= 2 ) {
+
+								_currentObject.name = word[ 1 ];
+
+							} else {
+
+								_currentObject.name = word[ 0 ] + this.Hierarchies.children.length;
+
+							}
+
+						} else {
+
+							_currentObject.name = nameData;
+							_currentObject.type = "";
+
+						}
+						if ( _currentObject.type === "Animation" ) {
+
+							_currentObject.data = this._data.substr( findNext, findEnd - findNext ).trim();
+							var refs = this._hierarchieParse( _currentObject, findEnd + 1 );
+							endRead = refs.end;
+							_currentObject.children = refs.parent.children;
+
+						} else {
+
+							var DataEnder = this._data.lastIndexOf( ';', findNext > 0 ? Math.min( findNext, findEnd ) : findEnd );
+							_currentObject.data = this._data.substr( find1, DataEnder - find1 ).trim();
+							if ( findNext <= 0 || findEnd < findNext ) {
+
+								endRead = findEnd + 1;
+
+							} else {
+
+								var nextStart = Math.max( DataEnder + 1, find1 );
+								var _refs = this._hierarchieParse( _currentObject, nextStart );
+								endRead = _refs.end;
+								_currentObject.children = _refs.parent.children;
+
+							}
+
+						}
+						_currentObject.parent = _parent;
+						if ( _currentObject.type != "template" ) {
+
+							_parent.children.push( _currentObject );
+
+						}
+
+					} else {
+
+						endRead = find1 === - 1 ? this._data.length : findEnd + 1;
+						break;
+
+					}
+
+				}
+				return {
+					parent: _parent,
+					end: endRead
+				};
+
+			}
+		}, {
+			key: '_mainloop',
+			value: function _mainloop() {
+
+				var _this2 = this;
+
+				this._mainProc();
+				if ( this._currentObject.parent || this._currentObject.children.length > 0 || ! this._currentObject.worked ) {
+
+					setTimeout( function () {
+
+						_this2._mainloop();
+
+					}, 1 );
+
+				} else {
+
+					setTimeout( function () {
+
+						_this2.onLoad( {
+							models: _this2.Meshes,
+							animations: _this2.animations
+						} );
+
+					}, 1 );
+
+				}
+
+			}
+		}, {
+			key: '_mainProc',
+			value: function _mainProc() {
+
+				var breakFlag = false;
+				while ( true ) {
+
+					if ( ! this._currentObject.worked ) {
+
+						switch ( this._currentObject.type ) {
+
+							case "template":
+								break;
+							case "AnimTicksPerSecond":
+								this.animTicksPerSecond = parseInt( this._currentObject.data );
+								break;
+							case "Frame":
+								this._setFrame();
+								break;
+							case "FrameTransformMatrix":
+								this._setFrameTransformMatrix();
+								break;
+							case "Mesh":
+								this._changeRoot();
+								this._currentGeo = {};
+								this._currentGeo.name = this._currentObject.name.trim();
+								this._currentGeo.parentName = this._getParentName( this._currentObject ).trim();
+								this._currentGeo.VertexSetedBoneCount = [];
+								this._currentGeo.GeometryData = {
+									vertices: [],
+									normals: [],
+									uvs: [],
+									skinIndices: [],
+									skinWeights: [],
+									indices: [],
+									materialIndices: []
+								};
+								this._currentGeo.Materials = [];
+								this._currentGeo.normalVectors = [];
+								this._currentGeo.BoneInfs = [];
+								this._currentGeo.baseFrame = this._currentFrame;
+								this._makeBoneFrom_CurrentFrame();
+								this._readVertexDatas();
+								breakFlag = true;
+								break;
+							case "MeshNormals":
+								this._readVertexDatas();
+								break;
+							case "MeshTextureCoords":
+								this._setMeshTextureCoords();
+								break;
+							case "VertexDuplicationIndices":
+								break;
+							case "MeshMaterialList":
+								this._setMeshMaterialList();
+								break;
+							case "Material":
+								this._setMaterial();
+								break;
+							case "SkinWeights":
+								this._setSkinWeights();
+								break;
+							case "AnimationSet":
+								this._changeRoot();
+								this._currentAnime = {};
+								this._currentAnime.name = this._currentObject.name.trim();
+								this._currentAnime.AnimeFrames = [];
+								break;
+							case "Animation":
+								if ( this._currentAnimeFrames ) {
+
+									this._currentAnime.AnimeFrames.push( this._currentAnimeFrames );
+
+								}
+								this._currentAnimeFrames = new XAnimationInfo();
+								this._currentAnimeFrames.boneName = this._currentObject.data.trim();
+								break;
+							case "AnimationKey":
+								this._readAnimationKey();
+								breakFlag = true;
+								break;
+
+						}
+						this._currentObject.worked = true;
+
+					}
+					if ( this._currentObject.children.length > 0 ) {
+
+						this._currentObject = this._currentObject.children.shift();
+						if ( this.debug ) {
+
+							console.log( 'processing ' + this._currentObject.name );
+
+						}
+						if ( breakFlag ) break;
+
+					} else {
+
+						if ( this._currentObject.worked ) {
+
+							if ( this._currentObject.parent && ! this._currentObject.parent.parent ) {
+
+								this._changeRoot();
+
+							}
+
+						}
+						if ( this._currentObject.parent ) {
+
+							this._currentObject = this._currentObject.parent;
+
+						} else {
+
+							breakFlag = true;
+
+						}
+						if ( breakFlag ) break;
+
+					}
+
+				}
+				return;
+
+			}
+		}, {
+			key: '_changeRoot',
+			value: function _changeRoot() {
+
+				if ( this._currentGeo != null && this._currentGeo.name ) {
+
+					this._makeOutputGeometry();
+
+				}
+				this._currentGeo = {};
+				if ( this._currentAnime != null && this._currentAnime.name ) {
+
+					if ( this._currentAnimeFrames ) {
+
+						this._currentAnime.AnimeFrames.push( this._currentAnimeFrames );
+						this._currentAnimeFrames = null;
+
+					}
+					this._makeOutputAnimation();
+
+				}
+				this._currentAnime = {};
+
+			}
+		}, {
+			key: '_getParentName',
+			value: function _getParentName( _obj ) {
+
+				if ( _obj.parent ) {
+
+					if ( _obj.parent.name ) {
+
+						return _obj.parent.name;
+
+					} else {
+
+						return this._getParentName( _obj.parent );
+
+					}
+
+				} else {
+
+					return "";
+
+				}
+
+			}
+		}, {
+			key: '_setFrame',
+			value: function _setFrame() {
+
+				this._nowFrameName = this._currentObject.name.trim();
+				this._currentFrame = {};
+				this._currentFrame.name = this._nowFrameName;
+				this._currentFrame.children = [];
+				if ( this._currentObject.parent && this._currentObject.parent.name ) {
+
+					this._currentFrame.parentName = this._currentObject.parent.name;
+
+				}
+				this.frameHierarchie.push( this._nowFrameName );
+				this.HieStack[ this._nowFrameName ] = this._currentFrame;
+
+			}
+		}, {
+			key: '_setFrameTransformMatrix',
+			value: function _setFrameTransformMatrix() {
+
+				this._currentFrame.FrameTransformMatrix = new Matrix4();
+				var data = this._currentObject.data.split( "," );
+				this._ParseMatrixData( this._currentFrame.FrameTransformMatrix, data );
+				this._makeBoneFrom_CurrentFrame();
+
+			}
+		}, {
+			key: '_makeBoneFrom_CurrentFrame',
+			value: function _makeBoneFrom_CurrentFrame() {
+
+				if ( ! this._currentFrame.FrameTransformMatrix ) {
+
+					return;
+
+				}
+				var b = new Bone();
+				b.name = this._currentFrame.name;
+				b.applyMatrix( this._currentFrame.FrameTransformMatrix );
+				b.matrixWorld = b.matrix;
+				b.FrameTransformMatrix = this._currentFrame.FrameTransformMatrix;
+				this._currentFrame.putBone = b;
+				if ( this._currentFrame.parentName ) {
+
+					for ( var frame in this.HieStack ) {
+
+						if ( this.HieStack[ frame ].name === this._currentFrame.parentName ) {
+
+							this.HieStack[ frame ].putBone.add( this._currentFrame.putBone );
+
+						}
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_readVertexDatas',
+			value: function _readVertexDatas() {
+
+				var endRead = 0;
+				var mode = 0;
+				var mode_local = 0;
+				var maxLength = 0;
+				while ( true ) {
+
+					var changeMode = false;
+					if ( mode_local === 0 ) {
+
+						var refO = this._readInt1( endRead );
+						endRead = refO.endRead;
+						mode_local = 1;
+						maxLength = this._currentObject.data.indexOf( ';;', endRead ) + 1;
+						if ( maxLength <= 0 ) {
+
+							maxLength = this._currentObject.data.length;
+
+						}
+
+					} else {
+
+						var find = 0;
+						switch ( mode ) {
+
+							case 0:
+								find = this._currentObject.data.indexOf( ',', endRead ) + 1;
+								break;
+							case 1:
+								find = this._currentObject.data.indexOf( ';,', endRead ) + 1;
+								break;
+
+						}
+						if ( find === 0 || find > maxLength ) {
+
+							find = maxLength;
+							mode_local = 0;
+							changeMode = true;
+
+						}
+						switch ( this._currentObject.type ) {
+
+							case "Mesh":
+								switch ( mode ) {
+
+									case 0:
+										this._readVertex1( this._currentObject.data.substr( endRead, find - endRead ) );
+										break;
+									case 1:
+										this._readFace1( this._currentObject.data.substr( endRead, find - endRead ) );
+										break;
+
+								}
+								break;
+							case "MeshNormals":
+								switch ( mode ) {
+
+									case 0:
+										this._readNormalVector1( this._currentObject.data.substr( endRead, find - endRead ) );
+										break;
+
+								}
+								break;
+
+						}
+						endRead = find + 1;
+						if ( changeMode ) {
+
+							mode ++;
+
+						}
+
+					}
+					if ( endRead >= this._currentObject.data.length ) {
+
+						break;
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_readInt1',
+			value: function _readInt1( start ) {
+
+				var find = this._currentObject.data.indexOf( ';', start );
+				return {
+					refI: parseInt( this._currentObject.data.substr( start, find - start ) ),
+					endRead: find + 1
+				};
+
+			}
+		}, {
+			key: '_readVertex1',
+			value: function _readVertex1( line ) {
+
+				var data = this._readLine( line.trim() ).substr( 0, line.length - 2 ).split( ";" );
+				this._currentGeo.GeometryData.vertices.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 2 ] ) );
+				this._currentGeo.GeometryData.skinIndices.push( 0, 0, 0, 0 );
+				this._currentGeo.GeometryData.skinWeights.push( 1, 0, 0, 0 );
+				this._currentGeo.VertexSetedBoneCount.push( 0 );
+
+			}
+		}, {
+			key: '_readFace1',
+			value: function _readFace1( line ) {
+
+				var data = this._readLine( line.trim() ).substr( 2, line.length - 4 ).split( "," );
+				this._currentGeo.GeometryData.indices.push( parseInt( data[ 0 ], 10 ), parseInt( data[ 1 ], 10 ), parseInt( data[ 2 ], 10 ) );
+
+			}
+		}, {
+			key: '_readNormalVector1',
+			value: function _readNormalVector1( line ) {
+
+				var data = this._readLine( line.trim() ).substr( 0, line.length - 2 ).split( ";" );
+				this._currentGeo.GeometryData.normals.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 2 ] ) );
+
+			}
+		}, {
+			key: '_buildGeometry',
+			value: function _buildGeometry() {
+
+				var bufferGeometry = new BufferGeometry();
+				var position = [];
+				var normals = [];
+				var uvs = [];
+				var skinIndices = [];
+				var skinWeights = [];
+
+				//
+
+				var data = this._currentGeo.GeometryData;
+
+				for ( var i = 0, l = data.indices.length; i < l; i ++ ) {
+
+					var stride2 = data.indices[ i ] * 2;
+					var stride3 = data.indices[ i ] * 3;
+					var stride4 = data.indices[ i ] * 4;
+
+					position.push( data.vertices[ stride3 ], data.vertices[ stride3 + 1 ], data.vertices[ stride3 + 2 ] );
+					normals.push( data.normals[ stride3 ], data.normals[ stride3 + 1 ], data.normals[ stride3 + 2 ] );
+					skinIndices.push( data.skinIndices[ stride4 ], data.skinIndices[ stride4 + 1 ], data.skinIndices[ stride4 + 2 ], data.skinIndices[ stride4 + 3 ] );
+					skinWeights.push( data.skinWeights[ stride4 ], data.skinWeights[ stride4 + 1 ], data.skinWeights[ stride4 + 2 ], data.skinWeights[ stride4 + 3 ] );
+					uvs.push( data.uvs[ stride2 ], data.uvs[ stride2 + 1 ] );
+
+				}
+
+				//
+
+				bufferGeometry.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) );
+				bufferGeometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+				bufferGeometry.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+				bufferGeometry.addAttribute( 'skinIndex', new Uint16BufferAttribute( skinIndices, 4 ) );
+				bufferGeometry.addAttribute( 'skinWeight', new Float32BufferAttribute( skinWeights, 4 ) );
+
+				this._computeGroups( bufferGeometry, data.materialIndices );
+
+				return bufferGeometry;
+
+			}
+		}, {
+			key: '_computeGroups',
+			value: function _computeGroups( bufferGeometry, materialIndices ) {
+
+				var group;
+				var groups = [];
+				var materialIndex = undefined;
+
+				for ( var i = 0; i < materialIndices.length; i ++ ) {
+
+					var currentMaterialIndex = materialIndices[ i ];
+
+					if ( currentMaterialIndex !== materialIndex ) {
+
+						materialIndex = currentMaterialIndex;
+
+						if ( group !== undefined ) {
+
+							group.count = ( i * 3 ) - group.start;
+							groups.push( group );
+
+						}
+
+						group = {
+							start: i * 3,
+							materialIndex: materialIndex
+						};
+
+					}
+
+				}
+
+				if ( group !== undefined ) {
+
+					group.count = ( i * 3 ) - group.start;
+					groups.push( group );
+
+				}
+
+				bufferGeometry.groups = groups;
+
+			}
+		}, {
+			key: '_setMeshTextureCoords',
+			value: function _setMeshTextureCoords() {
+
+				var endRead = 0;
+				var mode = 0;
+				var mode_local = 0;
+				while ( true ) {
+
+					switch ( mode ) {
+
+						case 0:
+							if ( mode_local === 0 ) {
+
+								var refO = this._readInt1( 0 );
+								endRead = refO.endRead;
+								mode_local = 1;
+
+							} else {
+
+								var find = this._currentObject.data.indexOf( ',', endRead ) + 1;
+								if ( find === 0 ) {
+
+									find = this._currentObject.data.length;
+									mode = 2;
+									mode_local = 0;
+
+								}
+								var line = this._currentObject.data.substr( endRead, find - endRead );
+								var data = this._readLine( line.trim() ).split( ";" );
+								if ( this.IsUvYReverse ) {
+
+									this._currentGeo.GeometryData.uvs.push( parseFloat( data[ 0 ] ), 1 - parseFloat( data[ 1 ] ) );
+
+								} else {
+
+									this._currentGeo.GeometryData.uvs.push( parseFloat( data[ 0 ] ), parseFloat( data[ 1 ] ) );
+
+								}
+								endRead = find + 1;
+
+							}
+							break;
+
+					}
+					if ( endRead >= this._currentObject.data.length ) {
+
+						break;
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_setMeshMaterialList',
+			value: function _setMeshMaterialList() {
+
+				var endRead = 0;
+				var mode = 0;
+				var mode_local = 0;
+				while ( true ) {
+
+					if ( mode_local < 2 ) {
+
+						var refO = this._readInt1( endRead );
+						endRead = refO.endRead;
+						mode_local ++;
+
+					} else {
+
+						var find = this._currentObject.data.indexOf( ';', endRead );
+						if ( find === - 1 ) {
+
+							find = this._currentObject.data.length;
+							mode = 3;
+							mode_local = 0;
+
+						}
+						var line = this._currentObject.data.substr( endRead, find - endRead );
+						var data = this._readLine( line.trim() ).split( "," );
+						for ( var i = 0; i < data.length; i ++ ) {
+
+							this._currentGeo.GeometryData.materialIndices[ i ] = parseInt( data[ i ] );
+
+						}
+						endRead = this._currentObject.data.length;
+
+					}
+					if ( endRead >= this._currentObject.data.length || mode >= 3 ) {
+
+						break;
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_setMaterial',
+			value: function _setMaterial() {
+
+				var _nowMat = new MeshPhongMaterial( {
+					color: Math.random() * 0xffffff
+				} );
+				_nowMat.side = FrontSide;
+				_nowMat.name = this._currentObject.name;
+				var endRead = 0;
+				var find = this._currentObject.data.indexOf( ';;', endRead );
+				var line = this._currentObject.data.substr( endRead, find - endRead );
+				var data = this._readLine( line.trim() ).split( ";" );
+				_nowMat.color.r = parseFloat( data[ 0 ] );
+				_nowMat.color.g = parseFloat( data[ 1 ] );
+				_nowMat.color.b = parseFloat( data[ 2 ] );
+				endRead = find + 2;
+				find = this._currentObject.data.indexOf( ';', endRead );
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				_nowMat.shininess = parseFloat( this._readLine( line ) );
+				endRead = find + 1;
+				find = this._currentObject.data.indexOf( ';;', endRead );
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				var data2 = this._readLine( line.trim() ).split( ";" );
+				_nowMat.specular.r = parseFloat( data2[ 0 ] );
+				_nowMat.specular.g = parseFloat( data2[ 1 ] );
+				_nowMat.specular.b = parseFloat( data2[ 2 ] );
+				endRead = find + 2;
+				find = this._currentObject.data.indexOf( ';;', endRead );
+				if ( find === - 1 ) {
+
+					find = this._currentObject.data.length;
+
+				}
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				var data3 = this._readLine( line.trim() ).split( ";" );
+				_nowMat.emissive.r = parseFloat( data3[ 0 ] );
+				_nowMat.emissive.g = parseFloat( data3[ 1 ] );
+				_nowMat.emissive.b = parseFloat( data3[ 2 ] );
+				var localObject = null;
+				while ( true ) {
+
+					if ( this._currentObject.children.length > 0 ) {
+
+						localObject = this._currentObject.children.shift();
+						if ( this.debug ) {
+
+							console.log( 'processing ' + localObject.name );
+
+						}
+						var fileName = localObject.data.substr( 1, localObject.data.length - 2 );
+						switch ( localObject.type ) {
+
+							case "TextureFilename":
+								_nowMat.map = this.texloader.load( fileName );
+								break;
+							case "BumpMapFilename":
+								_nowMat.bumpMap = this.texloader.load( fileName );
+								_nowMat.bumpScale = 0.05;
+								break;
+							case "NormalMapFilename":
+								_nowMat.normalMap = this.texloader.load( fileName );
+								_nowMat.normalScale = new Vector2( 2, 2 );
+								break;
+							case "EmissiveMapFilename":
+								_nowMat.emissiveMap = this.texloader.load( fileName );
+								break;
+							case "LightMapFilename":
+								_nowMat.lightMap = this.texloader.load( fileName );
+								break;
+
+						}
+
+					} else {
+
+						break;
+
+					}
+
+				}
+				this._currentGeo.Materials.push( _nowMat );
+
+			}
+		}, {
+			key: '_setSkinWeights',
+			value: function _setSkinWeights() {
+
+				var boneInf = new XboneInf();
+				var endRead = 0;
+				var find = this._currentObject.data.indexOf( ';', endRead );
+				var line = this._currentObject.data.substr( endRead, find - endRead );
+				endRead = find + 1;
+				boneInf.boneName = line.substr( 1, line.length - 2 );
+				boneInf.BoneIndex = this._currentGeo.BoneInfs.length;
+				find = this._currentObject.data.indexOf( ';', endRead );
+				endRead = find + 1;
+				find = this._currentObject.data.indexOf( ';', endRead );
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				var data = this._readLine( line.trim() ).split( "," );
+				for ( var i = 0; i < data.length; i ++ ) {
+
+					boneInf.Indeces.push( parseInt( data[ i ] ) );
+
+				}
+				endRead = find + 1;
+				find = this._currentObject.data.indexOf( ';', endRead );
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				var data2 = this._readLine( line.trim() ).split( "," );
+				for ( var _i = 0; _i < data2.length; _i ++ ) {
+
+					boneInf.Weights.push( parseFloat( data2[ _i ] ) );
+
+				}
+				endRead = find + 1;
+				find = this._currentObject.data.indexOf( ';', endRead );
+				if ( find <= 0 ) {
+
+					find = this._currentObject.data.length;
+
+				}
+				line = this._currentObject.data.substr( endRead, find - endRead );
+				var data3 = this._readLine( line.trim() ).split( "," );
+				boneInf.OffsetMatrix = new Matrix4();
+				this._ParseMatrixData( boneInf.OffsetMatrix, data3 );
+				this._currentGeo.BoneInfs.push( boneInf );
+
+			}
+		}, {
+			key: '_makePutBoneList',
+			value: function _makePutBoneList( _RootName, _bones ) {
+
+				var putting = false;
+				for ( var frame in this.HieStack ) {
+
+					if ( this.HieStack[ frame ].name === _RootName || putting ) {
+
+						putting = true;
+						var b = new Bone();
+						b.name = this.HieStack[ frame ].name;
+						b.applyMatrix( this.HieStack[ frame ].FrameTransformMatrix );
+						b.matrixWorld = b.matrix;
+						b.FrameTransformMatrix = this.HieStack[ frame ].FrameTransformMatrix;
+						b.pos = new Vector3().setFromMatrixPosition( b.FrameTransformMatrix ).toArray();
+						b.rotq = new Quaternion().setFromRotationMatrix( b.FrameTransformMatrix ).toArray();
+						b.scl = new Vector3().setFromMatrixScale( b.FrameTransformMatrix ).toArray();
+						if ( this.HieStack[ frame ].parentName && this.HieStack[ frame ].parentName.length > 0 ) {
+
+							for ( var i = 0; i < _bones.length; i ++ ) {
+
+								if ( this.HieStack[ frame ].parentName === _bones[ i ].name ) {
+
+									_bones[ i ].add( b );
+									b.parent = i;
+									break;
+
+								}
+
+							}
+
+						}
+						_bones.push( b );
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_makeOutputGeometry',
+			value: function _makeOutputGeometry() {
+
+				var mesh = null;
+				if ( this._currentGeo.BoneInfs.length > 0 ) {
+
+					var putBones = [];
+					this._makePutBoneList( this._currentGeo.baseFrame.parentName, putBones );
+					for ( var bi = 0; bi < this._currentGeo.BoneInfs.length; bi ++ ) {
+
+						var boneIndex = 0;
+						for ( var bb = 0; bb < putBones.length; bb ++ ) {
+
+							if ( putBones[ bb ].name === this._currentGeo.BoneInfs[ bi ].boneName ) {
+
+								boneIndex = bb;
+								putBones[ bb ].OffsetMatrix = new Matrix4();
+								putBones[ bb ].OffsetMatrix.copy( this._currentGeo.BoneInfs[ bi ].OffsetMatrix );
+								break;
+
+							}
+
+						}
+						for ( var vi = 0; vi < this._currentGeo.BoneInfs[ bi ].Indeces.length; vi ++ ) {
+
+							var nowVertexID = this._currentGeo.BoneInfs[ bi ].Indeces[ vi ];
+							var nowVal = this._currentGeo.BoneInfs[ bi ].Weights[ vi ];
+
+							var stride = nowVertexID * 4;
+
+							switch ( this._currentGeo.VertexSetedBoneCount[ nowVertexID ] ) {
+
+								case 0:
+									this._currentGeo.GeometryData.skinIndices[ stride ] = boneIndex;
+									this._currentGeo.GeometryData.skinWeights[ stride ] = nowVal;
+									break;
+								case 1:
+									this._currentGeo.GeometryData.skinIndices[ stride + 1 ] = boneIndex;
+									this._currentGeo.GeometryData.skinWeights[ stride + 1 ] = nowVal;
+									break;
+								case 2:
+									this._currentGeo.GeometryData.skinIndices[ stride + 2 ] = boneIndex;
+									this._currentGeo.GeometryData.skinWeights[ stride + 2 ] = nowVal;
+									break;
+								case 3:
+									this._currentGeo.GeometryData.skinIndices[ stride + 3 ] = boneIndex;
+									this._currentGeo.GeometryData.skinWeights[ stride + 3 ] = nowVal;
+									break;
+
+							}
+							this._currentGeo.VertexSetedBoneCount[ nowVertexID ] ++;
+							if ( this._currentGeo.VertexSetedBoneCount[ nowVertexID ] > 4 ) {
+
+								console.log( 'warn! over 4 bone weight! :' + nowVertexID );
+
+							}
+
+						}
+
+					}
+					for ( var sk = 0; sk < this._currentGeo.Materials.length; sk ++ ) {
+
+						this._currentGeo.Materials[ sk ].skinning = true;
+
+					}
+					var offsetList = [];
+					for ( var _bi = 0; _bi < putBones.length; _bi ++ ) {
+
+						if ( putBones[ _bi ].OffsetMatrix ) {
+
+							offsetList.push( putBones[ _bi ].OffsetMatrix );
+
+						} else {
+
+							offsetList.push( new Matrix4() );
+
+						}
+
+					}
+
+					var bufferGeometry = this._buildGeometry();
+					mesh = new SkinnedMesh( bufferGeometry, this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[ 0 ] : this._currentGeo.Materials );
+
+					this._initSkeleton( mesh, putBones, offsetList );
+
+				} else {
+
+					var _bufferGeometry = this._buildGeometry();
+					mesh = new Mesh( _bufferGeometry, this._currentGeo.Materials.length === 1 ? this._currentGeo.Materials[ 0 ] : this._currentGeo.Materials );
+
+				}
+				mesh.name = this._currentGeo.name;
+				var worldBaseMx = new Matrix4();
+				var currentMxFrame = this._currentGeo.baseFrame.putBone;
+				if ( currentMxFrame && currentMxFrame.parent ) {
+
+					while ( true ) {
+
+						currentMxFrame = currentMxFrame.parent;
+						if ( currentMxFrame ) {
+
+							worldBaseMx.multiply( currentMxFrame.FrameTransformMatrix );
+
+						} else {
+
+							break;
+
+						}
+
+					}
+					mesh.applyMatrix( worldBaseMx );
+
+				}
+				this.Meshes.push( mesh );
+
+			}
+		}, {
+			key: '_initSkeleton',
+			value: function _initSkeleton( mesh, boneList, boneInverses ) {
+
+				var bones = [], bone, gbone;
+				var i, il;
+
+				for ( i = 0, il = boneList.length; i < il; i ++ ) {
+
+					gbone = boneList[ i ];
+
+					bone = new Bone();
+					bones.push( bone );
+
+					bone.name = gbone.name;
+					bone.position.fromArray( gbone.pos );
+					bone.quaternion.fromArray( gbone.rotq );
+					if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );
+
+				}
+
+				for ( i = 0, il = boneList.length; i < il; i ++ ) {
+
+					gbone = boneList[ i ];
+
+					if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {
+
+						bones[ gbone.parent ].add( bones[ i ] );
+
+					} else {
+
+						mesh.add( bones[ i ] );
+
+					}
+
+				}
+
+				mesh.updateMatrixWorld( true );
+
+				var skeleton = new Skeleton( bones, boneInverses );
+				mesh.bind( skeleton, mesh.matrixWorld );
+
+			}
+
+		}, {
+			key: '_readAnimationKey',
+			value: function _readAnimationKey() {
+
+				var endRead = 0;
+				var find = this._currentObject.data.indexOf( ';', endRead );
+				var line = this._currentObject.data.substr( endRead, find - endRead );
+				endRead = find + 1;
+				var nowKeyType = parseInt( this._readLine( line ) );
+				find = this._currentObject.data.indexOf( ';', endRead );
+				endRead = find + 1;
+				line = this._currentObject.data.substr( endRead );
+				var data = this._readLine( line.trim() ).split( ";;," );
+				for ( var i = 0; i < data.length; i ++ ) {
+
+					var data2 = data[ i ].split( ";" );
+					var keyInfo = new XKeyFrameInfo();
+					keyInfo.type = nowKeyType;
+					keyInfo.Frame = parseInt( data2[ 0 ] );
+					keyInfo.index = this._currentAnimeFrames.keyFrames.length;
+					keyInfo.time = keyInfo.Frame;
+					if ( nowKeyType != 4 ) {
+
+						var frameFound = false;
+						for ( var mm = 0; mm < this._currentAnimeFrames.keyFrames.length; mm ++ ) {
+
+							if ( this._currentAnimeFrames.keyFrames[ mm ].Frame === keyInfo.Frame ) {
+
+								keyInfo = this._currentAnimeFrames.keyFrames[ mm ];
+								frameFound = true;
+								break;
+
+							}
+
+						}
+						var frameValue = data2[ 2 ].split( "," );
+						switch ( nowKeyType ) {
+
+							case 0:
+								keyInfo.rot = new Quaternion( parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ), parseFloat( frameValue[ 3 ] ), parseFloat( frameValue[ 0 ] ) * - 1 );
+								break;
+							case 1:
+								keyInfo.scl = new Vector3( parseFloat( frameValue[ 0 ] ), parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ) );
+								break;
+							case 2:
+								keyInfo.pos = new Vector3( parseFloat( frameValue[ 0 ] ), parseFloat( frameValue[ 1 ] ), parseFloat( frameValue[ 2 ] ) );
+								break;
+
+						}
+						if ( ! frameFound ) {
+
+							this._currentAnimeFrames.keyFrames.push( keyInfo );
+
+						}
+
+					} else {
+
+						keyInfo.matrix = new Matrix4();
+						this._ParseMatrixData( keyInfo.matrix, data2[ 2 ].split( "," ) );
+						this._currentAnimeFrames.keyFrames.push( keyInfo );
+
+					}
+
+				}
+
+			}
+		}, {
+			key: '_makeOutputAnimation',
+			value: function _makeOutputAnimation() {
+
+				var animationObj = new XAnimationObj( this.options );
+				animationObj.fps = this.animTicksPerSecond;
+				animationObj.name = this._currentAnime.name;
+				animationObj.make( this._currentAnime.AnimeFrames );
+				this.animations.push( animationObj );
+
+			}
+		}, {
+			key: 'assignAnimation',
+			value: function assignAnimation( _model, _animation ) {
+
+				var model = _model;
+				var animation = _animation;
+				if ( ! model ) {
+
+					model = this.Meshes[ 0 ];
+
+				}
+				if ( ! animation ) {
+
+					animation = this.animations[ 0 ];
+
+				}
+				if ( ! model || ! animation ) {
+
+					return null;
+
+				}
+				var put = {};
+				put.fps = animation.fps;
+				put.name = animation.name;
+				put.length = animation.length;
+				put.hierarchy = [];
+				for ( var b = 0; b < model.skeleton.bones.length; b ++ ) {
+
+					var findAnimation = false;
+					for ( var i = 0; i < animation.hierarchy.length; i ++ ) {
+
+						if ( model.skeleton.bones[ b ].name === animation.hierarchy[ i ].name ) {
+
+							findAnimation = true;
+							var c_key = animation.hierarchy[ i ].copy();
+							c_key.parent = - 1;
+							if ( model.skeleton.bones[ b ].parent && model.skeleton.bones[ b ].parent.type === "Bone" ) {
+
+								for ( var bb = 0; bb < put.hierarchy.length; bb ++ ) {
+
+									if ( put.hierarchy[ bb ].name === model.skeleton.bones[ b ].parent.name ) {
+
+										c_key.parent = bb;
+										c_key.parentName = model.skeleton.bones[ b ].parent.name;
+
+									}
+
+								}
+
+							}
+							put.hierarchy.push( c_key );
+							break;
+
+						}
+
+					}
+					if ( ! findAnimation ) {
+
+						var _c_key = animation.hierarchy[ 0 ].copy();
+						_c_key.name = model.skeleton.bones[ b ].name;
+						_c_key.parent = - 1;
+						for ( var k = 0; k < _c_key.keys.length; k ++ ) {
+
+							if ( _c_key.keys[ k ].pos ) {
+
+								_c_key.keys[ k ].pos.set( 0, 0, 0 );
+
+							}
+							if ( _c_key.keys[ k ].scl ) {
+
+								_c_key.keys[ k ].scl.set( 1, 1, 1 );
+
+							}
+							if ( _c_key.keys[ k ].rot ) {
+
+								_c_key.keys[ k ].rot.set( 0, 0, 0, 1 );
+
+							}
+
+						}
+						put.hierarchy.push( _c_key );
+
+					}
+
+				}
+				if ( ! model.geometry.animations ) {
+
+					model.geometry.animations = [];
+
+				}
+
+				model.geometry.animations.push( AnimationClip.parseAnimation( put, model.skeleton.bones ) );
+				if ( ! model.animationMixer ) {
+
+					model.animationMixer = new AnimationMixer( model );
+
+				}
+
+				return put;
+
+			}
+		}, {
+			key: '_ParseMatrixData',
+			value: function _ParseMatrixData( targetMatrix, data ) {
+
+				targetMatrix.set( parseFloat( data[ 0 ] ), parseFloat( data[ 4 ] ), parseFloat( data[ 8 ] ), parseFloat( data[ 12 ] ), parseFloat( data[ 1 ] ), parseFloat( data[ 5 ] ), parseFloat( data[ 9 ] ), parseFloat( data[ 13 ] ), parseFloat( data[ 2 ] ), parseFloat( data[ 6 ] ), parseFloat( data[ 10 ] ), parseFloat( data[ 14 ] ), parseFloat( data[ 3 ] ), parseFloat( data[ 7 ] ), parseFloat( data[ 11 ] ), parseFloat( data[ 15 ] ) );
+
+			}
+		} ] );
+		return XLoader;
+
+	}();
+
+	return XLoader;
+
+} )();
+
+export { XLoader };

部分文件因为文件数量过多而无法显示