瀏覽代碼

Remove examples/js. (#25043)

* Remove examples/js.

* Remove examples build script.

* Manual: Update links.
Michael Herzog 2 年之前
父節點
當前提交
1b31fef7c0
共有 100 個文件被更改,包括 23 次插入44562 次删除
  1. 1 1
      docs/examples/en/loaders/DRACOLoader.html
  2. 2 2
      docs/examples/en/loaders/GLTFLoader.html
  3. 3 3
      docs/examples/en/loaders/KTX2Loader.html
  4. 1 1
      docs/examples/zh/loaders/DRACOLoader.html
  5. 2 2
      docs/examples/zh/loaders/GLTFLoader.html
  6. 1 1
      editor/index.html
  7. 5 5
      editor/js/Loader.js
  8. 8 8
      editor/sw.js
  9. 0 89
      examples/js/animation/AnimationClipCreator.js
  10. 0 416
      examples/js/animation/CCDIKSolver.js
  11. 0 1046
      examples/js/animation/MMDAnimationHelper.js
  12. 0 1174
      examples/js/animation/MMDPhysics.js
  13. 0 168
      examples/js/cameras/CinematicCamera.js
  14. 0 2770
      examples/js/controls/ArcballControls.js
  15. 0 205
      examples/js/controls/DragControls.js
  16. 0 312
      examples/js/controls/FirstPersonControls.js
  17. 0 321
      examples/js/controls/FlyControls.js
  18. 0 1101
      examples/js/controls/OrbitControls.js
  19. 0 144
      examples/js/controls/PointerLockControls.js
  20. 0 729
      examples/js/controls/TrackballControls.js
  21. 0 1301
      examples/js/controls/TransformControls.js
  22. 0 347
      examples/js/csm/CSM.js
  23. 0 127
      examples/js/csm/CSMFrustum.js
  24. 0 165
      examples/js/csm/CSMHelper.js
  25. 0 253
      examples/js/csm/CSMShader.js
  26. 0 348
      examples/js/curves/CurveExtras.js
  27. 0 63
      examples/js/curves/NURBSCurve.js
  28. 0 48
      examples/js/curves/NURBSSurface.js
  29. 0 439
      examples/js/curves/NURBSUtils.js
  30. 0 86
      examples/js/effects/AnaglyphEffect.js
  31. 0 260
      examples/js/effects/AsciiEffect.js
  32. 0 450
      examples/js/effects/OutlineEffect.js
  33. 0 62
      examples/js/effects/ParallaxBarrierEffect.js
  34. 0 139
      examples/js/effects/PeppersGhostEffect.js
  35. 0 46
      examples/js/effects/StereoEffect.js
  36. 0 53
      examples/js/environments/DebugEnvironment.js
  37. 0 124
      examples/js/environments/RoomEnvironment.js
  38. 0 487
      examples/js/exporters/ColladaExporter.js
  39. 0 212
      examples/js/exporters/DRACOExporter.js
  40. 0 455
      examples/js/exporters/EXRExporter.js
  41. 0 2367
      examples/js/exporters/GLTFExporter.js
  42. 0 187
      examples/js/exporters/MMDExporter.js
  43. 0 260
      examples/js/exporters/OBJExporter.js
  44. 0 427
      examples/js/exporters/PLYExporter.js
  45. 0 188
      examples/js/exporters/STLExporter.js
  46. 0 608
      examples/js/exporters/USDZExporter.js
  47. 0 59
      examples/js/geometries/BoxLineGeometry.js
  48. 0 53
      examples/js/geometries/ConvexGeometry.js
  49. 0 324
      examples/js/geometries/DecalGeometry.js
  50. 0 861
      examples/js/geometries/LightningStrike.js
  51. 0 216
      examples/js/geometries/ParametricGeometries.js
  52. 0 121
      examples/js/geometries/ParametricGeometry.js
  53. 0 142
      examples/js/geometries/RoundedBoxGeometry.js
  54. 0 62
      examples/js/geometries/TeapotGeometry.js
  55. 0 53
      examples/js/geometries/TextGeometry.js
  56. 0 48
      examples/js/helpers/LightProbeHelper.js
  57. 0 76
      examples/js/helpers/OctreeHelper.js
  58. 0 91
      examples/js/helpers/PositionalAudioHelper.js
  59. 0 73
      examples/js/helpers/RectAreaLightHelper.js
  60. 0 74
      examples/js/helpers/VertexNormalsHelper.js
  61. 0 68
      examples/js/helpers/VertexTangentsHelper.js
  62. 0 281
      examples/js/helpers/ViewHelper.js
  63. 0 497
      examples/js/interactive/HTMLMesh.js
  64. 0 95
      examples/js/interactive/InteractiveGroup.js
  65. 0 195
      examples/js/interactive/SelectionBox.js
  66. 0 83
      examples/js/interactive/SelectionHelper.js
  67. 0 2
      examples/js/libs/chevrotain.min.js
  68. 0 6
      examples/js/libs/fflate.min.js
  69. 0 0
      examples/js/libs/ktx-parse.umd.js
  70. 0 7
      examples/js/libs/meshopt_decoder.js
  71. 0 0
      examples/js/libs/opentype.min.js
  72. 0 5
      examples/js/libs/stats.min.js
  73. 0 221
      examples/js/lights/LightProbeGenerator.js
  74. 0 25
      examples/js/lights/RectAreaLightUniformsLib.js
  75. 0 19
      examples/js/lines/Line2.js
  76. 0 69
      examples/js/lines/LineGeometry.js
  77. 0 635
      examples/js/lines/LineMaterial.js
  78. 0 313
      examples/js/lines/LineSegments2.js
  79. 0 198
      examples/js/lines/LineSegmentsGeometry.js
  80. 0 47
      examples/js/lines/Wireframe.js
  81. 0 20
      examples/js/lines/WireframeGeometry2.js
  82. 0 1273
      examples/js/loaders/3DMLoader.js
  83. 0 1306
      examples/js/loaders/3MFLoader.js
  84. 0 504
      examples/js/loaders/AMFLoader.js
  85. 0 395
      examples/js/loaders/BVHLoader.js
  86. 0 706
      examples/js/loaders/BasisTextureLoader.js
  87. 0 3690
      examples/js/loaders/ColladaLoader.js
  88. 0 244
      examples/js/loaders/DDSLoader.js
  89. 0 511
      examples/js/loaders/DRACOLoader.js
  90. 0 2039
      examples/js/loaders/EXRLoader.js
  91. 0 3681
      examples/js/loaders/FBXLoader.js
  92. 0 160
      examples/js/loaders/FontLoader.js
  93. 0 255
      examples/js/loaders/GCodeLoader.js
  94. 0 3862
      examples/js/loaders/GLTFLoader.js
  95. 0 87
      examples/js/loaders/HDRCubeTextureLoader.js
  96. 0 121
      examples/js/loaders/KMZLoader.js
  97. 0 159
      examples/js/loaders/KTXLoader.js
  98. 0 2263
      examples/js/loaders/LDrawLoader.js
  99. 0 135
      examples/js/loaders/LUT3dlLoader.js
  100. 0 132
      examples/js/loaders/LUTCubeLoader.js

+ 1 - 1
docs/examples/en/loaders/DRACOLoader.html

@@ -38,7 +38,7 @@
 		const loader = new DRACOLoader();
 
 		// Specify path to a folder containing WASM/JS decoding libraries.
-		loader.setDecoderPath( '/examples/js/libs/draco/' );
+		loader.setDecoderPath( '/examples/jsm/libs/draco/' );
 
 		// Optional: Pre-fetch Draco WASM/JS module.
 		loader.preload();

+ 2 - 2
docs/examples/en/loaders/GLTFLoader.html

@@ -83,7 +83,7 @@
 
 		// Optional: Provide a DRACOLoader instance to decode compressed mesh data
 		const dracoLoader = new DRACOLoader();
-		dracoLoader.setDecoderPath( '/examples/js/libs/draco/' );
+		dracoLoader.setDecoderPath( '/examples/jsm/libs/draco/' );
 		loader.setDRACOLoader( dracoLoader );
 
 		// Load a glTF resource
@@ -211,7 +211,7 @@
 		[page:DRACOLoader dracoLoader] — Instance of THREE.DRACOLoader, to be used for decoding assets compressed with the KHR_draco_mesh_compression extension.
 		</p>
 		<p>
-		Refer to this [link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/draco#readme readme] for the details of Draco and its decoder.
+		Refer to this [link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/draco#readme readme] for the details of Draco and its decoder.
 		</p>
 
 		<h3>[method:this setKTX2Loader]( [param:KTX2Loader ktx2Loader] )</h3>

+ 3 - 3
docs/examples/en/loaders/KTX2Loader.html

@@ -23,7 +23,7 @@
 		<p>
 			This loader parses the KTX 2.0 container and transcodes to a supported GPU compressed
 			texture format. The required WASM transcoder and JS wrapper are available from the
-			[link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/basis examples/js/libs/basis]
+			[link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/basis examples/jsm/libs/basis]
 			directory.
 		</p>
 
@@ -31,7 +31,7 @@
 
 		<code>
 		var ktx2Loader = new THREE.KTX2Loader();
-		ktx2Loader.setTranscoderPath( 'examples/js/libs/basis/' );
+		ktx2Loader.setTranscoderPath( 'examples/jsm/libs/basis/' );
 		ktx2Loader.detectSupport( renderer );
 		ktx2Loader.load( 'diffuse.ktx2', function ( texture ) {
 
@@ -106,7 +106,7 @@
 		</p>
 		<p>
 		The WASM transcoder and JS wrapper are available from the
-		[link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/basis examples/js/libs/basis]
+		[link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/basis examples/jsm/libs/basis]
 		directory.
 		</p>
 

+ 1 - 1
docs/examples/zh/loaders/DRACOLoader.html

@@ -33,7 +33,7 @@
 		const loader = new DRACOLoader();
 
 		// Specify path to a folder containing WASM/JS decoding libraries.
-		loader.setDecoderPath( '/examples/js/libs/draco/' );
+		loader.setDecoderPath( '/examples/jsm/libs/draco/' );
 
 		// Optional: Pre-fetch Draco WASM/JS module.
 		loader.preload();

+ 2 - 2
docs/examples/zh/loaders/GLTFLoader.html

@@ -82,7 +82,7 @@
 
 		// Optional: Provide a DRACOLoader instance to decode compressed mesh data
 		const dracoLoader = new DRACOLoader();
-		dracoLoader.setDecoderPath( '/examples/js/libs/draco/' );
+		dracoLoader.setDecoderPath( '/examples/jsm/libs/draco/' );
 		loader.setDRACOLoader( dracoLoader );
 
 		// Load a glTF resource
@@ -206,7 +206,7 @@
 		[page:DRACOLoader dracoLoader] — THREE.DRACOLoader的实例,用于解码使用KHR_draco_mesh_compression扩展压缩过的文件。
 		</p>
 		<p>
-		请参阅[link:https://github.com/mrdoob/three.js/tree/dev/examples/js/libs/draco#readme readme]来了解Draco及其解码器的详细信息。
+		请参阅[link:https://github.com/mrdoob/three.js/tree/dev/examples/jsm/libs/draco#readme readme]来了解Draco及其解码器的详细信息。
 		</p>
 
 		<h3>[method:undefined parse]( [param:ArrayBuffer data], [param:String path], [param:Function onLoad], [param:Function onError] )</h3>

+ 1 - 1
editor/index.html

@@ -12,7 +12,7 @@
 	<body>
 		<link rel="stylesheet" href="css/main.css">
 
-		<script src="../examples/js/libs/draco/draco_encoder.js"></script>
+		<script src="../examples/jsm/libs/draco/draco_encoder.js"></script>
 
 		<link rel="stylesheet" href="js/libs/codemirror/codemirror.css">
 		<link rel="stylesheet" href="js/libs/codemirror/theme/monokai.css">

+ 5 - 5
editor/js/Loader.js

@@ -199,7 +199,7 @@ function Loader( editor ) {
 					const { DRACOLoader } = await import( 'three/addons/loaders/DRACOLoader.js' );
 
 					const loader = new DRACOLoader();
-					loader.setDecoderPath( '../examples/js/libs/draco/' );
+					loader.setDecoderPath( '../examples/jsm/libs/draco/' );
 					loader.decodeDracoFile( contents, function ( geometry ) {
 
 						let object;
@@ -267,7 +267,7 @@ function Loader( editor ) {
 					const { GLTFLoader } = await import( 'three/addons/loaders/GLTFLoader.js' );
 
 					const dracoLoader = new DRACOLoader();
-					dracoLoader.setDecoderPath( '../examples/js/libs/draco/gltf/' );
+					dracoLoader.setDecoderPath( '../examples/jsm/libs/draco/gltf/' );
 
 					const loader = new GLTFLoader();
 					loader.setDRACOLoader( dracoLoader );
@@ -308,7 +308,7 @@ function Loader( editor ) {
 						const { GLTFLoader } = await import( 'three/addons/loaders/GLTFLoader.js' );
 
 						const dracoLoader = new DRACOLoader();
-						dracoLoader.setDecoderPath( '../examples/js/libs/draco/gltf/' );
+						dracoLoader.setDecoderPath( '../examples/jsm/libs/draco/gltf/' );
 
 						loader = new GLTFLoader( manager );
 						loader.setDRACOLoader( dracoLoader );
@@ -956,7 +956,7 @@ function Loader( editor ) {
 					const { GLTFLoader } = await import( 'three/addons/loaders/GLTFLoader.js' );
 
 					const dracoLoader = new DRACOLoader();
-					dracoLoader.setDecoderPath( '../examples/js/libs/draco/gltf/' );
+					dracoLoader.setDecoderPath( '../examples/jsm/libs/draco/gltf/' );
 
 					const loader = new GLTFLoader();
 					loader.setDRACOLoader( dracoLoader );
@@ -982,7 +982,7 @@ function Loader( editor ) {
 					const { GLTFLoader } = await import( 'three/addons/loaders/GLTFLoader.js' );
 
 					const dracoLoader = new DRACOLoader();
-					dracoLoader.setDecoderPath( '../examples/js/libs/draco/gltf/' );
+					dracoLoader.setDecoderPath( '../examples/jsm/libs/draco/gltf/' );
 
 					const loader = new GLTFLoader( manager );
 					loader.setDRACOLoader( dracoLoader );

+ 8 - 8
editor/sw.js

@@ -15,14 +15,14 @@ const assets = [
 	'../examples/jsm/libs/chevrotain.module.min.js',
 	'../examples/jsm/libs/fflate.module.js',
 
-	'../examples/js/libs/draco/draco_decoder.js',
-	'../examples/js/libs/draco/draco_decoder.wasm',
-	'../examples/js/libs/draco/draco_encoder.js',
-	'../examples/js/libs/draco/draco_wasm_wrapper.js',
-
-	'../examples/js/libs/draco/gltf/draco_decoder.js',
-	'../examples/js/libs/draco/gltf/draco_decoder.wasm',
-	'../examples/js/libs/draco/gltf/draco_wasm_wrapper.js',
+	'../examples/jsm/libs/draco/draco_decoder.js',
+	'../examples/jsm/libs/draco/draco_decoder.wasm',
+	'../examples/jsm/libs/draco/draco_encoder.js',
+	'../examples/jsm/libs/draco/draco_wasm_wrapper.js',
+
+	'../examples/jsm/libs/draco/gltf/draco_decoder.js',
+	'../examples/jsm/libs/draco/gltf/draco_decoder.wasm',
+	'../examples/jsm/libs/draco/gltf/draco_wasm_wrapper.js',
 
 	'../examples/jsm/libs/motion-controllers.module.js',
 

+ 0 - 89
examples/js/animation/AnimationClipCreator.js

@@ -1,89 +0,0 @@
-( function () {
-
-	class AnimationClipCreator {
-
-		static CreateRotationAnimation( period, axis = 'x' ) {
-
-			const times = [ 0, period ],
-				values = [ 0, 360 ];
-			const trackName = '.rotation[' + axis + ']';
-			const track = new THREE.NumberKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, period, [ track ] );
-
-		}
-		static CreateScaleAxisAnimation( period, axis = 'x' ) {
-
-			const times = [ 0, period ],
-				values = [ 0, 1 ];
-			const trackName = '.scale[' + axis + ']';
-			const track = new THREE.NumberKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, period, [ track ] );
-
-		}
-		static CreateShakeAnimation( duration, shakeScale ) {
-
-			const times = [],
-				values = [],
-				tmp = new THREE.Vector3();
-			for ( let 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 );
-
-			}
-
-			const trackName = '.position';
-			const track = new THREE.VectorKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, duration, [ track ] );
-
-		}
-		static CreatePulsationAnimation( duration, pulseScale ) {
-
-			const times = [],
-				values = [],
-				tmp = new THREE.Vector3();
-			for ( let i = 0; i < duration * 10; i ++ ) {
-
-				times.push( i / 10 );
-				const scaleFactor = Math.random() * pulseScale;
-				tmp.set( scaleFactor, scaleFactor, scaleFactor ).toArray( values, values.length );
-
-			}
-
-			const trackName = '.scale';
-			const track = new THREE.VectorKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, duration, [ track ] );
-
-		}
-		static CreateVisibilityAnimation( duration ) {
-
-			const times = [ 0, duration / 2, duration ],
-				values = [ true, false, true ];
-			const trackName = '.visible';
-			const track = new THREE.BooleanKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, duration, [ track ] );
-
-		}
-		static CreateMaterialColorAnimation( duration, colors ) {
-
-			const times = [],
-				values = [],
-				timeStep = duration / colors.length;
-			for ( let i = 0; i <= colors.length; i ++ ) {
-
-				times.push( i * timeStep );
-				values.push( colors[ i % colors.length ] );
-
-			}
-
-			const trackName = '.material[0].color';
-			const track = new THREE.ColorKeyframeTrack( trackName, times, values );
-			return new THREE.AnimationClip( null, duration, [ track ] );
-
-		}
-
-	}
-
-	THREE.AnimationClipCreator = AnimationClipCreator;
-
-} )();

+ 0 - 416
examples/js/animation/CCDIKSolver.js

@@ -1,416 +0,0 @@
-( function () {
-
-	const _q = new THREE.Quaternion();
-	const _targetPos = new THREE.Vector3();
-	const _targetVec = new THREE.Vector3();
-	const _effectorPos = new THREE.Vector3();
-	const _effectorVec = new THREE.Vector3();
-	const _linkPos = new THREE.Vector3();
-	const _invLinkQ = new THREE.Quaternion();
-	const _linkScale = new THREE.Vector3();
-	const _axis = new THREE.Vector3();
-	const _vector = new THREE.Vector3();
-	const _matrix = new THREE.Matrix4();
-
-	/**
- * 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 THREE.Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
- *	iteration: 10,
- *	minAngle: 0.0,
- *	maxAngle: 1.0,
- * } ];
- */
-
-	class CCDIKSolver {
-
-		/**
-   * @param {THREE.SkinnedMesh} mesh
-   * @param {Array<Object>} iks
-   */
-		constructor( mesh, iks = [] ) {
-
-			this.mesh = mesh;
-			this.iks = iks;
-			this._valid();
-
-		}
-
-		/**
-   * Update all IK bones.
-   *
-   * @return {CCDIKSolver}
-   */
-		update() {
-
-			const iks = this.iks;
-			for ( let i = 0, il = iks.length; i < il; i ++ ) {
-
-				this.updateOne( iks[ i ] );
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Update one IK bone
-   *
-   * @param {Object} ik parameter
-   * @return {CCDIKSolver}
-   */
-		updateOne( ik ) {
-
-			const bones = this.mesh.skeleton.bones;
-
-			// for reference overhead reduction in loop
-			const math = Math;
-			const effector = bones[ ik.effector ];
-			const target = bones[ ik.target ];
-
-			// don't use getWorldPosition() here for the performance
-			// because it calls updateMatrixWorld( true ) inside.
-			_targetPos.setFromMatrixPosition( target.matrixWorld );
-			const links = ik.links;
-			const iteration = ik.iteration !== undefined ? ik.iteration : 1;
-			for ( let i = 0; i < iteration; i ++ ) {
-
-				let rotated = false;
-				for ( let j = 0, jl = links.length; j < jl; j ++ ) {
-
-					const link = bones[ links[ j ].index ];
-
-					// skip this link and following links.
-					// this skip is used for MMD performance optimization.
-					if ( links[ j ].enabled === false ) break;
-					const limitation = links[ j ].limitation;
-					const rotationMin = links[ j ].rotationMin;
-					const rotationMax = links[ j ].rotationMax;
-
-					// don't use getWorldPosition/Quaternion() here for the performance
-					// because they call updateMatrixWorld( true ) inside.
-					link.matrixWorld.decompose( _linkPos, _invLinkQ, _linkScale );
-					_invLinkQ.invert();
-					_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();
-					let 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
-					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 ) {
-
-						let c = link.quaternion.w;
-						if ( c > 1.0 ) c = 1.0;
-						const 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( _vector.setFromEuler( link.rotation ).max( rotationMin ) );
-
-					}
-
-					if ( rotationMax !== undefined ) {
-
-						link.rotation.setFromVector3( _vector.setFromEuler( link.rotation ).min( rotationMax ) );
-
-					}
-
-					link.updateMatrixWorld( true );
-					rotated = true;
-
-				}
-
-				if ( ! rotated ) break;
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Creates Helper
-   *
-   * @return {CCDIKHelper}
-   */
-		createHelper() {
-
-			return new CCDIKHelper( this.mesh, this.iks );
-
-		}
-
-		// private methods
-
-		_valid() {
-
-			const iks = this.iks;
-			const bones = this.mesh.skeleton.bones;
-			for ( let i = 0, il = iks.length; i < il; i ++ ) {
-
-				const ik = iks[ i ];
-				const effector = bones[ ik.effector ];
-				const links = ik.links;
-				let link0, link1;
-				link0 = effector;
-				for ( let 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;
-
-				}
-
-			}
-
-		}
-
-	}
-	function getPosition( bone, matrixWorldInv ) {
-
-		return _vector.setFromMatrixPosition( bone.matrixWorld ).applyMatrix4( matrixWorldInv );
-
-	}
-
-	function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
-
-		const v = getPosition( bone, matrixWorldInv );
-		array[ index * 3 + 0 ] = v.x;
-		array[ index * 3 + 1 ] = v.y;
-		array[ index * 3 + 2 ] = v.z;
-
-	}
-
-	/**
- * Visualize IK bones
- *
- * @param {SkinnedMesh} mesh
- * @param {Array<Object>} iks
- */
-	class CCDIKHelper extends THREE.Object3D {
-
-		constructor( mesh, iks = [], sphereSize = 0.25 ) {
-
-			super();
-			this.root = mesh;
-			this.iks = iks;
-			this.matrix.copy( mesh.matrixWorld );
-			this.matrixAutoUpdate = false;
-			this.sphereGeometry = new THREE.SphereGeometry( sphereSize, 16, 8 );
-			this.targetSphereMaterial = new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0xff8888 ),
-				depthTest: false,
-				depthWrite: false,
-				transparent: true
-			} );
-			this.effectorSphereMaterial = new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0x88ff88 ),
-				depthTest: false,
-				depthWrite: false,
-				transparent: true
-			} );
-			this.linkSphereMaterial = new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0x8888ff ),
-				depthTest: false,
-				depthWrite: false,
-				transparent: true
-			} );
-			this.lineMaterial = new THREE.LineBasicMaterial( {
-				color: new THREE.Color( 0xff0000 ),
-				depthTest: false,
-				depthWrite: false,
-				transparent: true
-			} );
-			this._init();
-
-		}
-
-		/**
-   * Updates IK bones visualization.
-   */
-		updateMatrixWorld( force ) {
-
-			const mesh = this.root;
-			if ( this.visible ) {
-
-				let offset = 0;
-				const iks = this.iks;
-				const bones = mesh.skeleton.bones;
-				_matrix.copy( mesh.matrixWorld ).invert();
-				for ( let i = 0, il = iks.length; i < il; i ++ ) {
-
-					const ik = iks[ i ];
-					const targetBone = bones[ ik.target ];
-					const effectorBone = bones[ ik.effector ];
-					const targetMesh = this.children[ offset ++ ];
-					const effectorMesh = this.children[ offset ++ ];
-					targetMesh.position.copy( getPosition( targetBone, _matrix ) );
-					effectorMesh.position.copy( getPosition( effectorBone, _matrix ) );
-					for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
-
-						const link = ik.links[ j ];
-						const linkBone = bones[ link.index ];
-						const linkMesh = this.children[ offset ++ ];
-						linkMesh.position.copy( getPosition( linkBone, _matrix ) );
-
-					}
-
-					const line = this.children[ offset ++ ];
-					const array = line.geometry.attributes.position.array;
-					setPositionOfBoneToAttributeArray( array, 0, targetBone, _matrix );
-					setPositionOfBoneToAttributeArray( array, 1, effectorBone, _matrix );
-					for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
-
-						const link = ik.links[ j ];
-						const linkBone = bones[ link.index ];
-						setPositionOfBoneToAttributeArray( array, j + 2, linkBone, _matrix );
-
-					}
-
-					line.geometry.attributes.position.needsUpdate = true;
-
-				}
-
-			}
-
-			this.matrix.copy( mesh.matrixWorld );
-			super.updateMatrixWorld( force );
-
-		}
-
-		/**
-   * Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
-   */
-		dispose() {
-
-			this.sphereGeometry.dispose();
-			this.targetSphereMaterial.dispose();
-			this.effectorSphereMaterial.dispose();
-			this.linkSphereMaterial.dispose();
-			this.lineMaterial.dispose();
-			const children = this.children;
-			for ( let i = 0; i < children.length; i ++ ) {
-
-				const child = children[ i ];
-				if ( child.isLine ) child.geometry.dispose();
-
-			}
-
-		}
-
-		// private method
-
-		_init() {
-
-			const scope = this;
-			const iks = this.iks;
-			function createLineGeometry( ik ) {
-
-				const geometry = new THREE.BufferGeometry();
-				const vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
-				geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
-				return geometry;
-
-			}
-
-			function createTargetMesh() {
-
-				return new THREE.Mesh( scope.sphereGeometry, scope.targetSphereMaterial );
-
-			}
-
-			function createEffectorMesh() {
-
-				return new THREE.Mesh( scope.sphereGeometry, scope.effectorSphereMaterial );
-
-			}
-
-			function createLinkMesh() {
-
-				return new THREE.Mesh( scope.sphereGeometry, scope.linkSphereMaterial );
-
-			}
-
-			function createLine( ik ) {
-
-				return new THREE.Line( createLineGeometry( ik ), scope.lineMaterial );
-
-			}
-
-			for ( let i = 0, il = iks.length; i < il; i ++ ) {
-
-				const ik = iks[ i ];
-				this.add( createTargetMesh() );
-				this.add( createEffectorMesh() );
-				for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
-
-					this.add( createLinkMesh() );
-
-				}
-
-				this.add( createLine( ik ) );
-
-			}
-
-		}
-
-	}
-
-	THREE.CCDIKHelper = CCDIKHelper;
-	THREE.CCDIKSolver = CCDIKSolver;
-
-} )();

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

@@ -1,1046 +0,0 @@
-( function () {
-
-	/**
- * MMDAnimationHelper handles animation of MMD assets loaded by MMDLoader
- * with MMD special features as IK, Grant, and Physics.
- *
- * Dependencies
- *  - ammo.js https://github.com/kripken/ammo.js
- *  - THREE.MMDPhysics
- *  - THREE.CCDIKSolver
- *
- * TODO
- *  - more precise grant skinning support.
- */
-	class MMDAnimationHelper {
-
-		/**
-   * @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.
-   */
-		constructor( params = {} ) {
-
-			this.meshes = [];
-			this.camera = null;
-			this.cameraTarget = new THREE.Object3D();
-			this.cameraTarget.name = 'target';
-			this.audio = null;
-			this.audioManager = null;
-			this.objects = new WeakMap();
-			this.configuration = {
-				sync: params.sync !== undefined ? params.sync : true,
-				afterglow: params.afterglow !== undefined ? params.afterglow : 0.0,
-				resetPhysicsOnLoop: params.resetPhysicsOnLoop !== undefined ? params.resetPhysicsOnLoop : true,
-				pmxAnimation: params.pmxAnimation !== undefined ? params.pmxAnimation : false
-			};
-			this.enabled = {
-				animation: true,
-				ik: true,
-				grant: true,
-				physics: true,
-				cameraAnimation: true
-			};
-			this.onBeforePhysics = function /* mesh */ () {};
-
-			// experimental
-			this.sharedPhysics = false;
-			this.masterPhysics = null;
-
-		}
-
-		/**
-   * 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( object, 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( 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( delta ) {
-
-			if ( this.audioManager !== null ) this.audioManager.control( delta );
-			for ( let 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( mesh, vpd, params = {} ) {
-
-			if ( params.resetPose !== false ) mesh.pose();
-			const bones = mesh.skeleton.bones;
-			const boneParams = vpd.bones;
-			const boneNameDictionary = {};
-			for ( let i = 0, il = bones.length; i < il; i ++ ) {
-
-				boneNameDictionary[ bones[ i ].name ] = i;
-
-			}
-
-			const vector = new THREE.Vector3();
-			const quaternion = new THREE.Quaternion();
-			for ( let i = 0, il = boneParams.length; i < il; i ++ ) {
-
-				const boneParam = boneParams[ i ];
-				const boneIndex = boneNameDictionary[ boneParam.name ];
-				if ( boneIndex === undefined ) continue;
-				const bone = bones[ boneIndex ];
-				bone.position.add( vector.fromArray( boneParam.translation ) );
-				bone.quaternion.multiply( quaternion.fromArray( boneParam.quaternion ) );
-
-			}
-
-			mesh.updateMatrixWorld( true );
-
-			// PMX animation system special path
-			if ( this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === 'pmx' ) {
-
-				const sortedBonesData = this._sortBoneDataArray( mesh.geometry.userData.MMD.bones.slice() );
-				const ikSolver = params.ik !== false ? this._createCCDIKSolver( mesh ) : null;
-				const grantSolver = params.grant !== false ? this.createGrantSolver( mesh ) : null;
-				this._animatePMXMesh( mesh, sortedBonesData, ikSolver, grantSolver );
-
-			} else {
-
-				if ( params.ik !== false ) {
-
-					this._createCCDIKSolver( mesh ).update();
-
-				}
-
-				if ( params.grant !== false ) {
-
-					this.createGrantSolver( mesh ).update();
-
-				}
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Enabes/Disables an animation feature.
-   *
-   * @param {string} key
-   * @param {boolean} enabled
-   * @return {MMDAnimationHelper}
-   */
-		enable( key, enabled ) {
-
-			if ( this.enabled[ key ] === undefined ) {
-
-				throw new Error( 'THREE.MMDAnimationHelper.enable: ' + 'unknown key ' + key );
-
-			}
-
-			this.enabled[ key ] = enabled;
-			if ( key === 'physics' ) {
-
-				for ( let 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( mesh ) {
-
-			return new GrantSolver( mesh, mesh.geometry.userData.MMD.grants );
-
-		}
-
-		// private methods
-
-		_addMesh( 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( 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( 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( mesh ) {
-
-			let found = false;
-			let writeIndex = 0;
-			for ( let 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( 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( 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( mesh, animation ) {
-
-			const objects = this.objects.get( mesh );
-			if ( animation !== undefined ) {
-
-				const animations = Array.isArray( animation ) ? animation : [ animation ];
-				objects.mixer = new THREE.AnimationMixer( mesh );
-				for ( let 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 ) {
-
-					const 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( camera, animation ) {
-
-			const animations = Array.isArray( animation ) ? animation : [ animation ];
-			const objects = this.objects.get( camera );
-			objects.mixer = new THREE.AnimationMixer( camera );
-			for ( let i = 0, il = animations.length; i < il; i ++ ) {
-
-				objects.mixer.clipAction( animations[ i ] ).play();
-
-			}
-
-		}
-		_setupMeshPhysics( mesh, params ) {
-
-			const objects = this.objects.get( mesh );
-
-			// shared physics is experimental
-
-			if ( params.world === undefined && this.sharedPhysics ) {
-
-				const 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( mesh, delta ) {
-
-			const objects = this.objects.get( mesh );
-			const mixer = objects.mixer;
-			const ikSolver = objects.ikSolver;
-			const grantSolver = objects.grantSolver;
-			const physics = objects.physics;
-			const looped = objects.looped;
-			if ( mixer && this.enabled.animation ) {
-
-				// alternate solution to save/restore bones but less performant?
-				//mesh.pose();
-				//this._updatePropertyMixersBuffer( mesh );
-
-				this._restoreBones( mesh );
-				mixer.update( delta );
-				this._saveBones( mesh );
-
-				// PMX animation system special path
-				if ( this.configuration.pmxAnimation && mesh.geometry.userData.MMD && mesh.geometry.userData.MMD.format === 'pmx' ) {
-
-					if ( ! objects.sortedBonesData ) objects.sortedBonesData = this._sortBoneDataArray( mesh.geometry.userData.MMD.bones.slice() );
-					this._animatePMXMesh( mesh, objects.sortedBonesData, ikSolver && this.enabled.ik ? ikSolver : null, grantSolver && this.enabled.grant ? grantSolver : null );
-
-				} else {
-
-					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 );
-
-			}
-
-		}
-
-		// Sort bones in order by 1. transformationClass and 2. bone index.
-		// In PMX animation system, bone transformations should be processed
-		// in this order.
-		_sortBoneDataArray( boneDataArray ) {
-
-			return boneDataArray.sort( function ( a, b ) {
-
-				if ( a.transformationClass !== b.transformationClass ) {
-
-					return a.transformationClass - b.transformationClass;
-
-				} else {
-
-					return a.index - b.index;
-
-				}
-
-			} );
-
-		}
-
-		// PMX Animation system is a bit too complex and doesn't great match to
-		// Three.js Animation system. This method attempts to simulate it as much as
-		// possible but doesn't perfectly simulate.
-		// This method is more costly than the regular one so
-		// you are recommended to set constructor parameter "pmxAnimation: true"
-		// only if your PMX model animation doesn't work well.
-		// If you need better method you would be required to write your own.
-		_animatePMXMesh( mesh, sortedBonesData, ikSolver, grantSolver ) {
-
-			_quaternionIndex = 0;
-			_grantResultMap.clear();
-			for ( let i = 0, il = sortedBonesData.length; i < il; i ++ ) {
-
-				updateOne( mesh, sortedBonesData[ i ].index, ikSolver, grantSolver );
-
-			}
-
-			mesh.updateMatrixWorld( true );
-			return this;
-
-		}
-		_animateCamera( camera, delta ) {
-
-			const 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( mesh, physicsEnabled ) {
-
-			const iks = mesh.geometry.userData.MMD.iks;
-			const bones = mesh.geometry.userData.MMD.bones;
-			for ( let i = 0, il = iks.length; i < il; i ++ ) {
-
-				const ik = iks[ i ];
-				const links = ik.links;
-				for ( let j = 0, jl = links.length; j < jl; j ++ ) {
-
-					const 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( mesh ) {
-
-			if ( THREE.CCDIKSolver === undefined ) {
-
-				throw new Error( 'THREE.MMDAnimationHelper: Import THREE.CCDIKSolver.' );
-
-			}
-
-			return new THREE.CCDIKSolver( mesh, mesh.geometry.userData.MMD.iks );
-
-		}
-		_createMMDPhysics( mesh, params ) {
-
-			if ( THREE.MMDPhysics === undefined ) {
-
-				throw new Error( 'THREE.MMDPhysics: Import THREE.MMDPhysics.' );
-
-			}
-
-			return new THREE.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() {
-
-			let max = 0.0;
-			const objects = this.objects;
-			const meshes = this.meshes;
-			const camera = this.camera;
-			const audioManager = this.audioManager;
-
-			// get the longest duration
-
-			for ( let i = 0, il = meshes.length; i < il; i ++ ) {
-
-				const mixer = this.objects.get( meshes[ i ] ).mixer;
-				if ( mixer === undefined ) continue;
-				for ( let j = 0; j < mixer._actions.length; j ++ ) {
-
-					const 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 ) {
-
-				const mixer = this.objects.get( camera ).mixer;
-				if ( mixer !== undefined ) {
-
-					for ( let i = 0, il = mixer._actions.length; i < il; i ++ ) {
-
-						const 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 ( let i = 0, il = this.meshes.length; i < il; i ++ ) {
-
-				const mixer = this.objects.get( this.meshes[ i ] ).mixer;
-				if ( mixer === undefined ) continue;
-				for ( let j = 0, jl = mixer._actions.length; j < jl; j ++ ) {
-
-					mixer._actions[ j ]._clip.duration = max;
-
-				}
-
-			}
-
-			if ( camera !== null ) {
-
-				const mixer = this.objects.get( camera ).mixer;
-				if ( mixer !== undefined ) {
-
-					for ( let i = 0, il = mixer._actions.length; i < il; i ++ ) {
-
-						mixer._actions[ i ]._clip.duration = max;
-
-					}
-
-				}
-
-			}
-
-			if ( audioManager !== null ) {
-
-				audioManager.duration = max;
-
-			}
-
-		}
-
-		// workaround
-
-		_updatePropertyMixersBuffer( mesh ) {
-
-			const mixer = this.objects.get( mesh ).mixer;
-			const propertyMixers = mixer._bindings;
-			const accuIndex = mixer._accuIndex;
-			for ( let i = 0, il = propertyMixers.length; i < il; i ++ ) {
-
-				const propertyMixer = propertyMixers[ i ];
-				const buffer = propertyMixer.buffer;
-				const stride = propertyMixer.valueSize;
-				const 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 THREE.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( mesh ) {
-
-			const objects = this.objects.get( mesh );
-			const bones = mesh.skeleton.bones;
-			let backupBones = objects.backupBones;
-			if ( backupBones === undefined ) {
-
-				backupBones = new Float32Array( bones.length * 7 );
-				objects.backupBones = backupBones;
-
-			}
-
-			for ( let i = 0, il = bones.length; i < il; i ++ ) {
-
-				const bone = bones[ i ];
-				bone.position.toArray( backupBones, i * 7 );
-				bone.quaternion.toArray( backupBones, i * 7 + 3 );
-
-			}
-
-		}
-		_restoreBones( mesh ) {
-
-			const objects = this.objects.get( mesh );
-			const backupBones = objects.backupBones;
-			if ( backupBones === undefined ) return;
-			const bones = mesh.skeleton.bones;
-			for ( let i = 0, il = bones.length; i < il; i ++ ) {
-
-				const bone = bones[ i ];
-				bone.position.fromArray( backupBones, i * 7 );
-				bone.quaternion.fromArray( backupBones, i * 7 + 3 );
-
-			}
-
-		}
-
-		// experimental
-
-		_getMasterPhysics() {
-
-			if ( this.masterPhysics !== null ) return this.masterPhysics;
-			for ( let i = 0, il = this.meshes.length; i < il; i ++ ) {
-
-				const physics = this.meshes[ i ].physics;
-				if ( physics !== undefined && physics !== null ) {
-
-					this.masterPhysics = physics;
-					return this.masterPhysics;
-
-				}
-
-			}
-
-			return null;
-
-		}
-		_updateSharedPhysics( delta ) {
-
-			if ( this.meshes.length === 0 || ! this.enabled.physics || ! this.sharedPhysics ) return;
-			const physics = this._getMasterPhysics();
-			if ( physics === null ) return;
-			for ( let i = 0, il = this.meshes.length; i < il; i ++ ) {
-
-				const p = this.meshes[ i ].physics;
-				if ( p !== null && p !== undefined ) {
-
-					p.updateRigidBodies();
-
-				}
-
-			}
-
-			physics.stepSimulation( delta );
-			for ( let i = 0, il = this.meshes.length; i < il; i ++ ) {
-
-				const p = this.meshes[ i ].physics;
-				if ( p !== null && p !== undefined ) {
-
-					p.updateBones();
-
-				}
-
-			}
-
-		}
-
-	}
-
-	// Keep working quaternions for less GC
-	const _quaternions = [];
-	let _quaternionIndex = 0;
-	function getQuaternion() {
-
-		if ( _quaternionIndex >= _quaternions.length ) {
-
-			_quaternions.push( new THREE.Quaternion() );
-
-		}
-
-		return _quaternions[ _quaternionIndex ++ ];
-
-	}
-
-	// Save rotation whose grant and IK are already applied
-	// used by grant children
-	const _grantResultMap = new Map();
-	function updateOne( mesh, boneIndex, ikSolver, grantSolver ) {
-
-		const bones = mesh.skeleton.bones;
-		const bonesData = mesh.geometry.userData.MMD.bones;
-		const boneData = bonesData[ boneIndex ];
-		const bone = bones[ boneIndex ];
-
-		// Return if already updated by being referred as a grant parent.
-		if ( _grantResultMap.has( boneIndex ) ) return;
-		const quaternion = getQuaternion();
-
-		// Initialize grant result here to prevent infinite loop.
-		// If it's referred before updating with actual result later
-		// result without applyting IK or grant is gotten
-		// but better than composing of infinite loop.
-		_grantResultMap.set( boneIndex, quaternion.copy( bone.quaternion ) );
-
-		// @TODO: Support global grant and grant position
-		if ( grantSolver && boneData.grant && ! boneData.grant.isLocal && boneData.grant.affectRotation ) {
-
-			const parentIndex = boneData.grant.parentIndex;
-			const ratio = boneData.grant.ratio;
-			if ( ! _grantResultMap.has( parentIndex ) ) {
-
-				updateOne( mesh, parentIndex, ikSolver, grantSolver );
-
-			}
-
-			grantSolver.addGrantRotation( bone, _grantResultMap.get( parentIndex ), ratio );
-
-		}
-
-		if ( ikSolver && boneData.ik ) {
-
-			// @TODO: Updating world matrices every time solving an IK bone is
-			// costly. Optimize if possible.
-			mesh.updateMatrixWorld( true );
-			ikSolver.updateOne( boneData.ik );
-
-			// No confident, but it seems the grant results with ik links should be updated?
-			const links = boneData.ik.links;
-			for ( let i = 0, il = links.length; i < il; i ++ ) {
-
-				const link = links[ i ];
-				if ( link.enabled === false ) continue;
-				const linkIndex = link.index;
-				if ( _grantResultMap.has( linkIndex ) ) {
-
-					_grantResultMap.set( linkIndex, _grantResultMap.get( linkIndex ).copy( bones[ linkIndex ].quaternion ) );
-
-				}
-
-			}
-
-		}
-
-		// Update with the actual result here
-		quaternion.copy( bone.quaternion );
-
-	}
-
-	//
-
-	class AudioManager {
-
-		/**
-   * @param {THREE.Audio} audio
-   * @param {Object} params - (optional)
-   * @param {Nuumber} params.delayTime
-   */
-		constructor( audio, 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;
-
-		}
-
-		/**
-   * @param {Number} delta
-   * @return {AudioManager}
-   */
-		control( delta ) {
-
-			this.elapsed += delta;
-			this.currentTime += delta;
-			if ( this._shouldStopAudio() ) this.audio.stop();
-			if ( this._shouldStartAudio() ) this.audio.play();
-			return this;
-
-		}
-
-		// private methods
-
-		_shouldStartAudio() {
-
-			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;
-			return true;
-
-		}
-		_shouldStopAudio() {
-
-			return this.audio.isPlaying && this.currentTime >= this.duration;
-
-		}
-
-	}
-	const _q = new THREE.Quaternion();
-
-	/**
- * Solver for Grant (Fuyo in Japanese. I just google translated because
- * Fuyo may be MMD specific term and may not be common word in 3D CG terms.)
- * Grant propagates a bone's transform to other bones transforms even if
- * they are not children.
- * @param {THREE.SkinnedMesh} mesh
- * @param {Array<Object>} grants
- */
-	class GrantSolver {
-
-		constructor( mesh, grants = [] ) {
-
-			this.mesh = mesh;
-			this.grants = grants;
-
-		}
-
-		/**
-   * Solve all the grant bones
-   * @return {GrantSolver}
-   */
-		update() {
-
-			const grants = this.grants;
-			for ( let i = 0, il = grants.length; i < il; i ++ ) {
-
-				this.updateOne( grants[ i ] );
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Solve a grant bone
-   * @param {Object} grant - grant parameter
-   * @return {GrantSolver}
-   */
-		updateOne( grant ) {
-
-			const bones = this.mesh.skeleton.bones;
-			const bone = bones[ grant.index ];
-			const 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 ) {
-
-					this.addGrantRotation( bone, parentBone.quaternion, grant.ratio );
-
-				}
-
-			}
-
-			return this;
-
-		}
-		addGrantRotation( bone, q, ratio ) {
-
-			_q.set( 0, 0, 0, 1 );
-			_q.slerp( q, ratio );
-			bone.quaternion.multiply( _q );
-			return this;
-
-		}
-
-	}
-
-	THREE.MMDAnimationHelper = MMDAnimationHelper;
-
-} )();

+ 0 - 1174
examples/js/animation/MMDPhysics.js

@@ -1,1174 +0,0 @@
-( function () {
-
-	/**
- * 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
- */
-
-	/* global Ammo */
-
-	class MMDPhysics {
-
-		/**
-   * @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 )
-   */
-		constructor( mesh, rigidBodyParams, constraintParams = [], params = {} ) {
-
-			if ( typeof Ammo === 'undefined' ) {
-
-				throw new Error( 'THREE.MMDPhysics: Import ammo.js https://github.com/kripken/ammo.js' );
-
-			}
-
-			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 THREE.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 );
-
-		}
-
-		/**
-   * Advances Physics calculation and updates bones.
-   *
-   * @param {Number} delta - time in second
-   * @return {MMDPhysics}
-   */
-		update( delta ) {
-
-			const manager = this.manager;
-			const 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.
-
-			let isNonDefaultScale = false;
-			const position = manager.allocThreeVector3();
-			const quaternion = manager.allocThreeQuaternion();
-			const scale = manager.allocThreeVector3();
-			mesh.matrixWorld.decompose( position, quaternion, scale );
-			if ( scale.x !== 1 || scale.y !== 1 || scale.z !== 1 ) {
-
-				isNonDefaultScale = true;
-
-			}
-
-			let 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() {
-
-			for ( let 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( cycles ) {
-
-			for ( let i = 0; i < cycles; i ++ ) {
-
-				this.update( 1 / 60 );
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Sets gravity.
-   *
-   * @param {Vector3} gravity
-   * @return {MMDPhysicsHelper}
-   */
-		setGravity( gravity ) {
-
-			this.world.setGravity( new Ammo.btVector3( gravity.x, gravity.y, gravity.z ) );
-			this.gravity.copy( gravity );
-			return this;
-
-		}
-
-		/**
-   * Creates MMDPhysicsHelper
-   *
-   * @return {MMDPhysicsHelper}
-   */
-		createHelper() {
-
-			return new MMDPhysicsHelper( this.mesh, this );
-
-		}
-
-		// private methods
-
-		_init( mesh, rigidBodyParams, constraintParams ) {
-
-			const 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)
-
-			const parent = mesh.parent;
-			if ( parent !== null ) mesh.parent = null;
-			const currentPosition = manager.allocThreeVector3();
-			const currentQuaternion = manager.allocThreeQuaternion();
-			const 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() {
-
-			const config = new Ammo.btDefaultCollisionConfiguration();
-			const dispatcher = new Ammo.btCollisionDispatcher( config );
-			const cache = new Ammo.btDbvtBroadphase();
-			const solver = new Ammo.btSequentialImpulseConstraintSolver();
-			const world = new Ammo.btDiscreteDynamicsWorld( dispatcher, cache, solver, config );
-			return world;
-
-		}
-		_initRigidBodies( rigidBodies ) {
-
-			for ( let i = 0, il = rigidBodies.length; i < il; i ++ ) {
-
-				this.bodies.push( new RigidBody( this.mesh, this.world, rigidBodies[ i ], this.manager ) );
-
-			}
-
-		}
-		_initConstraints( constraints ) {
-
-			for ( let i = 0, il = constraints.length; i < il; i ++ ) {
-
-				const params = constraints[ i ];
-				const bodyA = this.bodies[ params.rigidBodyIndex1 ];
-				const bodyB = this.bodies[ params.rigidBodyIndex2 ];
-				this.constraints.push( new Constraint( this.mesh, this.world, bodyA, bodyB, params, this.manager ) );
-
-			}
-
-		}
-		_stepSimulation( delta ) {
-
-			const unitStep = this.unitStep;
-			let stepTime = delta;
-			let 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() {
-
-			for ( let i = 0, il = this.bodies.length; i < il; i ++ ) {
-
-				this.bodies[ i ].updateFromBone();
-
-			}
-
-		}
-		_updateBones() {
-
-			for ( let 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.
- */
-	class ResourceManager {
-
-		constructor() {
-
-			// for Three.js
-			this.threeVector3s = [];
-			this.threeMatrix4s = [];
-			this.threeQuaternions = [];
-			this.threeEulers = [];
-
-			// for Ammo.js
-			this.transforms = [];
-			this.quaternions = [];
-			this.vector3s = [];
-
-		}
-		allocThreeVector3() {
-
-			return this.threeVector3s.length > 0 ? this.threeVector3s.pop() : new THREE.Vector3();
-
-		}
-		freeThreeVector3( v ) {
-
-			this.threeVector3s.push( v );
-
-		}
-		allocThreeMatrix4() {
-
-			return this.threeMatrix4s.length > 0 ? this.threeMatrix4s.pop() : new THREE.Matrix4();
-
-		}
-		freeThreeMatrix4( m ) {
-
-			this.threeMatrix4s.push( m );
-
-		}
-		allocThreeQuaternion() {
-
-			return this.threeQuaternions.length > 0 ? this.threeQuaternions.pop() : new THREE.Quaternion();
-
-		}
-		freeThreeQuaternion( q ) {
-
-			this.threeQuaternions.push( q );
-
-		}
-		allocThreeEuler() {
-
-			return this.threeEulers.length > 0 ? this.threeEulers.pop() : new THREE.Euler();
-
-		}
-		freeThreeEuler( e ) {
-
-			this.threeEulers.push( e );
-
-		}
-		allocTransform() {
-
-			return this.transforms.length > 0 ? this.transforms.pop() : new Ammo.btTransform();
-
-		}
-		freeTransform( t ) {
-
-			this.transforms.push( t );
-
-		}
-		allocQuaternion() {
-
-			return this.quaternions.length > 0 ? this.quaternions.pop() : new Ammo.btQuaternion();
-
-		}
-		freeQuaternion( q ) {
-
-			this.quaternions.push( q );
-
-		}
-		allocVector3() {
-
-			return this.vector3s.length > 0 ? this.vector3s.pop() : new Ammo.btVector3();
-
-		}
-		freeVector3( v ) {
-
-			this.vector3s.push( v );
-
-		}
-		setIdentity( t ) {
-
-			t.setIdentity();
-
-		}
-		getBasis( t ) {
-
-			var q = this.allocQuaternion();
-			t.getBasis().getRotation( q );
-			return q;
-
-		}
-		getBasisAsMatrix3( t ) {
-
-			var q = this.getBasis( t );
-			var m = this.quaternionToMatrix3( q );
-			this.freeQuaternion( q );
-			return m;
-
-		}
-		getOrigin( t ) {
-
-			return t.getOrigin();
-
-		}
-		setOrigin( t, v ) {
-
-			t.getOrigin().setValue( v.x(), v.y(), v.z() );
-
-		}
-		copyOrigin( t1, t2 ) {
-
-			var o = t2.getOrigin();
-			this.setOrigin( t1, o );
-
-		}
-		setBasis( t, q ) {
-
-			t.setRotation( q );
-
-		}
-		setBasisFromMatrix3( t, m ) {
-
-			var q = this.matrix3ToQuaternion( m );
-			this.setBasis( t, q );
-			this.freeQuaternion( q );
-
-		}
-		setOriginFromArray3( t, a ) {
-
-			t.getOrigin().setValue( a[ 0 ], a[ 1 ], a[ 2 ] );
-
-		}
-		setOriginFromThreeVector3( t, v ) {
-
-			t.getOrigin().setValue( v.x, v.y, v.z );
-
-		}
-		setBasisFromArray3( 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( 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( 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( 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( 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( v1, v2 ) {
-
-			var v = this.allocVector3();
-			v.setValue( v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z() );
-			return v;
-
-		}
-		dotVectors3( v1, v2 ) {
-
-			return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z();
-
-		}
-		rowOfMatrix3( m, i ) {
-
-			var v = this.allocVector3();
-			v.setValue( m[ i * 3 + 0 ], m[ i * 3 + 1 ], m[ i * 3 + 2 ] );
-			return v;
-
-		}
-		columnOfMatrix3( m, i ) {
-
-			var v = this.allocVector3();
-			v.setValue( m[ i + 0 ], m[ i + 3 ], m[ i + 6 ] );
-			return v;
-
-		}
-		negativeVector3( v ) {
-
-			var v2 = this.allocVector3();
-			v2.setValue( - v.x(), - v.y(), - v.z() );
-			return v2;
-
-		}
-		multiplyMatrix3ByVector3( 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( 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( 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( 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
- */
-	class RigidBody {
-
-		constructor( 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();
-
-		}
-
-		/**
-   * Resets rigid body transform to the current bone's.
-   *
-   * @return {RigidBody}
-   */
-		reset() {
-
-			this._setTransformFromBone();
-			return this;
-
-		}
-
-		/**
-   * Updates rigid body's transform from the current bone.
-   *
-   * @return {RidigBody}
-   */
-		updateFromBone() {
-
-			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() {
-
-			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 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 new Error( 'unknown shape type ' + p.shapeType );
-
-				}
-
-			}
-
-			const manager = this.manager;
-			const params = this.params;
-			const bones = this.mesh.skeleton.bones;
-			const bone = params.boneIndex === - 1 ? new THREE.Bone() : bones[ params.boneIndex ];
-			const shape = generateShape( params );
-			const weight = params.type === 0 ? 0 : params.weight;
-			const localInertia = manager.allocVector3();
-			localInertia.setValue( 0, 0, 0 );
-			if ( weight !== 0 ) {
-
-				shape.calculateLocalInertia( weight, localInertia );
-
-			}
-
-			const boneOffsetForm = manager.allocTransform();
-			manager.setIdentity( boneOffsetForm );
-			manager.setOriginFromArray3( boneOffsetForm, params.position );
-			manager.setBasisFromArray3( boneOffsetForm, params.rotation );
-			const vector = manager.allocThreeVector3();
-			const boneForm = manager.allocTransform();
-			manager.setIdentity( boneForm );
-			manager.setOriginFromThreeVector3( boneForm, bone.getWorldPosition( vector ) );
-			const form = manager.multiplyTransforms( boneForm, boneOffsetForm );
-			const state = new Ammo.btDefaultMotionState( form );
-			const info = new Ammo.btRigidBodyConstructionInfo( weight, state, shape, localInertia );
-			info.set_m_friction( params.friction );
-			info.set_m_restitution( params.restitution );
-			const 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() {
-
-			const manager = this.manager;
-			const p = manager.allocThreeVector3();
-			const q = manager.allocThreeQuaternion();
-			const s = manager.allocThreeVector3();
-			this.bone.matrixWorld.decompose( p, q, s );
-			const tr = manager.allocTransform();
-			manager.setOriginFromThreeVector3( tr, p );
-			manager.setBasisFromThreeQuaternion( tr, q );
-			const form = manager.multiplyTransforms( tr, this.boneOffsetForm );
-			manager.freeTransform( tr );
-			manager.freeThreeVector3( s );
-			manager.freeThreeQuaternion( q );
-			manager.freeThreeVector3( p );
-			return form;
-
-		}
-		_getWorldTransformForBone() {
-
-			const manager = this.manager;
-			const tr = this.body.getCenterOfMassTransform();
-			return manager.multiplyTransforms( tr, this.boneOffsetFormInverse );
-
-		}
-		_setTransformFromBone() {
-
-			const manager = this.manager;
-			const 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() {
-
-			const manager = this.manager;
-			const form = this._getBoneTransform();
-			const 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() {
-
-			const manager = this.manager;
-			const tr = this._getWorldTransformForBone();
-			const q = manager.getBasis( tr );
-			const thQ = manager.allocThreeQuaternion();
-			const thQ2 = manager.allocThreeQuaternion();
-			const 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() {
-
-			const manager = this.manager;
-			const tr = this._getWorldTransformForBone();
-			const thV = manager.allocThreeVector3();
-			const 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 );
-
-		}
-
-	}
-
-	//
-
-	class Constraint {
-
-		/**
-   * @param {THREE.SkinnedMesh} mesh
-   * @param {Ammo.btDiscreteDynamicsWorld} world
-   * @param {RigidBody} bodyA
-   * @param {RigidBody} bodyB
-   * @param {Object} params
-   * @param {ResourceManager} manager
-   */
-		constructor( 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();
-
-		}
-
-		// private method
-
-		_init() {
-
-			const manager = this.manager;
-			const params = this.params;
-			const bodyA = this.bodyA;
-			const bodyB = this.bodyB;
-			const form = manager.allocTransform();
-			manager.setIdentity( form );
-			manager.setOriginFromArray3( form, params.position );
-			manager.setBasisFromArray3( form, params.rotation );
-			const formA = manager.allocTransform();
-			const formB = manager.allocTransform();
-			bodyA.body.getMotionState().getWorldTransform( formA );
-			bodyB.body.getMotionState().getWorldTransform( formB );
-			const formInverseA = manager.inverseTransform( formA );
-			const formInverseB = manager.inverseTransform( formB );
-			const formA2 = manager.multiplyTransforms( formInverseA, form );
-			const formB2 = manager.multiplyTransforms( formInverseB, form );
-			const constraint = new Ammo.btGeneric6DofSpringConstraint( bodyA.body, bodyB.body, formA2, formB2, true );
-			const lll = manager.allocVector3();
-			const lul = manager.allocVector3();
-			const all = manager.allocVector3();
-			const 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 ( let i = 0; i < 3; i ++ ) {
-
-				if ( params.springPosition[ i ] !== 0 ) {
-
-					constraint.enableSpring( i, true );
-					constraint.setStiffness( i, params.springPosition[ i ] );
-
-				}
-
-			}
-
-			for ( let 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 ( let i = 0; i < 6; i ++ ) {
-
-					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 );
-
-		}
-
-	}
-
-	//
-
-	const _position = new THREE.Vector3();
-	const _quaternion = new THREE.Quaternion();
-	const _scale = new THREE.Vector3();
-	const _matrixWorldInv = new THREE.Matrix4();
-	class MMDPhysicsHelper extends THREE.Object3D {
-
-		/**
-   * Visualize Rigid bodies
-   *
-   * @param {THREE.SkinnedMesh} mesh
-   * @param {Physics} physics
-   */
-		constructor( mesh, physics ) {
-
-			super();
-			this.root = mesh;
-			this.physics = physics;
-			this.matrix.copy( mesh.matrixWorld );
-			this.matrixAutoUpdate = false;
-			this.materials = [];
-			this.materials.push( new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0xff8888 ),
-				wireframe: true,
-				depthTest: false,
-				depthWrite: false,
-				opacity: 0.25,
-				transparent: true
-			} ) );
-			this.materials.push( new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0x88ff88 ),
-				wireframe: true,
-				depthTest: false,
-				depthWrite: false,
-				opacity: 0.25,
-				transparent: true
-			} ) );
-			this.materials.push( new THREE.MeshBasicMaterial( {
-				color: new THREE.Color( 0x8888ff ),
-				wireframe: true,
-				depthTest: false,
-				depthWrite: false,
-				opacity: 0.25,
-				transparent: true
-			} ) );
-			this._init();
-
-		}
-
-		/**
-   * Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
-   */
-		dispose() {
-
-			const materials = this.materials;
-			const children = this.children;
-			for ( let i = 0; i < materials.length; i ++ ) {
-
-				materials[ i ].dispose();
-
-			}
-
-			for ( let i = 0; i < children.length; i ++ ) {
-
-				const child = children[ i ];
-				if ( child.isMesh ) child.geometry.dispose();
-
-			}
-
-		}
-
-		/**
-   * Updates Rigid Bodies visualization.
-   */
-		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 ) ).invert();
-				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 ) );
-			super.updateMatrixWorld( force );
-
-		}
-
-		// private method
-
-		_init() {
-
-			var bodies = this.physics.bodies;
-			function createGeometry( param ) {
-
-				switch ( param.shapeType ) {
-
-					case 0:
-						return new THREE.SphereGeometry( param.width, 16, 8 );
-					case 1:
-						return new THREE.BoxGeometry( param.width * 2, param.height * 2, param.depth * 2, 8, 8, 8 );
-					case 2:
-						return new THREE.CapsuleGeometry( param.width, param.height, 8, 16 );
-					default:
-						return null;
-
-				}
-
-			}
-
-			for ( var i = 0, il = bodies.length; i < il; i ++ ) {
-
-				var param = bodies[ i ].params;
-				this.add( new THREE.Mesh( createGeometry( param ), this.materials[ param.type ] ) );
-
-			}
-
-		}
-
-	}
-
-	THREE.MMDPhysics = MMDPhysics;
-
-} )();

+ 0 - 168
examples/js/cameras/CinematicCamera.js

@@ -1,168 +0,0 @@
-( function () {
-
-	class CinematicCamera extends THREE.PerspectiveCamera {
-
-		constructor( fov, aspect, near, far ) {
-
-			super( fov, aspect, near, far );
-			this.type = 'CinematicCamera';
-			this.postprocessing = {
-				enabled: true
-			};
-			this.shaderSettings = {
-				rings: 3,
-				samples: 4
-			};
-			const depthShader = THREE.BokehDepthShader;
-			this.materialDepth = new THREE.ShaderMaterial( {
-				uniforms: depthShader.uniforms,
-				vertexShader: depthShader.vertexShader,
-				fragmentShader: depthShader.fragmentShader
-			} );
-			this.materialDepth.uniforms[ 'mNear' ].value = near;
-			this.materialDepth.uniforms[ 'mFar' ].value = far;
-
-			// In case of cinematicCamera, having a default lens set is important
-			this.setLens();
-			this.initPostProcessing();
-
-		}
-
-		// providing fnumber and coc(Circle of Confusion) as extra arguments
-		// In case of cinematicCamera, having a default lens set is important
-		// if fnumber and coc are not provided, cinematicCamera tries to act as a basic THREE.PerspectiveCamera
-		setLens( focalLength = 35, filmGauge = 35, fNumber = 8, coc = 0.019 ) {
-
-			this.filmGauge = filmGauge;
-			this.setFocalLength( focalLength );
-			this.fNumber = fNumber;
-			this.coc = coc;
-
-			// fNumber is focalLength by aperture
-			this.aperture = focalLength / this.fNumber;
-
-			// hyperFocal is required to calculate depthOfField when a lens tries to focus at a distance with given fNumber and focalLength
-			this.hyperFocal = focalLength * focalLength / ( this.aperture * this.coc );
-
-		}
-		linearize( depth ) {
-
-			const zfar = this.far;
-			const znear = this.near;
-			return - zfar * znear / ( depth * ( zfar - znear ) - zfar );
-
-		}
-		smoothstep( near, far, depth ) {
-
-			const x = this.saturate( ( depth - near ) / ( far - near ) );
-			return x * x * ( 3 - 2 * x );
-
-		}
-		saturate( x ) {
-
-			return Math.max( 0, Math.min( 1, x ) );
-
-		}
-
-		// function for focusing at a distance from the camera
-		focusAt( focusDistance = 20 ) {
-
-			const focalLength = this.getFocalLength();
-
-			// distance from the camera (normal to frustrum) to focus on
-			this.focus = focusDistance;
-
-			// the nearest point from the camera which is in focus (unused)
-			this.nearPoint = this.hyperFocal * this.focus / ( this.hyperFocal + ( this.focus - focalLength ) );
-
-			// the farthest point from the camera which is in focus (unused)
-			this.farPoint = this.hyperFocal * this.focus / ( this.hyperFocal - ( this.focus - focalLength ) );
-
-			// the gap or width of the space in which is everything is in focus (unused)
-			this.depthOfField = this.farPoint - this.nearPoint;
-
-			// Considering minimum distance of focus for a standard lens (unused)
-			if ( this.depthOfField < 0 ) this.depthOfField = 0;
-			this.sdistance = this.smoothstep( this.near, this.far, this.focus );
-			this.ldistance = this.linearize( 1 - this.sdistance );
-			this.postprocessing.bokeh_uniforms[ 'focalDepth' ].value = this.ldistance;
-
-		}
-		initPostProcessing() {
-
-			if ( this.postprocessing.enabled ) {
-
-				this.postprocessing.scene = new THREE.Scene();
-				this.postprocessing.camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 10000, 10000 );
-				this.postprocessing.scene.add( this.postprocessing.camera );
-				this.postprocessing.rtTextureDepth = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
-				this.postprocessing.rtTextureColor = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight );
-				const bokeh_shader = THREE.BokehShader;
-				this.postprocessing.bokeh_uniforms = THREE.UniformsUtils.clone( bokeh_shader.uniforms );
-				this.postprocessing.bokeh_uniforms[ 'tColor' ].value = this.postprocessing.rtTextureColor.texture;
-				this.postprocessing.bokeh_uniforms[ 'tDepth' ].value = this.postprocessing.rtTextureDepth.texture;
-				this.postprocessing.bokeh_uniforms[ 'manualdof' ].value = 0;
-				this.postprocessing.bokeh_uniforms[ 'shaderFocus' ].value = 0;
-				this.postprocessing.bokeh_uniforms[ 'fstop' ].value = 2.8;
-				this.postprocessing.bokeh_uniforms[ 'showFocus' ].value = 1;
-				this.postprocessing.bokeh_uniforms[ 'focalDepth' ].value = 0.1;
-
-				//console.log( this.postprocessing.bokeh_uniforms[ "focalDepth" ].value );
-
-				this.postprocessing.bokeh_uniforms[ 'znear' ].value = this.near;
-				this.postprocessing.bokeh_uniforms[ 'zfar' ].value = this.near;
-				this.postprocessing.bokeh_uniforms[ 'textureWidth' ].value = window.innerWidth;
-				this.postprocessing.bokeh_uniforms[ 'textureHeight' ].value = window.innerHeight;
-				this.postprocessing.materialBokeh = new THREE.ShaderMaterial( {
-					uniforms: this.postprocessing.bokeh_uniforms,
-					vertexShader: bokeh_shader.vertexShader,
-					fragmentShader: bokeh_shader.fragmentShader,
-					defines: {
-						RINGS: this.shaderSettings.rings,
-						SAMPLES: this.shaderSettings.samples,
-						DEPTH_PACKING: 1
-					}
-				} );
-				this.postprocessing.quad = new THREE.Mesh( new THREE.PlaneGeometry( window.innerWidth, window.innerHeight ), this.postprocessing.materialBokeh );
-				this.postprocessing.quad.position.z = - 500;
-				this.postprocessing.scene.add( this.postprocessing.quad );
-
-			}
-
-		}
-		renderCinematic( scene, renderer ) {
-
-			if ( this.postprocessing.enabled ) {
-
-				const currentRenderTarget = renderer.getRenderTarget();
-				renderer.clear();
-
-				// Render scene into texture
-
-				scene.overrideMaterial = null;
-				renderer.setRenderTarget( this.postprocessing.rtTextureColor );
-				renderer.clear();
-				renderer.render( scene, this );
-
-				// Render depth into texture
-
-				scene.overrideMaterial = this.materialDepth;
-				renderer.setRenderTarget( this.postprocessing.rtTextureDepth );
-				renderer.clear();
-				renderer.render( scene, this );
-
-				// Render bokeh composite
-
-				renderer.setRenderTarget( null );
-				renderer.render( this.postprocessing.scene, this.postprocessing.camera );
-				renderer.setRenderTarget( currentRenderTarget );
-
-			}
-
-		}
-
-	}
-
-	THREE.CinematicCamera = CinematicCamera;
-
-} )();

+ 0 - 2770
examples/js/controls/ArcballControls.js

@@ -1,2770 +0,0 @@
-( function () {
-
-	//trackball state
-	const STATE = {
-		IDLE: Symbol(),
-		ROTATE: Symbol(),
-		PAN: Symbol(),
-		SCALE: Symbol(),
-		FOV: Symbol(),
-		FOCUS: Symbol(),
-		ZROTATE: Symbol(),
-		TOUCH_MULTI: Symbol(),
-		ANIMATION_FOCUS: Symbol(),
-		ANIMATION_ROTATE: Symbol()
-	};
-	const INPUT = {
-		NONE: Symbol(),
-		ONE_FINGER: Symbol(),
-		ONE_FINGER_SWITCHED: Symbol(),
-		TWO_FINGER: Symbol(),
-		MULT_FINGER: Symbol(),
-		CURSOR: Symbol()
-	};
-
-	//cursor center coordinates
-	const _center = {
-		x: 0,
-		y: 0
-	};
-
-	//transformation matrices for gizmos and camera
-	const _transformation = {
-		camera: new THREE.Matrix4(),
-		gizmos: new THREE.Matrix4()
-	};
-
-	//events
-	const _changeEvent = {
-		type: 'change'
-	};
-	const _startEvent = {
-		type: 'start'
-	};
-	const _endEvent = {
-		type: 'end'
-	};
-	const _raycaster = new THREE.Raycaster();
-	const _offset = new THREE.Vector3();
-	const _gizmoMatrixStateTemp = new THREE.Matrix4();
-	const _cameraMatrixStateTemp = new THREE.Matrix4();
-	const _scalePointTemp = new THREE.Vector3();
-	/**
- *
- * @param {Camera} camera Virtual camera used in the scene
- * @param {HTMLElement} domElement Renderer's dom element
- * @param {Scene} scene The scene to be rendered
- */
-	class ArcballControls extends THREE.EventDispatcher {
-
-		constructor( _camera, domElement, scene = null ) {
-
-			super();
-			this.onWindowResize = () => {
-
-				const scale = ( this._gizmos.scale.x + this._gizmos.scale.y + this._gizmos.scale.z ) / 3;
-				this._tbRadius = this.calculateTbRadius( this.camera );
-				const newRadius = this._tbRadius / scale;
-				const curve = new THREE.EllipseCurve( 0, 0, newRadius, newRadius );
-				const points = curve.getPoints( this._curvePts );
-				const curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
-				for ( const gizmo in this._gizmos.children ) {
-
-					this._gizmos.children[ gizmo ].geometry = curveGeometry;
-
-				}
-
-				this.dispatchEvent( _changeEvent );
-
-			};
-
-			this.onContextMenu = event => {
-
-				if ( ! this.enabled ) {
-
-					return;
-
-				}
-
-				for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-					if ( this.mouseActions[ i ].mouse == 2 ) {
-
-						//prevent only if button 2 is actually used
-						event.preventDefault();
-						break;
-
-					}
-
-				}
-
-			};
-
-			this.onPointerCancel = () => {
-
-				this._touchStart.splice( 0, this._touchStart.length );
-				this._touchCurrent.splice( 0, this._touchCurrent.length );
-				this._input = INPUT.NONE;
-
-			};
-
-			this.onPointerDown = event => {
-
-				if ( event.button == 0 && event.isPrimary ) {
-
-					this._downValid = true;
-					this._downEvents.push( event );
-					this._downStart = performance.now();
-
-				} else {
-
-					this._downValid = false;
-
-				}
-
-				if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
-
-					this._touchStart.push( event );
-					this._touchCurrent.push( event );
-					switch ( this._input ) {
-
-						case INPUT.NONE:
-							//singleStart
-							this._input = INPUT.ONE_FINGER;
-							this.onSinglePanStart( event, 'ROTATE' );
-							window.addEventListener( 'pointermove', this.onPointerMove );
-							window.addEventListener( 'pointerup', this.onPointerUp );
-							break;
-						case INPUT.ONE_FINGER:
-						case INPUT.ONE_FINGER_SWITCHED:
-							//doubleStart
-							this._input = INPUT.TWO_FINGER;
-							this.onRotateStart();
-							this.onPinchStart();
-							this.onDoublePanStart();
-							break;
-						case INPUT.TWO_FINGER:
-							//multipleStart
-							this._input = INPUT.MULT_FINGER;
-							this.onTriplePanStart( event );
-							break;
-
-					}
-
-				} else if ( event.pointerType != 'touch' && this._input == INPUT.NONE ) {
-
-					let modifier = null;
-					if ( event.ctrlKey || event.metaKey ) {
-
-						modifier = 'CTRL';
-
-					} else if ( event.shiftKey ) {
-
-						modifier = 'SHIFT';
-
-					}
-
-					this._mouseOp = this.getOpFromAction( event.button, modifier );
-					if ( this._mouseOp != null ) {
-
-						window.addEventListener( 'pointermove', this.onPointerMove );
-						window.addEventListener( 'pointerup', this.onPointerUp );
-
-						//singleStart
-						this._input = INPUT.CURSOR;
-						this._button = event.button;
-						this.onSinglePanStart( event, this._mouseOp );
-
-					}
-
-				}
-
-			};
-
-			this.onPointerMove = event => {
-
-				if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
-
-					switch ( this._input ) {
-
-						case INPUT.ONE_FINGER:
-							//singleMove
-							this.updateTouchEvent( event );
-							this.onSinglePanMove( event, STATE.ROTATE );
-							break;
-						case INPUT.ONE_FINGER_SWITCHED:
-							const movement = this.calculatePointersDistance( this._touchCurrent[ 0 ], event ) * this._devPxRatio;
-							if ( movement >= this._switchSensibility ) {
-
-								//singleMove
-								this._input = INPUT.ONE_FINGER;
-								this.updateTouchEvent( event );
-								this.onSinglePanStart( event, 'ROTATE' );
-								break;
-
-							}
-
-							break;
-						case INPUT.TWO_FINGER:
-							//rotate/pan/pinchMove
-							this.updateTouchEvent( event );
-							this.onRotateMove();
-							this.onPinchMove();
-							this.onDoublePanMove();
-							break;
-						case INPUT.MULT_FINGER:
-							//multMove
-							this.updateTouchEvent( event );
-							this.onTriplePanMove( event );
-							break;
-
-					}
-
-				} else if ( event.pointerType != 'touch' && this._input == INPUT.CURSOR ) {
-
-					let modifier = null;
-					if ( event.ctrlKey || event.metaKey ) {
-
-						modifier = 'CTRL';
-
-					} else if ( event.shiftKey ) {
-
-						modifier = 'SHIFT';
-
-					}
-
-					const mouseOpState = this.getOpStateFromAction( this._button, modifier );
-					if ( mouseOpState != null ) {
-
-						this.onSinglePanMove( event, mouseOpState );
-
-					}
-
-				}
-
-				//checkDistance
-				if ( this._downValid ) {
-
-					const movement = this.calculatePointersDistance( this._downEvents[ this._downEvents.length - 1 ], event ) * this._devPxRatio;
-					if ( movement > this._movementThreshold ) {
-
-						this._downValid = false;
-
-					}
-
-				}
-
-			};
-
-			this.onPointerUp = event => {
-
-				if ( event.pointerType == 'touch' && this._input != INPUT.CURSOR ) {
-
-					const nTouch = this._touchCurrent.length;
-					for ( let i = 0; i < nTouch; i ++ ) {
-
-						if ( this._touchCurrent[ i ].pointerId == event.pointerId ) {
-
-							this._touchCurrent.splice( i, 1 );
-							this._touchStart.splice( i, 1 );
-							break;
-
-						}
-
-					}
-
-					switch ( this._input ) {
-
-						case INPUT.ONE_FINGER:
-						case INPUT.ONE_FINGER_SWITCHED:
-							//singleEnd
-							window.removeEventListener( 'pointermove', this.onPointerMove );
-							window.removeEventListener( 'pointerup', this.onPointerUp );
-							this._input = INPUT.NONE;
-							this.onSinglePanEnd();
-							break;
-						case INPUT.TWO_FINGER:
-							//doubleEnd
-							this.onDoublePanEnd( event );
-							this.onPinchEnd( event );
-							this.onRotateEnd( event );
-
-							//switching to singleStart
-							this._input = INPUT.ONE_FINGER_SWITCHED;
-							break;
-						case INPUT.MULT_FINGER:
-							if ( this._touchCurrent.length == 0 ) {
-
-								window.removeEventListener( 'pointermove', this.onPointerMove );
-								window.removeEventListener( 'pointerup', this.onPointerUp );
-
-								//multCancel
-								this._input = INPUT.NONE;
-								this.onTriplePanEnd();
-
-							}
-
-							break;
-
-					}
-
-				} else if ( event.pointerType != 'touch' && this._input == INPUT.CURSOR ) {
-
-					window.removeEventListener( 'pointermove', this.onPointerMove );
-					window.removeEventListener( 'pointerup', this.onPointerUp );
-					this._input = INPUT.NONE;
-					this.onSinglePanEnd();
-					this._button = - 1;
-
-				}
-
-				if ( event.isPrimary ) {
-
-					if ( this._downValid ) {
-
-						const downTime = event.timeStamp - this._downEvents[ this._downEvents.length - 1 ].timeStamp;
-						if ( downTime <= this._maxDownTime ) {
-
-							if ( this._nclicks == 0 ) {
-
-								//first valid click detected
-								this._nclicks = 1;
-								this._clickStart = performance.now();
-
-							} else {
-
-								const clickInterval = event.timeStamp - this._clickStart;
-								const movement = this.calculatePointersDistance( this._downEvents[ 1 ], this._downEvents[ 0 ] ) * this._devPxRatio;
-								if ( clickInterval <= this._maxInterval && movement <= this._posThreshold ) {
-
-									//second valid click detected
-									//fire double tap and reset values
-									this._nclicks = 0;
-									this._downEvents.splice( 0, this._downEvents.length );
-									this.onDoubleTap( event );
-
-								} else {
-
-									//new 'first click'
-									this._nclicks = 1;
-									this._downEvents.shift();
-									this._clickStart = performance.now();
-
-								}
-
-							}
-
-						} else {
-
-							this._downValid = false;
-							this._nclicks = 0;
-							this._downEvents.splice( 0, this._downEvents.length );
-
-						}
-
-					} else {
-
-						this._nclicks = 0;
-						this._downEvents.splice( 0, this._downEvents.length );
-
-					}
-
-				}
-
-			};
-
-			this.onWheel = event => {
-
-				if ( this.enabled && this.enableZoom ) {
-
-					let modifier = null;
-					if ( event.ctrlKey || event.metaKey ) {
-
-						modifier = 'CTRL';
-
-					} else if ( event.shiftKey ) {
-
-						modifier = 'SHIFT';
-
-					}
-
-					const mouseOp = this.getOpFromAction( 'WHEEL', modifier );
-					if ( mouseOp != null ) {
-
-						event.preventDefault();
-						this.dispatchEvent( _startEvent );
-						const notchDeltaY = 125; //distance of one notch of mouse wheel
-						let sgn = event.deltaY / notchDeltaY;
-						let size = 1;
-						if ( sgn > 0 ) {
-
-							size = 1 / this.scaleFactor;
-
-						} else if ( sgn < 0 ) {
-
-							size = this.scaleFactor;
-
-						}
-
-						switch ( mouseOp ) {
-
-							case 'ZOOM':
-								this.updateTbState( STATE.SCALE, true );
-								if ( sgn > 0 ) {
-
-									size = 1 / Math.pow( this.scaleFactor, sgn );
-
-								} else if ( sgn < 0 ) {
-
-									size = Math.pow( this.scaleFactor, - sgn );
-
-								}
-
-								if ( this.cursorZoom && this.enablePan ) {
-
-									let scalePoint;
-									if ( this.camera.isOrthographicCamera ) {
-
-										scalePoint = this.unprojectOnTbPlane( this.camera, event.clientX, event.clientY, this.domElement ).applyQuaternion( this.camera.quaternion ).multiplyScalar( 1 / this.camera.zoom ).add( this._gizmos.position );
-
-									} else if ( this.camera.isPerspectiveCamera ) {
-
-										scalePoint = this.unprojectOnTbPlane( this.camera, event.clientX, event.clientY, this.domElement ).applyQuaternion( this.camera.quaternion ).add( this._gizmos.position );
-
-									}
-
-									this.applyTransformMatrix( this.scale( size, scalePoint ) );
-
-								} else {
-
-									this.applyTransformMatrix( this.scale( size, this._gizmos.position ) );
-
-								}
-
-								if ( this._grid != null ) {
-
-									this.disposeGrid();
-									this.drawGrid();
-
-								}
-
-								this.updateTbState( STATE.IDLE, false );
-								this.dispatchEvent( _changeEvent );
-								this.dispatchEvent( _endEvent );
-								break;
-							case 'FOV':
-								if ( this.camera.isPerspectiveCamera ) {
-
-									this.updateTbState( STATE.FOV, true );
-
-									//Vertigo effect
-
-									//	  fov / 2
-									//		|\
-									//		| \
-									//		|  \
-									//	x	|	\
-									//		| 	 \
-									//		| 	  \
-									//		| _ _ _\
-									//			y
-
-									//check for iOs shift shortcut
-									if ( event.deltaX != 0 ) {
-
-										sgn = event.deltaX / notchDeltaY;
-										size = 1;
-										if ( sgn > 0 ) {
-
-											size = 1 / Math.pow( this.scaleFactor, sgn );
-
-										} else if ( sgn < 0 ) {
-
-											size = Math.pow( this.scaleFactor, - sgn );
-
-										}
-
-									}
-
-									this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
-									const x = this._v3_1.distanceTo( this._gizmos.position );
-									let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
-
-									//check min and max distance
-									xNew = THREE.MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
-									const y = x * Math.tan( THREE.MathUtils.DEG2RAD * this.camera.fov * 0.5 );
-
-									//calculate new fov
-									let newFov = THREE.MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
-
-									//check min and max fov
-									if ( newFov > this.maxFov ) {
-
-										newFov = this.maxFov;
-
-									} else if ( newFov < this.minFov ) {
-
-										newFov = this.minFov;
-
-									}
-
-									const newDistance = y / Math.tan( THREE.MathUtils.DEG2RAD * ( newFov / 2 ) );
-									size = x / newDistance;
-									this.setFov( newFov );
-									this.applyTransformMatrix( this.scale( size, this._gizmos.position, false ) );
-
-								}
-
-								if ( this._grid != null ) {
-
-									this.disposeGrid();
-									this.drawGrid();
-
-								}
-
-								this.updateTbState( STATE.IDLE, false );
-								this.dispatchEvent( _changeEvent );
-								this.dispatchEvent( _endEvent );
-								break;
-
-						}
-
-					}
-
-				}
-
-			};
-
-			this.onSinglePanStart = ( event, operation ) => {
-
-				if ( this.enabled ) {
-
-					this.dispatchEvent( _startEvent );
-					this.setCenter( event.clientX, event.clientY );
-					switch ( operation ) {
-
-						case 'PAN':
-							if ( ! this.enablePan ) {
-
-								return;
-
-							}
-
-							if ( this._animationId != - 1 ) {
-
-								cancelAnimationFrame( this._animationId );
-								this._animationId = - 1;
-								this._timeStart = - 1;
-								this.activateGizmos( false );
-								this.dispatchEvent( _changeEvent );
-
-							}
-
-							this.updateTbState( STATE.PAN, true );
-							this._startCursorPosition.copy( this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ) );
-							if ( this.enableGrid ) {
-
-								this.drawGrid();
-								this.dispatchEvent( _changeEvent );
-
-							}
-
-							break;
-						case 'ROTATE':
-							if ( ! this.enableRotate ) {
-
-								return;
-
-							}
-
-							if ( this._animationId != - 1 ) {
-
-								cancelAnimationFrame( this._animationId );
-								this._animationId = - 1;
-								this._timeStart = - 1;
-
-							}
-
-							this.updateTbState( STATE.ROTATE, true );
-							this._startCursorPosition.copy( this.unprojectOnTbSurface( this.camera, _center.x, _center.y, this.domElement, this._tbRadius ) );
-							this.activateGizmos( true );
-							if ( this.enableAnimations ) {
-
-								this._timePrev = this._timeCurrent = performance.now();
-								this._angleCurrent = this._anglePrev = 0;
-								this._cursorPosPrev.copy( this._startCursorPosition );
-								this._cursorPosCurr.copy( this._cursorPosPrev );
-								this._wCurr = 0;
-								this._wPrev = this._wCurr;
-
-							}
-
-							this.dispatchEvent( _changeEvent );
-							break;
-						case 'FOV':
-							if ( ! this.camera.isPerspectiveCamera || ! this.enableZoom ) {
-
-								return;
-
-							}
-
-							if ( this._animationId != - 1 ) {
-
-								cancelAnimationFrame( this._animationId );
-								this._animationId = - 1;
-								this._timeStart = - 1;
-								this.activateGizmos( false );
-								this.dispatchEvent( _changeEvent );
-
-							}
-
-							this.updateTbState( STATE.FOV, true );
-							this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-							this._currentCursorPosition.copy( this._startCursorPosition );
-							break;
-						case 'ZOOM':
-							if ( ! this.enableZoom ) {
-
-								return;
-
-							}
-
-							if ( this._animationId != - 1 ) {
-
-								cancelAnimationFrame( this._animationId );
-								this._animationId = - 1;
-								this._timeStart = - 1;
-								this.activateGizmos( false );
-								this.dispatchEvent( _changeEvent );
-
-							}
-
-							this.updateTbState( STATE.SCALE, true );
-							this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-							this._currentCursorPosition.copy( this._startCursorPosition );
-							break;
-
-					}
-
-				}
-
-			};
-
-			this.onSinglePanMove = ( event, opState ) => {
-
-				if ( this.enabled ) {
-
-					const restart = opState != this._state;
-					this.setCenter( event.clientX, event.clientY );
-					switch ( opState ) {
-
-						case STATE.PAN:
-							if ( this.enablePan ) {
-
-								if ( restart ) {
-
-									//switch to pan operation
-
-									this.dispatchEvent( _endEvent );
-									this.dispatchEvent( _startEvent );
-									this.updateTbState( opState, true );
-									this._startCursorPosition.copy( this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ) );
-									if ( this.enableGrid ) {
-
-										this.drawGrid();
-
-									}
-
-									this.activateGizmos( false );
-
-								} else {
-
-									//continue with pan operation
-									this._currentCursorPosition.copy( this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ) );
-									this.applyTransformMatrix( this.pan( this._startCursorPosition, this._currentCursorPosition ) );
-
-								}
-
-							}
-
-							break;
-						case STATE.ROTATE:
-							if ( this.enableRotate ) {
-
-								if ( restart ) {
-
-									//switch to rotate operation
-
-									this.dispatchEvent( _endEvent );
-									this.dispatchEvent( _startEvent );
-									this.updateTbState( opState, true );
-									this._startCursorPosition.copy( this.unprojectOnTbSurface( this.camera, _center.x, _center.y, this.domElement, this._tbRadius ) );
-									if ( this.enableGrid ) {
-
-										this.disposeGrid();
-
-									}
-
-									this.activateGizmos( true );
-
-								} else {
-
-									//continue with rotate operation
-									this._currentCursorPosition.copy( this.unprojectOnTbSurface( this.camera, _center.x, _center.y, this.domElement, this._tbRadius ) );
-									const distance = this._startCursorPosition.distanceTo( this._currentCursorPosition );
-									const angle = this._startCursorPosition.angleTo( this._currentCursorPosition );
-									const amount = Math.max( distance / this._tbRadius, angle ); //effective rotation angle
-
-									this.applyTransformMatrix( this.rotate( this.calculateRotationAxis( this._startCursorPosition, this._currentCursorPosition ), amount ) );
-									if ( this.enableAnimations ) {
-
-										this._timePrev = this._timeCurrent;
-										this._timeCurrent = performance.now();
-										this._anglePrev = this._angleCurrent;
-										this._angleCurrent = amount;
-										this._cursorPosPrev.copy( this._cursorPosCurr );
-										this._cursorPosCurr.copy( this._currentCursorPosition );
-										this._wPrev = this._wCurr;
-										this._wCurr = this.calculateAngularSpeed( this._anglePrev, this._angleCurrent, this._timePrev, this._timeCurrent );
-
-									}
-
-								}
-
-							}
-
-							break;
-						case STATE.SCALE:
-							if ( this.enableZoom ) {
-
-								if ( restart ) {
-
-									//switch to zoom operation
-
-									this.dispatchEvent( _endEvent );
-									this.dispatchEvent( _startEvent );
-									this.updateTbState( opState, true );
-									this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-									this._currentCursorPosition.copy( this._startCursorPosition );
-									if ( this.enableGrid ) {
-
-										this.disposeGrid();
-
-									}
-
-									this.activateGizmos( false );
-
-								} else {
-
-									//continue with zoom operation
-									const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
-									this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-									const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
-									let size = 1;
-									if ( movement < 0 ) {
-
-										size = 1 / Math.pow( this.scaleFactor, - movement * screenNotches );
-
-									} else if ( movement > 0 ) {
-
-										size = Math.pow( this.scaleFactor, movement * screenNotches );
-
-									}
-
-									this._v3_1.setFromMatrixPosition( this._gizmoMatrixState );
-									this.applyTransformMatrix( this.scale( size, this._v3_1 ) );
-
-								}
-
-							}
-
-							break;
-						case STATE.FOV:
-							if ( this.enableZoom && this.camera.isPerspectiveCamera ) {
-
-								if ( restart ) {
-
-									//switch to fov operation
-
-									this.dispatchEvent( _endEvent );
-									this.dispatchEvent( _startEvent );
-									this.updateTbState( opState, true );
-									this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-									this._currentCursorPosition.copy( this._startCursorPosition );
-									if ( this.enableGrid ) {
-
-										this.disposeGrid();
-
-									}
-
-									this.activateGizmos( false );
-
-								} else {
-
-									//continue with fov operation
-									const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
-									this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-									const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
-									let size = 1;
-									if ( movement < 0 ) {
-
-										size = 1 / Math.pow( this.scaleFactor, - movement * screenNotches );
-
-									} else if ( movement > 0 ) {
-
-										size = Math.pow( this.scaleFactor, movement * screenNotches );
-
-									}
-
-									this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
-									const x = this._v3_1.distanceTo( this._gizmos.position );
-									let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
-
-									//check min and max distance
-									xNew = THREE.MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
-									const y = x * Math.tan( THREE.MathUtils.DEG2RAD * this._fovState * 0.5 );
-
-									//calculate new fov
-									let newFov = THREE.MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
-
-									//check min and max fov
-									newFov = THREE.MathUtils.clamp( newFov, this.minFov, this.maxFov );
-									const newDistance = y / Math.tan( THREE.MathUtils.DEG2RAD * ( newFov / 2 ) );
-									size = x / newDistance;
-									this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
-									this.setFov( newFov );
-									this.applyTransformMatrix( this.scale( size, this._v3_2, false ) );
-
-									//adjusting distance
-									_offset.copy( this._gizmos.position ).sub( this.camera.position ).normalize().multiplyScalar( newDistance / x );
-									this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
-
-								}
-
-							}
-
-							break;
-
-					}
-
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.onSinglePanEnd = () => {
-
-				if ( this._state == STATE.ROTATE ) {
-
-					if ( ! this.enableRotate ) {
-
-						return;
-
-					}
-
-					if ( this.enableAnimations ) {
-
-						//perform rotation animation
-						const deltaTime = performance.now() - this._timeCurrent;
-						if ( deltaTime < 120 ) {
-
-							const w = Math.abs( ( this._wPrev + this._wCurr ) / 2 );
-							const self = this;
-							this._animationId = window.requestAnimationFrame( function ( t ) {
-
-								self.updateTbState( STATE.ANIMATION_ROTATE, true );
-								const rotationAxis = self.calculateRotationAxis( self._cursorPosPrev, self._cursorPosCurr );
-								self.onRotationAnim( t, rotationAxis, Math.min( w, self.wMax ) );
-
-							} );
-
-						} else {
-
-							//cursor has been standing still for over 120 ms since last movement
-							this.updateTbState( STATE.IDLE, false );
-							this.activateGizmos( false );
-							this.dispatchEvent( _changeEvent );
-
-						}
-
-					} else {
-
-						this.updateTbState( STATE.IDLE, false );
-						this.activateGizmos( false );
-						this.dispatchEvent( _changeEvent );
-
-					}
-
-				} else if ( this._state == STATE.PAN || this._state == STATE.IDLE ) {
-
-					this.updateTbState( STATE.IDLE, false );
-					if ( this.enableGrid ) {
-
-						this.disposeGrid();
-
-					}
-
-					this.activateGizmos( false );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-				this.dispatchEvent( _endEvent );
-
-			};
-
-			this.onDoubleTap = event => {
-
-				if ( this.enabled && this.enablePan && this.scene != null ) {
-
-					this.dispatchEvent( _startEvent );
-					this.setCenter( event.clientX, event.clientY );
-					const hitP = this.unprojectOnObj( this.getCursorNDC( _center.x, _center.y, this.domElement ), this.camera );
-					if ( hitP != null && this.enableAnimations ) {
-
-						const self = this;
-						if ( this._animationId != - 1 ) {
-
-							window.cancelAnimationFrame( this._animationId );
-
-						}
-
-						this._timeStart = - 1;
-						this._animationId = window.requestAnimationFrame( function ( t ) {
-
-							self.updateTbState( STATE.ANIMATION_FOCUS, true );
-							self.onFocusAnim( t, hitP, self._cameraMatrixState, self._gizmoMatrixState );
-
-						} );
-
-					} else if ( hitP != null && ! this.enableAnimations ) {
-
-						this.updateTbState( STATE.FOCUS, true );
-						this.focus( hitP, this.scaleFactor );
-						this.updateTbState( STATE.IDLE, false );
-						this.dispatchEvent( _changeEvent );
-
-					}
-
-				}
-
-				this.dispatchEvent( _endEvent );
-
-			};
-
-			this.onDoublePanStart = () => {
-
-				if ( this.enabled && this.enablePan ) {
-
-					this.dispatchEvent( _startEvent );
-					this.updateTbState( STATE.PAN, true );
-					this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
-					this._startCursorPosition.copy( this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement, true ) );
-					this._currentCursorPosition.copy( this._startCursorPosition );
-					this.activateGizmos( false );
-
-				}
-
-			};
-
-			this.onDoublePanMove = () => {
-
-				if ( this.enabled && this.enablePan ) {
-
-					this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
-					if ( this._state != STATE.PAN ) {
-
-						this.updateTbState( STATE.PAN, true );
-						this._startCursorPosition.copy( this._currentCursorPosition );
-
-					}
-
-					this._currentCursorPosition.copy( this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement, true ) );
-					this.applyTransformMatrix( this.pan( this._startCursorPosition, this._currentCursorPosition, true ) );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.onDoublePanEnd = () => {
-
-				this.updateTbState( STATE.IDLE, false );
-				this.dispatchEvent( _endEvent );
-
-			};
-
-			this.onRotateStart = () => {
-
-				if ( this.enabled && this.enableRotate ) {
-
-					this.dispatchEvent( _startEvent );
-					this.updateTbState( STATE.ZROTATE, true );
-
-					//this._startFingerRotation = event.rotation;
-
-					this._startFingerRotation = this.getAngle( this._touchCurrent[ 1 ], this._touchCurrent[ 0 ] ) + this.getAngle( this._touchStart[ 1 ], this._touchStart[ 0 ] );
-					this._currentFingerRotation = this._startFingerRotation;
-					this.camera.getWorldDirection( this._rotationAxis ); //rotation axis
-
-					if ( ! this.enablePan && ! this.enableZoom ) {
-
-						this.activateGizmos( true );
-
-					}
-
-				}
-
-			};
-
-			this.onRotateMove = () => {
-
-				if ( this.enabled && this.enableRotate ) {
-
-					this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
-					let rotationPoint;
-					if ( this._state != STATE.ZROTATE ) {
-
-						this.updateTbState( STATE.ZROTATE, true );
-						this._startFingerRotation = this._currentFingerRotation;
-
-					}
-
-					//this._currentFingerRotation = event.rotation;
-					this._currentFingerRotation = this.getAngle( this._touchCurrent[ 1 ], this._touchCurrent[ 0 ] ) + this.getAngle( this._touchStart[ 1 ], this._touchStart[ 0 ] );
-					if ( ! this.enablePan ) {
-
-						rotationPoint = new THREE.Vector3().setFromMatrixPosition( this._gizmoMatrixState );
-
-					} else {
-
-						this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
-						rotationPoint = this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ).applyQuaternion( this.camera.quaternion ).multiplyScalar( 1 / this.camera.zoom ).add( this._v3_2 );
-
-					}
-
-					const amount = THREE.MathUtils.DEG2RAD * ( this._startFingerRotation - this._currentFingerRotation );
-					this.applyTransformMatrix( this.zRotate( rotationPoint, amount ) );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.onRotateEnd = () => {
-
-				this.updateTbState( STATE.IDLE, false );
-				this.activateGizmos( false );
-				this.dispatchEvent( _endEvent );
-
-			};
-
-			this.onPinchStart = () => {
-
-				if ( this.enabled && this.enableZoom ) {
-
-					this.dispatchEvent( _startEvent );
-					this.updateTbState( STATE.SCALE, true );
-					this._startFingerDistance = this.calculatePointersDistance( this._touchCurrent[ 0 ], this._touchCurrent[ 1 ] );
-					this._currentFingerDistance = this._startFingerDistance;
-					this.activateGizmos( false );
-
-				}
-
-			};
-
-			this.onPinchMove = () => {
-
-				if ( this.enabled && this.enableZoom ) {
-
-					this.setCenter( ( this._touchCurrent[ 0 ].clientX + this._touchCurrent[ 1 ].clientX ) / 2, ( this._touchCurrent[ 0 ].clientY + this._touchCurrent[ 1 ].clientY ) / 2 );
-					const minDistance = 12; //minimum distance between fingers (in css pixels)
-
-					if ( this._state != STATE.SCALE ) {
-
-						this._startFingerDistance = this._currentFingerDistance;
-						this.updateTbState( STATE.SCALE, true );
-
-					}
-
-					this._currentFingerDistance = Math.max( this.calculatePointersDistance( this._touchCurrent[ 0 ], this._touchCurrent[ 1 ] ), minDistance * this._devPxRatio );
-					const amount = this._currentFingerDistance / this._startFingerDistance;
-					let scalePoint;
-					if ( ! this.enablePan ) {
-
-						scalePoint = this._gizmos.position;
-
-					} else {
-
-						if ( this.camera.isOrthographicCamera ) {
-
-							scalePoint = this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ).applyQuaternion( this.camera.quaternion ).multiplyScalar( 1 / this.camera.zoom ).add( this._gizmos.position );
-
-						} else if ( this.camera.isPerspectiveCamera ) {
-
-							scalePoint = this.unprojectOnTbPlane( this.camera, _center.x, _center.y, this.domElement ).applyQuaternion( this.camera.quaternion ).add( this._gizmos.position );
-
-						}
-
-					}
-
-					this.applyTransformMatrix( this.scale( amount, scalePoint ) );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.onPinchEnd = () => {
-
-				this.updateTbState( STATE.IDLE, false );
-				this.dispatchEvent( _endEvent );
-
-			};
-
-			this.onTriplePanStart = () => {
-
-				if ( this.enabled && this.enableZoom ) {
-
-					this.dispatchEvent( _startEvent );
-					this.updateTbState( STATE.SCALE, true );
-
-					//const center = event.center;
-					let clientX = 0;
-					let clientY = 0;
-					const nFingers = this._touchCurrent.length;
-					for ( let i = 0; i < nFingers; i ++ ) {
-
-						clientX += this._touchCurrent[ i ].clientX;
-						clientY += this._touchCurrent[ i ].clientY;
-
-					}
-
-					this.setCenter( clientX / nFingers, clientY / nFingers );
-					this._startCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-					this._currentCursorPosition.copy( this._startCursorPosition );
-
-				}
-
-			};
-
-			this.onTriplePanMove = () => {
-
-				if ( this.enabled && this.enableZoom ) {
-
-					//	  fov / 2
-					//		|\
-					//		| \
-					//		|  \
-					//	x	|	\
-					//		| 	 \
-					//		| 	  \
-					//		| _ _ _\
-					//			y
-
-					//const center = event.center;
-					let clientX = 0;
-					let clientY = 0;
-					const nFingers = this._touchCurrent.length;
-					for ( let i = 0; i < nFingers; i ++ ) {
-
-						clientX += this._touchCurrent[ i ].clientX;
-						clientY += this._touchCurrent[ i ].clientY;
-
-					}
-
-					this.setCenter( clientX / nFingers, clientY / nFingers );
-					const screenNotches = 8; //how many wheel notches corresponds to a full screen pan
-					this._currentCursorPosition.setY( this.getCursorNDC( _center.x, _center.y, this.domElement ).y * 0.5 );
-					const movement = this._currentCursorPosition.y - this._startCursorPosition.y;
-					let size = 1;
-					if ( movement < 0 ) {
-
-						size = 1 / Math.pow( this.scaleFactor, - movement * screenNotches );
-
-					} else if ( movement > 0 ) {
-
-						size = Math.pow( this.scaleFactor, movement * screenNotches );
-
-					}
-
-					this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
-					const x = this._v3_1.distanceTo( this._gizmos.position );
-					let xNew = x / size; //distance between camera and gizmos if scale(size, scalepoint) would be performed
-
-					//check min and max distance
-					xNew = THREE.MathUtils.clamp( xNew, this.minDistance, this.maxDistance );
-					const y = x * Math.tan( THREE.MathUtils.DEG2RAD * this._fovState * 0.5 );
-
-					//calculate new fov
-					let newFov = THREE.MathUtils.RAD2DEG * ( Math.atan( y / xNew ) * 2 );
-
-					//check min and max fov
-					newFov = THREE.MathUtils.clamp( newFov, this.minFov, this.maxFov );
-					const newDistance = y / Math.tan( THREE.MathUtils.DEG2RAD * ( newFov / 2 ) );
-					size = x / newDistance;
-					this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
-					this.setFov( newFov );
-					this.applyTransformMatrix( this.scale( size, this._v3_2, false ) );
-
-					//adjusting distance
-					_offset.copy( this._gizmos.position ).sub( this.camera.position ).normalize().multiplyScalar( newDistance / x );
-					this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.onTriplePanEnd = () => {
-
-				this.updateTbState( STATE.IDLE, false );
-				this.dispatchEvent( _endEvent );
-				//this.dispatchEvent( _changeEvent );
-
-			};
-
-			this.setCenter = ( clientX, clientY ) => {
-
-				_center.x = clientX;
-				_center.y = clientY;
-
-			};
-
-			this.initializeMouseActions = () => {
-
-				this.setMouseAction( 'PAN', 0, 'CTRL' );
-				this.setMouseAction( 'PAN', 2 );
-				this.setMouseAction( 'ROTATE', 0 );
-				this.setMouseAction( 'ZOOM', 'WHEEL' );
-				this.setMouseAction( 'ZOOM', 1 );
-				this.setMouseAction( 'FOV', 'WHEEL', 'SHIFT' );
-				this.setMouseAction( 'FOV', 1, 'SHIFT' );
-
-			};
-
-			this.compareMouseAction = ( action1, action2 ) => {
-
-				if ( action1.operation == action2.operation ) {
-
-					if ( action1.mouse == action2.mouse && action1.key == action2.key ) {
-
-						return true;
-
-					} else {
-
-						return false;
-
-					}
-
-				} else {
-
-					return false;
-
-				}
-
-			};
-
-			this.setMouseAction = ( operation, mouse, key = null ) => {
-
-				const operationInput = [ 'PAN', 'ROTATE', 'ZOOM', 'FOV' ];
-				const mouseInput = [ 0, 1, 2, 'WHEEL' ];
-				const keyInput = [ 'CTRL', 'SHIFT', null ];
-				let state;
-				if ( ! operationInput.includes( operation ) || ! mouseInput.includes( mouse ) || ! keyInput.includes( key ) ) {
-
-					//invalid parameters
-					return false;
-
-				}
-
-				if ( mouse == 'WHEEL' ) {
-
-					if ( operation != 'ZOOM' && operation != 'FOV' ) {
-
-						//cannot associate 2D operation to 1D input
-						return false;
-
-					}
-
-				}
-
-				switch ( operation ) {
-
-					case 'PAN':
-						state = STATE.PAN;
-						break;
-					case 'ROTATE':
-						state = STATE.ROTATE;
-						break;
-					case 'ZOOM':
-						state = STATE.SCALE;
-						break;
-					case 'FOV':
-						state = STATE.FOV;
-						break;
-
-				}
-
-				const action = {
-					operation: operation,
-					mouse: mouse,
-					key: key,
-					state: state
-				};
-				for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-					if ( this.mouseActions[ i ].mouse == action.mouse && this.mouseActions[ i ].key == action.key ) {
-
-						this.mouseActions.splice( i, 1, action );
-						return true;
-
-					}
-
-				}
-
-				this.mouseActions.push( action );
-				return true;
-
-			};
-
-			this.unsetMouseAction = ( mouse, key = null ) => {
-
-				for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-					if ( this.mouseActions[ i ].mouse == mouse && this.mouseActions[ i ].key == key ) {
-
-						this.mouseActions.splice( i, 1 );
-						return true;
-
-					}
-
-				}
-
-				return false;
-
-			};
-
-			this.getOpFromAction = ( mouse, key ) => {
-
-				let action;
-				for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-					action = this.mouseActions[ i ];
-					if ( action.mouse == mouse && action.key == key ) {
-
-						return action.operation;
-
-					}
-
-				}
-
-				if ( key != null ) {
-
-					for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-						action = this.mouseActions[ i ];
-						if ( action.mouse == mouse && action.key == null ) {
-
-							return action.operation;
-
-						}
-
-					}
-
-				}
-
-				return null;
-
-			};
-
-			this.getOpStateFromAction = ( mouse, key ) => {
-
-				let action;
-				for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-					action = this.mouseActions[ i ];
-					if ( action.mouse == mouse && action.key == key ) {
-
-						return action.state;
-
-					}
-
-				}
-
-				if ( key != null ) {
-
-					for ( let i = 0; i < this.mouseActions.length; i ++ ) {
-
-						action = this.mouseActions[ i ];
-						if ( action.mouse == mouse && action.key == null ) {
-
-							return action.state;
-
-						}
-
-					}
-
-				}
-
-				return null;
-
-			};
-
-			this.getAngle = ( p1, p2 ) => {
-
-				return Math.atan2( p2.clientY - p1.clientY, p2.clientX - p1.clientX ) * 180 / Math.PI;
-
-			};
-
-			this.updateTouchEvent = event => {
-
-				for ( let i = 0; i < this._touchCurrent.length; i ++ ) {
-
-					if ( this._touchCurrent[ i ].pointerId == event.pointerId ) {
-
-						this._touchCurrent.splice( i, 1, event );
-						break;
-
-					}
-
-				}
-
-			};
-
-			this.calculateAngularSpeed = ( p0, p1, t0, t1 ) => {
-
-				const s = p1 - p0;
-				const t = ( t1 - t0 ) / 1000;
-				if ( t == 0 ) {
-
-					return 0;
-
-				}
-
-				return s / t;
-
-			};
-
-			this.calculatePointersDistance = ( p0, p1 ) => {
-
-				return Math.sqrt( Math.pow( p1.clientX - p0.clientX, 2 ) + Math.pow( p1.clientY - p0.clientY, 2 ) );
-
-			};
-
-			this.calculateRotationAxis = ( vec1, vec2 ) => {
-
-				this._rotationMatrix.extractRotation( this._cameraMatrixState );
-				this._quat.setFromRotationMatrix( this._rotationMatrix );
-				this._rotationAxis.crossVectors( vec1, vec2 ).applyQuaternion( this._quat );
-				return this._rotationAxis.normalize().clone();
-
-			};
-
-			this.calculateTbRadius = camera => {
-
-				const distance = camera.position.distanceTo( this._gizmos.position );
-				if ( camera.type == 'PerspectiveCamera' ) {
-
-					const halfFovV = THREE.MathUtils.DEG2RAD * camera.fov * 0.5; //vertical fov/2 in radians
-					const halfFovH = Math.atan( camera.aspect * Math.tan( halfFovV ) ); //horizontal fov/2 in radians
-					return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * this.radiusFactor;
-
-				} else if ( camera.type == 'OrthographicCamera' ) {
-
-					return Math.min( camera.top, camera.right ) * this.radiusFactor;
-
-				}
-
-			};
-
-			this.focus = ( point, size, amount = 1 ) => {
-
-				//move center of camera (along with gizmos) towards point of interest
-				_offset.copy( point ).sub( this._gizmos.position ).multiplyScalar( amount );
-				this._translationMatrix.makeTranslation( _offset.x, _offset.y, _offset.z );
-				_gizmoMatrixStateTemp.copy( this._gizmoMatrixState );
-				this._gizmoMatrixState.premultiply( this._translationMatrix );
-				this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-				_cameraMatrixStateTemp.copy( this._cameraMatrixState );
-				this._cameraMatrixState.premultiply( this._translationMatrix );
-				this._cameraMatrixState.decompose( this.camera.position, this.camera.quaternion, this.camera.scale );
-
-				//apply zoom
-				if ( this.enableZoom ) {
-
-					this.applyTransformMatrix( this.scale( size, this._gizmos.position ) );
-
-				}
-
-				this._gizmoMatrixState.copy( _gizmoMatrixStateTemp );
-				this._cameraMatrixState.copy( _cameraMatrixStateTemp );
-
-			};
-
-			this.drawGrid = () => {
-
-				if ( this.scene != null ) {
-
-					const color = 0x888888;
-					const multiplier = 3;
-					let size, divisions, maxLength, tick;
-					if ( this.camera.isOrthographicCamera ) {
-
-						const width = this.camera.right - this.camera.left;
-						const height = this.camera.bottom - this.camera.top;
-						maxLength = Math.max( width, height );
-						tick = maxLength / 20;
-						size = maxLength / this.camera.zoom * multiplier;
-						divisions = size / tick * this.camera.zoom;
-
-					} else if ( this.camera.isPerspectiveCamera ) {
-
-						const distance = this.camera.position.distanceTo( this._gizmos.position );
-						const halfFovV = THREE.MathUtils.DEG2RAD * this.camera.fov * 0.5;
-						const halfFovH = Math.atan( this.camera.aspect * Math.tan( halfFovV ) );
-						maxLength = Math.tan( Math.max( halfFovV, halfFovH ) ) * distance * 2;
-						tick = maxLength / 20;
-						size = maxLength * multiplier;
-						divisions = size / tick;
-
-					}
-
-					if ( this._grid == null ) {
-
-						this._grid = new THREE.GridHelper( size, divisions, color, color );
-						this._grid.position.copy( this._gizmos.position );
-						this._gridPosition.copy( this._grid.position );
-						this._grid.quaternion.copy( this.camera.quaternion );
-						this._grid.rotateX( Math.PI * 0.5 );
-						this.scene.add( this._grid );
-
-					}
-
-				}
-
-			};
-
-			this.dispose = () => {
-
-				if ( this._animationId != - 1 ) {
-
-					window.cancelAnimationFrame( this._animationId );
-
-				}
-
-				this.domElement.removeEventListener( 'pointerdown', this.onPointerDown );
-				this.domElement.removeEventListener( 'pointercancel', this.onPointerCancel );
-				this.domElement.removeEventListener( 'wheel', this.onWheel );
-				this.domElement.removeEventListener( 'contextmenu', this.onContextMenu );
-				window.removeEventListener( 'pointermove', this.onPointerMove );
-				window.removeEventListener( 'pointerup', this.onPointerUp );
-				window.removeEventListener( 'resize', this.onWindowResize );
-				if ( this.scene !== null ) this.scene.remove( this._gizmos );
-				this.disposeGrid();
-
-			};
-
-			this.disposeGrid = () => {
-
-				if ( this._grid != null && this.scene != null ) {
-
-					this.scene.remove( this._grid );
-					this._grid = null;
-
-				}
-
-			};
-
-			this.easeOutCubic = t => {
-
-				return 1 - Math.pow( 1 - t, 3 );
-
-			};
-
-			this.activateGizmos = isActive => {
-
-				const gizmoX = this._gizmos.children[ 0 ];
-				const gizmoY = this._gizmos.children[ 1 ];
-				const gizmoZ = this._gizmos.children[ 2 ];
-				if ( isActive ) {
-
-					gizmoX.material.setValues( {
-						opacity: 1
-					} );
-					gizmoY.material.setValues( {
-						opacity: 1
-					} );
-					gizmoZ.material.setValues( {
-						opacity: 1
-					} );
-
-				} else {
-
-					gizmoX.material.setValues( {
-						opacity: 0.6
-					} );
-					gizmoY.material.setValues( {
-						opacity: 0.6
-					} );
-					gizmoZ.material.setValues( {
-						opacity: 0.6
-					} );
-
-				}
-
-			};
-
-			this.getCursorNDC = ( cursorX, cursorY, canvas ) => {
-
-				const canvasRect = canvas.getBoundingClientRect();
-				this._v2_1.setX( ( cursorX - canvasRect.left ) / canvasRect.width * 2 - 1 );
-				this._v2_1.setY( ( canvasRect.bottom - cursorY ) / canvasRect.height * 2 - 1 );
-				return this._v2_1.clone();
-
-			};
-
-			this.getCursorPosition = ( cursorX, cursorY, canvas ) => {
-
-				this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
-				this._v2_1.x *= ( this.camera.right - this.camera.left ) * 0.5;
-				this._v2_1.y *= ( this.camera.top - this.camera.bottom ) * 0.5;
-				return this._v2_1.clone();
-
-			};
-
-			this.setCamera = camera => {
-
-				camera.lookAt( this.target );
-				camera.updateMatrix();
-
-				//setting state
-				if ( camera.type == 'PerspectiveCamera' ) {
-
-					this._fov0 = camera.fov;
-					this._fovState = camera.fov;
-
-				}
-
-				this._cameraMatrixState0.copy( camera.matrix );
-				this._cameraMatrixState.copy( this._cameraMatrixState0 );
-				this._cameraProjectionState.copy( camera.projectionMatrix );
-				this._zoom0 = camera.zoom;
-				this._zoomState = this._zoom0;
-				this._initialNear = camera.near;
-				this._nearPos0 = camera.position.distanceTo( this.target ) - camera.near;
-				this._nearPos = this._initialNear;
-				this._initialFar = camera.far;
-				this._farPos0 = camera.position.distanceTo( this.target ) - camera.far;
-				this._farPos = this._initialFar;
-				this._up0.copy( camera.up );
-				this._upState.copy( camera.up );
-				this.camera = camera;
-				this.camera.updateProjectionMatrix();
-
-				//making gizmos
-				this._tbRadius = this.calculateTbRadius( camera );
-				this.makeGizmos( this.target, this._tbRadius );
-
-			};
-
-			this.makeGizmos = ( tbCenter, tbRadius ) => {
-
-				const curve = new THREE.EllipseCurve( 0, 0, tbRadius, tbRadius );
-				const points = curve.getPoints( this._curvePts );
-
-				//geometry
-				const curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
-
-				//material
-				const curveMaterialX = new THREE.LineBasicMaterial( {
-					color: 0xff8080,
-					fog: false,
-					transparent: true,
-					opacity: 0.6
-				} );
-				const curveMaterialY = new THREE.LineBasicMaterial( {
-					color: 0x80ff80,
-					fog: false,
-					transparent: true,
-					opacity: 0.6
-				} );
-				const curveMaterialZ = new THREE.LineBasicMaterial( {
-					color: 0x8080ff,
-					fog: false,
-					transparent: true,
-					opacity: 0.6
-				} );
-
-				//line
-				const gizmoX = new THREE.Line( curveGeometry, curveMaterialX );
-				const gizmoY = new THREE.Line( curveGeometry, curveMaterialY );
-				const gizmoZ = new THREE.Line( curveGeometry, curveMaterialZ );
-				const rotation = Math.PI * 0.5;
-				gizmoX.rotation.x = rotation;
-				gizmoY.rotation.y = rotation;
-
-				//setting state
-				this._gizmoMatrixState0.identity().setPosition( tbCenter );
-				this._gizmoMatrixState.copy( this._gizmoMatrixState0 );
-				if ( this.camera.zoom !== 1 ) {
-
-					//adapt gizmos size to camera zoom
-					const size = 1 / this.camera.zoom;
-					this._scaleMatrix.makeScale( size, size, size );
-					this._translationMatrix.makeTranslation( - tbCenter.x, - tbCenter.y, - tbCenter.z );
-					this._gizmoMatrixState.premultiply( this._translationMatrix ).premultiply( this._scaleMatrix );
-					this._translationMatrix.makeTranslation( tbCenter.x, tbCenter.y, tbCenter.z );
-					this._gizmoMatrixState.premultiply( this._translationMatrix );
-
-				}
-
-				this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-
-				//
-
-				this._gizmos.traverse( function ( object ) {
-
-					if ( object.isLine ) {
-
-						object.geometry.dispose();
-						object.material.dispose();
-
-					}
-
-				} );
-				this._gizmos.clear();
-
-				//
-
-				this._gizmos.add( gizmoX );
-				this._gizmos.add( gizmoY );
-				this._gizmos.add( gizmoZ );
-
-			};
-
-			this.onFocusAnim = ( time, point, cameraMatrix, gizmoMatrix ) => {
-
-				if ( this._timeStart == - 1 ) {
-
-					//animation start
-					this._timeStart = time;
-
-				}
-
-				if ( this._state == STATE.ANIMATION_FOCUS ) {
-
-					const deltaTime = time - this._timeStart;
-					const animTime = deltaTime / this.focusAnimationTime;
-					this._gizmoMatrixState.copy( gizmoMatrix );
-					if ( animTime >= 1 ) {
-
-						//animation end
-
-						this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-						this.focus( point, this.scaleFactor );
-						this._timeStart = - 1;
-						this.updateTbState( STATE.IDLE, false );
-						this.activateGizmos( false );
-						this.dispatchEvent( _changeEvent );
-
-					} else {
-
-						const amount = this.easeOutCubic( animTime );
-						const size = 1 - amount + this.scaleFactor * amount;
-						this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-						this.focus( point, size, amount );
-						this.dispatchEvent( _changeEvent );
-						const self = this;
-						this._animationId = window.requestAnimationFrame( function ( t ) {
-
-							self.onFocusAnim( t, point, cameraMatrix, gizmoMatrix.clone() );
-
-						} );
-
-					}
-
-				} else {
-
-					//interrupt animation
-
-					this._animationId = - 1;
-					this._timeStart = - 1;
-
-				}
-
-			};
-
-			this.onRotationAnim = ( time, rotationAxis, w0 ) => {
-
-				if ( this._timeStart == - 1 ) {
-
-					//animation start
-					this._anglePrev = 0;
-					this._angleCurrent = 0;
-					this._timeStart = time;
-
-				}
-
-				if ( this._state == STATE.ANIMATION_ROTATE ) {
-
-					//w = w0 + alpha * t
-					const deltaTime = ( time - this._timeStart ) / 1000;
-					const w = w0 + - this.dampingFactor * deltaTime;
-					if ( w > 0 ) {
-
-						//tetha = 0.5 * alpha * t^2 + w0 * t + tetha0
-						this._angleCurrent = 0.5 * - this.dampingFactor * Math.pow( deltaTime, 2 ) + w0 * deltaTime + 0;
-						this.applyTransformMatrix( this.rotate( rotationAxis, this._angleCurrent ) );
-						this.dispatchEvent( _changeEvent );
-						const self = this;
-						this._animationId = window.requestAnimationFrame( function ( t ) {
-
-							self.onRotationAnim( t, rotationAxis, w0 );
-
-						} );
-
-					} else {
-
-						this._animationId = - 1;
-						this._timeStart = - 1;
-						this.updateTbState( STATE.IDLE, false );
-						this.activateGizmos( false );
-						this.dispatchEvent( _changeEvent );
-
-					}
-
-				} else {
-
-					//interrupt animation
-
-					this._animationId = - 1;
-					this._timeStart = - 1;
-					if ( this._state != STATE.ROTATE ) {
-
-						this.activateGizmos( false );
-						this.dispatchEvent( _changeEvent );
-
-					}
-
-				}
-
-			};
-
-			this.pan = ( p0, p1, adjust = false ) => {
-
-				const movement = p0.clone().sub( p1 );
-				if ( this.camera.isOrthographicCamera ) {
-
-					//adjust movement amount
-					movement.multiplyScalar( 1 / this.camera.zoom );
-
-				} else if ( this.camera.isPerspectiveCamera && adjust ) {
-
-					//adjust movement amount
-					this._v3_1.setFromMatrixPosition( this._cameraMatrixState0 ); //camera's initial position
-					this._v3_2.setFromMatrixPosition( this._gizmoMatrixState0 ); //gizmo's initial position
-					const distanceFactor = this._v3_1.distanceTo( this._v3_2 ) / this.camera.position.distanceTo( this._gizmos.position );
-					movement.multiplyScalar( 1 / distanceFactor );
-
-				}
-
-				this._v3_1.set( movement.x, movement.y, 0 ).applyQuaternion( this.camera.quaternion );
-				this._m4_1.makeTranslation( this._v3_1.x, this._v3_1.y, this._v3_1.z );
-				this.setTransformationMatrices( this._m4_1, this._m4_1 );
-				return _transformation;
-
-			};
-
-			this.reset = () => {
-
-				this.camera.zoom = this._zoom0;
-				if ( this.camera.isPerspectiveCamera ) {
-
-					this.camera.fov = this._fov0;
-
-				}
-
-				this.camera.near = this._nearPos;
-				this.camera.far = this._farPos;
-				this._cameraMatrixState.copy( this._cameraMatrixState0 );
-				this._cameraMatrixState.decompose( this.camera.position, this.camera.quaternion, this.camera.scale );
-				this.camera.up.copy( this._up0 );
-				this.camera.updateMatrix();
-				this.camera.updateProjectionMatrix();
-				this._gizmoMatrixState.copy( this._gizmoMatrixState0 );
-				this._gizmoMatrixState0.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-				this._gizmos.updateMatrix();
-				this._tbRadius = this.calculateTbRadius( this.camera );
-				this.makeGizmos( this._gizmos.position, this._tbRadius );
-				this.camera.lookAt( this._gizmos.position );
-				this.updateTbState( STATE.IDLE, false );
-				this.dispatchEvent( _changeEvent );
-
-			};
-
-			this.rotate = ( axis, angle ) => {
-
-				const point = this._gizmos.position; //rotation center
-				this._translationMatrix.makeTranslation( - point.x, - point.y, - point.z );
-				this._rotationMatrix.makeRotationAxis( axis, - angle );
-
-				//rotate camera
-				this._m4_1.makeTranslation( point.x, point.y, point.z );
-				this._m4_1.multiply( this._rotationMatrix );
-				this._m4_1.multiply( this._translationMatrix );
-				this.setTransformationMatrices( this._m4_1 );
-				return _transformation;
-
-			};
-
-			this.copyState = () => {
-
-				let state;
-				if ( this.camera.isOrthographicCamera ) {
-
-					state = JSON.stringify( {
-						arcballState: {
-							cameraFar: this.camera.far,
-							cameraMatrix: this.camera.matrix,
-							cameraNear: this.camera.near,
-							cameraUp: this.camera.up,
-							cameraZoom: this.camera.zoom,
-							gizmoMatrix: this._gizmos.matrix
-						}
-					} );
-
-				} else if ( this.camera.isPerspectiveCamera ) {
-
-					state = JSON.stringify( {
-						arcballState: {
-							cameraFar: this.camera.far,
-							cameraFov: this.camera.fov,
-							cameraMatrix: this.camera.matrix,
-							cameraNear: this.camera.near,
-							cameraUp: this.camera.up,
-							cameraZoom: this.camera.zoom,
-							gizmoMatrix: this._gizmos.matrix
-						}
-					} );
-
-				}
-
-				navigator.clipboard.writeText( state );
-
-			};
-
-			this.pasteState = () => {
-
-				const self = this;
-				navigator.clipboard.readText().then( function resolved( value ) {
-
-					self.setStateFromJSON( value );
-
-				} );
-
-			};
-
-			this.saveState = () => {
-
-				this._cameraMatrixState0.copy( this.camera.matrix );
-				this._gizmoMatrixState0.copy( this._gizmos.matrix );
-				this._nearPos = this.camera.near;
-				this._farPos = this.camera.far;
-				this._zoom0 = this.camera.zoom;
-				this._up0.copy( this.camera.up );
-				if ( this.camera.isPerspectiveCamera ) {
-
-					this._fov0 = this.camera.fov;
-
-				}
-
-			};
-
-			this.scale = ( size, point, scaleGizmos = true ) => {
-
-				_scalePointTemp.copy( point );
-				let sizeInverse = 1 / size;
-				if ( this.camera.isOrthographicCamera ) {
-
-					//camera zoom
-					this.camera.zoom = this._zoomState;
-					this.camera.zoom *= size;
-
-					//check min and max zoom
-					if ( this.camera.zoom > this.maxZoom ) {
-
-						this.camera.zoom = this.maxZoom;
-						sizeInverse = this._zoomState / this.maxZoom;
-
-					} else if ( this.camera.zoom < this.minZoom ) {
-
-						this.camera.zoom = this.minZoom;
-						sizeInverse = this._zoomState / this.minZoom;
-
-					}
-
-					this.camera.updateProjectionMatrix();
-					this._v3_1.setFromMatrixPosition( this._gizmoMatrixState ); //gizmos position
-
-					//scale gizmos so they appear in the same spot having the same dimension
-					this._scaleMatrix.makeScale( sizeInverse, sizeInverse, sizeInverse );
-					this._translationMatrix.makeTranslation( - this._v3_1.x, - this._v3_1.y, - this._v3_1.z );
-					this._m4_2.makeTranslation( this._v3_1.x, this._v3_1.y, this._v3_1.z ).multiply( this._scaleMatrix );
-					this._m4_2.multiply( this._translationMatrix );
-
-					//move camera and gizmos to obtain pinch effect
-					_scalePointTemp.sub( this._v3_1 );
-					const amount = _scalePointTemp.clone().multiplyScalar( sizeInverse );
-					_scalePointTemp.sub( amount );
-					this._m4_1.makeTranslation( _scalePointTemp.x, _scalePointTemp.y, _scalePointTemp.z );
-					this._m4_2.premultiply( this._m4_1 );
-					this.setTransformationMatrices( this._m4_1, this._m4_2 );
-					return _transformation;
-
-				} else if ( this.camera.isPerspectiveCamera ) {
-
-					this._v3_1.setFromMatrixPosition( this._cameraMatrixState );
-					this._v3_2.setFromMatrixPosition( this._gizmoMatrixState );
-
-					//move camera
-					let distance = this._v3_1.distanceTo( _scalePointTemp );
-					let amount = distance - distance * sizeInverse;
-
-					//check min and max distance
-					const newDistance = distance - amount;
-					if ( newDistance < this.minDistance ) {
-
-						sizeInverse = this.minDistance / distance;
-						amount = distance - distance * sizeInverse;
-
-					} else if ( newDistance > this.maxDistance ) {
-
-						sizeInverse = this.maxDistance / distance;
-						amount = distance - distance * sizeInverse;
-
-					}
-
-					_offset.copy( _scalePointTemp ).sub( this._v3_1 ).normalize().multiplyScalar( amount );
-					this._m4_1.makeTranslation( _offset.x, _offset.y, _offset.z );
-					if ( scaleGizmos ) {
-
-						//scale gizmos so they appear in the same spot having the same dimension
-						const pos = this._v3_2;
-						distance = pos.distanceTo( _scalePointTemp );
-						amount = distance - distance * sizeInverse;
-						_offset.copy( _scalePointTemp ).sub( this._v3_2 ).normalize().multiplyScalar( amount );
-						this._translationMatrix.makeTranslation( pos.x, pos.y, pos.z );
-						this._scaleMatrix.makeScale( sizeInverse, sizeInverse, sizeInverse );
-						this._m4_2.makeTranslation( _offset.x, _offset.y, _offset.z ).multiply( this._translationMatrix );
-						this._m4_2.multiply( this._scaleMatrix );
-						this._translationMatrix.makeTranslation( - pos.x, - pos.y, - pos.z );
-						this._m4_2.multiply( this._translationMatrix );
-						this.setTransformationMatrices( this._m4_1, this._m4_2 );
-
-					} else {
-
-						this.setTransformationMatrices( this._m4_1 );
-
-					}
-
-					return _transformation;
-
-				}
-
-			};
-
-			this.setFov = value => {
-
-				if ( this.camera.isPerspectiveCamera ) {
-
-					this.camera.fov = THREE.MathUtils.clamp( value, this.minFov, this.maxFov );
-					this.camera.updateProjectionMatrix();
-
-				}
-
-			};
-
-			this.zRotate = ( point, angle ) => {
-
-				this._rotationMatrix.makeRotationAxis( this._rotationAxis, angle );
-				this._translationMatrix.makeTranslation( - point.x, - point.y, - point.z );
-				this._m4_1.makeTranslation( point.x, point.y, point.z );
-				this._m4_1.multiply( this._rotationMatrix );
-				this._m4_1.multiply( this._translationMatrix );
-				this._v3_1.setFromMatrixPosition( this._gizmoMatrixState ).sub( point ); //vector from rotation center to gizmos position
-				this._v3_2.copy( this._v3_1 ).applyAxisAngle( this._rotationAxis, angle ); //apply rotation
-				this._v3_2.sub( this._v3_1 );
-				this._m4_2.makeTranslation( this._v3_2.x, this._v3_2.y, this._v3_2.z );
-				this.setTransformationMatrices( this._m4_1, this._m4_2 );
-				return _transformation;
-
-			};
-
-			this.unprojectOnObj = ( cursor, camera ) => {
-
-				const raycaster = this.getRaycaster();
-				raycaster.near = camera.near;
-				raycaster.far = camera.far;
-				raycaster.setFromCamera( cursor, camera );
-				const intersect = raycaster.intersectObjects( this.scene.children, true );
-				for ( let i = 0; i < intersect.length; i ++ ) {
-
-					if ( intersect[ i ].object.uuid != this._gizmos.uuid && intersect[ i ].face != null ) {
-
-						return intersect[ i ].point.clone();
-
-					}
-
-				}
-
-				return null;
-
-			};
-
-			this.unprojectOnTbSurface = ( camera, cursorX, cursorY, canvas, tbRadius ) => {
-
-				if ( camera.type == 'OrthographicCamera' ) {
-
-					this._v2_1.copy( this.getCursorPosition( cursorX, cursorY, canvas ) );
-					this._v3_1.set( this._v2_1.x, this._v2_1.y, 0 );
-					const x2 = Math.pow( this._v2_1.x, 2 );
-					const y2 = Math.pow( this._v2_1.y, 2 );
-					const r2 = Math.pow( this._tbRadius, 2 );
-					if ( x2 + y2 <= r2 * 0.5 ) {
-
-						//intersection with sphere
-						this._v3_1.setZ( Math.sqrt( r2 - ( x2 + y2 ) ) );
-
-					} else {
-
-						//intersection with hyperboloid
-						this._v3_1.setZ( r2 * 0.5 / Math.sqrt( x2 + y2 ) );
-
-					}
-
-					return this._v3_1;
-
-				} else if ( camera.type == 'PerspectiveCamera' ) {
-
-					//unproject cursor on the near plane
-					this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
-					this._v3_1.set( this._v2_1.x, this._v2_1.y, - 1 );
-					this._v3_1.applyMatrix4( camera.projectionMatrixInverse );
-					const rayDir = this._v3_1.clone().normalize(); //unprojected ray direction
-					const cameraGizmoDistance = camera.position.distanceTo( this._gizmos.position );
-					const radius2 = Math.pow( tbRadius, 2 );
-
-					//	  camera
-					//		|\
-					//		| \
-					//		|  \
-					//	h	|	\
-					//		| 	 \
-					//		| 	  \
-					//	_ _ | _ _ _\ _ _  near plane
-					//			l
-
-					const h = this._v3_1.z;
-					const l = Math.sqrt( Math.pow( this._v3_1.x, 2 ) + Math.pow( this._v3_1.y, 2 ) );
-					if ( l == 0 ) {
-
-						//ray aligned with camera
-						rayDir.set( this._v3_1.x, this._v3_1.y, tbRadius );
-						return rayDir;
-
-					}
-
-					const m = h / l;
-					const q = cameraGizmoDistance;
-
-					/*
-         * calculate intersection point between unprojected ray and trackball surface
-         *|y = m * x + q
-         *|x^2 + y^2 = r^2
-         *
-         * (m^2 + 1) * x^2 + (2 * m * q) * x + q^2 - r^2 = 0
-         */
-					let a = Math.pow( m, 2 ) + 1;
-					let b = 2 * m * q;
-					let c = Math.pow( q, 2 ) - radius2;
-					let delta = Math.pow( b, 2 ) - 4 * a * c;
-					if ( delta >= 0 ) {
-
-						//intersection with sphere
-						this._v2_1.setX( ( - b - Math.sqrt( delta ) ) / ( 2 * a ) );
-						this._v2_1.setY( m * this._v2_1.x + q );
-						const angle = THREE.MathUtils.RAD2DEG * this._v2_1.angle();
-						if ( angle >= 45 ) {
-
-							//if angle between intersection point and X' axis is >= 45°, return that point
-							//otherwise, calculate intersection point with hyperboloid
-
-							const rayLength = Math.sqrt( Math.pow( this._v2_1.x, 2 ) + Math.pow( cameraGizmoDistance - this._v2_1.y, 2 ) );
-							rayDir.multiplyScalar( rayLength );
-							rayDir.z += cameraGizmoDistance;
-							return rayDir;
-
-						}
-
-					}
-
-					//intersection with hyperboloid
-					/*
-         *|y = m * x + q
-         *|y = (1 / x) * (r^2 / 2)
-         *
-         * m * x^2 + q * x - r^2 / 2 = 0
-         */
-
-					a = m;
-					b = q;
-					c = - radius2 * 0.5;
-					delta = Math.pow( b, 2 ) - 4 * a * c;
-					this._v2_1.setX( ( - b - Math.sqrt( delta ) ) / ( 2 * a ) );
-					this._v2_1.setY( m * this._v2_1.x + q );
-					const rayLength = Math.sqrt( Math.pow( this._v2_1.x, 2 ) + Math.pow( cameraGizmoDistance - this._v2_1.y, 2 ) );
-					rayDir.multiplyScalar( rayLength );
-					rayDir.z += cameraGizmoDistance;
-					return rayDir;
-
-				}
-
-			};
-
-			this.unprojectOnTbPlane = ( camera, cursorX, cursorY, canvas, initialDistance = false ) => {
-
-				if ( camera.type == 'OrthographicCamera' ) {
-
-					this._v2_1.copy( this.getCursorPosition( cursorX, cursorY, canvas ) );
-					this._v3_1.set( this._v2_1.x, this._v2_1.y, 0 );
-					return this._v3_1.clone();
-
-				} else if ( camera.type == 'PerspectiveCamera' ) {
-
-					this._v2_1.copy( this.getCursorNDC( cursorX, cursorY, canvas ) );
-
-					//unproject cursor on the near plane
-					this._v3_1.set( this._v2_1.x, this._v2_1.y, - 1 );
-					this._v3_1.applyMatrix4( camera.projectionMatrixInverse );
-					const rayDir = this._v3_1.clone().normalize(); //unprojected ray direction
-
-					//	  camera
-					//		|\
-					//		| \
-					//		|  \
-					//	h	|	\
-					//		| 	 \
-					//		| 	  \
-					//	_ _ | _ _ _\ _ _  near plane
-					//			l
-
-					const h = this._v3_1.z;
-					const l = Math.sqrt( Math.pow( this._v3_1.x, 2 ) + Math.pow( this._v3_1.y, 2 ) );
-					let cameraGizmoDistance;
-					if ( initialDistance ) {
-
-						cameraGizmoDistance = this._v3_1.setFromMatrixPosition( this._cameraMatrixState0 ).distanceTo( this._v3_2.setFromMatrixPosition( this._gizmoMatrixState0 ) );
-
-					} else {
-
-						cameraGizmoDistance = camera.position.distanceTo( this._gizmos.position );
-
-					}
-
-					/*
-         * calculate intersection point between unprojected ray and the plane
-         *|y = mx + q
-         *|y = 0
-         *
-         * x = -q/m
-        */
-					if ( l == 0 ) {
-
-						//ray aligned with camera
-						rayDir.set( 0, 0, 0 );
-						return rayDir;
-
-					}
-
-					const m = h / l;
-					const q = cameraGizmoDistance;
-					const x = - q / m;
-					const rayLength = Math.sqrt( Math.pow( q, 2 ) + Math.pow( x, 2 ) );
-					rayDir.multiplyScalar( rayLength );
-					rayDir.z = 0;
-					return rayDir;
-
-				}
-
-			};
-
-			this.updateMatrixState = () => {
-
-				//update camera and gizmos state
-				this._cameraMatrixState.copy( this.camera.matrix );
-				this._gizmoMatrixState.copy( this._gizmos.matrix );
-				if ( this.camera.isOrthographicCamera ) {
-
-					this._cameraProjectionState.copy( this.camera.projectionMatrix );
-					this.camera.updateProjectionMatrix();
-					this._zoomState = this.camera.zoom;
-
-				} else if ( this.camera.isPerspectiveCamera ) {
-
-					this._fovState = this.camera.fov;
-
-				}
-
-			};
-
-			this.updateTbState = ( newState, updateMatrices ) => {
-
-				this._state = newState;
-				if ( updateMatrices ) {
-
-					this.updateMatrixState();
-
-				}
-
-			};
-
-			this.update = () => {
-
-				const EPS = 0.000001;
-				if ( this.target.equals( this._currentTarget ) === false ) {
-
-					this._gizmos.position.copy( this.target ); //for correct radius calculation
-					this._tbRadius = this.calculateTbRadius( this.camera );
-					this.makeGizmos( this.target, this._tbRadius );
-					this._currentTarget.copy( this.target );
-
-				}
-
-				//check min/max parameters
-				if ( this.camera.isOrthographicCamera ) {
-
-					//check zoom
-					if ( this.camera.zoom > this.maxZoom || this.camera.zoom < this.minZoom ) {
-
-						const newZoom = THREE.MathUtils.clamp( this.camera.zoom, this.minZoom, this.maxZoom );
-						this.applyTransformMatrix( this.scale( newZoom / this.camera.zoom, this._gizmos.position, true ) );
-
-					}
-
-				} else if ( this.camera.isPerspectiveCamera ) {
-
-					//check distance
-					const distance = this.camera.position.distanceTo( this._gizmos.position );
-					if ( distance > this.maxDistance + EPS || distance < this.minDistance - EPS ) {
-
-						const newDistance = THREE.MathUtils.clamp( distance, this.minDistance, this.maxDistance );
-						this.applyTransformMatrix( this.scale( newDistance / distance, this._gizmos.position ) );
-						this.updateMatrixState();
-
-					}
-
-					//check fov
-					if ( this.camera.fov < this.minFov || this.camera.fov > this.maxFov ) {
-
-						this.camera.fov = THREE.MathUtils.clamp( this.camera.fov, this.minFov, this.maxFov );
-						this.camera.updateProjectionMatrix();
-
-					}
-
-					const oldRadius = this._tbRadius;
-					this._tbRadius = this.calculateTbRadius( this.camera );
-					if ( oldRadius < this._tbRadius - EPS || oldRadius > this._tbRadius + EPS ) {
-
-						const scale = ( this._gizmos.scale.x + this._gizmos.scale.y + this._gizmos.scale.z ) / 3;
-						const newRadius = this._tbRadius / scale;
-						const curve = new THREE.EllipseCurve( 0, 0, newRadius, newRadius );
-						const points = curve.getPoints( this._curvePts );
-						const curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
-						for ( const gizmo in this._gizmos.children ) {
-
-							this._gizmos.children[ gizmo ].geometry = curveGeometry;
-
-						}
-
-					}
-
-				}
-
-				this.camera.lookAt( this._gizmos.position );
-
-			};
-
-			this.setStateFromJSON = json => {
-
-				const state = JSON.parse( json );
-				if ( state.arcballState != undefined ) {
-
-					this._cameraMatrixState.fromArray( state.arcballState.cameraMatrix.elements );
-					this._cameraMatrixState.decompose( this.camera.position, this.camera.quaternion, this.camera.scale );
-					this.camera.up.copy( state.arcballState.cameraUp );
-					this.camera.near = state.arcballState.cameraNear;
-					this.camera.far = state.arcballState.cameraFar;
-					this.camera.zoom = state.arcballState.cameraZoom;
-					if ( this.camera.isPerspectiveCamera ) {
-
-						this.camera.fov = state.arcballState.cameraFov;
-
-					}
-
-					this._gizmoMatrixState.fromArray( state.arcballState.gizmoMatrix.elements );
-					this._gizmoMatrixState.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-					this.camera.updateMatrix();
-					this.camera.updateProjectionMatrix();
-					this._gizmos.updateMatrix();
-					this._tbRadius = this.calculateTbRadius( this.camera );
-					const gizmoTmp = new THREE.Matrix4().copy( this._gizmoMatrixState0 );
-					this.makeGizmos( this._gizmos.position, this._tbRadius );
-					this._gizmoMatrixState0.copy( gizmoTmp );
-					this.camera.lookAt( this._gizmos.position );
-					this.updateTbState( STATE.IDLE, false );
-					this.dispatchEvent( _changeEvent );
-
-				}
-
-			};
-
-			this.camera = null;
-			this.domElement = domElement;
-			this.scene = scene;
-			this.target = new THREE.Vector3();
-			this._currentTarget = new THREE.Vector3();
-			this.radiusFactor = 0.67;
-			this.mouseActions = [];
-			this._mouseOp = null;
-
-			//global vectors and matrices that are used in some operations to avoid creating new objects every time (e.g. every time cursor moves)
-			this._v2_1 = new THREE.Vector2();
-			this._v3_1 = new THREE.Vector3();
-			this._v3_2 = new THREE.Vector3();
-			this._m4_1 = new THREE.Matrix4();
-			this._m4_2 = new THREE.Matrix4();
-			this._quat = new THREE.Quaternion();
-
-			//transformation matrices
-			this._translationMatrix = new THREE.Matrix4(); //matrix for translation operation
-			this._rotationMatrix = new THREE.Matrix4(); //matrix for rotation operation
-			this._scaleMatrix = new THREE.Matrix4(); //matrix for scaling operation
-
-			this._rotationAxis = new THREE.Vector3(); //axis for rotate operation
-
-			//camera state
-			this._cameraMatrixState = new THREE.Matrix4();
-			this._cameraProjectionState = new THREE.Matrix4();
-			this._fovState = 1;
-			this._upState = new THREE.Vector3();
-			this._zoomState = 1;
-			this._nearPos = 0;
-			this._farPos = 0;
-			this._gizmoMatrixState = new THREE.Matrix4();
-
-			//initial values
-			this._up0 = new THREE.Vector3();
-			this._zoom0 = 1;
-			this._fov0 = 0;
-			this._initialNear = 0;
-			this._nearPos0 = 0;
-			this._initialFar = 0;
-			this._farPos0 = 0;
-			this._cameraMatrixState0 = new THREE.Matrix4();
-			this._gizmoMatrixState0 = new THREE.Matrix4();
-
-			//pointers array
-			this._button = - 1;
-			this._touchStart = [];
-			this._touchCurrent = [];
-			this._input = INPUT.NONE;
-
-			//two fingers touch interaction
-			this._switchSensibility = 32; //minimum movement to be performed to fire single pan start after the second finger has been released
-			this._startFingerDistance = 0; //distance between two fingers
-			this._currentFingerDistance = 0;
-			this._startFingerRotation = 0; //amount of rotation performed with two fingers
-			this._currentFingerRotation = 0;
-
-			//double tap
-			this._devPxRatio = 0;
-			this._downValid = true;
-			this._nclicks = 0;
-			this._downEvents = [];
-			this._downStart = 0; //pointerDown time
-			this._clickStart = 0; //first click time
-			this._maxDownTime = 250;
-			this._maxInterval = 300;
-			this._posThreshold = 24;
-			this._movementThreshold = 24;
-
-			//cursor positions
-			this._currentCursorPosition = new THREE.Vector3();
-			this._startCursorPosition = new THREE.Vector3();
-
-			//grid
-			this._grid = null; //grid to be visualized during pan operation
-			this._gridPosition = new THREE.Vector3();
-
-			//gizmos
-			this._gizmos = new THREE.Group();
-			this._curvePts = 128;
-
-			//animations
-			this._timeStart = - 1; //initial time
-			this._animationId = - 1;
-
-			//focus animation
-			this.focusAnimationTime = 500; //duration of focus animation in ms
-
-			//rotate animation
-			this._timePrev = 0; //time at which previous rotate operation has been detected
-			this._timeCurrent = 0; //time at which current rotate operation has been detected
-			this._anglePrev = 0; //angle of previous rotation
-			this._angleCurrent = 0; //angle of current rotation
-			this._cursorPosPrev = new THREE.Vector3(); //cursor position when previous rotate operation has been detected
-			this._cursorPosCurr = new THREE.Vector3(); //cursor position when current rotate operation has been detected
-			this._wPrev = 0; //angular velocity of the previous rotate operation
-			this._wCurr = 0; //angular velocity of the current rotate operation
-
-			//parameters
-			this.adjustNearFar = false;
-			this.scaleFactor = 1.1; //zoom/distance multiplier
-			this.dampingFactor = 25;
-			this.wMax = 20; //maximum angular velocity allowed
-			this.enableAnimations = true; //if animations should be performed
-			this.enableGrid = false; //if grid should be showed during pan operation
-			this.cursorZoom = false; //if wheel zoom should be cursor centered
-			this.minFov = 5;
-			this.maxFov = 90;
-			this.enabled = true;
-			this.enablePan = true;
-			this.enableRotate = true;
-			this.enableZoom = true;
-			this.enableGizmos = true;
-			this.minDistance = 0;
-			this.maxDistance = Infinity;
-			this.minZoom = 0;
-			this.maxZoom = Infinity;
-
-			//trackball parameters
-			this._tbRadius = 1;
-
-			//FSA
-			this._state = STATE.IDLE;
-			this.setCamera( _camera );
-			if ( this.scene != null ) {
-
-				this.scene.add( this._gizmos );
-
-			}
-
-			this.domElement.style.touchAction = 'none';
-			this._devPxRatio = window.devicePixelRatio;
-			this.initializeMouseActions();
-			this.domElement.addEventListener( 'contextmenu', this.onContextMenu );
-			this.domElement.addEventListener( 'wheel', this.onWheel );
-			this.domElement.addEventListener( 'pointerdown', this.onPointerDown );
-			this.domElement.addEventListener( 'pointercancel', this.onPointerCancel );
-			window.addEventListener( 'resize', this.onWindowResize );
-
-		}
-
-		//listeners
-
-		/**
-   * Apply a transformation matrix, to the camera and gizmos
-   * @param {Object} transformation Object containing matrices to apply to camera and gizmos
-   */
-		applyTransformMatrix( transformation ) {
-
-			if ( transformation.camera != null ) {
-
-				this._m4_1.copy( this._cameraMatrixState ).premultiply( transformation.camera );
-				this._m4_1.decompose( this.camera.position, this.camera.quaternion, this.camera.scale );
-				this.camera.updateMatrix();
-
-				//update camera up vector
-				if ( this._state == STATE.ROTATE || this._state == STATE.ZROTATE || this._state == STATE.ANIMATION_ROTATE ) {
-
-					this.camera.up.copy( this._upState ).applyQuaternion( this.camera.quaternion );
-
-				}
-
-			}
-
-			if ( transformation.gizmos != null ) {
-
-				this._m4_1.copy( this._gizmoMatrixState ).premultiply( transformation.gizmos );
-				this._m4_1.decompose( this._gizmos.position, this._gizmos.quaternion, this._gizmos.scale );
-				this._gizmos.updateMatrix();
-
-			}
-
-			if ( this._state == STATE.SCALE || this._state == STATE.FOCUS || this._state == STATE.ANIMATION_FOCUS ) {
-
-				this._tbRadius = this.calculateTbRadius( this.camera );
-				if ( this.adjustNearFar ) {
-
-					const cameraDistance = this.camera.position.distanceTo( this._gizmos.position );
-					const bb = new THREE.Box3();
-					bb.setFromObject( this._gizmos );
-					const sphere = new THREE.Sphere();
-					bb.getBoundingSphere( sphere );
-					const adjustedNearPosition = Math.max( this._nearPos0, sphere.radius + sphere.center.length() );
-					const regularNearPosition = cameraDistance - this._initialNear;
-					const minNearPos = Math.min( adjustedNearPosition, regularNearPosition );
-					this.camera.near = cameraDistance - minNearPos;
-					const adjustedFarPosition = Math.min( this._farPos0, - sphere.radius + sphere.center.length() );
-					const regularFarPosition = cameraDistance - this._initialFar;
-					const minFarPos = Math.min( adjustedFarPosition, regularFarPosition );
-					this.camera.far = cameraDistance - minFarPos;
-					this.camera.updateProjectionMatrix();
-
-				} else {
-
-					let update = false;
-					if ( this.camera.near != this._initialNear ) {
-
-						this.camera.near = this._initialNear;
-						update = true;
-
-					}
-
-					if ( this.camera.far != this._initialFar ) {
-
-						this.camera.far = this._initialFar;
-						update = true;
-
-					}
-
-					if ( update ) {
-
-						this.camera.updateProjectionMatrix();
-
-					}
-
-				}
-
-			}
-
-		}
-
-		/**
-   * Calculate the angular speed
-   * @param {Number} p0 Position at t0
-   * @param {Number} p1 Position at t1
-   * @param {Number} t0 Initial time in milliseconds
-   * @param {Number} t1 Ending time in milliseconds
-   */
-
-		/**
-   * Set gizmos visibility
-   * @param {Boolean} value Value of gizmos visibility
-   */
-		setGizmosVisible( value ) {
-
-			this._gizmos.visible = value;
-			this.dispatchEvent( _changeEvent );
-
-		}
-
-		/**
-   * Set gizmos radius factor and redraws gizmos
-   * @param {Float} value Value of radius factor
-   */
-		setTbRadius( value ) {
-
-			this.radiusFactor = value;
-			this._tbRadius = this.calculateTbRadius( this.camera );
-			const curve = new THREE.EllipseCurve( 0, 0, this._tbRadius, this._tbRadius );
-			const points = curve.getPoints( this._curvePts );
-			const curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
-			for ( const gizmo in this._gizmos.children ) {
-
-				this._gizmos.children[ gizmo ].geometry = curveGeometry;
-
-			}
-
-			this.dispatchEvent( _changeEvent );
-
-		}
-
-		/**
-   * Creates the rotation gizmos matching trackball center and radius
-   * @param {Vector3} tbCenter The trackball center
-   * @param {number} tbRadius The trackball radius
-   */
-
-		/**
-   * Set values in transformation object
-   * @param {Matrix4} camera Transformation to be applied to the camera
-   * @param {Matrix4} gizmos Transformation to be applied to gizmos
-   */
-		setTransformationMatrices( camera = null, gizmos = null ) {
-
-			if ( camera != null ) {
-
-				if ( _transformation.camera != null ) {
-
-					_transformation.camera.copy( camera );
-
-				} else {
-
-					_transformation.camera = camera.clone();
-
-				}
-
-			} else {
-
-				_transformation.camera = null;
-
-			}
-
-			if ( gizmos != null ) {
-
-				if ( _transformation.gizmos != null ) {
-
-					_transformation.gizmos.copy( gizmos );
-
-				} else {
-
-					_transformation.gizmos = gizmos.clone();
-
-				}
-
-			} else {
-
-				_transformation.gizmos = null;
-
-			}
-
-		}
-
-		/**
-   * Rotate camera around its direction axis passing by a given point by a given angle
-   * @param {Vector3} point The point where the rotation axis is passing trough
-   * @param {Number} angle Angle in radians
-   * @returns The computed transormation matix
-   */
-
-		getRaycaster() {
-
-			return _raycaster;
-
-		}
-
-		/**
-   * Unproject the cursor on the 3D object surface
-   * @param {Vector2} cursor Cursor coordinates in NDC
-   * @param {Camera} camera Virtual camera
-   * @returns {Vector3} The point of intersection with the model, if exist, null otherwise
-   */
-
-	}
-
-	THREE.ArcballControls = ArcballControls;
-
-} )();

+ 0 - 205
examples/js/controls/DragControls.js

@@ -1,205 +0,0 @@
-( function () {
-
-	const _plane = new THREE.Plane();
-	const _raycaster = new THREE.Raycaster();
-	const _pointer = new THREE.Vector2();
-	const _offset = new THREE.Vector3();
-	const _intersection = new THREE.Vector3();
-	const _worldPosition = new THREE.Vector3();
-	const _inverseMatrix = new THREE.Matrix4();
-	class DragControls extends THREE.EventDispatcher {
-
-		constructor( _objects, _camera, _domElement ) {
-
-			super();
-			_domElement.style.touchAction = 'none'; // disable touch scroll
-
-			let _selected = null,
-				_hovered = null;
-			const _intersections = [];
-
-			//
-
-			const scope = this;
-			function activate() {
-
-				_domElement.addEventListener( 'pointermove', onPointerMove );
-				_domElement.addEventListener( 'pointerdown', onPointerDown );
-				_domElement.addEventListener( 'pointerup', onPointerCancel );
-				_domElement.addEventListener( 'pointerleave', onPointerCancel );
-
-			}
-
-			function deactivate() {
-
-				_domElement.removeEventListener( 'pointermove', onPointerMove );
-				_domElement.removeEventListener( 'pointerdown', onPointerDown );
-				_domElement.removeEventListener( 'pointerup', onPointerCancel );
-				_domElement.removeEventListener( 'pointerleave', onPointerCancel );
-				_domElement.style.cursor = '';
-
-			}
-
-			function dispose() {
-
-				deactivate();
-
-			}
-
-			function getObjects() {
-
-				return _objects;
-
-			}
-
-			function getRaycaster() {
-
-				return _raycaster;
-
-			}
-
-			function onPointerMove( event ) {
-
-				if ( scope.enabled === false ) return;
-				updatePointer( event );
-				_raycaster.setFromCamera( _pointer, _camera );
-				if ( _selected ) {
-
-					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
-
-						_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
-
-					}
-
-					scope.dispatchEvent( {
-						type: 'drag',
-						object: _selected
-					} );
-					return;
-
-				}
-
-				// hover support
-
-				if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
-
-					_intersections.length = 0;
-					_raycaster.setFromCamera( _pointer, _camera );
-					_raycaster.intersectObjects( _objects, true, _intersections );
-					if ( _intersections.length > 0 ) {
-
-						const object = _intersections[ 0 ].object;
-						_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
-						if ( _hovered !== object && _hovered !== null ) {
-
-							scope.dispatchEvent( {
-								type: 'hoveroff',
-								object: _hovered
-							} );
-							_domElement.style.cursor = 'auto';
-							_hovered = null;
-
-						}
-
-						if ( _hovered !== object ) {
-
-							scope.dispatchEvent( {
-								type: 'hoveron',
-								object: object
-							} );
-							_domElement.style.cursor = 'pointer';
-							_hovered = object;
-
-						}
-
-					} else {
-
-						if ( _hovered !== null ) {
-
-							scope.dispatchEvent( {
-								type: 'hoveroff',
-								object: _hovered
-							} );
-							_domElement.style.cursor = 'auto';
-							_hovered = null;
-
-						}
-
-					}
-
-				}
-
-			}
-
-			function onPointerDown( event ) {
-
-				if ( scope.enabled === false ) return;
-				updatePointer( event );
-				_intersections.length = 0;
-				_raycaster.setFromCamera( _pointer, _camera );
-				_raycaster.intersectObjects( _objects, true, _intersections );
-				if ( _intersections.length > 0 ) {
-
-					_selected = scope.transformGroup === true ? _objects[ 0 ] : _intersections[ 0 ].object;
-					_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
-					if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
-
-						_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
-						_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
-
-					}
-
-					_domElement.style.cursor = 'move';
-					scope.dispatchEvent( {
-						type: 'dragstart',
-						object: _selected
-					} );
-
-				}
-
-			}
-
-			function onPointerCancel() {
-
-				if ( scope.enabled === false ) return;
-				if ( _selected ) {
-
-					scope.dispatchEvent( {
-						type: 'dragend',
-						object: _selected
-					} );
-					_selected = null;
-
-				}
-
-				_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
-
-			}
-
-			function updatePointer( event ) {
-
-				const rect = _domElement.getBoundingClientRect();
-				_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-				_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
-
-			}
-
-			activate();
-
-			// API
-
-			this.enabled = true;
-			this.transformGroup = false;
-			this.activate = activate;
-			this.deactivate = deactivate;
-			this.dispose = dispose;
-			this.getObjects = getObjects;
-			this.getRaycaster = getRaycaster;
-
-		}
-
-	}
-
-	THREE.DragControls = DragControls;
-
-} )();

+ 0 - 312
examples/js/controls/FirstPersonControls.js

@@ -1,312 +0,0 @@
-( function () {
-
-	const _lookDirection = new THREE.Vector3();
-	const _spherical = new THREE.Spherical();
-	const _target = new THREE.Vector3();
-	class FirstPersonControls {
-
-		constructor( object, domElement ) {
-
-			this.object = object;
-			this.domElement = domElement;
-
-			// API
-
-			this.enabled = true;
-			this.movementSpeed = 1.0;
-			this.lookSpeed = 0.005;
-			this.lookVertical = true;
-			this.autoForward = false;
-			this.activeLook = true;
-			this.heightSpeed = false;
-			this.heightCoef = 1.0;
-			this.heightMin = 0.0;
-			this.heightMax = 1.0;
-			this.constrainVertical = false;
-			this.verticalMin = 0;
-			this.verticalMax = Math.PI;
-			this.mouseDragOn = false;
-
-			// internals
-
-			this.autoSpeedFactor = 0.0;
-			this.pointerX = 0;
-			this.pointerY = 0;
-			this.moveForward = false;
-			this.moveBackward = false;
-			this.moveLeft = false;
-			this.moveRight = false;
-			this.viewHalfX = 0;
-			this.viewHalfY = 0;
-
-			// private variables
-
-			let lat = 0;
-			let lon = 0;
-
-			//
-
-			this.handleResize = function () {
-
-				if ( this.domElement === document ) {
-
-					this.viewHalfX = window.innerWidth / 2;
-					this.viewHalfY = window.innerHeight / 2;
-
-				} else {
-
-					this.viewHalfX = this.domElement.offsetWidth / 2;
-					this.viewHalfY = this.domElement.offsetHeight / 2;
-
-				}
-
-			};
-
-			this.onPointerDown = function ( event ) {
-
-				if ( this.domElement !== document ) {
-
-					this.domElement.focus();
-
-				}
-
-				if ( this.activeLook ) {
-
-					switch ( event.button ) {
-
-						case 0:
-							this.moveForward = true;
-							break;
-						case 2:
-							this.moveBackward = true;
-							break;
-
-					}
-
-				}
-
-				this.mouseDragOn = true;
-
-			};
-
-			this.onPointerUp = function ( event ) {
-
-				if ( this.activeLook ) {
-
-					switch ( event.button ) {
-
-						case 0:
-							this.moveForward = false;
-							break;
-						case 2:
-							this.moveBackward = false;
-							break;
-
-					}
-
-				}
-
-				this.mouseDragOn = false;
-
-			};
-
-			this.onPointerMove = function ( event ) {
-
-				if ( this.domElement === document ) {
-
-					this.pointerX = event.pageX - this.viewHalfX;
-					this.pointerY = event.pageY - this.viewHalfY;
-
-				} else {
-
-					this.pointerX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
-					this.pointerY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
-
-				}
-
-			};
-
-			this.onKeyDown = function ( event ) {
-
-				switch ( event.code ) {
-
-					case 'ArrowUp':
-					case 'KeyW':
-						this.moveForward = true;
-						break;
-					case 'ArrowLeft':
-					case 'KeyA':
-						this.moveLeft = true;
-						break;
-					case 'ArrowDown':
-					case 'KeyS':
-						this.moveBackward = true;
-						break;
-					case 'ArrowRight':
-					case 'KeyD':
-						this.moveRight = true;
-						break;
-					case 'KeyR':
-						this.moveUp = true;
-						break;
-					case 'KeyF':
-						this.moveDown = true;
-						break;
-
-				}
-
-			};
-
-			this.onKeyUp = function ( event ) {
-
-				switch ( event.code ) {
-
-					case 'ArrowUp':
-					case 'KeyW':
-						this.moveForward = false;
-						break;
-					case 'ArrowLeft':
-					case 'KeyA':
-						this.moveLeft = false;
-						break;
-					case 'ArrowDown':
-					case 'KeyS':
-						this.moveBackward = false;
-						break;
-					case 'ArrowRight':
-					case 'KeyD':
-						this.moveRight = false;
-						break;
-					case 'KeyR':
-						this.moveUp = false;
-						break;
-					case 'KeyF':
-						this.moveDown = false;
-						break;
-
-				}
-
-			};
-
-			this.lookAt = function ( x, y, z ) {
-
-				if ( x.isVector3 ) {
-
-					_target.copy( x );
-
-				} else {
-
-					_target.set( x, y, z );
-
-				}
-
-				this.object.lookAt( _target );
-				setOrientation( this );
-				return this;
-
-			};
-
-			this.update = function () {
-
-				const targetPosition = new THREE.Vector3();
-				return function update( delta ) {
-
-					if ( this.enabled === false ) return;
-					if ( this.heightSpeed ) {
-
-						const y = THREE.MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
-						const heightDelta = y - this.heightMin;
-						this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
-
-					} else {
-
-						this.autoSpeedFactor = 0.0;
-
-					}
-
-					const actualMoveSpeed = delta * this.movementSpeed;
-					if ( this.moveForward || this.autoForward && ! this.moveBackward ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
-					if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
-					if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
-					if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
-					if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
-					if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
-					let actualLookSpeed = delta * this.lookSpeed;
-					if ( ! this.activeLook ) {
-
-						actualLookSpeed = 0;
-
-					}
-
-					let verticalLookRatio = 1;
-					if ( this.constrainVertical ) {
-
-						verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
-
-					}
-
-					lon -= this.pointerX * actualLookSpeed;
-					if ( this.lookVertical ) lat -= this.pointerY * actualLookSpeed * verticalLookRatio;
-					lat = Math.max( - 85, Math.min( 85, lat ) );
-					let phi = THREE.MathUtils.degToRad( 90 - lat );
-					const theta = THREE.MathUtils.degToRad( lon );
-					if ( this.constrainVertical ) {
-
-						phi = THREE.MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
-
-					}
-
-					const position = this.object.position;
-					targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
-					this.object.lookAt( targetPosition );
-
-				};
-
-			}();
-			this.dispose = function () {
-
-				this.domElement.removeEventListener( 'contextmenu', contextmenu );
-				this.domElement.removeEventListener( 'pointerdown', _onPointerDown );
-				this.domElement.removeEventListener( 'pointermove', _onPointerMove );
-				this.domElement.removeEventListener( 'pointerup', _onPointerUp );
-				window.removeEventListener( 'keydown', _onKeyDown );
-				window.removeEventListener( 'keyup', _onKeyUp );
-
-			};
-
-			const _onPointerMove = this.onPointerMove.bind( this );
-			const _onPointerDown = this.onPointerDown.bind( this );
-			const _onPointerUp = this.onPointerUp.bind( this );
-			const _onKeyDown = this.onKeyDown.bind( this );
-			const _onKeyUp = this.onKeyUp.bind( this );
-			this.domElement.addEventListener( 'contextmenu', contextmenu );
-			this.domElement.addEventListener( 'pointerdown', _onPointerDown );
-			this.domElement.addEventListener( 'pointermove', _onPointerMove );
-			this.domElement.addEventListener( 'pointerup', _onPointerUp );
-			window.addEventListener( 'keydown', _onKeyDown );
-			window.addEventListener( 'keyup', _onKeyUp );
-			function setOrientation( controls ) {
-
-				const quaternion = controls.object.quaternion;
-				_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
-				_spherical.setFromVector3( _lookDirection );
-				lat = 90 - THREE.MathUtils.radToDeg( _spherical.phi );
-				lon = THREE.MathUtils.radToDeg( _spherical.theta );
-
-			}
-
-			this.handleResize();
-			setOrientation( this );
-
-		}
-
-	}
-	function contextmenu( event ) {
-
-		event.preventDefault();
-
-	}
-
-	THREE.FirstPersonControls = FirstPersonControls;
-
-} )();

+ 0 - 321
examples/js/controls/FlyControls.js

@@ -1,321 +0,0 @@
-( function () {
-
-	const _changeEvent = {
-		type: 'change'
-	};
-	class FlyControls extends THREE.EventDispatcher {
-
-		constructor( object, domElement ) {
-
-			super();
-			this.object = object;
-			this.domElement = domElement;
-
-			// API
-
-			this.movementSpeed = 1.0;
-			this.rollSpeed = 0.005;
-			this.dragToLook = false;
-			this.autoForward = false;
-
-			// disable default target object behavior
-
-			// internals
-
-			const scope = this;
-			const EPS = 0.000001;
-			const lastQuaternion = new THREE.Quaternion();
-			const lastPosition = new THREE.Vector3();
-			this.tmpQuaternion = new THREE.Quaternion();
-			this.status = 0;
-			this.moveState = {
-				up: 0,
-				down: 0,
-				left: 0,
-				right: 0,
-				forward: 0,
-				back: 0,
-				pitchUp: 0,
-				pitchDown: 0,
-				yawLeft: 0,
-				yawRight: 0,
-				rollLeft: 0,
-				rollRight: 0
-			};
-			this.moveVector = new THREE.Vector3( 0, 0, 0 );
-			this.rotationVector = new THREE.Vector3( 0, 0, 0 );
-			this.keydown = function ( event ) {
-
-				if ( event.altKey ) {
-
-					return;
-
-				}
-
-				switch ( event.code ) {
-
-					case 'ShiftLeft':
-					case 'ShiftRight':
-						this.movementSpeedMultiplier = .1;
-						break;
-					case 'KeyW':
-						this.moveState.forward = 1;
-						break;
-					case 'KeyS':
-						this.moveState.back = 1;
-						break;
-					case 'KeyA':
-						this.moveState.left = 1;
-						break;
-					case 'KeyD':
-						this.moveState.right = 1;
-						break;
-					case 'KeyR':
-						this.moveState.up = 1;
-						break;
-					case 'KeyF':
-						this.moveState.down = 1;
-						break;
-					case 'ArrowUp':
-						this.moveState.pitchUp = 1;
-						break;
-					case 'ArrowDown':
-						this.moveState.pitchDown = 1;
-						break;
-					case 'ArrowLeft':
-						this.moveState.yawLeft = 1;
-						break;
-					case 'ArrowRight':
-						this.moveState.yawRight = 1;
-						break;
-					case 'KeyQ':
-						this.moveState.rollLeft = 1;
-						break;
-					case 'KeyE':
-						this.moveState.rollRight = 1;
-						break;
-
-				}
-
-				this.updateMovementVector();
-				this.updateRotationVector();
-
-			};
-
-			this.keyup = function ( event ) {
-
-				switch ( event.code ) {
-
-					case 'ShiftLeft':
-					case 'ShiftRight':
-						this.movementSpeedMultiplier = 1;
-						break;
-					case 'KeyW':
-						this.moveState.forward = 0;
-						break;
-					case 'KeyS':
-						this.moveState.back = 0;
-						break;
-					case 'KeyA':
-						this.moveState.left = 0;
-						break;
-					case 'KeyD':
-						this.moveState.right = 0;
-						break;
-					case 'KeyR':
-						this.moveState.up = 0;
-						break;
-					case 'KeyF':
-						this.moveState.down = 0;
-						break;
-					case 'ArrowUp':
-						this.moveState.pitchUp = 0;
-						break;
-					case 'ArrowDown':
-						this.moveState.pitchDown = 0;
-						break;
-					case 'ArrowLeft':
-						this.moveState.yawLeft = 0;
-						break;
-					case 'ArrowRight':
-						this.moveState.yawRight = 0;
-						break;
-					case 'KeyQ':
-						this.moveState.rollLeft = 0;
-						break;
-					case 'KeyE':
-						this.moveState.rollRight = 0;
-						break;
-
-				}
-
-				this.updateMovementVector();
-				this.updateRotationVector();
-
-			};
-
-			this.pointerdown = function ( event ) {
-
-				if ( this.dragToLook ) {
-
-					this.status ++;
-
-				} else {
-
-					switch ( event.button ) {
-
-						case 0:
-							this.moveState.forward = 1;
-							break;
-						case 2:
-							this.moveState.back = 1;
-							break;
-
-					}
-
-					this.updateMovementVector();
-
-				}
-
-			};
-
-			this.pointermove = function ( event ) {
-
-				if ( ! this.dragToLook || this.status > 0 ) {
-
-					const container = this.getContainerDimensions();
-					const halfWidth = container.size[ 0 ] / 2;
-					const halfHeight = container.size[ 1 ] / 2;
-					this.moveState.yawLeft = - ( event.pageX - container.offset[ 0 ] - halfWidth ) / halfWidth;
-					this.moveState.pitchDown = ( event.pageY - container.offset[ 1 ] - halfHeight ) / halfHeight;
-					this.updateRotationVector();
-
-				}
-
-			};
-
-			this.pointerup = function ( event ) {
-
-				if ( this.dragToLook ) {
-
-					this.status --;
-					this.moveState.yawLeft = this.moveState.pitchDown = 0;
-
-				} else {
-
-					switch ( event.button ) {
-
-						case 0:
-							this.moveState.forward = 0;
-							break;
-						case 2:
-							this.moveState.back = 0;
-							break;
-
-					}
-
-					this.updateMovementVector();
-
-				}
-
-				this.updateRotationVector();
-
-			};
-
-			this.update = function ( delta ) {
-
-				const moveMult = delta * scope.movementSpeed;
-				const rotMult = delta * scope.rollSpeed;
-				scope.object.translateX( scope.moveVector.x * moveMult );
-				scope.object.translateY( scope.moveVector.y * moveMult );
-				scope.object.translateZ( scope.moveVector.z * moveMult );
-				scope.tmpQuaternion.set( scope.rotationVector.x * rotMult, scope.rotationVector.y * rotMult, scope.rotationVector.z * rotMult, 1 ).normalize();
-				scope.object.quaternion.multiply( scope.tmpQuaternion );
-				if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
-					scope.dispatchEvent( _changeEvent );
-					lastQuaternion.copy( scope.object.quaternion );
-					lastPosition.copy( scope.object.position );
-
-				}
-
-			};
-
-			this.updateMovementVector = function () {
-
-				const forward = this.moveState.forward || this.autoForward && ! this.moveState.back ? 1 : 0;
-				this.moveVector.x = - this.moveState.left + this.moveState.right;
-				this.moveVector.y = - this.moveState.down + this.moveState.up;
-				this.moveVector.z = - forward + this.moveState.back;
-
-				//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
-
-			};
-
-			this.updateRotationVector = function () {
-
-				this.rotationVector.x = - this.moveState.pitchDown + this.moveState.pitchUp;
-				this.rotationVector.y = - this.moveState.yawRight + this.moveState.yawLeft;
-				this.rotationVector.z = - this.moveState.rollRight + this.moveState.rollLeft;
-
-				//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
-
-			};
-
-			this.getContainerDimensions = function () {
-
-				if ( this.domElement != document ) {
-
-					return {
-						size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
-						offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
-					};
-
-				} else {
-
-					return {
-						size: [ window.innerWidth, window.innerHeight ],
-						offset: [ 0, 0 ]
-					};
-
-				}
-
-			};
-
-			this.dispose = function () {
-
-				this.domElement.removeEventListener( 'contextmenu', contextmenu );
-				this.domElement.removeEventListener( 'pointerdown', _pointerdown );
-				this.domElement.removeEventListener( 'pointermove', _pointermove );
-				this.domElement.removeEventListener( 'pointerup', _pointerup );
-				window.removeEventListener( 'keydown', _keydown );
-				window.removeEventListener( 'keyup', _keyup );
-
-			};
-
-			const _pointermove = this.pointermove.bind( this );
-			const _pointerdown = this.pointerdown.bind( this );
-			const _pointerup = this.pointerup.bind( this );
-			const _keydown = this.keydown.bind( this );
-			const _keyup = this.keyup.bind( this );
-			this.domElement.addEventListener( 'contextmenu', contextmenu );
-			this.domElement.addEventListener( 'pointerdown', _pointerdown );
-			this.domElement.addEventListener( 'pointermove', _pointermove );
-			this.domElement.addEventListener( 'pointerup', _pointerup );
-			window.addEventListener( 'keydown', _keydown );
-			window.addEventListener( 'keyup', _keyup );
-			this.updateMovementVector();
-			this.updateRotationVector();
-
-		}
-
-	}
-	function contextmenu( event ) {
-
-		event.preventDefault();
-
-	}
-
-	THREE.FlyControls = FlyControls;
-
-} )();

+ 0 - 1101
examples/js/controls/OrbitControls.js

@@ -1,1101 +0,0 @@
-( function () {
-
-	// This set of controls performs orbiting, dollying (zooming), and panning.
-	// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
-	//
-	//    Orbit - left mouse / touch: one-finger move
-	//    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-	//    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
-
-	const _changeEvent = {
-		type: 'change'
-	};
-	const _startEvent = {
-		type: 'start'
-	};
-	const _endEvent = {
-		type: 'end'
-	};
-	class OrbitControls extends THREE.EventDispatcher {
-
-		constructor( object, domElement ) {
-
-			super();
-			this.object = object;
-			this.domElement = domElement;
-			this.domElement.style.touchAction = 'none'; // disable touch scroll
-
-			// Set to false to disable this control
-			this.enabled = true;
-
-			// "target" sets the location of focus, where the object orbits around
-			this.target = new THREE.Vector3();
-
-			// How far you can dolly in and out ( PerspectiveCamera only )
-			this.minDistance = 0;
-			this.maxDistance = Infinity;
-
-			// How far you can zoom in and out ( OrthographicCamera only )
-			this.minZoom = 0;
-			this.maxZoom = Infinity;
-
-			// How far you can orbit vertically, upper and lower limits.
-			// Range is 0 to Math.PI radians.
-			this.minPolarAngle = 0; // radians
-			this.maxPolarAngle = Math.PI; // radians
-
-			// How far you can orbit horizontally, upper and lower limits.
-			// If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI )
-			this.minAzimuthAngle = - Infinity; // radians
-			this.maxAzimuthAngle = Infinity; // radians
-
-			// Set to true to enable damping (inertia)
-			// If damping is enabled, you must call controls.update() in your animation loop
-			this.enableDamping = false;
-			this.dampingFactor = 0.05;
-
-			// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
-			// Set to false to disable zooming
-			this.enableZoom = true;
-			this.zoomSpeed = 1.0;
-
-			// Set to false to disable rotating
-			this.enableRotate = true;
-			this.rotateSpeed = 1.0;
-
-			// Set to false to disable panning
-			this.enablePan = true;
-			this.panSpeed = 1.0;
-			this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up
-			this.keyPanSpeed = 7.0; // pixels moved per arrow key push
-
-			// Set to true to automatically rotate around the target
-			// If auto-rotate is enabled, you must call controls.update() in your animation loop
-			this.autoRotate = false;
-			this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60
-
-			// The four arrow keys
-			this.keys = {
-				LEFT: 'ArrowLeft',
-				UP: 'ArrowUp',
-				RIGHT: 'ArrowRight',
-				BOTTOM: 'ArrowDown'
-			};
-
-			// Mouse buttons
-			this.mouseButtons = {
-				LEFT: THREE.MOUSE.ROTATE,
-				MIDDLE: THREE.MOUSE.DOLLY,
-				RIGHT: THREE.MOUSE.PAN
-			};
-
-			// Touch fingers
-			this.touches = {
-				ONE: THREE.TOUCH.ROTATE,
-				TWO: THREE.TOUCH.DOLLY_PAN
-			};
-
-			// for reset
-			this.target0 = this.target.clone();
-			this.position0 = this.object.position.clone();
-			this.zoom0 = this.object.zoom;
-
-			// the target DOM element for key events
-			this._domElementKeyEvents = null;
-
-			//
-			// public methods
-			//
-
-			this.getPolarAngle = function () {
-
-				return spherical.phi;
-
-			};
-
-			this.getAzimuthalAngle = function () {
-
-				return spherical.theta;
-
-			};
-
-			this.getDistance = function () {
-
-				return this.object.position.distanceTo( this.target );
-
-			};
-
-			this.listenToKeyEvents = function ( domElement ) {
-
-				domElement.addEventListener( 'keydown', onKeyDown );
-				this._domElementKeyEvents = domElement;
-
-			};
-
-			this.saveState = function () {
-
-				scope.target0.copy( scope.target );
-				scope.position0.copy( scope.object.position );
-				scope.zoom0 = scope.object.zoom;
-
-			};
-
-			this.reset = function () {
-
-				scope.target.copy( scope.target0 );
-				scope.object.position.copy( scope.position0 );
-				scope.object.zoom = scope.zoom0;
-				scope.object.updateProjectionMatrix();
-				scope.dispatchEvent( _changeEvent );
-				scope.update();
-				state = STATE.NONE;
-
-			};
-
-			// this method is exposed, but perhaps it would be better if we can make it private...
-			this.update = function () {
-
-				const offset = new THREE.Vector3();
-
-				// so camera.up is the orbit axis
-				const quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
-				const quatInverse = quat.clone().invert();
-				const lastPosition = new THREE.Vector3();
-				const lastQuaternion = new THREE.Quaternion();
-				const twoPI = 2 * Math.PI;
-				return function update() {
-
-					const position = scope.object.position;
-					offset.copy( position ).sub( scope.target );
-
-					// rotate offset to "y-axis-is-up" space
-					offset.applyQuaternion( quat );
-
-					// angle from z-axis around y-axis
-					spherical.setFromVector3( offset );
-					if ( scope.autoRotate && state === STATE.NONE ) {
-
-						rotateLeft( getAutoRotationAngle() );
-
-					}
-
-					if ( scope.enableDamping ) {
-
-						spherical.theta += sphericalDelta.theta * scope.dampingFactor;
-						spherical.phi += sphericalDelta.phi * scope.dampingFactor;
-
-					} else {
-
-						spherical.theta += sphericalDelta.theta;
-						spherical.phi += sphericalDelta.phi;
-
-					}
-
-					// restrict theta to be between desired limits
-
-					let min = scope.minAzimuthAngle;
-					let max = scope.maxAzimuthAngle;
-					if ( isFinite( min ) && isFinite( max ) ) {
-
-						if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI;
-						if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI;
-						if ( min <= max ) {
-
-							spherical.theta = Math.max( min, Math.min( max, spherical.theta ) );
-
-						} else {
-
-							spherical.theta = spherical.theta > ( min + max ) / 2 ? Math.max( min, spherical.theta ) : Math.min( max, spherical.theta );
-
-						}
-
-					}
-
-					// restrict phi to be between desired limits
-					spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
-					spherical.makeSafe();
-					spherical.radius *= scale;
-
-					// restrict radius to be between desired limits
-					spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
-
-					// move target to panned location
-
-					if ( scope.enableDamping === true ) {
-
-						scope.target.addScaledVector( panOffset, scope.dampingFactor );
-
-					} else {
-
-						scope.target.add( panOffset );
-
-					}
-
-					offset.setFromSpherical( spherical );
-
-					// rotate offset back to "camera-up-vector-is-up" space
-					offset.applyQuaternion( quatInverse );
-					position.copy( scope.target ).add( offset );
-					scope.object.lookAt( scope.target );
-					if ( scope.enableDamping === true ) {
-
-						sphericalDelta.theta *= 1 - scope.dampingFactor;
-						sphericalDelta.phi *= 1 - scope.dampingFactor;
-						panOffset.multiplyScalar( 1 - scope.dampingFactor );
-
-					} else {
-
-						sphericalDelta.set( 0, 0, 0 );
-						panOffset.set( 0, 0, 0 );
-
-					}
-
-					scale = 1;
-
-					// update condition is:
-					// min(camera displacement, camera rotation in radians)^2 > EPS
-					// using small-angle approximation cos(x/2) = 1 - x^2 / 8
-
-					if ( zoomChanged || lastPosition.distanceToSquared( scope.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
-						scope.dispatchEvent( _changeEvent );
-						lastPosition.copy( scope.object.position );
-						lastQuaternion.copy( scope.object.quaternion );
-						zoomChanged = false;
-						return true;
-
-					}
-
-					return false;
-
-				};
-
-			}();
-			this.dispose = function () {
-
-				scope.domElement.removeEventListener( 'contextmenu', onContextMenu );
-				scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
-				scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
-				scope.domElement.removeEventListener( 'wheel', onMouseWheel );
-				scope.domElement.removeEventListener( 'pointermove', onPointerMove );
-				scope.domElement.removeEventListener( 'pointerup', onPointerUp );
-				if ( scope._domElementKeyEvents !== null ) {
-
-					scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown );
-
-				}
-
-				//scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
-
-			};
-
-			//
-			// internals
-			//
-
-			const scope = this;
-			const STATE = {
-				NONE: - 1,
-				ROTATE: 0,
-				DOLLY: 1,
-				PAN: 2,
-				TOUCH_ROTATE: 3,
-				TOUCH_PAN: 4,
-				TOUCH_DOLLY_PAN: 5,
-				TOUCH_DOLLY_ROTATE: 6
-			};
-			let state = STATE.NONE;
-			const EPS = 0.000001;
-
-			// current position in spherical coordinates
-			const spherical = new THREE.Spherical();
-			const sphericalDelta = new THREE.Spherical();
-			let scale = 1;
-			const panOffset = new THREE.Vector3();
-			let zoomChanged = false;
-			const rotateStart = new THREE.Vector2();
-			const rotateEnd = new THREE.Vector2();
-			const rotateDelta = new THREE.Vector2();
-			const panStart = new THREE.Vector2();
-			const panEnd = new THREE.Vector2();
-			const panDelta = new THREE.Vector2();
-			const dollyStart = new THREE.Vector2();
-			const dollyEnd = new THREE.Vector2();
-			const dollyDelta = new THREE.Vector2();
-			const pointers = [];
-			const pointerPositions = {};
-			function getAutoRotationAngle() {
-
-				return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
-
-			}
-
-			function getZoomScale() {
-
-				return Math.pow( 0.95, scope.zoomSpeed );
-
-			}
-
-			function rotateLeft( angle ) {
-
-				sphericalDelta.theta -= angle;
-
-			}
-
-			function rotateUp( angle ) {
-
-				sphericalDelta.phi -= angle;
-
-			}
-
-			const panLeft = function () {
-
-				const v = new THREE.Vector3();
-				return function panLeft( distance, objectMatrix ) {
-
-					v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
-					v.multiplyScalar( - distance );
-					panOffset.add( v );
-
-				};
-
-			}();
-			const panUp = function () {
-
-				const v = new THREE.Vector3();
-				return function panUp( distance, objectMatrix ) {
-
-					if ( scope.screenSpacePanning === true ) {
-
-						v.setFromMatrixColumn( objectMatrix, 1 );
-
-					} else {
-
-						v.setFromMatrixColumn( objectMatrix, 0 );
-						v.crossVectors( scope.object.up, v );
-
-					}
-
-					v.multiplyScalar( distance );
-					panOffset.add( v );
-
-				};
-
-			}();
-
-			// deltaX and deltaY are in pixels; right and down are positive
-			const pan = function () {
-
-				const offset = new THREE.Vector3();
-				return function pan( deltaX, deltaY ) {
-
-					const element = scope.domElement;
-					if ( scope.object.isPerspectiveCamera ) {
-
-						// perspective
-						const position = scope.object.position;
-						offset.copy( position ).sub( scope.target );
-						let targetDistance = offset.length();
-
-						// half of the fov is center to top of screen
-						targetDistance *= Math.tan( scope.object.fov / 2 * Math.PI / 180.0 );
-
-						// we use only clientHeight here so aspect ratio does not distort speed
-						panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
-						panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
-
-					} else if ( scope.object.isOrthographicCamera ) {
-
-						// orthographic
-						panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
-						panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
-
-					} else {
-
-						// camera neither orthographic nor perspective
-						console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
-						scope.enablePan = false;
-
-					}
-
-				};
-
-			}();
-			function dollyOut( dollyScale ) {
-
-				if ( scope.object.isPerspectiveCamera ) {
-
-					scale /= dollyScale;
-
-				} else if ( scope.object.isOrthographicCamera ) {
-
-					scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
-					scope.object.updateProjectionMatrix();
-					zoomChanged = true;
-
-				} else {
-
-					console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
-					scope.enableZoom = false;
-
-				}
-
-			}
-
-			function dollyIn( dollyScale ) {
-
-				if ( scope.object.isPerspectiveCamera ) {
-
-					scale *= dollyScale;
-
-				} else if ( scope.object.isOrthographicCamera ) {
-
-					scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
-					scope.object.updateProjectionMatrix();
-					zoomChanged = true;
-
-				} else {
-
-					console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
-					scope.enableZoom = false;
-
-				}
-
-			}
-
-			//
-			// event callbacks - update the object state
-			//
-
-			function handleMouseDownRotate( event ) {
-
-				rotateStart.set( event.clientX, event.clientY );
-
-			}
-
-			function handleMouseDownDolly( event ) {
-
-				dollyStart.set( event.clientX, event.clientY );
-
-			}
-
-			function handleMouseDownPan( event ) {
-
-				panStart.set( event.clientX, event.clientY );
-
-			}
-
-			function handleMouseMoveRotate( event ) {
-
-				rotateEnd.set( event.clientX, event.clientY );
-				rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-				const element = scope.domElement;
-				rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
-				rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-				rotateStart.copy( rotateEnd );
-				scope.update();
-
-			}
-
-			function handleMouseMoveDolly( event ) {
-
-				dollyEnd.set( event.clientX, event.clientY );
-				dollyDelta.subVectors( dollyEnd, dollyStart );
-				if ( dollyDelta.y > 0 ) {
-
-					dollyOut( getZoomScale() );
-
-				} else if ( dollyDelta.y < 0 ) {
-
-					dollyIn( getZoomScale() );
-
-				}
-
-				dollyStart.copy( dollyEnd );
-				scope.update();
-
-			}
-
-			function handleMouseMovePan( event ) {
-
-				panEnd.set( event.clientX, event.clientY );
-				panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-				pan( panDelta.x, panDelta.y );
-				panStart.copy( panEnd );
-				scope.update();
-
-			}
-
-			function handleMouseWheel( event ) {
-
-				if ( event.deltaY < 0 ) {
-
-					dollyIn( getZoomScale() );
-
-				} else if ( event.deltaY > 0 ) {
-
-					dollyOut( getZoomScale() );
-
-				}
-
-				scope.update();
-
-			}
-
-			function handleKeyDown( event ) {
-
-				let needsUpdate = false;
-				switch ( event.code ) {
-
-					case scope.keys.UP:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
-
-						} else {
-
-							pan( 0, scope.keyPanSpeed );
-
-						}
-
-						needsUpdate = true;
-						break;
-					case scope.keys.BOTTOM:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
-
-						} else {
-
-							pan( 0, - scope.keyPanSpeed );
-
-						}
-
-						needsUpdate = true;
-						break;
-					case scope.keys.LEFT:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
-
-						} else {
-
-							pan( scope.keyPanSpeed, 0 );
-
-						}
-
-						needsUpdate = true;
-						break;
-					case scope.keys.RIGHT:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight );
-
-						} else {
-
-							pan( - scope.keyPanSpeed, 0 );
-
-						}
-
-						needsUpdate = true;
-						break;
-
-				}
-
-				if ( needsUpdate ) {
-
-					// prevent the browser from scrolling on cursor keys
-					event.preventDefault();
-					scope.update();
-
-				}
-
-			}
-
-			function handleTouchStartRotate() {
-
-				if ( pointers.length === 1 ) {
-
-					rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
-
-				} else {
-
-					const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
-					const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
-					rotateStart.set( x, y );
-
-				}
-
-			}
-
-			function handleTouchStartPan() {
-
-				if ( pointers.length === 1 ) {
-
-					panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY );
-
-				} else {
-
-					const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX );
-					const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY );
-					panStart.set( x, y );
-
-				}
-
-			}
-
-			function handleTouchStartDolly() {
-
-				const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX;
-				const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY;
-				const distance = Math.sqrt( dx * dx + dy * dy );
-				dollyStart.set( 0, distance );
-
-			}
-
-			function handleTouchStartDollyPan() {
-
-				if ( scope.enableZoom ) handleTouchStartDolly();
-				if ( scope.enablePan ) handleTouchStartPan();
-
-			}
-
-			function handleTouchStartDollyRotate() {
-
-				if ( scope.enableZoom ) handleTouchStartDolly();
-				if ( scope.enableRotate ) handleTouchStartRotate();
-
-			}
-
-			function handleTouchMoveRotate( event ) {
-
-				if ( pointers.length == 1 ) {
-
-					rotateEnd.set( event.pageX, event.pageY );
-
-				} else {
-
-					const position = getSecondPointerPosition( event );
-					const x = 0.5 * ( event.pageX + position.x );
-					const y = 0.5 * ( event.pageY + position.y );
-					rotateEnd.set( x, y );
-
-				}
-
-				rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
-				const element = scope.domElement;
-				rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
-
-				rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
-				rotateStart.copy( rotateEnd );
-
-			}
-
-			function handleTouchMovePan( event ) {
-
-				if ( pointers.length === 1 ) {
-
-					panEnd.set( event.pageX, event.pageY );
-
-				} else {
-
-					const position = getSecondPointerPosition( event );
-					const x = 0.5 * ( event.pageX + position.x );
-					const y = 0.5 * ( event.pageY + position.y );
-					panEnd.set( x, y );
-
-				}
-
-				panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
-				pan( panDelta.x, panDelta.y );
-				panStart.copy( panEnd );
-
-			}
-
-			function handleTouchMoveDolly( event ) {
-
-				const position = getSecondPointerPosition( event );
-				const dx = event.pageX - position.x;
-				const dy = event.pageY - position.y;
-				const distance = Math.sqrt( dx * dx + dy * dy );
-				dollyEnd.set( 0, distance );
-				dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
-				dollyOut( dollyDelta.y );
-				dollyStart.copy( dollyEnd );
-
-			}
-
-			function handleTouchMoveDollyPan( event ) {
-
-				if ( scope.enableZoom ) handleTouchMoveDolly( event );
-				if ( scope.enablePan ) handleTouchMovePan( event );
-
-			}
-
-			function handleTouchMoveDollyRotate( event ) {
-
-				if ( scope.enableZoom ) handleTouchMoveDolly( event );
-				if ( scope.enableRotate ) handleTouchMoveRotate( event );
-
-			}
-
-			//
-			// event handlers - FSM: listen for events and reset state
-			//
-
-			function onPointerDown( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( pointers.length === 0 ) {
-
-					scope.domElement.setPointerCapture( event.pointerId );
-					scope.domElement.addEventListener( 'pointermove', onPointerMove );
-					scope.domElement.addEventListener( 'pointerup', onPointerUp );
-
-				}
-
-				//
-
-				addPointer( event );
-				if ( event.pointerType === 'touch' ) {
-
-					onTouchStart( event );
-
-				} else {
-
-					onMouseDown( event );
-
-				}
-
-			}
-
-			function onPointerMove( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( event.pointerType === 'touch' ) {
-
-					onTouchMove( event );
-
-				} else {
-
-					onMouseMove( event );
-
-				}
-
-			}
-
-			function onPointerUp( event ) {
-
-				removePointer( event );
-				if ( pointers.length === 0 ) {
-
-					scope.domElement.releasePointerCapture( event.pointerId );
-					scope.domElement.removeEventListener( 'pointermove', onPointerMove );
-					scope.domElement.removeEventListener( 'pointerup', onPointerUp );
-
-				}
-
-				scope.dispatchEvent( _endEvent );
-				state = STATE.NONE;
-
-			}
-
-			function onPointerCancel( event ) {
-
-				removePointer( event );
-
-			}
-
-			function onMouseDown( event ) {
-
-				let mouseAction;
-				switch ( event.button ) {
-
-					case 0:
-						mouseAction = scope.mouseButtons.LEFT;
-						break;
-					case 1:
-						mouseAction = scope.mouseButtons.MIDDLE;
-						break;
-					case 2:
-						mouseAction = scope.mouseButtons.RIGHT;
-						break;
-					default:
-						mouseAction = - 1;
-
-				}
-
-				switch ( mouseAction ) {
-
-					case THREE.MOUSE.DOLLY:
-						if ( scope.enableZoom === false ) return;
-						handleMouseDownDolly( event );
-						state = STATE.DOLLY;
-						break;
-					case THREE.MOUSE.ROTATE:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							if ( scope.enablePan === false ) return;
-							handleMouseDownPan( event );
-							state = STATE.PAN;
-
-						} else {
-
-							if ( scope.enableRotate === false ) return;
-							handleMouseDownRotate( event );
-							state = STATE.ROTATE;
-
-						}
-
-						break;
-					case THREE.MOUSE.PAN:
-						if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
-
-							if ( scope.enableRotate === false ) return;
-							handleMouseDownRotate( event );
-							state = STATE.ROTATE;
-
-						} else {
-
-							if ( scope.enablePan === false ) return;
-							handleMouseDownPan( event );
-							state = STATE.PAN;
-
-						}
-
-						break;
-					default:
-						state = STATE.NONE;
-
-				}
-
-				if ( state !== STATE.NONE ) {
-
-					scope.dispatchEvent( _startEvent );
-
-				}
-
-			}
-
-			function onMouseMove( event ) {
-
-				switch ( state ) {
-
-					case STATE.ROTATE:
-						if ( scope.enableRotate === false ) return;
-						handleMouseMoveRotate( event );
-						break;
-					case STATE.DOLLY:
-						if ( scope.enableZoom === false ) return;
-						handleMouseMoveDolly( event );
-						break;
-					case STATE.PAN:
-						if ( scope.enablePan === false ) return;
-						handleMouseMovePan( event );
-						break;
-
-				}
-
-			}
-
-			function onMouseWheel( event ) {
-
-				if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
-				event.preventDefault();
-				scope.dispatchEvent( _startEvent );
-				handleMouseWheel( event );
-				scope.dispatchEvent( _endEvent );
-
-			}
-
-			function onKeyDown( event ) {
-
-				if ( scope.enabled === false || scope.enablePan === false ) return;
-				handleKeyDown( event );
-
-			}
-
-			function onTouchStart( event ) {
-
-				trackPointer( event );
-				switch ( pointers.length ) {
-
-					case 1:
-						switch ( scope.touches.ONE ) {
-
-							case THREE.TOUCH.ROTATE:
-								if ( scope.enableRotate === false ) return;
-								handleTouchStartRotate();
-								state = STATE.TOUCH_ROTATE;
-								break;
-							case THREE.TOUCH.PAN:
-								if ( scope.enablePan === false ) return;
-								handleTouchStartPan();
-								state = STATE.TOUCH_PAN;
-								break;
-							default:
-								state = STATE.NONE;
-
-						}
-
-						break;
-					case 2:
-						switch ( scope.touches.TWO ) {
-
-							case THREE.TOUCH.DOLLY_PAN:
-								if ( scope.enableZoom === false && scope.enablePan === false ) return;
-								handleTouchStartDollyPan();
-								state = STATE.TOUCH_DOLLY_PAN;
-								break;
-							case THREE.TOUCH.DOLLY_ROTATE:
-								if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-								handleTouchStartDollyRotate();
-								state = STATE.TOUCH_DOLLY_ROTATE;
-								break;
-							default:
-								state = STATE.NONE;
-
-						}
-
-						break;
-					default:
-						state = STATE.NONE;
-
-				}
-
-				if ( state !== STATE.NONE ) {
-
-					scope.dispatchEvent( _startEvent );
-
-				}
-
-			}
-
-			function onTouchMove( event ) {
-
-				trackPointer( event );
-				switch ( state ) {
-
-					case STATE.TOUCH_ROTATE:
-						if ( scope.enableRotate === false ) return;
-						handleTouchMoveRotate( event );
-						scope.update();
-						break;
-					case STATE.TOUCH_PAN:
-						if ( scope.enablePan === false ) return;
-						handleTouchMovePan( event );
-						scope.update();
-						break;
-					case STATE.TOUCH_DOLLY_PAN:
-						if ( scope.enableZoom === false && scope.enablePan === false ) return;
-						handleTouchMoveDollyPan( event );
-						scope.update();
-						break;
-					case STATE.TOUCH_DOLLY_ROTATE:
-						if ( scope.enableZoom === false && scope.enableRotate === false ) return;
-						handleTouchMoveDollyRotate( event );
-						scope.update();
-						break;
-					default:
-						state = STATE.NONE;
-
-				}
-
-			}
-
-			function onContextMenu( event ) {
-
-				if ( scope.enabled === false ) return;
-				event.preventDefault();
-
-			}
-
-			function addPointer( event ) {
-
-				pointers.push( event );
-
-			}
-
-			function removePointer( event ) {
-
-				delete pointerPositions[ event.pointerId ];
-				for ( let i = 0; i < pointers.length; i ++ ) {
-
-					if ( pointers[ i ].pointerId == event.pointerId ) {
-
-						pointers.splice( i, 1 );
-						return;
-
-					}
-
-				}
-
-			}
-
-			function trackPointer( event ) {
-
-				let position = pointerPositions[ event.pointerId ];
-				if ( position === undefined ) {
-
-					position = new THREE.Vector2();
-					pointerPositions[ event.pointerId ] = position;
-
-				}
-
-				position.set( event.pageX, event.pageY );
-
-			}
-
-			function getSecondPointerPosition( event ) {
-
-				const pointer = event.pointerId === pointers[ 0 ].pointerId ? pointers[ 1 ] : pointers[ 0 ];
-				return pointerPositions[ pointer.pointerId ];
-
-			}
-
-			//
-
-			scope.domElement.addEventListener( 'contextmenu', onContextMenu );
-			scope.domElement.addEventListener( 'pointerdown', onPointerDown );
-			scope.domElement.addEventListener( 'pointercancel', onPointerCancel );
-			scope.domElement.addEventListener( 'wheel', onMouseWheel, {
-				passive: false
-			} );
-
-			// force an update at start
-
-			this.update();
-
-		}
-
-	}
-
-	// This set of controls performs orbiting, dollying (zooming), and panning.
-	// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
-	// This is very similar to OrbitControls, another set of touch behavior
-	//
-	//    Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
-	//    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
-	//    Pan - left mouse, or arrow keys / touch: one-finger move
-
-	class MapControls extends OrbitControls {
-
-		constructor( object, domElement ) {
-
-			super( object, domElement );
-			this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
-
-			this.mouseButtons.LEFT = THREE.MOUSE.PAN;
-			this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
-			this.touches.ONE = THREE.TOUCH.PAN;
-			this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
-
-		}
-
-	}
-
-	THREE.MapControls = MapControls;
-	THREE.OrbitControls = OrbitControls;
-
-} )();

+ 0 - 144
examples/js/controls/PointerLockControls.js

@@ -1,144 +0,0 @@
-( function () {
-
-	const _euler = new THREE.Euler( 0, 0, 0, 'YXZ' );
-	const _vector = new THREE.Vector3();
-	const _changeEvent = {
-		type: 'change'
-	};
-	const _lockEvent = {
-		type: 'lock'
-	};
-	const _unlockEvent = {
-		type: 'unlock'
-	};
-	const _PI_2 = Math.PI / 2;
-	class PointerLockControls extends THREE.EventDispatcher {
-
-		constructor( camera, domElement ) {
-
-			super();
-			this.domElement = domElement;
-			this.isLocked = false;
-
-			// Set to constrain the pitch of the camera
-			// Range is 0 to Math.PI radians
-			this.minPolarAngle = 0; // radians
-			this.maxPolarAngle = Math.PI; // radians
-
-			this.pointerSpeed = 1.0;
-			const scope = this;
-			function onMouseMove( event ) {
-
-				if ( scope.isLocked === false ) return;
-				const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
-				const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
-				_euler.setFromQuaternion( camera.quaternion );
-				_euler.y -= movementX * 0.002 * scope.pointerSpeed;
-				_euler.x -= movementY * 0.002 * scope.pointerSpeed;
-				_euler.x = Math.max( _PI_2 - scope.maxPolarAngle, Math.min( _PI_2 - scope.minPolarAngle, _euler.x ) );
-				camera.quaternion.setFromEuler( _euler );
-				scope.dispatchEvent( _changeEvent );
-
-			}
-
-			function onPointerlockChange() {
-
-				if ( scope.domElement.ownerDocument.pointerLockElement === scope.domElement ) {
-
-					scope.dispatchEvent( _lockEvent );
-					scope.isLocked = true;
-
-				} else {
-
-					scope.dispatchEvent( _unlockEvent );
-					scope.isLocked = false;
-
-				}
-
-			}
-
-			function onPointerlockError() {
-
-				console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
-
-			}
-
-			this.connect = function () {
-
-				scope.domElement.ownerDocument.addEventListener( 'mousemove', onMouseMove );
-				scope.domElement.ownerDocument.addEventListener( 'pointerlockchange', onPointerlockChange );
-				scope.domElement.ownerDocument.addEventListener( 'pointerlockerror', onPointerlockError );
-
-			};
-
-			this.disconnect = function () {
-
-				scope.domElement.ownerDocument.removeEventListener( 'mousemove', onMouseMove );
-				scope.domElement.ownerDocument.removeEventListener( 'pointerlockchange', onPointerlockChange );
-				scope.domElement.ownerDocument.removeEventListener( 'pointerlockerror', onPointerlockError );
-
-			};
-
-			this.dispose = function () {
-
-				this.disconnect();
-
-			};
-
-			this.getObject = function () {
-
-				// retaining this method for backward compatibility
-
-				return camera;
-
-			};
-
-			this.getDirection = function () {
-
-				const direction = new THREE.Vector3( 0, 0, - 1 );
-				return function ( v ) {
-
-					return v.copy( direction ).applyQuaternion( camera.quaternion );
-
-				};
-
-			}();
-			this.moveForward = function ( distance ) {
-
-				// move forward parallel to the xz-plane
-				// assumes camera.up is y-up
-
-				_vector.setFromMatrixColumn( camera.matrix, 0 );
-				_vector.crossVectors( camera.up, _vector );
-				camera.position.addScaledVector( _vector, distance );
-
-			};
-
-			this.moveRight = function ( distance ) {
-
-				_vector.setFromMatrixColumn( camera.matrix, 0 );
-				camera.position.addScaledVector( _vector, distance );
-
-			};
-
-			this.lock = function () {
-
-				this.domElement.requestPointerLock();
-
-			};
-
-			this.unlock = function () {
-
-				scope.domElement.ownerDocument.exitPointerLock();
-
-			};
-
-			this.connect();
-
-		}
-
-	}
-
-	THREE.PointerLockControls = PointerLockControls;
-
-} )();

+ 0 - 729
examples/js/controls/TrackballControls.js

@@ -1,729 +0,0 @@
-( function () {
-
-	const _changeEvent = {
-		type: 'change'
-	};
-	const _startEvent = {
-		type: 'start'
-	};
-	const _endEvent = {
-		type: 'end'
-	};
-	class TrackballControls extends THREE.EventDispatcher {
-
-		constructor( object, domElement ) {
-
-			super();
-			const scope = this;
-			const STATE = {
-				NONE: - 1,
-				ROTATE: 0,
-				ZOOM: 1,
-				PAN: 2,
-				TOUCH_ROTATE: 3,
-				TOUCH_ZOOM_PAN: 4
-			};
-			this.object = object;
-			this.domElement = domElement;
-			this.domElement.style.touchAction = 'none'; // disable touch scroll
-
-			// API
-
-			this.enabled = true;
-			this.screen = {
-				left: 0,
-				top: 0,
-				width: 0,
-				height: 0
-			};
-			this.rotateSpeed = 1.0;
-			this.zoomSpeed = 1.2;
-			this.panSpeed = 0.3;
-			this.noRotate = false;
-			this.noZoom = false;
-			this.noPan = false;
-			this.staticMoving = false;
-			this.dynamicDampingFactor = 0.2;
-			this.minDistance = 0;
-			this.maxDistance = Infinity;
-			this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/];
-
-			this.mouseButtons = {
-				LEFT: THREE.MOUSE.ROTATE,
-				MIDDLE: THREE.MOUSE.DOLLY,
-				RIGHT: THREE.MOUSE.PAN
-			};
-
-			// internals
-
-			this.target = new THREE.Vector3();
-			const EPS = 0.000001;
-			const lastPosition = new THREE.Vector3();
-			let lastZoom = 1;
-			let _state = STATE.NONE,
-				_keyState = STATE.NONE,
-				_touchZoomDistanceStart = 0,
-				_touchZoomDistanceEnd = 0,
-				_lastAngle = 0;
-			const _eye = new THREE.Vector3(),
-				_movePrev = new THREE.Vector2(),
-				_moveCurr = new THREE.Vector2(),
-				_lastAxis = new THREE.Vector3(),
-				_zoomStart = new THREE.Vector2(),
-				_zoomEnd = new THREE.Vector2(),
-				_panStart = new THREE.Vector2(),
-				_panEnd = new THREE.Vector2(),
-				_pointers = [],
-				_pointerPositions = {};
-
-			// for reset
-
-			this.target0 = this.target.clone();
-			this.position0 = this.object.position.clone();
-			this.up0 = this.object.up.clone();
-			this.zoom0 = this.object.zoom;
-
-			// methods
-
-			this.handleResize = function () {
-
-				const box = scope.domElement.getBoundingClientRect();
-				// adjustments come from similar code in the jquery offset() function
-				const d = scope.domElement.ownerDocument.documentElement;
-				scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
-				scope.screen.top = box.top + window.pageYOffset - d.clientTop;
-				scope.screen.width = box.width;
-				scope.screen.height = box.height;
-
-			};
-
-			const getMouseOnScreen = function () {
-
-				const vector = new THREE.Vector2();
-				return function getMouseOnScreen( pageX, pageY ) {
-
-					vector.set( ( pageX - scope.screen.left ) / scope.screen.width, ( pageY - scope.screen.top ) / scope.screen.height );
-					return vector;
-
-				};
-
-			}();
-			const getMouseOnCircle = function () {
-
-				const vector = new THREE.Vector2();
-				return function getMouseOnCircle( pageX, pageY ) {
-
-					vector.set( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ), ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width // screen.width intentional
-					);
-
-					return vector;
-
-				};
-
-			}();
-			this.rotateCamera = function () {
-
-				const axis = new THREE.Vector3(),
-					quaternion = new THREE.Quaternion(),
-					eyeDirection = new THREE.Vector3(),
-					objectUpDirection = new THREE.Vector3(),
-					objectSidewaysDirection = new THREE.Vector3(),
-					moveDirection = new THREE.Vector3();
-				return function rotateCamera() {
-
-					moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
-					let angle = moveDirection.length();
-					if ( angle ) {
-
-						_eye.copy( scope.object.position ).sub( scope.target );
-						eyeDirection.copy( _eye ).normalize();
-						objectUpDirection.copy( scope.object.up ).normalize();
-						objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
-						objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
-						objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
-						moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
-						axis.crossVectors( moveDirection, _eye ).normalize();
-						angle *= scope.rotateSpeed;
-						quaternion.setFromAxisAngle( axis, angle );
-						_eye.applyQuaternion( quaternion );
-						scope.object.up.applyQuaternion( quaternion );
-						_lastAxis.copy( axis );
-						_lastAngle = angle;
-
-					} else if ( ! scope.staticMoving && _lastAngle ) {
-
-						_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
-						_eye.copy( scope.object.position ).sub( scope.target );
-						quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
-						_eye.applyQuaternion( quaternion );
-						scope.object.up.applyQuaternion( quaternion );
-
-					}
-
-					_movePrev.copy( _moveCurr );
-
-				};
-
-			}();
-			this.zoomCamera = function () {
-
-				let factor;
-				if ( _state === STATE.TOUCH_ZOOM_PAN ) {
-
-					factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
-					_touchZoomDistanceStart = _touchZoomDistanceEnd;
-					if ( scope.object.isPerspectiveCamera ) {
-
-						_eye.multiplyScalar( factor );
-
-					} else if ( scope.object.isOrthographicCamera ) {
-
-						scope.object.zoom /= factor;
-						scope.object.updateProjectionMatrix();
-
-					} else {
-
-						console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
-					}
-
-				} else {
-
-					factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
-					if ( factor !== 1.0 && factor > 0.0 ) {
-
-						if ( scope.object.isPerspectiveCamera ) {
-
-							_eye.multiplyScalar( factor );
-
-						} else if ( scope.object.isOrthographicCamera ) {
-
-							scope.object.zoom /= factor;
-							scope.object.updateProjectionMatrix();
-
-						} else {
-
-							console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
-						}
-
-					}
-
-					if ( scope.staticMoving ) {
-
-						_zoomStart.copy( _zoomEnd );
-
-					} else {
-
-						_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
-
-					}
-
-				}
-
-			};
-
-			this.panCamera = function () {
-
-				const mouseChange = new THREE.Vector2(),
-					objectUp = new THREE.Vector3(),
-					pan = new THREE.Vector3();
-				return function panCamera() {
-
-					mouseChange.copy( _panEnd ).sub( _panStart );
-					if ( mouseChange.lengthSq() ) {
-
-						if ( scope.object.isOrthographicCamera ) {
-
-							const scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
-							const scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
-							mouseChange.x *= scale_x;
-							mouseChange.y *= scale_y;
-
-						}
-
-						mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
-						pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
-						pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
-						scope.object.position.add( pan );
-						scope.target.add( pan );
-						if ( scope.staticMoving ) {
-
-							_panStart.copy( _panEnd );
-
-						} else {
-
-							_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
-
-						}
-
-					}
-
-				};
-
-			}();
-			this.checkDistances = function () {
-
-				if ( ! scope.noZoom || ! scope.noPan ) {
-
-					if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
-
-						scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
-						_zoomStart.copy( _zoomEnd );
-
-					}
-
-					if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
-
-						scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
-						_zoomStart.copy( _zoomEnd );
-
-					}
-
-				}
-
-			};
-
-			this.update = function () {
-
-				_eye.subVectors( scope.object.position, scope.target );
-				if ( ! scope.noRotate ) {
-
-					scope.rotateCamera();
-
-				}
-
-				if ( ! scope.noZoom ) {
-
-					scope.zoomCamera();
-
-				}
-
-				if ( ! scope.noPan ) {
-
-					scope.panCamera();
-
-				}
-
-				scope.object.position.addVectors( scope.target, _eye );
-				if ( scope.object.isPerspectiveCamera ) {
-
-					scope.checkDistances();
-					scope.object.lookAt( scope.target );
-					if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
-
-						scope.dispatchEvent( _changeEvent );
-						lastPosition.copy( scope.object.position );
-
-					}
-
-				} else if ( scope.object.isOrthographicCamera ) {
-
-					scope.object.lookAt( scope.target );
-					if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
-
-						scope.dispatchEvent( _changeEvent );
-						lastPosition.copy( scope.object.position );
-						lastZoom = scope.object.zoom;
-
-					}
-
-				} else {
-
-					console.warn( 'THREE.TrackballControls: Unsupported camera type' );
-
-				}
-
-			};
-
-			this.reset = function () {
-
-				_state = STATE.NONE;
-				_keyState = STATE.NONE;
-				scope.target.copy( scope.target0 );
-				scope.object.position.copy( scope.position0 );
-				scope.object.up.copy( scope.up0 );
-				scope.object.zoom = scope.zoom0;
-				scope.object.updateProjectionMatrix();
-				_eye.subVectors( scope.object.position, scope.target );
-				scope.object.lookAt( scope.target );
-				scope.dispatchEvent( _changeEvent );
-				lastPosition.copy( scope.object.position );
-				lastZoom = scope.object.zoom;
-
-			};
-
-			// listeners
-
-			function onPointerDown( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( _pointers.length === 0 ) {
-
-					scope.domElement.setPointerCapture( event.pointerId );
-					scope.domElement.addEventListener( 'pointermove', onPointerMove );
-					scope.domElement.addEventListener( 'pointerup', onPointerUp );
-
-				}
-
-				//
-
-				addPointer( event );
-				if ( event.pointerType === 'touch' ) {
-
-					onTouchStart( event );
-
-				} else {
-
-					onMouseDown( event );
-
-				}
-
-			}
-
-			function onPointerMove( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( event.pointerType === 'touch' ) {
-
-					onTouchMove( event );
-
-				} else {
-
-					onMouseMove( event );
-
-				}
-
-			}
-
-			function onPointerUp( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( event.pointerType === 'touch' ) {
-
-					onTouchEnd( event );
-
-				} else {
-
-					onMouseUp();
-
-				}
-
-				//
-
-				removePointer( event );
-				if ( _pointers.length === 0 ) {
-
-					scope.domElement.releasePointerCapture( event.pointerId );
-					scope.domElement.removeEventListener( 'pointermove', onPointerMove );
-					scope.domElement.removeEventListener( 'pointerup', onPointerUp );
-
-				}
-
-			}
-
-			function onPointerCancel( event ) {
-
-				removePointer( event );
-
-			}
-
-			function keydown( event ) {
-
-				if ( scope.enabled === false ) return;
-				window.removeEventListener( 'keydown', keydown );
-				if ( _keyState !== STATE.NONE ) {
-
-					return;
-
-				} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
-
-					_keyState = STATE.ROTATE;
-
-				} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
-
-					_keyState = STATE.ZOOM;
-
-				} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
-
-					_keyState = STATE.PAN;
-
-				}
-
-			}
-
-			function keyup() {
-
-				if ( scope.enabled === false ) return;
-				_keyState = STATE.NONE;
-				window.addEventListener( 'keydown', keydown );
-
-			}
-
-			function onMouseDown( event ) {
-
-				if ( _state === STATE.NONE ) {
-
-					switch ( event.button ) {
-
-						case scope.mouseButtons.LEFT:
-							_state = STATE.ROTATE;
-							break;
-						case scope.mouseButtons.MIDDLE:
-							_state = STATE.ZOOM;
-							break;
-						case scope.mouseButtons.RIGHT:
-							_state = STATE.PAN;
-							break;
-
-					}
-
-				}
-
-				const state = _keyState !== STATE.NONE ? _keyState : _state;
-				if ( state === STATE.ROTATE && ! scope.noRotate ) {
-
-					_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-					_movePrev.copy( _moveCurr );
-
-				} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
-
-					_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-					_zoomEnd.copy( _zoomStart );
-
-				} else if ( state === STATE.PAN && ! scope.noPan ) {
-
-					_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-					_panEnd.copy( _panStart );
-
-				}
-
-				scope.dispatchEvent( _startEvent );
-
-			}
-
-			function onMouseMove( event ) {
-
-				const state = _keyState !== STATE.NONE ? _keyState : _state;
-				if ( state === STATE.ROTATE && ! scope.noRotate ) {
-
-					_movePrev.copy( _moveCurr );
-					_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-
-				} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
-
-					_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-
-				} else if ( state === STATE.PAN && ! scope.noPan ) {
-
-					_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
-
-				}
-
-			}
-
-			function onMouseUp() {
-
-				_state = STATE.NONE;
-				scope.dispatchEvent( _endEvent );
-
-			}
-
-			function onMouseWheel( event ) {
-
-				if ( scope.enabled === false ) return;
-				if ( scope.noZoom === true ) return;
-				event.preventDefault();
-				switch ( event.deltaMode ) {
-
-					case 2:
-						// Zoom in pages
-						_zoomStart.y -= event.deltaY * 0.025;
-						break;
-					case 1:
-						// Zoom in lines
-						_zoomStart.y -= event.deltaY * 0.01;
-						break;
-					default:
-						// undefined, 0, assume pixels
-						_zoomStart.y -= event.deltaY * 0.00025;
-						break;
-
-				}
-
-				scope.dispatchEvent( _startEvent );
-				scope.dispatchEvent( _endEvent );
-
-			}
-
-			function onTouchStart( event ) {
-
-				trackPointer( event );
-				switch ( _pointers.length ) {
-
-					case 1:
-						_state = STATE.TOUCH_ROTATE;
-						_moveCurr.copy( getMouseOnCircle( _pointers[ 0 ].pageX, _pointers[ 0 ].pageY ) );
-						_movePrev.copy( _moveCurr );
-						break;
-					default:
-						// 2 or more
-						_state = STATE.TOUCH_ZOOM_PAN;
-						const dx = _pointers[ 0 ].pageX - _pointers[ 1 ].pageX;
-						const dy = _pointers[ 0 ].pageY - _pointers[ 1 ].pageY;
-						_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
-						const x = ( _pointers[ 0 ].pageX + _pointers[ 1 ].pageX ) / 2;
-						const y = ( _pointers[ 0 ].pageY + _pointers[ 1 ].pageY ) / 2;
-						_panStart.copy( getMouseOnScreen( x, y ) );
-						_panEnd.copy( _panStart );
-						break;
-
-				}
-
-				scope.dispatchEvent( _startEvent );
-
-			}
-
-			function onTouchMove( event ) {
-
-				trackPointer( event );
-				switch ( _pointers.length ) {
-
-					case 1:
-						_movePrev.copy( _moveCurr );
-						_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-						break;
-					default:
-						// 2 or more
-
-						const position = getSecondPointerPosition( event );
-						const dx = event.pageX - position.x;
-						const dy = event.pageY - position.y;
-						_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
-						const x = ( event.pageX + position.x ) / 2;
-						const y = ( event.pageY + position.y ) / 2;
-						_panEnd.copy( getMouseOnScreen( x, y ) );
-						break;
-
-				}
-
-			}
-
-			function onTouchEnd( event ) {
-
-				switch ( _pointers.length ) {
-
-					case 0:
-						_state = STATE.NONE;
-						break;
-					case 1:
-						_state = STATE.TOUCH_ROTATE;
-						_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
-						_movePrev.copy( _moveCurr );
-						break;
-					case 2:
-						_state = STATE.TOUCH_ZOOM_PAN;
-						for ( let i = 0; i < _pointers.length; i ++ ) {
-
-							if ( _pointers[ i ].pointerId !== event.pointerId ) {
-
-								const position = _pointerPositions[ _pointers[ i ].pointerId ];
-								_moveCurr.copy( getMouseOnCircle( position.x, position.y ) );
-								_movePrev.copy( _moveCurr );
-								break;
-
-							}
-
-						}
-
-						break;
-
-				}
-
-				scope.dispatchEvent( _endEvent );
-
-			}
-
-			function contextmenu( event ) {
-
-				if ( scope.enabled === false ) return;
-				event.preventDefault();
-
-			}
-
-			function addPointer( event ) {
-
-				_pointers.push( event );
-
-			}
-
-			function removePointer( event ) {
-
-				delete _pointerPositions[ event.pointerId ];
-				for ( let i = 0; i < _pointers.length; i ++ ) {
-
-					if ( _pointers[ i ].pointerId == event.pointerId ) {
-
-						_pointers.splice( i, 1 );
-						return;
-
-					}
-
-				}
-
-			}
-
-			function trackPointer( event ) {
-
-				let position = _pointerPositions[ event.pointerId ];
-				if ( position === undefined ) {
-
-					position = new THREE.Vector2();
-					_pointerPositions[ event.pointerId ] = position;
-
-				}
-
-				position.set( event.pageX, event.pageY );
-
-			}
-
-			function getSecondPointerPosition( event ) {
-
-				const pointer = event.pointerId === _pointers[ 0 ].pointerId ? _pointers[ 1 ] : _pointers[ 0 ];
-				return _pointerPositions[ pointer.pointerId ];
-
-			}
-
-			this.dispose = function () {
-
-				scope.domElement.removeEventListener( 'contextmenu', contextmenu );
-				scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
-				scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
-				scope.domElement.removeEventListener( 'wheel', onMouseWheel );
-				scope.domElement.removeEventListener( 'pointermove', onPointerMove );
-				scope.domElement.removeEventListener( 'pointerup', onPointerUp );
-				window.removeEventListener( 'keydown', keydown );
-				window.removeEventListener( 'keyup', keyup );
-
-			};
-
-			this.domElement.addEventListener( 'contextmenu', contextmenu );
-			this.domElement.addEventListener( 'pointerdown', onPointerDown );
-			this.domElement.addEventListener( 'pointercancel', onPointerCancel );
-			this.domElement.addEventListener( 'wheel', onMouseWheel, {
-				passive: false
-			} );
-			window.addEventListener( 'keydown', keydown );
-			window.addEventListener( 'keyup', keyup );
-			this.handleResize();
-
-			// force an update at start
-			this.update();
-
-		}
-
-	}
-
-	THREE.TrackballControls = TrackballControls;
-
-} )();

+ 0 - 1301
examples/js/controls/TransformControls.js

@@ -1,1301 +0,0 @@
-( function () {
-
-	const _raycaster = new THREE.Raycaster();
-	const _tempVector = new THREE.Vector3();
-	const _tempVector2 = new THREE.Vector3();
-	const _tempQuaternion = new THREE.Quaternion();
-	const _unit = {
-		X: new THREE.Vector3( 1, 0, 0 ),
-		Y: new THREE.Vector3( 0, 1, 0 ),
-		Z: new THREE.Vector3( 0, 0, 1 )
-	};
-	const _changeEvent = {
-		type: 'change'
-	};
-	const _mouseDownEvent = {
-		type: 'mouseDown'
-	};
-	const _mouseUpEvent = {
-		type: 'mouseUp',
-		mode: null
-	};
-	const _objectChangeEvent = {
-		type: 'objectChange'
-	};
-	class TransformControls extends THREE.Object3D {
-
-		constructor( camera, domElement ) {
-
-			super();
-			if ( domElement === undefined ) {
-
-				console.warn( 'THREE.TransformControls: The second parameter "domElement" is now mandatory.' );
-				domElement = document;
-
-			}
-
-			this.isTransformControls = true;
-			this.visible = false;
-			this.domElement = domElement;
-			this.domElement.style.touchAction = 'none'; // disable touch scroll
-
-			const _gizmo = new TransformControlsGizmo();
-			this._gizmo = _gizmo;
-			this.add( _gizmo );
-			const _plane = new TransformControlsPlane();
-			this._plane = _plane;
-			this.add( _plane );
-			const scope = this;
-
-			// Defined getter, setter and store for a property
-			function defineProperty( propName, defaultValue ) {
-
-				let propValue = defaultValue;
-				Object.defineProperty( scope, propName, {
-					get: function () {
-
-						return propValue !== undefined ? propValue : defaultValue;
-
-					},
-					set: function ( value ) {
-
-						if ( propValue !== value ) {
-
-							propValue = value;
-							_plane[ propName ] = value;
-							_gizmo[ propName ] = value;
-							scope.dispatchEvent( {
-								type: propName + '-changed',
-								value: value
-							} );
-							scope.dispatchEvent( _changeEvent );
-
-						}
-
-					}
-				} );
-				scope[ propName ] = defaultValue;
-				_plane[ propName ] = defaultValue;
-				_gizmo[ propName ] = defaultValue;
-
-			}
-
-			// Define properties with getters/setter
-			// Setting the defined property will automatically trigger change event
-			// Defined properties are passed down to gizmo and plane
-
-			defineProperty( 'camera', camera );
-			defineProperty( 'object', undefined );
-			defineProperty( 'enabled', true );
-			defineProperty( 'axis', null );
-			defineProperty( 'mode', 'translate' );
-			defineProperty( 'translationSnap', null );
-			defineProperty( 'rotationSnap', null );
-			defineProperty( 'scaleSnap', null );
-			defineProperty( 'space', 'world' );
-			defineProperty( 'size', 1 );
-			defineProperty( 'dragging', false );
-			defineProperty( 'showX', true );
-			defineProperty( 'showY', true );
-			defineProperty( 'showZ', true );
-
-			// Reusable utility variables
-
-			const worldPosition = new THREE.Vector3();
-			const worldPositionStart = new THREE.Vector3();
-			const worldQuaternion = new THREE.Quaternion();
-			const worldQuaternionStart = new THREE.Quaternion();
-			const cameraPosition = new THREE.Vector3();
-			const cameraQuaternion = new THREE.Quaternion();
-			const pointStart = new THREE.Vector3();
-			const pointEnd = new THREE.Vector3();
-			const rotationAxis = new THREE.Vector3();
-			const rotationAngle = 0;
-			const eye = new THREE.Vector3();
-
-			// TODO: remove properties unused in plane and gizmo
-
-			defineProperty( 'worldPosition', worldPosition );
-			defineProperty( 'worldPositionStart', worldPositionStart );
-			defineProperty( 'worldQuaternion', worldQuaternion );
-			defineProperty( 'worldQuaternionStart', worldQuaternionStart );
-			defineProperty( 'cameraPosition', cameraPosition );
-			defineProperty( 'cameraQuaternion', cameraQuaternion );
-			defineProperty( 'pointStart', pointStart );
-			defineProperty( 'pointEnd', pointEnd );
-			defineProperty( 'rotationAxis', rotationAxis );
-			defineProperty( 'rotationAngle', rotationAngle );
-			defineProperty( 'eye', eye );
-			this._offset = new THREE.Vector3();
-			this._startNorm = new THREE.Vector3();
-			this._endNorm = new THREE.Vector3();
-			this._cameraScale = new THREE.Vector3();
-			this._parentPosition = new THREE.Vector3();
-			this._parentQuaternion = new THREE.Quaternion();
-			this._parentQuaternionInv = new THREE.Quaternion();
-			this._parentScale = new THREE.Vector3();
-			this._worldScaleStart = new THREE.Vector3();
-			this._worldQuaternionInv = new THREE.Quaternion();
-			this._worldScale = new THREE.Vector3();
-			this._positionStart = new THREE.Vector3();
-			this._quaternionStart = new THREE.Quaternion();
-			this._scaleStart = new THREE.Vector3();
-			this._getPointer = getPointer.bind( this );
-			this._onPointerDown = onPointerDown.bind( this );
-			this._onPointerHover = onPointerHover.bind( this );
-			this._onPointerMove = onPointerMove.bind( this );
-			this._onPointerUp = onPointerUp.bind( this );
-			this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
-			this.domElement.addEventListener( 'pointermove', this._onPointerHover );
-			this.domElement.addEventListener( 'pointerup', this._onPointerUp );
-
-		}
-
-		// updateMatrixWorld  updates key transformation variables
-		updateMatrixWorld() {
-
-			if ( this.object !== undefined ) {
-
-				this.object.updateMatrixWorld();
-				if ( this.object.parent === null ) {
-
-					console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' );
-
-				} else {
-
-					this.object.parent.matrixWorld.decompose( this._parentPosition, this._parentQuaternion, this._parentScale );
-
-				}
-
-				this.object.matrixWorld.decompose( this.worldPosition, this.worldQuaternion, this._worldScale );
-				this._parentQuaternionInv.copy( this._parentQuaternion ).invert();
-				this._worldQuaternionInv.copy( this.worldQuaternion ).invert();
-
-			}
-
-			this.camera.updateMatrixWorld();
-			this.camera.matrixWorld.decompose( this.cameraPosition, this.cameraQuaternion, this._cameraScale );
-			if ( this.camera.isOrthographicCamera ) {
-
-				this.camera.getWorldDirection( this.eye ).negate();
-
-			} else {
-
-				this.eye.copy( this.cameraPosition ).sub( this.worldPosition ).normalize();
-
-			}
-
-			super.updateMatrixWorld( this );
-
-		}
-		pointerHover( pointer ) {
-
-			if ( this.object === undefined || this.dragging === true ) return;
-			_raycaster.setFromCamera( pointer, this.camera );
-			const intersect = intersectObjectWithRay( this._gizmo.picker[ this.mode ], _raycaster );
-			if ( intersect ) {
-
-				this.axis = intersect.object.name;
-
-			} else {
-
-				this.axis = null;
-
-			}
-
-		}
-		pointerDown( pointer ) {
-
-			if ( this.object === undefined || this.dragging === true || pointer.button !== 0 ) return;
-			if ( this.axis !== null ) {
-
-				_raycaster.setFromCamera( pointer, this.camera );
-				const planeIntersect = intersectObjectWithRay( this._plane, _raycaster, true );
-				if ( planeIntersect ) {
-
-					this.object.updateMatrixWorld();
-					this.object.parent.updateMatrixWorld();
-					this._positionStart.copy( this.object.position );
-					this._quaternionStart.copy( this.object.quaternion );
-					this._scaleStart.copy( this.object.scale );
-					this.object.matrixWorld.decompose( this.worldPositionStart, this.worldQuaternionStart, this._worldScaleStart );
-					this.pointStart.copy( planeIntersect.point ).sub( this.worldPositionStart );
-
-				}
-
-				this.dragging = true;
-				_mouseDownEvent.mode = this.mode;
-				this.dispatchEvent( _mouseDownEvent );
-
-			}
-
-		}
-		pointerMove( pointer ) {
-
-			const axis = this.axis;
-			const mode = this.mode;
-			const object = this.object;
-			let space = this.space;
-			if ( mode === 'scale' ) {
-
-				space = 'local';
-
-			} else if ( axis === 'E' || axis === 'XYZE' || axis === 'XYZ' ) {
-
-				space = 'world';
-
-			}
-
-			if ( object === undefined || axis === null || this.dragging === false || pointer.button !== - 1 ) return;
-			_raycaster.setFromCamera( pointer, this.camera );
-			const planeIntersect = intersectObjectWithRay( this._plane, _raycaster, true );
-			if ( ! planeIntersect ) return;
-			this.pointEnd.copy( planeIntersect.point ).sub( this.worldPositionStart );
-			if ( mode === 'translate' ) {
-
-				// Apply translate
-
-				this._offset.copy( this.pointEnd ).sub( this.pointStart );
-				if ( space === 'local' && axis !== 'XYZ' ) {
-
-					this._offset.applyQuaternion( this._worldQuaternionInv );
-
-				}
-
-				if ( axis.indexOf( 'X' ) === - 1 ) this._offset.x = 0;
-				if ( axis.indexOf( 'Y' ) === - 1 ) this._offset.y = 0;
-				if ( axis.indexOf( 'Z' ) === - 1 ) this._offset.z = 0;
-				if ( space === 'local' && axis !== 'XYZ' ) {
-
-					this._offset.applyQuaternion( this._quaternionStart ).divide( this._parentScale );
-
-				} else {
-
-					this._offset.applyQuaternion( this._parentQuaternionInv ).divide( this._parentScale );
-
-				}
-
-				object.position.copy( this._offset ).add( this._positionStart );
-
-				// Apply translation snap
-
-				if ( this.translationSnap ) {
-
-					if ( space === 'local' ) {
-
-						object.position.applyQuaternion( _tempQuaternion.copy( this._quaternionStart ).invert() );
-						if ( axis.search( 'X' ) !== - 1 ) {
-
-							object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						if ( axis.search( 'Y' ) !== - 1 ) {
-
-							object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						if ( axis.search( 'Z' ) !== - 1 ) {
-
-							object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						object.position.applyQuaternion( this._quaternionStart );
-
-					}
-
-					if ( space === 'world' ) {
-
-						if ( object.parent ) {
-
-							object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
-
-						}
-
-						if ( axis.search( 'X' ) !== - 1 ) {
-
-							object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						if ( axis.search( 'Y' ) !== - 1 ) {
-
-							object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						if ( axis.search( 'Z' ) !== - 1 ) {
-
-							object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
-
-						}
-
-						if ( object.parent ) {
-
-							object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
-
-						}
-
-					}
-
-				}
-
-			} else if ( mode === 'scale' ) {
-
-				if ( axis.search( 'XYZ' ) !== - 1 ) {
-
-					let d = this.pointEnd.length() / this.pointStart.length();
-					if ( this.pointEnd.dot( this.pointStart ) < 0 ) d *= - 1;
-					_tempVector2.set( d, d, d );
-
-				} else {
-
-					_tempVector.copy( this.pointStart );
-					_tempVector2.copy( this.pointEnd );
-					_tempVector.applyQuaternion( this._worldQuaternionInv );
-					_tempVector2.applyQuaternion( this._worldQuaternionInv );
-					_tempVector2.divide( _tempVector );
-					if ( axis.search( 'X' ) === - 1 ) {
-
-						_tempVector2.x = 1;
-
-					}
-
-					if ( axis.search( 'Y' ) === - 1 ) {
-
-						_tempVector2.y = 1;
-
-					}
-
-					if ( axis.search( 'Z' ) === - 1 ) {
-
-						_tempVector2.z = 1;
-
-					}
-
-				}
-
-				// Apply scale
-
-				object.scale.copy( this._scaleStart ).multiply( _tempVector2 );
-				if ( this.scaleSnap ) {
-
-					if ( axis.search( 'X' ) !== - 1 ) {
-
-						object.scale.x = Math.round( object.scale.x / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
-
-					}
-
-					if ( axis.search( 'Y' ) !== - 1 ) {
-
-						object.scale.y = Math.round( object.scale.y / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
-
-					}
-
-					if ( axis.search( 'Z' ) !== - 1 ) {
-
-						object.scale.z = Math.round( object.scale.z / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
-
-					}
-
-				}
-
-			} else if ( mode === 'rotate' ) {
-
-				this._offset.copy( this.pointEnd ).sub( this.pointStart );
-				const ROTATION_SPEED = 20 / this.worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) );
-				if ( axis === 'E' ) {
-
-					this.rotationAxis.copy( this.eye );
-					this.rotationAngle = this.pointEnd.angleTo( this.pointStart );
-					this._startNorm.copy( this.pointStart ).normalize();
-					this._endNorm.copy( this.pointEnd ).normalize();
-					this.rotationAngle *= this._endNorm.cross( this._startNorm ).dot( this.eye ) < 0 ? 1 : - 1;
-
-				} else if ( axis === 'XYZE' ) {
-
-					this.rotationAxis.copy( this._offset ).cross( this.eye ).normalize();
-					this.rotationAngle = this._offset.dot( _tempVector.copy( this.rotationAxis ).cross( this.eye ) ) * ROTATION_SPEED;
-
-				} else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) {
-
-					this.rotationAxis.copy( _unit[ axis ] );
-					_tempVector.copy( _unit[ axis ] );
-					if ( space === 'local' ) {
-
-						_tempVector.applyQuaternion( this.worldQuaternion );
-
-					}
-
-					this.rotationAngle = this._offset.dot( _tempVector.cross( this.eye ).normalize() ) * ROTATION_SPEED;
-
-				}
-
-				// Apply rotation snap
-
-				if ( this.rotationSnap ) this.rotationAngle = Math.round( this.rotationAngle / this.rotationSnap ) * this.rotationSnap;
-
-				// Apply rotate
-				if ( space === 'local' && axis !== 'E' && axis !== 'XYZE' ) {
-
-					object.quaternion.copy( this._quaternionStart );
-					object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( this.rotationAxis, this.rotationAngle ) ).normalize();
-
-				} else {
-
-					this.rotationAxis.applyQuaternion( this._parentQuaternionInv );
-					object.quaternion.copy( _tempQuaternion.setFromAxisAngle( this.rotationAxis, this.rotationAngle ) );
-					object.quaternion.multiply( this._quaternionStart ).normalize();
-
-				}
-
-			}
-
-			this.dispatchEvent( _changeEvent );
-			this.dispatchEvent( _objectChangeEvent );
-
-		}
-		pointerUp( pointer ) {
-
-			if ( pointer.button !== 0 ) return;
-			if ( this.dragging && this.axis !== null ) {
-
-				_mouseUpEvent.mode = this.mode;
-				this.dispatchEvent( _mouseUpEvent );
-
-			}
-
-			this.dragging = false;
-			this.axis = null;
-
-		}
-		dispose() {
-
-			this.domElement.removeEventListener( 'pointerdown', this._onPointerDown );
-			this.domElement.removeEventListener( 'pointermove', this._onPointerHover );
-			this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
-			this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
-			this.traverse( function ( child ) {
-
-				if ( child.geometry ) child.geometry.dispose();
-				if ( child.material ) child.material.dispose();
-
-			} );
-
-		}
-
-		// Set current object
-		attach( object ) {
-
-			this.object = object;
-			this.visible = true;
-			return this;
-
-		}
-
-		// Detach from object
-		detach() {
-
-			this.object = undefined;
-			this.visible = false;
-			this.axis = null;
-			return this;
-
-		}
-		reset() {
-
-			if ( ! this.enabled ) return;
-			if ( this.dragging ) {
-
-				this.object.position.copy( this._positionStart );
-				this.object.quaternion.copy( this._quaternionStart );
-				this.object.scale.copy( this._scaleStart );
-				this.dispatchEvent( _changeEvent );
-				this.dispatchEvent( _objectChangeEvent );
-				this.pointStart.copy( this.pointEnd );
-
-			}
-
-		}
-		getRaycaster() {
-
-			return _raycaster;
-
-		}
-
-		// TODO: deprecate
-
-		getMode() {
-
-			return this.mode;
-
-		}
-		setMode( mode ) {
-
-			this.mode = mode;
-
-		}
-		setTranslationSnap( translationSnap ) {
-
-			this.translationSnap = translationSnap;
-
-		}
-		setRotationSnap( rotationSnap ) {
-
-			this.rotationSnap = rotationSnap;
-
-		}
-		setScaleSnap( scaleSnap ) {
-
-			this.scaleSnap = scaleSnap;
-
-		}
-		setSize( size ) {
-
-			this.size = size;
-
-		}
-		setSpace( space ) {
-
-			this.space = space;
-
-		}
-
-	}
-
-	// mouse / touch event handlers
-
-	function getPointer( event ) {
-
-		if ( this.domElement.ownerDocument.pointerLockElement ) {
-
-			return {
-				x: 0,
-				y: 0,
-				button: event.button
-			};
-
-		} else {
-
-			const rect = this.domElement.getBoundingClientRect();
-			return {
-				x: ( event.clientX - rect.left ) / rect.width * 2 - 1,
-				y: - ( event.clientY - rect.top ) / rect.height * 2 + 1,
-				button: event.button
-			};
-
-		}
-
-	}
-
-	function onPointerHover( event ) {
-
-		if ( ! this.enabled ) return;
-		switch ( event.pointerType ) {
-
-			case 'mouse':
-			case 'pen':
-				this.pointerHover( this._getPointer( event ) );
-				break;
-
-		}
-
-	}
-
-	function onPointerDown( event ) {
-
-		if ( ! this.enabled ) return;
-		if ( ! document.pointerLockElement ) {
-
-			this.domElement.setPointerCapture( event.pointerId );
-
-		}
-
-		this.domElement.addEventListener( 'pointermove', this._onPointerMove );
-		this.pointerHover( this._getPointer( event ) );
-		this.pointerDown( this._getPointer( event ) );
-
-	}
-
-	function onPointerMove( event ) {
-
-		if ( ! this.enabled ) return;
-		this.pointerMove( this._getPointer( event ) );
-
-	}
-
-	function onPointerUp( event ) {
-
-		if ( ! this.enabled ) return;
-		this.domElement.releasePointerCapture( event.pointerId );
-		this.domElement.removeEventListener( 'pointermove', this._onPointerMove );
-		this.pointerUp( this._getPointer( event ) );
-
-	}
-
-	function intersectObjectWithRay( object, raycaster, includeInvisible ) {
-
-		const allIntersections = raycaster.intersectObject( object, true );
-		for ( let i = 0; i < allIntersections.length; i ++ ) {
-
-			if ( allIntersections[ i ].object.visible || includeInvisible ) {
-
-				return allIntersections[ i ];
-
-			}
-
-		}
-
-		return false;
-
-	}
-
-	//
-
-	// Reusable utility variables
-
-	const _tempEuler = new THREE.Euler();
-	const _alignVector = new THREE.Vector3( 0, 1, 0 );
-	const _zeroVector = new THREE.Vector3( 0, 0, 0 );
-	const _lookAtMatrix = new THREE.Matrix4();
-	const _tempQuaternion2 = new THREE.Quaternion();
-	const _identityQuaternion = new THREE.Quaternion();
-	const _dirVector = new THREE.Vector3();
-	const _tempMatrix = new THREE.Matrix4();
-	const _unitX = new THREE.Vector3( 1, 0, 0 );
-	const _unitY = new THREE.Vector3( 0, 1, 0 );
-	const _unitZ = new THREE.Vector3( 0, 0, 1 );
-	const _v1 = new THREE.Vector3();
-	const _v2 = new THREE.Vector3();
-	const _v3 = new THREE.Vector3();
-	class TransformControlsGizmo extends THREE.Object3D {
-
-		constructor() {
-
-			super();
-			this.isTransformControlsGizmo = true;
-			this.type = 'TransformControlsGizmo';
-
-			// shared materials
-
-			const gizmoMaterial = new THREE.MeshBasicMaterial( {
-				depthTest: false,
-				depthWrite: false,
-				fog: false,
-				toneMapped: false,
-				transparent: true
-			} );
-			const gizmoLineMaterial = new THREE.LineBasicMaterial( {
-				depthTest: false,
-				depthWrite: false,
-				fog: false,
-				toneMapped: false,
-				transparent: true
-			} );
-
-			// Make unique material for each axis/color
-
-			const matInvisible = gizmoMaterial.clone();
-			matInvisible.opacity = 0.15;
-			const matHelper = gizmoLineMaterial.clone();
-			matHelper.opacity = 0.5;
-			const matRed = gizmoMaterial.clone();
-			matRed.color.setHex( 0xff0000 );
-			const matGreen = gizmoMaterial.clone();
-			matGreen.color.setHex( 0x00ff00 );
-			const matBlue = gizmoMaterial.clone();
-			matBlue.color.setHex( 0x0000ff );
-			const matRedTransparent = gizmoMaterial.clone();
-			matRedTransparent.color.setHex( 0xff0000 );
-			matRedTransparent.opacity = 0.5;
-			const matGreenTransparent = gizmoMaterial.clone();
-			matGreenTransparent.color.setHex( 0x00ff00 );
-			matGreenTransparent.opacity = 0.5;
-			const matBlueTransparent = gizmoMaterial.clone();
-			matBlueTransparent.color.setHex( 0x0000ff );
-			matBlueTransparent.opacity = 0.5;
-			const matWhiteTransparent = gizmoMaterial.clone();
-			matWhiteTransparent.opacity = 0.25;
-			const matYellowTransparent = gizmoMaterial.clone();
-			matYellowTransparent.color.setHex( 0xffff00 );
-			matYellowTransparent.opacity = 0.25;
-			const matYellow = gizmoMaterial.clone();
-			matYellow.color.setHex( 0xffff00 );
-			const matGray = gizmoMaterial.clone();
-			matGray.color.setHex( 0x787878 );
-
-			// reusable geometry
-
-			const arrowGeometry = new THREE.CylinderGeometry( 0, 0.04, 0.1, 12 );
-			arrowGeometry.translate( 0, 0.05, 0 );
-			const scaleHandleGeometry = new THREE.BoxGeometry( 0.08, 0.08, 0.08 );
-			scaleHandleGeometry.translate( 0, 0.04, 0 );
-			const lineGeometry = new THREE.BufferGeometry();
-			lineGeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
-			const lineGeometry2 = new THREE.CylinderGeometry( 0.0075, 0.0075, 0.5, 3 );
-			lineGeometry2.translate( 0, 0.25, 0 );
-			function CircleGeometry( radius, arc ) {
-
-				const geometry = new THREE.TorusGeometry( radius, 0.0075, 3, 64, arc * Math.PI * 2 );
-				geometry.rotateY( Math.PI / 2 );
-				geometry.rotateX( Math.PI / 2 );
-				return geometry;
-
-			}
-
-			// Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
-
-			function TranslateHelperGeometry() {
-
-				const geometry = new THREE.BufferGeometry();
-				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
-				return geometry;
-
-			}
-
-			// Gizmo definitions - custom hierarchy definitions for setupGizmo() function
-
-			const gizmoTranslate = {
-				X: [[ new THREE.Mesh( arrowGeometry, matRed ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( arrowGeometry, matRed ), [ - 0.5, 0, 0 ], [ 0, 0, Math.PI / 2 ]], [ new THREE.Mesh( lineGeometry2, matRed ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
-				Y: [[ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, 0.5, 0 ]], [ new THREE.Mesh( arrowGeometry, matGreen ), [ 0, - 0.5, 0 ], [ Math.PI, 0, 0 ]], [ new THREE.Mesh( lineGeometry2, matGreen ) ]],
-				Z: [[ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( arrowGeometry, matBlue ), [ 0, 0, - 0.5 ], [ - Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( lineGeometry2, matBlue ), null, [ Math.PI / 2, 0, 0 ]]],
-				XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ]]],
-				XY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matBlueTransparent.clone() ), [ 0.15, 0.15, 0 ]]],
-				YZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matRedTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]],
-				XZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matGreenTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]]
-			};
-			const pickerTranslate = {
-				X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0.3, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ - 0.3, 0, 0 ], [ 0, 0, Math.PI / 2 ]]],
-				Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0.3, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, - 0.3, 0 ], [ 0, 0, Math.PI ]]],
-				Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, 0.3 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, - 0.3 ], [ - Math.PI / 2, 0, 0 ]]],
-				XYZ: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.2, 0 ), matInvisible ) ]],
-				XY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0.15, 0 ]]],
-				YZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]],
-				XZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]]
-			};
-			const helperTranslate = {
-				START: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
-				END: [[ new THREE.Mesh( new THREE.OctahedronGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]],
-				DELTA: [[ new THREE.Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]],
-				X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
-				Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
-				Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
-			};
-			const gizmoRotate = {
-				XYZE: [[ new THREE.Mesh( CircleGeometry( 0.5, 1 ), matGray ), null, [ 0, Math.PI / 2, 0 ]]],
-				X: [[ new THREE.Mesh( CircleGeometry( 0.5, 0.5 ), matRed ) ]],
-				Y: [[ new THREE.Mesh( CircleGeometry( 0.5, 0.5 ), matGreen ), null, [ 0, 0, - Math.PI / 2 ]]],
-				Z: [[ new THREE.Mesh( CircleGeometry( 0.5, 0.5 ), matBlue ), null, [ 0, Math.PI / 2, 0 ]]],
-				E: [[ new THREE.Mesh( CircleGeometry( 0.75, 1 ), matYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]]]
-			};
-			const helperRotate = {
-				AXIS: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]]
-			};
-			const pickerRotate = {
-				XYZE: [[ new THREE.Mesh( new THREE.SphereGeometry( 0.25, 10, 8 ), matInvisible ) ]],
-				X: [[ new THREE.Mesh( new THREE.TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]]],
-				Y: [[ new THREE.Mesh( new THREE.TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]]],
-				Z: [[ new THREE.Mesh( new THREE.TorusGeometry( 0.5, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]],
-				E: [[ new THREE.Mesh( new THREE.TorusGeometry( 0.75, 0.1, 2, 24 ), matInvisible ) ]]
-			};
-			const gizmoScale = {
-				X: [[ new THREE.Mesh( scaleHandleGeometry, matRed ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( lineGeometry2, matRed ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( scaleHandleGeometry, matRed ), [ - 0.5, 0, 0 ], [ 0, 0, Math.PI / 2 ]]],
-				Y: [[ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.5, 0 ]], [ new THREE.Mesh( lineGeometry2, matGreen ) ], [ new THREE.Mesh( scaleHandleGeometry, matGreen ), [ 0, - 0.5, 0 ], [ 0, 0, Math.PI ]]],
-				Z: [[ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( lineGeometry2, matBlue ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, - 0.5 ], [ - Math.PI / 2, 0, 0 ]]],
-				XY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matBlueTransparent ), [ 0.15, 0.15, 0 ]]],
-				YZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matRedTransparent ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]],
-				XZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.15, 0.15, 0.01 ), matGreenTransparent ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]],
-				XYZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.1, 0.1, 0.1 ), matWhiteTransparent.clone() ) ]]
-			};
-			const pickerScale = {
-				X: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0.3, 0, 0 ], [ 0, 0, - Math.PI / 2 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ - 0.3, 0, 0 ], [ 0, 0, Math.PI / 2 ]]],
-				Y: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0.3, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, - 0.3, 0 ], [ 0, 0, Math.PI ]]],
-				Z: [[ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, 0.3 ], [ Math.PI / 2, 0, 0 ]], [ new THREE.Mesh( new THREE.CylinderGeometry( 0.2, 0, 0.6, 4 ), matInvisible ), [ 0, 0, - 0.3 ], [ - Math.PI / 2, 0, 0 ]]],
-				XY: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0.15, 0 ]]],
-				YZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]]],
-				XZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.01 ), matInvisible ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]]],
-				XYZ: [[ new THREE.Mesh( new THREE.BoxGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 0 ]]]
-			};
-			const helperScale = {
-				X: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]],
-				Y: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]],
-				Z: [[ new THREE.Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]]
-			};
-
-			// Creates an THREE.Object3D with gizmos described in custom hierarchy definition.
-
-			function setupGizmo( gizmoMap ) {
-
-				const gizmo = new THREE.Object3D();
-				for ( const name in gizmoMap ) {
-
-					for ( let i = gizmoMap[ name ].length; i --; ) {
-
-						const object = gizmoMap[ name ][ i ][ 0 ].clone();
-						const position = gizmoMap[ name ][ i ][ 1 ];
-						const rotation = gizmoMap[ name ][ i ][ 2 ];
-						const scale = gizmoMap[ name ][ i ][ 3 ];
-						const tag = gizmoMap[ name ][ i ][ 4 ];
-
-						// name and tag properties are essential for picking and updating logic.
-						object.name = name;
-						object.tag = tag;
-						if ( position ) {
-
-							object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
-
-						}
-
-						if ( rotation ) {
-
-							object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
-
-						}
-
-						if ( scale ) {
-
-							object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] );
-
-						}
-
-						object.updateMatrix();
-						const tempGeometry = object.geometry.clone();
-						tempGeometry.applyMatrix4( object.matrix );
-						object.geometry = tempGeometry;
-						object.renderOrder = Infinity;
-						object.position.set( 0, 0, 0 );
-						object.rotation.set( 0, 0, 0 );
-						object.scale.set( 1, 1, 1 );
-						gizmo.add( object );
-
-					}
-
-				}
-
-				return gizmo;
-
-			}
-
-			// Gizmo creation
-
-			this.gizmo = {};
-			this.picker = {};
-			this.helper = {};
-			this.add( this.gizmo[ 'translate' ] = setupGizmo( gizmoTranslate ) );
-			this.add( this.gizmo[ 'rotate' ] = setupGizmo( gizmoRotate ) );
-			this.add( this.gizmo[ 'scale' ] = setupGizmo( gizmoScale ) );
-			this.add( this.picker[ 'translate' ] = setupGizmo( pickerTranslate ) );
-			this.add( this.picker[ 'rotate' ] = setupGizmo( pickerRotate ) );
-			this.add( this.picker[ 'scale' ] = setupGizmo( pickerScale ) );
-			this.add( this.helper[ 'translate' ] = setupGizmo( helperTranslate ) );
-			this.add( this.helper[ 'rotate' ] = setupGizmo( helperRotate ) );
-			this.add( this.helper[ 'scale' ] = setupGizmo( helperScale ) );
-
-			// Pickers should be hidden always
-
-			this.picker[ 'translate' ].visible = false;
-			this.picker[ 'rotate' ].visible = false;
-			this.picker[ 'scale' ].visible = false;
-
-		}
-
-		// updateMatrixWorld will update transformations and appearance of individual handles
-
-		updateMatrixWorld( force ) {
-
-			const space = this.mode === 'scale' ? 'local' : this.space; // scale always oriented to local rotation
-
-			const quaternion = space === 'local' ? this.worldQuaternion : _identityQuaternion;
-
-			// Show only gizmos for current transform mode
-
-			this.gizmo[ 'translate' ].visible = this.mode === 'translate';
-			this.gizmo[ 'rotate' ].visible = this.mode === 'rotate';
-			this.gizmo[ 'scale' ].visible = this.mode === 'scale';
-			this.helper[ 'translate' ].visible = this.mode === 'translate';
-			this.helper[ 'rotate' ].visible = this.mode === 'rotate';
-			this.helper[ 'scale' ].visible = this.mode === 'scale';
-			let handles = [];
-			handles = handles.concat( this.picker[ this.mode ].children );
-			handles = handles.concat( this.gizmo[ this.mode ].children );
-			handles = handles.concat( this.helper[ this.mode ].children );
-			for ( let i = 0; i < handles.length; i ++ ) {
-
-				const handle = handles[ i ];
-
-				// hide aligned to camera
-
-				handle.visible = true;
-				handle.rotation.set( 0, 0, 0 );
-				handle.position.copy( this.worldPosition );
-				let factor;
-				if ( this.camera.isOrthographicCamera ) {
-
-					factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom;
-
-				} else {
-
-					factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 );
-
-				}
-
-				handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 4 );
-
-				// TODO: simplify helpers and consider decoupling from gizmo
-
-				if ( handle.tag === 'helper' ) {
-
-					handle.visible = false;
-					if ( handle.name === 'AXIS' ) {
-
-						handle.position.copy( this.worldPositionStart );
-						handle.visible = !! this.axis;
-						if ( this.axis === 'X' ) {
-
-							_tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, 0 ) );
-							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
-							if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
-
-								handle.visible = false;
-
-							}
-
-						}
-
-						if ( this.axis === 'Y' ) {
-
-							_tempQuaternion.setFromEuler( _tempEuler.set( 0, 0, Math.PI / 2 ) );
-							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
-							if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
-
-								handle.visible = false;
-
-							}
-
-						}
-
-						if ( this.axis === 'Z' ) {
-
-							_tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
-							handle.quaternion.copy( quaternion ).multiply( _tempQuaternion );
-							if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
-
-								handle.visible = false;
-
-							}
-
-						}
-
-						if ( this.axis === 'XYZE' ) {
-
-							_tempQuaternion.setFromEuler( _tempEuler.set( 0, Math.PI / 2, 0 ) );
-							_alignVector.copy( this.rotationAxis );
-							handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( _zeroVector, _alignVector, _unitY ) );
-							handle.quaternion.multiply( _tempQuaternion );
-							handle.visible = this.dragging;
-
-						}
-
-						if ( this.axis === 'E' ) {
-
-							handle.visible = false;
-
-						}
-
-					} else if ( handle.name === 'START' ) {
-
-						handle.position.copy( this.worldPositionStart );
-						handle.visible = this.dragging;
-
-					} else if ( handle.name === 'END' ) {
-
-						handle.position.copy( this.worldPosition );
-						handle.visible = this.dragging;
-
-					} else if ( handle.name === 'DELTA' ) {
-
-						handle.position.copy( this.worldPositionStart );
-						handle.quaternion.copy( this.worldQuaternionStart );
-						_tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 );
-						_tempVector.applyQuaternion( this.worldQuaternionStart.clone().invert() );
-						handle.scale.copy( _tempVector );
-						handle.visible = this.dragging;
-
-					} else {
-
-						handle.quaternion.copy( quaternion );
-						if ( this.dragging ) {
-
-							handle.position.copy( this.worldPositionStart );
-
-						} else {
-
-							handle.position.copy( this.worldPosition );
-
-						}
-
-						if ( this.axis ) {
-
-							handle.visible = this.axis.search( handle.name ) !== - 1;
-
-						}
-
-					}
-
-					// If updating helper, skip rest of the loop
-					continue;
-
-				}
-
-				// Align handles to current local or world rotation
-
-				handle.quaternion.copy( quaternion );
-				if ( this.mode === 'translate' || this.mode === 'scale' ) {
-
-					// Hide translate and scale axis facing the camera
-
-					const AXIS_HIDE_THRESHOLD = 0.99;
-					const PLANE_HIDE_THRESHOLD = 0.2;
-					if ( handle.name === 'X' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-					if ( handle.name === 'Y' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-					if ( handle.name === 'Z' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-					if ( handle.name === 'XY' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-					if ( handle.name === 'YZ' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-					if ( handle.name === 'XZ' ) {
-
-						if ( Math.abs( _alignVector.copy( _unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_THRESHOLD ) {
-
-							handle.scale.set( 1e-10, 1e-10, 1e-10 );
-							handle.visible = false;
-
-						}
-
-					}
-
-				} else if ( this.mode === 'rotate' ) {
-
-					// Align handles to current local or world rotation
-
-					_tempQuaternion2.copy( quaternion );
-					_alignVector.copy( this.eye ).applyQuaternion( _tempQuaternion.copy( quaternion ).invert() );
-					if ( handle.name.search( 'E' ) !== - 1 ) {
-
-						handle.quaternion.setFromRotationMatrix( _lookAtMatrix.lookAt( this.eye, _zeroVector, _unitY ) );
-
-					}
-
-					if ( handle.name === 'X' ) {
-
-						_tempQuaternion.setFromAxisAngle( _unitX, Math.atan2( - _alignVector.y, _alignVector.z ) );
-						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
-						handle.quaternion.copy( _tempQuaternion );
-
-					}
-
-					if ( handle.name === 'Y' ) {
-
-						_tempQuaternion.setFromAxisAngle( _unitY, Math.atan2( _alignVector.x, _alignVector.z ) );
-						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
-						handle.quaternion.copy( _tempQuaternion );
-
-					}
-
-					if ( handle.name === 'Z' ) {
-
-						_tempQuaternion.setFromAxisAngle( _unitZ, Math.atan2( _alignVector.y, _alignVector.x ) );
-						_tempQuaternion.multiplyQuaternions( _tempQuaternion2, _tempQuaternion );
-						handle.quaternion.copy( _tempQuaternion );
-
-					}
-
-				}
-
-				// Hide disabled axes
-				handle.visible = handle.visible && ( handle.name.indexOf( 'X' ) === - 1 || this.showX );
-				handle.visible = handle.visible && ( handle.name.indexOf( 'Y' ) === - 1 || this.showY );
-				handle.visible = handle.visible && ( handle.name.indexOf( 'Z' ) === - 1 || this.showZ );
-				handle.visible = handle.visible && ( handle.name.indexOf( 'E' ) === - 1 || this.showX && this.showY && this.showZ );
-
-				// highlight selected axis
-
-				handle.material._color = handle.material._color || handle.material.color.clone();
-				handle.material._opacity = handle.material._opacity || handle.material.opacity;
-				handle.material.color.copy( handle.material._color );
-				handle.material.opacity = handle.material._opacity;
-				if ( this.enabled && this.axis ) {
-
-					if ( handle.name === this.axis ) {
-
-						handle.material.color.setHex( 0xffff00 );
-						handle.material.opacity = 1.0;
-
-					} else if ( this.axis.split( '' ).some( function ( a ) {
-
-						return handle.name === a;
-
-					} ) ) {
-
-						handle.material.color.setHex( 0xffff00 );
-						handle.material.opacity = 1.0;
-
-					}
-
-				}
-
-			}
-
-			super.updateMatrixWorld( force );
-
-		}
-
-	}
-
-	//
-
-	class TransformControlsPlane extends THREE.Mesh {
-
-		constructor() {
-
-			super( new THREE.PlaneGeometry( 100000, 100000, 2, 2 ), new THREE.MeshBasicMaterial( {
-				visible: false,
-				wireframe: true,
-				side: THREE.DoubleSide,
-				transparent: true,
-				opacity: 0.1,
-				toneMapped: false
-			} ) );
-			this.isTransformControlsPlane = true;
-			this.type = 'TransformControlsPlane';
-
-		}
-		updateMatrixWorld( force ) {
-
-			let space = this.space;
-			this.position.copy( this.worldPosition );
-			if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
-
-			_v1.copy( _unitX ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
-			_v2.copy( _unitY ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
-			_v3.copy( _unitZ ).applyQuaternion( space === 'local' ? this.worldQuaternion : _identityQuaternion );
-
-			// Align the plane for current transform mode, axis and space.
-
-			_alignVector.copy( _v2 );
-			switch ( this.mode ) {
-
-				case 'translate':
-				case 'scale':
-					switch ( this.axis ) {
-
-						case 'X':
-							_alignVector.copy( this.eye ).cross( _v1 );
-							_dirVector.copy( _v1 ).cross( _alignVector );
-							break;
-						case 'Y':
-							_alignVector.copy( this.eye ).cross( _v2 );
-							_dirVector.copy( _v2 ).cross( _alignVector );
-							break;
-						case 'Z':
-							_alignVector.copy( this.eye ).cross( _v3 );
-							_dirVector.copy( _v3 ).cross( _alignVector );
-							break;
-						case 'XY':
-							_dirVector.copy( _v3 );
-							break;
-						case 'YZ':
-							_dirVector.copy( _v1 );
-							break;
-						case 'XZ':
-							_alignVector.copy( _v3 );
-							_dirVector.copy( _v2 );
-							break;
-						case 'XYZ':
-						case 'E':
-							_dirVector.set( 0, 0, 0 );
-							break;
-
-					}
-
-					break;
-				case 'rotate':
-				default:
-					// special case for rotate
-					_dirVector.set( 0, 0, 0 );
-
-			}
-
-			if ( _dirVector.length() === 0 ) {
-
-				// If in rotate mode, make the plane parallel to camera
-				this.quaternion.copy( this.cameraQuaternion );
-
-			} else {
-
-				_tempMatrix.lookAt( _tempVector.set( 0, 0, 0 ), _dirVector, _alignVector );
-				this.quaternion.setFromRotationMatrix( _tempMatrix );
-
-			}
-
-			super.updateMatrixWorld( force );
-
-		}
-
-	}
-
-	THREE.TransformControls = TransformControls;
-	THREE.TransformControlsGizmo = TransformControlsGizmo;
-	THREE.TransformControlsPlane = TransformControlsPlane;
-
-} )();

+ 0 - 347
examples/js/csm/CSM.js

@@ -1,347 +0,0 @@
-( function () {
-
-	const _cameraToLightMatrix = new THREE.Matrix4();
-	const _lightSpaceFrustum = new THREE.CSMFrustum();
-	const _center = new THREE.Vector3();
-	const _bbox = new THREE.Box3();
-	const _uniformArray = [];
-	const _logArray = [];
-	class CSM {
-
-		constructor( data ) {
-
-			data = data || {};
-			this.camera = data.camera;
-			this.parent = data.parent;
-			this.cascades = data.cascades || 3;
-			this.maxFar = data.maxFar || 100000;
-			this.mode = data.mode || 'practical';
-			this.shadowMapSize = data.shadowMapSize || 2048;
-			this.shadowBias = data.shadowBias || 0.000001;
-			this.lightDirection = data.lightDirection || new THREE.Vector3( 1, - 1, 1 ).normalize();
-			this.lightIntensity = data.lightIntensity || 1;
-			this.lightNear = data.lightNear || 1;
-			this.lightFar = data.lightFar || 2000;
-			this.lightMargin = data.lightMargin || 200;
-			this.customSplitsCallback = data.customSplitsCallback;
-			this.fade = false;
-			this.mainFrustum = new THREE.CSMFrustum();
-			this.frustums = [];
-			this.breaks = [];
-			this.lights = [];
-			this.shaders = new Map();
-			this.createLights();
-			this.updateFrustums();
-			this.injectInclude();
-
-		}
-		createLights() {
-
-			for ( let i = 0; i < this.cascades; i ++ ) {
-
-				const light = new THREE.DirectionalLight( 0xffffff, this.lightIntensity );
-				light.castShadow = true;
-				light.shadow.mapSize.width = this.shadowMapSize;
-				light.shadow.mapSize.height = this.shadowMapSize;
-				light.shadow.camera.near = this.lightNear;
-				light.shadow.camera.far = this.lightFar;
-				light.shadow.bias = this.shadowBias;
-				this.parent.add( light );
-				this.parent.add( light.target );
-				this.lights.push( light );
-
-			}
-
-		}
-		initCascades() {
-
-			const camera = this.camera;
-			camera.updateProjectionMatrix();
-			this.mainFrustum.setFromProjectionMatrix( camera.projectionMatrix, this.maxFar );
-			this.mainFrustum.split( this.breaks, this.frustums );
-
-		}
-		updateShadowBounds() {
-
-			const frustums = this.frustums;
-			for ( let i = 0; i < frustums.length; i ++ ) {
-
-				const light = this.lights[ i ];
-				const shadowCam = light.shadow.camera;
-				const frustum = this.frustums[ i ];
-
-				// Get the two points that represent that furthest points on the frustum assuming
-				// that's either the diagonal across the far plane or the diagonal across the whole
-				// frustum itself.
-				const nearVerts = frustum.vertices.near;
-				const farVerts = frustum.vertices.far;
-				const point1 = farVerts[ 0 ];
-				let point2;
-				if ( point1.distanceTo( farVerts[ 2 ] ) > point1.distanceTo( nearVerts[ 2 ] ) ) {
-
-					point2 = farVerts[ 2 ];
-
-				} else {
-
-					point2 = nearVerts[ 2 ];
-
-				}
-
-				let squaredBBWidth = point1.distanceTo( point2 );
-				if ( this.fade ) {
-
-					// expand the shadow extents by the fade margin if fade is enabled.
-					const camera = this.camera;
-					const far = Math.max( camera.far, this.maxFar );
-					const linearDepth = frustum.vertices.far[ 0 ].z / ( far - camera.near );
-					const margin = 0.25 * Math.pow( linearDepth, 2.0 ) * ( far - camera.near );
-					squaredBBWidth += margin;
-
-				}
-
-				shadowCam.left = - squaredBBWidth / 2;
-				shadowCam.right = squaredBBWidth / 2;
-				shadowCam.top = squaredBBWidth / 2;
-				shadowCam.bottom = - squaredBBWidth / 2;
-				shadowCam.updateProjectionMatrix();
-
-			}
-
-		}
-		getBreaks() {
-
-			const camera = this.camera;
-			const far = Math.min( camera.far, this.maxFar );
-			this.breaks.length = 0;
-			switch ( this.mode ) {
-
-				case 'uniform':
-					uniformSplit( this.cascades, camera.near, far, this.breaks );
-					break;
-				case 'logarithmic':
-					logarithmicSplit( this.cascades, camera.near, far, this.breaks );
-					break;
-				case 'practical':
-					practicalSplit( this.cascades, camera.near, far, 0.5, this.breaks );
-					break;
-				case 'custom':
-					if ( this.customSplitsCallback === undefined ) console.error( 'CSM: Custom split scheme callback not defined.' );
-					this.customSplitsCallback( this.cascades, camera.near, far, this.breaks );
-					break;
-
-			}
-
-			function uniformSplit( amount, near, far, target ) {
-
-				for ( let i = 1; i < amount; i ++ ) {
-
-					target.push( ( near + ( far - near ) * i / amount ) / far );
-
-				}
-
-				target.push( 1 );
-
-			}
-
-			function logarithmicSplit( amount, near, far, target ) {
-
-				for ( let i = 1; i < amount; i ++ ) {
-
-					target.push( near * ( far / near ) ** ( i / amount ) / far );
-
-				}
-
-				target.push( 1 );
-
-			}
-
-			function practicalSplit( amount, near, far, lambda, target ) {
-
-				_uniformArray.length = 0;
-				_logArray.length = 0;
-				logarithmicSplit( amount, near, far, _logArray );
-				uniformSplit( amount, near, far, _uniformArray );
-				for ( let i = 1; i < amount; i ++ ) {
-
-					target.push( THREE.MathUtils.lerp( _uniformArray[ i - 1 ], _logArray[ i - 1 ], lambda ) );
-
-				}
-
-				target.push( 1 );
-
-			}
-
-		}
-		update() {
-
-			const camera = this.camera;
-			const frustums = this.frustums;
-			for ( let i = 0; i < frustums.length; i ++ ) {
-
-				const light = this.lights[ i ];
-				const shadowCam = light.shadow.camera;
-				const texelWidth = ( shadowCam.right - shadowCam.left ) / this.shadowMapSize;
-				const texelHeight = ( shadowCam.top - shadowCam.bottom ) / this.shadowMapSize;
-				light.shadow.camera.updateMatrixWorld( true );
-				_cameraToLightMatrix.multiplyMatrices( light.shadow.camera.matrixWorldInverse, camera.matrixWorld );
-				frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
-				const nearVerts = _lightSpaceFrustum.vertices.near;
-				const farVerts = _lightSpaceFrustum.vertices.far;
-				_bbox.makeEmpty();
-				for ( let j = 0; j < 4; j ++ ) {
-
-					_bbox.expandByPoint( nearVerts[ j ] );
-					_bbox.expandByPoint( farVerts[ j ] );
-
-				}
-
-				_bbox.getCenter( _center );
-				_center.z = _bbox.max.z + this.lightMargin;
-				_center.x = Math.floor( _center.x / texelWidth ) * texelWidth;
-				_center.y = Math.floor( _center.y / texelHeight ) * texelHeight;
-				_center.applyMatrix4( light.shadow.camera.matrixWorld );
-				light.position.copy( _center );
-				light.target.position.copy( _center );
-				light.target.position.x += this.lightDirection.x;
-				light.target.position.y += this.lightDirection.y;
-				light.target.position.z += this.lightDirection.z;
-
-			}
-
-		}
-		injectInclude() {
-
-			THREE.ShaderChunk.lights_fragment_begin = THREE.CSMShader.lights_fragment_begin;
-			THREE.ShaderChunk.lights_pars_begin = THREE.CSMShader.lights_pars_begin;
-
-		}
-		setupMaterial( material ) {
-
-			material.defines = material.defines || {};
-			material.defines.USE_CSM = 1;
-			material.defines.CSM_CASCADES = this.cascades;
-			if ( this.fade ) {
-
-				material.defines.CSM_FADE = '';
-
-			}
-
-			const breaksVec2 = [];
-			const scope = this;
-			const shaders = this.shaders;
-			material.onBeforeCompile = function ( shader ) {
-
-				const far = Math.min( scope.camera.far, scope.maxFar );
-				scope.getExtendedBreaks( breaksVec2 );
-				shader.uniforms.CSM_cascades = {
-					value: breaksVec2
-				};
-				shader.uniforms.cameraNear = {
-					value: scope.camera.near
-				};
-				shader.uniforms.shadowFar = {
-					value: far
-				};
-				shaders.set( material, shader );
-
-			};
-
-			shaders.set( material, null );
-
-		}
-		updateUniforms() {
-
-			const far = Math.min( this.camera.far, this.maxFar );
-			const shaders = this.shaders;
-			shaders.forEach( function ( shader, material ) {
-
-				if ( shader !== null ) {
-
-					const uniforms = shader.uniforms;
-					this.getExtendedBreaks( uniforms.CSM_cascades.value );
-					uniforms.cameraNear.value = this.camera.near;
-					uniforms.shadowFar.value = far;
-
-				}
-
-				if ( ! this.fade && 'CSM_FADE' in material.defines ) {
-
-					delete material.defines.CSM_FADE;
-					material.needsUpdate = true;
-
-				} else if ( this.fade && ! ( 'CSM_FADE' in material.defines ) ) {
-
-					material.defines.CSM_FADE = '';
-					material.needsUpdate = true;
-
-				}
-
-			}, this );
-
-		}
-		getExtendedBreaks( target ) {
-
-			while ( target.length < this.breaks.length ) {
-
-				target.push( new THREE.Vector2() );
-
-			}
-
-			target.length = this.breaks.length;
-			for ( let i = 0; i < this.cascades; i ++ ) {
-
-				const amount = this.breaks[ i ];
-				const prev = this.breaks[ i - 1 ] || 0;
-				target[ i ].x = prev;
-				target[ i ].y = amount;
-
-			}
-
-		}
-		updateFrustums() {
-
-			this.getBreaks();
-			this.initCascades();
-			this.updateShadowBounds();
-			this.updateUniforms();
-
-		}
-		remove() {
-
-			for ( let i = 0; i < this.lights.length; i ++ ) {
-
-				this.parent.remove( this.lights[ i ].target );
-				this.parent.remove( this.lights[ i ] );
-
-			}
-
-		}
-		dispose() {
-
-			const shaders = this.shaders;
-			shaders.forEach( function ( shader, material ) {
-
-				delete material.onBeforeCompile;
-				delete material.defines.USE_CSM;
-				delete material.defines.CSM_CASCADES;
-				delete material.defines.CSM_FADE;
-				if ( shader !== null ) {
-
-					delete shader.uniforms.CSM_cascades;
-					delete shader.uniforms.cameraNear;
-					delete shader.uniforms.shadowFar;
-
-				}
-
-				material.needsUpdate = true;
-
-			} );
-			shaders.clear();
-
-		}
-
-	}
-
-	THREE.CSM = CSM;
-
-} )();

+ 0 - 127
examples/js/csm/CSMFrustum.js

@@ -1,127 +0,0 @@
-( function () {
-
-	const inverseProjectionMatrix = new THREE.Matrix4();
-	class CSMFrustum {
-
-		constructor( data ) {
-
-			data = data || {};
-			this.vertices = {
-				near: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ],
-				far: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]
-			};
-			if ( data.projectionMatrix !== undefined ) {
-
-				this.setFromProjectionMatrix( data.projectionMatrix, data.maxFar || 10000 );
-
-			}
-
-		}
-		setFromProjectionMatrix( projectionMatrix, maxFar ) {
-
-			const isOrthographic = projectionMatrix.elements[ 2 * 4 + 3 ] === 0;
-			inverseProjectionMatrix.copy( projectionMatrix ).invert();
-
-			// 3 --- 0  vertices.near/far order
-			// |     |
-			// 2 --- 1
-			// clip space spans from [-1, 1]
-
-			this.vertices.near[ 0 ].set( 1, 1, - 1 );
-			this.vertices.near[ 1 ].set( 1, - 1, - 1 );
-			this.vertices.near[ 2 ].set( - 1, - 1, - 1 );
-			this.vertices.near[ 3 ].set( - 1, 1, - 1 );
-			this.vertices.near.forEach( function ( v ) {
-
-				v.applyMatrix4( inverseProjectionMatrix );
-
-			} );
-			this.vertices.far[ 0 ].set( 1, 1, 1 );
-			this.vertices.far[ 1 ].set( 1, - 1, 1 );
-			this.vertices.far[ 2 ].set( - 1, - 1, 1 );
-			this.vertices.far[ 3 ].set( - 1, 1, 1 );
-			this.vertices.far.forEach( function ( v ) {
-
-				v.applyMatrix4( inverseProjectionMatrix );
-				const absZ = Math.abs( v.z );
-				if ( isOrthographic ) {
-
-					v.z *= Math.min( maxFar / absZ, 1.0 );
-
-				} else {
-
-					v.multiplyScalar( Math.min( maxFar / absZ, 1.0 ) );
-
-				}
-
-			} );
-			return this.vertices;
-
-		}
-		split( breaks, target ) {
-
-			while ( breaks.length > target.length ) {
-
-				target.push( new CSMFrustum() );
-
-			}
-
-			target.length = breaks.length;
-			for ( let i = 0; i < breaks.length; i ++ ) {
-
-				const cascade = target[ i ];
-				if ( i === 0 ) {
-
-					for ( let j = 0; j < 4; j ++ ) {
-
-						cascade.vertices.near[ j ].copy( this.vertices.near[ j ] );
-
-					}
-
-				} else {
-
-					for ( let j = 0; j < 4; j ++ ) {
-
-						cascade.vertices.near[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i - 1 ] );
-
-					}
-
-				}
-
-				if ( i === breaks.length - 1 ) {
-
-					for ( let j = 0; j < 4; j ++ ) {
-
-						cascade.vertices.far[ j ].copy( this.vertices.far[ j ] );
-
-					}
-
-				} else {
-
-					for ( let j = 0; j < 4; j ++ ) {
-
-						cascade.vertices.far[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i ] );
-
-					}
-
-				}
-
-			}
-
-		}
-		toSpace( cameraMatrix, target ) {
-
-			for ( let i = 0; i < 4; i ++ ) {
-
-				target.vertices.near[ i ].copy( this.vertices.near[ i ] ).applyMatrix4( cameraMatrix );
-				target.vertices.far[ i ].copy( this.vertices.far[ i ] ).applyMatrix4( cameraMatrix );
-
-			}
-
-		}
-
-	}
-
-	THREE.CSMFrustum = CSMFrustum;
-
-} )();

+ 0 - 165
examples/js/csm/CSMHelper.js

@@ -1,165 +0,0 @@
-( function () {
-
-	class CSMHelper extends THREE.Group {
-
-		constructor( csm ) {
-
-			super();
-			this.csm = csm;
-			this.displayFrustum = true;
-			this.displayPlanes = true;
-			this.displayShadowBounds = true;
-			const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
-			const positions = new Float32Array( 24 );
-			const frustumGeometry = new THREE.BufferGeometry();
-			frustumGeometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
-			frustumGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3, false ) );
-			const frustumLines = new THREE.LineSegments( frustumGeometry, new THREE.LineBasicMaterial() );
-			this.add( frustumLines );
-			this.frustumLines = frustumLines;
-			this.cascadeLines = [];
-			this.cascadePlanes = [];
-			this.shadowLines = [];
-
-		}
-		updateVisibility() {
-
-			const displayFrustum = this.displayFrustum;
-			const displayPlanes = this.displayPlanes;
-			const displayShadowBounds = this.displayShadowBounds;
-			const frustumLines = this.frustumLines;
-			const cascadeLines = this.cascadeLines;
-			const cascadePlanes = this.cascadePlanes;
-			const shadowLines = this.shadowLines;
-			for ( let i = 0, l = cascadeLines.length; i < l; i ++ ) {
-
-				const cascadeLine = cascadeLines[ i ];
-				const cascadePlane = cascadePlanes[ i ];
-				const shadowLineGroup = shadowLines[ i ];
-				cascadeLine.visible = displayFrustum;
-				cascadePlane.visible = displayFrustum && displayPlanes;
-				shadowLineGroup.visible = displayShadowBounds;
-
-			}
-
-			frustumLines.visible = displayFrustum;
-
-		}
-		update() {
-
-			const csm = this.csm;
-			const camera = csm.camera;
-			const cascades = csm.cascades;
-			const mainFrustum = csm.mainFrustum;
-			const frustums = csm.frustums;
-			const lights = csm.lights;
-			const frustumLines = this.frustumLines;
-			const frustumLinePositions = frustumLines.geometry.getAttribute( 'position' );
-			const cascadeLines = this.cascadeLines;
-			const cascadePlanes = this.cascadePlanes;
-			const shadowLines = this.shadowLines;
-			this.position.copy( camera.position );
-			this.quaternion.copy( camera.quaternion );
-			this.scale.copy( camera.scale );
-			this.updateMatrixWorld( true );
-			while ( cascadeLines.length > cascades ) {
-
-				this.remove( cascadeLines.pop() );
-				this.remove( cascadePlanes.pop() );
-				this.remove( shadowLines.pop() );
-
-			}
-
-			while ( cascadeLines.length < cascades ) {
-
-				const cascadeLine = new THREE.Box3Helper( new THREE.Box3(), 0xffffff );
-				const planeMat = new THREE.MeshBasicMaterial( {
-					transparent: true,
-					opacity: 0.1,
-					depthWrite: false,
-					side: THREE.DoubleSide
-				} );
-				const cascadePlane = new THREE.Mesh( new THREE.PlaneGeometry(), planeMat );
-				const shadowLineGroup = new THREE.Group();
-				const shadowLine = new THREE.Box3Helper( new THREE.Box3(), 0xffff00 );
-				shadowLineGroup.add( shadowLine );
-				this.add( cascadeLine );
-				this.add( cascadePlane );
-				this.add( shadowLineGroup );
-				cascadeLines.push( cascadeLine );
-				cascadePlanes.push( cascadePlane );
-				shadowLines.push( shadowLineGroup );
-
-			}
-
-			for ( let i = 0; i < cascades; i ++ ) {
-
-				const frustum = frustums[ i ];
-				const light = lights[ i ];
-				const shadowCam = light.shadow.camera;
-				const farVerts = frustum.vertices.far;
-				const cascadeLine = cascadeLines[ i ];
-				const cascadePlane = cascadePlanes[ i ];
-				const shadowLineGroup = shadowLines[ i ];
-				const shadowLine = shadowLineGroup.children[ 0 ];
-				cascadeLine.box.min.copy( farVerts[ 2 ] );
-				cascadeLine.box.max.copy( farVerts[ 0 ] );
-				cascadeLine.box.max.z += 1e-4;
-				cascadePlane.position.addVectors( farVerts[ 0 ], farVerts[ 2 ] );
-				cascadePlane.position.multiplyScalar( 0.5 );
-				cascadePlane.scale.subVectors( farVerts[ 0 ], farVerts[ 2 ] );
-				cascadePlane.scale.z = 1e-4;
-				this.remove( shadowLineGroup );
-				shadowLineGroup.position.copy( shadowCam.position );
-				shadowLineGroup.quaternion.copy( shadowCam.quaternion );
-				shadowLineGroup.scale.copy( shadowCam.scale );
-				shadowLineGroup.updateMatrixWorld( true );
-				this.attach( shadowLineGroup );
-				shadowLine.box.min.set( shadowCam.bottom, shadowCam.left, - shadowCam.far );
-				shadowLine.box.max.set( shadowCam.top, shadowCam.right, - shadowCam.near );
-
-			}
-
-			const nearVerts = mainFrustum.vertices.near;
-			const farVerts = mainFrustum.vertices.far;
-			frustumLinePositions.setXYZ( 0, farVerts[ 0 ].x, farVerts[ 0 ].y, farVerts[ 0 ].z );
-			frustumLinePositions.setXYZ( 1, farVerts[ 3 ].x, farVerts[ 3 ].y, farVerts[ 3 ].z );
-			frustumLinePositions.setXYZ( 2, farVerts[ 2 ].x, farVerts[ 2 ].y, farVerts[ 2 ].z );
-			frustumLinePositions.setXYZ( 3, farVerts[ 1 ].x, farVerts[ 1 ].y, farVerts[ 1 ].z );
-			frustumLinePositions.setXYZ( 4, nearVerts[ 0 ].x, nearVerts[ 0 ].y, nearVerts[ 0 ].z );
-			frustumLinePositions.setXYZ( 5, nearVerts[ 3 ].x, nearVerts[ 3 ].y, nearVerts[ 3 ].z );
-			frustumLinePositions.setXYZ( 6, nearVerts[ 2 ].x, nearVerts[ 2 ].y, nearVerts[ 2 ].z );
-			frustumLinePositions.setXYZ( 7, nearVerts[ 1 ].x, nearVerts[ 1 ].y, nearVerts[ 1 ].z );
-			frustumLinePositions.needsUpdate = true;
-
-		}
-		dispose() {
-
-			const frustumLines = this.frustumLines;
-			const cascadeLines = this.cascadeLines;
-			const cascadePlanes = this.cascadePlanes;
-			const shadowLines = this.shadowLines;
-			frustumLines.geometry.dispose();
-			frustumLines.material.dispose();
-			const cascades = this.csm.cascades;
-			for ( let i = 0; i < cascades; i ++ ) {
-
-				const cascadeLine = cascadeLines[ i ];
-				const cascadePlane = cascadePlanes[ i ];
-				const shadowLineGroup = shadowLines[ i ];
-				const shadowLine = shadowLineGroup.children[ 0 ];
-				cascadeLine.dispose(); // THREE.Box3Helper
-
-				cascadePlane.geometry.dispose();
-				cascadePlane.material.dispose();
-				shadowLine.dispose(); // THREE.Box3Helper
-
-			}
-
-		}
-
-	}
-
-	THREE.CSMHelper = CSMHelper;
-
-} )();

+ 0 - 253
examples/js/csm/CSMShader.js

@@ -1,253 +0,0 @@
-( function () {
-
-	const CSMShader = {
-		lights_fragment_begin: /* glsl */`
-GeometricContext geometry;
-
-geometry.position = - vViewPosition;
-geometry.normal = normal;
-geometry.viewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );
-
-#ifdef CLEARCOAT
-
-	geometry.clearcoatNormal = clearcoatNormal;
-
-#endif
-
-IncidentLight directLight;
-
-#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
-
-	PointLight pointLight;
-	#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
-	PointLightShadow pointLightShadow;
-	#endif
-
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
-
-		pointLight = pointLights[ i ];
-
-		getPointLightInfo( pointLight, geometry, directLight );
-
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
-		pointLightShadow = pointLightShadows[ i ];
-		directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;
-		#endif
-
-		RE_Direct( directLight, geometry, material, reflectedLight );
-
-	}
-	#pragma unroll_loop_end
-
-#endif
-
-#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )
-
-	SpotLight spotLight;
-	#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
-	SpotLightShadow spotLightShadow;
-	#endif
-
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
-
-		spotLight = spotLights[ i ];
-
-		getSpotLightInfo( spotLight, geometry, directLight );
-
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
-		spotLightShadow = spotLightShadows[ i ];
-		directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;
-		#endif
-
-		RE_Direct( directLight, geometry, material, reflectedLight );
-
-	}
-	#pragma unroll_loop_end
-
-#endif
-
-#if ( NUM_DIR_LIGHTS > 0) && defined( RE_Direct ) && defined( USE_CSM ) && defined( CSM_CASCADES )
-
-	DirectionalLight directionalLight;
-	float linearDepth = (vViewPosition.z) / (shadowFar - cameraNear);
-	#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
-	DirectionalLightShadow directionalLightShadow;
-	#endif
-
-	#if defined( USE_SHADOWMAP ) && defined( CSM_FADE )
-	vec2 cascade;
-	float cascadeCenter;
-	float closestEdge;
-	float margin;
-	float csmx;
-	float csmy;
-
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
-
-		directionalLight = directionalLights[ i ];
-		getDirectionalLightInfo( directionalLight, geometry, directLight );
-
-	  	#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-			// NOTE: Depth gets larger away from the camera.
-			// cascade.x is closer, cascade.y is further
-			cascade = CSM_cascades[ i ];
-			cascadeCenter = ( cascade.x + cascade.y ) / 2.0;
-			closestEdge = linearDepth < cascadeCenter ? cascade.x : cascade.y;
-			margin = 0.25 * pow( closestEdge, 2.0 );
-			csmx = cascade.x - margin / 2.0;
-			csmy = cascade.y + margin / 2.0;
-			if( linearDepth >= csmx && ( linearDepth < csmy || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 ) ) {
-
-				float dist = min( linearDepth - csmx, csmy - linearDepth );
-				float ratio = clamp( dist / margin, 0.0, 1.0 );
-
-				vec3 prevColor = directLight.color;
-				directionalLightShadow = directionalLightShadows[ i ];
-				directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
-
-				bool shouldFadeLastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth > cascadeCenter;
-				directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
-
-				ReflectedLight prevLight = reflectedLight;
-				RE_Direct( directLight, geometry, material, reflectedLight );
-
-				bool shouldBlend = UNROLLED_LOOP_INDEX != CSM_CASCADES - 1 || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth < cascadeCenter;
-				float blendRatio = shouldBlend ? ratio : 1.0;
-
-				reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
-				reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
-				reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
-				reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
-
-			}
-	  	#endif
-
-	}
-	#pragma unroll_loop_end
-	#else
-
-		#pragma unroll_loop_start
-		for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
-
-			directionalLight = directionalLights[ i ];
-			getDirectionalLightInfo( directionalLight, geometry, directLight );
-
-			#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-
-			directionalLightShadow = directionalLightShadows[ i ];
-			if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
-
-			if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometry, material, reflectedLight );
-
-			#endif
-
-		}
-		#pragma unroll_loop_end
-
-	#endif
-
-	#if ( NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS)
-		// compute the lights not casting shadows (if any)
-
-		#pragma unroll_loop_start
-		for ( int i = NUM_DIR_LIGHT_SHADOWS; i < NUM_DIR_LIGHTS; i ++ ) {
-
-			directionalLight = directionalLights[ i ];
-
-			getDirectionalLightInfo( directionalLight, geometry, directLight );
-
-			RE_Direct( directLight, geometry, material, reflectedLight );
-
-		}
-		#pragma unroll_loop_end
-
-	#endif
-
-#endif
-
-
-#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && !defined( USE_CSM ) && !defined( CSM_CASCADES )
-
-	DirectionalLight directionalLight;
-	#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
-	DirectionalLightShadow directionalLightShadow;
-	#endif
-
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
-
-		directionalLight = directionalLights[ i ];
-
-		getDirectionalLightInfo( directionalLight, geometry, directLight );
-
-		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
-		directionalLightShadow = directionalLightShadows[ i ];
-		directLight.color *= all( bvec2( directLight.visible, receiveShadow ) ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
-		#endif
-
-		RE_Direct( directLight, geometry, material, reflectedLight );
-
-	}
-	#pragma unroll_loop_end
-
-#endif
-
-#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
-
-	RectAreaLight rectAreaLight;
-
-	#pragma unroll_loop_start
-	for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
-
-		rectAreaLight = rectAreaLights[ i ];
-		RE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );
-
-	}
-	#pragma unroll_loop_end
-
-#endif
-
-#if defined( RE_IndirectDiffuse )
-
-	vec3 iblIrradiance = vec3( 0.0 );
-
-	vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
-
-	irradiance += getLightProbeIrradiance( lightProbe, geometry.normal );
-
-	#if ( NUM_HEMI_LIGHTS > 0 )
-
-		#pragma unroll_loop_start
-		for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
-
-			irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry.normal );
-
-		}
-		#pragma unroll_loop_end
-
-	#endif
-
-#endif
-
-#if defined( RE_IndirectSpecular )
-
-	vec3 radiance = vec3( 0.0 );
-	vec3 clearcoatRadiance = vec3( 0.0 );
-
-#endif
-`,
-		lights_pars_begin: /* glsl */`
-#if defined( USE_CSM ) && defined( CSM_CASCADES )
-uniform vec2 CSM_cascades[CSM_CASCADES];
-uniform float cameraNear;
-uniform float shadowFar;
-#endif
-	` + THREE.ShaderChunk.lights_pars_begin
-	};
-
-	THREE.CSMShader = CSMShader;
-
-} )();

+ 0 - 348
examples/js/curves/CurveExtras.js

@@ -1,348 +0,0 @@
-( function () {
-
-	/**
- * A bunch of parametric curves
- *
- * Formulas collected from various sources
- * http://mathworld.wolfram.com/HeartCurve.html
- * http://en.wikipedia.org/wiki/Viviani%27s_curve
- * http://www.mi.sanu.ac.rs/vismath/taylorapril2011/Taylor.pdf
- * https://prideout.net/blog/old/blog/index.html@p=44.html
- */
-
-	// GrannyKnot
-
-	class GrannyKnot extends THREE.Curve {
-
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t = 2 * Math.PI * t;
-			const x = - 0.22 * Math.cos( t ) - 1.28 * Math.sin( t ) - 0.44 * Math.cos( 3 * t ) - 0.78 * Math.sin( 3 * t );
-			const y = - 0.1 * Math.cos( 2 * t ) - 0.27 * Math.sin( 2 * t ) + 0.38 * Math.cos( 4 * t ) + 0.46 * Math.sin( 4 * t );
-			const z = 0.7 * Math.cos( 3 * t ) - 0.4 * Math.sin( 3 * t );
-			return point.set( x, y, z ).multiplyScalar( 20 );
-
-		}
-
-	}
-
-	// HeartCurve
-
-	class HeartCurve extends THREE.Curve {
-
-		constructor( scale = 5 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t *= 2 * Math.PI;
-			const x = 16 * Math.pow( Math.sin( t ), 3 );
-			const y = 13 * Math.cos( t ) - 5 * Math.cos( 2 * t ) - 2 * Math.cos( 3 * t ) - Math.cos( 4 * t );
-			const z = 0;
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// Viviani's THREE.Curve
-
-	class VivianiCurve extends THREE.Curve {
-
-		constructor( scale = 70 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t = t * 4 * Math.PI; // normalized to 0..1
-			const a = this.scale / 2;
-			const x = a * ( 1 + Math.cos( t ) );
-			const y = a * Math.sin( t );
-			const z = 2 * a * Math.sin( t / 2 );
-			return point.set( x, y, z );
-
-		}
-
-	}
-
-	// KnotCurve
-
-	class KnotCurve extends THREE.Curve {
-
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t *= 2 * Math.PI;
-			const R = 10;
-			const s = 50;
-			const x = s * Math.sin( t );
-			const y = Math.cos( t ) * ( R + s * Math.cos( t ) );
-			const z = Math.sin( t ) * ( R + s * Math.cos( t ) );
-			return point.set( x, y, z );
-
-		}
-
-	}
-
-	// HelixCurve
-
-	class HelixCurve extends THREE.Curve {
-
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const a = 30; // radius
-			const b = 150; // height
-
-			const t2 = 2 * Math.PI * t * b / 30;
-			const x = Math.cos( t2 ) * a;
-			const y = Math.sin( t2 ) * a;
-			const z = b * t;
-			return point.set( x, y, z );
-
-		}
-
-	}
-
-	// TrefoilKnot
-
-	class TrefoilKnot extends THREE.Curve {
-
-		constructor( scale = 10 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t *= Math.PI * 2;
-			const x = ( 2 + Math.cos( 3 * t ) ) * Math.cos( 2 * t );
-			const y = ( 2 + Math.cos( 3 * t ) ) * Math.sin( 2 * t );
-			const z = Math.sin( 3 * t );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// TorusKnot
-
-	class TorusKnot extends THREE.Curve {
-
-		constructor( scale = 10 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const p = 3;
-			const q = 4;
-			t *= Math.PI * 2;
-			const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
-			const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
-			const z = Math.sin( q * t );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// CinquefoilKnot
-
-	class CinquefoilKnot extends THREE.Curve {
-
-		constructor( scale = 10 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const p = 2;
-			const q = 5;
-			t *= Math.PI * 2;
-			const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
-			const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
-			const z = Math.sin( q * t );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// TrefoilPolynomialKnot
-
-	class TrefoilPolynomialKnot extends THREE.Curve {
-
-		constructor( scale = 10 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t = t * 4 - 2;
-			const x = Math.pow( t, 3 ) - 3 * t;
-			const y = Math.pow( t, 4 ) - 4 * t * t;
-			const z = 1 / 5 * Math.pow( t, 5 ) - 2 * t;
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-	function scaleTo( x, y, t ) {
-
-		const r = y - x;
-		return t * r + x;
-
-	}
-
-	// FigureEightPolynomialKnot
-
-	class FigureEightPolynomialKnot extends THREE.Curve {
-
-		constructor( scale = 1 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t = scaleTo( - 4, 4, t );
-			const x = 2 / 5 * t * ( t * t - 7 ) * ( t * t - 10 );
-			const y = Math.pow( t, 4 ) - 13 * t * t;
-			const z = 1 / 10 * t * ( t * t - 4 ) * ( t * t - 9 ) * ( t * t - 12 );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// DecoratedTorusKnot4a
-
-	class DecoratedTorusKnot4a extends THREE.Curve {
-
-		constructor( scale = 40 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			t *= Math.PI * 2;
-			const x = Math.cos( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
-			const y = Math.sin( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
-			const z = 0.35 * Math.sin( 5 * t );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// DecoratedTorusKnot4b
-
-	class DecoratedTorusKnot4b extends THREE.Curve {
-
-		constructor( scale = 40 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const fi = t * Math.PI * 2;
-			const x = Math.cos( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
-			const y = Math.sin( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
-			const z = 0.2 * Math.sin( 9 * fi );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// DecoratedTorusKnot5a
-
-	class DecoratedTorusKnot5a extends THREE.Curve {
-
-		constructor( scale = 40 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const fi = t * Math.PI * 2;
-			const x = Math.cos( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
-			const y = Math.sin( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
-			const z = 0.2 * Math.sin( 20 * fi );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	// DecoratedTorusKnot5c
-
-	class DecoratedTorusKnot5c extends THREE.Curve {
-
-		constructor( scale = 40 ) {
-
-			super();
-			this.scale = scale;
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const fi = t * Math.PI * 2;
-			const x = Math.cos( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
-			const y = Math.sin( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
-			const z = 0.35 * Math.sin( 15 * fi );
-			return point.set( x, y, z ).multiplyScalar( this.scale );
-
-		}
-
-	}
-
-	THREE.CinquefoilKnot = CinquefoilKnot;
-	THREE.DecoratedTorusKnot4a = DecoratedTorusKnot4a;
-	THREE.DecoratedTorusKnot4b = DecoratedTorusKnot4b;
-	THREE.DecoratedTorusKnot5a = DecoratedTorusKnot5a;
-	THREE.DecoratedTorusKnot5c = DecoratedTorusKnot5c;
-	THREE.FigureEightPolynomialKnot = FigureEightPolynomialKnot;
-	THREE.GrannyKnot = GrannyKnot;
-	THREE.HeartCurve = HeartCurve;
-	THREE.HelixCurve = HelixCurve;
-	THREE.KnotCurve = KnotCurve;
-	THREE.TorusKnot = TorusKnot;
-	THREE.TrefoilKnot = TrefoilKnot;
-	THREE.TrefoilPolynomialKnot = TrefoilPolynomialKnot;
-	THREE.VivianiCurve = VivianiCurve;
-
-} )();

+ 0 - 63
examples/js/curves/NURBSCurve.js

@@ -1,63 +0,0 @@
-( function () {
-
-	/**
- * NURBS curve object
- *
- * Derives from THREE.Curve, overriding getPoint and getTangent.
- *
- * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
- *
- **/
-
-	class NURBSCurve extends THREE.Curve {
-
-		constructor( degree, knots /* array of reals */, controlPoints /* array of Vector(2|3|4) */, startKnot /* index in knots */, endKnot /* index in knots */ ) {
-
-			super();
-			this.degree = degree;
-			this.knots = knots;
-			this.controlPoints = [];
-			// Used by periodic NURBS to remove hidden spans
-			this.startKnot = startKnot || 0;
-			this.endKnot = endKnot || this.knots.length - 1;
-			for ( let i = 0; i < controlPoints.length; ++ i ) {
-
-				// ensure THREE.Vector4 for control points
-				const point = controlPoints[ i ];
-				this.controlPoints[ i ] = new THREE.Vector4( point.x, point.y, point.z, point.w );
-
-			}
-
-		}
-		getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-			const point = optionalTarget;
-			const u = this.knots[ this.startKnot ] + t * ( this.knots[ this.endKnot ] - this.knots[ this.startKnot ] ); // linear mapping t->u
-
-			// following results in (wx, wy, wz, w) homogeneous point
-			const hpoint = THREE.NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u );
-			if ( hpoint.w !== 1.0 ) {
-
-				// project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1)
-				hpoint.divideScalar( hpoint.w );
-
-			}
-
-			return point.set( hpoint.x, hpoint.y, hpoint.z );
-
-		}
-		getTangent( t, optionalTarget = new THREE.Vector3() ) {
-
-			const tangent = optionalTarget;
-			const u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] );
-			const ders = THREE.NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 );
-			tangent.copy( ders[ 1 ] ).normalize();
-			return tangent;
-
-		}
-
-	}
-
-	THREE.NURBSCurve = NURBSCurve;
-
-} )();

+ 0 - 48
examples/js/curves/NURBSSurface.js

@@ -1,48 +0,0 @@
-( function () {
-
-	/**
- * NURBS surface object
- *
- * Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
- **/
-
-	class NURBSSurface {
-
-		constructor( degree1, degree2, knots1, knots2 /* arrays of reals */, controlPoints /* array^2 of Vector(2|3|4) */ ) {
-
-			this.degree1 = degree1;
-			this.degree2 = degree2;
-			this.knots1 = knots1;
-			this.knots2 = knots2;
-			this.controlPoints = [];
-			const len1 = knots1.length - degree1 - 1;
-			const len2 = knots2.length - degree2 - 1;
-
-			// ensure THREE.Vector4 for control points
-			for ( let i = 0; i < len1; ++ i ) {
-
-				this.controlPoints[ i ] = [];
-				for ( let j = 0; j < len2; ++ j ) {
-
-					const point = controlPoints[ i ][ j ];
-					this.controlPoints[ i ][ j ] = new THREE.Vector4( point.x, point.y, point.z, point.w );
-
-				}
-
-			}
-
-		}
-		getPoint( t1, t2, target ) {
-
-			const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
-			const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->u
-
-			THREE.NURBSUtils.calcSurfacePoint( this.degree1, this.degree2, this.knots1, this.knots2, this.controlPoints, u, v, target );
-
-		}
-
-	}
-
-	THREE.NURBSSurface = NURBSSurface;
-
-} )();

+ 0 - 439
examples/js/curves/NURBSUtils.js

@@ -1,439 +0,0 @@
-( function () {
-
-	/**
- * NURBS utils
- *
- * See NURBSCurve and NURBSSurface.
- **/
-
-	/**************************************************************
- *	NURBS Utils
- **************************************************************/
-
-	/*
-Finds knot vector span.
-
-p : degree
-u : parametric value
-U : knot vector
-
-returns the span
-*/
-	function findSpan( p, u, U ) {
-
-		const n = U.length - p - 1;
-		if ( u >= U[ n ] ) {
-
-			return n - 1;
-
-		}
-
-		if ( u <= U[ p ] ) {
-
-			return p;
-
-		}
-
-		let low = p;
-		let high = n;
-		let mid = Math.floor( ( low + high ) / 2 );
-		while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
-
-			if ( u < U[ mid ] ) {
-
-				high = mid;
-
-			} else {
-
-				low = mid;
-
-			}
-
-			mid = Math.floor( ( low + high ) / 2 );
-
-		}
-
-		return mid;
-
-	}
-
-	/*
-Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
-
-span : span in which u lies
-u    : parametric point
-p    : degree
-U    : knot vector
-
-returns array[p+1] with basis functions values.
-*/
-	function calcBasisFunctions( span, u, p, U ) {
-
-		const N = [];
-		const left = [];
-		const right = [];
-		N[ 0 ] = 1.0;
-		for ( let j = 1; j <= p; ++ j ) {
-
-			left[ j ] = u - U[ span + 1 - j ];
-			right[ j ] = U[ span + j ] - u;
-			let saved = 0.0;
-			for ( let r = 0; r < j; ++ r ) {
-
-				const rv = right[ r + 1 ];
-				const lv = left[ j - r ];
-				const temp = N[ r ] / ( rv + lv );
-				N[ r ] = saved + rv * temp;
-				saved = lv * temp;
-
-			}
-
-			N[ j ] = saved;
-
-		}
-
-		return N;
-
-	}
-
-	/*
-Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
-
-p : degree of B-Spline
-U : knot vector
-P : control points (x, y, z, w)
-u : parametric point
-
-returns point for given u
-*/
-	function calcBSplinePoint( p, U, P, u ) {
-
-		const span = findSpan( p, u, U );
-		const N = calcBasisFunctions( span, u, p, U );
-		const C = new THREE.Vector4( 0, 0, 0, 0 );
-		for ( let j = 0; j <= p; ++ j ) {
-
-			const point = P[ span - p + j ];
-			const Nj = N[ j ];
-			const wNj = point.w * Nj;
-			C.x += point.x * wNj;
-			C.y += point.y * wNj;
-			C.z += point.z * wNj;
-			C.w += point.w * Nj;
-
-		}
-
-		return C;
-
-	}
-
-	/*
-Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
-
-span : span in which u lies
-u    : parametric point
-p    : degree
-n    : number of derivatives to calculate
-U    : knot vector
-
-returns array[n+1][p+1] with basis functions derivatives
-*/
-	function calcBasisFunctionDerivatives( span, u, p, n, U ) {
-
-		const zeroArr = [];
-		for ( let i = 0; i <= p; ++ i ) zeroArr[ i ] = 0.0;
-		const ders = [];
-		for ( let i = 0; i <= n; ++ i ) ders[ i ] = zeroArr.slice( 0 );
-		const ndu = [];
-		for ( let i = 0; i <= p; ++ i ) ndu[ i ] = zeroArr.slice( 0 );
-		ndu[ 0 ][ 0 ] = 1.0;
-		const left = zeroArr.slice( 0 );
-		const right = zeroArr.slice( 0 );
-		for ( let j = 1; j <= p; ++ j ) {
-
-			left[ j ] = u - U[ span + 1 - j ];
-			right[ j ] = U[ span + j ] - u;
-			let saved = 0.0;
-			for ( let r = 0; r < j; ++ r ) {
-
-				const rv = right[ r + 1 ];
-				const lv = left[ j - r ];
-				ndu[ j ][ r ] = rv + lv;
-				const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
-				ndu[ r ][ j ] = saved + rv * temp;
-				saved = lv * temp;
-
-			}
-
-			ndu[ j ][ j ] = saved;
-
-		}
-
-		for ( let j = 0; j <= p; ++ j ) {
-
-			ders[ 0 ][ j ] = ndu[ j ][ p ];
-
-		}
-
-		for ( let r = 0; r <= p; ++ r ) {
-
-			let s1 = 0;
-			let s2 = 1;
-			const a = [];
-			for ( let i = 0; i <= p; ++ i ) {
-
-				a[ i ] = zeroArr.slice( 0 );
-
-			}
-
-			a[ 0 ][ 0 ] = 1.0;
-			for ( let k = 1; k <= n; ++ k ) {
-
-				let d = 0.0;
-				const rk = r - k;
-				const pk = p - k;
-				if ( r >= k ) {
-
-					a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
-					d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
-
-				}
-
-				const j1 = rk >= - 1 ? 1 : - rk;
-				const j2 = r - 1 <= pk ? k - 1 : p - r;
-				for ( let j = j1; j <= j2; ++ j ) {
-
-					a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
-					d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
-
-				}
-
-				if ( r <= pk ) {
-
-					a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
-					d += a[ s2 ][ k ] * ndu[ r ][ pk ];
-
-				}
-
-				ders[ k ][ r ] = d;
-				const j = s1;
-				s1 = s2;
-				s2 = j;
-
-			}
-
-		}
-
-		let r = p;
-		for ( let k = 1; k <= n; ++ k ) {
-
-			for ( let j = 0; j <= p; ++ j ) {
-
-				ders[ k ][ j ] *= r;
-
-			}
-
-			r *= p - k;
-
-		}
-
-		return ders;
-
-	}
-
-	/*
-	Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
-
-	p  : degree
-	U  : knot vector
-	P  : control points
-	u  : Parametric points
-	nd : number of derivatives
-
-	returns array[d+1] with derivatives
-	*/
-	function calcBSplineDerivatives( p, U, P, u, nd ) {
-
-		const du = nd < p ? nd : p;
-		const CK = [];
-		const span = findSpan( p, u, U );
-		const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
-		const Pw = [];
-		for ( let i = 0; i < P.length; ++ i ) {
-
-			const point = P[ i ].clone();
-			const w = point.w;
-			point.x *= w;
-			point.y *= w;
-			point.z *= w;
-			Pw[ i ] = point;
-
-		}
-
-		for ( let k = 0; k <= du; ++ k ) {
-
-			const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
-			for ( let j = 1; j <= p; ++ j ) {
-
-				point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
-
-			}
-
-			CK[ k ] = point;
-
-		}
-
-		for ( let k = du + 1; k <= nd + 1; ++ k ) {
-
-			CK[ k ] = new THREE.Vector4( 0, 0, 0 );
-
-		}
-
-		return CK;
-
-	}
-
-	/*
-Calculate "K over I"
-
-returns k!/(i!(k-i)!)
-*/
-	function calcKoverI( k, i ) {
-
-		let nom = 1;
-		for ( let j = 2; j <= k; ++ j ) {
-
-			nom *= j;
-
-		}
-
-		let denom = 1;
-		for ( let j = 2; j <= i; ++ j ) {
-
-			denom *= j;
-
-		}
-
-		for ( let j = 2; j <= k - i; ++ j ) {
-
-			denom *= j;
-
-		}
-
-		return nom / denom;
-
-	}
-
-	/*
-Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
-
-Pders : result of function calcBSplineDerivatives
-
-returns array with derivatives for rational curve.
-*/
-	function calcRationalCurveDerivatives( Pders ) {
-
-		const nd = Pders.length;
-		const Aders = [];
-		const wders = [];
-		for ( let i = 0; i < nd; ++ i ) {
-
-			const point = Pders[ i ];
-			Aders[ i ] = new THREE.Vector3( point.x, point.y, point.z );
-			wders[ i ] = point.w;
-
-		}
-
-		const CK = [];
-		for ( let k = 0; k < nd; ++ k ) {
-
-			const v = Aders[ k ].clone();
-			for ( let i = 1; i <= k; ++ i ) {
-
-				v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
-
-			}
-
-			CK[ k ] = v.divideScalar( wders[ 0 ] );
-
-		}
-
-		return CK;
-
-	}
-
-	/*
-Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
-
-p  : degree
-U  : knot vector
-P  : control points in homogeneous space
-u  : parametric points
-nd : number of derivatives
-
-returns array with derivatives.
-*/
-	function calcNURBSDerivatives( p, U, P, u, nd ) {
-
-		const Pders = calcBSplineDerivatives( p, U, P, u, nd );
-		return calcRationalCurveDerivatives( Pders );
-
-	}
-
-	/*
-Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
-
-p1, p2 : degrees of B-Spline surface
-U1, U2 : knot vectors
-P      : control points (x, y, z, w)
-u, v   : parametric values
-
-returns point for given (u, v)
-*/
-	function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
-
-		const uspan = findSpan( p, u, U );
-		const vspan = findSpan( q, v, V );
-		const Nu = calcBasisFunctions( uspan, u, p, U );
-		const Nv = calcBasisFunctions( vspan, v, q, V );
-		const temp = [];
-		for ( let l = 0; l <= q; ++ l ) {
-
-			temp[ l ] = new THREE.Vector4( 0, 0, 0, 0 );
-			for ( let k = 0; k <= p; ++ k ) {
-
-				const point = P[ uspan - p + k ][ vspan - q + l ].clone();
-				const w = point.w;
-				point.x *= w;
-				point.y *= w;
-				point.z *= w;
-				temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
-
-			}
-
-		}
-
-		const Sw = new THREE.Vector4( 0, 0, 0, 0 );
-		for ( let l = 0; l <= q; ++ l ) {
-
-			Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
-
-		}
-
-		Sw.divideScalar( Sw.w );
-		target.set( Sw.x, Sw.y, Sw.z );
-
-	}
-
-	THREE.NURBSUtils = {};
-	THREE.NURBSUtils.calcBSplineDerivatives = calcBSplineDerivatives;
-	THREE.NURBSUtils.calcBSplinePoint = calcBSplinePoint;
-	THREE.NURBSUtils.calcBasisFunctionDerivatives = calcBasisFunctionDerivatives;
-	THREE.NURBSUtils.calcBasisFunctions = calcBasisFunctions;
-	THREE.NURBSUtils.calcKoverI = calcKoverI;
-	THREE.NURBSUtils.calcNURBSDerivatives = calcNURBSDerivatives;
-	THREE.NURBSUtils.calcRationalCurveDerivatives = calcRationalCurveDerivatives;
-	THREE.NURBSUtils.calcSurfacePoint = calcSurfacePoint;
-	THREE.NURBSUtils.findSpan = findSpan;
-
-} )();

+ 0 - 86
examples/js/effects/AnaglyphEffect.js

@@ -1,86 +0,0 @@
-( function () {
-
-	class AnaglyphEffect {
-
-		constructor( renderer, width = 512, height = 512 ) {
-
-			// Dubois matrices from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.7.6968&rep=rep1&type=pdf#page=4
-
-			this.colorMatrixLeft = new THREE.Matrix3().fromArray( [ 0.456100, - 0.0400822, - 0.0152161, 0.500484, - 0.0378246, - 0.0205971, 0.176381, - 0.0157589, - 0.00546856 ] );
-			this.colorMatrixRight = new THREE.Matrix3().fromArray( [ - 0.0434706, 0.378476, - 0.0721527, - 0.0879388, 0.73364, - 0.112961, - 0.00155529, - 0.0184503, 1.2264 ] );
-			const _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
-			const _scene = new THREE.Scene();
-			const _stereo = new THREE.StereoCamera();
-			const _params = {
-				minFilter: THREE.LinearFilter,
-				magFilter: THREE.NearestFilter,
-				format: THREE.RGBAFormat
-			};
-			const _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params );
-			const _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params );
-			const _material = new THREE.ShaderMaterial( {
-				uniforms: {
-					'mapLeft': {
-						value: _renderTargetL.texture
-					},
-					'mapRight': {
-						value: _renderTargetR.texture
-					},
-					'colorMatrixLeft': {
-						value: this.colorMatrixLeft
-					},
-					'colorMatrixRight': {
-						value: this.colorMatrixRight
-					}
-				},
-				vertexShader: [ 'varying vec2 vUv;', 'void main() {', '	vUv = vec2( uv.x, uv.y );', '	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}' ].join( '\n' ),
-				fragmentShader: [ 'uniform sampler2D mapLeft;', 'uniform sampler2D mapRight;', 'varying vec2 vUv;', 'uniform mat3 colorMatrixLeft;', 'uniform mat3 colorMatrixRight;',
-					// These functions implement sRGB linearization and gamma correction
-
-					'float lin( float c ) {', '	return c <= 0.04045 ? c * 0.0773993808 :', '			pow( c * 0.9478672986 + 0.0521327014, 2.4 );', '}', 'vec4 lin( vec4 c ) {', '	return vec4( lin( c.r ), lin( c.g ), lin( c.b ), c.a );', '}', 'float dev( float c ) {', '	return c <= 0.0031308 ? c * 12.92', '			: pow( c, 0.41666 ) * 1.055 - 0.055;', '}', 'void main() {', '	vec2 uv = vUv;', '	vec4 colorL = lin( texture2D( mapLeft, uv ) );', '	vec4 colorR = lin( texture2D( mapRight, uv ) );', '	vec3 color = clamp(', '			colorMatrixLeft * colorL.rgb +', '			colorMatrixRight * colorR.rgb, 0., 1. );', '	gl_FragColor = vec4(', '			dev( color.r ), dev( color.g ), dev( color.b ),', '			max( colorL.a, colorR.a ) );', '}' ].join( '\n' )
-			} );
-			const _mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material );
-			_scene.add( _mesh );
-			this.setSize = function ( width, height ) {
-
-				renderer.setSize( width, height );
-				const pixelRatio = renderer.getPixelRatio();
-				_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
-				_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
-
-			};
-
-			this.render = function ( scene, camera ) {
-
-				const currentRenderTarget = renderer.getRenderTarget();
-				if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
-				if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
-				_stereo.update( camera );
-				renderer.setRenderTarget( _renderTargetL );
-				renderer.clear();
-				renderer.render( scene, _stereo.cameraL );
-				renderer.setRenderTarget( _renderTargetR );
-				renderer.clear();
-				renderer.render( scene, _stereo.cameraR );
-				renderer.setRenderTarget( null );
-				renderer.render( _scene, _camera );
-				renderer.setRenderTarget( currentRenderTarget );
-
-			};
-
-			this.dispose = function () {
-
-				_renderTargetL.dispose();
-				_renderTargetR.dispose();
-				_mesh.geometry.dispose();
-				_mesh.material.dispose();
-
-			};
-
-		}
-
-	}
-
-	THREE.AnaglyphEffect = AnaglyphEffect;
-
-} )();

+ 0 - 260
examples/js/effects/AsciiEffect.js

@@ -1,260 +0,0 @@
-( function () {
-
-	/**
- * Ascii generation is based on https://github.com/hassadee/jsascii/blob/master/jsascii.js
- *
- * 16 April 2012 - @blurspline
- */
-
-	class AsciiEffect {
-
-		constructor( renderer, charSet = ' .:-=+*#%@', options = {} ) {
-
-			// ' .,:;=|iI+hHOE#`$';
-			// darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
-			// ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');
-
-			// Some ASCII settings
-
-			const fResolution = options[ 'resolution' ] || 0.15; // Higher for more details
-			const iScale = options[ 'scale' ] || 1;
-			const bColor = options[ 'color' ] || false; // nice but slows down rendering!
-			const bAlpha = options[ 'alpha' ] || false; // Transparency
-			const bBlock = options[ 'block' ] || false; // blocked characters. like good O dos
-			const bInvert = options[ 'invert' ] || false; // black is white, white is black
-			const strResolution = options[ 'strResolution' ] || 'low';
-			let width, height;
-			const domElement = document.createElement( 'div' );
-			domElement.style.cursor = 'default';
-			const oAscii = document.createElement( 'table' );
-			domElement.appendChild( oAscii );
-			let iWidth, iHeight;
-			let oImg;
-			this.setSize = function ( w, h ) {
-
-				width = w;
-				height = h;
-				renderer.setSize( w, h );
-				initAsciiSize();
-
-			};
-
-			this.render = function ( scene, camera ) {
-
-				renderer.render( scene, camera );
-				asciifyImage( oAscii );
-
-			};
-
-			this.domElement = domElement;
-
-			// Throw in ascii library from https://github.com/hassadee/jsascii/blob/master/jsascii.js (MIT License)
-
-			function initAsciiSize() {
-
-				iWidth = Math.round( width * fResolution );
-				iHeight = Math.round( height * fResolution );
-				oCanvas.width = iWidth;
-				oCanvas.height = iHeight;
-				// oCanvas.style.display = "none";
-				// oCanvas.style.width = iWidth;
-				// oCanvas.style.height = iHeight;
-
-				oImg = renderer.domElement;
-				if ( oImg.style.backgroundColor ) {
-
-					oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
-					oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;
-
-				}
-
-				oAscii.cellSpacing = 0;
-				oAscii.cellPadding = 0;
-				const oStyle = oAscii.style;
-				oStyle.display = 'inline';
-				oStyle.width = Math.round( iWidth / fResolution * iScale ) + 'px';
-				oStyle.height = Math.round( iHeight / fResolution * iScale ) + 'px';
-				oStyle.whiteSpace = 'pre';
-				oStyle.margin = '0px';
-				oStyle.padding = '0px';
-				oStyle.letterSpacing = fLetterSpacing + 'px';
-				oStyle.fontFamily = strFont;
-				oStyle.fontSize = fFontSize + 'px';
-				oStyle.lineHeight = fLineHeight + 'px';
-				oStyle.textAlign = 'left';
-				oStyle.textDecoration = 'none';
-
-			}
-
-			const aDefaultCharList = ' .,:;i1tfLCG08@'.split( '' );
-			const aDefaultColorCharList = ' CGO08@'.split( '' );
-			const strFont = 'courier new, monospace';
-			const oCanvasImg = renderer.domElement;
-			const oCanvas = document.createElement( 'canvas' );
-			if ( ! oCanvas.getContext ) {
-
-				return;
-
-			}
-
-			const oCtx = oCanvas.getContext( '2d' );
-			if ( ! oCtx.getImageData ) {
-
-				return;
-
-			}
-
-			let aCharList = bColor ? aDefaultColorCharList : aDefaultCharList;
-			if ( charSet ) aCharList = charSet;
-
-			// Setup dom
-
-			const fFontSize = 2 / fResolution * iScale;
-			const fLineHeight = 2 / fResolution * iScale;
-
-			// adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.
-
-			let fLetterSpacing = 0;
-			if ( strResolution == 'low' ) {
-
-				switch ( iScale ) {
-
-					case 1:
-						fLetterSpacing = - 1;
-						break;
-					case 2:
-					case 3:
-						fLetterSpacing = - 2.1;
-						break;
-					case 4:
-						fLetterSpacing = - 3.1;
-						break;
-					case 5:
-						fLetterSpacing = - 4.15;
-						break;
-
-				}
-
-			}
-
-			if ( strResolution == 'medium' ) {
-
-				switch ( iScale ) {
-
-					case 1:
-						fLetterSpacing = 0;
-						break;
-					case 2:
-						fLetterSpacing = - 1;
-						break;
-					case 3:
-						fLetterSpacing = - 1.04;
-						break;
-					case 4:
-					case 5:
-						fLetterSpacing = - 2.1;
-						break;
-
-				}
-
-			}
-
-			if ( strResolution == 'high' ) {
-
-				switch ( iScale ) {
-
-					case 1:
-					case 2:
-						fLetterSpacing = 0;
-						break;
-					case 3:
-					case 4:
-					case 5:
-						fLetterSpacing = - 1;
-						break;
-
-				}
-
-			}
-
-			// can't get a span or div to flow like an img element, but a table works?
-
-			// convert img element to ascii
-
-			function asciifyImage( oAscii ) {
-
-				oCtx.clearRect( 0, 0, iWidth, iHeight );
-				oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight );
-				const oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
-
-				// Coloring loop starts now
-				let strChars = '';
-
-				// console.time('rendering');
-
-				for ( let y = 0; y < iHeight; y += 2 ) {
-
-					for ( let x = 0; x < iWidth; x ++ ) {
-
-						const iOffset = ( y * iWidth + x ) * 4;
-						const iRed = oImgData[ iOffset ];
-						const iGreen = oImgData[ iOffset + 1 ];
-						const iBlue = oImgData[ iOffset + 2 ];
-						const iAlpha = oImgData[ iOffset + 3 ];
-						let iCharIdx;
-						let fBrightness;
-						fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
-						// fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;
-
-						if ( iAlpha == 0 ) {
-
-							// should calculate alpha instead, but quick hack :)
-							//fBrightness *= (iAlpha / 255);
-							fBrightness = 1;
-
-						}
-
-						iCharIdx = Math.floor( ( 1 - fBrightness ) * ( aCharList.length - 1 ) );
-						if ( bInvert ) {
-
-							iCharIdx = aCharList.length - iCharIdx - 1;
-
-						}
-
-						// good for debugging
-						//fBrightness = Math.floor(fBrightness * 10);
-						//strThisChar = fBrightness;
-
-						let strThisChar = aCharList[ iCharIdx ];
-						if ( strThisChar === undefined || strThisChar == ' ' ) strThisChar = '&nbsp;';
-						if ( bColor ) {
-
-							strChars += '<span style=\'' + 'color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' + ( bBlock ? 'background-color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' : '' ) + ( bAlpha ? 'opacity:' + iAlpha / 255 + ';' : '' ) + '\'>' + strThisChar + '</span>';
-
-						} else {
-
-							strChars += strThisChar;
-
-						}
-
-					}
-
-					strChars += '<br/>';
-
-				}
-
-				oAscii.innerHTML = '<tr><td>' + strChars + '</td></tr>';
-
-				// console.timeEnd('rendering');
-
-				// return oAscii;
-
-			}
-
-		}
-
-	}
-
-	THREE.AsciiEffect = AsciiEffect;
-
-} )();

+ 0 - 450
examples/js/effects/OutlineEffect.js

@@ -1,450 +0,0 @@
-( function () {
-
-	/**
- * Reference: https://en.wikipedia.org/wiki/Cel_shading
- *
- * API
- *
- * 1. Traditional
- *
- * const effect = new OutlineEffect( renderer );
- *
- * function render() {
- *
- * 	effect.render( scene, camera );
- *
- * }
- *
- * 2. VR compatible
- *
- * const effect = new OutlineEffect( renderer );
- * let renderingOutline = false;
- *
- * scene.onAfterRender = function () {
- *
- * 	if ( renderingOutline ) return;
- *
- * 	renderingOutline = true;
- *
- * 	effect.renderOutline( scene, camera );
- *
- * 	renderingOutline = false;
- *
- * };
- *
- * function render() {
- *
- * 	renderer.render( scene, camera );
- *
- * }
- *
- * // How to set default outline parameters
- * new OutlineEffect( renderer, {
- * 	defaultThickness: 0.01,
- * 	defaultColor: [ 0, 0, 0 ],
- * 	defaultAlpha: 0.8,
- * 	defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
- * } );
- *
- * // How to set outline parameters for each material
- * material.userData.outlineParameters = {
- * 	thickness: 0.01,
- * 	color: [ 0, 0, 0 ],
- * 	alpha: 0.8,
- * 	visible: true,
- * 	keepAlive: true
- * };
- */
-
-	class OutlineEffect {
-
-		constructor( renderer, parameters = {} ) {
-
-			this.enabled = true;
-			const defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
-			const defaultColor = new THREE.Color().fromArray( parameters.defaultColor !== undefined ? parameters.defaultColor : [ 0, 0, 0 ] );
-			const defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
-			const defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
-
-			// object.material.uuid -> outlineMaterial or
-			// object.material[ n ].uuid -> outlineMaterial
-			// save at the outline material creation and release
-			// if it's unused removeThresholdCount frames
-			// unless keepAlive is true.
-			const cache = {};
-			const removeThresholdCount = 60;
-
-			// outlineMaterial.uuid -> object.material or
-			// outlineMaterial.uuid -> object.material[ n ]
-			// save before render and release after render.
-			const originalMaterials = {};
-
-			// object.uuid -> originalOnBeforeRender
-			// save before render and release after render.
-			const originalOnBeforeRenders = {};
-
-			//this.cache = cache;  // for debug
-
-			const uniformsOutline = {
-				outlineThickness: {
-					value: defaultThickness
-				},
-				outlineColor: {
-					value: defaultColor
-				},
-				outlineAlpha: {
-					value: defaultAlpha
-				}
-			};
-			const vertexShader = [ '#include <common>', '#include <uv_pars_vertex>', '#include <displacementmap_pars_vertex>', '#include <fog_pars_vertex>', '#include <morphtarget_pars_vertex>', '#include <skinning_pars_vertex>', '#include <logdepthbuf_pars_vertex>', '#include <clipping_planes_pars_vertex>', 'uniform float outlineThickness;', 'vec4 calculateOutline( vec4 pos, vec3 normal, vec4 skinned ) {', '	float thickness = outlineThickness;', '	const float ratio = 1.0;',
-				// TODO: support outline thickness ratio for each vertex
-				'	vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + normal, 1.0 );',
-				// NOTE: subtract pos2 from pos because THREE.BackSide objectNormal is negative
-				'	vec4 norm = normalize( pos - pos2 );', '	return pos + norm * thickness * pos.w * ratio;', '}', 'void main() {', '	#include <uv_vertex>', '	#include <beginnormal_vertex>', '	#include <morphnormal_vertex>', '	#include <skinbase_vertex>', '	#include <skinnormal_vertex>', '	#include <begin_vertex>', '	#include <morphtarget_vertex>', '	#include <skinning_vertex>', '	#include <displacementmap_vertex>', '	#include <project_vertex>', '	vec3 outlineNormal = - objectNormal;',
-				// the outline material is always rendered with THREE.BackSide
-
-				'	gl_Position = calculateOutline( gl_Position, outlineNormal, vec4( transformed, 1.0 ) );', '	#include <logdepthbuf_vertex>', '	#include <clipping_planes_vertex>', '	#include <fog_vertex>', '}' ].join( '\n' );
-			const fragmentShader = [ '#include <common>', '#include <fog_pars_fragment>', '#include <logdepthbuf_pars_fragment>', '#include <clipping_planes_pars_fragment>', 'uniform vec3 outlineColor;', 'uniform float outlineAlpha;', 'void main() {', '	#include <clipping_planes_fragment>', '	#include <logdepthbuf_fragment>', '	gl_FragColor = vec4( outlineColor, outlineAlpha );', '	#include <tonemapping_fragment>', '	#include <encodings_fragment>', '	#include <fog_fragment>', '	#include <premultiplied_alpha_fragment>', '}' ].join( '\n' );
-			function createMaterial() {
-
-				return new THREE.ShaderMaterial( {
-					type: 'OutlineEffect',
-					uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ 'fog' ], THREE.UniformsLib[ 'displacementmap' ], uniformsOutline ] ),
-					vertexShader: vertexShader,
-					fragmentShader: fragmentShader,
-					side: THREE.BackSide
-				} );
-
-			}
-
-			function getOutlineMaterialFromCache( originalMaterial ) {
-
-				let data = cache[ originalMaterial.uuid ];
-				if ( data === undefined ) {
-
-					data = {
-						material: createMaterial(),
-						used: true,
-						keepAlive: defaultKeepAlive,
-						count: 0
-					};
-					cache[ originalMaterial.uuid ] = data;
-
-				}
-
-				data.used = true;
-				return data.material;
-
-			}
-
-			function getOutlineMaterial( originalMaterial ) {
-
-				const outlineMaterial = getOutlineMaterialFromCache( originalMaterial );
-				originalMaterials[ outlineMaterial.uuid ] = originalMaterial;
-				updateOutlineMaterial( outlineMaterial, originalMaterial );
-				return outlineMaterial;
-
-			}
-
-			function isCompatible( object ) {
-
-				const geometry = object.geometry;
-				let hasNormals = false;
-				if ( object.geometry !== undefined ) {
-
-					if ( geometry.isBufferGeometry ) {
-
-						hasNormals = geometry.attributes.normal !== undefined;
-
-					} else {
-
-						hasNormals = true; // the renderer always produces a normal attribute for Geometry
-
-					}
-
-				}
-
-				return object.isMesh === true && object.material !== undefined && hasNormals === true;
-
-			}
-
-			function setOutlineMaterial( object ) {
-
-				if ( isCompatible( object ) === false ) return;
-				if ( Array.isArray( object.material ) ) {
-
-					for ( let i = 0, il = object.material.length; i < il; i ++ ) {
-
-						object.material[ i ] = getOutlineMaterial( object.material[ i ] );
-
-					}
-
-				} else {
-
-					object.material = getOutlineMaterial( object.material );
-
-				}
-
-				originalOnBeforeRenders[ object.uuid ] = object.onBeforeRender;
-				object.onBeforeRender = onBeforeRender;
-
-			}
-
-			function restoreOriginalMaterial( object ) {
-
-				if ( isCompatible( object ) === false ) return;
-				if ( Array.isArray( object.material ) ) {
-
-					for ( let i = 0, il = object.material.length; i < il; i ++ ) {
-
-						object.material[ i ] = originalMaterials[ object.material[ i ].uuid ];
-
-					}
-
-				} else {
-
-					object.material = originalMaterials[ object.material.uuid ];
-
-				}
-
-				object.onBeforeRender = originalOnBeforeRenders[ object.uuid ];
-
-			}
-
-			function onBeforeRender( renderer, scene, camera, geometry, material ) {
-
-				const originalMaterial = originalMaterials[ material.uuid ];
-
-				// just in case
-				if ( originalMaterial === undefined ) return;
-				updateUniforms( material, originalMaterial );
-
-			}
-
-			function updateUniforms( material, originalMaterial ) {
-
-				const outlineParameters = originalMaterial.userData.outlineParameters;
-				material.uniforms.outlineAlpha.value = originalMaterial.opacity;
-				if ( outlineParameters !== undefined ) {
-
-					if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
-					if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.fromArray( outlineParameters.color );
-					if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
-
-				}
-
-				if ( originalMaterial.displacementMap ) {
-
-					material.uniforms.displacementMap.value = originalMaterial.displacementMap;
-					material.uniforms.displacementScale.value = originalMaterial.displacementScale;
-					material.uniforms.displacementBias.value = originalMaterial.displacementBias;
-
-				}
-
-			}
-
-			function updateOutlineMaterial( material, originalMaterial ) {
-
-				if ( material.name === 'invisible' ) return;
-				const outlineParameters = originalMaterial.userData.outlineParameters;
-				material.fog = originalMaterial.fog;
-				material.toneMapped = originalMaterial.toneMapped;
-				material.premultipliedAlpha = originalMaterial.premultipliedAlpha;
-				material.displacementMap = originalMaterial.displacementMap;
-				if ( outlineParameters !== undefined ) {
-
-					if ( originalMaterial.visible === false ) {
-
-						material.visible = false;
-
-					} else {
-
-						material.visible = outlineParameters.visible !== undefined ? outlineParameters.visible : true;
-
-					}
-
-					material.transparent = outlineParameters.alpha !== undefined && outlineParameters.alpha < 1.0 ? true : originalMaterial.transparent;
-					if ( outlineParameters.keepAlive !== undefined ) cache[ originalMaterial.uuid ].keepAlive = outlineParameters.keepAlive;
-
-				} else {
-
-					material.transparent = originalMaterial.transparent;
-					material.visible = originalMaterial.visible;
-
-				}
-
-				if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false;
-				if ( originalMaterial.clippingPlanes ) {
-
-					material.clipping = true;
-					material.clippingPlanes = originalMaterial.clippingPlanes;
-					material.clipIntersection = originalMaterial.clipIntersection;
-					material.clipShadows = originalMaterial.clipShadows;
-
-				}
-
-				material.version = originalMaterial.version; // update outline material if necessary
-
-			}
-
-			function cleanupCache() {
-
-				let keys;
-
-				// clear originialMaterials
-				keys = Object.keys( originalMaterials );
-				for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-					originalMaterials[ keys[ i ] ] = undefined;
-
-				}
-
-				// clear originalOnBeforeRenders
-				keys = Object.keys( originalOnBeforeRenders );
-				for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-					originalOnBeforeRenders[ keys[ i ] ] = undefined;
-
-				}
-
-				// remove unused outlineMaterial from cache
-				keys = Object.keys( cache );
-				for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-					const key = keys[ i ];
-					if ( cache[ key ].used === false ) {
-
-						cache[ key ].count ++;
-						if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
-
-							delete cache[ key ];
-
-						}
-
-					} else {
-
-						cache[ key ].used = false;
-						cache[ key ].count = 0;
-
-					}
-
-				}
-
-			}
-
-			this.render = function ( scene, camera ) {
-
-				if ( this.enabled === false ) {
-
-					renderer.render( scene, camera );
-					return;
-
-				}
-
-				const currentAutoClear = renderer.autoClear;
-				renderer.autoClear = this.autoClear;
-				renderer.render( scene, camera );
-				renderer.autoClear = currentAutoClear;
-				this.renderOutline( scene, camera );
-
-			};
-
-			this.renderOutline = function ( scene, camera ) {
-
-				const currentAutoClear = renderer.autoClear;
-				const currentSceneAutoUpdate = scene.matrixWorldAutoUpdate;
-				const currentSceneBackground = scene.background;
-				const currentShadowMapEnabled = renderer.shadowMap.enabled;
-				scene.matrixWorldAutoUpdate = false;
-				scene.background = null;
-				renderer.autoClear = false;
-				renderer.shadowMap.enabled = false;
-				scene.traverse( setOutlineMaterial );
-				renderer.render( scene, camera );
-				scene.traverse( restoreOriginalMaterial );
-				cleanupCache();
-				scene.matrixWorldAutoUpdate = currentSceneAutoUpdate;
-				scene.background = currentSceneBackground;
-				renderer.autoClear = currentAutoClear;
-				renderer.shadowMap.enabled = currentShadowMapEnabled;
-
-			};
-
-			/*
-     * See #9918
-     *
-     * The following property copies and wrapper methods enable
-     * OutlineEffect to be called from other *Effect, like
-     *
-     * effect = new StereoEffect( new OutlineEffect( renderer ) );
-     *
-     * function render () {
-     *
-    	 * 	effect.render( scene, camera );
-     *
-     * }
-     */
-			this.autoClear = renderer.autoClear;
-			this.domElement = renderer.domElement;
-			this.shadowMap = renderer.shadowMap;
-			this.clear = function ( color, depth, stencil ) {
-
-				renderer.clear( color, depth, stencil );
-
-			};
-
-			this.getPixelRatio = function () {
-
-				return renderer.getPixelRatio();
-
-			};
-
-			this.setPixelRatio = function ( value ) {
-
-				renderer.setPixelRatio( value );
-
-			};
-
-			this.getSize = function ( target ) {
-
-				return renderer.getSize( target );
-
-			};
-
-			this.setSize = function ( width, height, updateStyle ) {
-
-				renderer.setSize( width, height, updateStyle );
-
-			};
-
-			this.setViewport = function ( x, y, width, height ) {
-
-				renderer.setViewport( x, y, width, height );
-
-			};
-
-			this.setScissor = function ( x, y, width, height ) {
-
-				renderer.setScissor( x, y, width, height );
-
-			};
-
-			this.setScissorTest = function ( boolean ) {
-
-				renderer.setScissorTest( boolean );
-
-			};
-
-			this.setRenderTarget = function ( renderTarget ) {
-
-				renderer.setRenderTarget( renderTarget );
-
-			};
-
-		}
-
-	}
-
-	THREE.OutlineEffect = OutlineEffect;
-
-} )();

+ 0 - 62
examples/js/effects/ParallaxBarrierEffect.js

@@ -1,62 +0,0 @@
-( function () {
-
-	class ParallaxBarrierEffect {
-
-		constructor( renderer ) {
-
-			const _camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
-			const _scene = new THREE.Scene();
-			const _stereo = new THREE.StereoCamera();
-			const _params = {
-				minFilter: THREE.LinearFilter,
-				magFilter: THREE.NearestFilter,
-				format: THREE.RGBAFormat
-			};
-			const _renderTargetL = new THREE.WebGLRenderTarget( 512, 512, _params );
-			const _renderTargetR = new THREE.WebGLRenderTarget( 512, 512, _params );
-			const _material = new THREE.ShaderMaterial( {
-				uniforms: {
-					'mapLeft': {
-						value: _renderTargetL.texture
-					},
-					'mapRight': {
-						value: _renderTargetR.texture
-					}
-				},
-				vertexShader: [ 'varying vec2 vUv;', 'void main() {', '	vUv = vec2( uv.x, uv.y );', '	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}' ].join( '\n' ),
-				fragmentShader: [ 'uniform sampler2D mapLeft;', 'uniform sampler2D mapRight;', 'varying vec2 vUv;', 'void main() {', '	vec2 uv = vUv;', '	if ( ( mod( gl_FragCoord.y, 2.0 ) ) > 1.00 ) {', '		gl_FragColor = texture2D( mapLeft, uv );', '	} else {', '		gl_FragColor = texture2D( mapRight, uv );', '	}', '}' ].join( '\n' )
-			} );
-			const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material );
-			_scene.add( mesh );
-			this.setSize = function ( width, height ) {
-
-				renderer.setSize( width, height );
-				const pixelRatio = renderer.getPixelRatio();
-				_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
-				_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
-
-			};
-
-			this.render = function ( scene, camera ) {
-
-				if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
-				if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
-				_stereo.update( camera );
-				renderer.setRenderTarget( _renderTargetL );
-				renderer.clear();
-				renderer.render( scene, _stereo.cameraL );
-				renderer.setRenderTarget( _renderTargetR );
-				renderer.clear();
-				renderer.render( scene, _stereo.cameraR );
-				renderer.setRenderTarget( null );
-				renderer.render( _scene, _camera );
-
-			};
-
-		}
-
-	}
-
-	THREE.ParallaxBarrierEffect = ParallaxBarrierEffect;
-
-} )();

+ 0 - 139
examples/js/effects/PeppersGhostEffect.js

@@ -1,139 +0,0 @@
-( function () {
-
-	/**
- * peppers ghost effect based on http://www.instructables.com/id/Reflective-Prism/?ALLSTEPS
- */
-
-	class PeppersGhostEffect {
-
-		constructor( renderer ) {
-
-			const scope = this;
-			scope.cameraDistance = 15;
-			scope.reflectFromAbove = false;
-
-			// Internals
-			let _halfWidth, _width, _height;
-			const _cameraF = new THREE.PerspectiveCamera(); //front
-			const _cameraB = new THREE.PerspectiveCamera(); //back
-			const _cameraL = new THREE.PerspectiveCamera(); //left
-			const _cameraR = new THREE.PerspectiveCamera(); //right
-
-			const _position = new THREE.Vector3();
-			const _quaternion = new THREE.Quaternion();
-			const _scale = new THREE.Vector3();
-
-			// Initialization
-			renderer.autoClear = false;
-			this.setSize = function ( width, height ) {
-
-				_halfWidth = width / 2;
-				if ( width < height ) {
-
-					_width = width / 3;
-					_height = width / 3;
-
-				} else {
-
-					_width = height / 3;
-					_height = height / 3;
-
-				}
-
-				renderer.setSize( width, height );
-
-			};
-
-			this.render = function ( scene, camera ) {
-
-				if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
-				if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
-				camera.matrixWorld.decompose( _position, _quaternion, _scale );
-
-				// front
-				_cameraF.position.copy( _position );
-				_cameraF.quaternion.copy( _quaternion );
-				_cameraF.translateZ( scope.cameraDistance );
-				_cameraF.lookAt( scene.position );
-
-				// back
-				_cameraB.position.copy( _position );
-				_cameraB.quaternion.copy( _quaternion );
-				_cameraB.translateZ( - scope.cameraDistance );
-				_cameraB.lookAt( scene.position );
-				_cameraB.rotation.z += 180 * ( Math.PI / 180 );
-
-				// left
-				_cameraL.position.copy( _position );
-				_cameraL.quaternion.copy( _quaternion );
-				_cameraL.translateX( - scope.cameraDistance );
-				_cameraL.lookAt( scene.position );
-				_cameraL.rotation.x += 90 * ( Math.PI / 180 );
-
-				// right
-				_cameraR.position.copy( _position );
-				_cameraR.quaternion.copy( _quaternion );
-				_cameraR.translateX( scope.cameraDistance );
-				_cameraR.lookAt( scene.position );
-				_cameraR.rotation.x += 90 * ( Math.PI / 180 );
-				renderer.clear();
-				renderer.setScissorTest( true );
-				renderer.setScissor( _halfWidth - _width / 2, _height * 2, _width, _height );
-				renderer.setViewport( _halfWidth - _width / 2, _height * 2, _width, _height );
-				if ( scope.reflectFromAbove ) {
-
-					renderer.render( scene, _cameraB );
-
-				} else {
-
-					renderer.render( scene, _cameraF );
-
-				}
-
-				renderer.setScissor( _halfWidth - _width / 2, 0, _width, _height );
-				renderer.setViewport( _halfWidth - _width / 2, 0, _width, _height );
-				if ( scope.reflectFromAbove ) {
-
-					renderer.render( scene, _cameraF );
-
-				} else {
-
-					renderer.render( scene, _cameraB );
-
-				}
-
-				renderer.setScissor( _halfWidth - _width / 2 - _width, _height, _width, _height );
-				renderer.setViewport( _halfWidth - _width / 2 - _width, _height, _width, _height );
-				if ( scope.reflectFromAbove ) {
-
-					renderer.render( scene, _cameraR );
-
-				} else {
-
-					renderer.render( scene, _cameraL );
-
-				}
-
-				renderer.setScissor( _halfWidth + _width / 2, _height, _width, _height );
-				renderer.setViewport( _halfWidth + _width / 2, _height, _width, _height );
-				if ( scope.reflectFromAbove ) {
-
-					renderer.render( scene, _cameraL );
-
-				} else {
-
-					renderer.render( scene, _cameraR );
-
-				}
-
-				renderer.setScissorTest( false );
-
-			};
-
-		}
-
-	}
-
-	THREE.PeppersGhostEffect = PeppersGhostEffect;
-
-} )();

+ 0 - 46
examples/js/effects/StereoEffect.js

@@ -1,46 +0,0 @@
-( function () {
-
-	class StereoEffect {
-
-		constructor( renderer ) {
-
-			const _stereo = new THREE.StereoCamera();
-			_stereo.aspect = 0.5;
-			const size = new THREE.Vector2();
-			this.setEyeSeparation = function ( eyeSep ) {
-
-				_stereo.eyeSep = eyeSep;
-
-			};
-
-			this.setSize = function ( width, height ) {
-
-				renderer.setSize( width, height );
-
-			};
-
-			this.render = function ( scene, camera ) {
-
-				if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
-				if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
-				_stereo.update( camera );
-				renderer.getSize( size );
-				if ( renderer.autoClear ) renderer.clear();
-				renderer.setScissorTest( true );
-				renderer.setScissor( 0, 0, size.width / 2, size.height );
-				renderer.setViewport( 0, 0, size.width / 2, size.height );
-				renderer.render( scene, _stereo.cameraL );
-				renderer.setScissor( size.width / 2, 0, size.width / 2, size.height );
-				renderer.setViewport( size.width / 2, 0, size.width / 2, size.height );
-				renderer.render( scene, _stereo.cameraR );
-				renderer.setScissorTest( false );
-
-			};
-
-		}
-
-	}
-
-	THREE.StereoEffect = StereoEffect;
-
-} )();

+ 0 - 53
examples/js/environments/DebugEnvironment.js

@@ -1,53 +0,0 @@
-( function () {
-
-	class DebugEnvironment extends THREE.Scene {
-
-		constructor() {
-
-			super();
-			const geometry = new THREE.BoxGeometry();
-			geometry.deleteAttribute( 'uv' );
-			const roomMaterial = new THREE.MeshStandardMaterial( {
-				metalness: 0,
-				side: THREE.BackSide
-			} );
-			const room = new THREE.Mesh( geometry, roomMaterial );
-			room.scale.setScalar( 10 );
-			this.add( room );
-			const mainLight = new THREE.PointLight( 0xffffff, 50, 0, 2 );
-			this.add( mainLight );
-			const material1 = new THREE.MeshLambertMaterial( {
-				color: 0xff0000,
-				emissive: 0xffffff,
-				emissiveIntensity: 10
-			} );
-			const light1 = new THREE.Mesh( geometry, material1 );
-			light1.position.set( - 5, 2, 0 );
-			light1.scale.set( 0.1, 1, 1 );
-			this.add( light1 );
-			const material2 = new THREE.MeshLambertMaterial( {
-				color: 0x00ff00,
-				emissive: 0xffffff,
-				emissiveIntensity: 10
-			} );
-			const light2 = new THREE.Mesh( geometry, material2 );
-			light2.position.set( 0, 5, 0 );
-			light2.scale.set( 1, 0.1, 1 );
-			this.add( light2 );
-			const material3 = new THREE.MeshLambertMaterial( {
-				color: 0x0000ff,
-				emissive: 0xffffff,
-				emissiveIntensity: 10
-			} );
-			const light3 = new THREE.Mesh( geometry, material3 );
-			light3.position.set( 2, 1, 5 );
-			light3.scale.set( 1.5, 2, 0.1 );
-			this.add( light3 );
-
-		}
-
-	}
-
-	THREE.DebugEnvironment = DebugEnvironment;
-
-} )();

+ 0 - 124
examples/js/environments/RoomEnvironment.js

@@ -1,124 +0,0 @@
-( function () {
-
-	/**
- * https://github.com/google/model-viewer/blob/master/packages/model-viewer/src/three-components/EnvironmentScene.ts
- */
-	class RoomEnvironment extends THREE.Scene {
-
-		constructor() {
-
-			super();
-			const geometry = new THREE.BoxGeometry();
-			geometry.deleteAttribute( 'uv' );
-			const roomMaterial = new THREE.MeshStandardMaterial( {
-				side: THREE.BackSide
-			} );
-			const boxMaterial = new THREE.MeshStandardMaterial();
-			const mainLight = new THREE.PointLight( 0xffffff, 5.0, 28, 2 );
-			mainLight.position.set( 0.418, 16.199, 0.300 );
-			this.add( mainLight );
-			const room = new THREE.Mesh( geometry, roomMaterial );
-			room.position.set( - 0.757, 13.219, 0.717 );
-			room.scale.set( 31.713, 28.305, 28.591 );
-			this.add( room );
-			const box1 = new THREE.Mesh( geometry, boxMaterial );
-			box1.position.set( - 10.906, 2.009, 1.846 );
-			box1.rotation.set( 0, - 0.195, 0 );
-			box1.scale.set( 2.328, 7.905, 4.651 );
-			this.add( box1 );
-			const box2 = new THREE.Mesh( geometry, boxMaterial );
-			box2.position.set( - 5.607, - 0.754, - 0.758 );
-			box2.rotation.set( 0, 0.994, 0 );
-			box2.scale.set( 1.970, 1.534, 3.955 );
-			this.add( box2 );
-			const box3 = new THREE.Mesh( geometry, boxMaterial );
-			box3.position.set( 6.167, 0.857, 7.803 );
-			box3.rotation.set( 0, 0.561, 0 );
-			box3.scale.set( 3.927, 6.285, 3.687 );
-			this.add( box3 );
-			const box4 = new THREE.Mesh( geometry, boxMaterial );
-			box4.position.set( - 2.017, 0.018, 6.124 );
-			box4.rotation.set( 0, 0.333, 0 );
-			box4.scale.set( 2.002, 4.566, 2.064 );
-			this.add( box4 );
-			const box5 = new THREE.Mesh( geometry, boxMaterial );
-			box5.position.set( 2.291, - 0.756, - 2.621 );
-			box5.rotation.set( 0, - 0.286, 0 );
-			box5.scale.set( 1.546, 1.552, 1.496 );
-			this.add( box5 );
-			const box6 = new THREE.Mesh( geometry, boxMaterial );
-			box6.position.set( - 2.193, - 0.369, - 5.547 );
-			box6.rotation.set( 0, 0.516, 0 );
-			box6.scale.set( 3.875, 3.487, 2.986 );
-			this.add( box6 );
-
-			// -x right
-			const light1 = new THREE.Mesh( geometry, createAreaLightMaterial( 50 ) );
-			light1.position.set( - 16.116, 14.37, 8.208 );
-			light1.scale.set( 0.1, 2.428, 2.739 );
-			this.add( light1 );
-
-			// -x left
-			const light2 = new THREE.Mesh( geometry, createAreaLightMaterial( 50 ) );
-			light2.position.set( - 16.109, 18.021, - 8.207 );
-			light2.scale.set( 0.1, 2.425, 2.751 );
-			this.add( light2 );
-
-			// +x
-			const light3 = new THREE.Mesh( geometry, createAreaLightMaterial( 17 ) );
-			light3.position.set( 14.904, 12.198, - 1.832 );
-			light3.scale.set( 0.15, 4.265, 6.331 );
-			this.add( light3 );
-
-			// +z
-			const light4 = new THREE.Mesh( geometry, createAreaLightMaterial( 43 ) );
-			light4.position.set( - 0.462, 8.89, 14.520 );
-			light4.scale.set( 4.38, 5.441, 0.088 );
-			this.add( light4 );
-
-			// -z
-			const light5 = new THREE.Mesh( geometry, createAreaLightMaterial( 20 ) );
-			light5.position.set( 3.235, 11.486, - 12.541 );
-			light5.scale.set( 2.5, 2.0, 0.1 );
-			this.add( light5 );
-
-			// +y
-			const light6 = new THREE.Mesh( geometry, createAreaLightMaterial( 100 ) );
-			light6.position.set( 0.0, 20.0, 0.0 );
-			light6.scale.set( 1.0, 0.1, 1.0 );
-			this.add( light6 );
-
-		}
-		dispose() {
-
-			const resources = new Set();
-			this.traverse( object => {
-
-				if ( object.isMesh ) {
-
-					resources.add( object.geometry );
-					resources.add( object.material );
-
-				}
-
-			} );
-			for ( const resource of resources ) {
-
-				resource.dispose();
-
-			}
-
-		}
-
-	}
-	function createAreaLightMaterial( intensity ) {
-
-		const material = new THREE.MeshBasicMaterial();
-		material.color.setScalar( intensity );
-		return material;
-
-	}
-
-	THREE.RoomEnvironment = RoomEnvironment;
-
-} )();

+ 0 - 487
examples/js/exporters/ColladaExporter.js

@@ -1,487 +0,0 @@
-( function () {
-
-	/**
- * https://github.com/gkjohnson/collada-exporter-js
- *
- * Usage:
- *  const exporter = new ColladaExporter();
- *
- *  const data = exporter.parse(mesh);
- *
- * Format Definition:
- *  https://www.khronos.org/collada/
- */
-
-	class ColladaExporter {
-
-		parse( object, onDone, options = {} ) {
-
-			options = Object.assign( {
-				version: '1.4.1',
-				author: null,
-				textureDirectory: '',
-				upAxis: 'Y_UP',
-				unitName: null,
-				unitMeter: null
-			}, options );
-			if ( options.upAxis.match( /^[XYZ]_UP$/ ) === null ) {
-
-				console.error( 'ColladaExporter: Invalid upAxis: valid values are X_UP, Y_UP or Z_UP.' );
-				return null;
-
-			}
-
-			if ( options.unitName !== null && options.unitMeter === null ) {
-
-				console.error( 'ColladaExporter: unitMeter needs to be specified if unitName is specified.' );
-				return null;
-
-			}
-
-			if ( options.unitMeter !== null && options.unitName === null ) {
-
-				console.error( 'ColladaExporter: unitName needs to be specified if unitMeter is specified.' );
-				return null;
-
-			}
-
-			if ( options.textureDirectory !== '' ) {
-
-				options.textureDirectory = `${options.textureDirectory}/`.replace( /\\/g, '/' ).replace( /\/+/g, '/' );
-
-			}
-
-			const version = options.version;
-			if ( version !== '1.4.1' && version !== '1.5.0' ) {
-
-				console.warn( `ColladaExporter : Version ${version} not supported for export. Only 1.4.1 and 1.5.0.` );
-				return null;
-
-			}
-
-			// Convert the urdf xml into a well-formatted, indented format
-			function format( urdf ) {
-
-				const IS_END_TAG = /^<\//;
-				const IS_SELF_CLOSING = /(\?>$)|(\/>$)/;
-				const HAS_TEXT = /<[^>]+>[^<]*<\/[^<]+>/;
-				const pad = ( ch, num ) => num > 0 ? ch + pad( ch, num - 1 ) : '';
-				let tagnum = 0;
-				return urdf.match( /(<[^>]+>[^<]+<\/[^<]+>)|(<[^>]+>)/g ).map( tag => {
-
-					if ( ! HAS_TEXT.test( tag ) && ! IS_SELF_CLOSING.test( tag ) && IS_END_TAG.test( tag ) ) {
-
-						tagnum --;
-
-					}
-
-					const res = `${pad( '  ', tagnum )}${tag}`;
-					if ( ! HAS_TEXT.test( tag ) && ! IS_SELF_CLOSING.test( tag ) && ! IS_END_TAG.test( tag ) ) {
-
-						tagnum ++;
-
-					}
-
-					return res;
-
-				} ).join( '\n' );
-
-			}
-
-			// Convert an image into a png format for saving
-			function base64ToBuffer( str ) {
-
-				const b = atob( str );
-				const buf = new Uint8Array( b.length );
-				for ( let i = 0, l = buf.length; i < l; i ++ ) {
-
-					buf[ i ] = b.charCodeAt( i );
-
-				}
-
-				return buf;
-
-			}
-
-			let canvas, ctx;
-			function imageToData( image, ext ) {
-
-				canvas = canvas || document.createElement( 'canvas' );
-				ctx = ctx || canvas.getContext( '2d' );
-				canvas.width = image.width;
-				canvas.height = image.height;
-				ctx.drawImage( image, 0, 0 );
-
-				// Get the base64 encoded data
-				const base64data = canvas.toDataURL( `image/${ext}`, 1 ).replace( /^data:image\/(png|jpg);base64,/, '' );
-
-				// Convert to a uint8 array
-				return base64ToBuffer( base64data );
-
-			}
-
-			// gets the attribute array. Generate a new array if the attribute is interleaved
-			const getFuncs = [ 'getX', 'getY', 'getZ', 'getW' ];
-			const tempColor = new THREE.Color();
-			function attrBufferToArray( attr, isColor = false ) {
-
-				if ( isColor ) {
-
-					// convert the colors to srgb before export
-					// colors are always written as floats
-					const arr = new Float32Array( attr.count * 3 );
-					for ( let i = 0, l = attr.count; i < l; i ++ ) {
-
-						tempColor.fromBufferAttribute( attr, i ).convertLinearToSRGB();
-						arr[ 3 * i + 0 ] = tempColor.r;
-						arr[ 3 * i + 1 ] = tempColor.g;
-						arr[ 3 * i + 2 ] = tempColor.b;
-
-					}
-
-					return arr;
-
-				} else if ( attr.isInterleavedBufferAttribute ) {
-
-					// use the typed array constructor to save on memory
-					const arr = new attr.array.constructor( attr.count * attr.itemSize );
-					const size = attr.itemSize;
-					for ( let i = 0, l = attr.count; i < l; i ++ ) {
-
-						for ( let j = 0; j < size; j ++ ) {
-
-							arr[ i * size + j ] = attr[ getFuncs[ j ] ]( i );
-
-						}
-
-					}
-
-					return arr;
-
-				} else {
-
-					return attr.array;
-
-				}
-
-			}
-
-			// Returns an array of the same type starting at the `st` index,
-			// and `ct` length
-			function subArray( arr, st, ct ) {
-
-				if ( Array.isArray( arr ) ) return arr.slice( st, st + ct ); else return new arr.constructor( arr.buffer, st * arr.BYTES_PER_ELEMENT, ct );
-
-			}
-
-			// Returns the string for a geometry's attribute
-			function getAttribute( attr, name, params, type, isColor = false ) {
-
-				const array = attrBufferToArray( attr, isColor );
-				const res = `<source id="${name}">` + `<float_array id="${name}-array" count="${array.length}">` + array.join( ' ' ) + '</float_array>' + '<technique_common>' + `<accessor source="#${name}-array" count="${Math.floor( array.length / attr.itemSize )}" stride="${attr.itemSize}">` + params.map( n => `<param name="${n}" type="${type}" />` ).join( '' ) + '</accessor>' + '</technique_common>' + '</source>';
-				return res;
-
-			}
-
-			// Returns the string for a node's transform information
-			let transMat;
-			function getTransform( o ) {
-
-				// ensure the object's matrix is up to date
-				// before saving the transform
-				o.updateMatrix();
-				transMat = transMat || new THREE.Matrix4();
-				transMat.copy( o.matrix );
-				transMat.transpose();
-				return `<matrix>${transMat.toArray().join( ' ' )}</matrix>`;
-
-			}
-
-			// Process the given piece of geometry into the geometry library
-			// Returns the mesh id
-			function processGeometry( bufferGeometry ) {
-
-				let info = geometryInfo.get( bufferGeometry );
-				if ( ! info ) {
-
-					const meshid = `Mesh${libraryGeometries.length + 1}`;
-					const indexCount = bufferGeometry.index ? bufferGeometry.index.count * bufferGeometry.index.itemSize : bufferGeometry.attributes.position.count;
-					const groups = bufferGeometry.groups != null && bufferGeometry.groups.length !== 0 ? bufferGeometry.groups : [ {
-						start: 0,
-						count: indexCount,
-						materialIndex: 0
-					} ];
-					const gname = bufferGeometry.name ? ` name="${bufferGeometry.name}"` : '';
-					let gnode = `<geometry id="${meshid}"${gname}><mesh>`;
-
-					// define the geometry node and the vertices for the geometry
-					const posName = `${meshid}-position`;
-					const vertName = `${meshid}-vertices`;
-					gnode += getAttribute( bufferGeometry.attributes.position, posName, [ 'X', 'Y', 'Z' ], 'float' );
-					gnode += `<vertices id="${vertName}"><input semantic="POSITION" source="#${posName}" /></vertices>`;
-
-					// NOTE: We're not optimizing the attribute arrays here, so they're all the same length and
-					// can therefore share the same triangle indices. However, MeshLab seems to have trouble opening
-					// models with attributes that share an offset.
-					// MeshLab Bug#424: https://sourceforge.net/p/meshlab/bugs/424/
-
-					// serialize normals
-					let triangleInputs = `<input semantic="VERTEX" source="#${vertName}" offset="0" />`;
-					if ( 'normal' in bufferGeometry.attributes ) {
-
-						const normName = `${meshid}-normal`;
-						gnode += getAttribute( bufferGeometry.attributes.normal, normName, [ 'X', 'Y', 'Z' ], 'float' );
-						triangleInputs += `<input semantic="NORMAL" source="#${normName}" offset="0" />`;
-
-					}
-
-					// serialize uvs
-					if ( 'uv' in bufferGeometry.attributes ) {
-
-						const uvName = `${meshid}-texcoord`;
-						gnode += getAttribute( bufferGeometry.attributes.uv, uvName, [ 'S', 'T' ], 'float' );
-						triangleInputs += `<input semantic="TEXCOORD" source="#${uvName}" offset="0" set="0" />`;
-
-					}
-
-					// serialize lightmap uvs
-					if ( 'uv2' in bufferGeometry.attributes ) {
-
-						const uvName = `${meshid}-texcoord2`;
-						gnode += getAttribute( bufferGeometry.attributes.uv2, uvName, [ 'S', 'T' ], 'float' );
-						triangleInputs += `<input semantic="TEXCOORD" source="#${uvName}" offset="0" set="1" />`;
-
-					}
-
-					// serialize colors
-					if ( 'color' in bufferGeometry.attributes ) {
-
-						// colors are always written as floats
-						const colName = `${meshid}-color`;
-						gnode += getAttribute( bufferGeometry.attributes.color, colName, [ 'R', 'G', 'B' ], 'float', true );
-						triangleInputs += `<input semantic="COLOR" source="#${colName}" offset="0" />`;
-
-					}
-
-					let indexArray = null;
-					if ( bufferGeometry.index ) {
-
-						indexArray = attrBufferToArray( bufferGeometry.index );
-
-					} else {
-
-						indexArray = new Array( indexCount );
-						for ( let i = 0, l = indexArray.length; i < l; i ++ ) indexArray[ i ] = i;
-
-					}
-
-					for ( let i = 0, l = groups.length; i < l; i ++ ) {
-
-						const group = groups[ i ];
-						const subarr = subArray( indexArray, group.start, group.count );
-						const polycount = subarr.length / 3;
-						gnode += `<triangles material="MESH_MATERIAL_${group.materialIndex}" count="${polycount}">`;
-						gnode += triangleInputs;
-						gnode += `<p>${subarr.join( ' ' )}</p>`;
-						gnode += '</triangles>';
-
-					}
-
-					gnode += '</mesh></geometry>';
-					libraryGeometries.push( gnode );
-					info = {
-						meshid: meshid,
-						bufferGeometry: bufferGeometry
-					};
-					geometryInfo.set( bufferGeometry, info );
-
-				}
-
-				return info;
-
-			}
-
-			// Process the given texture into the image library
-			// Returns the image library
-			function processTexture( tex ) {
-
-				let texid = imageMap.get( tex );
-				if ( texid == null ) {
-
-					texid = `image-${libraryImages.length + 1}`;
-					const ext = 'png';
-					const name = tex.name || texid;
-					let imageNode = `<image id="${texid}" name="${name}">`;
-					if ( version === '1.5.0' ) {
-
-						imageNode += `<init_from><ref>${options.textureDirectory}${name}.${ext}</ref></init_from>`;
-
-					} else {
-
-						// version image node 1.4.1
-						imageNode += `<init_from>${options.textureDirectory}${name}.${ext}</init_from>`;
-
-					}
-
-					imageNode += '</image>';
-					libraryImages.push( imageNode );
-					imageMap.set( tex, texid );
-					textures.push( {
-						directory: options.textureDirectory,
-						name,
-						ext,
-						data: imageToData( tex.image, ext ),
-						original: tex
-					} );
-
-				}
-
-				return texid;
-
-			}
-
-			// Process the given material into the material and effect libraries
-			// Returns the material id
-			function processMaterial( m ) {
-
-				let matid = materialMap.get( m );
-				if ( matid == null ) {
-
-					matid = `Mat${libraryEffects.length + 1}`;
-					let type = 'phong';
-					if ( m.isMeshLambertMaterial === true ) {
-
-						type = 'lambert';
-
-					} else if ( m.isMeshBasicMaterial === true ) {
-
-						type = 'constant';
-						if ( m.map !== null ) {
-
-							// The Collada spec does not support diffuse texture maps with the
-							// constant shader type.
-							// mrdoob/three.js#15469
-							console.warn( 'ColladaExporter: Texture maps not supported with THREE.MeshBasicMaterial.' );
-
-						}
-
-					}
-
-					const emissive = m.emissive ? m.emissive : new THREE.Color( 0, 0, 0 );
-					const diffuse = m.color ? m.color : new THREE.Color( 0, 0, 0 );
-					const specular = m.specular ? m.specular : new THREE.Color( 1, 1, 1 );
-					const shininess = m.shininess || 0;
-					const reflectivity = m.reflectivity || 0;
-					emissive.convertLinearToSRGB();
-					specular.convertLinearToSRGB();
-					diffuse.convertLinearToSRGB();
-
-					// 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
-					// channel to specify the transparency
-					let transparencyNode = '';
-					if ( m.transparent === true ) {
-
-						transparencyNode += '<transparent>' + ( m.map ? '<texture texture="diffuse-sampler"></texture>' : '<float>1</float>' ) + '</transparent>';
-						if ( m.opacity < 1 ) {
-
-							transparencyNode += `<transparency><float>${m.opacity}</float></transparency>`;
-
-						}
-
-					}
-
-					const techniqueNode = `<technique sid="common"><${type}>` + '<emission>' + ( m.emissiveMap ? '<texture texture="emissive-sampler" texcoord="TEXCOORD" />' : `<color sid="emission">${emissive.r} ${emissive.g} ${emissive.b} 1</color>` ) + '</emission>' + ( type !== 'constant' ? '<diffuse>' + ( m.map ? '<texture texture="diffuse-sampler" texcoord="TEXCOORD" />' : `<color sid="diffuse">${diffuse.r} ${diffuse.g} ${diffuse.b} 1</color>` ) + '</diffuse>' : '' ) + ( type !== 'constant' ? '<bump>' + ( m.normalMap ? '<texture texture="bump-sampler" texcoord="TEXCOORD" />' : '' ) + '</bump>' : '' ) + ( type === 'phong' ? `<specular><color sid="specular">${specular.r} ${specular.g} ${specular.b} 1</color></specular>` + '<shininess>' + ( m.specularMap ? '<texture texture="specular-sampler" texcoord="TEXCOORD" />' : `<float sid="shininess">${shininess}</float>` ) + '</shininess>' : '' ) + `<reflective><color>${diffuse.r} ${diffuse.g} ${diffuse.b} 1</color></reflective>` + `<reflectivity><float>${reflectivity}</float></reflectivity>` + transparencyNode + `</${type}></technique>`;
-					const effectnode = `<effect id="${matid}-effect">` + '<profile_COMMON>' + ( m.map ? '<newparam sid="diffuse-surface"><surface type="2D">' + `<init_from>${processTexture( m.map )}</init_from>` + '</surface></newparam>' + '<newparam sid="diffuse-sampler"><sampler2D><source>diffuse-surface</source></sampler2D></newparam>' : '' ) + ( m.specularMap ? '<newparam sid="specular-surface"><surface type="2D">' + `<init_from>${processTexture( m.specularMap )}</init_from>` + '</surface></newparam>' + '<newparam sid="specular-sampler"><sampler2D><source>specular-surface</source></sampler2D></newparam>' : '' ) + ( m.emissiveMap ? '<newparam sid="emissive-surface"><surface type="2D">' + `<init_from>${processTexture( m.emissiveMap )}</init_from>` + '</surface></newparam>' + '<newparam sid="emissive-sampler"><sampler2D><source>emissive-surface</source></sampler2D></newparam>' : '' ) + ( m.normalMap ? '<newparam sid="bump-surface"><surface type="2D">' + `<init_from>${processTexture( m.normalMap )}</init_from>` + '</surface></newparam>' + '<newparam sid="bump-sampler"><sampler2D><source>bump-surface</source></sampler2D></newparam>' : '' ) + techniqueNode + ( m.side === THREE.DoubleSide ? '<extra><technique profile="THREEJS"><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>' : '' ) + '</profile_COMMON>' + '</effect>';
-					const materialName = m.name ? ` name="${m.name}"` : '';
-					const materialNode = `<material id="${matid}"${materialName}><instance_effect url="#${matid}-effect" /></material>`;
-					libraryMaterials.push( materialNode );
-					libraryEffects.push( effectnode );
-					materialMap.set( m, matid );
-
-				}
-
-				return matid;
-
-			}
-
-			// Recursively process the object into a scene
-			function processObject( o ) {
-
-				let node = `<node name="${o.name}">`;
-				node += getTransform( o );
-				if ( o.isMesh === true && o.geometry !== null ) {
-
-					// function returns the id associated with the mesh and a "BufferGeometry" version
-					// of the geometry in case it's not a geometry.
-					const geomInfo = processGeometry( o.geometry );
-					const meshid = geomInfo.meshid;
-					const geometry = geomInfo.bufferGeometry;
-
-					// ids of the materials to bind to the geometry
-					let matids = null;
-					let matidsArray;
-
-					// get a list of materials to bind to the sub groups of the geometry.
-					// If the amount of subgroups is greater than the materials, than reuse
-					// the materials.
-					const mat = o.material || new THREE.MeshBasicMaterial();
-					const materials = Array.isArray( mat ) ? mat : [ mat ];
-					if ( geometry.groups.length > materials.length ) {
-
-						matidsArray = new Array( geometry.groups.length );
-
-					} else {
-
-						matidsArray = new Array( materials.length );
-
-					}
-
-					matids = matidsArray.fill().map( ( v, i ) => processMaterial( materials[ i % materials.length ] ) );
-					node += `<instance_geometry url="#${meshid}">` + ( matids.length > 0 ? '<bind_material><technique_common>' + matids.map( ( id, i ) => `<instance_material symbol="MESH_MATERIAL_${i}" target="#${id}" >` + '<bind_vertex_input semantic="TEXCOORD" input_semantic="TEXCOORD" input_set="0" />' + '</instance_material>' ).join( '' ) + '</technique_common></bind_material>' : '' ) + '</instance_geometry>';
-
-				}
-
-				o.children.forEach( c => node += processObject( c ) );
-				node += '</node>';
-				return node;
-
-			}
-
-			const geometryInfo = new WeakMap();
-			const materialMap = new WeakMap();
-			const imageMap = new WeakMap();
-			const textures = [];
-			const libraryImages = [];
-			const libraryGeometries = [];
-			const libraryEffects = [];
-			const libraryMaterials = [];
-			const libraryVisualScenes = processObject( object );
-			const specLink = version === '1.4.1' ? 'http://www.collada.org/2005/11/COLLADASchema' : 'https://www.khronos.org/collada/';
-			let dae = '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' + `<COLLADA xmlns="${specLink}" version="${version}">` + '<asset>' + ( '<contributor>' + '<authoring_tool>three.js Collada Exporter</authoring_tool>' + ( options.author !== null ? `<author>${options.author}</author>` : '' ) + '</contributor>' + `<created>${new Date().toISOString()}</created>` + `<modified>${new Date().toISOString()}</modified>` + ( options.unitName !== null ? `<unit name="${options.unitName}" meter="${options.unitMeter}" />` : '' ) + `<up_axis>${options.upAxis}</up_axis>` ) + '</asset>';
-			dae += `<library_images>${libraryImages.join( '' )}</library_images>`;
-			dae += `<library_effects>${libraryEffects.join( '' )}</library_effects>`;
-			dae += `<library_materials>${libraryMaterials.join( '' )}</library_materials>`;
-			dae += `<library_geometries>${libraryGeometries.join( '' )}</library_geometries>`;
-			dae += `<library_visual_scenes><visual_scene id="Scene" name="scene">${libraryVisualScenes}</visual_scene></library_visual_scenes>`;
-			dae += '<scene><instance_visual_scene url="#Scene"/></scene>';
-			dae += '</COLLADA>';
-			const res = {
-				data: format( dae ),
-				textures
-			};
-			if ( typeof onDone === 'function' ) {
-
-				requestAnimationFrame( () => onDone( res ) );
-
-			}
-
-			return res;
-
-		}
-
-	}
-
-	THREE.ColladaExporter = ColladaExporter;
-
-} )();

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

@@ -1,212 +0,0 @@
-( function () {
-
-	/**
- * 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
- */
-
-	/* global DracoEncoderModule */
-
-	class DRACOExporter {
-
-		parse( object, options = {
-			decodeSpeed: 5,
-			encodeSpeed: 5,
-			encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
-			quantization: [ 16, 8, 8, 8, 8 ],
-			exportUvs: true,
-			exportNormals: true,
-			exportColor: false
-		} ) {
-
-			if ( DracoEncoderModule === undefined ) {
-
-				throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' );
-
-			}
-
-			const geometry = object.geometry;
-			const dracoEncoder = DracoEncoderModule();
-			const encoder = new dracoEncoder.Encoder();
-			let builder;
-			let dracoObject;
-			if ( object.isMesh === true ) {
-
-				builder = new dracoEncoder.MeshBuilder();
-				dracoObject = new dracoEncoder.Mesh();
-				const vertices = geometry.getAttribute( 'position' );
-				builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
-				const faces = geometry.getIndex();
-				if ( faces !== null ) {
-
-					builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
-
-				} else {
-
-					const faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
-					for ( let i = 0; i < faces.length; i ++ ) {
-
-						faces[ i ] = i;
-
-					}
-
-					builder.AddFacesToMesh( dracoObject, vertices.count, faces );
-
-				}
-
-				if ( options.exportNormals === true ) {
-
-					const normals = geometry.getAttribute( 'normal' );
-					if ( normals !== undefined ) {
-
-						builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
-
-					}
-
-				}
-
-				if ( options.exportUvs === true ) {
-
-					const uvs = geometry.getAttribute( 'uv' );
-					if ( uvs !== undefined ) {
-
-						builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
-
-					}
-
-				}
-
-				if ( options.exportColor === true ) {
-
-					const colors = geometry.getAttribute( 'color' );
-					if ( colors !== undefined ) {
-
-						builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
-
-					}
-
-				}
-
-			} else if ( object.isPoints === true ) {
-
-				builder = new dracoEncoder.PointCloudBuilder();
-				dracoObject = new dracoEncoder.PointCloud();
-				const vertices = geometry.getAttribute( 'position' );
-				builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
-				if ( options.exportColor === true ) {
-
-					const colors = geometry.getAttribute( 'color' );
-					if ( colors !== undefined ) {
-
-						builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
-
-					}
-
-				}
-
-			} else {
-
-				throw new Error( 'DRACOExporter: Unsupported object type.' );
-
-			}
-
-			//Compress using draco encoder
-
-			const 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).
-
-			const encodeSpeed = options.encodeSpeed !== undefined ? options.encodeSpeed : 5;
-			const decodeSpeed = options.decodeSpeed !== undefined ? options.decodeSpeed : 5;
-			encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
-
-			// 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 ( let i = 0; i < 5; i ++ ) {
-
-					if ( options.quantization[ i ] !== undefined ) {
-
-						encoder.SetAttributeQuantization( i, options.quantization[ i ] );
-
-					}
-
-				}
-
-			}
-
-			let length;
-			if ( object.isMesh === true ) {
-
-				length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
-
-			} else {
-
-				length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
-
-			}
-
-			dracoEncoder.destroy( dracoObject );
-			if ( length === 0 ) {
-
-				throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' );
-
-			}
-
-			//Copy encoded data to buffer.
-			const outputData = new Int8Array( new ArrayBuffer( length ) );
-			for ( let 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;
-
-	THREE.DRACOExporter = DRACOExporter;
-
-} )();

+ 0 - 455
examples/js/exporters/EXRExporter.js

@@ -1,455 +0,0 @@
-( function () {
-
-	/**
- * @author sciecode / https://github.com/sciecode
- *
- * EXR format references:
- * 	https://www.openexr.com/documentation/openexrfilelayout.pdf
- */
-	const textEncoder = new TextEncoder();
-	const NO_COMPRESSION = 0;
-	const ZIPS_COMPRESSION = 2;
-	const ZIP_COMPRESSION = 3;
-	class EXRExporter {
-
-		parse( renderer, renderTarget, options ) {
-
-			if ( ! supported( renderer, renderTarget ) ) return undefined;
-			const info = buildInfo( renderTarget, options ),
-				dataBuffer = getPixelData( renderer, renderTarget, info ),
-				rawContentBuffer = reorganizeDataBuffer( dataBuffer, info ),
-				chunks = compressData( rawContentBuffer, info );
-			return fillData( chunks, info );
-
-		}
-
-	}
-	function supported( renderer, renderTarget ) {
-
-		if ( ! renderer || ! renderer.isWebGLRenderer ) {
-
-			console.error( 'EXRExporter.parse: Unsupported first parameter, expected instance of WebGLRenderer.' );
-			return false;
-
-		}
-
-		if ( ! renderTarget || ! renderTarget.isWebGLRenderTarget ) {
-
-			console.error( 'EXRExporter.parse: Unsupported second parameter, expected instance of WebGLRenderTarget.' );
-			return false;
-
-		}
-
-		if ( renderTarget.texture.type !== THREE.FloatType && renderTarget.texture.type !== THREE.HalfFloatType ) {
-
-			console.error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture type.' );
-			return false;
-
-		}
-
-		if ( renderTarget.texture.format !== THREE.RGBAFormat ) {
-
-			console.error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture format, expected THREE.RGBAFormat.' );
-			return false;
-
-		}
-
-		return true;
-
-	}
-
-	function buildInfo( renderTarget, options = {} ) {
-
-		const compressionSizes = {
-			0: 1,
-			2: 1,
-			3: 16
-		};
-		const WIDTH = renderTarget.width,
-			HEIGHT = renderTarget.height,
-			TYPE = renderTarget.texture.type,
-			FORMAT = renderTarget.texture.format,
-			ENCODING = renderTarget.texture.encoding,
-			COMPRESSION = options.compression !== undefined ? options.compression : ZIP_COMPRESSION,
-			EXPORTER_TYPE = options.type !== undefined ? options.type : THREE.HalfFloatType,
-			OUT_TYPE = EXPORTER_TYPE === THREE.FloatType ? 2 : 1,
-			COMPRESSION_SIZE = compressionSizes[ COMPRESSION ],
-			NUM_CHANNELS = 4;
-		return {
-			width: WIDTH,
-			height: HEIGHT,
-			type: TYPE,
-			format: FORMAT,
-			encoding: ENCODING,
-			compression: COMPRESSION,
-			blockLines: COMPRESSION_SIZE,
-			dataType: OUT_TYPE,
-			dataSize: 2 * OUT_TYPE,
-			numBlocks: Math.ceil( HEIGHT / COMPRESSION_SIZE ),
-			numInputChannels: 4,
-			numOutputChannels: NUM_CHANNELS
-		};
-
-	}
-
-	function getPixelData( renderer, rtt, info ) {
-
-		let dataBuffer;
-		if ( info.type === THREE.FloatType ) {
-
-			dataBuffer = new Float32Array( info.width * info.height * info.numInputChannels );
-
-		} else {
-
-			dataBuffer = new Uint16Array( info.width * info.height * info.numInputChannels );
-
-		}
-
-		renderer.readRenderTargetPixels( rtt, 0, 0, info.width, info.height, dataBuffer );
-		return dataBuffer;
-
-	}
-
-	function reorganizeDataBuffer( inBuffer, info ) {
-
-		const w = info.width,
-			h = info.height,
-			dec = {
-				r: 0,
-				g: 0,
-				b: 0,
-				a: 0
-			},
-			offset = {
-				value: 0
-			},
-			cOffset = info.numOutputChannels == 4 ? 1 : 0,
-			getValue = info.type == THREE.FloatType ? getFloat32 : getFloat16,
-			setValue = info.dataType == 1 ? setFloat16 : setFloat32,
-			outBuffer = new Uint8Array( info.width * info.height * info.numOutputChannels * info.dataSize ),
-			dv = new DataView( outBuffer.buffer );
-		for ( let y = 0; y < h; ++ y ) {
-
-			for ( let x = 0; x < w; ++ x ) {
-
-				const i = y * w * 4 + x * 4;
-				const r = getValue( inBuffer, i );
-				const g = getValue( inBuffer, i + 1 );
-				const b = getValue( inBuffer, i + 2 );
-				const a = getValue( inBuffer, i + 3 );
-				const line = ( h - y - 1 ) * w * ( 3 + cOffset ) * info.dataSize;
-				decodeLinear( dec, r, g, b, a );
-				offset.value = line + x * info.dataSize;
-				setValue( dv, dec.a, offset );
-				offset.value = line + cOffset * w * info.dataSize + x * info.dataSize;
-				setValue( dv, dec.b, offset );
-				offset.value = line + ( 1 + cOffset ) * w * info.dataSize + x * info.dataSize;
-				setValue( dv, dec.g, offset );
-				offset.value = line + ( 2 + cOffset ) * w * info.dataSize + x * info.dataSize;
-				setValue( dv, dec.r, offset );
-
-			}
-
-		}
-
-		return outBuffer;
-
-	}
-
-	function compressData( inBuffer, info ) {
-
-		let compress,
-			tmpBuffer,
-			sum = 0;
-		const chunks = {
-				data: new Array(),
-				totalSize: 0
-			},
-			size = info.width * info.numOutputChannels * info.blockLines * info.dataSize;
-		switch ( info.compression ) {
-
-			case 0:
-				compress = compressNONE;
-				break;
-			case 2:
-			case 3:
-				compress = compressZIP;
-				break;
-
-		}
-
-		if ( info.compression !== 0 ) {
-
-			tmpBuffer = new Uint8Array( size );
-
-		}
-
-		for ( let i = 0; i < info.numBlocks; ++ i ) {
-
-			const arr = inBuffer.subarray( size * i, size * ( i + 1 ) );
-			const block = compress( arr, tmpBuffer );
-			sum += block.length;
-			chunks.data.push( {
-				dataChunk: block,
-				size: block.length
-			} );
-
-		}
-
-		chunks.totalSize = sum;
-		return chunks;
-
-	}
-
-	function compressNONE( data ) {
-
-		return data;
-
-	}
-
-	function compressZIP( data, tmpBuffer ) {
-
-		//
-		// Reorder the pixel data.
-		//
-
-		let t1 = 0,
-			t2 = Math.floor( ( data.length + 1 ) / 2 ),
-			s = 0;
-		const stop = data.length - 1;
-		while ( true ) {
-
-			if ( s > stop ) break;
-			tmpBuffer[ t1 ++ ] = data[ s ++ ];
-			if ( s > stop ) break;
-			tmpBuffer[ t2 ++ ] = data[ s ++ ];
-
-		}
-
-		//
-		// Predictor.
-		//
-
-		let p = tmpBuffer[ 0 ];
-		for ( let t = 1; t < tmpBuffer.length; t ++ ) {
-
-			const d = tmpBuffer[ t ] - p + ( 128 + 256 );
-			p = tmpBuffer[ t ];
-			tmpBuffer[ t ] = d;
-
-		}
-
-		if ( typeof fflate === 'undefined' ) {
-
-			console.error( 'THREE.EXRLoader: External \`fflate.module.js\` required' );
-
-		}
-
-		const deflate = fflate.zlibSync( tmpBuffer ); // eslint-disable-line no-undef
-
-		return deflate;
-
-	}
-
-	function fillHeader( outBuffer, chunks, info ) {
-
-		const offset = {
-			value: 0
-		};
-		const dv = new DataView( outBuffer.buffer );
-		setUint32( dv, 20000630, offset ); // magic
-		setUint32( dv, 2, offset ); // mask
-
-		// = HEADER =
-
-		setString( dv, 'compression', offset );
-		setString( dv, 'compression', offset );
-		setUint32( dv, 1, offset );
-		setUint8( dv, info.compression, offset );
-		setString( dv, 'screenWindowCenter', offset );
-		setString( dv, 'v2f', offset );
-		setUint32( dv, 8, offset );
-		setUint32( dv, 0, offset );
-		setUint32( dv, 0, offset );
-		setString( dv, 'screenWindowWidth', offset );
-		setString( dv, 'float', offset );
-		setUint32( dv, 4, offset );
-		setFloat32( dv, 1.0, offset );
-		setString( dv, 'pixelAspectRatio', offset );
-		setString( dv, 'float', offset );
-		setUint32( dv, 4, offset );
-		setFloat32( dv, 1.0, offset );
-		setString( dv, 'lineOrder', offset );
-		setString( dv, 'lineOrder', offset );
-		setUint32( dv, 1, offset );
-		setUint8( dv, 0, offset );
-		setString( dv, 'dataWindow', offset );
-		setString( dv, 'box2i', offset );
-		setUint32( dv, 16, offset );
-		setUint32( dv, 0, offset );
-		setUint32( dv, 0, offset );
-		setUint32( dv, info.width - 1, offset );
-		setUint32( dv, info.height - 1, offset );
-		setString( dv, 'displayWindow', offset );
-		setString( dv, 'box2i', offset );
-		setUint32( dv, 16, offset );
-		setUint32( dv, 0, offset );
-		setUint32( dv, 0, offset );
-		setUint32( dv, info.width - 1, offset );
-		setUint32( dv, info.height - 1, offset );
-		setString( dv, 'channels', offset );
-		setString( dv, 'chlist', offset );
-		setUint32( dv, info.numOutputChannels * 18 + 1, offset );
-		setString( dv, 'A', offset );
-		setUint32( dv, info.dataType, offset );
-		offset.value += 4;
-		setUint32( dv, 1, offset );
-		setUint32( dv, 1, offset );
-		setString( dv, 'B', offset );
-		setUint32( dv, info.dataType, offset );
-		offset.value += 4;
-		setUint32( dv, 1, offset );
-		setUint32( dv, 1, offset );
-		setString( dv, 'G', offset );
-		setUint32( dv, info.dataType, offset );
-		offset.value += 4;
-		setUint32( dv, 1, offset );
-		setUint32( dv, 1, offset );
-		setString( dv, 'R', offset );
-		setUint32( dv, info.dataType, offset );
-		offset.value += 4;
-		setUint32( dv, 1, offset );
-		setUint32( dv, 1, offset );
-		setUint8( dv, 0, offset );
-
-		// null-byte
-		setUint8( dv, 0, offset );
-
-		// = OFFSET TABLE =
-
-		let sum = offset.value + info.numBlocks * 8;
-		for ( let i = 0; i < chunks.data.length; ++ i ) {
-
-			setUint64( dv, sum, offset );
-			sum += chunks.data[ i ].size + 8;
-
-		}
-
-	}
-
-	function fillData( chunks, info ) {
-
-		const TableSize = info.numBlocks * 8,
-			HeaderSize = 259 + 18 * info.numOutputChannels,
-			// 259 + 18 * chlist
-			offset = {
-				value: HeaderSize + TableSize
-			},
-			outBuffer = new Uint8Array( HeaderSize + TableSize + chunks.totalSize + info.numBlocks * 8 ),
-			dv = new DataView( outBuffer.buffer );
-		fillHeader( outBuffer, chunks, info );
-		for ( let i = 0; i < chunks.data.length; ++ i ) {
-
-			const data = chunks.data[ i ].dataChunk;
-			const size = chunks.data[ i ].size;
-			setUint32( dv, i * info.blockLines, offset );
-			setUint32( dv, size, offset );
-			outBuffer.set( data, offset.value );
-			offset.value += size;
-
-		}
-
-		return outBuffer;
-
-	}
-
-	function decodeLinear( dec, r, g, b, a ) {
-
-		dec.r = r;
-		dec.g = g;
-		dec.b = b;
-		dec.a = a;
-
-	}
-
-	// function decodeSRGB( dec, r, g, b, a ) {
-
-	// 	dec.r = r > 0.04045 ? Math.pow( r * 0.9478672986 + 0.0521327014, 2.4 ) : r * 0.0773993808;
-	// 	dec.g = g > 0.04045 ? Math.pow( g * 0.9478672986 + 0.0521327014, 2.4 ) : g * 0.0773993808;
-	// 	dec.b = b > 0.04045 ? Math.pow( b * 0.9478672986 + 0.0521327014, 2.4 ) : b * 0.0773993808;
-	// 	dec.a = a;
-
-	// }
-
-	function setUint8( dv, value, offset ) {
-
-		dv.setUint8( offset.value, value );
-		offset.value += 1;
-
-	}
-
-	function setUint32( dv, value, offset ) {
-
-		dv.setUint32( offset.value, value, true );
-		offset.value += 4;
-
-	}
-
-	function setFloat16( dv, value, offset ) {
-
-		dv.setUint16( offset.value, THREE.DataUtils.toHalfFloat( value ), true );
-		offset.value += 2;
-
-	}
-
-	function setFloat32( dv, value, offset ) {
-
-		dv.setFloat32( offset.value, value, true );
-		offset.value += 4;
-
-	}
-
-	function setUint64( dv, value, offset ) {
-
-		dv.setBigUint64( offset.value, BigInt( value ), true );
-		offset.value += 8;
-
-	}
-
-	function setString( dv, string, offset ) {
-
-		const tmp = textEncoder.encode( string + '\0' );
-		for ( let i = 0; i < tmp.length; ++ i ) {
-
-			setUint8( dv, tmp[ i ], offset );
-
-		}
-
-	}
-
-	function decodeFloat16( binary ) {
-
-		const exponent = ( binary & 0x7C00 ) >> 10,
-			fraction = binary & 0x03FF;
-		return ( binary >> 15 ? - 1 : 1 ) * ( exponent ? exponent === 0x1F ? fraction ? NaN : Infinity : Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 ) : 6.103515625e-5 * ( fraction / 0x400 ) );
-
-	}
-
-	function getFloat16( arr, i ) {
-
-		return decodeFloat16( arr[ i ] );
-
-	}
-
-	function getFloat32( arr, i ) {
-
-		return arr[ i ];
-
-	}
-
-	THREE.EXRExporter = EXRExporter;
-	THREE.NO_COMPRESSION = NO_COMPRESSION;
-	THREE.ZIPS_COMPRESSION = ZIPS_COMPRESSION;
-	THREE.ZIP_COMPRESSION = ZIP_COMPRESSION;
-
-} )();

+ 0 - 2367
examples/js/exporters/GLTFExporter.js

@@ -1,2367 +0,0 @@
-( function () {
-
-	class GLTFExporter {
-
-		constructor() {
-
-			this.pluginCallbacks = [];
-			this.register( function ( writer ) {
-
-				return new GLTFLightExtension( writer );
-
-			} );
-			this.register( function ( writer ) {
-
-				return new GLTFMaterialsUnlitExtension( writer );
-
-			} );
-			this.register( function ( writer ) {
-
-				return new GLTFMaterialsTransmissionExtension( writer );
-
-			} );
-			this.register( function ( writer ) {
-
-				return new GLTFMaterialsVolumeExtension( writer );
-
-			} );
-			this.register( function ( writer ) {
-
-				return new GLTFMaterialsClearcoatExtension( writer );
-
-			} );
-			this.register( function ( writer ) {
-
-				return new GLTFMaterialsIridescenceExtension( writer );
-
-			} );
-
-		}
-		register( callback ) {
-
-			if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) {
-
-				this.pluginCallbacks.push( callback );
-
-			}
-
-			return this;
-
-		}
-		unregister( callback ) {
-
-			if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) {
-
-				this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 );
-
-			}
-
-			return this;
-
-		}
-
-		/**
-   * Parse scenes and generate GLTF output
-   * @param  {Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
-   * @param  {Function} onDone  Callback on completed
-   * @param  {Function} onError  Callback on errors
-   * @param  {Object} options options
-   */
-		parse( input, onDone, onError, options ) {
-
-			const writer = new GLTFWriter();
-			const plugins = [];
-			for ( let i = 0, il = this.pluginCallbacks.length; i < il; i ++ ) {
-
-				plugins.push( this.pluginCallbacks[ i ]( writer ) );
-
-			}
-
-			writer.setPlugins( plugins );
-			writer.write( input, onDone, options ).catch( onError );
-
-		}
-		parseAsync( input, options ) {
-
-			const scope = this;
-			return new Promise( function ( resolve, reject ) {
-
-				scope.parse( input, resolve, reject, options );
-
-			} );
-
-		}
-
-	}
-
-	//------------------------------------------------------------------------------
-	// Constants
-	//------------------------------------------------------------------------------
-
-	const WEBGL_CONSTANTS = {
-		POINTS: 0x0000,
-		LINES: 0x0001,
-		LINE_LOOP: 0x0002,
-		LINE_STRIP: 0x0003,
-		TRIANGLES: 0x0004,
-		TRIANGLE_STRIP: 0x0005,
-		TRIANGLE_FAN: 0x0006,
-		UNSIGNED_BYTE: 0x1401,
-		UNSIGNED_SHORT: 0x1403,
-		FLOAT: 0x1406,
-		UNSIGNED_INT: 0x1405,
-		ARRAY_BUFFER: 0x8892,
-		ELEMENT_ARRAY_BUFFER: 0x8893,
-		NEAREST: 0x2600,
-		LINEAR: 0x2601,
-		NEAREST_MIPMAP_NEAREST: 0x2700,
-		LINEAR_MIPMAP_NEAREST: 0x2701,
-		NEAREST_MIPMAP_LINEAR: 0x2702,
-		LINEAR_MIPMAP_LINEAR: 0x2703,
-		CLAMP_TO_EDGE: 33071,
-		MIRRORED_REPEAT: 33648,
-		REPEAT: 10497
-	};
-	const THREE_TO_WEBGL = {};
-	THREE_TO_WEBGL[ THREE.NearestFilter ] = WEBGL_CONSTANTS.NEAREST;
-	THREE_TO_WEBGL[ THREE.NearestMipmapNearestFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_NEAREST;
-	THREE_TO_WEBGL[ THREE.NearestMipmapLinearFilter ] = WEBGL_CONSTANTS.NEAREST_MIPMAP_LINEAR;
-	THREE_TO_WEBGL[ THREE.LinearFilter ] = WEBGL_CONSTANTS.LINEAR;
-	THREE_TO_WEBGL[ THREE.LinearMipmapNearestFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_NEAREST;
-	THREE_TO_WEBGL[ THREE.LinearMipmapLinearFilter ] = WEBGL_CONSTANTS.LINEAR_MIPMAP_LINEAR;
-	THREE_TO_WEBGL[ THREE.ClampToEdgeWrapping ] = WEBGL_CONSTANTS.CLAMP_TO_EDGE;
-	THREE_TO_WEBGL[ THREE.RepeatWrapping ] = WEBGL_CONSTANTS.REPEAT;
-	THREE_TO_WEBGL[ THREE.MirroredRepeatWrapping ] = WEBGL_CONSTANTS.MIRRORED_REPEAT;
-	const PATH_PROPERTIES = {
-		scale: 'scale',
-		position: 'translation',
-		quaternion: 'rotation',
-		morphTargetInfluences: 'weights'
-	};
-
-	// GLB constants
-	// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
-
-	const GLB_HEADER_BYTES = 12;
-	const GLB_HEADER_MAGIC = 0x46546C67;
-	const GLB_VERSION = 2;
-	const GLB_CHUNK_PREFIX_BYTES = 8;
-	const GLB_CHUNK_TYPE_JSON = 0x4E4F534A;
-	const GLB_CHUNK_TYPE_BIN = 0x004E4942;
-
-	//------------------------------------------------------------------------------
-	// Utility functions
-	//------------------------------------------------------------------------------
-
-	/**
- * Compare two arrays
- * @param  {Array} array1 Array 1 to compare
- * @param  {Array} array2 Array 2 to compare
- * @return {Boolean}        Returns true if both arrays are equal
- */
-	function equalArray( array1, array2 ) {
-
-		return array1.length === array2.length && array1.every( function ( element, index ) {
-
-			return element === array2[ index ];
-
-		} );
-
-	}
-
-	/**
- * Converts a string to an ArrayBuffer.
- * @param  {string} text
- * @return {ArrayBuffer}
- */
-	function stringToArrayBuffer( text ) {
-
-		return new TextEncoder().encode( text ).buffer;
-
-	}
-
-	/**
- * Is identity matrix
- *
- * @param {Matrix4} matrix
- * @returns {Boolean} Returns true, if parameter is identity matrix
- */
-	function isIdentityMatrix( matrix ) {
-
-		return equalArray( matrix.elements, [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ] );
-
-	}
-
-	/**
- * Get the min and max vectors from the given attribute
- * @param  {BufferAttribute} attribute Attribute to find the min/max in range from start to start + count
- * @param  {Integer} start
- * @param  {Integer} count
- * @return {Object} Object containing the `min` and `max` values (As an array of attribute.itemSize components)
- */
-	function getMinMax( attribute, start, count ) {
-
-		const output = {
-			min: new Array( attribute.itemSize ).fill( Number.POSITIVE_INFINITY ),
-			max: new Array( attribute.itemSize ).fill( Number.NEGATIVE_INFINITY )
-		};
-		for ( let i = start; i < start + count; i ++ ) {
-
-			for ( let a = 0; a < attribute.itemSize; a ++ ) {
-
-				let value;
-				if ( attribute.itemSize > 4 ) {
-
-					// no support for interleaved data for itemSize > 4
-
-					value = attribute.array[ i * attribute.itemSize + a ];
-
-				} else {
-
-					if ( a === 0 ) value = attribute.getX( i ); else if ( a === 1 ) value = attribute.getY( i ); else if ( a === 2 ) value = attribute.getZ( i ); else if ( a === 3 ) value = attribute.getW( i );
-
-				}
-
-				output.min[ a ] = Math.min( output.min[ a ], value );
-				output.max[ a ] = Math.max( output.max[ a ], value );
-
-			}
-
-		}
-
-		return output;
-
-	}
-
-	/**
- * Get the required size + padding for a buffer, rounded to the next 4-byte boundary.
- * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
- *
- * @param {Integer} bufferSize The size the original buffer.
- * @returns {Integer} new buffer size with required padding.
- *
- */
-	function getPaddedBufferSize( bufferSize ) {
-
-		return Math.ceil( bufferSize / 4 ) * 4;
-
-	}
-
-	/**
- * Returns a buffer aligned to 4-byte boundary.
- *
- * @param {ArrayBuffer} arrayBuffer Buffer to pad
- * @param {Integer} paddingByte (Optional)
- * @returns {ArrayBuffer} The same buffer if it's already aligned to 4-byte boundary or a new buffer
- */
-	function getPaddedArrayBuffer( arrayBuffer, paddingByte = 0 ) {
-
-		const paddedLength = getPaddedBufferSize( arrayBuffer.byteLength );
-		if ( paddedLength !== arrayBuffer.byteLength ) {
-
-			const array = new Uint8Array( paddedLength );
-			array.set( new Uint8Array( arrayBuffer ) );
-			if ( paddingByte !== 0 ) {
-
-				for ( let i = arrayBuffer.byteLength; i < paddedLength; i ++ ) {
-
-					array[ i ] = paddingByte;
-
-				}
-
-			}
-
-			return array.buffer;
-
-		}
-
-		return arrayBuffer;
-
-	}
-
-	function getCanvas() {
-
-		if ( typeof document === 'undefined' && typeof OffscreenCanvas !== 'undefined' ) {
-
-			return new OffscreenCanvas( 1, 1 );
-
-		}
-
-		return document.createElement( 'canvas' );
-
-	}
-
-	function getToBlobPromise( canvas, mimeType ) {
-
-		if ( canvas.toBlob !== undefined ) {
-
-			return new Promise( resolve => canvas.toBlob( resolve, mimeType ) );
-
-		}
-
-		let quality;
-
-		// Blink's implementation of convertToBlob seems to default to a quality level of 100%
-		// Use the Blink default quality levels of toBlob instead so that file sizes are comparable.
-		if ( mimeType === 'image/jpeg' ) {
-
-			quality = 0.92;
-
-		} else if ( mimeType === 'image/webp' ) {
-
-			quality = 0.8;
-
-		}
-
-		return canvas.convertToBlob( {
-			type: mimeType,
-			quality: quality
-		} );
-
-	}
-
-	/**
- * Writer
- */
-	class GLTFWriter {
-
-		constructor() {
-
-			this.plugins = [];
-			this.options = {};
-			this.pending = [];
-			this.buffers = [];
-			this.byteOffset = 0;
-			this.buffers = [];
-			this.nodeMap = new Map();
-			this.skins = [];
-			this.extensionsUsed = {};
-			this.uids = new Map();
-			this.uid = 0;
-			this.json = {
-				asset: {
-					version: '2.0',
-					generator: 'THREE.GLTFExporter'
-				}
-			};
-			this.cache = {
-				meshes: new Map(),
-				attributes: new Map(),
-				attributesNormalized: new Map(),
-				materials: new Map(),
-				textures: new Map(),
-				images: new Map()
-			};
-
-		}
-		setPlugins( plugins ) {
-
-			this.plugins = plugins;
-
-		}
-
-		/**
-   * Parse scenes and generate GLTF output
-   * @param  {Scene or [THREE.Scenes]} input   THREE.Scene or Array of THREE.Scenes
-   * @param  {Function} onDone  Callback on completed
-   * @param  {Object} options options
-   */
-		async write( input, onDone, options ) {
-
-			this.options = Object.assign( {}, {
-				// default options
-				binary: false,
-				trs: false,
-				onlyVisible: true,
-				maxTextureSize: Infinity,
-				animations: [],
-				includeCustomExtensions: false
-			}, options );
-			if ( this.options.animations.length > 0 ) {
-
-				// Only TRS properties, and not matrices, may be targeted by animation.
-				this.options.trs = true;
-
-			}
-
-			this.processInput( input );
-			await Promise.all( this.pending );
-			const writer = this;
-			const buffers = writer.buffers;
-			const json = writer.json;
-			options = writer.options;
-			const extensionsUsed = writer.extensionsUsed;
-
-			// Merge buffers.
-			const blob = new Blob( buffers, {
-				type: 'application/octet-stream'
-			} );
-
-			// Declare extensions.
-			const extensionsUsedList = Object.keys( extensionsUsed );
-			if ( extensionsUsedList.length > 0 ) json.extensionsUsed = extensionsUsedList;
-
-			// Update bytelength of the single buffer.
-			if ( json.buffers && json.buffers.length > 0 ) json.buffers[ 0 ].byteLength = blob.size;
-			if ( options.binary === true ) {
-
-				// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
-
-				const reader = new FileReader();
-				reader.readAsArrayBuffer( blob );
-				reader.onloadend = function () {
-
-					// Binary chunk.
-					const binaryChunk = getPaddedArrayBuffer( reader.result );
-					const binaryChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
-					binaryChunkPrefix.setUint32( 0, binaryChunk.byteLength, true );
-					binaryChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_BIN, true );
-
-					// JSON chunk.
-					const jsonChunk = getPaddedArrayBuffer( stringToArrayBuffer( JSON.stringify( json ) ), 0x20 );
-					const jsonChunkPrefix = new DataView( new ArrayBuffer( GLB_CHUNK_PREFIX_BYTES ) );
-					jsonChunkPrefix.setUint32( 0, jsonChunk.byteLength, true );
-					jsonChunkPrefix.setUint32( 4, GLB_CHUNK_TYPE_JSON, true );
-
-					// GLB header.
-					const header = new ArrayBuffer( GLB_HEADER_BYTES );
-					const headerView = new DataView( header );
-					headerView.setUint32( 0, GLB_HEADER_MAGIC, true );
-					headerView.setUint32( 4, GLB_VERSION, true );
-					const totalByteLength = GLB_HEADER_BYTES + jsonChunkPrefix.byteLength + jsonChunk.byteLength + binaryChunkPrefix.byteLength + binaryChunk.byteLength;
-					headerView.setUint32( 8, totalByteLength, true );
-					const glbBlob = new Blob( [ header, jsonChunkPrefix, jsonChunk, binaryChunkPrefix, binaryChunk ], {
-						type: 'application/octet-stream'
-					} );
-					const glbReader = new FileReader();
-					glbReader.readAsArrayBuffer( glbBlob );
-					glbReader.onloadend = function () {
-
-						onDone( glbReader.result );
-
-					};
-
-				};
-
-			} else {
-
-				if ( json.buffers && json.buffers.length > 0 ) {
-
-					const reader = new FileReader();
-					reader.readAsDataURL( blob );
-					reader.onloadend = function () {
-
-						const base64data = reader.result;
-						json.buffers[ 0 ].uri = base64data;
-						onDone( json );
-
-					};
-
-				} else {
-
-					onDone( json );
-
-				}
-
-			}
-
-		}
-
-		/**
-   * Serializes a userData.
-   *
-   * @param {THREE.Object3D|THREE.Material} object
-   * @param {Object} objectDef
-   */
-		serializeUserData( object, objectDef ) {
-
-			if ( Object.keys( object.userData ).length === 0 ) return;
-			const options = this.options;
-			const extensionsUsed = this.extensionsUsed;
-			try {
-
-				const json = JSON.parse( JSON.stringify( object.userData ) );
-				if ( options.includeCustomExtensions && json.gltfExtensions ) {
-
-					if ( objectDef.extensions === undefined ) objectDef.extensions = {};
-					for ( const extensionName in json.gltfExtensions ) {
-
-						objectDef.extensions[ extensionName ] = json.gltfExtensions[ extensionName ];
-						extensionsUsed[ extensionName ] = true;
-
-					}
-
-					delete json.gltfExtensions;
-
-				}
-
-				if ( Object.keys( json ).length > 0 ) objectDef.extras = json;
-
-			} catch ( error ) {
-
-				console.warn( 'THREE.GLTFExporter: userData of \'' + object.name + '\' ' + 'won\'t be serialized because of JSON.stringify error - ' + error.message );
-
-			}
-
-		}
-
-		/**
-   * Returns ids for buffer attributes.
-   * @param  {Object} object
-   * @return {Integer}
-   */
-		getUID( attribute, isRelativeCopy = false ) {
-
-			if ( this.uids.has( attribute ) === false ) {
-
-				const uids = new Map();
-				uids.set( true, this.uid ++ );
-				uids.set( false, this.uid ++ );
-				this.uids.set( attribute, uids );
-
-			}
-
-			const uids = this.uids.get( attribute );
-			return uids.get( isRelativeCopy );
-
-		}
-
-		/**
-   * Checks if normal attribute values are normalized.
-   *
-   * @param {BufferAttribute} normal
-   * @returns {Boolean}
-   */
-		isNormalizedNormalAttribute( normal ) {
-
-			const cache = this.cache;
-			if ( cache.attributesNormalized.has( normal ) ) return false;
-			const v = new THREE.Vector3();
-			for ( let i = 0, il = normal.count; i < il; i ++ ) {
-
-				// 0.0005 is from glTF-validator
-				if ( Math.abs( v.fromBufferAttribute( normal, i ).length() - 1.0 ) > 0.0005 ) return false;
-
-			}
-
-			return true;
-
-		}
-
-		/**
-   * Creates normalized normal buffer attribute.
-   *
-   * @param {BufferAttribute} normal
-   * @returns {BufferAttribute}
-   *
-   */
-		createNormalizedNormalAttribute( normal ) {
-
-			const cache = this.cache;
-			if ( cache.attributesNormalized.has( normal ) ) return cache.attributesNormalized.get( normal );
-			const attribute = normal.clone();
-			const v = new THREE.Vector3();
-			for ( let i = 0, il = attribute.count; i < il; i ++ ) {
-
-				v.fromBufferAttribute( attribute, i );
-				if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
-
-					// if values can't be normalized set (1, 0, 0)
-					v.setX( 1.0 );
-
-				} else {
-
-					v.normalize();
-
-				}
-
-				attribute.setXYZ( i, v.x, v.y, v.z );
-
-			}
-
-			cache.attributesNormalized.set( normal, attribute );
-			return attribute;
-
-		}
-
-		/**
-   * Applies a texture transform, if present, to the map definition. Requires
-   * the KHR_texture_transform extension.
-   *
-   * @param {Object} mapDef
-   * @param {THREE.Texture} texture
-   */
-		applyTextureTransform( mapDef, texture ) {
-
-			let didTransform = false;
-			const transformDef = {};
-			if ( texture.offset.x !== 0 || texture.offset.y !== 0 ) {
-
-				transformDef.offset = texture.offset.toArray();
-				didTransform = true;
-
-			}
-
-			if ( texture.rotation !== 0 ) {
-
-				transformDef.rotation = texture.rotation;
-				didTransform = true;
-
-			}
-
-			if ( texture.repeat.x !== 1 || texture.repeat.y !== 1 ) {
-
-				transformDef.scale = texture.repeat.toArray();
-				didTransform = true;
-
-			}
-
-			if ( didTransform ) {
-
-				mapDef.extensions = mapDef.extensions || {};
-				mapDef.extensions[ 'KHR_texture_transform' ] = transformDef;
-				this.extensionsUsed[ 'KHR_texture_transform' ] = true;
-
-			}
-
-		}
-		buildMetalRoughTexture( metalnessMap, roughnessMap ) {
-
-			if ( metalnessMap === roughnessMap ) return metalnessMap;
-			function getEncodingConversion( map ) {
-
-				if ( map.encoding === THREE.sRGBEncoding ) {
-
-					return function SRGBToLinear( c ) {
-
-						return c < 0.04045 ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 );
-
-					};
-
-				}
-
-				return function LinearToLinear( c ) {
-
-					return c;
-
-				};
-
-			}
-
-			console.warn( 'THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.' );
-			const metalness = metalnessMap?.image;
-			const roughness = roughnessMap?.image;
-			const width = Math.max( metalness?.width || 0, roughness?.width || 0 );
-			const height = Math.max( metalness?.height || 0, roughness?.height || 0 );
-			const canvas = getCanvas();
-			canvas.width = width;
-			canvas.height = height;
-			const context = canvas.getContext( '2d' );
-			context.fillStyle = '#00ffff';
-			context.fillRect( 0, 0, width, height );
-			const composite = context.getImageData( 0, 0, width, height );
-			if ( metalness ) {
-
-				context.drawImage( metalness, 0, 0, width, height );
-				const convert = getEncodingConversion( metalnessMap );
-				const data = context.getImageData( 0, 0, width, height ).data;
-				for ( let i = 2; i < data.length; i += 4 ) {
-
-					composite.data[ i ] = convert( data[ i ] / 256 ) * 256;
-
-				}
-
-			}
-
-			if ( roughness ) {
-
-				context.drawImage( roughness, 0, 0, width, height );
-				const convert = getEncodingConversion( roughnessMap );
-				const data = context.getImageData( 0, 0, width, height ).data;
-				for ( let i = 1; i < data.length; i += 4 ) {
-
-					composite.data[ i ] = convert( data[ i ] / 256 ) * 256;
-
-				}
-
-			}
-
-			context.putImageData( composite, 0, 0 );
-
-			//
-
-			const reference = metalnessMap || roughnessMap;
-			const texture = reference.clone();
-			texture.source = new THREE.Source( canvas );
-			texture.encoding = THREE.LinearEncoding;
-			return texture;
-
-		}
-
-		/**
-   * Process a buffer to append to the default one.
-   * @param  {ArrayBuffer} buffer
-   * @return {Integer}
-   */
-		processBuffer( buffer ) {
-
-			const json = this.json;
-			const buffers = this.buffers;
-			if ( ! json.buffers ) json.buffers = [ {
-				byteLength: 0
-			} ];
-
-			// All buffers are merged before export.
-			buffers.push( buffer );
-			return 0;
-
-		}
-
-		/**
-   * Process and generate a BufferView
-   * @param  {BufferAttribute} attribute
-   * @param  {number} componentType
-   * @param  {number} start
-   * @param  {number} count
-   * @param  {number} target (Optional) Target usage of the BufferView
-   * @return {Object}
-   */
-		processBufferView( attribute, componentType, start, count, target ) {
-
-			const json = this.json;
-			if ( ! json.bufferViews ) json.bufferViews = [];
-
-			// Create a new dataview and dump the attribute's array into it
-
-			let componentSize;
-			if ( componentType === WEBGL_CONSTANTS.UNSIGNED_BYTE ) {
-
-				componentSize = 1;
-
-			} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) {
-
-				componentSize = 2;
-
-			} else {
-
-				componentSize = 4;
-
-			}
-
-			const byteLength = getPaddedBufferSize( count * attribute.itemSize * componentSize );
-			const dataView = new DataView( new ArrayBuffer( byteLength ) );
-			let offset = 0;
-			for ( let i = start; i < start + count; i ++ ) {
-
-				for ( let a = 0; a < attribute.itemSize; a ++ ) {
-
-					let value;
-					if ( attribute.itemSize > 4 ) {
-
-						// no support for interleaved data for itemSize > 4
-
-						value = attribute.array[ i * attribute.itemSize + a ];
-
-					} else {
-
-						if ( a === 0 ) value = attribute.getX( i ); else if ( a === 1 ) value = attribute.getY( i ); else if ( a === 2 ) value = attribute.getZ( i ); else if ( a === 3 ) value = attribute.getW( i );
-
-					}
-
-					if ( componentType === WEBGL_CONSTANTS.FLOAT ) {
-
-						dataView.setFloat32( offset, value, true );
-
-					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_INT ) {
-
-						dataView.setUint32( offset, value, true );
-
-					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_SHORT ) {
-
-						dataView.setUint16( offset, value, true );
-
-					} else if ( componentType === WEBGL_CONSTANTS.UNSIGNED_BYTE ) {
-
-						dataView.setUint8( offset, value );
-
-					}
-
-					offset += componentSize;
-
-				}
-
-			}
-
-			const bufferViewDef = {
-				buffer: this.processBuffer( dataView.buffer ),
-				byteOffset: this.byteOffset,
-				byteLength: byteLength
-			};
-			if ( target !== undefined ) bufferViewDef.target = target;
-			if ( target === WEBGL_CONSTANTS.ARRAY_BUFFER ) {
-
-				// Only define byteStride for vertex attributes.
-				bufferViewDef.byteStride = attribute.itemSize * componentSize;
-
-			}
-
-			this.byteOffset += byteLength;
-			json.bufferViews.push( bufferViewDef );
-
-			// @TODO Merge bufferViews where possible.
-			const output = {
-				id: json.bufferViews.length - 1,
-				byteLength: 0
-			};
-			return output;
-
-		}
-
-		/**
-   * Process and generate a BufferView from an image Blob.
-   * @param {Blob} blob
-   * @return {Promise<Integer>}
-   */
-		processBufferViewImage( blob ) {
-
-			const writer = this;
-			const json = writer.json;
-			if ( ! json.bufferViews ) json.bufferViews = [];
-			return new Promise( function ( resolve ) {
-
-				const reader = new FileReader();
-				reader.readAsArrayBuffer( blob );
-				reader.onloadend = function () {
-
-					const buffer = getPaddedArrayBuffer( reader.result );
-					const bufferViewDef = {
-						buffer: writer.processBuffer( buffer ),
-						byteOffset: writer.byteOffset,
-						byteLength: buffer.byteLength
-					};
-					writer.byteOffset += buffer.byteLength;
-					resolve( json.bufferViews.push( bufferViewDef ) - 1 );
-
-				};
-
-			} );
-
-		}
-
-		/**
-   * Process attribute to generate an accessor
-   * @param  {BufferAttribute} attribute Attribute to process
-   * @param  {THREE.BufferGeometry} geometry (Optional) Geometry used for truncated draw range
-   * @param  {Integer} start (Optional)
-   * @param  {Integer} count (Optional)
-   * @return {Integer|null} Index of the processed accessor on the "accessors" array
-   */
-		processAccessor( attribute, geometry, start, count ) {
-
-			const json = this.json;
-			const types = {
-				1: 'SCALAR',
-				2: 'VEC2',
-				3: 'VEC3',
-				4: 'VEC4',
-				16: 'MAT4'
-			};
-			let componentType;
-
-			// Detect the component type of the attribute array (float, uint or ushort)
-			if ( attribute.array.constructor === Float32Array ) {
-
-				componentType = WEBGL_CONSTANTS.FLOAT;
-
-			} else if ( attribute.array.constructor === Uint32Array ) {
-
-				componentType = WEBGL_CONSTANTS.UNSIGNED_INT;
-
-			} else if ( attribute.array.constructor === Uint16Array ) {
-
-				componentType = WEBGL_CONSTANTS.UNSIGNED_SHORT;
-
-			} else if ( attribute.array.constructor === Uint8Array ) {
-
-				componentType = WEBGL_CONSTANTS.UNSIGNED_BYTE;
-
-			} else {
-
-				throw new Error( 'THREE.GLTFExporter: Unsupported bufferAttribute component type.' );
-
-			}
-
-			if ( start === undefined ) start = 0;
-			if ( count === undefined ) count = attribute.count;
-
-			// Skip creating an accessor if the attribute doesn't have data to export
-			if ( count === 0 ) return null;
-			const minMax = getMinMax( attribute, start, count );
-			let bufferViewTarget;
-
-			// If geometry isn't provided, don't infer the target usage of the bufferView. For
-			// animation samplers, target must not be set.
-			if ( geometry !== undefined ) {
-
-				bufferViewTarget = attribute === geometry.index ? WEBGL_CONSTANTS.ELEMENT_ARRAY_BUFFER : WEBGL_CONSTANTS.ARRAY_BUFFER;
-
-			}
-
-			const bufferView = this.processBufferView( attribute, componentType, start, count, bufferViewTarget );
-			const accessorDef = {
-				bufferView: bufferView.id,
-				byteOffset: bufferView.byteOffset,
-				componentType: componentType,
-				count: count,
-				max: minMax.max,
-				min: minMax.min,
-				type: types[ attribute.itemSize ]
-			};
-			if ( attribute.normalized === true ) accessorDef.normalized = true;
-			if ( ! json.accessors ) json.accessors = [];
-			return json.accessors.push( accessorDef ) - 1;
-
-		}
-
-		/**
-   * Process image
-   * @param  {Image} image to process
-   * @param  {Integer} format of the image (THREE.RGBAFormat)
-   * @param  {Boolean} flipY before writing out the image
-   * @param  {String} mimeType export format
-   * @return {Integer}     Index of the processed texture in the "images" array
-   */
-		processImage( image, format, flipY, mimeType = 'image/png' ) {
-
-			const writer = this;
-			const cache = writer.cache;
-			const json = writer.json;
-			const options = writer.options;
-			const pending = writer.pending;
-			if ( ! cache.images.has( image ) ) cache.images.set( image, {} );
-			const cachedImages = cache.images.get( image );
-			const key = mimeType + ':flipY/' + flipY.toString();
-			if ( cachedImages[ key ] !== undefined ) return cachedImages[ key ];
-			if ( ! json.images ) json.images = [];
-			const imageDef = {
-				mimeType: mimeType
-			};
-			const canvas = getCanvas();
-			canvas.width = Math.min( image.width, options.maxTextureSize );
-			canvas.height = Math.min( image.height, options.maxTextureSize );
-			const ctx = canvas.getContext( '2d' );
-			if ( flipY === true ) {
-
-				ctx.translate( 0, canvas.height );
-				ctx.scale( 1, - 1 );
-
-			}
-
-			if ( image.data !== undefined ) {
-
-				// THREE.DataTexture
-
-				if ( format !== THREE.RGBAFormat ) {
-
-					console.error( 'GLTFExporter: Only THREE.RGBAFormat is supported.' );
-
-				}
-
-				if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
-
-					console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
-
-				}
-
-				const data = new Uint8ClampedArray( image.height * image.width * 4 );
-				for ( let i = 0; i < data.length; i += 4 ) {
-
-					data[ i + 0 ] = image.data[ i + 0 ];
-					data[ i + 1 ] = image.data[ i + 1 ];
-					data[ i + 2 ] = image.data[ i + 2 ];
-					data[ i + 3 ] = image.data[ i + 3 ];
-
-				}
-
-				ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
-
-			} else {
-
-				ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
-
-			}
-
-			if ( options.binary === true ) {
-
-				pending.push( getToBlobPromise( canvas, mimeType ).then( blob => writer.processBufferViewImage( blob ) ).then( bufferViewIndex => {
-
-					imageDef.bufferView = bufferViewIndex;
-
-				} ) );
-
-			} else {
-
-				if ( canvas.toDataURL !== undefined ) {
-
-					imageDef.uri = canvas.toDataURL( mimeType );
-
-				} else {
-
-					pending.push( getToBlobPromise( canvas, mimeType ).then( blob => new FileReader().readAsDataURL( blob ) ).then( dataURL => {
-
-						imageDef.uri = dataURL;
-
-					} ) );
-
-				}
-
-			}
-
-			const index = json.images.push( imageDef ) - 1;
-			cachedImages[ key ] = index;
-			return index;
-
-		}
-
-		/**
-   * Process sampler
-   * @param  {Texture} map Texture to process
-   * @return {Integer}     Index of the processed texture in the "samplers" array
-   */
-		processSampler( map ) {
-
-			const json = this.json;
-			if ( ! json.samplers ) json.samplers = [];
-			const samplerDef = {
-				magFilter: THREE_TO_WEBGL[ map.magFilter ],
-				minFilter: THREE_TO_WEBGL[ map.minFilter ],
-				wrapS: THREE_TO_WEBGL[ map.wrapS ],
-				wrapT: THREE_TO_WEBGL[ map.wrapT ]
-			};
-			return json.samplers.push( samplerDef ) - 1;
-
-		}
-
-		/**
-   * Process texture
-   * @param  {Texture} map Map to process
-   * @return {Integer} Index of the processed texture in the "textures" array
-   */
-		processTexture( map ) {
-
-			const cache = this.cache;
-			const json = this.json;
-			if ( cache.textures.has( map ) ) return cache.textures.get( map );
-			if ( ! json.textures ) json.textures = [];
-			let mimeType = map.userData.mimeType;
-			if ( mimeType === 'image/webp' ) mimeType = 'image/png';
-			const textureDef = {
-				sampler: this.processSampler( map ),
-				source: this.processImage( map.image, map.format, map.flipY, mimeType )
-			};
-			if ( map.name ) textureDef.name = map.name;
-			this._invokeAll( function ( ext ) {
-
-				ext.writeTexture && ext.writeTexture( map, textureDef );
-
-			} );
-			const index = json.textures.push( textureDef ) - 1;
-			cache.textures.set( map, index );
-			return index;
-
-		}
-
-		/**
-   * Process material
-   * @param  {THREE.Material} material Material to process
-   * @return {Integer|null} Index of the processed material in the "materials" array
-   */
-		processMaterial( material ) {
-
-			const cache = this.cache;
-			const json = this.json;
-			if ( cache.materials.has( material ) ) return cache.materials.get( material );
-			if ( material.isShaderMaterial ) {
-
-				console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
-				return null;
-
-			}
-
-			if ( ! json.materials ) json.materials = [];
-
-			// @QUESTION Should we avoid including any attribute that has the default value?
-			const materialDef = {
-				pbrMetallicRoughness: {}
-			};
-			if ( material.isMeshStandardMaterial !== true && material.isMeshBasicMaterial !== true ) {
-
-				console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' );
-
-			}
-
-			// pbrMetallicRoughness.baseColorFactor
-			const color = material.color.toArray().concat( [ material.opacity ] );
-			if ( ! equalArray( color, [ 1, 1, 1, 1 ] ) ) {
-
-				materialDef.pbrMetallicRoughness.baseColorFactor = color;
-
-			}
-
-			if ( material.isMeshStandardMaterial ) {
-
-				materialDef.pbrMetallicRoughness.metallicFactor = material.metalness;
-				materialDef.pbrMetallicRoughness.roughnessFactor = material.roughness;
-
-			} else {
-
-				materialDef.pbrMetallicRoughness.metallicFactor = 0.5;
-				materialDef.pbrMetallicRoughness.roughnessFactor = 0.5;
-
-			}
-
-			// pbrMetallicRoughness.metallicRoughnessTexture
-			if ( material.metalnessMap || material.roughnessMap ) {
-
-				const metalRoughTexture = this.buildMetalRoughTexture( material.metalnessMap, material.roughnessMap );
-				const metalRoughMapDef = {
-					index: this.processTexture( metalRoughTexture )
-				};
-				this.applyTextureTransform( metalRoughMapDef, metalRoughTexture );
-				materialDef.pbrMetallicRoughness.metallicRoughnessTexture = metalRoughMapDef;
-
-			}
-
-			// pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture
-			if ( material.map ) {
-
-				const baseColorMapDef = {
-					index: this.processTexture( material.map )
-				};
-				this.applyTextureTransform( baseColorMapDef, material.map );
-				materialDef.pbrMetallicRoughness.baseColorTexture = baseColorMapDef;
-
-			}
-
-			if ( material.emissive ) {
-
-				// note: emissive components are limited to stay within the 0 - 1 range to accommodate glTF spec. see #21849 and #22000.
-				const emissive = material.emissive.clone().multiplyScalar( material.emissiveIntensity );
-				const maxEmissiveComponent = Math.max( emissive.r, emissive.g, emissive.b );
-				if ( maxEmissiveComponent > 1 ) {
-
-					emissive.multiplyScalar( 1 / maxEmissiveComponent );
-					console.warn( 'THREE.GLTFExporter: Some emissive components exceed 1; emissive has been limited' );
-
-				}
-
-				if ( maxEmissiveComponent > 0 ) {
-
-					materialDef.emissiveFactor = emissive.toArray();
-
-				}
-
-				// emissiveTexture
-				if ( material.emissiveMap ) {
-
-					const emissiveMapDef = {
-						index: this.processTexture( material.emissiveMap )
-					};
-					this.applyTextureTransform( emissiveMapDef, material.emissiveMap );
-					materialDef.emissiveTexture = emissiveMapDef;
-
-				}
-
-			}
-
-			// normalTexture
-			if ( material.normalMap ) {
-
-				const normalMapDef = {
-					index: this.processTexture( material.normalMap )
-				};
-				if ( material.normalScale && material.normalScale.x !== 1 ) {
-
-					// glTF normal scale is univariate. Ignore `y`, which may be flipped.
-					// Context: https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-					normalMapDef.scale = material.normalScale.x;
-
-				}
-
-				this.applyTextureTransform( normalMapDef, material.normalMap );
-				materialDef.normalTexture = normalMapDef;
-
-			}
-
-			// occlusionTexture
-			if ( material.aoMap ) {
-
-				const occlusionMapDef = {
-					index: this.processTexture( material.aoMap ),
-					texCoord: 1
-				};
-				if ( material.aoMapIntensity !== 1.0 ) {
-
-					occlusionMapDef.strength = material.aoMapIntensity;
-
-				}
-
-				this.applyTextureTransform( occlusionMapDef, material.aoMap );
-				materialDef.occlusionTexture = occlusionMapDef;
-
-			}
-
-			// alphaMode
-			if ( material.transparent ) {
-
-				materialDef.alphaMode = 'BLEND';
-
-			} else {
-
-				if ( material.alphaTest > 0.0 ) {
-
-					materialDef.alphaMode = 'MASK';
-					materialDef.alphaCutoff = material.alphaTest;
-
-				}
-
-			}
-
-			// doubleSided
-			if ( material.side === THREE.DoubleSide ) materialDef.doubleSided = true;
-			if ( material.name !== '' ) materialDef.name = material.name;
-			this.serializeUserData( material, materialDef );
-			this._invokeAll( function ( ext ) {
-
-				ext.writeMaterial && ext.writeMaterial( material, materialDef );
-
-			} );
-			const index = json.materials.push( materialDef ) - 1;
-			cache.materials.set( material, index );
-			return index;
-
-		}
-
-		/**
-   * Process mesh
-   * @param  {THREE.Mesh} mesh Mesh to process
-   * @return {Integer|null} Index of the processed mesh in the "meshes" array
-   */
-		processMesh( mesh ) {
-
-			const cache = this.cache;
-			const json = this.json;
-			const meshCacheKeyParts = [ mesh.geometry.uuid ];
-			if ( Array.isArray( mesh.material ) ) {
-
-				for ( let i = 0, l = mesh.material.length; i < l; i ++ ) {
-
-					meshCacheKeyParts.push( mesh.material[ i ].uuid );
-
-				}
-
-			} else {
-
-				meshCacheKeyParts.push( mesh.material.uuid );
-
-			}
-
-			const meshCacheKey = meshCacheKeyParts.join( ':' );
-			if ( cache.meshes.has( meshCacheKey ) ) return cache.meshes.get( meshCacheKey );
-			const geometry = mesh.geometry;
-			let mode;
-
-			// Use the correct mode
-			if ( mesh.isLineSegments ) {
-
-				mode = WEBGL_CONSTANTS.LINES;
-
-			} else if ( mesh.isLineLoop ) {
-
-				mode = WEBGL_CONSTANTS.LINE_LOOP;
-
-			} else if ( mesh.isLine ) {
-
-				mode = WEBGL_CONSTANTS.LINE_STRIP;
-
-			} else if ( mesh.isPoints ) {
-
-				mode = WEBGL_CONSTANTS.POINTS;
-
-			} else {
-
-				mode = mesh.material.wireframe ? WEBGL_CONSTANTS.LINES : WEBGL_CONSTANTS.TRIANGLES;
-
-			}
-
-			const meshDef = {};
-			const attributes = {};
-			const primitives = [];
-			const targets = [];
-
-			// Conversion between attributes names in threejs and gltf spec
-			const nameConversion = {
-				uv: 'TEXCOORD_0',
-				uv2: 'TEXCOORD_1',
-				color: 'COLOR_0',
-				skinWeight: 'WEIGHTS_0',
-				skinIndex: 'JOINTS_0'
-			};
-			const originalNormal = geometry.getAttribute( 'normal' );
-			if ( originalNormal !== undefined && ! this.isNormalizedNormalAttribute( originalNormal ) ) {
-
-				console.warn( 'THREE.GLTFExporter: Creating normalized normal attribute from the non-normalized one.' );
-				geometry.setAttribute( 'normal', this.createNormalizedNormalAttribute( originalNormal ) );
-
-			}
-
-			// @QUESTION Detect if .vertexColors = true?
-			// For every attribute create an accessor
-			let modifiedAttribute = null;
-			for ( let attributeName in geometry.attributes ) {
-
-				// Ignore morph target attributes, which are exported later.
-				if ( attributeName.slice( 0, 5 ) === 'morph' ) continue;
-				const attribute = geometry.attributes[ attributeName ];
-				attributeName = nameConversion[ attributeName ] || attributeName.toUpperCase();
-
-				// Prefix all geometry attributes except the ones specifically
-				// listed in the spec; non-spec attributes are considered custom.
-				const validVertexAttributes = /^(POSITION|NORMAL|TANGENT|TEXCOORD_\d+|COLOR_\d+|JOINTS_\d+|WEIGHTS_\d+)$/;
-				if ( ! validVertexAttributes.test( attributeName ) ) attributeName = '_' + attributeName;
-				if ( cache.attributes.has( this.getUID( attribute ) ) ) {
-
-					attributes[ attributeName ] = cache.attributes.get( this.getUID( attribute ) );
-					continue;
-
-				}
-
-				// JOINTS_0 must be UNSIGNED_BYTE or UNSIGNED_SHORT.
-				modifiedAttribute = null;
-				const array = attribute.array;
-				if ( attributeName === 'JOINTS_0' && ! ( array instanceof Uint16Array ) && ! ( array instanceof Uint8Array ) ) {
-
-					console.warn( 'GLTFExporter: Attribute "skinIndex" converted to type UNSIGNED_SHORT.' );
-					modifiedAttribute = new THREE.BufferAttribute( new Uint16Array( array ), attribute.itemSize, attribute.normalized );
-
-				}
-
-				const accessor = this.processAccessor( modifiedAttribute || attribute, geometry );
-				if ( accessor !== null ) {
-
-					attributes[ attributeName ] = accessor;
-					cache.attributes.set( this.getUID( attribute ), accessor );
-
-				}
-
-			}
-
-			if ( originalNormal !== undefined ) geometry.setAttribute( 'normal', originalNormal );
-
-			// Skip if no exportable attributes found
-			if ( Object.keys( attributes ).length === 0 ) return null;
-
-			// Morph targets
-			if ( mesh.morphTargetInfluences !== undefined && mesh.morphTargetInfluences.length > 0 ) {
-
-				const weights = [];
-				const targetNames = [];
-				const reverseDictionary = {};
-				if ( mesh.morphTargetDictionary !== undefined ) {
-
-					for ( const key in mesh.morphTargetDictionary ) {
-
-						reverseDictionary[ mesh.morphTargetDictionary[ key ] ] = key;
-
-					}
-
-				}
-
-				for ( let i = 0; i < mesh.morphTargetInfluences.length; ++ i ) {
-
-					const target = {};
-					let warned = false;
-					for ( const attributeName in geometry.morphAttributes ) {
-
-						// glTF 2.0 morph supports only POSITION/NORMAL/TANGENT.
-						// Three.js doesn't support TANGENT yet.
-
-						if ( attributeName !== 'position' && attributeName !== 'normal' ) {
-
-							if ( ! warned ) {
-
-								console.warn( 'GLTFExporter: Only POSITION and NORMAL morph are supported.' );
-								warned = true;
-
-							}
-
-							continue;
-
-						}
-
-						const attribute = geometry.morphAttributes[ attributeName ][ i ];
-						const gltfAttributeName = attributeName.toUpperCase();
-
-						// Three.js morph attribute has absolute values while the one of glTF has relative values.
-						//
-						// glTF 2.0 Specification:
-						// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#morph-targets
-
-						const baseAttribute = geometry.attributes[ attributeName ];
-						if ( cache.attributes.has( this.getUID( attribute, true ) ) ) {
-
-							target[ gltfAttributeName ] = cache.attributes.get( this.getUID( attribute, true ) );
-							continue;
-
-						}
-
-						// Clones attribute not to override
-						const relativeAttribute = attribute.clone();
-						if ( ! geometry.morphTargetsRelative ) {
-
-							for ( let j = 0, jl = attribute.count; j < jl; j ++ ) {
-
-								relativeAttribute.setXYZ( j, attribute.getX( j ) - baseAttribute.getX( j ), attribute.getY( j ) - baseAttribute.getY( j ), attribute.getZ( j ) - baseAttribute.getZ( j ) );
-
-							}
-
-						}
-
-						target[ gltfAttributeName ] = this.processAccessor( relativeAttribute, geometry );
-						cache.attributes.set( this.getUID( baseAttribute, true ), target[ gltfAttributeName ] );
-
-					}
-
-					targets.push( target );
-					weights.push( mesh.morphTargetInfluences[ i ] );
-					if ( mesh.morphTargetDictionary !== undefined ) targetNames.push( reverseDictionary[ i ] );
-
-				}
-
-				meshDef.weights = weights;
-				if ( targetNames.length > 0 ) {
-
-					meshDef.extras = {};
-					meshDef.extras.targetNames = targetNames;
-
-				}
-
-			}
-
-			const isMultiMaterial = Array.isArray( mesh.material );
-			if ( isMultiMaterial && geometry.groups.length === 0 ) return null;
-			const materials = isMultiMaterial ? mesh.material : [ mesh.material ];
-			const groups = isMultiMaterial ? geometry.groups : [ {
-				materialIndex: 0,
-				start: undefined,
-				count: undefined
-			} ];
-			for ( let i = 0, il = groups.length; i < il; i ++ ) {
-
-				const primitive = {
-					mode: mode,
-					attributes: attributes
-				};
-				this.serializeUserData( geometry, primitive );
-				if ( targets.length > 0 ) primitive.targets = targets;
-				if ( geometry.index !== null ) {
-
-					let cacheKey = this.getUID( geometry.index );
-					if ( groups[ i ].start !== undefined || groups[ i ].count !== undefined ) {
-
-						cacheKey += ':' + groups[ i ].start + ':' + groups[ i ].count;
-
-					}
-
-					if ( cache.attributes.has( cacheKey ) ) {
-
-						primitive.indices = cache.attributes.get( cacheKey );
-
-					} else {
-
-						primitive.indices = this.processAccessor( geometry.index, geometry, groups[ i ].start, groups[ i ].count );
-						cache.attributes.set( cacheKey, primitive.indices );
-
-					}
-
-					if ( primitive.indices === null ) delete primitive.indices;
-
-				}
-
-				const material = this.processMaterial( materials[ groups[ i ].materialIndex ] );
-				if ( material !== null ) primitive.material = material;
-				primitives.push( primitive );
-
-			}
-
-			meshDef.primitives = primitives;
-			if ( ! json.meshes ) json.meshes = [];
-			this._invokeAll( function ( ext ) {
-
-				ext.writeMesh && ext.writeMesh( mesh, meshDef );
-
-			} );
-			const index = json.meshes.push( meshDef ) - 1;
-			cache.meshes.set( meshCacheKey, index );
-			return index;
-
-		}
-
-		/**
-   * Process camera
-   * @param  {THREE.Camera} camera Camera to process
-   * @return {Integer}      Index of the processed mesh in the "camera" array
-   */
-		processCamera( camera ) {
-
-			const json = this.json;
-			if ( ! json.cameras ) json.cameras = [];
-			const isOrtho = camera.isOrthographicCamera;
-			const cameraDef = {
-				type: isOrtho ? 'orthographic' : 'perspective'
-			};
-			if ( isOrtho ) {
-
-				cameraDef.orthographic = {
-					xmag: camera.right * 2,
-					ymag: camera.top * 2,
-					zfar: camera.far <= 0 ? 0.001 : camera.far,
-					znear: camera.near < 0 ? 0 : camera.near
-				};
-
-			} else {
-
-				cameraDef.perspective = {
-					aspectRatio: camera.aspect,
-					yfov: THREE.MathUtils.degToRad( camera.fov ),
-					zfar: camera.far <= 0 ? 0.001 : camera.far,
-					znear: camera.near < 0 ? 0 : camera.near
-				};
-
-			}
-
-			// Question: Is saving "type" as name intentional?
-			if ( camera.name !== '' ) cameraDef.name = camera.type;
-			return json.cameras.push( cameraDef ) - 1;
-
-		}
-
-		/**
-   * Creates glTF animation entry from AnimationClip object.
-   *
-   * Status:
-   * - Only properties listed in PATH_PROPERTIES may be animated.
-   *
-   * @param {THREE.AnimationClip} clip
-   * @param {THREE.Object3D} root
-   * @return {number|null}
-   */
-		processAnimation( clip, root ) {
-
-			const json = this.json;
-			const nodeMap = this.nodeMap;
-			if ( ! json.animations ) json.animations = [];
-			clip = GLTFExporter.Utils.mergeMorphTargetTracks( clip.clone(), root );
-			const tracks = clip.tracks;
-			const channels = [];
-			const samplers = [];
-			for ( let i = 0; i < tracks.length; ++ i ) {
-
-				const track = tracks[ i ];
-				const trackBinding = THREE.PropertyBinding.parseTrackName( track.name );
-				let trackNode = THREE.PropertyBinding.findNode( root, trackBinding.nodeName );
-				const trackProperty = PATH_PROPERTIES[ trackBinding.propertyName ];
-				if ( trackBinding.objectName === 'bones' ) {
-
-					if ( trackNode.isSkinnedMesh === true ) {
-
-						trackNode = trackNode.skeleton.getBoneByName( trackBinding.objectIndex );
-
-					} else {
-
-						trackNode = undefined;
-
-					}
-
-				}
-
-				if ( ! trackNode || ! trackProperty ) {
-
-					console.warn( 'THREE.GLTFExporter: Could not export animation track "%s".', track.name );
-					return null;
-
-				}
-
-				const inputItemSize = 1;
-				let outputItemSize = track.values.length / track.times.length;
-				if ( trackProperty === PATH_PROPERTIES.morphTargetInfluences ) {
-
-					outputItemSize /= trackNode.morphTargetInfluences.length;
-
-				}
-
-				let interpolation;
-
-				// @TODO export CubicInterpolant(InterpolateSmooth) as CUBICSPLINE
-
-				// Detecting glTF cubic spline interpolant by checking factory method's special property
-				// GLTFCubicSplineInterpolant is a custom interpolant and track doesn't return
-				// valid value from .getInterpolation().
-				if ( track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline === true ) {
-
-					interpolation = 'CUBICSPLINE';
-
-					// itemSize of CUBICSPLINE keyframe is 9
-					// (VEC3 * 3: inTangent, splineVertex, and outTangent)
-					// but needs to be stored as VEC3 so dividing by 3 here.
-					outputItemSize /= 3;
-
-				} else if ( track.getInterpolation() === THREE.InterpolateDiscrete ) {
-
-					interpolation = 'STEP';
-
-				} else {
-
-					interpolation = 'LINEAR';
-
-				}
-
-				samplers.push( {
-					input: this.processAccessor( new THREE.BufferAttribute( track.times, inputItemSize ) ),
-					output: this.processAccessor( new THREE.BufferAttribute( track.values, outputItemSize ) ),
-					interpolation: interpolation
-				} );
-				channels.push( {
-					sampler: samplers.length - 1,
-					target: {
-						node: nodeMap.get( trackNode ),
-						path: trackProperty
-					}
-				} );
-
-			}
-
-			json.animations.push( {
-				name: clip.name || 'clip_' + json.animations.length,
-				samplers: samplers,
-				channels: channels
-			} );
-			return json.animations.length - 1;
-
-		}
-
-		/**
-   * @param {THREE.Object3D} object
-   * @return {number|null}
-   */
-		processSkin( object ) {
-
-			const json = this.json;
-			const nodeMap = this.nodeMap;
-			const node = json.nodes[ nodeMap.get( object ) ];
-			const skeleton = object.skeleton;
-			if ( skeleton === undefined ) return null;
-			const rootJoint = object.skeleton.bones[ 0 ];
-			if ( rootJoint === undefined ) return null;
-			const joints = [];
-			const inverseBindMatrices = new Float32Array( skeleton.bones.length * 16 );
-			const temporaryBoneInverse = new THREE.Matrix4();
-			for ( let i = 0; i < skeleton.bones.length; ++ i ) {
-
-				joints.push( nodeMap.get( skeleton.bones[ i ] ) );
-				temporaryBoneInverse.copy( skeleton.boneInverses[ i ] );
-				temporaryBoneInverse.multiply( object.bindMatrix ).toArray( inverseBindMatrices, i * 16 );
-
-			}
-
-			if ( json.skins === undefined ) json.skins = [];
-			json.skins.push( {
-				inverseBindMatrices: this.processAccessor( new THREE.BufferAttribute( inverseBindMatrices, 16 ) ),
-				joints: joints,
-				skeleton: nodeMap.get( rootJoint )
-			} );
-			const skinIndex = node.skin = json.skins.length - 1;
-			return skinIndex;
-
-		}
-
-		/**
-   * Process Object3D node
-   * @param  {THREE.Object3D} node Object3D to processNode
-   * @return {Integer} Index of the node in the nodes list
-   */
-		processNode( object ) {
-
-			const json = this.json;
-			const options = this.options;
-			const nodeMap = this.nodeMap;
-			if ( ! json.nodes ) json.nodes = [];
-			const nodeDef = {};
-			if ( options.trs ) {
-
-				const rotation = object.quaternion.toArray();
-				const position = object.position.toArray();
-				const scale = object.scale.toArray();
-				if ( ! equalArray( rotation, [ 0, 0, 0, 1 ] ) ) {
-
-					nodeDef.rotation = rotation;
-
-				}
-
-				if ( ! equalArray( position, [ 0, 0, 0 ] ) ) {
-
-					nodeDef.translation = position;
-
-				}
-
-				if ( ! equalArray( scale, [ 1, 1, 1 ] ) ) {
-
-					nodeDef.scale = scale;
-
-				}
-
-			} else {
-
-				if ( object.matrixAutoUpdate ) {
-
-					object.updateMatrix();
-
-				}
-
-				if ( isIdentityMatrix( object.matrix ) === false ) {
-
-					nodeDef.matrix = object.matrix.elements;
-
-				}
-
-			}
-
-			// We don't export empty strings name because it represents no-name in Three.js.
-			if ( object.name !== '' ) nodeDef.name = String( object.name );
-			this.serializeUserData( object, nodeDef );
-			if ( object.isMesh || object.isLine || object.isPoints ) {
-
-				const meshIndex = this.processMesh( object );
-				if ( meshIndex !== null ) nodeDef.mesh = meshIndex;
-
-			} else if ( object.isCamera ) {
-
-				nodeDef.camera = this.processCamera( object );
-
-			}
-
-			if ( object.isSkinnedMesh ) this.skins.push( object );
-			if ( object.children.length > 0 ) {
-
-				const children = [];
-				for ( let i = 0, l = object.children.length; i < l; i ++ ) {
-
-					const child = object.children[ i ];
-					if ( child.visible || options.onlyVisible === false ) {
-
-						const nodeIndex = this.processNode( child );
-						if ( nodeIndex !== null ) children.push( nodeIndex );
-
-					}
-
-				}
-
-				if ( children.length > 0 ) nodeDef.children = children;
-
-			}
-
-			this._invokeAll( function ( ext ) {
-
-				ext.writeNode && ext.writeNode( object, nodeDef );
-
-			} );
-			const nodeIndex = json.nodes.push( nodeDef ) - 1;
-			nodeMap.set( object, nodeIndex );
-			return nodeIndex;
-
-		}
-
-		/**
-   * Process THREE.Scene
-   * @param  {Scene} node THREE.Scene to process
-   */
-		processScene( scene ) {
-
-			const json = this.json;
-			const options = this.options;
-			if ( ! json.scenes ) {
-
-				json.scenes = [];
-				json.scene = 0;
-
-			}
-
-			const sceneDef = {};
-			if ( scene.name !== '' ) sceneDef.name = scene.name;
-			json.scenes.push( sceneDef );
-			const nodes = [];
-			for ( let i = 0, l = scene.children.length; i < l; i ++ ) {
-
-				const child = scene.children[ i ];
-				if ( child.visible || options.onlyVisible === false ) {
-
-					const nodeIndex = this.processNode( child );
-					if ( nodeIndex !== null ) nodes.push( nodeIndex );
-
-				}
-
-			}
-
-			if ( nodes.length > 0 ) sceneDef.nodes = nodes;
-			this.serializeUserData( scene, sceneDef );
-
-		}
-
-		/**
-   * Creates a THREE.Scene to hold a list of objects and parse it
-   * @param  {Array} objects List of objects to process
-   */
-		processObjects( objects ) {
-
-			const scene = new THREE.Scene();
-			scene.name = 'AuxScene';
-			for ( let i = 0; i < objects.length; i ++ ) {
-
-				// We push directly to children instead of calling `add` to prevent
-				// modify the .parent and break its original scene and hierarchy
-				scene.children.push( objects[ i ] );
-
-			}
-
-			this.processScene( scene );
-
-		}
-
-		/**
-   * @param {THREE.Object3D|Array<THREE.Object3D>} input
-   */
-		processInput( input ) {
-
-			const options = this.options;
-			input = input instanceof Array ? input : [ input ];
-			this._invokeAll( function ( ext ) {
-
-				ext.beforeParse && ext.beforeParse( input );
-
-			} );
-			const objectsWithoutScene = [];
-			for ( let i = 0; i < input.length; i ++ ) {
-
-				if ( input[ i ] instanceof THREE.Scene ) {
-
-					this.processScene( input[ i ] );
-
-				} else {
-
-					objectsWithoutScene.push( input[ i ] );
-
-				}
-
-			}
-
-			if ( objectsWithoutScene.length > 0 ) this.processObjects( objectsWithoutScene );
-			for ( let i = 0; i < this.skins.length; ++ i ) {
-
-				this.processSkin( this.skins[ i ] );
-
-			}
-
-			for ( let i = 0; i < options.animations.length; ++ i ) {
-
-				this.processAnimation( options.animations[ i ], input[ 0 ] );
-
-			}
-
-			this._invokeAll( function ( ext ) {
-
-				ext.afterParse && ext.afterParse( input );
-
-			} );
-
-		}
-		_invokeAll( func ) {
-
-			for ( let i = 0, il = this.plugins.length; i < il; i ++ ) {
-
-				func( this.plugins[ i ] );
-
-			}
-
-		}
-
-	}
-
-	/**
- * Punctual Lights Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
- */
-	class GLTFLightExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_lights_punctual';
-
-		}
-		writeNode( light, nodeDef ) {
-
-			if ( ! light.isLight ) return;
-			if ( ! light.isDirectionalLight && ! light.isPointLight && ! light.isSpotLight ) {
-
-				console.warn( 'THREE.GLTFExporter: Only directional, point, and spot lights are supported.', light );
-				return;
-
-			}
-
-			const writer = this.writer;
-			const json = writer.json;
-			const extensionsUsed = writer.extensionsUsed;
-			const lightDef = {};
-			if ( light.name ) lightDef.name = light.name;
-			lightDef.color = light.color.toArray();
-			lightDef.intensity = light.intensity;
-			if ( light.isDirectionalLight ) {
-
-				lightDef.type = 'directional';
-
-			} else if ( light.isPointLight ) {
-
-				lightDef.type = 'point';
-				if ( light.distance > 0 ) lightDef.range = light.distance;
-
-			} else if ( light.isSpotLight ) {
-
-				lightDef.type = 'spot';
-				if ( light.distance > 0 ) lightDef.range = light.distance;
-				lightDef.spot = {};
-				lightDef.spot.innerConeAngle = ( light.penumbra - 1.0 ) * light.angle * - 1.0;
-				lightDef.spot.outerConeAngle = light.angle;
-
-			}
-
-			if ( light.decay !== undefined && light.decay !== 2 ) {
-
-				console.warn( 'THREE.GLTFExporter: Light decay may be lost. glTF is physically-based, ' + 'and expects light.decay=2.' );
-
-			}
-
-			if ( light.target && ( light.target.parent !== light || light.target.position.x !== 0 || light.target.position.y !== 0 || light.target.position.z !== - 1 ) ) {
-
-				console.warn( 'THREE.GLTFExporter: Light direction may be lost. For best results, ' + 'make light.target a child of the light with position 0,0,-1.' );
-
-			}
-
-			if ( ! extensionsUsed[ this.name ] ) {
-
-				json.extensions = json.extensions || {};
-				json.extensions[ this.name ] = {
-					lights: []
-				};
-				extensionsUsed[ this.name ] = true;
-
-			}
-
-			const lights = json.extensions[ this.name ].lights;
-			lights.push( lightDef );
-			nodeDef.extensions = nodeDef.extensions || {};
-			nodeDef.extensions[ this.name ] = {
-				light: lights.length - 1
-			};
-
-		}
-
-	}
-
-	/**
- * Unlit Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
- */
-	class GLTFMaterialsUnlitExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_materials_unlit';
-
-		}
-		writeMaterial( material, materialDef ) {
-
-			if ( ! material.isMeshBasicMaterial ) return;
-			const writer = this.writer;
-			const extensionsUsed = writer.extensionsUsed;
-			materialDef.extensions = materialDef.extensions || {};
-			materialDef.extensions[ this.name ] = {};
-			extensionsUsed[ this.name ] = true;
-			materialDef.pbrMetallicRoughness.metallicFactor = 0.0;
-			materialDef.pbrMetallicRoughness.roughnessFactor = 0.9;
-
-		}
-
-	}
-
-	/**
- * Clearcoat Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
- */
-	class GLTFMaterialsClearcoatExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_materials_clearcoat';
-
-		}
-		writeMaterial( material, materialDef ) {
-
-			if ( ! material.isMeshPhysicalMaterial ) return;
-			const writer = this.writer;
-			const extensionsUsed = writer.extensionsUsed;
-			const extensionDef = {};
-			extensionDef.clearcoatFactor = material.clearcoat;
-			if ( material.clearcoatMap ) {
-
-				const clearcoatMapDef = {
-					index: writer.processTexture( material.clearcoatMap )
-				};
-				writer.applyTextureTransform( clearcoatMapDef, material.clearcoatMap );
-				extensionDef.clearcoatTexture = clearcoatMapDef;
-
-			}
-
-			extensionDef.clearcoatRoughnessFactor = material.clearcoatRoughness;
-			if ( material.clearcoatRoughnessMap ) {
-
-				const clearcoatRoughnessMapDef = {
-					index: writer.processTexture( material.clearcoatRoughnessMap )
-				};
-				writer.applyTextureTransform( clearcoatRoughnessMapDef, material.clearcoatRoughnessMap );
-				extensionDef.clearcoatRoughnessTexture = clearcoatRoughnessMapDef;
-
-			}
-
-			if ( material.clearcoatNormalMap ) {
-
-				const clearcoatNormalMapDef = {
-					index: writer.processTexture( material.clearcoatNormalMap )
-				};
-				writer.applyTextureTransform( clearcoatNormalMapDef, material.clearcoatNormalMap );
-				extensionDef.clearcoatNormalTexture = clearcoatNormalMapDef;
-
-			}
-
-			materialDef.extensions = materialDef.extensions || {};
-			materialDef.extensions[ this.name ] = extensionDef;
-			extensionsUsed[ this.name ] = true;
-
-		}
-
-	}
-
-	/**
- * Iridescence Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence
- */
-	class GLTFMaterialsIridescenceExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_materials_iridescence';
-
-		}
-		writeMaterial( material, materialDef ) {
-
-			if ( ! material.isMeshPhysicalMaterial ) return;
-			const writer = this.writer;
-			const extensionsUsed = writer.extensionsUsed;
-			const extensionDef = {};
-			extensionDef.iridescenceFactor = material.iridescence;
-			if ( material.iridescenceMap ) {
-
-				const iridescenceMapDef = {
-					index: writer.processTexture( material.iridescenceMap )
-				};
-				writer.applyTextureTransform( iridescenceMapDef, material.iridescenceMap );
-				extensionDef.iridescenceTexture = iridescenceMapDef;
-
-			}
-
-			extensionDef.iridescenceIor = material.iridescenceIOR;
-			extensionDef.iridescenceThicknessMinimum = material.iridescenceThicknessRange[ 0 ];
-			extensionDef.iridescenceThicknessMaximum = material.iridescenceThicknessRange[ 1 ];
-			if ( material.iridescenceThicknessMap ) {
-
-				const iridescenceThicknessMapDef = {
-					index: writer.processTexture( material.iridescenceThicknessMap )
-				};
-				writer.applyTextureTransform( iridescenceThicknessMapDef, material.iridescenceThicknessMap );
-				extensionDef.iridescenceThicknessTexture = iridescenceThicknessMapDef;
-
-			}
-
-			materialDef.extensions = materialDef.extensions || {};
-			materialDef.extensions[ this.name ] = extensionDef;
-			extensionsUsed[ this.name ] = true;
-
-		}
-
-	}
-
-	/**
- * Transmission Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
- */
-	class GLTFMaterialsTransmissionExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_materials_transmission';
-
-		}
-		writeMaterial( material, materialDef ) {
-
-			if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
-			const writer = this.writer;
-			const extensionsUsed = writer.extensionsUsed;
-			const extensionDef = {};
-			extensionDef.transmissionFactor = material.transmission;
-			if ( material.transmissionMap ) {
-
-				const transmissionMapDef = {
-					index: writer.processTexture( material.transmissionMap )
-				};
-				writer.applyTextureTransform( transmissionMapDef, material.transmissionMap );
-				extensionDef.transmissionTexture = transmissionMapDef;
-
-			}
-
-			materialDef.extensions = materialDef.extensions || {};
-			materialDef.extensions[ this.name ] = extensionDef;
-			extensionsUsed[ this.name ] = true;
-
-		}
-
-	}
-
-	/**
- * Materials Volume Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume
- */
-	class GLTFMaterialsVolumeExtension {
-
-		constructor( writer ) {
-
-			this.writer = writer;
-			this.name = 'KHR_materials_volume';
-
-		}
-		writeMaterial( material, materialDef ) {
-
-			if ( ! material.isMeshPhysicalMaterial || material.transmission === 0 ) return;
-			const writer = this.writer;
-			const extensionsUsed = writer.extensionsUsed;
-			const extensionDef = {};
-			extensionDef.thicknessFactor = material.thickness;
-			if ( material.thicknessMap ) {
-
-				const thicknessMapDef = {
-					index: writer.processTexture( material.thicknessMap )
-				};
-				writer.applyTextureTransform( thicknessMapDef, material.thicknessMap );
-				extensionDef.thicknessTexture = thicknessMapDef;
-
-			}
-
-			extensionDef.attenuationDistance = material.attenuationDistance;
-			extensionDef.attenuationColor = material.attenuationColor.toArray();
-			materialDef.extensions = materialDef.extensions || {};
-			materialDef.extensions[ this.name ] = extensionDef;
-			extensionsUsed[ this.name ] = true;
-
-		}
-
-	}
-
-	/**
- * Static utility functions
- */
-	GLTFExporter.Utils = {
-		insertKeyframe: function ( track, time ) {
-
-			const tolerance = 0.001; // 1ms
-			const valueSize = track.getValueSize();
-			const times = new track.TimeBufferType( track.times.length + 1 );
-			const values = new track.ValueBufferType( track.values.length + valueSize );
-			const interpolant = track.createInterpolant( new track.ValueBufferType( valueSize ) );
-			let index;
-			if ( track.times.length === 0 ) {
-
-				times[ 0 ] = time;
-				for ( let i = 0; i < valueSize; i ++ ) {
-
-					values[ i ] = 0;
-
-				}
-
-				index = 0;
-
-			} else if ( time < track.times[ 0 ] ) {
-
-				if ( Math.abs( track.times[ 0 ] - time ) < tolerance ) return 0;
-				times[ 0 ] = time;
-				times.set( track.times, 1 );
-				values.set( interpolant.evaluate( time ), 0 );
-				values.set( track.values, valueSize );
-				index = 0;
-
-			} else if ( time > track.times[ track.times.length - 1 ] ) {
-
-				if ( Math.abs( track.times[ track.times.length - 1 ] - time ) < tolerance ) {
-
-					return track.times.length - 1;
-
-				}
-
-				times[ times.length - 1 ] = time;
-				times.set( track.times, 0 );
-				values.set( track.values, 0 );
-				values.set( interpolant.evaluate( time ), track.values.length );
-				index = times.length - 1;
-
-			} else {
-
-				for ( let i = 0; i < track.times.length; i ++ ) {
-
-					if ( Math.abs( track.times[ i ] - time ) < tolerance ) return i;
-					if ( track.times[ i ] < time && track.times[ i + 1 ] > time ) {
-
-						times.set( track.times.slice( 0, i + 1 ), 0 );
-						times[ i + 1 ] = time;
-						times.set( track.times.slice( i + 1 ), i + 2 );
-						values.set( track.values.slice( 0, ( i + 1 ) * valueSize ), 0 );
-						values.set( interpolant.evaluate( time ), ( i + 1 ) * valueSize );
-						values.set( track.values.slice( ( i + 1 ) * valueSize ), ( i + 2 ) * valueSize );
-						index = i + 1;
-						break;
-
-					}
-
-				}
-
-			}
-
-			track.times = times;
-			track.values = values;
-			return index;
-
-		},
-		mergeMorphTargetTracks: function ( clip, root ) {
-
-			const tracks = [];
-			const mergedTracks = {};
-			const sourceTracks = clip.tracks;
-			for ( let i = 0; i < sourceTracks.length; ++ i ) {
-
-				let sourceTrack = sourceTracks[ i ];
-				const sourceTrackBinding = THREE.PropertyBinding.parseTrackName( sourceTrack.name );
-				const sourceTrackNode = THREE.PropertyBinding.findNode( root, sourceTrackBinding.nodeName );
-				if ( sourceTrackBinding.propertyName !== 'morphTargetInfluences' || sourceTrackBinding.propertyIndex === undefined ) {
-
-					// Tracks that don't affect morph targets, or that affect all morph targets together, can be left as-is.
-					tracks.push( sourceTrack );
-					continue;
-
-				}
-
-				if ( sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodDiscrete && sourceTrack.createInterpolant !== sourceTrack.InterpolantFactoryMethodLinear ) {
-
-					if ( sourceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) {
-
-						// This should never happen, because glTF morph target animations
-						// affect all targets already.
-						throw new Error( 'THREE.GLTFExporter: Cannot merge tracks with glTF CUBICSPLINE interpolation.' );
-
-					}
-
-					console.warn( 'THREE.GLTFExporter: Morph target interpolation mode not yet supported. Using LINEAR instead.' );
-					sourceTrack = sourceTrack.clone();
-					sourceTrack.setInterpolation( THREE.InterpolateLinear );
-
-				}
-
-				const targetCount = sourceTrackNode.morphTargetInfluences.length;
-				const targetIndex = sourceTrackNode.morphTargetDictionary[ sourceTrackBinding.propertyIndex ];
-				if ( targetIndex === undefined ) {
-
-					throw new Error( 'THREE.GLTFExporter: Morph target name not found: ' + sourceTrackBinding.propertyIndex );
-
-				}
-
-				let mergedTrack;
-
-				// If this is the first time we've seen this object, create a new
-				// track to store merged keyframe data for each morph target.
-				if ( mergedTracks[ sourceTrackNode.uuid ] === undefined ) {
-
-					mergedTrack = sourceTrack.clone();
-					const values = new mergedTrack.ValueBufferType( targetCount * mergedTrack.times.length );
-					for ( let j = 0; j < mergedTrack.times.length; j ++ ) {
-
-						values[ j * targetCount + targetIndex ] = mergedTrack.values[ j ];
-
-					}
-
-					// We need to take into consideration the intended target node
-					// of our original un-merged morphTarget animation.
-					mergedTrack.name = ( sourceTrackBinding.nodeName || '' ) + '.morphTargetInfluences';
-					mergedTrack.values = values;
-					mergedTracks[ sourceTrackNode.uuid ] = mergedTrack;
-					tracks.push( mergedTrack );
-					continue;
-
-				}
-
-				const sourceInterpolant = sourceTrack.createInterpolant( new sourceTrack.ValueBufferType( 1 ) );
-				mergedTrack = mergedTracks[ sourceTrackNode.uuid ];
-
-				// For every existing keyframe of the merged track, write a (possibly
-				// interpolated) value from the source track.
-				for ( let j = 0; j < mergedTrack.times.length; j ++ ) {
-
-					mergedTrack.values[ j * targetCount + targetIndex ] = sourceInterpolant.evaluate( mergedTrack.times[ j ] );
-
-				}
-
-				// For every existing keyframe of the source track, write a (possibly
-				// new) keyframe to the merged track. Values from the previous loop may
-				// be written again, but keyframes are de-duplicated.
-				for ( let j = 0; j < sourceTrack.times.length; j ++ ) {
-
-					const keyframeIndex = this.insertKeyframe( mergedTrack, sourceTrack.times[ j ] );
-					mergedTrack.values[ keyframeIndex * targetCount + targetIndex ] = sourceTrack.values[ j ];
-
-				}
-
-			}
-
-			clip.tracks = tracks;
-			return clip;
-
-		}
-	};
-
-	THREE.GLTFExporter = GLTFExporter;
-
-} )();

+ 0 - 187
examples/js/exporters/MMDExporter.js

@@ -1,187 +0,0 @@
-( function () {
-
-	/**
- * Dependencies
- *  - mmd-parser https://github.com/takahirox/mmd-parser
- */
-
-	class MMDExporter {
-
-		/* TODO: implement
-  // mesh -> pmd
-  this.parsePmd = function ( object ) {
-  	};
-  */
-
-		/* TODO: implement
-  // mesh -> pmx
-  this.parsePmx = function ( object ) {
-  	};
-  */
-
-		/* TODO: implement
-  // animation + skeleton -> vmd
-  this.parseVmd = function ( object ) {
-  	};
-  */
-
-		/*
-   * skeleton -> vpd
-   * Returns Shift_JIS encoded Uint8Array. Otherwise return strings.
-   */
-		parseVpd( skin, outputShiftJis, useOriginalBones ) {
-
-			if ( skin.isSkinnedMesh !== true ) {
-
-				console.warn( 'THREE.MMDExporter: parseVpd() requires SkinnedMesh instance.' );
-				return null;
-
-			}
-
-			function toStringsFromNumber( num ) {
-
-				if ( Math.abs( num ) < 1e-6 ) num = 0;
-				let a = num.toString();
-				if ( a.indexOf( '.' ) === - 1 ) {
-
-					a += '.';
-
-				}
-
-				a += '000000';
-				const index = a.indexOf( '.' );
-				const d = a.slice( 0, index );
-				const p = a.slice( index + 1, index + 7 );
-				return d + '.' + p;
-
-			}
-
-			function toStringsFromArray( array ) {
-
-				const a = [];
-				for ( let i = 0, il = array.length; i < il; i ++ ) {
-
-					a.push( toStringsFromNumber( array[ i ] ) );
-
-				}
-
-				return a.join( ',' );
-
-			}
-
-			skin.updateMatrixWorld( true );
-			const bones = skin.skeleton.bones;
-			const bones2 = getBindBones( skin );
-			const position = new THREE.Vector3();
-			const quaternion = new THREE.Quaternion();
-			const quaternion2 = new THREE.Quaternion();
-			const matrix = new THREE.Matrix4();
-			const array = [];
-			array.push( 'Vocaloid Pose Data file' );
-			array.push( '' );
-			array.push( ( skin.name !== '' ? skin.name.replace( /\s/g, '_' ) : 'skin' ) + '.osm;' );
-			array.push( bones.length + ';' );
-			array.push( '' );
-			for ( let i = 0, il = bones.length; i < il; i ++ ) {
-
-				const bone = bones[ i ];
-				const bone2 = bones2[ i ];
-
-				/*
-       * use the bone matrix saved before solving IK.
-       * see CCDIKSolver for the detail.
-       */
-				if ( useOriginalBones === true && bone.userData.ik !== undefined && bone.userData.ik.originalMatrix !== undefined ) {
-
-					matrix.fromArray( bone.userData.ik.originalMatrix );
-
-				} else {
-
-					matrix.copy( bone.matrix );
-
-				}
-
-				position.setFromMatrixPosition( matrix );
-				quaternion.setFromRotationMatrix( matrix );
-				const pArray = position.sub( bone2.position ).toArray();
-				const qArray = quaternion2.copy( bone2.quaternion ).conjugate().multiply( quaternion ).toArray();
-
-				// right to left
-				pArray[ 2 ] = - pArray[ 2 ];
-				qArray[ 0 ] = - qArray[ 0 ];
-				qArray[ 1 ] = - qArray[ 1 ];
-				array.push( 'Bone' + i + '{' + bone.name );
-				array.push( '  ' + toStringsFromArray( pArray ) + ';' );
-				array.push( '  ' + toStringsFromArray( qArray ) + ';' );
-				array.push( '}' );
-				array.push( '' );
-
-			}
-
-			array.push( '' );
-			const lines = array.join( '\n' );
-			return outputShiftJis === true ? unicodeToShiftjis( lines ) : lines;
-
-		}
-
-	}
-
-	// Unicode to Shift_JIS table
-	let u2sTable;
-	function unicodeToShiftjis( str ) {
-
-		if ( u2sTable === undefined ) {
-
-			const encoder = new MMDParser.CharsetEncoder(); // eslint-disable-line no-undef
-			const table = encoder.s2uTable;
-			u2sTable = {};
-			const keys = Object.keys( table );
-			for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-				let key = keys[ i ];
-				const value = table[ key ];
-				key = parseInt( key );
-				u2sTable[ value ] = key;
-
-			}
-
-		}
-
-		const array = [];
-		for ( let i = 0, il = str.length; i < il; i ++ ) {
-
-			const code = str.charCodeAt( i );
-			const value = u2sTable[ code ];
-			if ( value === undefined ) {
-
-				throw new Error( 'cannot convert charcode 0x' + code.toString( 16 ) );
-
-			} else if ( value > 0xff ) {
-
-				array.push( value >> 8 & 0xff );
-				array.push( value & 0xff );
-
-			} else {
-
-				array.push( value & 0xff );
-
-			}
-
-		}
-
-		return new Uint8Array( array );
-
-	}
-
-	function getBindBones( skin ) {
-
-		// any more efficient ways?
-		const poseSkin = skin.clone();
-		poseSkin.pose();
-		return poseSkin.skeleton.bones;
-
-	}
-
-	THREE.MMDExporter = MMDExporter;
-
-} )();

+ 0 - 260
examples/js/exporters/OBJExporter.js

@@ -1,260 +0,0 @@
-( function () {
-
-	class OBJExporter {
-
-		parse( object ) {
-
-			let output = '';
-			let indexVertex = 0;
-			let indexVertexUvs = 0;
-			let indexNormals = 0;
-			const vertex = new THREE.Vector3();
-			const color = new THREE.Color();
-			const normal = new THREE.Vector3();
-			const uv = new THREE.Vector2();
-			const face = [];
-			function parseMesh( mesh ) {
-
-				let nbVertex = 0;
-				let nbNormals = 0;
-				let nbVertexUvs = 0;
-				const geometry = mesh.geometry;
-				const normalMatrixWorld = new THREE.Matrix3();
-
-				// shortcuts
-				const vertices = geometry.getAttribute( 'position' );
-				const normals = geometry.getAttribute( 'normal' );
-				const uvs = geometry.getAttribute( 'uv' );
-				const indices = geometry.getIndex();
-
-				// name of the mesh object
-				output += 'o ' + mesh.name + '\n';
-
-				// name of the mesh material
-				if ( mesh.material && mesh.material.name ) {
-
-					output += 'usemtl ' + mesh.material.name + '\n';
-
-				}
-
-				// vertices
-
-				if ( vertices !== undefined ) {
-
-					for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
-
-						vertex.fromBufferAttribute( vertices, i );
-
-						// transform the vertex to world space
-						vertex.applyMatrix4( mesh.matrixWorld );
-
-						// transform the vertex to export format
-						output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
-
-					}
-
-				}
-
-				// uvs
-
-				if ( uvs !== undefined ) {
-
-					for ( let i = 0, l = uvs.count; i < l; i ++, nbVertexUvs ++ ) {
-
-						uv.fromBufferAttribute( uvs, i );
-
-						// transform the uv to export format
-						output += 'vt ' + uv.x + ' ' + uv.y + '\n';
-
-					}
-
-				}
-
-				// normals
-
-				if ( normals !== undefined ) {
-
-					normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
-					for ( let i = 0, l = normals.count; i < l; i ++, nbNormals ++ ) {
-
-						normal.fromBufferAttribute( normals, i );
-
-						// transform the normal to world space
-						normal.applyMatrix3( normalMatrixWorld ).normalize();
-
-						// transform the normal to export format
-						output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
-
-					}
-
-				}
-
-				// faces
-
-				if ( indices !== null ) {
-
-					for ( let i = 0, l = indices.count; i < l; i += 3 ) {
-
-						for ( let m = 0; m < 3; m ++ ) {
-
-							const j = indices.getX( i + m ) + 1;
-							face[ m ] = indexVertex + j + ( normals || uvs ? '/' + ( uvs ? indexVertexUvs + j : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
-
-						}
-
-						// transform the face to export format
-						output += 'f ' + face.join( ' ' ) + '\n';
-
-					}
-
-				} else {
-
-					for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
-
-						for ( let m = 0; m < 3; m ++ ) {
-
-							const j = i + m + 1;
-							face[ m ] = indexVertex + j + ( normals || uvs ? '/' + ( uvs ? indexVertexUvs + j : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
-
-						}
-
-						// transform the face to export format
-						output += 'f ' + face.join( ' ' ) + '\n';
-
-					}
-
-				}
-
-				// update index
-				indexVertex += nbVertex;
-				indexVertexUvs += nbVertexUvs;
-				indexNormals += nbNormals;
-
-			}
-
-			function parseLine( line ) {
-
-				let nbVertex = 0;
-				const geometry = line.geometry;
-				const type = line.type;
-
-				// shortcuts
-				const vertices = geometry.getAttribute( 'position' );
-
-				// name of the line object
-				output += 'o ' + line.name + '\n';
-				if ( vertices !== undefined ) {
-
-					for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
-
-						vertex.fromBufferAttribute( vertices, i );
-
-						// transform the vertex to world space
-						vertex.applyMatrix4( line.matrixWorld );
-
-						// transform the vertex to export format
-						output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
-
-					}
-
-				}
-
-				if ( type === 'Line' ) {
-
-					output += 'l ';
-					for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
-
-						output += indexVertex + j + ' ';
-
-					}
-
-					output += '\n';
-
-				}
-
-				if ( type === 'LineSegments' ) {
-
-					for ( let j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) {
-
-						output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n';
-
-					}
-
-				}
-
-				// update index
-				indexVertex += nbVertex;
-
-			}
-
-			function parsePoints( points ) {
-
-				let nbVertex = 0;
-				const geometry = points.geometry;
-				const vertices = geometry.getAttribute( 'position' );
-				const colors = geometry.getAttribute( 'color' );
-				output += 'o ' + points.name + '\n';
-				if ( vertices !== undefined ) {
-
-					for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
-
-						vertex.fromBufferAttribute( vertices, i );
-						vertex.applyMatrix4( points.matrixWorld );
-						output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
-						if ( colors !== undefined ) {
-
-							color.fromBufferAttribute( colors, i ).convertLinearToSRGB();
-							output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
-
-						}
-
-						output += '\n';
-
-					}
-
-					output += 'p ';
-					for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
-
-						output += indexVertex + j + ' ';
-
-					}
-
-					output += '\n';
-
-				}
-
-				// update index
-				indexVertex += nbVertex;
-
-			}
-
-			object.traverse( function ( child ) {
-
-				if ( child.isMesh === true ) {
-
-					parseMesh( child );
-
-				}
-
-				if ( child.isLine === true ) {
-
-					parseLine( child );
-
-				}
-
-				if ( child.isPoints === true ) {
-
-					parsePoints( child );
-
-				}
-
-			} );
-			return output;
-
-		}
-
-	}
-
-	THREE.OBJExporter = OBJExporter;
-
-} )();

+ 0 - 427
examples/js/exporters/PLYExporter.js

@@ -1,427 +0,0 @@
-( function () {
-
-	/**
- * https://github.com/gkjohnson/ply-exporter-js
- *
- * Usage:
- *  const exporter = new PLYExporter();
- *
- *  // second argument is a list of options
- *  exporter.parse(mesh, data => console.log(data), { binary: true, excludeAttributes: [ 'color' ], littleEndian: true });
- *
- * Format Definition:
- * http://paulbourke.net/dataformats/ply/
- */
-
-	class PLYExporter {
-
-		parse( object, onDone, options ) {
-
-			// Iterate over the valid meshes in the object
-			function traverseMeshes( cb ) {
-
-				object.traverse( function ( child ) {
-
-					if ( child.isMesh === true || child.isPoints ) {
-
-						const mesh = child;
-						const geometry = mesh.geometry;
-						if ( geometry.hasAttribute( 'position' ) === true ) {
-
-							cb( mesh, geometry );
-
-						}
-
-					}
-
-				} );
-
-			}
-
-			// Default options
-			const defaultOptions = {
-				binary: false,
-				excludeAttributes: [],
-				// normal, uv, color, index
-				littleEndian: false
-			};
-			options = Object.assign( defaultOptions, options );
-			const excludeAttributes = options.excludeAttributes;
-			let includeIndices = true;
-			let includeNormals = false;
-			let includeColors = false;
-			let includeUVs = false;
-
-			// count the vertices, check which properties are used,
-			// and cache the BufferGeometry
-			let vertexCount = 0;
-			let faceCount = 0;
-			object.traverse( function ( child ) {
-
-				if ( child.isMesh === true ) {
-
-					const mesh = child;
-					const geometry = mesh.geometry;
-					const vertices = geometry.getAttribute( 'position' );
-					const normals = geometry.getAttribute( 'normal' );
-					const uvs = geometry.getAttribute( 'uv' );
-					const colors = geometry.getAttribute( 'color' );
-					const indices = geometry.getIndex();
-					if ( vertices === undefined ) {
-
-						return;
-
-					}
-
-					vertexCount += vertices.count;
-					faceCount += indices ? indices.count / 3 : vertices.count / 3;
-					if ( normals !== undefined ) includeNormals = true;
-					if ( uvs !== undefined ) includeUVs = true;
-					if ( colors !== undefined ) includeColors = true;
-
-				} else if ( child.isPoints ) {
-
-					const mesh = child;
-					const geometry = mesh.geometry;
-					const vertices = geometry.getAttribute( 'position' );
-					vertexCount += vertices.count;
-					includeIndices = false;
-
-				}
-
-			} );
-			const tempColor = new THREE.Color();
-			includeIndices = includeIndices && excludeAttributes.indexOf( 'index' ) === - 1;
-			includeNormals = includeNormals && excludeAttributes.indexOf( 'normal' ) === - 1;
-			includeColors = includeColors && excludeAttributes.indexOf( 'color' ) === - 1;
-			includeUVs = includeUVs && excludeAttributes.indexOf( 'uv' ) === - 1;
-			if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
-
-				// point cloud meshes will not have an index array and may not have a
-				// number of vertices that is divisble by 3 (and therefore representable
-				// as triangles)
-				console.error( 'PLYExporter: Failed to generate a valid PLY file with triangle indices because the ' + 'number of indices is not divisible by 3.' );
-				return null;
-
-			}
-
-			const indexByteCount = 4;
-			let header = 'ply\n' + `format ${options.binary ? options.littleEndian ? 'binary_little_endian' : 'binary_big_endian' : 'ascii'} 1.0\n` + `element vertex ${vertexCount}\n` +
-    // position
-    'property float x\n' + 'property float y\n' + 'property float z\n';
-			if ( includeNormals === true ) {
-
-				// normal
-				header += 'property float nx\n' + 'property float ny\n' + 'property float nz\n';
-
-			}
-
-			if ( includeUVs === true ) {
-
-				// uvs
-				header += 'property float s\n' + 'property float t\n';
-
-			}
-
-			if ( includeColors === true ) {
-
-				// colors
-				header += 'property uchar red\n' + 'property uchar green\n' + 'property uchar blue\n';
-
-			}
-
-			if ( includeIndices === true ) {
-
-				// faces
-				header += `element face ${faceCount}\n` + 'property list uchar int vertex_index\n';
-
-			}
-
-			header += 'end_header\n';
-
-			// Generate attribute data
-			const vertex = new THREE.Vector3();
-			const normalMatrixWorld = new THREE.Matrix3();
-			let result = null;
-			if ( options.binary === true ) {
-
-				// Binary File Generation
-				const headerBin = new TextEncoder().encode( header );
-
-				// 3 position values at 4 bytes
-				// 3 normal values at 4 bytes
-				// 3 color channels with 1 byte
-				// 2 uv values at 4 bytes
-				const vertexListLength = vertexCount * ( 4 * 3 + ( includeNormals ? 4 * 3 : 0 ) + ( includeColors ? 3 : 0 ) + ( includeUVs ? 4 * 2 : 0 ) );
-
-				// 1 byte shape desciptor
-				// 3 vertex indices at ${indexByteCount} bytes
-				const faceListLength = includeIndices ? faceCount * ( indexByteCount * 3 + 1 ) : 0;
-				const output = new DataView( new ArrayBuffer( headerBin.length + vertexListLength + faceListLength ) );
-				new Uint8Array( output.buffer ).set( headerBin, 0 );
-				let vOffset = headerBin.length;
-				let fOffset = headerBin.length + vertexListLength;
-				let writtenVertices = 0;
-				traverseMeshes( function ( mesh, geometry ) {
-
-					const vertices = geometry.getAttribute( 'position' );
-					const normals = geometry.getAttribute( 'normal' );
-					const uvs = geometry.getAttribute( 'uv' );
-					const colors = geometry.getAttribute( 'color' );
-					const indices = geometry.getIndex();
-					normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
-					for ( let i = 0, l = vertices.count; i < l; i ++ ) {
-
-						vertex.fromBufferAttribute( vertices, i );
-						vertex.applyMatrix4( mesh.matrixWorld );
-
-						// Position information
-						output.setFloat32( vOffset, vertex.x, options.littleEndian );
-						vOffset += 4;
-						output.setFloat32( vOffset, vertex.y, options.littleEndian );
-						vOffset += 4;
-						output.setFloat32( vOffset, vertex.z, options.littleEndian );
-						vOffset += 4;
-
-						// Normal information
-						if ( includeNormals === true ) {
-
-							if ( normals != null ) {
-
-								vertex.fromBufferAttribute( normals, i );
-								vertex.applyMatrix3( normalMatrixWorld ).normalize();
-								output.setFloat32( vOffset, vertex.x, options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, vertex.y, options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, vertex.z, options.littleEndian );
-								vOffset += 4;
-
-							} else {
-
-								output.setFloat32( vOffset, 0, options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, 0, options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, 0, options.littleEndian );
-								vOffset += 4;
-
-							}
-
-						}
-
-						// UV information
-						if ( includeUVs === true ) {
-
-							if ( uvs != null ) {
-
-								output.setFloat32( vOffset, uvs.getX( i ), options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, uvs.getY( i ), options.littleEndian );
-								vOffset += 4;
-
-							} else {
-
-								output.setFloat32( vOffset, 0, options.littleEndian );
-								vOffset += 4;
-								output.setFloat32( vOffset, 0, options.littleEndian );
-								vOffset += 4;
-
-							}
-
-						}
-
-						// THREE.Color information
-						if ( includeColors === true ) {
-
-							if ( colors != null ) {
-
-								tempColor.fromBufferAttribute( colors, i ).convertLinearToSRGB();
-								output.setUint8( vOffset, Math.floor( tempColor.r * 255 ) );
-								vOffset += 1;
-								output.setUint8( vOffset, Math.floor( tempColor.g * 255 ) );
-								vOffset += 1;
-								output.setUint8( vOffset, Math.floor( tempColor.b * 255 ) );
-								vOffset += 1;
-
-							} else {
-
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-								output.setUint8( vOffset, 255 );
-								vOffset += 1;
-
-							}
-
-						}
-
-					}
-
-					if ( includeIndices === true ) {
-
-						// Create the face list
-
-						if ( indices !== null ) {
-
-							for ( let i = 0, l = indices.count; i < l; i += 3 ) {
-
-								output.setUint8( fOffset, 3 );
-								fOffset += 1;
-								output.setUint32( fOffset, indices.getX( i + 0 ) + writtenVertices, options.littleEndian );
-								fOffset += indexByteCount;
-								output.setUint32( fOffset, indices.getX( i + 1 ) + writtenVertices, options.littleEndian );
-								fOffset += indexByteCount;
-								output.setUint32( fOffset, indices.getX( i + 2 ) + writtenVertices, options.littleEndian );
-								fOffset += indexByteCount;
-
-							}
-
-						} else {
-
-							for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
-
-								output.setUint8( fOffset, 3 );
-								fOffset += 1;
-								output.setUint32( fOffset, writtenVertices + i, options.littleEndian );
-								fOffset += indexByteCount;
-								output.setUint32( fOffset, writtenVertices + i + 1, options.littleEndian );
-								fOffset += indexByteCount;
-								output.setUint32( fOffset, writtenVertices + i + 2, options.littleEndian );
-								fOffset += indexByteCount;
-
-							}
-
-						}
-
-					}
-
-					// Save the amount of verts we've already written so we can offset
-					// the face index on the next mesh
-					writtenVertices += vertices.count;
-
-				} );
-				result = output.buffer;
-
-			} else {
-
-				// Ascii File Generation
-				// count the number of vertices
-				let writtenVertices = 0;
-				let vertexList = '';
-				let faceList = '';
-				traverseMeshes( function ( mesh, geometry ) {
-
-					const vertices = geometry.getAttribute( 'position' );
-					const normals = geometry.getAttribute( 'normal' );
-					const uvs = geometry.getAttribute( 'uv' );
-					const colors = geometry.getAttribute( 'color' );
-					const indices = geometry.getIndex();
-					normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
-
-					// form each line
-					for ( let i = 0, l = vertices.count; i < l; i ++ ) {
-
-						vertex.fromBufferAttribute( vertices, i );
-						vertex.applyMatrix4( mesh.matrixWorld );
-
-						// Position information
-						let line = vertex.x + ' ' + vertex.y + ' ' + vertex.z;
-
-						// Normal information
-						if ( includeNormals === true ) {
-
-							if ( normals != null ) {
-
-								vertex.fromBufferAttribute( normals, i );
-								vertex.applyMatrix3( normalMatrixWorld ).normalize();
-								line += ' ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
-
-							} else {
-
-								line += ' 0 0 0';
-
-							}
-
-						}
-
-						// UV information
-						if ( includeUVs === true ) {
-
-							if ( uvs != null ) {
-
-								line += ' ' + uvs.getX( i ) + ' ' + uvs.getY( i );
-
-							} else {
-
-								line += ' 0 0';
-
-							}
-
-						}
-
-						// THREE.Color information
-						if ( includeColors === true ) {
-
-							if ( colors != null ) {
-
-								tempColor.fromBufferAttribute( colors, i ).convertLinearToSRGB();
-								line += ' ' + Math.floor( tempColor.r * 255 ) + ' ' + Math.floor( tempColor.g * 255 ) + ' ' + Math.floor( tempColor.b * 255 );
-
-							} else {
-
-								line += ' 255 255 255';
-
-							}
-
-						}
-
-						vertexList += line + '\n';
-
-					}
-
-					// Create the face list
-					if ( includeIndices === true ) {
-
-						if ( indices !== null ) {
-
-							for ( let i = 0, l = indices.count; i < l; i += 3 ) {
-
-								faceList += `3 ${indices.getX( i + 0 ) + writtenVertices}`;
-								faceList += ` ${indices.getX( i + 1 ) + writtenVertices}`;
-								faceList += ` ${indices.getX( i + 2 ) + writtenVertices}\n`;
-
-							}
-
-						} else {
-
-							for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
-
-								faceList += `3 ${writtenVertices + i} ${writtenVertices + i + 1} ${writtenVertices + i + 2}\n`;
-
-							}
-
-						}
-
-						faceCount += indices ? indices.count / 3 : vertices.count / 3;
-
-					}
-
-					writtenVertices += vertices.count;
-
-				} );
-				result = `${header}${vertexList}${includeIndices ? `${faceList}\n` : '\n'}`;
-
-			}
-
-			if ( typeof onDone === 'function' ) requestAnimationFrame( () => onDone( result ) );
-			return result;
-
-		}
-
-	}
-
-	THREE.PLYExporter = PLYExporter;
-
-} )();

+ 0 - 188
examples/js/exporters/STLExporter.js

@@ -1,188 +0,0 @@
-( function () {
-
-	/**
- * Usage:
- *  const exporter = new STLExporter();
- *
- *  // second argument is a list of options
- *  const data = exporter.parse( mesh, { binary: true } );
- *
- */
-
-	class STLExporter {
-
-		parse( scene, options = {} ) {
-
-			const binary = options.binary !== undefined ? options.binary : false;
-
-			//
-
-			const objects = [];
-			let triangles = 0;
-			scene.traverse( function ( object ) {
-
-				if ( object.isMesh ) {
-
-					const geometry = object.geometry;
-					const index = geometry.index;
-					const positionAttribute = geometry.getAttribute( 'position' );
-					triangles += index !== null ? index.count / 3 : positionAttribute.count / 3;
-					objects.push( {
-						object3d: object,
-						geometry: geometry
-					} );
-
-				}
-
-			} );
-			let output;
-			let offset = 80; // skip header
-
-			if ( binary === true ) {
-
-				const bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
-				const arrayBuffer = new ArrayBuffer( bufferLength );
-				output = new DataView( arrayBuffer );
-				output.setUint32( offset, triangles, true );
-				offset += 4;
-
-			} else {
-
-				output = '';
-				output += 'solid exported\n';
-
-			}
-
-			const vA = new THREE.Vector3();
-			const vB = new THREE.Vector3();
-			const vC = new THREE.Vector3();
-			const cb = new THREE.Vector3();
-			const ab = new THREE.Vector3();
-			const normal = new THREE.Vector3();
-			for ( let i = 0, il = objects.length; i < il; i ++ ) {
-
-				const object = objects[ i ].object3d;
-				const geometry = objects[ i ].geometry;
-				const index = geometry.index;
-				const positionAttribute = geometry.getAttribute( 'position' );
-				if ( index !== null ) {
-
-					// indexed geometry
-
-					for ( let j = 0; j < index.count; j += 3 ) {
-
-						const a = index.getX( j + 0 );
-						const b = index.getX( j + 1 );
-						const c = index.getX( j + 2 );
-						writeFace( a, b, c, positionAttribute, object );
-
-					}
-
-				} else {
-
-					// non-indexed geometry
-
-					for ( let j = 0; j < positionAttribute.count; j += 3 ) {
-
-						const a = j + 0;
-						const b = j + 1;
-						const c = j + 2;
-						writeFace( a, b, c, positionAttribute, object );
-
-					}
-
-				}
-
-			}
-
-			if ( binary === false ) {
-
-				output += 'endsolid exported\n';
-
-			}
-
-			return output;
-			function writeFace( a, b, c, positionAttribute, object ) {
-
-				vA.fromBufferAttribute( positionAttribute, a );
-				vB.fromBufferAttribute( positionAttribute, b );
-				vC.fromBufferAttribute( positionAttribute, c );
-				if ( object.isSkinnedMesh === true ) {
-
-					object.boneTransform( a, vA );
-					object.boneTransform( b, vB );
-					object.boneTransform( c, vC );
-
-				}
-
-				vA.applyMatrix4( object.matrixWorld );
-				vB.applyMatrix4( object.matrixWorld );
-				vC.applyMatrix4( object.matrixWorld );
-				writeNormal( vA, vB, vC );
-				writeVertex( vA );
-				writeVertex( vB );
-				writeVertex( vC );
-				if ( binary === true ) {
-
-					output.setUint16( offset, 0, true );
-					offset += 2;
-
-				} else {
-
-					output += '\t\tendloop\n';
-					output += '\tendfacet\n';
-
-				}
-
-			}
-
-			function writeNormal( vA, vB, vC ) {
-
-				cb.subVectors( vC, vB );
-				ab.subVectors( vA, vB );
-				cb.cross( ab ).normalize();
-				normal.copy( cb ).normalize();
-				if ( binary === true ) {
-
-					output.setFloat32( offset, normal.x, true );
-					offset += 4;
-					output.setFloat32( offset, normal.y, true );
-					offset += 4;
-					output.setFloat32( offset, normal.z, true );
-					offset += 4;
-
-				} else {
-
-					output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
-					output += '\t\touter loop\n';
-
-				}
-
-			}
-
-			function writeVertex( vertex ) {
-
-				if ( binary === true ) {
-
-					output.setFloat32( offset, vertex.x, true );
-					offset += 4;
-					output.setFloat32( offset, vertex.y, true );
-					offset += 4;
-					output.setFloat32( offset, vertex.z, true );
-					offset += 4;
-
-				} else {
-
-					output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
-
-				}
-
-			}
-
-		}
-
-	}
-
-	THREE.STLExporter = STLExporter;
-
-} )();

+ 0 - 608
examples/js/exporters/USDZExporter.js

@@ -1,608 +0,0 @@
-( function () {
-
-	class USDZExporter {
-
-		async parse( scene, options = {
-			ar: {
-				anchoring: {
-					type: 'plane'
-				},
-				planeAnchoring: {
-					alignment: 'horizontal'
-				}
-			}
-		} ) {
-
-			const files = {};
-			const modelFileName = 'model.usda';
-
-			// model file should be first in USDZ archive so we init it here
-			files[ modelFileName ] = null;
-			let output = buildHeader();
-			output += buildSceneStart( options );
-			const materials = {};
-			const textures = {};
-			scene.traverseVisible( object => {
-
-				if ( object.isMesh ) {
-
-					const geometry = object.geometry;
-					const material = object.material;
-					if ( material.isMeshStandardMaterial ) {
-
-						const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usd';
-						if ( ! ( geometryFileName in files ) ) {
-
-							const meshObject = buildMeshObject( geometry );
-							files[ geometryFileName ] = buildUSDFileAsString( meshObject );
-
-						}
-
-						if ( ! ( material.uuid in materials ) ) {
-
-							materials[ material.uuid ] = material;
-
-						}
-
-						output += buildXform( object, geometry, material );
-
-					} else {
-
-						console.warn( 'THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', object );
-
-					}
-
-				} else if ( object.isCamera ) {
-
-					output += buildCamera( object );
-
-				}
-
-			} );
-			output += buildSceneEnd();
-			output += buildMaterials( materials, textures );
-			files[ modelFileName ] = fflate.strToU8( output );
-			output = null;
-			for ( const id in textures ) {
-
-				const texture = textures[ id ];
-				const color = id.split( '_' )[ 1 ];
-				const isRGBA = texture.format === 1023;
-				const canvas = imageToCanvas( texture.image, color );
-				const blob = await new Promise( resolve => canvas.toBlob( resolve, isRGBA ? 'image/png' : 'image/jpeg', 1 ) );
-				files[ `textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}` ] = new Uint8Array( await blob.arrayBuffer() );
-
-			}
-
-			// 64 byte alignment
-			// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
-
-			let offset = 0;
-			for ( const filename in files ) {
-
-				const file = files[ filename ];
-				const headerSize = 34 + filename.length;
-				offset += headerSize;
-				const offsetMod64 = offset & 63;
-				if ( offsetMod64 !== 4 ) {
-
-					const padLength = 64 - offsetMod64;
-					const padding = new Uint8Array( padLength );
-					files[ filename ] = [ file, {
-						extra: {
-							12345: padding
-						}
-					} ];
-
-				}
-
-				offset = file.length;
-
-			}
-
-			return fflate.zipSync( files, {
-				level: 0
-			} );
-
-		}
-
-	}
-	function imageToCanvas( image, color ) {
-
-		if ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) {
-
-			const scale = 1024 / Math.max( image.width, image.height );
-			const canvas = document.createElement( 'canvas' );
-			canvas.width = image.width * Math.min( 1, scale );
-			canvas.height = image.height * Math.min( 1, scale );
-			const context = canvas.getContext( '2d' );
-			context.drawImage( image, 0, 0, canvas.width, canvas.height );
-			if ( color !== undefined ) {
-
-				const hex = parseInt( color, 16 );
-				const r = ( hex >> 16 & 255 ) / 255;
-				const g = ( hex >> 8 & 255 ) / 255;
-				const b = ( hex & 255 ) / 255;
-				const imagedata = context.getImageData( 0, 0, canvas.width, canvas.height );
-				const data = imagedata.data;
-				for ( let i = 0; i < data.length; i += 4 ) {
-
-					data[ i + 0 ] = data[ i + 0 ] * r;
-					data[ i + 1 ] = data[ i + 1 ] * g;
-					data[ i + 2 ] = data[ i + 2 ] * b;
-
-				}
-
-				context.putImageData( imagedata, 0, 0 );
-
-			}
-
-			return canvas;
-
-		}
-
-	}
-
-	//
-
-	const PRECISION = 7;
-	function buildHeader() {
-
-		return `#usda 1.0
-(
-    customLayerData = {
-        string creator = "Three.js USDZExporter"
-    }
-    metersPerUnit = 1
-    upAxis = "Y"
-)
-
-`;
-
-	}
-
-	function buildSceneStart( options ) {
-
-		return `def Xform "Root"
-{
-    def Scope "Scenes" (
-        kind = "sceneLibrary"
-    )
-    {
-        def Xform "Scene" (
-            customData = {
-                bool preliminary_collidesWithEnvironment = 0
-                string sceneName = "Scene"
-            }
-            sceneName = "Scene"
-        )
-        {
-        token preliminary:anchoring:type = "${options.ar.anchoring.type}"
-        token preliminary:planeAnchoring:alignment = "${options.ar.planeAnchoring.alignment}"
-
-`;
-
-	}
-
-	function buildSceneEnd() {
-
-		return `
-        }
-    }
-}
-
-`;
-
-	}
-
-	function buildUSDFileAsString( dataToInsert ) {
-
-		let output = buildHeader();
-		output += dataToInsert;
-		return fflate.strToU8( output );
-
-	}
-
-	// Xform
-
-	function buildXform( object, geometry, material ) {
-
-		const name = 'Object_' + object.id;
-		const transform = buildMatrix( object.matrixWorld );
-		if ( object.matrixWorld.determinant() < 0 ) {
-
-			console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', object );
-
-		}
-
-		return `def Xform "${name}" (
-    prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
-)
-{
-    matrix4d xformOp:transform = ${transform}
-    uniform token[] xformOpOrder = ["xformOp:transform"]
-
-    rel material:binding = </Materials/Material_${material.id}>
-}
-
-`;
-
-	}
-
-	function buildMatrix( matrix ) {
-
-		const array = matrix.elements;
-		return `( ${buildMatrixRow( array, 0 )}, ${buildMatrixRow( array, 4 )}, ${buildMatrixRow( array, 8 )}, ${buildMatrixRow( array, 12 )} )`;
-
-	}
-
-	function buildMatrixRow( array, offset ) {
-
-		return `(${array[ offset + 0 ]}, ${array[ offset + 1 ]}, ${array[ offset + 2 ]}, ${array[ offset + 3 ]})`;
-
-	}
-
-	// Mesh
-
-	function buildMeshObject( geometry ) {
-
-		const mesh = buildMesh( geometry );
-		return `
-def "Geometry"
-{
-  ${mesh}
-}
-`;
-
-	}
-
-	function buildMesh( geometry ) {
-
-		const name = 'Geometry';
-		const attributes = geometry.attributes;
-		const count = attributes.position.count;
-		return `
-    def Mesh "${name}"
-    {
-        int[] faceVertexCounts = [${buildMeshVertexCount( geometry )}]
-        int[] faceVertexIndices = [${buildMeshVertexIndices( geometry )}]
-        normal3f[] normals = [${buildVector3Array( attributes.normal, count )}] (
-            interpolation = "vertex"
-        )
-        point3f[] points = [${buildVector3Array( attributes.position, count )}]
-        float2[] primvars:st = [${buildVector2Array( attributes.uv, count )}] (
-            interpolation = "vertex"
-        )
-        uniform token subdivisionScheme = "none"
-    }
-`;
-
-	}
-
-	function buildMeshVertexCount( geometry ) {
-
-		const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
-		return Array( count / 3 ).fill( 3 ).join( ', ' );
-
-	}
-
-	function buildMeshVertexIndices( geometry ) {
-
-		const index = geometry.index;
-		const array = [];
-		if ( index !== null ) {
-
-			for ( let i = 0; i < index.count; i ++ ) {
-
-				array.push( index.getX( i ) );
-
-			}
-
-		} else {
-
-			const length = geometry.attributes.position.count;
-			for ( let i = 0; i < length; i ++ ) {
-
-				array.push( i );
-
-			}
-
-		}
-
-		return array.join( ', ' );
-
-	}
-
-	function buildVector3Array( attribute, count ) {
-
-		if ( attribute === undefined ) {
-
-			console.warn( 'USDZExporter: Normals missing.' );
-			return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );
-
-		}
-
-		const array = [];
-		for ( let i = 0; i < attribute.count; i ++ ) {
-
-			const x = attribute.getX( i );
-			const y = attribute.getY( i );
-			const z = attribute.getZ( i );
-			array.push( `(${x.toPrecision( PRECISION )}, ${y.toPrecision( PRECISION )}, ${z.toPrecision( PRECISION )})` );
-
-		}
-
-		return array.join( ', ' );
-
-	}
-
-	function buildVector2Array( attribute, count ) {
-
-		if ( attribute === undefined ) {
-
-			console.warn( 'USDZExporter: UVs missing.' );
-			return Array( count ).fill( '(0, 0)' ).join( ', ' );
-
-		}
-
-		const array = [];
-		for ( let i = 0; i < attribute.count; i ++ ) {
-
-			const x = attribute.getX( i );
-			const y = attribute.getY( i );
-			array.push( `(${x.toPrecision( PRECISION )}, ${1 - y.toPrecision( PRECISION )})` );
-
-		}
-
-		return array.join( ', ' );
-
-	}
-
-	// Materials
-
-	function buildMaterials( materials, textures ) {
-
-		const array = [];
-		for ( const uuid in materials ) {
-
-			const material = materials[ uuid ];
-			array.push( buildMaterial( material, textures ) );
-
-		}
-
-		return `def "Materials"
-{
-${array.join( '' )}
-}
-
-`;
-
-	}
-
-	function buildMaterial( material, textures ) {
-
-		// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
-
-		const pad = '            ';
-		const inputs = [];
-		const samplers = [];
-		function buildTexture( texture, mapType, color ) {
-
-			const id = texture.id + ( color ? '_' + color.getHexString() : '' );
-			const isRGBA = texture.format === 1023;
-			textures[ id ] = texture;
-			return `
-        def Shader "Transform2d_${mapType}" (
-            sdrMetadata = {
-                string role = "math"
-            }
-        )
-        {
-            uniform token info:id = "UsdTransform2d"
-            float2 inputs:in.connect = </Materials/Material_${material.id}/uvReader_st.outputs:result>
-            float2 inputs:scale = ${buildVector2( texture.repeat )}
-            float2 inputs:translation = ${buildVector2( texture.offset )}
-            float2 outputs:result
-        }
-
-        def Shader "Texture_${texture.id}_${mapType}"
-        {
-            uniform token info:id = "UsdUVTexture"
-            asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
-            float2 inputs:st.connect = </Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>
-            token inputs:wrapS = "repeat"
-            token inputs:wrapT = "repeat"
-            float outputs:r
-            float outputs:g
-            float outputs:b
-            float3 outputs:rgb
-            ${material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : ''}
-        }`;
-
-		}
-
-		if ( material.side === THREE.DoubleSide ) {
-
-			console.warn( 'THREE.USDZExporter: USDZ does not support double sided materials', material );
-
-		}
-
-		if ( material.map !== null ) {
-
-			inputs.push( `${pad}color3f inputs:diffuseColor.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:rgb>` );
-			if ( material.transparent ) {
-
-				inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>` );
-
-			} else if ( material.alphaTest > 0.0 ) {
-
-				inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.map.id}_diffuse.outputs:a>` );
-				inputs.push( `${pad}float inputs:opacityThreshold = ${material.alphaTest}` );
-
-			}
-
-			samplers.push( buildTexture( material.map, 'diffuse', material.color ) );
-
-		} else {
-
-			inputs.push( `${pad}color3f inputs:diffuseColor = ${buildColor( material.color )}` );
-
-		}
-
-		if ( material.emissiveMap !== null ) {
-
-			inputs.push( `${pad}color3f inputs:emissiveColor.connect = </Materials/Material_${material.id}/Texture_${material.emissiveMap.id}_emissive.outputs:rgb>` );
-			samplers.push( buildTexture( material.emissiveMap, 'emissive' ) );
-
-		} else if ( material.emissive.getHex() > 0 ) {
-
-			inputs.push( `${pad}color3f inputs:emissiveColor = ${buildColor( material.emissive )}` );
-
-		}
-
-		if ( material.normalMap !== null ) {
-
-			inputs.push( `${pad}normal3f inputs:normal.connect = </Materials/Material_${material.id}/Texture_${material.normalMap.id}_normal.outputs:rgb>` );
-			samplers.push( buildTexture( material.normalMap, 'normal' ) );
-
-		}
-
-		if ( material.aoMap !== null ) {
-
-			inputs.push( `${pad}float inputs:occlusion.connect = </Materials/Material_${material.id}/Texture_${material.aoMap.id}_occlusion.outputs:r>` );
-			samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
-
-		}
-
-		if ( material.roughnessMap !== null && material.roughness === 1 ) {
-
-			inputs.push( `${pad}float inputs:roughness.connect = </Materials/Material_${material.id}/Texture_${material.roughnessMap.id}_roughness.outputs:g>` );
-			samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
-
-		} else {
-
-			inputs.push( `${pad}float inputs:roughness = ${material.roughness}` );
-
-		}
-
-		if ( material.metalnessMap !== null && material.metalness === 1 ) {
-
-			inputs.push( `${pad}float inputs:metallic.connect = </Materials/Material_${material.id}/Texture_${material.metalnessMap.id}_metallic.outputs:b>` );
-			samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
-
-		} else {
-
-			inputs.push( `${pad}float inputs:metallic = ${material.metalness}` );
-
-		}
-
-		if ( material.alphaMap !== null ) {
-
-			inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
-			inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
-			samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
-
-		} else {
-
-			inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
-
-		}
-
-		if ( material.isMeshPhysicalMaterial ) {
-
-			inputs.push( `${pad}float inputs:clearcoat = ${material.clearcoat}` );
-			inputs.push( `${pad}float inputs:clearcoatRoughness = ${material.clearcoatRoughness}` );
-			inputs.push( `${pad}float inputs:ior = ${material.ior}` );
-
-		}
-
-		return `
-    def Material "Material_${material.id}"
-    {
-        def Shader "PreviewSurface"
-        {
-            uniform token info:id = "UsdPreviewSurface"
-${inputs.join( '\n' )}
-            int inputs:useSpecularWorkflow = 0
-            token outputs:surface
-        }
-
-        token outputs:surface.connect = </Materials/Material_${material.id}/PreviewSurface.outputs:surface>
-        token inputs:frame:stPrimvarName = "st"
-
-        def Shader "uvReader_st"
-        {
-            uniform token info:id = "UsdPrimvarReader_float2"
-            token inputs:varname.connect = </Materials/Material_${material.id}.inputs:frame:stPrimvarName>
-            float2 inputs:fallback = (0.0, 0.0)
-            float2 outputs:result
-        }
-
-${samplers.join( '\n' )}
-
-    }
-`;
-
-	}
-
-	function buildColor( color ) {
-
-		return `(${color.r}, ${color.g}, ${color.b})`;
-
-	}
-
-	function buildVector2( vector ) {
-
-		return `(${vector.x}, ${vector.y})`;
-
-	}
-
-	function buildCamera( camera ) {
-
-		const name = camera.name ? camera.name : 'Camera_' + camera.id;
-		const transform = buildMatrix( camera.matrixWorld );
-		if ( camera.matrixWorld.determinant() < 0 ) {
-
-			console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', camera );
-
-		}
-
-		if ( camera.isOrthographicCamera ) {
-
-			return `def Camera "${name}"
-		{
-			matrix4d xformOp:transform = ${transform}
-			uniform token[] xformOpOrder = ["xformOp:transform"]
-	
-			float2 clippingRange = (${camera.near.toPrecision( PRECISION )}, ${camera.far.toPrecision( PRECISION )})
-			float horizontalAperture = ${( ( Math.abs( camera.left ) + Math.abs( camera.right ) ) * 10 ).toPrecision( PRECISION )}
-			float verticalAperture = ${( ( Math.abs( camera.top ) + Math.abs( camera.bottom ) ) * 10 ).toPrecision( PRECISION )}
-			token projection = "orthographic"
-		}
-	
-	`;
-
-		} else {
-
-			return `def Camera "${name}"
-		{
-			matrix4d xformOp:transform = ${transform}
-			uniform token[] xformOpOrder = ["xformOp:transform"]
-	
-			float2 clippingRange = (${camera.near.toPrecision( PRECISION )}, ${camera.far.toPrecision( PRECISION )})
-			float focalLength = ${camera.getFocalLength().toPrecision( PRECISION )}
-			float focusDistance = ${camera.focus.toPrecision( PRECISION )}
-			float horizontalAperture = ${camera.getFilmWidth().toPrecision( PRECISION )}
-			token projection = "perspective"
-			float verticalAperture = ${camera.getFilmHeight().toPrecision( PRECISION )}
-		}
-	
-	`;
-
-		}
-
-	}
-
-	THREE.USDZExporter = USDZExporter;
-
-} )();

+ 0 - 59
examples/js/geometries/BoxLineGeometry.js

@@ -1,59 +0,0 @@
-( function () {
-
-	class BoxLineGeometry extends THREE.BufferGeometry {
-
-		constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {
-
-			super();
-			widthSegments = Math.floor( widthSegments );
-			heightSegments = Math.floor( heightSegments );
-			depthSegments = Math.floor( depthSegments );
-			const widthHalf = width / 2;
-			const heightHalf = height / 2;
-			const depthHalf = depth / 2;
-			const segmentWidth = width / widthSegments;
-			const segmentHeight = height / heightSegments;
-			const segmentDepth = depth / depthSegments;
-			const vertices = [];
-			let x = - widthHalf;
-			let y = - heightHalf;
-			let z = - depthHalf;
-			for ( let i = 0; i <= widthSegments; i ++ ) {
-
-				vertices.push( x, - heightHalf, - depthHalf, x, heightHalf, - depthHalf );
-				vertices.push( x, heightHalf, - depthHalf, x, heightHalf, depthHalf );
-				vertices.push( x, heightHalf, depthHalf, x, - heightHalf, depthHalf );
-				vertices.push( x, - heightHalf, depthHalf, x, - heightHalf, - depthHalf );
-				x += segmentWidth;
-
-			}
-
-			for ( let i = 0; i <= heightSegments; i ++ ) {
-
-				vertices.push( - widthHalf, y, - depthHalf, widthHalf, y, - depthHalf );
-				vertices.push( widthHalf, y, - depthHalf, widthHalf, y, depthHalf );
-				vertices.push( widthHalf, y, depthHalf, - widthHalf, y, depthHalf );
-				vertices.push( - widthHalf, y, depthHalf, - widthHalf, y, - depthHalf );
-				y += segmentHeight;
-
-			}
-
-			for ( let i = 0; i <= depthSegments; i ++ ) {
-
-				vertices.push( - widthHalf, - heightHalf, z, - widthHalf, heightHalf, z );
-				vertices.push( - widthHalf, heightHalf, z, widthHalf, heightHalf, z );
-				vertices.push( widthHalf, heightHalf, z, widthHalf, - heightHalf, z );
-				vertices.push( widthHalf, - heightHalf, z, - widthHalf, - heightHalf, z );
-				z += segmentDepth;
-
-			}
-
-			this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-
-		}
-
-	}
-
-	THREE.BoxLineGeometry = BoxLineGeometry;
-
-} )();

+ 0 - 53
examples/js/geometries/ConvexGeometry.js

@@ -1,53 +0,0 @@
-( function () {
-
-	class ConvexGeometry extends THREE.BufferGeometry {
-
-		constructor( points = [] ) {
-
-			super();
-
-			// buffers
-
-			const vertices = [];
-			const normals = [];
-			if ( THREE.ConvexHull === undefined ) {
-
-				console.error( 'THREE.ConvexGeometry: ConvexGeometry relies on THREE.ConvexHull' );
-
-			}
-
-			const convexHull = new THREE.ConvexHull().setFromPoints( points );
-
-			// generate vertices and normals
-
-			const faces = convexHull.faces;
-			for ( let i = 0; i < faces.length; i ++ ) {
-
-				const face = faces[ i ];
-				let edge = face.edge;
-
-				// we move along a doubly-connected edge list to access all face points (see HalfEdge docs)
-
-				do {
-
-					const point = edge.head().point;
-					vertices.push( point.x, point.y, point.z );
-					normals.push( face.normal.x, face.normal.y, face.normal.z );
-					edge = edge.next;
-
-				} while ( edge !== face.edge );
-
-			}
-
-			// build geometry
-
-			this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-			this.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
-
-		}
-
-	}
-
-	THREE.ConvexGeometry = ConvexGeometry;
-
-} )();

+ 0 - 324
examples/js/geometries/DecalGeometry.js

@@ -1,324 +0,0 @@
-( function () {
-
-	/**
- * You can use this geometry to create a decal mesh, that serves different kinds of purposes.
- * e.g. adding unique details to models, performing dynamic visual environmental changes or covering seams.
- *
- * Constructor parameter:
- *
- * mesh — Any mesh object
- * position — Position of the decal projector
- * orientation — Orientation of the decal projector
- * size — Size of the decal projector
- *
- * reference: http://blog.wolfire.com/2009/06/how-to-project-decals/
- *
- */
-
-	class DecalGeometry extends THREE.BufferGeometry {
-
-		constructor( mesh, position, orientation, size ) {
-
-			super();
-
-			// buffers
-
-			const vertices = [];
-			const normals = [];
-			const uvs = [];
-
-			// helpers
-
-			const plane = new THREE.Vector3();
-
-			// this matrix represents the transformation of the decal projector
-
-			const projectorMatrix = new THREE.Matrix4();
-			projectorMatrix.makeRotationFromEuler( orientation );
-			projectorMatrix.setPosition( position );
-			const projectorMatrixInverse = new THREE.Matrix4();
-			projectorMatrixInverse.copy( projectorMatrix ).invert();
-
-			// generate buffers
-
-			generate();
-
-			// build geometry
-
-			this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-			this.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
-			this.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
-			function generate() {
-
-				let decalVertices = [];
-				const vertex = new THREE.Vector3();
-				const normal = new THREE.Vector3();
-
-				// handle different geometry types
-
-				const geometry = mesh.geometry;
-				const positionAttribute = geometry.attributes.position;
-				const normalAttribute = geometry.attributes.normal;
-
-				// first, create an array of 'DecalVertex' objects
-				// three consecutive 'DecalVertex' objects represent a single face
-				//
-				// this data structure will be later used to perform the clipping
-
-				if ( geometry.index !== null ) {
-
-					// indexed THREE.BufferGeometry
-
-					const index = geometry.index;
-					for ( let i = 0; i < index.count; i ++ ) {
-
-						vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );
-						normal.fromBufferAttribute( normalAttribute, index.getX( i ) );
-						pushDecalVertex( decalVertices, vertex, normal );
-
-					}
-
-				} else {
-
-					// non-indexed THREE.BufferGeometry
-
-					for ( let i = 0; i < positionAttribute.count; i ++ ) {
-
-						vertex.fromBufferAttribute( positionAttribute, i );
-						normal.fromBufferAttribute( normalAttribute, i );
-						pushDecalVertex( decalVertices, vertex, normal );
-
-					}
-
-				}
-
-				// second, clip the geometry so that it doesn't extend out from the projector
-
-				decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
-				decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
-				decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
-				decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
-				decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
-				decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
-
-				// third, generate final vertices, normals and uvs
-
-				for ( let i = 0; i < decalVertices.length; i ++ ) {
-
-					const decalVertex = decalVertices[ i ];
-
-					// create texture coordinates (we are still in projector space)
-
-					uvs.push( 0.5 + decalVertex.position.x / size.x, 0.5 + decalVertex.position.y / size.y );
-
-					// transform the vertex back to world space
-
-					decalVertex.position.applyMatrix4( projectorMatrix );
-
-					// now create vertex and normal buffer data
-
-					vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
-					normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
-
-				}
-
-			}
-
-			function pushDecalVertex( decalVertices, vertex, normal ) {
-
-				// transform the vertex to world space, then to projector space
-
-				vertex.applyMatrix4( mesh.matrixWorld );
-				vertex.applyMatrix4( projectorMatrixInverse );
-				normal.transformDirection( mesh.matrixWorld );
-				decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
-
-			}
-
-			function clipGeometry( inVertices, plane ) {
-
-				const outVertices = [];
-				const s = 0.5 * Math.abs( size.dot( plane ) );
-
-				// a single iteration clips one face,
-				// which consists of three consecutive 'DecalVertex' objects
-
-				for ( let i = 0; i < inVertices.length; i += 3 ) {
-
-					let total = 0;
-					let nV1;
-					let nV2;
-					let nV3;
-					let nV4;
-					const d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
-					const d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
-					const d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
-					const v1Out = d1 > 0;
-					const v2Out = d2 > 0;
-					const v3Out = d3 > 0;
-
-					// calculate, how many vertices of the face lie outside of the clipping plane
-
-					total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
-					switch ( total ) {
-
-						case 0:
-						{
-
-							// the entire face lies inside of the plane, no clipping needed
-
-							outVertices.push( inVertices[ i ] );
-							outVertices.push( inVertices[ i + 1 ] );
-							outVertices.push( inVertices[ i + 2 ] );
-							break;
-
-						}
-
-						case 1:
-						{
-
-							// one vertex lies outside of the plane, perform clipping
-
-							if ( v1Out ) {
-
-								nV1 = inVertices[ i + 1 ];
-								nV2 = inVertices[ i + 2 ];
-								nV3 = clip( inVertices[ i ], nV1, plane, s );
-								nV4 = clip( inVertices[ i ], nV2, plane, s );
-
-							}
-
-							if ( v2Out ) {
-
-								nV1 = inVertices[ i ];
-								nV2 = inVertices[ i + 2 ];
-								nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
-								nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
-								outVertices.push( nV3 );
-								outVertices.push( nV2.clone() );
-								outVertices.push( nV1.clone() );
-								outVertices.push( nV2.clone() );
-								outVertices.push( nV3.clone() );
-								outVertices.push( nV4 );
-								break;
-
-							}
-
-							if ( v3Out ) {
-
-								nV1 = inVertices[ i ];
-								nV2 = inVertices[ i + 1 ];
-								nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
-								nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
-
-							}
-
-							outVertices.push( nV1.clone() );
-							outVertices.push( nV2.clone() );
-							outVertices.push( nV3 );
-							outVertices.push( nV4 );
-							outVertices.push( nV3.clone() );
-							outVertices.push( nV2.clone() );
-							break;
-
-						}
-
-						case 2:
-						{
-
-							// two vertices lies outside of the plane, perform clipping
-
-							if ( ! v1Out ) {
-
-								nV1 = inVertices[ i ].clone();
-								nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
-								nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
-								outVertices.push( nV1 );
-								outVertices.push( nV2 );
-								outVertices.push( nV3 );
-
-							}
-
-							if ( ! v2Out ) {
-
-								nV1 = inVertices[ i + 1 ].clone();
-								nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
-								nV3 = clip( nV1, inVertices[ i ], plane, s );
-								outVertices.push( nV1 );
-								outVertices.push( nV2 );
-								outVertices.push( nV3 );
-
-							}
-
-							if ( ! v3Out ) {
-
-								nV1 = inVertices[ i + 2 ].clone();
-								nV2 = clip( nV1, inVertices[ i ], plane, s );
-								nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
-								outVertices.push( nV1 );
-								outVertices.push( nV2 );
-								outVertices.push( nV3 );
-
-							}
-
-							break;
-
-						}
-
-						case 3:
-						{
-
-							// the entire face lies outside of the plane, so let's discard the corresponding vertices
-
-							break;
-
-						}
-
-					}
-
-				}
-
-				return outVertices;
-
-			}
-
-			function clip( v0, v1, p, s ) {
-
-				const d0 = v0.position.dot( p ) - s;
-				const d1 = v1.position.dot( p ) - s;
-				const s0 = d0 / ( d0 - d1 );
-				const v = new DecalVertex( new THREE.Vector3( v0.position.x + s0 * ( v1.position.x - v0.position.x ), v0.position.y + s0 * ( v1.position.y - v0.position.y ), v0.position.z + s0 * ( v1.position.z - v0.position.z ) ), new THREE.Vector3( v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ), v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ), v0.normal.z + s0 * ( v1.normal.z - v0.normal.z ) ) );
-
-				// need to clip more values (texture coordinates)? do it this way:
-				// intersectpoint.value = a.value + s * ( b.value - a.value );
-
-				return v;
-
-			}
-
-		}
-
-	}
-
-	// helper
-
-	class DecalVertex {
-
-		constructor( position, normal ) {
-
-			this.position = position;
-			this.normal = normal;
-
-		}
-		clone() {
-
-			return new this.constructor( this.position.clone(), this.normal.clone() );
-
-		}
-
-	}
-
-	THREE.DecalGeometry = DecalGeometry;
-	THREE.DecalVertex = DecalVertex;
-
-} )();

+ 0 - 861
examples/js/geometries/LightningStrike.js

@@ -1,861 +0,0 @@
-( function () {
-
-	/**
- * @fileoverview LightningStrike object for creating lightning strikes and voltaic arcs.
- *
- *
- * Usage
- *
- * var myRay = new LightningStrike( paramsObject );
- * var myRayMesh = new THREE.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 THREE.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.
- *
- *
-*/
-
-	class LightningStrike extends THREE.BufferGeometry {
-
-		constructor( rayParameters = {} ) {
-
-			super();
-			this.isLightningStrike = true;
-			this.type = 'LightningStrike';
-
-			// Set parameters, and set undefined parameters to default values
-			this.init( LightningStrike.copyParameters( rayParameters, rayParameters ) );
-
-			// Creates and populates the mesh
-			this.createMesh();
-
-		}
-		static createRandomGenerator() {
-
-			const numSeeds = 2053;
-			const seeds = [];
-			for ( let i = 0; i < numSeeds; i ++ ) {
-
-				seeds.push( Math.random() );
-
-			}
-
-			const generator = {
-				currentSeed: 0,
-				random: function () {
-
-					const 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;
-
-		}
-		static copyParameters( dest = {}, source = {} ) {
-
-			const vecCopy = function ( v ) {
-
-				if ( source === dest ) {
-
-					return v;
-
-				} else {
-
-					return v.clone();
-
-				}
-
-			};
-
-			dest.sourceOffset = source.sourceOffset !== undefined ? vecCopy( source.sourceOffset ) : new THREE.Vector3( 0, 100, 0 ), dest.destOffset = source.destOffset !== undefined ? vecCopy( source.destOffset ) : new THREE.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 THREE.Vector3( 0, 0, 1 );
-			dest.up1 = source.up1 !== undefined ? vecCopy( source.up1 ) : new THREE.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;
-
-		}
-		update( 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;
-
-				}
-
-			}
-
-		}
-		init( 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 ( let i = 0; i < this.maxSubrays; i ++ ) {
-
-				this.subrays.push( this.createSubray() );
-
-			}
-
-			this.raySegments = [];
-			for ( let 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 THREE.SimplexNoise( this.seedGenerator );
-			this.simplexY = new THREE.SimplexNoise( this.seedGenerator );
-			this.simplexZ = new THREE.SimplexNoise( this.seedGenerator );
-
-			// Temp vectors
-			this.forwards = new THREE.Vector3();
-			this.forwardsFill = new THREE.Vector3();
-			this.side = new THREE.Vector3();
-			this.down = new THREE.Vector3();
-			this.middlePos = new THREE.Vector3();
-			this.middleLinPos = new THREE.Vector3();
-			this.newPos = new THREE.Vector3();
-			this.vPos = new THREE.Vector3();
-			this.cross1 = new THREE.Vector3();
-
-		}
-		createMesh() {
-
-			const maxDrawableSegmentsPerSubRay = 1 << this.maxIterations;
-			const maxVerts = 3 * ( maxDrawableSegmentsPerSubRay + 1 ) * this.maxSubrays;
-			const 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 THREE.Uint32BufferAttribute( this.indices, 1 ) );
-			this.positionAttribute = new THREE.Float32BufferAttribute( this.vertices, 3 );
-			this.setAttribute( 'position', this.positionAttribute );
-			if ( this.generateUVs ) {
-
-				this.uvsAttribute = new THREE.Float32BufferAttribute( new Float32Array( this.uvs ), 2 );
-				this.setAttribute( 'uv', this.uvsAttribute );
-
-			}
-
-			if ( ! this.isStatic ) {
-
-				this.index.usage = THREE.DynamicDrawUsage;
-				this.positionAttribute.usage = THREE.DynamicDrawUsage;
-				if ( this.generateUVs ) {
-
-					this.uvsAttribute.usage = THREE.DynamicDrawUsage;
-
-				}
-
-			}
-
-			// Store buffers for later modification
-			this.vertices = this.positionAttribute.array;
-			this.indices = this.index.array;
-			if ( this.generateUVs ) {
-
-				this.uvs = this.uvsAttribute.array;
-
-			}
-
-		}
-		updateMesh( time ) {
-
-			this.fillMesh( time );
-			this.drawRange.count = this.currentIndex;
-			this.index.needsUpdate = true;
-			this.positionAttribute.needsUpdate = true;
-			if ( this.generateUVs ) {
-
-				this.uvsAttribute.needsUpdate = true;
-
-			}
-
-		}
-		fillMesh( time ) {
-
-			const scope = this;
-			this.currentVertex = 0;
-			this.currentIndex = 0;
-			this.currentCoordinate = 0;
-			this.currentUVCoordinate = 0;
-			this.fractalRay( time, function fillVertices( segment ) {
-
-				const 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 );
-
-				}
-
-			} );
-
-		}
-		addNewSubray() {
-
-			return this.subrays[ this.numSubrays ++ ];
-
-		}
-		initSubray( 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;
-
-		}
-		fractalRay( 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 ( let subrayIndex = 0; subrayIndex < this.numSubrays; subrayIndex ++ ) {
-
-				const subray = this.subrays[ subrayIndex ];
-				this.currentSubray = subray;
-				this.randomGenerator.setSeed( subray.seed );
-				subray.endPropagationTime = THREE.MathUtils.lerp( subray.birthTime, subray.deathTime, subray.propagationTimeFactor );
-				subray.beginVanishingTime = THREE.MathUtils.lerp( subray.deathTime, subray.birthTime, 1 - subray.vanishingTimeFactor );
-				const 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;
-				const 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;
-
-		}
-		fractalRayRecursive( segment ) {
-
-			// Leave recursion condition
-			if ( segment.iteration >= this.currentSubray.maxIterations ) {
-
-				this.currentSegmentCallback( segment );
-				return;
-
-			}
-
-			// Interpolation
-			this.forwards.subVectors( segment.pos1, segment.pos0 );
-			let lForwards = this.forwards.length();
-			if ( lForwards < 0.000001 ) {
-
-				this.forwards.set( 0, 0, 0.01 );
-				lForwards = this.forwards.length();
-
-			}
-
-			const middleRadius = ( segment.radius0 + segment.radius1 ) * 0.5;
-			const middleFraction = ( segment.fraction0 + segment.fraction1 ) * 0.5;
-			const 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 );
-			const 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
-
-			const 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;
-			const 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 );
-
-		}
-		createPrism( 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();
-
-		}
-		createTriangleVerticesWithoutUVs( 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 );
-			const p = this.vPos;
-			const 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;
-
-		}
-		createTriangleVerticesWithUVs( 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 );
-			const p = this.vPos;
-			const v = this.vertices;
-			const 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;
-
-		}
-		createPrismFaces( vertex /*, index*/ ) {
-
-			const indices = this.indices;
-			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;
-
-		}
-		createDefaultSubrayCreationCallbacks() {
-
-			const random1 = this.randomGenerator.random;
-			this.onDecideSubrayCreation = function ( segment, lightningStrike ) {
-
-				// Decide subrays creation at parent (sub)ray segment
-
-				const subray = lightningStrike.currentSubray;
-				const period = lightningStrike.rayParameters.subrayPeriod;
-				const dutyCycle = lightningStrike.rayParameters.subrayDutyCycle;
-				const phase0 = lightningStrike.rayParameters.isEternal && subray.recursion == 0 ? - random1() * period : THREE.MathUtils.lerp( subray.birthTime, subray.endPropagationTime, segment.fraction0 ) - random1() * period;
-				const phase = lightningStrike.time - phase0;
-				const currentCycle = Math.floor( phase / period );
-				const childSubraySeed = random1() * ( currentCycle + 1 );
-				const isActive = phase % period <= dutyCycle * period;
-				let 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 ) {
-
-					const childSubray = lightningStrike.addNewSubray();
-					const 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 );
-
-				}
-
-			};
-
-			const vec1Pos = new THREE.Vector3();
-			const vec2Forward = new THREE.Vector3();
-			const vec3Side = new THREE.Vector3();
-			const vec4Up = new THREE.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 ) );
-				const length = vec1Pos.length();
-				vec3Side.crossVectors( parentSubray.up0, vec2Forward );
-				const 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 ) );
-				const length = vec1Pos.length();
-				vec3Side.crossVectors( parentSubray.up0, vec2Forward );
-				const 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 );
-
-			};
-
-		}
-		createSubray() {
-
-			return {
-				seed: 0,
-				maxIterations: 0,
-				recursion: 0,
-				pos0: new THREE.Vector3(),
-				pos1: new THREE.Vector3(),
-				linPos0: new THREE.Vector3(),
-				linPos1: new THREE.Vector3(),
-				up0: new THREE.Vector3(),
-				up1: new THREE.Vector3(),
-				radius0: 0,
-				radius1: 0,
-				birthTime: 0,
-				deathTime: 0,
-				timeScale: 0,
-				roughness: 0,
-				straightness: 0,
-				propagationTimeFactor: 0,
-				vanishingTimeFactor: 0,
-				endPropagationTime: 0,
-				beginVanishingTime: 0
-			};
-
-		}
-		createSegment() {
-
-			return {
-				iteration: 0,
-				pos0: new THREE.Vector3(),
-				pos1: new THREE.Vector3(),
-				linPos0: new THREE.Vector3(),
-				linPos1: new THREE.Vector3(),
-				up0: new THREE.Vector3(),
-				up1: new THREE.Vector3(),
-				radius0: 0,
-				radius1: 0,
-				fraction0: 0,
-				fraction1: 0,
-				positionVariationFactor: 0
-			};
-
-		}
-		getNewSegment() {
-
-			return this.raySegments[ this.currentSegmentIndex ++ ];
-
-		}
-		copy( source ) {
-
-			super.copy( source );
-			this.init( LightningStrike.copyParameters( {}, source.rayParameters ) );
-			return this;
-
-		}
-		clone() {
-
-			return new this.constructor( LightningStrike.copyParameters( {}, this.rayParameters ) );
-
-		}
-
-	}
-
-	// 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 );
-
-	THREE.LightningStrike = LightningStrike;
-
-} )();

+ 0 - 216
examples/js/geometries/ParametricGeometries.js

@@ -1,216 +0,0 @@
-( function () {
-
-	/**
- * Experimenting of primitive geometry creation using Surface Parametric equations
- */
-
-	const ParametricGeometries = {
-		klein: function ( v, u, target ) {
-
-			u *= Math.PI;
-			v *= 2 * Math.PI;
-			u = u * 2;
-			let x, z;
-			if ( u < Math.PI ) {
-
-				x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + 2 * ( 1 - Math.cos( u ) / 2 ) * Math.cos( u ) * Math.cos( v );
-				z = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v );
-
-			} else {
-
-				x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + 2 * ( 1 - Math.cos( u ) / 2 ) * Math.cos( v + Math.PI );
-				z = - 8 * Math.sin( u );
-
-			}
-
-			const y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );
-			target.set( x, y, z );
-
-		},
-		plane: function ( width, height ) {
-
-			return function ( u, v, target ) {
-
-				const x = u * width;
-				const y = 0;
-				const z = v * height;
-				target.set( x, y, z );
-
-			};
-
-		},
-		mobius: function ( u, t, target ) {
-
-			// flat mobius strip
-			// http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
-			u = u - 0.5;
-			const v = 2 * Math.PI * t;
-			const a = 2;
-			const x = Math.cos( v ) * ( a + u * Math.cos( v / 2 ) );
-			const y = Math.sin( v ) * ( a + u * Math.cos( v / 2 ) );
-			const z = u * Math.sin( v / 2 );
-			target.set( x, y, z );
-
-		},
-		mobius3d: function ( u, t, target ) {
-
-			// volumetric mobius strip
-
-			u *= Math.PI;
-			t *= 2 * Math.PI;
-			u = u * 2;
-			const phi = u / 2;
-			const major = 2.25,
-				a = 0.125,
-				b = 0.65;
-			let x = a * Math.cos( t ) * Math.cos( phi ) - b * Math.sin( t ) * Math.sin( phi );
-			const z = a * Math.cos( t ) * Math.sin( phi ) + b * Math.sin( t ) * Math.cos( phi );
-			const y = ( major + x ) * Math.sin( u );
-			x = ( major + x ) * Math.cos( u );
-			target.set( x, y, z );
-
-		}
-	};
-
-	/*********************************************
- *
- * Parametric Replacement for TubeGeometry
- *
- *********************************************/
-
-	ParametricGeometries.TubeGeometry = class TubeGeometry extends THREE.ParametricGeometry {
-
-		constructor( path, segments = 64, radius = 1, segmentsRadius = 8, closed = false ) {
-
-			const numpoints = segments + 1;
-			const frames = path.computeFrenetFrames( segments, closed ),
-				tangents = frames.tangents,
-				normals = frames.normals,
-				binormals = frames.binormals;
-			const position = new THREE.Vector3();
-			function ParametricTube( u, v, target ) {
-
-				v *= 2 * Math.PI;
-				const i = Math.floor( u * ( numpoints - 1 ) );
-				path.getPointAt( u, position );
-				const normal = normals[ i ];
-				const binormal = binormals[ i ];
-				const cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
-				const cy = radius * Math.sin( v );
-				position.x += cx * normal.x + cy * binormal.x;
-				position.y += cx * normal.y + cy * binormal.y;
-				position.z += cx * normal.z + cy * binormal.z;
-				target.copy( position );
-
-			}
-
-			super( ParametricTube, segments, segmentsRadius );
-
-			// proxy internals
-
-			this.tangents = tangents;
-			this.normals = normals;
-			this.binormals = binormals;
-			this.path = path;
-			this.segments = segments;
-			this.radius = radius;
-			this.segmentsRadius = segmentsRadius;
-			this.closed = closed;
-
-		}
-
-	};
-
-	/*********************************************
-  *
-  * Parametric Replacement for TorusKnotGeometry
-  *
-  *********************************************/
-	ParametricGeometries.TorusKnotGeometry = class TorusKnotGeometry extends ParametricGeometries.TubeGeometry {
-
-		constructor( radius = 200, tube = 40, segmentsT = 64, segmentsR = 8, p = 2, q = 3 ) {
-
-			class TorusKnotCurve extends THREE.Curve {
-
-				getPoint( t, optionalTarget = new THREE.Vector3() ) {
-
-					const point = optionalTarget;
-					t *= Math.PI * 2;
-					const r = 0.5;
-					const x = ( 1 + r * Math.cos( q * t ) ) * Math.cos( p * t );
-					const y = ( 1 + r * Math.cos( q * t ) ) * Math.sin( p * t );
-					const z = r * Math.sin( q * t );
-					return point.set( x, y, z ).multiplyScalar( radius );
-
-				}
-
-			}
-			const segments = segmentsT;
-			const radiusSegments = segmentsR;
-			const extrudePath = new TorusKnotCurve();
-			super( extrudePath, segments, tube, radiusSegments, true, false );
-			this.radius = radius;
-			this.tube = tube;
-			this.segmentsT = segmentsT;
-			this.segmentsR = segmentsR;
-			this.p = p;
-			this.q = q;
-
-		}
-
-	};
-
-	/*********************************************
-  *
-  * Parametric Replacement for SphereGeometry
-  *
-  *********************************************/
-	ParametricGeometries.SphereGeometry = class SphereGeometry extends THREE.ParametricGeometry {
-
-		constructor( size, u, v ) {
-
-			function sphere( u, v, target ) {
-
-				u *= Math.PI;
-				v *= 2 * Math.PI;
-				const x = size * Math.sin( u ) * Math.cos( v );
-				const y = size * Math.sin( u ) * Math.sin( v );
-				const z = size * Math.cos( u );
-				target.set( x, y, z );
-
-			}
-
-			super( sphere, u, v );
-
-		}
-
-	};
-
-	/*********************************************
-  *
-  * Parametric Replacement for PlaneGeometry
-  *
-  *********************************************/
-
-	ParametricGeometries.PlaneGeometry = class PlaneGeometry extends THREE.ParametricGeometry {
-
-		constructor( width, depth, segmentsWidth, segmentsDepth ) {
-
-			function plane( u, v, target ) {
-
-				const x = u * width;
-				const y = 0;
-				const z = v * depth;
-				target.set( x, y, z );
-
-			}
-
-			super( plane, segmentsWidth, segmentsDepth );
-
-		}
-
-	};
-
-	THREE.ParametricGeometries = ParametricGeometries;
-
-} )();

+ 0 - 121
examples/js/geometries/ParametricGeometry.js

@@ -1,121 +0,0 @@
-( function () {
-
-	/**
- * Parametric Surfaces Geometry
- * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
- */
-	class ParametricGeometry extends THREE.BufferGeometry {
-
-		constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
-
-			super();
-			this.type = 'ParametricGeometry';
-			this.parameters = {
-				func: func,
-				slices: slices,
-				stacks: stacks
-			};
-
-			// buffers
-
-			const indices = [];
-			const vertices = [];
-			const normals = [];
-			const uvs = [];
-			const EPS = 0.00001;
-			const normal = new THREE.Vector3();
-			const p0 = new THREE.Vector3(),
-				p1 = new THREE.Vector3();
-			const pu = new THREE.Vector3(),
-				pv = new THREE.Vector3();
-
-			// generate vertices, normals and uvs
-
-			const sliceCount = slices + 1;
-			for ( let i = 0; i <= stacks; i ++ ) {
-
-				const v = i / stacks;
-				for ( let j = 0; j <= slices; j ++ ) {
-
-					const u = j / slices;
-
-					// vertex
-
-					func( u, v, p0 );
-					vertices.push( p0.x, p0.y, p0.z );
-
-					// normal
-
-					// approximate tangent vectors via finite differences
-
-					if ( u - EPS >= 0 ) {
-
-						func( u - EPS, v, p1 );
-						pu.subVectors( p0, p1 );
-
-					} else {
-
-						func( u + EPS, v, p1 );
-						pu.subVectors( p1, p0 );
-
-					}
-
-					if ( v - EPS >= 0 ) {
-
-						func( u, v - EPS, p1 );
-						pv.subVectors( p0, p1 );
-
-					} else {
-
-						func( u, v + EPS, p1 );
-						pv.subVectors( p1, p0 );
-
-					}
-
-					// cross product of tangent vectors returns surface normal
-
-					normal.crossVectors( pu, pv ).normalize();
-					normals.push( normal.x, normal.y, normal.z );
-
-					// uv
-
-					uvs.push( u, v );
-
-				}
-
-			}
-
-			// generate indices
-
-			for ( let i = 0; i < stacks; i ++ ) {
-
-				for ( let j = 0; j < slices; j ++ ) {
-
-					const a = i * sliceCount + j;
-					const b = i * sliceCount + j + 1;
-					const c = ( i + 1 ) * sliceCount + j + 1;
-					const d = ( i + 1 ) * sliceCount + j;
-
-					// faces one and two
-
-					indices.push( a, b, d );
-					indices.push( b, c, d );
-
-				}
-
-			}
-
-			// build geometry
-
-			this.setIndex( indices );
-			this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-			this.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
-			this.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
-
-		}
-
-	}
-
-	THREE.ParametricGeometry = ParametricGeometry;
-
-} )();

+ 0 - 142
examples/js/geometries/RoundedBoxGeometry.js

@@ -1,142 +0,0 @@
-( function () {
-
-	const _tempNormal = new THREE.Vector3();
-	function getUv( faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength ) {
-
-		const totArcLength = 2 * Math.PI * radius / 4;
-
-		// length of the planes between the arcs on each axis
-		const centerLength = Math.max( sideLength - 2 * radius, 0 );
-		const halfArc = Math.PI / 4;
-
-		// Get the vector projected onto the Y plane
-		_tempNormal.copy( normal );
-		_tempNormal[ projectionAxis ] = 0;
-		_tempNormal.normalize();
-
-		// total amount of UV space alloted to a single arc
-		const arcUvRatio = 0.5 * totArcLength / ( totArcLength + centerLength );
-
-		// the distance along one arc the point is at
-		const arcAngleRatio = 1.0 - _tempNormal.angleTo( faceDirVector ) / halfArc;
-		if ( Math.sign( _tempNormal[ uvAxis ] ) === 1 ) {
-
-			return arcAngleRatio * arcUvRatio;
-
-		} else {
-
-			// total amount of UV space alloted to the plane between the arcs
-			const lenUv = centerLength / ( totArcLength + centerLength );
-			return lenUv + arcUvRatio + arcUvRatio * ( 1.0 - arcAngleRatio );
-
-		}
-
-	}
-
-	class RoundedBoxGeometry extends THREE.BoxGeometry {
-
-		constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
-
-			// ensure segments is odd so we have a plane connecting the rounded corners
-			segments = segments * 2 + 1;
-
-			// ensure radius isn't bigger than shortest side
-			radius = Math.min( width / 2, height / 2, depth / 2, radius );
-			super( 1, 1, 1, segments, segments, segments );
-
-			// if we just have one segment we're the same as a regular box
-			if ( segments === 1 ) return;
-			const geometry2 = this.toNonIndexed();
-			this.index = null;
-			this.attributes.position = geometry2.attributes.position;
-			this.attributes.normal = geometry2.attributes.normal;
-			this.attributes.uv = geometry2.attributes.uv;
-
-			//
-
-			const position = new THREE.Vector3();
-			const normal = new THREE.Vector3();
-			const box = new THREE.Vector3( width, height, depth ).divideScalar( 2 ).subScalar( radius );
-			const positions = this.attributes.position.array;
-			const normals = this.attributes.normal.array;
-			const uvs = this.attributes.uv.array;
-			const faceTris = positions.length / 6;
-			const faceDirVector = new THREE.Vector3();
-			const halfSegmentSize = 0.5 / segments;
-			for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
-
-				position.fromArray( positions, i );
-				normal.copy( position );
-				normal.x -= Math.sign( normal.x ) * halfSegmentSize;
-				normal.y -= Math.sign( normal.y ) * halfSegmentSize;
-				normal.z -= Math.sign( normal.z ) * halfSegmentSize;
-				normal.normalize();
-				positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius;
-				positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius;
-				positions[ i + 2 ] = box.z * Math.sign( position.z ) + normal.z * radius;
-				normals[ i + 0 ] = normal.x;
-				normals[ i + 1 ] = normal.y;
-				normals[ i + 2 ] = normal.z;
-				const side = Math.floor( i / faceTris );
-				switch ( side ) {
-
-					case 0:
-						// right
-
-						// generate UVs along Z then Y
-						faceDirVector.set( 1, 0, 0 );
-						uvs[ j + 0 ] = getUv( faceDirVector, normal, 'z', 'y', radius, depth );
-						uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
-						break;
-					case 1:
-						// left
-
-						// generate UVs along Z then Y
-						faceDirVector.set( - 1, 0, 0 );
-						uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'y', radius, depth );
-						uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
-						break;
-					case 2:
-						// top
-
-						// generate UVs along X then Z
-						faceDirVector.set( 0, 1, 0 );
-						uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
-						uvs[ j + 1 ] = getUv( faceDirVector, normal, 'z', 'x', radius, depth );
-						break;
-					case 3:
-						// bottom
-
-						// generate UVs along X then Z
-						faceDirVector.set( 0, - 1, 0 );
-						uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
-						uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'x', radius, depth );
-						break;
-					case 4:
-						// front
-
-						// generate UVs along X then Y
-						faceDirVector.set( 0, 0, 1 );
-						uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'y', radius, width );
-						uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
-						break;
-					case 5:
-						// back
-
-						// generate UVs along X then Y
-						faceDirVector.set( 0, 0, - 1 );
-						uvs[ j + 0 ] = getUv( faceDirVector, normal, 'x', 'y', radius, width );
-						uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
-						break;
-
-				}
-
-			}
-
-		}
-
-	}
-
-	THREE.RoundedBoxGeometry = RoundedBoxGeometry;
-
-} )();

文件差異過大導致無法顯示
+ 0 - 62
examples/js/geometries/TeapotGeometry.js


+ 0 - 53
examples/js/geometries/TextGeometry.js

@@ -1,53 +0,0 @@
-( function () {
-
-	/**
- * Text = 3D Text
- *
- * parameters = {
- *  font: <THREE.Font>, // font
- *
- *  size: <float>, // size of the text
- *  height: <float>, // thickness to extrude text
- *  curveSegments: <int>, // number of points on the curves
- *
- *  bevelEnabled: <bool>, // turn on bevel
- *  bevelThickness: <float>, // how deep into text bevel goes
- *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
- *  bevelOffset: <float> // how far from text outline does bevel start
- * }
- */
-	class TextGeometry extends THREE.ExtrudeGeometry {
-
-		constructor( text, parameters = {} ) {
-
-			const font = parameters.font;
-			if ( font === undefined ) {
-
-				super(); // generate default extrude geometry
-
-			} else {
-
-				const shapes = font.generateShapes( text, parameters.size );
-
-				// translate parameters to THREE.ExtrudeGeometry API
-
-				parameters.depth = parameters.height !== undefined ? parameters.height : 50;
-
-				// defaults
-
-				if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
-				if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
-				if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
-				super( shapes, parameters );
-
-			}
-
-			this.type = 'TextGeometry';
-
-		}
-
-	}
-
-	THREE.TextGeometry = TextGeometry;
-
-} )();

+ 0 - 48
examples/js/helpers/LightProbeHelper.js

@@ -1,48 +0,0 @@
-( function () {
-
-	class LightProbeHelper extends THREE.Mesh {
-
-		constructor( lightProbe, size ) {
-
-			const material = new THREE.ShaderMaterial( {
-				type: 'LightProbeHelperMaterial',
-				uniforms: {
-					sh: {
-						value: lightProbe.sh.coefficients
-					},
-					// by reference
-
-					intensity: {
-						value: lightProbe.intensity
-					}
-				},
-				vertexShader: [ 'varying vec3 vNormal;', 'void main() {', '	vNormal = normalize( normalMatrix * normal );', '	gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', '}' ].join( '\n' ),
-				fragmentShader: [ '#define RECIPROCAL_PI 0.318309886', 'vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {', '	// matrix is assumed to be orthogonal', '	return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );', '}', '// source: https://graphics.stanford.edu/papers/envmap/envmap.pdf', 'vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {', '	// normal is assumed to have unit length', '	float x = normal.x, y = normal.y, z = normal.z;', '	// band 0', '	vec3 result = shCoefficients[ 0 ] * 0.886227;', '	// band 1', '	result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;', '	result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;', '	result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;', '	// band 2', '	result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;', '	result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;', '	result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );', '	result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;', '	result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );', '	return result;', '}', 'uniform vec3 sh[ 9 ]; // sh coefficients', 'uniform float intensity; // light probe intensity', 'varying vec3 vNormal;', 'void main() {', '	vec3 normal = normalize( vNormal );', '	vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );', '	vec3 irradiance = shGetIrradianceAt( worldNormal, sh );', '	vec3 outgoingLight = RECIPROCAL_PI * irradiance * intensity;', '	gl_FragColor = linearToOutputTexel( vec4( outgoingLight, 1.0 ) );', '}' ].join( '\n' )
-			} );
-			const geometry = new THREE.SphereGeometry( 1, 32, 16 );
-			super( geometry, material );
-			this.lightProbe = lightProbe;
-			this.size = size;
-			this.type = 'LightProbeHelper';
-			this.onBeforeRender();
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material.dispose();
-
-		}
-		onBeforeRender() {
-
-			this.position.copy( this.lightProbe.position );
-			this.scale.set( 1, 1, 1 ).multiplyScalar( this.size );
-			this.material.uniforms.intensity.value = this.lightProbe.intensity;
-
-		}
-
-	}
-
-	THREE.LightProbeHelper = LightProbeHelper;
-
-} )();

+ 0 - 76
examples/js/helpers/OctreeHelper.js

@@ -1,76 +0,0 @@
-( function () {
-
-	class OctreeHelper extends THREE.LineSegments {
-
-		constructor( octree, color = 0xffff00 ) {
-
-			super( new THREE.BufferGeometry(), new THREE.LineBasicMaterial( {
-				color: color,
-				toneMapped: false
-			} ) );
-			this.octree = octree;
-			this.color = color;
-			this.type = 'OctreeHelper';
-			this.update();
-
-		}
-		update() {
-
-			const vertices = [];
-			function traverse( tree ) {
-
-				for ( let i = 0; i < tree.length; i ++ ) {
-
-					const min = tree[ i ].box.min;
-					const max = tree[ i ].box.max;
-					vertices.push( max.x, max.y, max.z );
-					vertices.push( min.x, max.y, max.z ); // 0, 1
-					vertices.push( min.x, max.y, max.z );
-					vertices.push( min.x, min.y, max.z ); // 1, 2
-					vertices.push( min.x, min.y, max.z );
-					vertices.push( max.x, min.y, max.z ); // 2, 3
-					vertices.push( max.x, min.y, max.z );
-					vertices.push( max.x, max.y, max.z ); // 3, 0
-
-					vertices.push( max.x, max.y, min.z );
-					vertices.push( min.x, max.y, min.z ); // 4, 5
-					vertices.push( min.x, max.y, min.z );
-					vertices.push( min.x, min.y, min.z ); // 5, 6
-					vertices.push( min.x, min.y, min.z );
-					vertices.push( max.x, min.y, min.z ); // 6, 7
-					vertices.push( max.x, min.y, min.z );
-					vertices.push( max.x, max.y, min.z ); // 7, 4
-
-					vertices.push( max.x, max.y, max.z );
-					vertices.push( max.x, max.y, min.z ); // 0, 4
-					vertices.push( min.x, max.y, max.z );
-					vertices.push( min.x, max.y, min.z ); // 1, 5
-					vertices.push( min.x, min.y, max.z );
-					vertices.push( min.x, min.y, min.z ); // 2, 6
-					vertices.push( max.x, min.y, max.z );
-					vertices.push( max.x, min.y, min.z ); // 3, 7
-
-					traverse( tree[ i ].subTrees );
-
-				}
-
-			}
-
-			traverse( this.octree.subTrees );
-			this.geometry.dispose();
-			this.geometry = new THREE.BufferGeometry();
-			this.geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material.dispose();
-
-		}
-
-	}
-
-	THREE.OctreeHelper = OctreeHelper;
-
-} )();

+ 0 - 91
examples/js/helpers/PositionalAudioHelper.js

@@ -1,91 +0,0 @@
-( function () {
-
-	class PositionalAudioHelper extends THREE.Line {
-
-		constructor( audio, range = 1, divisionsInnerAngle = 16, divisionsOuterAngle = 2 ) {
-
-			const geometry = new THREE.BufferGeometry();
-			const divisions = divisionsInnerAngle + divisionsOuterAngle * 2;
-			const positions = new Float32Array( ( divisions * 3 + 3 ) * 3 );
-			geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-			const materialInnerAngle = new THREE.LineBasicMaterial( {
-				color: 0x00ff00
-			} );
-			const materialOuterAngle = new THREE.LineBasicMaterial( {
-				color: 0xffff00
-			} );
-			super( geometry, [ materialOuterAngle, materialInnerAngle ] );
-			this.audio = audio;
-			this.range = range;
-			this.divisionsInnerAngle = divisionsInnerAngle;
-			this.divisionsOuterAngle = divisionsOuterAngle;
-			this.type = 'PositionalAudioHelper';
-			this.update();
-
-		}
-		update() {
-
-			const audio = this.audio;
-			const range = this.range;
-			const divisionsInnerAngle = this.divisionsInnerAngle;
-			const divisionsOuterAngle = this.divisionsOuterAngle;
-			const coneInnerAngle = THREE.MathUtils.degToRad( audio.panner.coneInnerAngle );
-			const coneOuterAngle = THREE.MathUtils.degToRad( audio.panner.coneOuterAngle );
-			const halfConeInnerAngle = coneInnerAngle / 2;
-			const halfConeOuterAngle = coneOuterAngle / 2;
-			let start = 0;
-			let count = 0;
-			let i;
-			let stride;
-			const geometry = this.geometry;
-			const positionAttribute = geometry.attributes.position;
-			geometry.clearGroups();
-
-			//
-
-			function generateSegment( from, to, divisions, materialIndex ) {
-
-				const step = ( to - from ) / divisions;
-				positionAttribute.setXYZ( start, 0, 0, 0 );
-				count ++;
-				for ( i = from; i < to; i += step ) {
-
-					stride = start + count;
-					positionAttribute.setXYZ( stride, Math.sin( i ) * range, 0, Math.cos( i ) * range );
-					positionAttribute.setXYZ( stride + 1, Math.sin( Math.min( i + step, to ) ) * range, 0, Math.cos( Math.min( i + step, to ) ) * range );
-					positionAttribute.setXYZ( stride + 2, 0, 0, 0 );
-					count += 3;
-
-				}
-
-				geometry.addGroup( start, count, materialIndex );
-				start += count;
-				count = 0;
-
-			}
-
-			//
-
-			generateSegment( - halfConeOuterAngle, - halfConeInnerAngle, divisionsOuterAngle, 0 );
-			generateSegment( - halfConeInnerAngle, halfConeInnerAngle, divisionsInnerAngle, 1 );
-			generateSegment( halfConeInnerAngle, halfConeOuterAngle, divisionsOuterAngle, 0 );
-
-			//
-
-			positionAttribute.needsUpdate = true;
-			if ( coneInnerAngle === coneOuterAngle ) this.material[ 0 ].visible = false;
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material[ 0 ].dispose();
-			this.material[ 1 ].dispose();
-
-		}
-
-	}
-
-	THREE.PositionalAudioHelper = PositionalAudioHelper;
-
-} )();

+ 0 - 73
examples/js/helpers/RectAreaLightHelper.js

@@ -1,73 +0,0 @@
-( function () {
-
-	/**
- *  This helper must be added as a child of the light
- */
-
-	class RectAreaLightHelper extends THREE.Line {
-
-		constructor( light, color ) {
-
-			const positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ];
-			const geometry = new THREE.BufferGeometry();
-			geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
-			geometry.computeBoundingSphere();
-			const material = new THREE.LineBasicMaterial( {
-				fog: false
-			} );
-			super( geometry, material );
-			this.light = light;
-			this.color = color; // optional hardwired color for the helper
-			this.type = 'RectAreaLightHelper';
-
-			//
-
-			const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];
-			const geometry2 = new THREE.BufferGeometry();
-			geometry2.setAttribute( 'position', new THREE.Float32BufferAttribute( positions2, 3 ) );
-			geometry2.computeBoundingSphere();
-			this.add( new THREE.Mesh( geometry2, new THREE.MeshBasicMaterial( {
-				side: THREE.BackSide,
-				fog: false
-			} ) ) );
-
-		}
-		updateMatrixWorld() {
-
-			this.scale.set( 0.5 * this.light.width, 0.5 * this.light.height, 1 );
-			if ( this.color !== undefined ) {
-
-				this.material.color.set( this.color );
-				this.children[ 0 ].material.color.set( this.color );
-
-			} else {
-
-				this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
-
-				// prevent hue shift
-				const c = this.material.color;
-				const max = Math.max( c.r, c.g, c.b );
-				if ( max > 1 ) c.multiplyScalar( 1 / max );
-				this.children[ 0 ].material.color.copy( this.material.color );
-
-			}
-
-			// ignore world scale on light
-			this.matrixWorld.extractRotation( this.light.matrixWorld ).scale( this.scale ).copyPosition( this.light.matrixWorld );
-			this.children[ 0 ].matrixWorld.copy( this.matrixWorld );
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material.dispose();
-			this.children[ 0 ].geometry.dispose();
-			this.children[ 0 ].material.dispose();
-
-		}
-
-	}
-
-	THREE.RectAreaLightHelper = RectAreaLightHelper;
-
-} )();

+ 0 - 74
examples/js/helpers/VertexNormalsHelper.js

@@ -1,74 +0,0 @@
-( function () {
-
-	const _v1 = new THREE.Vector3();
-	const _v2 = new THREE.Vector3();
-	const _normalMatrix = new THREE.Matrix3();
-	class VertexNormalsHelper extends THREE.LineSegments {
-
-		constructor( object, size = 1, color = 0xff0000 ) {
-
-			const geometry = new THREE.BufferGeometry();
-			const nNormals = object.geometry.attributes.normal.count;
-			const positions = new THREE.Float32BufferAttribute( nNormals * 2 * 3, 3 );
-			geometry.setAttribute( 'position', positions );
-			super( geometry, new THREE.LineBasicMaterial( {
-				color,
-				toneMapped: false
-			} ) );
-			this.object = object;
-			this.size = size;
-			this.type = 'VertexNormalsHelper';
-
-			//
-
-			this.matrixAutoUpdate = false;
-			this.update();
-
-		}
-		update() {
-
-			this.object.updateMatrixWorld( true );
-			_normalMatrix.getNormalMatrix( this.object.matrixWorld );
-			const matrixWorld = this.object.matrixWorld;
-			const position = this.geometry.attributes.position;
-
-			//
-
-			const objGeometry = this.object.geometry;
-			if ( objGeometry ) {
-
-				const objPos = objGeometry.attributes.position;
-				const objNorm = objGeometry.attributes.normal;
-				let idx = 0;
-
-				// for simplicity, ignore index and drawcalls, and render every normal
-
-				for ( let j = 0, jl = objPos.count; j < jl; j ++ ) {
-
-					_v1.fromBufferAttribute( objPos, j ).applyMatrix4( matrixWorld );
-					_v2.fromBufferAttribute( objNorm, j );
-					_v2.applyMatrix3( _normalMatrix ).normalize().multiplyScalar( this.size ).add( _v1 );
-					position.setXYZ( idx, _v1.x, _v1.y, _v1.z );
-					idx = idx + 1;
-					position.setXYZ( idx, _v2.x, _v2.y, _v2.z );
-					idx = idx + 1;
-
-				}
-
-			}
-
-			position.needsUpdate = true;
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material.dispose();
-
-		}
-
-	}
-
-	THREE.VertexNormalsHelper = VertexNormalsHelper;
-
-} )();

+ 0 - 68
examples/js/helpers/VertexTangentsHelper.js

@@ -1,68 +0,0 @@
-( function () {
-
-	const _v1 = new THREE.Vector3();
-	const _v2 = new THREE.Vector3();
-	class VertexTangentsHelper extends THREE.LineSegments {
-
-		constructor( object, size = 1, color = 0x00ffff ) {
-
-			const geometry = new THREE.BufferGeometry();
-			const nTangents = object.geometry.attributes.tangent.count;
-			const positions = new THREE.Float32BufferAttribute( nTangents * 2 * 3, 3 );
-			geometry.setAttribute( 'position', positions );
-			super( geometry, new THREE.LineBasicMaterial( {
-				color,
-				toneMapped: false
-			} ) );
-			this.object = object;
-			this.size = size;
-			this.type = 'VertexTangentsHelper';
-
-			//
-
-			this.matrixAutoUpdate = false;
-			this.update();
-
-		}
-		update() {
-
-			this.object.updateMatrixWorld( true );
-			const matrixWorld = this.object.matrixWorld;
-			const position = this.geometry.attributes.position;
-
-			//
-
-			const objGeometry = this.object.geometry;
-			const objPos = objGeometry.attributes.position;
-			const objTan = objGeometry.attributes.tangent;
-			let idx = 0;
-
-			// for simplicity, ignore index and drawcalls, and render every tangent
-
-			for ( let j = 0, jl = objPos.count; j < jl; j ++ ) {
-
-				_v1.fromBufferAttribute( objPos, j ).applyMatrix4( matrixWorld );
-				_v2.fromBufferAttribute( objTan, j );
-				_v2.transformDirection( matrixWorld ).multiplyScalar( this.size ).add( _v1 );
-				position.setXYZ( idx, _v1.x, _v1.y, _v1.z );
-				idx = idx + 1;
-				position.setXYZ( idx, _v2.x, _v2.y, _v2.z );
-				idx = idx + 1;
-
-			}
-
-			position.needsUpdate = true;
-
-		}
-		dispose() {
-
-			this.geometry.dispose();
-			this.material.dispose();
-
-		}
-
-	}
-
-	THREE.VertexTangentsHelper = VertexTangentsHelper;
-
-} )();

+ 0 - 281
examples/js/helpers/ViewHelper.js

@@ -1,281 +0,0 @@
-( function () {
-
-	const vpTemp = new THREE.Vector4();
-	class ViewHelper extends THREE.Object3D {
-
-		constructor( editorCamera, dom ) {
-
-			super();
-			this.isViewHelper = true;
-			this.animating = false;
-			this.controls = null;
-			const color1 = new THREE.Color( '#ff3653' );
-			const color2 = new THREE.Color( '#8adb00' );
-			const color3 = new THREE.Color( '#2c8fff' );
-			const interactiveObjects = [];
-			const raycaster = new THREE.Raycaster();
-			const mouse = new THREE.Vector2();
-			const dummy = new THREE.Object3D();
-			const camera = new THREE.OrthographicCamera( - 2, 2, 2, - 2, 0, 4 );
-			camera.position.set( 0, 0, 2 );
-			const geometry = new THREE.BoxGeometry( 0.8, 0.05, 0.05 ).translate( 0.4, 0, 0 );
-			const xAxis = new THREE.Mesh( geometry, getAxisMaterial( color1 ) );
-			const yAxis = new THREE.Mesh( geometry, getAxisMaterial( color2 ) );
-			const zAxis = new THREE.Mesh( geometry, getAxisMaterial( color3 ) );
-			yAxis.rotation.z = Math.PI / 2;
-			zAxis.rotation.y = - Math.PI / 2;
-			this.add( xAxis );
-			this.add( zAxis );
-			this.add( yAxis );
-			const posXAxisHelper = new THREE.Sprite( getSpriteMaterial( color1, 'X' ) );
-			posXAxisHelper.userData.type = 'posX';
-			const posYAxisHelper = new THREE.Sprite( getSpriteMaterial( color2, 'Y' ) );
-			posYAxisHelper.userData.type = 'posY';
-			const posZAxisHelper = new THREE.Sprite( getSpriteMaterial( color3, 'Z' ) );
-			posZAxisHelper.userData.type = 'posZ';
-			const negXAxisHelper = new THREE.Sprite( getSpriteMaterial( color1 ) );
-			negXAxisHelper.userData.type = 'negX';
-			const negYAxisHelper = new THREE.Sprite( getSpriteMaterial( color2 ) );
-			negYAxisHelper.userData.type = 'negY';
-			const negZAxisHelper = new THREE.Sprite( getSpriteMaterial( color3 ) );
-			negZAxisHelper.userData.type = 'negZ';
-			posXAxisHelper.position.x = 1;
-			posYAxisHelper.position.y = 1;
-			posZAxisHelper.position.z = 1;
-			negXAxisHelper.position.x = - 1;
-			negXAxisHelper.scale.setScalar( 0.8 );
-			negYAxisHelper.position.y = - 1;
-			negYAxisHelper.scale.setScalar( 0.8 );
-			negZAxisHelper.position.z = - 1;
-			negZAxisHelper.scale.setScalar( 0.8 );
-			this.add( posXAxisHelper );
-			this.add( posYAxisHelper );
-			this.add( posZAxisHelper );
-			this.add( negXAxisHelper );
-			this.add( negYAxisHelper );
-			this.add( negZAxisHelper );
-			interactiveObjects.push( posXAxisHelper );
-			interactiveObjects.push( posYAxisHelper );
-			interactiveObjects.push( posZAxisHelper );
-			interactiveObjects.push( negXAxisHelper );
-			interactiveObjects.push( negYAxisHelper );
-			interactiveObjects.push( negZAxisHelper );
-			const point = new THREE.Vector3();
-			const dim = 128;
-			const turnRate = 2 * Math.PI; // turn rate in angles per second
-
-			this.render = function ( renderer ) {
-
-				this.quaternion.copy( editorCamera.quaternion ).invert();
-				this.updateMatrixWorld();
-				point.set( 0, 0, 1 );
-				point.applyQuaternion( editorCamera.quaternion );
-				if ( point.x >= 0 ) {
-
-					posXAxisHelper.material.opacity = 1;
-					negXAxisHelper.material.opacity = 0.5;
-
-				} else {
-
-					posXAxisHelper.material.opacity = 0.5;
-					negXAxisHelper.material.opacity = 1;
-
-				}
-
-				if ( point.y >= 0 ) {
-
-					posYAxisHelper.material.opacity = 1;
-					negYAxisHelper.material.opacity = 0.5;
-
-				} else {
-
-					posYAxisHelper.material.opacity = 0.5;
-					negYAxisHelper.material.opacity = 1;
-
-				}
-
-				if ( point.z >= 0 ) {
-
-					posZAxisHelper.material.opacity = 1;
-					negZAxisHelper.material.opacity = 0.5;
-
-				} else {
-
-					posZAxisHelper.material.opacity = 0.5;
-					negZAxisHelper.material.opacity = 1;
-
-				}
-
-				//
-
-				const x = dom.offsetWidth - dim;
-				renderer.clearDepth();
-				renderer.getViewport( vpTemp );
-				renderer.setViewport( x, 0, dim, dim );
-				renderer.render( this, camera );
-				renderer.setViewport( vpTemp.x, vpTemp.y, vpTemp.z, vpTemp.w );
-
-			};
-
-			const targetPosition = new THREE.Vector3();
-			const targetQuaternion = new THREE.Quaternion();
-			const q1 = new THREE.Quaternion();
-			const q2 = new THREE.Quaternion();
-			let radius = 0;
-			this.handleClick = function ( event ) {
-
-				if ( this.animating === true ) return false;
-				const rect = dom.getBoundingClientRect();
-				const offsetX = rect.left + ( dom.offsetWidth - dim );
-				const offsetY = rect.top + ( dom.offsetHeight - dim );
-				mouse.x = ( event.clientX - offsetX ) / ( rect.width - offsetX ) * 2 - 1;
-				mouse.y = - ( ( event.clientY - offsetY ) / ( rect.bottom - offsetY ) ) * 2 + 1;
-				raycaster.setFromCamera( mouse, camera );
-				const intersects = raycaster.intersectObjects( interactiveObjects );
-				if ( intersects.length > 0 ) {
-
-					const intersection = intersects[ 0 ];
-					const object = intersection.object;
-					prepareAnimationData( object, this.controls.center );
-					this.animating = true;
-					return true;
-
-				} else {
-
-					return false;
-
-				}
-
-			};
-
-			this.update = function ( delta ) {
-
-				const step = delta * turnRate;
-				const focusPoint = this.controls.center;
-
-				// animate position by doing a slerp and then scaling the position on the unit sphere
-
-				q1.rotateTowards( q2, step );
-				editorCamera.position.set( 0, 0, 1 ).applyQuaternion( q1 ).multiplyScalar( radius ).add( focusPoint );
-
-				// animate orientation
-
-				editorCamera.quaternion.rotateTowards( targetQuaternion, step );
-				if ( q1.angleTo( q2 ) === 0 ) {
-
-					this.animating = false;
-
-				}
-
-			};
-
-			this.dispose = function () {
-
-				geometry.dispose();
-				xAxis.material.dispose();
-				yAxis.material.dispose();
-				zAxis.material.dispose();
-				posXAxisHelper.material.map.dispose();
-				posYAxisHelper.material.map.dispose();
-				posZAxisHelper.material.map.dispose();
-				negXAxisHelper.material.map.dispose();
-				negYAxisHelper.material.map.dispose();
-				negZAxisHelper.material.map.dispose();
-				posXAxisHelper.material.dispose();
-				posYAxisHelper.material.dispose();
-				posZAxisHelper.material.dispose();
-				negXAxisHelper.material.dispose();
-				negYAxisHelper.material.dispose();
-				negZAxisHelper.material.dispose();
-
-			};
-
-			function prepareAnimationData( object, focusPoint ) {
-
-				switch ( object.userData.type ) {
-
-					case 'posX':
-						targetPosition.set( 1, 0, 0 );
-						targetQuaternion.setFromEuler( new THREE.Euler( 0, Math.PI * 0.5, 0 ) );
-						break;
-					case 'posY':
-						targetPosition.set( 0, 1, 0 );
-						targetQuaternion.setFromEuler( new THREE.Euler( - Math.PI * 0.5, 0, 0 ) );
-						break;
-					case 'posZ':
-						targetPosition.set( 0, 0, 1 );
-						targetQuaternion.setFromEuler( new THREE.Euler() );
-						break;
-					case 'negX':
-						targetPosition.set( - 1, 0, 0 );
-						targetQuaternion.setFromEuler( new THREE.Euler( 0, - Math.PI * 0.5, 0 ) );
-						break;
-					case 'negY':
-						targetPosition.set( 0, - 1, 0 );
-						targetQuaternion.setFromEuler( new THREE.Euler( Math.PI * 0.5, 0, 0 ) );
-						break;
-					case 'negZ':
-						targetPosition.set( 0, 0, - 1 );
-						targetQuaternion.setFromEuler( new THREE.Euler( 0, Math.PI, 0 ) );
-						break;
-					default:
-						console.error( 'ViewHelper: Invalid axis.' );
-
-				}
-
-				//
-
-				radius = editorCamera.position.distanceTo( focusPoint );
-				targetPosition.multiplyScalar( radius ).add( focusPoint );
-				dummy.position.copy( focusPoint );
-				dummy.lookAt( editorCamera.position );
-				q1.copy( dummy.quaternion );
-				dummy.lookAt( targetPosition );
-				q2.copy( dummy.quaternion );
-
-			}
-
-			function getAxisMaterial( color ) {
-
-				return new THREE.MeshBasicMaterial( {
-					color: color,
-					toneMapped: false
-				} );
-
-			}
-
-			function getSpriteMaterial( color, text = null ) {
-
-				const canvas = document.createElement( 'canvas' );
-				canvas.width = 64;
-				canvas.height = 64;
-				const context = canvas.getContext( '2d' );
-				context.beginPath();
-				context.arc( 32, 32, 16, 0, 2 * Math.PI );
-				context.closePath();
-				context.fillStyle = color.getStyle();
-				context.fill();
-				if ( text !== null ) {
-
-					context.font = '24px Arial';
-					context.textAlign = 'center';
-					context.fillStyle = '#000000';
-					context.fillText( text, 32, 41 );
-
-				}
-
-				const texture = new THREE.CanvasTexture( canvas );
-				return new THREE.SpriteMaterial( {
-					map: texture,
-					toneMapped: false
-				} );
-
-			}
-
-		}
-
-	}
-
-	THREE.ViewHelper = ViewHelper;
-
-} )();

+ 0 - 497
examples/js/interactive/HTMLMesh.js

@@ -1,497 +0,0 @@
-( function () {
-
-	class HTMLMesh extends THREE.Mesh {
-
-		constructor( dom ) {
-
-			const texture = new HTMLTexture( dom );
-			const geometry = new THREE.PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
-			const material = new THREE.MeshBasicMaterial( {
-				map: texture,
-				toneMapped: false,
-				transparent: true
-			} );
-			super( geometry, material );
-			function onEvent( event ) {
-
-				material.map.dispatchDOMEvent( event );
-
-			}
-
-			this.addEventListener( 'mousedown', onEvent );
-			this.addEventListener( 'mousemove', onEvent );
-			this.addEventListener( 'mouseup', onEvent );
-			this.addEventListener( 'click', onEvent );
-			this.dispose = function () {
-
-				geometry.dispose();
-				material.dispose();
-				material.map.dispose();
-				canvases.delete( dom );
-				this.removeEventListener( 'mousedown', onEvent );
-				this.removeEventListener( 'mousemove', onEvent );
-				this.removeEventListener( 'mouseup', onEvent );
-				this.removeEventListener( 'click', onEvent );
-
-			};
-
-		}
-
-	}
-	class HTMLTexture extends THREE.CanvasTexture {
-
-		constructor( dom ) {
-
-			super( html2canvas( dom ) );
-			this.dom = dom;
-			this.anisotropy = 16;
-			this.encoding = THREE.sRGBEncoding;
-			this.minFilter = THREE.LinearFilter;
-			this.magFilter = THREE.LinearFilter;
-
-			// Create an observer on the DOM, and run html2canvas update in the next loop
-			const observer = new MutationObserver( () => {
-
-				if ( ! this.scheduleUpdate ) {
-
-					// ideally should use xr.requestAnimationFrame, here setTimeout to avoid passing the renderer
-					this.scheduleUpdate = setTimeout( () => this.update(), 16 );
-
-				}
-
-			} );
-			const config = {
-				attributes: true,
-				childList: true,
-				subtree: true,
-				characterData: true
-			};
-			observer.observe( dom, config );
-			this.observer = observer;
-
-		}
-		dispatchDOMEvent( event ) {
-
-			if ( event.data ) {
-
-				htmlevent( this.dom, event.type, event.data.x, event.data.y );
-
-			}
-
-		}
-		update() {
-
-			this.image = html2canvas( this.dom );
-			this.needsUpdate = true;
-			this.scheduleUpdate = null;
-
-		}
-		dispose() {
-
-			if ( this.observer ) {
-
-				this.observer.disconnect();
-
-			}
-
-			this.scheduleUpdate = clearTimeout( this.scheduleUpdate );
-			super.dispose();
-
-		}
-
-	}
-
-	//
-
-	const canvases = new WeakMap();
-	function html2canvas( element ) {
-
-		const range = document.createRange();
-		const color = new THREE.Color();
-		function Clipper( context ) {
-
-			const clips = [];
-			let isClipping = false;
-			function doClip() {
-
-				if ( isClipping ) {
-
-					isClipping = false;
-					context.restore();
-
-				}
-
-				if ( clips.length === 0 ) return;
-				let minX = - Infinity,
-					minY = - Infinity;
-				let maxX = Infinity,
-					maxY = Infinity;
-				for ( let i = 0; i < clips.length; i ++ ) {
-
-					const clip = clips[ i ];
-					minX = Math.max( minX, clip.x );
-					minY = Math.max( minY, clip.y );
-					maxX = Math.min( maxX, clip.x + clip.width );
-					maxY = Math.min( maxY, clip.y + clip.height );
-
-				}
-
-				context.save();
-				context.beginPath();
-				context.rect( minX, minY, maxX - minX, maxY - minY );
-				context.clip();
-				isClipping = true;
-
-			}
-
-			return {
-				add: function ( clip ) {
-
-					clips.push( clip );
-					doClip();
-
-				},
-				remove: function () {
-
-					clips.pop();
-					doClip();
-
-				}
-			};
-
-		}
-
-		function drawText( style, x, y, string ) {
-
-			if ( string !== '' ) {
-
-				if ( style.textTransform === 'uppercase' ) {
-
-					string = string.toUpperCase();
-
-				}
-
-				context.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily;
-				context.textBaseline = 'top';
-				context.fillStyle = style.color;
-				context.fillText( string, x, y + parseFloat( style.fontSize ) * 0.1 );
-
-			}
-
-		}
-
-		function buildRectPath( x, y, w, h, r ) {
-
-			if ( w < 2 * r ) r = w / 2;
-			if ( h < 2 * r ) r = h / 2;
-			context.beginPath();
-			context.moveTo( x + r, y );
-			context.arcTo( x + w, y, x + w, y + h, r );
-			context.arcTo( x + w, y + h, x, y + h, r );
-			context.arcTo( x, y + h, x, y, r );
-			context.arcTo( x, y, x + w, y, r );
-			context.closePath();
-
-		}
-
-		function drawBorder( style, which, x, y, width, height ) {
-
-			const borderWidth = style[ which + 'Width' ];
-			const borderStyle = style[ which + 'Style' ];
-			const borderColor = style[ which + 'Color' ];
-			if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
-
-				context.strokeStyle = borderColor;
-				context.lineWidth = parseFloat( borderWidth );
-				context.beginPath();
-				context.moveTo( x, y );
-				context.lineTo( x + width, y + height );
-				context.stroke();
-
-			}
-
-		}
-
-		function drawElement( element, style ) {
-
-			let x = 0,
-				y = 0,
-				width = 0,
-				height = 0;
-			if ( element.nodeType === Node.TEXT_NODE ) {
-
-				// text
-
-				range.selectNode( element );
-				const rect = range.getBoundingClientRect();
-				x = rect.left - offset.left - 0.5;
-				y = rect.top - offset.top - 0.5;
-				width = rect.width;
-				height = rect.height;
-				drawText( style, x, y, element.nodeValue.trim() );
-
-			} else if ( element.nodeType === Node.COMMENT_NODE ) {
-
-				return;
-
-			} else if ( element instanceof HTMLCanvasElement ) {
-
-				// Canvas element
-				if ( element.style.display === 'none' ) return;
-				context.save();
-				const dpr = window.devicePixelRatio;
-				context.scale( 1 / dpr, 1 / dpr );
-				context.drawImage( element, 0, 0 );
-				context.restore();
-
-			} else {
-
-				if ( element.style.display === 'none' ) return;
-				const rect = element.getBoundingClientRect();
-				x = rect.left - offset.left - 0.5;
-				y = rect.top - offset.top - 0.5;
-				width = rect.width;
-				height = rect.height;
-				style = window.getComputedStyle( element );
-
-				// Get the border of the element used for fill and border
-
-				buildRectPath( x, y, width, height, parseFloat( style.borderRadius ) );
-				const backgroundColor = style.backgroundColor;
-				if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
-
-					context.fillStyle = backgroundColor;
-					context.fill();
-
-				}
-
-				// If all the borders match then stroke the round rectangle
-
-				const borders = [ 'borderTop', 'borderLeft', 'borderBottom', 'borderRight' ];
-				let match = true;
-				let prevBorder = null;
-				for ( const border of borders ) {
-
-					if ( prevBorder !== null ) {
-
-						match = style[ border + 'Width' ] === style[ prevBorder + 'Width' ] && style[ border + 'Color' ] === style[ prevBorder + 'Color' ] && style[ border + 'Style' ] === style[ prevBorder + 'Style' ];
-
-					}
-
-					if ( match === false ) break;
-					prevBorder = border;
-
-				}
-
-				if ( match === true ) {
-
-					// They all match so stroke the rectangle from before allows for border-radius
-
-					const width = parseFloat( style.borderTopWidth );
-					if ( style.borderTopWidth !== '0px' && style.borderTopStyle !== 'none' && style.borderTopColor !== 'transparent' && style.borderTopColor !== 'rgba(0, 0, 0, 0)' ) {
-
-						context.strokeStyle = style.borderTopColor;
-						context.lineWidth = width;
-						context.stroke();
-
-					}
-
-				} else {
-
-					// Otherwise draw individual borders
-
-					drawBorder( style, 'borderTop', x, y, width, 0 );
-					drawBorder( style, 'borderLeft', x, y, 0, height );
-					drawBorder( style, 'borderBottom', x, y + height, width, 0 );
-					drawBorder( style, 'borderRight', x + width, y, 0, height );
-
-				}
-
-				if ( element instanceof HTMLInputElement ) {
-
-					let accentColor = style.accentColor;
-					if ( accentColor === undefined || accentColor === 'auto' ) accentColor = style.color;
-					color.set( accentColor );
-					const luminance = Math.sqrt( 0.299 * color.r ** 2 + 0.587 * color.g ** 2 + 0.114 * color.b ** 2 );
-					const accentTextColor = luminance < 0.5 ? 'white' : '#111111';
-					if ( element.type === 'radio' ) {
-
-						buildRectPath( x, y, width, height, height );
-						context.fillStyle = 'white';
-						context.strokeStyle = accentColor;
-						context.lineWidth = 1;
-						context.fill();
-						context.stroke();
-						if ( element.checked ) {
-
-							buildRectPath( x + 2, y + 2, width - 4, height - 4, height );
-							context.fillStyle = accentColor;
-							context.strokeStyle = accentTextColor;
-							context.lineWidth = 2;
-							context.fill();
-							context.stroke();
-
-						}
-
-					}
-
-					if ( element.type === 'checkbox' ) {
-
-						buildRectPath( x, y, width, height, 2 );
-						context.fillStyle = element.checked ? accentColor : 'white';
-						context.strokeStyle = element.checked ? accentTextColor : accentColor;
-						context.lineWidth = 1;
-						context.stroke();
-						context.fill();
-						if ( element.checked ) {
-
-							const currentTextAlign = context.textAlign;
-							context.textAlign = 'center';
-							const properties = {
-								color: accentTextColor,
-								fontFamily: style.fontFamily,
-								fontSize: height + 'px',
-								fontWeight: 'bold'
-							};
-							drawText( properties, x + width / 2, y, '✔' );
-							context.textAlign = currentTextAlign;
-
-						}
-
-					}
-
-					if ( element.type === 'range' ) {
-
-						const [ min, max, value ] = [ 'min', 'max', 'value' ].map( property => parseFloat( element[ property ] ) );
-						const position = ( value - min ) / ( max - min ) * ( width - height );
-						buildRectPath( x, y + height / 4, width, height / 2, height / 4 );
-						context.fillStyle = accentTextColor;
-						context.strokeStyle = accentColor;
-						context.lineWidth = 1;
-						context.fill();
-						context.stroke();
-						buildRectPath( x, y + height / 4, position + height / 2, height / 2, height / 4 );
-						context.fillStyle = accentColor;
-						context.fill();
-						buildRectPath( x + position, y, height, height, height / 2 );
-						context.fillStyle = accentColor;
-						context.fill();
-
-					}
-
-					if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
-
-						clipper.add( {
-							x: x,
-							y: y,
-							width: width,
-							height: height
-						} );
-						drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
-						clipper.remove();
-
-					}
-
-				}
-
-			}
-
-			/*
-    // debug
-    context.strokeStyle = '#' + Math.random().toString( 16 ).slice( - 3 );
-    context.strokeRect( x - 0.5, y - 0.5, width + 1, height + 1 );
-    */
-
-			const isClipping = style.overflow === 'auto' || style.overflow === 'hidden';
-			if ( isClipping ) clipper.add( {
-				x: x,
-				y: y,
-				width: width,
-				height: height
-			} );
-			for ( let i = 0; i < element.childNodes.length; i ++ ) {
-
-				drawElement( element.childNodes[ i ], style );
-
-			}
-
-			if ( isClipping ) clipper.remove();
-
-		}
-
-		const offset = element.getBoundingClientRect();
-		let canvas = canvases.get( element );
-		if ( canvas === undefined ) {
-
-			canvas = document.createElement( 'canvas' );
-			canvas.width = offset.width;
-			canvas.height = offset.height;
-			canvases.set( element, canvas );
-
-		}
-
-		const context = canvas.getContext( '2d' /*, { alpha: false }*/ );
-
-		const clipper = new Clipper( context );
-
-		// console.time( 'drawElement' );
-
-		drawElement( element );
-
-		// console.timeEnd( 'drawElement' );
-
-		return canvas;
-
-	}
-
-	function htmlevent( element, event, x, y ) {
-
-		const mouseEventInit = {
-			clientX: x * element.offsetWidth + element.offsetLeft,
-			clientY: y * element.offsetHeight + element.offsetTop,
-			view: element.ownerDocument.defaultView
-		};
-		window.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
-		const rect = element.getBoundingClientRect();
-		x = x * rect.width + rect.left;
-		y = y * rect.height + rect.top;
-		function traverse( element ) {
-
-			if ( element.nodeType !== Node.TEXT_NODE && element.nodeType !== Node.COMMENT_NODE ) {
-
-				const rect = element.getBoundingClientRect();
-				if ( x > rect.left && x < rect.right && y > rect.top && y < rect.bottom ) {
-
-					element.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
-					if ( element instanceof HTMLInputElement && element.type === 'range' && ( event === 'mousedown' || event === 'click' ) ) {
-
-						const [ min, max ] = [ 'min', 'max' ].map( property => parseFloat( element[ property ] ) );
-						const width = rect.width;
-						const offsetX = x - rect.x;
-						const proportion = offsetX / width;
-						element.value = min + ( max - min ) * proportion;
-						element.dispatchEvent( new InputEvent( 'input', {
-							bubbles: true
-						} ) );
-
-					}
-
-				}
-
-				for ( let i = 0; i < element.childNodes.length; i ++ ) {
-
-					traverse( element.childNodes[ i ] );
-
-				}
-
-			}
-
-		}
-
-		traverse( element );
-
-	}
-
-	THREE.HTMLMesh = HTMLMesh;
-
-} )();

+ 0 - 95
examples/js/interactive/InteractiveGroup.js

@@ -1,95 +0,0 @@
-( function () {
-
-	const _pointer = new THREE.Vector2();
-	const _event = {
-		type: '',
-		data: _pointer
-	};
-	class InteractiveGroup extends THREE.Group {
-
-		constructor( renderer, camera ) {
-
-			super();
-			const scope = this;
-			const raycaster = new THREE.Raycaster();
-			const tempMatrix = new THREE.Matrix4();
-
-			// Pointer Events
-
-			const element = renderer.domElement;
-			function onPointerEvent( event ) {
-
-				event.stopPropagation();
-				const rect = renderer.domElement.getBoundingClientRect();
-				_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
-				_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
-				raycaster.setFromCamera( _pointer, camera );
-				const intersects = raycaster.intersectObjects( scope.children, false );
-				if ( intersects.length > 0 ) {
-
-					const intersection = intersects[ 0 ];
-					const object = intersection.object;
-					const uv = intersection.uv;
-					_event.type = event.type;
-					_event.data.set( uv.x, 1 - uv.y );
-					object.dispatchEvent( _event );
-
-				}
-
-			}
-
-			element.addEventListener( 'pointerdown', onPointerEvent );
-			element.addEventListener( 'pointerup', onPointerEvent );
-			element.addEventListener( 'pointermove', onPointerEvent );
-			element.addEventListener( 'mousedown', onPointerEvent );
-			element.addEventListener( 'mouseup', onPointerEvent );
-			element.addEventListener( 'mousemove', onPointerEvent );
-			element.addEventListener( 'click', onPointerEvent );
-
-			// WebXR Controller Events
-			// TODO: Dispatch pointerevents too
-
-			const events = {
-				'move': 'mousemove',
-				'select': 'click',
-				'selectstart': 'mousedown',
-				'selectend': 'mouseup'
-			};
-			function onXRControllerEvent( event ) {
-
-				const controller = event.target;
-				tempMatrix.identity().extractRotation( controller.matrixWorld );
-				raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
-				raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
-				const intersections = raycaster.intersectObjects( scope.children, false );
-				if ( intersections.length > 0 ) {
-
-					const intersection = intersections[ 0 ];
-					const object = intersection.object;
-					const uv = intersection.uv;
-					_event.type = events[ event.type ];
-					_event.data.set( uv.x, 1 - uv.y );
-					object.dispatchEvent( _event );
-
-				}
-
-			}
-
-			const controller1 = renderer.xr.getController( 0 );
-			controller1.addEventListener( 'move', onXRControllerEvent );
-			controller1.addEventListener( 'select', onXRControllerEvent );
-			controller1.addEventListener( 'selectstart', onXRControllerEvent );
-			controller1.addEventListener( 'selectend', onXRControllerEvent );
-			const controller2 = renderer.xr.getController( 1 );
-			controller2.addEventListener( 'move', onXRControllerEvent );
-			controller2.addEventListener( 'select', onXRControllerEvent );
-			controller2.addEventListener( 'selectstart', onXRControllerEvent );
-			controller2.addEventListener( 'selectend', onXRControllerEvent );
-
-		}
-
-	}
-
-	THREE.InteractiveGroup = InteractiveGroup;
-
-} )();

+ 0 - 195
examples/js/interactive/SelectionBox.js

@@ -1,195 +0,0 @@
-( function () {
-
-	/**
- * This is a class to check whether objects are in a selection area in 3D space
- */
-
-	const _frustum = new THREE.Frustum();
-	const _center = new THREE.Vector3();
-	const _tmpPoint = new THREE.Vector3();
-	const _vecNear = new THREE.Vector3();
-	const _vecTopLeft = new THREE.Vector3();
-	const _vecTopRight = new THREE.Vector3();
-	const _vecDownRight = new THREE.Vector3();
-	const _vecDownLeft = new THREE.Vector3();
-	const _vecFarTopLeft = new THREE.Vector3();
-	const _vecFarTopRight = new THREE.Vector3();
-	const _vecFarDownRight = new THREE.Vector3();
-	const _vecFarDownLeft = new THREE.Vector3();
-	const _vectemp1 = new THREE.Vector3();
-	const _vectemp2 = new THREE.Vector3();
-	const _vectemp3 = new THREE.Vector3();
-	const _matrix = new THREE.Matrix4();
-	const _quaternion = new THREE.Quaternion();
-	const _scale = new THREE.Vector3();
-	class SelectionBox {
-
-		constructor( camera, scene, deep = Number.MAX_VALUE ) {
-
-			this.camera = camera;
-			this.scene = scene;
-			this.startPoint = new THREE.Vector3();
-			this.endPoint = new THREE.Vector3();
-			this.collection = [];
-			this.instances = {};
-			this.deep = deep;
-
-		}
-		select( startPoint, endPoint ) {
-
-			this.startPoint = startPoint || this.startPoint;
-			this.endPoint = endPoint || this.endPoint;
-			this.collection = [];
-			this.updateFrustum( this.startPoint, this.endPoint );
-			this.searchChildInFrustum( _frustum, this.scene );
-			return this.collection;
-
-		}
-		updateFrustum( startPoint, endPoint ) {
-
-			startPoint = startPoint || this.startPoint;
-			endPoint = endPoint || this.endPoint;
-
-			// Avoid invalid frustum
-
-			if ( startPoint.x === endPoint.x ) {
-
-				endPoint.x += Number.EPSILON;
-
-			}
-
-			if ( startPoint.y === endPoint.y ) {
-
-				endPoint.y += Number.EPSILON;
-
-			}
-
-			this.camera.updateProjectionMatrix();
-			this.camera.updateMatrixWorld();
-			if ( this.camera.isPerspectiveCamera ) {
-
-				_tmpPoint.copy( startPoint );
-				_tmpPoint.x = Math.min( startPoint.x, endPoint.x );
-				_tmpPoint.y = Math.max( startPoint.y, endPoint.y );
-				endPoint.x = Math.max( startPoint.x, endPoint.x );
-				endPoint.y = Math.min( startPoint.y, endPoint.y );
-				_vecNear.setFromMatrixPosition( this.camera.matrixWorld );
-				_vecTopLeft.copy( _tmpPoint );
-				_vecTopRight.set( endPoint.x, _tmpPoint.y, 0 );
-				_vecDownRight.copy( endPoint );
-				_vecDownLeft.set( _tmpPoint.x, endPoint.y, 0 );
-				_vecTopLeft.unproject( this.camera );
-				_vecTopRight.unproject( this.camera );
-				_vecDownRight.unproject( this.camera );
-				_vecDownLeft.unproject( this.camera );
-				_vectemp1.copy( _vecTopLeft ).sub( _vecNear );
-				_vectemp2.copy( _vecTopRight ).sub( _vecNear );
-				_vectemp3.copy( _vecDownRight ).sub( _vecNear );
-				_vectemp1.normalize();
-				_vectemp2.normalize();
-				_vectemp3.normalize();
-				_vectemp1.multiplyScalar( this.deep );
-				_vectemp2.multiplyScalar( this.deep );
-				_vectemp3.multiplyScalar( this.deep );
-				_vectemp1.add( _vecNear );
-				_vectemp2.add( _vecNear );
-				_vectemp3.add( _vecNear );
-				const planes = _frustum.planes;
-				planes[ 0 ].setFromCoplanarPoints( _vecNear, _vecTopLeft, _vecTopRight );
-				planes[ 1 ].setFromCoplanarPoints( _vecNear, _vecTopRight, _vecDownRight );
-				planes[ 2 ].setFromCoplanarPoints( _vecDownRight, _vecDownLeft, _vecNear );
-				planes[ 3 ].setFromCoplanarPoints( _vecDownLeft, _vecTopLeft, _vecNear );
-				planes[ 4 ].setFromCoplanarPoints( _vecTopRight, _vecDownRight, _vecDownLeft );
-				planes[ 5 ].setFromCoplanarPoints( _vectemp3, _vectemp2, _vectemp1 );
-				planes[ 5 ].normal.multiplyScalar( - 1 );
-
-			} else if ( this.camera.isOrthographicCamera ) {
-
-				const left = Math.min( startPoint.x, endPoint.x );
-				const top = Math.max( startPoint.y, endPoint.y );
-				const right = Math.max( startPoint.x, endPoint.x );
-				const down = Math.min( startPoint.y, endPoint.y );
-				_vecTopLeft.set( left, top, - 1 );
-				_vecTopRight.set( right, top, - 1 );
-				_vecDownRight.set( right, down, - 1 );
-				_vecDownLeft.set( left, down, - 1 );
-				_vecFarTopLeft.set( left, top, 1 );
-				_vecFarTopRight.set( right, top, 1 );
-				_vecFarDownRight.set( right, down, 1 );
-				_vecFarDownLeft.set( left, down, 1 );
-				_vecTopLeft.unproject( this.camera );
-				_vecTopRight.unproject( this.camera );
-				_vecDownRight.unproject( this.camera );
-				_vecDownLeft.unproject( this.camera );
-				_vecFarTopLeft.unproject( this.camera );
-				_vecFarTopRight.unproject( this.camera );
-				_vecFarDownRight.unproject( this.camera );
-				_vecFarDownLeft.unproject( this.camera );
-				const planes = _frustum.planes;
-				planes[ 0 ].setFromCoplanarPoints( _vecTopLeft, _vecFarTopLeft, _vecFarTopRight );
-				planes[ 1 ].setFromCoplanarPoints( _vecTopRight, _vecFarTopRight, _vecFarDownRight );
-				planes[ 2 ].setFromCoplanarPoints( _vecFarDownRight, _vecFarDownLeft, _vecDownLeft );
-				planes[ 3 ].setFromCoplanarPoints( _vecFarDownLeft, _vecFarTopLeft, _vecTopLeft );
-				planes[ 4 ].setFromCoplanarPoints( _vecTopRight, _vecDownRight, _vecDownLeft );
-				planes[ 5 ].setFromCoplanarPoints( _vecFarDownRight, _vecFarTopRight, _vecFarTopLeft );
-				planes[ 5 ].normal.multiplyScalar( - 1 );
-
-			} else {
-
-				console.error( 'THREE.SelectionBox: Unsupported camera type.' );
-
-			}
-
-		}
-		searchChildInFrustum( frustum, object ) {
-
-			if ( object.isMesh || object.isLine || object.isPoints ) {
-
-				if ( object.isInstancedMesh ) {
-
-					this.instances[ object.uuid ] = [];
-					for ( let instanceId = 0; instanceId < object.count; instanceId ++ ) {
-
-						object.getMatrixAt( instanceId, _matrix );
-						_matrix.decompose( _center, _quaternion, _scale );
-						_center.applyMatrix4( object.matrixWorld );
-						if ( frustum.containsPoint( _center ) ) {
-
-							this.instances[ object.uuid ].push( instanceId );
-
-						}
-
-					}
-
-				} else {
-
-					if ( object.geometry.boundingSphere === null ) object.geometry.computeBoundingSphere();
-					_center.copy( object.geometry.boundingSphere.center );
-					_center.applyMatrix4( object.matrixWorld );
-					if ( frustum.containsPoint( _center ) ) {
-
-						this.collection.push( object );
-
-					}
-
-				}
-
-			}
-
-			if ( object.children.length > 0 ) {
-
-				for ( let x = 0; x < object.children.length; x ++ ) {
-
-					this.searchChildInFrustum( frustum, object.children[ x ] );
-
-				}
-
-			}
-
-		}
-
-	}
-
-	THREE.SelectionBox = SelectionBox;
-
-} )();

+ 0 - 83
examples/js/interactive/SelectionHelper.js

@@ -1,83 +0,0 @@
-( function () {
-
-	class SelectionHelper {
-
-		constructor( renderer, cssClassName ) {
-
-			this.element = document.createElement( 'div' );
-			this.element.classList.add( cssClassName );
-			this.element.style.pointerEvents = 'none';
-			this.renderer = renderer;
-			this.startPoint = new THREE.Vector2();
-			this.pointTopLeft = new THREE.Vector2();
-			this.pointBottomRight = new THREE.Vector2();
-			this.isDown = false;
-			this.onPointerDown = function ( event ) {
-
-				this.isDown = true;
-				this.onSelectStart( event );
-
-			}.bind( this );
-			this.onPointerMove = function ( event ) {
-
-				if ( this.isDown ) {
-
-					this.onSelectMove( event );
-
-				}
-
-			}.bind( this );
-			this.onPointerUp = function () {
-
-				this.isDown = false;
-				this.onSelectOver();
-
-			}.bind( this );
-			this.renderer.domElement.addEventListener( 'pointerdown', this.onPointerDown );
-			this.renderer.domElement.addEventListener( 'pointermove', this.onPointerMove );
-			this.renderer.domElement.addEventListener( 'pointerup', this.onPointerUp );
-
-		}
-		dispose() {
-
-			this.renderer.domElement.removeEventListener( 'pointerdown', this.onPointerDown );
-			this.renderer.domElement.removeEventListener( 'pointermove', this.onPointerMove );
-			this.renderer.domElement.removeEventListener( 'pointerup', this.onPointerUp );
-
-		}
-		onSelectStart( event ) {
-
-			this.element.style.display = 'none';
-			this.renderer.domElement.parentElement.appendChild( this.element );
-			this.element.style.left = event.clientX + 'px';
-			this.element.style.top = event.clientY + 'px';
-			this.element.style.width = '0px';
-			this.element.style.height = '0px';
-			this.startPoint.x = event.clientX;
-			this.startPoint.y = event.clientY;
-
-		}
-		onSelectMove( event ) {
-
-			this.element.style.display = 'block';
-			this.pointBottomRight.x = Math.max( this.startPoint.x, event.clientX );
-			this.pointBottomRight.y = Math.max( this.startPoint.y, event.clientY );
-			this.pointTopLeft.x = Math.min( this.startPoint.x, event.clientX );
-			this.pointTopLeft.y = Math.min( this.startPoint.y, event.clientY );
-			this.element.style.left = this.pointTopLeft.x + 'px';
-			this.element.style.top = this.pointTopLeft.y + 'px';
-			this.element.style.width = this.pointBottomRight.x - this.pointTopLeft.x + 'px';
-			this.element.style.height = this.pointBottomRight.y - this.pointTopLeft.y + 'px';
-
-		}
-		onSelectOver() {
-
-			this.element.parentElement.removeChild( this.element );
-
-		}
-
-	}
-
-	THREE.SelectionHelper = SelectionHelper;
-
-} )();

文件差異過大導致無法顯示
+ 0 - 2
examples/js/libs/chevrotain.min.js


文件差異過大導致無法顯示
+ 0 - 6
examples/js/libs/fflate.min.js


文件差異過大導致無法顯示
+ 0 - 0
examples/js/libs/ktx-parse.umd.js


文件差異過大導致無法顯示
+ 0 - 7
examples/js/libs/meshopt_decoder.js


文件差異過大導致無法顯示
+ 0 - 0
examples/js/libs/opentype.min.js


+ 0 - 5
examples/js/libs/stats.min.js

@@ -1,5 +0,0 @@
-// stats.js - http://github.com/mrdoob/stats.js
-var Stats=function(){function h(a){c.appendChild(a.dom);return a}function k(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();k(++l%c.children.length)},!1);var g=(performance||Date).now(),e=g,a=0,r=h(new Stats.Panel("FPS","#0ff","#002")),f=h(new Stats.Panel("MS","#0f0","#020"));
-if(self.performance&&self.performance.memory)var t=h(new Stats.Panel("MB","#f08","#201"));k(0);return{REVISION:16,dom:c,addPanel:h,showPanel:k,begin:function(){g=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();f.update(c-g,200);if(c>e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}};
-Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f,
-v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module&&(module.exports=Stats);

+ 0 - 221
examples/js/lights/LightProbeGenerator.js

@@ -1,221 +0,0 @@
-( function () {
-
-	class LightProbeGenerator {
-
-		// https://www.ppsloan.org/publications/StupidSH36.pdf
-		static fromCubeTexture( cubeTexture ) {
-
-			let totalWeight = 0;
-			const coord = new THREE.Vector3();
-			const dir = new THREE.Vector3();
-			const color = new THREE.Color();
-			const shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
-			const sh = new THREE.SphericalHarmonics3();
-			const shCoefficients = sh.coefficients;
-			for ( let faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
-
-				const image = cubeTexture.image[ faceIndex ];
-				const width = image.width;
-				const height = image.height;
-				const canvas = document.createElement( 'canvas' );
-				canvas.width = width;
-				canvas.height = height;
-				const context = canvas.getContext( '2d' );
-				context.drawImage( image, 0, 0, width, height );
-				const imageData = context.getImageData( 0, 0, width, height );
-				const data = imageData.data;
-				const imageWidth = imageData.width; // assumed to be square
-
-				const pixelSize = 2 / imageWidth;
-				for ( let 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
-					convertColorToLinear( color, cubeTexture.encoding );
-
-					// pixel coordinate on unit cube
-
-					const pixelIndex = i / 4;
-					const col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
-					const 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
-
-					const lengthSq = coord.lengthSq();
-					const weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
-					totalWeight += weight;
-
-					// direction vector to this pixel
-					dir.copy( coord ).normalize();
-
-					// evaluate SH basis functions in direction dir
-					THREE.SphericalHarmonics3.getBasisAt( dir, shBasis );
-
-					// accummuulate
-					for ( let 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
-			const norm = 4 * Math.PI / totalWeight;
-			for ( let j = 0; j < 9; j ++ ) {
-
-				shCoefficients[ j ].x *= norm;
-				shCoefficients[ j ].y *= norm;
-				shCoefficients[ j ].z *= norm;
-
-			}
-
-			return new THREE.LightProbe( sh );
-
-		}
-		static fromCubeRenderTarget( renderer, cubeRenderTarget ) {
-
-			// The renderTarget must be set to RGBA in order to make readRenderTargetPixels works
-			let totalWeight = 0;
-			const coord = new THREE.Vector3();
-			const dir = new THREE.Vector3();
-			const color = new THREE.Color();
-			const shBasis = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
-			const sh = new THREE.SphericalHarmonics3();
-			const shCoefficients = sh.coefficients;
-			for ( let faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
-
-				const imageWidth = cubeRenderTarget.width; // assumed to be square
-				const data = new Uint8Array( imageWidth * imageWidth * 4 );
-				renderer.readRenderTargetPixels( cubeRenderTarget, 0, 0, imageWidth, imageWidth, data, faceIndex );
-				const pixelSize = 2 / imageWidth;
-				for ( let 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
-					convertColorToLinear( color, cubeRenderTarget.texture.encoding );
-
-					// pixel coordinate on unit cube
-
-					const pixelIndex = i / 4;
-					const col = - 1 + ( pixelIndex % imageWidth + 0.5 ) * pixelSize;
-					const 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
-
-					const lengthSq = coord.lengthSq();
-					const weight = 4 / ( Math.sqrt( lengthSq ) * lengthSq );
-					totalWeight += weight;
-
-					// direction vector to this pixel
-					dir.copy( coord ).normalize();
-
-					// evaluate SH basis functions in direction dir
-					THREE.SphericalHarmonics3.getBasisAt( dir, shBasis );
-
-					// accummuulate
-					for ( let 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
-			const norm = 4 * Math.PI / totalWeight;
-			for ( let j = 0; j < 9; j ++ ) {
-
-				shCoefficients[ j ].x *= norm;
-				shCoefficients[ j ].y *= norm;
-				shCoefficients[ j ].z *= norm;
-
-			}
-
-			return new THREE.LightProbe( sh );
-
-		}
-
-	}
-	function convertColorToLinear( color, encoding ) {
-
-		switch ( encoding ) {
-
-			case THREE.sRGBEncoding:
-				color.convertSRGBToLinear();
-				break;
-			case THREE.LinearEncoding:
-				break;
-			default:
-				console.warn( 'WARNING: LightProbeGenerator convertColorToLinear() encountered an unsupported encoding.' );
-				break;
-
-		}
-
-		return color;
-
-	}
-
-	THREE.LightProbeGenerator = LightProbeGenerator;
-
-} )();

文件差異過大導致無法顯示
+ 0 - 25
examples/js/lights/RectAreaLightUniformsLib.js


+ 0 - 19
examples/js/lines/Line2.js

@@ -1,19 +0,0 @@
-( function () {
-
-	class Line2 extends THREE.LineSegments2 {
-
-		constructor( geometry = new THREE.LineGeometry(), material = new THREE.LineMaterial( {
-			color: Math.random() * 0xffffff
-		} ) ) {
-
-			super( geometry, material );
-			this.isLine2 = true;
-			this.type = 'Line2';
-
-		}
-
-	}
-
-	THREE.Line2 = Line2;
-
-} )();

+ 0 - 69
examples/js/lines/LineGeometry.js

@@ -1,69 +0,0 @@
-( function () {
-
-	class LineGeometry extends THREE.LineSegmentsGeometry {
-
-		constructor() {
-
-			super();
-			this.isLineGeometry = true;
-			this.type = 'LineGeometry';
-
-		}
-		setPositions( array ) {
-
-			// converts [ x1, y1, z1,  x2, y2, z2, ... ] to pairs format
-
-			const length = array.length - 3;
-			const points = new Float32Array( 2 * length );
-			for ( let i = 0; i < length; i += 3 ) {
-
-				points[ 2 * i ] = array[ i ];
-				points[ 2 * i + 1 ] = array[ i + 1 ];
-				points[ 2 * i + 2 ] = array[ i + 2 ];
-				points[ 2 * i + 3 ] = array[ i + 3 ];
-				points[ 2 * i + 4 ] = array[ i + 4 ];
-				points[ 2 * i + 5 ] = array[ i + 5 ];
-
-			}
-
-			super.setPositions( points );
-			return this;
-
-		}
-		setColors( array ) {
-
-			// converts [ r1, g1, b1,  r2, g2, b2, ... ] to pairs format
-
-			const length = array.length - 3;
-			const colors = new Float32Array( 2 * length );
-			for ( let i = 0; i < length; i += 3 ) {
-
-				colors[ 2 * i ] = array[ i ];
-				colors[ 2 * i + 1 ] = array[ i + 1 ];
-				colors[ 2 * i + 2 ] = array[ i + 2 ];
-				colors[ 2 * i + 3 ] = array[ i + 3 ];
-				colors[ 2 * i + 4 ] = array[ i + 4 ];
-				colors[ 2 * i + 5 ] = array[ i + 5 ];
-
-			}
-
-			super.setColors( colors );
-			return this;
-
-		}
-		fromLine( line ) {
-
-			const geometry = line.geometry;
-			this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
-
-			// set colors, maybe
-
-			return this;
-
-		}
-
-	}
-
-	THREE.LineGeometry = LineGeometry;
-
-} )();

+ 0 - 635
examples/js/lines/LineMaterial.js

@@ -1,635 +0,0 @@
-( function () {
-
-	/**
- * parameters = {
- *  color: <hex>,
- *  linewidth: <float>,
- *  dashed: <boolean>,
- *  dashScale: <float>,
- *  dashSize: <float>,
- *  dashOffset: <float>,
- *  gapSize: <float>,
- *  resolution: <Vector2>, // to be set by renderer
- * }
- */
-	THREE.UniformsLib.line = {
-		worldUnits: {
-			value: 1
-		},
-		linewidth: {
-			value: 1
-		},
-		resolution: {
-			value: new THREE.Vector2( 1, 1 )
-		},
-		dashOffset: {
-			value: 0
-		},
-		dashScale: {
-			value: 1
-		},
-		dashSize: {
-			value: 1
-		},
-		gapSize: {
-			value: 1
-		} // todo FIX - maybe change to totalSize
-	};
-
-	THREE.ShaderLib[ 'line' ] = {
-		uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.common, THREE.UniformsLib.fog, THREE.UniformsLib.line ] ),
-		vertexShader: /* glsl */`
-		#include <common>
-		#include <color_pars_vertex>
-		#include <fog_pars_vertex>
-		#include <logdepthbuf_pars_vertex>
-		#include <clipping_planes_pars_vertex>
-
-		uniform float linewidth;
-		uniform vec2 resolution;
-
-		attribute vec3 instanceStart;
-		attribute vec3 instanceEnd;
-
-		attribute vec3 instanceColorStart;
-		attribute vec3 instanceColorEnd;
-
-		#ifdef WORLD_UNITS
-
-			varying vec4 worldPos;
-			varying vec3 worldStart;
-			varying vec3 worldEnd;
-
-			#ifdef USE_DASH
-
-				varying vec2 vUv;
-
-			#endif
-
-		#else
-
-			varying vec2 vUv;
-
-		#endif
-
-		#ifdef USE_DASH
-
-			uniform float dashScale;
-			attribute float instanceDistanceStart;
-			attribute float instanceDistanceEnd;
-			varying float vLineDistance;
-
-		#endif
-
-		void trimSegment( const in vec4 start, inout vec4 end ) {
-
-			// trim end segment so it terminates between the camera plane and the near plane
-
-			// conservative estimate of the near plane
-			float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column
-			float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column
-			float nearEstimate = - 0.5 * b / a;
-
-			float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );
-
-			end.xyz = mix( start.xyz, end.xyz, alpha );
-
-		}
-
-		void main() {
-
-			#ifdef USE_COLOR
-
-				vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;
-
-			#endif
-
-			#ifdef USE_DASH
-
-				vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
-				vUv = uv;
-
-			#endif
-
-			float aspect = resolution.x / resolution.y;
-
-			// camera space
-			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
-			vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
-
-			#ifdef WORLD_UNITS
-
-				worldStart = start.xyz;
-				worldEnd = end.xyz;
-
-			#else
-
-				vUv = uv;
-
-			#endif
-
-			// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
-			// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
-			// but we need to perform ndc-space calculations in the shader, so we must address this issue directly
-			// perhaps there is a more elegant solution -- WestLangley
-
-			bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column
-
-			if ( perspective ) {
-
-				if ( start.z < 0.0 && end.z >= 0.0 ) {
-
-					trimSegment( start, end );
-
-				} else if ( end.z < 0.0 && start.z >= 0.0 ) {
-
-					trimSegment( end, start );
-
-				}
-
-			}
-
-			// clip space
-			vec4 clipStart = projectionMatrix * start;
-			vec4 clipEnd = projectionMatrix * end;
-
-			// ndc space
-			vec3 ndcStart = clipStart.xyz / clipStart.w;
-			vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
-
-			// direction
-			vec2 dir = ndcEnd.xy - ndcStart.xy;
-
-			// account for clip-space aspect ratio
-			dir.x *= aspect;
-			dir = normalize( dir );
-
-			#ifdef WORLD_UNITS
-
-				// get the offset direction as perpendicular to the view vector
-				vec3 worldDir = normalize( end.xyz - start.xyz );
-				vec3 offset;
-				if ( position.y < 0.5 ) {
-
-					offset = normalize( cross( start.xyz, worldDir ) );
-
-				} else {
-
-					offset = normalize( cross( end.xyz, worldDir ) );
-
-				}
-
-				// sign flip
-				if ( position.x < 0.0 ) offset *= - 1.0;
-
-				float forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );
-
-				// don't extend the line if we're rendering dashes because we
-				// won't be rendering the endcaps
-				#ifndef USE_DASH
-
-					// extend the line bounds to encompass  endcaps
-					start.xyz += - worldDir * linewidth * 0.5;
-					end.xyz += worldDir * linewidth * 0.5;
-
-					// shift the position of the quad so it hugs the forward edge of the line
-					offset.xy -= dir * forwardOffset;
-					offset.z += 0.5;
-
-				#endif
-
-				// endcaps
-				if ( position.y > 1.0 || position.y < 0.0 ) {
-
-					offset.xy += dir * 2.0 * forwardOffset;
-
-				}
-
-				// adjust for linewidth
-				offset *= linewidth * 0.5;
-
-				// set the world position
-				worldPos = ( position.y < 0.5 ) ? start : end;
-				worldPos.xyz += offset;
-
-				// project the worldpos
-				vec4 clip = projectionMatrix * worldPos;
-
-				// shift the depth of the projected points so the line
-				// segments overlap neatly
-				vec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;
-				clip.z = clipPose.z * clip.w;
-
-			#else
-
-				vec2 offset = vec2( dir.y, - dir.x );
-				// undo aspect ratio adjustment
-				dir.x /= aspect;
-				offset.x /= aspect;
-
-				// sign flip
-				if ( position.x < 0.0 ) offset *= - 1.0;
-
-				// endcaps
-				if ( position.y < 0.0 ) {
-
-					offset += - dir;
-
-				} else if ( position.y > 1.0 ) {
-
-					offset += dir;
-
-				}
-
-				// adjust for linewidth
-				offset *= linewidth;
-
-				// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...
-				offset /= resolution.y;
-
-				// select end
-				vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
-
-				// back to clip space
-				offset *= clip.w;
-
-				clip.xy += offset;
-
-			#endif
-
-			gl_Position = clip;
-
-			vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation
-
-			#include <logdepthbuf_vertex>
-			#include <clipping_planes_vertex>
-			#include <fog_vertex>
-
-		}
-		`,
-		fragmentShader: /* glsl */`
-		uniform vec3 diffuse;
-		uniform float opacity;
-		uniform float linewidth;
-
-		#ifdef USE_DASH
-
-			uniform float dashOffset;
-			uniform float dashSize;
-			uniform float gapSize;
-
-		#endif
-
-		varying float vLineDistance;
-
-		#ifdef WORLD_UNITS
-
-			varying vec4 worldPos;
-			varying vec3 worldStart;
-			varying vec3 worldEnd;
-
-			#ifdef USE_DASH
-
-				varying vec2 vUv;
-
-			#endif
-
-		#else
-
-			varying vec2 vUv;
-
-		#endif
-
-		#include <common>
-		#include <color_pars_fragment>
-		#include <fog_pars_fragment>
-		#include <logdepthbuf_pars_fragment>
-		#include <clipping_planes_pars_fragment>
-
-		vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
-
-			float mua;
-			float mub;
-
-			vec3 p13 = p1 - p3;
-			vec3 p43 = p4 - p3;
-
-			vec3 p21 = p2 - p1;
-
-			float d1343 = dot( p13, p43 );
-			float d4321 = dot( p43, p21 );
-			float d1321 = dot( p13, p21 );
-			float d4343 = dot( p43, p43 );
-			float d2121 = dot( p21, p21 );
-
-			float denom = d2121 * d4343 - d4321 * d4321;
-
-			float numer = d1343 * d4321 - d1321 * d4343;
-
-			mua = numer / denom;
-			mua = clamp( mua, 0.0, 1.0 );
-			mub = ( d1343 + d4321 * ( mua ) ) / d4343;
-			mub = clamp( mub, 0.0, 1.0 );
-
-			return vec2( mua, mub );
-
-		}
-
-		void main() {
-
-			#include <clipping_planes_fragment>
-
-			#ifdef USE_DASH
-
-				if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
-
-				if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
-
-			#endif
-
-			float alpha = opacity;
-
-			#ifdef WORLD_UNITS
-
-				// Find the closest points on the view ray and the line segment
-				vec3 rayEnd = normalize( worldPos.xyz ) * 1e5;
-				vec3 lineDir = worldEnd - worldStart;
-				vec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );
-
-				vec3 p1 = worldStart + lineDir * params.x;
-				vec3 p2 = rayEnd * params.y;
-				vec3 delta = p1 - p2;
-				float len = length( delta );
-				float norm = len / linewidth;
-
-				#ifndef USE_DASH
-
-					#ifdef USE_ALPHA_TO_COVERAGE
-
-						float dnorm = fwidth( norm );
-						alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
-
-					#else
-
-						if ( norm > 0.5 ) {
-
-							discard;
-
-						}
-
-					#endif
-
-				#endif
-
-			#else
-
-				#ifdef USE_ALPHA_TO_COVERAGE
-
-					// artifacts appear on some hardware if a derivative is taken within a conditional
-					float a = vUv.x;
-					float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
-					float len2 = a * a + b * b;
-					float dlen = fwidth( len2 );
-
-					if ( abs( vUv.y ) > 1.0 ) {
-
-						alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
-
-					}
-
-				#else
-
-					if ( abs( vUv.y ) > 1.0 ) {
-
-						float a = vUv.x;
-						float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
-						float len2 = a * a + b * b;
-
-						if ( len2 > 1.0 ) discard;
-
-					}
-
-				#endif
-
-			#endif
-
-			vec4 diffuseColor = vec4( diffuse, alpha );
-
-			#include <logdepthbuf_fragment>
-			#include <color_fragment>
-
-			gl_FragColor = vec4( diffuseColor.rgb, alpha );
-
-			#include <tonemapping_fragment>
-			#include <encodings_fragment>
-			#include <fog_fragment>
-			#include <premultiplied_alpha_fragment>
-
-		}
-		`
-	};
-	class LineMaterial extends THREE.ShaderMaterial {
-
-		constructor( parameters ) {
-
-			super( {
-				type: 'LineMaterial',
-				uniforms: THREE.UniformsUtils.clone( THREE.ShaderLib[ 'line' ].uniforms ),
-				vertexShader: THREE.ShaderLib[ 'line' ].vertexShader,
-				fragmentShader: THREE.ShaderLib[ 'line' ].fragmentShader,
-				clipping: true // required for clipping support
-			} );
-
-			this.isLineMaterial = true;
-			Object.defineProperties( this, {
-				color: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.diffuse.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.diffuse.value = value;
-
-					}
-				},
-				worldUnits: {
-					enumerable: true,
-					get: function () {
-
-						return 'WORLD_UNITS' in this.defines;
-
-					},
-					set: function ( value ) {
-
-						if ( value === true ) {
-
-							this.defines.WORLD_UNITS = '';
-
-						} else {
-
-							delete this.defines.WORLD_UNITS;
-
-						}
-
-					}
-				},
-				linewidth: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.linewidth.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.linewidth.value = value;
-
-					}
-				},
-				dashed: {
-					enumerable: true,
-					get: function () {
-
-						return Boolean( 'USE_DASH' in this.defines );
-
-					},
-					set( value ) {
-
-						if ( Boolean( value ) !== Boolean( 'USE_DASH' in this.defines ) ) {
-
-							this.needsUpdate = true;
-
-						}
-
-						if ( value === true ) {
-
-							this.defines.USE_DASH = '';
-
-						} else {
-
-							delete this.defines.USE_DASH;
-
-						}
-
-					}
-				},
-				dashScale: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.dashScale.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.dashScale.value = value;
-
-					}
-				},
-				dashSize: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.dashSize.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.dashSize.value = value;
-
-					}
-				},
-				dashOffset: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.dashOffset.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.dashOffset.value = value;
-
-					}
-				},
-				gapSize: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.gapSize.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.gapSize.value = value;
-
-					}
-				},
-				opacity: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.opacity.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.opacity.value = value;
-
-					}
-				},
-				resolution: {
-					enumerable: true,
-					get: function () {
-
-						return this.uniforms.resolution.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.resolution.value.copy( value );
-
-					}
-				},
-				alphaToCoverage: {
-					enumerable: true,
-					get: function () {
-
-						return Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines );
-
-					},
-					set: function ( value ) {
-
-						if ( Boolean( value ) !== Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines ) ) {
-
-							this.needsUpdate = true;
-
-						}
-
-						if ( value === true ) {
-
-							this.defines.USE_ALPHA_TO_COVERAGE = '';
-							this.extensions.derivatives = true;
-
-						} else {
-
-							delete this.defines.USE_ALPHA_TO_COVERAGE;
-							this.extensions.derivatives = false;
-
-						}
-
-					}
-				}
-			} );
-			this.setValues( parameters );
-
-		}
-
-	}
-
-	THREE.LineMaterial = LineMaterial;
-
-} )();

+ 0 - 313
examples/js/lines/LineSegments2.js

@@ -1,313 +0,0 @@
-( function () {
-
-	const _start = new THREE.Vector3();
-	const _end = new THREE.Vector3();
-	const _start4 = new THREE.Vector4();
-	const _end4 = new THREE.Vector4();
-	const _ssOrigin = new THREE.Vector4();
-	const _ssOrigin3 = new THREE.Vector3();
-	const _mvMatrix = new THREE.Matrix4();
-	const _line = new THREE.Line3();
-	const _closestPoint = new THREE.Vector3();
-	const _box = new THREE.Box3();
-	const _sphere = new THREE.Sphere();
-	const _clipToWorldVector = new THREE.Vector4();
-	let _ray, _instanceStart, _instanceEnd, _lineWidth;
-
-	// Returns the margin required to expand by in world space given the distance from the camera,
-	// line width, resolution, and camera projection
-	function getWorldSpaceHalfWidth( camera, distance, resolution ) {
-
-		// transform into clip space, adjust the x and y values by the pixel width offset, then
-		// transform back into world space to get world offset. Note clip space is [-1, 1] so full
-		// width does not need to be halved.
-		_clipToWorldVector.set( 0, 0, - distance, 1.0 ).applyMatrix4( camera.projectionMatrix );
-		_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
-		_clipToWorldVector.x = _lineWidth / resolution.width;
-		_clipToWorldVector.y = _lineWidth / resolution.height;
-		_clipToWorldVector.applyMatrix4( camera.projectionMatrixInverse );
-		_clipToWorldVector.multiplyScalar( 1.0 / _clipToWorldVector.w );
-		return Math.abs( Math.max( _clipToWorldVector.x, _clipToWorldVector.y ) );
-
-	}
-
-	function raycastWorldUnits( lineSegments, intersects ) {
-
-		const matrixWorld = lineSegments.matrixWorld;
-		for ( let i = 0, l = _instanceStart.count; i < l; i ++ ) {
-
-			_line.start.fromBufferAttribute( _instanceStart, i );
-			_line.end.fromBufferAttribute( _instanceEnd, i );
-			_line.applyMatrix4( matrixWorld );
-			const pointOnLine = new THREE.Vector3();
-			const point = new THREE.Vector3();
-			_ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
-			const isInside = point.distanceTo( pointOnLine ) < _lineWidth * 0.5;
-			if ( isInside ) {
-
-				intersects.push( {
-					point,
-					pointOnLine,
-					distance: _ray.origin.distanceTo( point ),
-					object: lineSegments,
-					face: null,
-					faceIndex: i,
-					uv: null,
-					uv2: null
-				} );
-
-			}
-
-		}
-
-	}
-
-	function raycastScreenSpace( lineSegments, camera, intersects ) {
-
-		const projectionMatrix = camera.projectionMatrix;
-		const material = lineSegments.material;
-		const resolution = material.resolution;
-		const matrixWorld = lineSegments.matrixWorld;
-		const geometry = lineSegments.geometry;
-		const instanceStart = geometry.attributes.instanceStart;
-		const instanceEnd = geometry.attributes.instanceEnd;
-		const near = - camera.near;
-
-		//
-
-		// pick a point 1 unit out along the ray to avoid the ray origin
-		// sitting at the camera origin which will cause "w" to be 0 when
-		// applying the projection matrix.
-		_ray.at( 1, _ssOrigin );
-
-		// ndc space [ - 1.0, 1.0 ]
-		_ssOrigin.w = 1;
-		_ssOrigin.applyMatrix4( camera.matrixWorldInverse );
-		_ssOrigin.applyMatrix4( projectionMatrix );
-		_ssOrigin.multiplyScalar( 1 / _ssOrigin.w );
-
-		// screen space
-		_ssOrigin.x *= resolution.x / 2;
-		_ssOrigin.y *= resolution.y / 2;
-		_ssOrigin.z = 0;
-		_ssOrigin3.copy( _ssOrigin );
-		_mvMatrix.multiplyMatrices( camera.matrixWorldInverse, matrixWorld );
-		for ( let i = 0, l = instanceStart.count; i < l; i ++ ) {
-
-			_start4.fromBufferAttribute( instanceStart, i );
-			_end4.fromBufferAttribute( instanceEnd, i );
-			_start4.w = 1;
-			_end4.w = 1;
-
-			// camera space
-			_start4.applyMatrix4( _mvMatrix );
-			_end4.applyMatrix4( _mvMatrix );
-
-			// skip the segment if it's entirely behind the camera
-			const isBehindCameraNear = _start4.z > near && _end4.z > near;
-			if ( isBehindCameraNear ) {
-
-				continue;
-
-			}
-
-			// trim the segment if it extends behind camera near
-			if ( _start4.z > near ) {
-
-				const deltaDist = _start4.z - _end4.z;
-				const t = ( _start4.z - near ) / deltaDist;
-				_start4.lerp( _end4, t );
-
-			} else if ( _end4.z > near ) {
-
-				const deltaDist = _end4.z - _start4.z;
-				const t = ( _end4.z - near ) / deltaDist;
-				_end4.lerp( _start4, t );
-
-			}
-
-			// clip space
-			_start4.applyMatrix4( projectionMatrix );
-			_end4.applyMatrix4( projectionMatrix );
-
-			// ndc space [ - 1.0, 1.0 ]
-			_start4.multiplyScalar( 1 / _start4.w );
-			_end4.multiplyScalar( 1 / _end4.w );
-
-			// screen space
-			_start4.x *= resolution.x / 2;
-			_start4.y *= resolution.y / 2;
-			_end4.x *= resolution.x / 2;
-			_end4.y *= resolution.y / 2;
-
-			// create 2d segment
-			_line.start.copy( _start4 );
-			_line.start.z = 0;
-			_line.end.copy( _end4 );
-			_line.end.z = 0;
-
-			// get closest point on ray to segment
-			const param = _line.closestPointToPointParameter( _ssOrigin3, true );
-			_line.at( param, _closestPoint );
-
-			// check if the intersection point is within clip space
-			const zPos = THREE.MathUtils.lerp( _start4.z, _end4.z, param );
-			const isInClipSpace = zPos >= - 1 && zPos <= 1;
-			const isInside = _ssOrigin3.distanceTo( _closestPoint ) < _lineWidth * 0.5;
-			if ( isInClipSpace && isInside ) {
-
-				_line.start.fromBufferAttribute( instanceStart, i );
-				_line.end.fromBufferAttribute( instanceEnd, i );
-				_line.start.applyMatrix4( matrixWorld );
-				_line.end.applyMatrix4( matrixWorld );
-				const pointOnLine = new THREE.Vector3();
-				const point = new THREE.Vector3();
-				_ray.distanceSqToSegment( _line.start, _line.end, point, pointOnLine );
-				intersects.push( {
-					point: point,
-					pointOnLine: pointOnLine,
-					distance: _ray.origin.distanceTo( point ),
-					object: lineSegments,
-					face: null,
-					faceIndex: i,
-					uv: null,
-					uv2: null
-				} );
-
-			}
-
-		}
-
-	}
-
-	class LineSegments2 extends THREE.Mesh {
-
-		constructor( geometry = new THREE.LineSegmentsGeometry(), material = new THREE.LineMaterial( {
-			color: Math.random() * 0xffffff
-		} ) ) {
-
-			super( geometry, material );
-			this.isLineSegments2 = true;
-			this.type = 'LineSegments2';
-
-		}
-
-		// for backwards-compatibility, but could be a method of THREE.LineSegmentsGeometry...
-
-		computeLineDistances() {
-
-			const geometry = this.geometry;
-			const instanceStart = geometry.attributes.instanceStart;
-			const instanceEnd = geometry.attributes.instanceEnd;
-			const lineDistances = new Float32Array( 2 * instanceStart.count );
-			for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {
-
-				_start.fromBufferAttribute( instanceStart, i );
-				_end.fromBufferAttribute( instanceEnd, i );
-				lineDistances[ j ] = j === 0 ? 0 : lineDistances[ j - 1 ];
-				lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );
-
-			}
-
-			const instanceDistanceBuffer = new THREE.InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
-
-			geometry.setAttribute( 'instanceDistanceStart', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
-			geometry.setAttribute( 'instanceDistanceEnd', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
-
-			return this;
-
-		}
-		raycast( raycaster, intersects ) {
-
-			const worldUnits = this.material.worldUnits;
-			const camera = raycaster.camera;
-			if ( camera === null && ! worldUnits ) {
-
-				console.error( 'LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.' );
-
-			}
-
-			const threshold = raycaster.params.Line2 !== undefined ? raycaster.params.Line2.threshold || 0 : 0;
-			_ray = raycaster.ray;
-			const matrixWorld = this.matrixWorld;
-			const geometry = this.geometry;
-			const material = this.material;
-			_lineWidth = material.linewidth + threshold;
-			_instanceStart = geometry.attributes.instanceStart;
-			_instanceEnd = geometry.attributes.instanceEnd;
-
-			// check if we intersect the sphere bounds
-			if ( geometry.boundingSphere === null ) {
-
-				geometry.computeBoundingSphere();
-
-			}
-
-			_sphere.copy( geometry.boundingSphere ).applyMatrix4( matrixWorld );
-
-			// increase the sphere bounds by the worst case line screen space width
-			let sphereMargin;
-			if ( worldUnits ) {
-
-				sphereMargin = _lineWidth * 0.5;
-
-			} else {
-
-				const distanceToSphere = Math.max( camera.near, _sphere.distanceToPoint( _ray.origin ) );
-				sphereMargin = getWorldSpaceHalfWidth( camera, distanceToSphere, material.resolution );
-
-			}
-
-			_sphere.radius += sphereMargin;
-			if ( _ray.intersectsSphere( _sphere ) === false ) {
-
-				return;
-
-			}
-
-			// check if we intersect the box bounds
-			if ( geometry.boundingBox === null ) {
-
-				geometry.computeBoundingBox();
-
-			}
-
-			_box.copy( geometry.boundingBox ).applyMatrix4( matrixWorld );
-
-			// increase the box bounds by the worst case line width
-			let boxMargin;
-			if ( worldUnits ) {
-
-				boxMargin = _lineWidth * 0.5;
-
-			} else {
-
-				const distanceToBox = Math.max( camera.near, _box.distanceToPoint( _ray.origin ) );
-				boxMargin = getWorldSpaceHalfWidth( camera, distanceToBox, material.resolution );
-
-			}
-
-			_box.expandByScalar( boxMargin );
-			if ( _ray.intersectsBox( _box ) === false ) {
-
-				return;
-
-			}
-
-			if ( worldUnits ) {
-
-				raycastWorldUnits( this, intersects );
-
-			} else {
-
-				raycastScreenSpace( this, camera, intersects );
-
-			}
-
-		}
-
-	}
-
-	THREE.LineSegments2 = LineSegments2;
-
-} )();

+ 0 - 198
examples/js/lines/LineSegmentsGeometry.js

@@ -1,198 +0,0 @@
-( function () {
-
-	const _box = new THREE.Box3();
-	const _vector = new THREE.Vector3();
-	class LineSegmentsGeometry extends THREE.InstancedBufferGeometry {
-
-		constructor() {
-
-			super();
-			this.isLineSegmentsGeometry = true;
-			this.type = 'LineSegmentsGeometry';
-			const positions = [ - 1, 2, 0, 1, 2, 0, - 1, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 0, - 1, - 1, 0, 1, - 1, 0 ];
-			const uvs = [ - 1, 2, 1, 2, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 2, 1, - 2 ];
-			const index = [ 0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5 ];
-			this.setIndex( index );
-			this.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
-			this.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
-
-		}
-		applyMatrix4( matrix ) {
-
-			const start = this.attributes.instanceStart;
-			const end = this.attributes.instanceEnd;
-			if ( start !== undefined ) {
-
-				start.applyMatrix4( matrix );
-				end.applyMatrix4( matrix );
-				start.needsUpdate = true;
-
-			}
-
-			if ( this.boundingBox !== null ) {
-
-				this.computeBoundingBox();
-
-			}
-
-			if ( this.boundingSphere !== null ) {
-
-				this.computeBoundingSphere();
-
-			}
-
-			return this;
-
-		}
-		setPositions( array ) {
-
-			let lineSegments;
-			if ( array instanceof Float32Array ) {
-
-				lineSegments = array;
-
-			} else if ( Array.isArray( array ) ) {
-
-				lineSegments = new Float32Array( array );
-
-			}
-
-			const instanceBuffer = new THREE.InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz
-
-			this.setAttribute( 'instanceStart', new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz
-			this.setAttribute( 'instanceEnd', new THREE.InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz
-
-			//
-
-			this.computeBoundingBox();
-			this.computeBoundingSphere();
-			return this;
-
-		}
-		setColors( array ) {
-
-			let colors;
-			if ( array instanceof Float32Array ) {
-
-				colors = array;
-
-			} else if ( Array.isArray( array ) ) {
-
-				colors = new Float32Array( array );
-
-			}
-
-			const instanceColorBuffer = new THREE.InstancedInterleavedBuffer( colors, 6, 1 ); // rgb, rgb
-
-			this.setAttribute( 'instanceColorStart', new THREE.InterleavedBufferAttribute( instanceColorBuffer, 3, 0 ) ); // rgb
-			this.setAttribute( 'instanceColorEnd', new THREE.InterleavedBufferAttribute( instanceColorBuffer, 3, 3 ) ); // rgb
-
-			return this;
-
-		}
-		fromWireframeGeometry( geometry ) {
-
-			this.setPositions( geometry.attributes.position.array );
-			return this;
-
-		}
-		fromEdgesGeometry( geometry ) {
-
-			this.setPositions( geometry.attributes.position.array );
-			return this;
-
-		}
-		fromMesh( mesh ) {
-
-			this.fromWireframeGeometry( new THREE.WireframeGeometry( mesh.geometry ) );
-
-			// set colors, maybe
-
-			return this;
-
-		}
-		fromLineSegments( lineSegments ) {
-
-			const geometry = lineSegments.geometry;
-			this.setPositions( geometry.attributes.position.array ); // assumes non-indexed
-
-			// set colors, maybe
-
-			return this;
-
-		}
-		computeBoundingBox() {
-
-			if ( this.boundingBox === null ) {
-
-				this.boundingBox = new THREE.Box3();
-
-			}
-
-			const start = this.attributes.instanceStart;
-			const end = this.attributes.instanceEnd;
-			if ( start !== undefined && end !== undefined ) {
-
-				this.boundingBox.setFromBufferAttribute( start );
-				_box.setFromBufferAttribute( end );
-				this.boundingBox.union( _box );
-
-			}
-
-		}
-		computeBoundingSphere() {
-
-			if ( this.boundingSphere === null ) {
-
-				this.boundingSphere = new THREE.Sphere();
-
-			}
-
-			if ( this.boundingBox === null ) {
-
-				this.computeBoundingBox();
-
-			}
-
-			const start = this.attributes.instanceStart;
-			const end = this.attributes.instanceEnd;
-			if ( start !== undefined && end !== undefined ) {
-
-				const center = this.boundingSphere.center;
-				this.boundingBox.getCenter( center );
-				let maxRadiusSq = 0;
-				for ( let i = 0, il = start.count; i < il; i ++ ) {
-
-					_vector.fromBufferAttribute( start, i );
-					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
-					_vector.fromBufferAttribute( end, i );
-					maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
-
-				}
-
-				this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
-				if ( isNaN( this.boundingSphere.radius ) ) {
-
-					console.error( 'THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
-
-				}
-
-			}
-
-		}
-		toJSON() {
-
-			// todo
-		}
-		applyMatrix( matrix ) {
-
-			console.warn( 'THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().' );
-			return this.applyMatrix4( matrix );
-
-		}
-
-	}
-
-	THREE.LineSegmentsGeometry = LineSegmentsGeometry;
-
-} )();

+ 0 - 47
examples/js/lines/Wireframe.js

@@ -1,47 +0,0 @@
-( function () {
-
-	const _start = new THREE.Vector3();
-	const _end = new THREE.Vector3();
-	class Wireframe extends THREE.Mesh {
-
-		constructor( geometry = new THREE.LineSegmentsGeometry(), material = new THREE.LineMaterial( {
-			color: Math.random() * 0xffffff
-		} ) ) {
-
-			super( geometry, material );
-			this.isWireframe = true;
-			this.type = 'Wireframe';
-
-		}
-
-		// for backwards-compatibility, but could be a method of THREE.LineSegmentsGeometry...
-
-		computeLineDistances() {
-
-			const geometry = this.geometry;
-			const instanceStart = geometry.attributes.instanceStart;
-			const instanceEnd = geometry.attributes.instanceEnd;
-			const lineDistances = new Float32Array( 2 * instanceStart.count );
-			for ( let i = 0, j = 0, l = instanceStart.count; i < l; i ++, j += 2 ) {
-
-				_start.fromBufferAttribute( instanceStart, i );
-				_end.fromBufferAttribute( instanceEnd, i );
-				lineDistances[ j ] = j === 0 ? 0 : lineDistances[ j - 1 ];
-				lineDistances[ j + 1 ] = lineDistances[ j ] + _start.distanceTo( _end );
-
-			}
-
-			const instanceDistanceBuffer = new THREE.InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1
-
-			geometry.setAttribute( 'instanceDistanceStart', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0
-			geometry.setAttribute( 'instanceDistanceEnd', new THREE.InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1
-
-			return this;
-
-		}
-
-	}
-
-	THREE.Wireframe = Wireframe;
-
-} )();

+ 0 - 20
examples/js/lines/WireframeGeometry2.js

@@ -1,20 +0,0 @@
-( function () {
-
-	class WireframeGeometry2 extends THREE.LineSegmentsGeometry {
-
-		constructor( geometry ) {
-
-			super();
-			this.isWireframeGeometry2 = true;
-			this.type = 'WireframeGeometry2';
-			this.fromWireframeGeometry( new THREE.WireframeGeometry( geometry ) );
-
-			// set colors, maybe
-
-		}
-
-	}
-
-	THREE.WireframeGeometry2 = WireframeGeometry2;
-
-} )();

+ 0 - 1273
examples/js/loaders/3DMLoader.js

@@ -1,1273 +0,0 @@
-( function () {
-
-	const _taskCache = new WeakMap();
-	class Rhino3dmLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.libraryPath = '';
-			this.libraryPending = null;
-			this.libraryBinary = null;
-			this.libraryConfig = {};
-			this.url = '';
-			this.workerLimit = 4;
-			this.workerPool = [];
-			this.workerNextTaskID = 1;
-			this.workerSourceURL = '';
-			this.workerConfig = {};
-			this.materials = [];
-			this.warnings = [];
-
-		}
-		setLibraryPath( path ) {
-
-			this.libraryPath = path;
-			return this;
-
-		}
-		setWorkerLimit( workerLimit ) {
-
-			this.workerLimit = workerLimit;
-			return this;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( this.requestHeader );
-			this.url = url;
-			loader.load( url, buffer => {
-
-				// Check for an existing task using this buffer. A transferred buffer cannot be transferred
-				// again from this thread.
-				if ( _taskCache.has( buffer ) ) {
-
-					const cachedTask = _taskCache.get( buffer );
-					return cachedTask.promise.then( onLoad ).catch( onError );
-
-				}
-
-				this.decodeObjects( buffer, url ).then( result => {
-
-					result.userData.warnings = this.warnings;
-					onLoad( result );
-
-				} ).catch( e => onError( e ) );
-
-			}, onProgress, onError );
-
-		}
-		debug() {
-
-			console.log( 'Task load: ', this.workerPool.map( worker => worker._taskLoad ) );
-
-		}
-		decodeObjects( buffer, url ) {
-
-			let worker;
-			let taskID;
-			const taskCost = buffer.byteLength;
-			const objectPending = this._getWorker( taskCost ).then( _worker => {
-
-				worker = _worker;
-				taskID = this.workerNextTaskID ++;
-				return new Promise( ( resolve, reject ) => {
-
-					worker._callbacks[ taskID ] = {
-						resolve,
-						reject
-					};
-					worker.postMessage( {
-						type: 'decode',
-						id: taskID,
-						buffer
-					}, [ buffer ] );
-
-					// this.debug();
-
-				} );
-
-			} ).then( message => this._createGeometry( message.data ) ).catch( e => {
-
-				throw e;
-
-			} );
-
-			// Remove task from the task list.
-			// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
-			objectPending.catch( () => true ).then( () => {
-
-				if ( worker && taskID ) {
-
-					this._releaseTask( worker, taskID );
-
-					//this.debug();
-
-				}
-
-			} );
-
-			// Cache the task result.
-			_taskCache.set( buffer, {
-				url: url,
-				promise: objectPending
-			} );
-			return objectPending;
-
-		}
-		parse( data, onLoad, onError ) {
-
-			this.decodeObjects( data, '' ).then( result => {
-
-				result.userData.warnings = this.warnings;
-				onLoad( result );
-
-			} ).catch( e => onError( e ) );
-
-		}
-		_compareMaterials( material ) {
-
-			const mat = {};
-			mat.name = material.name;
-			mat.color = {};
-			mat.color.r = material.color.r;
-			mat.color.g = material.color.g;
-			mat.color.b = material.color.b;
-			mat.type = material.type;
-			for ( let i = 0; i < this.materials.length; i ++ ) {
-
-				const m = this.materials[ i ];
-				const _mat = {};
-				_mat.name = m.name;
-				_mat.color = {};
-				_mat.color.r = m.color.r;
-				_mat.color.g = m.color.g;
-				_mat.color.b = m.color.b;
-				_mat.type = m.type;
-				if ( JSON.stringify( mat ) === JSON.stringify( _mat ) ) {
-
-					return m;
-
-				}
-
-			}
-
-			this.materials.push( material );
-			return material;
-
-		}
-		_createMaterial( material ) {
-
-			if ( material === undefined ) {
-
-				return new THREE.MeshStandardMaterial( {
-					color: new THREE.Color( 1, 1, 1 ),
-					metalness: 0.8,
-					name: 'default',
-					side: 2
-				} );
-
-			}
-
-			const _diffuseColor = material.diffuseColor;
-			const diffusecolor = new THREE.Color( _diffuseColor.r / 255.0, _diffuseColor.g / 255.0, _diffuseColor.b / 255.0 );
-			if ( _diffuseColor.r === 0 && _diffuseColor.g === 0 && _diffuseColor.b === 0 ) {
-
-				diffusecolor.r = 1;
-				diffusecolor.g = 1;
-				diffusecolor.b = 1;
-
-			}
-
-			// console.log( material );
-
-			const mat = new THREE.MeshStandardMaterial( {
-				color: diffusecolor,
-				name: material.name,
-				side: 2,
-				transparent: material.transparency > 0 ? true : false,
-				opacity: 1.0 - material.transparency
-			} );
-			const textureLoader = new THREE.TextureLoader();
-			for ( let i = 0; i < material.textures.length; i ++ ) {
-
-				const texture = material.textures[ i ];
-				if ( texture.image !== null ) {
-
-					const map = textureLoader.load( texture.image );
-					switch ( texture.type ) {
-
-						case 'Diffuse':
-							mat.map = map;
-							break;
-						case 'Bump':
-							mat.bumpMap = map;
-							break;
-						case 'Transparency':
-							mat.alphaMap = map;
-							mat.transparent = true;
-							break;
-						case 'Emap':
-							mat.envMap = map;
-							break;
-
-					}
-
-					map.wrapS = texture.wrapU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-					map.wrapT = texture.wrapV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-					map.repeat.set( texture.repeat[ 0 ], texture.repeat[ 1 ] );
-
-				}
-
-			}
-
-			return mat;
-
-		}
-		_createGeometry( data ) {
-
-			// console.log(data);
-
-			const object = new THREE.Object3D();
-			const instanceDefinitionObjects = [];
-			const instanceDefinitions = [];
-			const instanceReferences = [];
-			object.userData[ 'layers' ] = data.layers;
-			object.userData[ 'groups' ] = data.groups;
-			object.userData[ 'settings' ] = data.settings;
-			object.userData[ 'objectType' ] = 'File3dm';
-			object.userData[ 'materials' ] = null;
-			object.name = this.url;
-			let objects = data.objects;
-			const materials = data.materials;
-			for ( let i = 0; i < objects.length; i ++ ) {
-
-				const obj = objects[ i ];
-				const attributes = obj.attributes;
-				switch ( obj.objectType ) {
-
-					case 'InstanceDefinition':
-						instanceDefinitions.push( obj );
-						break;
-					case 'InstanceReference':
-						instanceReferences.push( obj );
-						break;
-					default:
-						let _object;
-						if ( attributes.materialIndex >= 0 ) {
-
-							const rMaterial = materials[ attributes.materialIndex ];
-							let material = this._createMaterial( rMaterial );
-							material = this._compareMaterials( material );
-							_object = this._createObject( obj, material );
-
-						} else {
-
-							const material = this._createMaterial();
-							_object = this._createObject( obj, material );
-
-						}
-
-						if ( _object === undefined ) {
-
-							continue;
-
-						}
-
-						const layer = data.layers[ attributes.layerIndex ];
-						_object.visible = layer ? data.layers[ attributes.layerIndex ].visible : true;
-						if ( attributes.isInstanceDefinitionObject ) {
-
-							instanceDefinitionObjects.push( _object );
-
-						} else {
-
-							object.add( _object );
-
-						}
-
-						break;
-
-				}
-
-			}
-
-			for ( let i = 0; i < instanceDefinitions.length; i ++ ) {
-
-				const iDef = instanceDefinitions[ i ];
-				objects = [];
-				for ( let j = 0; j < iDef.attributes.objectIds.length; j ++ ) {
-
-					const objId = iDef.attributes.objectIds[ j ];
-					for ( let p = 0; p < instanceDefinitionObjects.length; p ++ ) {
-
-						const idoId = instanceDefinitionObjects[ p ].userData.attributes.id;
-						if ( objId === idoId ) {
-
-							objects.push( instanceDefinitionObjects[ p ] );
-
-						}
-
-					}
-
-				}
-
-				// Currently clones geometry and does not take advantage of instancing
-
-				for ( let j = 0; j < instanceReferences.length; j ++ ) {
-
-					const iRef = instanceReferences[ j ];
-					if ( iRef.geometry.parentIdefId === iDef.attributes.id ) {
-
-						const iRefObject = new THREE.Object3D();
-						const xf = iRef.geometry.xform.array;
-						const matrix = new THREE.Matrix4();
-						matrix.set( xf[ 0 ], xf[ 1 ], xf[ 2 ], xf[ 3 ], xf[ 4 ], xf[ 5 ], xf[ 6 ], xf[ 7 ], xf[ 8 ], xf[ 9 ], xf[ 10 ], xf[ 11 ], xf[ 12 ], xf[ 13 ], xf[ 14 ], xf[ 15 ] );
-						iRefObject.applyMatrix4( matrix );
-						for ( let p = 0; p < objects.length; p ++ ) {
-
-							iRefObject.add( objects[ p ].clone( true ) );
-
-						}
-
-						object.add( iRefObject );
-
-					}
-
-				}
-
-			}
-
-			object.userData[ 'materials' ] = this.materials;
-			return object;
-
-		}
-		_createObject( obj, mat ) {
-
-			const loader = new THREE.BufferGeometryLoader();
-			const attributes = obj.attributes;
-			let geometry, material, _color, color;
-			switch ( obj.objectType ) {
-
-				case 'Point':
-				case 'PointSet':
-					geometry = loader.parse( obj.geometry );
-					if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
-
-						material = new THREE.PointsMaterial( {
-							vertexColors: true,
-							sizeAttenuation: false,
-							size: 2
-						} );
-
-					} else {
-
-						_color = attributes.drawColor;
-						color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
-						material = new THREE.PointsMaterial( {
-							color: color,
-							sizeAttenuation: false,
-							size: 2
-						} );
-
-					}
-
-					material = this._compareMaterials( material );
-					const points = new THREE.Points( geometry, material );
-					points.userData[ 'attributes' ] = attributes;
-					points.userData[ 'objectType' ] = obj.objectType;
-					if ( attributes.name ) {
-
-						points.name = attributes.name;
-
-					}
-
-					return points;
-				case 'Mesh':
-				case 'Extrusion':
-				case 'SubD':
-				case 'Brep':
-					if ( obj.geometry === null ) return;
-					geometry = loader.parse( obj.geometry );
-					if ( geometry.attributes.hasOwnProperty( 'color' ) ) {
-
-						mat.vertexColors = true;
-
-					}
-
-					if ( mat === null ) {
-
-						mat = this._createMaterial();
-						mat = this._compareMaterials( mat );
-
-					}
-
-					const mesh = new THREE.Mesh( geometry, mat );
-					mesh.castShadow = attributes.castsShadows;
-					mesh.receiveShadow = attributes.receivesShadows;
-					mesh.userData[ 'attributes' ] = attributes;
-					mesh.userData[ 'objectType' ] = obj.objectType;
-					if ( attributes.name ) {
-
-						mesh.name = attributes.name;
-
-					}
-
-					return mesh;
-				case 'Curve':
-					geometry = loader.parse( obj.geometry );
-					_color = attributes.drawColor;
-					color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
-					material = new THREE.LineBasicMaterial( {
-						color: color
-					} );
-					material = this._compareMaterials( material );
-					const lines = new THREE.Line( geometry, material );
-					lines.userData[ 'attributes' ] = attributes;
-					lines.userData[ 'objectType' ] = obj.objectType;
-					if ( attributes.name ) {
-
-						lines.name = attributes.name;
-
-					}
-
-					return lines;
-				case 'TextDot':
-					geometry = obj.geometry;
-					const ctx = document.createElement( 'canvas' ).getContext( '2d' );
-					const font = `${geometry.fontHeight}px ${geometry.fontFace}`;
-					ctx.font = font;
-					const width = ctx.measureText( geometry.text ).width + 10;
-					const height = geometry.fontHeight + 10;
-					const r = window.devicePixelRatio;
-					ctx.canvas.width = width * r;
-					ctx.canvas.height = height * r;
-					ctx.canvas.style.width = width + 'px';
-					ctx.canvas.style.height = height + 'px';
-					ctx.setTransform( r, 0, 0, r, 0, 0 );
-					ctx.font = font;
-					ctx.textBaseline = 'middle';
-					ctx.textAlign = 'center';
-					color = attributes.drawColor;
-					ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},${color.a})`;
-					ctx.fillRect( 0, 0, width, height );
-					ctx.fillStyle = 'white';
-					ctx.fillText( geometry.text, width / 2, height / 2 );
-					const texture = new THREE.CanvasTexture( ctx.canvas );
-					texture.minFilter = THREE.LinearFilter;
-					texture.wrapS = THREE.ClampToEdgeWrapping;
-					texture.wrapT = THREE.ClampToEdgeWrapping;
-					material = new THREE.SpriteMaterial( {
-						map: texture,
-						depthTest: false
-					} );
-					const sprite = new THREE.Sprite( material );
-					sprite.position.set( geometry.point[ 0 ], geometry.point[ 1 ], geometry.point[ 2 ] );
-					sprite.scale.set( width / 10, height / 10, 1.0 );
-					sprite.userData[ 'attributes' ] = attributes;
-					sprite.userData[ 'objectType' ] = obj.objectType;
-					if ( attributes.name ) {
-
-						sprite.name = attributes.name;
-
-					}
-
-					return sprite;
-				case 'Light':
-					geometry = obj.geometry;
-					let light;
-					switch ( geometry.lightStyle.name ) {
-
-						case 'LightStyle_WorldPoint':
-							light = new THREE.PointLight();
-							light.castShadow = attributes.castsShadows;
-							light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
-							light.shadow.normalBias = 0.1;
-							break;
-						case 'LightStyle_WorldSpot':
-							light = new THREE.SpotLight();
-							light.castShadow = attributes.castsShadows;
-							light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
-							light.target.position.set( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] );
-							light.angle = geometry.spotAngleRadians;
-							light.shadow.normalBias = 0.1;
-							break;
-						case 'LightStyle_WorldRectangular':
-							light = new THREE.RectAreaLight();
-							const width = Math.abs( geometry.width[ 2 ] );
-							const height = Math.abs( geometry.length[ 0 ] );
-							light.position.set( geometry.location[ 0 ] - height / 2, geometry.location[ 1 ], geometry.location[ 2 ] - width / 2 );
-							light.height = height;
-							light.width = width;
-							light.lookAt( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] );
-							break;
-						case 'LightStyle_WorldDirectional':
-							light = new THREE.DirectionalLight();
-							light.castShadow = attributes.castsShadows;
-							light.position.set( geometry.location[ 0 ], geometry.location[ 1 ], geometry.location[ 2 ] );
-							light.target.position.set( geometry.direction[ 0 ], geometry.direction[ 1 ], geometry.direction[ 2 ] );
-							light.shadow.normalBias = 0.1;
-							break;
-						case 'LightStyle_WorldLinear':
-							// not conversion exists, warning has already been printed to the console
-							break;
-						default:
-							break;
-
-					}
-
-					if ( light ) {
-
-						light.intensity = geometry.intensity;
-						_color = geometry.diffuse;
-						color = new THREE.Color( _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 );
-						light.color = color;
-						light.userData[ 'attributes' ] = attributes;
-						light.userData[ 'objectType' ] = obj.objectType;
-
-					}
-
-					return light;
-
-			}
-
-		}
-		_initLibrary() {
-
-			if ( ! this.libraryPending ) {
-
-				// Load rhino3dm wrapper.
-				const jsLoader = new THREE.FileLoader( this.manager );
-				jsLoader.setPath( this.libraryPath );
-				const jsContent = new Promise( ( resolve, reject ) => {
-
-					jsLoader.load( 'rhino3dm.js', resolve, undefined, reject );
-
-				} );
-
-				// Load rhino3dm WASM binary.
-				const binaryLoader = new THREE.FileLoader( this.manager );
-				binaryLoader.setPath( this.libraryPath );
-				binaryLoader.setResponseType( 'arraybuffer' );
-				const binaryContent = new Promise( ( resolve, reject ) => {
-
-					binaryLoader.load( 'rhino3dm.wasm', resolve, undefined, reject );
-
-				} );
-				this.libraryPending = Promise.all( [ jsContent, binaryContent ] ).then( ( [ jsContent, binaryContent ] ) => {
-
-					//this.libraryBinary = binaryContent;
-					this.libraryConfig.wasmBinary = binaryContent;
-					const fn = Rhino3dmWorker.toString();
-					const body = [ '/* rhino3dm.js */', jsContent, '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );
-					this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
-
-				} );
-
-			}
-
-			return this.libraryPending;
-
-		}
-		_getWorker( taskCost ) {
-
-			return this._initLibrary().then( () => {
-
-				if ( this.workerPool.length < this.workerLimit ) {
-
-					const worker = new Worker( this.workerSourceURL );
-					worker._callbacks = {};
-					worker._taskCosts = {};
-					worker._taskLoad = 0;
-					worker.postMessage( {
-						type: 'init',
-						libraryConfig: this.libraryConfig
-					} );
-					worker.onmessage = e => {
-
-						const message = e.data;
-						switch ( message.type ) {
-
-							case 'warning':
-								this.warnings.push( message.data );
-								console.warn( message.data );
-								break;
-							case 'decode':
-								worker._callbacks[ message.id ].resolve( message );
-								break;
-							case 'error':
-								worker._callbacks[ message.id ].reject( message );
-								break;
-							default:
-								console.error( 'THREE.Rhino3dmLoader: Unexpected message, "' + message.type + '"' );
-
-						}
-
-					};
-
-					this.workerPool.push( worker );
-
-				} else {
-
-					this.workerPool.sort( function ( a, b ) {
-
-						return a._taskLoad > b._taskLoad ? - 1 : 1;
-
-					} );
-
-				}
-
-				const worker = this.workerPool[ this.workerPool.length - 1 ];
-				worker._taskLoad += taskCost;
-				return worker;
-
-			} );
-
-		}
-		_releaseTask( worker, taskID ) {
-
-			worker._taskLoad -= worker._taskCosts[ taskID ];
-			delete worker._callbacks[ taskID ];
-			delete worker._taskCosts[ taskID ];
-
-		}
-		dispose() {
-
-			for ( let i = 0; i < this.workerPool.length; ++ i ) {
-
-				this.workerPool[ i ].terminate();
-
-			}
-
-			this.workerPool.length = 0;
-			return this;
-
-		}
-
-	}
-
-	/* WEB WORKER */
-
-	function Rhino3dmWorker() {
-
-		let libraryPending;
-		let libraryConfig;
-		let rhino;
-		let taskID;
-		onmessage = function ( e ) {
-
-			const message = e.data;
-			switch ( message.type ) {
-
-				case 'init':
-					// console.log(message)
-					libraryConfig = message.libraryConfig;
-					const wasmBinary = libraryConfig.wasmBinary;
-					let RhinoModule;
-					libraryPending = new Promise( function ( resolve ) {
-
-						/* Like Basis THREE.Loader */
-						RhinoModule = {
-							wasmBinary,
-							onRuntimeInitialized: resolve
-						};
-						rhino3dm( RhinoModule ); // eslint-disable-line no-undef
-
-					} ).then( () => {
-
-						rhino = RhinoModule;
-
-					} );
-					break;
-				case 'decode':
-					taskID = message.id;
-					const buffer = message.buffer;
-					libraryPending.then( () => {
-
-						try {
-
-							const data = decodeObjects( rhino, buffer );
-							self.postMessage( {
-								type: 'decode',
-								id: message.id,
-								data
-							} );
-
-						} catch ( error ) {
-
-							self.postMessage( {
-								type: 'error',
-								id: message.id,
-								error
-							} );
-
-						}
-
-					} );
-					break;
-
-			}
-
-		};
-
-		function decodeObjects( rhino, buffer ) {
-
-			const arr = new Uint8Array( buffer );
-			const doc = rhino.File3dm.fromByteArray( arr );
-			const objects = [];
-			const materials = [];
-			const layers = [];
-			const views = [];
-			const namedViews = [];
-			const groups = [];
-			const strings = [];
-
-			//Handle objects
-
-			const objs = doc.objects();
-			const cnt = objs.count;
-			for ( let i = 0; i < cnt; i ++ ) {
-
-				const _object = objs.get( i );
-				const object = extractObjectData( _object, doc );
-				_object.delete();
-				if ( object ) {
-
-					objects.push( object );
-
-				}
-
-			}
-
-			// Handle instance definitions
-			// console.log( `Instance Definitions Count: ${doc.instanceDefinitions().count()}` );
-
-			for ( let i = 0; i < doc.instanceDefinitions().count(); i ++ ) {
-
-				const idef = doc.instanceDefinitions().get( i );
-				const idefAttributes = extractProperties( idef );
-				idefAttributes.objectIds = idef.getObjectIds();
-				objects.push( {
-					geometry: null,
-					attributes: idefAttributes,
-					objectType: 'InstanceDefinition'
-				} );
-
-			}
-
-			// Handle materials
-
-			const textureTypes = [
-				// rhino.TextureType.Bitmap,
-				rhino.TextureType.Diffuse, rhino.TextureType.Bump, rhino.TextureType.Transparency, rhino.TextureType.Opacity, rhino.TextureType.Emap ];
-			const pbrTextureTypes = [ rhino.TextureType.PBR_BaseColor, rhino.TextureType.PBR_Subsurface, rhino.TextureType.PBR_SubsurfaceScattering, rhino.TextureType.PBR_SubsurfaceScatteringRadius, rhino.TextureType.PBR_Metallic, rhino.TextureType.PBR_Specular, rhino.TextureType.PBR_SpecularTint, rhino.TextureType.PBR_Roughness, rhino.TextureType.PBR_Anisotropic, rhino.TextureType.PBR_Anisotropic_Rotation, rhino.TextureType.PBR_Sheen, rhino.TextureType.PBR_SheenTint, rhino.TextureType.PBR_Clearcoat, rhino.TextureType.PBR_ClearcoatBump, rhino.TextureType.PBR_ClearcoatRoughness, rhino.TextureType.PBR_OpacityIor, rhino.TextureType.PBR_OpacityRoughness, rhino.TextureType.PBR_Emission, rhino.TextureType.PBR_AmbientOcclusion, rhino.TextureType.PBR_Displacement ];
-			for ( let i = 0; i < doc.materials().count(); i ++ ) {
-
-				const _material = doc.materials().get( i );
-				const _pbrMaterial = _material.physicallyBased();
-				let material = extractProperties( _material );
-				const textures = [];
-				for ( let j = 0; j < textureTypes.length; j ++ ) {
-
-					const _texture = _material.getTexture( textureTypes[ j ] );
-					if ( _texture ) {
-
-						let textureType = textureTypes[ j ].constructor.name;
-						textureType = textureType.substring( 12, textureType.length );
-						const texture = {
-							type: textureType
-						};
-						const image = doc.getEmbeddedFileAsBase64( _texture.fileName );
-						texture.wrapU = _texture.wrapU;
-						texture.wrapV = _texture.wrapV;
-						texture.wrapW = _texture.wrapW;
-						const uvw = _texture.uvwTransform.toFloatArray( true );
-						texture.repeat = [ uvw[ 0 ], uvw[ 5 ] ];
-						if ( image ) {
-
-							texture.image = 'data:image/png;base64,' + image;
-
-						} else {
-
-							self.postMessage( {
-								type: 'warning',
-								id: taskID,
-								data: {
-									message: `THREE.3DMLoader: Image for ${textureType} texture not embedded in file.`,
-									type: 'missing resource'
-								}
-							} );
-							texture.image = null;
-
-						}
-
-						textures.push( texture );
-						_texture.delete();
-
-					}
-
-				}
-
-				material.textures = textures;
-				if ( _pbrMaterial.supported ) {
-
-					for ( let j = 0; j < pbrTextureTypes.length; j ++ ) {
-
-						const _texture = _material.getTexture( pbrTextureTypes[ j ] );
-						if ( _texture ) {
-
-							const image = doc.getEmbeddedFileAsBase64( _texture.fileName );
-							let textureType = pbrTextureTypes[ j ].constructor.name;
-							textureType = textureType.substring( 12, textureType.length );
-							const texture = {
-								type: textureType,
-								image: 'data:image/png;base64,' + image
-							};
-							textures.push( texture );
-							_texture.delete();
-
-						}
-
-					}
-
-					const pbMaterialProperties = extractProperties( _material.physicallyBased() );
-					material = Object.assign( pbMaterialProperties, material );
-
-				}
-
-				materials.push( material );
-				_material.delete();
-				_pbrMaterial.delete();
-
-			}
-
-			// Handle layers
-
-			for ( let i = 0; i < doc.layers().count(); i ++ ) {
-
-				const _layer = doc.layers().get( i );
-				const layer = extractProperties( _layer );
-				layers.push( layer );
-				_layer.delete();
-
-			}
-
-			// Handle views
-
-			for ( let i = 0; i < doc.views().count(); i ++ ) {
-
-				const _view = doc.views().get( i );
-				const view = extractProperties( _view );
-				views.push( view );
-				_view.delete();
-
-			}
-
-			// Handle named views
-
-			for ( let i = 0; i < doc.namedViews().count(); i ++ ) {
-
-				const _namedView = doc.namedViews().get( i );
-				const namedView = extractProperties( _namedView );
-				namedViews.push( namedView );
-				_namedView.delete();
-
-			}
-
-			// Handle groups
-
-			for ( let i = 0; i < doc.groups().count(); i ++ ) {
-
-				const _group = doc.groups().get( i );
-				const group = extractProperties( _group );
-				groups.push( group );
-				_group.delete();
-
-			}
-
-			// Handle settings
-
-			const settings = extractProperties( doc.settings() );
-
-			//TODO: Handle other document stuff like dimstyles, instance definitions, bitmaps etc.
-
-			// Handle dimstyles
-			// console.log( `Dimstyle Count: ${doc.dimstyles().count()}` );
-
-			// Handle bitmaps
-			// console.log( `Bitmap Count: ${doc.bitmaps().count()}` );
-
-			// Handle strings
-			// console.log( `Document Strings Count: ${doc.strings().count()}` );
-			// Note: doc.strings().documentUserTextCount() counts any doc.strings defined in a section
-			//console.log( `Document User Text Count: ${doc.strings().documentUserTextCount()}` );
-
-			const strings_count = doc.strings().count();
-			for ( let i = 0; i < strings_count; i ++ ) {
-
-				strings.push( doc.strings().get( i ) );
-
-			}
-
-			doc.delete();
-			return {
-				objects,
-				materials,
-				layers,
-				views,
-				namedViews,
-				groups,
-				strings,
-				settings
-			};
-
-		}
-
-		function extractObjectData( object, doc ) {
-
-			const _geometry = object.geometry();
-			const _attributes = object.attributes();
-			let objectType = _geometry.objectType;
-			let geometry, attributes, position, data, mesh;
-
-			// skip instance definition objects
-			//if( _attributes.isInstanceDefinitionObject ) { continue; }
-
-			// TODO: handle other geometry types
-			switch ( objectType ) {
-
-				case rhino.ObjectType.Curve:
-					const pts = curveToPoints( _geometry, 100 );
-					position = {};
-					attributes = {};
-					data = {};
-					position.itemSize = 3;
-					position.type = 'Float32Array';
-					position.array = [];
-					for ( let j = 0; j < pts.length; j ++ ) {
-
-						position.array.push( pts[ j ][ 0 ] );
-						position.array.push( pts[ j ][ 1 ] );
-						position.array.push( pts[ j ][ 2 ] );
-
-					}
-
-					attributes.position = position;
-					data.attributes = attributes;
-					geometry = {
-						data
-					};
-					break;
-				case rhino.ObjectType.Point:
-					const pt = _geometry.location;
-					position = {};
-					const color = {};
-					attributes = {};
-					data = {};
-					position.itemSize = 3;
-					position.type = 'Float32Array';
-					position.array = [ pt[ 0 ], pt[ 1 ], pt[ 2 ] ];
-					const _color = _attributes.drawColor( doc );
-					color.itemSize = 3;
-					color.type = 'Float32Array';
-					color.array = [ _color.r / 255.0, _color.g / 255.0, _color.b / 255.0 ];
-					attributes.position = position;
-					attributes.color = color;
-					data.attributes = attributes;
-					geometry = {
-						data
-					};
-					break;
-				case rhino.ObjectType.PointSet:
-				case rhino.ObjectType.Mesh:
-					geometry = _geometry.toThreejsJSON();
-					break;
-				case rhino.ObjectType.Brep:
-					const faces = _geometry.faces();
-					mesh = new rhino.Mesh();
-					for ( let faceIndex = 0; faceIndex < faces.count; faceIndex ++ ) {
-
-						const face = faces.get( faceIndex );
-						const _mesh = face.getMesh( rhino.MeshType.Any );
-						if ( _mesh ) {
-
-							mesh.append( _mesh );
-							_mesh.delete();
-
-						}
-
-						face.delete();
-
-					}
-
-					if ( mesh.faces().count > 0 ) {
-
-						mesh.compact();
-						geometry = mesh.toThreejsJSON();
-						faces.delete();
-
-					}
-
-					mesh.delete();
-					break;
-				case rhino.ObjectType.Extrusion:
-					mesh = _geometry.getMesh( rhino.MeshType.Any );
-					if ( mesh ) {
-
-						geometry = mesh.toThreejsJSON();
-						mesh.delete();
-
-					}
-
-					break;
-				case rhino.ObjectType.TextDot:
-					geometry = extractProperties( _geometry );
-					break;
-				case rhino.ObjectType.Light:
-					geometry = extractProperties( _geometry );
-					if ( geometry.lightStyle.name === 'LightStyle_WorldLinear' ) {
-
-						self.postMessage( {
-							type: 'warning',
-							id: taskID,
-							data: {
-								message: `THREE.3DMLoader: No conversion exists for ${objectType.constructor.name} ${geometry.lightStyle.name}`,
-								type: 'no conversion',
-								guid: _attributes.id
-							}
-						} );
-
-					}
-
-					break;
-				case rhino.ObjectType.InstanceReference:
-					geometry = extractProperties( _geometry );
-					geometry.xform = extractProperties( _geometry.xform );
-					geometry.xform.array = _geometry.xform.toFloatArray( true );
-					break;
-				case rhino.ObjectType.SubD:
-					// TODO: precalculate resulting vertices and faces and warn on excessive results
-					_geometry.subdivide( 3 );
-					mesh = rhino.Mesh.createFromSubDControlNet( _geometry );
-					if ( mesh ) {
-
-						geometry = mesh.toThreejsJSON();
-						mesh.delete();
-
-					}
-
-					break;
-
-					/*
-      case rhino.ObjectType.Annotation:
-      case rhino.ObjectType.Hatch:
-      case rhino.ObjectType.ClipPlane:
-      */
-
-				default:
-					self.postMessage( {
-						type: 'warning',
-						id: taskID,
-						data: {
-							message: `THREE.3DMLoader: Conversion not implemented for ${objectType.constructor.name}`,
-							type: 'not implemented',
-							guid: _attributes.id
-						}
-					} );
-					break;
-
-			}
-
-			if ( geometry ) {
-
-				attributes = extractProperties( _attributes );
-				attributes.geometry = extractProperties( _geometry );
-				if ( _attributes.groupCount > 0 ) {
-
-					attributes.groupIds = _attributes.getGroupList();
-
-				}
-
-				if ( _attributes.userStringCount > 0 ) {
-
-					attributes.userStrings = _attributes.getUserStrings();
-
-				}
-
-				if ( _geometry.userStringCount > 0 ) {
-
-					attributes.geometry.userStrings = _geometry.getUserStrings();
-
-				}
-
-				attributes.drawColor = _attributes.drawColor( doc );
-				objectType = objectType.constructor.name;
-				objectType = objectType.substring( 11, objectType.length );
-				return {
-					geometry,
-					attributes,
-					objectType
-				};
-
-			} else {
-
-				self.postMessage( {
-					type: 'warning',
-					id: taskID,
-					data: {
-						message: `THREE.3DMLoader: ${objectType.constructor.name} has no associated mesh geometry.`,
-						type: 'missing mesh',
-						guid: _attributes.id
-					}
-				} );
-
-			}
-
-		}
-
-		function extractProperties( object ) {
-
-			const result = {};
-			for ( const property in object ) {
-
-				const value = object[ property ];
-				if ( typeof value !== 'function' ) {
-
-					if ( typeof value === 'object' && value !== null && value.hasOwnProperty( 'constructor' ) ) {
-
-						result[ property ] = {
-							name: value.constructor.name,
-							value: value.value
-						};
-
-					} else {
-
-						result[ property ] = value;
-
-					}
-
-				} else {
-
-					// these are functions that could be called to extract more data.
-					//console.log( `${property}: ${object[ property ].constructor.name}` );
-				}
-
-			}
-
-			return result;
-
-		}
-
-		function curveToPoints( curve, pointLimit ) {
-
-			let pointCount = pointLimit;
-			let rc = [];
-			const ts = [];
-			if ( curve instanceof rhino.LineCurve ) {
-
-				return [ curve.pointAtStart, curve.pointAtEnd ];
-
-			}
-
-			if ( curve instanceof rhino.PolylineCurve ) {
-
-				pointCount = curve.pointCount;
-				for ( let i = 0; i < pointCount; i ++ ) {
-
-					rc.push( curve.point( i ) );
-
-				}
-
-				return rc;
-
-			}
-
-			if ( curve instanceof rhino.PolyCurve ) {
-
-				const segmentCount = curve.segmentCount;
-				for ( let i = 0; i < segmentCount; i ++ ) {
-
-					const segment = curve.segmentCurve( i );
-					const segmentArray = curveToPoints( segment, pointCount );
-					rc = rc.concat( segmentArray );
-					segment.delete();
-
-				}
-
-				return rc;
-
-			}
-
-			if ( curve instanceof rhino.ArcCurve ) {
-
-				pointCount = Math.floor( curve.angleDegrees / 5 );
-				pointCount = pointCount < 2 ? 2 : pointCount;
-				// alternative to this hardcoded version: https://stackoverflow.com/a/18499923/2179399
-
-			}
-
-			if ( curve instanceof rhino.NurbsCurve && curve.degree === 1 ) {
-
-				const pLine = curve.tryGetPolyline();
-				for ( let i = 0; i < pLine.count; i ++ ) {
-
-					rc.push( pLine.get( i ) );
-
-				}
-
-				pLine.delete();
-				return rc;
-
-			}
-
-			const domain = curve.domain;
-			const divisions = pointCount - 1.0;
-			for ( let j = 0; j < pointCount; j ++ ) {
-
-				const t = domain[ 0 ] + j / divisions * ( domain[ 1 ] - domain[ 0 ] );
-				if ( t === domain[ 0 ] || t === domain[ 1 ] ) {
-
-					ts.push( t );
-					continue;
-
-				}
-
-				const tan = curve.tangentAt( t );
-				const prevTan = curve.tangentAt( ts.slice( - 1 )[ 0 ] );
-
-				// Duplicated from THREE.Vector3
-				// How to pass imports to worker?
-
-				const tS = tan[ 0 ] * tan[ 0 ] + tan[ 1 ] * tan[ 1 ] + tan[ 2 ] * tan[ 2 ];
-				const ptS = prevTan[ 0 ] * prevTan[ 0 ] + prevTan[ 1 ] * prevTan[ 1 ] + prevTan[ 2 ] * prevTan[ 2 ];
-				const denominator = Math.sqrt( tS * ptS );
-				let angle;
-				if ( denominator === 0 ) {
-
-					angle = Math.PI / 2;
-
-				} else {
-
-					const theta = ( tan.x * prevTan.x + tan.y * prevTan.y + tan.z * prevTan.z ) / denominator;
-					angle = Math.acos( Math.max( - 1, Math.min( 1, theta ) ) );
-
-				}
-
-				if ( angle < 0.1 ) continue;
-				ts.push( t );
-
-			}
-
-			rc = ts.map( t => curve.pointAt( t ) );
-			return rc;
-
-		}
-
-	}
-
-	THREE.Rhino3dmLoader = Rhino3dmLoader;
-
-} )();

+ 0 - 1306
examples/js/loaders/3MFLoader.js

@@ -1,1306 +0,0 @@
-( function () {
-
-	/**
- *
- * 3D Manufacturing Format (3MF) specification: https://3mf.io/specification/
- *
- * The following features from the core specification are supported:
- *
- * - 3D Models
- * - Object Resources (Meshes and Components)
- * - Material Resources (Base Materials)
- *
- * 3MF Materials and Properties Extension are only partially supported.
- *
- * - Texture 2D
- * - Texture 2D Groups
- * - THREE.Color Groups (Vertex Colors)
- * - Metallic Display Properties (PBR)
- */
-
-	class ThreeMFLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.availableExtensions = [];
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( buffer ) {
-
-				try {
-
-					onLoad( scope.parse( buffer ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( data ) {
-
-			const scope = this;
-			const textureLoader = new THREE.TextureLoader( this.manager );
-			function loadDocument( data ) {
-
-				let zip = null;
-				let file = null;
-				let relsName;
-				let modelRelsName;
-				const modelPartNames = [];
-				const texturesPartNames = [];
-				let modelRels;
-				const modelParts = {};
-				const printTicketParts = {};
-				const texturesParts = {};
-				try {
-
-					zip = fflate.unzipSync( new Uint8Array( data ) ); // eslint-disable-line no-undef
-
-				} catch ( e ) {
-
-					if ( e instanceof ReferenceError ) {
-
-						console.error( 'THREE.3MFLoader: fflate missing and file is compressed.' );
-						return null;
-
-					}
-
-				}
-
-				for ( file in zip ) {
-
-					if ( file.match( /\_rels\/.rels$/ ) ) {
-
-						relsName = file;
-
-					} else if ( file.match( /3D\/_rels\/.*\.model\.rels$/ ) ) {
-
-						modelRelsName = file;
-
-					} else if ( file.match( /^3D\/.*\.model$/ ) ) {
-
-						modelPartNames.push( file );
-
-					} else if ( file.match( /^3D\/Textures?\/.*/ ) ) {
-
-						texturesPartNames.push( file );
-
-					}
-
-				}
-
-				//
-
-				const relsView = zip[ relsName ];
-				const relsFileText = THREE.LoaderUtils.decodeText( relsView );
-				const rels = parseRelsXml( relsFileText );
-
-				//
-
-				if ( modelRelsName ) {
-
-					const relsView = zip[ modelRelsName ];
-					const relsFileText = THREE.LoaderUtils.decodeText( relsView );
-					modelRels = parseRelsXml( relsFileText );
-
-				}
-
-				//
-
-				for ( let i = 0; i < modelPartNames.length; i ++ ) {
-
-					const modelPart = modelPartNames[ i ];
-					const view = zip[ modelPart ];
-					const fileText = THREE.LoaderUtils.decodeText( view );
-					const xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
-					if ( xmlData.documentElement.nodeName.toLowerCase() !== 'model' ) {
-
-						console.error( 'THREE.3MFLoader: Error loading 3MF - no 3MF document found: ', modelPart );
-
-					}
-
-					const modelNode = xmlData.querySelector( 'model' );
-					const extensions = {};
-					for ( let i = 0; i < modelNode.attributes.length; i ++ ) {
-
-						const attr = modelNode.attributes[ i ];
-						if ( attr.name.match( /^xmlns:(.+)$/ ) ) {
-
-							extensions[ attr.value ] = RegExp.$1;
-
-						}
-
-					}
-
-					const modelData = parseModelNode( modelNode );
-					modelData[ 'xml' ] = modelNode;
-					if ( 0 < Object.keys( extensions ).length ) {
-
-						modelData[ 'extensions' ] = extensions;
-
-					}
-
-					modelParts[ modelPart ] = modelData;
-
-				}
-
-				//
-
-				for ( let i = 0; i < texturesPartNames.length; i ++ ) {
-
-					const texturesPartName = texturesPartNames[ i ];
-					texturesParts[ texturesPartName ] = zip[ texturesPartName ].buffer;
-
-				}
-
-				return {
-					rels: rels,
-					modelRels: modelRels,
-					model: modelParts,
-					printTicket: printTicketParts,
-					texture: texturesParts
-				};
-
-			}
-
-			function parseRelsXml( relsFileText ) {
-
-				const relationships = [];
-				const relsXmlData = new DOMParser().parseFromString( relsFileText, 'application/xml' );
-				const relsNodes = relsXmlData.querySelectorAll( 'Relationship' );
-				for ( let i = 0; i < relsNodes.length; i ++ ) {
-
-					const relsNode = relsNodes[ i ];
-					const relationship = {
-						target: relsNode.getAttribute( 'Target' ),
-						//required
-						id: relsNode.getAttribute( 'Id' ),
-						//required
-						type: relsNode.getAttribute( 'Type' ) //required
-					};
-
-					relationships.push( relationship );
-
-				}
-
-				return relationships;
-
-			}
-
-			function parseMetadataNodes( metadataNodes ) {
-
-				const metadataData = {};
-				for ( let i = 0; i < metadataNodes.length; i ++ ) {
-
-					const metadataNode = metadataNodes[ i ];
-					const name = metadataNode.getAttribute( 'name' );
-					const validNames = [ 'Title', 'Designer', 'Description', 'Copyright', 'LicenseTerms', 'Rating', 'CreationDate', 'ModificationDate' ];
-					if ( 0 <= validNames.indexOf( name ) ) {
-
-						metadataData[ name ] = metadataNode.textContent;
-
-					}
-
-				}
-
-				return metadataData;
-
-			}
-
-			function parseBasematerialsNode( basematerialsNode ) {
-
-				const basematerialsData = {
-					id: basematerialsNode.getAttribute( 'id' ),
-					// required
-					basematerials: []
-				};
-				const basematerialNodes = basematerialsNode.querySelectorAll( 'base' );
-				for ( let i = 0; i < basematerialNodes.length; i ++ ) {
-
-					const basematerialNode = basematerialNodes[ i ];
-					const basematerialData = parseBasematerialNode( basematerialNode );
-					basematerialData.index = i; // the order and count of the material nodes form an implicit 0-based index
-					basematerialsData.basematerials.push( basematerialData );
-
-				}
-
-				return basematerialsData;
-
-			}
-
-			function parseTexture2DNode( texture2DNode ) {
-
-				const texture2dData = {
-					id: texture2DNode.getAttribute( 'id' ),
-					// required
-					path: texture2DNode.getAttribute( 'path' ),
-					// required
-					contenttype: texture2DNode.getAttribute( 'contenttype' ),
-					// required
-					tilestyleu: texture2DNode.getAttribute( 'tilestyleu' ),
-					tilestylev: texture2DNode.getAttribute( 'tilestylev' ),
-					filter: texture2DNode.getAttribute( 'filter' )
-				};
-				return texture2dData;
-
-			}
-
-			function parseTextures2DGroupNode( texture2DGroupNode ) {
-
-				const texture2DGroupData = {
-					id: texture2DGroupNode.getAttribute( 'id' ),
-					// required
-					texid: texture2DGroupNode.getAttribute( 'texid' ),
-					// required
-					displaypropertiesid: texture2DGroupNode.getAttribute( 'displaypropertiesid' )
-				};
-				const tex2coordNodes = texture2DGroupNode.querySelectorAll( 'tex2coord' );
-				const uvs = [];
-				for ( let i = 0; i < tex2coordNodes.length; i ++ ) {
-
-					const tex2coordNode = tex2coordNodes[ i ];
-					const u = tex2coordNode.getAttribute( 'u' );
-					const v = tex2coordNode.getAttribute( 'v' );
-					uvs.push( parseFloat( u ), parseFloat( v ) );
-
-				}
-
-				texture2DGroupData[ 'uvs' ] = new Float32Array( uvs );
-				return texture2DGroupData;
-
-			}
-
-			function parseColorGroupNode( colorGroupNode ) {
-
-				const colorGroupData = {
-					id: colorGroupNode.getAttribute( 'id' ),
-					// required
-					displaypropertiesid: colorGroupNode.getAttribute( 'displaypropertiesid' )
-				};
-				const colorNodes = colorGroupNode.querySelectorAll( 'color' );
-				const colors = [];
-				const colorObject = new THREE.Color();
-				for ( let i = 0; i < colorNodes.length; i ++ ) {
-
-					const colorNode = colorNodes[ i ];
-					const color = colorNode.getAttribute( 'color' );
-					colorObject.setStyle( color.substring( 0, 7 ) );
-					colorObject.convertSRGBToLinear(); // color is in sRGB
-
-					colors.push( colorObject.r, colorObject.g, colorObject.b );
-
-				}
-
-				colorGroupData[ 'colors' ] = new Float32Array( colors );
-				return colorGroupData;
-
-			}
-
-			function parseMetallicDisplaypropertiesNode( metallicDisplaypropetiesNode ) {
-
-				const metallicDisplaypropertiesData = {
-					id: metallicDisplaypropetiesNode.getAttribute( 'id' ) // required
-				};
-
-				const metallicNodes = metallicDisplaypropetiesNode.querySelectorAll( 'pbmetallic' );
-				const metallicData = [];
-				for ( let i = 0; i < metallicNodes.length; i ++ ) {
-
-					const metallicNode = metallicNodes[ i ];
-					metallicData.push( {
-						name: metallicNode.getAttribute( 'name' ),
-						// required
-						metallicness: parseFloat( metallicNode.getAttribute( 'metallicness' ) ),
-						// required
-						roughness: parseFloat( metallicNode.getAttribute( 'roughness' ) ) // required
-					} );
-
-				}
-
-				metallicDisplaypropertiesData.data = metallicData;
-				return metallicDisplaypropertiesData;
-
-			}
-
-			function parseBasematerialNode( basematerialNode ) {
-
-				const basematerialData = {};
-				basematerialData[ 'name' ] = basematerialNode.getAttribute( 'name' ); // required
-				basematerialData[ 'displaycolor' ] = basematerialNode.getAttribute( 'displaycolor' ); // required
-				basematerialData[ 'displaypropertiesid' ] = basematerialNode.getAttribute( 'displaypropertiesid' );
-				return basematerialData;
-
-			}
-
-			function parseMeshNode( meshNode ) {
-
-				const meshData = {};
-				const vertices = [];
-				const vertexNodes = meshNode.querySelectorAll( 'vertices vertex' );
-				for ( let i = 0; i < vertexNodes.length; i ++ ) {
-
-					const vertexNode = vertexNodes[ i ];
-					const x = vertexNode.getAttribute( 'x' );
-					const y = vertexNode.getAttribute( 'y' );
-					const z = vertexNode.getAttribute( 'z' );
-					vertices.push( parseFloat( x ), parseFloat( y ), parseFloat( z ) );
-
-				}
-
-				meshData[ 'vertices' ] = new Float32Array( vertices );
-				const triangleProperties = [];
-				const triangles = [];
-				const triangleNodes = meshNode.querySelectorAll( 'triangles triangle' );
-				for ( let i = 0; i < triangleNodes.length; i ++ ) {
-
-					const triangleNode = triangleNodes[ i ];
-					const v1 = triangleNode.getAttribute( 'v1' );
-					const v2 = triangleNode.getAttribute( 'v2' );
-					const v3 = triangleNode.getAttribute( 'v3' );
-					const p1 = triangleNode.getAttribute( 'p1' );
-					const p2 = triangleNode.getAttribute( 'p2' );
-					const p3 = triangleNode.getAttribute( 'p3' );
-					const pid = triangleNode.getAttribute( 'pid' );
-					const triangleProperty = {};
-					triangleProperty[ 'v1' ] = parseInt( v1, 10 );
-					triangleProperty[ 'v2' ] = parseInt( v2, 10 );
-					triangleProperty[ 'v3' ] = parseInt( v3, 10 );
-					triangles.push( triangleProperty[ 'v1' ], triangleProperty[ 'v2' ], triangleProperty[ 'v3' ] );
-
-					// optional
-
-					if ( p1 ) {
-
-						triangleProperty[ 'p1' ] = parseInt( p1, 10 );
-
-					}
-
-					if ( p2 ) {
-
-						triangleProperty[ 'p2' ] = parseInt( p2, 10 );
-
-					}
-
-					if ( p3 ) {
-
-						triangleProperty[ 'p3' ] = parseInt( p3, 10 );
-
-					}
-
-					if ( pid ) {
-
-						triangleProperty[ 'pid' ] = pid;
-
-					}
-
-					if ( 0 < Object.keys( triangleProperty ).length ) {
-
-						triangleProperties.push( triangleProperty );
-
-					}
-
-				}
-
-				meshData[ 'triangleProperties' ] = triangleProperties;
-				meshData[ 'triangles' ] = new Uint32Array( triangles );
-				return meshData;
-
-			}
-
-			function parseComponentsNode( componentsNode ) {
-
-				const components = [];
-				const componentNodes = componentsNode.querySelectorAll( 'component' );
-				for ( let i = 0; i < componentNodes.length; i ++ ) {
-
-					const componentNode = componentNodes[ i ];
-					const componentData = parseComponentNode( componentNode );
-					components.push( componentData );
-
-				}
-
-				return components;
-
-			}
-
-			function parseComponentNode( componentNode ) {
-
-				const componentData = {};
-				componentData[ 'objectId' ] = componentNode.getAttribute( 'objectid' ); // required
-
-				const transform = componentNode.getAttribute( 'transform' );
-				if ( transform ) {
-
-					componentData[ 'transform' ] = parseTransform( transform );
-
-				}
-
-				return componentData;
-
-			}
-
-			function parseTransform( transform ) {
-
-				const t = [];
-				transform.split( ' ' ).forEach( function ( s ) {
-
-					t.push( parseFloat( s ) );
-
-				} );
-				const matrix = new THREE.Matrix4();
-				matrix.set( t[ 0 ], t[ 3 ], t[ 6 ], t[ 9 ], t[ 1 ], t[ 4 ], t[ 7 ], t[ 10 ], t[ 2 ], t[ 5 ], t[ 8 ], t[ 11 ], 0.0, 0.0, 0.0, 1.0 );
-				return matrix;
-
-			}
-
-			function parseObjectNode( objectNode ) {
-
-				const objectData = {
-					type: objectNode.getAttribute( 'type' )
-				};
-				const id = objectNode.getAttribute( 'id' );
-				if ( id ) {
-
-					objectData[ 'id' ] = id;
-
-				}
-
-				const pid = objectNode.getAttribute( 'pid' );
-				if ( pid ) {
-
-					objectData[ 'pid' ] = pid;
-
-				}
-
-				const pindex = objectNode.getAttribute( 'pindex' );
-				if ( pindex ) {
-
-					objectData[ 'pindex' ] = pindex;
-
-				}
-
-				const thumbnail = objectNode.getAttribute( 'thumbnail' );
-				if ( thumbnail ) {
-
-					objectData[ 'thumbnail' ] = thumbnail;
-
-				}
-
-				const partnumber = objectNode.getAttribute( 'partnumber' );
-				if ( partnumber ) {
-
-					objectData[ 'partnumber' ] = partnumber;
-
-				}
-
-				const name = objectNode.getAttribute( 'name' );
-				if ( name ) {
-
-					objectData[ 'name' ] = name;
-
-				}
-
-				const meshNode = objectNode.querySelector( 'mesh' );
-				if ( meshNode ) {
-
-					objectData[ 'mesh' ] = parseMeshNode( meshNode );
-
-				}
-
-				const componentsNode = objectNode.querySelector( 'components' );
-				if ( componentsNode ) {
-
-					objectData[ 'components' ] = parseComponentsNode( componentsNode );
-
-				}
-
-				return objectData;
-
-			}
-
-			function parseResourcesNode( resourcesNode ) {
-
-				const resourcesData = {};
-				resourcesData[ 'basematerials' ] = {};
-				const basematerialsNodes = resourcesNode.querySelectorAll( 'basematerials' );
-				for ( let i = 0; i < basematerialsNodes.length; i ++ ) {
-
-					const basematerialsNode = basematerialsNodes[ i ];
-					const basematerialsData = parseBasematerialsNode( basematerialsNode );
-					resourcesData[ 'basematerials' ][ basematerialsData[ 'id' ] ] = basematerialsData;
-
-				}
-
-				//
-
-				resourcesData[ 'texture2d' ] = {};
-				const textures2DNodes = resourcesNode.querySelectorAll( 'texture2d' );
-				for ( let i = 0; i < textures2DNodes.length; i ++ ) {
-
-					const textures2DNode = textures2DNodes[ i ];
-					const texture2DData = parseTexture2DNode( textures2DNode );
-					resourcesData[ 'texture2d' ][ texture2DData[ 'id' ] ] = texture2DData;
-
-				}
-
-				//
-
-				resourcesData[ 'colorgroup' ] = {};
-				const colorGroupNodes = resourcesNode.querySelectorAll( 'colorgroup' );
-				for ( let i = 0; i < colorGroupNodes.length; i ++ ) {
-
-					const colorGroupNode = colorGroupNodes[ i ];
-					const colorGroupData = parseColorGroupNode( colorGroupNode );
-					resourcesData[ 'colorgroup' ][ colorGroupData[ 'id' ] ] = colorGroupData;
-
-				}
-
-				//
-
-				resourcesData[ 'pbmetallicdisplayproperties' ] = {};
-				const pbmetallicdisplaypropertiesNodes = resourcesNode.querySelectorAll( 'pbmetallicdisplayproperties' );
-				for ( let i = 0; i < pbmetallicdisplaypropertiesNodes.length; i ++ ) {
-
-					const pbmetallicdisplaypropertiesNode = pbmetallicdisplaypropertiesNodes[ i ];
-					const pbmetallicdisplaypropertiesData = parseMetallicDisplaypropertiesNode( pbmetallicdisplaypropertiesNode );
-					resourcesData[ 'pbmetallicdisplayproperties' ][ pbmetallicdisplaypropertiesData[ 'id' ] ] = pbmetallicdisplaypropertiesData;
-
-				}
-
-				//
-
-				resourcesData[ 'texture2dgroup' ] = {};
-				const textures2DGroupNodes = resourcesNode.querySelectorAll( 'texture2dgroup' );
-				for ( let i = 0; i < textures2DGroupNodes.length; i ++ ) {
-
-					const textures2DGroupNode = textures2DGroupNodes[ i ];
-					const textures2DGroupData = parseTextures2DGroupNode( textures2DGroupNode );
-					resourcesData[ 'texture2dgroup' ][ textures2DGroupData[ 'id' ] ] = textures2DGroupData;
-
-				}
-
-				//
-
-				resourcesData[ 'object' ] = {};
-				const objectNodes = resourcesNode.querySelectorAll( 'object' );
-				for ( let i = 0; i < objectNodes.length; i ++ ) {
-
-					const objectNode = objectNodes[ i ];
-					const objectData = parseObjectNode( objectNode );
-					resourcesData[ 'object' ][ objectData[ 'id' ] ] = objectData;
-
-				}
-
-				return resourcesData;
-
-			}
-
-			function parseBuildNode( buildNode ) {
-
-				const buildData = [];
-				const itemNodes = buildNode.querySelectorAll( 'item' );
-				for ( let i = 0; i < itemNodes.length; i ++ ) {
-
-					const itemNode = itemNodes[ i ];
-					const buildItem = {
-						objectId: itemNode.getAttribute( 'objectid' )
-					};
-					const transform = itemNode.getAttribute( 'transform' );
-					if ( transform ) {
-
-						buildItem[ 'transform' ] = parseTransform( transform );
-
-					}
-
-					buildData.push( buildItem );
-
-				}
-
-				return buildData;
-
-			}
-
-			function parseModelNode( modelNode ) {
-
-				const modelData = {
-					unit: modelNode.getAttribute( 'unit' ) || 'millimeter'
-				};
-				const metadataNodes = modelNode.querySelectorAll( 'metadata' );
-				if ( metadataNodes ) {
-
-					modelData[ 'metadata' ] = parseMetadataNodes( metadataNodes );
-
-				}
-
-				const resourcesNode = modelNode.querySelector( 'resources' );
-				if ( resourcesNode ) {
-
-					modelData[ 'resources' ] = parseResourcesNode( resourcesNode );
-
-				}
-
-				const buildNode = modelNode.querySelector( 'build' );
-				if ( buildNode ) {
-
-					modelData[ 'build' ] = parseBuildNode( buildNode );
-
-				}
-
-				return modelData;
-
-			}
-
-			function buildTexture( texture2dgroup, objects, modelData, textureData ) {
-
-				const texid = texture2dgroup.texid;
-				const texture2ds = modelData.resources.texture2d;
-				const texture2d = texture2ds[ texid ];
-				if ( texture2d ) {
-
-					const data = textureData[ texture2d.path ];
-					const type = texture2d.contenttype;
-					const blob = new Blob( [ data ], {
-						type: type
-					} );
-					const sourceURI = URL.createObjectURL( blob );
-					const texture = textureLoader.load( sourceURI, function () {
-
-						URL.revokeObjectURL( sourceURI );
-
-					} );
-					texture.encoding = THREE.sRGBEncoding;
-
-					// texture parameters
-
-					switch ( texture2d.tilestyleu ) {
-
-						case 'wrap':
-							texture.wrapS = THREE.RepeatWrapping;
-							break;
-						case 'mirror':
-							texture.wrapS = THREE.MirroredRepeatWrapping;
-							break;
-						case 'none':
-						case 'clamp':
-							texture.wrapS = THREE.ClampToEdgeWrapping;
-							break;
-						default:
-							texture.wrapS = THREE.RepeatWrapping;
-
-					}
-
-					switch ( texture2d.tilestylev ) {
-
-						case 'wrap':
-							texture.wrapT = THREE.RepeatWrapping;
-							break;
-						case 'mirror':
-							texture.wrapT = THREE.MirroredRepeatWrapping;
-							break;
-						case 'none':
-						case 'clamp':
-							texture.wrapT = THREE.ClampToEdgeWrapping;
-							break;
-						default:
-							texture.wrapT = THREE.RepeatWrapping;
-
-					}
-
-					switch ( texture2d.filter ) {
-
-						case 'auto':
-							texture.magFilter = THREE.LinearFilter;
-							texture.minFilter = THREE.LinearMipmapLinearFilter;
-							break;
-						case 'linear':
-							texture.magFilter = THREE.LinearFilter;
-							texture.minFilter = THREE.LinearFilter;
-							break;
-						case 'nearest':
-							texture.magFilter = THREE.NearestFilter;
-							texture.minFilter = THREE.NearestFilter;
-							break;
-						default:
-							texture.magFilter = THREE.LinearFilter;
-							texture.minFilter = THREE.LinearMipmapLinearFilter;
-
-					}
-
-					return texture;
-
-				} else {
-
-					return null;
-
-				}
-
-			}
-
-			function buildBasematerialsMeshes( basematerials, triangleProperties, meshData, objects, modelData, textureData, objectData ) {
-
-				const objectPindex = objectData.pindex;
-				const materialMap = {};
-				for ( let i = 0, l = triangleProperties.length; i < l; i ++ ) {
-
-					const triangleProperty = triangleProperties[ i ];
-					const pindex = triangleProperty.p1 !== undefined ? triangleProperty.p1 : objectPindex;
-					if ( materialMap[ pindex ] === undefined ) materialMap[ pindex ] = [];
-					materialMap[ pindex ].push( triangleProperty );
-
-				}
-
-				//
-
-				const keys = Object.keys( materialMap );
-				const meshes = [];
-				for ( let i = 0, l = keys.length; i < l; i ++ ) {
-
-					const materialIndex = keys[ i ];
-					const trianglePropertiesProps = materialMap[ materialIndex ];
-					const basematerialData = basematerials.basematerials[ materialIndex ];
-					const material = getBuild( basematerialData, objects, modelData, textureData, objectData, buildBasematerial );
-
-					//
-
-					const geometry = new THREE.BufferGeometry();
-					const positionData = [];
-					const vertices = meshData.vertices;
-					for ( let j = 0, jl = trianglePropertiesProps.length; j < jl; j ++ ) {
-
-						const triangleProperty = trianglePropertiesProps[ j ];
-						positionData.push( vertices[ triangleProperty.v1 * 3 + 0 ] );
-						positionData.push( vertices[ triangleProperty.v1 * 3 + 1 ] );
-						positionData.push( vertices[ triangleProperty.v1 * 3 + 2 ] );
-						positionData.push( vertices[ triangleProperty.v2 * 3 + 0 ] );
-						positionData.push( vertices[ triangleProperty.v2 * 3 + 1 ] );
-						positionData.push( vertices[ triangleProperty.v2 * 3 + 2 ] );
-						positionData.push( vertices[ triangleProperty.v3 * 3 + 0 ] );
-						positionData.push( vertices[ triangleProperty.v3 * 3 + 1 ] );
-						positionData.push( vertices[ triangleProperty.v3 * 3 + 2 ] );
-
-					}
-
-					geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positionData, 3 ) );
-
-					//
-
-					const mesh = new THREE.Mesh( geometry, material );
-					meshes.push( mesh );
-
-				}
-
-				return meshes;
-
-			}
-
-			function buildTexturedMesh( texture2dgroup, triangleProperties, meshData, objects, modelData, textureData, objectData ) {
-
-				// geometry
-
-				const geometry = new THREE.BufferGeometry();
-				const positionData = [];
-				const uvData = [];
-				const vertices = meshData.vertices;
-				const uvs = texture2dgroup.uvs;
-				for ( let i = 0, l = triangleProperties.length; i < l; i ++ ) {
-
-					const triangleProperty = triangleProperties[ i ];
-					positionData.push( vertices[ triangleProperty.v1 * 3 + 0 ] );
-					positionData.push( vertices[ triangleProperty.v1 * 3 + 1 ] );
-					positionData.push( vertices[ triangleProperty.v1 * 3 + 2 ] );
-					positionData.push( vertices[ triangleProperty.v2 * 3 + 0 ] );
-					positionData.push( vertices[ triangleProperty.v2 * 3 + 1 ] );
-					positionData.push( vertices[ triangleProperty.v2 * 3 + 2 ] );
-					positionData.push( vertices[ triangleProperty.v3 * 3 + 0 ] );
-					positionData.push( vertices[ triangleProperty.v3 * 3 + 1 ] );
-					positionData.push( vertices[ triangleProperty.v3 * 3 + 2 ] );
-
-					//
-
-					uvData.push( uvs[ triangleProperty.p1 * 2 + 0 ] );
-					uvData.push( uvs[ triangleProperty.p1 * 2 + 1 ] );
-					uvData.push( uvs[ triangleProperty.p2 * 2 + 0 ] );
-					uvData.push( uvs[ triangleProperty.p2 * 2 + 1 ] );
-					uvData.push( uvs[ triangleProperty.p3 * 2 + 0 ] );
-					uvData.push( uvs[ triangleProperty.p3 * 2 + 1 ] );
-
-				}
-
-				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positionData, 3 ) );
-				geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvData, 2 ) );
-
-				// material
-
-				const texture = getBuild( texture2dgroup, objects, modelData, textureData, objectData, buildTexture );
-				const material = new THREE.MeshPhongMaterial( {
-					map: texture,
-					flatShading: true
-				} );
-
-				// mesh
-
-				const mesh = new THREE.Mesh( geometry, material );
-				return mesh;
-
-			}
-
-			function buildVertexColorMesh( colorgroup, triangleProperties, meshData, objectData ) {
-
-				// geometry
-
-				const geometry = new THREE.BufferGeometry();
-				const positionData = [];
-				const colorData = [];
-				const vertices = meshData.vertices;
-				const colors = colorgroup.colors;
-				for ( let i = 0, l = triangleProperties.length; i < l; i ++ ) {
-
-					const triangleProperty = triangleProperties[ i ];
-					const v1 = triangleProperty.v1;
-					const v2 = triangleProperty.v2;
-					const v3 = triangleProperty.v3;
-					positionData.push( vertices[ v1 * 3 + 0 ] );
-					positionData.push( vertices[ v1 * 3 + 1 ] );
-					positionData.push( vertices[ v1 * 3 + 2 ] );
-					positionData.push( vertices[ v2 * 3 + 0 ] );
-					positionData.push( vertices[ v2 * 3 + 1 ] );
-					positionData.push( vertices[ v2 * 3 + 2 ] );
-					positionData.push( vertices[ v3 * 3 + 0 ] );
-					positionData.push( vertices[ v3 * 3 + 1 ] );
-					positionData.push( vertices[ v3 * 3 + 2 ] );
-
-					//
-
-					const p1 = triangleProperty.p1 !== undefined ? triangleProperty.p1 : objectData.pindex;
-					const p2 = triangleProperty.p2 !== undefined ? triangleProperty.p2 : p1;
-					const p3 = triangleProperty.p3 !== undefined ? triangleProperty.p3 : p1;
-					colorData.push( colors[ p1 * 3 + 0 ] );
-					colorData.push( colors[ p1 * 3 + 1 ] );
-					colorData.push( colors[ p1 * 3 + 2 ] );
-					colorData.push( colors[ p2 * 3 + 0 ] );
-					colorData.push( colors[ p2 * 3 + 1 ] );
-					colorData.push( colors[ p2 * 3 + 2 ] );
-					colorData.push( colors[ p3 * 3 + 0 ] );
-					colorData.push( colors[ p3 * 3 + 1 ] );
-					colorData.push( colors[ p3 * 3 + 2 ] );
-
-				}
-
-				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positionData, 3 ) );
-				geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colorData, 3 ) );
-
-				// material
-
-				const material = new THREE.MeshPhongMaterial( {
-					vertexColors: true,
-					flatShading: true
-				} );
-
-				// mesh
-
-				const mesh = new THREE.Mesh( geometry, material );
-				return mesh;
-
-			}
-
-			function buildDefaultMesh( meshData ) {
-
-				const geometry = new THREE.BufferGeometry();
-				geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
-				geometry.setAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
-				const material = new THREE.MeshPhongMaterial( {
-					color: 0xffffff,
-					flatShading: true
-				} );
-				const mesh = new THREE.Mesh( geometry, material );
-				return mesh;
-
-			}
-
-			function buildMeshes( resourceMap, meshData, objects, modelData, textureData, objectData ) {
-
-				const keys = Object.keys( resourceMap );
-				const meshes = [];
-				for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-					const resourceId = keys[ i ];
-					const triangleProperties = resourceMap[ resourceId ];
-					const resourceType = getResourceType( resourceId, modelData );
-					switch ( resourceType ) {
-
-						case 'material':
-							const basematerials = modelData.resources.basematerials[ resourceId ];
-							const newMeshes = buildBasematerialsMeshes( basematerials, triangleProperties, meshData, objects, modelData, textureData, objectData );
-							for ( let j = 0, jl = newMeshes.length; j < jl; j ++ ) {
-
-								meshes.push( newMeshes[ j ] );
-
-							}
-
-							break;
-						case 'texture':
-							const texture2dgroup = modelData.resources.texture2dgroup[ resourceId ];
-							meshes.push( buildTexturedMesh( texture2dgroup, triangleProperties, meshData, objects, modelData, textureData, objectData ) );
-							break;
-						case 'vertexColors':
-							const colorgroup = modelData.resources.colorgroup[ resourceId ];
-							meshes.push( buildVertexColorMesh( colorgroup, triangleProperties, meshData, objectData ) );
-							break;
-						case 'default':
-							meshes.push( buildDefaultMesh( meshData ) );
-							break;
-						default:
-							console.error( 'THREE.3MFLoader: Unsupported resource type.' );
-
-					}
-
-				}
-
-				if ( objectData.name ) {
-
-					for ( let i = 0; i < meshes.length; i ++ ) {
-
-						meshes[ i ].name = objectData.name;
-
-					}
-
-				}
-
-				return meshes;
-
-			}
-
-			function getResourceType( pid, modelData ) {
-
-				if ( modelData.resources.texture2dgroup[ pid ] !== undefined ) {
-
-					return 'texture';
-
-				} else if ( modelData.resources.basematerials[ pid ] !== undefined ) {
-
-					return 'material';
-
-				} else if ( modelData.resources.colorgroup[ pid ] !== undefined ) {
-
-					return 'vertexColors';
-
-				} else if ( pid === 'default' ) {
-
-					return 'default';
-
-				} else {
-
-					return undefined;
-
-				}
-
-			}
-
-			function analyzeObject( meshData, objectData ) {
-
-				const resourceMap = {};
-				const triangleProperties = meshData[ 'triangleProperties' ];
-				const objectPid = objectData.pid;
-				for ( let i = 0, l = triangleProperties.length; i < l; i ++ ) {
-
-					const triangleProperty = triangleProperties[ i ];
-					let pid = triangleProperty.pid !== undefined ? triangleProperty.pid : objectPid;
-					if ( pid === undefined ) pid = 'default';
-					if ( resourceMap[ pid ] === undefined ) resourceMap[ pid ] = [];
-					resourceMap[ pid ].push( triangleProperty );
-
-				}
-
-				return resourceMap;
-
-			}
-
-			function buildGroup( meshData, objects, modelData, textureData, objectData ) {
-
-				const group = new THREE.Group();
-				const resourceMap = analyzeObject( meshData, objectData );
-				const meshes = buildMeshes( resourceMap, meshData, objects, modelData, textureData, objectData );
-				for ( let i = 0, l = meshes.length; i < l; i ++ ) {
-
-					group.add( meshes[ i ] );
-
-				}
-
-				return group;
-
-			}
-
-			function applyExtensions( extensions, meshData, modelXml ) {
-
-				if ( ! extensions ) {
-
-					return;
-
-				}
-
-				const availableExtensions = [];
-				const keys = Object.keys( extensions );
-				for ( let i = 0; i < keys.length; i ++ ) {
-
-					const ns = keys[ i ];
-					for ( let j = 0; j < scope.availableExtensions.length; j ++ ) {
-
-						const extension = scope.availableExtensions[ j ];
-						if ( extension.ns === ns ) {
-
-							availableExtensions.push( extension );
-
-						}
-
-					}
-
-				}
-
-				for ( let i = 0; i < availableExtensions.length; i ++ ) {
-
-					const extension = availableExtensions[ i ];
-					extension.apply( modelXml, extensions[ extension[ 'ns' ] ], meshData );
-
-				}
-
-			}
-
-			function getBuild( data, objects, modelData, textureData, objectData, builder ) {
-
-				if ( data.build !== undefined ) return data.build;
-				data.build = builder( data, objects, modelData, textureData, objectData );
-				return data.build;
-
-			}
-
-			function buildBasematerial( materialData, objects, modelData ) {
-
-				let material;
-				const displaypropertiesid = materialData.displaypropertiesid;
-				const pbmetallicdisplayproperties = modelData.resources.pbmetallicdisplayproperties;
-				if ( displaypropertiesid !== null && pbmetallicdisplayproperties[ displaypropertiesid ] !== undefined ) {
-
-					// metallic display property, use StandardMaterial
-
-					const pbmetallicdisplayproperty = pbmetallicdisplayproperties[ displaypropertiesid ];
-					const metallicData = pbmetallicdisplayproperty.data[ materialData.index ];
-					material = new THREE.MeshStandardMaterial( {
-						flatShading: true,
-						roughness: metallicData.roughness,
-						metalness: metallicData.metallicness
-					} );
-
-				} else {
-
-					// otherwise use PhongMaterial
-
-					material = new THREE.MeshPhongMaterial( {
-						flatShading: true
-					} );
-
-				}
-
-				material.name = materialData.name;
-
-				// displaycolor MUST be specified with a value of a 6 or 8 digit hexadecimal number, e.g. "#RRGGBB" or "#RRGGBBAA"
-
-				const displaycolor = materialData.displaycolor;
-				const color = displaycolor.substring( 0, 7 );
-				material.color.setStyle( color );
-				material.color.convertSRGBToLinear(); // displaycolor is in sRGB
-
-				// process alpha if set
-
-				if ( displaycolor.length === 9 ) {
-
-					material.opacity = parseInt( displaycolor.charAt( 7 ) + displaycolor.charAt( 8 ), 16 ) / 255;
-
-				}
-
-				return material;
-
-			}
-
-			function buildComposite( compositeData, objects, modelData, textureData ) {
-
-				const composite = new THREE.Group();
-				for ( let j = 0; j < compositeData.length; j ++ ) {
-
-					const component = compositeData[ j ];
-					let build = objects[ component.objectId ];
-					if ( build === undefined ) {
-
-						buildObject( component.objectId, objects, modelData, textureData );
-						build = objects[ component.objectId ];
-
-					}
-
-					const object3D = build.clone();
-
-					// apply component transform
-
-					const transform = component.transform;
-					if ( transform ) {
-
-						object3D.applyMatrix4( transform );
-
-					}
-
-					composite.add( object3D );
-
-				}
-
-				return composite;
-
-			}
-
-			function buildObject( objectId, objects, modelData, textureData ) {
-
-				const objectData = modelData[ 'resources' ][ 'object' ][ objectId ];
-				if ( objectData[ 'mesh' ] ) {
-
-					const meshData = objectData[ 'mesh' ];
-					const extensions = modelData[ 'extensions' ];
-					const modelXml = modelData[ 'xml' ];
-					applyExtensions( extensions, meshData, modelXml );
-					objects[ objectData.id ] = getBuild( meshData, objects, modelData, textureData, objectData, buildGroup );
-
-				} else {
-
-					const compositeData = objectData[ 'components' ];
-					objects[ objectData.id ] = getBuild( compositeData, objects, modelData, textureData, objectData, buildComposite );
-
-				}
-
-				if ( objectData.name ) {
-
-					objects[ objectData.id ].name = objectData.name;
-
-				}
-
-			}
-
-			function buildObjects( data3mf ) {
-
-				const modelsData = data3mf.model;
-				const modelRels = data3mf.modelRels;
-				const objects = {};
-				const modelsKeys = Object.keys( modelsData );
-				const textureData = {};
-
-				// evaluate model relationships to textures
-
-				if ( modelRels ) {
-
-					for ( let i = 0, l = modelRels.length; i < l; i ++ ) {
-
-						const modelRel = modelRels[ i ];
-						const textureKey = modelRel.target.substring( 1 );
-						if ( data3mf.texture[ textureKey ] ) {
-
-							textureData[ modelRel.target ] = data3mf.texture[ textureKey ];
-
-						}
-
-					}
-
-				}
-
-				// start build
-
-				for ( let i = 0; i < modelsKeys.length; i ++ ) {
-
-					const modelsKey = modelsKeys[ i ];
-					const modelData = modelsData[ modelsKey ];
-					const objectIds = Object.keys( modelData[ 'resources' ][ 'object' ] );
-					for ( let j = 0; j < objectIds.length; j ++ ) {
-
-						const objectId = objectIds[ j ];
-						buildObject( objectId, objects, modelData, textureData );
-
-					}
-
-				}
-
-				return objects;
-
-			}
-
-			function fetch3DModelPart( rels ) {
-
-				for ( let i = 0; i < rels.length; i ++ ) {
-
-					const rel = rels[ i ];
-					const extension = rel.target.split( '.' ).pop();
-					if ( extension.toLowerCase() === 'model' ) return rel;
-
-				}
-
-			}
-
-			function build( objects, data3mf ) {
-
-				const group = new THREE.Group();
-				const relationship = fetch3DModelPart( data3mf[ 'rels' ] );
-				const buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
-				for ( let i = 0; i < buildData.length; i ++ ) {
-
-					const buildItem = buildData[ i ];
-					const object3D = objects[ buildItem[ 'objectId' ] ].clone();
-
-					// apply transform
-
-					const transform = buildItem[ 'transform' ];
-					if ( transform ) {
-
-						object3D.applyMatrix4( transform );
-
-					}
-
-					group.add( object3D );
-
-				}
-
-				return group;
-
-			}
-
-			const data3mf = loadDocument( data );
-			const objects = buildObjects( data3mf );
-			return build( objects, data3mf );
-
-		}
-		addExtension( extension ) {
-
-			this.availableExtensions.push( extension );
-
-		}
-
-	}
-
-	THREE.ThreeMFLoader = ThreeMFLoader;
-
-} )();

+ 0 - 504
examples/js/loaders/AMFLoader.js

@@ -1,504 +0,0 @@
-( function () {
-
-	/**
- * Description: Early release of an AMF THREE.Loader following the pattern of the
- * example loaders in the three.js project.
- *
- * Usage:
- *	const loader = new AMFLoader();
- *	loader.load('/path/to/project.amf', function(objecttree) {
- *		scene.add(objecttree);
- *	});
- *
- * Materials now supported, material colors supported
- * Zip support, requires fflate
- * No constellation support (yet)!
- *
- */
-
-	class AMFLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( text ) {
-
-				try {
-
-					onLoad( scope.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( data ) {
-
-			function loadDocument( data ) {
-
-				let view = new DataView( data );
-				const magic = String.fromCharCode( view.getUint8( 0 ), view.getUint8( 1 ) );
-				if ( magic === 'PK' ) {
-
-					let zip = null;
-					let file = null;
-					console.log( 'THREE.AMFLoader: Loading Zip' );
-					try {
-
-						zip = fflate.unzipSync( new Uint8Array( data ) ); // eslint-disable-line no-undef
-
-					} catch ( e ) {
-
-						if ( e instanceof ReferenceError ) {
-
-							console.log( 'THREE.AMFLoader: fflate missing and file is compressed.' );
-							return null;
-
-						}
-
-					}
-
-					for ( file in zip ) {
-
-						if ( file.toLowerCase().slice( - 4 ) === '.amf' ) {
-
-							break;
-
-						}
-
-					}
-
-					console.log( 'THREE.AMFLoader: Trying to load file asset: ' + file );
-					view = new DataView( zip[ file ].buffer );
-
-				}
-
-				const fileText = THREE.LoaderUtils.decodeText( view );
-				const xmlData = new DOMParser().parseFromString( fileText, 'application/xml' );
-				if ( xmlData.documentElement.nodeName.toLowerCase() !== 'amf' ) {
-
-					console.log( 'THREE.AMFLoader: Error loading AMF - no AMF document found.' );
-					return null;
-
-				}
-
-				return xmlData;
-
-			}
-
-			function loadDocumentScale( node ) {
-
-				let scale = 1.0;
-				let unit = 'millimeter';
-				if ( node.documentElement.attributes.unit !== undefined ) {
-
-					unit = node.documentElement.attributes.unit.value.toLowerCase();
-
-				}
-
-				const scaleUnits = {
-					millimeter: 1.0,
-					inch: 25.4,
-					feet: 304.8,
-					meter: 1000.0,
-					micron: 0.001
-				};
-				if ( scaleUnits[ unit ] !== undefined ) {
-
-					scale = scaleUnits[ unit ];
-
-				}
-
-				console.log( 'THREE.AMFLoader: Unit scale: ' + scale );
-				return scale;
-
-			}
-
-			function loadMaterials( node ) {
-
-				let matName = 'AMF Material';
-				const matId = node.attributes.id.textContent;
-				let color = {
-					r: 1.0,
-					g: 1.0,
-					b: 1.0,
-					a: 1.0
-				};
-				let loadedMaterial = null;
-				for ( let i = 0; i < node.childNodes.length; i ++ ) {
-
-					const matChildEl = node.childNodes[ i ];
-					if ( matChildEl.nodeName === 'metadata' && matChildEl.attributes.type !== undefined ) {
-
-						if ( matChildEl.attributes.type.value === 'name' ) {
-
-							matName = matChildEl.textContent;
-
-						}
-
-					} else if ( matChildEl.nodeName === 'color' ) {
-
-						color = loadColor( matChildEl );
-
-					}
-
-				}
-
-				loadedMaterial = new THREE.MeshPhongMaterial( {
-					flatShading: true,
-					color: new THREE.Color( color.r, color.g, color.b ),
-					name: matName
-				} );
-				if ( color.a !== 1.0 ) {
-
-					loadedMaterial.transparent = true;
-					loadedMaterial.opacity = color.a;
-
-				}
-
-				return {
-					id: matId,
-					material: loadedMaterial
-				};
-
-			}
-
-			function loadColor( node ) {
-
-				const color = {
-					r: 1.0,
-					g: 1.0,
-					b: 1.0,
-					a: 1.0
-				};
-				for ( let i = 0; i < node.childNodes.length; i ++ ) {
-
-					const matColor = node.childNodes[ i ];
-					if ( matColor.nodeName === 'r' ) {
-
-						color.r = matColor.textContent;
-
-					} else if ( matColor.nodeName === 'g' ) {
-
-						color.g = matColor.textContent;
-
-					} else if ( matColor.nodeName === 'b' ) {
-
-						color.b = matColor.textContent;
-
-					} else if ( matColor.nodeName === 'a' ) {
-
-						color.a = matColor.textContent;
-
-					}
-
-				}
-
-				return color;
-
-			}
-
-			function loadMeshVolume( node ) {
-
-				const volume = {
-					name: '',
-					triangles: [],
-					materialid: null
-				};
-				let currVolumeNode = node.firstElementChild;
-				if ( node.attributes.materialid !== undefined ) {
-
-					volume.materialId = node.attributes.materialid.nodeValue;
-
-				}
-
-				while ( currVolumeNode ) {
-
-					if ( currVolumeNode.nodeName === 'metadata' ) {
-
-						if ( currVolumeNode.attributes.type !== undefined ) {
-
-							if ( currVolumeNode.attributes.type.value === 'name' ) {
-
-								volume.name = currVolumeNode.textContent;
-
-							}
-
-						}
-
-					} else if ( currVolumeNode.nodeName === 'triangle' ) {
-
-						const v1 = currVolumeNode.getElementsByTagName( 'v1' )[ 0 ].textContent;
-						const v2 = currVolumeNode.getElementsByTagName( 'v2' )[ 0 ].textContent;
-						const v3 = currVolumeNode.getElementsByTagName( 'v3' )[ 0 ].textContent;
-						volume.triangles.push( v1, v2, v3 );
-
-					}
-
-					currVolumeNode = currVolumeNode.nextElementSibling;
-
-				}
-
-				return volume;
-
-			}
-
-			function loadMeshVertices( node ) {
-
-				const vertArray = [];
-				const normalArray = [];
-				let currVerticesNode = node.firstElementChild;
-				while ( currVerticesNode ) {
-
-					if ( currVerticesNode.nodeName === 'vertex' ) {
-
-						let vNode = currVerticesNode.firstElementChild;
-						while ( vNode ) {
-
-							if ( vNode.nodeName === 'coordinates' ) {
-
-								const x = vNode.getElementsByTagName( 'x' )[ 0 ].textContent;
-								const y = vNode.getElementsByTagName( 'y' )[ 0 ].textContent;
-								const z = vNode.getElementsByTagName( 'z' )[ 0 ].textContent;
-								vertArray.push( x, y, z );
-
-							} else if ( vNode.nodeName === 'normal' ) {
-
-								const nx = vNode.getElementsByTagName( 'nx' )[ 0 ].textContent;
-								const ny = vNode.getElementsByTagName( 'ny' )[ 0 ].textContent;
-								const nz = vNode.getElementsByTagName( 'nz' )[ 0 ].textContent;
-								normalArray.push( nx, ny, nz );
-
-							}
-
-							vNode = vNode.nextElementSibling;
-
-						}
-
-					}
-
-					currVerticesNode = currVerticesNode.nextElementSibling;
-
-				}
-
-				return {
-					'vertices': vertArray,
-					'normals': normalArray
-				};
-
-			}
-
-			function loadObject( node ) {
-
-				const objId = node.attributes.id.textContent;
-				const loadedObject = {
-					name: 'amfobject',
-					meshes: []
-				};
-				let currColor = null;
-				let currObjNode = node.firstElementChild;
-				while ( currObjNode ) {
-
-					if ( currObjNode.nodeName === 'metadata' ) {
-
-						if ( currObjNode.attributes.type !== undefined ) {
-
-							if ( currObjNode.attributes.type.value === 'name' ) {
-
-								loadedObject.name = currObjNode.textContent;
-
-							}
-
-						}
-
-					} else if ( currObjNode.nodeName === 'color' ) {
-
-						currColor = loadColor( currObjNode );
-
-					} else if ( currObjNode.nodeName === 'mesh' ) {
-
-						let currMeshNode = currObjNode.firstElementChild;
-						const mesh = {
-							vertices: [],
-							normals: [],
-							volumes: [],
-							color: currColor
-						};
-						while ( currMeshNode ) {
-
-							if ( currMeshNode.nodeName === 'vertices' ) {
-
-								const loadedVertices = loadMeshVertices( currMeshNode );
-								mesh.normals = mesh.normals.concat( loadedVertices.normals );
-								mesh.vertices = mesh.vertices.concat( loadedVertices.vertices );
-
-							} else if ( currMeshNode.nodeName === 'volume' ) {
-
-								mesh.volumes.push( loadMeshVolume( currMeshNode ) );
-
-							}
-
-							currMeshNode = currMeshNode.nextElementSibling;
-
-						}
-
-						loadedObject.meshes.push( mesh );
-
-					}
-
-					currObjNode = currObjNode.nextElementSibling;
-
-				}
-
-				return {
-					'id': objId,
-					'obj': loadedObject
-				};
-
-			}
-
-			const xmlData = loadDocument( data );
-			let amfName = '';
-			let amfAuthor = '';
-			const amfScale = loadDocumentScale( xmlData );
-			const amfMaterials = {};
-			const amfObjects = {};
-			const childNodes = xmlData.documentElement.childNodes;
-			let i, j;
-			for ( i = 0; i < childNodes.length; i ++ ) {
-
-				const child = childNodes[ i ];
-				if ( child.nodeName === 'metadata' ) {
-
-					if ( child.attributes.type !== undefined ) {
-
-						if ( child.attributes.type.value === 'name' ) {
-
-							amfName = child.textContent;
-
-						} else if ( child.attributes.type.value === 'author' ) {
-
-							amfAuthor = child.textContent;
-
-						}
-
-					}
-
-				} else if ( child.nodeName === 'material' ) {
-
-					const loadedMaterial = loadMaterials( child );
-					amfMaterials[ loadedMaterial.id ] = loadedMaterial.material;
-
-				} else if ( child.nodeName === 'object' ) {
-
-					const loadedObject = loadObject( child );
-					amfObjects[ loadedObject.id ] = loadedObject.obj;
-
-				}
-
-			}
-
-			const sceneObject = new THREE.Group();
-			const defaultMaterial = new THREE.MeshPhongMaterial( {
-				color: 0xaaaaff,
-				flatShading: true
-			} );
-			sceneObject.name = amfName;
-			sceneObject.userData.author = amfAuthor;
-			sceneObject.userData.loader = 'AMF';
-			for ( const id in amfObjects ) {
-
-				const part = amfObjects[ id ];
-				const meshes = part.meshes;
-				const newObject = new THREE.Group();
-				newObject.name = part.name || '';
-				for ( i = 0; i < meshes.length; i ++ ) {
-
-					let objDefaultMaterial = defaultMaterial;
-					const mesh = meshes[ i ];
-					const vertices = new THREE.Float32BufferAttribute( mesh.vertices, 3 );
-					let normals = null;
-					if ( mesh.normals.length ) {
-
-						normals = new THREE.Float32BufferAttribute( mesh.normals, 3 );
-
-					}
-
-					if ( mesh.color ) {
-
-						const color = mesh.color;
-						objDefaultMaterial = defaultMaterial.clone();
-						objDefaultMaterial.color = new THREE.Color( color.r, color.g, color.b );
-						if ( color.a !== 1.0 ) {
-
-							objDefaultMaterial.transparent = true;
-							objDefaultMaterial.opacity = color.a;
-
-						}
-
-					}
-
-					const volumes = mesh.volumes;
-					for ( j = 0; j < volumes.length; j ++ ) {
-
-						const volume = volumes[ j ];
-						const newGeometry = new THREE.BufferGeometry();
-						let material = objDefaultMaterial;
-						newGeometry.setIndex( volume.triangles );
-						newGeometry.setAttribute( 'position', vertices.clone() );
-						if ( normals ) {
-
-							newGeometry.setAttribute( 'normal', normals.clone() );
-
-						}
-
-						if ( amfMaterials[ volume.materialId ] !== undefined ) {
-
-							material = amfMaterials[ volume.materialId ];
-
-						}
-
-						newGeometry.scale( amfScale, amfScale, amfScale );
-						newObject.add( new THREE.Mesh( newGeometry, material.clone() ) );
-
-					}
-
-				}
-
-				sceneObject.add( newObject );
-
-			}
-
-			return sceneObject;
-
-		}
-
-	}
-
-	THREE.AMFLoader = AMFLoader;
-
-} )();

+ 0 - 395
examples/js/loaders/BVHLoader.js

@@ -1,395 +0,0 @@
-( function () {
-
-	/**
- * Description: reads BVH files and outputs a single THREE.Skeleton and an THREE.AnimationClip
- *
- * Currently only supports bvh files containing a single root.
- *
- */
-
-	class BVHLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.animateBonePositions = true;
-			this.animateBoneRotations = true;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( text ) {
-
-				try {
-
-					onLoad( scope.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( text ) {
-
-			/*
-    	reads a string array (lines) from a BVH file
-    	and outputs a skeleton structure including motion data
-    		returns thee root node:
-    	{ name: '', channels: [], children: [] }
-    */
-			function readBvh( lines ) {
-
-				// read model structure
-
-				if ( nextLine( lines ) !== 'HIERARCHY' ) {
-
-					console.error( 'THREE.BVHLoader: HIERARCHY expected.' );
-
-				}
-
-				const list = []; // collects flat array of all bones
-				const root = readNode( lines, nextLine( lines ), list );
-
-				// read motion data
-
-				if ( nextLine( lines ) !== 'MOTION' ) {
-
-					console.error( 'THREE.BVHLoader: MOTION expected.' );
-
-				}
-
-				// number of frames
-
-				let tokens = nextLine( lines ).split( /[\s]+/ );
-				const numFrames = parseInt( tokens[ 1 ] );
-				if ( isNaN( numFrames ) ) {
-
-					console.error( 'THREE.BVHLoader: Failed to read number of frames.' );
-
-				}
-
-				// frame time
-
-				tokens = nextLine( lines ).split( /[\s]+/ );
-				const frameTime = parseFloat( tokens[ 2 ] );
-				if ( isNaN( frameTime ) ) {
-
-					console.error( 'THREE.BVHLoader: Failed to read frame time.' );
-
-				}
-
-				// read frame data line by line
-
-				for ( let i = 0; i < numFrames; i ++ ) {
-
-					tokens = nextLine( lines ).split( /[\s]+/ );
-					readFrameData( tokens, i * frameTime, root );
-
-				}
-
-				return list;
-
-			}
-
-			/*
-    	Recursively reads data from a single frame into the bone hierarchy.
-    	The passed bone hierarchy has to be structured in the same order as the BVH file.
-    	keyframe data is stored in bone.frames.
-    		- data: splitted string array (frame values), values are shift()ed so
-    	this should be empty after parsing the whole hierarchy.
-    	- frameTime: playback time for this keyframe.
-    	- bone: the bone to read frame data from.
-    */
-			function readFrameData( data, frameTime, bone ) {
-
-				// end sites have no motion data
-
-				if ( bone.type === 'ENDSITE' ) return;
-
-				// add keyframe
-
-				const keyframe = {
-					time: frameTime,
-					position: new THREE.Vector3(),
-					rotation: new THREE.Quaternion()
-				};
-				bone.frames.push( keyframe );
-				const quat = new THREE.Quaternion();
-				const vx = new THREE.Vector3( 1, 0, 0 );
-				const vy = new THREE.Vector3( 0, 1, 0 );
-				const vz = new THREE.Vector3( 0, 0, 1 );
-
-				// parse values for each channel in node
-
-				for ( let i = 0; i < bone.channels.length; i ++ ) {
-
-					switch ( bone.channels[ i ] ) {
-
-						case 'Xposition':
-							keyframe.position.x = parseFloat( data.shift().trim() );
-							break;
-						case 'Yposition':
-							keyframe.position.y = parseFloat( data.shift().trim() );
-							break;
-						case 'Zposition':
-							keyframe.position.z = parseFloat( data.shift().trim() );
-							break;
-						case 'Xrotation':
-							quat.setFromAxisAngle( vx, parseFloat( data.shift().trim() ) * Math.PI / 180 );
-							keyframe.rotation.multiply( quat );
-							break;
-						case 'Yrotation':
-							quat.setFromAxisAngle( vy, parseFloat( data.shift().trim() ) * Math.PI / 180 );
-							keyframe.rotation.multiply( quat );
-							break;
-						case 'Zrotation':
-							quat.setFromAxisAngle( vz, parseFloat( data.shift().trim() ) * Math.PI / 180 );
-							keyframe.rotation.multiply( quat );
-							break;
-						default:
-							console.warn( 'THREE.BVHLoader: Invalid channel type.' );
-
-					}
-
-				}
-
-				// parse child nodes
-
-				for ( let i = 0; i < bone.children.length; i ++ ) {
-
-					readFrameData( data, frameTime, bone.children[ i ] );
-
-				}
-
-			}
-
-			/*
-     Recursively parses the HIERACHY section of the BVH file
-    	 - lines: all lines of the file. lines are consumed as we go along.
-     - firstline: line containing the node type and name e.g. 'JOINT hip'
-     - list: collects a flat list of nodes
-    	 returns: a BVH node including children
-    */
-			function readNode( lines, firstline, list ) {
-
-				const node = {
-					name: '',
-					type: '',
-					frames: []
-				};
-				list.push( node );
-
-				// parse node type and name
-
-				let tokens = firstline.split( /[\s]+/ );
-				if ( tokens[ 0 ].toUpperCase() === 'END' && tokens[ 1 ].toUpperCase() === 'SITE' ) {
-
-					node.type = 'ENDSITE';
-					node.name = 'ENDSITE'; // bvh end sites have no name
-
-				} else {
-
-					node.name = tokens[ 1 ];
-					node.type = tokens[ 0 ].toUpperCase();
-
-				}
-
-				if ( nextLine( lines ) !== '{' ) {
-
-					console.error( 'THREE.BVHLoader: Expected opening { after type & name' );
-
-				}
-
-				// parse OFFSET
-
-				tokens = nextLine( lines ).split( /[\s]+/ );
-				if ( tokens[ 0 ] !== 'OFFSET' ) {
-
-					console.error( 'THREE.BVHLoader: Expected OFFSET but got: ' + tokens[ 0 ] );
-
-				}
-
-				if ( tokens.length !== 4 ) {
-
-					console.error( 'THREE.BVHLoader: Invalid number of values for OFFSET.' );
-
-				}
-
-				const offset = new THREE.Vector3( parseFloat( tokens[ 1 ] ), parseFloat( tokens[ 2 ] ), parseFloat( tokens[ 3 ] ) );
-				if ( isNaN( offset.x ) || isNaN( offset.y ) || isNaN( offset.z ) ) {
-
-					console.error( 'THREE.BVHLoader: Invalid values of OFFSET.' );
-
-				}
-
-				node.offset = offset;
-
-				// parse CHANNELS definitions
-
-				if ( node.type !== 'ENDSITE' ) {
-
-					tokens = nextLine( lines ).split( /[\s]+/ );
-					if ( tokens[ 0 ] !== 'CHANNELS' ) {
-
-						console.error( 'THREE.BVHLoader: Expected CHANNELS definition.' );
-
-					}
-
-					const numChannels = parseInt( tokens[ 1 ] );
-					node.channels = tokens.splice( 2, numChannels );
-					node.children = [];
-
-				}
-
-				// read children
-
-				while ( true ) {
-
-					const line = nextLine( lines );
-					if ( line === '}' ) {
-
-						return node;
-
-					} else {
-
-						node.children.push( readNode( lines, line, list ) );
-
-					}
-
-				}
-
-			}
-
-			/*
-    	recursively converts the internal bvh node structure to a THREE.Bone hierarchy
-    		source: the bvh root node
-    	list: pass an empty array, collects a flat list of all converted THREE.Bones
-    		returns the root THREE.Bone
-    */
-			function toTHREEBone( source, list ) {
-
-				const bone = new THREE.Bone();
-				list.push( bone );
-				bone.position.add( source.offset );
-				bone.name = source.name;
-				if ( source.type !== 'ENDSITE' ) {
-
-					for ( let i = 0; i < source.children.length; i ++ ) {
-
-						bone.add( toTHREEBone( source.children[ i ], list ) );
-
-					}
-
-				}
-
-				return bone;
-
-			}
-
-			/*
-    	builds a THREE.AnimationClip from the keyframe data saved in each bone.
-    		bone: bvh root node
-    		returns: a THREE.AnimationClip containing position and quaternion tracks
-    */
-			function toTHREEAnimation( bones ) {
-
-				const tracks = [];
-
-				// create a position and quaternion animation track for each node
-
-				for ( let i = 0; i < bones.length; i ++ ) {
-
-					const bone = bones[ i ];
-					if ( bone.type === 'ENDSITE' ) continue;
-
-					// track data
-
-					const times = [];
-					const positions = [];
-					const rotations = [];
-					for ( let j = 0; j < bone.frames.length; j ++ ) {
-
-						const frame = bone.frames[ j ];
-						times.push( frame.time );
-
-						// the animation system animates the position property,
-						// so we have to add the joint offset to all values
-
-						positions.push( frame.position.x + bone.offset.x );
-						positions.push( frame.position.y + bone.offset.y );
-						positions.push( frame.position.z + bone.offset.z );
-						rotations.push( frame.rotation.x );
-						rotations.push( frame.rotation.y );
-						rotations.push( frame.rotation.z );
-						rotations.push( frame.rotation.w );
-
-					}
-
-					if ( scope.animateBonePositions ) {
-
-						tracks.push( new THREE.VectorKeyframeTrack( '.bones[' + bone.name + '].position', times, positions ) );
-
-					}
-
-					if ( scope.animateBoneRotations ) {
-
-						tracks.push( new THREE.QuaternionKeyframeTrack( '.bones[' + bone.name + '].quaternion', times, rotations ) );
-
-					}
-
-				}
-
-				return new THREE.AnimationClip( 'animation', - 1, tracks );
-
-			}
-
-			/*
-    	returns the next non-empty line in lines
-    */
-			function nextLine( lines ) {
-
-				let line;
-				// skip empty lines
-				while ( ( line = lines.shift().trim() ).length === 0 ) {}
-
-				return line;
-
-			}
-
-			const scope = this;
-			const lines = text.split( /[\r\n]+/g );
-			const bones = readBvh( lines );
-			const threeBones = [];
-			toTHREEBone( bones[ 0 ], threeBones );
-			const threeClip = toTHREEAnimation( bones );
-			return {
-				skeleton: new THREE.Skeleton( threeBones ),
-				clip: threeClip
-			};
-
-		}
-
-	}
-
-	THREE.BVHLoader = BVHLoader;
-
-} )();

+ 0 - 706
examples/js/loaders/BasisTextureLoader.js

@@ -1,706 +0,0 @@
-( function () {
-
-	/**
- * THREE.Loader for Basis Universal GPU Texture Codec.
- *
- * Basis Universal is a "supercompressed" GPU texture and texture video
- * compression system that outputs a highly compressed intermediate file format
- * (.basis) that can be quickly transcoded to a wide variety of GPU texture
- * compression formats.
- *
- * This loader parallelizes the transcoding process across a configurable number
- * of web workers, before transferring the transcoded compressed texture back
- * to the main thread.
- */
-
-	const _taskCache = new WeakMap();
-	class BasisTextureLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.transcoderPath = '';
-			this.transcoderBinary = null;
-			this.transcoderPending = null;
-			this.workerLimit = 4;
-			this.workerPool = [];
-			this.workerNextTaskID = 1;
-			this.workerSourceURL = '';
-			this.workerConfig = null;
-			console.warn( 'THREE.BasisTextureLoader: This loader is deprecated, and will be removed in a future release. ' + 'Instead, use Basis Universal compression in KTX2 (.ktx2) files with THREE.KTX2Loader.' );
-
-		}
-		setTranscoderPath( path ) {
-
-			this.transcoderPath = path;
-			return this;
-
-		}
-		setWorkerLimit( workerLimit ) {
-
-			this.workerLimit = workerLimit;
-			return this;
-
-		}
-		detectSupport( renderer ) {
-
-			this.workerConfig = {
-				astcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_astc' ),
-				etc1Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc1' ),
-				etc2Supported: renderer.extensions.has( 'WEBGL_compressed_texture_etc' ),
-				dxtSupported: renderer.extensions.has( 'WEBGL_compressed_texture_s3tc' ),
-				bptcSupported: renderer.extensions.has( 'EXT_texture_compression_bptc' ),
-				pvrtcSupported: renderer.extensions.has( 'WEBGL_compressed_texture_pvrtc' ) || renderer.extensions.has( 'WEBKIT_WEBGL_compressed_texture_pvrtc' )
-			};
-			return this;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setWithCredentials( this.withCredentials );
-			const texture = new THREE.CompressedTexture();
-			loader.load( url, buffer => {
-
-				// Check for an existing task using this buffer. A transferred buffer cannot be transferred
-				// again from this thread.
-				if ( _taskCache.has( buffer ) ) {
-
-					const cachedTask = _taskCache.get( buffer );
-					return cachedTask.promise.then( onLoad ).catch( onError );
-
-				}
-
-				this._createTexture( [ buffer ] ).then( function ( _texture ) {
-
-					texture.copy( _texture );
-					texture.needsUpdate = true;
-					if ( onLoad ) onLoad( texture );
-
-				} ).catch( onError );
-
-			}, onProgress, onError );
-			return texture;
-
-		}
-
-		/** Low-level transcoding API, exposed for use by KTX2Loader. */
-		parseInternalAsync( options ) {
-
-			const {
-				levels
-			} = options;
-			const buffers = new Set();
-			for ( let i = 0; i < levels.length; i ++ ) {
-
-				buffers.add( levels[ i ].data.buffer );
-
-			}
-
-			return this._createTexture( Array.from( buffers ), {
-				...options,
-				lowLevel: true
-			} );
-
-		}
-
-		/**
-   * @param {ArrayBuffer[]} buffers
-   * @param {object?} config
-   * @return {Promise<CompressedTexture>}
-   */
-		_createTexture( buffers, config = {} ) {
-
-			let worker;
-			let taskID;
-			const taskConfig = config;
-			let taskCost = 0;
-			for ( let i = 0; i < buffers.length; i ++ ) {
-
-				taskCost += buffers[ i ].byteLength;
-
-			}
-
-			const texturePending = this._allocateWorker( taskCost ).then( _worker => {
-
-				worker = _worker;
-				taskID = this.workerNextTaskID ++;
-				return new Promise( ( resolve, reject ) => {
-
-					worker._callbacks[ taskID ] = {
-						resolve,
-						reject
-					};
-					worker.postMessage( {
-						type: 'transcode',
-						id: taskID,
-						buffers: buffers,
-						taskConfig: taskConfig
-					}, buffers );
-
-				} );
-
-			} ).then( message => {
-
-				const {
-					mipmaps,
-					width,
-					height,
-					format
-				} = message;
-				const texture = new THREE.CompressedTexture( mipmaps, width, height, format, THREE.UnsignedByteType );
-				texture.minFilter = mipmaps.length === 1 ? THREE.LinearFilter : THREE.LinearMipmapLinearFilter;
-				texture.magFilter = THREE.LinearFilter;
-				texture.generateMipmaps = false;
-				texture.needsUpdate = true;
-				return texture;
-
-			} );
-
-			// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
-			texturePending.catch( () => true ).then( () => {
-
-				if ( worker && taskID ) {
-
-					worker._taskLoad -= taskCost;
-					delete worker._callbacks[ taskID ];
-
-				}
-
-			} );
-
-			// Cache the task result.
-			_taskCache.set( buffers[ 0 ], {
-				promise: texturePending
-			} );
-			return texturePending;
-
-		}
-		_initTranscoder() {
-
-			if ( ! this.transcoderPending ) {
-
-				// Load transcoder wrapper.
-				const jsLoader = new THREE.FileLoader( this.manager );
-				jsLoader.setPath( this.transcoderPath );
-				jsLoader.setWithCredentials( this.withCredentials );
-				const jsContent = new Promise( ( resolve, reject ) => {
-
-					jsLoader.load( 'basis_transcoder.js', resolve, undefined, reject );
-
-				} );
-
-				// Load transcoder WASM binary.
-				const binaryLoader = new THREE.FileLoader( this.manager );
-				binaryLoader.setPath( this.transcoderPath );
-				binaryLoader.setResponseType( 'arraybuffer' );
-				binaryLoader.setWithCredentials( this.withCredentials );
-				const binaryContent = new Promise( ( resolve, reject ) => {
-
-					binaryLoader.load( 'basis_transcoder.wasm', resolve, undefined, reject );
-
-				} );
-				this.transcoderPending = Promise.all( [ jsContent, binaryContent ] ).then( ( [ jsContent, binaryContent ] ) => {
-
-					const fn = BasisTextureLoader.BasisWorker.toString();
-					const body = [ '/* constants */', 'let _EngineFormat = ' + JSON.stringify( BasisTextureLoader.EngineFormat ), 'let _TranscoderFormat = ' + JSON.stringify( BasisTextureLoader.TranscoderFormat ), 'let _BasisFormat = ' + JSON.stringify( BasisTextureLoader.BasisFormat ), '/* basis_transcoder.js */', jsContent, '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );
-					this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
-					this.transcoderBinary = binaryContent;
-
-				} );
-
-			}
-
-			return this.transcoderPending;
-
-		}
-		_allocateWorker( taskCost ) {
-
-			return this._initTranscoder().then( () => {
-
-				if ( this.workerPool.length < this.workerLimit ) {
-
-					const worker = new Worker( this.workerSourceURL );
-					worker._callbacks = {};
-					worker._taskLoad = 0;
-					worker.postMessage( {
-						type: 'init',
-						config: this.workerConfig,
-						transcoderBinary: this.transcoderBinary
-					} );
-					worker.onmessage = function ( e ) {
-
-						const message = e.data;
-						switch ( message.type ) {
-
-							case 'transcode':
-								worker._callbacks[ message.id ].resolve( message );
-								break;
-							case 'error':
-								worker._callbacks[ message.id ].reject( message );
-								break;
-							default:
-								console.error( 'THREE.BasisTextureLoader: Unexpected message, "' + message.type + '"' );
-
-						}
-
-					};
-
-					this.workerPool.push( worker );
-
-				} else {
-
-					this.workerPool.sort( function ( a, b ) {
-
-						return a._taskLoad > b._taskLoad ? - 1 : 1;
-
-					} );
-
-				}
-
-				const worker = this.workerPool[ this.workerPool.length - 1 ];
-				worker._taskLoad += taskCost;
-				return worker;
-
-			} );
-
-		}
-		dispose() {
-
-			for ( let i = 0; i < this.workerPool.length; i ++ ) {
-
-				this.workerPool[ i ].terminate();
-
-			}
-
-			this.workerPool.length = 0;
-			return this;
-
-		}
-
-	}
-
-	/* CONSTANTS */
-
-	BasisTextureLoader.BasisFormat = {
-		ETC1S: 0,
-		UASTC_4x4: 1
-	};
-	BasisTextureLoader.TranscoderFormat = {
-		ETC1: 0,
-		ETC2: 1,
-		BC1: 2,
-		BC3: 3,
-		BC4: 4,
-		BC5: 5,
-		BC7_M6_OPAQUE_ONLY: 6,
-		BC7_M5: 7,
-		PVRTC1_4_RGB: 8,
-		PVRTC1_4_RGBA: 9,
-		ASTC_4x4: 10,
-		ATC_RGB: 11,
-		ATC_RGBA_INTERPOLATED_ALPHA: 12,
-		RGBA32: 13,
-		RGB565: 14,
-		BGR565: 15,
-		RGBA4444: 16
-	};
-	BasisTextureLoader.EngineFormat = {
-		RGBAFormat: THREE.RGBAFormat,
-		RGBA_ASTC_4x4_Format: THREE.RGBA_ASTC_4x4_Format,
-		RGBA_BPTC_Format: THREE.RGBA_BPTC_Format,
-		RGBA_ETC2_EAC_Format: THREE.RGBA_ETC2_EAC_Format,
-		RGBA_PVRTC_4BPPV1_Format: THREE.RGBA_PVRTC_4BPPV1_Format,
-		RGBA_S3TC_DXT5_Format: THREE.RGBA_S3TC_DXT5_Format,
-		RGB_ETC1_Format: THREE.RGB_ETC1_Format,
-		RGB_ETC2_Format: THREE.RGB_ETC2_Format,
-		RGB_PVRTC_4BPPV1_Format: THREE.RGB_PVRTC_4BPPV1_Format,
-		RGB_S3TC_DXT1_Format: THREE.RGB_S3TC_DXT1_Format
-	};
-
-	/* WEB WORKER */
-
-	BasisTextureLoader.BasisWorker = function () {
-
-		let config;
-		let transcoderPending;
-		let BasisModule;
-		const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
-		const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
-		const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
-
-		onmessage = function ( e ) {
-
-			const message = e.data;
-			switch ( message.type ) {
-
-				case 'init':
-					config = message.config;
-					init( message.transcoderBinary );
-					break;
-				case 'transcode':
-					transcoderPending.then( () => {
-
-						try {
-
-							const {
-								width,
-								height,
-								hasAlpha,
-								mipmaps,
-								format
-							} = message.taskConfig.lowLevel ? transcodeLowLevel( message.taskConfig ) : transcode( message.buffers[ 0 ] );
-							const buffers = [];
-							for ( let i = 0; i < mipmaps.length; ++ i ) {
-
-								buffers.push( mipmaps[ i ].data.buffer );
-
-							}
-
-							self.postMessage( {
-								type: 'transcode',
-								id: message.id,
-								width,
-								height,
-								hasAlpha,
-								mipmaps,
-								format
-							}, buffers );
-
-						} catch ( error ) {
-
-							console.error( error );
-							self.postMessage( {
-								type: 'error',
-								id: message.id,
-								error: error.message
-							} );
-
-						}
-
-					} );
-					break;
-
-			}
-
-		};
-
-		function init( wasmBinary ) {
-
-			transcoderPending = new Promise( resolve => {
-
-				BasisModule = {
-					wasmBinary,
-					onRuntimeInitialized: resolve
-				};
-				BASIS( BasisModule ); // eslint-disable-line no-undef
-
-			} ).then( () => {
-
-				BasisModule.initializeBasis();
-
-			} );
-
-		}
-
-		function transcodeLowLevel( taskConfig ) {
-
-			const {
-				basisFormat,
-				width,
-				height,
-				hasAlpha
-			} = taskConfig;
-			const {
-				transcoderFormat,
-				engineFormat
-			} = getTranscoderFormat( basisFormat, width, height, hasAlpha );
-			const blockByteLength = BasisModule.getBytesPerBlockOrPixel( transcoderFormat );
-			assert( BasisModule.isFormatSupported( transcoderFormat ), 'THREE.BasisTextureLoader: Unsupported format.' );
-			const mipmaps = [];
-			if ( basisFormat === BasisFormat.ETC1S ) {
-
-				const transcoder = new BasisModule.LowLevelETC1SImageTranscoder();
-				const {
-					endpointCount,
-					endpointsData,
-					selectorCount,
-					selectorsData,
-					tablesData
-				} = taskConfig.globalData;
-				try {
-
-					let ok;
-					ok = transcoder.decodePalettes( endpointCount, endpointsData, selectorCount, selectorsData );
-					assert( ok, 'THREE.BasisTextureLoader: decodePalettes() failed.' );
-					ok = transcoder.decodeTables( tablesData );
-					assert( ok, 'THREE.BasisTextureLoader: decodeTables() failed.' );
-					for ( let i = 0; i < taskConfig.levels.length; i ++ ) {
-
-						const level = taskConfig.levels[ i ];
-						const imageDesc = taskConfig.globalData.imageDescs[ i ];
-						const dstByteLength = getTranscodedImageByteLength( transcoderFormat, level.width, level.height );
-						const dst = new Uint8Array( dstByteLength );
-						ok = transcoder.transcodeImage( transcoderFormat, dst, dstByteLength / blockByteLength, level.data, getWidthInBlocks( transcoderFormat, level.width ), getHeightInBlocks( transcoderFormat, level.height ), level.width, level.height, level.index, imageDesc.rgbSliceByteOffset, imageDesc.rgbSliceByteLength, imageDesc.alphaSliceByteOffset, imageDesc.alphaSliceByteLength, imageDesc.imageFlags, hasAlpha, false, 0, 0 );
-						assert( ok, 'THREE.BasisTextureLoader: transcodeImage() failed for level ' + level.index + '.' );
-						mipmaps.push( {
-							data: dst,
-							width: level.width,
-							height: level.height
-						} );
-
-					}
-
-				} finally {
-
-					transcoder.delete();
-
-				}
-
-			} else {
-
-				for ( let i = 0; i < taskConfig.levels.length; i ++ ) {
-
-					const level = taskConfig.levels[ i ];
-					const dstByteLength = getTranscodedImageByteLength( transcoderFormat, level.width, level.height );
-					const dst = new Uint8Array( dstByteLength );
-					const ok = BasisModule.transcodeUASTCImage( transcoderFormat, dst, dstByteLength / blockByteLength, level.data, getWidthInBlocks( transcoderFormat, level.width ), getHeightInBlocks( transcoderFormat, level.height ), level.width, level.height, level.index, 0, level.data.byteLength, 0, hasAlpha, false, 0, 0, - 1, - 1 );
-					assert( ok, 'THREE.BasisTextureLoader: transcodeUASTCImage() failed for level ' + level.index + '.' );
-					mipmaps.push( {
-						data: dst,
-						width: level.width,
-						height: level.height
-					} );
-
-				}
-
-			}
-
-			return {
-				width,
-				height,
-				hasAlpha,
-				mipmaps,
-				format: engineFormat
-			};
-
-		}
-
-		function transcode( buffer ) {
-
-			const basisFile = new BasisModule.BasisFile( new Uint8Array( buffer ) );
-			const basisFormat = basisFile.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
-			const width = basisFile.getImageWidth( 0, 0 );
-			const height = basisFile.getImageHeight( 0, 0 );
-			const levels = basisFile.getNumLevels( 0 );
-			const hasAlpha = basisFile.getHasAlpha();
-			function cleanup() {
-
-				basisFile.close();
-				basisFile.delete();
-
-			}
-
-			const {
-				transcoderFormat,
-				engineFormat
-			} = getTranscoderFormat( basisFormat, width, height, hasAlpha );
-			if ( ! width || ! height || ! levels ) {
-
-				cleanup();
-				throw new Error( 'THREE.BasisTextureLoader:	Invalid texture' );
-
-			}
-
-			if ( ! basisFile.startTranscoding() ) {
-
-				cleanup();
-				throw new Error( 'THREE.BasisTextureLoader: .startTranscoding failed' );
-
-			}
-
-			const mipmaps = [];
-			for ( let mip = 0; mip < levels; mip ++ ) {
-
-				const mipWidth = basisFile.getImageWidth( 0, mip );
-				const mipHeight = basisFile.getImageHeight( 0, mip );
-				const dst = new Uint8Array( basisFile.getImageTranscodedSizeInBytes( 0, mip, transcoderFormat ) );
-				const status = basisFile.transcodeImage( dst, 0, mip, transcoderFormat, 0, hasAlpha );
-				if ( ! status ) {
-
-					cleanup();
-					throw new Error( 'THREE.BasisTextureLoader: .transcodeImage failed.' );
-
-				}
-
-				mipmaps.push( {
-					data: dst,
-					width: mipWidth,
-					height: mipHeight
-				} );
-
-			}
-
-			cleanup();
-			return {
-				width,
-				height,
-				hasAlpha,
-				mipmaps,
-				format: engineFormat
-			};
-
-		}
-
-		//
-
-		// Optimal choice of a transcoder target format depends on the Basis format (ETC1S or UASTC),
-		// device capabilities, and texture dimensions. The list below ranks the formats separately
-		// for ETC1S and UASTC.
-		//
-		// In some cases, transcoding UASTC to RGBA32 might be preferred for higher quality (at
-		// significant memory cost) compared to ETC1/2, BC1/3, and PVRTC. The transcoder currently
-		// chooses RGBA32 only as a last resort and does not expose that option to the caller.
-		const FORMAT_OPTIONS = [ {
-			if: 'astcSupported',
-			basisFormat: [ BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.ASTC_4x4, TranscoderFormat.ASTC_4x4 ],
-			engineFormat: [ EngineFormat.RGBA_ASTC_4x4_Format, EngineFormat.RGBA_ASTC_4x4_Format ],
-			priorityETC1S: Infinity,
-			priorityUASTC: 1,
-			needsPowerOfTwo: false
-		}, {
-			if: 'bptcSupported',
-			basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.BC7_M5, TranscoderFormat.BC7_M5 ],
-			engineFormat: [ EngineFormat.RGBA_BPTC_Format, EngineFormat.RGBA_BPTC_Format ],
-			priorityETC1S: 3,
-			priorityUASTC: 2,
-			needsPowerOfTwo: false
-		}, {
-			if: 'dxtSupported',
-			basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.BC1, TranscoderFormat.BC3 ],
-			engineFormat: [ EngineFormat.RGB_S3TC_DXT1_Format, EngineFormat.RGBA_S3TC_DXT5_Format ],
-			priorityETC1S: 4,
-			priorityUASTC: 5,
-			needsPowerOfTwo: false
-		}, {
-			if: 'etc2Supported',
-			basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC2 ],
-			engineFormat: [ EngineFormat.RGB_ETC2_Format, EngineFormat.RGBA_ETC2_EAC_Format ],
-			priorityETC1S: 1,
-			priorityUASTC: 3,
-			needsPowerOfTwo: false
-		}, {
-			if: 'etc1Supported',
-			basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.ETC1, TranscoderFormat.ETC1 ],
-			engineFormat: [ EngineFormat.RGB_ETC1_Format, EngineFormat.RGB_ETC1_Format ],
-			priorityETC1S: 2,
-			priorityUASTC: 4,
-			needsPowerOfTwo: false
-		}, {
-			if: 'pvrtcSupported',
-			basisFormat: [ BasisFormat.ETC1S, BasisFormat.UASTC_4x4 ],
-			transcoderFormat: [ TranscoderFormat.PVRTC1_4_RGB, TranscoderFormat.PVRTC1_4_RGBA ],
-			engineFormat: [ EngineFormat.RGB_PVRTC_4BPPV1_Format, EngineFormat.RGBA_PVRTC_4BPPV1_Format ],
-			priorityETC1S: 5,
-			priorityUASTC: 6,
-			needsPowerOfTwo: true
-		} ];
-		const ETC1S_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
-
-			return a.priorityETC1S - b.priorityETC1S;
-
-		} );
-		const UASTC_OPTIONS = FORMAT_OPTIONS.sort( function ( a, b ) {
-
-			return a.priorityUASTC - b.priorityUASTC;
-
-		} );
-		function getTranscoderFormat( basisFormat, width, height, hasAlpha ) {
-
-			let transcoderFormat;
-			let engineFormat;
-			const options = basisFormat === BasisFormat.ETC1S ? ETC1S_OPTIONS : UASTC_OPTIONS;
-			for ( let i = 0; i < options.length; i ++ ) {
-
-				const opt = options[ i ];
-				if ( ! config[ opt.if ] ) continue;
-				if ( ! opt.basisFormat.includes( basisFormat ) ) continue;
-				if ( opt.needsPowerOfTwo && ! ( isPowerOfTwo( width ) && isPowerOfTwo( height ) ) ) continue;
-				transcoderFormat = opt.transcoderFormat[ hasAlpha ? 1 : 0 ];
-				engineFormat = opt.engineFormat[ hasAlpha ? 1 : 0 ];
-				return {
-					transcoderFormat,
-					engineFormat
-				};
-
-			}
-
-			console.warn( 'THREE.BasisTextureLoader: No suitable compressed texture format found. Decoding to RGBA32.' );
-			transcoderFormat = TranscoderFormat.RGBA32;
-			engineFormat = EngineFormat.RGBAFormat;
-			return {
-				transcoderFormat,
-				engineFormat
-			};
-
-		}
-
-		function assert( ok, message ) {
-
-			if ( ! ok ) throw new Error( message );
-
-		}
-
-		function getWidthInBlocks( transcoderFormat, width ) {
-
-			return Math.ceil( width / BasisModule.getFormatBlockWidth( transcoderFormat ) );
-
-		}
-
-		function getHeightInBlocks( transcoderFormat, height ) {
-
-			return Math.ceil( height / BasisModule.getFormatBlockHeight( transcoderFormat ) );
-
-		}
-
-		function getTranscodedImageByteLength( transcoderFormat, width, height ) {
-
-			const blockByteLength = BasisModule.getBytesPerBlockOrPixel( transcoderFormat );
-			if ( BasisModule.formatIsUncompressed( transcoderFormat ) ) {
-
-				return width * height * blockByteLength;
-
-			}
-
-			if ( transcoderFormat === TranscoderFormat.PVRTC1_4_RGB || transcoderFormat === TranscoderFormat.PVRTC1_4_RGBA ) {
-
-				// GL requires extra padding for very small textures:
-				// https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
-				const paddedWidth = width + 3 & ~ 3;
-				const paddedHeight = height + 3 & ~ 3;
-				return ( Math.max( 8, paddedWidth ) * Math.max( 8, paddedHeight ) * 4 + 7 ) / 8;
-
-			}
-
-			return getWidthInBlocks( transcoderFormat, width ) * getHeightInBlocks( transcoderFormat, height ) * blockByteLength;
-
-		}
-
-		function isPowerOfTwo( value ) {
-
-			if ( value <= 2 ) return true;
-			return ( value & value - 1 ) === 0 && value !== 0;
-
-		}
-
-	};
-
-	THREE.BasisTextureLoader = BasisTextureLoader;
-
-} )();

+ 0 - 3690
examples/js/loaders/ColladaLoader.js

@@ -1,3690 +0,0 @@
-( function () {
-
-	class ColladaLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const path = scope.path === '' ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( text ) {
-
-				try {
-
-					onLoad( scope.parse( text, path ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( text, path ) {
-
-			function getElementsByTagName( xml, name ) {
-
-				// Non recursive xml.getElementsByTagName() ...
-
-				const array = [];
-				const childNodes = xml.childNodes;
-				for ( let i = 0, l = childNodes.length; i < l; i ++ ) {
-
-					const child = childNodes[ i ];
-					if ( child.nodeName === name ) {
-
-						array.push( child );
-
-					}
-
-				}
-
-				return array;
-
-			}
-
-			function parseStrings( text ) {
-
-				if ( text.length === 0 ) return [];
-				const parts = text.trim().split( /\s+/ );
-				const array = new Array( parts.length );
-				for ( let i = 0, l = parts.length; i < l; i ++ ) {
-
-					array[ i ] = parts[ i ];
-
-				}
-
-				return array;
-
-			}
-
-			function parseFloats( text ) {
-
-				if ( text.length === 0 ) return [];
-				const parts = text.trim().split( /\s+/ );
-				const array = new Array( parts.length );
-				for ( let i = 0, l = parts.length; i < l; i ++ ) {
-
-					array[ i ] = parseFloat( parts[ i ] );
-
-				}
-
-				return array;
-
-			}
-
-			function parseInts( text ) {
-
-				if ( text.length === 0 ) return [];
-				const parts = text.trim().split( /\s+/ );
-				const array = new Array( parts.length );
-				for ( let i = 0, l = parts.length; i < l; i ++ ) {
-
-					array[ i ] = parseInt( parts[ i ] );
-
-				}
-
-				return array;
-
-			}
-
-			function parseId( text ) {
-
-				return text.substring( 1 );
-
-			}
-
-			function generateId() {
-
-				return 'three_default_' + count ++;
-
-			}
-
-			function isEmpty( object ) {
-
-				return Object.keys( object ).length === 0;
-
-			}
-
-			// asset
-
-			function parseAsset( xml ) {
-
-				return {
-					unit: parseAssetUnit( getElementsByTagName( xml, 'unit' )[ 0 ] ),
-					upAxis: parseAssetUpAxis( getElementsByTagName( xml, 'up_axis' )[ 0 ] )
-				};
-
-			}
-
-			function parseAssetUnit( xml ) {
-
-				if ( xml !== undefined && xml.hasAttribute( 'meter' ) === true ) {
-
-					return parseFloat( xml.getAttribute( 'meter' ) );
-
-				} else {
-
-					return 1; // default 1 meter
-
-				}
-
-			}
-
-			function parseAssetUpAxis( xml ) {
-
-				return xml !== undefined ? xml.textContent : 'Y_UP';
-
-			}
-
-			// library
-
-			function parseLibrary( xml, libraryName, nodeName, parser ) {
-
-				const library = getElementsByTagName( xml, libraryName )[ 0 ];
-				if ( library !== undefined ) {
-
-					const elements = getElementsByTagName( library, nodeName );
-					for ( let i = 0; i < elements.length; i ++ ) {
-
-						parser( elements[ i ] );
-
-					}
-
-				}
-
-			}
-
-			function buildLibrary( data, builder ) {
-
-				for ( const name in data ) {
-
-					const object = data[ name ];
-					object.build = builder( data[ name ] );
-
-				}
-
-			}
-
-			// get
-
-			function getBuild( data, builder ) {
-
-				if ( data.build !== undefined ) return data.build;
-				data.build = builder( data );
-				return data.build;
-
-			}
-
-			// animation
-
-			function parseAnimation( xml ) {
-
-				const data = {
-					sources: {},
-					samplers: {},
-					channels: {}
-				};
-				let hasChildren = false;
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					let id;
-					switch ( child.nodeName ) {
-
-						case 'source':
-							id = child.getAttribute( 'id' );
-							data.sources[ id ] = parseSource( child );
-							break;
-						case 'sampler':
-							id = child.getAttribute( 'id' );
-							data.samplers[ id ] = parseAnimationSampler( child );
-							break;
-						case 'channel':
-							id = child.getAttribute( 'target' );
-							data.channels[ id ] = parseAnimationChannel( child );
-							break;
-						case 'animation':
-							// hierarchy of related animations
-							parseAnimation( child );
-							hasChildren = true;
-							break;
-						default:
-							console.log( child );
-
-					}
-
-				}
-
-				if ( hasChildren === false ) {
-
-					// since 'id' attributes can be optional, it's necessary to generate a UUID for unqiue assignment
-
-					library.animations[ xml.getAttribute( 'id' ) || THREE.MathUtils.generateUUID() ] = data;
-
-				}
-
-			}
-
-			function parseAnimationSampler( xml ) {
-
-				const data = {
-					inputs: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'input':
-							const id = parseId( child.getAttribute( 'source' ) );
-							const semantic = child.getAttribute( 'semantic' );
-							data.inputs[ semantic ] = id;
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseAnimationChannel( xml ) {
-
-				const data = {};
-				const target = xml.getAttribute( 'target' );
-
-				// parsing SID Addressing Syntax
-
-				let parts = target.split( '/' );
-				const id = parts.shift();
-				let sid = parts.shift();
-
-				// check selection syntax
-
-				const arraySyntax = sid.indexOf( '(' ) !== - 1;
-				const memberSyntax = sid.indexOf( '.' ) !== - 1;
-				if ( memberSyntax ) {
-
-					//  member selection access
-
-					parts = sid.split( '.' );
-					sid = parts.shift();
-					data.member = parts.shift();
-
-				} else if ( arraySyntax ) {
-
-					// array-access syntax. can be used to express fields in one-dimensional vectors or two-dimensional matrices.
-
-					const indices = sid.split( '(' );
-					sid = indices.shift();
-					for ( let i = 0; i < indices.length; i ++ ) {
-
-						indices[ i ] = parseInt( indices[ i ].replace( /\)/, '' ) );
-
-					}
-
-					data.indices = indices;
-
-				}
-
-				data.id = id;
-				data.sid = sid;
-				data.arraySyntax = arraySyntax;
-				data.memberSyntax = memberSyntax;
-				data.sampler = parseId( xml.getAttribute( 'source' ) );
-				return data;
-
-			}
-
-			function buildAnimation( data ) {
-
-				const tracks = [];
-				const channels = data.channels;
-				const samplers = data.samplers;
-				const sources = data.sources;
-				for ( const target in channels ) {
-
-					if ( channels.hasOwnProperty( target ) ) {
-
-						const channel = channels[ target ];
-						const sampler = samplers[ channel.sampler ];
-						const inputId = sampler.inputs.INPUT;
-						const outputId = sampler.inputs.OUTPUT;
-						const inputSource = sources[ inputId ];
-						const outputSource = sources[ outputId ];
-						const animation = buildAnimationChannel( channel, inputSource, outputSource );
-						createKeyframeTracks( animation, tracks );
-
-					}
-
-				}
-
-				return tracks;
-
-			}
-
-			function getAnimation( id ) {
-
-				return getBuild( library.animations[ id ], buildAnimation );
-
-			}
-
-			function buildAnimationChannel( channel, inputSource, outputSource ) {
-
-				const node = library.nodes[ channel.id ];
-				const object3D = getNode( node.id );
-				const transform = node.transforms[ channel.sid ];
-				const defaultMatrix = node.matrix.clone().transpose();
-				let time, stride;
-				let i, il, j, jl;
-				const data = {};
-
-				// the collada spec allows the animation of data in various ways.
-				// depending on the transform type (matrix, translate, rotate, scale), we execute different logic
-
-				switch ( transform ) {
-
-					case 'matrix':
-						for ( i = 0, il = inputSource.array.length; i < il; i ++ ) {
-
-							time = inputSource.array[ i ];
-							stride = i * outputSource.stride;
-							if ( data[ time ] === undefined ) data[ time ] = {};
-							if ( channel.arraySyntax === true ) {
-
-								const value = outputSource.array[ stride ];
-								const index = channel.indices[ 0 ] + 4 * channel.indices[ 1 ];
-								data[ time ][ index ] = value;
-
-							} else {
-
-								for ( j = 0, jl = outputSource.stride; j < jl; j ++ ) {
-
-									data[ time ][ j ] = outputSource.array[ stride + j ];
-
-								}
-
-							}
-
-						}
-
-						break;
-					case 'translate':
-						console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform );
-						break;
-					case 'rotate':
-						console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform );
-						break;
-					case 'scale':
-						console.warn( 'THREE.ColladaLoader: Animation transform type "%s" not yet implemented.', transform );
-						break;
-
-				}
-
-				const keyframes = prepareAnimationData( data, defaultMatrix );
-				const animation = {
-					name: object3D.uuid,
-					keyframes: keyframes
-				};
-				return animation;
-
-			}
-
-			function prepareAnimationData( data, defaultMatrix ) {
-
-				const keyframes = [];
-
-				// transfer data into a sortable array
-
-				for ( const time in data ) {
-
-					keyframes.push( {
-						time: parseFloat( time ),
-						value: data[ time ]
-					} );
-
-				}
-
-				// ensure keyframes are sorted by time
-
-				keyframes.sort( ascending );
-
-				// now we clean up all animation data, so we can use them for keyframe tracks
-
-				for ( let i = 0; i < 16; i ++ ) {
-
-					transformAnimationData( keyframes, i, defaultMatrix.elements[ i ] );
-
-				}
-
-				return keyframes;
-
-				// array sort function
-
-				function ascending( a, b ) {
-
-					return a.time - b.time;
-
-				}
-
-			}
-
-			const position = new THREE.Vector3();
-			const scale = new THREE.Vector3();
-			const quaternion = new THREE.Quaternion();
-			function createKeyframeTracks( animation, tracks ) {
-
-				const keyframes = animation.keyframes;
-				const name = animation.name;
-				const times = [];
-				const positionData = [];
-				const quaternionData = [];
-				const scaleData = [];
-				for ( let i = 0, l = keyframes.length; i < l; i ++ ) {
-
-					const keyframe = keyframes[ i ];
-					const time = keyframe.time;
-					const value = keyframe.value;
-					matrix.fromArray( value ).transpose();
-					matrix.decompose( position, quaternion, scale );
-					times.push( time );
-					positionData.push( position.x, position.y, position.z );
-					quaternionData.push( quaternion.x, quaternion.y, quaternion.z, quaternion.w );
-					scaleData.push( scale.x, scale.y, scale.z );
-
-				}
-
-				if ( positionData.length > 0 ) tracks.push( new THREE.VectorKeyframeTrack( name + '.position', times, positionData ) );
-				if ( quaternionData.length > 0 ) tracks.push( new THREE.QuaternionKeyframeTrack( name + '.quaternion', times, quaternionData ) );
-				if ( scaleData.length > 0 ) tracks.push( new THREE.VectorKeyframeTrack( name + '.scale', times, scaleData ) );
-				return tracks;
-
-			}
-
-			function transformAnimationData( keyframes, property, defaultValue ) {
-
-				let keyframe;
-				let empty = true;
-				let i, l;
-
-				// check, if values of a property are missing in our keyframes
-
-				for ( i = 0, l = keyframes.length; i < l; i ++ ) {
-
-					keyframe = keyframes[ i ];
-					if ( keyframe.value[ property ] === undefined ) {
-
-						keyframe.value[ property ] = null; // mark as missing
-
-					} else {
-
-						empty = false;
-
-					}
-
-				}
-
-				if ( empty === true ) {
-
-					// no values at all, so we set a default value
-
-					for ( i = 0, l = keyframes.length; i < l; i ++ ) {
-
-						keyframe = keyframes[ i ];
-						keyframe.value[ property ] = defaultValue;
-
-					}
-
-				} else {
-
-					// filling gaps
-
-					createMissingKeyframes( keyframes, property );
-
-				}
-
-			}
-
-			function createMissingKeyframes( keyframes, property ) {
-
-				let prev, next;
-				for ( let i = 0, l = keyframes.length; i < l; i ++ ) {
-
-					const keyframe = keyframes[ i ];
-					if ( keyframe.value[ property ] === null ) {
-
-						prev = getPrev( keyframes, i, property );
-						next = getNext( keyframes, i, property );
-						if ( prev === null ) {
-
-							keyframe.value[ property ] = next.value[ property ];
-							continue;
-
-						}
-
-						if ( next === null ) {
-
-							keyframe.value[ property ] = prev.value[ property ];
-							continue;
-
-						}
-
-						interpolate( keyframe, prev, next, property );
-
-					}
-
-				}
-
-			}
-
-			function getPrev( keyframes, i, property ) {
-
-				while ( i >= 0 ) {
-
-					const keyframe = keyframes[ i ];
-					if ( keyframe.value[ property ] !== null ) return keyframe;
-					i --;
-
-				}
-
-				return null;
-
-			}
-
-			function getNext( keyframes, i, property ) {
-
-				while ( i < keyframes.length ) {
-
-					const keyframe = keyframes[ i ];
-					if ( keyframe.value[ property ] !== null ) return keyframe;
-					i ++;
-
-				}
-
-				return null;
-
-			}
-
-			function interpolate( key, prev, next, property ) {
-
-				if ( next.time - prev.time === 0 ) {
-
-					key.value[ property ] = prev.value[ property ];
-					return;
-
-				}
-
-				key.value[ property ] = ( key.time - prev.time ) * ( next.value[ property ] - prev.value[ property ] ) / ( next.time - prev.time ) + prev.value[ property ];
-
-			}
-
-			// animation clips
-
-			function parseAnimationClip( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'id' ) || 'default',
-					start: parseFloat( xml.getAttribute( 'start' ) || 0 ),
-					end: parseFloat( xml.getAttribute( 'end' ) || 0 ),
-					animations: []
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'instance_animation':
-							data.animations.push( parseId( child.getAttribute( 'url' ) ) );
-							break;
-
-					}
-
-				}
-
-				library.clips[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function buildAnimationClip( data ) {
-
-				const tracks = [];
-				const name = data.name;
-				const duration = data.end - data.start || - 1;
-				const animations = data.animations;
-				for ( let i = 0, il = animations.length; i < il; i ++ ) {
-
-					const animationTracks = getAnimation( animations[ i ] );
-					for ( let j = 0, jl = animationTracks.length; j < jl; j ++ ) {
-
-						tracks.push( animationTracks[ j ] );
-
-					}
-
-				}
-
-				return new THREE.AnimationClip( name, duration, tracks );
-
-			}
-
-			function getAnimationClip( id ) {
-
-				return getBuild( library.clips[ id ], buildAnimationClip );
-
-			}
-
-			// controller
-
-			function parseController( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'skin':
-							// there is exactly one skin per controller
-							data.id = parseId( child.getAttribute( 'source' ) );
-							data.skin = parseSkin( child );
-							break;
-						case 'morph':
-							data.id = parseId( child.getAttribute( 'source' ) );
-							console.warn( 'THREE.ColladaLoader: Morph target animation not supported yet.' );
-							break;
-
-					}
-
-				}
-
-				library.controllers[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parseSkin( xml ) {
-
-				const data = {
-					sources: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'bind_shape_matrix':
-							data.bindShapeMatrix = parseFloats( child.textContent );
-							break;
-						case 'source':
-							const id = child.getAttribute( 'id' );
-							data.sources[ id ] = parseSource( child );
-							break;
-						case 'joints':
-							data.joints = parseJoints( child );
-							break;
-						case 'vertex_weights':
-							data.vertexWeights = parseVertexWeights( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseJoints( xml ) {
-
-				const data = {
-					inputs: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'input':
-							const semantic = child.getAttribute( 'semantic' );
-							const id = parseId( child.getAttribute( 'source' ) );
-							data.inputs[ semantic ] = id;
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseVertexWeights( xml ) {
-
-				const data = {
-					inputs: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'input':
-							const semantic = child.getAttribute( 'semantic' );
-							const id = parseId( child.getAttribute( 'source' ) );
-							const offset = parseInt( child.getAttribute( 'offset' ) );
-							data.inputs[ semantic ] = {
-								id: id,
-								offset: offset
-							};
-							break;
-						case 'vcount':
-							data.vcount = parseInts( child.textContent );
-							break;
-						case 'v':
-							data.v = parseInts( child.textContent );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildController( data ) {
-
-				const build = {
-					id: data.id
-				};
-				const geometry = library.geometries[ build.id ];
-				if ( data.skin !== undefined ) {
-
-					build.skin = buildSkin( data.skin );
-
-					// we enhance the 'sources' property of the corresponding geometry with our skin data
-
-					geometry.sources.skinIndices = build.skin.indices;
-					geometry.sources.skinWeights = build.skin.weights;
-
-				}
-
-				return build;
-
-			}
-
-			function buildSkin( data ) {
-
-				const BONE_LIMIT = 4;
-				const build = {
-					joints: [],
-					// this must be an array to preserve the joint order
-					indices: {
-						array: [],
-						stride: BONE_LIMIT
-					},
-					weights: {
-						array: [],
-						stride: BONE_LIMIT
-					}
-				};
-				const sources = data.sources;
-				const vertexWeights = data.vertexWeights;
-				const vcount = vertexWeights.vcount;
-				const v = vertexWeights.v;
-				const jointOffset = vertexWeights.inputs.JOINT.offset;
-				const weightOffset = vertexWeights.inputs.WEIGHT.offset;
-				const jointSource = data.sources[ data.joints.inputs.JOINT ];
-				const inverseSource = data.sources[ data.joints.inputs.INV_BIND_MATRIX ];
-				const weights = sources[ vertexWeights.inputs.WEIGHT.id ].array;
-				let stride = 0;
-				let i, j, l;
-
-				// procces skin data for each vertex
-
-				for ( i = 0, l = vcount.length; i < l; i ++ ) {
-
-					const jointCount = vcount[ i ]; // this is the amount of joints that affect a single vertex
-					const vertexSkinData = [];
-					for ( j = 0; j < jointCount; j ++ ) {
-
-						const skinIndex = v[ stride + jointOffset ];
-						const weightId = v[ stride + weightOffset ];
-						const skinWeight = weights[ weightId ];
-						vertexSkinData.push( {
-							index: skinIndex,
-							weight: skinWeight
-						} );
-						stride += 2;
-
-					}
-
-					// we sort the joints in descending order based on the weights.
-					// this ensures, we only procced the most important joints of the vertex
-
-					vertexSkinData.sort( descending );
-
-					// now we provide for each vertex a set of four index and weight values.
-					// the order of the skin data matches the order of vertices
-
-					for ( j = 0; j < BONE_LIMIT; j ++ ) {
-
-						const d = vertexSkinData[ j ];
-						if ( d !== undefined ) {
-
-							build.indices.array.push( d.index );
-							build.weights.array.push( d.weight );
-
-						} else {
-
-							build.indices.array.push( 0 );
-							build.weights.array.push( 0 );
-
-						}
-
-					}
-
-				}
-
-				// setup bind matrix
-
-				if ( data.bindShapeMatrix ) {
-
-					build.bindMatrix = new THREE.Matrix4().fromArray( data.bindShapeMatrix ).transpose();
-
-				} else {
-
-					build.bindMatrix = new THREE.Matrix4().identity();
-
-				}
-
-				// process bones and inverse bind matrix data
-
-				for ( i = 0, l = jointSource.array.length; i < l; i ++ ) {
-
-					const name = jointSource.array[ i ];
-					const boneInverse = new THREE.Matrix4().fromArray( inverseSource.array, i * inverseSource.stride ).transpose();
-					build.joints.push( {
-						name: name,
-						boneInverse: boneInverse
-					} );
-
-				}
-
-				return build;
-
-				// array sort function
-
-				function descending( a, b ) {
-
-					return b.weight - a.weight;
-
-				}
-
-			}
-
-			function getController( id ) {
-
-				return getBuild( library.controllers[ id ], buildController );
-
-			}
-
-			// image
-
-			function parseImage( xml ) {
-
-				const data = {
-					init_from: getElementsByTagName( xml, 'init_from' )[ 0 ].textContent
-				};
-				library.images[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function buildImage( data ) {
-
-				if ( data.build !== undefined ) return data.build;
-				return data.init_from;
-
-			}
-
-			function getImage( id ) {
-
-				const data = library.images[ id ];
-				if ( data !== undefined ) {
-
-					return getBuild( data, buildImage );
-
-				}
-
-				console.warn( 'THREE.ColladaLoader: Couldn\'t find image with ID:', id );
-				return null;
-
-			}
-
-			// effect
-
-			function parseEffect( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'profile_COMMON':
-							data.profile = parseEffectProfileCOMMON( child );
-							break;
-
-					}
-
-				}
-
-				library.effects[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parseEffectProfileCOMMON( xml ) {
-
-				const data = {
-					surfaces: {},
-					samplers: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'newparam':
-							parseEffectNewparam( child, data );
-							break;
-						case 'technique':
-							data.technique = parseEffectTechnique( child );
-							break;
-						case 'extra':
-							data.extra = parseEffectExtra( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectNewparam( xml, data ) {
-
-				const sid = xml.getAttribute( 'sid' );
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'surface':
-							data.surfaces[ sid ] = parseEffectSurface( child );
-							break;
-						case 'sampler2D':
-							data.samplers[ sid ] = parseEffectSampler( child );
-							break;
-
-					}
-
-				}
-
-			}
-
-			function parseEffectSurface( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'init_from':
-							data.init_from = child.textContent;
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectSampler( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'source':
-							data.source = child.textContent;
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectTechnique( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'constant':
-						case 'lambert':
-						case 'blinn':
-						case 'phong':
-							data.type = child.nodeName;
-							data.parameters = parseEffectParameters( child );
-							break;
-						case 'extra':
-							data.extra = parseEffectExtra( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectParameters( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'emission':
-						case 'diffuse':
-						case 'specular':
-						case 'bump':
-						case 'ambient':
-						case 'shininess':
-						case 'transparency':
-							data[ child.nodeName ] = parseEffectParameter( child );
-							break;
-						case 'transparent':
-							data[ child.nodeName ] = {
-								opaque: child.hasAttribute( 'opaque' ) ? child.getAttribute( 'opaque' ) : 'A_ONE',
-								data: parseEffectParameter( child )
-							};
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectParameter( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'color':
-							data[ child.nodeName ] = parseFloats( child.textContent );
-							break;
-						case 'float':
-							data[ child.nodeName ] = parseFloat( child.textContent );
-							break;
-						case 'texture':
-							data[ child.nodeName ] = {
-								id: child.getAttribute( 'texture' ),
-								extra: parseEffectParameterTexture( child )
-							};
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectParameterTexture( xml ) {
-
-				const data = {
-					technique: {}
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'extra':
-							parseEffectParameterTextureExtra( child, data );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectParameterTextureExtra( xml, data ) {
-
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'technique':
-							parseEffectParameterTextureExtraTechnique( child, data );
-							break;
-
-					}
-
-				}
-
-			}
-
-			function parseEffectParameterTextureExtraTechnique( xml, data ) {
-
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'repeatU':
-						case 'repeatV':
-						case 'offsetU':
-						case 'offsetV':
-							data.technique[ child.nodeName ] = parseFloat( child.textContent );
-							break;
-						case 'wrapU':
-						case 'wrapV':
-							// some files have values for wrapU/wrapV which become NaN via parseInt
-
-							if ( child.textContent.toUpperCase() === 'TRUE' ) {
-
-								data.technique[ child.nodeName ] = 1;
-
-							} else if ( child.textContent.toUpperCase() === 'FALSE' ) {
-
-								data.technique[ child.nodeName ] = 0;
-
-							} else {
-
-								data.technique[ child.nodeName ] = parseInt( child.textContent );
-
-							}
-
-							break;
-						case 'bump':
-							data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
-							break;
-
-					}
-
-				}
-
-			}
-
-			function parseEffectExtra( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'technique':
-							data.technique = parseEffectExtraTechnique( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectExtraTechnique( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'double_sided':
-							data[ child.nodeName ] = parseInt( child.textContent );
-							break;
-						case 'bump':
-							data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseEffectExtraTechniqueBump( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'texture':
-							data[ child.nodeName ] = {
-								id: child.getAttribute( 'texture' ),
-								texcoord: child.getAttribute( 'texcoord' ),
-								extra: parseEffectParameterTexture( child )
-							};
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildEffect( data ) {
-
-				return data;
-
-			}
-
-			function getEffect( id ) {
-
-				return getBuild( library.effects[ id ], buildEffect );
-
-			}
-
-			// material
-
-			function parseMaterial( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' )
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'instance_effect':
-							data.url = parseId( child.getAttribute( 'url' ) );
-							break;
-
-					}
-
-				}
-
-				library.materials[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function getTextureLoader( image ) {
-
-				let loader;
-				let extension = image.slice( ( image.lastIndexOf( '.' ) - 1 >>> 0 ) + 2 ); // http://www.jstips.co/en/javascript/get-file-extension/
-				extension = extension.toLowerCase();
-				switch ( extension ) {
-
-					case 'tga':
-						loader = tgaLoader;
-						break;
-					default:
-						loader = textureLoader;
-
-				}
-
-				return loader;
-
-			}
-
-			function buildMaterial( data ) {
-
-				const effect = getEffect( data.url );
-				const technique = effect.profile.technique;
-				let material;
-				switch ( technique.type ) {
-
-					case 'phong':
-					case 'blinn':
-						material = new THREE.MeshPhongMaterial();
-						break;
-					case 'lambert':
-						material = new THREE.MeshLambertMaterial();
-						break;
-					default:
-						material = new THREE.MeshBasicMaterial();
-						break;
-
-				}
-
-				material.name = data.name || '';
-				function getTexture( textureObject, encoding = null ) {
-
-					const sampler = effect.profile.samplers[ textureObject.id ];
-					let image = null;
-
-					// get image
-
-					if ( sampler !== undefined ) {
-
-						const surface = effect.profile.surfaces[ sampler.source ];
-						image = getImage( surface.init_from );
-
-					} else {
-
-						console.warn( 'THREE.ColladaLoader: Undefined sampler. Access image directly (see #12530).' );
-						image = getImage( textureObject.id );
-
-					}
-
-					// create texture if image is avaiable
-
-					if ( image !== null ) {
-
-						const loader = getTextureLoader( image );
-						if ( loader !== undefined ) {
-
-							const texture = loader.load( image );
-							const extra = textureObject.extra;
-							if ( extra !== undefined && extra.technique !== undefined && isEmpty( extra.technique ) === false ) {
-
-								const technique = extra.technique;
-								texture.wrapS = technique.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-								texture.wrapT = technique.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-								texture.offset.set( technique.offsetU || 0, technique.offsetV || 0 );
-								texture.repeat.set( technique.repeatU || 1, technique.repeatV || 1 );
-
-							} else {
-
-								texture.wrapS = THREE.RepeatWrapping;
-								texture.wrapT = THREE.RepeatWrapping;
-
-							}
-
-							if ( encoding !== null ) {
-
-								texture.encoding = encoding;
-
-							}
-
-							return texture;
-
-						} else {
-
-							console.warn( 'THREE.ColladaLoader: THREE.Loader for texture %s not found.', image );
-							return null;
-
-						}
-
-					} else {
-
-						console.warn( 'THREE.ColladaLoader: Couldn\'t create texture with ID:', textureObject.id );
-						return null;
-
-					}
-
-				}
-
-				const parameters = technique.parameters;
-				for ( const key in parameters ) {
-
-					const parameter = parameters[ key ];
-					switch ( key ) {
-
-						case 'diffuse':
-							if ( parameter.color ) material.color.fromArray( parameter.color );
-							if ( parameter.texture ) material.map = getTexture( parameter.texture, THREE.sRGBEncoding );
-							break;
-						case 'specular':
-							if ( parameter.color && material.specular ) material.specular.fromArray( parameter.color );
-							if ( parameter.texture ) material.specularMap = getTexture( parameter.texture );
-							break;
-						case 'bump':
-							if ( parameter.texture ) material.normalMap = getTexture( parameter.texture );
-							break;
-						case 'ambient':
-							if ( parameter.texture ) material.lightMap = getTexture( parameter.texture, THREE.sRGBEncoding );
-							break;
-						case 'shininess':
-							if ( parameter.float && material.shininess ) material.shininess = parameter.float;
-							break;
-						case 'emission':
-							if ( parameter.color && material.emissive ) material.emissive.fromArray( parameter.color );
-							if ( parameter.texture ) material.emissiveMap = getTexture( parameter.texture, THREE.sRGBEncoding );
-							break;
-
-					}
-
-				}
-
-				material.color.convertSRGBToLinear();
-				if ( material.specular ) material.specular.convertSRGBToLinear();
-				if ( material.emissive ) material.emissive.convertSRGBToLinear();
-
-				//
-
-				let transparent = parameters[ 'transparent' ];
-				let transparency = parameters[ 'transparency' ];
-
-				// <transparency> does not exist but <transparent>
-
-				if ( transparency === undefined && transparent ) {
-
-					transparency = {
-						float: 1
-					};
-
-				}
-
-				// <transparent> does not exist but <transparency>
-
-				if ( transparent === undefined && transparency ) {
-
-					transparent = {
-						opaque: 'A_ONE',
-						data: {
-							color: [ 1, 1, 1, 1 ]
-						}
-					};
-
-				}
-
-				if ( transparent && transparency ) {
-
-					// handle case if a texture exists but no color
-
-					if ( transparent.data.texture ) {
-
-						// we do not set an alpha map (see #13792)
-
-						material.transparent = true;
-
-					} else {
-
-						const color = transparent.data.color;
-						switch ( transparent.opaque ) {
-
-							case 'A_ONE':
-								material.opacity = color[ 3 ] * transparency.float;
-								break;
-							case 'RGB_ZERO':
-								material.opacity = 1 - color[ 0 ] * transparency.float;
-								break;
-							case 'A_ZERO':
-								material.opacity = 1 - color[ 3 ] * transparency.float;
-								break;
-							case 'RGB_ONE':
-								material.opacity = color[ 0 ] * transparency.float;
-								break;
-							default:
-								console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque );
-
-						}
-
-						if ( material.opacity < 1 ) material.transparent = true;
-
-					}
-
-				}
-
-				//
-
-				if ( technique.extra !== undefined && technique.extra.technique !== undefined ) {
-
-					const techniques = technique.extra.technique;
-					for ( const k in techniques ) {
-
-						const v = techniques[ k ];
-						switch ( k ) {
-
-							case 'double_sided':
-								material.side = v === 1 ? THREE.DoubleSide : THREE.FrontSide;
-								break;
-							case 'bump':
-								material.normalMap = getTexture( v.texture );
-								material.normalScale = new THREE.Vector2( 1, 1 );
-								break;
-
-						}
-
-					}
-
-				}
-
-				return material;
-
-			}
-
-			function getMaterial( id ) {
-
-				return getBuild( library.materials[ id ], buildMaterial );
-
-			}
-
-			// camera
-
-			function parseCamera( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' )
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'optics':
-							data.optics = parseCameraOptics( child );
-							break;
-
-					}
-
-				}
-
-				library.cameras[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parseCameraOptics( xml ) {
-
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					switch ( child.nodeName ) {
-
-						case 'technique_common':
-							return parseCameraTechnique( child );
-
-					}
-
-				}
-
-				return {};
-
-			}
-
-			function parseCameraTechnique( xml ) {
-
-				const data = {};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					switch ( child.nodeName ) {
-
-						case 'perspective':
-						case 'orthographic':
-							data.technique = child.nodeName;
-							data.parameters = parseCameraParameters( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseCameraParameters( xml ) {
-
-				const data = {};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					switch ( child.nodeName ) {
-
-						case 'xfov':
-						case 'yfov':
-						case 'xmag':
-						case 'ymag':
-						case 'znear':
-						case 'zfar':
-						case 'aspect_ratio':
-							data[ child.nodeName ] = parseFloat( child.textContent );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildCamera( data ) {
-
-				let camera;
-				switch ( data.optics.technique ) {
-
-					case 'perspective':
-						camera = new THREE.PerspectiveCamera( data.optics.parameters.yfov, data.optics.parameters.aspect_ratio, data.optics.parameters.znear, data.optics.parameters.zfar );
-						break;
-					case 'orthographic':
-						let ymag = data.optics.parameters.ymag;
-						let xmag = data.optics.parameters.xmag;
-						const aspectRatio = data.optics.parameters.aspect_ratio;
-						xmag = xmag === undefined ? ymag * aspectRatio : xmag;
-						ymag = ymag === undefined ? xmag / aspectRatio : ymag;
-						xmag *= 0.5;
-						ymag *= 0.5;
-						camera = new THREE.OrthographicCamera( - xmag, xmag, ymag, - ymag,
-							// left, right, top, bottom
-							data.optics.parameters.znear, data.optics.parameters.zfar );
-						break;
-					default:
-						camera = new THREE.PerspectiveCamera();
-						break;
-
-				}
-
-				camera.name = data.name || '';
-				return camera;
-
-			}
-
-			function getCamera( id ) {
-
-				const data = library.cameras[ id ];
-				if ( data !== undefined ) {
-
-					return getBuild( data, buildCamera );
-
-				}
-
-				console.warn( 'THREE.ColladaLoader: Couldn\'t find camera with ID:', id );
-				return null;
-
-			}
-
-			// light
-
-			function parseLight( xml ) {
-
-				let data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'technique_common':
-							data = parseLightTechnique( child );
-							break;
-
-					}
-
-				}
-
-				library.lights[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parseLightTechnique( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'directional':
-						case 'point':
-						case 'spot':
-						case 'ambient':
-							data.technique = child.nodeName;
-							data.parameters = parseLightParameters( child );
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseLightParameters( xml ) {
-
-				const data = {};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'color':
-							const array = parseFloats( child.textContent );
-							data.color = new THREE.Color().fromArray( array ).convertSRGBToLinear();
-							break;
-						case 'falloff_angle':
-							data.falloffAngle = parseFloat( child.textContent );
-							break;
-						case 'quadratic_attenuation':
-							const f = parseFloat( child.textContent );
-							data.distance = f ? Math.sqrt( 1 / f ) : 0;
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildLight( data ) {
-
-				let light;
-				switch ( data.technique ) {
-
-					case 'directional':
-						light = new THREE.DirectionalLight();
-						break;
-					case 'point':
-						light = new THREE.PointLight();
-						break;
-					case 'spot':
-						light = new THREE.SpotLight();
-						break;
-					case 'ambient':
-						light = new THREE.AmbientLight();
-						break;
-
-				}
-
-				if ( data.parameters.color ) light.color.copy( data.parameters.color );
-				if ( data.parameters.distance ) light.distance = data.parameters.distance;
-				return light;
-
-			}
-
-			function getLight( id ) {
-
-				const data = library.lights[ id ];
-				if ( data !== undefined ) {
-
-					return getBuild( data, buildLight );
-
-				}
-
-				console.warn( 'THREE.ColladaLoader: Couldn\'t find light with ID:', id );
-				return null;
-
-			}
-
-			// geometry
-
-			function parseGeometry( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' ),
-					sources: {},
-					vertices: {},
-					primitives: []
-				};
-				const mesh = getElementsByTagName( xml, 'mesh' )[ 0 ];
-
-				// the following tags inside geometry are not supported yet (see https://github.com/mrdoob/three.js/pull/12606): convex_mesh, spline, brep
-				if ( mesh === undefined ) return;
-				for ( let i = 0; i < mesh.childNodes.length; i ++ ) {
-
-					const child = mesh.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					const id = child.getAttribute( 'id' );
-					switch ( child.nodeName ) {
-
-						case 'source':
-							data.sources[ id ] = parseSource( child );
-							break;
-						case 'vertices':
-							// data.sources[ id ] = data.sources[ parseId( getElementsByTagName( child, 'input' )[ 0 ].getAttribute( 'source' ) ) ];
-							data.vertices = parseGeometryVertices( child );
-							break;
-						case 'polygons':
-							console.warn( 'THREE.ColladaLoader: Unsupported primitive type: ', child.nodeName );
-							break;
-						case 'lines':
-						case 'linestrips':
-						case 'polylist':
-						case 'triangles':
-							data.primitives.push( parseGeometryPrimitive( child ) );
-							break;
-						default:
-							console.log( child );
-
-					}
-
-				}
-
-				library.geometries[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parseSource( xml ) {
-
-				const data = {
-					array: [],
-					stride: 3
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'float_array':
-							data.array = parseFloats( child.textContent );
-							break;
-						case 'Name_array':
-							data.array = parseStrings( child.textContent );
-							break;
-						case 'technique_common':
-							const accessor = getElementsByTagName( child, 'accessor' )[ 0 ];
-							if ( accessor !== undefined ) {
-
-								data.stride = parseInt( accessor.getAttribute( 'stride' ) );
-
-							}
-
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseGeometryVertices( xml ) {
-
-				const data = {};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					data[ child.getAttribute( 'semantic' ) ] = parseId( child.getAttribute( 'source' ) );
-
-				}
-
-				return data;
-
-			}
-
-			function parseGeometryPrimitive( xml ) {
-
-				const primitive = {
-					type: xml.nodeName,
-					material: xml.getAttribute( 'material' ),
-					count: parseInt( xml.getAttribute( 'count' ) ),
-					inputs: {},
-					stride: 0,
-					hasUV: false
-				};
-				for ( let i = 0, l = xml.childNodes.length; i < l; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'input':
-							const id = parseId( child.getAttribute( 'source' ) );
-							const semantic = child.getAttribute( 'semantic' );
-							const offset = parseInt( child.getAttribute( 'offset' ) );
-							const set = parseInt( child.getAttribute( 'set' ) );
-							const inputname = set > 0 ? semantic + set : semantic;
-							primitive.inputs[ inputname ] = {
-								id: id,
-								offset: offset
-							};
-							primitive.stride = Math.max( primitive.stride, offset + 1 );
-							if ( semantic === 'TEXCOORD' ) primitive.hasUV = true;
-							break;
-						case 'vcount':
-							primitive.vcount = parseInts( child.textContent );
-							break;
-						case 'p':
-							primitive.p = parseInts( child.textContent );
-							break;
-
-					}
-
-				}
-
-				return primitive;
-
-			}
-
-			function groupPrimitives( primitives ) {
-
-				const build = {};
-				for ( let i = 0; i < primitives.length; i ++ ) {
-
-					const primitive = primitives[ i ];
-					if ( build[ primitive.type ] === undefined ) build[ primitive.type ] = [];
-					build[ primitive.type ].push( primitive );
-
-				}
-
-				return build;
-
-			}
-
-			function checkUVCoordinates( primitives ) {
-
-				let count = 0;
-				for ( let i = 0, l = primitives.length; i < l; i ++ ) {
-
-					const primitive = primitives[ i ];
-					if ( primitive.hasUV === true ) {
-
-						count ++;
-
-					}
-
-				}
-
-				if ( count > 0 && count < primitives.length ) {
-
-					primitives.uvsNeedsFix = true;
-
-				}
-
-			}
-
-			function buildGeometry( data ) {
-
-				const build = {};
-				const sources = data.sources;
-				const vertices = data.vertices;
-				const primitives = data.primitives;
-				if ( primitives.length === 0 ) return {};
-
-				// our goal is to create one buffer geometry for a single type of primitives
-				// first, we group all primitives by their type
-
-				const groupedPrimitives = groupPrimitives( primitives );
-				for ( const type in groupedPrimitives ) {
-
-					const primitiveType = groupedPrimitives[ type ];
-
-					// second, ensure consistent uv coordinates for each type of primitives (polylist,triangles or lines)
-
-					checkUVCoordinates( primitiveType );
-
-					// third, create a buffer geometry for each type of primitives
-
-					build[ type ] = buildGeometryType( primitiveType, sources, vertices );
-
-				}
-
-				return build;
-
-			}
-
-			function buildGeometryType( primitives, sources, vertices ) {
-
-				const build = {};
-				const position = {
-					array: [],
-					stride: 0
-				};
-				const normal = {
-					array: [],
-					stride: 0
-				};
-				const uv = {
-					array: [],
-					stride: 0
-				};
-				const uv2 = {
-					array: [],
-					stride: 0
-				};
-				const color = {
-					array: [],
-					stride: 0
-				};
-				const skinIndex = {
-					array: [],
-					stride: 4
-				};
-				const skinWeight = {
-					array: [],
-					stride: 4
-				};
-				const geometry = new THREE.BufferGeometry();
-				const materialKeys = [];
-				let start = 0;
-				for ( let p = 0; p < primitives.length; p ++ ) {
-
-					const primitive = primitives[ p ];
-					const inputs = primitive.inputs;
-
-					// groups
-
-					let count = 0;
-					switch ( primitive.type ) {
-
-						case 'lines':
-						case 'linestrips':
-							count = primitive.count * 2;
-							break;
-						case 'triangles':
-							count = primitive.count * 3;
-							break;
-						case 'polylist':
-							for ( let g = 0; g < primitive.count; g ++ ) {
-
-								const vc = primitive.vcount[ g ];
-								switch ( vc ) {
-
-									case 3:
-										count += 3; // single triangle
-										break;
-									case 4:
-										count += 6; // quad, subdivided into two triangles
-										break;
-									default:
-										count += ( vc - 2 ) * 3; // polylist with more than four vertices
-										break;
-
-								}
-
-							}
-
-							break;
-						default:
-							console.warn( 'THREE.ColladaLoader: Unknow primitive type:', primitive.type );
-
-					}
-
-					geometry.addGroup( start, count, p );
-					start += count;
-
-					// material
-
-					if ( primitive.material ) {
-
-						materialKeys.push( primitive.material );
-
-					}
-
-					// geometry data
-
-					for ( const name in inputs ) {
-
-						const input = inputs[ name ];
-						switch ( name ) {
-
-							case 'VERTEX':
-								for ( const key in vertices ) {
-
-									const id = vertices[ key ];
-									switch ( key ) {
-
-										case 'POSITION':
-											const prevLength = position.array.length;
-											buildGeometryData( primitive, sources[ id ], input.offset, position.array );
-											position.stride = sources[ id ].stride;
-											if ( sources.skinWeights && sources.skinIndices ) {
-
-												buildGeometryData( primitive, sources.skinIndices, input.offset, skinIndex.array );
-												buildGeometryData( primitive, sources.skinWeights, input.offset, skinWeight.array );
-
-											}
-
-											// see #3803
-
-											if ( primitive.hasUV === false && primitives.uvsNeedsFix === true ) {
-
-												const count = ( position.array.length - prevLength ) / position.stride;
-												for ( let i = 0; i < count; i ++ ) {
-
-													// fill missing uv coordinates
-
-													uv.array.push( 0, 0 );
-
-												}
-
-											}
-
-											break;
-										case 'NORMAL':
-											buildGeometryData( primitive, sources[ id ], input.offset, normal.array );
-											normal.stride = sources[ id ].stride;
-											break;
-										case 'COLOR':
-											buildGeometryData( primitive, sources[ id ], input.offset, color.array );
-											color.stride = sources[ id ].stride;
-											break;
-										case 'TEXCOORD':
-											buildGeometryData( primitive, sources[ id ], input.offset, uv.array );
-											uv.stride = sources[ id ].stride;
-											break;
-										case 'TEXCOORD1':
-											buildGeometryData( primitive, sources[ id ], input.offset, uv2.array );
-											uv.stride = sources[ id ].stride;
-											break;
-										default:
-											console.warn( 'THREE.ColladaLoader: Semantic "%s" not handled in geometry build process.', key );
-
-									}
-
-								}
-
-								break;
-							case 'NORMAL':
-								buildGeometryData( primitive, sources[ input.id ], input.offset, normal.array );
-								normal.stride = sources[ input.id ].stride;
-								break;
-							case 'COLOR':
-								buildGeometryData( primitive, sources[ input.id ], input.offset, color.array, true );
-								color.stride = sources[ input.id ].stride;
-								break;
-							case 'TEXCOORD':
-								buildGeometryData( primitive, sources[ input.id ], input.offset, uv.array );
-								uv.stride = sources[ input.id ].stride;
-								break;
-							case 'TEXCOORD1':
-								buildGeometryData( primitive, sources[ input.id ], input.offset, uv2.array );
-								uv2.stride = sources[ input.id ].stride;
-								break;
-
-						}
-
-					}
-
-				}
-
-				// build geometry
-
-				if ( position.array.length > 0 ) geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( position.array, position.stride ) );
-				if ( normal.array.length > 0 ) geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normal.array, normal.stride ) );
-				if ( color.array.length > 0 ) geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( color.array, color.stride ) );
-				if ( uv.array.length > 0 ) geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uv.array, uv.stride ) );
-				if ( uv2.array.length > 0 ) geometry.setAttribute( 'uv2', new THREE.Float32BufferAttribute( uv2.array, uv2.stride ) );
-				if ( skinIndex.array.length > 0 ) geometry.setAttribute( 'skinIndex', new THREE.Float32BufferAttribute( skinIndex.array, skinIndex.stride ) );
-				if ( skinWeight.array.length > 0 ) geometry.setAttribute( 'skinWeight', new THREE.Float32BufferAttribute( skinWeight.array, skinWeight.stride ) );
-				build.data = geometry;
-				build.type = primitives[ 0 ].type;
-				build.materialKeys = materialKeys;
-				return build;
-
-			}
-
-			function buildGeometryData( primitive, source, offset, array, isColor = false ) {
-
-				const indices = primitive.p;
-				const stride = primitive.stride;
-				const vcount = primitive.vcount;
-				function pushVector( i ) {
-
-					let index = indices[ i + offset ] * sourceStride;
-					const length = index + sourceStride;
-					for ( ; index < length; index ++ ) {
-
-						array.push( sourceArray[ index ] );
-
-					}
-
-					if ( isColor ) {
-
-						// convert the vertex colors from srgb to linear if present
-						const startIndex = array.length - sourceStride - 1;
-						tempColor.setRGB( array[ startIndex + 0 ], array[ startIndex + 1 ], array[ startIndex + 2 ] ).convertSRGBToLinear();
-						array[ startIndex + 0 ] = tempColor.r;
-						array[ startIndex + 1 ] = tempColor.g;
-						array[ startIndex + 2 ] = tempColor.b;
-
-					}
-
-				}
-
-				const sourceArray = source.array;
-				const sourceStride = source.stride;
-				if ( primitive.vcount !== undefined ) {
-
-					let index = 0;
-					for ( let i = 0, l = vcount.length; i < l; i ++ ) {
-
-						const count = vcount[ i ];
-						if ( count === 4 ) {
-
-							const a = index + stride * 0;
-							const b = index + stride * 1;
-							const c = index + stride * 2;
-							const d = index + stride * 3;
-							pushVector( a );
-							pushVector( b );
-							pushVector( d );
-							pushVector( b );
-							pushVector( c );
-							pushVector( d );
-
-						} else if ( count === 3 ) {
-
-							const a = index + stride * 0;
-							const b = index + stride * 1;
-							const c = index + stride * 2;
-							pushVector( a );
-							pushVector( b );
-							pushVector( c );
-
-						} else if ( count > 4 ) {
-
-							for ( let k = 1, kl = count - 2; k <= kl; k ++ ) {
-
-								const a = index + stride * 0;
-								const b = index + stride * k;
-								const c = index + stride * ( k + 1 );
-								pushVector( a );
-								pushVector( b );
-								pushVector( c );
-
-							}
-
-						}
-
-						index += stride * count;
-
-					}
-
-				} else {
-
-					for ( let i = 0, l = indices.length; i < l; i += stride ) {
-
-						pushVector( i );
-
-					}
-
-				}
-
-			}
-
-			function getGeometry( id ) {
-
-				return getBuild( library.geometries[ id ], buildGeometry );
-
-			}
-
-			// kinematics
-
-			function parseKinematicsModel( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' ) || '',
-					joints: {},
-					links: []
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'technique_common':
-							parseKinematicsTechniqueCommon( child, data );
-							break;
-
-					}
-
-				}
-
-				library.kinematicsModels[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function buildKinematicsModel( data ) {
-
-				if ( data.build !== undefined ) return data.build;
-				return data;
-
-			}
-
-			function getKinematicsModel( id ) {
-
-				return getBuild( library.kinematicsModels[ id ], buildKinematicsModel );
-
-			}
-
-			function parseKinematicsTechniqueCommon( xml, data ) {
-
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'joint':
-							data.joints[ child.getAttribute( 'sid' ) ] = parseKinematicsJoint( child );
-							break;
-						case 'link':
-							data.links.push( parseKinematicsLink( child ) );
-							break;
-
-					}
-
-				}
-
-			}
-
-			function parseKinematicsJoint( xml ) {
-
-				let data;
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'prismatic':
-						case 'revolute':
-							data = parseKinematicsJointParameter( child );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseKinematicsJointParameter( xml ) {
-
-				const data = {
-					sid: xml.getAttribute( 'sid' ),
-					name: xml.getAttribute( 'name' ) || '',
-					axis: new THREE.Vector3(),
-					limits: {
-						min: 0,
-						max: 0
-					},
-					type: xml.nodeName,
-					static: false,
-					zeroPosition: 0,
-					middlePosition: 0
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'axis':
-							const array = parseFloats( child.textContent );
-							data.axis.fromArray( array );
-							break;
-						case 'limits':
-							const max = child.getElementsByTagName( 'max' )[ 0 ];
-							const min = child.getElementsByTagName( 'min' )[ 0 ];
-							data.limits.max = parseFloat( max.textContent );
-							data.limits.min = parseFloat( min.textContent );
-							break;
-
-					}
-
-				}
-
-				// if min is equal to or greater than max, consider the joint static
-
-				if ( data.limits.min >= data.limits.max ) {
-
-					data.static = true;
-
-				}
-
-				// calculate middle position
-
-				data.middlePosition = ( data.limits.min + data.limits.max ) / 2.0;
-				return data;
-
-			}
-
-			function parseKinematicsLink( xml ) {
-
-				const data = {
-					sid: xml.getAttribute( 'sid' ),
-					name: xml.getAttribute( 'name' ) || '',
-					attachments: [],
-					transforms: []
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'attachment_full':
-							data.attachments.push( parseKinematicsAttachment( child ) );
-							break;
-						case 'matrix':
-						case 'translate':
-						case 'rotate':
-							data.transforms.push( parseKinematicsTransform( child ) );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseKinematicsAttachment( xml ) {
-
-				const data = {
-					joint: xml.getAttribute( 'joint' ).split( '/' ).pop(),
-					transforms: [],
-					links: []
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'link':
-							data.links.push( parseKinematicsLink( child ) );
-							break;
-						case 'matrix':
-						case 'translate':
-						case 'rotate':
-							data.transforms.push( parseKinematicsTransform( child ) );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function parseKinematicsTransform( xml ) {
-
-				const data = {
-					type: xml.nodeName
-				};
-				const array = parseFloats( xml.textContent );
-				switch ( data.type ) {
-
-					case 'matrix':
-						data.obj = new THREE.Matrix4();
-						data.obj.fromArray( array ).transpose();
-						break;
-					case 'translate':
-						data.obj = new THREE.Vector3();
-						data.obj.fromArray( array );
-						break;
-					case 'rotate':
-						data.obj = new THREE.Vector3();
-						data.obj.fromArray( array );
-						data.angle = THREE.MathUtils.degToRad( array[ 3 ] );
-						break;
-
-				}
-
-				return data;
-
-			}
-
-			// physics
-
-			function parsePhysicsModel( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' ) || '',
-					rigidBodies: {}
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'rigid_body':
-							data.rigidBodies[ child.getAttribute( 'name' ) ] = {};
-							parsePhysicsRigidBody( child, data.rigidBodies[ child.getAttribute( 'name' ) ] );
-							break;
-
-					}
-
-				}
-
-				library.physicsModels[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function parsePhysicsRigidBody( xml, data ) {
-
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'technique_common':
-							parsePhysicsTechniqueCommon( child, data );
-							break;
-
-					}
-
-				}
-
-			}
-
-			function parsePhysicsTechniqueCommon( xml, data ) {
-
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'inertia':
-							data.inertia = parseFloats( child.textContent );
-							break;
-						case 'mass':
-							data.mass = parseFloats( child.textContent )[ 0 ];
-							break;
-
-					}
-
-				}
-
-			}
-
-			// scene
-
-			function parseKinematicsScene( xml ) {
-
-				const data = {
-					bindJointAxis: []
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'bind_joint_axis':
-							data.bindJointAxis.push( parseKinematicsBindJointAxis( child ) );
-							break;
-
-					}
-
-				}
-
-				library.kinematicsScenes[ parseId( xml.getAttribute( 'url' ) ) ] = data;
-
-			}
-
-			function parseKinematicsBindJointAxis( xml ) {
-
-				const data = {
-					target: xml.getAttribute( 'target' ).split( '/' ).pop()
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					switch ( child.nodeName ) {
-
-						case 'axis':
-							const param = child.getElementsByTagName( 'param' )[ 0 ];
-							data.axis = param.textContent;
-							const tmpJointIndex = data.axis.split( 'inst_' ).pop().split( 'axis' )[ 0 ];
-							data.jointIndex = tmpJointIndex.substring( 0, tmpJointIndex.length - 1 );
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildKinematicsScene( data ) {
-
-				if ( data.build !== undefined ) return data.build;
-				return data;
-
-			}
-
-			function getKinematicsScene( id ) {
-
-				return getBuild( library.kinematicsScenes[ id ], buildKinematicsScene );
-
-			}
-
-			function setupKinematics() {
-
-				const kinematicsModelId = Object.keys( library.kinematicsModels )[ 0 ];
-				const kinematicsSceneId = Object.keys( library.kinematicsScenes )[ 0 ];
-				const visualSceneId = Object.keys( library.visualScenes )[ 0 ];
-				if ( kinematicsModelId === undefined || kinematicsSceneId === undefined ) return;
-				const kinematicsModel = getKinematicsModel( kinematicsModelId );
-				const kinematicsScene = getKinematicsScene( kinematicsSceneId );
-				const visualScene = getVisualScene( visualSceneId );
-				const bindJointAxis = kinematicsScene.bindJointAxis;
-				const jointMap = {};
-				for ( let i = 0, l = bindJointAxis.length; i < l; i ++ ) {
-
-					const axis = bindJointAxis[ i ];
-
-					// the result of the following query is an element of type 'translate', 'rotate','scale' or 'matrix'
-
-					const targetElement = collada.querySelector( '[sid="' + axis.target + '"]' );
-					if ( targetElement ) {
-
-						// get the parent of the transform element
-
-						const parentVisualElement = targetElement.parentElement;
-
-						// connect the joint of the kinematics model with the element in the visual scene
-
-						connect( axis.jointIndex, parentVisualElement );
-
-					}
-
-				}
-
-				function connect( jointIndex, visualElement ) {
-
-					const visualElementName = visualElement.getAttribute( 'name' );
-					const joint = kinematicsModel.joints[ jointIndex ];
-					visualScene.traverse( function ( object ) {
-
-						if ( object.name === visualElementName ) {
-
-							jointMap[ jointIndex ] = {
-								object: object,
-								transforms: buildTransformList( visualElement ),
-								joint: joint,
-								position: joint.zeroPosition
-							};
-
-						}
-
-					} );
-
-				}
-
-				const m0 = new THREE.Matrix4();
-				kinematics = {
-					joints: kinematicsModel && kinematicsModel.joints,
-					getJointValue: function ( jointIndex ) {
-
-						const jointData = jointMap[ jointIndex ];
-						if ( jointData ) {
-
-							return jointData.position;
-
-						} else {
-
-							console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' doesn\'t exist.' );
-
-						}
-
-					},
-					setJointValue: function ( jointIndex, value ) {
-
-						const jointData = jointMap[ jointIndex ];
-						if ( jointData ) {
-
-							const joint = jointData.joint;
-							if ( value > joint.limits.max || value < joint.limits.min ) {
-
-								console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ').' );
-
-							} else if ( joint.static ) {
-
-								console.warn( 'THREE.ColladaLoader: Joint ' + jointIndex + ' is static.' );
-
-							} else {
-
-								const object = jointData.object;
-								const axis = joint.axis;
-								const transforms = jointData.transforms;
-								matrix.identity();
-
-								// each update, we have to apply all transforms in the correct order
-
-								for ( let i = 0; i < transforms.length; i ++ ) {
-
-									const transform = transforms[ i ];
-
-									// if there is a connection of the transform node with a joint, apply the joint value
-
-									if ( transform.sid && transform.sid.indexOf( jointIndex ) !== - 1 ) {
-
-										switch ( joint.type ) {
-
-											case 'revolute':
-												matrix.multiply( m0.makeRotationAxis( axis, THREE.MathUtils.degToRad( value ) ) );
-												break;
-											case 'prismatic':
-												matrix.multiply( m0.makeTranslation( axis.x * value, axis.y * value, axis.z * value ) );
-												break;
-											default:
-												console.warn( 'THREE.ColladaLoader: Unknown joint type: ' + joint.type );
-												break;
-
-										}
-
-									} else {
-
-										switch ( transform.type ) {
-
-											case 'matrix':
-												matrix.multiply( transform.obj );
-												break;
-											case 'translate':
-												matrix.multiply( m0.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) );
-												break;
-											case 'scale':
-												matrix.scale( transform.obj );
-												break;
-											case 'rotate':
-												matrix.multiply( m0.makeRotationAxis( transform.obj, transform.angle ) );
-												break;
-
-										}
-
-									}
-
-								}
-
-								object.matrix.copy( matrix );
-								object.matrix.decompose( object.position, object.quaternion, object.scale );
-								jointMap[ jointIndex ].position = value;
-
-							}
-
-						} else {
-
-							console.log( 'THREE.ColladaLoader: ' + jointIndex + ' does not exist.' );
-
-						}
-
-					}
-				};
-
-			}
-
-			function buildTransformList( node ) {
-
-				const transforms = [];
-				const xml = collada.querySelector( '[id="' + node.id + '"]' );
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					let array, vector;
-					switch ( child.nodeName ) {
-
-						case 'matrix':
-							array = parseFloats( child.textContent );
-							const matrix = new THREE.Matrix4().fromArray( array ).transpose();
-							transforms.push( {
-								sid: child.getAttribute( 'sid' ),
-								type: child.nodeName,
-								obj: matrix
-							} );
-							break;
-						case 'translate':
-						case 'scale':
-							array = parseFloats( child.textContent );
-							vector = new THREE.Vector3().fromArray( array );
-							transforms.push( {
-								sid: child.getAttribute( 'sid' ),
-								type: child.nodeName,
-								obj: vector
-							} );
-							break;
-						case 'rotate':
-							array = parseFloats( child.textContent );
-							vector = new THREE.Vector3().fromArray( array );
-							const angle = THREE.MathUtils.degToRad( array[ 3 ] );
-							transforms.push( {
-								sid: child.getAttribute( 'sid' ),
-								type: child.nodeName,
-								obj: vector,
-								angle: angle
-							} );
-							break;
-
-					}
-
-				}
-
-				return transforms;
-
-			}
-
-			// nodes
-
-			function prepareNodes( xml ) {
-
-				const elements = xml.getElementsByTagName( 'node' );
-
-				// ensure all node elements have id attributes
-
-				for ( let i = 0; i < elements.length; i ++ ) {
-
-					const element = elements[ i ];
-					if ( element.hasAttribute( 'id' ) === false ) {
-
-						element.setAttribute( 'id', generateId() );
-
-					}
-
-				}
-
-			}
-
-			const matrix = new THREE.Matrix4();
-			const vector = new THREE.Vector3();
-			function parseNode( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' ) || '',
-					type: xml.getAttribute( 'type' ),
-					id: xml.getAttribute( 'id' ),
-					sid: xml.getAttribute( 'sid' ),
-					matrix: new THREE.Matrix4(),
-					nodes: [],
-					instanceCameras: [],
-					instanceControllers: [],
-					instanceLights: [],
-					instanceGeometries: [],
-					instanceNodes: [],
-					transforms: {}
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					if ( child.nodeType !== 1 ) continue;
-					let array;
-					switch ( child.nodeName ) {
-
-						case 'node':
-							data.nodes.push( child.getAttribute( 'id' ) );
-							parseNode( child );
-							break;
-						case 'instance_camera':
-							data.instanceCameras.push( parseId( child.getAttribute( 'url' ) ) );
-							break;
-						case 'instance_controller':
-							data.instanceControllers.push( parseNodeInstance( child ) );
-							break;
-						case 'instance_light':
-							data.instanceLights.push( parseId( child.getAttribute( 'url' ) ) );
-							break;
-						case 'instance_geometry':
-							data.instanceGeometries.push( parseNodeInstance( child ) );
-							break;
-						case 'instance_node':
-							data.instanceNodes.push( parseId( child.getAttribute( 'url' ) ) );
-							break;
-						case 'matrix':
-							array = parseFloats( child.textContent );
-							data.matrix.multiply( matrix.fromArray( array ).transpose() );
-							data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName;
-							break;
-						case 'translate':
-							array = parseFloats( child.textContent );
-							vector.fromArray( array );
-							data.matrix.multiply( matrix.makeTranslation( vector.x, vector.y, vector.z ) );
-							data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName;
-							break;
-						case 'rotate':
-							array = parseFloats( child.textContent );
-							const angle = THREE.MathUtils.degToRad( array[ 3 ] );
-							data.matrix.multiply( matrix.makeRotationAxis( vector.fromArray( array ), angle ) );
-							data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName;
-							break;
-						case 'scale':
-							array = parseFloats( child.textContent );
-							data.matrix.scale( vector.fromArray( array ) );
-							data.transforms[ child.getAttribute( 'sid' ) ] = child.nodeName;
-							break;
-						case 'extra':
-							break;
-						default:
-							console.log( child );
-
-					}
-
-				}
-
-				if ( hasNode( data.id ) ) {
-
-					console.warn( 'THREE.ColladaLoader: There is already a node with ID %s. Exclude current node from further processing.', data.id );
-
-				} else {
-
-					library.nodes[ data.id ] = data;
-
-				}
-
-				return data;
-
-			}
-
-			function parseNodeInstance( xml ) {
-
-				const data = {
-					id: parseId( xml.getAttribute( 'url' ) ),
-					materials: {},
-					skeletons: []
-				};
-				for ( let i = 0; i < xml.childNodes.length; i ++ ) {
-
-					const child = xml.childNodes[ i ];
-					switch ( child.nodeName ) {
-
-						case 'bind_material':
-							const instances = child.getElementsByTagName( 'instance_material' );
-							for ( let j = 0; j < instances.length; j ++ ) {
-
-								const instance = instances[ j ];
-								const symbol = instance.getAttribute( 'symbol' );
-								const target = instance.getAttribute( 'target' );
-								data.materials[ symbol ] = parseId( target );
-
-							}
-
-							break;
-						case 'skeleton':
-							data.skeletons.push( parseId( child.textContent ) );
-							break;
-						default:
-							break;
-
-					}
-
-				}
-
-				return data;
-
-			}
-
-			function buildSkeleton( skeletons, joints ) {
-
-				const boneData = [];
-				const sortedBoneData = [];
-				let i, j, data;
-
-				// a skeleton can have multiple root bones. collada expresses this
-				// situtation with multiple "skeleton" tags per controller instance
-
-				for ( i = 0; i < skeletons.length; i ++ ) {
-
-					const skeleton = skeletons[ i ];
-					let root;
-					if ( hasNode( skeleton ) ) {
-
-						root = getNode( skeleton );
-						buildBoneHierarchy( root, joints, boneData );
-
-					} else if ( hasVisualScene( skeleton ) ) {
-
-						// handle case where the skeleton refers to the visual scene (#13335)
-
-						const visualScene = library.visualScenes[ skeleton ];
-						const children = visualScene.children;
-						for ( let j = 0; j < children.length; j ++ ) {
-
-							const child = children[ j ];
-							if ( child.type === 'JOINT' ) {
-
-								const root = getNode( child.id );
-								buildBoneHierarchy( root, joints, boneData );
-
-							}
-
-						}
-
-					} else {
-
-						console.error( 'THREE.ColladaLoader: Unable to find root bone of skeleton with ID:', skeleton );
-
-					}
-
-				}
-
-				// sort bone data (the order is defined in the corresponding controller)
-
-				for ( i = 0; i < joints.length; i ++ ) {
-
-					for ( j = 0; j < boneData.length; j ++ ) {
-
-						data = boneData[ j ];
-						if ( data.bone.name === joints[ i ].name ) {
-
-							sortedBoneData[ i ] = data;
-							data.processed = true;
-							break;
-
-						}
-
-					}
-
-				}
-
-				// add unprocessed bone data at the end of the list
-
-				for ( i = 0; i < boneData.length; i ++ ) {
-
-					data = boneData[ i ];
-					if ( data.processed === false ) {
-
-						sortedBoneData.push( data );
-						data.processed = true;
-
-					}
-
-				}
-
-				// setup arrays for skeleton creation
-
-				const bones = [];
-				const boneInverses = [];
-				for ( i = 0; i < sortedBoneData.length; i ++ ) {
-
-					data = sortedBoneData[ i ];
-					bones.push( data.bone );
-					boneInverses.push( data.boneInverse );
-
-				}
-
-				return new THREE.Skeleton( bones, boneInverses );
-
-			}
-
-			function buildBoneHierarchy( root, joints, boneData ) {
-
-				// setup bone data from visual scene
-
-				root.traverse( function ( object ) {
-
-					if ( object.isBone === true ) {
-
-						let boneInverse;
-
-						// retrieve the boneInverse from the controller data
-
-						for ( let i = 0; i < joints.length; i ++ ) {
-
-							const joint = joints[ i ];
-							if ( joint.name === object.name ) {
-
-								boneInverse = joint.boneInverse;
-								break;
-
-							}
-
-						}
-
-						if ( boneInverse === undefined ) {
-
-							// Unfortunately, there can be joints in the visual scene that are not part of the
-							// corresponding controller. In this case, we have to create a dummy boneInverse matrix
-							// for the respective bone. This bone won't affect any vertices, because there are no skin indices
-							// and weights defined for it. But we still have to add the bone to the sorted bone list in order to
-							// ensure a correct animation of the model.
-
-							boneInverse = new THREE.Matrix4();
-
-						}
-
-						boneData.push( {
-							bone: object,
-							boneInverse: boneInverse,
-							processed: false
-						} );
-
-					}
-
-				} );
-
-			}
-
-			function buildNode( data ) {
-
-				const objects = [];
-				const matrix = data.matrix;
-				const nodes = data.nodes;
-				const type = data.type;
-				const instanceCameras = data.instanceCameras;
-				const instanceControllers = data.instanceControllers;
-				const instanceLights = data.instanceLights;
-				const instanceGeometries = data.instanceGeometries;
-				const instanceNodes = data.instanceNodes;
-
-				// nodes
-
-				for ( let i = 0, l = nodes.length; i < l; i ++ ) {
-
-					objects.push( getNode( nodes[ i ] ) );
-
-				}
-
-				// instance cameras
-
-				for ( let i = 0, l = instanceCameras.length; i < l; i ++ ) {
-
-					const instanceCamera = getCamera( instanceCameras[ i ] );
-					if ( instanceCamera !== null ) {
-
-						objects.push( instanceCamera.clone() );
-
-					}
-
-				}
-
-				// instance controllers
-
-				for ( let i = 0, l = instanceControllers.length; i < l; i ++ ) {
-
-					const instance = instanceControllers[ i ];
-					const controller = getController( instance.id );
-					const geometries = getGeometry( controller.id );
-					const newObjects = buildObjects( geometries, instance.materials );
-					const skeletons = instance.skeletons;
-					const joints = controller.skin.joints;
-					const skeleton = buildSkeleton( skeletons, joints );
-					for ( let j = 0, jl = newObjects.length; j < jl; j ++ ) {
-
-						const object = newObjects[ j ];
-						if ( object.isSkinnedMesh ) {
-
-							object.bind( skeleton, controller.skin.bindMatrix );
-							object.normalizeSkinWeights();
-
-						}
-
-						objects.push( object );
-
-					}
-
-				}
-
-				// instance lights
-
-				for ( let i = 0, l = instanceLights.length; i < l; i ++ ) {
-
-					const instanceLight = getLight( instanceLights[ i ] );
-					if ( instanceLight !== null ) {
-
-						objects.push( instanceLight.clone() );
-
-					}
-
-				}
-
-				// instance geometries
-
-				for ( let i = 0, l = instanceGeometries.length; i < l; i ++ ) {
-
-					const instance = instanceGeometries[ i ];
-
-					// a single geometry instance in collada can lead to multiple object3Ds.
-					// this is the case when primitives are combined like triangles and lines
-
-					const geometries = getGeometry( instance.id );
-					const newObjects = buildObjects( geometries, instance.materials );
-					for ( let j = 0, jl = newObjects.length; j < jl; j ++ ) {
-
-						objects.push( newObjects[ j ] );
-
-					}
-
-				}
-
-				// instance nodes
-
-				for ( let i = 0, l = instanceNodes.length; i < l; i ++ ) {
-
-					objects.push( getNode( instanceNodes[ i ] ).clone() );
-
-				}
-
-				let object;
-				if ( nodes.length === 0 && objects.length === 1 ) {
-
-					object = objects[ 0 ];
-
-				} else {
-
-					object = type === 'JOINT' ? new THREE.Bone() : new THREE.Group();
-					for ( let i = 0; i < objects.length; i ++ ) {
-
-						object.add( objects[ i ] );
-
-					}
-
-				}
-
-				object.name = type === 'JOINT' ? data.sid : data.name;
-				object.matrix.copy( matrix );
-				object.matrix.decompose( object.position, object.quaternion, object.scale );
-				return object;
-
-			}
-
-			const fallbackMaterial = new THREE.MeshBasicMaterial( {
-				color: 0xff00ff
-			} );
-			function resolveMaterialBinding( keys, instanceMaterials ) {
-
-				const materials = [];
-				for ( let i = 0, l = keys.length; i < l; i ++ ) {
-
-					const id = instanceMaterials[ keys[ i ] ];
-					if ( id === undefined ) {
-
-						console.warn( 'THREE.ColladaLoader: Material with key %s not found. Apply fallback material.', keys[ i ] );
-						materials.push( fallbackMaterial );
-
-					} else {
-
-						materials.push( getMaterial( id ) );
-
-					}
-
-				}
-
-				return materials;
-
-			}
-
-			function buildObjects( geometries, instanceMaterials ) {
-
-				const objects = [];
-				for ( const type in geometries ) {
-
-					const geometry = geometries[ type ];
-					const materials = resolveMaterialBinding( geometry.materialKeys, instanceMaterials );
-
-					// handle case if no materials are defined
-
-					if ( materials.length === 0 ) {
-
-						if ( type === 'lines' || type === 'linestrips' ) {
-
-							materials.push( new THREE.LineBasicMaterial() );
-
-						} else {
-
-							materials.push( new THREE.MeshPhongMaterial() );
-
-						}
-
-					}
-
-					// Collada allows to use phong and lambert materials with lines. Replacing these cases with THREE.LineBasicMaterial.
-
-					if ( type === 'lines' || type === 'linestrips' ) {
-
-						for ( let i = 0, l = materials.length; i < l; i ++ ) {
-
-							const material = materials[ i ];
-							if ( material.isMeshPhongMaterial === true || material.isMeshLambertMaterial === true ) {
-
-								const lineMaterial = new THREE.LineBasicMaterial();
-
-								// copy compatible properties
-
-								lineMaterial.color.copy( material.color );
-								lineMaterial.opacity = material.opacity;
-								lineMaterial.transparent = material.transparent;
-
-								// replace material
-
-								materials[ i ] = lineMaterial;
-
-							}
-
-						}
-
-					}
-
-					// regard skinning
-
-					const skinning = geometry.data.attributes.skinIndex !== undefined;
-
-					// choose between a single or multi materials (material array)
-
-					const material = materials.length === 1 ? materials[ 0 ] : materials;
-
-					// now create a specific 3D object
-
-					let object;
-					switch ( type ) {
-
-						case 'lines':
-							object = new THREE.LineSegments( geometry.data, material );
-							break;
-						case 'linestrips':
-							object = new THREE.Line( geometry.data, material );
-							break;
-						case 'triangles':
-						case 'polylist':
-							if ( skinning ) {
-
-								object = new THREE.SkinnedMesh( geometry.data, material );
-
-							} else {
-
-								object = new THREE.Mesh( geometry.data, material );
-
-							}
-
-							break;
-
-					}
-
-					objects.push( object );
-
-				}
-
-				return objects;
-
-			}
-
-			function hasNode( id ) {
-
-				return library.nodes[ id ] !== undefined;
-
-			}
-
-			function getNode( id ) {
-
-				return getBuild( library.nodes[ id ], buildNode );
-
-			}
-
-			// visual scenes
-
-			function parseVisualScene( xml ) {
-
-				const data = {
-					name: xml.getAttribute( 'name' ),
-					children: []
-				};
-				prepareNodes( xml );
-				const elements = getElementsByTagName( xml, 'node' );
-				for ( let i = 0; i < elements.length; i ++ ) {
-
-					data.children.push( parseNode( elements[ i ] ) );
-
-				}
-
-				library.visualScenes[ xml.getAttribute( 'id' ) ] = data;
-
-			}
-
-			function buildVisualScene( data ) {
-
-				const group = new THREE.Group();
-				group.name = data.name;
-				const children = data.children;
-				for ( let i = 0; i < children.length; i ++ ) {
-
-					const child = children[ i ];
-					group.add( getNode( child.id ) );
-
-				}
-
-				return group;
-
-			}
-
-			function hasVisualScene( id ) {
-
-				return library.visualScenes[ id ] !== undefined;
-
-			}
-
-			function getVisualScene( id ) {
-
-				return getBuild( library.visualScenes[ id ], buildVisualScene );
-
-			}
-
-			// scenes
-
-			function parseScene( xml ) {
-
-				const instance = getElementsByTagName( xml, 'instance_visual_scene' )[ 0 ];
-				return getVisualScene( parseId( instance.getAttribute( 'url' ) ) );
-
-			}
-
-			function setupAnimations() {
-
-				const clips = library.clips;
-				if ( isEmpty( clips ) === true ) {
-
-					if ( isEmpty( library.animations ) === false ) {
-
-						// if there are animations but no clips, we create a default clip for playback
-
-						const tracks = [];
-						for ( const id in library.animations ) {
-
-							const animationTracks = getAnimation( id );
-							for ( let i = 0, l = animationTracks.length; i < l; i ++ ) {
-
-								tracks.push( animationTracks[ i ] );
-
-							}
-
-						}
-
-						animations.push( new THREE.AnimationClip( 'default', - 1, tracks ) );
-
-					}
-
-				} else {
-
-					for ( const id in clips ) {
-
-						animations.push( getAnimationClip( id ) );
-
-					}
-
-				}
-
-			}
-
-			// convert the parser error element into text with each child elements text
-			// separated by new lines.
-
-			function parserErrorToText( parserError ) {
-
-				let result = '';
-				const stack = [ parserError ];
-				while ( stack.length ) {
-
-					const node = stack.shift();
-					if ( node.nodeType === Node.TEXT_NODE ) {
-
-						result += node.textContent;
-
-					} else {
-
-						result += '\n';
-						stack.push.apply( stack, node.childNodes );
-
-					}
-
-				}
-
-				return result.trim();
-
-			}
-
-			if ( text.length === 0 ) {
-
-				return {
-					scene: new THREE.Scene()
-				};
-
-			}
-
-			const xml = new DOMParser().parseFromString( text, 'application/xml' );
-			const collada = getElementsByTagName( xml, 'COLLADA' )[ 0 ];
-			const parserError = xml.getElementsByTagName( 'parsererror' )[ 0 ];
-			if ( parserError !== undefined ) {
-
-				// Chrome will return parser error with a div in it
-
-				const errorElement = getElementsByTagName( parserError, 'div' )[ 0 ];
-				let errorText;
-				if ( errorElement ) {
-
-					errorText = errorElement.textContent;
-
-				} else {
-
-					errorText = parserErrorToText( parserError );
-
-				}
-
-				console.error( 'THREE.ColladaLoader: Failed to parse collada file.\n', errorText );
-				return null;
-
-			}
-
-			// metadata
-
-			const version = collada.getAttribute( 'version' );
-			console.log( 'THREE.ColladaLoader: File version', version );
-			const asset = parseAsset( getElementsByTagName( collada, 'asset' )[ 0 ] );
-			const textureLoader = new THREE.TextureLoader( this.manager );
-			textureLoader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
-			let tgaLoader;
-			if ( THREE.TGALoader ) {
-
-				tgaLoader = new THREE.TGALoader( this.manager );
-				tgaLoader.setPath( this.resourcePath || path );
-
-			}
-
-			//
-
-			const tempColor = new THREE.Color();
-			const animations = [];
-			let kinematics = {};
-			let count = 0;
-
-			//
-
-			const library = {
-				animations: {},
-				clips: {},
-				controllers: {},
-				images: {},
-				effects: {},
-				materials: {},
-				cameras: {},
-				lights: {},
-				geometries: {},
-				nodes: {},
-				visualScenes: {},
-				kinematicsModels: {},
-				physicsModels: {},
-				kinematicsScenes: {}
-			};
-			parseLibrary( collada, 'library_animations', 'animation', parseAnimation );
-			parseLibrary( collada, 'library_animation_clips', 'animation_clip', parseAnimationClip );
-			parseLibrary( collada, 'library_controllers', 'controller', parseController );
-			parseLibrary( collada, 'library_images', 'image', parseImage );
-			parseLibrary( collada, 'library_effects', 'effect', parseEffect );
-			parseLibrary( collada, 'library_materials', 'material', parseMaterial );
-			parseLibrary( collada, 'library_cameras', 'camera', parseCamera );
-			parseLibrary( collada, 'library_lights', 'light', parseLight );
-			parseLibrary( collada, 'library_geometries', 'geometry', parseGeometry );
-			parseLibrary( collada, 'library_nodes', 'node', parseNode );
-			parseLibrary( collada, 'library_visual_scenes', 'visual_scene', parseVisualScene );
-			parseLibrary( collada, 'library_kinematics_models', 'kinematics_model', parseKinematicsModel );
-			parseLibrary( collada, 'library_physics_models', 'physics_model', parsePhysicsModel );
-			parseLibrary( collada, 'scene', 'instance_kinematics_scene', parseKinematicsScene );
-			buildLibrary( library.animations, buildAnimation );
-			buildLibrary( library.clips, buildAnimationClip );
-			buildLibrary( library.controllers, buildController );
-			buildLibrary( library.images, buildImage );
-			buildLibrary( library.effects, buildEffect );
-			buildLibrary( library.materials, buildMaterial );
-			buildLibrary( library.cameras, buildCamera );
-			buildLibrary( library.lights, buildLight );
-			buildLibrary( library.geometries, buildGeometry );
-			buildLibrary( library.visualScenes, buildVisualScene );
-			setupAnimations();
-			setupKinematics();
-			const scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] );
-			scene.animations = animations;
-			if ( asset.upAxis === 'Z_UP' ) {
-
-				console.warn( 'THREE.ColladaLoader: You are loading an asset with a Z-UP coordinate system. The loader just rotates the asset to transform it into Y-UP. The vertex data are not converted, see #24289.' );
-				scene.quaternion.setFromEuler( new THREE.Euler( - Math.PI / 2, 0, 0 ) );
-
-			}
-
-			scene.scale.multiplyScalar( asset.unit );
-			return {
-				get animations() {
-
-					console.warn( 'THREE.ColladaLoader: Please access animations over scene.animations now.' );
-					return animations;
-
-				},
-				kinematics: kinematics,
-				library: library,
-				scene: scene
-			};
-
-		}
-
-	}
-
-	THREE.ColladaLoader = ColladaLoader;
-
-} )();

+ 0 - 244
examples/js/loaders/DDSLoader.js

@@ -1,244 +0,0 @@
-( function () {
-
-	class DDSLoader extends THREE.CompressedTextureLoader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		parse( buffer, loadMipmaps ) {
-
-			const dds = {
-				mipmaps: [],
-				width: 0,
-				height: 0,
-				format: null,
-				mipmapCount: 1
-			};
-
-			// Adapted from @toji's DDS utils
-			// https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
-
-			// All values and structures referenced from:
-			// http://msdn.microsoft.com/en-us/library/bb943991.aspx/
-
-			const DDS_MAGIC = 0x20534444;
-
-			// const DDSD_CAPS = 0x1;
-			// const DDSD_HEIGHT = 0x2;
-			// const DDSD_WIDTH = 0x4;
-			// const DDSD_PITCH = 0x8;
-			// const DDSD_PIXELFORMAT = 0x1000;
-			const DDSD_MIPMAPCOUNT = 0x20000;
-			// const DDSD_LINEARSIZE = 0x80000;
-			// const DDSD_DEPTH = 0x800000;
-
-			// const DDSCAPS_COMPLEX = 0x8;
-			// const DDSCAPS_MIPMAP = 0x400000;
-			// const DDSCAPS_TEXTURE = 0x1000;
-
-			const DDSCAPS2_CUBEMAP = 0x200;
-			const DDSCAPS2_CUBEMAP_POSITIVEX = 0x400;
-			const DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800;
-			const DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000;
-			const DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000;
-			const DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000;
-			const DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000;
-			// const DDSCAPS2_VOLUME = 0x200000;
-
-			// const DDPF_ALPHAPIXELS = 0x1;
-			// const DDPF_ALPHA = 0x2;
-			// const DDPF_FOURCC = 0x4;
-			// const DDPF_RGB = 0x40;
-			// const DDPF_YUV = 0x200;
-			// const DDPF_LUMINANCE = 0x20000;
-
-			function fourCCToInt32( value ) {
-
-				return value.charCodeAt( 0 ) + ( value.charCodeAt( 1 ) << 8 ) + ( value.charCodeAt( 2 ) << 16 ) + ( value.charCodeAt( 3 ) << 24 );
-
-			}
-
-			function int32ToFourCC( value ) {
-
-				return String.fromCharCode( value & 0xff, value >> 8 & 0xff, value >> 16 & 0xff, value >> 24 & 0xff );
-
-			}
-
-			function loadARGBMip( buffer, dataOffset, width, height ) {
-
-				const dataLength = width * height * 4;
-				const srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
-				const byteArray = new Uint8Array( dataLength );
-				let dst = 0;
-				let src = 0;
-				for ( let y = 0; y < height; y ++ ) {
-
-					for ( let x = 0; x < width; x ++ ) {
-
-						const b = srcBuffer[ src ];
-						src ++;
-						const g = srcBuffer[ src ];
-						src ++;
-						const r = srcBuffer[ src ];
-						src ++;
-						const a = srcBuffer[ src ];
-						src ++;
-						byteArray[ dst ] = r;
-						dst ++; //r
-						byteArray[ dst ] = g;
-						dst ++; //g
-						byteArray[ dst ] = b;
-						dst ++; //b
-						byteArray[ dst ] = a;
-						dst ++; //a
-
-					}
-
-				}
-
-				return byteArray;
-
-			}
-
-			const FOURCC_DXT1 = fourCCToInt32( 'DXT1' );
-			const FOURCC_DXT3 = fourCCToInt32( 'DXT3' );
-			const FOURCC_DXT5 = fourCCToInt32( 'DXT5' );
-			const FOURCC_ETC1 = fourCCToInt32( 'ETC1' );
-			const headerLengthInt = 31; // The header length in 32 bit ints
-
-			// Offsets into the header array
-
-			const off_magic = 0;
-			const off_size = 1;
-			const off_flags = 2;
-			const off_height = 3;
-			const off_width = 4;
-			const off_mipmapCount = 7;
-
-			// const off_pfFlags = 20;
-			const off_pfFourCC = 21;
-			const off_RGBBitCount = 22;
-			const off_RBitMask = 23;
-			const off_GBitMask = 24;
-			const off_BBitMask = 25;
-			const off_ABitMask = 26;
-
-			// const off_caps = 27;
-			const off_caps2 = 28;
-			// const off_caps3 = 29;
-			// const off_caps4 = 30;
-
-			// Parse header
-
-			const header = new Int32Array( buffer, 0, headerLengthInt );
-			if ( header[ off_magic ] !== DDS_MAGIC ) {
-
-				console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
-				return dds;
-
-			}
-
-			let blockBytes;
-			const fourCC = header[ off_pfFourCC ];
-			let isRGBAUncompressed = false;
-			switch ( fourCC ) {
-
-				case FOURCC_DXT1:
-					blockBytes = 8;
-					dds.format = THREE.RGB_S3TC_DXT1_Format;
-					break;
-				case FOURCC_DXT3:
-					blockBytes = 16;
-					dds.format = THREE.RGBA_S3TC_DXT3_Format;
-					break;
-				case FOURCC_DXT5:
-					blockBytes = 16;
-					dds.format = THREE.RGBA_S3TC_DXT5_Format;
-					break;
-				case FOURCC_ETC1:
-					blockBytes = 8;
-					dds.format = THREE.RGB_ETC1_Format;
-					break;
-				default:
-					if ( header[ off_RGBBitCount ] === 32 && header[ off_RBitMask ] & 0xff0000 && header[ off_GBitMask ] & 0xff00 && header[ off_BBitMask ] & 0xff && header[ off_ABitMask ] & 0xff000000 ) {
-
-						isRGBAUncompressed = true;
-						blockBytes = 64;
-						dds.format = THREE.RGBAFormat;
-
-					} else {
-
-						console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
-						return dds;
-
-					}
-
-			}
-
-			dds.mipmapCount = 1;
-			if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
-
-				dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
-
-			}
-
-			const caps2 = header[ off_caps2 ];
-			dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false;
-			if ( dds.isCubemap && ( ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) || ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) || ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) || ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) || ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) || ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ ) ) ) {
-
-				console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' );
-				return dds;
-
-			}
-
-			dds.width = header[ off_width ];
-			dds.height = header[ off_height ];
-			let dataOffset = header[ off_size ] + 4;
-
-			// Extract mipmaps buffers
-
-			const faces = dds.isCubemap ? 6 : 1;
-			for ( let face = 0; face < faces; face ++ ) {
-
-				let width = dds.width;
-				let height = dds.height;
-				for ( let i = 0; i < dds.mipmapCount; i ++ ) {
-
-					let byteArray, dataLength;
-					if ( isRGBAUncompressed ) {
-
-						byteArray = loadARGBMip( buffer, dataOffset, width, height );
-						dataLength = byteArray.length;
-
-					} else {
-
-						dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
-						byteArray = new Uint8Array( buffer, dataOffset, dataLength );
-
-					}
-
-					const mipmap = {
-						'data': byteArray,
-						'width': width,
-						'height': height
-					};
-					dds.mipmaps.push( mipmap );
-					dataOffset += dataLength;
-					width = Math.max( width >> 1, 1 );
-					height = Math.max( height >> 1, 1 );
-
-				}
-
-			}
-
-			return dds;
-
-		}
-
-	}
-
-	THREE.DDSLoader = DDSLoader;
-
-} )();

+ 0 - 511
examples/js/loaders/DRACOLoader.js

@@ -1,511 +0,0 @@
-( function () {
-
-	const _taskCache = new WeakMap();
-	class DRACOLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.decoderPath = '';
-			this.decoderConfig = {};
-			this.decoderBinary = null;
-			this.decoderPending = null;
-			this.workerLimit = 4;
-			this.workerPool = [];
-			this.workerNextTaskID = 1;
-			this.workerSourceURL = '';
-			this.defaultAttributeIDs = {
-				position: 'POSITION',
-				normal: 'NORMAL',
-				color: 'COLOR',
-				uv: 'TEX_COORD'
-			};
-			this.defaultAttributeTypes = {
-				position: 'Float32Array',
-				normal: 'Float32Array',
-				color: 'Float32Array',
-				uv: 'Float32Array'
-			};
-
-		}
-		setDecoderPath( path ) {
-
-			this.decoderPath = path;
-			return this;
-
-		}
-		setDecoderConfig( config ) {
-
-			this.decoderConfig = config;
-			return this;
-
-		}
-		setWorkerLimit( workerLimit ) {
-
-			this.workerLimit = workerLimit;
-			return this;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( this.requestHeader );
-			loader.setWithCredentials( this.withCredentials );
-			loader.load( url, buffer => {
-
-				this.decodeDracoFile( buffer, onLoad ).catch( onError );
-
-			}, onProgress, onError );
-
-		}
-		decodeDracoFile( buffer, callback, attributeIDs, attributeTypes ) {
-
-			const taskConfig = {
-				attributeIDs: attributeIDs || this.defaultAttributeIDs,
-				attributeTypes: attributeTypes || this.defaultAttributeTypes,
-				useUniqueIDs: !! attributeIDs
-			};
-			return this.decodeGeometry( buffer, taskConfig ).then( callback );
-
-		}
-		decodeGeometry( buffer, taskConfig ) {
-
-			const taskKey = JSON.stringify( taskConfig );
-
-			// Check for an existing task using this buffer. A transferred buffer cannot be transferred
-			// again from this thread.
-			if ( _taskCache.has( buffer ) ) {
-
-				const cachedTask = _taskCache.get( buffer );
-				if ( cachedTask.key === taskKey ) {
-
-					return cachedTask.promise;
-
-				} else if ( buffer.byteLength === 0 ) {
-
-					// Technically, it would be possible to wait for the previous task to complete,
-					// transfer the buffer back, and decode again with the second configuration. That
-					// is complex, and I don't know of any reason to decode a Draco buffer twice in
-					// different ways, so this is left unimplemented.
-					throw new Error( 'THREE.DRACOLoader: Unable to re-decode a buffer with different ' + 'settings. Buffer has already been transferred.' );
-
-				}
-
-			}
-
-			//
-
-			let worker;
-			const taskID = this.workerNextTaskID ++;
-			const taskCost = buffer.byteLength;
-
-			// Obtain a worker and assign a task, and construct a geometry instance
-			// when the task completes.
-			const geometryPending = this._getWorker( taskID, taskCost ).then( _worker => {
-
-				worker = _worker;
-				return new Promise( ( resolve, reject ) => {
-
-					worker._callbacks[ taskID ] = {
-						resolve,
-						reject
-					};
-					worker.postMessage( {
-						type: 'decode',
-						id: taskID,
-						taskConfig,
-						buffer
-					}, [ buffer ] );
-
-					// this.debug();
-
-				} );
-
-			} ).then( message => this._createGeometry( message.geometry ) );
-
-			// Remove task from the task list.
-			// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
-			geometryPending.catch( () => true ).then( () => {
-
-				if ( worker && taskID ) {
-
-					this._releaseTask( worker, taskID );
-
-					// this.debug();
-
-				}
-
-			} );
-
-			// Cache the task result.
-			_taskCache.set( buffer, {
-				key: taskKey,
-				promise: geometryPending
-			} );
-			return geometryPending;
-
-		}
-		_createGeometry( geometryData ) {
-
-			const geometry = new THREE.BufferGeometry();
-			if ( geometryData.index ) {
-
-				geometry.setIndex( new THREE.BufferAttribute( geometryData.index.array, 1 ) );
-
-			}
-
-			for ( let i = 0; i < geometryData.attributes.length; i ++ ) {
-
-				const attribute = geometryData.attributes[ i ];
-				const name = attribute.name;
-				const array = attribute.array;
-				const itemSize = attribute.itemSize;
-				geometry.setAttribute( name, new THREE.BufferAttribute( array, itemSize ) );
-
-			}
-
-			return geometry;
-
-		}
-		_loadLibrary( url, responseType ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.decoderPath );
-			loader.setResponseType( responseType );
-			loader.setWithCredentials( this.withCredentials );
-			return new Promise( ( resolve, reject ) => {
-
-				loader.load( url, resolve, undefined, reject );
-
-			} );
-
-		}
-		preload() {
-
-			this._initDecoder();
-			return this;
-
-		}
-		_initDecoder() {
-
-			if ( this.decoderPending ) return this.decoderPending;
-			const useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
-			const librariesPending = [];
-			if ( useJS ) {
-
-				librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
-
-			} else {
-
-				librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
-				librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
-
-			}
-
-			this.decoderPending = Promise.all( librariesPending ).then( libraries => {
-
-				const jsContent = libraries[ 0 ];
-				if ( ! useJS ) {
-
-					this.decoderConfig.wasmBinary = libraries[ 1 ];
-
-				}
-
-				const fn = DRACOWorker.toString();
-				const body = [ '/* draco decoder */', jsContent, '', '/* worker */', fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) ) ].join( '\n' );
-				this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
-
-			} );
-			return this.decoderPending;
-
-		}
-		_getWorker( taskID, taskCost ) {
-
-			return this._initDecoder().then( () => {
-
-				if ( this.workerPool.length < this.workerLimit ) {
-
-					const worker = new Worker( this.workerSourceURL );
-					worker._callbacks = {};
-					worker._taskCosts = {};
-					worker._taskLoad = 0;
-					worker.postMessage( {
-						type: 'init',
-						decoderConfig: this.decoderConfig
-					} );
-					worker.onmessage = function ( e ) {
-
-						const message = e.data;
-						switch ( message.type ) {
-
-							case 'decode':
-								worker._callbacks[ message.id ].resolve( message );
-								break;
-							case 'error':
-								worker._callbacks[ message.id ].reject( message );
-								break;
-							default:
-								console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
-
-						}
-
-					};
-
-					this.workerPool.push( worker );
-
-				} else {
-
-					this.workerPool.sort( function ( a, b ) {
-
-						return a._taskLoad > b._taskLoad ? - 1 : 1;
-
-					} );
-
-				}
-
-				const worker = this.workerPool[ this.workerPool.length - 1 ];
-				worker._taskCosts[ taskID ] = taskCost;
-				worker._taskLoad += taskCost;
-				return worker;
-
-			} );
-
-		}
-		_releaseTask( worker, taskID ) {
-
-			worker._taskLoad -= worker._taskCosts[ taskID ];
-			delete worker._callbacks[ taskID ];
-			delete worker._taskCosts[ taskID ];
-
-		}
-		debug() {
-
-			console.log( 'Task load: ', this.workerPool.map( worker => worker._taskLoad ) );
-
-		}
-		dispose() {
-
-			for ( let i = 0; i < this.workerPool.length; ++ i ) {
-
-				this.workerPool[ i ].terminate();
-
-			}
-
-			this.workerPool.length = 0;
-			return this;
-
-		}
-
-	}
-
-	/* WEB WORKER */
-
-	function DRACOWorker() {
-
-		let decoderConfig;
-		let decoderPending;
-		onmessage = function ( e ) {
-
-			const message = e.data;
-			switch ( message.type ) {
-
-				case 'init':
-					decoderConfig = message.decoderConfig;
-					decoderPending = new Promise( function ( resolve /*, reject*/ ) {
-
-						decoderConfig.onModuleLoaded = function ( draco ) {
-
-							// Module is Promise-like. Wrap before resolving to avoid loop.
-							resolve( {
-								draco: draco
-							} );
-
-						};
-
-						DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
-
-					} );
-
-					break;
-				case 'decode':
-					const buffer = message.buffer;
-					const taskConfig = message.taskConfig;
-					decoderPending.then( module => {
-
-						const draco = module.draco;
-						const decoder = new draco.Decoder();
-						const decoderBuffer = new draco.DecoderBuffer();
-						decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
-						try {
-
-							const geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
-							const buffers = geometry.attributes.map( attr => attr.array.buffer );
-							if ( geometry.index ) buffers.push( geometry.index.array.buffer );
-							self.postMessage( {
-								type: 'decode',
-								id: message.id,
-								geometry
-							}, buffers );
-
-						} catch ( error ) {
-
-							console.error( error );
-							self.postMessage( {
-								type: 'error',
-								id: message.id,
-								error: error.message
-							} );
-
-						} finally {
-
-							draco.destroy( decoderBuffer );
-							draco.destroy( decoder );
-
-						}
-
-					} );
-					break;
-
-			}
-
-		};
-
-		function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
-
-			const attributeIDs = taskConfig.attributeIDs;
-			const attributeTypes = taskConfig.attributeTypes;
-			let dracoGeometry;
-			let decodingStatus;
-			const geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
-			if ( geometryType === draco.TRIANGULAR_MESH ) {
-
-				dracoGeometry = new draco.Mesh();
-				decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
-
-			} else if ( geometryType === draco.POINT_CLOUD ) {
-
-				dracoGeometry = new draco.PointCloud();
-				decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
-
-			} else {
-
-				throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
-
-			}
-
-			if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
-
-				throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
-
-			}
-
-			const geometry = {
-				index: null,
-				attributes: []
-			};
-
-			// Gather all vertex attributes.
-			for ( const attributeName in attributeIDs ) {
-
-				const attributeType = self[ attributeTypes[ attributeName ] ];
-				let attribute;
-				let attributeID;
-
-				// A Draco file may be created with default vertex attributes, whose attribute IDs
-				// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
-				// a Draco file may contain a custom set of attributes, identified by known unique
-				// IDs. glTF files always do the latter, and `.drc` files typically do the former.
-				if ( taskConfig.useUniqueIDs ) {
-
-					attributeID = attributeIDs[ attributeName ];
-					attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
-
-				} else {
-
-					attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
-					if ( attributeID === - 1 ) continue;
-					attribute = decoder.GetAttribute( dracoGeometry, attributeID );
-
-				}
-
-				geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
-
-			}
-
-			// Add index.
-			if ( geometryType === draco.TRIANGULAR_MESH ) {
-
-				geometry.index = decodeIndex( draco, decoder, dracoGeometry );
-
-			}
-
-			draco.destroy( dracoGeometry );
-			return geometry;
-
-		}
-
-		function decodeIndex( draco, decoder, dracoGeometry ) {
-
-			const numFaces = dracoGeometry.num_faces();
-			const numIndices = numFaces * 3;
-			const byteLength = numIndices * 4;
-			const ptr = draco._malloc( byteLength );
-			decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
-			const index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
-			draco._free( ptr );
-			return {
-				array: index,
-				itemSize: 1
-			};
-
-		}
-
-		function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
-
-			const numComponents = attribute.num_components();
-			const numPoints = dracoGeometry.num_points();
-			const numValues = numPoints * numComponents;
-			const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
-			const dataType = getDracoDataType( draco, attributeType );
-			const ptr = draco._malloc( byteLength );
-			decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
-			const array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
-			draco._free( ptr );
-			return {
-				name: attributeName,
-				array: array,
-				itemSize: numComponents
-			};
-
-		}
-
-		function getDracoDataType( draco, attributeType ) {
-
-			switch ( attributeType ) {
-
-				case Float32Array:
-					return draco.DT_FLOAT32;
-				case Int8Array:
-					return draco.DT_INT8;
-				case Int16Array:
-					return draco.DT_INT16;
-				case Int32Array:
-					return draco.DT_INT32;
-				case Uint8Array:
-					return draco.DT_UINT8;
-				case Uint16Array:
-					return draco.DT_UINT16;
-				case Uint32Array:
-					return draco.DT_UINT32;
-
-			}
-
-		}
-
-	}
-
-	THREE.DRACOLoader = DRACOLoader;
-
-} )();

+ 0 - 2039
examples/js/loaders/EXRLoader.js

@@ -1,2039 +0,0 @@
-( function () {
-
-	/**
- * OpenEXR loader currently supports uncompressed, ZIP(S), RLE, PIZ and DWA/B compression.
- * Supports reading as UnsignedByte, HalfFloat and Float type data texture.
- *
- * Referred to the original Industrial Light & Magic OpenEXR implementation and the TinyEXR / Syoyo Fujita
- * implementation, so I have preserved their copyright notices.
- */
-
-	// /*
-	// Copyright (c) 2014 - 2017, Syoyo Fujita
-	// All rights reserved.
-
-	// Redistribution and use in source and binary forms, with or without
-	// modification, are permitted provided that the following conditions are met:
-	//     * Redistributions of source code must retain the above copyright
-	//       notice, this list of conditions and the following disclaimer.
-	//     * Redistributions in binary form must reproduce the above copyright
-	//       notice, this list of conditions and the following disclaimer in the
-	//       documentation and/or other materials provided with the distribution.
-	//     * Neither the name of the Syoyo Fujita nor the
-	//       names of its contributors may be used to endorse or promote products
-	//       derived from this software without specific prior written permission.
-
-	// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-	// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-	// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-	// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-	// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-	// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-	// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-	// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-	// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-	// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-	// */
-
-	// // TinyEXR contains some OpenEXR code, which is licensed under ------------
-
-	// ///////////////////////////////////////////////////////////////////////////
-	// //
-	// // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
-	// // Digital Ltd. LLC
-	// //
-	// // All rights reserved.
-	// //
-	// // Redistribution and use in source and binary forms, with or without
-	// // modification, are permitted provided that the following conditions are
-	// // met:
-	// // *       Redistributions of source code must retain the above copyright
-	// // notice, this list of conditions and the following disclaimer.
-	// // *       Redistributions in binary form must reproduce the above
-	// // copyright notice, this list of conditions and the following disclaimer
-	// // in the documentation and/or other materials provided with the
-	// // distribution.
-	// // *       Neither the name of Industrial Light & Magic nor the names of
-	// // its contributors may be used to endorse or promote products derived
-	// // from this software without specific prior written permission.
-	// //
-	// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-	// // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-	// // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-	// // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-	// // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-	// // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-	// // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-	// // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-	// // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-	// // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-	// // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-	// //
-	// ///////////////////////////////////////////////////////////////////////////
-
-	// // End of OpenEXR license -------------------------------------------------
-
-	class EXRLoader extends THREE.DataTextureLoader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.type = THREE.HalfFloatType;
-
-		}
-		parse( buffer ) {
-
-			const USHORT_RANGE = 1 << 16;
-			const BITMAP_SIZE = USHORT_RANGE >> 3;
-			const HUF_ENCBITS = 16; // literal (value) bit length
-			const HUF_DECBITS = 14; // decoding bit size (>= 8)
-
-			const HUF_ENCSIZE = ( 1 << HUF_ENCBITS ) + 1; // encoding table size
-			const HUF_DECSIZE = 1 << HUF_DECBITS; // decoding table size
-			const HUF_DECMASK = HUF_DECSIZE - 1;
-			const NBITS = 16;
-			const A_OFFSET = 1 << NBITS - 1;
-			const MOD_MASK = ( 1 << NBITS ) - 1;
-			const SHORT_ZEROCODE_RUN = 59;
-			const LONG_ZEROCODE_RUN = 63;
-			const SHORTEST_LONG_RUN = 2 + LONG_ZEROCODE_RUN - SHORT_ZEROCODE_RUN;
-			const ULONG_SIZE = 8;
-			const FLOAT32_SIZE = 4;
-			const INT32_SIZE = 4;
-			const INT16_SIZE = 2;
-			const INT8_SIZE = 1;
-			const STATIC_HUFFMAN = 0;
-			const DEFLATE = 1;
-			const UNKNOWN = 0;
-			const LOSSY_DCT = 1;
-			const RLE = 2;
-			const logBase = Math.pow( 2.7182818, 2.2 );
-			function reverseLutFromBitmap( bitmap, lut ) {
-
-				let k = 0;
-				for ( let i = 0; i < USHORT_RANGE; ++ i ) {
-
-					if ( i == 0 || bitmap[ i >> 3 ] & 1 << ( i & 7 ) ) {
-
-						lut[ k ++ ] = i;
-
-					}
-
-				}
-
-				const n = k - 1;
-				while ( k < USHORT_RANGE ) lut[ k ++ ] = 0;
-				return n;
-
-			}
-
-			function hufClearDecTable( hdec ) {
-
-				for ( let i = 0; i < HUF_DECSIZE; i ++ ) {
-
-					hdec[ i ] = {};
-					hdec[ i ].len = 0;
-					hdec[ i ].lit = 0;
-					hdec[ i ].p = null;
-
-				}
-
-			}
-
-			const getBitsReturn = {
-				l: 0,
-				c: 0,
-				lc: 0
-			};
-			function getBits( nBits, c, lc, uInt8Array, inOffset ) {
-
-				while ( lc < nBits ) {
-
-					c = c << 8 | parseUint8Array( uInt8Array, inOffset );
-					lc += 8;
-
-				}
-
-				lc -= nBits;
-				getBitsReturn.l = c >> lc & ( 1 << nBits ) - 1;
-				getBitsReturn.c = c;
-				getBitsReturn.lc = lc;
-
-			}
-
-			const hufTableBuffer = new Array( 59 );
-			function hufCanonicalCodeTable( hcode ) {
-
-				for ( let i = 0; i <= 58; ++ i ) hufTableBuffer[ i ] = 0;
-				for ( let i = 0; i < HUF_ENCSIZE; ++ i ) hufTableBuffer[ hcode[ i ] ] += 1;
-				let c = 0;
-				for ( let i = 58; i > 0; -- i ) {
-
-					const nc = c + hufTableBuffer[ i ] >> 1;
-					hufTableBuffer[ i ] = c;
-					c = nc;
-
-				}
-
-				for ( let i = 0; i < HUF_ENCSIZE; ++ i ) {
-
-					const l = hcode[ i ];
-					if ( l > 0 ) hcode[ i ] = l | hufTableBuffer[ l ] ++ << 6;
-
-				}
-
-			}
-
-			function hufUnpackEncTable( uInt8Array, inOffset, ni, im, iM, hcode ) {
-
-				const p = inOffset;
-				let c = 0;
-				let lc = 0;
-				for ( ; im <= iM; im ++ ) {
-
-					if ( p.value - inOffset.value > ni ) return false;
-					getBits( 6, c, lc, uInt8Array, p );
-					const l = getBitsReturn.l;
-					c = getBitsReturn.c;
-					lc = getBitsReturn.lc;
-					hcode[ im ] = l;
-					if ( l == LONG_ZEROCODE_RUN ) {
-
-						if ( p.value - inOffset.value > ni ) {
-
-							throw new Error( 'Something wrong with hufUnpackEncTable' );
-
-						}
-
-						getBits( 8, c, lc, uInt8Array, p );
-						let zerun = getBitsReturn.l + SHORTEST_LONG_RUN;
-						c = getBitsReturn.c;
-						lc = getBitsReturn.lc;
-						if ( im + zerun > iM + 1 ) {
-
-							throw new Error( 'Something wrong with hufUnpackEncTable' );
-
-						}
-
-						while ( zerun -- ) hcode[ im ++ ] = 0;
-						im --;
-
-					} else if ( l >= SHORT_ZEROCODE_RUN ) {
-
-						let zerun = l - SHORT_ZEROCODE_RUN + 2;
-						if ( im + zerun > iM + 1 ) {
-
-							throw new Error( 'Something wrong with hufUnpackEncTable' );
-
-						}
-
-						while ( zerun -- ) hcode[ im ++ ] = 0;
-						im --;
-
-					}
-
-				}
-
-				hufCanonicalCodeTable( hcode );
-
-			}
-
-			function hufLength( code ) {
-
-				return code & 63;
-
-			}
-
-			function hufCode( code ) {
-
-				return code >> 6;
-
-			}
-
-			function hufBuildDecTable( hcode, im, iM, hdecod ) {
-
-				for ( ; im <= iM; im ++ ) {
-
-					const c = hufCode( hcode[ im ] );
-					const l = hufLength( hcode[ im ] );
-					if ( c >> l ) {
-
-						throw new Error( 'Invalid table entry' );
-
-					}
-
-					if ( l > HUF_DECBITS ) {
-
-						const pl = hdecod[ c >> l - HUF_DECBITS ];
-						if ( pl.len ) {
-
-							throw new Error( 'Invalid table entry' );
-
-						}
-
-						pl.lit ++;
-						if ( pl.p ) {
-
-							const p = pl.p;
-							pl.p = new Array( pl.lit );
-							for ( let i = 0; i < pl.lit - 1; ++ i ) {
-
-								pl.p[ i ] = p[ i ];
-
-							}
-
-						} else {
-
-							pl.p = new Array( 1 );
-
-						}
-
-						pl.p[ pl.lit - 1 ] = im;
-
-					} else if ( l ) {
-
-						let plOffset = 0;
-						for ( let i = 1 << HUF_DECBITS - l; i > 0; i -- ) {
-
-							const pl = hdecod[ ( c << HUF_DECBITS - l ) + plOffset ];
-							if ( pl.len || pl.p ) {
-
-								throw new Error( 'Invalid table entry' );
-
-							}
-
-							pl.len = l;
-							pl.lit = im;
-							plOffset ++;
-
-						}
-
-					}
-
-				}
-
-				return true;
-
-			}
-
-			const getCharReturn = {
-				c: 0,
-				lc: 0
-			};
-			function getChar( c, lc, uInt8Array, inOffset ) {
-
-				c = c << 8 | parseUint8Array( uInt8Array, inOffset );
-				lc += 8;
-				getCharReturn.c = c;
-				getCharReturn.lc = lc;
-
-			}
-
-			const getCodeReturn = {
-				c: 0,
-				lc: 0
-			};
-			function getCode( po, rlc, c, lc, uInt8Array, inOffset, outBuffer, outBufferOffset, outBufferEndOffset ) {
-
-				if ( po == rlc ) {
-
-					if ( lc < 8 ) {
-
-						getChar( c, lc, uInt8Array, inOffset );
-						c = getCharReturn.c;
-						lc = getCharReturn.lc;
-
-					}
-
-					lc -= 8;
-					let cs = c >> lc;
-					cs = new Uint8Array( [ cs ] )[ 0 ];
-					if ( outBufferOffset.value + cs > outBufferEndOffset ) {
-
-						return false;
-
-					}
-
-					const s = outBuffer[ outBufferOffset.value - 1 ];
-					while ( cs -- > 0 ) {
-
-						outBuffer[ outBufferOffset.value ++ ] = s;
-
-					}
-
-				} else if ( outBufferOffset.value < outBufferEndOffset ) {
-
-					outBuffer[ outBufferOffset.value ++ ] = po;
-
-				} else {
-
-					return false;
-
-				}
-
-				getCodeReturn.c = c;
-				getCodeReturn.lc = lc;
-
-			}
-
-			function UInt16( value ) {
-
-				return value & 0xFFFF;
-
-			}
-
-			function Int16( value ) {
-
-				const ref = UInt16( value );
-				return ref > 0x7FFF ? ref - 0x10000 : ref;
-
-			}
-
-			const wdec14Return = {
-				a: 0,
-				b: 0
-			};
-			function wdec14( l, h ) {
-
-				const ls = Int16( l );
-				const hs = Int16( h );
-				const hi = hs;
-				const ai = ls + ( hi & 1 ) + ( hi >> 1 );
-				const as = ai;
-				const bs = ai - hi;
-				wdec14Return.a = as;
-				wdec14Return.b = bs;
-
-			}
-
-			function wdec16( l, h ) {
-
-				const m = UInt16( l );
-				const d = UInt16( h );
-				const bb = m - ( d >> 1 ) & MOD_MASK;
-				const aa = d + bb - A_OFFSET & MOD_MASK;
-				wdec14Return.a = aa;
-				wdec14Return.b = bb;
-
-			}
-
-			function wav2Decode( buffer, j, nx, ox, ny, oy, mx ) {
-
-				const w14 = mx < 1 << 14;
-				const n = nx > ny ? ny : nx;
-				let p = 1;
-				let p2;
-				let py;
-				while ( p <= n ) p <<= 1;
-				p >>= 1;
-				p2 = p;
-				p >>= 1;
-				while ( p >= 1 ) {
-
-					py = 0;
-					const ey = py + oy * ( ny - p2 );
-					const oy1 = oy * p;
-					const oy2 = oy * p2;
-					const ox1 = ox * p;
-					const ox2 = ox * p2;
-					let i00, i01, i10, i11;
-					for ( ; py <= ey; py += oy2 ) {
-
-						let px = py;
-						const ex = py + ox * ( nx - p2 );
-						for ( ; px <= ex; px += ox2 ) {
-
-							const p01 = px + ox1;
-							const p10 = px + oy1;
-							const p11 = p10 + ox1;
-							if ( w14 ) {
-
-								wdec14( buffer[ px + j ], buffer[ p10 + j ] );
-								i00 = wdec14Return.a;
-								i10 = wdec14Return.b;
-								wdec14( buffer[ p01 + j ], buffer[ p11 + j ] );
-								i01 = wdec14Return.a;
-								i11 = wdec14Return.b;
-								wdec14( i00, i01 );
-								buffer[ px + j ] = wdec14Return.a;
-								buffer[ p01 + j ] = wdec14Return.b;
-								wdec14( i10, i11 );
-								buffer[ p10 + j ] = wdec14Return.a;
-								buffer[ p11 + j ] = wdec14Return.b;
-
-							} else {
-
-								wdec16( buffer[ px + j ], buffer[ p10 + j ] );
-								i00 = wdec14Return.a;
-								i10 = wdec14Return.b;
-								wdec16( buffer[ p01 + j ], buffer[ p11 + j ] );
-								i01 = wdec14Return.a;
-								i11 = wdec14Return.b;
-								wdec16( i00, i01 );
-								buffer[ px + j ] = wdec14Return.a;
-								buffer[ p01 + j ] = wdec14Return.b;
-								wdec16( i10, i11 );
-								buffer[ p10 + j ] = wdec14Return.a;
-								buffer[ p11 + j ] = wdec14Return.b;
-
-							}
-
-						}
-
-						if ( nx & p ) {
-
-							const p10 = px + oy1;
-							if ( w14 ) wdec14( buffer[ px + j ], buffer[ p10 + j ] ); else wdec16( buffer[ px + j ], buffer[ p10 + j ] );
-							i00 = wdec14Return.a;
-							buffer[ p10 + j ] = wdec14Return.b;
-							buffer[ px + j ] = i00;
-
-						}
-
-					}
-
-					if ( ny & p ) {
-
-						let px = py;
-						const ex = py + ox * ( nx - p2 );
-						for ( ; px <= ex; px += ox2 ) {
-
-							const p01 = px + ox1;
-							if ( w14 ) wdec14( buffer[ px + j ], buffer[ p01 + j ] ); else wdec16( buffer[ px + j ], buffer[ p01 + j ] );
-							i00 = wdec14Return.a;
-							buffer[ p01 + j ] = wdec14Return.b;
-							buffer[ px + j ] = i00;
-
-						}
-
-					}
-
-					p2 = p;
-					p >>= 1;
-
-				}
-
-				return py;
-
-			}
-
-			function hufDecode( encodingTable, decodingTable, uInt8Array, inOffset, ni, rlc, no, outBuffer, outOffset ) {
-
-				let c = 0;
-				let lc = 0;
-				const outBufferEndOffset = no;
-				const inOffsetEnd = Math.trunc( inOffset.value + ( ni + 7 ) / 8 );
-				while ( inOffset.value < inOffsetEnd ) {
-
-					getChar( c, lc, uInt8Array, inOffset );
-					c = getCharReturn.c;
-					lc = getCharReturn.lc;
-					while ( lc >= HUF_DECBITS ) {
-
-						const index = c >> lc - HUF_DECBITS & HUF_DECMASK;
-						const pl = decodingTable[ index ];
-						if ( pl.len ) {
-
-							lc -= pl.len;
-							getCode( pl.lit, rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset );
-							c = getCodeReturn.c;
-							lc = getCodeReturn.lc;
-
-						} else {
-
-							if ( ! pl.p ) {
-
-								throw new Error( 'hufDecode issues' );
-
-							}
-
-							let j;
-							for ( j = 0; j < pl.lit; j ++ ) {
-
-								const l = hufLength( encodingTable[ pl.p[ j ] ] );
-								while ( lc < l && inOffset.value < inOffsetEnd ) {
-
-									getChar( c, lc, uInt8Array, inOffset );
-									c = getCharReturn.c;
-									lc = getCharReturn.lc;
-
-								}
-
-								if ( lc >= l ) {
-
-									if ( hufCode( encodingTable[ pl.p[ j ] ] ) == ( c >> lc - l & ( 1 << l ) - 1 ) ) {
-
-										lc -= l;
-										getCode( pl.p[ j ], rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset );
-										c = getCodeReturn.c;
-										lc = getCodeReturn.lc;
-										break;
-
-									}
-
-								}
-
-							}
-
-							if ( j == pl.lit ) {
-
-								throw new Error( 'hufDecode issues' );
-
-							}
-
-						}
-
-					}
-
-				}
-
-				const i = 8 - ni & 7;
-				c >>= i;
-				lc -= i;
-				while ( lc > 0 ) {
-
-					const pl = decodingTable[ c << HUF_DECBITS - lc & HUF_DECMASK ];
-					if ( pl.len ) {
-
-						lc -= pl.len;
-						getCode( pl.lit, rlc, c, lc, uInt8Array, inOffset, outBuffer, outOffset, outBufferEndOffset );
-						c = getCodeReturn.c;
-						lc = getCodeReturn.lc;
-
-					} else {
-
-						throw new Error( 'hufDecode issues' );
-
-					}
-
-				}
-
-				return true;
-
-			}
-
-			function hufUncompress( uInt8Array, inDataView, inOffset, nCompressed, outBuffer, nRaw ) {
-
-				const outOffset = {
-					value: 0
-				};
-				const initialInOffset = inOffset.value;
-				const im = parseUint32( inDataView, inOffset );
-				const iM = parseUint32( inDataView, inOffset );
-				inOffset.value += 4;
-				const nBits = parseUint32( inDataView, inOffset );
-				inOffset.value += 4;
-				if ( im < 0 || im >= HUF_ENCSIZE || iM < 0 || iM >= HUF_ENCSIZE ) {
-
-					throw new Error( 'Something wrong with HUF_ENCSIZE' );
-
-				}
-
-				const freq = new Array( HUF_ENCSIZE );
-				const hdec = new Array( HUF_DECSIZE );
-				hufClearDecTable( hdec );
-				const ni = nCompressed - ( inOffset.value - initialInOffset );
-				hufUnpackEncTable( uInt8Array, inOffset, ni, im, iM, freq );
-				if ( nBits > 8 * ( nCompressed - ( inOffset.value - initialInOffset ) ) ) {
-
-					throw new Error( 'Something wrong with hufUncompress' );
-
-				}
-
-				hufBuildDecTable( freq, im, iM, hdec );
-				hufDecode( freq, hdec, uInt8Array, inOffset, nBits, iM, nRaw, outBuffer, outOffset );
-
-			}
-
-			function applyLut( lut, data, nData ) {
-
-				for ( let i = 0; i < nData; ++ i ) {
-
-					data[ i ] = lut[ data[ i ] ];
-
-				}
-
-			}
-
-			function predictor( source ) {
-
-				for ( let t = 1; t < source.length; t ++ ) {
-
-					const d = source[ t - 1 ] + source[ t ] - 128;
-					source[ t ] = d;
-
-				}
-
-			}
-
-			function interleaveScalar( source, out ) {
-
-				let t1 = 0;
-				let t2 = Math.floor( ( source.length + 1 ) / 2 );
-				let s = 0;
-				const stop = source.length - 1;
-				while ( true ) {
-
-					if ( s > stop ) break;
-					out[ s ++ ] = source[ t1 ++ ];
-					if ( s > stop ) break;
-					out[ s ++ ] = source[ t2 ++ ];
-
-				}
-
-			}
-
-			function decodeRunLength( source ) {
-
-				let size = source.byteLength;
-				const out = new Array();
-				let p = 0;
-				const reader = new DataView( source );
-				while ( size > 0 ) {
-
-					const l = reader.getInt8( p ++ );
-					if ( l < 0 ) {
-
-						const count = - l;
-						size -= count + 1;
-						for ( let i = 0; i < count; i ++ ) {
-
-							out.push( reader.getUint8( p ++ ) );
-
-						}
-
-					} else {
-
-						const count = l;
-						size -= 2;
-						const value = reader.getUint8( p ++ );
-						for ( let i = 0; i < count + 1; i ++ ) {
-
-							out.push( value );
-
-						}
-
-					}
-
-				}
-
-				return out;
-
-			}
-
-			function lossyDctDecode( cscSet, rowPtrs, channelData, acBuffer, dcBuffer, outBuffer ) {
-
-				let dataView = new DataView( outBuffer.buffer );
-				const width = channelData[ cscSet.idx[ 0 ] ].width;
-				const height = channelData[ cscSet.idx[ 0 ] ].height;
-				const numComp = 3;
-				const numFullBlocksX = Math.floor( width / 8.0 );
-				const numBlocksX = Math.ceil( width / 8.0 );
-				const numBlocksY = Math.ceil( height / 8.0 );
-				const leftoverX = width - ( numBlocksX - 1 ) * 8;
-				const leftoverY = height - ( numBlocksY - 1 ) * 8;
-				const currAcComp = {
-					value: 0
-				};
-				const currDcComp = new Array( numComp );
-				const dctData = new Array( numComp );
-				const halfZigBlock = new Array( numComp );
-				const rowBlock = new Array( numComp );
-				const rowOffsets = new Array( numComp );
-				for ( let comp = 0; comp < numComp; ++ comp ) {
-
-					rowOffsets[ comp ] = rowPtrs[ cscSet.idx[ comp ] ];
-					currDcComp[ comp ] = comp < 1 ? 0 : currDcComp[ comp - 1 ] + numBlocksX * numBlocksY;
-					dctData[ comp ] = new Float32Array( 64 );
-					halfZigBlock[ comp ] = new Uint16Array( 64 );
-					rowBlock[ comp ] = new Uint16Array( numBlocksX * 64 );
-
-				}
-
-				for ( let blocky = 0; blocky < numBlocksY; ++ blocky ) {
-
-					let maxY = 8;
-					if ( blocky == numBlocksY - 1 ) maxY = leftoverY;
-					let maxX = 8;
-					for ( let blockx = 0; blockx < numBlocksX; ++ blockx ) {
-
-						if ( blockx == numBlocksX - 1 ) maxX = leftoverX;
-						for ( let comp = 0; comp < numComp; ++ comp ) {
-
-							halfZigBlock[ comp ].fill( 0 );
-
-							// set block DC component
-							halfZigBlock[ comp ][ 0 ] = dcBuffer[ currDcComp[ comp ] ++ ];
-							// set block AC components
-							unRleAC( currAcComp, acBuffer, halfZigBlock[ comp ] );
-
-							// UnZigZag block to float
-							unZigZag( halfZigBlock[ comp ], dctData[ comp ] );
-							// decode float dct
-							dctInverse( dctData[ comp ] );
-
-						}
-
-						if ( numComp == 3 ) {
-
-							csc709Inverse( dctData );
-
-						}
-
-						for ( let comp = 0; comp < numComp; ++ comp ) {
-
-							convertToHalf( dctData[ comp ], rowBlock[ comp ], blockx * 64 );
-
-						}
-
-					} // blockx
-
-					let offset = 0;
-					for ( let comp = 0; comp < numComp; ++ comp ) {
-
-						const type = channelData[ cscSet.idx[ comp ] ].type;
-						for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) {
-
-							offset = rowOffsets[ comp ][ y ];
-							for ( let blockx = 0; blockx < numFullBlocksX; ++ blockx ) {
-
-								const src = blockx * 64 + ( y & 0x7 ) * 8;
-								dataView.setUint16( offset + 0 * INT16_SIZE * type, rowBlock[ comp ][ src + 0 ], true );
-								dataView.setUint16( offset + 1 * INT16_SIZE * type, rowBlock[ comp ][ src + 1 ], true );
-								dataView.setUint16( offset + 2 * INT16_SIZE * type, rowBlock[ comp ][ src + 2 ], true );
-								dataView.setUint16( offset + 3 * INT16_SIZE * type, rowBlock[ comp ][ src + 3 ], true );
-								dataView.setUint16( offset + 4 * INT16_SIZE * type, rowBlock[ comp ][ src + 4 ], true );
-								dataView.setUint16( offset + 5 * INT16_SIZE * type, rowBlock[ comp ][ src + 5 ], true );
-								dataView.setUint16( offset + 6 * INT16_SIZE * type, rowBlock[ comp ][ src + 6 ], true );
-								dataView.setUint16( offset + 7 * INT16_SIZE * type, rowBlock[ comp ][ src + 7 ], true );
-								offset += 8 * INT16_SIZE * type;
-
-							}
-
-						}
-
-						// handle partial X blocks
-						if ( numFullBlocksX != numBlocksX ) {
-
-							for ( let y = 8 * blocky; y < 8 * blocky + maxY; ++ y ) {
-
-								const offset = rowOffsets[ comp ][ y ] + 8 * numFullBlocksX * INT16_SIZE * type;
-								const src = numFullBlocksX * 64 + ( y & 0x7 ) * 8;
-								for ( let x = 0; x < maxX; ++ x ) {
-
-									dataView.setUint16( offset + x * INT16_SIZE * type, rowBlock[ comp ][ src + x ], true );
-
-								}
-
-							}
-
-						}
-
-					} // comp
-
-				} // blocky
-
-				const halfRow = new Uint16Array( width );
-				dataView = new DataView( outBuffer.buffer );
-
-				// convert channels back to float, if needed
-				for ( let comp = 0; comp < numComp; ++ comp ) {
-
-					channelData[ cscSet.idx[ comp ] ].decoded = true;
-					const type = channelData[ cscSet.idx[ comp ] ].type;
-					if ( channelData[ comp ].type != 2 ) continue;
-					for ( let y = 0; y < height; ++ y ) {
-
-						const offset = rowOffsets[ comp ][ y ];
-						for ( let x = 0; x < width; ++ x ) {
-
-							halfRow[ x ] = dataView.getUint16( offset + x * INT16_SIZE * type, true );
-
-						}
-
-						for ( let x = 0; x < width; ++ x ) {
-
-							dataView.setFloat32( offset + x * INT16_SIZE * type, decodeFloat16( halfRow[ x ] ), true );
-
-						}
-
-					}
-
-				}
-
-			}
-
-			function unRleAC( currAcComp, acBuffer, halfZigBlock ) {
-
-				let acValue;
-				let dctComp = 1;
-				while ( dctComp < 64 ) {
-
-					acValue = acBuffer[ currAcComp.value ];
-					if ( acValue == 0xff00 ) {
-
-						dctComp = 64;
-
-					} else if ( acValue >> 8 == 0xff ) {
-
-						dctComp += acValue & 0xff;
-
-					} else {
-
-						halfZigBlock[ dctComp ] = acValue;
-						dctComp ++;
-
-					}
-
-					currAcComp.value ++;
-
-				}
-
-			}
-
-			function unZigZag( src, dst ) {
-
-				dst[ 0 ] = decodeFloat16( src[ 0 ] );
-				dst[ 1 ] = decodeFloat16( src[ 1 ] );
-				dst[ 2 ] = decodeFloat16( src[ 5 ] );
-				dst[ 3 ] = decodeFloat16( src[ 6 ] );
-				dst[ 4 ] = decodeFloat16( src[ 14 ] );
-				dst[ 5 ] = decodeFloat16( src[ 15 ] );
-				dst[ 6 ] = decodeFloat16( src[ 27 ] );
-				dst[ 7 ] = decodeFloat16( src[ 28 ] );
-				dst[ 8 ] = decodeFloat16( src[ 2 ] );
-				dst[ 9 ] = decodeFloat16( src[ 4 ] );
-				dst[ 10 ] = decodeFloat16( src[ 7 ] );
-				dst[ 11 ] = decodeFloat16( src[ 13 ] );
-				dst[ 12 ] = decodeFloat16( src[ 16 ] );
-				dst[ 13 ] = decodeFloat16( src[ 26 ] );
-				dst[ 14 ] = decodeFloat16( src[ 29 ] );
-				dst[ 15 ] = decodeFloat16( src[ 42 ] );
-				dst[ 16 ] = decodeFloat16( src[ 3 ] );
-				dst[ 17 ] = decodeFloat16( src[ 8 ] );
-				dst[ 18 ] = decodeFloat16( src[ 12 ] );
-				dst[ 19 ] = decodeFloat16( src[ 17 ] );
-				dst[ 20 ] = decodeFloat16( src[ 25 ] );
-				dst[ 21 ] = decodeFloat16( src[ 30 ] );
-				dst[ 22 ] = decodeFloat16( src[ 41 ] );
-				dst[ 23 ] = decodeFloat16( src[ 43 ] );
-				dst[ 24 ] = decodeFloat16( src[ 9 ] );
-				dst[ 25 ] = decodeFloat16( src[ 11 ] );
-				dst[ 26 ] = decodeFloat16( src[ 18 ] );
-				dst[ 27 ] = decodeFloat16( src[ 24 ] );
-				dst[ 28 ] = decodeFloat16( src[ 31 ] );
-				dst[ 29 ] = decodeFloat16( src[ 40 ] );
-				dst[ 30 ] = decodeFloat16( src[ 44 ] );
-				dst[ 31 ] = decodeFloat16( src[ 53 ] );
-				dst[ 32 ] = decodeFloat16( src[ 10 ] );
-				dst[ 33 ] = decodeFloat16( src[ 19 ] );
-				dst[ 34 ] = decodeFloat16( src[ 23 ] );
-				dst[ 35 ] = decodeFloat16( src[ 32 ] );
-				dst[ 36 ] = decodeFloat16( src[ 39 ] );
-				dst[ 37 ] = decodeFloat16( src[ 45 ] );
-				dst[ 38 ] = decodeFloat16( src[ 52 ] );
-				dst[ 39 ] = decodeFloat16( src[ 54 ] );
-				dst[ 40 ] = decodeFloat16( src[ 20 ] );
-				dst[ 41 ] = decodeFloat16( src[ 22 ] );
-				dst[ 42 ] = decodeFloat16( src[ 33 ] );
-				dst[ 43 ] = decodeFloat16( src[ 38 ] );
-				dst[ 44 ] = decodeFloat16( src[ 46 ] );
-				dst[ 45 ] = decodeFloat16( src[ 51 ] );
-				dst[ 46 ] = decodeFloat16( src[ 55 ] );
-				dst[ 47 ] = decodeFloat16( src[ 60 ] );
-				dst[ 48 ] = decodeFloat16( src[ 21 ] );
-				dst[ 49 ] = decodeFloat16( src[ 34 ] );
-				dst[ 50 ] = decodeFloat16( src[ 37 ] );
-				dst[ 51 ] = decodeFloat16( src[ 47 ] );
-				dst[ 52 ] = decodeFloat16( src[ 50 ] );
-				dst[ 53 ] = decodeFloat16( src[ 56 ] );
-				dst[ 54 ] = decodeFloat16( src[ 59 ] );
-				dst[ 55 ] = decodeFloat16( src[ 61 ] );
-				dst[ 56 ] = decodeFloat16( src[ 35 ] );
-				dst[ 57 ] = decodeFloat16( src[ 36 ] );
-				dst[ 58 ] = decodeFloat16( src[ 48 ] );
-				dst[ 59 ] = decodeFloat16( src[ 49 ] );
-				dst[ 60 ] = decodeFloat16( src[ 57 ] );
-				dst[ 61 ] = decodeFloat16( src[ 58 ] );
-				dst[ 62 ] = decodeFloat16( src[ 62 ] );
-				dst[ 63 ] = decodeFloat16( src[ 63 ] );
-
-			}
-
-			function dctInverse( data ) {
-
-				const a = 0.5 * Math.cos( 3.14159 / 4.0 );
-				const b = 0.5 * Math.cos( 3.14159 / 16.0 );
-				const c = 0.5 * Math.cos( 3.14159 / 8.0 );
-				const d = 0.5 * Math.cos( 3.0 * 3.14159 / 16.0 );
-				const e = 0.5 * Math.cos( 5.0 * 3.14159 / 16.0 );
-				const f = 0.5 * Math.cos( 3.0 * 3.14159 / 8.0 );
-				const g = 0.5 * Math.cos( 7.0 * 3.14159 / 16.0 );
-				const alpha = new Array( 4 );
-				const beta = new Array( 4 );
-				const theta = new Array( 4 );
-				const gamma = new Array( 4 );
-				for ( let row = 0; row < 8; ++ row ) {
-
-					const rowPtr = row * 8;
-					alpha[ 0 ] = c * data[ rowPtr + 2 ];
-					alpha[ 1 ] = f * data[ rowPtr + 2 ];
-					alpha[ 2 ] = c * data[ rowPtr + 6 ];
-					alpha[ 3 ] = f * data[ rowPtr + 6 ];
-					beta[ 0 ] = b * data[ rowPtr + 1 ] + d * data[ rowPtr + 3 ] + e * data[ rowPtr + 5 ] + g * data[ rowPtr + 7 ];
-					beta[ 1 ] = d * data[ rowPtr + 1 ] - g * data[ rowPtr + 3 ] - b * data[ rowPtr + 5 ] - e * data[ rowPtr + 7 ];
-					beta[ 2 ] = e * data[ rowPtr + 1 ] - b * data[ rowPtr + 3 ] + g * data[ rowPtr + 5 ] + d * data[ rowPtr + 7 ];
-					beta[ 3 ] = g * data[ rowPtr + 1 ] - e * data[ rowPtr + 3 ] + d * data[ rowPtr + 5 ] - b * data[ rowPtr + 7 ];
-					theta[ 0 ] = a * ( data[ rowPtr + 0 ] + data[ rowPtr + 4 ] );
-					theta[ 3 ] = a * ( data[ rowPtr + 0 ] - data[ rowPtr + 4 ] );
-					theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ];
-					theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ];
-					gamma[ 0 ] = theta[ 0 ] + theta[ 1 ];
-					gamma[ 1 ] = theta[ 3 ] + theta[ 2 ];
-					gamma[ 2 ] = theta[ 3 ] - theta[ 2 ];
-					gamma[ 3 ] = theta[ 0 ] - theta[ 1 ];
-					data[ rowPtr + 0 ] = gamma[ 0 ] + beta[ 0 ];
-					data[ rowPtr + 1 ] = gamma[ 1 ] + beta[ 1 ];
-					data[ rowPtr + 2 ] = gamma[ 2 ] + beta[ 2 ];
-					data[ rowPtr + 3 ] = gamma[ 3 ] + beta[ 3 ];
-					data[ rowPtr + 4 ] = gamma[ 3 ] - beta[ 3 ];
-					data[ rowPtr + 5 ] = gamma[ 2 ] - beta[ 2 ];
-					data[ rowPtr + 6 ] = gamma[ 1 ] - beta[ 1 ];
-					data[ rowPtr + 7 ] = gamma[ 0 ] - beta[ 0 ];
-
-				}
-
-				for ( let column = 0; column < 8; ++ column ) {
-
-					alpha[ 0 ] = c * data[ 16 + column ];
-					alpha[ 1 ] = f * data[ 16 + column ];
-					alpha[ 2 ] = c * data[ 48 + column ];
-					alpha[ 3 ] = f * data[ 48 + column ];
-					beta[ 0 ] = b * data[ 8 + column ] + d * data[ 24 + column ] + e * data[ 40 + column ] + g * data[ 56 + column ];
-					beta[ 1 ] = d * data[ 8 + column ] - g * data[ 24 + column ] - b * data[ 40 + column ] - e * data[ 56 + column ];
-					beta[ 2 ] = e * data[ 8 + column ] - b * data[ 24 + column ] + g * data[ 40 + column ] + d * data[ 56 + column ];
-					beta[ 3 ] = g * data[ 8 + column ] - e * data[ 24 + column ] + d * data[ 40 + column ] - b * data[ 56 + column ];
-					theta[ 0 ] = a * ( data[ column ] + data[ 32 + column ] );
-					theta[ 3 ] = a * ( data[ column ] - data[ 32 + column ] );
-					theta[ 1 ] = alpha[ 0 ] + alpha[ 3 ];
-					theta[ 2 ] = alpha[ 1 ] - alpha[ 2 ];
-					gamma[ 0 ] = theta[ 0 ] + theta[ 1 ];
-					gamma[ 1 ] = theta[ 3 ] + theta[ 2 ];
-					gamma[ 2 ] = theta[ 3 ] - theta[ 2 ];
-					gamma[ 3 ] = theta[ 0 ] - theta[ 1 ];
-					data[ 0 + column ] = gamma[ 0 ] + beta[ 0 ];
-					data[ 8 + column ] = gamma[ 1 ] + beta[ 1 ];
-					data[ 16 + column ] = gamma[ 2 ] + beta[ 2 ];
-					data[ 24 + column ] = gamma[ 3 ] + beta[ 3 ];
-					data[ 32 + column ] = gamma[ 3 ] - beta[ 3 ];
-					data[ 40 + column ] = gamma[ 2 ] - beta[ 2 ];
-					data[ 48 + column ] = gamma[ 1 ] - beta[ 1 ];
-					data[ 56 + column ] = gamma[ 0 ] - beta[ 0 ];
-
-				}
-
-			}
-
-			function csc709Inverse( data ) {
-
-				for ( let i = 0; i < 64; ++ i ) {
-
-					const y = data[ 0 ][ i ];
-					const cb = data[ 1 ][ i ];
-					const cr = data[ 2 ][ i ];
-					data[ 0 ][ i ] = y + 1.5747 * cr;
-					data[ 1 ][ i ] = y - 0.1873 * cb - 0.4682 * cr;
-					data[ 2 ][ i ] = y + 1.8556 * cb;
-
-				}
-
-			}
-
-			function convertToHalf( src, dst, idx ) {
-
-				for ( let i = 0; i < 64; ++ i ) {
-
-					dst[ idx + i ] = THREE.DataUtils.toHalfFloat( toLinear( src[ i ] ) );
-
-				}
-
-			}
-
-			function toLinear( float ) {
-
-				if ( float <= 1 ) {
-
-					return Math.sign( float ) * Math.pow( Math.abs( float ), 2.2 );
-
-				} else {
-
-					return Math.sign( float ) * Math.pow( logBase, Math.abs( float ) - 1.0 );
-
-				}
-
-			}
-
-			function uncompressRAW( info ) {
-
-				return new DataView( info.array.buffer, info.offset.value, info.size );
-
-			}
-
-			function uncompressRLE( info ) {
-
-				const compressed = info.viewer.buffer.slice( info.offset.value, info.offset.value + info.size );
-				const rawBuffer = new Uint8Array( decodeRunLength( compressed ) );
-				const tmpBuffer = new Uint8Array( rawBuffer.length );
-				predictor( rawBuffer ); // revert predictor
-
-				interleaveScalar( rawBuffer, tmpBuffer ); // interleave pixels
-
-				return new DataView( tmpBuffer.buffer );
-
-			}
-
-			function uncompressZIP( info ) {
-
-				const compressed = info.array.slice( info.offset.value, info.offset.value + info.size );
-				if ( typeof fflate === 'undefined' ) {
-
-					console.error( 'THREE.EXRLoader: External library fflate.min.js required.' );
-
-				}
-
-				const rawBuffer = fflate.unzlibSync( compressed ); // eslint-disable-line no-undef
-				const tmpBuffer = new Uint8Array( rawBuffer.length );
-				predictor( rawBuffer ); // revert predictor
-
-				interleaveScalar( rawBuffer, tmpBuffer ); // interleave pixels
-
-				return new DataView( tmpBuffer.buffer );
-
-			}
-
-			function uncompressPIZ( info ) {
-
-				const inDataView = info.viewer;
-				const inOffset = {
-					value: info.offset.value
-				};
-				const outBuffer = new Uint16Array( info.width * info.scanlineBlockSize * ( info.channels * info.type ) );
-				const bitmap = new Uint8Array( BITMAP_SIZE );
-
-				// Setup channel info
-				let outBufferEnd = 0;
-				const pizChannelData = new Array( info.channels );
-				for ( let i = 0; i < info.channels; i ++ ) {
-
-					pizChannelData[ i ] = {};
-					pizChannelData[ i ][ 'start' ] = outBufferEnd;
-					pizChannelData[ i ][ 'end' ] = pizChannelData[ i ][ 'start' ];
-					pizChannelData[ i ][ 'nx' ] = info.width;
-					pizChannelData[ i ][ 'ny' ] = info.lines;
-					pizChannelData[ i ][ 'size' ] = info.type;
-					outBufferEnd += pizChannelData[ i ].nx * pizChannelData[ i ].ny * pizChannelData[ i ].size;
-
-				}
-
-				// Read range compression data
-
-				const minNonZero = parseUint16( inDataView, inOffset );
-				const maxNonZero = parseUint16( inDataView, inOffset );
-				if ( maxNonZero >= BITMAP_SIZE ) {
-
-					throw new Error( 'Something is wrong with PIZ_COMPRESSION BITMAP_SIZE' );
-
-				}
-
-				if ( minNonZero <= maxNonZero ) {
-
-					for ( let i = 0; i < maxNonZero - minNonZero + 1; i ++ ) {
-
-						bitmap[ i + minNonZero ] = parseUint8( inDataView, inOffset );
-
-					}
-
-				}
-
-				// Reverse LUT
-				const lut = new Uint16Array( USHORT_RANGE );
-				const maxValue = reverseLutFromBitmap( bitmap, lut );
-				const length = parseUint32( inDataView, inOffset );
-
-				// Huffman decoding
-				hufUncompress( info.array, inDataView, inOffset, length, outBuffer, outBufferEnd );
-
-				// Wavelet decoding
-				for ( let i = 0; i < info.channels; ++ i ) {
-
-					const cd = pizChannelData[ i ];
-					for ( let j = 0; j < pizChannelData[ i ].size; ++ j ) {
-
-						wav2Decode( outBuffer, cd.start + j, cd.nx, cd.size, cd.ny, cd.nx * cd.size, maxValue );
-
-					}
-
-				}
-
-				// Expand the pixel data to their original range
-				applyLut( lut, outBuffer, outBufferEnd );
-
-				// Rearrange the pixel data into the format expected by the caller.
-				let tmpOffset = 0;
-				const tmpBuffer = new Uint8Array( outBuffer.buffer.byteLength );
-				for ( let y = 0; y < info.lines; y ++ ) {
-
-					for ( let c = 0; c < info.channels; c ++ ) {
-
-						const cd = pizChannelData[ c ];
-						const n = cd.nx * cd.size;
-						const cp = new Uint8Array( outBuffer.buffer, cd.end * INT16_SIZE, n * INT16_SIZE );
-						tmpBuffer.set( cp, tmpOffset );
-						tmpOffset += n * INT16_SIZE;
-						cd.end += n;
-
-					}
-
-				}
-
-				return new DataView( tmpBuffer.buffer );
-
-			}
-
-			function uncompressPXR( info ) {
-
-				const compressed = info.array.slice( info.offset.value, info.offset.value + info.size );
-				if ( typeof fflate === 'undefined' ) {
-
-					console.error( 'THREE.EXRLoader: External library fflate.min.js required.' );
-
-				}
-
-				const rawBuffer = fflate.unzlibSync( compressed ); // eslint-disable-line no-undef
-
-				const sz = info.lines * info.channels * info.width;
-				const tmpBuffer = info.type == 1 ? new Uint16Array( sz ) : new Uint32Array( sz );
-				let tmpBufferEnd = 0;
-				let writePtr = 0;
-				const ptr = new Array( 4 );
-				for ( let y = 0; y < info.lines; y ++ ) {
-
-					for ( let c = 0; c < info.channels; c ++ ) {
-
-						let pixel = 0;
-						switch ( info.type ) {
-
-							case 1:
-								ptr[ 0 ] = tmpBufferEnd;
-								ptr[ 1 ] = ptr[ 0 ] + info.width;
-								tmpBufferEnd = ptr[ 1 ] + info.width;
-								for ( let j = 0; j < info.width; ++ j ) {
-
-									const diff = rawBuffer[ ptr[ 0 ] ++ ] << 8 | rawBuffer[ ptr[ 1 ] ++ ];
-									pixel += diff;
-									tmpBuffer[ writePtr ] = pixel;
-									writePtr ++;
-
-								}
-
-								break;
-							case 2:
-								ptr[ 0 ] = tmpBufferEnd;
-								ptr[ 1 ] = ptr[ 0 ] + info.width;
-								ptr[ 2 ] = ptr[ 1 ] + info.width;
-								tmpBufferEnd = ptr[ 2 ] + info.width;
-								for ( let j = 0; j < info.width; ++ j ) {
-
-									const diff = rawBuffer[ ptr[ 0 ] ++ ] << 24 | rawBuffer[ ptr[ 1 ] ++ ] << 16 | rawBuffer[ ptr[ 2 ] ++ ] << 8;
-									pixel += diff;
-									tmpBuffer[ writePtr ] = pixel;
-									writePtr ++;
-
-								}
-
-								break;
-
-						}
-
-					}
-
-				}
-
-				return new DataView( tmpBuffer.buffer );
-
-			}
-
-			function uncompressDWA( info ) {
-
-				const inDataView = info.viewer;
-				const inOffset = {
-					value: info.offset.value
-				};
-				const outBuffer = new Uint8Array( info.width * info.lines * ( info.channels * info.type * INT16_SIZE ) );
-
-				// Read compression header information
-				const dwaHeader = {
-					version: parseInt64( inDataView, inOffset ),
-					unknownUncompressedSize: parseInt64( inDataView, inOffset ),
-					unknownCompressedSize: parseInt64( inDataView, inOffset ),
-					acCompressedSize: parseInt64( inDataView, inOffset ),
-					dcCompressedSize: parseInt64( inDataView, inOffset ),
-					rleCompressedSize: parseInt64( inDataView, inOffset ),
-					rleUncompressedSize: parseInt64( inDataView, inOffset ),
-					rleRawSize: parseInt64( inDataView, inOffset ),
-					totalAcUncompressedCount: parseInt64( inDataView, inOffset ),
-					totalDcUncompressedCount: parseInt64( inDataView, inOffset ),
-					acCompression: parseInt64( inDataView, inOffset )
-				};
-				if ( dwaHeader.version < 2 ) throw new Error( 'EXRLoader.parse: ' + EXRHeader.compression + ' version ' + dwaHeader.version + ' is unsupported' );
-
-				// Read channel ruleset information
-				const channelRules = new Array();
-				let ruleSize = parseUint16( inDataView, inOffset ) - INT16_SIZE;
-				while ( ruleSize > 0 ) {
-
-					const name = parseNullTerminatedString( inDataView.buffer, inOffset );
-					const value = parseUint8( inDataView, inOffset );
-					const compression = value >> 2 & 3;
-					const csc = ( value >> 4 ) - 1;
-					const index = new Int8Array( [ csc ] )[ 0 ];
-					const type = parseUint8( inDataView, inOffset );
-					channelRules.push( {
-						name: name,
-						index: index,
-						type: type,
-						compression: compression
-					} );
-					ruleSize -= name.length + 3;
-
-				}
-
-				// Classify channels
-				const channels = EXRHeader.channels;
-				const channelData = new Array( info.channels );
-				for ( let i = 0; i < info.channels; ++ i ) {
-
-					const cd = channelData[ i ] = {};
-					const channel = channels[ i ];
-					cd.name = channel.name;
-					cd.compression = UNKNOWN;
-					cd.decoded = false;
-					cd.type = channel.pixelType;
-					cd.pLinear = channel.pLinear;
-					cd.width = info.width;
-					cd.height = info.lines;
-
-				}
-
-				const cscSet = {
-					idx: new Array( 3 )
-				};
-				for ( let offset = 0; offset < info.channels; ++ offset ) {
-
-					const cd = channelData[ offset ];
-					for ( let i = 0; i < channelRules.length; ++ i ) {
-
-						const rule = channelRules[ i ];
-						if ( cd.name == rule.name ) {
-
-							cd.compression = rule.compression;
-							if ( rule.index >= 0 ) {
-
-								cscSet.idx[ rule.index ] = offset;
-
-							}
-
-							cd.offset = offset;
-
-						}
-
-					}
-
-				}
-
-				let acBuffer, dcBuffer, rleBuffer;
-
-				// Read DCT - AC component data
-				if ( dwaHeader.acCompressedSize > 0 ) {
-
-					switch ( dwaHeader.acCompression ) {
-
-						case STATIC_HUFFMAN:
-							acBuffer = new Uint16Array( dwaHeader.totalAcUncompressedCount );
-							hufUncompress( info.array, inDataView, inOffset, dwaHeader.acCompressedSize, acBuffer, dwaHeader.totalAcUncompressedCount );
-							break;
-						case DEFLATE:
-							const compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.totalAcUncompressedCount );
-							const data = fflate.unzlibSync( compressed ); // eslint-disable-line no-undef
-							acBuffer = new Uint16Array( data.buffer );
-							inOffset.value += dwaHeader.totalAcUncompressedCount;
-							break;
-
-					}
-
-				}
-
-				// Read DCT - DC component data
-				if ( dwaHeader.dcCompressedSize > 0 ) {
-
-					const zlibInfo = {
-						array: info.array,
-						offset: inOffset,
-						size: dwaHeader.dcCompressedSize
-					};
-					dcBuffer = new Uint16Array( uncompressZIP( zlibInfo ).buffer );
-					inOffset.value += dwaHeader.dcCompressedSize;
-
-				}
-
-				// Read RLE compressed data
-				if ( dwaHeader.rleRawSize > 0 ) {
-
-					const compressed = info.array.slice( inOffset.value, inOffset.value + dwaHeader.rleCompressedSize );
-					const data = fflate.unzlibSync( compressed ); // eslint-disable-line no-undef
-					rleBuffer = decodeRunLength( data.buffer );
-					inOffset.value += dwaHeader.rleCompressedSize;
-
-				}
-
-				// Prepare outbuffer data offset
-				let outBufferEnd = 0;
-				const rowOffsets = new Array( channelData.length );
-				for ( let i = 0; i < rowOffsets.length; ++ i ) {
-
-					rowOffsets[ i ] = new Array();
-
-				}
-
-				for ( let y = 0; y < info.lines; ++ y ) {
-
-					for ( let chan = 0; chan < channelData.length; ++ chan ) {
-
-						rowOffsets[ chan ].push( outBufferEnd );
-						outBufferEnd += channelData[ chan ].width * info.type * INT16_SIZE;
-
-					}
-
-				}
-
-				// Lossy DCT decode RGB channels
-				lossyDctDecode( cscSet, rowOffsets, channelData, acBuffer, dcBuffer, outBuffer );
-
-				// Decode other channels
-				for ( let i = 0; i < channelData.length; ++ i ) {
-
-					const cd = channelData[ i ];
-					if ( cd.decoded ) continue;
-					switch ( cd.compression ) {
-
-						case RLE:
-							let row = 0;
-							let rleOffset = 0;
-							for ( let y = 0; y < info.lines; ++ y ) {
-
-								let rowOffsetBytes = rowOffsets[ i ][ row ];
-								for ( let x = 0; x < cd.width; ++ x ) {
-
-									for ( let byte = 0; byte < INT16_SIZE * cd.type; ++ byte ) {
-
-										outBuffer[ rowOffsetBytes ++ ] = rleBuffer[ rleOffset + byte * cd.width * cd.height ];
-
-									}
-
-									rleOffset ++;
-
-								}
-
-								row ++;
-
-							}
-
-							break;
-						case LOSSY_DCT: // skip
-
-						default:
-							throw new Error( 'EXRLoader.parse: unsupported channel compression' );
-
-					}
-
-				}
-
-				return new DataView( outBuffer.buffer );
-
-			}
-
-			function parseNullTerminatedString( buffer, offset ) {
-
-				const uintBuffer = new Uint8Array( buffer );
-				let endOffset = 0;
-				while ( uintBuffer[ offset.value + endOffset ] != 0 ) {
-
-					endOffset += 1;
-
-				}
-
-				const stringValue = new TextDecoder().decode( uintBuffer.slice( offset.value, offset.value + endOffset ) );
-				offset.value = offset.value + endOffset + 1;
-				return stringValue;
-
-			}
-
-			function parseFixedLengthString( buffer, offset, size ) {
-
-				const stringValue = new TextDecoder().decode( new Uint8Array( buffer ).slice( offset.value, offset.value + size ) );
-				offset.value = offset.value + size;
-				return stringValue;
-
-			}
-
-			function parseRational( dataView, offset ) {
-
-				const x = parseInt32( dataView, offset );
-				const y = parseUint32( dataView, offset );
-				return [ x, y ];
-
-			}
-
-			function parseTimecode( dataView, offset ) {
-
-				const x = parseUint32( dataView, offset );
-				const y = parseUint32( dataView, offset );
-				return [ x, y ];
-
-			}
-
-			function parseInt32( dataView, offset ) {
-
-				const Int32 = dataView.getInt32( offset.value, true );
-				offset.value = offset.value + INT32_SIZE;
-				return Int32;
-
-			}
-
-			function parseUint32( dataView, offset ) {
-
-				const Uint32 = dataView.getUint32( offset.value, true );
-				offset.value = offset.value + INT32_SIZE;
-				return Uint32;
-
-			}
-
-			function parseUint8Array( uInt8Array, offset ) {
-
-				const Uint8 = uInt8Array[ offset.value ];
-				offset.value = offset.value + INT8_SIZE;
-				return Uint8;
-
-			}
-
-			function parseUint8( dataView, offset ) {
-
-				const Uint8 = dataView.getUint8( offset.value );
-				offset.value = offset.value + INT8_SIZE;
-				return Uint8;
-
-			}
-
-			const parseInt64 = function ( dataView, offset ) {
-
-				const Int64 = Number( dataView.getBigInt64( offset.value, true ) );
-				offset.value += ULONG_SIZE;
-				return Int64;
-
-			};
-
-			function parseFloat32( dataView, offset ) {
-
-				const float = dataView.getFloat32( offset.value, true );
-				offset.value += FLOAT32_SIZE;
-				return float;
-
-			}
-
-			function decodeFloat32( dataView, offset ) {
-
-				return THREE.DataUtils.toHalfFloat( parseFloat32( dataView, offset ) );
-
-			}
-
-			// https://stackoverflow.com/questions/5678432/decompressing-half-precision-floats-in-javascript
-			function decodeFloat16( binary ) {
-
-				const exponent = ( binary & 0x7C00 ) >> 10,
-					fraction = binary & 0x03FF;
-				return ( binary >> 15 ? - 1 : 1 ) * ( exponent ? exponent === 0x1F ? fraction ? NaN : Infinity : Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 ) : 6.103515625e-5 * ( fraction / 0x400 ) );
-
-			}
-
-			function parseUint16( dataView, offset ) {
-
-				const Uint16 = dataView.getUint16( offset.value, true );
-				offset.value += INT16_SIZE;
-				return Uint16;
-
-			}
-
-			function parseFloat16( buffer, offset ) {
-
-				return decodeFloat16( parseUint16( buffer, offset ) );
-
-			}
-
-			function parseChlist( dataView, buffer, offset, size ) {
-
-				const startOffset = offset.value;
-				const channels = [];
-				while ( offset.value < startOffset + size - 1 ) {
-
-					const name = parseNullTerminatedString( buffer, offset );
-					const pixelType = parseInt32( dataView, offset );
-					const pLinear = parseUint8( dataView, offset );
-					offset.value += 3; // reserved, three chars
-					const xSampling = parseInt32( dataView, offset );
-					const ySampling = parseInt32( dataView, offset );
-					channels.push( {
-						name: name,
-						pixelType: pixelType,
-						pLinear: pLinear,
-						xSampling: xSampling,
-						ySampling: ySampling
-					} );
-
-				}
-
-				offset.value += 1;
-				return channels;
-
-			}
-
-			function parseChromaticities( dataView, offset ) {
-
-				const redX = parseFloat32( dataView, offset );
-				const redY = parseFloat32( dataView, offset );
-				const greenX = parseFloat32( dataView, offset );
-				const greenY = parseFloat32( dataView, offset );
-				const blueX = parseFloat32( dataView, offset );
-				const blueY = parseFloat32( dataView, offset );
-				const whiteX = parseFloat32( dataView, offset );
-				const whiteY = parseFloat32( dataView, offset );
-				return {
-					redX: redX,
-					redY: redY,
-					greenX: greenX,
-					greenY: greenY,
-					blueX: blueX,
-					blueY: blueY,
-					whiteX: whiteX,
-					whiteY: whiteY
-				};
-
-			}
-
-			function parseCompression( dataView, offset ) {
-
-				const compressionCodes = [ 'NO_COMPRESSION', 'RLE_COMPRESSION', 'ZIPS_COMPRESSION', 'ZIP_COMPRESSION', 'PIZ_COMPRESSION', 'PXR24_COMPRESSION', 'B44_COMPRESSION', 'B44A_COMPRESSION', 'DWAA_COMPRESSION', 'DWAB_COMPRESSION' ];
-				const compression = parseUint8( dataView, offset );
-				return compressionCodes[ compression ];
-
-			}
-
-			function parseBox2i( dataView, offset ) {
-
-				const xMin = parseUint32( dataView, offset );
-				const yMin = parseUint32( dataView, offset );
-				const xMax = parseUint32( dataView, offset );
-				const yMax = parseUint32( dataView, offset );
-				return {
-					xMin: xMin,
-					yMin: yMin,
-					xMax: xMax,
-					yMax: yMax
-				};
-
-			}
-
-			function parseLineOrder( dataView, offset ) {
-
-				const lineOrders = [ 'INCREASING_Y' ];
-				const lineOrder = parseUint8( dataView, offset );
-				return lineOrders[ lineOrder ];
-
-			}
-
-			function parseV2f( dataView, offset ) {
-
-				const x = parseFloat32( dataView, offset );
-				const y = parseFloat32( dataView, offset );
-				return [ x, y ];
-
-			}
-
-			function parseV3f( dataView, offset ) {
-
-				const x = parseFloat32( dataView, offset );
-				const y = parseFloat32( dataView, offset );
-				const z = parseFloat32( dataView, offset );
-				return [ x, y, z ];
-
-			}
-
-			function parseValue( dataView, buffer, offset, type, size ) {
-
-				if ( type === 'string' || type === 'stringvector' || type === 'iccProfile' ) {
-
-					return parseFixedLengthString( buffer, offset, size );
-
-				} else if ( type === 'chlist' ) {
-
-					return parseChlist( dataView, buffer, offset, size );
-
-				} else if ( type === 'chromaticities' ) {
-
-					return parseChromaticities( dataView, offset );
-
-				} else if ( type === 'compression' ) {
-
-					return parseCompression( dataView, offset );
-
-				} else if ( type === 'box2i' ) {
-
-					return parseBox2i( dataView, offset );
-
-				} else if ( type === 'lineOrder' ) {
-
-					return parseLineOrder( dataView, offset );
-
-				} else if ( type === 'float' ) {
-
-					return parseFloat32( dataView, offset );
-
-				} else if ( type === 'v2f' ) {
-
-					return parseV2f( dataView, offset );
-
-				} else if ( type === 'v3f' ) {
-
-					return parseV3f( dataView, offset );
-
-				} else if ( type === 'int' ) {
-
-					return parseInt32( dataView, offset );
-
-				} else if ( type === 'rational' ) {
-
-					return parseRational( dataView, offset );
-
-				} else if ( type === 'timecode' ) {
-
-					return parseTimecode( dataView, offset );
-
-				} else if ( type === 'preview' ) {
-
-					offset.value += size;
-					return 'skipped';
-
-				} else {
-
-					offset.value += size;
-					return undefined;
-
-				}
-
-			}
-
-			function parseHeader( dataView, buffer, offset ) {
-
-				const EXRHeader = {};
-				if ( dataView.getUint32( 0, true ) != 20000630 ) {
-
-					// magic
-
-					throw new Error( 'THREE.EXRLoader: provided file doesn\'t appear to be in OpenEXR format.' );
-
-				}
-
-				EXRHeader.version = dataView.getUint8( 4 );
-				const spec = dataView.getUint8( 5 ); // fullMask
-
-				EXRHeader.spec = {
-					singleTile: !! ( spec & 2 ),
-					longName: !! ( spec & 4 ),
-					deepFormat: !! ( spec & 8 ),
-					multiPart: !! ( spec & 16 )
-				};
-
-				// start of header
-
-				offset.value = 8; // start at 8 - after pre-amble
-
-				let keepReading = true;
-				while ( keepReading ) {
-
-					const attributeName = parseNullTerminatedString( buffer, offset );
-					if ( attributeName == 0 ) {
-
-						keepReading = false;
-
-					} else {
-
-						const attributeType = parseNullTerminatedString( buffer, offset );
-						const attributeSize = parseUint32( dataView, offset );
-						const attributeValue = parseValue( dataView, buffer, offset, attributeType, attributeSize );
-						if ( attributeValue === undefined ) {
-
-							console.warn( `EXRLoader.parse: skipped unknown header attribute type \'${attributeType}\'.` );
-
-						} else {
-
-							EXRHeader[ attributeName ] = attributeValue;
-
-						}
-
-					}
-
-				}
-
-				if ( ( spec & ~ 0x04 ) != 0 ) {
-
-					// unsupported tiled, deep-image, multi-part
-
-					console.error( 'EXRHeader:', EXRHeader );
-					throw new Error( 'THREE.EXRLoader: provided file is currently unsupported.' );
-
-				}
-
-				return EXRHeader;
-
-			}
-
-			function setupDecoder( EXRHeader, dataView, uInt8Array, offset, outputType ) {
-
-				const EXRDecoder = {
-					size: 0,
-					viewer: dataView,
-					array: uInt8Array,
-					offset: offset,
-					width: EXRHeader.dataWindow.xMax - EXRHeader.dataWindow.xMin + 1,
-					height: EXRHeader.dataWindow.yMax - EXRHeader.dataWindow.yMin + 1,
-					channels: EXRHeader.channels.length,
-					bytesPerLine: null,
-					lines: null,
-					inputSize: null,
-					type: EXRHeader.channels[ 0 ].pixelType,
-					uncompress: null,
-					getter: null,
-					format: null,
-					encoding: null
-				};
-				switch ( EXRHeader.compression ) {
-
-					case 'NO_COMPRESSION':
-						EXRDecoder.lines = 1;
-						EXRDecoder.uncompress = uncompressRAW;
-						break;
-					case 'RLE_COMPRESSION':
-						EXRDecoder.lines = 1;
-						EXRDecoder.uncompress = uncompressRLE;
-						break;
-					case 'ZIPS_COMPRESSION':
-						EXRDecoder.lines = 1;
-						EXRDecoder.uncompress = uncompressZIP;
-						break;
-					case 'ZIP_COMPRESSION':
-						EXRDecoder.lines = 16;
-						EXRDecoder.uncompress = uncompressZIP;
-						break;
-					case 'PIZ_COMPRESSION':
-						EXRDecoder.lines = 32;
-						EXRDecoder.uncompress = uncompressPIZ;
-						break;
-					case 'PXR24_COMPRESSION':
-						EXRDecoder.lines = 16;
-						EXRDecoder.uncompress = uncompressPXR;
-						break;
-					case 'DWAA_COMPRESSION':
-						EXRDecoder.lines = 32;
-						EXRDecoder.uncompress = uncompressDWA;
-						break;
-					case 'DWAB_COMPRESSION':
-						EXRDecoder.lines = 256;
-						EXRDecoder.uncompress = uncompressDWA;
-						break;
-					default:
-						throw new Error( 'EXRLoader.parse: ' + EXRHeader.compression + ' is unsupported' );
-
-				}
-
-				EXRDecoder.scanlineBlockSize = EXRDecoder.lines;
-				if ( EXRDecoder.type == 1 ) {
-
-					// half
-					switch ( outputType ) {
-
-						case THREE.FloatType:
-							EXRDecoder.getter = parseFloat16;
-							EXRDecoder.inputSize = INT16_SIZE;
-							break;
-						case THREE.HalfFloatType:
-							EXRDecoder.getter = parseUint16;
-							EXRDecoder.inputSize = INT16_SIZE;
-							break;
-
-					}
-
-				} else if ( EXRDecoder.type == 2 ) {
-
-					// float
-					switch ( outputType ) {
-
-						case THREE.FloatType:
-							EXRDecoder.getter = parseFloat32;
-							EXRDecoder.inputSize = FLOAT32_SIZE;
-							break;
-						case THREE.HalfFloatType:
-							EXRDecoder.getter = decodeFloat32;
-							EXRDecoder.inputSize = FLOAT32_SIZE;
-
-					}
-
-				} else {
-
-					throw new Error( 'EXRLoader.parse: unsupported pixelType ' + EXRDecoder.type + ' for ' + EXRHeader.compression + '.' );
-
-				}
-
-				EXRDecoder.blockCount = ( EXRHeader.dataWindow.yMax + 1 ) / EXRDecoder.scanlineBlockSize;
-				for ( let i = 0; i < EXRDecoder.blockCount; i ++ ) parseInt64( dataView, offset ); // scanlineOffset
-
-				// we should be passed the scanline offset table, ready to start reading pixel data.
-
-				// RGB images will be converted to RGBA format, preventing software emulation in select devices.
-				EXRDecoder.outputChannels = EXRDecoder.channels == 3 ? 4 : EXRDecoder.channels;
-				const size = EXRDecoder.width * EXRDecoder.height * EXRDecoder.outputChannels;
-				switch ( outputType ) {
-
-					case THREE.FloatType:
-						EXRDecoder.byteArray = new Float32Array( size );
-
-						// Fill initially with 1s for the alpha value if the texture is not RGBA, RGB values will be overwritten
-						if ( EXRDecoder.channels < EXRDecoder.outputChannels ) EXRDecoder.byteArray.fill( 1, 0, size );
-						break;
-					case THREE.HalfFloatType:
-						EXRDecoder.byteArray = new Uint16Array( size );
-						if ( EXRDecoder.channels < EXRDecoder.outputChannels ) EXRDecoder.byteArray.fill( 0x3C00, 0, size ); // Uint16Array holds half float data, 0x3C00 is 1
-
-						break;
-					default:
-						console.error( 'THREE.EXRLoader: unsupported type: ', outputType );
-						break;
-
-				}
-
-				EXRDecoder.bytesPerLine = EXRDecoder.width * EXRDecoder.inputSize * EXRDecoder.channels;
-				if ( EXRDecoder.outputChannels == 4 ) {
-
-					EXRDecoder.format = THREE.RGBAFormat;
-					EXRDecoder.encoding = THREE.LinearEncoding;
-
-				} else {
-
-					EXRDecoder.format = THREE.RedFormat;
-					EXRDecoder.encoding = THREE.LinearEncoding;
-
-				}
-
-				return EXRDecoder;
-
-			}
-
-			// start parsing file [START]
-
-			const bufferDataView = new DataView( buffer );
-			const uInt8Array = new Uint8Array( buffer );
-			const offset = {
-				value: 0
-			};
-
-			// get header information and validate format.
-			const EXRHeader = parseHeader( bufferDataView, buffer, offset );
-
-			// get input compression information and prepare decoding.
-			const EXRDecoder = setupDecoder( EXRHeader, bufferDataView, uInt8Array, offset, this.type );
-			const tmpOffset = {
-				value: 0
-			};
-			const channelOffsets = {
-				R: 0,
-				G: 1,
-				B: 2,
-				A: 3,
-				Y: 0
-			};
-			for ( let scanlineBlockIdx = 0; scanlineBlockIdx < EXRDecoder.height / EXRDecoder.scanlineBlockSize; scanlineBlockIdx ++ ) {
-
-				const line = parseUint32( bufferDataView, offset ); // line_no
-				EXRDecoder.size = parseUint32( bufferDataView, offset ); // data_len
-				EXRDecoder.lines = line + EXRDecoder.scanlineBlockSize > EXRDecoder.height ? EXRDecoder.height - line : EXRDecoder.scanlineBlockSize;
-				const isCompressed = EXRDecoder.size < EXRDecoder.lines * EXRDecoder.bytesPerLine;
-				const viewer = isCompressed ? EXRDecoder.uncompress( EXRDecoder ) : uncompressRAW( EXRDecoder );
-				offset.value += EXRDecoder.size;
-				for ( let line_y = 0; line_y < EXRDecoder.scanlineBlockSize; line_y ++ ) {
-
-					const true_y = line_y + scanlineBlockIdx * EXRDecoder.scanlineBlockSize;
-					if ( true_y >= EXRDecoder.height ) break;
-					for ( let channelID = 0; channelID < EXRDecoder.channels; channelID ++ ) {
-
-						const cOff = channelOffsets[ EXRHeader.channels[ channelID ].name ];
-						for ( let x = 0; x < EXRDecoder.width; x ++ ) {
-
-							tmpOffset.value = ( line_y * ( EXRDecoder.channels * EXRDecoder.width ) + channelID * EXRDecoder.width + x ) * EXRDecoder.inputSize;
-							const outIndex = ( EXRDecoder.height - 1 - true_y ) * ( EXRDecoder.width * EXRDecoder.outputChannels ) + x * EXRDecoder.outputChannels + cOff;
-							EXRDecoder.byteArray[ outIndex ] = EXRDecoder.getter( viewer, tmpOffset );
-
-						}
-
-					}
-
-				}
-
-			}
-
-			return {
-				header: EXRHeader,
-				width: EXRDecoder.width,
-				height: EXRDecoder.height,
-				data: EXRDecoder.byteArray,
-				format: EXRDecoder.format,
-				encoding: EXRDecoder.encoding,
-				type: this.type
-			};
-
-		}
-		setDataType( value ) {
-
-			this.type = value;
-			return this;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			function onLoadCallback( texture, texData ) {
-
-				texture.encoding = texData.encoding;
-				texture.minFilter = THREE.LinearFilter;
-				texture.magFilter = THREE.LinearFilter;
-				texture.generateMipmaps = false;
-				texture.flipY = false;
-				if ( onLoad ) onLoad( texture, texData );
-
-			}
-
-			return super.load( url, onLoadCallback, onProgress, onError );
-
-		}
-
-	}
-
-	THREE.EXRLoader = EXRLoader;
-
-} )();

+ 0 - 3681
examples/js/loaders/FBXLoader.js

@@ -1,3681 +0,0 @@
-( function () {
-
-	/**
- * THREE.Loader loads FBX file and generates THREE.Group representing FBX scene.
- * Requires FBX file to be >= 7.0 and in ASCII or >= 6400 in Binary format
- * Versions lower than this may load but will probably have errors
- *
- * Needs Support:
- *  Morph normals / blend shape normals
- *
- * FBX format references:
- * 	https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_index_html (C++ SDK reference)
- *
- * Binary format specification:
- *	https://code.blender.org/2013/08/fbx-binary-file-format-specification/
- */
-
-	let fbxTree;
-	let connections;
-	let sceneGraph;
-	class FBXLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const path = scope.path === '' ? THREE.LoaderUtils.extractUrlBase( url ) : scope.path;
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( scope.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( buffer ) {
-
-				try {
-
-					onLoad( scope.parse( buffer, path ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( FBXBuffer, path ) {
-
-			if ( isFbxFormatBinary( FBXBuffer ) ) {
-
-				fbxTree = new BinaryParser().parse( FBXBuffer );
-
-			} else {
-
-				const FBXText = convertArrayBufferToString( FBXBuffer );
-				if ( ! isFbxFormatASCII( FBXText ) ) {
-
-					throw new Error( 'THREE.FBXLoader: Unknown format.' );
-
-				}
-
-				if ( getFbxVersion( FBXText ) < 7000 ) {
-
-					throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + getFbxVersion( FBXText ) );
-
-				}
-
-				fbxTree = new TextParser().parse( FBXText );
-
-			}
-
-			// console.log( fbxTree );
-
-			const textureLoader = new THREE.TextureLoader( this.manager ).setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
-			return new FBXTreeParser( textureLoader, this.manager ).parse( fbxTree );
-
-		}
-
-	}
-
-	// Parse the FBXTree object returned by the BinaryParser or TextParser and return a THREE.Group
-	class FBXTreeParser {
-
-		constructor( textureLoader, manager ) {
-
-			this.textureLoader = textureLoader;
-			this.manager = manager;
-
-		}
-		parse() {
-
-			connections = this.parseConnections();
-			const images = this.parseImages();
-			const textures = this.parseTextures( images );
-			const materials = this.parseMaterials( textures );
-			const deformers = this.parseDeformers();
-			const geometryMap = new GeometryParser().parse( deformers );
-			this.parseScene( deformers, geometryMap, materials );
-			return sceneGraph;
-
-		}
-
-		// Parses FBXTree.Connections which holds parent-child connections between objects (e.g. material -> texture, model->geometry )
-		// and details the connection type
-		parseConnections() {
-
-			const connectionMap = new Map();
-			if ( 'Connections' in fbxTree ) {
-
-				const rawConnections = fbxTree.Connections.connections;
-				rawConnections.forEach( function ( rawConnection ) {
-
-					const fromID = rawConnection[ 0 ];
-					const toID = rawConnection[ 1 ];
-					const relationship = rawConnection[ 2 ];
-					if ( ! connectionMap.has( fromID ) ) {
-
-						connectionMap.set( fromID, {
-							parents: [],
-							children: []
-						} );
-
-					}
-
-					const parentRelationship = {
-						ID: toID,
-						relationship: relationship
-					};
-					connectionMap.get( fromID ).parents.push( parentRelationship );
-					if ( ! connectionMap.has( toID ) ) {
-
-						connectionMap.set( toID, {
-							parents: [],
-							children: []
-						} );
-
-					}
-
-					const childRelationship = {
-						ID: fromID,
-						relationship: relationship
-					};
-					connectionMap.get( toID ).children.push( childRelationship );
-
-				} );
-
-			}
-
-			return connectionMap;
-
-		}
-
-		// Parse FBXTree.Objects.Video for embedded image data
-		// These images are connected to textures in FBXTree.Objects.Textures
-		// via FBXTree.Connections.
-		parseImages() {
-
-			const images = {};
-			const blobs = {};
-			if ( 'Video' in fbxTree.Objects ) {
-
-				const videoNodes = fbxTree.Objects.Video;
-				for ( const nodeID in videoNodes ) {
-
-					const videoNode = videoNodes[ nodeID ];
-					const id = parseInt( nodeID );
-					images[ id ] = videoNode.RelativeFilename || videoNode.Filename;
-
-					// raw image data is in videoNode.Content
-					if ( 'Content' in videoNode ) {
-
-						const arrayBufferContent = videoNode.Content instanceof ArrayBuffer && videoNode.Content.byteLength > 0;
-						const base64Content = typeof videoNode.Content === 'string' && videoNode.Content !== '';
-						if ( arrayBufferContent || base64Content ) {
-
-							const image = this.parseImage( videoNodes[ nodeID ] );
-							blobs[ videoNode.RelativeFilename || videoNode.Filename ] = image;
-
-						}
-
-					}
-
-				}
-
-			}
-
-			for ( const id in images ) {
-
-				const filename = images[ id ];
-				if ( blobs[ filename ] !== undefined ) images[ id ] = blobs[ filename ]; else images[ id ] = images[ id ].split( '\\' ).pop();
-
-			}
-
-			return images;
-
-		}
-
-		// Parse embedded image data in FBXTree.Video.Content
-		parseImage( videoNode ) {
-
-			const content = videoNode.Content;
-			const fileName = videoNode.RelativeFilename || videoNode.Filename;
-			const extension = fileName.slice( fileName.lastIndexOf( '.' ) + 1 ).toLowerCase();
-			let type;
-			switch ( extension ) {
-
-				case 'bmp':
-					type = 'image/bmp';
-					break;
-				case 'jpg':
-				case 'jpeg':
-					type = 'image/jpeg';
-					break;
-				case 'png':
-					type = 'image/png';
-					break;
-				case 'tif':
-					type = 'image/tiff';
-					break;
-				case 'tga':
-					if ( this.manager.getHandler( '.tga' ) === null ) {
-
-						console.warn( 'FBXLoader: TGA loader not found, skipping ', fileName );
-
-					}
-
-					type = 'image/tga';
-					break;
-				default:
-					console.warn( 'FBXLoader: Image type "' + extension + '" is not supported.' );
-					return;
-
-			}
-
-			if ( typeof content === 'string' ) {
-
-				// ASCII format
-
-				return 'data:' + type + ';base64,' + content;
-
-			} else {
-
-				// Binary Format
-
-				const array = new Uint8Array( content );
-				return window.URL.createObjectURL( new Blob( [ array ], {
-					type: type
-				} ) );
-
-			}
-
-		}
-
-		// Parse nodes in FBXTree.Objects.Texture
-		// These contain details such as UV scaling, cropping, rotation etc and are connected
-		// to images in FBXTree.Objects.Video
-		parseTextures( images ) {
-
-			const textureMap = new Map();
-			if ( 'Texture' in fbxTree.Objects ) {
-
-				const textureNodes = fbxTree.Objects.Texture;
-				for ( const nodeID in textureNodes ) {
-
-					const texture = this.parseTexture( textureNodes[ nodeID ], images );
-					textureMap.set( parseInt( nodeID ), texture );
-
-				}
-
-			}
-
-			return textureMap;
-
-		}
-
-		// Parse individual node in FBXTree.Objects.Texture
-		parseTexture( textureNode, images ) {
-
-			const texture = this.loadTexture( textureNode, images );
-			texture.ID = textureNode.id;
-			texture.name = textureNode.attrName;
-			const wrapModeU = textureNode.WrapModeU;
-			const wrapModeV = textureNode.WrapModeV;
-			const valueU = wrapModeU !== undefined ? wrapModeU.value : 0;
-			const valueV = wrapModeV !== undefined ? wrapModeV.value : 0;
-
-			// http://download.autodesk.com/us/fbx/SDKdocs/FBX_SDK_Help/files/fbxsdkref/class_k_fbx_texture.html#889640e63e2e681259ea81061b85143a
-			// 0: repeat(default), 1: clamp
-
-			texture.wrapS = valueU === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-			texture.wrapT = valueV === 0 ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
-			if ( 'Scaling' in textureNode ) {
-
-				const values = textureNode.Scaling.value;
-				texture.repeat.x = values[ 0 ];
-				texture.repeat.y = values[ 1 ];
-
-			}
-
-			if ( 'Translation' in textureNode ) {
-
-				const values = textureNode.Translation.value;
-				texture.offset.x = values[ 0 ];
-				texture.offset.y = values[ 1 ];
-
-			}
-
-			return texture;
-
-		}
-
-		// load a texture specified as a blob or data URI, or via an external URL using THREE.TextureLoader
-		loadTexture( textureNode, images ) {
-
-			let fileName;
-			const currentPath = this.textureLoader.path;
-			const children = connections.get( textureNode.id ).children;
-			if ( children !== undefined && children.length > 0 && images[ children[ 0 ].ID ] !== undefined ) {
-
-				fileName = images[ children[ 0 ].ID ];
-				if ( fileName.indexOf( 'blob:' ) === 0 || fileName.indexOf( 'data:' ) === 0 ) {
-
-					this.textureLoader.setPath( undefined );
-
-				}
-
-			}
-
-			let texture;
-			const extension = textureNode.FileName.slice( - 3 ).toLowerCase();
-			if ( extension === 'tga' ) {
-
-				const loader = this.manager.getHandler( '.tga' );
-				if ( loader === null ) {
-
-					console.warn( 'FBXLoader: TGA loader not found, creating placeholder texture for', textureNode.RelativeFilename );
-					texture = new THREE.Texture();
-
-				} else {
-
-					loader.setPath( this.textureLoader.path );
-					texture = loader.load( fileName );
-
-				}
-
-			} else if ( extension === 'psd' ) {
-
-				console.warn( 'FBXLoader: PSD textures are not supported, creating placeholder texture for', textureNode.RelativeFilename );
-				texture = new THREE.Texture();
-
-			} else {
-
-				texture = this.textureLoader.load( fileName );
-
-			}
-
-			this.textureLoader.setPath( currentPath );
-			return texture;
-
-		}
-
-		// Parse nodes in FBXTree.Objects.Material
-		parseMaterials( textureMap ) {
-
-			const materialMap = new Map();
-			if ( 'Material' in fbxTree.Objects ) {
-
-				const materialNodes = fbxTree.Objects.Material;
-				for ( const nodeID in materialNodes ) {
-
-					const material = this.parseMaterial( materialNodes[ nodeID ], textureMap );
-					if ( material !== null ) materialMap.set( parseInt( nodeID ), material );
-
-				}
-
-			}
-
-			return materialMap;
-
-		}
-
-		// Parse single node in FBXTree.Objects.Material
-		// Materials are connected to texture maps in FBXTree.Objects.Textures
-		// FBX format currently only supports Lambert and Phong shading models
-		parseMaterial( materialNode, textureMap ) {
-
-			const ID = materialNode.id;
-			const name = materialNode.attrName;
-			let type = materialNode.ShadingModel;
-
-			// Case where FBX wraps shading model in property object.
-			if ( typeof type === 'object' ) {
-
-				type = type.value;
-
-			}
-
-			// Ignore unused materials which don't have any connections.
-			if ( ! connections.has( ID ) ) return null;
-			const parameters = this.parseParameters( materialNode, textureMap, ID );
-			let material;
-			switch ( type.toLowerCase() ) {
-
-				case 'phong':
-					material = new THREE.MeshPhongMaterial();
-					break;
-				case 'lambert':
-					material = new THREE.MeshLambertMaterial();
-					break;
-				default:
-					console.warn( 'THREE.FBXLoader: unknown material type "%s". Defaulting to THREE.MeshPhongMaterial.', type );
-					material = new THREE.MeshPhongMaterial();
-					break;
-
-			}
-
-			material.setValues( parameters );
-			material.name = name;
-			return material;
-
-		}
-
-		// Parse FBX material and return parameters suitable for a three.js material
-		// Also parse the texture map and return any textures associated with the material
-		parseParameters( materialNode, textureMap, ID ) {
-
-			const parameters = {};
-			if ( materialNode.BumpFactor ) {
-
-				parameters.bumpScale = materialNode.BumpFactor.value;
-
-			}
-
-			if ( materialNode.Diffuse ) {
-
-				parameters.color = new THREE.Color().fromArray( materialNode.Diffuse.value );
-
-			} else if ( materialNode.DiffuseColor && ( materialNode.DiffuseColor.type === 'Color' || materialNode.DiffuseColor.type === 'ColorRGB' ) ) {
-
-				// The blender exporter exports diffuse here instead of in materialNode.Diffuse
-				parameters.color = new THREE.Color().fromArray( materialNode.DiffuseColor.value );
-
-			}
-
-			if ( materialNode.DisplacementFactor ) {
-
-				parameters.displacementScale = materialNode.DisplacementFactor.value;
-
-			}
-
-			if ( materialNode.Emissive ) {
-
-				parameters.emissive = new THREE.Color().fromArray( materialNode.Emissive.value );
-
-			} else if ( materialNode.EmissiveColor && ( materialNode.EmissiveColor.type === 'Color' || materialNode.EmissiveColor.type === 'ColorRGB' ) ) {
-
-				// The blender exporter exports emissive color here instead of in materialNode.Emissive
-				parameters.emissive = new THREE.Color().fromArray( materialNode.EmissiveColor.value );
-
-			}
-
-			if ( materialNode.EmissiveFactor ) {
-
-				parameters.emissiveIntensity = parseFloat( materialNode.EmissiveFactor.value );
-
-			}
-
-			if ( materialNode.Opacity ) {
-
-				parameters.opacity = parseFloat( materialNode.Opacity.value );
-
-			}
-
-			if ( parameters.opacity < 1.0 ) {
-
-				parameters.transparent = true;
-
-			}
-
-			if ( materialNode.ReflectionFactor ) {
-
-				parameters.reflectivity = materialNode.ReflectionFactor.value;
-
-			}
-
-			if ( materialNode.Shininess ) {
-
-				parameters.shininess = materialNode.Shininess.value;
-
-			}
-
-			if ( materialNode.Specular ) {
-
-				parameters.specular = new THREE.Color().fromArray( materialNode.Specular.value );
-
-			} else if ( materialNode.SpecularColor && materialNode.SpecularColor.type === 'Color' ) {
-
-				// The blender exporter exports specular color here instead of in materialNode.Specular
-				parameters.specular = new THREE.Color().fromArray( materialNode.SpecularColor.value );
-
-			}
-
-			const scope = this;
-			connections.get( ID ).children.forEach( function ( child ) {
-
-				const type = child.relationship;
-				switch ( type ) {
-
-					case 'Bump':
-						parameters.bumpMap = scope.getTexture( textureMap, child.ID );
-						break;
-					case 'Maya|TEX_ao_map':
-						parameters.aoMap = scope.getTexture( textureMap, child.ID );
-						break;
-					case 'DiffuseColor':
-					case 'Maya|TEX_color_map':
-						parameters.map = scope.getTexture( textureMap, child.ID );
-						if ( parameters.map !== undefined ) {
-
-							parameters.map.encoding = THREE.sRGBEncoding;
-
-						}
-
-						break;
-					case 'DisplacementColor':
-						parameters.displacementMap = scope.getTexture( textureMap, child.ID );
-						break;
-					case 'EmissiveColor':
-						parameters.emissiveMap = scope.getTexture( textureMap, child.ID );
-						if ( parameters.emissiveMap !== undefined ) {
-
-							parameters.emissiveMap.encoding = THREE.sRGBEncoding;
-
-						}
-
-						break;
-					case 'NormalMap':
-					case 'Maya|TEX_normal_map':
-						parameters.normalMap = scope.getTexture( textureMap, child.ID );
-						break;
-					case 'ReflectionColor':
-						parameters.envMap = scope.getTexture( textureMap, child.ID );
-						if ( parameters.envMap !== undefined ) {
-
-							parameters.envMap.mapping = THREE.EquirectangularReflectionMapping;
-							parameters.envMap.encoding = THREE.sRGBEncoding;
-
-						}
-
-						break;
-					case 'SpecularColor':
-						parameters.specularMap = scope.getTexture( textureMap, child.ID );
-						if ( parameters.specularMap !== undefined ) {
-
-							parameters.specularMap.encoding = THREE.sRGBEncoding;
-
-						}
-
-						break;
-					case 'TransparentColor':
-					case 'TransparencyFactor':
-						parameters.alphaMap = scope.getTexture( textureMap, child.ID );
-						parameters.transparent = true;
-						break;
-					case 'AmbientColor':
-					case 'ShininessExponent': // AKA glossiness map
-					case 'SpecularFactor': // AKA specularLevel
-					case 'VectorDisplacementColor': // NOTE: Seems to be a copy of DisplacementColor
-					default:
-						console.warn( 'THREE.FBXLoader: %s map is not supported in three.js, skipping texture.', type );
-						break;
-
-				}
-
-			} );
-			return parameters;
-
-		}
-
-		// get a texture from the textureMap for use by a material.
-		getTexture( textureMap, id ) {
-
-			// if the texture is a layered texture, just use the first layer and issue a warning
-			if ( 'LayeredTexture' in fbxTree.Objects && id in fbxTree.Objects.LayeredTexture ) {
-
-				console.warn( 'THREE.FBXLoader: layered textures are not supported in three.js. Discarding all but first layer.' );
-				id = connections.get( id ).children[ 0 ].ID;
-
-			}
-
-			return textureMap.get( id );
-
-		}
-
-		// Parse nodes in FBXTree.Objects.Deformer
-		// Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here
-		// Generates map of THREE.Skeleton-like objects for use later when generating and binding skeletons.
-		parseDeformers() {
-
-			const skeletons = {};
-			const morphTargets = {};
-			if ( 'Deformer' in fbxTree.Objects ) {
-
-				const DeformerNodes = fbxTree.Objects.Deformer;
-				for ( const nodeID in DeformerNodes ) {
-
-					const deformerNode = DeformerNodes[ nodeID ];
-					const relationships = connections.get( parseInt( nodeID ) );
-					if ( deformerNode.attrType === 'Skin' ) {
-
-						const skeleton = this.parseSkeleton( relationships, DeformerNodes );
-						skeleton.ID = nodeID;
-						if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: skeleton attached to more than one geometry is not supported.' );
-						skeleton.geometryID = relationships.parents[ 0 ].ID;
-						skeletons[ nodeID ] = skeleton;
-
-					} else if ( deformerNode.attrType === 'BlendShape' ) {
-
-						const morphTarget = {
-							id: nodeID
-						};
-						morphTarget.rawTargets = this.parseMorphTargets( relationships, DeformerNodes );
-						morphTarget.id = nodeID;
-						if ( relationships.parents.length > 1 ) console.warn( 'THREE.FBXLoader: morph target attached to more than one geometry is not supported.' );
-						morphTargets[ nodeID ] = morphTarget;
-
-					}
-
-				}
-
-			}
-
-			return {
-				skeletons: skeletons,
-				morphTargets: morphTargets
-			};
-
-		}
-
-		// Parse single nodes in FBXTree.Objects.Deformer
-		// The top level skeleton node has type 'Skin' and sub nodes have type 'Cluster'
-		// Each skin node represents a skeleton and each cluster node represents a bone
-		parseSkeleton( relationships, deformerNodes ) {
-
-			const rawBones = [];
-			relationships.children.forEach( function ( child ) {
-
-				const boneNode = deformerNodes[ child.ID ];
-				if ( boneNode.attrType !== 'Cluster' ) return;
-				const rawBone = {
-					ID: child.ID,
-					indices: [],
-					weights: [],
-					transformLink: new THREE.Matrix4().fromArray( boneNode.TransformLink.a )
-					// transform: new THREE.Matrix4().fromArray( boneNode.Transform.a ),
-					// linkMode: boneNode.Mode,
-				};
-
-				if ( 'Indexes' in boneNode ) {
-
-					rawBone.indices = boneNode.Indexes.a;
-					rawBone.weights = boneNode.Weights.a;
-
-				}
-
-				rawBones.push( rawBone );
-
-			} );
-			return {
-				rawBones: rawBones,
-				bones: []
-			};
-
-		}
-
-		// The top level morph deformer node has type "BlendShape" and sub nodes have type "BlendShapeChannel"
-		parseMorphTargets( relationships, deformerNodes ) {
-
-			const rawMorphTargets = [];
-			for ( let i = 0; i < relationships.children.length; i ++ ) {
-
-				const child = relationships.children[ i ];
-				const morphTargetNode = deformerNodes[ child.ID ];
-				const rawMorphTarget = {
-					name: morphTargetNode.attrName,
-					initialWeight: morphTargetNode.DeformPercent,
-					id: morphTargetNode.id,
-					fullWeights: morphTargetNode.FullWeights.a
-				};
-				if ( morphTargetNode.attrType !== 'BlendShapeChannel' ) return;
-				rawMorphTarget.geoID = connections.get( parseInt( child.ID ) ).children.filter( function ( child ) {
-
-					return child.relationship === undefined;
-
-				} )[ 0 ].ID;
-				rawMorphTargets.push( rawMorphTarget );
-
-			}
-
-			return rawMorphTargets;
-
-		}
-
-		// create the main THREE.Group() to be returned by the loader
-		parseScene( deformers, geometryMap, materialMap ) {
-
-			sceneGraph = new THREE.Group();
-			const modelMap = this.parseModels( deformers.skeletons, geometryMap, materialMap );
-			const modelNodes = fbxTree.Objects.Model;
-			const scope = this;
-			modelMap.forEach( function ( model ) {
-
-				const modelNode = modelNodes[ model.ID ];
-				scope.setLookAtProperties( model, modelNode );
-				const parentConnections = connections.get( model.ID ).parents;
-				parentConnections.forEach( function ( connection ) {
-
-					const parent = modelMap.get( connection.ID );
-					if ( parent !== undefined ) parent.add( model );
-
-				} );
-				if ( model.parent === null ) {
-
-					sceneGraph.add( model );
-
-				}
-
-			} );
-			this.bindSkeleton( deformers.skeletons, geometryMap, modelMap );
-			this.createAmbientLight();
-			sceneGraph.traverse( function ( node ) {
-
-				if ( node.userData.transformData ) {
-
-					if ( node.parent ) {
-
-						node.userData.transformData.parentMatrix = node.parent.matrix;
-						node.userData.transformData.parentMatrixWorld = node.parent.matrixWorld;
-
-					}
-
-					const transform = generateTransform( node.userData.transformData );
-					node.applyMatrix4( transform );
-					node.updateWorldMatrix();
-
-				}
-
-			} );
-			const animations = new AnimationParser().parse();
-
-			// if all the models where already combined in a single group, just return that
-			if ( sceneGraph.children.length === 1 && sceneGraph.children[ 0 ].isGroup ) {
-
-				sceneGraph.children[ 0 ].animations = animations;
-				sceneGraph = sceneGraph.children[ 0 ];
-
-			}
-
-			sceneGraph.animations = animations;
-
-		}
-
-		// parse nodes in FBXTree.Objects.Model
-		parseModels( skeletons, geometryMap, materialMap ) {
-
-			const modelMap = new Map();
-			const modelNodes = fbxTree.Objects.Model;
-			for ( const nodeID in modelNodes ) {
-
-				const id = parseInt( nodeID );
-				const node = modelNodes[ nodeID ];
-				const relationships = connections.get( id );
-				let model = this.buildSkeleton( relationships, skeletons, id, node.attrName );
-				if ( ! model ) {
-
-					switch ( node.attrType ) {
-
-						case 'Camera':
-							model = this.createCamera( relationships );
-							break;
-						case 'Light':
-							model = this.createLight( relationships );
-							break;
-						case 'Mesh':
-							model = this.createMesh( relationships, geometryMap, materialMap );
-							break;
-						case 'NurbsCurve':
-							model = this.createCurve( relationships, geometryMap );
-							break;
-						case 'LimbNode':
-						case 'Root':
-							model = new THREE.Bone();
-							break;
-						case 'Null':
-						default:
-							model = new THREE.Group();
-							break;
-
-					}
-
-					model.name = node.attrName ? THREE.PropertyBinding.sanitizeNodeName( node.attrName ) : '';
-					model.ID = id;
-
-				}
-
-				this.getTransformData( model, node );
-				modelMap.set( id, model );
-
-			}
-
-			return modelMap;
-
-		}
-		buildSkeleton( relationships, skeletons, id, name ) {
-
-			let bone = null;
-			relationships.parents.forEach( function ( parent ) {
-
-				for ( const ID in skeletons ) {
-
-					const skeleton = skeletons[ ID ];
-					skeleton.rawBones.forEach( function ( rawBone, i ) {
-
-						if ( rawBone.ID === parent.ID ) {
-
-							const subBone = bone;
-							bone = new THREE.Bone();
-							bone.matrixWorld.copy( rawBone.transformLink );
-
-							// set name and id here - otherwise in cases where "subBone" is created it will not have a name / id
-
-							bone.name = name ? THREE.PropertyBinding.sanitizeNodeName( name ) : '';
-							bone.ID = id;
-							skeleton.bones[ i ] = bone;
-
-							// In cases where a bone is shared between multiple meshes
-							// duplicate the bone here and and it as a child of the first bone
-							if ( subBone !== null ) {
-
-								bone.add( subBone );
-
-							}
-
-						}
-
-					} );
-
-				}
-
-			} );
-			return bone;
-
-		}
-
-		// create a THREE.PerspectiveCamera or THREE.OrthographicCamera
-		createCamera( relationships ) {
-
-			let model;
-			let cameraAttribute;
-			relationships.children.forEach( function ( child ) {
-
-				const attr = fbxTree.Objects.NodeAttribute[ child.ID ];
-				if ( attr !== undefined ) {
-
-					cameraAttribute = attr;
-
-				}
-
-			} );
-			if ( cameraAttribute === undefined ) {
-
-				model = new THREE.Object3D();
-
-			} else {
-
-				let type = 0;
-				if ( cameraAttribute.CameraProjectionType !== undefined && cameraAttribute.CameraProjectionType.value === 1 ) {
-
-					type = 1;
-
-				}
-
-				let nearClippingPlane = 1;
-				if ( cameraAttribute.NearPlane !== undefined ) {
-
-					nearClippingPlane = cameraAttribute.NearPlane.value / 1000;
-
-				}
-
-				let farClippingPlane = 1000;
-				if ( cameraAttribute.FarPlane !== undefined ) {
-
-					farClippingPlane = cameraAttribute.FarPlane.value / 1000;
-
-				}
-
-				let width = window.innerWidth;
-				let height = window.innerHeight;
-				if ( cameraAttribute.AspectWidth !== undefined && cameraAttribute.AspectHeight !== undefined ) {
-
-					width = cameraAttribute.AspectWidth.value;
-					height = cameraAttribute.AspectHeight.value;
-
-				}
-
-				const aspect = width / height;
-				let fov = 45;
-				if ( cameraAttribute.FieldOfView !== undefined ) {
-
-					fov = cameraAttribute.FieldOfView.value;
-
-				}
-
-				const focalLength = cameraAttribute.FocalLength ? cameraAttribute.FocalLength.value : null;
-				switch ( type ) {
-
-					case 0:
-						// Perspective
-						model = new THREE.PerspectiveCamera( fov, aspect, nearClippingPlane, farClippingPlane );
-						if ( focalLength !== null ) model.setFocalLength( focalLength );
-						break;
-					case 1:
-						// Orthographic
-						model = new THREE.OrthographicCamera( - width / 2, width / 2, height / 2, - height / 2, nearClippingPlane, farClippingPlane );
-						break;
-					default:
-						console.warn( 'THREE.FBXLoader: Unknown camera type ' + type + '.' );
-						model = new THREE.Object3D();
-						break;
-
-				}
-
-			}
-
-			return model;
-
-		}
-
-		// Create a THREE.DirectionalLight, THREE.PointLight or THREE.SpotLight
-		createLight( relationships ) {
-
-			let model;
-			let lightAttribute;
-			relationships.children.forEach( function ( child ) {
-
-				const attr = fbxTree.Objects.NodeAttribute[ child.ID ];
-				if ( attr !== undefined ) {
-
-					lightAttribute = attr;
-
-				}
-
-			} );
-			if ( lightAttribute === undefined ) {
-
-				model = new THREE.Object3D();
-
-			} else {
-
-				let type;
-
-				// LightType can be undefined for Point lights
-				if ( lightAttribute.LightType === undefined ) {
-
-					type = 0;
-
-				} else {
-
-					type = lightAttribute.LightType.value;
-
-				}
-
-				let color = 0xffffff;
-				if ( lightAttribute.Color !== undefined ) {
-
-					color = new THREE.Color().fromArray( lightAttribute.Color.value );
-
-				}
-
-				let intensity = lightAttribute.Intensity === undefined ? 1 : lightAttribute.Intensity.value / 100;
-
-				// light disabled
-				if ( lightAttribute.CastLightOnObject !== undefined && lightAttribute.CastLightOnObject.value === 0 ) {
-
-					intensity = 0;
-
-				}
-
-				let distance = 0;
-				if ( lightAttribute.FarAttenuationEnd !== undefined ) {
-
-					if ( lightAttribute.EnableFarAttenuation !== undefined && lightAttribute.EnableFarAttenuation.value === 0 ) {
-
-						distance = 0;
-
-					} else {
-
-						distance = lightAttribute.FarAttenuationEnd.value;
-
-					}
-
-				}
-
-				// TODO: could this be calculated linearly from FarAttenuationStart to FarAttenuationEnd?
-				const decay = 1;
-				switch ( type ) {
-
-					case 0:
-						// Point
-						model = new THREE.PointLight( color, intensity, distance, decay );
-						break;
-					case 1:
-						// Directional
-						model = new THREE.DirectionalLight( color, intensity );
-						break;
-					case 2:
-						// Spot
-						let angle = Math.PI / 3;
-						if ( lightAttribute.InnerAngle !== undefined ) {
-
-							angle = THREE.MathUtils.degToRad( lightAttribute.InnerAngle.value );
-
-						}
-
-						let penumbra = 0;
-						if ( lightAttribute.OuterAngle !== undefined ) {
-
-							// TODO: this is not correct - FBX calculates outer and inner angle in degrees
-							// with OuterAngle > InnerAngle && OuterAngle <= Math.PI
-							// while three.js uses a penumbra between (0, 1) to attenuate the inner angle
-							penumbra = THREE.MathUtils.degToRad( lightAttribute.OuterAngle.value );
-							penumbra = Math.max( penumbra, 1 );
-
-						}
-
-						model = new THREE.SpotLight( color, intensity, distance, angle, penumbra, decay );
-						break;
-					default:
-						console.warn( 'THREE.FBXLoader: Unknown light type ' + lightAttribute.LightType.value + ', defaulting to a THREE.PointLight.' );
-						model = new THREE.PointLight( color, intensity );
-						break;
-
-				}
-
-				if ( lightAttribute.CastShadows !== undefined && lightAttribute.CastShadows.value === 1 ) {
-
-					model.castShadow = true;
-
-				}
-
-			}
-
-			return model;
-
-		}
-		createMesh( relationships, geometryMap, materialMap ) {
-
-			let model;
-			let geometry = null;
-			let material = null;
-			const materials = [];
-
-			// get geometry and materials(s) from connections
-			relationships.children.forEach( function ( child ) {
-
-				if ( geometryMap.has( child.ID ) ) {
-
-					geometry = geometryMap.get( child.ID );
-
-				}
-
-				if ( materialMap.has( child.ID ) ) {
-
-					materials.push( materialMap.get( child.ID ) );
-
-				}
-
-			} );
-			if ( materials.length > 1 ) {
-
-				material = materials;
-
-			} else if ( materials.length > 0 ) {
-
-				material = materials[ 0 ];
-
-			} else {
-
-				material = new THREE.MeshPhongMaterial( {
-					color: 0xcccccc
-				} );
-				materials.push( material );
-
-			}
-
-			if ( 'color' in geometry.attributes ) {
-
-				materials.forEach( function ( material ) {
-
-					material.vertexColors = true;
-
-				} );
-
-			}
-
-			if ( geometry.FBX_Deformer ) {
-
-				model = new THREE.SkinnedMesh( geometry, material );
-				model.normalizeSkinWeights();
-
-			} else {
-
-				model = new THREE.Mesh( geometry, material );
-
-			}
-
-			return model;
-
-		}
-		createCurve( relationships, geometryMap ) {
-
-			const geometry = relationships.children.reduce( function ( geo, child ) {
-
-				if ( geometryMap.has( child.ID ) ) geo = geometryMap.get( child.ID );
-				return geo;
-
-			}, null );
-
-			// FBX does not list materials for Nurbs lines, so we'll just put our own in here.
-			const material = new THREE.LineBasicMaterial( {
-				color: 0x3300ff,
-				linewidth: 1
-			} );
-			return new THREE.Line( geometry, material );
-
-		}
-
-		// parse the model node for transform data
-		getTransformData( model, modelNode ) {
-
-			const transformData = {};
-			if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value );
-			if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value ); else transformData.eulerOrder = 'ZYX';
-			if ( 'Lcl_Translation' in modelNode ) transformData.translation = modelNode.Lcl_Translation.value;
-			if ( 'PreRotation' in modelNode ) transformData.preRotation = modelNode.PreRotation.value;
-			if ( 'Lcl_Rotation' in modelNode ) transformData.rotation = modelNode.Lcl_Rotation.value;
-			if ( 'PostRotation' in modelNode ) transformData.postRotation = modelNode.PostRotation.value;
-			if ( 'Lcl_Scaling' in modelNode ) transformData.scale = modelNode.Lcl_Scaling.value;
-			if ( 'ScalingOffset' in modelNode ) transformData.scalingOffset = modelNode.ScalingOffset.value;
-			if ( 'ScalingPivot' in modelNode ) transformData.scalingPivot = modelNode.ScalingPivot.value;
-			if ( 'RotationOffset' in modelNode ) transformData.rotationOffset = modelNode.RotationOffset.value;
-			if ( 'RotationPivot' in modelNode ) transformData.rotationPivot = modelNode.RotationPivot.value;
-			model.userData.transformData = transformData;
-
-		}
-		setLookAtProperties( model, modelNode ) {
-
-			if ( 'LookAtProperty' in modelNode ) {
-
-				const children = connections.get( model.ID ).children;
-				children.forEach( function ( child ) {
-
-					if ( child.relationship === 'LookAtProperty' ) {
-
-						const lookAtTarget = fbxTree.Objects.Model[ child.ID ];
-						if ( 'Lcl_Translation' in lookAtTarget ) {
-
-							const pos = lookAtTarget.Lcl_Translation.value;
-
-							// THREE.DirectionalLight, THREE.SpotLight
-							if ( model.target !== undefined ) {
-
-								model.target.position.fromArray( pos );
-								sceneGraph.add( model.target );
-
-							} else {
-
-								// Cameras and other Object3Ds
-
-								model.lookAt( new THREE.Vector3().fromArray( pos ) );
-
-							}
-
-						}
-
-					}
-
-				} );
-
-			}
-
-		}
-		bindSkeleton( skeletons, geometryMap, modelMap ) {
-
-			const bindMatrices = this.parsePoseNodes();
-			for ( const ID in skeletons ) {
-
-				const skeleton = skeletons[ ID ];
-				const parents = connections.get( parseInt( skeleton.ID ) ).parents;
-				parents.forEach( function ( parent ) {
-
-					if ( geometryMap.has( parent.ID ) ) {
-
-						const geoID = parent.ID;
-						const geoRelationships = connections.get( geoID );
-						geoRelationships.parents.forEach( function ( geoConnParent ) {
-
-							if ( modelMap.has( geoConnParent.ID ) ) {
-
-								const model = modelMap.get( geoConnParent.ID );
-								model.bind( new THREE.Skeleton( skeleton.bones ), bindMatrices[ geoConnParent.ID ] );
-
-							}
-
-						} );
-
-					}
-
-				} );
-
-			}
-
-		}
-		parsePoseNodes() {
-
-			const bindMatrices = {};
-			if ( 'Pose' in fbxTree.Objects ) {
-
-				const BindPoseNode = fbxTree.Objects.Pose;
-				for ( const nodeID in BindPoseNode ) {
-
-					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' && BindPoseNode[ nodeID ].NbPoseNodes > 0 ) {
-
-						const poseNodes = BindPoseNode[ nodeID ].PoseNode;
-						if ( Array.isArray( poseNodes ) ) {
-
-							poseNodes.forEach( function ( poseNode ) {
-
-								bindMatrices[ poseNode.Node ] = new THREE.Matrix4().fromArray( poseNode.Matrix.a );
-
-							} );
-
-						} else {
-
-							bindMatrices[ poseNodes.Node ] = new THREE.Matrix4().fromArray( poseNodes.Matrix.a );
-
-						}
-
-					}
-
-				}
-
-			}
-
-			return bindMatrices;
-
-		}
-
-		// Parse ambient color in FBXTree.GlobalSettings - if it's not set to black (default), create an ambient light
-		createAmbientLight() {
-
-			if ( 'GlobalSettings' in fbxTree && 'AmbientColor' in fbxTree.GlobalSettings ) {
-
-				const ambientColor = fbxTree.GlobalSettings.AmbientColor.value;
-				const r = ambientColor[ 0 ];
-				const g = ambientColor[ 1 ];
-				const b = ambientColor[ 2 ];
-				if ( r !== 0 || g !== 0 || b !== 0 ) {
-
-					const color = new THREE.Color( r, g, b );
-					sceneGraph.add( new THREE.AmbientLight( color, 1 ) );
-
-				}
-
-			}
-
-		}
-
-	}
-
-	// parse Geometry data from FBXTree and return map of BufferGeometries
-	class GeometryParser {
-
-		constructor() {
-
-			this.negativeMaterialIndices = false;
-
-		}
-
-		// Parse nodes in FBXTree.Objects.Geometry
-		parse( deformers ) {
-
-			const geometryMap = new Map();
-			if ( 'Geometry' in fbxTree.Objects ) {
-
-				const geoNodes = fbxTree.Objects.Geometry;
-				for ( const nodeID in geoNodes ) {
-
-					const relationships = connections.get( parseInt( nodeID ) );
-					const geo = this.parseGeometry( relationships, geoNodes[ nodeID ], deformers );
-					geometryMap.set( parseInt( nodeID ), geo );
-
-				}
-
-			}
-
-			// report warnings
-
-			if ( this.negativeMaterialIndices === true ) {
-
-				console.warn( 'THREE.FBXLoader: The FBX file contains invalid (negative) material indices. The asset might not render as expected.' );
-
-			}
-
-			return geometryMap;
-
-		}
-
-		// Parse single node in FBXTree.Objects.Geometry
-		parseGeometry( relationships, geoNode, deformers ) {
-
-			switch ( geoNode.attrType ) {
-
-				case 'Mesh':
-					return this.parseMeshGeometry( relationships, geoNode, deformers );
-					break;
-				case 'NurbsCurve':
-					return this.parseNurbsGeometry( geoNode );
-					break;
-
-			}
-
-		}
-
-		// Parse single node mesh geometry in FBXTree.Objects.Geometry
-		parseMeshGeometry( relationships, geoNode, deformers ) {
-
-			const skeletons = deformers.skeletons;
-			const morphTargets = [];
-			const modelNodes = relationships.parents.map( function ( parent ) {
-
-				return fbxTree.Objects.Model[ parent.ID ];
-
-			} );
-
-			// don't create geometry if it is not associated with any models
-			if ( modelNodes.length === 0 ) return;
-			const skeleton = relationships.children.reduce( function ( skeleton, child ) {
-
-				if ( skeletons[ child.ID ] !== undefined ) skeleton = skeletons[ child.ID ];
-				return skeleton;
-
-			}, null );
-			relationships.children.forEach( function ( child ) {
-
-				if ( deformers.morphTargets[ child.ID ] !== undefined ) {
-
-					morphTargets.push( deformers.morphTargets[ child.ID ] );
-
-				}
-
-			} );
-
-			// Assume one model and get the preRotation from that
-			// if there is more than one model associated with the geometry this may cause problems
-			const modelNode = modelNodes[ 0 ];
-			const transformData = {};
-			if ( 'RotationOrder' in modelNode ) transformData.eulerOrder = getEulerOrder( modelNode.RotationOrder.value );
-			if ( 'InheritType' in modelNode ) transformData.inheritType = parseInt( modelNode.InheritType.value );
-			if ( 'GeometricTranslation' in modelNode ) transformData.translation = modelNode.GeometricTranslation.value;
-			if ( 'GeometricRotation' in modelNode ) transformData.rotation = modelNode.GeometricRotation.value;
-			if ( 'GeometricScaling' in modelNode ) transformData.scale = modelNode.GeometricScaling.value;
-			const transform = generateTransform( transformData );
-			return this.genGeometry( geoNode, skeleton, morphTargets, transform );
-
-		}
-
-		// Generate a THREE.BufferGeometry from a node in FBXTree.Objects.Geometry
-		genGeometry( geoNode, skeleton, morphTargets, preTransform ) {
-
-			const geo = new THREE.BufferGeometry();
-			if ( geoNode.attrName ) geo.name = geoNode.attrName;
-			const geoInfo = this.parseGeoNode( geoNode, skeleton );
-			const buffers = this.genBuffers( geoInfo );
-			const positionAttribute = new THREE.Float32BufferAttribute( buffers.vertex, 3 );
-			positionAttribute.applyMatrix4( preTransform );
-			geo.setAttribute( 'position', positionAttribute );
-			if ( buffers.colors.length > 0 ) {
-
-				geo.setAttribute( 'color', new THREE.Float32BufferAttribute( buffers.colors, 3 ) );
-
-			}
-
-			if ( skeleton ) {
-
-				geo.setAttribute( 'skinIndex', new THREE.Uint16BufferAttribute( buffers.weightsIndices, 4 ) );
-				geo.setAttribute( 'skinWeight', new THREE.Float32BufferAttribute( buffers.vertexWeights, 4 ) );
-
-				// used later to bind the skeleton to the model
-				geo.FBX_Deformer = skeleton;
-
-			}
-
-			if ( buffers.normal.length > 0 ) {
-
-				const normalMatrix = new THREE.Matrix3().getNormalMatrix( preTransform );
-				const normalAttribute = new THREE.Float32BufferAttribute( buffers.normal, 3 );
-				normalAttribute.applyNormalMatrix( normalMatrix );
-				geo.setAttribute( 'normal', normalAttribute );
-
-			}
-
-			buffers.uvs.forEach( function ( uvBuffer, i ) {
-
-				// subsequent uv buffers are called 'uv1', 'uv2', ...
-				let name = 'uv' + ( i + 1 ).toString();
-
-				// the first uv buffer is just called 'uv'
-				if ( i === 0 ) {
-
-					name = 'uv';
-
-				}
-
-				geo.setAttribute( name, new THREE.Float32BufferAttribute( buffers.uvs[ i ], 2 ) );
-
-			} );
-			if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
-
-				// Convert the material indices of each vertex into rendering groups on the geometry.
-				let prevMaterialIndex = buffers.materialIndex[ 0 ];
-				let startIndex = 0;
-				buffers.materialIndex.forEach( function ( currentIndex, i ) {
-
-					if ( currentIndex !== prevMaterialIndex ) {
-
-						geo.addGroup( startIndex, i - startIndex, prevMaterialIndex );
-						prevMaterialIndex = currentIndex;
-						startIndex = i;
-
-					}
-
-				} );
-
-				// the loop above doesn't add the last group, do that here.
-				if ( geo.groups.length > 0 ) {
-
-					const lastGroup = geo.groups[ geo.groups.length - 1 ];
-					const lastIndex = lastGroup.start + lastGroup.count;
-					if ( lastIndex !== buffers.materialIndex.length ) {
-
-						geo.addGroup( lastIndex, buffers.materialIndex.length - lastIndex, prevMaterialIndex );
-
-					}
-
-				}
-
-				// case where there are multiple materials but the whole geometry is only
-				// using one of them
-				if ( geo.groups.length === 0 ) {
-
-					geo.addGroup( 0, buffers.materialIndex.length, buffers.materialIndex[ 0 ] );
-
-				}
-
-			}
-
-			this.addMorphTargets( geo, geoNode, morphTargets, preTransform );
-			return geo;
-
-		}
-		parseGeoNode( geoNode, skeleton ) {
-
-			const geoInfo = {};
-			geoInfo.vertexPositions = geoNode.Vertices !== undefined ? geoNode.Vertices.a : [];
-			geoInfo.vertexIndices = geoNode.PolygonVertexIndex !== undefined ? geoNode.PolygonVertexIndex.a : [];
-			if ( geoNode.LayerElementColor ) {
-
-				geoInfo.color = this.parseVertexColors( geoNode.LayerElementColor[ 0 ] );
-
-			}
-
-			if ( geoNode.LayerElementMaterial ) {
-
-				geoInfo.material = this.parseMaterialIndices( geoNode.LayerElementMaterial[ 0 ] );
-
-			}
-
-			if ( geoNode.LayerElementNormal ) {
-
-				geoInfo.normal = this.parseNormals( geoNode.LayerElementNormal[ 0 ] );
-
-			}
-
-			if ( geoNode.LayerElementUV ) {
-
-				geoInfo.uv = [];
-				let i = 0;
-				while ( geoNode.LayerElementUV[ i ] ) {
-
-					if ( geoNode.LayerElementUV[ i ].UV ) {
-
-						geoInfo.uv.push( this.parseUVs( geoNode.LayerElementUV[ i ] ) );
-
-					}
-
-					i ++;
-
-				}
-
-			}
-
-			geoInfo.weightTable = {};
-			if ( skeleton !== null ) {
-
-				geoInfo.skeleton = skeleton;
-				skeleton.rawBones.forEach( function ( rawBone, i ) {
-
-					// loop over the bone's vertex indices and weights
-					rawBone.indices.forEach( function ( index, j ) {
-
-						if ( geoInfo.weightTable[ index ] === undefined ) geoInfo.weightTable[ index ] = [];
-						geoInfo.weightTable[ index ].push( {
-							id: i,
-							weight: rawBone.weights[ j ]
-						} );
-
-					} );
-
-				} );
-
-			}
-
-			return geoInfo;
-
-		}
-		genBuffers( geoInfo ) {
-
-			const buffers = {
-				vertex: [],
-				normal: [],
-				colors: [],
-				uvs: [],
-				materialIndex: [],
-				vertexWeights: [],
-				weightsIndices: []
-			};
-			let polygonIndex = 0;
-			let faceLength = 0;
-			let displayedWeightsWarning = false;
-
-			// these will hold data for a single face
-			let facePositionIndexes = [];
-			let faceNormals = [];
-			let faceColors = [];
-			let faceUVs = [];
-			let faceWeights = [];
-			let faceWeightIndices = [];
-			const scope = this;
-			geoInfo.vertexIndices.forEach( function ( vertexIndex, polygonVertexIndex ) {
-
-				let materialIndex;
-				let endOfFace = false;
-
-				// Face index and vertex index arrays are combined in a single array
-				// A cube with quad faces looks like this:
-				// PolygonVertexIndex: *24 {
-				//  a: 0, 1, 3, -3, 2, 3, 5, -5, 4, 5, 7, -7, 6, 7, 1, -1, 1, 7, 5, -4, 6, 0, 2, -5
-				//  }
-				// Negative numbers mark the end of a face - first face here is 0, 1, 3, -3
-				// to find index of last vertex bit shift the index: ^ - 1
-				if ( vertexIndex < 0 ) {
-
-					vertexIndex = vertexIndex ^ - 1; // equivalent to ( x * -1 ) - 1
-					endOfFace = true;
-
-				}
-
-				let weightIndices = [];
-				let weights = [];
-				facePositionIndexes.push( vertexIndex * 3, vertexIndex * 3 + 1, vertexIndex * 3 + 2 );
-				if ( geoInfo.color ) {
-
-					const data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.color );
-					faceColors.push( data[ 0 ], data[ 1 ], data[ 2 ] );
-
-				}
-
-				if ( geoInfo.skeleton ) {
-
-					if ( geoInfo.weightTable[ vertexIndex ] !== undefined ) {
-
-						geoInfo.weightTable[ vertexIndex ].forEach( function ( wt ) {
-
-							weights.push( wt.weight );
-							weightIndices.push( wt.id );
-
-						} );
-
-					}
-
-					if ( weights.length > 4 ) {
-
-						if ( ! displayedWeightsWarning ) {
-
-							console.warn( 'THREE.FBXLoader: Vertex has more than 4 skinning weights assigned to vertex. Deleting additional weights.' );
-							displayedWeightsWarning = true;
-
-						}
-
-						const wIndex = [ 0, 0, 0, 0 ];
-						const Weight = [ 0, 0, 0, 0 ];
-						weights.forEach( function ( weight, weightIndex ) {
-
-							let currentWeight = weight;
-							let currentIndex = weightIndices[ weightIndex ];
-							Weight.forEach( function ( comparedWeight, comparedWeightIndex, comparedWeightArray ) {
-
-								if ( currentWeight > comparedWeight ) {
-
-									comparedWeightArray[ comparedWeightIndex ] = currentWeight;
-									currentWeight = comparedWeight;
-									const tmp = wIndex[ comparedWeightIndex ];
-									wIndex[ comparedWeightIndex ] = currentIndex;
-									currentIndex = tmp;
-
-								}
-
-							} );
-
-						} );
-						weightIndices = wIndex;
-						weights = Weight;
-
-					}
-
-					// if the weight array is shorter than 4 pad with 0s
-					while ( weights.length < 4 ) {
-
-						weights.push( 0 );
-						weightIndices.push( 0 );
-
-					}
-
-					for ( let i = 0; i < 4; ++ i ) {
-
-						faceWeights.push( weights[ i ] );
-						faceWeightIndices.push( weightIndices[ i ] );
-
-					}
-
-				}
-
-				if ( geoInfo.normal ) {
-
-					const data = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.normal );
-					faceNormals.push( data[ 0 ], data[ 1 ], data[ 2 ] );
-
-				}
-
-				if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
-
-					materialIndex = getData( polygonVertexIndex, polygonIndex, vertexIndex, geoInfo.material )[ 0 ];
-					if ( materialIndex < 0 ) {
-
-						scope.negativeMaterialIndices = true;
-						materialIndex = 0; // fallback
-
-					}
-
-				}
-
-				if ( geoInfo.uv ) {
-
-					geoInfo.uv.forEach( function ( uv, i ) {
-
-						const data = getData( polygonVertexIndex, polygonIndex, vertexIndex, uv );
-						if ( faceUVs[ i ] === undefined ) {
-
-							faceUVs[ i ] = [];
-
-						}
-
-						faceUVs[ i ].push( data[ 0 ] );
-						faceUVs[ i ].push( data[ 1 ] );
-
-					} );
-
-				}
-
-				faceLength ++;
-				if ( endOfFace ) {
-
-					if ( faceLength > 4 ) console.warn( 'THREE.FBXLoader: Polygons with more than four sides are not supported. Make sure to triangulate the geometry during export.' );
-					scope.genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength );
-					polygonIndex ++;
-					faceLength = 0;
-
-					// reset arrays for the next face
-					facePositionIndexes = [];
-					faceNormals = [];
-					faceColors = [];
-					faceUVs = [];
-					faceWeights = [];
-					faceWeightIndices = [];
-
-				}
-
-			} );
-			return buffers;
-
-		}
-
-		// Generate data for a single face in a geometry. If the face is a quad then split it into 2 tris
-		genFace( buffers, geoInfo, facePositionIndexes, materialIndex, faceNormals, faceColors, faceUVs, faceWeights, faceWeightIndices, faceLength ) {
-
-			for ( let i = 2; i < faceLength; i ++ ) {
-
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 0 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 1 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ 2 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 1 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ ( i - 1 ) * 3 + 2 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 1 ] ] );
-				buffers.vertex.push( geoInfo.vertexPositions[ facePositionIndexes[ i * 3 + 2 ] ] );
-				if ( geoInfo.skeleton ) {
-
-					buffers.vertexWeights.push( faceWeights[ 0 ] );
-					buffers.vertexWeights.push( faceWeights[ 1 ] );
-					buffers.vertexWeights.push( faceWeights[ 2 ] );
-					buffers.vertexWeights.push( faceWeights[ 3 ] );
-					buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 ] );
-					buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 1 ] );
-					buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 2 ] );
-					buffers.vertexWeights.push( faceWeights[ ( i - 1 ) * 4 + 3 ] );
-					buffers.vertexWeights.push( faceWeights[ i * 4 ] );
-					buffers.vertexWeights.push( faceWeights[ i * 4 + 1 ] );
-					buffers.vertexWeights.push( faceWeights[ i * 4 + 2 ] );
-					buffers.vertexWeights.push( faceWeights[ i * 4 + 3 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ 0 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ 1 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ 2 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ 3 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 1 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 2 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ ( i - 1 ) * 4 + 3 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ i * 4 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 1 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 2 ] );
-					buffers.weightsIndices.push( faceWeightIndices[ i * 4 + 3 ] );
-
-				}
-
-				if ( geoInfo.color ) {
-
-					buffers.colors.push( faceColors[ 0 ] );
-					buffers.colors.push( faceColors[ 1 ] );
-					buffers.colors.push( faceColors[ 2 ] );
-					buffers.colors.push( faceColors[ ( i - 1 ) * 3 ] );
-					buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 1 ] );
-					buffers.colors.push( faceColors[ ( i - 1 ) * 3 + 2 ] );
-					buffers.colors.push( faceColors[ i * 3 ] );
-					buffers.colors.push( faceColors[ i * 3 + 1 ] );
-					buffers.colors.push( faceColors[ i * 3 + 2 ] );
-
-				}
-
-				if ( geoInfo.material && geoInfo.material.mappingType !== 'AllSame' ) {
-
-					buffers.materialIndex.push( materialIndex );
-					buffers.materialIndex.push( materialIndex );
-					buffers.materialIndex.push( materialIndex );
-
-				}
-
-				if ( geoInfo.normal ) {
-
-					buffers.normal.push( faceNormals[ 0 ] );
-					buffers.normal.push( faceNormals[ 1 ] );
-					buffers.normal.push( faceNormals[ 2 ] );
-					buffers.normal.push( faceNormals[ ( i - 1 ) * 3 ] );
-					buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 1 ] );
-					buffers.normal.push( faceNormals[ ( i - 1 ) * 3 + 2 ] );
-					buffers.normal.push( faceNormals[ i * 3 ] );
-					buffers.normal.push( faceNormals[ i * 3 + 1 ] );
-					buffers.normal.push( faceNormals[ i * 3 + 2 ] );
-
-				}
-
-				if ( geoInfo.uv ) {
-
-					geoInfo.uv.forEach( function ( uv, j ) {
-
-						if ( buffers.uvs[ j ] === undefined ) buffers.uvs[ j ] = [];
-						buffers.uvs[ j ].push( faceUVs[ j ][ 0 ] );
-						buffers.uvs[ j ].push( faceUVs[ j ][ 1 ] );
-						buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 ] );
-						buffers.uvs[ j ].push( faceUVs[ j ][ ( i - 1 ) * 2 + 1 ] );
-						buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 ] );
-						buffers.uvs[ j ].push( faceUVs[ j ][ i * 2 + 1 ] );
-
-					} );
-
-				}
-
-			}
-
-		}
-		addMorphTargets( parentGeo, parentGeoNode, morphTargets, preTransform ) {
-
-			if ( morphTargets.length === 0 ) return;
-			parentGeo.morphTargetsRelative = true;
-			parentGeo.morphAttributes.position = [];
-			// parentGeo.morphAttributes.normal = []; // not implemented
-
-			const scope = this;
-			morphTargets.forEach( function ( morphTarget ) {
-
-				morphTarget.rawTargets.forEach( function ( rawTarget ) {
-
-					const morphGeoNode = fbxTree.Objects.Geometry[ rawTarget.geoID ];
-					if ( morphGeoNode !== undefined ) {
-
-						scope.genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, rawTarget.name );
-
-					}
-
-				} );
-
-			} );
-
-		}
-
-		// a morph geometry node is similar to a standard  node, and the node is also contained
-		// in FBXTree.Objects.Geometry, however it can only have attributes for position, normal
-		// and a special attribute Index defining which vertices of the original geometry are affected
-		// Normal and position attributes only have data for the vertices that are affected by the morph
-		genMorphGeometry( parentGeo, parentGeoNode, morphGeoNode, preTransform, name ) {
-
-			const vertexIndices = parentGeoNode.PolygonVertexIndex !== undefined ? parentGeoNode.PolygonVertexIndex.a : [];
-			const morphPositionsSparse = morphGeoNode.Vertices !== undefined ? morphGeoNode.Vertices.a : [];
-			const indices = morphGeoNode.Indexes !== undefined ? morphGeoNode.Indexes.a : [];
-			const length = parentGeo.attributes.position.count * 3;
-			const morphPositions = new Float32Array( length );
-			for ( let i = 0; i < indices.length; i ++ ) {
-
-				const morphIndex = indices[ i ] * 3;
-				morphPositions[ morphIndex ] = morphPositionsSparse[ i * 3 ];
-				morphPositions[ morphIndex + 1 ] = morphPositionsSparse[ i * 3 + 1 ];
-				morphPositions[ morphIndex + 2 ] = morphPositionsSparse[ i * 3 + 2 ];
-
-			}
-
-			// TODO: add morph normal support
-			const morphGeoInfo = {
-				vertexIndices: vertexIndices,
-				vertexPositions: morphPositions
-			};
-			const morphBuffers = this.genBuffers( morphGeoInfo );
-			const positionAttribute = new THREE.Float32BufferAttribute( morphBuffers.vertex, 3 );
-			positionAttribute.name = name || morphGeoNode.attrName;
-			positionAttribute.applyMatrix4( preTransform );
-			parentGeo.morphAttributes.position.push( positionAttribute );
-
-		}
-
-		// Parse normal from FBXTree.Objects.Geometry.LayerElementNormal if it exists
-		parseNormals( NormalNode ) {
-
-			const mappingType = NormalNode.MappingInformationType;
-			const referenceType = NormalNode.ReferenceInformationType;
-			const buffer = NormalNode.Normals.a;
-			let indexBuffer = [];
-			if ( referenceType === 'IndexToDirect' ) {
-
-				if ( 'NormalIndex' in NormalNode ) {
-
-					indexBuffer = NormalNode.NormalIndex.a;
-
-				} else if ( 'NormalsIndex' in NormalNode ) {
-
-					indexBuffer = NormalNode.NormalsIndex.a;
-
-				}
-
-			}
-
-			return {
-				dataSize: 3,
-				buffer: buffer,
-				indices: indexBuffer,
-				mappingType: mappingType,
-				referenceType: referenceType
-			};
-
-		}
-
-		// Parse UVs from FBXTree.Objects.Geometry.LayerElementUV if it exists
-		parseUVs( UVNode ) {
-
-			const mappingType = UVNode.MappingInformationType;
-			const referenceType = UVNode.ReferenceInformationType;
-			const buffer = UVNode.UV.a;
-			let indexBuffer = [];
-			if ( referenceType === 'IndexToDirect' ) {
-
-				indexBuffer = UVNode.UVIndex.a;
-
-			}
-
-			return {
-				dataSize: 2,
-				buffer: buffer,
-				indices: indexBuffer,
-				mappingType: mappingType,
-				referenceType: referenceType
-			};
-
-		}
-
-		// Parse Vertex Colors from FBXTree.Objects.Geometry.LayerElementColor if it exists
-		parseVertexColors( ColorNode ) {
-
-			const mappingType = ColorNode.MappingInformationType;
-			const referenceType = ColorNode.ReferenceInformationType;
-			const buffer = ColorNode.Colors.a;
-			let indexBuffer = [];
-			if ( referenceType === 'IndexToDirect' ) {
-
-				indexBuffer = ColorNode.ColorIndex.a;
-
-			}
-
-			return {
-				dataSize: 4,
-				buffer: buffer,
-				indices: indexBuffer,
-				mappingType: mappingType,
-				referenceType: referenceType
-			};
-
-		}
-
-		// Parse mapping and material data in FBXTree.Objects.Geometry.LayerElementMaterial if it exists
-		parseMaterialIndices( MaterialNode ) {
-
-			const mappingType = MaterialNode.MappingInformationType;
-			const referenceType = MaterialNode.ReferenceInformationType;
-			if ( mappingType === 'NoMappingInformation' ) {
-
-				return {
-					dataSize: 1,
-					buffer: [ 0 ],
-					indices: [ 0 ],
-					mappingType: 'AllSame',
-					referenceType: referenceType
-				};
-
-			}
-
-			const materialIndexBuffer = MaterialNode.Materials.a;
-
-			// Since materials are stored as indices, there's a bit of a mismatch between FBX and what
-			// we expect.So we create an intermediate buffer that points to the index in the buffer,
-			// for conforming with the other functions we've written for other data.
-			const materialIndices = [];
-			for ( let i = 0; i < materialIndexBuffer.length; ++ i ) {
-
-				materialIndices.push( i );
-
-			}
-
-			return {
-				dataSize: 1,
-				buffer: materialIndexBuffer,
-				indices: materialIndices,
-				mappingType: mappingType,
-				referenceType: referenceType
-			};
-
-		}
-
-		// Generate a NurbGeometry from a node in FBXTree.Objects.Geometry
-		parseNurbsGeometry( geoNode ) {
-
-			if ( THREE.NURBSCurve === undefined ) {
-
-				console.error( 'THREE.FBXLoader: The loader relies on THREE.NURBSCurve for any nurbs present in the model. Nurbs will show up as empty geometry.' );
-				return new THREE.BufferGeometry();
-
-			}
-
-			const order = parseInt( geoNode.Order );
-			if ( isNaN( order ) ) {
-
-				console.error( 'THREE.FBXLoader: Invalid Order %s given for geometry ID: %s', geoNode.Order, geoNode.id );
-				return new THREE.BufferGeometry();
-
-			}
-
-			const degree = order - 1;
-			const knots = geoNode.KnotVector.a;
-			const controlPoints = [];
-			const pointsValues = geoNode.Points.a;
-			for ( let i = 0, l = pointsValues.length; i < l; i += 4 ) {
-
-				controlPoints.push( new THREE.Vector4().fromArray( pointsValues, i ) );
-
-			}
-
-			let startKnot, endKnot;
-			if ( geoNode.Form === 'Closed' ) {
-
-				controlPoints.push( controlPoints[ 0 ] );
-
-			} else if ( geoNode.Form === 'Periodic' ) {
-
-				startKnot = degree;
-				endKnot = knots.length - 1 - startKnot;
-				for ( let i = 0; i < degree; ++ i ) {
-
-					controlPoints.push( controlPoints[ i ] );
-
-				}
-
-			}
-
-			const curve = new THREE.NURBSCurve( degree, knots, controlPoints, startKnot, endKnot );
-			const points = curve.getPoints( controlPoints.length * 12 );
-			return new THREE.BufferGeometry().setFromPoints( points );
-
-		}
-
-	}
-
-	// parse animation data from FBXTree
-	class AnimationParser {
-
-		// take raw animation clips and turn them into three.js animation clips
-		parse() {
-
-			const animationClips = [];
-			const rawClips = this.parseClips();
-			if ( rawClips !== undefined ) {
-
-				for ( const key in rawClips ) {
-
-					const rawClip = rawClips[ key ];
-					const clip = this.addClip( rawClip );
-					animationClips.push( clip );
-
-				}
-
-			}
-
-			return animationClips;
-
-		}
-		parseClips() {
-
-			// since the actual transformation data is stored in FBXTree.Objects.AnimationCurve,
-			// if this is undefined we can safely assume there are no animations
-			if ( fbxTree.Objects.AnimationCurve === undefined ) return undefined;
-			const curveNodesMap = this.parseAnimationCurveNodes();
-			this.parseAnimationCurves( curveNodesMap );
-			const layersMap = this.parseAnimationLayers( curveNodesMap );
-			const rawClips = this.parseAnimStacks( layersMap );
-			return rawClips;
-
-		}
-
-		// parse nodes in FBXTree.Objects.AnimationCurveNode
-		// each AnimationCurveNode holds data for an animation transform for a model (e.g. left arm rotation )
-		// and is referenced by an AnimationLayer
-		parseAnimationCurveNodes() {
-
-			const rawCurveNodes = fbxTree.Objects.AnimationCurveNode;
-			const curveNodesMap = new Map();
-			for ( const nodeID in rawCurveNodes ) {
-
-				const rawCurveNode = rawCurveNodes[ nodeID ];
-				if ( rawCurveNode.attrName.match( /S|R|T|DeformPercent/ ) !== null ) {
-
-					const curveNode = {
-						id: rawCurveNode.id,
-						attr: rawCurveNode.attrName,
-						curves: {}
-					};
-					curveNodesMap.set( curveNode.id, curveNode );
-
-				}
-
-			}
-
-			return curveNodesMap;
-
-		}
-
-		// parse nodes in FBXTree.Objects.AnimationCurve and connect them up to
-		// previously parsed AnimationCurveNodes. Each AnimationCurve holds data for a single animated
-		// axis ( e.g. times and values of x rotation)
-		parseAnimationCurves( curveNodesMap ) {
-
-			const rawCurves = fbxTree.Objects.AnimationCurve;
-
-			// TODO: Many values are identical up to roundoff error, but won't be optimised
-			// e.g. position times: [0, 0.4, 0. 8]
-			// position values: [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.23538335023477e-7, 93.67518615722656, -0.9982695579528809, 7.235384487103147e-7, 93.67520904541016, -0.9982695579528809]
-			// clearly, this should be optimised to
-			// times: [0], positions [7.23538335023477e-7, 93.67518615722656, -0.9982695579528809]
-			// this shows up in nearly every FBX file, and generally time array is length > 100
-
-			for ( const nodeID in rawCurves ) {
-
-				const animationCurve = {
-					id: rawCurves[ nodeID ].id,
-					times: rawCurves[ nodeID ].KeyTime.a.map( convertFBXTimeToSeconds ),
-					values: rawCurves[ nodeID ].KeyValueFloat.a
-				};
-				const relationships = connections.get( animationCurve.id );
-				if ( relationships !== undefined ) {
-
-					const animationCurveID = relationships.parents[ 0 ].ID;
-					const animationCurveRelationship = relationships.parents[ 0 ].relationship;
-					if ( animationCurveRelationship.match( /X/ ) ) {
-
-						curveNodesMap.get( animationCurveID ).curves[ 'x' ] = animationCurve;
-
-					} else if ( animationCurveRelationship.match( /Y/ ) ) {
-
-						curveNodesMap.get( animationCurveID ).curves[ 'y' ] = animationCurve;
-
-					} else if ( animationCurveRelationship.match( /Z/ ) ) {
-
-						curveNodesMap.get( animationCurveID ).curves[ 'z' ] = animationCurve;
-
-					} else if ( animationCurveRelationship.match( /d|DeformPercent/ ) && curveNodesMap.has( animationCurveID ) ) {
-
-						curveNodesMap.get( animationCurveID ).curves[ 'morph' ] = animationCurve;
-
-					}
-
-				}
-
-			}
-
-		}
-
-		// parse nodes in FBXTree.Objects.AnimationLayer. Each layers holds references
-		// to various AnimationCurveNodes and is referenced by an AnimationStack node
-		// note: theoretically a stack can have multiple layers, however in practice there always seems to be one per stack
-		parseAnimationLayers( curveNodesMap ) {
-
-			const rawLayers = fbxTree.Objects.AnimationLayer;
-			const layersMap = new Map();
-			for ( const nodeID in rawLayers ) {
-
-				const layerCurveNodes = [];
-				const connection = connections.get( parseInt( nodeID ) );
-				if ( connection !== undefined ) {
-
-					// all the animationCurveNodes used in the layer
-					const children = connection.children;
-					children.forEach( function ( child, i ) {
-
-						if ( curveNodesMap.has( child.ID ) ) {
-
-							const curveNode = curveNodesMap.get( child.ID );
-
-							// check that the curves are defined for at least one axis, otherwise ignore the curveNode
-							if ( curveNode.curves.x !== undefined || curveNode.curves.y !== undefined || curveNode.curves.z !== undefined ) {
-
-								if ( layerCurveNodes[ i ] === undefined ) {
-
-									const modelID = connections.get( child.ID ).parents.filter( function ( parent ) {
-
-										return parent.relationship !== undefined;
-
-									} )[ 0 ].ID;
-									if ( modelID !== undefined ) {
-
-										const rawModel = fbxTree.Objects.Model[ modelID.toString() ];
-										if ( rawModel === undefined ) {
-
-											console.warn( 'THREE.FBXLoader: Encountered a unused curve.', child );
-											return;
-
-										}
-
-										const node = {
-											modelName: rawModel.attrName ? THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ) : '',
-											ID: rawModel.id,
-											initialPosition: [ 0, 0, 0 ],
-											initialRotation: [ 0, 0, 0 ],
-											initialScale: [ 1, 1, 1 ]
-										};
-										sceneGraph.traverse( function ( child ) {
-
-											if ( child.ID === rawModel.id ) {
-
-												node.transform = child.matrix;
-												if ( child.userData.transformData ) node.eulerOrder = child.userData.transformData.eulerOrder;
-
-											}
-
-										} );
-										if ( ! node.transform ) node.transform = new THREE.Matrix4();
-
-										// if the animated model is pre rotated, we'll have to apply the pre rotations to every
-										// animation value as well
-										if ( 'PreRotation' in rawModel ) node.preRotation = rawModel.PreRotation.value;
-										if ( 'PostRotation' in rawModel ) node.postRotation = rawModel.PostRotation.value;
-										layerCurveNodes[ i ] = node;
-
-									}
-
-								}
-
-								if ( layerCurveNodes[ i ] ) layerCurveNodes[ i ][ curveNode.attr ] = curveNode;
-
-							} else if ( curveNode.curves.morph !== undefined ) {
-
-								if ( layerCurveNodes[ i ] === undefined ) {
-
-									const deformerID = connections.get( child.ID ).parents.filter( function ( parent ) {
-
-										return parent.relationship !== undefined;
-
-									} )[ 0 ].ID;
-									const morpherID = connections.get( deformerID ).parents[ 0 ].ID;
-									const geoID = connections.get( morpherID ).parents[ 0 ].ID;
-
-									// assuming geometry is not used in more than one model
-									const modelID = connections.get( geoID ).parents[ 0 ].ID;
-									const rawModel = fbxTree.Objects.Model[ modelID ];
-									const node = {
-										modelName: rawModel.attrName ? THREE.PropertyBinding.sanitizeNodeName( rawModel.attrName ) : '',
-										morphName: fbxTree.Objects.Deformer[ deformerID ].attrName
-									};
-									layerCurveNodes[ i ] = node;
-
-								}
-
-								layerCurveNodes[ i ][ curveNode.attr ] = curveNode;
-
-							}
-
-						}
-
-					} );
-					layersMap.set( parseInt( nodeID ), layerCurveNodes );
-
-				}
-
-			}
-
-			return layersMap;
-
-		}
-
-		// parse nodes in FBXTree.Objects.AnimationStack. These are the top level node in the animation
-		// hierarchy. Each Stack node will be used to create a THREE.AnimationClip
-		parseAnimStacks( layersMap ) {
-
-			const rawStacks = fbxTree.Objects.AnimationStack;
-
-			// connect the stacks (clips) up to the layers
-			const rawClips = {};
-			for ( const nodeID in rawStacks ) {
-
-				const children = connections.get( parseInt( nodeID ) ).children;
-				if ( children.length > 1 ) {
-
-					// it seems like stacks will always be associated with a single layer. But just in case there are files
-					// where there are multiple layers per stack, we'll display a warning
-					console.warn( 'THREE.FBXLoader: Encountered an animation stack with multiple layers, this is currently not supported. Ignoring subsequent layers.' );
-
-				}
-
-				const layer = layersMap.get( children[ 0 ].ID );
-				rawClips[ nodeID ] = {
-					name: rawStacks[ nodeID ].attrName,
-					layer: layer
-				};
-
-			}
-
-			return rawClips;
-
-		}
-		addClip( rawClip ) {
-
-			let tracks = [];
-			const scope = this;
-			rawClip.layer.forEach( function ( rawTracks ) {
-
-				tracks = tracks.concat( scope.generateTracks( rawTracks ) );
-
-			} );
-			return new THREE.AnimationClip( rawClip.name, - 1, tracks );
-
-		}
-		generateTracks( rawTracks ) {
-
-			const tracks = [];
-			let initialPosition = new THREE.Vector3();
-			let initialRotation = new THREE.Quaternion();
-			let initialScale = new THREE.Vector3();
-			if ( rawTracks.transform ) rawTracks.transform.decompose( initialPosition, initialRotation, initialScale );
-			initialPosition = initialPosition.toArray();
-			initialRotation = new THREE.Euler().setFromQuaternion( initialRotation, rawTracks.eulerOrder ).toArray();
-			initialScale = initialScale.toArray();
-			if ( rawTracks.T !== undefined && Object.keys( rawTracks.T.curves ).length > 0 ) {
-
-				const positionTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.T.curves, initialPosition, 'position' );
-				if ( positionTrack !== undefined ) tracks.push( positionTrack );
-
-			}
-
-			if ( rawTracks.R !== undefined && Object.keys( rawTracks.R.curves ).length > 0 ) {
-
-				const rotationTrack = this.generateRotationTrack( rawTracks.modelName, rawTracks.R.curves, initialRotation, rawTracks.preRotation, rawTracks.postRotation, rawTracks.eulerOrder );
-				if ( rotationTrack !== undefined ) tracks.push( rotationTrack );
-
-			}
-
-			if ( rawTracks.S !== undefined && Object.keys( rawTracks.S.curves ).length > 0 ) {
-
-				const scaleTrack = this.generateVectorTrack( rawTracks.modelName, rawTracks.S.curves, initialScale, 'scale' );
-				if ( scaleTrack !== undefined ) tracks.push( scaleTrack );
-
-			}
-
-			if ( rawTracks.DeformPercent !== undefined ) {
-
-				const morphTrack = this.generateMorphTrack( rawTracks );
-				if ( morphTrack !== undefined ) tracks.push( morphTrack );
-
-			}
-
-			return tracks;
-
-		}
-		generateVectorTrack( modelName, curves, initialValue, type ) {
-
-			const times = this.getTimesForAllAxes( curves );
-			const values = this.getKeyframeTrackValues( times, curves, initialValue );
-			return new THREE.VectorKeyframeTrack( modelName + '.' + type, times, values );
-
-		}
-		generateRotationTrack( modelName, curves, initialValue, preRotation, postRotation, eulerOrder ) {
-
-			if ( curves.x !== undefined ) {
-
-				this.interpolateRotations( curves.x );
-				curves.x.values = curves.x.values.map( THREE.MathUtils.degToRad );
-
-			}
-
-			if ( curves.y !== undefined ) {
-
-				this.interpolateRotations( curves.y );
-				curves.y.values = curves.y.values.map( THREE.MathUtils.degToRad );
-
-			}
-
-			if ( curves.z !== undefined ) {
-
-				this.interpolateRotations( curves.z );
-				curves.z.values = curves.z.values.map( THREE.MathUtils.degToRad );
-
-			}
-
-			const times = this.getTimesForAllAxes( curves );
-			const values = this.getKeyframeTrackValues( times, curves, initialValue );
-			if ( preRotation !== undefined ) {
-
-				preRotation = preRotation.map( THREE.MathUtils.degToRad );
-				preRotation.push( eulerOrder );
-				preRotation = new THREE.Euler().fromArray( preRotation );
-				preRotation = new THREE.Quaternion().setFromEuler( preRotation );
-
-			}
-
-			if ( postRotation !== undefined ) {
-
-				postRotation = postRotation.map( THREE.MathUtils.degToRad );
-				postRotation.push( eulerOrder );
-				postRotation = new THREE.Euler().fromArray( postRotation );
-				postRotation = new THREE.Quaternion().setFromEuler( postRotation ).invert();
-
-			}
-
-			const quaternion = new THREE.Quaternion();
-			const euler = new THREE.Euler();
-			const quaternionValues = [];
-			for ( let i = 0; i < values.length; i += 3 ) {
-
-				euler.set( values[ i ], values[ i + 1 ], values[ i + 2 ], eulerOrder );
-				quaternion.setFromEuler( euler );
-				if ( preRotation !== undefined ) quaternion.premultiply( preRotation );
-				if ( postRotation !== undefined ) quaternion.multiply( postRotation );
-				quaternion.toArray( quaternionValues, i / 3 * 4 );
-
-			}
-
-			return new THREE.QuaternionKeyframeTrack( modelName + '.quaternion', times, quaternionValues );
-
-		}
-		generateMorphTrack( rawTracks ) {
-
-			const curves = rawTracks.DeformPercent.curves.morph;
-			const values = curves.values.map( function ( val ) {
-
-				return val / 100;
-
-			} );
-			const morphNum = sceneGraph.getObjectByName( rawTracks.modelName ).morphTargetDictionary[ rawTracks.morphName ];
-			return new THREE.NumberKeyframeTrack( rawTracks.modelName + '.morphTargetInfluences[' + morphNum + ']', curves.times, values );
-
-		}
-
-		// For all animated objects, times are defined separately for each axis
-		// Here we'll combine the times into one sorted array without duplicates
-		getTimesForAllAxes( curves ) {
-
-			let times = [];
-
-			// first join together the times for each axis, if defined
-			if ( curves.x !== undefined ) times = times.concat( curves.x.times );
-			if ( curves.y !== undefined ) times = times.concat( curves.y.times );
-			if ( curves.z !== undefined ) times = times.concat( curves.z.times );
-
-			// then sort them
-			times = times.sort( function ( a, b ) {
-
-				return a - b;
-
-			} );
-
-			// and remove duplicates
-			if ( times.length > 1 ) {
-
-				let targetIndex = 1;
-				let lastValue = times[ 0 ];
-				for ( let i = 1; i < times.length; i ++ ) {
-
-					const currentValue = times[ i ];
-					if ( currentValue !== lastValue ) {
-
-						times[ targetIndex ] = currentValue;
-						lastValue = currentValue;
-						targetIndex ++;
-
-					}
-
-				}
-
-				times = times.slice( 0, targetIndex );
-
-			}
-
-			return times;
-
-		}
-		getKeyframeTrackValues( times, curves, initialValue ) {
-
-			const prevValue = initialValue;
-			const values = [];
-			let xIndex = - 1;
-			let yIndex = - 1;
-			let zIndex = - 1;
-			times.forEach( function ( time ) {
-
-				if ( curves.x ) xIndex = curves.x.times.indexOf( time );
-				if ( curves.y ) yIndex = curves.y.times.indexOf( time );
-				if ( curves.z ) zIndex = curves.z.times.indexOf( time );
-
-				// if there is an x value defined for this frame, use that
-				if ( xIndex !== - 1 ) {
-
-					const xValue = curves.x.values[ xIndex ];
-					values.push( xValue );
-					prevValue[ 0 ] = xValue;
-
-				} else {
-
-					// otherwise use the x value from the previous frame
-					values.push( prevValue[ 0 ] );
-
-				}
-
-				if ( yIndex !== - 1 ) {
-
-					const yValue = curves.y.values[ yIndex ];
-					values.push( yValue );
-					prevValue[ 1 ] = yValue;
-
-				} else {
-
-					values.push( prevValue[ 1 ] );
-
-				}
-
-				if ( zIndex !== - 1 ) {
-
-					const zValue = curves.z.values[ zIndex ];
-					values.push( zValue );
-					prevValue[ 2 ] = zValue;
-
-				} else {
-
-					values.push( prevValue[ 2 ] );
-
-				}
-
-			} );
-			return values;
-
-		}
-
-		// Rotations are defined as THREE.Euler angles which can have values  of any size
-		// These will be converted to quaternions which don't support values greater than
-		// PI, so we'll interpolate large rotations
-		interpolateRotations( curve ) {
-
-			for ( let i = 1; i < curve.values.length; i ++ ) {
-
-				const initialValue = curve.values[ i - 1 ];
-				const valuesSpan = curve.values[ i ] - initialValue;
-				const absoluteSpan = Math.abs( valuesSpan );
-				if ( absoluteSpan >= 180 ) {
-
-					const numSubIntervals = absoluteSpan / 180;
-					const step = valuesSpan / numSubIntervals;
-					let nextValue = initialValue + step;
-					const initialTime = curve.times[ i - 1 ];
-					const timeSpan = curve.times[ i ] - initialTime;
-					const interval = timeSpan / numSubIntervals;
-					let nextTime = initialTime + interval;
-					const interpolatedTimes = [];
-					const interpolatedValues = [];
-					while ( nextTime < curve.times[ i ] ) {
-
-						interpolatedTimes.push( nextTime );
-						nextTime += interval;
-						interpolatedValues.push( nextValue );
-						nextValue += step;
-
-					}
-
-					curve.times = inject( curve.times, i, interpolatedTimes );
-					curve.values = inject( curve.values, i, interpolatedValues );
-
-				}
-
-			}
-
-		}
-
-	}
-
-	// parse an FBX file in ASCII format
-	class TextParser {
-
-		getPrevNode() {
-
-			return this.nodeStack[ this.currentIndent - 2 ];
-
-		}
-		getCurrentNode() {
-
-			return this.nodeStack[ this.currentIndent - 1 ];
-
-		}
-		getCurrentProp() {
-
-			return this.currentProp;
-
-		}
-		pushStack( node ) {
-
-			this.nodeStack.push( node );
-			this.currentIndent += 1;
-
-		}
-		popStack() {
-
-			this.nodeStack.pop();
-			this.currentIndent -= 1;
-
-		}
-		setCurrentProp( val, name ) {
-
-			this.currentProp = val;
-			this.currentPropName = name;
-
-		}
-		parse( text ) {
-
-			this.currentIndent = 0;
-			this.allNodes = new FBXTree();
-			this.nodeStack = [];
-			this.currentProp = [];
-			this.currentPropName = '';
-			const scope = this;
-			const split = text.split( /[\r\n]+/ );
-			split.forEach( function ( line, i ) {
-
-				const matchComment = line.match( /^[\s\t]*;/ );
-				const matchEmpty = line.match( /^[\s\t]*$/ );
-				if ( matchComment || matchEmpty ) return;
-				const matchBeginning = line.match( '^\\t{' + scope.currentIndent + '}(\\w+):(.*){', '' );
-				const matchProperty = line.match( '^\\t{' + scope.currentIndent + '}(\\w+):[\\s\\t\\r\\n](.*)' );
-				const matchEnd = line.match( '^\\t{' + ( scope.currentIndent - 1 ) + '}}' );
-				if ( matchBeginning ) {
-
-					scope.parseNodeBegin( line, matchBeginning );
-
-				} else if ( matchProperty ) {
-
-					scope.parseNodeProperty( line, matchProperty, split[ ++ i ] );
-
-				} else if ( matchEnd ) {
-
-					scope.popStack();
-
-				} else if ( line.match( /^[^\s\t}]/ ) ) {
-
-					// large arrays are split over multiple lines terminated with a ',' character
-					// if this is encountered the line needs to be joined to the previous line
-					scope.parseNodePropertyContinued( line );
-
-				}
-
-			} );
-			return this.allNodes;
-
-		}
-		parseNodeBegin( line, property ) {
-
-			const nodeName = property[ 1 ].trim().replace( /^"/, '' ).replace( /"$/, '' );
-			const nodeAttrs = property[ 2 ].split( ',' ).map( function ( attr ) {
-
-				return attr.trim().replace( /^"/, '' ).replace( /"$/, '' );
-
-			} );
-			const node = {
-				name: nodeName
-			};
-			const attrs = this.parseNodeAttr( nodeAttrs );
-			const currentNode = this.getCurrentNode();
-
-			// a top node
-			if ( this.currentIndent === 0 ) {
-
-				this.allNodes.add( nodeName, node );
-
-			} else {
-
-				// a subnode
-
-				// if the subnode already exists, append it
-				if ( nodeName in currentNode ) {
-
-					// special case Pose needs PoseNodes as an array
-					if ( nodeName === 'PoseNode' ) {
-
-						currentNode.PoseNode.push( node );
-
-					} else if ( currentNode[ nodeName ].id !== undefined ) {
-
-						currentNode[ nodeName ] = {};
-						currentNode[ nodeName ][ currentNode[ nodeName ].id ] = currentNode[ nodeName ];
-
-					}
-
-					if ( attrs.id !== '' ) currentNode[ nodeName ][ attrs.id ] = node;
-
-				} else if ( typeof attrs.id === 'number' ) {
-
-					currentNode[ nodeName ] = {};
-					currentNode[ nodeName ][ attrs.id ] = node;
-
-				} else if ( nodeName !== 'Properties70' ) {
-
-					if ( nodeName === 'PoseNode' ) currentNode[ nodeName ] = [ node ]; else currentNode[ nodeName ] = node;
-
-				}
-
-			}
-
-			if ( typeof attrs.id === 'number' ) node.id = attrs.id;
-			if ( attrs.name !== '' ) node.attrName = attrs.name;
-			if ( attrs.type !== '' ) node.attrType = attrs.type;
-			this.pushStack( node );
-
-		}
-		parseNodeAttr( attrs ) {
-
-			let id = attrs[ 0 ];
-			if ( attrs[ 0 ] !== '' ) {
-
-				id = parseInt( attrs[ 0 ] );
-				if ( isNaN( id ) ) {
-
-					id = attrs[ 0 ];
-
-				}
-
-			}
-
-			let name = '',
-				type = '';
-			if ( attrs.length > 1 ) {
-
-				name = attrs[ 1 ].replace( /^(\w+)::/, '' );
-				type = attrs[ 2 ];
-
-			}
-
-			return {
-				id: id,
-				name: name,
-				type: type
-			};
-
-		}
-		parseNodeProperty( line, property, contentLine ) {
-
-			let propName = property[ 1 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
-			let propValue = property[ 2 ].replace( /^"/, '' ).replace( /"$/, '' ).trim();
-
-			// for special case: base64 image data follows "Content: ," line
-			//	Content: ,
-			//	 "/9j/4RDaRXhpZgAATU0A..."
-			if ( propName === 'Content' && propValue === ',' ) {
-
-				propValue = contentLine.replace( /"/g, '' ).replace( /,$/, '' ).trim();
-
-			}
-
-			const currentNode = this.getCurrentNode();
-			const parentName = currentNode.name;
-			if ( parentName === 'Properties70' ) {
-
-				this.parseNodeSpecialProperty( line, propName, propValue );
-				return;
-
-			}
-
-			// Connections
-			if ( propName === 'C' ) {
-
-				const connProps = propValue.split( ',' ).slice( 1 );
-				const from = parseInt( connProps[ 0 ] );
-				const to = parseInt( connProps[ 1 ] );
-				let rest = propValue.split( ',' ).slice( 3 );
-				rest = rest.map( function ( elem ) {
-
-					return elem.trim().replace( /^"/, '' );
-
-				} );
-				propName = 'connections';
-				propValue = [ from, to ];
-				append( propValue, rest );
-				if ( currentNode[ propName ] === undefined ) {
-
-					currentNode[ propName ] = [];
-
-				}
-
-			}
-
-			// Node
-			if ( propName === 'Node' ) currentNode.id = propValue;
-
-			// connections
-			if ( propName in currentNode && Array.isArray( currentNode[ propName ] ) ) {
-
-				currentNode[ propName ].push( propValue );
-
-			} else {
-
-				if ( propName !== 'a' ) currentNode[ propName ] = propValue; else currentNode.a = propValue;
-
-			}
-
-			this.setCurrentProp( currentNode, propName );
-
-			// convert string to array, unless it ends in ',' in which case more will be added to it
-			if ( propName === 'a' && propValue.slice( - 1 ) !== ',' ) {
-
-				currentNode.a = parseNumberArray( propValue );
-
-			}
-
-		}
-		parseNodePropertyContinued( line ) {
-
-			const currentNode = this.getCurrentNode();
-			currentNode.a += line;
-
-			// if the line doesn't end in ',' we have reached the end of the property value
-			// so convert the string to an array
-			if ( line.slice( - 1 ) !== ',' ) {
-
-				currentNode.a = parseNumberArray( currentNode.a );
-
-			}
-
-		}
-
-		// parse "Property70"
-		parseNodeSpecialProperty( line, propName, propValue ) {
-
-			// split this
-			// P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
-			// into array like below
-			// ["Lcl Scaling", "Lcl Scaling", "", "A", "1,1,1" ]
-			const props = propValue.split( '",' ).map( function ( prop ) {
-
-				return prop.trim().replace( /^\"/, '' ).replace( /\s/, '_' );
-
-			} );
-			const innerPropName = props[ 0 ];
-			const innerPropType1 = props[ 1 ];
-			const innerPropType2 = props[ 2 ];
-			const innerPropFlag = props[ 3 ];
-			let innerPropValue = props[ 4 ];
-
-			// cast values where needed, otherwise leave as strings
-			switch ( innerPropType1 ) {
-
-				case 'int':
-				case 'enum':
-				case 'bool':
-				case 'ULongLong':
-				case 'double':
-				case 'Number':
-				case 'FieldOfView':
-					innerPropValue = parseFloat( innerPropValue );
-					break;
-				case 'Color':
-				case 'ColorRGB':
-				case 'Vector3D':
-				case 'Lcl_Translation':
-				case 'Lcl_Rotation':
-				case 'Lcl_Scaling':
-					innerPropValue = parseNumberArray( innerPropValue );
-					break;
-
-			}
-
-			// CAUTION: these props must append to parent's parent
-			this.getPrevNode()[ innerPropName ] = {
-				'type': innerPropType1,
-				'type2': innerPropType2,
-				'flag': innerPropFlag,
-				'value': innerPropValue
-			};
-			this.setCurrentProp( this.getPrevNode(), innerPropName );
-
-		}
-
-	}
-
-	// Parse an FBX file in Binary format
-	class BinaryParser {
-
-		parse( buffer ) {
-
-			const reader = new BinaryReader( buffer );
-			reader.skip( 23 ); // skip magic 23 bytes
-
-			const version = reader.getUint32();
-			if ( version < 6400 ) {
-
-				throw new Error( 'THREE.FBXLoader: FBX version not supported, FileVersion: ' + version );
-
-			}
-
-			const allNodes = new FBXTree();
-			while ( ! this.endOfContent( reader ) ) {
-
-				const node = this.parseNode( reader, version );
-				if ( node !== null ) allNodes.add( node.name, node );
-
-			}
-
-			return allNodes;
-
-		}
-
-		// Check if reader has reached the end of content.
-		endOfContent( reader ) {
-
-			// footer size: 160bytes + 16-byte alignment padding
-			// - 16bytes: magic
-			// - padding til 16-byte alignment (at least 1byte?)
-			//	(seems like some exporters embed fixed 15 or 16bytes?)
-			// - 4bytes: magic
-			// - 4bytes: version
-			// - 120bytes: zero
-			// - 16bytes: magic
-			if ( reader.size() % 16 === 0 ) {
-
-				return ( reader.getOffset() + 160 + 16 & ~ 0xf ) >= reader.size();
-
-			} else {
-
-				return reader.getOffset() + 160 + 16 >= reader.size();
-
-			}
-
-		}
-
-		// recursively parse nodes until the end of the file is reached
-		parseNode( reader, version ) {
-
-			const node = {};
-
-			// The first three data sizes depends on version.
-			const endOffset = version >= 7500 ? reader.getUint64() : reader.getUint32();
-			const numProperties = version >= 7500 ? reader.getUint64() : reader.getUint32();
-			version >= 7500 ? reader.getUint64() : reader.getUint32(); // the returned propertyListLen is not used
-
-			const nameLen = reader.getUint8();
-			const name = reader.getString( nameLen );
-
-			// Regards this node as NULL-record if endOffset is zero
-			if ( endOffset === 0 ) return null;
-			const propertyList = [];
-			for ( let i = 0; i < numProperties; i ++ ) {
-
-				propertyList.push( this.parseProperty( reader ) );
-
-			}
-
-			// Regards the first three elements in propertyList as id, attrName, and attrType
-			const id = propertyList.length > 0 ? propertyList[ 0 ] : '';
-			const attrName = propertyList.length > 1 ? propertyList[ 1 ] : '';
-			const attrType = propertyList.length > 2 ? propertyList[ 2 ] : '';
-
-			// check if this node represents just a single property
-			// like (name, 0) set or (name2, [0, 1, 2]) set of {name: 0, name2: [0, 1, 2]}
-			node.singleProperty = numProperties === 1 && reader.getOffset() === endOffset ? true : false;
-			while ( endOffset > reader.getOffset() ) {
-
-				const subNode = this.parseNode( reader, version );
-				if ( subNode !== null ) this.parseSubNode( name, node, subNode );
-
-			}
-
-			node.propertyList = propertyList; // raw property list used by parent
-
-			if ( typeof id === 'number' ) node.id = id;
-			if ( attrName !== '' ) node.attrName = attrName;
-			if ( attrType !== '' ) node.attrType = attrType;
-			if ( name !== '' ) node.name = name;
-			return node;
-
-		}
-		parseSubNode( name, node, subNode ) {
-
-			// special case: child node is single property
-			if ( subNode.singleProperty === true ) {
-
-				const value = subNode.propertyList[ 0 ];
-				if ( Array.isArray( value ) ) {
-
-					node[ subNode.name ] = subNode;
-					subNode.a = value;
-
-				} else {
-
-					node[ subNode.name ] = value;
-
-				}
-
-			} else if ( name === 'Connections' && subNode.name === 'C' ) {
-
-				const array = [];
-				subNode.propertyList.forEach( function ( property, i ) {
-
-					// first Connection is FBX type (OO, OP, etc.). We'll discard these
-					if ( i !== 0 ) array.push( property );
-
-				} );
-				if ( node.connections === undefined ) {
-
-					node.connections = [];
-
-				}
-
-				node.connections.push( array );
-
-			} else if ( subNode.name === 'Properties70' ) {
-
-				const keys = Object.keys( subNode );
-				keys.forEach( function ( key ) {
-
-					node[ key ] = subNode[ key ];
-
-				} );
-
-			} else if ( name === 'Properties70' && subNode.name === 'P' ) {
-
-				let innerPropName = subNode.propertyList[ 0 ];
-				let innerPropType1 = subNode.propertyList[ 1 ];
-				const innerPropType2 = subNode.propertyList[ 2 ];
-				const innerPropFlag = subNode.propertyList[ 3 ];
-				let innerPropValue;
-				if ( innerPropName.indexOf( 'Lcl ' ) === 0 ) innerPropName = innerPropName.replace( 'Lcl ', 'Lcl_' );
-				if ( innerPropType1.indexOf( 'Lcl ' ) === 0 ) innerPropType1 = innerPropType1.replace( 'Lcl ', 'Lcl_' );
-				if ( innerPropType1 === 'Color' || innerPropType1 === 'ColorRGB' || innerPropType1 === 'Vector' || innerPropType1 === 'Vector3D' || innerPropType1.indexOf( 'Lcl_' ) === 0 ) {
-
-					innerPropValue = [ subNode.propertyList[ 4 ], subNode.propertyList[ 5 ], subNode.propertyList[ 6 ] ];
-
-				} else {
-
-					innerPropValue = subNode.propertyList[ 4 ];
-
-				}
-
-				// this will be copied to parent, see above
-				node[ innerPropName ] = {
-					'type': innerPropType1,
-					'type2': innerPropType2,
-					'flag': innerPropFlag,
-					'value': innerPropValue
-				};
-
-			} else if ( node[ subNode.name ] === undefined ) {
-
-				if ( typeof subNode.id === 'number' ) {
-
-					node[ subNode.name ] = {};
-					node[ subNode.name ][ subNode.id ] = subNode;
-
-				} else {
-
-					node[ subNode.name ] = subNode;
-
-				}
-
-			} else {
-
-				if ( subNode.name === 'PoseNode' ) {
-
-					if ( ! Array.isArray( node[ subNode.name ] ) ) {
-
-						node[ subNode.name ] = [ node[ subNode.name ] ];
-
-					}
-
-					node[ subNode.name ].push( subNode );
-
-				} else if ( node[ subNode.name ][ subNode.id ] === undefined ) {
-
-					node[ subNode.name ][ subNode.id ] = subNode;
-
-				}
-
-			}
-
-		}
-		parseProperty( reader ) {
-
-			const type = reader.getString( 1 );
-			let length;
-			switch ( type ) {
-
-				case 'C':
-					return reader.getBoolean();
-				case 'D':
-					return reader.getFloat64();
-				case 'F':
-					return reader.getFloat32();
-				case 'I':
-					return reader.getInt32();
-				case 'L':
-					return reader.getInt64();
-				case 'R':
-					length = reader.getUint32();
-					return reader.getArrayBuffer( length );
-				case 'S':
-					length = reader.getUint32();
-					return reader.getString( length );
-				case 'Y':
-					return reader.getInt16();
-				case 'b':
-				case 'c':
-				case 'd':
-				case 'f':
-				case 'i':
-				case 'l':
-					const arrayLength = reader.getUint32();
-					const encoding = reader.getUint32(); // 0: non-compressed, 1: compressed
-					const compressedLength = reader.getUint32();
-					if ( encoding === 0 ) {
-
-						switch ( type ) {
-
-							case 'b':
-							case 'c':
-								return reader.getBooleanArray( arrayLength );
-							case 'd':
-								return reader.getFloat64Array( arrayLength );
-							case 'f':
-								return reader.getFloat32Array( arrayLength );
-							case 'i':
-								return reader.getInt32Array( arrayLength );
-							case 'l':
-								return reader.getInt64Array( arrayLength );
-
-						}
-
-					}
-
-					if ( typeof fflate === 'undefined' ) {
-
-						console.error( 'THREE.FBXLoader: External library fflate.min.js required.' );
-
-					}
-
-					const data = fflate.unzlibSync( new Uint8Array( reader.getArrayBuffer( compressedLength ) ) ); // eslint-disable-line no-undef
-					const reader2 = new BinaryReader( data.buffer );
-					switch ( type ) {
-
-						case 'b':
-						case 'c':
-							return reader2.getBooleanArray( arrayLength );
-						case 'd':
-							return reader2.getFloat64Array( arrayLength );
-						case 'f':
-							return reader2.getFloat32Array( arrayLength );
-						case 'i':
-							return reader2.getInt32Array( arrayLength );
-						case 'l':
-							return reader2.getInt64Array( arrayLength );
-
-					}
-
-					break;
-					// cannot happen but is required by the DeepScan
-
-				default:
-					throw new Error( 'THREE.FBXLoader: Unknown property type ' + type );
-
-			}
-
-		}
-
-	}
-	class BinaryReader {
-
-		constructor( buffer, littleEndian ) {
-
-			this.dv = new DataView( buffer );
-			this.offset = 0;
-			this.littleEndian = littleEndian !== undefined ? littleEndian : true;
-
-		}
-		getOffset() {
-
-			return this.offset;
-
-		}
-		size() {
-
-			return this.dv.buffer.byteLength;
-
-		}
-		skip( length ) {
-
-			this.offset += length;
-
-		}
-
-		// seems like true/false representation depends on exporter.
-		// true: 1 or 'Y'(=0x59), false: 0 or 'T'(=0x54)
-		// then sees LSB.
-		getBoolean() {
-
-			return ( this.getUint8() & 1 ) === 1;
-
-		}
-		getBooleanArray( size ) {
-
-			const a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a.push( this.getBoolean() );
-
-			}
-
-			return a;
-
-		}
-		getUint8() {
-
-			const value = this.dv.getUint8( this.offset );
-			this.offset += 1;
-			return value;
-
-		}
-		getInt16() {
-
-			const value = this.dv.getInt16( this.offset, this.littleEndian );
-			this.offset += 2;
-			return value;
-
-		}
-		getInt32() {
-
-			const value = this.dv.getInt32( this.offset, this.littleEndian );
-			this.offset += 4;
-			return value;
-
-		}
-		getInt32Array( size ) {
-
-			const a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a.push( this.getInt32() );
-
-			}
-
-			return a;
-
-		}
-		getUint32() {
-
-			const value = this.dv.getUint32( this.offset, this.littleEndian );
-			this.offset += 4;
-			return value;
-
-		}
-
-		// JavaScript doesn't support 64-bit integer so calculate this here
-		// 1 << 32 will return 1 so using multiply operation instead here.
-		// There's a possibility that this method returns wrong value if the value
-		// is out of the range between Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER.
-		// TODO: safely handle 64-bit integer
-		getInt64() {
-
-			let low, high;
-			if ( this.littleEndian ) {
-
-				low = this.getUint32();
-				high = this.getUint32();
-
-			} else {
-
-				high = this.getUint32();
-				low = this.getUint32();
-
-			}
-
-			// calculate negative value
-			if ( high & 0x80000000 ) {
-
-				high = ~ high & 0xFFFFFFFF;
-				low = ~ low & 0xFFFFFFFF;
-				if ( low === 0xFFFFFFFF ) high = high + 1 & 0xFFFFFFFF;
-				low = low + 1 & 0xFFFFFFFF;
-				return - ( high * 0x100000000 + low );
-
-			}
-
-			return high * 0x100000000 + low;
-
-		}
-		getInt64Array( size ) {
-
-			const a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a.push( this.getInt64() );
-
-			}
-
-			return a;
-
-		}
-
-		// Note: see getInt64() comment
-		getUint64() {
-
-			let low, high;
-			if ( this.littleEndian ) {
-
-				low = this.getUint32();
-				high = this.getUint32();
-
-			} else {
-
-				high = this.getUint32();
-				low = this.getUint32();
-
-			}
-
-			return high * 0x100000000 + low;
-
-		}
-		getFloat32() {
-
-			const value = this.dv.getFloat32( this.offset, this.littleEndian );
-			this.offset += 4;
-			return value;
-
-		}
-		getFloat32Array( size ) {
-
-			const a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a.push( this.getFloat32() );
-
-			}
-
-			return a;
-
-		}
-		getFloat64() {
-
-			const value = this.dv.getFloat64( this.offset, this.littleEndian );
-			this.offset += 8;
-			return value;
-
-		}
-		getFloat64Array( size ) {
-
-			const a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a.push( this.getFloat64() );
-
-			}
-
-			return a;
-
-		}
-		getArrayBuffer( size ) {
-
-			const value = this.dv.buffer.slice( this.offset, this.offset + size );
-			this.offset += size;
-			return value;
-
-		}
-		getString( size ) {
-
-			// note: safari 9 doesn't support Uint8Array.indexOf; create intermediate array instead
-			let a = [];
-			for ( let i = 0; i < size; i ++ ) {
-
-				a[ i ] = this.getUint8();
-
-			}
-
-			const nullByte = a.indexOf( 0 );
-			if ( nullByte >= 0 ) a = a.slice( 0, nullByte );
-			return THREE.LoaderUtils.decodeText( new Uint8Array( a ) );
-
-		}
-
-	}
-
-	// FBXTree holds a representation of the FBX data, returned by the TextParser ( FBX ASCII format)
-	// and BinaryParser( FBX Binary format)
-	class FBXTree {
-
-		add( key, val ) {
-
-			this[ key ] = val;
-
-		}
-
-	}
-
-	// ************** UTILITY FUNCTIONS **************
-
-	function isFbxFormatBinary( buffer ) {
-
-		const CORRECT = 'Kaydara\u0020FBX\u0020Binary\u0020\u0020\0';
-		return buffer.byteLength >= CORRECT.length && CORRECT === convertArrayBufferToString( buffer, 0, CORRECT.length );
-
-	}
-
-	function isFbxFormatASCII( text ) {
-
-		const CORRECT = [ 'K', 'a', 'y', 'd', 'a', 'r', 'a', '\\', 'F', 'B', 'X', '\\', 'B', 'i', 'n', 'a', 'r', 'y', '\\', '\\' ];
-		let cursor = 0;
-		function read( offset ) {
-
-			const result = text[ offset - 1 ];
-			text = text.slice( cursor + offset );
-			cursor ++;
-			return result;
-
-		}
-
-		for ( let i = 0; i < CORRECT.length; ++ i ) {
-
-			const num = read( 1 );
-			if ( num === CORRECT[ i ] ) {
-
-				return false;
-
-			}
-
-		}
-
-		return true;
-
-	}
-
-	function getFbxVersion( text ) {
-
-		const versionRegExp = /FBXVersion: (\d+)/;
-		const match = text.match( versionRegExp );
-		if ( match ) {
-
-			const version = parseInt( match[ 1 ] );
-			return version;
-
-		}
-
-		throw new Error( 'THREE.FBXLoader: Cannot find the version number for the file given.' );
-
-	}
-
-	// Converts FBX ticks into real time seconds.
-	function convertFBXTimeToSeconds( time ) {
-
-		return time / 46186158000;
-
-	}
-
-	const dataArray = [];
-
-	// extracts the data from the correct position in the FBX array based on indexing type
-	function getData( polygonVertexIndex, polygonIndex, vertexIndex, infoObject ) {
-
-		let index;
-		switch ( infoObject.mappingType ) {
-
-			case 'ByPolygonVertex':
-				index = polygonVertexIndex;
-				break;
-			case 'ByPolygon':
-				index = polygonIndex;
-				break;
-			case 'ByVertice':
-				index = vertexIndex;
-				break;
-			case 'AllSame':
-				index = infoObject.indices[ 0 ];
-				break;
-			default:
-				console.warn( 'THREE.FBXLoader: unknown attribute mapping type ' + infoObject.mappingType );
-
-		}
-
-		if ( infoObject.referenceType === 'IndexToDirect' ) index = infoObject.indices[ index ];
-		const from = index * infoObject.dataSize;
-		const to = from + infoObject.dataSize;
-		return slice( dataArray, infoObject.buffer, from, to );
-
-	}
-
-	const tempEuler = new THREE.Euler();
-	const tempVec = new THREE.Vector3();
-
-	// generate transformation from FBX transform data
-	// ref: https://help.autodesk.com/view/FBX/2017/ENU/?guid=__files_GUID_10CDD63C_79C1_4F2D_BB28_AD2BE65A02ED_htm
-	// ref: http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/index.html?url=cpp_ref/_transformations_2main_8cxx-example.html,topicNumber=cpp_ref__transformations_2main_8cxx_example_htmlfc10a1e1-b18d-4e72-9dc0-70d0f1959f5e
-	function generateTransform( transformData ) {
-
-		const lTranslationM = new THREE.Matrix4();
-		const lPreRotationM = new THREE.Matrix4();
-		const lRotationM = new THREE.Matrix4();
-		const lPostRotationM = new THREE.Matrix4();
-		const lScalingM = new THREE.Matrix4();
-		const lScalingPivotM = new THREE.Matrix4();
-		const lScalingOffsetM = new THREE.Matrix4();
-		const lRotationOffsetM = new THREE.Matrix4();
-		const lRotationPivotM = new THREE.Matrix4();
-		const lParentGX = new THREE.Matrix4();
-		const lParentLX = new THREE.Matrix4();
-		const lGlobalT = new THREE.Matrix4();
-		const inheritType = transformData.inheritType ? transformData.inheritType : 0;
-		if ( transformData.translation ) lTranslationM.setPosition( tempVec.fromArray( transformData.translation ) );
-		if ( transformData.preRotation ) {
-
-			const array = transformData.preRotation.map( THREE.MathUtils.degToRad );
-			array.push( transformData.eulerOrder || THREE.Euler.DefaultOrder );
-			lPreRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
-
-		}
-
-		if ( transformData.rotation ) {
-
-			const array = transformData.rotation.map( THREE.MathUtils.degToRad );
-			array.push( transformData.eulerOrder || THREE.Euler.DefaultOrder );
-			lRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
-
-		}
-
-		if ( transformData.postRotation ) {
-
-			const array = transformData.postRotation.map( THREE.MathUtils.degToRad );
-			array.push( transformData.eulerOrder || THREE.Euler.DefaultOrder );
-			lPostRotationM.makeRotationFromEuler( tempEuler.fromArray( array ) );
-			lPostRotationM.invert();
-
-		}
-
-		if ( transformData.scale ) lScalingM.scale( tempVec.fromArray( transformData.scale ) );
-
-		// Pivots and offsets
-		if ( transformData.scalingOffset ) lScalingOffsetM.setPosition( tempVec.fromArray( transformData.scalingOffset ) );
-		if ( transformData.scalingPivot ) lScalingPivotM.setPosition( tempVec.fromArray( transformData.scalingPivot ) );
-		if ( transformData.rotationOffset ) lRotationOffsetM.setPosition( tempVec.fromArray( transformData.rotationOffset ) );
-		if ( transformData.rotationPivot ) lRotationPivotM.setPosition( tempVec.fromArray( transformData.rotationPivot ) );
-
-		// parent transform
-		if ( transformData.parentMatrixWorld ) {
-
-			lParentLX.copy( transformData.parentMatrix );
-			lParentGX.copy( transformData.parentMatrixWorld );
-
-		}
-
-		const lLRM = lPreRotationM.clone().multiply( lRotationM ).multiply( lPostRotationM );
-		// Global Rotation
-		const lParentGRM = new THREE.Matrix4();
-		lParentGRM.extractRotation( lParentGX );
-
-		// Global Shear*Scaling
-		const lParentTM = new THREE.Matrix4();
-		lParentTM.copyPosition( lParentGX );
-		const lParentGRSM = lParentTM.clone().invert().multiply( lParentGX );
-		const lParentGSM = lParentGRM.clone().invert().multiply( lParentGRSM );
-		const lLSM = lScalingM;
-		const lGlobalRS = new THREE.Matrix4();
-		if ( inheritType === 0 ) {
-
-			lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM ).multiply( lLSM );
-
-		} else if ( inheritType === 1 ) {
-
-			lGlobalRS.copy( lParentGRM ).multiply( lParentGSM ).multiply( lLRM ).multiply( lLSM );
-
-		} else {
-
-			const lParentLSM = new THREE.Matrix4().scale( new THREE.Vector3().setFromMatrixScale( lParentLX ) );
-			const lParentLSM_inv = lParentLSM.clone().invert();
-			const lParentGSM_noLocal = lParentGSM.clone().multiply( lParentLSM_inv );
-			lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
-
-		}
-
-		const lRotationPivotM_inv = lRotationPivotM.clone().invert();
-		const lScalingPivotM_inv = lScalingPivotM.clone().invert();
-		// Calculate the local transform matrix
-		let lTransform = lTranslationM.clone().multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM_inv ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM_inv );
-		const lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform );
-		const lGlobalTranslation = lParentGX.clone().multiply( lLocalTWithAllPivotAndOffsetInfo );
-		lGlobalT.copyPosition( lGlobalTranslation );
-		lTransform = lGlobalT.clone().multiply( lGlobalRS );
-
-		// from global to local
-		lTransform.premultiply( lParentGX.invert() );
-		return lTransform;
-
-	}
-
-	// Returns the three.js intrinsic THREE.Euler order corresponding to FBX extrinsic THREE.Euler order
-	// ref: http://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_euler_html
-	function getEulerOrder( order ) {
-
-		order = order || 0;
-		const enums = [ 'ZYX',
-			// -> XYZ extrinsic
-			'YZX',
-			// -> XZY extrinsic
-			'XZY',
-			// -> YZX extrinsic
-			'ZXY',
-			// -> YXZ extrinsic
-			'YXZ',
-			// -> ZXY extrinsic
-			'XYZ' // -> ZYX extrinsic
-			//'SphericXYZ', // not possible to support
-		];
-
-		if ( order === 6 ) {
-
-			console.warn( 'THREE.FBXLoader: unsupported THREE.Euler Order: Spherical XYZ. Animations and rotations may be incorrect.' );
-			return enums[ 0 ];
-
-		}
-
-		return enums[ order ];
-
-	}
-
-	// Parses comma separated list of numbers and returns them an array.
-	// Used internally by the TextParser
-	function parseNumberArray( value ) {
-
-		const array = value.split( ',' ).map( function ( val ) {
-
-			return parseFloat( val );
-
-		} );
-		return array;
-
-	}
-
-	function convertArrayBufferToString( buffer, from, to ) {
-
-		if ( from === undefined ) from = 0;
-		if ( to === undefined ) to = buffer.byteLength;
-		return THREE.LoaderUtils.decodeText( new Uint8Array( buffer, from, to ) );
-
-	}
-
-	function append( a, b ) {
-
-		for ( let i = 0, j = a.length, l = b.length; i < l; i ++, j ++ ) {
-
-			a[ j ] = b[ i ];
-
-		}
-
-	}
-
-	function slice( a, b, from, to ) {
-
-		for ( let i = from, j = 0; i < to; i ++, j ++ ) {
-
-			a[ j ] = b[ i ];
-
-		}
-
-		return a;
-
-	}
-
-	// inject array a2 into array a1 at index
-	function inject( a1, index, a2 ) {
-
-		return a1.slice( 0, index ).concat( a2 ).concat( a1.slice( index ) );
-
-	}
-
-	THREE.FBXLoader = FBXLoader;
-
-} )();

+ 0 - 160
examples/js/loaders/FontLoader.js

@@ -1,160 +0,0 @@
-( function () {
-
-	class FontLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setRequestHeader( this.requestHeader );
-			loader.setWithCredentials( this.withCredentials );
-			loader.load( url, function ( text ) {
-
-				const font = scope.parse( JSON.parse( text ) );
-				if ( onLoad ) onLoad( font );
-
-			}, onProgress, onError );
-
-		}
-		parse( json ) {
-
-			return new Font( json );
-
-		}
-
-	}
-
-	//
-
-	class Font {
-
-		constructor( data ) {
-
-			this.isFont = true;
-			this.type = 'Font';
-			this.data = data;
-
-		}
-		generateShapes( text, size = 100 ) {
-
-			const shapes = [];
-			const paths = createPaths( text, size, this.data );
-			for ( let p = 0, pl = paths.length; p < pl; p ++ ) {
-
-				shapes.push( ...paths[ p ].toShapes() );
-
-			}
-
-			return shapes;
-
-		}
-
-	}
-	function createPaths( text, size, data ) {
-
-		const chars = Array.from( text );
-		const scale = size / data.resolution;
-		const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
-		const paths = [];
-		let offsetX = 0,
-			offsetY = 0;
-		for ( let i = 0; i < chars.length; i ++ ) {
-
-			const char = chars[ i ];
-			if ( char === '\n' ) {
-
-				offsetX = 0;
-				offsetY -= line_height;
-
-			} else {
-
-				const ret = createPath( char, scale, offsetX, offsetY, data );
-				offsetX += ret.offsetX;
-				paths.push( ret.path );
-
-			}
-
-		}
-
-		return paths;
-
-	}
-
-	function createPath( char, scale, offsetX, offsetY, data ) {
-
-		const glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
-		if ( ! glyph ) {
-
-			console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' );
-			return;
-
-		}
-
-		const path = new THREE.ShapePath();
-		let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
-		if ( glyph.o ) {
-
-			const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
-			for ( let i = 0, l = outline.length; i < l; ) {
-
-				const action = outline[ i ++ ];
-				switch ( action ) {
-
-					case 'm':
-						// moveTo
-
-						x = outline[ i ++ ] * scale + offsetX;
-						y = outline[ i ++ ] * scale + offsetY;
-						path.moveTo( x, y );
-						break;
-					case 'l':
-						// lineTo
-
-						x = outline[ i ++ ] * scale + offsetX;
-						y = outline[ i ++ ] * scale + offsetY;
-						path.lineTo( x, y );
-						break;
-					case 'q':
-						// quadraticCurveTo
-
-						cpx = outline[ i ++ ] * scale + offsetX;
-						cpy = outline[ i ++ ] * scale + offsetY;
-						cpx1 = outline[ i ++ ] * scale + offsetX;
-						cpy1 = outline[ i ++ ] * scale + offsetY;
-						path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
-						break;
-					case 'b':
-						// bezierCurveTo
-
-						cpx = outline[ i ++ ] * scale + offsetX;
-						cpy = outline[ i ++ ] * scale + offsetY;
-						cpx1 = outline[ i ++ ] * scale + offsetX;
-						cpy1 = outline[ i ++ ] * scale + offsetY;
-						cpx2 = outline[ i ++ ] * scale + offsetX;
-						cpy2 = outline[ i ++ ] * scale + offsetY;
-						path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
-						break;
-
-				}
-
-			}
-
-		}
-
-		return {
-			offsetX: glyph.ha * scale,
-			path: path
-		};
-
-	}
-
-	THREE.Font = Font;
-	THREE.FontLoader = FontLoader;
-
-} )();

+ 0 - 255
examples/js/loaders/GCodeLoader.js

@@ -1,255 +0,0 @@
-( function () {
-
-	/**
- * GCodeLoader is used to load gcode files usually used for 3D printing or CNC applications.
- *
- * Gcode files are composed by commands used by machines to create objects.
- *
- * @class GCodeLoader
- * @param {Manager} manager Loading manager.
- */
-
-	class GCodeLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.splitLayer = false;
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( text ) {
-
-				try {
-
-					onLoad( scope.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( data ) {
-
-			let state = {
-				x: 0,
-				y: 0,
-				z: 0,
-				e: 0,
-				f: 0,
-				extruding: false,
-				relative: false
-			};
-			const layers = [];
-			let currentLayer = undefined;
-			const pathMaterial = new THREE.LineBasicMaterial( {
-				color: 0xFF0000
-			} );
-			pathMaterial.name = 'path';
-			const extrudingMaterial = new THREE.LineBasicMaterial( {
-				color: 0x00FF00
-			} );
-			extrudingMaterial.name = 'extruded';
-			function newLayer( line ) {
-
-				currentLayer = {
-					vertex: [],
-					pathVertex: [],
-					z: line.z
-				};
-				layers.push( currentLayer );
-
-			}
-
-			//Create lie segment between p1 and p2
-			function addSegment( p1, p2 ) {
-
-				if ( currentLayer === undefined ) {
-
-					newLayer( p1 );
-
-				}
-
-				if ( state.extruding ) {
-
-					currentLayer.vertex.push( p1.x, p1.y, p1.z );
-					currentLayer.vertex.push( p2.x, p2.y, p2.z );
-
-				} else {
-
-					currentLayer.pathVertex.push( p1.x, p1.y, p1.z );
-					currentLayer.pathVertex.push( p2.x, p2.y, p2.z );
-
-				}
-
-			}
-
-			function delta( v1, v2 ) {
-
-				return state.relative ? v2 : v2 - v1;
-
-			}
-
-			function absolute( v1, v2 ) {
-
-				return state.relative ? v1 + v2 : v2;
-
-			}
-
-			const lines = data.replace( /;.+/g, '' ).split( '\n' );
-			for ( let i = 0; i < lines.length; i ++ ) {
-
-				const tokens = lines[ i ].split( ' ' );
-				const cmd = tokens[ 0 ].toUpperCase();
-
-				//Argumments
-				const args = {};
-				tokens.splice( 1 ).forEach( function ( token ) {
-
-					if ( token[ 0 ] !== undefined ) {
-
-						const key = token[ 0 ].toLowerCase();
-						const value = parseFloat( token.substring( 1 ) );
-						args[ key ] = value;
-
-					}
-
-				} );
-
-				//Process commands
-				//G0/G1 – Linear Movement
-				if ( cmd === 'G0' || cmd === 'G1' ) {
-
-					const line = {
-						x: args.x !== undefined ? absolute( state.x, args.x ) : state.x,
-						y: args.y !== undefined ? absolute( state.y, args.y ) : state.y,
-						z: args.z !== undefined ? absolute( state.z, args.z ) : state.z,
-						e: args.e !== undefined ? absolute( state.e, args.e ) : state.e,
-						f: args.f !== undefined ? absolute( state.f, args.f ) : state.f
-					};
-
-					//Layer change detection is or made by watching Z, it's made by watching when we extrude at a new Z position
-					if ( delta( state.e, line.e ) > 0 ) {
-
-						state.extruding = delta( state.e, line.e ) > 0;
-						if ( currentLayer == undefined || line.z != currentLayer.z ) {
-
-							newLayer( line );
-
-						}
-
-					}
-
-					addSegment( state, line );
-					state = line;
-
-				} else if ( cmd === 'G2' || cmd === 'G3' ) {
-
-					//G2/G3 - Arc Movement ( G2 clock wise and G3 counter clock wise )
-					//console.warn( 'THREE.GCodeLoader: Arc command not supported' );
-				} else if ( cmd === 'G90' ) {
-
-					//G90: Set to Absolute Positioning
-					state.relative = false;
-
-				} else if ( cmd === 'G91' ) {
-
-					//G91: Set to state.relative Positioning
-					state.relative = true;
-
-				} else if ( cmd === 'G92' ) {
-
-					//G92: Set Position
-					const line = state;
-					line.x = args.x !== undefined ? args.x : line.x;
-					line.y = args.y !== undefined ? args.y : line.y;
-					line.z = args.z !== undefined ? args.z : line.z;
-					line.e = args.e !== undefined ? args.e : line.e;
-
-				} else {
-
-					//console.warn( 'THREE.GCodeLoader: Command not supported:' + cmd );
-				}
-
-			}
-
-			function addObject( vertex, extruding, i ) {
-
-				const geometry = new THREE.BufferGeometry();
-				geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertex, 3 ) );
-				const segments = new THREE.LineSegments( geometry, extruding ? extrudingMaterial : pathMaterial );
-				segments.name = 'layer' + i;
-				object.add( segments );
-
-			}
-
-			const object = new THREE.Group();
-			object.name = 'gcode';
-			if ( this.splitLayer ) {
-
-				for ( let i = 0; i < layers.length; i ++ ) {
-
-					const layer = layers[ i ];
-					addObject( layer.vertex, true, i );
-					addObject( layer.pathVertex, false, i );
-
-				}
-
-			} else {
-
-				const vertex = [],
-					pathVertex = [];
-				for ( let i = 0; i < layers.length; i ++ ) {
-
-					const layer = layers[ i ];
-					const layerVertex = layer.vertex;
-					const layerPathVertex = layer.pathVertex;
-					for ( let j = 0; j < layerVertex.length; j ++ ) {
-
-						vertex.push( layerVertex[ j ] );
-
-					}
-
-					for ( let j = 0; j < layerPathVertex.length; j ++ ) {
-
-						pathVertex.push( layerPathVertex[ j ] );
-
-					}
-
-				}
-
-				addObject( vertex, true, layers.length );
-				addObject( pathVertex, false, layers.length );
-
-			}
-
-			object.quaternion.setFromEuler( new THREE.Euler( - Math.PI / 2, 0, 0 ) );
-			return object;
-
-		}
-
-	}
-
-	THREE.GCodeLoader = GCodeLoader;
-
-} )();

+ 0 - 3862
examples/js/loaders/GLTFLoader.js

@@ -1,3862 +0,0 @@
-( function () {
-
-	class GLTFLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.dracoLoader = null;
-			this.ktx2Loader = null;
-			this.meshoptDecoder = null;
-			this.pluginCallbacks = [];
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsClearcoatExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFTextureBasisUExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFTextureWebPExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsSheenExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsTransmissionExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsVolumeExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsIorExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsEmissiveStrengthExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsSpecularExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMaterialsIridescenceExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFLightsExtension( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMeshoptCompression( parser );
-
-			} );
-			this.register( function ( parser ) {
-
-				return new GLTFMeshGpuInstancing( parser );
-
-			} );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			let resourcePath;
-			if ( this.resourcePath !== '' ) {
-
-				resourcePath = this.resourcePath;
-
-			} else if ( this.path !== '' ) {
-
-				resourcePath = this.path;
-
-			} else {
-
-				resourcePath = THREE.LoaderUtils.extractUrlBase( url );
-
-			}
-
-			// Tells the LoadingManager to track an extra item, which resolves after
-			// the model is fully loaded. This means the count of items loaded will
-			// be incorrect, but ensures manager.onLoad() does not fire early.
-			this.manager.itemStart( url );
-			const _onError = function ( e ) {
-
-				if ( onError ) {
-
-					onError( e );
-
-				} else {
-
-					console.error( e );
-
-				}
-
-				scope.manager.itemError( url );
-				scope.manager.itemEnd( url );
-
-			};
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( this.requestHeader );
-			loader.setWithCredentials( this.withCredentials );
-			loader.load( url, function ( data ) {
-
-				try {
-
-					scope.parse( data, resourcePath, function ( gltf ) {
-
-						onLoad( gltf );
-						scope.manager.itemEnd( url );
-
-					}, _onError );
-
-				} catch ( e ) {
-
-					_onError( e );
-
-				}
-
-			}, onProgress, _onError );
-
-		}
-		setDRACOLoader( dracoLoader ) {
-
-			this.dracoLoader = dracoLoader;
-			return this;
-
-		}
-		setDDSLoader() {
-
-			throw new Error( 'THREE.GLTFLoader: "MSFT_texture_dds" no longer supported. Please update to "KHR_texture_basisu".' );
-
-		}
-		setKTX2Loader( ktx2Loader ) {
-
-			this.ktx2Loader = ktx2Loader;
-			return this;
-
-		}
-		setMeshoptDecoder( meshoptDecoder ) {
-
-			this.meshoptDecoder = meshoptDecoder;
-			return this;
-
-		}
-		register( callback ) {
-
-			if ( this.pluginCallbacks.indexOf( callback ) === - 1 ) {
-
-				this.pluginCallbacks.push( callback );
-
-			}
-
-			return this;
-
-		}
-		unregister( callback ) {
-
-			if ( this.pluginCallbacks.indexOf( callback ) !== - 1 ) {
-
-				this.pluginCallbacks.splice( this.pluginCallbacks.indexOf( callback ), 1 );
-
-			}
-
-			return this;
-
-		}
-		parse( data, path, onLoad, onError ) {
-
-			let json;
-			const extensions = {};
-			const plugins = {};
-			if ( typeof data === 'string' ) {
-
-				json = JSON.parse( data );
-
-			} else if ( data instanceof ArrayBuffer ) {
-
-				const magic = THREE.LoaderUtils.decodeText( new Uint8Array( data, 0, 4 ) );
-				if ( magic === BINARY_EXTENSION_HEADER_MAGIC ) {
-
-					try {
-
-						extensions[ EXTENSIONS.KHR_BINARY_GLTF ] = new GLTFBinaryExtension( data );
-
-					} catch ( error ) {
-
-						if ( onError ) onError( error );
-						return;
-
-					}
-
-					json = JSON.parse( extensions[ EXTENSIONS.KHR_BINARY_GLTF ].content );
-
-				} else {
-
-					json = JSON.parse( THREE.LoaderUtils.decodeText( new Uint8Array( data ) ) );
-
-				}
-
-			} else {
-
-				json = data;
-
-			}
-
-			if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) {
-
-				if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported.' ) );
-				return;
-
-			}
-
-			const parser = new GLTFParser( json, {
-				path: path || this.resourcePath || '',
-				crossOrigin: this.crossOrigin,
-				requestHeader: this.requestHeader,
-				manager: this.manager,
-				ktx2Loader: this.ktx2Loader,
-				meshoptDecoder: this.meshoptDecoder
-			} );
-			parser.fileLoader.setRequestHeader( this.requestHeader );
-			for ( let i = 0; i < this.pluginCallbacks.length; i ++ ) {
-
-				const plugin = this.pluginCallbacks[ i ]( parser );
-				plugins[ plugin.name ] = plugin;
-
-				// Workaround to avoid determining as unknown extension
-				// in addUnknownExtensionsToUserData().
-				// Remove this workaround if we move all the existing
-				// extension handlers to plugin system
-				extensions[ plugin.name ] = true;
-
-			}
-
-			if ( json.extensionsUsed ) {
-
-				for ( let i = 0; i < json.extensionsUsed.length; ++ i ) {
-
-					const extensionName = json.extensionsUsed[ i ];
-					const extensionsRequired = json.extensionsRequired || [];
-					switch ( extensionName ) {
-
-						case EXTENSIONS.KHR_MATERIALS_UNLIT:
-							extensions[ extensionName ] = new GLTFMaterialsUnlitExtension();
-							break;
-						case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
-							extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
-							break;
-						case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
-							extensions[ extensionName ] = new GLTFTextureTransformExtension();
-							break;
-						case EXTENSIONS.KHR_MESH_QUANTIZATION:
-							extensions[ extensionName ] = new GLTFMeshQuantizationExtension();
-							break;
-						default:
-							if ( extensionsRequired.indexOf( extensionName ) >= 0 && plugins[ extensionName ] === undefined ) {
-
-								console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' );
-
-							}
-
-					}
-
-				}
-
-			}
-
-			parser.setExtensions( extensions );
-			parser.setPlugins( plugins );
-			parser.parse( onLoad, onError );
-
-		}
-		parseAsync( data, path ) {
-
-			const scope = this;
-			return new Promise( function ( resolve, reject ) {
-
-				scope.parse( data, path, resolve, reject );
-
-			} );
-
-		}
-
-	}
-
-	/* GLTFREGISTRY */
-
-	function GLTFRegistry() {
-
-		let objects = {};
-		return {
-			get: function ( key ) {
-
-				return objects[ key ];
-
-			},
-			add: function ( key, object ) {
-
-				objects[ key ] = object;
-
-			},
-			remove: function ( key ) {
-
-				delete objects[ key ];
-
-			},
-			removeAll: function () {
-
-				objects = {};
-
-			}
-		};
-
-	}
-
-	/*********************************/
-	/********** EXTENSIONS ***********/
-	/*********************************/
-
-	const EXTENSIONS = {
-		KHR_BINARY_GLTF: 'KHR_binary_glTF',
-		KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
-		KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
-		KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
-		KHR_MATERIALS_IOR: 'KHR_materials_ior',
-		KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
-		KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
-		KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
-		KHR_MATERIALS_IRIDESCENCE: 'KHR_materials_iridescence',
-		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
-		KHR_MATERIALS_VOLUME: 'KHR_materials_volume',
-		KHR_TEXTURE_BASISU: 'KHR_texture_basisu',
-		KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
-		KHR_MESH_QUANTIZATION: 'KHR_mesh_quantization',
-		KHR_MATERIALS_EMISSIVE_STRENGTH: 'KHR_materials_emissive_strength',
-		EXT_TEXTURE_WEBP: 'EXT_texture_webp',
-		EXT_MESHOPT_COMPRESSION: 'EXT_meshopt_compression',
-		EXT_MESH_GPU_INSTANCING: 'EXT_mesh_gpu_instancing'
-	};
-
-	/**
- * Punctual Lights Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
- */
-	class GLTFLightsExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
-
-			// THREE.Object3D instance caches
-			this.cache = {
-				refs: {},
-				uses: {}
-			};
-
-		}
-		_markDefs() {
-
-			const parser = this.parser;
-			const nodeDefs = this.parser.json.nodes || [];
-			for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
-
-				const nodeDef = nodeDefs[ nodeIndex ];
-				if ( nodeDef.extensions && nodeDef.extensions[ this.name ] && nodeDef.extensions[ this.name ].light !== undefined ) {
-
-					parser._addNodeRef( this.cache, nodeDef.extensions[ this.name ].light );
-
-				}
-
-			}
-
-		}
-		_loadLight( lightIndex ) {
-
-			const parser = this.parser;
-			const cacheKey = 'light:' + lightIndex;
-			let dependency = parser.cache.get( cacheKey );
-			if ( dependency ) return dependency;
-			const json = parser.json;
-			const extensions = json.extensions && json.extensions[ this.name ] || {};
-			const lightDefs = extensions.lights || [];
-			const lightDef = lightDefs[ lightIndex ];
-			let lightNode;
-			const color = new THREE.Color( 0xffffff );
-			if ( lightDef.color !== undefined ) color.fromArray( lightDef.color );
-			const range = lightDef.range !== undefined ? lightDef.range : 0;
-			switch ( lightDef.type ) {
-
-				case 'directional':
-					lightNode = new THREE.DirectionalLight( color );
-					lightNode.target.position.set( 0, 0, - 1 );
-					lightNode.add( lightNode.target );
-					break;
-				case 'point':
-					lightNode = new THREE.PointLight( color );
-					lightNode.distance = range;
-					break;
-				case 'spot':
-					lightNode = new THREE.SpotLight( color );
-					lightNode.distance = range;
-					// Handle spotlight properties.
-					lightDef.spot = lightDef.spot || {};
-					lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
-					lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
-					lightNode.angle = lightDef.spot.outerConeAngle;
-					lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
-					lightNode.target.position.set( 0, 0, - 1 );
-					lightNode.add( lightNode.target );
-					break;
-				default:
-					throw new Error( 'THREE.GLTFLoader: Unexpected light type: ' + lightDef.type );
-
-			}
-
-			// Some lights (e.g. spot) default to a position other than the origin. Reset the position
-			// here, because node-level parsing will only override position if explicitly specified.
-			lightNode.position.set( 0, 0, 0 );
-			lightNode.decay = 2;
-			assignExtrasToUserData( lightNode, lightDef );
-			if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;
-			lightNode.name = parser.createUniqueName( lightDef.name || 'light_' + lightIndex );
-			dependency = Promise.resolve( lightNode );
-			parser.cache.add( cacheKey, dependency );
-			return dependency;
-
-		}
-		getDependency( type, index ) {
-
-			if ( type !== 'light' ) return;
-			return this._loadLight( index );
-
-		}
-		createNodeAttachment( nodeIndex ) {
-
-			const self = this;
-			const parser = this.parser;
-			const json = parser.json;
-			const nodeDef = json.nodes[ nodeIndex ];
-			const lightDef = nodeDef.extensions && nodeDef.extensions[ this.name ] || {};
-			const lightIndex = lightDef.light;
-			if ( lightIndex === undefined ) return null;
-			return this._loadLight( lightIndex ).then( function ( light ) {
-
-				return parser._getNodeRef( self.cache, lightIndex, light );
-
-			} );
-
-		}
-
-	}
-
-	/**
- * Unlit Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
- */
-	class GLTFMaterialsUnlitExtension {
-
-		constructor() {
-
-			this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
-
-		}
-		getMaterialType() {
-
-			return THREE.MeshBasicMaterial;
-
-		}
-		extendParams( materialParams, materialDef, parser ) {
-
-			const pending = [];
-			materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
-			materialParams.opacity = 1.0;
-			const metallicRoughness = materialDef.pbrMetallicRoughness;
-			if ( metallicRoughness ) {
-
-				if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
-
-					const array = metallicRoughness.baseColorFactor;
-					materialParams.color.fromArray( array );
-					materialParams.opacity = array[ 3 ];
-
-				}
-
-				if ( metallicRoughness.baseColorTexture !== undefined ) {
-
-					pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, THREE.sRGBEncoding ) );
-
-				}
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Materials Emissive Strength Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/blob/5768b3ce0ef32bc39cdf1bef10b948586635ead3/extensions/2.0/Khronos/KHR_materials_emissive_strength/README.md
- */
-	class GLTFMaterialsEmissiveStrengthExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_EMISSIVE_STRENGTH;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const emissiveStrength = materialDef.extensions[ this.name ].emissiveStrength;
-			if ( emissiveStrength !== undefined ) {
-
-				materialParams.emissiveIntensity = emissiveStrength;
-
-			}
-
-			return Promise.resolve();
-
-		}
-
-	}
-
-	/**
- * Clearcoat Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat
- */
-	class GLTFMaterialsClearcoatExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_CLEARCOAT;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			const extension = materialDef.extensions[ this.name ];
-			if ( extension.clearcoatFactor !== undefined ) {
-
-				materialParams.clearcoat = extension.clearcoatFactor;
-
-			}
-
-			if ( extension.clearcoatTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'clearcoatMap', extension.clearcoatTexture ) );
-
-			}
-
-			if ( extension.clearcoatRoughnessFactor !== undefined ) {
-
-				materialParams.clearcoatRoughness = extension.clearcoatRoughnessFactor;
-
-			}
-
-			if ( extension.clearcoatRoughnessTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'clearcoatRoughnessMap', extension.clearcoatRoughnessTexture ) );
-
-			}
-
-			if ( extension.clearcoatNormalTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'clearcoatNormalMap', extension.clearcoatNormalTexture ) );
-				if ( extension.clearcoatNormalTexture.scale !== undefined ) {
-
-					const scale = extension.clearcoatNormalTexture.scale;
-					materialParams.clearcoatNormalScale = new THREE.Vector2( scale, scale );
-
-				}
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Iridescence Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_iridescence
- */
-	class GLTFMaterialsIridescenceExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_IRIDESCENCE;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			const extension = materialDef.extensions[ this.name ];
-			if ( extension.iridescenceFactor !== undefined ) {
-
-				materialParams.iridescence = extension.iridescenceFactor;
-
-			}
-
-			if ( extension.iridescenceTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'iridescenceMap', extension.iridescenceTexture ) );
-
-			}
-
-			if ( extension.iridescenceIor !== undefined ) {
-
-				materialParams.iridescenceIOR = extension.iridescenceIor;
-
-			}
-
-			if ( materialParams.iridescenceThicknessRange === undefined ) {
-
-				materialParams.iridescenceThicknessRange = [ 100, 400 ];
-
-			}
-
-			if ( extension.iridescenceThicknessMinimum !== undefined ) {
-
-				materialParams.iridescenceThicknessRange[ 0 ] = extension.iridescenceThicknessMinimum;
-
-			}
-
-			if ( extension.iridescenceThicknessMaximum !== undefined ) {
-
-				materialParams.iridescenceThicknessRange[ 1 ] = extension.iridescenceThicknessMaximum;
-
-			}
-
-			if ( extension.iridescenceThicknessTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'iridescenceThicknessMap', extension.iridescenceThicknessTexture ) );
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Sheen Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen
- */
-	class GLTFMaterialsSheenExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_SHEEN;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			materialParams.sheenColor = new THREE.Color( 0, 0, 0 );
-			materialParams.sheenRoughness = 0;
-			materialParams.sheen = 1;
-			const extension = materialDef.extensions[ this.name ];
-			if ( extension.sheenColorFactor !== undefined ) {
-
-				materialParams.sheenColor.fromArray( extension.sheenColorFactor );
-
-			}
-
-			if ( extension.sheenRoughnessFactor !== undefined ) {
-
-				materialParams.sheenRoughness = extension.sheenRoughnessFactor;
-
-			}
-
-			if ( extension.sheenColorTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture, THREE.sRGBEncoding ) );
-
-			}
-
-			if ( extension.sheenRoughnessTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) );
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Transmission Materials Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission
- * Draft: https://github.com/KhronosGroup/glTF/pull/1698
- */
-	class GLTFMaterialsTransmissionExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_TRANSMISSION;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			const extension = materialDef.extensions[ this.name ];
-			if ( extension.transmissionFactor !== undefined ) {
-
-				materialParams.transmission = extension.transmissionFactor;
-
-			}
-
-			if ( extension.transmissionTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'transmissionMap', extension.transmissionTexture ) );
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Materials Volume Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_volume
- */
-	class GLTFMaterialsVolumeExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_VOLUME;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			const extension = materialDef.extensions[ this.name ];
-			materialParams.thickness = extension.thicknessFactor !== undefined ? extension.thicknessFactor : 0;
-			if ( extension.thicknessTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'thicknessMap', extension.thicknessTexture ) );
-
-			}
-
-			materialParams.attenuationDistance = extension.attenuationDistance || Infinity;
-			const colorArray = extension.attenuationColor || [ 1, 1, 1 ];
-			materialParams.attenuationColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * Materials ior Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_ior
- */
-	class GLTFMaterialsIorExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_IOR;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const extension = materialDef.extensions[ this.name ];
-			materialParams.ior = extension.ior !== undefined ? extension.ior : 1.5;
-			return Promise.resolve();
-
-		}
-
-	}
-
-	/**
- * Materials specular Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_specular
- */
-	class GLTFMaterialsSpecularExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_MATERIALS_SPECULAR;
-
-		}
-		getMaterialType( materialIndex ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
-			return THREE.MeshPhysicalMaterial;
-
-		}
-		extendMaterialParams( materialIndex, materialParams ) {
-
-			const parser = this.parser;
-			const materialDef = parser.json.materials[ materialIndex ];
-			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
-
-				return Promise.resolve();
-
-			}
-
-			const pending = [];
-			const extension = materialDef.extensions[ this.name ];
-			materialParams.specularIntensity = extension.specularFactor !== undefined ? extension.specularFactor : 1.0;
-			if ( extension.specularTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'specularIntensityMap', extension.specularTexture ) );
-
-			}
-
-			const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
-			materialParams.specularColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
-			if ( extension.specularColorTexture !== undefined ) {
-
-				pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture, THREE.sRGBEncoding ) );
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-	}
-
-	/**
- * BasisU THREE.Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_basisu
- */
-	class GLTFTextureBasisUExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.KHR_TEXTURE_BASISU;
-
-		}
-		loadTexture( textureIndex ) {
-
-			const parser = this.parser;
-			const json = parser.json;
-			const textureDef = json.textures[ textureIndex ];
-			if ( ! textureDef.extensions || ! textureDef.extensions[ this.name ] ) {
-
-				return null;
-
-			}
-
-			const extension = textureDef.extensions[ this.name ];
-			const loader = parser.options.ktx2Loader;
-			if ( ! loader ) {
-
-				if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
-
-					throw new Error( 'THREE.GLTFLoader: setKTX2Loader must be called before loading KTX2 textures' );
-
-				} else {
-
-					// Assumes that the extension is optional and that a fallback texture is present
-					return null;
-
-				}
-
-			}
-
-			return parser.loadTextureImage( textureIndex, extension.source, loader );
-
-		}
-
-	}
-
-	/**
- * WebP THREE.Texture Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_texture_webp
- */
-	class GLTFTextureWebPExtension {
-
-		constructor( parser ) {
-
-			this.parser = parser;
-			this.name = EXTENSIONS.EXT_TEXTURE_WEBP;
-			this.isSupported = null;
-
-		}
-		loadTexture( textureIndex ) {
-
-			const name = this.name;
-			const parser = this.parser;
-			const json = parser.json;
-			const textureDef = json.textures[ textureIndex ];
-			if ( ! textureDef.extensions || ! textureDef.extensions[ name ] ) {
-
-				return null;
-
-			}
-
-			const extension = textureDef.extensions[ name ];
-			const source = json.images[ extension.source ];
-			let loader = parser.textureLoader;
-			if ( source.uri ) {
-
-				const handler = parser.options.manager.getHandler( source.uri );
-				if ( handler !== null ) loader = handler;
-
-			}
-
-			return this.detectSupport().then( function ( isSupported ) {
-
-				if ( isSupported ) return parser.loadTextureImage( textureIndex, extension.source, loader );
-				if ( json.extensionsRequired && json.extensionsRequired.indexOf( name ) >= 0 ) {
-
-					throw new Error( 'THREE.GLTFLoader: WebP required by asset but unsupported.' );
-
-				}
-
-				// Fall back to PNG or JPEG.
-				return parser.loadTexture( textureIndex );
-
-			} );
-
-		}
-		detectSupport() {
-
-			if ( ! this.isSupported ) {
-
-				this.isSupported = new Promise( function ( resolve ) {
-
-					const image = new Image();
-
-					// Lossy test image. Support for lossy images doesn't guarantee support for all
-					// WebP images, unfortunately.
-					image.src = 'data:image/webp;base64,UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA';
-					image.onload = image.onerror = function () {
-
-						resolve( image.height === 1 );
-
-					};
-
-				} );
-
-			}
-
-			return this.isSupported;
-
-		}
-
-	}
-
-	/**
- * meshopt BufferView Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_meshopt_compression
- */
-	class GLTFMeshoptCompression {
-
-		constructor( parser ) {
-
-			this.name = EXTENSIONS.EXT_MESHOPT_COMPRESSION;
-			this.parser = parser;
-
-		}
-		loadBufferView( index ) {
-
-			const json = this.parser.json;
-			const bufferView = json.bufferViews[ index ];
-			if ( bufferView.extensions && bufferView.extensions[ this.name ] ) {
-
-				const extensionDef = bufferView.extensions[ this.name ];
-				const buffer = this.parser.getDependency( 'buffer', extensionDef.buffer );
-				const decoder = this.parser.options.meshoptDecoder;
-				if ( ! decoder || ! decoder.supported ) {
-
-					if ( json.extensionsRequired && json.extensionsRequired.indexOf( this.name ) >= 0 ) {
-
-						throw new Error( 'THREE.GLTFLoader: setMeshoptDecoder must be called before loading compressed files' );
-
-					} else {
-
-						// Assumes that the extension is optional and that fallback buffer data is present
-						return null;
-
-					}
-
-				}
-
-				return buffer.then( function ( res ) {
-
-					const byteOffset = extensionDef.byteOffset || 0;
-					const byteLength = extensionDef.byteLength || 0;
-					const count = extensionDef.count;
-					const stride = extensionDef.byteStride;
-					const source = new Uint8Array( res, byteOffset, byteLength );
-					if ( decoder.decodeGltfBufferAsync ) {
-
-						return decoder.decodeGltfBufferAsync( count, stride, source, extensionDef.mode, extensionDef.filter ).then( function ( res ) {
-
-							return res.buffer;
-
-						} );
-
-					} else {
-
-						// Support for MeshoptDecoder 0.18 or earlier, without decodeGltfBufferAsync
-						return decoder.ready.then( function () {
-
-							const result = new ArrayBuffer( count * stride );
-							decoder.decodeGltfBuffer( new Uint8Array( result ), count, stride, source, extensionDef.mode, extensionDef.filter );
-							return result;
-
-						} );
-
-					}
-
-				} );
-
-			} else {
-
-				return null;
-
-			}
-
-		}
-
-	}
-
-	/**
- * GPU Instancing Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/EXT_mesh_gpu_instancing
- *
- */
-	class GLTFMeshGpuInstancing {
-
-		constructor( parser ) {
-
-			this.name = EXTENSIONS.EXT_MESH_GPU_INSTANCING;
-			this.parser = parser;
-
-		}
-		createNodeMesh( nodeIndex ) {
-
-			const json = this.parser.json;
-			const nodeDef = json.nodes[ nodeIndex ];
-			if ( ! nodeDef.extensions || ! nodeDef.extensions[ this.name ] || nodeDef.mesh === undefined ) {
-
-				return null;
-
-			}
-
-			const meshDef = json.meshes[ nodeDef.mesh ];
-
-			// No THREE.Points or Lines + Instancing support yet
-
-			for ( const primitive of meshDef.primitives ) {
-
-				if ( primitive.mode !== WEBGL_CONSTANTS.TRIANGLES && primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_STRIP && primitive.mode !== WEBGL_CONSTANTS.TRIANGLE_FAN && primitive.mode !== undefined ) {
-
-					return null;
-
-				}
-
-			}
-
-			const extensionDef = nodeDef.extensions[ this.name ];
-			const attributesDef = extensionDef.attributes;
-
-			// @TODO: Can we support THREE.InstancedMesh + THREE.SkinnedMesh?
-
-			const pending = [];
-			const attributes = {};
-			for ( const key in attributesDef ) {
-
-				pending.push( this.parser.getDependency( 'accessor', attributesDef[ key ] ).then( accessor => {
-
-					attributes[ key ] = accessor;
-					return attributes[ key ];
-
-				} ) );
-
-			}
-
-			if ( pending.length < 1 ) {
-
-				return null;
-
-			}
-
-			pending.push( this.parser.createNodeMesh( nodeIndex ) );
-			return Promise.all( pending ).then( results => {
-
-				const nodeObject = results.pop();
-				const meshes = nodeObject.isGroup ? nodeObject.children : [ nodeObject ];
-				const count = results[ 0 ].count; // All attribute counts should be same
-				const instancedMeshes = [];
-				for ( const mesh of meshes ) {
-
-					// Temporal variables
-					const m = new THREE.Matrix4();
-					const p = new THREE.Vector3();
-					const q = new THREE.Quaternion();
-					const s = new THREE.Vector3( 1, 1, 1 );
-					const instancedMesh = new THREE.InstancedMesh( mesh.geometry, mesh.material, count );
-					for ( let i = 0; i < count; i ++ ) {
-
-						if ( attributes.TRANSLATION ) {
-
-							p.fromBufferAttribute( attributes.TRANSLATION, i );
-
-						}
-
-						if ( attributes.ROTATION ) {
-
-							q.fromBufferAttribute( attributes.ROTATION, i );
-
-						}
-
-						if ( attributes.SCALE ) {
-
-							s.fromBufferAttribute( attributes.SCALE, i );
-
-						}
-
-						instancedMesh.setMatrixAt( i, m.compose( p, q, s ) );
-
-					}
-
-					// Add instance attributes to the geometry, excluding TRS.
-					for ( const attributeName in attributes ) {
-
-						if ( attributeName !== 'TRANSLATION' && attributeName !== 'ROTATION' && attributeName !== 'SCALE' ) {
-
-							mesh.geometry.setAttribute( attributeName, attributes[ attributeName ] );
-
-						}
-
-					}
-
-					// Just in case
-					THREE.Object3D.prototype.copy.call( instancedMesh, mesh );
-
-					// https://github.com/mrdoob/three.js/issues/18334
-					instancedMesh.frustumCulled = false;
-					this.parser.assignFinalMaterial( instancedMesh );
-					instancedMeshes.push( instancedMesh );
-
-				}
-
-				if ( nodeObject.isGroup ) {
-
-					nodeObject.clear();
-					nodeObject.add( ...instancedMeshes );
-					return nodeObject;
-
-				}
-
-				return instancedMeshes[ 0 ];
-
-			} );
-
-		}
-
-	}
-
-	/* BINARY EXTENSION */
-	const BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
-	const BINARY_EXTENSION_HEADER_LENGTH = 12;
-	const BINARY_EXTENSION_CHUNK_TYPES = {
-		JSON: 0x4E4F534A,
-		BIN: 0x004E4942
-	};
-	class GLTFBinaryExtension {
-
-		constructor( data ) {
-
-			this.name = EXTENSIONS.KHR_BINARY_GLTF;
-			this.content = null;
-			this.body = null;
-			const headerView = new DataView( data, 0, BINARY_EXTENSION_HEADER_LENGTH );
-			this.header = {
-				magic: THREE.LoaderUtils.decodeText( new Uint8Array( data.slice( 0, 4 ) ) ),
-				version: headerView.getUint32( 4, true ),
-				length: headerView.getUint32( 8, true )
-			};
-			if ( this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC ) {
-
-				throw new Error( 'THREE.GLTFLoader: Unsupported glTF-Binary header.' );
-
-			} else if ( this.header.version < 2.0 ) {
-
-				throw new Error( 'THREE.GLTFLoader: Legacy binary file detected.' );
-
-			}
-
-			const chunkContentsLength = this.header.length - BINARY_EXTENSION_HEADER_LENGTH;
-			const chunkView = new DataView( data, BINARY_EXTENSION_HEADER_LENGTH );
-			let chunkIndex = 0;
-			while ( chunkIndex < chunkContentsLength ) {
-
-				const chunkLength = chunkView.getUint32( chunkIndex, true );
-				chunkIndex += 4;
-				const chunkType = chunkView.getUint32( chunkIndex, true );
-				chunkIndex += 4;
-				if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON ) {
-
-					const contentArray = new Uint8Array( data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength );
-					this.content = THREE.LoaderUtils.decodeText( contentArray );
-
-				} else if ( chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN ) {
-
-					const byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
-					this.body = data.slice( byteOffset, byteOffset + chunkLength );
-
-				}
-
-				// Clients must ignore chunks with unknown types.
-
-				chunkIndex += chunkLength;
-
-			}
-
-			if ( this.content === null ) {
-
-				throw new Error( 'THREE.GLTFLoader: JSON content not found.' );
-
-			}
-
-		}
-
-	}
-
-	/**
- * DRACO THREE.Mesh Compression Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
- */
-	class GLTFDracoMeshCompressionExtension {
-
-		constructor( json, dracoLoader ) {
-
-			if ( ! dracoLoader ) {
-
-				throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );
-
-			}
-
-			this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
-			this.json = json;
-			this.dracoLoader = dracoLoader;
-			this.dracoLoader.preload();
-
-		}
-		decodePrimitive( primitive, parser ) {
-
-			const json = this.json;
-			const dracoLoader = this.dracoLoader;
-			const bufferViewIndex = primitive.extensions[ this.name ].bufferView;
-			const gltfAttributeMap = primitive.extensions[ this.name ].attributes;
-			const threeAttributeMap = {};
-			const attributeNormalizedMap = {};
-			const attributeTypeMap = {};
-			for ( const attributeName in gltfAttributeMap ) {
-
-				const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
-				threeAttributeMap[ threeAttributeName ] = gltfAttributeMap[ attributeName ];
-
-			}
-
-			for ( const attributeName in primitive.attributes ) {
-
-				const threeAttributeName = ATTRIBUTES[ attributeName ] || attributeName.toLowerCase();
-				if ( gltfAttributeMap[ attributeName ] !== undefined ) {
-
-					const accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
-					const componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
-					attributeTypeMap[ threeAttributeName ] = componentType.name;
-					attributeNormalizedMap[ threeAttributeName ] = accessorDef.normalized === true;
-
-				}
-
-			}
-
-			return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
-
-				return new Promise( function ( resolve ) {
-
-					dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
-
-						for ( const attributeName in geometry.attributes ) {
-
-							const attribute = geometry.attributes[ attributeName ];
-							const normalized = attributeNormalizedMap[ attributeName ];
-							if ( normalized !== undefined ) attribute.normalized = normalized;
-
-						}
-
-						resolve( geometry );
-
-					}, threeAttributeMap, attributeTypeMap );
-
-				} );
-
-			} );
-
-		}
-
-	}
-
-	/**
- * THREE.Texture Transform Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
- */
-	class GLTFTextureTransformExtension {
-
-		constructor() {
-
-			this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
-
-		}
-		extendTexture( texture, transform ) {
-
-			if ( transform.texCoord !== undefined ) {
-
-				console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );
-
-			}
-
-			if ( transform.offset === undefined && transform.rotation === undefined && transform.scale === undefined ) {
-
-				// See https://github.com/mrdoob/three.js/issues/21819.
-				return texture;
-
-			}
-
-			texture = texture.clone();
-			if ( transform.offset !== undefined ) {
-
-				texture.offset.fromArray( transform.offset );
-
-			}
-
-			if ( transform.rotation !== undefined ) {
-
-				texture.rotation = transform.rotation;
-
-			}
-
-			if ( transform.scale !== undefined ) {
-
-				texture.repeat.fromArray( transform.scale );
-
-			}
-
-			texture.needsUpdate = true;
-			return texture;
-
-		}
-
-	}
-
-	/**
- * THREE.Mesh Quantization Extension
- *
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization
- */
-	class GLTFMeshQuantizationExtension {
-
-		constructor() {
-
-			this.name = EXTENSIONS.KHR_MESH_QUANTIZATION;
-
-		}
-
-	}
-
-	/*********************************/
-	/********** INTERPOLATION ********/
-	/*********************************/
-
-	// Spline Interpolation
-	// Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
-	class GLTFCubicSplineInterpolant extends THREE.Interpolant {
-
-		constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
-
-			super( parameterPositions, sampleValues, sampleSize, resultBuffer );
-
-		}
-		copySampleValue_( index ) {
-
-			// Copies a sample value to the result buffer. See description of glTF
-			// CUBICSPLINE values layout in interpolate_() function below.
-
-			const result = this.resultBuffer,
-				values = this.sampleValues,
-				valueSize = this.valueSize,
-				offset = index * valueSize * 3 + valueSize;
-			for ( let i = 0; i !== valueSize; i ++ ) {
-
-				result[ i ] = values[ offset + i ];
-
-			}
-
-			return result;
-
-		}
-		interpolate_( i1, t0, t, t1 ) {
-
-			const result = this.resultBuffer;
-			const values = this.sampleValues;
-			const stride = this.valueSize;
-			const stride2 = stride * 2;
-			const stride3 = stride * 3;
-			const td = t1 - t0;
-			const p = ( t - t0 ) / td;
-			const pp = p * p;
-			const ppp = pp * p;
-			const offset1 = i1 * stride3;
-			const offset0 = offset1 - stride3;
-			const s2 = - 2 * ppp + 3 * pp;
-			const s3 = ppp - pp;
-			const s0 = 1 - s2;
-			const s1 = s3 - pp + p;
-
-			// Layout of keyframe output values for CUBICSPLINE animations:
-			//   [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
-			for ( let i = 0; i !== stride; i ++ ) {
-
-				const p0 = values[ offset0 + i + stride ]; // splineVertex_k
-				const m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k)
-				const p1 = values[ offset1 + i + stride ]; // splineVertex_k+1
-				const m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k)
-
-				result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
-
-			}
-
-			return result;
-
-		}
-
-	}
-	const _q = new THREE.Quaternion();
-	class GLTFCubicSplineQuaternionInterpolant extends GLTFCubicSplineInterpolant {
-
-		interpolate_( i1, t0, t, t1 ) {
-
-			const result = super.interpolate_( i1, t0, t, t1 );
-			_q.fromArray( result ).normalize().toArray( result );
-			return result;
-
-		}
-
-	}
-
-	/*********************************/
-	/********** INTERNALS ************/
-	/*********************************/
-
-	/* CONSTANTS */
-
-	const WEBGL_CONSTANTS = {
-		FLOAT: 5126,
-		//FLOAT_MAT2: 35674,
-		FLOAT_MAT3: 35675,
-		FLOAT_MAT4: 35676,
-		FLOAT_VEC2: 35664,
-		FLOAT_VEC3: 35665,
-		FLOAT_VEC4: 35666,
-		LINEAR: 9729,
-		REPEAT: 10497,
-		SAMPLER_2D: 35678,
-		POINTS: 0,
-		LINES: 1,
-		LINE_LOOP: 2,
-		LINE_STRIP: 3,
-		TRIANGLES: 4,
-		TRIANGLE_STRIP: 5,
-		TRIANGLE_FAN: 6,
-		UNSIGNED_BYTE: 5121,
-		UNSIGNED_SHORT: 5123
-	};
-	const WEBGL_COMPONENT_TYPES = {
-		5120: Int8Array,
-		5121: Uint8Array,
-		5122: Int16Array,
-		5123: Uint16Array,
-		5125: Uint32Array,
-		5126: Float32Array
-	};
-	const WEBGL_FILTERS = {
-		9728: THREE.NearestFilter,
-		9729: THREE.LinearFilter,
-		9984: THREE.NearestMipmapNearestFilter,
-		9985: THREE.LinearMipmapNearestFilter,
-		9986: THREE.NearestMipmapLinearFilter,
-		9987: THREE.LinearMipmapLinearFilter
-	};
-	const WEBGL_WRAPPINGS = {
-		33071: THREE.ClampToEdgeWrapping,
-		33648: THREE.MirroredRepeatWrapping,
-		10497: THREE.RepeatWrapping
-	};
-	const WEBGL_TYPE_SIZES = {
-		'SCALAR': 1,
-		'VEC2': 2,
-		'VEC3': 3,
-		'VEC4': 4,
-		'MAT2': 4,
-		'MAT3': 9,
-		'MAT4': 16
-	};
-	const ATTRIBUTES = {
-		POSITION: 'position',
-		NORMAL: 'normal',
-		TANGENT: 'tangent',
-		TEXCOORD_0: 'uv',
-		TEXCOORD_1: 'uv2',
-		COLOR_0: 'color',
-		WEIGHTS_0: 'skinWeight',
-		JOINTS_0: 'skinIndex'
-	};
-	const PATH_PROPERTIES = {
-		scale: 'scale',
-		translation: 'position',
-		rotation: 'quaternion',
-		weights: 'morphTargetInfluences'
-	};
-	const INTERPOLATION = {
-		CUBICSPLINE: undefined,
-		// We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
-		// keyframe track will be initialized with a default interpolation type, then modified.
-		LINEAR: THREE.InterpolateLinear,
-		STEP: THREE.InterpolateDiscrete
-	};
-	const ALPHA_MODES = {
-		OPAQUE: 'OPAQUE',
-		MASK: 'MASK',
-		BLEND: 'BLEND'
-	};
-
-	/**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
- */
-	function createDefaultMaterial( cache ) {
-
-		if ( cache[ 'DefaultMaterial' ] === undefined ) {
-
-			cache[ 'DefaultMaterial' ] = new THREE.MeshStandardMaterial( {
-				color: 0xFFFFFF,
-				emissive: 0x000000,
-				metalness: 1,
-				roughness: 1,
-				transparent: false,
-				depthTest: true,
-				side: THREE.FrontSide
-			} );
-
-		}
-
-		return cache[ 'DefaultMaterial' ];
-
-	}
-
-	function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
-
-		// Add unknown glTF extensions to an object's userData.
-
-		for ( const name in objectDef.extensions ) {
-
-			if ( knownExtensions[ name ] === undefined ) {
-
-				object.userData.gltfExtensions = object.userData.gltfExtensions || {};
-				object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ];
-
-			}
-
-		}
-
-	}
-
-	/**
- * @param {Object3D|Material|BufferGeometry} object
- * @param {GLTF.definition} gltfDef
- */
-	function assignExtrasToUserData( object, gltfDef ) {
-
-		if ( gltfDef.extras !== undefined ) {
-
-			if ( typeof gltfDef.extras === 'object' ) {
-
-				Object.assign( object.userData, gltfDef.extras );
-
-			} else {
-
-				console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras );
-
-			}
-
-		}
-
-	}
-
-	/**
- * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
- *
- * @param {BufferGeometry} geometry
- * @param {Array<GLTF.Target>} targets
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
-	function addMorphTargets( geometry, targets, parser ) {
-
-		let hasMorphPosition = false;
-		let hasMorphNormal = false;
-		let hasMorphColor = false;
-		for ( let i = 0, il = targets.length; i < il; i ++ ) {
-
-			const target = targets[ i ];
-			if ( target.POSITION !== undefined ) hasMorphPosition = true;
-			if ( target.NORMAL !== undefined ) hasMorphNormal = true;
-			if ( target.COLOR_0 !== undefined ) hasMorphColor = true;
-			if ( hasMorphPosition && hasMorphNormal && hasMorphColor ) break;
-
-		}
-
-		if ( ! hasMorphPosition && ! hasMorphNormal && ! hasMorphColor ) return Promise.resolve( geometry );
-		const pendingPositionAccessors = [];
-		const pendingNormalAccessors = [];
-		const pendingColorAccessors = [];
-		for ( let i = 0, il = targets.length; i < il; i ++ ) {
-
-			const target = targets[ i ];
-			if ( hasMorphPosition ) {
-
-				const pendingAccessor = target.POSITION !== undefined ? parser.getDependency( 'accessor', target.POSITION ) : geometry.attributes.position;
-				pendingPositionAccessors.push( pendingAccessor );
-
-			}
-
-			if ( hasMorphNormal ) {
-
-				const pendingAccessor = target.NORMAL !== undefined ? parser.getDependency( 'accessor', target.NORMAL ) : geometry.attributes.normal;
-				pendingNormalAccessors.push( pendingAccessor );
-
-			}
-
-			if ( hasMorphColor ) {
-
-				const pendingAccessor = target.COLOR_0 !== undefined ? parser.getDependency( 'accessor', target.COLOR_0 ) : geometry.attributes.color;
-				pendingColorAccessors.push( pendingAccessor );
-
-			}
-
-		}
-
-		return Promise.all( [ Promise.all( pendingPositionAccessors ), Promise.all( pendingNormalAccessors ), Promise.all( pendingColorAccessors ) ] ).then( function ( accessors ) {
-
-			const morphPositions = accessors[ 0 ];
-			const morphNormals = accessors[ 1 ];
-			const morphColors = accessors[ 2 ];
-			if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
-			if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
-			if ( hasMorphColor ) geometry.morphAttributes.color = morphColors;
-			geometry.morphTargetsRelative = true;
-			return geometry;
-
-		} );
-
-	}
-
-	/**
- * @param {Mesh} mesh
- * @param {GLTF.Mesh} meshDef
- */
-	function updateMorphTargets( mesh, meshDef ) {
-
-		mesh.updateMorphTargets();
-		if ( meshDef.weights !== undefined ) {
-
-			for ( let i = 0, il = meshDef.weights.length; i < il; i ++ ) {
-
-				mesh.morphTargetInfluences[ i ] = meshDef.weights[ i ];
-
-			}
-
-		}
-
-		// .extras has user-defined data, so check that .extras.targetNames is an array.
-		if ( meshDef.extras && Array.isArray( meshDef.extras.targetNames ) ) {
-
-			const targetNames = meshDef.extras.targetNames;
-			if ( mesh.morphTargetInfluences.length === targetNames.length ) {
-
-				mesh.morphTargetDictionary = {};
-				for ( let i = 0, il = targetNames.length; i < il; i ++ ) {
-
-					mesh.morphTargetDictionary[ targetNames[ i ] ] = i;
-
-				}
-
-			} else {
-
-				console.warn( 'THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.' );
-
-			}
-
-		}
-
-	}
-
-	function createPrimitiveKey( primitiveDef ) {
-
-		const dracoExtension = primitiveDef.extensions && primitiveDef.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ];
-		let geometryKey;
-		if ( dracoExtension ) {
-
-			geometryKey = 'draco:' + dracoExtension.bufferView + ':' + dracoExtension.indices + ':' + createAttributesKey( dracoExtension.attributes );
-
-		} else {
-
-			geometryKey = primitiveDef.indices + ':' + createAttributesKey( primitiveDef.attributes ) + ':' + primitiveDef.mode;
-
-		}
-
-		return geometryKey;
-
-	}
-
-	function createAttributesKey( attributes ) {
-
-		let attributesKey = '';
-		const keys = Object.keys( attributes ).sort();
-		for ( let i = 0, il = keys.length; i < il; i ++ ) {
-
-			attributesKey += keys[ i ] + ':' + attributes[ keys[ i ] ] + ';';
-
-		}
-
-		return attributesKey;
-
-	}
-
-	function getNormalizedComponentScale( constructor ) {
-
-		// Reference:
-		// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
-
-		switch ( constructor ) {
-
-			case Int8Array:
-				return 1 / 127;
-			case Uint8Array:
-				return 1 / 255;
-			case Int16Array:
-				return 1 / 32767;
-			case Uint16Array:
-				return 1 / 65535;
-			default:
-				throw new Error( 'THREE.GLTFLoader: Unsupported normalized accessor component type.' );
-
-		}
-
-	}
-
-	function getImageURIMimeType( uri ) {
-
-		if ( uri.search( /\.jpe?g($|\?)/i ) > 0 || uri.search( /^data\:image\/jpeg/ ) === 0 ) return 'image/jpeg';
-		if ( uri.search( /\.webp($|\?)/i ) > 0 || uri.search( /^data\:image\/webp/ ) === 0 ) return 'image/webp';
-		return 'image/png';
-
-	}
-
-	/* GLTF PARSER */
-
-	class GLTFParser {
-
-		constructor( json = {}, options = {} ) {
-
-			this.json = json;
-			this.extensions = {};
-			this.plugins = {};
-			this.options = options;
-
-			// loader object cache
-			this.cache = new GLTFRegistry();
-
-			// associations between Three.js objects and glTF elements
-			this.associations = new Map();
-
-			// THREE.BufferGeometry caching
-			this.primitiveCache = {};
-
-			// THREE.Object3D instance caches
-			this.meshCache = {
-				refs: {},
-				uses: {}
-			};
-			this.cameraCache = {
-				refs: {},
-				uses: {}
-			};
-			this.lightCache = {
-				refs: {},
-				uses: {}
-			};
-			this.sourceCache = {};
-			this.textureCache = {};
-
-			// Track node names, to ensure no duplicates
-			this.nodeNamesUsed = {};
-
-			// Use an THREE.ImageBitmapLoader if imageBitmaps are supported. Moves much of the
-			// expensive work of uploading a texture to the GPU off the main thread.
-
-			let isSafari = false;
-			let isFirefox = false;
-			let firefoxVersion = - 1;
-			if ( typeof navigator !== 'undefined' ) {
-
-				isSafari = /^((?!chrome|android).)*safari/i.test( navigator.userAgent ) === true;
-				isFirefox = navigator.userAgent.indexOf( 'Firefox' ) > - 1;
-				firefoxVersion = isFirefox ? navigator.userAgent.match( /Firefox\/([0-9]+)\./ )[ 1 ] : - 1;
-
-			}
-
-			if ( typeof createImageBitmap === 'undefined' || isSafari || isFirefox && firefoxVersion < 98 ) {
-
-				this.textureLoader = new THREE.TextureLoader( this.options.manager );
-
-			} else {
-
-				this.textureLoader = new THREE.ImageBitmapLoader( this.options.manager );
-
-			}
-
-			this.textureLoader.setCrossOrigin( this.options.crossOrigin );
-			this.textureLoader.setRequestHeader( this.options.requestHeader );
-			this.fileLoader = new THREE.FileLoader( this.options.manager );
-			this.fileLoader.setResponseType( 'arraybuffer' );
-			if ( this.options.crossOrigin === 'use-credentials' ) {
-
-				this.fileLoader.setWithCredentials( true );
-
-			}
-
-		}
-		setExtensions( extensions ) {
-
-			this.extensions = extensions;
-
-		}
-		setPlugins( plugins ) {
-
-			this.plugins = plugins;
-
-		}
-		parse( onLoad, onError ) {
-
-			const parser = this;
-			const json = this.json;
-			const extensions = this.extensions;
-
-			// Clear the loader cache
-			this.cache.removeAll();
-
-			// Mark the special nodes/meshes in json for efficient parse
-			this._invokeAll( function ( ext ) {
-
-				return ext._markDefs && ext._markDefs();
-
-			} );
-			Promise.all( this._invokeAll( function ( ext ) {
-
-				return ext.beforeRoot && ext.beforeRoot();
-
-			} ) ).then( function () {
-
-				return Promise.all( [ parser.getDependencies( 'scene' ), parser.getDependencies( 'animation' ), parser.getDependencies( 'camera' ) ] );
-
-			} ).then( function ( dependencies ) {
-
-				const result = {
-					scene: dependencies[ 0 ][ json.scene || 0 ],
-					scenes: dependencies[ 0 ],
-					animations: dependencies[ 1 ],
-					cameras: dependencies[ 2 ],
-					asset: json.asset,
-					parser: parser,
-					userData: {}
-				};
-				addUnknownExtensionsToUserData( extensions, result, json );
-				assignExtrasToUserData( result, json );
-				Promise.all( parser._invokeAll( function ( ext ) {
-
-					return ext.afterRoot && ext.afterRoot( result );
-
-				} ) ).then( function () {
-
-					onLoad( result );
-
-				} );
-
-			} ).catch( onError );
-
-		}
-
-		/**
-   * Marks the special nodes/meshes in json for efficient parse.
-   */
-		_markDefs() {
-
-			const nodeDefs = this.json.nodes || [];
-			const skinDefs = this.json.skins || [];
-			const meshDefs = this.json.meshes || [];
-
-			// Nothing in the node definition indicates whether it is a THREE.Bone or an
-			// THREE.Object3D. Use the skins' joint references to mark bones.
-			for ( let skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex ++ ) {
-
-				const joints = skinDefs[ skinIndex ].joints;
-				for ( let i = 0, il = joints.length; i < il; i ++ ) {
-
-					nodeDefs[ joints[ i ] ].isBone = true;
-
-				}
-
-			}
-
-			// Iterate over all nodes, marking references to shared resources,
-			// as well as skeleton joints.
-			for ( let nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex ++ ) {
-
-				const nodeDef = nodeDefs[ nodeIndex ];
-				if ( nodeDef.mesh !== undefined ) {
-
-					this._addNodeRef( this.meshCache, nodeDef.mesh );
-
-					// Nothing in the mesh definition indicates whether it is
-					// a THREE.SkinnedMesh or THREE.Mesh. Use the node's mesh reference
-					// to mark THREE.SkinnedMesh if node has skin.
-					if ( nodeDef.skin !== undefined ) {
-
-						meshDefs[ nodeDef.mesh ].isSkinnedMesh = true;
-
-					}
-
-				}
-
-				if ( nodeDef.camera !== undefined ) {
-
-					this._addNodeRef( this.cameraCache, nodeDef.camera );
-
-				}
-
-			}
-
-		}
-
-		/**
-   * Counts references to shared node / THREE.Object3D resources. These resources
-   * can be reused, or "instantiated", at multiple nodes in the scene
-   * hierarchy. THREE.Mesh, Camera, and Light instances are instantiated and must
-   * be marked. Non-scenegraph resources (like Materials, Geometries, and
-   * Textures) can be reused directly and are not marked here.
-   *
-   * Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
-   */
-		_addNodeRef( cache, index ) {
-
-			if ( index === undefined ) return;
-			if ( cache.refs[ index ] === undefined ) {
-
-				cache.refs[ index ] = cache.uses[ index ] = 0;
-
-			}
-
-			cache.refs[ index ] ++;
-
-		}
-
-		/** Returns a reference to a shared resource, cloning it if necessary. */
-		_getNodeRef( cache, index, object ) {
-
-			if ( cache.refs[ index ] <= 1 ) return object;
-			const ref = object.clone();
-
-			// Propagates mappings to the cloned object, prevents mappings on the
-			// original object from being lost.
-			const updateMappings = ( original, clone ) => {
-
-				const mappings = this.associations.get( original );
-				if ( mappings != null ) {
-
-					this.associations.set( clone, mappings );
-
-				}
-
-				for ( const [ i, child ] of original.children.entries() ) {
-
-					updateMappings( child, clone.children[ i ] );
-
-				}
-
-			};
-
-			updateMappings( object, ref );
-			ref.name += '_instance_' + cache.uses[ index ] ++;
-			return ref;
-
-		}
-		_invokeOne( func ) {
-
-			const extensions = Object.values( this.plugins );
-			extensions.push( this );
-			for ( let i = 0; i < extensions.length; i ++ ) {
-
-				const result = func( extensions[ i ] );
-				if ( result ) return result;
-
-			}
-
-			return null;
-
-		}
-		_invokeAll( func ) {
-
-			const extensions = Object.values( this.plugins );
-			extensions.unshift( this );
-			const pending = [];
-			for ( let i = 0; i < extensions.length; i ++ ) {
-
-				const result = func( extensions[ i ] );
-				if ( result ) pending.push( result );
-
-			}
-
-			return pending;
-
-		}
-
-		/**
-   * Requests the specified dependency asynchronously, with caching.
-   * @param {string} type
-   * @param {number} index
-   * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
-   */
-		getDependency( type, index ) {
-
-			const cacheKey = type + ':' + index;
-			let dependency = this.cache.get( cacheKey );
-			if ( ! dependency ) {
-
-				switch ( type ) {
-
-					case 'scene':
-						dependency = this.loadScene( index );
-						break;
-					case 'node':
-						dependency = this.loadNode( index );
-						break;
-					case 'mesh':
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext.loadMesh && ext.loadMesh( index );
-
-						} );
-						break;
-					case 'accessor':
-						dependency = this.loadAccessor( index );
-						break;
-					case 'bufferView':
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext.loadBufferView && ext.loadBufferView( index );
-
-						} );
-						break;
-					case 'buffer':
-						dependency = this.loadBuffer( index );
-						break;
-					case 'material':
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext.loadMaterial && ext.loadMaterial( index );
-
-						} );
-						break;
-					case 'texture':
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext.loadTexture && ext.loadTexture( index );
-
-						} );
-						break;
-					case 'skin':
-						dependency = this.loadSkin( index );
-						break;
-					case 'animation':
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext.loadAnimation && ext.loadAnimation( index );
-
-						} );
-						break;
-					case 'camera':
-						dependency = this.loadCamera( index );
-						break;
-					default:
-						dependency = this._invokeOne( function ( ext ) {
-
-							return ext != this && ext.getDependency && ext.getDependency( type, index );
-
-						} );
-						if ( ! dependency ) {
-
-							throw new Error( 'Unknown type: ' + type );
-
-						}
-
-						break;
-
-				}
-
-				this.cache.add( cacheKey, dependency );
-
-			}
-
-			return dependency;
-
-		}
-
-		/**
-   * Requests all dependencies of the specified type asynchronously, with caching.
-   * @param {string} type
-   * @return {Promise<Array<Object>>}
-   */
-		getDependencies( type ) {
-
-			let dependencies = this.cache.get( type );
-			if ( ! dependencies ) {
-
-				const parser = this;
-				const defs = this.json[ type + ( type === 'mesh' ? 'es' : 's' ) ] || [];
-				dependencies = Promise.all( defs.map( function ( def, index ) {
-
-					return parser.getDependency( type, index );
-
-				} ) );
-				this.cache.add( type, dependencies );
-
-			}
-
-			return dependencies;
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
-   * @param {number} bufferIndex
-   * @return {Promise<ArrayBuffer>}
-   */
-		loadBuffer( bufferIndex ) {
-
-			const bufferDef = this.json.buffers[ bufferIndex ];
-			const loader = this.fileLoader;
-			if ( bufferDef.type && bufferDef.type !== 'arraybuffer' ) {
-
-				throw new Error( 'THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.' );
-
-			}
-
-			// If present, GLB container is required to be the first buffer.
-			if ( bufferDef.uri === undefined && bufferIndex === 0 ) {
-
-				return Promise.resolve( this.extensions[ EXTENSIONS.KHR_BINARY_GLTF ].body );
-
-			}
-
-			const options = this.options;
-			return new Promise( function ( resolve, reject ) {
-
-				loader.load( THREE.LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
-
-					reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) );
-
-				} );
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
-   * @param {number} bufferViewIndex
-   * @return {Promise<ArrayBuffer>}
-   */
-		loadBufferView( bufferViewIndex ) {
-
-			const bufferViewDef = this.json.bufferViews[ bufferViewIndex ];
-			return this.getDependency( 'buffer', bufferViewDef.buffer ).then( function ( buffer ) {
-
-				const byteLength = bufferViewDef.byteLength || 0;
-				const byteOffset = bufferViewDef.byteOffset || 0;
-				return buffer.slice( byteOffset, byteOffset + byteLength );
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
-   * @param {number} accessorIndex
-   * @return {Promise<BufferAttribute|InterleavedBufferAttribute>}
-   */
-		loadAccessor( accessorIndex ) {
-
-			const parser = this;
-			const json = this.json;
-			const accessorDef = this.json.accessors[ accessorIndex ];
-			if ( accessorDef.bufferView === undefined && accessorDef.sparse === undefined ) {
-
-				const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ];
-				const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
-				const normalized = accessorDef.normalized === true;
-				const array = new TypedArray( accessorDef.count * itemSize );
-				return Promise.resolve( new THREE.BufferAttribute( array, itemSize, normalized ) );
-
-			}
-
-			const pendingBufferViews = [];
-			if ( accessorDef.bufferView !== undefined ) {
-
-				pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.bufferView ) );
-
-			} else {
-
-				pendingBufferViews.push( null );
-
-			}
-
-			if ( accessorDef.sparse !== undefined ) {
-
-				pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.indices.bufferView ) );
-				pendingBufferViews.push( this.getDependency( 'bufferView', accessorDef.sparse.values.bufferView ) );
-
-			}
-
-			return Promise.all( pendingBufferViews ).then( function ( bufferViews ) {
-
-				const bufferView = bufferViews[ 0 ];
-				const itemSize = WEBGL_TYPE_SIZES[ accessorDef.type ];
-				const TypedArray = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
-
-				// For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
-				const elementBytes = TypedArray.BYTES_PER_ELEMENT;
-				const itemBytes = elementBytes * itemSize;
-				const byteOffset = accessorDef.byteOffset || 0;
-				const byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[ accessorDef.bufferView ].byteStride : undefined;
-				const normalized = accessorDef.normalized === true;
-				let array, bufferAttribute;
-
-				// The buffer is not interleaved if the stride is the item size in bytes.
-				if ( byteStride && byteStride !== itemBytes ) {
-
-					// Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own THREE.InterleavedBuffer
-					// This makes sure that IBA.count reflects accessor.count properly
-					const ibSlice = Math.floor( byteOffset / byteStride );
-					const ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count;
-					let ib = parser.cache.get( ibCacheKey );
-					if ( ! ib ) {
-
-						array = new TypedArray( bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes );
-
-						// Integer parameters to IB/IBA are in array elements, not bytes.
-						ib = new THREE.InterleavedBuffer( array, byteStride / elementBytes );
-						parser.cache.add( ibCacheKey, ib );
-
-					}
-
-					bufferAttribute = new THREE.InterleavedBufferAttribute( ib, itemSize, byteOffset % byteStride / elementBytes, normalized );
-
-				} else {
-
-					if ( bufferView === null ) {
-
-						array = new TypedArray( accessorDef.count * itemSize );
-
-					} else {
-
-						array = new TypedArray( bufferView, byteOffset, accessorDef.count * itemSize );
-
-					}
-
-					bufferAttribute = new THREE.BufferAttribute( array, itemSize, normalized );
-
-				}
-
-				// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
-				if ( accessorDef.sparse !== undefined ) {
-
-					const itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR;
-					const TypedArrayIndices = WEBGL_COMPONENT_TYPES[ accessorDef.sparse.indices.componentType ];
-					const byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0;
-					const byteOffsetValues = accessorDef.sparse.values.byteOffset || 0;
-					const sparseIndices = new TypedArrayIndices( bufferViews[ 1 ], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices );
-					const sparseValues = new TypedArray( bufferViews[ 2 ], byteOffsetValues, accessorDef.sparse.count * itemSize );
-					if ( bufferView !== null ) {
-
-						// Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
-						bufferAttribute = new THREE.BufferAttribute( bufferAttribute.array.slice(), bufferAttribute.itemSize, bufferAttribute.normalized );
-
-					}
-
-					for ( let i = 0, il = sparseIndices.length; i < il; i ++ ) {
-
-						const index = sparseIndices[ i ];
-						bufferAttribute.setX( index, sparseValues[ i * itemSize ] );
-						if ( itemSize >= 2 ) bufferAttribute.setY( index, sparseValues[ i * itemSize + 1 ] );
-						if ( itemSize >= 3 ) bufferAttribute.setZ( index, sparseValues[ i * itemSize + 2 ] );
-						if ( itemSize >= 4 ) bufferAttribute.setW( index, sparseValues[ i * itemSize + 3 ] );
-						if ( itemSize >= 5 ) throw new Error( 'THREE.GLTFLoader: Unsupported itemSize in sparse THREE.BufferAttribute.' );
-
-					}
-
-				}
-
-				return bufferAttribute;
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
-   * @param {number} textureIndex
-   * @return {Promise<THREE.Texture|null>}
-   */
-		loadTexture( textureIndex ) {
-
-			const json = this.json;
-			const options = this.options;
-			const textureDef = json.textures[ textureIndex ];
-			const sourceIndex = textureDef.source;
-			const sourceDef = json.images[ sourceIndex ];
-			let loader = this.textureLoader;
-			if ( sourceDef.uri ) {
-
-				const handler = options.manager.getHandler( sourceDef.uri );
-				if ( handler !== null ) loader = handler;
-
-			}
-
-			return this.loadTextureImage( textureIndex, sourceIndex, loader );
-
-		}
-		loadTextureImage( textureIndex, sourceIndex, loader ) {
-
-			const parser = this;
-			const json = this.json;
-			const textureDef = json.textures[ textureIndex ];
-			const sourceDef = json.images[ sourceIndex ];
-			const cacheKey = ( sourceDef.uri || sourceDef.bufferView ) + ':' + textureDef.sampler;
-			if ( this.textureCache[ cacheKey ] ) {
-
-				// See https://github.com/mrdoob/three.js/issues/21559.
-				return this.textureCache[ cacheKey ];
-
-			}
-
-			const promise = this.loadImageSource( sourceIndex, loader ).then( function ( texture ) {
-
-				texture.flipY = false;
-				texture.name = textureDef.name || sourceDef.name || '';
-				const samplers = json.samplers || {};
-				const sampler = samplers[ textureDef.sampler ] || {};
-				texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
-				texture.minFilter = WEBGL_FILTERS[ sampler.minFilter ] || THREE.LinearMipmapLinearFilter;
-				texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
-				texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
-				parser.associations.set( texture, {
-					textures: textureIndex
-				} );
-				return texture;
-
-			} ).catch( function () {
-
-				return null;
-
-			} );
-			this.textureCache[ cacheKey ] = promise;
-			return promise;
-
-		}
-		loadImageSource( sourceIndex, loader ) {
-
-			const parser = this;
-			const json = this.json;
-			const options = this.options;
-			if ( this.sourceCache[ sourceIndex ] !== undefined ) {
-
-				return this.sourceCache[ sourceIndex ].then( texture => texture.clone() );
-
-			}
-
-			const sourceDef = json.images[ sourceIndex ];
-			const URL = self.URL || self.webkitURL;
-			let sourceURI = sourceDef.uri || '';
-			let isObjectURL = false;
-			if ( sourceDef.bufferView !== undefined ) {
-
-				// Load binary image data from bufferView, if provided.
-
-				sourceURI = parser.getDependency( 'bufferView', sourceDef.bufferView ).then( function ( bufferView ) {
-
-					isObjectURL = true;
-					const blob = new Blob( [ bufferView ], {
-						type: sourceDef.mimeType
-					} );
-					sourceURI = URL.createObjectURL( blob );
-					return sourceURI;
-
-				} );
-
-			} else if ( sourceDef.uri === undefined ) {
-
-				throw new Error( 'THREE.GLTFLoader: Image ' + sourceIndex + ' is missing URI and bufferView' );
-
-			}
-
-			const promise = Promise.resolve( sourceURI ).then( function ( sourceURI ) {
-
-				return new Promise( function ( resolve, reject ) {
-
-					let onLoad = resolve;
-					if ( loader.isImageBitmapLoader === true ) {
-
-						onLoad = function ( imageBitmap ) {
-
-							const texture = new THREE.Texture( imageBitmap );
-							texture.needsUpdate = true;
-							resolve( texture );
-
-						};
-
-					}
-
-					loader.load( THREE.LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
-
-				} );
-
-			} ).then( function ( texture ) {
-
-				// Clean up resources and configure THREE.Texture.
-
-				if ( isObjectURL === true ) {
-
-					URL.revokeObjectURL( sourceURI );
-
-				}
-
-				texture.userData.mimeType = sourceDef.mimeType || getImageURIMimeType( sourceDef.uri );
-				return texture;
-
-			} ).catch( function ( error ) {
-
-				console.error( 'THREE.GLTFLoader: Couldn\'t load texture', sourceURI );
-				throw error;
-
-			} );
-			this.sourceCache[ sourceIndex ] = promise;
-			return promise;
-
-		}
-
-		/**
-   * Asynchronously assigns a texture to the given material parameters.
-   * @param {Object} materialParams
-   * @param {string} mapName
-   * @param {Object} mapDef
-   * @return {Promise<Texture>}
-   */
-		assignTexture( materialParams, mapName, mapDef, encoding ) {
-
-			const parser = this;
-			return this.getDependency( 'texture', mapDef.index ).then( function ( texture ) {
-
-				if ( ! texture ) return null;
-
-				// Materials sample aoMap from UV set 1 and other maps from UV set 0 - this can't be configured
-				// However, we will copy UV set 0 to UV set 1 on demand for aoMap
-				if ( mapDef.texCoord !== undefined && mapDef.texCoord != 0 && ! ( mapName === 'aoMap' && mapDef.texCoord == 1 ) ) {
-
-					console.warn( 'THREE.GLTFLoader: Custom UV set ' + mapDef.texCoord + ' for texture ' + mapName + ' not yet supported.' );
-
-				}
-
-				if ( parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] ) {
-
-					const transform = mapDef.extensions !== undefined ? mapDef.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] : undefined;
-					if ( transform ) {
-
-						const gltfReference = parser.associations.get( texture );
-						texture = parser.extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ].extendTexture( texture, transform );
-						parser.associations.set( texture, gltfReference );
-
-					}
-
-				}
-
-				if ( encoding !== undefined ) {
-
-					texture.encoding = encoding;
-
-				}
-
-				materialParams[ mapName ] = texture;
-				return texture;
-
-			} );
-
-		}
-
-		/**
-   * Assigns final material to a THREE.Mesh, THREE.Line, or THREE.Points instance. The instance
-   * already has a material (generated from the glTF material options alone)
-   * but reuse of the same glTF material may require multiple threejs materials
-   * to accommodate different primitive types, defines, etc. New materials will
-   * be created if necessary, and reused from a cache.
-   * @param  {Object3D} mesh THREE.Mesh, THREE.Line, or THREE.Points instance.
-   */
-		assignFinalMaterial( mesh ) {
-
-			const geometry = mesh.geometry;
-			let material = mesh.material;
-			const useDerivativeTangents = geometry.attributes.tangent === undefined;
-			const useVertexColors = geometry.attributes.color !== undefined;
-			const useFlatShading = geometry.attributes.normal === undefined;
-			if ( mesh.isPoints ) {
-
-				const cacheKey = 'PointsMaterial:' + material.uuid;
-				let pointsMaterial = this.cache.get( cacheKey );
-				if ( ! pointsMaterial ) {
-
-					pointsMaterial = new THREE.PointsMaterial();
-					THREE.Material.prototype.copy.call( pointsMaterial, material );
-					pointsMaterial.color.copy( material.color );
-					pointsMaterial.map = material.map;
-					pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
-
-					this.cache.add( cacheKey, pointsMaterial );
-
-				}
-
-				material = pointsMaterial;
-
-			} else if ( mesh.isLine ) {
-
-				const cacheKey = 'LineBasicMaterial:' + material.uuid;
-				let lineMaterial = this.cache.get( cacheKey );
-				if ( ! lineMaterial ) {
-
-					lineMaterial = new THREE.LineBasicMaterial();
-					THREE.Material.prototype.copy.call( lineMaterial, material );
-					lineMaterial.color.copy( material.color );
-					this.cache.add( cacheKey, lineMaterial );
-
-				}
-
-				material = lineMaterial;
-
-			}
-
-			// Clone the material if it will be modified
-			if ( useDerivativeTangents || useVertexColors || useFlatShading ) {
-
-				let cacheKey = 'ClonedMaterial:' + material.uuid + ':';
-				if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:';
-				if ( useVertexColors ) cacheKey += 'vertex-colors:';
-				if ( useFlatShading ) cacheKey += 'flat-shading:';
-				let cachedMaterial = this.cache.get( cacheKey );
-				if ( ! cachedMaterial ) {
-
-					cachedMaterial = material.clone();
-					if ( useVertexColors ) cachedMaterial.vertexColors = true;
-					if ( useFlatShading ) cachedMaterial.flatShading = true;
-					if ( useDerivativeTangents ) {
-
-						// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-						if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1;
-						if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1;
-
-					}
-
-					this.cache.add( cacheKey, cachedMaterial );
-					this.associations.set( cachedMaterial, this.associations.get( material ) );
-
-				}
-
-				material = cachedMaterial;
-
-			}
-
-			// workarounds for mesh and geometry
-
-			if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
-
-				geometry.setAttribute( 'uv2', geometry.attributes.uv );
-
-			}
-
-			mesh.material = material;
-
-		}
-		getMaterialType() {
-
-			return THREE.MeshStandardMaterial;
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
-   * @param {number} materialIndex
-   * @return {Promise<Material>}
-   */
-		loadMaterial( materialIndex ) {
-
-			const parser = this;
-			const json = this.json;
-			const extensions = this.extensions;
-			const materialDef = json.materials[ materialIndex ];
-			let materialType;
-			const materialParams = {};
-			const materialExtensions = materialDef.extensions || {};
-			const pending = [];
-			if ( materialExtensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ] ) {
-
-				const kmuExtension = extensions[ EXTENSIONS.KHR_MATERIALS_UNLIT ];
-				materialType = kmuExtension.getMaterialType();
-				pending.push( kmuExtension.extendParams( materialParams, materialDef, parser ) );
-
-			} else {
-
-				// Specification:
-				// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
-
-				const metallicRoughness = materialDef.pbrMetallicRoughness || {};
-				materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
-				materialParams.opacity = 1.0;
-				if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
-
-					const array = metallicRoughness.baseColorFactor;
-					materialParams.color.fromArray( array );
-					materialParams.opacity = array[ 3 ];
-
-				}
-
-				if ( metallicRoughness.baseColorTexture !== undefined ) {
-
-					pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture, THREE.sRGBEncoding ) );
-
-				}
-
-				materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
-				materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;
-				if ( metallicRoughness.metallicRoughnessTexture !== undefined ) {
-
-					pending.push( parser.assignTexture( materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture ) );
-					pending.push( parser.assignTexture( materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture ) );
-
-				}
-
-				materialType = this._invokeOne( function ( ext ) {
-
-					return ext.getMaterialType && ext.getMaterialType( materialIndex );
-
-				} );
-				pending.push( Promise.all( this._invokeAll( function ( ext ) {
-
-					return ext.extendMaterialParams && ext.extendMaterialParams( materialIndex, materialParams );
-
-				} ) ) );
-
-			}
-
-			if ( materialDef.doubleSided === true ) {
-
-				materialParams.side = THREE.DoubleSide;
-
-			}
-
-			const alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE;
-			if ( alphaMode === ALPHA_MODES.BLEND ) {
-
-				materialParams.transparent = true;
-
-				// See: https://github.com/mrdoob/three.js/issues/17706
-				materialParams.depthWrite = false;
-
-			} else {
-
-				materialParams.transparent = false;
-				if ( alphaMode === ALPHA_MODES.MASK ) {
-
-					materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;
-
-				}
-
-			}
-
-			if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
-
-				pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
-				materialParams.normalScale = new THREE.Vector2( 1, 1 );
-				if ( materialDef.normalTexture.scale !== undefined ) {
-
-					const scale = materialDef.normalTexture.scale;
-					materialParams.normalScale.set( scale, scale );
-
-				}
-
-			}
-
-			if ( materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
-
-				pending.push( parser.assignTexture( materialParams, 'aoMap', materialDef.occlusionTexture ) );
-				if ( materialDef.occlusionTexture.strength !== undefined ) {
-
-					materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;
-
-				}
-
-			}
-
-			if ( materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial ) {
-
-				materialParams.emissive = new THREE.Color().fromArray( materialDef.emissiveFactor );
-
-			}
-
-			if ( materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
-
-				pending.push( parser.assignTexture( materialParams, 'emissiveMap', materialDef.emissiveTexture, THREE.sRGBEncoding ) );
-
-			}
-
-			return Promise.all( pending ).then( function () {
-
-				const material = new materialType( materialParams );
-				if ( materialDef.name ) material.name = materialDef.name;
-				assignExtrasToUserData( material, materialDef );
-				parser.associations.set( material, {
-					materials: materialIndex
-				} );
-				if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
-				return material;
-
-			} );
-
-		}
-
-		/** When THREE.Object3D instances are targeted by animation, they need unique names. */
-		createUniqueName( originalName ) {
-
-			const sanitizedName = THREE.PropertyBinding.sanitizeNodeName( originalName || '' );
-			let name = sanitizedName;
-			for ( let i = 1; this.nodeNamesUsed[ name ]; ++ i ) {
-
-				name = sanitizedName + '_' + i;
-
-			}
-
-			this.nodeNamesUsed[ name ] = true;
-			return name;
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
-   *
-   * Creates BufferGeometries from primitives.
-   *
-   * @param {Array<GLTF.Primitive>} primitives
-   * @return {Promise<Array<BufferGeometry>>}
-   */
-		loadGeometries( primitives ) {
-
-			const parser = this;
-			const extensions = this.extensions;
-			const cache = this.primitiveCache;
-			function createDracoPrimitive( primitive ) {
-
-				return extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ].decodePrimitive( primitive, parser ).then( function ( geometry ) {
-
-					return addPrimitiveAttributes( geometry, primitive, parser );
-
-				} );
-
-			}
-
-			const pending = [];
-			for ( let i = 0, il = primitives.length; i < il; i ++ ) {
-
-				const primitive = primitives[ i ];
-				const cacheKey = createPrimitiveKey( primitive );
-
-				// See if we've already created this geometry
-				const cached = cache[ cacheKey ];
-				if ( cached ) {
-
-					// Use the cached geometry if it exists
-					pending.push( cached.promise );
-
-				} else {
-
-					let geometryPromise;
-					if ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) {
-
-						// Use DRACO geometry if available
-						geometryPromise = createDracoPrimitive( primitive );
-
-					} else {
-
-						// Otherwise create a new geometry
-						geometryPromise = addPrimitiveAttributes( new THREE.BufferGeometry(), primitive, parser );
-
-					}
-
-					// Cache this geometry
-					cache[ cacheKey ] = {
-						primitive: primitive,
-						promise: geometryPromise
-					};
-					pending.push( geometryPromise );
-
-				}
-
-			}
-
-			return Promise.all( pending );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
-   * @param {number} meshIndex
-   * @return {Promise<Group|Mesh|SkinnedMesh>}
-   */
-		loadMesh( meshIndex ) {
-
-			const parser = this;
-			const json = this.json;
-			const extensions = this.extensions;
-			const meshDef = json.meshes[ meshIndex ];
-			const primitives = meshDef.primitives;
-			const pending = [];
-			for ( let i = 0, il = primitives.length; i < il; i ++ ) {
-
-				const material = primitives[ i ].material === undefined ? createDefaultMaterial( this.cache ) : this.getDependency( 'material', primitives[ i ].material );
-				pending.push( material );
-
-			}
-
-			pending.push( parser.loadGeometries( primitives ) );
-			return Promise.all( pending ).then( function ( results ) {
-
-				const materials = results.slice( 0, results.length - 1 );
-				const geometries = results[ results.length - 1 ];
-				const meshes = [];
-				for ( let i = 0, il = geometries.length; i < il; i ++ ) {
-
-					const geometry = geometries[ i ];
-					const primitive = primitives[ i ];
-
-					// 1. create THREE.Mesh
-
-					let mesh;
-					const material = materials[ i ];
-					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES || primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP || primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN || primitive.mode === undefined ) {
-
-						// .isSkinnedMesh isn't in glTF spec. See ._markDefs()
-						mesh = meshDef.isSkinnedMesh === true ? new THREE.SkinnedMesh( geometry, material ) : new THREE.Mesh( geometry, material );
-						if ( mesh.isSkinnedMesh === true && ! mesh.geometry.attributes.skinWeight.normalized ) {
-
-							// we normalize floating point skin weight array to fix malformed assets (see #15319)
-							// it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
-							mesh.normalizeSkinWeights();
-
-						}
-
-						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
-
-							mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleStripDrawMode );
-
-						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
-
-							mesh.geometry = toTrianglesDrawMode( mesh.geometry, THREE.TriangleFanDrawMode );
-
-						}
-
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
-
-						mesh = new THREE.LineSegments( geometry, material );
-
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
-
-						mesh = new THREE.Line( geometry, material );
-
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
-
-						mesh = new THREE.LineLoop( geometry, material );
-
-					} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
-
-						mesh = new THREE.Points( geometry, material );
-
-					} else {
-
-						throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
-
-					}
-
-					if ( Object.keys( mesh.geometry.morphAttributes ).length > 0 ) {
-
-						updateMorphTargets( mesh, meshDef );
-
-					}
-
-					mesh.name = parser.createUniqueName( meshDef.name || 'mesh_' + meshIndex );
-					assignExtrasToUserData( mesh, meshDef );
-					if ( primitive.extensions ) addUnknownExtensionsToUserData( extensions, mesh, primitive );
-					parser.assignFinalMaterial( mesh );
-					meshes.push( mesh );
-
-				}
-
-				for ( let i = 0, il = meshes.length; i < il; i ++ ) {
-
-					parser.associations.set( meshes[ i ], {
-						meshes: meshIndex,
-						primitives: i
-					} );
-
-				}
-
-				if ( meshes.length === 1 ) {
-
-					return meshes[ 0 ];
-
-				}
-
-				const group = new THREE.Group();
-				parser.associations.set( group, {
-					meshes: meshIndex
-				} );
-				for ( let i = 0, il = meshes.length; i < il; i ++ ) {
-
-					group.add( meshes[ i ] );
-
-				}
-
-				return group;
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
-   * @param {number} cameraIndex
-   * @return {Promise<THREE.Camera>}
-   */
-		loadCamera( cameraIndex ) {
-
-			let camera;
-			const cameraDef = this.json.cameras[ cameraIndex ];
-			const params = cameraDef[ cameraDef.type ];
-			if ( ! params ) {
-
-				console.warn( 'THREE.GLTFLoader: Missing camera parameters.' );
-				return;
-
-			}
-
-			if ( cameraDef.type === 'perspective' ) {
-
-				camera = new THREE.PerspectiveCamera( THREE.MathUtils.radToDeg( params.yfov ), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6 );
-
-			} else if ( cameraDef.type === 'orthographic' ) {
-
-				camera = new THREE.OrthographicCamera( - params.xmag, params.xmag, params.ymag, - params.ymag, params.znear, params.zfar );
-
-			}
-
-			if ( cameraDef.name ) camera.name = this.createUniqueName( cameraDef.name );
-			assignExtrasToUserData( camera, cameraDef );
-			return Promise.resolve( camera );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
-   * @param {number} skinIndex
-   * @return {Promise<Skeleton>}
-   */
-		loadSkin( skinIndex ) {
-
-			const skinDef = this.json.skins[ skinIndex ];
-			const pending = [];
-			for ( let i = 0, il = skinDef.joints.length; i < il; i ++ ) {
-
-				pending.push( this.getDependency( 'node', skinDef.joints[ i ] ) );
-
-			}
-
-			if ( skinDef.inverseBindMatrices !== undefined ) {
-
-				pending.push( this.getDependency( 'accessor', skinDef.inverseBindMatrices ) );
-
-			} else {
-
-				pending.push( null );
-
-			}
-
-			return Promise.all( pending ).then( function ( results ) {
-
-				const inverseBindMatrices = results.pop();
-				const jointNodes = results;
-				const bones = [];
-				const boneInverses = [];
-				for ( let i = 0, il = jointNodes.length; i < il; i ++ ) {
-
-					const jointNode = jointNodes[ i ];
-					if ( jointNode ) {
-
-						bones.push( jointNode );
-						const mat = new THREE.Matrix4();
-						if ( inverseBindMatrices !== null ) {
-
-							mat.fromArray( inverseBindMatrices.array, i * 16 );
-
-						}
-
-						boneInverses.push( mat );
-
-					} else {
-
-						console.warn( 'THREE.GLTFLoader: Joint "%s" could not be found.', skinDef.joints[ i ] );
-
-					}
-
-				}
-
-				return new THREE.Skeleton( bones, boneInverses );
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
-   * @param {number} animationIndex
-   * @return {Promise<AnimationClip>}
-   */
-		loadAnimation( animationIndex ) {
-
-			const json = this.json;
-			const animationDef = json.animations[ animationIndex ];
-			const pendingNodes = [];
-			const pendingInputAccessors = [];
-			const pendingOutputAccessors = [];
-			const pendingSamplers = [];
-			const pendingTargets = [];
-			for ( let i = 0, il = animationDef.channels.length; i < il; i ++ ) {
-
-				const channel = animationDef.channels[ i ];
-				const sampler = animationDef.samplers[ channel.sampler ];
-				const target = channel.target;
-				const name = target.node;
-				const input = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.input ] : sampler.input;
-				const output = animationDef.parameters !== undefined ? animationDef.parameters[ sampler.output ] : sampler.output;
-				pendingNodes.push( this.getDependency( 'node', name ) );
-				pendingInputAccessors.push( this.getDependency( 'accessor', input ) );
-				pendingOutputAccessors.push( this.getDependency( 'accessor', output ) );
-				pendingSamplers.push( sampler );
-				pendingTargets.push( target );
-
-			}
-
-			return Promise.all( [ Promise.all( pendingNodes ), Promise.all( pendingInputAccessors ), Promise.all( pendingOutputAccessors ), Promise.all( pendingSamplers ), Promise.all( pendingTargets ) ] ).then( function ( dependencies ) {
-
-				const nodes = dependencies[ 0 ];
-				const inputAccessors = dependencies[ 1 ];
-				const outputAccessors = dependencies[ 2 ];
-				const samplers = dependencies[ 3 ];
-				const targets = dependencies[ 4 ];
-				const tracks = [];
-				for ( let i = 0, il = nodes.length; i < il; i ++ ) {
-
-					const node = nodes[ i ];
-					const inputAccessor = inputAccessors[ i ];
-					const outputAccessor = outputAccessors[ i ];
-					const sampler = samplers[ i ];
-					const target = targets[ i ];
-					if ( node === undefined ) continue;
-					node.updateMatrix();
-					let TypedKeyframeTrack;
-					switch ( PATH_PROPERTIES[ target.path ] ) {
-
-						case PATH_PROPERTIES.weights:
-							TypedKeyframeTrack = THREE.NumberKeyframeTrack;
-							break;
-						case PATH_PROPERTIES.rotation:
-							TypedKeyframeTrack = THREE.QuaternionKeyframeTrack;
-							break;
-						case PATH_PROPERTIES.position:
-						case PATH_PROPERTIES.scale:
-						default:
-							TypedKeyframeTrack = THREE.VectorKeyframeTrack;
-							break;
-
-					}
-
-					const targetName = node.name ? node.name : node.uuid;
-					const interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear;
-					const targetNames = [];
-					if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
-
-						node.traverse( function ( object ) {
-
-							if ( object.morphTargetInfluences ) {
-
-								targetNames.push( object.name ? object.name : object.uuid );
-
-							}
-
-						} );
-
-					} else {
-
-						targetNames.push( targetName );
-
-					}
-
-					let outputArray = outputAccessor.array;
-					if ( outputAccessor.normalized ) {
-
-						const scale = getNormalizedComponentScale( outputArray.constructor );
-						const scaled = new Float32Array( outputArray.length );
-						for ( let j = 0, jl = outputArray.length; j < jl; j ++ ) {
-
-							scaled[ j ] = outputArray[ j ] * scale;
-
-						}
-
-						outputArray = scaled;
-
-					}
-
-					for ( let j = 0, jl = targetNames.length; j < jl; j ++ ) {
-
-						const track = new TypedKeyframeTrack( targetNames[ j ] + '.' + PATH_PROPERTIES[ target.path ], inputAccessor.array, outputArray, interpolation );
-
-						// Override interpolation with custom factory method.
-						if ( sampler.interpolation === 'CUBICSPLINE' ) {
-
-							track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline( result ) {
-
-								// A CUBICSPLINE keyframe in glTF has three output values for each input value,
-								// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
-								// must be divided by three to get the interpolant's sampleSize argument.
-
-								const interpolantType = this instanceof THREE.QuaternionKeyframeTrack ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant;
-								return new interpolantType( this.times, this.values, this.getValueSize() / 3, result );
-
-							};
-
-							// Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
-							track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;
-
-						}
-
-						tracks.push( track );
-
-					}
-
-				}
-
-				const name = animationDef.name ? animationDef.name : 'animation_' + animationIndex;
-				return new THREE.AnimationClip( name, undefined, tracks );
-
-			} );
-
-		}
-		createNodeMesh( nodeIndex ) {
-
-			const json = this.json;
-			const parser = this;
-			const nodeDef = json.nodes[ nodeIndex ];
-			if ( nodeDef.mesh === undefined ) return null;
-			return parser.getDependency( 'mesh', nodeDef.mesh ).then( function ( mesh ) {
-
-				const node = parser._getNodeRef( parser.meshCache, nodeDef.mesh, mesh );
-
-				// if weights are provided on the node, override weights on the mesh.
-				if ( nodeDef.weights !== undefined ) {
-
-					node.traverse( function ( o ) {
-
-						if ( ! o.isMesh ) return;
-						for ( let i = 0, il = nodeDef.weights.length; i < il; i ++ ) {
-
-							o.morphTargetInfluences[ i ] = nodeDef.weights[ i ];
-
-						}
-
-					} );
-
-				}
-
-				return node;
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
-   * @param {number} nodeIndex
-   * @return {Promise<Object3D>}
-   */
-		loadNode( nodeIndex ) {
-
-			const json = this.json;
-			const extensions = this.extensions;
-			const parser = this;
-			const nodeDef = json.nodes[ nodeIndex ];
-
-			// reserve node's name before its dependencies, so the root has the intended name.
-			const nodeName = nodeDef.name ? parser.createUniqueName( nodeDef.name ) : '';
-			return function () {
-
-				const pending = [];
-				const meshPromise = parser._invokeOne( function ( ext ) {
-
-					return ext.createNodeMesh && ext.createNodeMesh( nodeIndex );
-
-				} );
-				if ( meshPromise ) {
-
-					pending.push( meshPromise );
-
-				}
-
-				if ( nodeDef.camera !== undefined ) {
-
-					pending.push( parser.getDependency( 'camera', nodeDef.camera ).then( function ( camera ) {
-
-						return parser._getNodeRef( parser.cameraCache, nodeDef.camera, camera );
-
-					} ) );
-
-				}
-
-				parser._invokeAll( function ( ext ) {
-
-					return ext.createNodeAttachment && ext.createNodeAttachment( nodeIndex );
-
-				} ).forEach( function ( promise ) {
-
-					pending.push( promise );
-
-				} );
-				return Promise.all( pending );
-
-			}().then( function ( objects ) {
-
-				let node;
-
-				// .isBone isn't in glTF spec. See ._markDefs
-				if ( nodeDef.isBone === true ) {
-
-					node = new THREE.Bone();
-
-				} else if ( objects.length > 1 ) {
-
-					node = new THREE.Group();
-
-				} else if ( objects.length === 1 ) {
-
-					node = objects[ 0 ];
-
-				} else {
-
-					node = new THREE.Object3D();
-
-				}
-
-				if ( node !== objects[ 0 ] ) {
-
-					for ( let i = 0, il = objects.length; i < il; i ++ ) {
-
-						node.add( objects[ i ] );
-
-					}
-
-				}
-
-				if ( nodeDef.name ) {
-
-					node.userData.name = nodeDef.name;
-					node.name = nodeName;
-
-				}
-
-				assignExtrasToUserData( node, nodeDef );
-				if ( nodeDef.extensions ) addUnknownExtensionsToUserData( extensions, node, nodeDef );
-				if ( nodeDef.matrix !== undefined ) {
-
-					const matrix = new THREE.Matrix4();
-					matrix.fromArray( nodeDef.matrix );
-					node.applyMatrix4( matrix );
-
-				} else {
-
-					if ( nodeDef.translation !== undefined ) {
-
-						node.position.fromArray( nodeDef.translation );
-
-					}
-
-					if ( nodeDef.rotation !== undefined ) {
-
-						node.quaternion.fromArray( nodeDef.rotation );
-
-					}
-
-					if ( nodeDef.scale !== undefined ) {
-
-						node.scale.fromArray( nodeDef.scale );
-
-					}
-
-				}
-
-				if ( ! parser.associations.has( node ) ) {
-
-					parser.associations.set( node, {} );
-
-				}
-
-				parser.associations.get( node ).nodes = nodeIndex;
-				return node;
-
-			} );
-
-		}
-
-		/**
-   * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
-   * @param {number} sceneIndex
-   * @return {Promise<Group>}
-   */
-		loadScene( sceneIndex ) {
-
-			const json = this.json;
-			const extensions = this.extensions;
-			const sceneDef = this.json.scenes[ sceneIndex ];
-			const parser = this;
-
-			// THREE.Loader returns THREE.Group, not Scene.
-			// See: https://github.com/mrdoob/three.js/issues/18342#issuecomment-578981172
-			const scene = new THREE.Group();
-			if ( sceneDef.name ) scene.name = parser.createUniqueName( sceneDef.name );
-			assignExtrasToUserData( scene, sceneDef );
-			if ( sceneDef.extensions ) addUnknownExtensionsToUserData( extensions, scene, sceneDef );
-			const nodeIds = sceneDef.nodes || [];
-			const pending = [];
-			for ( let i = 0, il = nodeIds.length; i < il; i ++ ) {
-
-				pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) );
-
-			}
-
-			return Promise.all( pending ).then( function () {
-
-				// Removes dangling associations, associations that reference a node that
-				// didn't make it into the scene.
-				const reduceAssociations = node => {
-
-					const reducedAssociations = new Map();
-					for ( const [ key, value ] of parser.associations ) {
-
-						if ( key instanceof THREE.Material || key instanceof THREE.Texture ) {
-
-							reducedAssociations.set( key, value );
-
-						}
-
-					}
-
-					node.traverse( node => {
-
-						const mappings = parser.associations.get( node );
-						if ( mappings != null ) {
-
-							reducedAssociations.set( node, mappings );
-
-						}
-
-					} );
-					return reducedAssociations;
-
-				};
-
-				parser.associations = reduceAssociations( scene );
-				return scene;
-
-			} );
-
-		}
-
-	}
-	function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
-
-		const nodeDef = json.nodes[ nodeId ];
-		return parser.getDependency( 'node', nodeId ).then( function ( node ) {
-
-			if ( nodeDef.skin === undefined ) return node;
-
-			// build skeleton here as well
-
-			return parser.getDependency( 'skin', nodeDef.skin ).then( function ( skeleton ) {
-
-				node.traverse( function ( mesh ) {
-
-					if ( ! mesh.isSkinnedMesh ) return;
-					mesh.bind( skeleton, mesh.matrixWorld );
-
-				} );
-				return node;
-
-			} );
-
-		} ).then( function ( node ) {
-
-			// build node hierachy
-
-			parentObject.add( node );
-			const pending = [];
-			if ( nodeDef.children ) {
-
-				const children = nodeDef.children;
-				for ( let i = 0, il = children.length; i < il; i ++ ) {
-
-					const child = children[ i ];
-					pending.push( buildNodeHierarchy( child, node, json, parser ) );
-
-				}
-
-			}
-
-			return Promise.all( pending );
-
-		} );
-
-	}
-
-	/**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- */
-	function computeBounds( geometry, primitiveDef, parser ) {
-
-		const attributes = primitiveDef.attributes;
-		const box = new THREE.Box3();
-		if ( attributes.POSITION !== undefined ) {
-
-			const accessor = parser.json.accessors[ attributes.POSITION ];
-			const min = accessor.min;
-			const max = accessor.max;
-
-			// glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
-
-			if ( min !== undefined && max !== undefined ) {
-
-				box.set( new THREE.Vector3( min[ 0 ], min[ 1 ], min[ 2 ] ), new THREE.Vector3( max[ 0 ], max[ 1 ], max[ 2 ] ) );
-				if ( accessor.normalized ) {
-
-					const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] );
-					box.min.multiplyScalar( boxScale );
-					box.max.multiplyScalar( boxScale );
-
-				}
-
-			} else {
-
-				console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
-				return;
-
-			}
-
-		} else {
-
-			return;
-
-		}
-
-		const targets = primitiveDef.targets;
-		if ( targets !== undefined ) {
-
-			const maxDisplacement = new THREE.Vector3();
-			const vector = new THREE.Vector3();
-			for ( let i = 0, il = targets.length; i < il; i ++ ) {
-
-				const target = targets[ i ];
-				if ( target.POSITION !== undefined ) {
-
-					const accessor = parser.json.accessors[ target.POSITION ];
-					const min = accessor.min;
-					const max = accessor.max;
-
-					// glTF requires 'min' and 'max', but VRM (which extends glTF) currently ignores that requirement.
-
-					if ( min !== undefined && max !== undefined ) {
-
-						// we need to get max of absolute components because target weight is [-1,1]
-						vector.setX( Math.max( Math.abs( min[ 0 ] ), Math.abs( max[ 0 ] ) ) );
-						vector.setY( Math.max( Math.abs( min[ 1 ] ), Math.abs( max[ 1 ] ) ) );
-						vector.setZ( Math.max( Math.abs( min[ 2 ] ), Math.abs( max[ 2 ] ) ) );
-						if ( accessor.normalized ) {
-
-							const boxScale = getNormalizedComponentScale( WEBGL_COMPONENT_TYPES[ accessor.componentType ] );
-							vector.multiplyScalar( boxScale );
-
-						}
-
-						// Note: this assumes that the sum of all weights is at most 1. This isn't quite correct - it's more conservative
-						// to assume that each target can have a max weight of 1. However, for some use cases - notably, when morph targets
-						// are used to implement key-frame animations and as such only two are active at a time - this results in very large
-						// boxes. So for now we make a box that's sometimes a touch too small but is hopefully mostly of reasonable size.
-						maxDisplacement.max( vector );
-
-					} else {
-
-						console.warn( 'THREE.GLTFLoader: Missing min/max properties for accessor POSITION.' );
-
-					}
-
-				}
-
-			}
-
-			// As per comment above this box isn't conservative, but has a reasonable size for a very large number of morph targets.
-			box.expandByVector( maxDisplacement );
-
-		}
-
-		geometry.boundingBox = box;
-		const sphere = new THREE.Sphere();
-		box.getCenter( sphere.center );
-		sphere.radius = box.min.distanceTo( box.max ) / 2;
-		geometry.boundingSphere = sphere;
-
-	}
-
-	/**
- * @param {BufferGeometry} geometry
- * @param {GLTF.Primitive} primitiveDef
- * @param {GLTFParser} parser
- * @return {Promise<BufferGeometry>}
- */
-	function addPrimitiveAttributes( geometry, primitiveDef, parser ) {
-
-		const attributes = primitiveDef.attributes;
-		const pending = [];
-		function assignAttributeAccessor( accessorIndex, attributeName ) {
-
-			return parser.getDependency( 'accessor', accessorIndex ).then( function ( accessor ) {
-
-				geometry.setAttribute( attributeName, accessor );
-
-			} );
-
-		}
-
-		for ( const gltfAttributeName in attributes ) {
-
-			const threeAttributeName = ATTRIBUTES[ gltfAttributeName ] || gltfAttributeName.toLowerCase();
-
-			// Skip attributes already provided by e.g. Draco extension.
-			if ( threeAttributeName in geometry.attributes ) continue;
-			pending.push( assignAttributeAccessor( attributes[ gltfAttributeName ], threeAttributeName ) );
-
-		}
-
-		if ( primitiveDef.indices !== undefined && ! geometry.index ) {
-
-			const accessor = parser.getDependency( 'accessor', primitiveDef.indices ).then( function ( accessor ) {
-
-				geometry.setIndex( accessor );
-
-			} );
-			pending.push( accessor );
-
-		}
-
-		assignExtrasToUserData( geometry, primitiveDef );
-		computeBounds( geometry, primitiveDef, parser );
-		return Promise.all( pending ).then( function () {
-
-			return primitiveDef.targets !== undefined ? addMorphTargets( geometry, primitiveDef.targets, parser ) : geometry;
-
-		} );
-
-	}
-
-	/**
- * @param {BufferGeometry} geometry
- * @param {Number} drawMode
- * @return {BufferGeometry}
- */
-	function toTrianglesDrawMode( geometry, drawMode ) {
-
-		let index = geometry.getIndex();
-
-		// generate index if not present
-
-		if ( index === null ) {
-
-			const indices = [];
-			const position = geometry.getAttribute( 'position' );
-			if ( position !== undefined ) {
-
-				for ( let i = 0; i < position.count; i ++ ) {
-
-					indices.push( i );
-
-				}
-
-				geometry.setIndex( indices );
-				index = geometry.getIndex();
-
-			} else {
-
-				console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
-				return geometry;
-
-			}
-
-		}
-
-		//
-
-		const numberOfTriangles = index.count - 2;
-		const newIndices = [];
-		if ( drawMode === THREE.TriangleFanDrawMode ) {
-
-			// gl.TRIANGLE_FAN
-
-			for ( let i = 1; i <= numberOfTriangles; i ++ ) {
-
-				newIndices.push( index.getX( 0 ) );
-				newIndices.push( index.getX( i ) );
-				newIndices.push( index.getX( i + 1 ) );
-
-			}
-
-		} else {
-
-			// gl.TRIANGLE_STRIP
-
-			for ( let i = 0; i < numberOfTriangles; i ++ ) {
-
-				if ( i % 2 === 0 ) {
-
-					newIndices.push( index.getX( i ) );
-					newIndices.push( index.getX( i + 1 ) );
-					newIndices.push( index.getX( i + 2 ) );
-
-				} else {
-
-					newIndices.push( index.getX( i + 2 ) );
-					newIndices.push( index.getX( i + 1 ) );
-					newIndices.push( index.getX( i ) );
-
-				}
-
-			}
-
-		}
-
-		if ( newIndices.length / 3 !== numberOfTriangles ) {
-
-			console.error( 'THREE.GLTFLoader.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
-
-		}
-
-		// build final geometry
-
-		const newGeometry = geometry.clone();
-		newGeometry.setIndex( newIndices );
-		return newGeometry;
-
-	}
-
-	THREE.GLTFLoader = GLTFLoader;
-
-} )();

+ 0 - 87
examples/js/loaders/HDRCubeTextureLoader.js

@@ -1,87 +0,0 @@
-( function () {
-
-	class HDRCubeTextureLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-			this.hdrLoader = new THREE.RGBELoader();
-			this.type = THREE.HalfFloatType;
-
-		}
-		load( urls, onLoad, onProgress, onError ) {
-
-			const texture = new THREE.CubeTexture();
-			texture.type = this.type;
-			switch ( texture.type ) {
-
-				case THREE.FloatType:
-					texture.encoding = THREE.LinearEncoding;
-					texture.minFilter = THREE.LinearFilter;
-					texture.magFilter = THREE.LinearFilter;
-					texture.generateMipmaps = false;
-					break;
-				case THREE.HalfFloatType:
-					texture.encoding = THREE.LinearEncoding;
-					texture.minFilter = THREE.LinearFilter;
-					texture.magFilter = THREE.LinearFilter;
-					texture.generateMipmaps = false;
-					break;
-
-			}
-
-			const scope = this;
-			let loaded = 0;
-			function loadHDRData( i, onLoad, onProgress, onError ) {
-
-				new THREE.FileLoader( scope.manager ).setPath( scope.path ).setResponseType( 'arraybuffer' ).setWithCredentials( scope.withCredentials ).load( urls[ i ], function ( buffer ) {
-
-					loaded ++;
-					const texData = scope.hdrLoader.parse( buffer );
-					if ( ! texData ) return;
-					if ( texData.data !== undefined ) {
-
-						const dataTexture = new THREE.DataTexture( texData.data, texData.width, texData.height );
-						dataTexture.type = texture.type;
-						dataTexture.encoding = texture.encoding;
-						dataTexture.format = texture.format;
-						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 );
-
-			}
-
-			for ( let i = 0; i < urls.length; i ++ ) {
-
-				loadHDRData( i, onLoad, onProgress, onError );
-
-			}
-
-			return texture;
-
-		}
-		setDataType( value ) {
-
-			this.type = value;
-			this.hdrLoader.setDataType( value );
-			return this;
-
-		}
-
-	}
-
-	THREE.HDRCubeTextureLoader = HDRCubeTextureLoader;
-
-} )();

+ 0 - 121
examples/js/loaders/KMZLoader.js

@@ -1,121 +0,0 @@
-( function () {
-
-	class KMZLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const scope = this;
-			const loader = new THREE.FileLoader( scope.manager );
-			loader.setPath( scope.path );
-			loader.setResponseType( 'arraybuffer' );
-			loader.setRequestHeader( scope.requestHeader );
-			loader.setWithCredentials( scope.withCredentials );
-			loader.load( url, function ( text ) {
-
-				try {
-
-					onLoad( scope.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					scope.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( data ) {
-
-			function findFile( url ) {
-
-				for ( const path in zip ) {
-
-					if ( path.slice( - url.length ) === url ) {
-
-						return zip[ path ];
-
-					}
-
-				}
-
-			}
-
-			const manager = new THREE.LoadingManager();
-			manager.setURLModifier( function ( url ) {
-
-				const image = findFile( url );
-				if ( image ) {
-
-					console.log( 'Loading', url );
-					const blob = new Blob( [ image.buffer ], {
-						type: 'application/octet-stream'
-					} );
-					return URL.createObjectURL( blob );
-
-				}
-
-				return url;
-
-			} );
-
-			//
-
-			const zip = fflate.unzipSync( new Uint8Array( data ) ); // eslint-disable-line no-undef
-
-			if ( zip[ 'doc.kml' ] ) {
-
-				const xml = new DOMParser().parseFromString( fflate.strFromU8( zip[ 'doc.kml' ] ), 'application/xml' ); // eslint-disable-line no-undef
-
-				const model = xml.querySelector( 'Placemark Model Link href' );
-				if ( model ) {
-
-					const loader = new THREE.ColladaLoader( manager );
-					return loader.parse( fflate.strFromU8( zip[ model.textContent ] ) ); // eslint-disable-line no-undef
-
-				}
-
-			} else {
-
-				console.warn( 'KMZLoader: Missing doc.kml file.' );
-				for ( const path in zip ) {
-
-					const extension = path.split( '.' ).pop().toLowerCase();
-					if ( extension === 'dae' ) {
-
-						const loader = new THREE.ColladaLoader( manager );
-						return loader.parse( fflate.strFromU8( zip[ path ] ) ); // eslint-disable-line no-undef
-
-					}
-
-				}
-
-			}
-
-			console.error( 'KMZLoader: Couldn\'t find .dae file.' );
-			return {
-				scene: new THREE.Group()
-			};
-
-		}
-
-	}
-
-	THREE.KMZLoader = KMZLoader;
-
-} )();

+ 0 - 159
examples/js/loaders/KTXLoader.js

@@ -1,159 +0,0 @@
-( function () {
-
-	/**
- * for description see https://www.khronos.org/opengles/sdk/tools/KTX/
- * for file layout see https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
- *
- * ported from https://github.com/BabylonJS/Babylon.js/blob/master/src/Misc/khronosTextureContainer.ts
- */
-
-	class KTXLoader extends THREE.CompressedTextureLoader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-		}
-		parse( buffer, loadMipmaps ) {
-
-			const ktx = new KhronosTextureContainer( buffer, 1 );
-			return {
-				mipmaps: ktx.mipmaps( loadMipmaps ),
-				width: ktx.pixelWidth,
-				height: ktx.pixelHeight,
-				format: ktx.glInternalFormat,
-				isCubemap: ktx.numberOfFaces === 6,
-				mipmapCount: ktx.numberOfMipmapLevels
-			};
-
-		}
-
-	}
-	const HEADER_LEN = 12 + 13 * 4; // identifier + header elements (not including key value meta-data pairs)
-	// load types
-	const COMPRESSED_2D = 0; // uses a gl.compressedTexImage2D()
-	//const COMPRESSED_3D = 1; // uses a gl.compressedTexImage3D()
-	//const TEX_2D = 2; // uses a gl.texImage2D()
-	//const TEX_3D = 3; // uses a gl.texImage3D()
-
-	class KhronosTextureContainer {
-
-		/**
-   * @param {ArrayBuffer} arrayBuffer- contents of the KTX container file
-   * @param {number} facesExpected- should be either 1 or 6, based whether a cube texture or or
-   * @param {boolean} threeDExpected- provision for indicating that data should be a 3D texture, not implemented
-   * @param {boolean} textureArrayExpected- provision for indicating that data should be a texture array, not implemented
-   */
-		constructor( arrayBuffer, facesExpected /*, threeDExpected, textureArrayExpected */ ) {
-
-			this.arrayBuffer = arrayBuffer;
-
-			// Test that it is a ktx formatted file, based on the first 12 bytes, character representation is:
-			// '´', 'K', 'T', 'X', ' ', '1', '1', 'ª', '\r', '\n', '\x1A', '\n'
-			// 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
-			const identifier = new Uint8Array( this.arrayBuffer, 0, 12 );
-			if ( identifier[ 0 ] !== 0xAB || identifier[ 1 ] !== 0x4B || identifier[ 2 ] !== 0x54 || identifier[ 3 ] !== 0x58 || identifier[ 4 ] !== 0x20 || identifier[ 5 ] !== 0x31 || identifier[ 6 ] !== 0x31 || identifier[ 7 ] !== 0xBB || identifier[ 8 ] !== 0x0D || identifier[ 9 ] !== 0x0A || identifier[ 10 ] !== 0x1A || identifier[ 11 ] !== 0x0A ) {
-
-				console.error( 'texture missing KTX identifier' );
-				return;
-
-			}
-
-			// load the reset of the header in native 32 bit uint
-			const dataSize = Uint32Array.BYTES_PER_ELEMENT;
-			const headerDataView = new DataView( this.arrayBuffer, 12, 13 * dataSize );
-			const endianness = headerDataView.getUint32( 0, true );
-			const littleEndian = endianness === 0x04030201;
-			this.glType = headerDataView.getUint32( 1 * dataSize, littleEndian ); // must be 0 for compressed textures
-			this.glTypeSize = headerDataView.getUint32( 2 * dataSize, littleEndian ); // must be 1 for compressed textures
-			this.glFormat = headerDataView.getUint32( 3 * dataSize, littleEndian ); // must be 0 for compressed textures
-			this.glInternalFormat = headerDataView.getUint32( 4 * dataSize, littleEndian ); // the value of arg passed to gl.compressedTexImage2D(,,x,,,,)
-			this.glBaseInternalFormat = headerDataView.getUint32( 5 * dataSize, littleEndian ); // specify GL_RGB, GL_RGBA, GL_ALPHA, etc (un-compressed only)
-			this.pixelWidth = headerDataView.getUint32( 6 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage2D(,,,x,,,)
-			this.pixelHeight = headerDataView.getUint32( 7 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage2D(,,,,x,,)
-			this.pixelDepth = headerDataView.getUint32( 8 * dataSize, littleEndian ); // level 0 value of arg passed to gl.compressedTexImage3D(,,,,,x,,)
-			this.numberOfArrayElements = headerDataView.getUint32( 9 * dataSize, littleEndian ); // used for texture arrays
-			this.numberOfFaces = headerDataView.getUint32( 10 * dataSize, littleEndian ); // used for cubemap textures, should either be 1 or 6
-			this.numberOfMipmapLevels = headerDataView.getUint32( 11 * dataSize, littleEndian ); // number of levels; disregard possibility of 0 for compressed textures
-			this.bytesOfKeyValueData = headerDataView.getUint32( 12 * dataSize, littleEndian ); // the amount of space after the header for meta-data
-
-			// Make sure we have a compressed type.  Not only reduces work, but probably better to let dev know they are not compressing.
-			if ( this.glType !== 0 ) {
-
-				console.warn( 'only compressed formats currently supported' );
-				return;
-
-			} else {
-
-				// value of zero is an indication to generate mipmaps @ runtime.  Not usually allowed for compressed, so disregard.
-				this.numberOfMipmapLevels = Math.max( 1, this.numberOfMipmapLevels );
-
-			}
-
-			if ( this.pixelHeight === 0 || this.pixelDepth !== 0 ) {
-
-				console.warn( 'only 2D textures currently supported' );
-				return;
-
-			}
-
-			if ( this.numberOfArrayElements !== 0 ) {
-
-				console.warn( 'texture arrays not currently supported' );
-				return;
-
-			}
-
-			if ( this.numberOfFaces !== facesExpected ) {
-
-				console.warn( 'number of faces expected' + facesExpected + ', but found ' + this.numberOfFaces );
-				return;
-
-			}
-
-			// we now have a completely validated file, so could use existence of loadType as success
-			// would need to make this more elaborate & adjust checks above to support more than one load type
-			this.loadType = COMPRESSED_2D;
-
-		}
-		mipmaps( loadMipmaps ) {
-
-			const mipmaps = [];
-
-			// initialize width & height for level 1
-			let dataOffset = HEADER_LEN + this.bytesOfKeyValueData;
-			let width = this.pixelWidth;
-			let height = this.pixelHeight;
-			const mipmapCount = loadMipmaps ? this.numberOfMipmapLevels : 1;
-			for ( let level = 0; level < mipmapCount; level ++ ) {
-
-				const imageSize = new Int32Array( this.arrayBuffer, dataOffset, 1 )[ 0 ]; // size per face, since not supporting array cubemaps
-				dataOffset += 4; // size of the image + 4 for the imageSize field
-
-				for ( let face = 0; face < this.numberOfFaces; face ++ ) {
-
-					const byteArray = new Uint8Array( this.arrayBuffer, dataOffset, imageSize );
-					mipmaps.push( {
-						'data': byteArray,
-						'width': width,
-						'height': height
-					} );
-					dataOffset += imageSize;
-					dataOffset += 3 - ( imageSize + 3 ) % 4; // add padding for odd sized image
-
-				}
-
-				width = Math.max( 1.0, width * 0.5 );
-				height = Math.max( 1.0, height * 0.5 );
-
-			}
-
-			return mipmaps;
-
-		}
-
-	}
-
-	THREE.KTXLoader = KTXLoader;
-
-} )();

+ 0 - 2263
examples/js/loaders/LDrawLoader.js

@@ -1,2263 +0,0 @@
-( function () {
-
-	// Special surface finish tag types.
-	// Note: "MATERIAL" tag (e.g. GLITTER, SPECKLE) is not implemented
-	const FINISH_TYPE_DEFAULT = 0;
-	const FINISH_TYPE_CHROME = 1;
-	const FINISH_TYPE_PEARLESCENT = 2;
-	const FINISH_TYPE_RUBBER = 3;
-	const FINISH_TYPE_MATTE_METALLIC = 4;
-	const FINISH_TYPE_METAL = 5;
-
-	// State machine to search a subobject path.
-	// The LDraw standard establishes these various possible subfolders.
-	const FILE_LOCATION_TRY_PARTS = 0;
-	const FILE_LOCATION_TRY_P = 1;
-	const FILE_LOCATION_TRY_MODELS = 2;
-	const FILE_LOCATION_AS_IS = 3;
-	const FILE_LOCATION_TRY_RELATIVE = 4;
-	const FILE_LOCATION_TRY_ABSOLUTE = 5;
-	const FILE_LOCATION_NOT_FOUND = 6;
-	const MAIN_COLOUR_CODE = '16';
-	const MAIN_EDGE_COLOUR_CODE = '24';
-	const _tempVec0 = new THREE.Vector3();
-	const _tempVec1 = new THREE.Vector3();
-	class LDrawConditionalLineMaterial extends THREE.ShaderMaterial {
-
-		constructor( parameters ) {
-
-			super( {
-				uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.fog, {
-					diffuse: {
-						value: new THREE.Color()
-					},
-					opacity: {
-						value: 1.0
-					}
-				} ] ),
-				vertexShader: /* 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>
-				}
-			`,
-				fragmentShader: /* 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 <tonemapping_fragment>
-				#include <encodings_fragment>
-				#include <fog_fragment>
-				#include <premultiplied_alpha_fragment>
-			}
-			`
-			} );
-			Object.defineProperties( this, {
-				opacity: {
-					get: function () {
-
-						return this.uniforms.opacity.value;
-
-					},
-					set: function ( value ) {
-
-						this.uniforms.opacity.value = value;
-
-					}
-				},
-				color: {
-					get: function () {
-
-						return this.uniforms.diffuse.value;
-
-					}
-				}
-			} );
-			this.setValues( parameters );
-			this.isLDrawConditionalLineMaterial = true;
-
-		}
-
-	}
-	class ConditionalLineSegments extends THREE.LineSegments {
-
-		constructor( geometry, material ) {
-
-			super( geometry, material );
-			this.isConditionalLine = true;
-
-		}
-
-	}
-	function generateFaceNormals( faces ) {
-
-		for ( let i = 0, l = faces.length; i < l; i ++ ) {
-
-			const face = faces[ i ];
-			const vertices = face.vertices;
-			const v0 = vertices[ 0 ];
-			const v1 = vertices[ 1 ];
-			const v2 = vertices[ 2 ];
-			_tempVec0.subVectors( v1, v0 );
-			_tempVec1.subVectors( v2, v1 );
-			face.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
-
-		}
-
-	}
-
-	const _ray = new THREE.Ray();
-	function smoothNormals( faces, lineSegments, checkSubSegments = false ) {
-
-		// NOTE: 1e2 is pretty coarse but was chosen to quantize the resulting value because
-		// it allows edges to be smoothed as expected (see minifig arms).
-		// --
-		// And the vector values are initialize multiplied by 1 + 1e-10 to account for floating
-		// point errors on vertices along quantization boundaries. Ie after matrix multiplication
-		// vertices that should be merged might be set to "1.7" and "1.6999..." meaning they won't
-		// get merged. This added epsilon attempts to push these error values to the same quantized
-		// value for the sake of hashing. See "AT-ST mini" dishes. See mrdoob/three#23169.
-
-		const hashMultiplier = ( 1 + 1e-10 ) * 1e2;
-		function hashVertex( v ) {
-
-			const x = ~ ~ ( v.x * hashMultiplier );
-			const y = ~ ~ ( v.y * hashMultiplier );
-			const z = ~ ~ ( v.z * hashMultiplier );
-			return `${x},${y},${z}`;
-
-		}
-
-		function hashEdge( v0, v1 ) {
-
-			return `${hashVertex( v0 )}_${hashVertex( v1 )}`;
-
-		}
-
-		// converts the two vertices to a ray with a normalized direction and origin of 0, 0, 0 projected
-		// onto the original line.
-		function toNormalizedRay( v0, v1, targetRay ) {
-
-			targetRay.direction.subVectors( v1, v0 ).normalize();
-			const scalar = v0.dot( targetRay.direction );
-			targetRay.origin.copy( v0 ).addScaledVector( targetRay.direction, - scalar );
-			return targetRay;
-
-		}
-
-		function hashRay( ray ) {
-
-			return hashEdge( ray.origin, ray.direction );
-
-		}
-
-		const hardEdges = new Set();
-		const hardEdgeRays = new Map();
-		const halfEdgeList = {};
-		const normals = [];
-
-		// Save the list of hard edges by hash
-		for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
-
-			const ls = lineSegments[ i ];
-			const vertices = ls.vertices;
-			const v0 = vertices[ 0 ];
-			const v1 = vertices[ 1 ];
-			hardEdges.add( hashEdge( v0, v1 ) );
-			hardEdges.add( hashEdge( v1, v0 ) );
-
-			// only generate the hard edge ray map if we're checking subsegments because it's more expensive to check
-			// and requires more memory.
-			if ( checkSubSegments ) {
-
-				// add both ray directions to the map
-				const ray = toNormalizedRay( v0, v1, new THREE.Ray() );
-				const rh1 = hashRay( ray );
-				if ( ! hardEdgeRays.has( rh1 ) ) {
-
-					toNormalizedRay( v1, v0, ray );
-					const rh2 = hashRay( ray );
-					const info = {
-						ray,
-						distances: []
-					};
-					hardEdgeRays.set( rh1, info );
-					hardEdgeRays.set( rh2, info );
-
-				}
-
-				// store both segments ends in min, max order in the distances array to check if a face edge is a
-				// subsegment later.
-				const info = hardEdgeRays.get( rh1 );
-				let d0 = info.ray.direction.dot( v0 );
-				let d1 = info.ray.direction.dot( v1 );
-				if ( d0 > d1 ) {
-
-					[ d0, d1 ] = [ d1, d0 ];
-
-				}
-
-				info.distances.push( d0, d1 );
-
-			}
-
-		}
-
-		// track the half edges associated with each triangle
-		for ( let i = 0, l = faces.length; i < l; i ++ ) {
-
-			const tri = faces[ i ];
-			const vertices = tri.vertices;
-			const vertCount = vertices.length;
-			for ( let i2 = 0; i2 < vertCount; i2 ++ ) {
-
-				const index = i2;
-				const next = ( i2 + 1 ) % vertCount;
-				const v0 = vertices[ index ];
-				const v1 = vertices[ next ];
-				const hash = hashEdge( v0, v1 );
-
-				// don't add the triangle if the edge is supposed to be hard
-				if ( hardEdges.has( hash ) ) {
-
-					continue;
-
-				}
-
-				// if checking subsegments then check to see if this edge lies on a hard edge ray and whether its within any ray bounds
-				if ( checkSubSegments ) {
-
-					toNormalizedRay( v0, v1, _ray );
-					const rayHash = hashRay( _ray );
-					if ( hardEdgeRays.has( rayHash ) ) {
-
-						const info = hardEdgeRays.get( rayHash );
-						const {
-							ray,
-							distances
-						} = info;
-						let d0 = ray.direction.dot( v0 );
-						let d1 = ray.direction.dot( v1 );
-						if ( d0 > d1 ) {
-
-							[ d0, d1 ] = [ d1, d0 ];
-
-						}
-
-						// return early if the face edge is found to be a subsegment of a line edge meaning the edge will have "hard" normals
-						let found = false;
-						for ( let i = 0, l = distances.length; i < l; i += 2 ) {
-
-							if ( d0 >= distances[ i ] && d1 <= distances[ i + 1 ] ) {
-
-								found = true;
-								break;
-
-							}
-
-						}
-
-						if ( found ) {
-
-							continue;
-
-						}
-
-					}
-
-				}
-
-				const info = {
-					index: index,
-					tri: tri
-				};
-				halfEdgeList[ hash ] = info;
-
-			}
-
-		}
-
-		// Iterate until we've tried to connect all faces to share normals
-		while ( true ) {
-
-			// Stop if there are no more faces left
-			let halfEdge = null;
-			for ( const key in halfEdgeList ) {
-
-				halfEdge = halfEdgeList[ key ];
-				break;
-
-			}
-
-			if ( halfEdge === null ) {
-
-				break;
-
-			}
-
-			// Exhaustively find all connected faces
-			const queue = [ halfEdge ];
-			while ( queue.length > 0 ) {
-
-				// initialize all vertex normals in this triangle
-				const tri = queue.pop().tri;
-				const vertices = tri.vertices;
-				const vertNormals = tri.normals;
-				const faceNormal = tri.faceNormal;
-
-				// Check if any edge is connected to another triangle edge
-				const vertCount = vertices.length;
-				for ( let i2 = 0; i2 < vertCount; i2 ++ ) {
-
-					const index = i2;
-					const next = ( i2 + 1 ) % vertCount;
-					const v0 = vertices[ index ];
-					const v1 = vertices[ next ];
-
-					// delete this triangle from the list so it won't be found again
-					const hash = hashEdge( v0, v1 );
-					delete halfEdgeList[ hash ];
-					const reverseHash = hashEdge( v1, v0 );
-					const otherInfo = halfEdgeList[ reverseHash ];
-					if ( otherInfo ) {
-
-						const otherTri = otherInfo.tri;
-						const otherIndex = otherInfo.index;
-						const otherNormals = otherTri.normals;
-						const otherVertCount = otherNormals.length;
-						const otherFaceNormal = otherTri.faceNormal;
-
-						// NOTE: If the angle between faces 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( otherInfo );
-							delete halfEdgeList[ reverseHash ];
-
-						}
-
-						// share the first normal
-						const otherNext = ( otherIndex + 1 ) % otherVertCount;
-						if ( vertNormals[ index ] && otherNormals[ otherNext ] && vertNormals[ index ] !== otherNormals[ otherNext ] ) {
-
-							otherNormals[ otherNext ].norm.add( vertNormals[ index ].norm );
-							vertNormals[ index ].norm = otherNormals[ otherNext ].norm;
-
-						}
-
-						let sharedNormal1 = vertNormals[ index ] || otherNormals[ otherNext ];
-						if ( sharedNormal1 === null ) {
-
-							// it's possible to encounter an edge of a triangle that has already been traversed meaning
-							// both edges already have different normals defined and shared. To work around this we create
-							// a wrapper object so when those edges are merged the normals can be updated everywhere.
-							sharedNormal1 = {
-								norm: new THREE.Vector3()
-							};
-							normals.push( sharedNormal1.norm );
-
-						}
-
-						if ( vertNormals[ index ] === null ) {
-
-							vertNormals[ index ] = sharedNormal1;
-							sharedNormal1.norm.add( faceNormal );
-
-						}
-
-						if ( otherNormals[ otherNext ] === null ) {
-
-							otherNormals[ otherNext ] = sharedNormal1;
-							sharedNormal1.norm.add( otherFaceNormal );
-
-						}
-
-						// share the second normal
-						if ( vertNormals[ next ] && otherNormals[ otherIndex ] && vertNormals[ next ] !== otherNormals[ otherIndex ] ) {
-
-							otherNormals[ otherIndex ].norm.add( vertNormals[ next ].norm );
-							vertNormals[ next ].norm = otherNormals[ otherIndex ].norm;
-
-						}
-
-						let sharedNormal2 = vertNormals[ next ] || otherNormals[ otherIndex ];
-						if ( sharedNormal2 === null ) {
-
-							sharedNormal2 = {
-								norm: new THREE.Vector3()
-							};
-							normals.push( sharedNormal2.norm );
-
-						}
-
-						if ( vertNormals[ next ] === null ) {
-
-							vertNormals[ next ] = sharedNormal2;
-							sharedNormal2.norm.add( faceNormal );
-
-						}
-
-						if ( otherNormals[ otherIndex ] === null ) {
-
-							otherNormals[ otherIndex ] = sharedNormal2;
-							sharedNormal2.norm.add( otherFaceNormal );
-
-						}
-
-					}
-
-				}
-
-			}
-
-		}
-
-		// The normals of each face have been added up so now we average them by normalizing the vector.
-		for ( let i = 0, l = normals.length; i < l; i ++ ) {
-
-			normals[ i ].normalize();
-
-		}
-
-	}
-
-	function isPartType( type ) {
-
-		return type === 'Part' || type === 'Unofficial_Part';
-
-	}
-
-	function isPrimitiveType( type ) {
-
-		return /primitive/i.test( type ) || type === 'Subpart';
-
-	}
-
-	class LineParser {
-
-		constructor( line, lineNumber ) {
-
-			this.line = line;
-			this.lineLength = line.length;
-			this.currentCharIndex = 0;
-			this.currentChar = ' ';
-			this.lineNumber = lineNumber;
-
-		}
-		seekNonSpace() {
-
-			while ( this.currentCharIndex < this.lineLength ) {
-
-				this.currentChar = this.line.charAt( this.currentCharIndex );
-				if ( this.currentChar !== ' ' && this.currentChar !== '\t' ) {
-
-					return;
-
-				}
-
-				this.currentCharIndex ++;
-
-			}
-
-		}
-		getToken() {
-
-			const 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 ++;
-
-			}
-
-			const pos1 = this.currentCharIndex;
-			this.seekNonSpace();
-			return this.line.substring( pos0, pos1 );
-
-		}
-		getVector() {
-
-			return new THREE.Vector3( parseFloat( this.getToken() ), parseFloat( this.getToken() ), parseFloat( this.getToken() ) );
-
-		}
-		getRemainingString() {
-
-			return this.line.substring( this.currentCharIndex, this.lineLength );
-
-		}
-		isAtTheEnd() {
-
-			return this.currentCharIndex >= this.lineLength;
-
-		}
-		setToEnd() {
-
-			this.currentCharIndex = this.lineLength;
-
-		}
-		getLineNumberString() {
-
-			return this.lineNumber >= 0 ? ' at line ' + this.lineNumber : '';
-
-		}
-
-	}
-
-	// Fetches and parses an intermediate representation of LDraw parts files.
-	class LDrawParsedCache {
-
-		constructor( loader ) {
-
-			this.loader = loader;
-			this._cache = {};
-
-		}
-		cloneResult( original ) {
-
-			const result = {};
-
-			// vertices are transformed and normals computed before being converted to geometry
-			// so these pieces must be cloned.
-			result.faces = original.faces.map( face => {
-
-				return {
-					colorCode: face.colorCode,
-					material: face.material,
-					vertices: face.vertices.map( v => v.clone() ),
-					normals: face.normals.map( () => null ),
-					faceNormal: null
-				};
-
-			} );
-			result.conditionalSegments = original.conditionalSegments.map( face => {
-
-				return {
-					colorCode: face.colorCode,
-					material: face.material,
-					vertices: face.vertices.map( v => v.clone() ),
-					controlPoints: face.controlPoints.map( v => v.clone() )
-				};
-
-			} );
-			result.lineSegments = original.lineSegments.map( face => {
-
-				return {
-					colorCode: face.colorCode,
-					material: face.material,
-					vertices: face.vertices.map( v => v.clone() )
-				};
-
-			} );
-
-			// none if this is subsequently modified
-			result.type = original.type;
-			result.category = original.category;
-			result.keywords = original.keywords;
-			result.author = original.author;
-			result.subobjects = original.subobjects;
-			result.fileName = original.fileName;
-			result.totalFaces = original.totalFaces;
-			result.startingBuildingStep = original.startingBuildingStep;
-			result.materials = original.materials;
-			result.group = null;
-			return result;
-
-		}
-		async fetchData( fileName ) {
-
-			let triedLowerCase = false;
-			let locationState = FILE_LOCATION_TRY_PARTS;
-			while ( locationState !== FILE_LOCATION_NOT_FOUND ) {
-
-				let subobjectURL = fileName;
-				switch ( locationState ) {
-
-					case FILE_LOCATION_AS_IS:
-						locationState = locationState + 1;
-						break;
-					case FILE_LOCATION_TRY_PARTS:
-						subobjectURL = 'parts/' + subobjectURL;
-						locationState = locationState + 1;
-						break;
-					case FILE_LOCATION_TRY_P:
-						subobjectURL = 'p/' + subobjectURL;
-						locationState = locationState + 1;
-						break;
-					case FILE_LOCATION_TRY_MODELS:
-						subobjectURL = 'models/' + subobjectURL;
-						locationState = locationState + 1;
-						break;
-					case FILE_LOCATION_TRY_RELATIVE:
-						subobjectURL = fileName.substring( 0, fileName.lastIndexOf( '/' ) + 1 ) + subobjectURL;
-						locationState = locationState + 1;
-						break;
-					case FILE_LOCATION_TRY_ABSOLUTE:
-						if ( triedLowerCase ) {
-
-							// Try absolute path
-							locationState = FILE_LOCATION_NOT_FOUND;
-
-						} else {
-
-							// Next attempt is lower case
-							fileName = fileName.toLowerCase();
-							subobjectURL = fileName;
-							triedLowerCase = true;
-							locationState = FILE_LOCATION_TRY_PARTS;
-
-						}
-
-						break;
-
-				}
-
-				const loader = this.loader;
-				const fileLoader = new THREE.FileLoader( loader.manager );
-				fileLoader.setPath( loader.partsLibraryPath );
-				fileLoader.setRequestHeader( loader.requestHeader );
-				fileLoader.setWithCredentials( loader.withCredentials );
-				try {
-
-					const text = await fileLoader.loadAsync( subobjectURL );
-					return text;
-
-				} catch {
-
-					continue;
-
-				}
-
-			}
-
-			throw new Error( 'LDrawLoader: Subobject "' + fileName + '" could not be loaded.' );
-
-		}
-		parse( text, fileName = null ) {
-
-			const loader = this.loader;
-
-			// final results
-			const faces = [];
-			const lineSegments = [];
-			const conditionalSegments = [];
-			const subobjects = [];
-			const materials = {};
-			const getLocalMaterial = colorCode => {
-
-				return materials[ colorCode ] || null;
-
-			};
-
-			let type = 'Model';
-			let category = null;
-			let keywords = null;
-			let author = null;
-			let totalFaces = 0;
-
-			// split into lines
-			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' );
-
-			}
-
-			const lines = text.split( '\n' );
-			const numLines = lines.length;
-			let parsingEmbeddedFiles = false;
-			let currentEmbeddedFileName = null;
-			let currentEmbeddedText = null;
-			let bfcCertified = false;
-			let bfcCCW = true;
-			let bfcInverted = false;
-			let bfcCull = true;
-			let startingBuildingStep = false;
-
-			// Parse all line commands
-			for ( let lineIndex = 0; lineIndex < numLines; lineIndex ++ ) {
-
-				const line = lines[ lineIndex ];
-				if ( line.length === 0 ) continue;
-				if ( parsingEmbeddedFiles ) {
-
-					if ( line.startsWith( '0 FILE ' ) ) {
-
-						// Save previous embedded file in the cache
-						this.setData( currentEmbeddedFileName, currentEmbeddedText );
-
-						// New embedded text file
-						currentEmbeddedFileName = line.substring( 7 );
-						currentEmbeddedText = '';
-
-					} else {
-
-						currentEmbeddedText += line + '\n';
-
-					}
-
-					continue;
-
-				}
-
-				const lp = new LineParser( line, lineIndex + 1 );
-				lp.seekNonSpace();
-				if ( lp.isAtTheEnd() ) {
-
-					// Empty line
-					continue;
-
-				}
-
-				// Parse the line type
-				const lineType = lp.getToken();
-				let material;
-				let colorCode;
-				let segment;
-				let ccw;
-				let doubleSided;
-				let v0, v1, v2, v3, c0, c1;
-				switch ( lineType ) {
-
-					// Line type 0: Comment or META
-					case '0':
-						// Parse meta directive
-						const meta = lp.getToken();
-						if ( meta ) {
-
-							switch ( meta ) {
-
-								case '!LDRAW_ORG':
-									type = lp.getToken();
-									break;
-								case '!COLOUR':
-									material = loader.parseColorMetaDirective( lp );
-									if ( material ) {
-
-										materials[ material.userData.code ] = material;
-
-									} else {
-
-										console.warn( 'LDrawLoader: Error parsing material' + lp.getLineNumberString() );
-
-									}
-
-									break;
-								case '!CATEGORY':
-									category = lp.getToken();
-									break;
-								case '!KEYWORDS':
-									const 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() ) {
-
-										const 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;
-								case 'STEP':
-									startingBuildingStep = true;
-									break;
-								case 'Author:':
-									author = lp.getToken();
-									break;
-								default:
-									// Other meta directives are not implemented
-									break;
-
-							}
-
-						}
-
-						break;
-
-						// Line type 1: Sub-object file
-					case '1':
-						colorCode = lp.getToken();
-						material = getLocalMaterial( colorCode );
-						const posX = parseFloat( lp.getToken() );
-						const posY = parseFloat( lp.getToken() );
-						const posZ = parseFloat( lp.getToken() );
-						const m0 = parseFloat( lp.getToken() );
-						const m1 = parseFloat( lp.getToken() );
-						const m2 = parseFloat( lp.getToken() );
-						const m3 = parseFloat( lp.getToken() );
-						const m4 = parseFloat( lp.getToken() );
-						const m5 = parseFloat( lp.getToken() );
-						const m6 = parseFloat( lp.getToken() );
-						const m7 = parseFloat( lp.getToken() );
-						const m8 = parseFloat( lp.getToken() );
-						const matrix = new THREE.Matrix4().set( m0, m1, m2, posX, m3, m4, m5, posY, m6, m7, m8, posZ, 0, 0, 0, 1 );
-						let fileName = lp.getRemainingString().trim().replace( /\\/g, '/' );
-						if ( loader.fileMap[ fileName ] ) {
-
-							// Found the subobject path in the preloaded file path map
-							fileName = loader.fileMap[ fileName ];
-
-						} else {
-
-							// Standardized subfolders
-							if ( fileName.startsWith( 's/' ) ) {
-
-								fileName = 'parts/' + fileName;
-
-							} else if ( fileName.startsWith( '48/' ) ) {
-
-								fileName = 'p/' + fileName;
-
-							}
-
-						}
-
-						subobjects.push( {
-							material: material,
-							colorCode: colorCode,
-							matrix: matrix,
-							fileName: fileName,
-							inverted: bfcInverted,
-							startingBuildingStep: startingBuildingStep
-						} );
-						startingBuildingStep = false;
-						bfcInverted = false;
-						break;
-
-						// Line type 2: Line segment
-					case '2':
-						colorCode = lp.getToken();
-						material = getLocalMaterial( colorCode );
-						v0 = lp.getVector();
-						v1 = lp.getVector();
-						segment = {
-							material: material,
-							colorCode: colorCode,
-							vertices: [ v0, v1 ]
-						};
-						lineSegments.push( segment );
-						break;
-
-						// Line type 5: Conditional Line segment
-					case '5':
-						colorCode = lp.getToken();
-						material = getLocalMaterial( colorCode );
-						v0 = lp.getVector();
-						v1 = lp.getVector();
-						c0 = lp.getVector();
-						c1 = lp.getVector();
-						segment = {
-							material: material,
-							colorCode: colorCode,
-							vertices: [ v0, v1 ],
-							controlPoints: [ c0, c1 ]
-						};
-						conditionalSegments.push( segment );
-						break;
-
-						// Line type 3: Triangle
-					case '3':
-						colorCode = lp.getToken();
-						material = getLocalMaterial( colorCode );
-						ccw = bfcCCW;
-						doubleSided = ! bfcCertified || ! bfcCull;
-						if ( ccw === true ) {
-
-							v0 = lp.getVector();
-							v1 = lp.getVector();
-							v2 = lp.getVector();
-
-						} else {
-
-							v2 = lp.getVector();
-							v1 = lp.getVector();
-							v0 = lp.getVector();
-
-						}
-
-						faces.push( {
-							material: material,
-							colorCode: colorCode,
-							faceNormal: null,
-							vertices: [ v0, v1, v2 ],
-							normals: [ null, null, null ]
-						} );
-						totalFaces ++;
-						if ( doubleSided === true ) {
-
-							faces.push( {
-								material: material,
-								colorCode: colorCode,
-								faceNormal: null,
-								vertices: [ v2, v1, v0 ],
-								normals: [ null, null, null ]
-							} );
-							totalFaces ++;
-
-						}
-
-						break;
-
-						// Line type 4: Quadrilateral
-					case '4':
-						colorCode = lp.getToken();
-						material = getLocalMaterial( colorCode );
-						ccw = bfcCCW;
-						doubleSided = ! bfcCertified || ! bfcCull;
-						if ( ccw === true ) {
-
-							v0 = lp.getVector();
-							v1 = lp.getVector();
-							v2 = lp.getVector();
-							v3 = lp.getVector();
-
-						} else {
-
-							v3 = lp.getVector();
-							v2 = lp.getVector();
-							v1 = lp.getVector();
-							v0 = lp.getVector();
-
-						}
-
-						// specifically place the triangle diagonal in the v0 and v1 slots so we can
-						// account for the doubling of vertices later when smoothing normals.
-						faces.push( {
-							material: material,
-							colorCode: colorCode,
-							faceNormal: null,
-							vertices: [ v0, v1, v2, v3 ],
-							normals: [ null, null, null, null ]
-						} );
-						totalFaces += 2;
-						if ( doubleSided === true ) {
-
-							faces.push( {
-								material: material,
-								colorCode: colorCode,
-								faceNormal: null,
-								vertices: [ v3, v2, v1, v0 ],
-								normals: [ null, null, null, null ]
-							} );
-							totalFaces += 2;
-
-						}
-
-						break;
-					default:
-						throw new Error( 'LDrawLoader: Unknown line type "' + lineType + '"' + lp.getLineNumberString() + '.' );
-
-				}
-
-			}
-
-			if ( parsingEmbeddedFiles ) {
-
-				this.setData( currentEmbeddedFileName, currentEmbeddedText );
-
-			}
-
-			return {
-				faces,
-				conditionalSegments,
-				lineSegments,
-				type,
-				category,
-				keywords,
-				author,
-				subobjects,
-				totalFaces,
-				startingBuildingStep,
-				materials,
-				fileName,
-				group: null
-			};
-
-		}
-
-		// returns an (optionally cloned) instance of the data
-		getData( fileName, clone = true ) {
-
-			const key = fileName.toLowerCase();
-			const result = this._cache[ key ];
-			if ( result === null || result instanceof Promise ) {
-
-				return null;
-
-			}
-
-			if ( clone ) {
-
-				return this.cloneResult( result );
-
-			} else {
-
-				return result;
-
-			}
-
-		}
-
-		// kicks off a fetch and parse of the requested data if it hasn't already been loaded. Returns when
-		// the data is ready to use and can be retrieved synchronously with "getData".
-		async ensureDataLoaded( fileName ) {
-
-			const key = fileName.toLowerCase();
-			if ( ! ( key in this._cache ) ) {
-
-				// replace the promise with a copy of the parsed data for immediate processing
-				this._cache[ key ] = this.fetchData( fileName ).then( text => {
-
-					const info = this.parse( text, fileName );
-					this._cache[ key ] = info;
-					return info;
-
-				} );
-
-			}
-
-			await this._cache[ key ];
-
-		}
-
-		// sets the data in the cache from parsed data
-		setData( fileName, text ) {
-
-			const key = fileName.toLowerCase();
-			this._cache[ key ] = this.parse( text, fileName );
-
-		}
-
-	}
-
-	// returns the material for an associated color code. If the color code is 16 for a face or 24 for
-	// an edge then the passthroughColorCode is used.
-	function getMaterialFromCode( colorCode, parentColorCode, materialHierarchy, forEdge ) {
-
-		const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
-		if ( isPassthrough ) {
-
-			colorCode = parentColorCode;
-
-		}
-
-		return materialHierarchy[ colorCode ] || null;
-
-	}
-
-	// Class used to parse and build LDraw parts as three.js objects and cache them if they're a "Part" type.
-	class LDrawPartsGeometryCache {
-
-		constructor( loader ) {
-
-			this.loader = loader;
-			this.parseCache = new LDrawParsedCache( loader );
-			this._cache = {};
-
-		}
-
-		// Convert the given file information into a mesh by processing subobjects.
-		async processIntoMesh( info ) {
-
-			const loader = this.loader;
-			const parseCache = this.parseCache;
-			const faceMaterials = new Set();
-
-			// Processes the part subobject information to load child parts and merge geometry onto part
-			// piece object.
-			const processInfoSubobjects = async ( info, subobject = null ) => {
-
-				const subobjects = info.subobjects;
-				const promises = [];
-
-				// Trigger load of all subobjects. If a subobject isn't a primitive then load it as a separate
-				// group which lets instruction steps apply correctly.
-				for ( let i = 0, l = subobjects.length; i < l; i ++ ) {
-
-					const subobject = subobjects[ i ];
-					const promise = parseCache.ensureDataLoaded( subobject.fileName ).then( () => {
-
-						const subobjectInfo = parseCache.getData( subobject.fileName, false );
-						if ( ! isPrimitiveType( subobjectInfo.type ) ) {
-
-							return this.loadModel( subobject.fileName ).catch( error => {
-
-								console.warn( error );
-								return null;
-
-							} );
-
-						}
-
-						return processInfoSubobjects( parseCache.getData( subobject.fileName ), subobject );
-
-					} );
-					promises.push( promise );
-
-				}
-
-				const group = new THREE.Group();
-				group.userData.category = info.category;
-				group.userData.keywords = info.keywords;
-				group.userData.author = info.author;
-				group.userData.type = info.type;
-				group.userData.fileName = info.fileName;
-				info.group = group;
-				const subobjectInfos = await Promise.all( promises );
-				for ( let i = 0, l = subobjectInfos.length; i < l; i ++ ) {
-
-					const subobject = info.subobjects[ i ];
-					const subobjectInfo = subobjectInfos[ i ];
-					if ( subobjectInfo === null ) {
-
-						// the subobject failed to load
-						continue;
-
-					}
-
-					// if the subobject was loaded as a separate group then apply the parent scopes materials
-					if ( subobjectInfo.isGroup ) {
-
-						const subobjectGroup = subobjectInfo;
-						subobject.matrix.decompose( subobjectGroup.position, subobjectGroup.quaternion, subobjectGroup.scale );
-						subobjectGroup.userData.startingBuildingStep = subobject.startingBuildingStep;
-						subobjectGroup.name = subobject.fileName;
-						loader.applyMaterialsToMesh( subobjectGroup, subobject.colorCode, info.materials );
-						subobjectGroup.userData.colorCode = subobject.colorCode;
-						group.add( subobjectGroup );
-						continue;
-
-					}
-
-					// add the subobject group if it has children in case it has both children and primitives
-					if ( subobjectInfo.group.children.length ) {
-
-						group.add( subobjectInfo.group );
-
-					}
-
-					// transform the primitives into the local space of the parent piece and append them to
-					// to the parent primitives list.
-					const parentLineSegments = info.lineSegments;
-					const parentConditionalSegments = info.conditionalSegments;
-					const parentFaces = info.faces;
-					const lineSegments = subobjectInfo.lineSegments;
-					const conditionalSegments = subobjectInfo.conditionalSegments;
-					const faces = subobjectInfo.faces;
-					const matrix = subobject.matrix;
-					const inverted = subobject.inverted;
-					const matrixScaleInverted = matrix.determinant() < 0;
-					const colorCode = subobject.colorCode;
-					const lineColorCode = colorCode === MAIN_COLOUR_CODE ? MAIN_EDGE_COLOUR_CODE : colorCode;
-					for ( let i = 0, l = lineSegments.length; i < l; i ++ ) {
-
-						const ls = lineSegments[ i ];
-						const vertices = ls.vertices;
-						vertices[ 0 ].applyMatrix4( matrix );
-						vertices[ 1 ].applyMatrix4( matrix );
-						ls.colorCode = ls.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : ls.colorCode;
-						ls.material = ls.material || getMaterialFromCode( ls.colorCode, ls.colorCode, info.materials, true );
-						parentLineSegments.push( ls );
-
-					}
-
-					for ( let i = 0, l = conditionalSegments.length; i < l; i ++ ) {
-
-						const os = conditionalSegments[ i ];
-						const vertices = os.vertices;
-						const controlPoints = os.controlPoints;
-						vertices[ 0 ].applyMatrix4( matrix );
-						vertices[ 1 ].applyMatrix4( matrix );
-						controlPoints[ 0 ].applyMatrix4( matrix );
-						controlPoints[ 1 ].applyMatrix4( matrix );
-						os.colorCode = os.colorCode === MAIN_EDGE_COLOUR_CODE ? lineColorCode : os.colorCode;
-						os.material = os.material || getMaterialFromCode( os.colorCode, os.colorCode, info.materials, true );
-						parentConditionalSegments.push( os );
-
-					}
-
-					for ( let i = 0, l = faces.length; i < l; i ++ ) {
-
-						const tri = faces[ i ];
-						const vertices = tri.vertices;
-						for ( let i = 0, l = vertices.length; i < l; i ++ ) {
-
-							vertices[ i ].applyMatrix4( matrix );
-
-						}
-
-						tri.colorCode = tri.colorCode === MAIN_COLOUR_CODE ? colorCode : tri.colorCode;
-						tri.material = tri.material || getMaterialFromCode( tri.colorCode, colorCode, info.materials, false );
-						faceMaterials.add( tri.colorCode );
-
-						// If the scale of the object is negated then the triangle winding order
-						// needs to be flipped.
-						if ( matrixScaleInverted !== inverted ) {
-
-							vertices.reverse();
-
-						}
-
-						parentFaces.push( tri );
-
-					}
-
-					info.totalFaces += subobjectInfo.totalFaces;
-
-				}
-
-				// Apply the parent subobjects pass through material code to this object. This is done several times due
-				// to material scoping.
-				if ( subobject ) {
-
-					loader.applyMaterialsToMesh( group, subobject.colorCode, info.materials );
-					group.userData.colorCode = subobject.colorCode;
-
-				}
-
-				return info;
-
-			};
-
-			// Track material use to see if we need to use the normal smooth slow path for hard edges.
-			for ( let i = 0, l = info.faces; i < l; i ++ ) {
-
-				faceMaterials.add( info.faces[ i ].colorCode );
-
-			}
-
-			await processInfoSubobjects( info );
-			if ( loader.smoothNormals ) {
-
-				const checkSubSegments = faceMaterials.size > 1;
-				generateFaceNormals( info.faces );
-				smoothNormals( info.faces, info.lineSegments, checkSubSegments );
-
-			}
-
-			// Add the primitive objects and metadata.
-			const group = info.group;
-			if ( info.faces.length > 0 ) {
-
-				group.add( createObject( info.faces, 3, false, info.totalFaces ) );
-
-			}
-
-			if ( info.lineSegments.length > 0 ) {
-
-				group.add( createObject( info.lineSegments, 2 ) );
-
-			}
-
-			if ( info.conditionalSegments.length > 0 ) {
-
-				group.add( createObject( info.conditionalSegments, 2, true ) );
-
-			}
-
-			return group;
-
-		}
-		hasCachedModel( fileName ) {
-
-			return fileName !== null && fileName.toLowerCase() in this._cache;
-
-		}
-		async getCachedModel( fileName ) {
-
-			if ( fileName !== null && this.hasCachedModel( fileName ) ) {
-
-				const key = fileName.toLowerCase();
-				const group = await this._cache[ key ];
-				return group.clone();
-
-			} else {
-
-				return null;
-
-			}
-
-		}
-
-		// Loads and parses the model with the given file name. Returns a cached copy if available.
-		async loadModel( fileName ) {
-
-			const parseCache = this.parseCache;
-			const key = fileName.toLowerCase();
-			if ( this.hasCachedModel( fileName ) ) {
-
-				// Return cached model if available.
-				return this.getCachedModel( fileName );
-
-			} else {
-
-				// Otherwise parse a new model.
-				// Ensure the file data is loaded and pre parsed.
-				await parseCache.ensureDataLoaded( fileName );
-				const info = parseCache.getData( fileName );
-				const promise = this.processIntoMesh( info );
-
-				// Now that the file has loaded it's possible that another part parse has been waiting in parallel
-				// so check the cache again to see if it's been added since the last async operation so we don't
-				// do unnecessary work.
-				if ( this.hasCachedModel( fileName ) ) {
-
-					return this.getCachedModel( fileName );
-
-				}
-
-				// Cache object if it's a part so it can be reused later.
-				if ( isPartType( info.type ) ) {
-
-					this._cache[ key ] = promise;
-
-				}
-
-				// return a copy
-				const group = await promise;
-				return group.clone();
-
-			}
-
-		}
-
-		// parses the given model text into a renderable object. Returns cached copy if available.
-		async parseModel( text ) {
-
-			const parseCache = this.parseCache;
-			const info = parseCache.parse( text );
-			if ( isPartType( info.type ) && this.hasCachedModel( info.fileName ) ) {
-
-				return this.getCachedModel( info.fileName );
-
-			}
-
-			return this.processIntoMesh( info );
-
-		}
-
-	}
-	function sortByMaterial( a, b ) {
-
-		if ( a.colorCode === b.colorCode ) {
-
-			return 0;
-
-		}
-
-		if ( a.colorCode < b.colorCode ) {
-
-			return - 1;
-
-		}
-
-		return 1;
-
-	}
-
-	function createObject( elements, elementSize, isConditionalSegments = false, totalElements = null ) {
-
-		// Creates a THREE.LineSegments (elementSize = 2) or a THREE.Mesh (elementSize = 3 )
-		// With per face / segment material, implemented with mesh groups and materials array
-
-		// Sort the faces or line segments by color code to make later the mesh groups
-		elements.sort( sortByMaterial );
-		if ( totalElements === null ) {
-
-			totalElements = elements.length;
-
-		}
-
-		const positions = new Float32Array( elementSize * totalElements * 3 );
-		const normals = elementSize === 3 ? new Float32Array( elementSize * totalElements * 3 ) : null;
-		const materials = [];
-		const quadArray = new Array( 6 );
-		const bufferGeometry = new THREE.BufferGeometry();
-		let prevMaterial = null;
-		let index0 = 0;
-		let numGroupVerts = 0;
-		let offset = 0;
-		for ( let iElem = 0, nElem = elements.length; iElem < nElem; iElem ++ ) {
-
-			const elem = elements[ iElem ];
-			let vertices = elem.vertices;
-			if ( vertices.length === 4 ) {
-
-				quadArray[ 0 ] = vertices[ 0 ];
-				quadArray[ 1 ] = vertices[ 1 ];
-				quadArray[ 2 ] = vertices[ 2 ];
-				quadArray[ 3 ] = vertices[ 0 ];
-				quadArray[ 4 ] = vertices[ 2 ];
-				quadArray[ 5 ] = vertices[ 3 ];
-				vertices = quadArray;
-
-			}
-
-			for ( let j = 0, l = vertices.length; j < l; j ++ ) {
-
-				const v = vertices[ j ];
-				const index = offset + j * 3;
-				positions[ index + 0 ] = v.x;
-				positions[ index + 1 ] = v.y;
-				positions[ index + 2 ] = v.z;
-
-			}
-
-			// create the normals array if this is a set of faces
-			if ( elementSize === 3 ) {
-
-				if ( ! elem.faceNormal ) {
-
-					const v0 = vertices[ 0 ];
-					const v1 = vertices[ 1 ];
-					const v2 = vertices[ 2 ];
-					_tempVec0.subVectors( v1, v0 );
-					_tempVec1.subVectors( v2, v1 );
-					elem.faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize();
-
-				}
-
-				let elemNormals = elem.normals;
-				if ( elemNormals.length === 4 ) {
-
-					quadArray[ 0 ] = elemNormals[ 0 ];
-					quadArray[ 1 ] = elemNormals[ 1 ];
-					quadArray[ 2 ] = elemNormals[ 2 ];
-					quadArray[ 3 ] = elemNormals[ 0 ];
-					quadArray[ 4 ] = elemNormals[ 2 ];
-					quadArray[ 5 ] = elemNormals[ 3 ];
-					elemNormals = quadArray;
-
-				}
-
-				for ( let j = 0, l = elemNormals.length; j < l; j ++ ) {
-
-					// use face normal if a vertex normal is not provided
-					let n = elem.faceNormal;
-					if ( elemNormals[ j ] ) {
-
-						n = elemNormals[ j ].norm;
-
-					}
-
-					const index = offset + j * 3;
-					normals[ index + 0 ] = n.x;
-					normals[ index + 1 ] = n.y;
-					normals[ index + 2 ] = n.z;
-
-				}
-
-			}
-
-			if ( prevMaterial !== elem.colorCode ) {
-
-				if ( prevMaterial !== null ) {
-
-					bufferGeometry.addGroup( index0, numGroupVerts, materials.length - 1 );
-
-				}
-
-				const material = elem.material;
-				if ( material !== null ) {
-
-					if ( elementSize === 3 ) {
-
-						materials.push( material );
-
-					} else if ( elementSize === 2 ) {
-
-						if ( isConditionalSegments ) {
-
-							materials.push( material.userData.edgeMaterial.userData.conditionalEdgeMaterial );
-
-						} else {
-
-							materials.push( material.userData.edgeMaterial );
-
-						}
-
-					}
-
-				} else {
-
-					// If a material has not been made available yet then keep the color code string in the material array
-					// to save the spot for the material once a parent scopes materials are being applied to the object.
-					materials.push( elem.colorCode );
-
-				}
-
-				prevMaterial = elem.colorCode;
-				index0 = offset / 3;
-				numGroupVerts = vertices.length;
-
-			} else {
-
-				numGroupVerts += vertices.length;
-
-			}
-
-			offset += 3 * vertices.length;
-
-		}
-
-		if ( numGroupVerts > 0 ) {
-
-			bufferGeometry.addGroup( index0, Infinity, materials.length - 1 );
-
-		}
-
-		bufferGeometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
-		if ( normals !== null ) {
-
-			bufferGeometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
-
-		}
-
-		let object3d = null;
-		if ( elementSize === 2 ) {
-
-			if ( isConditionalSegments ) {
-
-				object3d = new ConditionalLineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
-
-			} else {
-
-				object3d = new THREE.LineSegments( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
-
-			}
-
-		} else if ( elementSize === 3 ) {
-
-			object3d = new THREE.Mesh( bufferGeometry, materials.length === 1 ? materials[ 0 ] : materials );
-
-		}
-
-		if ( isConditionalSegments ) {
-
-			object3d.isConditionalLine = true;
-			const controlArray0 = new Float32Array( elements.length * 3 * 2 );
-			const controlArray1 = new Float32Array( elements.length * 3 * 2 );
-			const directionArray = new Float32Array( elements.length * 3 * 2 );
-			for ( let i = 0, l = elements.length; i < l; i ++ ) {
-
-				const os = elements[ i ];
-				const vertices = os.vertices;
-				const controlPoints = os.controlPoints;
-				const c0 = controlPoints[ 0 ];
-				const c1 = controlPoints[ 1 ];
-				const v0 = vertices[ 0 ];
-				const v1 = vertices[ 1 ];
-				const 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.setAttribute( 'control0', new THREE.BufferAttribute( controlArray0, 3, false ) );
-			bufferGeometry.setAttribute( 'control1', new THREE.BufferAttribute( controlArray1, 3, false ) );
-			bufferGeometry.setAttribute( 'direction', new THREE.BufferAttribute( directionArray, 3, false ) );
-
-		}
-
-		return object3d;
-
-	}
-
-	//
-
-	class LDrawLoader extends THREE.Loader {
-
-		constructor( manager ) {
-
-			super( manager );
-
-			// Array of THREE.Material
-			this.materials = [];
-			this.materialLibrary = {};
-
-			// This also allows to handle the embedded text files ("0 FILE" lines)
-			this.partsCache = new LDrawPartsGeometryCache( this );
-
-			// 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 = {};
-
-			// Initializes the materials library with default materials
-			this.setMaterials( [] );
-
-			// If this flag is set to true the vertex normals will be smoothed.
-			this.smoothNormals = true;
-
-			// The path to load parts from the LDraw parts library from.
-			this.partsLibraryPath = '';
-
-			// Material assigned to not available colors for meshes and edges
-			this.missingColorMaterial = new THREE.MeshStandardMaterial( {
-				color: 0xFF00FF,
-				roughness: 0.3,
-				metalness: 0
-			} );
-			this.missingColorMaterial.name = 'Missing material';
-			this.missingEdgeColorMaterial = new THREE.LineBasicMaterial( {
-				color: 0xFF00FF
-			} );
-			this.missingEdgeColorMaterial.name = 'Missing material - Edge';
-			this.missingConditionalEdgeColorMaterial = new LDrawConditionalLineMaterial( {
-				fog: true,
-				color: 0xFF00FF
-			} );
-			this.missingConditionalEdgeColorMaterial.name = 'Missing material - Conditional Edge';
-			this.missingColorMaterial.userData.edgeMaterial = this.missingEdgeColorMaterial;
-			this.missingEdgeColorMaterial.userData.conditionalEdgeMaterial = this.missingConditionalEdgeColorMaterial;
-
-		}
-		setPartsLibraryPath( path ) {
-
-			this.partsLibraryPath = path;
-			return this;
-
-		}
-		async preloadMaterials( url ) {
-
-			const fileLoader = new THREE.FileLoader( this.manager );
-			fileLoader.setPath( this.path );
-			fileLoader.setRequestHeader( this.requestHeader );
-			fileLoader.setWithCredentials( this.withCredentials );
-			const text = await fileLoader.loadAsync( url );
-			const colorLineRegex = /^0 !COLOUR/;
-			const lines = text.split( /[\n\r]/g );
-			const materials = [];
-			for ( let i = 0, l = lines.length; i < l; i ++ ) {
-
-				const line = lines[ i ];
-				if ( colorLineRegex.test( line ) ) {
-
-					const directive = line.replace( colorLineRegex, '' );
-					const material = this.parseColorMetaDirective( new LineParser( directive ) );
-					materials.push( material );
-
-				}
-
-			}
-
-			this.setMaterials( materials );
-
-		}
-		load( url, onLoad, onProgress, onError ) {
-
-			const fileLoader = new THREE.FileLoader( this.manager );
-			fileLoader.setPath( this.path );
-			fileLoader.setRequestHeader( this.requestHeader );
-			fileLoader.setWithCredentials( this.withCredentials );
-			fileLoader.load( url, text => {
-
-				this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
-
-					this.applyMaterialsToMesh( group, MAIN_COLOUR_CODE, this.materialLibrary, true );
-					this.computeBuildingSteps( group );
-					group.userData.fileName = url;
-					onLoad( group );
-
-				} ).catch( onError );
-
-			}, onProgress, onError );
-
-		}
-		parse( text, onLoad ) {
-
-			this.partsCache.parseModel( text, this.materialLibrary ).then( group => {
-
-				this.applyMaterialsToMesh( group, MAIN_COLOUR_CODE, this.materialLibrary, true );
-				this.computeBuildingSteps( group );
-				group.userData.fileName = '';
-				onLoad( group );
-
-			} );
-
-		}
-		setMaterials( materials ) {
-
-			this.materialLibrary = {};
-			this.materials = [];
-			for ( let i = 0, l = materials.length; i < l; i ++ ) {
-
-				this.addMaterial( materials[ i ] );
-
-			}
-
-			// Add default main triangle and line edge materials (used in pieces that can be colored with a main color)
-			this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Main_Colour CODE 16 VALUE #FF8080 EDGE #333333' ) ) );
-			this.addMaterial( this.parseColorMetaDirective( new LineParser( 'Edge_Colour CODE 24 VALUE #A0A0A0 EDGE #333333' ) ) );
-			return this;
-
-		}
-		setFileMap( fileMap ) {
-
-			this.fileMap = fileMap;
-			return this;
-
-		}
-		addMaterial( material ) {
-
-			// Adds a material to the material library which is on top of the parse scopes stack. And also to the materials array
-
-			const matLib = this.materialLibrary;
-			if ( ! matLib[ material.userData.code ] ) {
-
-				this.materials.push( material );
-				matLib[ material.userData.code ] = material;
-
-			}
-
-			return this;
-
-		}
-		getMaterial( colorCode ) {
-
-			if ( colorCode.startsWith( '0x2' ) ) {
-
-				// Special 'direct' material value (RGB color)
-				const color = colorCode.substring( 3 );
-				return this.parseColorMetaDirective( new LineParser( 'Direct_Color_' + color + ' CODE -1 VALUE #' + color + ' EDGE #' + color + '' ) );
-
-			}
-
-			return this.materialLibrary[ colorCode ] || null;
-
-		}
-
-		// Applies the appropriate materials to a prebuilt hierarchy of geometry. Assumes that color codes are present
-		// in the material array if they need to be filled in.
-		applyMaterialsToMesh( group, parentColorCode, materialHierarchy, finalMaterialPass = false ) {
-
-			// find any missing materials as indicated by a color code string and replace it with a material from the current material lib
-			const loader = this;
-			const parentIsPassthrough = parentColorCode === MAIN_COLOUR_CODE;
-			group.traverse( c => {
-
-				if ( c.isMesh || c.isLineSegments ) {
-
-					if ( Array.isArray( c.material ) ) {
-
-						for ( let i = 0, l = c.material.length; i < l; i ++ ) {
-
-							if ( ! c.material[ i ].isMaterial ) {
-
-								c.material[ i ] = getMaterial( c, c.material[ i ] );
-
-							}
-
-						}
-
-					} else if ( ! c.material.isMaterial ) {
-
-						c.material = getMaterial( c, c.material );
-
-					}
-
-				}
-
-			} );
-
-			// Returns the appropriate material for the object (line or face) given color code. If the code is "pass through"
-			// (24 for lines, 16 for edges) then the pass through color code is used. If that is also pass through then it's
-			// simply returned for the subsequent material application.
-			function getMaterial( c, colorCode ) {
-
-				// if our parent is a passthrough color code and we don't have the current material color available then
-				// return early.
-				if ( parentIsPassthrough && ! ( colorCode in materialHierarchy ) && ! finalMaterialPass ) {
-
-					return colorCode;
-
-				}
-
-				const forEdge = c.isLineSegments || c.isConditionalLine;
-				const isPassthrough = ! forEdge && colorCode === MAIN_COLOUR_CODE || forEdge && colorCode === MAIN_EDGE_COLOUR_CODE;
-				if ( isPassthrough ) {
-
-					colorCode = parentColorCode;
-
-				}
-
-				let material = null;
-				if ( colorCode in materialHierarchy ) {
-
-					material = materialHierarchy[ colorCode ];
-
-				} else if ( finalMaterialPass ) {
-
-					// see if we can get the final material from from the "getMaterial" function which will attempt to
-					// parse the "direct" colors
-					material = loader.getMaterial( colorCode );
-					if ( material === null ) {
-
-						// otherwise throw a warning if this is final opportunity to set the material
-						console.warn( `LDrawLoader: Material properties for code ${colorCode} not available.` );
-
-						// And return the 'missing color' material
-						material = loader.missingColorMaterial;
-
-					}
-
-				} else {
-
-					return colorCode;
-
-				}
-
-				if ( c.isLineSegments ) {
-
-					material = material.userData.edgeMaterial;
-					if ( c.isConditionalLine ) {
-
-						material = material.userData.conditionalEdgeMaterial;
-
-					}
-
-				}
-
-				return material;
-
-			}
-
-		}
-		getMainMaterial() {
-
-			return this.getMaterial( MAIN_COLOUR_CODE );
-
-		}
-		getMainEdgeMaterial() {
-
-			const mat = this.getMaterial( MAIN_EDGE_COLOUR_CODE );
-			return mat ? mat.userData.edgeMaterial : null;
-
-		}
-		parseColorMetaDirective( lineParser ) {
-
-			// Parses a color definition and returns a THREE.Material
-
-			let code = null;
-
-			// Triangle and line colors
-			let color = 0xFF00FF;
-			let edgeColor = 0xFF00FF;
-
-			// Transparency
-			let alpha = 1;
-			let isTransparent = false;
-			// Self-illumination:
-			let luminance = 0;
-			let finishType = FINISH_TYPE_DEFAULT;
-			let edgeMaterial = null;
-			const name = lineParser.getToken();
-			if ( ! name ) {
-
-				throw new Error( 'LDrawLoader: Material name was expected after "!COLOUR tag' + lineParser.getLineNumberString() + '.' );
-
-			}
-
-			// Parse tag tokens and their parameters
-			let token = null;
-			while ( true ) {
-
-				token = lineParser.getToken();
-				if ( ! token ) {
-
-					break;
-
-				}
-
-				if ( ! parseLuminance( token ) ) {
-
-					switch ( token.toUpperCase() ) {
-
-						case 'CODE':
-							code = lineParser.getToken();
-							break;
-						case 'VALUE':
-							color = lineParser.getToken();
-							if ( color.startsWith( '0x' ) ) {
-
-								color = '#' + color.substring( 2 );
-
-							} else if ( ! color.startsWith( '#' ) ) {
-
-								throw new Error( 'LDrawLoader: Invalid color while parsing material' + lineParser.getLineNumberString() + '.' );
-
-							}
-
-							break;
-						case 'EDGE':
-							edgeColor = lineParser.getToken();
-							if ( edgeColor.startsWith( '0x' ) ) {
-
-								edgeColor = '#' + edgeColor.substring( 2 );
-
-							} else if ( ! edgeColor.startsWith( '#' ) ) {
-
-								// Try to see if edge color is a color code
-								edgeMaterial = this.getMaterial( edgeColor );
-								if ( ! edgeMaterial ) {
-
-									throw new Error( 'LDrawLoader: Invalid edge color 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 new Error( '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':
-							if ( ! parseLuminance( lineParser.getToken() ) ) {
-
-								throw new Error( 'LDrawLoader: Invalid luminance value in material definition' + LineParser.getLineNumberString() + '.' );
-
-							}
-
-							break;
-						case 'CHROME':
-							finishType = FINISH_TYPE_CHROME;
-							break;
-						case 'PEARLESCENT':
-							finishType = FINISH_TYPE_PEARLESCENT;
-							break;
-						case 'RUBBER':
-							finishType = FINISH_TYPE_RUBBER;
-							break;
-						case 'MATTE_METALLIC':
-							finishType = FINISH_TYPE_MATTE_METALLIC;
-							break;
-						case 'METAL':
-							finishType = FINISH_TYPE_METAL;
-							break;
-						case 'MATERIAL':
-							// Not implemented
-							lineParser.setToEnd();
-							break;
-						default:
-							throw new Error( 'LDrawLoader: Unknown token "' + token + '" while parsing material' + lineParser.getLineNumberString() + '.' );
-
-					}
-
-				}
-
-			}
-
-			let material = null;
-			switch ( finishType ) {
-
-				case FINISH_TYPE_DEFAULT:
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						roughness: 0.3,
-						metalness: 0
-					} );
-					break;
-				case FINISH_TYPE_PEARLESCENT:
-					// Try to imitate pearlescency by making the surface glossy
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						roughness: 0.3,
-						metalness: 0.25
-					} );
-					break;
-				case FINISH_TYPE_CHROME:
-					// Mirror finish surface
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						roughness: 0,
-						metalness: 1
-					} );
-					break;
-				case FINISH_TYPE_RUBBER:
-					// Rubber finish
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						roughness: 0.9,
-						metalness: 0
-					} );
-					break;
-				case FINISH_TYPE_MATTE_METALLIC:
-					// Brushed metal finish
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						roughness: 0.8,
-						metalness: 0.4
-					} );
-					break;
-				case FINISH_TYPE_METAL:
-					// Average metal finish
-					material = new THREE.MeshStandardMaterial( {
-						color: color,
-						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.color.convertSRGBToLinear();
-			material.polygonOffset = true;
-			material.polygonOffsetFactor = 1;
-			if ( luminance !== 0 ) {
-
-				material.emissive.set( material.color ).multiplyScalar( luminance );
-
-			}
-
-			if ( ! edgeMaterial ) {
-
-				// This is the material used for edges
-				edgeMaterial = new THREE.LineBasicMaterial( {
-					color: edgeColor,
-					transparent: isTransparent,
-					opacity: alpha,
-					depthWrite: ! isTransparent
-				} );
-				edgeMaterial.userData.code = code;
-				edgeMaterial.name = name + ' - Edge';
-				edgeMaterial.color.convertSRGBToLinear();
-
-				// This is the material used for conditional edges
-				edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( {
-					fog: true,
-					transparent: isTransparent,
-					depthWrite: ! isTransparent,
-					color: edgeColor,
-					opacity: alpha
-				} );
-				edgeMaterial.userData.conditionalEdgeMaterial.color.convertSRGBToLinear();
-				edgeMaterial.userData.conditionalEdgeMaterial.userData.code = code;
-				edgeMaterial.userData.conditionalEdgeMaterial.name = name + ' - Conditional Edge';
-
-			}
-
-			material.userData.code = code;
-			material.name = name;
-			material.userData.edgeMaterial = edgeMaterial;
-			this.addMaterial( material );
-			return material;
-			function parseLuminance( token ) {
-
-				// Returns success
-
-				let lum;
-				if ( token.startsWith( 'LUMINANCE' ) ) {
-
-					lum = parseInt( token.substring( 9 ) );
-
-				} else {
-
-					lum = parseInt( token );
-
-				}
-
-				if ( isNaN( lum ) ) {
-
-					return false;
-
-				}
-
-				luminance = Math.max( 0, Math.min( 1, lum / 255 ) );
-				return true;
-
-			}
-
-		}
-		computeBuildingSteps( model ) {
-
-			// Sets userdata.buildingStep number in THREE.Group objects and userData.numBuildingSteps number in the root THREE.Group object.
-
-			let stepNumber = 0;
-			model.traverse( c => {
-
-				if ( c.isGroup ) {
-
-					if ( c.userData.startingBuildingStep ) {
-
-						stepNumber ++;
-
-					}
-
-					c.userData.buildingStep = stepNumber;
-
-				}
-
-			} );
-			model.userData.numBuildingSteps = stepNumber + 1;
-
-		}
-
-	}
-
-	THREE.LDrawLoader = LDrawLoader;
-
-} )();

+ 0 - 135
examples/js/loaders/LUT3dlLoader.js

@@ -1,135 +0,0 @@
-( function () {
-
-	// http://download.autodesk.com/us/systemdocs/help/2011/lustre/index.html?url=./files/WSc4e151a45a3b785a24c3d9a411df9298473-7ffd.htm,topicNumber=d0e9492
-	class LUT3dlLoader extends THREE.Loader {
-
-		load( url, onLoad, onProgress, onError ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setResponseType( 'text' );
-			loader.load( url, text => {
-
-				try {
-
-					onLoad( this.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					this.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( str ) {
-
-			// remove empty lines and comment lints
-			str = str.replace( /^#.*?(\n|\r)/gm, '' ).replace( /^\s*?(\n|\r)/gm, '' ).trim();
-			const lines = str.split( /[\n\r]+/g );
-
-			// first line is the positions on the grid that are provided by the LUT
-			const gridLines = lines[ 0 ].trim().split( /\s+/g ).map( e => parseFloat( e ) );
-			const gridStep = gridLines[ 1 ] - gridLines[ 0 ];
-			const size = gridLines.length;
-			for ( let i = 1, l = gridLines.length; i < l; i ++ ) {
-
-				if ( gridStep !== gridLines[ i ] - gridLines[ i - 1 ] ) {
-
-					throw new Error( 'LUT3dlLoader: Inconsistent grid size not supported.' );
-
-				}
-
-			}
-
-			const dataArray = new Array( size * size * size * 4 );
-			let index = 0;
-			let maxOutputValue = 0.0;
-			for ( let i = 1, l = lines.length; i < l; i ++ ) {
-
-				const line = lines[ i ].trim();
-				const split = line.split( /\s/g );
-				const r = parseFloat( split[ 0 ] );
-				const g = parseFloat( split[ 1 ] );
-				const b = parseFloat( split[ 2 ] );
-				maxOutputValue = Math.max( maxOutputValue, r, g, b );
-				const bLayer = index % size;
-				const gLayer = Math.floor( index / size ) % size;
-				const rLayer = Math.floor( index / ( size * size ) ) % size;
-
-				// b grows first, then g, then r
-				const pixelIndex = bLayer * size * size + gLayer * size + rLayer;
-				dataArray[ 4 * pixelIndex + 0 ] = r;
-				dataArray[ 4 * pixelIndex + 1 ] = g;
-				dataArray[ 4 * pixelIndex + 2 ] = b;
-				dataArray[ 4 * pixelIndex + 3 ] = 1.0;
-				index += 1;
-
-			}
-
-			// Find the apparent bit depth of the stored RGB values and map the
-			// values to [ 0, 255 ].
-			const bits = Math.ceil( Math.log2( maxOutputValue ) );
-			const maxBitValue = Math.pow( 2.0, bits );
-			for ( let i = 0, l = dataArray.length; i < l; i += 4 ) {
-
-				const r = dataArray[ i + 0 ];
-				const g = dataArray[ i + 1 ];
-				const b = dataArray[ i + 2 ];
-				dataArray[ i + 0 ] = 255 * r / maxBitValue; // r
-				dataArray[ i + 1 ] = 255 * g / maxBitValue; // g
-				dataArray[ i + 2 ] = 255 * b / maxBitValue; // b
-
-			}
-
-			const data = new Uint8Array( dataArray );
-			const texture = new THREE.DataTexture();
-			texture.image.data = data;
-			texture.image.width = size;
-			texture.image.height = size * size;
-			texture.format = THREE.RGBAFormat;
-			texture.type = THREE.UnsignedByteType;
-			texture.magFilter = THREE.LinearFilter;
-			texture.minFilter = THREE.LinearFilter;
-			texture.wrapS = THREE.ClampToEdgeWrapping;
-			texture.wrapT = THREE.ClampToEdgeWrapping;
-			texture.generateMipmaps = false;
-			texture.needsUpdate = true;
-			const texture3D = new THREE.Data3DTexture();
-			texture3D.image.data = data;
-			texture3D.image.width = size;
-			texture3D.image.height = size;
-			texture3D.image.depth = size;
-			texture3D.format = THREE.RGBAFormat;
-			texture3D.type = THREE.UnsignedByteType;
-			texture3D.magFilter = THREE.LinearFilter;
-			texture3D.minFilter = THREE.LinearFilter;
-			texture3D.wrapS = THREE.ClampToEdgeWrapping;
-			texture3D.wrapT = THREE.ClampToEdgeWrapping;
-			texture3D.wrapR = THREE.ClampToEdgeWrapping;
-			texture3D.generateMipmaps = false;
-			texture3D.needsUpdate = true;
-			return {
-				size,
-				texture,
-				texture3D
-			};
-
-		}
-
-	}
-
-	THREE.LUT3dlLoader = LUT3dlLoader;
-
-} )();

+ 0 - 132
examples/js/loaders/LUTCubeLoader.js

@@ -1,132 +0,0 @@
-( function () {
-
-	// https://wwwimages2.adobe.com/content/dam/acom/en/products/speedgrade/cc/pdfs/cube-lut-specification-1.0.pdf
-	class LUTCubeLoader extends THREE.Loader {
-
-		load( url, onLoad, onProgress, onError ) {
-
-			const loader = new THREE.FileLoader( this.manager );
-			loader.setPath( this.path );
-			loader.setResponseType( 'text' );
-			loader.load( url, text => {
-
-				try {
-
-					onLoad( this.parse( text ) );
-
-				} catch ( e ) {
-
-					if ( onError ) {
-
-						onError( e );
-
-					} else {
-
-						console.error( e );
-
-					}
-
-					this.manager.itemError( url );
-
-				}
-
-			}, onProgress, onError );
-
-		}
-		parse( str ) {
-
-			// Remove empty lines and comments
-			str = str.replace( /^#.*?(\n|\r)/gm, '' ).replace( /^\s*?(\n|\r)/gm, '' ).trim();
-			let title = null;
-			let size = null;
-			const domainMin = new THREE.Vector3( 0, 0, 0 );
-			const domainMax = new THREE.Vector3( 1, 1, 1 );
-			const lines = str.split( /[\n\r]+/g );
-			let data = null;
-			let currIndex = 0;
-			for ( let i = 0, l = lines.length; i < l; i ++ ) {
-
-				const line = lines[ i ].trim();
-				const split = line.split( /\s/g );
-				switch ( split[ 0 ] ) {
-
-					case 'TITLE':
-						title = line.substring( 7, line.length - 1 );
-						break;
-					case 'LUT_3D_SIZE':
-						// TODO: A .CUBE LUT file specifies floating point values and could be represented with
-						// more precision than can be captured with Uint8Array.
-						const sizeToken = split[ 1 ];
-						size = parseFloat( sizeToken );
-						data = new Uint8Array( size * size * size * 4 );
-						break;
-					case 'DOMAIN_MIN':
-						domainMin.x = parseFloat( split[ 1 ] );
-						domainMin.y = parseFloat( split[ 2 ] );
-						domainMin.z = parseFloat( split[ 3 ] );
-						break;
-					case 'DOMAIN_MAX':
-						domainMax.x = parseFloat( split[ 1 ] );
-						domainMax.y = parseFloat( split[ 2 ] );
-						domainMax.z = parseFloat( split[ 3 ] );
-						break;
-					default:
-						const r = parseFloat( split[ 0 ] );
-						const g = parseFloat( split[ 1 ] );
-						const b = parseFloat( split[ 2 ] );
-						if ( r > 1.0 || r < 0.0 || g > 1.0 || g < 0.0 || b > 1.0 || b < 0.0 ) {
-
-							throw new Error( 'LUTCubeLoader : Non normalized values not supported.' );
-
-						}
-
-						data[ currIndex + 0 ] = r * 255;
-						data[ currIndex + 1 ] = g * 255;
-						data[ currIndex + 2 ] = b * 255;
-						data[ currIndex + 3 ] = 255;
-						currIndex += 4;
-
-				}
-
-			}
-
-			const texture = new THREE.DataTexture();
-			texture.image.data = data;
-			texture.image.width = size;
-			texture.image.height = size * size;
-			texture.type = THREE.UnsignedByteType;
-			texture.magFilter = THREE.LinearFilter;
-			texture.minFilter = THREE.LinearFilter;
-			texture.wrapS = THREE.ClampToEdgeWrapping;
-			texture.wrapT = THREE.ClampToEdgeWrapping;
-			texture.generateMipmaps = false;
-			texture.needsUpdate = true;
-			const texture3D = new THREE.Data3DTexture();
-			texture3D.image.data = data;
-			texture3D.image.width = size;
-			texture3D.image.height = size;
-			texture3D.image.depth = size;
-			texture3D.type = THREE.UnsignedByteType;
-			texture3D.magFilter = THREE.LinearFilter;
-			texture3D.minFilter = THREE.LinearFilter;
-			texture3D.wrapS = THREE.ClampToEdgeWrapping;
-			texture3D.wrapT = THREE.ClampToEdgeWrapping;
-			texture3D.wrapR = THREE.ClampToEdgeWrapping;
-			texture3D.generateMipmaps = false;
-			texture3D.needsUpdate = true;
-			return {
-				title,
-				size,
-				domainMin,
-				domainMax,
-				texture,
-				texture3D
-			};
-
-		}
-
-	}
-
-	THREE.LUTCubeLoader = LUTCubeLoader;
-
-} )();

部分文件因文件數量過多而無法顯示